swallowkit 2.0.0-alpha.4 → 2.0.0-alpha.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commands/add-auth.js +37 -6
- package/dist/cli/commands/add-auth.js.map +1 -1
- package/dist/cli/commands/init.d.ts +2 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +3 -1
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/scaffold.d.ts.map +1 -1
- package/dist/cli/commands/scaffold.js +45 -1
- package/dist/cli/commands/scaffold.js.map +1 -1
- package/dist/cli/index.js +2 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/core/operations/create-model.d.ts +2 -0
- package/dist/core/operations/create-model.d.ts.map +1 -1
- package/dist/core/operations/create-model.js +60 -8
- package/dist/core/operations/create-model.js.map +1 -1
- package/dist/core/project/boundaries.js +3 -3
- package/dist/core/project/boundaries.js.map +1 -1
- package/dist/core/project/capabilities.d.ts +1 -0
- package/dist/core/project/capabilities.d.ts.map +1 -1
- package/dist/core/project/capabilities.js +9 -3
- package/dist/core/project/capabilities.js.map +1 -1
- package/dist/core/project/react-project.js +1 -1
- package/dist/core/project/workflows.d.ts.map +1 -1
- package/dist/core/project/workflows.js +15 -5
- package/dist/core/project/workflows.js.map +1 -1
- package/dist/core/scaffold/auth-generator.d.ts.map +1 -1
- package/dist/core/scaffold/auth-generator.js +18 -19
- package/dist/core/scaffold/auth-generator.js.map +1 -1
- package/dist/core/scaffold/native-schema-generator.d.ts +2 -0
- package/dist/core/scaffold/native-schema-generator.d.ts.map +1 -1
- package/dist/core/scaffold/native-schema-generator.js +16 -1
- package/dist/core/scaffold/native-schema-generator.js.map +1 -1
- package/dist/core/verify/index.d.ts +4 -3
- package/dist/core/verify/index.d.ts.map +1 -1
- package/dist/core/verify/index.js +98 -4
- package/dist/core/verify/index.js.map +1 -1
- package/dist/machine/contracts.d.ts +1 -0
- package/dist/machine/contracts.d.ts.map +1 -1
- package/dist/machine/errors.d.ts +3 -2
- package/dist/machine/errors.d.ts.map +1 -1
- package/dist/machine/errors.js +2 -1
- package/dist/machine/errors.js.map +1 -1
- package/dist/machine/index.d.ts.map +1 -1
- package/dist/machine/index.js +3 -2
- package/dist/machine/index.js.map +1 -1
- package/dist/mcp/index.d.ts +1 -0
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +19 -4
- package/dist/mcp/index.js.map +1 -1
- package/package.json +6 -3
- package/src/__tests__/__snapshots__/boundaries.test.ts.snap +6 -6
- package/src/__tests__/agent-loop-phase34.test.ts +95 -0
- package/src/__tests__/generated-project-matrix.test.ts +789 -0
- package/src/__tests__/mcp.test.ts +15 -0
- package/src/__tests__/multi-auth.test.ts +115 -4
- package/src/__tests__/plan-apply.test.ts +111 -0
- package/src/cli/commands/add-auth.ts +36 -6
- package/src/cli/commands/init.ts +4 -4
- package/src/cli/commands/scaffold.ts +65 -1
- package/src/cli/index.ts +2 -2
- package/src/core/operations/create-model.ts +51 -13
- package/src/core/project/agent-templates/AGENTS.md +28 -3
- package/src/core/project/agent-templates/CLAUDE.md +4 -0
- package/src/core/project/agent-templates/copilot-instructions.md +4 -0
- package/src/core/project/agent-templates/shared-models.instructions.md +11 -3
- package/src/core/project/boundaries.ts +3 -3
- package/src/core/project/capabilities.ts +10 -3
- package/src/core/project/react-project.ts +1 -1
- package/src/core/project/workflows.ts +15 -5
- package/src/core/scaffold/auth-generator.ts +18 -19
- package/src/core/scaffold/native-schema-generator.ts +16 -0
- package/src/core/verify/index.ts +95 -5
- package/src/machine/contracts.ts +1 -0
- package/src/machine/errors.ts +4 -2
- package/src/machine/index.ts +3 -2
- package/src/mcp/index.ts +19 -6
|
@@ -319,6 +319,20 @@ custom endpoints that derive the scoping key (e.g. `learnerId`) from the
|
|
|
319
319
|
verified principal — never from the request body — and restrict the generated
|
|
320
320
|
write endpoints to admin roles.
|
|
321
321
|
|
|
322
|
+
Configure authentication with `plan auth` / `apply auth` **before** declaring
|
|
323
|
+
`authPolicy`. Without authentication, an explicit policy
|
|
324
|
+
blocks plan/apply/scaffold with `auth-policy-without-auth` before file writes;
|
|
325
|
+
follow `nextActions`, then re-plan. Do not remove a required policy just to pass
|
|
326
|
+
generation.
|
|
327
|
+
An empty `authPolicy = {}` means no explicit policy; it does not enable authentication.
|
|
328
|
+
|
|
329
|
+
With named `auth.schemes`, define `auth.authorization.policies` selecting the
|
|
330
|
+
required schemes and roles, then use `authPolicy = { policy: 'adminOnly' }` (or
|
|
331
|
+
read/write policy names). Role-only declarations do not select a named scheme
|
|
332
|
+
and block with `auth-policy-requires-named-policy`. Conversely, single-provider
|
|
333
|
+
`auth.provider` supports role lists only; a named policy (including a named
|
|
334
|
+
`defaultPolicy`) blocks with `auth-named-policy-requires-named-scheme`.
|
|
335
|
+
|
|
322
336
|
## Verification Is a Completion Gate
|
|
323
337
|
|
|
324
338
|
Before treating an agent-driven implementation as complete, run SwallowKit
|
|
@@ -330,8 +344,17 @@ verification:
|
|
|
330
344
|
|
|
331
345
|
or the equivalent MCP operation.
|
|
332
346
|
|
|
333
|
-
|
|
334
|
-
|
|
347
|
+
Default checks are `structure`, `drift`, `typecheck`, `backend-build`, plus
|
|
348
|
+
configured project checks. `backend-build` checks standalone `functions/`:
|
|
349
|
+
C# uses `dotnet build`, TypeScript uses the project's `tsc --noEmit`, and Python
|
|
350
|
+
uses Python 3 `compileall` (syntax only, not imports or type checking), preferring
|
|
351
|
+
`functions/.venv` over PATH. Missing toolchains are `skip` with an explanation;
|
|
352
|
+
a skipped backend check is **not** evidence that the backend works. Restore the
|
|
353
|
+
toolchain and re-run it before claiming backend validation.
|
|
354
|
+
|
|
355
|
+
Root `build` scripts do not replace standalone backend verification. To include
|
|
356
|
+
frontend production builds, use `--checks "structure,drift,typecheck,backend-build,build"`.
|
|
357
|
+
Quote comma-separated values in PowerShell.
|
|
335
358
|
|
|
336
359
|
When verification fails, use the structured failure explanation:
|
|
337
360
|
|
|
@@ -474,7 +497,9 @@ same generated structure.
|
|
|
474
497
|
- Model schema files use `shared/models/{kebab-case}.ts`.
|
|
475
498
|
- The Zod schema constant and inferred TypeScript type use the same PascalCase
|
|
476
499
|
name: `export const Todo = z.object(...); export type Todo = z.infer<typeof Todo>`.
|
|
477
|
-
- Re-export models from `shared/index.ts`.
|
|
500
|
+
- Re-export models from `shared/index.ts`. create-model and plan/apply scaffold
|
|
501
|
+
append missing exports, including manually authored models. Manual exports
|
|
502
|
+
are allowed; this file has no managed markers and existing content is preserved.
|
|
478
503
|
- `id` is a required `z.string()`; only `createdAt` / `updatedAt` are optional
|
|
479
504
|
backend-managed fields.
|
|
480
505
|
- Models may declare `partitionKey`, `authPolicy`, `displayName`,
|
|
@@ -25,6 +25,10 @@ Always-on rules:
|
|
|
25
25
|
- Use plan/apply for deterministic generation.
|
|
26
26
|
- Respect all `requires-human` states and provisioning approval gates.
|
|
27
27
|
- Run SwallowKit verification before considering implementation complete.
|
|
28
|
+
- Default verification includes `backend-build` for standalone Functions.
|
|
29
|
+
Skipped toolchains are not validation; Python compilation checks syntax only.
|
|
30
|
+
- Configure auth via plan/apply auth before adding `authPolicy`; otherwise
|
|
31
|
+
scaffold blocks before file writes. Scaffold appends missing shared model exports.
|
|
28
32
|
|
|
29
33
|
Task-specific SwallowKit Agent Skills are available under `.github/skills/`.
|
|
30
34
|
Equivalent agent-agnostic runbooks are available under
|
|
@@ -24,6 +24,10 @@ Key rules:
|
|
|
24
24
|
- Use plan/apply for deterministic generation.
|
|
25
25
|
- Respect `requires-human` states and other human approval gates.
|
|
26
26
|
- Run SwallowKit verification before completion.
|
|
27
|
+
- Default verification includes `backend-build` for standalone Functions.
|
|
28
|
+
Skipped toolchains are not validation; Python compilation checks syntax only.
|
|
29
|
+
- Configure auth via plan/apply auth before adding `authPolicy`; otherwise
|
|
30
|
+
scaffold blocks before file writes. Scaffold appends missing shared model exports.
|
|
27
31
|
|
|
28
32
|
Task-specific SwallowKit Agent Skills live under `.github/skills/`.
|
|
29
33
|
Agent-agnostic equivalents live under `.swallowkit/workflows/`.
|
|
@@ -18,8 +18,9 @@ source-of-truth models, subject to the current result of
|
|
|
18
18
|
- `createdAt` and `updatedAt` are **optional** string fields
|
|
19
19
|
(`z.string().optional()`). The backend manages their values.
|
|
20
20
|
- Export a `displayName` string for UI display.
|
|
21
|
-
-
|
|
22
|
-
|
|
21
|
+
- create-model and plan/apply scaffold append missing model exports to
|
|
22
|
+
`shared/index.ts`, including manually authored models. Manual exports are
|
|
23
|
+
allowed; there are no managed markers and existing content is preserved.
|
|
23
24
|
- For relationships, use nested schemas rather than duplicating model shapes.
|
|
24
25
|
|
|
25
26
|
## Optional model declarations (exact formats)
|
|
@@ -30,7 +31,7 @@ These are the only declarations SwallowKit parses from a model file:
|
|
|
30
31
|
// Cosmos DB partition key (default '/id'). Must reference a schema field.
|
|
31
32
|
export const partitionKey = '/learnerId';
|
|
32
33
|
|
|
33
|
-
// Role-based access control on generated CRUD.
|
|
34
|
+
// Role-based access control on generated CRUD (requires plan/apply auth first).
|
|
34
35
|
export const authPolicy = { roles: ['admin'] };
|
|
35
36
|
// or: { read: ['user'], write: ['admin'] } or: { policy: 'adminOnly' }
|
|
36
37
|
|
|
@@ -48,6 +49,13 @@ export const connectorConfig = {
|
|
|
48
49
|
The full machine-readable contract:
|
|
49
50
|
`{{runCmd}} swallowkit machine inspect capabilities`
|
|
50
51
|
|
|
52
|
+
An explicit `authPolicy` without authentication blocks plan/apply/scaffold with
|
|
53
|
+
`auth-policy-without-auth` before writing files. Follow `nextActions` to configure
|
|
54
|
+
auth, then re-plan. Do not drop a required guard to bypass the prerequisite.
|
|
55
|
+
For named `auth.schemes`, use named authorization policies selecting schemes
|
|
56
|
+
and roles, referenced by `policy` or read/write names. Role-only declarations
|
|
57
|
+
block with `auth-policy-requires-named-policy` rather than emitting missing helpers.
|
|
58
|
+
|
|
51
59
|
After changing a model, do not manually patch generated artifacts.
|
|
52
60
|
|
|
53
61
|
## After changing a model
|
|
@@ -86,7 +86,7 @@ export const OWNERSHIP_POLICIES: OwnershipPolicy[] = [
|
|
|
86
86
|
zone: "shared",
|
|
87
87
|
editPolicy: "edit-outside-markers",
|
|
88
88
|
howToChange:
|
|
89
|
-
"SwallowKit appends
|
|
89
|
+
"SwallowKit appends at the file-specific extension point: marked sections (e.g. `# SwallowKit scaffold registrations`) or append-only files such as shared/index.ts. Follow the path rule; preserve existing generated entries and any markers.",
|
|
90
90
|
},
|
|
91
91
|
{
|
|
92
92
|
ownership: "metadata",
|
|
@@ -134,8 +134,8 @@ const COMMON_LEADING_RULES: BoundaryPathRule[] = [
|
|
|
134
134
|
{
|
|
135
135
|
pattern: "shared/index.ts",
|
|
136
136
|
zone: "shared",
|
|
137
|
-
editPolicy: "edit
|
|
138
|
-
description: "
|
|
137
|
+
editPolicy: "free-edit",
|
|
138
|
+
description: "create-model and scaffold append missing model exports while preserving existing content. No managed markers; manual exports are allowed. Keep exports needed by generated code.",
|
|
139
139
|
},
|
|
140
140
|
];
|
|
141
141
|
|
|
@@ -65,6 +65,7 @@ export interface CapabilitiesContract {
|
|
|
65
65
|
defaultChecks: string[];
|
|
66
66
|
optInChecks: string[];
|
|
67
67
|
customChecks: string;
|
|
68
|
+
backendBuild: string;
|
|
68
69
|
};
|
|
69
70
|
}
|
|
70
71
|
|
|
@@ -109,6 +110,11 @@ export function inspectCapabilities(): CapabilitiesContract {
|
|
|
109
110
|
"Role-based access control metadata applied to the generated CRUD Functions. `roles` guards all operations; `read`/`write` guard per-operation; `policy` references a named authorization policy from swallowkit.config auth.authorization.policies.",
|
|
110
111
|
format:
|
|
111
112
|
"export const authPolicy = { roles: ['admin'] };\n// or per-operation:\nexport const authPolicy = { read: ['user'], write: ['admin'] };\n// or a named policy:\nexport const authPolicy = { policy: 'adminOnly' };",
|
|
113
|
+
notes: [
|
|
114
|
+
"An explicit authPolicy without configured authentication blocks plan/apply/generate scaffold with auth-policy-without-auth before any artifacts are written. Run plan auth, apply auth, then re-plan scaffold. An empty {} declaration keeps the existing behavior: no explicit policy.",
|
|
115
|
+
"With named auth.schemes, define auth.authorization.policies selecting schemes and roles, then reference them via policy or read/write names. A role-only declaration cannot select a named scheme and blocks with auth-policy-requires-named-policy; it is supported by legacy single-provider authentication.",
|
|
116
|
+
"Conversely, single-provider authentication (auth.provider) supports role lists only. Referencing a named policy (authPolicy.policy, read/write names, or a named auth.authorization.defaultPolicy) blocks with auth-named-policy-requires-named-scheme; add a named scheme first.",
|
|
117
|
+
],
|
|
112
118
|
},
|
|
113
119
|
{
|
|
114
120
|
name: "references",
|
|
@@ -186,7 +192,7 @@ export function inspectCapabilities(): CapabilitiesContract {
|
|
|
186
192
|
"Without authentication, plan scaffold reports user-scoped-data-without-auth when the partition key or an owner-like field (userId, learnerId, ownerId, ...) suggests per-user data.",
|
|
187
193
|
],
|
|
188
194
|
guidance: [
|
|
189
|
-
"For multi-user data where users may only touch their own records, add ai-authored custom endpoints that derive the scoping key (e.g. learnerId) from the verified principal — never from the request body — and restrict generated write endpoints to admin roles or disable them.",
|
|
195
|
+
"Configure authentication via plan/apply auth before declaring authPolicy. For multi-user data where users may only touch their own records, add ai-authored custom endpoints that derive the scoping key (e.g. learnerId) from the verified principal — never from the request body — and restrict generated write endpoints to admin roles or disable them.",
|
|
190
196
|
],
|
|
191
197
|
},
|
|
192
198
|
sharedModels: {
|
|
@@ -200,7 +206,7 @@ export function inspectCapabilities(): CapabilitiesContract {
|
|
|
200
206
|
"String formats (email/uuid/url), refinements, and transforms are not re-implemented natively; the BFF Zod schemas remain the authoritative validation.",
|
|
201
207
|
],
|
|
202
208
|
buildNote:
|
|
203
|
-
"verify typecheck resolves the shared package from its built dist/. SwallowKit rebuilds shared automatically during verify; when typechecking manually, build
|
|
209
|
+
"create-model and plan/apply scaffold append missing model exports to shared/index.ts, including manually authored models. Manual exports are allowed (no markers); existing content is preserved. verify typecheck resolves the shared package from its built dist/. SwallowKit rebuilds shared automatically during verify; when typechecking manually, build shared first.",
|
|
204
210
|
},
|
|
205
211
|
seeding: {
|
|
206
212
|
createTemplates: "swallowkit create-dev-seeds <environment> — writes dev-seeds/<environment>/<model>.json templates.",
|
|
@@ -219,9 +225,10 @@ export function inspectCapabilities(): CapabilitiesContract {
|
|
|
219
225
|
"(regular CLI) configure-mcp [--version auto|latest|<semver>] — re-select the MCP launcher version in .mcp.json",
|
|
220
226
|
],
|
|
221
227
|
verifyChecks: {
|
|
222
|
-
defaultChecks: ["structure", "drift", "typecheck"],
|
|
228
|
+
defaultChecks: ["structure", "drift", "typecheck", "backend-build"],
|
|
223
229
|
optInChecks: ["build", "lint", "test"],
|
|
224
230
|
customChecks: "swallowkit.config verify.checks: [{ id, command, title?, timeoutMs? }]",
|
|
231
|
+
backendBuild: "Runs dotnet build in functions/ (C#), the project tsc --noEmit (TypeScript), or Python 3 compileall (syntax only, using functions/.venv before PATH). Missing toolchains are skip with a reason, not proof of validation. Root build scripts do not replace backend-build; use --checks \"structure,drift,typecheck,backend-build,build\" for both.",
|
|
225
232
|
},
|
|
226
233
|
};
|
|
227
234
|
}
|
|
@@ -407,7 +407,7 @@ live in \`functions/local.settings.json\`. Keep their secrets out of source cont
|
|
|
407
407
|
\`\`\`sh
|
|
408
408
|
${pm} run build
|
|
409
409
|
${cmd.exec} swallowkit machine verify project
|
|
410
|
-
${cmd.exec} swallowkit machine verify project --checks structure,drift,typecheck,build
|
|
410
|
+
${cmd.exec} swallowkit machine verify project --checks "structure,drift,typecheck,backend-build,build"
|
|
411
411
|
\`\`\`
|
|
412
412
|
|
|
413
413
|
The build compiles shared schemas, checks the SPA, builds static assets, and
|
|
@@ -62,6 +62,13 @@ Goal: add a data model and derive CRUD/BFF/UI/infra deterministically.
|
|
|
62
62
|
the plan warns \`infra-skipped\`; later run \`${runCmd} swallowkit migrations add-model <name>\` to add only the container migration.
|
|
63
63
|
- Read \`data.warnings\`. \`user-scoped-data-without-auth\` means generated CRUD would expose every user's documents;
|
|
64
64
|
add auth (\`swallowkit-add-auth\`) and owner-scoped ai-authored endpoints before exposing the model.
|
|
65
|
+
- \`auth-policy-without-auth\` blocks generation before file writes: configure authentication with plan/apply auth,
|
|
66
|
+
then re-plan. Do not remove a required policy to bypass this prerequisite.
|
|
67
|
+
For named \`auth.schemes\`, define named authorization policies selecting schemes/roles and reference them from
|
|
68
|
+
\`authPolicy.policy\` or read/write names; role-only declarations block with \`auth-policy-requires-named-policy\`.
|
|
69
|
+
Single-provider \`auth.provider\` supports role lists only; named policies block with \`auth-named-policy-requires-named-scheme\`.
|
|
70
|
+
- Scaffold appends missing exports to \`shared/index.ts\` for hand-authored models too.
|
|
71
|
+
Manual exports are allowed; no managed markers exist in that file.
|
|
65
72
|
4. Apply:
|
|
66
73
|
\`${runCmd} swallowkit machine apply scaffold --plan <planId>\`
|
|
67
74
|
5. Verify:
|
|
@@ -160,7 +167,7 @@ the scoping key from the verified principal, never from the request body.
|
|
|
160
167
|
title: "Verify and repair loop",
|
|
161
168
|
skillName: "swallowkit-verify-repair",
|
|
162
169
|
skillDescription:
|
|
163
|
-
"Converge a SwallowKit project to a verified state: run structure/drift/typecheck (and build/lint/test) checks, read evidence with explain failure, repair, and repeat. Use after any code or schema change, when verification fails, when the build/lint/tests are broken, or before finishing a task.",
|
|
170
|
+
"Converge a SwallowKit project to a verified state: run structure/drift/typecheck/backend-build (and build/lint/test) checks, read evidence with explain failure, repair, and repeat. Use after any code or schema change, when verification fails, when the build/lint/tests are broken, or before finishing a task.",
|
|
164
171
|
content: `# Workflow: Verify and repair
|
|
165
172
|
|
|
166
173
|
Goal: converge the project to a verified state after any change.
|
|
@@ -168,14 +175,16 @@ Goal: converge the project to a verified state after any change.
|
|
|
168
175
|
## Loop
|
|
169
176
|
|
|
170
177
|
1. \`${runCmd} swallowkit machine verify project\`
|
|
171
|
-
- default checks: structure, drift, typecheck (+ project-specific checks from swallowkit.config \`verify.checks\`)
|
|
172
|
-
-
|
|
178
|
+
- default checks: structure, drift, typecheck, backend-build (+ project-specific checks from swallowkit.config \`verify.checks\`)
|
|
179
|
+
- backend-build: standalone C# \`dotnet build\`, TypeScript \`tsc --noEmit\`, Python 3 \`compileall\` (syntax only).
|
|
180
|
+
Missing toolchains are skipped with a reason, not proof of a working backend. Restore them and re-run.
|
|
181
|
+
- opt-in heavier checks: \`--checks "structure,drift,typecheck,backend-build,build,lint,test"\` (quote CSV in PowerShell)
|
|
173
182
|
2. If \`summary.done\` is false:
|
|
174
183
|
- \`${runCmd} swallowkit machine explain failure\` → evidence (logTail, findings, violations) and suggestedActions per check.
|
|
175
184
|
- Repair:
|
|
176
185
|
- \`drift\` failures → follow each finding's \`repairAction\` (usually re-run plan/apply scaffold).
|
|
177
186
|
- \`structure\` failures → follow \`suggestedFix\` per violation.
|
|
178
|
-
- \`typecheck\`/\`build\`/\`lint\`/\`test\` failures → fix the code
|
|
187
|
+
- \`typecheck\`/\`backend-build\`/\`build\`/\`lint\`/\`test\` failures → fix the source code, not generated artifacts.
|
|
179
188
|
3. Repeat until \`summary.done: true\`.
|
|
180
189
|
|
|
181
190
|
## Exit criteria
|
|
@@ -254,7 +263,8 @@ under \`.github/skills/\` so skills-aware agents load them automatically.
|
|
|
254
263
|
|
|
255
264
|
- **ai-authored** (write freely): \`shared/models/\`, business logic, custom pages/components, tests.
|
|
256
265
|
- **deterministic** (never hand-edit): artifacts generated by SwallowKit (see \`.swallowkit/artifacts.json\`) and \`.swallowkit/\` metadata. Change models/config and regenerate via plan/apply.
|
|
257
|
-
- **shared
|
|
266
|
+
- **shared**: \`shared/index.ts\` allows manual exports (no markers; SwallowKit appends missing entries).
|
|
267
|
+
For Python \`functions/function_app.py\`, edit outside the registration marker.
|
|
258
268
|
- **infrastructure**: baseline \`infra/\` files are checksum-locked by \`infra/migrations/manifest.json\`; add resources as new migrations (\`swallowkit create-migration <slug>\`, \`swallowkit migrations add-model <model>\`).
|
|
259
269
|
|
|
260
270
|
Full machine-readable contract: \`${runCmd} swallowkit machine inspect boundaries\`
|
|
@@ -117,8 +117,7 @@ export function generateNamedAuthRouterCSharp(config: NormalizedAuthConfig): str
|
|
|
117
117
|
using System.Text;
|
|
118
118
|
using System.Text.Json;
|
|
119
119
|
using Microsoft.Azure.Functions.Worker.Http;
|
|
120
|
-
using Functions.Auth.Schemes;
|
|
121
|
-
namespace Functions.Auth;
|
|
120
|
+
${externalCases ? "using Functions.Auth.Schemes;\n" : ""}namespace Functions.Auth;
|
|
122
121
|
public record AuthPrincipal(string Subject, string Scheme, string Issuer, string[] Roles, IReadOnlyDictionary<string, object?> Claims)
|
|
123
122
|
{
|
|
124
123
|
[Obsolete("Use Subject")] public string UserId => Subject;
|
|
@@ -171,15 +170,15 @@ ${externalCases}
|
|
|
171
170
|
export function generateNamedAuthRouterPython(config: NormalizedAuthConfig): string {
|
|
172
171
|
const external = Object.entries(config.schemes).filter(([,s])=>s.provider === "external-token");
|
|
173
172
|
const imports = external.map(([n])=>`from auth.schemes.${schemeSlug(n).replace(/-/g,"_")}.verifier import verify_external_token as verify_${safeIdentifier(n)}, ExternalTokenVerificationError as Error_${safeIdentifier(n)}`).join("\n");
|
|
174
|
-
const cases = external.map(([n], index)=>`
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
173
|
+
const cases = external.map(([n], index)=>` ${index === 0 ? "if" : "elif"} scheme == ${JSON.stringify(n)}:
|
|
174
|
+
try:
|
|
175
|
+
value = verify_${safeIdentifier(n)}(bearer)
|
|
176
|
+
except Error_${safeIdentifier(n)} as exc:
|
|
177
|
+
raise AuthError(503 if exc.identity_provider_unavailable else 401, "Identity provider unavailable" if exc.identity_provider_unavailable else "Invalid token")
|
|
178
|
+
except Exception:
|
|
179
|
+
raise AuthError(503, "External token verifier unavailable")
|
|
180
|
+
if not value.get("subject") or not isinstance(value.get("roles"), list): raise AuthError(401, "Invalid external principal")
|
|
181
|
+
principal = {"subject": value["subject"], "scheme": scheme, "issuer": value.get("issuer", scheme), "roles": value["roles"], "claims": value.get("claims", {}), "userId": value["subject"]}`).join("\n");
|
|
183
182
|
return `import base64
|
|
184
183
|
import json
|
|
185
184
|
import azure.functions as func
|
|
@@ -199,14 +198,14 @@ def require_auth(req: func.HttpRequest, policy_name: str):
|
|
|
199
198
|
if len(candidates) != 1: raise AuthError(401, "Authentication required" if not candidates else "Ambiguous credentials")
|
|
200
199
|
scheme = candidates[0]
|
|
201
200
|
${cases}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
201
|
+
${external.length ? "elif" : "if"} PROVIDERS[scheme] == "swa":
|
|
202
|
+
try:
|
|
203
|
+
value=json.loads(base64.b64decode(swa).decode("utf-8")); roles=[r for r in value.get("userRoles",[]) if isinstance(r,str)]
|
|
204
|
+
if not value.get("userId") or "authenticated" not in roles: raise ValueError()
|
|
205
|
+
if ALLOWED_PROVIDERS[scheme] and value.get("identityProvider") not in ALLOWED_PROVIDERS[scheme]: raise ValueError()
|
|
206
|
+
principal={"subject":value["userId"],"scheme":scheme,"issuer":"swa:"+value.get("identityProvider", "unknown"),"roles":roles,"claims":value,"userId":value["userId"]}
|
|
207
|
+
except Exception: raise AuthError(401,"Invalid SWA client principal")
|
|
208
|
+
else: raise AuthError(401,"Authentication adapter is not configured")
|
|
210
209
|
require_roles(principal, policy.get("roles", [])); return principal
|
|
211
210
|
def require_roles(principal, roles):
|
|
212
211
|
if roles and not any(role in principal.get("roles",[]) for role in roles): raise AuthError(403,"Insufficient permissions")
|
|
@@ -939,6 +939,21 @@ function ensurePythonCodegenProjectFiles(
|
|
|
939
939
|
return requirementsPath;
|
|
940
940
|
}
|
|
941
941
|
|
|
942
|
+
/** Generated backend_models import pydantic v2 at runtime, so the deployed Functions app must install it. */
|
|
943
|
+
export const PYTHON_SCHEMA_RUNTIME_REQUIREMENT = "pydantic>=2.6,<3";
|
|
944
|
+
|
|
945
|
+
function ensurePythonRuntimeRequirement(functionsRoot: string, session: FileOperationSession): void {
|
|
946
|
+
const requirementsPath = path.join(functionsRoot, "requirements.txt");
|
|
947
|
+
const current = session.fileExists(requirementsPath) ? session.readFile(requirementsPath) : "";
|
|
948
|
+
const declared = current.split(/\r?\n/).some((line) => line.trim().split(/[<>=!~;[ ]/)[0].toLowerCase() === "pydantic");
|
|
949
|
+
if (declared) return;
|
|
950
|
+
const prefix = current && !current.endsWith("\n") ? `${current}\n` : current;
|
|
951
|
+
session.writeFile(requirementsPath, `${prefix}${PYTHON_SCHEMA_RUNTIME_REQUIREMENT}\n`, {
|
|
952
|
+
ownership: "extension-point",
|
|
953
|
+
generator: "native-schema",
|
|
954
|
+
});
|
|
955
|
+
}
|
|
956
|
+
|
|
942
957
|
async function ensureProjectLocalUvCommand(projectRoot: string): Promise<{ command: string; env: NodeJS.ProcessEnv }> {
|
|
943
958
|
const uvEnv = { ...process.env };
|
|
944
959
|
const { localUvExecutable } = getProjectLocalUvPaths(projectRoot);
|
|
@@ -1082,6 +1097,7 @@ async function generatePythonSchemaArtifacts(
|
|
|
1082
1097
|
sourceModel: string
|
|
1083
1098
|
): Promise<void> {
|
|
1084
1099
|
ensurePythonCodegenProjectFiles(functionsRoot, session, sourceModel);
|
|
1100
|
+
ensurePythonRuntimeRequirement(functionsRoot, session);
|
|
1085
1101
|
|
|
1086
1102
|
const packageRoot = path.join(outputDir, "backend_models");
|
|
1087
1103
|
const modelsRoot = path.join(packageRoot, "models");
|
package/src/core/verify/index.ts
CHANGED
|
@@ -5,16 +5,17 @@
|
|
|
5
5
|
* - structure: プロジェクト構造規約 (validateProject)
|
|
6
6
|
* - drift: 生成物と現在状態の乖離 (detectDrift)
|
|
7
7
|
* - typecheck: TypeScript 型チェック (tsc --noEmit / typecheck script)
|
|
8
|
+
* - backend-build: standalone Functions のコンパイル (C#/TypeScript) または構文検証 (Python)
|
|
8
9
|
* - build/lint/test: package.json scripts に同名 script がある場合のみ実行 (なければ skip)
|
|
9
10
|
* - custom: swallowkit.config の verify.checks で定義されたプロジェクト固有チェック
|
|
10
11
|
*
|
|
11
|
-
* 既定実行セットは structure/drift/typecheck + custom。build/lint/test は --checks で明示指定。
|
|
12
|
+
* 既定実行セットは structure/drift/typecheck/backend-build + custom。build/lint/test は --checks で明示指定。
|
|
12
13
|
*/
|
|
13
14
|
|
|
14
15
|
import * as fs from "fs";
|
|
15
16
|
import * as path from "path";
|
|
16
17
|
import { spawnSync } from "child_process";
|
|
17
|
-
import { loadConfig } from "../config";
|
|
18
|
+
import { loadConfig, loadConfigFromEnv } from "../config";
|
|
18
19
|
import { detectDrift, DriftFinding } from "../project/drift";
|
|
19
20
|
import { saveLastVerifyState } from "../project/state";
|
|
20
21
|
import { ProjectViolation, validateProject } from "../project/validation";
|
|
@@ -60,11 +61,11 @@ export interface VerifyResult {
|
|
|
60
61
|
verifiedAt: string;
|
|
61
62
|
}
|
|
62
63
|
|
|
63
|
-
export const AVAILABLE_VERIFY_CHECKS = ["structure", "drift", "typecheck", "build", "lint", "test"] as const;
|
|
64
|
+
export const AVAILABLE_VERIFY_CHECKS = ["structure", "drift", "typecheck", "backend-build", "build", "lint", "test"] as const;
|
|
64
65
|
export type VerifyCheckId = (typeof AVAILABLE_VERIFY_CHECKS)[number];
|
|
65
66
|
|
|
66
67
|
/** 既定で実行される built-in チェック。build/lint/test は明示指定時のみ。 */
|
|
67
|
-
export const DEFAULT_VERIFY_CHECKS = ["structure", "drift", "typecheck"] as const;
|
|
68
|
+
export const DEFAULT_VERIFY_CHECKS = ["structure", "drift", "typecheck", "backend-build"] as const;
|
|
68
69
|
|
|
69
70
|
async function runStructureCheck(projectRoot: string): Promise<VerifyCheckResult> {
|
|
70
71
|
const startedAt = Date.now();
|
|
@@ -137,6 +138,7 @@ interface CommandCheckSpec {
|
|
|
137
138
|
args: string[];
|
|
138
139
|
timeoutMs?: number;
|
|
139
140
|
failAction: string;
|
|
141
|
+
shell?: boolean;
|
|
140
142
|
}
|
|
141
143
|
|
|
142
144
|
function runCommandCheck(projectRoot: string, spec: CommandCheckSpec): VerifyCheckResult {
|
|
@@ -145,9 +147,10 @@ function runCommandCheck(projectRoot: string, spec: CommandCheckSpec): VerifyChe
|
|
|
145
147
|
try {
|
|
146
148
|
const result = spawnSync(spec.command, spec.args, {
|
|
147
149
|
cwd: projectRoot,
|
|
148
|
-
shell: true,
|
|
150
|
+
shell: spec.shell ?? true,
|
|
149
151
|
encoding: "utf-8",
|
|
150
152
|
timeout: spec.timeoutMs ?? 5 * 60 * 1000,
|
|
153
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
151
154
|
});
|
|
152
155
|
|
|
153
156
|
const output = `${result.stdout ?? ""}\n${result.stderr ?? ""}`;
|
|
@@ -180,6 +183,11 @@ function runCommandCheck(projectRoot: string, spec: CommandCheckSpec): VerifyChe
|
|
|
180
183
|
command: commandLabel,
|
|
181
184
|
exitCode,
|
|
182
185
|
logTail: tailLines(output, 50),
|
|
186
|
+
...(spec.id === "backend-build" ? {
|
|
187
|
+
diagnostics: [...new Set(output.split(/\r?\n/).filter((line) =>
|
|
188
|
+
/\(\d+,\d+\):\s*error\s+\w+/.test(line) ||
|
|
189
|
+
/^\s*(File ".+", line \d+|(?:Sorry: )?(?:Syntax|Indentation|Tab)Error:|\*\*\* Error compiling)/.test(line)))],
|
|
190
|
+
} : {}),
|
|
183
191
|
},
|
|
184
192
|
suggestedActions: exitCode === 0 ? [] : [spec.failAction],
|
|
185
193
|
};
|
|
@@ -201,6 +209,86 @@ function runCommandCheck(projectRoot: string, spec: CommandCheckSpec): VerifyChe
|
|
|
201
209
|
}
|
|
202
210
|
}
|
|
203
211
|
|
|
212
|
+
function backendUnavailable(reason: string): VerifyCheckResult {
|
|
213
|
+
return {
|
|
214
|
+
id: "backend-build",
|
|
215
|
+
title: "Standalone Functions compilation",
|
|
216
|
+
status: "skip",
|
|
217
|
+
durationMs: 0,
|
|
218
|
+
fixable: false,
|
|
219
|
+
evidence: { logTail: [reason], diagnostics: [reason] },
|
|
220
|
+
suggestedActions: ["Install or restore the backend toolchain, then re-run verify with --checks \"backend-build\". A skipped check is not backend validation."],
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function runBackendBuildCheck(projectRoot: string): VerifyCheckResult {
|
|
225
|
+
const functionsRoot = path.join(projectRoot, "functions");
|
|
226
|
+
if (!fs.existsSync(functionsRoot)) return backendUnavailable("functions/ not found; no standalone backend was checked.");
|
|
227
|
+
const configPath = ["swallowkit.config.json", "swallowkit.config.js", ".swallowkitrc.json"]
|
|
228
|
+
.map((name) => path.join(projectRoot, name)).find((file) => fs.existsSync(file));
|
|
229
|
+
if (!configPath) return backendUnavailable("SwallowKit configuration not found; backend language is unknown.");
|
|
230
|
+
// Same effective language as generation (getBackendLanguage): env override, then file, invalid -> typescript.
|
|
231
|
+
const configured = loadConfigFromEnv().backend?.language ?? loadConfig(configPath, true, true).backend?.language;
|
|
232
|
+
const language = configured && ["typescript", "csharp", "python"].includes(configured) ? configured : "typescript";
|
|
233
|
+
let command: string;
|
|
234
|
+
let args: string[];
|
|
235
|
+
if (language === "csharp") {
|
|
236
|
+
command = "dotnet";
|
|
237
|
+
args = ["build", "--nologo"];
|
|
238
|
+
const probe = spawnSync(command, ["--version"], { cwd: functionsRoot, encoding: "utf-8", timeout: 30_000 });
|
|
239
|
+
if (probe.error && "code" in probe.error && probe.error.code === "ENOENT") return backendUnavailable("dotnet SDK is not installed or is not on PATH; C# compilation was skipped.");
|
|
240
|
+
} else if (language === "python") {
|
|
241
|
+
const venvPython = process.platform === "win32"
|
|
242
|
+
? path.join(functionsRoot, ".venv", "Scripts", "python.exe")
|
|
243
|
+
: path.join(functionsRoot, ".venv", "bin", "python");
|
|
244
|
+
const candidates = fs.existsSync(venvPython)
|
|
245
|
+
? [{ command: venvPython, args: [] as string[] }]
|
|
246
|
+
: process.platform === "win32"
|
|
247
|
+
? [{ command: "python", args: [] }, { command: "python3", args: [] }, { command: "py", args: ["-3"] }]
|
|
248
|
+
: [{ command: "python3", args: [] }, { command: "python", args: [] }];
|
|
249
|
+
const python = candidates.find((candidate) => {
|
|
250
|
+
const probe = spawnSync(candidate.command, [...candidate.args, "-c", "import sys; sys.exit(0 if sys.version_info.major == 3 else 1)"], {
|
|
251
|
+
cwd: functionsRoot, encoding: "utf-8", timeout: 30_000,
|
|
252
|
+
});
|
|
253
|
+
// Preserve execution errors for an installed interpreter instead of silently falling back.
|
|
254
|
+
return probe.status === 0 || (probe.error && (!("code" in probe.error) || probe.error.code !== "ENOENT"));
|
|
255
|
+
});
|
|
256
|
+
if (!python) return backendUnavailable("Python 3 is not available in functions/.venv or on PATH; Python syntax compilation was skipped.");
|
|
257
|
+
command = python.command;
|
|
258
|
+
// -f: never trust cached bytecode, which can survive same-size edits within one mtime second.
|
|
259
|
+
args = [...python.args, "-m", "compileall", "-q", "-f", "-x", "[/\\\\](?:\\.venv|\\.codegen-venv|node_modules|__pycache__)(?:[/\\\\]|$)", "."];
|
|
260
|
+
} else {
|
|
261
|
+
let tscPath: string;
|
|
262
|
+
try {
|
|
263
|
+
tscPath = require.resolve("typescript/bin/tsc", { paths: [functionsRoot] });
|
|
264
|
+
} catch (error) {
|
|
265
|
+
if (error instanceof Error && "code" in error && error.code === "MODULE_NOT_FOUND") {
|
|
266
|
+
return backendUnavailable("The project's TypeScript compiler is not installed; restore dependencies before verifying functions/.");
|
|
267
|
+
}
|
|
268
|
+
throw error;
|
|
269
|
+
}
|
|
270
|
+
const sharedBuild = runSharedBuildIfPresent(projectRoot);
|
|
271
|
+
if (sharedBuild.ran && !sharedBuild.ok) {
|
|
272
|
+
return {
|
|
273
|
+
id: "backend-build", title: "Standalone Functions compilation", status: sharedBuild.unavailable ? "error" : "fail",
|
|
274
|
+
durationMs: 0, fixable: !sharedBuild.unavailable,
|
|
275
|
+
evidence: { command: sharedBuild.command, logTail: sharedBuild.logTail,
|
|
276
|
+
...(sharedBuild.unavailable ? { errorCode: "verification-command-unavailable" as const } : {}) },
|
|
277
|
+
suggestedActions: ["Fix the shared package build before compiling the TypeScript backend."],
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
command = process.execPath;
|
|
281
|
+
args = [tscPath, "--project", "tsconfig.json", "--noEmit"];
|
|
282
|
+
}
|
|
283
|
+
const result = runCommandCheck(functionsRoot, {
|
|
284
|
+
id: "backend-build", title: "Standalone Functions compilation", command, args, shell: false,
|
|
285
|
+
timeoutMs: 10 * 60 * 1000,
|
|
286
|
+
failAction: "Fix the backend compilation errors in evidence.diagnostics / evidence.logTail, then re-run verify.",
|
|
287
|
+
});
|
|
288
|
+
result.evidence.command = `${result.evidence.command} (in functions/)`;
|
|
289
|
+
return result;
|
|
290
|
+
}
|
|
291
|
+
|
|
204
292
|
function readPackageScripts(projectRoot: string): Record<string, string> {
|
|
205
293
|
const packageJsonPath = path.join(projectRoot, "package.json");
|
|
206
294
|
if (!fs.existsSync(packageJsonPath)) return {};
|
|
@@ -430,6 +518,8 @@ export async function runVerify(
|
|
|
430
518
|
checks.push(await runDriftCheck(projectRoot));
|
|
431
519
|
} else if (id === "typecheck") {
|
|
432
520
|
checks.push(runTypecheckCheck(projectRoot));
|
|
521
|
+
} else if (id === "backend-build") {
|
|
522
|
+
checks.push(runBackendBuildCheck(projectRoot));
|
|
433
523
|
} else if (id === "build" || id === "lint" || id === "test") {
|
|
434
524
|
checks.push(runScriptCheck(projectRoot, id));
|
|
435
525
|
} else {
|
package/src/machine/contracts.ts
CHANGED
package/src/machine/errors.ts
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
|
-
import type { MachineOperationStatus } from "./contracts";
|
|
1
|
+
import type { MachineNextAction, MachineOperationStatus } from "./contracts";
|
|
2
2
|
|
|
3
3
|
export class MachineCommandError extends Error {
|
|
4
4
|
readonly code: string;
|
|
5
5
|
readonly details?: unknown;
|
|
6
6
|
readonly status?: MachineOperationStatus;
|
|
7
|
+
readonly nextActions?: MachineNextAction[];
|
|
7
8
|
|
|
8
|
-
constructor(code: string, message: string, details?: unknown, status?: MachineOperationStatus) {
|
|
9
|
+
constructor(code: string, message: string, details?: unknown, status?: MachineOperationStatus, nextActions?: MachineNextAction[]) {
|
|
9
10
|
super(message);
|
|
10
11
|
this.name = "MachineCommandError";
|
|
11
12
|
this.code = code;
|
|
12
13
|
this.details = details;
|
|
13
14
|
this.status = status;
|
|
15
|
+
this.nextActions = nextActions;
|
|
14
16
|
}
|
|
15
17
|
}
|
|
16
18
|
|
package/src/machine/index.ts
CHANGED
|
@@ -58,6 +58,7 @@ function writeMachineError(command: string, error: unknown): void {
|
|
|
58
58
|
ok: false,
|
|
59
59
|
command,
|
|
60
60
|
status: resolveMachineErrorStatus(error),
|
|
61
|
+
...(error instanceof MachineCommandError && error.nextActions?.length ? { nextActions: error.nextActions } : {}),
|
|
61
62
|
error: toMachineError(error),
|
|
62
63
|
};
|
|
63
64
|
writeMachineResponse(response);
|
|
@@ -567,10 +568,10 @@ function createMachineProgram(): Command {
|
|
|
567
568
|
const verify = new Command("verify");
|
|
568
569
|
verify
|
|
569
570
|
.command("project")
|
|
570
|
-
.description("Run verification checks (structure, drift, typecheck) with machine-readable evidence")
|
|
571
|
+
.description("Run verification checks (structure, drift, typecheck, backend-build) with machine-readable evidence")
|
|
571
572
|
.option(
|
|
572
573
|
"--checks <ids>",
|
|
573
|
-
"Comma-separated check ids to run (structure,drift,typecheck,build,lint,test, plus custom ids from swallowkit.config verify.checks)"
|
|
574
|
+
"Comma-separated check ids to run (structure,drift,typecheck,backend-build,build,lint,test, plus custom ids from swallowkit.config verify.checks); quote CSV values in PowerShell"
|
|
574
575
|
)
|
|
575
576
|
.option("--compact", "Suppress info-severity findings/violations from evidence to keep agent-facing output small", false)
|
|
576
577
|
.action(async (options: { checks?: string; compact?: boolean }) => {
|
package/src/mcp/index.ts
CHANGED
|
@@ -5,7 +5,7 @@ import * as path from "path";
|
|
|
5
5
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6
6
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
7
7
|
import * as z from "zod/v4";
|
|
8
|
-
import type { MachineNextAction, MachineOperationStatus, MachineResponse } from "../machine/contracts";
|
|
8
|
+
import type { MachineErrorResponse, MachineNextAction, MachineOperationStatus, MachineResponse } from "../machine/contracts";
|
|
9
9
|
import { getSwallowKitVersion } from "../version";
|
|
10
10
|
import { detectFromProject, getCommands } from "../utils/package-manager";
|
|
11
11
|
import {
|
|
@@ -25,7 +25,12 @@ interface MachineSuccessPayload<TData> {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
type MachineCliRunner = (args: string[]) => Promise<{ stdout: string; stderr: string; exitCode: number }>;
|
|
28
|
-
type ToolContentResult = { content: Array<{ type: "text"; text: string }
|
|
28
|
+
type ToolContentResult = { content: Array<{ type: "text"; text: string }>; isError?: boolean };
|
|
29
|
+
class MachineCliError extends Error {
|
|
30
|
+
constructor(message: string, readonly response: MachineErrorResponse) {
|
|
31
|
+
super(message);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
29
34
|
type ToolDefinition = {
|
|
30
35
|
name: string;
|
|
31
36
|
description: string;
|
|
@@ -73,7 +78,7 @@ async function executeMachineCommand<TData>(
|
|
|
73
78
|
const detailsSuffix =
|
|
74
79
|
parsed.error.details !== undefined ? `\ndetails: ${JSON.stringify(parsed.error.details)}` : "";
|
|
75
80
|
const statusSuffix = parsed.status ? ` (status: ${parsed.status})` : "";
|
|
76
|
-
throw new
|
|
81
|
+
throw new MachineCliError(`[${parsed.error.code}] ${parsed.error.message}${statusSuffix}${detailsSuffix}`, parsed);
|
|
77
82
|
}
|
|
78
83
|
|
|
79
84
|
return parsed;
|
|
@@ -308,7 +313,7 @@ export function buildSwallowKitToolDefinitions(
|
|
|
308
313
|
},
|
|
309
314
|
{
|
|
310
315
|
name: "swallowkit_verify_project",
|
|
311
|
-
description: "Run verification checks (structure, drift, typecheck by default; build, lint, test and custom checks from swallowkit.config verify.checks are also available) and return machine-readable evidence. summary.done=true means
|
|
316
|
+
description: "Run verification checks (structure, drift, typecheck, backend-build by default; build, lint, test and custom checks from swallowkit.config verify.checks are also available) and return machine-readable evidence. backend-build compiles standalone C#/TypeScript Functions or checks Python syntax; unavailable toolchains are skipped with a reason. summary.done=true means no checks failed, not that skipped checks were validated or runtime/auth flows work. Set compact=true to suppress info-severity findings and keep the output small.",
|
|
312
317
|
inputSchema: z.object({
|
|
313
318
|
checks: z.array(z.string()).optional(),
|
|
314
319
|
compact: z.boolean().optional(),
|
|
@@ -452,8 +457,16 @@ export function buildSwallowKitToolDefinitions(
|
|
|
452
457
|
return definitions.map((definition) => ({
|
|
453
458
|
...definition,
|
|
454
459
|
handler: async (input: unknown) => {
|
|
455
|
-
|
|
456
|
-
|
|
460
|
+
try {
|
|
461
|
+
const result = await definition.handler(input);
|
|
462
|
+
return jsonTextContent(withSwallowKitMetadata(JSON.parse(result.content[0].text)));
|
|
463
|
+
} catch (error) {
|
|
464
|
+
// Prerequisite failures carry nextActions; keep them structured so agents can follow them.
|
|
465
|
+
if (error instanceof MachineCliError && error.response.nextActions?.length) {
|
|
466
|
+
return { ...jsonTextContent(withSwallowKitMetadata(error.response)), isError: true };
|
|
467
|
+
}
|
|
468
|
+
throw error;
|
|
469
|
+
}
|
|
457
470
|
},
|
|
458
471
|
}));
|
|
459
472
|
}
|