promigence 0.1.0-beta.12

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 (97) hide show
  1. package/NOTICE +14 -0
  2. package/README.md +88 -0
  3. package/dist/account.js +850 -0
  4. package/dist/agent.js +374 -0
  5. package/dist/argv.js +159 -0
  6. package/dist/bin.js +332 -0
  7. package/dist/bundle.js +133 -0
  8. package/dist/capacity.js +69 -0
  9. package/dist/commands.d.ts +110 -0
  10. package/dist/commands.js +2675 -0
  11. package/dist/connectreq.js +125 -0
  12. package/dist/credfile.js +188 -0
  13. package/dist/credits.js +197 -0
  14. package/dist/devcontainer.d.ts +99 -0
  15. package/dist/devcontainer.js +744 -0
  16. package/dist/device.js +129 -0
  17. package/dist/dirsnap.js +378 -0
  18. package/dist/errfix.js +262 -0
  19. package/dist/generated.d.ts +35 -0
  20. package/dist/generated.js +307 -0
  21. package/dist/github.js +307 -0
  22. package/dist/gitlab.js +210 -0
  23. package/dist/guide.d.ts +19 -0
  24. package/dist/guide.js +366 -0
  25. package/dist/handlers.js +511 -0
  26. package/dist/index.d.ts +14 -0
  27. package/dist/index.js +7 -0
  28. package/dist/init.d.ts +40 -0
  29. package/dist/init.js +352 -0
  30. package/dist/install.d.ts +23 -0
  31. package/dist/install.js +127 -0
  32. package/dist/io.d.ts +37 -0
  33. package/dist/io.js +318 -0
  34. package/dist/live.js +469 -0
  35. package/dist/login.js +1063 -0
  36. package/dist/mcp.js +889 -0
  37. package/dist/money.js +5 -0
  38. package/dist/orgs.js +475 -0
  39. package/dist/pricing.d.ts +52 -0
  40. package/dist/pricing.js +181 -0
  41. package/dist/report.js +132 -0
  42. package/dist/routes.js +135 -0
  43. package/dist/runners-github.js +257 -0
  44. package/dist/runners-gitlab.js +125 -0
  45. package/dist/runners-spec.js +137 -0
  46. package/dist/runners.js +1458 -0
  47. package/dist/sdk/blake3.d.ts +10 -0
  48. package/dist/sdk/blake3.js +111 -0
  49. package/dist/sdk/bundle.d.ts +96 -0
  50. package/dist/sdk/bundle.js +227 -0
  51. package/dist/sdk/ca-guard.js +166 -0
  52. package/dist/sdk/caller.d.ts +73 -0
  53. package/dist/sdk/caller.js +47 -0
  54. package/dist/sdk/client.d.ts +3015 -0
  55. package/dist/sdk/client.js +2583 -0
  56. package/dist/sdk/command-errors.d.ts +41 -0
  57. package/dist/sdk/command-errors.js +36 -0
  58. package/dist/sdk/context.d.ts +119 -0
  59. package/dist/sdk/context.js +588 -0
  60. package/dist/sdk/core.d.ts +285 -0
  61. package/dist/sdk/core.js +266 -0
  62. package/dist/sdk/credstore.d.ts +90 -0
  63. package/dist/sdk/credstore.js +206 -0
  64. package/dist/sdk/dispatcher.js +86 -0
  65. package/dist/sdk/github.d.ts +158 -0
  66. package/dist/sdk/github.js +78 -0
  67. package/dist/sdk/gitlab.d.ts +104 -0
  68. package/dist/sdk/gitlab.js +20 -0
  69. package/dist/sdk/handles.d.ts +82 -0
  70. package/dist/sdk/handles.js +135 -0
  71. package/dist/sdk/hint.js +7 -0
  72. package/dist/sdk/http2.js +1921 -0
  73. package/dist/sdk/index.d.ts +34 -0
  74. package/dist/sdk/index.js +15 -0
  75. package/dist/sdk/keyed.gen.js +7 -0
  76. package/dist/sdk/models.gen.d.ts +6162 -0
  77. package/dist/sdk/models.gen.js +1 -0
  78. package/dist/sdk/proxy.js +249 -0
  79. package/dist/sdk/pty.d.ts +171 -0
  80. package/dist/sdk/pty.js +496 -0
  81. package/dist/sdk/sandbox.d.ts +537 -0
  82. package/dist/sdk/sandbox.js +725 -0
  83. package/dist/sdk/ssh.d.ts +25 -0
  84. package/dist/sdk/ssh.js +114 -0
  85. package/dist/sdk/trace.js +80 -0
  86. package/dist/sdk/transport-worker.js +466 -0
  87. package/dist/sdk/warm.js +40 -0
  88. package/dist/ssh.js +267 -0
  89. package/dist/surface.d.ts +95 -0
  90. package/dist/surface.js +1452 -0
  91. package/dist/templates.js +53 -0
  92. package/dist/term.js +50 -0
  93. package/dist/timing.js +71 -0
  94. package/dist/untar.js +324 -0
  95. package/dist/uploadguard.js +75 -0
  96. package/llms.txt +209 -0
  97. package/package.json +52 -0
@@ -0,0 +1,1452 @@
1
+ import { VERSION, DOCS_URL } from "./sdk/index.js";
2
+ import { SANDBOX_STATES } from "./generated.js";
3
+ export { VERSION, DOCS_URL };
4
+ export const SCHEMA = "promigence.cli/v1";
5
+ export const GUIDE_POINTER = "AI agents: run 'promigence agent-guide' (~1.4k tokens) before using this tool.";
6
+ export const DESCRIPTION = "Promigence — verified, reproducible, forkable execution environments for agents.";
7
+ export const GOLDEN_PATH = "promigence run --snapshot promigence/base --count 2 --cap 1 --json -- python3 -c 'print(6*7)'";
8
+ export const WAIT_TIMEOUT_DEFAULT = "100s";
9
+ export const REQUEST_TIMEOUT_DEFAULT = "30s";
10
+ export const LIVE_COMMANDS = [
11
+ "login", "signup", "logout", "auth login", "keys list", "keys create", "keys revoke",
12
+ "org list", "org create", "org switch", "org members", "org invite", "org invites", "org uninvite", "org resend", "org role", "org remove", "org leave", "org join", "org audit", "org rename", "org delete",
13
+ "project list", "project create", "project rename", "project delete", "project cap", "project move",
14
+ "me", "billing card", "doctor", "ping", "init", "examples", "templates", "templates show", "agent-guide", "mcp",
15
+ "snapshot create", "snapshot status", "snapshot list", "snapshot inspect", "snapshot verify",
16
+ "snapshot export",
17
+ "fork", "exec", "exec output", "exec ps", "exec kill", "files put", "files get", "files ls",
18
+ "run", "run quote", "run report", "run status", "run cancel", "run kill", "run replay",
19
+ "run open", "run captures",
20
+ "sandbox kill", "sandbox shell", "sandbox ssh", "sandbox network", "port expose", "port list", "port revoke",
21
+ "secrets set", "bench burst",
22
+ "status", "billing usage", "billing portal", "snapshot delete", "snapshot builds", "snapshot alias",
23
+ "run list", "sandbox list", "sandbox info", "sandbox pause", "sandbox resume", "sandbox extend",
24
+ "sandbox probe", "files rm", "secrets list", "secrets delete",
25
+ "image resolve",
26
+ "sandbox snapshot",
27
+ "files search", "files mkdir", "files mv", "files cp", "files chmod", "exec stdin",
28
+ "sandbox autopause",
29
+ "agent start",
30
+ "referrals", "referrals apply",
31
+ "credits", "credits claim",
32
+ "runners github", "runners gitlab",
33
+ "pricing",
34
+ "github connect", "github status", "github disconnect", "github untrack", "repos",
35
+ "gitlab connect", "gitlab status", "gitlab disconnect", "gitlab untrack",
36
+ "billing history",
37
+ ];
38
+ export const GUIDE_COMMON_ERRORS = [
39
+ "auth", "credit_unclaimed", "not_found", "not_verified", "cap_exceeded", "timeout", "nonzero_exit",
40
+ ];
41
+ export const TIER_NOTE = "a sandbox has its snapshot's size (base: small); another size: snapshot create --image REF --tier T";
42
+ export const BACKGROUND_NOTE = "background commands started with `exec --background` are ended; anything else you started keeps running when the sandbox resumes";
43
+ const json = { name: "--json", summary: "force the JSON envelope" };
44
+ const signInFlag = { name: "--no-browser", summary: "sign in headless (or PROMIGENCE_ID_TOKEN)" };
45
+ const signInYes = { name: "--yes", summary: "sign in to PROMIGENCE_API_URL without the prompt" };
46
+ const orgFlag = { name: "--org", arg: "ORG", summary: "another org than this key's" };
47
+ const wait = { name: "--wait", summary: `bounded by --wait-timeout (default ${WAIT_TIMEOUT_DEFAULT})` };
48
+ const waitTimeout = { name: "--wait-timeout", arg: "DUR", summary: `bound (${WAIT_TIMEOUT_DEFAULT}); expiry = exit 11, nothing killed` };
49
+ const snapshot = { name: "--snapshot", arg: "H", summary: "snap_<id> | alias[@commit7] | ./env.promigence", required: true };
50
+ const count = { name: "--count", arg: "N", summary: "number of sandboxes", required: true };
51
+ const cap = { name: "--cap", arg: "USD", summary: "spend cap; refused above it (exit 5)", required: true };
52
+ const idem = { name: "--idempotency-key", arg: "K", summary: "a key you pick (24 h)" };
53
+ const verbose = { name: "--verbose", summary: "text: fork timings and totals too" };
54
+ const stream = { name: "--stream", summary: "live output (auto on a TTY)" };
55
+ const noStream = { name: "--no-stream", summary: "one envelope even on a TTY" };
56
+ const fresh = { name: "--fresh", summary: "skip idempotent replay" };
57
+ const gradeFlags = [
58
+ { name: "--grade", arg: "CMD", summary: "score the work in a clean env" },
59
+ { name: "--holdout", arg: "CMD", summary: "second test set; gap reported" },
60
+ { name: "--grade-score", arg: "M", summary: "exit|last_line_json|tap (exit)" },
61
+ { name: "--grade-timeout", arg: "DUR", summary: "per grader (10m)" },
62
+ { name: "--grade-output", summary: "keep last 16 KiB" },
63
+ ];
64
+ const optimizeFor = { name: "--optimize-for", arg: "start|compute", summary: "default start" };
65
+ const forkFlags = [
66
+ snapshot,
67
+ count,
68
+ { name: "--timeout", arg: "DUR", summary: "sandbox timeout (15m)" },
69
+ cap,
70
+ { name: "--allow-partial", summary: "start what the cap allows; the rest not_started" },
71
+ { name: "--secret", arg: "NAME", summary: "stored secret as an env var", repeatable: true },
72
+ { name: "--verify-each", summary: "verify forks before hand-over" },
73
+ { name: "--network", arg: "none|egress", summary: "default egress" },
74
+ { name: "--allow", arg: "HOST", summary: "outbound allow-list", repeatable: true },
75
+ optimizeFor,
76
+ { name: "--label", arg: "K=V", summary: "run label", repeatable: true },
77
+ waitTimeout,
78
+ ];
79
+ export const COMMANDS = [
80
+ {
81
+ name: "login", group: "Setup", status: "released", signIn: "always",
82
+ helpRow: { key: "login", summary: "sign up or in · sign out · store a key" },
83
+ summary: "sign up or sign in (email; GitHub/Google where enabled); stores a new API key",
84
+ flags: [
85
+ { name: "--provider", arg: "NAME", summary: "a sign-in method the API offers (email; github|google where enabled): skip the chooser" },
86
+ { name: "--no-browser", summary: "headless: print the URL, paste the code back" },
87
+ { name: "--key-name", arg: "N", summary: "label for the key this login mints (default cli@<host>)" },
88
+ { name: "--api-url", arg: "URL", summary: "sign in to this API instead of the default (no prompt)" },
89
+ { name: "--yes", summary: "accept a non-default API from PROMIGENCE_API_URL without the prompt" },
90
+ { name: "--ref", arg: "CODE", summary: "a referral code (new accounts)" },
91
+ json,
92
+ ],
93
+ next: ["promigence me"],
94
+ },
95
+ {
96
+ name: "signup", group: "Setup", status: "released", signIn: "always",
97
+ helpRow: { key: "login", summary: "" },
98
+ summary: "create an account",
99
+ flags: [
100
+ { name: "--invite", arg: "CODE", summary: "the code (prefer the prompt; argv is visible)" },
101
+ { name: "--provider", arg: "NAME", summary: "a sign-in method the API offers (email; github|google where enabled): skip the chooser" },
102
+ { name: "--no-browser", summary: "headless: print the URL, paste the code back" },
103
+ { name: "--key-name", arg: "N", summary: "label for the key this sign-in mints (default cli@<host>)" },
104
+ { name: "--api-url", arg: "URL", summary: "sign up on this API instead of the default (no prompt)" },
105
+ { name: "--yes", summary: "accept a non-default API from PROMIGENCE_API_URL without the prompt" },
106
+ { name: "--ref", arg: "CODE", summary: "a referral code (new accounts)" },
107
+ json,
108
+ ],
109
+ next: ["promigence me"],
110
+ },
111
+ {
112
+ name: "logout", group: "Setup", status: "released",
113
+ helpRow: { key: "login", summary: "" },
114
+ summary: "forget this CLI's key (revoked when `promigence login` minted it)",
115
+ flags: [{ name: "--keep-key", summary: "only forget it locally; the key keeps working" }, json],
116
+ next: ["promigence login"],
117
+ },
118
+ {
119
+ name: "auth login", group: "Setup", status: "released",
120
+ helpRow: { key: "login", summary: "" },
121
+ summary: "store an API key (--from-env VAR | stdin; never on argv)",
122
+ flags: [
123
+ { name: "--from-env", arg: "VAR", summary: "read the key from an environment variable" },
124
+ { name: "--api-key", arg: "K", summary: "accepted but warns: key on argv" },
125
+ ],
126
+ next: ["promigence me"],
127
+ },
128
+ {
129
+ name: "keys list", group: "Setup", status: "released", signIn: "with --project",
130
+ helpRow: { key: "keys", summary: "org API keys; secret shown once" },
131
+ summary: "your org's API keys",
132
+ flags: [
133
+ { name: "--all", summary: "include revoked keys" },
134
+ { name: "--project", arg: "P", summary: "another project's" },
135
+ { name: "--org", arg: "ORG", summary: "with --project: in this org" },
136
+ signInFlag, signInYes, json,
137
+ ],
138
+ next: ["promigence keys create --name NAME"],
139
+ },
140
+ {
141
+ name: "keys create", group: "Setup", status: "released", signIn: "with --project or --service, or --org naming another org",
142
+ helpRow: { key: "keys", summary: "" },
143
+ summary: "mint a key (secret printed once)",
144
+ flags: [
145
+ { name: "--name", arg: "N", summary: "label, e.g. ci-prod" },
146
+ { name: "--org", arg: "ORG", summary: "in this org (default: this key's)" },
147
+ { name: "--project", arg: "P", summary: "in this project" },
148
+ { name: "--service", summary: "owners: outlives your membership (CI)" },
149
+ { name: "--access", arg: "A", summary: "full (default) | run: sandboxes, runs, builds; no secret/key/billing changes | read: reads all, files and output too; changes nothing" },
150
+ { name: "--expires", arg: "WHEN", summary: "1h, 30d, 1y, a date, or a UTC time (2027-01-31T12:00:00Z); any future time; default never" },
151
+ signInFlag, signInYes, json,
152
+ ],
153
+ next: ["promigence keys list"],
154
+ },
155
+ {
156
+ name: "keys revoke", group: "Setup", status: "released", signIn: "with --project",
157
+ helpRow: { key: "keys", summary: "" },
158
+ summary: "revoke a key", args: "KEY_ID",
159
+ flags: [
160
+ { name: "--project", arg: "P", summary: "a key in another project" },
161
+ { name: "--org", arg: "ORG", summary: "with --project: in this org" },
162
+ signInFlag, signInYes, json,
163
+ ],
164
+ next: ["promigence keys list"],
165
+ },
166
+ {
167
+ name: "org list", group: "Setup", status: "released", signIn: "with --all",
168
+ helpRow: { key: "org", summary: "orgs, members" },
169
+ summary: "your organizations (* = default)",
170
+ flags: [{ name: "--all", summary: "every one" }, signInFlag, signInYes, json],
171
+ next: ["promigence org switch <org>"],
172
+ },
173
+ {
174
+ name: "org create", group: "Setup", status: "released", signIn: "always",
175
+ helpRow: { key: "org", summary: "" },
176
+ summary: "create one you own (no trial credit)", args: "NAME", flags: [signInFlag, signInYes, json],
177
+ next: ["promigence org switch <org>"],
178
+ },
179
+ {
180
+ name: "org switch", group: "Setup", status: "released", signIn: "always",
181
+ helpRow: { key: "org", summary: "" },
182
+ summary: "sign in, make ORG the default, store a key for it", args: "ORG",
183
+ flags: [
184
+ { name: "--project", arg: "P", summary: "the new key's project" },
185
+ { name: "--keep-old-key", summary: "keep the replaced key working" },
186
+ signInFlag,
187
+ signInYes, json,
188
+ ],
189
+ next: ["promigence me"],
190
+ },
191
+ {
192
+ name: "org members", group: "Setup", status: "released", signIn: "with --org naming another org",
193
+ helpRow: { key: "org", summary: "" },
194
+ summary: "members and roles", flags: [orgFlag, signInFlag, signInYes, json],
195
+ next: ["promigence org invite <email>"],
196
+ },
197
+ {
198
+ name: "org invite", group: "Setup", status: "released", signIn: "always",
199
+ helpRow: { key: "org", summary: "" },
200
+ summary: "invite an email (owners)", args: "EMAIL",
201
+ flags: [{ name: "--role", arg: "R", summary: "owner|member (member)" }, orgFlag, signInFlag, signInYes, json],
202
+ next: ["promigence org invites"],
203
+ },
204
+ {
205
+ name: "org invites", group: "Setup", status: "released", signIn: "with --mine, or --org naming another org",
206
+ helpRow: { key: "org", summary: "" },
207
+ summary: "pending invites (14-day expiry)",
208
+ flags: [{ name: "--mine", summary: "sent to you" }, orgFlag, signInFlag, signInYes, json],
209
+ next: ["promigence org uninvite <invite_id>"],
210
+ },
211
+ {
212
+ name: "org uninvite", group: "Setup", status: "released", signIn: "always",
213
+ helpRow: { key: "org", summary: "" },
214
+ summary: "revoke an invite (owners)", args: "INVITE_ID", flags: [orgFlag, signInFlag, signInYes, json],
215
+ next: ["promigence org invites"],
216
+ },
217
+ {
218
+ name: "org resend", group: "Setup", status: "released", signIn: "always",
219
+ helpRow: { key: "org", summary: "" },
220
+ summary: "send an invite again (owners)", args: "INVITE_ID", flags: [orgFlag, signInFlag, signInYes, json],
221
+ next: ["promigence org invites"],
222
+ },
223
+ {
224
+ name: "org role", group: "Setup", status: "released", signIn: "always",
225
+ helpRow: { key: "org", summary: "" },
226
+ summary: "set owner|member (owners)", args: "USER_ID ROLE", flags: [orgFlag, signInFlag, signInYes, json],
227
+ next: ["promigence org members"],
228
+ },
229
+ {
230
+ name: "org remove", group: "Setup", status: "released", signIn: "always",
231
+ helpRow: { key: "org", summary: "" },
232
+ summary: "remove a member; their keys die (owners)", args: "USER_ID", flags: [orgFlag, signInFlag, signInYes, json],
233
+ next: ["promigence org members"],
234
+ },
235
+ {
236
+ name: "org leave", group: "Setup", status: "released", signIn: "for an org other than this key's",
237
+ helpRow: { key: "org", summary: "" },
238
+ summary: "leave an organization", args: "[ORG]", flags: [signInFlag, signInYes, json],
239
+ next: ["promigence org list"],
240
+ },
241
+ {
242
+ name: "org join", group: "Setup", status: "released", signIn: "always",
243
+ helpRow: { key: "org", summary: "" },
244
+ summary: "accept an invite sent to you", args: "INVITE_ID",
245
+ flags: [{ name: "--decline", summary: "decline it instead" }, signInFlag, signInYes, json],
246
+ next: ["promigence org switch <org>"],
247
+ },
248
+ {
249
+ name: "org audit", group: "Setup", status: "released", signIn: "with --sign-in (never on its own)",
250
+ helpRow: { key: "org", summary: "" },
251
+ summary: "who did what (owners; needs --sign-in)",
252
+ flags: [{ name: "--before", arg: "SEQ", summary: "older entries" }, { name: "--limit", arg: "N", summary: "how many (100)" }, orgFlag,
253
+ { name: "--sign-in", summary: "sign in in your browser: the log is read as you, never with a key" }, signInFlag, signInYes, json],
254
+ next: ["promigence org members"],
255
+ },
256
+ {
257
+ name: "org rename", group: "Setup", status: "released", signIn: "always",
258
+ helpRow: { key: "org", summary: "" },
259
+ summary: "rename it (owners)", args: "ORG NAME", flags: [signInFlag, signInYes, json],
260
+ next: ["promigence org list --all"],
261
+ },
262
+ {
263
+ name: "org delete", group: "Setup", status: "released", signIn: "always",
264
+ helpRow: { key: "org", summary: "" },
265
+ summary: "delete it; keys die (owners)", args: "ORG",
266
+ flags: [{ name: "--confirm", arg: "NAME", summary: "its name (required)" }, signInFlag, signInYes, json],
267
+ next: ["promigence org list --all"],
268
+ },
269
+ {
270
+ name: "project list", group: "Setup", status: "released", signIn: "with --org naming another org",
271
+ helpRow: { key: "project", summary: "projects: keys belong to one" },
272
+ summary: "projects and their spend this month", flags: [{ name: "--month", arg: "YYYY-MM", summary: "another month" }, orgFlag, signInFlag, signInYes, json],
273
+ next: ["promigence project create <name>"],
274
+ },
275
+ {
276
+ name: "project create", group: "Setup", status: "released", signIn: "with --org naming another org",
277
+ helpRow: { key: "project", summary: "" },
278
+ summary: "create a project", args: "NAME", flags: [orgFlag, signInFlag, signInYes, json],
279
+ next: ["promigence keys create --project <name>"],
280
+ },
281
+ {
282
+ name: "project rename", group: "Setup", status: "released", signIn: "with --org naming another org",
283
+ helpRow: { key: "project", summary: "" },
284
+ summary: "rename (not `default`)", args: "PROJECT NAME", flags: [orgFlag, signInFlag, signInYes, json],
285
+ next: ["promigence project list"],
286
+ },
287
+ {
288
+ name: "project delete", group: "Setup", status: "released", signIn: "always",
289
+ helpRow: { key: "project", summary: "" },
290
+ summary: "delete an empty one (owners)", args: "PROJECT", flags: [orgFlag, signInFlag, signInYes, json],
291
+ next: ["promigence project list"],
292
+ },
293
+ {
294
+ name: "project cap", group: "Setup", status: "released", signIn: "always",
295
+ helpRow: { key: "project", summary: "" },
296
+ summary: "monthly USD cap, or off (owners)", args: "PROJECT USD|off", flags: [orgFlag, signInFlag, signInYes, json],
297
+ next: ["promigence project list"],
298
+ },
299
+ {
300
+ name: "project move", group: "Setup", status: "released", signIn: "always",
301
+ helpRow: { key: "project", summary: "" },
302
+ summary: "to another org; keys die (owners)", args: "PROJECT",
303
+ flags: [{ name: "--to", arg: "ORG", summary: "yours, with a card" }, orgFlag, signInFlag, signInYes, json],
304
+ next: ["promigence keys create --org <to> --project <name>"],
305
+ },
306
+ {
307
+ name: "me", group: "Setup", status: "released",
308
+ helpRow: { key: "me", summary: "org limits, spend · live prices" },
309
+ summary: "plan, concurrency in use, trial credit, spend, limits",
310
+ flags: [json],
311
+ next: ["promigence billing usage"],
312
+ },
313
+ {
314
+ name: "billing card", group: "Setup", status: "released",
315
+ helpRow: { key: "billing", summary: "add a card · spend · the portal" },
316
+ summary: "add a card: prints a checkout URL (trial credits kept)",
317
+ flags: [json],
318
+ next: ["promigence me"],
319
+ },
320
+ {
321
+ name: "doctor", group: "Setup", status: "released",
322
+ helpRow: { key: "doctor", summary: "check client, auth, API · services, concurrency" },
323
+ summary: "check client, auth and API",
324
+ flags: [json],
325
+ next: ["promigence examples"],
326
+ },
327
+ {
328
+ name: "ping", group: "Setup", status: "released",
329
+ helpRow: { key: "doctor", summary: "" },
330
+ summary: "your distance to our servers",
331
+ flags: [json],
332
+ next: ["promigence doctor"],
333
+ },
334
+ {
335
+ name: "init", group: "Setup", status: "released",
336
+ summary: "write promigence.toml",
337
+ flags: [
338
+ { name: "--repo", arg: "U", summary: "git URL (default: the `origin` remote)" },
339
+ { name: "--commit", arg: "SHA", summary: "commit to pin (default: HEAD)" },
340
+ { name: "--setup", arg: "CMD", summary: "override the inferred setup step", repeatable: true },
341
+ { name: "--verify", arg: "CMD", summary: "override the inferred verify command" },
342
+ { name: "--tier", arg: "xsmall|small|medium|large-16|large|xlarge|2xlarge", summary: "default medium" },
343
+ { name: "--force", summary: "overwrite an existing promigence.toml" },
344
+ { name: "--print", summary: "write nothing; print what it would write" },
345
+ json,
346
+ ],
347
+ next: ["promigence snapshot create --image IMAGE --setup 'git clone …' --verify CMD --wait"],
348
+ },
349
+ {
350
+ name: "examples", group: "Setup", status: "released",
351
+ helpRow: { key: "examples", summary: "no-build forks (no auth)" },
352
+ summary: "what forks with no build (no auth)",
353
+ flags: [
354
+ { name: "--limit", arg: "N", summary: "how many to list (default 20)" },
355
+ { name: "--bundle", summary: "write each public snapshot as a .promigence file (none yet)" },
356
+ json,
357
+ ],
358
+ next: [GOLDEN_PATH],
359
+ },
360
+ {
361
+ name: "templates", group: "Setup", status: "released",
362
+ helpRow: { key: "examples", summary: "" },
363
+ summary: "the templates: ready-made starting points to fork (no auth)",
364
+ flags: [json],
365
+ next: ["promigence templates show <name>"],
366
+ },
367
+ {
368
+ name: "templates show", group: "Setup", status: "released",
369
+ helpRow: { key: "examples", summary: "" },
370
+ summary: "one template: everything it has installed, and its versions (no auth)", args: "NAME",
371
+ flags: [{ name: "--packages", summary: "list every installed package, not only the count" }, json],
372
+ next: ["promigence fork --snapshot <name> --count 1 --cap 1"],
373
+ },
374
+ {
375
+ name: "agent-guide", group: "Setup", status: "released",
376
+ helpRow: { key: "agent-guide", summary: "~1.4k-token guide for agents · MCP server on stdio" },
377
+ summary: "~1.4k-token guide for agents (--section commands|errors|cost)",
378
+ flags: [
379
+ { name: "--section", arg: "commands|errors|cost", summary: "one section instead of the core" },
380
+ { name: "--code", arg: "C", summary: "the entry for one error code" },
381
+ { name: "--format", arg: "md|json", summary: "default md" },
382
+ { name: "--install", summary: "write the fence into AGENTS.md/CLAUDE.md, .claude/skills, .cursor/rules" },
383
+ { name: "--create", summary: "with --install: create AGENTS.md if neither file exists" },
384
+ { name: "--check", summary: "report what --install would change; write nothing; exit 13 if it would" },
385
+ ],
386
+ next: [GOLDEN_PATH],
387
+ },
388
+ {
389
+ name: "mcp", group: "Setup", status: "released",
390
+ helpRow: { key: "agent-guide", summary: "" },
391
+ summary: "MCP server on stdio: `claude mcp add promigence -- promigence mcp` (Windows: `-- cmd /c promigence mcp`)",
392
+ flags: [
393
+ { name: "--read-only", summary: "offer only the tools that read (not a fence around data: they still return what a sandbox holds)" },
394
+ { name: "--root", arg: "PATH", summary: "keep the file tools to this directory of the sandbox (sandbox_exec is not fenced)" },
395
+ { name: "--allow-tools", arg: "A,B", summary: "offer only these tools (comma-separated names)" },
396
+ { name: "--cap", arg: "USD", summary: "spend cap for every sandbox and batch it starts (else PROMIGENCE_SPEND_CAP, else the org default)" },
397
+ { name: "--keep", summary: "leave the sandboxes this session created running at shutdown" },
398
+ ],
399
+ noGuidePointer: true,
400
+ next: [],
401
+ },
402
+ {
403
+ name: "snapshot create", group: "Snapshots", status: "released",
404
+ summary: "build + verify a snapshot (--image | --dockerfile | --devcontainer)",
405
+ flags: [
406
+ { name: "--repo", arg: "R", summary: "owner/name (connected GitHub) or a git URL" },
407
+ { name: "--ref", arg: "R", summary: "with owner/name: branch|tag|sha" },
408
+ { name: "--commit", arg: "SHA", summary: "40-char sha, never a branch" },
409
+ { name: "--also-repo", arg: "URL@SHA[:PATH]", summary: "another pinned repo, before setup", repeatable: true, hidden: true },
410
+ { name: "--image", arg: "REF", summary: "a tag (we pin it) or REF@sha256:D" },
411
+ { name: "--dockerfile", arg: "PATH", summary: "a Dockerfile we build" },
412
+ { name: "--context", arg: "DIR|URL@SHA", summary: "default: the Dockerfile's dir" },
413
+ { name: "--devcontainer", arg: "PATH", summary: "a devcontainer.json (default: the one here)" },
414
+ { name: "--no-devcontainer", summary: "ignore the repo's devcontainer.json" },
415
+ { name: "--devcontainer-local-env", summary: "let its ${localEnv:VAR} read your environment (baked in)", hidden: true },
416
+ { name: "--dir", arg: "PATH", summary: "upload the files git tracks there" },
417
+ { name: "--from", arg: "SNAP", summary: "owner/name|--dir: start from (base)" },
418
+ { name: "--track", summary: "owner/name: rebuild on every push" },
419
+ { name: "--include-untracked", summary: "--dir: also untracked files git does not ignore", hidden: true },
420
+ { name: "--allow-secrets", summary: "--dir/--dockerfile: also send files that look like credentials (a best-effort check by file name); each is named, and asked about on a terminal unless --yes", hidden: true },
421
+ { name: "--list-files", summary: "--dir/--dockerfile: print every file that would be uploaded, and send nothing", hidden: true },
422
+ { name: "--yes", summary: "--allow-secrets/--devcontainer-local-env: send without the question on a terminal", hidden: true },
423
+ { name: "--keep-sandbox", summary: "--dir: keep the sandbox after (debugging)", hidden: true },
424
+ { name: "--timeout", arg: "DUR", summary: "--dir: the sandbox's life (30m)", hidden: true },
425
+ { name: "--setup", arg: "CMD", summary: "setup step (ordered)", repeatable: true },
426
+ { name: "--verify", arg: "CMD", summary: "a command that must exit 0 or nothing is stored (it checks the command, not any one file)" },
427
+ { name: "--start", arg: "CMD", summary: "a command started at build time and already running in every sandbox made from the snapshot" },
428
+ { name: "--tier", arg: "xsmall|small|medium|large-16|large|xlarge|2xlarge", summary: "build and fork size" },
429
+ { name: "--alias", arg: "N", summary: "unique per org" },
430
+ ...gradeFlags.map((f) => (f.name === "--grade" ? f : { ...f, hidden: true })),
431
+ { ...fresh, summary: "no 10-min replay (identical inputs still reuse their snapshot)" },
432
+ wait, waitTimeout, idem, json,
433
+ ],
434
+ autoIdempotencyKey: true,
435
+ next: ["promigence snapshot status <build_id> --wait"],
436
+ },
437
+ {
438
+ name: "snapshot status", group: "Snapshots", status: "released",
439
+ helpRow: { key: "snapshot", summary: "poll (--wait) · re-verify · .promigence export" },
440
+ summary: "poll a build by build_id", args: "<build_id>",
441
+ flags: [wait, waitTimeout, { name: "--log", arg: "N", summary: "print the last N lines of the build log" }, json],
442
+ next: ["promigence run quote --snapshot <snapshot_id> --count N"],
443
+ },
444
+ {
445
+ name: "snapshot list", group: "Snapshots", status: "released",
446
+ helpRow: { key: "snapshot", summary: "" }, summary: "list snapshots in your org", flags: [json],
447
+ next: ["promigence snapshot inspect <hash>"],
448
+ },
449
+ {
450
+ name: "snapshot inspect", group: "Snapshots", status: "released",
451
+ helpRow: { key: "snapshot", summary: "" }, summary: "show a snapshot's record", args: "<hash>", flags: [json],
452
+ next: ["promigence run quote --snapshot <hash> --count N"],
453
+ },
454
+ {
455
+ name: "snapshot verify", group: "Snapshots", status: "released",
456
+ helpRow: { key: "snapshot", summary: "" }, summary: "re-verify a snapshot in a fork", args: "<hash>", flags: [json],
457
+ next: ["promigence snapshot inspect <hash>"],
458
+ },
459
+ {
460
+ name: "snapshot export", group: "Snapshots", status: "released",
461
+ helpRow: { key: "snapshot", summary: "" },
462
+ summary: "write a portable .promigence bundle (commit it, cite it, mail it)", args: "<hash>",
463
+ flags: [
464
+ { name: "--out", arg: "FILE", summary: "default <alias>.promigence; `-` or a pipe writes stdout" },
465
+ { name: "--citation", summary: "accepted; every export is the same form", hidden: true },
466
+ { name: "--full", summary: "accepted; every export is the same form", hidden: true },
467
+ { name: "--no-layers", summary: "old name of --citation", hidden: true },
468
+ { name: "--layers", summary: "old name of --full", hidden: true },
469
+ json,
470
+ ],
471
+ next: ["promigence run --snapshot ./<alias>.promigence --count N --cap USD -- CMD"],
472
+ },
473
+ {
474
+ name: "image resolve", group: "Snapshots", status: "released",
475
+ helpRow: { key: "image", summary: "pin a tag to a digest, server-side (no local docker)" },
476
+ summary: "resolve REF (python:3.12) to REF@sha256:… without a local container runtime",
477
+ args: "REF",
478
+ flags: [
479
+ { name: "--registry-secret", arg: "NAME", summary: "stored secret holding user:password for a private registry" },
480
+ json,
481
+ ],
482
+ next: ["promigence snapshot create --image <pinned> --tier small"],
483
+ },
484
+ {
485
+ name: "github connect", group: "Snapshots", status: "released",
486
+ helpRow: { key: "github", summary: "private repos for --repo" },
487
+ summary: "connect GitHub in the console; waits for it",
488
+ flags: [
489
+ { name: "--no-open", summary: "print the page, open nothing" },
490
+ { name: "--no-wait", summary: "print the page and return" },
491
+ { name: "--wait-timeout", arg: "DUR", summary: "for the connection (10m); expiry = exit 11" },
492
+ json,
493
+ ],
494
+ next: ["promigence repos"],
495
+ },
496
+ {
497
+ name: "github status", group: "Snapshots", status: "released",
498
+ helpRow: { key: "github", summary: "" },
499
+ summary: "connected accounts, pending installs, tracked branches",
500
+ flags: [json],
501
+ next: ["promigence repos"],
502
+ },
503
+ {
504
+ name: "github disconnect", group: "Snapshots", status: "released",
505
+ helpRow: { key: "github", summary: "" },
506
+ summary: "stop building from a GitHub account", args: "<installation_id|account>",
507
+ flags: [json],
508
+ next: ["promigence github status"],
509
+ },
510
+ {
511
+ name: "github untrack", group: "Snapshots", status: "released",
512
+ helpRow: { key: "github", summary: "" },
513
+ summary: "stop rebuilding on push (the snapshot stays)", args: "<track_id|alias>",
514
+ flags: [json],
515
+ next: ["promigence github status"],
516
+ },
517
+ {
518
+ name: "repos", group: "Snapshots", status: "released",
519
+ helpRow: { key: "github", summary: "" },
520
+ summary: "repos your GitHub/GitLab connection reads (for --repo)",
521
+ flags: [
522
+ { name: "--page", arg: "N", summary: "page (100 a page)" },
523
+ { name: "--provider", arg: "NAME", summary: "github (default) | gitlab" },
524
+ { name: "--connection", arg: "ID", summary: "gitlab: one connection (glc_…)" },
525
+ json,
526
+ ],
527
+ next: ["promigence snapshot create --repo <owner/name> --wait"],
528
+ },
529
+ {
530
+ name: "gitlab connect", group: "Snapshots", status: "released",
531
+ helpRow: { key: "github", summary: "", via: "gitlab" },
532
+ summary: "connect GitLab read-only in the console; waits",
533
+ flags: [
534
+ { name: "--no-open", summary: "print the page, open nothing" },
535
+ { name: "--no-wait", summary: "print the page and return" },
536
+ { name: "--wait-timeout", arg: "DUR", summary: "for the connection (10m); expiry = exit 11" },
537
+ json,
538
+ ],
539
+ next: ["promigence repos --provider gitlab"],
540
+ },
541
+ {
542
+ name: "gitlab status", group: "Snapshots", status: "released",
543
+ helpRow: { key: "github", summary: "", via: "gitlab" },
544
+ summary: "GitLab users, pending confirmations, tracks",
545
+ flags: [json],
546
+ next: ["promigence repos --provider gitlab"],
547
+ },
548
+ {
549
+ name: "gitlab disconnect", group: "Snapshots", status: "released",
550
+ helpRow: { key: "github", summary: "", via: "gitlab" },
551
+ summary: "revoke a GitLab connection", args: "<connection_id|username>",
552
+ flags: [json],
553
+ next: ["promigence gitlab status"],
554
+ },
555
+ {
556
+ name: "gitlab untrack", group: "Snapshots", status: "released",
557
+ helpRow: { key: "github", summary: "", via: "gitlab" },
558
+ summary: "stop rebuilding on commits (snapshot stays)", args: "<track_id|alias>",
559
+ flags: [json],
560
+ next: ["promigence gitlab status"],
561
+ },
562
+ {
563
+ name: "run", group: "Runs", status: "released",
564
+ summary: "fork N, exec CMD in each, print the validity report",
565
+ args: "-- CMD",
566
+ flags: [
567
+ { ...snapshot, required: false, summary: "id | alias[@commit7] | ./env.promigence (default promigence/base)" },
568
+ { name: "--image", arg: "REF", summary: "not with --snapshot: a tag or REF@sha256:D" },
569
+ ...forkFlags.slice(1).map((f) => (f.name === "--count"
570
+ ? { ...f, summary: "sandboxes; each has PROMIGENCE_INDEX (0..N-1) and PROMIGENCE_SEED" }
571
+ : f)),
572
+ ...gradeFlags.map((f) => (f.name === "--grade-score"
573
+ ? { ...f, summary: "exit|last_line_json|tap (exit); JSON: {\"score\":0.8,\"pass\":true}" }
574
+ : f)),
575
+ { name: "--fail-on", arg: "task|env|none", summary: "what exits non-zero (task)" },
576
+ { name: "--keep", summary: "keep sandboxes after CMD" },
577
+ stream, noStream,
578
+ { name: "--max-output", arg: "BYTES", summary: "output cap (64k)" },
579
+ idem,
580
+ verbose,
581
+ ],
582
+ next: ["promigence run report <run_id>", "promigence run status <run_id> --wait"],
583
+ },
584
+ {
585
+ name: "run quote", group: "Runs", status: "released",
586
+ summary: "price a run before launch",
587
+ flags: [
588
+ { ...snapshot, required: false, summary: "id | alias[@commit7] | ./env.promigence (default promigence/base)" }, count,
589
+ { name: "--timeout", arg: "DUR", summary: "sandbox timeout (15m)" },
590
+ { name: "--tier", arg: "T", summary: "must match the snapshot's assigned tier" },
591
+ { name: "--cap", arg: "USD", summary: "price it against this cap; spends nothing" },
592
+ json,
593
+ ],
594
+ next: ["promigence fork --snapshot H --count N --cap USD"],
595
+ },
596
+ {
597
+ name: "run report", group: "Runs", status: "released",
598
+ summary: "validity report: env vs task failures", args: "ID",
599
+ flags: [
600
+ json,
601
+ { name: "--sandboxes", arg: "all|failed", summary: "default: summary + non-completed sandboxes" },
602
+ { name: "--sandbox", arg: "SID", summary: "one sandbox" },
603
+ { name: "--repro", summary: "print the repro command for --sandbox" },
604
+ { name: "--limit", arg: "N", summary: "text rows before '… N more' (50; 0 = all). --json is never paged" },
605
+ ],
606
+ next: ["promigence run replay <run_id> --sandbox <sid>"],
607
+ },
608
+ {
609
+ name: "run status", group: "Runs", status: "released",
610
+ helpRow: { key: "run", summary: "list · poll (--wait) · stop queued · kill all · replay · open a failure" },
611
+ summary: "poll a run", args: "ID", flags: [wait, waitTimeout, json],
612
+ next: ["promigence run report <run_id>"],
613
+ },
614
+ {
615
+ name: "run cancel", group: "Runs", status: "released",
616
+ helpRow: { key: "run", summary: "" }, summary: "stop queued starts", args: "ID", flags: [json],
617
+ next: ["promigence run report <run_id>"],
618
+ },
619
+ {
620
+ name: "run kill", group: "Runs", status: "released",
621
+ helpRow: { key: "run", summary: "" }, summary: "kill every sandbox of a run", args: "ID", flags: [json],
622
+ next: ["promigence run report <run_id>"],
623
+ },
624
+ {
625
+ name: "run replay", group: "Runs", status: "released",
626
+ helpRow: { key: "run", summary: "" }, summary: "replay sandboxes (client-triggered, never automatic)", args: "ID",
627
+ flags: [
628
+ { name: "--sandbox", arg: "SID", summary: "only these sandboxes", repeatable: true },
629
+ {
630
+ name: "--env", arg: "K=V", repeatable: true,
631
+ summary: "a value for each variable the run's command was started with (runs keep names, never values)",
632
+ },
633
+ json,
634
+ ],
635
+ next: ["promigence run report <run_id>"],
636
+ },
637
+ {
638
+ name: "run open", group: "Runs", status: "released",
639
+ helpRow: { key: "run", summary: "" },
640
+ summary: "reopen a failed sandbox as a live one, at the moment it failed", args: "ID",
641
+ flags: [
642
+ { name: "--sandbox", arg: "SID", summary: "which failure (default: the only one)" },
643
+ { name: "--cap", arg: "USD", summary: "what the reopened sandbox may spend" },
644
+ { name: "--timeout", arg: "DUR", summary: "how long it may run (default: the run's)" },
645
+ json,
646
+ ],
647
+ next: ["promigence exec --sandbox <sid> -- ls -la"],
648
+ },
649
+ {
650
+ name: "run captures", group: "Runs", status: "released",
651
+ helpRow: { key: "run", summary: "" },
652
+ summary: "what of this run is preserved, and when it expires", args: "ID",
653
+ flags: [{ name: "--delete", arg: "SNAPSHOT_ID", summary: "destroy one now, before it expires" }, json],
654
+ next: ["promigence run open <run_id>"],
655
+ },
656
+ {
657
+ name: "fork", group: "Sandboxes", status: "released",
658
+ summary: "fork N sandboxes from a snapshot (--count --cap)",
659
+ note: TIER_NOTE,
660
+ flags: [{ ...snapshot, required: false, summary: "snap_<id> | alias[@commit7] | ./env.promigence (default promigence/base)" }, ...forkFlags.slice(1), { name: "--no-wait", summary: "return after POST /runs" }, fresh, stream, noStream, idem, verbose, json],
661
+ autoIdempotencyKey: true,
662
+ next: ["promigence exec --run <run_id> --all -- CMD"],
663
+ },
664
+ {
665
+ name: "exec", group: "Sandboxes", status: "released",
666
+ summary: "run CMD in a sandbox (--sandbox SID) or a run (--run ID --all)",
667
+ args: "-- CMD",
668
+ flags: [
669
+ { name: "--sandbox", arg: "SID", summary: "stdout is the child's own bytes (JSON only with --json or PROMIGENCE_OUTPUT=json); its exit code passes through; not found 127; Promigence's own failures 125" },
670
+ { name: "--run", arg: "ID", summary: "with --all: every ready sandbox; queued reported skipped unless --wait" },
671
+ { name: "--all", summary: "0 if every child exits 0, else 4" },
672
+ { name: "--no-passthrough", summary: "return 0–12 instead of the child's code" },
673
+ { name: "--background", summary: "with --sandbox: start it, print its exec_id, return now" },
674
+ { name: "--stdin", summary: "with --background: keep stdin open for `promigence exec stdin`" },
675
+ { name: "--secret", arg: "NAME", summary: "stored secret as an env var", repeatable: true },
676
+ stream, noStream,
677
+ { name: "--max-output", arg: "BYTES", summary: "per stream (64k; 32m, the most, into a pipe or file); past it the rest is cut, and text exits 12" },
678
+ { name: "--timeout", arg: "DUR", summary: "child timeout (default 15m)" },
679
+ wait, waitTimeout, idem,
680
+ ],
681
+ noGuidePointer: true,
682
+ passthroughExit: 125,
683
+ next: ["promigence exec output <x_id> --sandbox <sid> --follow", "promigence run report <run_id>"],
684
+ },
685
+ {
686
+ name: "exec output", group: "Sandboxes", status: "released",
687
+ helpRow: { key: "exec", summary: "output · background: list · write stdin · kill" },
688
+ summary: "an exec's output by id (background x_…: --follow)", args: "EXEC_ID",
689
+ flags: [
690
+ { name: "--range", arg: "A-B", summary: "byte range, 0-based and B inclusive; `A-` runs to the end" },
691
+ { name: "--sandbox", arg: "SID", summary: "resolve the exec inside this sandbox (required for x_…)" },
692
+ { name: "--run", arg: "ID", summary: "resolve the exec inside this run" },
693
+ { name: "--stderr", summary: "the child's stderr instead of its stdout" },
694
+ { name: "--follow", summary: "background exec: stream until it exits, pass its code through" },
695
+ json,
696
+ ],
697
+ noGuidePointer: true,
698
+ next: ["promigence run report <run_id>"],
699
+ },
700
+ {
701
+ name: "exec ps", group: "Sandboxes", status: "released",
702
+ helpRow: { key: "exec", summary: "" },
703
+ summary: "list a sandbox's background execs", flags: [{ name: "--sandbox", arg: "SID", summary: "the sandbox", required: true }, json],
704
+ next: ["promigence exec output <exec_id> --sandbox <sid> --follow"],
705
+ },
706
+ {
707
+ name: "exec stdin", group: "Sandboxes", status: "released",
708
+ helpRow: { key: "exec", summary: "" },
709
+ summary: "write to a background exec's stdin (needs `exec --background --stdin`)", args: "EXEC_ID",
710
+ flags: [
711
+ { name: "--sandbox", arg: "SID", summary: "the sandbox it runs in", required: true },
712
+ { name: "--data", arg: "TEXT", summary: "write this (default: read stdin to EOF)" },
713
+ { name: "--file", arg: "P", summary: "write this local file's bytes" },
714
+ { name: "--eof", summary: "close stdin after writing (implied when reading a pipe)" },
715
+ { name: "--no-eof", summary: "keep it open after piped input" },
716
+ json,
717
+ ],
718
+ next: ["promigence exec output <exec_id> --sandbox <sid> --follow"],
719
+ },
720
+ {
721
+ name: "exec kill", group: "Sandboxes", status: "released",
722
+ helpRow: { key: "exec", summary: "" },
723
+ summary: "signal a background exec (default SIGKILL)", args: "EXEC_ID",
724
+ flags: [
725
+ { name: "--sandbox", arg: "SID", summary: "the sandbox it runs in", required: true },
726
+ { name: "--signal", arg: "SIG", summary: "TERM | INT | HUP | KILL | a number" },
727
+ { name: "--forget", summary: "then drop its handle and buffered output" },
728
+ json,
729
+ ],
730
+ next: ["promigence exec ps --sandbox <sid>"],
731
+ },
732
+ {
733
+ name: "files put", group: "Sandboxes", status: "released",
734
+ helpRow: { key: "files", summary: "in / out, or inside" },
735
+ summary: "copy a local file into a sandbox (--recursive: a directory)", args: "SRC DST",
736
+ flags: [
737
+ { name: "--sandbox", arg: "SID", summary: "target sandbox", required: true },
738
+ { name: "--recursive", summary: "SRC is a local directory; DST is a directory in the sandbox" },
739
+ json,
740
+ ],
741
+ next: ["promigence exec --sandbox <sid> -- CMD"],
742
+ },
743
+ {
744
+ name: "files get", group: "Sandboxes", status: "released",
745
+ helpRow: { key: "files", summary: "" },
746
+ summary: "copy a file out of a sandbox (--recursive: a directory)", args: "SRC DST",
747
+ flags: [
748
+ { name: "--sandbox", arg: "SID", summary: "source sandbox", required: true },
749
+ { name: "--recursive", summary: "SRC is a directory in the sandbox; DST a local directory" },
750
+ { name: "--exclude", arg: "GLOB", summary: "with --recursive: leave these out, e.g. .git", repeatable: true },
751
+ { name: "--keep-links", summary: "with --recursive: keep links leaving DST" },
752
+ json,
753
+ ],
754
+ next: ["promigence files ls <dir> --sandbox <sid>"],
755
+ },
756
+ {
757
+ name: "files ls", group: "Sandboxes", status: "released",
758
+ helpRow: { key: "files", summary: "" },
759
+ summary: "list a directory in a sandbox (lstat per entry)", args: "[PATH]",
760
+ flags: [
761
+ { name: "--sandbox", arg: "SID", summary: "the sandbox", required: true },
762
+ { name: "--recursive", summary: "walk below PATH, not one level" },
763
+ { name: "--depth", arg: "N", summary: "with --recursive: how many levels" },
764
+ { name: "--limit", arg: "N", summary: "stop after N entries (truncated is reported)" },
765
+ { name: "--no-hidden", summary: "drop dotfiles (they are listed by default)" },
766
+ json,
767
+ ],
768
+ next: ["promigence files get SRC DST --sandbox <sid>"],
769
+ },
770
+ {
771
+ name: "files search", group: "Sandboxes", status: "released",
772
+ helpRow: { key: "files", summary: "" },
773
+ summary: "find files by name glob and/or text inside them", args: "[PATH]",
774
+ flags: [
775
+ { name: "--sandbox", arg: "SID", summary: "the sandbox", required: true },
776
+ { name: "--name", arg: "GLOB", summary: "name glob, e.g. '*.py'" },
777
+ { name: "--contains", arg: "TEXT", summary: "literal text to find inside matching files" },
778
+ { name: "--ignore-case", summary: "case-insensitive --contains" },
779
+ { name: "--limit", arg: "N", summary: "stop after N hits (truncated is reported)" },
780
+ { name: "--no-hidden", summary: "skip dotfiles and dot-directories" },
781
+ json,
782
+ ],
783
+ next: ["promigence files get SRC DST --sandbox <sid>"],
784
+ },
785
+ {
786
+ name: "files mkdir", group: "Sandboxes", status: "released",
787
+ helpRow: { key: "files", summary: "" },
788
+ summary: "create a directory in a sandbox", args: "PATH",
789
+ flags: [
790
+ { name: "--sandbox", arg: "SID", summary: "the sandbox", required: true },
791
+ { name: "--parents", summary: "mkdir -p: intermediate directories, and no error if it exists" },
792
+ { name: "--mode", arg: "OCTAL", summary: "e.g. 755" },
793
+ json,
794
+ ],
795
+ next: ["promigence files ls <path> --sandbox <sid>"],
796
+ },
797
+ {
798
+ name: "files mv", group: "Sandboxes", status: "released",
799
+ helpRow: { key: "files", summary: "" },
800
+ summary: "move or rename a path inside a sandbox", args: "SRC DST",
801
+ flags: [
802
+ { name: "--sandbox", arg: "SID", summary: "the sandbox", required: true },
803
+ { name: "--overwrite", summary: "replace DST if it is already there" },
804
+ json,
805
+ ],
806
+ next: ["promigence files ls <path> --sandbox <sid>"],
807
+ },
808
+ {
809
+ name: "files cp", group: "Sandboxes", status: "released",
810
+ helpRow: { key: "files", summary: "" },
811
+ summary: "copy a path inside a sandbox (both ends are in the sandbox)", args: "SRC DST",
812
+ flags: [
813
+ { name: "--sandbox", arg: "SID", summary: "the sandbox", required: true },
814
+ { name: "--recursive", summary: "a directory and everything under it" },
815
+ { name: "--overwrite", summary: "replace DST if it is already there" },
816
+ json,
817
+ ],
818
+ next: ["promigence files ls <path> --sandbox <sid>"],
819
+ },
820
+ {
821
+ name: "files chmod", group: "Sandboxes", status: "released",
822
+ helpRow: { key: "files", summary: "" },
823
+ summary: "change mode and/or owner of a path in a sandbox", args: "PATH",
824
+ flags: [
825
+ { name: "--sandbox", arg: "SID", summary: "the sandbox", required: true },
826
+ { name: "--mode", arg: "OCTAL", summary: "e.g. 755" },
827
+ { name: "--uid", arg: "N", summary: "owner" },
828
+ { name: "--gid", arg: "N", summary: "group" },
829
+ { name: "--recursive", summary: "the directory and everything under it" },
830
+ json,
831
+ ],
832
+ next: ["promigence files ls <path> --sandbox <sid>"],
833
+ },
834
+ {
835
+ name: "agent start", group: "Sandboxes", status: "released",
836
+ ownExtraWords: true,
837
+ summary: "a coding agent in a new sandbox", args: "claude|codex|opencode",
838
+ flags: [
839
+ { name: "--key-secret", arg: "NAME[=VAR]", summary: "stored secret with the model key (default ANTHROPIC_API_KEY; codex: OPENAI_API_KEY)" },
840
+ { name: "--key-from-env", arg: "VAR", summary: "store the key from this variable first (never argv)" },
841
+ { name: "--snapshot", arg: "H", summary: "start from this snapshot (default promigence/base; the agent is installed if missing)" },
842
+ { name: "--image", arg: "REF@sha256:D", summary: "instead of --snapshot" },
843
+ { name: "--repo", arg: "URL", summary: "clone this public repository and start the agent in it" },
844
+ { name: "--prompt", arg: "TEXT", summary: "work on this task headless, in the background" },
845
+ { name: "--model", arg: "M", summary: "the agent's model (opencode: provider/model, e.g. xai/grok-4-0709)" },
846
+ { name: "--attach", summary: "open the agent's terminal now" },
847
+ { name: "--timeout", arg: "DUR", summary: "sandbox lifetime (default 1h)" },
848
+ { name: "--cap", arg: "USD", summary: "spend cap (else PROMIGENCE_SPEND_CAP, else the org default)" },
849
+ waitTimeout, json,
850
+ ],
851
+ next: ["promigence sandbox shell <sid> --secret ANTHROPIC_API_KEY -- claude", "promigence sandbox kill <sid>"],
852
+ },
853
+ {
854
+ name: "sandbox kill", group: "Sandboxes", status: "released",
855
+ helpRow: { key: "sandbox", summary: "list · kill · pause/resume · interactive terminal · what it may reach" },
856
+ summary: "kill one sandbox", args: "SID", flags: [json],
857
+ next: ["promigence run report <run_id>"],
858
+ },
859
+ {
860
+ name: "sandbox network", group: "Sandboxes", status: "released",
861
+ helpRow: { key: "sandbox", summary: "" },
862
+ summary: "what it may reach; with an allow-list, hosts it was refused", args: "SID",
863
+ flags: [
864
+ { name: "--cut", summary: "cut this sandbox's egress now; it cannot be turned back on" },
865
+ json,
866
+ ],
867
+ next: ["promigence run report <run_id>"],
868
+ },
869
+ {
870
+ name: "sandbox shell", group: "Sandboxes", status: "released",
871
+ helpRow: { key: "sandbox", summary: "" },
872
+ summary: "interactive terminal (raw TTY, follows window size)", args: "SID [-- CMD]",
873
+ flags: [
874
+ { name: "--cmd", arg: "CMD", summary: "run this instead of the login shell (sh -c)" },
875
+ { name: "--cwd", arg: "DIR", summary: "start in this directory" },
876
+ { name: "--env", arg: "K=V", summary: "environment variable", repeatable: true },
877
+ {
878
+ name: "--secret",
879
+ arg: "NAME[=VAR]",
880
+ summary: "stored secret in the terminal's environment; NAME=VAR binds it to VAR",
881
+ repeatable: true,
882
+ },
883
+ ],
884
+ noGuidePointer: true,
885
+ passthroughExit: 125,
886
+ next: ["promigence sandbox kill <sid>"],
887
+ },
888
+ {
889
+ name: "sandbox ssh", group: "Sandboxes", status: "released",
890
+ helpRow: { key: "sandbox", summary: "" },
891
+ summary: "log in over SSH (scp, sftp and -L work too)", args: "SID [-- CMD]",
892
+ flags: [
893
+ { name: "--config", summary: "print an ssh config block instead of logging in" },
894
+ { name: "--print", summary: "print the connection details instead of logging in" },
895
+ { name: "--key", arg: "PATH", summary: "use this key pair instead of making one" },
896
+ { name: "--ttl", arg: "S", summary: "how long the access lasts (default 3600, max 3600)" },
897
+ { name: "--idle", arg: "S", summary: "close a session after this long with no activity" },
898
+ { name: "--proxy", summary: "carry one connection on stdin/stdout (used by ssh itself)" },
899
+ { name: "--list", summary: "the keys that have access, then exit" },
900
+ { name: "--revoke", arg: "KEY_ID", summary: "end one key's access (and the access credential), then exit" },
901
+ json,
902
+ ],
903
+ noGuidePointer: true,
904
+ passthroughExit: 125,
905
+ next: ["promigence sandbox ssh <sid> --config"],
906
+ },
907
+ {
908
+ name: "port expose", group: "Sandboxes", status: "released", notOnHosted: "preview URLs",
909
+ helpRow: { key: "port", summary: "preview URLs" },
910
+ summary: "publish a port as a preview URL (anyone with it can reach it)", args: "PORT",
911
+ flags: [{ name: "--sandbox", arg: "SID", summary: "the sandbox", required: true }, json],
912
+ next: ["promigence port revoke PORT --sandbox <sid>"],
913
+ },
914
+ {
915
+ name: "port list", group: "Sandboxes", status: "released", notOnHosted: "preview URLs",
916
+ helpRow: { key: "port", summary: "" },
917
+ summary: "list a sandbox's preview URLs",
918
+ flags: [{ name: "--sandbox", arg: "SID", summary: "the sandbox", required: true }, json],
919
+ next: ["promigence port revoke PORT --sandbox <sid>"],
920
+ },
921
+ {
922
+ name: "port revoke", group: "Sandboxes", status: "released", notOnHosted: "preview URLs",
923
+ helpRow: { key: "port", summary: "" },
924
+ summary: "take a preview URL down (a token, or every URL for a port)", args: "TOKEN|PORT",
925
+ flags: [{ name: "--sandbox", arg: "SID", summary: "the sandbox", required: true }, json],
926
+ next: ["promigence port list --sandbox <sid>"],
927
+ },
928
+ {
929
+ name: "secrets set", group: "Sandboxes", status: "released",
930
+ ownExtraWords: true,
931
+ summary: "store secret NAME (env/file/stdin, never argv)", args: "NAME",
932
+ flags: [
933
+ { name: "--from-env", arg: "VAR", summary: "read the value from an environment variable" },
934
+ { name: "--from-file", arg: "P", summary: "read the value from a file" },
935
+ { name: "--host", arg: "HOST", summary: "bind it to a host: requests sent to the address this command prints are authenticated for you; the value stays out of the sandbox unless a run asks (see --no-raw)", repeatable: true },
936
+ { name: "--header", arg: "NAME", summary: "with --host: the request header the key is sent in (default: the usual key headers)", repeatable: true },
937
+ { name: "--no-raw", summary: "with --host: the value itself can never be given to a sandbox" },
938
+ { name: "--list", summary: "list stored secret names (no values, ever)" },
939
+ { name: "--delete", summary: "remove NAME" },
940
+ json,
941
+ ],
942
+ next: [
943
+ "promigence run --snapshot H --count N --cap USD --secret NAME -- CMD",
944
+ "promigence secrets set --list",
945
+ ],
946
+ },
947
+ {
948
+ name: "bench burst", group: "Ops", status: "released",
949
+ summary: "N sandboxes at once: hand-off percentiles + success rate",
950
+ flags: [
951
+ snapshot, count,
952
+ { name: "--timeout", arg: "D", summary: "per-sandbox timeout (default 120s, the public burst harness's)" },
953
+ { name: "--cap", arg: "USD", summary: "spend cap for the burst (required unless PROMIGENCE_SPEND_CAP is set)" },
954
+ { name: "--keep", summary: "leave the sandboxes running (default: killed once measured)" },
955
+ json,
956
+ ],
957
+ next: ["promigence run report <run_id>"],
958
+ },
959
+ {
960
+ name: "runners github", group: "Ops", status: "released",
961
+ helpRow: { key: "runners", summary: "GitHub Actions / GitLab CI jobs in sandboxes" },
962
+ summary: "GitHub Actions jobs in sandboxes: `runs-on: promigence` (until Ctrl-C)",
963
+ note: "token from the environment, GitHub API only: Administration (--org: Self-hosted runners) write + Actions read; classic: repo (+ admin:org)",
964
+ flags: [
965
+ { name: "--repo", arg: "OWNER/REPO", summary: "take this repository's jobs" },
966
+ { name: "--org", arg: "ORG", summary: "or every repository's of an organization" },
967
+ { name: "--runner-group", arg: "NAME", summary: "with --org: the runner group (Default)" },
968
+ { name: "--token-env", arg: "VAR", summary: "variable holding a GitHub token (GH_TOKEN, then GITHUB_TOKEN)" },
969
+ { name: "--labels", arg: "A,B", summary: "the runs-on labels (promigence); promigence-small|medium|large pick a size" },
970
+ { name: "--tier", arg: "small|medium|large", summary: "size of a job that names none (small)" },
971
+ { name: "--warm", arg: "N", summary: "keep N runners waiting: instant pickup, billed while idle" },
972
+ { name: "--max", arg: "M", summary: "sandboxes at once (10)" },
973
+ { name: "--cap", arg: "USD", summary: "most one job may cost (default: its timeout at its size)" },
974
+ { name: "--timeout", arg: "DUR", summary: "longest job (default: your plan's longest sandbox)" },
975
+ { name: "--idle-timeout", arg: "DUR", summary: "stop an on-demand runner no job took (90s)" },
976
+ { name: "--kill-on-exit", summary: "Ctrl-C cancels running jobs instead of waiting" },
977
+ { name: "--prepare", summary: "build the --tier runner snapshot, then exit" },
978
+ { name: "--yes", summary: "build a missing runner snapshot (billed per second, per size) without asking" },
979
+ { name: "--github-api", arg: "URL", summary: "GitHub Enterprise Server's API URL" },
980
+ { name: "--poll", arg: "DUR", summary: "how often GitHub is asked for queued jobs", hidden: true },
981
+ json,
982
+ ],
983
+ next: ["promigence billing usage"],
984
+ },
985
+ {
986
+ name: "runners gitlab", group: "Ops", status: "released",
987
+ helpRow: { key: "runners", summary: "" },
988
+ summary: "GitLab CI jobs in sandboxes, as a runner with your tags (until Ctrl-C)",
989
+ note: "its token (glrt-…) comes from the environment; its jobs can read it in their sandbox",
990
+ flags: [
991
+ { name: "--url", arg: "URL", summary: "your GitLab (https://gitlab.com)" },
992
+ { name: "--token-env", arg: "VAR", summary: "variable holding the runner authentication token (GITLAB_RUNNER_TOKEN)" },
993
+ { name: "--executor", arg: "docker|shell", summary: "jobs in containers (docker) or in the sandbox" },
994
+ { name: "--default-image", arg: "REF", summary: "docker: image of a job with no image: (ubuntu:24.04)" },
995
+ { name: "--pool", arg: "N", summary: "runners kept up, billed while idle (1)" },
996
+ { name: "--api-token-env", arg: "VAR", summary: "read_api token: start runners only for pending jobs" },
997
+ { name: "--max", arg: "M", summary: "sandboxes at once (10)" },
998
+ { name: "--tier", arg: "small|medium|large", summary: "runner size (small)" },
999
+ { name: "--cap", arg: "USD", summary: "most one job may cost (default: its timeout at its size)" },
1000
+ { name: "--timeout", arg: "DUR", summary: "longest job (default: your plan's longest sandbox)" },
1001
+ { name: "--idle-timeout", arg: "DUR", summary: "with --api-token-env: a runner no job took exits (90s)" },
1002
+ { name: "--kill-on-exit", summary: "Ctrl-C cancels running jobs instead of waiting" },
1003
+ { name: "--prepare", summary: "build the --tier runner snapshot, then exit" },
1004
+ { name: "--yes", summary: "build a missing runner snapshot (billed per second) without asking" },
1005
+ { name: "--poll", arg: "DUR", summary: "how often GitLab is asked for pending jobs", hidden: true },
1006
+ json,
1007
+ ],
1008
+ next: ["promigence billing usage"],
1009
+ },
1010
+ {
1011
+ name: "status", group: "Setup", status: "released",
1012
+ helpRow: { key: "doctor", summary: "" },
1013
+ summary: "services · your concurrency in use vs your limit",
1014
+ flags: [json],
1015
+ next: ["promigence me"],
1016
+ },
1017
+ {
1018
+ name: "billing usage", group: "Setup", status: "released",
1019
+ helpRow: { key: "billing", summary: "" },
1020
+ summary: "this month's spend: plan, usage by tier, credits, next invoice",
1021
+ flags: [
1022
+ { name: "--ledger", summary: "also print this key's project's metered rows" },
1023
+ { name: "--limit", arg: "N", summary: "with --ledger: how many rows (default 20)" },
1024
+ { name: "--cursor", arg: "C", summary: "with --ledger: the next_cursor of an earlier page" },
1025
+ json,
1026
+ ],
1027
+ next: ["promigence billing card"],
1028
+ },
1029
+ {
1030
+ name: "billing history", group: "Setup", status: "released",
1031
+ helpRow: { key: "billing", summary: "" },
1032
+ summary: "credits and payments, newest first (owners)",
1033
+ flags: [
1034
+ { name: "--kind", arg: "K", summary: "all | credits | payments (default all)" },
1035
+ { name: "--limit", arg: "N", summary: "how many (default 50, at most 100)" },
1036
+ { name: "--cursor", arg: "C", summary: "the next_cursor of an earlier page" },
1037
+ json,
1038
+ ],
1039
+ next: ["promigence billing usage"],
1040
+ },
1041
+ {
1042
+ name: "referrals", group: "Setup", status: "released",
1043
+ helpRow: { key: "referrals", summary: "your code · apply one" },
1044
+ summary: "your referral code, its offer, your referrals",
1045
+ flags: [json],
1046
+ next: ["promigence referrals apply <code>"],
1047
+ },
1048
+ {
1049
+ name: "referrals apply", group: "Setup", status: "released",
1050
+ helpRow: { key: "referrals", summary: "" },
1051
+ summary: "apply a code (owner, before the first paid invoice)", args: "<code>",
1052
+ flags: [json],
1053
+ next: ["promigence referrals"],
1054
+ },
1055
+ {
1056
+ name: "pricing", group: "Setup", status: "released",
1057
+ helpRow: { key: "me", summary: "" },
1058
+ summary: "your prices, live (list prices with no key)",
1059
+ flags: [{ name: "--new-account", summary: "the published list" }, json],
1060
+ next: ["promigence run quote --snapshot promigence/base --count 1"],
1061
+ },
1062
+ {
1063
+ name: "credits", group: "Setup", status: "released",
1064
+ helpRow: { key: "credits", summary: "free credit" },
1065
+ summary: "your free-credit offer: state, credit, expiry, campaign",
1066
+ flags: [json],
1067
+ next: ["promigence credits claim"],
1068
+ },
1069
+ {
1070
+ name: "credits claim", group: "Setup", status: "released",
1071
+ helpRow: { key: "credits", summary: "" },
1072
+ summary: "open the claim page (accept the terms in the browser); prints its URL",
1073
+ flags: [{ name: "--no-open", summary: "print the URL, open nothing" }, json],
1074
+ next: ["promigence credits"],
1075
+ },
1076
+ {
1077
+ name: "snapshot delete", group: "Snapshots", status: "released",
1078
+ helpRow: { key: "snapshot", summary: "" },
1079
+ summary: "remove one of your snapshots (refused while a fork of it is alive)", args: "<hash|alias>",
1080
+ flags: [json],
1081
+ next: ["promigence snapshot list"],
1082
+ },
1083
+ {
1084
+ name: "sandbox list", group: "Sandboxes", status: "released",
1085
+ helpRow: { key: "sandbox", summary: "" },
1086
+ summary: "your sandboxes, newest first",
1087
+ flags: [
1088
+ { name: "--limit", arg: "N", summary: "how many (default 50)" },
1089
+ { name: "--state", arg: "S", summary: `only these states, comma-separated: ${SANDBOX_STATES.join(", ")}`, repeatable: true },
1090
+ { name: "--label", arg: "K=V", summary: "only sandboxes whose run carries every label given", repeatable: true },
1091
+ { name: "--cursor", arg: "C", summary: "the next_cursor of an earlier page" },
1092
+ json,
1093
+ ],
1094
+ next: ["promigence sandbox kill <sid>"],
1095
+ },
1096
+ {
1097
+ name: "sandbox pause", group: "Sandboxes", status: "released",
1098
+ helpRow: { key: "sandbox", summary: "" },
1099
+ summary: "park a sandbox: it bills no seconds and does not count against your limit", args: "SID",
1100
+ note: BACKGROUND_NOTE,
1101
+ flags: [json],
1102
+ next: ["promigence sandbox resume <sid>"],
1103
+ },
1104
+ {
1105
+ name: "sandbox resume", group: "Sandboxes", status: "released",
1106
+ helpRow: { key: "sandbox", summary: "" },
1107
+ summary: "bring a paused sandbox back and restart its clock", args: "SID",
1108
+ flags: [
1109
+ { ...optimizeFor, summary: "default: as created" },
1110
+ { name: "--cap", arg: "USD", summary: "for a sandbox paused at its run's spend cap: the run's new, higher cap" },
1111
+ json,
1112
+ ],
1113
+ next: ["promigence exec --sandbox <sid> -- CMD"],
1114
+ },
1115
+ {
1116
+ name: "sandbox snapshot", group: "Sandboxes", status: "released",
1117
+ helpRow: { key: "sandbox", summary: "" },
1118
+ summary: "snapshot it without stopping it", args: "SID",
1119
+ note: BACKGROUND_NOTE,
1120
+ flags: [
1121
+ json,
1122
+ { name: "--alias", arg: "NAME", summary: "name it" },
1123
+ { name: "--move-alias", summary: "take the alias from the snapshot that has it" },
1124
+ { name: "--force", summary: "take it even though the sandbox was given a secret; the snapshot, and what starts from it, can then contain that value" },
1125
+ ],
1126
+ next: ["promigence run --snapshot <snap>"],
1127
+ },
1128
+ {
1129
+ name: "secrets list", group: "Sandboxes", status: "released",
1130
+ helpRow: { key: "secrets", summary: "list · delete (a value is never shown)" },
1131
+ summary: "stored secret names (never a value)",
1132
+ flags: [json],
1133
+ next: ["promigence secrets set NAME --from-env VAR"],
1134
+ },
1135
+ {
1136
+ name: "secrets delete", group: "Sandboxes", status: "released",
1137
+ helpRow: { key: "secrets", summary: "" },
1138
+ summary: "remove a stored secret", args: "NAME",
1139
+ flags: [json],
1140
+ next: ["promigence secrets list"],
1141
+ },
1142
+ {
1143
+ name: "billing portal", group: "Setup", status: "released",
1144
+ helpRow: { key: "billing", summary: "" },
1145
+ summary: "manage the card on file and past invoices: prints a portal URL",
1146
+ flags: [json],
1147
+ next: ["promigence billing usage"],
1148
+ },
1149
+ {
1150
+ name: "snapshot builds", group: "Snapshots", status: "released",
1151
+ helpRow: { key: "snapshot", summary: "" },
1152
+ summary: "your builds, newest first (a rejected build keeps its reason)",
1153
+ flags: [{ name: "--limit", arg: "N", summary: "how many (default 20)" }, { name: "--state", arg: "S", summary: "only this state, e.g. running | rejected" }, { name: "--cursor", arg: "C", summary: "the next_cursor of an earlier page" }, json],
1154
+ next: ["promigence snapshot status <build_id>"],
1155
+ },
1156
+ {
1157
+ name: "snapshot alias", group: "Snapshots", status: "released",
1158
+ helpRow: { key: "snapshot", summary: "" },
1159
+ summary: "name a snapshot, or clear its name (unique per org)", args: "<hash> [NAME]",
1160
+ flags: [{ name: "--clear", summary: "remove the alias instead" }, json],
1161
+ next: ["promigence snapshot list"],
1162
+ },
1163
+ {
1164
+ name: "run list", group: "Runs", status: "released",
1165
+ helpRow: { key: "run", summary: "" },
1166
+ summary: "your runs, newest first",
1167
+ flags: [{ name: "--limit", arg: "N", summary: "how many (default 20)" }, { name: "--state", arg: "S", summary: "only this state" }, { name: "--cursor", arg: "C", summary: "the next_cursor of an earlier page" }, json],
1168
+ next: ["promigence run report <run_id>"],
1169
+ },
1170
+ {
1171
+ name: "sandbox info", group: "Sandboxes", status: "released",
1172
+ helpRow: { key: "sandbox", summary: "" },
1173
+ summary: "one sandbox: state, tier, time left, cost so far, and its execs", args: "SID",
1174
+ flags: [json],
1175
+ next: ["promigence exec output <exec_id> --sandbox <sid>"],
1176
+ },
1177
+ {
1178
+ name: "sandbox extend", group: "Sandboxes", status: "released",
1179
+ helpRow: { key: "sandbox", summary: "" },
1180
+ summary: "give a live sandbox more time, within its run's quote and --cap (exit 5 past them)", args: "SID",
1181
+ flags: [{ name: "--timeout", arg: "DUR", summary: "the new total timeout", required: true }, json],
1182
+ next: ["promigence sandbox info <sid>"],
1183
+ },
1184
+ {
1185
+ name: "sandbox autopause", group: "Sandboxes", status: "released",
1186
+ helpRow: { key: "sandbox", summary: "" },
1187
+ summary: "pause it once it has sat idle, or read the rule it is under", args: "SID",
1188
+ flags: [
1189
+ { name: "--after", arg: "DUR", summary: "idle this long and it pauses itself (2s or more)" },
1190
+ { name: "--off", summary: "never pause it on idle; only its timeout ends it" },
1191
+ { name: "--mode", arg: "MODE", summary: "requests (default) | activity: also no CPU, no bytes, no open connection inside" },
1192
+ json,
1193
+ ],
1194
+ next: ["promigence sandbox info <sid>"],
1195
+ },
1196
+ {
1197
+ name: "sandbox probe", group: "Sandboxes", status: "released",
1198
+ helpRow: { key: "sandbox", summary: "" },
1199
+ summary: "is this still the verified environment? (its recorded paths, or --path)", args: "SID",
1200
+ flags: [{ name: "--path", arg: "P", summary: "check these paths instead; PATH=blake3:<hex> where the snapshot recorded none (base, --image)", repeatable: true }, json],
1201
+ next: ["promigence run report <run_id>"],
1202
+ },
1203
+ {
1204
+ name: "files rm", group: "Sandboxes", status: "released",
1205
+ helpRow: { key: "files", summary: "" },
1206
+ summary: "delete a path in a sandbox", args: "PATH",
1207
+ flags: [
1208
+ { name: "--sandbox", arg: "SID", summary: "the sandbox", required: true },
1209
+ { name: "--recursive", summary: "a directory and everything under it" },
1210
+ json,
1211
+ ],
1212
+ next: ["promigence files ls --sandbox <sid>"],
1213
+ },
1214
+ ];
1215
+ export function compactNames(names) {
1216
+ const heads = [];
1217
+ const subs = new Map();
1218
+ const bare = new Set();
1219
+ for (const n of names) {
1220
+ const [head, ...rest] = n.split(" ");
1221
+ if (!subs.has(head)) {
1222
+ subs.set(head, []);
1223
+ heads.push(head);
1224
+ }
1225
+ if (rest.length)
1226
+ subs.get(head).push(rest.join(" "));
1227
+ else
1228
+ bare.add(head);
1229
+ }
1230
+ return heads.map((h) => {
1231
+ const list = subs.get(h);
1232
+ if (!list.length)
1233
+ return h;
1234
+ return bare.has(h) ? `${h} +${list.join("/")}` : `${h} ${list.join("/")}`;
1235
+ }).join(" · ");
1236
+ }
1237
+ export const LISTED_COMMANDS = COMMANDS.filter((c) => c.status === "released");
1238
+ export const LIVE_SUMMARY = compactNames(LISTED_COMMANDS.map((c) => c.name));
1239
+ export const NOT_LIVE_SUMMARY = "";
1240
+ export const STATUS_LINE = `Status: v${VERSION} — all ${LISTED_COMMANDS.length} commands live; preview URLs (port expose) may be off in this beta.`;
1241
+ export const GROUPS = ["Setup", "Snapshots", "Runs", "Sandboxes", "Ops"];
1242
+ export const GLOBAL_FLAGS = [
1243
+ { name: "--json", summary: "one JSON document on stdout (default when stdout is not a TTY, except exec --sandbox: --json or PROMIGENCE_OUTPUT=json there)" },
1244
+ { name: "--text", summary: "human text: a result on stdout, an error on stderr" },
1245
+ { name: "--api-key", arg: "K", summary: "precedence: --api-key > PROMIGENCE_API_KEY > the stored sign-in" },
1246
+ { name: "--request-timeout", arg: "DUR", summary: `how long one API request may take (default ${REQUEST_TIMEOUT_DEFAULT}); PROMIGENCE_REQUEST_TIMEOUT` },
1247
+ { name: "--version", summary: "print the version" },
1248
+ { name: "--help", summary: "this help; `promigence <command> --help` for one command" },
1249
+ ];
1250
+ export function knownFlags(cmd) {
1251
+ const out = new Set();
1252
+ for (const f of GLOBAL_FLAGS)
1253
+ out.add(f.name);
1254
+ for (const f of cmd.flags)
1255
+ out.add(f.name);
1256
+ if (cmd.autoIdempotencyKey)
1257
+ out.add("--fresh");
1258
+ return out;
1259
+ }
1260
+ export function unknownFlags(cmd, typed) {
1261
+ const known = knownFlags(cmd);
1262
+ return [...typed].filter((f) => !known.has(f));
1263
+ }
1264
+ export function nearestFlag(typo, cmd) {
1265
+ const known = [...knownFlags(cmd)];
1266
+ const near = known.filter((k) => k.includes(typo) || typo.includes(k));
1267
+ if (near.length)
1268
+ return near.sort((a, b) => Math.abs(a.length - typo.length) - Math.abs(b.length - typo.length))[0];
1269
+ const scored = known.map((k) => [k, levenshtein(k, typo)]).filter(([, d]) => d <= 2).sort((a, b) => a[1] - b[1]);
1270
+ if (!scored.length || (scored.length > 1 && scored[0][1] === scored[1][1]))
1271
+ return undefined;
1272
+ return scored[0][0];
1273
+ }
1274
+ function levenshtein(a, b) {
1275
+ const d = Array.from({ length: b.length + 1 }, (_, i) => i);
1276
+ for (let i = 1; i <= a.length; i++) {
1277
+ let prev = d[0];
1278
+ d[0] = i;
1279
+ for (let j = 1; j <= b.length; j++) {
1280
+ const cur = d[j];
1281
+ d[j] = Math.min(d[j] + 1, d[j - 1] + 1, prev + (a[i - 1] === b[j - 1] ? 0 : 1));
1282
+ prev = cur;
1283
+ }
1284
+ }
1285
+ return d[b.length];
1286
+ }
1287
+ export const EXIT_CODES = [
1288
+ { code: 0, name: "ok", summary: "success" },
1289
+ { code: 1, name: "internal", summary: "Promigence's own failure (incl. not_released)" },
1290
+ { code: 2, name: "usage", summary: "bad arguments / unknown command" },
1291
+ { code: 3, name: "env_failed", summary: "the environment failed (ours, never the task's)" },
1292
+ { code: 4, name: "task_failed", summary: "the task/command failed" },
1293
+ { code: 5, name: "spend_cap", summary: "a spend cap or an account limit refused it; nothing started" },
1294
+ { code: 6, name: "not_verified", summary: "fork/run against a snapshot whose build is not verified" },
1295
+ { code: 7, name: "auth", summary: "missing/invalid key; fix names the issuance contact" },
1296
+ { code: 8, name: "not_found", summary: "snapshot / run / sandbox not found" },
1297
+ { code: 9, name: "unreachable", summary: "API unreachable" },
1298
+ { code: 10, name: "rate_limited", summary: "429; Retry-After honoured" },
1299
+ { code: 11, name: "timeout", summary: "a bounded wait expired; nothing is killed; next is id-based" },
1300
+ { code: 12, name: "partial", summary: "some sandboxes not_started | cap_killed | expired | stopped_by_support" },
1301
+ { code: 13, name: "changes_pending", summary: "agent-guide --check: the files would change; nothing was written" },
1302
+ { code: 125, name: "passthrough", summary: "exec --sandbox: Promigence's own failure (child code otherwise passes through; a command not found exits 127)" },
1303
+ { code: 130, name: "interrupted", summary: "SIGINT/SIGTERM/SIGHUP; sandboxes killed unless --keep" },
1304
+ ];
1305
+ export const EXIT_PRECEDENCE = [2, 5, 6, 7, 8, 9, 10, 11, 3, 12, 4, 1, 0];
1306
+ export const EXIT_PRECEDENCE_TEXT = "2 > 5 > 6 > 7/8/9/10 > 11 > 3 > 12 > 4 > 1 > 0";
1307
+ export const OUTPUT_MODE_PRECEDENCE = ["--json | --text", "PROMIGENCE_OUTPUT=json|text", "stdout TTY → text, else json"];
1308
+ export function findCommand(words) {
1309
+ for (let n = Math.min(2, words.length); n >= 1; n--) {
1310
+ const name = words.slice(0, n).join(" ");
1311
+ const c = COMMANDS.find((x) => x.name === name);
1312
+ if (c)
1313
+ return c;
1314
+ }
1315
+ return undefined;
1316
+ }
1317
+ function pad(s, w) {
1318
+ return s.length >= w ? s + " " : s + " ".repeat(w - s.length);
1319
+ }
1320
+ function groupRows(group) {
1321
+ const rows = [];
1322
+ const merged = new Map();
1323
+ for (const c of LISTED_COMMANDS) {
1324
+ if (c.group !== group)
1325
+ continue;
1326
+ if (!c.helpRow) {
1327
+ rows.push([c.name, c.summary]);
1328
+ continue;
1329
+ }
1330
+ const m = merged.get(c.helpRow.key);
1331
+ const via = c.helpRow.via;
1332
+ if (via && m && c.name.startsWith(via + " ")) {
1333
+ if (!m.vias.includes(via))
1334
+ m.vias.push(via);
1335
+ const sub = c.name.slice(via.length + 1);
1336
+ if (!m.subs.includes(sub)) {
1337
+ m.subs.push(sub);
1338
+ m.names.push(sub);
1339
+ }
1340
+ continue;
1341
+ }
1342
+ const bare = c.name === c.helpRow.key;
1343
+ const prefixed = c.name.startsWith(c.helpRow.key + " ");
1344
+ const sub = prefixed ? c.name.slice(c.helpRow.key.length + 1) : c.name;
1345
+ if (m) {
1346
+ if (bare)
1347
+ m.bare = true;
1348
+ else {
1349
+ m.names.push(sub);
1350
+ (prefixed ? m.subs : m.fulls).push(sub);
1351
+ m.prefixed &&= prefixed;
1352
+ }
1353
+ }
1354
+ else {
1355
+ merged.set(c.helpRow.key, {
1356
+ names: bare ? [] : [sub], subs: bare || !prefixed ? [] : [sub], fulls: bare || prefixed ? [] : [sub],
1357
+ summary: c.helpRow.summary, prefixed: bare || prefixed, bare, vias: [],
1358
+ });
1359
+ rows.push([c.helpRow.key, ""]);
1360
+ }
1361
+ }
1362
+ return rows.map(([name, summary]) => {
1363
+ const m = merged.get(name);
1364
+ if (!m || summary !== "")
1365
+ return [name, summary];
1366
+ if (m.bare && m.prefixed)
1367
+ return [m.names.length ? `${name} [${m.names.join("|")}]` : name, m.summary];
1368
+ const head = [name, ...m.vias].join("|");
1369
+ if (!m.bare && m.subs.length && m.fulls.length)
1370
+ return [`${head} ${m.subs.join("|")} · ${m.fulls.join("|")}`, m.summary];
1371
+ return [m.prefixed ? `${name} ${m.names.join("|")}` : [...(m.bare ? [name] : []), ...m.names].join("|"), m.summary];
1372
+ });
1373
+ }
1374
+ export function renderHelp() {
1375
+ const out = [GUIDE_POINTER, DESCRIPTION, ""];
1376
+ out.push("Usage: promigence <command> [flags] [-- CMD] (--json | --text · --api-key K · --version)");
1377
+ for (const g of GROUPS) {
1378
+ out.push(g);
1379
+ for (const [name, summary] of groupRows(g))
1380
+ out.push(` ${pad(name, 26)}${summary}`);
1381
+ }
1382
+ out.push("", `Golden path: ${GOLDEN_PATH}`, "");
1383
+ out.push("Output: --json|--text > PROMIGENCE_OUTPUT > TTY (non-TTY = JSON; exec --sandbox prints its command's own output); no ANSI if NO_COLOR or piped.");
1384
+ const codes = EXIT_CODES.filter((e) => e.code !== 125 && e.code !== 13).map((e) => `${e.code} ${e.name}`).join(" · ");
1385
+ out.push(`Exit: ${codes}`);
1386
+ out.push(` precedence ${EXIT_PRECEDENCE_TEXT}; waits bounded (--wait-timeout ${WAIT_TIMEOUT_DEFAULT})`);
1387
+ out.push("", STATUS_LINE);
1388
+ return out.join("\n") + "\n";
1389
+ }
1390
+ export function signInLine(c) {
1391
+ if (!c.signIn)
1392
+ return undefined;
1393
+ const when = c.signIn === "always" ? "always" : `only ${c.signIn}`;
1394
+ return `Sign-in: ${when}, in your browser (--no-browser on a headless box).`;
1395
+ }
1396
+ export function renderCommandHelp(c) {
1397
+ const out = [`promigence ${c.name} — ${c.summary}`];
1398
+ const req = c.flags.filter((f) => f.required).map((f) => `${f.name} ${f.arg ?? ""}`.trim()).join(" ");
1399
+ out.push(`Usage: promigence ${c.name}${req ? " " + req : ""}${c.args ? " " + c.args : ""} [flags]`);
1400
+ const shown = c.flags.filter((f) => !f.hidden);
1401
+ if (shown.length) {
1402
+ out.push("Flags:");
1403
+ for (const f of shown) {
1404
+ const head = `${f.name}${f.arg ? " " + f.arg : ""}${f.repeatable ? " …" : ""}`;
1405
+ out.push(` ${pad(head, 24)}${f.summary}`);
1406
+ }
1407
+ }
1408
+ if (c.note)
1409
+ out.push(`Note: ${c.note}.`);
1410
+ if (c.passthroughExit)
1411
+ out.push(`Exit: child's code passes through; a command not found in the sandbox exits 127; Promigence's own failures exit ${c.passthroughExit} (promigence_exit in JSON).`);
1412
+ if (c.autoIdempotencyKey)
1413
+ out.push("Idempotency: auto-keyed per 10-min bucket.");
1414
+ const signIn = signInLine(c);
1415
+ if (signIn)
1416
+ out.push(signIn);
1417
+ if (c.next.length)
1418
+ out.push(`Next: ${c.next.join(" · ")}`);
1419
+ if (c.status !== "released")
1420
+ out.push(`Status: not released in v${VERSION} (exits 1 not_released).`);
1421
+ return out.join("\n") + "\n";
1422
+ }
1423
+ export function notReleasedEnvelope(c) {
1424
+ const env = {
1425
+ schema: SCHEMA,
1426
+ command: c.name,
1427
+ ok: false,
1428
+ error: {
1429
+ code: "not_released",
1430
+ message: `promigence ${c.name} is not released yet: promigence@${VERSION} does not implement it.`,
1431
+ fix: `Update with 'npm i -g promigence' once a release implements '${c.name}' (support@promigence.ai can tell you when). ` +
1432
+ `Until then read ${DOCS_URL}.`,
1433
+ docs: DOCS_URL,
1434
+ guide: "promigence agent-guide --section errors --code not_released",
1435
+ },
1436
+ next: [
1437
+ { cmd: "promigence agent-guide", why: "the primitive, golden path, output contract and exit codes" },
1438
+ { cmd: "promigence --version", why: "the version you have now" },
1439
+ ],
1440
+ };
1441
+ if (c.passthroughExit)
1442
+ env.promigence_exit = 1;
1443
+ return env;
1444
+ }
1445
+ export function notReleasedLines(c, env) {
1446
+ return [
1447
+ `promigence ${c.name}: not yet released (error not_released, exit 1)`,
1448
+ ` ${env.error.message}`,
1449
+ ` fix: ${env.error.fix}`,
1450
+ " next: promigence agent-guide",
1451
+ ];
1452
+ }