@phnx-labs/agents-cli 1.20.65 → 1.20.67
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 +45 -0
- package/README.md +119 -1
- package/dist/bin/agents +0 -0
- package/dist/commands/exec.d.ts +48 -0
- package/dist/commands/exec.js +71 -0
- package/dist/commands/monitors.js +8 -0
- package/dist/commands/plugins.js +28 -7
- package/dist/commands/sessions-browser.d.ts +82 -0
- package/dist/commands/sessions-browser.js +320 -0
- package/dist/commands/sessions.d.ts +1 -0
- package/dist/commands/sessions.js +121 -4
- package/dist/commands/share.d.ts +2 -0
- package/dist/commands/share.js +150 -0
- package/dist/commands/ssh.js +9 -1
- package/dist/commands/teams.js +1 -1
- package/dist/index.js +2 -1
- package/dist/lib/agents.d.ts +28 -0
- package/dist/lib/agents.js +76 -0
- package/dist/lib/devices/connect.d.ts +18 -1
- package/dist/lib/devices/connect.js +10 -2
- package/dist/lib/devices/known-hosts.d.ts +62 -0
- package/dist/lib/devices/known-hosts.js +137 -0
- package/dist/lib/exec.d.ts +15 -0
- package/dist/lib/exec.js +31 -6
- package/dist/lib/hosts/dispatch.js +20 -2
- package/dist/lib/hosts/remote-cmd.js +8 -4
- package/dist/lib/monitors/engine.js +4 -0
- package/dist/lib/monitors/sources/device.js +13 -3
- package/dist/lib/picker.d.ts +53 -0
- package/dist/lib/picker.js +214 -1
- package/dist/lib/plugins.d.ts +31 -1
- package/dist/lib/plugins.js +74 -13
- package/dist/lib/runner.js +6 -7
- package/dist/lib/secrets/agent.d.ts +35 -0
- package/dist/lib/secrets/agent.js +114 -55
- package/dist/lib/secrets/filestore.d.ts +9 -0
- package/dist/lib/secrets/filestore.js +21 -8
- package/dist/lib/secrets/index.d.ts +7 -0
- package/dist/lib/secrets/index.js +26 -6
- package/dist/lib/share/capture.d.ts +29 -0
- package/dist/lib/share/capture.js +140 -0
- package/dist/lib/share/config.d.ts +35 -0
- package/dist/lib/share/config.js +100 -0
- package/dist/lib/share/og.d.ts +25 -0
- package/dist/lib/share/og.js +84 -0
- package/dist/lib/share/provision.d.ts +10 -0
- package/dist/lib/share/provision.js +91 -0
- package/dist/lib/share/publish.d.ts +57 -0
- package/dist/lib/share/publish.js +145 -0
- package/dist/lib/share/worker-template.d.ts +2 -0
- package/dist/lib/share/worker-template.js +82 -0
- package/dist/lib/shims.d.ts +13 -0
- package/dist/lib/shims.js +42 -2
- package/dist/lib/ssh-exec.d.ts +24 -0
- package/dist/lib/ssh-exec.js +15 -3
- package/dist/lib/startup/command-registry.d.ts +1 -0
- package/dist/lib/startup/command-registry.js +2 -0
- package/dist/lib/types.d.ts +20 -0
- package/package.json +1 -1
package/dist/lib/shims.js
CHANGED
|
@@ -16,7 +16,7 @@ import { confirm, select } from '@inquirer/prompts';
|
|
|
16
16
|
import { IS_WINDOWS, prependToWindowsUserPath } from './platform/index.js';
|
|
17
17
|
import { getShimsDir, getVersionsDir, getBackupsDir, getHistoryDir, ensureAgentsDir } from './state.js';
|
|
18
18
|
export { getShimsDir };
|
|
19
|
-
import { AGENTS, agentConfigDirName } from './agents.js';
|
|
19
|
+
import { AGENTS, agentConfigDirName, readAuthAccountIdentity } from './agents.js';
|
|
20
20
|
/**
|
|
21
21
|
* Files and directories to always skip during conflict detection and migration.
|
|
22
22
|
* These are never user config that should be migrated.
|
|
@@ -1143,6 +1143,21 @@ function detectMigrationConflicts(agent, version) {
|
|
|
1143
1143
|
* preserved so the "freshest" comparison stays stable and switches don't
|
|
1144
1144
|
* ping-pong. Best-effort: a failed copy just means the user re-logs in.
|
|
1145
1145
|
*/
|
|
1146
|
+
/**
|
|
1147
|
+
* Best-effort account identity for the credential *directory* of a file-auth
|
|
1148
|
+
* agent (droid / kimi / antigravity), or null when the directory holds no
|
|
1149
|
+
* decodable account claim. Delegates to readAuthAccountIdentity, which decrypts
|
|
1150
|
+
* / decodes each agent's REAL on-disk format (droid AES-256-GCM + WorkOS JWT,
|
|
1151
|
+
* kimi access-token JWT, antigravity refresh-token) — the earlier plaintext
|
|
1152
|
+
* top-level-key scan matched NO real credential file, so the guard below never
|
|
1153
|
+
* engaged. Two dirs for the SAME account compare equal; DIFFERENT accounts
|
|
1154
|
+
* compare distinct. Used by carryForwardAuthFiles to refuse overwriting one
|
|
1155
|
+
* account's login with a credential that belongs to a DIFFERENT account
|
|
1156
|
+
* (RUSH-1764).
|
|
1157
|
+
*/
|
|
1158
|
+
export function readAuthFileIdentity(agent, configDir) {
|
|
1159
|
+
return readAuthAccountIdentity(agent, configDir);
|
|
1160
|
+
}
|
|
1146
1161
|
export function carryForwardAuthFiles(agent, toConfigDir) {
|
|
1147
1162
|
const authFiles = AGENTS[agent].authFiles;
|
|
1148
1163
|
if (!authFiles || authFiles.length === 0)
|
|
@@ -1158,12 +1173,33 @@ export function carryForwardAuthFiles(agent, toConfigDir) {
|
|
|
1158
1173
|
catch {
|
|
1159
1174
|
return; // no installed versions to source from
|
|
1160
1175
|
}
|
|
1176
|
+
// Account identity currently installed at the destination home (null when the
|
|
1177
|
+
// dest is empty or its credential can't be decoded). When it IS known, only a
|
|
1178
|
+
// source home whose credential decodes to the SAME account is eligible to
|
|
1179
|
+
// overwrite it — so a newer login for a DIFFERENT account sitting in another
|
|
1180
|
+
// version-home can't silently replace the account the user is signed into
|
|
1181
|
+
// (RUSH-1764). An empty destination still seeds from the freshest source (the
|
|
1182
|
+
// version-switch case). Identity is a per-DIR/account property, not per-file:
|
|
1183
|
+
// for droid it comes from decrypting auth.v2.file with auth.v2.key, so it must
|
|
1184
|
+
// gate BOTH files as a unit (never carry account B's key over account A's).
|
|
1185
|
+
const toResolved = path.resolve(toConfigDir);
|
|
1186
|
+
const destIdentity = readAuthFileIdentity(agent, toConfigDir);
|
|
1187
|
+
const identityCache = new Map();
|
|
1188
|
+
const dirIdentity = (dir) => {
|
|
1189
|
+
const key = path.resolve(dir);
|
|
1190
|
+
if (!identityCache.has(key))
|
|
1191
|
+
identityCache.set(key, readAuthFileIdentity(agent, dir));
|
|
1192
|
+
return identityCache.get(key) ?? null;
|
|
1193
|
+
};
|
|
1161
1194
|
for (const rel of authFiles) {
|
|
1162
1195
|
const dest = path.join(toConfigDir, rel);
|
|
1163
1196
|
const destResolved = path.resolve(dest);
|
|
1164
|
-
// Newest existing source copy across all version homes (excluding dest)
|
|
1197
|
+
// Newest existing source copy across all version homes (excluding dest),
|
|
1198
|
+
// constrained to the destination's account identity when it is known.
|
|
1165
1199
|
let newest = null;
|
|
1166
1200
|
for (const dir of sourceDirs) {
|
|
1201
|
+
if (path.resolve(dir) === toResolved)
|
|
1202
|
+
continue; // never source from self
|
|
1167
1203
|
const src = path.join(dir, rel);
|
|
1168
1204
|
if (path.resolve(src) === destResolved)
|
|
1169
1205
|
continue;
|
|
@@ -1176,6 +1212,10 @@ export function carryForwardAuthFiles(agent, toConfigDir) {
|
|
|
1176
1212
|
}
|
|
1177
1213
|
if (!st.isFile())
|
|
1178
1214
|
continue;
|
|
1215
|
+
// Account-identity guard: never carry a different account's credential over
|
|
1216
|
+
// an existing login. Only enforced when the destination's identity is known.
|
|
1217
|
+
if (destIdentity !== null && dirIdentity(dir) !== destIdentity)
|
|
1218
|
+
continue;
|
|
1179
1219
|
if (!newest || st.mtimeMs > newest.mtimeMs)
|
|
1180
1220
|
newest = { path: src, mtimeMs: st.mtimeMs };
|
|
1181
1221
|
}
|
package/dist/lib/ssh-exec.d.ts
CHANGED
|
@@ -29,6 +29,16 @@ export declare function shellQuote(s: string): string;
|
|
|
29
29
|
*/
|
|
30
30
|
export declare const SSH_OPTS: readonly string[];
|
|
31
31
|
export declare function controlOpts(): string[];
|
|
32
|
+
/**
|
|
33
|
+
* Compose an ssh connection-option prefix.
|
|
34
|
+
*
|
|
35
|
+
* `hostKeyOpts` (a caller's host-key posture, e.g. a pinned
|
|
36
|
+
* `StrictHostKeyChecking=yes` + managed `UserKnownHostsFile`) go FIRST, ahead of
|
|
37
|
+
* the `SSH_OPTS` baseline: ssh honors the *first* value it sees for each option,
|
|
38
|
+
* so an override placed after the baseline's `accept-new` would be silently
|
|
39
|
+
* ignored. Pure so the ordering contract is unit-testable. See RUSH-1767.
|
|
40
|
+
*/
|
|
41
|
+
export declare function sshConnectOpts(mux: string[], hostKeyOpts?: string[]): string[];
|
|
32
42
|
export interface SshExecOptions {
|
|
33
43
|
/** Piped to the remote command's stdin (never interpolated into the shell). */
|
|
34
44
|
input?: string;
|
|
@@ -38,6 +48,13 @@ export interface SshExecOptions {
|
|
|
38
48
|
extraSshArgs?: string[];
|
|
39
49
|
/** Reuse a persistent control socket across calls (default true; see `controlOpts`). */
|
|
40
50
|
multiplex?: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Host-key `-o` options that OVERRIDE the accept-new baseline — prepended so
|
|
53
|
+
* ssh's first-value-wins rule takes them (see {@link sshConnectOpts}). Used to
|
|
54
|
+
* force strict verification against the managed known_hosts store on the
|
|
55
|
+
* credential-copy path (RUSH-1767).
|
|
56
|
+
*/
|
|
57
|
+
hostKeyOpts?: string[];
|
|
41
58
|
}
|
|
42
59
|
export interface SshExecResult {
|
|
43
60
|
/** Remote exit status, or null if ssh itself failed / timed out. */
|
|
@@ -79,6 +96,13 @@ export interface SshStreamOptions {
|
|
|
79
96
|
tty?: boolean;
|
|
80
97
|
/** Reuse a persistent control socket across calls (default true; see `controlOpts`). */
|
|
81
98
|
multiplex?: boolean;
|
|
99
|
+
/**
|
|
100
|
+
* Host-key `-o` options that OVERRIDE the accept-new baseline — prepended so
|
|
101
|
+
* ssh's first-value-wins rule takes them (see {@link sshConnectOpts}). Used to
|
|
102
|
+
* force strict verification against the managed known_hosts store on the
|
|
103
|
+
* interactive credential-copy path (RUSH-1767).
|
|
104
|
+
*/
|
|
105
|
+
hostKeyOpts?: string[];
|
|
82
106
|
}
|
|
83
107
|
/**
|
|
84
108
|
* Foreground counterpart to `sshExec`: run `remoteCmd` on `target` with the
|
package/dist/lib/ssh-exec.js
CHANGED
|
@@ -88,6 +88,18 @@ export function controlOpts() {
|
|
|
88
88
|
'-o', 'ControlPersist=60s',
|
|
89
89
|
];
|
|
90
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* Compose an ssh connection-option prefix.
|
|
93
|
+
*
|
|
94
|
+
* `hostKeyOpts` (a caller's host-key posture, e.g. a pinned
|
|
95
|
+
* `StrictHostKeyChecking=yes` + managed `UserKnownHostsFile`) go FIRST, ahead of
|
|
96
|
+
* the `SSH_OPTS` baseline: ssh honors the *first* value it sees for each option,
|
|
97
|
+
* so an override placed after the baseline's `accept-new` would be silently
|
|
98
|
+
* ignored. Pure so the ordering contract is unit-testable. See RUSH-1767.
|
|
99
|
+
*/
|
|
100
|
+
export function sshConnectOpts(mux, hostKeyOpts) {
|
|
101
|
+
return [...(hostKeyOpts ?? []), ...SSH_OPTS, ...mux];
|
|
102
|
+
}
|
|
91
103
|
/**
|
|
92
104
|
* Run `remoteCmd` on `target` over ssh and capture stdout/stderr/exit.
|
|
93
105
|
*
|
|
@@ -97,7 +109,7 @@ export function controlOpts() {
|
|
|
97
109
|
export function sshExec(target, remoteCmd, opts = {}) {
|
|
98
110
|
assertValidSshTarget(target);
|
|
99
111
|
const mux = opts.multiplex === false ? [] : controlOpts();
|
|
100
|
-
const args = [...
|
|
112
|
+
const args = [...sshConnectOpts(mux, opts.hostKeyOpts), ...(opts.extraSshArgs ?? []), target, remoteCmd];
|
|
101
113
|
const res = spawnSync('ssh', args, {
|
|
102
114
|
input: opts.input,
|
|
103
115
|
encoding: 'utf-8',
|
|
@@ -123,7 +135,7 @@ export function sshExec(target, remoteCmd, opts = {}) {
|
|
|
123
135
|
export function sshExecRaw(target, remoteCmd, opts = {}) {
|
|
124
136
|
assertValidSshTarget(target);
|
|
125
137
|
const mux = opts.multiplex === false ? [] : controlOpts();
|
|
126
|
-
const args = [...
|
|
138
|
+
const args = [...sshConnectOpts(mux, opts.hostKeyOpts), ...(opts.extraSshArgs ?? []), target, remoteCmd];
|
|
127
139
|
const res = spawnSync('ssh', args, {
|
|
128
140
|
input: opts.input,
|
|
129
141
|
// No `encoding` → spawnSync returns Buffers.
|
|
@@ -154,7 +166,7 @@ export function sshStream(target, remoteCmd, opts = {}) {
|
|
|
154
166
|
assertValidSshTarget(target);
|
|
155
167
|
const mux = opts.multiplex === false ? [] : controlOpts();
|
|
156
168
|
const tty = opts.tty ? ['-tt'] : [];
|
|
157
|
-
const args = [...
|
|
169
|
+
const args = [...sshConnectOpts(mux, opts.hostKeyOpts), ...tty, target, remoteCmd];
|
|
158
170
|
const res = spawnSync('ssh', args, { stdio: 'inherit' });
|
|
159
171
|
if (typeof res.status === 'number')
|
|
160
172
|
return res.status;
|
|
@@ -91,6 +91,7 @@ export declare const loadMessage: ModuleLoader;
|
|
|
91
91
|
export declare const loadFeed: ModuleLoader;
|
|
92
92
|
export declare const loadMailboxes: ModuleLoader;
|
|
93
93
|
export declare const loadServe: ModuleLoader;
|
|
94
|
+
export declare const loadShare: ModuleLoader;
|
|
94
95
|
export declare const loadAudit: ModuleLoader;
|
|
95
96
|
export declare const loadWebhook: ModuleLoader;
|
|
96
97
|
export declare const loadFunnel: ModuleLoader;
|
|
@@ -69,6 +69,7 @@ export const loadMessage = async () => (await import('../../commands/message.js'
|
|
|
69
69
|
export const loadFeed = async () => (await import('../../commands/feed.js')).registerFeedCommand;
|
|
70
70
|
export const loadMailboxes = async () => (await import('../../commands/mailboxes.js')).registerMailboxesCommand;
|
|
71
71
|
export const loadServe = async () => (await import('../../commands/serve.js')).registerServeCommand;
|
|
72
|
+
export const loadShare = async () => (await import('../../commands/share.js')).registerShareCommands;
|
|
72
73
|
export const loadAudit = async () => (await import('../../commands/audit.js')).registerAuditCommands;
|
|
73
74
|
export const loadWebhook = async () => (await import('../../commands/webhook.js')).registerWebhookCommand;
|
|
74
75
|
export const loadFunnel = async () => (await import('../../commands/funnel.js')).registerFunnelCommand;
|
|
@@ -179,6 +180,7 @@ export const COMMAND_LOADERS = {
|
|
|
179
180
|
mailboxes: [loadMailboxes],
|
|
180
181
|
mailbox: [loadMailboxes],
|
|
181
182
|
serve: [loadServe],
|
|
183
|
+
share: [loadShare],
|
|
182
184
|
audit: [loadAudit],
|
|
183
185
|
webhook: [loadWebhook],
|
|
184
186
|
funnel: [loadFunnel],
|
package/dist/lib/types.d.ts
CHANGED
|
@@ -160,6 +160,16 @@ export interface AgentConfig {
|
|
|
160
160
|
* `skip` errors with a clear message naming the supported modes.
|
|
161
161
|
*/
|
|
162
162
|
modes: Mode[];
|
|
163
|
+
/**
|
|
164
|
+
* Whether `plan` mode works in a HEADLESS run (`--prompt`/`-p`). Some CLIs
|
|
165
|
+
* list a `plan` mode that only works interactively — kimi refuses `--prompt`
|
|
166
|
+
* combined with `--plan`, and grok's `--permission-mode plan` silently stalls
|
|
167
|
+
* a headless run at its ExitPlanMode gate. Absent (undefined) means true:
|
|
168
|
+
* headless plan is assumed to work unless a agent opts out with `false`, in
|
|
169
|
+
* which case a headless `--mode plan` request auto-downgrades to `auto`
|
|
170
|
+
* (see resolveHeadlessMode). Interactive plan is unaffected.
|
|
171
|
+
*/
|
|
172
|
+
headlessPlan?: boolean;
|
|
163
173
|
/**
|
|
164
174
|
* Whether the agent natively resolves `@path/to/file` imports inside its
|
|
165
175
|
* rules file at session start. If false, agents-cli must pre-compile the
|
|
@@ -723,6 +733,16 @@ export interface Meta {
|
|
|
723
733
|
* Full shape in `lib/fleet/types.ts` (FleetManifest).
|
|
724
734
|
*/
|
|
725
735
|
fleet?: import('./fleet/types.js').FleetManifest;
|
|
736
|
+
/** `agents share` endpoint (Cloudflare R2 + Worker). Set by `agents share
|
|
737
|
+
* setup`/`join`; syncs fleet-wide via `agents repo push/pull`. The write token
|
|
738
|
+
* lives in the `share` secrets bundle, not here. */
|
|
739
|
+
share?: {
|
|
740
|
+
baseUrl?: string;
|
|
741
|
+
accountId?: string;
|
|
742
|
+
workerName?: string;
|
|
743
|
+
bucketName?: string;
|
|
744
|
+
domain?: string;
|
|
745
|
+
};
|
|
726
746
|
}
|
|
727
747
|
/** Persisted agent-host entry in agents.yaml (overlay or inline). */
|
|
728
748
|
export interface HostEntry {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phnx-labs/agents-cli",
|
|
3
|
-
"version": "1.20.
|
|
3
|
+
"version": "1.20.67",
|
|
4
4
|
"description": "One CLI for all your AI coding agents - versions, config, cloud dispatch, sessions, and teams (now with first-class Grok Build CLI support)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|