@01.software/cli 0.16.0 → 0.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -13,18 +13,52 @@ import {
13
13
 
14
14
  // src/lib/credentials.ts
15
15
  import {
16
+ chmodSync,
17
+ closeSync,
18
+ constants as fsConstants,
16
19
  existsSync,
20
+ fchmodSync,
21
+ fstatSync,
22
+ fsyncSync,
23
+ lstatSync,
17
24
  mkdirSync,
25
+ openSync,
18
26
  readFileSync,
19
- writeFileSync,
27
+ renameSync,
20
28
  unlinkSync,
29
+ writeFileSync,
21
30
  appendFileSync
22
31
  } from "fs";
23
- import { join } from "path";
32
+ import { spawnSync } from "child_process";
33
+ import { randomBytes } from "crypto";
34
+ import { dirname, join } from "path";
24
35
  import { homedir } from "os";
25
36
  var DIR_NAME = ".01software";
26
37
  var FILE_NAME = "credentials.json";
27
38
  var TENANTS_FILE = "tenants.json";
39
+ var LOCAL_CREDENTIAL_PATH = `${DIR_NAME}/${FILE_NAME}`;
40
+ var LOCAL_GITIGNORE_ENTRY = `${DIR_NAME}/`;
41
+ var PRIVATE_DIRECTORY_MODE = 448;
42
+ var PRIVATE_FILE_MODE = 384;
43
+ var CURRENT_USER_ID = process.platform !== "win32" && typeof process.getuid === "function" ? process.getuid() : null;
44
+ var HAS_POSIX_FILE_PERMISSIONS = CURRENT_USER_ID !== null;
45
+ var SANITIZED_GIT_ENVIRONMENT_VARIABLES = /* @__PURE__ */ new Set([
46
+ "GIT_ALTERNATE_OBJECT_DIRECTORIES",
47
+ "GIT_CEILING_DIRECTORIES",
48
+ "GIT_COMMON_DIR",
49
+ "GIT_CONFIG_COUNT",
50
+ "GIT_CONFIG_PARAMETERS",
51
+ "GIT_DIR",
52
+ "GIT_DISCOVERY_ACROSS_FILESYSTEM",
53
+ "GIT_INDEX_FILE",
54
+ "GIT_ICASE_PATHSPECS",
55
+ "GIT_GLOB_PATHSPECS",
56
+ "GIT_LITERAL_PATHSPECS",
57
+ "GIT_NOGLOB_PATHSPECS",
58
+ "GIT_OBJECT_DIRECTORY",
59
+ "GIT_PREFIX",
60
+ "GIT_WORK_TREE"
61
+ ]);
28
62
  function getCredentialsPath() {
29
63
  return join(homedir(), DIR_NAME, FILE_NAME);
30
64
  }
@@ -32,19 +66,12 @@ function loadCredentials() {
32
66
  return loadCredentialsFrom(getCredentialsPath());
33
67
  }
34
68
  function saveCredentials(creds) {
35
- const dir = join(homedir(), DIR_NAME);
36
- if (!existsSync(dir)) {
37
- mkdirSync(dir, { recursive: true, mode: 448 });
38
- }
39
69
  const filePath = getCredentialsPath();
40
70
  const data = {
41
71
  ...creds,
42
72
  storedAt: (/* @__PURE__ */ new Date()).toISOString()
43
73
  };
44
- writeFileSync(filePath, JSON.stringify(data, null, 2), {
45
- encoding: "utf-8",
46
- mode: 384
47
- });
74
+ writeCredentialFile(filePath, JSON.stringify(data, null, 2));
48
75
  }
49
76
  function deleteCredentials() {
50
77
  const filePath = getCredentialsPath();
@@ -58,21 +85,19 @@ function getLocalCredentialsPath() {
58
85
  function loadLocalCredentials() {
59
86
  return loadCredentialsFrom(getLocalCredentialsPath());
60
87
  }
61
- function saveLocalCredentials(creds) {
62
- const dir = join(process.cwd(), DIR_NAME);
63
- if (!existsSync(dir)) {
64
- mkdirSync(dir, { recursive: true, mode: 448 });
65
- }
66
- const filePath = getLocalCredentialsPath();
88
+ function saveLocalCredentials(creds, workingDirectory = process.cwd()) {
89
+ const temporaryFileName = createTemporaryCredentialFileName();
90
+ ensureGitignore(workingDirectory, `${DIR_NAME}/${temporaryFileName}`);
91
+ const filePath = join(workingDirectory, DIR_NAME, FILE_NAME);
67
92
  const data = {
68
93
  ...creds,
69
94
  storedAt: (/* @__PURE__ */ new Date()).toISOString()
70
95
  };
71
- writeFileSync(filePath, JSON.stringify(data, null, 2), {
72
- encoding: "utf-8",
73
- mode: 384
74
- });
75
- ensureGitignore();
96
+ writeCredentialFile(
97
+ filePath,
98
+ JSON.stringify(data, null, 2),
99
+ temporaryFileName
100
+ );
76
101
  }
77
102
  function saveTenantList(tenants) {
78
103
  const dir = join(homedir(), DIR_NAME);
@@ -100,6 +125,161 @@ function loadTenantList() {
100
125
  return null;
101
126
  }
102
127
  }
128
+ function writeCredentialFile(filePath, content, temporaryFileName = createTemporaryCredentialFileName()) {
129
+ const directoryPath = dirname(filePath);
130
+ const directoryDescriptor = openPrivateCredentialDirectory(directoryPath);
131
+ const initialTarget = inspectCredentialTarget(filePath);
132
+ const tempPath = join(directoryPath, temporaryFileName);
133
+ let tempDescriptor;
134
+ try {
135
+ tempDescriptor = openSync(tempPath, "wx", PRIVATE_FILE_MODE);
136
+ const tempStats = fstatSync(tempDescriptor);
137
+ assertRegularOwnedCredentialFile(tempStats, "temporary credential file");
138
+ if (HAS_POSIX_FILE_PERMISSIONS) {
139
+ fchmodSync(tempDescriptor, PRIVATE_FILE_MODE);
140
+ assertMode(
141
+ fstatSync(tempDescriptor),
142
+ PRIVATE_FILE_MODE,
143
+ "temporary credential file"
144
+ );
145
+ }
146
+ writeFileSync(tempDescriptor, content, { encoding: "utf-8" });
147
+ fsyncSync(tempDescriptor);
148
+ closeSync(tempDescriptor);
149
+ tempDescriptor = void 0;
150
+ const currentTarget = inspectCredentialTarget(filePath);
151
+ if (!sameFileIdentity(initialTarget, currentTarget)) {
152
+ throw credentialFileProtectionError(
153
+ "credential target changed while being checked"
154
+ );
155
+ }
156
+ renameSync(tempPath, filePath);
157
+ const finalStats = lstatCredentialPath(filePath);
158
+ if (!finalStats) {
159
+ throw credentialFileProtectionError(
160
+ "credential target disappeared after atomic replacement"
161
+ );
162
+ }
163
+ assertRegularOwnedCredentialFile(finalStats, "credential target");
164
+ if (HAS_POSIX_FILE_PERMISSIONS) {
165
+ assertMode(finalStats, PRIVATE_FILE_MODE, "credential target");
166
+ if (directoryDescriptor !== void 0) {
167
+ fsyncSync(directoryDescriptor);
168
+ }
169
+ }
170
+ } catch (error) {
171
+ if (tempDescriptor !== void 0) {
172
+ try {
173
+ closeSync(tempDescriptor);
174
+ } catch {
175
+ }
176
+ }
177
+ try {
178
+ unlinkSync(tempPath);
179
+ } catch {
180
+ }
181
+ throw error;
182
+ } finally {
183
+ if (directoryDescriptor !== void 0) {
184
+ closeSync(directoryDescriptor);
185
+ }
186
+ }
187
+ }
188
+ function openPrivateCredentialDirectory(directoryPath) {
189
+ let directoryStats = lstatCredentialPath(directoryPath);
190
+ if (!directoryStats) {
191
+ try {
192
+ mkdirSync(directoryPath, { mode: PRIVATE_DIRECTORY_MODE });
193
+ if (HAS_POSIX_FILE_PERMISSIONS) {
194
+ chmodSync(directoryPath, PRIVATE_DIRECTORY_MODE);
195
+ }
196
+ } catch (error) {
197
+ if (error.code !== "EEXIST") throw error;
198
+ }
199
+ directoryStats = lstatCredentialPath(directoryPath);
200
+ }
201
+ if (!directoryStats || directoryStats.isSymbolicLink() || !directoryStats.isDirectory()) {
202
+ throw credentialFileProtectionError(
203
+ "credential directory must be a regular directory, not a symlink"
204
+ );
205
+ }
206
+ assertCurrentUserOwns(directoryStats, "credential directory");
207
+ if (!HAS_POSIX_FILE_PERMISSIONS) return void 0;
208
+ const descriptor = openSync(
209
+ directoryPath,
210
+ fsConstants.O_RDONLY | fsConstants.O_DIRECTORY | fsConstants.O_NOFOLLOW
211
+ );
212
+ try {
213
+ const openedStats = fstatSync(descriptor);
214
+ if (!openedStats.isDirectory() || openedStats.dev !== directoryStats.dev || openedStats.ino !== directoryStats.ino) {
215
+ throw credentialFileProtectionError(
216
+ "credential directory changed while being checked"
217
+ );
218
+ }
219
+ assertCurrentUserOwns(openedStats, "credential directory");
220
+ fchmodSync(descriptor, PRIVATE_DIRECTORY_MODE);
221
+ assertMode(
222
+ fstatSync(descriptor),
223
+ PRIVATE_DIRECTORY_MODE,
224
+ "credential directory"
225
+ );
226
+ return descriptor;
227
+ } catch (error) {
228
+ closeSync(descriptor);
229
+ throw error;
230
+ }
231
+ }
232
+ function inspectCredentialTarget(filePath) {
233
+ const stats = lstatCredentialPath(filePath);
234
+ if (!stats) return null;
235
+ assertRegularOwnedCredentialFile(stats, "credential target");
236
+ return { dev: stats.dev, ino: stats.ino };
237
+ }
238
+ function assertRegularOwnedCredentialFile(stats, description) {
239
+ if (stats.isSymbolicLink()) {
240
+ throw credentialFileProtectionError(`${description} must not be a symlink`);
241
+ }
242
+ if (!stats.isFile()) {
243
+ throw credentialFileProtectionError(`${description} must be a regular file`);
244
+ }
245
+ if (HAS_POSIX_FILE_PERMISSIONS && stats.nlink > 1) {
246
+ throw credentialFileProtectionError(
247
+ `${description} must not have multiple hard links`
248
+ );
249
+ }
250
+ assertCurrentUserOwns(stats, description);
251
+ }
252
+ function assertCurrentUserOwns(stats, description) {
253
+ if (CURRENT_USER_ID !== null && stats.uid !== CURRENT_USER_ID) {
254
+ throw credentialFileProtectionError(
255
+ `${description} must be owned by the current user`
256
+ );
257
+ }
258
+ }
259
+ function assertMode(stats, expectedMode, description) {
260
+ if ((stats.mode & 511) !== expectedMode) {
261
+ throw credentialFileProtectionError(
262
+ `${description} permissions could not be secured`
263
+ );
264
+ }
265
+ }
266
+ function sameFileIdentity(initial, current) {
267
+ if (!initial || !current) return initial === current;
268
+ return initial.dev === current.dev && initial.ino === current.ino;
269
+ }
270
+ function lstatCredentialPath(filePath) {
271
+ try {
272
+ return lstatSync(filePath);
273
+ } catch (error) {
274
+ if (error.code === "ENOENT") return null;
275
+ throw credentialFileProtectionError(
276
+ "credential filesystem target could not be inspected"
277
+ );
278
+ }
279
+ }
280
+ function credentialFileProtectionError(message) {
281
+ return new Error(`Cannot save credentials: ${message}`);
282
+ }
103
283
  function loadCredentialsFrom(filePath) {
104
284
  if (!existsSync(filePath)) return null;
105
285
  try {
@@ -111,25 +291,160 @@ function loadCredentialsFrom(filePath) {
111
291
  return null;
112
292
  }
113
293
  }
114
- function ensureGitignore() {
115
- const gitignorePath = join(process.cwd(), ".gitignore");
116
- const entry = ".01software/";
117
- if (existsSync(gitignorePath)) {
118
- const content = readFileSync(gitignorePath, "utf-8");
119
- if (content.includes(entry)) return;
120
- appendFileSync(gitignorePath, `
121
- ${entry}
122
- `, "utf-8");
123
- } else {
124
- writeFileSync(gitignorePath, `${entry}
125
- `, "utf-8");
294
+ function ensureGitignore(cwd, temporaryCredentialPath) {
295
+ const gitignorePath = join(cwd, ".gitignore");
296
+ assertSafeGitignorePath(gitignorePath);
297
+ const gitState = detectGitWorkTree(cwd);
298
+ if (gitState === "inside") {
299
+ assertCredentialPathIsUntracked(cwd);
300
+ if (areCredentialPathsIgnored(cwd, temporaryCredentialPath)) return;
301
+ ensureTerminalGitignoreEntry(gitignorePath, true);
302
+ if (!areCredentialPathsIgnored(cwd, temporaryCredentialPath)) {
303
+ throw credentialProtectionError(
304
+ "Git still reports a credential path as unignored"
305
+ );
306
+ }
307
+ return;
308
+ }
309
+ ensureTerminalGitignoreEntry(gitignorePath, false);
310
+ }
311
+ function assertSafeGitignorePath(gitignorePath) {
312
+ const stats = lstatIfPresent(gitignorePath);
313
+ if (stats && (!stats.isFile() || stats.isSymbolicLink())) {
314
+ throw credentialProtectionError(".gitignore must be a regular file");
315
+ }
316
+ }
317
+ function ensureTerminalGitignoreEntry(gitignorePath, forceAppend) {
318
+ const initialStats = lstatIfPresent(gitignorePath);
319
+ if (!initialStats) {
320
+ try {
321
+ writeFileSync(gitignorePath, `${LOCAL_GITIGNORE_ENTRY}
322
+ `, {
323
+ encoding: "utf-8",
324
+ flag: "wx"
325
+ });
326
+ return;
327
+ } catch (error) {
328
+ if (error.code !== "EEXIST") throw error;
329
+ return ensureTerminalGitignoreEntry(gitignorePath, forceAppend);
330
+ }
331
+ }
332
+ if (!initialStats.isFile() || initialStats.isSymbolicLink()) {
333
+ throw credentialProtectionError(".gitignore must be a regular file");
334
+ }
335
+ const descriptor = openSync(gitignorePath, "a+");
336
+ try {
337
+ const openedStats = fstatSync(descriptor);
338
+ if (!openedStats.isFile() || openedStats.dev !== initialStats.dev || openedStats.ino !== initialStats.ino) {
339
+ throw credentialProtectionError(".gitignore changed while being checked");
340
+ }
341
+ const content = readFileSync(descriptor, "utf-8");
342
+ const lastNonEmptyLine = content.split(/\r?\n/).filter((line) => line.length > 0).at(-1);
343
+ if (!forceAppend && (lastNonEmptyLine === LOCAL_GITIGNORE_ENTRY || lastNonEmptyLine === `/${LOCAL_GITIGNORE_ENTRY}`)) {
344
+ return;
345
+ }
346
+ const separator = content.length > 0 && !content.endsWith("\n") ? "\n" : "";
347
+ appendFileSync(
348
+ descriptor,
349
+ `${separator}${LOCAL_GITIGNORE_ENTRY}
350
+ `,
351
+ "utf-8"
352
+ );
353
+ } finally {
354
+ closeSync(descriptor);
126
355
  }
127
356
  }
357
+ function detectGitWorkTree(cwd) {
358
+ const result = runGit(cwd, ["rev-parse", "--is-inside-work-tree"]);
359
+ if (result.error) {
360
+ if (result.error.code === "ENOENT" && !hasGitMarker(cwd)) {
361
+ return "outside";
362
+ }
363
+ throw credentialProtectionError("Git repository detection failed");
364
+ }
365
+ if (result.status === 0) {
366
+ if (result.stdout.trim() === "true") return "inside";
367
+ throw credentialProtectionError(
368
+ "the current directory is not a Git work tree"
369
+ );
370
+ }
371
+ if (result.status === 128 && !hasGitMarker(cwd)) return "outside";
372
+ throw credentialProtectionError("Git repository detection failed");
373
+ }
374
+ function assertCredentialPathIsUntracked(cwd) {
375
+ const result = runGit(cwd, [
376
+ "ls-files",
377
+ "-z",
378
+ "--",
379
+ `:(icase,literal)${LOCAL_CREDENTIAL_PATH}`
380
+ ]);
381
+ if (result.error || result.status !== 0) {
382
+ throw credentialProtectionError("Git tracked-file verification failed");
383
+ }
384
+ if (result.stdout.length > 0) {
385
+ throw credentialProtectionError(
386
+ ".01software/credentials.json is already tracked by Git"
387
+ );
388
+ }
389
+ }
390
+ function createTemporaryCredentialFileName() {
391
+ return `.${FILE_NAME}.${randomBytes(12).toString("hex")}.tmp`;
392
+ }
393
+ function areCredentialPathsIgnored(cwd, temporaryCredentialPath) {
394
+ return isPathIgnored(cwd, LOCAL_CREDENTIAL_PATH) && isPathIgnored(cwd, temporaryCredentialPath);
395
+ }
396
+ function isPathIgnored(cwd, relativePath) {
397
+ const result = runGit(cwd, [
398
+ "check-ignore",
399
+ "--quiet",
400
+ "--no-index",
401
+ "--",
402
+ relativePath
403
+ ]);
404
+ if (result.error || result.status !== 0 && result.status !== 1) {
405
+ throw credentialProtectionError("Git ignore verification failed");
406
+ }
407
+ return result.status === 0;
408
+ }
409
+ function runGit(cwd, args) {
410
+ const env = { ...process.env, LC_ALL: "C" };
411
+ for (const key of Object.keys(env)) {
412
+ const normalizedKey = key.toUpperCase();
413
+ if (SANITIZED_GIT_ENVIRONMENT_VARIABLES.has(normalizedKey) || normalizedKey.startsWith("GIT_CONFIG_KEY_") || normalizedKey.startsWith("GIT_CONFIG_VALUE_")) {
414
+ delete env[key];
415
+ }
416
+ }
417
+ return spawnSync("git", args, {
418
+ cwd,
419
+ encoding: "utf-8",
420
+ env
421
+ });
422
+ }
423
+ function hasGitMarker(startPath) {
424
+ let currentPath = startPath;
425
+ while (true) {
426
+ if (lstatIfPresent(join(currentPath, ".git"))) return true;
427
+ const parentPath = dirname(currentPath);
428
+ if (parentPath === currentPath) return false;
429
+ currentPath = parentPath;
430
+ }
431
+ }
432
+ function lstatIfPresent(filePath) {
433
+ try {
434
+ return lstatSync(filePath);
435
+ } catch (error) {
436
+ if (error.code === "ENOENT") return null;
437
+ throw credentialProtectionError("local Git metadata could not be inspected");
438
+ }
439
+ }
440
+ function credentialProtectionError(message) {
441
+ return new Error(`Cannot save local credentials: ${message}`);
442
+ }
128
443
 
129
444
  // src/lib/output.ts
130
445
  import pc from "picocolors";
131
446
 
132
- // ../errors/src/admin-error.ts
447
+ // ../../internal/errors/src/admin-error.ts
133
448
  var ADMIN_ERROR_CODES = {
134
449
  permission: [
135
450
  "tenant_mismatch",
@@ -268,7 +583,7 @@ var CLI_I18N_KO = {
268
583
  RetiredHexCredentialsRejected: "\uC774\uC804 hex \uC790\uACA9 \uC99D\uBA85\uC740 \uB354 \uC774\uC0C1 \uD5C8\uC6A9\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2014 `01 login`\uC73C\uB85C \uB2E4\uC2DC \uC778\uC99D\uD558\uC138\uC694.",
269
584
  InvalidJsonObject: "`--{{label}}` \uC778\uC790\uB294 JSON \uAC1D\uCCB4\uC5EC\uC57C \uD569\uB2C8\uB2E4.",
270
585
  InvalidJsonArray: "`--{{label}}` \uC778\uC790\uB294 JSON \uBC30\uC5F4\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4.",
271
- InvalidJsonValue: "`--{{label}}` \uC778\uC790\uC758 JSON \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4: {{value}}",
586
+ InvalidJsonValue: "`--{{label}}` \uC778\uC790\uC758 JSON \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
272
587
  Empty: "(\uC5C6\uC74C)",
273
588
  Total: "\uCD1D {{n}}\uAC1C",
274
589
  PageOf: "{{page}} / {{total}} \uD398\uC774\uC9C0",
@@ -300,7 +615,7 @@ var CLI_I18N_EN = {
300
615
  RetiredHexCredentialsRejected: "Retired hex credentials are no longer accepted \u2014 run `01 login` to re-authenticate.",
301
616
  InvalidJsonObject: "--{{label}} must be a JSON object.",
302
617
  InvalidJsonArray: "--{{label}} must be a JSON array.",
303
- InvalidJsonValue: "Invalid JSON for --{{label}}: {{value}}",
618
+ InvalidJsonValue: "Invalid JSON for --{{label}}.",
304
619
  Empty: "(empty)",
305
620
  Total: "{{n}} total",
306
621
  PageOf: "page {{page}}/{{total}}",
@@ -552,40 +867,24 @@ function parseSelect(input) {
552
867
  }
553
868
 
554
869
  // src/lib/parse.ts
555
- function failArg(label, value, expectedKind) {
556
- const message = expectedKind === "object" ? t("InvalidJsonObject", { label }) : expectedKind === "array" ? t("InvalidJsonArray", { label }) : t("InvalidJsonValue", { label, value });
870
+ function failArg(label, _value, expectedKind) {
871
+ const message = expectedKind === "object" ? t("InvalidJsonObject", { label }) : expectedKind === "array" ? t("InvalidJsonArray", { label }) : t("InvalidJsonValue", { label });
557
872
  exitWithError({
558
873
  type: "validation",
559
874
  code: "invalid_argument",
560
875
  field: label,
561
- // `message` is read by `printError` for the stderr headline so the
562
- // user sees the label/value in the one-line summary; `detail` carries
563
- // the structured fields for `--format json` consumers.
876
+ // Keep rejected values out of terminal and structured output. `message`
877
+ // is the stderr headline; `detail` carries only the stable field label for
878
+ // machine consumers.
564
879
  message,
565
- detail: { message, value, field: label }
880
+ detail: { message, field: label }
566
881
  });
567
882
  }
568
- function stringifyValue(value) {
569
- try {
570
- return JSON.stringify(value);
571
- } catch {
572
- return String(value);
573
- }
574
- }
575
- function formatSchemaIssues(label, issues) {
576
- return issues.map((issue) => {
577
- const path = issue.path.length > 0 ? issue.path.join(".") : label;
578
- return `${path}: ${issue.message}`;
579
- }).join("; ");
580
- }
581
883
  function parseWithSchema(value, label, schema) {
582
884
  const parsed = schema.safeParse(value);
583
885
  if (parsed.success) return parsed.data;
584
- const issues = parsed.error.issues.map((issue) => ({
585
- path: issue.path.map(String),
586
- message: issue.message
587
- }));
588
- const message = `Invalid value for --${label}: ${formatSchemaIssues(label, issues)}`;
886
+ const issues = parsed.error.issues.map((issue) => ({ code: issue.code }));
887
+ const message = `Invalid value for --${label}.`;
589
888
  exitWithError({
590
889
  type: "validation",
591
890
  code: "invalid_argument",
@@ -593,7 +892,6 @@ function parseWithSchema(value, label, schema) {
593
892
  message,
594
893
  detail: {
595
894
  message,
596
- value: stringifyValue(value),
597
895
  field: label,
598
896
  issues
599
897
  }
@@ -804,7 +1102,7 @@ function registerCrudCommands(program2, getClient2, getFormat2) {
804
1102
  });
805
1103
  }
806
1104
 
807
- // ../contracts/src/tenant/index.ts
1105
+ // ../../internal/contracts/src/tenant/index.ts
808
1106
  import { z } from "zod";
809
1107
  var tenantFieldConfigStateSchema = z.object({
810
1108
  hiddenFields: z.array(z.string()),
@@ -953,10 +1251,10 @@ var collectionSchemaResponseSchema = z.object({
953
1251
  }).strict()
954
1252
  }).strict();
955
1253
 
956
- // ../contracts/src/ecommerce/index.ts
957
- import { z as z4 } from "zod";
1254
+ // ../../internal/contracts/src/ecommerce/index.ts
1255
+ import { z as z7 } from "zod";
958
1256
 
959
- // ../contracts/src/ecommerce/product-upsert.ts
1257
+ // ../../internal/contracts/src/ecommerce/product-upsert.ts
960
1258
  import { z as z2 } from "zod";
961
1259
  var IdSchema = z2.union([z2.string(), z2.number()]).transform(String);
962
1260
  var RemovedLegacyMediaFieldSchema = z2.unknown().optional();
@@ -980,6 +1278,7 @@ var productFieldShape = {
980
1278
  publishedAt: z2.string().optional().nullable(),
981
1279
  categories: z2.array(IdSchema).optional(),
982
1280
  tags: z2.array(IdSchema).optional(),
1281
+ markets: z2.array(IdSchema).optional().nullable(),
983
1282
  seo: z2.object({
984
1283
  title: z2.string().optional().nullable(),
985
1284
  description: z2.string().optional().nullable()
@@ -1035,6 +1334,26 @@ var VariantOptionValueObjectSchema = z2.object({
1035
1334
  }).refine((data) => Boolean(data.valueSlug ?? data.valueId ?? data.value), {
1036
1335
  message: "Variant option value object requires valueSlug, valueId, or value"
1037
1336
  });
1337
+ var MAX_CATALOG_AMOUNT_DECIMAL_PLACES = 2;
1338
+ function catalogAmountDecimalPlaces(value) {
1339
+ if (!Number.isFinite(value)) return 0;
1340
+ const text = String(value);
1341
+ const exponential = text.indexOf("e-");
1342
+ if (exponential !== -1) return Number(text.slice(exponential + 2)) || 0;
1343
+ const dot = text.indexOf(".");
1344
+ return dot === -1 ? 0 : text.length - dot - 1;
1345
+ }
1346
+ var CatalogAmountSchema = z2.number().min(0).refine(
1347
+ (value) => catalogAmountDecimalPlaces(value) <= MAX_CATALOG_AMOUNT_DECIMAL_PLACES,
1348
+ {
1349
+ message: `Amount must have at most ${MAX_CATALOG_AMOUNT_DECIMAL_PLACES} decimal places; the store currency may allow fewer.`
1350
+ }
1351
+ );
1352
+ var VariantMarketPriceInputSchema = z2.object({
1353
+ market: IdSchema,
1354
+ price: CatalogAmountSchema,
1355
+ compareAtPrice: CatalogAmountSchema.optional().nullable()
1356
+ });
1038
1357
  var VariantInputSchema = z2.object({
1039
1358
  id: IdSchema.optional(),
1040
1359
  optionValues: z2.union([
@@ -1046,8 +1365,8 @@ var VariantInputSchema = z2.object({
1046
1365
  ]).optional(),
1047
1366
  sku: z2.string().optional().nullable(),
1048
1367
  title: z2.string().optional().nullable(),
1049
- price: z2.number().min(0),
1050
- compareAtPrice: z2.number().min(0).optional().nullable(),
1368
+ price: CatalogAmountSchema,
1369
+ compareAtPrice: CatalogAmountSchema.optional().nullable(),
1051
1370
  stock: z2.number().int().min(0).optional(),
1052
1371
  isUnlimited: z2.boolean().optional(),
1053
1372
  inventoryPolicy: z2.enum(["deny", "continue"]).optional(),
@@ -1057,6 +1376,7 @@ var VariantInputSchema = z2.object({
1057
1376
  externalId: z2.string().optional().nullable(),
1058
1377
  isActive: z2.boolean().optional(),
1059
1378
  images: z2.array(IdSchema).optional(),
1379
+ marketPrices: z2.array(VariantMarketPriceInputSchema).optional(),
1060
1380
  thumbnail: RemovedLegacyMediaFieldSchema,
1061
1381
  featuredMediaItemId: RemovedLegacyMediaFieldSchema,
1062
1382
  metadata: z2.unknown().optional()
@@ -1109,260 +1429,1035 @@ var ProductUpsertSchema = ProductUpsertObjectSchema.superRefine(
1109
1429
  }
1110
1430
  );
1111
1431
 
1112
- // ../contracts/src/ecommerce/checkout.ts
1432
+ // ../../internal/contracts/src/ecommerce/cart.ts
1433
+ import { z as z4 } from "zod";
1434
+
1435
+ // ../../internal/contracts/src/ecommerce/commerce-common.ts
1113
1436
  import { z as z3 } from "zod";
1114
- var providerSlugSchema = z3.string().trim().regex(/^[a-z0-9][a-z0-9_-]{0,63}$/, "pgProvider must be lowercase slug");
1115
- var CheckoutShippingAddressSchema = z3.object({
1116
- recipientName: z3.string().min(1),
1117
- phone: z3.string().min(1),
1118
- postalCode: z3.string().min(1),
1119
- address: z3.string().min(1),
1120
- detailAddress: z3.string().min(1),
1121
- deliveryMessage: z3.string().optional()
1437
+ var currencyCodeSchema = z3.string().regex(
1438
+ /^[A-Z]{3}$/,
1439
+ "currency must be a three-letter uppercase ISO 4217 code"
1440
+ );
1441
+ var minorAmountSchema = z3.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER);
1442
+ var moneySchema = z3.object({
1443
+ amountMinor: minorAmountSchema,
1444
+ currency: currencyCodeSchema
1445
+ }).strict();
1446
+ var MAX_COMMERCE_EXTENSION_ENTRIES = 50;
1447
+ var MAX_COMMERCE_EXTENSION_KEY_LENGTH = 40;
1448
+ var MAX_COMMERCE_EXTENSION_VALUE_LENGTH = 500;
1449
+ var MAX_COMMERCE_RESOURCE_ID_LENGTH = 255;
1450
+ var MAX_COMMERCE_CAPABILITY_LENGTH = 512;
1451
+ var MAX_COMMERCE_CUSTOMER_TOKEN_LENGTH = 4096;
1452
+ var MAX_COMMERCE_SHORT_TEXT_LENGTH = 200;
1453
+ var MAX_COMMERCE_ADDRESS_LENGTH = 500;
1454
+ var MAX_COMMERCE_EMAIL_LENGTH = 254;
1455
+ var MAX_COMMERCE_PHONE_LENGTH = 32;
1456
+ var MAX_COMMERCE_POSTAL_CODE_LENGTH = 32;
1457
+ var commerceResourceIdSchema = z3.string().min(1).max(MAX_COMMERCE_RESOURCE_ID_LENGTH);
1458
+ var commerceCapabilitySchema = z3.string().min(1).max(MAX_COMMERCE_CAPABILITY_LENGTH);
1459
+ var commerceCustomerTokenSchema = z3.string().min(1).max(MAX_COMMERCE_CUSTOMER_TOKEN_LENGTH);
1460
+ var publicAttributeKeySchema = z3.string().min(1).max(MAX_COMMERCE_EXTENSION_KEY_LENGTH).refine((key) => !key.startsWith("__"), {
1461
+ message: "attribute key uses a reserved platform prefix"
1462
+ });
1463
+ var publicAttributeSchema = z3.object({
1464
+ key: publicAttributeKeySchema,
1465
+ value: z3.string().max(MAX_COMMERCE_EXTENSION_VALUE_LENGTH)
1466
+ }).strict();
1467
+ var publicAttributesSchema = z3.array(publicAttributeSchema).max(MAX_COMMERCE_EXTENSION_ENTRIES).superRefine((attributes, ctx) => {
1468
+ const seen = /* @__PURE__ */ new Set();
1469
+ attributes.forEach(({ key }, index) => {
1470
+ if (seen.has(key)) {
1471
+ ctx.addIssue({
1472
+ code: "custom",
1473
+ path: [index, "key"],
1474
+ message: "attribute keys must be unique"
1475
+ });
1476
+ }
1477
+ seen.add(key);
1478
+ });
1122
1479
  });
1123
- var CheckoutCustomerSnapshotSchema = z3.object({
1124
- name: z3.string().optional(),
1125
- email: z3.string().email("Invalid email format"),
1126
- phone: z3.string().optional()
1480
+ var integrationMetadataKeySchema = z3.string().min(1).max(MAX_COMMERCE_EXTENSION_KEY_LENGTH).refine((key) => !key.includes("[") && !key.includes("]"), {
1481
+ message: "integration metadata keys must not use bracket notation"
1127
1482
  });
1128
- var CheckoutRequestSchema = z3.object({
1129
- cartId: z3.union([z3.string(), z3.number()]).transform(String),
1130
- cartToken: z3.string().min(1).optional(),
1131
- pgPaymentId: z3.string().min(1).optional(),
1132
- pgProvider: providerSlugSchema.optional(),
1133
- orderNumber: z3.string().min(1, "orderNumber is required"),
1134
- customerSnapshot: CheckoutCustomerSnapshotSchema,
1135
- shippingAddress: CheckoutShippingAddressSchema.optional(),
1136
- discountCode: z3.string().optional()
1137
- }).superRefine((data, ctx) => {
1138
- if (data.pgPaymentId && !data.pgProvider) {
1483
+ var integrationMetadataSchema = z3.record(
1484
+ integrationMetadataKeySchema,
1485
+ z3.string().max(MAX_COMMERCE_EXTENSION_VALUE_LENGTH)
1486
+ ).superRefine((metadata, ctx) => {
1487
+ if (Object.keys(metadata).length > MAX_COMMERCE_EXTENSION_ENTRIES) {
1139
1488
  ctx.addIssue({
1140
- code: z3.ZodIssueCode.custom,
1141
- path: ["pgProvider"],
1142
- message: "pgProvider is required when pgPaymentId is provided"
1489
+ code: "too_big",
1490
+ origin: "object",
1491
+ maximum: MAX_COMMERCE_EXTENSION_ENTRIES,
1492
+ inclusive: true,
1493
+ message: `integration metadata cannot contain more than ${MAX_COMMERCE_EXTENSION_ENTRIES} entries`
1143
1494
  });
1144
1495
  }
1145
- if (data.pgProvider && !data.pgPaymentId) {
1496
+ });
1497
+ var customerSnapshotSchema = z3.object({
1498
+ email: z3.string().email().max(MAX_COMMERCE_EMAIL_LENGTH),
1499
+ name: z3.string().min(1).max(MAX_COMMERCE_SHORT_TEXT_LENGTH).optional(),
1500
+ phone: z3.string().min(1).max(MAX_COMMERCE_PHONE_LENGTH).optional()
1501
+ }).strict();
1502
+ var checkoutShippingAddressSchema = z3.object({
1503
+ recipientName: z3.string().min(1).max(MAX_COMMERCE_SHORT_TEXT_LENGTH),
1504
+ phone: z3.string().min(1).max(MAX_COMMERCE_PHONE_LENGTH),
1505
+ countryCode: z3.string().trim().length(2).optional(),
1506
+ regionCode: z3.string().trim().min(1).max(64).optional(),
1507
+ postalCode: z3.string().min(1).max(MAX_COMMERCE_POSTAL_CODE_LENGTH),
1508
+ address: z3.string().min(1).max(MAX_COMMERCE_ADDRESS_LENGTH),
1509
+ detailAddress: z3.string().min(1).max(MAX_COMMERCE_ADDRESS_LENGTH),
1510
+ deliveryMessage: z3.string().max(MAX_COMMERCE_EXTENSION_VALUE_LENGTH).optional()
1511
+ }).strict();
1512
+ var commerceIdempotencyKeySchema = z3.string().max(255).refine(
1513
+ (value) => /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(
1514
+ value
1515
+ ) || /^ik_[A-Za-z0-9_-]{22}$/.test(value),
1516
+ "idempotencyKey must be a UUIDv4 or ik_-prefixed 128-bit base64url value"
1517
+ );
1518
+
1519
+ // ../../internal/contracts/src/ecommerce/cart.ts
1520
+ var MAX_CART_LINE_QUANTITY = 99;
1521
+ var MAX_CART_DISCOUNT_CODES = 250;
1522
+ var MAX_CART_DISCOUNT_CODE_LENGTH = 128;
1523
+ var cartDiscountCodeSchema = z4.object({
1524
+ code: z4.string().min(1).max(MAX_CART_DISCOUNT_CODE_LENGTH),
1525
+ applicable: z4.boolean()
1526
+ }).strict();
1527
+ var CART_WARNING_CODES = [
1528
+ "DISCOUNT_CODE_NOT_HONOURED",
1529
+ "DISCOUNT_CURRENTLY_INACTIVE",
1530
+ "DISCOUNT_CUSTOMER_NOT_ELIGIBLE",
1531
+ "DISCOUNT_CUSTOMER_USAGE_LIMIT_REACHED",
1532
+ "DISCOUNT_ELIGIBLE_CUSTOMER_MISSING",
1533
+ "DISCOUNT_NO_ENTITLED_LINE_ITEMS",
1534
+ "DISCOUNT_NO_ENTITLED_SHIPPING_LINES",
1535
+ "DISCOUNT_NOT_FOUND",
1536
+ "DISCOUNT_PURCHASE_NOT_IN_RANGE",
1537
+ "DISCOUNT_USAGE_LIMIT_REACHED",
1538
+ "MERCHANDISE_NOT_ENOUGH_STOCK",
1539
+ "MERCHANDISE_OUT_OF_STOCK"
1540
+ ];
1541
+ var cartWarningCodeSchema = z4.enum(CART_WARNING_CODES);
1542
+ var cartWarningSchema = z4.object({
1543
+ code: cartWarningCodeSchema,
1544
+ message: z4.string().min(1),
1545
+ target: z4.string().min(1)
1546
+ }).strict();
1547
+ var updateDiscountCodesBodySchema = z4.object({
1548
+ cartToken: commerceCapabilitySchema,
1549
+ expectedRevision: z4.number().int().nonnegative(),
1550
+ discountCodes: z4.array(z4.string().trim().min(1).max(MAX_CART_DISCOUNT_CODE_LENGTH)).max(
1551
+ MAX_CART_DISCOUNT_CODES,
1552
+ `discountCodes cannot contain more than ${MAX_CART_DISCOUNT_CODES} values`
1553
+ )
1554
+ }).strict();
1555
+ var updateDiscountCodesParamsSchema = updateDiscountCodesBodySchema.extend({
1556
+ idempotencyKey: commerceIdempotencyKeySchema
1557
+ });
1558
+ var MAX_CART_LINE_ADMISSIONS = 32;
1559
+ var publicMediaSizeSchema = z4.object({
1560
+ width: z4.number().nullable().optional(),
1561
+ height: z4.number().nullable().optional(),
1562
+ mimeType: z4.string().nullable().optional(),
1563
+ filename: z4.string().nullable().optional(),
1564
+ url: z4.string().nullable().optional()
1565
+ }).strict();
1566
+ var publicMediaAssetSchema = z4.union([
1567
+ z4.string(),
1568
+ z4.object({
1569
+ id: z4.union([z4.string(), z4.number()]).nullable().optional(),
1570
+ alt: z4.string().nullable().optional(),
1571
+ width: z4.number().nullable().optional(),
1572
+ height: z4.number().nullable().optional(),
1573
+ mimeType: z4.string().nullable().optional(),
1574
+ filename: z4.string().nullable().optional(),
1575
+ url: z4.string().nullable().optional(),
1576
+ sizes: z4.record(z4.string(), publicMediaSizeSchema).optional()
1577
+ }).strict()
1578
+ ]);
1579
+ var publicCartReferenceSchema = z4.object({
1580
+ id: z4.string().min(1),
1581
+ title: z4.string().min(1),
1582
+ handle: z4.string().min(1).nullable(),
1583
+ image: publicMediaAssetSchema.nullable()
1584
+ }).strict();
1585
+ var publicCartMarketSchema = z4.object({
1586
+ id: z4.string().min(1),
1587
+ handle: z4.string().min(1),
1588
+ name: z4.string().min(1)
1589
+ }).strict();
1590
+ var publicCartItemAdmissionSchema = z4.object({
1591
+ eventId: z4.string().min(1),
1592
+ occurrenceId: z4.string().min(1),
1593
+ admissionTypeId: z4.string().min(1)
1594
+ }).strict();
1595
+ var cartLineAdmissionsInputSchema = z4.array(publicCartItemAdmissionSchema).max(
1596
+ MAX_CART_LINE_ADMISSIONS,
1597
+ `admissions cannot contain more than ${MAX_CART_LINE_ADMISSIONS} values`
1598
+ ).min(1, "admissions must contain at least one value").superRefine((admissions, ctx) => {
1599
+ const seen = /* @__PURE__ */ new Set();
1600
+ admissions.forEach((admission, index) => {
1601
+ const key = `${admission.eventId}:${admission.occurrenceId}:${admission.admissionTypeId}`;
1602
+ if (seen.has(key)) {
1603
+ ctx.addIssue({
1604
+ code: "custom",
1605
+ path: [index],
1606
+ message: "admissions must be unique"
1607
+ });
1608
+ }
1609
+ seen.add(key);
1610
+ });
1611
+ });
1612
+ var cartTokenField = z4.string().min(1, "cartToken is required");
1613
+ var cartMarketField = z4.string().trim().min(1).optional();
1614
+ var cartEntityIdField = z4.union([z4.string(), z4.number()]).transform(String);
1615
+ var createCartSchema = z4.object({
1616
+ market: cartMarketField,
1617
+ attributes: publicAttributesSchema.optional()
1618
+ }).strict();
1619
+ var getCartSchema = z4.object({ cartToken: cartTokenField }).strict();
1620
+ var addCartItemSchema = z4.object({
1621
+ cartToken: cartTokenField,
1622
+ product: cartEntityIdField,
1623
+ variant: cartEntityIdField,
1624
+ option: cartEntityIdField.optional(),
1625
+ quantity: z4.number().int().positive("quantity must be a positive integer"),
1626
+ market: cartMarketField,
1627
+ admissions: cartLineAdmissionsInputSchema.optional()
1628
+ }).strict();
1629
+ var updateCartItemSchema = z4.object({
1630
+ cartToken: cartTokenField,
1631
+ cartItemId: cartEntityIdField,
1632
+ quantity: z4.number().int().positive("quantity must be a positive integer")
1633
+ }).strict();
1634
+ var removeCartItemSchema = z4.object({
1635
+ cartToken: cartTokenField,
1636
+ cartItemId: cartEntityIdField
1637
+ }).strict();
1638
+ var updateCartAttributesSchema = z4.object({
1639
+ cartToken: cartTokenField,
1640
+ attributes: publicAttributesSchema
1641
+ }).strict();
1642
+ var clearCartSchema = z4.object({ cartToken: cartTokenField }).strict();
1643
+ function compareAdmissions(left, right) {
1644
+ for (const field of ["eventId", "occurrenceId", "admissionTypeId"]) {
1645
+ if (left[field] < right[field]) return -1;
1646
+ if (left[field] > right[field]) return 1;
1647
+ }
1648
+ return 0;
1649
+ }
1650
+ var publicCartItemAdmissionsSchema = z4.array(publicCartItemAdmissionSchema).max(MAX_CART_LINE_ADMISSIONS).superRefine((admissions, ctx) => {
1651
+ let previous;
1652
+ admissions.forEach((admission, index) => {
1653
+ const comparison = previous ? compareAdmissions(admission, previous) : void 0;
1654
+ if (comparison !== void 0 && comparison <= 0) {
1655
+ ctx.addIssue({
1656
+ code: "custom",
1657
+ path: [index],
1658
+ message: comparison === 0 ? "admissions must be unique" : "admissions must be in canonical order"
1659
+ });
1660
+ }
1661
+ previous = admission;
1662
+ });
1663
+ });
1664
+ var publicCartItemSchema = z4.object({
1665
+ id: z4.string().min(1),
1666
+ product: publicCartReferenceSchema,
1667
+ variant: publicCartReferenceSchema,
1668
+ admissions: publicCartItemAdmissionsSchema,
1669
+ quantity: z4.number().int().positive().max(MAX_CART_LINE_QUANTITY),
1670
+ unitPriceMinor: minorAmountSchema,
1671
+ discountMinor: minorAmountSchema,
1672
+ estimatedLineTotalMinor: minorAmountSchema,
1673
+ requiresShipping: z4.boolean()
1674
+ }).strict().superRefine((item, ctx) => {
1675
+ const expectedLineTotal = BigInt(item.unitPriceMinor) * BigInt(item.quantity) - BigInt(item.discountMinor);
1676
+ if (expectedLineTotal < 0n) {
1146
1677
  ctx.addIssue({
1147
- code: z3.ZodIssueCode.custom,
1148
- path: ["pgPaymentId"],
1149
- message: "pgPaymentId is required when pgProvider is provided"
1678
+ code: "custom",
1679
+ path: ["discountMinor"],
1680
+ message: "discountMinor cannot exceed the undiscounted line total"
1681
+ });
1682
+ return;
1683
+ }
1684
+ if (BigInt(item.estimatedLineTotalMinor) !== expectedLineTotal) {
1685
+ ctx.addIssue({
1686
+ code: "custom",
1687
+ path: ["estimatedLineTotalMinor"],
1688
+ message: "estimatedLineTotalMinor must equal unitPriceMinor * quantity - discountMinor"
1150
1689
  });
1151
1690
  }
1152
1691
  });
1692
+ var publicCartSchema = z4.object({
1693
+ id: z4.string().min(1),
1694
+ status: z4.enum(["active", "completed", "abandoned"]),
1695
+ revision: z4.number().int().nonnegative(),
1696
+ currency: currencyCodeSchema,
1697
+ market: publicCartMarketSchema,
1698
+ subtotalMinor: minorAmountSchema,
1699
+ discountMinor: minorAmountSchema,
1700
+ estimatedTotalMinor: minorAmountSchema,
1701
+ discountCodes: z4.array(cartDiscountCodeSchema),
1702
+ attributes: publicAttributesSchema,
1703
+ items: z4.array(publicCartItemSchema),
1704
+ expiresAt: z4.string().datetime({ offset: true }),
1705
+ createdAt: z4.string().datetime({ offset: true }),
1706
+ updatedAt: z4.string().datetime({ offset: true })
1707
+ }).strict().superRefine((cart, ctx) => {
1708
+ const expectedSubtotal = cart.items.reduce(
1709
+ (total, item) => total + BigInt(item.unitPriceMinor) * BigInt(item.quantity),
1710
+ 0n
1711
+ );
1712
+ const expectedTotal = expectedSubtotal - BigInt(cart.discountMinor);
1713
+ const expectedDiscount = cart.items.reduce(
1714
+ (total, item) => total + BigInt(item.discountMinor),
1715
+ 0n
1716
+ );
1717
+ if (BigInt(cart.subtotalMinor) !== expectedSubtotal) {
1718
+ ctx.addIssue({
1719
+ code: "custom",
1720
+ path: ["subtotalMinor"],
1721
+ message: "subtotalMinor must equal the sum of undiscounted cart line totals"
1722
+ });
1723
+ }
1724
+ if (BigInt(cart.discountMinor) !== expectedDiscount) {
1725
+ ctx.addIssue({
1726
+ code: "custom",
1727
+ path: ["discountMinor"],
1728
+ message: "discountMinor must equal the sum of cart line discounts"
1729
+ });
1730
+ }
1731
+ if (expectedTotal < 0n) {
1732
+ ctx.addIssue({
1733
+ code: "custom",
1734
+ path: ["discountMinor"],
1735
+ message: "discountMinor cannot exceed subtotalMinor"
1736
+ });
1737
+ } else if (BigInt(cart.estimatedTotalMinor) !== expectedTotal) {
1738
+ ctx.addIssue({
1739
+ code: "custom",
1740
+ path: ["estimatedTotalMinor"],
1741
+ message: "estimatedTotalMinor must equal subtotalMinor - discountMinor"
1742
+ });
1743
+ }
1744
+ });
1745
+ var cartMutationResultSchema = z4.object({
1746
+ cart: publicCartSchema,
1747
+ warnings: z4.array(cartWarningSchema)
1748
+ }).strict();
1749
+ var createCartParamsSchema = z4.object({
1750
+ market: commerceResourceIdSchema.optional(),
1751
+ attributes: publicAttributesSchema.optional()
1752
+ }).strict();
1753
+ var createCartResultSchema = z4.object({
1754
+ cart: publicCartSchema,
1755
+ warnings: z4.array(cartWarningSchema),
1756
+ cartToken: commerceCapabilitySchema
1757
+ }).strict().superRefine((result, ctx) => {
1758
+ if (result.cart.revision !== 0) {
1759
+ ctx.addIssue({
1760
+ code: "custom",
1761
+ path: ["cart", "revision"],
1762
+ message: "a newly created Cart must start at revision 0"
1763
+ });
1764
+ }
1765
+ });
1766
+ var retrieveCartParamsSchema = z4.object({ cartToken: commerceCapabilitySchema }).strict();
1767
+ var addItemAdmissionsSchema = z4.tuple([publicCartItemAdmissionSchema], publicCartItemAdmissionSchema).readonly().superRefine((admissions, ctx) => {
1768
+ if (admissions.length > MAX_CART_LINE_ADMISSIONS) {
1769
+ ctx.addIssue({
1770
+ code: "too_big",
1771
+ origin: "array",
1772
+ maximum: MAX_CART_LINE_ADMISSIONS,
1773
+ inclusive: true,
1774
+ message: `admissions cannot contain more than ${MAX_CART_LINE_ADMISSIONS} values`
1775
+ });
1776
+ }
1777
+ admissions.forEach((admission, index) => {
1778
+ if (index === 0) return;
1779
+ const comparison = compareAdmissions(admission, admissions[index - 1]);
1780
+ if (comparison <= 0) {
1781
+ ctx.addIssue({
1782
+ code: "custom",
1783
+ path: [index],
1784
+ message: comparison === 0 ? "admissions must be unique" : "admissions must be in canonical order"
1785
+ });
1786
+ }
1787
+ });
1788
+ });
1789
+ var addItemBodySchema = z4.object({
1790
+ cartToken: commerceCapabilitySchema,
1791
+ expectedRevision: z4.number().int().nonnegative(),
1792
+ productId: commerceResourceIdSchema,
1793
+ variantId: commerceResourceIdSchema,
1794
+ quantity: z4.number().int().positive().max(MAX_CART_LINE_QUANTITY),
1795
+ admissions: addItemAdmissionsSchema.optional()
1796
+ }).strict();
1797
+ var addItemParamsSchema = addItemBodySchema.extend({
1798
+ idempotencyKey: commerceIdempotencyKeySchema
1799
+ });
1800
+ var updateItemBodySchema = z4.object({
1801
+ cartToken: commerceCapabilitySchema,
1802
+ expectedRevision: z4.number().int().nonnegative(),
1803
+ cartItemId: commerceResourceIdSchema,
1804
+ quantity: z4.number().int().positive().max(MAX_CART_LINE_QUANTITY)
1805
+ }).strict();
1806
+ var updateItemParamsSchema = updateItemBodySchema.extend({
1807
+ idempotencyKey: commerceIdempotencyKeySchema
1808
+ });
1809
+ var removeItemBodySchema = z4.object({
1810
+ cartToken: commerceCapabilitySchema,
1811
+ expectedRevision: z4.number().int().nonnegative(),
1812
+ cartItemId: commerceResourceIdSchema
1813
+ }).strict();
1814
+ var removeItemParamsSchema = removeItemBodySchema.extend({
1815
+ idempotencyKey: commerceIdempotencyKeySchema
1816
+ });
1817
+ var clearCartBodySchema = z4.object({
1818
+ cartToken: commerceCapabilitySchema,
1819
+ expectedRevision: z4.number().int().nonnegative()
1820
+ }).strict();
1821
+ var clearCartParamsSchema = clearCartBodySchema.extend({
1822
+ idempotencyKey: commerceIdempotencyKeySchema
1823
+ });
1824
+ var updateCartAttributesBodySchema = z4.object({
1825
+ cartToken: commerceCapabilitySchema,
1826
+ expectedRevision: z4.number().int().nonnegative(),
1827
+ attributes: publicAttributesSchema
1828
+ }).strict();
1829
+ var updateCartAttributesParamsSchema = updateCartAttributesBodySchema.extend({
1830
+ idempotencyKey: commerceIdempotencyKeySchema
1831
+ });
1153
1832
 
1154
- // ../contracts/src/ecommerce/index.ts
1155
- var transactionStatusSchema = z4.enum([
1156
- "pending",
1157
- "paid",
1158
- "failed",
1159
- "canceled",
1160
- "refunded"
1161
- ]);
1162
- var financialStatusSchema = z4.enum([
1163
- "pending",
1164
- "paid",
1165
- "canceled",
1166
- "partially_refunded",
1167
- "refunded"
1168
- ]);
1169
- var orderDisplayFinancialStatusSchema = z4.enum([
1170
- "pending",
1171
- "paid",
1172
- "partially_refunded",
1173
- "refunded",
1174
- "voided"
1175
- ]);
1176
- var confirmationStatusSchema = z4.enum(["unconfirmed", "confirmed"]);
1177
- var fulfillmentOrderStatusSchema = z4.enum([
1178
- "open",
1179
- "in_progress",
1180
- "on_hold",
1181
- "canceled",
1182
- "closed"
1833
+ // ../../internal/contracts/src/ecommerce/checkout.ts
1834
+ import { z as z6 } from "zod";
1835
+
1836
+ // ../../internal/contracts/src/ecommerce/payment-session.ts
1837
+ import { z as z5 } from "zod";
1838
+ var tossProviderOrderIdSchema = z5.string().min(6).max(64).regex(
1839
+ /^[A-Za-z0-9_-]+$/,
1840
+ "providerOrderId may contain only ASCII letters, digits, underscores, and hyphens"
1841
+ );
1842
+ var accountAliasUuidV4Schema = z5.string().regex(
1843
+ /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,
1844
+ "providerCustomerKey must be ANONYMOUS or a UUIDv4 account alias"
1845
+ );
1846
+ var tossProviderCustomerKeySchema = z5.union([
1847
+ z5.literal("ANONYMOUS"),
1848
+ accountAliasUuidV4Schema
1183
1849
  ]);
1184
- var shipmentStatusSchema = z4.enum([
1185
- "pending",
1186
- "shipped",
1187
- "delivered",
1188
- "canceled",
1189
- "failed"
1850
+ var createPaymentSessionBaseBodyShape = {
1851
+ checkoutToken: commerceCapabilitySchema
1852
+ };
1853
+ var createTossPaymentSessionBodySchema = z5.object({
1854
+ ...createPaymentSessionBaseBodyShape,
1855
+ provider: z5.literal("toss"),
1856
+ providerOptions: z5.object({ profile: z5.literal("widget_v2") }).strict()
1857
+ }).strict();
1858
+ var createPaymentSessionParamsSchema = createTossPaymentSessionBodySchema.extend({
1859
+ idempotencyKey: commerceIdempotencyKeySchema
1860
+ });
1861
+ var retrieveBrowserPaymentSessionParamsSchema = z5.object({
1862
+ paymentSessionId: commerceResourceIdSchema,
1863
+ checkoutToken: commerceCapabilitySchema
1864
+ }).strict();
1865
+ var retrieveServerPaymentSessionParamsSchema = z5.object({ paymentSessionId: commerceResourceIdSchema }).strict();
1866
+ var cancelBrowserPaymentSessionBodySchema = z5.object({
1867
+ paymentSessionId: commerceResourceIdSchema,
1868
+ checkoutToken: commerceCapabilitySchema
1869
+ }).strict();
1870
+ var cancelBrowserPaymentSessionParamsSchema = cancelBrowserPaymentSessionBodySchema.extend({
1871
+ idempotencyKey: commerceIdempotencyKeySchema
1872
+ });
1873
+ var cancelServerPaymentSessionBodySchema = z5.object({
1874
+ paymentSessionId: commerceResourceIdSchema
1875
+ }).strict();
1876
+ var cancelServerPaymentSessionParamsSchema = cancelServerPaymentSessionBodySchema.extend({
1877
+ idempotencyKey: commerceIdempotencyKeySchema
1878
+ });
1879
+ var cancelPaymentSessionParamsSchema = z5.union([
1880
+ cancelBrowserPaymentSessionParamsSchema,
1881
+ cancelServerPaymentSessionParamsSchema
1190
1882
  ]);
1191
- var orderDisplayFulfillmentStatusSchema = z4.enum([
1192
- "unfulfilled",
1193
- "in_progress",
1194
- "on_hold",
1195
- "partially_fulfilled",
1196
- "shipped",
1197
- "fulfilled",
1883
+ var confirmTossPaymentSessionBodySchema = z5.object({
1884
+ paymentSessionId: commerceResourceIdSchema,
1885
+ action: z5.object({
1886
+ type: z5.literal("toss_authorization"),
1887
+ paymentKey: z5.string().min(1).max(512),
1888
+ providerOrderId: tossProviderOrderIdSchema
1889
+ }).strict()
1890
+ }).strict();
1891
+ var confirmTossPaymentSessionParamsSchema = confirmTossPaymentSessionBodySchema.extend({
1892
+ idempotencyKey: commerceIdempotencyKeySchema
1893
+ });
1894
+ var customPaymentProviderSchema = z5.string().trim().min(1).max(64).regex(/^[a-z0-9][a-z0-9_-]{0,63}$/i, "provider must be a slug");
1895
+ var customPaymentIdentitySchema = z5.string().trim().min(1);
1896
+ var customPaymentCurrencyInputSchema = z5.string().trim().min(1).regex(/^[A-Za-z]{3}$/, "currency must be a three-letter ISO 4217 code").transform((currency) => currency.toUpperCase());
1897
+ var createCustomPaymentSessionBodySchema = z5.object({
1898
+ provider: customPaymentProviderSchema,
1899
+ checkoutToken: commerceCapabilitySchema
1900
+ }).strict();
1901
+ var createCustomPaymentSessionParamsSchema = createCustomPaymentSessionBodySchema.extend({
1902
+ idempotencyKey: commerceIdempotencyKeySchema
1903
+ });
1904
+ var customPaymentSessionSchema = z5.object({
1905
+ id: commerceResourceIdSchema,
1906
+ provider: customPaymentProviderSchema,
1907
+ providerSessionId: customPaymentIdentitySchema,
1908
+ money: moneySchema
1909
+ }).strict();
1910
+ var confirmCustomPaymentSessionBodySchema = z5.object({
1911
+ provider: customPaymentProviderSchema,
1912
+ providerSessionId: customPaymentIdentitySchema,
1913
+ providerPaymentId: customPaymentIdentitySchema,
1914
+ providerEventId: customPaymentIdentitySchema,
1915
+ amount: z5.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
1916
+ currency: customPaymentCurrencyInputSchema,
1917
+ occurredAt: customPaymentIdentitySchema
1918
+ }).strict();
1919
+ var confirmCustomPaymentSessionResponseSchema = z5.object({
1920
+ orderId: commerceResourceIdSchema,
1921
+ transactionId: commerceResourceIdSchema,
1922
+ status: z5.literal("paid"),
1923
+ alreadyConfirmed: z5.boolean().optional()
1924
+ }).strict();
1925
+ var paymentNextActionSchema = z5.object({
1926
+ type: z5.literal("toss_widget"),
1927
+ clientKey: z5.string().min(1).max(255),
1928
+ paymentMethodVariantKey: z5.string().min(1).max(255),
1929
+ agreementVariantKey: z5.string().min(1).max(255),
1930
+ providerOrderId: tossProviderOrderIdSchema,
1931
+ providerCustomerKey: tossProviderCustomerKeySchema,
1932
+ orderName: z5.string().min(1).max(200),
1933
+ customerName: z5.string().min(1).max(200).optional(),
1934
+ customerEmail: z5.string().email().max(254).optional()
1935
+ }).strict();
1936
+ var PAYMENT_SESSION_ROLES = [
1937
+ "active",
1938
+ "canonical_candidate",
1939
+ "canonical",
1940
+ "residual",
1941
+ "historical"
1942
+ ];
1943
+ var paymentSessionRoleSchema = z5.enum(PAYMENT_SESSION_ROLES);
1944
+ var PAYMENT_PROVIDER_STATUSES = [
1945
+ "requires_payment_method",
1946
+ "requires_action",
1947
+ "requires_confirmation",
1948
+ "initializing",
1949
+ "processing",
1950
+ "authorized",
1951
+ "canceling",
1952
+ "unknown",
1953
+ "captured",
1954
+ "failed",
1198
1955
  "canceled"
1199
- ]);
1200
- var orderReturnStatusSchema = z4.enum([
1201
- "no_return",
1202
- "return_requested",
1203
- "in_progress",
1204
- "returned"
1205
- ]);
1206
- var orderStatusSchema = z4.enum([
1207
- "pending",
1208
- "paid",
1209
- "canceled",
1210
- "refunded",
1211
- "preparing",
1212
- "shipped",
1213
- "delivered",
1214
- "confirmed",
1215
- "return_requested",
1216
- "return_processing",
1217
- "returned"
1218
- ]);
1219
- var entityIdSchema = z4.union([z4.string().min(1), z4.number()]).transform(String);
1220
- var createOrderItemSchema = z4.object({
1221
- // `product` and `option` are storefront-optional: the create-order
1222
- // endpoint derives the product from the variant's parent and snapshots
1223
- // option selection from the variant.
1224
- product: entityIdSchema.optional(),
1225
- variant: entityIdSchema,
1226
- option: entityIdSchema.optional(),
1227
- quantity: z4.number().int().positive("quantity must be a positive integer"),
1228
- unitPrice: z4.number().optional(),
1229
- totalPrice: z4.number().optional()
1956
+ ];
1957
+ var paymentProviderStatusSchema = z5.enum(PAYMENT_PROVIDER_STATUSES);
1958
+ var PAYMENT_RECONCILIATION_REASONS = [
1959
+ "provider_outcome_unknown",
1960
+ "local_commit_ambiguous",
1961
+ "amount_mismatch",
1962
+ "currency_mismatch",
1963
+ "excess_capture_pending_order",
1964
+ "inventory_unavailable",
1965
+ "admission_unavailable",
1966
+ "excess_capture",
1967
+ "late_capture",
1968
+ "cross_checkout_duplicate"
1969
+ ];
1970
+ var paymentReconciliationReasonSchema = z5.enum(
1971
+ PAYMENT_RECONCILIATION_REASONS
1972
+ );
1973
+ function requiredReconciliationSchema(reasons) {
1974
+ const reasonSchema = z5.enum(reasons);
1975
+ return z5.object({
1976
+ status: z5.literal("required"),
1977
+ reasons: z5.array(reasonSchema).min(1).readonly()
1978
+ }).strict().superRefine((value, ctx) => {
1979
+ const indexes = new Map(reasons.map((reason, index) => [reason, index]));
1980
+ let previous = -1;
1981
+ value.reasons.forEach((reason, index) => {
1982
+ const current = indexes.get(reason) ?? -1;
1983
+ if (current <= previous) {
1984
+ ctx.addIssue({
1985
+ code: "custom",
1986
+ path: ["reasons", index],
1987
+ message: "reconciliation reasons must be unique and canonical-sorted"
1988
+ });
1989
+ }
1990
+ previous = current;
1991
+ });
1992
+ });
1993
+ }
1994
+ var noReconciliationSchema = z5.object({ status: z5.literal("none") }).strict();
1995
+ var publicCaptureEvidenceSchema = z5.object({
1996
+ amountMinor: z5.number().int().nonnegative(),
1997
+ currency: currencyCodeSchema,
1998
+ observedAt: z5.string().datetime({ offset: true })
1999
+ }).strict();
2000
+ var reconciliationRefundEvidenceSchema = z5.object({
2001
+ providerRefundId: z5.string().min(1),
2002
+ money: moneySchema,
2003
+ observedAt: z5.string().datetime({ offset: true })
2004
+ }).strict();
2005
+ var PAYMENT_FAILURE_REASONS = [
2006
+ "payment_declined",
2007
+ "authentication_failed",
2008
+ "authorization_expired",
2009
+ "payment_method_unavailable",
2010
+ "provider_terminal_failure",
2011
+ "unknown_provider_terminal_failure"
2012
+ ];
2013
+ var paymentFailureReasonSchema = z5.enum(PAYMENT_FAILURE_REASONS);
2014
+ var orderReferenceSchema = z5.object({
2015
+ id: z5.string().min(1),
2016
+ orderNumber: z5.string().min(1)
2017
+ }).strict();
2018
+ var paymentSessionBaseShape = {
2019
+ id: z5.string().min(1),
2020
+ money: moneySchema,
2021
+ expiresAt: z5.string().datetime({ offset: true }).nullable()
2022
+ };
2023
+ var suppressedNotReadyShape = {
2024
+ presentationStatus: z5.literal("suppressed"),
2025
+ finalizationStatus: z5.literal("not_ready"),
2026
+ reconciliation: noReconciliationSchema,
2027
+ order: z5.null()
2028
+ };
2029
+ function stateSchemas(nextAction) {
2030
+ return [
2031
+ z5.object({
2032
+ providerStatus: z5.enum(["requires_payment_method", "requires_action"]),
2033
+ sessionRole: z5.literal("active"),
2034
+ presentationStatus: z5.literal("actionable"),
2035
+ finalizationStatus: z5.literal("not_ready"),
2036
+ nextAction,
2037
+ reconciliation: noReconciliationSchema,
2038
+ order: z5.null()
2039
+ }),
2040
+ z5.object({
2041
+ providerStatus: z5.enum(["requires_payment_method", "requires_action"]),
2042
+ sessionRole: z5.literal("residual"),
2043
+ ...suppressedNotReadyShape
2044
+ }),
2045
+ z5.object({
2046
+ providerStatus: z5.literal("requires_confirmation"),
2047
+ sessionRole: z5.enum(["active", "residual"]),
2048
+ ...suppressedNotReadyShape
2049
+ }),
2050
+ z5.object({
2051
+ providerStatus: z5.enum(["initializing", "processing", "authorized"]),
2052
+ sessionRole: z5.enum(["active", "residual"]),
2053
+ ...suppressedNotReadyShape
2054
+ }),
2055
+ z5.object({
2056
+ providerStatus: z5.literal("canceling"),
2057
+ sessionRole: z5.enum(["active", "residual"]),
2058
+ ...suppressedNotReadyShape
2059
+ }),
2060
+ z5.object({
2061
+ providerStatus: z5.literal("unknown"),
2062
+ sessionRole: z5.enum(["active", "residual"]),
2063
+ presentationStatus: z5.literal("suppressed"),
2064
+ finalizationStatus: z5.literal("not_ready"),
2065
+ reconciliation: requiredReconciliationSchema([
2066
+ "provider_outcome_unknown"
2067
+ ]),
2068
+ order: z5.null()
2069
+ }),
2070
+ z5.object({
2071
+ providerStatus: z5.literal("captured"),
2072
+ sessionRole: z5.enum(["canonical_candidate", "residual"]),
2073
+ presentationStatus: z5.literal("suppressed"),
2074
+ finalizationStatus: z5.literal("pending"),
2075
+ reconciliation: noReconciliationSchema,
2076
+ providerEvidence: publicCaptureEvidenceSchema,
2077
+ order: z5.null()
2078
+ }),
2079
+ z5.object({
2080
+ providerStatus: z5.literal("captured"),
2081
+ sessionRole: z5.enum(["canonical", "historical"]),
2082
+ presentationStatus: z5.literal("suppressed"),
2083
+ finalizationStatus: z5.literal("committed"),
2084
+ reconciliation: noReconciliationSchema,
2085
+ providerEvidence: publicCaptureEvidenceSchema,
2086
+ order: orderReferenceSchema
2087
+ }),
2088
+ z5.object({
2089
+ providerStatus: z5.literal("captured"),
2090
+ sessionRole: z5.enum(["canonical_candidate", "residual"]),
2091
+ presentationStatus: z5.literal("suppressed"),
2092
+ finalizationStatus: z5.literal("pending"),
2093
+ reconciliation: requiredReconciliationSchema([
2094
+ "local_commit_ambiguous",
2095
+ "amount_mismatch",
2096
+ "currency_mismatch",
2097
+ "excess_capture_pending_order"
2098
+ ]),
2099
+ providerEvidence: publicCaptureEvidenceSchema,
2100
+ order: z5.null()
2101
+ }),
2102
+ z5.object({
2103
+ providerStatus: z5.literal("captured"),
2104
+ sessionRole: z5.enum(["canonical", "residual"]),
2105
+ presentationStatus: z5.literal("suppressed"),
2106
+ finalizationStatus: z5.literal("committed"),
2107
+ reconciliation: requiredReconciliationSchema([
2108
+ "inventory_unavailable",
2109
+ "admission_unavailable",
2110
+ "amount_mismatch",
2111
+ "currency_mismatch",
2112
+ "excess_capture",
2113
+ "late_capture",
2114
+ "cross_checkout_duplicate"
2115
+ ]),
2116
+ providerEvidence: publicCaptureEvidenceSchema,
2117
+ order: orderReferenceSchema
2118
+ }),
2119
+ z5.object({
2120
+ providerStatus: z5.literal("failed"),
2121
+ sessionRole: z5.literal("historical"),
2122
+ presentationStatus: z5.literal("suppressed"),
2123
+ finalizationStatus: z5.literal("not_ready"),
2124
+ reconciliation: noReconciliationSchema,
2125
+ failure: z5.object({
2126
+ reason: paymentFailureReasonSchema,
2127
+ message: z5.string().min(1).optional()
2128
+ }).strict(),
2129
+ order: z5.null()
2130
+ }),
2131
+ z5.object({
2132
+ providerStatus: z5.literal("canceled"),
2133
+ sessionRole: z5.literal("historical"),
2134
+ presentationStatus: z5.literal("suppressed"),
2135
+ finalizationStatus: z5.literal("not_ready"),
2136
+ cancellationReason: z5.enum([
2137
+ "expired",
2138
+ "customer",
2139
+ "operator",
2140
+ "provider"
2141
+ ]),
2142
+ reconciliation: noReconciliationSchema,
2143
+ order: z5.null()
2144
+ })
2145
+ ];
2146
+ }
2147
+ function providerSessionSchemas(action) {
2148
+ return stateSchemas(action).map(
2149
+ (state) => z5.object({
2150
+ ...paymentSessionBaseShape,
2151
+ provider: z5.literal("toss"),
2152
+ ...state.shape
2153
+ }).strict()
2154
+ );
2155
+ }
2156
+ var publicPaymentSessionVariants = [
2157
+ ...providerSessionSchemas(paymentNextActionSchema)
2158
+ ];
2159
+ var publicPaymentSessionSchema = z5.union(publicPaymentSessionVariants);
2160
+ var CreatePaymentSessionParamsSchema = createPaymentSessionParamsSchema;
2161
+ var CancelServerPaymentSessionParamsSchema = cancelServerPaymentSessionParamsSchema;
2162
+ var ConfirmTossPaymentSessionParamsSchema = confirmTossPaymentSessionParamsSchema;
2163
+
2164
+ // ../../internal/contracts/src/ecommerce/checkout.ts
2165
+ var canonicalPublicCheckoutItemSchema = z6.object({
2166
+ id: z6.string().min(1),
2167
+ product: publicCartReferenceSchema,
2168
+ variant: publicCartReferenceSchema,
2169
+ productTitle: z6.string().min(1),
2170
+ variantTitle: z6.string().min(1).nullable(),
2171
+ sku: z6.string().min(1).nullable(),
2172
+ admissions: publicCartItemAdmissionsSchema,
2173
+ quantity: z6.number().int().positive().max(MAX_CART_LINE_QUANTITY),
2174
+ unitPriceMinor: minorAmountSchema,
2175
+ discountMinor: minorAmountSchema,
2176
+ lineTotalMinor: minorAmountSchema,
2177
+ requiresShipping: z6.boolean()
2178
+ }).strict().superRefine((item, ctx) => {
2179
+ const expectedLineTotal = BigInt(item.unitPriceMinor) * BigInt(item.quantity) - BigInt(item.discountMinor);
2180
+ if (expectedLineTotal < 0n) {
2181
+ ctx.addIssue({
2182
+ code: "custom",
2183
+ path: ["discountMinor"],
2184
+ message: "discountMinor cannot exceed the undiscounted line total"
2185
+ });
2186
+ return;
2187
+ }
2188
+ if (BigInt(item.lineTotalMinor) !== expectedLineTotal) {
2189
+ ctx.addIssue({
2190
+ code: "custom",
2191
+ path: ["lineTotalMinor"],
2192
+ message: "lineTotalMinor must equal unitPriceMinor * quantity - discountMinor"
2193
+ });
2194
+ }
2195
+ });
2196
+ var checkoutAmountsSchema = z6.object({
2197
+ currency: currencyCodeSchema,
2198
+ subtotalMinor: minorAmountSchema,
2199
+ shippingMinor: minorAmountSchema,
2200
+ discountMinor: minorAmountSchema,
2201
+ totalMinor: minorAmountSchema
2202
+ }).strict().superRefine((amounts, ctx) => {
2203
+ const expectedTotal = BigInt(amounts.subtotalMinor) + BigInt(amounts.shippingMinor) - BigInt(amounts.discountMinor);
2204
+ if (BigInt(amounts.totalMinor) !== expectedTotal) {
2205
+ ctx.addIssue({
2206
+ code: "custom",
2207
+ path: ["totalMinor"],
2208
+ message: "totalMinor must equal subtotalMinor + shippingMinor - discountMinor"
2209
+ });
2210
+ }
2211
+ });
2212
+ var checkoutPaymentSessionRefSchema = z6.object({
2213
+ id: z6.string().min(1),
2214
+ provider: z6.literal("toss"),
2215
+ providerStatus: paymentProviderStatusSchema
2216
+ }).strict();
2217
+ var openCheckoutPaymentStateSchema = z6.object({
2218
+ activePaymentSession: checkoutPaymentSessionRefSchema.nullable()
1230
2219
  }).strict();
1231
- var providerSlugSchema2 = z4.string().trim().regex(/^[a-z0-9][a-z0-9_-]{0,63}$/, "pgProvider must be lowercase slug");
1232
- var createOrderSchema = z4.object({
1233
- pgPaymentId: z4.string().min(1).optional(),
1234
- pgProvider: providerSlugSchema2.optional(),
1235
- orderNumber: z4.string().min(1, "orderNumber is required"),
1236
- customer: entityIdSchema.optional(),
1237
- customerSnapshot: z4.object({
1238
- name: z4.string().optional(),
1239
- email: z4.string().email("Invalid email format"),
1240
- phone: z4.string().optional()
2220
+ var spentCheckoutPaymentStateSchema = z6.object({
2221
+ activePaymentSession: z6.null()
2222
+ }).strict();
2223
+ var lightweightOrderReferenceSchema = z6.object({
2224
+ id: z6.string().min(1),
2225
+ orderNumber: z6.string().min(1)
2226
+ }).strict();
2227
+ var publicCheckoutBaseShape = {
2228
+ id: z6.string().min(1),
2229
+ amounts: checkoutAmountsSchema,
2230
+ items: z6.array(canonicalPublicCheckoutItemSchema).min(1),
2231
+ customerSnapshot: customerSnapshotSchema,
2232
+ shippingAddress: checkoutShippingAddressSchema.nullable(),
2233
+ attributes: publicAttributesSchema,
2234
+ expiresAt: z6.string().datetime({ offset: true }),
2235
+ createdAt: z6.string().datetime({ offset: true }),
2236
+ updatedAt: z6.string().datetime({ offset: true })
2237
+ };
2238
+ var abandonedReasonSchema = z6.enum(["expired", "customer", "operator"]);
2239
+ var publicCheckoutVariants = [
2240
+ z6.object({
2241
+ ...publicCheckoutBaseShape,
2242
+ status: z6.literal("open"),
2243
+ paymentState: openCheckoutPaymentStateSchema,
2244
+ order: z6.null()
2245
+ }).strict(),
2246
+ z6.object({
2247
+ ...publicCheckoutBaseShape,
2248
+ status: z6.literal("placed"),
2249
+ placedAt: z6.string().datetime({ offset: true }),
2250
+ paymentState: spentCheckoutPaymentStateSchema,
2251
+ order: lightweightOrderReferenceSchema
1241
2252
  }).strict(),
1242
- shippingAddress: z4.object({
1243
- postalCode: z4.string().optional(),
1244
- address: z4.string().optional(),
1245
- detailAddress: z4.string().optional(),
1246
- deliveryMessage: z4.string().optional(),
1247
- recipientName: z4.string().optional(),
1248
- phone: z4.string().optional()
1249
- }).strict().optional(),
1250
- orderItems: z4.array(createOrderItemSchema).min(1, "At least one order item is required").max(100, "Maximum 100 items per order"),
1251
- totalAmount: z4.number().nonnegative("totalAmount must be non-negative"),
1252
- shippingAmount: z4.number().min(0).optional(),
1253
- discountCode: z4.string().optional()
1254
- }).strict().superRefine((data, ctx) => {
1255
- if (data.pgPaymentId && !data.pgProvider) {
2253
+ z6.object({
2254
+ ...publicCheckoutBaseShape,
2255
+ status: z6.literal("placed"),
2256
+ placedAt: z6.string().datetime({ offset: true }),
2257
+ placedVia: z6.literal("late_capture"),
2258
+ abandonedAt: z6.string().datetime({ offset: true }),
2259
+ abandonedReason: abandonedReasonSchema,
2260
+ paymentState: spentCheckoutPaymentStateSchema,
2261
+ order: lightweightOrderReferenceSchema
2262
+ }).strict(),
2263
+ z6.object({
2264
+ ...publicCheckoutBaseShape,
2265
+ status: z6.literal("abandoned"),
2266
+ abandonedAt: z6.string().datetime({ offset: true }),
2267
+ abandonedReason: abandonedReasonSchema,
2268
+ paymentState: z6.object({
2269
+ activePaymentSession: z6.null()
2270
+ }).strict(),
2271
+ order: z6.null()
2272
+ }).strict()
2273
+ ];
2274
+ function validateCheckoutSnapshot(input, ctx) {
2275
+ const checkout = input;
2276
+ const expectedSubtotal = checkout.items.reduce(
2277
+ (total, item) => total + BigInt(item.unitPriceMinor) * BigInt(item.quantity),
2278
+ 0n
2279
+ );
2280
+ const expectedDiscount = checkout.items.reduce(
2281
+ (total, item) => total + BigInt(item.discountMinor),
2282
+ 0n
2283
+ );
2284
+ if (BigInt(checkout.amounts.subtotalMinor) !== expectedSubtotal) {
1256
2285
  ctx.addIssue({
1257
- code: z4.ZodIssueCode.custom,
1258
- path: ["pgProvider"],
1259
- message: "pgProvider is required when pgPaymentId is provided"
2286
+ code: "custom",
2287
+ path: ["amounts", "subtotalMinor"],
2288
+ message: "subtotalMinor must equal the sum of undiscounted checkout line totals"
1260
2289
  });
1261
2290
  }
1262
- if (data.pgProvider && !data.pgPaymentId) {
2291
+ if (BigInt(checkout.amounts.discountMinor) !== expectedDiscount) {
1263
2292
  ctx.addIssue({
1264
- code: z4.ZodIssueCode.custom,
1265
- path: ["pgPaymentId"],
1266
- message: "pgPaymentId is required when pgProvider is provided"
2293
+ code: "custom",
2294
+ path: ["amounts", "discountMinor"],
2295
+ message: "discountMinor must equal the sum of checkout line discounts"
1267
2296
  });
1268
2297
  }
2298
+ }
2299
+ var publicCheckoutSchema = z6.union(publicCheckoutVariants).superRefine(validateCheckoutSnapshot);
2300
+ var serverCheckoutExtraShape = {
2301
+ clientReferenceId: commerceResourceIdSchema.nullable(),
2302
+ integrationMetadata: integrationMetadataSchema
2303
+ };
2304
+ var serverCheckoutVariants = publicCheckoutVariants.map(
2305
+ (variant) => variant.extend(serverCheckoutExtraShape)
2306
+ );
2307
+ var serverCheckoutSchema = z6.union(serverCheckoutVariants).superRefine(validateCheckoutSnapshot);
2308
+ var retrieveCheckoutParamsSchema = z6.object({ checkoutToken: commerceCapabilitySchema }).strict();
2309
+ var retrieveServerCheckoutParamsSchema = z6.object({ checkoutId: commerceResourceIdSchema }).strict();
2310
+ var createCheckoutBodySchema = z6.object({
2311
+ cartToken: commerceCapabilitySchema,
2312
+ customer: customerSnapshotSchema,
2313
+ shippingAddress: checkoutShippingAddressSchema.optional()
2314
+ }).strict();
2315
+ var createCheckoutParamsSchema = createCheckoutBodySchema.extend({
2316
+ idempotencyKey: commerceIdempotencyKeySchema
1269
2317
  });
1270
- var updateTransactionSchema = z4.object({
1271
- pgPaymentId: z4.string().min(1, "pgPaymentId is required").describe("PG payment ID (required)"),
1272
- status: transactionStatusSchema.describe(
1273
- "New transaction status (required)"
1274
- ),
1275
- paymentMethod: z4.string().optional().describe("Payment method (optional)"),
1276
- receiptUrl: z4.string().optional().describe("Receipt URL (optional)"),
1277
- paymentKey: z4.string().min(1).optional().describe("Provider payment key for verified paid confirmation"),
1278
- amount: z4.number().int().positive().optional().describe("Provider-confirmed amount for verified paid confirmation")
2318
+ var createServerCheckoutBodySchema = createCheckoutBodySchema.safeExtend({
2319
+ clientReferenceId: commerceResourceIdSchema.optional(),
2320
+ integrationMetadata: integrationMetadataSchema.optional()
1279
2321
  }).strict();
1280
- var confirmPaymentSchema = z4.object({
1281
- orderNumber: z4.string().min(1).optional(),
1282
- pgPaymentId: z4.string().min(1, "pgPaymentId is required").describe("Provider payment identifier stored on the transaction"),
1283
- pgProvider: providerSlugSchema2.describe(
1284
- "Payment provider slug, e.g. toss, portone, stripe"
1285
- ),
1286
- pgOrderId: z4.string().min(1).optional(),
1287
- amount: z4.number().int().nonnegative("amount must be non-negative").describe("Provider-confirmed amount in minor units"),
1288
- currency: z4.string().min(1).optional(),
1289
- paymentMethod: z4.string().optional(),
1290
- receiptUrl: z4.string().url().optional(),
1291
- approvedAt: z4.string().optional(),
1292
- providerStatus: z4.string().optional(),
1293
- providerEventId: z4.string().min(1).optional(),
1294
- confirmationSource: z4.enum([
1295
- "provider_webhook",
1296
- "provider_lookup",
1297
- "provider_api_confirm",
1298
- "manual_server"
1299
- ]).optional(),
1300
- paymentKey: z4.string().min(1).optional().describe(
1301
- "Optional provider payment key from the client confirm handshake; stored for BFF/provider refund workflows (also accepted as metadata.tossPaymentKey). Local cancel does not read this field."
1302
- ),
1303
- metadata: z4.record(z4.string(), z4.unknown()).optional()
2322
+ var createServerCheckoutParamsSchema = createServerCheckoutBodySchema.extend({
2323
+ idempotencyKey: commerceIdempotencyKeySchema
2324
+ });
2325
+ var createCheckoutResultSchema = z6.object({
2326
+ checkout: publicCheckoutSchema,
2327
+ checkoutToken: commerceCapabilitySchema
2328
+ }).strict();
2329
+ var createServerCheckoutResultSchema = z6.object({
2330
+ checkout: serverCheckoutSchema,
2331
+ checkoutToken: commerceCapabilitySchema
1304
2332
  }).strict();
1305
- var confirmPaymentResponseSchema = z4.object({
1306
- orderId: z4.string().min(1),
1307
- transactionId: z4.string().min(1),
1308
- status: z4.literal("paid"),
1309
- alreadyConfirmed: z4.boolean().optional()
2333
+ var canonicalCheckoutQuerySchema = z6.object({
2334
+ limit: z6.number().int().min(1).max(100).default(20),
2335
+ cursor: z6.string().min(1).max(512).optional(),
2336
+ status: z6.array(z6.enum(["open", "placed", "abandoned"])).optional(),
2337
+ createdAtFrom: z6.string().datetime({ offset: true }).optional(),
2338
+ createdAtTo: z6.string().datetime({ offset: true }).optional(),
2339
+ clientReferenceId: commerceResourceIdSchema.optional()
2340
+ }).strict();
2341
+ var checkoutListItemVariants = publicCheckoutVariants.map(
2342
+ (variant) => variant.omit({
2343
+ items: true,
2344
+ shippingAddress: true,
2345
+ attributes: true
2346
+ }).extend({ clientReferenceId: commerceResourceIdSchema.nullable() })
2347
+ );
2348
+ var checkoutListItemSchema = z6.union(checkoutListItemVariants);
2349
+ var canonicalCheckoutQueryResultSchema = z6.object({
2350
+ data: z6.array(checkoutListItemSchema),
2351
+ hasMore: z6.boolean(),
2352
+ nextCursor: z6.string().min(1).max(512).nullable()
1310
2353
  }).strict();
2354
+ var CreateServerCheckoutParamsSchema = createServerCheckoutParamsSchema;
2355
+ var CanonicalCheckoutQuerySchema = canonicalCheckoutQuerySchema;
2356
+
2357
+ // ../../internal/contracts/src/ecommerce/index.ts
2358
+ var transactionStatusSchema = z7.enum([
2359
+ "pending",
2360
+ "paid",
2361
+ "success",
2362
+ "failed",
2363
+ "error",
2364
+ "canceled",
2365
+ "refunded"
2366
+ ]);
2367
+ var transactionKindSchema = z7.enum([
2368
+ "authorization",
2369
+ "sale",
2370
+ "capture",
2371
+ "void",
2372
+ "refund"
2373
+ ]);
1311
2374
  var RETURN_REASON_CODES = [
1312
2375
  "change_of_mind",
1313
2376
  "defective",
1314
2377
  "wrong_delivery",
1315
2378
  "damaged",
2379
+ "size_too_large",
2380
+ "size_too_small",
2381
+ "color",
2382
+ "style",
2383
+ "not_as_described",
2384
+ "wrong_item",
2385
+ "unknown",
1316
2386
  "other"
1317
2387
  ];
1318
- var returnReasonSchema = z4.enum(RETURN_REASON_CODES);
2388
+ var returnReasonSchema = z7.enum(RETURN_REASON_CODES);
1319
2389
  var RETURN_REJECTION_REASON_CODES = [
1320
2390
  "window_expired",
1321
2391
  "used_or_damaged",
1322
2392
  "non_returnable",
1323
2393
  "other"
1324
2394
  ];
1325
- var returnRejectionReasonCodeSchema = z4.enum(
2395
+ var returnRejectionReasonCodeSchema = z7.enum(
1326
2396
  RETURN_REJECTION_REASON_CODES
1327
2397
  );
1328
- var restockActionSchema = z4.enum(["return_to_stock", "discard"]);
1329
- var returnWithRefundItemSchema = z4.object({
1330
- orderItem: z4.union([z4.string().min(1), z4.number()]).transform(String),
1331
- quantity: z4.number().int().positive("quantity must be a positive integer"),
2398
+ var RETURN_CANCELLATION_REASON_CODES = [
2399
+ "items_not_received",
2400
+ "customer_withdrew",
2401
+ "merchant_decision",
2402
+ "other"
2403
+ ];
2404
+ var returnCancellationReasonCodeSchema = z7.enum(
2405
+ RETURN_CANCELLATION_REASON_CODES
2406
+ );
2407
+ var restockActionSchema = z7.enum(["return_to_stock", "discard"]);
2408
+ var returnWithRefundItemSchema = z7.object({
2409
+ orderItem: z7.union([z7.string().min(1), z7.number()]).transform(String),
2410
+ quantity: z7.number().int().positive("quantity must be a positive integer"),
1332
2411
  restockAction: restockActionSchema.default("return_to_stock"),
1333
- restockingFee: z4.number().min(0, "restockingFee must be non-negative").optional().describe("Restocking fee charged for this line (ADR 0005 \xA7Gap 1)")
2412
+ restockingFee: z7.number().min(0, "restockingFee must be non-negative").optional().describe("Restocking fee charged for this line (ADR 0005 \xA7Gap 1)")
1334
2413
  }).strict();
1335
- var returnWithRefundSchema = z4.object({
1336
- orderNumber: z4.string().min(1, "orderNumber is required").describe("Order number (required)"),
2414
+ var returnWithRefundSchema = z7.object({
2415
+ orderNumber: z7.string().min(1, "orderNumber is required").describe("Order number (required)"),
1337
2416
  reason: returnReasonSchema.optional().describe("Return reason (optional)"),
1338
- reasonDetail: z4.string().optional().describe("Detailed reason text (optional)"),
1339
- returnItems: z4.array(returnWithRefundItemSchema).min(1, "At least one return item is required").max(100, "Too many return items").describe("Array of products to return (required)"),
1340
- refundAmount: z4.number().min(0, "refundAmount must be non-negative").describe("Refund amount (required, min 0)"),
1341
- returnShippingFee: z4.number().min(0, "returnShippingFee must be non-negative").optional().describe(
2417
+ reasonDetail: z7.string().optional().describe("Detailed reason text (optional)"),
2418
+ returnItems: z7.array(returnWithRefundItemSchema).min(1, "At least one return item is required").max(100, "Too many return items").describe("Array of products to return (required)"),
2419
+ refundAmount: z7.number().min(0, "refundAmount must be non-negative").describe("Refund amount (required, min 0)"),
2420
+ returnShippingFee: z7.number().min(0, "returnShippingFee must be non-negative").optional().describe(
1342
2421
  "Return shipping fee charged to the customer (ADR 0005 \xA7Gap 1)"
1343
2422
  ),
1344
- initialShippingRefundAmount: z4.number().min(0, "initialShippingRefundAmount must be non-negative").optional().describe("Initial order shipping amount refunded to the customer"),
1345
- initialShippingRefundOverrideNote: z4.string().min(1).optional().describe(
2423
+ initialShippingRefundAmount: z7.number().min(0, "initialShippingRefundAmount must be non-negative").optional().describe("Initial order shipping amount refunded to the customer"),
2424
+ initialShippingRefundOverrideNote: z7.string().min(1).optional().describe(
1346
2425
  "Operator audit note required when overriding policy suggestion"
1347
2426
  ),
1348
- pgPaymentId: z4.string().min(1, "pgPaymentId is required").describe("PG payment ID for refund (required)"),
1349
- paymentKey: z4.string().min(1).optional().describe("Provider payment key for verified refund"),
1350
- refundReceiptUrl: z4.string().optional().describe("Refund receipt URL (optional)")
2427
+ pgPaymentId: z7.string().min(1, "pgPaymentId is required").describe("PG payment ID for refund (required)"),
2428
+ paymentKey: z7.string().min(1).optional().describe("Provider payment key for verified refund"),
2429
+ refundReceiptUrl: z7.string().optional().describe("Refund receipt URL (optional)")
1351
2430
  }).strict();
1352
- var createReturnSchema = z4.object({
1353
- orderNumber: z4.string().min(1, "orderNumber is required").describe("Order number (required)"),
2431
+ var createReturnSchema = z7.object({
2432
+ orderNumber: z7.string().min(1, "orderNumber is required").describe("Order number (required)"),
1354
2433
  reason: returnReasonSchema.optional().describe("Return reason (optional)"),
1355
- reasonDetail: z4.string().optional().describe("Detailed reason text (optional)"),
1356
- returnItems: z4.array(returnWithRefundItemSchema).min(1, "At least one return item is required").max(100, "Too many return items").describe("Array of products to return (required)"),
1357
- refundAmount: z4.number().min(0, "refundAmount must be non-negative").describe(
2434
+ reasonDetail: z7.string().optional().describe("Detailed reason text (optional)"),
2435
+ returnItems: z7.array(returnWithRefundItemSchema).min(1, "At least one return item is required").max(100, "Too many return items").describe("Array of products to return (required)"),
2436
+ refundAmount: z7.number().min(0, "refundAmount must be non-negative").describe(
1358
2437
  "Line refund amount before initial shipping refund (required, min 0)"
1359
2438
  ),
1360
- returnShippingFee: z4.number().min(0, "returnShippingFee must be non-negative").optional().describe("Return shipping fee charged to the customer"),
1361
- initialShippingRefundAmount: z4.number().min(0, "initialShippingRefundAmount must be non-negative").optional().describe("Initial order shipping amount refunded to the customer"),
1362
- initialShippingRefundOverrideNote: z4.string().min(1).optional().describe(
2439
+ returnShippingFee: z7.number().min(0, "returnShippingFee must be non-negative").optional().describe("Return shipping fee charged to the customer"),
2440
+ initialShippingRefundAmount: z7.number().min(0, "initialShippingRefundAmount must be non-negative").optional().describe("Initial order shipping amount refunded to the customer"),
2441
+ initialShippingRefundOverrideNote: z7.string().min(1).optional().describe(
1363
2442
  "Operator audit note required when overriding policy suggestion"
1364
2443
  )
1365
2444
  }).strict();
2445
+ var updateReturnSchema = z7.object({
2446
+ returnId: z7.union([z7.string(), z7.number()]).transform(String),
2447
+ // Only non-financial transitions are accepted here. `completed` is included
2448
+ // so the server can return the canonical `server_derived` error instead of
2449
+ // a generic validation failure.
2450
+ status: z7.enum([
2451
+ "processing",
2452
+ "approved",
2453
+ "rejected",
2454
+ "canceled",
2455
+ "completed"
2456
+ ]),
2457
+ reasonCode: returnRejectionReasonCodeSchema.optional(),
2458
+ cancellationReasonCode: returnCancellationReasonCodeSchema.optional(),
2459
+ cancellationReasonDetail: z7.string().trim().min(1).max(2e3).optional()
2460
+ }).strict();
1366
2461
  var CANCEL_REASON_CODES = [
1367
2462
  "customer",
1368
2463
  "inventory",
@@ -1371,245 +2466,159 @@ var CANCEL_REASON_CODES = [
1371
2466
  "staff",
1372
2467
  "other"
1373
2468
  ];
1374
- var cancelReasonCodeSchema = z4.enum(CANCEL_REASON_CODES);
2469
+ var cancelReasonCodeSchema = z7.enum(CANCEL_REASON_CODES);
1375
2470
  var FORCE_RESOLUTION_REASON_CODES = [
1376
2471
  "staff_error",
1377
2472
  "wrong_fulfillment",
1378
2473
  "other"
1379
2474
  ];
1380
- var forceResolutionReasonCodeSchema = z4.enum(
2475
+ var forceResolutionReasonCodeSchema = z7.enum(
1381
2476
  FORCE_RESOLUTION_REASON_CODES
1382
2477
  );
1383
- var idempotencyKeySchema = z4.string().trim().min(1, "idempotencyKey is required").max(255, "idempotencyKey must be 255 characters or fewer").regex(
2478
+ var idempotencyKeySchema = z7.string().trim().min(1, "idempotencyKey is required").max(255, "idempotencyKey must be 255 characters or fewer").regex(
1384
2479
  /^[\x21-\x7E]+$/,
1385
2480
  "idempotencyKey must contain only printable header-safe characters"
1386
2481
  );
1387
- var cancelOrderSchema = z4.object({
1388
- orderNumber: z4.string().min(1, "orderNumber is required").describe("Order number to cancel"),
2482
+ var cancelOrderSchema = z7.object({
2483
+ orderNumber: z7.string().min(1, "orderNumber is required").describe("Order number to cancel"),
1389
2484
  reasonCode: cancelReasonCodeSchema.default("other").describe("Operator-selected cancel reason code"),
1390
- reasonDetail: z4.string().trim().max(2e3, "reasonDetail must be 2000 characters or fewer").optional().describe("Internal cancellation detail stored on the order")
2485
+ reasonDetail: z7.string().trim().max(2e3, "reasonDetail must be 2000 characters or fewer").optional().describe("Internal cancellation detail stored on the order")
1391
2486
  }).strict();
1392
2487
  var cancelOrderResponseBaseSchema = {
1393
- orderId: z4.string().min(1)
2488
+ orderId: z7.string().min(1)
1394
2489
  };
1395
2490
  var unpaidLocalCancelCommittedResponseFields = {
1396
- refundedAmount: z4.literal(0),
1397
- providerRefunded: z4.literal(false)
2491
+ refundedAmount: z7.literal(0),
2492
+ providerRefunded: z7.literal(false)
1398
2493
  };
1399
2494
  var paidLocalCancelCommittedResponseFields = {
1400
- transactionId: z4.string().min(1),
1401
- refundedAmount: z4.literal(0),
1402
- providerRefunded: z4.literal(false),
1403
- refundPending: z4.literal(true)
1404
- };
1405
- var legacyProviderRefundResponseFields = {
1406
- transactionId: z4.string().min(1),
1407
- refundedAmount: z4.number().int().positive(),
1408
- refundSeq: z4.number().int().positive(),
1409
- providerRefunded: z4.literal(true)
2495
+ transactionId: z7.string().min(1),
2496
+ refundedAmount: z7.literal(0),
2497
+ providerRefunded: z7.literal(false),
2498
+ refundPending: z7.literal(true)
1410
2499
  };
1411
2500
  var alreadyCanceledResponseFields = {
1412
- refundedAmount: z4.number().int().nonnegative(),
1413
- providerRefunded: z4.literal(false)
2501
+ refundedAmount: z7.number().int().nonnegative(),
2502
+ providerRefunded: z7.literal(false)
1414
2503
  };
1415
2504
  var alreadyCanceledRefundPendingResponseFields = {
1416
- transactionId: z4.string().min(1),
1417
- refundedAmount: z4.number().int().nonnegative(),
1418
- providerRefunded: z4.literal(false),
1419
- refundPending: z4.literal(true)
2505
+ transactionId: z7.string().min(1),
2506
+ refundedAmount: z7.number().int().nonnegative(),
2507
+ providerRefunded: z7.literal(false),
2508
+ refundPending: z7.literal(true)
1420
2509
  };
1421
- var cancelOrderReconciliationStatusSchema = z4.enum([
1422
- "paid",
1423
- "preparing",
1424
- "shipped",
1425
- "delivered",
1426
- "confirmed",
1427
- "return_requested",
1428
- "return_processing",
1429
- "returned",
1430
- "refunded"
1431
- ]);
1432
- var cancelOrderResponseSchema = z4.union([
1433
- z4.object({
2510
+ var cancelOrderResponseSchema = z7.union([
2511
+ z7.object({
1434
2512
  ...cancelOrderResponseBaseSchema,
1435
2513
  ...paidLocalCancelCommittedResponseFields,
1436
- status: z4.literal("canceled"),
1437
- cancelCommitted: z4.literal(true)
2514
+ status: z7.literal("canceled"),
2515
+ cancelCommitted: z7.literal(true)
1438
2516
  }).strict(),
1439
- z4.object({
2517
+ z7.object({
1440
2518
  ...cancelOrderResponseBaseSchema,
1441
2519
  ...unpaidLocalCancelCommittedResponseFields,
1442
- status: z4.literal("canceled"),
1443
- cancelCommitted: z4.literal(true)
2520
+ status: z7.literal("canceled"),
2521
+ cancelCommitted: z7.literal(true)
1444
2522
  }).strict(),
1445
- z4.object({
2523
+ z7.object({
1446
2524
  ...cancelOrderResponseBaseSchema,
1447
2525
  ...alreadyCanceledResponseFields,
1448
- status: z4.literal("canceled"),
1449
- cancelCommitted: z4.literal(false),
1450
- alreadyCanceled: z4.literal(true)
2526
+ status: z7.literal("canceled"),
2527
+ cancelCommitted: z7.literal(false),
2528
+ alreadyCanceled: z7.literal(true)
1451
2529
  }).strict(),
1452
- z4.object({
2530
+ z7.object({
1453
2531
  ...cancelOrderResponseBaseSchema,
1454
2532
  ...alreadyCanceledRefundPendingResponseFields,
1455
- status: z4.literal("canceled"),
1456
- cancelCommitted: z4.literal(false),
1457
- alreadyCanceled: z4.literal(true)
1458
- }).strict(),
1459
- z4.object({
1460
- ...cancelOrderResponseBaseSchema,
1461
- ...legacyProviderRefundResponseFields,
1462
- status: cancelOrderReconciliationStatusSchema,
1463
- cancelCommitted: z4.literal(false),
1464
- reconciliationRequired: z4.literal(true)
2533
+ status: z7.literal("canceled"),
2534
+ cancelCommitted: z7.literal(false),
2535
+ alreadyCanceled: z7.literal(true)
1465
2536
  }).strict()
1466
2537
  ]);
1467
- var resolveCancelRefundOutcomeSchema = z4.enum(["succeeded", "failed"]);
1468
- var resolveCancelRefundSchema = z4.object({
1469
- orderNumber: z4.string().min(1, "orderNumber is required").describe("Order number whose pending cancel refund is being resolved"),
2538
+ var resolveCancelRefundOutcomeSchema = z7.enum(["succeeded", "failed"]);
2539
+ var resolveCancelRefundSchema = z7.object({
2540
+ orderNumber: z7.string().min(1, "orderNumber is required").describe("Order number whose pending cancel refund is being resolved"),
1470
2541
  idempotencyKey: idempotencyKeySchema.describe(
1471
2542
  "Stable key for this PG refund result report"
1472
2543
  ),
1473
2544
  outcome: resolveCancelRefundOutcomeSchema.describe(
1474
2545
  "PG refund result reported by the storefront or BFF"
1475
2546
  ),
1476
- refundedAmount: z4.number().int("refundedAmount must be an integer minor-unit amount").nonnegative("refundedAmount must be nonnegative"),
1477
- pgProvider: z4.string().trim().min(1, "pgProvider is required").regex(
2547
+ refundedAmount: z7.number().int("refundedAmount must be an integer minor-unit amount").nonnegative("refundedAmount must be nonnegative"),
2548
+ pgProvider: z7.string().trim().min(1, "pgProvider is required").regex(
1478
2549
  /^[a-z0-9][a-z0-9_-]*$/,
1479
2550
  "pgProvider must be a lowercase provider slug"
1480
2551
  ),
1481
- pgRefundId: z4.string().trim().min(1).optional()
2552
+ pgRefundId: z7.string().trim().min(1).optional()
1482
2553
  }).strict().superRefine((value, ctx) => {
1483
2554
  if (value.outcome === "succeeded" && value.refundedAmount <= 0) {
1484
2555
  ctx.addIssue({
1485
- code: z4.ZodIssueCode.custom,
2556
+ code: z7.ZodIssueCode.custom,
1486
2557
  path: ["refundedAmount"],
1487
2558
  message: "refundedAmount must be positive when outcome is succeeded"
1488
2559
  });
1489
2560
  }
1490
2561
  if (value.outcome === "succeeded" && !value.pgRefundId) {
1491
2562
  ctx.addIssue({
1492
- code: z4.ZodIssueCode.custom,
2563
+ code: z7.ZodIssueCode.custom,
1493
2564
  path: ["pgRefundId"],
1494
2565
  message: "pgRefundId is required when outcome is succeeded"
1495
2566
  });
1496
2567
  }
1497
2568
  if (value.outcome === "failed" && value.refundedAmount !== 0) {
1498
2569
  ctx.addIssue({
1499
- code: z4.ZodIssueCode.custom,
2570
+ code: z7.ZodIssueCode.custom,
1500
2571
  path: ["refundedAmount"],
1501
2572
  message: "refundedAmount must be 0 when outcome is failed"
1502
2573
  });
1503
2574
  }
1504
2575
  });
1505
- var resolveCancelRefundResponseSchema = z4.union([
1506
- z4.object({
1507
- orderId: z4.string().min(1),
1508
- transactionId: z4.string().min(1),
1509
- refundTransactionId: z4.string().min(1),
1510
- refundedAmount: z4.number().int().positive(),
1511
- refundStatus: z4.literal("succeeded"),
1512
- transactionStatus: z4.literal("refunded")
2576
+ var resolveCancelRefundResponseSchema = z7.union([
2577
+ z7.object({
2578
+ orderId: z7.string().min(1),
2579
+ transactionId: z7.string().min(1),
2580
+ refundTransactionId: z7.string().min(1),
2581
+ refundedAmount: z7.number().int().positive(),
2582
+ refundStatus: z7.literal("succeeded"),
2583
+ transactionStatus: z7.enum(["paid", "refunded"])
1513
2584
  }).strict(),
1514
- z4.object({
1515
- orderId: z4.string().min(1),
1516
- transactionId: z4.string().min(1),
1517
- refundTransactionId: z4.string().min(1),
1518
- refundedAmount: z4.literal(0),
1519
- refundStatus: z4.literal("failed"),
1520
- transactionStatus: z4.literal("paid")
2585
+ z7.object({
2586
+ orderId: z7.string().min(1),
2587
+ transactionId: z7.string().min(1),
2588
+ refundTransactionId: z7.string().min(1),
2589
+ refundedAmount: z7.literal(0),
2590
+ refundStatus: z7.literal("failed"),
2591
+ transactionStatus: z7.literal("paid")
1521
2592
  }).strict()
1522
2593
  ]);
1523
2594
 
1524
2595
  // src/commands/order.ts
1525
- import { z as z5 } from "zod";
1526
- var idSchema = z5.union([z5.string().min(1), z5.number()]).transform(String);
1527
- var customerSnapshotSchema = z5.object({
1528
- email: z5.string().email("Invalid email format"),
1529
- name: z5.string().optional(),
1530
- phone: z5.string().optional()
1531
- }).strict();
1532
- var orderStatusSchema2 = z5.enum(["confirmed"]);
1533
- var fulfillmentStatusSchema = z5.enum(["shipped", "delivered", "failed"]);
1534
- var updateFulfillmentSchema = z5.object({
2596
+ import { z as z8 } from "zod";
2597
+ var idSchema = z8.union([z8.string().min(1), z8.number()]).transform(String);
2598
+ var orderStatusSchema = z8.enum(["confirmed"]);
2599
+ var fulfillmentStatusSchema = z8.enum(["shipped", "delivered", "failed"]);
2600
+ var updateFulfillmentSchema = z8.object({
1535
2601
  fulfillmentId: idSchema,
1536
2602
  status: fulfillmentStatusSchema.optional(),
1537
- carrier: z5.string().min(1).optional(),
1538
- trackingNumber: z5.string().min(1).optional()
2603
+ carrier: z8.string().min(1).optional(),
2604
+ trackingNumber: z8.string().min(1).optional()
1539
2605
  }).refine(
1540
2606
  (value) => value.status !== void 0 || value.carrier !== void 0 || value.trackingNumber !== void 0,
1541
2607
  {
1542
2608
  message: "status, carrier, or trackingNumber is required"
1543
2609
  }
1544
2610
  );
1545
- var fulfillmentItemsSchema = z5.array(
1546
- z5.object({
2611
+ var fulfillmentItemsSchema = z8.array(
2612
+ z8.object({
1547
2613
  orderItem: idSchema,
1548
- quantity: z5.number().int().positive("quantity must be a positive integer")
2614
+ quantity: z8.number().int().positive("quantity must be a positive integer")
1549
2615
  }).strict()
1550
2616
  ).min(1, "At least one fulfillment item is required").max(100, "Maximum 100 items per fulfillment");
1551
2617
  function registerOrderCommands(program2, getClient2, getFormat2) {
1552
2618
  const order = program2.command("order").description("Order management");
1553
- order.command("create").description("Create a new order").option("--payment-id <id>", "Payment ID").option("--provider <slug>", "Payment provider slug").requiredOption("--order-number <num>", "Order number").requiredOption("--email <email>", "Customer email").option("--customer <id>", "Customer ID").option("--name <name>", "Customer name").option("--phone <phone>", "Customer phone").requiredOption("--shipping-address <json>", "Shipping address (JSON)").requiredOption("--products <json>", "Order products array (JSON)").requiredOption("--total-amount <n>", "Total amount", parseFloat).option("--shipping-amount <n>", "Shipping amount", parseFloat).option("--discount-code <code>", "Discount code").option("--dry-run", "Validate inputs without executing").action(async (opts) => {
1554
- try {
1555
- const parsed = parseWithSchema(
1556
- {
1557
- ...opts.paymentId ? { pgPaymentId: opts.paymentId } : {},
1558
- ...opts.provider ? { pgProvider: opts.provider } : {},
1559
- orderNumber: opts.orderNumber,
1560
- customerSnapshot: {
1561
- email: opts.email,
1562
- name: opts.name,
1563
- phone: opts.phone
1564
- },
1565
- customer: opts.customer,
1566
- shippingAddress: parseJson(
1567
- opts.shippingAddress,
1568
- "shipping-address"
1569
- ),
1570
- orderItems: parseJsonArray(opts.products, "products"),
1571
- totalAmount: opts.totalAmount,
1572
- shippingAmount: opts.shippingAmount,
1573
- discountCode: opts.discountCode
1574
- },
1575
- "order",
1576
- createOrderSchema
1577
- );
1578
- const { pgPaymentId, pgProvider, ...orderData } = parsed;
1579
- const data = pgPaymentId ? { ...orderData, pgPaymentId, pgProvider } : orderData;
1580
- if (opts.dryRun) {
1581
- printResult(
1582
- { dryRun: true, valid: true, action: "order create", data },
1583
- getFormat2()
1584
- );
1585
- return;
1586
- }
1587
- const client = getClient2();
1588
- const result = await client.commerce.orders.create(data);
1589
- printResult(result, getFormat2());
1590
- } catch (e) {
1591
- exitWithError(e);
1592
- }
1593
- });
1594
- order.command("get <orderNumber>").description("Get an order by order number").action(async (orderNumber) => {
1595
- try {
1596
- const client = getClient2();
1597
- const {
1598
- docs: [order2]
1599
- } = await client.collections.from("orders").find({
1600
- where: { orderNumber: { equals: orderNumber } },
1601
- limit: 1,
1602
- depth: 1
1603
- });
1604
- if (!order2) throw new Error("Order not found");
1605
- printResult(order2, getFormat2());
1606
- } catch (e) {
1607
- exitWithError(e);
1608
- }
1609
- });
1610
2619
  order.command("update <orderNumber>").description("Confirm a delivered order purchase").requiredOption("--status <status>", "New status (confirmed only)").option("--dry-run", "Validate inputs without executing").action(async (orderNumber, opts) => {
1611
2620
  try {
1612
- const status = parseWithSchema(opts.status, "status", orderStatusSchema2);
2621
+ const status = parseWithSchema(opts.status, "status", orderStatusSchema);
1613
2622
  const data = { orderNumber, status };
1614
2623
  if (opts.dryRun) {
1615
2624
  printResult(
@@ -1653,40 +2662,6 @@ function registerOrderCommands(program2, getClient2, getFormat2) {
1653
2662
  exitWithError(e);
1654
2663
  }
1655
2664
  });
1656
- order.command("checkout").description("Convert a cart to an order").requiredOption("--cart-id <id>", "Cart ID").option("--payment-id <id>", "Payment ID (optional for free orders)").option("--provider <slug>", "Payment provider slug").requiredOption("--order-number <num>", "Order number").requiredOption("--customer <json>", "Customer snapshot (JSON)").option("--dry-run", "Validate inputs without executing").action(async (opts) => {
1657
- try {
1658
- const customerSnapshot = parseWithSchema(
1659
- parseJson(opts.customer, "customer"),
1660
- "customer",
1661
- customerSnapshotSchema
1662
- );
1663
- const parsed = parseWithSchema(
1664
- {
1665
- cartId: opts.cartId,
1666
- ...opts.paymentId ? { pgPaymentId: opts.paymentId } : {},
1667
- ...opts.provider ? { pgProvider: opts.provider } : {},
1668
- orderNumber: opts.orderNumber,
1669
- customerSnapshot
1670
- },
1671
- "checkout",
1672
- CheckoutRequestSchema
1673
- );
1674
- const { pgPaymentId, pgProvider, ...checkoutData } = parsed;
1675
- const data = pgPaymentId ? { ...checkoutData, pgPaymentId, pgProvider } : checkoutData;
1676
- if (opts.dryRun) {
1677
- printResult(
1678
- { dryRun: true, valid: true, action: "order checkout", data },
1679
- getFormat2()
1680
- );
1681
- return;
1682
- }
1683
- const client = getClient2();
1684
- const result = await client.commerce.orders.checkout(data);
1685
- printResult(result, getFormat2());
1686
- } catch (e) {
1687
- exitWithError(e);
1688
- }
1689
- });
1690
2665
  order.command("prepare <orderNumber>").description("Move paid order fulfillment work to preparation").option("--dry-run", "Validate inputs without executing").action(async (orderNumber, opts) => {
1691
2666
  try {
1692
2667
  const data = { orderNumber };
@@ -1770,8 +2745,8 @@ function registerOrderCommands(program2, getClient2, getFormat2) {
1770
2745
  }
1771
2746
 
1772
2747
  // src/commands/return.ts
1773
- import { z as z6 } from "zod";
1774
- var returnStatusSchema = z6.enum([
2748
+ import { z as z9 } from "zod";
2749
+ var returnStatusSchema = z9.enum([
1775
2750
  "processing",
1776
2751
  "approved",
1777
2752
  "rejected",
@@ -1900,146 +2875,227 @@ function registerReturnCommands(program2, getClient2, getFormat2) {
1900
2875
  });
1901
2876
  }
1902
2877
 
2878
+ // src/lib/sensitive-json-input.ts
2879
+ import { createReadStream } from "fs";
2880
+ import { stdin as processStdin } from "process";
2881
+ var SENSITIVE_JSON_MAX_BYTES = 1024 * 1024;
2882
+ function invalidInput(message) {
2883
+ return {
2884
+ type: "validation",
2885
+ code: "invalid_argument",
2886
+ field: "data",
2887
+ message,
2888
+ detail: { message, field: "data" }
2889
+ };
2890
+ }
2891
+ function isInvalidInput(error) {
2892
+ if (!error || typeof error !== "object") return false;
2893
+ const value = error;
2894
+ return value.type === "validation" && value.code === "invalid_argument";
2895
+ }
2896
+ async function readBoundedUtf8(source) {
2897
+ const chunks = [];
2898
+ let totalBytes = 0;
2899
+ for await (const chunk of source) {
2900
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
2901
+ totalBytes += buffer.byteLength;
2902
+ if (totalBytes > SENSITIVE_JSON_MAX_BYTES) {
2903
+ throw invalidInput(
2904
+ `Sensitive commerce data must be ${SENSITIVE_JSON_MAX_BYTES} bytes or fewer.`
2905
+ );
2906
+ }
2907
+ chunks.push(buffer);
2908
+ }
2909
+ return Buffer.concat(chunks, totalBytes).toString("utf8");
2910
+ }
2911
+ async function readSensitiveJsonObject(opts, sources = {}) {
2912
+ const sourceCount = [opts.dataStdin, opts.dataFile].filter(Boolean).length;
2913
+ if (sourceCount !== 1) {
2914
+ throw invalidInput(
2915
+ "Exactly one of --data-stdin or --data-file is required for sensitive commerce data."
2916
+ );
2917
+ }
2918
+ let raw;
2919
+ const input = sources.stdin ?? processStdin;
2920
+ try {
2921
+ if (opts.dataStdin) {
2922
+ if (input.isTTY === true) {
2923
+ throw invalidInput(
2924
+ "Sensitive commerce data cannot be read from an interactive terminal. Pipe JSON to --data-stdin or use --data-file."
2925
+ );
2926
+ }
2927
+ raw = await readBoundedUtf8(input);
2928
+ } else {
2929
+ const file = (sources.openFile ?? createReadStream)(opts.dataFile);
2930
+ try {
2931
+ raw = await readBoundedUtf8(file);
2932
+ } finally {
2933
+ file.destroy?.();
2934
+ }
2935
+ }
2936
+ } catch (error) {
2937
+ if (isInvalidInput(error)) throw error;
2938
+ throw invalidInput("Sensitive commerce data could not be read.");
2939
+ }
2940
+ let parsed;
2941
+ try {
2942
+ parsed = JSON.parse(raw);
2943
+ } catch {
2944
+ throw invalidInput("Sensitive commerce data must be valid JSON.");
2945
+ }
2946
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
2947
+ throw invalidInput("Sensitive commerce data must be a JSON object.");
2948
+ }
2949
+ return parsed;
2950
+ }
2951
+ function sensitiveDryRunResult(action) {
2952
+ return { dryRun: true, valid: true, action };
2953
+ }
2954
+
1903
2955
  // src/commands/cart.ts
1904
2956
  function registerCartCommands(program2, getClient2, getFormat2) {
1905
2957
  const cart = program2.command("cart").description("Cart management");
1906
- cart.command("create").description("Create a cart and print its cartToken handle").option("--dry-run", "Validate inputs without executing").action(async (opts) => {
2958
+ cart.command("create").description("Create a cart and print its cartToken capability").option("--data-stdin", "Read market and attributes JSON from stdin").option("--data-file <path>", "Read market and attributes JSON from file").option("--dry-run", "Validate inputs without executing").action(async (opts) => {
1907
2959
  try {
2960
+ const input = opts.dataStdin || opts.dataFile ? await readSensitiveJsonObject(opts) : {};
2961
+ const data = parseWithSchema(
2962
+ input,
2963
+ "cart create",
2964
+ createCartParamsSchema
2965
+ );
1908
2966
  if (opts.dryRun) {
1909
- printResult(
1910
- { dryRun: true, valid: true, action: "cart create", data: {} },
1911
- getFormat2()
1912
- );
2967
+ printResult(sensitiveDryRunResult("cart create"), getFormat2());
1913
2968
  return;
1914
2969
  }
1915
- const client = getClient2();
1916
- const result = await client.commerce.cart.create();
1917
- printResult(result, getFormat2());
1918
- } catch (e) {
1919
- exitWithError(e);
2970
+ printResult(await getClient2().commerce.carts.create(data), getFormat2());
2971
+ } catch (error) {
2972
+ exitWithError(error);
1920
2973
  }
1921
2974
  });
1922
- cart.command("add <cartToken>").description("Add an item to a cart (addressed by its cartToken)").requiredOption("--product <id>", "Product ID").requiredOption("--variant <id>", "Variant ID").requiredOption("--option <id>", "Option ID").requiredOption(
1923
- "--quantity <n>",
1924
- "Quantity",
1925
- (v) => parseInt(v, 10)
1926
- ).option("--dry-run", "Validate inputs without executing").action(async (cartToken, opts) => {
2975
+ cart.command("retrieve").description("Retrieve a cart by its capability token").option("--data-stdin", "Read the Cart capability JSON from stdin").option("--data-file <path>", "Read the Cart capability JSON from file").action(async (opts) => {
1927
2976
  try {
1928
- const data = {
1929
- cartToken,
1930
- product: opts.product,
1931
- variant: opts.variant,
1932
- option: opts.option,
1933
- quantity: opts.quantity
1934
- };
2977
+ const data = parseWithSchema(
2978
+ await readSensitiveJsonObject(opts),
2979
+ "cart retrieve",
2980
+ retrieveCartParamsSchema
2981
+ );
2982
+ printResult(
2983
+ await getClient2().commerce.carts.retrieve(data),
2984
+ getFormat2()
2985
+ );
2986
+ } catch (error) {
2987
+ exitWithError(error);
2988
+ }
2989
+ });
2990
+ cart.command("add").description("Add an item to a cart").option("--data-stdin", "Read capability and item JSON from stdin").option("--data-file <path>", "Read capability and item JSON from file").option("--dry-run", "Validate inputs without executing").action(async (opts) => {
2991
+ try {
2992
+ const data = parseWithSchema(
2993
+ await readSensitiveJsonObject(opts),
2994
+ "cart add",
2995
+ addItemParamsSchema
2996
+ );
1935
2997
  if (opts.dryRun) {
1936
- printResult(
1937
- { dryRun: true, valid: true, action: "cart add", data },
1938
- getFormat2()
1939
- );
2998
+ printResult(sensitiveDryRunResult("cart add"), getFormat2());
1940
2999
  return;
1941
3000
  }
1942
- const client = getClient2();
1943
- const result = await client.commerce.cart.addItem(data);
1944
- printResult(result, getFormat2());
1945
- } catch (e) {
1946
- exitWithError(e);
3001
+ printResult(await getClient2().commerce.carts.addItem(data), getFormat2());
3002
+ } catch (error) {
3003
+ exitWithError(error);
1947
3004
  }
1948
3005
  });
1949
- cart.command("update <cartItemId>").description("Update cart item quantity").requiredOption("--cart-token <token>", "Cart token (cart handle)").requiredOption(
1950
- "--quantity <n>",
1951
- "New quantity",
1952
- (v) => parseInt(v, 10)
1953
- ).option("--dry-run", "Validate inputs without executing").action(async (cartItemId, opts) => {
3006
+ cart.command("update").description("Update cart item quantity").option("--data-stdin", "Read capability and item JSON from stdin").option("--data-file <path>", "Read capability and item JSON from file").option("--dry-run", "Validate inputs without executing").action(async (opts) => {
1954
3007
  try {
1955
- const data = {
1956
- cartToken: opts.cartToken,
1957
- cartItemId,
1958
- quantity: opts.quantity
1959
- };
3008
+ const data = parseWithSchema(
3009
+ await readSensitiveJsonObject(opts),
3010
+ "cart update",
3011
+ updateItemParamsSchema
3012
+ );
1960
3013
  if (opts.dryRun) {
1961
- printResult(
1962
- { dryRun: true, valid: true, action: "cart update", data },
1963
- getFormat2()
1964
- );
3014
+ printResult(sensitiveDryRunResult("cart update"), getFormat2());
1965
3015
  return;
1966
3016
  }
1967
- const client = getClient2();
1968
- const result = await client.commerce.cart.updateItem(data);
1969
- printResult(result, getFormat2());
1970
- } catch (e) {
1971
- exitWithError(e);
3017
+ printResult(
3018
+ await getClient2().commerce.carts.updateItem(data),
3019
+ getFormat2()
3020
+ );
3021
+ } catch (error) {
3022
+ exitWithError(error);
1972
3023
  }
1973
3024
  });
1974
- cart.command("remove <cartItemId>").description("Remove an item from a cart").requiredOption("--cart-token <token>", "Cart token (cart handle)").option("--dry-run", "Validate inputs without executing").action(async (cartItemId, opts) => {
3025
+ cart.command("remove").description("Remove an item from a cart").option("--data-stdin", "Read capability and item JSON from stdin").option("--data-file <path>", "Read capability and item JSON from file").option("--dry-run", "Validate inputs without executing").action(async (opts) => {
1975
3026
  try {
1976
- const data = { cartToken: opts.cartToken, cartItemId };
1977
- if (opts.dryRun) {
1978
- printResult(
1979
- { dryRun: true, valid: true, action: "cart remove", data },
1980
- getFormat2()
1981
- );
3027
+ const data = parseWithSchema(
3028
+ await readSensitiveJsonObject(opts),
3029
+ "cart remove",
3030
+ removeItemParamsSchema
3031
+ );
3032
+ if (opts.dryRun) {
3033
+ printResult(sensitiveDryRunResult("cart remove"), getFormat2());
1982
3034
  return;
1983
3035
  }
1984
- const client = getClient2();
1985
- const result = await client.commerce.cart.removeItem(data);
1986
- printResult(result, getFormat2());
1987
- } catch (e) {
1988
- exitWithError(e);
3036
+ printResult(
3037
+ await getClient2().commerce.carts.removeItem(data),
3038
+ getFormat2()
3039
+ );
3040
+ } catch (error) {
3041
+ exitWithError(error);
1989
3042
  }
1990
3043
  });
1991
- cart.command("clear <cartToken>").description("Remove all items from a cart").option("--dry-run", "Validate inputs without executing").action(async (cartToken, opts) => {
3044
+ cart.command("clear").description("Remove all items from a cart").option("--data-stdin", "Read the Cart capability JSON from stdin").option("--data-file <path>", "Read the Cart capability JSON from file").option("--dry-run", "Validate inputs without executing").action(async (opts) => {
1992
3045
  try {
3046
+ const data = parseWithSchema(
3047
+ await readSensitiveJsonObject(opts),
3048
+ "cart clear",
3049
+ clearCartParamsSchema
3050
+ );
1993
3051
  if (opts.dryRun) {
1994
- printResult(
1995
- { dryRun: true, valid: true, action: "cart clear", data: { cartToken } },
1996
- getFormat2()
1997
- );
3052
+ printResult(sensitiveDryRunResult("cart clear"), getFormat2());
1998
3053
  return;
1999
3054
  }
2000
- const client = getClient2();
2001
- const result = await client.commerce.cart.clear({ cartToken });
2002
- printResult(result, getFormat2());
2003
- } catch (e) {
2004
- exitWithError(e);
3055
+ printResult(await getClient2().commerce.carts.clear(data), getFormat2());
3056
+ } catch (error) {
3057
+ exitWithError(error);
2005
3058
  }
2006
3059
  });
2007
- cart.command("apply-discount <cartToken>").description("Apply a discount code to a cart").requiredOption("--code <code>", "Discount code").option("--dry-run", "Validate inputs without executing").action(async (cartToken, opts) => {
3060
+ cart.command("discounts").description("Replace all cart discount codes; pass [] to clear").option("--data-stdin", "Read capability and discount JSON from stdin").option("--data-file <path>", "Read capability and discount JSON from file").option("--dry-run", "Validate inputs without executing").action(async (opts) => {
2008
3061
  try {
2009
- const data = { cartToken, discountCode: opts.code };
3062
+ const data = parseWithSchema(
3063
+ await readSensitiveJsonObject(opts),
3064
+ "cart discounts",
3065
+ updateDiscountCodesParamsSchema
3066
+ );
2010
3067
  if (opts.dryRun) {
2011
- printResult(
2012
- { dryRun: true, valid: true, action: "cart apply-discount", data },
2013
- getFormat2()
2014
- );
3068
+ printResult(sensitiveDryRunResult("cart discounts"), getFormat2());
2015
3069
  return;
2016
3070
  }
2017
- const client = getClient2();
2018
- const result = await client.commerce.cart.applyDiscount(data);
2019
- printResult(result, getFormat2());
2020
- } catch (e) {
2021
- exitWithError(e);
3071
+ printResult(
3072
+ await getClient2().commerce.carts.updateDiscountCodes(data),
3073
+ getFormat2()
3074
+ );
3075
+ } catch (error) {
3076
+ exitWithError(error);
2022
3077
  }
2023
3078
  });
2024
- cart.command("remove-discount <cartToken>").description("Remove the discount from a cart").option("--dry-run", "Validate inputs without executing").action(async (cartToken, opts) => {
3079
+ cart.command("attributes").description("Replace all public cart attributes; pass [] to clear").option("--data-stdin", "Read capability and attributes JSON from stdin").option(
3080
+ "--data-file <path>",
3081
+ "Read capability and attributes JSON from file"
3082
+ ).option("--dry-run", "Validate inputs without executing").action(async (opts) => {
2025
3083
  try {
3084
+ const data = parseWithSchema(
3085
+ await readSensitiveJsonObject(opts),
3086
+ "cart attributes",
3087
+ updateCartAttributesParamsSchema
3088
+ );
2026
3089
  if (opts.dryRun) {
2027
- printResult(
2028
- {
2029
- dryRun: true,
2030
- valid: true,
2031
- action: "cart remove-discount",
2032
- data: { cartToken }
2033
- },
2034
- getFormat2()
2035
- );
3090
+ printResult(sensitiveDryRunResult("cart attributes"), getFormat2());
2036
3091
  return;
2037
3092
  }
2038
- const client = getClient2();
2039
- const result = await client.commerce.cart.removeDiscount({ cartToken });
2040
- printResult(result, getFormat2());
2041
- } catch (e) {
2042
- exitWithError(e);
3093
+ printResult(
3094
+ await getClient2().commerce.carts.updateAttributes(data),
3095
+ getFormat2()
3096
+ );
3097
+ } catch (error) {
3098
+ exitWithError(error);
2043
3099
  }
2044
3100
  });
2045
3101
  }
@@ -2063,124 +3119,196 @@ function registerStockCommands(program2, getClient2, getFormat2) {
2063
3119
  }
2064
3120
 
2065
3121
  // src/commands/transaction.ts
2066
- import { z as z7 } from "zod";
2067
- var transactionAmountSchema = z7.object({
2068
- amount: z7.string().regex(/^[1-9]\d*$/, "amount must be a positive integer").transform(Number)
2069
- });
2070
- var confirmPaymentAmountSchema = z7.object({
2071
- amount: z7.string().regex(/^(0|[1-9]\d*)$/, "amount must be a non-negative integer").transform(Number)
2072
- });
2073
- var idempotencyKeyOptionSchema = z7.object({
2074
- idempotencyKey: idempotencyKeySchema
2075
- });
2076
- function parseTransactionAmount(value) {
2077
- if (value === void 0) return void 0;
2078
- return parseWithSchema({ amount: value }, "amount", transactionAmountSchema).amount;
2079
- }
2080
- function parseConfirmPaymentAmount(value) {
2081
- return parseWithSchema({ amount: value }, "amount", confirmPaymentAmountSchema).amount;
2082
- }
2083
- function parseIdempotencyKey(value) {
2084
- if (value === void 0) return void 0;
2085
- return parseWithSchema(
2086
- { idempotencyKey: value },
2087
- "idempotencyKey",
2088
- idempotencyKeyOptionSchema
2089
- ).idempotencyKey;
2090
- }
2091
- function registerTransactionCommands(program2, getClient2, getFormat2) {
2092
- const tx = program2.command("transaction").description("Transaction management");
2093
- tx.command("update").description("Update transaction status").requiredOption("--payment-id <id>", "Payment ID").requiredOption(
2094
- "--status <status>",
2095
- "New status (pending, paid, failed, canceled, refunded)"
2096
- ).option("--payment-method <method>", "Payment method").option("--receipt-url <url>", "Receipt URL").option("--payment-key <key>", "Provider payment key").option("--amount <n>", "Provider-confirmed amount").option("--dry-run", "Validate inputs without executing").action(async (opts) => {
3122
+ function registerCommerceSessionCommands(program2, getClient2, getFormat2) {
3123
+ const checkout = program2.command("checkout").description("Checkout management");
3124
+ checkout.command("create").description("Create a Checkout from a server-authoritative Cart").option("--data-stdin", "Read capability and customer JSON from stdin").option("--data-file <path>", "Read capability and customer JSON from file").option("--dry-run", "Validate inputs without executing").action(async (opts) => {
2097
3125
  try {
2098
3126
  const data = parseWithSchema(
2099
- {
2100
- pgPaymentId: opts.paymentId,
2101
- status: opts.status,
2102
- paymentMethod: opts.paymentMethod,
2103
- receiptUrl: opts.receiptUrl,
2104
- paymentKey: opts.paymentKey,
2105
- amount: parseTransactionAmount(opts.amount)
2106
- },
2107
- "transaction",
2108
- updateTransactionSchema
3127
+ await readSensitiveJsonObject(opts),
3128
+ "checkout",
3129
+ CreateServerCheckoutParamsSchema
3130
+ );
3131
+ if (opts.dryRun) {
3132
+ printResult(sensitiveDryRunResult("checkout create"), getFormat2());
3133
+ return;
3134
+ }
3135
+ printResult(
3136
+ await getClient2().commerce.checkouts.create(data),
3137
+ getFormat2()
3138
+ );
3139
+ } catch (error) {
3140
+ exitWithError(error);
3141
+ }
3142
+ });
3143
+ checkout.command("retrieve <checkoutId>").description("Retrieve a Checkout by ID with server credentials").action(async (checkoutId) => {
3144
+ try {
3145
+ printResult(
3146
+ await getClient2().commerce.checkouts.retrieve({ checkoutId }),
3147
+ getFormat2()
3148
+ );
3149
+ } catch (error) {
3150
+ exitWithError(error);
3151
+ }
3152
+ });
3153
+ checkout.command("query").description("Query lightweight Checkouts with server credentials").option("--params <json>", "Checkout query parameters (JSON)", "{}").action(async (opts) => {
3154
+ try {
3155
+ const data = parseWithSchema(
3156
+ parseJson(opts.params, "params"),
3157
+ "checkout query",
3158
+ CanonicalCheckoutQuerySchema
3159
+ );
3160
+ printResult(
3161
+ await getClient2().commerce.checkouts.query(data),
3162
+ getFormat2()
3163
+ );
3164
+ } catch (error) {
3165
+ exitWithError(error);
3166
+ }
3167
+ });
3168
+ const sessions = program2.command("payment-session").description("PaymentSession management");
3169
+ sessions.command("create").description("Create a canonical provider-configured PaymentSession").option("--data-stdin", "Read canonical create parameters from stdin").option("--data-file <path>", "Read canonical create parameters from file").option("--dry-run", "Validate inputs without executing").action(async (opts) => {
3170
+ try {
3171
+ const data = parseWithSchema(
3172
+ await readSensitiveJsonObject(opts),
3173
+ "payment-session",
3174
+ CreatePaymentSessionParamsSchema
2109
3175
  );
2110
3176
  if (opts.dryRun) {
2111
3177
  printResult(
2112
- { dryRun: true, valid: true, action: "transaction update", data },
3178
+ sensitiveDryRunResult("payment-session create"),
2113
3179
  getFormat2()
2114
3180
  );
2115
3181
  return;
2116
3182
  }
2117
- const client = getClient2();
2118
- const result = await client.commerce.orders.updateTransaction(data);
2119
- printResult(result, getFormat2());
2120
- } catch (e) {
2121
- exitWithError(e);
3183
+ printResult(
3184
+ await getClient2().commerce.paymentSessions.create(data),
3185
+ getFormat2()
3186
+ );
3187
+ } catch (error) {
3188
+ exitWithError(error);
3189
+ }
3190
+ });
3191
+ sessions.command("retrieve <paymentSessionId>").description("Retrieve a PaymentSession").action(async (paymentSessionId) => {
3192
+ try {
3193
+ printResult(
3194
+ await getClient2().commerce.paymentSessions.retrieve({
3195
+ paymentSessionId
3196
+ }),
3197
+ getFormat2()
3198
+ );
3199
+ } catch (error) {
3200
+ exitWithError(error);
2122
3201
  }
2123
3202
  });
2124
- tx.command("confirm-payment").description("Confirm a provider-verified payment for an order").requiredOption("--payment-id <id>", "Provider payment identifier (pgPaymentId)").requiredOption(
2125
- "--provider <slug>",
2126
- "Payment provider slug (toss, portone, stripe, ...)"
2127
- ).requiredOption(
2128
- "--amount <n>",
2129
- "Provider-confirmed amount in minor units"
2130
- ).option("--order-number <num>", "Order number").option("--pg-order-id <id>", "Provider order id").option("--currency <code>", "Currency code").option("--payment-method <method>", "Payment method").option("--receipt-url <url>", "Receipt URL").option("--approved-at <ts>", "Provider approval timestamp").option("--provider-status <status>", "Provider status").option("--provider-event-id <id>", "Provider event id (idempotency)").option("--idempotency-key <key>", "Idempotency key header override").option(
2131
- "--confirmation-source <src>",
2132
- "provider_webhook | provider_lookup | provider_api_confirm | manual_server"
2133
- ).option("--payment-key <key>", "Provider payment key").option("--dry-run", "Validate inputs without executing").action(async (opts) => {
3203
+ sessions.command("cancel").description("Cancel a PaymentSession and release its payment slot").option("--data-stdin", "Read session ID and idempotency key from stdin").option(
3204
+ "--data-file <path>",
3205
+ "Read session ID and idempotency key from file"
3206
+ ).option("--dry-run", "Validate inputs without executing").action(async (opts) => {
2134
3207
  try {
2135
3208
  const data = parseWithSchema(
2136
- {
2137
- pgPaymentId: opts.paymentId,
2138
- pgProvider: opts.provider,
2139
- amount: parseConfirmPaymentAmount(opts.amount),
2140
- ...opts.orderNumber ? { orderNumber: opts.orderNumber } : {},
2141
- ...opts.pgOrderId ? { pgOrderId: opts.pgOrderId } : {},
2142
- ...opts.currency ? { currency: opts.currency } : {},
2143
- ...opts.paymentMethod ? { paymentMethod: opts.paymentMethod } : {},
2144
- ...opts.receiptUrl ? { receiptUrl: opts.receiptUrl } : {},
2145
- ...opts.approvedAt ? { approvedAt: opts.approvedAt } : {},
2146
- ...opts.providerStatus ? { providerStatus: opts.providerStatus } : {},
2147
- ...opts.providerEventId ? { providerEventId: opts.providerEventId } : {},
2148
- ...opts.confirmationSource ? { confirmationSource: opts.confirmationSource } : {},
2149
- ...opts.paymentKey ? { paymentKey: opts.paymentKey } : {}
2150
- },
2151
- "confirm-payment",
2152
- confirmPaymentSchema
3209
+ await readSensitiveJsonObject(opts),
3210
+ "payment-session cancel",
3211
+ CancelServerPaymentSessionParamsSchema
2153
3212
  );
2154
- const sdkParams = {
2155
- ...data,
2156
- ...opts.idempotencyKey !== void 0 ? { idempotencyKey: parseIdempotencyKey(opts.idempotencyKey) } : {}
2157
- };
2158
3213
  if (opts.dryRun) {
2159
3214
  printResult(
2160
- {
2161
- dryRun: true,
2162
- valid: true,
2163
- action: "transaction confirm-payment",
2164
- data: sdkParams
2165
- },
3215
+ sensitiveDryRunResult("payment-session cancel"),
2166
3216
  getFormat2()
2167
3217
  );
2168
3218
  return;
2169
3219
  }
2170
- const client = getClient2();
2171
- const result = await client.commerce.orders.confirmPayment(
2172
- sdkParams
3220
+ printResult(
3221
+ await getClient2().commerce.paymentSessions.cancel(data),
3222
+ getFormat2()
2173
3223
  );
2174
- printResult(result, getFormat2());
2175
- } catch (e) {
2176
- exitWithError(e);
3224
+ } catch (error) {
3225
+ exitWithError(error);
3226
+ }
3227
+ });
3228
+ sessions.command("confirm-toss").description("Confirm a Toss authorization with the typed server action").option("--data-stdin", "Read Toss confirmation params from stdin").option("--data-file <path>", "Read Toss confirmation params from file").option("--dry-run", "Validate inputs without executing").action(async (opts) => {
3229
+ try {
3230
+ const data = parseWithSchema(
3231
+ await readSensitiveJsonObject(opts),
3232
+ "payment-session confirm-toss",
3233
+ ConfirmTossPaymentSessionParamsSchema
3234
+ );
3235
+ if (opts.dryRun) {
3236
+ printResult(
3237
+ sensitiveDryRunResult("payment-session confirm-toss"),
3238
+ getFormat2()
3239
+ );
3240
+ return;
3241
+ }
3242
+ printResult(
3243
+ await getClient2().commerce.paymentSessions.confirm(data),
3244
+ getFormat2()
3245
+ );
3246
+ } catch (error) {
3247
+ exitWithError(error);
2177
3248
  }
2178
3249
  });
2179
3250
  }
2180
3251
 
3252
+ // src/lib/product-detail-options.ts
3253
+ function invalidOption(message, field, value) {
3254
+ const error = new Error(message);
3255
+ Object.assign(error, {
3256
+ type: "validation",
3257
+ code: "invalid_argument",
3258
+ field,
3259
+ detail: { message, value, field }
3260
+ });
3261
+ return error;
3262
+ }
3263
+ function parseLocale(value) {
3264
+ if (value == null) return void 0;
3265
+ if (value === "ko" || value === "en" || value === "all") return value;
3266
+ throw invalidOption("--locale must be one of: ko, en, all", "locale", value);
3267
+ }
3268
+ function parseFallbackLocale(value) {
3269
+ if (value == null) return void 0;
3270
+ if (value === "ko" || value === "en") return value;
3271
+ if (value === "none") return false;
3272
+ throw invalidOption(
3273
+ "--fallback-locale must be one of: ko, en, none",
3274
+ "fallbackLocale",
3275
+ value
3276
+ );
3277
+ }
3278
+ function productDetailParamsFromOptions(options) {
3279
+ const hasId = typeof options.id === "string" && options.id.length > 0;
3280
+ const hasSlug = typeof options.slug === "string" && options.slug.length > 0;
3281
+ if (hasId === hasSlug) {
3282
+ throw invalidOption(
3283
+ "Exactly one of --id or --slug is required.",
3284
+ "product",
3285
+ { id: options.id, slug: options.slug }
3286
+ );
3287
+ }
3288
+ const locale = parseLocale(options.locale);
3289
+ const fallbackLocale = parseFallbackLocale(options.fallbackLocale);
3290
+ const identifier = hasId ? { id: options.id } : { slug: options.slug };
3291
+ return {
3292
+ ...identifier,
3293
+ ...options.market !== void 0 ? { market: options.market } : {},
3294
+ ...locale !== void 0 ? { locale } : {},
3295
+ ...fallbackLocale !== void 0 ? { fallbackLocale } : {}
3296
+ };
3297
+ }
3298
+
2181
3299
  // src/commands/product.ts
2182
3300
  function registerProductCommands(program2, getClient2, getFormat2) {
2183
3301
  const product = program2.command("product").description("Product management");
3302
+ product.command("detail").description("Get shaped product detail by slug or ID").option("--slug <slug>", "Product slug").option("--id <id>", "Product ID").option("--market <market>", "Market slug").option("--locale <locale>", "Locale: ko, en, or all").option("--fallback-locale <locale>", "Fallback locale: ko, en, or none").action(async (opts) => {
3303
+ try {
3304
+ const params = productDetailParamsFromOptions(opts);
3305
+ const client = getClient2();
3306
+ const result = await client.commerce.product.detail(params);
3307
+ printResult(result, getFormat2());
3308
+ } catch (e) {
3309
+ exitWithError(e);
3310
+ }
3311
+ });
2184
3312
  product.command("upsert").description("Create or update a product graph (options, values, variants)").requiredOption("--input <json>", "Product upsert input (JSON)").option("--dry-run", "Validate inputs without executing").action(async (opts) => {
2185
3313
  try {
2186
3314
  const data = parseWithSchema(
@@ -2234,12 +3362,14 @@ function registerShippingCommands(program2, getClient2, getFormat2) {
2234
3362
  "--order-amount <n>",
2235
3363
  "Order amount",
2236
3364
  (v) => parseInt(v, 10)
2237
- ).option("--shipping-policy-id <id>", "Shipping policy ID").option("--postal-code <code>", "Destination postal code").action(async (opts) => {
3365
+ ).option("--shipping-policy-id <id>", "Shipping policy ID").option("--country-code <code>", "Destination country code").option("--region-code <code>", "Destination region code").option("--postal-code <code>", "Destination postal code").action(async (opts) => {
2238
3366
  try {
2239
3367
  const client = getClient2();
2240
3368
  const result = await client.commerce.shipping.calculate({
2241
3369
  orderAmount: opts.orderAmount,
2242
3370
  ...opts.shippingPolicyId ? { shippingPolicyId: opts.shippingPolicyId } : {},
3371
+ ...opts.countryCode ? { countryCode: opts.countryCode } : {},
3372
+ ...opts.regionCode ? { regionCode: opts.regionCode } : {},
2243
3373
  ...opts.postalCode ? { postalCode: opts.postalCode } : {}
2244
3374
  });
2245
3375
  printResult(result, getFormat2());
@@ -2250,7 +3380,7 @@ function registerShippingCommands(program2, getClient2, getFormat2) {
2250
3380
  }
2251
3381
 
2252
3382
  // src/commands/auth.ts
2253
- import { randomBytes } from "crypto";
3383
+ import { randomBytes as randomBytes2 } from "crypto";
2254
3384
  import { createServer } from "http";
2255
3385
  import { execFile, exec } from "child_process";
2256
3386
  import { platform } from "os";
@@ -2425,7 +3555,7 @@ Logged in successfully!`));
2425
3555
  }
2426
3556
  function registerAuthCommands(program2) {
2427
3557
  program2.command("login").description("Login via browser and store credentials").action(async () => {
2428
- const state = randomBytes(32).toString("hex");
3558
+ const state = randomBytes2(32).toString("hex");
2429
3559
  try {
2430
3560
  const { port } = await startAuthServer({
2431
3561
  state,
@@ -2514,7 +3644,7 @@ ${loginUrl}`));
2514
3644
  );
2515
3645
  process.exit(3);
2516
3646
  }
2517
- const state = randomBytes(32).toString("hex");
3647
+ const state = randomBytes2(32).toString("hex");
2518
3648
  const isLocal = !!opts.local;
2519
3649
  try {
2520
3650
  const { port } = await startAuthServer({
@@ -2613,9 +3743,15 @@ function registerFeatureCommands(program2, getClient2, getFormat2) {
2613
3743
  }
2614
3744
 
2615
3745
  // src/commands/agent.ts
3746
+ import { SERVER_COLLECTIONS as SERVER_COLLECTIONS4 } from "@01.software/sdk";
3747
+
3748
+ // src/lib/agent-plan-allowlist.ts
2616
3749
  import { SERVER_COLLECTIONS as SERVER_COLLECTIONS3 } from "@01.software/sdk";
2617
3750
 
2618
3751
  // src/lib/agent-output.ts
3752
+ import { randomBytes as randomBytes3 } from "crypto";
3753
+ import fs from "fs";
3754
+ import path from "path";
2619
3755
  var PLAN_AGENT_ERROR_CODES = /* @__PURE__ */ new Set([
2620
3756
  "PLAN_EXPIRED",
2621
3757
  "PLAN_MISMATCH",
@@ -2632,9 +3768,95 @@ function agentPlanError(code, message) {
2632
3768
  });
2633
3769
  return error;
2634
3770
  }
3771
+ var AGENT_OUTPUT_FILE_MODE = 384;
3772
+ var isWin32 = process.platform === "win32";
2635
3773
  function stringifyAgentJson(data, options = {}) {
2636
3774
  return options.pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data);
2637
3775
  }
3776
+ function invalidOutputTarget(message, outputFile) {
3777
+ const error = new Error(message);
3778
+ Object.assign(error, {
3779
+ type: "validation",
3780
+ code: "invalid_argument",
3781
+ field: "outputFile",
3782
+ detail: { message, field: "outputFile", value: outputFile }
3783
+ });
3784
+ return error;
3785
+ }
3786
+ function assertSafeAgentOutputTarget(outputFile) {
3787
+ let stat;
3788
+ try {
3789
+ stat = fs.lstatSync(outputFile);
3790
+ } catch (error) {
3791
+ if (error.code === "ENOENT") return;
3792
+ throw error;
3793
+ }
3794
+ if (stat.isSymbolicLink()) {
3795
+ throw invalidOutputTarget(
3796
+ `Refusing to write Agent output through a symlink: ${outputFile}.`,
3797
+ outputFile
3798
+ );
3799
+ }
3800
+ if (!stat.isFile()) {
3801
+ throw invalidOutputTarget(
3802
+ `Refusing to write Agent output to a non-regular file: ${outputFile}.`,
3803
+ outputFile
3804
+ );
3805
+ }
3806
+ if (!isWin32 && stat.nlink > 1) {
3807
+ throw invalidOutputTarget(
3808
+ `Refusing to write Agent output to a file with multiple hard links: ${outputFile}.`,
3809
+ outputFile
3810
+ );
3811
+ }
3812
+ }
3813
+ function writeAgentOutputFile(requestedPath, content) {
3814
+ const outputFile = path.resolve(requestedPath);
3815
+ assertSafeAgentOutputTarget(outputFile);
3816
+ const tempFile = path.join(
3817
+ path.dirname(outputFile),
3818
+ `.${path.basename(outputFile)}.${randomBytes3(12).toString("hex")}.tmp`
3819
+ );
3820
+ let tempFd;
3821
+ try {
3822
+ tempFd = fs.openSync(tempFile, "wx", AGENT_OUTPUT_FILE_MODE);
3823
+ if (!isWin32) {
3824
+ fs.fchmodSync(tempFd, AGENT_OUTPUT_FILE_MODE);
3825
+ }
3826
+ fs.writeFileSync(tempFd, content, { encoding: "utf8" });
3827
+ fs.closeSync(tempFd);
3828
+ tempFd = void 0;
3829
+ fs.renameSync(tempFile, outputFile);
3830
+ } catch (error) {
3831
+ if (tempFd !== void 0) {
3832
+ try {
3833
+ fs.closeSync(tempFd);
3834
+ } catch {
3835
+ }
3836
+ }
3837
+ try {
3838
+ fs.unlinkSync(tempFile);
3839
+ } catch {
3840
+ }
3841
+ throw error;
3842
+ }
3843
+ return {
3844
+ outputFile,
3845
+ bytes: Buffer.byteLength(content, "utf8")
3846
+ };
3847
+ }
3848
+ function resolveAgentPrintOptions(command) {
3849
+ const options = command.optsWithGlobals();
3850
+ return {
3851
+ pretty: Boolean(options.pretty),
3852
+ outputFile: options.outputFile
3853
+ };
3854
+ }
3855
+ function validateAgentOutputTarget(options) {
3856
+ if (options.outputFile !== void 0) {
3857
+ assertSafeAgentOutputTarget(path.resolve(options.outputFile));
3858
+ }
3859
+ }
2638
3860
  function asRecord(value) {
2639
3861
  return value && typeof value === "object" ? value : null;
2640
3862
  }
@@ -2660,6 +3882,20 @@ function isRawNotFound(error) {
2660
3882
  const raw = asRecord(error);
2661
3883
  return raw?.status === 404 || raw?.code === "not_found" || raw?.name === "GoneError";
2662
3884
  }
3885
+ function isSdkUsageLimitError(error) {
3886
+ const raw = asRecord(error);
3887
+ return raw?.name === "UsageLimitError" || raw?.code === "USAGE_LIMIT_ERROR";
3888
+ }
3889
+ function agentErrorEnvelope(code, message, retryable, retryAfter) {
3890
+ return {
3891
+ error: {
3892
+ code,
3893
+ message,
3894
+ retryable,
3895
+ ...typeof retryAfter === "number" ? { retryAfter } : {}
3896
+ }
3897
+ };
3898
+ }
2663
3899
  function agentErrorExitCode(code) {
2664
3900
  switch (code) {
2665
3901
  case "INVALID_INPUT":
@@ -2683,22 +3919,30 @@ function classifyAgentError(error) {
2683
3919
  const detailRecord = detail && typeof detail === "object" ? detail : null;
2684
3920
  const agentCode = raw?.agentCode ?? detailRecord?.agentCode;
2685
3921
  if (typeof agentCode === "string" && PLAN_AGENT_ERROR_CODES.has(agentCode)) {
2686
- return { error: { code: agentCode, message } };
3922
+ return agentErrorEnvelope(agentCode, message, false);
2687
3923
  }
2688
3924
  const adminError = classifyError(error);
2689
3925
  if (isRawNotFound(error) || adminError.code === "not_found") {
2690
- return { error: { code: "NOT_FOUND", message } };
3926
+ return agentErrorEnvelope("NOT_FOUND", message, false);
2691
3927
  }
2692
3928
  if (adminError.type === "permission") {
2693
- return { error: { code: "AUTH_FAILED", message } };
3929
+ return agentErrorEnvelope("AUTH_FAILED", message, false);
2694
3930
  }
2695
3931
  if (adminError.type === "validation" && adminError.code !== "unknown") {
2696
- return { error: { code: "INVALID_INPUT", message } };
3932
+ return agentErrorEnvelope("INVALID_INPUT", message, false);
2697
3933
  }
2698
- return { error: { code: "INTERNAL", message } };
3934
+ const retryable = !isSdkUsageLimitError(error) && (adminError.type === "network" || adminError.type === "degraded");
3935
+ const retryAfter = retryable && adminError.type === "degraded" && adminError.code === "rate_limited" ? adminError.retryAfter : void 0;
3936
+ return agentErrorEnvelope("INTERNAL", message, retryable, retryAfter);
2699
3937
  }
2700
3938
  function printAgentSuccess(data, options = {}) {
2701
- console.log(stringifyAgentJson(data, options));
3939
+ const output = stringifyAgentJson(data, options);
3940
+ if (options.outputFile !== void 0) {
3941
+ const receipt = writeAgentOutputFile(options.outputFile, output);
3942
+ console.log(JSON.stringify(receipt));
3943
+ return;
3944
+ }
3945
+ console.log(output);
2702
3946
  }
2703
3947
  function printAgentError(error, options = {}) {
2704
3948
  const envelope = classifyAgentError(error);
@@ -2711,25 +3955,77 @@ function exitWithAgentError(error, options = {}) {
2711
3955
  }
2712
3956
 
2713
3957
  // src/lib/agent-plan-allowlist.ts
2714
- var AGENT_PLAN_COLLECTIONS = [
2715
- "articles",
2716
- "article-categories",
2717
- "article-tags",
2718
- "links",
2719
- "link-categories",
2720
- "link-tags"
2721
- ];
2722
- var AGENT_PLAN_COLLECTION_SET = new Set(AGENT_PLAN_COLLECTIONS);
3958
+ var AGENT_PLAN_CAPABILITIES = {
3959
+ articles: ["create", "update", "delete"],
3960
+ "article-categories": ["create", "update", "delete"],
3961
+ "article-tags": ["create", "update", "delete"],
3962
+ links: ["create", "update", "delete"],
3963
+ "link-categories": ["create", "update", "delete"],
3964
+ "link-tags": ["create", "update", "delete"],
3965
+ documents: ["create", "update"],
3966
+ "document-categories": ["create", "update"],
3967
+ "document-types": ["create", "update"],
3968
+ canvases: ["create", "update", "delete"],
3969
+ "canvas-categories": ["create", "update", "delete"],
3970
+ "canvas-tags": ["create", "update", "delete"]
3971
+ };
3972
+ var SERVER_COLLECTION_SET = new Set(SERVER_COLLECTIONS3);
3973
+ var AGENT_PLAN_OPERATIONS = /* @__PURE__ */ new Set([
3974
+ "create",
3975
+ "update",
3976
+ "delete"
3977
+ ]);
3978
+ function assertAgentPlanCapabilityMatrix(matrix) {
3979
+ for (const [collection, operations] of Object.entries(matrix)) {
3980
+ if (!SERVER_COLLECTION_SET.has(collection)) {
3981
+ throw new Error(
3982
+ `Agent plan capability collection "${collection}" is not a ServerCollection.`
3983
+ );
3984
+ }
3985
+ if (operations.length === 0) {
3986
+ throw new Error(
3987
+ `Agent plan capability collection "${collection}" has no operations.`
3988
+ );
3989
+ }
3990
+ const seen = /* @__PURE__ */ new Set();
3991
+ for (const operation of operations) {
3992
+ if (!AGENT_PLAN_OPERATIONS.has(operation)) {
3993
+ throw new Error(
3994
+ `Agent plan capability operation "${operation}" is invalid for "${collection}".`
3995
+ );
3996
+ }
3997
+ if (seen.has(operation)) {
3998
+ throw new Error(
3999
+ `Agent plan capability operation "${operation}" is duplicated for "${collection}".`
4000
+ );
4001
+ }
4002
+ seen.add(operation);
4003
+ }
4004
+ }
4005
+ }
4006
+ assertAgentPlanCapabilityMatrix(AGENT_PLAN_CAPABILITIES);
2723
4007
  function isAgentPlanCollection(collection) {
2724
- return AGENT_PLAN_COLLECTION_SET.has(collection);
4008
+ return Object.prototype.hasOwnProperty.call(
4009
+ AGENT_PLAN_CAPABILITIES,
4010
+ collection
4011
+ );
4012
+ }
4013
+ function getAgentPlanOperations(collection) {
4014
+ return isAgentPlanCollection(collection) ? AGENT_PLAN_CAPABILITIES[collection] : [];
2725
4015
  }
2726
- function assertAgentPlanCollection(collection) {
2727
- if (isAgentPlanCollection(collection)) {
4016
+ function isAgentPlanCapability(collection, operation) {
4017
+ if (!isAgentPlanCollection(collection)) return false;
4018
+ return AGENT_PLAN_CAPABILITIES[collection].includes(
4019
+ operation
4020
+ );
4021
+ }
4022
+ function assertAgentPlanCapability(collection, operation) {
4023
+ if (isAgentPlanCapability(collection, operation)) {
2728
4024
  return collection;
2729
4025
  }
2730
4026
  throw agentPlanError(
2731
4027
  "UNSUPPORTED_OPERATION",
2732
- `Collection "${collection}" is not supported for agent plan mutations.`
4028
+ `Operation "${operation}" is not supported for collection "${collection}" in agent plan mutations.`
2733
4029
  );
2734
4030
  }
2735
4031
 
@@ -2832,6 +4128,12 @@ function parseStoredAgentPlan(raw, planId) {
2832
4128
  if (operation !== "create" && operation !== "update" && operation !== "delete") {
2833
4129
  throw agentPlanError("PLAN_MISMATCH", "Stored plan has invalid operation.");
2834
4130
  }
4131
+ if (!isAgentPlanCapability(collection, operation)) {
4132
+ throw agentPlanError(
4133
+ "PLAN_MISMATCH",
4134
+ "Stored plan has unsupported collection operation."
4135
+ );
4136
+ }
2835
4137
  const payload = record.payload == null ? void 0 : typeof record.payload === "object" && !Array.isArray(record.payload) ? record.payload : (() => {
2836
4138
  throw agentPlanError("PLAN_MISMATCH", "Stored plan payload is invalid.");
2837
4139
  })();
@@ -2856,6 +4158,12 @@ function parseStoredAgentPlan(raw, planId) {
2856
4158
  };
2857
4159
  }
2858
4160
  function assertStoredPlanHashMatches(plan) {
4161
+ if (!isAgentPlanCapability(plan.collection, plan.operation)) {
4162
+ throw agentPlanError(
4163
+ "PLAN_MISMATCH",
4164
+ "Stored plan has unsupported collection operation."
4165
+ );
4166
+ }
2859
4167
  const expected = hashAgentPlanEnvelope({
2860
4168
  operation: plan.operation,
2861
4169
  collection: plan.collection,
@@ -3076,10 +4384,10 @@ function isSecretFieldName(key) {
3076
4384
  const snake = key.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase();
3077
4385
  return SECRET_KEY.test(snake);
3078
4386
  }
3079
- function assertNoSecretFields(value, path = "") {
4387
+ function assertNoSecretFields(value, path2 = "") {
3080
4388
  if (Array.isArray(value)) {
3081
4389
  value.forEach((item, index) => {
3082
- assertNoSecretFields(item, `${path}[${index}]`);
4390
+ assertNoSecretFields(item, `${path2}[${index}]`);
3083
4391
  });
3084
4392
  return;
3085
4393
  }
@@ -3087,7 +4395,7 @@ function assertNoSecretFields(value, path = "") {
3087
4395
  return;
3088
4396
  }
3089
4397
  for (const [key, child] of Object.entries(value)) {
3090
- const fieldPath = path ? `${path}.${key}` : key;
4398
+ const fieldPath = path2 ? `${path2}.${key}` : key;
3091
4399
  if (isSecretFieldName(key)) {
3092
4400
  throw agentPlanError(
3093
4401
  "INVALID_INPUT",
@@ -3129,7 +4437,7 @@ function buildConfirmCommand(planToken, planHash) {
3129
4437
  }
3130
4438
 
3131
4439
  // src/commands/agent-plan.ts
3132
- function invalidInput(message, field) {
4440
+ function invalidInput2(message, field) {
3133
4441
  const error = new Error(message);
3134
4442
  Object.assign(error, {
3135
4443
  type: "validation",
@@ -3143,7 +4451,7 @@ function handleCommanderError(error) {
3143
4451
  if (error.code === "commander.helpDisplayed") {
3144
4452
  process.exit(error.exitCode);
3145
4453
  }
3146
- exitWithAgentError(invalidInput(error.message, "command"));
4454
+ exitWithAgentError(invalidInput2(error.message, "command"));
3147
4455
  }
3148
4456
  function configureAgentParser(command) {
3149
4457
  command.configureOutput({
@@ -3161,9 +4469,12 @@ async function captureBaseUpdatedAt(client, collection, documentId) {
3161
4469
  return readDocumentUpdatedAt(doc);
3162
4470
  }
3163
4471
  async function runPlanAction(input) {
3164
- const collection = assertAgentPlanCollection(input.collection);
4472
+ const collection = assertAgentPlanCapability(
4473
+ input.collection,
4474
+ input.operation
4475
+ );
3165
4476
  if (input.operation !== "create" && !input.documentId) {
3166
- throw invalidInput("Missing required argument: id", "id");
4477
+ throw invalidInput2("Missing required argument: id", "id");
3167
4478
  }
3168
4479
  let payload;
3169
4480
  if (input.operation === "create" || input.operation === "update") {
@@ -3225,7 +4536,7 @@ async function runPlanAction(input) {
3225
4536
  documentId: stored.documentId,
3226
4537
  confirmCommand: buildConfirmCommand(planToken, stored.planHash)
3227
4538
  },
3228
- { pretty: Boolean(input.pretty) }
4539
+ input.outputOptions
3229
4540
  );
3230
4541
  }
3231
4542
  function registerAgentPlanCommands(agent, getClient2) {
@@ -3233,63 +4544,83 @@ function registerAgentPlanCommands(agent, getClient2) {
3233
4544
  agent.command("plan").description("Plan a mutation without executing")
3234
4545
  );
3235
4546
  configureAgentParser(
3236
- plan.command("create <collection>").description("Plan a document create").option("--data-stdin", "Read mutation JSON from stdin").option("--data-file <path>", "Read mutation JSON from file").option("--pretty", "Print 2-space indented JSON").action(
3237
- async (collection, opts) => {
4547
+ plan.command("create <collection>").description("Plan a document create").option("--data-stdin", "Read mutation JSON from stdin").option("--data-file <path>", "Read mutation JSON from file").option("--pretty", "Print 2-space indented success JSON").option(
4548
+ "--output-file <path>",
4549
+ "Write success JSON to a secure file and print a receipt"
4550
+ ).action(
4551
+ async (collection, opts, command) => {
4552
+ const outputOptions = resolveAgentPrintOptions(command);
3238
4553
  try {
4554
+ validateAgentOutputTarget(outputOptions);
3239
4555
  await runPlanAction({
3240
4556
  operation: "create",
3241
4557
  collection,
3242
4558
  dataStdin: opts.dataStdin,
3243
4559
  dataFile: opts.dataFile,
3244
- pretty: opts.pretty,
4560
+ outputOptions,
3245
4561
  getClient: getClient2
3246
4562
  });
3247
4563
  } catch (error) {
3248
- exitWithAgentError(error, { pretty: Boolean(opts.pretty) });
4564
+ exitWithAgentError(error, outputOptions);
3249
4565
  }
3250
4566
  }
3251
4567
  )
3252
4568
  );
3253
4569
  configureAgentParser(
3254
- plan.command("update <collection> <id>").description("Plan a document update").option("--data-stdin", "Read mutation JSON from stdin").option("--data-file <path>", "Read mutation JSON from file").option("--pretty", "Print 2-space indented JSON").action(
3255
- async (collection, id, opts) => {
4570
+ plan.command("update <collection> <id>").description("Plan a document update").option("--data-stdin", "Read mutation JSON from stdin").option("--data-file <path>", "Read mutation JSON from file").option("--pretty", "Print 2-space indented success JSON").option(
4571
+ "--output-file <path>",
4572
+ "Write success JSON to a secure file and print a receipt"
4573
+ ).action(
4574
+ async (collection, id, opts, command) => {
4575
+ const outputOptions = resolveAgentPrintOptions(command);
3256
4576
  try {
4577
+ validateAgentOutputTarget(outputOptions);
3257
4578
  await runPlanAction({
3258
4579
  operation: "update",
3259
4580
  collection,
3260
4581
  documentId: id,
3261
4582
  dataStdin: opts.dataStdin,
3262
4583
  dataFile: opts.dataFile,
3263
- pretty: opts.pretty,
4584
+ outputOptions,
3264
4585
  getClient: getClient2
3265
4586
  });
3266
4587
  } catch (error) {
3267
- exitWithAgentError(error, { pretty: Boolean(opts.pretty) });
4588
+ exitWithAgentError(error, outputOptions);
3268
4589
  }
3269
4590
  }
3270
4591
  )
3271
4592
  );
3272
4593
  configureAgentParser(
3273
- plan.command("delete <collection> <id>").description("Plan a document delete").option("--pretty", "Print 2-space indented JSON").action(
3274
- async (collection, id, opts) => {
4594
+ plan.command("delete <collection> <id>").description("Plan a document delete").option("--pretty", "Print 2-space indented success JSON").option(
4595
+ "--output-file <path>",
4596
+ "Write success JSON to a secure file and print a receipt"
4597
+ ).action(
4598
+ async (collection, id, opts, command) => {
4599
+ const outputOptions = resolveAgentPrintOptions(command);
3275
4600
  try {
4601
+ validateAgentOutputTarget(outputOptions);
3276
4602
  await runPlanAction({
3277
4603
  operation: "delete",
3278
4604
  collection,
3279
4605
  documentId: id,
3280
- pretty: opts.pretty,
4606
+ outputOptions,
3281
4607
  getClient: getClient2
3282
4608
  });
3283
4609
  } catch (error) {
3284
- exitWithAgentError(error, { pretty: Boolean(opts.pretty) });
4610
+ exitWithAgentError(error, outputOptions);
3285
4611
  }
3286
4612
  }
3287
4613
  )
3288
4614
  );
3289
4615
  configureAgentParser(
3290
- agent.command("confirm <planToken>").description("Execute a previously planned mutation").requiredOption("--hash <planHash>", "Plan hash from plan output").option("--pretty", "Print 2-space indented JSON").action(
3291
- async (planTokenRaw, opts) => {
4616
+ agent.command("confirm <planToken>").description("Execute a previously planned mutation").requiredOption("--hash <planHash>", "Plan hash from plan output").option("--pretty", "Print 2-space indented success JSON").option(
4617
+ "--output-file <path>",
4618
+ "Write success JSON to a secure file and print a receipt"
4619
+ ).action(
4620
+ async (planTokenRaw, opts, command) => {
4621
+ const outputOptions = resolveAgentPrintOptions(command);
3292
4622
  try {
4623
+ validateAgentOutputTarget(outputOptions);
3293
4624
  const token = decodePlanToken(planTokenRaw);
3294
4625
  if (token.planHash !== opts.hash) {
3295
4626
  throw agentPlanError(
@@ -3335,10 +4666,10 @@ function registerAgentPlanCommands(agent, getClient2) {
3335
4666
  id: doc && typeof doc === "object" && "id" in doc ? String(doc.id) : plan2.documentId,
3336
4667
  doc
3337
4668
  },
3338
- { pretty: Boolean(opts.pretty) }
4669
+ outputOptions
3339
4670
  );
3340
4671
  } catch (error) {
3341
- exitWithAgentError(error, { pretty: Boolean(opts.pretty) });
4672
+ exitWithAgentError(error, outputOptions);
3342
4673
  }
3343
4674
  }
3344
4675
  )
@@ -3354,7 +4685,7 @@ function buildStableAgentFields() {
3354
4685
  updatedAt: { type: "string", queryable: true }
3355
4686
  };
3356
4687
  }
3357
- function invalidInput2(message, field, value) {
4688
+ function invalidInput3(message, field, value) {
3358
4689
  const error = new Error(message);
3359
4690
  Object.assign(error, {
3360
4691
  type: "validation",
@@ -3367,13 +4698,13 @@ function invalidInput2(message, field, value) {
3367
4698
  function parsePositiveInteger(value, field) {
3368
4699
  if (value == null) return void 0;
3369
4700
  if (/^[1-9]\d*$/.test(value)) return Number(value);
3370
- throw invalidInput2(`--${field} must be a positive integer`, field, value);
4701
+ throw invalidInput3(`--${field} must be a positive integer`, field, value);
3371
4702
  }
3372
4703
  function handleCommanderError2(error) {
3373
4704
  if (error.code === "commander.helpDisplayed") {
3374
4705
  process.exit(error.exitCode);
3375
4706
  }
3376
- exitWithAgentError(invalidInput2(error.message, "command"));
4707
+ exitWithAgentError(invalidInput3(error.message, "command"));
3377
4708
  }
3378
4709
  function configureAgentParser2(command) {
3379
4710
  command.configureOutput({
@@ -3394,23 +4725,27 @@ function parseWhere(value) {
3394
4725
  return parsed;
3395
4726
  }
3396
4727
  } catch {
3397
- throw invalidInput2("--where must be a JSON object", "where", value);
4728
+ throw invalidInput3("--where must be a JSON object", "where", value);
3398
4729
  }
3399
- throw invalidInput2("--where must be a JSON object", "where", value);
4730
+ throw invalidInput3("--where must be a JSON object", "where", value);
3400
4731
  }
3401
4732
  var AGENT_READ_OPERATIONS = [
3402
4733
  "query",
3403
4734
  "get"
3404
4735
  ];
3405
- var AGENT_PLAN_OPERATIONS = [
3406
- "plan:create",
3407
- "plan:update",
3408
- "plan:delete"
4736
+ var AGENT_PRODUCT_OPERATIONS = [
4737
+ "detail"
3409
4738
  ];
3410
4739
  function buildAgentManifest() {
3411
4740
  const collections = {};
3412
- for (const collection of SERVER_COLLECTIONS3) {
3413
- const operations = isAgentPlanCollection(collection) ? [...AGENT_READ_OPERATIONS, ...AGENT_PLAN_OPERATIONS] : [...AGENT_READ_OPERATIONS];
4741
+ for (const collection of SERVER_COLLECTIONS4) {
4742
+ const operations = [
4743
+ ...AGENT_READ_OPERATIONS,
4744
+ ...collection === "products" ? AGENT_PRODUCT_OPERATIONS : [],
4745
+ ...getAgentPlanOperations(collection).map(
4746
+ (operation) => `plan:${operation}`
4747
+ )
4748
+ ];
3414
4749
  collections[collection] = {
3415
4750
  operations,
3416
4751
  fields: buildStableAgentFields()
@@ -3418,6 +4753,10 @@ function buildAgentManifest() {
3418
4753
  }
3419
4754
  return {
3420
4755
  version: AGENT_PROTOCOL_VERSION,
4756
+ discovery: {
4757
+ context: { counts: true },
4758
+ schema: { mode: "effective" }
4759
+ },
3421
4760
  collections
3422
4761
  };
3423
4762
  }
@@ -3426,27 +4765,75 @@ function validateAgentCollection(collection) {
3426
4765
  return validateCollection(collection);
3427
4766
  } catch (error) {
3428
4767
  const message = error instanceof Error ? error.message : `Unknown collection "${collection}".`;
3429
- throw invalidInput2(message, "collection", collection);
4768
+ throw invalidInput3(message, "collection", collection);
3430
4769
  }
3431
4770
  }
3432
4771
  function registerAgentCommands(program2, getClient2) {
3433
4772
  const agent = configureAgentParser2(
3434
- program2.command("agent").description("Machine-stable CLI namespace for coding agents").addHelpText(
4773
+ program2.command("agent").description("Machine-stable CLI namespace for coding agents").option("--pretty", "Print 2-space indented success JSON").option(
4774
+ "--output-file <path>",
4775
+ "Write success JSON to a secure file and print a receipt"
4776
+ ).addHelpText(
3435
4777
  "after",
3436
4778
  "\nContract: docs/agent-cli-contract.md\nJSON only on stdout; no TTY effects."
3437
4779
  )
3438
4780
  );
3439
4781
  configureAgentParser2(
3440
- agent.command("manifest").description("Print the Agent CLI protocol manifest").option("--pretty", "Print 2-space indented JSON").action((opts) => {
3441
- printAgentSuccess(buildAgentManifest(), {
3442
- pretty: Boolean(opts.pretty)
3443
- });
4782
+ agent.command("manifest").description("Print the Agent CLI protocol manifest").option("--pretty", "Print 2-space indented success JSON").option(
4783
+ "--output-file <path>",
4784
+ "Write success JSON to a secure file and print a receipt"
4785
+ ).action((_opts, command) => {
4786
+ const outputOptions = resolveAgentPrintOptions(command);
4787
+ try {
4788
+ validateAgentOutputTarget(outputOptions);
4789
+ printAgentSuccess(buildAgentManifest(), outputOptions);
4790
+ } catch (error) {
4791
+ exitWithAgentError(error, outputOptions);
4792
+ }
4793
+ })
4794
+ );
4795
+ configureAgentParser2(
4796
+ agent.command("context").description("Get the active tenant context").option("--counts", "Include collection document counts").option("--pretty", "Print 2-space indented success JSON").option(
4797
+ "--output-file <path>",
4798
+ "Write success JSON to a secure file and print a receipt"
4799
+ ).action(async (opts, command) => {
4800
+ const outputOptions = resolveAgentPrintOptions(command);
4801
+ try {
4802
+ validateAgentOutputTarget(outputOptions);
4803
+ const client = getClient2();
4804
+ const result = opts.counts ? await client.tenant.context({ includeCounts: true }) : await client.tenant.context();
4805
+ printAgentSuccess(result, outputOptions);
4806
+ } catch (error) {
4807
+ exitWithAgentError(error, outputOptions);
4808
+ }
4809
+ })
4810
+ );
4811
+ configureAgentParser2(
4812
+ agent.command("schema <collection>").description("Get the effective field schema for a collection").option("--pretty", "Print 2-space indented success JSON").option(
4813
+ "--output-file <path>",
4814
+ "Write success JSON to a secure file and print a receipt"
4815
+ ).action(async (collection, _opts, command) => {
4816
+ const outputOptions = resolveAgentPrintOptions(command);
4817
+ try {
4818
+ validateAgentOutputTarget(outputOptions);
4819
+ const col = validateAgentCollection(collection);
4820
+ const client = getClient2();
4821
+ const result = await client.tenant.collectionSchema(col);
4822
+ printAgentSuccess(result, outputOptions);
4823
+ } catch (error) {
4824
+ exitWithAgentError(error, outputOptions);
4825
+ }
3444
4826
  })
3445
4827
  );
3446
4828
  configureAgentParser2(
3447
- agent.command("query <collection>").description("Query documents from a collection").option("--where <json>", "Filter conditions as a JSON object").option("--limit <n>", "Max results").option("--page <n>", "Page number").option("--sort <field>", "Sort field; prefix with - for descending").option("--select <fields>", "Comma-separated fields to select").option("--pretty", "Print 2-space indented JSON").action(
3448
- async (collection, opts) => {
4829
+ agent.command("query <collection>").description("Query documents from a collection").option("--where <json>", "Filter conditions as a JSON object").option("--limit <n>", "Max results").option("--page <n>", "Page number").option("--sort <field>", "Sort field; prefix with - for descending").option("--select <fields>", "Comma-separated fields to select").option("--pretty", "Print 2-space indented success JSON").option(
4830
+ "--output-file <path>",
4831
+ "Write success JSON to a secure file and print a receipt"
4832
+ ).action(
4833
+ async (collection, opts, command) => {
4834
+ const outputOptions = resolveAgentPrintOptions(command);
3449
4835
  try {
4836
+ validateAgentOutputTarget(outputOptions);
3450
4837
  const col = validateAgentCollection(collection);
3451
4838
  const options = {};
3452
4839
  const where = parseWhere(opts.where);
@@ -3459,28 +4846,55 @@ function registerAgentCommands(program2, getClient2) {
3459
4846
  if (opts.select) options.select = parseSelect(opts.select);
3460
4847
  const client = getClient2();
3461
4848
  const result = await client.collections.from(col).find(options);
3462
- printAgentSuccess(result, { pretty: Boolean(opts.pretty) });
4849
+ printAgentSuccess(result, outputOptions);
3463
4850
  } catch (error) {
3464
- exitWithAgentError(error, { pretty: Boolean(opts.pretty) });
4851
+ exitWithAgentError(error, outputOptions);
3465
4852
  }
3466
4853
  }
3467
4854
  )
3468
4855
  );
3469
4856
  configureAgentParser2(
3470
- agent.command("get <collection> [id]").description("Get a document by ID").option("--select <fields>", "Comma-separated fields to select").option("--pretty", "Print 2-space indented JSON").action(
3471
- async (collection, id, opts) => {
4857
+ agent.command("get <collection> [id]").description("Get a document by ID").option("--select <fields>", "Comma-separated fields to select").option("--pretty", "Print 2-space indented success JSON").option(
4858
+ "--output-file <path>",
4859
+ "Write success JSON to a secure file and print a receipt"
4860
+ ).action(
4861
+ async (collection, id, opts, command) => {
4862
+ const outputOptions = resolveAgentPrintOptions(command);
3472
4863
  try {
4864
+ validateAgentOutputTarget(outputOptions);
3473
4865
  const col = validateAgentCollection(collection);
3474
4866
  if (!id) {
3475
- throw invalidInput2("Missing required argument: id", "id");
4867
+ throw invalidInput3("Missing required argument: id", "id");
3476
4868
  }
3477
4869
  const options = {};
3478
4870
  if (opts.select) options.select = parseSelect(opts.select);
3479
4871
  const client = getClient2();
3480
4872
  const result = await client.collections.from(col).findById(id, options);
3481
- printAgentSuccess(result, { pretty: Boolean(opts.pretty) });
4873
+ printAgentSuccess(result, outputOptions);
4874
+ } catch (error) {
4875
+ exitWithAgentError(error, outputOptions);
4876
+ }
4877
+ }
4878
+ )
4879
+ );
4880
+ const product = configureAgentParser2(
4881
+ agent.command("product").description("Shaped product reads")
4882
+ );
4883
+ configureAgentParser2(
4884
+ product.command("detail").description("Get shaped product detail by slug or ID").option("--slug <slug>", "Product slug").option("--id <id>", "Product ID").option("--market <market>", "Market slug").option("--locale <locale>", "Locale: ko, en, or all").option("--fallback-locale <locale>", "Fallback locale: ko, en, or none").option("--pretty", "Print 2-space indented success JSON").option(
4885
+ "--output-file <path>",
4886
+ "Write success JSON to a secure file and print a receipt"
4887
+ ).action(
4888
+ async (opts, command) => {
4889
+ const outputOptions = resolveAgentPrintOptions(command);
4890
+ try {
4891
+ validateAgentOutputTarget(outputOptions);
4892
+ const params = productDetailParamsFromOptions(opts);
4893
+ const client = getClient2();
4894
+ const result = await client.commerce.product.detail(params);
4895
+ printAgentSuccess(result, outputOptions);
3482
4896
  } catch (error) {
3483
- exitWithAgentError(error, { pretty: Boolean(opts.pretty) });
4897
+ exitWithAgentError(error, outputOptions);
3484
4898
  }
3485
4899
  }
3486
4900
  )
@@ -3510,7 +4924,7 @@ registerOrderCommands(program, getClient, getFormat);
3510
4924
  registerReturnCommands(program, getClient, getFormat);
3511
4925
  registerCartCommands(program, getClient, getFormat);
3512
4926
  registerStockCommands(program, getClient, getFormat);
3513
- registerTransactionCommands(program, getClient, getFormat);
4927
+ registerCommerceSessionCommands(program, getClient, getFormat);
3514
4928
  registerProductCommands(program, getClient, getFormat);
3515
4929
  registerDiscountCommands(program, getClient, getFormat);
3516
4930
  registerShippingCommands(program, getClient, getFormat);