@shardflux/cli 0.19.0 → 0.19.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 +13 -1
- package/dist/agent-commands.js +7 -2
- package/dist/args.d.ts +1 -1
- package/dist/args.js +1 -1
- package/dist/initial-prompt.d.ts +15 -2
- package/dist/initial-prompt.js +29 -4
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.19.
|
|
3
|
+
## 0.19.1 (not yet published)
|
|
4
|
+
|
|
5
|
+
- A first prompt is never lost to a slow or unusual start. `--prompt` / `--prompt-file` types the prompt only into the
|
|
6
|
+
agent's input box, never into one of its dialogs ("❯ 1. Yes"). If the terminal never shows the input box and
|
|
7
|
+
nothing was typed, the prompt goes to the conversation's own inbox, which delivers it once the agent reads input.
|
|
8
|
+
`--json` then reports `"initial_prompt": {"submitted": false, "queued": true}` instead of failing with
|
|
9
|
+
`initial_prompt_failed`.
|
|
10
|
+
- Brings the Shardflux app for Ubuntu 0.1.3: Try it once never waits silently. A demo agent still waiting with nothing
|
|
11
|
+
said 45 s after its start gets its task once more through its conversation, and one line says so; still nothing
|
|
12
|
+
45 s later, one line says it hasn't started. The optional dependency on `@shardflux/desktop-linux-x64` / `-arm64` is
|
|
13
|
+
exactly 0.1.3.
|
|
14
|
+
|
|
15
|
+
## 0.19.0 (2026-10-11)
|
|
4
16
|
|
|
5
17
|
- A new conversation on an account without a saved machine gets its first prompt in seconds, whatever is in
|
|
6
18
|
`~/.claude` and `~/.codex`. The prompt is typed first, and this computer's Claude and Codex config is copied into
|
package/dist/agent-commands.js
CHANGED
|
@@ -28,6 +28,7 @@ import { openInFor } from "./t3-mode.js";
|
|
|
28
28
|
import { openClaudeDesktopSession, openDesktopWorkspace, queueT3Prewarm } from "./agent-target-commands.js";
|
|
29
29
|
import { submitInitialPrompt, prepareInitialCodexProject } from "./initial-prompt.js";
|
|
30
30
|
import { conversationWorkspace, conversationsUnavailable, getConversation, listConversations, resumeConversation } from "./conversations.js";
|
|
31
|
+
import { conversationClient } from "./conversation-commands.js";
|
|
31
32
|
import { conversationTerminal, reopenConversation } from "./conversation-terminal.js";
|
|
32
33
|
import { CHOICE_PENDING, choiceReport, hasChoice, launchChoice, modelChoice, runFlags } from "./agent-models.js";
|
|
33
34
|
const active = (session) => session.state === 'running' || session.state === 'starting';
|
|
@@ -145,8 +146,12 @@ export async function terminal(ctx, ws, app, agent, project, values, setupHash,
|
|
|
145
146
|
throw new Error(`project setup could not be queued: ${queued.stderr.trim() || `exit ${queued.exitCode}`}`);
|
|
146
147
|
});
|
|
147
148
|
store.save(project, app);
|
|
149
|
+
let delivery;
|
|
148
150
|
if (typeof values.initialPrompt === 'string') {
|
|
149
|
-
|
|
151
|
+
const text = values.initialPrompt;
|
|
152
|
+
// A conversation's guest delivers the prompt itself when this terminal never showed its input box (0.19.1+).
|
|
153
|
+
const queue = conversation ? () => conversationClient(ctx).conversations.send(conversation.id, text, { signal: ctx.signal }) : undefined;
|
|
154
|
+
delivery = await submitInitialPrompt(signal => cellConnector(cell, session.session_id)(0, signal), text, ws.key, ctx.signal, app, bytes => cell.pty.input(session.session_id, bytes), queue);
|
|
150
155
|
}
|
|
151
156
|
// Work that must never delay the first prompt (the laptop's agent config, 0.19.0+) starts once the agent has it.
|
|
152
157
|
const after = conversation?.afterStart?.();
|
|
@@ -154,7 +159,7 @@ export async function terminal(ctx, ws, app, agent, project, values, setupHash,
|
|
|
154
159
|
await preparation;
|
|
155
160
|
// conversation (0.16.0+): which conversation this session is, also when its computer hosts several threads.
|
|
156
161
|
// model_choice (0.17.0+): the requested model and effort, and whether this thread started with them.
|
|
157
|
-
ctx.io.stdout.write(json({ workspace: ws.data, session, ...(conversation ? { conversation: { id: conversation.id } } : {}), ...(
|
|
162
|
+
ctx.io.stdout.write(json({ workspace: ws.data, session, ...(conversation ? { conversation: { id: conversation.id } } : {}), ...(delivery ? { initial_prompt: delivery === 'submitted' ? { submitted: true } : { submitted: false, queued: true } } : {}),
|
|
158
163
|
...(hasChoice(explicit) ? { model_choice: choiceReport(explicit, applied) } : {}) }));
|
|
159
164
|
await after;
|
|
160
165
|
return 0;
|
package/dist/args.d.ts
CHANGED
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.19.
|
|
16
|
+
export const CLI_VERSION = '0.19.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).' },
|
package/dist/initial-prompt.d.ts
CHANGED
|
@@ -22,5 +22,18 @@ export declare function readInitialImages(ctx: Ctx, values: Values, prompt: stri
|
|
|
22
22
|
export declare function promptWithImages(prompt: string, images: readonly InitialImage[]): string;
|
|
23
23
|
/** Writes the images into the conversation VM before the first message refers to them. */
|
|
24
24
|
export declare function uploadInitialImages(cell: Pick<CellClient, 'files'>, images: readonly InitialImage[], signal?: AbortSignal): Promise<void>;
|
|
25
|
-
/**
|
|
26
|
-
export declare
|
|
25
|
+
/** The conversation inbox takes a message of at most this many characters (the API's text limit). */
|
|
26
|
+
export declare const INBOX_MAX_CHARS = 65536;
|
|
27
|
+
/**
|
|
28
|
+
* The harness's input box is the last thing that drew its prompt glyph (0.19.1+). Claude Code and Codex draw the same
|
|
29
|
+
* glyph as the selection cursor of their dialogs ("❯ 1. Yes, I trust this folder"); a prompt pasted there would pick
|
|
30
|
+
* options. The guest's own first-message delivery reads the screen the same way (coding 39+).
|
|
31
|
+
*/
|
|
32
|
+
export declare function inputBoxReady(tail: string, glyph: string): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Wait for the native terminal to enable paste before submitting one first turn. No blind timing or input replay.
|
|
35
|
+
* When nothing was typed yet and the terminal never became ready, `queue` hands the prompt to the conversation's guest
|
|
36
|
+
* (its inbox, which waits for the harness and retries Enter) and the result is `queued` (0.19.1+): a first prompt is
|
|
37
|
+
* never lost to a slow start.
|
|
38
|
+
*/
|
|
39
|
+
export declare function submitInitialPrompt(connect: (signal: AbortSignal) => Promise<TerminalConnection>, prompt: string, workspaceKey: string, signal: AbortSignal, app?: 'claude' | 'codex', write?: (bytes: Uint8Array) => Promise<unknown>, queue?: () => Promise<unknown>): Promise<'submitted' | 'queued'>;
|
package/dist/initial-prompt.js
CHANGED
|
@@ -137,8 +137,26 @@ export async function uploadInitialImages(cell, images, signal) {
|
|
|
137
137
|
await cell.files.write(image.path, image.data, { mode: '0600', createParents: true });
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
|
-
/**
|
|
141
|
-
export
|
|
140
|
+
/** The conversation inbox takes a message of at most this many characters (the API's text limit). */
|
|
141
|
+
export const INBOX_MAX_CHARS = 65536;
|
|
142
|
+
/**
|
|
143
|
+
* The harness's input box is the last thing that drew its prompt glyph (0.19.1+). Claude Code and Codex draw the same
|
|
144
|
+
* glyph as the selection cursor of their dialogs ("❯ 1. Yes, I trust this folder"); a prompt pasted there would pick
|
|
145
|
+
* options. The guest's own first-message delivery reads the screen the same way (coding 39+).
|
|
146
|
+
*/
|
|
147
|
+
export function inputBoxReady(tail, glyph) {
|
|
148
|
+
// eslint-disable-next-line no-control-regex -- Native terminal escapes are removed before reading the screen text.
|
|
149
|
+
const plain = tail.replace(/\x1b\[[0-?]*[ -/]*[@-~]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g, ' ');
|
|
150
|
+
const at = plain.lastIndexOf(glyph);
|
|
151
|
+
return at >= 0 && !/^[\s\u00a0]*\d+\./.test(plain.slice(at + glyph.length));
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Wait for the native terminal to enable paste before submitting one first turn. No blind timing or input replay.
|
|
155
|
+
* When nothing was typed yet and the terminal never became ready, `queue` hands the prompt to the conversation's guest
|
|
156
|
+
* (its inbox, which waits for the harness and retries Enter) and the result is `queued` (0.19.1+): a first prompt is
|
|
157
|
+
* never lost to a slow start.
|
|
158
|
+
*/
|
|
159
|
+
export async function submitInitialPrompt(connect, prompt, workspaceKey, signal, app = 'claude', write, queue) {
|
|
142
160
|
const bounded = AbortSignal.any([signal, AbortSignal.timeout(20000)]);
|
|
143
161
|
let connection;
|
|
144
162
|
let sent = false, pasted = false;
|
|
@@ -159,7 +177,7 @@ export async function submitInitialPrompt(connect, prompt, workspaceKey, signal,
|
|
|
159
177
|
// eslint-disable-next-line no-control-regex -- Strip native terminal ANSI before matching prompt echo.
|
|
160
178
|
const plain = tail.replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, '').replace(/\s/g, '');
|
|
161
179
|
if (sent && bytes.length)
|
|
162
|
-
return;
|
|
180
|
+
return 'submitted';
|
|
163
181
|
if (pasted) {
|
|
164
182
|
// A long prompt overflows the input box: a 24-row terminal shows only its end, so either end confirms the paste.
|
|
165
183
|
const compact = prompt.replace(/\s/g, '');
|
|
@@ -174,7 +192,7 @@ export async function submitInitialPrompt(connect, prompt, workspaceKey, signal,
|
|
|
174
192
|
tail = '';
|
|
175
193
|
continue;
|
|
176
194
|
}
|
|
177
|
-
const ready = app === 'claude' ?
|
|
195
|
+
const ready = inputBoxReady(tail, app === 'claude' ? '❯' : '›');
|
|
178
196
|
if (!ready || !pasteEnabled)
|
|
179
197
|
continue;
|
|
180
198
|
const paste = Buffer.from(`\x1b[200~${prompt}\x1b[201~`);
|
|
@@ -188,6 +206,13 @@ export async function submitInitialPrompt(connect, prompt, workspaceKey, signal,
|
|
|
188
206
|
throw new Error('The agent terminal closed before accepting a prompt.');
|
|
189
207
|
}
|
|
190
208
|
catch {
|
|
209
|
+
if (!pasted && queue && !signal.aborted && prompt.length <= INBOX_MAX_CHARS) {
|
|
210
|
+
try {
|
|
211
|
+
await queue();
|
|
212
|
+
return 'queued';
|
|
213
|
+
}
|
|
214
|
+
catch { /* reported as not submitted below */ }
|
|
215
|
+
}
|
|
191
216
|
throw new CodedError('initial_prompt_failed', `Created ${workspaceKey}, but the initial prompt ${sent ? 'could not be confirmed' : 'was not submitted'}. Open that conversation before starting another.`, undefined, { workspace_key: workspaceKey, submitted: sent });
|
|
192
217
|
}
|
|
193
218
|
finally {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shardflux/cli",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.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,9 +58,9 @@
|
|
|
58
58
|
"typescript-eslint": "8.70.1"
|
|
59
59
|
},
|
|
60
60
|
"optionalDependencies": {
|
|
61
|
-
"@shardflux/desktop-linux-x64": "0.1.
|
|
62
|
-
"@shardflux/desktop-linux-arm64": "0.1.
|
|
63
|
-
"@shardflux/menubar-darwin": "^0.7.
|
|
61
|
+
"@shardflux/desktop-linux-x64": "0.1.3",
|
|
62
|
+
"@shardflux/desktop-linux-arm64": "0.1.3",
|
|
63
|
+
"@shardflux/menubar-darwin": "^0.7.1"
|
|
64
64
|
},
|
|
65
65
|
"scripts": {
|
|
66
66
|
"build": "node scripts/build.mjs",
|