@phnx-labs/agents-cli 1.22.41 → 1.22.43
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 +86 -0
- package/README.md +10 -4
- package/dist/bin/agents +0 -0
- package/dist/bootstrap.js +25 -7
- package/dist/cli/command-registry.d.ts +2 -3
- package/dist/cli/command-registry.js +4 -8
- package/dist/commands/accounts.d.ts +29 -0
- package/dist/commands/accounts.js +220 -29
- package/dist/commands/alias.d.ts +7 -4
- package/dist/commands/alias.js +34 -9
- package/dist/commands/auth.d.ts +9 -0
- package/dist/commands/auth.js +108 -0
- package/dist/commands/browser-sessions-picker.js +42 -62
- package/dist/commands/browser.js +30 -38
- package/dist/commands/computer-actions.d.ts +1 -1
- package/dist/commands/computer-actions.js +1 -1
- package/dist/commands/computer-sessions-picker.js +32 -55
- package/dist/commands/computer.d.ts +1 -1
- package/dist/commands/computer.js +22 -8
- package/dist/commands/daemon.js +1 -1
- package/dist/commands/doctor.d.ts +5 -3
- package/dist/commands/doctor.js +23 -8
- package/dist/commands/exec.js +3 -2
- package/dist/commands/feed.js +2 -31
- package/dist/commands/harness.js +3 -3
- package/dist/commands/hooks.js +1 -1
- package/dist/commands/import.js +1 -1
- package/dist/commands/insights.js +163 -85
- package/dist/commands/inspect.js +2 -2
- package/dist/commands/mine.js +1 -1
- package/dist/commands/monitors.js +1 -1
- package/dist/commands/org.d.ts +6 -0
- package/dist/commands/org.js +175 -0
- package/dist/commands/packages.js +1 -1
- package/dist/commands/profiles.js +2 -2
- package/dist/commands/prune.js +1 -1
- package/dist/commands/routines.js +44 -26
- package/dist/commands/run-account-picker.d.ts +19 -0
- package/dist/commands/run-account-picker.js +81 -0
- package/dist/commands/sessions-picker-factory.d.ts +40 -0
- package/dist/commands/sessions-picker-factory.js +67 -0
- package/dist/commands/sessions-picker.js +124 -122
- package/dist/commands/setup-browser.js +4 -4
- package/dist/commands/setup-mine.js +1 -1
- package/dist/commands/setup-preferences.js +2 -2
- package/dist/commands/setup.js +10 -4
- package/dist/commands/snapshot.d.ts +1 -1
- package/dist/commands/snapshot.js +4 -4
- package/dist/commands/status.d.ts +4 -2
- package/dist/commands/status.js +10 -7
- package/dist/commands/sync.d.ts +1 -0
- package/dist/commands/sync.js +30 -3
- package/dist/commands/versions.js +1 -1
- package/dist/commands/view.js +7 -1
- package/dist/commands/webhook.js +1 -1
- package/dist/lib/account-capabilities.d.ts +10 -0
- package/dist/lib/account-capabilities.js +34 -0
- 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 +584 -0
- package/dist/lib/agent-spec/agents.js +3127 -0
- package/dist/lib/agent-spec/provider.js +1 -1
- package/dist/lib/agents.d.ts +2 -584
- package/dist/lib/agents.js +2 -3127
- package/dist/lib/browser/drivers/local.d.ts +8 -2
- package/dist/lib/browser/drivers/local.js +8 -2
- package/dist/lib/browser/drivers/ssh.d.ts +8 -2
- package/dist/lib/browser/drivers/ssh.js +10 -4
- package/dist/lib/browser/hygiene.d.ts +1 -1
- package/dist/lib/browser/hygiene.js +5 -1
- package/dist/lib/browser/profiles.d.ts +74 -1
- package/dist/lib/browser/profiles.js +114 -5
- package/dist/lib/browser/runtime-state.d.ts +11 -5
- package/dist/lib/browser/runtime-state.js +13 -9
- package/dist/lib/browser/service.d.ts +68 -30
- package/dist/lib/browser/service.js +305 -194
- package/dist/lib/browser/types.d.ts +95 -3
- package/dist/lib/browser/types.js +51 -0
- package/dist/lib/catchup.js +1 -1
- package/dist/lib/cloud/rush.d.ts +0 -15
- package/dist/lib/cloud/rush.js +0 -35
- package/dist/lib/computer/actions.d.ts +1 -1
- package/dist/lib/computer/actions.js +1 -1
- package/dist/lib/{computer-rpc.js → computer/computer-rpc.js} +5 -5
- package/dist/lib/computer/dispatch.d.ts +1 -1
- package/dist/lib/computer/download.d.ts +1 -1
- package/dist/lib/computer/download.js +3 -3
- package/dist/lib/{ssh-tunnel.d.ts → computer/ssh-tunnel.d.ts} +1 -1
- package/dist/lib/{ssh-tunnel.js → computer/ssh-tunnel.js} +12 -12
- package/dist/lib/crabbox/runtimes.d.ts +6 -2
- package/dist/lib/crabbox/runtimes.js +21 -5
- package/dist/lib/daemon/daemon.js +87 -59
- package/dist/lib/{runner.d.ts → daemon/runner.d.ts} +28 -11
- package/dist/lib/{runner.js → daemon/runner.js} +143 -123
- package/dist/lib/devices/doctor-findings.d.ts +1 -1
- package/dist/lib/devices/doctor-findings.js +4 -4
- package/dist/lib/devices/fleet-inventory.js +1 -1
- package/dist/lib/devices/harness-inventory.js +1 -1
- package/dist/lib/devices/health-report.js +1 -1
- package/dist/lib/devices/registry.js +4 -22
- package/dist/lib/doctor-diff.d.ts +1 -1
- package/dist/lib/doctor-diff.js +1 -1
- 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/drift.js +1 -1
- package/dist/lib/entitlement.d.ts +31 -0
- package/dist/lib/entitlement.js +137 -0
- package/dist/lib/exec.js +52 -205
- package/dist/lib/feed/feed.js +2 -8
- package/dist/lib/format.d.ts +9 -0
- package/dist/lib/format.js +16 -0
- package/dist/lib/fs-atomic.d.ts +7 -0
- package/dist/lib/fs-atomic.js +9 -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 +161 -0
- package/dist/lib/harness/adapter.js +47 -0
- package/dist/lib/harness/adapters/claude.d.ts +2 -0
- package/dist/lib/harness/adapters/claude.js +110 -0
- package/dist/lib/harness/adapters/codex.d.ts +2 -0
- package/dist/lib/harness/adapters/codex.js +83 -0
- package/dist/lib/harness/adapters/copilot.d.ts +2 -0
- package/dist/lib/harness/adapters/copilot.js +23 -0
- package/dist/lib/harness/adapters/cursor.d.ts +2 -0
- package/dist/lib/harness/adapters/cursor.js +38 -0
- package/dist/lib/harness/adapters/droid.d.ts +2 -0
- package/dist/lib/harness/adapters/droid.js +17 -0
- package/dist/lib/harness/adapters/grok.d.ts +2 -0
- package/dist/lib/harness/adapters/grok.js +14 -0
- package/dist/lib/harness/adapters/kimi.d.ts +2 -0
- package/dist/lib/harness/adapters/kimi.js +49 -0
- package/dist/lib/harness/adapters/muse.d.ts +2 -0
- package/dist/lib/harness/adapters/muse.js +44 -0
- package/dist/lib/harness/adapters/opencode.d.ts +2 -0
- package/dist/lib/harness/adapters/opencode.js +14 -0
- package/dist/lib/harness/exec-config-version.d.ts +8 -0
- package/dist/lib/harness/exec-config-version.js +22 -0
- package/dist/lib/harness/index.d.ts +1 -0
- package/dist/lib/harness/index.js +34 -0
- package/dist/lib/{hooks.d.ts → hooks/install.d.ts} +1 -1
- package/dist/lib/{hooks.js → hooks/install.js} +11 -11
- package/dist/lib/hosts/passthrough.d.ts +10 -0
- package/dist/lib/hosts/passthrough.js +28 -6
- package/dist/lib/hosts/session-index.js +1 -1
- package/dist/lib/import.js +1 -1
- package/dist/lib/installations/index.d.ts +4 -4
- package/dist/lib/installations/index.js +3 -4
- package/dist/lib/installations/migrate.js +6 -6
- package/dist/lib/{shims.d.ts → installations/shims.d.ts} +2 -2
- package/dist/lib/{shims.js → installations/shims.js} +13 -115
- package/dist/lib/installations/store.d.ts +141 -3
- package/dist/lib/installations/store.js +475 -5
- package/dist/lib/installations/versions.d.ts +40 -125
- package/dist/lib/installations/versions.js +57 -462
- package/dist/lib/isolation-boundary-report.d.ts +1 -1
- package/dist/lib/isolation-boundary-report.js +1 -1
- package/dist/lib/mailbox-gc.js +2 -3
- package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/menubar/install-menubar.js +19 -8
- package/dist/lib/monitors/config.d.ts +2 -2
- package/dist/lib/monitors/config.js +3 -2
- package/dist/lib/monitors/dispatch.d.ts +1 -1
- package/dist/lib/monitors/dispatch.js +2 -2
- package/dist/lib/monitors/state.d.ts +1 -1
- 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/plugins/plugins.js +1 -1
- package/dist/lib/plugins/skills.js +1 -1
- package/dist/lib/prix-account.d.ts +158 -0
- package/dist/lib/prix-account.js +214 -0
- package/dist/lib/profiles.d.ts +7 -0
- package/dist/lib/profiles.js +26 -1
- package/dist/lib/project-key.js +2 -2
- package/dist/lib/project-launch.js +1 -1
- package/dist/lib/refresh.js +2 -2
- package/dist/lib/resource-inventory.d.ts +1 -1
- package/dist/lib/resource-inventory.js +1 -1
- package/dist/lib/rules/rules.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 +17 -9
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/secrets/agent.js +9 -1
- package/dist/lib/secrets/index.js +1 -1
- package/dist/lib/secrets/list-filter.js +1 -1
- package/dist/lib/self-heal/checks/hook-runtime.js +1 -1
- package/dist/lib/self-heal/checks/path.js +1 -1
- package/dist/lib/self-heal/checks/shadowing.js +1 -1
- package/dist/lib/self-heal/checks/shims.js +1 -1
- package/dist/lib/service-manifest.d.ts +20 -0
- package/dist/lib/service-manifest.js +34 -0
- package/dist/lib/session/cloud.js +1 -1
- package/dist/lib/session/db.js +1 -1
- package/dist/lib/session/discover.d.ts +39 -0
- package/dist/lib/session/discover.js +3 -3
- 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/parse.js +24 -42
- package/dist/lib/session/recovery.js +1 -1
- package/dist/lib/session/relative-time.d.ts +1 -44
- package/dist/lib/session/relative-time.js +4 -120
- package/dist/lib/session/remote-list.js +1 -1
- package/dist/lib/session/remote.js +2 -2
- package/dist/lib/session/short-id.d.ts +1 -17
- package/dist/lib/session/short-id.js +4 -20
- package/dist/lib/session/width.d.ts +1 -29
- package/dist/lib/session/width.js +4 -101
- package/dist/lib/snapshot.d.ts +2 -2
- package/dist/lib/snapshot.js +1 -1
- package/dist/lib/staleness/prune.d.ts +1 -1
- package/dist/lib/staleness/prune.js +1 -1
- package/dist/lib/staleness/writers/hooks.js +1 -1
- package/dist/lib/startup/command-registry.d.ts +4 -1
- package/dist/lib/startup/command-registry.js +12 -5
- package/dist/lib/teams/agents.js +2 -19
- package/dist/lib/teams/parsers.js +20 -26
- package/dist/lib/teams/placement-probe.js +1 -1
- package/dist/lib/teams/registry.js +2 -21
- 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 +9 -1
- package/dist/lib/triggers/webhook.d.ts +1 -0
- package/dist/lib/triggers/webhook.js +3 -2
- package/dist/lib/uninstall.js +1 -1
- package/dist/lib/version.d.ts +9 -2
- package/dist/lib/version.js +11 -9
- package/dist/lib/view-types.d.ts +4 -0
- package/package.json +1 -1
- package/dist/commands/tickets.d.ts +0 -2
- package/dist/commands/tickets.js +0 -43
- package/dist/lib/observe-aliases.d.ts +0 -30
- package/dist/lib/observe-aliases.js +0 -38
- package/dist/lib/tickets/list.d.ts +0 -53
- package/dist/lib/tickets/list.js +0 -153
- /package/dist/lib/{computer-rpc.d.ts → computer/computer-rpc.d.ts} +0 -0
package/dist/lib/project-key.js
CHANGED
|
@@ -58,8 +58,8 @@ export function repoRootForCwd(dir, home = os.homedir()) {
|
|
|
58
58
|
// Never climb past $HOME: an ancestor of home (/tmp, /, ...) is not part of
|
|
59
59
|
// any project this cwd belongs to, and treating one as the repo root would
|
|
60
60
|
// let unrelated host state (e.g. a stray /tmp/.git) swallow every loose
|
|
61
|
-
// directory under home. Mirrors the shim's own home boundary — see
|
|
62
|
-
//
|
|
61
|
+
// directory under home. Mirrors the shim's own home boundary — see the codex
|
|
62
|
+
// adapter's shimExecTail (lib/harness/adapters/codex.ts).
|
|
63
63
|
if (current === stop)
|
|
64
64
|
return undefined;
|
|
65
65
|
const parent = path.dirname(current);
|
|
@@ -86,7 +86,7 @@ export function runLaunchSync(opts) {
|
|
|
86
86
|
}
|
|
87
87
|
/**
|
|
88
88
|
* Path of the shim's skip-fast sentinel for this (agent, version, cwd) tuple.
|
|
89
|
-
* Must match the SHIM-SIDE format in src/lib/shims.ts (PROJECT_SLUG derivation),
|
|
89
|
+
* Must match the SHIM-SIDE format in src/lib/installations/shims.ts (PROJECT_SLUG derivation),
|
|
90
90
|
* which is the canonical `toPortableKey` mapping: drop the Windows drive colon
|
|
91
91
|
* and fold `\`, `/`, and ` ` → `_`. On POSIX this is byte-identical to the old
|
|
92
92
|
* `/` and ` ` → `_` slug; on Windows it yields a legal filename (no `C:\`).
|
package/dist/lib/refresh.js
CHANGED
|
@@ -20,8 +20,8 @@ import { readManifest, MANIFEST_FILENAME } from './manifest.js';
|
|
|
20
20
|
import { getUserAgentsDir } from './state.js';
|
|
21
21
|
import { installVersion, listInstalledVersions, isVersionIsolated, getGlobalDefault, setGlobalDefault, getVersionHomePath, syncResourcesToVersion, getAvailableResources, getActuallySyncedResources, getNewResources, getProjectOnlyResources, hasNewResources, promptNewResourceSelection, promptResourceSelection, resolveConfiguredAgentTargets, } from './installations/versions.js';
|
|
22
22
|
import { listCliStatus, installCli, describeMethod, describeCheck, selectInstallMethod, } from './cli-resources.js';
|
|
23
|
-
import { ensureShimCurrent, isShimsInPath, addShimsToPath, getPathSetupInstructions, switchConfigSymlink, switchHomeFileSymlinks, } from './shims.js';
|
|
24
|
-
import { parseHookManifest, registerHooksToSettings } from './hooks.js';
|
|
23
|
+
import { ensureShimCurrent, isShimsInPath, addShimsToPath, getPathSetupInstructions, switchConfigSymlink, switchHomeFileSymlinks, } from './installations/shims.js';
|
|
24
|
+
import { parseHookManifest, registerHooksToSettings } from './hooks/install.js';
|
|
25
25
|
import { isPromptCancelled } from './format.js';
|
|
26
26
|
/**
|
|
27
27
|
* Old repo layout stored promptcuts under claude/promptcuts.yaml (agent-scoped).
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* muse), Grok hooks.json, and Kimi config.toml via checkVersionHookWiring.
|
|
19
19
|
*/
|
|
20
20
|
import type { AgentId } from './types.js';
|
|
21
|
-
import { type HookWiringReport } from './hooks.js';
|
|
21
|
+
import { type HookWiringReport } from './hooks/install.js';
|
|
22
22
|
/** Resource kinds the inventory can report. Only 'hooks' is implemented so far
|
|
23
23
|
* (RUSH-2238); the remaining kinds land with their follow-up tickets. */
|
|
24
24
|
export type InventoryKind = 'hooks' | 'skills' | 'commands' | 'plugins' | 'mcp';
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
import { AGENTS } from './agents.js';
|
|
21
21
|
import { supports } from './capabilities.js';
|
|
22
22
|
import { listResources } from './resources.js';
|
|
23
|
-
import { checkVersionHookWiring, listHooksInVersionHome, listInstalledHooksWithScope, } from './hooks.js';
|
|
23
|
+
import { checkVersionHookWiring, listHooksInVersionHome, listInstalledHooksWithScope, } from './hooks/install.js';
|
|
24
24
|
const IMPLEMENTED_KINDS = ['hooks'];
|
|
25
25
|
/**
|
|
26
26
|
* The single inventory API. Harness-scoped (claude/codex/grok/kimi/droid) —
|
package/dist/lib/rules/rules.js
CHANGED
|
@@ -10,7 +10,7 @@ import * as fs from 'fs';
|
|
|
10
10
|
import * as path from 'path';
|
|
11
11
|
import { AGENTS, ALL_AGENT_IDS, agentConfigDirName } from '../agents.js';
|
|
12
12
|
import { getResolvedRulesDir, getUserRulesDir, getProjectAgentsDir } from '../state.js';
|
|
13
|
-
import { getEffectiveHome } from '../installations/
|
|
13
|
+
import { getEffectiveHome } from '../installations/store.js';
|
|
14
14
|
/**
|
|
15
15
|
* Central rules filename constant.
|
|
16
16
|
* All agents map to this file in ~/.agents/rules/, renamed per-agent when synced.
|
package/dist/lib/sandbox.d.ts
CHANGED
|
@@ -7,6 +7,14 @@
|
|
|
7
7
|
* access to explicitly allowed paths.
|
|
8
8
|
*/
|
|
9
9
|
import type { JobConfig } from './scheduling/routines.js';
|
|
10
|
+
/**
|
|
11
|
+
* Absolute path to this host's `gh` config directory (`hosts.yml` + `config.yml`),
|
|
12
|
+
* or null when the host has never run `gh auth login`. Prefer `GH_CONFIG_DIR` when
|
|
13
|
+
* the parent already pinned one; otherwise `$XDG_CONFIG_HOME/gh` or `~/.config/gh`.
|
|
14
|
+
*/
|
|
15
|
+
export declare function resolveHostGhConfigDir(): string | null;
|
|
16
|
+
/** True when this host holds usable GitHub CLI auth (hosts.yml or a token env). */
|
|
17
|
+
export declare function hostHasGhAuth(): boolean;
|
|
10
18
|
/** Build a restricted environment for a sandboxed process, setting HOME to the overlay. */
|
|
11
19
|
export declare function buildSpawnEnv(overlayHome: string, extraEnv?: Record<string, string>): Record<string, string>;
|
|
12
20
|
/**
|
|
@@ -21,6 +29,22 @@ export declare function buildSpawnEnv(overlayHome: string, extraEnv?: Record<str
|
|
|
21
29
|
export declare function getJobHomePath(name: string): string;
|
|
22
30
|
/** Create a fresh overlay HOME for a job, including agent config and allowed-dir symlinks. */
|
|
23
31
|
export declare function prepareJobHome(config: JobConfig): string;
|
|
32
|
+
/**
|
|
33
|
+
* Link this host's `gh` config directory into the disposable overlay so
|
|
34
|
+
* `$HOME/.config/gh` resolves even when `GH_CONFIG_DIR` is unset. Mirrors
|
|
35
|
+
* `generateCursorConfig`: same-host credentials, never copied to another box.
|
|
36
|
+
*/
|
|
37
|
+
export declare function linkHostGhConfig(overlayHome: string): void;
|
|
38
|
+
/**
|
|
39
|
+
* Refuse to launch a sandboxed child when THIS host holds GitHub auth but the
|
|
40
|
+
* spawn env would hide it. The RUSH-2860 failure mode was silent: the monitor
|
|
41
|
+
* fire recorded `ok` (spawn succeeded) while every `gh` call inside the child
|
|
42
|
+
* failed. Prefer forwarding (buildSpawnEnv / linkHostGhConfig); this assert is
|
|
43
|
+
* the regression tripwire — if forwarding ever regresses, fail loud at spawn
|
|
44
|
+
* instead of recording a hollow success. No-ops when the host has no gh auth
|
|
45
|
+
* (jobs that do not need GitHub still run).
|
|
46
|
+
*/
|
|
47
|
+
export declare function assertSandboxForwardsHostGhAuth(spawnEnv: Record<string, string>): void;
|
|
24
48
|
/** Link this host's Cursor login and CLI config into the disposable overlay. */
|
|
25
49
|
export declare function generateCursorConfig(overlayHome: string): void;
|
|
26
50
|
/** Remove a job's overlay HOME directory entirely. */
|
package/dist/lib/sandbox.js
CHANGED
|
@@ -43,6 +43,14 @@ const ENV_ALLOWLIST = [
|
|
|
43
43
|
'VISUAL',
|
|
44
44
|
'NO_COLOR',
|
|
45
45
|
'FORCE_COLOR',
|
|
46
|
+
// GitHub CLI — same host credentials interactive `agents run` already sees.
|
|
47
|
+
// Without these, a sandboxed monitor `--run` child has no gh auth even when
|
|
48
|
+
// the daemon user is logged in (RUSH-2860).
|
|
49
|
+
'GH_TOKEN',
|
|
50
|
+
'GH_HOST',
|
|
51
|
+
'GH_ENTERPRISE_TOKEN',
|
|
52
|
+
'GITHUB_TOKEN',
|
|
53
|
+
'GH_CONFIG_DIR',
|
|
46
54
|
];
|
|
47
55
|
/** Tools safe to grant as wildcards (no filesystem access). */
|
|
48
56
|
const SAFE_TOOLS = {
|
|
@@ -57,6 +65,32 @@ function tomlString(value) {
|
|
|
57
65
|
}
|
|
58
66
|
return `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
|
|
59
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* Absolute path to this host's `gh` config directory (`hosts.yml` + `config.yml`),
|
|
70
|
+
* or null when the host has never run `gh auth login`. Prefer `GH_CONFIG_DIR` when
|
|
71
|
+
* the parent already pinned one; otherwise `$XDG_CONFIG_HOME/gh` or `~/.config/gh`.
|
|
72
|
+
*/
|
|
73
|
+
export function resolveHostGhConfigDir() {
|
|
74
|
+
// Honor an explicit pin only when it actually exists; otherwise fall through
|
|
75
|
+
// to the default locations so a stale/broken GH_CONFIG_DIR cannot hide a
|
|
76
|
+
// healthy ~/.config/gh (RUSH-2860).
|
|
77
|
+
if (process.env.GH_CONFIG_DIR && fs.existsSync(process.env.GH_CONFIG_DIR)) {
|
|
78
|
+
return process.env.GH_CONFIG_DIR;
|
|
79
|
+
}
|
|
80
|
+
const configHome = process.env.XDG_CONFIG_HOME || path.join(resolveRealHome(), '.config');
|
|
81
|
+
const ghDir = path.join(configHome, 'gh');
|
|
82
|
+
return fs.existsSync(ghDir) ? ghDir : null;
|
|
83
|
+
}
|
|
84
|
+
/** True when this host holds usable GitHub CLI auth (hosts.yml or a token env). */
|
|
85
|
+
export function hostHasGhAuth() {
|
|
86
|
+
if (process.env.GH_TOKEN || process.env.GH_ENTERPRISE_TOKEN || process.env.GITHUB_TOKEN) {
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
const dir = resolveHostGhConfigDir();
|
|
90
|
+
if (!dir)
|
|
91
|
+
return false;
|
|
92
|
+
return fs.existsSync(path.join(dir, 'hosts.yml'));
|
|
93
|
+
}
|
|
60
94
|
/** Build a restricted environment for a sandboxed process, setting HOME to the overlay. */
|
|
61
95
|
export function buildSpawnEnv(overlayHome, extraEnv) {
|
|
62
96
|
const env = {
|
|
@@ -68,6 +102,18 @@ export function buildSpawnEnv(overlayHome, extraEnv) {
|
|
|
68
102
|
env[key] = process.env[key];
|
|
69
103
|
}
|
|
70
104
|
}
|
|
105
|
+
// Pin GH_CONFIG_DIR at the REAL host config so `gh` does not look under the
|
|
106
|
+
// disposable overlay HOME (which has no hosts.yml). Same posture as linking
|
|
107
|
+
// Cursor's auth.json — forward the credentials the daemon user already holds
|
|
108
|
+
// (RUSH-2860). Always prefer resolveHostGhConfigDir over a stale allowlisted
|
|
109
|
+
// value; an explicit extraEnv GH_CONFIG_DIR still wins below.
|
|
110
|
+
const hostGh = resolveHostGhConfigDir();
|
|
111
|
+
if (hostGh) {
|
|
112
|
+
env.GH_CONFIG_DIR = hostGh;
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
delete env.GH_CONFIG_DIR;
|
|
116
|
+
}
|
|
71
117
|
if (extraEnv) {
|
|
72
118
|
Object.assign(env, extraEnv);
|
|
73
119
|
}
|
|
@@ -101,11 +147,66 @@ export function prepareJobHome(config) {
|
|
|
101
147
|
else if (config.agent === 'cursor') {
|
|
102
148
|
generateCursorConfig(overlayHome);
|
|
103
149
|
}
|
|
150
|
+
// Host tool credentials / setup the overlay HOME would otherwise hide.
|
|
151
|
+
// Cursor already links its own auth above; gh is harness-agnostic and needed
|
|
152
|
+
// by every sandboxed `--run` that shells out to `gh` (RUSH-2860 — monitor
|
|
153
|
+
// merge agents saw "not logged into any GitHub hosts" while the daemon user
|
|
154
|
+
// was fine).
|
|
155
|
+
//
|
|
156
|
+
// Deliberately NOT linking the real ~/.agents here. It would put the secrets
|
|
157
|
+
// master key (.secrets-key) and the encrypted store (.cache/secrets) together
|
|
158
|
+
// at a predictable path inside the overlay, through a writable symlink, in a
|
|
159
|
+
// child whose prompt can carry untrusted text from a watched source. The
|
|
160
|
+
// separate "agents-cli is not set up" defect (state.ts ignores AGENTS_USER_DIR)
|
|
161
|
+
// gets its own scoped change: RUSH-2954.
|
|
162
|
+
linkHostGhConfig(overlayHome);
|
|
104
163
|
if (config.allow?.dirs) {
|
|
105
164
|
symlinkAllowedDirs(overlayHome, config.allow.dirs);
|
|
106
165
|
}
|
|
107
166
|
return overlayHome;
|
|
108
167
|
}
|
|
168
|
+
/**
|
|
169
|
+
* Link this host's `gh` config directory into the disposable overlay so
|
|
170
|
+
* `$HOME/.config/gh` resolves even when `GH_CONFIG_DIR` is unset. Mirrors
|
|
171
|
+
* `generateCursorConfig`: same-host credentials, never copied to another box.
|
|
172
|
+
*/
|
|
173
|
+
export function linkHostGhConfig(overlayHome) {
|
|
174
|
+
const realGhDir = resolveHostGhConfigDir();
|
|
175
|
+
if (!realGhDir || !fs.existsSync(realGhDir))
|
|
176
|
+
return;
|
|
177
|
+
const overlayGhDir = path.join(overlayHome, '.config', 'gh');
|
|
178
|
+
if (fs.existsSync(overlayGhDir))
|
|
179
|
+
return;
|
|
180
|
+
fs.mkdirSync(path.dirname(overlayGhDir), { recursive: true });
|
|
181
|
+
try {
|
|
182
|
+
createLink(realGhDir, overlayGhDir);
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
/* cross-volume or link creation refused: GH_CONFIG_DIR in buildSpawnEnv is the backup */
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Refuse to launch a sandboxed child when THIS host holds GitHub auth but the
|
|
190
|
+
* spawn env would hide it. The RUSH-2860 failure mode was silent: the monitor
|
|
191
|
+
* fire recorded `ok` (spawn succeeded) while every `gh` call inside the child
|
|
192
|
+
* failed. Prefer forwarding (buildSpawnEnv / linkHostGhConfig); this assert is
|
|
193
|
+
* the regression tripwire — if forwarding ever regresses, fail loud at spawn
|
|
194
|
+
* instead of recording a hollow success. No-ops when the host has no gh auth
|
|
195
|
+
* (jobs that do not need GitHub still run).
|
|
196
|
+
*/
|
|
197
|
+
export function assertSandboxForwardsHostGhAuth(spawnEnv) {
|
|
198
|
+
if (!hostHasGhAuth())
|
|
199
|
+
return;
|
|
200
|
+
if (spawnEnv.GH_TOKEN || spawnEnv.GH_ENTERPRISE_TOKEN || spawnEnv.GITHUB_TOKEN)
|
|
201
|
+
return;
|
|
202
|
+
if (spawnEnv.GH_CONFIG_DIR && fs.existsSync(path.join(spawnEnv.GH_CONFIG_DIR, 'hosts.yml')))
|
|
203
|
+
return;
|
|
204
|
+
if (spawnEnv.HOME && fs.existsSync(path.join(spawnEnv.HOME, '.config', 'gh', 'hosts.yml')))
|
|
205
|
+
return;
|
|
206
|
+
throw new Error("sandbox spawn would hide this host's GitHub auth from the child " +
|
|
207
|
+
'(no GH_CONFIG_DIR / ~/.config/gh / GH_TOKEN). Refusing to launch — ' +
|
|
208
|
+
'the agent would record ok then fail every gh call (RUSH-2860).');
|
|
209
|
+
}
|
|
109
210
|
/** Link this host's Cursor login and CLI config into the disposable overlay. */
|
|
110
211
|
export function generateCursorConfig(overlayHome) {
|
|
111
212
|
const realConfigHome = process.env.XDG_CONFIG_HOME || path.join(resolveRealHome(), '.config');
|
|
@@ -107,8 +107,14 @@ export interface JobConfig {
|
|
|
107
107
|
schedule?: string;
|
|
108
108
|
/** Event/webhook fire condition. Optional when `schedule` is set. */
|
|
109
109
|
trigger?: JobTrigger;
|
|
110
|
-
/**
|
|
111
|
-
|
|
110
|
+
/**
|
|
111
|
+
* Which agent runs the routine — a native harness id, or the name of a
|
|
112
|
+
* custom harness (`agents harness list`), which the runner delegates to
|
|
113
|
+
* `agents run <name>` the same way workflow jobs are. Optional — omitted
|
|
114
|
+
* for `workflow`/`command` routines. Exactly one of agent/workflow/command
|
|
115
|
+
* must be set.
|
|
116
|
+
*/
|
|
117
|
+
agent?: AgentId | (string & {});
|
|
112
118
|
workflow?: string;
|
|
113
119
|
/**
|
|
114
120
|
* A plain shell command run directly instead of an agent/workflow — no LLM,
|
|
@@ -298,16 +304,16 @@ export interface JobConfig {
|
|
|
298
304
|
* `lib/monitors/config.ts`) — re-gating here was double-gating on the wrong
|
|
299
305
|
* key.
|
|
300
306
|
*
|
|
301
|
-
* Deliberately narrow: a monitor's `routine` action fires a
|
|
302
|
-
* keeps its activation gate, so a routine defined but not
|
|
303
|
-
* device is still refused.
|
|
307
|
+
* Deliberately narrow: a monitor's or webhook handler's `routine` action fires a
|
|
308
|
+
* REAL routine, which keeps its activation gate, so a routine defined but not
|
|
309
|
+
* activated on this device is still refused.
|
|
304
310
|
*
|
|
305
311
|
* Runtime-only, enforced at both ends of the schema boundary: `writeJob`
|
|
306
312
|
* strips it, and `readJobFileResult` refuses a definition that carries it
|
|
307
313
|
* (a hand-authored one would otherwise fire on every box regardless of
|
|
308
314
|
* activation, since the daemon's load path never calls `validateJob`).
|
|
309
315
|
*/
|
|
310
|
-
dispatchedBy?: 'monitor';
|
|
316
|
+
dispatchedBy?: 'monitor' | 'webhook';
|
|
311
317
|
}
|
|
312
318
|
/**
|
|
313
319
|
* Canonical form of a routine's `projects` field: drop non-string and empty
|
|
@@ -399,7 +405,7 @@ export declare function resolveJobExecutionContext(config: Pick<JobConfig, 'name
|
|
|
399
405
|
export interface RunMeta {
|
|
400
406
|
jobName: string;
|
|
401
407
|
runId: string;
|
|
402
|
-
agent?: AgentId;
|
|
408
|
+
agent?: AgentId | (string & {});
|
|
403
409
|
/**
|
|
404
410
|
* Resolved agent version this run launched under. Re-pointed to each failover
|
|
405
411
|
* attempt's version as the single-shot chain advances (runner.ts), so it names
|
|
@@ -504,10 +510,13 @@ export declare function finalizeRunMeta(meta: RunMeta, status: RunMeta['status']
|
|
|
504
510
|
* `yosemite-s0` all agree. Every fire path (cron scheduler, webhook,
|
|
505
511
|
* catchup/overdue, manual run) gates on this.
|
|
506
512
|
*
|
|
507
|
-
* A monitor-dispatched job ({@link JobConfig.dispatchedBy}
|
|
513
|
+
* A monitor- or webhook-dispatched job ({@link JobConfig.dispatchedBy} set) skips
|
|
508
514
|
* the routine activation manifest: it is not a routine, so its name can never be
|
|
509
515
|
* a member and the lookup could only ever answer "not activated here"
|
|
510
|
-
* (RUSH-2681
|
|
516
|
+
* (RUSH-2681 for monitors; the same hole broke every `run.agent`/`run.workflow`
|
|
517
|
+
* webhook handler, which the receiver logged as `fired` while the run record read
|
|
518
|
+
* `skipped` with an empty allowlist). Ownership was already decided upstream — by
|
|
519
|
+
* the monitor's `device:` pin, or by the one box the webhook was delivered to.
|
|
511
520
|
*/
|
|
512
521
|
export declare function jobRunsOnThisDevice(config: Pick<JobConfig, 'name' | 'devices' | 'dispatchedBy'>): boolean;
|
|
513
522
|
/**
|
|
@@ -14,6 +14,7 @@ import { getRoutinesDir, getSystemRoutinesDir, getRunsDir, ensureAgentsDir, getP
|
|
|
14
14
|
import * as os from 'os';
|
|
15
15
|
import { safeJoin, isSafeSegmentName } from '../paths.js';
|
|
16
16
|
import { isSafeProjectName, loadProjectDef, projectBasePath } from '../projects.js';
|
|
17
|
+
import { isCustomHarnessName } from '../profiles.js';
|
|
17
18
|
import { resolveRoutineExecutionContext, } from '../routine-context.js';
|
|
18
19
|
import { atomicWriteFileSync } from '../fs-atomic.js';
|
|
19
20
|
import { ALL_AGENT_IDS, ROUTINE_AGENT_IDS } from '../agents.js';
|
|
@@ -25,7 +26,7 @@ import { enabledRoutineNames, devicesWithRoutineEnabled, replaceEnabledRoutines,
|
|
|
25
26
|
import { humanizeCron, humanizeNextRun } from '../routines-format.js';
|
|
26
27
|
import { discoverProjectRoutines } from '../routines-project.js';
|
|
27
28
|
import { listProjectDefs } from '../projects.js';
|
|
28
|
-
import { monitorRunningJobs } from '../runner.js';
|
|
29
|
+
import { monitorRunningJobs } from '../daemon/runner.js';
|
|
29
30
|
import { JobScheduler } from '../scheduler.js';
|
|
30
31
|
import { detectOverdueJobs } from '../overdue.js';
|
|
31
32
|
export function fireConditionLabel(job) {
|
|
@@ -351,13 +352,16 @@ export function finalizeRunMeta(meta, status, exitCode, opts) {
|
|
|
351
352
|
* `yosemite-s0` all agree. Every fire path (cron scheduler, webhook,
|
|
352
353
|
* catchup/overdue, manual run) gates on this.
|
|
353
354
|
*
|
|
354
|
-
* A monitor-dispatched job ({@link JobConfig.dispatchedBy}
|
|
355
|
+
* A monitor- or webhook-dispatched job ({@link JobConfig.dispatchedBy} set) skips
|
|
355
356
|
* the routine activation manifest: it is not a routine, so its name can never be
|
|
356
357
|
* a member and the lookup could only ever answer "not activated here"
|
|
357
|
-
* (RUSH-2681
|
|
358
|
+
* (RUSH-2681 for monitors; the same hole broke every `run.agent`/`run.workflow`
|
|
359
|
+
* webhook handler, which the receiver logged as `fired` while the run record read
|
|
360
|
+
* `skipped` with an empty allowlist). Ownership was already decided upstream — by
|
|
361
|
+
* the monitor's `device:` pin, or by the one box the webhook was delivered to.
|
|
358
362
|
*/
|
|
359
363
|
export function jobRunsOnThisDevice(config) {
|
|
360
|
-
if (config.dispatchedBy
|
|
364
|
+
if (config.dispatchedBy === undefined) {
|
|
361
365
|
const activated = routineEnabledOnThisDevice(config.name);
|
|
362
366
|
if (activated !== null)
|
|
363
367
|
return activated;
|
|
@@ -636,7 +640,7 @@ export function readJobFileResult(filePath) {
|
|
|
636
640
|
// written back (writeJob deletes it), so its presence here means hand-authored
|
|
637
641
|
// state, not drift.
|
|
638
642
|
if (Object.prototype.hasOwnProperty.call(parsed, 'dispatchedBy')) {
|
|
639
|
-
return { config: null, problem: '`dispatchedBy:` is a runtime-only monitor marker, not a routine field — routine is inert' };
|
|
643
|
+
return { config: null, problem: '`dispatchedBy:` is a runtime-only monitor/webhook marker, not a routine field — routine is inert' };
|
|
640
644
|
}
|
|
641
645
|
return {
|
|
642
646
|
config: {
|
|
@@ -844,13 +848,17 @@ export function validateJob(config) {
|
|
|
844
848
|
if (config.command !== undefined && (typeof config.command !== 'string' || config.command.trim() === '')) {
|
|
845
849
|
errors.push('command must be a non-empty shell command string');
|
|
846
850
|
}
|
|
847
|
-
if (hasAgent && config.agent && !ALL_AGENT_IDS.includes(config.agent)) {
|
|
848
|
-
errors.push(`agent must be one of: ${ALL_AGENT_IDS.join(', ')}`);
|
|
851
|
+
if (hasAgent && config.agent && !ALL_AGENT_IDS.includes(config.agent) && !isCustomHarnessName(config.agent)) {
|
|
852
|
+
errors.push(`agent must be one of: ${ALL_AGENT_IDS.join(', ')}, or a custom harness (agents harness list)`);
|
|
849
853
|
}
|
|
850
854
|
else if (hasAgent && config.agent && strategy === 'local' &&
|
|
851
|
-
!ROUTINE_AGENT_IDS.includes(config.agent)
|
|
855
|
+
!ROUTINE_AGENT_IDS.includes(config.agent) &&
|
|
856
|
+
// A custom harness is exempt from the local-daemon command table: the
|
|
857
|
+
// runner delegates it to `agents run <name>`, the same path workflow
|
|
858
|
+
// jobs take, so no ROUTINE_AGENT_COMMANDS template is needed.
|
|
859
|
+
!isCustomHarnessName(config.agent)) {
|
|
852
860
|
// The local daemon only knows how to build a command for the agents in
|
|
853
|
-
// ROUTINE_AGENT_IDS (runner.ts
|
|
861
|
+
// ROUTINE_AGENT_IDS (baked from AGENT_COMMANDS in runner.ts) — anything else is a
|
|
854
862
|
// real, installable agent (it passed the ALL_AGENT_IDS check above) but one
|
|
855
863
|
// the daemon can't fire itself, so reject it now instead of accepting the
|
|
856
864
|
// routine and failing at fire time (runner.ts buildJobCommand: "Unsupported
|
|
Binary file
|
|
Binary file
|
|
@@ -36,6 +36,7 @@ import { getCliVersion, getCliVersionFresh } from '../version.js';
|
|
|
36
36
|
import { getCliLaunch } from '../cli-entry.js';
|
|
37
37
|
import { GLOBAL_HARNESS, bundleScopeChain } from './scope.js';
|
|
38
38
|
import { deleteLeaseSession, rehydrateSessions, pruneSessionsOnSleep } from './session-store.js';
|
|
39
|
+
import { serviceManagerRegistrationAllowed } from '../service-manifest.js';
|
|
39
40
|
import { SYNC_GET_CMD, SYNC_PING_CMD, SYNC_LOCK_CMD } from './sync-commands.js';
|
|
40
41
|
import { MAX_LEASE_MS, MIN_LEASE_MS } from './lease.js';
|
|
41
42
|
import { selectLeasedEnv } from './lease.js';
|
|
@@ -316,7 +317,11 @@ export function retireLegacySecretsAgentService() {
|
|
|
316
317
|
const plist = servicePlistPath();
|
|
317
318
|
// Only the real LaunchAgents dir is launchd-managed; a relocated (test) dir
|
|
318
319
|
// has no bootstrapped job, so skip launchctl and just remove the plist.
|
|
319
|
-
|
|
320
|
+
// Also skip launchctl under a redirected HOME: launchctl is per-user-session
|
|
321
|
+
// and HOME-independent, so a sandboxed process would still talk to the real
|
|
322
|
+
// launchd (RUSH-2968).
|
|
323
|
+
const reg = serviceManagerRegistrationAllowed();
|
|
324
|
+
if (!process.env.AGENTS_SECRETS_LAUNCHAGENTS_DIR && reg.allowed) {
|
|
320
325
|
const uid = process.getuid?.() ?? 0;
|
|
321
326
|
try {
|
|
322
327
|
execFileSync('launchctl', ['bootout', `gui/${uid}/${SERVICE_LABEL}`], { stdio: ['ignore', 'ignore', 'ignore'] });
|
|
@@ -328,6 +333,9 @@ export function retireLegacySecretsAgentService() {
|
|
|
328
333
|
catch { /* not loaded */ }
|
|
329
334
|
}
|
|
330
335
|
}
|
|
336
|
+
else if (!reg.allowed) {
|
|
337
|
+
process.stderr.write(`[agents] ${reg.reason}\n`);
|
|
338
|
+
}
|
|
331
339
|
try {
|
|
332
340
|
fs.unlinkSync(plist);
|
|
333
341
|
}
|
|
@@ -32,7 +32,7 @@ import { windowsBackend, usesFileFallback as windowsUsesFileFallback, importNati
|
|
|
32
32
|
import { isHeadlessSecretsContext } from './headless.js';
|
|
33
33
|
import { KEYCHAIN_READ_BACKOFF_TTL_MS, clearKeychainReadBackoff, isKeychainReadBackedOff, noteKeychainReadFailure, } from './read-backoff.js';
|
|
34
34
|
import { getKeychainHelperPath } from './install-helper.js';
|
|
35
|
-
import { deriveShortId } from '../
|
|
35
|
+
import { deriveShortId } from '../text/short-id.js';
|
|
36
36
|
const SERVICE_PREFIX = 'agents-cli';
|
|
37
37
|
export const SECRETS_ITEM_PREFIX = `${SERVICE_PREFIX}.secrets.`;
|
|
38
38
|
const BUNDLES_ITEM_PREFIX = `${SERVICE_PREFIX}.bundles.`;
|
|
@@ -21,7 +21,7 @@ import { parseCommaSeparatedList } from '../format.js';
|
|
|
21
21
|
// From the leaf module, NOT discover.js — the latter imports `../sqlite.js`, so
|
|
22
22
|
// reaching through it for one parser would pull node:sqlite into every `agents
|
|
23
23
|
// secrets` invocation and print Node's SQLite ExperimentalWarning on stderr.
|
|
24
|
-
import { parseTimeFilter } from '../
|
|
24
|
+
import { parseTimeFilter } from '../text/relative-time.js';
|
|
25
25
|
import { describeBundle, bundlePolicy, SECRET_TYPES, } from './bundles.js';
|
|
26
26
|
/** Ref kinds a var's value can have, from `describeBundle`. */
|
|
27
27
|
export const REF_KINDS = ['literal', 'keychain', 'env', 'file', 'exec'];
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// or broken — silent breakage. Repair runs at most once per unique path per
|
|
5
5
|
// pass (no retry, no sync recursion); unresolved findings stay needsAttention.
|
|
6
6
|
import { resultOf } from '../types.js';
|
|
7
|
-
import { repairManagedHookRuntimeArtifacts } from '../../hooks.js';
|
|
7
|
+
import { repairManagedHookRuntimeArtifacts } from '../../hooks/install.js';
|
|
8
8
|
export const hookRuntimeCheck = {
|
|
9
9
|
id: 'hook-runtime',
|
|
10
10
|
title: 'Generated hook runtime shims',
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// Caveat: an already-open shell won't pick up the new rc/PATH entry until it reloads
|
|
7
7
|
// — but new shells will, so the recurring prompt stops.
|
|
8
8
|
import { resultOf } from '../types.js';
|
|
9
|
-
import { isShimsInPath, addShimsToPath, listAgentsWithNonIsolatedInstalledVersions } from '../../shims.js';
|
|
9
|
+
import { isShimsInPath, addShimsToPath, listAgentsWithNonIsolatedInstalledVersions } from '../../installations/shims.js';
|
|
10
10
|
export const pathCheck = {
|
|
11
11
|
id: 'path',
|
|
12
12
|
title: 'Shims directory on PATH',
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// and PATH-order problem are POSIX; Windows resolves via the registry PATH).
|
|
6
6
|
import { resultOf } from '../types.js';
|
|
7
7
|
import { AGENTS } from '../../agents.js';
|
|
8
|
-
import { getPathShadowingExecutable, adoptShadowingLauncher, listAgentsWithNonIsolatedInstalledVersions, } from '../../shims.js';
|
|
8
|
+
import { getPathShadowingExecutable, adoptShadowingLauncher, listAgentsWithNonIsolatedInstalledVersions, } from '../../installations/shims.js';
|
|
9
9
|
import { getGlobalDefault } from '../../installations/versions.js';
|
|
10
10
|
export const shadowingCheck = {
|
|
11
11
|
id: 'shadowing',
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// background so the shim schema settles without user-facing churn.
|
|
5
5
|
import { resultOf } from '../types.js';
|
|
6
6
|
import { AGENTS } from '../../agents.js';
|
|
7
|
-
import { createShim, ensureShimCurrent, ensureVersionedAliasCurrent, isShimCurrent, isVersionedAliasCurrent, removeShim, shimExists, shimPointsAtLiveInstall, removeLegacyUserShim, listAgentsWithInstalledVersions, listAgentsWithNonIsolatedInstalledVersions, listShimFileNames, pruneOrphanedCommandShim, } from '../../shims.js';
|
|
7
|
+
import { createShim, ensureShimCurrent, ensureVersionedAliasCurrent, isShimCurrent, isVersionedAliasCurrent, removeShim, shimExists, shimPointsAtLiveInstall, removeLegacyUserShim, listAgentsWithInstalledVersions, listAgentsWithNonIsolatedInstalledVersions, listShimFileNames, pruneOrphanedCommandShim, } from '../../installations/shims.js';
|
|
8
8
|
import { listInstalledVersions } from '../../installations/versions.js';
|
|
9
9
|
export const shimsCheck = {
|
|
10
10
|
id: 'shims',
|
|
@@ -27,3 +27,23 @@ export declare function serviceManifestHomeEnv(): {
|
|
|
27
27
|
HOME: string;
|
|
28
28
|
AGENTS_REAL_HOME: string;
|
|
29
29
|
};
|
|
30
|
+
/**
|
|
31
|
+
* Whether this process may safely register (or tear down) a service with the
|
|
32
|
+
* user's real service manager.
|
|
33
|
+
*
|
|
34
|
+
* `launchctl` and `systemctl --user` are per-user-session and HOME-independent:
|
|
35
|
+
* a process running under a redirected HOME still talks to the SAME launchd or
|
|
36
|
+
* systemd instance as a normal login session. service-manifest.ts namespaces the
|
|
37
|
+
* job label under a redirected HOME, but that only changes the identifier — the
|
|
38
|
+
* registration still lands in the real service manager. A hermetic test fork (or
|
|
39
|
+
* any CLI running under a sandbox HOME) must therefore never touch launchd/
|
|
40
|
+
* systemd unless it has explicitly opted in.
|
|
41
|
+
*
|
|
42
|
+
* `AGENTS_SERVICE_MANAGER_ALLOW_REDIRECTED_HOME=1` is the test seam for the
|
|
43
|
+
* shim/PATH-based tests that intentionally exercise the registration code path
|
|
44
|
+
* under a redirected HOME. Production code must never set it.
|
|
45
|
+
*/
|
|
46
|
+
export declare function serviceManagerRegistrationAllowed(): {
|
|
47
|
+
allowed: boolean;
|
|
48
|
+
reason: string;
|
|
49
|
+
};
|
|
@@ -78,3 +78,37 @@ export function serviceManifestHomeEnv() {
|
|
|
78
78
|
const home = process.env.HOME || os.homedir();
|
|
79
79
|
return { HOME: home, AGENTS_REAL_HOME: process.env.AGENTS_REAL_HOME || home };
|
|
80
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* Whether this process may safely register (or tear down) a service with the
|
|
83
|
+
* user's real service manager.
|
|
84
|
+
*
|
|
85
|
+
* `launchctl` and `systemctl --user` are per-user-session and HOME-independent:
|
|
86
|
+
* a process running under a redirected HOME still talks to the SAME launchd or
|
|
87
|
+
* systemd instance as a normal login session. service-manifest.ts namespaces the
|
|
88
|
+
* job label under a redirected HOME, but that only changes the identifier — the
|
|
89
|
+
* registration still lands in the real service manager. A hermetic test fork (or
|
|
90
|
+
* any CLI running under a sandbox HOME) must therefore never touch launchd/
|
|
91
|
+
* systemd unless it has explicitly opted in.
|
|
92
|
+
*
|
|
93
|
+
* `AGENTS_SERVICE_MANAGER_ALLOW_REDIRECTED_HOME=1` is the test seam for the
|
|
94
|
+
* shim/PATH-based tests that intentionally exercise the registration code path
|
|
95
|
+
* under a redirected HOME. Production code must never set it.
|
|
96
|
+
*/
|
|
97
|
+
export function serviceManagerRegistrationAllowed() {
|
|
98
|
+
const suffix = isolatedHomeSuffix();
|
|
99
|
+
if (!suffix) {
|
|
100
|
+
return { allowed: true, reason: 'production HOME: service-manager registration allowed' };
|
|
101
|
+
}
|
|
102
|
+
if (process.env.AGENTS_SERVICE_MANAGER_ALLOW_REDIRECTED_HOME === '1') {
|
|
103
|
+
return {
|
|
104
|
+
allowed: true,
|
|
105
|
+
reason: `test seam AGENTS_SERVICE_MANAGER_ALLOW_REDIRECTED_HOME allows registration under sandbox-${suffix}`,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
allowed: false,
|
|
110
|
+
reason: `refusing service-manager registration under redirected HOME (sandbox-${suffix}): ` +
|
|
111
|
+
`launchctl/systemd are per-user-session and HOME-independent, so a sandboxed process ` +
|
|
112
|
+
`would register in the real service manager (RUSH-2968)`,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
@@ -13,7 +13,7 @@ import * as fs from 'fs';
|
|
|
13
13
|
import * as path from 'path';
|
|
14
14
|
import * as os from 'os';
|
|
15
15
|
import * as yaml from 'yaml';
|
|
16
|
-
import { deriveShortId } from '
|
|
16
|
+
import { deriveShortId } from '../text/short-id.js';
|
|
17
17
|
import { getCacheDir } from '../state.js';
|
|
18
18
|
const PROXY_BASE = process.env.RUSH_PROXY_BASE ?? 'https://api.prix.dev';
|
|
19
19
|
const USER_YAML = path.join(os.homedir(), '.rush', 'user.yaml');
|
package/dist/lib/session/db.js
CHANGED
|
@@ -13,7 +13,7 @@ import { parseSession, sessionFilePathContainer } from './parse.js';
|
|
|
13
13
|
import { extractRecentDirectoriesTouched, extractTodoProgressFromEvents } from './state.js';
|
|
14
14
|
import { getSessionsDir, getSessionsDbPath } from '../state.js';
|
|
15
15
|
import { query as queryEvents, queryToolUsageForSessions } from '../feed/events.js';
|
|
16
|
-
import { machineForSessionFile } from '
|
|
16
|
+
import { machineForSessionFile } from '../origin-machine.js';
|
|
17
17
|
import { loadSessionActorIndex, readSessionActorRecord } from './actor-sidecar.js';
|
|
18
18
|
import { toolCallsFromEvents } from './tool-calls.js';
|
|
19
19
|
import { persistToolCalls, toolEvidenceSourcePath } from './tool-store.js';
|
|
@@ -644,6 +644,29 @@ export declare function scanClaudeSessionIncremental(filePath: string, fromOffse
|
|
|
644
644
|
newOffset: number;
|
|
645
645
|
toolCalls: IndexedToolCall[];
|
|
646
646
|
}>;
|
|
647
|
+
/**
|
|
648
|
+
* Decide full-vs-incremental for one Claude file and parse it uniformly, always
|
|
649
|
+
* returning a finalized scan plus the continuation to persist. Both branches run
|
|
650
|
+
* through the SAME reducer (via {@link scanClaudeSessionIncremental}), so the row
|
|
651
|
+
* an append produces is identical to a from-scratch full reparse by construction
|
|
652
|
+
* (the B-1 parity harness proves this at the function level).
|
|
653
|
+
*
|
|
654
|
+
* INCREMENTAL when a prior continuation exists AND the file grew past the
|
|
655
|
+
* persisted offset AND its mtime did not go backwards — an in-place append.
|
|
656
|
+
* FULL (from byte 0, fresh state) otherwise: cold start (no prior), truncation /
|
|
657
|
+
* rewrite (size shrank to at or below the offset), or a clock rewind / restore
|
|
658
|
+
* (mtime older than the last parse). A FULL parse still produces a continuation,
|
|
659
|
+
* so the file's very next append can go incremental.
|
|
660
|
+
*
|
|
661
|
+
* `mode` is returned so the caller (and tests) can confirm which branch ran.
|
|
662
|
+
*/
|
|
663
|
+
export declare function scanClaudeSessionResumable(filePath: string, prior: ClaudeParserState | null, currentFileMtimeMs: number, currentFileSize: number, priorFileMtimeMs?: number): Promise<{
|
|
664
|
+
scan: ClaudeSessionScan;
|
|
665
|
+
newState: ClaudeParserState;
|
|
666
|
+
newOffset: number;
|
|
667
|
+
toolCalls: IndexedToolCall[];
|
|
668
|
+
mode: 'full' | 'incremental';
|
|
669
|
+
}>;
|
|
647
670
|
/** Test seam: read the (incremental, full) Claude parse counters. */
|
|
648
671
|
export declare function __claudeScanBranchCountsForTest(): {
|
|
649
672
|
incremental: number;
|
|
@@ -776,6 +799,22 @@ export declare function scanCodexSessionIncremental(filePath: string, fromOffset
|
|
|
776
799
|
newOffset: number;
|
|
777
800
|
toolCalls: IndexedToolCall[];
|
|
778
801
|
}>;
|
|
802
|
+
/**
|
|
803
|
+
* Decide full-vs-incremental for one Codex rollout and parse it uniformly,
|
|
804
|
+
* always returning a finalized scan plus the continuation to persist. Both
|
|
805
|
+
* branches run through the SAME reducer (via {@link scanCodexSessionIncremental}),
|
|
806
|
+
* so an append produces a row identical to a from-scratch full reparse by
|
|
807
|
+
* construction. INCREMENTAL when a prior continuation exists AND the file grew
|
|
808
|
+
* past the persisted offset AND its mtime did not go backwards; FULL (from byte
|
|
809
|
+
* 0, fresh state) otherwise (cold start, truncation/rewrite, clock rewind).
|
|
810
|
+
*/
|
|
811
|
+
export declare function scanCodexSessionResumable(filePath: string, prior: CodexParserState | null, currentFileMtimeMs: number, currentFileSize: number, priorFileMtimeMs?: number): Promise<{
|
|
812
|
+
scan: CodexSessionScan;
|
|
813
|
+
newState: CodexParserState;
|
|
814
|
+
newOffset: number;
|
|
815
|
+
toolCalls: IndexedToolCall[];
|
|
816
|
+
mode: 'full' | 'incremental';
|
|
817
|
+
}>;
|
|
779
818
|
/** Test seam: read the (incremental, full) Codex parse counters. */
|
|
780
819
|
export declare function __codexScanBranchCountsForTest(): {
|
|
781
820
|
incremental: number;
|
|
@@ -22,7 +22,7 @@ import { AGENTS, agentConfigDirName, getCliVersion, resolveOpenCodeAccountId } f
|
|
|
22
22
|
import { walkForFilesWithStat } from '../fs-walk.js';
|
|
23
23
|
import { hasCommand } from '../cli-resources.js';
|
|
24
24
|
import { execFileShellSpec } from '../platform/exec.js';
|
|
25
|
-
import { getConfigSymlinkVersion } from '../shims.js';
|
|
25
|
+
import { getConfigSymlinkVersion } from '../installations/shims.js';
|
|
26
26
|
import { SESSION_AGENTS } from './types.js';
|
|
27
27
|
import { deriveShortId } from './short-id.js';
|
|
28
28
|
import { buildClaudeAccountIndex, resolveClaudeAccount } from './claude-accounts.js';
|
|
@@ -3491,7 +3491,7 @@ function freshClaudeParserState() {
|
|
|
3491
3491
|
*
|
|
3492
3492
|
* `mode` is returned so the caller (and tests) can confirm which branch ran.
|
|
3493
3493
|
*/
|
|
3494
|
-
async function scanClaudeSessionResumable(filePath, prior, currentFileMtimeMs, currentFileSize, priorFileMtimeMs) {
|
|
3494
|
+
export async function scanClaudeSessionResumable(filePath, prior, currentFileMtimeMs, currentFileSize, priorFileMtimeMs) {
|
|
3495
3495
|
// File size + mtime cannot distinguish an APPEND from an in-place rewrite or a
|
|
3496
3496
|
// restore that dropped DIFFERENT, larger content at the same path: both grow
|
|
3497
3497
|
// the file and move mtime forward. Resuming from the stored offset across that
|
|
@@ -3977,7 +3977,7 @@ async function codexSessionIdentityAt(filePath, maxBytes = 1_048_576) {
|
|
|
3977
3977
|
* past the persisted offset AND its mtime did not go backwards; FULL (from byte
|
|
3978
3978
|
* 0, fresh state) otherwise (cold start, truncation/rewrite, clock rewind).
|
|
3979
3979
|
*/
|
|
3980
|
-
async function scanCodexSessionResumable(filePath, prior, currentFileMtimeMs, currentFileSize, priorFileMtimeMs) {
|
|
3980
|
+
export async function scanCodexSessionResumable(filePath, prior, currentFileMtimeMs, currentFileSize, priorFileMtimeMs) {
|
|
3981
3981
|
// File size + mtime cannot distinguish an APPEND from an in-place rewrite or a
|
|
3982
3982
|
// restore that dropped a DIFFERENT, larger rollout at the same path: both grow
|
|
3983
3983
|
// the file and move mtime forward. Resuming from the stored offset across that
|