@phnx-labs/agents-cli 1.20.4 → 1.20.6
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 +19 -0
- package/README.md +49 -18
- package/dist/commands/browser.js +31 -4
- package/dist/commands/cli.js +1 -1
- package/dist/commands/cloud.js +1 -1
- package/dist/commands/commands.js +2 -0
- package/dist/commands/computer.js +10 -2
- package/dist/commands/defaults.d.ts +7 -0
- package/dist/commands/defaults.js +89 -0
- package/dist/commands/doctor.js +1 -1
- package/dist/commands/exec.js +73 -19
- package/dist/commands/hooks.js +6 -6
- package/dist/commands/inspect.d.ts +26 -0
- package/dist/commands/inspect.js +590 -0
- package/dist/commands/mcp.js +17 -16
- package/dist/commands/models.js +1 -1
- package/dist/commands/packages.js +6 -4
- package/dist/commands/permissions.js +13 -12
- package/dist/commands/plugins.d.ts +13 -0
- package/dist/commands/plugins.js +100 -11
- package/dist/commands/prune.js +3 -2
- package/dist/commands/pull.d.ts +12 -5
- package/dist/commands/pull.js +26 -422
- package/dist/commands/push.d.ts +14 -0
- package/dist/commands/push.js +30 -0
- package/dist/commands/repo.d.ts +1 -1
- package/dist/commands/repo.js +155 -112
- package/dist/commands/resource-view.d.ts +2 -0
- package/dist/commands/resource-view.js +12 -3
- package/dist/commands/routines.js +32 -7
- package/dist/commands/rules.js +4 -4
- package/dist/commands/secrets.js +46 -9
- package/dist/commands/sessions.js +1 -0
- package/dist/commands/setup.d.ts +3 -3
- package/dist/commands/setup.js +17 -17
- package/dist/commands/skills.js +6 -5
- package/dist/commands/subagents.js +5 -4
- package/dist/commands/sync.d.ts +18 -5
- package/dist/commands/sync.js +251 -65
- package/dist/commands/teams.js +109 -11
- package/dist/commands/tmux.d.ts +25 -0
- package/dist/commands/tmux.js +415 -0
- package/dist/commands/trash.d.ts +2 -2
- package/dist/commands/trash.js +1 -1
- package/dist/commands/versions.js +2 -2
- package/dist/commands/view.d.ts +12 -1
- package/dist/commands/view.js +128 -40
- package/dist/commands/workflows.js +4 -3
- package/dist/commands/worktree.d.ts +4 -5
- package/dist/commands/worktree.js +4 -4
- package/dist/index.js +106 -41
- package/dist/lib/agents.d.ts +23 -10
- package/dist/lib/agents.js +88 -25
- package/dist/lib/auto-pull-worker.d.ts +1 -1
- package/dist/lib/auto-pull-worker.js +2 -2
- package/dist/lib/auto-pull.d.ts +1 -1
- package/dist/lib/auto-pull.js +1 -1
- package/dist/lib/beta.d.ts +1 -1
- package/dist/lib/beta.js +1 -1
- package/dist/lib/browser/chrome.d.ts +10 -0
- package/dist/lib/browser/chrome.js +84 -3
- package/dist/lib/capabilities.js +2 -0
- package/dist/lib/commands.d.ts +28 -1
- package/dist/lib/commands.js +125 -20
- package/dist/lib/doctor-diff.js +2 -2
- package/dist/lib/exec.d.ts +14 -0
- package/dist/lib/exec.js +59 -5
- package/dist/lib/fuzzy.d.ts +12 -2
- package/dist/lib/fuzzy.js +29 -4
- package/dist/lib/git.js +8 -1
- package/dist/lib/hooks.d.ts +2 -2
- package/dist/lib/hooks.js +97 -10
- package/dist/lib/mcp.js +32 -2
- package/dist/lib/migrate.d.ts +51 -0
- package/dist/lib/migrate.js +233 -5
- package/dist/lib/models.js +62 -15
- package/dist/lib/permissions.d.ts +59 -2
- package/dist/lib/permissions.js +299 -7
- package/dist/lib/plugin-marketplace.d.ts +98 -40
- package/dist/lib/plugin-marketplace.js +196 -93
- package/dist/lib/plugins.d.ts +21 -4
- package/dist/lib/plugins.js +130 -49
- package/dist/lib/profiles-presets.js +12 -12
- package/dist/lib/project-launch.d.ts +70 -0
- package/dist/lib/project-launch.js +404 -0
- package/dist/lib/pty-client.js +1 -1
- package/dist/lib/pty-server.d.ts +1 -1
- package/dist/lib/pty-server.js +8 -5
- package/dist/lib/refresh.d.ts +26 -0
- package/dist/lib/refresh.js +315 -0
- package/dist/lib/resource-patterns.d.ts +1 -1
- package/dist/lib/resource-patterns.js +1 -1
- package/dist/lib/resources/commands.js +2 -2
- package/dist/lib/resources/hooks.d.ts +1 -1
- package/dist/lib/resources/hooks.js +1 -1
- package/dist/lib/resources/mcp.d.ts +1 -1
- package/dist/lib/resources/mcp.js +5 -6
- package/dist/lib/resources/permissions.js +5 -2
- package/dist/lib/resources/rules.js +3 -2
- package/dist/lib/resources/skills.js +3 -2
- package/dist/lib/resources/types.d.ts +1 -1
- package/dist/lib/resources.d.ts +2 -0
- package/dist/lib/resources.js +4 -3
- package/dist/lib/rotate.d.ts +1 -1
- package/dist/lib/rotate.js +7 -19
- package/dist/lib/routines.d.ts +16 -4
- package/dist/lib/routines.js +67 -17
- package/dist/lib/rules/compile.js +22 -10
- package/dist/lib/rules/rules.js +3 -3
- package/dist/lib/run-config.d.ts +9 -0
- package/dist/lib/run-config.js +35 -0
- package/dist/lib/run-defaults.d.ts +42 -0
- package/dist/lib/run-defaults.js +180 -0
- package/dist/lib/runner.js +16 -3
- package/dist/lib/scheduler.js +15 -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 +9 -1
- package/dist/lib/secrets/Agents CLI.app/Contents/embedded.provisionprofile +0 -0
- package/dist/lib/secrets/install-helper.d.ts +11 -3
- package/dist/lib/secrets/install-helper.js +48 -6
- package/dist/lib/secrets/linux.d.ts +56 -9
- package/dist/lib/secrets/linux.js +327 -59
- package/dist/lib/session/db.js +15 -2
- package/dist/lib/session/discover.js +118 -3
- package/dist/lib/session/parse.js +3 -0
- package/dist/lib/session/types.d.ts +1 -1
- package/dist/lib/session/types.js +1 -1
- package/dist/lib/shims.d.ts +18 -9
- package/dist/lib/shims.js +133 -50
- package/dist/lib/skills.d.ts +1 -1
- package/dist/lib/skills.js +10 -9
- package/dist/lib/staleness/detectors/commands.d.ts +3 -0
- package/dist/lib/staleness/detectors/commands.js +46 -0
- package/dist/lib/staleness/detectors/hooks.d.ts +3 -0
- package/dist/lib/staleness/detectors/hooks.js +44 -0
- package/dist/lib/staleness/detectors/mcp.d.ts +3 -0
- package/dist/lib/staleness/detectors/mcp.js +31 -0
- package/dist/lib/staleness/detectors/permissions.d.ts +3 -0
- package/dist/lib/staleness/detectors/permissions.js +201 -0
- package/dist/lib/staleness/detectors/plugins.d.ts +8 -0
- package/dist/lib/staleness/detectors/plugins.js +23 -0
- package/dist/lib/staleness/detectors/rules.d.ts +3 -0
- package/dist/lib/staleness/detectors/rules.js +34 -0
- package/dist/lib/staleness/detectors/skills.d.ts +3 -0
- package/dist/lib/staleness/detectors/skills.js +71 -0
- package/dist/lib/staleness/detectors/subagents.d.ts +3 -0
- package/dist/lib/staleness/detectors/subagents.js +50 -0
- package/dist/lib/staleness/detectors/types.d.ts +22 -0
- package/dist/lib/staleness/detectors/types.js +1 -0
- package/dist/lib/staleness/detectors/workflows.d.ts +3 -0
- package/dist/lib/staleness/detectors/workflows.js +28 -0
- package/dist/lib/staleness/registry.d.ts +26 -0
- package/dist/lib/staleness/registry.js +123 -0
- package/dist/lib/staleness/writers/commands.d.ts +3 -0
- package/dist/lib/staleness/writers/commands.js +111 -0
- package/dist/lib/staleness/writers/hooks.d.ts +3 -0
- package/dist/lib/staleness/writers/hooks.js +47 -0
- package/dist/lib/staleness/writers/kinds.d.ts +10 -0
- package/dist/lib/staleness/writers/kinds.js +15 -0
- package/dist/lib/staleness/writers/lazy-map.d.ts +13 -0
- package/dist/lib/staleness/writers/lazy-map.js +19 -0
- package/dist/lib/staleness/writers/mcp.d.ts +10 -0
- package/dist/lib/staleness/writers/mcp.js +19 -0
- package/dist/lib/staleness/writers/permissions.d.ts +13 -0
- package/dist/lib/staleness/writers/permissions.js +26 -0
- package/dist/lib/staleness/writers/plugins.d.ts +7 -0
- package/dist/lib/staleness/writers/plugins.js +31 -0
- package/dist/lib/staleness/writers/rules.d.ts +7 -0
- package/dist/lib/staleness/writers/rules.js +55 -0
- package/dist/lib/staleness/writers/skills.d.ts +3 -0
- package/dist/lib/staleness/writers/skills.js +81 -0
- package/dist/lib/staleness/writers/sources.d.ts +16 -0
- package/dist/lib/staleness/writers/sources.js +72 -0
- package/dist/lib/staleness/writers/subagents.d.ts +3 -0
- package/dist/lib/staleness/writers/subagents.js +53 -0
- package/dist/lib/staleness/writers/types.d.ts +36 -0
- package/dist/lib/staleness/writers/types.js +1 -0
- package/dist/lib/staleness/writers/workflows.d.ts +7 -0
- package/dist/lib/staleness/writers/workflows.js +31 -0
- package/dist/lib/state.d.ts +34 -11
- package/dist/lib/state.js +58 -13
- package/dist/lib/subagents.d.ts +0 -2
- package/dist/lib/subagents.js +6 -6
- package/dist/lib/teams/agents.js +1 -1
- package/dist/lib/teams/api.d.ts +67 -0
- package/dist/lib/teams/api.js +78 -0
- package/dist/lib/teams/parsers.d.ts +1 -1
- package/dist/lib/tmux/binary.d.ts +67 -0
- package/dist/lib/tmux/binary.js +141 -0
- package/dist/lib/tmux/index.d.ts +8 -0
- package/dist/lib/tmux/index.js +8 -0
- package/dist/lib/tmux/paths.d.ts +17 -0
- package/dist/lib/tmux/paths.js +30 -0
- package/dist/lib/tmux/session.d.ts +122 -0
- package/dist/lib/tmux/session.js +305 -0
- package/dist/lib/types.d.ts +73 -13
- package/dist/lib/types.js +1 -1
- package/dist/lib/usage.js +1 -1
- package/dist/lib/versions.d.ts +4 -4
- package/dist/lib/versions.js +138 -496
- package/dist/lib/workflows.d.ts +2 -4
- package/dist/lib/workflows.js +3 -4
- package/package.json +6 -3
- package/scripts/postinstall.js +16 -63
- package/dist/commands/status.d.ts +0 -9
- package/dist/commands/status.js +0 -25
package/dist/lib/routines.d.ts
CHANGED
|
@@ -32,6 +32,7 @@ export interface JobConfig {
|
|
|
32
32
|
config?: Record<string, unknown>;
|
|
33
33
|
version?: string;
|
|
34
34
|
runOnce?: boolean;
|
|
35
|
+
endAt?: string;
|
|
35
36
|
}
|
|
36
37
|
/** Metadata for a single job execution, persisted as JSON in the run directory. */
|
|
37
38
|
export interface RunMeta {
|
|
@@ -45,10 +46,19 @@ export interface RunMeta {
|
|
|
45
46
|
completedAt: string | null;
|
|
46
47
|
exitCode: number | null;
|
|
47
48
|
}
|
|
48
|
-
/**
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
/**
|
|
50
|
+
* List all job configs, scanning project > user routine dirs.
|
|
51
|
+
* Project routines (`<project>/.agents/routines/`) shadow user routines of the
|
|
52
|
+
* same name. Project discovery is opt-in via `cwd`; the daemon (which calls
|
|
53
|
+
* `listJobs()` with no argument) only sees user routines.
|
|
54
|
+
*/
|
|
55
|
+
export declare function listJobs(cwd?: string): JobConfig[];
|
|
56
|
+
/**
|
|
57
|
+
* Read a single job config by name, checking project > user.
|
|
58
|
+
* Project discovery is opt-in via `cwd`; daemon callers pass no argument and
|
|
59
|
+
* only resolve user routines.
|
|
60
|
+
*/
|
|
61
|
+
export declare function readJob(name: string, cwd?: string): JobConfig | null;
|
|
52
62
|
/** Write a job config to disk, omitting fields that match defaults. */
|
|
53
63
|
export declare function writeJob(config: JobConfig): void;
|
|
54
64
|
/** Delete a job config file by name. Returns true if the file existed. */
|
|
@@ -57,6 +67,8 @@ export declare function deleteJob(name: string): boolean;
|
|
|
57
67
|
export declare function setJobEnabled(name: string, enabled: boolean): void;
|
|
58
68
|
/** Validate a partial job config, returning a list of human-readable errors. */
|
|
59
69
|
export declare function validateJob(config: Partial<JobConfig>): string[];
|
|
70
|
+
/** True when a job's endAt has already elapsed. False when endAt is unset or in the future. */
|
|
71
|
+
export declare function isPastEndAt(config: Pick<JobConfig, 'endAt'>, now?: Date): boolean;
|
|
60
72
|
/** Expand built-in and user-defined template variables in a job's prompt string. */
|
|
61
73
|
export declare function resolveJobPrompt(config: JobConfig): string;
|
|
62
74
|
/** Parse a human-readable timeout string (e.g. "10m", "2h", "1h30m", "3d", "1w") into milliseconds.
|
package/dist/lib/routines.js
CHANGED
|
@@ -10,7 +10,7 @@ import * as fs from 'fs';
|
|
|
10
10
|
import * as path from 'path';
|
|
11
11
|
import * as yaml from 'yaml';
|
|
12
12
|
import { Cron } from 'croner';
|
|
13
|
-
import { getRoutinesDir, getRunsDir, ensureAgentsDir } from './state.js';
|
|
13
|
+
import { getRoutinesDir, getRunsDir, ensureAgentsDir, getProjectRoutinesDir } from './state.js';
|
|
14
14
|
import { safeJoin } from './paths.js';
|
|
15
15
|
import { ALL_AGENT_IDS } from './agents.js';
|
|
16
16
|
/** Default values applied to every job config when fields are omitted. */
|
|
@@ -20,29 +20,59 @@ const JOB_DEFAULTS = {
|
|
|
20
20
|
timeout: '10m',
|
|
21
21
|
enabled: true,
|
|
22
22
|
};
|
|
23
|
-
/**
|
|
24
|
-
|
|
23
|
+
/**
|
|
24
|
+
* List all job configs, scanning project > user routine dirs.
|
|
25
|
+
* Project routines (`<project>/.agents/routines/`) shadow user routines of the
|
|
26
|
+
* same name. Project discovery is opt-in via `cwd`; the daemon (which calls
|
|
27
|
+
* `listJobs()` with no argument) only sees user routines.
|
|
28
|
+
*/
|
|
29
|
+
export function listJobs(cwd) {
|
|
25
30
|
ensureAgentsDir();
|
|
26
|
-
const
|
|
27
|
-
if (!fs.existsSync(jobsDir))
|
|
28
|
-
return [];
|
|
29
|
-
const files = fs.readdirSync(jobsDir).filter((f) => f.endsWith('.yml') || f.endsWith('.yaml'));
|
|
31
|
+
const seen = new Set();
|
|
30
32
|
const jobs = [];
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
const dirs = [];
|
|
34
|
+
if (cwd) {
|
|
35
|
+
const projectDir = getProjectRoutinesDir(cwd);
|
|
36
|
+
if (projectDir)
|
|
37
|
+
dirs.push(projectDir);
|
|
38
|
+
}
|
|
39
|
+
dirs.push(getRoutinesDir());
|
|
40
|
+
for (const dir of dirs) {
|
|
41
|
+
if (!fs.existsSync(dir))
|
|
42
|
+
continue;
|
|
43
|
+
const files = fs.readdirSync(dir).filter((f) => f.endsWith('.yml') || f.endsWith('.yaml'));
|
|
44
|
+
for (const file of files) {
|
|
45
|
+
const job = readJobFile(path.join(dir, file));
|
|
46
|
+
if (!job)
|
|
47
|
+
continue;
|
|
48
|
+
if (seen.has(job.name))
|
|
49
|
+
continue;
|
|
50
|
+
seen.add(job.name);
|
|
34
51
|
jobs.push(job);
|
|
52
|
+
}
|
|
35
53
|
}
|
|
36
54
|
return jobs;
|
|
37
55
|
}
|
|
38
|
-
/**
|
|
39
|
-
|
|
56
|
+
/**
|
|
57
|
+
* Read a single job config by name, checking project > user.
|
|
58
|
+
* Project discovery is opt-in via `cwd`; daemon callers pass no argument and
|
|
59
|
+
* only resolve user routines.
|
|
60
|
+
*/
|
|
61
|
+
export function readJob(name, cwd) {
|
|
40
62
|
ensureAgentsDir();
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
const
|
|
44
|
-
if (
|
|
45
|
-
|
|
63
|
+
const dirs = [];
|
|
64
|
+
if (cwd) {
|
|
65
|
+
const projectDir = getProjectRoutinesDir(cwd);
|
|
66
|
+
if (projectDir)
|
|
67
|
+
dirs.push(projectDir);
|
|
68
|
+
}
|
|
69
|
+
dirs.push(getRoutinesDir());
|
|
70
|
+
for (const dir of dirs) {
|
|
71
|
+
for (const ext of ['.yml', '.yaml']) {
|
|
72
|
+
const filePath = safeJoin(dir, name + ext);
|
|
73
|
+
if (fs.existsSync(filePath)) {
|
|
74
|
+
return readJobFile(filePath);
|
|
75
|
+
}
|
|
46
76
|
}
|
|
47
77
|
}
|
|
48
78
|
return null;
|
|
@@ -147,8 +177,28 @@ export function validateJob(config) {
|
|
|
147
177
|
if (config.timeout && !parseTimeout(config.timeout)) {
|
|
148
178
|
errors.push('timeout must be like 10m, 2h, 3d, 1w (max 1w)');
|
|
149
179
|
}
|
|
180
|
+
if (config.endAt !== undefined) {
|
|
181
|
+
if (typeof config.endAt !== 'string' || !isParseableDate(config.endAt)) {
|
|
182
|
+
errors.push('endAt must be a parseable ISO 8601 / RFC3339 timestamp (e.g., 2026-12-31T23:59:00Z)');
|
|
183
|
+
}
|
|
184
|
+
}
|
|
150
185
|
return errors;
|
|
151
186
|
}
|
|
187
|
+
function isParseableDate(value) {
|
|
188
|
+
if (!value.trim())
|
|
189
|
+
return false;
|
|
190
|
+
const ts = Date.parse(value);
|
|
191
|
+
return Number.isFinite(ts);
|
|
192
|
+
}
|
|
193
|
+
/** True when a job's endAt has already elapsed. False when endAt is unset or in the future. */
|
|
194
|
+
export function isPastEndAt(config, now = new Date()) {
|
|
195
|
+
if (!config.endAt)
|
|
196
|
+
return false;
|
|
197
|
+
const end = Date.parse(config.endAt);
|
|
198
|
+
if (!Number.isFinite(end))
|
|
199
|
+
return false;
|
|
200
|
+
return now.getTime() >= end;
|
|
201
|
+
}
|
|
152
202
|
/** Expand built-in and user-defined template variables in a job's prompt string. */
|
|
153
203
|
export function resolveJobPrompt(config) {
|
|
154
204
|
const now = new Date();
|
|
@@ -10,8 +10,8 @@ import * as fs from 'fs';
|
|
|
10
10
|
import * as path from 'path';
|
|
11
11
|
import * as os from 'os';
|
|
12
12
|
import * as crypto from 'crypto';
|
|
13
|
-
import { AGENTS } from '../agents.js';
|
|
14
|
-
import {
|
|
13
|
+
import { AGENTS, agentConfigDirName } from '../agents.js';
|
|
14
|
+
import { getVersionsDir } from '../state.js';
|
|
15
15
|
import { composeRules, composeRulesFromState } from './compose.js';
|
|
16
16
|
// Match `@path` preceded by start-of-string or whitespace. This avoids
|
|
17
17
|
// matching emails ("foo@bar.com") and the middle of words. The leading
|
|
@@ -97,7 +97,7 @@ export function supportsRulesImports(agentId) {
|
|
|
97
97
|
function getCompiledRulesPath(agentId, version) {
|
|
98
98
|
const agentConfig = AGENTS[agentId];
|
|
99
99
|
const versionHome = path.join(getVersionsDir(), agentId, version, 'home');
|
|
100
|
-
return path.join(versionHome,
|
|
100
|
+
return path.join(versionHome, agentConfigDirName(agentId), agentConfig.instructionsFile);
|
|
101
101
|
}
|
|
102
102
|
function getManifestPath(compiledPath) {
|
|
103
103
|
return compiledPath + '.manifest.json';
|
|
@@ -151,14 +151,24 @@ export function compileRulesForAgent(agentId, version) {
|
|
|
151
151
|
if (supportsRulesImports(agentId)) {
|
|
152
152
|
return { compiled: false, compiledPath: '', sources: 0 };
|
|
153
153
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
154
|
+
// Route through the layered composer (project > user > extras > system).
|
|
155
|
+
// The previous implementation read only `<systemRules>/AGENTS.md` and
|
|
156
|
+
// inlined its @-imports — that dropped user/extras/project subrules
|
|
157
|
+
// entirely for @-import-incapable agents (Cursor, older Codex), so
|
|
158
|
+
// updates to ~/.agents/rules/subrules/ never reached the version home.
|
|
159
|
+
// composeRulesFromState already returns the concatenated content with
|
|
160
|
+
// every fragment resolved across layers; we just need to record the
|
|
161
|
+
// composed source list for staleness detection.
|
|
162
|
+
let composed;
|
|
163
|
+
try {
|
|
164
|
+
composed = composeRulesFromState({ preset: undefined });
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
// No rules.yaml in any layer, or the default preset is missing — leave
|
|
168
|
+
// the version home untouched (matches the previous file-missing branch).
|
|
157
169
|
return { compiled: false, compiledPath: '', sources: 0 };
|
|
158
170
|
}
|
|
159
|
-
const
|
|
160
|
-
const { content, sources } = resolveImports(rootContent, rulesDir);
|
|
161
|
-
const newContent = COMPILED_HEADER + content;
|
|
171
|
+
const newContent = COMPILED_HEADER + composed.content;
|
|
162
172
|
const compiledPath = getCompiledRulesPath(agentId, version);
|
|
163
173
|
fs.mkdirSync(path.dirname(compiledPath), { recursive: true });
|
|
164
174
|
const existing = fs.existsSync(compiledPath) ? fs.readFileSync(compiledPath, 'utf8') : null;
|
|
@@ -166,7 +176,9 @@ export function compileRulesForAgent(agentId, version) {
|
|
|
166
176
|
return { compiled: false, compiledPath, sources: 0 };
|
|
167
177
|
}
|
|
168
178
|
fs.writeFileSync(compiledPath, newContent);
|
|
169
|
-
|
|
179
|
+
// Track every concrete subrule file the composer included as a source for
|
|
180
|
+
// staleness. composeRulesFromState exposes sourcePath on each ComposedSubrule.
|
|
181
|
+
const allSources = composed.subrules.map(s => s.sourcePath);
|
|
170
182
|
const manifest = {
|
|
171
183
|
compiledAt: new Date().toISOString(),
|
|
172
184
|
sources: allSources.map(p => {
|
package/dist/lib/rules/rules.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import * as fs from 'fs';
|
|
10
10
|
import * as path from 'path';
|
|
11
|
-
import { AGENTS, ALL_AGENT_IDS } from '../agents.js';
|
|
11
|
+
import { AGENTS, ALL_AGENT_IDS, agentConfigDirName } from '../agents.js';
|
|
12
12
|
import { getResolvedRulesDir, getUserRulesDir, getProjectAgentsDir } from '../state.js';
|
|
13
13
|
import { getEffectiveHome } from '../versions.js';
|
|
14
14
|
/**
|
|
@@ -65,7 +65,7 @@ function normalizeContent(content) {
|
|
|
65
65
|
*/
|
|
66
66
|
function getUserConfigDir(agentId) {
|
|
67
67
|
const home = getEffectiveHome(agentId);
|
|
68
|
-
return path.join(home,
|
|
68
|
+
return path.join(home, agentConfigDirName(agentId));
|
|
69
69
|
}
|
|
70
70
|
export function getInstructionsPath(agentId, scope, cwd = process.cwd()) {
|
|
71
71
|
const agent = AGENTS[agentId];
|
|
@@ -211,7 +211,7 @@ export function listInstalledInstructionsWithScope(agentId, cwd = process.cwd(),
|
|
|
211
211
|
const agent = AGENTS[agentId];
|
|
212
212
|
// User-scoped instructions (version-aware when home is provided)
|
|
213
213
|
const home = options?.home || getEffectiveHome(agentId);
|
|
214
|
-
const userConfigDir = path.join(home,
|
|
214
|
+
const userConfigDir = path.join(home, agentConfigDirName(agentId));
|
|
215
215
|
const userPath = path.join(userConfigDir, agent.instructionsFile);
|
|
216
216
|
results.push({
|
|
217
217
|
agentId,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project-local `run:` config discovery.
|
|
3
|
+
*
|
|
4
|
+
* The user/system `agents.yaml` is read through state.ts. Project-local
|
|
5
|
+
* agents.yaml files are discovered from the current working directory upward.
|
|
6
|
+
*/
|
|
7
|
+
import type { RunConfig } from './types.js';
|
|
8
|
+
/** Return project-local run configs from nearest directory upward. */
|
|
9
|
+
export declare function getProjectRunConfigs(startPath?: string): RunConfig[];
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project-local `run:` config discovery.
|
|
3
|
+
*
|
|
4
|
+
* The user/system `agents.yaml` is read through state.ts. Project-local
|
|
5
|
+
* agents.yaml files are discovered from the current working directory upward.
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from 'fs';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
import * as yaml from 'yaml';
|
|
10
|
+
import { getUserAgentsDir } from './state.js';
|
|
11
|
+
function isRecord(value) {
|
|
12
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
13
|
+
}
|
|
14
|
+
/** Return project-local run configs from nearest directory upward. */
|
|
15
|
+
export function getProjectRunConfigs(startPath = process.cwd()) {
|
|
16
|
+
const configs = [];
|
|
17
|
+
let dir = path.resolve(startPath);
|
|
18
|
+
const userAgentsYaml = path.join(getUserAgentsDir(), 'agents.yaml');
|
|
19
|
+
while (dir !== path.dirname(dir)) {
|
|
20
|
+
const manifestPath = path.join(dir, 'agents.yaml');
|
|
21
|
+
if (manifestPath !== userAgentsYaml && fs.existsSync(manifestPath)) {
|
|
22
|
+
try {
|
|
23
|
+
const parsed = yaml.parse(fs.readFileSync(manifestPath, 'utf-8'));
|
|
24
|
+
if (isRecord(parsed?.run)) {
|
|
25
|
+
configs.push(parsed.run);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// Ignore malformed project config and keep walking.
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
dir = path.dirname(dir);
|
|
33
|
+
}
|
|
34
|
+
return configs;
|
|
35
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Selector-based defaults for `agents run`.
|
|
3
|
+
*
|
|
4
|
+
* Stored under agents.yaml:
|
|
5
|
+
*
|
|
6
|
+
* run:
|
|
7
|
+
* defaults:
|
|
8
|
+
* "claude:*":
|
|
9
|
+
* mode: auto
|
|
10
|
+
* model: opus
|
|
11
|
+
* "claude:2.1.45":
|
|
12
|
+
* mode: plan
|
|
13
|
+
*/
|
|
14
|
+
import type { AgentId, Mode, RunConfig, RunDefaults } from './types.js';
|
|
15
|
+
export interface ParsedRunDefaultSelector {
|
|
16
|
+
agent: AgentId;
|
|
17
|
+
version: string;
|
|
18
|
+
selector: string;
|
|
19
|
+
}
|
|
20
|
+
export interface ResolvedRunDefaults extends RunDefaults {
|
|
21
|
+
sources: {
|
|
22
|
+
mode?: string;
|
|
23
|
+
model?: string;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export interface RunDefaultEntry {
|
|
27
|
+
selector: string;
|
|
28
|
+
defaults: RunDefaults;
|
|
29
|
+
}
|
|
30
|
+
type RunDefaultsInput = {
|
|
31
|
+
mode?: unknown;
|
|
32
|
+
model?: unknown;
|
|
33
|
+
};
|
|
34
|
+
export declare function normalizeRunDefaultMode(input: string): Mode;
|
|
35
|
+
export declare function parseRunDefaultSelector(input: string): ParsedRunDefaultSelector;
|
|
36
|
+
export declare function resolveRunDefaultsFromConfig(runConfig: RunConfig | undefined, agent: AgentId, version?: string | null): ResolvedRunDefaults;
|
|
37
|
+
export declare function resolveRunDefaultsFromConfigs(runConfigs: Array<RunConfig | undefined>, agent: AgentId, version?: string | null): ResolvedRunDefaults;
|
|
38
|
+
export declare function resolveRunDefaults(agent: AgentId, version?: string | null, startPath?: string): ResolvedRunDefaults;
|
|
39
|
+
export declare function listRunDefaults(): RunDefaultEntry[];
|
|
40
|
+
export declare function setRunDefault(selectorInput: string, defaultsInput: RunDefaultsInput): RunDefaultEntry;
|
|
41
|
+
export declare function unsetRunDefault(selectorInput: string): boolean;
|
|
42
|
+
export {};
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Selector-based defaults for `agents run`.
|
|
3
|
+
*
|
|
4
|
+
* Stored under agents.yaml:
|
|
5
|
+
*
|
|
6
|
+
* run:
|
|
7
|
+
* defaults:
|
|
8
|
+
* "claude:*":
|
|
9
|
+
* mode: auto
|
|
10
|
+
* model: opus
|
|
11
|
+
* "claude:2.1.45":
|
|
12
|
+
* mode: plan
|
|
13
|
+
*/
|
|
14
|
+
import { ALL_MODES } from './types.js';
|
|
15
|
+
import { AGENTS } from './agents.js';
|
|
16
|
+
import { readMeta, updateMeta } from './state.js';
|
|
17
|
+
import { getProjectRunConfigs } from './run-config.js';
|
|
18
|
+
const VERSION_RE = /^(?:\*|latest|(?!.*\.\.)[A-Za-z0-9._+-]{1,64})$/;
|
|
19
|
+
function isAgentId(value) {
|
|
20
|
+
return value in AGENTS;
|
|
21
|
+
}
|
|
22
|
+
export function normalizeRunDefaultMode(input) {
|
|
23
|
+
const mode = input.trim().toLowerCase();
|
|
24
|
+
if (mode === 'full')
|
|
25
|
+
return 'skip';
|
|
26
|
+
if (ALL_MODES.includes(mode))
|
|
27
|
+
return mode;
|
|
28
|
+
throw new Error(`Invalid mode '${input}'. Use one of: ${ALL_MODES.join(', ')} (or 'full' as an alias for 'skip').`);
|
|
29
|
+
}
|
|
30
|
+
function normalizeRunDefaults(defaults, selector) {
|
|
31
|
+
const out = {};
|
|
32
|
+
if (defaults.mode !== undefined) {
|
|
33
|
+
if (typeof defaults.mode !== 'string') {
|
|
34
|
+
throw new Error(`Invalid mode in run.defaults.${selector}: expected a string.`);
|
|
35
|
+
}
|
|
36
|
+
out.mode = normalizeRunDefaultMode(defaults.mode);
|
|
37
|
+
}
|
|
38
|
+
if (defaults.model !== undefined) {
|
|
39
|
+
if (typeof defaults.model !== 'string' || defaults.model.trim() === '') {
|
|
40
|
+
throw new Error(`Invalid model in run.defaults.${selector}: expected a non-empty string.`);
|
|
41
|
+
}
|
|
42
|
+
out.model = defaults.model.trim();
|
|
43
|
+
}
|
|
44
|
+
return out;
|
|
45
|
+
}
|
|
46
|
+
export function parseRunDefaultSelector(input) {
|
|
47
|
+
const raw = input.trim();
|
|
48
|
+
if (!raw)
|
|
49
|
+
throw new Error('Selector is required. Use <agent>:<version>, <agent>@<version>, or <agent>:*.');
|
|
50
|
+
let agentPart;
|
|
51
|
+
let versionPart;
|
|
52
|
+
if (raw.includes('@')) {
|
|
53
|
+
const parts = raw.split('@');
|
|
54
|
+
if (parts.length !== 2)
|
|
55
|
+
throw new Error(`Invalid selector '${input}'. Use <agent>@<version>.`);
|
|
56
|
+
[agentPart, versionPart] = parts;
|
|
57
|
+
}
|
|
58
|
+
else if (raw.includes(':')) {
|
|
59
|
+
const idx = raw.indexOf(':');
|
|
60
|
+
agentPart = raw.slice(0, idx);
|
|
61
|
+
versionPart = raw.slice(idx + 1);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
agentPart = raw;
|
|
65
|
+
versionPart = '*';
|
|
66
|
+
}
|
|
67
|
+
const agent = agentPart.toLowerCase();
|
|
68
|
+
if (!isAgentId(agent)) {
|
|
69
|
+
throw new Error(`Invalid agent '${agentPart}'. Available agents: ${Object.keys(AGENTS).join(', ')}.`);
|
|
70
|
+
}
|
|
71
|
+
if (!VERSION_RE.test(versionPart)) {
|
|
72
|
+
throw new Error(`Invalid selector version '${versionPart}'. Use *, latest, or [A-Za-z0-9._+-]{1,64}.`);
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
agent,
|
|
76
|
+
version: versionPart,
|
|
77
|
+
selector: `${agent}:${versionPart}`,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function sortedDefaults(defaults) {
|
|
81
|
+
return Object.fromEntries(Object.entries(defaults).sort(([a], [b]) => a.localeCompare(b)));
|
|
82
|
+
}
|
|
83
|
+
export function resolveRunDefaultsFromConfig(runConfig, agent, version) {
|
|
84
|
+
const defaults = runConfig?.defaults ?? {};
|
|
85
|
+
const wildcardSelector = `${agent}:*`;
|
|
86
|
+
const exactSelector = version ? `${agent}:${version}` : null;
|
|
87
|
+
const resolved = { sources: {} };
|
|
88
|
+
const wildcard = defaults[wildcardSelector]
|
|
89
|
+
? normalizeRunDefaults(defaults[wildcardSelector], wildcardSelector)
|
|
90
|
+
: null;
|
|
91
|
+
if (wildcard?.mode) {
|
|
92
|
+
resolved.mode = wildcard.mode;
|
|
93
|
+
resolved.sources.mode = wildcardSelector;
|
|
94
|
+
}
|
|
95
|
+
if (wildcard?.model) {
|
|
96
|
+
resolved.model = wildcard.model;
|
|
97
|
+
resolved.sources.model = wildcardSelector;
|
|
98
|
+
}
|
|
99
|
+
if (exactSelector && defaults[exactSelector]) {
|
|
100
|
+
const exact = normalizeRunDefaults(defaults[exactSelector], exactSelector);
|
|
101
|
+
if (exact.mode) {
|
|
102
|
+
resolved.mode = exact.mode;
|
|
103
|
+
resolved.sources.mode = exactSelector;
|
|
104
|
+
}
|
|
105
|
+
if (exact.model) {
|
|
106
|
+
resolved.model = exact.model;
|
|
107
|
+
resolved.sources.model = exactSelector;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return resolved;
|
|
111
|
+
}
|
|
112
|
+
export function resolveRunDefaultsFromConfigs(runConfigs, agent, version) {
|
|
113
|
+
const resolved = { sources: {} };
|
|
114
|
+
for (const runConfig of runConfigs) {
|
|
115
|
+
const next = resolveRunDefaultsFromConfig(runConfig, agent, version);
|
|
116
|
+
if (next.mode) {
|
|
117
|
+
resolved.mode = next.mode;
|
|
118
|
+
resolved.sources.mode = next.sources.mode;
|
|
119
|
+
}
|
|
120
|
+
if (next.model) {
|
|
121
|
+
resolved.model = next.model;
|
|
122
|
+
resolved.sources.model = next.sources.model;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return resolved;
|
|
126
|
+
}
|
|
127
|
+
export function resolveRunDefaults(agent, version, startPath = process.cwd()) {
|
|
128
|
+
const projectRunConfigs = getProjectRunConfigs(startPath).reverse();
|
|
129
|
+
return resolveRunDefaultsFromConfigs([readMeta().run, ...projectRunConfigs], agent, version);
|
|
130
|
+
}
|
|
131
|
+
export function listRunDefaults() {
|
|
132
|
+
const defaults = readMeta().run?.defaults ?? {};
|
|
133
|
+
return Object.entries(defaults)
|
|
134
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
135
|
+
.map(([selector, value]) => ({
|
|
136
|
+
selector,
|
|
137
|
+
defaults: normalizeRunDefaults(value, selector),
|
|
138
|
+
}));
|
|
139
|
+
}
|
|
140
|
+
export function setRunDefault(selectorInput, defaultsInput) {
|
|
141
|
+
const parsed = parseRunDefaultSelector(selectorInput);
|
|
142
|
+
const defaults = normalizeRunDefaults(defaultsInput, parsed.selector);
|
|
143
|
+
if (!defaults.mode && !defaults.model) {
|
|
144
|
+
throw new Error('Set at least one default: --mode <mode> or --model <model>.');
|
|
145
|
+
}
|
|
146
|
+
updateMeta((meta) => {
|
|
147
|
+
const run = { ...(meta.run ?? {}) };
|
|
148
|
+
const currentDefaults = { ...(run.defaults ?? {}) };
|
|
149
|
+
currentDefaults[parsed.selector] = {
|
|
150
|
+
...(currentDefaults[parsed.selector] ?? {}),
|
|
151
|
+
...defaults,
|
|
152
|
+
};
|
|
153
|
+
run.defaults = sortedDefaults(currentDefaults);
|
|
154
|
+
return { ...meta, run };
|
|
155
|
+
});
|
|
156
|
+
return {
|
|
157
|
+
selector: parsed.selector,
|
|
158
|
+
defaults: {
|
|
159
|
+
...(readMeta().run?.defaults?.[parsed.selector] ?? {}),
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
export function unsetRunDefault(selectorInput) {
|
|
164
|
+
const parsed = parseRunDefaultSelector(selectorInput);
|
|
165
|
+
let removed = false;
|
|
166
|
+
updateMeta((meta) => {
|
|
167
|
+
const run = { ...(meta.run ?? {}) };
|
|
168
|
+
const currentDefaults = { ...(run.defaults ?? {}) };
|
|
169
|
+
removed = Object.prototype.hasOwnProperty.call(currentDefaults, parsed.selector);
|
|
170
|
+
delete currentDefaults[parsed.selector];
|
|
171
|
+
if (Object.keys(currentDefaults).length > 0) {
|
|
172
|
+
run.defaults = sortedDefaults(currentDefaults);
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
delete run.defaults;
|
|
176
|
+
}
|
|
177
|
+
return { ...meta, run };
|
|
178
|
+
});
|
|
179
|
+
return removed;
|
|
180
|
+
}
|
package/dist/lib/runner.js
CHANGED
|
@@ -21,6 +21,7 @@ const AGENT_COMMANDS = {
|
|
|
21
21
|
claude: ['claude', '-p', '--verbose', '{prompt}', '--output-format', 'stream-json', '--permission-mode', 'plan'],
|
|
22
22
|
codex: ['codex', 'exec', '--sandbox', 'workspace-write', '{prompt}', '--json'],
|
|
23
23
|
gemini: ['gemini', '{prompt}', '--output-format', 'stream-json'],
|
|
24
|
+
kimi: ['kimi', '--prompt', '{prompt}', '--output-format', 'stream-json'],
|
|
24
25
|
};
|
|
25
26
|
/** Build the full CLI argv for executing a job, applying mode, model, and permission flags. */
|
|
26
27
|
export function buildJobCommand(config, resolvedPrompt) {
|
|
@@ -72,14 +73,14 @@ export function buildJobCommand(config, resolvedPrompt) {
|
|
|
72
73
|
}
|
|
73
74
|
if (config.agent === 'codex') {
|
|
74
75
|
if (mode === 'edit') {
|
|
75
|
-
cmd.push('--
|
|
76
|
+
cmd.push('--dangerously-bypass-approvals-and-sandbox');
|
|
76
77
|
}
|
|
77
78
|
else if (mode === 'skip') {
|
|
78
|
-
// Remove sandbox restriction, just --
|
|
79
|
+
// Remove sandbox restriction, just --dangerously-bypass-approvals-and-sandbox
|
|
79
80
|
const sbIndex = cmd.indexOf('--sandbox');
|
|
80
81
|
if (sbIndex !== -1)
|
|
81
82
|
cmd.splice(sbIndex, 2);
|
|
82
|
-
cmd.push('--
|
|
83
|
+
cmd.push('--dangerously-bypass-approvals-and-sandbox');
|
|
83
84
|
}
|
|
84
85
|
appendModelAndReasoning(cmd, config);
|
|
85
86
|
}
|
|
@@ -92,6 +93,18 @@ export function buildJobCommand(config, resolvedPrompt) {
|
|
|
92
93
|
}
|
|
93
94
|
appendModelAndReasoning(cmd, config);
|
|
94
95
|
}
|
|
96
|
+
if (config.agent === 'kimi') {
|
|
97
|
+
if (mode === 'plan') {
|
|
98
|
+
cmd.push('--plan');
|
|
99
|
+
}
|
|
100
|
+
else if (mode === 'auto') {
|
|
101
|
+
cmd.push('--auto');
|
|
102
|
+
}
|
|
103
|
+
else if (mode === 'skip') {
|
|
104
|
+
cmd.push('--yolo');
|
|
105
|
+
}
|
|
106
|
+
appendModelAndReasoning(cmd, config);
|
|
107
|
+
}
|
|
95
108
|
return cmd;
|
|
96
109
|
}
|
|
97
110
|
/**
|
package/dist/lib/scheduler.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* on startup and reloads them on SIGHUP.
|
|
7
7
|
*/
|
|
8
8
|
import { Cron } from 'croner';
|
|
9
|
-
import { listJobs, deleteJob } from './routines.js';
|
|
9
|
+
import { listJobs, deleteJob, isPastEndAt, setJobEnabled } from './routines.js';
|
|
10
10
|
/** In-memory cron scheduler that triggers a callback when jobs fire. */
|
|
11
11
|
export class JobScheduler {
|
|
12
12
|
jobs = new Map();
|
|
@@ -32,6 +32,20 @@ export class JobScheduler {
|
|
|
32
32
|
if (config.timezone)
|
|
33
33
|
cronOptions.timezone = config.timezone;
|
|
34
34
|
const cron = new Cron(config.schedule, cronOptions, async () => {
|
|
35
|
+
// endAt: once the configured end time has passed, auto-disable and stop
|
|
36
|
+
// firing. We persist enabled=false to disk so the next daemon reload
|
|
37
|
+
// doesn't re-schedule, and unschedule in-memory so this cron stops.
|
|
38
|
+
if (isPastEndAt(config)) {
|
|
39
|
+
this.unschedule(config.name);
|
|
40
|
+
try {
|
|
41
|
+
setJobEnabled(config.name, false);
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
console.error(`Job '${config.name}' endAt auto-disable failed:`, err.message);
|
|
45
|
+
}
|
|
46
|
+
console.log(`Job '${config.name}' reached endAt (${config.endAt}); auto-disabled.`);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
35
49
|
try {
|
|
36
50
|
await this.onTrigger(config);
|
|
37
51
|
}
|
|
Binary file
|
|
Binary file
|
|
@@ -5,7 +5,15 @@
|
|
|
5
5
|
<key>files</key>
|
|
6
6
|
<dict/>
|
|
7
7
|
<key>files2</key>
|
|
8
|
-
<dict
|
|
8
|
+
<dict>
|
|
9
|
+
<key>embedded.provisionprofile</key>
|
|
10
|
+
<dict>
|
|
11
|
+
<key>hash2</key>
|
|
12
|
+
<data>
|
|
13
|
+
2vfA/eR3dTYgNc/fXhdADUPkp5tRIepPzE3FCLfDx4w=
|
|
14
|
+
</data>
|
|
15
|
+
</dict>
|
|
16
|
+
</dict>
|
|
9
17
|
<key>rules</key>
|
|
10
18
|
<dict>
|
|
11
19
|
<key>^Resources/</key>
|
|
@@ -12,10 +12,14 @@
|
|
|
12
12
|
* modules in `src/lib/secrets/` must import `getKeychainHelperPath()` rather
|
|
13
13
|
* than recomputing it.
|
|
14
14
|
*/
|
|
15
|
+
/** Redirect the install root (test only). Returns the previous override so callers can restore. */
|
|
16
|
+
export declare function setInstallRootForTest(dir: string | null): string | null;
|
|
15
17
|
/**
|
|
16
18
|
* Idempotent install. Copies the bundled `.app` to the stable user path. Skips
|
|
17
|
-
* if the destination already exists
|
|
18
|
-
*
|
|
19
|
+
* if the destination already exists, `codesign --verify` passes, AND the
|
|
20
|
+
* installed executable matches the bundled one byte-for-byte — a valid
|
|
21
|
+
* signature alone is not enough, because an outdated helper signs clean too.
|
|
22
|
+
* `forceReinstall=true` skips all checks and always copies.
|
|
19
23
|
*
|
|
20
24
|
* Notarization is checked via `spctl --assess` after install — a failure is
|
|
21
25
|
* logged as a warning but does NOT throw. Notarization checks require network
|
|
@@ -27,7 +31,11 @@ export declare function ensureKeychainHelperInstalled(opts?: {
|
|
|
27
31
|
}): void;
|
|
28
32
|
/**
|
|
29
33
|
* Return the absolute path to the helper executable. If the installed bundle
|
|
30
|
-
* is missing,
|
|
34
|
+
* is missing, or is stale relative to the bundled source helper, performs a
|
|
35
|
+
* lazy (re)install first. The staleness check is what lets an upgraded CLI
|
|
36
|
+
* replace a helper a previous version installed — `agents helper install`
|
|
37
|
+
* never runs on `npm i -g`, so this call site is the only one every machine
|
|
38
|
+
* is guaranteed to pass through.
|
|
31
39
|
*
|
|
32
40
|
* Throws on non-darwin.
|
|
33
41
|
*/
|