@cimplify/cli 0.15.3 → 0.16.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.
Files changed (120) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +2 -2
  3. package/dist/add-SXC6RUPQ.mjs +140 -0
  4. package/dist/{add-KRIIKAS4.mjs → add-XQVQLD3F.mjs} +4 -4
  5. package/dist/ai-NQH5L4I6.mjs +17 -0
  6. package/dist/{assets-CWB72PZG.mjs → assets-FZGVOQXH.mjs} +12 -7
  7. package/dist/{auth-step-up-Z6KOTTV7.mjs → auth-step-up-67LCPDNL.mjs} +5 -5
  8. package/dist/build-J4ZUIOPX.mjs +625 -0
  9. package/dist/{cancel-ZHVVILOU.mjs → cancel-VGJHZJ4J.mjs} +4 -4
  10. package/dist/capture-RAJ2B3BH.mjs +69 -0
  11. package/dist/chunk-25ZPFHLF.mjs +51 -0
  12. package/dist/chunk-47ZXSY47.mjs +52 -0
  13. package/dist/{chunk-O2TO2J4I.mjs → chunk-54J3XR4T.mjs} +2 -2
  14. package/dist/chunk-5HP2INMY.mjs +10631 -0
  15. package/dist/chunk-7JR6JCFF.mjs +32 -0
  16. package/dist/chunk-7PQL7E7K.mjs +68 -0
  17. package/dist/chunk-AKACUJ3W.mjs +81 -0
  18. package/dist/chunk-BYNE553A.mjs +2945 -0
  19. package/dist/{chunk-NYXA6X56.mjs → chunk-CLQYPTJ2.mjs} +31 -11
  20. package/dist/chunk-CNZAOVSK.mjs +104 -0
  21. package/dist/chunk-CPBESPWK.mjs +5576 -0
  22. package/dist/{chunk-3RPR33MO.mjs → chunk-DYWUPHV3.mjs} +11 -7
  23. package/dist/chunk-EAV2P4N3.mjs +1941 -0
  24. package/dist/{chunk-UTD34C2H.mjs → chunk-EDZSOL5O.mjs} +2 -4
  25. package/dist/chunk-EGQIPYUN.mjs +696 -0
  26. package/dist/{chunk-DMLQBAGC.mjs → chunk-EOO4UOQD.mjs} +1 -1
  27. package/dist/chunk-EYEMPIMP.mjs +74 -0
  28. package/dist/chunk-G2ZEFSQJ.mjs +46 -0
  29. package/dist/{chunk-TH4XENKH.mjs → chunk-I4HNHPEM.mjs} +1 -1
  30. package/dist/chunk-JEVSFNPT.mjs +363 -0
  31. package/dist/chunk-JKECUAAV.mjs +2169 -0
  32. package/dist/chunk-KMHORI4Y.mjs +638 -0
  33. package/dist/chunk-KNPYBZ6B.mjs +502 -0
  34. package/dist/{chunk-3QUK5BPY.mjs → chunk-KOD4GBQ5.mjs} +8 -1
  35. package/dist/chunk-KQNZKV6E.mjs +535 -0
  36. package/dist/{chunk-2MKZKUVH.mjs → chunk-LLRUOIMU.mjs} +1 -2
  37. package/dist/{chunk-OLZKNUMK.mjs → chunk-LNMITJ4Z.mjs} +6 -6
  38. package/dist/chunk-MEM3MDV4.mjs +6463 -0
  39. package/dist/chunk-NY4YT7O6.mjs +84 -0
  40. package/dist/{chunk-LZEG3FEY.mjs → chunk-Q6CF4C3E.mjs} +7 -7
  41. package/dist/chunk-RP66A52P.mjs +86 -0
  42. package/dist/{chunk-I2UP7YTG.mjs → chunk-SAXGWS2K.mjs} +1 -1
  43. package/dist/chunk-SCKQ4XSD.mjs +41 -0
  44. package/dist/chunk-SF44L2OD.mjs +1152 -0
  45. package/dist/chunk-SGT5IJVW.mjs +28 -0
  46. package/dist/{chunk-LJEKKFC7.mjs → chunk-SS4JTMIV.mjs} +8 -8
  47. package/dist/chunk-T4MW3PD4.mjs +1456 -0
  48. package/dist/chunk-THATLMEX.mjs +1227 -0
  49. package/dist/{chunk-MLE4MVYC.mjs → chunk-TT4YI33G.mjs} +2 -2
  50. package/dist/chunk-TYV2SVFN.mjs +101 -0
  51. package/dist/chunk-VHK7WBO3.mjs +189 -0
  52. package/dist/chunk-VSA44UKR.mjs +391 -0
  53. package/dist/chunk-XL54SQJ3.mjs +42 -0
  54. package/dist/{chunk-5KYR6WJB.mjs → chunk-XQAHREKO.mjs} +17 -1
  55. package/dist/chunk-ZUCDXRFI.mjs +2011 -0
  56. package/dist/create-4S2EAUR3.mjs +10 -0
  57. package/dist/credits-NJOAOPPW.mjs +33 -0
  58. package/dist/{deploy-STWZ3OSM.mjs → deploy-26Y4UGZZ.mjs} +11 -11
  59. package/dist/dev-MOYURRWQ.mjs +401 -0
  60. package/dist/dev-WL43Q7VD.mjs +8 -0
  61. package/dist/dispatcher.mjs +131 -30
  62. package/dist/{doctor-3RW3OGZJ.mjs → doctor-E6ETD2GJ.mjs} +12 -20
  63. package/dist/{domains-FTNK4IKY.mjs → domains-LOTQMVOR.mjs} +6 -6
  64. package/dist/engine-runtime-fetch-CIE7S7PB.mjs +10 -0
  65. package/dist/{env-TNCYUAMT.mjs → env-3PDFC5XF.mjs} +7 -7
  66. package/dist/env-MOTYOPM5.mjs +19 -0
  67. package/dist/explain-K5QEVWCD.mjs +251 -0
  68. package/dist/{inspect-X3M7WSTL.mjs → inspect-Q3E2DHYY.mjs} +3 -3
  69. package/dist/inspect-SJ4WBAXO.mjs +86 -0
  70. package/dist/introspect-7F34YN5V.mjs +9 -0
  71. package/dist/kernel-DEGWMHFL.mjs +662 -0
  72. package/dist/kernel-plugin.js +6862 -0
  73. package/dist/keys-W5SN3DPK.mjs +71 -0
  74. package/dist/lines-XGJYTQ7P.mjs +56 -0
  75. package/dist/{link-76Z6V6CJ.mjs → link-NKUAVNM2.mjs} +6 -6
  76. package/dist/{lint-DDZ7HUQN.mjs → lint-G4YPQSSO.mjs} +2 -2
  77. package/dist/{list-2MFOQKKQ.mjs → list-TDR7K2JB.mjs} +3 -3
  78. package/dist/lock-EJPW2LM3.mjs +57 -0
  79. package/dist/{login-RLMDEWAL.mjs → login-AYLU7JU7.mjs} +10 -8
  80. package/dist/{logout-Z57X6HPE.mjs → logout-3M6VMHLH.mjs} +2 -2
  81. package/dist/{logs-TNDM4RFE.mjs → logs-4EL5SOXN.mjs} +5 -5
  82. package/dist/logs-4QQGG2XY.mjs +44 -0
  83. package/dist/mcp-4R2R6E5M.mjs +1077 -0
  84. package/dist/{mock-EVER34IX.mjs → mock-AK24GSP2.mjs} +2 -2
  85. package/dist/outdated-JEKCVLTG.mjs +58 -0
  86. package/dist/port-SBZ7X3EH.mjs +2408 -0
  87. package/dist/{projects-YL4T24VV.mjs → projects-7JKTC5WJ.mjs} +6 -6
  88. package/dist/publish-QQCX47HK.mjs +20 -0
  89. package/dist/remix-NLLYD3BI.mjs +125 -0
  90. package/dist/repo-Z4G4EW3N.mjs +9 -0
  91. package/dist/report-M7F4ZYP7.mjs +2 -0
  92. package/dist/rollback-2WQ2CW7T.mjs +43 -0
  93. package/dist/{rollback-47VQ4HJK.mjs → rollback-ZGU32HGT.mjs} +6 -6
  94. package/dist/scene-HXARHUM7.mjs +324 -0
  95. package/dist/{status-TWPOMWNA.mjs → status-C3ZSLZ7Q.mjs} +5 -5
  96. package/dist/status-WEQOC25K.mjs +18 -0
  97. package/dist/test-ZPBWV4VX.mjs +233 -0
  98. package/dist/testers-A3LMXOJJ.mjs +49 -0
  99. package/dist/{unlink-H3BO3DOQ.mjs → unlink-4G4FE4RJ.mjs} +2 -2
  100. package/dist/{update-FLY46XI3.mjs → update-GHKKM5FS.mjs} +4 -4
  101. package/dist/{whoami-CQPPARRS.mjs → whoami-3QQN6G2A.mjs} +3 -3
  102. package/dist/workshop-CD74D2LK.mjs +1703 -0
  103. package/package.json +11 -7
  104. package/templates/storefront-auto/app/products/[slug]/page.tsx +1 -1
  105. package/templates/storefront-auto/package.json +1 -1
  106. package/templates/storefront-bakery/package.json +1 -1
  107. package/templates/storefront-fashion/app/products/[slug]/page.tsx +1 -1
  108. package/templates/storefront-fashion/package.json +1 -1
  109. package/templates/storefront-grocery/package.json +1 -1
  110. package/templates/storefront-pharmacy/app/products/[slug]/page.tsx +1 -1
  111. package/templates/storefront-pharmacy/package.json +1 -1
  112. package/templates/storefront-restaurant/package.json +1 -1
  113. package/templates/storefront-retail/app/products/[slug]/page.tsx +1 -1
  114. package/templates/storefront-retail/package.json +1 -1
  115. package/templates/storefront-services/package.json +1 -1
  116. package/dist/chunk-ARIDYKLO.mjs +0 -6463
  117. package/dist/dev-7O65HY6C.mjs +0 -8
  118. package/dist/explain-DLRUUCNS.mjs +0 -251
  119. package/dist/introspect-RC265ACF.mjs +0 -9
  120. package/dist/repo-UUT3AZ3L.mjs +0 -9
@@ -0,0 +1,233 @@
1
+ #!/usr/bin/env node
2
+ import { readinessScript, readinessGate, READINESS_GATE } from './chunk-47ZXSY47.mjs';
3
+ import { runSession, prepareSession } from './chunk-TYV2SVFN.mjs';
4
+ import { buildGates, frameGates, browserRequired } from './chunk-KMHORI4Y.mjs';
5
+ import { CHECK_DEVICES, DEFAULT_FRAMES, loadScript, DEFAULT_DEVICE, compareImages, decodePng, encodePng, GOLDEN, parseDevice, DEFAULT_SEED } from './chunk-KNPYBZ6B.mjs';
6
+ import { locateChromium } from './chunk-BYNE553A.mjs';
7
+ import './chunk-NY4YT7O6.mjs';
8
+ import './chunk-SF44L2OD.mjs';
9
+ import './chunk-VSA44UKR.mjs';
10
+ import './chunk-EGQIPYUN.mjs';
11
+ import './chunk-SCKQ4XSD.mjs';
12
+ import { buildLocal, workPath, requireInteractiveProject } from './chunk-CPBESPWK.mjs';
13
+ import './chunk-VHK7WBO3.mjs';
14
+ import './chunk-5HP2INMY.mjs';
15
+ import './chunk-JKECUAAV.mjs';
16
+ import { formatFinding } from './chunk-THATLMEX.mjs';
17
+ import './chunk-T4MW3PD4.mjs';
18
+ import './chunk-KQNZKV6E.mjs';
19
+ import { detectPackageManager } from './chunk-LNMITJ4Z.mjs';
20
+ import './chunk-SAXGWS2K.mjs';
21
+ import './chunk-G2ZEFSQJ.mjs';
22
+ import { locateToolchain } from './chunk-ZUCDXRFI.mjs';
23
+ import './chunk-EYEMPIMP.mjs';
24
+ import './chunk-AKACUJ3W.mjs';
25
+ import './chunk-25ZPFHLF.mjs';
26
+ import { formatDiagnostic } from './chunk-SGT5IJVW.mjs';
27
+ import './chunk-LLRUOIMU.mjs';
28
+ import { parseArgs, flagBool } from './chunk-XQAHREKO.mjs';
29
+ import './chunk-CLQYPTJ2.mjs';
30
+ import './chunk-TT4YI33G.mjs';
31
+ import { failure, warn, info, dim, success, CliError, CLI_ERROR_CODE, result } from './chunk-KOD4GBQ5.mjs';
32
+ import { spawn } from 'child_process';
33
+ import { promises, existsSync } from 'fs';
34
+ import path from 'path';
35
+
36
+ var UI_GATE = "game UI";
37
+ var VISUAL_GATE = "visual";
38
+ var VISUAL_DIR = "test/visual";
39
+ var GOLDEN_DIR = "goldens";
40
+ var SCRIPT_EXTENSIONS = [".json", ".ts"];
41
+ var CANVAS_PROFILE = "canvas";
42
+ var GAME_API = "game_api";
43
+ var REPORTED_FINDINGS = 12;
44
+ var SEPARATOR = "; ";
45
+ var DEFAULT_DEPS = {
46
+ prepare: prepareSession,
47
+ run: runSession,
48
+ browser: (setup) => {
49
+ const found = locateChromium([setup.toolchain.base, setup.toolchain.sdkDir]);
50
+ return typeof found === "string" ? found : null;
51
+ },
52
+ toolchain: (root) => locateToolchain(root) !== null
53
+ };
54
+ function gate(name, status, detail) {
55
+ return { name, status, detail };
56
+ }
57
+ function request(device, script, scriptPath) {
58
+ const parsed = parseDevice(device);
59
+ if (typeof parsed === "string") throw new Error(parsed);
60
+ return { device: parsed, backend: "swiftshader", seed: script?.seed ?? DEFAULT_SEED, scene: script?.scene ?? null, frame: DEFAULT_FRAMES, script, scriptPath, screenshots: true, inspect: true };
61
+ }
62
+ async function visualScripts(root) {
63
+ const dir = path.join(root, VISUAL_DIR);
64
+ if (!existsSync(dir)) return [];
65
+ const names = await promises.readdir(dir);
66
+ return names.filter((n) => SCRIPT_EXTENSIONS.includes(path.extname(n))).sort().map((n) => path.join(dir, n));
67
+ }
68
+ function goldenPath(root, script, capture, device) {
69
+ const base = path.basename(script, path.extname(script));
70
+ return path.join(root, VISUAL_DIR, GOLDEN_DIR, `${base}-${capture}-${device}.png`);
71
+ }
72
+ async function checkGate(setup, deps) {
73
+ const lines = [];
74
+ let findings = 0;
75
+ let exempted = 0;
76
+ let unresolved = 0;
77
+ for (const device of CHECK_DEVICES) {
78
+ const frames = await deps.run(setup, request(device, null, null));
79
+ for (const frame of frames) {
80
+ const report = frame.inspect;
81
+ if (report === null) continue;
82
+ findings += report.checks.findings.length;
83
+ exempted += report.checks.exempted.length;
84
+ unresolved += report.checks.unresolved.length;
85
+ for (const f of report.checks.findings) if (lines.length < REPORTED_FINDINGS) lines.push(`${device}: ${formatFinding(f)}`);
86
+ for (const invalid of report.checks.invalid_exemptions) lines.push(`${device}: ${invalid}`);
87
+ for (const problem of report.problems) if (lines.length < REPORTED_FINDINGS) lines.push(`${device}: ${problem}`);
88
+ }
89
+ }
90
+ const summary = `${findings} findings on ${CHECK_DEVICES.join(", ")} at frame ${DEFAULT_FRAMES} (${exempted} exempted with a reason, ${unresolved} contrast checks without a known background)`;
91
+ if (findings > 0) return gate(UI_GATE, "failed", [summary, ...lines].join(SEPARATOR));
92
+ return gate(UI_GATE, "passed", lines.length === 0 ? summary : [summary, ...lines].join(SEPARATOR));
93
+ }
94
+ async function visualGate(root, setup, options, deps) {
95
+ const scripts = await visualScripts(root);
96
+ if (scripts.length === 0) return gate(VISUAL_GATE, "skipped", `add input scripts under ${VISUAL_DIR}/ with capture steps to compare frames against goldens`);
97
+ const misses = [];
98
+ const written = [];
99
+ let compared = 0;
100
+ for (const file of scripts) {
101
+ const script = await loadScript(file);
102
+ for (const device of script.devices ?? [DEFAULT_DEVICE]) {
103
+ const frames = await deps.run(setup, request(device, script, path.relative(root, file)));
104
+ for (const frame of frames) {
105
+ const golden = goldenPath(root, file, frame.sidecar.name, device);
106
+ const label = path.relative(root, golden);
107
+ if (options.update) {
108
+ await promises.mkdir(path.dirname(golden), { recursive: true });
109
+ await promises.writeFile(golden, frame.png);
110
+ written.push(label);
111
+ continue;
112
+ }
113
+ if (!existsSync(golden)) {
114
+ misses.push(`${label} has no golden; run cimplify test --visual --update and commit it`);
115
+ continue;
116
+ }
117
+ compared++;
118
+ const diff = compareImages(decodePng(frame.png), decodePng(await promises.readFile(golden)));
119
+ if (diff.passed) continue;
120
+ const actual = workPath(root, "visual", `${path.basename(golden, ".png")}.actual.png`);
121
+ await promises.mkdir(path.dirname(actual), { recursive: true });
122
+ await promises.writeFile(actual, frame.png);
123
+ if (diff.diff !== null) await promises.writeFile(actual.replace(/\.actual\.png$/, ".diff.png"), encodePng(diff.diff));
124
+ misses.push(
125
+ diff.size_matches ? `${label}: mean ${diff.mean.toFixed(3)} (limit ${GOLDEN.mean}), outliers ${diff.outliers.toFixed(4)} (limit ${GOLDEN.outliers}); actual and diff in ${path.relative(root, path.dirname(actual))}` : `${label}: the capture is a different size from the golden`
126
+ );
127
+ }
128
+ }
129
+ }
130
+ if (options.update) return gate(VISUAL_GATE, "passed", `wrote ${written.length} goldens: ${written.join(", ")}`);
131
+ if (misses.length > 0) return gate(VISUAL_GATE, "failed", misses.join(SEPARATOR));
132
+ return gate(VISUAL_GATE, "passed", `${compared} captures match their goldens (mean <= ${GOLDEN.mean}, outliers <= ${GOLDEN.outliers})`);
133
+ }
134
+ async function uiGates(root, build, options, deps = DEFAULT_DEPS) {
135
+ const profile = build.result.manifest?.profile ?? null;
136
+ if (profile === null || profile.kind !== CANVAS_PROFILE || profile.game !== GAME_API) {
137
+ return options.visual ? [gate(VISUAL_GATE, "skipped", "visual goldens capture canvas games built on the game API")] : [];
138
+ }
139
+ const readiness = readinessScript(root) !== null;
140
+ const unavailable = (reason) => {
141
+ const status = browserRequired() ? "failed" : "skipped";
142
+ const names = options.visual ? [UI_GATE, VISUAL_GATE] : [UI_GATE];
143
+ if (readiness) names.push(READINESS_GATE);
144
+ return names.map((name) => gate(name, status, reason));
145
+ };
146
+ if (!deps.toolchain(root)) return unavailable("the pinned SDK is not installed, so the game cannot be served");
147
+ let setup;
148
+ try {
149
+ setup = await deps.prepare(root);
150
+ } catch (error) {
151
+ return [gate(UI_GATE, "failed", error.message)];
152
+ }
153
+ const missing = deps.browser(setup);
154
+ if (missing !== null) return unavailable(missing);
155
+ const out = [];
156
+ try {
157
+ out.push(await checkGate(setup, deps));
158
+ } catch (error) {
159
+ out.push(gate(UI_GATE, "failed", `the game could not be inspected: ${error.message}`));
160
+ }
161
+ if (options.visual) {
162
+ try {
163
+ out.push(await visualGate(root, setup, options, deps));
164
+ } catch (error) {
165
+ out.push(gate(VISUAL_GATE, "failed", `the visual captures did not run: ${error.message}`));
166
+ }
167
+ }
168
+ if (readiness) {
169
+ try {
170
+ out.push(await readinessGate(root, setup, deps.run));
171
+ } catch (error) {
172
+ out.push(gate(READINESS_GATE, "failed", `the readiness run did not complete: ${error.message}`));
173
+ }
174
+ }
175
+ return out;
176
+ }
177
+
178
+ // src/interactives/commands/test.ts
179
+ var TEST_DIR = "test";
180
+ var TEST_SCRIPT = "test";
181
+ async function hasTestScript(root) {
182
+ try {
183
+ const pkg = JSON.parse(await promises.readFile(path.join(root, "package.json"), "utf8"));
184
+ return typeof pkg.scripts?.[TEST_SCRIPT] === "string";
185
+ } catch {
186
+ return false;
187
+ }
188
+ }
189
+ var runPackageTests = async (root) => {
190
+ if (!await hasTestScript(root)) return { name: "project tests", status: "skipped", detail: "package.json has no test script" };
191
+ const pm = await detectPackageManager(root);
192
+ const code = await new Promise((resolve, reject) => {
193
+ const child = spawn(pm, ["run", TEST_SCRIPT], { cwd: root, stdio: "inherit" });
194
+ child.on("error", reject);
195
+ child.on("close", (c) => resolve(c ?? 1));
196
+ });
197
+ return code === 0 ? { name: "project tests", status: "passed", detail: `${pm} run ${TEST_SCRIPT}` } : { name: "project tests", status: "failed", detail: `${pm} run ${TEST_SCRIPT} exited with ${code}` };
198
+ };
199
+ async function testProject(root, options) {
200
+ const gates = [];
201
+ if (options.projectTests !== null) gates.push(await options.projectTests(root));
202
+ const local = await buildLocal(root);
203
+ for (const d of local.result.diagnostics) (d.severity === "error" ? failure : warn)(formatDiagnostic(d));
204
+ for (const g of buildGates(local.result)) gates.push(g);
205
+ const frames = await frameGates(workPath(root, TEST_DIR), local.result, locateToolchain(root));
206
+ for (const g of frames.gates) gates.push(g);
207
+ if (options.ui !== void 0 && options.ui !== null) for (const g of await options.ui(root, local)) gates.push(g);
208
+ return { ok: gates.every((g) => g.status !== "failed"), gates, lockUpdated: local.lockUpdated };
209
+ }
210
+ async function run(argv) {
211
+ const args = parseArgs(argv);
212
+ const root = process.cwd();
213
+ requireInteractiveProject(root, "test");
214
+ const visual = { visual: flagBool(args, "visual"), update: flagBool(args, "update") };
215
+ const report = await testProject(root, {
216
+ projectTests: flagBool(args, "skip-project-tests") ? null : runPackageTests,
217
+ ui: (dir, build) => uiGates(dir, build, visual)
218
+ });
219
+ if (report.lockUpdated) info(dim("Pinned newly used libraries in cimplify.lock"));
220
+ for (const g of report.gates) {
221
+ const line = `${g.name}: ${g.detail}`;
222
+ if (g.status === "passed") success(line);
223
+ else if (g.status === "failed") failure(line);
224
+ else info(dim(`${line} (skipped)`));
225
+ }
226
+ if (!report.ok) {
227
+ const failed = report.gates.filter((g) => g.status === "failed").map((g) => g.name);
228
+ throw new CliError(CLI_ERROR_CODE.BUILD_FAILED, `gates failed: ${failed.join(", ")}; admission would refuse this source set`, { details: report });
229
+ }
230
+ result(report);
231
+ }
232
+
233
+ export { run as default, runPackageTests, testProject };
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env node
2
+ import { remoteFromEnv } from './chunk-EGQIPYUN.mjs';
3
+ import { readInteractiveId, remoteCall } from './chunk-SCKQ4XSD.mjs';
4
+ import { requireInteractiveProject } from './chunk-CPBESPWK.mjs';
5
+ import './chunk-VHK7WBO3.mjs';
6
+ import './chunk-5HP2INMY.mjs';
7
+ import './chunk-JKECUAAV.mjs';
8
+ import './chunk-T4MW3PD4.mjs';
9
+ import './chunk-KQNZKV6E.mjs';
10
+ import './chunk-ZUCDXRFI.mjs';
11
+ import './chunk-EYEMPIMP.mjs';
12
+ import './chunk-AKACUJ3W.mjs';
13
+ import './chunk-25ZPFHLF.mjs';
14
+ import './chunk-SGT5IJVW.mjs';
15
+ import { parseArgs } from './chunk-XQAHREKO.mjs';
16
+ import './chunk-CLQYPTJ2.mjs';
17
+ import './chunk-TT4YI33G.mjs';
18
+ import { CliError, CLI_ERROR_CODE, success, info, dim, result } from './chunk-KOD4GBQ5.mjs';
19
+
20
+ // src/interactives/commands/testers.ts
21
+ var USAGE = "usage: cimplify testers add|remove <account_id>, or cimplify testers list";
22
+ var ADD = "add";
23
+ var REMOVE = "remove";
24
+ var LIST = "list";
25
+ function parseTesterCommand(positional) {
26
+ const [action, accountId] = positional;
27
+ if (action === LIST) return { action };
28
+ if ((action === ADD || action === REMOVE) && accountId) return { action, accountId };
29
+ throw new CliError(CLI_ERROR_CODE.INVALID_INPUT, USAGE);
30
+ }
31
+ async function manageTesters(root, remote, command) {
32
+ const id = await readInteractiveId(root);
33
+ if (command.action === ADD) await remoteCall(() => remote.addTester(id, command.accountId));
34
+ else if (command.action === REMOVE) await remoteCall(() => remote.removeTester(id, command.accountId));
35
+ return remoteCall(() => remote.testers(id));
36
+ }
37
+ async function run(argv) {
38
+ const command = parseTesterCommand(parseArgs(argv).positional);
39
+ const root = process.cwd();
40
+ requireInteractiveProject(root, "testers");
41
+ const testers = await manageTesters(root, await remoteFromEnv(), command);
42
+ if (command.action === ADD) success(`${command.accountId} can open the test channel`);
43
+ else if (command.action === REMOVE) success(`${command.accountId} no longer opens the test channel`);
44
+ for (const t of testers) info(`${t.accountId} ${dim(`added ${t.addedAt}`)}`);
45
+ if (testers.length === 0) info(dim("no testers; only you can open the test channel"));
46
+ result(testers);
47
+ }
48
+
49
+ export { run as default, manageTesters, parseTesterCommand };
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { clearProjectLink } from './chunk-MLE4MVYC.mjs';
3
- import { success, info, result } from './chunk-3QUK5BPY.mjs';
2
+ import { clearProjectLink } from './chunk-TT4YI33G.mjs';
3
+ import { success, info, result } from './chunk-KOD4GBQ5.mjs';
4
4
 
5
5
  // src/commands/unlink.ts
6
6
  async function run(_argv) {
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { promptYesNo } from './chunk-TH4XENKH.mjs';
3
- import { package_default } from './chunk-3RPR33MO.mjs';
4
- import { parseArgs, flagBool, flagString } from './chunk-5KYR6WJB.mjs';
5
- import { success, bold, info, dim, result, failure, CliError, CLI_ERROR_CODE, step, isJsonMode } from './chunk-3QUK5BPY.mjs';
2
+ import { promptYesNo } from './chunk-I4HNHPEM.mjs';
3
+ import { package_default } from './chunk-DYWUPHV3.mjs';
4
+ import { parseArgs, flagBool, flagString } from './chunk-XQAHREKO.mjs';
5
+ import { success, bold, info, dim, result, failure, CliError, CLI_ERROR_CODE, step, isJsonMode } from './chunk-KOD4GBQ5.mjs';
6
6
  import { spawn } from 'child_process';
7
7
  import { basename, dirname } from 'path';
8
8
 
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { ApiClient } from './chunk-NYXA6X56.mjs';
3
- import { readAuth } from './chunk-MLE4MVYC.mjs';
4
- import { info, bold, dim, result } from './chunk-3QUK5BPY.mjs';
2
+ import { ApiClient } from './chunk-CLQYPTJ2.mjs';
3
+ import { readAuth } from './chunk-TT4YI33G.mjs';
4
+ import { info, bold, dim, result } from './chunk-KOD4GBQ5.mjs';
5
5
 
6
6
  // src/commands/whoami.ts
7
7
  var ENDPOINT_AUTH_ME = "/v1/auth/cli/me";