@shardflux/cli 0.6.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,7 +3,19 @@
3
3
  Every command and option the README shows is available from the version named here. Below 1.0, a minor release may
4
4
  break compatibility; breaking changes are marked **Breaking**.
5
5
 
6
- ## 0.6.0 (not yet published)
6
+ ## 0.6.1
7
+
8
+ Needs `@shardflux/sdk` 0.11.0 or later.
9
+
10
+ ### `--when-idle 0`: suspend as soon as the workspace is idle
11
+
12
+ `shard ws suspend <id|key> --when-idle` accepts `0` (also `0s`) up to `1h`; before it refused anything under `30s`.
13
+ The API now takes `after_seconds` from 0: the workspace is suspended as soon as it is idle (a few seconds of activity
14
+ flush grace after its last work), and a running command, an attached stream or a keepalive still postpones it until
15
+ it ends. The human output says `is suspended as soon as it is idle`, and `ws get` shows `as soon as idle`. Out of
16
+ range is still exit 2 before any request: `--when-idle must be between 0s and 1h`.
17
+
18
+ ## 0.6.0
7
19
 
8
20
  Needs `@shardflux/sdk` 0.11.0 or later.
9
21
 
package/README.md CHANGED
@@ -385,7 +385,7 @@ shows the status; `shard version --check` asks now (`--json`: `{"cli", "sdk", "u
385
385
  | `files search <id\|key> <path> <pattern> [--regex] [-i\|--ignore-case] [--include G]... [--exclude G]... [--max N]` | Search file contents under a directory (or in one file; gitignore-style globs): `path:line:column: text` per match (0.5.0+). |
386
386
  | `files patch <id\|key> <path> (--old T --new T)... [--replace-all] \| --content-file F\|- [--expected-revision R] [--if-revision N]` | Replace exact text (each `--old` must occur once unless `--replace-all`), or the whole content, atomically; `R` is a revision from `files stat --revision` or `absent` (0.5.0+). `--if-revision` (0.5.0+): file-first workspaces only. |
387
387
  | `workspaces suspend\|resume <id\|key> [--wait] [--timeout T] [--timing]` | Lifecycle operation. Returns immediately unless you pass `--wait`. |
388
- | `workspaces suspend <id\|key> --when-idle D` / `--cancel-when-idle` | (0.5.1+) Suspend once the workspace has been idle for `D` (30s to 1h), or cancel that. See "Suspend when idle". |
388
+ | `workspaces suspend <id\|key> --when-idle D` / `--cancel-when-idle` | (0.5.1+) Suspend once the workspace has been idle for `D` (0 to 1h; `0` = as soon as it is idle, 0.6.1+), or cancel that. See "Suspend when idle". |
389
389
  | `workspaces fork <id\|key> <new-key> [caps] [--wait]` | Fork into a new key. |
390
390
  | `workspaces delete <id\|key> --yes [--wait]` | Delete the workspace. Tool access ends at once. The key of a persistent workspace is never reused. |
391
391
  | `workspaces close <id\|key> [--wait]` | End a session workspace now: it is deleted, and the key then opens a new workspace. A persistent workspace is refused (`not_session`). |
@@ -597,11 +597,13 @@ A running workspace is billed while it is awake. When an agent's turn ends, ask
597
597
  been idle for a short time, instead of waiting for its idle policy:
598
598
 
599
599
  ```sh
600
- shard ws suspend acme/demo --when-idle 2m # 30s to 1h; whole seconds
600
+ shard ws suspend acme/demo --when-idle 2m # 0 to 1h; whole seconds
601
601
  shard ws get acme/demo # "suspend when idle after 120 s idle, not before ..."
602
602
  shard ws suspend acme/demo --cancel-when-idle
603
603
  ```
604
604
 
605
+ - `--when-idle 0` (0.6.1+) suspends the workspace as soon as it is idle (a few seconds after its last work).
606
+
605
607
  - The idle time counts from the later of the workspace's last work and the request.
606
608
  - A command still running, an attached stream or a keepalive postpones the suspend until the duration after it ends.
607
609
  - The next tool call on the workspace (the next turn) or a resume cancels the request. Repeating replaces it.
package/dist/args.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export declare const CLI_VERSION = "0.6.0";
1
+ export declare const CLI_VERSION = "0.6.1";
2
2
  export interface OptionSpec {
3
3
  type: 'string' | 'boolean';
4
4
  short?: string;
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.6.0';
15
+ export const CLI_VERSION = '0.6.1';
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).' },
@@ -274,7 +274,7 @@ const KEY_COMMANDS = [
274
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.',
275
275
  positionals: [REF],
276
276
  options: {
277
- 'when-idle': { type: 'string', value: '<duration>', description: 'Suspend once the workspace has been idle this long (30s to 1h, e.g. 30s, 2m; 0.5.1+). Cannot be combined with --wait.' },
277
+ '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
278
  'cancel-when-idle': { type: 'boolean', description: 'Cancel a pending --when-idle request (0.5.1+; idempotent).' },
279
279
  ...WAIT,
280
280
  },
@@ -70,7 +70,7 @@ 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 30 to 3600 (the API's after_seconds bounds, contracts §20.6). */
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, contracts §20.6). */
74
74
  export declare function whenIdleSeconds(text: string): number;
75
75
  export declare function waitTimeout(values: Values): number;
76
76
  /**
package/dist/commands.js CHANGED
@@ -66,14 +66,16 @@ 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 30 to 3600 (the API's after_seconds bounds, contracts §20.6). */
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, contracts §20.6). */
70
70
  export function whenIdleSeconds(text) {
71
+ if (/^0+(?:\.0+)?(?:ms|s|m|h)?$/.test(text.trim()))
72
+ return 0;
71
73
  const ms = parseDuration(text, 'when-idle');
72
74
  if (ms % 1000 !== 0)
73
75
  throw new UsageError('--when-idle must be a whole number of seconds (e.g. 30s, 90s, 2m)');
74
76
  const s = ms / 1000;
75
- if (s < 30 || s > 3600)
76
- throw new UsageError(`--when-idle must be between 30s and 1h (got "${text}")`);
77
+ if (s > 3600)
78
+ throw new UsageError(`--when-idle must be between 0s and 1h (got "${text}")`);
77
79
  return s;
78
80
  }
79
81
  export function waitTimeout(values) {
package/dist/format.js CHANGED
@@ -109,13 +109,13 @@ export function startupTail(s) {
109
109
  }
110
110
  /** A pending suspend-when-idle request in one line (0.5.1; contracts §20.6). */
111
111
  export function suspendRequestText(r) {
112
- return `after ${r.after_seconds} s idle, not before ${time(r.not_before)} (requested ${time(r.requested_at)})`;
112
+ return `${r.after_seconds === 0 ? 'as soon as idle' : `after ${r.after_seconds} s idle`}, not before ${time(r.not_before)} (requested ${time(r.requested_at)})`;
113
113
  }
114
114
  /** `shard ws suspend --when-idle`: the recorded request, or the suspend already in progress. */
115
115
  export function suspendWhenIdleText(w, r) {
116
116
  if (r.suspend_request) {
117
117
  const s = r.suspend_request;
118
- return (`Suspend when idle: ${w.key} (${w.id}) is suspended once it has been idle for ${s.after_seconds} s, not before ${time(s.not_before)}.\n` +
118
+ return (`Suspend when idle: ${w.key} (${w.id}) is suspended ${s.after_seconds === 0 ? 'as soon as it is idle' : `once it has been idle for ${s.after_seconds} s`}, not before ${time(s.not_before)}.\n` +
119
119
  'A running command, an attached stream or a keepalive postpones it; the next tool call or a resume cancels it.\n' +
120
120
  `cancel with: shard workspaces suspend ${w.key} --cancel-when-idle\n`);
121
121
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shardflux/cli",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "type": "module",
5
5
  "description": "shard: the Shardflux command line. Create an account, sign in, set up keys and billing, then open, run commands in, move files to, suspend, resume and fork persistent agent workspaces.",
6
6
  "license": "Apache-2.0",