@shardflux/cli 0.4.1 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The account plane's command table (0.5.0+; docs/progress/account-cli.md §5): sign-in, setup, organizations,
3
+ * projects, API keys, members, invitations, billing, audit, account export/deletion, plus the project-key commands
4
+ * that were dashboard-only before (egress, volumes) and template publish/archive. Pure data like args.ts, which
5
+ * merges it into COMMANDS.
6
+ *
7
+ * Credentials: commands under auth, orgs, projects, api-keys, members, invitations, billing, audit, account and
8
+ * templates publish|archive use the user session (SHARDFLUX_SESSION_TOKEN, else `shard auth login`'s saved one);
9
+ * egress and volumes use the project API key like the other workspace commands. Secrets never come from arguments:
10
+ * passwords are read from standard input or a no-echo prompt; emailed links are single-use positionals.
11
+ */
12
+ import type { CommandSpec } from './args.js';
13
+ export declare const ACCOUNT_COMMANDS: CommandSpec[];
@@ -0,0 +1,425 @@
1
+ const ORG = {
2
+ org: { type: 'string', value: '<org>', description: 'Organization: id or slug. Default: the saved one (shard orgs use), else your only organization.' },
3
+ };
4
+ const PROJECT = {
5
+ project: { type: 'string', value: '<project>', description: 'Project: id or slug. Default: the saved one (shard projects use), else the organization’s only project.' },
6
+ };
7
+ const SECOND_FACTOR = {
8
+ code: { type: 'string', value: '<totp>', description: 'Current 6-digit code of your authenticator app (two-factor accounts).' },
9
+ 'recovery-code': { type: 'string', value: '<code>', description: 'A one-time recovery code instead of --code.' },
10
+ };
11
+ const YES = (what) => ({ yes: { type: 'boolean', description: `Required: confirm ${what}.` } });
12
+ /** An emailed link (or the token in it): single use, so an argument is fine. */
13
+ const LINK = { name: 'link|token', required: true };
14
+ const PAGE = {
15
+ all: { type: 'boolean', description: 'Fetch every page.' },
16
+ limit: { type: 'string', value: '<n>', description: 'Page size.' },
17
+ cursor: { type: 'string', value: '<cursor>', description: 'Continue from a previous page.' },
18
+ };
19
+ const OPEN = { open: { type: 'boolean', description: 'Also open the page in the default browser (best effort; the URL is printed either way).' } };
20
+ const AUDIT_FILTERS = {
21
+ source: { type: 'string', value: '<app|cell_tool>', description: 'API events (app) or workspace tool calls (cell_tool).' },
22
+ 'actor-type': { type: 'string', value: '<type>', description: 'user, api_key, system, operator or anonymous.' },
23
+ actor: { type: 'string', value: '<id>', description: 'Actor id (a user or API key id).' },
24
+ action: { type: 'string', value: '<prefix>', description: 'Action prefix, e.g. workspace. or tool.exec.' },
25
+ 'target-type': { type: 'string', value: '<type>', description: 'Target type, e.g. workspace or api_key.' },
26
+ target: { type: 'string', value: '<id>', description: 'Target id.' },
27
+ 'project-id': { type: 'string', value: '<id>', description: 'Only events of this project.' },
28
+ workspace: { type: 'string', value: '<id>', description: 'Only events of this workspace (id).' },
29
+ 'agent-session': { type: 'string', value: '<id>', description: 'Only events of this agent session.' },
30
+ outcome: { type: 'string', value: '<outcome>', description: 'success, failure or denied.' },
31
+ from: { type: 'string', value: '<time>', description: 'Inclusive start: an RFC 3339 time, or a duration ago such as 24h or 7d.' },
32
+ to: { type: 'string', value: '<time>', description: 'Exclusive end: an RFC 3339 time, or a duration ago.' },
33
+ };
34
+ const EGRESS_SCOPE = {
35
+ project: { type: 'string', value: '<project-id>', description: 'Project policy of this project (default: the API key’s project).' },
36
+ workspace: { type: 'string', value: '<id|key>', description: 'The workspace’s own policy (override) and its enforcement instead.' },
37
+ };
38
+ const VOLUME_WAIT = {
39
+ wait: { type: 'boolean', description: 'Wait until the cell finished (default; --no-wait returns with the operation).' },
40
+ timeout: { type: 'string', value: '<duration>', description: 'Give up waiting after this long (default 5m); the operation continues server side.' },
41
+ };
42
+ export const ACCOUNT_COMMANDS = [
43
+ // ---- sign-in (auth) ------------------------------------------------------------------------------------
44
+ {
45
+ path: ['auth', 'register'],
46
+ summary: 'Create an account; a verification link is emailed (the password is read from standard input or a prompt)',
47
+ description: 'The answer is the same whether or not the address already has an account (no enumeration). On a terminal the password is asked twice; otherwise it is the first line of standard input. Next: open the link, or pass it to "shard auth verify-email", then "shard auth login".',
48
+ positionals: [],
49
+ options: {
50
+ email: { type: 'string', value: '<email>', description: 'Your email address (required).' },
51
+ name: { type: 'string', value: '<display name>', description: 'Your name as others see it (default: none; setup names the organization after it).' },
52
+ },
53
+ examples: [`printf '%s\\n' "$PASSWORD" | shard auth register --email you@example.com --name "Ada Lovelace"`, 'shard auth register --email you@example.com # prompts for the password'],
54
+ },
55
+ {
56
+ path: ['auth', 'verify-email'],
57
+ summary: 'Verify your email with the emailed link (or its token)',
58
+ description: 'Pass the whole link from the email (quote it: it contains #). A link from "resend-verification" needs a new password: it is read from standard input (or asked twice on a terminal) and set at once; without one the command prints how to set it later.',
59
+ positionals: [LINK],
60
+ options: {},
61
+ examples: [`shard auth verify-email 'https://app.shardflux.dev/auth/verify-email#token=...'`],
62
+ },
63
+ { path: ['auth', 'resend-verification'], summary: 'Email a new verification link (signed in, email not verified yet)', positionals: [], options: {} },
64
+ {
65
+ path: ['auth', 'login'],
66
+ summary: 'Sign in: saves a session for this API in the credentials file (the password is read from standard input or a prompt)',
67
+ description: 'Two-factor accounts: pass --code (or --recovery-code), or type the code at the prompt; without either the pending sign-in is saved and "shard auth mfa --code <code>" completes it within 10 minutes (exit 4 until then). Prints who you are, your organizations and the next step. A session lasts 30 days of use (90 at most). SHARDFLUX_SESSION_TOKEN, when set, is used instead of the saved session.',
68
+ positionals: [],
69
+ options: { email: { type: 'string', value: '<email>', description: 'Your email address (required).' }, ...SECOND_FACTOR },
70
+ examples: [`printf '%s\\n' "$PASSWORD" | shard auth login --email you@example.com`, `printf '%s\\n' "$PASSWORD" | shard auth login --email you@example.com --code 123456`],
71
+ },
72
+ {
73
+ path: ['auth', 'mfa'],
74
+ summary: 'Complete a pending two-factor sign-in with a code',
75
+ positionals: [],
76
+ options: { ...SECOND_FACTOR },
77
+ examples: ['shard auth mfa --code 123456', 'shard auth mfa --recovery-code abcd-efgh-ijkl'],
78
+ },
79
+ {
80
+ path: ['auth', 'status'],
81
+ summary: 'Show the session (user, verification, two-factor, expiry, step-up) and the saved API key, without secrets',
82
+ description: 'Works signed out too ("not signed in"). A saved session the API no longer accepts is removed.',
83
+ positionals: [],
84
+ options: {},
85
+ },
86
+ {
87
+ path: ['auth', 'logout'],
88
+ summary: 'Sign out: end the session and remove it from the credentials file (a saved API key stays)',
89
+ positionals: [],
90
+ options: { all: { type: 'boolean', description: 'End every session of your account (browser and CLI) and revoke workspace tool tokens issued through them.' } },
91
+ },
92
+ {
93
+ path: ['auth', 'sessions'],
94
+ summary: 'List your live sessions (browser and CLI), or revoke one',
95
+ positionals: [],
96
+ options: { revoke: { type: 'string', value: '<session-id>', description: 'End this session.' } },
97
+ },
98
+ {
99
+ path: ['auth', 'step-up'],
100
+ summary: 'Confirm your password (and code) for sensitive actions: valid 10 minutes',
101
+ description: 'Needed for two-factor changes, an email change, data exports and deletions. On a terminal "shard" asks by itself when the API requires it; scripts run this first. The password comes from standard input or a prompt; two-factor accounts add --code (or type it at the prompt).',
102
+ positionals: [],
103
+ options: { ...SECOND_FACTOR },
104
+ examples: [`printf '%s\\n' "$PASSWORD" | shard auth step-up`, `printf '%s\\n' "$PASSWORD" | shard auth step-up --code 123456`],
105
+ },
106
+ {
107
+ path: ['auth', 'password', 'change'],
108
+ summary: 'Change your password (current, then new, from standard input lines or prompts); other sessions end',
109
+ positionals: [],
110
+ options: {},
111
+ examples: [`printf '%s\\n%s\\n' "$CURRENT" "$NEW" | shard auth password change`],
112
+ },
113
+ {
114
+ path: ['auth', 'password', 'reset'],
115
+ summary: 'Email a password reset link',
116
+ positionals: [],
117
+ options: { email: { type: 'string', value: '<email>', description: 'The account’s email address (required).' } },
118
+ },
119
+ {
120
+ path: ['auth', 'password', 'reset-confirm'],
121
+ summary: 'Set a new password with the emailed reset link (new password from standard input or a prompt)',
122
+ positionals: [LINK],
123
+ options: {},
124
+ examples: [`printf '%s\\n' "$NEW" | shard auth password reset-confirm 'https://app.shardflux.dev/auth/reset-password#token=...'`],
125
+ },
126
+ {
127
+ path: ['auth', 'email', 'change'],
128
+ summary: 'Change your sign-in email: a confirmation link goes to the new address',
129
+ positionals: [],
130
+ options: { email: { type: 'string', value: '<new email>', description: 'The new address (required).' } },
131
+ },
132
+ { path: ['auth', 'email', 'confirm'], summary: 'Confirm an email change with the link sent to the new address', positionals: [LINK], options: {} },
133
+ {
134
+ path: ['auth', 'totp', 'enroll'],
135
+ summary: 'Start two-factor authentication: prints the secret and otpauth:// URI for your authenticator app',
136
+ description: 'Then "shard auth totp confirm --code <code>" turns it on and prints recovery codes. Needs a recent step-up.',
137
+ positionals: [],
138
+ options: {},
139
+ },
140
+ {
141
+ path: ['auth', 'totp', 'confirm'],
142
+ summary: 'Turn two-factor authentication on with a current code; prints recovery codes once',
143
+ positionals: [],
144
+ options: { code: { type: 'string', value: '<totp>', description: 'Current 6-digit code (required).' } },
145
+ },
146
+ {
147
+ path: ['auth', 'totp', 'disable'],
148
+ summary: 'Turn two-factor authentication off (step-up with a code first; the password from standard input or a prompt)',
149
+ positionals: [],
150
+ options: { ...SECOND_FACTOR },
151
+ },
152
+ { path: ['auth', 'totp', 'recovery-codes'], summary: 'Replace your recovery codes (the old ones stop working); prints the new ones once', positionals: [], options: {} },
153
+ // ---- setup and environment -----------------------------------------------------------------------------
154
+ {
155
+ path: ['setup'],
156
+ summary: 'Get ready to run workspaces: use (or create) an organization and project, create an API key and save it',
157
+ description: 'Needs a session (shard auth login). With no organization it creates "<your name>\'s organization" and a project "Default"; the key gets every workspace tool unless --tool. The key is saved in the credentials file, checked against the API, and every workspace command uses it from then on. Idempotent: a saved key that still works is kept (unless --new-key).',
158
+ positionals: [],
159
+ options: {
160
+ ...ORG,
161
+ ...PROJECT,
162
+ 'org-name': { type: 'string', value: '<name>', description: 'Name of the organization to create when you have none.' },
163
+ 'project-name': { type: 'string', value: '<name>', description: 'Name of the project to create when the organization has none (default Default).' },
164
+ 'key-name': { type: 'string', value: '<name>', description: 'Name of the API key (default "shard-cli <hostname>").' },
165
+ tool: { type: 'string', multiple: true, value: '<tool>', description: 'Workspace tool the key may use (repeatable; exec, files, pty, process, git, browser). Default: all.' },
166
+ 'new-key': { type: 'boolean', description: 'Create and save a new key even when the saved one works (the old key stays valid until revoked).' },
167
+ },
168
+ examples: ['shard setup', 'shard setup --org acme --project web --tool exec --tool files'],
169
+ },
170
+ {
171
+ path: ['env'],
172
+ summary: 'Print shell commands that export the saved API key (and API URL) for tools that read the environment',
173
+ description: 'Prints the key in clear text: for eval, not for logs. SHARDFLUX_API_URL is included when it is not the default.',
174
+ positionals: [],
175
+ options: { shell: { type: 'string', value: '<sh|fish|powershell>', description: 'Syntax (default sh: bash, zsh, dash).' } },
176
+ examples: ['eval "$(shard env)"', 'shard env --shell fish | source', 'shard env --shell powershell | Invoke-Expression'],
177
+ },
178
+ // ---- organizations, projects, keys, members, invitations -----------------------------------------------
179
+ { path: ['orgs', 'list'], summary: 'List your organizations and your role in each (* marks the default)', positionals: [], options: {} },
180
+ {
181
+ path: ['orgs', 'create'],
182
+ summary: 'Create an organization (you become its owner)',
183
+ positionals: [{ name: 'name', required: true }],
184
+ options: { slug: { type: 'string', value: '<slug>', description: 'URL name (default: from the name plus a random suffix).' }, use: { type: 'boolean', description: 'Make it the default organization.' } },
185
+ },
186
+ { path: ['orgs', 'get'], summary: 'Show an organization with its plan', positionals: [{ name: 'org' }], options: {} },
187
+ { path: ['orgs', 'use'], summary: 'Make an organization the default for account commands (saved per API URL)', positionals: [{ name: 'org', required: true }], options: {} },
188
+ {
189
+ path: ['orgs', 'export'],
190
+ summary: 'Export an organization’s data as JSON to a file (owners; needs a recent step-up)',
191
+ positionals: [{ name: 'org' }],
192
+ options: { out: { type: 'string', value: '<file>', description: 'Where to write it (default ./organization-export-<id>.json; created 0600).' }, force: { type: 'boolean', description: 'Overwrite an existing file.' } },
193
+ },
194
+ { path: ['orgs', 'deletion'], summary: 'Show what deleting an organization would do and what blocks it', positionals: [{ name: 'org' }], options: {} },
195
+ {
196
+ path: ['orgs', 'workspaces'],
197
+ summary: 'List the organization’s workspaces in every project (as the dashboard does)',
198
+ description: 'Your session sees what your role allows. Running commands in a workspace (ws open, exec, files) stays per project with its API key: shard projects use <project> && shard setup.',
199
+ positionals: [{ name: 'org' }],
200
+ options: {
201
+ project: { type: 'string', value: '<project>', description: 'Only this project (id or slug).' },
202
+ state: { type: 'string', value: '<state>', description: 'Observed state filter (running, suspended, creating, ...).' },
203
+ prefix: { type: 'string', value: '<key-prefix>', description: 'Only keys starting with this prefix.' },
204
+ lifetime: { type: 'string', value: '<persistent|session|any>', description: 'Lifetime filter (default persistent: sessions are hidden).' },
205
+ purpose: { type: 'string', value: '<standard|template_draft|template_test|any>', description: 'Purpose filter (default standard).' },
206
+ 'include-deleted': { type: 'boolean', description: 'Include deleted workspaces and ended sessions.' },
207
+ ...PAGE,
208
+ limit: { type: 'string', value: '<n>', description: 'Page size, 1-200 (default 50).' },
209
+ },
210
+ examples: ['shard orgs workspaces', 'shard orgs workspaces acme --project web --state running'],
211
+ },
212
+ {
213
+ path: ['orgs', 'delete'],
214
+ summary: 'Delete an organization: workspaces are deleted, keys revoked, projects removed (owners; step-up)',
215
+ description: 'Irreversible. Refused while a subscription is active (cancel it in the portal: shard billing portal) or a checkout is open.',
216
+ positionals: [{ name: 'org', required: true }],
217
+ options: { confirm: { type: 'string', value: '<slug>', description: 'The organization’s slug, typed out (required).' }, ...YES('the deletion') },
218
+ },
219
+ { path: ['projects', 'list'], summary: 'List the organization’s projects (* marks the default)', positionals: [], options: { ...ORG } },
220
+ {
221
+ path: ['projects', 'create'],
222
+ summary: 'Create a project (owners and admins)',
223
+ positionals: [{ name: 'name', required: true }],
224
+ options: { slug: { type: 'string', value: '<slug>', description: 'URL name (default: from the name).' }, ...ORG, use: { type: 'boolean', description: 'Make it the default project.' } },
225
+ },
226
+ { path: ['projects', 'get'], summary: 'Show a project', positionals: [{ name: 'project', required: true }], options: { ...ORG } },
227
+ { path: ['projects', 'use'], summary: 'Make a project (and its organization) the default for account commands', positionals: [{ name: 'project', required: true }], options: { ...ORG } },
228
+ { path: ['api-keys', 'list'], summary: 'List the project’s API keys (never secrets; * marks the saved key)', positionals: [], options: { ...PROJECT, ...ORG } },
229
+ {
230
+ path: ['api-keys', 'create'],
231
+ summary: 'Create a project API key: printed once (or saved with --save)',
232
+ description: 'Owners and admins grant workspace tools; billing members create keys without tools. The secret cannot be shown again.',
233
+ positionals: [{ name: 'name', required: true }],
234
+ options: {
235
+ tool: { type: 'string', multiple: true, value: '<tool>', description: 'Workspace tool the key may use (repeatable). Default: all six.' },
236
+ 'read-only': { type: 'boolean', description: 'No workspace tools (read-only API use).' },
237
+ expires: { type: 'string', value: '<30d|90d|365d|never>', description: 'Expiry (default never).' },
238
+ save: { type: 'boolean', description: 'Save it in the credentials file for the workspace commands instead of printing it.' },
239
+ ...PROJECT,
240
+ ...ORG,
241
+ },
242
+ examples: ['shard api-keys create ci --tool exec --tool files --expires 90d', 'shard api-keys create laptop --save'],
243
+ },
244
+ {
245
+ path: ['api-keys', 'revoke'],
246
+ summary: 'Revoke an API key (API calls stop at once, tool tokens within 30 s)',
247
+ positionals: [{ name: 'id|key_id', required: true }],
248
+ options: { ...PROJECT, ...ORG, ...YES('the revocation') },
249
+ },
250
+ { path: ['members', 'list'], summary: 'List the organization’s members and roles', positionals: [], options: { ...ORG } },
251
+ {
252
+ path: ['members', 'set-role'],
253
+ summary: 'Change a member’s role: owner, admin, member or billing (owners and admins)',
254
+ positionals: [{ name: 'user|email', required: true }, { name: 'role', required: true }],
255
+ options: { ...ORG },
256
+ },
257
+ {
258
+ path: ['members', 'remove'],
259
+ summary: 'Remove a member (or yourself: leave the organization)',
260
+ positionals: [{ name: 'user|email', required: true }],
261
+ options: { ...ORG, ...YES('the removal') },
262
+ },
263
+ { path: ['invitations', 'list'], summary: 'List the organization’s invitations (owners and admins)', positionals: [], options: { ...ORG } },
264
+ {
265
+ path: ['invitations', 'create'],
266
+ summary: 'Invite someone by email (they get a link; they accept with shard invitations accept)',
267
+ positionals: [{ name: 'email', required: true }],
268
+ options: { role: { type: 'string', value: '<role>', description: 'owner, admin, member or billing (required).' }, ...ORG },
269
+ },
270
+ { path: ['invitations', 'revoke'], summary: 'Revoke a pending invitation', positionals: [{ name: 'id', required: true }], options: { ...ORG } },
271
+ {
272
+ path: ['invitations', 'accept'],
273
+ summary: 'Accept an invitation with the emailed link (your verified email must be the invited one)',
274
+ positionals: [LINK],
275
+ options: { use: { type: 'boolean', description: 'Make the organization the default.' } },
276
+ },
277
+ // ---- billing, audit, account ---------------------------------------------------------------------------
278
+ { path: ['billing', 'plans'], summary: 'List the plans and prices (public; no sign-in needed)', positionals: [], options: {} },
279
+ { path: ['billing', 'status'], summary: 'Show the plan, billing state, subscription period and any pending checkout', positionals: [], options: { ...ORG } },
280
+ {
281
+ path: ['billing', 'upgrade'],
282
+ summary: 'Buy a plan: prints the Stripe Checkout URL where a person pays (owners and billing members)',
283
+ description: 'The plan must be purchasable (shard billing plans). The API activates it only after Stripe confirms the payment: --wait polls until the subscription is active (exit 0), the checkout expires or is canceled (exit 6), or --timeout (exit 5; the checkout stays open). An organization that already has a subscription changes plans in the billing portal: its URL is printed instead (exit 0).',
284
+ positionals: [{ name: 'plan', required: true }],
285
+ options: {
286
+ ...ORG,
287
+ ...OPEN,
288
+ wait: { type: 'boolean', description: 'Wait until the subscription is active (progress on stderr).' },
289
+ timeout: { type: 'string', value: '<duration>', description: 'Give up waiting after this long (default 15m).' },
290
+ },
291
+ examples: ['shard billing upgrade developer --open --wait'],
292
+ },
293
+ { path: ['billing', 'portal'], summary: 'Open the Stripe billing portal: plan changes, payment methods, invoices, cancellation', positionals: [], options: { ...ORG, ...OPEN } },
294
+ { path: ['billing', 'invoices'], summary: 'List invoices, newest first', positionals: [], options: { ...ORG, ...PAGE, limit: { type: 'string', value: '<n>', description: 'Page size, 1-100 (default 20).' } } },
295
+ {
296
+ path: ['billing', 'alerts'],
297
+ summary: 'Show or set the usage alert thresholds (percent of each allowance; email alerts)',
298
+ positionals: [],
299
+ options: {
300
+ ...ORG,
301
+ set: { type: 'string', value: '<p1,p2,...>', description: 'Thresholds in percent, 1-100, at most 5 (e.g. 50,80,100).' },
302
+ clear: { type: 'boolean', description: 'Turn alerts off.' },
303
+ },
304
+ examples: ['shard billing alerts --set 50,80,100'],
305
+ },
306
+ {
307
+ path: ['billing', 'overage'],
308
+ summary: 'Show or change opt-in overage and its spend cap: status (default), on --cap <dollars>, off, cap <dollars>',
309
+ description: 'With overage on, workspaces keep opening and running past the CPU-hours and RAM GiB-hours allowances; the usage past them is charged on the next invoice until the charges reach the spend cap (per billing period, $1 up to the plan price). Off by default; turning it off is always allowed and what it charged stays on the invoice. Owners and billing members change it, and each of them gets an email about the change. This period\'s charges: shard usage.',
310
+ positionals: [{ name: 'status|on|off|cap' }, { name: 'dollars' }],
311
+ options: {
312
+ ...ORG,
313
+ cap: { type: 'string', value: '<dollars>', description: 'With on: the spend cap per billing period in dollars, e.g. 9 or 9.50 (needed unless a cap is set already).' },
314
+ 'if-version': { type: 'string', value: '<n>', description: 'Only if the policy version is still n (shard billing overage --json shows it): refuses a concurrent change.' },
315
+ },
316
+ examples: ['shard billing overage', 'shard billing overage on --cap 9', 'shard billing overage cap 25.50', 'shard billing overage off'],
317
+ },
318
+ {
319
+ path: ['audit', 'list'],
320
+ summary: 'List the organization’s audit log, newest first (owners and admins)',
321
+ positionals: [],
322
+ options: { ...ORG, ...AUDIT_FILTERS, ...PAGE, limit: { type: 'string', value: '<n>', description: 'Page size, 1-200 (default 50).' } },
323
+ examples: ['shard audit list --action api_key. --from 7d', 'shard audit list --source cell_tool --workspace <id> --all --json'],
324
+ },
325
+ {
326
+ path: ['audit', 'export'],
327
+ summary: 'Export the audit log as NDJSON or CSV (at most 90 days and 50 000 rows)',
328
+ positionals: [],
329
+ options: {
330
+ ...ORG,
331
+ format: { type: 'string', value: '<ndjson|csv>', description: 'Default ndjson.' },
332
+ out: { type: 'string', value: '<file>', description: 'Write to this file (created 0600) instead of stdout.' },
333
+ force: { type: 'boolean', description: 'Overwrite an existing --out file.' },
334
+ ...AUDIT_FILTERS,
335
+ },
336
+ examples: ['shard audit export --format csv --from 30d --out audit.csv'],
337
+ },
338
+ { path: ['account', 'status'], summary: 'Show your account: profile, memberships, and any scheduled deletion and what blocks it', positionals: [], options: {} },
339
+ {
340
+ path: ['account', 'export'],
341
+ summary: 'Export your account data as JSON to a file (needs a recent step-up)',
342
+ positionals: [],
343
+ options: { out: { type: 'string', value: '<file>', description: 'Where to write it (default ./account-export-<id>.json; created 0600).' }, force: { type: 'boolean', description: 'Overwrite an existing file.' } },
344
+ },
345
+ {
346
+ path: ['account', 'delete'],
347
+ summary: 'Schedule the deletion of your account (cancel within the grace period; step-up)',
348
+ description: 'Refused while you are the only owner of an organization with other members, or its only member: transfer or delete it first (shard account status names them).',
349
+ positionals: [],
350
+ options: { confirm: { type: 'string', value: '<email>', description: 'Your email address, typed out (required).' }, ...YES('the deletion') },
351
+ },
352
+ { path: ['account', 'cancel-deletion'], summary: 'Cancel a scheduled account deletion', positionals: [], options: {} },
353
+ // ---- project API key: egress and volumes ---------------------------------------------------------------
354
+ {
355
+ path: ['egress', 'get'],
356
+ summary: 'Show the outbound network policy of the project (or of a workspace, with its enforcement)',
357
+ positionals: [],
358
+ options: { ...EGRESS_SCOPE },
359
+ },
360
+ {
361
+ path: ['egress', 'set'],
362
+ summary: 'Replace the project’s (or a workspace’s) outbound network policy from a JSON file',
363
+ description: 'The file is {"mode": "allow_all" | "allowlist" | "deny_all", "rules": [{"host": "api.github.com", "ports": [443]}], "cidrs": [...]}. A new immutable version is stored; the host enforces it (shard egress get --workspace shows how far).',
364
+ positionals: [],
365
+ options: {
366
+ file: { type: 'string', value: '<policy.json|->', description: 'The policy (required; - for standard input).' },
367
+ ...EGRESS_SCOPE,
368
+ 'if-version': { type: 'string', value: '<n>', description: 'Only if the current version is still n (from egress get): refuses a concurrent change.' },
369
+ },
370
+ examples: ['shard egress set --file policy.json', 'shard egress set --workspace acme/demo --file policy.json'],
371
+ },
372
+ { path: ['egress', 'history'], summary: 'List the policy versions, newest first', positionals: [], options: { ...EGRESS_SCOPE, limit: { type: 'string', value: '<n>', description: 'Page size, 1-200.' } } },
373
+ {
374
+ path: ['egress', 'clear'],
375
+ summary: 'Remove a workspace’s own policy (it follows the project policy again)',
376
+ positionals: [],
377
+ options: { workspace: { type: 'string', value: '<id|key>', description: 'The workspace (required).' } },
378
+ },
379
+ {
380
+ path: ['volumes', 'list'],
381
+ summary: 'List the project’s shared volumes',
382
+ positionals: [],
383
+ options: { 'include-org-shared': { type: 'boolean', description: 'Also the organization’s shared volumes (attachable here).' }, 'include-deleted': { type: 'boolean', description: 'Include deleted volumes.' }, all: { type: 'boolean', description: 'Fetch every page.' } },
384
+ },
385
+ {
386
+ path: ['volumes', 'create'],
387
+ summary: 'Create a shared volume (persistent storage attached to workspaces at a path)',
388
+ positionals: [{ name: 'name', required: true }],
389
+ options: {
390
+ 'quota-gib': { type: 'string', value: '<n>', description: 'Size limit in GiB (required).' },
391
+ 'org-shared': { type: 'boolean', description: 'Attachable from every project of the organization (owners/admins; API keys get 403).' },
392
+ ...VOLUME_WAIT,
393
+ },
394
+ examples: ['shard volumes create datasets --quota-gib 20'],
395
+ },
396
+ { path: ['volumes', 'get'], summary: 'Show a volume', positionals: [{ name: 'id|name', required: true }], options: {} },
397
+ {
398
+ path: ['volumes', 'delete'],
399
+ summary: 'Delete a volume and its data (refused while attached unless --force)',
400
+ positionals: [{ name: 'id|name', required: true }],
401
+ options: { force: { type: 'boolean', description: 'Detach it from every workspace first (guests see I/O errors on the mount).' }, ...YES('the deletion'), ...VOLUME_WAIT },
402
+ },
403
+ { path: ['volumes', 'attachments'], summary: 'List where a volume is attached', positionals: [{ name: 'id|name', required: true }], options: { 'include-detached': { type: 'boolean', description: 'Include past attachments.' } } },
404
+ {
405
+ path: ['volumes', 'attach'],
406
+ summary: 'Attach a volume to a workspace at a path',
407
+ positionals: [{ name: 'ws', required: true }, { name: 'volume', required: true }],
408
+ options: { mount: { type: 'string', value: '<path>', description: 'Absolute guest path, e.g. /mnt/data (required).' }, 'read-only': { type: 'boolean', description: 'Mount read-only (default read-write).' }, ...VOLUME_WAIT },
409
+ examples: ['shard volumes attach acme/demo datasets --mount /mnt/datasets --read-only'],
410
+ },
411
+ { path: ['volumes', 'detach'], summary: 'Detach a volume from a workspace', positionals: [{ name: 'ws', required: true }, { name: 'volume', required: true }], options: { ...VOLUME_WAIT } },
412
+ // ---- templates with the session ------------------------------------------------------------------------
413
+ {
414
+ path: ['templates', 'publish'],
415
+ summary: 'Publish a registered version of an organization template (owners/admins; signed in)',
416
+ positionals: [{ name: 'slug@version', required: true }],
417
+ options: { ...ORG },
418
+ },
419
+ {
420
+ path: ['templates', 'archive'],
421
+ summary: 'Archive a version: new opens stop using it; existing workspaces keep it (owners/admins; signed in)',
422
+ positionals: [{ name: 'slug@version', required: true }],
423
+ options: { ...ORG },
424
+ },
425
+ ];
@@ -0,0 +1,18 @@
1
+ import type { Handler } from './commands.js';
2
+ export declare const DEFAULT_API_URL = "https://api.shardflux.dev";
3
+ /**
4
+ * The organization name setup creates: "<display name or email local part>'s organization", within the API's rules
5
+ * for names (1-200 characters, no control characters or line breaks).
6
+ */
7
+ export declare function defaultOrgName(user: {
8
+ display_name: string | null;
9
+ email: string;
10
+ }): string;
11
+ export type ShellKind = 'sh' | 'fish' | 'powershell';
12
+ /** One line that sets `name` for `shell`, quoted so any value round-trips. */
13
+ export declare function exportLine(shell: ShellKind, name: string, value: string): string;
14
+ /** An RFC 3339 time, or a duration ago (`90m`, `24h`, `7d`, `2w`). */
15
+ export declare function parseTime(text: string, option: string, now?: number): string;
16
+ /** A spend cap in dollars ("9", "9.5", "9.50", "$9") as minor units (cents); at most two decimals. */
17
+ export declare function dollarsToMinor(text: string, what: string): number;
18
+ export declare const ACCOUNT_HANDLERS: Record<string, Handler>;