@shardflux/cli 0.23.0 → 0.23.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
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.23.1
4
+
5
+ - Pin the Ubuntu desktop packages to 0.1.11. Read the desktop account snapshot with the selected project key so organization, usage, spend and connector metadata match the macOS Settings app, including API-key-only profiles. Optional missing metadata stays unavailable. Conversation lists support --all with bounded cursor handling; the desktop reads all pages of conversations and workspace states.
6
+
3
7
  ## 0.23.0 (2026-10-14)
4
8
 
5
9
  - Codex Cloud enable and setup report when a ChatGPT sign-in is required, with `codex login` and `shard setup` as the next steps. Setup offers the sign-in action.
package/README.md CHANGED
@@ -6,7 +6,7 @@ Plan-derived API budgets (0.23.0+, SDK 0.27.0): `--json` error output includes `
6
6
 
7
7
  Exec survives a dropped connection (0.23.0+): a lost start response is retried with the same session ID and reattaches to the command that already started, and a cut-off JSON answer is retried within the same safe retry boundary.
8
8
 
9
- Suspension controls arrive in 0.22.0 (next coordinated release). `shard operations progress <operation-id>` reads the actual phase, host deadline, cancellation window and safe next action. `shard operations cancel <operation-id>` requests cancellation; `--wait --timeout 5m` waits for confirmed `suspend_canceled` recovery of the original workspace. Asynchronous acceptance is labeled separately. The owning host refuses cancellation outside validated preparation; after capture, wait for the outcome and resume if it succeeded. These commands never reset, replace or launch a provider turn. `--json` preserves the reported values; unavailable progress is explicit.
9
+ Suspension controls are available from 0.22.0. `shard operations progress <operation-id>` reads the actual phase, host deadline, cancellation window and safe next action. `shard operations cancel <operation-id>` requests cancellation; `--wait --timeout 5m` waits for confirmed `suspend_canceled` recovery of the original workspace. Asynchronous acceptance is labeled separately. The owning host refuses cancellation outside validated preparation; after capture, wait for the outcome and resume if it succeeded. These commands never reset, replace or launch a provider turn. `--json` preserves the reported values; unavailable progress is explicit.
10
10
 
11
11
  The command line for [Shardflux](https://shardflux.dev): serverless VMs for AI agents. Open a
12
12
  persistent workspace by key, run commands in it, move files in and out, and suspend, resume, fork
@@ -188,6 +188,10 @@ you can ask the agent itself to continue in the cloud. A session is listed under
188
188
  one that began in another folder such as Claude Desktop's scratch folder, and `--path <repository>` moves it from
189
189
  anywhere (0.22.0+).
190
190
 
191
+ With menu bar 0.8.5+, a failed move stays at its session’s row with its reason on hover and **Retry move to cloud**
192
+ in **⋯**. Retry or Refresh clears the previous failure. CLI 0.23.0+ records a matching stable Node.js path on macOS,
193
+ and menu bar 0.8.5+ recovers Node.js after an upgrade removes the recorded binary.
194
+
191
195
  Start a thread in T3 Code from the command line (0.17.0+):
192
196
 
193
197
  ```sh
@@ -1597,15 +1601,17 @@ shard agents new --app claude --path ./ --prompt "What is wrong on this page?" -
1597
1601
 
1598
1602
  `--image <file|->` (0.15.0+, repeatable) attaches up to five PNG, JPEG, GIF or WebP images of up to 10 MiB each to that first message. They are written into the conversation VM under `/home/user/.shardflux/attachments/` and named in the message: Claude Code attaches them as images and Codex opens them with its image viewer. `--image -` reads one image, or PNG images back to back, from standard input; use a file for `--prompt-file` when images come from standard input.
1599
1603
 
1600
- Use `shard agents open <workspace> --in auto --json` to resolve where a conversation opens: a saved target first; then, for Claude (0.15.1+), the conversation itself in Claude Desktop when it is installed (`{"target":"claude-desktop","url":"claude://code/session_…","app_bundle_id":…}`), else Claude Code in the terminal (`{"target":"terminal","command":["shard","agents","open","<workspace>"]}`; without `--json` it attaches in this terminal). Codex opens its Cloud thread in the Codex app. claude.ai opens with `--in web`. The caller opens the `url` or app, or runs the command in a terminal. Explicit `--in terminal` retains terminal attachment. The menu uses this resolver for new and existing conversations, without provider URLs in Swift.
1604
+ With CLI 0.23.0+, a full conversation key or id opens that same Claude or Codex session from any folder, including after **Move to cloud**. A computer key opens its bound conversation.
1605
+
1606
+ Use `shard agents open <workspace> --in auto --json` to resolve where a conversation opens: a saved target first; then, for Claude (0.15.1+), the conversation itself in Claude Desktop when it is installed (`{"target":"claude-desktop","url":"claude://code/session_…","app_bundle_id":…}`), else Claude Code in the terminal (`{"target":"terminal","command":["shard","agents","open","<workspace>"]}`; without `--json` it attaches in this terminal). Codex Cloud opens its current thread in your browser (CLI 0.23.0+). claude.ai opens with `--in web`. The caller opens the `url` or app, or runs the command in a terminal. Explicit `--in terminal` retains terminal attachment. The menu uses this resolver for new and existing conversations, without provider URLs in Swift.
1601
1607
 
1602
- ## Codex Cloud (0.15.0+, unreleased)
1608
+ ## Codex Cloud (0.15.0+)
1603
1609
 
1604
1610
  New Codex conversations use `shard agents new --app codex --path <directory>` or `shard ./ codex`. Each gets a fresh conversation VM and a genuine Codex Cloud thread. `shard agents open <conversation-id>` opens the existing thread without waking its VM. These paths create no SSH device entries and need no pairing.
1605
1611
 
1606
1612
  `shard setup` uses your existing Codex ChatGPT login to register one executor for the account. Its small connector workspace stays resident and is billed as ordinary workspace memory/CPU usage, including while conversations wait. Use `--no-codex` to skip setup, `shard agents codex status` to inspect it, or `shard agents codex disable` to remove the executor and release its residency. Conversation VMs wake for tools and follow the normal adaptive idle policy afterwards.
1607
1613
 
1608
- Explicit `agents new --in terminal`, `--in t3`, and `--like` launches keep their existing VM routes. Local Git is prepared and validated before Cloud enrollment. This branch's Cloud feature is awaiting release. Existing local-mode conversations keep their original route. Cloud uses OpenAI's model/configuration capabilities; local app-server plugins and settings are not automatically transferred.
1614
+ Explicit `agents new --in terminal`, `--in t3`, and `--like` launches keep their existing VM routes. Local Git is prepared and validated before Cloud enrollment. Existing local-mode conversations keep their original route. Cloud uses OpenAI's model/configuration capabilities; local app-server plugins and settings are not automatically transferred.
1609
1615
 
1610
1616
  Save progress (0.22.0+): Machine and template saves show source size and an estimated duration, then queued/build state and phase updates immediately. With `--json`, progress events go to stderr and the final stdout document includes `preflight` and `phases`. Failure and timeout output retain the latest counters and `details.last_progress`, with the build id and recovery action. If an older API cannot expose the build, JSON stderr emits `save_started` with the version/build id and leaves the phase unknown. Missing final host reports preserve the last observed progress.
1611
1617
 
package/dist/args.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export declare const CLI_VERSION = "0.23.0";
1
+ export declare const CLI_VERSION = "0.23.1";
2
2
  export interface OptionSpec {
3
3
  type: 'string' | 'boolean';
4
4
  short?: string;
package/dist/args.js CHANGED
@@ -13,7 +13,7 @@ import { parseArgs } from 'node:util';
13
13
  import { ACCOUNT_COMMANDS } from "./account-args.js";
14
14
  import { UsageError } from "./errors.js";
15
15
  import { DEFAULT_CHOICE, MODEL_CHOICES } from "./agent-models.js";
16
- export const CLI_VERSION = '0.23.0';
16
+ export const CLI_VERSION = '0.23.1';
17
17
  export const GLOBAL_OPTIONS = {
18
18
  'api-url': { type: 'string', value: '<url>', description: 'API base URL (default $SHARDFLUX_API_URL, else https://api.shardflux.dev).' },
19
19
  json: { type: 'boolean', description: 'Print machine-readable JSON on stdout (errors as JSON on stderr).' },
@@ -146,10 +146,9 @@ const LAYER_HELP = {
146
146
  template: 'The project template is this repository’s layer: toolchains, dependencies, services and project secrets. Setup after "shard ./" saves it; "shard template save" saves the current agent VM’s project changes, and "shard template delete" starts the project over from your machine. It is saved from an agent’s VM; "shard templates" builds templates for your own app’s workspaces instead.',
147
147
  };
148
148
  const KEY_COMMANDS = [
149
- { path: ['conversations', 'list'], summary: 'List project conversations', positionals: [], options: {
149
+ { path: ['conversations', 'list'], summary: 'List project conversations', positionals: [], options: { all: { type: 'boolean', description: 'Fetch every page.' },
150
150
  project: { type: 'string', value: '<id>', description: 'Project ID for a user session.' }, state: { type: 'string', value: '<state>', description: 'Conversation state.' },
151
- parent: { type: 'string', value: '<id>', description: 'Children of this conversation.' }, limit: { type: 'string', value: '<count>', description: 'Page size.' }, cursor: { type: 'string', value: '<cursor>', description: 'Next page cursor.' }
152
- } },
151
+ parent: { type: 'string', value: '<id>', description: 'Children of this conversation.' }, limit: { type: 'string', value: '<count>', description: 'Page size.' }, cursor: { type: 'string', value: '<cursor>', description: 'Next page cursor.' } } },
153
152
  { path: ['conversations', 'resume'], summary: 'Resume a saved conversation in a fresh VM', positionals: [{ name: 'id', required: true }], options: {} },
154
153
  { path: ['conversations', 'reader'], summary: 'Open a read-only project space reader', positionals: [{ name: 'project_id', required: true }], options: {} },
155
154
  { path: ['conversations', 'get'], summary: 'Show a conversation and its current VM', positionals: [{ name: 'id', required: true }], options: {} },
@@ -1364,10 +1363,10 @@ const CLI_PROXY_COMMANDS = [
1364
1363
  { path: ['cli-proxy', 'forward'], hidden: true, summary: 'Forward 127.0.0.1:8317 to your hosted CLIProxyAPI (started at login)', positionals: [], options: {} },
1365
1364
  ];
1366
1365
  /** Every command: the account plane first (sign in, set up), then the project-key commands. */
1367
- const MENUBAR_COMMANDS = ['start', 'stop', 'status'].map(action => ({
1368
- path: ['menubar', action], summary: `${action === 'start' ? 'Install and start' : action === 'stop' ? 'Stop' : 'Show'} the macOS menu bar app`,
1369
- positionals: [], options: { login: { type: 'string', value: '<on|off>', description: 'Enable or disable launch at login.' } },
1370
- }));
1366
+ const MENUBAR_COMMANDS = [{ path: ['menubar', 'account'], hidden: true, summary: 'Read this profile’s desktop account snapshot', positionals: [], options: {} }, ...['start', 'stop', 'status'].map((action) => ({
1367
+ path: ['menubar', action], summary: `${action === 'start' ? 'Install and start' : action === 'stop' ? 'Stop' : 'Show'} the macOS menu bar app`,
1368
+ positionals: [], options: { login: { type: 'string', value: '<on|off>', description: 'Enable or disable launch at login.' } },
1369
+ }))];
1371
1370
  const SETTINGS_COMMANDS = [{
1372
1371
  path: ['settings'], summary: 'Show or change this computer’s settings for cloud conversations (0.20.1+)', positionals: [], options: {
1373
1372
  'title-prefix': { type: 'string', value: '<text|none|default>', description: 'The marker your cloud conversations’ Claude session titles start with in claude.ai and the Claude app ("☁ Fix login bug · todo-app"): up to 16 characters on one line; none for no marker, default for ☁. New conversations use it.' },
@@ -19,9 +19,23 @@ export const CONVERSATION_HANDLERS = {
19
19
  async 'conversations reader'(ctx, _values, [id]) { const r = await conversationClient(ctx).conversations.spaceReader(id, { signal: ctx.signal }); out(ctx, `${r.workspace.workspace_key}\n`, r); return 0; },
20
20
  async 'conversations list'(ctx, values) {
21
21
  const state = str(values.state);
22
- const page = await conversationClient(ctx).conversations.list({ projectId: str(values.project), state, parentId: str(values.parent), cursor: str(values.cursor), limit: values.limit ? Number(values.limit) : undefined });
22
+ const data = [], seen = new Set();
23
+ let cursor = str(values.cursor), next;
24
+ if (cursor)
25
+ seen.add(cursor);
26
+ do {
27
+ const page = await conversationClient(ctx).conversations.list({ projectId: str(values.project), state, parentId: str(values.parent), cursor, limit: values.limit ? Number(values.limit) : values.all ? 200 : undefined });
28
+ data.push(...page.data);
29
+ next = page.nextCursor;
30
+ cursor = next ?? undefined;
31
+ if (values.all && cursor) {
32
+ if (seen.has(cursor) || seen.size >= 100)
33
+ throw new UsageError('The conversation list repeated its cursor. Try again.');
34
+ seen.add(cursor);
35
+ }
36
+ } while (values.all && cursor);
23
37
  // A conversation that needs you adds a fifth column: why (0.18.0+).
24
- out(ctx, page.data.map(c => { const q = questionLine(c); return `${c.id}\t${displayTitle(c)}\t${c.state}\t${c.branch}${q ? `\t${q}` : ''}`; }).join('\n') + '\n', page);
38
+ out(ctx, data.map(c => { const q = questionLine(c); return `${c.id}\t${displayTitle(c)}\t${c.state}\t${c.branch}${q ? `\t${q}` : ''}`; }).join('\n') + '\n', { data, nextCursor: next });
25
39
  return 0;
26
40
  },
27
41
  async 'conversations get'(ctx, _values, [id]) { const c = await conversationClient(ctx).conversations.get(id); const q = questionLine(c, 200); out(ctx, `${displayTitle(c)}\n${c.state}\n${c.branch}\n${q ? `${q}\n` : ''}`, c); return 0; },
@@ -87,12 +87,17 @@ export async function getConversation(ctx, id) {
87
87
  return validateConversation(c);
88
88
  }
89
89
  export async function listConversations(ctx, projectId) {
90
- const all = [];
90
+ const all = [], seen = new Set();
91
91
  let cursor;
92
92
  do {
93
93
  const page = await conversationClient(ctx).conversations.list({ ...(projectId ? { projectId } : {}), limit: 200, ...(cursor ? { cursor } : {}) });
94
94
  all.push(...page.data.map(validateConversation));
95
95
  cursor = page.nextCursor ?? undefined;
96
+ if (cursor) {
97
+ if (seen.has(cursor) || seen.size >= 100)
98
+ throw new UsageError('The conversation list repeated its cursor. Try again.');
99
+ seen.add(cursor);
100
+ }
96
101
  } while (cursor);
97
102
  return all;
98
103
  }
@@ -0,0 +1,16 @@
1
+ import type { Ctx } from './commands.js';
2
+ /** The desktop Settings snapshot uses the selected project key's organization, as AccountClient on macOS does.
3
+ * The optional user identity comes from the session; saved credentials never imply connector liveness. */
4
+ export declare function desktopAccount(ctx: Ctx): Promise<{
5
+ identity: {
6
+ id: string;
7
+ email: string;
8
+ email_verified: boolean;
9
+ display_name: string | null;
10
+ mfa_enabled: boolean;
11
+ } | null;
12
+ organization: Record<string, unknown>;
13
+ usage: Record<string, unknown> | null;
14
+ spend: Record<string, unknown> | null;
15
+ credentials: import("@shardflux/sdk").AgentCredentialMetadata[] | null;
16
+ }>;
@@ -0,0 +1,29 @@
1
+ import { UsageError } from "./errors.js";
2
+ /** The desktop Settings snapshot uses the selected project key's organization, as AccountClient on macOS does.
3
+ * The optional user identity comes from the session; saved credentials never imply connector liveness. */
4
+ export async function desktopAccount(ctx) {
5
+ const token = ctx.apiKey().token;
6
+ const request = async (path) => {
7
+ const response = await (ctx.accountOptions().fetch ?? fetch)(new URL(`v1/${path}`, `${ctx.apiUrl.replace(/\/+$/, '')}/`), {
8
+ headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' }, redirect: 'error',
9
+ signal: AbortSignal.any([ctx.signal, AbortSignal.timeout(20_000)]),
10
+ });
11
+ if (!response.ok)
12
+ throw new UsageError(`Couldn’t refresh your account (${response.status}). Try again.`);
13
+ return await response.json();
14
+ };
15
+ const me = await ctx.cloud().me();
16
+ const org = me.api_key?.organization_id;
17
+ if (!org)
18
+ throw new UsageError('The project key did not identify its organization. Sign in to Shardflux again.');
19
+ const [organization, usage, spend, credentials, identity] = await Promise.all([
20
+ request(`organizations/${encodeURIComponent(org)}`),
21
+ request(`organizations/${encodeURIComponent(org)}/usage/summary`).catch(() => null),
22
+ request(`organizations/${encodeURIComponent(org)}/spend`).catch(() => null),
23
+ ctx.cloud().agentCredentials.list().catch(() => null),
24
+ ctx.sessionSource() ? ctx.account().me().then(value => value.user).catch(() => null) : Promise.resolve(null),
25
+ ]);
26
+ if (organization.id !== org || typeof organization.name !== 'string' || !organization.name)
27
+ throw new UsageError('The organization response is unreadable. Refresh your account and try again.');
28
+ return { identity, organization, usage, spend, credentials };
29
+ }
package/dist/menubar.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { desktopAccount } from "./desktop-account.js";
1
2
  import { execFile, spawn } from 'node:child_process';
2
3
  import { chmodSync, closeSync, cpSync, existsSync, lstatSync, mkdirSync, mkdtempSync, openSync, readdirSync, readFileSync, renameSync, rmSync, writeSync } from 'node:fs';
3
4
  import { createRequire } from 'node:module';
@@ -492,3 +493,4 @@ export const MENUBAR_HANDLERS = Object.fromEntries(['start', 'stop', 'status'].m
492
493
  };
493
494
  return [`menubar ${action}`, handler];
494
495
  }));
496
+ MENUBAR_HANDLERS['menubar account'] = async (ctx) => { ctx.io.stdout.write(json(await desktopAccount(ctx))); return 0; };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shardflux/cli",
3
- "version": "0.23.0",
3
+ "version": "0.23.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 serverless agent workspaces.",
6
6
  "license": "Apache-2.0",
@@ -58,8 +58,8 @@
58
58
  "typescript-eslint": "8.70.1"
59
59
  },
60
60
  "optionalDependencies": {
61
- "@shardflux/desktop-linux-x64": "0.1.10",
62
- "@shardflux/desktop-linux-arm64": "0.1.10",
61
+ "@shardflux/desktop-linux-x64": "0.1.11",
62
+ "@shardflux/desktop-linux-arm64": "0.1.11",
63
63
  "@shardflux/menubar-darwin": "^0.8.5"
64
64
  },
65
65
  "scripts": {