@phnx-labs/agents-cli 1.14.6 → 1.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +148 -1
- package/dist/commands/beta.js +6 -1
- package/dist/commands/exec.js +9 -2
- package/dist/commands/init.js +10 -0
- package/dist/commands/mcp.js +4 -4
- package/dist/commands/prune.d.ts +0 -20
- package/dist/commands/prune.js +268 -15
- package/dist/commands/secrets.js +83 -0
- package/dist/commands/teams.js +2 -3
- package/dist/commands/usage.js +6 -0
- package/dist/commands/versions.js +8 -6
- package/dist/lib/browser/chrome.js +1 -1
- package/dist/lib/browser/drivers/ssh.d.ts +1 -0
- package/dist/lib/browser/drivers/ssh.js +23 -2
- package/dist/lib/browser/ipc.js +1 -0
- package/dist/lib/browser/service.d.ts +3 -0
- package/dist/lib/browser/service.js +114 -6
- package/dist/lib/daemon.js +4 -4
- package/dist/lib/events.d.ts +159 -0
- package/dist/lib/events.js +441 -0
- package/dist/lib/exec.js +29 -6
- package/dist/lib/permissions.d.ts +6 -3
- package/dist/lib/permissions.js +38 -34
- package/dist/lib/routines.d.ts +15 -0
- package/dist/lib/routines.js +68 -0
- package/dist/lib/runner.js +15 -0
- package/dist/lib/secrets/bundles.js +7 -1
- package/dist/lib/secrets/index.d.ts +14 -11
- package/dist/lib/secrets/index.js +49 -21
- package/dist/lib/secrets/linux.d.ts +27 -0
- package/dist/lib/secrets/linux.js +161 -0
- package/dist/lib/session/db.d.ts +4 -0
- package/dist/lib/session/db.js +26 -0
- package/dist/lib/skills.js +4 -0
- package/dist/lib/usage.d.ts +1 -1
- package/dist/lib/usage.js +13 -46
- package/dist/lib/versions.js +16 -0
- package/package.json +1 -1
- package/scripts/postinstall.js +37 -9
package/dist/lib/usage.js
CHANGED
|
@@ -15,6 +15,7 @@ import * as readline from 'readline';
|
|
|
15
15
|
import { promisify } from 'util';
|
|
16
16
|
import chalk from 'chalk';
|
|
17
17
|
import { walkForFiles } from './fs-walk.js';
|
|
18
|
+
import { getKeychainToken, setKeychainToken, deleteKeychainToken, } from './secrets/index.js';
|
|
18
19
|
import { getAgentsDir } from './state.js';
|
|
19
20
|
const execFileAsync = promisify(execFile);
|
|
20
21
|
const CLAUDE_USAGE_URL = 'https://api.anthropic.com/api/oauth/usage';
|
|
@@ -343,27 +344,15 @@ function normalizeClaudeWindow(window, key, label, shortLabel) {
|
|
|
343
344
|
windowMinutes: inferWindowMinutes(key),
|
|
344
345
|
};
|
|
345
346
|
}
|
|
346
|
-
|
|
347
|
-
/** Load Claude OAuth credentials from the macOS Keychain. */
|
|
347
|
+
/** Load Claude OAuth credentials from the system keychain/keyring. */
|
|
348
348
|
export async function loadClaudeOauth(home) {
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
process.stderr.write('[agents] Usage tracking requires macOS Keychain. Skipped on this platform.\n');
|
|
352
|
-
warnedNonDarwin = true;
|
|
353
|
-
}
|
|
349
|
+
// Windows not yet supported
|
|
350
|
+
if (process.platform !== 'darwin' && process.platform !== 'linux') {
|
|
354
351
|
return null;
|
|
355
352
|
}
|
|
356
353
|
try {
|
|
357
|
-
const
|
|
358
|
-
const
|
|
359
|
-
'find-generic-password',
|
|
360
|
-
'-a',
|
|
361
|
-
account,
|
|
362
|
-
'-s',
|
|
363
|
-
// Managed Claude homes must stay pinned to their own service name.
|
|
364
|
-
getClaudeKeychainService(home),
|
|
365
|
-
'-w',
|
|
366
|
-
]);
|
|
354
|
+
const service = getClaudeKeychainService(home);
|
|
355
|
+
const stdout = getKeychainToken(service);
|
|
367
356
|
const payload = JSON.parse(stdout.trim());
|
|
368
357
|
if (typeof payload?.claudeAiOauth?.accessToken !== 'string')
|
|
369
358
|
return null;
|
|
@@ -380,27 +369,20 @@ export async function loadClaudeOauth(home) {
|
|
|
380
369
|
}
|
|
381
370
|
}
|
|
382
371
|
/**
|
|
383
|
-
* Save Claude OAuth credentials to the
|
|
372
|
+
* Save Claude OAuth credentials to the system keychain/keyring.
|
|
384
373
|
* Reads the existing payload, merges the new OAuth fields, and writes back.
|
|
385
374
|
*/
|
|
386
375
|
async function saveClaudeOauth(home, credentials) {
|
|
387
|
-
|
|
376
|
+
// Windows not yet supported
|
|
377
|
+
if (process.platform !== 'darwin' && process.platform !== 'linux') {
|
|
388
378
|
return false;
|
|
389
379
|
}
|
|
390
380
|
try {
|
|
391
|
-
const account = os.userInfo().username;
|
|
392
381
|
const service = getClaudeKeychainService(home);
|
|
393
382
|
// Read existing payload to preserve other fields
|
|
394
383
|
let existingPayload = {};
|
|
395
384
|
try {
|
|
396
|
-
const
|
|
397
|
-
'find-generic-password',
|
|
398
|
-
'-a',
|
|
399
|
-
account,
|
|
400
|
-
'-s',
|
|
401
|
-
service,
|
|
402
|
-
'-w',
|
|
403
|
-
]);
|
|
385
|
+
const stdout = getKeychainToken(service);
|
|
404
386
|
existingPayload = JSON.parse(stdout.trim());
|
|
405
387
|
}
|
|
406
388
|
catch {
|
|
@@ -418,29 +400,14 @@ async function saveClaudeOauth(home, credentials) {
|
|
|
418
400
|
},
|
|
419
401
|
};
|
|
420
402
|
const payloadJson = JSON.stringify(newPayload);
|
|
421
|
-
// Delete existing entry first
|
|
403
|
+
// Delete existing entry first, then add updated entry
|
|
422
404
|
try {
|
|
423
|
-
|
|
424
|
-
'delete-generic-password',
|
|
425
|
-
'-a',
|
|
426
|
-
account,
|
|
427
|
-
'-s',
|
|
428
|
-
service,
|
|
429
|
-
]);
|
|
405
|
+
deleteKeychainToken(service);
|
|
430
406
|
}
|
|
431
407
|
catch {
|
|
432
408
|
// Entry might not exist, ignore
|
|
433
409
|
}
|
|
434
|
-
|
|
435
|
-
await execFileAsync('security', [
|
|
436
|
-
'add-generic-password',
|
|
437
|
-
'-a',
|
|
438
|
-
account,
|
|
439
|
-
'-s',
|
|
440
|
-
service,
|
|
441
|
-
'-w',
|
|
442
|
-
payloadJson,
|
|
443
|
-
]);
|
|
410
|
+
setKeychainToken(service, payloadJson);
|
|
444
411
|
return true;
|
|
445
412
|
}
|
|
446
413
|
catch {
|
package/dist/lib/versions.js
CHANGED
|
@@ -37,6 +37,7 @@ import { discoverPlugins, syncPluginToVersion, isPluginSynced, pluginSupportsAge
|
|
|
37
37
|
import { composeRulesFromState } from './rules/compose.js';
|
|
38
38
|
import { loadSyncManifest, saveSyncManifest, buildManifest, isSyncStale } from './sync-manifest.js';
|
|
39
39
|
import { PLUGINS_CAPABLE_AGENTS } from './agents.js';
|
|
40
|
+
import { emit } from './events.js';
|
|
40
41
|
import { safeJoin } from './paths.js';
|
|
41
42
|
import { installCommandSkillToVersion, listCommandSkillsInVersion, shouldInstallCommandAsSkill } from './command-skills.js';
|
|
42
43
|
/** Promisified exec for running shell commands. */
|
|
@@ -909,6 +910,7 @@ export function setGlobalDefault(agent, version) {
|
|
|
909
910
|
}
|
|
910
911
|
else {
|
|
911
912
|
meta.agents[agent] = version;
|
|
913
|
+
emit('version.switch', { agent, version });
|
|
912
914
|
}
|
|
913
915
|
writeMeta(meta);
|
|
914
916
|
}
|
|
@@ -943,6 +945,17 @@ export async function installVersion(agent, version, onProgress) {
|
|
|
943
945
|
throw new Error(`Invalid version: ${JSON.stringify(version)}`);
|
|
944
946
|
}
|
|
945
947
|
try {
|
|
948
|
+
// Check npm is available
|
|
949
|
+
try {
|
|
950
|
+
await execFileAsync('which', ['npm']);
|
|
951
|
+
}
|
|
952
|
+
catch {
|
|
953
|
+
return {
|
|
954
|
+
success: false,
|
|
955
|
+
installedVersion: version,
|
|
956
|
+
error: 'npm is not installed. Install Node.js and npm first: https://nodejs.org/',
|
|
957
|
+
};
|
|
958
|
+
}
|
|
946
959
|
onProgress?.(`Installing ${packageSpec}...`);
|
|
947
960
|
const { stdout } = await execFileAsync('npm', ['install', packageSpec], { cwd: versionDir });
|
|
948
961
|
// Determine the actual installed version
|
|
@@ -989,6 +1002,7 @@ export async function installVersion(agent, version, onProgress) {
|
|
|
989
1002
|
/* non-fatal; the install itself succeeded */
|
|
990
1003
|
}
|
|
991
1004
|
}
|
|
1005
|
+
emit('version.install', { agent, version: installedVersion });
|
|
992
1006
|
return { success: true, installedVersion };
|
|
993
1007
|
}
|
|
994
1008
|
catch (err) {
|
|
@@ -997,6 +1011,7 @@ export async function installVersion(agent, version, onProgress) {
|
|
|
997
1011
|
if (fs.existsSync(versionDir)) {
|
|
998
1012
|
removeInstallArtifacts(versionDir);
|
|
999
1013
|
}
|
|
1014
|
+
emit('version.install', { agent, version, error: err.message });
|
|
1000
1015
|
return { success: false, installedVersion: version, error: err.message };
|
|
1001
1016
|
}
|
|
1002
1017
|
}
|
|
@@ -1086,6 +1101,7 @@ export function removeVersion(agent, version) {
|
|
|
1086
1101
|
// Ignore if already gone
|
|
1087
1102
|
}
|
|
1088
1103
|
}
|
|
1104
|
+
emit('version.remove', { agent, version });
|
|
1089
1105
|
return true;
|
|
1090
1106
|
}
|
|
1091
1107
|
/**
|
package/package.json
CHANGED
package/scripts/postinstall.js
CHANGED
|
@@ -13,8 +13,15 @@ const SHIMS_DIR = path.join(HOME, '.agents-system', 'shims');
|
|
|
13
13
|
const SYSTEM_DIR = path.join(HOME, '.agents-system');
|
|
14
14
|
const USER_DIR = path.join(HOME, '.agents');
|
|
15
15
|
|
|
16
|
-
//
|
|
17
|
-
|
|
16
|
+
// For local installs, create directories and show a message
|
|
17
|
+
const isGlobalInstall = process.env.npm_config_global || process.argv.includes('-g');
|
|
18
|
+
if (!isGlobalInstall) {
|
|
19
|
+
// Still create user directories for local installs
|
|
20
|
+
fs.mkdirSync(USER_DIR, { recursive: true, mode: 0o700 });
|
|
21
|
+
console.log(`
|
|
22
|
+
agents-cli installed locally.
|
|
23
|
+
To complete setup, run: npx agents init
|
|
24
|
+
`);
|
|
18
25
|
process.exit(0);
|
|
19
26
|
}
|
|
20
27
|
|
|
@@ -165,17 +172,38 @@ async function main() {
|
|
|
165
172
|
return;
|
|
166
173
|
}
|
|
167
174
|
|
|
168
|
-
// Default:
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
175
|
+
// Default: offer to auto-add shims to PATH (like homebrew does)
|
|
176
|
+
const rcFile = getShellRc();
|
|
177
|
+
let alreadyConfigured = false;
|
|
178
|
+
if (fs.existsSync(rcFile)) {
|
|
179
|
+
const content = fs.readFileSync(rcFile, 'utf-8');
|
|
180
|
+
alreadyConfigured = content.includes('.agents-system/shims');
|
|
181
|
+
}
|
|
172
182
|
|
|
173
|
-
|
|
183
|
+
console.log(`\nagents-cli installed.`);
|
|
174
184
|
|
|
175
|
-
(
|
|
185
|
+
if (!alreadyConfigured && process.stdin.isTTY && process.stdout.isTTY) {
|
|
186
|
+
const answer = await ask(`\nAdd shims to PATH in ~/${path.basename(rcFile)}? [Y/n] `);
|
|
187
|
+
if (answer === '' || answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes') {
|
|
188
|
+
const addition = `\n# agents-cli: version switching for AI coding agents\n${exportLine}\n`;
|
|
189
|
+
fs.mkdirSync(path.dirname(rcFile), { recursive: true });
|
|
190
|
+
fs.appendFileSync(rcFile, addition);
|
|
191
|
+
console.log(`\n Added ${SHIMS_DIR} to PATH in ${path.basename(rcFile)}`);
|
|
192
|
+
console.log(` Restart your shell or run: source ~/${path.basename(rcFile)}\n`);
|
|
193
|
+
} else {
|
|
194
|
+
console.log(`
|
|
195
|
+
To enable version-aware shims, add this to your shell config:
|
|
176
196
|
|
|
177
|
-
|
|
197
|
+
${exportLine}
|
|
178
198
|
`);
|
|
199
|
+
}
|
|
200
|
+
} else if (!alreadyConfigured) {
|
|
201
|
+
console.log(`
|
|
202
|
+
To enable version-aware shims, add this to your shell config:
|
|
203
|
+
|
|
204
|
+
${exportLine}
|
|
205
|
+
`);
|
|
206
|
+
}
|
|
179
207
|
|
|
180
208
|
const choice = await promptForAliases();
|
|
181
209
|
if (choice === 'install') {
|