@phnx-labs/agents-cli 1.22.69 → 1.22.71
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 +34 -0
- package/README.md +34 -9
- package/dist/bootstrap.js +4 -4
- package/dist/commands/accounts.js +23 -7
- package/dist/commands/exec.js +2 -2
- package/dist/commands/import.js +2 -2
- package/dist/commands/models.js +2 -2
- package/dist/commands/permissions.js +2 -2
- package/dist/commands/repo.js +2 -2
- package/dist/commands/rules.js +1 -1
- package/dist/commands/send.js +9 -3
- package/dist/commands/sessions-export.d.ts +5 -1
- package/dist/commands/sessions-export.js +100 -24
- package/dist/commands/sessions-import.d.ts +2 -1
- package/dist/commands/sessions-import.js +85 -21
- package/dist/commands/traces.js +1 -1
- package/dist/lib/account-capabilities.js +0 -2
- package/dist/lib/account-registry.d.ts +3 -3
- package/dist/lib/account-registry.js +25 -7
- package/dist/lib/accounting/usage-sync.d.ts +1 -1
- package/dist/lib/accounting/usage-sync.js +3 -3
- package/dist/lib/acp/client.d.ts +1 -1
- package/dist/lib/acp/client.js +12 -1
- package/dist/lib/acp/harnesses.js +1 -1
- package/dist/lib/add-dir.js +0 -2
- package/dist/lib/agent-cli-commands.js +0 -2
- package/dist/lib/agent-spec/agents.d.ts +1 -1
- package/dist/lib/agent-spec/agents.js +2 -83
- package/dist/lib/browser/ipc.d.ts +34 -0
- package/dist/lib/browser/ipc.js +149 -20
- package/dist/lib/browser/remote-control.d.ts +6 -3
- package/dist/lib/browser/remote-control.js +6 -3
- package/dist/lib/browser/service.d.ts +4 -1
- package/dist/lib/browser/service.js +7 -1
- package/dist/lib/browser/types.d.ts +3 -1
- package/dist/lib/channels/registry.d.ts +6 -0
- package/dist/lib/channels/send.d.ts +11 -2
- package/dist/lib/channels/send.js +11 -2
- package/dist/lib/cloud/rush.d.ts +10 -2
- package/dist/lib/cloud/rush.js +15 -9
- package/dist/lib/daemon/auth-sync-service.js +1 -1
- package/dist/lib/daemon/browser-task-reap-service.js +1 -1
- package/dist/lib/daemon/daemon.js +41 -11
- package/dist/lib/daemon/heartbeat-service.js +3 -3
- package/dist/lib/daemon/keychain-reap-service.js +1 -1
- package/dist/lib/daemon/runner.d.ts +18 -1
- package/dist/lib/daemon/runner.js +237 -80
- package/dist/lib/daemon/self-heal-service.js +13 -3
- package/dist/lib/daemon/self-update-service.d.ts +174 -0
- package/dist/lib/daemon/self-update-service.js +353 -0
- package/dist/lib/daemon/state-dir-check-service.js +3 -3
- package/dist/lib/daemon/usage-sync-service.js +1 -1
- package/dist/lib/daemon/watchdog-service.js +4 -4
- package/dist/lib/daemon-services.d.ts +1 -1
- package/dist/lib/daemon-services.js +5 -0
- package/dist/lib/device-config.d.ts +12 -1
- package/dist/lib/device-config.js +63 -13
- package/dist/lib/exec-bounded.d.ts +52 -0
- package/dist/lib/exec-bounded.js +113 -0
- package/dist/lib/exec.d.ts +2 -2
- package/dist/lib/exec.js +3 -34
- package/dist/lib/feed/events.d.ts +22 -14
- package/dist/lib/feed/events.js +84 -44
- package/dist/lib/feed-broadcast.d.ts +12 -29
- package/dist/lib/feed-broadcast.js +28 -27
- package/dist/lib/fleet-shared-state.d.ts +12 -5
- package/dist/lib/fleet-shared-state.js +50 -20
- package/dist/lib/fs-atomic.d.ts +11 -0
- package/dist/lib/fs-atomic.js +60 -0
- package/dist/lib/hooks/install.js +0 -87
- package/dist/lib/hosts/reconcile.d.ts +11 -4
- package/dist/lib/hosts/reconcile.js +31 -5
- package/dist/lib/installations/strategies.js +1 -1
- package/dist/lib/mcp-registry.js +0 -13
- package/dist/lib/mcp.js +2 -2
- package/dist/lib/model-tiers.js +1 -1
- package/dist/lib/models.js +0 -63
- package/dist/lib/notify.d.ts +11 -0
- package/dist/lib/notify.js +17 -4
- package/dist/lib/owner-message.d.ts +46 -3
- package/dist/lib/owner-message.js +26 -6
- package/dist/lib/permissions-registry.d.ts +0 -2
- package/dist/lib/permissions-registry.js +3 -50
- package/dist/lib/permissions.d.ts +3 -17
- package/dist/lib/permissions.js +4 -73
- package/dist/lib/project-resources.d.ts +12 -0
- package/dist/lib/project-resources.js +129 -0
- package/dist/lib/routine-process-cleanup.d.ts +2 -2
- package/dist/lib/routine-process-cleanup.js +45 -34
- package/dist/lib/rush-session.d.ts +19 -0
- package/dist/lib/rush-session.js +24 -0
- package/dist/lib/secrets/drivers/rush.js +2 -1
- package/dist/lib/secrets/reaper.d.ts +2 -2
- package/dist/lib/secrets/reaper.js +13 -10
- package/dist/lib/secrets/reserved-sync.d.ts +1 -1
- package/dist/lib/secrets/reserved-sync.js +4 -4
- package/dist/lib/self-update.d.ts +21 -8
- package/dist/lib/self-update.js +54 -31
- package/dist/lib/session/cloud.js +2 -1
- package/dist/lib/session/sync/backend.d.ts +61 -0
- package/dist/lib/session/sync/backend.js +89 -0
- package/dist/lib/session/sync/managed-config.d.ts +29 -0
- package/dist/lib/session/sync/managed-config.js +23 -0
- package/dist/lib/session/sync/managed-key.d.ts +45 -0
- package/dist/lib/session/sync/managed-key.js +128 -0
- package/dist/lib/session/sync/net-client.d.ts +65 -0
- package/dist/lib/session/sync/net-client.js +117 -0
- package/dist/lib/session/sync/provision.d.ts +19 -0
- package/dist/lib/session/sync/provision.js +38 -0
- package/dist/lib/session/sync/r2.d.ts +5 -2
- package/dist/lib/session/sync/r2.js +5 -2
- package/dist/lib/session/sync/worker-template.d.ts +6 -0
- package/dist/lib/session/sync/worker-template.js +847 -0
- package/dist/lib/sink-format.d.ts +34 -0
- package/dist/lib/sink-format.js +17 -0
- package/dist/lib/staleness/detectors/permissions.js +0 -20
- package/dist/lib/staleness/writers/commands.js +1 -1
- package/dist/lib/staleness/writers/hooks.js +2 -2
- package/dist/lib/subagents-registry.js +2 -12
- package/dist/lib/subagents.d.ts +0 -10
- package/dist/lib/subagents.js +0 -37
- package/dist/lib/tmux/orphan-reap.js +6 -4
- package/dist/lib/tmux/session.js +4 -1
- package/dist/lib/traces/classify.d.ts +8 -1
- package/dist/lib/traces/insights.d.ts +13 -1
- package/dist/lib/traces/insights.js +78 -3
- package/dist/lib/traces/sync.js +8 -3
- package/dist/lib/traces/worker-template.js +9 -5
- package/dist/lib/types.d.ts +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SessionsHttpClient — the managed transport for `agents sessions export
|
|
3
|
+
* --to-r2` / `import --from-r2` when the caller is signed in to Phoenix.
|
|
4
|
+
*
|
|
5
|
+
* Same put / get / list / delete surface as `R2Client` (`./r2.ts`), so the
|
|
6
|
+
* export/import command drives EITHER a managed HTTP client or a BYO S3 client
|
|
7
|
+
* through the one shared {@link SessionsBackupClient} interface. The difference
|
|
8
|
+
* is only the wire: this talks to the managed sessions Worker over `fetch` with
|
|
9
|
+
* a `Authorization: Bearer <phoenix-token>` header (the same request shape as
|
|
10
|
+
* `traces/sync.ts`), instead of SigV4 against R2 directly.
|
|
11
|
+
*
|
|
12
|
+
* The userId namespace prefix is prepended INSIDE this client
|
|
13
|
+
* (`${baseUrl}/${userId}/${key}`), so the caller passes the SAME object key it
|
|
14
|
+
* passes to `R2Client` (`sessions/<machine>/<agent>/<sessionId>.jsonl`). That
|
|
15
|
+
* keeps the BYO object layout (SES-27a) byte-identical while the managed Worker
|
|
16
|
+
* gets its required `segments[0] === userId` prefix.
|
|
17
|
+
*/
|
|
18
|
+
export class SessionsHttpClient {
|
|
19
|
+
kind = 'managed';
|
|
20
|
+
base;
|
|
21
|
+
userId;
|
|
22
|
+
token;
|
|
23
|
+
constructor(opts) {
|
|
24
|
+
this.base = opts.baseUrl.replace(/\/+$/, '');
|
|
25
|
+
this.userId = safeOwnerSegment(opts.userId);
|
|
26
|
+
this.token = opts.token;
|
|
27
|
+
}
|
|
28
|
+
/** `${baseUrl}/<userId>/<key>` with every path segment percent-encoded. */
|
|
29
|
+
objUrl(key) {
|
|
30
|
+
const rel = safeObjectKey(key).map(encodeURIComponent).join('/');
|
|
31
|
+
return `${this.base}/${encodeURIComponent(this.userId)}/${rel}`;
|
|
32
|
+
}
|
|
33
|
+
authHeaders(extra = {}) {
|
|
34
|
+
return { authorization: `Bearer ${this.token}`, ...extra };
|
|
35
|
+
}
|
|
36
|
+
/** Upload an object under this owner's namespace. Overwrites unconditionally. */
|
|
37
|
+
async put(key, body, contentType = 'application/octet-stream') {
|
|
38
|
+
const res = await fetch(this.objUrl(key), {
|
|
39
|
+
method: 'PUT',
|
|
40
|
+
headers: this.authHeaders({ 'content-type': contentType }),
|
|
41
|
+
body,
|
|
42
|
+
});
|
|
43
|
+
if (!res.ok)
|
|
44
|
+
throw new Error(`sessions PUT ${key} failed: ${res.status} ${await safeText(res)}`);
|
|
45
|
+
}
|
|
46
|
+
/** Create an object exactly once. A concurrent winner returns false. */
|
|
47
|
+
async putIfAbsent(key, body, contentType = 'application/octet-stream') {
|
|
48
|
+
const res = await fetch(this.objUrl(key), {
|
|
49
|
+
method: 'PUT',
|
|
50
|
+
headers: this.authHeaders({ 'content-type': contentType, 'if-none-match': '*' }),
|
|
51
|
+
body,
|
|
52
|
+
});
|
|
53
|
+
if (res.status === 409)
|
|
54
|
+
return false;
|
|
55
|
+
if (!res.ok) {
|
|
56
|
+
throw new Error(`sessions PUT-IF-ABSENT ${key} failed: ${res.status} ${await safeText(res)}`);
|
|
57
|
+
}
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
/** Fetch an object as text, or null if it does not exist (404). */
|
|
61
|
+
async get(key) {
|
|
62
|
+
const res = await fetch(this.objUrl(key), { method: 'GET', headers: this.authHeaders() });
|
|
63
|
+
if (res.status === 404)
|
|
64
|
+
return null;
|
|
65
|
+
if (!res.ok)
|
|
66
|
+
throw new Error(`sessions GET ${key} failed: ${res.status} ${await safeText(res)}`);
|
|
67
|
+
return await res.text();
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* List this owner's object keys, each relative to the `<userId>/` prefix (so
|
|
71
|
+
* they feed straight back into `get`/`delete`). The `prefix` arg is accepted
|
|
72
|
+
* for interface parity with `R2Client` but ignored — the managed Worker
|
|
73
|
+
* enumerates the whole owner namespace and excludes the reserved `__` keys
|
|
74
|
+
* server-side.
|
|
75
|
+
*/
|
|
76
|
+
async list(_prefix) {
|
|
77
|
+
const url = `${this.base}/${encodeURIComponent(this.userId)}/?list`;
|
|
78
|
+
const res = await fetch(url, { method: 'GET', headers: this.authHeaders() });
|
|
79
|
+
if (!res.ok)
|
|
80
|
+
throw new Error(`sessions LIST failed: ${res.status} ${await safeText(res)}`);
|
|
81
|
+
const body = (await res.json());
|
|
82
|
+
if (!body || !Array.isArray(body.keys))
|
|
83
|
+
return [];
|
|
84
|
+
return body.keys.filter((k) => typeof k === 'string');
|
|
85
|
+
}
|
|
86
|
+
/** Delete an object (the Worker refunds its bytes to the quota ledger). */
|
|
87
|
+
async delete(key) {
|
|
88
|
+
const res = await fetch(this.objUrl(key), { method: 'DELETE', headers: this.authHeaders() });
|
|
89
|
+
if (!res.ok && res.status !== 404) {
|
|
90
|
+
throw new Error(`sessions DELETE ${key} failed: ${res.status} ${await safeText(res)}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function safeOwnerSegment(owner) {
|
|
95
|
+
const value = owner.trim();
|
|
96
|
+
if (!value || value === '.' || value === '..' || value.includes('/')) {
|
|
97
|
+
throw new Error(`Invalid managed sessions user id: ${JSON.stringify(owner)}`);
|
|
98
|
+
}
|
|
99
|
+
return value;
|
|
100
|
+
}
|
|
101
|
+
/** Prevent URL dot-segment normalization from escaping the verified owner path. */
|
|
102
|
+
function safeObjectKey(key) {
|
|
103
|
+
const segments = key.split('/');
|
|
104
|
+
if (segments.length === 0 ||
|
|
105
|
+
segments.some(segment => !segment || segment === '.' || segment === '..')) {
|
|
106
|
+
throw new Error(`Invalid managed sessions object key: ${JSON.stringify(key)}`);
|
|
107
|
+
}
|
|
108
|
+
return segments;
|
|
109
|
+
}
|
|
110
|
+
async function safeText(res) {
|
|
111
|
+
try {
|
|
112
|
+
return (await res.text()).slice(0, 200);
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
return '';
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type CloudflareRequester } from '../../share/provision.js';
|
|
2
|
+
interface ProvisionOptions {
|
|
3
|
+
request?: CloudflareRequester;
|
|
4
|
+
}
|
|
5
|
+
/** Bind the Phoenix identity base URL used to verify every session read and write. */
|
|
6
|
+
export declare function setPhoenixIdBaseSecret(apiToken: string, accountId: string, workerName: string, phoenixIdBase: string, opts?: ProvisionOptions): Promise<void>;
|
|
7
|
+
export interface ProvisionSessionsOptions extends ProvisionOptions {
|
|
8
|
+
apiToken: string;
|
|
9
|
+
accountId: string;
|
|
10
|
+
workerName?: string;
|
|
11
|
+
bucketName?: string;
|
|
12
|
+
phoenixIdBase: string;
|
|
13
|
+
domain?: string;
|
|
14
|
+
}
|
|
15
|
+
/** Provision the complete isolated sessions deployment using the canonical Worker template. */
|
|
16
|
+
export declare function provisionSessions(opts: ProvisionSessionsOptions): Promise<{
|
|
17
|
+
baseUrl: string;
|
|
18
|
+
}>;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Cloudflare orchestration for the managed session-backup store. The generic
|
|
2
|
+
// request primitives live in share/provision; only the isolated sessions
|
|
3
|
+
// resource choices and Worker template belong here. Mirrors lib/traces/provision.ts.
|
|
4
|
+
//
|
|
5
|
+
// This is the OPERATOR provisioning path — cutting the `agents-sessions` Worker +
|
|
6
|
+
// bucket + custom domain once. Product traffic never runs it; a signed-in user
|
|
7
|
+
// talks to the already-deployed Worker through `SessionsHttpClient`.
|
|
8
|
+
import { addCustomDomain, createBucket, deployWorker, enableWorkersDev, findZoneId, putWorkerSecret, } from '../../share/provision.js';
|
|
9
|
+
import { DEFAULT_SESSIONS_BUCKET_NAME, DEFAULT_SESSIONS_DOMAIN, DEFAULT_SESSIONS_WORKER_NAME, } from './managed-config.js';
|
|
10
|
+
import { renderSessionsWorkerScript } from './worker-template.js';
|
|
11
|
+
/** Bind the Phoenix identity base URL used to verify every session read and write. */
|
|
12
|
+
export async function setPhoenixIdBaseSecret(apiToken, accountId, workerName, phoenixIdBase, opts = {}) {
|
|
13
|
+
const normalized = phoenixIdBase.replace(/\/+$/, '').trim();
|
|
14
|
+
if (!normalized)
|
|
15
|
+
throw new Error('PHOENIX_ID_BASE is required to verify session requests.');
|
|
16
|
+
await putWorkerSecret(apiToken, accountId, workerName, 'PHOENIX_ID_BASE', normalized, opts);
|
|
17
|
+
}
|
|
18
|
+
/** Provision the complete isolated sessions deployment using the canonical Worker template. */
|
|
19
|
+
export async function provisionSessions(opts) {
|
|
20
|
+
const domain = opts.domain ?? DEFAULT_SESSIONS_DOMAIN;
|
|
21
|
+
const workerName = opts.workerName ?? DEFAULT_SESSIONS_WORKER_NAME;
|
|
22
|
+
const bucketName = opts.bucketName ?? DEFAULT_SESSIONS_BUCKET_NAME;
|
|
23
|
+
const requestOpts = opts.request ? { request: opts.request } : {};
|
|
24
|
+
await createBucket(opts.apiToken, opts.accountId, bucketName, requestOpts);
|
|
25
|
+
await deployWorker(opts.apiToken, opts.accountId, workerName, renderSessionsWorkerScript(), bucketName, requestOpts);
|
|
26
|
+
// No WRITE_TOKEN secret: the managed sessions Worker is Phoenix-only (PHNX-3726).
|
|
27
|
+
// A static token would be a Phoenix-and-quota bypass with no legitimate caller —
|
|
28
|
+
// the zero-knowledge BYO path uses the user's own R2 bucket directly, never this
|
|
29
|
+
// Worker.
|
|
30
|
+
await setPhoenixIdBaseSecret(opts.apiToken, opts.accountId, workerName, opts.phoenixIdBase, requestOpts);
|
|
31
|
+
const workersDevSubdomain = await enableWorkersDev(opts.apiToken, opts.accountId, workerName, requestOpts);
|
|
32
|
+
const zoneId = await findZoneId(opts.apiToken, domain, requestOpts);
|
|
33
|
+
if (!zoneId) {
|
|
34
|
+
return { baseUrl: `https://${workerName}.${workersDevSubdomain}.workers.dev` };
|
|
35
|
+
}
|
|
36
|
+
await addCustomDomain(opts.apiToken, opts.accountId, workerName, zoneId, domain, requestOpts);
|
|
37
|
+
return { baseUrl: `https://${domain}` };
|
|
38
|
+
}
|
|
@@ -15,6 +15,7 @@ export interface HeadResult {
|
|
|
15
15
|
etag: string;
|
|
16
16
|
}
|
|
17
17
|
export declare class R2Client {
|
|
18
|
+
readonly kind: "byo";
|
|
18
19
|
private aws;
|
|
19
20
|
private base;
|
|
20
21
|
constructor(cfg: R2Config);
|
|
@@ -29,6 +30,8 @@ export declare class R2Client {
|
|
|
29
30
|
delete(key: string): Promise<void>;
|
|
30
31
|
/** List immediate sub-prefixes under a prefix (delimiter '/'), e.g. machine dirs. */
|
|
31
32
|
listPrefixes(prefix: string): Promise<string[]>;
|
|
32
|
-
/** List all object keys under a prefix (handles pagination).
|
|
33
|
-
|
|
33
|
+
/** List all object keys under a prefix (handles pagination). An empty prefix
|
|
34
|
+
* lists the whole bucket — the default so R2Client satisfies the shared
|
|
35
|
+
* {@link SessionsBackupClient} interface (managed lists with no prefix). */
|
|
36
|
+
list(prefix?: string): Promise<string[]>;
|
|
34
37
|
}
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { AwsClient } from 'aws4fetch';
|
|
13
13
|
export class R2Client {
|
|
14
|
+
kind = 'byo';
|
|
14
15
|
aws;
|
|
15
16
|
base;
|
|
16
17
|
constructor(cfg) {
|
|
@@ -84,8 +85,10 @@ export class R2Client {
|
|
|
84
85
|
} while (token);
|
|
85
86
|
return prefixes;
|
|
86
87
|
}
|
|
87
|
-
/** List all object keys under a prefix (handles pagination).
|
|
88
|
-
|
|
88
|
+
/** List all object keys under a prefix (handles pagination). An empty prefix
|
|
89
|
+
* lists the whole bucket — the default so R2Client satisfies the shared
|
|
90
|
+
* {@link SessionsBackupClient} interface (managed lists with no prefix). */
|
|
91
|
+
async list(prefix = '') {
|
|
89
92
|
const keys = [];
|
|
90
93
|
let token;
|
|
91
94
|
do {
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render the managed sessions Worker source. Pure — bindings are wired at
|
|
3
|
+
* deploy time. No `${}` interpolation in the emitted source (string
|
|
4
|
+
* concatenation only) so the outer template literal never captures it.
|
|
5
|
+
*/
|
|
6
|
+
export declare function renderSessionsWorkerScript(): string;
|