@phnx-labs/agents-cli 1.22.41 → 1.22.42
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 +14 -0
- package/README.md +9 -3
- package/dist/bin/agents +0 -0
- package/dist/cli/command-registry.d.ts +2 -0
- package/dist/cli/command-registry.js +4 -0
- package/dist/commands/accounts.d.ts +29 -0
- package/dist/commands/accounts.js +220 -29
- package/dist/commands/alias.js +1 -1
- package/dist/commands/auth.d.ts +9 -0
- package/dist/commands/auth.js +104 -0
- package/dist/commands/browser-sessions-picker.js +42 -62
- package/dist/commands/browser.js +30 -38
- package/dist/commands/computer-actions.d.ts +1 -1
- package/dist/commands/computer-actions.js +1 -1
- package/dist/commands/computer-sessions-picker.js +32 -55
- package/dist/commands/computer.d.ts +1 -1
- package/dist/commands/computer.js +2 -2
- package/dist/commands/doctor.d.ts +1 -1
- package/dist/commands/doctor.js +3 -3
- package/dist/commands/feed.js +1 -1
- package/dist/commands/hooks.js +1 -1
- package/dist/commands/import.js +1 -1
- package/dist/commands/insights.js +163 -85
- package/dist/commands/inspect.js +2 -2
- package/dist/commands/mine.js +1 -1
- package/dist/commands/org.d.ts +6 -0
- package/dist/commands/org.js +174 -0
- package/dist/commands/packages.js +1 -1
- package/dist/commands/prune.js +1 -1
- package/dist/commands/routines.js +1 -1
- package/dist/commands/run-account-picker.d.ts +19 -0
- package/dist/commands/run-account-picker.js +81 -0
- package/dist/commands/sessions-picker-factory.d.ts +40 -0
- package/dist/commands/sessions-picker-factory.js +67 -0
- package/dist/commands/setup-browser.js +4 -4
- package/dist/commands/setup-mine.js +1 -1
- package/dist/commands/setup-preferences.js +2 -2
- package/dist/commands/setup.js +5 -3
- package/dist/commands/sync.js +1 -1
- package/dist/commands/versions.js +1 -1
- package/dist/commands/view.js +1 -1
- package/dist/lib/account-capabilities.d.ts +10 -0
- package/dist/lib/account-capabilities.js +34 -0
- package/dist/lib/agent-spec/agents.d.ts +584 -0
- package/dist/lib/agent-spec/agents.js +3127 -0
- package/dist/lib/agent-spec/provider.js +1 -1
- package/dist/lib/agents.d.ts +2 -584
- package/dist/lib/agents.js +2 -3127
- package/dist/lib/browser/drivers/local.d.ts +8 -2
- package/dist/lib/browser/drivers/local.js +8 -2
- package/dist/lib/browser/drivers/ssh.d.ts +8 -2
- package/dist/lib/browser/drivers/ssh.js +10 -4
- package/dist/lib/browser/hygiene.d.ts +1 -1
- package/dist/lib/browser/hygiene.js +5 -1
- package/dist/lib/browser/profiles.d.ts +74 -1
- package/dist/lib/browser/profiles.js +114 -5
- package/dist/lib/browser/runtime-state.d.ts +11 -5
- package/dist/lib/browser/runtime-state.js +13 -9
- package/dist/lib/browser/service.d.ts +68 -30
- package/dist/lib/browser/service.js +305 -194
- package/dist/lib/browser/types.d.ts +95 -3
- package/dist/lib/browser/types.js +51 -0
- package/dist/lib/catchup.js +1 -1
- package/dist/lib/computer/actions.d.ts +1 -1
- package/dist/lib/computer/actions.js +1 -1
- package/dist/lib/{computer-rpc.js → computer/computer-rpc.js} +5 -5
- package/dist/lib/computer/dispatch.d.ts +1 -1
- package/dist/lib/computer/download.d.ts +1 -1
- package/dist/lib/computer/download.js +3 -3
- package/dist/lib/{ssh-tunnel.d.ts → computer/ssh-tunnel.d.ts} +1 -1
- package/dist/lib/{ssh-tunnel.js → computer/ssh-tunnel.js} +12 -12
- package/dist/lib/daemon/daemon.js +1 -1
- package/dist/lib/{runner.d.ts → daemon/runner.d.ts} +10 -10
- package/dist/lib/{runner.js → daemon/runner.js} +40 -114
- package/dist/lib/devices/doctor-findings.d.ts +1 -1
- package/dist/lib/devices/doctor-findings.js +3 -3
- package/dist/lib/devices/fleet-inventory.js +1 -1
- package/dist/lib/devices/harness-inventory.js +1 -1
- package/dist/lib/devices/registry.js +4 -22
- package/dist/lib/doctor-diff.d.ts +1 -1
- package/dist/lib/doctor-diff.js +1 -1
- package/dist/lib/drift.js +1 -1
- package/dist/lib/entitlement.d.ts +31 -0
- package/dist/lib/entitlement.js +137 -0
- package/dist/lib/exec.js +39 -202
- package/dist/lib/feed/feed.js +2 -8
- package/dist/lib/fs-atomic.d.ts +7 -0
- package/dist/lib/fs-atomic.js +9 -0
- package/dist/lib/harness/adapter.d.ts +160 -0
- package/dist/lib/harness/adapter.js +47 -0
- package/dist/lib/harness/adapters/claude.d.ts +2 -0
- package/dist/lib/harness/adapters/claude.js +110 -0
- package/dist/lib/harness/adapters/codex.d.ts +2 -0
- package/dist/lib/harness/adapters/codex.js +83 -0
- package/dist/lib/harness/adapters/copilot.d.ts +2 -0
- package/dist/lib/harness/adapters/copilot.js +23 -0
- package/dist/lib/harness/adapters/cursor.d.ts +2 -0
- package/dist/lib/harness/adapters/cursor.js +38 -0
- package/dist/lib/harness/adapters/droid.d.ts +2 -0
- package/dist/lib/harness/adapters/droid.js +17 -0
- package/dist/lib/harness/adapters/grok.d.ts +2 -0
- package/dist/lib/harness/adapters/grok.js +14 -0
- package/dist/lib/harness/adapters/kimi.d.ts +2 -0
- package/dist/lib/harness/adapters/kimi.js +49 -0
- package/dist/lib/harness/adapters/muse.d.ts +2 -0
- package/dist/lib/harness/adapters/muse.js +44 -0
- package/dist/lib/harness/adapters/opencode.d.ts +2 -0
- package/dist/lib/harness/adapters/opencode.js +14 -0
- package/dist/lib/harness/exec-config-version.d.ts +8 -0
- package/dist/lib/harness/exec-config-version.js +22 -0
- package/dist/lib/harness/index.d.ts +1 -0
- package/dist/lib/harness/index.js +34 -0
- package/dist/lib/{hooks.d.ts → hooks/install.d.ts} +1 -1
- package/dist/lib/{hooks.js → hooks/install.js} +11 -11
- package/dist/lib/import.js +1 -1
- package/dist/lib/installations/index.d.ts +4 -4
- package/dist/lib/installations/index.js +3 -4
- package/dist/lib/installations/migrate.js +6 -6
- package/dist/lib/{shims.d.ts → installations/shims.d.ts} +2 -2
- package/dist/lib/{shims.js → installations/shims.js} +12 -114
- package/dist/lib/installations/store.d.ts +141 -3
- package/dist/lib/installations/store.js +475 -5
- package/dist/lib/installations/versions.d.ts +1 -126
- package/dist/lib/installations/versions.js +5 -465
- package/dist/lib/isolation-boundary-report.d.ts +1 -1
- package/dist/lib/isolation-boundary-report.js +1 -1
- package/dist/lib/mailbox-gc.js +2 -3
- package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/monitors/dispatch.d.ts +1 -1
- package/dist/lib/monitors/dispatch.js +2 -2
- package/dist/lib/monitors/state.d.ts +1 -1
- package/dist/lib/plugins/plugins.js +1 -1
- package/dist/lib/plugins/skills.js +1 -1
- package/dist/lib/prix-account.d.ts +158 -0
- package/dist/lib/prix-account.js +214 -0
- package/dist/lib/project-key.js +2 -2
- package/dist/lib/project-launch.js +1 -1
- package/dist/lib/refresh.js +2 -2
- package/dist/lib/resource-inventory.d.ts +1 -1
- package/dist/lib/resource-inventory.js +1 -1
- package/dist/lib/rules/rules.js +1 -1
- package/dist/lib/scheduling/routines.js +1 -1
- 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/self-heal/checks/hook-runtime.js +1 -1
- package/dist/lib/self-heal/checks/path.js +1 -1
- package/dist/lib/self-heal/checks/shadowing.js +1 -1
- package/dist/lib/self-heal/checks/shims.js +1 -1
- package/dist/lib/session/discover.d.ts +39 -0
- package/dist/lib/session/discover.js +3 -3
- package/dist/lib/session/parse.js +24 -42
- package/dist/lib/session/recovery.js +1 -1
- package/dist/lib/staleness/prune.d.ts +1 -1
- package/dist/lib/staleness/prune.js +1 -1
- package/dist/lib/staleness/writers/hooks.js +1 -1
- package/dist/lib/startup/command-registry.js +1 -1
- package/dist/lib/teams/agents.js +2 -19
- package/dist/lib/teams/parsers.js +20 -26
- package/dist/lib/teams/registry.js +2 -21
- package/dist/lib/triggers/handlers.js +1 -1
- package/dist/lib/triggers/webhook.js +1 -1
- package/dist/lib/uninstall.js +1 -1
- package/dist/lib/version.d.ts +9 -2
- package/dist/lib/version.js +11 -9
- package/package.json +1 -1
- /package/dist/lib/{computer-rpc.d.ts → computer/computer-rpc.d.ts} +0 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client for the Rush account layer (`api.prix.dev`) — backs `agents auth` and
|
|
3
|
+
* `agents org`. `agents org` maps to `/api/v1/spaces`, not `/api/v1/orgs`: spaces
|
|
4
|
+
* already carry the free-tier caps (1 owned space, 3 members) and can exist
|
|
5
|
+
* standalone with no parent organization, matching a "just want a team" CLI flow
|
|
6
|
+
* better than the heavier enterprise-tenancy `orgs` routes (domain, SSO). See
|
|
7
|
+
* `.agents/artifacts/2026-08-20/agi-cli-client-integration-spec.md` §4.2 in the
|
|
8
|
+
* agi-cli-web repo for the full reasoning and the live-confirmed route/shape audit.
|
|
9
|
+
*
|
|
10
|
+
* Token custody: `agents auth login` writes its own session file
|
|
11
|
+
* (`getPrixSessionFile()`), separate from `~/.rush/user.yaml`, so `agents auth
|
|
12
|
+
* logout` never signs the user out of `rush` (the two CLIs share a backend
|
|
13
|
+
* account, not a credential store). Reads fall back to `~/.rush/user.yaml`
|
|
14
|
+
* (the pattern `readRushToken` in `lib/secrets/drivers/rush.ts` already uses)
|
|
15
|
+
* so a user who is only signed in via `rush login` still gets a working
|
|
16
|
+
* `agents auth whoami` / `agents org` with zero extra login step.
|
|
17
|
+
*/
|
|
18
|
+
export declare const PRIX_API_BASE = "https://api.prix.dev";
|
|
19
|
+
/** The session `agents auth login` owns. */
|
|
20
|
+
export interface PrixSession {
|
|
21
|
+
access_token: string;
|
|
22
|
+
refresh_token?: string;
|
|
23
|
+
/** Unix ms. */
|
|
24
|
+
expires_at?: number;
|
|
25
|
+
email?: string;
|
|
26
|
+
userId?: string;
|
|
27
|
+
}
|
|
28
|
+
export declare function getPrixSessionFile(): string;
|
|
29
|
+
export declare function readPrixSession(): PrixSession | null;
|
|
30
|
+
export declare function writePrixSession(session: PrixSession): void;
|
|
31
|
+
export declare function clearPrixSession(): boolean;
|
|
32
|
+
/** Where the caller's Bearer token came from — surfaced by `whoami` so the user knows which login is live. */
|
|
33
|
+
export type PrixTokenSource = 'agents' | 'rush';
|
|
34
|
+
export declare function resolvePrixToken(): {
|
|
35
|
+
token: string;
|
|
36
|
+
source: PrixTokenSource;
|
|
37
|
+
} | null;
|
|
38
|
+
export declare class PrixApiError extends Error {
|
|
39
|
+
status: number;
|
|
40
|
+
constructor(status: number, message: string);
|
|
41
|
+
}
|
|
42
|
+
export interface WhoAmI {
|
|
43
|
+
userId: string;
|
|
44
|
+
email: string;
|
|
45
|
+
valid: true;
|
|
46
|
+
}
|
|
47
|
+
/** `GET /api/v1/auth/me` — live-confirmed shape: `{email, userId, valid}`. */
|
|
48
|
+
export declare function fetchWhoAmI(token?: string): Promise<WhoAmI>;
|
|
49
|
+
export interface DeviceAuthorization {
|
|
50
|
+
device_code: string;
|
|
51
|
+
user_code: string;
|
|
52
|
+
verification_uri: string;
|
|
53
|
+
verification_uri_complete: string;
|
|
54
|
+
expires_in: number;
|
|
55
|
+
interval: number;
|
|
56
|
+
}
|
|
57
|
+
/** `POST /api/v1/auth/device/authorization` — public, no token. */
|
|
58
|
+
export declare function startDeviceAuthorization(): Promise<DeviceAuthorization>;
|
|
59
|
+
export type DeviceTokenPoll = {
|
|
60
|
+
status: 'authorized';
|
|
61
|
+
access_token: string;
|
|
62
|
+
refresh_token?: string;
|
|
63
|
+
expires_in?: number;
|
|
64
|
+
user: {
|
|
65
|
+
email: string;
|
|
66
|
+
id: string;
|
|
67
|
+
};
|
|
68
|
+
} | {
|
|
69
|
+
status: 'pending';
|
|
70
|
+
} | {
|
|
71
|
+
status: 'slow_down';
|
|
72
|
+
} | {
|
|
73
|
+
status: 'expired';
|
|
74
|
+
} | {
|
|
75
|
+
status: 'denied';
|
|
76
|
+
};
|
|
77
|
+
/** `POST /api/v1/auth/device/token` — one poll attempt. Callers own the interval loop. */
|
|
78
|
+
export declare function pollDeviceToken(deviceCode: string): Promise<DeviceTokenPoll>;
|
|
79
|
+
export interface SpaceSummary {
|
|
80
|
+
id: string;
|
|
81
|
+
slug: string;
|
|
82
|
+
name: string;
|
|
83
|
+
organization_id: string | null;
|
|
84
|
+
owner_user_id: string;
|
|
85
|
+
invite_code?: string;
|
|
86
|
+
user_role: 'owner' | 'admin' | 'member';
|
|
87
|
+
created_at: string;
|
|
88
|
+
}
|
|
89
|
+
export interface SpaceMember {
|
|
90
|
+
user_id: string;
|
|
91
|
+
email: string;
|
|
92
|
+
name?: string;
|
|
93
|
+
avatar_url?: string;
|
|
94
|
+
role: 'owner' | 'admin' | 'member';
|
|
95
|
+
joined_at: string;
|
|
96
|
+
}
|
|
97
|
+
/** `GET /api/v1/spaces` — live-confirmed: array of `SpaceSummary`. */
|
|
98
|
+
export declare function listSpaces(): Promise<SpaceSummary[]>;
|
|
99
|
+
/** `POST /api/v1/spaces` — 403 if the caller already owns a space (free tier: 1). */
|
|
100
|
+
export declare function createSpace(input: {
|
|
101
|
+
name: string;
|
|
102
|
+
slug: string;
|
|
103
|
+
description?: string;
|
|
104
|
+
}): Promise<SpaceSummary>;
|
|
105
|
+
/** `GET /api/v1/spaces/:id` — requires membership. */
|
|
106
|
+
export declare function getSpace(spaceId: string): Promise<SpaceSummary>;
|
|
107
|
+
/** `GET /api/v1/spaces/:id/members`. */
|
|
108
|
+
export declare function listSpaceMembers(spaceId: string): Promise<SpaceMember[]>;
|
|
109
|
+
export type CreateSpaceInviteResult = {
|
|
110
|
+
invited: true;
|
|
111
|
+
email: string;
|
|
112
|
+
role: string;
|
|
113
|
+
member_added: true;
|
|
114
|
+
} | {
|
|
115
|
+
invited: true;
|
|
116
|
+
email: string;
|
|
117
|
+
role: string;
|
|
118
|
+
invite_code: string;
|
|
119
|
+
member_added?: false;
|
|
120
|
+
};
|
|
121
|
+
/** `POST /api/v1/spaces/:id/invites` — sends a real email for the pending-invite path. */
|
|
122
|
+
export declare function createSpaceInvite(spaceId: string, email: string, role?: 'admin' | 'member'): Promise<CreateSpaceInviteResult>;
|
|
123
|
+
export interface SpaceInvite {
|
|
124
|
+
id: string;
|
|
125
|
+
space_id: string;
|
|
126
|
+
email: string;
|
|
127
|
+
role: 'admin' | 'member';
|
|
128
|
+
invite_code: string;
|
|
129
|
+
created_at: string;
|
|
130
|
+
}
|
|
131
|
+
/** `GET /api/v1/spaces/:id/invites`. */
|
|
132
|
+
export declare function listSpaceInvites(spaceId: string): Promise<SpaceInvite[]>;
|
|
133
|
+
/** `DELETE /api/v1/spaces/:id/invites/:inviteId`. */
|
|
134
|
+
export declare function revokeSpaceInvite(spaceId: string, inviteId: string): Promise<{
|
|
135
|
+
revoked: true;
|
|
136
|
+
}>;
|
|
137
|
+
/** `PATCH /api/v1/spaces/:id/members/:userId` — owner-only for admin changes. Route takes userId, not email. */
|
|
138
|
+
export declare function updateSpaceMemberRole(spaceId: string, userId: string, role: 'admin' | 'member'): Promise<{
|
|
139
|
+
user_id: string;
|
|
140
|
+
role: string;
|
|
141
|
+
updated: true;
|
|
142
|
+
}>;
|
|
143
|
+
/** `DELETE /api/v1/spaces/:id/members/:userId` — owner, admin, or the member themself (leave). */
|
|
144
|
+
export declare function removeSpaceMember(spaceId: string, userId: string): Promise<void>;
|
|
145
|
+
/** `DELETE /api/v1/spaces/:id` — soft delete, 30-day restore window. */
|
|
146
|
+
export declare function deleteSpace(spaceId: string): Promise<void>;
|
|
147
|
+
/** `agents-cli-space-name` -> `agi-cli-space-name`; lowercase, hyphenated, matches the backend's `^[a-z0-9-]+$` slug rule. */
|
|
148
|
+
export declare function slugify(name: string): string;
|
|
149
|
+
/**
|
|
150
|
+
* Resolve the `--space` a command should act on: an explicit id/slug match
|
|
151
|
+
* against the caller's own space list, or — with nothing passed — the
|
|
152
|
+
* caller's sole space (free tier caps ownership at one, so this is almost
|
|
153
|
+
* always unambiguous). Pure over an already-fetched list so it's cheaply
|
|
154
|
+
* unit-testable with a fixture.
|
|
155
|
+
*/
|
|
156
|
+
export declare function resolveSpaceFromList(spaces: SpaceSummary[], explicit?: string): SpaceSummary;
|
|
157
|
+
/** Resolve a member's email to their `user_id` from an already-fetched member list. */
|
|
158
|
+
export declare function resolveMemberFromList(members: SpaceMember[], email: string): SpaceMember;
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client for the Rush account layer (`api.prix.dev`) — backs `agents auth` and
|
|
3
|
+
* `agents org`. `agents org` maps to `/api/v1/spaces`, not `/api/v1/orgs`: spaces
|
|
4
|
+
* already carry the free-tier caps (1 owned space, 3 members) and can exist
|
|
5
|
+
* standalone with no parent organization, matching a "just want a team" CLI flow
|
|
6
|
+
* better than the heavier enterprise-tenancy `orgs` routes (domain, SSO). See
|
|
7
|
+
* `.agents/artifacts/2026-08-20/agi-cli-client-integration-spec.md` §4.2 in the
|
|
8
|
+
* agi-cli-web repo for the full reasoning and the live-confirmed route/shape audit.
|
|
9
|
+
*
|
|
10
|
+
* Token custody: `agents auth login` writes its own session file
|
|
11
|
+
* (`getPrixSessionFile()`), separate from `~/.rush/user.yaml`, so `agents auth
|
|
12
|
+
* logout` never signs the user out of `rush` (the two CLIs share a backend
|
|
13
|
+
* account, not a credential store). Reads fall back to `~/.rush/user.yaml`
|
|
14
|
+
* (the pattern `readRushToken` in `lib/secrets/drivers/rush.ts` already uses)
|
|
15
|
+
* so a user who is only signed in via `rush login` still gets a working
|
|
16
|
+
* `agents auth whoami` / `agents org` with zero extra login step.
|
|
17
|
+
*/
|
|
18
|
+
import * as fs from 'fs';
|
|
19
|
+
import * as os from 'os';
|
|
20
|
+
import * as path from 'path';
|
|
21
|
+
import * as yaml from 'yaml';
|
|
22
|
+
import { getRuntimeStateDir } from './state.js';
|
|
23
|
+
export const PRIX_API_BASE = 'https://api.prix.dev';
|
|
24
|
+
const RUSH_USER_YAML = path.join(os.homedir(), '.rush', 'user.yaml');
|
|
25
|
+
/** Computed per-call (not cached at module load) so `AGENTS_STATE_DIR` overrides in tests take effect. */
|
|
26
|
+
function prixSessionFile() {
|
|
27
|
+
return path.join(getRuntimeStateDir(), 'prix-account.json');
|
|
28
|
+
}
|
|
29
|
+
/** Read the token `rush login` wrote, with no expiry check — the same shape `readRushToken` reads. */
|
|
30
|
+
function readRushSessionToken() {
|
|
31
|
+
if (!fs.existsSync(RUSH_USER_YAML))
|
|
32
|
+
return null;
|
|
33
|
+
try {
|
|
34
|
+
const data = yaml.parse(fs.readFileSync(RUSH_USER_YAML, 'utf-8'));
|
|
35
|
+
return data?.session?.access_token ?? null;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export function getPrixSessionFile() {
|
|
42
|
+
return prixSessionFile();
|
|
43
|
+
}
|
|
44
|
+
export function readPrixSession() {
|
|
45
|
+
const file = prixSessionFile();
|
|
46
|
+
if (!fs.existsSync(file))
|
|
47
|
+
return null;
|
|
48
|
+
try {
|
|
49
|
+
const parsed = JSON.parse(fs.readFileSync(file, 'utf-8'));
|
|
50
|
+
if (!parsed || typeof parsed.access_token !== 'string')
|
|
51
|
+
return null;
|
|
52
|
+
return parsed;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export function writePrixSession(session) {
|
|
59
|
+
const file = prixSessionFile();
|
|
60
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
61
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
62
|
+
fs.writeFileSync(tmp, JSON.stringify(session, null, 2), { mode: 0o600 });
|
|
63
|
+
fs.renameSync(tmp, file);
|
|
64
|
+
}
|
|
65
|
+
export function clearPrixSession() {
|
|
66
|
+
const file = prixSessionFile();
|
|
67
|
+
if (!fs.existsSync(file))
|
|
68
|
+
return false;
|
|
69
|
+
fs.rmSync(file);
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
export function resolvePrixToken() {
|
|
73
|
+
const own = readPrixSession();
|
|
74
|
+
if (own?.access_token)
|
|
75
|
+
return { token: own.access_token, source: 'agents' };
|
|
76
|
+
const rushToken = readRushSessionToken();
|
|
77
|
+
if (rushToken)
|
|
78
|
+
return { token: rushToken, source: 'rush' };
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
export class PrixApiError extends Error {
|
|
82
|
+
status;
|
|
83
|
+
constructor(status, message) {
|
|
84
|
+
super(message);
|
|
85
|
+
this.status = status;
|
|
86
|
+
this.name = 'PrixApiError';
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
async function prixRequest(method, endpoint, opts = {}) {
|
|
90
|
+
const auth = opts.auth !== false;
|
|
91
|
+
let token = opts.token;
|
|
92
|
+
if (auth && !token) {
|
|
93
|
+
const resolved = resolvePrixToken();
|
|
94
|
+
if (!resolved)
|
|
95
|
+
throw new Error("Not signed in. Run 'agents auth login' first.");
|
|
96
|
+
token = resolved.token;
|
|
97
|
+
}
|
|
98
|
+
const headers = { 'Content-Type': 'application/json' };
|
|
99
|
+
if (token)
|
|
100
|
+
headers.Authorization = `Bearer ${token}`;
|
|
101
|
+
const res = await fetch(`${PRIX_API_BASE}${endpoint}`, {
|
|
102
|
+
method,
|
|
103
|
+
headers,
|
|
104
|
+
body: opts.body === undefined ? undefined : JSON.stringify(opts.body),
|
|
105
|
+
});
|
|
106
|
+
const text = await res.text();
|
|
107
|
+
const data = text ? JSON.parse(text) : undefined;
|
|
108
|
+
if (!res.ok) {
|
|
109
|
+
const message = (data && typeof data === 'object' && 'error' in data) ? String(data.error) : `${res.status} ${res.statusText}`;
|
|
110
|
+
throw new PrixApiError(res.status, message);
|
|
111
|
+
}
|
|
112
|
+
return data;
|
|
113
|
+
}
|
|
114
|
+
/** `GET /api/v1/auth/me` — live-confirmed shape: `{email, userId, valid}`. */
|
|
115
|
+
export async function fetchWhoAmI(token) {
|
|
116
|
+
return prixRequest('GET', '/api/v1/auth/me', { token });
|
|
117
|
+
}
|
|
118
|
+
/** `POST /api/v1/auth/device/authorization` — public, no token. */
|
|
119
|
+
export async function startDeviceAuthorization() {
|
|
120
|
+
return prixRequest('POST', '/api/v1/auth/device/authorization', { auth: false, body: {} });
|
|
121
|
+
}
|
|
122
|
+
/** `POST /api/v1/auth/device/token` — one poll attempt. Callers own the interval loop. */
|
|
123
|
+
export async function pollDeviceToken(deviceCode) {
|
|
124
|
+
try {
|
|
125
|
+
const data = await prixRequest('POST', '/api/v1/auth/device/token', { auth: false, body: { grant_type: 'urn:ietf:params:oauth:grant-type:device_code', device_code: deviceCode } });
|
|
126
|
+
return { status: 'authorized', ...data };
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
if (err instanceof PrixApiError) {
|
|
130
|
+
if (err.message.includes('authorization_pending'))
|
|
131
|
+
return { status: 'pending' };
|
|
132
|
+
if (err.message.includes('slow_down'))
|
|
133
|
+
return { status: 'slow_down' };
|
|
134
|
+
if (err.message.includes('expired_token'))
|
|
135
|
+
return { status: 'expired' };
|
|
136
|
+
if (err.message.includes('access_denied'))
|
|
137
|
+
return { status: 'denied' };
|
|
138
|
+
}
|
|
139
|
+
throw err;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/** `GET /api/v1/spaces` — live-confirmed: array of `SpaceSummary`. */
|
|
143
|
+
export async function listSpaces() {
|
|
144
|
+
return prixRequest('GET', '/api/v1/spaces');
|
|
145
|
+
}
|
|
146
|
+
/** `POST /api/v1/spaces` — 403 if the caller already owns a space (free tier: 1). */
|
|
147
|
+
export async function createSpace(input) {
|
|
148
|
+
return prixRequest('POST', '/api/v1/spaces', { body: input });
|
|
149
|
+
}
|
|
150
|
+
/** `GET /api/v1/spaces/:id` — requires membership. */
|
|
151
|
+
export async function getSpace(spaceId) {
|
|
152
|
+
return prixRequest('GET', `/api/v1/spaces/${encodeURIComponent(spaceId)}`);
|
|
153
|
+
}
|
|
154
|
+
/** `GET /api/v1/spaces/:id/members`. */
|
|
155
|
+
export async function listSpaceMembers(spaceId) {
|
|
156
|
+
return prixRequest('GET', `/api/v1/spaces/${encodeURIComponent(spaceId)}/members`);
|
|
157
|
+
}
|
|
158
|
+
/** `POST /api/v1/spaces/:id/invites` — sends a real email for the pending-invite path. */
|
|
159
|
+
export async function createSpaceInvite(spaceId, email, role = 'member') {
|
|
160
|
+
return prixRequest('POST', `/api/v1/spaces/${encodeURIComponent(spaceId)}/invites`, { body: { email, role } });
|
|
161
|
+
}
|
|
162
|
+
/** `GET /api/v1/spaces/:id/invites`. */
|
|
163
|
+
export async function listSpaceInvites(spaceId) {
|
|
164
|
+
return prixRequest('GET', `/api/v1/spaces/${encodeURIComponent(spaceId)}/invites`);
|
|
165
|
+
}
|
|
166
|
+
/** `DELETE /api/v1/spaces/:id/invites/:inviteId`. */
|
|
167
|
+
export async function revokeSpaceInvite(spaceId, inviteId) {
|
|
168
|
+
return prixRequest('DELETE', `/api/v1/spaces/${encodeURIComponent(spaceId)}/invites/${encodeURIComponent(inviteId)}`);
|
|
169
|
+
}
|
|
170
|
+
/** `PATCH /api/v1/spaces/:id/members/:userId` — owner-only for admin changes. Route takes userId, not email. */
|
|
171
|
+
export async function updateSpaceMemberRole(spaceId, userId, role) {
|
|
172
|
+
return prixRequest('PATCH', `/api/v1/spaces/${encodeURIComponent(spaceId)}/members/${encodeURIComponent(userId)}`, { body: { role } });
|
|
173
|
+
}
|
|
174
|
+
/** `DELETE /api/v1/spaces/:id/members/:userId` — owner, admin, or the member themself (leave). */
|
|
175
|
+
export async function removeSpaceMember(spaceId, userId) {
|
|
176
|
+
await prixRequest('DELETE', `/api/v1/spaces/${encodeURIComponent(spaceId)}/members/${encodeURIComponent(userId)}`);
|
|
177
|
+
}
|
|
178
|
+
/** `DELETE /api/v1/spaces/:id` — soft delete, 30-day restore window. */
|
|
179
|
+
export async function deleteSpace(spaceId) {
|
|
180
|
+
await prixRequest('DELETE', `/api/v1/spaces/${encodeURIComponent(spaceId)}`);
|
|
181
|
+
}
|
|
182
|
+
/** `agents-cli-space-name` -> `agi-cli-space-name`; lowercase, hyphenated, matches the backend's `^[a-z0-9-]+$` slug rule. */
|
|
183
|
+
export function slugify(name) {
|
|
184
|
+
const slug = name.toLowerCase().trim().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
|
|
185
|
+
return slug || 'space';
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Resolve the `--space` a command should act on: an explicit id/slug match
|
|
189
|
+
* against the caller's own space list, or — with nothing passed — the
|
|
190
|
+
* caller's sole space (free tier caps ownership at one, so this is almost
|
|
191
|
+
* always unambiguous). Pure over an already-fetched list so it's cheaply
|
|
192
|
+
* unit-testable with a fixture.
|
|
193
|
+
*/
|
|
194
|
+
export function resolveSpaceFromList(spaces, explicit) {
|
|
195
|
+
if (explicit) {
|
|
196
|
+
const match = spaces.find(s => s.id === explicit || s.slug === explicit);
|
|
197
|
+
if (!match)
|
|
198
|
+
throw new Error(`No space matching '${explicit}'. Run 'agents org list' to see your spaces.`);
|
|
199
|
+
return match;
|
|
200
|
+
}
|
|
201
|
+
if (spaces.length === 0)
|
|
202
|
+
throw new Error("You have no spaces. Create one with 'agents org create <name>'.");
|
|
203
|
+
if (spaces.length > 1) {
|
|
204
|
+
throw new Error(`You belong to ${spaces.length} spaces — pass --space <id-or-slug>: ${spaces.map(s => s.slug).join(', ')}`);
|
|
205
|
+
}
|
|
206
|
+
return spaces[0];
|
|
207
|
+
}
|
|
208
|
+
/** Resolve a member's email to their `user_id` from an already-fetched member list. */
|
|
209
|
+
export function resolveMemberFromList(members, email) {
|
|
210
|
+
const match = members.find(m => m.email.toLowerCase() === email.toLowerCase());
|
|
211
|
+
if (!match)
|
|
212
|
+
throw new Error(`'${email}' is not a member of this space. Run 'agents org members' to see who is.`);
|
|
213
|
+
return match;
|
|
214
|
+
}
|
package/dist/lib/project-key.js
CHANGED
|
@@ -58,8 +58,8 @@ export function repoRootForCwd(dir, home = os.homedir()) {
|
|
|
58
58
|
// Never climb past $HOME: an ancestor of home (/tmp, /, ...) is not part of
|
|
59
59
|
// any project this cwd belongs to, and treating one as the repo root would
|
|
60
60
|
// let unrelated host state (e.g. a stray /tmp/.git) swallow every loose
|
|
61
|
-
// directory under home. Mirrors the shim's own home boundary — see
|
|
62
|
-
//
|
|
61
|
+
// directory under home. Mirrors the shim's own home boundary — see the codex
|
|
62
|
+
// adapter's shimExecTail (lib/harness/adapters/codex.ts).
|
|
63
63
|
if (current === stop)
|
|
64
64
|
return undefined;
|
|
65
65
|
const parent = path.dirname(current);
|
|
@@ -86,7 +86,7 @@ export function runLaunchSync(opts) {
|
|
|
86
86
|
}
|
|
87
87
|
/**
|
|
88
88
|
* Path of the shim's skip-fast sentinel for this (agent, version, cwd) tuple.
|
|
89
|
-
* Must match the SHIM-SIDE format in src/lib/shims.ts (PROJECT_SLUG derivation),
|
|
89
|
+
* Must match the SHIM-SIDE format in src/lib/installations/shims.ts (PROJECT_SLUG derivation),
|
|
90
90
|
* which is the canonical `toPortableKey` mapping: drop the Windows drive colon
|
|
91
91
|
* and fold `\`, `/`, and ` ` → `_`. On POSIX this is byte-identical to the old
|
|
92
92
|
* `/` and ` ` → `_` slug; on Windows it yields a legal filename (no `C:\`).
|
package/dist/lib/refresh.js
CHANGED
|
@@ -20,8 +20,8 @@ import { readManifest, MANIFEST_FILENAME } from './manifest.js';
|
|
|
20
20
|
import { getUserAgentsDir } from './state.js';
|
|
21
21
|
import { installVersion, listInstalledVersions, isVersionIsolated, getGlobalDefault, setGlobalDefault, getVersionHomePath, syncResourcesToVersion, getAvailableResources, getActuallySyncedResources, getNewResources, getProjectOnlyResources, hasNewResources, promptNewResourceSelection, promptResourceSelection, resolveConfiguredAgentTargets, } from './installations/versions.js';
|
|
22
22
|
import { listCliStatus, installCli, describeMethod, describeCheck, selectInstallMethod, } from './cli-resources.js';
|
|
23
|
-
import { ensureShimCurrent, isShimsInPath, addShimsToPath, getPathSetupInstructions, switchConfigSymlink, switchHomeFileSymlinks, } from './shims.js';
|
|
24
|
-
import { parseHookManifest, registerHooksToSettings } from './hooks.js';
|
|
23
|
+
import { ensureShimCurrent, isShimsInPath, addShimsToPath, getPathSetupInstructions, switchConfigSymlink, switchHomeFileSymlinks, } from './installations/shims.js';
|
|
24
|
+
import { parseHookManifest, registerHooksToSettings } from './hooks/install.js';
|
|
25
25
|
import { isPromptCancelled } from './format.js';
|
|
26
26
|
/**
|
|
27
27
|
* Old repo layout stored promptcuts under claude/promptcuts.yaml (agent-scoped).
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* muse), Grok hooks.json, and Kimi config.toml via checkVersionHookWiring.
|
|
19
19
|
*/
|
|
20
20
|
import type { AgentId } from './types.js';
|
|
21
|
-
import { type HookWiringReport } from './hooks.js';
|
|
21
|
+
import { type HookWiringReport } from './hooks/install.js';
|
|
22
22
|
/** Resource kinds the inventory can report. Only 'hooks' is implemented so far
|
|
23
23
|
* (RUSH-2238); the remaining kinds land with their follow-up tickets. */
|
|
24
24
|
export type InventoryKind = 'hooks' | 'skills' | 'commands' | 'plugins' | 'mcp';
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
import { AGENTS } from './agents.js';
|
|
21
21
|
import { supports } from './capabilities.js';
|
|
22
22
|
import { listResources } from './resources.js';
|
|
23
|
-
import { checkVersionHookWiring, listHooksInVersionHome, listInstalledHooksWithScope, } from './hooks.js';
|
|
23
|
+
import { checkVersionHookWiring, listHooksInVersionHome, listInstalledHooksWithScope, } from './hooks/install.js';
|
|
24
24
|
const IMPLEMENTED_KINDS = ['hooks'];
|
|
25
25
|
/**
|
|
26
26
|
* The single inventory API. Harness-scoped (claude/codex/grok/kimi/droid) —
|
package/dist/lib/rules/rules.js
CHANGED
|
@@ -10,7 +10,7 @@ import * as fs from 'fs';
|
|
|
10
10
|
import * as path from 'path';
|
|
11
11
|
import { AGENTS, ALL_AGENT_IDS, agentConfigDirName } from '../agents.js';
|
|
12
12
|
import { getResolvedRulesDir, getUserRulesDir, getProjectAgentsDir } from '../state.js';
|
|
13
|
-
import { getEffectiveHome } from '../installations/
|
|
13
|
+
import { getEffectiveHome } from '../installations/store.js';
|
|
14
14
|
/**
|
|
15
15
|
* Central rules filename constant.
|
|
16
16
|
* All agents map to this file in ~/.agents/rules/, renamed per-agent when synced.
|
|
@@ -25,7 +25,7 @@ import { enabledRoutineNames, devicesWithRoutineEnabled, replaceEnabledRoutines,
|
|
|
25
25
|
import { humanizeCron, humanizeNextRun } from '../routines-format.js';
|
|
26
26
|
import { discoverProjectRoutines } from '../routines-project.js';
|
|
27
27
|
import { listProjectDefs } from '../projects.js';
|
|
28
|
-
import { monitorRunningJobs } from '../runner.js';
|
|
28
|
+
import { monitorRunningJobs } from '../daemon/runner.js';
|
|
29
29
|
import { JobScheduler } from '../scheduler.js';
|
|
30
30
|
import { detectOverdueJobs } from '../overdue.js';
|
|
31
31
|
export function fireConditionLabel(job) {
|
|
Binary file
|
|
Binary file
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// or broken — silent breakage. Repair runs at most once per unique path per
|
|
5
5
|
// pass (no retry, no sync recursion); unresolved findings stay needsAttention.
|
|
6
6
|
import { resultOf } from '../types.js';
|
|
7
|
-
import { repairManagedHookRuntimeArtifacts } from '../../hooks.js';
|
|
7
|
+
import { repairManagedHookRuntimeArtifacts } from '../../hooks/install.js';
|
|
8
8
|
export const hookRuntimeCheck = {
|
|
9
9
|
id: 'hook-runtime',
|
|
10
10
|
title: 'Generated hook runtime shims',
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// Caveat: an already-open shell won't pick up the new rc/PATH entry until it reloads
|
|
7
7
|
// — but new shells will, so the recurring prompt stops.
|
|
8
8
|
import { resultOf } from '../types.js';
|
|
9
|
-
import { isShimsInPath, addShimsToPath, listAgentsWithNonIsolatedInstalledVersions } from '../../shims.js';
|
|
9
|
+
import { isShimsInPath, addShimsToPath, listAgentsWithNonIsolatedInstalledVersions } from '../../installations/shims.js';
|
|
10
10
|
export const pathCheck = {
|
|
11
11
|
id: 'path',
|
|
12
12
|
title: 'Shims directory on PATH',
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// and PATH-order problem are POSIX; Windows resolves via the registry PATH).
|
|
6
6
|
import { resultOf } from '../types.js';
|
|
7
7
|
import { AGENTS } from '../../agents.js';
|
|
8
|
-
import { getPathShadowingExecutable, adoptShadowingLauncher, listAgentsWithNonIsolatedInstalledVersions, } from '../../shims.js';
|
|
8
|
+
import { getPathShadowingExecutable, adoptShadowingLauncher, listAgentsWithNonIsolatedInstalledVersions, } from '../../installations/shims.js';
|
|
9
9
|
import { getGlobalDefault } from '../../installations/versions.js';
|
|
10
10
|
export const shadowingCheck = {
|
|
11
11
|
id: 'shadowing',
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// background so the shim schema settles without user-facing churn.
|
|
5
5
|
import { resultOf } from '../types.js';
|
|
6
6
|
import { AGENTS } from '../../agents.js';
|
|
7
|
-
import { createShim, ensureShimCurrent, ensureVersionedAliasCurrent, isShimCurrent, isVersionedAliasCurrent, removeShim, shimExists, shimPointsAtLiveInstall, removeLegacyUserShim, listAgentsWithInstalledVersions, listAgentsWithNonIsolatedInstalledVersions, listShimFileNames, pruneOrphanedCommandShim, } from '../../shims.js';
|
|
7
|
+
import { createShim, ensureShimCurrent, ensureVersionedAliasCurrent, isShimCurrent, isVersionedAliasCurrent, removeShim, shimExists, shimPointsAtLiveInstall, removeLegacyUserShim, listAgentsWithInstalledVersions, listAgentsWithNonIsolatedInstalledVersions, listShimFileNames, pruneOrphanedCommandShim, } from '../../installations/shims.js';
|
|
8
8
|
import { listInstalledVersions } from '../../installations/versions.js';
|
|
9
9
|
export const shimsCheck = {
|
|
10
10
|
id: 'shims',
|
|
@@ -644,6 +644,29 @@ export declare function scanClaudeSessionIncremental(filePath: string, fromOffse
|
|
|
644
644
|
newOffset: number;
|
|
645
645
|
toolCalls: IndexedToolCall[];
|
|
646
646
|
}>;
|
|
647
|
+
/**
|
|
648
|
+
* Decide full-vs-incremental for one Claude file and parse it uniformly, always
|
|
649
|
+
* returning a finalized scan plus the continuation to persist. Both branches run
|
|
650
|
+
* through the SAME reducer (via {@link scanClaudeSessionIncremental}), so the row
|
|
651
|
+
* an append produces is identical to a from-scratch full reparse by construction
|
|
652
|
+
* (the B-1 parity harness proves this at the function level).
|
|
653
|
+
*
|
|
654
|
+
* INCREMENTAL when a prior continuation exists AND the file grew past the
|
|
655
|
+
* persisted offset AND its mtime did not go backwards — an in-place append.
|
|
656
|
+
* FULL (from byte 0, fresh state) otherwise: cold start (no prior), truncation /
|
|
657
|
+
* rewrite (size shrank to at or below the offset), or a clock rewind / restore
|
|
658
|
+
* (mtime older than the last parse). A FULL parse still produces a continuation,
|
|
659
|
+
* so the file's very next append can go incremental.
|
|
660
|
+
*
|
|
661
|
+
* `mode` is returned so the caller (and tests) can confirm which branch ran.
|
|
662
|
+
*/
|
|
663
|
+
export declare function scanClaudeSessionResumable(filePath: string, prior: ClaudeParserState | null, currentFileMtimeMs: number, currentFileSize: number, priorFileMtimeMs?: number): Promise<{
|
|
664
|
+
scan: ClaudeSessionScan;
|
|
665
|
+
newState: ClaudeParserState;
|
|
666
|
+
newOffset: number;
|
|
667
|
+
toolCalls: IndexedToolCall[];
|
|
668
|
+
mode: 'full' | 'incremental';
|
|
669
|
+
}>;
|
|
647
670
|
/** Test seam: read the (incremental, full) Claude parse counters. */
|
|
648
671
|
export declare function __claudeScanBranchCountsForTest(): {
|
|
649
672
|
incremental: number;
|
|
@@ -776,6 +799,22 @@ export declare function scanCodexSessionIncremental(filePath: string, fromOffset
|
|
|
776
799
|
newOffset: number;
|
|
777
800
|
toolCalls: IndexedToolCall[];
|
|
778
801
|
}>;
|
|
802
|
+
/**
|
|
803
|
+
* Decide full-vs-incremental for one Codex rollout and parse it uniformly,
|
|
804
|
+
* always returning a finalized scan plus the continuation to persist. Both
|
|
805
|
+
* branches run through the SAME reducer (via {@link scanCodexSessionIncremental}),
|
|
806
|
+
* so an append produces a row identical to a from-scratch full reparse by
|
|
807
|
+
* construction. INCREMENTAL when a prior continuation exists AND the file grew
|
|
808
|
+
* past the persisted offset AND its mtime did not go backwards; FULL (from byte
|
|
809
|
+
* 0, fresh state) otherwise (cold start, truncation/rewrite, clock rewind).
|
|
810
|
+
*/
|
|
811
|
+
export declare function scanCodexSessionResumable(filePath: string, prior: CodexParserState | null, currentFileMtimeMs: number, currentFileSize: number, priorFileMtimeMs?: number): Promise<{
|
|
812
|
+
scan: CodexSessionScan;
|
|
813
|
+
newState: CodexParserState;
|
|
814
|
+
newOffset: number;
|
|
815
|
+
toolCalls: IndexedToolCall[];
|
|
816
|
+
mode: 'full' | 'incremental';
|
|
817
|
+
}>;
|
|
779
818
|
/** Test seam: read the (incremental, full) Codex parse counters. */
|
|
780
819
|
export declare function __codexScanBranchCountsForTest(): {
|
|
781
820
|
incremental: number;
|
|
@@ -22,7 +22,7 @@ import { AGENTS, agentConfigDirName, getCliVersion, resolveOpenCodeAccountId } f
|
|
|
22
22
|
import { walkForFilesWithStat } from '../fs-walk.js';
|
|
23
23
|
import { hasCommand } from '../cli-resources.js';
|
|
24
24
|
import { execFileShellSpec } from '../platform/exec.js';
|
|
25
|
-
import { getConfigSymlinkVersion } from '../shims.js';
|
|
25
|
+
import { getConfigSymlinkVersion } from '../installations/shims.js';
|
|
26
26
|
import { SESSION_AGENTS } from './types.js';
|
|
27
27
|
import { deriveShortId } from './short-id.js';
|
|
28
28
|
import { buildClaudeAccountIndex, resolveClaudeAccount } from './claude-accounts.js';
|
|
@@ -3491,7 +3491,7 @@ function freshClaudeParserState() {
|
|
|
3491
3491
|
*
|
|
3492
3492
|
* `mode` is returned so the caller (and tests) can confirm which branch ran.
|
|
3493
3493
|
*/
|
|
3494
|
-
async function scanClaudeSessionResumable(filePath, prior, currentFileMtimeMs, currentFileSize, priorFileMtimeMs) {
|
|
3494
|
+
export async function scanClaudeSessionResumable(filePath, prior, currentFileMtimeMs, currentFileSize, priorFileMtimeMs) {
|
|
3495
3495
|
// File size + mtime cannot distinguish an APPEND from an in-place rewrite or a
|
|
3496
3496
|
// restore that dropped DIFFERENT, larger content at the same path: both grow
|
|
3497
3497
|
// the file and move mtime forward. Resuming from the stored offset across that
|
|
@@ -3977,7 +3977,7 @@ async function codexSessionIdentityAt(filePath, maxBytes = 1_048_576) {
|
|
|
3977
3977
|
* past the persisted offset AND its mtime did not go backwards; FULL (from byte
|
|
3978
3978
|
* 0, fresh state) otherwise (cold start, truncation/rewrite, clock rewind).
|
|
3979
3979
|
*/
|
|
3980
|
-
async function scanCodexSessionResumable(filePath, prior, currentFileMtimeMs, currentFileSize, priorFileMtimeMs) {
|
|
3980
|
+
export async function scanCodexSessionResumable(filePath, prior, currentFileMtimeMs, currentFileSize, priorFileMtimeMs) {
|
|
3981
3981
|
// File size + mtime cannot distinguish an APPEND from an in-place rewrite or a
|
|
3982
3982
|
// restore that dropped a DIFFERENT, larger rollout at the same path: both grow
|
|
3983
3983
|
// the file and move mtime forward. Resuming from the stored offset across that
|
|
@@ -117,53 +117,35 @@ function truncateNormalizedToolOutput(output, maxChars) {
|
|
|
117
117
|
return output;
|
|
118
118
|
return `${output.slice(0, maxChars)}\n\n[Output truncated: ${output.length - maxChars} characters omitted.]`;
|
|
119
119
|
}
|
|
120
|
+
/**
|
|
121
|
+
* Registry-dispatch table: each `SessionAgentId` to its offline transcript
|
|
122
|
+
* parser. Replaces the per-harness `switch` — the harness axis of Move 3. Kept
|
|
123
|
+
* as its own table (not on the HarnessAdapter registry) because its id domain is
|
|
124
|
+
* `SessionAgentId`: `rush` is a session agent with no `AgentId`, and the offline
|
|
125
|
+
* transcript reader is a deliberately separate concern from live team events.
|
|
126
|
+
* The `Record` is total, so a new session harness must add an entry here.
|
|
127
|
+
*/
|
|
128
|
+
const TRANSCRIPT_PARSERS = {
|
|
129
|
+
claude: (filePath, opts) => parseClaude(filePath, opts),
|
|
130
|
+
codex: (filePath) => parseCodex(filePath),
|
|
131
|
+
gemini: (filePath) => parseGemini(filePath),
|
|
132
|
+
antigravity: (filePath) => parseAntigravity(filePath),
|
|
133
|
+
opencode: (filePath) => parseOpenCode(filePath),
|
|
134
|
+
grok: (filePath) => parseGrok(filePath),
|
|
135
|
+
rush: (filePath) => parseRush(filePath),
|
|
136
|
+
openclaw: () => [], // OpenClaw sessions don't have parseable files yet
|
|
137
|
+
hermes: (filePath) => parseHermes(filePath),
|
|
138
|
+
kimi: (filePath) => parseKimi(filePath),
|
|
139
|
+
droid: (filePath) => parseDroid(filePath),
|
|
140
|
+
cursor: (filePath) => parseCursor(filePath),
|
|
141
|
+
muse: (filePath) => parseMuse(filePath),
|
|
142
|
+
};
|
|
120
143
|
export function parseSession(filePath, agent, opts = {}) {
|
|
121
144
|
const detected = agent || detectAgent(filePath);
|
|
122
145
|
if (!detected) {
|
|
123
146
|
throw new Error(`Cannot detect agent type from path: ${filePath}`);
|
|
124
147
|
}
|
|
125
|
-
|
|
126
|
-
switch (detected) {
|
|
127
|
-
case 'claude':
|
|
128
|
-
events = parseClaude(filePath, opts);
|
|
129
|
-
break;
|
|
130
|
-
case 'codex':
|
|
131
|
-
events = parseCodex(filePath);
|
|
132
|
-
break;
|
|
133
|
-
case 'gemini':
|
|
134
|
-
events = parseGemini(filePath);
|
|
135
|
-
break;
|
|
136
|
-
case 'antigravity':
|
|
137
|
-
events = parseAntigravity(filePath);
|
|
138
|
-
break;
|
|
139
|
-
case 'opencode':
|
|
140
|
-
events = parseOpenCode(filePath);
|
|
141
|
-
break;
|
|
142
|
-
case 'grok':
|
|
143
|
-
events = parseGrok(filePath);
|
|
144
|
-
break;
|
|
145
|
-
case 'rush':
|
|
146
|
-
events = parseRush(filePath);
|
|
147
|
-
break;
|
|
148
|
-
case 'openclaw':
|
|
149
|
-
events = [];
|
|
150
|
-
break; // OpenClaw sessions don't have parseable files yet
|
|
151
|
-
case 'hermes':
|
|
152
|
-
events = parseHermes(filePath);
|
|
153
|
-
break;
|
|
154
|
-
case 'kimi':
|
|
155
|
-
events = parseKimi(filePath);
|
|
156
|
-
break;
|
|
157
|
-
case 'droid':
|
|
158
|
-
events = parseDroid(filePath);
|
|
159
|
-
break;
|
|
160
|
-
case 'cursor':
|
|
161
|
-
events = parseCursor(filePath);
|
|
162
|
-
break;
|
|
163
|
-
case 'muse':
|
|
164
|
-
events = parseMuse(filePath);
|
|
165
|
-
break;
|
|
166
|
-
}
|
|
148
|
+
const events = TRANSCRIPT_PARSERS[detected](filePath, opts);
|
|
167
149
|
// Chokepoint: every string field that originated in an untrusted session
|
|
168
150
|
// file gets stripped of terminal escapes here, so renderers downstream can
|
|
169
151
|
// safely splat values into chalk/console output. Same pass flags
|