@shardflux/cli 0.4.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +175 -0
- package/README.md +442 -34
- package/dist/account-args.d.ts +13 -0
- package/dist/account-args.js +413 -0
- package/dist/account-commands.d.ts +16 -0
- package/dist/account-commands.js +1616 -0
- package/dist/args.d.ts +15 -3
- package/dist/args.js +280 -40
- package/dist/bin.js +5 -16
- package/dist/browser.d.ts +7 -0
- package/dist/browser.js +39 -0
- package/dist/commands.d.ts +67 -5
- package/dist/commands.js +390 -29
- package/dist/credentials.d.ts +77 -0
- package/dist/credentials.js +217 -0
- package/dist/errors.d.ts +63 -11
- package/dist/errors.js +107 -21
- package/dist/format.d.ts +70 -5
- package/dist/format.js +365 -6
- package/dist/handlers.d.ts +2 -0
- package/dist/handlers.js +7 -0
- package/dist/index.d.ts +19 -7
- package/dist/index.js +13 -6
- package/dist/input.d.ts +25 -0
- package/dist/input.js +167 -0
- package/dist/main.d.ts +16 -2
- package/dist/main.js +399 -31
- package/dist/update-check.d.ts +64 -0
- package/dist/update-check.js +128 -0
- package/package.json +3 -3
package/dist/format.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* `--json` output is the API's own objects (see README "JSON output"), so
|
|
4
4
|
* these functions only shape what people read.
|
|
5
5
|
*/
|
|
6
|
-
import type { AgentSession, DraftState, FileList, Operation, Secret, SecretAccessEvent, SecretVersion, TemplateBuild, TemplateDetail, TemplateDiffEntry, TemplateDraft, TemplateFileEntry, TemplateLanguages, TemplatePackagePage, TemplateSettings, TemplateSummary, WorkspaceChange, WorkspaceSecretBindings, WorkspaceView } from '@shardflux/sdk';
|
|
6
|
+
import type { AgentSession, ApiKey, AuditEvent, AuthSessionInfo, BillingCatalog, BillingInvoicePage, BillingSubscription, DataExport, DraftState, ExecutionChange, ExecutionResult, FileInfo, EgressPolicyVersion, Invitation, Member, Organization, Project, ProjectEgressPolicy, Volume, VolumeAttachment, WorkspaceEgressPolicy, FileList, FileSearchResponse, Operation, Secret, SecretAccessEvent, SecretVersion, TemplateBuild, TemplateDetail, TemplateDiffEntry, TemplateDraft, TemplateFileEntry, TemplateLanguages, TemplatePackagePage, TemplateSettings, TemplateSummary, WorkspaceChange, WorkspaceSecretBindings, WorkspaceView } from '@shardflux/sdk';
|
|
7
7
|
import type { ErrorInfo } from './errors.js';
|
|
8
8
|
export declare function json(value: unknown): string;
|
|
9
9
|
export declare function table(headers: readonly string[], rows: readonly (readonly string[])[]): string;
|
|
@@ -14,7 +14,9 @@ export declare function time(iso: string | null | undefined): string;
|
|
|
14
14
|
export declare function pairs(obj: Record<string, unknown> | null | undefined): string;
|
|
15
15
|
export declare function operationSummary(op: Operation | null | undefined): string;
|
|
16
16
|
export declare const WORKSPACE_HEADERS: readonly ["ID", "KEY", "STATE", "DESIRED", "TEMPLATE", "OPERATION", "CREATED"];
|
|
17
|
-
/**
|
|
17
|
+
/** `file_first` as people write it (`--mode file-first`). */
|
|
18
|
+
export declare function modeText(mode: WorkspaceView['mode'] | undefined): string;
|
|
19
|
+
/** Observed state plus what sets the workspace apart: file-first, session, draft or test instance, and how it ended. */
|
|
18
20
|
export declare function workspaceState(w: WorkspaceView): string;
|
|
19
21
|
export declare function workspaceRow(w: WorkspaceView): string[];
|
|
20
22
|
export declare function workspaceTable(ws: readonly WorkspaceView[]): string;
|
|
@@ -31,6 +33,10 @@ export declare const OPERATION_HEADERS: readonly ["ID", "KIND", "STATE", "REASON
|
|
|
31
33
|
export declare function operationTable(ops: readonly Operation[]): string;
|
|
32
34
|
export declare function operationDetail(op: Operation): string;
|
|
33
35
|
export declare function sessionTable(sessions: readonly AgentSession[]): string;
|
|
36
|
+
/** `files stat`: one path's metadata; the revision only when requested and returned. */
|
|
37
|
+
export declare function fileInfoText(f: FileInfo): string;
|
|
38
|
+
/** `files search`: grep-style `path:line:column: text`, context lines as `path-line- text`. */
|
|
39
|
+
export declare function searchText(r: FileSearchResponse): string;
|
|
34
40
|
export declare function fileTable(list: FileList): string;
|
|
35
41
|
export declare function secretTable(rows: readonly Secret[]): string;
|
|
36
42
|
/** Secret metadata; there is no value to show (values are write-only). */
|
|
@@ -44,10 +50,17 @@ export declare function bindingsText(b: WorkspaceSecretBindings): string;
|
|
|
44
50
|
*/
|
|
45
51
|
export declare function usageText(summary: unknown): string;
|
|
46
52
|
/**
|
|
47
|
-
*
|
|
48
|
-
* operation
|
|
53
|
+
* The `shard feedback` command a failed command suggests (0.5.0+), with the error's request id and code so the founder
|
|
54
|
+
* can find the logs (a failed operation's or execution's own error code, e.g. capacity_unavailable); the agent fills in
|
|
55
|
+
* the text.
|
|
49
56
|
*/
|
|
50
|
-
export declare function
|
|
57
|
+
export declare function feedbackCommand(e: ErrorInfo): string;
|
|
58
|
+
/**
|
|
59
|
+
* One stderr line: `error: <message> [code=... status=... request_id=...]` plus a hint when there is one, and the
|
|
60
|
+
* `feedback:` line (the command to report it) when given. A failed operation or execution also shows
|
|
61
|
+
* `retryable=true|false`.
|
|
62
|
+
*/
|
|
63
|
+
export declare function errorText(e: ErrorInfo, hint?: string, feedback?: string): string;
|
|
51
64
|
export declare function templateTable(rows: readonly TemplateSummary[]): string;
|
|
52
65
|
/** Permission bits as the four-digit octal `ls -l` users read (e.g. 0755). */
|
|
53
66
|
export declare function octal(mode: number): string;
|
|
@@ -66,3 +79,55 @@ export declare function packageTable(page: TemplatePackagePage): string;
|
|
|
66
79
|
export declare function templateDetailText(t: TemplateDetail): string;
|
|
67
80
|
/** A version's settings (contracts §24.3), one line per part. */
|
|
68
81
|
export declare function settingsText(s: TemplateSettings): string;
|
|
82
|
+
/** How many changed paths the human output lists before "... N more" (--json lists every one the result has). */
|
|
83
|
+
export declare const MAX_CHANGED_SHOWN = 100;
|
|
84
|
+
/**
|
|
85
|
+
* What an execution changed, one `A|M|D path` line each (the letters of a short VCS status; a directory ends in `/`), at
|
|
86
|
+
* most `max`; then how many more the result has, and a note when the cell listed only the first 10,000.
|
|
87
|
+
*/
|
|
88
|
+
export declare function changedText(changed: readonly ExecutionChange[], changedTruncated: boolean, max?: number): string;
|
|
89
|
+
/** The execution's state: `succeeded (exit code 0)`, `failed: <message> (<reason>)`, `running`. */
|
|
90
|
+
export declare function executionStateText(r: ExecutionResult): string;
|
|
91
|
+
/**
|
|
92
|
+
* The summary `shard ws exec -v` prints after a file-first command's output, and `shard executions get` with it (on
|
|
93
|
+
* stderr): execution id, state and exit status, base -> tree revision, `replayed` when true, truncated output, timings
|
|
94
|
+
* (`key=value`: queue_ms, run_ms, publish_ms, total_ms and the host's), then what changed.
|
|
95
|
+
*/
|
|
96
|
+
export declare function executionSummary(r: ExecutionResult, maxChanged?: number, opts?: {
|
|
97
|
+
replayed?: boolean;
|
|
98
|
+
}): string;
|
|
99
|
+
/**
|
|
100
|
+
* The `--json` document of an execution (`shard ws exec` on a file-first workspace, `shard executions get`): the cell's
|
|
101
|
+
* ExecutionResult with stdout/stderr as UTF-8 text (like the processful exec's), plus `stdout_base64` / `stderr_base64`
|
|
102
|
+
* with the exact bytes when a stream is not valid UTF-8 (its text then has U+FFFD for the invalid sequences).
|
|
103
|
+
*/
|
|
104
|
+
export declare function executionJson(r: ExecutionResult): Record<string, unknown>;
|
|
105
|
+
export declare function orgTable(rows: readonly Organization[], defaultId: string | null): string;
|
|
106
|
+
export declare function projectTable(rows: readonly Project[], defaultId: string | null): string;
|
|
107
|
+
/** active, expired or revoked (as the web app shows it). */
|
|
108
|
+
export declare function keyStatus(k: Pick<ApiKey, 'revoked_at' | 'expires_at'>, now?: number): 'active' | 'expired' | 'revoked';
|
|
109
|
+
export declare function toolsText(tools: readonly string[]): string;
|
|
110
|
+
export declare function apiKeyTable(rows: readonly ApiKey[], savedId: string | null): string;
|
|
111
|
+
export declare function memberTable(rows: readonly Member[]): string;
|
|
112
|
+
export declare function invitationTable(rows: readonly Invitation[]): string;
|
|
113
|
+
export declare function authSessionTable(rows: readonly AuthSessionInfo[]): string;
|
|
114
|
+
/** 900 + "usd" -> "9.00 USD" (minor units; zero-decimal currencies shown as whole numbers). */
|
|
115
|
+
/**
|
|
116
|
+
* A usage time series (GET .../usage or /v1/workspaces/{id}/usage): the range and freshness, then one row per bucket
|
|
117
|
+
* and meter. `FINAL` is no while the bucket can still change (the ledger finalizes hours after they end).
|
|
118
|
+
*/
|
|
119
|
+
export declare function usageSeriesText(series: unknown): string;
|
|
120
|
+
/** The period estimate (GET .../usage/estimate): the bill so far and where each allowance is heading. */
|
|
121
|
+
export declare function usageEstimateText(estimate: unknown): string;
|
|
122
|
+
export declare function money(amountMinor: number, currency: string): string;
|
|
123
|
+
export declare function planTable(catalog: BillingCatalog): string;
|
|
124
|
+
export declare function billingStatusText(s: BillingSubscription, org: Pick<Organization, 'name' | 'slug'>): string;
|
|
125
|
+
export declare function invoiceTable(rows: readonly BillingInvoicePage['data'][number][]): string;
|
|
126
|
+
export declare function auditTable(rows: readonly AuditEvent[]): string;
|
|
127
|
+
export declare function dataExportText(e: DataExport, out: string): string;
|
|
128
|
+
export declare function volumeTable(rows: readonly Volume[]): string;
|
|
129
|
+
export declare function volumeDetail(v: Volume): string;
|
|
130
|
+
export declare function attachmentTable(rows: readonly VolumeAttachment[]): string;
|
|
131
|
+
export declare function projectEgressText(p: ProjectEgressPolicy): string;
|
|
132
|
+
export declare function workspaceEgressText(w: WorkspaceEgressPolicy): string;
|
|
133
|
+
export declare function egressVersionTable(rows: readonly EgressPolicyVersion[]): string;
|
package/dist/format.js
CHANGED
|
@@ -44,9 +44,15 @@ export function operationSummary(op) {
|
|
|
44
44
|
return `${op.kind} ${op.state}${op.state_reason ? ` (${op.state_reason})` : ''}`;
|
|
45
45
|
}
|
|
46
46
|
export const WORKSPACE_HEADERS = ['ID', 'KEY', 'STATE', 'DESIRED', 'TEMPLATE', 'OPERATION', 'CREATED'];
|
|
47
|
-
/**
|
|
47
|
+
/** `file_first` as people write it (`--mode file-first`). */
|
|
48
|
+
export function modeText(mode) {
|
|
49
|
+
return mode === 'file_first' ? 'file-first' : (mode ?? 'processful');
|
|
50
|
+
}
|
|
51
|
+
/** Observed state plus what sets the workspace apart: file-first, session, draft or test instance, and how it ended. */
|
|
48
52
|
export function workspaceState(w) {
|
|
49
53
|
const tags = [];
|
|
54
|
+
if (w.mode === 'file_first')
|
|
55
|
+
tags.push('file-first');
|
|
50
56
|
if (w.purpose === 'template_draft')
|
|
51
57
|
tags.push('draft');
|
|
52
58
|
else if (w.purpose === 'template_test')
|
|
@@ -110,6 +116,9 @@ export function workspaceDetail(w) {
|
|
|
110
116
|
['state', `${w.observed_state} (desired ${w.desired_state})`],
|
|
111
117
|
['ready', isReady(w) ? 'yes' : 'no'],
|
|
112
118
|
['template', `${w.template.slug} v${w.template.version} (${w.template.version_id})`],
|
|
119
|
+
// The mode (0.5.0; contracts §29) when the API reports it; a file-first workspace's state is its tree revision.
|
|
120
|
+
...(w.mode !== undefined ? [['mode', modeText(w.mode)]] : []),
|
|
121
|
+
...(w.mode === 'file_first' ? [['tree revision', String(w.tree_revision)]] : []),
|
|
113
122
|
['cell', w.cell_id ? `${w.cell_id} ${w.cell_endpoint ?? '(endpoint unresolved)'}` : '-'],
|
|
114
123
|
['ceilings', c ? `cpu ${c.cpu_millis}m (${c.sources.cpu_millis}), memory ${c.memory_mib} MiB (${c.sources.memory_mib}), disk ${c.disk_gib} GiB (${c.sources.disk_gib})` : '-'],
|
|
115
124
|
['grants', pairs(w.grants)],
|
|
@@ -152,6 +161,31 @@ export function operationDetail(op) {
|
|
|
152
161
|
export function sessionTable(sessions) {
|
|
153
162
|
return table(['ID', 'LABEL', 'PRINCIPAL', 'TOKENS', 'LAST TOKEN', 'REVOKED'], sessions.map((s) => [s.id, s.agent_label, `${s.principal_type}:${s.principal_id}`, String(s.tokens_issued), time(s.last_token_issued_at), time(s.revoked_at)]));
|
|
154
163
|
}
|
|
164
|
+
/** `files stat`: one path's metadata; the revision only when requested and returned. */
|
|
165
|
+
export function fileInfoText(f) {
|
|
166
|
+
return kv([
|
|
167
|
+
['path', f.path],
|
|
168
|
+
['type', f.type],
|
|
169
|
+
['size', String(f.size)],
|
|
170
|
+
['mode', f.mode],
|
|
171
|
+
['owner', f.uid !== undefined || f.gid !== undefined ? `${f.uid ?? '-'}:${f.gid ?? '-'}` : '-'],
|
|
172
|
+
['modified', time(f.modified_at)],
|
|
173
|
+
...(f.symlink_target !== undefined ? [['target', f.symlink_target]] : []),
|
|
174
|
+
...(f.revision !== undefined ? [['revision', f.revision]] : []),
|
|
175
|
+
]);
|
|
176
|
+
}
|
|
177
|
+
/** `files search`: grep-style `path:line:column: text`, context lines as `path-line- text`. */
|
|
178
|
+
export function searchText(r) {
|
|
179
|
+
if (r.matches.length === 0)
|
|
180
|
+
return 'No matches.\n';
|
|
181
|
+
const lines = [];
|
|
182
|
+
for (const m of r.matches) {
|
|
183
|
+
(m.before ?? []).forEach((t, i, all) => lines.push(`${m.path}-${m.line - all.length + i}- ${t}`));
|
|
184
|
+
lines.push(`${m.path}:${m.line}:${m.column}: ${m.text}`);
|
|
185
|
+
(m.after ?? []).forEach((t, i) => lines.push(`${m.path}-${m.line + i + 1}- ${t}`));
|
|
186
|
+
}
|
|
187
|
+
return `${lines.join('\n')}\n`;
|
|
188
|
+
}
|
|
155
189
|
export function fileTable(list) {
|
|
156
190
|
return table(['TYPE', 'SIZE', 'MODIFIED', 'NAME'], list.entries.map((e) => [e.type, String(e.size), time(e.modified_at), e.name]));
|
|
157
191
|
}
|
|
@@ -248,11 +282,29 @@ export function usageText(summary) {
|
|
|
248
282
|
out += `\n${allowances}`;
|
|
249
283
|
return out || json(summary);
|
|
250
284
|
}
|
|
285
|
+
/** Request ids and error codes pasted unquoted into the suggested command must be shell-inert. */
|
|
286
|
+
const SHELL_SAFE = /^[A-Za-z0-9._:-]{1,200}$/;
|
|
287
|
+
/**
|
|
288
|
+
* The `shard feedback` command a failed command suggests (0.5.0+), with the error's request id and code so the founder
|
|
289
|
+
* can find the logs (a failed operation's or execution's own error code, e.g. capacity_unavailable); the agent fills in
|
|
290
|
+
* the text.
|
|
291
|
+
*/
|
|
292
|
+
export function feedbackCommand(e) {
|
|
293
|
+
const code = (e.code === 'operation_failed' || e.code === 'execution_failed') && typeof e.details?.error_code === 'string' ? e.details.error_code : e.code;
|
|
294
|
+
const parts = ['shard feedback --category bug'];
|
|
295
|
+
if (e.request_id && SHELL_SAFE.test(e.request_id))
|
|
296
|
+
parts.push(`--request-id ${e.request_id}`);
|
|
297
|
+
if (SHELL_SAFE.test(code))
|
|
298
|
+
parts.push(`--error-code ${code}`);
|
|
299
|
+
parts.push('"<what you expected>"');
|
|
300
|
+
return parts.join(' ');
|
|
301
|
+
}
|
|
251
302
|
/**
|
|
252
|
-
* One stderr line: `error: <message> [code=... status=... request_id=...]` plus a hint when there is one
|
|
253
|
-
* operation also shows
|
|
303
|
+
* One stderr line: `error: <message> [code=... status=... request_id=...]` plus a hint when there is one, and the
|
|
304
|
+
* `feedback:` line (the command to report it) when given. A failed operation or execution also shows
|
|
305
|
+
* `retryable=true|false`.
|
|
254
306
|
*/
|
|
255
|
-
export function errorText(e, hint) {
|
|
307
|
+
export function errorText(e, hint, feedback) {
|
|
256
308
|
const meta = [`code=${e.code}`];
|
|
257
309
|
if (e.status !== undefined)
|
|
258
310
|
meta.push(`status=${e.status}`);
|
|
@@ -260,15 +312,17 @@ export function errorText(e, hint) {
|
|
|
260
312
|
meta.push(`request_id=${e.request_id}`);
|
|
261
313
|
if (e.operation_id)
|
|
262
314
|
meta.push(`operation_id=${e.operation_id}`);
|
|
315
|
+
if (e.execution_id)
|
|
316
|
+
meta.push(`execution_id=${e.execution_id}`);
|
|
263
317
|
if (e.source === 'cell')
|
|
264
318
|
meta.push('source=cell');
|
|
265
|
-
if (e.code === 'operation_failed' && e.retryable !== undefined)
|
|
319
|
+
if ((e.code === 'operation_failed' || e.code === 'execution_failed') && e.retryable !== undefined)
|
|
266
320
|
meta.push(`retryable=${e.retryable}`);
|
|
267
321
|
const reason = e.details && typeof e.details.reason === 'string' ? ` (${e.details.reason})` : '';
|
|
268
322
|
const issues = Array.isArray(e.details?.issues)
|
|
269
323
|
? e.details.issues.filter(isObject).map((i) => ` ${scalar(i.path)}: ${scalar(i.message)}\n`).join('')
|
|
270
324
|
: '';
|
|
271
|
-
return `error: ${e.message}${reason} [${meta.join(' ')}]\n${issues}${hint ? `hint: ${hint}\n` : ''}`;
|
|
325
|
+
return `error: ${e.message}${reason} [${meta.join(' ')}]\n${issues}${hint ? `hint: ${hint}\n` : ''}${feedback ? `feedback: ${feedback}\n` : ''}`;
|
|
272
326
|
}
|
|
273
327
|
// ---- Templates v2 (contracts §19) ---------------------------------------------------------------
|
|
274
328
|
export function templateTable(rows) {
|
|
@@ -396,3 +450,308 @@ export function settingsText(s) {
|
|
|
396
450
|
['egress', egress],
|
|
397
451
|
]).replace(/^/gm, ' ');
|
|
398
452
|
}
|
|
453
|
+
// ---- File-first executions (0.5.0; contracts §29.8) ----------------------------------------------------------------------
|
|
454
|
+
const CHANGE_LETTER = { added: 'A', modified: 'M', deleted: 'D' };
|
|
455
|
+
/** How many changed paths the human output lists before "... N more" (--json lists every one the result has). */
|
|
456
|
+
export const MAX_CHANGED_SHOWN = 100;
|
|
457
|
+
/**
|
|
458
|
+
* What an execution changed, one `A|M|D path` line each (the letters of a short VCS status; a directory ends in `/`), at
|
|
459
|
+
* most `max`; then how many more the result has, and a note when the cell listed only the first 10,000.
|
|
460
|
+
*/
|
|
461
|
+
export function changedText(changed, changedTruncated, max = MAX_CHANGED_SHOWN) {
|
|
462
|
+
const shown = changed.slice(0, max).map((c) => `${CHANGE_LETTER[c.change] ?? '?'} ${c.path}${c.type === 'directory' ? '/' : ''}\n`);
|
|
463
|
+
const more = changed.length - shown.length;
|
|
464
|
+
const tail = more > 0 ? `... ${more} more (--json lists every one)\n` : '';
|
|
465
|
+
const cut = changedTruncated ? `... and more: the result lists only the first ${changed.length} changes\n` : '';
|
|
466
|
+
return shown.join('') + tail + cut;
|
|
467
|
+
}
|
|
468
|
+
/** An execution's exit status in words: `exit code 0`, `killed by signal 15`, `killed by signal 9 (timed out)`. */
|
|
469
|
+
function exitStatus(r) {
|
|
470
|
+
const signal = r.termSignal !== null && r.termSignal > 0 ? `killed by signal ${r.termSignal}` : null;
|
|
471
|
+
if (r.timedOut)
|
|
472
|
+
return `${signal ?? 'killed'}, timed out`;
|
|
473
|
+
if (signal && (r.exitCode === null || r.exitCode < 0))
|
|
474
|
+
return signal;
|
|
475
|
+
return `exit code ${r.exitCode ?? '-'}`;
|
|
476
|
+
}
|
|
477
|
+
/** The execution's state: `succeeded (exit code 0)`, `failed: <message> (<reason>)`, `running`. */
|
|
478
|
+
export function executionStateText(r) {
|
|
479
|
+
if (r.state === 'succeeded')
|
|
480
|
+
return `succeeded (${exitStatus(r)})`;
|
|
481
|
+
if (r.state === 'failed' || r.state === 'lost')
|
|
482
|
+
return `${r.state}${r.error?.message ? `: ${r.error.message}` : ''}${r.errorReason ? ` (${r.errorReason})` : ''}`;
|
|
483
|
+
return r.state;
|
|
484
|
+
}
|
|
485
|
+
/** `3 -> 4`, `3 (unchanged)`, `3 (nothing published)`, or the base revision a pending execution runs on. */
|
|
486
|
+
function revisionText(r) {
|
|
487
|
+
if (r.pending)
|
|
488
|
+
return `${r.baseRevision} (the command runs on it)`;
|
|
489
|
+
if (r.treeRevision === null)
|
|
490
|
+
return `${r.baseRevision} (nothing published)`;
|
|
491
|
+
return r.treeRevision === r.baseRevision ? `${r.baseRevision} (unchanged)` : `${r.baseRevision} -> ${r.treeRevision}`;
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* The summary `shard ws exec -v` prints after a file-first command's output, and `shard executions get` with it (on
|
|
495
|
+
* stderr): execution id, state and exit status, base -> tree revision, `replayed` when true, truncated output, timings
|
|
496
|
+
* (`key=value`: queue_ms, run_ms, publish_ms, total_ms and the host's), then what changed.
|
|
497
|
+
*/
|
|
498
|
+
export function executionSummary(r, maxChanged = MAX_CHANGED_SHOWN, opts = {}) {
|
|
499
|
+
const truncated = [r.stdoutTruncated ? 'stdout' : '', r.stderrTruncated ? 'stderr' : ''].filter(Boolean).join(' and ');
|
|
500
|
+
const rows = [
|
|
501
|
+
['execution', r.executionId],
|
|
502
|
+
['state', executionStateText(r)],
|
|
503
|
+
['revision', revisionText(r)],
|
|
504
|
+
];
|
|
505
|
+
// `executions get` passes replayed: false: every result it reads is a recorded one, so the line would say nothing.
|
|
506
|
+
if (r.replayed && opts.replayed !== false)
|
|
507
|
+
rows.push(['replayed', 'yes (the recorded result; the command did not run again)']);
|
|
508
|
+
if (truncated)
|
|
509
|
+
rows.push(['truncated', `${truncated} (at the output limit)`]);
|
|
510
|
+
if (Object.keys(r.timings).length)
|
|
511
|
+
rows.push(['timings', pairs(r.timings)]);
|
|
512
|
+
if (!r.pending)
|
|
513
|
+
rows.push(['changed', r.changed.length === 0 ? 'nothing' : `${r.changed.length}${r.changedTruncated ? '+' : ''} path${r.changed.length === 1 ? '' : 's'}`]);
|
|
514
|
+
return kv(rows) + changedText(r.changed, r.changedTruncated, maxChanged);
|
|
515
|
+
}
|
|
516
|
+
/** Bytes as UTF-8 text; null when they are not valid UTF-8. */
|
|
517
|
+
function utf8(bytes) {
|
|
518
|
+
try {
|
|
519
|
+
return new TextDecoder('utf-8', { fatal: true }).decode(bytes);
|
|
520
|
+
}
|
|
521
|
+
catch {
|
|
522
|
+
return null;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* The `--json` document of an execution (`shard ws exec` on a file-first workspace, `shard executions get`): the cell's
|
|
527
|
+
* ExecutionResult with stdout/stderr as UTF-8 text (like the processful exec's), plus `stdout_base64` / `stderr_base64`
|
|
528
|
+
* with the exact bytes when a stream is not valid UTF-8 (its text then has U+FFFD for the invalid sequences).
|
|
529
|
+
*/
|
|
530
|
+
export function executionJson(r) {
|
|
531
|
+
const stream = (name, bytes) => {
|
|
532
|
+
const text = utf8(bytes);
|
|
533
|
+
return text !== null ? { [name]: text } : { [name]: r.text(name), [`${name}_base64`]: Buffer.from(bytes).toString('base64') };
|
|
534
|
+
};
|
|
535
|
+
return {
|
|
536
|
+
execution_id: r.executionId,
|
|
537
|
+
state: r.state,
|
|
538
|
+
exit_code: r.exitCode,
|
|
539
|
+
term_signal: r.termSignal,
|
|
540
|
+
timed_out: r.timedOut,
|
|
541
|
+
base_revision: r.baseRevision,
|
|
542
|
+
tree_revision: r.treeRevision,
|
|
543
|
+
changed: r.changed,
|
|
544
|
+
changed_truncated: r.changedTruncated,
|
|
545
|
+
...stream('stdout', r.stdout),
|
|
546
|
+
stdout_truncated: r.stdoutTruncated,
|
|
547
|
+
...stream('stderr', r.stderr),
|
|
548
|
+
stderr_truncated: r.stderrTruncated,
|
|
549
|
+
replayed: r.replayed,
|
|
550
|
+
timings: r.timings,
|
|
551
|
+
error: r.error,
|
|
552
|
+
created_at: r.createdAt,
|
|
553
|
+
finished_at: r.finishedAt,
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
// ---- Account plane (0.5.0) ----------------------------------------------------------------------------------------
|
|
557
|
+
/** "*" for the row that is the saved default, else "" (a narrow first column). */
|
|
558
|
+
const mark = (on) => (on ? '*' : '');
|
|
559
|
+
export function orgTable(rows, defaultId) {
|
|
560
|
+
return table(['', 'ID', 'SLUG', 'NAME', 'ROLE', 'CREATED'], rows.map((o) => [mark(o.id === defaultId), o.id, o.slug, o.name, o.role ?? '-', time(o.created_at)]));
|
|
561
|
+
}
|
|
562
|
+
export function projectTable(rows, defaultId) {
|
|
563
|
+
return table(['', 'ID', 'SLUG', 'NAME', 'CREATED'], rows.map((p) => [mark(p.id === defaultId), p.id, p.slug, p.name, time(p.created_at)]));
|
|
564
|
+
}
|
|
565
|
+
/** active, expired or revoked (as the web app shows it). */
|
|
566
|
+
export function keyStatus(k, now = Date.now()) {
|
|
567
|
+
if (k.revoked_at)
|
|
568
|
+
return 'revoked';
|
|
569
|
+
if (k.expires_at && Date.parse(k.expires_at) <= now)
|
|
570
|
+
return 'expired';
|
|
571
|
+
return 'active';
|
|
572
|
+
}
|
|
573
|
+
export function toolsText(tools) {
|
|
574
|
+
if (tools.length === 0)
|
|
575
|
+
return '(none: read-only)';
|
|
576
|
+
return tools.length === 6 ? 'all' : tools.join(',');
|
|
577
|
+
}
|
|
578
|
+
export function apiKeyTable(rows, savedId) {
|
|
579
|
+
return table(['', 'ID', 'KEY ID', 'NAME', 'TOOLS', 'STATUS', 'CREATED', 'EXPIRES', 'LAST USED'], rows.map((k) => [mark(k.id === savedId), k.id, k.key_id, k.name, toolsText(k.tool_permissions), keyStatus(k), time(k.created_at), time(k.expires_at), time(k.last_used_at)]));
|
|
580
|
+
}
|
|
581
|
+
export function memberTable(rows) {
|
|
582
|
+
return table(['USER', 'EMAIL', 'NAME', 'ROLE', 'JOINED'], rows.map((m) => [m.user_id, m.email, m.display_name ?? '-', m.role, time(m.joined_at)]));
|
|
583
|
+
}
|
|
584
|
+
export function invitationTable(rows) {
|
|
585
|
+
return table(['ID', 'EMAIL', 'ROLE', 'STATUS', 'EXPIRES', 'CREATED'], rows.map((i) => [i.id, i.email, i.role, i.status, time(i.expires_at), time(i.created_at)]));
|
|
586
|
+
}
|
|
587
|
+
export function authSessionTable(rows) {
|
|
588
|
+
return table(['', 'ID', 'CLIENT', 'CREATED', 'LAST SEEN', 'EXPIRES', 'IP', 'USER AGENT'], rows.map((s) => [s.current ? '*' : '', s.id, s.client, time(s.created_at), time(s.last_seen_at), time(s.absolute_expires_at), s.client_ip ?? '-', s.user_agent ? (s.user_agent.length > 60 ? `${s.user_agent.slice(0, 59)}…` : s.user_agent) : '-']));
|
|
589
|
+
}
|
|
590
|
+
/** 900 + "usd" -> "9.00 USD" (minor units; zero-decimal currencies shown as whole numbers). */
|
|
591
|
+
/**
|
|
592
|
+
* A usage time series (GET .../usage or /v1/workspaces/{id}/usage): the range and freshness, then one row per bucket
|
|
593
|
+
* and meter. `FINAL` is no while the bucket can still change (the ledger finalizes hours after they end).
|
|
594
|
+
*/
|
|
595
|
+
export function usageSeriesText(series) {
|
|
596
|
+
if (!isObject(series))
|
|
597
|
+
return json(series);
|
|
598
|
+
const s = series;
|
|
599
|
+
const head = [];
|
|
600
|
+
if (s.workspace_id)
|
|
601
|
+
head.push(['workspace', scalar(s.workspace_id)]);
|
|
602
|
+
head.push(['range', `${time(scalar(s.from))} .. ${time(scalar(s.to))}${s.granularity ? ` (by ${scalar(s.granularity)})` : ''}`]);
|
|
603
|
+
const m = isObject(s.measurement) ? s.measurement : {};
|
|
604
|
+
if (m.measured_through !== undefined)
|
|
605
|
+
head.push(['measured through', time(m.measured_through)]);
|
|
606
|
+
const data = Array.isArray(s.data) ? s.data : [];
|
|
607
|
+
const rows = data.map((r) => (isObject(r) ? { ...r, start: time(r.start), finalized: r.finalized === true ? 'yes' : 'no' } : r));
|
|
608
|
+
const table = objectTable(rows, [
|
|
609
|
+
['start', 'START'],
|
|
610
|
+
['meter', 'METER'],
|
|
611
|
+
['raw_quantity', 'RAW'],
|
|
612
|
+
['billable_quantity', 'BILLABLE'],
|
|
613
|
+
['finalized', 'FINAL'],
|
|
614
|
+
]);
|
|
615
|
+
return `${kv(head)}\n${table || 'no usage in this range\n'}`;
|
|
616
|
+
}
|
|
617
|
+
/** The period estimate (GET .../usage/estimate): the bill so far and where each allowance is heading. */
|
|
618
|
+
export function usageEstimateText(estimate) {
|
|
619
|
+
if (!isObject(estimate))
|
|
620
|
+
return json(estimate);
|
|
621
|
+
const e = estimate;
|
|
622
|
+
const currency = typeof e.currency === 'string' ? e.currency : 'USD';
|
|
623
|
+
const amount = (v) => (typeof v === 'number' ? money(v, currency) : '-');
|
|
624
|
+
const fee = isObject(e.subscription_fee) ? e.subscription_fee.amount_minor : e.subscription_fee;
|
|
625
|
+
const head = [];
|
|
626
|
+
if (isObject(e.plan))
|
|
627
|
+
head.push(['plan', scalar(e.plan.plan_key)]);
|
|
628
|
+
if (isObject(e.period))
|
|
629
|
+
head.push(['period', `${time(scalar(e.period.start))} .. ${time(scalar(e.period.end))}`]);
|
|
630
|
+
head.push(['subscription', amount(fee)], ['usage charges', amount(e.usage_charges_minor)], ['estimated total', amount(e.estimated_total_minor)]);
|
|
631
|
+
if (typeof e.overage === 'string')
|
|
632
|
+
head.push(['overage', e.overage]);
|
|
633
|
+
let out = kv(head);
|
|
634
|
+
const projections = objectTable(e.projections, [
|
|
635
|
+
['allowance', 'ALLOWANCE'],
|
|
636
|
+
['unit', 'UNIT'],
|
|
637
|
+
['included', 'INCLUDED'],
|
|
638
|
+
['used', 'USED'],
|
|
639
|
+
['projected_at_period_end', 'PROJECTED'],
|
|
640
|
+
['projected_exhaustion_at', 'RUNS OUT'],
|
|
641
|
+
['enforcement', 'ENFORCEMENT'],
|
|
642
|
+
]);
|
|
643
|
+
if (projections)
|
|
644
|
+
out += `\n${projections}`;
|
|
645
|
+
const notes = Array.isArray(e.notes) ? e.notes.filter((n) => typeof n === 'string') : [];
|
|
646
|
+
if (notes.length)
|
|
647
|
+
out += `\n${notes.map((n) => `note: ${n}`).join('\n')}\n`;
|
|
648
|
+
return out;
|
|
649
|
+
}
|
|
650
|
+
export function money(amountMinor, currency) {
|
|
651
|
+
const zeroDecimal = new Set(['jpy', 'krw', 'vnd', 'clp', 'isk', 'ugx', 'xaf', 'xof']);
|
|
652
|
+
const c = currency.toLowerCase();
|
|
653
|
+
return `${zeroDecimal.has(c) ? String(amountMinor) : (amountMinor / 100).toFixed(2)} ${c.toUpperCase()}`;
|
|
654
|
+
}
|
|
655
|
+
export function planTable(catalog) {
|
|
656
|
+
const rows = catalog.plans.map((p) => {
|
|
657
|
+
const prices = p.prices.length ? p.prices.map((x) => `${money(x.amount_minor, x.currency)}/${x.interval}`).join(', ') : 'free';
|
|
658
|
+
const buy = p.prices.some((x) => x.purchasable) ? 'yes' : '-';
|
|
659
|
+
return [p.plan_key, p.name, prices, buy];
|
|
660
|
+
});
|
|
661
|
+
return `${table(['PLAN', 'NAME', 'PRICE', 'PURCHASABLE'], rows)}catalog ${catalog.catalog_version} (${catalog.mode})\n`;
|
|
662
|
+
}
|
|
663
|
+
export function billingStatusText(s, org) {
|
|
664
|
+
const sub = s.subscription;
|
|
665
|
+
const pending = s.pending_checkout;
|
|
666
|
+
return kv([
|
|
667
|
+
['organization', `${org.name} (${org.slug})`],
|
|
668
|
+
['plan', s.plan ? `${s.plan.plan_key} (${s.plan.source}, catalog ${s.plan.catalog_version})` : '-'],
|
|
669
|
+
['billing state', s.billing_state],
|
|
670
|
+
['subscription', sub ? `${sub.status}${sub.plan_key ? ` ${sub.plan_key}` : ''}${sub.cancel_at_period_end ? ', cancels at period end' : ''}` : 'none'],
|
|
671
|
+
['period end', sub ? time(sub.current_period_end) : '-'],
|
|
672
|
+
...(sub?.grace_until ? [['grace until', time(sub.grace_until)]] : []),
|
|
673
|
+
['pending checkout', pending ? `${pending.id} ${pending.plan_key ?? '-'} ${pending.status}${pending.expires_at ? ` (expires ${time(pending.expires_at)})` : ''}` : '-'],
|
|
674
|
+
['purchase', s.purchase_available ? 'available (shard billing upgrade <plan>)' : 'not available on this API'],
|
|
675
|
+
]);
|
|
676
|
+
}
|
|
677
|
+
export function invoiceTable(rows) {
|
|
678
|
+
return table(['NUMBER', 'STATUS', 'TOTAL', 'PAID', 'DUE', 'PERIOD', 'CREATED', 'URL'], rows.map((i) => [
|
|
679
|
+
i.number ?? i.id,
|
|
680
|
+
i.status ?? '-',
|
|
681
|
+
money(i.total, i.currency),
|
|
682
|
+
money(i.amount_paid, i.currency),
|
|
683
|
+
money(i.amount_remaining, i.currency),
|
|
684
|
+
i.period_start || i.period_end ? `${time(i.period_start).slice(0, 10)}..${time(i.period_end).slice(0, 10)}` : '-',
|
|
685
|
+
time(i.created_at),
|
|
686
|
+
i.hosted_invoice_url ?? '-',
|
|
687
|
+
]));
|
|
688
|
+
}
|
|
689
|
+
export function auditTable(rows) {
|
|
690
|
+
return table(['TIME', 'ACTION', 'OUTCOME', 'ACTOR', 'TARGET', 'REQUEST'], rows.map((e) => [time(e.occurred_at), e.action, e.outcome, `${e.actor.type}${e.actor.id ? `:${e.actor.id}` : ''}`, e.target ? `${e.target.type}${e.target.id ? `:${e.target.id}` : ''}` : '-', e.request_id ?? '-']));
|
|
691
|
+
}
|
|
692
|
+
export function dataExportText(e, out) {
|
|
693
|
+
return kv([
|
|
694
|
+
['export', `${e.kind} ${e.id}`],
|
|
695
|
+
['written to', `${out} (${e.size_bytes} bytes, sha256 ${e.sha256})`],
|
|
696
|
+
['created', time(e.created_at)],
|
|
697
|
+
['expires', `${time(e.expires_at)} (the server copy; download it again before then with the same command)`],
|
|
698
|
+
// The API's entries are sentences ("Workspace file contents: ... ."): run them together as sentences.
|
|
699
|
+
...(e.not_included.length ? [['not included', e.not_included.map((x) => x.trim()).join(' ')]] : []),
|
|
700
|
+
]);
|
|
701
|
+
}
|
|
702
|
+
export function volumeTable(rows) {
|
|
703
|
+
return table(['ID', 'NAME', 'STATE', 'QUOTA', 'SHARED', 'ATTACHED', 'CREATED'], rows.map((v) => [v.id, v.name, `${v.state}${v.deleted_at ? ' (deleted)' : ''}`, `${v.quota_gib} GiB`, v.org_shared ? 'organization' : 'project', String(v.live_attachments), time(v.created_at)]));
|
|
704
|
+
}
|
|
705
|
+
export function volumeDetail(v) {
|
|
706
|
+
const err = v.error;
|
|
707
|
+
return kv([
|
|
708
|
+
['id', v.id],
|
|
709
|
+
['name', v.name],
|
|
710
|
+
['state', `${v.state}${v.state_reason ? ` (${v.state_reason})` : ''}`],
|
|
711
|
+
['quota', `${v.quota_gib} GiB`],
|
|
712
|
+
['shared', v.org_shared ? 'every project of the organization' : `project ${v.project_id}`],
|
|
713
|
+
['attachments', String(v.live_attachments)],
|
|
714
|
+
['usage', pairs(v.usage)],
|
|
715
|
+
['operation', v.active_operation ? `${operationSummary(v.active_operation)} ${v.active_operation.id}` : '-'],
|
|
716
|
+
['error', err ? `${scalar(err.code)}${err.message ? `: ${scalar(err.message)}` : ''}` : '-'],
|
|
717
|
+
['created', time(v.created_at)],
|
|
718
|
+
['deleted', time(v.deleted_at)],
|
|
719
|
+
]);
|
|
720
|
+
}
|
|
721
|
+
export function attachmentTable(rows) {
|
|
722
|
+
return table(['ID', 'WORKSPACE', 'VOLUME', 'MOUNT', 'MODE', 'STATE', 'MOUNTED', 'ATTACHED'], rows.map((a) => [a.id, a.workspace_id, a.volume_id, a.mount_path, a.mode, a.state, a.mounted ? 'yes' : 'no', time(a.attached_at)]));
|
|
723
|
+
}
|
|
724
|
+
function egressPolicyLine(p) {
|
|
725
|
+
if (!p)
|
|
726
|
+
return '-';
|
|
727
|
+
if (p.mode !== 'allowlist')
|
|
728
|
+
return p.mode;
|
|
729
|
+
const rules = p.rules.map((r) => `${r.host}:${r.ports.join('/')}`);
|
|
730
|
+
return `allowlist ${[...rules, ...p.cidrs].join(', ') || '(empty: nothing allowed)'}`;
|
|
731
|
+
}
|
|
732
|
+
export function projectEgressText(p) {
|
|
733
|
+
const pr = p.propagation;
|
|
734
|
+
return kv([
|
|
735
|
+
['project', p.project_id],
|
|
736
|
+
['version', String(p.version)],
|
|
737
|
+
['policy', p.policy ? `${egressPolicyLine({ mode: p.policy.mode ?? 'allow_all', rules: p.policy.rules, cidrs: p.policy.cidrs })} (v${p.policy.version})` : 'none (the platform default applies)'],
|
|
738
|
+
['effective', `${egressPolicyLine(p.effective)} (${p.effective.source})`],
|
|
739
|
+
['override', p.organization_override ? `${p.organization_override.mode}: ${p.organization_override.reason} until ${time(p.organization_override.lifts_at)}` : '-'],
|
|
740
|
+
['workspaces', `${pr.workspaces}: ${pr.enforced} enforced, ${pr.pending} pending, ${pr.failed} failed, ${pr.not_enforced} not enforced${pr.truncated ? ' (truncated)' : ''}`],
|
|
741
|
+
]);
|
|
742
|
+
}
|
|
743
|
+
export function workspaceEgressText(w) {
|
|
744
|
+
const e = w.enforcement;
|
|
745
|
+
return kv([
|
|
746
|
+
['workspace', w.workspace_id],
|
|
747
|
+
['version', String(w.version)],
|
|
748
|
+
['override', w.override ? `${egressPolicyLine(w.override.kind === 'cleared' ? null : { mode: w.override.mode ?? 'allow_all', rules: w.override.rules, cidrs: w.override.cidrs })} (v${w.override.version}${w.override.kind === 'cleared' ? ', cleared' : ''})` : 'none'],
|
|
749
|
+
['project policy', w.project_policy ? `${egressPolicyLine({ mode: w.project_policy.mode ?? 'allow_all', rules: w.project_policy.rules, cidrs: w.project_policy.cidrs })} (v${w.project_policy.version})` : 'none'],
|
|
750
|
+
['template ceiling', w.template_egress ? `${w.template_egress.mode}${w.template_egress.allow_hosts.length ? ` ${w.template_egress.allow_hosts.join(', ')}` : ''}` : '-'],
|
|
751
|
+
['effective', `${egressPolicyLine(w.effective_policy)} (${w.effective.source})`],
|
|
752
|
+
['enforcement', `${e.state}${e.reason ? ` (${e.reason})` : ''}; applied v${e.applied_version ?? '-'} of v${e.effective_version ?? '-'}`],
|
|
753
|
+
]);
|
|
754
|
+
}
|
|
755
|
+
export function egressVersionTable(rows) {
|
|
756
|
+
return table(['VERSION', 'KIND', 'POLICY', 'BY', 'CREATED'], rows.map((v) => [String(v.version), v.kind, v.kind === 'cleared' ? '-' : egressPolicyLine({ mode: v.mode ?? 'allow_all', rules: v.rules, cidrs: v.cidrs }), `${v.created_by.type}:${v.created_by.id}`, time(v.created_at)]));
|
|
757
|
+
}
|
package/dist/handlers.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every command's handler, keyed by its path ("workspaces open", "auth login"): the project-key commands of
|
|
3
|
+
* commands.ts and the account plane of account-commands.ts (0.5.0). args.test.ts checks that it matches COMMANDS.
|
|
4
|
+
*/
|
|
5
|
+
import { ACCOUNT_HANDLERS } from "./account-commands.js";
|
|
6
|
+
import { KEY_HANDLERS } from "./commands.js";
|
|
7
|
+
export const HANDLERS = { ...KEY_HANDLERS, ...ACCOUNT_HANDLERS };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @shardflux/cli — `shard`, the Shardflux command line on @shardflux/sdk.
|
|
3
|
-
* The executable is src/bin.ts; `run()` drives the same code in-process.
|
|
3
|
+
* The executable is src/bin.ts (`main()`); `run()` drives the same code in-process. A package that embeds the CLI
|
|
4
|
+
* (the `shardflux` npm bundle) calls `main({ distribution: { package, version } })` so the version check names it.
|
|
4
5
|
*/
|
|
5
|
-
export { run, abortableSleep } from './main.js';
|
|
6
|
-
export {
|
|
6
|
+
export { run, main, abortableSleep } from './main.js';
|
|
7
|
+
export type { MainOptions, RunOptions } from './main.js';
|
|
8
|
+
export { COMMANDS, GLOBAL_OPTIONS, CLI_VERSION, HELP_SECTIONS, commandHelp, findCommand, groupHelp, normalizeWords, parseCommandLine, parseDuration, parseEnvPairs, parsePositiveInt, resolveApiUrl, resolveWake, resolveWakeTimeout, topHelp } from './args.js';
|
|
7
9
|
export type { CommandSpec, OptionSpec, Parsed, PositionalSpec, Values } from './args.js';
|
|
8
|
-
export { HANDLERS
|
|
9
|
-
export
|
|
10
|
-
export {
|
|
10
|
+
export { HANDLERS } from './handlers.js';
|
|
11
|
+
export { KEY_HANDLERS, execExitCode, resolveWorkspace } from './commands.js';
|
|
12
|
+
export type { Ctx, Handler, Io, ResolvedApiKey, Writer } from './commands.js';
|
|
13
|
+
export { ACCOUNT_HANDLERS, DEFAULT_API_URL, defaultOrgName, exportLine, parseTime } from './account-commands.js';
|
|
14
|
+
export type { ShellKind } from './account-commands.js';
|
|
15
|
+
export { CREDENTIALS_FILE, CredentialStore, CredentialsFileError, configDir, ensurePrivateDir, parseCredentials, writePrivateFile } from './credentials.js';
|
|
16
|
+
export type { CredentialsDoc, Profile, StoredApiKey, StoredSession, StoredUser } from './credentials.js';
|
|
17
|
+
export { Input } from './input.js';
|
|
18
|
+
export type { StdinLike } from './input.js';
|
|
19
|
+
export { CHECK_INTERVAL_MS, CLI_DISTRIBUTION, NOTICE_WAIT_MS, UPDATE_CHECK_FILE, UpdateCheck, noticeOf, statusFromEntry, updateCheckDisabled, updateView } from './update-check.js';
|
|
20
|
+
export type { Distribution, UpdateView } from './update-check.js';
|
|
21
|
+
export { openUrl, openerFor } from './browser.js';
|
|
22
|
+
export { EXIT, AuthConfigError, CheckoutEndedError, ExecutionFailedError, ExecutionPendingError, InterruptedError, MfaPendingError, NotFoundError, UsageError, describeError, exitCodeFor, redact } from './errors.js';
|
|
11
23
|
export type { ErrorInfo } from './errors.js';
|
|
12
|
-
export { errorText, json, table, time, usageText, workspaceDetail, workspaceTable } from './format.js';
|
|
24
|
+
export { changedText, errorText, executionJson, executionSummary, feedbackCommand, json, table, time, usageText, workspaceDetail, workspaceTable } from './format.js';
|
|
13
25
|
export { TimingRecorder, timingJson, timingText } from './timing.js';
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @shardflux/cli — `shard`, the Shardflux command line on @shardflux/sdk.
|
|
3
|
-
* The executable is src/bin.ts; `run()` drives the same code in-process.
|
|
3
|
+
* The executable is src/bin.ts (`main()`); `run()` drives the same code in-process. A package that embeds the CLI
|
|
4
|
+
* (the `shardflux` npm bundle) calls `main({ distribution: { package, version } })` so the version check names it.
|
|
4
5
|
*/
|
|
5
|
-
export { run, abortableSleep } from "./main.js";
|
|
6
|
-
export { COMMANDS, GLOBAL_OPTIONS, CLI_VERSION, commandHelp, findCommand, normalizeWords, parseCommandLine, parseDuration, parseEnvPairs, parsePositiveInt, resolveApiUrl, resolveWake, resolveWakeTimeout, topHelp } from "./args.js";
|
|
7
|
-
export { HANDLERS
|
|
8
|
-
export {
|
|
9
|
-
export {
|
|
6
|
+
export { run, main, abortableSleep } from "./main.js";
|
|
7
|
+
export { COMMANDS, GLOBAL_OPTIONS, CLI_VERSION, HELP_SECTIONS, commandHelp, findCommand, groupHelp, normalizeWords, parseCommandLine, parseDuration, parseEnvPairs, parsePositiveInt, resolveApiUrl, resolveWake, resolveWakeTimeout, topHelp } from "./args.js";
|
|
8
|
+
export { HANDLERS } from "./handlers.js";
|
|
9
|
+
export { KEY_HANDLERS, execExitCode, resolveWorkspace } from "./commands.js";
|
|
10
|
+
export { ACCOUNT_HANDLERS, DEFAULT_API_URL, defaultOrgName, exportLine, parseTime } from "./account-commands.js";
|
|
11
|
+
export { CREDENTIALS_FILE, CredentialStore, CredentialsFileError, configDir, ensurePrivateDir, parseCredentials, writePrivateFile } from "./credentials.js";
|
|
12
|
+
export { Input } from "./input.js";
|
|
13
|
+
export { CHECK_INTERVAL_MS, CLI_DISTRIBUTION, NOTICE_WAIT_MS, UPDATE_CHECK_FILE, UpdateCheck, noticeOf, statusFromEntry, updateCheckDisabled, updateView } from "./update-check.js";
|
|
14
|
+
export { openUrl, openerFor } from "./browser.js";
|
|
15
|
+
export { EXIT, AuthConfigError, CheckoutEndedError, ExecutionFailedError, ExecutionPendingError, InterruptedError, MfaPendingError, NotFoundError, UsageError, describeError, exitCodeFor, redact } from "./errors.js";
|
|
16
|
+
export { changedText, errorText, executionJson, executionSummary, feedbackCommand, json, table, time, usageText, workspaceDetail, workspaceTable } from "./format.js";
|
|
10
17
|
export { TimingRecorder, timingJson, timingText } from "./timing.js";
|
package/dist/input.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface StdinLike extends AsyncIterable<Buffer | string> {
|
|
2
|
+
isTTY?: boolean;
|
|
3
|
+
/** tty.ReadStream#setRawMode: needed to read a secret on a terminal without echoing it. */
|
|
4
|
+
setRawMode?: (raw: boolean) => unknown;
|
|
5
|
+
}
|
|
6
|
+
export interface ErrWriter {
|
|
7
|
+
write(chunk: string | Uint8Array): boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare class Input {
|
|
10
|
+
#private;
|
|
11
|
+
constructor(stdin: StdinLike, stderr: ErrWriter);
|
|
12
|
+
/** True when standard input is a terminal `shard` can prompt on. */
|
|
13
|
+
get interactive(): boolean;
|
|
14
|
+
/** The next line of piped input without its line ending, or null at the end of input. */
|
|
15
|
+
line(): Promise<string | null>;
|
|
16
|
+
/**
|
|
17
|
+
* A secret: a no-echo prompt on a terminal, else the next piped line. `what` names it in errors ("the password").
|
|
18
|
+
* An empty secret is refused.
|
|
19
|
+
*/
|
|
20
|
+
secret(prompt: string, what: string, pipeHint: string): Promise<string>;
|
|
21
|
+
/** A new password: asked twice (and compared) on a terminal, one piped line otherwise. */
|
|
22
|
+
newPassword(prompt: string, what: string, pipeHint: string): Promise<string>;
|
|
23
|
+
/** A visible answer to a question on a terminal (e.g. an authenticator code), else the next piped line. */
|
|
24
|
+
answer(prompt: string): Promise<string | null>;
|
|
25
|
+
}
|