siteplane 0.1.41 → 0.1.43

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.
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/bin/run-siteplane.ts
4
- import { randomUUID as randomUUID4 } from "crypto";
5
- import { access as access2, readFile as readFile8 } from "fs/promises";
6
- import { join as join7, resolve } from "path";
4
+ import { randomUUID as randomUUID7 } from "crypto";
5
+ import { access as access6, readFile as readFile13 } from "fs/promises";
6
+ import { join as join12, resolve as resolve4 } from "path";
7
7
 
8
8
  // ../cli/dist/analytics/manifest.js
9
9
  import { mkdir, readFile, writeFile } from "fs/promises";
@@ -892,7 +892,7 @@ function redactBookingManifest(value) {
892
892
  var BOOKING_CREATE_PAYLOAD_LIMIT_BYTES = 16 * 1024;
893
893
 
894
894
  // ../shared/dist/bridge-protocol.js
895
- import { z as z15 } from "zod";
895
+ import { z as z16 } from "zod";
896
896
 
897
897
  // ../shared/dist/field-id.js
898
898
  import { z as z9 } from "zod";
@@ -939,21 +939,70 @@ var imageValueSchema = z11.object({
939
939
  ]).optional()
940
940
  });
941
941
 
942
+ // ../shared/dist/setup-deployment.js
943
+ import { z as z12 } from "zod";
944
+ var version = z12.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u);
945
+ var hash = z12.string().regex(/^sha256:[0-9a-f]{64}$/u);
946
+ var setupDeploymentRecordSchema = z12.object({
947
+ version: z12.literal(1),
948
+ operationId: z12.string().uuid(),
949
+ provider: z12.literal("vercel"),
950
+ projectId: z12.string().regex(/^prj_[A-Za-z0-9]+$/u),
951
+ teamId: z12.string().regex(/^(?:team|user)_[A-Za-z0-9]+$/u),
952
+ origin: z12.string().url().refine((value) => {
953
+ const url = new URL(value);
954
+ return url.protocol === "https:" && url.origin === value && !url.username && !url.password;
955
+ }),
956
+ deploymentId: z12.string().regex(/^dpl_[A-Za-z0-9]+$/u).nullable(),
957
+ revision: z12.string().regex(/^[0-9a-f]{40}$/u),
958
+ fieldContractHash: hash,
959
+ packages: z12.object({ siteplane: version, runtimeNext: version }).strict(),
960
+ configurationFingerprint: hash,
961
+ adminSecretGeneration: z12.string().uuid(),
962
+ revalidationGeneration: z12.number().int().positive(),
963
+ startedAt: z12.string().datetime(),
964
+ environmentWrittenAt: z12.string().datetime().nullable(),
965
+ deployRequestedAt: z12.string().datetime().nullable(),
966
+ deadline: z12.string().datetime(),
967
+ phase: z12.enum([
968
+ "planned",
969
+ "environment_written",
970
+ "deploy_requested",
971
+ "deployed",
972
+ "verified",
973
+ "failed"
974
+ ]),
975
+ build: z12.object({
976
+ node: version,
977
+ packageManager: z12.enum(["npm", "pnpm"]),
978
+ packageManagerVersion: version,
979
+ lockfileHash: hash
980
+ }).strict().nullable()
981
+ }).strict();
982
+ var setupDeploymentInputSchema = z12.discriminatedUnion("action", [
983
+ z12.object({ action: z12.literal("read") }).strict(),
984
+ z12.object({
985
+ action: z12.literal("record"),
986
+ expectedOperationId: z12.string().uuid().nullable(),
987
+ record: setupDeploymentRecordSchema
988
+ }).strict()
989
+ ]);
990
+
942
991
  // ../shared/dist/site-setup.js
943
- import { z as z14 } from "zod";
992
+ import { z as z15 } from "zod";
944
993
 
945
994
  // ../shared/dist/site-field-contract.js
946
- import { z as z13 } from "zod";
995
+ import { z as z14 } from "zod";
947
996
 
948
997
  // ../shared/dist/fallback-hash.js
949
- import { z as z12 } from "zod";
998
+ import { z as z13 } from "zod";
950
999
  var SAFE_REPRESENTATION_REASONS = [
951
1000
  "asset_import_rewritten",
952
1001
  "relative_url_normalized",
953
1002
  "line_endings_normalized",
954
1003
  "image_value_serialized"
955
1004
  ];
956
- var safeRepresentationReasonSchema = z12.enum(SAFE_REPRESENTATION_REASONS);
1005
+ var safeRepresentationReasonSchema = z13.enum(SAFE_REPRESENTATION_REASONS);
957
1006
  function canonicalizeFallbackValue(input) {
958
1007
  const fieldType = fieldTypeSchema.parse(input.fieldType);
959
1008
  if (input.safeRepresentationReason !== void 0) {
@@ -989,10 +1038,10 @@ function createCanonicalFallbackHash(input) {
989
1038
  const payload = stableJsonStringify(canonical);
990
1039
  return `sha256:${sha256Hex(payload)}`;
991
1040
  }
992
- var linkFallbackSchema = z12.object({
993
- href: z12.string().trim().min(1),
994
- label: z12.string().trim().min(1).optional(),
995
- target: z12.enum(["_self", "_blank"]).optional()
1041
+ var linkFallbackSchema = z13.object({
1042
+ href: z13.string().trim().min(1),
1043
+ label: z13.string().trim().min(1).optional(),
1044
+ target: z13.enum(["_self", "_blank"]).optional()
996
1045
  }).passthrough();
997
1046
  function normalizeLineEndings(value) {
998
1047
  return value.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
@@ -1170,67 +1219,67 @@ var SHA256_CONSTANTS = [
1170
1219
  // ../shared/dist/site-field-contract.js
1171
1220
  var fieldContractVersion = "1.0.0";
1172
1221
  var scannerContractVersion = "1.0.0";
1173
- var siteFieldStatusSchema = z13.enum(["hidden", "active"]);
1174
- var semanticVersionSchema = z13.string().regex(/^\d+\.\d+\.\d+$/u);
1175
- var siteFieldRouteKeySchema = z13.string().trim().refine((value) => value === "$global" || value.startsWith("/") && !value.includes("?") && !value.includes("#") && (value === "/" || !value.endsWith("/")), "Route keys must be canonical paths or $global.");
1176
- var repositoryRelativePosixPathSchema = z13.string().trim().min(1).refine((value) => !value.startsWith("/") && !value.includes("\\") && !/^[a-z]:/iu.test(value) && !value.split("/").includes(".."), "Source paths must be repository-relative POSIX paths.");
1177
- var sourceMappingSchema = z13.object({
1178
- kind: z13.literal("function"),
1222
+ var siteFieldStatusSchema = z14.enum(["hidden", "active"]);
1223
+ var semanticVersionSchema = z14.string().regex(/^\d+\.\d+\.\d+$/u);
1224
+ var siteFieldRouteKeySchema = z14.string().trim().refine((value) => value === "$global" || value.startsWith("/") && !value.includes("?") && !value.includes("#") && (value === "/" || !value.endsWith("/")), "Route keys must be canonical paths or $global.");
1225
+ var repositoryRelativePosixPathSchema = z14.string().trim().min(1).refine((value) => !value.startsWith("/") && !value.includes("\\") && !/^[a-z]:/iu.test(value) && !value.split("/").includes(".."), "Source paths must be repository-relative POSIX paths.");
1226
+ var sourceMappingSchema = z14.object({
1227
+ kind: z14.literal("function"),
1179
1228
  filePath: repositoryRelativePosixPathSchema,
1180
- exportName: z13.string().trim().min(1).optional(),
1181
- sourcePath: z13.string().trim().min(1).optional(),
1229
+ exportName: z14.string().trim().min(1).optional(),
1230
+ sourcePath: z14.string().trim().min(1).optional(),
1182
1231
  editTarget: fieldIdSchema.optional()
1183
1232
  }).strict();
1184
- var domMappingSchema = z13.object({
1185
- attribute: z13.literal("data-siteplane-field-id"),
1186
- previewStrategy: z13.enum(["dom", "event"])
1233
+ var domMappingSchema = z14.object({
1234
+ attribute: z14.literal("data-siteplane-field-id"),
1235
+ previewStrategy: z14.enum(["dom", "event"])
1187
1236
  }).strict();
1188
- var fieldBaseSchema = z13.object({
1237
+ var fieldBaseSchema = z14.object({
1189
1238
  fieldId: fieldIdSchema,
1190
1239
  routeKey: siteFieldRouteKeySchema,
1191
1240
  source: sourceMappingSchema,
1192
1241
  dom: domMappingSchema.optional()
1193
1242
  }).strict();
1194
- var linkFallbackSchema2 = z13.object({
1195
- href: z13.string().trim().min(1),
1196
- label: z13.string().trim().min(1).optional(),
1197
- target: z13.enum(["_self", "_blank"]).optional()
1243
+ var linkFallbackSchema2 = z14.object({
1244
+ href: z14.string().trim().min(1),
1245
+ label: z14.string().trim().min(1).optional(),
1246
+ target: z14.enum(["_self", "_blank"]).optional()
1198
1247
  }).strict();
1199
- var imageFallbackSchema = z13.object({
1200
- src: z13.string().trim().min(1),
1201
- alt: z13.string().trim().max(300),
1202
- width: z13.number().int().positive().optional(),
1203
- height: z13.number().int().positive().optional(),
1204
- crop: z13.object({
1205
- x: z13.number().min(0).max(100),
1206
- y: z13.number().min(0).max(100),
1207
- zoom: z13.number().min(1).max(4),
1208
- aspectRatio: z13.number().positive().max(10)
1248
+ var imageFallbackSchema = z14.object({
1249
+ src: z14.string().trim().min(1),
1250
+ alt: z14.string().trim().max(300),
1251
+ width: z14.number().int().positive().optional(),
1252
+ height: z14.number().int().positive().optional(),
1253
+ crop: z14.object({
1254
+ x: z14.number().min(0).max(100),
1255
+ y: z14.number().min(0).max(100),
1256
+ zoom: z14.number().min(1).max(4),
1257
+ aspectRatio: z14.number().positive().max(10)
1209
1258
  }).strict().optional()
1210
1259
  }).strict();
1211
- var siteFieldContractFieldSchema = z13.discriminatedUnion("fieldType", [
1260
+ var siteFieldContractFieldSchema = z14.discriminatedUnion("fieldType", [
1212
1261
  fieldBaseSchema.extend({
1213
- fieldType: z13.literal("text"),
1214
- fallback: z13.string()
1262
+ fieldType: z14.literal("text"),
1263
+ fallback: z14.string()
1215
1264
  }),
1216
1265
  fieldBaseSchema.extend({
1217
- fieldType: z13.literal("longText"),
1218
- fallback: z13.string()
1266
+ fieldType: z14.literal("longText"),
1267
+ fallback: z14.string()
1219
1268
  }),
1220
1269
  fieldBaseSchema.extend({
1221
- fieldType: z13.literal("link"),
1270
+ fieldType: z14.literal("link"),
1222
1271
  fallback: linkFallbackSchema2
1223
1272
  }),
1224
1273
  fieldBaseSchema.extend({
1225
- fieldType: z13.literal("image"),
1274
+ fieldType: z14.literal("image"),
1226
1275
  fallback: imageFallbackSchema
1227
1276
  })
1228
1277
  ]);
1229
- var siteFieldContractV1Schema = z13.object({
1230
- version: z13.literal(1),
1278
+ var siteFieldContractV1Schema = z14.object({
1279
+ version: z14.literal(1),
1231
1280
  fieldContractVersion: semanticVersionSchema,
1232
1281
  scannerContractVersion: semanticVersionSchema,
1233
- fields: z13.array(siteFieldContractFieldSchema)
1282
+ fields: z14.array(siteFieldContractFieldSchema)
1234
1283
  }).strict().superRefine((contract, context) => {
1235
1284
  const seen = /* @__PURE__ */ new Set();
1236
1285
  for (const [index, field] of contract.fields.entries()) {
@@ -1290,55 +1339,20 @@ var SITE_SETUP_OWNER_EDITABILITY_NOTE_MAX_LENGTH = 2e3;
1290
1339
  var SITE_SETUP_OWNER_EDITABILITY_NOTE_MAX_BYTES = 8e3;
1291
1340
  var CONTINUE_SITE_SETUP_ITEMS_MAX_BYTES = 32 * 1024;
1292
1341
  var CONTINUE_SITE_SETUP_HTTP_BODY_MAX_BYTES = 64 * 1024;
1293
- var siteSetupRunStatusSchema = z14.enum(SITE_SETUP_RUN_STATUSES);
1294
- var siteSetupRunModeSchema = z14.enum(SITE_SETUP_RUN_MODES);
1295
- var siteSetupErrorCodeSchema = z14.enum(SITE_SETUP_ERROR_CODES);
1296
- var siteSetupTaskSchema = z14.object({
1297
- contractVersion: z14.literal("siteplane.setup-task.v1"),
1298
- workflow: z14.enum(["initial_setup", "structure_update"]),
1299
- goal: z14.string().min(1),
1300
- modules: z14.tuple([z14.literal("visual_editor")]),
1301
- packages: z14.object({
1302
- siteplane: z14.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u),
1303
- runtimeNext: z14.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u)
1304
- }).strict(),
1305
- communication: z14.object({
1306
- beforeSourceChanges: z14.string().min(1),
1307
- updates: z14.array(z14.string().min(1)).min(1),
1308
- blocker: z14.string().min(1)
1309
- }).strict(),
1310
- discovery: z14.object({
1311
- inspectPublicRoutes: z14.boolean(),
1312
- contentKinds: z14.array(z14.enum(["text", "longText", "link", "image"])).min(1),
1313
- rules: z14.array(z14.string().min(1)).min(1)
1314
- }).strict(),
1315
- steps: z14.array(z14.object({
1316
- id: z14.enum([
1317
- "inspect_repository",
1318
- "install_packages",
1319
- "instrument_fields",
1320
- "apply_contract",
1321
- "deploy_production",
1322
- "verify_deployment"
1323
- ]),
1324
- instruction: z14.string().min(1)
1325
- }).strict()).length(6),
1326
- completion: z14.object({
1327
- verifiedStatus: z14.literal("ready_for_review"),
1328
- instruction: z14.string().min(1)
1329
- }).strict()
1330
- }).strict();
1331
- var editorFieldSchema = z14.object({
1342
+ var siteSetupRunStatusSchema = z15.enum(SITE_SETUP_RUN_STATUSES);
1343
+ var siteSetupRunModeSchema = z15.enum(SITE_SETUP_RUN_MODES);
1344
+ var siteSetupErrorCodeSchema = z15.enum(SITE_SETUP_ERROR_CODES);
1345
+ var editorFieldSchema = z15.object({
1332
1346
  fieldId: fieldIdSchema,
1333
- label: z14.string().trim().min(1),
1334
- subsection: z14.string().trim().min(1).optional(),
1335
- description: z14.string().trim().min(1).optional(),
1336
- required: z14.boolean().optional(),
1337
- maxLength: z14.number().int().positive().optional()
1347
+ label: z15.string().trim().min(1),
1348
+ subsection: z15.string().trim().min(1).optional(),
1349
+ description: z15.string().trim().min(1).optional(),
1350
+ required: z15.boolean().optional(),
1351
+ maxLength: z15.number().int().positive().optional()
1338
1352
  }).strict();
1339
- var editorSectionSchema = z14.object({
1340
- label: z14.string().trim().min(1),
1341
- fields: z14.array(editorFieldSchema).min(1)
1353
+ var editorSectionSchema = z15.object({
1354
+ label: z15.string().trim().min(1),
1355
+ fields: z15.array(editorFieldSchema).min(1)
1342
1356
  }).strict().superRefine((section, context) => {
1343
1357
  const closedSubsections = /* @__PURE__ */ new Set();
1344
1358
  let currentSubsection;
@@ -1357,10 +1371,10 @@ var editorSectionSchema = z14.object({
1357
1371
  }
1358
1372
  }
1359
1373
  });
1360
- var editorRouteSchema = z14.object({
1374
+ var editorRouteSchema = z15.object({
1361
1375
  routeKey: siteFieldRouteKeySchema,
1362
- label: z14.string().trim().min(1),
1363
- sections: z14.array(editorSectionSchema).min(1)
1376
+ label: z15.string().trim().min(1),
1377
+ sections: z15.array(editorSectionSchema).min(1)
1364
1378
  }).strict().superRefine((route, context) => {
1365
1379
  const sections = /* @__PURE__ */ new Set();
1366
1380
  for (const [index, section] of route.sections.entries()) {
@@ -1374,9 +1388,9 @@ var editorRouteSchema = z14.object({
1374
1388
  sections.add(section.label);
1375
1389
  }
1376
1390
  });
1377
- var editorDefinitionV1Schema = z14.object({
1378
- version: z14.literal(1),
1379
- routes: z14.array(editorRouteSchema).min(1)
1391
+ var editorDefinitionV1Schema = z15.object({
1392
+ version: z15.literal(1),
1393
+ routes: z15.array(editorRouteSchema).min(1)
1380
1394
  }).strict().superRefine((definition, context) => {
1381
1395
  const routes = /* @__PURE__ */ new Set();
1382
1396
  const fields = /* @__PURE__ */ new Set();
@@ -1414,7 +1428,7 @@ var editorDefinitionV1Schema = z14.object({
1414
1428
  var forbiddenControlOrBidi = /[\u0000-\u001f\u007f-\u009f\u202a-\u202e\u2066-\u2069]/u;
1415
1429
  var forbiddenOwnerNoteControlOrBidi = /[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f-\u009f\u202a-\u202e\u2066-\u2069]/u;
1416
1430
  var unsafeLocator = /<\/?[a-z][^>]*>|\b(?:javascript|data:text\/html)\s*:|\bon[a-z]+\s*=/iu;
1417
- var renderedPathSchema = z14.string().trim().transform((value) => value !== "/" ? value.replace(/\/+$/u, "") : value).superRefine((value, context) => {
1431
+ var renderedPathSchema = z15.string().trim().transform((value) => value !== "/" ? value.replace(/\/+$/u, "") : value).superRefine((value, context) => {
1418
1432
  if (!value.startsWith("/") || value === "$global" || value.includes("?") || value.includes("#")) {
1419
1433
  context.addIssue({
1420
1434
  code: "custom",
@@ -1424,7 +1438,7 @@ var renderedPathSchema = z14.string().trim().transform((value) => value !== "/"
1424
1438
  addSafeByteIssues(value, CONTINUE_SITE_SETUP_RENDERED_PATH_MAX_BYTES, "renderedPath", context);
1425
1439
  });
1426
1440
  function normalizedSafeTextSchema(maxBytes, label) {
1427
- return z14.string().transform((value) => value.trim().replace(new RegExp("\\p{White_Space}+", "gu"), " ")).pipe(z14.string().min(1)).superRefine((value, context) => {
1441
+ return z15.string().transform((value) => value.trim().replace(new RegExp("\\p{White_Space}+", "gu"), " ")).pipe(z15.string().min(1)).superRefine((value, context) => {
1428
1442
  addSafeByteIssues(value, maxBytes, label, context);
1429
1443
  if (label === "locatorHint" && unsafeLocator.test(value)) {
1430
1444
  context.addIssue({
@@ -1441,12 +1455,12 @@ var correctionItemBase = {
1441
1455
  requestedLabel: normalizedSafeTextSchema(CONTINUE_SITE_SETUP_REQUESTED_LABEL_MAX_BYTES, "requestedLabel").optional(),
1442
1456
  requestedSection: normalizedSafeTextSchema(CONTINUE_SITE_SETUP_REQUESTED_SECTION_MAX_BYTES, "requestedSection").optional(),
1443
1457
  requestedSubsection: normalizedSafeTextSchema(CONTINUE_SITE_SETUP_REQUESTED_SUBSECTION_MAX_BYTES, "requestedSubsection").optional(),
1444
- requestedSortOrder: z14.number().int().nonnegative().optional()
1458
+ requestedSortOrder: z15.number().int().nonnegative().optional()
1445
1459
  };
1446
- var setupCorrectionItemSchema = z14.discriminatedUnion("kind", [
1447
- z14.object({ kind: z14.literal("text"), ...correctionItemBase }).strict(),
1448
- z14.object({ kind: z14.literal("link"), ...correctionItemBase }).strict(),
1449
- z14.object({ kind: z14.literal("image"), ...correctionItemBase }).strict()
1460
+ var setupCorrectionItemSchema = z15.discriminatedUnion("kind", [
1461
+ z15.object({ kind: z15.literal("text"), ...correctionItemBase }).strict(),
1462
+ z15.object({ kind: z15.literal("link"), ...correctionItemBase }).strict(),
1463
+ z15.object({ kind: z15.literal("image"), ...correctionItemBase }).strict()
1450
1464
  ]).superRefine((item, context) => {
1451
1465
  if (item.requestedSubsection && !item.requestedSection) {
1452
1466
  context.addIssue({
@@ -1456,7 +1470,7 @@ var setupCorrectionItemSchema = z14.discriminatedUnion("kind", [
1456
1470
  });
1457
1471
  }
1458
1472
  });
1459
- var setupCorrectionItemsSchema = z14.array(setupCorrectionItemSchema).max(CONTINUE_SITE_SETUP_MAX_ITEMS).superRefine((items, context) => {
1473
+ var setupCorrectionItemsSchema = z15.array(setupCorrectionItemSchema).max(CONTINUE_SITE_SETUP_MAX_ITEMS).superRefine((items, context) => {
1460
1474
  const bytes = utf8Bytes(stableJsonStringify(items));
1461
1475
  if (bytes > CONTINUE_SITE_SETUP_ITEMS_MAX_BYTES) {
1462
1476
  context.addIssue({
@@ -1465,7 +1479,7 @@ var setupCorrectionItemsSchema = z14.array(setupCorrectionItemSchema).max(CONTIN
1465
1479
  });
1466
1480
  }
1467
1481
  });
1468
- var siteSetupOwnerEditabilityNoteSchema = z14.string().trim().max(SITE_SETUP_OWNER_EDITABILITY_NOTE_MAX_LENGTH).superRefine((value, context) => {
1482
+ var siteSetupOwnerEditabilityNoteSchema = z15.string().max(SITE_SETUP_OWNER_EDITABILITY_NOTE_MAX_LENGTH).superRefine((value, context) => {
1469
1483
  if (utf8Bytes(value) > SITE_SETUP_OWNER_EDITABILITY_NOTE_MAX_BYTES) {
1470
1484
  context.addIssue({
1471
1485
  code: "custom",
@@ -1479,27 +1493,190 @@ var siteSetupOwnerEditabilityNoteSchema = z14.string().trim().max(SITE_SETUP_OWN
1479
1493
  });
1480
1494
  }
1481
1495
  });
1482
- var canonicalHashSchema = z14.string().regex(/^sha256:[0-9a-f]{64}$/iu);
1496
+ var SITE_SETUP_MODULE_SCOPES = {
1497
+ editing: ["setup:read", "setup:write"],
1498
+ analytics: [
1499
+ "analytics:setup",
1500
+ "analytics:sync",
1501
+ "analytics:test",
1502
+ "analytics:readiness",
1503
+ "analytics:agent_instructions",
1504
+ "analytics:public_key_read"
1505
+ ]
1506
+ };
1507
+ var siteSetupModulesSchema = z15.array(z15.enum(["editing", "analytics"])).min(1).max(2).refine((modules) => modules[0] === "editing" && (modules.length === 1 || modules[1] === "analytics"), "Choose Editing with optional Analytics.");
1508
+ function siteSetupScopes(modules) {
1509
+ return modules.flatMap((module) => [...SITE_SETUP_MODULE_SCOPES[module]]);
1510
+ }
1511
+ function hasExactSiteSetupScopes(scopes, modules) {
1512
+ const matches = (expected) => scopes.length === expected.length && expected.every((scope) => scopes.includes(scope));
1513
+ return modules ? matches(siteSetupScopes(modules)) : matches(siteSetupScopes(["editing"])) || matches(siteSetupScopes(["editing", "analytics"]));
1514
+ }
1515
+ var siteSetupActivityInputSchema = z15.object({
1516
+ phase: z15.enum([
1517
+ "inspecting",
1518
+ "integrating",
1519
+ "deploying",
1520
+ "verifying",
1521
+ "repairing"
1522
+ ]),
1523
+ textId: z15.enum([
1524
+ "inspecting",
1525
+ "integrating",
1526
+ "deploying",
1527
+ "verifying",
1528
+ "repairing"
1529
+ ])
1530
+ }).strict().refine((value) => value.phase === value.textId, "Activity text must describe its phase.");
1531
+ var nextActionFields = {
1532
+ actor: z15.enum(["agent", "user", "siteplane"]),
1533
+ boundary: z15.enum([
1534
+ "source",
1535
+ "environment",
1536
+ "deployment",
1537
+ "verification",
1538
+ "authorization",
1539
+ "review"
1540
+ ]),
1541
+ precondition: z15.string().min(1).max(1e3),
1542
+ instruction: z15.string().min(1).max(2e3),
1543
+ expectedEvidence: z15.string().min(1).max(1e3)
1544
+ };
1545
+ var siteSetupNextActionSchema = z15.discriminatedUnion("kind", [
1546
+ z15.object({
1547
+ ...nextActionFields,
1548
+ kind: z15.literal("run_command"),
1549
+ command: z15.string().min(1).max(500)
1550
+ }).strict(),
1551
+ z15.object({ ...nextActionFields, kind: z15.literal("agent_edit") }).strict(),
1552
+ z15.object({ ...nextActionFields, kind: z15.literal("needs_user_action") }).strict(),
1553
+ z15.object({ ...nextActionFields, kind: z15.literal("wait") }).strict(),
1554
+ z15.object({ ...nextActionFields, kind: z15.literal("done") }).strict()
1555
+ ]);
1556
+ var siteSetupTaskRunSchema = z15.object({
1557
+ runId: z15.string().uuid(),
1558
+ mode: siteSetupRunModeSchema,
1559
+ status: siteSetupRunStatusSchema,
1560
+ fieldContractHash: z15.string().nullable(),
1561
+ deploymentOrigin: z15.string().url().nullable(),
1562
+ deploymentRevision: z15.string().nullable(),
1563
+ publicRuntimeKeyId: z15.string().uuid().nullable(),
1564
+ reviewVersion: z15.string().nullable()
1565
+ }).strict();
1566
+ var siteSetupTaskProgressSchema = z15.object({
1567
+ progressStage: z15.enum([
1568
+ "waiting_for_connection",
1569
+ "agent_connected",
1570
+ "project_context_loaded",
1571
+ "fields_prepared",
1572
+ "deployment_verification",
1573
+ "ready_for_review",
1574
+ "action_required",
1575
+ "completed"
1576
+ ]),
1577
+ lastVerifiedAt: z15.string().datetime().nullable(),
1578
+ lastActivityAt: z15.string().datetime(),
1579
+ activitySource: z15.enum(["user", "server", "agent"]),
1580
+ activityPhase: siteSetupActivityInputSchema.shape.phase.nullable(),
1581
+ activityTextId: siteSetupActivityInputSchema.shape.textId.nullable()
1582
+ }).strict();
1583
+ var siteSetupTaskSchema = z15.object({
1584
+ contractVersion: z15.literal("siteplane.setup-task.v2"),
1585
+ workflow: z15.enum(["initial_setup", "structure_update"]),
1586
+ goal: z15.string().min(1),
1587
+ modules: siteSetupModulesSchema,
1588
+ authorizationVersion: z15.number().int().positive(),
1589
+ authorizedScopes: z15.array(z15.string()).min(2),
1590
+ site: z15.object({
1591
+ siteId: z15.string().uuid(),
1592
+ portalPath: z15.string(),
1593
+ adminOrigin: z15.string().url()
1594
+ }).strict().nullable(),
1595
+ run: siteSetupTaskRunSchema.nullable(),
1596
+ progress: siteSetupTaskProgressSchema.nullable(),
1597
+ deployment: setupDeploymentRecordSchema.nullable(),
1598
+ revalidationGeneration: z15.number().int().positive(),
1599
+ assignment: z15.literal("owned"),
1600
+ deadline: z15.string().datetime().nullable(),
1601
+ userInstructions: z15.object({
1602
+ text: siteSetupOwnerEditabilityNoteSchema,
1603
+ source: z15.literal("user"),
1604
+ trust: z15.literal("untrusted")
1605
+ }).strict(),
1606
+ corrections: setupCorrectionItemsSchema,
1607
+ contentSelectionSummary: z15.object({
1608
+ text: siteSetupOwnerEditabilityNoteSchema,
1609
+ source: z15.literal("agent"),
1610
+ trust: z15.literal("untrusted")
1611
+ }).strict().nullable(),
1612
+ environmentBindings: z15.array(z15.object({
1613
+ name: z15.string().regex(/^[A-Z][A-Z0-9_]+$/u),
1614
+ source: z15.string().min(1),
1615
+ sensitivity: z15.enum(["public", "secret"]),
1616
+ usage: z15.enum(["build", "server", "build_and_server"]),
1617
+ evidence: z15.string().min(1)
1618
+ }).strict()),
1619
+ nextAction: siteSetupNextActionSchema,
1620
+ error: z15.object({
1621
+ code: z15.string().regex(/^[a-z0-9_.]+$/u).max(160),
1622
+ boundary: nextActionFields.boundary,
1623
+ responsibleActor: nextActionFields.actor,
1624
+ retryable: z15.boolean(),
1625
+ retryAfter: z15.number().int().nonnegative().nullable(),
1626
+ requestId: z15.string().regex(/^[A-Za-z0-9_-]+$/u).max(128)
1627
+ }).strict().nullable(),
1628
+ packages: z15.object({
1629
+ siteplane: z15.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u),
1630
+ runtimeNext: z15.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u)
1631
+ }).strict(),
1632
+ communication: z15.object({
1633
+ beforeSourceChanges: z15.string().min(1),
1634
+ updates: z15.array(z15.string().min(1)).min(1),
1635
+ blocker: z15.string().min(1)
1636
+ }).strict(),
1637
+ discovery: z15.object({
1638
+ inspectPublicRoutes: z15.boolean(),
1639
+ contentKinds: z15.array(z15.enum(["text", "longText", "link", "image"])).min(1),
1640
+ rules: z15.array(z15.string().min(1)).min(1)
1641
+ }).strict(),
1642
+ steps: z15.array(z15.object({
1643
+ id: z15.enum([
1644
+ "inspect_repository",
1645
+ "install_packages",
1646
+ "instrument_fields",
1647
+ "apply_contract",
1648
+ "deploy_production",
1649
+ "verify_deployment"
1650
+ ]),
1651
+ instruction: z15.string().min(1)
1652
+ }).strict()).length(6),
1653
+ completion: z15.object({
1654
+ verifiedStatus: z15.literal("ready_for_review"),
1655
+ instruction: z15.string().min(1)
1656
+ }).strict()
1657
+ }).strict().refine((task) => hasExactSiteSetupScopes(task.authorizedScopes, task.modules), "Task scopes must exactly match its modules.");
1658
+ var canonicalHashSchema = z15.string().regex(/^sha256:[0-9a-f]{64}$/iu);
1483
1659
  var deploymentRevisionSchema = normalizedSafeTextSchema(256, "deploymentRevision");
1484
- var continueSiteSetupInputSchema = z14.object({
1485
- siteId: z14.string().uuid(),
1486
- runId: z14.string().uuid(),
1660
+ var continueSiteSetupInputSchema = z15.object({
1661
+ siteId: z15.string().uuid(),
1662
+ runId: z15.string().uuid(),
1487
1663
  expectedFieldContractHash: canonicalHashSchema,
1488
1664
  expectedDeploymentRevision: deploymentRevisionSchema,
1489
1665
  correctionItems: setupCorrectionItemsSchema,
1490
1666
  ownerEditabilityNote: siteSetupOwnerEditabilityNoteSchema.optional()
1491
1667
  }).strict();
1492
- var saveSiteSetupOwnerNoteInputSchema = z14.object({
1493
- siteId: z14.string().uuid(),
1494
- runId: z14.string().uuid(),
1668
+ var saveSiteSetupOwnerNoteInputSchema = z15.object({
1669
+ siteId: z15.string().uuid(),
1670
+ runId: z15.string().uuid(),
1495
1671
  expectedFieldContractHash: canonicalHashSchema,
1496
1672
  expectedDeploymentRevision: deploymentRevisionSchema,
1497
1673
  ownerEditabilityNote: siteSetupOwnerEditabilityNoteSchema
1498
1674
  }).strict();
1499
- var siteSetupContextInputSchema = z14.object({}).strict();
1500
- var siteSetupApplyInputSchema = z14.object({
1675
+ var siteSetupContextInputSchema = z15.object({}).strict();
1676
+ var siteSetupApplyInputSchema = z15.object({
1501
1677
  fieldContract: siteFieldContractV1Schema,
1502
- editorDefinition: editorDefinitionV1Schema
1678
+ editorDefinition: editorDefinitionV1Schema,
1679
+ contentSelectionSummary: siteSetupOwnerEditabilityNoteSchema.refine((text) => text.trim().length > 0, "Describe the selected content, important exclusions and any open gaps.")
1503
1680
  }).strict().superRefine((input, context) => {
1504
1681
  const contractRoutesByFieldId = new Map(input.fieldContract.fields.map((field) => [
1505
1682
  field.fieldId,
@@ -1563,37 +1740,37 @@ var siteSetupApplyInputSchema = z14.object({
1563
1740
  }
1564
1741
  }
1565
1742
  });
1566
- var siteSetupVerifyInputSchema = z14.object({
1567
- deploymentUrl: z14.string().url().refine((value) => value.startsWith("https://")),
1743
+ var siteSetupVerifyInputSchema = z15.object({
1744
+ deploymentUrl: z15.string().url().refine((value) => value.startsWith("https://")),
1568
1745
  deploymentRevision: deploymentRevisionSchema,
1569
- wait: z14.boolean().optional()
1746
+ wait: z15.boolean().optional()
1570
1747
  }).strict();
1571
- var siteSetupEvidenceInputSchema = z14.object({
1572
- siteId: z14.string().uuid(),
1573
- runId: z14.string().uuid(),
1748
+ var siteSetupEvidenceInputSchema = z15.object({
1749
+ siteId: z15.string().uuid(),
1750
+ runId: z15.string().uuid(),
1574
1751
  expectedFieldContractHash: canonicalHashSchema,
1575
- expectedDeploymentOrigin: z14.string().url().refine((value) => value.startsWith("https://")),
1752
+ expectedDeploymentOrigin: z15.string().url().refine((value) => value.startsWith("https://")),
1576
1753
  expectedDeploymentRevision: deploymentRevisionSchema,
1577
- publicRuntimeKeyId: z14.string().uuid(),
1754
+ publicRuntimeKeyId: z15.string().uuid(),
1578
1755
  bridgeSessionId: normalizedSafeTextSchema(256, "bridgeSessionId"),
1579
- observations: z14.array(z14.object({
1756
+ observations: z15.array(z15.object({
1580
1757
  fieldId: fieldIdSchema,
1581
1758
  routeKey: siteFieldRouteKeySchema,
1582
1759
  renderedPath: renderedPathSchema,
1583
- targetResolved: z14.boolean(),
1584
- previewAck: z14.boolean()
1760
+ targetResolved: z15.boolean(),
1761
+ previewAck: z15.boolean()
1585
1762
  }).strict().refine((value) => !value.previewAck || value.targetResolved, "Preview ACK requires a resolved target.")).min(1).max(200)
1586
1763
  }).strict();
1587
- var completeSiteSetupInputSchema = z14.object({
1588
- siteId: z14.string().uuid(),
1589
- runId: z14.string().uuid(),
1764
+ var completeSiteSetupInputSchema = z15.object({
1765
+ siteId: z15.string().uuid(),
1766
+ runId: z15.string().uuid(),
1590
1767
  expectedFieldContractHash: canonicalHashSchema,
1591
1768
  expectedDeploymentRevision: deploymentRevisionSchema,
1592
1769
  expectedReadyFingerprint: canonicalHashSchema
1593
1770
  }).strict();
1594
- var refreshSiteSetupDeploymentInputSchema = z14.object({
1595
- siteId: z14.string().uuid(),
1596
- deploymentUrl: z14.string().url().refine((value) => value.startsWith("https://")),
1771
+ var refreshSiteSetupDeploymentInputSchema = z15.object({
1772
+ siteId: z15.string().uuid(),
1773
+ deploymentUrl: z15.string().url().refine((value) => value.startsWith("https://")),
1597
1774
  deploymentRevision: deploymentRevisionSchema
1598
1775
  }).strict();
1599
1776
  function addSafeByteIssues(value, maxBytes, label, context) {
@@ -1616,34 +1793,34 @@ function utf8Bytes(value) {
1616
1793
 
1617
1794
  // ../shared/dist/bridge-protocol.js
1618
1795
  var BRIDGE_PROTOCOL_VERSION = 2;
1619
- var bridgeRectSchema = z15.object({
1620
- x: z15.number(),
1621
- y: z15.number(),
1622
- width: z15.number().nonnegative(),
1623
- height: z15.number().nonnegative()
1796
+ var bridgeRectSchema = z16.object({
1797
+ x: z16.number(),
1798
+ y: z16.number(),
1799
+ width: z16.number().nonnegative(),
1800
+ height: z16.number().nonnegative()
1624
1801
  });
1625
- var previewTextValueSchema = z15.object({
1626
- fieldType: z15.enum(["text", "longText"]),
1627
- value: z15.string()
1802
+ var previewTextValueSchema = z16.object({
1803
+ fieldType: z16.enum(["text", "longText"]),
1804
+ value: z16.string()
1628
1805
  });
1629
- var previewLinkValueSchema = z15.object({
1630
- fieldType: z15.literal("link"),
1631
- value: z15.object({
1632
- href: z15.string().trim().min(1),
1633
- label: z15.string().trim().min(1).optional()
1806
+ var previewLinkValueSchema = z16.object({
1807
+ fieldType: z16.literal("link"),
1808
+ value: z16.object({
1809
+ href: z16.string().trim().min(1),
1810
+ label: z16.string().trim().min(1).optional()
1634
1811
  })
1635
1812
  });
1636
- var previewImageValueSchema = z15.object({
1637
- fieldType: z15.literal("image"),
1813
+ var previewImageValueSchema = z16.object({
1814
+ fieldType: z16.literal("image"),
1638
1815
  value: imageValueSchema
1639
1816
  });
1640
- var bridgePreviewValueSchema = z15.discriminatedUnion("fieldType", [
1817
+ var bridgePreviewValueSchema = z16.discriminatedUnion("fieldType", [
1641
1818
  previewTextValueSchema,
1642
1819
  previewLinkValueSchema,
1643
1820
  previewImageValueSchema
1644
1821
  ]);
1645
- var fieldSelectionModeSchema = z15.enum(["replace", "range", "toggle"]);
1646
- var fieldBridgeMissingContentActionSchema = z15.enum([
1822
+ var fieldSelectionModeSchema = z16.enum(["replace", "range", "toggle"]);
1823
+ var fieldBridgeMissingContentActionSchema = z16.enum([
1647
1824
  "add",
1648
1825
  "select",
1649
1826
  "remove"
@@ -1662,77 +1839,77 @@ var FIELD_BRIDGE_MESSAGE_TYPES = [
1662
1839
  "siteplane:field-bridge:preview-scrolled",
1663
1840
  "siteplane:field-bridge:error"
1664
1841
  ];
1665
- var canonicalFieldContractHashSchema = z15.string().regex(/^sha256:[0-9a-f]{64}$/u);
1666
- var bridgeSectionSchema = z15.string().trim().min(1).max(256);
1667
- var realRenderedPathSchema = z15.string().trim().refine((value) => value.startsWith("/") && value !== "$global" && !value.includes("?") && !value.includes("#"), "A field bridge rendered path must be a real canonical path.");
1668
- var fieldBridgeIdentitySchema = z15.object({
1669
- publicRuntimeKeyId: z15.string().uuid(),
1842
+ var canonicalFieldContractHashSchema = z16.string().regex(/^sha256:[0-9a-f]{64}$/u);
1843
+ var bridgeSectionSchema = z16.string().trim().min(1).max(256);
1844
+ var realRenderedPathSchema = z16.string().trim().refine((value) => value.startsWith("/") && value !== "$global" && !value.includes("?") && !value.includes("#"), "A field bridge rendered path must be a real canonical path.");
1845
+ var fieldBridgeIdentitySchema = z16.object({
1846
+ publicRuntimeKeyId: z16.string().uuid(),
1670
1847
  fieldContractHash: canonicalFieldContractHashSchema,
1671
- deploymentRevision: z15.string().trim().min(1).max(256)
1848
+ deploymentRevision: z16.string().trim().min(1).max(256)
1672
1849
  }).strict();
1673
- var fieldBridgeBaseSchema = z15.object({
1674
- version: z15.literal(BRIDGE_PROTOCOL_VERSION),
1675
- type: z15.enum(FIELD_BRIDGE_MESSAGE_TYPES),
1676
- siteId: z15.string().uuid(),
1677
- sessionId: z15.string().trim().min(1),
1678
- publicRuntimeKeyId: z15.string().uuid(),
1850
+ var fieldBridgeBaseSchema = z16.object({
1851
+ version: z16.literal(BRIDGE_PROTOCOL_VERSION),
1852
+ type: z16.enum(FIELD_BRIDGE_MESSAGE_TYPES),
1853
+ siteId: z16.string().uuid(),
1854
+ sessionId: z16.string().trim().min(1),
1855
+ publicRuntimeKeyId: z16.string().uuid(),
1679
1856
  fieldContractHash: canonicalFieldContractHashSchema,
1680
- deploymentRevision: z15.string().trim().min(1).max(256),
1681
- requestId: z15.string().trim().min(1),
1682
- sentAt: z15.string().datetime()
1857
+ deploymentRevision: z16.string().trim().min(1).max(256),
1858
+ requestId: z16.string().trim().min(1),
1859
+ sentAt: z16.string().datetime()
1683
1860
  }).strict();
1684
- var fieldBridgeFieldSchema = z15.object({
1861
+ var fieldBridgeFieldSchema = z16.object({
1685
1862
  fieldId: fieldIdSchema,
1686
1863
  fieldType: fieldTypeSchema,
1687
1864
  routeKey: siteFieldRouteKeySchema,
1688
1865
  renderedPath: realRenderedPathSchema,
1689
- sourcePath: z15.string().trim().min(1).optional(),
1866
+ sourcePath: z16.string().trim().min(1).optional(),
1690
1867
  editTarget: fieldIdSchema,
1691
1868
  rect: bridgeRectSchema,
1692
- targetResolved: z15.literal(true),
1869
+ targetResolved: z16.literal(true),
1693
1870
  currentValue: bridgePreviewValueSchema.optional()
1694
1871
  }).strict();
1695
1872
  var fieldBridgeInitMessageSchema = fieldBridgeBaseSchema.extend({
1696
- type: z15.literal("siteplane:field-bridge:init"),
1697
- payload: z15.object({
1698
- adminOrigin: z15.string().url(),
1699
- customerOrigin: z15.string().url(),
1873
+ type: z16.literal("siteplane:field-bridge:init"),
1874
+ payload: z16.object({
1875
+ adminOrigin: z16.string().url(),
1876
+ customerOrigin: z16.string().url(),
1700
1877
  renderedPath: realRenderedPathSchema
1701
1878
  }).strict()
1702
1879
  });
1703
1880
  var fieldBridgeReadyMessageSchema = fieldBridgeBaseSchema.extend({
1704
- type: z15.literal("siteplane:field-bridge:ready"),
1705
- payload: z15.object({
1881
+ type: z16.literal("siteplane:field-bridge:ready"),
1882
+ payload: z16.object({
1706
1883
  renderedPath: realRenderedPathSchema,
1707
- fieldCount: z15.number().int().nonnegative(),
1708
- capabilities: z15.tuple([
1709
- z15.literal("field_selection"),
1710
- z15.literal("preview_values"),
1711
- z15.literal("field_evidence"),
1712
- z15.literal("visual_field_control")
1884
+ fieldCount: z16.number().int().nonnegative(),
1885
+ capabilities: z16.tuple([
1886
+ z16.literal("field_selection"),
1887
+ z16.literal("preview_values"),
1888
+ z16.literal("field_evidence"),
1889
+ z16.literal("visual_field_control")
1713
1890
  ])
1714
1891
  }).strict()
1715
1892
  });
1716
1893
  var fieldBridgeFieldsMessageSchema = fieldBridgeBaseSchema.extend({
1717
- type: z15.literal("siteplane:field-bridge:fields"),
1718
- payload: z15.object({
1894
+ type: z16.literal("siteplane:field-bridge:fields"),
1895
+ payload: z16.object({
1719
1896
  renderedPath: realRenderedPathSchema,
1720
- fields: z15.array(fieldBridgeFieldSchema)
1897
+ fields: z16.array(fieldBridgeFieldSchema)
1721
1898
  }).strict()
1722
1899
  });
1723
1900
  var fieldBridgeSelectFieldMessageSchema = fieldBridgeBaseSchema.extend({
1724
- type: z15.literal("siteplane:field-bridge:select-field"),
1725
- payload: z15.object({
1901
+ type: z16.literal("siteplane:field-bridge:select-field"),
1902
+ payload: z16.object({
1726
1903
  fieldId: fieldIdSchema,
1727
1904
  selectionMode: fieldSelectionModeSchema
1728
1905
  }).strict()
1729
1906
  });
1730
1907
  var fieldBridgeSetSelectedFieldMessageSchema = fieldBridgeBaseSchema.extend({
1731
- type: z15.literal("siteplane:field-bridge:set-selected-field"),
1732
- payload: z15.object({
1733
- fieldIds: z15.array(fieldIdSchema).max(200),
1908
+ type: z16.literal("siteplane:field-bridge:set-selected-field"),
1909
+ payload: z16.object({
1910
+ fieldIds: z16.array(fieldIdSchema).max(200),
1734
1911
  primaryFieldId: fieldIdSchema.nullable(),
1735
- scrollIntoView: z15.boolean().optional()
1912
+ scrollIntoView: z16.boolean().optional()
1736
1913
  }).strict().superRefine((value, context) => {
1737
1914
  if (value.fieldIds.length > 0 && value.primaryFieldId === null) {
1738
1915
  context.addIssue({
@@ -1751,58 +1928,58 @@ var fieldBridgeSetSelectedFieldMessageSchema = fieldBridgeBaseSchema.extend({
1751
1928
  })
1752
1929
  });
1753
1930
  var fieldBridgeApplyPreviewValuesMessageSchema = fieldBridgeBaseSchema.extend({
1754
- type: z15.literal("siteplane:field-bridge:apply-preview-values"),
1755
- payload: z15.object({ values: z15.record(fieldIdSchema, bridgePreviewValueSchema) }).strict()
1931
+ type: z16.literal("siteplane:field-bridge:apply-preview-values"),
1932
+ payload: z16.object({ values: z16.record(fieldIdSchema, bridgePreviewValueSchema) }).strict()
1756
1933
  });
1757
1934
  var fieldBridgeTestPreviewValuesMessageSchema = fieldBridgeBaseSchema.extend({
1758
- type: z15.literal("siteplane:field-bridge:test-preview-values"),
1759
- payload: z15.object({ fieldIds: z15.array(fieldIdSchema).min(1).max(200) }).strict()
1935
+ type: z16.literal("siteplane:field-bridge:test-preview-values"),
1936
+ payload: z16.object({ fieldIds: z16.array(fieldIdSchema).min(1).max(200) }).strict()
1760
1937
  });
1761
1938
  var fieldBridgePreviewAckMessageSchema = fieldBridgeBaseSchema.extend({
1762
- type: z15.literal("siteplane:field-bridge:preview-ack"),
1763
- payload: z15.object({
1764
- ackRequestId: z15.string().trim().min(1),
1765
- kind: z15.enum(["apply", "test"]),
1939
+ type: z16.literal("siteplane:field-bridge:preview-ack"),
1940
+ payload: z16.object({
1941
+ ackRequestId: z16.string().trim().min(1),
1942
+ kind: z16.enum(["apply", "test"]),
1766
1943
  renderedPath: realRenderedPathSchema,
1767
- fieldIds: z15.array(fieldIdSchema).max(200)
1944
+ fieldIds: z16.array(fieldIdSchema).max(200)
1768
1945
  }).strict()
1769
1946
  });
1770
1947
  var fieldBridgeSetVisualControlStateMessageSchema = fieldBridgeBaseSchema.extend({
1771
- type: z15.literal("siteplane:field-bridge:set-visual-control-state"),
1772
- payload: z15.object({
1773
- enabled: z15.boolean(),
1774
- fieldProximityEnabled: z15.boolean(),
1775
- pendingKeys: z15.array(z15.string().trim().min(1).max(2048)).max(50),
1776
- activeFieldIds: z15.array(fieldIdSchema),
1777
- fieldSections: z15.record(fieldIdSchema, bridgeSectionSchema),
1778
- selectedPending: z15.array(z15.object({
1779
- key: z15.string().trim().min(1).max(2048),
1780
- locatorHint: z15.string().trim().min(1).max(2048)
1948
+ type: z16.literal("siteplane:field-bridge:set-visual-control-state"),
1949
+ payload: z16.object({
1950
+ enabled: z16.boolean(),
1951
+ fieldProximityEnabled: z16.boolean(),
1952
+ pendingKeys: z16.array(z16.string().trim().min(1).max(2048)).max(50),
1953
+ activeFieldIds: z16.array(fieldIdSchema),
1954
+ fieldSections: z16.record(fieldIdSchema, bridgeSectionSchema),
1955
+ selectedPending: z16.array(z16.object({
1956
+ key: z16.string().trim().min(1).max(2048),
1957
+ locatorHint: z16.string().trim().min(1).max(2048)
1781
1958
  }).strict()).max(50),
1782
- scrollPendingIntoView: z15.boolean().optional()
1959
+ scrollPendingIntoView: z16.boolean().optional()
1783
1960
  }).strict()
1784
1961
  });
1785
1962
  var fieldBridgeMissingContentMessageSchema = fieldBridgeBaseSchema.extend({
1786
- type: z15.literal("siteplane:field-bridge:missing-content"),
1787
- payload: z15.object({
1963
+ type: z16.literal("siteplane:field-bridge:missing-content"),
1964
+ payload: z16.object({
1788
1965
  action: fieldBridgeMissingContentActionSchema,
1789
1966
  item: setupCorrectionItemSchema,
1790
1967
  suggestedSection: bridgeSectionSchema.optional()
1791
1968
  }).strict()
1792
1969
  });
1793
1970
  var fieldBridgePreviewScrolledMessageSchema = fieldBridgeBaseSchema.extend({
1794
- type: z15.literal("siteplane:field-bridge:preview-scrolled"),
1795
- payload: z15.object({ renderedPath: realRenderedPathSchema }).strict()
1971
+ type: z16.literal("siteplane:field-bridge:preview-scrolled"),
1972
+ payload: z16.object({ renderedPath: realRenderedPathSchema }).strict()
1796
1973
  });
1797
1974
  var fieldBridgeErrorMessageSchema = fieldBridgeBaseSchema.extend({
1798
- type: z15.literal("siteplane:field-bridge:error"),
1799
- payload: z15.object({
1800
- code: z15.string().trim().min(1),
1801
- message: z15.string().trim().min(1),
1802
- recoverable: z15.boolean().default(false)
1975
+ type: z16.literal("siteplane:field-bridge:error"),
1976
+ payload: z16.object({
1977
+ code: z16.string().trim().min(1),
1978
+ message: z16.string().trim().min(1),
1979
+ recoverable: z16.boolean().default(false)
1803
1980
  }).strict()
1804
1981
  });
1805
- var fieldBridgeMessageSchema = z15.discriminatedUnion("type", [
1982
+ var fieldBridgeMessageSchema = z16.discriminatedUnion("type", [
1806
1983
  fieldBridgeInitMessageSchema,
1807
1984
  fieldBridgeReadyMessageSchema,
1808
1985
  fieldBridgeFieldsMessageSchema,
@@ -1818,51 +1995,51 @@ var fieldBridgeMessageSchema = z15.discriminatedUnion("type", [
1818
1995
  ]);
1819
1996
 
1820
1997
  // ../shared/dist/commands.js
1821
- import { z as z17 } from "zod";
1998
+ import { z as z18 } from "zod";
1822
1999
 
1823
2000
  // ../shared/dist/errors.js
1824
- import { z as z16 } from "zod";
2001
+ import { z as z17 } from "zod";
1825
2002
  var UI_ERROR_CATEGORIES = [
1826
2003
  "site_member",
1827
2004
  "workspace_member",
1828
2005
  "internal"
1829
2006
  ];
1830
- var uiErrorCategorySchema = z16.enum(UI_ERROR_CATEGORIES);
1831
- var commandErrorPayloadSchema = z16.object({
1832
- code: z16.string().min(1),
1833
- message: z16.string().min(1),
2007
+ var uiErrorCategorySchema = z17.enum(UI_ERROR_CATEGORIES);
2008
+ var commandErrorPayloadSchema = z17.object({
2009
+ code: z17.string().min(1),
2010
+ message: z17.string().min(1),
1834
2011
  category: uiErrorCategorySchema,
1835
- details: z16.record(z16.string(), z16.unknown()).optional()
2012
+ details: z17.record(z17.string(), z17.unknown()).optional()
1836
2013
  });
1837
2014
 
1838
2015
  // ../shared/dist/commands.js
1839
- var commandSuccessSchema = z17.object({
1840
- ok: z17.literal(true),
1841
- data: z17.unknown()
2016
+ var commandSuccessSchema = z18.object({
2017
+ ok: z18.literal(true),
2018
+ data: z18.unknown()
1842
2019
  });
1843
- var commandErrorSchema = z17.object({
1844
- ok: z17.literal(false),
2020
+ var commandErrorSchema = z18.object({
2021
+ ok: z18.literal(false),
1845
2022
  error: commandErrorPayloadSchema
1846
2023
  });
1847
- var commandResultSchema = z17.discriminatedUnion("ok", [
2024
+ var commandResultSchema = z18.discriminatedUnion("ok", [
1848
2025
  commandSuccessSchema,
1849
2026
  commandErrorSchema
1850
2027
  ]);
1851
2028
 
1852
2029
  // ../shared/dist/editor-settings.js
1853
- import { z as z18 } from "zod";
1854
- var editorColorSchemeSchema = z18.enum([
2030
+ import { z as z19 } from "zod";
2031
+ var editorColorSchemeSchema = z19.enum([
1855
2032
  "paper",
1856
2033
  "slate",
1857
2034
  "siteplane",
1858
2035
  "bloom"
1859
2036
  ]);
1860
2037
  var editorColorSchemes = editorColorSchemeSchema.options;
1861
- var editorColorModeSchema = z18.enum(["light", "dark"]);
2038
+ var editorColorModeSchema = z19.enum(["light", "dark"]);
1862
2039
  var editorColorModes = editorColorModeSchema.options;
1863
2040
 
1864
2041
  // ../shared/dist/site-slug.js
1865
- import { z as z19 } from "zod";
2042
+ import { z as z20 } from "zod";
1866
2043
  var SITE_SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
1867
2044
  var RESERVED_SITE_SLUGS = /* @__PURE__ */ new Set([
1868
2045
  "account",
@@ -1879,31 +2056,31 @@ var RESERVED_SITE_SLUGS = /* @__PURE__ */ new Set([
1879
2056
  "support",
1880
2057
  "workspace"
1881
2058
  ]);
1882
- var siteSlugSchema = z19.string().regex(SITE_SLUG_PATTERN, "Invalid site slug").refine((value) => !RESERVED_SITE_SLUGS.has(value), "Reserved site slug");
2059
+ var siteSlugSchema = z20.string().regex(SITE_SLUG_PATTERN, "Invalid site slug").refine((value) => !RESERVED_SITE_SLUGS.has(value), "Reserved site slug");
1883
2060
 
1884
2061
  // ../shared/dist/entitlements.js
1885
- import { z as z20 } from "zod";
1886
- var entitlementSnapshotSchema = z20.object({
1887
- planKey: z20.string().trim().min(1),
1888
- sitesLimit: z20.number().int().positive(),
1889
- siteMembersPerSiteLimit: z20.number().int().positive(),
1890
- customAdminDomainsEnabled: z20.boolean(),
1891
- agentMcpEnabled: z20.boolean(),
1892
- emailNotificationsEnabled: z20.boolean(),
1893
- analyticsEnabled: z20.boolean(),
1894
- analyticsSitesLimit: z20.number().int().min(0),
1895
- analyticsPortalPerformanceEnabled: z20.boolean(),
1896
- analyticsMonthlyReportsEnabled: z20.boolean(),
1897
- analyticsAiSummaryEnabled: z20.boolean(),
1898
- analyticsRawEventRetentionDays: z20.number().int().min(1).max(ANALYTICS_RETENTION_DEFAULTS.maxRawEventRetentionDaysWithoutAdr),
1899
- analyticsMonthlyEventLimit: z20.number().int().min(0),
1900
- bookingEnabled: z20.boolean().default(false),
1901
- bookingSitesLimit: z20.number().int().min(0).default(0),
1902
- bookingPortalManagementEnabled: z20.boolean().default(false),
1903
- bookingResourcesPerSiteLimit: z20.number().int().min(0).default(0),
1904
- bookingServicesPerSiteLimit: z20.number().int().min(0).default(0),
1905
- bookingMonthlyBookingsLimit: z20.number().int().min(0).default(0),
1906
- bookingRemindersEnabled: z20.boolean().default(false)
2062
+ import { z as z21 } from "zod";
2063
+ var entitlementSnapshotSchema = z21.object({
2064
+ planKey: z21.string().trim().min(1),
2065
+ sitesLimit: z21.number().int().positive(),
2066
+ siteMembersPerSiteLimit: z21.number().int().positive(),
2067
+ customAdminDomainsEnabled: z21.boolean(),
2068
+ agentMcpEnabled: z21.boolean(),
2069
+ emailNotificationsEnabled: z21.boolean(),
2070
+ analyticsEnabled: z21.boolean(),
2071
+ analyticsSitesLimit: z21.number().int().min(0),
2072
+ analyticsPortalPerformanceEnabled: z21.boolean(),
2073
+ analyticsMonthlyReportsEnabled: z21.boolean(),
2074
+ analyticsAiSummaryEnabled: z21.boolean(),
2075
+ analyticsRawEventRetentionDays: z21.number().int().min(1).max(ANALYTICS_RETENTION_DEFAULTS.maxRawEventRetentionDaysWithoutAdr),
2076
+ analyticsMonthlyEventLimit: z21.number().int().min(0),
2077
+ bookingEnabled: z21.boolean().default(false),
2078
+ bookingSitesLimit: z21.number().int().min(0).default(0),
2079
+ bookingPortalManagementEnabled: z21.boolean().default(false),
2080
+ bookingResourcesPerSiteLimit: z21.number().int().min(0).default(0),
2081
+ bookingServicesPerSiteLimit: z21.number().int().min(0).default(0),
2082
+ bookingMonthlyBookingsLimit: z21.number().int().min(0).default(0),
2083
+ bookingRemindersEnabled: z21.boolean().default(false)
1907
2084
  });
1908
2085
  var EARLY_ACCESS_PLAN = {
1909
2086
  planKey: "early_access_2026",
@@ -1929,7 +2106,7 @@ var EARLY_ACCESS_PLAN = {
1929
2106
  };
1930
2107
 
1931
2108
  // ../shared/dist/readiness.js
1932
- import { z as z21 } from "zod";
2109
+ import { z as z22 } from "zod";
1933
2110
  var READINESS_CHECK_KEYS = [
1934
2111
  "agent_instructions_installed",
1935
2112
  "runtime_package_connected",
@@ -1944,19 +2121,19 @@ var READINESS_STATUSES = [
1944
2121
  "failing",
1945
2122
  "warning"
1946
2123
  ];
1947
- var readinessCheckKeySchema = z21.enum(READINESS_CHECK_KEYS);
1948
- var readinessStatusSchema = z21.enum(READINESS_STATUSES);
2124
+ var readinessCheckKeySchema = z22.enum(READINESS_CHECK_KEYS);
2125
+ var readinessStatusSchema = z22.enum(READINESS_STATUSES);
1949
2126
 
1950
2127
  // ../shared/dist/site.js
1951
- import { z as z22 } from "zod";
2128
+ import { z as z23 } from "zod";
1952
2129
  var SITE_STATUSES = [
1953
2130
  "setup",
1954
2131
  "active",
1955
2132
  "disabled",
1956
2133
  "archived"
1957
2134
  ];
1958
- var siteStatusSchema = z22.enum(SITE_STATUSES);
1959
- var websiteUrlSchema = z22.string().trim().url().refine((value) => value.startsWith("https://") || value.startsWith("http://"), "Website URL must use http or https");
2135
+ var siteStatusSchema = z23.enum(SITE_STATUSES);
2136
+ var websiteUrlSchema = z23.string().trim().url().refine((value) => value.startsWith("https://") || value.startsWith("http://"), "Website URL must use http or https");
1960
2137
 
1961
2138
  // ../cli/dist/analytics/manifest.js
1962
2139
  async function readAnalyticsManifestFile(manifestPath) {
@@ -2283,17 +2460,17 @@ function readJsxAttributes(attributes) {
2283
2460
  if (!ts.isJsxAttribute(attribute)) {
2284
2461
  continue;
2285
2462
  }
2286
- const name = attribute.name.getText();
2463
+ const name2 = attribute.name.getText();
2287
2464
  if (!attribute.initializer) {
2288
- values.set(name, true);
2465
+ values.set(name2, true);
2289
2466
  continue;
2290
2467
  }
2291
2468
  if (ts.isStringLiteral(attribute.initializer)) {
2292
- values.set(name, attribute.initializer.text);
2469
+ values.set(name2, attribute.initializer.text);
2293
2470
  continue;
2294
2471
  }
2295
2472
  if (ts.isJsxExpression(attribute.initializer)) {
2296
- values.set(name, staticExpressionValue(attribute.initializer.expression));
2473
+ values.set(name2, staticExpressionValue(attribute.initializer.expression));
2297
2474
  }
2298
2475
  }
2299
2476
  return values;
@@ -2761,16 +2938,16 @@ async function analyticsReadinessCommand(input) {
2761
2938
  }
2762
2939
 
2763
2940
  // ../cli/dist/commands/analytics/public-key.js
2764
- import { z as z23 } from "zod";
2765
- var responseSchema = z23.object({
2766
- ok: z23.literal(true),
2767
- data: z23.object({
2768
- rawPublicKey: z23.string().regex(/^pk_siteplane_[A-Za-z0-9_-]{16}_[A-Za-z0-9_-]{43}$/)
2941
+ import { z as z24 } from "zod";
2942
+ var responseSchema = z24.object({
2943
+ ok: z24.literal(true),
2944
+ data: z24.object({
2945
+ rawPublicKey: z24.string().regex(/^pk_siteplane_[A-Za-z0-9_-]{16}_[A-Za-z0-9_-]{43}$/)
2769
2946
  })
2770
2947
  });
2771
- var errorResponseSchema = z23.object({
2772
- error: z23.object({
2773
- code: z23.string().regex(/^[a-z][a-z0-9_.-]+$/u)
2948
+ var errorResponseSchema = z24.object({
2949
+ error: z24.object({
2950
+ code: z24.string().regex(/^[a-z][a-z0-9_.-]+$/u)
2774
2951
  })
2775
2952
  });
2776
2953
  async function analyticsPublicKeyCommand(input) {
@@ -2990,14 +3167,22 @@ async function bookingTestCommand(input) {
2990
3167
  import { access, chmod, open, readFile as readFile5, rename, rm } from "fs/promises";
2991
3168
  import { randomUUID as randomUUID2 } from "crypto";
2992
3169
  import { join as join3 } from "path";
2993
- import { z as z24 } from "zod";
2994
- var siteplaneProjectConfigSchema = z24.object({
2995
- version: z24.literal(1),
2996
- apiBaseUrl: z24.string().url(),
2997
- siteId: z24.string().uuid(),
2998
- publicSiteKeyId: z24.string().uuid(),
2999
- publicSiteKey: z24.string().trim().min(1),
3000
- fieldContractHash: z24.string().regex(/^sha256:[0-9a-f]{64}$/iu).optional()
3170
+ import { z as z25 } from "zod";
3171
+ var siteplaneProjectConfigSchema = z25.object({
3172
+ version: z25.literal(2),
3173
+ apiBaseUrl: z25.string().url(),
3174
+ siteId: z25.string().uuid(),
3175
+ publicSiteKeyId: z25.string().uuid(),
3176
+ publicSiteKey: z25.string().trim().min(1),
3177
+ runId: z25.string().uuid(),
3178
+ appDirectory: z25.string().min(1),
3179
+ taskContractVersion: z25.literal("siteplane.setup-task.v2"),
3180
+ adminProtocolVersion: z25.literal(2),
3181
+ packages: z25.object({
3182
+ siteplane: z25.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u),
3183
+ runtimeNext: z25.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u)
3184
+ }).strict(),
3185
+ fieldContractHash: z25.string().regex(/^sha256:[0-9a-f]{64}$/iu).optional()
3001
3186
  }).strict();
3002
3187
  async function readProjectPackageJson(projectDir) {
3003
3188
  return JSON.parse(await readFile5(join3(projectDir, "package.json"), "utf8"));
@@ -3054,16 +3239,21 @@ async function writeJsonAtomically(path, value, mode) {
3054
3239
  // ../cli/dist/config/credentials.js
3055
3240
  import { execFile } from "child_process";
3056
3241
  import { randomUUID as randomUUID3 } from "crypto";
3057
- import { chmod as chmod2, mkdir as mkdir5, open as open2, readFile as readFile6, rename as rename2, rm as rm2 } from "fs/promises";
3242
+ import { chmod as chmod2, lstat, mkdir as mkdir5, open as open2, readFile as readFile6, rename as rename2, rm as rm2 } from "fs/promises";
3058
3243
  import { dirname as dirname5, join as join4, relative } from "path";
3059
3244
  import { promisify } from "util";
3060
- import { z as z25 } from "zod";
3061
- var siteplaneCredentialsSchema = z25.object({
3062
- accessToken: z25.string().trim().min(1),
3063
- siteRevalidationSecret: z25.string().trim().min(1)
3245
+ import { z as z26 } from "zod";
3246
+ var siteplaneCredentialsSchema = z26.object({
3247
+ accessToken: z26.string().trim().min(1),
3248
+ siteRevalidationSecret: z26.string().trim().min(1),
3249
+ adminSession: z26.object({
3250
+ secret: z26.string().regex(/^[A-Za-z0-9_-]{43}$/u),
3251
+ generation: z26.string().uuid()
3252
+ }).strict().optional()
3064
3253
  }).strict();
3065
3254
  async function assertLocalCredentialsPathSafe(projectDir, options = {}) {
3066
3255
  const path = join4(projectDir, ".siteplane/credentials.json");
3256
+ await assertCredentialFilesystemSafe(projectDir);
3067
3257
  const isTrackedFile = options.isTrackedFile ?? ((targetPath) => gitPathMatches(projectDir, targetPath, "ls-files"));
3068
3258
  const isIgnoredFile = options.isIgnoredFile ?? ((targetPath) => gitPathMatches(projectDir, targetPath, "check-ignore"));
3069
3259
  if (await isTrackedFile(path)) {
@@ -3074,6 +3264,27 @@ async function assertLocalCredentialsPathSafe(projectDir, options = {}) {
3074
3264
  }
3075
3265
  return path;
3076
3266
  }
3267
+ async function assertCredentialFilesystemSafe(projectDir) {
3268
+ for (const [path, directory, mode] of [
3269
+ [projectDir, true, null],
3270
+ [join4(projectDir, ".siteplane"), true, 448],
3271
+ [join4(projectDir, ".siteplane/credentials.json"), false, 384]
3272
+ ]) {
3273
+ const stat = await lstat(path).catch((error) => {
3274
+ if (error.code === "ENOENT")
3275
+ return null;
3276
+ throw error;
3277
+ });
3278
+ if (!stat)
3279
+ continue;
3280
+ if (stat.isSymbolicLink() || (directory ? !stat.isDirectory() : !stat.isFile())) {
3281
+ throw new Error("Siteplane credential paths must not contain symlinks or unexpected file types.");
3282
+ }
3283
+ if (mode !== null && (stat.mode & 511) !== mode) {
3284
+ throw new Error(`Siteplane credential ${directory ? "directory" : "file"} permissions must be ${mode.toString(8)} before setup.`);
3285
+ }
3286
+ }
3287
+ }
3077
3288
  async function writeLocalCredentials(projectDir, credentials, options = {}) {
3078
3289
  const path = await assertLocalCredentialsPathSafe(projectDir, options);
3079
3290
  const parsed = siteplaneCredentialsSchema.parse(credentials);
@@ -3082,6 +3293,7 @@ async function writeLocalCredentials(projectDir, credentials, options = {}) {
3082
3293
  return path;
3083
3294
  }
3084
3295
  async function readLocalCredentials(projectDir) {
3296
+ await assertCredentialFilesystemSafe(projectDir);
3085
3297
  return siteplaneCredentialsSchema.parse(JSON.parse(await readFile6(join4(projectDir, ".siteplane/credentials.json"), "utf8")));
3086
3298
  }
3087
3299
  async function writeCredentialsAtomically(path, credentials) {
@@ -3117,8 +3329,467 @@ async function gitPathMatches(projectDir, path, command) {
3117
3329
  }
3118
3330
 
3119
3331
  // ../cli/dist/commands/init.js
3332
+ import { access as access3 } from "fs/promises";
3333
+ import { join as join7, relative as relative3 } from "path";
3334
+ import { z as z29 } from "zod";
3335
+
3336
+ // ../cli/dist/commands/setup-preflight.js
3337
+ import { createHash as createHash2 } from "crypto";
3338
+ import semver from "semver";
3339
+ import { minimatch } from "minimatch";
3340
+ import { parse as parseYaml } from "yaml";
3341
+
3342
+ // ../cli/dist/config/installed-package.js
3343
+ import { createRequire } from "module";
3344
+ import { readFile as readFile7 } from "fs/promises";
3120
3345
  import { join as join5 } from "path";
3121
- import { z as z26 } from "zod";
3346
+ import { z as z27 } from "zod";
3347
+ var packageSchema = z27.object({
3348
+ name: z27.string().optional(),
3349
+ version: z27.string(),
3350
+ peerDependencies: z27.record(z27.string(), z27.string()).optional()
3351
+ });
3352
+ async function readInstalledPackage(projectDir, name2) {
3353
+ const require2 = createRequire(join5(projectDir, "package.json"));
3354
+ for (const directory of require2.resolve.paths(name2) ?? []) {
3355
+ try {
3356
+ return packageSchema.parse(JSON.parse(await readFile7(join5(directory, name2, "package.json"), "utf8")));
3357
+ } catch (error) {
3358
+ if (error.code !== "ENOENT")
3359
+ throw error;
3360
+ }
3361
+ }
3362
+ throw new Error(`Install ${name2} with the canonical package manager before setup.`);
3363
+ }
3364
+ async function assertExactSetupPackages(projectDir, packages) {
3365
+ const manifest = z27.object({
3366
+ dependencies: z27.record(z27.string(), z27.string()).optional(),
3367
+ devDependencies: z27.record(z27.string(), z27.string()).optional()
3368
+ }).parse(JSON.parse(await readFile7(join5(projectDir, "package.json"), "utf8")));
3369
+ for (const [name2, version2] of [
3370
+ ["siteplane", packages.siteplane],
3371
+ ["@siteplane/runtime-next", packages.runtimeNext]
3372
+ ]) {
3373
+ const declared = manifest.dependencies?.[name2] ?? manifest.devDependencies?.[name2];
3374
+ const installed = await readInstalledPackage(projectDir, name2);
3375
+ if (declared !== version2 || installed.version !== version2)
3376
+ throw new Error("The installed Siteplane packages must match the current task's exact pins.");
3377
+ }
3378
+ }
3379
+
3380
+ // ../cli/dist/commands/setup-preflight.js
3381
+ import { access as access2, readFile as readFile8, realpath, readdir, readlink } from "fs/promises";
3382
+ import { dirname as dirname6, join as join6, relative as relative2, resolve, sep } from "path";
3383
+ import { z as z28 } from "zod";
3384
+
3385
+ // ../cli/dist/commands/setup-process.js
3386
+ import { spawn } from "child_process";
3387
+ var SetupFailure = class extends Error {
3388
+ code;
3389
+ constructor(code, message) {
3390
+ super(message);
3391
+ this.code = code;
3392
+ }
3393
+ };
3394
+ async function setupProcess(binary, args, options) {
3395
+ return new Promise((resolve5, reject) => {
3396
+ const child = spawn(binary, args, {
3397
+ cwd: options.cwd,
3398
+ env: process.env,
3399
+ stdio: ["pipe", "pipe", "pipe"]
3400
+ });
3401
+ const chunks = [];
3402
+ const errors = [];
3403
+ let errorBytes = 0;
3404
+ let bytes = 0;
3405
+ let exceeded = false;
3406
+ const timer = setTimeout(() => {
3407
+ exceeded = true;
3408
+ child.kill("SIGKILL");
3409
+ }, options.timeoutMs ?? 12e4);
3410
+ const capture = (chunk) => {
3411
+ bytes += chunk.length;
3412
+ if (bytes > 4 * 1024 * 1024) {
3413
+ exceeded = true;
3414
+ child.kill("SIGKILL");
3415
+ } else
3416
+ chunks.push(chunk);
3417
+ };
3418
+ child.stdout.on("data", capture);
3419
+ child.stderr.on("data", (chunk) => {
3420
+ errorBytes += chunk.length;
3421
+ if (errorBytes <= 4 * 1024 * 1024)
3422
+ errors.push(chunk);
3423
+ else {
3424
+ exceeded = true;
3425
+ child.kill("SIGKILL");
3426
+ }
3427
+ });
3428
+ child.on("error", () => {
3429
+ clearTimeout(timer);
3430
+ reject(new SetupFailure("local_tool_unavailable", `${binary} could not be started. Check the local installation and access.`));
3431
+ });
3432
+ child.on("close", (code) => {
3433
+ clearTimeout(timer);
3434
+ if (exceeded)
3435
+ reject(new SetupFailure("provider_timeout", "The operation exceeded its time or response limit; inspect the existing operation before retrying."));
3436
+ else if (code !== 0) {
3437
+ const diagnostic = Buffer.concat([...chunks, ...errors]).toString("utf8");
3438
+ if (/\b(?:401|403|unauthorized|forbidden|not authenticated|not authorized|login required|E401|E403)\b/iu.test(diagnostic))
3439
+ reject(new SetupFailure("provider_access_denied", `${binary} denied access. Restore the existing local login and project/package permissions before retrying.`));
3440
+ else if (/\b(?:ECONNRESET|ETIMEDOUT|ENOTFOUND|EAI_AGAIN|ECONNREFUSED|429|502|503|504)\b/iu.test(diagnostic))
3441
+ reject(new SetupFailure("provider_transport_failed", `${binary} could not reach its provider. Inspect the existing operation before retrying.`));
3442
+ else
3443
+ reject(new SetupFailure("local_command_failed", `${binary} failed. Check its local login, permissions and project configuration.`));
3444
+ } else
3445
+ resolve5(Buffer.concat(options.includeStderr ? [...chunks, ...errors] : chunks).toString("utf8"));
3446
+ });
3447
+ child.stdin.on("error", () => {
3448
+ });
3449
+ child.stdin.end(options.input);
3450
+ });
3451
+ }
3452
+ async function setupVercelApi(cwd, endpoint, body, method, options) {
3453
+ const args = ["api", endpoint, "--raw"];
3454
+ if (body !== void 0)
3455
+ args.push("--method", method ?? "POST", "--input", "-");
3456
+ else if (method)
3457
+ args.push("--method", method);
3458
+ let output = "";
3459
+ for (let attempt = 0; attempt < 3; attempt++) {
3460
+ const remaining = (options?.deadline ?? Date.now() + 3e4) - Date.now();
3461
+ if (remaining <= 0)
3462
+ throw new SetupFailure("provider_deadline_exceeded", "The provider deadline expired; inspect the recorded operation before continuing.");
3463
+ try {
3464
+ output = await setupProcess("vercel", args, {
3465
+ cwd,
3466
+ timeoutMs: Math.min(3e4, remaining),
3467
+ ...body === void 0 ? {} : { input: JSON.stringify(body) }
3468
+ });
3469
+ break;
3470
+ } catch (error) {
3471
+ if (!(error instanceof SetupFailure) || error.code !== "provider_transport_failed" || attempt === 2)
3472
+ throw error;
3473
+ await new Promise((resolve5) => setTimeout(resolve5, Math.min(1e3 * 2 ** attempt, Math.max(0, (options?.deadline ?? Date.now() + 2e3) - Date.now()))));
3474
+ }
3475
+ }
3476
+ try {
3477
+ return JSON.parse(output);
3478
+ } catch {
3479
+ throw new SetupFailure("invalid_provider_response", "Vercel did not return valid JSON for the bound project.");
3480
+ }
3481
+ }
3482
+
3483
+ // ../cli/dist/commands/setup-preflight.js
3484
+ var manifestSchema = z28.object({
3485
+ packageManager: z28.string().optional(),
3486
+ engines: z28.object({ node: z28.string().optional() }).optional(),
3487
+ dependencies: z28.record(z28.string(), z28.string()).optional(),
3488
+ devDependencies: z28.record(z28.string(), z28.string()).optional(),
3489
+ scripts: z28.record(z28.string(), z28.string()).optional(),
3490
+ workspaces: z28.unknown().optional()
3491
+ });
3492
+ var vercelProjectSchema = z28.object({
3493
+ id: z28.string(),
3494
+ accountId: z28.string(),
3495
+ name: z28.string(),
3496
+ nodeVersion: z28.string().nullable().optional(),
3497
+ rootDirectory: z28.string().nullable().optional(),
3498
+ framework: z28.string().nullable().optional(),
3499
+ installCommand: z28.string().nullable().optional(),
3500
+ buildCommand: z28.string().nullable().optional(),
3501
+ link: z28.object({
3502
+ type: z28.string(),
3503
+ repo: z28.string().optional(),
3504
+ repoId: z28.union([z28.string(), z28.number()]).optional(),
3505
+ org: z28.string().optional(),
3506
+ productionBranch: z28.string().optional()
3507
+ }).nullable().optional()
3508
+ });
3509
+ var exists = async (path) => access2(path).then(() => true, () => false);
3510
+ var fail = (code, message) => {
3511
+ throw new SetupFailure(code, message);
3512
+ };
3513
+ var stable = (version2, major) => new RegExp(`^${major}\\.\\d+\\.\\d+$`, "u").test(version2);
3514
+ async function inspectSetupLocalProject(projectDir) {
3515
+ const projectDirectory = await realpath(resolve(projectDir));
3516
+ const manifest = manifestSchema.parse(JSON.parse(await readFile8(join6(projectDirectory, "package.json"), "utf8")));
3517
+ const apps = (await Promise.all(["app", "src/app"].map(async (path) => await exists(join6(projectDirectory, path)) ? path : null))).filter((path) => path !== null);
3518
+ if (apps.length !== 1)
3519
+ fail("ambiguous_app_directory", "Select one Next.js App Router application with exactly one app/ or src/app/ directory.");
3520
+ const repositoryRoot = (await setupProcess("git", ["rev-parse", "--show-toplevel"], {
3521
+ cwd: projectDirectory
3522
+ })).trim();
3523
+ const tracked = await setupProcess("git", ["ls-files", "--stage", "-z"], {
3524
+ cwd: repositoryRoot
3525
+ });
3526
+ for (const entry of tracked.split("\0").filter(Boolean)) {
3527
+ if (entry.startsWith("160000 "))
3528
+ fail("git_submodule_conflict", "The repository contains Git submodules. Resolve the explicit deployment source boundary before setup; the CLI will not copy external repositories.");
3529
+ if (!entry.startsWith("120000 "))
3530
+ continue;
3531
+ const path = join6(repositoryRoot, entry.slice(entry.indexOf(" ") + 1));
3532
+ const target = resolve(dirname6(path), await readlink(path));
3533
+ const bound = relative2(repositoryRoot, target);
3534
+ if (bound.startsWith("..") || bound.startsWith(sep))
3535
+ fail("external_symlink_conflict", "A tracked symbolic link points outside this repository. Resolve that source boundary before setup; local external files cannot enter a deployment.");
3536
+ }
3537
+ let lockfileRoot = projectDirectory;
3538
+ const lockNames = [
3539
+ "package-lock.json",
3540
+ "npm-shrinkwrap.json",
3541
+ "pnpm-lock.yaml",
3542
+ "yarn.lock",
3543
+ "bun.lock",
3544
+ "bun.lockb"
3545
+ ];
3546
+ let locks = [];
3547
+ for (; ; ) {
3548
+ locks = (await Promise.all(lockNames.map(async (name3) => await exists(join6(lockfileRoot, name3)) ? name3 : null))).filter((name3) => name3 !== null);
3549
+ if (locks.length || lockfileRoot === repositoryRoot || dirname6(lockfileRoot) === lockfileRoot)
3550
+ break;
3551
+ lockfileRoot = dirname6(lockfileRoot);
3552
+ }
3553
+ if (locks.length > 1)
3554
+ fail("conflicting_lockfiles", "Resolve conflicting package-manager lockfiles before setup; Siteplane does not replace them.");
3555
+ if (!locks.length)
3556
+ fail("lockfile_missing", "Install the existing application with its chosen package manager to create one canonical lockfile.");
3557
+ const lockName = locks[0];
3558
+ if (!["package-lock.json", "pnpm-lock.yaml"].includes(lockName))
3559
+ fail("unsupported_package_manager", "This setup supports npm 10 or pnpm 9/10 with their canonical lockfile.");
3560
+ const rootManifest = manifestSchema.parse(JSON.parse(await readFile8(join6(lockfileRoot, "package.json"), "utf8")));
3561
+ if (lockfileRoot !== projectDirectory) {
3562
+ const workspaceFile = join6(lockfileRoot, "pnpm-workspace.yaml");
3563
+ const workspace = lockName === "pnpm-lock.yaml" && await exists(workspaceFile) ? z28.object({ packages: z28.array(z28.string()) }).parse(parseYaml(await readFile8(workspaceFile, "utf8"))).packages : z28.union([
3564
+ z28.array(z28.string()),
3565
+ z28.object({ packages: z28.array(z28.string()) }).transform((value) => value.packages)
3566
+ ]).parse(rootManifest.workspaces);
3567
+ const selected = relative2(lockfileRoot, projectDirectory).split(sep).join("/");
3568
+ if (!workspace.some((pattern) => !pattern.startsWith("!") && minimatch(selected, pattern)) || workspace.some((pattern) => pattern.startsWith("!") && minimatch(selected, pattern.slice(1))))
3569
+ fail("unbound_workspace_app", "The selected application is excluded from the canonical lockfile's workspace packages. Select its actual application root.");
3570
+ }
3571
+ for (let ancestor = dirname6(lockfileRoot); ancestor.startsWith(repositoryRoot + sep) || ancestor === repositoryRoot; ancestor = dirname6(ancestor)) {
3572
+ const ancestorLocks = await Promise.all(lockNames.map((name3) => exists(join6(ancestor, name3))));
3573
+ if (ancestorLocks.some(Boolean))
3574
+ fail("ambiguous_lockfile_root", "Both the selected package and its repository ancestors contain lockfiles. Select one canonical workspace resolution before setup.");
3575
+ if (ancestor === repositoryRoot)
3576
+ break;
3577
+ }
3578
+ const lockText = await readFile8(join6(lockfileRoot, lockName), "utf8");
3579
+ const npmLock = lockName === "package-lock.json" ? z28.object({
3580
+ lockfileVersion: z28.number(),
3581
+ packages: z28.record(z28.string(), z28.object({
3582
+ version: z28.string().optional(),
3583
+ peerDependencies: z28.record(z28.string(), z28.string()).optional()
3584
+ })).optional()
3585
+ }).parse(JSON.parse(lockText)) : null;
3586
+ async function version2(name3) {
3587
+ try {
3588
+ return (await readInstalledPackage(projectDirectory, name3)).version;
3589
+ } catch {
3590
+ const resolved = npmLock?.packages?.[`node_modules/${name3}`]?.version;
3591
+ if (resolved)
3592
+ return resolved;
3593
+ return fail("dependencies_not_installed", `Install the existing ${name3} dependency using the canonical lockfile before setup.`);
3594
+ }
3595
+ }
3596
+ const [nextVersion, reactVersion, reactDomVersion] = await Promise.all([
3597
+ version2("next"),
3598
+ version2("react"),
3599
+ version2("react-dom")
3600
+ ]);
3601
+ if (!stable(nextVersion, 16))
3602
+ fail("unsupported_next_version", "Setup supports stable Next.js 16 App Router projects. Framework migration requires a separate owner decision.");
3603
+ if (!stable(reactVersion, 19) || reactDomVersion !== reactVersion)
3604
+ fail("react_version_conflict", "Use the same stable React 19/react-dom version compatible with the installed Next.js peers.");
3605
+ if (!stable(process.versions.node, 22))
3606
+ fail("unsupported_node_version", "Run setup under Node.js 22; this does not change your framework or package-manager versions.");
3607
+ const nextMetadata = await readInstalledPackage(projectDirectory, "next").catch(() => npmLock?.packages?.["node_modules/next"] ?? null);
3608
+ const peers = nextMetadata?.peerDependencies;
3609
+ if (!peers?.react || !peers["react-dom"])
3610
+ fail("next_peer_metadata_missing", "Install the locked Next.js dependency before setup so its actual React peer requirements can be checked.");
3611
+ if (!semver.satisfies(reactVersion, peers.react) || !semver.satisfies(reactDomVersion, peers["react-dom"]))
3612
+ fail("react_peer_conflict", "The installed React pair does not satisfy the installed Next.js peer requirements. Choose a compatible pair explicitly.");
3613
+ if (manifest.engines?.node && !semver.satisfies(process.versions.node, manifest.engines.node))
3614
+ fail("node_engine_conflict", "The selected application's Node engine declaration excludes the active supported Node.js 22 runtime.");
3615
+ const name2 = lockName === "package-lock.json" ? "npm" : "pnpm";
3616
+ const declared = rootManifest.packageManager ?? manifest.packageManager ?? null;
3617
+ if (rootManifest.packageManager && manifest.packageManager && rootManifest.packageManager !== manifest.packageManager)
3618
+ fail("package_manager_conflict", "Application and workspace packageManager declarations disagree.");
3619
+ if (declared && !new RegExp(`^${name2}@\\d+\\.\\d+\\.\\d+$`, "u").test(declared))
3620
+ fail("package_manager_conflict", "Declare an exact supported npm/pnpm packageManager version matching the canonical lockfile.");
3621
+ const localVersion = (await setupProcess(declared ? "npx" : name2, declared ? ["--yes", declared, "--version"] : ["--version"], { cwd: lockfileRoot })).trim();
3622
+ if (!(name2 === "npm" ? stable(localVersion, 10) : stable(localVersion, 9) || stable(localVersion, 10)))
3623
+ fail("unsupported_package_manager_version", "Use npm 10 or pnpm 9/10; other versions are outside the initial setup support range.");
3624
+ if (declared && declared !== `${name2}@${localVersion}`)
3625
+ fail("package_manager_conflict", "The actual local package-manager version differs from the project's exact packageManager declaration. Select the declared tool without rewriting the lockfile.");
3626
+ const format = npmLock ? String(npmLock.lockfileVersion) : /^lockfileVersion:\s*['"]?([\d.]+)/mu.exec(lockText)?.[1];
3627
+ if (name2 === "npm" && format !== "3" || name2 === "pnpm" && format !== "9.0" && format !== "9")
3628
+ fail("lockfile_format_conflict", "The canonical lockfile format does not match npm 10 or pnpm 9/10.");
3629
+ const sourceConflicts = [];
3630
+ for (const file of ["next.config.js", "next.config.mjs", "next.config.ts"]) {
3631
+ if (!await exists(join6(projectDirectory, file)))
3632
+ continue;
3633
+ const source = await readFile8(join6(projectDirectory, file), "utf8");
3634
+ if (/output\s*:\s*['"]export['"]/u.test(source))
3635
+ sourceConflicts.push("static_export");
3636
+ if (/\bbasePath\s*:/u.test(source))
3637
+ sourceConflicts.push("base_path");
3638
+ }
3639
+ if (sourceConflicts.length)
3640
+ fail("unsupported_server_configuration", "Resolve static export/basePath configuration explicitly; Siteplane requires its server-side admin and revalidation handlers.");
3641
+ for (const name3 of [
3642
+ "middleware.ts",
3643
+ "middleware.js",
3644
+ "proxy.ts",
3645
+ "proxy.js",
3646
+ "src/middleware.ts",
3647
+ "src/middleware.js",
3648
+ "src/proxy.ts",
3649
+ "src/proxy.js"
3650
+ ]) {
3651
+ if (await exists(join6(projectDirectory, name3)))
3652
+ sourceConflicts.push(`inspect_routing:${name3}`);
3653
+ }
3654
+ async function inspectSource(directory) {
3655
+ for (const entry of await readdir(directory, { withFileTypes: true })) {
3656
+ if (entry.isSymbolicLink() || ["node_modules", ".git", ".next", ".siteplane", ".vercel"].includes(entry.name))
3657
+ continue;
3658
+ const path = join6(directory, entry.name);
3659
+ if (entry.isDirectory()) {
3660
+ await inspectSource(path);
3661
+ continue;
3662
+ }
3663
+ if (!/\.[cm]?[jt]sx?$/u.test(entry.name))
3664
+ continue;
3665
+ const source = await readFile8(path, "utf8");
3666
+ if (/from\s*["']next\/image["']/u.test(source))
3667
+ sourceConflicts.push(`inspect_image_policy:${relative2(projectDirectory, path)}`);
3668
+ if (/\b(?:Cookiebot|OneTrust|cookieyes|consentManager|cookieConsent)\b/iu.test(source))
3669
+ sourceConflicts.push(`inspect_existing_consent:${relative2(projectDirectory, path)}`);
3670
+ if (/from\s*["']next\/image["']/u.test(source) && semver.lt(nextVersion, "16.3.3"))
3671
+ fail("next_security_advisory", `Next.js ${nextVersion} uses image optimization affected by the August 2026 AVIF advisory. Review https://nextjs.org/blog/august-2026-security-release and explicitly update the affected dependency before setup; no framework upgrade was performed.`);
3672
+ if (/["']use server["']/u.test(source) && semver.lt(nextVersion, "16.2.11"))
3673
+ fail("next_security_advisory", `Next.js ${nextVersion} contains Server Actions affected by the July 2026 advisory. Review https://nextjs.org/blog/july-2026-security-release and explicitly update the affected dependency before setup.`);
3674
+ }
3675
+ }
3676
+ await inspectSource(join6(projectDirectory, apps[0]));
3677
+ const gitRead = async (args) => setupProcess("git", args, { cwd: projectDirectory }).then((value) => value.trim(), () => null);
3678
+ return {
3679
+ projectDirectory,
3680
+ repositoryRoot,
3681
+ lockfileRoot,
3682
+ appDirectory: apps[0],
3683
+ nextVersion,
3684
+ reactVersion,
3685
+ nodeVersion: process.versions.node,
3686
+ packageManager: { name: name2, version: localVersion, declared },
3687
+ lockfile: {
3688
+ path: relative2(repositoryRoot, join6(lockfileRoot, lockName)),
3689
+ format,
3690
+ sha256: createHash2("sha256").update(lockText).digest("hex")
3691
+ },
3692
+ git: {
3693
+ branch: await gitRead(["branch", "--show-current"]),
3694
+ revision: await gitRead(["rev-parse", "HEAD"]),
3695
+ remote: safeGitRemote(await gitRead(["remote", "get-url", "origin"])),
3696
+ changes: (await gitRead(["status", "--porcelain=v1", "--untracked-files=all"]))?.split("\n").filter(Boolean) ?? []
3697
+ },
3698
+ sourceConflicts
3699
+ };
3700
+ }
3701
+ async function setupPreflightCommand(input) {
3702
+ let local;
3703
+ try {
3704
+ local = await inspectSetupLocalProject(input.projectDir);
3705
+ await assertLocalCredentialsPathSafe(input.projectDir);
3706
+ let linkDirectory = local.projectDirectory;
3707
+ while (!await exists(join6(linkDirectory, ".vercel/project.json")) && linkDirectory !== local.repositoryRoot && dirname6(linkDirectory) !== linkDirectory)
3708
+ linkDirectory = dirname6(linkDirectory);
3709
+ if (!await exists(join6(linkDirectory, ".vercel/project.json")))
3710
+ fail("vercel_project_not_linked", "Link this application to its intended Vercel project before setup; Siteplane will not guess a target.");
3711
+ const linked = z28.object({
3712
+ projectId: z28.string().regex(/^prj_[A-Za-z0-9]+$/u),
3713
+ orgId: z28.string().regex(/^(?:team|user)_[A-Za-z0-9]+$/u)
3714
+ }).parse(JSON.parse(await readFile8(join6(linkDirectory, ".vercel/project.json"), "utf8")));
3715
+ const project = vercelProjectSchema.parse(await setupVercelApi(linkDirectory, `/v9/projects/${linked.projectId}?teamId=${linked.orgId}`));
3716
+ if (project.id !== linked.projectId || project.accountId !== linked.orgId)
3717
+ fail("vercel_identity_conflict", "The provider project does not match the locally linked project and team.");
3718
+ const expectedRoot = relative2(local.repositoryRoot, local.projectDirectory).split(sep).join("/");
3719
+ if ((project.rootDirectory ?? "") !== expectedRoot)
3720
+ fail("vercel_root_conflict", "Vercel Root Directory and the selected local app must identify the same application.");
3721
+ if (project.nodeVersion !== "22.x" || project.framework !== "nextjs")
3722
+ fail("vercel_framework_conflict", "The bound Vercel project must use Next.js with Node.js 22 before setup.");
3723
+ if (project.link && project.link.productionBranch !== local.git.branch)
3724
+ fail("vercel_production_branch_conflict", "The local branch differs from the bound Vercel Production branch.");
3725
+ if (project.link?.repo && project.link.org && !local.git.remote?.replace(/\.git$/u, "").endsWith(`${project.link.org}/${project.link.repo}`))
3726
+ fail("vercel_git_conflict", "The local origin repository differs from the Vercel Git connection.");
3727
+ const env = z28.object({
3728
+ envs: z28.array(z28.object({
3729
+ key: z28.string(),
3730
+ value: z28.string().optional(),
3731
+ target: z28.array(z28.string()).optional()
3732
+ }))
3733
+ }).parse(await setupVercelApi(linkDirectory, `/v10/projects/${linked.projectId}/env?teamId=${linked.orgId}`));
3734
+ const corepack = env.envs.some((item) => item.key === "ENABLE_EXPERIMENTAL_COREPACK" && item.value === "1" && item.target?.includes("production"));
3735
+ const install = project.installCommand ?? null;
3736
+ const selected = corepack ? "corepack" : install ? "install_override" : "automatic";
3737
+ if (local.packageManager.declared && selected === "automatic")
3738
+ fail("vercel_package_manager_pin_unconfirmed", "This project declares an exact packageManager but Vercel automatic selection does not bind that version. Enable its supported Corepack selection or set an exact install AND build override in the existing project, then rerun preflight. No package, lockfile or provider setting was changed.");
3739
+ if (install && /\b(?:yarn|bun)\b/u.test(install))
3740
+ fail("vercel_package_manager_conflict", "The Vercel install override selects an unsupported package manager.");
3741
+ const overridePin = install ? /\b(npm|pnpm)@(\d+\.\d+\.\d+)\b/u.exec(install) : null;
3742
+ if (overridePin && overridePin[1] !== local.packageManager.name)
3743
+ fail("vercel_package_manager_conflict", "The provider override selects a different package manager than the canonical lockfile.");
3744
+ const overrideFamily = install ? /(?:^|[;&|]\s*)(npm|pnpm)\s+(?:ci|install)\b/u.exec(install)?.[1] : null;
3745
+ if (overrideFamily && overrideFamily !== local.packageManager.name)
3746
+ fail("vercel_package_manager_conflict", "The provider install command conflicts with the canonical lockfile.");
3747
+ if (overridePin && local.packageManager.declared && `${overridePin[1]}@${overridePin[2]}` !== local.packageManager.declared)
3748
+ fail("vercel_package_manager_conflict", "The Vercel install override conflicts with the exact packageManager declaration.");
3749
+ return {
3750
+ status: "ready",
3751
+ local,
3752
+ provider: {
3753
+ projectId: project.id,
3754
+ teamId: project.accountId,
3755
+ name: project.name,
3756
+ rootDirectory: expectedRoot,
3757
+ nodeVersion: project.nodeVersion,
3758
+ git: project.link,
3759
+ installCommand: install,
3760
+ buildCommand: project.buildCommand ?? null,
3761
+ packageManagerSelection: selected,
3762
+ declaredPackageManager: local.packageManager.declared,
3763
+ observedPackageManager: null
3764
+ }
3765
+ };
3766
+ } catch (error) {
3767
+ return {
3768
+ status: "action_required",
3769
+ code: error instanceof SetupFailure ? error.code : "invalid_local_project",
3770
+ message: error instanceof SetupFailure ? error.message : "The selected project or provider response could not be validated. Check the application manifest and local project access.",
3771
+ ...local ? { local } : {}
3772
+ };
3773
+ }
3774
+ }
3775
+ function safeGitRemote(value) {
3776
+ if (!value)
3777
+ return null;
3778
+ if (!value.includes("://"))
3779
+ return value;
3780
+ try {
3781
+ const url = new URL(value);
3782
+ url.username = "";
3783
+ url.password = "";
3784
+ url.search = "";
3785
+ url.hash = "";
3786
+ return url.toString().replace(/\/$/u, "");
3787
+ } catch {
3788
+ return null;
3789
+ }
3790
+ }
3791
+
3792
+ // ../cli/dist/commands/init.js
3122
3793
  async function initCommand(input) {
3123
3794
  if (!input.setupToken.trim()) {
3124
3795
  throw new Error("A Siteplane setup token is required.");
@@ -3126,47 +3797,64 @@ async function initCommand(input) {
3126
3797
  if (!await detectNextProject(input.projectDir)) {
3127
3798
  throw new Error("Siteplane init currently supports Next.js projects.");
3128
3799
  }
3800
+ await assertLocalCredentialsPathSafe(input.projectDir, {
3801
+ ...input.isIgnoredFile ? { isIgnoredFile: input.isIgnoredFile } : {},
3802
+ ...input.isTrackedFile ? { isTrackedFile: input.isTrackedFile } : {}
3803
+ });
3804
+ const preflight = await (input.preflight ?? setupPreflightCommand)({
3805
+ projectDir: input.projectDir
3806
+ });
3807
+ if (preflight.status !== "ready")
3808
+ throw new Error(`${preflight.code}: ${preflight.message}`);
3129
3809
  const bootstrap = await bootstrapProjectConnection(input);
3130
- const paths = bootstrap.credentialPurpose === "site_setup" ? await persistSiteSetupBootstrap(input, bootstrap) : await persistFeatureBootstrap(input, bootstrap);
3810
+ const paths = bootstrap.credentialPurpose === "site_setup" ? await persistSiteSetupBootstrap(input, bootstrap, relative3(preflight.local.lockfileRoot, preflight.local.projectDirectory) || ".") : await persistFeatureBootstrap(input, bootstrap);
3131
3811
  await acknowledgeBootstrapResponse(input);
3132
3812
  return {
3133
3813
  ...paths,
3134
3814
  nextSteps: [
3135
3815
  "Run npx siteplane setup context and read the returned task.",
3136
3816
  "Before source changes, briefly tell the user what Siteplane requested and what you will do.",
3137
- "Follow the returned steps through setup verify and stop only at ready_for_review or action_required."
3817
+ "Follow the returned steps through setup prepare and setup deploy --wait, which performs setup verify; stop only at ready_for_review or action_required."
3138
3818
  ]
3139
3819
  };
3140
3820
  }
3141
- var siteSetupBootstrapDataSchema = z26.object({
3142
- credentialPurpose: z26.literal("site_setup"),
3143
- apiBaseUrl: z26.string().url(),
3144
- siteId: z26.string().uuid(),
3145
- publicSiteKeyId: z26.string().uuid(),
3146
- publicSiteKey: z26.string().trim().min(1),
3147
- accessToken: z26.string().trim().min(1),
3148
- siteRevalidationSecret: z26.string().regex(/^[A-Za-z0-9_-]{43}$/u)
3821
+ var siteSetupBootstrapDataSchema = z29.object({
3822
+ credentialPurpose: z29.literal("site_setup"),
3823
+ apiBaseUrl: z29.string().url(),
3824
+ siteId: z29.string().uuid(),
3825
+ publicSiteKeyId: z29.string().uuid(),
3826
+ publicSiteKey: z29.string().trim().min(1),
3827
+ accessToken: z29.string().trim().min(1),
3828
+ siteRevalidationSecret: z29.string().regex(/^[A-Za-z0-9_-]{43}$/u),
3829
+ runId: z29.string().uuid(),
3830
+ packages: z29.object({
3831
+ siteplane: z29.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u),
3832
+ runtimeNext: z29.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u)
3833
+ }).strict()
3149
3834
  }).strict();
3150
- var featureBootstrapDataSchema = z26.object({
3151
- credentialPurpose: z26.enum(["analytics", "booking"]),
3152
- apiBaseUrl: z26.string().url(),
3153
- siteId: z26.string().uuid(),
3154
- accessToken: z26.string().trim().min(1)
3835
+ var featureBootstrapDataSchema = z29.object({
3836
+ credentialPurpose: z29.enum(["analytics", "booking"]),
3837
+ apiBaseUrl: z29.string().url(),
3838
+ siteId: z29.string().uuid(),
3839
+ accessToken: z29.string().trim().min(1)
3155
3840
  }).strict();
3156
- var bootstrapResponseSchema = z26.object({
3157
- ok: z26.literal(true),
3158
- data: z26.discriminatedUnion("credentialPurpose", [
3841
+ var bootstrapResponseSchema = z29.object({
3842
+ ok: z29.literal(true),
3843
+ data: z29.discriminatedUnion("credentialPurpose", [
3159
3844
  siteSetupBootstrapDataSchema,
3160
3845
  featureBootstrapDataSchema
3161
3846
  ])
3162
3847
  }).strict();
3163
3848
  async function bootstrapProjectConnection(input) {
3164
3849
  const fetcher = input.fetcher ?? fetch;
3850
+ const existing = await access3(join7(input.projectDir, "siteplane.config.json")).then(() => readProjectConfig(input.projectDir), () => null);
3165
3851
  const response = await fetcher(`${input.apiBaseUrl.replace(/\/$/, "")}/api/cli/setup/bootstrap`, {
3166
3852
  method: "POST",
3853
+ signal: AbortSignal.timeout(2e4),
3167
3854
  headers: createBootstrapHeaders(input),
3168
3855
  body: JSON.stringify({
3169
3856
  action: "bootstrap",
3857
+ ...existing ? { expectedSiteId: existing.siteId, expectedRunId: existing.runId } : {},
3170
3858
  agentClient: input.agentClient ?? "codex"
3171
3859
  })
3172
3860
  });
@@ -3181,6 +3869,7 @@ async function acknowledgeBootstrapResponse(input) {
3181
3869
  const fetcher = input.fetcher ?? fetch;
3182
3870
  const response = await fetcher(`${input.apiBaseUrl.replace(/\/$/, "")}/api/cli/setup/bootstrap`, {
3183
3871
  method: "POST",
3872
+ signal: AbortSignal.timeout(2e4),
3184
3873
  headers: createBootstrapHeaders(input),
3185
3874
  body: JSON.stringify({ action: "acknowledge" })
3186
3875
  });
@@ -3188,8 +3877,13 @@ async function acknowledgeBootstrapResponse(input) {
3188
3877
  throw new Error("Siteplane setup acknowledgement failed.");
3189
3878
  }
3190
3879
  }
3191
- async function persistSiteSetupBootstrap(input, bootstrap) {
3880
+ async function persistSiteSetupBootstrap(input, bootstrap, appDirectory) {
3881
+ const previous = await access3(join7(input.projectDir, "siteplane.config.json")).then(() => readProjectConfig(input.projectDir), () => null);
3882
+ if (previous && (previous.siteId !== bootstrap.siteId || previous.runId !== bootstrap.runId || previous.publicSiteKeyId !== bootstrap.publicSiteKeyId || previous.publicSiteKey !== bootstrap.publicSiteKey))
3883
+ throw new Error("The grant does not match this local Siteplane run. Preserve this directory and choose the correct owner connection.");
3884
+ const previousCredentials = previous ? await access3(join7(input.projectDir, ".siteplane/credentials.json")).then(() => readLocalCredentials(input.projectDir), () => null) : null;
3192
3885
  const credentialsPath = await writeLocalCredentials(input.projectDir, {
3886
+ ...previousCredentials?.adminSession ? { adminSession: previousCredentials.adminSession } : {},
3193
3887
  accessToken: bootstrap.accessToken,
3194
3888
  siteRevalidationSecret: bootstrap.siteRevalidationSecret
3195
3889
  }, {
@@ -3197,7 +3891,13 @@ async function persistSiteSetupBootstrap(input, bootstrap) {
3197
3891
  ...input.isTrackedFile ? { isTrackedFile: input.isTrackedFile } : {}
3198
3892
  });
3199
3893
  const configPath = await writeProjectConfig(input.projectDir, {
3200
- version: 1,
3894
+ ...previous ?? {},
3895
+ version: 2,
3896
+ appDirectory,
3897
+ runId: bootstrap.runId,
3898
+ packages: bootstrap.packages,
3899
+ taskContractVersion: "siteplane.setup-task.v2",
3900
+ adminProtocolVersion: 2,
3201
3901
  apiBaseUrl: bootstrap.apiBaseUrl,
3202
3902
  siteId: bootstrap.siteId,
3203
3903
  publicSiteKeyId: bootstrap.publicSiteKeyId,
@@ -3207,7 +3907,7 @@ async function persistSiteSetupBootstrap(input, bootstrap) {
3207
3907
  readLocalCredentials(input.projectDir),
3208
3908
  readProjectConfig(input.projectDir)
3209
3909
  ]);
3210
- if (credentials.accessToken !== bootstrap.accessToken || credentials.siteRevalidationSecret !== bootstrap.siteRevalidationSecret || config.apiBaseUrl !== bootstrap.apiBaseUrl || config.siteId !== bootstrap.siteId || config.publicSiteKeyId !== bootstrap.publicSiteKeyId || config.publicSiteKey !== bootstrap.publicSiteKey) {
3910
+ if (credentials.accessToken !== bootstrap.accessToken || credentials.siteRevalidationSecret !== bootstrap.siteRevalidationSecret || config.runId !== bootstrap.runId || config.packages.siteplane !== bootstrap.packages.siteplane || config.packages.runtimeNext !== bootstrap.packages.runtimeNext || config.apiBaseUrl !== bootstrap.apiBaseUrl || config.siteId !== bootstrap.siteId || config.publicSiteKeyId !== bootstrap.publicSiteKeyId || config.publicSiteKey !== bootstrap.publicSiteKey) {
3211
3911
  throw new Error("Siteplane setup files could not be verified.");
3212
3912
  }
3213
3913
  return { configPath, credentialsPath };
@@ -3221,8 +3921,8 @@ async function persistFeatureBootstrap(input, bootstrap) {
3221
3921
  throw new Error("The setup token belongs to a different Siteplane site.");
3222
3922
  }
3223
3923
  const credentialsPath = await writeLocalCredentials(input.projectDir, {
3224
- accessToken: bootstrap.accessToken,
3225
- siteRevalidationSecret: existingCredentials.siteRevalidationSecret
3924
+ ...existingCredentials,
3925
+ accessToken: bootstrap.accessToken
3226
3926
  }, {
3227
3927
  ...input.isIgnoredFile ? { isIgnoredFile: input.isIgnoredFile } : {},
3228
3928
  ...input.isTrackedFile ? { isTrackedFile: input.isTrackedFile } : {}
@@ -3232,7 +3932,7 @@ async function persistFeatureBootstrap(input, bootstrap) {
3232
3932
  throw new Error("Siteplane credentials could not be verified.");
3233
3933
  }
3234
3934
  return {
3235
- configPath: join5(input.projectDir, "siteplane.config.json"),
3935
+ configPath: join7(input.projectDir, "siteplane.config.json"),
3236
3936
  credentialsPath
3237
3937
  };
3238
3938
  }
@@ -3254,8 +3954,8 @@ function readErrorMessage(payload) {
3254
3954
  }
3255
3955
 
3256
3956
  // ../cli/dist/commands/scan.js
3257
- import { readdir } from "fs/promises";
3258
- import { extname, join as join6 } from "path";
3957
+ import { readdir as readdir2 } from "fs/promises";
3958
+ import { extname, join as join8 } from "path";
3259
3959
  var defaultScanRoots = ["app", "src", "pages", "components", "lib"];
3260
3960
  var excludedDirectories = /* @__PURE__ */ new Set([
3261
3961
  ".git",
@@ -3273,14 +3973,14 @@ async function resolveScanFilePaths(filePaths, options = {}) {
3273
3973
  return filePaths;
3274
3974
  }
3275
3975
  const projectDir = options.projectDir ?? process.cwd();
3276
- const discovered = await Promise.all(defaultScanRoots.map((root) => collectSourceFiles(join6(projectDir, root))));
3976
+ const discovered = await Promise.all(defaultScanRoots.map((root) => collectSourceFiles(join8(projectDir, root))));
3277
3977
  return [...new Set(discovered.flat())].sort();
3278
3978
  }
3279
3979
  async function collectSourceFiles(directory) {
3280
- const entries = await readdir(directory, { withFileTypes: true }).catch(() => []);
3980
+ const entries = await readdir2(directory, { withFileTypes: true }).catch(() => []);
3281
3981
  const files = [];
3282
3982
  for (const entry of entries) {
3283
- const entryPath = join6(directory, entry.name);
3983
+ const entryPath = join8(directory, entry.name);
3284
3984
  if (entry.isDirectory()) {
3285
3985
  if (!excludedDirectories.has(entry.name)) {
3286
3986
  files.push(...await collectSourceFiles(entryPath));
@@ -3296,17 +3996,17 @@ async function collectSourceFiles(directory) {
3296
3996
 
3297
3997
  // ../cli/dist/commands/site-setup.js
3298
3998
  import { execFile as execFile2 } from "child_process";
3299
- import { readFile as readFile7 } from "fs/promises";
3999
+ import { readFile as readFile9 } from "fs/promises";
3300
4000
  import { promisify as promisify2 } from "util";
3301
- import { z as z27 } from "zod";
4001
+ import { z as z30 } from "zod";
3302
4002
 
3303
4003
  // ../cli/dist/scan/next-source-scan.js
3304
4004
  import ts2 from "typescript";
3305
- import { isAbsolute, relative as relative2 } from "path";
4005
+ import { isAbsolute, relative as relative4 } from "path";
3306
4006
  var eventPreviewHandlerMissingMessage = "Event preview fields must consume preview values with useSiteplanePreviewText from @siteplane/runtime-next/client or a siteplane:preview-value-applied listener.";
3307
4007
  var valueCallUnusedMessage = "Siteplane value calls must feed rendered content. Assign or await the returned value and render that value instead of ignoring the call.";
3308
4008
  var legacyEditableComponentPrefix = "Editable";
3309
- var editableComponentNames = new Set(["Image", "Text", "LongText", "Link"].map((name) => `${legacyEditableComponentPrefix}${name}`));
4009
+ var editableComponentNames = new Set(["Image", "Text", "LongText", "Link"].map((name2) => `${legacyEditableComponentPrefix}${name2}`));
3310
4010
  var functionTypeByName = {
3311
4011
  text: "text",
3312
4012
  longText: "longText",
@@ -3844,17 +4544,17 @@ function readObjectLiteral(expression) {
3844
4544
  if (!ts2.isPropertyAssignment(property)) {
3845
4545
  continue;
3846
4546
  }
3847
- const name = getPropertyName(property.name);
3848
- if (!name) {
4547
+ const name2 = getPropertyName(property.name);
4548
+ if (!name2) {
3849
4549
  continue;
3850
4550
  }
3851
- value[name] = staticExpressionValue2(property.initializer);
4551
+ value[name2] = staticExpressionValue2(property.initializer);
3852
4552
  }
3853
4553
  return value;
3854
4554
  }
3855
- function getPropertyName(name) {
3856
- if (ts2.isIdentifier(name) || ts2.isStringLiteral(name)) {
3857
- return name.text;
4555
+ function getPropertyName(name2) {
4556
+ if (ts2.isIdentifier(name2) || ts2.isStringLiteral(name2)) {
4557
+ return name2.text;
3858
4558
  }
3859
4559
  return null;
3860
4560
  }
@@ -3880,9 +4580,9 @@ function collectEventPreviewComponentPropUsages(node, eventPreviewAttrVariables,
3880
4580
  });
3881
4581
  }
3882
4582
  }
3883
- function getJsxAttributeName(name) {
3884
- if (ts2.isIdentifier(name)) {
3885
- return name.text;
4583
+ function getJsxAttributeName(name2) {
4584
+ if (ts2.isIdentifier(name2)) {
4585
+ return name2.text;
3886
4586
  }
3887
4587
  return void 0;
3888
4588
  }
@@ -3907,8 +4607,8 @@ function getFunctionComponentName(node) {
3907
4607
  }
3908
4608
  return void 0;
3909
4609
  }
3910
- function isComponentName(name) {
3911
- return /^[A-Z]/.test(name);
4610
+ function isComponentName(name2) {
4611
+ return /^[A-Z]/.test(name2);
3912
4612
  }
3913
4613
  function getAssignedVariableName(node) {
3914
4614
  let current = node;
@@ -3956,7 +4656,7 @@ function inferRouteHint(filePath) {
3956
4656
  return route ? `/${route}` : "/";
3957
4657
  }
3958
4658
  function toRepositoryRelativePosixPath(filePath, projectRoot) {
3959
- const path = isAbsolute(filePath) ? relative2(projectRoot, filePath) : filePath;
4659
+ const path = isAbsolute(filePath) ? relative4(projectRoot, filePath) : filePath;
3960
4660
  return path.replaceAll("\\", "/");
3961
4661
  }
3962
4662
  function isPositionalFieldReference(value) {
@@ -3965,54 +4665,30 @@ function isPositionalFieldReference(value) {
3965
4665
 
3966
4666
  // ../cli/dist/commands/site-setup.js
3967
4667
  var execFileAsync2 = promisify2(execFile2);
3968
- var safeErrorSchema = z27.object({
3969
- code: z27.string().trim().min(1),
3970
- message: z27.string().trim().min(1)
4668
+ var safeErrorSchema = z30.object({
4669
+ code: z30.string().trim().min(1),
4670
+ message: z30.string().trim().min(1)
3971
4671
  }).passthrough();
3972
- var errorResponseSchema2 = z27.object({ ok: z27.literal(false), error: safeErrorSchema }).passthrough();
3973
- var contextDataSchema = z27.object({
3974
- task: siteSetupTaskSchema,
3975
- site: z27.object({ siteId: z27.string().uuid(), portalPath: z27.string() }),
3976
- run: z27.object({
3977
- runId: z27.string().uuid(),
3978
- mode: z27.enum(["initial", "update"]),
3979
- status: z27.enum([
3980
- "waiting_for_agent",
3981
- "working",
3982
- "action_required",
3983
- "ready_for_review"
3984
- ]),
3985
- fieldContractHash: z27.string().nullable(),
3986
- deploymentOrigin: z27.string().nullable(),
3987
- deploymentRevision: z27.string().nullable(),
3988
- errorCode: z27.string().nullable(),
3989
- errorMessage: z27.string().nullable()
3990
- }),
3991
- structureHandoff: z27.object({
3992
- pendingFields: z27.array(setupCorrectionItemSchema).max(50),
3993
- ownerEditabilityNote: siteSetupOwnerEditabilityNoteSchema
3994
- }).strict().nullable(),
3995
- allowedTasks: z27.array(z27.string()),
3996
- requiredEnvironmentNames: z27.array(z27.string())
3997
- }).strict();
3998
- var contextResponseSchema = z27.object({ ok: z27.literal(true), data: contextDataSchema }).strict();
3999
- var applyDataSchema = z27.object({
4000
- status: z27.enum(["applied", "ready_for_review"]),
4001
- runId: z27.string().uuid().optional(),
4002
- contractVersionId: z27.string().uuid().optional(),
4003
- fieldContractHash: z27.string().regex(/^sha256:[0-9a-f]{64}$/u),
4004
- editorDefinitionHash: z27.string().regex(/^sha256:[0-9a-f]{64}$/u).optional(),
4005
- fieldCount: z27.number().int().nonnegative().optional(),
4006
- routeCount: z27.number().int().nonnegative().optional()
4672
+ var errorResponseSchema2 = z30.object({ ok: z30.literal(false), error: safeErrorSchema }).passthrough();
4673
+ var contextDataSchema = z30.object({ task: siteSetupTaskSchema }).strict();
4674
+ var contextResponseSchema = z30.object({ ok: z30.literal(true), data: contextDataSchema }).strict();
4675
+ var applyDataSchema = z30.object({
4676
+ status: z30.enum(["applied", "ready_for_review"]),
4677
+ runId: z30.string().uuid().optional(),
4678
+ contractVersionId: z30.string().uuid().optional(),
4679
+ fieldContractHash: z30.string().regex(/^sha256:[0-9a-f]{64}$/u),
4680
+ editorDefinitionHash: z30.string().regex(/^sha256:[0-9a-f]{64}$/u).optional(),
4681
+ fieldCount: z30.number().int().nonnegative().optional(),
4682
+ routeCount: z30.number().int().nonnegative().optional()
4007
4683
  }).strict();
4008
- var applyResponseSchema = z27.object({ ok: z27.literal(true), data: applyDataSchema }).strict();
4009
- var verifyDataSchema = z27.object({
4010
- status: z27.literal("ready_for_review"),
4011
- runId: z27.string().uuid().optional(),
4012
- deploymentOrigin: z27.string().url().optional(),
4013
- deploymentRevision: z27.string().optional()
4684
+ var applyResponseSchema = z30.object({ ok: z30.literal(true), data: applyDataSchema }).strict();
4685
+ var verifyDataSchema = z30.object({
4686
+ status: z30.literal("ready_for_review"),
4687
+ runId: z30.string().uuid().optional(),
4688
+ deploymentOrigin: z30.string().url().optional(),
4689
+ deploymentRevision: z30.string().optional()
4014
4690
  }).strict();
4015
- var verifyResponseSchema = z27.object({ ok: z27.literal(true), data: verifyDataSchema }).strict();
4691
+ var verifyResponseSchema = z30.object({ ok: z30.literal(true), data: verifyDataSchema }).strict();
4016
4692
  async function siteSetupContextCommand(input) {
4017
4693
  const response = await postSiteSetup(input, "context", {});
4018
4694
  const parsed = contextResponseSchema.safeParse(response.payload);
@@ -4022,9 +4698,19 @@ async function siteSetupContextCommand(input) {
4022
4698
  return actionRequired(response.payload, "Siteplane setup context is unavailable.");
4023
4699
  }
4024
4700
  async function siteSetupApplyCommand(input) {
4701
+ const config = await readProjectConfig(input.projectDir);
4702
+ try {
4703
+ await assertExactSetupPackages(input.projectDir, config.packages);
4704
+ } catch {
4705
+ return {
4706
+ status: "action_required",
4707
+ code: "siteplane_package_pin_conflict",
4708
+ message: "Run setup prepare to install the exact Siteplane packages from the current task before applying fields."
4709
+ };
4710
+ }
4025
4711
  const editorDefinition = editorDefinitionV1Schema.parse(input.editorDefinition);
4026
4712
  const resolveSourceFiles = input.resolveSourceFiles ?? resolveScanFilePaths;
4027
- const readSourceFile = input.readSourceFile ?? ((path) => readFile7(path, "utf8"));
4713
+ const readSourceFile = input.readSourceFile ?? ((path) => readFile9(path, "utf8"));
4028
4714
  const scanSourceFiles = input.scanSourceFiles ?? scanNextSourceFilesToSiteFieldContract;
4029
4715
  const paths = await resolveSourceFiles([], { projectDir: input.projectDir });
4030
4716
  const files = await Promise.all(paths.map(async (filePath) => ({
@@ -4041,7 +4727,8 @@ async function siteSetupApplyCommand(input) {
4041
4727
  }
4042
4728
  const payload = siteSetupApplyInputSchema.parse({
4043
4729
  fieldContract: scan.contract,
4044
- editorDefinition
4730
+ editorDefinition,
4731
+ contentSelectionSummary: input.contentSelectionSummary
4045
4732
  });
4046
4733
  const response = await postSiteSetup(input, "apply", payload);
4047
4734
  const parsed = applyResponseSchema.safeParse(response.payload);
@@ -4056,7 +4743,6 @@ async function siteSetupApplyCommand(input) {
4056
4743
  message: "The applied field contract hash does not match the final local source scan."
4057
4744
  };
4058
4745
  }
4059
- const config = await readProjectConfig(input.projectDir);
4060
4746
  await writeProjectConfig(input.projectDir, {
4061
4747
  ...config,
4062
4748
  fieldContractHash: parsed.data.data.fieldContractHash
@@ -4065,7 +4751,7 @@ async function siteSetupApplyCommand(input) {
4065
4751
  }
4066
4752
  async function siteSetupVerifyCommand(input) {
4067
4753
  const resolveSourceFiles = input.resolveSourceFiles ?? resolveScanFilePaths;
4068
- const readSourceFile = input.readSourceFile ?? ((path) => readFile7(path, "utf8"));
4754
+ const readSourceFile = input.readSourceFile ?? ((path) => readFile9(path, "utf8"));
4069
4755
  const paths = await resolveSourceFiles([], { projectDir: input.projectDir });
4070
4756
  const sources = await Promise.all(paths.map(readSourceFile));
4071
4757
  if (!sources.some(hasMountedFieldRuntimeBridge)) {
@@ -4095,19 +4781,33 @@ async function postSiteSetup(input, operation, body) {
4095
4781
  readProjectConfig(input.projectDir),
4096
4782
  readLocalCredentials(input.projectDir)
4097
4783
  ]);
4098
- const response = await (input.fetcher ?? fetch)(`${config.apiBaseUrl.replace(/\/$/u, "")}/api/cli/setup/${operation}`, {
4099
- method: "POST",
4100
- headers: {
4101
- authorization: `Bearer ${credentials.accessToken}`,
4102
- "content-type": "application/json",
4103
- ...input.vercelAutomationBypassSecret?.trim() ? {
4104
- "x-vercel-protection-bypass": input.vercelAutomationBypassSecret.trim()
4105
- } : {}
4106
- },
4107
- body: JSON.stringify(body)
4108
- });
4109
- const payload = await response.json().catch(() => null);
4110
- return { ok: response.ok, payload };
4784
+ try {
4785
+ const response = await (input.fetcher ?? fetch)(`${config.apiBaseUrl.replace(/\/$/u, "")}/api/cli/setup/${operation}`, {
4786
+ method: "POST",
4787
+ signal: AbortSignal.timeout(Math.max(1, Math.min(2e4, (input.deadline ?? Date.now() + 2e4) - Date.now()))),
4788
+ headers: {
4789
+ authorization: `Bearer ${credentials.accessToken}`,
4790
+ "content-type": "application/json",
4791
+ ...input.vercelAutomationBypassSecret?.trim() ? {
4792
+ "x-vercel-protection-bypass": input.vercelAutomationBypassSecret.trim()
4793
+ } : {}
4794
+ },
4795
+ body: JSON.stringify(body)
4796
+ });
4797
+ const payload = await response.json().catch(() => null);
4798
+ return { ok: response.ok, payload };
4799
+ } catch {
4800
+ return {
4801
+ ok: false,
4802
+ payload: {
4803
+ ok: false,
4804
+ error: {
4805
+ code: "site_setup.transport_failed",
4806
+ message: "The setup request was not acknowledged. Reload the existing task before retrying; no credential values are included."
4807
+ }
4808
+ }
4809
+ };
4810
+ }
4111
4811
  }
4112
4812
  function actionRequired(payload, fallbackMessage) {
4113
4813
  const parsed = errorResponseSchema2.safeParse(payload);
@@ -4132,9 +4832,880 @@ async function readGitRevision(projectDir) {
4132
4832
  }
4133
4833
  return revision;
4134
4834
  }
4835
+ async function siteSetupActivityCommand(input) {
4836
+ const activity = siteSetupActivityInputSchema.safeParse({
4837
+ phase: input.phase,
4838
+ textId: input.phase
4839
+ });
4840
+ if (!activity.success)
4841
+ return {
4842
+ status: "action_required",
4843
+ code: "site_setup.invalid_activity",
4844
+ message: "Choose inspecting, integrating, deploying, verifying or repairing."
4845
+ };
4846
+ const response = await postSiteSetup(input, "activity", activity.data);
4847
+ const parsed = z30.object({
4848
+ ok: z30.literal(true),
4849
+ data: z30.object({ status: z30.enum(["recorded", "rate_limited"]) }).strict()
4850
+ }).strict().safeParse(response.payload);
4851
+ return response.ok && parsed.success ? parsed.data.data : actionRequired(response.payload, "Setup activity could not be recorded.");
4852
+ }
4853
+
4854
+ // ../cli/dist/commands/setup-prepare.js
4855
+ import { access as access4, lstat as lstat2, mkdir as mkdir6, open as open3, readFile as readFile10, rename as rename3, rm as rm3 } from "fs/promises";
4856
+ import { randomUUID as randomUUID4 } from "crypto";
4857
+ import { dirname as dirname7, join as join9, relative as relative5, sep as sep2 } from "path";
4858
+ import ts3 from "typescript";
4859
+ var exists2 = async (path) => access4(path).then(() => true, () => false);
4860
+ var importPath = (from, target) => {
4861
+ const path = relative5(dirname7(from), target).split(sep2).join("/");
4862
+ return path.startsWith(".") ? path : `./${path}`;
4863
+ };
4864
+ async function setupPrepareCommand(input) {
4865
+ try {
4866
+ const preflight = await setupPreflightCommand(input);
4867
+ if (preflight.status !== "ready")
4868
+ return preflight;
4869
+ const config = await readProjectConfig(input.projectDir);
4870
+ const context = await siteSetupContextCommand(input);
4871
+ if (context.status !== "ok")
4872
+ return context;
4873
+ const task = context.task;
4874
+ if (task.site?.siteId !== config.siteId || task.run?.runId !== config.runId || task.packages.siteplane !== config.packages.siteplane || task.packages.runtimeNext !== config.packages.runtimeNext) {
4875
+ throw new SetupFailure("setup_contract_conflict", "The local configuration does not match this server task and its exact package versions.");
4876
+ }
4877
+ if (task.run.status === "completed" || task.run.status === "ready_for_review")
4878
+ return {
4879
+ status: "unchanged",
4880
+ message: "The verified setup is available for owner review; no preparation writes are needed."
4881
+ };
4882
+ const directory = preflight.local.projectDirectory;
4883
+ const app = join9(directory, preflight.local.appDirectory);
4884
+ const layouts = (await Promise.all(["layout.tsx", "layout.jsx", "layout.js"].map(async (name2) => await exists2(join9(app, name2)) ? join9(app, name2) : null))).filter((path) => path !== null);
4885
+ if (layouts.length !== 1)
4886
+ throw new SetupFailure("layout_integration_conflict", "Select one root layout and mount the Siteplane preview bridge there; preserve existing providers and rendering.");
4887
+ const layoutPath = layouts[0];
4888
+ const typed = layoutPath.endsWith(".tsx");
4889
+ const routeExtension = typed ? "ts" : "js";
4890
+ const bridgePath = join9(app, "_siteplane", `bridge.${typed ? "tsx" : "jsx"}`);
4891
+ const portal = task.site.portalPath;
4892
+ if (!/^\/[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/u.test(portal))
4893
+ throw new SetupFailure("portal_route_conflict", "The selected portal path is not a supported single route segment.");
4894
+ const adminRoot = join9(app, portal.slice(1));
4895
+ for (const name2 of [
4896
+ "page.tsx",
4897
+ "page.jsx",
4898
+ "page.js",
4899
+ "route.ts",
4900
+ "route.js"
4901
+ ]) {
4902
+ if (await exists2(join9(adminRoot, name2)))
4903
+ throw new SetupFailure("portal_route_conflict", "The selected admin path already belongs to the website. Choose an unused editor path in Siteplane.");
4904
+ }
4905
+ const configPath = join9(directory, "siteplane.config.json");
4906
+ const adminPath = join9(adminRoot, "[[...siteplane]]", `route.${routeExtension}`);
4907
+ const revalidationPath = join9(app, "api/siteplane/revalidate", `route.${routeExtension}`);
4908
+ const commonOptions = `siteId: config.siteId,
4909
+ publicRuntimeKeyId: config.publicSiteKeyId,
4910
+ fieldContractHash: process.env.SITEPLANE_BUILD_FIELD_CONTRACT_HASH${typed ? "!" : ""},
4911
+ deploymentRevision: process.env.SITEPLANE_BUILD_REVISION${typed ? "!" : ""},`;
4912
+ const admin = `import { createSiteplaneAdminRouteHandler } from "@siteplane/runtime-next/server";
4913
+ import config from ${JSON.stringify(importPath(adminPath, configPath))};
4914
+ export const runtime = "nodejs";
4915
+ function handle(request${typed ? ": Request" : ""}) {
4916
+ return createSiteplaneAdminRouteHandler({
4917
+ ${commonOptions}
4918
+ setupRunId: config.runId, portalPath: ${JSON.stringify(portal)},
4919
+ deploymentOrigin: process.env.SITEPLANE_SITE_ORIGIN${typed ? "!" : ""},
4920
+ publicSiteKey: config.publicSiteKey, apiBaseUrl: config.apiBaseUrl,
4921
+ revalidationSecret: process.env.SITEPLANE_REVALIDATION_SECRET${typed ? "!" : ""},
4922
+ sessionSecret: process.env.SITEPLANE_ADMIN_SESSION_SECRET${typed ? "!" : ""},
4923
+ })(request);
4924
+ }
4925
+ export { handle as GET, handle as POST };
4926
+ `;
4927
+ const revalidation = `import { revalidatePath, revalidateTag } from "next/cache";
4928
+ import { createSiteplaneRevalidationHandler } from "@siteplane/runtime-next/server";
4929
+ import config from ${JSON.stringify(importPath(revalidationPath, configPath))};
4930
+ export const runtime = "nodejs";
4931
+ export async function POST(request${typed ? ": Request" : ""}) {
4932
+ return createSiteplaneRevalidationHandler({
4933
+ ${commonOptions}
4934
+ audienceOrigin: process.env.SITEPLANE_SITE_ORIGIN${typed ? "!" : ""},
4935
+ secret: process.env.SITEPLANE_REVALIDATION_SECRET,
4936
+ revalidate: async (_payload, context) => {
4937
+ revalidateTag(context.cacheTag, { expire: 0 });
4938
+ revalidatePath("/", "layout");
4939
+ },
4940
+ })(request);
4941
+ }
4942
+ `;
4943
+ const bridge = `"use client";
4944
+ import { useEffect, useState } from "react";
4945
+ import { FieldRuntimeBridge } from "@siteplane/runtime-next/client";
4946
+ import config from ${JSON.stringify(importPath(bridgePath, configPath))};
4947
+ export default function SiteplaneBridge() {
4948
+ const [sessionId, setSessionId] = useState${typed ? "<string | null>" : ""}(null);
4949
+ useEffect(() => {
4950
+ const query = new URL(window.location.href).searchParams;
4951
+ const session = query.get("siteplaneSessionId");
4952
+ if (query.get("siteplanePreview") === "1" && query.get("siteplaneAdminOrigin") === new URL(config.apiBaseUrl).origin && session && /^[A-Za-z0-9_-]{1,128}$/.test(session)) setSessionId(session);
4953
+ }, []);
4954
+ return sessionId ? <FieldRuntimeBridge siteId={config.siteId} adminOrigin={new URL(config.apiBaseUrl).origin} sessionId={sessionId} /> : null;
4955
+ }
4956
+ `;
4957
+ const files = /* @__PURE__ */ new Map([
4958
+ [adminPath, admin],
4959
+ [revalidationPath, revalidation],
4960
+ [bridgePath, bridge]
4961
+ ]);
4962
+ const layoutSource = await readFile10(layoutPath, "utf8");
4963
+ if (!/import\s+SiteplaneBridge\s+from\s+["']\.\/_siteplane\/bridge["']/u.test(layoutSource)) {
4964
+ const ast = ts3.createSourceFile(layoutPath, layoutSource, ts3.ScriptTarget.Latest, true, ts3.ScriptKind.TSX);
4965
+ const closingBodies = [];
4966
+ const visit = (node) => {
4967
+ if (ts3.isJsxClosingElement(node) && node.tagName.getText(ast) === "body")
4968
+ closingBodies.push(node.getStart(ast));
4969
+ ts3.forEachChild(node, visit);
4970
+ };
4971
+ visit(ast);
4972
+ if (closingBodies.length !== 1)
4973
+ throw new SetupFailure("layout_integration_conflict", "Mount SiteplaneBridge inside the existing root body. The layout shape requires a deliberate agent edit.");
4974
+ const point = closingBodies[0];
4975
+ files.set(layoutPath, `import SiteplaneBridge from "./_siteplane/bridge";
4976
+ ${layoutSource.slice(0, point)}<SiteplaneBridge />${layoutSource.slice(point)}`);
4977
+ }
4978
+ const configs = (await Promise.all(["next.config.ts", "next.config.mjs", "next.config.js"].map(async (name2) => await exists2(join9(directory, name2)) ? join9(directory, name2) : null))).filter((path) => path !== null);
4979
+ if (configs.length > 1)
4980
+ throw new SetupFailure("next_config_conflict", "Choose one existing Next.js configuration before setup.");
4981
+ const nextPath = configs[0] ?? join9(directory, "next.config.mjs");
4982
+ const nextSource = configs.length ? await readFile10(nextPath, "utf8") : "export default {};\n";
4983
+ if (!/\bwithSiteplane\s*\(/u.test(nextSource)) {
4984
+ const ast = ts3.createSourceFile(nextPath, nextSource, ts3.ScriptTarget.Latest, true);
4985
+ const exported = ast.statements.filter(ts3.isExportAssignment);
4986
+ if (exported.length !== 1 || exported[0].isExportEquals)
4987
+ throw new SetupFailure("next_config_conflict", "Wrap the existing default Next.js configuration with withSiteplane from siteplane/next, preserving its existing options.");
4988
+ const expression = exported[0].expression;
4989
+ files.set(nextPath, `import { withSiteplane } from "siteplane/next";
4990
+ ${nextSource.slice(0, expression.getStart(ast))}withSiteplane(${expression.getText(ast)})${nextSource.slice(expression.end)}`);
4991
+ }
4992
+ files.set(join9(directory, ".siteplane/setup-guide-v2.md"), `# Siteplane setup task v2
4993
+
4994
+ ${task.goal}
4995
+
4996
+ ## Technical rules
4997
+
4998
+ ${task.discovery.rules.map((rule) => `- ${rule}`).join("\n")}
4999
+
5000
+ Run setup context for the current user instructions and authorized modules. Keep the user instructions distinct from your selection summary. Use setup deploy --wait for environment transfer and deployment; never read or print credential values.
5001
+ `);
5002
+ for (const [path, content] of files) {
5003
+ if (path === layoutPath || path === nextPath)
5004
+ continue;
5005
+ if (await exists2(path) && await readFile10(path, "utf8") !== content)
5006
+ throw new SetupFailure("owned_file_conflict", `Preserve the existing ${relative5(directory, path)} and reconcile its integration with the current Siteplane guide before retrying.`);
5007
+ }
5008
+ for (const path of files.keys())
5009
+ await assertSourcePath(directory, path);
5010
+ const before = /* @__PURE__ */ new Map();
5011
+ for (const path of files.keys())
5012
+ before.set(path, await readFile10(path, "utf8").catch((error) => {
5013
+ if (error.code === "ENOENT")
5014
+ return null;
5015
+ throw error;
5016
+ }));
5017
+ const packageManifest = JSON.parse(await readFile10(join9(directory, "package.json"), "utf8"));
5018
+ const pm = preflight.local.packageManager;
5019
+ const command = (args) => setupProcess("npx", ["--yes", `${pm.name}@${pm.version}`, ...args], {
5020
+ cwd: directory,
5021
+ timeoutMs: 3e5
5022
+ });
5023
+ if (packageManifest.devDependencies?.siteplane !== task.packages.siteplane)
5024
+ await command(pm.name === "npm" ? [
5025
+ "install",
5026
+ "--save-dev",
5027
+ "--save-exact",
5028
+ `siteplane@${task.packages.siteplane}`
5029
+ ] : [
5030
+ "add",
5031
+ "--save-dev",
5032
+ "--save-exact",
5033
+ `siteplane@${task.packages.siteplane}`
5034
+ ]);
5035
+ if (packageManifest.dependencies?.["@siteplane/runtime-next"] !== task.packages.runtimeNext)
5036
+ await command(pm.name === "npm" ? [
5037
+ "install",
5038
+ "--save-exact",
5039
+ `@siteplane/runtime-next@${task.packages.runtimeNext}`
5040
+ ] : [
5041
+ "add",
5042
+ "--save-exact",
5043
+ `@siteplane/runtime-next@${task.packages.runtimeNext}`
5044
+ ]);
5045
+ try {
5046
+ await assertExactSetupPackages(directory, task.packages);
5047
+ } catch {
5048
+ throw new SetupFailure("siteplane_package_pin_conflict", "The actual installed Siteplane packages do not match the task's exact versions. Resolve package access and the canonical lockfile before preparation.");
5049
+ }
5050
+ const written = [];
5051
+ for (const [path, content] of files) {
5052
+ await assertSourcePath(directory, path);
5053
+ const current = await readFile10(path, "utf8").catch((error) => {
5054
+ if (error.code === "ENOENT")
5055
+ return null;
5056
+ throw error;
5057
+ });
5058
+ if (current !== before.get(path))
5059
+ throw new SetupFailure("source_changed_during_prepare", "The source changed while dependencies were installing. Preserve the concurrent edit and reconcile it before retrying preparation.");
5060
+ if (current === content)
5061
+ continue;
5062
+ await mkdir6(dirname7(path), {
5063
+ recursive: true,
5064
+ mode: path.includes(`${sep2}.siteplane${sep2}`) ? 448 : 493
5065
+ });
5066
+ const temporary = `${path}.${randomUUID4()}.tmp`;
5067
+ const handle = await open3(temporary, "wx", path.includes(`${sep2}.siteplane${sep2}`) ? 384 : 420);
5068
+ try {
5069
+ await handle.writeFile(content, "utf8");
5070
+ await handle.sync();
5071
+ await handle.close();
5072
+ await assertSourcePath(directory, path);
5073
+ const latest = await readFile10(path, "utf8").catch((error) => {
5074
+ if (error.code === "ENOENT")
5075
+ return null;
5076
+ throw error;
5077
+ });
5078
+ if (latest !== current)
5079
+ throw new SetupFailure("source_changed_during_prepare", "The source changed before the prepared write. The existing edit was preserved.");
5080
+ await rename3(temporary, path);
5081
+ } finally {
5082
+ await handle.close().catch(() => void 0);
5083
+ await rm3(temporary, { force: true });
5084
+ }
5085
+ written.push(relative5(directory, path));
5086
+ }
5087
+ return {
5088
+ status: "prepared",
5089
+ preexistingChanges: preflight.local.git.changes,
5090
+ files: written,
5091
+ packages: task.packages,
5092
+ nextAction: "Describe your content selection, instrument those values, then run setup apply with the editor definition and --selection-summary-file. Commit only the intended source before setup deploy --wait."
5093
+ };
5094
+ } catch (error) {
5095
+ return {
5096
+ status: "action_required",
5097
+ code: error instanceof SetupFailure ? error.code : "setup_preparation_failed",
5098
+ message: error instanceof SetupFailure ? error.message : "Setup preparation could not complete. Check the selected project, package access and current task; no credential values are included in this diagnostic."
5099
+ };
5100
+ }
5101
+ }
5102
+ async function assertSourcePath(root, target) {
5103
+ let current = root;
5104
+ for (const part of relative5(root, target).split(sep2)) {
5105
+ if (part === "..")
5106
+ throw new SetupFailure("unsafe_source_path", "The prepared file is outside the selected application.");
5107
+ current = join9(current, part);
5108
+ const stat = await lstat2(current).catch((error) => {
5109
+ if (error.code === "ENOENT")
5110
+ return null;
5111
+ throw error;
5112
+ });
5113
+ if (stat?.isSymbolicLink())
5114
+ throw new SetupFailure("unsafe_source_path", "Preparation cannot write through a symbolic link.");
5115
+ }
5116
+ }
5117
+
5118
+ // ../cli/dist/commands/setup-lock.js
5119
+ import { randomUUID as randomUUID5 } from "crypto";
5120
+ import { lstat as lstat3, mkdir as mkdir7, open as open4, readFile as readFile11, rm as rm4, rmdir } from "fs/promises";
5121
+ import { resolve as resolve2 } from "path";
5122
+ import { z as z31 } from "zod";
5123
+ var ownerSchema = z31.object({ pid: z31.number().int().positive(), nonce: z31.string().uuid() }).strict();
5124
+ async function withSetupLock(projectDir, work) {
5125
+ const gitPath = (await setupProcess("git", ["rev-parse", "--git-path", "siteplane-setup.lock"], { cwd: projectDir })).trim();
5126
+ const path = resolve2(projectDir, gitPath);
5127
+ const owner = { pid: process.pid, nonce: randomUUID5() };
5128
+ for (let attempt = 0; ; attempt++) {
5129
+ try {
5130
+ const handle = await open4(path, "wx", 384);
5131
+ try {
5132
+ await handle.writeFile(JSON.stringify(owner));
5133
+ await handle.sync();
5134
+ } finally {
5135
+ await handle.close();
5136
+ }
5137
+ break;
5138
+ } catch (error) {
5139
+ if (error.code !== "EEXIST")
5140
+ throw error;
5141
+ if (attempt || (await lstat3(path)).isSymbolicLink())
5142
+ throw locked();
5143
+ const recovery = `${path}.recovery`;
5144
+ try {
5145
+ await mkdir7(recovery, { mode: 448 });
5146
+ } catch {
5147
+ throw locked();
5148
+ }
5149
+ try {
5150
+ const source = await readFile11(path, "utf8");
5151
+ const existing = ownerSchema.safeParse(JSON.parse(source));
5152
+ if (!existing.success)
5153
+ throw locked();
5154
+ try {
5155
+ process.kill(existing.data.pid, 0);
5156
+ throw locked();
5157
+ } catch (failure) {
5158
+ if (failure.code !== "ESRCH")
5159
+ throw locked();
5160
+ }
5161
+ if (await readFile11(path, "utf8") !== source)
5162
+ throw locked();
5163
+ await rm4(path);
5164
+ } finally {
5165
+ await rmdir(recovery);
5166
+ }
5167
+ }
5168
+ }
5169
+ try {
5170
+ return await work();
5171
+ } finally {
5172
+ const current = await readFile11(path, "utf8").catch(() => "");
5173
+ if (current === JSON.stringify(owner))
5174
+ await rm4(path);
5175
+ }
5176
+ }
5177
+ function locked() {
5178
+ return new SetupFailure("setup_process_active", "Another setup process owns this repository. Wait for it to finish; a live lock is never replaced.");
5179
+ }
5180
+
5181
+ // ../cli/dist/config/build-provenance.js
5182
+ import { createHash as createHash3 } from "crypto";
5183
+ import { execFile as execFile3 } from "child_process";
5184
+ import { lstat as lstat4, readFile as readFile12, writeFile as writeFile5, mkdir as mkdir8 } from "fs/promises";
5185
+ import { dirname as dirname8, isAbsolute as isAbsolute2, join as join10, relative as relative6, resolve as resolve3, sep as sep3 } from "path";
5186
+ import { isDeepStrictEqual, promisify as promisify3 } from "util";
5187
+ import { z as z32 } from "zod";
5188
+ var execute = promisify3(execFile3);
5189
+ var name = ".siteplane/build-source.json";
5190
+ var oid = z32.string().regex(/^[0-9a-f]{40}$/u);
5191
+ var entrySchema = z32.object({
5192
+ path: z32.string().min(1).max(4096),
5193
+ mode: z32.enum(["100644", "100755", "120000"]),
5194
+ oid
5195
+ }).strict();
5196
+ var schema = z32.object({
5197
+ version: z32.literal(2),
5198
+ revision: oid,
5199
+ commit: z32.string().min(1).max(1048576),
5200
+ entries: z32.array(entrySchema).min(1).max(1e5),
5201
+ providerConfigurations: z32.array(z32.object({
5202
+ path: z32.string().max(4096).regex(/(?:^|\/)vercel\.json$/u),
5203
+ source: z32.string().max(1048576)
5204
+ }).strict()).max(1e3)
5205
+ }).strict();
5206
+ async function writeBuildProvenance(checkout, revision) {
5207
+ const commit = (await execute("git", ["cat-file", "commit", revision], {
5208
+ cwd: checkout,
5209
+ maxBuffer: 1048576
5210
+ })).stdout;
5211
+ const tree = (await execute("git", ["ls-tree", "-rz", "--full-tree", revision], {
5212
+ cwd: checkout,
5213
+ maxBuffer: 16777216
5214
+ })).stdout;
5215
+ const entries = tree.split("\0").filter(Boolean).map((line) => {
5216
+ const match = /^(100644|100755|120000) blob ([0-9a-f]{40})\t([\s\S]+)$/u.exec(line);
5217
+ if (!match)
5218
+ throw new Error("Git submodules or unsupported tracked objects need an explicit deployment configuration.");
5219
+ return { path: match[3], mode: match[1], oid: match[2] };
5220
+ });
5221
+ for (const entry of entries) {
5222
+ if (entry.mode !== "120000")
5223
+ continue;
5224
+ const { readlink: readlink2 } = await import("fs/promises");
5225
+ const path = join10(checkout, entry.path);
5226
+ const target2 = resolve3(dirname8(path), await readlink2(path));
5227
+ const bound = relative6(checkout, target2);
5228
+ if (bound.startsWith("..") || isAbsolute2(bound))
5229
+ throw new Error("A tracked symbolic link escapes the committed upload; resolve that source boundary before deployment.");
5230
+ }
5231
+ const providerConfigurations = await Promise.all(entries.filter((entry) => /(?:^|\/)vercel\.json$/u.test(entry.path)).map(async (entry) => ({
5232
+ path: entry.path,
5233
+ source: await readFile12(join10(checkout, entry.path), "utf8")
5234
+ })));
5235
+ const record = schema.parse({
5236
+ version: 2,
5237
+ revision,
5238
+ commit,
5239
+ entries,
5240
+ providerConfigurations
5241
+ });
5242
+ const target = join10(checkout, name);
5243
+ await mkdir8(dirname8(target), { recursive: true, mode: 448 });
5244
+ await writeFile5(target, JSON.stringify(record), { flag: "wx", mode: 384 });
5245
+ }
5246
+
5247
+ // ../cli/dist/commands/setup-deploy.js
5248
+ import { createHash as createHash4, randomBytes, randomUUID as randomUUID6 } from "crypto";
5249
+ import { access as access5, mkdir as mkdir9, mkdtemp, rm as rm5, writeFile as writeFile6 } from "fs/promises";
5250
+ import { tmpdir } from "os";
5251
+ import { join as join11 } from "path";
5252
+ import { z as z33 } from "zod";
5253
+ var envSchema = z33.object({
5254
+ id: z33.string(),
5255
+ key: z33.string(),
5256
+ target: z33.array(z33.string()),
5257
+ type: z33.string(),
5258
+ comment: z33.string().optional(),
5259
+ updatedAt: z33.number().optional()
5260
+ });
5261
+ var deploymentSchema = z33.object({
5262
+ id: z33.string(),
5263
+ projectId: z33.string(),
5264
+ ownerId: z33.string().optional(),
5265
+ target: z33.string().nullable(),
5266
+ readyState: z33.string(),
5267
+ createdAt: z33.number(),
5268
+ url: z33.string(),
5269
+ meta: z33.record(z33.string(), z33.unknown()).optional(),
5270
+ gitSource: z33.object({ sha: z33.string().optional() }).nullable().optional()
5271
+ });
5272
+ var buildProofSchema = z33.object({
5273
+ node: z33.string(),
5274
+ packageManager: z33.enum(["npm", "pnpm"]),
5275
+ packageManagerVersion: z33.string(),
5276
+ lockfileHash: z33.string(),
5277
+ revision: z33.string(),
5278
+ fieldContractHash: z33.string(),
5279
+ siteplane: z33.string(),
5280
+ runtimeNext: z33.string(),
5281
+ next: z33.string(),
5282
+ react: z33.string()
5283
+ }).strict();
5284
+ var digest = (value) => `sha256:${createHash4("sha256").update(stableJsonStringify(value)).digest("hex")}`;
5285
+ function fail2(code, message) {
5286
+ throw new SetupFailure(code, message);
5287
+ }
5288
+ var defaults = {
5289
+ preflight: setupPreflightCommand,
5290
+ context: siteSetupContextCommand,
5291
+ api: setupVercelApi,
5292
+ process: setupProcess,
5293
+ verify: siteSetupVerifyCommand,
5294
+ post: postSiteSetup,
5295
+ writeBuildProvenance,
5296
+ now: Date.now,
5297
+ pause: (milliseconds) => new Promise((resolve5) => setTimeout(resolve5, milliseconds))
5298
+ };
5299
+ async function setupDeployCommand(input, deps = defaults) {
5300
+ let record = null;
5301
+ try {
5302
+ if (!await access5(join11(input.projectDir, "siteplane.config.json")).then(() => true, () => false) || !await access5(join11(input.projectDir, ".siteplane/credentials.json")).then(() => true, () => false))
5303
+ fail2("local_connection_missing", "Open this site's setup and choose Replace local connection, then initialize this folder with its new one-time grant.");
5304
+ await assertLocalCredentialsPathSafe(input.projectDir);
5305
+ const config = await readProjectConfig(input.projectDir);
5306
+ let credentials = await readLocalCredentials(input.projectDir);
5307
+ const context = await deps.context(input);
5308
+ if (context.status !== "ok")
5309
+ return context;
5310
+ const task = context.task;
5311
+ if (task.run?.runId !== config.runId || task.site?.siteId !== config.siteId || task.packages.siteplane !== config.packages.siteplane || task.packages.runtimeNext !== config.packages.runtimeNext || task.run.fieldContractHash !== config.fieldContractHash)
5312
+ fail2("setup_contract_conflict", "Local configuration and the current applied setup task must match before deployment.");
5313
+ if (!config.fieldContractHash)
5314
+ fail2("setup_apply_required", "Instrument the agreed content and run setup apply before deployment.");
5315
+ record = task.deployment;
5316
+ const preflight = await deps.preflight(input);
5317
+ if (preflight.status !== "ready")
5318
+ return {
5319
+ ...preflight,
5320
+ ...record ? { deploymentId: record.deploymentId, deadline: record.deadline } : {}
5321
+ };
5322
+ const { local, provider } = preflight;
5323
+ if (local.git.changes.length || !local.git.revision || !local.git.branch)
5324
+ fail2("uncommitted_source", "Commit only the intended setup source on a named branch before deployment. Siteplane does not stage, reset or publish unrelated local work.");
5325
+ const revision = local.git.revision;
5326
+ if (!/^[0-9a-f]{40}$/u.test(revision))
5327
+ fail2("invalid_git_revision", "A full committed Git revision is required.");
5328
+ try {
5329
+ await assertExactSetupPackages(local.projectDirectory, config.packages);
5330
+ } catch {
5331
+ fail2("siteplane_package_pin_conflict", "Install the exact Siteplane package versions from the current task and commit their canonical lockfile.");
5332
+ }
5333
+ let operationDeadline = record && !["verified", "failed"].includes(record.phase) ? Date.parse(record.deadline) : deps.now() + 9e5;
5334
+ const remaining = () => {
5335
+ const milliseconds = operationDeadline - deps.now();
5336
+ if (milliseconds <= 0)
5337
+ fail2("provider_deadline_exceeded", "The 15-minute provider deadline expired. Inspect the recorded operation; setup will not start a second deployment on resume.");
5338
+ return milliseconds;
5339
+ };
5340
+ const processCommand = async (binary, args, options) => {
5341
+ const output = await deps.process(binary, args, {
5342
+ ...options,
5343
+ timeoutMs: Math.min(options.timeoutMs ?? 12e4, remaining())
5344
+ });
5345
+ remaining();
5346
+ return output;
5347
+ };
5348
+ const scope = `teamId=${provider.teamId}`;
5349
+ const api = async (endpoint, body, method) => {
5350
+ remaining();
5351
+ const result = await deps.api(local.projectDirectory, endpoint, body, method, { deadline: operationDeadline });
5352
+ remaining();
5353
+ return result;
5354
+ };
5355
+ const domains = z33.object({
5356
+ domains: z33.array(z33.object({
5357
+ name: z33.string(),
5358
+ verified: z33.boolean(),
5359
+ redirect: z33.string().nullable().optional(),
5360
+ gitBranch: z33.string().nullable().optional()
5361
+ }))
5362
+ }).parse(await api(`/v9/projects/${provider.projectId}/domains?${scope}`)).domains.filter((domain) => domain.verified && !domain.redirect && !domain.gitBranch);
5363
+ const origin = record?.origin ?? (domains.length === 1 ? `https://${domains[0].name}` : null);
5364
+ if (!origin || !domains.some((domain) => `https://${domain.name}` === origin))
5365
+ fail2("production_origin_ambiguous", "The linked project needs one verified canonical Production domain, or the existing setup domain must still belong to it.");
5366
+ if (record && (record.projectId !== provider.projectId || record.teamId !== provider.teamId))
5367
+ fail2("deployment_target_conflict", "This setup already belongs to a different provider project/team. Restore its original local link.");
5368
+ const listEnv = async () => z33.object({ envs: z33.array(envSchema) }).parse(await api(`/v10/projects/${provider.projectId}/env?${scope}`)).envs;
5369
+ let env = await listEnv();
5370
+ const owned = (key) => {
5371
+ const matches2 = env.filter((item) => item.key === key && item.target.includes("production"));
5372
+ if (matches2.length > 1 || matches2.some((item) => item.target.length !== 1))
5373
+ fail2("environment_scope_conflict", `Separate ${key} into an unambiguous Production-only binding before setup; other scopes are preserved.`);
5374
+ return matches2[0];
5375
+ };
5376
+ const admin = owned("SITEPLANE_ADMIN_SESSION_SECRET");
5377
+ if (record && !admin && !credentials.adminSession)
5378
+ fail2("admin_secret_recovery_required", "The previously configured admin-session secret is missing. Restore it through an explicit owner-directed configuration recovery; connection replacement never rotates it.");
5379
+ if (!credentials.adminSession && !record && admin)
5380
+ fail2("admin_secret_binding_unknown", "An existing admin-session secret has no matching setup generation. Resolve its ownership before configuration; Siteplane will not overwrite it.");
5381
+ if (!credentials.adminSession && !record && !admin) {
5382
+ credentials = {
5383
+ ...credentials,
5384
+ adminSession: {
5385
+ secret: randomBytes(32).toString("base64url"),
5386
+ generation: randomUUID6()
5387
+ }
5388
+ };
5389
+ await writeLocalCredentials(input.projectDir, credentials);
5390
+ const persisted = await readLocalCredentials(input.projectDir);
5391
+ if (persisted.adminSession?.secret !== credentials.adminSession.secret)
5392
+ fail2("credential_write_failed", "The generated admin credential could not be read back securely.");
5393
+ }
5394
+ const generation = record?.adminSecretGeneration ?? credentials.adminSession.generation;
5395
+ if (credentials.adminSession && credentials.adminSession.generation !== generation)
5396
+ fail2("admin_secret_generation_conflict", "The local admin credential belongs to another configuration generation. Preserve the provider secret and resolve the local binding.");
5397
+ if (admin && admin.comment !== `Siteplane admin generation ${generation}`)
5398
+ fail2("admin_secret_generation_conflict", "The provider admin secret does not match the recorded setup generation. No secret was replaced.");
5399
+ const publicBindings = {
5400
+ SITEPLANE_SITE_ORIGIN: origin,
5401
+ SITEPLANE_SETUP_RUN_ID: config.runId,
5402
+ SITEPLANE_PORTAL_PATH: task.site.portalPath,
5403
+ SITEPLANE_BUILD_FIELD_CONTRACT_HASH: config.fieldContractHash,
5404
+ SITEPLANE_BUILD_REVISION: revision
5405
+ };
5406
+ const fingerprint = digest({
5407
+ publicBindings,
5408
+ siteId: config.siteId,
5409
+ publicKeyId: config.publicSiteKeyId,
5410
+ apiBaseUrl: config.apiBaseUrl,
5411
+ packages: config.packages,
5412
+ bindings: [
5413
+ ...Object.keys(publicBindings),
5414
+ "SITEPLANE_REVALIDATION_SECRET",
5415
+ "SITEPLANE_ADMIN_SESSION_SECRET"
5416
+ ].sort(),
5417
+ adminSecretGeneration: generation,
5418
+ revalidationGeneration: task.revalidationGeneration,
5419
+ projectId: provider.projectId,
5420
+ teamId: provider.teamId,
5421
+ rootDirectory: provider.rootDirectory,
5422
+ nodeVersion: provider.nodeVersion,
5423
+ installCommand: provider.installCommand,
5424
+ buildCommand: provider.buildCommand,
5425
+ packageManagerSelection: provider.packageManagerSelection,
5426
+ declaredPackageManager: provider.declaredPackageManager,
5427
+ lockfileHash: local.lockfile.sha256
5428
+ });
5429
+ const save = async (next) => {
5430
+ const result = await deps.post({ ...input, deadline: operationDeadline }, "deployment", {
5431
+ action: "record",
5432
+ expectedOperationId: record?.operationId ?? null,
5433
+ record: next
5434
+ });
5435
+ const parsed = z33.object({
5436
+ ok: z33.literal(true),
5437
+ data: z33.object({ record: setupDeploymentRecordSchema }).strict()
5438
+ }).strict().safeParse(result.payload);
5439
+ if (!result.ok || !parsed.success || stableJsonStringify(parsed.data.data.record) !== stableJsonStringify(next))
5440
+ fail2("deployment_record_unconfirmed", "The setup deployment record was not acknowledged. Reload context before continuing; no deployment is blindly repeated.");
5441
+ record = parsed.data.data.record;
5442
+ if (next.phase === "planned")
5443
+ operationDeadline = Date.parse(next.deadline);
5444
+ };
5445
+ const fresh = () => ({
5446
+ version: 1,
5447
+ operationId: randomUUID6(),
5448
+ provider: "vercel",
5449
+ projectId: provider.projectId,
5450
+ teamId: provider.teamId,
5451
+ origin,
5452
+ deploymentId: null,
5453
+ revision,
5454
+ fieldContractHash: config.fieldContractHash,
5455
+ packages: config.packages,
5456
+ configurationFingerprint: fingerprint,
5457
+ adminSecretGeneration: generation,
5458
+ revalidationGeneration: task.revalidationGeneration,
5459
+ startedAt: new Date(deps.now()).toISOString(),
5460
+ environmentWrittenAt: null,
5461
+ deployRequestedAt: null,
5462
+ deadline: new Date(deps.now() + 9e5).toISOString(),
5463
+ phase: "planned",
5464
+ build: null
5465
+ });
5466
+ if (!record || record.configurationFingerprint !== fingerprint) {
5467
+ if (record && !["verified", "deployed", "failed"].includes(record.phase))
5468
+ fail2("previous_deployment_unresolved", "Resolve the previous provider operation before changing the source or configuration.");
5469
+ await save(fresh());
5470
+ }
5471
+ const progress = () => input.onProgress?.({
5472
+ phase: record.phase,
5473
+ deadline: record.deadline,
5474
+ deploymentId: record.deploymentId
5475
+ });
5476
+ progress();
5477
+ const values = {
5478
+ ...publicBindings,
5479
+ SITEPLANE_REVALIDATION_SECRET: credentials.siteRevalidationSecret,
5480
+ ...credentials.adminSession ? { SITEPLANE_ADMIN_SESSION_SECRET: credentials.adminSession.secret } : {}
5481
+ };
5482
+ const pending = [];
5483
+ for (const [key, value] of Object.entries(values)) {
5484
+ const item = owned(key);
5485
+ if (key === "SITEPLANE_ADMIN_SESSION_SECRET" && item && record.phase === "verified")
5486
+ continue;
5487
+ const current = item ? z33.object({ key: z33.literal(key), value: z33.string() }).parse(await api(`/v1/projects/${provider.projectId}/env/${encodeURIComponent(item.id)}?${scope}`)).value : null;
5488
+ if (current !== value)
5489
+ pending.push({
5490
+ key,
5491
+ value,
5492
+ type: key.endsWith("SECRET") ? "encrypted" : "plain",
5493
+ target: ["production"],
5494
+ comment: key === "SITEPLANE_ADMIN_SESSION_SECRET" ? `Siteplane admin generation ${generation}` : "Managed by Siteplane setup"
5495
+ });
5496
+ }
5497
+ if (pending.length && record.phase !== "planned") {
5498
+ if (!["verified", "deployed", "failed"].includes(record.phase))
5499
+ fail2("environment_changed_during_deploy", "The environment changed during an unresolved deployment. Inspect that operation before retrying.");
5500
+ await save(fresh());
5501
+ }
5502
+ if (record.phase === "planned") {
5503
+ if (deps.now() >= Date.parse(record.deadline))
5504
+ fail2("provider_deadline_exceeded", "The setup provider deadline expired before configuration. Inspect its existing record before a new attempt.");
5505
+ if (pending.length)
5506
+ await api(`/v10/projects/${provider.projectId}/env?${scope}&upsert=true`, pending);
5507
+ env = await listEnv();
5508
+ for (const [key, value] of Object.entries(values)) {
5509
+ const item = owned(key);
5510
+ if (!item)
5511
+ fail2("environment_write_unconfirmed", "A Siteplane Production binding was not persisted; retry the same configuration.");
5512
+ const observed = z33.object({ key: z33.literal(key), value: z33.string() }).parse(await api(`/v1/projects/${provider.projectId}/env/${encodeURIComponent(item.id)}?${scope}`));
5513
+ if (observed.value !== value)
5514
+ fail2("environment_write_unconfirmed", "The provider readback does not match the authorized Siteplane value; no deployment was started.");
5515
+ }
5516
+ await save({
5517
+ ...record,
5518
+ phase: "environment_written",
5519
+ environmentWrittenAt: new Date(deps.now()).toISOString()
5520
+ });
5521
+ }
5522
+ const detail = async (id) => deploymentSchema.parse(await api(`/v13/deployments/${encodeURIComponent(id)}?${scope}&withGitRepoInfo=true`));
5523
+ const matches = (deployment2) => deployment2.projectId === provider.projectId && deployment2.target === "production" && (!deployment2.ownerId || deployment2.ownerId === provider.teamId) && deployment2.createdAt >= Date.parse(record.environmentWrittenAt) && (deployment2.meta?.siteplaneOperationId === record.operationId || provider.git && (deployment2.gitSource?.sha ?? deployment2.meta?.githubCommitSha ?? deployment2.meta?.gitlabCommitSha ?? deployment2.meta?.bitbucketCommitSha ?? deployment2.meta?.gitCommitSha) === revision);
5524
+ const discover = async () => {
5525
+ const result = z33.object({ deployments: z33.array(z33.object({ uid: z33.string() })) }).parse(await api(`/v6/deployments?${scope}&projectId=${provider.projectId}&target=production&since=${Date.parse(record.environmentWrittenAt)}&limit=100`));
5526
+ const candidates = [];
5527
+ for (const item of result.deployments) {
5528
+ const found = await detail(item.uid);
5529
+ if (matches(found))
5530
+ candidates.push(found);
5531
+ }
5532
+ if (candidates.length > 1)
5533
+ fail2("deployment_ambiguous", "Multiple matching provider deployments exist. Select the existing intended operation before continuing; no additional build was started.");
5534
+ return candidates[0] ?? null;
5535
+ };
5536
+ let deployment = record.deploymentId ? await detail(record.deploymentId) : await discover();
5537
+ if (deployment && !matches(deployment))
5538
+ fail2("deployment_binding_conflict", "The stored deployment does not match this project, commit and environment generation.");
5539
+ if (!deployment && record.phase === "environment_written") {
5540
+ const status = await processCommand("git", ["status", "--porcelain=v1", "--untracked-files=all"], { cwd: local.repositoryRoot });
5541
+ const head = (await processCommand("git", ["rev-parse", "HEAD"], {
5542
+ cwd: local.repositoryRoot
5543
+ })).trim();
5544
+ if (status.trim() || head !== revision)
5545
+ fail2("source_changed_before_deploy", "The source changed after preflight. Commit only the intended source and rerun setup deploy.");
5546
+ await save({
5547
+ ...record,
5548
+ phase: "deploy_requested",
5549
+ deployRequestedAt: new Date(deps.now()).toISOString()
5550
+ });
5551
+ try {
5552
+ if (provider.git) {
5553
+ const remote = (await processCommand("git", ["ls-remote", "origin", `refs/heads/${local.git.branch}`], { cwd: local.repositoryRoot })).trim().split(/\s/u)[0];
5554
+ if (remote !== revision)
5555
+ await processCommand("git", ["push", "origin", `HEAD:refs/heads/${local.git.branch}`], { cwd: local.repositoryRoot });
5556
+ else {
5557
+ const gitSource = {
5558
+ type: provider.git.type,
5559
+ ref: revision,
5560
+ ...provider.git.repoId ? { repoId: provider.git.repoId } : {}
5561
+ };
5562
+ const body = JSON.stringify({
5563
+ name: provider.name,
5564
+ project: provider.projectId,
5565
+ target: "production",
5566
+ gitSource,
5567
+ meta: { siteplaneOperationId: record.operationId }
5568
+ });
5569
+ await processCommand("vercel", [
5570
+ "api",
5571
+ `/v13/deployments?${scope}`,
5572
+ "--method",
5573
+ "POST",
5574
+ "--input",
5575
+ "-",
5576
+ "--raw"
5577
+ ], { cwd: local.projectDirectory, input: body, timeoutMs: 3e4 });
5578
+ }
5579
+ } else {
5580
+ const upload = await mkdtemp(join11(tmpdir(), "siteplane-setup-upload-"));
5581
+ try {
5582
+ await processCommand("git", [
5583
+ "clone",
5584
+ "--no-hardlinks",
5585
+ "--no-checkout",
5586
+ "--",
5587
+ local.repositoryRoot,
5588
+ upload
5589
+ ], { cwd: local.repositoryRoot });
5590
+ await processCommand("git", ["checkout", "--detach", revision], {
5591
+ cwd: upload
5592
+ });
5593
+ await deps.writeBuildProvenance(upload, revision);
5594
+ await mkdir9(join11(upload, ".vercel"), {
5595
+ recursive: true,
5596
+ mode: 448
5597
+ });
5598
+ await writeFile6(join11(upload, ".vercel/project.json"), JSON.stringify({
5599
+ projectId: provider.projectId,
5600
+ orgId: provider.teamId
5601
+ }), { mode: 384 });
5602
+ await processCommand("vercel", [
5603
+ "deploy",
5604
+ "--yes",
5605
+ "--prod",
5606
+ "--no-wait",
5607
+ "--scope",
5608
+ provider.teamId,
5609
+ "--meta",
5610
+ `siteplaneOperationId=${record.operationId}`
5611
+ ], { cwd: upload, timeoutMs: 12e4 });
5612
+ } finally {
5613
+ await rm5(upload, { recursive: true, force: true });
5614
+ }
5615
+ }
5616
+ } catch (error) {
5617
+ if (error instanceof SetupFailure && error.code === "provider_access_denied") {
5618
+ await save({
5619
+ ...record,
5620
+ phase: "environment_written",
5621
+ deployRequestedAt: null
5622
+ });
5623
+ throw error;
5624
+ }
5625
+ if (!(error instanceof SetupFailure) || !["provider_timeout", "provider_transport_failed"].includes(error.code))
5626
+ throw error;
5627
+ }
5628
+ }
5629
+ let poll = 0;
5630
+ while (!deployment || !["READY", "ERROR", "CANCELED"].includes(deployment.readyState)) {
5631
+ progress();
5632
+ const remaining2 = Date.parse(record.deadline) - deps.now();
5633
+ if (remaining2 <= 0)
5634
+ fail2("provider_deadline_exceeded", "The 15-minute provider deadline expired. Inspect the recorded operation; setup will not start a second deployment on resume.");
5635
+ await deps.pause(Math.min(remaining2, Math.min(15e3, 2e3 * 2 ** Math.min(poll++, 3)) + Math.floor(Math.random() * 500)));
5636
+ deployment = deployment ? await detail(deployment.id) : await discover();
5637
+ if (deployment && !matches(deployment))
5638
+ fail2("deployment_binding_conflict", "The provider operation no longer matches this setup deployment.");
5639
+ }
5640
+ if (deployment.readyState !== "READY") {
5641
+ await save({ ...record, phase: "failed", deploymentId: deployment.id });
5642
+ fail2("provider_build_failed", "The existing provider build failed or was canceled. Fix its diagnosed package/build configuration before requesting another deployment.");
5643
+ }
5644
+ if (record.phase !== "verified")
5645
+ await save({
5646
+ ...record,
5647
+ phase: "deployed",
5648
+ deploymentId: deployment.id
5649
+ });
5650
+ const logs = await processCommand("vercel", ["inspect", deployment.url, "--logs", "--scope", provider.teamId], { cwd: local.projectDirectory, includeStderr: true });
5651
+ const markers = [
5652
+ ...logs.matchAll(/SITEPLANE_BUILD_PROOF=(\{[^\n]+\})/gu)
5653
+ ].map((match) => buildProofSchema.parse(JSON.parse(match[1])));
5654
+ const proof = markers.find((item) => item.revision === revision && item.fieldContractHash === config.fieldContractHash);
5655
+ if (!proof || !/^22\.\d+\.\d+$/u.test(proof.node) || proof.siteplane !== config.packages.siteplane || proof.runtimeNext !== config.packages.runtimeNext || proof.lockfileHash !== `sha256:${local.lockfile.sha256}` || proof.next !== local.nextVersion || proof.react !== local.reactVersion || !(proof.packageManager === "npm" ? /^10\./u : /^(9|10)\./u).test(proof.packageManagerVersion) || local.packageManager.declared && `${proof.packageManager}@${proof.packageManagerVersion}` !== local.packageManager.declared)
5656
+ fail2("provider_build_identity_missing", "The actual provider build did not prove the expected Node, package manager, lockfile, package pins and applied source. Inspect its install/build configuration.");
5657
+ const canonical = await detail(new URL(origin).hostname);
5658
+ if (canonical.id !== deployment.id)
5659
+ fail2("production_alias_not_ready", "The canonical Production domain does not yet point to the verified deployment. Inspect the existing promotion; do not create another build.");
5660
+ const verified = await deps.verify({ ...input, deploymentUrl: origin });
5661
+ if (verified.status !== "ready_for_review")
5662
+ return {
5663
+ ...verified,
5664
+ deadline: record.deadline,
5665
+ deploymentId: deployment.id
5666
+ };
5667
+ await save({
5668
+ ...record,
5669
+ phase: "verified",
5670
+ deploymentId: deployment.id,
5671
+ build: {
5672
+ node: proof.node,
5673
+ packageManager: proof.packageManager,
5674
+ packageManagerVersion: proof.packageManagerVersion,
5675
+ lockfileHash: proof.lockfileHash
5676
+ }
5677
+ });
5678
+ return {
5679
+ status: "ready_for_review",
5680
+ deploymentId: deployment.id,
5681
+ origin,
5682
+ configurationFingerprint: fingerprint,
5683
+ deadline: record.deadline,
5684
+ build: record.build
5685
+ };
5686
+ } catch (error) {
5687
+ return {
5688
+ status: "action_required",
5689
+ code: error instanceof SetupFailure ? error.code : "setup_deployment_failed",
5690
+ message: error instanceof SetupFailure ? error.message : "The setup deployment could not be validated. Inspect the local connection and bound provider operation; credential values are never included.",
5691
+ ...record ? { deploymentId: record.deploymentId, deadline: record.deadline } : {}
5692
+ };
5693
+ }
5694
+ }
4135
5695
 
4136
5696
  // src/bin/run-siteplane.ts
4137
5697
  async function runSiteplaneCli(args, dependencies = createDefaultDependencies()) {
5698
+ const writes = args[0] === "init" || args[0] === "setup" && ["apply", "prepare", "deploy"].includes(args[1] ?? "");
5699
+ const missingDeploy = args[0] === "setup" && args[1] === "deploy" && !await access6(join12(dependencies.cwd(), "siteplane.config.json")).then(
5700
+ () => true,
5701
+ () => false
5702
+ );
5703
+ return writes && !missingDeploy ? dependencies.withSetupLock(
5704
+ dependencies.cwd(),
5705
+ () => runSiteplaneCliUnlocked(args, dependencies)
5706
+ ) : runSiteplaneCliUnlocked(args, dependencies);
5707
+ }
5708
+ async function runSiteplaneCliUnlocked(args, dependencies = createDefaultDependencies()) {
4138
5709
  const [command, ...commandArgs] = args;
4139
5710
  if (command === "init") {
4140
5711
  const setupToken = readOption(commandArgs, "--setup-token");
@@ -4207,7 +5778,7 @@ async function runAnalyticsCommand(args, dependencies) {
4207
5778
  siteId: projectConfig.siteId,
4208
5779
  ...readOptionalVercelAutomationBypassSecret(dependencies.env)
4209
5780
  };
4210
- const manifestPath = join7(projectDir, "siteplane.analytics.json");
5781
+ const manifestPath = join12(projectDir, "siteplane.analytics.json");
4211
5782
  if (subcommand === "init") {
4212
5783
  const rawPublicKey = await dependencies.commands.analyticsPublicKeyCommand({
4213
5784
  config
@@ -4239,18 +5810,18 @@ async function runAnalyticsCommand(args, dependencies) {
4239
5810
  projectDir
4240
5811
  }
4241
5812
  );
4242
- const artifactFilePaths = [join7(projectDir, ".siteplane/analytics.md")];
5813
+ const artifactFilePaths = [join12(projectDir, ".siteplane/analytics.md")];
4243
5814
  const cspFilePaths = await existingPaths([
4244
- join7(projectDir, "next.config.js"),
4245
- join7(projectDir, "next.config.mjs"),
4246
- join7(projectDir, "next.config.ts"),
4247
- join7(projectDir, "middleware.ts"),
4248
- join7(projectDir, "src/middleware.ts")
5815
+ join12(projectDir, "next.config.js"),
5816
+ join12(projectDir, "next.config.mjs"),
5817
+ join12(projectDir, "next.config.ts"),
5818
+ join12(projectDir, "middleware.ts"),
5819
+ join12(projectDir, "src/middleware.ts")
4249
5820
  ]);
4250
5821
  const check = await dependencies.commands.analyticsCheckCommand({
4251
5822
  manifestPath,
4252
5823
  sourceFilePaths,
4253
- packageJsonPath: join7(projectDir, "package.json"),
5824
+ packageJsonPath: join12(projectDir, "package.json"),
4254
5825
  cspFilePaths,
4255
5826
  artifactFilePaths
4256
5827
  });
@@ -4318,6 +5889,28 @@ async function runSetupCommand(args, dependencies) {
4318
5889
  const connectionOptions = readOptionalVercelAutomationBypassSecret(
4319
5890
  dependencies.env
4320
5891
  );
5892
+ if (area === "deploy") {
5893
+ const result = await dependencies.commands.setupDeployCommand({
5894
+ projectDir,
5895
+ onProgress: (progress) => dependencies.stderr(JSON.stringify({ status: "waiting", ...progress }))
5896
+ });
5897
+ dependencies.stdout(JSON.stringify(result, null, 2));
5898
+ return { exitCode: result.status === "ready_for_review" ? 0 : 1 };
5899
+ }
5900
+ if (area === "preflight") {
5901
+ const result = await dependencies.commands.setupPreflightCommand({
5902
+ projectDir
5903
+ });
5904
+ dependencies.stdout(JSON.stringify(result, null, 2));
5905
+ return { exitCode: result.status === "ready" ? 0 : 1 };
5906
+ }
5907
+ if (area === "prepare") {
5908
+ const result = await dependencies.commands.setupPrepareCommand({
5909
+ projectDir
5910
+ });
5911
+ dependencies.stdout(JSON.stringify(result, null, 2));
5912
+ return { exitCode: result.status === "action_required" ? 1 : 0 };
5913
+ }
4321
5914
  if (area === "context") {
4322
5915
  const result = await dependencies.commands.siteSetupContextCommand({
4323
5916
  projectDir,
@@ -4326,6 +5919,15 @@ async function runSetupCommand(args, dependencies) {
4326
5919
  dependencies.stdout(JSON.stringify(result, null, 2));
4327
5920
  return { exitCode: result.status === "action_required" ? 1 : 0 };
4328
5921
  }
5922
+ if (area === "activity") {
5923
+ const result = await dependencies.commands.siteSetupActivityCommand({
5924
+ projectDir,
5925
+ phase: readRequiredOption(areaArgs, "--phase"),
5926
+ ...connectionOptions
5927
+ });
5928
+ dependencies.stdout(JSON.stringify(result, null, 2));
5929
+ return { exitCode: result.status === "action_required" ? 1 : 0 };
5930
+ }
4329
5931
  if (area === "apply") {
4330
5932
  const editorDefinition = await dependencies.readJsonFile(
4331
5933
  readRequiredOption(areaArgs, "--definition")
@@ -4333,6 +5935,12 @@ async function runSetupCommand(args, dependencies) {
4333
5935
  const result = await dependencies.commands.siteSetupApplyCommand({
4334
5936
  projectDir,
4335
5937
  editorDefinition,
5938
+ contentSelectionSummary: await dependencies.readTextFile(
5939
+ resolve4(
5940
+ projectDir,
5941
+ readRequiredOption(areaArgs, "--selection-summary-file")
5942
+ )
5943
+ ),
4336
5944
  ...connectionOptions
4337
5945
  });
4338
5946
  dependencies.stdout(JSON.stringify(result, null, 2));
@@ -4348,13 +5956,15 @@ async function runSetupCommand(args, dependencies) {
4348
5956
  dependencies.stdout(JSON.stringify(result, null, 2));
4349
5957
  return { exitCode: result.status === "ready_for_review" ? 0 : 1 };
4350
5958
  }
4351
- dependencies.stdout("Usage: siteplane setup <context|apply|verify>");
5959
+ dependencies.stdout(
5960
+ "Usage: siteplane setup <preflight|prepare|context|activity|apply|deploy|verify>"
5961
+ );
4352
5962
  return { exitCode: 1 };
4353
5963
  }
4354
5964
  async function runBookingCommand(args, dependencies) {
4355
5965
  const [subcommand, ...subcommandArgs] = args;
4356
5966
  const projectDir = dependencies.cwd();
4357
- const manifestPath = join7(projectDir, "siteplane.booking.json");
5967
+ const manifestPath = join12(projectDir, "siteplane.booking.json");
4358
5968
  if (subcommand === "init") {
4359
5969
  const siteId = readRequiredOption(subcommandArgs, "--site-id");
4360
5970
  const siteKeyPrefix = readRequiredOption(
@@ -4433,7 +6043,7 @@ async function runBookingCommand(args, dependencies) {
4433
6043
  resourceId: readOption(subcommandArgs, "--resource-id") ?? null,
4434
6044
  startUtc: readRequiredOption(subcommandArgs, "--start-utc"),
4435
6045
  customer,
4436
- clientToken: readOption(subcommandArgs, "--client-token") ?? randomUUID4(),
6046
+ clientToken: readOption(subcommandArgs, "--client-token") ?? randomUUID7(),
4437
6047
  paymentChoice: "onsite"
4438
6048
  }
4439
6049
  });
@@ -4469,19 +6079,25 @@ function createDefaultDependencies() {
4469
6079
  env: process.env,
4470
6080
  stdout: (line) => console.log(line),
4471
6081
  stderr: (line) => console.error(line),
6082
+ readTextFile: (path) => readFile13(path, "utf8"),
6083
+ withSetupLock,
4472
6084
  readJsonFile: async (path) => JSON.parse(
4473
- await readFile8(resolve(process.cwd(), path), "utf8")
6085
+ await readFile13(resolve4(process.cwd(), path), "utf8")
4474
6086
  ),
4475
6087
  readPackageVersion: async () => {
4476
6088
  const packageJson = JSON.parse(
4477
- await readFile8(new URL("../../package.json", import.meta.url), "utf8")
6089
+ await readFile13(new URL("../../package.json", import.meta.url), "utf8")
4478
6090
  );
4479
6091
  return packageJson.version;
4480
6092
  },
4481
6093
  commands: {
4482
6094
  siteSetupApplyCommand,
4483
6095
  siteSetupContextCommand,
6096
+ siteSetupActivityCommand,
4484
6097
  siteSetupVerifyCommand,
6098
+ setupPreflightCommand,
6099
+ setupPrepareCommand,
6100
+ setupDeployCommand,
4485
6101
  initCommand,
4486
6102
  bookingInitCommand,
4487
6103
  bookingCheckCommand,
@@ -4542,7 +6158,7 @@ async function existingPaths(paths) {
4542
6158
  const existing = [];
4543
6159
  for (const path of paths) {
4544
6160
  try {
4545
- await access2(path);
6161
+ await access6(path);
4546
6162
  existing.push(path);
4547
6163
  } catch {
4548
6164
  }