@shardflux/cli 0.18.0-next.5 → 0.18.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 +61 -8
- package/README.md +92 -33
- package/dist/account-args.js +5 -3
- package/dist/account-commands.d.ts +4 -1
- package/dist/account-commands.js +104 -14
- package/dist/account-pool.d.ts +31 -0
- package/dist/account-pool.js +313 -0
- package/dist/agent-setup.d.ts +38 -0
- package/dist/agent-setup.js +116 -0
- package/dist/agent-target-commands.js +10 -1
- package/dist/agent-targets.d.ts +11 -0
- package/dist/agent-targets.js +22 -4
- package/dist/args.d.ts +3 -1
- package/dist/args.js +50 -5
- package/dist/claude-cloud-login.d.ts +11 -0
- package/dist/claude-cloud-login.js +49 -39
- package/dist/cli-proxy-hub.d.ts +26 -1
- package/dist/cli-proxy-hub.js +60 -4
- package/dist/commands.d.ts +3 -0
- package/dist/commands.js +7 -3
- package/dist/conversation-commands.d.ts +5 -0
- package/dist/conversation-commands.js +8 -2
- package/dist/conversations.d.ts +9 -0
- package/dist/desktop-linux.js +6 -5
- package/dist/filesystem-commands.d.ts +12 -2
- package/dist/filesystem-commands.js +46 -18
- package/dist/git-remote-bin.js +0 -0
- package/dist/handlers.js +2 -1
- package/dist/input.d.ts +4 -2
- package/dist/input.js +6 -3
- package/dist/main.js +19 -8
- package/dist/menubar.js +1 -1
- package/dist/t3-asar.d.ts +12 -2
- package/dist/t3-asar.js +14 -5
- package/dist/t3-launch.js +4 -1
- package/dist/t3-linux.d.ts +12 -13
- package/dist/t3-linux.js +81 -87
- package/dist/t3-mode.d.ts +4 -3
- package/dist/t3-mode.js +15 -14
- package/dist/t3-version.d.ts +8 -0
- package/dist/t3-version.js +42 -0
- package/package.json +21 -7
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { codingAgentShell } from "./agent-setup.js";
|
|
1
2
|
import { waitTimeout } from "./commands.js";
|
|
2
3
|
import { UsageError } from "./errors.js";
|
|
3
4
|
import { projectIdentity, normalizeRemote } from "./project-identity.js";
|
|
@@ -37,9 +38,17 @@ async function saved(ctx, values, save, level) {
|
|
|
37
38
|
const state = await save({ signal: ctx.signal, ...(wait ? { wait } : {}) });
|
|
38
39
|
if (ctx.json)
|
|
39
40
|
return output(ctx, state);
|
|
40
|
-
|
|
41
|
+
const moved = movedLine(state, level);
|
|
42
|
+
ctx.io.stdout.write(wait ? `${level}: version ${state.version} saved${moved}\n` : state.pending ? `${filesystemLine(state)}; shard ${level} shows when it is ready${moved}\n` : `${filesystemLine(state)}${moved}\n`);
|
|
41
43
|
return 0;
|
|
42
44
|
}
|
|
45
|
+
/** The save's one line about software under the home that went into the image (0.18.0+), as the in-VM shard says it. */
|
|
46
|
+
export function movedLine(state, level) {
|
|
47
|
+
const moved = state.moved_to_image ?? [];
|
|
48
|
+
if (moved.length === 0)
|
|
49
|
+
return '';
|
|
50
|
+
return `; ${moved.length} large tool folder${moved.length === 1 ? '' : 's'} went into the ${level} image (${moved.join(', ')}); they keep working at the same paths`;
|
|
51
|
+
}
|
|
43
52
|
const rollbackTo = (value) => {
|
|
44
53
|
if (value === undefined)
|
|
45
54
|
return undefined;
|
|
@@ -48,34 +57,53 @@ const rollbackTo = (value) => {
|
|
|
48
57
|
throw new UsageError('--to must be a saved version number');
|
|
49
58
|
return n;
|
|
50
59
|
};
|
|
60
|
+
/** Creates or resumes the machine VM of the saved machine version; the first one gets this computer's agent config. */
|
|
61
|
+
/** The account's machine VM, created or resumed; `sync: false` skips the first-open config sync (an account sign-in). */
|
|
62
|
+
export async function openMachine(ctx, state, opts = {}) {
|
|
63
|
+
const cloud = ctx.cloud();
|
|
64
|
+
state ??= await cloud.machine.get({ signal: ctx.signal });
|
|
65
|
+
const key = `machine/${state.organization_id}/v${state.version}`;
|
|
66
|
+
const existing = await cloud.workspaces.findByKey(key, { includeDeleted: false, signal: ctx.signal });
|
|
67
|
+
if (!existing)
|
|
68
|
+
progress(ctx, `Creating your machine VM (${key}); the first open takes about a minute…`);
|
|
69
|
+
else if (existing.data.observed_state !== 'running')
|
|
70
|
+
progress(ctx, `Resuming your machine VM (${key})…`);
|
|
71
|
+
const ws = await cloud.workspaces.open({ key, template: state.template ? `${state.template.slug}@${state.template.version}` : 'coding',
|
|
72
|
+
labels: { ...existing?.labels, shard_layer: 'machine', shard_machine: state.organization_id, repo_path: '/home/user', app: 'claude', agent: 'machine' },
|
|
73
|
+
wait: { timeoutMs: ctx.wakeTimeoutMs, signal: ctx.signal } });
|
|
74
|
+
if (state.version === 0 && opts.sync !== false) {
|
|
75
|
+
progress(ctx, 'Syncing your Claude and Codex config into the machine…');
|
|
76
|
+
await syncAgentConfig(ctx, ws);
|
|
77
|
+
}
|
|
78
|
+
return { state, ws };
|
|
79
|
+
}
|
|
51
80
|
export const FILESYSTEM_HANDLERS = {
|
|
52
81
|
async machine(ctx) {
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
if (ctx.json || !ctx.io.stdin.isTTY)
|
|
82
|
+
const state = await ctx.cloud().machine.get({ signal: ctx.signal });
|
|
83
|
+
// Without a terminal (or in a coding agent's shell, 0.17.1+) there is nobody to type to the machine agent: the status.
|
|
84
|
+
if (ctx.json || !ctx.io.stdin.isTTY || codingAgentShell(ctx.io.env))
|
|
56
85
|
return output(ctx, state);
|
|
57
|
-
const
|
|
58
|
-
const existing = await cloud.workspaces.findByKey(key, { includeDeleted: false, signal: ctx.signal });
|
|
59
|
-
if (!existing)
|
|
60
|
-
progress(ctx, `Creating your machine VM (${key}); the first open takes about a minute…`);
|
|
61
|
-
else if (existing.data.observed_state !== 'running')
|
|
62
|
-
progress(ctx, `Resuming your machine VM (${key})…`);
|
|
63
|
-
const ws = await cloud.workspaces.open({ key, template: state.template ? `${state.template.slug}@${state.template.version}` : 'coding',
|
|
64
|
-
labels: { ...existing?.labels, shard_layer: 'machine', shard_machine: state.organization_id, repo_path: '/home/user', app: 'claude', agent: 'machine' },
|
|
65
|
-
wait: { timeoutMs: ctx.wakeTimeoutMs, signal: ctx.signal } });
|
|
66
|
-
if (state.version === 0) {
|
|
67
|
-
progress(ctx, 'Syncing your Claude and Codex config into the machine…');
|
|
68
|
-
await syncAgentConfig(ctx, ws);
|
|
69
|
-
}
|
|
86
|
+
const { ws } = await openMachine(ctx, state);
|
|
70
87
|
return AGENT_HANDLERS['agents open'](ctx, {}, [ws.key]);
|
|
71
88
|
},
|
|
89
|
+
/** `shard machine open` (0.17.1+): the machine VM without the agent terminal, for an agent that builds the machine. */
|
|
90
|
+
async 'machine open'(ctx) {
|
|
91
|
+
const { state, ws } = await openMachine(ctx);
|
|
92
|
+
if (ctx.json)
|
|
93
|
+
ctx.io.stdout.write(JSON.stringify({ workspace: ws.data, key: ws.key, machine: state, next: [`shard ws exec ${ws.key} -- bash -lc '<install commands>'`, `shard files write ${ws.key} <path> --from -`, 'shard machine save --wait'] }) + '\n');
|
|
94
|
+
else {
|
|
95
|
+
ctx.io.stdout.write(`${ws.key}\n`);
|
|
96
|
+
progress(ctx, `Install with shard ws exec ${ws.key} -- bash -lc '…', write files with shard files write ${ws.key} <path> --from -, then shard machine save --wait.`);
|
|
97
|
+
}
|
|
98
|
+
return 0;
|
|
99
|
+
},
|
|
72
100
|
async 'machine save'(ctx, values) {
|
|
73
101
|
const cloud = ctx.cloud();
|
|
74
102
|
const state = await cloud.machine.get({ signal: ctx.signal });
|
|
75
103
|
const ws = await cloud.workspaces.findByKey(`machine/${state.organization_id}/v${state.version}`, { signal: ctx.signal }) ??
|
|
76
104
|
(state.workspace_id ? await cloud.workspaces.get(state.workspace_id) : null);
|
|
77
105
|
if (!ws)
|
|
78
|
-
throw new UsageError('Open
|
|
106
|
+
throw new UsageError('Open your machine and change it first, then run shard machine save', 'shard machine in a terminal, or shard machine open --json for an agent (0.17.1+)');
|
|
79
107
|
return saved(ctx, values, options => cloud.machine.save({ workspaceId: ws.id }, options), 'machine');
|
|
80
108
|
},
|
|
81
109
|
async 'machine rollback'(ctx, values) {
|
package/dist/git-remote-bin.js
CHANGED
|
File without changes
|
package/dist/handlers.js
CHANGED
|
@@ -20,4 +20,5 @@ import { TWIN_HANDLERS } from "./worktree-twin-engine.js";
|
|
|
20
20
|
import { CODEX_CLOUD_HANDLERS } from "./codex-cloud.js";
|
|
21
21
|
import { THREAD_HANDLERS } from "./thread-on-computer.js";
|
|
22
22
|
import { CLI_PROXY_HANDLERS } from "./cli-proxy.js";
|
|
23
|
-
|
|
23
|
+
import { ACCOUNT_POOL_HANDLERS } from "./account-pool.js";
|
|
24
|
+
export const HANDLERS = { ...DEVICE_HANDLERS, ...CODEX_CLOUD_HANDLERS, ...CLI_PROXY_HANDLERS, ...THREAD_HANDLERS, ...CONVERSATION_HANDLERS, ...LOCAL_CONVERSATION_HANDLERS, ...TWIN_HANDLERS, ...CREDENTIAL_HANDLERS, ...AGENT_CREDENTIAL_HANDLERS, ...ACCOUNT_POOL_HANDLERS, ...KEY_HANDLERS, ...ACCOUNT_HANDLERS, ...AGENT_HANDLERS, ...CENTER_HANDLERS, ...TARGET_HANDLERS, ...AGENT_GIT_HANDLERS, ...MENUBAR_HANDLERS, ...FILESYSTEM_HANDLERS, ...UPDATE_HANDLERS };
|
package/dist/input.d.ts
CHANGED
|
@@ -13,8 +13,10 @@ export interface ErrWriter {
|
|
|
13
13
|
}
|
|
14
14
|
export declare class Input {
|
|
15
15
|
#private;
|
|
16
|
-
constructor(stdin: StdinLike, stderr: ErrWriter
|
|
17
|
-
|
|
16
|
+
constructor(stdin: StdinLike, stderr: ErrWriter, opts?: {
|
|
17
|
+
agentShell?: boolean;
|
|
18
|
+
});
|
|
19
|
+
/** True when standard input is a terminal `shard` can prompt on (and not a coding agent's shell). */
|
|
18
20
|
get interactive(): boolean;
|
|
19
21
|
/** The next line of piped input without its line ending, or null at the end of input. */
|
|
20
22
|
line(): Promise<string | null>;
|
package/dist/input.js
CHANGED
|
@@ -16,18 +16,21 @@ const MAX_PIPED_BYTES = 64 * 1024;
|
|
|
16
16
|
export class Input {
|
|
17
17
|
#stdin;
|
|
18
18
|
#stderr;
|
|
19
|
+
/** In a coding agent's shell nobody types at a prompt, even on a terminal (0.17.1+). */
|
|
20
|
+
#agentShell;
|
|
19
21
|
#it = null;
|
|
20
22
|
#decoder = new TextDecoder('utf-8');
|
|
21
23
|
#buf = '';
|
|
22
24
|
#eof = false;
|
|
23
25
|
#read = 0;
|
|
24
|
-
constructor(stdin, stderr) {
|
|
26
|
+
constructor(stdin, stderr, opts = {}) {
|
|
25
27
|
this.#stdin = stdin;
|
|
26
28
|
this.#stderr = stderr;
|
|
29
|
+
this.#agentShell = opts.agentShell === true;
|
|
27
30
|
}
|
|
28
|
-
/** True when standard input is a terminal `shard` can prompt on. */
|
|
31
|
+
/** True when standard input is a terminal `shard` can prompt on (and not a coding agent's shell). */
|
|
29
32
|
get interactive() {
|
|
30
|
-
return this.#stdin.isTTY === true;
|
|
33
|
+
return this.#stdin.isTTY === true && !this.#agentShell;
|
|
31
34
|
}
|
|
32
35
|
async #more() {
|
|
33
36
|
if (this.#eof)
|
package/dist/main.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
import { homedir, hostname } from 'node:os';
|
|
14
14
|
import { statSync } from 'node:fs';
|
|
15
15
|
import { SDK_VERSION, Shardflux, ShardfluxAccount, isSessionToken } from '@shardflux/sdk';
|
|
16
|
-
import { CLI_VERSION, CENTER_COMMAND, ONBOARDING_COMMAND, PROJECT_COMMAND,
|
|
16
|
+
import { CLI_VERSION, CENTER_COMMAND, GUIDE_COMMAND, ONBOARDING_COMMAND, PROJECT_COMMAND, parseCommandLine, resolveApiUrl, resolveWake, resolveWakeTimeout } from "./args.js";
|
|
17
17
|
import { commandCenter } from "./command-center.js";
|
|
18
18
|
import { launchProjectCommand } from "./conversation-terminal.js";
|
|
19
19
|
import { runAcp } from "./acp.js";
|
|
@@ -21,6 +21,8 @@ import { registerInstalledT3 } from "./t3-provider.js";
|
|
|
21
21
|
import { refreshForwarderAgent } from "./cli-proxy.js";
|
|
22
22
|
import { runAcpLogin } from "./acp-login.js";
|
|
23
23
|
import { terminalOnboarding } from "./terminal-onboarding.js";
|
|
24
|
+
import { codingAgentShell, setupGuideText } from "./agent-setup.js";
|
|
25
|
+
import { agentSetupGuide } from "./account-commands.js";
|
|
24
26
|
import { tryLinuxDesktop } from "./desktop-linux.js";
|
|
25
27
|
import { startLinuxHelper } from "./linux-helper.js";
|
|
26
28
|
import { runDeviceHelper } from "./device-helper.js";
|
|
@@ -40,7 +42,7 @@ import { CLI_DISTRIBUTION, NOTICE_WAIT_MS, UpdateCheck, noticeOf, updateCheckDis
|
|
|
40
42
|
const KEY_SHAPE = /^sfk_[a-z2-7]{16}_[A-Za-z0-9]+$/;
|
|
41
43
|
// Printable (no C0 controls or DEL), as the API's AgentLabel pattern.
|
|
42
44
|
const isLabel = (s) => s.length >= 1 && s.length <= 100 && ![...s].some((ch) => ch.charCodeAt(0) < 0x20 || ch.charCodeAt(0) === 0x7f);
|
|
43
|
-
const LOGIN_HINT = 'shard auth login --
|
|
45
|
+
const LOGIN_HINT = 'shard auth login --browser';
|
|
44
46
|
const STEP_UP_HINT = `confirm your password, then run the command again (valid 10 minutes): printf '%s\\n' "$PASSWORD" | shard auth step-up (add --code <totp> with two-factor authentication)`;
|
|
45
47
|
/** A sleep that ends early when the signal aborts (so Ctrl-C never waits out a poll interval). */
|
|
46
48
|
export function abortableSleep(signal) {
|
|
@@ -427,9 +429,12 @@ export async function run(argv, io, opts = {}) {
|
|
|
427
429
|
io.stderr.write(redact(text, ...secrets));
|
|
428
430
|
return code;
|
|
429
431
|
};
|
|
432
|
+
// A coding agent's shell (0.17.1+) is no terminal to attach to or prompt on, even when it is a PTY.
|
|
433
|
+
const agentShell = codingAgentShell(io.env);
|
|
434
|
+
const tty = io.stdin.isTTY === true && io.stdout.isTTY !== false && !agentShell;
|
|
430
435
|
let parsed;
|
|
431
436
|
try {
|
|
432
|
-
parsed = parseCommandLine(argv, { tty
|
|
437
|
+
parsed = parseCommandLine(argv, { tty, isProjectPath: (path) => { try {
|
|
433
438
|
return statSync(path).isDirectory();
|
|
434
439
|
}
|
|
435
440
|
catch {
|
|
@@ -449,10 +454,11 @@ export async function run(argv, io, opts = {}) {
|
|
|
449
454
|
io.stdout.write(wantsJson ? json({ cli: CLI_VERSION, sdk: SDK_VERSION }) : `shard ${CLI_VERSION} (@shardflux/sdk ${SDK_VERSION})\n`);
|
|
450
455
|
return EXIT.ok;
|
|
451
456
|
}
|
|
452
|
-
|
|
457
|
+
// shard ./ attaches Claude Code or Codex to this terminal: without one (or in a coding agent's shell) it says how to
|
|
458
|
+
// start the conversation instead of starting one nobody can see (0.17.1+: a usage error, before 0.17.1 its help).
|
|
459
|
+
if (parsed.command === PROJECT_COMMAND && !tty && !parsed.global.json) {
|
|
453
460
|
await startupHousekeeping({ io, store, signal });
|
|
454
|
-
|
|
455
|
-
return EXIT.ok;
|
|
461
|
+
return report(new UsageError('shard ./ opens Claude Code or Codex in a terminal, and this shell has none.', 'ask your user to run shard ./ in their own terminal, or start the conversation detached: shard ./ --json --prompt "<task>" (shard ./ codex --json ... for Codex)'));
|
|
456
462
|
}
|
|
457
463
|
command = parsed.command.path.join(' ');
|
|
458
464
|
let ctx;
|
|
@@ -544,7 +550,7 @@ export async function run(argv, io, opts = {}) {
|
|
|
544
550
|
timing: showTiming,
|
|
545
551
|
traces,
|
|
546
552
|
store,
|
|
547
|
-
input: new Input(io.stdin, io.stderr),
|
|
553
|
+
input: new Input(io.stdin, io.stderr, { agentShell }),
|
|
548
554
|
distribution,
|
|
549
555
|
runtime: opts.runtime ?? null,
|
|
550
556
|
async clientVersions() {
|
|
@@ -606,7 +612,7 @@ export async function run(argv, io, opts = {}) {
|
|
|
606
612
|
else {
|
|
607
613
|
const saved = store.profile(apiUrl).session;
|
|
608
614
|
if (!saved)
|
|
609
|
-
throw new AuthConfigError(`Not signed in to ${apiUrl}.`, `sign in: ${LOGIN_HINT} (new here? shard
|
|
615
|
+
throw new AuthConfigError(`Not signed in to ${apiUrl}.`, `sign in: ${LOGIN_HINT} (new here? shard signup; setting Shardflux up for someone: shard setup --guide)`);
|
|
610
616
|
cred.sessionSource = 'file';
|
|
611
617
|
cred.email = saved.user?.email ?? null;
|
|
612
618
|
secrets.push(saved.token);
|
|
@@ -689,6 +695,11 @@ export async function run(argv, io, opts = {}) {
|
|
|
689
695
|
}
|
|
690
696
|
stopHelper = startLinuxHelper(ctx);
|
|
691
697
|
};
|
|
698
|
+
// Bare shard without a terminal (0.17.1+): the setup guide, read from local state only (never waits).
|
|
699
|
+
if (parsed.command === GUIDE_COMMAND) {
|
|
700
|
+
io.stdout.write(setupGuideText(agentSetupGuide(ctx)));
|
|
701
|
+
return EXIT.ok;
|
|
702
|
+
}
|
|
692
703
|
const handler = parsed.command.path[0] === 'acp' ? (ctx) => parsed.values.login === true ? runAcpLogin(ctx) : runAcp(ctx) : parsed.command.path[0] === 'device-helper' ? (ctx) => runDeviceHelper(ctx, { service: parsed.values.service === true }) : command === 'menubar refresh' ? runMenubarRefresh : parsed.command === PROJECT_COMMAND ? launchProjectCommand : parsed.command === ONBOARDING_COMMAND ? async (ctx, values) => await tryLinuxDesktop(ctx) ? 0 : terminalOnboarding(ctx, values, { startHelper }) : parsed.command === CENTER_COMMAND ? commandCenter : HANDLERS[parsed.command.path.join(' ')];
|
|
693
704
|
if (!handler)
|
|
694
705
|
return report(new UsageError(`unknown command "${parsed.command.path.join(' ')}"`));
|
package/dist/menubar.js
CHANGED
|
@@ -342,7 +342,7 @@ export function refreshMenubarInBackground(o) {
|
|
|
342
342
|
return false;
|
|
343
343
|
const manifest = (dir) => { try {
|
|
344
344
|
const m = JSON.parse(readFileSync(join(dir, 'manifest.json'), 'utf8'));
|
|
345
|
-
return m.id === 'dev.shardflux.Desktop' ? m.version : null;
|
|
345
|
+
return m.id === 'dev.shardflux.Desktop' && typeof m.version === 'string' ? m.version : null;
|
|
346
346
|
}
|
|
347
347
|
catch {
|
|
348
348
|
return null;
|
package/dist/t3-asar.d.ts
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
/** The server CLI inside the archive: what Electron runs as Node to issue T3 sessions. */
|
|
2
2
|
export declare const T3_SERVER_ENTRY = "apps/server/dist/bin.mjs";
|
|
3
|
-
/** The
|
|
4
|
-
export
|
|
3
|
+
/** The archive's package.json: whether it is T3 Code's, and its release version. */
|
|
4
|
+
export interface T3Package {
|
|
5
|
+
t3: boolean;
|
|
6
|
+
version: string | null;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* The archive's package.json (null when it has none): T3 Code's when its `name` is `t3code` (as 0.0.45 and the
|
|
10
|
+
* nightlies ship it) or its `productName` is T3 Code's, and its release version (e.g. `0.0.46-nightly.20261010.2908`).
|
|
11
|
+
*/
|
|
12
|
+
export declare function asarPackage(archive: string): T3Package | null;
|
|
13
|
+
/** The app's release version from its package.json, or null. */
|
|
14
|
+
export declare const asarVersion: (archive: string) => string | null;
|
|
5
15
|
/**
|
|
6
16
|
* Whether the server registers the ACP Registry driver. A version number alone is insufficient (0.0.45 lacks it while
|
|
7
17
|
* upstream main had it). Builds put the driver in `bin.mjs` itself, or (nightlies since October 2026) in a chunk the
|
package/dist/t3-asar.js
CHANGED
|
@@ -61,13 +61,22 @@ function read(a, path) {
|
|
|
61
61
|
const bytes = Buffer.alloc(size);
|
|
62
62
|
return readSync(a.fd, bytes, 0, size, offset) === size ? bytes : undefined;
|
|
63
63
|
}
|
|
64
|
-
/**
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
64
|
+
/**
|
|
65
|
+
* The archive's package.json (null when it has none): T3 Code's when its `name` is `t3code` (as 0.0.45 and the
|
|
66
|
+
* nightlies ship it) or its `productName` is T3 Code's, and its release version (e.g. `0.0.46-nightly.20261010.2908`).
|
|
67
|
+
*/
|
|
68
|
+
export function asarPackage(archive) {
|
|
69
|
+
return fact(archive, 'app', a => {
|
|
70
|
+
const bytes = read(a, 'package.json');
|
|
71
|
+
if (!bytes)
|
|
72
|
+
return null;
|
|
73
|
+
const doc = JSON.parse(bytes.toString());
|
|
74
|
+
return { t3: doc.name === 't3code' || (typeof doc.productName === 'string' && /^T3 Code(?: \(.+\))?$/.test(doc.productName)),
|
|
75
|
+
version: typeof doc.version === 'string' && RELEASE.test(doc.version) ? doc.version : null };
|
|
69
76
|
}, null) ?? null;
|
|
70
77
|
}
|
|
78
|
+
/** The app's release version from its package.json, or null. */
|
|
79
|
+
export const asarVersion = (archive) => asarPackage(archive)?.version ?? null;
|
|
71
80
|
/**
|
|
72
81
|
* Whether the server registers the ACP Registry driver. A version number alone is insufficient (0.0.45 lacks it while
|
|
73
82
|
* upstream main had it). Builds put the driver in `bin.mjs` itself, or (nightlies since October 2026) in a chunk the
|
package/dist/t3-launch.js
CHANGED
|
@@ -9,6 +9,7 @@ import { conversationWorkspace, getConversation } from "./conversations.js";
|
|
|
9
9
|
import { resolveOn } from "./thread-on-computer.js";
|
|
10
10
|
import { ensureCloudClaude } from "./claude-cloud-login.js";
|
|
11
11
|
import { modelChoice } from "./agent-models.js";
|
|
12
|
+
import { conversationPooled } from "./account-pool.js";
|
|
12
13
|
/**
|
|
13
14
|
* A `t3_*` refusal raised once the workspace, conversation or thread exists names them (details.workspace_key,
|
|
14
15
|
* conversation_id, t3_thread_id), so a client opens what exists instead of starting it again.
|
|
@@ -127,7 +128,9 @@ export async function openT3Conversation(ctx, conversationId, values = {}) {
|
|
|
127
128
|
let ws = await conversationWorkspace(ctx, conversation);
|
|
128
129
|
if (values.reachable !== undefined)
|
|
129
130
|
ws = await ctx.cloud().workspaces.open({ key: ws.key, ...agentReachability(values), wait: false });
|
|
130
|
-
|
|
131
|
+
// A pooled thread (0.18.0+) runs on the account hub, not on the account's cloud Claude login.
|
|
132
|
+
if (!conversationPooled(conversation))
|
|
133
|
+
await ensureCloudClaude(ctx, ws);
|
|
131
134
|
const { openDesktopWorkspace } = await import("./agent-target-commands.js");
|
|
132
135
|
return openDesktopWorkspace(ctx, ws, 't3', install.version ?? undefined, false, undefined, install, conversationId);
|
|
133
136
|
}
|
package/dist/t3-linux.d.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
type Env = Record<string, string | undefined>;
|
|
2
|
-
export type T3LinuxFormat = 'deb' | 'directory' | 'appimage' | 'flatpak'
|
|
2
|
+
export type T3LinuxFormat = 'deb' | 'directory' | 'appimage' | 'flatpak';
|
|
3
3
|
export interface T3LinuxApp {
|
|
4
4
|
format: T3LinuxFormat;
|
|
5
5
|
/** The desktop entry's file name (the id `gtk-launch` takes); null for an AppImage found by its file name. */
|
|
6
6
|
desktop_id: string | null;
|
|
7
7
|
/** What starts it: the entry's Exec without field codes, or the AppImage. */
|
|
8
8
|
exec: string[];
|
|
9
|
-
/** The app's resources/app.asar on this computer (deb, other unpacked installs, flatpak); null for an AppImage
|
|
9
|
+
/** The app's resources/app.asar on this computer (deb, other unpacked installs, flatpak); null for an AppImage. */
|
|
10
10
|
asar: string | null;
|
|
11
|
-
/** Flatpak: the app id and the app's files folder on this computer (mounted at /app in its sandbox). */
|
|
11
|
+
/** Flatpak (unverified layout): the app id and the app's files folder on this computer (mounted at /app in its sandbox). */
|
|
12
12
|
flatpak?: {
|
|
13
13
|
id: string;
|
|
14
14
|
files: string;
|
|
@@ -20,7 +20,7 @@ export interface T3LinuxInstall {
|
|
|
20
20
|
version: string | null;
|
|
21
21
|
linux: T3LinuxApp;
|
|
22
22
|
}
|
|
23
|
-
/** The launcher T3 (
|
|
23
|
+
/** The launcher T3 (from v0.0.46-preview.20261008.2800) writes to `<T3 home>/bin/t3` for its bundled CLI; for an AppImage it mounts the image itself. */
|
|
24
24
|
export declare const T3_LAUNCHER_MARKER = "Written by T3 Code: runs the desktop app's bundled t3 CLI.";
|
|
25
25
|
/**
|
|
26
26
|
* The folders scanned on Linux, in XDG precedence: the data home's and each data dir's `applications`, snapd's and
|
|
@@ -35,8 +35,8 @@ export declare function desktopExec(value: string): string[] | undefined;
|
|
|
35
35
|
export declare function linuxT3Apps(dirs: string[], env: Env): T3LinuxInstall[];
|
|
36
36
|
/**
|
|
37
37
|
* Whether this install runs the Shardflux provider (ACP mode): the server in its app.asar has the ACP Registry driver
|
|
38
|
-
* (deb, unpacked installs, flatpak). An AppImage is never mounted to look: its
|
|
39
|
-
*
|
|
38
|
+
* (deb, unpacked installs, flatpak). An AppImage is never mounted to look: its sessions come from T3's own launcher, so
|
|
39
|
+
* it is ACP from the first build that writes one (t3WritesLauncher); earlier builds start threads over SSH.
|
|
40
40
|
*/
|
|
41
41
|
export declare function linuxSupportsAcp(app: T3LinuxInstall): boolean;
|
|
42
42
|
/**
|
|
@@ -48,6 +48,11 @@ export declare function linuxServerCli(app: T3LinuxInstall, t3Home: string): {
|
|
|
48
48
|
command: string;
|
|
49
49
|
args: string[];
|
|
50
50
|
} | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* The environment T3 is opened with: the caller's, without what would make its Electron run as plain Node
|
|
53
|
+
* (ELECTRON_RUN_AS_NODE: `t3code` would exit at once while the open still looked fine) or detach its console.
|
|
54
|
+
*/
|
|
55
|
+
export declare function t3OpenEnv(env: Env): Env;
|
|
51
56
|
/**
|
|
52
57
|
* How T3 is opened (or brought to the front, as a second launch of T3 hands over to the running one): `gtk-launch`
|
|
53
58
|
* with its desktop entry when there is one, else its own program, started detached.
|
|
@@ -58,11 +63,5 @@ export declare function linuxOpenCommands(app: T3LinuxInstall, env: Env): Array<
|
|
|
58
63
|
detached: boolean;
|
|
59
64
|
}>;
|
|
60
65
|
/** Opens T3 Code on Linux. Nothing here ever stops a T3 process: a launcher that hangs is left alone. */
|
|
61
|
-
export declare function openLinuxT3(
|
|
62
|
-
/**
|
|
63
|
-
* When T3 last started, as the filesystem records it: the program's access time once it is later than its modification
|
|
64
|
-
* time (dpkg installs both at the build time; relatime updates the access time on the first start, then daily). Null
|
|
65
|
-
* before the first start and on noatime mounts.
|
|
66
|
-
*/
|
|
67
|
-
export declare function linuxLastUsed(path: string): string | null;
|
|
66
|
+
export declare function openLinuxT3(callerEnv: Env, app: T3LinuxInstall): Promise<void>;
|
|
68
67
|
export {};
|