@phnx-labs/agents-cli 1.20.21 → 1.20.23
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 +14 -0
- package/dist/commands/cloud.js +142 -13
- package/dist/commands/exec.js +13 -1
- package/dist/commands/menubar.d.ts +10 -0
- package/dist/commands/menubar.js +83 -0
- package/dist/commands/routines.js +34 -1
- package/dist/commands/secrets.d.ts +1 -1
- package/dist/commands/secrets.js +95 -38
- package/dist/index.js +292 -225
- package/dist/lib/agents.js +8 -0
- package/dist/lib/cloud/antigravity.d.ts +70 -0
- package/dist/lib/cloud/antigravity.js +196 -0
- package/dist/lib/cloud/codex.d.ts +1 -0
- package/dist/lib/cloud/codex.js +8 -2
- package/dist/lib/cloud/factory.d.ts +79 -18
- package/dist/lib/cloud/factory.js +324 -26
- package/dist/lib/cloud/registry.d.ts +18 -2
- package/dist/lib/cloud/registry.js +28 -4
- package/dist/lib/cloud/types.d.ts +73 -2
- package/dist/lib/cloud/types.js +17 -0
- package/dist/lib/exec.d.ts +2 -0
- package/dist/lib/exec.js +5 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/Info.plist +20 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/_CodeSignature/CodeResources +115 -0
- package/dist/lib/menubar/install-menubar.d.ts +57 -0
- package/dist/lib/menubar/install-menubar.js +291 -0
- package/dist/lib/secrets/agent.d.ts +9 -1
- package/dist/lib/secrets/agent.js +91 -10
- package/dist/lib/secrets/bundles.d.ts +19 -12
- package/dist/lib/secrets/bundles.js +22 -14
- package/dist/lib/self-update.d.ts +34 -0
- package/dist/lib/self-update.js +63 -2
- package/dist/lib/startup/command-registry.d.ts +99 -0
- package/dist/lib/startup/command-registry.js +136 -0
- package/dist/lib/types.d.ts +8 -0
- package/dist/lib/version.d.ts +11 -0
- package/dist/lib/version.js +20 -0
- package/package.json +5 -3
- package/scripts/postinstall.js +35 -0
|
@@ -39,15 +39,21 @@ export interface VarMeta {
|
|
|
39
39
|
note?: string;
|
|
40
40
|
}
|
|
41
41
|
/**
|
|
42
|
-
*
|
|
43
|
-
* - `
|
|
44
|
-
* agent; every other read pops Touch ID.
|
|
45
|
-
* to confirm
|
|
46
|
-
* - `
|
|
47
|
-
* is enabled) the first real
|
|
48
|
-
* read it silently.
|
|
42
|
+
* A bundle's prompt policy — how often macOS asks for Touch ID to read it:
|
|
43
|
+
* - `always` (default): asks every time. Only an explicit `agents secrets
|
|
44
|
+
* unlock` ever holds it in the secrets-agent; every other read pops Touch ID.
|
|
45
|
+
* Use for high-value bundles you want to confirm every time.
|
|
46
|
+
* - `daily`: ask once, then hold it silently. Eligible for the secrets-agent —
|
|
47
|
+
* `unlock` it, or (when `secrets.agent.auto` is enabled) the first real
|
|
48
|
+
* keychain read auto-loads it so concurrent runs read it silently. Held up to
|
|
49
|
+
* ~24h from that unlock (not refreshed on use); re-asks sooner after
|
|
50
|
+
* screen-lock, sleep, logout, or `agents secrets lock`.
|
|
51
|
+
*
|
|
52
|
+
* Stored on disk under the legacy `tier` key (`session` == `daily`; absent ==
|
|
53
|
+
* `always`) so bundles stay readable across mixed CLI versions on synced
|
|
54
|
+
* machines. The in-memory and user-facing vocabulary is `policy`/`always`/`daily`.
|
|
49
55
|
*/
|
|
50
|
-
export type
|
|
56
|
+
export type SecretsPolicy = 'always' | 'daily';
|
|
51
57
|
/** A named set of environment variable definitions backed by various secret providers. */
|
|
52
58
|
export interface SecretsBundle {
|
|
53
59
|
name: string;
|
|
@@ -55,8 +61,9 @@ export interface SecretsBundle {
|
|
|
55
61
|
allow_exec?: boolean;
|
|
56
62
|
/** Which store carries this bundle's items. Absent ⇒ `keychain` (the default). */
|
|
57
63
|
backend?: SecretsBackend;
|
|
58
|
-
/**
|
|
59
|
-
|
|
64
|
+
/** Prompt policy. Absent ⇒ `always` (the safe default). Serialized under the
|
|
65
|
+
* legacy `tier` key — see SecretsPolicy. */
|
|
66
|
+
policy?: SecretsPolicy;
|
|
60
67
|
/** ISO 8601 UTC timestamp. Set once on the first writeBundle() for a bundle. */
|
|
61
68
|
created_at?: string;
|
|
62
69
|
/** ISO 8601 UTC timestamp. Refreshed on every writeBundle(). */
|
|
@@ -90,8 +97,8 @@ export declare function validateSecretType(t: string): asserts t is SecretType;
|
|
|
90
97
|
export declare function validateExpiresFutureDated(iso: string): void;
|
|
91
98
|
export declare function bundleExists(name: string): boolean;
|
|
92
99
|
export declare function readBundle(name: string): SecretsBundle;
|
|
93
|
-
/** The effective
|
|
94
|
-
export declare function
|
|
100
|
+
/** The effective prompt policy of a bundle (absent ⇒ `always`). */
|
|
101
|
+
export declare function bundlePolicy(bundle: SecretsBundle): SecretsPolicy;
|
|
95
102
|
export declare function writeBundle(bundle: SecretsBundle): void;
|
|
96
103
|
export declare function deleteBundle(name: string): boolean;
|
|
97
104
|
export declare function listBundles(): SecretsBundle[];
|
|
@@ -223,10 +223,11 @@ export function readBundle(name) {
|
|
|
223
223
|
name,
|
|
224
224
|
description: parsed.description,
|
|
225
225
|
allow_exec: Boolean(parsed.allow_exec),
|
|
226
|
-
// Absent ⇒ keychain
|
|
227
|
-
//
|
|
226
|
+
// Absent ⇒ keychain; only set when file-backed so a keychain bundle
|
|
227
|
+
// round-trips byte-for-byte.
|
|
228
228
|
backend: backend === 'file' ? 'file' : undefined,
|
|
229
|
-
|
|
229
|
+
// Legacy wire key: the policy is persisted under `tier` (`session` == `daily`).
|
|
230
|
+
policy: parsePolicy(parsed.tier),
|
|
230
231
|
vars: parsed.vars && typeof parsed.vars === 'object' ? parsed.vars : {},
|
|
231
232
|
};
|
|
232
233
|
if (typeof parsed.created_at === 'string')
|
|
@@ -243,13 +244,16 @@ export function readBundle(name) {
|
|
|
243
244
|
}
|
|
244
245
|
return bundle;
|
|
245
246
|
}
|
|
246
|
-
/** Normalize
|
|
247
|
-
|
|
248
|
-
|
|
247
|
+
/** Normalize the persisted prompt policy. The on-disk `tier` key uses the
|
|
248
|
+
* legacy `session` token for `daily` (and `biometry`/absent for the default),
|
|
249
|
+
* so accept both the legacy and current tokens. Anything but `daily`/`session`
|
|
250
|
+
* ⇒ undefined (resolves to the `always` default). */
|
|
251
|
+
function parsePolicy(raw) {
|
|
252
|
+
return raw === 'daily' || raw === 'session' ? 'daily' : undefined;
|
|
249
253
|
}
|
|
250
|
-
/** The effective
|
|
251
|
-
export function
|
|
252
|
-
return bundle.
|
|
254
|
+
/** The effective prompt policy of a bundle (absent ⇒ `always`). */
|
|
255
|
+
export function bundlePolicy(bundle) {
|
|
256
|
+
return bundle.policy ?? 'always';
|
|
253
257
|
}
|
|
254
258
|
export function writeBundle(bundle) {
|
|
255
259
|
validateBundleName(bundle.name);
|
|
@@ -288,7 +292,9 @@ export function writeBundle(bundle) {
|
|
|
288
292
|
description: bundle.description,
|
|
289
293
|
allow_exec: bundle.allow_exec ? true : undefined,
|
|
290
294
|
backend: backend === 'file' ? 'file' : undefined,
|
|
291
|
-
|
|
295
|
+
// Wire format: persist `daily` under the legacy `tier`/`session` token so
|
|
296
|
+
// older CLI versions on other synced machines keep reading the policy.
|
|
297
|
+
tier: bundle.policy === 'daily' ? 'session' : undefined,
|
|
292
298
|
created_at: bundle.created_at,
|
|
293
299
|
updated_at: bundle.updated_at,
|
|
294
300
|
last_used: bundle.last_used,
|
|
@@ -328,7 +334,8 @@ function parseBundleMeta(name, json, backend) {
|
|
|
328
334
|
description: parsed.description,
|
|
329
335
|
allow_exec: Boolean(parsed.allow_exec),
|
|
330
336
|
backend: backend === 'file' ? 'file' : undefined,
|
|
331
|
-
|
|
337
|
+
// Legacy wire key: the policy is persisted under `tier` (`session` == `daily`).
|
|
338
|
+
policy: parsePolicy(parsed.tier),
|
|
332
339
|
vars: parsed.vars && typeof parsed.vars === 'object' ? parsed.vars : {},
|
|
333
340
|
};
|
|
334
341
|
if (typeof parsed.created_at === 'string')
|
|
@@ -568,7 +575,8 @@ export function readAndResolveBundleEnv(name, opts = {}) {
|
|
|
568
575
|
description: parsed.description,
|
|
569
576
|
allow_exec: Boolean(parsed.allow_exec),
|
|
570
577
|
backend: backend === 'file' ? 'file' : undefined,
|
|
571
|
-
|
|
578
|
+
// Legacy wire key: the policy is persisted under `tier` (`session` == `daily`).
|
|
579
|
+
policy: parsePolicy(parsed.tier),
|
|
572
580
|
vars: parsed.vars && typeof parsed.vars === 'object' ? parsed.vars : {},
|
|
573
581
|
};
|
|
574
582
|
if (typeof parsed.created_at === 'string')
|
|
@@ -639,14 +647,14 @@ export function readAndResolveBundleEnv(name, opts = {}) {
|
|
|
639
647
|
}
|
|
640
648
|
emitReadAudit('success');
|
|
641
649
|
// Auto-cache: this was a real keychain read (the agent fast-path returned
|
|
642
|
-
// earlier on a hit). If the bundle opts into the
|
|
650
|
+
// earlier on a hit). If the bundle opts into the `daily` policy and the user
|
|
643
651
|
// enabled `secrets.agent.auto`, populate the broker in the background so the
|
|
644
652
|
// next concurrent run reads silently. Skipped when noAgent (e.g. `unlock`,
|
|
645
653
|
// which loads the agent itself). Fire-and-forget — never blocks this read.
|
|
646
654
|
if (backend === 'keychain' &&
|
|
647
655
|
!opts.noAgent &&
|
|
648
656
|
process.env.AGENTS_SECRETS_NO_AGENT !== '1' &&
|
|
649
|
-
|
|
657
|
+
bundlePolicy(bundle) === 'daily' &&
|
|
650
658
|
secretsAgentAutoEnabled()) {
|
|
651
659
|
agentAutoLoadSync(name, bundle, env, DEFAULT_TTL_MS);
|
|
652
660
|
}
|
|
@@ -12,6 +12,31 @@
|
|
|
12
12
|
* to PATH resolution.
|
|
13
13
|
*/
|
|
14
14
|
export declare const NPM_PACKAGE_NAME = "@phnx-labs/agents-cli";
|
|
15
|
+
export type PackageManager = 'npm' | 'bun';
|
|
16
|
+
/**
|
|
17
|
+
* The directory bun installs global packages into:
|
|
18
|
+
* <BUN_INSTALL>/install/global (BUN_INSTALL defaults to ~/.bun)
|
|
19
|
+
*
|
|
20
|
+
* A globally-installed scoped package then lives at
|
|
21
|
+
* `<bunGlobalDir>/node_modules/@phnx-labs/agents-cli` — note there is NO `lib`
|
|
22
|
+
* segment, unlike npm's POSIX layout. That single difference is why an
|
|
23
|
+
* npm-based upgrade silently misses a bun install (see deriveGlobalPrefix).
|
|
24
|
+
*/
|
|
25
|
+
export declare function bunGlobalDir(): string;
|
|
26
|
+
/**
|
|
27
|
+
* Identify which package manager owns the install at `packageRoot`, so the
|
|
28
|
+
* upgrade can shell out to the one that actually replaces this copy.
|
|
29
|
+
*
|
|
30
|
+
* bun lays a global package out as `<bunGlobalDir>/node_modules/<scoped pkg>`,
|
|
31
|
+
* so the prefix (the parent of `node_modules`) is the bun global dir itself.
|
|
32
|
+
* Everything else — npm's `<prefix>/lib/node_modules` and the Windows
|
|
33
|
+
* `<prefix>/node_modules` — is treated as npm.
|
|
34
|
+
*
|
|
35
|
+
* Detection is path-based (no subprocess): it matches the resolved bun global
|
|
36
|
+
* dir from BUN_INSTALL/$HOME, and falls back to the structural `.bun/install/
|
|
37
|
+
* global` tail for a relocated BUN_INSTALL not exported into this process.
|
|
38
|
+
*/
|
|
39
|
+
export declare function detectPackageManager(packageRoot: string): PackageManager;
|
|
15
40
|
export interface UpdateCheckCache {
|
|
16
41
|
lastCheck: number;
|
|
17
42
|
latestVersion: string;
|
|
@@ -49,6 +74,15 @@ export declare function deriveGlobalPrefix(packageRoot: string): string;
|
|
|
49
74
|
* refreshAliasShims().
|
|
50
75
|
*/
|
|
51
76
|
export declare function installPackageIntoPrefix(spec: string, prefix: string): Promise<void>;
|
|
77
|
+
/**
|
|
78
|
+
* Install `spec` into bun's global store with `bun add -g`. bun writes to
|
|
79
|
+
* `<bunGlobalDir>/node_modules/<pkg>`, which is exactly the running package
|
|
80
|
+
* root for a bun install — so verifyInstalledVersion() sees the new version
|
|
81
|
+
* in place. bun skips untrusted lifecycle scripts, so the caller refreshes
|
|
82
|
+
* alias shims afterwards via refreshAliasShims() rather than relying on the
|
|
83
|
+
* package's postinstall hook.
|
|
84
|
+
*/
|
|
85
|
+
export declare function installPackageWithBun(spec: string): Promise<void>;
|
|
52
86
|
/** Read the version field of the package.json at `packageRoot`, fresh from disk. */
|
|
53
87
|
export declare function readInstalledVersion(packageRoot: string): string;
|
|
54
88
|
/**
|
package/dist/lib/self-update.js
CHANGED
|
@@ -12,10 +12,55 @@
|
|
|
12
12
|
* to PATH resolution.
|
|
13
13
|
*/
|
|
14
14
|
import * as fs from 'fs';
|
|
15
|
+
import * as os from 'os';
|
|
15
16
|
import * as path from 'path';
|
|
16
17
|
import { spawnSync } from 'child_process';
|
|
17
18
|
import { compareVersions } from './versions.js';
|
|
18
19
|
export const NPM_PACKAGE_NAME = '@phnx-labs/agents-cli';
|
|
20
|
+
/**
|
|
21
|
+
* The directory bun installs global packages into:
|
|
22
|
+
* <BUN_INSTALL>/install/global (BUN_INSTALL defaults to ~/.bun)
|
|
23
|
+
*
|
|
24
|
+
* A globally-installed scoped package then lives at
|
|
25
|
+
* `<bunGlobalDir>/node_modules/@phnx-labs/agents-cli` — note there is NO `lib`
|
|
26
|
+
* segment, unlike npm's POSIX layout. That single difference is why an
|
|
27
|
+
* npm-based upgrade silently misses a bun install (see deriveGlobalPrefix).
|
|
28
|
+
*/
|
|
29
|
+
export function bunGlobalDir() {
|
|
30
|
+
const bunInstall = process.env.BUN_INSTALL || path.join(os.homedir(), '.bun');
|
|
31
|
+
return path.join(bunInstall, 'install', 'global');
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Identify which package manager owns the install at `packageRoot`, so the
|
|
35
|
+
* upgrade can shell out to the one that actually replaces this copy.
|
|
36
|
+
*
|
|
37
|
+
* bun lays a global package out as `<bunGlobalDir>/node_modules/<scoped pkg>`,
|
|
38
|
+
* so the prefix (the parent of `node_modules`) is the bun global dir itself.
|
|
39
|
+
* Everything else — npm's `<prefix>/lib/node_modules` and the Windows
|
|
40
|
+
* `<prefix>/node_modules` — is treated as npm.
|
|
41
|
+
*
|
|
42
|
+
* Detection is path-based (no subprocess): it matches the resolved bun global
|
|
43
|
+
* dir from BUN_INSTALL/$HOME, and falls back to the structural `.bun/install/
|
|
44
|
+
* global` tail for a relocated BUN_INSTALL not exported into this process.
|
|
45
|
+
*/
|
|
46
|
+
export function detectPackageManager(packageRoot) {
|
|
47
|
+
const resolved = path.resolve(packageRoot);
|
|
48
|
+
const prefix = path.dirname(path.dirname(path.dirname(resolved))); // strip <scope>/<pkg>/node_modules
|
|
49
|
+
if (prefix === path.resolve(bunGlobalDir()))
|
|
50
|
+
return 'bun';
|
|
51
|
+
const parts = prefix.split(path.sep);
|
|
52
|
+
const n = parts.length;
|
|
53
|
+
if (n >= 3 && parts[n - 1] === 'global' && parts[n - 2] === 'install' && parts[n - 3] === '.bun') {
|
|
54
|
+
return 'bun';
|
|
55
|
+
}
|
|
56
|
+
return 'npm';
|
|
57
|
+
}
|
|
58
|
+
/** The shell command a user can run by hand to reproduce the upgrade for `manager`. */
|
|
59
|
+
function manualInstallHint(manager, packageRoot, spec) {
|
|
60
|
+
if (manager === 'bun')
|
|
61
|
+
return `bun add -g ${spec}`;
|
|
62
|
+
return `npm install -g --prefix ${deriveGlobalPrefix(packageRoot)} ${spec}`;
|
|
63
|
+
}
|
|
19
64
|
/** Read the cached update-check state from disk. Returns null if the file is missing or corrupt. */
|
|
20
65
|
export function readUpdateCache(file) {
|
|
21
66
|
try {
|
|
@@ -102,6 +147,20 @@ export async function installPackageIntoPrefix(spec, prefix) {
|
|
|
102
147
|
const execFileAsync = promisify(execFile);
|
|
103
148
|
await execFileAsync('npm', ['install', '-g', '--prefix', prefix, spec, '--ignore-scripts']);
|
|
104
149
|
}
|
|
150
|
+
/**
|
|
151
|
+
* Install `spec` into bun's global store with `bun add -g`. bun writes to
|
|
152
|
+
* `<bunGlobalDir>/node_modules/<pkg>`, which is exactly the running package
|
|
153
|
+
* root for a bun install — so verifyInstalledVersion() sees the new version
|
|
154
|
+
* in place. bun skips untrusted lifecycle scripts, so the caller refreshes
|
|
155
|
+
* alias shims afterwards via refreshAliasShims() rather than relying on the
|
|
156
|
+
* package's postinstall hook.
|
|
157
|
+
*/
|
|
158
|
+
export async function installPackageWithBun(spec) {
|
|
159
|
+
const { execFile } = await import('child_process');
|
|
160
|
+
const { promisify } = await import('util');
|
|
161
|
+
const execFileAsync = promisify(execFile);
|
|
162
|
+
await execFileAsync('bun', ['add', '-g', spec]);
|
|
163
|
+
}
|
|
105
164
|
/** Read the version field of the package.json at `packageRoot`, fresh from disk. */
|
|
106
165
|
export function readInstalledVersion(packageRoot) {
|
|
107
166
|
return JSON.parse(fs.readFileSync(path.join(packageRoot, 'package.json'), 'utf-8')).version;
|
|
@@ -113,8 +172,10 @@ export function readInstalledVersion(packageRoot) {
|
|
|
113
172
|
export function verifyInstalledVersion(packageRoot, expectedVersion) {
|
|
114
173
|
const actual = readInstalledVersion(packageRoot);
|
|
115
174
|
if (actual !== expectedVersion) {
|
|
116
|
-
|
|
117
|
-
|
|
175
|
+
const manager = detectPackageManager(packageRoot);
|
|
176
|
+
const hint = manualInstallHint(manager, packageRoot, `${NPM_PACKAGE_NAME}@${expectedVersion}`);
|
|
177
|
+
throw new Error(`the package manager reported success but ${packageRoot} is still ${actual} (expected ${expectedVersion}). ` +
|
|
178
|
+
`Run manually: ${hint}`);
|
|
118
179
|
}
|
|
119
180
|
}
|
|
120
181
|
/**
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lazy command registry.
|
|
3
|
+
*
|
|
4
|
+
* The CLI entry point (src/index.ts) used to statically import every command
|
|
5
|
+
* module and call its `registerXCommand(program)` on every invocation. That
|
|
6
|
+
* loaded the entire command tree (~50 modules) before the first line of output,
|
|
7
|
+
* dominating cold-start latency.
|
|
8
|
+
*
|
|
9
|
+
* This module maps each user-typed top-level command name to a thunk that
|
|
10
|
+
* dynamically imports ONLY the module(s) that command needs. Fast commands
|
|
11
|
+
* (`--version`, `view`, ...) now pay for just the one module they use; the full
|
|
12
|
+
* tree is loaded only on the rare slow paths (unknown-command spellcheck, bare
|
|
13
|
+
* help) via `registerAllEagerCommands` in src/index.ts.
|
|
14
|
+
*
|
|
15
|
+
* Parity is non-negotiable: the name -> loader map below mirrors exactly which
|
|
16
|
+
* module registers which top-level command on `main`. Multi-command modules
|
|
17
|
+
* (versions, packages) map several names to the same loader; `prune` needs BOTH
|
|
18
|
+
* versions (which creates `prune <specs...>`) and prune.js (which attaches the
|
|
19
|
+
* `cleanup` subcommand to it), in that order — see commands/prune.ts.
|
|
20
|
+
*/
|
|
21
|
+
import type { Command } from 'commander';
|
|
22
|
+
/** A function that registers one or more commands onto the root program. */
|
|
23
|
+
export type Registrar = (program: Command) => void;
|
|
24
|
+
/** A thunk that dynamically imports a command module and returns its registrar. */
|
|
25
|
+
export type ModuleLoader = () => Promise<Registrar>;
|
|
26
|
+
export declare const loadView: ModuleLoader;
|
|
27
|
+
export declare const loadInspect: ModuleLoader;
|
|
28
|
+
export declare const loadFeedback: ModuleLoader;
|
|
29
|
+
export declare const loadCommands: ModuleLoader;
|
|
30
|
+
export declare const loadHooks: ModuleLoader;
|
|
31
|
+
export declare const loadSkills: ModuleLoader;
|
|
32
|
+
export declare const loadRules: ModuleLoader;
|
|
33
|
+
export declare const loadPermissions: ModuleLoader;
|
|
34
|
+
export declare const loadMcp: ModuleLoader;
|
|
35
|
+
export declare const loadCli: ModuleLoader;
|
|
36
|
+
export declare const loadSubagents: ModuleLoader;
|
|
37
|
+
export declare const loadPlugins: ModuleLoader;
|
|
38
|
+
export declare const loadWorkflows: ModuleLoader;
|
|
39
|
+
export declare const loadWorktree: ModuleLoader;
|
|
40
|
+
export declare const loadVersions: ModuleLoader;
|
|
41
|
+
export declare const loadImport: ModuleLoader;
|
|
42
|
+
export declare const loadPackages: ModuleLoader;
|
|
43
|
+
export declare const loadDaemon: ModuleLoader;
|
|
44
|
+
export declare const loadRoutines: ModuleLoader;
|
|
45
|
+
export declare const loadRun: ModuleLoader;
|
|
46
|
+
export declare const loadDefaults: ModuleLoader;
|
|
47
|
+
export declare const loadModels: ModuleLoader;
|
|
48
|
+
export declare const loadPrune: ModuleLoader;
|
|
49
|
+
export declare const loadTrash: ModuleLoader;
|
|
50
|
+
export declare const loadRestore: ModuleLoader;
|
|
51
|
+
export declare const loadDoctor: ModuleLoader;
|
|
52
|
+
export declare const loadProfiles: ModuleLoader;
|
|
53
|
+
export declare const loadSecrets: ModuleLoader;
|
|
54
|
+
export declare const loadWallet: ModuleLoader;
|
|
55
|
+
export declare const loadHelper: ModuleLoader;
|
|
56
|
+
export declare const loadMenubar: ModuleLoader;
|
|
57
|
+
export declare const loadBeta: ModuleLoader;
|
|
58
|
+
export declare const loadSync: ModuleLoader;
|
|
59
|
+
export declare const loadRefreshRules: ModuleLoader;
|
|
60
|
+
export declare const loadDrive: ModuleLoader;
|
|
61
|
+
export declare const loadFactory: ModuleLoader;
|
|
62
|
+
export declare const loadUsage: ModuleLoader;
|
|
63
|
+
export declare const loadCost: ModuleLoader;
|
|
64
|
+
export declare const loadBudget: ModuleLoader;
|
|
65
|
+
export declare const loadAlias: ModuleLoader;
|
|
66
|
+
export declare const loadPty: ModuleLoader;
|
|
67
|
+
export declare const loadTmux: ModuleLoader;
|
|
68
|
+
export declare const loadBrowser: ModuleLoader;
|
|
69
|
+
export declare const loadComputer: ModuleLoader;
|
|
70
|
+
export declare const loadPull: ModuleLoader;
|
|
71
|
+
export declare const loadPush: ModuleLoader;
|
|
72
|
+
export declare const loadRepo: ModuleLoader;
|
|
73
|
+
export declare const loadSetup: ModuleLoader;
|
|
74
|
+
export declare const loadSessions: ModuleLoader;
|
|
75
|
+
export declare const loadTeams: ModuleLoader;
|
|
76
|
+
export declare const loadCloud: ModuleLoader;
|
|
77
|
+
/**
|
|
78
|
+
* Commands whose modules pull in the SQLite-backed session/cloud stack. They are
|
|
79
|
+
* registered AFTER `applyGlobalHelpConventions` (mirroring main's order: help
|
|
80
|
+
* conventions at module top-level, lazy registration just before parse), so they
|
|
81
|
+
* inherit the root's custom help formatter rather than getting the per-command
|
|
82
|
+
* recursive pass. Keeping that ordering preserves their `--help` output exactly.
|
|
83
|
+
*/
|
|
84
|
+
export declare const LAZY_COMMAND_NAMES: ReadonlySet<string>;
|
|
85
|
+
/**
|
|
86
|
+
* User-typed top-level command name -> ordered list of module loaders to run.
|
|
87
|
+
*
|
|
88
|
+
* Most names map to a single loader. The exceptions encode real coupling on main:
|
|
89
|
+
* - `add`/`use`/`list`/`remove`/`rm`/`purge` all come from the versions module.
|
|
90
|
+
* - `registry`/`search`/`install` all come from the packages module.
|
|
91
|
+
* - `trash` and `restore` are separate registrars in the trash module.
|
|
92
|
+
* - `prune` needs versions FIRST (it creates `prune <specs...>`) then prune.js
|
|
93
|
+
* (which finds that command and attaches the `cleanup` subcommand).
|
|
94
|
+
*
|
|
95
|
+
* Inline deprecated aliases (memory/perms/exec/jobs/cron) and the inline
|
|
96
|
+
* `upgrade` command are NOT here — they are closures over entry-point state and
|
|
97
|
+
* are handled directly in src/index.ts.
|
|
98
|
+
*/
|
|
99
|
+
export declare const COMMAND_LOADERS: Record<string, ModuleLoader[]>;
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
// One loader per command module. Each dynamically imports the module and hands
|
|
2
|
+
// back its register function. Kept as named consts so src/index.ts can compose
|
|
3
|
+
// them into the exact main-branch registration order for the slow path.
|
|
4
|
+
export const loadView = async () => (await import('../../commands/view.js')).registerViewCommand;
|
|
5
|
+
export const loadInspect = async () => (await import('../../commands/inspect.js')).registerInspectCommand;
|
|
6
|
+
export const loadFeedback = async () => (await import('../../commands/feedback.js')).registerFeedbackCommand;
|
|
7
|
+
export const loadCommands = async () => (await import('../../commands/commands.js')).registerCommandsCommands;
|
|
8
|
+
export const loadHooks = async () => (await import('../../commands/hooks.js')).registerHooksCommands;
|
|
9
|
+
export const loadSkills = async () => (await import('../../commands/skills.js')).registerSkillsCommands;
|
|
10
|
+
export const loadRules = async () => (await import('../../commands/rules.js')).registerRulesCommands;
|
|
11
|
+
export const loadPermissions = async () => (await import('../../commands/permissions.js')).registerPermissionsCommands;
|
|
12
|
+
export const loadMcp = async () => (await import('../../commands/mcp.js')).registerMcpCommands;
|
|
13
|
+
export const loadCli = async () => (await import('../../commands/cli.js')).registerCliCommands;
|
|
14
|
+
export const loadSubagents = async () => (await import('../../commands/subagents.js')).registerSubagentsCommands;
|
|
15
|
+
export const loadPlugins = async () => (await import('../../commands/plugins.js')).registerPluginsCommands;
|
|
16
|
+
export const loadWorkflows = async () => (await import('../../commands/workflows.js')).registerWorkflowsCommands;
|
|
17
|
+
export const loadWorktree = async () => (await import('../../commands/worktree.js')).registerWorktreeCommands;
|
|
18
|
+
export const loadVersions = async () => (await import('../../commands/versions.js')).registerVersionsCommands;
|
|
19
|
+
export const loadImport = async () => (await import('../../commands/import.js')).registerImportCommand;
|
|
20
|
+
export const loadPackages = async () => (await import('../../commands/packages.js')).registerPackagesCommands;
|
|
21
|
+
export const loadDaemon = async () => (await import('../../commands/daemon.js')).registerDaemonCommands;
|
|
22
|
+
export const loadRoutines = async () => (await import('../../commands/routines.js')).registerRoutinesCommands;
|
|
23
|
+
export const loadRun = async () => (await import('../../commands/exec.js')).registerRunCommand;
|
|
24
|
+
export const loadDefaults = async () => (await import('../../commands/defaults.js')).registerDefaultsCommands;
|
|
25
|
+
export const loadModels = async () => (await import('../../commands/models.js')).registerModelsCommand;
|
|
26
|
+
export const loadPrune = async () => (await import('../../commands/prune.js')).registerPruneCommand;
|
|
27
|
+
export const loadTrash = async () => (await import('../../commands/trash.js')).registerTrashCommands;
|
|
28
|
+
export const loadRestore = async () => (await import('../../commands/trash.js')).registerRestoreCommand;
|
|
29
|
+
export const loadDoctor = async () => (await import('../../commands/doctor.js')).registerDoctorCommand;
|
|
30
|
+
export const loadProfiles = async () => (await import('../../commands/profiles.js')).registerProfilesCommands;
|
|
31
|
+
export const loadSecrets = async () => (await import('../../commands/secrets.js')).registerSecretsCommands;
|
|
32
|
+
export const loadWallet = async () => (await import('../../commands/wallet.js')).registerWalletCommands;
|
|
33
|
+
export const loadHelper = async () => (await import('../../commands/helper.js')).registerHelperCommand;
|
|
34
|
+
export const loadMenubar = async () => (await import('../../commands/menubar.js')).registerMenubarCommands;
|
|
35
|
+
export const loadBeta = async () => (await import('../../commands/beta.js')).registerBetaCommands;
|
|
36
|
+
export const loadSync = async () => (await import('../../commands/sync.js')).registerSyncCommand;
|
|
37
|
+
export const loadRefreshRules = async () => (await import('../../commands/refresh-rules.js')).registerRefreshRulesCommand;
|
|
38
|
+
export const loadDrive = async () => (await import('../../commands/drive.js')).registerDriveCommands;
|
|
39
|
+
export const loadFactory = async () => (await import('../../commands/factory.js')).registerFactoryCommands;
|
|
40
|
+
export const loadUsage = async () => (await import('../../commands/usage.js')).registerUsageCommand;
|
|
41
|
+
export const loadCost = async () => (await import('../../commands/cost.js')).registerCostCommand;
|
|
42
|
+
export const loadBudget = async () => (await import('../../commands/budget.js')).registerBudgetCommand;
|
|
43
|
+
export const loadAlias = async () => (await import('../../commands/alias.js')).registerAliasCommand;
|
|
44
|
+
export const loadPty = async () => (await import('../../commands/pty.js')).registerPtyCommands;
|
|
45
|
+
export const loadTmux = async () => (await import('../../commands/tmux.js')).registerTmuxCommands;
|
|
46
|
+
export const loadBrowser = async () => (await import('../../commands/browser.js')).registerBrowserCommand;
|
|
47
|
+
export const loadComputer = async () => (await import('../../commands/computer.js')).registerComputerCommand;
|
|
48
|
+
export const loadPull = async () => (await import('../../commands/pull.js')).registerPullCommand;
|
|
49
|
+
export const loadPush = async () => (await import('../../commands/push.js')).registerPushCommand;
|
|
50
|
+
export const loadRepo = async () => (await import('../../commands/repo.js')).registerRepoCommands;
|
|
51
|
+
export const loadSetup = async () => (await import('../../commands/setup.js')).registerSetupCommand;
|
|
52
|
+
export const loadSessions = async () => (await import('../../commands/sessions.js')).registerSessionsCommands;
|
|
53
|
+
export const loadTeams = async () => (await import('../../commands/teams.js')).registerTeamsCommands;
|
|
54
|
+
export const loadCloud = async () => (await import('../../commands/cloud.js')).registerCloudCommands;
|
|
55
|
+
/**
|
|
56
|
+
* Commands whose modules pull in the SQLite-backed session/cloud stack. They are
|
|
57
|
+
* registered AFTER `applyGlobalHelpConventions` (mirroring main's order: help
|
|
58
|
+
* conventions at module top-level, lazy registration just before parse), so they
|
|
59
|
+
* inherit the root's custom help formatter rather than getting the per-command
|
|
60
|
+
* recursive pass. Keeping that ordering preserves their `--help` output exactly.
|
|
61
|
+
*/
|
|
62
|
+
export const LAZY_COMMAND_NAMES = new Set(['sessions', 'teams', 'cloud']);
|
|
63
|
+
/**
|
|
64
|
+
* User-typed top-level command name -> ordered list of module loaders to run.
|
|
65
|
+
*
|
|
66
|
+
* Most names map to a single loader. The exceptions encode real coupling on main:
|
|
67
|
+
* - `add`/`use`/`list`/`remove`/`rm`/`purge` all come from the versions module.
|
|
68
|
+
* - `registry`/`search`/`install` all come from the packages module.
|
|
69
|
+
* - `trash` and `restore` are separate registrars in the trash module.
|
|
70
|
+
* - `prune` needs versions FIRST (it creates `prune <specs...>`) then prune.js
|
|
71
|
+
* (which finds that command and attaches the `cleanup` subcommand).
|
|
72
|
+
*
|
|
73
|
+
* Inline deprecated aliases (memory/perms/exec/jobs/cron) and the inline
|
|
74
|
+
* `upgrade` command are NOT here — they are closures over entry-point state and
|
|
75
|
+
* are handled directly in src/index.ts.
|
|
76
|
+
*/
|
|
77
|
+
export const COMMAND_LOADERS = {
|
|
78
|
+
view: [loadView],
|
|
79
|
+
inspect: [loadInspect],
|
|
80
|
+
feedback: [loadFeedback],
|
|
81
|
+
commands: [loadCommands],
|
|
82
|
+
hooks: [loadHooks],
|
|
83
|
+
skills: [loadSkills],
|
|
84
|
+
rules: [loadRules],
|
|
85
|
+
permissions: [loadPermissions],
|
|
86
|
+
mcp: [loadMcp],
|
|
87
|
+
cli: [loadCli],
|
|
88
|
+
subagents: [loadSubagents],
|
|
89
|
+
plugins: [loadPlugins],
|
|
90
|
+
workflows: [loadWorkflows],
|
|
91
|
+
worktree: [loadWorktree],
|
|
92
|
+
add: [loadVersions],
|
|
93
|
+
use: [loadVersions],
|
|
94
|
+
list: [loadVersions],
|
|
95
|
+
remove: [loadVersions],
|
|
96
|
+
rm: [loadVersions],
|
|
97
|
+
purge: [loadVersions],
|
|
98
|
+
prune: [loadVersions, loadPrune],
|
|
99
|
+
import: [loadImport],
|
|
100
|
+
registry: [loadPackages],
|
|
101
|
+
search: [loadPackages],
|
|
102
|
+
install: [loadPackages],
|
|
103
|
+
daemon: [loadDaemon],
|
|
104
|
+
routines: [loadRoutines],
|
|
105
|
+
run: [loadRun],
|
|
106
|
+
defaults: [loadDefaults],
|
|
107
|
+
models: [loadModels],
|
|
108
|
+
trash: [loadTrash],
|
|
109
|
+
restore: [loadRestore],
|
|
110
|
+
doctor: [loadDoctor],
|
|
111
|
+
profiles: [loadProfiles],
|
|
112
|
+
secrets: [loadSecrets],
|
|
113
|
+
wallet: [loadWallet],
|
|
114
|
+
helper: [loadHelper],
|
|
115
|
+
menubar: [loadMenubar],
|
|
116
|
+
beta: [loadBeta],
|
|
117
|
+
sync: [loadSync],
|
|
118
|
+
'refresh-rules': [loadRefreshRules],
|
|
119
|
+
drive: [loadDrive],
|
|
120
|
+
factory: [loadFactory],
|
|
121
|
+
usage: [loadUsage],
|
|
122
|
+
cost: [loadCost],
|
|
123
|
+
budget: [loadBudget],
|
|
124
|
+
alias: [loadAlias],
|
|
125
|
+
pty: [loadPty],
|
|
126
|
+
tmux: [loadTmux],
|
|
127
|
+
browser: [loadBrowser],
|
|
128
|
+
computer: [loadComputer],
|
|
129
|
+
pull: [loadPull],
|
|
130
|
+
push: [loadPush],
|
|
131
|
+
repo: [loadRepo],
|
|
132
|
+
setup: [loadSetup],
|
|
133
|
+
sessions: [loadSessions],
|
|
134
|
+
teams: [loadTeams],
|
|
135
|
+
cloud: [loadCloud],
|
|
136
|
+
};
|
package/dist/lib/types.d.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* configuration schemas, resource tracking, registry types, and permission
|
|
6
6
|
* formats for each supported agent.
|
|
7
7
|
*/
|
|
8
|
+
import type { CloudProviderId } from './cloud/types.js';
|
|
8
9
|
/** Unique identifier for a supported AI coding agent. */
|
|
9
10
|
export type AgentId = 'claude' | 'codex' | 'gemini' | 'cursor' | 'opencode' | 'openclaw' | 'copilot' | 'amp' | 'kiro' | 'goose' | 'roo' | 'antigravity' | 'grok' | 'kimi' | 'droid';
|
|
10
11
|
/** How `agents run <agent>` chooses an installed version when none is pinned. */
|
|
@@ -83,6 +84,13 @@ export interface AgentConfig {
|
|
|
83
84
|
variableSyntax: string;
|
|
84
85
|
supportsHooks: boolean;
|
|
85
86
|
nativeAgentsSkillsDir?: boolean;
|
|
87
|
+
/**
|
|
88
|
+
* This agent's *own* cloud backend. `agents cloud run --agent <id>` routes
|
|
89
|
+
* here when no `--provider` is given (precedence: --provider > this >
|
|
90
|
+
* cloud.default_provider > rush). Undefined means the agent has no native
|
|
91
|
+
* cloud and falls back to the configured default.
|
|
92
|
+
*/
|
|
93
|
+
cloudProvider?: CloudProviderId;
|
|
86
94
|
capabilities: {
|
|
87
95
|
hooks: Capability;
|
|
88
96
|
mcp: Capability;
|
package/dist/lib/version.d.ts
CHANGED
|
@@ -5,3 +5,14 @@
|
|
|
5
5
|
* get stale behavior without this check.
|
|
6
6
|
*/
|
|
7
7
|
export declare function getCliVersion(): string;
|
|
8
|
+
/**
|
|
9
|
+
* Read the version from package.json on disk every call, bypassing the cache.
|
|
10
|
+
*
|
|
11
|
+
* `getCliVersion()` memoizes the version a long-running process *started* with.
|
|
12
|
+
* After `npm i -g` overwrites the install in place, the on-disk package.json
|
|
13
|
+
* changes but the running process keeps its old in-memory code. Comparing this
|
|
14
|
+
* fresh read against the cached startup value is how a daemon/broker detects it
|
|
15
|
+
* is now stale and should reload onto the new code (self-healing). Returns
|
|
16
|
+
* 'unknown' on any error.
|
|
17
|
+
*/
|
|
18
|
+
export declare function getCliVersionFresh(): string;
|
package/dist/lib/version.js
CHANGED
|
@@ -23,3 +23,23 @@ export function getCliVersion() {
|
|
|
23
23
|
}
|
|
24
24
|
return cached;
|
|
25
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Read the version from package.json on disk every call, bypassing the cache.
|
|
28
|
+
*
|
|
29
|
+
* `getCliVersion()` memoizes the version a long-running process *started* with.
|
|
30
|
+
* After `npm i -g` overwrites the install in place, the on-disk package.json
|
|
31
|
+
* changes but the running process keeps its old in-memory code. Comparing this
|
|
32
|
+
* fresh read against the cached startup value is how a daemon/broker detects it
|
|
33
|
+
* is now stale and should reload onto the new code (self-healing). Returns
|
|
34
|
+
* 'unknown' on any error.
|
|
35
|
+
*/
|
|
36
|
+
export function getCliVersionFresh() {
|
|
37
|
+
try {
|
|
38
|
+
const pkgPath = path.join(__dirname, '..', '..', 'package.json');
|
|
39
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
40
|
+
return String(pkg.version || 'unknown');
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return 'unknown';
|
|
44
|
+
}
|
|
45
|
+
}
|
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.23",
|
|
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",
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"dist/**/*.d.ts",
|
|
26
26
|
"dist/**/*.json",
|
|
27
27
|
"dist/lib/secrets/Agents CLI.app/**",
|
|
28
|
+
"dist/lib/menubar/MenubarHelper.app/**",
|
|
28
29
|
"scripts/postinstall.js",
|
|
29
30
|
"scripts/install-helper.js",
|
|
30
31
|
"CHANGELOG.md",
|
|
@@ -43,8 +44,9 @@
|
|
|
43
44
|
"url": "https://github.com/phnx-labs/agents-cli/issues"
|
|
44
45
|
},
|
|
45
46
|
"scripts": {
|
|
46
|
-
"build": "tsc && rm -rf 'dist/lib/secrets/AgentsKeychain.app' 'dist/lib/secrets/Agents CLI.app' && ([ \"$(uname)\" = \"Darwin\" ] && cp -R 'bin/Agents CLI.app' 'dist/lib/secrets/Agents CLI.app' || true)",
|
|
47
|
-
"
|
|
47
|
+
"build": "tsc && rm -rf 'dist/lib/secrets/AgentsKeychain.app' 'dist/lib/secrets/Agents CLI.app' 'dist/lib/menubar/MenubarHelper.app' && ([ \"$(uname)\" = \"Darwin\" ] && cp -R 'bin/Agents CLI.app' 'dist/lib/secrets/Agents CLI.app' || true) && ([ \"$(uname)\" = \"Darwin\" ] && [ -d 'bin/MenubarHelper.app' ] && mkdir -p 'dist/lib/menubar' && cp -R 'bin/MenubarHelper.app' 'dist/lib/menubar/MenubarHelper.app' || true)",
|
|
48
|
+
"build:bin": "scripts/build-bin.sh",
|
|
49
|
+
"prepack": "scripts/verify-keychain-helper.sh && scripts/verify-menubar-helper.sh",
|
|
48
50
|
"postinstall": "node scripts/postinstall.js",
|
|
49
51
|
"dev": "tsx src/index.ts",
|
|
50
52
|
"start": "node dist/index.js",
|