@phnx-labs/agents-cli 1.22.43 → 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 +77 -0
- package/README.md +14 -12
- package/dist/bootstrap.js +8 -6
- package/dist/cli/command-registry.d.ts +0 -5
- package/dist/cli/command-registry.js +1 -17
- 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 +7 -3
- 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/doctor.js +1 -4
- 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 +8 -4
- package/dist/commands/factory.d.ts +1 -1
- package/dist/commands/fleet-capture.d.ts +1 -1
- package/dist/commands/fleet-capture.js +4 -5
- package/dist/commands/harness.js +1 -49
- 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/org.d.ts +5 -0
- package/dist/commands/org.js +82 -29
- package/dist/commands/output.js +2 -2
- package/dist/commands/prune.js +5 -4
- package/dist/commands/routines.test-fixture.d.ts +86 -0
- package/dist/commands/routines.test-fixture.js +345 -0
- package/dist/commands/sessions.test-fixture.d.ts +24 -0
- package/dist/commands/sessions.test-fixture.js +224 -0
- package/dist/commands/setup.js +5 -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/ssh.js +15 -62
- package/dist/commands/subagents.js +1 -1
- package/dist/commands/view.js +5 -2
- 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/daemon/daemon.js +11 -0
- 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-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/fleet.d.ts +4 -8
- package/dist/lib/devices/fleet.js +3 -15
- package/dist/lib/devices/health-report.js +3 -3
- 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/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/hosts/dispatch.d.ts +2 -0
- package/dist/lib/hosts/dispatch.js +8 -1
- package/dist/lib/hosts/passthrough.js +0 -1
- 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/installations/versions.js +21 -2
- 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 +199 -4
- package/dist/lib/prix-account.d.ts +2 -1
- package/dist/lib/prix-account.js +2 -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/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/session/active.d.ts +4 -16
- package/dist/lib/session/active.js +2 -6
- package/dist/lib/session/db.d.ts +13 -98
- package/dist/lib/session/db.js +4 -11
- package/dist/lib/session/discover.d.ts +5 -81
- package/dist/lib/session/discover.js +5 -5
- 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/watch.d.ts +1 -101
- package/dist/lib/session/watch.js +3 -242
- 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/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 +7 -1
- package/dist/lib/startup/command-registry.js +17 -5
- 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 +27 -12
- package/dist/lib/teams/scheduler.d.ts +3 -1
- package/dist/lib/types.d.ts +1 -1
- package/dist/lib/view-types.d.ts +3 -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/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/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
|
@@ -24,7 +24,7 @@ import { normalizeHost } from '../machine-id.js';
|
|
|
24
24
|
import { readMeta } from '../state.js';
|
|
25
25
|
import { isSshConfigHost } from './ssh-config.js';
|
|
26
26
|
import { resolveRemoteOsSync } from './remote-os.js';
|
|
27
|
-
import { loadDevices
|
|
27
|
+
import { loadDevices } from '../devices/registry.js';
|
|
28
28
|
import { resolveDeviceProfile } from '../devices/resolve-profile.js';
|
|
29
29
|
import { isDeviceAuto, resolveDeviceAffinity } from '../smart-launch.js';
|
|
30
30
|
import { localMachineId } from '../session/origin-machine.js';
|
|
@@ -148,7 +148,7 @@ function literalHost(token, host, user) {
|
|
|
148
148
|
* all resolve the same way regardless of which subcommand called.
|
|
149
149
|
*
|
|
150
150
|
* Non-throwing: returns null on an injection-guard failure or an unresolved bare
|
|
151
|
-
* token.
|
|
151
|
+
* token. The device-only refusal (password auth) is NOT applied
|
|
152
152
|
* here — that is the dispatch wrapper's job ({@link resolveHost}), so the fan-out
|
|
153
153
|
* and `agents ssh` paths, which handle those cases differently, aren't forced
|
|
154
154
|
* into a dispatch verdict.
|
|
@@ -240,8 +240,7 @@ export async function listAllHosts() {
|
|
|
240
240
|
* caller consumes (`run --device`, the generic passthrough, teams placement, the
|
|
241
241
|
* cloud host provider, doctor, funnel, remote secrets). Grammar and merge come
|
|
242
242
|
* from {@link matchHost}; on top, this layer applies the device-only dispatch
|
|
243
|
-
*
|
|
244
|
-
* - a control device (a paired iPhone) can never be a dispatch target;
|
|
243
|
+
* refusal so it holds even when an inline overlay shadows the device:
|
|
245
244
|
* - a password-auth device throws the typed {@link DeviceOffloadUnsupportedError}
|
|
246
245
|
* (offload rides `BatchMode=yes` ssh, which can't answer a prompt);
|
|
247
246
|
* - an addressless device has nothing to dial.
|
|
@@ -254,9 +253,6 @@ export async function resolveHost(name) {
|
|
|
254
253
|
if (!host)
|
|
255
254
|
return null;
|
|
256
255
|
if (host.device) {
|
|
257
|
-
if (isControlDevice(host.device)) {
|
|
258
|
-
throw new Error(`Device "${host.device.name}" is a control device (a cockpit), not an executor — it can't run agents. Dispatch to a worker device instead.`);
|
|
259
|
-
}
|
|
260
256
|
if (host.device.auth.method === 'password') {
|
|
261
257
|
throw new DeviceOffloadUnsupportedError(host.device.name);
|
|
262
258
|
}
|
|
@@ -2491,6 +2491,10 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
|
|
|
2491
2491
|
const commandsAlsoAsSkills = shouldAlsoInstallCommandAsSkill(agent, version);
|
|
2492
2492
|
const commandsInstallAsSkills = commandsAsSkills || commandsAlsoAsSkills;
|
|
2493
2493
|
let writtenCommands = [];
|
|
2494
|
+
// Artifact paths the writers report (WriteResult.paths), persisted to the
|
|
2495
|
+
// manifest as `writtenTargets` after a full sync so isStale can flag a
|
|
2496
|
+
// deleted artifact as stale (#2398).
|
|
2497
|
+
const writtenTargets = [];
|
|
2494
2498
|
if (commandsToSync.length > 0 && commandsWriter) {
|
|
2495
2499
|
// Agents that replace native command files with skills (codex >= 0.117.0,
|
|
2496
2500
|
// kimi) must have their legacy command dir removed, or files written by an
|
|
@@ -2503,6 +2507,8 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
|
|
|
2503
2507
|
}
|
|
2504
2508
|
const r = commandsWriter.write({ version, versionHome, selection: commandsToSync, cwd });
|
|
2505
2509
|
writtenCommands = r.synced;
|
|
2510
|
+
if (r.paths)
|
|
2511
|
+
writtenTargets.push(...r.paths);
|
|
2506
2512
|
result.commands = r.synced.length > 0;
|
|
2507
2513
|
}
|
|
2508
2514
|
// Orphan-sweep stale top-level command files from previous syncs under a
|
|
@@ -2510,8 +2516,9 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
|
|
|
2510
2516
|
// not pass an explicit `selection`. Callers that pass explicit selections
|
|
2511
2517
|
// are using the incremental/additive API (sync exactly these; leave others
|
|
2512
2518
|
// alone), so the sweep would be a contract violation there. The
|
|
2513
|
-
// cross-project leak
|
|
2514
|
-
// launch
|
|
2519
|
+
// cross-project leak comes from no-selection full syncs run under different
|
|
2520
|
+
// cwds (`agents sync` / `agents refresh`; the shim's `agents sync --launch`
|
|
2521
|
+
// skips version-home reconciliation and never reaches this path).
|
|
2515
2522
|
if (!userPassedSelection && commandsWriter && !shouldInstallCommandAsSkill(agent, version)) {
|
|
2516
2523
|
const commandsTargetSweep = path.join(agentDir, agentConfig.commandsSubdir);
|
|
2517
2524
|
if (fs.existsSync(commandsTargetSweep)) {
|
|
@@ -2570,6 +2577,8 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
|
|
|
2570
2577
|
else if (skillsWriter) {
|
|
2571
2578
|
if (skillsToSync.length > 0) {
|
|
2572
2579
|
const r = skillsWriter.write({ version, versionHome, selection: skillsToSync, cwd });
|
|
2580
|
+
if (r.paths)
|
|
2581
|
+
writtenTargets.push(...r.paths);
|
|
2573
2582
|
result.skills = r.synced.length > 0;
|
|
2574
2583
|
}
|
|
2575
2584
|
// Orphan-sweep stale skill directories from previous syncs under a
|
|
@@ -2625,6 +2634,8 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
|
|
|
2625
2634
|
}
|
|
2626
2635
|
}
|
|
2627
2636
|
}
|
|
2637
|
+
if (r.paths)
|
|
2638
|
+
writtenTargets.push(...r.paths);
|
|
2628
2639
|
result.hooks = r.synced.length > 0;
|
|
2629
2640
|
hookManifest = selectHookManifest(parseHookManifest(), hooksToSync);
|
|
2630
2641
|
}
|
|
@@ -2651,6 +2662,8 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
|
|
|
2651
2662
|
: null;
|
|
2652
2663
|
const preset = overridePreset || activeRulesPreset() || getActiveRulesPreset(agent, version);
|
|
2653
2664
|
const r = rulesWriter.write({ version, versionHome, selection: { preset }, cwd });
|
|
2665
|
+
if (r.paths)
|
|
2666
|
+
writtenTargets.push(...r.paths);
|
|
2654
2667
|
result.memory.push(...r.synced);
|
|
2655
2668
|
// rulesPreset is tracked separately via setActiveRulesPreset.
|
|
2656
2669
|
}
|
|
@@ -2728,6 +2741,8 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
|
|
|
2728
2741
|
const mcpToSync = mcpToSyncAll.filter(n => !untrustedProjectMcpNames.has(n));
|
|
2729
2742
|
if (mcpToSync.length > 0 && mcpWriter) {
|
|
2730
2743
|
const r = mcpWriter.write({ version, versionHome, selection: mcpToSync, cwd });
|
|
2744
|
+
if (r.paths)
|
|
2745
|
+
writtenTargets.push(...r.paths);
|
|
2731
2746
|
result.mcp = r.synced;
|
|
2732
2747
|
if (r.errors?.length)
|
|
2733
2748
|
result.declined.push(...r.errors.map((e) => `mcp: ${e}`));
|
|
@@ -2749,6 +2764,8 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
|
|
|
2749
2764
|
}
|
|
2750
2765
|
if (subagentsToSync.length > 0 && subagentsWriter) {
|
|
2751
2766
|
const r = subagentsWriter.write({ version, versionHome, selection: subagentsToSync, cwd });
|
|
2767
|
+
if (r.paths)
|
|
2768
|
+
writtenTargets.push(...r.paths);
|
|
2752
2769
|
result.subagents.push(...r.synced);
|
|
2753
2770
|
// Orphan-sweep for Claude only — see comment on commands/skills sweep
|
|
2754
2771
|
// for the no-selection guard. OpenClaw stores subagents as siblings of
|
|
@@ -2855,6 +2872,8 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
|
|
|
2855
2872
|
const previous = loadManifest(agent, version);
|
|
2856
2873
|
const manifest = buildSyncManifest(agent, version, cwd, previous);
|
|
2857
2874
|
manifest.writtenCommands = writtenCommands;
|
|
2875
|
+
// Deduped + sorted so repeated full syncs write byte-identical manifests.
|
|
2876
|
+
manifest.writtenTargets = Array.from(new Set(writtenTargets)).sort();
|
|
2858
2877
|
saveManifest(agent, version, manifest);
|
|
2859
2878
|
}
|
|
2860
2879
|
return result;
|
|
@@ -7,11 +7,7 @@
|
|
|
7
7
|
<key>CFBundleIdentifier</key>
|
|
8
8
|
<string>com.phnx-labs.agents-menubar</string>
|
|
9
9
|
<key>CFBundleName</key>
|
|
10
|
-
<string>
|
|
11
|
-
<key>CFBundleDisplayName</key>
|
|
12
|
-
<string>AGI Menu</string>
|
|
13
|
-
<key>CFBundleIconFile</key>
|
|
14
|
-
<string>AppIcon</string>
|
|
10
|
+
<string>Agents Menu Bar</string>
|
|
15
11
|
<key>CFBundlePackageType</key>
|
|
16
12
|
<string>APPL</string>
|
|
17
13
|
<key>CFBundleShortVersionString</key>
|
|
Binary file
|
|
@@ -3,22 +3,9 @@
|
|
|
3
3
|
<plist version="1.0">
|
|
4
4
|
<dict>
|
|
5
5
|
<key>files</key>
|
|
6
|
-
<dict
|
|
7
|
-
<key>Resources/AppIcon.icns</key>
|
|
8
|
-
<data>
|
|
9
|
-
jOjZVimcFRHoP2VPzgn8uM8mjkA=
|
|
10
|
-
</data>
|
|
11
|
-
</dict>
|
|
6
|
+
<dict/>
|
|
12
7
|
<key>files2</key>
|
|
13
|
-
<dict
|
|
14
|
-
<key>Resources/AppIcon.icns</key>
|
|
15
|
-
<dict>
|
|
16
|
-
<key>hash2</key>
|
|
17
|
-
<data>
|
|
18
|
-
GFvSLeNYJ3ASxW3OzcuiB4aID0gZUBkpozmWZkbTFNw=
|
|
19
|
-
</data>
|
|
20
|
-
</dict>
|
|
21
|
-
</dict>
|
|
8
|
+
<dict/>
|
|
22
9
|
<key>rules</key>
|
|
23
10
|
<dict>
|
|
24
11
|
<key>^Resources/</key>
|
|
@@ -75,6 +75,33 @@ export declare function generateServicePlist(execPath: string): string;
|
|
|
75
75
|
export declare function restartMenubarLaunchAgent(uid: number, plist: string, exec?: (cmd: string, args: readonly string[], opts: {
|
|
76
76
|
stdio: ['ignore', 'ignore', 'ignore'];
|
|
77
77
|
}) => Buffer): void;
|
|
78
|
+
/**
|
|
79
|
+
* Force a running helper off the binary that was just swapped underneath it.
|
|
80
|
+
*
|
|
81
|
+
* `installMenubarLaunchAgentOnUpgrade()` calls this after a heal actually
|
|
82
|
+
* replaces the on-disk bundle (a version bump or the ad-hoc -> Developer ID
|
|
83
|
+
* transition — see `menubarHealReplacedBundle`). `restartMenubarLaunchAgent`
|
|
84
|
+
* (run moments earlier via `installAndStartService`) already attempts
|
|
85
|
+
* bootout+bootstrap+kickstart, but that targets the GUI launchd domain and
|
|
86
|
+
* fails silently — `launchctl kickstart -k gui/<uid>/<label>` prints "Could not
|
|
87
|
+
* find service ... in domain for user gui" — from a shell with no Aqua session,
|
|
88
|
+
* which is the ordinary case for `agents` invoked from a terminal/tmux/ssh
|
|
89
|
+
* session rather than literally the login item (verified live). When that
|
|
90
|
+
* happens the plist and on-disk bundle are current but the LIVE process is
|
|
91
|
+
* still the old binary, so it requests Accessibility under the OLD code
|
|
92
|
+
* identity and the grant never sticks.
|
|
93
|
+
*
|
|
94
|
+
* `kickstart -k` (force-restart, unlike the bare kickstart already tried) is
|
|
95
|
+
* attempted first since it is the clean launchd-native path when it works. If
|
|
96
|
+
* launchd has no record of the service in this context either, fall back to
|
|
97
|
+
* ending the specific pid(s) already confirmed to be running the installed
|
|
98
|
+
* bundle (`ownProcesses`, resolved by the caller via `liveMenubarProcesses`) —
|
|
99
|
+
* never a broad pkill. The launchd plist has `KeepAlive=true`, so the ended
|
|
100
|
+
* process is relaunched from the swapped binary within seconds.
|
|
101
|
+
*/
|
|
102
|
+
export declare function restartMenubarHelperAfterSwap(uid: number, ownProcesses: readonly MenubarProcess[], exec?: (cmd: string, args: readonly string[], opts: {
|
|
103
|
+
stdio: ['ignore', 'ignore', 'ignore'];
|
|
104
|
+
}) => Buffer, kill?: (pid: number) => void): void;
|
|
78
105
|
/**
|
|
79
106
|
* Install + start the menu-bar helper as a launchd user service (idempotent).
|
|
80
107
|
* Clears the sticky opt-out, installs the .app, writes the plist, and
|
|
@@ -97,6 +124,23 @@ export declare function isMenubarStale(opts: {
|
|
|
97
124
|
currentVersion: string;
|
|
98
125
|
execExists: boolean;
|
|
99
126
|
}): boolean;
|
|
127
|
+
/**
|
|
128
|
+
* Pure decision (no I/O): did THIS heal actually replace bundle content a live
|
|
129
|
+
* process could be running stale, as opposed to a plist-only repoint?
|
|
130
|
+
*
|
|
131
|
+
* `stale` (version bump or missing exec) and `needsDevIdHeal` (ad-hoc ->
|
|
132
|
+
* Developer ID) both mean the shipped `.app` differs from what is already
|
|
133
|
+
* installed — the exact condition under which a running helper can be left on
|
|
134
|
+
* an old code identity after `enableMenubarService` swaps the bundle. A
|
|
135
|
+
* repoint-only heal (`menubarSetupNeedsRepoint()` alone, same version, same
|
|
136
|
+
* signing identity) does NOT count: RUSH-3005 already owns the churn from
|
|
137
|
+
* mixed-Node-interpreter repoints, and restarting the helper on every one of
|
|
138
|
+
* those would double that churn rather than fix this bug.
|
|
139
|
+
*/
|
|
140
|
+
export declare function menubarHealReplacedBundle(opts: {
|
|
141
|
+
stale: boolean;
|
|
142
|
+
needsDevIdHeal: boolean;
|
|
143
|
+
}): boolean;
|
|
100
144
|
/**
|
|
101
145
|
* Pure re-point decision (no I/O): the plist's baked interpreter/entry no longer
|
|
102
146
|
* match the install that is now running `agents`. This catches DUAL-INSTALL skew
|
|
@@ -110,6 +154,7 @@ export declare function isMenubarStale(opts: {
|
|
|
110
154
|
export declare function menubarPlistNeedsRepoint(opts: {
|
|
111
155
|
plistEntry: string | null;
|
|
112
156
|
plistNode: string | null;
|
|
157
|
+
plistNodeExists: boolean;
|
|
113
158
|
activeEntry: string | null;
|
|
114
159
|
activeNode: string | null;
|
|
115
160
|
}): boolean;
|
|
@@ -185,6 +230,31 @@ export declare function mayInstallMenubarHelper(opts: {
|
|
|
185
230
|
* throws into startup.
|
|
186
231
|
*/
|
|
187
232
|
export declare function installMenubarLaunchAgentOnUpgrade(): void;
|
|
233
|
+
/**
|
|
234
|
+
* Pure decision (no I/O): run the one-time `tccutil reset Accessibility` only
|
|
235
|
+
* on a real ad-hoc -> Developer ID transition, and only once ever. A machine
|
|
236
|
+
* that was always Developer ID never accumulated a dead TCC row under an
|
|
237
|
+
* ad-hoc identity, so resetting there would just force a needless re-prompt;
|
|
238
|
+
* a machine that already migrated must not be reset again on every heal.
|
|
239
|
+
*/
|
|
240
|
+
export declare function shouldMigrateMenubarTcc(opts: {
|
|
241
|
+
needsDevIdHeal: boolean;
|
|
242
|
+
alreadyMigrated: boolean;
|
|
243
|
+
}): boolean;
|
|
244
|
+
/**
|
|
245
|
+
* Reset the stale Accessibility grant TCC recorded against the pre-migration
|
|
246
|
+
* ad-hoc identity, then stamp the marker so this never runs again on this
|
|
247
|
+
* machine. Targets `SERVICE_LABEL_BASE` — the bundle's actual
|
|
248
|
+
* `CFBundleIdentifier`/codesign `--identifier` (menubar/scripts/build.sh) that
|
|
249
|
+
* TCC keys grants to — not the namespaced `serviceLabel()`, which exists only
|
|
250
|
+
* to keep launchd job labels apart under a redirected HOME (RUSH-2639) and is
|
|
251
|
+
* never the app's real identity. Best-effort: a missing/failing `tccutil`
|
|
252
|
+
* (older macOS, a sandboxed test HOME) must never block the heal that just
|
|
253
|
+
* fixed the signing identity itself.
|
|
254
|
+
*/
|
|
255
|
+
export declare function resetMenubarAccessibilityTcc(exec?: (cmd: string, args: readonly string[], opts: {
|
|
256
|
+
stdio: ['ignore', 'ignore', 'ignore'];
|
|
257
|
+
}) => Buffer): void;
|
|
188
258
|
/** One step of `agents menubar setup`, and how it came out. */
|
|
189
259
|
export interface SetupStep {
|
|
190
260
|
/** What was configured. */
|
|
@@ -272,3 +342,42 @@ export interface MenubarStatus {
|
|
|
272
342
|
disabledByUser: boolean;
|
|
273
343
|
}
|
|
274
344
|
export declare function getMenubarStatus(): MenubarStatus;
|
|
345
|
+
/** Read-only diagnostic for `agents menubar doctor` — probes, never mutates. */
|
|
346
|
+
export interface MenubarDoctorReport {
|
|
347
|
+
platform: string;
|
|
348
|
+
installPath: string | null;
|
|
349
|
+
installedVersion: string | null;
|
|
350
|
+
currentVersion: string;
|
|
351
|
+
versionMatches: boolean;
|
|
352
|
+
/** `unknown` on non-darwin or when nothing is installed to inspect. */
|
|
353
|
+
signingIdentity: 'developer-id' | 'ad-hoc' | 'unknown';
|
|
354
|
+
running: boolean;
|
|
355
|
+
/** A live helper pid started before the installed bundle's on-disk mtime —
|
|
356
|
+
* it is running the binary that has since been swapped underneath it. */
|
|
357
|
+
staleRunningProcess: PidStaleness[];
|
|
358
|
+
/** Grant likely needs to be re-made: an ad-hoc identity breaks on every
|
|
359
|
+
* update, or a live process is confirmed to predate the current bundle. */
|
|
360
|
+
accessibilityHintNeeded: boolean;
|
|
361
|
+
}
|
|
362
|
+
/** One live pid checked against the bundle's on-disk mtime. */
|
|
363
|
+
export interface PidStaleness {
|
|
364
|
+
pid: number;
|
|
365
|
+
stale: boolean;
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Pure comparison (no I/O) behind the stale-process check: a helper pid that
|
|
369
|
+
* started before the bundle on disk was last written is still running the
|
|
370
|
+
* PREVIOUS binary — the exact condition `restartMenubarHelperAfterSwap` exists
|
|
371
|
+
* to fix. Millisecond epoch timestamps in, so the truth table is unit-testable
|
|
372
|
+
* without a live process or filesystem.
|
|
373
|
+
*/
|
|
374
|
+
export declare function isMenubarProcessStaleAgainstBundle(pidStartedAtMs: number, bundleMtimeMs: number): boolean;
|
|
375
|
+
/**
|
|
376
|
+
* `agents menubar doctor` — everything a human needs to tell "why did
|
|
377
|
+
* Accessibility ask again" apart from "the helper is genuinely broken":
|
|
378
|
+
* install path, version skew, whether the signing identity is update-stable,
|
|
379
|
+
* and whether a live process predates the bundle it's supposed to be running.
|
|
380
|
+
* Read-only: no install, no restart, no TCC reset — `agents menubar setup`
|
|
381
|
+
* remains the command that fixes what this reports.
|
|
382
|
+
*/
|
|
383
|
+
export declare function buildMenubarDoctorReport(): MenubarDoctorReport;
|
|
@@ -353,6 +353,45 @@ export function restartMenubarLaunchAgent(uid, plist, exec = execFileSync) {
|
|
|
353
353
|
}
|
|
354
354
|
catch { /* best effort */ }
|
|
355
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
|
+
}
|
|
356
395
|
/**
|
|
357
396
|
* Install + start the menu-bar helper as a launchd user service (idempotent).
|
|
358
397
|
* Clears the sticky opt-out, installs the .app, writes the plist, and
|
|
@@ -425,6 +464,22 @@ function menubarSetupStale() {
|
|
|
425
464
|
execExists: fs.existsSync(installedExecutablePath()),
|
|
426
465
|
});
|
|
427
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
|
+
}
|
|
428
483
|
/**
|
|
429
484
|
* Pure re-point decision (no I/O): the plist's baked interpreter/entry no longer
|
|
430
485
|
* match the install that is now running `agents`. This catches DUAL-INSTALL skew
|
|
@@ -440,7 +495,12 @@ export function menubarPlistNeedsRepoint(opts) {
|
|
|
440
495
|
return false; // can't resolve the running install — don't churn
|
|
441
496
|
if (opts.plistEntry !== opts.activeEntry)
|
|
442
497
|
return true;
|
|
443
|
-
|
|
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))
|
|
444
504
|
return true;
|
|
445
505
|
return false;
|
|
446
506
|
}
|
|
@@ -457,9 +517,11 @@ function readPlistEnvValue(key) {
|
|
|
457
517
|
}
|
|
458
518
|
/** True when the installed plist points at a different install than the active one. */
|
|
459
519
|
function menubarSetupNeedsRepoint() {
|
|
520
|
+
const plistNode = readPlistEnvValue('AGENTS_NODE');
|
|
460
521
|
return menubarPlistNeedsRepoint({
|
|
461
522
|
plistEntry: readPlistEnvValue('AGENTS_ENTRY'),
|
|
462
|
-
plistNode
|
|
523
|
+
plistNode,
|
|
524
|
+
plistNodeExists: Boolean(plistNode) && fs.existsSync(plistNode),
|
|
463
525
|
activeEntry: resolveCliEntry(),
|
|
464
526
|
activeNode: process.execPath,
|
|
465
527
|
});
|
|
@@ -650,7 +712,8 @@ export function installMenubarLaunchAgentOnUpgrade() {
|
|
|
650
712
|
// installed copy is still ad-hoc while the shipped source is Developer ID
|
|
651
713
|
// (older heal path; Accessibility re-prompts until the identity is restored).
|
|
652
714
|
const needsDevIdHeal = installedNeedsDevIdHeal();
|
|
653
|
-
|
|
715
|
+
const stale = menubarSetupStale();
|
|
716
|
+
if (!(stale || menubarSetupNeedsRepoint() || needsDevIdHeal))
|
|
654
717
|
return;
|
|
655
718
|
// ...but a copy that does not own the helper only gets to act on that drift
|
|
656
719
|
// once per cooldown. Without the gate every coexisting install recopies the
|
|
@@ -661,8 +724,22 @@ export function installMenubarLaunchAgentOnUpgrade() {
|
|
|
661
724
|
// false without installing when the bundle fails the Gatekeeper check, and
|
|
662
725
|
// stamping first would spend the shared cooldown on a no-op — locking every
|
|
663
726
|
// non-owner out for another hour while nothing had been fixed.
|
|
664
|
-
if (enableMenubarService({ clearOptOut: false }))
|
|
727
|
+
if (enableMenubarService({ clearOptOut: false })) {
|
|
665
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
|
+
}
|
|
666
743
|
}
|
|
667
744
|
catch {
|
|
668
745
|
/* never block startup on the menu bar */
|
|
@@ -677,6 +754,46 @@ function installedNeedsDevIdHeal() {
|
|
|
677
754
|
return false;
|
|
678
755
|
return hasDeveloperIdSignature(src);
|
|
679
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
|
+
}
|
|
680
797
|
/**
|
|
681
798
|
* Decide which live helper processes must be ended so exactly one status item
|
|
682
799
|
* survives. Pure so the choice is unit-testable without a live menu bar.
|
|
@@ -867,3 +984,81 @@ export function getMenubarStatus() {
|
|
|
867
984
|
disabledByUser: menubarDisabledByUser(),
|
|
868
985
|
};
|
|
869
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
|
+
}
|
|
@@ -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
|
|
@@ -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;
|
|
Binary file
|
|
Binary file
|