@phnx-labs/agents-cli 1.22.42 → 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 +72 -0
- package/README.md +1 -1
- package/dist/bin/agents +0 -0
- package/dist/bootstrap.js +25 -7
- package/dist/cli/command-registry.d.ts +0 -3
- package/dist/cli/command-registry.js +0 -8
- package/dist/commands/alias.d.ts +7 -4
- package/dist/commands/alias.js +33 -8
- package/dist/commands/auth.js +7 -3
- 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 +20 -5
- package/dist/commands/exec.js +3 -2
- package/dist/commands/feed.js +1 -30
- package/dist/commands/harness.js +3 -3
- package/dist/commands/monitors.js +1 -1
- package/dist/commands/org.js +9 -8
- package/dist/commands/profiles.js +2 -2
- package/dist/commands/routines.js +43 -25
- package/dist/commands/sessions-picker.js +124 -122
- package/dist/commands/setup.js +5 -1
- 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 +29 -2
- package/dist/commands/view.js +6 -0
- 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/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 +86 -58
- package/dist/lib/daemon/runner.d.ts +18 -1
- package/dist/lib/daemon/runner.js +105 -11
- package/dist/lib/devices/doctor-findings.js +1 -1
- package/dist/lib/devices/health-report.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/exec.js +13 -3
- 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/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/installations/shims.js +1 -1
- package/dist/lib/installations/versions.d.ts +40 -0
- package/dist/lib/installations/versions.js +57 -2
- 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/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/profiles.d.ts +7 -0
- package/dist/lib/profiles.js +26 -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/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/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/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-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/startup/command-registry.d.ts +4 -1
- package/dist/lib/startup/command-registry.js +11 -4
- package/dist/lib/teams/placement-probe.js +1 -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/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/cloud/rush.d.ts
CHANGED
|
@@ -25,21 +25,6 @@ export interface AccountManifest {
|
|
|
25
25
|
fp: string;
|
|
26
26
|
versions: AccountManifestEntry[];
|
|
27
27
|
}
|
|
28
|
-
/**
|
|
29
|
-
* Read the raw OAuth credentials for one Claude version. On Mac, prefer the
|
|
30
|
-
* Keychain blob (canonical). On Linux/CI, fall back to `.claude/.credentials.json`
|
|
31
|
-
* inside the version home (where the Linux Claude CLI stores its OAuth).
|
|
32
|
-
*
|
|
33
|
-
* Returns null when no credentials are findable — caller treats as "version
|
|
34
|
-
* is installed but not signed in" and skips it from the manifest.
|
|
35
|
-
*
|
|
36
|
-
* NOTE: the darwin branch returns the UNWRAPPED oauth sub-object (what the Rush
|
|
37
|
-
* server re-wraps). It is NOT the shape Claude Code reads back from
|
|
38
|
-
* `.credentials.json`. The lease exporter (crabbox/runtimes.ts) therefore only
|
|
39
|
-
* reuses the Linux `.credentials.json` branch here and reads the wrapped raw
|
|
40
|
-
* Keychain payload itself on darwin.
|
|
41
|
-
*/
|
|
42
|
-
export declare function readClaudeCredentialsBlob(home: string): Promise<string | null>;
|
|
43
28
|
/**
|
|
44
29
|
* Build a manifest of the user's local Claude installations to send on every
|
|
45
30
|
* cloud dispatch. The manifest is the contract the server uses to detect when
|
package/dist/lib/cloud/rush.js
CHANGED
|
@@ -13,7 +13,6 @@ import { resolveDispatchRepos, normalizeProviderStatus, MAX_IMAGES_PER_DISPATCH
|
|
|
13
13
|
import { parseSSE } from './stream.js';
|
|
14
14
|
import { listInstalledVersions, getVersionHomePath } from '../installations/versions.js';
|
|
15
15
|
import { getAccountInfo } from '../agents.js';
|
|
16
|
-
import { loadClaudeOauth } from '../accounting/usage.js';
|
|
17
16
|
import { selectBalancedVersion } from '../accounting/rotate.js';
|
|
18
17
|
const PROXY_BASE = process.env.RUSH_PROXY_BASE ?? 'https://api.prix.dev';
|
|
19
18
|
const USER_YAML = path.join(os.homedir(), '.rush', 'user.yaml');
|
|
@@ -112,40 +111,6 @@ function parsePromptCode(body) {
|
|
|
112
111
|
return null;
|
|
113
112
|
}
|
|
114
113
|
}
|
|
115
|
-
/**
|
|
116
|
-
* Read the raw OAuth credentials for one Claude version. On Mac, prefer the
|
|
117
|
-
* Keychain blob (canonical). On Linux/CI, fall back to `.claude/.credentials.json`
|
|
118
|
-
* inside the version home (where the Linux Claude CLI stores its OAuth).
|
|
119
|
-
*
|
|
120
|
-
* Returns null when no credentials are findable — caller treats as "version
|
|
121
|
-
* is installed but not signed in" and skips it from the manifest.
|
|
122
|
-
*
|
|
123
|
-
* NOTE: the darwin branch returns the UNWRAPPED oauth sub-object (what the Rush
|
|
124
|
-
* server re-wraps). It is NOT the shape Claude Code reads back from
|
|
125
|
-
* `.credentials.json`. The lease exporter (crabbox/runtimes.ts) therefore only
|
|
126
|
-
* reuses the Linux `.credentials.json` branch here and reads the wrapped raw
|
|
127
|
-
* Keychain payload itself on darwin.
|
|
128
|
-
*/
|
|
129
|
-
export async function readClaudeCredentialsBlob(home) {
|
|
130
|
-
if (process.platform === 'darwin') {
|
|
131
|
-
const oauth = await loadClaudeOauth(home);
|
|
132
|
-
if (oauth && oauth.accessToken) {
|
|
133
|
-
return JSON.stringify(oauth);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
const credsPath = path.join(home, '.claude', '.credentials.json');
|
|
137
|
-
try {
|
|
138
|
-
if (fs.existsSync(credsPath)) {
|
|
139
|
-
const raw = fs.readFileSync(credsPath, 'utf-8').trim();
|
|
140
|
-
if (raw)
|
|
141
|
-
return raw;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
catch {
|
|
145
|
-
// fall through to null
|
|
146
|
-
}
|
|
147
|
-
return null;
|
|
148
|
-
}
|
|
149
114
|
/**
|
|
150
115
|
* Build a manifest of the user's local Claude installations to send on every
|
|
151
116
|
* cloud dispatch. The manifest is the contract the server uses to detect when
|
|
@@ -85,8 +85,12 @@ export declare const CLAUDE_TOKEN_REMOTE = ".claude/.credentials.json";
|
|
|
85
85
|
* versioned home) uses a hash-suffixed service, so we try the bare service first,
|
|
86
86
|
* then enumerate installed version homes (preferring the account whose email
|
|
87
87
|
* matches `preferEmail`, so the token matches the `.claude.json` config we copy).
|
|
88
|
-
* Off macOS the local Claude CLI stores the
|
|
89
|
-
*
|
|
88
|
+
* Off macOS the local Claude CLI stores the wrapped rotating blob in
|
|
89
|
+
* `.credentials.json` already. Read that file here with a shape check
|
|
90
|
+
* (`claudeAiOauth.accessToken`). Do not reintroduce a shared helper that
|
|
91
|
+
* also serves Rush Cloud dispatch — dispatch is email-only (SING-1b) and
|
|
92
|
+
* the old `readClaudeCredentialsBlob` path was the #1767 TTY-banner leak
|
|
93
|
+
* (RUSH-2359).
|
|
90
94
|
*
|
|
91
95
|
* The reader/service/version helpers are injected so unit tests never touch the
|
|
92
96
|
* real Keychain.
|
|
@@ -19,7 +19,6 @@ import { getAccountInfo } from '../agents.js';
|
|
|
19
19
|
import { getKeychainToken } from '../secrets/index.js';
|
|
20
20
|
import { getClaudeKeychainService } from '../accounting/usage.js';
|
|
21
21
|
import { listInstalledVersions, getVersionHomePath } from '../installations/versions.js';
|
|
22
|
-
import { readClaudeCredentialsBlob } from '../cloud/rush.js';
|
|
23
22
|
export const LEASE_RUNTIMES = [
|
|
24
23
|
{ id: 'claude', label: 'Claude Code', localCandidates: ['.claude/.claude.json', '.claude.json'], remote: '.claude.json' },
|
|
25
24
|
{ id: 'codex', label: 'Codex CLI', localCandidates: ['.codex/auth.json'], remote: '.codex/auth.json' },
|
|
@@ -193,8 +192,12 @@ function isClaudeCredentialsBlob(s) {
|
|
|
193
192
|
* versioned home) uses a hash-suffixed service, so we try the bare service first,
|
|
194
193
|
* then enumerate installed version homes (preferring the account whose email
|
|
195
194
|
* matches `preferEmail`, so the token matches the `.claude.json` config we copy).
|
|
196
|
-
* Off macOS the local Claude CLI stores the
|
|
197
|
-
*
|
|
195
|
+
* Off macOS the local Claude CLI stores the wrapped rotating blob in
|
|
196
|
+
* `.credentials.json` already. Read that file here with a shape check
|
|
197
|
+
* (`claudeAiOauth.accessToken`). Do not reintroduce a shared helper that
|
|
198
|
+
* also serves Rush Cloud dispatch — dispatch is email-only (SING-1b) and
|
|
199
|
+
* the old `readClaudeCredentialsBlob` path was the #1767 TTY-banner leak
|
|
200
|
+
* (RUSH-2359).
|
|
198
201
|
*
|
|
199
202
|
* The reader/service/version helpers are injected so unit tests never touch the
|
|
200
203
|
* real Keychain.
|
|
@@ -248,9 +251,22 @@ export async function resolveClaudeCredentialsBlob(opts) {
|
|
|
248
251
|
}
|
|
249
252
|
return null;
|
|
250
253
|
}
|
|
251
|
-
// Off darwin: the local Claude CLI
|
|
254
|
+
// Off darwin: the local Claude CLI stores the wrapped rotating blob on disk.
|
|
255
|
+
// Read it directly so a file-based setup-token (Rush Cloud dispatch) is not
|
|
256
|
+
// mistaken for a native OAuth login that SING-1b must refuse to copy.
|
|
252
257
|
const home = process.env.AGENTS_REAL_HOME || os.homedir();
|
|
253
|
-
|
|
258
|
+
const credsPath = path.join(home, '.claude', '.credentials.json');
|
|
259
|
+
try {
|
|
260
|
+
if (fs.existsSync(credsPath)) {
|
|
261
|
+
const raw = fs.readFileSync(credsPath, 'utf-8').trim();
|
|
262
|
+
if (raw && isClaudeCredentialsBlob(raw))
|
|
263
|
+
return raw;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
catch {
|
|
267
|
+
/* no readable credentials file */
|
|
268
|
+
}
|
|
269
|
+
return null;
|
|
254
270
|
}
|
|
255
271
|
/**
|
|
256
272
|
* The credential-provisioning snippet for a `--lease` box.
|
|
@@ -11,7 +11,7 @@ import * as fs from 'fs';
|
|
|
11
11
|
import * as path from 'path';
|
|
12
12
|
import * as os from 'os';
|
|
13
13
|
import { getDaemonDir } from '../state.js';
|
|
14
|
-
import { isolatedHomeSuffix, namespacedServiceLabel, serviceManifestHomeEnv } from '../service-manifest.js';
|
|
14
|
+
import { isolatedHomeSuffix, namespacedServiceLabel, serviceManifestHomeEnv, serviceManagerRegistrationAllowed } from '../service-manifest.js';
|
|
15
15
|
import { isAlive, killTree, backgroundSpawnOptions, waitForExit } from '../platform/index.js';
|
|
16
16
|
import { listJobs as listAllJobs } from '../scheduling/routines.js';
|
|
17
17
|
import { syncAllProjectRoutines } from '../routines-project.js';
|
|
@@ -1763,6 +1763,9 @@ export { getAgentsBinPath };
|
|
|
1763
1763
|
* in fact up. Returns null when the service isn't running or the query fails.
|
|
1764
1764
|
*/
|
|
1765
1765
|
function readServiceManagerPid(platform = os.platform()) {
|
|
1766
|
+
const reg = serviceManagerRegistrationAllowed();
|
|
1767
|
+
if (!reg.allowed)
|
|
1768
|
+
return null;
|
|
1766
1769
|
try {
|
|
1767
1770
|
if (platform === 'linux') {
|
|
1768
1771
|
const out = execFileSync('systemctl', ['--user', 'show', '-p', 'MainPID', '--value', daemonSystemdUnitName()], { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
|
|
@@ -1912,61 +1915,72 @@ function startDaemonLocked(agentsBin, releaseLock) {
|
|
|
1912
1915
|
releaseLock();
|
|
1913
1916
|
return startDetached({ agentsBin });
|
|
1914
1917
|
};
|
|
1918
|
+
const reg = serviceManagerRegistrationAllowed();
|
|
1915
1919
|
if (platform === 'darwin') {
|
|
1916
|
-
|
|
1917
|
-
const plistPath = getLaunchdPlistPath();
|
|
1918
|
-
const plistDir = path.dirname(plistPath);
|
|
1919
|
-
if (!fs.existsSync(plistDir)) {
|
|
1920
|
-
fs.mkdirSync(plistDir, { recursive: true });
|
|
1921
|
-
}
|
|
1922
|
-
// The plist carries no credential (RUSH-1759 — the daemon reads the OAuth
|
|
1923
|
-
// token itself at startup); still create owner-only atomically to match the
|
|
1924
|
-
// detached path and keep the log/PATH surface owner-private.
|
|
1925
|
-
writeOwnerOnlyServiceManifest(plistPath, generateLaunchdPlist(agentsBin));
|
|
1920
|
+
if (reg.allowed) {
|
|
1926
1921
|
try {
|
|
1927
|
-
|
|
1922
|
+
const plistPath = getLaunchdPlistPath();
|
|
1923
|
+
const plistDir = path.dirname(plistPath);
|
|
1924
|
+
if (!fs.existsSync(plistDir)) {
|
|
1925
|
+
fs.mkdirSync(plistDir, { recursive: true });
|
|
1926
|
+
}
|
|
1927
|
+
// The plist carries no credential (RUSH-1759 — the daemon reads the OAuth
|
|
1928
|
+
// token itself at startup); still create owner-only atomically to match the
|
|
1929
|
+
// detached path and keep the log/PATH surface owner-private.
|
|
1930
|
+
writeOwnerOnlyServiceManifest(plistPath, generateLaunchdPlist(agentsBin));
|
|
1931
|
+
try {
|
|
1932
|
+
execFileSync('launchctl', ['unload', plistPath], { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'] });
|
|
1933
|
+
}
|
|
1934
|
+
catch { /* not loaded, expected */ }
|
|
1935
|
+
// launchctl prints `Load failed:` and exits 0 when the label is in a
|
|
1936
|
+
// stuck state from a prior session — so a zero exit code isn't proof
|
|
1937
|
+
// of success. If no pid materializes within the window, give up on
|
|
1938
|
+
// launchd and fall through to a plain detached spawn.
|
|
1939
|
+
execFileSync('launchctl', ['load', plistPath], { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'] });
|
|
1940
|
+
// Launch issued — the child needs this lock to claim (RUSH-2417).
|
|
1941
|
+
releaseLock();
|
|
1942
|
+
const pid = waitForPid(3000) ?? readServiceManagerPid();
|
|
1943
|
+
if (pid)
|
|
1944
|
+
return { pid, method: 'launchd' };
|
|
1945
|
+
// launchctl claimed success but nothing ran. Fall through.
|
|
1946
|
+
}
|
|
1947
|
+
catch {
|
|
1948
|
+
// load threw — fall through to detached spawn
|
|
1928
1949
|
}
|
|
1929
|
-
catch { /* not loaded, expected */ }
|
|
1930
|
-
// launchctl prints `Load failed:` and exits 0 when the label is in a
|
|
1931
|
-
// stuck state from a prior session — so a zero exit code isn't proof
|
|
1932
|
-
// of success. If no pid materializes within the window, give up on
|
|
1933
|
-
// launchd and fall through to a plain detached spawn.
|
|
1934
|
-
execFileSync('launchctl', ['load', plistPath], { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'] });
|
|
1935
|
-
// Launch issued — the child needs this lock to claim (RUSH-2417).
|
|
1936
|
-
releaseLock();
|
|
1937
|
-
const pid = waitForPid(3000) ?? readServiceManagerPid();
|
|
1938
|
-
if (pid)
|
|
1939
|
-
return { pid, method: 'launchd' };
|
|
1940
|
-
// launchctl claimed success but nothing ran. Fall through.
|
|
1941
1950
|
}
|
|
1942
|
-
|
|
1943
|
-
|
|
1951
|
+
else {
|
|
1952
|
+
process.stderr.write(`[agents] ${reg.reason}\n`);
|
|
1944
1953
|
}
|
|
1945
1954
|
return detachedFallback();
|
|
1946
1955
|
}
|
|
1947
1956
|
if (platform === 'linux') {
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
fs.
|
|
1957
|
+
if (reg.allowed) {
|
|
1958
|
+
try {
|
|
1959
|
+
const unitPath = getSystemdUnitPath();
|
|
1960
|
+
const unitDir = path.dirname(unitPath);
|
|
1961
|
+
if (!fs.existsSync(unitDir)) {
|
|
1962
|
+
fs.mkdirSync(unitDir, { recursive: true });
|
|
1963
|
+
}
|
|
1964
|
+
// Carries no credential (RUSH-1759 — the daemon reads the OAuth token
|
|
1965
|
+
// itself at startup); owner-only to keep the PATH/log surface private.
|
|
1966
|
+
writeOwnerOnlyServiceManifest(unitPath, generateSystemdUnit(agentsBin));
|
|
1967
|
+
execFileSync('systemctl', ['--user', 'daemon-reload'], { encoding: 'utf-8' });
|
|
1968
|
+
execFileSync('systemctl', ['--user', 'enable', daemonSystemdUnitName()], { encoding: 'utf-8' });
|
|
1969
|
+
execFileSync('systemctl', ['--user', 'start', daemonSystemdUnitName()], { encoding: 'utf-8' });
|
|
1970
|
+
// Launch issued — the child needs this lock to claim (RUSH-2417).
|
|
1971
|
+
releaseLock();
|
|
1972
|
+
const pid = waitForPid(3000) ?? readServiceManagerPid();
|
|
1973
|
+
if (pid)
|
|
1974
|
+
return { pid, method: 'systemd' };
|
|
1975
|
+
// systemctl returned success but no PID surfaced — fall through to a
|
|
1976
|
+
// plain detached spawn rather than reporting a null PID.
|
|
1977
|
+
}
|
|
1978
|
+
catch {
|
|
1979
|
+
// start threw — fall through to detached spawn
|
|
1953
1980
|
}
|
|
1954
|
-
// Carries no credential (RUSH-1759 — the daemon reads the OAuth token
|
|
1955
|
-
// itself at startup); owner-only to keep the PATH/log surface private.
|
|
1956
|
-
writeOwnerOnlyServiceManifest(unitPath, generateSystemdUnit(agentsBin));
|
|
1957
|
-
execFileSync('systemctl', ['--user', 'daemon-reload'], { encoding: 'utf-8' });
|
|
1958
|
-
execFileSync('systemctl', ['--user', 'enable', daemonSystemdUnitName()], { encoding: 'utf-8' });
|
|
1959
|
-
execFileSync('systemctl', ['--user', 'start', daemonSystemdUnitName()], { encoding: 'utf-8' });
|
|
1960
|
-
// Launch issued — the child needs this lock to claim (RUSH-2417).
|
|
1961
|
-
releaseLock();
|
|
1962
|
-
const pid = waitForPid(3000) ?? readServiceManagerPid();
|
|
1963
|
-
if (pid)
|
|
1964
|
-
return { pid, method: 'systemd' };
|
|
1965
|
-
// systemctl returned success but no PID surfaced — fall through to a
|
|
1966
|
-
// plain detached spawn rather than reporting a null PID.
|
|
1967
1981
|
}
|
|
1968
|
-
|
|
1969
|
-
|
|
1982
|
+
else {
|
|
1983
|
+
process.stderr.write(`[agents] ${reg.reason}\n`);
|
|
1970
1984
|
}
|
|
1971
1985
|
return detachedFallback();
|
|
1972
1986
|
}
|
|
@@ -2261,29 +2275,43 @@ export function stopDaemon() {
|
|
|
2261
2275
|
const released = [];
|
|
2262
2276
|
const surviving = [];
|
|
2263
2277
|
let escalated = false;
|
|
2278
|
+
const reg = serviceManagerRegistrationAllowed();
|
|
2264
2279
|
if (platform === 'darwin') {
|
|
2265
2280
|
const plistPath = getLaunchdPlistPath();
|
|
2266
2281
|
if (fs.existsSync(plistPath)) {
|
|
2282
|
+
if (reg.allowed) {
|
|
2283
|
+
try {
|
|
2284
|
+
execFileSync('launchctl', ['unload', plistPath], { encoding: 'utf-8' });
|
|
2285
|
+
}
|
|
2286
|
+
catch (err) {
|
|
2287
|
+
if (process.env.AGENTS_DEBUG) {
|
|
2288
|
+
console.error(`[debug] launchctl unload failed: ${err.message}`);
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2291
|
+
}
|
|
2292
|
+
else {
|
|
2293
|
+
process.stderr.write(`[agents] ${reg.reason}\n`);
|
|
2294
|
+
}
|
|
2267
2295
|
try {
|
|
2268
|
-
execFileSync('launchctl', ['unload', plistPath], { encoding: 'utf-8' });
|
|
2269
2296
|
fs.unlinkSync(plistPath);
|
|
2270
2297
|
}
|
|
2298
|
+
catch { /* plist already removed */ }
|
|
2299
|
+
}
|
|
2300
|
+
}
|
|
2301
|
+
if (platform === 'linux') {
|
|
2302
|
+
if (reg.allowed) {
|
|
2303
|
+
try {
|
|
2304
|
+
execFileSync('systemctl', ['--user', 'stop', daemonSystemdUnitName()], { encoding: 'utf-8' });
|
|
2305
|
+
execFileSync('systemctl', ['--user', 'disable', daemonSystemdUnitName()], { encoding: 'utf-8' });
|
|
2306
|
+
}
|
|
2271
2307
|
catch (err) {
|
|
2272
2308
|
if (process.env.AGENTS_DEBUG) {
|
|
2273
|
-
console.error(`[debug]
|
|
2309
|
+
console.error(`[debug] systemctl stop failed: ${err.message}`);
|
|
2274
2310
|
}
|
|
2275
2311
|
}
|
|
2276
2312
|
}
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
try {
|
|
2280
|
-
execFileSync('systemctl', ['--user', 'stop', daemonSystemdUnitName()], { encoding: 'utf-8' });
|
|
2281
|
-
execFileSync('systemctl', ['--user', 'disable', daemonSystemdUnitName()], { encoding: 'utf-8' });
|
|
2282
|
-
}
|
|
2283
|
-
catch (err) {
|
|
2284
|
-
if (process.env.AGENTS_DEBUG) {
|
|
2285
|
-
console.error(`[debug] systemctl stop failed: ${err.message}`);
|
|
2286
|
-
}
|
|
2313
|
+
else {
|
|
2314
|
+
process.stderr.write(`[agents] ${reg.reason}\n`);
|
|
2287
2315
|
}
|
|
2288
2316
|
const unitPath = getSystemdUnitPath();
|
|
2289
2317
|
if (fs.existsSync(unitPath)) {
|
|
@@ -74,6 +74,23 @@ interface RoutineAttempt {
|
|
|
74
74
|
* but the gate should have paused such a routine before it ever spawned.
|
|
75
75
|
*/
|
|
76
76
|
export declare function routineSpawnCwd(config: Pick<JobConfig, 'name' | 'project' | 'cwd' | 'agent' | 'workflow' | 'command'>): string;
|
|
77
|
+
/**
|
|
78
|
+
* Bake the daemon-job argv skeleton from AGENT_COMMANDS.
|
|
79
|
+
*
|
|
80
|
+
* The two tables used to be independent copies of the same launch decision.
|
|
81
|
+
* Argv now comes from AGENT_COMMANDS.base / promptFlag / jsonFlags / modeFlags.
|
|
82
|
+
* Two documented exceptions, not a second table:
|
|
83
|
+
* - kimi uses `--prompt` (long form). `-p` is the exec alias; combining
|
|
84
|
+
* either with --plan/--auto/--yolo aborts, so routineModeArgs emits no
|
|
85
|
+
* mode flag. The daemon has always spawned the long form.
|
|
86
|
+
* - claude places `--verbose` (from jsonFlags) before the prompt and bakes
|
|
87
|
+
* modeFlags.plan so claudeAdapter.routineModeArgs can splice
|
|
88
|
+
* plan → acceptEdits / auto / skip.
|
|
89
|
+
*
|
|
90
|
+
* Returns undefined when `agent` is outside ROUTINE_AGENT_IDS (gemini, grok,
|
|
91
|
+
* and every other harness the daemon does not fire locally).
|
|
92
|
+
*/
|
|
93
|
+
export declare function bakeRoutineArgv(agent: string): string[] | undefined;
|
|
77
94
|
/** Build the full CLI argv for executing a job, applying mode, model, and permission flags. */
|
|
78
95
|
export declare function buildJobCommand(config: JobConfig, resolvedPrompt: string, forwardAccount?: boolean): string[];
|
|
79
96
|
/**
|
|
@@ -161,7 +178,7 @@ export declare function buildHostDispatchOptions(config: JobConfig, ctx: {
|
|
|
161
178
|
runDir: string;
|
|
162
179
|
detached: boolean;
|
|
163
180
|
}): import('../hosts/run-target.js').HostPromptRun;
|
|
164
|
-
export declare function dispatchesViaAgentsRun(config: Pick<JobConfig, 'workflow' | 'resume'>): boolean;
|
|
181
|
+
export declare function dispatchesViaAgentsRun(config: Pick<JobConfig, 'workflow' | 'resume' | 'agent'>): boolean;
|
|
165
182
|
/**
|
|
166
183
|
* Merge sandbox/base env with the canonical per-version exec env
|
|
167
184
|
* (CLAUDE_CONFIG_DIR / CODEX_HOME / …) so routines share account isolation
|
|
@@ -21,7 +21,7 @@ import { getCliLaunch, getAgentsBinDir } from '../cli-entry.js';
|
|
|
21
21
|
import { resolveJobPrompt, parseTimeout, writeRunMeta, listRuns, getJobRunsDir, getRunDir, checkJobDeviceEligibility, finalizeRunMeta, resolveJobExecutionContext, slotRunId, claimRunSlot, readRunMeta, resolveHostStrategy, } from '../scheduling/routines.js';
|
|
22
22
|
import { getRunsDir, getUserAgentsDir, readMeta, getDaemonDir } from '../state.js';
|
|
23
23
|
import { shortCodexHome } from '../codex-home.js';
|
|
24
|
-
import { prepareJobHome, buildSpawnEnv, getJobHomePath } from '../sandbox.js';
|
|
24
|
+
import { prepareJobHome, buildSpawnEnv, getJobHomePath, assertSandboxForwardsHostGhAuth } from '../sandbox.js';
|
|
25
25
|
import { resolveModel, buildReasoningFlags } from '../models.js';
|
|
26
26
|
import { createTimer, redactPrompt, emitRoutineEnd } from '../feed/events.js';
|
|
27
27
|
import { resolveHarnessAdapter } from '../harness/index.js';
|
|
@@ -39,7 +39,8 @@ import { resolveClaudeSetupToken } from '../claude-account-token.js';
|
|
|
39
39
|
import { getConfiguredRunStrategy, resolveRunVersion, resolveAccountVersion, rotationFailoverChain, readinessFromCandidate, formatNoHealthyAccountError, } from '../accounting/rotate.js';
|
|
40
40
|
import { readAuthHealth, isDeadVerdict } from '../auth-health.js';
|
|
41
41
|
import { machineId } from '../machine-id.js';
|
|
42
|
-
import { isSelfUpdatingAgent,
|
|
42
|
+
import { isSelfUpdatingAgent, ROUTINE_AGENT_IDS, isAgentHardDeprecated, hardDeprecationError } from '../agents.js';
|
|
43
|
+
import { isCustomHarnessName, readProfile } from '../profiles.js';
|
|
43
44
|
export class RoutineAlreadyRunningError extends Error {
|
|
44
45
|
constructor(jobName, runId) {
|
|
45
46
|
super(`Routine '${jobName}' already has a running execution (${runId})`);
|
|
@@ -308,7 +309,7 @@ function allocateRoutineAttempt(config, trigger) {
|
|
|
308
309
|
// harness leaves the command table (gemini did), and the legacy routine must
|
|
309
310
|
// still land a visible 'blocked' record, never a generic buildJobCommand
|
|
310
311
|
// failure (RUSH-2202).
|
|
311
|
-
if (!config.workflow && config.agent && isAgentHardDeprecated(config.agent)) {
|
|
312
|
+
if (!config.workflow && config.agent && !isCustomHarnessName(config.agent) && isAgentHardDeprecated(config.agent)) {
|
|
312
313
|
const reason = hardDeprecationError(config.agent);
|
|
313
314
|
return {
|
|
314
315
|
proceed: false,
|
|
@@ -488,6 +489,48 @@ export function routineSpawnCwd(config) {
|
|
|
488
489
|
const ctx = resolveJobExecutionContext(config, { mode: 'local' });
|
|
489
490
|
return ctx.absoluteCwd ?? os.homedir();
|
|
490
491
|
}
|
|
492
|
+
/**
|
|
493
|
+
* Bake the daemon-job argv skeleton from AGENT_COMMANDS.
|
|
494
|
+
*
|
|
495
|
+
* The two tables used to be independent copies of the same launch decision.
|
|
496
|
+
* Argv now comes from AGENT_COMMANDS.base / promptFlag / jsonFlags / modeFlags.
|
|
497
|
+
* Two documented exceptions, not a second table:
|
|
498
|
+
* - kimi uses `--prompt` (long form). `-p` is the exec alias; combining
|
|
499
|
+
* either with --plan/--auto/--yolo aborts, so routineModeArgs emits no
|
|
500
|
+
* mode flag. The daemon has always spawned the long form.
|
|
501
|
+
* - claude places `--verbose` (from jsonFlags) before the prompt and bakes
|
|
502
|
+
* modeFlags.plan so claudeAdapter.routineModeArgs can splice
|
|
503
|
+
* plan → acceptEdits / auto / skip.
|
|
504
|
+
*
|
|
505
|
+
* Returns undefined when `agent` is outside ROUTINE_AGENT_IDS (gemini, grok,
|
|
506
|
+
* and every other harness the daemon does not fire locally).
|
|
507
|
+
*/
|
|
508
|
+
export function bakeRoutineArgv(agent) {
|
|
509
|
+
if (!ROUTINE_AGENT_IDS.includes(agent))
|
|
510
|
+
return undefined;
|
|
511
|
+
const template = AGENT_COMMANDS[agent];
|
|
512
|
+
if (!template)
|
|
513
|
+
return undefined;
|
|
514
|
+
const json = template.jsonFlags ?? [];
|
|
515
|
+
const cmd = [...template.base];
|
|
516
|
+
if (agent === 'kimi') {
|
|
517
|
+
cmd.push('--prompt', '{prompt}', ...json);
|
|
518
|
+
return cmd;
|
|
519
|
+
}
|
|
520
|
+
if (agent === 'claude') {
|
|
521
|
+
const verbose = json.includes('--verbose') ? ['--verbose'] : [];
|
|
522
|
+
const jsonRest = json.filter((flag) => flag !== '--verbose');
|
|
523
|
+
cmd.push(template.promptFlag, ...verbose, '{prompt}', ...jsonRest, ...(template.modeFlags.plan ?? []));
|
|
524
|
+
return cmd;
|
|
525
|
+
}
|
|
526
|
+
if (template.promptFlag === 'positional') {
|
|
527
|
+
cmd.push('{prompt}', ...json);
|
|
528
|
+
}
|
|
529
|
+
else {
|
|
530
|
+
cmd.push(template.promptFlag, '{prompt}', ...json);
|
|
531
|
+
}
|
|
532
|
+
return cmd;
|
|
533
|
+
}
|
|
491
534
|
/** Build the full CLI argv for executing a job, applying mode, model, and permission flags. */
|
|
492
535
|
export function buildJobCommand(config, resolvedPrompt, forwardAccount = true) {
|
|
493
536
|
// Workflow branch: delegate to `agents run <workflow>` which handles subagent
|
|
@@ -514,9 +557,25 @@ export function buildJobCommand(config, resolvedPrompt, forwardAccount = true) {
|
|
|
514
557
|
cmd.push('--account', config.account);
|
|
515
558
|
return cmd;
|
|
516
559
|
}
|
|
517
|
-
|
|
560
|
+
// Custom-harness branch: delegate to `agents run <name>`, which owns profile
|
|
561
|
+
// resolution (host binary, model env, provider auth) — the same delegation
|
|
562
|
+
// the workflow and resume branches use. The profile pins its own version and
|
|
563
|
+
// auth, so no command template, binary pinning, or account-env injection
|
|
564
|
+
// applies here.
|
|
565
|
+
if (isCustomHarnessName(agent)) {
|
|
566
|
+
const cmd = ['agents', 'run', agent, resolvedPrompt, '--mode', config.mode];
|
|
567
|
+
if (config.account && forwardAccount)
|
|
568
|
+
cmd.push('--account', config.account);
|
|
569
|
+
return cmd;
|
|
570
|
+
}
|
|
571
|
+
const template = bakeRoutineArgv(agent);
|
|
518
572
|
if (!template) {
|
|
519
|
-
|
|
573
|
+
// A name outside both tables was either never valid or was a custom
|
|
574
|
+
// harness whose profile has since been deleted — validateJob accepted it
|
|
575
|
+
// against a profile that existed then. Name the repair, not just the miss.
|
|
576
|
+
throw new Error(`Unsupported agent for daemon jobs: ${agent}. ` +
|
|
577
|
+
`If '${agent}' was a custom harness, its profile no longer exists on this device — ` +
|
|
578
|
+
`recreate it (agents harness add ${agent} ...) or point the routine at another agent.`);
|
|
520
579
|
}
|
|
521
580
|
let cmd = template.map((part) => part.replace('{prompt}', resolvedPrompt));
|
|
522
581
|
// Canonicalize mode (accepts legacy `full` as alias for `skip`).
|
|
@@ -554,7 +613,8 @@ export function buildJobCommand(config, resolvedPrompt, forwardAccount = true) {
|
|
|
554
613
|
* Reasoning level (config.config.reasoning) maps to per-agent flags via models.ts.
|
|
555
614
|
*/
|
|
556
615
|
function appendModelAndReasoning(cmd, config) {
|
|
557
|
-
// Only called from buildJobCommand's agent path
|
|
616
|
+
// Only called from buildJobCommand's agent path AFTER the custom-harness
|
|
617
|
+
// branch returned — config.agent is a native id here.
|
|
558
618
|
const agent = config.agent;
|
|
559
619
|
const model = config.config?.model;
|
|
560
620
|
if (model) {
|
|
@@ -636,6 +696,8 @@ function transcriptBasePath(runDir) {
|
|
|
636
696
|
export function snapshotRoutineTranscriptBase(meta, runDir, overlayHome) {
|
|
637
697
|
if (!meta.agent)
|
|
638
698
|
return;
|
|
699
|
+
// A host/cloud custom-harness run records the harness name, which has no
|
|
700
|
+
// transcript spec — the null-specs guard below skips it, as before.
|
|
639
701
|
const specs = ROUTINE_TRANSCRIPT_SPECS[meta.agent];
|
|
640
702
|
if (!specs)
|
|
641
703
|
return;
|
|
@@ -674,6 +736,8 @@ function readTranscriptBase(runDir) {
|
|
|
674
736
|
export function archiveRoutineTranscripts(meta, runDir, overlayHome) {
|
|
675
737
|
if (!meta.agent)
|
|
676
738
|
return;
|
|
739
|
+
// A host/cloud custom-harness run records the harness name, which has no
|
|
740
|
+
// transcript spec — the null-specs guard below skips it, as before.
|
|
677
741
|
const specs = ROUTINE_TRANSCRIPT_SPECS[meta.agent];
|
|
678
742
|
if (!specs)
|
|
679
743
|
return;
|
|
@@ -808,6 +872,13 @@ export async function resolveRoutineLaunch(config, cwd = process.cwd(), deps = {
|
|
|
808
872
|
if (config.workflow) {
|
|
809
873
|
return { chain: [], rotation: null, pinned: false };
|
|
810
874
|
}
|
|
875
|
+
if (config.agent && isCustomHarnessName(config.agent)) {
|
|
876
|
+
// A custom harness pins its own version/auth in the profile, so there is
|
|
877
|
+
// no version/account chain to resolve here — `agents run <name>` owns it
|
|
878
|
+
// (matching exec's "strategy ignored: custom harness pins its own
|
|
879
|
+
// version/auth").
|
|
880
|
+
return { chain: [], rotation: null, pinned: false };
|
|
881
|
+
}
|
|
811
882
|
// resolveRoutineLaunch is only called for agent jobs (workflow returns above;
|
|
812
883
|
// command jobs branch out of execute*Job before reaching this).
|
|
813
884
|
const agent = config.agent;
|
|
@@ -1012,7 +1083,7 @@ export function buildHostDispatchOptions(config, ctx) {
|
|
|
1012
1083
|
};
|
|
1013
1084
|
}
|
|
1014
1085
|
export function dispatchesViaAgentsRun(config) {
|
|
1015
|
-
return Boolean(config.workflow || config.resume);
|
|
1086
|
+
return Boolean(config.workflow || config.resume || (config.agent && isCustomHarnessName(config.agent)));
|
|
1016
1087
|
}
|
|
1017
1088
|
/**
|
|
1018
1089
|
* Merge sandbox/base env with the canonical per-version exec env
|
|
@@ -1214,7 +1285,11 @@ async function executeJobPlaced(config, deps, attempt) {
|
|
|
1214
1285
|
// the agent's config dir, and the sandbox overlay home has only a freshly-generated
|
|
1215
1286
|
// config with no session store. So a resume job is never sandboxed, regardless of
|
|
1216
1287
|
// `config.sandbox` (see the resume branch in buildJobCommand).
|
|
1217
|
-
|
|
1288
|
+
// Resume needs the REAL home (session store); a custom harness needs it too —
|
|
1289
|
+
// the delegated `agents run <name>` resolves ~/.agents (profiles, setup
|
|
1290
|
+
// sentinel, version homes) from HOME, which the overlay would hide. Exec
|
|
1291
|
+
// still isolates the run in the host version home it swaps HOME into.
|
|
1292
|
+
const useSandbox = config.sandbox !== false && !config.resume && !(config.agent && isCustomHarnessName(config.agent));
|
|
1218
1293
|
const overlayHome = useSandbox ? prepareJobHome(config) : undefined;
|
|
1219
1294
|
const runId = attempt.runId;
|
|
1220
1295
|
const runDir = getRunDir(config.name, runId);
|
|
@@ -1225,7 +1300,11 @@ async function executeJobPlaced(config, deps, attempt) {
|
|
|
1225
1300
|
// Workflows run via `agents run <workflow>` which delegates to claude under the hood.
|
|
1226
1301
|
// Use 'claude' as the effective agent for report extraction and metadata when workflow is set.
|
|
1227
1302
|
// (command jobs branched out earlier, so config.agent is set on the non-workflow path.)
|
|
1228
|
-
const effectiveAgent = config.workflow
|
|
1303
|
+
const effectiveAgent = config.workflow
|
|
1304
|
+
? 'claude'
|
|
1305
|
+
: isCustomHarnessName(config.agent)
|
|
1306
|
+
? readProfile(config.agent).host.agent
|
|
1307
|
+
: config.agent;
|
|
1229
1308
|
if (!dispatchesViaAgentsRun(config)) {
|
|
1230
1309
|
const { findUnifiedAccount, resolveAccountSelection, resolveCredentialAccount } = await import('../account-registry.js');
|
|
1231
1310
|
const meta = readMeta();
|
|
@@ -1238,6 +1317,9 @@ async function executeJobPlaced(config, deps, attempt) {
|
|
|
1238
1317
|
Object.assign(baseEnv, resolveCredentialAccount(selectedAccount, effectiveAgent).env);
|
|
1239
1318
|
}
|
|
1240
1319
|
}
|
|
1320
|
+
// RUSH-2860: if this host holds gh auth, the sandbox child MUST see it.
|
|
1321
|
+
if (useSandbox)
|
|
1322
|
+
assertSandboxForwardsHostGhAuth(baseEnv);
|
|
1241
1323
|
const meta = {
|
|
1242
1324
|
jobName: config.name,
|
|
1243
1325
|
runId,
|
|
@@ -1728,7 +1810,11 @@ async function executeJobDetachedClaimed(config, attempt, hooks) {
|
|
|
1728
1810
|
// the agent's config dir, and the sandbox overlay home has only a freshly-generated
|
|
1729
1811
|
// config with no session store. So a resume job is never sandboxed, regardless of
|
|
1730
1812
|
// `config.sandbox` (see the resume branch in buildJobCommand).
|
|
1731
|
-
|
|
1813
|
+
// Resume needs the REAL home (session store); a custom harness needs it too —
|
|
1814
|
+
// the delegated `agents run <name>` resolves ~/.agents (profiles, setup
|
|
1815
|
+
// sentinel, version homes) from HOME, which the overlay would hide. Exec
|
|
1816
|
+
// still isolates the run in the host version home it swaps HOME into.
|
|
1817
|
+
const useSandbox = config.sandbox !== false && !config.resume && !(config.agent && isCustomHarnessName(config.agent));
|
|
1732
1818
|
const overlayHome = useSandbox ? prepareJobHome(config) : undefined;
|
|
1733
1819
|
const runId = attempt.runId;
|
|
1734
1820
|
const runDir = getRunDir(config.name, runId);
|
|
@@ -1755,7 +1841,15 @@ async function executeJobDetachedClaimed(config, attempt, hooks) {
|
|
|
1755
1841
|
})()
|
|
1756
1842
|
// Non-command path only: config.agent is always set here (command/workflow branch earlier).
|
|
1757
1843
|
: buildRoutineSpawnEnv(baseEnv, config.agent, version, config.timezone, overlayHome);
|
|
1758
|
-
|
|
1844
|
+
// RUSH-2860: if this host holds gh auth, the sandbox child MUST see it —
|
|
1845
|
+
// otherwise monitors runs records ok while every gh call inside fails.
|
|
1846
|
+
if (useSandbox)
|
|
1847
|
+
assertSandboxForwardsHostGhAuth(spawnEnv);
|
|
1848
|
+
const effectiveAgent = config.workflow
|
|
1849
|
+
? 'claude'
|
|
1850
|
+
: isCustomHarnessName(config.agent)
|
|
1851
|
+
? readProfile(config.agent).host.agent
|
|
1852
|
+
: config.agent;
|
|
1759
1853
|
const meta = {
|
|
1760
1854
|
jobName: config.name,
|
|
1761
1855
|
runId,
|
|
@@ -42,7 +42,7 @@ import { AGENTS, ALL_AGENT_IDS, supportsAccountInspection } from '../agents.js';
|
|
|
42
42
|
import { blocksLocalScripts } from '../platform/winpath.js';
|
|
43
43
|
import { loginHint } from '../signin-badge.js';
|
|
44
44
|
import { CONFIG_ENV_ISOLATED_AGENTS } from '../installations/shims.js';
|
|
45
|
-
import { padToWidth, stringWidth } from '../
|
|
45
|
+
import { padToWidth, stringWidth } from '../text/width.js';
|
|
46
46
|
import { windowsSshEnrollmentProblem } from './windows-ssh-enrollment.js';
|
|
47
47
|
const AGENT_NAMES = Object.fromEntries(ALL_AGENT_IDS.map((id) => [id, AGENTS[id].name]));
|
|
48
48
|
/** Agents with NO per-version credential isolation: their login is shared across
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
import { padToWidth, stringWidth, terminalWidth, truncateToWidth } from '../
|
|
2
|
+
import { padToWidth, stringWidth, terminalWidth, truncateToWidth } from '../text/width.js';
|
|
3
3
|
import { fmtBytes, headroom } from './health.js';
|
|
4
4
|
import { formatCheckedAge } from '../auth-health.js';
|
|
5
5
|
import { compareFleetInventories } from './fleet-divergence.js';
|
package/dist/lib/drift-sync.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Interactive drift-sync flow — the single "we detected drift, want to fix it?"
|
|
3
|
-
* action, shared by `agents status`, `agents doctor`, and the menu-bar "NEEDS
|
|
3
|
+
* action, shared by `agents sync status`, `agents doctor`, and the menu-bar "NEEDS
|
|
4
4
|
* SYNC" row.
|
|
5
5
|
*
|
|
6
6
|
* It composes existing pieces, re-implementing nothing:
|
package/dist/lib/drift-sync.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Interactive drift-sync flow — the single "we detected drift, want to fix it?"
|
|
3
|
-
* action, shared by `agents status`, `agents doctor`, and the menu-bar "NEEDS
|
|
3
|
+
* action, shared by `agents sync status`, `agents doctor`, and the menu-bar "NEEDS
|
|
4
4
|
* SYNC" row.
|
|
5
5
|
*
|
|
6
6
|
* It composes existing pieces, re-implementing nothing:
|
|
@@ -115,7 +115,7 @@ export async function promptDriftSync(opts = {}) {
|
|
|
115
115
|
if (opts.yes || !isInteractiveTerminal()) {
|
|
116
116
|
if (!opts.yes) {
|
|
117
117
|
// Non-TTY without --yes: report, don't act, don't throw.
|
|
118
|
-
console.log(chalk.gray('\nRun `agents status --yes` to sync, or `agents status` in a terminal to choose.'));
|
|
118
|
+
console.log(chalk.gray('\nRun `agents sync status --yes` to sync, or `agents sync status` in a terminal to choose.'));
|
|
119
119
|
return base;
|
|
120
120
|
}
|
|
121
121
|
const systemPulled = await pullSystem(status);
|
package/dist/lib/drift.d.ts
CHANGED
|
@@ -62,7 +62,7 @@ export interface DriftSummary {
|
|
|
62
62
|
* True when the install is out of sync: any installed version is stale,
|
|
63
63
|
* never-synced, carries unwired hooks, or has a broken generated hook runtime,
|
|
64
64
|
* OR a source layer is behind origin.
|
|
65
|
-
* `agents doctor` surfaces it as "run `agents status`"; `agents doctor --check`
|
|
65
|
+
* `agents doctor` surfaces it as "run `agents sync status`"; `agents doctor --check`
|
|
66
66
|
* maps it to a non-zero exit. Orphans are a `prune` concern, not sync drift, so they do
|
|
67
67
|
* NOT set this flag (mirrors the sync-status engine: an orphan alone never
|
|
68
68
|
* flags needsSync).
|