@ours.network/fleet 1.2.0-nightly.3 → 1.2.0-nightly.5
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/README.md +32 -19
- package/dist/agent-ours/bridge.d.ts +3 -0
- package/dist/agent-ours/bridge.js +169 -0
- package/dist/agent-ours/controller.d.ts +14 -0
- package/dist/agent-ours/controller.js +60 -0
- package/dist/agent-ours/file-rpc.d.ts +23 -0
- package/dist/agent-ours/file-rpc.js +123 -0
- package/dist/agent-ours/harness.d.ts +14 -0
- package/dist/agent-ours/harness.js +64 -0
- package/dist/agent-ours/mcp-endpoint.d.ts +15 -0
- package/dist/agent-ours/mcp-endpoint.js +111 -0
- package/dist/agent-ours/runtime.d.ts +53 -0
- package/dist/agent-ours/runtime.js +247 -0
- package/dist/agent-ours/service.d.ts +23 -0
- package/dist/agent-ours/service.js +352 -0
- package/dist/agent-ours/state.d.ts +45 -0
- package/dist/agent-ours/state.js +95 -0
- package/dist/agent-ours/wire.d.ts +14 -0
- package/dist/agent-ours/wire.js +55 -0
- package/dist/briefing.js +10 -59
- package/dist/build-info.json +5 -4
- package/dist/capabilities.d.ts +2 -1
- package/dist/capabilities.js +2 -0
- package/dist/cli.js +15 -2
- package/dist/client-profile.d.ts +1 -0
- package/dist/client-profile.js +18 -3
- package/dist/creation.d.ts +1 -1
- package/dist/creation.js +3 -14
- package/dist/docs.d.ts +1 -1
- package/dist/docs.js +15 -20
- package/dist/doctor.d.ts +1 -1
- package/dist/doctor.js +28 -9
- package/dist/fleet-command-audit.js +1 -1
- package/dist/harness/agent-session.d.ts +2 -0
- package/dist/harness/claude-code-session.js +4 -2
- package/dist/harness/codex-session.js +9 -2
- package/dist/harness/hermes-session.js +7 -1
- package/dist/rooms-tasks/cowork-adapter.js +9 -2
- package/dist/rooms-tasks/cowork-http.d.ts +3 -0
- package/dist/rooms-tasks/cowork-http.js +70 -0
- package/dist/rooms-tasks/provision.js +9 -2
- package/dist/runner.d.ts +4 -0
- package/dist/runner.js +522 -492
- package/dist/session/codex-app-server-transport.js +1 -1
- package/dist/spawn.js +12 -16
- package/dist/temp-supervisor-recovery.d.ts +3 -0
- package/dist/temp-supervisor-recovery.js +46 -0
- package/dist/watchdog/briefing.js +3 -16
- package/dist/watchdog/run.js +19 -5
- package/package.json +6 -5
package/dist/docs.js
CHANGED
|
@@ -253,20 +253,15 @@ records receive a bounded grace so a not-yet-registered transient unit cannot be
|
|
|
253
253
|
mistaken for a stopped one. Stale recorded supervisors are reclaimed in bounded
|
|
254
254
|
batches by moving their state to the same recovery archive, never by blind deletion.
|
|
255
255
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
The temporary supervisor treats its first positive identity observation as the
|
|
267
|
-
lifecycle readiness gate: a cold harness may take as long as needed to read its
|
|
268
|
-
briefing and bind, without a fixed first-bind retirement timer. After readiness,
|
|
269
|
-
only sustained authoritative absence closes the role. Unreachable, malformed, or
|
|
256
|
+
The Fleet supervisor creates and binds the assigned identity before the harness
|
|
257
|
+
starts. Temporary identities survive harness reconnects and are released only when
|
|
258
|
+
the logical run ends. Permanent identities remain available after the supervisor
|
|
259
|
+
releases ownership. Agents receive ready tools; no model-owned identity bootstrap
|
|
260
|
+
or onboarding skill is required.
|
|
261
|
+
|
|
262
|
+
The temporary supervisor provisions identity and verifies room readiness before
|
|
263
|
+
starting the harness. After readiness, only sustained authoritative absence
|
|
264
|
+
closes the role. Unreachable, malformed, or
|
|
270
265
|
valid-but-empty daemon indexes are ambiguous and reset closure debounce rather
|
|
271
266
|
than becoming cleanup authority.
|
|
272
267
|
|
|
@@ -454,10 +449,10 @@ tasks:
|
|
|
454
449
|
\`\`\`
|
|
455
450
|
|
|
456
451
|
Fleet launches each template member with a dedicated one-time Cowork invite.
|
|
457
|
-
The
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
452
|
+
The supervisor accepts the startup invite and verifies the provisioned identity CID
|
|
453
|
+
in the authenticated room seat before starting the model. The generated briefing
|
|
454
|
+
contains the assigned identity, room, role and task, without invite material.
|
|
455
|
+
A room is ready only after the Cowork room (and its task, when
|
|
461
456
|
task-bound) is durably active, every configured member seat is authenticated and active with its matching
|
|
462
457
|
live Fleet launch, and the configured Owner seat is active when owner attachment is
|
|
463
458
|
enabled. There is no briefing hash, startup ACK, or separate role-briefing readiness
|
|
@@ -480,8 +475,8 @@ Set \`room.anonymous: true\` on a room template, or pass \`--anonymous\` to
|
|
|
480
475
|
\`task create\`, \`task start\`, \`task work\`, or \`room create\`, to create an
|
|
481
476
|
anonymous Cowork room. \`--no-anonymous\` explicitly overrides an anonymous
|
|
482
477
|
template. Fleet records the resolved value before room creation so retries keep
|
|
483
|
-
the same choice.
|
|
484
|
-
|
|
478
|
+
the same choice. The supervisor creates anonymous room members with local contact-book exposure
|
|
479
|
+
disabled. Their generated briefings
|
|
485
480
|
do not disclose or compare an Owner participant CID. A participant-originated
|
|
486
481
|
instruction has Owner authority only when the authenticated Cowork room envelope
|
|
487
482
|
attributes that participant seat the exact \`Owner\` role. Literal text, display
|
package/dist/doctor.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { type Exec } from './exec.js';
|
|
|
3
3
|
import type { YamlMode } from './config-yaml.js';
|
|
4
4
|
import { type FetchLike } from './monitor.js';
|
|
5
5
|
import type { PrereqReport } from './harness/types.js';
|
|
6
|
-
type DoctorDaemonClient = Pick<OursClient, 'version'>;
|
|
6
|
+
type DoctorDaemonClient = Pick<OursClient, 'version' | 'close'>;
|
|
7
7
|
type AttachDoctorDaemon = (options: AttachOursClientOptions) => Promise<DoctorDaemonClient>;
|
|
8
8
|
/** Host-level + per-harness prerequisite report with actionable messages. */
|
|
9
9
|
export declare function doctor(opts?: {
|
package/dist/doctor.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { userInfo, homedir } from 'node:os';
|
|
2
3
|
import { existsSync, readFileSync } from 'node:fs';
|
|
3
4
|
import { attachOursClient, } from '@ours.network/sdk/client';
|
|
4
5
|
import { realExec } from './exec.js';
|
|
@@ -272,21 +273,39 @@ export async function doctor(opts = {}, exec = realExec, platform = process.plat
|
|
|
272
273
|
}
|
|
273
274
|
catch { /* state dir may not exist yet */ }
|
|
274
275
|
}
|
|
276
|
+
let managedSelection = false;
|
|
275
277
|
try {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
+
// Mark explicit selection before validation so a damaged managed profile does
|
|
279
|
+
// not produce advice to start an unrelated local daemon.
|
|
280
|
+
managedSelection = process.env.OURS_CONFIG !== undefined
|
|
281
|
+
|| existsSync(join(process.env.HOME || homedir(), '.ours-client', 'profile.json'));
|
|
282
|
+
const selected = readClientProfile(process.env);
|
|
283
|
+
managedSelection = selected !== undefined;
|
|
284
|
+
const client = await attachDaemon(selected ? {
|
|
285
|
+
endpoint: selected.endpoint, expectedInstanceId: selected.expectedInstanceId,
|
|
286
|
+
credentialPath: selected.credentialPath, env: {}, sessionMode: 'external',
|
|
278
287
|
leaseToken: `ours-fleet-doctor-${process.pid}`,
|
|
279
|
-
|
|
288
|
+
} : {
|
|
289
|
+
env: process.env, leaseToken: `ours-fleet-doctor-${process.pid}`, clientPid: process.pid,
|
|
280
290
|
});
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
291
|
+
let daemonVersion;
|
|
292
|
+
try {
|
|
293
|
+
const info = await client.version();
|
|
294
|
+
if (info.name !== 'ours' || typeof info.version !== 'string')
|
|
295
|
+
throw new Error('the selected endpoint did not return a valid ours daemon identity');
|
|
296
|
+
daemonVersion = info.version;
|
|
297
|
+
}
|
|
298
|
+
finally {
|
|
299
|
+
await client.close();
|
|
300
|
+
}
|
|
301
|
+
checks.push({ name: 'ours daemon', ok: true, detail: `running (${daemonVersion})` });
|
|
285
302
|
}
|
|
286
303
|
catch (error) {
|
|
287
304
|
checks.push({
|
|
288
305
|
name: 'ours daemon', ok: false,
|
|
289
|
-
detail:
|
|
306
|
+
detail: (managedSelection
|
|
307
|
+
? 'selected client profile failed — check its endpoint, instance and issued credential '
|
|
308
|
+
: 'not reachable through the SDK — start it with: ours-daemon start ')
|
|
290
309
|
+ `[${error?.message ?? String(error)}]`,
|
|
291
310
|
});
|
|
292
311
|
}
|
|
@@ -132,7 +132,7 @@ export const fleetProxyTopLevelInventory = Object.freeze({
|
|
|
132
132
|
'up', 'down', 'restart', 'force-restart', 'attach', 'send', 'loops', 'owner-channel',
|
|
133
133
|
'watchdog-run', 'watchdog-report', 'rm', 'init', 'web',
|
|
134
134
|
], denied: [...DENIED],
|
|
135
|
-
hidden: ['_run', '_run-temp', '_run-watchdog', '_run-watchdogs'], aliases: ['man'],
|
|
135
|
+
hidden: ['_run', '_run-temp', '_run-temp-worker', '_run-watchdog', '_run-watchdogs'], aliases: ['man'],
|
|
136
136
|
});
|
|
137
137
|
const globalValueOptions = new Set(['-c', '--configuration']);
|
|
138
138
|
/** Classify before Commander parsing. Unknown and internal paths fail closed. */
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ManagedHarnessOurs } from '../agent-ours/harness.js';
|
|
1
2
|
import type { CommonPermissions, FleetPermissionMode, ResolvedRole } from '../config.js';
|
|
2
3
|
import type { AgentSession } from '../session/types.js';
|
|
3
4
|
import type { Launch, SessionPrep } from './types.js';
|
|
@@ -22,6 +23,7 @@ export interface AgentSessionConfigSelection {
|
|
|
22
23
|
value: string;
|
|
23
24
|
}
|
|
24
25
|
export interface AgentSessionStartOptions {
|
|
26
|
+
managedOurs?: ManagedHarnessOurs;
|
|
25
27
|
role: ResolvedRole;
|
|
26
28
|
prep: SessionPrep;
|
|
27
29
|
launch: PreparedAgentSessionLaunch;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { managedClaudeMeta } from '../agent-ours/harness.js';
|
|
1
2
|
import { AcpSession } from '../session/acp.js';
|
|
2
3
|
import { acpAdapterState, } from './acp-session-transport.js';
|
|
3
4
|
/** Explicit Claude Code implementation of Fleet's shared agent-session factory. */
|
|
@@ -25,8 +26,9 @@ export class ClaudeCodeAgentSessionAdapter {
|
|
|
25
26
|
argv: launch.argv, cwd: options.cwd, env: launch.env,
|
|
26
27
|
stateDir: options.stateDir, mode: options.mode, permissions: options.permissions,
|
|
27
28
|
modeId: this.strategy.permissionModeId(role),
|
|
28
|
-
mcpServers: this.strategy.mcpServers(role),
|
|
29
|
-
|
|
29
|
+
mcpServers: options.managedOurs ? [...(this.strategy.mcpServers(role) ?? []).filter(s => s.name !== 'ours'), options.managedOurs.server] : this.strategy.mcpServers(role),
|
|
30
|
+
inheritEnvironment: options.managedOurs ? false : undefined,
|
|
31
|
+
sessionMeta: options.managedOurs ? managedClaudeMeta(this.strategy.sessionMeta(role, prep)) : this.strategy.sessionMeta(role, prep),
|
|
30
32
|
configSelections: this.strategy.sessionConfigSelections(role),
|
|
31
33
|
permissionMode: options.permissionMode,
|
|
32
34
|
permissionMetadataSource: acpAdapterState(launch.adapterState).permissionMetadataSource,
|
|
@@ -23,6 +23,12 @@ export class CodexAgentSessionAdapter {
|
|
|
23
23
|
}
|
|
24
24
|
start(options) {
|
|
25
25
|
const { role, prep, launch } = options;
|
|
26
|
+
const original = this.strategy.nativeConfig(role) ?? {};
|
|
27
|
+
const managed = options.managedOurs?.native;
|
|
28
|
+
const config = managed ? { ...original, ...managed,
|
|
29
|
+
mcp_servers: { ...(original.mcp_servers ?? {}), ...(managed.mcp_servers ?? {}) },
|
|
30
|
+
plugins: { ...(original.plugins ?? {}), ...(managed.plugins ?? {}) },
|
|
31
|
+
} : original;
|
|
26
32
|
if (role.session === 'codex-app-server')
|
|
27
33
|
return this.nativeTransport({
|
|
28
34
|
name: role.name, argv: launch.argv, cwd: options.cwd, env: launch.env,
|
|
@@ -31,7 +37,7 @@ export class CodexAgentSessionAdapter {
|
|
|
31
37
|
model: role.model, effort: role.effort,
|
|
32
38
|
approvalPolicy: this.strategy.approvalPolicy(role),
|
|
33
39
|
sandbox: this.strategy.sandbox(role),
|
|
34
|
-
config
|
|
40
|
+
config,
|
|
35
41
|
addDirs: this.strategy.addDirs(role),
|
|
36
42
|
log: options.log,
|
|
37
43
|
});
|
|
@@ -40,7 +46,8 @@ export class CodexAgentSessionAdapter {
|
|
|
40
46
|
argv: launch.argv, cwd: options.cwd, env: launch.env,
|
|
41
47
|
stateDir: options.stateDir, mode: options.mode, permissions: options.permissions,
|
|
42
48
|
modeId: this.strategy.permissionModeId(role),
|
|
43
|
-
mcpServers: this.strategy.mcpServers(role),
|
|
49
|
+
mcpServers: options.managedOurs ? [...(this.strategy.mcpServers(role) ?? []).filter(s => s.name !== 'ours'), options.managedOurs.server] : this.strategy.mcpServers(role),
|
|
50
|
+
inheritEnvironment: options.managedOurs ? false : undefined,
|
|
44
51
|
sessionMeta: this.strategy.sessionMeta(role, prep),
|
|
45
52
|
configSelections: this.strategy.sessionConfigSelections(role),
|
|
46
53
|
permissionMode: options.permissionMode,
|
|
@@ -53,6 +53,12 @@ export class HermesAgentSessionAdapter {
|
|
|
53
53
|
// composition; no second ambient merge may reintroduce provider secrets.
|
|
54
54
|
const env = hermesChildEnvironment(role, preparedHome(prep), launch.env, launch.env);
|
|
55
55
|
delete env.OURS_AUTOSTART;
|
|
56
|
+
if (options.managedOurs) {
|
|
57
|
+
for (const key of Object.keys(env))
|
|
58
|
+
if (key.startsWith('OURS_') && !key.startsWith('OURS_FLEET_'))
|
|
59
|
+
delete env[key];
|
|
60
|
+
Object.assign(env, { FLEET_OURS_MANAGED: '1' });
|
|
61
|
+
}
|
|
56
62
|
await this.checks.preflight?.(options, env);
|
|
57
63
|
const nativeProvider = await this.checks.expectedProvider(role, prep);
|
|
58
64
|
if (typeof nativeProvider !== 'string' || !nativeProvider.trim())
|
|
@@ -68,7 +74,7 @@ export class HermesAgentSessionAdapter {
|
|
|
68
74
|
permissions: options.permissions, modeId, requireMode: true,
|
|
69
75
|
permissionMode: { fleetMode: options.permissionMode.fleetMode, nativeMode: modeId },
|
|
70
76
|
permissionTimeoutMs: 50_000,
|
|
71
|
-
mcpServers: hermesMcpServers(role, env),
|
|
77
|
+
mcpServers: options.managedOurs ? [...hermesMcpServers(role, env).filter(s => s.name !== 'ours'), options.managedOurs.server] : hermesMcpServers(role, env),
|
|
72
78
|
scrubObsoleteOursAutostart: true,
|
|
73
79
|
validateStartupResponse: async (initialized, created) => {
|
|
74
80
|
await this.checks.validateArtifact(initialized);
|
|
@@ -8,6 +8,8 @@ import { existsSync, readFileSync } from 'node:fs';
|
|
|
8
8
|
import { homedir } from 'node:os';
|
|
9
9
|
import { join, resolve } from 'node:path';
|
|
10
10
|
import { createConnection } from 'node:net';
|
|
11
|
+
import { readClientProfile } from '../client-profile.js';
|
|
12
|
+
import { coworkHttpCall } from './cowork-http.js';
|
|
11
13
|
const RPC_VERSION = 1;
|
|
12
14
|
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
13
15
|
const MAX_RESPONSE_BYTES = 4 * 1024 * 1024;
|
|
@@ -275,10 +277,15 @@ function rpcCall(socketPath, method, params, timeoutMs, connect) {
|
|
|
275
277
|
});
|
|
276
278
|
}
|
|
277
279
|
export function createCoworkAdapter(options = {}) {
|
|
278
|
-
const
|
|
280
|
+
const env = options.env ?? process.env;
|
|
281
|
+
const explicitLocal = options.socketPath !== undefined || options.configPath !== undefined
|
|
282
|
+
|| env.OURS_COWORK_CONFIG !== undefined || !!env.OURS_COWORK_STATE_DIR;
|
|
283
|
+
const profile = explicitLocal ? undefined : readClientProfile({ ...env, HOME: options.home ?? env.HOME });
|
|
284
|
+
const remote = profile?.serverUrl ? profile : undefined;
|
|
285
|
+
const socketPath = remote ? undefined : resolveCoworkSocketPath(options);
|
|
279
286
|
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
280
287
|
const connect = options.connect ?? ((path) => createConnection(path));
|
|
281
|
-
const call = (method, params) => rpcCall(socketPath, method, params, timeoutMs, connect);
|
|
288
|
+
const call = (method, params) => remote ? coworkHttpCall(remote, method, params, timeoutMs) : rpcCall(socketPath, method, params, timeoutMs, connect);
|
|
282
289
|
return {
|
|
283
290
|
async available() {
|
|
284
291
|
try {
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { ExplicitClientProfile } from '../client-profile.js';
|
|
2
|
+
/** Same RPC protocol as the local socket; never replay a possibly delivered mutation. */
|
|
3
|
+
export declare function coworkHttpCall(profile: ExplicitClientProfile, method: string, params: Record<string, unknown>, timeoutMs: number): Promise<unknown>;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { attachOursClient } from '@ours.network/sdk/client';
|
|
3
|
+
import { readPrivateFile } from '@ours.network/sdk/connector';
|
|
4
|
+
import { CoworkProtocolError, CoworkUnavailableError } from './cowork-adapter.js';
|
|
5
|
+
/** Same RPC protocol as the local socket; never replay a possibly delivered mutation. */
|
|
6
|
+
export async function coworkHttpCall(profile, method, params, timeoutMs) {
|
|
7
|
+
const id = randomUUID();
|
|
8
|
+
const signal = AbortSignal.timeout(timeoutMs);
|
|
9
|
+
const body = JSON.stringify({ version: 1, id, method, params });
|
|
10
|
+
if (Buffer.byteLength(body) > 1024 * 1024)
|
|
11
|
+
throw new CoworkProtocolError(method, 'request exceeded 1 MiB');
|
|
12
|
+
try {
|
|
13
|
+
const client = await attachOursClient({ endpoint: profile.endpoint, expectedInstanceId: profile.expectedInstanceId,
|
|
14
|
+
credentialPath: profile.credentialPath, sessionMode: 'external', leaseToken: `fleet-cowork-check-${id}`, env: {}, requestSignal: signal });
|
|
15
|
+
await client.close();
|
|
16
|
+
const token = readPrivateFile(profile.credentialPath, 4096).toString('utf8').trim();
|
|
17
|
+
const response = await fetch(profile.serverUrl + '/cowork/management/rpc', {
|
|
18
|
+
method: 'POST', redirect: 'error', signal,
|
|
19
|
+
headers: { 'content-type': 'application/json', 'x-ours-api-token': token },
|
|
20
|
+
body,
|
|
21
|
+
});
|
|
22
|
+
if (response.status === 401 || response.status === 403) {
|
|
23
|
+
await response.body?.cancel();
|
|
24
|
+
throw new CoworkProtocolError(method, 'server credential was rejected', 'unauthorized');
|
|
25
|
+
}
|
|
26
|
+
const reader = response.body?.getReader();
|
|
27
|
+
if (!reader)
|
|
28
|
+
throw new CoworkProtocolError(method, 'empty HTTP response');
|
|
29
|
+
const chunks = [];
|
|
30
|
+
let size = 0;
|
|
31
|
+
try {
|
|
32
|
+
for (;;) {
|
|
33
|
+
const { done, value } = await reader.read();
|
|
34
|
+
if (done)
|
|
35
|
+
break;
|
|
36
|
+
size += value.length;
|
|
37
|
+
if (size > 4 * 1024 * 1024)
|
|
38
|
+
throw new CoworkProtocolError(method, 'response exceeded 4 MiB');
|
|
39
|
+
chunks.push(value);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
await reader.cancel();
|
|
44
|
+
}
|
|
45
|
+
let rpc;
|
|
46
|
+
try {
|
|
47
|
+
rpc = JSON.parse(Buffer.concat(chunks).toString('utf8'));
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
throw new CoworkProtocolError(method, 'server returned malformed JSON');
|
|
51
|
+
}
|
|
52
|
+
if (!rpc || Array.isArray(rpc) || rpc.version !== 1 || rpc.id !== id
|
|
53
|
+
|| Object.hasOwn(rpc, 'result') === Object.hasOwn(rpc, 'error'))
|
|
54
|
+
throw new CoworkProtocolError(method, 'server returned an invalid RPC response');
|
|
55
|
+
if (Object.hasOwn(rpc, 'error')) {
|
|
56
|
+
const error = rpc.error;
|
|
57
|
+
if (!error || typeof error.code !== 'string' || typeof error.message !== 'string')
|
|
58
|
+
throw new CoworkProtocolError(method, 'server returned an invalid RPC error');
|
|
59
|
+
throw new CoworkProtocolError(method, error.message, error.code);
|
|
60
|
+
}
|
|
61
|
+
if (!response.ok)
|
|
62
|
+
throw new CoworkProtocolError(method, `management answered HTTP ${response.status}`);
|
|
63
|
+
return rpc.result;
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
if (error instanceof CoworkProtocolError)
|
|
67
|
+
throw error;
|
|
68
|
+
throw new CoworkUnavailableError('Cowork HTTP management failed; check the selected server and credential', { cause: error });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readRoomReadiness } from '../agent-ours/service.js';
|
|
1
2
|
import { createHash, randomUUID } from 'node:crypto';
|
|
2
3
|
import { existsSync, readFileSync, realpathSync } from 'node:fs';
|
|
3
4
|
import { join } from 'node:path';
|
|
@@ -149,8 +150,7 @@ function launchMatches(dir, member, actionId, taskSha, roomId, roomIdentityCid,
|
|
|
149
150
|
&& (startup.anonymous ?? false) === anonymous
|
|
150
151
|
&& sha256Text(startup.task ?? '') === taskSha
|
|
151
152
|
&& (expectedInviteId === undefined || startup.invite_id === expectedInviteId)
|
|
152
|
-
&&
|
|
153
|
-
&& startup.invite.length > 0;
|
|
153
|
+
&& startup.invite === '';
|
|
154
154
|
}
|
|
155
155
|
catch {
|
|
156
156
|
return false;
|
|
@@ -345,7 +345,14 @@ function reconcileMemberSeats(roomId, members, observed, ownerSeatCid) {
|
|
|
345
345
|
&& seat.seat_state === 'active');
|
|
346
346
|
const seats = current.member_seats.map(seat => {
|
|
347
347
|
const member = members.find(candidate => candidate.name === seat.role_name);
|
|
348
|
+
const readiness = readRoomReadiness(current.room_identity_cid, member.name);
|
|
349
|
+
if (!readiness || readiness.room !== roomId || readiness.invite !== seat.invite_id) {
|
|
350
|
+
complete = false;
|
|
351
|
+
return seat;
|
|
352
|
+
}
|
|
348
353
|
const found = exactCoworkSeat(observed, member, seat.invite_id);
|
|
354
|
+
if (found && found.identity_cid !== readiness.cid)
|
|
355
|
+
throw new Error('Cowork seat CID does not match supervisor provisioned identity');
|
|
349
356
|
if (!found || found.seat_state !== 'active') {
|
|
350
357
|
complete = false;
|
|
351
358
|
return seat;
|
package/dist/runner.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { prepareManagedAgent, releaseManagedAgent } from './agent-ours/service.js';
|
|
1
2
|
import { type ResolvedRole } from './config.js';
|
|
2
3
|
import { type MonitorHandle, type MonitorOpts, type FetchLike } from './monitor.js';
|
|
3
4
|
import { type DaemonGenerationProbe } from './daemon-recovery.js';
|
|
@@ -9,6 +10,8 @@ import { type OwnerChannelHandle, type OwnerChannelOptions } from './owner-chann
|
|
|
9
10
|
import { type OwnerBinderLease } from './owner-channel/binder.js';
|
|
10
11
|
import { ScheduledLoopManager, type ScheduledLoopManagerHandle } from './loops/manager.js';
|
|
11
12
|
export interface RunnerDeps {
|
|
13
|
+
prepareAgentOurs: typeof prepareManagedAgent;
|
|
14
|
+
releaseAgentOurs: typeof releaseManagedAgent;
|
|
12
15
|
exec: Exec;
|
|
13
16
|
cpuDelegated(): boolean;
|
|
14
17
|
isAlive(pid: number): boolean;
|
|
@@ -168,6 +171,7 @@ export declare function runOnce(name: string, opts?: {
|
|
|
168
171
|
temp?: boolean;
|
|
169
172
|
configPath?: string;
|
|
170
173
|
allowResumeRotation?: boolean;
|
|
174
|
+
identityLifetime?: 'permanent' | 'temporary';
|
|
171
175
|
}, partialDeps?: Partial<RunnerDeps>): Promise<AttemptResult>;
|
|
172
176
|
/**
|
|
173
177
|
* The persistent supervisor for one permanent role: run child sessions in a
|