@phnx-labs/agents-cli 1.20.72 → 1.20.73
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 +299 -0
- package/README.md +40 -4
- package/dist/bin/agents +0 -0
- package/dist/commands/activity.d.ts +12 -0
- package/dist/commands/activity.js +68 -0
- package/dist/commands/apply.d.ts +13 -0
- package/dist/commands/apply.js +28 -3
- package/dist/commands/browser.js +1 -1
- package/dist/commands/cloud.js +21 -11
- package/dist/commands/doctor.js +8 -4
- package/dist/commands/events.d.ts +11 -10
- package/dist/commands/events.js +30 -21
- package/dist/commands/exec.d.ts +34 -0
- package/dist/commands/exec.js +337 -39
- package/dist/commands/feed.d.ts +13 -0
- package/dist/commands/feed.js +130 -29
- package/dist/commands/hq.d.ts +2 -0
- package/dist/commands/hq.js +58 -0
- package/dist/commands/lease.d.ts +19 -0
- package/dist/commands/lease.js +138 -11
- package/dist/commands/login.d.ts +2 -0
- package/dist/commands/login.js +123 -0
- package/dist/commands/logs.js +1 -1
- package/dist/commands/mine.d.ts +27 -0
- package/dist/commands/mine.js +207 -0
- package/dist/commands/monitors.js +79 -49
- package/dist/commands/plugins.js +1 -0
- package/dist/commands/profiles.js +158 -0
- package/dist/commands/repo.js +2 -60
- package/dist/commands/resources.d.ts +5 -0
- package/dist/commands/resources.js +95 -0
- package/dist/commands/routines.d.ts +2 -0
- package/dist/commands/routines.js +139 -37
- package/dist/commands/secrets.js +234 -47
- package/dist/commands/send.d.ts +15 -0
- package/dist/commands/send.js +62 -0
- package/dist/commands/sessions-tail.d.ts +1 -0
- package/dist/commands/sessions-tail.js +20 -9
- package/dist/commands/sessions.js +18 -3
- package/dist/commands/setup-mine.d.ts +18 -0
- package/dist/commands/setup-mine.js +106 -0
- package/dist/commands/setup-share.js +2 -2
- package/dist/commands/setup.js +3 -1
- package/dist/commands/share.d.ts +8 -1
- package/dist/commands/share.js +122 -37
- package/dist/commands/ssh.d.ts +9 -0
- package/dist/commands/ssh.js +196 -26
- package/dist/commands/sync.js +1 -1
- package/dist/commands/teams.js +11 -2
- package/dist/index.js +79 -10
- package/dist/lib/activity.d.ts +124 -0
- package/dist/lib/activity.js +542 -0
- package/dist/lib/agents.js +16 -5
- package/dist/lib/artifact-actions.d.ts +1 -1
- package/dist/lib/artifact-actions.js +1 -1
- package/dist/lib/ask-classifier.d.ts +2 -1
- package/dist/lib/ask-classifier.js +3 -3
- package/dist/lib/auth-health.d.ts +32 -4
- package/dist/lib/auth-health.js +40 -1
- package/dist/lib/auto-pull.js +8 -1
- package/dist/lib/brand.d.ts +40 -0
- package/dist/lib/brand.js +122 -0
- package/dist/lib/browser/drivers/ssh.js +4 -7
- package/dist/lib/browser/service.js +6 -8
- package/dist/lib/channels/providers/index.d.ts +2 -0
- package/dist/lib/channels/providers/index.js +20 -0
- package/dist/lib/channels/providers/mailbox.d.ts +2 -0
- package/dist/lib/channels/providers/mailbox.js +26 -0
- package/dist/lib/channels/providers/openclaw-telegram.d.ts +2 -0
- package/dist/lib/channels/providers/openclaw-telegram.js +34 -0
- package/dist/lib/channels/providers/rush.d.ts +6 -0
- package/dist/lib/channels/providers/rush.js +55 -0
- package/dist/lib/channels/registry.d.ts +42 -0
- package/dist/lib/channels/registry.js +20 -0
- package/dist/lib/channels/resolve.d.ts +11 -0
- package/dist/lib/channels/resolve.js +12 -0
- package/dist/lib/cli-resources.d.ts +18 -0
- package/dist/lib/cli-resources.js +53 -4
- package/dist/lib/cloud/codex.js +6 -3
- package/dist/lib/cloud/factory.d.ts +1 -0
- package/dist/lib/cloud/factory.js +10 -4
- package/dist/lib/cloud/rush.d.ts +3 -1
- package/dist/lib/cloud/rush.js +6 -1
- package/dist/lib/cloud/types.d.ts +2 -0
- package/dist/lib/codex-home.d.ts +35 -0
- package/dist/lib/codex-home.js +136 -0
- package/dist/lib/crabbox/cli.d.ts +44 -0
- package/dist/lib/crabbox/cli.js +120 -20
- package/dist/lib/crabbox/lease.d.ts +52 -1
- package/dist/lib/crabbox/lease.js +117 -16
- package/dist/lib/crabbox/progress.d.ts +31 -0
- package/dist/lib/crabbox/progress.js +76 -0
- package/dist/lib/crabbox/runtimes.d.ts +4 -0
- package/dist/lib/crabbox/runtimes.js +33 -8
- package/dist/lib/crabbox/setup-copy.d.ts +87 -0
- package/dist/lib/crabbox/setup-copy.js +127 -0
- package/dist/lib/daemon.d.ts +5 -5
- package/dist/lib/daemon.js +9 -9
- package/dist/lib/drive-sync.js +6 -3
- package/dist/lib/event-stream.d.ts +36 -0
- package/dist/lib/event-stream.js +68 -0
- package/dist/lib/events.d.ts +1 -1
- package/dist/lib/exec.d.ts +11 -0
- package/dist/lib/exec.js +23 -2
- package/dist/lib/feed-policy.d.ts +1 -1
- package/dist/lib/feed-policy.js +14 -9
- package/dist/lib/feed-ranking.d.ts +32 -0
- package/dist/lib/feed-ranking.js +224 -0
- package/dist/lib/feed.d.ts +35 -2
- package/dist/lib/feed.js +66 -3
- package/dist/lib/fleet/auth-sync.d.ts +53 -0
- package/dist/lib/fleet/auth-sync.js +92 -0
- package/dist/lib/fleet/remote-login.d.ts +187 -0
- package/dist/lib/fleet/remote-login.js +556 -0
- package/dist/lib/format.d.ts +30 -3
- package/dist/lib/format.js +34 -5
- package/dist/lib/fs-atomic.d.ts +7 -2
- package/dist/lib/fs-atomic.js +8 -12
- package/dist/lib/git.d.ts +16 -0
- package/dist/lib/git.js +79 -2
- package/dist/lib/heal.js +11 -3
- package/dist/lib/hosts/progress.d.ts +28 -10
- package/dist/lib/hosts/progress.js +79 -22
- package/dist/lib/hosts/remote-cmd.js +4 -0
- package/dist/lib/hq/floor.d.ts +87 -0
- package/dist/lib/hq/floor.js +226 -0
- package/dist/lib/menubar/install-menubar.js +14 -20
- package/dist/lib/notify.d.ts +1 -0
- package/dist/lib/notify.js +3 -3
- package/dist/lib/open-url.d.ts +2 -0
- package/dist/lib/open-url.js +19 -0
- package/dist/lib/openclaw-keychain.d.ts +56 -0
- package/dist/lib/openclaw-keychain.js +236 -0
- package/dist/lib/overdue.js +10 -0
- package/dist/lib/permissions.d.ts +44 -1
- package/dist/lib/permissions.js +284 -7
- package/dist/lib/project-launch.d.ts +6 -12
- package/dist/lib/project-launch.js +13 -228
- package/dist/lib/project-resources.d.ts +7 -0
- package/dist/lib/project-resources.js +291 -0
- package/dist/lib/pty-client.d.ts +27 -0
- package/dist/lib/pty-client.js +136 -10
- package/dist/lib/refresh.js +14 -10
- package/dist/lib/resource-profiles.d.ts +26 -0
- package/dist/lib/resource-profiles.js +157 -0
- package/dist/lib/resources/permissions.js +7 -1
- package/dist/lib/resources/types.d.ts +1 -1
- package/dist/lib/resources.d.ts +1 -1
- package/dist/lib/resources.js +32 -3
- package/dist/lib/routines.d.ts +11 -0
- package/dist/lib/routines.js +56 -15
- package/dist/lib/runner.d.ts +1 -0
- package/dist/lib/runner.js +66 -5
- package/dist/lib/secrets/bundles.d.ts +16 -3
- package/dist/lib/secrets/bundles.js +206 -37
- package/dist/lib/secrets/icloud-import.d.ts +2 -2
- package/dist/lib/secrets/icloud-import.js +9 -6
- package/dist/lib/secrets/mcp.js +1 -1
- package/dist/lib/secrets/vault-age-helper.d.ts +1 -0
- package/dist/lib/secrets/vault-age-helper.js +34 -0
- package/dist/lib/secrets/vault.d.ts +49 -0
- package/dist/lib/secrets/vault.js +397 -0
- package/dist/lib/self-heal/checks/path.js +3 -1
- package/dist/lib/self-heal/checks/shadowing.js +10 -2
- package/dist/lib/self-heal/checks/shims.js +19 -11
- package/dist/lib/session/cloud.d.ts +2 -2
- package/dist/lib/session/cloud.js +2 -2
- package/dist/lib/session/db.d.ts +4 -0
- package/dist/lib/session/db.js +44 -7
- package/dist/lib/session/discover.d.ts +2 -0
- package/dist/lib/session/discover.js +114 -3
- package/dist/lib/session/stream-render.d.ts +3 -0
- package/dist/lib/session/stream-render.js +130 -0
- package/dist/lib/session/types.d.ts +6 -0
- package/dist/lib/share/analytics.d.ts +13 -0
- package/dist/lib/share/analytics.js +45 -0
- package/dist/lib/share/config.d.ts +19 -5
- package/dist/lib/share/config.js +44 -8
- package/dist/lib/share/provision.d.ts +58 -6
- package/dist/lib/share/provision.js +97 -22
- package/dist/lib/share/publish.d.ts +36 -13
- package/dist/lib/share/publish.js +55 -8
- package/dist/lib/share/worker-template.js +83 -17
- package/dist/lib/shims.d.ts +9 -0
- package/dist/lib/shims.js +91 -13
- package/dist/lib/ssh-exec.d.ts +14 -0
- package/dist/lib/ssh-exec.js +57 -0
- package/dist/lib/staleness/detectors/hooks.js +25 -1
- package/dist/lib/staleness/detectors/permissions.js +66 -0
- package/dist/lib/staleness/detectors/workflows.js +20 -0
- package/dist/lib/staleness/writers/hooks.js +58 -4
- package/dist/lib/staleness/writers/permissions.js +2 -2
- package/dist/lib/staleness/writers/skills.js +1 -1
- package/dist/lib/staleness/writers/sources.d.ts +10 -1
- package/dist/lib/staleness/writers/sources.js +68 -1
- package/dist/lib/star-nudge.d.ts +45 -0
- package/dist/lib/star-nudge.js +91 -0
- package/dist/lib/startup/command-registry.d.ts +6 -1
- package/dist/lib/startup/command-registry.js +17 -3
- package/dist/lib/state.d.ts +2 -0
- package/dist/lib/state.js +3 -0
- package/dist/lib/subagents-registry.d.ts +2 -0
- package/dist/lib/subagents-registry.js +3 -0
- package/dist/lib/teams/parsers.js +214 -6
- package/dist/lib/teams/supervisor.d.ts +7 -0
- package/dist/lib/teams/supervisor.js +2 -1
- package/dist/lib/template.d.ts +1 -1
- package/dist/lib/template.js +1 -1
- package/dist/lib/triggers/webhook.js +36 -3
- package/dist/lib/types.d.ts +73 -0
- package/dist/lib/version.d.ts +40 -0
- package/dist/lib/version.js +94 -16
- package/dist/lib/versions.d.ts +11 -0
- package/dist/lib/versions.js +208 -76
- package/dist/lib/workflows.d.ts +2 -0
- package/dist/lib/workflows.js +88 -0
- package/package.json +2 -1
- package/dist/commands/daemon.d.ts +0 -10
- package/dist/commands/daemon.js +0 -121
package/dist/lib/pty-client.js
CHANGED
|
@@ -11,8 +11,10 @@ import { fileURLToPath } from 'url';
|
|
|
11
11
|
import * as path from 'path';
|
|
12
12
|
import { getSocketPath, getPtyLogPath, isPtyServerRunning } from './pty-server.js';
|
|
13
13
|
import { backgroundSpawnOptions } from './platform/process.js';
|
|
14
|
+
import { BUN_VIRTUAL_ROOT } from './cli-entry.js';
|
|
14
15
|
const CONNECT_TIMEOUT_MS = 5000;
|
|
15
16
|
const RESPONSE_TIMEOUT_MS = 30000;
|
|
17
|
+
const START_TIMEOUT_MS = 5000;
|
|
16
18
|
const IS_WINDOWS = process.platform === 'win32';
|
|
17
19
|
/**
|
|
18
20
|
* Send a request to the PTY server and return the response.
|
|
@@ -36,18 +38,44 @@ async function ensureServer() {
|
|
|
36
38
|
// Find the entry point to spawn the server
|
|
37
39
|
const { bin, args } = getServerSpawnArgs();
|
|
38
40
|
const logPath = getPtyLogPath();
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
let logFd;
|
|
42
|
+
try {
|
|
43
|
+
logFd = fs.openSync(logPath, 'a');
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
47
|
+
throw new Error([
|
|
48
|
+
'PTY server failed to start before spawning.',
|
|
49
|
+
`Spawned: ${formatCommand({ bin, args })}`,
|
|
50
|
+
`Log: ${logPath}`,
|
|
51
|
+
`Log open error: ${message}`,
|
|
52
|
+
].join('\n'));
|
|
53
|
+
}
|
|
54
|
+
let child;
|
|
55
|
+
try {
|
|
56
|
+
child = spawn(bin, args, {
|
|
57
|
+
stdio: ['ignore', logFd, logFd],
|
|
58
|
+
...backgroundSpawnOptions({ fdStdio: true }),
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
fs.closeSync(logFd);
|
|
63
|
+
}
|
|
64
|
+
let childExit;
|
|
65
|
+
let childError;
|
|
66
|
+
let lastReadinessError;
|
|
67
|
+
child.once('exit', (code, signal) => {
|
|
68
|
+
childExit = { code, signal };
|
|
69
|
+
});
|
|
70
|
+
child.once('error', (err) => {
|
|
71
|
+
childError = err;
|
|
43
72
|
});
|
|
44
73
|
child.unref();
|
|
45
|
-
fs.closeSync(logFd);
|
|
46
74
|
// Wait for the server to become reachable. On Unix the socket file appearing is
|
|
47
75
|
// a cheap readiness signal; on Windows the named pipe is not a filesystem object
|
|
48
76
|
// (fs.existsSync always returns false), so we just attempt the ping directly.
|
|
49
77
|
const socketPath = getSocketPath();
|
|
50
|
-
const deadline = Date.now() +
|
|
78
|
+
const deadline = Date.now() + START_TIMEOUT_MS;
|
|
51
79
|
while (Date.now() < deadline) {
|
|
52
80
|
if (IS_WINDOWS || fs.existsSync(socketPath)) {
|
|
53
81
|
// Verify we can connect
|
|
@@ -55,15 +83,50 @@ async function ensureServer() {
|
|
|
55
83
|
await sendRequest({ action: 'ping' });
|
|
56
84
|
return;
|
|
57
85
|
}
|
|
58
|
-
catch {
|
|
59
|
-
|
|
86
|
+
catch (err) {
|
|
87
|
+
lastReadinessError = err instanceof Error ? err : new Error(String(err));
|
|
60
88
|
}
|
|
61
89
|
}
|
|
90
|
+
if (childError || childExit)
|
|
91
|
+
break;
|
|
62
92
|
await new Promise(r => setTimeout(r, 100));
|
|
63
93
|
}
|
|
64
|
-
throw new Error(
|
|
94
|
+
throw new Error(buildPtyStartFailureMessage({
|
|
95
|
+
timeoutMs: START_TIMEOUT_MS,
|
|
96
|
+
spawn: { bin, args },
|
|
97
|
+
exit: childExit,
|
|
98
|
+
spawnError: childError,
|
|
99
|
+
lastReadinessError,
|
|
100
|
+
logPath,
|
|
101
|
+
}));
|
|
65
102
|
}
|
|
66
|
-
function getServerSpawnArgs() {
|
|
103
|
+
export function getServerSpawnArgs(options = {}) {
|
|
104
|
+
const isStandaloneExecutable = options.isStandaloneExecutable
|
|
105
|
+
?? isBunStandaloneExecutable();
|
|
106
|
+
if (isStandaloneExecutable) {
|
|
107
|
+
// A Bun `--compile` standalone binary cannot `require()` a native addon, so
|
|
108
|
+
// running the PTY sidecar AS this binary fails to load node-pty's pty.node
|
|
109
|
+
// (fatal on macOS; see #315). Prefer a real `node` running the dist/index.js
|
|
110
|
+
// that ships beside the binary — there node-pty loads from the on-disk
|
|
111
|
+
// prebuild. We can't do `<binary> dist/index.js` (the standalone misparses a
|
|
112
|
+
// script arg as a subcommand), so a genuine `node` is required. Fall back to
|
|
113
|
+
// the binary itself only when no node / no dist is found (a bare standalone
|
|
114
|
+
// install with no Node — PTY may then be unavailable, but nothing else can
|
|
115
|
+
// load the addon anyway).
|
|
116
|
+
const node = (options.resolveNode ?? resolveNodeExecutable)();
|
|
117
|
+
const exists = options.fileExists ?? fs.existsSync;
|
|
118
|
+
const execPath = options.execPath ?? process.execPath;
|
|
119
|
+
if (node) {
|
|
120
|
+
try {
|
|
121
|
+
const distIndex = path.join(path.dirname(execPath), '..', 'index.js');
|
|
122
|
+
if (exists(distIndex)) {
|
|
123
|
+
return { bin: node, args: [distIndex, 'pty', '_server'] };
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch { }
|
|
127
|
+
}
|
|
128
|
+
return { bin: process.execPath, args: ['pty', '_server'] };
|
|
129
|
+
}
|
|
67
130
|
// Prefer the dist/index.js from the same installation as this code.
|
|
68
131
|
// This avoids version mismatch when a globally installed `agents` is older.
|
|
69
132
|
try {
|
|
@@ -86,6 +149,69 @@ function getServerSpawnArgs() {
|
|
|
86
149
|
catch { }
|
|
87
150
|
return { bin: 'agents', args: ['pty', '_server'] };
|
|
88
151
|
}
|
|
152
|
+
export function isBunStandaloneExecutable(moduleUrl = import.meta.url) {
|
|
153
|
+
return BUN_VIRTUAL_ROOT.test(moduleUrl);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Locate a real `node` on PATH for running the sidecar's dist/index.js from a
|
|
157
|
+
* Bun standalone binary (which cannot itself load native addons). Returns
|
|
158
|
+
* undefined when no node is found — the caller then falls back to the binary.
|
|
159
|
+
*/
|
|
160
|
+
function resolveNodeExecutable() {
|
|
161
|
+
try {
|
|
162
|
+
const lookup = IS_WINDOWS ? 'where node' : 'which node';
|
|
163
|
+
const node = execSync(lookup, { encoding: 'utf-8' }).split(/\r?\n/)[0].trim();
|
|
164
|
+
if (node && fs.existsSync(node))
|
|
165
|
+
return node;
|
|
166
|
+
}
|
|
167
|
+
catch { }
|
|
168
|
+
return undefined;
|
|
169
|
+
}
|
|
170
|
+
export function readRecentLogLines(logPath, maxLines = 20) {
|
|
171
|
+
try {
|
|
172
|
+
if (!fs.existsSync(logPath))
|
|
173
|
+
return [];
|
|
174
|
+
return fs.readFileSync(logPath, 'utf-8')
|
|
175
|
+
.split(/\r?\n/)
|
|
176
|
+
.filter(line => line.trim().length > 0)
|
|
177
|
+
.slice(-maxLines);
|
|
178
|
+
}
|
|
179
|
+
catch (err) {
|
|
180
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
181
|
+
return [`Unable to read PTY server log: ${message}`];
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
export function buildPtyStartFailureMessage(details) {
|
|
185
|
+
const lines = [
|
|
186
|
+
`PTY server failed to start within ${Math.round(details.timeoutMs / 1000)} seconds.`,
|
|
187
|
+
`Spawned: ${formatCommand(details.spawn)}`,
|
|
188
|
+
];
|
|
189
|
+
if (details.spawnError) {
|
|
190
|
+
lines.push(`Spawn error: ${details.spawnError.message}`);
|
|
191
|
+
}
|
|
192
|
+
if (details.exit) {
|
|
193
|
+
const reason = details.exit.signal
|
|
194
|
+
? `signal ${details.exit.signal}`
|
|
195
|
+
: `code ${details.exit.code ?? 'unknown'}`;
|
|
196
|
+
lines.push(`PTY server process exited with ${reason} before listening.`);
|
|
197
|
+
}
|
|
198
|
+
if (details.lastReadinessError) {
|
|
199
|
+
lines.push(`Last readiness error: ${details.lastReadinessError.message}`);
|
|
200
|
+
}
|
|
201
|
+
lines.push(`Log: ${details.logPath}`);
|
|
202
|
+
const logLines = readRecentLogLines(details.logPath);
|
|
203
|
+
if (logLines.length > 0) {
|
|
204
|
+
lines.push('Recent PTY server log:');
|
|
205
|
+
lines.push(...logLines.map(line => ` ${line}`));
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
lines.push('No PTY server log output was written.');
|
|
209
|
+
}
|
|
210
|
+
return lines.join('\n');
|
|
211
|
+
}
|
|
212
|
+
function formatCommand(spawnArgs) {
|
|
213
|
+
return [spawnArgs.bin, ...spawnArgs.args].map((part) => JSON.stringify(part)).join(' ');
|
|
214
|
+
}
|
|
89
215
|
/**
|
|
90
216
|
* Send a JSON request over the unix socket and return the parsed response.
|
|
91
217
|
*/
|
package/dist/lib/refresh.js
CHANGED
|
@@ -16,7 +16,7 @@ import { capableAgents } from './capabilities.js';
|
|
|
16
16
|
import { AGENTS, ALL_AGENT_IDS, getAllCliStates, registerMcpToTargets, agentLabel, } from './agents.js';
|
|
17
17
|
import { readManifest, MANIFEST_FILENAME } from './manifest.js';
|
|
18
18
|
import { getUserAgentsDir } from './state.js';
|
|
19
|
-
import { installVersion, listInstalledVersions, getGlobalDefault, setGlobalDefault, getVersionHomePath, syncResourcesToVersion, getAvailableResources, getActuallySyncedResources, getNewResources, getProjectOnlyResources, hasNewResources, promptNewResourceSelection, promptResourceSelection, resolveConfiguredAgentTargets, } from './versions.js';
|
|
19
|
+
import { installVersion, listInstalledVersions, isVersionIsolated, getGlobalDefault, setGlobalDefault, getVersionHomePath, syncResourcesToVersion, getAvailableResources, getActuallySyncedResources, getNewResources, getProjectOnlyResources, hasNewResources, promptNewResourceSelection, promptResourceSelection, resolveConfiguredAgentTargets, } from './versions.js';
|
|
20
20
|
import { listCliStatus, installCli, describeMethod, describeCheck, selectInstallMethod, } from './cli-resources.js';
|
|
21
21
|
import { ensureShimCurrent, isShimsInPath, addShimsToPath, getPathSetupInstructions, switchConfigSymlink, switchHomeFileSymlinks, } from './shims.js';
|
|
22
22
|
import { parseHookManifest, registerHooksToSettings } from './hooks.js';
|
|
@@ -133,13 +133,9 @@ export async function refresh(options = {}) {
|
|
|
133
133
|
actuallySynced.plugins.length > 0;
|
|
134
134
|
try {
|
|
135
135
|
let selection;
|
|
136
|
+
let forceFullSync = false;
|
|
136
137
|
if (skipPrompts) {
|
|
137
|
-
|
|
138
|
-
selection = {
|
|
139
|
-
commands: 'all', skills: 'all', hooks: 'all', memory: 'all',
|
|
140
|
-
mcp: 'all', permissions: 'all', subagents: 'all', plugins: 'all',
|
|
141
|
-
};
|
|
142
|
-
}
|
|
138
|
+
forceFullSync = true;
|
|
143
139
|
}
|
|
144
140
|
else if (!hasAnySynced) {
|
|
145
141
|
console.log(chalk.yellow(`\n${agentLabel(agentId)}@${defaultVer} has no synced resources.`));
|
|
@@ -153,8 +149,11 @@ export async function refresh(options = {}) {
|
|
|
153
149
|
if (userSelection)
|
|
154
150
|
selection = userSelection;
|
|
155
151
|
}
|
|
156
|
-
|
|
157
|
-
|
|
152
|
+
else {
|
|
153
|
+
forceFullSync = true;
|
|
154
|
+
}
|
|
155
|
+
if (forceFullSync || (selection && Object.keys(selection).length > 0)) {
|
|
156
|
+
const syncResult = syncResourcesToVersion(agentId, defaultVer, selection, forceFullSync ? { force: true } : undefined);
|
|
158
157
|
const synced = [];
|
|
159
158
|
if (syncResult.commands)
|
|
160
159
|
synced.push('commands');
|
|
@@ -231,7 +230,12 @@ export async function refresh(options = {}) {
|
|
|
231
230
|
}
|
|
232
231
|
const selectedVersions = [];
|
|
233
232
|
for (const agentId of agentsNeedingDefault) {
|
|
234
|
-
|
|
233
|
+
// Isolated copies are not default-eligible — `agents use` refuses them and
|
|
234
|
+
// setting one here would also switch the config symlink, pointing the user's
|
|
235
|
+
// real ~/.<agent> at an isolated home. Keep them out of the picker entirely.
|
|
236
|
+
const versions = listInstalledVersions(agentId).filter((v) => !isVersionIsolated(agentId, v));
|
|
237
|
+
if (versions.length === 0)
|
|
238
|
+
continue;
|
|
235
239
|
const agent = AGENTS[agentId];
|
|
236
240
|
const shouldSwitch = await select({
|
|
237
241
|
message: `${agentLabel(agent.id)} has no default version. Set one now?`,
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Top-level resource profiles.
|
|
3
|
+
*
|
|
4
|
+
* These are distinct from model-provider run profiles in lib/profiles.ts. A
|
|
5
|
+
* resource profile is a global mode switch stored in agents.yaml that filters
|
|
6
|
+
* the resolved DotAgents resource view and secrets bundles.
|
|
7
|
+
*/
|
|
8
|
+
import type { ResourceProfilePreset } from './types.js';
|
|
9
|
+
export type ProfiledResourceKind = 'commands' | 'skills' | 'hooks' | 'subagents' | 'plugins' | 'workflows' | 'permissions' | 'mcp' | 'memory' | 'secrets';
|
|
10
|
+
export type PatternedProfileKind = Exclude<ProfiledResourceKind, 'memory' | 'secrets'>;
|
|
11
|
+
export interface ActiveResourceProfile {
|
|
12
|
+
name: string;
|
|
13
|
+
preset: ResourceProfilePreset;
|
|
14
|
+
}
|
|
15
|
+
export declare function validateResourceProfileName(name: string): void;
|
|
16
|
+
export declare function listResourceProfileNames(): string[];
|
|
17
|
+
export declare function getResourceProfilePreset(name: string): ResourceProfilePreset | null;
|
|
18
|
+
export declare function getActiveResourceProfileName(): string | null;
|
|
19
|
+
export declare function getActiveResourceProfile(): ActiveResourceProfile | null;
|
|
20
|
+
export declare function upsertResourceProfilePreset(name: string, preset: ResourceProfilePreset): void;
|
|
21
|
+
export declare function setActiveResourceProfile(name: string | null): void;
|
|
22
|
+
export declare function patternedProfileKinds(): PatternedProfileKind[];
|
|
23
|
+
export declare function activeRulesPreset(): string | null;
|
|
24
|
+
export declare function filterNamesForActiveResourceProfile(kind: ProfiledResourceKind, names: string[], sourceMap?: Map<string, string>): string[];
|
|
25
|
+
export declare function isNameActiveInResourceProfile(kind: ProfiledResourceKind, name: string, source?: string): boolean;
|
|
26
|
+
export declare function assertNameActiveInResourceProfile(kind: ProfiledResourceKind, name: string): void;
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Top-level resource profiles.
|
|
3
|
+
*
|
|
4
|
+
* These are distinct from model-provider run profiles in lib/profiles.ts. A
|
|
5
|
+
* resource profile is a global mode switch stored in agents.yaml that filters
|
|
6
|
+
* the resolved DotAgents resource view and secrets bundles.
|
|
7
|
+
*/
|
|
8
|
+
import { readMeta, updateMeta } from './state.js';
|
|
9
|
+
import { brandProfileName } from './brand.js';
|
|
10
|
+
const PROFILE_NAME_PATTERN = /^[a-z0-9][a-z0-9-_]{0,48}$/i;
|
|
11
|
+
const PATTERNED_KINDS = [
|
|
12
|
+
'commands',
|
|
13
|
+
'skills',
|
|
14
|
+
'hooks',
|
|
15
|
+
'subagents',
|
|
16
|
+
'plugins',
|
|
17
|
+
'workflows',
|
|
18
|
+
'permissions',
|
|
19
|
+
'mcp',
|
|
20
|
+
];
|
|
21
|
+
export function validateResourceProfileName(name) {
|
|
22
|
+
if (!PROFILE_NAME_PATTERN.test(name)) {
|
|
23
|
+
throw new Error(`Invalid profile name '${name}'. Use letters, digits, dash, underscore (max 48 chars).`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export function listResourceProfileNames() {
|
|
27
|
+
return Object.keys(readMeta().profiles?.presets ?? {}).sort((a, b) => a.localeCompare(b));
|
|
28
|
+
}
|
|
29
|
+
export function getResourceProfilePreset(name) {
|
|
30
|
+
validateResourceProfileName(name);
|
|
31
|
+
return readMeta().profiles?.presets?.[name] ?? null;
|
|
32
|
+
}
|
|
33
|
+
export function getActiveResourceProfileName() {
|
|
34
|
+
// A white-label brand pins its own profile; when running under a brand
|
|
35
|
+
// (AGENTS_BRAND set) that preset wins over the global `profiles.active`, so
|
|
36
|
+
// every resource filter that keys off the active profile becomes brand-scoped.
|
|
37
|
+
// See lib/brand.ts.
|
|
38
|
+
const branded = brandProfileName();
|
|
39
|
+
if (branded)
|
|
40
|
+
return branded;
|
|
41
|
+
return readMeta().profiles?.active ?? null;
|
|
42
|
+
}
|
|
43
|
+
export function getActiveResourceProfile() {
|
|
44
|
+
const meta = readMeta();
|
|
45
|
+
const name = getActiveResourceProfileName();
|
|
46
|
+
if (!name)
|
|
47
|
+
return null;
|
|
48
|
+
const preset = meta.profiles?.presets?.[name];
|
|
49
|
+
return preset ? { name, preset } : null;
|
|
50
|
+
}
|
|
51
|
+
export function upsertResourceProfilePreset(name, preset) {
|
|
52
|
+
validateResourceProfileName(name);
|
|
53
|
+
updateMeta((meta) => ({
|
|
54
|
+
...meta,
|
|
55
|
+
profiles: {
|
|
56
|
+
...meta.profiles,
|
|
57
|
+
presets: {
|
|
58
|
+
...(meta.profiles?.presets ?? {}),
|
|
59
|
+
[name]: preset,
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
}));
|
|
63
|
+
}
|
|
64
|
+
export function setActiveResourceProfile(name) {
|
|
65
|
+
if (name) {
|
|
66
|
+
validateResourceProfileName(name);
|
|
67
|
+
const preset = getResourceProfilePreset(name);
|
|
68
|
+
if (!preset)
|
|
69
|
+
throw new Error(`Profile '${name}' not found.`);
|
|
70
|
+
}
|
|
71
|
+
updateMeta((meta) => ({
|
|
72
|
+
...meta,
|
|
73
|
+
profiles: {
|
|
74
|
+
...meta.profiles,
|
|
75
|
+
active: name || undefined,
|
|
76
|
+
},
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
export function patternedProfileKinds() {
|
|
80
|
+
return [...PATTERNED_KINDS];
|
|
81
|
+
}
|
|
82
|
+
function plainPatternMatches(pattern, name) {
|
|
83
|
+
if (pattern === '*')
|
|
84
|
+
return true;
|
|
85
|
+
if (!pattern.includes('*'))
|
|
86
|
+
return pattern === name;
|
|
87
|
+
const escaped = pattern
|
|
88
|
+
.split('*')
|
|
89
|
+
.map((part) => part.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
|
|
90
|
+
.join('.*');
|
|
91
|
+
return new RegExp(`^${escaped}$`).test(name);
|
|
92
|
+
}
|
|
93
|
+
function filterByPlainPatterns(names, patterns) {
|
|
94
|
+
const include = patterns.filter((p) => !p.startsWith('!'));
|
|
95
|
+
const exclude = patterns.filter((p) => p.startsWith('!')).map((p) => p.slice(1));
|
|
96
|
+
return names.filter((name) => include.some((p) => plainPatternMatches(p, name)) &&
|
|
97
|
+
!exclude.some((p) => plainPatternMatches(p, name)));
|
|
98
|
+
}
|
|
99
|
+
function sourcePatternMatches(pattern, name, sourceMap) {
|
|
100
|
+
const colon = pattern.indexOf(':');
|
|
101
|
+
if (colon === -1)
|
|
102
|
+
return false;
|
|
103
|
+
const source = pattern.slice(0, colon);
|
|
104
|
+
const rawName = pattern.slice(colon + 1);
|
|
105
|
+
const actualSource = sourceMap.get(name);
|
|
106
|
+
if (actualSource !== source)
|
|
107
|
+
return false;
|
|
108
|
+
const patternNames = rawName === '*' ? ['*'] : rawName.split(',').map((n) => n.trim()).filter(Boolean);
|
|
109
|
+
return patternNames.some((patternName) => plainPatternMatches(patternName, name));
|
|
110
|
+
}
|
|
111
|
+
function expandProfilePatterns(names, patterns, sourceMap) {
|
|
112
|
+
if (sourceMap) {
|
|
113
|
+
const included = new Set();
|
|
114
|
+
const excluded = new Set();
|
|
115
|
+
for (const pattern of patterns) {
|
|
116
|
+
const negate = pattern.startsWith('!');
|
|
117
|
+
const raw = negate ? pattern.slice(1) : pattern;
|
|
118
|
+
const target = negate ? excluded : included;
|
|
119
|
+
const matches = raw.includes(':')
|
|
120
|
+
? names.filter((name) => sourcePatternMatches(raw, name, sourceMap))
|
|
121
|
+
: names.filter((name) => plainPatternMatches(raw, name));
|
|
122
|
+
for (const name of matches)
|
|
123
|
+
target.add(name);
|
|
124
|
+
}
|
|
125
|
+
return names.filter((name) => included.has(name) && !excluded.has(name));
|
|
126
|
+
}
|
|
127
|
+
return filterByPlainPatterns(names, patterns);
|
|
128
|
+
}
|
|
129
|
+
export function activeRulesPreset() {
|
|
130
|
+
const preset = getActiveResourceProfile()?.preset;
|
|
131
|
+
return preset?.rules ?? preset?.rulesPreset ?? null;
|
|
132
|
+
}
|
|
133
|
+
export function filterNamesForActiveResourceProfile(kind, names, sourceMap) {
|
|
134
|
+
const profile = getActiveResourceProfile();
|
|
135
|
+
if (!profile)
|
|
136
|
+
return names;
|
|
137
|
+
if (kind === 'memory') {
|
|
138
|
+
const rules = activeRulesPreset();
|
|
139
|
+
return rules ? names.filter((name) => name === rules) : names;
|
|
140
|
+
}
|
|
141
|
+
const patterns = profile.preset[kind];
|
|
142
|
+
if (!patterns)
|
|
143
|
+
return names;
|
|
144
|
+
return expandProfilePatterns(names, patterns, sourceMap);
|
|
145
|
+
}
|
|
146
|
+
export function isNameActiveInResourceProfile(kind, name, source) {
|
|
147
|
+
const sourceMap = source ? new Map([[name, source]]) : undefined;
|
|
148
|
+
return filterNamesForActiveResourceProfile(kind, [name], sourceMap).includes(name);
|
|
149
|
+
}
|
|
150
|
+
export function assertNameActiveInResourceProfile(kind, name) {
|
|
151
|
+
const active = getActiveResourceProfile();
|
|
152
|
+
if (!active)
|
|
153
|
+
return;
|
|
154
|
+
if (isNameActiveInResourceProfile(kind, name))
|
|
155
|
+
return;
|
|
156
|
+
throw new Error(`${kind === 'secrets' ? 'Secrets bundle' : 'Resource'} '${name}' is not active in profile '${active.name}'.`);
|
|
157
|
+
}
|
|
@@ -77,6 +77,12 @@ function getAgentConfigPath(agent, versionHome) {
|
|
|
77
77
|
return path.join(versionHome, '.kiro', 'settings', 'permissions.yaml');
|
|
78
78
|
case 'openclaw':
|
|
79
79
|
return path.join(versionHome, '.openclaw', 'openclaw.json');
|
|
80
|
+
case 'copilot':
|
|
81
|
+
return path.join(versionHome, '.copilot', 'permissions-config.json');
|
|
82
|
+
case 'forge':
|
|
83
|
+
return path.join(versionHome, '.forge', 'permissions.yaml');
|
|
84
|
+
case 'hermes':
|
|
85
|
+
return path.join(versionHome, '.hermes', 'config.yaml');
|
|
80
86
|
case 'goose':
|
|
81
87
|
return path.join(versionHome, '.config', 'goose', 'permission.yaml');
|
|
82
88
|
default:
|
|
@@ -173,7 +179,7 @@ export const PermissionsHandler = {
|
|
|
173
179
|
merged = mergePermissionSets(merged, r.item);
|
|
174
180
|
}
|
|
175
181
|
// Apply to the agent's config file
|
|
176
|
-
applyPermissionsToVersion(agent, merged, versionHome, true);
|
|
182
|
+
applyPermissionsToVersion(agent, merged, versionHome, true, cwd);
|
|
177
183
|
},
|
|
178
184
|
/**
|
|
179
185
|
* Permissions use YAML format.
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* - Union: All resources from all layers are combined
|
|
6
6
|
* - Override on name conflict: Higher layer wins (project > user > system)
|
|
7
7
|
*/
|
|
8
|
-
export type AgentId = 'claude' | 'codex' | 'gemini' | 'cursor' | 'opencode' | 'openclaw' | 'kiro' | 'goose' | 'antigravity' | 'grok' | 'kimi' | 'droid' | 'hermes' | 'forge';
|
|
8
|
+
export type AgentId = 'claude' | 'codex' | 'gemini' | 'cursor' | 'opencode' | 'openclaw' | 'copilot' | 'kiro' | 'goose' | 'antigravity' | 'grok' | 'kimi' | 'droid' | 'hermes' | 'forge';
|
|
9
9
|
export type Layer = 'system' | 'user' | 'project';
|
|
10
10
|
export type ResourceKind = 'command' | 'hook' | 'skill' | 'rule' | 'mcp' | 'permission' | 'subagent' | 'workflow' | 'memory';
|
|
11
11
|
/** A resolved resource with its origin layer. */
|
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' | 'cli' | 'permissions' | 'subagents' | 'profiles' | 'secrets';
|
|
8
|
+
export type ResourceKind = 'commands' | 'skills' | 'hooks' | 'rules' | 'mcp' | 'cli' | 'permissions' | 'subagents' | 'workflows' | 'profiles' | 'secrets';
|
|
9
9
|
/** A resource resolved with its origin. */
|
|
10
10
|
export interface ResolvedResource {
|
|
11
11
|
name: string;
|
package/dist/lib/resources.js
CHANGED
|
@@ -14,6 +14,27 @@ import { listMcpServerConfigs } from './mcp.js';
|
|
|
14
14
|
import { WorkflowsHandler } from './resources/workflows.js';
|
|
15
15
|
import { isCapable } from './capabilities.js';
|
|
16
16
|
import { getProjectAgentsDir, getUserAgentsDir, getSystemAgentsDir, getEnabledExtraRepos, } from './state.js';
|
|
17
|
+
import { isNameActiveInResourceProfile } from './resource-profiles.js';
|
|
18
|
+
function profiledKind(kind) {
|
|
19
|
+
switch (kind) {
|
|
20
|
+
case 'commands':
|
|
21
|
+
case 'skills':
|
|
22
|
+
case 'hooks':
|
|
23
|
+
case 'mcp':
|
|
24
|
+
case 'permissions':
|
|
25
|
+
case 'subagents':
|
|
26
|
+
case 'secrets':
|
|
27
|
+
return kind;
|
|
28
|
+
case 'rules':
|
|
29
|
+
return 'memory';
|
|
30
|
+
default:
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function resourceIsActive(kind, name, source) {
|
|
35
|
+
const activeKind = profiledKind(kind);
|
|
36
|
+
return activeKind ? isNameActiveInResourceProfile(activeKind, name, source) : true;
|
|
37
|
+
}
|
|
17
38
|
/**
|
|
18
39
|
* Resolve a single resource by kind + name using project > user > system precedence.
|
|
19
40
|
* For file-based resources the path ends in `.md`, `.yaml`, or `.yml` as appropriate.
|
|
@@ -36,13 +57,19 @@ export function resolveResource(kind, name, cwd) {
|
|
|
36
57
|
// Try exact name (for directories like skills/subagents)
|
|
37
58
|
const exactPath = path.join(dir, name);
|
|
38
59
|
if (fs.existsSync(exactPath)) {
|
|
39
|
-
|
|
60
|
+
if (resourceIsActive(kind, name, source)) {
|
|
61
|
+
return { name, path: exactPath, source };
|
|
62
|
+
}
|
|
63
|
+
continue;
|
|
40
64
|
}
|
|
41
65
|
// Try with common file extensions
|
|
42
66
|
for (const ext of ['.md', '.yaml', '.yml']) {
|
|
43
67
|
const withExt = exactPath + ext;
|
|
44
68
|
if (fs.existsSync(withExt)) {
|
|
45
|
-
|
|
69
|
+
if (resourceIsActive(kind, name, source)) {
|
|
70
|
+
return { name, path: withExt, source };
|
|
71
|
+
}
|
|
72
|
+
continue;
|
|
46
73
|
}
|
|
47
74
|
}
|
|
48
75
|
}
|
|
@@ -80,6 +107,8 @@ export function listResources(kind, cwd) {
|
|
|
80
107
|
const rawName = entry.name.replace(/\.(md|yaml|yml)$/, '');
|
|
81
108
|
if (seen.has(rawName))
|
|
82
109
|
continue;
|
|
110
|
+
if (!resourceIsActive(kind, rawName, source))
|
|
111
|
+
continue;
|
|
83
112
|
seen.add(rawName);
|
|
84
113
|
results.push({
|
|
85
114
|
name: rawName,
|
|
@@ -162,7 +191,7 @@ export function getAgentResources(agentId, options = {}) {
|
|
|
162
191
|
hooks.push({ name: hook.name, path: hook.path, scope: hook.scope });
|
|
163
192
|
}
|
|
164
193
|
}
|
|
165
|
-
// Workflows
|
|
194
|
+
// Workflows
|
|
166
195
|
const workflows = [];
|
|
167
196
|
if (isCapable(agentId, 'workflows')) {
|
|
168
197
|
for (const w of WorkflowsHandler.listAll(agentId, cwd)) {
|
package/dist/lib/routines.d.ts
CHANGED
|
@@ -41,6 +41,10 @@ export interface GithubJobTrigger {
|
|
|
41
41
|
repo?: string;
|
|
42
42
|
/** git branch (ref short name) — when set, only payloads for this branch match. */
|
|
43
43
|
branch?: string;
|
|
44
|
+
/** GitHub webhook action, e.g. `opened`, `synchronize`, or `labeled`. */
|
|
45
|
+
action?: string;
|
|
46
|
+
/** Required GitHub label name. For `pull_request.labeled`, this is the added label. */
|
|
47
|
+
label?: string;
|
|
44
48
|
}
|
|
45
49
|
export interface LinearJobTrigger {
|
|
46
50
|
type: 'linear_event';
|
|
@@ -184,6 +188,10 @@ export declare function checkJobDeviceEligibility(config: Pick<JobConfig, 'name'
|
|
|
184
188
|
* Higher layers shadow lower ones of the same name (first-seen wins): a project
|
|
185
189
|
* routine shadows a user routine, and a user routine shadows a built-in system
|
|
186
190
|
* routine (`~/.agents/.system/routines/`, shipped via gh:phnx-labs/.agents-system).
|
|
191
|
+
* When a same-name project routine wins for inspection, the user-layer
|
|
192
|
+
* `devices` allowlist is overlaid only if the project routine does not declare
|
|
193
|
+
* its own allowlist, so CWD project discovery cannot hide an operational fleet
|
|
194
|
+
* pin or erase a project-authored one.
|
|
187
195
|
* Project discovery is opt-in via `cwd`; the daemon (which calls `listJobs()`
|
|
188
196
|
* with no argument) sees user + system routines, so a built-in routine fires for
|
|
189
197
|
* every install unless the user overrides or disables it by name.
|
|
@@ -191,6 +199,9 @@ export declare function checkJobDeviceEligibility(config: Pick<JobConfig, 'name'
|
|
|
191
199
|
export declare function listJobs(cwd?: string): JobConfig[];
|
|
192
200
|
/**
|
|
193
201
|
* Read a single job config by name, checking project > user > system.
|
|
202
|
+
* Same-name project routines keep the user-layer `devices` allowlist only when
|
|
203
|
+
* the project routine does not declare its own allowlist, for the same reason
|
|
204
|
+
* as listJobs().
|
|
194
205
|
* Project discovery is opt-in via `cwd`; daemon callers pass no argument and
|
|
195
206
|
* resolve user + system routines (a user routine of the same name shadows a
|
|
196
207
|
* built-in system routine).
|