@phnx-labs/agents-cli 1.22.42 → 1.22.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +149 -0
- package/README.md +15 -13
- package/dist/bootstrap.js +33 -13
- package/dist/cli/command-registry.d.ts +0 -8
- package/dist/cli/command-registry.js +1 -25
- package/dist/commands/alias.d.ts +7 -4
- package/dist/commands/alias.js +33 -8
- package/dist/commands/apply.d.ts +7 -10
- package/dist/commands/apply.js +15 -35
- package/dist/commands/artifacts.js +4 -5
- package/dist/commands/audit.d.ts +6 -6
- package/dist/commands/audit.js +12 -12
- package/dist/commands/auth.js +14 -6
- package/dist/commands/beta.d.ts +7 -1
- package/dist/commands/beta.js +16 -9
- package/dist/commands/commands.js +1 -1
- package/dist/commands/computer.js +20 -6
- package/dist/commands/daemon.js +1 -1
- package/dist/commands/doctor.d.ts +4 -2
- package/dist/commands/doctor.js +21 -9
- package/dist/commands/events.d.ts +2 -2
- package/dist/commands/events.js +6 -5
- package/dist/commands/exec.d.ts +1 -1
- package/dist/commands/exec.js +11 -6
- package/dist/commands/factory.d.ts +1 -1
- package/dist/commands/feed.js +1 -30
- package/dist/commands/fleet-capture.d.ts +1 -1
- package/dist/commands/fleet-capture.js +4 -5
- package/dist/commands/harness.js +4 -52
- package/dist/commands/hooks.js +1 -1
- package/dist/commands/insights.d.ts +1 -1
- package/dist/commands/insights.js +3 -2
- package/dist/commands/menubar.js +58 -1
- package/dist/commands/monitors.js +1 -1
- package/dist/commands/org.d.ts +5 -0
- package/dist/commands/org.js +83 -29
- package/dist/commands/output.js +2 -2
- package/dist/commands/profiles.js +2 -2
- package/dist/commands/prune.js +5 -4
- package/dist/commands/routines.js +43 -25
- package/dist/commands/routines.test-fixture.d.ts +86 -0
- package/dist/commands/routines.test-fixture.js +345 -0
- package/dist/commands/sessions-picker.js +124 -122
- package/dist/commands/sessions.test-fixture.d.ts +24 -0
- package/dist/commands/sessions.test-fixture.js +224 -0
- package/dist/commands/setup.js +9 -1
- package/dist/commands/share.d.ts +9 -10
- package/dist/commands/share.js +68 -50
- package/dist/commands/skills.js +1 -1
- package/dist/commands/snapshot.d.ts +1 -1
- package/dist/commands/snapshot.js +4 -4
- package/dist/commands/ssh.js +15 -62
- package/dist/commands/status.d.ts +4 -2
- package/dist/commands/status.js +10 -7
- package/dist/commands/subagents.js +1 -1
- package/dist/commands/sync.d.ts +1 -0
- package/dist/commands/sync.js +29 -2
- package/dist/commands/view.js +11 -2
- package/dist/commands/webhook.js +1 -1
- package/dist/lib/accounting/rotate.d.ts +5 -3
- package/dist/lib/accounting/rotate.js +36 -7
- package/dist/lib/accounting/usage.d.ts +18 -4
- package/dist/lib/accounting/usage.js +138 -27
- package/dist/lib/agent-spec/agents.d.ts +7 -7
- package/dist/lib/agent-spec/agents.js +15 -15
- package/dist/lib/analytics/mix-commands.d.ts +6 -28
- package/dist/lib/analytics/mix-commands.js +14 -40
- package/dist/lib/audit/log.d.ts +2 -2
- package/dist/lib/audit/log.js +2 -2
- package/dist/lib/beta.js +1 -1
- package/dist/lib/browser/domain-skills.d.ts +26 -6
- package/dist/lib/browser/domain-skills.js +81 -43
- package/dist/lib/cloud/rush.d.ts +0 -15
- package/dist/lib/cloud/rush.js +0 -35
- package/dist/lib/crabbox/runtimes.d.ts +6 -2
- package/dist/lib/crabbox/runtimes.js +21 -5
- package/dist/lib/daemon/daemon.js +97 -58
- package/dist/lib/daemon/daemon.test-fixture.d.ts +24 -0
- package/dist/lib/daemon/daemon.test-fixture.js +71 -0
- package/dist/lib/daemon/runner.d.ts +18 -1
- package/dist/lib/daemon/runner.js +105 -11
- package/dist/lib/daemon-ticks.d.ts +38 -1
- package/dist/lib/daemon-ticks.js +52 -5
- package/dist/lib/device-config.js +1 -2
- package/dist/lib/devices/discovery-policy.d.ts +3 -3
- package/dist/lib/devices/discovery-policy.js +3 -3
- package/dist/lib/devices/doctor-findings.js +1 -1
- package/dist/lib/devices/fleet.d.ts +4 -8
- package/dist/lib/devices/fleet.js +3 -15
- package/dist/lib/devices/health-report.js +4 -4
- package/dist/lib/devices/pool.d.ts +0 -6
- package/dist/lib/devices/pool.js +0 -6
- package/dist/lib/devices/registry.d.ts +0 -15
- package/dist/lib/devices/registry.js +0 -9
- package/dist/lib/devices/stats-cache.d.ts +12 -0
- package/dist/lib/devices/stats-cache.js +24 -5
- package/dist/lib/drift-sync.d.ts +1 -1
- package/dist/lib/drift-sync.js +2 -2
- package/dist/lib/drift.d.ts +1 -1
- package/dist/lib/exec.js +13 -3
- package/dist/lib/fleet/capture.d.ts +1 -1
- package/dist/lib/fleet/manifest.js +1 -1
- package/dist/lib/fleet/remote-login.d.ts +2 -2
- package/dist/lib/fleet/remote-login.js +3 -4
- package/dist/lib/format.d.ts +9 -0
- package/dist/lib/format.js +16 -0
- package/dist/lib/github/pr-mergeable.d.ts +49 -0
- package/dist/lib/github/pr-mergeable.js +157 -0
- package/dist/lib/github/pr-verdict.d.ts +67 -0
- package/dist/lib/github/pr-verdict.js +72 -0
- package/dist/lib/harness/adapter.d.ts +6 -5
- package/dist/lib/hosts/dispatch.d.ts +2 -0
- package/dist/lib/hosts/dispatch.js +8 -1
- package/dist/lib/hosts/passthrough.d.ts +10 -0
- package/dist/lib/hosts/passthrough.js +28 -7
- package/dist/lib/hosts/providers/devices.js +1 -13
- package/dist/lib/hosts/ready.d.ts +12 -3
- package/dist/lib/hosts/ready.js +27 -12
- package/dist/lib/hosts/registry.d.ts +4 -5
- package/dist/lib/hosts/registry.js +3 -7
- package/dist/lib/hosts/session-index.js +1 -1
- package/dist/lib/installations/shims.js +1 -1
- package/dist/lib/installations/versions.d.ts +40 -0
- package/dist/lib/installations/versions.js +78 -4
- package/dist/lib/menubar/MenubarHelper.app/Contents/Info.plist +1 -5
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/_CodeSignature/CodeResources +2 -15
- package/dist/lib/menubar/install-menubar.d.ts +109 -0
- package/dist/lib/menubar/install-menubar.js +218 -12
- package/dist/lib/monitors/config.d.ts +2 -2
- package/dist/lib/monitors/config.js +3 -2
- package/dist/lib/origin-machine.d.ts +18 -0
- package/dist/lib/origin-machine.js +34 -0
- package/dist/lib/perf/db.js +1 -1
- package/dist/lib/perf/spool.js +1 -1
- package/dist/lib/prix-account.d.ts +2 -1
- package/dist/lib/prix-account.js +2 -1
- package/dist/lib/profiles.d.ts +7 -0
- package/dist/lib/profiles.js +26 -1
- package/dist/lib/remote-agents-json.js +1 -7
- package/dist/lib/routines-placement.d.ts +1 -1
- package/dist/lib/routines-placement.js +1 -1
- package/dist/lib/sandbox.d.ts +24 -0
- package/dist/lib/sandbox.js +101 -0
- package/dist/lib/scheduling/routines.d.ts +18 -9
- package/dist/lib/scheduling/routines.js +16 -8
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/Info.plist +0 -2
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/_CodeSignature/CodeResources +1 -13
- package/dist/lib/secrets/agent.js +9 -1
- package/dist/lib/secrets/index.js +1 -1
- package/dist/lib/secrets/list-filter.js +1 -1
- package/dist/lib/service-manifest.d.ts +20 -0
- package/dist/lib/service-manifest.js +34 -0
- package/dist/lib/session/active.d.ts +4 -16
- package/dist/lib/session/active.js +2 -6
- package/dist/lib/session/cloud.js +1 -1
- package/dist/lib/session/db.d.ts +13 -98
- package/dist/lib/session/db.js +5 -12
- package/dist/lib/session/discover.d.ts +5 -81
- package/dist/lib/session/discover.js +5 -5
- package/dist/lib/session/fork.js +1 -1
- package/dist/lib/session/live-metadata.js +1 -1
- package/dist/lib/session/origin-machine.d.ts +1 -18
- package/dist/lib/session/origin-machine.js +4 -34
- package/dist/lib/session/relative-time.d.ts +1 -44
- package/dist/lib/session/relative-time.js +4 -120
- package/dist/lib/session/remote/remote-bundle.d.ts +12 -0
- package/dist/lib/session/remote/remote-bundle.js +61 -0
- package/dist/lib/session/remote/remote-list.d.ts +148 -0
- package/dist/lib/session/remote/remote-list.js +607 -0
- package/dist/lib/session/remote/remote.d.ts +132 -0
- package/dist/lib/session/remote/remote.js +314 -0
- package/dist/lib/session/remote/watch.d.ts +101 -0
- package/dist/lib/session/remote/watch.js +241 -0
- package/dist/lib/session/remote-bundle.d.ts +1 -12
- package/dist/lib/session/remote-bundle.js +3 -61
- package/dist/lib/session/remote-list.d.ts +1 -148
- package/dist/lib/session/remote-list.js +3 -607
- package/dist/lib/session/remote.d.ts +1 -132
- package/dist/lib/session/remote.js +3 -314
- package/dist/lib/session/short-id.d.ts +1 -17
- package/dist/lib/session/short-id.js +4 -20
- package/dist/lib/session/watch.d.ts +1 -101
- package/dist/lib/session/watch.js +3 -242
- package/dist/lib/session/width.d.ts +1 -29
- package/dist/lib/session/width.js +4 -101
- package/dist/lib/share/config.d.ts +12 -2
- package/dist/lib/share/config.js +47 -11
- package/dist/lib/share/delete.js +1 -1
- package/dist/lib/share/publish.js +1 -1
- package/dist/lib/share/worker-template.js +2 -2
- package/dist/lib/smart-launch.d.ts +4 -4
- package/dist/lib/smart-launch.js +8 -18
- package/dist/lib/snapshot.d.ts +2 -2
- package/dist/lib/snapshot.js +1 -1
- package/dist/lib/staleness/index.d.ts +3 -1
- package/dist/lib/staleness/index.js +21 -1
- package/dist/lib/staleness/types.d.ts +11 -0
- package/dist/lib/staleness/writers/commands.js +13 -4
- package/dist/lib/staleness/writers/hooks.js +3 -1
- package/dist/lib/staleness/writers/mcp.d.ts +0 -7
- package/dist/lib/staleness/writers/mcp.js +16 -1
- package/dist/lib/staleness/writers/rules.js +1 -1
- package/dist/lib/staleness/writers/skills.js +3 -1
- package/dist/lib/staleness/writers/subagents.d.ts +0 -9
- package/dist/lib/staleness/writers/subagents.js +18 -1
- package/dist/lib/staleness/writers/types.d.ts +10 -0
- package/dist/lib/startup/command-registry.d.ts +10 -1
- package/dist/lib/startup/command-registry.js +26 -7
- package/dist/lib/startup/root-command.d.ts +18 -1
- package/dist/lib/startup/root-command.js +18 -1
- package/dist/lib/state.js +7 -0
- package/dist/lib/teams/placement-probe.js +28 -13
- package/dist/lib/teams/scheduler.d.ts +3 -1
- package/dist/lib/teams/scheduler.js +1 -1
- package/dist/lib/text/relative-time.d.ts +44 -0
- package/dist/lib/text/relative-time.js +120 -0
- package/dist/lib/text/short-id.d.ts +17 -0
- package/dist/lib/text/short-id.js +20 -0
- package/dist/lib/text/width.d.ts +29 -0
- package/dist/lib/text/width.js +101 -0
- package/dist/lib/triggers/handlers.d.ts +7 -0
- package/dist/lib/triggers/handlers.js +8 -0
- package/dist/lib/triggers/webhook.d.ts +1 -0
- package/dist/lib/triggers/webhook.js +2 -1
- package/dist/lib/types.d.ts +1 -1
- package/dist/lib/view-types.d.ts +7 -0
- package/package.json +2 -3
- package/dist/bin/agents +0 -0
- package/dist/commands/serve.d.ts +0 -10
- package/dist/commands/serve.js +0 -68
- package/dist/commands/tickets.d.ts +0 -2
- package/dist/commands/tickets.js +0 -43
- package/dist/commands/trends.d.ts +0 -10
- package/dist/commands/trends.js +0 -12
- package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/Resources/AppIcon.icns +0 -0
- package/dist/lib/observe-aliases.d.ts +0 -30
- package/dist/lib/observe-aliases.js +0 -38
- package/dist/lib/secrets/Agents CLI.app/Contents/Resources/AppIcon.icns +0 -0
- package/dist/lib/serve/control.d.ts +0 -95
- package/dist/lib/serve/control.js +0 -260
- package/dist/lib/serve/data.d.ts +0 -81
- package/dist/lib/serve/data.js +0 -91
- package/dist/lib/serve/page.d.ts +0 -7
- package/dist/lib/serve/page.js +0 -140
- package/dist/lib/serve/server.d.ts +0 -80
- package/dist/lib/serve/server.js +0 -145
- package/dist/lib/serve/stream.d.ts +0 -43
- package/dist/lib/serve/stream.js +0 -116
- package/dist/lib/serve/token.d.ts +0 -35
- package/dist/lib/serve/token.js +0 -85
- package/dist/lib/tickets/list.d.ts +0 -53
- package/dist/lib/tickets/list.js +0 -153
|
@@ -24,7 +24,7 @@ import { getRuntimeStateDir, getHelpersDir } from '../state.js';
|
|
|
24
24
|
import { getCliVersion, resolveAgentsBin, resolveInstalledLayout } from '../version.js';
|
|
25
25
|
import { copyAppBundle, withInstallLock } from '../app-bundle-install.js';
|
|
26
26
|
import { compareVersions } from '../agent-spec/primitives.js';
|
|
27
|
-
import { namespacedServiceLabel, serviceManifestHomeEnv } from '../service-manifest.js';
|
|
27
|
+
import { namespacedServiceLabel, serviceManifestHomeEnv, serviceManagerRegistrationAllowed } from '../service-manifest.js';
|
|
28
28
|
const APP_BUNDLE_NAME = 'MenubarHelper.app';
|
|
29
29
|
const INSTALL_DIR_NAME = 'agents-cli';
|
|
30
30
|
const SERVICE_LABEL_BASE = 'com.phnx-labs.agents-menubar';
|
|
@@ -333,6 +333,11 @@ ${envXml}
|
|
|
333
333
|
* is the only sequence that reliably re-attaches the status item.
|
|
334
334
|
*/
|
|
335
335
|
export function restartMenubarLaunchAgent(uid, plist, exec = execFileSync) {
|
|
336
|
+
const reg = serviceManagerRegistrationAllowed();
|
|
337
|
+
if (!reg.allowed) {
|
|
338
|
+
process.stderr.write(`[agents] ${reg.reason}\n`);
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
336
341
|
const serviceTarget = `gui/${uid}/${serviceLabel()}`;
|
|
337
342
|
const opts = { stdio: ['ignore', 'ignore', 'ignore'] };
|
|
338
343
|
try {
|
|
@@ -348,6 +353,45 @@ export function restartMenubarLaunchAgent(uid, plist, exec = execFileSync) {
|
|
|
348
353
|
}
|
|
349
354
|
catch { /* best effort */ }
|
|
350
355
|
}
|
|
356
|
+
/**
|
|
357
|
+
* Force a running helper off the binary that was just swapped underneath it.
|
|
358
|
+
*
|
|
359
|
+
* `installMenubarLaunchAgentOnUpgrade()` calls this after a heal actually
|
|
360
|
+
* replaces the on-disk bundle (a version bump or the ad-hoc -> Developer ID
|
|
361
|
+
* transition — see `menubarHealReplacedBundle`). `restartMenubarLaunchAgent`
|
|
362
|
+
* (run moments earlier via `installAndStartService`) already attempts
|
|
363
|
+
* bootout+bootstrap+kickstart, but that targets the GUI launchd domain and
|
|
364
|
+
* fails silently — `launchctl kickstart -k gui/<uid>/<label>` prints "Could not
|
|
365
|
+
* find service ... in domain for user gui" — from a shell with no Aqua session,
|
|
366
|
+
* which is the ordinary case for `agents` invoked from a terminal/tmux/ssh
|
|
367
|
+
* session rather than literally the login item (verified live). When that
|
|
368
|
+
* happens the plist and on-disk bundle are current but the LIVE process is
|
|
369
|
+
* still the old binary, so it requests Accessibility under the OLD code
|
|
370
|
+
* identity and the grant never sticks.
|
|
371
|
+
*
|
|
372
|
+
* `kickstart -k` (force-restart, unlike the bare kickstart already tried) is
|
|
373
|
+
* attempted first since it is the clean launchd-native path when it works. If
|
|
374
|
+
* launchd has no record of the service in this context either, fall back to
|
|
375
|
+
* ending the specific pid(s) already confirmed to be running the installed
|
|
376
|
+
* bundle (`ownProcesses`, resolved by the caller via `liveMenubarProcesses`) —
|
|
377
|
+
* never a broad pkill. The launchd plist has `KeepAlive=true`, so the ended
|
|
378
|
+
* process is relaunched from the swapped binary within seconds.
|
|
379
|
+
*/
|
|
380
|
+
export function restartMenubarHelperAfterSwap(uid, ownProcesses, exec = execFileSync, kill = endProcess) {
|
|
381
|
+
const reg = serviceManagerRegistrationAllowed();
|
|
382
|
+
if (!reg.allowed)
|
|
383
|
+
return;
|
|
384
|
+
const target = `gui/${uid}/${serviceLabel()}`;
|
|
385
|
+
const opts = { stdio: ['ignore', 'ignore', 'ignore'] };
|
|
386
|
+
try {
|
|
387
|
+
exec('launchctl', ['kickstart', '-k', target], opts);
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
catch {
|
|
391
|
+
for (const p of ownProcesses)
|
|
392
|
+
kill(p.pid);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
351
395
|
/**
|
|
352
396
|
* Install + start the menu-bar helper as a launchd user service (idempotent).
|
|
353
397
|
* Clears the sticky opt-out, installs the .app, writes the plist, and
|
|
@@ -420,6 +464,22 @@ function menubarSetupStale() {
|
|
|
420
464
|
execExists: fs.existsSync(installedExecutablePath()),
|
|
421
465
|
});
|
|
422
466
|
}
|
|
467
|
+
/**
|
|
468
|
+
* Pure decision (no I/O): did THIS heal actually replace bundle content a live
|
|
469
|
+
* process could be running stale, as opposed to a plist-only repoint?
|
|
470
|
+
*
|
|
471
|
+
* `stale` (version bump or missing exec) and `needsDevIdHeal` (ad-hoc ->
|
|
472
|
+
* Developer ID) both mean the shipped `.app` differs from what is already
|
|
473
|
+
* installed — the exact condition under which a running helper can be left on
|
|
474
|
+
* an old code identity after `enableMenubarService` swaps the bundle. A
|
|
475
|
+
* repoint-only heal (`menubarSetupNeedsRepoint()` alone, same version, same
|
|
476
|
+
* signing identity) does NOT count: RUSH-3005 already owns the churn from
|
|
477
|
+
* mixed-Node-interpreter repoints, and restarting the helper on every one of
|
|
478
|
+
* those would double that churn rather than fix this bug.
|
|
479
|
+
*/
|
|
480
|
+
export function menubarHealReplacedBundle(opts) {
|
|
481
|
+
return opts.stale || opts.needsDevIdHeal;
|
|
482
|
+
}
|
|
423
483
|
/**
|
|
424
484
|
* Pure re-point decision (no I/O): the plist's baked interpreter/entry no longer
|
|
425
485
|
* match the install that is now running `agents`. This catches DUAL-INSTALL skew
|
|
@@ -435,7 +495,12 @@ export function menubarPlistNeedsRepoint(opts) {
|
|
|
435
495
|
return false; // can't resolve the running install — don't churn
|
|
436
496
|
if (opts.plistEntry !== opts.activeEntry)
|
|
437
497
|
return true;
|
|
438
|
-
|
|
498
|
+
// The entry path owns the helper. The same installed CLI may be invoked through
|
|
499
|
+
// several compatible Node interpreters (nvm, Homebrew, a parent process's
|
|
500
|
+
// process.execPath). Re-pointing merely because those valid interpreter paths
|
|
501
|
+
// differ makes every invocation replace and relaunch the shared helper. Keep the
|
|
502
|
+
// recorded interpreter until it disappears; then repoint to the active one.
|
|
503
|
+
if (opts.activeNode && (!opts.plistNode || !opts.plistNodeExists))
|
|
439
504
|
return true;
|
|
440
505
|
return false;
|
|
441
506
|
}
|
|
@@ -452,9 +517,11 @@ function readPlistEnvValue(key) {
|
|
|
452
517
|
}
|
|
453
518
|
/** True when the installed plist points at a different install than the active one. */
|
|
454
519
|
function menubarSetupNeedsRepoint() {
|
|
520
|
+
const plistNode = readPlistEnvValue('AGENTS_NODE');
|
|
455
521
|
return menubarPlistNeedsRepoint({
|
|
456
522
|
plistEntry: readPlistEnvValue('AGENTS_ENTRY'),
|
|
457
|
-
plistNode
|
|
523
|
+
plistNode,
|
|
524
|
+
plistNodeExists: Boolean(plistNode) && fs.existsSync(plistNode),
|
|
458
525
|
activeEntry: resolveCliEntry(),
|
|
459
526
|
activeNode: process.execPath,
|
|
460
527
|
});
|
|
@@ -467,15 +534,21 @@ export function disableMenubarService() {
|
|
|
467
534
|
if (!onDarwin())
|
|
468
535
|
return;
|
|
469
536
|
const plist = servicePlistPath();
|
|
470
|
-
const
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
}
|
|
474
|
-
catch {
|
|
537
|
+
const reg = serviceManagerRegistrationAllowed();
|
|
538
|
+
if (reg.allowed) {
|
|
539
|
+
const uid = process.getuid?.() ?? 0;
|
|
475
540
|
try {
|
|
476
|
-
execFileSync('launchctl', ['
|
|
541
|
+
execFileSync('launchctl', ['bootout', `gui/${uid}/${serviceLabel()}`], { stdio: ['ignore', 'ignore', 'ignore'] });
|
|
542
|
+
}
|
|
543
|
+
catch {
|
|
544
|
+
try {
|
|
545
|
+
execFileSync('launchctl', ['unload', '-w', plist], { stdio: ['ignore', 'ignore', 'ignore'] });
|
|
546
|
+
}
|
|
547
|
+
catch { /* not loaded */ }
|
|
477
548
|
}
|
|
478
|
-
|
|
549
|
+
}
|
|
550
|
+
else {
|
|
551
|
+
process.stderr.write(`[agents] ${reg.reason}\n`);
|
|
479
552
|
}
|
|
480
553
|
try {
|
|
481
554
|
fs.unlinkSync(plist);
|
|
@@ -639,7 +712,8 @@ export function installMenubarLaunchAgentOnUpgrade() {
|
|
|
639
712
|
// installed copy is still ad-hoc while the shipped source is Developer ID
|
|
640
713
|
// (older heal path; Accessibility re-prompts until the identity is restored).
|
|
641
714
|
const needsDevIdHeal = installedNeedsDevIdHeal();
|
|
642
|
-
|
|
715
|
+
const stale = menubarSetupStale();
|
|
716
|
+
if (!(stale || menubarSetupNeedsRepoint() || needsDevIdHeal))
|
|
643
717
|
return;
|
|
644
718
|
// ...but a copy that does not own the helper only gets to act on that drift
|
|
645
719
|
// once per cooldown. Without the gate every coexisting install recopies the
|
|
@@ -650,8 +724,22 @@ export function installMenubarLaunchAgentOnUpgrade() {
|
|
|
650
724
|
// false without installing when the bundle fails the Gatekeeper check, and
|
|
651
725
|
// stamping first would spend the shared cooldown on a no-op — locking every
|
|
652
726
|
// non-owner out for another hour while nothing had been fixed.
|
|
653
|
-
if (enableMenubarService({ clearOptOut: false }))
|
|
727
|
+
if (enableMenubarService({ clearOptOut: false })) {
|
|
654
728
|
stampMenubarHeal();
|
|
729
|
+
// One-time: the ad-hoc -> Developer ID transition leaves a dead TCC row
|
|
730
|
+
// under the old identity (tccutil on zion reset it 11 times across
|
|
731
|
+
// machines that made this jump) — clear it so the fresh grant sticks.
|
|
732
|
+
if (shouldMigrateMenubarTcc({ needsDevIdHeal, alreadyMigrated: menubarTccAlreadyMigrated() })) {
|
|
733
|
+
resetMenubarAccessibilityTcc();
|
|
734
|
+
}
|
|
735
|
+
// Only a REAL content swap (version bump or the Dev-ID transition) can
|
|
736
|
+
// leave a running process on stale code — a plist-only repoint (handled
|
|
737
|
+
// above by menubarSetupNeedsRepoint alone) is RUSH-3005's churn to own,
|
|
738
|
+
// not this heal's to restart on top of.
|
|
739
|
+
if (menubarHealReplacedBundle({ stale, needsDevIdHeal })) {
|
|
740
|
+
restartMenubarHelperAfterSwap(process.getuid?.() ?? 0, liveMenubarProcesses().own);
|
|
741
|
+
}
|
|
742
|
+
}
|
|
655
743
|
}
|
|
656
744
|
catch {
|
|
657
745
|
/* never block startup on the menu bar */
|
|
@@ -666,6 +754,46 @@ function installedNeedsDevIdHeal() {
|
|
|
666
754
|
return false;
|
|
667
755
|
return hasDeveloperIdSignature(src);
|
|
668
756
|
}
|
|
757
|
+
/** Marker written once the one-time ad-hoc -> Developer ID TCC migration has
|
|
758
|
+
* run on this machine, next to the version/heal stamps. */
|
|
759
|
+
function tccMigrationMarkerPath() {
|
|
760
|
+
return path.join(installDir(), '.menubar-tcc-migrated');
|
|
761
|
+
}
|
|
762
|
+
function menubarTccAlreadyMigrated() {
|
|
763
|
+
return fs.existsSync(tccMigrationMarkerPath());
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* Pure decision (no I/O): run the one-time `tccutil reset Accessibility` only
|
|
767
|
+
* on a real ad-hoc -> Developer ID transition, and only once ever. A machine
|
|
768
|
+
* that was always Developer ID never accumulated a dead TCC row under an
|
|
769
|
+
* ad-hoc identity, so resetting there would just force a needless re-prompt;
|
|
770
|
+
* a machine that already migrated must not be reset again on every heal.
|
|
771
|
+
*/
|
|
772
|
+
export function shouldMigrateMenubarTcc(opts) {
|
|
773
|
+
return opts.needsDevIdHeal && !opts.alreadyMigrated;
|
|
774
|
+
}
|
|
775
|
+
/**
|
|
776
|
+
* Reset the stale Accessibility grant TCC recorded against the pre-migration
|
|
777
|
+
* ad-hoc identity, then stamp the marker so this never runs again on this
|
|
778
|
+
* machine. Targets `SERVICE_LABEL_BASE` — the bundle's actual
|
|
779
|
+
* `CFBundleIdentifier`/codesign `--identifier` (menubar/scripts/build.sh) that
|
|
780
|
+
* TCC keys grants to — not the namespaced `serviceLabel()`, which exists only
|
|
781
|
+
* to keep launchd job labels apart under a redirected HOME (RUSH-2639) and is
|
|
782
|
+
* never the app's real identity. Best-effort: a missing/failing `tccutil`
|
|
783
|
+
* (older macOS, a sandboxed test HOME) must never block the heal that just
|
|
784
|
+
* fixed the signing identity itself.
|
|
785
|
+
*/
|
|
786
|
+
export function resetMenubarAccessibilityTcc(exec = execFileSync) {
|
|
787
|
+
try {
|
|
788
|
+
exec('tccutil', ['reset', 'Accessibility', SERVICE_LABEL_BASE], { stdio: ['ignore', 'ignore', 'ignore'] });
|
|
789
|
+
}
|
|
790
|
+
catch { /* best effort — a missing/failing tccutil must not block the heal */ }
|
|
791
|
+
try {
|
|
792
|
+
fs.mkdirSync(installDir(), { recursive: true });
|
|
793
|
+
fs.writeFileSync(tccMigrationMarkerPath(), new Date().toISOString());
|
|
794
|
+
}
|
|
795
|
+
catch { /* best effort */ }
|
|
796
|
+
}
|
|
669
797
|
/**
|
|
670
798
|
* Decide which live helper processes must be ended so exactly one status item
|
|
671
799
|
* survives. Pure so the choice is unit-testable without a live menu bar.
|
|
@@ -856,3 +984,81 @@ export function getMenubarStatus() {
|
|
|
856
984
|
disabledByUser: menubarDisabledByUser(),
|
|
857
985
|
};
|
|
858
986
|
}
|
|
987
|
+
/**
|
|
988
|
+
* Pure comparison (no I/O) behind the stale-process check: a helper pid that
|
|
989
|
+
* started before the bundle on disk was last written is still running the
|
|
990
|
+
* PREVIOUS binary — the exact condition `restartMenubarHelperAfterSwap` exists
|
|
991
|
+
* to fix. Millisecond epoch timestamps in, so the truth table is unit-testable
|
|
992
|
+
* without a live process or filesystem.
|
|
993
|
+
*/
|
|
994
|
+
export function isMenubarProcessStaleAgainstBundle(pidStartedAtMs, bundleMtimeMs) {
|
|
995
|
+
return pidStartedAtMs < bundleMtimeMs;
|
|
996
|
+
}
|
|
997
|
+
/** Wall-clock start time of a live pid via `ps`, or null if it can't be read. */
|
|
998
|
+
function pidStartTimeMs(pid) {
|
|
999
|
+
const r = spawnSync('ps', ['-o', 'lstart=', '-p', String(pid)], {
|
|
1000
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
1001
|
+
encoding: 'utf-8',
|
|
1002
|
+
});
|
|
1003
|
+
if (r.status !== 0)
|
|
1004
|
+
return null;
|
|
1005
|
+
const raw = (r.stdout || '').trim();
|
|
1006
|
+
if (!raw)
|
|
1007
|
+
return null;
|
|
1008
|
+
const t = new Date(raw).getTime();
|
|
1009
|
+
return Number.isNaN(t) ? null : t;
|
|
1010
|
+
}
|
|
1011
|
+
/**
|
|
1012
|
+
* `agents menubar doctor` — everything a human needs to tell "why did
|
|
1013
|
+
* Accessibility ask again" apart from "the helper is genuinely broken":
|
|
1014
|
+
* install path, version skew, whether the signing identity is update-stable,
|
|
1015
|
+
* and whether a live process predates the bundle it's supposed to be running.
|
|
1016
|
+
* Read-only: no install, no restart, no TCC reset — `agents menubar setup`
|
|
1017
|
+
* remains the command that fixes what this reports.
|
|
1018
|
+
*/
|
|
1019
|
+
export function buildMenubarDoctorReport() {
|
|
1020
|
+
if (!onDarwin()) {
|
|
1021
|
+
return {
|
|
1022
|
+
platform: process.platform,
|
|
1023
|
+
installPath: null,
|
|
1024
|
+
installedVersion: null,
|
|
1025
|
+
currentVersion: getCliVersion(),
|
|
1026
|
+
versionMatches: false,
|
|
1027
|
+
signingIdentity: 'unknown',
|
|
1028
|
+
running: false,
|
|
1029
|
+
staleRunningProcess: [],
|
|
1030
|
+
accessibilityHintNeeded: false,
|
|
1031
|
+
};
|
|
1032
|
+
}
|
|
1033
|
+
const status = getMenubarStatus();
|
|
1034
|
+
const appPath = status.installedApp;
|
|
1035
|
+
const signingIdentity = appPath
|
|
1036
|
+
? (hasDeveloperIdSignature(appPath) ? 'developer-id' : 'ad-hoc')
|
|
1037
|
+
: 'unknown';
|
|
1038
|
+
let staleRunningProcess = [];
|
|
1039
|
+
if (appPath && status.instances.length > 0) {
|
|
1040
|
+
try {
|
|
1041
|
+
const bundleMtimeMs = fs.statSync(installedExecutablePath()).mtimeMs;
|
|
1042
|
+
staleRunningProcess = status.instances
|
|
1043
|
+
.map((p) => {
|
|
1044
|
+
const startedAt = pidStartTimeMs(p.pid);
|
|
1045
|
+
return startedAt === null
|
|
1046
|
+
? null
|
|
1047
|
+
: { pid: p.pid, stale: isMenubarProcessStaleAgainstBundle(startedAt, bundleMtimeMs) };
|
|
1048
|
+
})
|
|
1049
|
+
.filter((p) => p !== null);
|
|
1050
|
+
}
|
|
1051
|
+
catch { /* exec vanished mid-check — report no staleness rather than throw */ }
|
|
1052
|
+
}
|
|
1053
|
+
return {
|
|
1054
|
+
platform: process.platform,
|
|
1055
|
+
installPath: appPath,
|
|
1056
|
+
installedVersion: status.installedVersion,
|
|
1057
|
+
currentVersion: status.currentVersion,
|
|
1058
|
+
versionMatches: status.installedVersion === status.currentVersion,
|
|
1059
|
+
signingIdentity,
|
|
1060
|
+
running: status.running,
|
|
1061
|
+
staleRunningProcess,
|
|
1062
|
+
accessibilityHintNeeded: signingIdentity === 'ad-hoc' || staleRunningProcess.some((p) => p.stale),
|
|
1063
|
+
};
|
|
1064
|
+
}
|
|
@@ -68,8 +68,8 @@ export type MonitorActionType = 'run' | 'routine' | 'notify' | 'webhook-out';
|
|
|
68
68
|
*/
|
|
69
69
|
export interface ActionConfig {
|
|
70
70
|
type: MonitorActionType;
|
|
71
|
-
/** run: which agent to spawn. */
|
|
72
|
-
agent?: AgentId;
|
|
71
|
+
/** run: which agent to spawn — a native harness id or a custom harness name (agents harness list). */
|
|
72
|
+
agent?: AgentId | (string & {});
|
|
73
73
|
/** run: the prompt; `{event}` is replaced with the fired event summary. */
|
|
74
74
|
prompt?: string;
|
|
75
75
|
/** run: execution mode (shared with routines). */
|
|
@@ -17,6 +17,7 @@ import { safeJoin, isSafeSegmentName } from '../paths.js';
|
|
|
17
17
|
import { atomicWriteFileSync } from '../fs-atomic.js';
|
|
18
18
|
import { machineId, normalizeHost } from '../machine-id.js';
|
|
19
19
|
import { ALL_AGENT_IDS } from '../agents.js';
|
|
20
|
+
import { isCustomHarnessName } from '../profiles.js';
|
|
20
21
|
/** Default values applied to every monitor config when fields are omitted. */
|
|
21
22
|
const MONITOR_DEFAULTS = {
|
|
22
23
|
enabled: true,
|
|
@@ -215,8 +216,8 @@ export function validateMonitor(config) {
|
|
|
215
216
|
errors.push(`action has conflicting fields (${populatedAct.join(', ')}); specify exactly one action`);
|
|
216
217
|
}
|
|
217
218
|
if (action.type === 'run') {
|
|
218
|
-
if (action.agent && !ALL_AGENT_IDS.includes(action.agent)) {
|
|
219
|
-
errors.push(`action.agent must be one of: ${ALL_AGENT_IDS.join(', ')}`);
|
|
219
|
+
if (action.agent && !ALL_AGENT_IDS.includes(action.agent) && !isCustomHarnessName(action.agent)) {
|
|
220
|
+
errors.push(`action.agent must be one of: ${ALL_AGENT_IDS.join(', ')}, or a custom harness (agents harness list)`);
|
|
220
221
|
}
|
|
221
222
|
if (!action.prompt || typeof action.prompt !== 'string') {
|
|
222
223
|
errors.push("action.type 'run' requires action.prompt");
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve which machine a session transcript originated on.
|
|
3
|
+
*
|
|
4
|
+
* Cross-machine sync mirrors a remote transcript to
|
|
5
|
+
* `backups/<agent>/<machine>/<subdir>/…`. Every other transcript is a live-home
|
|
6
|
+
* file on this box, so the origin is the local machine id.
|
|
7
|
+
*
|
|
8
|
+
* Kept in a leaf module (no session/db imports) so both discovery and the
|
|
9
|
+
* sessions DB upsert path can stamp `machine` without circular deps.
|
|
10
|
+
*/
|
|
11
|
+
/** Local machine id, cached for the process lifetime. */
|
|
12
|
+
export declare function localMachineId(): string;
|
|
13
|
+
/**
|
|
14
|
+
* The machine a discovered session originated on.
|
|
15
|
+
* When the path sits under the agent's backups root, the first segment below it
|
|
16
|
+
* is the origin machine id; otherwise it's the local machine.
|
|
17
|
+
*/
|
|
18
|
+
export declare function machineForSessionFile(filePath: string, agent: string): string;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve which machine a session transcript originated on.
|
|
3
|
+
*
|
|
4
|
+
* Cross-machine sync mirrors a remote transcript to
|
|
5
|
+
* `backups/<agent>/<machine>/<subdir>/…`. Every other transcript is a live-home
|
|
6
|
+
* file on this box, so the origin is the local machine id.
|
|
7
|
+
*
|
|
8
|
+
* Kept in a leaf module (no session/db imports) so both discovery and the
|
|
9
|
+
* sessions DB upsert path can stamp `machine` without circular deps.
|
|
10
|
+
*/
|
|
11
|
+
import * as path from 'path';
|
|
12
|
+
import { getHistoryDir } from './state.js';
|
|
13
|
+
import { machineId } from './machine-id.js';
|
|
14
|
+
let _localMachineId;
|
|
15
|
+
/** Local machine id, cached for the process lifetime. */
|
|
16
|
+
export function localMachineId() {
|
|
17
|
+
return (_localMachineId ??= machineId());
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The machine a discovered session originated on.
|
|
21
|
+
* When the path sits under the agent's backups root, the first segment below it
|
|
22
|
+
* is the origin machine id; otherwise it's the local machine.
|
|
23
|
+
*/
|
|
24
|
+
export function machineForSessionFile(filePath, agent) {
|
|
25
|
+
if (!filePath)
|
|
26
|
+
return localMachineId();
|
|
27
|
+
const base = path.join(getHistoryDir(), 'backups', agent) + path.sep;
|
|
28
|
+
if (filePath.startsWith(base)) {
|
|
29
|
+
const seg = filePath.slice(base.length).split(path.sep)[0];
|
|
30
|
+
if (seg)
|
|
31
|
+
return seg;
|
|
32
|
+
}
|
|
33
|
+
return localMachineId();
|
|
34
|
+
}
|
package/dist/lib/perf/db.js
CHANGED
|
@@ -8,7 +8,7 @@ import * as fs from 'fs';
|
|
|
8
8
|
import * as path from 'path';
|
|
9
9
|
import Database from '../sqlite.js';
|
|
10
10
|
import { getPerfDbPath, getPerfDir } from '../state.js';
|
|
11
|
-
import { localMachineId } from '../
|
|
11
|
+
import { localMachineId } from '../origin-machine.js';
|
|
12
12
|
import { resolveProjectKey } from '../project-key.js';
|
|
13
13
|
import { percentile } from '../percentile.js';
|
|
14
14
|
import { resolveSpoolPath, shortSessionId, _resetPerfSpoolForTest } from './spool.js';
|
package/dist/lib/perf/spool.js
CHANGED
|
@@ -9,7 +9,7 @@ import * as fs from 'fs';
|
|
|
9
9
|
import * as os from 'os';
|
|
10
10
|
import * as path from 'path';
|
|
11
11
|
import { getPerfSpoolPath } from '../state.js';
|
|
12
|
-
import { localMachineId } from '../
|
|
12
|
+
import { localMachineId } from '../origin-machine.js';
|
|
13
13
|
let _spoolOverride = null;
|
|
14
14
|
let _disabled = false;
|
|
15
15
|
/** Test seam — pair with db._resetPerfDbForTest. */
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Client for the Rush account layer (`api.prix.dev`) — backs `agents auth` and
|
|
3
|
-
* `agents
|
|
3
|
+
* `agents auth space` (canonical) / `agents org` (deprecated alias). `agents auth space`
|
|
4
|
+
* maps to `/api/v1/spaces`, not `/api/v1/orgs`: spaces
|
|
4
5
|
* already carry the free-tier caps (1 owned space, 3 members) and can exist
|
|
5
6
|
* standalone with no parent organization, matching a "just want a team" CLI flow
|
|
6
7
|
* better than the heavier enterprise-tenancy `orgs` routes (domain, SSO). See
|
package/dist/lib/prix-account.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Client for the Rush account layer (`api.prix.dev`) — backs `agents auth` and
|
|
3
|
-
* `agents
|
|
3
|
+
* `agents auth space` (canonical) / `agents org` (deprecated alias). `agents auth space`
|
|
4
|
+
* maps to `/api/v1/spaces`, not `/api/v1/orgs`: spaces
|
|
4
5
|
* already carry the free-tier caps (1 owned space, 3 members) and can exist
|
|
5
6
|
* standalone with no parent organization, matching a "just want a team" CLI flow
|
|
6
7
|
* better than the heavier enterprise-tenancy `orgs` routes (domain, SSO). See
|
package/dist/lib/profiles.d.ts
CHANGED
|
@@ -96,6 +96,13 @@ export declare function getProfilePath(name: string): string;
|
|
|
96
96
|
export declare function validateProfileName(name: string): void;
|
|
97
97
|
/** Check whether a profile YAML file exists on disk. */
|
|
98
98
|
export declare function profileExists(name: string): boolean;
|
|
99
|
+
/**
|
|
100
|
+
* True when `name` is a custom harness (a profile that is not shadowing a
|
|
101
|
+
* native agent id) — the predicate routines/monitors use to accept an
|
|
102
|
+
* `agent:` value that `agents run` will resolve as a profile. A native id
|
|
103
|
+
* always reads as native, matching exec's resolution order.
|
|
104
|
+
*/
|
|
105
|
+
export declare function isCustomHarnessName(name: string): boolean;
|
|
99
106
|
/** Read and parse a profile from disk. Throws if not found or malformed. */
|
|
100
107
|
export declare function readProfile(name: string): Profile;
|
|
101
108
|
/** Write a profile to disk atomically (write-to-tmp then rename). */
|
package/dist/lib/profiles.js
CHANGED
|
@@ -9,6 +9,7 @@ import * as fs from 'fs';
|
|
|
9
9
|
import * as path from 'path';
|
|
10
10
|
import * as crypto from 'node:crypto';
|
|
11
11
|
import * as yaml from 'yaml';
|
|
12
|
+
import { ALL_AGENT_IDS } from './agents.js';
|
|
12
13
|
import { getUserAgentsDir } from './state.js';
|
|
13
14
|
import { deleteKeychainToken, getKeychainToken, hasKeychainToken, keychainItemName } from './secrets/profiles.js';
|
|
14
15
|
import { getPreset } from './profiles-presets.js';
|
|
@@ -39,6 +40,17 @@ export function validateProfileName(name) {
|
|
|
39
40
|
export function profileExists(name) {
|
|
40
41
|
return fs.existsSync(profilePath(name));
|
|
41
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* True when `name` is a custom harness (a profile that is not shadowing a
|
|
45
|
+
* native agent id) — the predicate routines/monitors use to accept an
|
|
46
|
+
* `agent:` value that `agents run` will resolve as a profile. A native id
|
|
47
|
+
* always reads as native, matching exec's resolution order.
|
|
48
|
+
*/
|
|
49
|
+
export function isCustomHarnessName(name) {
|
|
50
|
+
if (!PROFILE_NAME_PATTERN.test(name))
|
|
51
|
+
return false;
|
|
52
|
+
return !ALL_AGENT_IDS.includes(name) && profileExists(name);
|
|
53
|
+
}
|
|
42
54
|
/** Read and parse a profile from disk. Throws if not found or malformed. */
|
|
43
55
|
export function readProfile(name) {
|
|
44
56
|
validateProfileName(name);
|
|
@@ -77,7 +89,11 @@ function migrateLegacyProfileAuth(profile, file) {
|
|
|
77
89
|
}
|
|
78
90
|
const migratedAccount = findAccount(accountName);
|
|
79
91
|
const oldItem = profile.auth.keychainItem;
|
|
80
|
-
|
|
92
|
+
// Reference by NAME, not id: profiles sync fleet-wide with `agents repo push`
|
|
93
|
+
// while account ids are minted per-device, so an id ref breaks on every other
|
|
94
|
+
// machine ("Unknown account '<uuid>'"). Names are the portable handle — the
|
|
95
|
+
// registry resolves both, and `accounts rename` already rewrites name refs.
|
|
96
|
+
profile.account = migratedAccount.name;
|
|
81
97
|
profile.provider = provider;
|
|
82
98
|
delete profile.auth;
|
|
83
99
|
delete profile.authOptional;
|
|
@@ -477,6 +493,15 @@ export function renameProfile(oldName, newName) {
|
|
|
477
493
|
export function resolveProfileEnv(profile) {
|
|
478
494
|
const env = { ...profile.env };
|
|
479
495
|
if (profile.account) {
|
|
496
|
+
if (!findAccount(profile.account)) {
|
|
497
|
+
// The commonest cause: the profile synced here via `agents repo push/pull`
|
|
498
|
+
// but its account ref was minted on another device (legacy id refs), so
|
|
499
|
+
// the bare "Unknown account" from the registry gives the user nothing to
|
|
500
|
+
// act on. Name the harness and the repair.
|
|
501
|
+
throw new Error(`Harness '${profile.name}' references account '${profile.account}', which does not exist on this device. ` +
|
|
502
|
+
`Accounts are per-machine; pick one from 'agents accounts list' and repoint with ` +
|
|
503
|
+
`'agents harness edit ${profile.name} --account <name>', or add it with 'agents accounts add'.`);
|
|
504
|
+
}
|
|
480
505
|
const account = resolveCredentialAccount(profile.account, profile.host.agent, profile.provider);
|
|
481
506
|
Object.assign(env, account.env);
|
|
482
507
|
}
|
|
@@ -12,7 +12,7 @@ import chalk from 'chalk';
|
|
|
12
12
|
import { SSH_OPTS, controlOpts, assertValidSshTarget, shellQuote, REMOTE_STDOUT_MAX_BYTES, RemoteUtf8Accumulator, } from './ssh-exec.js';
|
|
13
13
|
import { deviceIdentityArgs, sshTargetFor } from './devices/connect.js';
|
|
14
14
|
import { resolveExplicitTargets } from './devices/resolve-target.js';
|
|
15
|
-
import { loadDevices,
|
|
15
|
+
import { loadDevices, isDialableDevice } from './devices/registry.js';
|
|
16
16
|
import { remoteShellFor, buildWindowsAgentsCommand, stripClixml } from './hosts/remote-cmd.js';
|
|
17
17
|
import { machineId, normalizeHost } from './machine-id.js';
|
|
18
18
|
const REMOTE_TIMEOUT_MS = 12_000;
|
|
@@ -117,12 +117,6 @@ export async function gatherRemoteAgentsJson(options, deps = {}) {
|
|
|
117
117
|
continue;
|
|
118
118
|
if (normalizeHost(device.name) === self)
|
|
119
119
|
continue;
|
|
120
|
-
// Control-only devices (a phone/tablet cockpit) drive the fleet but never
|
|
121
|
-
// run agents — never dial them, whatever their platform reads as. Keyed on
|
|
122
|
-
// role, not platform, so this holds even for a control device that carries
|
|
123
|
-
// a real OS value (mirrors the skip in session/remote-list.ts).
|
|
124
|
-
if (isControlDevice(device))
|
|
125
|
-
continue;
|
|
126
120
|
if (!['windows', 'linux', 'macos'].includes(device.platform))
|
|
127
121
|
continue;
|
|
128
122
|
try {
|
|
@@ -29,7 +29,7 @@ export type PlacementTarget = {
|
|
|
29
29
|
* `config.devices` is the *firing* allowlist only (which daemon may fire the
|
|
30
30
|
* job). It is intentionally NOT used as an execution pool filter — otherwise
|
|
31
31
|
* the double-fire pin (`devices: [self]`) would collapse fleet placement to
|
|
32
|
-
* always-local.
|
|
32
|
+
* always-local. Offline / no-address devices are never chosen.
|
|
33
33
|
*/
|
|
34
34
|
export declare function pickFleetDevice(_config?: Pick<JobConfig, 'devices'>, platform?: DevicePlatform): string | null;
|
|
35
35
|
/**
|
|
@@ -23,7 +23,7 @@ import { planFleetTargets } from './devices/fleet.js';
|
|
|
23
23
|
* `config.devices` is the *firing* allowlist only (which daemon may fire the
|
|
24
24
|
* job). It is intentionally NOT used as an execution pool filter — otherwise
|
|
25
25
|
* the double-fire pin (`devices: [self]`) would collapse fleet placement to
|
|
26
|
-
* always-local.
|
|
26
|
+
* always-local. Offline / no-address devices are never chosen.
|
|
27
27
|
*/
|
|
28
28
|
export function pickFleetDevice(_config, platform) {
|
|
29
29
|
let reg;
|
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. */
|