@phnx-labs/agents-cli 1.22.42 → 1.22.44
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 +149 -0
- package/README.md +15 -13
- package/dist/bootstrap.js +33 -13
- package/dist/cli/command-registry.d.ts +0 -8
- package/dist/cli/command-registry.js +1 -25
- package/dist/commands/alias.d.ts +7 -4
- package/dist/commands/alias.js +33 -8
- package/dist/commands/apply.d.ts +7 -10
- package/dist/commands/apply.js +15 -35
- package/dist/commands/artifacts.js +4 -5
- package/dist/commands/audit.d.ts +6 -6
- package/dist/commands/audit.js +12 -12
- package/dist/commands/auth.js +14 -6
- package/dist/commands/beta.d.ts +7 -1
- package/dist/commands/beta.js +16 -9
- package/dist/commands/commands.js +1 -1
- package/dist/commands/computer.js +20 -6
- package/dist/commands/daemon.js +1 -1
- package/dist/commands/doctor.d.ts +4 -2
- package/dist/commands/doctor.js +21 -9
- package/dist/commands/events.d.ts +2 -2
- package/dist/commands/events.js +6 -5
- package/dist/commands/exec.d.ts +1 -1
- package/dist/commands/exec.js +11 -6
- package/dist/commands/factory.d.ts +1 -1
- package/dist/commands/feed.js +1 -30
- package/dist/commands/fleet-capture.d.ts +1 -1
- package/dist/commands/fleet-capture.js +4 -5
- package/dist/commands/harness.js +4 -52
- package/dist/commands/hooks.js +1 -1
- package/dist/commands/insights.d.ts +1 -1
- package/dist/commands/insights.js +3 -2
- package/dist/commands/menubar.js +58 -1
- package/dist/commands/monitors.js +1 -1
- package/dist/commands/org.d.ts +5 -0
- package/dist/commands/org.js +83 -29
- package/dist/commands/output.js +2 -2
- package/dist/commands/profiles.js +2 -2
- package/dist/commands/prune.js +5 -4
- package/dist/commands/routines.js +43 -25
- package/dist/commands/routines.test-fixture.d.ts +86 -0
- package/dist/commands/routines.test-fixture.js +345 -0
- package/dist/commands/sessions-picker.js +124 -122
- package/dist/commands/sessions.test-fixture.d.ts +24 -0
- package/dist/commands/sessions.test-fixture.js +224 -0
- package/dist/commands/setup.js +9 -1
- package/dist/commands/share.d.ts +9 -10
- package/dist/commands/share.js +68 -50
- package/dist/commands/skills.js +1 -1
- package/dist/commands/snapshot.d.ts +1 -1
- package/dist/commands/snapshot.js +4 -4
- package/dist/commands/ssh.js +15 -62
- package/dist/commands/status.d.ts +4 -2
- package/dist/commands/status.js +10 -7
- package/dist/commands/subagents.js +1 -1
- package/dist/commands/sync.d.ts +1 -0
- package/dist/commands/sync.js +29 -2
- package/dist/commands/view.js +11 -2
- package/dist/commands/webhook.js +1 -1
- package/dist/lib/accounting/rotate.d.ts +5 -3
- package/dist/lib/accounting/rotate.js +36 -7
- package/dist/lib/accounting/usage.d.ts +18 -4
- package/dist/lib/accounting/usage.js +138 -27
- package/dist/lib/agent-spec/agents.d.ts +7 -7
- package/dist/lib/agent-spec/agents.js +15 -15
- package/dist/lib/analytics/mix-commands.d.ts +6 -28
- package/dist/lib/analytics/mix-commands.js +14 -40
- package/dist/lib/audit/log.d.ts +2 -2
- package/dist/lib/audit/log.js +2 -2
- package/dist/lib/beta.js +1 -1
- package/dist/lib/browser/domain-skills.d.ts +26 -6
- package/dist/lib/browser/domain-skills.js +81 -43
- package/dist/lib/cloud/rush.d.ts +0 -15
- package/dist/lib/cloud/rush.js +0 -35
- package/dist/lib/crabbox/runtimes.d.ts +6 -2
- package/dist/lib/crabbox/runtimes.js +21 -5
- package/dist/lib/daemon/daemon.js +97 -58
- package/dist/lib/daemon/daemon.test-fixture.d.ts +24 -0
- package/dist/lib/daemon/daemon.test-fixture.js +71 -0
- package/dist/lib/daemon/runner.d.ts +18 -1
- package/dist/lib/daemon/runner.js +105 -11
- package/dist/lib/daemon-ticks.d.ts +38 -1
- package/dist/lib/daemon-ticks.js +52 -5
- package/dist/lib/device-config.js +1 -2
- package/dist/lib/devices/discovery-policy.d.ts +3 -3
- package/dist/lib/devices/discovery-policy.js +3 -3
- package/dist/lib/devices/doctor-findings.js +1 -1
- package/dist/lib/devices/fleet.d.ts +4 -8
- package/dist/lib/devices/fleet.js +3 -15
- package/dist/lib/devices/health-report.js +4 -4
- package/dist/lib/devices/pool.d.ts +0 -6
- package/dist/lib/devices/pool.js +0 -6
- package/dist/lib/devices/registry.d.ts +0 -15
- package/dist/lib/devices/registry.js +0 -9
- package/dist/lib/devices/stats-cache.d.ts +12 -0
- package/dist/lib/devices/stats-cache.js +24 -5
- package/dist/lib/drift-sync.d.ts +1 -1
- package/dist/lib/drift-sync.js +2 -2
- package/dist/lib/drift.d.ts +1 -1
- package/dist/lib/exec.js +13 -3
- package/dist/lib/fleet/capture.d.ts +1 -1
- package/dist/lib/fleet/manifest.js +1 -1
- package/dist/lib/fleet/remote-login.d.ts +2 -2
- package/dist/lib/fleet/remote-login.js +3 -4
- package/dist/lib/format.d.ts +9 -0
- package/dist/lib/format.js +16 -0
- package/dist/lib/github/pr-mergeable.d.ts +49 -0
- package/dist/lib/github/pr-mergeable.js +157 -0
- package/dist/lib/github/pr-verdict.d.ts +67 -0
- package/dist/lib/github/pr-verdict.js +72 -0
- package/dist/lib/harness/adapter.d.ts +6 -5
- package/dist/lib/hosts/dispatch.d.ts +2 -0
- package/dist/lib/hosts/dispatch.js +8 -1
- package/dist/lib/hosts/passthrough.d.ts +10 -0
- package/dist/lib/hosts/passthrough.js +28 -7
- package/dist/lib/hosts/providers/devices.js +1 -13
- package/dist/lib/hosts/ready.d.ts +12 -3
- package/dist/lib/hosts/ready.js +27 -12
- package/dist/lib/hosts/registry.d.ts +4 -5
- package/dist/lib/hosts/registry.js +3 -7
- package/dist/lib/hosts/session-index.js +1 -1
- package/dist/lib/installations/shims.js +1 -1
- package/dist/lib/installations/versions.d.ts +40 -0
- package/dist/lib/installations/versions.js +78 -4
- package/dist/lib/menubar/MenubarHelper.app/Contents/Info.plist +1 -5
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/_CodeSignature/CodeResources +2 -15
- package/dist/lib/menubar/install-menubar.d.ts +109 -0
- package/dist/lib/menubar/install-menubar.js +218 -12
- package/dist/lib/monitors/config.d.ts +2 -2
- package/dist/lib/monitors/config.js +3 -2
- package/dist/lib/origin-machine.d.ts +18 -0
- package/dist/lib/origin-machine.js +34 -0
- package/dist/lib/perf/db.js +1 -1
- package/dist/lib/perf/spool.js +1 -1
- package/dist/lib/prix-account.d.ts +2 -1
- package/dist/lib/prix-account.js +2 -1
- package/dist/lib/profiles.d.ts +7 -0
- package/dist/lib/profiles.js +26 -1
- package/dist/lib/remote-agents-json.js +1 -7
- package/dist/lib/routines-placement.d.ts +1 -1
- package/dist/lib/routines-placement.js +1 -1
- package/dist/lib/sandbox.d.ts +24 -0
- package/dist/lib/sandbox.js +101 -0
- package/dist/lib/scheduling/routines.d.ts +18 -9
- package/dist/lib/scheduling/routines.js +16 -8
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/Info.plist +0 -2
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/_CodeSignature/CodeResources +1 -13
- package/dist/lib/secrets/agent.js +9 -1
- package/dist/lib/secrets/index.js +1 -1
- package/dist/lib/secrets/list-filter.js +1 -1
- package/dist/lib/service-manifest.d.ts +20 -0
- package/dist/lib/service-manifest.js +34 -0
- package/dist/lib/session/active.d.ts +4 -16
- package/dist/lib/session/active.js +2 -6
- package/dist/lib/session/cloud.js +1 -1
- package/dist/lib/session/db.d.ts +13 -98
- package/dist/lib/session/db.js +5 -12
- package/dist/lib/session/discover.d.ts +5 -81
- package/dist/lib/session/discover.js +5 -5
- package/dist/lib/session/fork.js +1 -1
- package/dist/lib/session/live-metadata.js +1 -1
- package/dist/lib/session/origin-machine.d.ts +1 -18
- package/dist/lib/session/origin-machine.js +4 -34
- package/dist/lib/session/relative-time.d.ts +1 -44
- package/dist/lib/session/relative-time.js +4 -120
- package/dist/lib/session/remote/remote-bundle.d.ts +12 -0
- package/dist/lib/session/remote/remote-bundle.js +61 -0
- package/dist/lib/session/remote/remote-list.d.ts +148 -0
- package/dist/lib/session/remote/remote-list.js +607 -0
- package/dist/lib/session/remote/remote.d.ts +132 -0
- package/dist/lib/session/remote/remote.js +314 -0
- package/dist/lib/session/remote/watch.d.ts +101 -0
- package/dist/lib/session/remote/watch.js +241 -0
- package/dist/lib/session/remote-bundle.d.ts +1 -12
- package/dist/lib/session/remote-bundle.js +3 -61
- package/dist/lib/session/remote-list.d.ts +1 -148
- package/dist/lib/session/remote-list.js +3 -607
- package/dist/lib/session/remote.d.ts +1 -132
- package/dist/lib/session/remote.js +3 -314
- package/dist/lib/session/short-id.d.ts +1 -17
- package/dist/lib/session/short-id.js +4 -20
- package/dist/lib/session/watch.d.ts +1 -101
- package/dist/lib/session/watch.js +3 -242
- package/dist/lib/session/width.d.ts +1 -29
- package/dist/lib/session/width.js +4 -101
- package/dist/lib/share/config.d.ts +12 -2
- package/dist/lib/share/config.js +47 -11
- package/dist/lib/share/delete.js +1 -1
- package/dist/lib/share/publish.js +1 -1
- package/dist/lib/share/worker-template.js +2 -2
- package/dist/lib/smart-launch.d.ts +4 -4
- package/dist/lib/smart-launch.js +8 -18
- package/dist/lib/snapshot.d.ts +2 -2
- package/dist/lib/snapshot.js +1 -1
- package/dist/lib/staleness/index.d.ts +3 -1
- package/dist/lib/staleness/index.js +21 -1
- package/dist/lib/staleness/types.d.ts +11 -0
- package/dist/lib/staleness/writers/commands.js +13 -4
- package/dist/lib/staleness/writers/hooks.js +3 -1
- package/dist/lib/staleness/writers/mcp.d.ts +0 -7
- package/dist/lib/staleness/writers/mcp.js +16 -1
- package/dist/lib/staleness/writers/rules.js +1 -1
- package/dist/lib/staleness/writers/skills.js +3 -1
- package/dist/lib/staleness/writers/subagents.d.ts +0 -9
- package/dist/lib/staleness/writers/subagents.js +18 -1
- package/dist/lib/staleness/writers/types.d.ts +10 -0
- package/dist/lib/startup/command-registry.d.ts +10 -1
- package/dist/lib/startup/command-registry.js +26 -7
- package/dist/lib/startup/root-command.d.ts +18 -1
- package/dist/lib/startup/root-command.js +18 -1
- package/dist/lib/state.js +7 -0
- package/dist/lib/teams/placement-probe.js +28 -13
- package/dist/lib/teams/scheduler.d.ts +3 -1
- package/dist/lib/teams/scheduler.js +1 -1
- package/dist/lib/text/relative-time.d.ts +44 -0
- package/dist/lib/text/relative-time.js +120 -0
- package/dist/lib/text/short-id.d.ts +17 -0
- package/dist/lib/text/short-id.js +20 -0
- package/dist/lib/text/width.d.ts +29 -0
- package/dist/lib/text/width.js +101 -0
- package/dist/lib/triggers/handlers.d.ts +7 -0
- package/dist/lib/triggers/handlers.js +8 -0
- package/dist/lib/triggers/webhook.d.ts +1 -0
- package/dist/lib/triggers/webhook.js +2 -1
- package/dist/lib/types.d.ts +1 -1
- package/dist/lib/view-types.d.ts +7 -0
- package/package.json +2 -3
- package/dist/bin/agents +0 -0
- package/dist/commands/serve.d.ts +0 -10
- package/dist/commands/serve.js +0 -68
- package/dist/commands/tickets.d.ts +0 -2
- package/dist/commands/tickets.js +0 -43
- package/dist/commands/trends.d.ts +0 -10
- package/dist/commands/trends.js +0 -12
- package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/Resources/AppIcon.icns +0 -0
- package/dist/lib/observe-aliases.d.ts +0 -30
- package/dist/lib/observe-aliases.js +0 -38
- package/dist/lib/secrets/Agents CLI.app/Contents/Resources/AppIcon.icns +0 -0
- package/dist/lib/serve/control.d.ts +0 -95
- package/dist/lib/serve/control.js +0 -260
- package/dist/lib/serve/data.d.ts +0 -81
- package/dist/lib/serve/data.js +0 -91
- package/dist/lib/serve/page.d.ts +0 -7
- package/dist/lib/serve/page.js +0 -140
- package/dist/lib/serve/server.d.ts +0 -80
- package/dist/lib/serve/server.js +0 -145
- package/dist/lib/serve/stream.d.ts +0 -43
- package/dist/lib/serve/stream.js +0 -116
- package/dist/lib/serve/token.d.ts +0 -35
- package/dist/lib/serve/token.js +0 -85
- package/dist/lib/tickets/list.d.ts +0 -53
- package/dist/lib/tickets/list.js +0 -153
|
@@ -24,7 +24,7 @@ import { normalizeHost } from '../machine-id.js';
|
|
|
24
24
|
import { readMeta } from '../state.js';
|
|
25
25
|
import { isSshConfigHost } from './ssh-config.js';
|
|
26
26
|
import { resolveRemoteOsSync } from './remote-os.js';
|
|
27
|
-
import { loadDevices
|
|
27
|
+
import { loadDevices } from '../devices/registry.js';
|
|
28
28
|
import { resolveDeviceProfile } from '../devices/resolve-profile.js';
|
|
29
29
|
import { isDeviceAuto, resolveDeviceAffinity } from '../smart-launch.js';
|
|
30
30
|
import { localMachineId } from '../session/origin-machine.js';
|
|
@@ -148,7 +148,7 @@ function literalHost(token, host, user) {
|
|
|
148
148
|
* all resolve the same way regardless of which subcommand called.
|
|
149
149
|
*
|
|
150
150
|
* Non-throwing: returns null on an injection-guard failure or an unresolved bare
|
|
151
|
-
* token.
|
|
151
|
+
* token. The device-only refusal (password auth) is NOT applied
|
|
152
152
|
* here — that is the dispatch wrapper's job ({@link resolveHost}), so the fan-out
|
|
153
153
|
* and `agents ssh` paths, which handle those cases differently, aren't forced
|
|
154
154
|
* into a dispatch verdict.
|
|
@@ -240,8 +240,7 @@ export async function listAllHosts() {
|
|
|
240
240
|
* caller consumes (`run --device`, the generic passthrough, teams placement, the
|
|
241
241
|
* cloud host provider, doctor, funnel, remote secrets). Grammar and merge come
|
|
242
242
|
* from {@link matchHost}; on top, this layer applies the device-only dispatch
|
|
243
|
-
*
|
|
244
|
-
* - a control device (a paired iPhone) can never be a dispatch target;
|
|
243
|
+
* refusal so it holds even when an inline overlay shadows the device:
|
|
245
244
|
* - a password-auth device throws the typed {@link DeviceOffloadUnsupportedError}
|
|
246
245
|
* (offload rides `BatchMode=yes` ssh, which can't answer a prompt);
|
|
247
246
|
* - an addressless device has nothing to dial.
|
|
@@ -254,9 +253,6 @@ export async function resolveHost(name) {
|
|
|
254
253
|
if (!host)
|
|
255
254
|
return null;
|
|
256
255
|
if (host.device) {
|
|
257
|
-
if (isControlDevice(host.device)) {
|
|
258
|
-
throw new Error(`Device "${host.device.name}" is a control device (a cockpit), not an executor — it can't run agents. Dispatch to a worker device instead.`);
|
|
259
|
-
}
|
|
260
256
|
if (host.device.auth.method === 'password') {
|
|
261
257
|
throw new DeviceOffloadUnsupportedError(host.device.name);
|
|
262
258
|
}
|
|
@@ -21,7 +21,7 @@ import { upsertSession } from '../session/db.js';
|
|
|
21
21
|
import { isSessionTrackedAgent } from '../session/types.js';
|
|
22
22
|
import { localLogPath, updateTask } from './tasks.js';
|
|
23
23
|
import { parseSessionIdMarker } from './session-marker.js';
|
|
24
|
-
import { deriveShortId } from '../
|
|
24
|
+
import { deriveShortId } from '../text/short-id.js';
|
|
25
25
|
import { normalizeHost } from '../machine-id.js';
|
|
26
26
|
/**
|
|
27
27
|
* Build the SessionMeta for a host-dispatched run. Returns null when the run has
|
|
@@ -2015,7 +2015,7 @@ export function pruneOrphanedCommandShim(fileName) {
|
|
|
2015
2015
|
return false;
|
|
2016
2016
|
}
|
|
2017
2017
|
if (content.includes('# Alias shim:'))
|
|
2018
|
-
return false; // a user `agents alias` — leave it
|
|
2018
|
+
return false; // a user `agents setup alias` shim — leave it
|
|
2019
2019
|
const bin = readAgentsBinFromShim(shimPath);
|
|
2020
2020
|
if (!bin)
|
|
2021
2021
|
return false; // not an AGENTS_BIN-baked shim
|
|
@@ -230,6 +230,46 @@ export declare function printTrashFooter(moved: Array<{
|
|
|
230
230
|
* containing only `home/`) remain under the agent dir.
|
|
231
231
|
*/
|
|
232
232
|
export declare function removeAllVersions(agent: AgentId): number;
|
|
233
|
+
export interface HealedVersionPointers {
|
|
234
|
+
/** Global default reassigned off a not-installed version (`to: null` = cleared). */
|
|
235
|
+
globalDefault?: {
|
|
236
|
+
from: string;
|
|
237
|
+
to: string | null;
|
|
238
|
+
};
|
|
239
|
+
/** Isolated default (bare `agents run <agent>`) reassigned off a not-installed version. */
|
|
240
|
+
isolatedDefault?: {
|
|
241
|
+
from: string;
|
|
242
|
+
to: string | null;
|
|
243
|
+
};
|
|
244
|
+
/** `~/.<agent>` config symlink repointed off a not-installed version. */
|
|
245
|
+
configSymlink?: {
|
|
246
|
+
from: string;
|
|
247
|
+
to: string;
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Repoint an agent's version pointers — the global/isolated default and the
|
|
252
|
+
* `~/.<agent>` config symlink — off any version that is no longer installed, so
|
|
253
|
+
* neither `agents sync`/`agents run` resolution nor the conventional-path home
|
|
254
|
+
* can land on a dead version (RUSH-2471).
|
|
255
|
+
*
|
|
256
|
+
* How a pointer goes dangling: {@link removeVersion} reassigns the defaults and
|
|
257
|
+
* clears the config symlink only when IT removes the pointed-at version. A
|
|
258
|
+
* version-home whose launch binary disappears by any other route — grok
|
|
259
|
+
* self-updated its per-version binary out from under the old dir, a manual
|
|
260
|
+
* delete, a half-finished install that seeded the home but never landed the
|
|
261
|
+
* binary — leaves the default and/or the symlink pointing at something
|
|
262
|
+
* {@link isVersionInstalled} reports as gone.
|
|
263
|
+
*
|
|
264
|
+
* Called from the sync resolve path so the invariant self-heals. Mirrors
|
|
265
|
+
* {@link removeVersion}'s reassignment: the global default prefers the newest
|
|
266
|
+
* NON-isolated survivor else clears; the isolated default prefers the newest
|
|
267
|
+
* remaining isolated copy else clears. The symlink is repointed at the now-healed
|
|
268
|
+
* resolved default, else the newest installed non-isolated version. A symlink
|
|
269
|
+
* already on an installed version, a real (non-symlink) config dir, and an
|
|
270
|
+
* isolated-only agent are left untouched.
|
|
271
|
+
*/
|
|
272
|
+
export declare function healDanglingVersionPointers(agent: AgentId, cwd: string): Promise<HealedVersionPointers>;
|
|
233
273
|
/**
|
|
234
274
|
* Normalize a user-supplied @version token across CLI subcommands.
|
|
235
275
|
*
|
|
@@ -32,9 +32,9 @@ import { VERSION_RE, compareVersions } from '../agent-spec/primitives.js';
|
|
|
32
32
|
import { AGENTS, agentConfigDirName, getAccountEmail, resolveAgentName, formatAgentError, findInPath, isSelfUpdatingAgent, isAgentHardDeprecated, hardDeprecationError } from '../agents.js';
|
|
33
33
|
import { discoverPermissionGroups, getActivePermissionPresetName, readPermissionPresetRecipe, PERMISSION_PRESET_ENV_VAR } from '../permissions.js';
|
|
34
34
|
import { parseMcpConfigForScan, isProjectMcpTrusted } from '../mcp.js';
|
|
35
|
-
import { createVersionedAlias, removeVersionedAlias, getConfigSymlinkVersion, ensureClaudeInsideSymlink, assertIsolationBoundary, } from './shims.js';
|
|
35
|
+
import { createVersionedAlias, removeVersionedAlias, switchConfigSymlink, getConfigSymlinkVersion, ensureClaudeInsideSymlink, assertIsolationBoundary, isIsolationProtected, } from './shims.js';
|
|
36
36
|
import { importInstallScriptBinary } from '../import.js';
|
|
37
|
-
import { createInstallation, getBinaryPath, getCliVersionFromPath, getGlobalDefault, getIsolatedDefault, getLiveVersion, getVersionDir, getVersionHomePath, invalidateInstalledVersionsCache, invalidateLiveVersionCache, isGlobalBinaryAgent, isVersionInstalled, isVersionIsolated, listInstalledVersions, pickCanonicalGlobalBinaryVersion, } from './store.js';
|
|
37
|
+
import { createInstallation, getBinaryPath, getCliVersionFromPath, getGlobalDefault, getIsolatedDefault, getLiveVersion, getVersionDir, getVersionHomePath, invalidateInstalledVersionsCache, invalidateLiveVersionCache, isGlobalBinaryAgent, isVersionInstalled, isVersionIsolated, listInstalledVersions, pickCanonicalGlobalBinaryVersion, resolveVersion, } from './store.js';
|
|
38
38
|
export * from './store.js';
|
|
39
39
|
import { INSTALLATION_RECORD_FILE } from './types.js';
|
|
40
40
|
import { composeWin32CommandLine } from '../platform/index.js';
|
|
@@ -1745,6 +1745,61 @@ export function removeAllVersions(agent) {
|
|
|
1745
1745
|
}
|
|
1746
1746
|
return removed;
|
|
1747
1747
|
}
|
|
1748
|
+
/**
|
|
1749
|
+
* Repoint an agent's version pointers — the global/isolated default and the
|
|
1750
|
+
* `~/.<agent>` config symlink — off any version that is no longer installed, so
|
|
1751
|
+
* neither `agents sync`/`agents run` resolution nor the conventional-path home
|
|
1752
|
+
* can land on a dead version (RUSH-2471).
|
|
1753
|
+
*
|
|
1754
|
+
* How a pointer goes dangling: {@link removeVersion} reassigns the defaults and
|
|
1755
|
+
* clears the config symlink only when IT removes the pointed-at version. A
|
|
1756
|
+
* version-home whose launch binary disappears by any other route — grok
|
|
1757
|
+
* self-updated its per-version binary out from under the old dir, a manual
|
|
1758
|
+
* delete, a half-finished install that seeded the home but never landed the
|
|
1759
|
+
* binary — leaves the default and/or the symlink pointing at something
|
|
1760
|
+
* {@link isVersionInstalled} reports as gone.
|
|
1761
|
+
*
|
|
1762
|
+
* Called from the sync resolve path so the invariant self-heals. Mirrors
|
|
1763
|
+
* {@link removeVersion}'s reassignment: the global default prefers the newest
|
|
1764
|
+
* NON-isolated survivor else clears; the isolated default prefers the newest
|
|
1765
|
+
* remaining isolated copy else clears. The symlink is repointed at the now-healed
|
|
1766
|
+
* resolved default, else the newest installed non-isolated version. A symlink
|
|
1767
|
+
* already on an installed version, a real (non-symlink) config dir, and an
|
|
1768
|
+
* isolated-only agent are left untouched.
|
|
1769
|
+
*/
|
|
1770
|
+
export async function healDanglingVersionPointers(agent, cwd) {
|
|
1771
|
+
const healed = {};
|
|
1772
|
+
const installed = listInstalledVersions(agent);
|
|
1773
|
+
const globalDefault = getGlobalDefault(agent);
|
|
1774
|
+
if (globalDefault !== null && !isVersionInstalled(agent, globalDefault)) {
|
|
1775
|
+
const promotable = installed.filter((v) => !isVersionIsolated(agent, v));
|
|
1776
|
+
const to = promotable.length > 0 ? promotable[promotable.length - 1] : undefined;
|
|
1777
|
+
setGlobalDefault(agent, to);
|
|
1778
|
+
healed.globalDefault = { from: globalDefault, to: to ?? null };
|
|
1779
|
+
}
|
|
1780
|
+
const isolatedDefault = getIsolatedDefault(agent);
|
|
1781
|
+
if (isolatedDefault !== null && !isVersionInstalled(agent, isolatedDefault)) {
|
|
1782
|
+
const survivors = installed.filter((v) => isVersionIsolated(agent, v));
|
|
1783
|
+
const to = survivors.length > 0 ? survivors[survivors.length - 1] : undefined;
|
|
1784
|
+
setIsolatedDefault(agent, to);
|
|
1785
|
+
healed.isolatedDefault = { from: isolatedDefault, to: to ?? null };
|
|
1786
|
+
}
|
|
1787
|
+
const current = getConfigSymlinkVersion(agent);
|
|
1788
|
+
const nonIsolated = installed.filter((v) => !isVersionIsolated(agent, v));
|
|
1789
|
+
if (current !== null &&
|
|
1790
|
+
!isVersionInstalled(agent, current) &&
|
|
1791
|
+
!isIsolationProtected(agent) &&
|
|
1792
|
+
nonIsolated.length > 0) {
|
|
1793
|
+
const pinned = resolveVersion(agent, cwd);
|
|
1794
|
+
const target = pinned && isVersionInstalled(agent, pinned) && !isVersionIsolated(agent, pinned)
|
|
1795
|
+
? pinned
|
|
1796
|
+
: nonIsolated[nonIsolated.length - 1];
|
|
1797
|
+
const result = await switchConfigSymlink(agent, target);
|
|
1798
|
+
if (result.success)
|
|
1799
|
+
healed.configSymlink = { from: current, to: target };
|
|
1800
|
+
}
|
|
1801
|
+
return healed;
|
|
1802
|
+
}
|
|
1748
1803
|
/**
|
|
1749
1804
|
* Normalize a user-supplied @version token across CLI subcommands.
|
|
1750
1805
|
*
|
|
@@ -2436,6 +2491,10 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
|
|
|
2436
2491
|
const commandsAlsoAsSkills = shouldAlsoInstallCommandAsSkill(agent, version);
|
|
2437
2492
|
const commandsInstallAsSkills = commandsAsSkills || commandsAlsoAsSkills;
|
|
2438
2493
|
let writtenCommands = [];
|
|
2494
|
+
// Artifact paths the writers report (WriteResult.paths), persisted to the
|
|
2495
|
+
// manifest as `writtenTargets` after a full sync so isStale can flag a
|
|
2496
|
+
// deleted artifact as stale (#2398).
|
|
2497
|
+
const writtenTargets = [];
|
|
2439
2498
|
if (commandsToSync.length > 0 && commandsWriter) {
|
|
2440
2499
|
// Agents that replace native command files with skills (codex >= 0.117.0,
|
|
2441
2500
|
// kimi) must have their legacy command dir removed, or files written by an
|
|
@@ -2448,6 +2507,8 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
|
|
|
2448
2507
|
}
|
|
2449
2508
|
const r = commandsWriter.write({ version, versionHome, selection: commandsToSync, cwd });
|
|
2450
2509
|
writtenCommands = r.synced;
|
|
2510
|
+
if (r.paths)
|
|
2511
|
+
writtenTargets.push(...r.paths);
|
|
2451
2512
|
result.commands = r.synced.length > 0;
|
|
2452
2513
|
}
|
|
2453
2514
|
// Orphan-sweep stale top-level command files from previous syncs under a
|
|
@@ -2455,8 +2516,9 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
|
|
|
2455
2516
|
// not pass an explicit `selection`. Callers that pass explicit selections
|
|
2456
2517
|
// are using the incremental/additive API (sync exactly these; leave others
|
|
2457
2518
|
// alone), so the sweep would be a contract violation there. The
|
|
2458
|
-
// cross-project leak
|
|
2459
|
-
// launch
|
|
2519
|
+
// cross-project leak comes from no-selection full syncs run under different
|
|
2520
|
+
// cwds (`agents sync` / `agents refresh`; the shim's `agents sync --launch`
|
|
2521
|
+
// skips version-home reconciliation and never reaches this path).
|
|
2460
2522
|
if (!userPassedSelection && commandsWriter && !shouldInstallCommandAsSkill(agent, version)) {
|
|
2461
2523
|
const commandsTargetSweep = path.join(agentDir, agentConfig.commandsSubdir);
|
|
2462
2524
|
if (fs.existsSync(commandsTargetSweep)) {
|
|
@@ -2515,6 +2577,8 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
|
|
|
2515
2577
|
else if (skillsWriter) {
|
|
2516
2578
|
if (skillsToSync.length > 0) {
|
|
2517
2579
|
const r = skillsWriter.write({ version, versionHome, selection: skillsToSync, cwd });
|
|
2580
|
+
if (r.paths)
|
|
2581
|
+
writtenTargets.push(...r.paths);
|
|
2518
2582
|
result.skills = r.synced.length > 0;
|
|
2519
2583
|
}
|
|
2520
2584
|
// Orphan-sweep stale skill directories from previous syncs under a
|
|
@@ -2570,6 +2634,8 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
|
|
|
2570
2634
|
}
|
|
2571
2635
|
}
|
|
2572
2636
|
}
|
|
2637
|
+
if (r.paths)
|
|
2638
|
+
writtenTargets.push(...r.paths);
|
|
2573
2639
|
result.hooks = r.synced.length > 0;
|
|
2574
2640
|
hookManifest = selectHookManifest(parseHookManifest(), hooksToSync);
|
|
2575
2641
|
}
|
|
@@ -2596,6 +2662,8 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
|
|
|
2596
2662
|
: null;
|
|
2597
2663
|
const preset = overridePreset || activeRulesPreset() || getActiveRulesPreset(agent, version);
|
|
2598
2664
|
const r = rulesWriter.write({ version, versionHome, selection: { preset }, cwd });
|
|
2665
|
+
if (r.paths)
|
|
2666
|
+
writtenTargets.push(...r.paths);
|
|
2599
2667
|
result.memory.push(...r.synced);
|
|
2600
2668
|
// rulesPreset is tracked separately via setActiveRulesPreset.
|
|
2601
2669
|
}
|
|
@@ -2673,6 +2741,8 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
|
|
|
2673
2741
|
const mcpToSync = mcpToSyncAll.filter(n => !untrustedProjectMcpNames.has(n));
|
|
2674
2742
|
if (mcpToSync.length > 0 && mcpWriter) {
|
|
2675
2743
|
const r = mcpWriter.write({ version, versionHome, selection: mcpToSync, cwd });
|
|
2744
|
+
if (r.paths)
|
|
2745
|
+
writtenTargets.push(...r.paths);
|
|
2676
2746
|
result.mcp = r.synced;
|
|
2677
2747
|
if (r.errors?.length)
|
|
2678
2748
|
result.declined.push(...r.errors.map((e) => `mcp: ${e}`));
|
|
@@ -2694,6 +2764,8 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
|
|
|
2694
2764
|
}
|
|
2695
2765
|
if (subagentsToSync.length > 0 && subagentsWriter) {
|
|
2696
2766
|
const r = subagentsWriter.write({ version, versionHome, selection: subagentsToSync, cwd });
|
|
2767
|
+
if (r.paths)
|
|
2768
|
+
writtenTargets.push(...r.paths);
|
|
2697
2769
|
result.subagents.push(...r.synced);
|
|
2698
2770
|
// Orphan-sweep for Claude only — see comment on commands/skills sweep
|
|
2699
2771
|
// for the no-selection guard. OpenClaw stores subagents as siblings of
|
|
@@ -2800,6 +2872,8 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
|
|
|
2800
2872
|
const previous = loadManifest(agent, version);
|
|
2801
2873
|
const manifest = buildSyncManifest(agent, version, cwd, previous);
|
|
2802
2874
|
manifest.writtenCommands = writtenCommands;
|
|
2875
|
+
// Deduped + sorted so repeated full syncs write byte-identical manifests.
|
|
2876
|
+
manifest.writtenTargets = Array.from(new Set(writtenTargets)).sort();
|
|
2803
2877
|
saveManifest(agent, version, manifest);
|
|
2804
2878
|
}
|
|
2805
2879
|
return result;
|
|
@@ -7,11 +7,7 @@
|
|
|
7
7
|
<key>CFBundleIdentifier</key>
|
|
8
8
|
<string>com.phnx-labs.agents-menubar</string>
|
|
9
9
|
<key>CFBundleName</key>
|
|
10
|
-
<string>
|
|
11
|
-
<key>CFBundleDisplayName</key>
|
|
12
|
-
<string>AGI Menu</string>
|
|
13
|
-
<key>CFBundleIconFile</key>
|
|
14
|
-
<string>AppIcon</string>
|
|
10
|
+
<string>Agents Menu Bar</string>
|
|
15
11
|
<key>CFBundlePackageType</key>
|
|
16
12
|
<string>APPL</string>
|
|
17
13
|
<key>CFBundleShortVersionString</key>
|
|
Binary file
|
|
@@ -3,22 +3,9 @@
|
|
|
3
3
|
<plist version="1.0">
|
|
4
4
|
<dict>
|
|
5
5
|
<key>files</key>
|
|
6
|
-
<dict
|
|
7
|
-
<key>Resources/AppIcon.icns</key>
|
|
8
|
-
<data>
|
|
9
|
-
jOjZVimcFRHoP2VPzgn8uM8mjkA=
|
|
10
|
-
</data>
|
|
11
|
-
</dict>
|
|
6
|
+
<dict/>
|
|
12
7
|
<key>files2</key>
|
|
13
|
-
<dict
|
|
14
|
-
<key>Resources/AppIcon.icns</key>
|
|
15
|
-
<dict>
|
|
16
|
-
<key>hash2</key>
|
|
17
|
-
<data>
|
|
18
|
-
GFvSLeNYJ3ASxW3OzcuiB4aID0gZUBkpozmWZkbTFNw=
|
|
19
|
-
</data>
|
|
20
|
-
</dict>
|
|
21
|
-
</dict>
|
|
8
|
+
<dict/>
|
|
22
9
|
<key>rules</key>
|
|
23
10
|
<dict>
|
|
24
11
|
<key>^Resources/</key>
|
|
@@ -75,6 +75,33 @@ export declare function generateServicePlist(execPath: string): string;
|
|
|
75
75
|
export declare function restartMenubarLaunchAgent(uid: number, plist: string, exec?: (cmd: string, args: readonly string[], opts: {
|
|
76
76
|
stdio: ['ignore', 'ignore', 'ignore'];
|
|
77
77
|
}) => Buffer): void;
|
|
78
|
+
/**
|
|
79
|
+
* Force a running helper off the binary that was just swapped underneath it.
|
|
80
|
+
*
|
|
81
|
+
* `installMenubarLaunchAgentOnUpgrade()` calls this after a heal actually
|
|
82
|
+
* replaces the on-disk bundle (a version bump or the ad-hoc -> Developer ID
|
|
83
|
+
* transition — see `menubarHealReplacedBundle`). `restartMenubarLaunchAgent`
|
|
84
|
+
* (run moments earlier via `installAndStartService`) already attempts
|
|
85
|
+
* bootout+bootstrap+kickstart, but that targets the GUI launchd domain and
|
|
86
|
+
* fails silently — `launchctl kickstart -k gui/<uid>/<label>` prints "Could not
|
|
87
|
+
* find service ... in domain for user gui" — from a shell with no Aqua session,
|
|
88
|
+
* which is the ordinary case for `agents` invoked from a terminal/tmux/ssh
|
|
89
|
+
* session rather than literally the login item (verified live). When that
|
|
90
|
+
* happens the plist and on-disk bundle are current but the LIVE process is
|
|
91
|
+
* still the old binary, so it requests Accessibility under the OLD code
|
|
92
|
+
* identity and the grant never sticks.
|
|
93
|
+
*
|
|
94
|
+
* `kickstart -k` (force-restart, unlike the bare kickstart already tried) is
|
|
95
|
+
* attempted first since it is the clean launchd-native path when it works. If
|
|
96
|
+
* launchd has no record of the service in this context either, fall back to
|
|
97
|
+
* ending the specific pid(s) already confirmed to be running the installed
|
|
98
|
+
* bundle (`ownProcesses`, resolved by the caller via `liveMenubarProcesses`) —
|
|
99
|
+
* never a broad pkill. The launchd plist has `KeepAlive=true`, so the ended
|
|
100
|
+
* process is relaunched from the swapped binary within seconds.
|
|
101
|
+
*/
|
|
102
|
+
export declare function restartMenubarHelperAfterSwap(uid: number, ownProcesses: readonly MenubarProcess[], exec?: (cmd: string, args: readonly string[], opts: {
|
|
103
|
+
stdio: ['ignore', 'ignore', 'ignore'];
|
|
104
|
+
}) => Buffer, kill?: (pid: number) => void): void;
|
|
78
105
|
/**
|
|
79
106
|
* Install + start the menu-bar helper as a launchd user service (idempotent).
|
|
80
107
|
* Clears the sticky opt-out, installs the .app, writes the plist, and
|
|
@@ -97,6 +124,23 @@ export declare function isMenubarStale(opts: {
|
|
|
97
124
|
currentVersion: string;
|
|
98
125
|
execExists: boolean;
|
|
99
126
|
}): boolean;
|
|
127
|
+
/**
|
|
128
|
+
* Pure decision (no I/O): did THIS heal actually replace bundle content a live
|
|
129
|
+
* process could be running stale, as opposed to a plist-only repoint?
|
|
130
|
+
*
|
|
131
|
+
* `stale` (version bump or missing exec) and `needsDevIdHeal` (ad-hoc ->
|
|
132
|
+
* Developer ID) both mean the shipped `.app` differs from what is already
|
|
133
|
+
* installed — the exact condition under which a running helper can be left on
|
|
134
|
+
* an old code identity after `enableMenubarService` swaps the bundle. A
|
|
135
|
+
* repoint-only heal (`menubarSetupNeedsRepoint()` alone, same version, same
|
|
136
|
+
* signing identity) does NOT count: RUSH-3005 already owns the churn from
|
|
137
|
+
* mixed-Node-interpreter repoints, and restarting the helper on every one of
|
|
138
|
+
* those would double that churn rather than fix this bug.
|
|
139
|
+
*/
|
|
140
|
+
export declare function menubarHealReplacedBundle(opts: {
|
|
141
|
+
stale: boolean;
|
|
142
|
+
needsDevIdHeal: boolean;
|
|
143
|
+
}): boolean;
|
|
100
144
|
/**
|
|
101
145
|
* Pure re-point decision (no I/O): the plist's baked interpreter/entry no longer
|
|
102
146
|
* match the install that is now running `agents`. This catches DUAL-INSTALL skew
|
|
@@ -110,6 +154,7 @@ export declare function isMenubarStale(opts: {
|
|
|
110
154
|
export declare function menubarPlistNeedsRepoint(opts: {
|
|
111
155
|
plistEntry: string | null;
|
|
112
156
|
plistNode: string | null;
|
|
157
|
+
plistNodeExists: boolean;
|
|
113
158
|
activeEntry: string | null;
|
|
114
159
|
activeNode: string | null;
|
|
115
160
|
}): boolean;
|
|
@@ -185,6 +230,31 @@ export declare function mayInstallMenubarHelper(opts: {
|
|
|
185
230
|
* throws into startup.
|
|
186
231
|
*/
|
|
187
232
|
export declare function installMenubarLaunchAgentOnUpgrade(): void;
|
|
233
|
+
/**
|
|
234
|
+
* Pure decision (no I/O): run the one-time `tccutil reset Accessibility` only
|
|
235
|
+
* on a real ad-hoc -> Developer ID transition, and only once ever. A machine
|
|
236
|
+
* that was always Developer ID never accumulated a dead TCC row under an
|
|
237
|
+
* ad-hoc identity, so resetting there would just force a needless re-prompt;
|
|
238
|
+
* a machine that already migrated must not be reset again on every heal.
|
|
239
|
+
*/
|
|
240
|
+
export declare function shouldMigrateMenubarTcc(opts: {
|
|
241
|
+
needsDevIdHeal: boolean;
|
|
242
|
+
alreadyMigrated: boolean;
|
|
243
|
+
}): boolean;
|
|
244
|
+
/**
|
|
245
|
+
* Reset the stale Accessibility grant TCC recorded against the pre-migration
|
|
246
|
+
* ad-hoc identity, then stamp the marker so this never runs again on this
|
|
247
|
+
* machine. Targets `SERVICE_LABEL_BASE` — the bundle's actual
|
|
248
|
+
* `CFBundleIdentifier`/codesign `--identifier` (menubar/scripts/build.sh) that
|
|
249
|
+
* TCC keys grants to — not the namespaced `serviceLabel()`, which exists only
|
|
250
|
+
* to keep launchd job labels apart under a redirected HOME (RUSH-2639) and is
|
|
251
|
+
* never the app's real identity. Best-effort: a missing/failing `tccutil`
|
|
252
|
+
* (older macOS, a sandboxed test HOME) must never block the heal that just
|
|
253
|
+
* fixed the signing identity itself.
|
|
254
|
+
*/
|
|
255
|
+
export declare function resetMenubarAccessibilityTcc(exec?: (cmd: string, args: readonly string[], opts: {
|
|
256
|
+
stdio: ['ignore', 'ignore', 'ignore'];
|
|
257
|
+
}) => Buffer): void;
|
|
188
258
|
/** One step of `agents menubar setup`, and how it came out. */
|
|
189
259
|
export interface SetupStep {
|
|
190
260
|
/** What was configured. */
|
|
@@ -272,3 +342,42 @@ export interface MenubarStatus {
|
|
|
272
342
|
disabledByUser: boolean;
|
|
273
343
|
}
|
|
274
344
|
export declare function getMenubarStatus(): MenubarStatus;
|
|
345
|
+
/** Read-only diagnostic for `agents menubar doctor` — probes, never mutates. */
|
|
346
|
+
export interface MenubarDoctorReport {
|
|
347
|
+
platform: string;
|
|
348
|
+
installPath: string | null;
|
|
349
|
+
installedVersion: string | null;
|
|
350
|
+
currentVersion: string;
|
|
351
|
+
versionMatches: boolean;
|
|
352
|
+
/** `unknown` on non-darwin or when nothing is installed to inspect. */
|
|
353
|
+
signingIdentity: 'developer-id' | 'ad-hoc' | 'unknown';
|
|
354
|
+
running: boolean;
|
|
355
|
+
/** A live helper pid started before the installed bundle's on-disk mtime —
|
|
356
|
+
* it is running the binary that has since been swapped underneath it. */
|
|
357
|
+
staleRunningProcess: PidStaleness[];
|
|
358
|
+
/** Grant likely needs to be re-made: an ad-hoc identity breaks on every
|
|
359
|
+
* update, or a live process is confirmed to predate the current bundle. */
|
|
360
|
+
accessibilityHintNeeded: boolean;
|
|
361
|
+
}
|
|
362
|
+
/** One live pid checked against the bundle's on-disk mtime. */
|
|
363
|
+
export interface PidStaleness {
|
|
364
|
+
pid: number;
|
|
365
|
+
stale: boolean;
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Pure comparison (no I/O) behind the stale-process check: a helper pid that
|
|
369
|
+
* started before the bundle on disk was last written is still running the
|
|
370
|
+
* PREVIOUS binary — the exact condition `restartMenubarHelperAfterSwap` exists
|
|
371
|
+
* to fix. Millisecond epoch timestamps in, so the truth table is unit-testable
|
|
372
|
+
* without a live process or filesystem.
|
|
373
|
+
*/
|
|
374
|
+
export declare function isMenubarProcessStaleAgainstBundle(pidStartedAtMs: number, bundleMtimeMs: number): boolean;
|
|
375
|
+
/**
|
|
376
|
+
* `agents menubar doctor` — everything a human needs to tell "why did
|
|
377
|
+
* Accessibility ask again" apart from "the helper is genuinely broken":
|
|
378
|
+
* install path, version skew, whether the signing identity is update-stable,
|
|
379
|
+
* and whether a live process predates the bundle it's supposed to be running.
|
|
380
|
+
* Read-only: no install, no restart, no TCC reset — `agents menubar setup`
|
|
381
|
+
* remains the command that fixes what this reports.
|
|
382
|
+
*/
|
|
383
|
+
export declare function buildMenubarDoctorReport(): MenubarDoctorReport;
|