@phnx-labs/agents-cli 1.20.71 → 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 +325 -1
- 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/commands.js +2 -0
- 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 +39 -10
- package/dist/commands/mcp.js +2 -0
- 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/resource-view.d.ts +2 -0
- package/dist/commands/resource-view.js +8 -0
- 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.d.ts +6 -0
- package/dist/commands/sessions.js +26 -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/skills.js +2 -0
- package/dist/commands/ssh.d.ts +9 -0
- package/dist/commands/ssh.js +196 -26
- package/dist/commands/status.js +8 -2
- package/dist/commands/subagents.js +3 -1
- package/dist/commands/sync.js +1 -1
- package/dist/commands/teams.js +11 -2
- package/dist/commands/uninstall.d.ts +11 -0
- package/dist/commands/uninstall.js +158 -0
- package/dist/commands/utils.d.ts +29 -0
- package/dist/commands/utils.js +24 -0
- package/dist/commands/versions.js +120 -11
- package/dist/index.js +83 -12
- 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 +14 -5
- package/dist/lib/daemon.js +52 -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/logs.d.ts +12 -0
- package/dist/lib/hosts/logs.js +18 -0
- 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 +36 -0
- package/dist/lib/shims.js +120 -16
- 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 +7 -1
- package/dist/lib/startup/command-registry.js +19 -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/uninstall.d.ts +84 -0
- package/dist/lib/uninstall.js +347 -0
- package/dist/lib/usage.d.ts +13 -1
- package/dist/lib/usage.js +32 -14
- package/dist/lib/version.d.ts +40 -0
- package/dist/lib/version.js +94 -16
- package/dist/lib/versions.d.ts +27 -0
- package/dist/lib/versions.js +248 -79
- 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
|
@@ -104,6 +104,22 @@ export declare function convertToClaudeFormat(set: PermissionSet): ClaudePermiss
|
|
|
104
104
|
* (`~/.cursor/cli-config.json` permissions.allow/deny).
|
|
105
105
|
*/
|
|
106
106
|
export declare function convertToCursorFormat(set: PermissionSet): CursorPermissions;
|
|
107
|
+
type CopilotToolApproval = {
|
|
108
|
+
kind: 'commands';
|
|
109
|
+
commandIdentifiers: string[];
|
|
110
|
+
} | {
|
|
111
|
+
kind: 'read' | 'write';
|
|
112
|
+
} | {
|
|
113
|
+
kind: 'mcp';
|
|
114
|
+
serverName: string;
|
|
115
|
+
toolName: string | null;
|
|
116
|
+
};
|
|
117
|
+
export interface CopilotPermissionsConfig {
|
|
118
|
+
locations: Record<string, {
|
|
119
|
+
tool_approvals?: CopilotToolApproval[];
|
|
120
|
+
allowed_directories?: string[];
|
|
121
|
+
}>;
|
|
122
|
+
}
|
|
107
123
|
/**
|
|
108
124
|
* Convert canonical permission set to Gemini format.
|
|
109
125
|
* Gemini reads tool allow/deny lists from settings.json under
|
|
@@ -117,6 +133,7 @@ export declare function convertToGeminiFormat(set: PermissionSet): {
|
|
|
117
133
|
exclude?: string[];
|
|
118
134
|
};
|
|
119
135
|
};
|
|
136
|
+
export declare function convertToCopilotFormat(set: PermissionSet, location: string): CopilotPermissionsConfig;
|
|
120
137
|
/** Convert canonical Bash rules into Droid's command arrays. */
|
|
121
138
|
export declare function convertToDroidFormat(set: PermissionSet): {
|
|
122
139
|
commandAllowlist: string[];
|
|
@@ -137,6 +154,31 @@ export declare function convertToOpenClawFormat(set: PermissionSet): {
|
|
|
137
154
|
alsoAllow: string[];
|
|
138
155
|
deny: string[];
|
|
139
156
|
};
|
|
157
|
+
export type ForgePermission = 'allow' | 'deny' | 'confirm';
|
|
158
|
+
export type ForgeOperation = 'read' | 'write' | 'command' | 'url';
|
|
159
|
+
export type ForgePolicy = {
|
|
160
|
+
permission: ForgePermission;
|
|
161
|
+
rule: Partial<Record<ForgeOperation, string>>;
|
|
162
|
+
};
|
|
163
|
+
/**
|
|
164
|
+
* Convert canonical permissions to ForgeCode's permissions.yaml policy list.
|
|
165
|
+
*
|
|
166
|
+
* ForgeCode gates built-in tools by operation family (`read`, `write`,
|
|
167
|
+
* `command`, `url`) plus glob patterns, optionally scoped by `dir`. The policy
|
|
168
|
+
* file is ignored unless `.forge.toml` has `restricted = true`, and MCP tools
|
|
169
|
+
* bypass permissions.yaml entirely; agents-cli therefore maps only canonical
|
|
170
|
+
* built-in allow/deny rules and does not try to express per-named-MCP-tool
|
|
171
|
+
* grants.
|
|
172
|
+
*/
|
|
173
|
+
export declare function convertToForgeFormat(set: PermissionSet): {
|
|
174
|
+
policies: ForgePolicy[];
|
|
175
|
+
};
|
|
176
|
+
export declare function convertToHermesFormat(set: PermissionSet): {
|
|
177
|
+
command_allowlist: string[];
|
|
178
|
+
approvals: {
|
|
179
|
+
deny: string[];
|
|
180
|
+
};
|
|
181
|
+
};
|
|
140
182
|
/**
|
|
141
183
|
* Convert canonical permission set to Antigravity format.
|
|
142
184
|
* Antigravity reads ~/.gemini/antigravity-cli/settings.json with
|
|
@@ -252,7 +294,7 @@ export declare function openCodeConfigPath(scope: 'user' | 'project', cwd?: stri
|
|
|
252
294
|
* Apply a permission set to a specific version's home directory.
|
|
253
295
|
* This writes to {versionHome}/.{agent}/settings.json (or equivalent).
|
|
254
296
|
*/
|
|
255
|
-
export declare function applyPermissionsToVersion(agentId: AgentId, set: PermissionSet, versionHome: string, merge?: boolean): {
|
|
297
|
+
export declare function applyPermissionsToVersion(agentId: AgentId, set: PermissionSet, versionHome: string, merge?: boolean, cwd?: string): {
|
|
256
298
|
success: boolean;
|
|
257
299
|
error?: string;
|
|
258
300
|
};
|
|
@@ -302,3 +344,4 @@ export declare function saveDefaultPermissionSet(set: PermissionSet): {
|
|
|
302
344
|
success: boolean;
|
|
303
345
|
error?: string;
|
|
304
346
|
};
|
|
347
|
+
export {};
|
package/dist/lib/permissions.js
CHANGED
|
@@ -433,14 +433,27 @@ export function removePermissionSet(name) {
|
|
|
433
433
|
// ============================================================================
|
|
434
434
|
// Agent-specific converters
|
|
435
435
|
// ============================================================================
|
|
436
|
+
/**
|
|
437
|
+
* Map a canonical rule to Claude settings.json syntax.
|
|
438
|
+
* Claude's file-permission checks only match Edit(path) rules — an Edit rule
|
|
439
|
+
* covers every file-editing tool (Write, Edit, NotebookEdit) — and Claude
|
|
440
|
+
* warns at startup about unmatched Write(path) rules. Canonical Write(path)
|
|
441
|
+
* stays for other agents' converters; for Claude it is emitted as Edit(path).
|
|
442
|
+
*/
|
|
443
|
+
function canonicalToClaudeRule(perm) {
|
|
444
|
+
if (perm.startsWith('Write(')) {
|
|
445
|
+
return perm.replace(/^Write/, 'Edit');
|
|
446
|
+
}
|
|
447
|
+
return perm;
|
|
448
|
+
}
|
|
436
449
|
/**
|
|
437
450
|
* Convert canonical permission set to Claude format.
|
|
438
451
|
* Claude uses: { permissions: { allow: ["Bash(*)", "Read(**)"], deny: [] } }
|
|
439
452
|
*/
|
|
440
453
|
export function convertToClaudeFormat(set) {
|
|
441
454
|
const permissions = {
|
|
442
|
-
allow: [...set.allow],
|
|
443
|
-
deny: set.deny ? [...set.deny] : [],
|
|
455
|
+
allow: [...new Set(set.allow.map(canonicalToClaudeRule))],
|
|
456
|
+
deny: set.deny ? [...new Set(set.deny.map(canonicalToClaudeRule))] : [],
|
|
444
457
|
};
|
|
445
458
|
if (set.additionalDirectories?.length) {
|
|
446
459
|
permissions.additionalDirectories = [...set.additionalDirectories];
|
|
@@ -540,6 +553,89 @@ function normalizeBashPattern(pattern) {
|
|
|
540
553
|
return pattern.slice(0, -2) + ' *';
|
|
541
554
|
return pattern;
|
|
542
555
|
}
|
|
556
|
+
function canonicalBashToCopilotCommandIdentifier(pattern) {
|
|
557
|
+
if (pattern === '*' || pattern === '**')
|
|
558
|
+
return null;
|
|
559
|
+
if (pattern.endsWith(':*'))
|
|
560
|
+
return pattern;
|
|
561
|
+
if (pattern.endsWith(' *'))
|
|
562
|
+
return `${pattern.slice(0, -2)}:*`;
|
|
563
|
+
return pattern;
|
|
564
|
+
}
|
|
565
|
+
function canonicalToCopilotMcpApproval(parsed) {
|
|
566
|
+
const parts = parsed.pattern.split(/[.:/]/).filter(Boolean);
|
|
567
|
+
if (parts.length === 0)
|
|
568
|
+
return null;
|
|
569
|
+
const [serverName, toolName] = parts;
|
|
570
|
+
return { kind: 'mcp', serverName, toolName: toolName ?? null };
|
|
571
|
+
}
|
|
572
|
+
function canonicalToCopilotApproval(permission) {
|
|
573
|
+
const parsed = parseCanonicalPattern(permission);
|
|
574
|
+
if (!parsed)
|
|
575
|
+
return null;
|
|
576
|
+
if (parsed.tool === 'bash') {
|
|
577
|
+
const identifier = canonicalBashToCopilotCommandIdentifier(parsed.pattern);
|
|
578
|
+
return identifier ? { kind: 'commands', commandIdentifiers: [identifier] } : null;
|
|
579
|
+
}
|
|
580
|
+
if ((parsed.tool === 'write' || parsed.tool === 'edit') && (parsed.pattern === '*' || parsed.pattern === '**')) {
|
|
581
|
+
return { kind: 'write' };
|
|
582
|
+
}
|
|
583
|
+
if (parsed.tool === 'read' && (parsed.pattern === '*' || parsed.pattern === '**')) {
|
|
584
|
+
return { kind: 'read' };
|
|
585
|
+
}
|
|
586
|
+
if (parsed.tool === 'mcp') {
|
|
587
|
+
return canonicalToCopilotMcpApproval(parsed);
|
|
588
|
+
}
|
|
589
|
+
return null;
|
|
590
|
+
}
|
|
591
|
+
function mergeCopilotApprovals(existing, incoming) {
|
|
592
|
+
const byKey = new Map();
|
|
593
|
+
const add = (approval) => {
|
|
594
|
+
if (approval.kind === 'commands') {
|
|
595
|
+
const current = byKey.get('commands');
|
|
596
|
+
const commandIdentifiers = new Set([...(current?.commandIdentifiers ?? []), ...approval.commandIdentifiers]);
|
|
597
|
+
byKey.set('commands', { kind: 'commands', commandIdentifiers: Array.from(commandIdentifiers).sort() });
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
byKey.set(JSON.stringify(approval), approval);
|
|
601
|
+
};
|
|
602
|
+
for (const approval of existing) {
|
|
603
|
+
if (!approval || typeof approval !== 'object' || Array.isArray(approval))
|
|
604
|
+
continue;
|
|
605
|
+
const record = approval;
|
|
606
|
+
if (record.kind === 'commands' && Array.isArray(record.commandIdentifiers)) {
|
|
607
|
+
add({ kind: 'commands', commandIdentifiers: record.commandIdentifiers.filter((v) => typeof v === 'string') });
|
|
608
|
+
}
|
|
609
|
+
else if (record.kind === 'read' || record.kind === 'write') {
|
|
610
|
+
add({ kind: record.kind });
|
|
611
|
+
}
|
|
612
|
+
else if (record.kind === 'mcp' && typeof record.serverName === 'string' && (typeof record.toolName === 'string' || record.toolName === null)) {
|
|
613
|
+
add({ kind: 'mcp', serverName: record.serverName, toolName: record.toolName });
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
for (const approval of incoming)
|
|
617
|
+
add(approval);
|
|
618
|
+
return Array.from(byKey.values());
|
|
619
|
+
}
|
|
620
|
+
export function convertToCopilotFormat(set, location) {
|
|
621
|
+
const approvals = [];
|
|
622
|
+
for (const permission of set.allow) {
|
|
623
|
+
const approval = canonicalToCopilotApproval(permission);
|
|
624
|
+
if (approval)
|
|
625
|
+
approvals.push(approval);
|
|
626
|
+
}
|
|
627
|
+
const allowedDirectories = (set.additionalDirectories ?? [])
|
|
628
|
+
.filter((dir) => dir.trim().length > 0)
|
|
629
|
+
.map((dir) => path.isAbsolute(dir) ? dir : path.resolve(location, dir));
|
|
630
|
+
return {
|
|
631
|
+
locations: {
|
|
632
|
+
[location]: {
|
|
633
|
+
...(approvals.length > 0 ? { tool_approvals: mergeCopilotApprovals([], approvals) } : {}),
|
|
634
|
+
...(allowedDirectories.length > 0 ? { allowed_directories: Array.from(new Set(allowedDirectories)).sort() } : {}),
|
|
635
|
+
},
|
|
636
|
+
},
|
|
637
|
+
};
|
|
638
|
+
}
|
|
543
639
|
/** Convert canonical Bash rules into Droid's command arrays. */
|
|
544
640
|
export function convertToDroidFormat(set) {
|
|
545
641
|
const commands = (permissions) => {
|
|
@@ -614,6 +710,83 @@ export function convertToOpenClawFormat(set) {
|
|
|
614
710
|
deny: map(set.deny ?? []),
|
|
615
711
|
};
|
|
616
712
|
}
|
|
713
|
+
const FORGE_OPERATION_BY_CANONICAL = {
|
|
714
|
+
bash: 'command',
|
|
715
|
+
read: 'read',
|
|
716
|
+
grep: 'read',
|
|
717
|
+
glob: 'read',
|
|
718
|
+
write: 'write',
|
|
719
|
+
edit: 'write',
|
|
720
|
+
notebookedit: 'write',
|
|
721
|
+
webfetch: 'url',
|
|
722
|
+
websearch: 'url',
|
|
723
|
+
};
|
|
724
|
+
function canonicalToForgePolicy(perm, permission) {
|
|
725
|
+
if (BLANKET_BASH_FORMS.has(perm)) {
|
|
726
|
+
return { permission, rule: { command: '*' } };
|
|
727
|
+
}
|
|
728
|
+
const parsed = parseCanonicalPattern(perm);
|
|
729
|
+
if (!parsed)
|
|
730
|
+
return null;
|
|
731
|
+
const operation = FORGE_OPERATION_BY_CANONICAL[parsed.tool];
|
|
732
|
+
if (!operation)
|
|
733
|
+
return null;
|
|
734
|
+
const pattern = parsed.tool === 'bash'
|
|
735
|
+
? normalizeBashPattern(parsed.pattern)
|
|
736
|
+
: (parsed.tool === 'webfetch' || parsed.tool === 'websearch') && parsed.pattern.startsWith('domain:')
|
|
737
|
+
? `${parsed.pattern.slice('domain:'.length)}*`
|
|
738
|
+
: parsed.pattern === '**'
|
|
739
|
+
? '**/*'
|
|
740
|
+
: parsed.pattern;
|
|
741
|
+
return { permission, rule: { [operation]: pattern } };
|
|
742
|
+
}
|
|
743
|
+
/**
|
|
744
|
+
* Convert canonical permissions to ForgeCode's permissions.yaml policy list.
|
|
745
|
+
*
|
|
746
|
+
* ForgeCode gates built-in tools by operation family (`read`, `write`,
|
|
747
|
+
* `command`, `url`) plus glob patterns, optionally scoped by `dir`. The policy
|
|
748
|
+
* file is ignored unless `.forge.toml` has `restricted = true`, and MCP tools
|
|
749
|
+
* bypass permissions.yaml entirely; agents-cli therefore maps only canonical
|
|
750
|
+
* built-in allow/deny rules and does not try to express per-named-MCP-tool
|
|
751
|
+
* grants.
|
|
752
|
+
*/
|
|
753
|
+
export function convertToForgeFormat(set) {
|
|
754
|
+
const policies = [];
|
|
755
|
+
const seen = new Set();
|
|
756
|
+
const add = (policy) => {
|
|
757
|
+
if (!policy)
|
|
758
|
+
return;
|
|
759
|
+
const key = `${policy.permission}|${JSON.stringify(policy.rule)}`;
|
|
760
|
+
if (seen.has(key))
|
|
761
|
+
return;
|
|
762
|
+
seen.add(key);
|
|
763
|
+
policies.push(policy);
|
|
764
|
+
};
|
|
765
|
+
for (const perm of set.allow)
|
|
766
|
+
add(canonicalToForgePolicy(perm, 'allow'));
|
|
767
|
+
for (const perm of set.deny ?? [])
|
|
768
|
+
add(canonicalToForgePolicy(perm, 'deny'));
|
|
769
|
+
return { policies };
|
|
770
|
+
}
|
|
771
|
+
export function convertToHermesFormat(set) {
|
|
772
|
+
const commands = (permissions) => {
|
|
773
|
+
const out = new Set();
|
|
774
|
+
for (const perm of permissions) {
|
|
775
|
+
if (BLANKET_BASH_FORMS.has(perm)) {
|
|
776
|
+
out.add('*');
|
|
777
|
+
continue;
|
|
778
|
+
}
|
|
779
|
+
const parsed = parseCanonicalPattern(perm);
|
|
780
|
+
if (parsed?.tool === 'bash')
|
|
781
|
+
out.add(normalizeBashPattern(parsed.pattern));
|
|
782
|
+
}
|
|
783
|
+
return Array.from(out).sort();
|
|
784
|
+
};
|
|
785
|
+
return {
|
|
786
|
+
command_allowlist: commands(set.allow),
|
|
787
|
+
approvals: { deny: commands(set.deny ?? []) },
|
|
788
|
+
};
|
|
789
|
+
}
|
|
617
790
|
/**
|
|
618
791
|
* Convert canonical permission set to Antigravity format.
|
|
619
792
|
* Antigravity reads ~/.gemini/antigravity-cli/settings.json with
|
|
@@ -1144,8 +1317,9 @@ export function applyClaudePermissions(set, scope = 'user', cwd, merge = true) {
|
|
|
1144
1317
|
const newPermissions = convertToClaudeFormat(set);
|
|
1145
1318
|
if (merge && config.permissions) {
|
|
1146
1319
|
const existing = config.permissions;
|
|
1147
|
-
|
|
1148
|
-
const
|
|
1320
|
+
// Rewrite stale Write(path) rules already installed in settings.json too.
|
|
1321
|
+
const mergedAllow = new Set([...(existing.allow || []).map(canonicalToClaudeRule), ...newPermissions.permissions.allow]);
|
|
1322
|
+
const mergedDeny = new Set([...(existing.deny || []).map(canonicalToClaudeRule), ...newPermissions.permissions.deny]);
|
|
1149
1323
|
config.permissions = {
|
|
1150
1324
|
allow: [...mergedAllow],
|
|
1151
1325
|
deny: [...mergedDeny],
|
|
@@ -1292,7 +1466,7 @@ function applyPermissionsToAgent(agentId, set, scope = 'user', cwd, merge = true
|
|
|
1292
1466
|
* Apply a permission set to a specific version's home directory.
|
|
1293
1467
|
* This writes to {versionHome}/.{agent}/settings.json (or equivalent).
|
|
1294
1468
|
*/
|
|
1295
|
-
export function applyPermissionsToVersion(agentId, set, versionHome, merge = true) {
|
|
1469
|
+
export function applyPermissionsToVersion(agentId, set, versionHome, merge = true, cwd) {
|
|
1296
1470
|
const configDir = path.join(versionHome, agentConfigDirName(agentId));
|
|
1297
1471
|
try {
|
|
1298
1472
|
fs.mkdirSync(configDir, { recursive: true });
|
|
@@ -1305,8 +1479,9 @@ export function applyPermissionsToVersion(agentId, set, versionHome, merge = tru
|
|
|
1305
1479
|
const newPermissions = convertToClaudeFormat(set);
|
|
1306
1480
|
if (merge && config.permissions) {
|
|
1307
1481
|
const existing = config.permissions;
|
|
1308
|
-
|
|
1309
|
-
const
|
|
1482
|
+
// Rewrite stale Write(path) rules already installed in settings.json too.
|
|
1483
|
+
const mergedAllow = new Set([...(existing.allow || []).map(canonicalToClaudeRule), ...newPermissions.permissions.allow]);
|
|
1484
|
+
const mergedDeny = new Set([...(existing.deny || []).map(canonicalToClaudeRule), ...newPermissions.permissions.deny]);
|
|
1310
1485
|
const mergedDirs = new Set([...(existing.additionalDirectories || []), ...(newPermissions.permissions.additionalDirectories || [])]);
|
|
1311
1486
|
const perms = {
|
|
1312
1487
|
allow: [...mergedAllow],
|
|
@@ -1593,6 +1768,50 @@ export function applyPermissionsToVersion(agentId, set, versionHome, merge = tru
|
|
|
1593
1768
|
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
1594
1769
|
return { success: true };
|
|
1595
1770
|
}
|
|
1771
|
+
if (agentId === 'copilot') {
|
|
1772
|
+
const configPath = path.join(versionHome, '.copilot', 'permissions-config.json');
|
|
1773
|
+
let config = { locations: {} };
|
|
1774
|
+
if (fs.existsSync(configPath)) {
|
|
1775
|
+
config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
1776
|
+
}
|
|
1777
|
+
const location = path.resolve(cwd ?? process.cwd());
|
|
1778
|
+
const converted = convertToCopilotFormat(set, location);
|
|
1779
|
+
const incoming = converted.locations[location]?.tool_approvals ?? [];
|
|
1780
|
+
const incomingDirectories = converted.locations[location]?.allowed_directories ?? [];
|
|
1781
|
+
if (merge && incoming.length === 0 && incomingDirectories.length === 0)
|
|
1782
|
+
return { success: true };
|
|
1783
|
+
const locations = (typeof config.locations === 'object' && config.locations !== null && !Array.isArray(config.locations))
|
|
1784
|
+
? config.locations
|
|
1785
|
+
: {};
|
|
1786
|
+
const existingLocation = (typeof locations[location] === 'object' && locations[location] !== null && !Array.isArray(locations[location]))
|
|
1787
|
+
? locations[location]
|
|
1788
|
+
: {};
|
|
1789
|
+
const existingApprovals = Array.isArray(existingLocation.tool_approvals) ? existingLocation.tool_approvals : [];
|
|
1790
|
+
const nextApprovals = merge ? mergeCopilotApprovals(existingApprovals, incoming) : incoming;
|
|
1791
|
+
const existingDirectories = Array.isArray(existingLocation.allowed_directories)
|
|
1792
|
+
? existingLocation.allowed_directories.filter((v) => typeof v === 'string')
|
|
1793
|
+
: [];
|
|
1794
|
+
const nextDirectories = merge
|
|
1795
|
+
? Array.from(new Set([...existingDirectories, ...incomingDirectories])).sort()
|
|
1796
|
+
: incomingDirectories;
|
|
1797
|
+
const nextLocation = { ...existingLocation };
|
|
1798
|
+
if (nextApprovals.length > 0)
|
|
1799
|
+
nextLocation.tool_approvals = nextApprovals;
|
|
1800
|
+
else
|
|
1801
|
+
delete nextLocation.tool_approvals;
|
|
1802
|
+
if (nextDirectories.length > 0)
|
|
1803
|
+
nextLocation.allowed_directories = nextDirectories;
|
|
1804
|
+
else
|
|
1805
|
+
delete nextLocation.allowed_directories;
|
|
1806
|
+
if (Object.keys(nextLocation).length > 0)
|
|
1807
|
+
locations[location] = nextLocation;
|
|
1808
|
+
else
|
|
1809
|
+
delete locations[location];
|
|
1810
|
+
config.locations = locations;
|
|
1811
|
+
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
1812
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
1813
|
+
return { success: true };
|
|
1814
|
+
}
|
|
1596
1815
|
if (agentId === 'kiro') {
|
|
1597
1816
|
const permissionsPath = path.join(versionHome, '.kiro', 'settings', 'permissions.yaml');
|
|
1598
1817
|
let config = {};
|
|
@@ -1669,6 +1888,64 @@ export function applyPermissionsToVersion(agentId, set, versionHome, merge = tru
|
|
|
1669
1888
|
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
1670
1889
|
return { success: true };
|
|
1671
1890
|
}
|
|
1891
|
+
if (agentId === 'forge') {
|
|
1892
|
+
const permissionsPath = path.join(versionHome, '.forge', 'permissions.yaml');
|
|
1893
|
+
let config = {};
|
|
1894
|
+
if (fs.existsSync(permissionsPath)) {
|
|
1895
|
+
const parsed = yaml.parse(fs.readFileSync(permissionsPath, 'utf-8'));
|
|
1896
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
1897
|
+
config = parsed;
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
const newPolicies = convertToForgeFormat(set).policies;
|
|
1901
|
+
if (merge) {
|
|
1902
|
+
const existingPolicies = Array.isArray(config.policies) ? config.policies : [];
|
|
1903
|
+
const seen = new Set();
|
|
1904
|
+
config.policies = [...existingPolicies, ...newPolicies].filter((policy) => {
|
|
1905
|
+
if (!policy || typeof policy !== 'object' || Array.isArray(policy))
|
|
1906
|
+
return false;
|
|
1907
|
+
const key = JSON.stringify(policy);
|
|
1908
|
+
if (seen.has(key))
|
|
1909
|
+
return false;
|
|
1910
|
+
seen.add(key);
|
|
1911
|
+
return true;
|
|
1912
|
+
});
|
|
1913
|
+
}
|
|
1914
|
+
else {
|
|
1915
|
+
config.policies = newPolicies;
|
|
1916
|
+
}
|
|
1917
|
+
fs.mkdirSync(path.dirname(permissionsPath), { recursive: true });
|
|
1918
|
+
fs.writeFileSync(permissionsPath, yaml.stringify(config), 'utf-8');
|
|
1919
|
+
return { success: true };
|
|
1920
|
+
}
|
|
1921
|
+
if (agentId === 'hermes') {
|
|
1922
|
+
const configPath = path.join(versionHome, '.hermes', 'config.yaml');
|
|
1923
|
+
let config = {};
|
|
1924
|
+
if (fs.existsSync(configPath)) {
|
|
1925
|
+
const parsed = yaml.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
1926
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
1927
|
+
config = parsed;
|
|
1928
|
+
}
|
|
1929
|
+
}
|
|
1930
|
+
const converted = convertToHermesFormat(set);
|
|
1931
|
+
const approvals = (typeof config.approvals === 'object' && config.approvals !== null && !Array.isArray(config.approvals))
|
|
1932
|
+
? config.approvals
|
|
1933
|
+
: {};
|
|
1934
|
+
if (merge) {
|
|
1935
|
+
const existingAllow = Array.isArray(config.command_allowlist) ? config.command_allowlist : [];
|
|
1936
|
+
const existingDeny = Array.isArray(approvals.deny) ? approvals.deny : [];
|
|
1937
|
+
config.command_allowlist = Array.from(new Set([...existingAllow, ...converted.command_allowlist])).sort();
|
|
1938
|
+
approvals.deny = Array.from(new Set([...existingDeny, ...converted.approvals.deny])).sort();
|
|
1939
|
+
}
|
|
1940
|
+
else {
|
|
1941
|
+
config.command_allowlist = converted.command_allowlist;
|
|
1942
|
+
approvals.deny = converted.approvals.deny;
|
|
1943
|
+
}
|
|
1944
|
+
config.approvals = approvals;
|
|
1945
|
+
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
1946
|
+
fs.writeFileSync(configPath, yaml.stringify(config), 'utf-8');
|
|
1947
|
+
return { success: true };
|
|
1948
|
+
}
|
|
1672
1949
|
return { success: false, error: `Agent '${agentId}' does not support permissions` };
|
|
1673
1950
|
}
|
|
1674
1951
|
catch (err) {
|
|
@@ -8,18 +8,12 @@
|
|
|
8
8
|
* (+ per-agent symlinks). Delegates to compileRulesForProject, which is
|
|
9
9
|
* the same helper management-side `agents sync` uses.
|
|
10
10
|
*
|
|
11
|
-
* 2.
|
|
12
|
-
* into the agent's workspace-local discovery
|
|
13
|
-
* `<cwd>/.
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* uses `~/.config/amp`, antigravity uses `~/.gemini/antigravity-cli`,
|
|
18
|
-
* codex/gemini/cursor lack subagent support entirely) — they're follow-up
|
|
19
|
-
* material. NOTE: `.mcp.json` is intentionally NOT auto-symlinked from
|
|
20
|
-
* the launch path — that's a supply-chain surface (cloning a hostile
|
|
21
|
-
* repo would auto-register an attacker MCP server). Belongs to full
|
|
22
|
-
* `agents sync` with explicit opt-in, not the hot path.
|
|
11
|
+
* 2. Copy project resources from `<cwd>/.agents/{commands,skills,subagents,workflows}`
|
|
12
|
+
* into the agent's workspace-local discovery dir (`<cwd>/.claude/`,
|
|
13
|
+
* `<cwd>/.codex/`, etc.) with an ownership manifest at
|
|
14
|
+
* `<cwd>/.{agent}/.agents-managed.json`. The manifest is the only clobber
|
|
15
|
+
* authority: paths it lists are removed and refreshed, pre-existing paths it
|
|
16
|
+
* does not list are skipped as user-owned.
|
|
23
17
|
*
|
|
24
18
|
* 3. Synthesize four scope-grouped plugin marketplaces under the version's
|
|
25
19
|
* `<versionHome>/.{agent}/plugins/marketplaces/` (for plugin-capable agents):
|