swallowkit 1.0.0-beta.55 → 1.0.0-beta.57
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.d.ts.map +1 -1
- package/dist/cli/commands/add-auth.js +154 -157
- package/dist/cli/commands/add-auth.js.map +1 -1
- package/dist/cli/commands/init.d.ts +5 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +178 -25
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/scaffold.d.ts +7 -2
- package/dist/cli/commands/scaffold.d.ts.map +1 -1
- package/dist/cli/commands/scaffold.js +113 -123
- package/dist/cli/commands/scaffold.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +127 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/core/config.d.ts +1 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +8 -4
- package/dist/core/config.js.map +1 -1
- package/dist/core/operations/auth-operations.d.ts +57 -0
- package/dist/core/operations/auth-operations.d.ts.map +1 -0
- package/dist/core/operations/auth-operations.js +269 -0
- package/dist/core/operations/auth-operations.js.map +1 -0
- package/dist/core/operations/create-model.d.ts.map +1 -1
- package/dist/core/operations/create-model.js +23 -13
- package/dist/core/operations/create-model.js.map +1 -1
- package/dist/core/operations/file-session.d.ts +56 -0
- package/dist/core/operations/file-session.d.ts.map +1 -0
- package/dist/core/operations/file-session.js +203 -0
- package/dist/core/operations/file-session.js.map +1 -0
- package/dist/core/operations/provision-operations.d.ts +52 -0
- package/dist/core/operations/provision-operations.d.ts.map +1 -0
- package/dist/core/operations/provision-operations.js +211 -0
- package/dist/core/operations/provision-operations.js.map +1 -0
- package/dist/core/operations/runtime.d.ts +5 -0
- package/dist/core/operations/runtime.d.ts.map +1 -1
- package/dist/core/operations/runtime.js +23 -0
- package/dist/core/operations/runtime.js.map +1 -1
- package/dist/core/operations/scaffold-apply.d.ts +21 -0
- package/dist/core/operations/scaffold-apply.d.ts.map +1 -0
- package/dist/core/operations/scaffold-apply.js +57 -0
- package/dist/core/operations/scaffold-apply.js.map +1 -0
- package/dist/core/operations/scaffold-machine.d.ts.map +1 -1
- package/dist/core/operations/scaffold-machine.js +1 -13
- package/dist/core/operations/scaffold-machine.js.map +1 -1
- package/dist/core/operations/scaffold-plan.d.ts +43 -0
- package/dist/core/operations/scaffold-plan.d.ts.map +1 -0
- package/dist/core/operations/scaffold-plan.js +187 -0
- package/dist/core/operations/scaffold-plan.js.map +1 -0
- package/dist/core/project/artifacts.d.ts +58 -0
- package/dist/core/project/artifacts.d.ts.map +1 -0
- package/dist/core/project/artifacts.js +179 -0
- package/dist/core/project/artifacts.js.map +1 -0
- package/dist/core/project/boundaries.d.ts +50 -0
- package/dist/core/project/boundaries.d.ts.map +1 -0
- package/dist/core/project/boundaries.js +117 -0
- package/dist/core/project/boundaries.js.map +1 -0
- package/dist/core/project/drift.d.ts +29 -0
- package/dist/core/project/drift.d.ts.map +1 -0
- package/dist/core/project/drift.js +188 -0
- package/dist/core/project/drift.js.map +1 -0
- package/dist/core/project/infra.d.ts +49 -0
- package/dist/core/project/infra.d.ts.map +1 -0
- package/dist/core/project/infra.js +167 -0
- package/dist/core/project/infra.js.map +1 -0
- package/dist/core/project/state.d.ts +15 -0
- package/dist/core/project/state.d.ts.map +1 -0
- package/dist/core/project/state.js +106 -0
- package/dist/core/project/state.js.map +1 -0
- package/dist/core/project/workflows.d.ts +43 -0
- package/dist/core/project/workflows.d.ts.map +1 -0
- package/dist/core/project/workflows.js +255 -0
- package/dist/core/project/workflows.js.map +1 -0
- package/dist/core/scaffold/auth-generator.d.ts.map +1 -1
- package/dist/core/scaffold/auth-generator.js +24 -27
- package/dist/core/scaffold/auth-generator.js.map +1 -1
- package/dist/core/scaffold/functions-generator.js +9 -9
- package/dist/core/scaffold/native-schema-generator.d.ts +7 -1
- package/dist/core/scaffold/native-schema-generator.d.ts.map +1 -1
- package/dist/core/scaffold/native-schema-generator.js +104 -47
- package/dist/core/scaffold/native-schema-generator.js.map +1 -1
- package/dist/core/scaffold/nextjs-generator.js +6 -6
- package/dist/core/scaffold/ui-generator.js +19 -19
- package/dist/core/scaffold/ui-generator.js.map +1 -1
- package/dist/core/verify/index.d.ts +62 -0
- package/dist/core/verify/index.d.ts.map +1 -0
- package/dist/core/verify/index.js +343 -0
- package/dist/core/verify/index.js.map +1 -0
- package/dist/machine/contracts.d.ts +8 -0
- package/dist/machine/contracts.d.ts.map +1 -1
- package/dist/machine/errors.d.ts +4 -1
- package/dist/machine/errors.d.ts.map +1 -1
- package/dist/machine/errors.js +9 -1
- package/dist/machine/errors.js.map +1 -1
- package/dist/machine/index.d.ts.map +1 -1
- package/dist/machine/index.js +350 -16
- package/dist/machine/index.js.map +1 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +202 -1
- package/dist/mcp/index.js.map +1 -1
- package/dist/types/index.d.ts +14 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/__snapshots__/functions-generator.test.ts.snap +17 -17
- package/src/__tests__/__snapshots__/nextjs-generator.test.ts.snap +6 -6
- package/src/__tests__/__snapshots__/ui-generator.test.ts.snap +11 -11
- package/src/__tests__/agent-loop-phase34.test.ts +449 -0
- package/src/__tests__/file-session.test.ts +98 -0
- package/src/__tests__/init.test.ts +50 -0
- package/src/__tests__/mcp.test.ts +113 -5
- package/src/__tests__/plan-apply.test.ts +461 -0
- package/src/__tests__/scaffold.test.ts +2 -2
- package/src/cli/commands/add-auth.ts +164 -164
- package/src/cli/commands/init.ts +186 -24
- package/src/cli/commands/scaffold.ts +136 -130
- package/src/cli/index.ts +148 -2
- package/src/core/config.ts +5 -4
- package/src/core/operations/auth-operations.ts +353 -0
- package/src/core/operations/create-model.ts +25 -13
- package/src/core/operations/file-session.ts +222 -0
- package/src/core/operations/provision-operations.ts +297 -0
- package/src/core/operations/runtime.ts +23 -0
- package/src/core/operations/scaffold-apply.ts +106 -0
- package/src/core/operations/scaffold-machine.ts +2 -16
- package/src/core/operations/scaffold-plan.ts +215 -0
- package/src/core/project/artifacts.ts +205 -0
- package/src/core/project/boundaries.ts +170 -0
- package/src/core/project/drift.ts +202 -0
- package/src/core/project/infra.ts +196 -0
- package/src/core/project/state.ts +77 -0
- package/src/core/project/workflows.ts +249 -0
- package/src/core/scaffold/auth-generator.ts +24 -27
- package/src/core/scaffold/functions-generator.ts +9 -9
- package/src/core/scaffold/native-schema-generator.ts +152 -70
- package/src/core/scaffold/nextjs-generator.ts +6 -6
- package/src/core/scaffold/ui-generator.ts +19 -19
- package/src/core/verify/index.ts +392 -0
- package/src/machine/contracts.ts +16 -0
- package/src/machine/errors.ts +12 -1
- package/src/machine/index.ts +444 -15
- package/src/mcp/index.ts +198 -2
- package/src/types/index.ts +17 -0
package/src/machine/index.ts
CHANGED
|
@@ -1,19 +1,44 @@
|
|
|
1
1
|
import { Command, CommanderError } from "commander";
|
|
2
|
+
import { isSwallowKitProject } from "../core/config";
|
|
2
3
|
import { createModelOperation } from "../core/operations/create-model";
|
|
4
|
+
import { applyScaffoldOperation } from "../core/operations/scaffold-apply";
|
|
5
|
+
import { applyAuthOperation, planAuthOperation } from "../core/operations/auth-operations";
|
|
6
|
+
import { applyProvisionOperation, planProvisionOperation } from "../core/operations/provision-operations";
|
|
7
|
+
import { ProcessExitInterceptError } from "../core/operations/runtime";
|
|
8
|
+
import { inspectInfra } from "../core/project/infra";
|
|
3
9
|
import { runMachineScaffoldOperation } from "../core/operations/scaffold-machine";
|
|
10
|
+
import { planScaffoldOperation } from "../core/operations/scaffold-plan";
|
|
11
|
+
import { inspectArtifacts } from "../core/project/artifacts";
|
|
12
|
+
import { inspectBoundaries } from "../core/project/boundaries";
|
|
13
|
+
import { detectDrift } from "../core/project/drift";
|
|
4
14
|
import { loadProjectManifest } from "../core/project/manifest";
|
|
15
|
+
import { loadLastVerifyState } from "../core/project/state";
|
|
5
16
|
import { validateProject } from "../core/project/validation";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
17
|
+
import { explainVerifyFailure, runVerify, VerifyResult } from "../core/verify";
|
|
18
|
+
import {
|
|
19
|
+
MachineErrorResponse,
|
|
20
|
+
MachineNextAction,
|
|
21
|
+
MachineOperationStatus,
|
|
22
|
+
MachineResponse,
|
|
23
|
+
MachineSuccessResponse,
|
|
24
|
+
} from "./contracts";
|
|
25
|
+
import { MachineCommandError, resolveMachineErrorStatus, toMachineError } from "./errors";
|
|
8
26
|
|
|
9
27
|
function writeMachineResponse<TData>(response: MachineResponse<TData>): void {
|
|
10
28
|
process.stdout.write(`${JSON.stringify(response, null, 2)}\n`);
|
|
11
29
|
}
|
|
12
30
|
|
|
13
|
-
|
|
31
|
+
interface MachineResponseMeta {
|
|
32
|
+
status?: MachineOperationStatus;
|
|
33
|
+
nextActions?: MachineNextAction[];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function writeMachineSuccess<TData>(command: string, data: TData, meta?: MachineResponseMeta): void {
|
|
14
37
|
const response: MachineSuccessResponse<TData> = {
|
|
15
38
|
ok: true,
|
|
16
39
|
command,
|
|
40
|
+
status: meta?.status ?? "complete",
|
|
41
|
+
...(meta?.nextActions && meta.nextActions.length > 0 ? { nextActions: meta.nextActions } : {}),
|
|
17
42
|
data,
|
|
18
43
|
};
|
|
19
44
|
writeMachineResponse(response);
|
|
@@ -23,17 +48,75 @@ function writeMachineError(command: string, error: unknown): void {
|
|
|
23
48
|
const response: MachineErrorResponse = {
|
|
24
49
|
ok: false,
|
|
25
50
|
command,
|
|
51
|
+
status: resolveMachineErrorStatus(error),
|
|
26
52
|
error: toMachineError(error),
|
|
27
53
|
};
|
|
28
54
|
writeMachineResponse(response);
|
|
29
55
|
}
|
|
30
56
|
|
|
57
|
+
/** 書き込み系操作の前提チェック。プロジェクト外なら明確な blocked エラーを返す。 */
|
|
58
|
+
function ensureProjectForMachine(): void {
|
|
59
|
+
if (!isSwallowKitProject()) {
|
|
60
|
+
throw new MachineCommandError(
|
|
61
|
+
"not-a-swallowkit-project",
|
|
62
|
+
`No SwallowKit project found in ${process.cwd()}. Expected swallowkit.config.js, swallowkit.config.json, or .swallowkitrc.json in the current directory. Run this command from the project root, or create a project with "swallowkit init".`,
|
|
63
|
+
{ cwd: process.cwd() },
|
|
64
|
+
"blocked"
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* レガシーな人間向けコードパスが process.exit を直接呼んでも、
|
|
71
|
+
* JSON envelope なしにプロセスが死なないようにする安全網。
|
|
72
|
+
* console.error の出力を集めてエラーメッセージとして返す。
|
|
73
|
+
*/
|
|
74
|
+
async function runActionWithExitGuard<TData>(action: () => Promise<TData>): Promise<TData> {
|
|
75
|
+
const capturedErrors: string[] = [];
|
|
76
|
+
const originalError = console.error;
|
|
77
|
+
console.error = (...args: unknown[]) => {
|
|
78
|
+
const text = args
|
|
79
|
+
.map((arg) => (typeof arg === "string" ? arg : arg instanceof Error ? arg.stack || arg.message : JSON.stringify(arg)))
|
|
80
|
+
.join(" ");
|
|
81
|
+
capturedErrors.push(text);
|
|
82
|
+
process.stderr.write(`${text}\n`);
|
|
83
|
+
};
|
|
84
|
+
const originalExit = process.exit;
|
|
85
|
+
process.exit = ((code?: number | string | null) => {
|
|
86
|
+
const normalized = typeof code === "number" ? code : typeof process.exitCode === "number" ? process.exitCode : 1;
|
|
87
|
+
throw new ProcessExitInterceptError(normalized);
|
|
88
|
+
}) as typeof process.exit;
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
return await action();
|
|
92
|
+
} catch (error) {
|
|
93
|
+
if (error instanceof ProcessExitInterceptError) {
|
|
94
|
+
const message = capturedErrors
|
|
95
|
+
.map((entry) => entry.replace(/[❌💡]\s*/gu, "").trim())
|
|
96
|
+
.filter((entry) => entry.length > 0)
|
|
97
|
+
.join("\n");
|
|
98
|
+
throw new MachineCommandError(
|
|
99
|
+
"operation-aborted",
|
|
100
|
+
message || `Operation aborted with exit code ${error.exitCode}.`,
|
|
101
|
+
undefined,
|
|
102
|
+
"failed"
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
throw error;
|
|
106
|
+
} finally {
|
|
107
|
+
console.error = originalError;
|
|
108
|
+
process.exit = originalExit;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
31
112
|
async function handleMachineAction<TData>(
|
|
32
113
|
command: string,
|
|
33
|
-
action: () => Promise<TData
|
|
114
|
+
action: () => Promise<TData>,
|
|
115
|
+
meta?: (data: TData) => MachineResponseMeta
|
|
34
116
|
): Promise<void> {
|
|
35
117
|
try {
|
|
36
|
-
|
|
118
|
+
const data = await runActionWithExitGuard(action);
|
|
119
|
+
writeMachineSuccess(command, data, meta?.(data));
|
|
37
120
|
} catch (error) {
|
|
38
121
|
writeMachineError(command, error);
|
|
39
122
|
process.exitCode = 1;
|
|
@@ -45,11 +128,7 @@ function createMachineProgram(): Command {
|
|
|
45
128
|
program
|
|
46
129
|
.name("swallowkit machine")
|
|
47
130
|
.description("SwallowKit machine-readable CLI for AI and MCP integrations")
|
|
48
|
-
.showHelpAfterError(false)
|
|
49
|
-
.configureOutput({
|
|
50
|
-
writeErr: () => undefined,
|
|
51
|
-
writeOut: () => undefined,
|
|
52
|
-
});
|
|
131
|
+
.showHelpAfterError(false);
|
|
53
132
|
|
|
54
133
|
const inspect = new Command("inspect");
|
|
55
134
|
inspect
|
|
@@ -94,6 +173,49 @@ function createMachineProgram(): Command {
|
|
|
94
173
|
});
|
|
95
174
|
});
|
|
96
175
|
|
|
176
|
+
inspect
|
|
177
|
+
.command("artifacts")
|
|
178
|
+
.description("Inspect generated artifacts and their ownership from the artifact ledger")
|
|
179
|
+
.action(async () => {
|
|
180
|
+
await handleMachineAction("inspect-artifacts", async () => inspectArtifacts());
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
inspect
|
|
184
|
+
.command("boundaries")
|
|
185
|
+
.description("Return the responsibility boundary contract between AI free-form authoring and deterministic generation")
|
|
186
|
+
.action(async () => {
|
|
187
|
+
await handleMachineAction("inspect-boundaries", async () => inspectBoundaries());
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
inspect
|
|
191
|
+
.command("infra")
|
|
192
|
+
.description("Inspect Bicep infrastructure assets (params, modules, outputs, container wiring) without calling Azure")
|
|
193
|
+
.action(async () => {
|
|
194
|
+
await handleMachineAction("inspect-infra", async () => inspectInfra());
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
inspect
|
|
198
|
+
.command("drift")
|
|
199
|
+
.description("Detect drift between generated artifacts and the current project state")
|
|
200
|
+
.action(async () => {
|
|
201
|
+
await handleMachineAction(
|
|
202
|
+
"inspect-drift",
|
|
203
|
+
async () => detectDrift(),
|
|
204
|
+
(result) => ({
|
|
205
|
+
status: "complete",
|
|
206
|
+
nextActions:
|
|
207
|
+
result.findings.length > 0
|
|
208
|
+
? [
|
|
209
|
+
{
|
|
210
|
+
command: "swallowkit machine verify project",
|
|
211
|
+
description: "Run full verification to confirm the project is in a consistent state.",
|
|
212
|
+
},
|
|
213
|
+
]
|
|
214
|
+
: [],
|
|
215
|
+
})
|
|
216
|
+
);
|
|
217
|
+
});
|
|
218
|
+
|
|
97
219
|
const validate = new Command("validate");
|
|
98
220
|
validate
|
|
99
221
|
.command("project")
|
|
@@ -112,6 +234,7 @@ function createMachineProgram(): Command {
|
|
|
112
234
|
.option("--overwrite <mode>", "Overwrite policy: always | never", "never")
|
|
113
235
|
.action(async (names: string[], options: { modelsDir?: string; connector?: string; overwrite?: string }) => {
|
|
114
236
|
await handleMachineAction("generate-model", async () => {
|
|
237
|
+
ensureProjectForMachine();
|
|
115
238
|
if (options.overwrite !== "always" && options.overwrite !== "never") {
|
|
116
239
|
throw new MachineCommandError(
|
|
117
240
|
"invalid-overwrite-mode",
|
|
@@ -136,17 +259,273 @@ function createMachineProgram(): Command {
|
|
|
136
259
|
.option("--api-dir <dir>", "API routes directory", "app/api")
|
|
137
260
|
.option("--api-only", "Skip UI components; still update Functions, BFF routes, OpenAPI, and native schema assets", false)
|
|
138
261
|
.action(async (model: string, options: { functionsDir?: string; apiDir?: string; apiOnly?: boolean }) => {
|
|
139
|
-
await handleMachineAction("generate-scaffold", async () =>
|
|
262
|
+
await handleMachineAction("generate-scaffold", async () => {
|
|
263
|
+
ensureProjectForMachine();
|
|
264
|
+
return runMachineScaffoldOperation({
|
|
140
265
|
model,
|
|
141
266
|
functionsDir: options.functionsDir,
|
|
142
267
|
apiDir: options.apiDir,
|
|
143
268
|
apiOnly: options.apiOnly,
|
|
144
|
-
})
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
const plan = new Command("plan");
|
|
274
|
+
plan
|
|
275
|
+
.command("scaffold")
|
|
276
|
+
.description("Compute the scaffold change plan without writing any files")
|
|
277
|
+
.argument("<model>", "Model file or model name")
|
|
278
|
+
.option("--functions-dir <dir>", "Functions directory", "functions")
|
|
279
|
+
.option("--api-dir <dir>", "API routes directory", "app/api")
|
|
280
|
+
.option("--api-only", "Skip UI components in the plan", false)
|
|
281
|
+
.action(async (model: string, options: { functionsDir?: string; apiDir?: string; apiOnly?: boolean }) => {
|
|
282
|
+
await handleMachineAction(
|
|
283
|
+
"plan-scaffold",
|
|
284
|
+
async () => {
|
|
285
|
+
ensureProjectForMachine();
|
|
286
|
+
return planScaffoldOperation({
|
|
287
|
+
model,
|
|
288
|
+
functionsDir: options.functionsDir,
|
|
289
|
+
apiDir: options.apiDir,
|
|
290
|
+
apiOnly: options.apiOnly,
|
|
291
|
+
});
|
|
292
|
+
},
|
|
293
|
+
(planData) => ({
|
|
294
|
+
status: planData.requiresApproval ? "requires-human" : "complete",
|
|
295
|
+
nextActions: [
|
|
296
|
+
{
|
|
297
|
+
command: `swallowkit machine apply scaffold --plan ${planData.planId}${planData.requiresApproval ? " --approve" : ""}`,
|
|
298
|
+
description: planData.requiresApproval
|
|
299
|
+
? "Apply the plan after reviewing the listed conflicts (requires --approve)."
|
|
300
|
+
: "Apply the plan.",
|
|
301
|
+
},
|
|
302
|
+
],
|
|
303
|
+
})
|
|
304
|
+
);
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
plan
|
|
308
|
+
.command("auth")
|
|
309
|
+
.description("Compute the add-auth change plan without writing any files")
|
|
310
|
+
.requiredOption("--provider <provider>", "Auth provider: custom-jwt | swa | external-token | none")
|
|
311
|
+
.option("--scheme <name>", "Add as a named authentication scheme")
|
|
312
|
+
.action(async (options: { provider: string; scheme?: string }) => {
|
|
313
|
+
await handleMachineAction(
|
|
314
|
+
"plan-auth",
|
|
315
|
+
async () => {
|
|
316
|
+
ensureProjectForMachine();
|
|
317
|
+
return planAuthOperation({ provider: options.provider, scheme: options.scheme });
|
|
318
|
+
},
|
|
319
|
+
(planData) => ({
|
|
320
|
+
status: planData.requiresApproval ? "requires-human" : "complete",
|
|
321
|
+
nextActions: [
|
|
322
|
+
{
|
|
323
|
+
command: `swallowkit machine apply auth --plan ${planData.planId}${planData.requiresApproval ? " --approve" : ""}`,
|
|
324
|
+
description: planData.requiresApproval
|
|
325
|
+
? "Apply the plan after reviewing the listed conflicts (requires --approve)."
|
|
326
|
+
: "Apply the plan.",
|
|
327
|
+
},
|
|
328
|
+
],
|
|
329
|
+
})
|
|
330
|
+
);
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
plan
|
|
334
|
+
.command("provision")
|
|
335
|
+
.description("Preflight Azure provisioning locally; use --what-if for an az what-if analysis (requires az login)")
|
|
336
|
+
.requiredOption("-g, --resource-group <name>", "Resource group name")
|
|
337
|
+
.requiredOption("--location <region>", "Primary location for Functions and Cosmos DB")
|
|
338
|
+
.requiredOption("--swa-location <region>", "Static Web App location")
|
|
339
|
+
.option("--subscription <id>", "Azure subscription GUID")
|
|
340
|
+
.option("--what-if", "Run az deployment group what-if (requires az login)", false)
|
|
341
|
+
.action(async (options: { resourceGroup: string; location: string; swaLocation: string; subscription?: string; whatIf?: boolean }) => {
|
|
342
|
+
await handleMachineAction(
|
|
343
|
+
"plan-provision",
|
|
344
|
+
async () => {
|
|
345
|
+
ensureProjectForMachine();
|
|
346
|
+
return planProvisionOperation({
|
|
347
|
+
resourceGroup: options.resourceGroup,
|
|
348
|
+
location: options.location,
|
|
349
|
+
swaLocation: options.swaLocation,
|
|
350
|
+
subscription: options.subscription,
|
|
351
|
+
whatIf: options.whatIf,
|
|
352
|
+
});
|
|
353
|
+
},
|
|
354
|
+
(planData) => ({
|
|
355
|
+
status: "requires-human",
|
|
356
|
+
nextActions: [
|
|
357
|
+
{
|
|
358
|
+
command: `swallowkit machine apply provision --plan ${planData.planId} --approve`,
|
|
359
|
+
description:
|
|
360
|
+
"Provisioning creates billable Azure resources. A human must review plan.commands and approve before applying.",
|
|
361
|
+
},
|
|
362
|
+
],
|
|
363
|
+
})
|
|
364
|
+
);
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
const apply = new Command("apply");
|
|
368
|
+
apply
|
|
369
|
+
.command("scaffold")
|
|
370
|
+
.description("Apply scaffold changes, verifying plan freshness and overwrite approval")
|
|
371
|
+
.argument("[model]", "Model file or model name (optional when --plan is given)")
|
|
372
|
+
.option("--plan <planId>", "Apply a previously computed plan")
|
|
373
|
+
.option("--approve", "Approve overwriting files that were modified after generation", false)
|
|
374
|
+
.option("--functions-dir <dir>", "Functions directory")
|
|
375
|
+
.option("--api-dir <dir>", "API routes directory")
|
|
376
|
+
.option("--api-only", "Skip UI components; still update Functions, BFF routes, OpenAPI, and native schema assets")
|
|
377
|
+
.action(
|
|
378
|
+
async (
|
|
379
|
+
model: string | undefined,
|
|
380
|
+
options: { plan?: string; approve?: boolean; functionsDir?: string; apiDir?: string; apiOnly?: boolean }
|
|
381
|
+
) => {
|
|
382
|
+
await handleMachineAction(
|
|
383
|
+
"apply-scaffold",
|
|
384
|
+
async () => {
|
|
385
|
+
ensureProjectForMachine();
|
|
386
|
+
return applyScaffoldOperation({
|
|
387
|
+
model,
|
|
388
|
+
planId: options.plan,
|
|
389
|
+
approve: options.approve,
|
|
390
|
+
functionsDir: options.functionsDir,
|
|
391
|
+
apiDir: options.apiDir,
|
|
392
|
+
apiOnly: options.apiOnly,
|
|
393
|
+
});
|
|
394
|
+
},
|
|
395
|
+
() => ({
|
|
396
|
+
status: "complete",
|
|
397
|
+
nextActions: [
|
|
398
|
+
{
|
|
399
|
+
command: "swallowkit machine verify project",
|
|
400
|
+
description: "Verify the project after applying scaffold changes.",
|
|
401
|
+
},
|
|
402
|
+
],
|
|
403
|
+
})
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
);
|
|
407
|
+
|
|
408
|
+
apply
|
|
409
|
+
.command("auth")
|
|
410
|
+
.description("Apply add-auth changes, verifying plan freshness and overwrite approval")
|
|
411
|
+
.option("--plan <planId>", "Apply a previously computed plan")
|
|
412
|
+
.option("--provider <provider>", "Auth provider (optional when --plan is given)")
|
|
413
|
+
.option("--scheme <name>", "Add as a named authentication scheme")
|
|
414
|
+
.option("--approve", "Approve overwriting files that were modified after generation", false)
|
|
415
|
+
.action(async (options: { plan?: string; provider?: string; scheme?: string; approve?: boolean }) => {
|
|
416
|
+
await handleMachineAction(
|
|
417
|
+
"apply-auth",
|
|
418
|
+
async () => {
|
|
419
|
+
ensureProjectForMachine();
|
|
420
|
+
return applyAuthOperation({
|
|
421
|
+
provider: options.provider,
|
|
422
|
+
scheme: options.scheme,
|
|
423
|
+
planId: options.plan,
|
|
424
|
+
approve: options.approve,
|
|
425
|
+
});
|
|
426
|
+
},
|
|
427
|
+
() => ({
|
|
428
|
+
status: "complete",
|
|
429
|
+
nextActions: [
|
|
430
|
+
{
|
|
431
|
+
command: "swallowkit machine verify project",
|
|
432
|
+
description: "Verify the project after applying auth changes.",
|
|
433
|
+
},
|
|
434
|
+
],
|
|
435
|
+
})
|
|
436
|
+
);
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
apply
|
|
440
|
+
.command("provision")
|
|
441
|
+
.description("Apply an approved provisioning plan (always requires --approve; creates billable Azure resources)")
|
|
442
|
+
.requiredOption("--plan <planId>", "Provision plan to apply")
|
|
443
|
+
.option("--approve", "Confirm human approval of the provisioning plan", false)
|
|
444
|
+
.action(async (options: { plan: string; approve?: boolean }) => {
|
|
445
|
+
await handleMachineAction(
|
|
446
|
+
"apply-provision",
|
|
447
|
+
async () => {
|
|
448
|
+
ensureProjectForMachine();
|
|
449
|
+
return applyProvisionOperation({ planId: options.plan, approve: options.approve });
|
|
450
|
+
},
|
|
451
|
+
() => ({
|
|
452
|
+
status: "complete",
|
|
453
|
+
nextActions: [
|
|
454
|
+
{
|
|
455
|
+
command: "swallowkit machine inspect infra",
|
|
456
|
+
description: "Re-inspect infra assets after provisioning.",
|
|
457
|
+
},
|
|
458
|
+
],
|
|
459
|
+
})
|
|
460
|
+
);
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
const verify = new Command("verify");
|
|
464
|
+
verify
|
|
465
|
+
.command("project")
|
|
466
|
+
.description("Run verification checks (structure, drift, typecheck) with machine-readable evidence")
|
|
467
|
+
.option(
|
|
468
|
+
"--checks <ids>",
|
|
469
|
+
"Comma-separated check ids to run (structure,drift,typecheck,build,lint,test, plus custom ids from swallowkit.config verify.checks)"
|
|
470
|
+
)
|
|
471
|
+
.action(async (options: { checks?: string }) => {
|
|
472
|
+
await handleMachineAction(
|
|
473
|
+
"verify-project",
|
|
474
|
+
async () => runVerify(options.checks ? options.checks.split(",") : undefined),
|
|
475
|
+
(result) => ({
|
|
476
|
+
status: result.summary.done ? "complete" : "in-progress",
|
|
477
|
+
nextActions: result.summary.done
|
|
478
|
+
? []
|
|
479
|
+
: [
|
|
480
|
+
{
|
|
481
|
+
command: "swallowkit machine explain failure",
|
|
482
|
+
description: "Get evidence and suggested actions for the failed checks.",
|
|
483
|
+
},
|
|
484
|
+
],
|
|
485
|
+
})
|
|
486
|
+
);
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
const explain = new Command("explain");
|
|
490
|
+
explain
|
|
491
|
+
.command("failure")
|
|
492
|
+
.description("Explain failures from the most recent verify run")
|
|
493
|
+
.option("--check <id>", "Explain a specific check id")
|
|
494
|
+
.action(async (options: { check?: string }) => {
|
|
495
|
+
await handleMachineAction("explain-failure", async () => {
|
|
496
|
+
const lastVerify = loadLastVerifyState<VerifyResult>();
|
|
497
|
+
if (!lastVerify) {
|
|
498
|
+
throw new MachineCommandError(
|
|
499
|
+
"no-verification-result",
|
|
500
|
+
'No verify result found. Run "swallowkit machine verify project" first.',
|
|
501
|
+
undefined,
|
|
502
|
+
"blocked"
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
try {
|
|
507
|
+
return {
|
|
508
|
+
verifiedAt: lastVerify.verifiedAt,
|
|
509
|
+
failures: explainVerifyFailure(lastVerify, options.check),
|
|
510
|
+
};
|
|
511
|
+
} catch (error) {
|
|
512
|
+
throw new MachineCommandError(
|
|
513
|
+
"unknown-check",
|
|
514
|
+
error instanceof Error ? error.message : String(error),
|
|
515
|
+
{ check: options.check },
|
|
516
|
+
"failed"
|
|
517
|
+
);
|
|
518
|
+
}
|
|
519
|
+
});
|
|
145
520
|
});
|
|
146
521
|
|
|
147
522
|
program.addCommand(inspect);
|
|
148
523
|
program.addCommand(validate);
|
|
149
524
|
program.addCommand(generate);
|
|
525
|
+
program.addCommand(plan);
|
|
526
|
+
program.addCommand(apply);
|
|
527
|
+
program.addCommand(verify);
|
|
528
|
+
program.addCommand(explain);
|
|
150
529
|
return program;
|
|
151
530
|
}
|
|
152
531
|
|
|
@@ -154,20 +533,70 @@ export function isMachineCommand(argv: string[]): boolean {
|
|
|
154
533
|
return argv[2] === "machine";
|
|
155
534
|
}
|
|
156
535
|
|
|
536
|
+
/**
|
|
537
|
+
* すべてのコマンド階層に exitOverride と出力キャプチャを適用する。
|
|
538
|
+
* commander の addCommand は親の出力設定を継承しないため、再帰的に設定する。
|
|
539
|
+
*/
|
|
540
|
+
function applyMachineOutputSettings(command: Command, sink: { text: string }): void {
|
|
541
|
+
command.exitOverride();
|
|
542
|
+
command.showHelpAfterError(false);
|
|
543
|
+
command.configureOutput({
|
|
544
|
+
writeErr: (str) => {
|
|
545
|
+
sink.text += str;
|
|
546
|
+
},
|
|
547
|
+
writeOut: (str) => {
|
|
548
|
+
sink.text += str;
|
|
549
|
+
},
|
|
550
|
+
});
|
|
551
|
+
for (const sub of command.commands) {
|
|
552
|
+
applyMachineOutputSettings(sub, sink);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
157
556
|
export async function runMachineCli(argv: string[] = process.argv): Promise<void> {
|
|
158
557
|
const program = createMachineProgram();
|
|
159
|
-
|
|
558
|
+
const commanderOutput = { text: "" };
|
|
559
|
+
applyMachineOutputSettings(program, commanderOutput);
|
|
560
|
+
|
|
561
|
+
// Keep stdout reserved for the single JSON envelope: any console output
|
|
562
|
+
// produced by underlying operations (e.g. config-load logs) goes to stderr.
|
|
563
|
+
const originalLog = console.log;
|
|
564
|
+
const originalInfo = console.info;
|
|
565
|
+
const originalWarn = console.warn;
|
|
566
|
+
const toStderr = (...args: unknown[]) => {
|
|
567
|
+
process.stderr.write(`${args.map((arg) => (typeof arg === "string" ? arg : JSON.stringify(arg))).join(" ")}\n`);
|
|
568
|
+
};
|
|
569
|
+
console.log = toStderr;
|
|
570
|
+
console.info = toStderr;
|
|
571
|
+
console.warn = toStderr;
|
|
160
572
|
|
|
161
573
|
try {
|
|
162
574
|
await program.parseAsync(argv.slice(3), { from: "user" });
|
|
163
575
|
} catch (error) {
|
|
164
576
|
if (error instanceof CommanderError) {
|
|
165
|
-
|
|
166
|
-
|
|
577
|
+
const capturedText = commanderOutput.text.trim();
|
|
578
|
+
if (error.code === "commander.helpDisplayed" || error.code === "commander.help") {
|
|
579
|
+
// --help / help は正常応答として usage を JSON envelope で返す
|
|
580
|
+
writeMachineSuccess("machine-help", { help: capturedText });
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
writeMachineError(
|
|
584
|
+
"machine-parse",
|
|
585
|
+
new MachineCommandError(
|
|
586
|
+
"invalid-command",
|
|
587
|
+
error.message,
|
|
588
|
+
capturedText ? { usage: capturedText } : undefined
|
|
589
|
+
)
|
|
590
|
+
);
|
|
591
|
+
process.exitCode = Number.isFinite(error.exitCode) && error.exitCode !== 0 ? error.exitCode : 1;
|
|
167
592
|
return;
|
|
168
593
|
}
|
|
169
594
|
|
|
170
595
|
writeMachineError("machine-parse", error);
|
|
171
596
|
process.exitCode = 1;
|
|
597
|
+
} finally {
|
|
598
|
+
console.log = originalLog;
|
|
599
|
+
console.info = originalInfo;
|
|
600
|
+
console.warn = originalWarn;
|
|
172
601
|
}
|
|
173
602
|
}
|