@shardflux/cli 0.6.1 → 0.7.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 +79 -24
- package/README.md +105 -67
- package/dist/account-args.js +1 -1
- package/dist/account-commands.js +10 -3
- package/dist/args.d.ts +1 -1
- package/dist/args.js +45 -22
- package/dist/browser.js +1 -1
- package/dist/commands.d.ts +2 -2
- package/dist/commands.js +81 -17
- package/dist/errors.d.ts +3 -3
- package/dist/errors.js +16 -9
- package/dist/format.d.ts +22 -10
- package/dist/format.js +64 -13
- package/dist/http.d.ts +1 -17
- package/dist/http.js +6 -24
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/main.js +21 -21
- package/dist/template-yaml.js +1 -1
- package/dist/timing.d.ts +12 -0
- package/dist/timing.js +46 -0
- package/dist/update-check.js +1 -1
- package/package.json +2 -2
package/dist/args.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import { parseArgs } from 'node:util';
|
|
13
13
|
import { ACCOUNT_COMMANDS } from "./account-args.js";
|
|
14
14
|
import { UsageError } from "./errors.js";
|
|
15
|
-
export const CLI_VERSION = '0.
|
|
15
|
+
export const CLI_VERSION = '0.7.0';
|
|
16
16
|
export const GLOBAL_OPTIONS = {
|
|
17
17
|
'api-url': { type: 'string', value: '<url>', description: 'API base URL (default $SHARDFLUX_API_URL, else https://api.shardflux.dev).' },
|
|
18
18
|
json: { type: 'boolean', description: 'Print machine-readable JSON on stdout (errors as JSON on stderr).' },
|
|
@@ -38,7 +38,7 @@ const TIMING = {
|
|
|
38
38
|
const WAKE_TIMING = {
|
|
39
39
|
timing: { type: 'boolean', description: 'Print where the time went when the command had to wake the workspace (stderr; with --json a "timing" field, null without a wake).' },
|
|
40
40
|
};
|
|
41
|
-
/** `--if-revision` (0.5.0) of the mutating files commands: the tree revision a file-first write applies to
|
|
41
|
+
/** `--if-revision` (0.5.0) of the mutating files commands: the tree revision a file-first write applies to. */
|
|
42
42
|
const IF_REVISION = {
|
|
43
43
|
'if-revision': {
|
|
44
44
|
type: 'string',
|
|
@@ -58,7 +58,7 @@ const PAGE = {
|
|
|
58
58
|
limit: { type: 'string', value: '<n>', description: 'Page size.' },
|
|
59
59
|
cursor: { type: 'string', value: '<cursor>', description: 'Continue from a previous page.' },
|
|
60
60
|
};
|
|
61
|
-
/** Options of a version produced by save-as-template or a draft publish
|
|
61
|
+
/** Options of a version produced by save-as-template or a draft publish. */
|
|
62
62
|
const SAVE = {
|
|
63
63
|
description: { type: 'string', value: '<text>', description: 'Version description (default: the source version’s).' },
|
|
64
64
|
'default-lifetime': { type: 'string', value: '<persistent|session>', description: 'Default lifetime of workspaces opened from the new version.' },
|
|
@@ -68,7 +68,7 @@ const SAVE = {
|
|
|
68
68
|
wait: { type: 'boolean', description: 'Wait until the build is published and registered (or fails).' },
|
|
69
69
|
timeout: { type: 'string', value: '<duration>', description: 'Give up waiting after this long (default 30m). The build continues server side.' },
|
|
70
70
|
};
|
|
71
|
-
/** Open-time text inputs of a template version (0.4.0
|
|
71
|
+
/** Open-time text inputs of a template version (0.4.0). */
|
|
72
72
|
const INPUTS = {
|
|
73
73
|
input: {
|
|
74
74
|
type: 'string',
|
|
@@ -93,12 +93,12 @@ const FEEDBACK_HELP = [
|
|
|
93
93
|
* MCP server's send_feedback tool. It has to make an agent actually send feedback while it works.
|
|
94
94
|
*/
|
|
95
95
|
const FEEDBACK_WHY = [
|
|
96
|
-
'Sends your message straight to the Shardflux
|
|
96
|
+
'Sends your message straight to the Shardflux team, who read every one. Agents: use it actively while you work,',
|
|
97
97
|
'not once at the end. Send a short note the moment:',
|
|
98
98
|
'- something fails unexpectedly, an error or doc is unclear, an option is missing, or you needed a workaround;',
|
|
99
|
-
'- your user
|
|
100
|
-
'
|
|
101
|
-
'- your user
|
|
99
|
+
'- your user asks for a capability, an option or a smoother workflow: paraphrase it, with what they were trying',
|
|
100
|
+
' to do;',
|
|
101
|
+
'- your user has an idea for how Shardflux could fit their work better.',
|
|
102
102
|
'Short and frequent beats polished; include the request id and error code (a failed shard command prints a',
|
|
103
103
|
'ready-made "feedback:" line to fill in). No need to ask first, but tell your user you sent it. Leave out private',
|
|
104
104
|
'data about the user and secrets or code they did not mean to share; paraphrase, never paste transcripts.',
|
|
@@ -138,7 +138,7 @@ const KEY_COMMANDS = [
|
|
|
138
138
|
},
|
|
139
139
|
{
|
|
140
140
|
path: ['feedback'],
|
|
141
|
-
summary: 'Tell the Shardflux
|
|
141
|
+
summary: 'Tell the Shardflux team what failed, confused you or is missing (agents: as you work)',
|
|
142
142
|
description: [
|
|
143
143
|
...FEEDBACK_WHY,
|
|
144
144
|
'',
|
|
@@ -154,15 +154,14 @@ const KEY_COMMANDS = [
|
|
|
154
154
|
options: {
|
|
155
155
|
category: { type: 'string', value: '<bug|confusing|missing|idea|praise|other>', description: 'Kind of feedback (default other; see Categories).' },
|
|
156
156
|
workspace: { type: 'string', value: '<id|key>', description: 'The workspace it is about.' },
|
|
157
|
-
'request-id': { type: 'string', value: '<id>', description: 'request_id from the error, so the
|
|
158
|
-
'error-code': { type: 'string', value: '<code>', description: 'The error code you saw, e.g.
|
|
157
|
+
'request-id': { type: 'string', value: '<id>', description: 'request_id from the error, so the team can find the logs.' },
|
|
158
|
+
'error-code': { type: 'string', value: '<code>', description: 'The error code you saw, e.g. template_not_found.' },
|
|
159
159
|
command: { type: 'string', value: '<text>', description: 'What you ran, e.g. "shard ws exec acme/demo -- npm test".' },
|
|
160
160
|
'from-file': { type: 'string', value: '<file|->', description: 'Read the message from this file, or - for standard input (long reports, logs); instead of <message...>.' },
|
|
161
161
|
},
|
|
162
162
|
examples: [
|
|
163
|
-
'shard feedback --category bug --request-id req-5c7744b2bb5c457b86b76101 --error-code
|
|
164
|
-
'shard feedback --category
|
|
165
|
-
'shard feedback --category missing no way to copy a file between two workspaces, piped files read into files write instead',
|
|
163
|
+
'shard feedback --category bug --request-id req-5c7744b2bb5c457b86b76101 --error-code template_not_found "ws open --template python-node failed; expected a suggestion of the closest slug"',
|
|
164
|
+
'shard feedback --category idea "user would like shard ws open to remember the last template they used"',
|
|
166
165
|
'npm test 2>&1 | tail -n 60 | shard feedback --category bug --workspace acme/demo --from-file -',
|
|
167
166
|
],
|
|
168
167
|
},
|
|
@@ -190,6 +189,8 @@ const KEY_COMMANDS = [
|
|
|
190
189
|
positionals: [{ name: 'key', required: true }],
|
|
191
190
|
options: {
|
|
192
191
|
template: { type: 'string', value: '<slug>', description: 'Template slug (required), e.g. python-node-browser.' },
|
|
192
|
+
labels: { type: 'string', value: '<json>', description: 'Replace workspace labels with a JSON object of strings.' },
|
|
193
|
+
'idle-policy': { type: 'string', value: '<policy>', description: 'adaptive, never or fixed:<seconds>.' },
|
|
193
194
|
secret: {
|
|
194
195
|
type: 'string',
|
|
195
196
|
multiple: true,
|
|
@@ -229,6 +230,7 @@ const KEY_COMMANDS = [
|
|
|
229
230
|
options: {
|
|
230
231
|
state: { type: 'string', value: '<state>', description: 'Observed state filter (running, suspended, creating, ...).' },
|
|
231
232
|
prefix: { type: 'string', value: '<key-prefix>', description: 'Only keys starting with this prefix.' },
|
|
233
|
+
labels: { type: 'string', value: '<json>', description: 'Exact label matches (JSON object; all pairs must match).' },
|
|
232
234
|
...LIFETIME_FILTER,
|
|
233
235
|
'include-deleted': { type: 'boolean', description: 'Include deleted workspaces and ended sessions (tombstones).' },
|
|
234
236
|
all: { type: 'boolean', description: 'Fetch every page.' },
|
|
@@ -236,12 +238,28 @@ const KEY_COMMANDS = [
|
|
|
236
238
|
cursor: { type: 'string', value: '<cursor>', description: 'Continue from a previous page.' },
|
|
237
239
|
},
|
|
238
240
|
},
|
|
241
|
+
{ path: ['workspaces', 'labels'], summary: 'Read or replace workspace labels', positionals: [REF], options: {
|
|
242
|
+
set: { type: 'string', value: '<json>', description: 'Replace all labels; {} clears them.' },
|
|
243
|
+
} },
|
|
244
|
+
{ path: ['workspaces', 'idle'], summary: 'Inspect idle status, set policy or declare a keepalive', positionals: [REF], options: {
|
|
245
|
+
policy: { type: 'string', value: '<policy>', description: 'adaptive, never, fixed:<seconds>, or default to clear the override.' },
|
|
246
|
+
keepalive: { type: 'string', value: '<seconds>', description: 'Declare ongoing work for this many seconds.' },
|
|
247
|
+
} },
|
|
248
|
+
{ path: ['workspaces', 'exec-start'], summary: 'Start a background exec session with optional pipe stdin', positionals: [REF, { name: 'command', required: true, variadic: true }], options: {
|
|
249
|
+
'stdin-open': { type: 'boolean', description: 'Keep pipe stdin open for exec-input.' },
|
|
250
|
+
'session-id': { type: 'string', value: '<id>', description: 'Caller-chosen id to deduplicate starts.' },
|
|
251
|
+
cwd: { type: 'string', value: '<dir>', description: 'Absolute working directory.' },
|
|
252
|
+
} },
|
|
253
|
+
{ path: ['workspaces', 'exec-input'], summary: 'Write pipe stdin at its acknowledged byte offset', positionals: [REF, { name: 'session-id', required: true }, { name: 'data' }], options: {
|
|
254
|
+
offset: { type: 'string', value: '<bytes>', description: 'Required acknowledged offset (0 initially).' },
|
|
255
|
+
close: { type: 'boolean', description: 'Send EOF after these bytes are accepted.' },
|
|
256
|
+
} },
|
|
239
257
|
{ path: ['workspaces', 'get'], summary: 'Show one workspace (state, template, startup)', positionals: [REF], options: {} },
|
|
240
258
|
{ path: ['workspaces', 'inputs'], summary: 'Show the text inputs of a workspace (secret inputs are bound secrets: shard ws secrets)', positionals: [REF], options: {} },
|
|
241
259
|
{
|
|
242
260
|
path: ['workspaces', 'exec'],
|
|
243
261
|
summary: 'Run a command in a workspace; prints its output and exits with its exit code',
|
|
244
|
-
description: 'Runs argv directly (no shell; use `-- bash -lc "..."` for shell syntax). Processful workspaces: output is streamed as it arrives and resumes from byte offsets after dropped connections; Ctrl-C stops waiting and cancels the command. File-first workspaces: the command runs as an execution in a fresh VM on the latest tree revision; its output is printed when it ended, and the files it changed under /home/user become the next tree revision (-v lists them). An execution
|
|
262
|
+
description: 'Runs argv directly (no shell; use `-- bash -lc "..."` for shell syntax). Processful workspaces: output is streamed as it arrives and resumes from byte offsets after dropped connections; Ctrl-C stops waiting and cancels the command. File-first workspaces: the command runs as an execution in a fresh VM on the latest tree revision; its output is printed when it ended, and the files it changed under /home/user become the next tree revision (-v lists them). An execution runs to completion: Ctrl-C stops waiting and names the command that fetches its result later. A failed or lost execution (nothing published) exits 6, as does a command that could not start (a --cwd that is not a directory, a program not on PATH), with the workspace’s reason.',
|
|
245
263
|
positionals: [REF, { name: 'command', required: true, variadic: true }],
|
|
246
264
|
options: {
|
|
247
265
|
cwd: { type: 'string', value: '<dir>', description: 'Working directory, an absolute path (default /home/user; under /home/user on a file-first workspace). A relative path is refused (invalid_cwd).' },
|
|
@@ -270,15 +288,16 @@ const KEY_COMMANDS = [
|
|
|
270
288
|
},
|
|
271
289
|
{
|
|
272
290
|
path: ['workspaces', 'suspend'],
|
|
273
|
-
summary: 'Suspend a running workspace (
|
|
274
|
-
description: 'Without options the suspend is requested now. --when-idle (0.5.1+) defers it instead: the workspace is suspended once it has been idle for that long, counted from the later of its last work and the request, so call it when an agent turn ends. A command still running, an attached exec or terminal stream, or a keepalive postpones it; the next tool call (the next turn) or a resume cancels it. Repeating replaces the pending request. --cancel-when-idle cancels it.',
|
|
291
|
+
summary: 'Suspend a running workspace (memory and processes checkpointed), now or once it has been idle',
|
|
292
|
+
description: 'Without options the suspend is requested now. With --wait it returns as soon as the workspace is sealed on its host, typically in a few hundred ms; the result\'s durable turns true when the copy lands in durable storage, typically within a second. --durable (0.7.0+) waits for that too. --when-idle (0.5.1+) defers it instead: the workspace is suspended once it has been idle for that long, counted from the later of its last work and the request, so call it when an agent turn ends. A command still running, an attached exec or terminal stream, or a keepalive postpones it; the next tool call (the next turn) or a resume cancels it. Repeating replaces the pending request. --cancel-when-idle cancels it.',
|
|
275
293
|
positionals: [REF],
|
|
276
294
|
options: {
|
|
277
295
|
'when-idle': { type: 'string', value: '<duration>', description: 'Suspend once the workspace has been idle this long (0.5.1+; 0 to 1h, e.g. 0, 30s, 2m; 0 = as soon as it is idle, 0.6.1+). Cannot be combined with --wait.' },
|
|
278
296
|
'cancel-when-idle': { type: 'boolean', description: 'Cancel a pending --when-idle request (0.5.1+; idempotent).' },
|
|
297
|
+
durable: { type: 'boolean', description: 'Wait until the suspend is in durable storage (result durable: true; 0.7.0+). Implies --wait and shares its --timeout.' },
|
|
279
298
|
...WAIT,
|
|
280
299
|
},
|
|
281
|
-
examples: ['shard ws suspend acme/demo --wait', 'shard ws suspend acme/demo --when-idle 2m', 'shard ws suspend acme/demo --cancel-when-idle'],
|
|
300
|
+
examples: ['shard ws suspend acme/demo --wait', 'shard ws suspend acme/demo --durable', 'shard ws suspend acme/demo --when-idle 2m', 'shard ws suspend acme/demo --cancel-when-idle'],
|
|
282
301
|
},
|
|
283
302
|
{ path: ['workspaces', 'resume'], summary: 'Resume a suspended workspace', positionals: [REF], options: { ...WAIT } },
|
|
284
303
|
{
|
|
@@ -519,7 +538,7 @@ const KEY_COMMANDS = [
|
|
|
519
538
|
{
|
|
520
539
|
path: ['templates', 'init'],
|
|
521
540
|
summary: 'Write a template.yaml that starts from a base template',
|
|
522
|
-
description: 'The file is the recipe v2 document
|
|
541
|
+
description: 'The file is the recipe v2 document: languages, packages, files (local "from" paths are uploaded by "shard templates build"), build steps, and the settings a workspace gets when it opens (env, inputs, start commands, services, defaults). Edit it, then build it.',
|
|
523
542
|
positionals: [],
|
|
524
543
|
options: {
|
|
525
544
|
base: { type: 'string', value: '<slug[@version]>', description: 'Base template (required), e.g. ubuntu-24.04; without @version, the version open resolves to now.' },
|
|
@@ -696,7 +715,11 @@ const KEY_COMMANDS = [
|
|
|
696
715
|
path: ['operations', 'wait'],
|
|
697
716
|
summary: 'Wait for an operation to finish (exit 0 succeeded, 6 failed/canceled, 5 timeout)',
|
|
698
717
|
positionals: [{ name: 'operation-id', required: true }],
|
|
699
|
-
options: {
|
|
718
|
+
options: {
|
|
719
|
+
timeout: { type: 'string', value: '<duration>', description: 'Give up after this long (default 5m).' },
|
|
720
|
+
durable: { type: 'boolean', description: 'A suspend or fork: also wait until its copy is in durable storage (result durable: true; 0.7.0+).' },
|
|
721
|
+
...TIMING,
|
|
722
|
+
},
|
|
700
723
|
},
|
|
701
724
|
];
|
|
702
725
|
/** Every command: the account plane first (sign in, set up), then the project-key commands. */
|
|
@@ -1058,8 +1081,8 @@ export function topHelp() {
|
|
|
1058
1081
|
'',
|
|
1059
1082
|
'Feedback: shard feedback [options] <message...>',
|
|
1060
1083
|
...FEEDBACK_WHY.map((l) => ` ${l}`),
|
|
1061
|
-
' shard feedback --category bug --request-id req-5c7744b2bb5c457b86b76101 "ws open
|
|
1062
|
-
' shard feedback --category
|
|
1084
|
+
' shard feedback --category bug --request-id req-5c7744b2bb5c457b86b76101 --error-code template_not_found "ws open --template python-node failed; expected a suggestion of the closest slug"',
|
|
1085
|
+
' shard feedback --category idea "user would like shard ws open to remember the last template they used"',
|
|
1063
1086
|
'',
|
|
1064
1087
|
'Start here (everything the web app does, from a terminal; a person only opens the emailed link and pays in Checkout):',
|
|
1065
1088
|
` printf '%s\\n' "$PASSWORD" | shard auth register --email you@example.com`,
|
package/dist/browser.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `--open` (0.5.0+): opens a URL in the default browser
|
|
2
|
+
* `--open` (0.5.0+): opens a URL in the default browser when it can. The URL is always printed as well, so a
|
|
3
3
|
* failure to open changes nothing: no error, no exit code. $BROWSER (a program run with the URL as its only argument)
|
|
4
4
|
* wins; otherwise `open` (macOS), `rundll32 url.dll,FileProtocolHandler` (Windows) or `xdg-open` (elsewhere).
|
|
5
5
|
*/
|
package/dist/commands.d.ts
CHANGED
|
@@ -70,13 +70,13 @@ export type Handler = (ctx: Ctx, values: Values, positionals: string[]) => Promi
|
|
|
70
70
|
export declare const str: (v: Values[string]) => string | undefined;
|
|
71
71
|
export declare const strs: (v: Values[string]) => string[];
|
|
72
72
|
export declare function out(ctx: Ctx, human: string, machine: unknown): void;
|
|
73
|
-
/** `--when-idle <duration>`: whole seconds from 0 (as soon as it is idle; 0.6.1) to 3600 (the API's after_seconds bounds
|
|
73
|
+
/** `--when-idle <duration>`: whole seconds from 0 (as soon as it is idle; 0.6.1) to 3600 (the API's after_seconds bounds). */
|
|
74
74
|
export declare function whenIdleSeconds(text: string): number;
|
|
75
75
|
export declare function waitTimeout(values: Values): number;
|
|
76
76
|
/**
|
|
77
77
|
* `<id|key>`: a UUID is tried as an id first; anything else (or an unknown UUID) as an exact workspace key, across every
|
|
78
78
|
* lifetime and purpose (sessions, drafts and test instances are hidden from the default list), preferring the live
|
|
79
|
-
* workspace over tombstones of ended sessions that had the same key
|
|
79
|
+
* workspace over tombstones of ended sessions that had the same key.
|
|
80
80
|
*/
|
|
81
81
|
export declare function resolveWorkspace(ctx: Ctx, ref: string): Promise<Workspace>;
|
|
82
82
|
export declare function oneOf<T extends string>(values: Values, option: string, allowed: readonly T[]): T | undefined;
|
package/dist/commands.js
CHANGED
|
@@ -16,7 +16,7 @@ import { updateCheckDisabled } from "./update-check.js";
|
|
|
16
16
|
import { initTemplateYaml, recipeYaml } from "./template-yaml.js";
|
|
17
17
|
import { BuildTimer, buildTimingJson, buildTimingText, progressLine } from "./timing.js";
|
|
18
18
|
import { timingJson } from "./timing.js";
|
|
19
|
-
import { bindingsText, buildTable, buildText, changesTable, diffSummaryText, diffTable, draftText, draftStateTable, executionJson, executionSummary, fileInfoText, fileTable, languageTable, json, kv, modeText, operationDetail, pairs, operationTable, packageTable, searchText, secretAccessTable, secretDetail, secretTable, secretVersionTable, sessionTable, suspendRequestText, suspendWhenIdleText, templateDetailText, templateEntryText, templateFileTable, templateTable, usageEstimateText, usageSeriesText, usageText, workspaceDetail, workspaceTable, } from "./format.js";
|
|
19
|
+
import { bindingsText, buildTable, buildText, changesTable, diffSummaryText, diffTable, draftText, draftStateTable, executionJson, executionSummary, fileInfoText, fileTable, languageTable, json, kv, modeText, operationDetail, pairs, operationTable, packageTable, searchText, secretAccessTable, secretDetail, secretTable, secretVersionTable, sessionTable, durableText, suspendRequestText, suspendWhenIdleText, templateDetailText, templateEntryText, templateFileTable, templateTable, usageEstimateText, usageSeriesText, usageText, workspaceDetail, workspaceTable, } from "./format.js";
|
|
20
20
|
const DEFAULT_WAIT_MS = 300_000;
|
|
21
21
|
const MAX_JSON_OUTPUT = 16 * 1024 * 1024;
|
|
22
22
|
const MAX_STDIN = 1024 * 1024;
|
|
@@ -30,10 +30,10 @@ export const str = (v) => (typeof v === 'string' ? v : undefined);
|
|
|
30
30
|
const cellOf = (ctx, ws) => ws.cell({ agentLabel: ctx.agentLabel, transitionTimeoutMs: ctx.wakeTimeoutMs, ...(ctx.wake ? {} : { wake: null }) });
|
|
31
31
|
export const strs = (v) => (Array.isArray(v) ? v : typeof v === 'string' ? [v] : []);
|
|
32
32
|
const MAX_SECRET_VALUE_BYTES = 65_536;
|
|
33
|
-
/** `files patch --content-file`: a patch request is at most 7 MiB
|
|
33
|
+
/** `files patch --content-file`: a patch request is at most 7 MiB; larger files go through `files write`. */
|
|
34
34
|
const MAX_PATCH_CONTENT = 7 * 1024 * 1024;
|
|
35
35
|
const STOPPED_AT = { max_matches: '--max matches', budget: 'its 10 s budget', max_bytes: '4 MiB of results' };
|
|
36
|
-
/** A file revision (SHA-256 of the content) or `absent
|
|
36
|
+
/** A file revision (SHA-256 of the content) or `absent`. */
|
|
37
37
|
const REVISION = /^([0-9a-f]{64}|absent)$/;
|
|
38
38
|
const TOOLS = ['exec', 'files', 'pty', 'process', 'git', 'browser'];
|
|
39
39
|
function secretNames(names, option) {
|
|
@@ -66,7 +66,7 @@ function caps(values) {
|
|
|
66
66
|
c.disk_gib = parsePositiveInt(disk, 'disk-gib', 1_000_000);
|
|
67
67
|
return Object.keys(c).length ? c : undefined;
|
|
68
68
|
}
|
|
69
|
-
/** `--when-idle <duration>`: whole seconds from 0 (as soon as it is idle; 0.6.1) to 3600 (the API's after_seconds bounds
|
|
69
|
+
/** `--when-idle <duration>`: whole seconds from 0 (as soon as it is idle; 0.6.1) to 3600 (the API's after_seconds bounds). */
|
|
70
70
|
export function whenIdleSeconds(text) {
|
|
71
71
|
if (/^0+(?:\.0+)?(?:ms|s|m|h)?$/.test(text.trim()))
|
|
72
72
|
return 0;
|
|
@@ -85,7 +85,7 @@ export function waitTimeout(values) {
|
|
|
85
85
|
/**
|
|
86
86
|
* `<id|key>`: a UUID is tried as an id first; anything else (or an unknown UUID) as an exact workspace key, across every
|
|
87
87
|
* lifetime and purpose (sessions, drafts and test instances are hidden from the default list), preferring the live
|
|
88
|
-
* workspace over tombstones of ended sessions that had the same key
|
|
88
|
+
* workspace over tombstones of ended sessions that had the same key.
|
|
89
89
|
*/
|
|
90
90
|
export async function resolveWorkspace(ctx, ref) {
|
|
91
91
|
const cloud = ctx.cloud();
|
|
@@ -192,7 +192,7 @@ export function parseSlugVersion(text, what) {
|
|
|
192
192
|
}
|
|
193
193
|
const INPUT_NAME = /^[A-Za-z_][A-Za-z0-9_]{0,127}$/;
|
|
194
194
|
/**
|
|
195
|
-
* Open-time text inputs
|
|
195
|
+
* Open-time text inputs from --input-file (NAME=value lines with # comments, or a JSON object of
|
|
196
196
|
* strings), then each --input NAME=value (a later one wins). Undefined when neither option is given.
|
|
197
197
|
*/
|
|
198
198
|
async function inputsFrom(ctx, values) {
|
|
@@ -417,7 +417,7 @@ export function execExitCode(r) {
|
|
|
417
417
|
async function bestEffortCancel(cell, sessionId) {
|
|
418
418
|
await Promise.race([cell.exec.cancel(sessionId).catch(() => undefined), new Promise((r) => setTimeout(r, 3_000).unref())]);
|
|
419
419
|
}
|
|
420
|
-
// ---- File-first workspaces (0.5.0
|
|
420
|
+
// ---- File-first workspaces (0.5.0) ----------------------------------------------------------------------
|
|
421
421
|
const MODES = { processful: 'processful', 'file-first': 'file_first', file_first: 'file_first' };
|
|
422
422
|
/** `--mode processful|file-first` (`file_first` too). */
|
|
423
423
|
function modeFlag(values) {
|
|
@@ -452,7 +452,7 @@ function treeRevisionAfter(ws, cell) {
|
|
|
452
452
|
return cell.treeRevision ?? ws.treeRevision;
|
|
453
453
|
}
|
|
454
454
|
/**
|
|
455
|
-
* A lifecycle call a file-first workspace does not have (
|
|
455
|
+
* A lifecycle call a file-first workspace does not have (suspend, resume, fork, reset,
|
|
456
456
|
* save-as-template), refused before any request as the SDK's workspace handle does: NotSupportedForModeError, printed
|
|
457
457
|
* with its hint like the API's own refusal.
|
|
458
458
|
*/
|
|
@@ -496,7 +496,7 @@ function reportExecution(ctx, ref, r, opts) {
|
|
|
496
496
|
}
|
|
497
497
|
function lifecycleText(verb, w, op) {
|
|
498
498
|
const finished = op.state === 'succeeded' || op.state === 'failed' || op.state === 'canceled';
|
|
499
|
-
return `${verb} ${w.key} (${w.id}): operation ${op.id} ${op.kind} ${op.state}${op.state_reason ? ` (${op.state_reason})` : ''}\n${finished ?
|
|
499
|
+
return `${verb} ${w.key} (${w.id}): operation ${op.id} ${op.kind} ${op.state}${op.state_reason ? ` (${op.state_reason})` : ''}\n${finished ? durableText(op) : `wait with: shard operations wait ${op.id}\n`}`;
|
|
500
500
|
}
|
|
501
501
|
/** The project-key commands; handlers.ts adds the account plane's. */
|
|
502
502
|
export const KEY_HANDLERS = {
|
|
@@ -519,7 +519,7 @@ export const KEY_HANDLERS = {
|
|
|
519
519
|
out(ctx, `shard ${CLI_VERSION} (@shardflux/sdk ${SDK_VERSION})\n${line}`, { cli: CLI_VERSION, sdk: SDK_VERSION, update });
|
|
520
520
|
return 0;
|
|
521
521
|
},
|
|
522
|
-
// Feedback (0.5.0+): POST /v1/feedback, emailed to the
|
|
522
|
+
// Feedback (0.5.0+): POST /v1/feedback, emailed to the Shardflux team. Validated here first so a bad call costs no request.
|
|
523
523
|
async feedback(ctx, values, words) {
|
|
524
524
|
const category = oneOf(values, 'category', FEEDBACK_CATEGORIES);
|
|
525
525
|
const context = feedbackContext(ctx, values);
|
|
@@ -527,7 +527,7 @@ export const KEY_HANDLERS = {
|
|
|
527
527
|
const r = await feedbackSender(ctx).sendFeedback({ message, ...(category ? { category } : {}), context });
|
|
528
528
|
const human = r.duplicate
|
|
529
529
|
? `Already received: the same message was sent in the last 24 hours (feedback ${r.id}); not emailed again.\n`
|
|
530
|
-
: `Sent to the Shardflux
|
|
530
|
+
: `Sent to the Shardflux team (feedback ${r.id}). Thank you.\n`;
|
|
531
531
|
out(ctx, human, { id: r.id, received_at: r.receivedAt, duplicate: r.duplicate });
|
|
532
532
|
return 0;
|
|
533
533
|
},
|
|
@@ -565,6 +565,46 @@ export const KEY_HANDLERS = {
|
|
|
565
565
|
out(ctx, usageText(summary), summary);
|
|
566
566
|
return 0;
|
|
567
567
|
},
|
|
568
|
+
async 'workspaces labels'(ctx, values, [ref]) {
|
|
569
|
+
const ws = await resolveWorkspace(ctx, ref);
|
|
570
|
+
const labels = labelMap(str(values.set));
|
|
571
|
+
if (labels !== undefined)
|
|
572
|
+
await ws.setLabels(labels);
|
|
573
|
+
out(ctx, JSON.stringify(ws.labels, null, 2) + '\n', ws.labels);
|
|
574
|
+
return 0;
|
|
575
|
+
},
|
|
576
|
+
async 'workspaces idle'(ctx, values, [ref]) {
|
|
577
|
+
const ws = await resolveWorkspace(ctx, ref);
|
|
578
|
+
const policy = str(values.policy);
|
|
579
|
+
const keepalive = str(values.keepalive);
|
|
580
|
+
if (policy !== undefined && keepalive !== undefined)
|
|
581
|
+
throw new UsageError('--policy and --keepalive cannot be combined');
|
|
582
|
+
let result;
|
|
583
|
+
if (policy !== undefined)
|
|
584
|
+
result = (await ws.setIdlePolicy(policy === 'default' ? null : policy)).data;
|
|
585
|
+
else if (keepalive !== undefined)
|
|
586
|
+
result = await ws.keepalive(parsePositiveInt(keepalive, 'keepalive', 86400));
|
|
587
|
+
else
|
|
588
|
+
result = await ws.idle(ctx.signal);
|
|
589
|
+
out(ctx, JSON.stringify(result, null, 2) + '\n', result);
|
|
590
|
+
return 0;
|
|
591
|
+
},
|
|
592
|
+
async 'workspaces exec-start'(ctx, values, [ref, ...argv]) {
|
|
593
|
+
const ws = await resolveWorkspace(ctx, ref);
|
|
594
|
+
const session = await cellOf(ctx, ws).exec.start({ argv, stdin_open: values['stdin-open'] === true,
|
|
595
|
+
...(str(values['session-id']) ? { session_id: str(values['session-id']) } : {}), ...(str(values.cwd) ? { cwd: str(values.cwd) } : {}) }, ctx.signal);
|
|
596
|
+
out(ctx, JSON.stringify(session, null, 2) + '\n', session);
|
|
597
|
+
return 0;
|
|
598
|
+
},
|
|
599
|
+
async 'workspaces exec-input'(ctx, values, [ref, sessionId, data]) {
|
|
600
|
+
const offset = Number(str(values.offset));
|
|
601
|
+
if (str(values.offset) === undefined || !Number.isSafeInteger(offset) || offset < 0)
|
|
602
|
+
throw new UsageError('--offset must be a non-negative integer');
|
|
603
|
+
const ws = await resolveWorkspace(ctx, ref);
|
|
604
|
+
const ack = await cellOf(ctx, ws).exec.input(sessionId, data ?? '', { offset, close: values.close === true, signal: ctx.signal });
|
|
605
|
+
out(ctx, JSON.stringify(ack) + '\n', ack);
|
|
606
|
+
return 0;
|
|
607
|
+
},
|
|
568
608
|
async 'workspaces open'(ctx, values, [key]) {
|
|
569
609
|
const template = str(values.template);
|
|
570
610
|
if (!template)
|
|
@@ -583,6 +623,8 @@ export const KEY_HANDLERS = {
|
|
|
583
623
|
const ws = await waited(ctx, 'the open continues server side (open the key again to keep waiting).', () => cloud.workspaces.open({
|
|
584
624
|
key: key,
|
|
585
625
|
template,
|
|
626
|
+
...(str(values.labels) !== undefined ? { labels: labelMap(str(values.labels)) } : {}),
|
|
627
|
+
...(str(values['idle-policy']) ? { idlePolicy: str(values['idle-policy']) } : {}),
|
|
586
628
|
...(c ? { caps: c } : {}),
|
|
587
629
|
...(bind.length ? { secrets: bind } : {}),
|
|
588
630
|
...(inputs ? { inputs } : {}),
|
|
@@ -609,6 +651,7 @@ export const KEY_HANDLERS = {
|
|
|
609
651
|
const params = {
|
|
610
652
|
...(state ? { state: state } : {}),
|
|
611
653
|
...(prefix ? { keyPrefix: prefix } : {}),
|
|
654
|
+
...(str(values.labels) !== undefined ? { labels: labelMap(str(values.labels)) } : {}),
|
|
612
655
|
...(lifetime ? { lifetime } : {}),
|
|
613
656
|
...(purpose ? { purpose } : {}),
|
|
614
657
|
...(values['include-deleted'] === true ? { includeDeleted: true } : {}),
|
|
@@ -661,7 +704,7 @@ export const KEY_HANDLERS = {
|
|
|
661
704
|
const cell = cellOf(ctx, ws);
|
|
662
705
|
const cwd = str(values.cwd);
|
|
663
706
|
if (ws.mode === 'file_first') {
|
|
664
|
-
// An execution
|
|
707
|
+
// An execution: answered when the command ended. The id is ours, so Ctrl-C can name it.
|
|
665
708
|
const id = executionId ?? newExecutionId();
|
|
666
709
|
let r;
|
|
667
710
|
try {
|
|
@@ -677,7 +720,7 @@ export const KEY_HANDLERS = {
|
|
|
677
720
|
}
|
|
678
721
|
catch (err) {
|
|
679
722
|
if (ctx.signal.aborted) {
|
|
680
|
-
throw new InterruptedError(`Interrupted; execution ${id} keeps running server side if it started (an execution
|
|
723
|
+
throw new InterruptedError(`Interrupted; execution ${id} keeps running server side if it started (an execution runs to completion).`, undefined, {
|
|
681
724
|
executionId: id,
|
|
682
725
|
hint: `fetch its result later with: shard executions get ${ref} ${id}`,
|
|
683
726
|
});
|
|
@@ -854,8 +897,12 @@ export const KEY_HANDLERS = {
|
|
|
854
897
|
async 'workspaces suspend'(ctx, values, [ref]) {
|
|
855
898
|
const whenIdle = str(values['when-idle']);
|
|
856
899
|
const cancel = values['cancel-when-idle'] === true;
|
|
857
|
-
|
|
858
|
-
|
|
900
|
+
const durable = values.durable === true;
|
|
901
|
+
if (whenIdle === undefined && !cancel) {
|
|
902
|
+
return lifecycle(ctx, values, ref, 'Suspending', (ws, o) => ctx.cloud().workspaces.suspend(ws.id, durable ? { ...o, durable: true, wait: o.wait ?? { timeoutMs: waitTimeout(values), signal: ctx.signal } } : o), ['suspend', 'Suspend']);
|
|
903
|
+
}
|
|
904
|
+
if (durable)
|
|
905
|
+
throw new UsageError(`${whenIdle !== undefined ? '--when-idle' : '--cancel-when-idle'} cannot be combined with --durable (nothing is suspended now)`);
|
|
859
906
|
if (whenIdle !== undefined && cancel)
|
|
860
907
|
throw new UsageError('--when-idle and --cancel-when-idle cannot be combined');
|
|
861
908
|
const flag = cancel ? '--cancel-when-idle' : '--when-idle';
|
|
@@ -876,7 +923,7 @@ export const KEY_HANDLERS = {
|
|
|
876
923
|
return 0;
|
|
877
924
|
},
|
|
878
925
|
async 'workspaces resume'(ctx, values, [ref]) {
|
|
879
|
-
// With --wait the resume is held until the workspace runs
|
|
926
|
+
// With --wait the resume is held until the workspace runs; its tool token is the CLI's label.
|
|
880
927
|
return lifecycle(ctx, values, ref, 'Resuming', (ws, o) => ctx.cloud().workspaces.resume(ws.id, { ...o, agentLabel: ctx.agentLabel }), ['resume', 'Resume']);
|
|
881
928
|
},
|
|
882
929
|
async 'workspaces delete'(ctx, values, [ref]) {
|
|
@@ -1526,7 +1573,10 @@ export const KEY_HANDLERS = {
|
|
|
1526
1573
|
async 'operations wait'(ctx, values, [id]) {
|
|
1527
1574
|
if (!UUID.test(id))
|
|
1528
1575
|
throw new UsageError(`<operation-id> must be a UUID (got "${id}")`);
|
|
1529
|
-
const
|
|
1576
|
+
const timeoutMs = waitTimeout(values);
|
|
1577
|
+
const op = values.durable === true
|
|
1578
|
+
? await waited(ctx, `operation ${id} continues server side.`, () => ctx.cloud().workspaces.waitForDurable(id, { timeoutMs, signal: ctx.signal }))
|
|
1579
|
+
: await waitOperation(ctx, id, timeoutMs);
|
|
1530
1580
|
out(ctx, operationDetail(op), op);
|
|
1531
1581
|
return 0;
|
|
1532
1582
|
},
|
|
@@ -1681,3 +1731,17 @@ async function lifecycle(ctx, values, ref, verb, start, processfulOnly) {
|
|
|
1681
1731
|
out(ctx, lifecycleText(verb, ws, op), op);
|
|
1682
1732
|
return 0;
|
|
1683
1733
|
}
|
|
1734
|
+
function labelMap(value) {
|
|
1735
|
+
if (value === undefined)
|
|
1736
|
+
return undefined;
|
|
1737
|
+
let parsed;
|
|
1738
|
+
try {
|
|
1739
|
+
parsed = JSON.parse(value);
|
|
1740
|
+
}
|
|
1741
|
+
catch {
|
|
1742
|
+
throw new UsageError('labels must be a JSON object of strings');
|
|
1743
|
+
}
|
|
1744
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed) || Object.values(parsed).some((v) => typeof v !== 'string'))
|
|
1745
|
+
throw new UsageError('labels must be a JSON object of strings');
|
|
1746
|
+
return parsed;
|
|
1747
|
+
}
|
package/dist/errors.d.ts
CHANGED
|
@@ -48,7 +48,7 @@ export declare class CheckoutEndedError extends Error {
|
|
|
48
48
|
}
|
|
49
49
|
/**
|
|
50
50
|
* The user pressed Ctrl-C; `operationId` is set when a server-side operation keeps running, `executionId` when a
|
|
51
|
-
* file-first execution does (
|
|
51
|
+
* file-first execution does (an execution runs to completion), with `hint` naming the command that fetches its result.
|
|
52
52
|
*/
|
|
53
53
|
export declare class InterruptedError extends Error {
|
|
54
54
|
name: string;
|
|
@@ -60,7 +60,7 @@ export declare class InterruptedError extends Error {
|
|
|
60
60
|
hint?: string;
|
|
61
61
|
});
|
|
62
62
|
}
|
|
63
|
-
/** A file-first execution that ended `failed` or `lost
|
|
63
|
+
/** A file-first execution that ended `failed` or `lost`: nothing was published. */
|
|
64
64
|
export declare class ExecutionFailedError extends Error {
|
|
65
65
|
name: string;
|
|
66
66
|
readonly result: ExecutionResult;
|
|
@@ -84,7 +84,7 @@ export interface ErrorInfo {
|
|
|
84
84
|
retryable?: boolean;
|
|
85
85
|
operation_id?: string;
|
|
86
86
|
details?: Record<string, unknown>;
|
|
87
|
-
source?: 'api' | 'cell';
|
|
87
|
+
source?: 'api' | 'cell' | 'unknown';
|
|
88
88
|
last_state?: string;
|
|
89
89
|
operation?: unknown;
|
|
90
90
|
/** A file-first execution the error is about (failed, lost, still running, or left running by Ctrl-C). */
|
package/dist/errors.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* stderr with the API's error code, message and request id; the process exit
|
|
4
4
|
* code tells scripts what kind of failure it was (README "Exit codes").
|
|
5
5
|
*/
|
|
6
|
-
import { CheckoutTimeoutError, ExecStartError, OperationFailedError, OperationTimeoutError, ShardfluxApiError, ShardfluxProtocolError, TemplateBuildTimeoutError, TemplateFileError, TemplateUploadError } from '@shardflux/sdk';
|
|
6
|
+
import { CheckoutTimeoutError, DurabilityLostError, ExecStartError, OperationFailedError, OperationTimeoutError, ShardfluxApiError, ShardfluxProtocolError, TemplateBuildTimeoutError, TemplateFileError, TemplateUploadError } from '@shardflux/sdk';
|
|
7
7
|
export const EXIT = {
|
|
8
8
|
ok: 0,
|
|
9
9
|
/** Any other failure: API refusal (conflict, quota, ...), network, protocol. */
|
|
@@ -59,7 +59,7 @@ export class CheckoutEndedError extends Error {
|
|
|
59
59
|
}
|
|
60
60
|
/**
|
|
61
61
|
* The user pressed Ctrl-C; `operationId` is set when a server-side operation keeps running, `executionId` when a
|
|
62
|
-
* file-first execution does (
|
|
62
|
+
* file-first execution does (an execution runs to completion), with `hint` naming the command that fetches its result.
|
|
63
63
|
*/
|
|
64
64
|
export class InterruptedError extends Error {
|
|
65
65
|
name = 'InterruptedError';
|
|
@@ -73,7 +73,7 @@ export class InterruptedError extends Error {
|
|
|
73
73
|
this.hint = more.hint;
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
|
-
/** A file-first execution that ended `failed` or `lost
|
|
76
|
+
/** A file-first execution that ended `failed` or `lost`: nothing was published. */
|
|
77
77
|
export class ExecutionFailedError extends Error {
|
|
78
78
|
name = 'ExecutionFailedError';
|
|
79
79
|
result;
|
|
@@ -137,17 +137,24 @@ export function describeError(err) {
|
|
|
137
137
|
source: err.source,
|
|
138
138
|
};
|
|
139
139
|
}
|
|
140
|
-
if (err instanceof OperationTimeoutError)
|
|
141
|
-
return { code: 'timeout', message: err.message, operation_id: err.operationId, last_state: err.lastState };
|
|
140
|
+
if (err instanceof OperationTimeoutError) {
|
|
141
|
+
return { code: 'timeout', message: err.message, operation_id: err.operationId, last_state: err.lastState, ...(err.durable ? { details: { durable: false, state: 'pending' } } : {}) };
|
|
142
|
+
}
|
|
143
|
+
// --durable (0.7.0+): the suspend succeeded; its durable copy could not be made (see the lifecycle reference).
|
|
144
|
+
if (err instanceof DurabilityLostError) {
|
|
145
|
+
return { code: 'durability_lost', message: err.message, operation_id: err.operationId, retryable: false, details: { state: 'lost', ...(err.durability.reason ? { reason: err.durability.reason } : {}), checkpoint_id: err.durability.checkpointId }, operation: err.operation };
|
|
146
|
+
}
|
|
142
147
|
if (err instanceof OperationFailedError) {
|
|
143
148
|
// retryable: the operation error's own flag (true for capacity_unavailable: nothing was started, retry later).
|
|
144
149
|
// details.reason: the operation error's reason (e.g. the scheduler's no_ready_hosts), printed after the message.
|
|
145
150
|
const opDetails = err.operation.error?.details;
|
|
146
151
|
const reason = typeof opDetails === 'object' && opDetails !== null ? opDetails.reason : undefined;
|
|
147
152
|
const details = { ...(err.errorCode ? { error_code: err.errorCode } : {}), ...(typeof reason === 'string' ? { reason } : {}) };
|
|
148
|
-
|
|
153
|
+
// A suspend-when-idle canceled workspace_active (0.7.0+): the workspace was in use and keeps running.
|
|
154
|
+
const message = err.workspaceActive ? `${err.message}: the workspace was in use, so the suspend-when-idle was canceled; it keeps running` : err.message;
|
|
155
|
+
return { code: 'operation_failed', message, operation_id: err.operationId, retryable: err.retryable, ...(Object.keys(details).length ? { details } : {}), operation: err.operation };
|
|
149
156
|
}
|
|
150
|
-
// File-first executions (0.5.0
|
|
157
|
+
// File-first executions (0.5.0): failed or lost like a failed operation (details.reason says why;
|
|
151
158
|
// retryable: a new execution id may succeed), still running like a wait that gave up.
|
|
152
159
|
if (err instanceof ExecutionFailedError) {
|
|
153
160
|
const e = err.result.error;
|
|
@@ -180,7 +187,7 @@ export function describeError(err) {
|
|
|
180
187
|
return { code: 'interrupted', message: err.message, ...(err.operationId ? { operation_id: err.operationId } : {}), ...(err.executionId ? { execution_id: err.executionId } : {}) };
|
|
181
188
|
}
|
|
182
189
|
if (err instanceof ShardfluxProtocolError)
|
|
183
|
-
return { code: 'protocol_error', message: err.message, status: err.status };
|
|
190
|
+
return { code: 'protocol_error', message: err.message, status: err.status, source: err.source };
|
|
184
191
|
if (isNamed(err, 'TimeoutError'))
|
|
185
192
|
return { code: 'timeout', message: 'The request timed out.' };
|
|
186
193
|
if (isNamed(err, 'AbortError'))
|
|
@@ -205,7 +212,7 @@ export function exitCodeFor(err, interrupted = false) {
|
|
|
205
212
|
return EXIT.notFound;
|
|
206
213
|
if (err instanceof OperationTimeoutError || err instanceof CheckoutTimeoutError || isNamed(err, 'TimeoutError'))
|
|
207
214
|
return EXIT.timeout;
|
|
208
|
-
if (err instanceof OperationFailedError || err instanceof ExecutionFailedError || err instanceof CheckoutEndedError)
|
|
215
|
+
if (err instanceof OperationFailedError || err instanceof DurabilityLostError || err instanceof ExecutionFailedError || err instanceof CheckoutEndedError)
|
|
209
216
|
return EXIT.operationFailed;
|
|
210
217
|
if (err instanceof ExecutionPendingError)
|
|
211
218
|
return EXIT.timeout;
|
package/dist/format.d.ts
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Human-readable output (aligned tables, key/value blocks) and the error line.
|
|
3
|
-
* `--json` output is the API's own objects (see README "JSON output"), so
|
|
4
|
-
* these functions only shape what people read.
|
|
5
|
-
*/
|
|
6
1
|
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, SpendPolicy, SuspendRequest, TemplateBuild, TemplateDetail, TemplateDiffEntry, TemplateDraft, TemplateFileEntry, TemplateLanguages, TemplatePackagePage, TemplateSettings, TemplateSummary, WorkspaceChange, WorkspaceSecretBindings, WorkspaceView } from '@shardflux/sdk';
|
|
7
2
|
import type { ErrorInfo } from './errors.js';
|
|
8
3
|
export declare function json(value: unknown): string;
|
|
@@ -22,13 +17,13 @@ export declare function workspaceRow(w: WorkspaceView): string[];
|
|
|
22
17
|
export declare function workspaceTable(ws: readonly WorkspaceView[]): string;
|
|
23
18
|
export declare function isReady(w: WorkspaceView): boolean;
|
|
24
19
|
/**
|
|
25
|
-
* The startup of a workspace's template version
|
|
20
|
+
* The startup of a workspace's template version in one line: state, trigger, and for a failure the
|
|
26
21
|
* step or service, exit code and reason. Null when the version has no start commands or services.
|
|
27
22
|
*/
|
|
28
23
|
export declare function startupText(s: WorkspaceView['startup'] | undefined): string | null;
|
|
29
24
|
/** The failed step's output tail, indented, after the detail block (empty otherwise). */
|
|
30
25
|
export declare function startupTail(s: WorkspaceView['startup'] | undefined): string;
|
|
31
|
-
/** A pending suspend-when-idle request in one line (0.5.1
|
|
26
|
+
/** A pending suspend-when-idle request in one line (0.5.1). */
|
|
32
27
|
export declare function suspendRequestText(r: SuspendRequest): string;
|
|
33
28
|
/** `shard ws suspend --when-idle`: the recorded request, or the suspend already in progress. */
|
|
34
29
|
export declare function suspendWhenIdleText(w: {
|
|
@@ -41,6 +36,11 @@ export declare function suspendWhenIdleText(w: {
|
|
|
41
36
|
export declare function workspaceDetail(w: WorkspaceView): string;
|
|
42
37
|
export declare const OPERATION_HEADERS: readonly ["ID", "KIND", "STATE", "REASON", "CREATED", "COMPLETED"];
|
|
43
38
|
export declare function operationTable(ops: readonly Operation[]): string;
|
|
39
|
+
/**
|
|
40
|
+
* A finished suspend or fork (0.7.0+): whether its copy is in durable storage, when the result says (`durable`,
|
|
41
|
+
* `durability`). Empty when the result has no durability block (stored durably before the operation completed).
|
|
42
|
+
*/
|
|
43
|
+
export declare function durableText(op: Operation): string;
|
|
44
44
|
export declare function operationDetail(op: Operation): string;
|
|
45
45
|
export declare function sessionTable(sessions: readonly AgentSession[]): string;
|
|
46
46
|
/** `files stat`: one path's metadata; the revision only when requested and returned. */
|
|
@@ -63,8 +63,8 @@ export declare function capMoney(minor: unknown, currency: string): string;
|
|
|
63
63
|
*/
|
|
64
64
|
export declare function usageText(summary: unknown): string;
|
|
65
65
|
/**
|
|
66
|
-
* The `shard feedback` command a failed command suggests (0.5.0+), with the error's request id and code so the
|
|
67
|
-
* can find the logs (a failed operation's or execution's own error code, e.g.
|
|
66
|
+
* The `shard feedback` command a failed command suggests (0.5.0+), with the error's request id and code so the team
|
|
67
|
+
* can find the logs (a failed operation's or execution's own error code, e.g. template_not_found); the agent fills in
|
|
68
68
|
* the text.
|
|
69
69
|
*/
|
|
70
70
|
export declare function feedbackCommand(e: ErrorInfo): string;
|
|
@@ -90,7 +90,7 @@ export declare function languageTable(l: TemplateLanguages): string;
|
|
|
90
90
|
export declare function packageTable(page: TemplatePackagePage): string;
|
|
91
91
|
/** A template: identity, what open resolves to, its versions, and the open version's settings. */
|
|
92
92
|
export declare function templateDetailText(t: TemplateDetail): string;
|
|
93
|
-
/** A version's settings
|
|
93
|
+
/** A version's settings, one line per part. */
|
|
94
94
|
export declare function settingsText(s: TemplateSettings): string;
|
|
95
95
|
/** How many changed paths the human output lists before "... N more" (--json lists every one the result has). */
|
|
96
96
|
export declare const MAX_CHANGED_SHOWN = 100;
|
|
@@ -133,6 +133,17 @@ export declare function usageSeriesText(series: unknown): string;
|
|
|
133
133
|
/** The period estimate (GET .../usage/estimate): the bill so far and where each allowance is heading. */
|
|
134
134
|
export declare function usageEstimateText(estimate: unknown): string;
|
|
135
135
|
export declare function money(amountMinor: number, currency: string): string;
|
|
136
|
+
export declare function isGrantPlan(plan: {
|
|
137
|
+
plan_key: string;
|
|
138
|
+
grant_days?: unknown;
|
|
139
|
+
}): boolean;
|
|
140
|
+
/** The plan in force with the grant's end (`ends_at`, 0.6.2+), which the SDK's generated types add with the API change. */
|
|
141
|
+
type PlanInForce = NonNullable<BillingSubscription['plan']> & {
|
|
142
|
+
ends_at?: string | null;
|
|
143
|
+
};
|
|
144
|
+
/** "trial, ends 2026-11-29 14:03:00Z, then free (trial, catalog live-v7)"; a plan without an end as before. */
|
|
145
|
+
export declare function planInForceText(plan: PlanInForce | null): string;
|
|
146
|
+
/** The public plans: granted plans (the trial, design partners) are never listed or offered. */
|
|
136
147
|
export declare function planTable(catalog: BillingCatalog): string;
|
|
137
148
|
export declare function billingStatusText(s: BillingSubscription, org: Pick<Organization, 'name' | 'slug'>): string;
|
|
138
149
|
export declare function invoiceTable(rows: readonly BillingInvoicePage['data'][number][]): string;
|
|
@@ -149,3 +160,4 @@ export declare function egressVersionTable(rows: readonly EgressPolicyVersion[])
|
|
|
149
160
|
* cap and its bounds, the rates, and the next step. `saved` adds the version the change was saved as.
|
|
150
161
|
*/
|
|
151
162
|
export declare function overagePolicyText(p: SpendPolicy, orgName: string, saved?: boolean): string;
|
|
163
|
+
export {};
|