@phnx-labs/agents-cli 1.19.2 → 1.20.3
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 +140 -0
- package/README.md +72 -12
- package/dist/browser.js +0 -0
- package/dist/commands/browser.js +88 -16
- package/dist/commands/cli.d.ts +14 -0
- package/dist/commands/cli.js +244 -0
- package/dist/commands/cloud.js +1 -1
- package/dist/commands/commands.js +27 -10
- package/dist/commands/computer.js +18 -1
- package/dist/commands/doctor.d.ts +1 -1
- package/dist/commands/doctor.js +2 -2
- package/dist/commands/exec.js +38 -18
- package/dist/commands/factory.d.ts +3 -14
- package/dist/commands/factory.js +3 -3
- package/dist/commands/feedback.d.ts +7 -0
- package/dist/commands/feedback.js +89 -0
- package/dist/commands/helper.d.ts +12 -0
- package/dist/commands/helper.js +87 -0
- package/dist/commands/hooks.js +89 -10
- package/dist/commands/mcp.js +166 -10
- package/dist/commands/packages.js +196 -27
- package/dist/commands/permissions.js +21 -6
- package/dist/commands/plugins.js +11 -4
- package/dist/commands/profiles.d.ts +8 -0
- package/dist/commands/profiles.js +118 -5
- package/dist/commands/prune.js +39 -160
- package/dist/commands/pull.js +58 -5
- package/dist/commands/routines.js +107 -14
- package/dist/commands/rules.js +8 -4
- package/dist/commands/secrets-migrate.d.ts +24 -0
- package/dist/commands/secrets-migrate.js +198 -0
- package/dist/commands/secrets-sync.d.ts +11 -0
- package/dist/commands/secrets-sync.js +155 -0
- package/dist/commands/secrets.js +79 -46
- package/dist/commands/sessions.d.ts +28 -0
- package/dist/commands/sessions.js +98 -33
- package/dist/commands/setup.d.ts +1 -0
- package/dist/commands/setup.js +37 -28
- package/dist/commands/skills.js +25 -8
- package/dist/commands/subagents.js +69 -49
- package/dist/commands/teams.js +61 -10
- package/dist/commands/utils.d.ts +33 -0
- package/dist/commands/utils.js +139 -0
- package/dist/commands/versions.d.ts +4 -3
- package/dist/commands/versions.js +134 -130
- package/dist/commands/view.d.ts +6 -0
- package/dist/commands/view.js +175 -19
- package/dist/commands/workflows.js +29 -6
- package/dist/computer.js +0 -0
- package/dist/index.js +38 -6
- package/dist/lib/acp/client.js +6 -1
- package/dist/lib/acp/harnesses.js +8 -0
- package/dist/lib/agents.d.ts +4 -0
- package/dist/lib/agents.js +125 -34
- package/dist/lib/auto-pull-worker.js +18 -1
- package/dist/lib/browser/cdp.d.ts +8 -1
- package/dist/lib/browser/cdp.js +40 -3
- package/dist/lib/browser/chrome.d.ts +13 -0
- package/dist/lib/browser/chrome.js +46 -3
- package/dist/lib/browser/domain-skills.d.ts +51 -0
- package/dist/lib/browser/domain-skills.js +157 -0
- package/dist/lib/browser/drivers/local.js +45 -4
- package/dist/lib/browser/drivers/ssh.js +2 -2
- package/dist/lib/browser/ipc.d.ts +8 -1
- package/dist/lib/browser/ipc.js +37 -28
- package/dist/lib/browser/profiles.d.ts +16 -3
- package/dist/lib/browser/profiles.js +44 -4
- package/dist/lib/browser/service.d.ts +3 -0
- package/dist/lib/browser/service.js +40 -5
- package/dist/lib/browser/types.d.ts +11 -4
- package/dist/lib/cli-resources.d.ts +137 -0
- package/dist/lib/cli-resources.js +477 -0
- package/dist/lib/cloud/factory.d.ts +1 -1
- package/dist/lib/cloud/factory.js +1 -1
- package/dist/lib/cloud/rush.js +5 -5
- package/dist/lib/command-skills.js +0 -2
- package/dist/lib/computer-rpc.d.ts +3 -0
- package/dist/lib/computer-rpc.js +53 -0
- package/dist/lib/daemon.js +20 -0
- package/dist/lib/events.d.ts +16 -2
- package/dist/lib/events.js +33 -2
- package/dist/lib/exec.d.ts +42 -13
- package/dist/lib/exec.js +127 -33
- package/dist/lib/help.js +11 -5
- package/dist/lib/hooks/cache.d.ts +38 -0
- package/dist/lib/hooks/cache.js +242 -0
- package/dist/lib/hooks/profile.d.ts +33 -0
- package/dist/lib/hooks/profile.js +129 -0
- package/dist/lib/hooks.d.ts +0 -10
- package/dist/lib/hooks.js +246 -11
- package/dist/lib/mcp.d.ts +15 -0
- package/dist/lib/mcp.js +46 -0
- package/dist/lib/migrate.js +1 -1
- package/dist/lib/overdue.d.ts +26 -0
- package/dist/lib/overdue.js +101 -0
- package/dist/lib/permissions.d.ts +13 -0
- package/dist/lib/permissions.js +55 -1
- package/dist/lib/plugin-marketplace.js +1 -1
- package/dist/lib/plugins.js +15 -1
- package/dist/lib/profiles-presets.d.ts +26 -0
- package/dist/lib/profiles-presets.js +216 -0
- package/dist/lib/profiles.d.ts +34 -0
- package/dist/lib/profiles.js +112 -1
- package/dist/lib/resources/mcp.js +37 -0
- package/dist/lib/resources.d.ts +1 -1
- package/dist/lib/rotate.js +10 -4
- package/dist/lib/routines-format.d.ts +47 -0
- package/dist/lib/routines-format.js +194 -0
- package/dist/lib/routines.d.ts +8 -2
- package/dist/lib/routines.js +34 -14
- package/dist/lib/runner.js +83 -15
- package/dist/lib/scheduler.js +8 -1
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/_CodeSignature/CodeResources +1 -9
- package/dist/lib/secrets/bundles.d.ts +34 -17
- package/dist/lib/secrets/bundles.js +210 -36
- package/dist/lib/secrets/index.d.ts +49 -30
- package/dist/lib/secrets/index.js +126 -115
- package/dist/lib/secrets/install-helper.d.ts +45 -0
- package/dist/lib/secrets/install-helper.js +165 -0
- package/dist/lib/secrets/linux.js +4 -4
- package/dist/lib/secrets/sync.d.ts +56 -0
- package/dist/lib/secrets/sync.js +180 -0
- package/dist/lib/session/active.d.ts +8 -0
- package/dist/lib/session/active.js +3 -2
- package/dist/lib/session/db.d.ts +0 -4
- package/dist/lib/session/db.js +0 -26
- package/dist/lib/session/parse.d.ts +1 -0
- package/dist/lib/session/parse.js +44 -0
- package/dist/lib/session/render.js +4 -4
- package/dist/lib/session/types.d.ts +2 -2
- package/dist/lib/session/types.js +1 -1
- package/dist/lib/shims.d.ts +5 -2
- package/dist/lib/shims.js +70 -38
- package/dist/lib/state.d.ts +14 -2
- package/dist/lib/state.js +51 -20
- package/dist/lib/teams/agents.d.ts +5 -4
- package/dist/lib/teams/agents.js +48 -22
- package/dist/lib/teams/api.d.ts +2 -1
- package/dist/lib/teams/api.js +4 -3
- package/dist/lib/teams/parsers.d.ts +1 -1
- package/dist/lib/teams/parsers.js +153 -3
- package/dist/lib/teams/summarizer.js +18 -2
- package/dist/lib/teams/worktree.js +14 -3
- package/dist/lib/types.d.ts +63 -4
- package/dist/lib/types.js +8 -3
- package/dist/lib/usage.d.ts +27 -2
- package/dist/lib/usage.js +100 -17
- package/dist/lib/versions.d.ts +45 -3
- package/dist/lib/versions.js +455 -60
- package/package.json +15 -14
- package/scripts/install-helper.js +97 -0
- package/scripts/postinstall.js +16 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/embedded.provisionprofile +0 -0
- package/npm-shrinkwrap.json +0 -3162
package/dist/lib/plugins.js
CHANGED
|
@@ -28,6 +28,20 @@ export const PLUGIN_EXEC_SURFACE_LABELS = {
|
|
|
28
28
|
hasSettings: 'settings.json',
|
|
29
29
|
hasPermissions: 'permissions/',
|
|
30
30
|
};
|
|
31
|
+
function isPluginRootEntry(pluginsDir, entry) {
|
|
32
|
+
if (entry.name.startsWith('.'))
|
|
33
|
+
return false;
|
|
34
|
+
if (entry.isDirectory())
|
|
35
|
+
return true;
|
|
36
|
+
if (!entry.isSymbolicLink())
|
|
37
|
+
return false;
|
|
38
|
+
try {
|
|
39
|
+
return fs.statSync(path.join(pluginsDir, entry.name)).isDirectory();
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
31
45
|
/**
|
|
32
46
|
* Discover all plugins in ~/.agents/plugins/.
|
|
33
47
|
* A valid plugin has a .claude-plugin/plugin.json manifest.
|
|
@@ -40,7 +54,7 @@ export function discoverPlugins() {
|
|
|
40
54
|
const plugins = [];
|
|
41
55
|
const entries = fs.readdirSync(pluginsDir, { withFileTypes: true });
|
|
42
56
|
for (const entry of entries) {
|
|
43
|
-
if (!
|
|
57
|
+
if (!isPluginRootEntry(pluginsDir, entry))
|
|
44
58
|
continue;
|
|
45
59
|
const pluginRoot = path.join(pluginsDir, entry.name);
|
|
46
60
|
const manifest = loadPluginManifest(pluginRoot);
|
|
@@ -5,6 +5,20 @@
|
|
|
5
5
|
* name so users can `agents profiles add kimi` without manual configuration.
|
|
6
6
|
*/
|
|
7
7
|
import type { AgentId } from './types.js';
|
|
8
|
+
export interface PresetVar {
|
|
9
|
+
/** Env var name to set in the resulting profile. */
|
|
10
|
+
envVar: string;
|
|
11
|
+
/** User-facing prompt text. */
|
|
12
|
+
prompt: string;
|
|
13
|
+
/** True for secrets — wizard will mask input and store in keychain. */
|
|
14
|
+
secret?: boolean;
|
|
15
|
+
/** Default value (shown in prompt). */
|
|
16
|
+
default?: string;
|
|
17
|
+
/** Optional regex pattern — input validated against it. */
|
|
18
|
+
pattern?: string;
|
|
19
|
+
/** Optional hint text shown beside the prompt. */
|
|
20
|
+
hint?: string;
|
|
21
|
+
}
|
|
8
22
|
/** A pre-configured profile template for a model provider. */
|
|
9
23
|
export interface Preset {
|
|
10
24
|
name: string;
|
|
@@ -12,10 +26,22 @@ export interface Preset {
|
|
|
12
26
|
provider: string;
|
|
13
27
|
host: AgentId;
|
|
14
28
|
env: Record<string, string>;
|
|
29
|
+
vars?: PresetVar[];
|
|
15
30
|
authEnvVar: string;
|
|
31
|
+
/** True if the provider can function without a keychain token (e.g. Bedrock with SSO). */
|
|
32
|
+
authOptional?: boolean;
|
|
16
33
|
signupUrl?: string;
|
|
34
|
+
docPath?: string;
|
|
17
35
|
}
|
|
18
36
|
export declare const PRESETS: Preset[];
|
|
37
|
+
export interface ResolvedPresetEnv {
|
|
38
|
+
/** Env vars from preset.env — always set, no user input. */
|
|
39
|
+
static: Record<string, string>;
|
|
40
|
+
/** Vars the wizard needs to prompt for. */
|
|
41
|
+
prompts: PresetVar[];
|
|
42
|
+
}
|
|
43
|
+
/** Split a preset into static env vars and prompts needed from the user. */
|
|
44
|
+
export declare function expandPreset(p: Preset): ResolvedPresetEnv;
|
|
19
45
|
/** Look up a preset by name (case-sensitive). */
|
|
20
46
|
export declare function getPreset(name: string): Preset | undefined;
|
|
21
47
|
/** Return a copy of all available presets. */
|
|
@@ -88,7 +88,223 @@ export const PRESETS = [
|
|
|
88
88
|
ANTHROPIC_SMALL_FAST_MODEL: 'deepseek/deepseek-chat-v3-0324',
|
|
89
89
|
},
|
|
90
90
|
},
|
|
91
|
+
// ----- xAI Grok Build CLI (native host) -----
|
|
92
|
+
{
|
|
93
|
+
name: 'grok-fast',
|
|
94
|
+
description: 'xAI Grok Build CLI — fast tier. Optimized for speed and low-latency coding tasks.',
|
|
95
|
+
provider: 'xai',
|
|
96
|
+
host: 'grok',
|
|
97
|
+
authEnvVar: 'XAI_API_KEY',
|
|
98
|
+
signupUrl: 'https://console.x.ai',
|
|
99
|
+
env: {
|
|
100
|
+
GROK_MODEL: 'grok-build-0.1',
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
name: 'grok-heavy',
|
|
105
|
+
description: 'xAI Grok Build CLI — flagship tier (Grok 4.3). Best for complex reasoning and large context windows.',
|
|
106
|
+
provider: 'xai',
|
|
107
|
+
host: 'grok',
|
|
108
|
+
authEnvVar: 'XAI_API_KEY',
|
|
109
|
+
signupUrl: 'https://console.x.ai',
|
|
110
|
+
env: {
|
|
111
|
+
GROK_MODEL: 'grok-4.3',
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
// ----- Google Antigravity CLI (native host) -----
|
|
115
|
+
{
|
|
116
|
+
name: 'agy',
|
|
117
|
+
description: 'Google Antigravity CLI default (gemini-3.5-flash). Optimized for speed and large context.',
|
|
118
|
+
provider: 'google',
|
|
119
|
+
host: 'antigravity',
|
|
120
|
+
authEnvVar: 'ANTIGRAVITY_API_KEY',
|
|
121
|
+
signupUrl: 'https://antigravity.google',
|
|
122
|
+
env: {
|
|
123
|
+
// Antigravity defaults to gemini-3.5-flash as of June 2026
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
// ----- Direct Providers -----
|
|
127
|
+
{
|
|
128
|
+
name: 'anthropic',
|
|
129
|
+
description: 'Anthropic direct API — standard Claude Code experience with your own API key.',
|
|
130
|
+
provider: 'anthropic',
|
|
131
|
+
host: 'claude',
|
|
132
|
+
authEnvVar: 'ANTHROPIC_API_KEY',
|
|
133
|
+
signupUrl: 'https://console.anthropic.com',
|
|
134
|
+
env: {
|
|
135
|
+
ANTHROPIC_MODEL: 'claude-3-5-sonnet-latest',
|
|
136
|
+
ANTHROPIC_SMALL_FAST_MODEL: 'claude-3-5-haiku-latest',
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
// ----- Gateway / enterprise / self-hosted -----
|
|
140
|
+
{
|
|
141
|
+
name: 'proxy',
|
|
142
|
+
description: 'Generic local proxy / gateway — points at a local router (CCR, LiteLLM) or internal corporate inference endpoint.',
|
|
143
|
+
provider: 'proxy',
|
|
144
|
+
host: 'claude',
|
|
145
|
+
authEnvVar: 'ANTHROPIC_AUTH_TOKEN',
|
|
146
|
+
authOptional: true,
|
|
147
|
+
env: {
|
|
148
|
+
API_TIMEOUT_MS: '600000',
|
|
149
|
+
},
|
|
150
|
+
vars: [
|
|
151
|
+
{
|
|
152
|
+
envVar: 'ANTHROPIC_BASE_URL',
|
|
153
|
+
prompt: 'Gateway base URL',
|
|
154
|
+
default: 'http://127.0.0.1:3456',
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
envVar: 'ANTHROPIC_MODEL',
|
|
158
|
+
prompt: 'Model ID',
|
|
159
|
+
default: 'claude-3-5-sonnet-latest',
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
name: 'truefoundry',
|
|
165
|
+
description: 'TrueFoundry AI Gateway routing to Anthropic-compatible backends (often Bedrock). Strips experimental headers + disables prompt caching to satisfy Bedrock validation.',
|
|
166
|
+
provider: 'truefoundry',
|
|
167
|
+
host: 'claude',
|
|
168
|
+
authEnvVar: 'ANTHROPIC_AUTH_TOKEN',
|
|
169
|
+
signupUrl: 'https://www.truefoundry.com',
|
|
170
|
+
docPath: 'truefoundry',
|
|
171
|
+
env: {
|
|
172
|
+
CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS: '1',
|
|
173
|
+
CLAUDE_CODE_ATTRIBUTION_HEADER: '0',
|
|
174
|
+
DISABLE_PROMPT_CACHING: '1',
|
|
175
|
+
API_TIMEOUT_MS: '600000',
|
|
176
|
+
CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY: '1',
|
|
177
|
+
},
|
|
178
|
+
vars: [
|
|
179
|
+
{
|
|
180
|
+
envVar: 'ANTHROPIC_BASE_URL',
|
|
181
|
+
prompt: 'TrueFoundry gateway base URL',
|
|
182
|
+
hint: 'e.g. https://<tenant>.truefoundry.cloud/api/llm',
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
envVar: 'ANTHROPIC_MODEL',
|
|
186
|
+
prompt: 'Model ID',
|
|
187
|
+
hint: 'provider-account/model-id',
|
|
188
|
+
},
|
|
189
|
+
],
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
name: 'bedrock',
|
|
193
|
+
description: 'AWS Bedrock — Claude Code native Bedrock mode. Uses the standard AWS SDK credential chain (SSO, IAM roles, env). Set AWS_BEARER_TOKEN_BEDROCK only if your gateway requires a static token.',
|
|
194
|
+
provider: 'bedrock',
|
|
195
|
+
host: 'claude',
|
|
196
|
+
authEnvVar: 'AWS_BEARER_TOKEN_BEDROCK',
|
|
197
|
+
authOptional: true,
|
|
198
|
+
signupUrl: 'https://aws.amazon.com/bedrock/',
|
|
199
|
+
env: {
|
|
200
|
+
CLAUDE_CODE_USE_BEDROCK: '1',
|
|
201
|
+
DISABLE_PROMPT_CACHING: '1',
|
|
202
|
+
},
|
|
203
|
+
vars: [
|
|
204
|
+
{
|
|
205
|
+
envVar: 'AWS_REGION',
|
|
206
|
+
prompt: 'AWS region',
|
|
207
|
+
default: 'us-east-1',
|
|
208
|
+
},
|
|
209
|
+
],
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
name: 'vertex',
|
|
213
|
+
description: 'Google Vertex AI — Claude Code native Vertex mode.',
|
|
214
|
+
provider: 'vertex',
|
|
215
|
+
host: 'claude',
|
|
216
|
+
authEnvVar: 'GOOGLE_APPLICATION_CREDENTIALS',
|
|
217
|
+
signupUrl: 'https://cloud.google.com/vertex-ai',
|
|
218
|
+
env: {
|
|
219
|
+
CLAUDE_CODE_USE_VERTEX: '1',
|
|
220
|
+
},
|
|
221
|
+
vars: [
|
|
222
|
+
{
|
|
223
|
+
envVar: 'CLOUD_ML_REGION',
|
|
224
|
+
prompt: 'Vertex region',
|
|
225
|
+
default: 'us-east5',
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
envVar: 'ANTHROPIC_VERTEX_PROJECT_ID',
|
|
229
|
+
prompt: 'GCP project ID',
|
|
230
|
+
},
|
|
231
|
+
],
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
name: 'foundry',
|
|
235
|
+
description: 'Microsoft Azure AI Foundry — Anthropic models hosted on Azure. Distinct from TrueFoundry.',
|
|
236
|
+
provider: 'foundry',
|
|
237
|
+
host: 'claude',
|
|
238
|
+
authEnvVar: 'ANTHROPIC_FOUNDRY_API_KEY',
|
|
239
|
+
signupUrl: 'https://ai.azure.com',
|
|
240
|
+
env: {
|
|
241
|
+
CLAUDE_CODE_USE_FOUNDRY: '1',
|
|
242
|
+
},
|
|
243
|
+
vars: [
|
|
244
|
+
{
|
|
245
|
+
envVar: 'ANTHROPIC_FOUNDRY_BASE_URL',
|
|
246
|
+
prompt: 'Azure AI Foundry base URL',
|
|
247
|
+
hint: '<resource>.services.ai.azure.com/anthropic',
|
|
248
|
+
},
|
|
249
|
+
],
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
name: 'litellm',
|
|
253
|
+
description: 'LiteLLM proxy in Anthropic-compatible mode.',
|
|
254
|
+
provider: 'litellm',
|
|
255
|
+
host: 'claude',
|
|
256
|
+
authEnvVar: 'ANTHROPIC_AUTH_TOKEN',
|
|
257
|
+
env: {
|
|
258
|
+
API_TIMEOUT_MS: '600000',
|
|
259
|
+
},
|
|
260
|
+
vars: [
|
|
261
|
+
{ envVar: 'ANTHROPIC_BASE_URL', prompt: 'LiteLLM base URL' },
|
|
262
|
+
{ envVar: 'ANTHROPIC_MODEL', prompt: 'Model ID' },
|
|
263
|
+
],
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
name: 'vllm',
|
|
267
|
+
description: 'Self-hosted vLLM with native Anthropic-compatible endpoint.',
|
|
268
|
+
provider: 'vllm',
|
|
269
|
+
host: 'claude',
|
|
270
|
+
authEnvVar: 'ANTHROPIC_AUTH_TOKEN',
|
|
271
|
+
env: {
|
|
272
|
+
API_TIMEOUT_MS: '600000',
|
|
273
|
+
},
|
|
274
|
+
vars: [
|
|
275
|
+
{
|
|
276
|
+
envVar: 'ANTHROPIC_BASE_URL',
|
|
277
|
+
prompt: 'vLLM base URL',
|
|
278
|
+
default: 'http://127.0.0.1:8000',
|
|
279
|
+
},
|
|
280
|
+
{ envVar: 'ANTHROPIC_MODEL', prompt: 'Model ID' },
|
|
281
|
+
],
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
name: 'ollama',
|
|
285
|
+
description: 'Local Ollama via Codex CLI (OpenAI-compatible). Codex host because Anthropic translation through CCR/LiteLLM drops tool_use.',
|
|
286
|
+
provider: 'ollama',
|
|
287
|
+
host: 'codex',
|
|
288
|
+
authEnvVar: 'OPENAI_API_KEY',
|
|
289
|
+
env: {},
|
|
290
|
+
vars: [
|
|
291
|
+
{
|
|
292
|
+
envVar: 'OPENAI_BASE_URL',
|
|
293
|
+
prompt: 'Ollama base URL',
|
|
294
|
+
default: 'http://127.0.0.1:11434/v1',
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
envVar: 'OPENAI_MODEL',
|
|
298
|
+
prompt: 'Model ID',
|
|
299
|
+
default: 'qwen3-coder:30b',
|
|
300
|
+
},
|
|
301
|
+
],
|
|
302
|
+
},
|
|
91
303
|
];
|
|
304
|
+
/** Split a preset into static env vars and prompts needed from the user. */
|
|
305
|
+
export function expandPreset(p) {
|
|
306
|
+
return { static: { ...p.env }, prompts: p.vars ?? [] };
|
|
307
|
+
}
|
|
92
308
|
/** Look up a preset by name (case-sensitive). */
|
|
93
309
|
export function getPreset(name) {
|
|
94
310
|
return PRESETS.find((p) => p.name === name);
|
package/dist/lib/profiles.d.ts
CHANGED
|
@@ -23,8 +23,24 @@ export interface Profile {
|
|
|
23
23
|
preset?: string;
|
|
24
24
|
provider?: string;
|
|
25
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Stable, machine-readable summary used by `agents view` and `--json`.
|
|
28
|
+
* `agent` is the underlying harness (claude/codex/...) so consumers can
|
|
29
|
+
* group profiles under installed agents without reparsing host strings.
|
|
30
|
+
*/
|
|
31
|
+
export interface ProfileSummary {
|
|
32
|
+
name: string;
|
|
33
|
+
agent: AgentId;
|
|
34
|
+
host: string;
|
|
35
|
+
provider: string;
|
|
36
|
+
model: string;
|
|
37
|
+
auth: string;
|
|
38
|
+
path: string;
|
|
39
|
+
}
|
|
26
40
|
/** Get the directory where profile YAML files are stored. */
|
|
27
41
|
export declare function getProfilesDir(): string;
|
|
42
|
+
/** Return the on-disk YAML path for a profile name. */
|
|
43
|
+
export declare function getProfilePath(name: string): string;
|
|
28
44
|
/** Validate a profile name against the allowed pattern. Throws on invalid input. */
|
|
29
45
|
export declare function validateProfileName(name: string): void;
|
|
30
46
|
/** Check whether a profile YAML file exists on disk. */
|
|
@@ -37,6 +53,24 @@ export declare function writeProfile(profile: Profile): void;
|
|
|
37
53
|
export declare function deleteProfile(name: string): boolean;
|
|
38
54
|
/** List all valid profiles, sorted by name. Malformed files are silently skipped. */
|
|
39
55
|
export declare function listProfiles(): Profile[];
|
|
56
|
+
/** Format the host harness and optional pinned version for display. */
|
|
57
|
+
export declare function profileHostLabel(profile: Profile): string;
|
|
58
|
+
/** Return the configured provider name, deriving it from the shared keychain item when needed. */
|
|
59
|
+
export declare function profileProviderLabel(profile: Profile): string;
|
|
60
|
+
/** Return the configured model env value for display. */
|
|
61
|
+
export declare function profileModelLabel(profile: Profile): string;
|
|
62
|
+
/**
|
|
63
|
+
* Build a non-secret auth identity/status label for list surfaces.
|
|
64
|
+
*
|
|
65
|
+
* - Inline JWT in env: decode locally and show email / preferred_username / sub.
|
|
66
|
+
* - Inline opaque token in env: masked prefix/suffix (user explicitly stored it
|
|
67
|
+
* in the YAML, so they accept the leak in their own output).
|
|
68
|
+
* - Keychain-backed auth: provider + "stored" or "missing" (non-prompting).
|
|
69
|
+
* - No auth at all: provider only.
|
|
70
|
+
*/
|
|
71
|
+
export declare function profileAuthLabel(profile: Profile): string;
|
|
72
|
+
/** Build a stable, machine-readable summary for list and view surfaces. */
|
|
73
|
+
export declare function profileSummary(profile: Profile): ProfileSummary;
|
|
40
74
|
/**
|
|
41
75
|
* Build a profile from a preset. The keychain item is shared across all
|
|
42
76
|
* profiles that point at the same provider, so adding kimi + deepseek prompts
|
package/dist/lib/profiles.js
CHANGED
|
@@ -9,7 +9,7 @@ import * as fs from 'fs';
|
|
|
9
9
|
import * as path from 'path';
|
|
10
10
|
import * as yaml from 'yaml';
|
|
11
11
|
import { getUserAgentsDir } from './state.js';
|
|
12
|
-
import { getKeychainToken, keychainItemName } from './secrets/profiles.js';
|
|
12
|
+
import { getKeychainToken, hasKeychainToken, keychainItemName } from './secrets/profiles.js';
|
|
13
13
|
import { getPreset } from './profiles-presets.js';
|
|
14
14
|
const PROFILE_NAME_PATTERN = /^[a-z0-9][a-z0-9-_]{0,48}$/i;
|
|
15
15
|
/** Get the directory where profile YAML files are stored. */
|
|
@@ -19,6 +19,11 @@ export function getProfilesDir() {
|
|
|
19
19
|
function profilePath(name) {
|
|
20
20
|
return path.join(getProfilesDir(), `${name}.yml`);
|
|
21
21
|
}
|
|
22
|
+
/** Return the on-disk YAML path for a profile name. */
|
|
23
|
+
export function getProfilePath(name) {
|
|
24
|
+
validateProfileName(name);
|
|
25
|
+
return profilePath(name);
|
|
26
|
+
}
|
|
22
27
|
/** Validate a profile name against the allowed pattern. Throws on invalid input. */
|
|
23
28
|
export function validateProfileName(name) {
|
|
24
29
|
if (!PROFILE_NAME_PATTERN.test(name)) {
|
|
@@ -89,6 +94,112 @@ export function listProfiles() {
|
|
|
89
94
|
}
|
|
90
95
|
return profiles.sort((a, b) => a.name.localeCompare(b.name));
|
|
91
96
|
}
|
|
97
|
+
/** Format the host harness and optional pinned version for display. */
|
|
98
|
+
export function profileHostLabel(profile) {
|
|
99
|
+
return profile.host.version ? `${profile.host.agent}@${profile.host.version}` : profile.host.agent;
|
|
100
|
+
}
|
|
101
|
+
/** Return the configured provider name, deriving it from the shared keychain item when needed. */
|
|
102
|
+
export function profileProviderLabel(profile) {
|
|
103
|
+
return profile.provider || profile.auth?.keychainItem?.split('.')[1] || '-';
|
|
104
|
+
}
|
|
105
|
+
const MODEL_ENV_KEYS = [
|
|
106
|
+
'ANTHROPIC_MODEL',
|
|
107
|
+
'ANTHROPIC_SMALL_FAST_MODEL',
|
|
108
|
+
'OPENAI_MODEL',
|
|
109
|
+
'GEMINI_MODEL',
|
|
110
|
+
'GROK_MODEL',
|
|
111
|
+
];
|
|
112
|
+
/** Return the configured model env value for display. */
|
|
113
|
+
export function profileModelLabel(profile) {
|
|
114
|
+
for (const key of MODEL_ENV_KEYS) {
|
|
115
|
+
const value = profile.env[key];
|
|
116
|
+
if (value)
|
|
117
|
+
return value;
|
|
118
|
+
}
|
|
119
|
+
for (const [key, value] of Object.entries(profile.env)) {
|
|
120
|
+
if ((key === 'MODEL' || key.endsWith('_MODEL')) && value)
|
|
121
|
+
return value;
|
|
122
|
+
}
|
|
123
|
+
return '-';
|
|
124
|
+
}
|
|
125
|
+
function decodeJwtPayload(token) {
|
|
126
|
+
const parts = token.split('.');
|
|
127
|
+
if (parts.length < 2)
|
|
128
|
+
return null;
|
|
129
|
+
try {
|
|
130
|
+
const normalized = parts[1].replace(/-/g, '+').replace(/_/g, '/');
|
|
131
|
+
const padded = normalized.padEnd(Math.ceil(normalized.length / 4) * 4, '=');
|
|
132
|
+
const decoded = Buffer.from(padded, 'base64').toString('utf-8');
|
|
133
|
+
const parsed = JSON.parse(decoded);
|
|
134
|
+
return parsed && typeof parsed === 'object' ? parsed : null;
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
function maskToken(token) {
|
|
141
|
+
if (token.length <= 12)
|
|
142
|
+
return `${token.slice(0, 3)}...${token.slice(-2)}`;
|
|
143
|
+
return `${token.slice(0, 6)}...${token.slice(-4)}`;
|
|
144
|
+
}
|
|
145
|
+
const INLINE_AUTH_KEYS = [
|
|
146
|
+
'ANTHROPIC_AUTH_TOKEN',
|
|
147
|
+
'ANTHROPIC_API_KEY',
|
|
148
|
+
'OPENAI_API_KEY',
|
|
149
|
+
'GEMINI_API_KEY',
|
|
150
|
+
'GOOGLE_API_KEY',
|
|
151
|
+
'XAI_API_KEY',
|
|
152
|
+
];
|
|
153
|
+
function inlineAuthToken(profile) {
|
|
154
|
+
if (profile.auth?.envVar && profile.env[profile.auth.envVar]) {
|
|
155
|
+
return profile.env[profile.auth.envVar];
|
|
156
|
+
}
|
|
157
|
+
for (const key of INLINE_AUTH_KEYS) {
|
|
158
|
+
const value = profile.env[key];
|
|
159
|
+
if (value)
|
|
160
|
+
return value;
|
|
161
|
+
}
|
|
162
|
+
return undefined;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Build a non-secret auth identity/status label for list surfaces.
|
|
166
|
+
*
|
|
167
|
+
* - Inline JWT in env: decode locally and show email / preferred_username / sub.
|
|
168
|
+
* - Inline opaque token in env: masked prefix/suffix (user explicitly stored it
|
|
169
|
+
* in the YAML, so they accept the leak in their own output).
|
|
170
|
+
* - Keychain-backed auth: provider + "stored" or "missing" (non-prompting).
|
|
171
|
+
* - No auth at all: provider only.
|
|
172
|
+
*/
|
|
173
|
+
export function profileAuthLabel(profile) {
|
|
174
|
+
const provider = profileProviderLabel(profile);
|
|
175
|
+
const token = inlineAuthToken(profile);
|
|
176
|
+
if (token) {
|
|
177
|
+
const payload = decodeJwtPayload(token);
|
|
178
|
+
const identity = payload?.email ||
|
|
179
|
+
payload?.preferred_username ||
|
|
180
|
+
payload?.username ||
|
|
181
|
+
payload?.sub;
|
|
182
|
+
if (typeof identity === 'string')
|
|
183
|
+
return `${provider} ${identity}`;
|
|
184
|
+
return `${provider} ${maskToken(token)}`;
|
|
185
|
+
}
|
|
186
|
+
if (profile.auth) {
|
|
187
|
+
return `${provider} ${hasKeychainToken(profile.auth.keychainItem) ? 'stored' : 'missing'}`;
|
|
188
|
+
}
|
|
189
|
+
return provider;
|
|
190
|
+
}
|
|
191
|
+
/** Build a stable, machine-readable summary for list and view surfaces. */
|
|
192
|
+
export function profileSummary(profile) {
|
|
193
|
+
return {
|
|
194
|
+
name: profile.name,
|
|
195
|
+
agent: profile.host.agent,
|
|
196
|
+
host: profileHostLabel(profile),
|
|
197
|
+
provider: profileProviderLabel(profile),
|
|
198
|
+
model: profileModelLabel(profile),
|
|
199
|
+
auth: profileAuthLabel(profile),
|
|
200
|
+
path: getProfilePath(profile.name),
|
|
201
|
+
};
|
|
202
|
+
}
|
|
92
203
|
/**
|
|
93
204
|
* Build a profile from a preset. The keychain item is shared across all
|
|
94
205
|
* profiles that point at the same provider, so adding kimi + deepseek prompts
|
|
@@ -239,6 +239,39 @@ function syncToCodexConfig(configPath, items) {
|
|
|
239
239
|
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
240
240
|
fs.writeFileSync(configPath, TOML.stringify(config), 'utf-8');
|
|
241
241
|
}
|
|
242
|
+
/**
|
|
243
|
+
* Write MCP servers to Grok config.toml format ([mcp_servers] section).
|
|
244
|
+
*/
|
|
245
|
+
function syncToGrokConfig(configPath, items) {
|
|
246
|
+
let config = {};
|
|
247
|
+
if (fs.existsSync(configPath)) {
|
|
248
|
+
try {
|
|
249
|
+
config = TOML.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
250
|
+
}
|
|
251
|
+
catch {
|
|
252
|
+
config = {};
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
const mcpServers = {};
|
|
256
|
+
for (const item of items) {
|
|
257
|
+
if (item.transport === 'stdio') {
|
|
258
|
+
mcpServers[item.name] = {
|
|
259
|
+
command: item.command,
|
|
260
|
+
args: item.args || [],
|
|
261
|
+
...(item.env && { env: item.env }),
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
else if (item.transport === 'http' || item.transport === 'sse') {
|
|
265
|
+
mcpServers[item.name] = {
|
|
266
|
+
url: item.url,
|
|
267
|
+
...(item.headers && { headers: item.headers }),
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
config.mcp_servers = mcpServers;
|
|
272
|
+
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
273
|
+
fs.writeFileSync(configPath, TOML.stringify(config), 'utf-8');
|
|
274
|
+
}
|
|
242
275
|
/**
|
|
243
276
|
* Write MCP servers to OpenCode opencode.jsonc format.
|
|
244
277
|
*/
|
|
@@ -467,11 +500,15 @@ export const McpHandler = {
|
|
|
467
500
|
case 'openclaw':
|
|
468
501
|
syncToOpenClawConfig(configPath, mcpItems);
|
|
469
502
|
break;
|
|
503
|
+
case 'grok':
|
|
504
|
+
syncToGrokConfig(configPath, mcpItems);
|
|
505
|
+
break;
|
|
470
506
|
}
|
|
471
507
|
},
|
|
472
508
|
format(agent) {
|
|
473
509
|
switch (agent) {
|
|
474
510
|
case 'codex':
|
|
511
|
+
case 'grok':
|
|
475
512
|
return 'toml';
|
|
476
513
|
default:
|
|
477
514
|
return 'json';
|
package/dist/lib/resources.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import type { AgentId } from './types.js';
|
|
6
6
|
import { type SkillParseError } from './skills.js';
|
|
7
7
|
/** Resource kind — matches the subdirectory name under each repo root. */
|
|
8
|
-
export type ResourceKind = 'commands' | 'skills' | 'hooks' | 'rules' | 'mcp' | 'permissions' | 'subagents' | 'profiles' | 'secrets';
|
|
8
|
+
export type ResourceKind = 'commands' | 'skills' | 'hooks' | 'rules' | 'mcp' | 'cli' | 'permissions' | 'subagents' | 'profiles' | 'secrets';
|
|
9
9
|
/** A resource resolved with its origin. */
|
|
10
10
|
export interface ResolvedResource {
|
|
11
11
|
name: string;
|
package/dist/lib/rotate.js
CHANGED
|
@@ -10,7 +10,7 @@ import * as yaml from 'yaml';
|
|
|
10
10
|
import { getAccountInfo } from './agents.js';
|
|
11
11
|
import { readMeta, writeMeta, getHelpersDir, getUserAgentsDir } from './state.js';
|
|
12
12
|
import { listInstalledVersions, getVersionHomePath, resolveVersion } from './versions.js';
|
|
13
|
-
import { getUsageInfoByIdentity, getUsageLookupKey,
|
|
13
|
+
import { getUsageInfoByIdentity, getUsageLookupKey, } from './usage.js';
|
|
14
14
|
function getRotateDir() {
|
|
15
15
|
const dir = path.join(getHelpersDir(), 'rotate');
|
|
16
16
|
fs.mkdirSync(dir, { recursive: true });
|
|
@@ -233,9 +233,15 @@ async function collectRunCandidates(agent) {
|
|
|
233
233
|
const rows = await Promise.all(versions.map(async (version) => {
|
|
234
234
|
const home = getVersionHomePath(agent, version);
|
|
235
235
|
const info = await getAccountInfo(agent, home);
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
236
|
+
// `info.email` (from .claude.json's oauthAccount) is the auth heuristic.
|
|
237
|
+
// We used to additionally call isClaudeAuthValid(home), which reads
|
|
238
|
+
// "Claude Code-credentials-<hash>" from the system keychain. That item is
|
|
239
|
+
// written by Claude Code itself with its own process in the ACL, so our
|
|
240
|
+
// helper triggers a macOS keychain-authorization sheet on every probe —
|
|
241
|
+
// one per installed version, every time `agents run` cold-starts. If
|
|
242
|
+
// claude's stored token has actually expired, the spawned agent detects
|
|
243
|
+
// it at its own startup and re-auths; that's the correct UX.
|
|
244
|
+
const authValid = info.email != null;
|
|
239
245
|
return {
|
|
240
246
|
agent,
|
|
241
247
|
version,
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure display helpers for `agents routines list`.
|
|
3
|
+
*
|
|
4
|
+
* No external dependencies. All functions are pure (no I/O, no side effects).
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Convert a cron expression to a human-readable phrase.
|
|
8
|
+
*
|
|
9
|
+
* Handles the common patterns. For anything unrecognized, returns the raw
|
|
10
|
+
* expression so the user still sees something useful. NEVER throws.
|
|
11
|
+
*/
|
|
12
|
+
export declare function humanizeCron(expr: string, _tz?: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* Convert a next-run Date into a human phrase relative to `now`.
|
|
15
|
+
*
|
|
16
|
+
* - null → '-'
|
|
17
|
+
* - same calendar day → 'today 9:00 AM'
|
|
18
|
+
* - next calendar day → 'tomorrow 9:00 AM'
|
|
19
|
+
* - within 7 days → 'Mon 9:00 AM'
|
|
20
|
+
* - further out → 'Jun 15, 9:00 AM'
|
|
21
|
+
*/
|
|
22
|
+
export declare function humanizeNextRun(date: Date | null, now: Date, tz?: string): string;
|
|
23
|
+
/**
|
|
24
|
+
* Maximum display length for a repo cell. Display strings longer than this
|
|
25
|
+
* are truncated with an ellipsis so column alignment is preserved.
|
|
26
|
+
* Consumers that render the column should use this constant as the column width.
|
|
27
|
+
*/
|
|
28
|
+
export declare const REPO_DISPLAY_MAX = 24;
|
|
29
|
+
/**
|
|
30
|
+
* Parse a repo string into a display label and an optional hyperlink target.
|
|
31
|
+
*
|
|
32
|
+
* Rules:
|
|
33
|
+
* - null / undefined / empty / non-string → display '-', href null
|
|
34
|
+
* - 'owner/name' (one slash) → display 'owner/name', href 'https://github.com/owner/name/pulls'
|
|
35
|
+
* - 'https://...' or 'http://...' → display hostname+path, href the URL verbatim
|
|
36
|
+
* - anything else → display raw string, href null
|
|
37
|
+
*
|
|
38
|
+
* The display string is truncated to REPO_DISPLAY_MAX characters (with a
|
|
39
|
+
* trailing '…') when it would otherwise exceed the column width. The href
|
|
40
|
+
* is always the full untruncated URL so hyperlinks remain functional.
|
|
41
|
+
*
|
|
42
|
+
* NEVER throws — mirrors the contract of humanizeCron.
|
|
43
|
+
*/
|
|
44
|
+
export declare function formatRepoLink(repo: unknown): {
|
|
45
|
+
display: string;
|
|
46
|
+
href: string | null;
|
|
47
|
+
};
|