@phnx-labs/agents-cli 1.20.71 → 1.20.73
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 +325 -1
- package/README.md +40 -4
- package/dist/bin/agents +0 -0
- package/dist/commands/activity.d.ts +12 -0
- package/dist/commands/activity.js +68 -0
- package/dist/commands/apply.d.ts +13 -0
- package/dist/commands/apply.js +28 -3
- package/dist/commands/browser.js +1 -1
- package/dist/commands/cloud.js +21 -11
- package/dist/commands/commands.js +2 -0
- package/dist/commands/doctor.js +8 -4
- package/dist/commands/events.d.ts +11 -10
- package/dist/commands/events.js +30 -21
- package/dist/commands/exec.d.ts +34 -0
- package/dist/commands/exec.js +337 -39
- package/dist/commands/feed.d.ts +13 -0
- package/dist/commands/feed.js +130 -29
- package/dist/commands/hq.d.ts +2 -0
- package/dist/commands/hq.js +58 -0
- package/dist/commands/lease.d.ts +19 -0
- package/dist/commands/lease.js +138 -11
- package/dist/commands/login.d.ts +2 -0
- package/dist/commands/login.js +123 -0
- package/dist/commands/logs.js +39 -10
- package/dist/commands/mcp.js +2 -0
- package/dist/commands/mine.d.ts +27 -0
- package/dist/commands/mine.js +207 -0
- package/dist/commands/monitors.js +79 -49
- package/dist/commands/plugins.js +1 -0
- package/dist/commands/profiles.js +158 -0
- package/dist/commands/repo.js +2 -60
- package/dist/commands/resource-view.d.ts +2 -0
- package/dist/commands/resource-view.js +8 -0
- package/dist/commands/resources.d.ts +5 -0
- package/dist/commands/resources.js +95 -0
- package/dist/commands/routines.d.ts +2 -0
- package/dist/commands/routines.js +139 -37
- package/dist/commands/secrets.js +234 -47
- package/dist/commands/send.d.ts +15 -0
- package/dist/commands/send.js +62 -0
- package/dist/commands/sessions-tail.d.ts +1 -0
- package/dist/commands/sessions-tail.js +20 -9
- package/dist/commands/sessions.d.ts +6 -0
- package/dist/commands/sessions.js +26 -3
- package/dist/commands/setup-mine.d.ts +18 -0
- package/dist/commands/setup-mine.js +106 -0
- package/dist/commands/setup-share.js +2 -2
- package/dist/commands/setup.js +3 -1
- package/dist/commands/share.d.ts +8 -1
- package/dist/commands/share.js +122 -37
- package/dist/commands/skills.js +2 -0
- package/dist/commands/ssh.d.ts +9 -0
- package/dist/commands/ssh.js +196 -26
- package/dist/commands/status.js +8 -2
- package/dist/commands/subagents.js +3 -1
- package/dist/commands/sync.js +1 -1
- package/dist/commands/teams.js +11 -2
- package/dist/commands/uninstall.d.ts +11 -0
- package/dist/commands/uninstall.js +158 -0
- package/dist/commands/utils.d.ts +29 -0
- package/dist/commands/utils.js +24 -0
- package/dist/commands/versions.js +120 -11
- package/dist/index.js +83 -12
- package/dist/lib/activity.d.ts +124 -0
- package/dist/lib/activity.js +542 -0
- package/dist/lib/agents.js +16 -5
- package/dist/lib/artifact-actions.d.ts +1 -1
- package/dist/lib/artifact-actions.js +1 -1
- package/dist/lib/ask-classifier.d.ts +2 -1
- package/dist/lib/ask-classifier.js +3 -3
- package/dist/lib/auth-health.d.ts +32 -4
- package/dist/lib/auth-health.js +40 -1
- package/dist/lib/auto-pull.js +8 -1
- package/dist/lib/brand.d.ts +40 -0
- package/dist/lib/brand.js +122 -0
- package/dist/lib/browser/drivers/ssh.js +4 -7
- package/dist/lib/browser/service.js +6 -8
- package/dist/lib/channels/providers/index.d.ts +2 -0
- package/dist/lib/channels/providers/index.js +20 -0
- package/dist/lib/channels/providers/mailbox.d.ts +2 -0
- package/dist/lib/channels/providers/mailbox.js +26 -0
- package/dist/lib/channels/providers/openclaw-telegram.d.ts +2 -0
- package/dist/lib/channels/providers/openclaw-telegram.js +34 -0
- package/dist/lib/channels/providers/rush.d.ts +6 -0
- package/dist/lib/channels/providers/rush.js +55 -0
- package/dist/lib/channels/registry.d.ts +42 -0
- package/dist/lib/channels/registry.js +20 -0
- package/dist/lib/channels/resolve.d.ts +11 -0
- package/dist/lib/channels/resolve.js +12 -0
- package/dist/lib/cli-resources.d.ts +18 -0
- package/dist/lib/cli-resources.js +53 -4
- package/dist/lib/cloud/codex.js +6 -3
- package/dist/lib/cloud/factory.d.ts +1 -0
- package/dist/lib/cloud/factory.js +10 -4
- package/dist/lib/cloud/rush.d.ts +3 -1
- package/dist/lib/cloud/rush.js +6 -1
- package/dist/lib/cloud/types.d.ts +2 -0
- package/dist/lib/codex-home.d.ts +35 -0
- package/dist/lib/codex-home.js +136 -0
- package/dist/lib/crabbox/cli.d.ts +44 -0
- package/dist/lib/crabbox/cli.js +120 -20
- package/dist/lib/crabbox/lease.d.ts +52 -1
- package/dist/lib/crabbox/lease.js +117 -16
- package/dist/lib/crabbox/progress.d.ts +31 -0
- package/dist/lib/crabbox/progress.js +76 -0
- package/dist/lib/crabbox/runtimes.d.ts +4 -0
- package/dist/lib/crabbox/runtimes.js +33 -8
- package/dist/lib/crabbox/setup-copy.d.ts +87 -0
- package/dist/lib/crabbox/setup-copy.js +127 -0
- package/dist/lib/daemon.d.ts +14 -5
- package/dist/lib/daemon.js +52 -9
- package/dist/lib/drive-sync.js +6 -3
- package/dist/lib/event-stream.d.ts +36 -0
- package/dist/lib/event-stream.js +68 -0
- package/dist/lib/events.d.ts +1 -1
- package/dist/lib/exec.d.ts +11 -0
- package/dist/lib/exec.js +23 -2
- package/dist/lib/feed-policy.d.ts +1 -1
- package/dist/lib/feed-policy.js +14 -9
- package/dist/lib/feed-ranking.d.ts +32 -0
- package/dist/lib/feed-ranking.js +224 -0
- package/dist/lib/feed.d.ts +35 -2
- package/dist/lib/feed.js +66 -3
- package/dist/lib/fleet/auth-sync.d.ts +53 -0
- package/dist/lib/fleet/auth-sync.js +92 -0
- package/dist/lib/fleet/remote-login.d.ts +187 -0
- package/dist/lib/fleet/remote-login.js +556 -0
- package/dist/lib/format.d.ts +30 -3
- package/dist/lib/format.js +34 -5
- package/dist/lib/fs-atomic.d.ts +7 -2
- package/dist/lib/fs-atomic.js +8 -12
- package/dist/lib/git.d.ts +16 -0
- package/dist/lib/git.js +79 -2
- package/dist/lib/heal.js +11 -3
- package/dist/lib/hosts/logs.d.ts +12 -0
- package/dist/lib/hosts/logs.js +18 -0
- package/dist/lib/hosts/progress.d.ts +28 -10
- package/dist/lib/hosts/progress.js +79 -22
- package/dist/lib/hosts/remote-cmd.js +4 -0
- package/dist/lib/hq/floor.d.ts +87 -0
- package/dist/lib/hq/floor.js +226 -0
- package/dist/lib/menubar/install-menubar.js +14 -20
- package/dist/lib/notify.d.ts +1 -0
- package/dist/lib/notify.js +3 -3
- package/dist/lib/open-url.d.ts +2 -0
- package/dist/lib/open-url.js +19 -0
- package/dist/lib/openclaw-keychain.d.ts +56 -0
- package/dist/lib/openclaw-keychain.js +236 -0
- package/dist/lib/overdue.js +10 -0
- package/dist/lib/permissions.d.ts +44 -1
- package/dist/lib/permissions.js +284 -7
- package/dist/lib/project-launch.d.ts +6 -12
- package/dist/lib/project-launch.js +13 -228
- package/dist/lib/project-resources.d.ts +7 -0
- package/dist/lib/project-resources.js +291 -0
- package/dist/lib/pty-client.d.ts +27 -0
- package/dist/lib/pty-client.js +136 -10
- package/dist/lib/refresh.js +14 -10
- package/dist/lib/resource-profiles.d.ts +26 -0
- package/dist/lib/resource-profiles.js +157 -0
- package/dist/lib/resources/permissions.js +7 -1
- package/dist/lib/resources/types.d.ts +1 -1
- package/dist/lib/resources.d.ts +1 -1
- package/dist/lib/resources.js +32 -3
- package/dist/lib/routines.d.ts +11 -0
- package/dist/lib/routines.js +56 -15
- package/dist/lib/runner.d.ts +1 -0
- package/dist/lib/runner.js +66 -5
- package/dist/lib/secrets/bundles.d.ts +16 -3
- package/dist/lib/secrets/bundles.js +206 -37
- package/dist/lib/secrets/icloud-import.d.ts +2 -2
- package/dist/lib/secrets/icloud-import.js +9 -6
- package/dist/lib/secrets/mcp.js +1 -1
- package/dist/lib/secrets/vault-age-helper.d.ts +1 -0
- package/dist/lib/secrets/vault-age-helper.js +34 -0
- package/dist/lib/secrets/vault.d.ts +49 -0
- package/dist/lib/secrets/vault.js +397 -0
- package/dist/lib/self-heal/checks/path.js +3 -1
- package/dist/lib/self-heal/checks/shadowing.js +10 -2
- package/dist/lib/self-heal/checks/shims.js +19 -11
- package/dist/lib/session/cloud.d.ts +2 -2
- package/dist/lib/session/cloud.js +2 -2
- package/dist/lib/session/db.d.ts +4 -0
- package/dist/lib/session/db.js +44 -7
- package/dist/lib/session/discover.d.ts +2 -0
- package/dist/lib/session/discover.js +114 -3
- package/dist/lib/session/stream-render.d.ts +3 -0
- package/dist/lib/session/stream-render.js +130 -0
- package/dist/lib/session/types.d.ts +6 -0
- package/dist/lib/share/analytics.d.ts +13 -0
- package/dist/lib/share/analytics.js +45 -0
- package/dist/lib/share/config.d.ts +19 -5
- package/dist/lib/share/config.js +44 -8
- package/dist/lib/share/provision.d.ts +58 -6
- package/dist/lib/share/provision.js +97 -22
- package/dist/lib/share/publish.d.ts +36 -13
- package/dist/lib/share/publish.js +55 -8
- package/dist/lib/share/worker-template.js +83 -17
- package/dist/lib/shims.d.ts +36 -0
- package/dist/lib/shims.js +120 -16
- package/dist/lib/ssh-exec.d.ts +14 -0
- package/dist/lib/ssh-exec.js +57 -0
- package/dist/lib/staleness/detectors/hooks.js +25 -1
- package/dist/lib/staleness/detectors/permissions.js +66 -0
- package/dist/lib/staleness/detectors/workflows.js +20 -0
- package/dist/lib/staleness/writers/hooks.js +58 -4
- package/dist/lib/staleness/writers/permissions.js +2 -2
- package/dist/lib/staleness/writers/skills.js +1 -1
- package/dist/lib/staleness/writers/sources.d.ts +10 -1
- package/dist/lib/staleness/writers/sources.js +68 -1
- package/dist/lib/star-nudge.d.ts +45 -0
- package/dist/lib/star-nudge.js +91 -0
- package/dist/lib/startup/command-registry.d.ts +7 -1
- package/dist/lib/startup/command-registry.js +19 -3
- package/dist/lib/state.d.ts +2 -0
- package/dist/lib/state.js +3 -0
- package/dist/lib/subagents-registry.d.ts +2 -0
- package/dist/lib/subagents-registry.js +3 -0
- package/dist/lib/teams/parsers.js +214 -6
- package/dist/lib/teams/supervisor.d.ts +7 -0
- package/dist/lib/teams/supervisor.js +2 -1
- package/dist/lib/template.d.ts +1 -1
- package/dist/lib/template.js +1 -1
- package/dist/lib/triggers/webhook.js +36 -3
- package/dist/lib/types.d.ts +73 -0
- package/dist/lib/uninstall.d.ts +84 -0
- package/dist/lib/uninstall.js +347 -0
- package/dist/lib/usage.d.ts +13 -1
- package/dist/lib/usage.js +32 -14
- package/dist/lib/version.d.ts +40 -0
- package/dist/lib/version.js +94 -16
- package/dist/lib/versions.d.ts +27 -0
- package/dist/lib/versions.js +248 -79
- package/dist/lib/workflows.d.ts +2 -0
- package/dist/lib/workflows.js +88 -0
- package/package.json +2 -1
- package/dist/commands/daemon.d.ts +0 -10
- package/dist/commands/daemon.js +0 -121
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Channel-provider registry for `agents send` / `agents notify`.
|
|
3
|
+
*
|
|
4
|
+
* One primitive, many channels. A provider knows how to deliver a message over
|
|
5
|
+
* exactly one channel — the agent mailbox, a rush-daemon gateway (telegram /
|
|
6
|
+
* imessage / slack / discord), openclaw, or a future "app". Providers register
|
|
7
|
+
* themselves at module load; the command layer only ever calls
|
|
8
|
+
* `resolveChannelProvider(name).send(...)` — no channel is special-cased above
|
|
9
|
+
* this seam, so new channels (apps) slot in without touching the command.
|
|
10
|
+
*/
|
|
11
|
+
export interface SendOptions {
|
|
12
|
+
/** Channel-specific recipient id: a chat id, a mailbox/agent id, a Slack C0…, etc. */
|
|
13
|
+
target: string;
|
|
14
|
+
/** Channel thread id / timestamp (slack/telegram threads). */
|
|
15
|
+
thread?: string;
|
|
16
|
+
/** Absolute paths to file attachments. */
|
|
17
|
+
attachments?: string[];
|
|
18
|
+
/** Sender label (used by the mailbox provider). */
|
|
19
|
+
from?: string;
|
|
20
|
+
/** Resolve + build the delivery but do not actually send. */
|
|
21
|
+
dryRun?: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface SendResult {
|
|
24
|
+
ok: boolean;
|
|
25
|
+
/** Channel/provider name as surfaced back to the caller. */
|
|
26
|
+
channel: string;
|
|
27
|
+
/** Resolved recipient id, echoed for --json parity with `rush send`. */
|
|
28
|
+
id: string;
|
|
29
|
+
error?: string;
|
|
30
|
+
/** Echoed attachment paths (rush shape parity). */
|
|
31
|
+
attachments?: string[];
|
|
32
|
+
/** Mailbox provider returns the enqueued message id. */
|
|
33
|
+
msgId?: string;
|
|
34
|
+
}
|
|
35
|
+
export interface ChannelProvider {
|
|
36
|
+
/** Stable name used in `--channel` and as a `notify.transports` value. */
|
|
37
|
+
name: string;
|
|
38
|
+
send(text: string, opts: SendOptions): Promise<SendResult>;
|
|
39
|
+
}
|
|
40
|
+
export declare function registerChannelProvider(p: ChannelProvider): void;
|
|
41
|
+
export declare function resolveChannelProvider(name: string): ChannelProvider | undefined;
|
|
42
|
+
export declare function listChannelProviders(): string[];
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Channel-provider registry for `agents send` / `agents notify`.
|
|
3
|
+
*
|
|
4
|
+
* One primitive, many channels. A provider knows how to deliver a message over
|
|
5
|
+
* exactly one channel — the agent mailbox, a rush-daemon gateway (telegram /
|
|
6
|
+
* imessage / slack / discord), openclaw, or a future "app". Providers register
|
|
7
|
+
* themselves at module load; the command layer only ever calls
|
|
8
|
+
* `resolveChannelProvider(name).send(...)` — no channel is special-cased above
|
|
9
|
+
* this seam, so new channels (apps) slot in without touching the command.
|
|
10
|
+
*/
|
|
11
|
+
const REGISTRY = new Map();
|
|
12
|
+
export function registerChannelProvider(p) {
|
|
13
|
+
REGISTRY.set(p.name, p);
|
|
14
|
+
}
|
|
15
|
+
export function resolveChannelProvider(name) {
|
|
16
|
+
return REGISTRY.get(name);
|
|
17
|
+
}
|
|
18
|
+
export function listChannelProviders() {
|
|
19
|
+
return [...REGISTRY.keys()].sort();
|
|
20
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transport resolution: map a user-facing channel name to the provider that
|
|
3
|
+
* actually delivers it, using `notify.transports` from agents.yaml.
|
|
4
|
+
*
|
|
5
|
+
* Default is name-identity (`--channel slack` -> `slack` provider) — NOT a
|
|
6
|
+
* fallback to a different transport. Only telegram is dual-homed (rush vs
|
|
7
|
+
* openclaw-telegram); config picks. An unmapped-to-unregistered name dies loud.
|
|
8
|
+
*/
|
|
9
|
+
import type { Meta } from '../types.js';
|
|
10
|
+
import { type ChannelProvider } from './registry.js';
|
|
11
|
+
export declare function resolveTransport(channel: string, meta: Meta): ChannelProvider;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { die } from '../format.js';
|
|
2
|
+
import { resolveChannelProvider, listChannelProviders } from './registry.js';
|
|
3
|
+
export function resolveTransport(channel, meta) {
|
|
4
|
+
const providerName = meta.notify?.transports?.[channel] ?? channel;
|
|
5
|
+
const provider = resolveChannelProvider(providerName);
|
|
6
|
+
if (!provider) {
|
|
7
|
+
die(`No channel provider '${providerName}'` +
|
|
8
|
+
(providerName === channel ? '' : ` (mapped from channel '${channel}' via notify.transports)`) +
|
|
9
|
+
`. Registered: ${listChannelProviders().join(', ')}.`);
|
|
10
|
+
}
|
|
11
|
+
return provider;
|
|
12
|
+
}
|
|
@@ -110,6 +110,24 @@ export interface InstallResult {
|
|
|
110
110
|
/** Set when the install runner threw or exited non-zero. */
|
|
111
111
|
error?: string;
|
|
112
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* Resolve the directory a `binary` install method downloads/extracts into.
|
|
115
|
+
*
|
|
116
|
+
* `/usr/local/bin` used to be hardcoded here, which fails on Apple Silicon
|
|
117
|
+
* Macs where that directory is root-owned and not user-writable (Homebrew
|
|
118
|
+
* moved on to /opt/homebrew but left /usr/local/bin behind). Resolution
|
|
119
|
+
* order:
|
|
120
|
+
* 1. `AGENTS_CLI_BIN_DIR` — explicit override, used as-is when set.
|
|
121
|
+
* 2. `~/.local/bin` — the XDG user-bin dir already used for shims (see
|
|
122
|
+
* posixpath.ts / shims.ts); created with mkdir -p if missing.
|
|
123
|
+
* 3. `/usr/local/bin` — last resort, for hosts where it's still writable.
|
|
124
|
+
* When it isn't either, throw an actionable error naming the env var and
|
|
125
|
+
* ~/.local/bin instead of letting curl/tar fail with a bare EACCES.
|
|
126
|
+
*
|
|
127
|
+
* Called once per install/dry-run so the same directory is used everywhere
|
|
128
|
+
* that method is described or executed.
|
|
129
|
+
*/
|
|
130
|
+
export declare function resolveBinDir(): string;
|
|
113
131
|
/**
|
|
114
132
|
* Display-only rendering of how a method would be run, for `--dry-run` and
|
|
115
133
|
* status output. Not used by installCli — execution goes through runInstallMethod
|
|
@@ -22,6 +22,7 @@ import { spawnSync } from 'child_process';
|
|
|
22
22
|
import * as yaml from 'yaml';
|
|
23
23
|
import { listResources, resolveResource } from './resources.js';
|
|
24
24
|
import { composeWin32CommandLine } from './platform/index.js';
|
|
25
|
+
import { localBinDir } from './platform/posixpath.js';
|
|
25
26
|
// ─── Validation primitives ───────────────────────────────────────────────────
|
|
26
27
|
/** Token allowed inside `check:` strings — letters, digits, underscore, dot, slash, dash. */
|
|
27
28
|
const SAFE_CHECK_TOKEN = /^[a-zA-Z0-9_./-]+$/;
|
|
@@ -340,6 +341,52 @@ export function describeMethod(method) {
|
|
|
340
341
|
const spec = method.binary[key];
|
|
341
342
|
return spec ? `download ${spec.url}` : 'binary download';
|
|
342
343
|
}
|
|
344
|
+
/** Env var that overrides where `binary` installs write their downloaded file(s). */
|
|
345
|
+
const BIN_DIR_ENV = 'AGENTS_CLI_BIN_DIR';
|
|
346
|
+
/** Historical default install dir — kept only as a last-resort fallback. */
|
|
347
|
+
const LEGACY_BIN_DIR = '/usr/local/bin';
|
|
348
|
+
function isWritableDir(dir, opts) {
|
|
349
|
+
try {
|
|
350
|
+
if (opts.create)
|
|
351
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
352
|
+
fs.accessSync(dir, fs.constants.W_OK);
|
|
353
|
+
return true;
|
|
354
|
+
}
|
|
355
|
+
catch {
|
|
356
|
+
return false;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Resolve the directory a `binary` install method downloads/extracts into.
|
|
361
|
+
*
|
|
362
|
+
* `/usr/local/bin` used to be hardcoded here, which fails on Apple Silicon
|
|
363
|
+
* Macs where that directory is root-owned and not user-writable (Homebrew
|
|
364
|
+
* moved on to /opt/homebrew but left /usr/local/bin behind). Resolution
|
|
365
|
+
* order:
|
|
366
|
+
* 1. `AGENTS_CLI_BIN_DIR` — explicit override, used as-is when set.
|
|
367
|
+
* 2. `~/.local/bin` — the XDG user-bin dir already used for shims (see
|
|
368
|
+
* posixpath.ts / shims.ts); created with mkdir -p if missing.
|
|
369
|
+
* 3. `/usr/local/bin` — last resort, for hosts where it's still writable.
|
|
370
|
+
* When it isn't either, throw an actionable error naming the env var and
|
|
371
|
+
* ~/.local/bin instead of letting curl/tar fail with a bare EACCES.
|
|
372
|
+
*
|
|
373
|
+
* Called once per install/dry-run so the same directory is used everywhere
|
|
374
|
+
* that method is described or executed.
|
|
375
|
+
*/
|
|
376
|
+
export function resolveBinDir() {
|
|
377
|
+
const override = process.env[BIN_DIR_ENV];
|
|
378
|
+
if (override && override.trim())
|
|
379
|
+
return override.trim();
|
|
380
|
+
const local = localBinDir();
|
|
381
|
+
if (isWritableDir(local, { create: true }))
|
|
382
|
+
return local;
|
|
383
|
+
if (isWritableDir(LEGACY_BIN_DIR, { create: false }))
|
|
384
|
+
return LEGACY_BIN_DIR;
|
|
385
|
+
throw new Error(`${LEGACY_BIN_DIR} is not writable (EACCES) and ~/.local/bin could not be created or ` +
|
|
386
|
+
`written to either. Set ${BIN_DIR_ENV} to a writable directory, e.g.:\n` +
|
|
387
|
+
` export ${BIN_DIR_ENV}="$HOME/.local/bin"\n` +
|
|
388
|
+
`then re-run the install.`);
|
|
389
|
+
}
|
|
343
390
|
/**
|
|
344
391
|
* Display-only rendering of how a method would be run, for `--dry-run` and
|
|
345
392
|
* status output. Not used by installCli — execution goes through runInstallMethod
|
|
@@ -359,9 +406,10 @@ export function buildInstallCommand(method) {
|
|
|
359
406
|
const spec = method.binary[key];
|
|
360
407
|
if (!spec)
|
|
361
408
|
return 'binary download';
|
|
409
|
+
const binDir = resolveBinDir();
|
|
362
410
|
return spec.extract
|
|
363
|
-
? `curl -fsSL ${spec.url} -o /tmp/agents-cli-bin.tgz && tar -xzf /tmp/agents-cli-bin.tgz -C
|
|
364
|
-
: `curl -fsSL ${spec.url} -o
|
|
411
|
+
? `curl -fsSL ${spec.url} -o /tmp/agents-cli-bin.tgz && tar -xzf /tmp/agents-cli-bin.tgz -C ${binDir} ${spec.extract}`
|
|
412
|
+
: `curl -fsSL ${spec.url} -o ${path.join(binDir, 'agents-cli-downloaded')}`;
|
|
365
413
|
}
|
|
366
414
|
/**
|
|
367
415
|
* Execute an install method via spawnSync with argv arrays. Each branch
|
|
@@ -425,6 +473,7 @@ function runInstallMethod(method) {
|
|
|
425
473
|
if (!spec)
|
|
426
474
|
throw new Error(`no binary declared for ${key}`);
|
|
427
475
|
assertHttpsUrl(spec.url);
|
|
476
|
+
const binDir = resolveBinDir();
|
|
428
477
|
if (spec.extract) {
|
|
429
478
|
assertSafePathSegment(spec.extract);
|
|
430
479
|
const tmp = path.join(os.tmpdir(), `agents-cli-bin-${process.pid}-${Date.now()}.tgz`);
|
|
@@ -433,7 +482,7 @@ function runInstallMethod(method) {
|
|
|
433
482
|
if (dl.status !== 0) {
|
|
434
483
|
throw new Error(`binary download failed (status ${dl.status ?? 'unknown'})`);
|
|
435
484
|
}
|
|
436
|
-
const x = spawnSync('tar', ['-xzf', tmp, '-C',
|
|
485
|
+
const x = spawnSync('tar', ['-xzf', tmp, '-C', binDir, spec.extract], {
|
|
437
486
|
stdio: 'inherit',
|
|
438
487
|
});
|
|
439
488
|
if (x.status !== 0) {
|
|
@@ -448,7 +497,7 @@ function runInstallMethod(method) {
|
|
|
448
497
|
}
|
|
449
498
|
}
|
|
450
499
|
else {
|
|
451
|
-
const r = spawnSync('curl', ['-fsSL', spec.url, '-o', '
|
|
500
|
+
const r = spawnSync('curl', ['-fsSL', spec.url, '-o', path.join(binDir, 'agents-cli-downloaded')], { stdio: 'inherit' });
|
|
452
501
|
if (r.status !== 0) {
|
|
453
502
|
throw new Error(`binary download failed (status ${r.status ?? 'unknown'})`);
|
|
454
503
|
}
|
package/dist/lib/cloud/codex.js
CHANGED
|
@@ -30,12 +30,15 @@ function codexAvailable() {
|
|
|
30
30
|
return findCodexBinary() !== null;
|
|
31
31
|
}
|
|
32
32
|
/** Spawn the codex CLI with the given arguments and capture its output. */
|
|
33
|
-
function runCodex(args) {
|
|
33
|
+
function runCodex(args, env) {
|
|
34
34
|
const bin = findCodexBinary();
|
|
35
35
|
if (!bin)
|
|
36
36
|
return Promise.resolve({ stdout: '', stderr: 'codex not found', code: 127 });
|
|
37
37
|
return new Promise((resolve) => {
|
|
38
|
-
const proc = spawn(bin, args, {
|
|
38
|
+
const proc = spawn(bin, args, {
|
|
39
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
40
|
+
env: env ? { ...process.env, ...env } : process.env,
|
|
41
|
+
});
|
|
39
42
|
let stdout = '';
|
|
40
43
|
let stderr = '';
|
|
41
44
|
proc.stdout.on('data', (d) => { stdout += d.toString(); });
|
|
@@ -120,7 +123,7 @@ export class CodexCloudProvider {
|
|
|
120
123
|
if (options.branch)
|
|
121
124
|
args.push('--branch', options.branch);
|
|
122
125
|
args.push(options.prompt);
|
|
123
|
-
const { stdout, stderr, code } = await runCodex(args);
|
|
126
|
+
const { stdout, stderr, code } = await runCodex(args, options.env);
|
|
124
127
|
if (code !== 0) {
|
|
125
128
|
throw new Error(`codex cloud exec failed: ${stderr || stdout}`);
|
|
126
129
|
}
|
|
@@ -53,6 +53,7 @@ export declare function buildSshArgs(computer: string, remoteBin: string, remote
|
|
|
53
53
|
droidBin: string;
|
|
54
54
|
user: string;
|
|
55
55
|
port?: string;
|
|
56
|
+
env?: Record<string, string>;
|
|
56
57
|
}): string[];
|
|
57
58
|
/** Map a droid stream-json `result.subtype` / `is_error` to a CloudTaskStatus. */
|
|
58
59
|
export declare function mapResultStatus(line: {
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
import { spawn, execFileSync } from 'child_process';
|
|
23
23
|
import * as fs from 'fs';
|
|
24
24
|
import * as path from 'path';
|
|
25
|
+
import { controlOpts, sshConnectOpts } from '../ssh-exec.js';
|
|
25
26
|
import { MissingTargetError } from './types.js';
|
|
26
27
|
import { getShimsDir } from '../state.js';
|
|
27
28
|
const SHIMS_DIR = getShimsDir();
|
|
@@ -105,11 +106,16 @@ export function buildExecArgs(prompt, opts) {
|
|
|
105
106
|
* shell-quoted into a single remote command string.
|
|
106
107
|
*/
|
|
107
108
|
export function buildSshArgs(computer, remoteBin, remoteArgv, opts) {
|
|
108
|
-
const
|
|
109
|
+
const envPrefix = Object.entries(opts.env ?? {}).map(([key, value]) => {
|
|
110
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
|
|
111
|
+
throw new Error(`Invalid runtime env key for Factory dispatch: ${key}`);
|
|
112
|
+
}
|
|
113
|
+
return `${key}=${shellQuote(value)}`;
|
|
114
|
+
});
|
|
115
|
+
const remoteCmd = [...envPrefix, remoteBin, ...remoteArgv].map((part, idx) => idx < envPrefix.length ? part : shellQuote(part)).join(' ');
|
|
109
116
|
const proxy = `ProxyCommand=${opts.droidBin} computer ssh ${computer} --proxy --port %p`;
|
|
110
117
|
return [
|
|
111
|
-
'-o', proxy,
|
|
112
|
-
'-o', 'StrictHostKeyChecking=accept-new',
|
|
118
|
+
...sshConnectOpts(controlOpts(), ['-o', proxy]),
|
|
113
119
|
'-p', opts.port ?? '22',
|
|
114
120
|
`${opts.user}@${computer}`,
|
|
115
121
|
remoteCmd,
|
|
@@ -247,7 +253,7 @@ export class FactoryCloudProvider {
|
|
|
247
253
|
const autonomy = resolveAutonomy(options.providerOptions?.autonomy ?? options.providerOptions?.mode, this.defaultAutonomy);
|
|
248
254
|
const user = options.providerOptions?.user ?? 'droid';
|
|
249
255
|
const execArgs = buildExecArgs(options.prompt, { autonomy });
|
|
250
|
-
const sshArgs = buildSshArgs(computer, 'droid', execArgs, { droidBin, user });
|
|
256
|
+
const sshArgs = buildSshArgs(computer, 'droid', execArgs, { droidBin, user, env: options.env });
|
|
251
257
|
const { events, status, summary, sessionId } = await this.runRemote(sshArgs);
|
|
252
258
|
const now = new Date().toISOString();
|
|
253
259
|
const id = sessionId ?? `droid-${Date.now()}`;
|
package/dist/lib/cloud/rush.d.ts
CHANGED
|
@@ -64,7 +64,7 @@ export declare function accountTokensFingerprint(tokens: AccountTokenEntry[]): s
|
|
|
64
64
|
* Build the POST body for /api/v1/cloud-runs. Exported so tests can verify
|
|
65
65
|
* the back-compat shape (singular fields + repos[]) without needing real
|
|
66
66
|
* GitHub installations or a live Rush session. `findInstallation` is the
|
|
67
|
-
* only other I/O and it's tested by the
|
|
67
|
+
* only other I/O and it's tested by the cloud proxy integration suite.
|
|
68
68
|
*/
|
|
69
69
|
export declare function buildDispatchBody(input: {
|
|
70
70
|
agent?: string;
|
|
@@ -89,6 +89,8 @@ export declare function buildDispatchBody(input: {
|
|
|
89
89
|
* MAX_IMAGES_PER_DISPATCH — extras are dropped, never sent. Omitted when empty.
|
|
90
90
|
*/
|
|
91
91
|
images?: ImageAttachment[] | null;
|
|
92
|
+
/** Runtime env vars mounted into the cloud agent process. */
|
|
93
|
+
env?: Record<string, string> | null;
|
|
92
94
|
}): Record<string, unknown>;
|
|
93
95
|
export declare class RushCloudProvider implements CloudProvider {
|
|
94
96
|
id: "rush";
|
package/dist/lib/cloud/rush.js
CHANGED
|
@@ -255,7 +255,7 @@ export function accountTokensFingerprint(tokens) {
|
|
|
255
255
|
* Build the POST body for /api/v1/cloud-runs. Exported so tests can verify
|
|
256
256
|
* the back-compat shape (singular fields + repos[]) without needing real
|
|
257
257
|
* GitHub installations or a live Rush session. `findInstallation` is the
|
|
258
|
-
* only other I/O and it's tested by the
|
|
258
|
+
* only other I/O and it's tested by the cloud proxy integration suite.
|
|
259
259
|
*/
|
|
260
260
|
export function buildDispatchBody(input) {
|
|
261
261
|
if (input.resolvedRepos.length === 0) {
|
|
@@ -286,6 +286,9 @@ export function buildDispatchBody(input) {
|
|
|
286
286
|
if (input.images && input.images.length > 0) {
|
|
287
287
|
body.images = input.images.slice(0, MAX_IMAGES_PER_DISPATCH);
|
|
288
288
|
}
|
|
289
|
+
if (input.env && Object.keys(input.env).length > 0) {
|
|
290
|
+
body.env = input.env;
|
|
291
|
+
}
|
|
289
292
|
return body;
|
|
290
293
|
}
|
|
291
294
|
export class RushCloudProvider {
|
|
@@ -358,6 +361,7 @@ export class RushCloudProvider {
|
|
|
358
361
|
strategy,
|
|
359
362
|
skills: options.skills,
|
|
360
363
|
images: options.images,
|
|
364
|
+
env: options.env,
|
|
361
365
|
});
|
|
362
366
|
let res = await api('POST', '/api/v1/cloud-runs', token, body);
|
|
363
367
|
// Server detects drift (new account or rotated token) by comparing the
|
|
@@ -402,6 +406,7 @@ export class RushCloudProvider {
|
|
|
402
406
|
accountTokens,
|
|
403
407
|
skills: options.skills,
|
|
404
408
|
images: options.images,
|
|
409
|
+
env: options.env,
|
|
405
410
|
});
|
|
406
411
|
res = await api('POST', '/api/v1/cloud-runs', token, retryBody);
|
|
407
412
|
// Persist consent on first successful upload so we don't re-prompt
|
|
@@ -181,6 +181,8 @@ export interface DispatchOptions {
|
|
|
181
181
|
images?: ImageAttachment[];
|
|
182
182
|
/** Provider-specific options (e.g., codex env ID, factory computer name). */
|
|
183
183
|
providerOptions?: Record<string, unknown>;
|
|
184
|
+
/** Runtime env vars to inject into the remote agent process when the provider supports it. */
|
|
185
|
+
env?: Record<string, string>;
|
|
184
186
|
}
|
|
185
187
|
/**
|
|
186
188
|
* Collapse `repo` + `repos` into a single deduped list. Exported so callers,
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** macOS Unix-domain socket path cap: `sizeof(sockaddr_un.sun_path)` in <sys/un.h>. */
|
|
2
|
+
export declare const SUN_LEN = 104;
|
|
3
|
+
/** Suffix codex appends to CODEX_HOME to reach its app-server control socket. */
|
|
4
|
+
export declare const CODEX_CONTROL_SOCKET_SUFFIX = "/app-server-control/app-server-control.sock";
|
|
5
|
+
/**
|
|
6
|
+
* True when this CODEX_HOME's derived control-socket path would overflow
|
|
7
|
+
* SUN_LEN on macOS.
|
|
8
|
+
*/
|
|
9
|
+
export declare function codexHomeOverflowsSunLen(home: string): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* The short, per-version codex home used when the versioned home overflows.
|
|
12
|
+
* `~/.agents/.codex-homes/<version>/.codex` keeps the socket path well under
|
|
13
|
+
* SUN_LEN while staying stable across reboots (unlike $TMPDIR) and per-version
|
|
14
|
+
* isolated.
|
|
15
|
+
*/
|
|
16
|
+
export declare function shortCodexHome(agentsUserDir: string, version: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* Resolve a macOS SUN_LEN-safe CODEX_HOME for the given versioned home,
|
|
19
|
+
* migrating the home to a short real path (once, idempotently) when needed.
|
|
20
|
+
*
|
|
21
|
+
* On non-darwin platforms, or when the versioned home already fits, the
|
|
22
|
+
* versioned home is returned unchanged. If migration fails for any reason the
|
|
23
|
+
* versioned home is returned (no worse than the pre-fix behavior).
|
|
24
|
+
*/
|
|
25
|
+
export declare function resolveCodexHome(versionedHome: string, agentsUserDir: string, version: string, platform?: NodeJS.Platform): string;
|
|
26
|
+
/**
|
|
27
|
+
* Emit the bash block that a generated codex shim uses to export a
|
|
28
|
+
* SUN_LEN-safe CODEX_HOME. Mirrors {@link resolveCodexHome}.
|
|
29
|
+
*
|
|
30
|
+
* @param homeExpr shell expression for the versioned codex home
|
|
31
|
+
* (e.g. `$VERSION_DIR/home/.codex`)
|
|
32
|
+
* @param shortBaseExpr shell expression for the per-version short base dir
|
|
33
|
+
* (e.g. `$AGENTS_USER_DIR/.codex-homes/$VERSION`)
|
|
34
|
+
*/
|
|
35
|
+
export declare function codexHomeShimBash(homeExpr: string, shortBaseExpr: string): string;
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* macOS SUN_LEN-safe CODEX_HOME resolution.
|
|
3
|
+
*
|
|
4
|
+
* Codex reads its config (approval_policy, sandbox_mode, MCP servers, rules)
|
|
5
|
+
* from CODEX_HOME and, for its app-server daemon, binds a Unix-domain control
|
|
6
|
+
* socket at `$CODEX_HOME/app-server-control/app-server-control.sock`. macOS
|
|
7
|
+
* caps Unix socket paths at 104 bytes (SUN_LEN — `sizeof(sockaddr_un.sun_path)`
|
|
8
|
+
* in `<sys/un.h>`). agents-cli points CODEX_HOME at the deep versioned home
|
|
9
|
+
* (`~/.agents/.history/versions/codex/<version>/home/.codex`), which for a
|
|
10
|
+
* typical user is long enough that the derived socket path exceeds 104 bytes.
|
|
11
|
+
* `codex app-server daemon start` then fails with `path must be shorter than
|
|
12
|
+
* SUN_LEN`, and every codex spawn on macOS dies (this took down all OpenClaw
|
|
13
|
+
* agents on mac-mini — RUSH-1866).
|
|
14
|
+
*
|
|
15
|
+
* Codex exposes no socket-path override and resolves symlinks before binding,
|
|
16
|
+
* so a short symlink pointing at the deep home does NOT help (the socket lives
|
|
17
|
+
* at the resolved target). The only fix is to make the *real* CODEX_HOME short.
|
|
18
|
+
* On macOS, when the versioned home's socket path would overflow, we relocate
|
|
19
|
+
* the home once to a short real directory under `~/.agents/.codex-homes/` and
|
|
20
|
+
* leave a symlink behind at the versioned path, then point CODEX_HOME at the
|
|
21
|
+
* short real path. Config, auth, and state migrate intact; there is no socket
|
|
22
|
+
* or sqlite fragmentation because a single physical home simply moves.
|
|
23
|
+
*
|
|
24
|
+
* This module is the single source of truth for that logic. `exec.ts` calls
|
|
25
|
+
* the TS resolver for `agents run`/`agents exec`; `shims.ts` emits the bash
|
|
26
|
+
* equivalent (`codexHomeShimBash`) into the generated codex shims. Keep the two
|
|
27
|
+
* in lockstep.
|
|
28
|
+
*/
|
|
29
|
+
import * as fs from 'fs';
|
|
30
|
+
import * as path from 'path';
|
|
31
|
+
/** macOS Unix-domain socket path cap: `sizeof(sockaddr_un.sun_path)` in <sys/un.h>. */
|
|
32
|
+
export const SUN_LEN = 104;
|
|
33
|
+
/** Suffix codex appends to CODEX_HOME to reach its app-server control socket. */
|
|
34
|
+
export const CODEX_CONTROL_SOCKET_SUFFIX = '/app-server-control/app-server-control.sock';
|
|
35
|
+
/**
|
|
36
|
+
* True when this CODEX_HOME's derived control-socket path would overflow
|
|
37
|
+
* SUN_LEN on macOS.
|
|
38
|
+
*/
|
|
39
|
+
export function codexHomeOverflowsSunLen(home) {
|
|
40
|
+
return home.length + CODEX_CONTROL_SOCKET_SUFFIX.length > SUN_LEN;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The short, per-version codex home used when the versioned home overflows.
|
|
44
|
+
* `~/.agents/.codex-homes/<version>/.codex` keeps the socket path well under
|
|
45
|
+
* SUN_LEN while staying stable across reboots (unlike $TMPDIR) and per-version
|
|
46
|
+
* isolated.
|
|
47
|
+
*/
|
|
48
|
+
export function shortCodexHome(agentsUserDir, version) {
|
|
49
|
+
return path.join(agentsUserDir, '.codex-homes', version, '.codex');
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Resolve a macOS SUN_LEN-safe CODEX_HOME for the given versioned home,
|
|
53
|
+
* migrating the home to a short real path (once, idempotently) when needed.
|
|
54
|
+
*
|
|
55
|
+
* On non-darwin platforms, or when the versioned home already fits, the
|
|
56
|
+
* versioned home is returned unchanged. If migration fails for any reason the
|
|
57
|
+
* versioned home is returned (no worse than the pre-fix behavior).
|
|
58
|
+
*/
|
|
59
|
+
export function resolveCodexHome(versionedHome, agentsUserDir, version, platform = process.platform) {
|
|
60
|
+
if (platform !== 'darwin')
|
|
61
|
+
return versionedHome;
|
|
62
|
+
if (!codexHomeOverflowsSunLen(versionedHome))
|
|
63
|
+
return versionedHome;
|
|
64
|
+
const short = shortCodexHome(agentsUserDir, version);
|
|
65
|
+
try {
|
|
66
|
+
if (!fs.existsSync(short)) {
|
|
67
|
+
fs.mkdirSync(path.dirname(short), { recursive: true });
|
|
68
|
+
const st = fs.lstatSync(versionedHome, { throwIfNoEntry: false });
|
|
69
|
+
if (st && st.isDirectory() && !st.isSymbolicLink()) {
|
|
70
|
+
// Migrate the existing deep home so config/auth/state stay intact,
|
|
71
|
+
// then leave a symlink so anything referencing the versioned path
|
|
72
|
+
// still resolves.
|
|
73
|
+
fs.renameSync(versionedHome, short);
|
|
74
|
+
fs.symlinkSync(short, versionedHome);
|
|
75
|
+
}
|
|
76
|
+
else if (!st) {
|
|
77
|
+
// Fresh install: create the short home and link the versioned path to it.
|
|
78
|
+
fs.mkdirSync(short, { recursive: true });
|
|
79
|
+
fs.symlinkSync(short, versionedHome);
|
|
80
|
+
}
|
|
81
|
+
// If versionedHome is already a symlink (migrated by a prior run or the
|
|
82
|
+
// shim), leave it; `short` will be populated below.
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// Migration lost a race or hit a permission error. Fall back to the
|
|
87
|
+
// versioned home rather than crash the invocation.
|
|
88
|
+
if (!fs.existsSync(short))
|
|
89
|
+
return versionedHome;
|
|
90
|
+
}
|
|
91
|
+
return fs.existsSync(short) ? short : versionedHome;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Emit the bash block that a generated codex shim uses to export a
|
|
95
|
+
* SUN_LEN-safe CODEX_HOME. Mirrors {@link resolveCodexHome}.
|
|
96
|
+
*
|
|
97
|
+
* @param homeExpr shell expression for the versioned codex home
|
|
98
|
+
* (e.g. `$VERSION_DIR/home/.codex`)
|
|
99
|
+
* @param shortBaseExpr shell expression for the per-version short base dir
|
|
100
|
+
* (e.g. `$AGENTS_USER_DIR/.codex-homes/$VERSION`)
|
|
101
|
+
*/
|
|
102
|
+
export function codexHomeShimBash(homeExpr, shortBaseExpr) {
|
|
103
|
+
return `
|
|
104
|
+
# Codex reads its config (approval_policy, sandbox_mode, MCP servers, rules)
|
|
105
|
+
# from CODEX_HOME and binds a Unix control socket at
|
|
106
|
+
# "\$CODEX_HOME/app-server-control/app-server-control.sock" for its app-server
|
|
107
|
+
# daemon. macOS caps Unix socket paths at 104 bytes (SUN_LEN); the deep
|
|
108
|
+
# versioned home overflows, so "codex app-server daemon start" fails with
|
|
109
|
+
# "path must be shorter than SUN_LEN" and every codex spawn dies (RUSH-1866).
|
|
110
|
+
# Codex has no socket-path override and resolves symlinks before binding, so a
|
|
111
|
+
# short symlink to the deep home does NOT help. On macOS, when the derived
|
|
112
|
+
# socket path would overflow, relocate the home once to a short real dir and
|
|
113
|
+
# leave a symlink behind, then point CODEX_HOME at the short real path. A
|
|
114
|
+
# caller-provided CODEX_HOME always wins.
|
|
115
|
+
if [ -z "\${CODEX_HOME:-}" ]; then
|
|
116
|
+
CODEX_HOME="${homeExpr}"
|
|
117
|
+
# 43 = length of "/app-server-control/app-server-control.sock"; 104 = SUN_LEN.
|
|
118
|
+
if [ "\$(uname -s)" = "Darwin" ] && [ "\$(( \${#CODEX_HOME} + 43 ))" -gt 104 ]; then
|
|
119
|
+
_codex_short="${shortBaseExpr}/.codex"
|
|
120
|
+
if [ ! -e "\$_codex_short" ]; then
|
|
121
|
+
mkdir -p "${shortBaseExpr}"
|
|
122
|
+
if [ -d "\$CODEX_HOME" ] && [ ! -L "\$CODEX_HOME" ]; then
|
|
123
|
+
if mv "\$CODEX_HOME" "\$_codex_short" 2>/dev/null; then
|
|
124
|
+
ln -snf "\$_codex_short" "\$CODEX_HOME"
|
|
125
|
+
fi
|
|
126
|
+
elif [ ! -e "\$CODEX_HOME" ]; then
|
|
127
|
+
mkdir -p "\$_codex_short"
|
|
128
|
+
ln -snf "\$_codex_short" "\$CODEX_HOME"
|
|
129
|
+
fi
|
|
130
|
+
fi
|
|
131
|
+
[ -d "\$_codex_short" ] && CODEX_HOME="\$_codex_short"
|
|
132
|
+
fi
|
|
133
|
+
fi
|
|
134
|
+
export CODEX_HOME="\$CODEX_HOME"
|
|
135
|
+
`;
|
|
136
|
+
}
|
|
@@ -26,6 +26,10 @@ export interface CrabboxBox {
|
|
|
26
26
|
state: string;
|
|
27
27
|
/** Public IPv4, when the provider exposes one. */
|
|
28
28
|
ip?: string;
|
|
29
|
+
/** Tailnet IPv4 (100.x), when the box was leased with `--network tailscale`. */
|
|
30
|
+
tailscaleIPv4?: string;
|
|
31
|
+
/** Tailnet MagicDNS FQDN, when the box joined the tailnet. */
|
|
32
|
+
tailscaleFQDN?: string;
|
|
29
33
|
profile?: string;
|
|
30
34
|
class?: string;
|
|
31
35
|
/** True when running + bootstrap-complete. */
|
|
@@ -49,6 +53,13 @@ export interface CrabboxOptions {
|
|
|
49
53
|
* `crabbox login` credentials.
|
|
50
54
|
*/
|
|
51
55
|
secretsBundle?: string;
|
|
56
|
+
/**
|
|
57
|
+
* Hard cap (ms) on a single crabbox invocation that hits the provider API
|
|
58
|
+
* (`list`). Prevents a slow/unreachable provider from hanging an ambient
|
|
59
|
+
* command like `agents devices`. Defaults to 8s; callers on a fast/interactive
|
|
60
|
+
* path (devices list, `agents ssh` fall-through) pass a shorter bound.
|
|
61
|
+
*/
|
|
62
|
+
timeoutMs?: number;
|
|
52
63
|
}
|
|
53
64
|
/** Locate the crabbox binary, or throw an actionable error. */
|
|
54
65
|
export declare function findCrabbox(): string;
|
|
@@ -61,6 +72,17 @@ export declare function findCrabbox(): string;
|
|
|
61
72
|
export declare const LEASE_PROVIDER_TOKEN_KEYS: string[];
|
|
62
73
|
/** The first bundle that declares a provider token key, or undefined. Pure over `bundles`. */
|
|
63
74
|
export declare function pickLeaseBundleFromList(bundles: SecretsBundle[]): string | undefined;
|
|
75
|
+
/**
|
|
76
|
+
* Env keys a bundle may use for a Tailscale auth key. crabbox reads
|
|
77
|
+
* `CRABBOX_TAILSCALE_AUTH_KEY` to join a leased box to the tailnet; we accept the
|
|
78
|
+
* common alternate names too and rename to that canonical key on injection.
|
|
79
|
+
*/
|
|
80
|
+
export declare const TAILSCALE_AUTH_KEY_NAMES: string[];
|
|
81
|
+
/** The first bundle + key that declares a Tailscale auth key, or undefined. Pure over `bundles`. */
|
|
82
|
+
export declare function pickTailscaleBundleFromList(bundles: SecretsBundle[]): {
|
|
83
|
+
name: string;
|
|
84
|
+
key: string;
|
|
85
|
+
} | undefined;
|
|
64
86
|
/** A resolved lease bundle: its name, plus (auto-detect only) the exact keys to inject. */
|
|
65
87
|
export interface ResolvedLeaseBundle {
|
|
66
88
|
name: string;
|
|
@@ -97,6 +119,13 @@ export interface WarmupOptions extends CrabboxOptions {
|
|
|
97
119
|
code?: boolean;
|
|
98
120
|
/** Cloud backend override (crabbox provider id, e.g. hetzner/aws/do). */
|
|
99
121
|
provider?: string;
|
|
122
|
+
/**
|
|
123
|
+
* Network mode for the leased box. `'public'` (default) leases with a public
|
|
124
|
+
* IP; `'tailscale'` joins the box to the tailnet (`--network tailscale`,
|
|
125
|
+
* tagged `tag:crabbox`) so it is reachable only over Tailscale. The caller
|
|
126
|
+
* (command layer) decides WHEN to enable this — F5 here is plumbing only.
|
|
127
|
+
*/
|
|
128
|
+
netMode?: 'public' | 'tailscale';
|
|
100
129
|
}
|
|
101
130
|
/**
|
|
102
131
|
* Lease a box and block until it is ready. Returns the leased box.
|
|
@@ -137,6 +166,21 @@ export declare function crabboxRun(slug: string, remoteCmd: string, opts?: Crabb
|
|
|
137
166
|
* Streams combined output; resolves with the remote exit code (null on dispatch failure).
|
|
138
167
|
*/
|
|
139
168
|
export declare function crabboxRunScript(slug: string, script: string, opts?: CrabboxRunOptions): Promise<number | null>;
|
|
169
|
+
/**
|
|
170
|
+
* Parse crabbox's shell-quoted `ssh` command (`'ssh' '-i' '<key>' … 'crabbox@ip'`)
|
|
171
|
+
* into an argv array. Exported for testing.
|
|
172
|
+
*/
|
|
173
|
+
export declare function parseCrabboxSshArgv(stdout: string): string[] | null;
|
|
174
|
+
/**
|
|
175
|
+
* The concrete `ssh` argv crabbox uses to reach box `slug` — including the
|
|
176
|
+
* per-lease identity key and known_hosts crabbox provisions under its own config
|
|
177
|
+
* dir. A raw `ssh crabbox@ip` fails `publickey`; this is the only auth that works.
|
|
178
|
+
* `crabbox ssh --id` PRINTS the command (it doesn't connect — that's `crabbox
|
|
179
|
+
* connect`), and `--reclaim` makes it resolve regardless of which repo holds the
|
|
180
|
+
* lease claim (matching `crabboxRunScript`). Returns null when crabbox can't
|
|
181
|
+
* resolve the box (caller treats setup-copy / `agents ssh` as best-effort).
|
|
182
|
+
*/
|
|
183
|
+
export declare function crabboxSshArgv(slug: string, opts?: CrabboxOptions): string[] | null;
|
|
140
184
|
/** Release the lease / delete the box. Best-effort; never throws. */
|
|
141
185
|
export declare function crabboxStop(slug: string, opts?: CrabboxOptions): boolean;
|
|
142
186
|
/** Never reap a box touched within this many seconds, regardless of idle-timeout. */
|