Merge branch 'main' into hide-run-as-task-if-not-supported
This commit is contained in:
commit
6d18cf3711
6 changed files with 68 additions and 62 deletions
6
.github/workflows/claude.yml
vendored
6
.github/workflows/claude.yml
vendored
|
|
@ -33,7 +33,7 @@ jobs:
|
||||||
github.event_name == 'pull_request_review_comment' ||
|
github.event_name == 'pull_request_review_comment' ||
|
||||||
github.event_name == 'pull_request_review'
|
github.event_name == 'pull_request_review'
|
||||||
id: pr
|
id: pr
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@v8
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
let prNumber;
|
let prNumber;
|
||||||
|
|
@ -54,7 +54,7 @@ jobs:
|
||||||
|
|
||||||
- name: Checkout PR branch
|
- name: Checkout PR branch
|
||||||
if: steps.pr.outcome == 'success'
|
if: steps.pr.outcome == 'success'
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
ref: ${{ steps.pr.outputs.sha }}
|
ref: ${{ steps.pr.outputs.sha }}
|
||||||
repository: ${{ steps.pr.outputs.repo }}
|
repository: ${{ steps.pr.outputs.repo }}
|
||||||
|
|
@ -62,7 +62,7 @@ jobs:
|
||||||
|
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
if: steps.pr.outcome != 'success'
|
if: steps.pr.outcome != 'success'
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
|
|
|
||||||
4
.github/workflows/cli_tests.yml
vendored
4
.github/workflows/cli_tests.yml
vendored
|
|
@ -15,10 +15,10 @@ jobs:
|
||||||
run:
|
run:
|
||||||
working-directory: ./cli
|
working-directory: ./cli
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Set up Node.js
|
- name: Set up Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version-file: package.json
|
node-version-file: package.json
|
||||||
cache: npm
|
cache: npm
|
||||||
|
|
|
||||||
8
.github/workflows/e2e_tests.yml
vendored
8
.github/workflows/e2e_tests.yml
vendored
|
|
@ -18,9 +18,9 @@ jobs:
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install -y libwoff1
|
sudo apt-get install -y libwoff1
|
||||||
|
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v6
|
||||||
id: setup_node
|
id: setup_node
|
||||||
with:
|
with:
|
||||||
node-version-file: package.json
|
node-version-file: package.json
|
||||||
|
|
@ -29,7 +29,7 @@ jobs:
|
||||||
# Cache Playwright browsers
|
# Cache Playwright browsers
|
||||||
- name: Cache Playwright browsers
|
- name: Cache Playwright browsers
|
||||||
id: cache-playwright
|
id: cache-playwright
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v5
|
||||||
with:
|
with:
|
||||||
path: ~/.cache/ms-playwright # The default Playwright cache path
|
path: ~/.cache/ms-playwright # The default Playwright cache path
|
||||||
key: ${{ runner.os }}-playwright-${{ hashFiles('package-lock.json') }} # Cache key based on OS and package-lock.json
|
key: ${{ runner.os }}-playwright-${{ hashFiles('package-lock.json') }} # Cache key based on OS and package-lock.json
|
||||||
|
|
@ -51,7 +51,7 @@ jobs:
|
||||||
run: npm run test:e2e
|
run: npm run test:e2e
|
||||||
|
|
||||||
- name: Upload Playwright Report and Screenshots
|
- name: Upload Playwright Report and Screenshots
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v6
|
||||||
if: steps.playwright-tests.conclusion != 'skipped'
|
if: steps.playwright-tests.conclusion != 'skipped'
|
||||||
with:
|
with:
|
||||||
name: playwright-report
|
name: playwright-report
|
||||||
|
|
|
||||||
12
.github/workflows/main.yml
vendored
12
.github/workflows/main.yml
vendored
|
|
@ -12,12 +12,12 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Check formatting
|
- name: Check formatting
|
||||||
run: npx prettier --check .
|
run: npx prettier --check .
|
||||||
|
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version-file: package.json
|
node-version-file: package.json
|
||||||
cache: npm
|
cache: npm
|
||||||
|
|
@ -50,8 +50,8 @@ jobs:
|
||||||
id-token: write
|
id-token: write
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version-file: package.json
|
node-version-file: package.json
|
||||||
cache: npm
|
cache: npm
|
||||||
|
|
@ -77,7 +77,7 @@ jobs:
|
||||||
attestations: write
|
attestations: write
|
||||||
id-token: write
|
id-token: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Log in to the Container registry
|
- name: Log in to the Container registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
|
|
@ -109,7 +109,7 @@ jobs:
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
|
||||||
- name: Generate artifact attestation
|
- name: Generate artifact attestation
|
||||||
uses: actions/attest-build-provenance@v2
|
uses: actions/attest-build-provenance@v3
|
||||||
with:
|
with:
|
||||||
subject-name: ghcr.io/${{ github.repository }}
|
subject-name: ghcr.io/${{ github.repository }}
|
||||||
subject-digest: ${{ steps.push.outputs.digest }}
|
subject-digest: ${{ steps.push.outputs.digest }}
|
||||||
|
|
|
||||||
|
|
@ -203,7 +203,7 @@ const ToolsTab = ({
|
||||||
<IconDisplay icons={(tool as ExtendedTool).icons} size="sm" />
|
<IconDisplay icons={(tool as ExtendedTool).icons} size="sm" />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col flex-1 min-w-0">
|
<div className="flex flex-col flex-1 min-w-0">
|
||||||
<span className="truncate">{tool.name}</span>
|
<span className="truncate">{tool.title || tool.name}</span>
|
||||||
<span className="text-sm text-gray-500 text-left line-clamp-2">
|
<span className="text-sm text-gray-500 text-left line-clamp-2">
|
||||||
{tool.description}
|
{tool.description}
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -226,7 +226,9 @@ const ToolsTab = ({
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<h3 className="font-semibold">
|
<h3 className="font-semibold">
|
||||||
{selectedTool ? selectedTool.name : "Select a tool"}
|
{selectedTool
|
||||||
|
? selectedTool.title || selectedTool.name
|
||||||
|
: "Select a tool"}
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -159,20 +159,46 @@ export function isPropertyRequired(
|
||||||
* Resolves $ref references in JSON schema
|
* Resolves $ref references in JSON schema
|
||||||
* @param schema The schema that may contain $ref
|
* @param schema The schema that may contain $ref
|
||||||
* @param rootSchema The root schema to resolve references against
|
* @param rootSchema The root schema to resolve references against
|
||||||
|
* @param visitedRefs Optional set of visited $ref paths to detect circular references
|
||||||
* @returns The resolved schema without $ref
|
* @returns The resolved schema without $ref
|
||||||
*/
|
*/
|
||||||
export function resolveRef(
|
export function resolveRef(
|
||||||
schema: JsonSchemaType,
|
schema: JsonSchemaType,
|
||||||
rootSchema: JsonSchemaType,
|
rootSchema: JsonSchemaType,
|
||||||
|
visitedRefs: Set<string> = new Set(),
|
||||||
): JsonSchemaType {
|
): JsonSchemaType {
|
||||||
|
if (!schema) return schema;
|
||||||
|
|
||||||
if (!("$ref" in schema) || !schema.$ref) {
|
if (!("$ref" in schema) || !schema.$ref) {
|
||||||
|
// Recursively resolve $ref in anyOf (and other nested structures)
|
||||||
|
if (schema.anyOf && Array.isArray(schema.anyOf)) {
|
||||||
|
const resolvedAnyOf = schema.anyOf.map((item) => {
|
||||||
|
if (typeof item === "object" && item !== null) {
|
||||||
|
return resolveRef(item, rootSchema, visitedRefs);
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
...schema,
|
||||||
|
anyOf: resolvedAnyOf,
|
||||||
|
};
|
||||||
|
}
|
||||||
return schema;
|
return schema;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ref = schema.$ref;
|
const ref = schema.$ref;
|
||||||
|
|
||||||
// Handle simple #/properties/name references
|
// Handle all #/ formats (#/properties/, #/$defs/, etc.)
|
||||||
if (ref.startsWith("#/")) {
|
if (ref.startsWith("#/")) {
|
||||||
|
// Check for circular reference
|
||||||
|
if (visitedRefs.has(ref)) {
|
||||||
|
console.warn(`Circular reference detected: ${ref}`);
|
||||||
|
return schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add current ref to visited set
|
||||||
|
visitedRefs.add(ref);
|
||||||
|
|
||||||
const path = ref.substring(2).split("/");
|
const path = ref.substring(2).split("/");
|
||||||
let current: unknown = rootSchema;
|
let current: unknown = rootSchema;
|
||||||
|
|
||||||
|
|
@ -186,12 +212,16 @@ export function resolveRef(
|
||||||
current = (current as Record<string, unknown>)[segment];
|
current = (current as Record<string, unknown>)[segment];
|
||||||
} else {
|
} else {
|
||||||
// If reference cannot be resolved, return the original schema
|
// If reference cannot be resolved, return the original schema
|
||||||
|
visitedRefs.delete(ref); // Clean up on failure
|
||||||
console.warn(`Could not resolve $ref: ${ref}`);
|
console.warn(`Could not resolve $ref: ${ref}`);
|
||||||
return schema;
|
return schema;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return current as JsonSchemaType;
|
const resolved = current as JsonSchemaType;
|
||||||
|
|
||||||
|
// Recursively resolve nested structures (anyOf, oneOf, items, properties)
|
||||||
|
return resolveRef(resolved, rootSchema, visitedRefs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// For other types of references, return the original schema
|
// For other types of references, return the original schema
|
||||||
|
|
@ -205,54 +235,28 @@ export function resolveRef(
|
||||||
* @returns A normalized schema or the original schema
|
* @returns A normalized schema or the original schema
|
||||||
*/
|
*/
|
||||||
export function normalizeUnionType(schema: JsonSchemaType): JsonSchemaType {
|
export function normalizeUnionType(schema: JsonSchemaType): JsonSchemaType {
|
||||||
// Handle anyOf with exactly string and null (FastMCP pattern)
|
// Handle anyOf with exactly 2 items (type and null) - unified handling
|
||||||
|
// Preserves enum and other properties automatically
|
||||||
if (
|
if (
|
||||||
schema.anyOf &&
|
schema.anyOf &&
|
||||||
schema.anyOf.length === 2 &&
|
schema.anyOf.length === 2 &&
|
||||||
schema.anyOf.some((t) => (t as JsonSchemaType).type === "string") &&
|
|
||||||
schema.anyOf.some((t) => (t as JsonSchemaType).type === "null")
|
schema.anyOf.some((t) => (t as JsonSchemaType).type === "null")
|
||||||
) {
|
) {
|
||||||
return { ...schema, type: "string", anyOf: undefined, nullable: true };
|
const nonNullItem = schema.anyOf.find((t) => {
|
||||||
}
|
const item = t as JsonSchemaType;
|
||||||
|
return item?.type !== "null";
|
||||||
|
}) as JsonSchemaType;
|
||||||
|
|
||||||
// Handle anyOf with exactly boolean and null (FastMCP pattern)
|
// Only process if non-null item has type or enum
|
||||||
if (
|
if (nonNullItem?.type || nonNullItem?.enum) {
|
||||||
schema.anyOf &&
|
return {
|
||||||
schema.anyOf.length === 2 &&
|
...schema,
|
||||||
schema.anyOf.some((t) => (t as JsonSchemaType).type === "boolean") &&
|
...nonNullItem,
|
||||||
schema.anyOf.some((t) => (t as JsonSchemaType).type === "null")
|
type: nonNullItem?.type || (nonNullItem?.enum ? "string" : undefined),
|
||||||
) {
|
nullable: true,
|
||||||
return { ...schema, type: "boolean", anyOf: undefined, nullable: true };
|
anyOf: undefined,
|
||||||
}
|
};
|
||||||
|
}
|
||||||
// Handle anyOf with exactly number and null (FastMCP pattern)
|
|
||||||
if (
|
|
||||||
schema.anyOf &&
|
|
||||||
schema.anyOf.length === 2 &&
|
|
||||||
schema.anyOf.some((t) => (t as JsonSchemaType).type === "number") &&
|
|
||||||
schema.anyOf.some((t) => (t as JsonSchemaType).type === "null")
|
|
||||||
) {
|
|
||||||
return { ...schema, type: "number", anyOf: undefined, nullable: true };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle anyOf with exactly integer and null (FastMCP pattern)
|
|
||||||
if (
|
|
||||||
schema.anyOf &&
|
|
||||||
schema.anyOf.length === 2 &&
|
|
||||||
schema.anyOf.some((t) => (t as JsonSchemaType).type === "integer") &&
|
|
||||||
schema.anyOf.some((t) => (t as JsonSchemaType).type === "null")
|
|
||||||
) {
|
|
||||||
return { ...schema, type: "integer", anyOf: undefined, nullable: true };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle anyOf with exactly array and null (FastMCP pattern)
|
|
||||||
if (
|
|
||||||
schema.anyOf &&
|
|
||||||
schema.anyOf.length === 2 &&
|
|
||||||
schema.anyOf.some((t) => (t as JsonSchemaType).type === "array") &&
|
|
||||||
schema.anyOf.some((t) => (t as JsonSchemaType).type === "null")
|
|
||||||
) {
|
|
||||||
return { ...schema, type: "array", anyOf: undefined, nullable: true };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle array type with exactly string and null
|
// Handle array type with exactly string and null
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue