@01.software/cli 0.15.1 → 0.17.0

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 z3 } 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,202 +1429,1004 @@ var ProductUpsertSchema = ProductUpsertObjectSchema.superRefine(
1109
1429
  }
1110
1430
  );
1111
1431
 
1112
- // ../contracts/src/ecommerce/index.ts
1113
- var transactionStatusSchema = z3.enum([
1114
- "pending",
1115
- "paid",
1116
- "failed",
1117
- "canceled",
1118
- "refunded"
1119
- ]);
1120
- var financialStatusSchema = z3.enum([
1121
- "pending",
1122
- "paid",
1123
- "canceled",
1124
- "partially_refunded",
1125
- "refunded"
1126
- ]);
1127
- var orderDisplayFinancialStatusSchema = z3.enum([
1128
- "pending",
1129
- "paid",
1130
- "partially_refunded",
1131
- "refunded",
1132
- "voided"
1432
+ // ../../internal/contracts/src/ecommerce/cart.ts
1433
+ import { z as z4 } from "zod";
1434
+
1435
+ // ../../internal/contracts/src/ecommerce/commerce-common.ts
1436
+ import { z as z3 } from "zod";
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
+ });
1479
+ });
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"
1482
+ });
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) {
1488
+ ctx.addIssue({
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`
1494
+ });
1495
+ }
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()
1133
1578
  ]);
1134
- var confirmationStatusSchema = z3.enum(["unconfirmed", "confirmed"]);
1135
- var fulfillmentOrderStatusSchema = z3.enum([
1136
- "open",
1137
- "in_progress",
1138
- "on_hold",
1139
- "canceled",
1140
- "closed"
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) {
1677
+ ctx.addIssue({
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"
1689
+ });
1690
+ }
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
+ });
1832
+
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
1141
1849
  ]);
1142
- var shipmentStatusSchema = z3.enum([
1143
- "pending",
1144
- "shipped",
1145
- "delivered",
1146
- "canceled",
1147
- "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
1148
1882
  ]);
1149
- var orderDisplayFulfillmentStatusSchema = z3.enum([
1150
- "unfulfilled",
1151
- "in_progress",
1152
- "on_hold",
1153
- "partially_fulfilled",
1154
- "shipped",
1155
- "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 paymentNextActionSchema = z5.object({
1895
+ type: z5.literal("toss_widget"),
1896
+ clientKey: z5.string().min(1).max(255),
1897
+ paymentMethodVariantKey: z5.string().min(1).max(255),
1898
+ agreementVariantKey: z5.string().min(1).max(255),
1899
+ providerOrderId: tossProviderOrderIdSchema,
1900
+ providerCustomerKey: tossProviderCustomerKeySchema,
1901
+ orderName: z5.string().min(1).max(200),
1902
+ customerName: z5.string().min(1).max(200).optional(),
1903
+ customerEmail: z5.string().email().max(254).optional()
1904
+ }).strict();
1905
+ var PAYMENT_SESSION_ROLES = [
1906
+ "active",
1907
+ "canonical_candidate",
1908
+ "canonical",
1909
+ "residual",
1910
+ "historical"
1911
+ ];
1912
+ var paymentSessionRoleSchema = z5.enum(PAYMENT_SESSION_ROLES);
1913
+ var PAYMENT_PROVIDER_STATUSES = [
1914
+ "requires_payment_method",
1915
+ "requires_action",
1916
+ "requires_confirmation",
1917
+ "initializing",
1918
+ "processing",
1919
+ "authorized",
1920
+ "canceling",
1921
+ "unknown",
1922
+ "captured",
1923
+ "failed",
1156
1924
  "canceled"
1157
- ]);
1158
- var orderReturnStatusSchema = z3.enum([
1159
- "no_return",
1160
- "return_requested",
1161
- "in_progress",
1162
- "returned"
1163
- ]);
1164
- var orderStatusSchema = z3.enum([
1165
- "pending",
1166
- "paid",
1167
- "canceled",
1168
- "refunded",
1169
- "preparing",
1170
- "shipped",
1171
- "delivered",
1172
- "confirmed",
1173
- "return_requested",
1174
- "return_processing",
1175
- "returned"
1176
- ]);
1177
- var entityIdSchema = z3.union([z3.string().min(1), z3.number()]).transform(String);
1178
- var createOrderItemSchema = z3.object({
1179
- // `product` and `option` are storefront-optional: the create-order
1180
- // endpoint derives the product from the variant's parent and snapshots
1181
- // option selection from the variant.
1182
- product: entityIdSchema.optional(),
1183
- variant: entityIdSchema,
1184
- option: entityIdSchema.optional(),
1185
- quantity: z3.number().int().positive("quantity must be a positive integer"),
1186
- unitPrice: z3.number().optional(),
1187
- totalPrice: z3.number().optional()
1925
+ ];
1926
+ var paymentProviderStatusSchema = z5.enum(PAYMENT_PROVIDER_STATUSES);
1927
+ var PAYMENT_RECONCILIATION_REASONS = [
1928
+ "provider_outcome_unknown",
1929
+ "local_commit_ambiguous",
1930
+ "amount_mismatch",
1931
+ "currency_mismatch",
1932
+ "excess_capture_pending_order",
1933
+ "inventory_unavailable",
1934
+ "admission_unavailable",
1935
+ "excess_capture",
1936
+ "late_capture",
1937
+ "cross_checkout_duplicate"
1938
+ ];
1939
+ var paymentReconciliationReasonSchema = z5.enum(
1940
+ PAYMENT_RECONCILIATION_REASONS
1941
+ );
1942
+ function requiredReconciliationSchema(reasons) {
1943
+ const reasonSchema = z5.enum(reasons);
1944
+ return z5.object({
1945
+ status: z5.literal("required"),
1946
+ reasons: z5.array(reasonSchema).min(1).readonly()
1947
+ }).strict().superRefine((value, ctx) => {
1948
+ const indexes = new Map(reasons.map((reason, index) => [reason, index]));
1949
+ let previous = -1;
1950
+ value.reasons.forEach((reason, index) => {
1951
+ const current = indexes.get(reason) ?? -1;
1952
+ if (current <= previous) {
1953
+ ctx.addIssue({
1954
+ code: "custom",
1955
+ path: ["reasons", index],
1956
+ message: "reconciliation reasons must be unique and canonical-sorted"
1957
+ });
1958
+ }
1959
+ previous = current;
1960
+ });
1961
+ });
1962
+ }
1963
+ var noReconciliationSchema = z5.object({ status: z5.literal("none") }).strict();
1964
+ var publicCaptureEvidenceSchema = z5.object({
1965
+ amountMinor: z5.number().int().nonnegative(),
1966
+ currency: currencyCodeSchema,
1967
+ observedAt: z5.string().datetime({ offset: true })
1968
+ }).strict();
1969
+ var reconciliationRefundEvidenceSchema = z5.object({
1970
+ providerRefundId: z5.string().min(1),
1971
+ money: moneySchema,
1972
+ observedAt: z5.string().datetime({ offset: true })
1973
+ }).strict();
1974
+ var PAYMENT_FAILURE_REASONS = [
1975
+ "payment_declined",
1976
+ "authentication_failed",
1977
+ "authorization_expired",
1978
+ "payment_method_unavailable",
1979
+ "provider_terminal_failure",
1980
+ "unknown_provider_terminal_failure"
1981
+ ];
1982
+ var paymentFailureReasonSchema = z5.enum(PAYMENT_FAILURE_REASONS);
1983
+ var orderReferenceSchema = z5.object({
1984
+ id: z5.string().min(1),
1985
+ orderNumber: z5.string().min(1)
1986
+ }).strict();
1987
+ var paymentSessionBaseShape = {
1988
+ id: z5.string().min(1),
1989
+ money: moneySchema,
1990
+ expiresAt: z5.string().datetime({ offset: true }).nullable()
1991
+ };
1992
+ var suppressedNotReadyShape = {
1993
+ presentationStatus: z5.literal("suppressed"),
1994
+ finalizationStatus: z5.literal("not_ready"),
1995
+ reconciliation: noReconciliationSchema,
1996
+ order: z5.null()
1997
+ };
1998
+ function stateSchemas(nextAction) {
1999
+ return [
2000
+ z5.object({
2001
+ providerStatus: z5.enum(["requires_payment_method", "requires_action"]),
2002
+ sessionRole: z5.literal("active"),
2003
+ presentationStatus: z5.literal("actionable"),
2004
+ finalizationStatus: z5.literal("not_ready"),
2005
+ nextAction,
2006
+ reconciliation: noReconciliationSchema,
2007
+ order: z5.null()
2008
+ }),
2009
+ z5.object({
2010
+ providerStatus: z5.enum(["requires_payment_method", "requires_action"]),
2011
+ sessionRole: z5.literal("residual"),
2012
+ ...suppressedNotReadyShape
2013
+ }),
2014
+ z5.object({
2015
+ providerStatus: z5.literal("requires_confirmation"),
2016
+ sessionRole: z5.enum(["active", "residual"]),
2017
+ ...suppressedNotReadyShape
2018
+ }),
2019
+ z5.object({
2020
+ providerStatus: z5.enum(["initializing", "processing", "authorized"]),
2021
+ sessionRole: z5.enum(["active", "residual"]),
2022
+ ...suppressedNotReadyShape
2023
+ }),
2024
+ z5.object({
2025
+ providerStatus: z5.literal("canceling"),
2026
+ sessionRole: z5.enum(["active", "residual"]),
2027
+ ...suppressedNotReadyShape
2028
+ }),
2029
+ z5.object({
2030
+ providerStatus: z5.literal("unknown"),
2031
+ sessionRole: z5.enum(["active", "residual"]),
2032
+ presentationStatus: z5.literal("suppressed"),
2033
+ finalizationStatus: z5.literal("not_ready"),
2034
+ reconciliation: requiredReconciliationSchema([
2035
+ "provider_outcome_unknown"
2036
+ ]),
2037
+ order: z5.null()
2038
+ }),
2039
+ z5.object({
2040
+ providerStatus: z5.literal("captured"),
2041
+ sessionRole: z5.enum(["canonical_candidate", "residual"]),
2042
+ presentationStatus: z5.literal("suppressed"),
2043
+ finalizationStatus: z5.literal("pending"),
2044
+ reconciliation: noReconciliationSchema,
2045
+ providerEvidence: publicCaptureEvidenceSchema,
2046
+ order: z5.null()
2047
+ }),
2048
+ z5.object({
2049
+ providerStatus: z5.literal("captured"),
2050
+ sessionRole: z5.enum(["canonical", "historical"]),
2051
+ presentationStatus: z5.literal("suppressed"),
2052
+ finalizationStatus: z5.literal("committed"),
2053
+ reconciliation: noReconciliationSchema,
2054
+ providerEvidence: publicCaptureEvidenceSchema,
2055
+ order: orderReferenceSchema
2056
+ }),
2057
+ z5.object({
2058
+ providerStatus: z5.literal("captured"),
2059
+ sessionRole: z5.enum(["canonical_candidate", "residual"]),
2060
+ presentationStatus: z5.literal("suppressed"),
2061
+ finalizationStatus: z5.literal("pending"),
2062
+ reconciliation: requiredReconciliationSchema([
2063
+ "local_commit_ambiguous",
2064
+ "amount_mismatch",
2065
+ "currency_mismatch",
2066
+ "excess_capture_pending_order"
2067
+ ]),
2068
+ providerEvidence: publicCaptureEvidenceSchema,
2069
+ order: z5.null()
2070
+ }),
2071
+ z5.object({
2072
+ providerStatus: z5.literal("captured"),
2073
+ sessionRole: z5.enum(["canonical", "residual"]),
2074
+ presentationStatus: z5.literal("suppressed"),
2075
+ finalizationStatus: z5.literal("committed"),
2076
+ reconciliation: requiredReconciliationSchema([
2077
+ "inventory_unavailable",
2078
+ "admission_unavailable",
2079
+ "amount_mismatch",
2080
+ "currency_mismatch",
2081
+ "excess_capture",
2082
+ "late_capture",
2083
+ "cross_checkout_duplicate"
2084
+ ]),
2085
+ providerEvidence: publicCaptureEvidenceSchema,
2086
+ order: orderReferenceSchema
2087
+ }),
2088
+ z5.object({
2089
+ providerStatus: z5.literal("failed"),
2090
+ sessionRole: z5.literal("historical"),
2091
+ presentationStatus: z5.literal("suppressed"),
2092
+ finalizationStatus: z5.literal("not_ready"),
2093
+ reconciliation: noReconciliationSchema,
2094
+ failure: z5.object({
2095
+ reason: paymentFailureReasonSchema,
2096
+ message: z5.string().min(1).optional()
2097
+ }).strict(),
2098
+ order: z5.null()
2099
+ }),
2100
+ z5.object({
2101
+ providerStatus: z5.literal("canceled"),
2102
+ sessionRole: z5.literal("historical"),
2103
+ presentationStatus: z5.literal("suppressed"),
2104
+ finalizationStatus: z5.literal("not_ready"),
2105
+ cancellationReason: z5.enum([
2106
+ "expired",
2107
+ "customer",
2108
+ "operator",
2109
+ "provider"
2110
+ ]),
2111
+ reconciliation: noReconciliationSchema,
2112
+ order: z5.null()
2113
+ })
2114
+ ];
2115
+ }
2116
+ function providerSessionSchemas(action) {
2117
+ return stateSchemas(action).map(
2118
+ (state) => z5.object({
2119
+ ...paymentSessionBaseShape,
2120
+ provider: z5.literal("toss"),
2121
+ ...state.shape
2122
+ }).strict()
2123
+ );
2124
+ }
2125
+ var publicPaymentSessionVariants = [
2126
+ ...providerSessionSchemas(paymentNextActionSchema)
2127
+ ];
2128
+ var publicPaymentSessionSchema = z5.union(publicPaymentSessionVariants);
2129
+ var CreatePaymentSessionParamsSchema = createPaymentSessionParamsSchema;
2130
+ var CancelServerPaymentSessionParamsSchema = cancelServerPaymentSessionParamsSchema;
2131
+ var ConfirmTossPaymentSessionParamsSchema = confirmTossPaymentSessionParamsSchema;
2132
+
2133
+ // ../../internal/contracts/src/ecommerce/checkout.ts
2134
+ var canonicalPublicCheckoutItemSchema = z6.object({
2135
+ id: z6.string().min(1),
2136
+ product: publicCartReferenceSchema,
2137
+ variant: publicCartReferenceSchema,
2138
+ productTitle: z6.string().min(1),
2139
+ variantTitle: z6.string().min(1).nullable(),
2140
+ sku: z6.string().min(1).nullable(),
2141
+ admissions: publicCartItemAdmissionsSchema,
2142
+ quantity: z6.number().int().positive().max(MAX_CART_LINE_QUANTITY),
2143
+ unitPriceMinor: minorAmountSchema,
2144
+ discountMinor: minorAmountSchema,
2145
+ lineTotalMinor: minorAmountSchema,
2146
+ requiresShipping: z6.boolean()
2147
+ }).strict().superRefine((item, ctx) => {
2148
+ const expectedLineTotal = BigInt(item.unitPriceMinor) * BigInt(item.quantity) - BigInt(item.discountMinor);
2149
+ if (expectedLineTotal < 0n) {
2150
+ ctx.addIssue({
2151
+ code: "custom",
2152
+ path: ["discountMinor"],
2153
+ message: "discountMinor cannot exceed the undiscounted line total"
2154
+ });
2155
+ return;
2156
+ }
2157
+ if (BigInt(item.lineTotalMinor) !== expectedLineTotal) {
2158
+ ctx.addIssue({
2159
+ code: "custom",
2160
+ path: ["lineTotalMinor"],
2161
+ message: "lineTotalMinor must equal unitPriceMinor * quantity - discountMinor"
2162
+ });
2163
+ }
2164
+ });
2165
+ var checkoutAmountsSchema = z6.object({
2166
+ currency: currencyCodeSchema,
2167
+ subtotalMinor: minorAmountSchema,
2168
+ shippingMinor: minorAmountSchema,
2169
+ discountMinor: minorAmountSchema,
2170
+ totalMinor: minorAmountSchema
2171
+ }).strict().superRefine((amounts, ctx) => {
2172
+ const expectedTotal = BigInt(amounts.subtotalMinor) + BigInt(amounts.shippingMinor) - BigInt(amounts.discountMinor);
2173
+ if (BigInt(amounts.totalMinor) !== expectedTotal) {
2174
+ ctx.addIssue({
2175
+ code: "custom",
2176
+ path: ["totalMinor"],
2177
+ message: "totalMinor must equal subtotalMinor + shippingMinor - discountMinor"
2178
+ });
2179
+ }
2180
+ });
2181
+ var checkoutPaymentSessionRefSchema = z6.object({
2182
+ id: z6.string().min(1),
2183
+ provider: z6.literal("toss"),
2184
+ providerStatus: paymentProviderStatusSchema
1188
2185
  }).strict();
1189
- var createOrderSchema = z3.object({
1190
- pgPaymentId: z3.string().min(1).optional(),
1191
- orderNumber: z3.string().min(1, "orderNumber is required"),
1192
- customer: entityIdSchema.optional(),
1193
- customerSnapshot: z3.object({
1194
- name: z3.string().optional(),
1195
- email: z3.string().email("Invalid email format"),
1196
- phone: z3.string().optional()
2186
+ var openCheckoutPaymentStateSchema = z6.object({
2187
+ activePaymentSession: checkoutPaymentSessionRefSchema.nullable()
2188
+ }).strict();
2189
+ var spentCheckoutPaymentStateSchema = z6.object({
2190
+ activePaymentSession: z6.null()
2191
+ }).strict();
2192
+ var lightweightOrderReferenceSchema = z6.object({
2193
+ id: z6.string().min(1),
2194
+ orderNumber: z6.string().min(1)
2195
+ }).strict();
2196
+ var publicCheckoutBaseShape = {
2197
+ id: z6.string().min(1),
2198
+ amounts: checkoutAmountsSchema,
2199
+ items: z6.array(canonicalPublicCheckoutItemSchema).min(1),
2200
+ customerSnapshot: customerSnapshotSchema,
2201
+ shippingAddress: checkoutShippingAddressSchema.nullable(),
2202
+ attributes: publicAttributesSchema,
2203
+ expiresAt: z6.string().datetime({ offset: true }),
2204
+ createdAt: z6.string().datetime({ offset: true }),
2205
+ updatedAt: z6.string().datetime({ offset: true })
2206
+ };
2207
+ var abandonedReasonSchema = z6.enum(["expired", "customer", "operator"]);
2208
+ var publicCheckoutVariants = [
2209
+ z6.object({
2210
+ ...publicCheckoutBaseShape,
2211
+ status: z6.literal("open"),
2212
+ paymentState: openCheckoutPaymentStateSchema,
2213
+ order: z6.null()
1197
2214
  }).strict(),
1198
- shippingAddress: z3.object({
1199
- postalCode: z3.string().optional(),
1200
- address: z3.string().optional(),
1201
- detailAddress: z3.string().optional(),
1202
- deliveryMessage: z3.string().optional(),
1203
- recipientName: z3.string().optional(),
1204
- phone: z3.string().optional()
1205
- }).strict().optional(),
1206
- orderItems: z3.array(createOrderItemSchema).min(1, "At least one order item is required").max(100, "Maximum 100 items per order"),
1207
- totalAmount: z3.number().nonnegative("totalAmount must be non-negative"),
1208
- shippingAmount: z3.number().min(0).optional(),
1209
- discountCode: z3.string().optional()
2215
+ z6.object({
2216
+ ...publicCheckoutBaseShape,
2217
+ status: z6.literal("placed"),
2218
+ placedAt: z6.string().datetime({ offset: true }),
2219
+ paymentState: spentCheckoutPaymentStateSchema,
2220
+ order: lightweightOrderReferenceSchema
2221
+ }).strict(),
2222
+ z6.object({
2223
+ ...publicCheckoutBaseShape,
2224
+ status: z6.literal("placed"),
2225
+ placedAt: z6.string().datetime({ offset: true }),
2226
+ placedVia: z6.literal("late_capture"),
2227
+ abandonedAt: z6.string().datetime({ offset: true }),
2228
+ abandonedReason: abandonedReasonSchema,
2229
+ paymentState: spentCheckoutPaymentStateSchema,
2230
+ order: lightweightOrderReferenceSchema
2231
+ }).strict(),
2232
+ z6.object({
2233
+ ...publicCheckoutBaseShape,
2234
+ status: z6.literal("abandoned"),
2235
+ abandonedAt: z6.string().datetime({ offset: true }),
2236
+ abandonedReason: abandonedReasonSchema,
2237
+ paymentState: z6.object({
2238
+ activePaymentSession: z6.null()
2239
+ }).strict(),
2240
+ order: z6.null()
2241
+ }).strict()
2242
+ ];
2243
+ function validateCheckoutSnapshot(input, ctx) {
2244
+ const checkout = input;
2245
+ const expectedSubtotal = checkout.items.reduce(
2246
+ (total, item) => total + BigInt(item.unitPriceMinor) * BigInt(item.quantity),
2247
+ 0n
2248
+ );
2249
+ const expectedDiscount = checkout.items.reduce(
2250
+ (total, item) => total + BigInt(item.discountMinor),
2251
+ 0n
2252
+ );
2253
+ if (BigInt(checkout.amounts.subtotalMinor) !== expectedSubtotal) {
2254
+ ctx.addIssue({
2255
+ code: "custom",
2256
+ path: ["amounts", "subtotalMinor"],
2257
+ message: "subtotalMinor must equal the sum of undiscounted checkout line totals"
2258
+ });
2259
+ }
2260
+ if (BigInt(checkout.amounts.discountMinor) !== expectedDiscount) {
2261
+ ctx.addIssue({
2262
+ code: "custom",
2263
+ path: ["amounts", "discountMinor"],
2264
+ message: "discountMinor must equal the sum of checkout line discounts"
2265
+ });
2266
+ }
2267
+ }
2268
+ var publicCheckoutSchema = z6.union(publicCheckoutVariants).superRefine(validateCheckoutSnapshot);
2269
+ var serverCheckoutExtraShape = {
2270
+ clientReferenceId: commerceResourceIdSchema.nullable(),
2271
+ integrationMetadata: integrationMetadataSchema
2272
+ };
2273
+ var serverCheckoutVariants = publicCheckoutVariants.map(
2274
+ (variant) => variant.extend(serverCheckoutExtraShape)
2275
+ );
2276
+ var serverCheckoutSchema = z6.union(serverCheckoutVariants).superRefine(validateCheckoutSnapshot);
2277
+ var retrieveCheckoutParamsSchema = z6.object({ checkoutToken: commerceCapabilitySchema }).strict();
2278
+ var retrieveServerCheckoutParamsSchema = z6.object({ checkoutId: commerceResourceIdSchema }).strict();
2279
+ var createCheckoutBodySchema = z6.object({
2280
+ cartToken: commerceCapabilitySchema,
2281
+ customer: customerSnapshotSchema,
2282
+ shippingAddress: checkoutShippingAddressSchema.optional()
1210
2283
  }).strict();
1211
- var updateTransactionSchema = z3.object({
1212
- pgPaymentId: z3.string().min(1, "pgPaymentId is required").describe("PG payment ID (required)"),
1213
- status: transactionStatusSchema.describe(
1214
- "New transaction status (required)"
1215
- ),
1216
- paymentMethod: z3.string().optional().describe("Payment method (optional)"),
1217
- receiptUrl: z3.string().optional().describe("Receipt URL (optional)"),
1218
- paymentKey: z3.string().min(1).optional().describe("Provider payment key for verified paid confirmation"),
1219
- amount: z3.number().int().positive().optional().describe("Provider-confirmed amount for verified paid confirmation")
2284
+ var createCheckoutParamsSchema = createCheckoutBodySchema.extend({
2285
+ idempotencyKey: commerceIdempotencyKeySchema
2286
+ });
2287
+ var createServerCheckoutBodySchema = createCheckoutBodySchema.safeExtend({
2288
+ clientReferenceId: commerceResourceIdSchema.optional(),
2289
+ integrationMetadata: integrationMetadataSchema.optional()
1220
2290
  }).strict();
1221
- var providerSlugSchema = z3.string().trim().regex(/^[a-z0-9][a-z0-9_-]{0,63}$/, "pgProvider must be lowercase slug");
1222
- var confirmPaymentSchema = z3.object({
1223
- orderNumber: z3.string().min(1).optional(),
1224
- pgPaymentId: z3.string().min(1, "pgPaymentId is required").describe("Provider payment identifier stored on the transaction"),
1225
- pgProvider: providerSlugSchema.describe(
1226
- "Payment provider slug, e.g. toss, portone, stripe"
1227
- ),
1228
- pgOrderId: z3.string().min(1).optional(),
1229
- amount: z3.number().int().nonnegative("amount must be non-negative").describe("Provider-confirmed amount in minor units"),
1230
- currency: z3.string().min(1).optional(),
1231
- paymentMethod: z3.string().optional(),
1232
- receiptUrl: z3.string().url().optional(),
1233
- approvedAt: z3.string().optional(),
1234
- providerStatus: z3.string().optional(),
1235
- providerEventId: z3.string().min(1).optional(),
1236
- confirmationSource: z3.enum([
1237
- "provider_webhook",
1238
- "provider_lookup",
1239
- "provider_api_confirm",
1240
- "manual_server"
1241
- ]).optional(),
1242
- paymentKey: z3.string().min(1).optional().describe(
1243
- "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."
1244
- ),
1245
- metadata: z3.record(z3.string(), z3.unknown()).optional()
2291
+ var createServerCheckoutParamsSchema = createServerCheckoutBodySchema.extend({
2292
+ idempotencyKey: commerceIdempotencyKeySchema
2293
+ });
2294
+ var createCheckoutResultSchema = z6.object({
2295
+ checkout: publicCheckoutSchema,
2296
+ checkoutToken: commerceCapabilitySchema
1246
2297
  }).strict();
1247
- var confirmPaymentResponseSchema = z3.object({
1248
- orderId: z3.string().min(1),
1249
- transactionId: z3.string().min(1),
1250
- status: z3.literal("paid"),
1251
- alreadyConfirmed: z3.boolean().optional()
2298
+ var createServerCheckoutResultSchema = z6.object({
2299
+ checkout: serverCheckoutSchema,
2300
+ checkoutToken: commerceCapabilitySchema
2301
+ }).strict();
2302
+ var canonicalCheckoutQuerySchema = z6.object({
2303
+ limit: z6.number().int().min(1).max(100).default(20),
2304
+ cursor: z6.string().min(1).max(512).optional(),
2305
+ status: z6.array(z6.enum(["open", "placed", "abandoned"])).optional(),
2306
+ createdAtFrom: z6.string().datetime({ offset: true }).optional(),
2307
+ createdAtTo: z6.string().datetime({ offset: true }).optional(),
2308
+ clientReferenceId: commerceResourceIdSchema.optional()
2309
+ }).strict();
2310
+ var checkoutListItemVariants = publicCheckoutVariants.map(
2311
+ (variant) => variant.omit({
2312
+ items: true,
2313
+ shippingAddress: true,
2314
+ attributes: true
2315
+ }).extend({ clientReferenceId: commerceResourceIdSchema.nullable() })
2316
+ );
2317
+ var checkoutListItemSchema = z6.union(checkoutListItemVariants);
2318
+ var canonicalCheckoutQueryResultSchema = z6.object({
2319
+ data: z6.array(checkoutListItemSchema),
2320
+ hasMore: z6.boolean(),
2321
+ nextCursor: z6.string().min(1).max(512).nullable()
1252
2322
  }).strict();
2323
+ var CreateServerCheckoutParamsSchema = createServerCheckoutParamsSchema;
2324
+ var CanonicalCheckoutQuerySchema = canonicalCheckoutQuerySchema;
2325
+
2326
+ // ../../internal/contracts/src/ecommerce/index.ts
2327
+ var transactionStatusSchema = z7.enum([
2328
+ "pending",
2329
+ "paid",
2330
+ "success",
2331
+ "failed",
2332
+ "error",
2333
+ "canceled",
2334
+ "refunded"
2335
+ ]);
2336
+ var transactionKindSchema = z7.enum([
2337
+ "authorization",
2338
+ "sale",
2339
+ "capture",
2340
+ "void",
2341
+ "refund"
2342
+ ]);
1253
2343
  var RETURN_REASON_CODES = [
1254
2344
  "change_of_mind",
1255
2345
  "defective",
1256
2346
  "wrong_delivery",
1257
2347
  "damaged",
2348
+ "size_too_large",
2349
+ "size_too_small",
2350
+ "color",
2351
+ "style",
2352
+ "not_as_described",
2353
+ "wrong_item",
2354
+ "unknown",
1258
2355
  "other"
1259
2356
  ];
1260
- var returnReasonSchema = z3.enum(RETURN_REASON_CODES);
2357
+ var returnReasonSchema = z7.enum(RETURN_REASON_CODES);
1261
2358
  var RETURN_REJECTION_REASON_CODES = [
1262
2359
  "window_expired",
1263
2360
  "used_or_damaged",
1264
2361
  "non_returnable",
1265
2362
  "other"
1266
2363
  ];
1267
- var returnRejectionReasonCodeSchema = z3.enum(
2364
+ var returnRejectionReasonCodeSchema = z7.enum(
1268
2365
  RETURN_REJECTION_REASON_CODES
1269
2366
  );
1270
- var restockActionSchema = z3.enum(["return_to_stock", "discard"]);
1271
- var returnWithRefundItemSchema = z3.object({
1272
- orderItem: z3.union([z3.string().min(1), z3.number()]).transform(String),
1273
- quantity: z3.number().int().positive("quantity must be a positive integer"),
2367
+ var RETURN_CANCELLATION_REASON_CODES = [
2368
+ "items_not_received",
2369
+ "customer_withdrew",
2370
+ "merchant_decision",
2371
+ "other"
2372
+ ];
2373
+ var returnCancellationReasonCodeSchema = z7.enum(
2374
+ RETURN_CANCELLATION_REASON_CODES
2375
+ );
2376
+ var restockActionSchema = z7.enum(["return_to_stock", "discard"]);
2377
+ var returnWithRefundItemSchema = z7.object({
2378
+ orderItem: z7.union([z7.string().min(1), z7.number()]).transform(String),
2379
+ quantity: z7.number().int().positive("quantity must be a positive integer"),
1274
2380
  restockAction: restockActionSchema.default("return_to_stock"),
1275
- restockingFee: z3.number().min(0, "restockingFee must be non-negative").optional().describe("Restocking fee charged for this line (ADR 0005 \xA7Gap 1)")
2381
+ restockingFee: z7.number().min(0, "restockingFee must be non-negative").optional().describe("Restocking fee charged for this line (ADR 0005 \xA7Gap 1)")
1276
2382
  }).strict();
1277
- var returnWithRefundSchema = z3.object({
1278
- orderNumber: z3.string().min(1, "orderNumber is required").describe("Order number (required)"),
2383
+ var returnWithRefundSchema = z7.object({
2384
+ orderNumber: z7.string().min(1, "orderNumber is required").describe("Order number (required)"),
1279
2385
  reason: returnReasonSchema.optional().describe("Return reason (optional)"),
1280
- reasonDetail: z3.string().optional().describe("Detailed reason text (optional)"),
1281
- returnItems: z3.array(returnWithRefundItemSchema).min(1, "At least one return item is required").max(100, "Too many return items").describe("Array of products to return (required)"),
1282
- refundAmount: z3.number().min(0, "refundAmount must be non-negative").describe("Refund amount (required, min 0)"),
1283
- returnShippingFee: z3.number().min(0, "returnShippingFee must be non-negative").optional().describe(
2386
+ reasonDetail: z7.string().optional().describe("Detailed reason text (optional)"),
2387
+ 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)"),
2388
+ refundAmount: z7.number().min(0, "refundAmount must be non-negative").describe("Refund amount (required, min 0)"),
2389
+ returnShippingFee: z7.number().min(0, "returnShippingFee must be non-negative").optional().describe(
1284
2390
  "Return shipping fee charged to the customer (ADR 0005 \xA7Gap 1)"
1285
2391
  ),
1286
- initialShippingRefundAmount: z3.number().min(0, "initialShippingRefundAmount must be non-negative").optional().describe("Initial order shipping amount refunded to the customer"),
1287
- initialShippingRefundOverrideNote: z3.string().min(1).optional().describe(
2392
+ initialShippingRefundAmount: z7.number().min(0, "initialShippingRefundAmount must be non-negative").optional().describe("Initial order shipping amount refunded to the customer"),
2393
+ initialShippingRefundOverrideNote: z7.string().min(1).optional().describe(
1288
2394
  "Operator audit note required when overriding policy suggestion"
1289
2395
  ),
1290
- pgPaymentId: z3.string().min(1, "pgPaymentId is required").describe("PG payment ID for refund (required)"),
1291
- paymentKey: z3.string().min(1).optional().describe("Provider payment key for verified refund"),
1292
- refundReceiptUrl: z3.string().optional().describe("Refund receipt URL (optional)")
2396
+ pgPaymentId: z7.string().min(1, "pgPaymentId is required").describe("PG payment ID for refund (required)"),
2397
+ paymentKey: z7.string().min(1).optional().describe("Provider payment key for verified refund"),
2398
+ refundReceiptUrl: z7.string().optional().describe("Refund receipt URL (optional)")
1293
2399
  }).strict();
1294
- var createReturnSchema = z3.object({
1295
- orderNumber: z3.string().min(1, "orderNumber is required").describe("Order number (required)"),
2400
+ var createReturnSchema = z7.object({
2401
+ orderNumber: z7.string().min(1, "orderNumber is required").describe("Order number (required)"),
1296
2402
  reason: returnReasonSchema.optional().describe("Return reason (optional)"),
1297
- reasonDetail: z3.string().optional().describe("Detailed reason text (optional)"),
1298
- returnItems: z3.array(returnWithRefundItemSchema).min(1, "At least one return item is required").max(100, "Too many return items").describe("Array of products to return (required)"),
1299
- refundAmount: z3.number().min(0, "refundAmount must be non-negative").describe(
2403
+ reasonDetail: z7.string().optional().describe("Detailed reason text (optional)"),
2404
+ 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)"),
2405
+ refundAmount: z7.number().min(0, "refundAmount must be non-negative").describe(
1300
2406
  "Line refund amount before initial shipping refund (required, min 0)"
1301
2407
  ),
1302
- returnShippingFee: z3.number().min(0, "returnShippingFee must be non-negative").optional().describe("Return shipping fee charged to the customer"),
1303
- initialShippingRefundAmount: z3.number().min(0, "initialShippingRefundAmount must be non-negative").optional().describe("Initial order shipping amount refunded to the customer"),
1304
- initialShippingRefundOverrideNote: z3.string().min(1).optional().describe(
2408
+ returnShippingFee: z7.number().min(0, "returnShippingFee must be non-negative").optional().describe("Return shipping fee charged to the customer"),
2409
+ initialShippingRefundAmount: z7.number().min(0, "initialShippingRefundAmount must be non-negative").optional().describe("Initial order shipping amount refunded to the customer"),
2410
+ initialShippingRefundOverrideNote: z7.string().min(1).optional().describe(
1305
2411
  "Operator audit note required when overriding policy suggestion"
1306
2412
  )
1307
2413
  }).strict();
2414
+ var updateReturnSchema = z7.object({
2415
+ returnId: z7.union([z7.string(), z7.number()]).transform(String),
2416
+ // Only non-financial transitions are accepted here. `completed` is included
2417
+ // so the server can return the canonical `server_derived` error instead of
2418
+ // a generic validation failure.
2419
+ status: z7.enum([
2420
+ "processing",
2421
+ "approved",
2422
+ "rejected",
2423
+ "canceled",
2424
+ "completed"
2425
+ ]),
2426
+ reasonCode: returnRejectionReasonCodeSchema.optional(),
2427
+ cancellationReasonCode: returnCancellationReasonCodeSchema.optional(),
2428
+ cancellationReasonDetail: z7.string().trim().min(1).max(2e3).optional()
2429
+ }).strict();
1308
2430
  var CANCEL_REASON_CODES = [
1309
2431
  "customer",
1310
2432
  "inventory",
@@ -1313,242 +2435,159 @@ var CANCEL_REASON_CODES = [
1313
2435
  "staff",
1314
2436
  "other"
1315
2437
  ];
1316
- var cancelReasonCodeSchema = z3.enum(CANCEL_REASON_CODES);
2438
+ var cancelReasonCodeSchema = z7.enum(CANCEL_REASON_CODES);
1317
2439
  var FORCE_RESOLUTION_REASON_CODES = [
1318
2440
  "staff_error",
1319
2441
  "wrong_fulfillment",
1320
2442
  "other"
1321
2443
  ];
1322
- var forceResolutionReasonCodeSchema = z3.enum(
2444
+ var forceResolutionReasonCodeSchema = z7.enum(
1323
2445
  FORCE_RESOLUTION_REASON_CODES
1324
2446
  );
1325
- var idempotencyKeySchema = z3.string().trim().min(1, "idempotencyKey is required").max(255, "idempotencyKey must be 255 characters or fewer").regex(
2447
+ var idempotencyKeySchema = z7.string().trim().min(1, "idempotencyKey is required").max(255, "idempotencyKey must be 255 characters or fewer").regex(
1326
2448
  /^[\x21-\x7E]+$/,
1327
2449
  "idempotencyKey must contain only printable header-safe characters"
1328
2450
  );
1329
- var cancelOrderSchema = z3.object({
1330
- orderNumber: z3.string().min(1, "orderNumber is required").describe("Order number to cancel"),
2451
+ var cancelOrderSchema = z7.object({
2452
+ orderNumber: z7.string().min(1, "orderNumber is required").describe("Order number to cancel"),
1331
2453
  reasonCode: cancelReasonCodeSchema.default("other").describe("Operator-selected cancel reason code"),
1332
- reasonDetail: z3.string().trim().max(2e3, "reasonDetail must be 2000 characters or fewer").optional().describe("Internal cancellation detail stored on the order")
2454
+ reasonDetail: z7.string().trim().max(2e3, "reasonDetail must be 2000 characters or fewer").optional().describe("Internal cancellation detail stored on the order")
1333
2455
  }).strict();
1334
2456
  var cancelOrderResponseBaseSchema = {
1335
- orderId: z3.string().min(1)
2457
+ orderId: z7.string().min(1)
1336
2458
  };
1337
2459
  var unpaidLocalCancelCommittedResponseFields = {
1338
- refundedAmount: z3.literal(0),
1339
- providerRefunded: z3.literal(false)
2460
+ refundedAmount: z7.literal(0),
2461
+ providerRefunded: z7.literal(false)
1340
2462
  };
1341
2463
  var paidLocalCancelCommittedResponseFields = {
1342
- transactionId: z3.string().min(1),
1343
- refundedAmount: z3.literal(0),
1344
- providerRefunded: z3.literal(false),
1345
- refundPending: z3.literal(true)
1346
- };
1347
- var legacyProviderRefundResponseFields = {
1348
- transactionId: z3.string().min(1),
1349
- refundedAmount: z3.number().int().positive(),
1350
- refundSeq: z3.number().int().positive(),
1351
- providerRefunded: z3.literal(true)
2464
+ transactionId: z7.string().min(1),
2465
+ refundedAmount: z7.literal(0),
2466
+ providerRefunded: z7.literal(false),
2467
+ refundPending: z7.literal(true)
1352
2468
  };
1353
2469
  var alreadyCanceledResponseFields = {
1354
- refundedAmount: z3.number().int().nonnegative(),
1355
- providerRefunded: z3.literal(false)
2470
+ refundedAmount: z7.number().int().nonnegative(),
2471
+ providerRefunded: z7.literal(false)
1356
2472
  };
1357
2473
  var alreadyCanceledRefundPendingResponseFields = {
1358
- transactionId: z3.string().min(1),
1359
- refundedAmount: z3.number().int().nonnegative(),
1360
- providerRefunded: z3.literal(false),
1361
- refundPending: z3.literal(true)
2474
+ transactionId: z7.string().min(1),
2475
+ refundedAmount: z7.number().int().nonnegative(),
2476
+ providerRefunded: z7.literal(false),
2477
+ refundPending: z7.literal(true)
1362
2478
  };
1363
- var cancelOrderReconciliationStatusSchema = z3.enum([
1364
- "paid",
1365
- "preparing",
1366
- "shipped",
1367
- "delivered",
1368
- "confirmed",
1369
- "return_requested",
1370
- "return_processing",
1371
- "returned",
1372
- "refunded"
1373
- ]);
1374
- var cancelOrderResponseSchema = z3.union([
1375
- z3.object({
2479
+ var cancelOrderResponseSchema = z7.union([
2480
+ z7.object({
1376
2481
  ...cancelOrderResponseBaseSchema,
1377
2482
  ...paidLocalCancelCommittedResponseFields,
1378
- status: z3.literal("canceled"),
1379
- cancelCommitted: z3.literal(true)
2483
+ status: z7.literal("canceled"),
2484
+ cancelCommitted: z7.literal(true)
1380
2485
  }).strict(),
1381
- z3.object({
2486
+ z7.object({
1382
2487
  ...cancelOrderResponseBaseSchema,
1383
2488
  ...unpaidLocalCancelCommittedResponseFields,
1384
- status: z3.literal("canceled"),
1385
- cancelCommitted: z3.literal(true)
2489
+ status: z7.literal("canceled"),
2490
+ cancelCommitted: z7.literal(true)
1386
2491
  }).strict(),
1387
- z3.object({
2492
+ z7.object({
1388
2493
  ...cancelOrderResponseBaseSchema,
1389
2494
  ...alreadyCanceledResponseFields,
1390
- status: z3.literal("canceled"),
1391
- cancelCommitted: z3.literal(false),
1392
- alreadyCanceled: z3.literal(true)
2495
+ status: z7.literal("canceled"),
2496
+ cancelCommitted: z7.literal(false),
2497
+ alreadyCanceled: z7.literal(true)
1393
2498
  }).strict(),
1394
- z3.object({
2499
+ z7.object({
1395
2500
  ...cancelOrderResponseBaseSchema,
1396
2501
  ...alreadyCanceledRefundPendingResponseFields,
1397
- status: z3.literal("canceled"),
1398
- cancelCommitted: z3.literal(false),
1399
- alreadyCanceled: z3.literal(true)
1400
- }).strict(),
1401
- z3.object({
1402
- ...cancelOrderResponseBaseSchema,
1403
- ...legacyProviderRefundResponseFields,
1404
- status: cancelOrderReconciliationStatusSchema,
1405
- cancelCommitted: z3.literal(false),
1406
- reconciliationRequired: z3.literal(true)
2502
+ status: z7.literal("canceled"),
2503
+ cancelCommitted: z7.literal(false),
2504
+ alreadyCanceled: z7.literal(true)
1407
2505
  }).strict()
1408
2506
  ]);
1409
- var resolveCancelRefundOutcomeSchema = z3.enum(["succeeded", "failed"]);
1410
- var resolveCancelRefundSchema = z3.object({
1411
- orderNumber: z3.string().min(1, "orderNumber is required").describe("Order number whose pending cancel refund is being resolved"),
2507
+ var resolveCancelRefundOutcomeSchema = z7.enum(["succeeded", "failed"]);
2508
+ var resolveCancelRefundSchema = z7.object({
2509
+ orderNumber: z7.string().min(1, "orderNumber is required").describe("Order number whose pending cancel refund is being resolved"),
1412
2510
  idempotencyKey: idempotencyKeySchema.describe(
1413
2511
  "Stable key for this PG refund result report"
1414
2512
  ),
1415
2513
  outcome: resolveCancelRefundOutcomeSchema.describe(
1416
2514
  "PG refund result reported by the storefront or BFF"
1417
2515
  ),
1418
- refundedAmount: z3.number().int("refundedAmount must be an integer minor-unit amount").nonnegative("refundedAmount must be nonnegative"),
1419
- pgProvider: z3.string().trim().min(1, "pgProvider is required").regex(
2516
+ refundedAmount: z7.number().int("refundedAmount must be an integer minor-unit amount").nonnegative("refundedAmount must be nonnegative"),
2517
+ pgProvider: z7.string().trim().min(1, "pgProvider is required").regex(
1420
2518
  /^[a-z0-9][a-z0-9_-]*$/,
1421
2519
  "pgProvider must be a lowercase provider slug"
1422
2520
  ),
1423
- pgRefundId: z3.string().trim().min(1).optional()
2521
+ pgRefundId: z7.string().trim().min(1).optional()
1424
2522
  }).strict().superRefine((value, ctx) => {
1425
2523
  if (value.outcome === "succeeded" && value.refundedAmount <= 0) {
1426
2524
  ctx.addIssue({
1427
- code: z3.ZodIssueCode.custom,
2525
+ code: z7.ZodIssueCode.custom,
1428
2526
  path: ["refundedAmount"],
1429
2527
  message: "refundedAmount must be positive when outcome is succeeded"
1430
2528
  });
1431
2529
  }
1432
2530
  if (value.outcome === "succeeded" && !value.pgRefundId) {
1433
2531
  ctx.addIssue({
1434
- code: z3.ZodIssueCode.custom,
2532
+ code: z7.ZodIssueCode.custom,
1435
2533
  path: ["pgRefundId"],
1436
2534
  message: "pgRefundId is required when outcome is succeeded"
1437
2535
  });
1438
2536
  }
1439
2537
  if (value.outcome === "failed" && value.refundedAmount !== 0) {
1440
2538
  ctx.addIssue({
1441
- code: z3.ZodIssueCode.custom,
2539
+ code: z7.ZodIssueCode.custom,
1442
2540
  path: ["refundedAmount"],
1443
2541
  message: "refundedAmount must be 0 when outcome is failed"
1444
2542
  });
1445
2543
  }
1446
2544
  });
1447
- var resolveCancelRefundResponseSchema = z3.union([
1448
- z3.object({
1449
- orderId: z3.string().min(1),
1450
- transactionId: z3.string().min(1),
1451
- refundTransactionId: z3.string().min(1),
1452
- refundedAmount: z3.number().int().positive(),
1453
- refundStatus: z3.literal("succeeded"),
1454
- transactionStatus: z3.literal("refunded")
2545
+ var resolveCancelRefundResponseSchema = z7.union([
2546
+ z7.object({
2547
+ orderId: z7.string().min(1),
2548
+ transactionId: z7.string().min(1),
2549
+ refundTransactionId: z7.string().min(1),
2550
+ refundedAmount: z7.number().int().positive(),
2551
+ refundStatus: z7.literal("succeeded"),
2552
+ transactionStatus: z7.enum(["paid", "refunded"])
1455
2553
  }).strict(),
1456
- z3.object({
1457
- orderId: z3.string().min(1),
1458
- transactionId: z3.string().min(1),
1459
- refundTransactionId: z3.string().min(1),
1460
- refundedAmount: z3.literal(0),
1461
- refundStatus: z3.literal("failed"),
1462
- transactionStatus: z3.literal("paid")
2554
+ z7.object({
2555
+ orderId: z7.string().min(1),
2556
+ transactionId: z7.string().min(1),
2557
+ refundTransactionId: z7.string().min(1),
2558
+ refundedAmount: z7.literal(0),
2559
+ refundStatus: z7.literal("failed"),
2560
+ transactionStatus: z7.literal("paid")
1463
2561
  }).strict()
1464
2562
  ]);
1465
2563
 
1466
2564
  // src/commands/order.ts
1467
- import { z as z4 } from "zod";
1468
- var idSchema = z4.union([z4.string().min(1), z4.number()]).transform(String);
1469
- var customerSnapshotSchema = z4.object({
1470
- email: z4.string().email("Invalid email format"),
1471
- name: z4.string().optional(),
1472
- phone: z4.string().optional()
1473
- }).strict();
1474
- var orderStatusSchema2 = z4.enum(["confirmed"]);
1475
- var fulfillmentStatusSchema = z4.enum(["shipped", "delivered", "failed"]);
1476
- var updateFulfillmentSchema = z4.object({
2565
+ import { z as z8 } from "zod";
2566
+ var idSchema = z8.union([z8.string().min(1), z8.number()]).transform(String);
2567
+ var orderStatusSchema = z8.enum(["confirmed"]);
2568
+ var fulfillmentStatusSchema = z8.enum(["shipped", "delivered", "failed"]);
2569
+ var updateFulfillmentSchema = z8.object({
1477
2570
  fulfillmentId: idSchema,
1478
2571
  status: fulfillmentStatusSchema.optional(),
1479
- carrier: z4.string().min(1).optional(),
1480
- trackingNumber: z4.string().min(1).optional()
2572
+ carrier: z8.string().min(1).optional(),
2573
+ trackingNumber: z8.string().min(1).optional()
1481
2574
  }).refine(
1482
2575
  (value) => value.status !== void 0 || value.carrier !== void 0 || value.trackingNumber !== void 0,
1483
2576
  {
1484
2577
  message: "status, carrier, or trackingNumber is required"
1485
2578
  }
1486
2579
  );
1487
- var fulfillmentItemsSchema = z4.array(
1488
- z4.object({
2580
+ var fulfillmentItemsSchema = z8.array(
2581
+ z8.object({
1489
2582
  orderItem: idSchema,
1490
- quantity: z4.number().int().positive("quantity must be a positive integer")
2583
+ quantity: z8.number().int().positive("quantity must be a positive integer")
1491
2584
  }).strict()
1492
2585
  ).min(1, "At least one fulfillment item is required").max(100, "Maximum 100 items per fulfillment");
1493
2586
  function registerOrderCommands(program2, getClient2, getFormat2) {
1494
2587
  const order = program2.command("order").description("Order management");
1495
- order.command("create").description("Create a new order").option("--payment-id <id>", "Payment ID").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) => {
1496
- try {
1497
- const data = parseWithSchema(
1498
- {
1499
- pgPaymentId: opts.paymentId,
1500
- orderNumber: opts.orderNumber,
1501
- customerSnapshot: {
1502
- email: opts.email,
1503
- name: opts.name,
1504
- phone: opts.phone
1505
- },
1506
- customer: opts.customer,
1507
- shippingAddress: parseJson(
1508
- opts.shippingAddress,
1509
- "shipping-address"
1510
- ),
1511
- orderItems: parseJsonArray(opts.products, "products"),
1512
- totalAmount: opts.totalAmount,
1513
- shippingAmount: opts.shippingAmount,
1514
- discountCode: opts.discountCode
1515
- },
1516
- "order",
1517
- createOrderSchema
1518
- );
1519
- if (opts.dryRun) {
1520
- printResult(
1521
- { dryRun: true, valid: true, action: "order create", data },
1522
- getFormat2()
1523
- );
1524
- return;
1525
- }
1526
- const client = getClient2();
1527
- const result = await client.commerce.orders.create(data);
1528
- printResult(result, getFormat2());
1529
- } catch (e) {
1530
- exitWithError(e);
1531
- }
1532
- });
1533
- order.command("get <orderNumber>").description("Get an order by order number").action(async (orderNumber) => {
1534
- try {
1535
- const client = getClient2();
1536
- const {
1537
- docs: [order2]
1538
- } = await client.collections.from("orders").find({
1539
- where: { orderNumber: { equals: orderNumber } },
1540
- limit: 1,
1541
- depth: 1
1542
- });
1543
- if (!order2) throw new Error("Order not found");
1544
- printResult(order2, getFormat2());
1545
- } catch (e) {
1546
- exitWithError(e);
1547
- }
1548
- });
1549
2588
  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) => {
1550
2589
  try {
1551
- const status = parseWithSchema(opts.status, "status", orderStatusSchema2);
2590
+ const status = parseWithSchema(opts.status, "status", orderStatusSchema);
1552
2591
  const data = { orderNumber, status };
1553
2592
  if (opts.dryRun) {
1554
2593
  printResult(
@@ -1592,36 +2631,6 @@ function registerOrderCommands(program2, getClient2, getFormat2) {
1592
2631
  exitWithError(e);
1593
2632
  }
1594
2633
  });
1595
- 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)").requiredOption("--order-number <num>", "Order number").requiredOption("--customer <json>", "Customer snapshot (JSON)").option("--dry-run", "Validate inputs without executing").action(async (opts) => {
1596
- try {
1597
- const customerSnapshot = parseWithSchema(
1598
- parseJson(opts.customer, "customer"),
1599
- "customer",
1600
- customerSnapshotSchema
1601
- );
1602
- const data = {
1603
- cartId: opts.cartId,
1604
- pgPaymentId: opts.paymentId,
1605
- orderNumber: opts.orderNumber,
1606
- customerSnapshot
1607
- };
1608
- if (opts.dryRun) {
1609
- printResult(
1610
- { dryRun: true, valid: true, action: "order checkout", data },
1611
- getFormat2()
1612
- );
1613
- return;
1614
- }
1615
- const client = getClient2();
1616
- const result = await client.commerce.orders.checkout({
1617
- ...data,
1618
- customerSnapshot
1619
- });
1620
- printResult(result, getFormat2());
1621
- } catch (e) {
1622
- exitWithError(e);
1623
- }
1624
- });
1625
2634
  order.command("prepare <orderNumber>").description("Move paid order fulfillment work to preparation").option("--dry-run", "Validate inputs without executing").action(async (orderNumber, opts) => {
1626
2635
  try {
1627
2636
  const data = { orderNumber };
@@ -1705,8 +2714,8 @@ function registerOrderCommands(program2, getClient2, getFormat2) {
1705
2714
  }
1706
2715
 
1707
2716
  // src/commands/return.ts
1708
- import { z as z5 } from "zod";
1709
- var returnStatusSchema = z5.enum([
2717
+ import { z as z9 } from "zod";
2718
+ var returnStatusSchema = z9.enum([
1710
2719
  "processing",
1711
2720
  "approved",
1712
2721
  "rejected",
@@ -1835,146 +2844,227 @@ function registerReturnCommands(program2, getClient2, getFormat2) {
1835
2844
  });
1836
2845
  }
1837
2846
 
2847
+ // src/lib/sensitive-json-input.ts
2848
+ import { createReadStream } from "fs";
2849
+ import { stdin as processStdin } from "process";
2850
+ var SENSITIVE_JSON_MAX_BYTES = 1024 * 1024;
2851
+ function invalidInput(message) {
2852
+ return {
2853
+ type: "validation",
2854
+ code: "invalid_argument",
2855
+ field: "data",
2856
+ message,
2857
+ detail: { message, field: "data" }
2858
+ };
2859
+ }
2860
+ function isInvalidInput(error) {
2861
+ if (!error || typeof error !== "object") return false;
2862
+ const value = error;
2863
+ return value.type === "validation" && value.code === "invalid_argument";
2864
+ }
2865
+ async function readBoundedUtf8(source) {
2866
+ const chunks = [];
2867
+ let totalBytes = 0;
2868
+ for await (const chunk of source) {
2869
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
2870
+ totalBytes += buffer.byteLength;
2871
+ if (totalBytes > SENSITIVE_JSON_MAX_BYTES) {
2872
+ throw invalidInput(
2873
+ `Sensitive commerce data must be ${SENSITIVE_JSON_MAX_BYTES} bytes or fewer.`
2874
+ );
2875
+ }
2876
+ chunks.push(buffer);
2877
+ }
2878
+ return Buffer.concat(chunks, totalBytes).toString("utf8");
2879
+ }
2880
+ async function readSensitiveJsonObject(opts, sources = {}) {
2881
+ const sourceCount = [opts.dataStdin, opts.dataFile].filter(Boolean).length;
2882
+ if (sourceCount !== 1) {
2883
+ throw invalidInput(
2884
+ "Exactly one of --data-stdin or --data-file is required for sensitive commerce data."
2885
+ );
2886
+ }
2887
+ let raw;
2888
+ const input = sources.stdin ?? processStdin;
2889
+ try {
2890
+ if (opts.dataStdin) {
2891
+ if (input.isTTY === true) {
2892
+ throw invalidInput(
2893
+ "Sensitive commerce data cannot be read from an interactive terminal. Pipe JSON to --data-stdin or use --data-file."
2894
+ );
2895
+ }
2896
+ raw = await readBoundedUtf8(input);
2897
+ } else {
2898
+ const file = (sources.openFile ?? createReadStream)(opts.dataFile);
2899
+ try {
2900
+ raw = await readBoundedUtf8(file);
2901
+ } finally {
2902
+ file.destroy?.();
2903
+ }
2904
+ }
2905
+ } catch (error) {
2906
+ if (isInvalidInput(error)) throw error;
2907
+ throw invalidInput("Sensitive commerce data could not be read.");
2908
+ }
2909
+ let parsed;
2910
+ try {
2911
+ parsed = JSON.parse(raw);
2912
+ } catch {
2913
+ throw invalidInput("Sensitive commerce data must be valid JSON.");
2914
+ }
2915
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
2916
+ throw invalidInput("Sensitive commerce data must be a JSON object.");
2917
+ }
2918
+ return parsed;
2919
+ }
2920
+ function sensitiveDryRunResult(action) {
2921
+ return { dryRun: true, valid: true, action };
2922
+ }
2923
+
1838
2924
  // src/commands/cart.ts
1839
2925
  function registerCartCommands(program2, getClient2, getFormat2) {
1840
2926
  const cart = program2.command("cart").description("Cart management");
1841
- cart.command("create").description("Create a cart and print its cartToken handle").option("--dry-run", "Validate inputs without executing").action(async (opts) => {
2927
+ 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) => {
1842
2928
  try {
2929
+ const input = opts.dataStdin || opts.dataFile ? await readSensitiveJsonObject(opts) : {};
2930
+ const data = parseWithSchema(
2931
+ input,
2932
+ "cart create",
2933
+ createCartParamsSchema
2934
+ );
1843
2935
  if (opts.dryRun) {
1844
- printResult(
1845
- { dryRun: true, valid: true, action: "cart create", data: {} },
1846
- getFormat2()
1847
- );
2936
+ printResult(sensitiveDryRunResult("cart create"), getFormat2());
1848
2937
  return;
1849
2938
  }
1850
- const client = getClient2();
1851
- const result = await client.commerce.cart.create();
1852
- printResult(result, getFormat2());
1853
- } catch (e) {
1854
- exitWithError(e);
2939
+ printResult(await getClient2().commerce.carts.create(data), getFormat2());
2940
+ } catch (error) {
2941
+ exitWithError(error);
1855
2942
  }
1856
2943
  });
1857
- 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(
1858
- "--quantity <n>",
1859
- "Quantity",
1860
- (v) => parseInt(v, 10)
1861
- ).option("--dry-run", "Validate inputs without executing").action(async (cartToken, opts) => {
2944
+ 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) => {
1862
2945
  try {
1863
- const data = {
1864
- cartToken,
1865
- product: opts.product,
1866
- variant: opts.variant,
1867
- option: opts.option,
1868
- quantity: opts.quantity
1869
- };
2946
+ const data = parseWithSchema(
2947
+ await readSensitiveJsonObject(opts),
2948
+ "cart retrieve",
2949
+ retrieveCartParamsSchema
2950
+ );
2951
+ printResult(
2952
+ await getClient2().commerce.carts.retrieve(data),
2953
+ getFormat2()
2954
+ );
2955
+ } catch (error) {
2956
+ exitWithError(error);
2957
+ }
2958
+ });
2959
+ 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) => {
2960
+ try {
2961
+ const data = parseWithSchema(
2962
+ await readSensitiveJsonObject(opts),
2963
+ "cart add",
2964
+ addItemParamsSchema
2965
+ );
1870
2966
  if (opts.dryRun) {
1871
- printResult(
1872
- { dryRun: true, valid: true, action: "cart add", data },
1873
- getFormat2()
1874
- );
2967
+ printResult(sensitiveDryRunResult("cart add"), getFormat2());
1875
2968
  return;
1876
2969
  }
1877
- const client = getClient2();
1878
- const result = await client.commerce.cart.addItem(data);
1879
- printResult(result, getFormat2());
1880
- } catch (e) {
1881
- exitWithError(e);
2970
+ printResult(await getClient2().commerce.carts.addItem(data), getFormat2());
2971
+ } catch (error) {
2972
+ exitWithError(error);
1882
2973
  }
1883
2974
  });
1884
- cart.command("update <cartItemId>").description("Update cart item quantity").requiredOption("--cart-token <token>", "Cart token (cart handle)").requiredOption(
1885
- "--quantity <n>",
1886
- "New quantity",
1887
- (v) => parseInt(v, 10)
1888
- ).option("--dry-run", "Validate inputs without executing").action(async (cartItemId, opts) => {
2975
+ 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) => {
1889
2976
  try {
1890
- const data = {
1891
- cartToken: opts.cartToken,
1892
- cartItemId,
1893
- quantity: opts.quantity
1894
- };
2977
+ const data = parseWithSchema(
2978
+ await readSensitiveJsonObject(opts),
2979
+ "cart update",
2980
+ updateItemParamsSchema
2981
+ );
1895
2982
  if (opts.dryRun) {
1896
- printResult(
1897
- { dryRun: true, valid: true, action: "cart update", data },
1898
- getFormat2()
1899
- );
2983
+ printResult(sensitiveDryRunResult("cart update"), getFormat2());
1900
2984
  return;
1901
2985
  }
1902
- const client = getClient2();
1903
- const result = await client.commerce.cart.updateItem(data);
1904
- printResult(result, getFormat2());
1905
- } catch (e) {
1906
- exitWithError(e);
2986
+ printResult(
2987
+ await getClient2().commerce.carts.updateItem(data),
2988
+ getFormat2()
2989
+ );
2990
+ } catch (error) {
2991
+ exitWithError(error);
1907
2992
  }
1908
2993
  });
1909
- 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) => {
2994
+ 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) => {
1910
2995
  try {
1911
- const data = { cartToken: opts.cartToken, cartItemId };
2996
+ const data = parseWithSchema(
2997
+ await readSensitiveJsonObject(opts),
2998
+ "cart remove",
2999
+ removeItemParamsSchema
3000
+ );
1912
3001
  if (opts.dryRun) {
1913
- printResult(
1914
- { dryRun: true, valid: true, action: "cart remove", data },
1915
- getFormat2()
1916
- );
3002
+ printResult(sensitiveDryRunResult("cart remove"), getFormat2());
1917
3003
  return;
1918
3004
  }
1919
- const client = getClient2();
1920
- const result = await client.commerce.cart.removeItem(data);
1921
- printResult(result, getFormat2());
1922
- } catch (e) {
1923
- exitWithError(e);
3005
+ printResult(
3006
+ await getClient2().commerce.carts.removeItem(data),
3007
+ getFormat2()
3008
+ );
3009
+ } catch (error) {
3010
+ exitWithError(error);
1924
3011
  }
1925
3012
  });
1926
- cart.command("clear <cartToken>").description("Remove all items from a cart").option("--dry-run", "Validate inputs without executing").action(async (cartToken, opts) => {
3013
+ 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) => {
1927
3014
  try {
3015
+ const data = parseWithSchema(
3016
+ await readSensitiveJsonObject(opts),
3017
+ "cart clear",
3018
+ clearCartParamsSchema
3019
+ );
1928
3020
  if (opts.dryRun) {
1929
- printResult(
1930
- { dryRun: true, valid: true, action: "cart clear", data: { cartToken } },
1931
- getFormat2()
1932
- );
3021
+ printResult(sensitiveDryRunResult("cart clear"), getFormat2());
1933
3022
  return;
1934
3023
  }
1935
- const client = getClient2();
1936
- const result = await client.commerce.cart.clear({ cartToken });
1937
- printResult(result, getFormat2());
1938
- } catch (e) {
1939
- exitWithError(e);
3024
+ printResult(await getClient2().commerce.carts.clear(data), getFormat2());
3025
+ } catch (error) {
3026
+ exitWithError(error);
1940
3027
  }
1941
3028
  });
1942
- 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) => {
3029
+ 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) => {
1943
3030
  try {
1944
- const data = { cartToken, discountCode: opts.code };
3031
+ const data = parseWithSchema(
3032
+ await readSensitiveJsonObject(opts),
3033
+ "cart discounts",
3034
+ updateDiscountCodesParamsSchema
3035
+ );
1945
3036
  if (opts.dryRun) {
1946
- printResult(
1947
- { dryRun: true, valid: true, action: "cart apply-discount", data },
1948
- getFormat2()
1949
- );
3037
+ printResult(sensitiveDryRunResult("cart discounts"), getFormat2());
1950
3038
  return;
1951
3039
  }
1952
- const client = getClient2();
1953
- const result = await client.commerce.cart.applyDiscount(data);
1954
- printResult(result, getFormat2());
1955
- } catch (e) {
1956
- exitWithError(e);
3040
+ printResult(
3041
+ await getClient2().commerce.carts.updateDiscountCodes(data),
3042
+ getFormat2()
3043
+ );
3044
+ } catch (error) {
3045
+ exitWithError(error);
1957
3046
  }
1958
3047
  });
1959
- cart.command("remove-discount <cartToken>").description("Remove the discount from a cart").option("--dry-run", "Validate inputs without executing").action(async (cartToken, opts) => {
3048
+ cart.command("attributes").description("Replace all public cart attributes; pass [] to clear").option("--data-stdin", "Read capability and attributes JSON from stdin").option(
3049
+ "--data-file <path>",
3050
+ "Read capability and attributes JSON from file"
3051
+ ).option("--dry-run", "Validate inputs without executing").action(async (opts) => {
1960
3052
  try {
3053
+ const data = parseWithSchema(
3054
+ await readSensitiveJsonObject(opts),
3055
+ "cart attributes",
3056
+ updateCartAttributesParamsSchema
3057
+ );
1961
3058
  if (opts.dryRun) {
1962
- printResult(
1963
- {
1964
- dryRun: true,
1965
- valid: true,
1966
- action: "cart remove-discount",
1967
- data: { cartToken }
1968
- },
1969
- getFormat2()
1970
- );
3059
+ printResult(sensitiveDryRunResult("cart attributes"), getFormat2());
1971
3060
  return;
1972
3061
  }
1973
- const client = getClient2();
1974
- const result = await client.commerce.cart.removeDiscount({ cartToken });
1975
- printResult(result, getFormat2());
1976
- } catch (e) {
1977
- exitWithError(e);
3062
+ printResult(
3063
+ await getClient2().commerce.carts.updateAttributes(data),
3064
+ getFormat2()
3065
+ );
3066
+ } catch (error) {
3067
+ exitWithError(error);
1978
3068
  }
1979
3069
  });
1980
3070
  }
@@ -1998,124 +3088,196 @@ function registerStockCommands(program2, getClient2, getFormat2) {
1998
3088
  }
1999
3089
 
2000
3090
  // src/commands/transaction.ts
2001
- import { z as z6 } from "zod";
2002
- var transactionAmountSchema = z6.object({
2003
- amount: z6.string().regex(/^[1-9]\d*$/, "amount must be a positive integer").transform(Number)
2004
- });
2005
- var confirmPaymentAmountSchema = z6.object({
2006
- amount: z6.string().regex(/^(0|[1-9]\d*)$/, "amount must be a non-negative integer").transform(Number)
2007
- });
2008
- var idempotencyKeyOptionSchema = z6.object({
2009
- idempotencyKey: idempotencyKeySchema
2010
- });
2011
- function parseTransactionAmount(value) {
2012
- if (value === void 0) return void 0;
2013
- return parseWithSchema({ amount: value }, "amount", transactionAmountSchema).amount;
2014
- }
2015
- function parseConfirmPaymentAmount(value) {
2016
- return parseWithSchema({ amount: value }, "amount", confirmPaymentAmountSchema).amount;
2017
- }
2018
- function parseIdempotencyKey(value) {
2019
- if (value === void 0) return void 0;
2020
- return parseWithSchema(
2021
- { idempotencyKey: value },
2022
- "idempotencyKey",
2023
- idempotencyKeyOptionSchema
2024
- ).idempotencyKey;
2025
- }
2026
- function registerTransactionCommands(program2, getClient2, getFormat2) {
2027
- const tx = program2.command("transaction").description("Transaction management");
2028
- tx.command("update").description("Update transaction status").requiredOption("--payment-id <id>", "Payment ID").requiredOption(
2029
- "--status <status>",
2030
- "New status (pending, paid, failed, canceled, refunded)"
2031
- ).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) => {
3091
+ function registerCommerceSessionCommands(program2, getClient2, getFormat2) {
3092
+ const checkout = program2.command("checkout").description("Checkout management");
3093
+ 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) => {
2032
3094
  try {
2033
3095
  const data = parseWithSchema(
2034
- {
2035
- pgPaymentId: opts.paymentId,
2036
- status: opts.status,
2037
- paymentMethod: opts.paymentMethod,
2038
- receiptUrl: opts.receiptUrl,
2039
- paymentKey: opts.paymentKey,
2040
- amount: parseTransactionAmount(opts.amount)
2041
- },
2042
- "transaction",
2043
- updateTransactionSchema
3096
+ await readSensitiveJsonObject(opts),
3097
+ "checkout",
3098
+ CreateServerCheckoutParamsSchema
3099
+ );
3100
+ if (opts.dryRun) {
3101
+ printResult(sensitiveDryRunResult("checkout create"), getFormat2());
3102
+ return;
3103
+ }
3104
+ printResult(
3105
+ await getClient2().commerce.checkouts.create(data),
3106
+ getFormat2()
3107
+ );
3108
+ } catch (error) {
3109
+ exitWithError(error);
3110
+ }
3111
+ });
3112
+ checkout.command("retrieve <checkoutId>").description("Retrieve a Checkout by ID with server credentials").action(async (checkoutId) => {
3113
+ try {
3114
+ printResult(
3115
+ await getClient2().commerce.checkouts.retrieve({ checkoutId }),
3116
+ getFormat2()
3117
+ );
3118
+ } catch (error) {
3119
+ exitWithError(error);
3120
+ }
3121
+ });
3122
+ checkout.command("query").description("Query lightweight Checkouts with server credentials").option("--params <json>", "Checkout query parameters (JSON)", "{}").action(async (opts) => {
3123
+ try {
3124
+ const data = parseWithSchema(
3125
+ parseJson(opts.params, "params"),
3126
+ "checkout query",
3127
+ CanonicalCheckoutQuerySchema
3128
+ );
3129
+ printResult(
3130
+ await getClient2().commerce.checkouts.query(data),
3131
+ getFormat2()
3132
+ );
3133
+ } catch (error) {
3134
+ exitWithError(error);
3135
+ }
3136
+ });
3137
+ const sessions = program2.command("payment-session").description("PaymentSession management");
3138
+ 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) => {
3139
+ try {
3140
+ const data = parseWithSchema(
3141
+ await readSensitiveJsonObject(opts),
3142
+ "payment-session",
3143
+ CreatePaymentSessionParamsSchema
2044
3144
  );
2045
3145
  if (opts.dryRun) {
2046
3146
  printResult(
2047
- { dryRun: true, valid: true, action: "transaction update", data },
3147
+ sensitiveDryRunResult("payment-session create"),
2048
3148
  getFormat2()
2049
3149
  );
2050
3150
  return;
2051
3151
  }
2052
- const client = getClient2();
2053
- const result = await client.commerce.orders.updateTransaction(data);
2054
- printResult(result, getFormat2());
2055
- } catch (e) {
2056
- exitWithError(e);
3152
+ printResult(
3153
+ await getClient2().commerce.paymentSessions.create(data),
3154
+ getFormat2()
3155
+ );
3156
+ } catch (error) {
3157
+ exitWithError(error);
3158
+ }
3159
+ });
3160
+ sessions.command("retrieve <paymentSessionId>").description("Retrieve a PaymentSession").action(async (paymentSessionId) => {
3161
+ try {
3162
+ printResult(
3163
+ await getClient2().commerce.paymentSessions.retrieve({
3164
+ paymentSessionId
3165
+ }),
3166
+ getFormat2()
3167
+ );
3168
+ } catch (error) {
3169
+ exitWithError(error);
2057
3170
  }
2058
3171
  });
2059
- tx.command("confirm-payment").description("Confirm a provider-verified payment for an order").requiredOption("--payment-id <id>", "Provider payment identifier (pgPaymentId)").requiredOption(
2060
- "--provider <slug>",
2061
- "Payment provider slug (toss, portone, stripe, ...)"
2062
- ).requiredOption(
2063
- "--amount <n>",
2064
- "Provider-confirmed amount in minor units"
2065
- ).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(
2066
- "--confirmation-source <src>",
2067
- "provider_webhook | provider_lookup | provider_api_confirm | manual_server"
2068
- ).option("--payment-key <key>", "Provider payment key").option("--dry-run", "Validate inputs without executing").action(async (opts) => {
3172
+ sessions.command("cancel").description("Cancel a PaymentSession and release its payment slot").option("--data-stdin", "Read session ID and idempotency key from stdin").option(
3173
+ "--data-file <path>",
3174
+ "Read session ID and idempotency key from file"
3175
+ ).option("--dry-run", "Validate inputs without executing").action(async (opts) => {
2069
3176
  try {
2070
3177
  const data = parseWithSchema(
2071
- {
2072
- pgPaymentId: opts.paymentId,
2073
- pgProvider: opts.provider,
2074
- amount: parseConfirmPaymentAmount(opts.amount),
2075
- ...opts.orderNumber ? { orderNumber: opts.orderNumber } : {},
2076
- ...opts.pgOrderId ? { pgOrderId: opts.pgOrderId } : {},
2077
- ...opts.currency ? { currency: opts.currency } : {},
2078
- ...opts.paymentMethod ? { paymentMethod: opts.paymentMethod } : {},
2079
- ...opts.receiptUrl ? { receiptUrl: opts.receiptUrl } : {},
2080
- ...opts.approvedAt ? { approvedAt: opts.approvedAt } : {},
2081
- ...opts.providerStatus ? { providerStatus: opts.providerStatus } : {},
2082
- ...opts.providerEventId ? { providerEventId: opts.providerEventId } : {},
2083
- ...opts.confirmationSource ? { confirmationSource: opts.confirmationSource } : {},
2084
- ...opts.paymentKey ? { paymentKey: opts.paymentKey } : {}
2085
- },
2086
- "confirm-payment",
2087
- confirmPaymentSchema
3178
+ await readSensitiveJsonObject(opts),
3179
+ "payment-session cancel",
3180
+ CancelServerPaymentSessionParamsSchema
2088
3181
  );
2089
- const sdkParams = {
2090
- ...data,
2091
- ...opts.idempotencyKey !== void 0 ? { idempotencyKey: parseIdempotencyKey(opts.idempotencyKey) } : {}
2092
- };
2093
3182
  if (opts.dryRun) {
2094
3183
  printResult(
2095
- {
2096
- dryRun: true,
2097
- valid: true,
2098
- action: "transaction confirm-payment",
2099
- data: sdkParams
2100
- },
3184
+ sensitiveDryRunResult("payment-session cancel"),
2101
3185
  getFormat2()
2102
3186
  );
2103
3187
  return;
2104
3188
  }
2105
- const client = getClient2();
2106
- const result = await client.commerce.orders.confirmPayment(
2107
- sdkParams
3189
+ printResult(
3190
+ await getClient2().commerce.paymentSessions.cancel(data),
3191
+ getFormat2()
2108
3192
  );
2109
- printResult(result, getFormat2());
2110
- } catch (e) {
2111
- exitWithError(e);
3193
+ } catch (error) {
3194
+ exitWithError(error);
3195
+ }
3196
+ });
3197
+ 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) => {
3198
+ try {
3199
+ const data = parseWithSchema(
3200
+ await readSensitiveJsonObject(opts),
3201
+ "payment-session confirm-toss",
3202
+ ConfirmTossPaymentSessionParamsSchema
3203
+ );
3204
+ if (opts.dryRun) {
3205
+ printResult(
3206
+ sensitiveDryRunResult("payment-session confirm-toss"),
3207
+ getFormat2()
3208
+ );
3209
+ return;
3210
+ }
3211
+ printResult(
3212
+ await getClient2().commerce.paymentSessions.confirm(data),
3213
+ getFormat2()
3214
+ );
3215
+ } catch (error) {
3216
+ exitWithError(error);
2112
3217
  }
2113
3218
  });
2114
3219
  }
2115
3220
 
3221
+ // src/lib/product-detail-options.ts
3222
+ function invalidOption(message, field, value) {
3223
+ const error = new Error(message);
3224
+ Object.assign(error, {
3225
+ type: "validation",
3226
+ code: "invalid_argument",
3227
+ field,
3228
+ detail: { message, value, field }
3229
+ });
3230
+ return error;
3231
+ }
3232
+ function parseLocale(value) {
3233
+ if (value == null) return void 0;
3234
+ if (value === "ko" || value === "en" || value === "all") return value;
3235
+ throw invalidOption("--locale must be one of: ko, en, all", "locale", value);
3236
+ }
3237
+ function parseFallbackLocale(value) {
3238
+ if (value == null) return void 0;
3239
+ if (value === "ko" || value === "en") return value;
3240
+ if (value === "none") return false;
3241
+ throw invalidOption(
3242
+ "--fallback-locale must be one of: ko, en, none",
3243
+ "fallbackLocale",
3244
+ value
3245
+ );
3246
+ }
3247
+ function productDetailParamsFromOptions(options) {
3248
+ const hasId = typeof options.id === "string" && options.id.length > 0;
3249
+ const hasSlug = typeof options.slug === "string" && options.slug.length > 0;
3250
+ if (hasId === hasSlug) {
3251
+ throw invalidOption(
3252
+ "Exactly one of --id or --slug is required.",
3253
+ "product",
3254
+ { id: options.id, slug: options.slug }
3255
+ );
3256
+ }
3257
+ const locale = parseLocale(options.locale);
3258
+ const fallbackLocale = parseFallbackLocale(options.fallbackLocale);
3259
+ const identifier = hasId ? { id: options.id } : { slug: options.slug };
3260
+ return {
3261
+ ...identifier,
3262
+ ...options.market !== void 0 ? { market: options.market } : {},
3263
+ ...locale !== void 0 ? { locale } : {},
3264
+ ...fallbackLocale !== void 0 ? { fallbackLocale } : {}
3265
+ };
3266
+ }
3267
+
2116
3268
  // src/commands/product.ts
2117
3269
  function registerProductCommands(program2, getClient2, getFormat2) {
2118
3270
  const product = program2.command("product").description("Product management");
3271
+ 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) => {
3272
+ try {
3273
+ const params = productDetailParamsFromOptions(opts);
3274
+ const client = getClient2();
3275
+ const result = await client.commerce.product.detail(params);
3276
+ printResult(result, getFormat2());
3277
+ } catch (e) {
3278
+ exitWithError(e);
3279
+ }
3280
+ });
2119
3281
  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) => {
2120
3282
  try {
2121
3283
  const data = parseWithSchema(
@@ -2169,12 +3331,14 @@ function registerShippingCommands(program2, getClient2, getFormat2) {
2169
3331
  "--order-amount <n>",
2170
3332
  "Order amount",
2171
3333
  (v) => parseInt(v, 10)
2172
- ).option("--shipping-policy-id <id>", "Shipping policy ID").option("--postal-code <code>", "Destination postal code").action(async (opts) => {
3334
+ ).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) => {
2173
3335
  try {
2174
3336
  const client = getClient2();
2175
3337
  const result = await client.commerce.shipping.calculate({
2176
3338
  orderAmount: opts.orderAmount,
2177
3339
  ...opts.shippingPolicyId ? { shippingPolicyId: opts.shippingPolicyId } : {},
3340
+ ...opts.countryCode ? { countryCode: opts.countryCode } : {},
3341
+ ...opts.regionCode ? { regionCode: opts.regionCode } : {},
2178
3342
  ...opts.postalCode ? { postalCode: opts.postalCode } : {}
2179
3343
  });
2180
3344
  printResult(result, getFormat2());
@@ -2185,7 +3349,7 @@ function registerShippingCommands(program2, getClient2, getFormat2) {
2185
3349
  }
2186
3350
 
2187
3351
  // src/commands/auth.ts
2188
- import { randomBytes } from "crypto";
3352
+ import { randomBytes as randomBytes2 } from "crypto";
2189
3353
  import { createServer } from "http";
2190
3354
  import { execFile, exec } from "child_process";
2191
3355
  import { platform } from "os";
@@ -2360,7 +3524,7 @@ Logged in successfully!`));
2360
3524
  }
2361
3525
  function registerAuthCommands(program2) {
2362
3526
  program2.command("login").description("Login via browser and store credentials").action(async () => {
2363
- const state = randomBytes(32).toString("hex");
3527
+ const state = randomBytes2(32).toString("hex");
2364
3528
  try {
2365
3529
  const { port } = await startAuthServer({
2366
3530
  state,
@@ -2449,7 +3613,7 @@ ${loginUrl}`));
2449
3613
  );
2450
3614
  process.exit(3);
2451
3615
  }
2452
- const state = randomBytes(32).toString("hex");
3616
+ const state = randomBytes2(32).toString("hex");
2453
3617
  const isLocal = !!opts.local;
2454
3618
  try {
2455
3619
  const { port } = await startAuthServer({
@@ -2548,9 +3712,15 @@ function registerFeatureCommands(program2, getClient2, getFormat2) {
2548
3712
  }
2549
3713
 
2550
3714
  // src/commands/agent.ts
3715
+ import { SERVER_COLLECTIONS as SERVER_COLLECTIONS4 } from "@01.software/sdk";
3716
+
3717
+ // src/lib/agent-plan-allowlist.ts
2551
3718
  import { SERVER_COLLECTIONS as SERVER_COLLECTIONS3 } from "@01.software/sdk";
2552
3719
 
2553
3720
  // src/lib/agent-output.ts
3721
+ import { randomBytes as randomBytes3 } from "crypto";
3722
+ import fs from "fs";
3723
+ import path from "path";
2554
3724
  var PLAN_AGENT_ERROR_CODES = /* @__PURE__ */ new Set([
2555
3725
  "PLAN_EXPIRED",
2556
3726
  "PLAN_MISMATCH",
@@ -2567,9 +3737,95 @@ function agentPlanError(code, message) {
2567
3737
  });
2568
3738
  return error;
2569
3739
  }
3740
+ var AGENT_OUTPUT_FILE_MODE = 384;
3741
+ var isWin32 = process.platform === "win32";
2570
3742
  function stringifyAgentJson(data, options = {}) {
2571
3743
  return options.pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data);
2572
3744
  }
3745
+ function invalidOutputTarget(message, outputFile) {
3746
+ const error = new Error(message);
3747
+ Object.assign(error, {
3748
+ type: "validation",
3749
+ code: "invalid_argument",
3750
+ field: "outputFile",
3751
+ detail: { message, field: "outputFile", value: outputFile }
3752
+ });
3753
+ return error;
3754
+ }
3755
+ function assertSafeAgentOutputTarget(outputFile) {
3756
+ let stat;
3757
+ try {
3758
+ stat = fs.lstatSync(outputFile);
3759
+ } catch (error) {
3760
+ if (error.code === "ENOENT") return;
3761
+ throw error;
3762
+ }
3763
+ if (stat.isSymbolicLink()) {
3764
+ throw invalidOutputTarget(
3765
+ `Refusing to write Agent output through a symlink: ${outputFile}.`,
3766
+ outputFile
3767
+ );
3768
+ }
3769
+ if (!stat.isFile()) {
3770
+ throw invalidOutputTarget(
3771
+ `Refusing to write Agent output to a non-regular file: ${outputFile}.`,
3772
+ outputFile
3773
+ );
3774
+ }
3775
+ if (!isWin32 && stat.nlink > 1) {
3776
+ throw invalidOutputTarget(
3777
+ `Refusing to write Agent output to a file with multiple hard links: ${outputFile}.`,
3778
+ outputFile
3779
+ );
3780
+ }
3781
+ }
3782
+ function writeAgentOutputFile(requestedPath, content) {
3783
+ const outputFile = path.resolve(requestedPath);
3784
+ assertSafeAgentOutputTarget(outputFile);
3785
+ const tempFile = path.join(
3786
+ path.dirname(outputFile),
3787
+ `.${path.basename(outputFile)}.${randomBytes3(12).toString("hex")}.tmp`
3788
+ );
3789
+ let tempFd;
3790
+ try {
3791
+ tempFd = fs.openSync(tempFile, "wx", AGENT_OUTPUT_FILE_MODE);
3792
+ if (!isWin32) {
3793
+ fs.fchmodSync(tempFd, AGENT_OUTPUT_FILE_MODE);
3794
+ }
3795
+ fs.writeFileSync(tempFd, content, { encoding: "utf8" });
3796
+ fs.closeSync(tempFd);
3797
+ tempFd = void 0;
3798
+ fs.renameSync(tempFile, outputFile);
3799
+ } catch (error) {
3800
+ if (tempFd !== void 0) {
3801
+ try {
3802
+ fs.closeSync(tempFd);
3803
+ } catch {
3804
+ }
3805
+ }
3806
+ try {
3807
+ fs.unlinkSync(tempFile);
3808
+ } catch {
3809
+ }
3810
+ throw error;
3811
+ }
3812
+ return {
3813
+ outputFile,
3814
+ bytes: Buffer.byteLength(content, "utf8")
3815
+ };
3816
+ }
3817
+ function resolveAgentPrintOptions(command) {
3818
+ const options = command.optsWithGlobals();
3819
+ return {
3820
+ pretty: Boolean(options.pretty),
3821
+ outputFile: options.outputFile
3822
+ };
3823
+ }
3824
+ function validateAgentOutputTarget(options) {
3825
+ if (options.outputFile !== void 0) {
3826
+ assertSafeAgentOutputTarget(path.resolve(options.outputFile));
3827
+ }
3828
+ }
2573
3829
  function asRecord(value) {
2574
3830
  return value && typeof value === "object" ? value : null;
2575
3831
  }
@@ -2595,6 +3851,20 @@ function isRawNotFound(error) {
2595
3851
  const raw = asRecord(error);
2596
3852
  return raw?.status === 404 || raw?.code === "not_found" || raw?.name === "GoneError";
2597
3853
  }
3854
+ function isSdkUsageLimitError(error) {
3855
+ const raw = asRecord(error);
3856
+ return raw?.name === "UsageLimitError" || raw?.code === "USAGE_LIMIT_ERROR";
3857
+ }
3858
+ function agentErrorEnvelope(code, message, retryable, retryAfter) {
3859
+ return {
3860
+ error: {
3861
+ code,
3862
+ message,
3863
+ retryable,
3864
+ ...typeof retryAfter === "number" ? { retryAfter } : {}
3865
+ }
3866
+ };
3867
+ }
2598
3868
  function agentErrorExitCode(code) {
2599
3869
  switch (code) {
2600
3870
  case "INVALID_INPUT":
@@ -2618,22 +3888,30 @@ function classifyAgentError(error) {
2618
3888
  const detailRecord = detail && typeof detail === "object" ? detail : null;
2619
3889
  const agentCode = raw?.agentCode ?? detailRecord?.agentCode;
2620
3890
  if (typeof agentCode === "string" && PLAN_AGENT_ERROR_CODES.has(agentCode)) {
2621
- return { error: { code: agentCode, message } };
3891
+ return agentErrorEnvelope(agentCode, message, false);
2622
3892
  }
2623
3893
  const adminError = classifyError(error);
2624
3894
  if (isRawNotFound(error) || adminError.code === "not_found") {
2625
- return { error: { code: "NOT_FOUND", message } };
3895
+ return agentErrorEnvelope("NOT_FOUND", message, false);
2626
3896
  }
2627
3897
  if (adminError.type === "permission") {
2628
- return { error: { code: "AUTH_FAILED", message } };
3898
+ return agentErrorEnvelope("AUTH_FAILED", message, false);
2629
3899
  }
2630
3900
  if (adminError.type === "validation" && adminError.code !== "unknown") {
2631
- return { error: { code: "INVALID_INPUT", message } };
3901
+ return agentErrorEnvelope("INVALID_INPUT", message, false);
2632
3902
  }
2633
- return { error: { code: "INTERNAL", message } };
3903
+ const retryable = !isSdkUsageLimitError(error) && (adminError.type === "network" || adminError.type === "degraded");
3904
+ const retryAfter = retryable && adminError.type === "degraded" && adminError.code === "rate_limited" ? adminError.retryAfter : void 0;
3905
+ return agentErrorEnvelope("INTERNAL", message, retryable, retryAfter);
2634
3906
  }
2635
3907
  function printAgentSuccess(data, options = {}) {
2636
- console.log(stringifyAgentJson(data, options));
3908
+ const output = stringifyAgentJson(data, options);
3909
+ if (options.outputFile !== void 0) {
3910
+ const receipt = writeAgentOutputFile(options.outputFile, output);
3911
+ console.log(JSON.stringify(receipt));
3912
+ return;
3913
+ }
3914
+ console.log(output);
2637
3915
  }
2638
3916
  function printAgentError(error, options = {}) {
2639
3917
  const envelope = classifyAgentError(error);
@@ -2646,25 +3924,77 @@ function exitWithAgentError(error, options = {}) {
2646
3924
  }
2647
3925
 
2648
3926
  // src/lib/agent-plan-allowlist.ts
2649
- var AGENT_PLAN_COLLECTIONS = [
2650
- "articles",
2651
- "article-categories",
2652
- "article-tags",
2653
- "links",
2654
- "link-categories",
2655
- "link-tags"
2656
- ];
2657
- var AGENT_PLAN_COLLECTION_SET = new Set(AGENT_PLAN_COLLECTIONS);
3927
+ var AGENT_PLAN_CAPABILITIES = {
3928
+ articles: ["create", "update", "delete"],
3929
+ "article-categories": ["create", "update", "delete"],
3930
+ "article-tags": ["create", "update", "delete"],
3931
+ links: ["create", "update", "delete"],
3932
+ "link-categories": ["create", "update", "delete"],
3933
+ "link-tags": ["create", "update", "delete"],
3934
+ documents: ["create", "update"],
3935
+ "document-categories": ["create", "update"],
3936
+ "document-types": ["create", "update"],
3937
+ canvases: ["create", "update", "delete"],
3938
+ "canvas-categories": ["create", "update", "delete"],
3939
+ "canvas-tags": ["create", "update", "delete"]
3940
+ };
3941
+ var SERVER_COLLECTION_SET = new Set(SERVER_COLLECTIONS3);
3942
+ var AGENT_PLAN_OPERATIONS = /* @__PURE__ */ new Set([
3943
+ "create",
3944
+ "update",
3945
+ "delete"
3946
+ ]);
3947
+ function assertAgentPlanCapabilityMatrix(matrix) {
3948
+ for (const [collection, operations] of Object.entries(matrix)) {
3949
+ if (!SERVER_COLLECTION_SET.has(collection)) {
3950
+ throw new Error(
3951
+ `Agent plan capability collection "${collection}" is not a ServerCollection.`
3952
+ );
3953
+ }
3954
+ if (operations.length === 0) {
3955
+ throw new Error(
3956
+ `Agent plan capability collection "${collection}" has no operations.`
3957
+ );
3958
+ }
3959
+ const seen = /* @__PURE__ */ new Set();
3960
+ for (const operation of operations) {
3961
+ if (!AGENT_PLAN_OPERATIONS.has(operation)) {
3962
+ throw new Error(
3963
+ `Agent plan capability operation "${operation}" is invalid for "${collection}".`
3964
+ );
3965
+ }
3966
+ if (seen.has(operation)) {
3967
+ throw new Error(
3968
+ `Agent plan capability operation "${operation}" is duplicated for "${collection}".`
3969
+ );
3970
+ }
3971
+ seen.add(operation);
3972
+ }
3973
+ }
3974
+ }
3975
+ assertAgentPlanCapabilityMatrix(AGENT_PLAN_CAPABILITIES);
2658
3976
  function isAgentPlanCollection(collection) {
2659
- return AGENT_PLAN_COLLECTION_SET.has(collection);
3977
+ return Object.prototype.hasOwnProperty.call(
3978
+ AGENT_PLAN_CAPABILITIES,
3979
+ collection
3980
+ );
3981
+ }
3982
+ function getAgentPlanOperations(collection) {
3983
+ return isAgentPlanCollection(collection) ? AGENT_PLAN_CAPABILITIES[collection] : [];
2660
3984
  }
2661
- function assertAgentPlanCollection(collection) {
2662
- if (isAgentPlanCollection(collection)) {
3985
+ function isAgentPlanCapability(collection, operation) {
3986
+ if (!isAgentPlanCollection(collection)) return false;
3987
+ return AGENT_PLAN_CAPABILITIES[collection].includes(
3988
+ operation
3989
+ );
3990
+ }
3991
+ function assertAgentPlanCapability(collection, operation) {
3992
+ if (isAgentPlanCapability(collection, operation)) {
2663
3993
  return collection;
2664
3994
  }
2665
3995
  throw agentPlanError(
2666
3996
  "UNSUPPORTED_OPERATION",
2667
- `Collection "${collection}" is not supported for agent plan mutations.`
3997
+ `Operation "${operation}" is not supported for collection "${collection}" in agent plan mutations.`
2668
3998
  );
2669
3999
  }
2670
4000
 
@@ -2767,6 +4097,12 @@ function parseStoredAgentPlan(raw, planId) {
2767
4097
  if (operation !== "create" && operation !== "update" && operation !== "delete") {
2768
4098
  throw agentPlanError("PLAN_MISMATCH", "Stored plan has invalid operation.");
2769
4099
  }
4100
+ if (!isAgentPlanCapability(collection, operation)) {
4101
+ throw agentPlanError(
4102
+ "PLAN_MISMATCH",
4103
+ "Stored plan has unsupported collection operation."
4104
+ );
4105
+ }
2770
4106
  const payload = record.payload == null ? void 0 : typeof record.payload === "object" && !Array.isArray(record.payload) ? record.payload : (() => {
2771
4107
  throw agentPlanError("PLAN_MISMATCH", "Stored plan payload is invalid.");
2772
4108
  })();
@@ -2791,6 +4127,12 @@ function parseStoredAgentPlan(raw, planId) {
2791
4127
  };
2792
4128
  }
2793
4129
  function assertStoredPlanHashMatches(plan) {
4130
+ if (!isAgentPlanCapability(plan.collection, plan.operation)) {
4131
+ throw agentPlanError(
4132
+ "PLAN_MISMATCH",
4133
+ "Stored plan has unsupported collection operation."
4134
+ );
4135
+ }
2794
4136
  const expected = hashAgentPlanEnvelope({
2795
4137
  operation: plan.operation,
2796
4138
  collection: plan.collection,
@@ -3011,10 +4353,10 @@ function isSecretFieldName(key) {
3011
4353
  const snake = key.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase();
3012
4354
  return SECRET_KEY.test(snake);
3013
4355
  }
3014
- function assertNoSecretFields(value, path = "") {
4356
+ function assertNoSecretFields(value, path2 = "") {
3015
4357
  if (Array.isArray(value)) {
3016
4358
  value.forEach((item, index) => {
3017
- assertNoSecretFields(item, `${path}[${index}]`);
4359
+ assertNoSecretFields(item, `${path2}[${index}]`);
3018
4360
  });
3019
4361
  return;
3020
4362
  }
@@ -3022,7 +4364,7 @@ function assertNoSecretFields(value, path = "") {
3022
4364
  return;
3023
4365
  }
3024
4366
  for (const [key, child] of Object.entries(value)) {
3025
- const fieldPath = path ? `${path}.${key}` : key;
4367
+ const fieldPath = path2 ? `${path2}.${key}` : key;
3026
4368
  if (isSecretFieldName(key)) {
3027
4369
  throw agentPlanError(
3028
4370
  "INVALID_INPUT",
@@ -3064,7 +4406,7 @@ function buildConfirmCommand(planToken, planHash) {
3064
4406
  }
3065
4407
 
3066
4408
  // src/commands/agent-plan.ts
3067
- function invalidInput(message, field) {
4409
+ function invalidInput2(message, field) {
3068
4410
  const error = new Error(message);
3069
4411
  Object.assign(error, {
3070
4412
  type: "validation",
@@ -3078,7 +4420,7 @@ function handleCommanderError(error) {
3078
4420
  if (error.code === "commander.helpDisplayed") {
3079
4421
  process.exit(error.exitCode);
3080
4422
  }
3081
- exitWithAgentError(invalidInput(error.message, "command"));
4423
+ exitWithAgentError(invalidInput2(error.message, "command"));
3082
4424
  }
3083
4425
  function configureAgentParser(command) {
3084
4426
  command.configureOutput({
@@ -3096,9 +4438,12 @@ async function captureBaseUpdatedAt(client, collection, documentId) {
3096
4438
  return readDocumentUpdatedAt(doc);
3097
4439
  }
3098
4440
  async function runPlanAction(input) {
3099
- const collection = assertAgentPlanCollection(input.collection);
4441
+ const collection = assertAgentPlanCapability(
4442
+ input.collection,
4443
+ input.operation
4444
+ );
3100
4445
  if (input.operation !== "create" && !input.documentId) {
3101
- throw invalidInput("Missing required argument: id", "id");
4446
+ throw invalidInput2("Missing required argument: id", "id");
3102
4447
  }
3103
4448
  let payload;
3104
4449
  if (input.operation === "create" || input.operation === "update") {
@@ -3160,7 +4505,7 @@ async function runPlanAction(input) {
3160
4505
  documentId: stored.documentId,
3161
4506
  confirmCommand: buildConfirmCommand(planToken, stored.planHash)
3162
4507
  },
3163
- { pretty: Boolean(input.pretty) }
4508
+ input.outputOptions
3164
4509
  );
3165
4510
  }
3166
4511
  function registerAgentPlanCommands(agent, getClient2) {
@@ -3168,63 +4513,83 @@ function registerAgentPlanCommands(agent, getClient2) {
3168
4513
  agent.command("plan").description("Plan a mutation without executing")
3169
4514
  );
3170
4515
  configureAgentParser(
3171
- 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(
3172
- async (collection, opts) => {
4516
+ 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(
4517
+ "--output-file <path>",
4518
+ "Write success JSON to a secure file and print a receipt"
4519
+ ).action(
4520
+ async (collection, opts, command) => {
4521
+ const outputOptions = resolveAgentPrintOptions(command);
3173
4522
  try {
4523
+ validateAgentOutputTarget(outputOptions);
3174
4524
  await runPlanAction({
3175
4525
  operation: "create",
3176
4526
  collection,
3177
4527
  dataStdin: opts.dataStdin,
3178
4528
  dataFile: opts.dataFile,
3179
- pretty: opts.pretty,
4529
+ outputOptions,
3180
4530
  getClient: getClient2
3181
4531
  });
3182
4532
  } catch (error) {
3183
- exitWithAgentError(error, { pretty: Boolean(opts.pretty) });
4533
+ exitWithAgentError(error, outputOptions);
3184
4534
  }
3185
4535
  }
3186
4536
  )
3187
4537
  );
3188
4538
  configureAgentParser(
3189
- 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(
3190
- async (collection, id, opts) => {
4539
+ 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(
4540
+ "--output-file <path>",
4541
+ "Write success JSON to a secure file and print a receipt"
4542
+ ).action(
4543
+ async (collection, id, opts, command) => {
4544
+ const outputOptions = resolveAgentPrintOptions(command);
3191
4545
  try {
4546
+ validateAgentOutputTarget(outputOptions);
3192
4547
  await runPlanAction({
3193
4548
  operation: "update",
3194
4549
  collection,
3195
4550
  documentId: id,
3196
4551
  dataStdin: opts.dataStdin,
3197
4552
  dataFile: opts.dataFile,
3198
- pretty: opts.pretty,
4553
+ outputOptions,
3199
4554
  getClient: getClient2
3200
4555
  });
3201
4556
  } catch (error) {
3202
- exitWithAgentError(error, { pretty: Boolean(opts.pretty) });
4557
+ exitWithAgentError(error, outputOptions);
3203
4558
  }
3204
4559
  }
3205
4560
  )
3206
4561
  );
3207
4562
  configureAgentParser(
3208
- plan.command("delete <collection> <id>").description("Plan a document delete").option("--pretty", "Print 2-space indented JSON").action(
3209
- async (collection, id, opts) => {
4563
+ plan.command("delete <collection> <id>").description("Plan a document delete").option("--pretty", "Print 2-space indented success JSON").option(
4564
+ "--output-file <path>",
4565
+ "Write success JSON to a secure file and print a receipt"
4566
+ ).action(
4567
+ async (collection, id, opts, command) => {
4568
+ const outputOptions = resolveAgentPrintOptions(command);
3210
4569
  try {
4570
+ validateAgentOutputTarget(outputOptions);
3211
4571
  await runPlanAction({
3212
4572
  operation: "delete",
3213
4573
  collection,
3214
4574
  documentId: id,
3215
- pretty: opts.pretty,
4575
+ outputOptions,
3216
4576
  getClient: getClient2
3217
4577
  });
3218
4578
  } catch (error) {
3219
- exitWithAgentError(error, { pretty: Boolean(opts.pretty) });
4579
+ exitWithAgentError(error, outputOptions);
3220
4580
  }
3221
4581
  }
3222
4582
  )
3223
4583
  );
3224
4584
  configureAgentParser(
3225
- 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(
3226
- async (planTokenRaw, opts) => {
4585
+ 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(
4586
+ "--output-file <path>",
4587
+ "Write success JSON to a secure file and print a receipt"
4588
+ ).action(
4589
+ async (planTokenRaw, opts, command) => {
4590
+ const outputOptions = resolveAgentPrintOptions(command);
3227
4591
  try {
4592
+ validateAgentOutputTarget(outputOptions);
3228
4593
  const token = decodePlanToken(planTokenRaw);
3229
4594
  if (token.planHash !== opts.hash) {
3230
4595
  throw agentPlanError(
@@ -3270,10 +4635,10 @@ function registerAgentPlanCommands(agent, getClient2) {
3270
4635
  id: doc && typeof doc === "object" && "id" in doc ? String(doc.id) : plan2.documentId,
3271
4636
  doc
3272
4637
  },
3273
- { pretty: Boolean(opts.pretty) }
4638
+ outputOptions
3274
4639
  );
3275
4640
  } catch (error) {
3276
- exitWithAgentError(error, { pretty: Boolean(opts.pretty) });
4641
+ exitWithAgentError(error, outputOptions);
3277
4642
  }
3278
4643
  }
3279
4644
  )
@@ -3289,7 +4654,7 @@ function buildStableAgentFields() {
3289
4654
  updatedAt: { type: "string", queryable: true }
3290
4655
  };
3291
4656
  }
3292
- function invalidInput2(message, field, value) {
4657
+ function invalidInput3(message, field, value) {
3293
4658
  const error = new Error(message);
3294
4659
  Object.assign(error, {
3295
4660
  type: "validation",
@@ -3302,13 +4667,13 @@ function invalidInput2(message, field, value) {
3302
4667
  function parsePositiveInteger(value, field) {
3303
4668
  if (value == null) return void 0;
3304
4669
  if (/^[1-9]\d*$/.test(value)) return Number(value);
3305
- throw invalidInput2(`--${field} must be a positive integer`, field, value);
4670
+ throw invalidInput3(`--${field} must be a positive integer`, field, value);
3306
4671
  }
3307
4672
  function handleCommanderError2(error) {
3308
4673
  if (error.code === "commander.helpDisplayed") {
3309
4674
  process.exit(error.exitCode);
3310
4675
  }
3311
- exitWithAgentError(invalidInput2(error.message, "command"));
4676
+ exitWithAgentError(invalidInput3(error.message, "command"));
3312
4677
  }
3313
4678
  function configureAgentParser2(command) {
3314
4679
  command.configureOutput({
@@ -3329,23 +4694,27 @@ function parseWhere(value) {
3329
4694
  return parsed;
3330
4695
  }
3331
4696
  } catch {
3332
- throw invalidInput2("--where must be a JSON object", "where", value);
4697
+ throw invalidInput3("--where must be a JSON object", "where", value);
3333
4698
  }
3334
- throw invalidInput2("--where must be a JSON object", "where", value);
4699
+ throw invalidInput3("--where must be a JSON object", "where", value);
3335
4700
  }
3336
4701
  var AGENT_READ_OPERATIONS = [
3337
4702
  "query",
3338
4703
  "get"
3339
4704
  ];
3340
- var AGENT_PLAN_OPERATIONS = [
3341
- "plan:create",
3342
- "plan:update",
3343
- "plan:delete"
4705
+ var AGENT_PRODUCT_OPERATIONS = [
4706
+ "detail"
3344
4707
  ];
3345
4708
  function buildAgentManifest() {
3346
4709
  const collections = {};
3347
- for (const collection of SERVER_COLLECTIONS3) {
3348
- const operations = isAgentPlanCollection(collection) ? [...AGENT_READ_OPERATIONS, ...AGENT_PLAN_OPERATIONS] : [...AGENT_READ_OPERATIONS];
4710
+ for (const collection of SERVER_COLLECTIONS4) {
4711
+ const operations = [
4712
+ ...AGENT_READ_OPERATIONS,
4713
+ ...collection === "products" ? AGENT_PRODUCT_OPERATIONS : [],
4714
+ ...getAgentPlanOperations(collection).map(
4715
+ (operation) => `plan:${operation}`
4716
+ )
4717
+ ];
3349
4718
  collections[collection] = {
3350
4719
  operations,
3351
4720
  fields: buildStableAgentFields()
@@ -3353,6 +4722,10 @@ function buildAgentManifest() {
3353
4722
  }
3354
4723
  return {
3355
4724
  version: AGENT_PROTOCOL_VERSION,
4725
+ discovery: {
4726
+ context: { counts: true },
4727
+ schema: { mode: "effective" }
4728
+ },
3356
4729
  collections
3357
4730
  };
3358
4731
  }
@@ -3361,27 +4734,75 @@ function validateAgentCollection(collection) {
3361
4734
  return validateCollection(collection);
3362
4735
  } catch (error) {
3363
4736
  const message = error instanceof Error ? error.message : `Unknown collection "${collection}".`;
3364
- throw invalidInput2(message, "collection", collection);
4737
+ throw invalidInput3(message, "collection", collection);
3365
4738
  }
3366
4739
  }
3367
4740
  function registerAgentCommands(program2, getClient2) {
3368
4741
  const agent = configureAgentParser2(
3369
- program2.command("agent").description("Machine-stable CLI namespace for coding agents").addHelpText(
4742
+ program2.command("agent").description("Machine-stable CLI namespace for coding agents").option("--pretty", "Print 2-space indented success JSON").option(
4743
+ "--output-file <path>",
4744
+ "Write success JSON to a secure file and print a receipt"
4745
+ ).addHelpText(
3370
4746
  "after",
3371
4747
  "\nContract: docs/agent-cli-contract.md\nJSON only on stdout; no TTY effects."
3372
4748
  )
3373
4749
  );
3374
4750
  configureAgentParser2(
3375
- agent.command("manifest").description("Print the Agent CLI protocol manifest").option("--pretty", "Print 2-space indented JSON").action((opts) => {
3376
- printAgentSuccess(buildAgentManifest(), {
3377
- pretty: Boolean(opts.pretty)
3378
- });
4751
+ agent.command("manifest").description("Print the Agent CLI protocol manifest").option("--pretty", "Print 2-space indented success JSON").option(
4752
+ "--output-file <path>",
4753
+ "Write success JSON to a secure file and print a receipt"
4754
+ ).action((_opts, command) => {
4755
+ const outputOptions = resolveAgentPrintOptions(command);
4756
+ try {
4757
+ validateAgentOutputTarget(outputOptions);
4758
+ printAgentSuccess(buildAgentManifest(), outputOptions);
4759
+ } catch (error) {
4760
+ exitWithAgentError(error, outputOptions);
4761
+ }
4762
+ })
4763
+ );
4764
+ configureAgentParser2(
4765
+ agent.command("context").description("Get the active tenant context").option("--counts", "Include collection document counts").option("--pretty", "Print 2-space indented success JSON").option(
4766
+ "--output-file <path>",
4767
+ "Write success JSON to a secure file and print a receipt"
4768
+ ).action(async (opts, command) => {
4769
+ const outputOptions = resolveAgentPrintOptions(command);
4770
+ try {
4771
+ validateAgentOutputTarget(outputOptions);
4772
+ const client = getClient2();
4773
+ const result = opts.counts ? await client.tenant.context({ includeCounts: true }) : await client.tenant.context();
4774
+ printAgentSuccess(result, outputOptions);
4775
+ } catch (error) {
4776
+ exitWithAgentError(error, outputOptions);
4777
+ }
4778
+ })
4779
+ );
4780
+ configureAgentParser2(
4781
+ agent.command("schema <collection>").description("Get the effective field schema for a collection").option("--pretty", "Print 2-space indented success JSON").option(
4782
+ "--output-file <path>",
4783
+ "Write success JSON to a secure file and print a receipt"
4784
+ ).action(async (collection, _opts, command) => {
4785
+ const outputOptions = resolveAgentPrintOptions(command);
4786
+ try {
4787
+ validateAgentOutputTarget(outputOptions);
4788
+ const col = validateAgentCollection(collection);
4789
+ const client = getClient2();
4790
+ const result = await client.tenant.collectionSchema(col);
4791
+ printAgentSuccess(result, outputOptions);
4792
+ } catch (error) {
4793
+ exitWithAgentError(error, outputOptions);
4794
+ }
3379
4795
  })
3380
4796
  );
3381
4797
  configureAgentParser2(
3382
- 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(
3383
- async (collection, opts) => {
4798
+ 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(
4799
+ "--output-file <path>",
4800
+ "Write success JSON to a secure file and print a receipt"
4801
+ ).action(
4802
+ async (collection, opts, command) => {
4803
+ const outputOptions = resolveAgentPrintOptions(command);
3384
4804
  try {
4805
+ validateAgentOutputTarget(outputOptions);
3385
4806
  const col = validateAgentCollection(collection);
3386
4807
  const options = {};
3387
4808
  const where = parseWhere(opts.where);
@@ -3394,28 +4815,55 @@ function registerAgentCommands(program2, getClient2) {
3394
4815
  if (opts.select) options.select = parseSelect(opts.select);
3395
4816
  const client = getClient2();
3396
4817
  const result = await client.collections.from(col).find(options);
3397
- printAgentSuccess(result, { pretty: Boolean(opts.pretty) });
4818
+ printAgentSuccess(result, outputOptions);
3398
4819
  } catch (error) {
3399
- exitWithAgentError(error, { pretty: Boolean(opts.pretty) });
4820
+ exitWithAgentError(error, outputOptions);
3400
4821
  }
3401
4822
  }
3402
4823
  )
3403
4824
  );
3404
4825
  configureAgentParser2(
3405
- 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(
3406
- async (collection, id, opts) => {
4826
+ 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(
4827
+ "--output-file <path>",
4828
+ "Write success JSON to a secure file and print a receipt"
4829
+ ).action(
4830
+ async (collection, id, opts, command) => {
4831
+ const outputOptions = resolveAgentPrintOptions(command);
3407
4832
  try {
4833
+ validateAgentOutputTarget(outputOptions);
3408
4834
  const col = validateAgentCollection(collection);
3409
4835
  if (!id) {
3410
- throw invalidInput2("Missing required argument: id", "id");
4836
+ throw invalidInput3("Missing required argument: id", "id");
3411
4837
  }
3412
4838
  const options = {};
3413
4839
  if (opts.select) options.select = parseSelect(opts.select);
3414
4840
  const client = getClient2();
3415
4841
  const result = await client.collections.from(col).findById(id, options);
3416
- printAgentSuccess(result, { pretty: Boolean(opts.pretty) });
4842
+ printAgentSuccess(result, outputOptions);
4843
+ } catch (error) {
4844
+ exitWithAgentError(error, outputOptions);
4845
+ }
4846
+ }
4847
+ )
4848
+ );
4849
+ const product = configureAgentParser2(
4850
+ agent.command("product").description("Shaped product reads")
4851
+ );
4852
+ configureAgentParser2(
4853
+ 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(
4854
+ "--output-file <path>",
4855
+ "Write success JSON to a secure file and print a receipt"
4856
+ ).action(
4857
+ async (opts, command) => {
4858
+ const outputOptions = resolveAgentPrintOptions(command);
4859
+ try {
4860
+ validateAgentOutputTarget(outputOptions);
4861
+ const params = productDetailParamsFromOptions(opts);
4862
+ const client = getClient2();
4863
+ const result = await client.commerce.product.detail(params);
4864
+ printAgentSuccess(result, outputOptions);
3417
4865
  } catch (error) {
3418
- exitWithAgentError(error, { pretty: Boolean(opts.pretty) });
4866
+ exitWithAgentError(error, outputOptions);
3419
4867
  }
3420
4868
  }
3421
4869
  )
@@ -3445,7 +4893,7 @@ registerOrderCommands(program, getClient, getFormat);
3445
4893
  registerReturnCommands(program, getClient, getFormat);
3446
4894
  registerCartCommands(program, getClient, getFormat);
3447
4895
  registerStockCommands(program, getClient, getFormat);
3448
- registerTransactionCommands(program, getClient, getFormat);
4896
+ registerCommerceSessionCommands(program, getClient, getFormat);
3449
4897
  registerProductCommands(program, getClient, getFormat);
3450
4898
  registerDiscountCommands(program, getClient, getFormat);
3451
4899
  registerShippingCommands(program, getClient, getFormat);