swallowkit 1.0.3 → 1.1.1

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.
Files changed (77) hide show
  1. package/dist/cli/commands/create-migration.d.ts +3 -0
  2. package/dist/cli/commands/create-migration.d.ts.map +1 -0
  3. package/dist/cli/commands/create-migration.js +16 -0
  4. package/dist/cli/commands/create-migration.js.map +1 -0
  5. package/dist/cli/commands/index.d.ts +2 -0
  6. package/dist/cli/commands/index.d.ts.map +1 -1
  7. package/dist/cli/commands/index.js +5 -1
  8. package/dist/cli/commands/index.js.map +1 -1
  9. package/dist/cli/commands/init.d.ts.map +1 -1
  10. package/dist/cli/commands/init.js +2 -0
  11. package/dist/cli/commands/init.js.map +1 -1
  12. package/dist/cli/commands/migrations.d.ts +3 -0
  13. package/dist/cli/commands/migrations.d.ts.map +1 -0
  14. package/dist/cli/commands/migrations.js +22 -0
  15. package/dist/cli/commands/migrations.js.map +1 -0
  16. package/dist/cli/commands/provision.d.ts +1 -1
  17. package/dist/cli/commands/provision.d.ts.map +1 -1
  18. package/dist/cli/commands/provision.js +95 -185
  19. package/dist/cli/commands/provision.js.map +1 -1
  20. package/dist/cli/commands/scaffold.d.ts.map +1 -1
  21. package/dist/cli/commands/scaffold.js +6 -130
  22. package/dist/cli/commands/scaffold.js.map +1 -1
  23. package/dist/cli/index.d.ts.map +1 -1
  24. package/dist/cli/index.js +4 -0
  25. package/dist/cli/index.js.map +1 -1
  26. package/dist/core/operations/migration-generator.d.ts +10 -0
  27. package/dist/core/operations/migration-generator.d.ts.map +1 -0
  28. package/dist/core/operations/migration-generator.js +202 -0
  29. package/dist/core/operations/migration-generator.js.map +1 -0
  30. package/dist/core/operations/provision-operations.d.ts +19 -0
  31. package/dist/core/operations/provision-operations.d.ts.map +1 -1
  32. package/dist/core/operations/provision-operations.js +299 -30
  33. package/dist/core/operations/provision-operations.js.map +1 -1
  34. package/dist/core/operations/scaffold-plan.d.ts.map +1 -1
  35. package/dist/core/operations/scaffold-plan.js +13 -1
  36. package/dist/core/operations/scaffold-plan.js.map +1 -1
  37. package/dist/core/project/capabilities.js +1 -1
  38. package/dist/core/project/capabilities.js.map +1 -1
  39. package/dist/core/project/infra.d.ts +4 -0
  40. package/dist/core/project/infra.d.ts.map +1 -1
  41. package/dist/core/project/infra.js +19 -1
  42. package/dist/core/project/infra.js.map +1 -1
  43. package/dist/core/project/migrations.d.ts +53 -0
  44. package/dist/core/project/migrations.d.ts.map +1 -0
  45. package/dist/core/project/migrations.js +262 -0
  46. package/dist/core/project/migrations.js.map +1 -0
  47. package/dist/core/project/workflows.d.ts.map +1 -1
  48. package/dist/core/project/workflows.js +7 -3
  49. package/dist/core/project/workflows.js.map +1 -1
  50. package/dist/machine/index.d.ts.map +1 -1
  51. package/dist/machine/index.js +6 -0
  52. package/dist/machine/index.js.map +1 -1
  53. package/dist/mcp/index.d.ts.map +1 -1
  54. package/dist/mcp/index.js +11 -2
  55. package/dist/mcp/index.js.map +1 -1
  56. package/package.json +1 -1
  57. package/src/__tests__/agent-loop-phase34.test.ts +139 -0
  58. package/src/__tests__/init.test.ts +8 -0
  59. package/src/__tests__/migration.test.ts +116 -0
  60. package/src/__tests__/plan-apply.test.ts +132 -0
  61. package/src/__tests__/scaffold.test.ts +7 -4
  62. package/src/cli/commands/create-migration.ts +13 -0
  63. package/src/cli/commands/index.ts +2 -0
  64. package/src/cli/commands/init.ts +2 -0
  65. package/src/cli/commands/migrations.ts +20 -0
  66. package/src/cli/commands/provision.ts +115 -184
  67. package/src/cli/commands/scaffold.ts +6 -145
  68. package/src/cli/index.ts +4 -0
  69. package/src/core/operations/migration-generator.ts +206 -0
  70. package/src/core/operations/provision-operations.ts +391 -44
  71. package/src/core/operations/scaffold-plan.ts +17 -1
  72. package/src/core/project/capabilities.ts +1 -1
  73. package/src/core/project/infra.ts +23 -1
  74. package/src/core/project/migrations.ts +274 -0
  75. package/src/core/project/workflows.ts +7 -3
  76. package/src/machine/index.ts +7 -1
  77. package/src/mcp/index.ts +8 -2
@@ -0,0 +1,274 @@
1
+ import * as crypto from "crypto";
2
+ import * as fs from "fs";
3
+ import * as path from "path";
4
+ import { MachineCommandError } from "../../machine/errors";
5
+ import { hashContent } from "../operations/file-session";
6
+
7
+ export const MIGRATION_MANIFEST_VERSION = 1;
8
+ export const MIGRATION_MANIFEST_PATH = path.join("infra", "migrations", "manifest.json");
9
+
10
+ export interface MigrationBaselineFile {
11
+ path: string;
12
+ checksum: string;
13
+ }
14
+
15
+ export interface MigrationBaseline {
16
+ version: 0;
17
+ legacy: boolean;
18
+ files: MigrationBaselineFile[];
19
+ checksum: string;
20
+ }
21
+
22
+ export interface InfrastructureMigration {
23
+ version: number;
24
+ slug: string;
25
+ template: string;
26
+ parameters?: string;
27
+ source: "model" | "custom";
28
+ sourceModel?: string;
29
+ }
30
+
31
+ export interface MigrationManifest {
32
+ schemaVersion: 1;
33
+ projectId: string;
34
+ baseline: MigrationBaseline;
35
+ migrations: InfrastructureMigration[];
36
+ }
37
+
38
+ export interface ResolvedMigration extends InfrastructureMigration {
39
+ checksum: string;
40
+ cumulativeChecksum: string;
41
+ }
42
+
43
+ export interface MigrationState {
44
+ schemaVersion: 1;
45
+ version: number;
46
+ checksum: string;
47
+ }
48
+
49
+ function fail(code: string, message: string, details?: unknown): never {
50
+ throw new MachineCommandError(code, message, details, "blocked");
51
+ }
52
+
53
+ function normalizeRelativePath(value: string): string {
54
+ const normalized = value.replace(/\\/g, "/");
55
+ if (path.posix.isAbsolute(normalized) || normalized === ".." || normalized.startsWith("../") || normalized.includes("/../")) {
56
+ fail("invalid-migration-manifest", `Migration path must stay inside the project: ${value}`);
57
+ }
58
+ return normalized;
59
+ }
60
+
61
+ function readRequiredFile(projectRoot: string, relativePath: string): string {
62
+ const normalized = normalizeRelativePath(relativePath);
63
+ const absolutePath = path.resolve(projectRoot, normalized);
64
+ const rootWithSeparator = `${path.resolve(projectRoot)}${path.sep}`;
65
+ if (!absolutePath.startsWith(rootWithSeparator) || !fs.existsSync(absolutePath) || !fs.statSync(absolutePath).isFile()) {
66
+ fail("migration-file-not-found", `Migration file not found: ${normalized}`);
67
+ }
68
+ const realPath = fs.realpathSync(absolutePath);
69
+ if (!realPath.startsWith(rootWithSeparator)) {
70
+ fail("invalid-migration-manifest", `Migration file resolves outside the project: ${normalized}`);
71
+ }
72
+ return fs.readFileSync(absolutePath, "utf-8");
73
+ }
74
+
75
+ function checksumObject(value: unknown): string {
76
+ return hashContent(JSON.stringify(value));
77
+ }
78
+
79
+ export function computeBaselineChecksum(files: MigrationBaselineFile[]): string {
80
+ return checksumObject([...files].sort((left, right) => left.path.localeCompare(right.path)));
81
+ }
82
+
83
+ export function findBaselineDrift(manifest: MigrationManifest, projectRoot: string = process.cwd()): string[] {
84
+ return manifest.baseline.files
85
+ .filter((file) => {
86
+ const absolutePath = path.join(projectRoot, normalizeRelativePath(file.path));
87
+ return !fs.existsSync(absolutePath) || hashContent(fs.readFileSync(absolutePath, "utf-8")) !== file.checksum;
88
+ })
89
+ .map((file) => file.path);
90
+ }
91
+
92
+ export function collectLegacyBaselineFiles(projectRoot: string = process.cwd()): MigrationBaselineFile[] {
93
+ const infraRoot = path.join(projectRoot, "infra");
94
+ if (!fs.existsSync(path.join(infraRoot, "main.bicep"))) {
95
+ fail("infra-not-found", 'infra/main.bicep not found. Run this command from a SwallowKit project.');
96
+ }
97
+
98
+ const files: MigrationBaselineFile[] = [];
99
+ const visit = (directory: string): void => {
100
+ for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
101
+ const absolutePath = path.join(directory, entry.name);
102
+ if (entry.isDirectory()) {
103
+ if (entry.name !== "migrations") visit(absolutePath);
104
+ } else if (entry.isFile() && (entry.name.endsWith(".bicep") || entry.name.endsWith(".json"))) {
105
+ const relativePath = path.relative(projectRoot, absolutePath).replace(/\\/g, "/");
106
+ files.push({ path: relativePath, checksum: hashContent(fs.readFileSync(absolutePath, "utf-8")) });
107
+ }
108
+ }
109
+ };
110
+ visit(infraRoot);
111
+ return files.sort((left, right) => left.path.localeCompare(right.path));
112
+ }
113
+
114
+ export function createMigrationManifest(
115
+ projectRoot: string = process.cwd(),
116
+ options: { legacy?: boolean; projectId?: string } = {}
117
+ ): MigrationManifest {
118
+ const files = collectLegacyBaselineFiles(projectRoot);
119
+ return {
120
+ schemaVersion: MIGRATION_MANIFEST_VERSION,
121
+ projectId: options.projectId ?? crypto.randomUUID(),
122
+ baseline: {
123
+ version: 0,
124
+ legacy: options.legacy ?? false,
125
+ files,
126
+ checksum: computeBaselineChecksum(files),
127
+ },
128
+ migrations: [],
129
+ };
130
+ }
131
+
132
+ export function saveMigrationManifest(manifest: MigrationManifest, projectRoot: string = process.cwd()): void {
133
+ const manifestPath = path.join(projectRoot, MIGRATION_MANIFEST_PATH);
134
+ if (fs.existsSync(manifestPath)) {
135
+ fail("migration-manifest-exists", `${MIGRATION_MANIFEST_PATH} already exists.`);
136
+ }
137
+ fs.mkdirSync(path.dirname(manifestPath), { recursive: true });
138
+ fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, "utf-8");
139
+ }
140
+
141
+ export function replaceMigrationManifest(manifest: MigrationManifest, projectRoot: string = process.cwd()): void {
142
+ validateMigrationManifest(manifest, projectRoot);
143
+ const manifestPath = path.join(projectRoot, MIGRATION_MANIFEST_PATH);
144
+ const temporaryPath = `${manifestPath}.tmp-${process.pid}`;
145
+ fs.mkdirSync(path.dirname(manifestPath), { recursive: true });
146
+ fs.writeFileSync(temporaryPath, `${JSON.stringify(manifest, null, 2)}\n`, "utf-8");
147
+ fs.renameSync(temporaryPath, manifestPath);
148
+ }
149
+
150
+ export function loadMigrationManifest(projectRoot: string = process.cwd()): MigrationManifest {
151
+ const manifestPath = path.join(projectRoot, MIGRATION_MANIFEST_PATH);
152
+ if (!fs.existsSync(manifestPath)) {
153
+ fail("migration-manifest-not-found", `${MIGRATION_MANIFEST_PATH} not found. Initialize migrations first.`);
154
+ }
155
+ let manifest: MigrationManifest;
156
+ try {
157
+ manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8")) as MigrationManifest;
158
+ } catch {
159
+ fail("invalid-migration-manifest", `${MIGRATION_MANIFEST_PATH} is not valid JSON.`);
160
+ }
161
+ validateMigrationManifest(manifest, projectRoot);
162
+ return manifest;
163
+ }
164
+
165
+ export function validateMigrationManifest(manifest: MigrationManifest, projectRoot: string = process.cwd()): void {
166
+ if (
167
+ !manifest || typeof manifest !== "object" ||
168
+ manifest.schemaVersion !== MIGRATION_MANIFEST_VERSION ||
169
+ typeof manifest.projectId !== "string" || !manifest.projectId ||
170
+ !manifest.baseline || typeof manifest.baseline !== "object" ||
171
+ manifest.baseline.version !== 0 || typeof manifest.baseline.legacy !== "boolean" ||
172
+ !Array.isArray(manifest.baseline.files) || typeof manifest.baseline.checksum !== "string" ||
173
+ !Array.isArray(manifest.migrations)
174
+ ) {
175
+ fail("invalid-migration-manifest", "Unsupported or incomplete migration manifest.");
176
+ }
177
+ for (const file of manifest.baseline.files) {
178
+ if (!file || typeof file.path !== "string" || !/^[a-f0-9]{64}$/.test(file.checksum)) {
179
+ fail("invalid-migration-manifest", "Migration baseline contains an invalid file record.");
180
+ }
181
+ }
182
+ const baselineFiles = manifest.baseline.files.map((file) => ({
183
+ path: normalizeRelativePath(file.path),
184
+ checksum: file.checksum,
185
+ }));
186
+ if (new Set(baselineFiles.map((file) => file.path)).size !== baselineFiles.length) {
187
+ fail("invalid-migration-manifest", "Migration baseline contains duplicate paths.");
188
+ }
189
+ if (computeBaselineChecksum(baselineFiles) !== manifest.baseline.checksum) {
190
+ fail("migration-checksum-mismatch", "The migration baseline inventory was modified.");
191
+ }
192
+
193
+ const sorted = [...manifest.migrations].sort((left, right) => left.version - right.version);
194
+ for (let index = 0; index < sorted.length; index += 1) {
195
+ const migration = sorted[index];
196
+ if (!migration || !Number.isSafeInteger(migration.version) || typeof migration.slug !== "string" || typeof migration.template !== "string") {
197
+ fail("invalid-migration-manifest", "Migration manifest contains an invalid entry.");
198
+ }
199
+ const expectedVersion = index + 1;
200
+ if (migration.version !== expectedVersion || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(migration.slug)) {
201
+ fail("invalid-migration-manifest", `Migration versions must be contiguous and slugs must be kebab-case (expected version ${expectedVersion}).`);
202
+ }
203
+ if (migration.source !== "model" && migration.source !== "custom") {
204
+ fail("invalid-migration-manifest", `Migration ${migration.version} has an invalid source.`);
205
+ }
206
+ const template = normalizeRelativePath(migration.template);
207
+ if (!template.startsWith("infra/migrations/") || !template.endsWith(".bicep")) {
208
+ fail("invalid-migration-manifest", `Migration template must be a .bicep file under infra/migrations: ${template}`);
209
+ }
210
+ readRequiredFile(projectRoot, template);
211
+ if (migration.parameters) {
212
+ const parameters = normalizeRelativePath(migration.parameters);
213
+ if (!parameters.startsWith("infra/migrations/") || !parameters.endsWith(".json")) {
214
+ fail("invalid-migration-manifest", `Migration parameters must be JSON under infra/migrations: ${parameters}`);
215
+ }
216
+ readRequiredFile(projectRoot, parameters);
217
+ }
218
+ }
219
+ }
220
+
221
+ export function resolveMigrations(manifest: MigrationManifest, projectRoot: string = process.cwd()): ResolvedMigration[] {
222
+ validateMigrationManifest(manifest, projectRoot);
223
+ let cumulativeChecksum = manifest.baseline.checksum;
224
+ return [...manifest.migrations]
225
+ .sort((left, right) => left.version - right.version)
226
+ .map((migration) => {
227
+ const templateContent = readRequiredFile(projectRoot, migration.template);
228
+ const parametersContent = migration.parameters ? readRequiredFile(projectRoot, migration.parameters) : null;
229
+ const checksum = checksumObject({ migration, templateContent, parametersContent });
230
+ cumulativeChecksum = checksumObject({ previous: cumulativeChecksum, version: migration.version, checksum });
231
+ return { ...migration, checksum, cumulativeChecksum };
232
+ });
233
+ }
234
+
235
+ export function checksumAtVersion(manifest: MigrationManifest, version: number, projectRoot: string = process.cwd()): string {
236
+ if (version === 0) return manifest.baseline.checksum;
237
+ const migration = resolveMigrations(manifest, projectRoot).find((item) => item.version === version);
238
+ if (!migration) fail("migration-version-not-found", `Migration version ${version} does not exist locally.`);
239
+ return migration.cumulativeChecksum;
240
+ }
241
+
242
+ export function getMigrationTagKey(projectId: string): string {
243
+ const projectHash = crypto.createHash("sha256").update(projectId).digest("hex").slice(0, 12);
244
+ return `swallowkit-migration-${projectHash}`;
245
+ }
246
+
247
+ export function encodeMigrationState(state: MigrationState): string {
248
+ if (state.schemaVersion !== 1 || !Number.isSafeInteger(state.version) || state.version < 0 || !/^[a-f0-9]{64}$/.test(state.checksum)) {
249
+ fail("invalid-migration-state", "Cannot encode an invalid migration state.");
250
+ }
251
+ return `1:${state.version}:${state.checksum}`;
252
+ }
253
+
254
+ export function decodeMigrationState(value: string): MigrationState {
255
+ const match = /^1:(0|[1-9]\d*):([a-f0-9]{64})$/.exec(value);
256
+ if (!match) fail("invalid-migration-state", "The Azure migration state tag is invalid.");
257
+ return { schemaVersion: 1, version: Number(match[1]), checksum: match[2] };
258
+ }
259
+
260
+ export function selectPendingMigrations(
261
+ manifest: MigrationManifest,
262
+ remoteState: MigrationState,
263
+ projectRoot: string = process.cwd()
264
+ ): ResolvedMigration[] {
265
+ const latestVersion = manifest.migrations.length;
266
+ if (remoteState.version > latestVersion) {
267
+ fail("migration-state-ahead", `Azure is at migration ${remoteState.version}, but local latest is ${latestVersion}.`);
268
+ }
269
+ const expectedChecksum = checksumAtVersion(manifest, remoteState.version, projectRoot);
270
+ if (remoteState.checksum !== expectedChecksum) {
271
+ fail("migration-checksum-mismatch", `Azure migration ${remoteState.version} does not match the local history.`);
272
+ }
273
+ return resolveMigrations(manifest, projectRoot).filter((migration) => migration.version > remoteState.version);
274
+ }
@@ -187,7 +187,7 @@ Stop and ask a human when:
187
187
  "Provision Azure resources (Static Web Apps, Functions, Cosmos DB) for a SwallowKit project using the two-phase plan/apply flow with a mandatory human approval gate. Use when asked to deploy infrastructure, provision Azure, create resource groups, or run Bicep deployments.",
188
188
  content: `# Workflow: Provision Azure resources
189
189
 
190
- Goal: deploy the Bicep infrastructure with an explicit human approval gate.
190
+ Goal: bootstrap Azure once, then apply only pending versioned infrastructure migrations with an explicit human approval gate.
191
191
 
192
192
  ## Rules
193
193
 
@@ -196,6 +196,8 @@ Goal: deploy the Bicep infrastructure with an explicit human approval gate.
196
196
  - Never pass \`--approve\` to \`apply provision\` unless a human explicitly
197
197
  instructed you to provision in this session.
198
198
  - Inspect infra locally first — no \`az\` calls are needed for inspection.
199
+ - Never edit an applied migration. Create a corrective migration instead.
200
+ - Never use \`--reconcile\` for an ordinary model or resource addition.
199
201
 
200
202
  ## Steps
201
203
 
@@ -204,8 +206,10 @@ Goal: deploy the Bicep infrastructure with an explicit human approval gate.
204
206
  - review parameters, modules, outputs, and container wiring warnings.
205
207
  2. Compute the provisioning plan (no resources are created):
206
208
  \`${runCmd} swallowkit machine plan provision -g <resource-group> --location <region>\`
207
- - optional: \`--what-if\` to include an \`az deployment ... what-if\` preview.
208
- - the plan lists the exact \`az\` commands that would run.
209
+ - the plan reports \`bootstrap\`, \`migrate\`, \`noop\`, or \`adoption-required\`.
210
+ - it lists only pending migrations and the exact \`az\` commands that would run.
211
+ - for an older untagged environment, first run \`${runCmd} swallowkit migrations init --legacy-baseline\`, then plan with \`--adopt-existing --baseline 0 --what-if\`.
212
+ - only intentional baseline changes use \`--reconcile --what-if\`; this can reset template-managed settings.
209
213
  3. Present the plan to the human and wait for explicit approval.
210
214
  4. Only after approval:
211
215
  \`${runCmd} swallowkit machine apply provision --plan <planId> --approve\`
@@ -366,7 +366,10 @@ function createMachineProgram(): Command {
366
366
  .requiredOption("--swa-location <region>", "Static Web App location")
367
367
  .option("--subscription <id>", "Azure subscription GUID")
368
368
  .option("--what-if", "Run az deployment group what-if (requires az login)", false)
369
- .action(async (options: { resourceGroup: string; location: string; swaLocation: string; subscription?: string; whatIf?: boolean }) => {
369
+ .option("--adopt-existing", "Adopt an existing untagged resource group without deploying main.bicep", false)
370
+ .option("--baseline <version>", "Baseline version for adoption", (value) => Number(value))
371
+ .option("--reconcile", "Explicitly redeploy infra/main.bicep", false)
372
+ .action(async (options: { resourceGroup: string; location: string; swaLocation: string; subscription?: string; whatIf?: boolean; adoptExisting?: boolean; baseline?: number; reconcile?: boolean }) => {
370
373
  await handleMachineAction(
371
374
  "plan-provision",
372
375
  async () => {
@@ -377,6 +380,9 @@ function createMachineProgram(): Command {
377
380
  swaLocation: options.swaLocation,
378
381
  subscription: options.subscription,
379
382
  whatIf: options.whatIf,
383
+ adoptExisting: options.adoptExisting,
384
+ baseline: options.baseline,
385
+ reconcile: options.reconcile,
380
386
  });
381
387
  },
382
388
  (planData) => ({
package/src/mcp/index.ts CHANGED
@@ -352,18 +352,24 @@ export function buildSwallowKitToolDefinitions(
352
352
  },
353
353
  {
354
354
  name: "swallowkit_plan_provision",
355
- description: "Preflight Azure provisioning locally (Bicep analysis, az CLI availability, command preview). Never deploys. Set whatIf=true to run az what-if (requires az login). The returned plan always requires human approval.",
355
+ description: "Plan Azure bootstrap or pending infrastructure migrations. Existing untagged environments require explicit adoption with what-if; full main.bicep redeployment requires reconcile=true. Never deploys and always requires human approval.",
356
356
  inputSchema: z.object({
357
357
  resourceGroup: z.string(),
358
358
  location: z.string(),
359
359
  swaLocation: z.string(),
360
360
  subscription: z.string().optional(),
361
361
  whatIf: z.boolean().optional(),
362
+ adoptExisting: z.boolean().optional(),
363
+ baseline: z.number().int().nonnegative().optional(),
364
+ reconcile: z.boolean().optional(),
362
365
  }),
363
- handler: async ({ resourceGroup, location, swaLocation, subscription, whatIf }: { resourceGroup: string; location: string; swaLocation: string; subscription?: string; whatIf?: boolean }) => {
366
+ handler: async ({ resourceGroup, location, swaLocation, subscription, whatIf, adoptExisting, baseline, reconcile }: { resourceGroup: string; location: string; swaLocation: string; subscription?: string; whatIf?: boolean; adoptExisting?: boolean; baseline?: number; reconcile?: boolean }) => {
364
367
  const args = ["plan", "provision", "--resource-group", resourceGroup, "--location", location, "--swa-location", swaLocation];
365
368
  if (subscription) args.push("--subscription", subscription);
366
369
  if (whatIf) args.push("--what-if");
370
+ if (adoptExisting) args.push("--adopt-existing");
371
+ if (baseline !== undefined) args.push("--baseline", String(baseline));
372
+ if (reconcile) args.push("--reconcile");
367
373
 
368
374
  const response = await executeMachineCommand(args, runMachineCli);
369
375
  return jsonTextContent(withOperationStatus(response));