@volter/supercode-orchestrator 0.5.207 → 0.5.208
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 +30 -2
- package/agents.mjs +27 -12
- package/bin/connection-runner.mjs +12 -21
- package/bin/orchestrator.mjs +16 -3
- package/board/agent-declarations.mjs +3 -3
- package/board/args.mjs +2 -2
- package/board/card-state.mjs +29 -0
- package/board/ci.mjs +87 -29
- package/board/cli.mjs +125 -91
- package/board/codex-pane-inputs.mjs +44 -0
- package/board/dispatch.mjs +354 -84
- package/board/engine.mjs +77 -50
- package/board/event-stream.mjs +6 -1
- package/board/files.mjs +3 -1
- package/board/listing.mjs +13 -11
- package/board/machine-fleet.mjs +7 -5
- package/board/notices.mjs +2 -2
- package/board/published.mjs +119 -32
- package/board/readable-id.mjs +34 -0
- package/board/record-view.mjs +10 -1
- package/board/request-mail.mjs +9 -3
- package/board/store.mjs +95 -14
- package/board/tools.mjs +10 -8
- package/board/visibility.mjs +11 -0
- package/board/workflow.mjs +58 -16
- package/board/workspace.mjs +21 -9
- package/board/ztrack.mjs +111 -44
- package/codex-pane-resume.mjs +1 -0
- package/connection.mjs +95 -26
- package/index.d.ts +6 -2
- package/loop.mjs +51 -3
- package/mcp/bridge.mjs +4 -1
- package/mcp/hermes-kanban-tools.json +5 -1
- package/ontology.d.ts +7 -24
- package/orchestration.mjs +6 -3
- package/package.json +5 -4
- package/worker-route.mjs +31 -3
package/loop.mjs
CHANGED
|
@@ -4,13 +4,14 @@
|
|
|
4
4
|
// This is the only module that performs I/O for the loop. It holds the single
|
|
5
5
|
// state value, the vault, the SDK client, the adapters, and an injected clock.
|
|
6
6
|
|
|
7
|
-
import { mkdirSync, writeFileSync, existsSync, readdirSync, readFileSync, renameSync, unlinkSync } from 'node:fs';
|
|
7
|
+
import { mkdirSync, writeFileSync, existsSync, readdirSync, readFileSync, renameSync, unlinkSync, realpathSync } from 'node:fs';
|
|
8
8
|
import { basename, join, dirname } from 'node:path';
|
|
9
9
|
import { step, leaseId } from './reducer.mjs';
|
|
10
10
|
import * as events from './events.mjs';
|
|
11
|
-
import { read, save, retainWorld, releaseWorld } from './orchestration.mjs';
|
|
11
|
+
import { read, save, retainWorld, releaseWorld, refreshVault } from './orchestration.mjs';
|
|
12
12
|
import { INSTRUCTION_ARGUMENTS, NAME_ARGUMENTS, activationEnv, activationCwd, workerCwd, declareWorkerFolder, declareWorkerHome, approvalPolicy, sandboxArguments, limitArguments, materializePersona, materializeSkills, modelArguments, turnCounter, userHomeArguments, userHomePersona } from './activation.mjs';
|
|
13
|
-
import { workerRoute } from './worker-route.mjs';
|
|
13
|
+
import { workerRoute, codexPaneRoute } from './worker-route.mjs';
|
|
14
|
+
import { projectCodexPaneResume } from './codex-pane-resume.mjs';
|
|
14
15
|
import { hermesAccess, hermesApproval, untrustedServerVerdict, hermesToolEnv, memorySnapshot, mirrorSession, mirrorState, recordIncident, sealMirror } from './hermes-cli.mjs';
|
|
15
16
|
import { hermesSessionKey, homeChannel } from './ir.mjs';
|
|
16
17
|
import { surfaceKeyString } from './ir.mjs';
|
|
@@ -536,6 +537,50 @@ export class Orchestrator {
|
|
|
536
537
|
return runtime.handle;
|
|
537
538
|
}
|
|
538
539
|
|
|
540
|
+
/** A private local preparation door for an explicit headful mailbox main.
|
|
541
|
+
* It returns only launch metadata and the configured forward's stand-in key;
|
|
542
|
+
* it creates no hosted conversation, controller lease, process or first turn. */
|
|
543
|
+
async prepareMainPane({ directory, name, resume = null }) {
|
|
544
|
+
if (typeof directory !== 'string' || typeof name !== 'string' || !/^[A-Za-z0-9._-]+$/.test(name))
|
|
545
|
+
throw new Error('a headful main needs its exact profile directory and agent name');
|
|
546
|
+
const folder = realpathSync(directory);
|
|
547
|
+
const profiles = Object.values(this.state.profiles).filter(profile => {
|
|
548
|
+
try { return realpathSync(profile.dir) === folder; } catch { return false; }
|
|
549
|
+
});
|
|
550
|
+
if (!profiles.length) throw Object.assign(new Error('this home has no profile at that directory'), { code: 'not_this_home' });
|
|
551
|
+
if (profiles.length !== 1) throw new Error('the directory names more than one profile; no main was prepared');
|
|
552
|
+
const profile = profiles[0], worker = profile.worker;
|
|
553
|
+
if (worker?.harness !== 'codex') throw new Error('the selected main profile does not run Codex');
|
|
554
|
+
refreshVault(this.state, this.vault);
|
|
555
|
+
const route = codexPaneRoute(profile, worker, this.vault);
|
|
556
|
+
if (resume !== null) {
|
|
557
|
+
if (resume.harness !== 'codex' || typeof resume.session_id !== 'string' || resume.storage?.kind !== 'file')
|
|
558
|
+
throw new Error('a main resume needs its exact Codex transcript locator');
|
|
559
|
+
projectCodexPaneResume(route.env.CODEX_HOME, resume.storage.path, resume.session_id);
|
|
560
|
+
}
|
|
561
|
+
const context = process.env.SUPERCODE_CONTEXT, scope = agentScope(context);
|
|
562
|
+
const agent = await localProfileAgent({ root: this.state.root, org: scope.org, server: scope.server,
|
|
563
|
+
profiles: this.state.profiles, profile: profile.name, agent: name, declarations: this.state.agents });
|
|
564
|
+
const env = { ...route.env, ...agentEnvironment(agent, { root: this.state.root,
|
|
565
|
+
context: localReaderContext(scope.org, agent.identity.id) ?? undefined, main: true }) };
|
|
566
|
+
// The headful main uses the same profile-owned MCP door and identity
|
|
567
|
+
// preamble. The returned plan carries no configured server credentials.
|
|
568
|
+
mountOrchestratorMcp({ root: this.state.root, profile, harness: 'codex',
|
|
569
|
+
socketPath: this.bridge?.path ?? socketPathFor(this.state.root), vault: this.vault,
|
|
570
|
+
agentId: agent.identity.id });
|
|
571
|
+
const instructions = agentPreamble(agent, { main: true,
|
|
572
|
+
previous: localAgentSessions(this.state.root, scope.org, agent.identity.id) });
|
|
573
|
+
const arguments_ = [...route.args, ...INSTRUCTION_ARGUMENTS.codex(instructions)];
|
|
574
|
+
if (resume === null) {
|
|
575
|
+
arguments_.push(...modelArguments({ ...worker, model: route.model }), '--ask-for-approval', approvalPolicy(profile, 'codex'));
|
|
576
|
+
arguments_.push(...sandboxArguments(profile, 'codex'));
|
|
577
|
+
if (worker.reasoning_effort) arguments_.push('-c', `model_reasoning_effort=${JSON.stringify(worker.reasoning_effort)}`);
|
|
578
|
+
}
|
|
579
|
+
declareWorkerHome({ harness: 'codex', env, cwd: folder, log: (level, text) => this.log(level, text) });
|
|
580
|
+
return { ran: `prepared a headful main for profile ${profile.name}`, program: 'codex',
|
|
581
|
+
profile: profile.name, cwd: folder, arguments: arguments_, env };
|
|
582
|
+
}
|
|
583
|
+
|
|
539
584
|
/**
|
|
540
585
|
* A new concurrent session of this profile's agent, asked for on its behalf (operator `sessions.launch`): the agent is
|
|
541
586
|
* the one this home holds for the profile, and must be the one asked for. The session is composed by startRuntime
|
|
@@ -570,6 +615,8 @@ export class Orchestrator {
|
|
|
570
615
|
const native=prior.session_ref.slice(prior.session_ref.indexOf(':')+1);
|
|
571
616
|
const page=await this.client.discover({harnesses:[locator.harness],query:native,limit:200});
|
|
572
617
|
target=page.sessions.find(item=>item.locator.harness===locator.harness&&item.locator.session_id===native)?.locator;
|
|
618
|
+
// a source still being read is no proof the retained session is gone: no replacement is made either way
|
|
619
|
+
if(!target&&page.reconciling?.length)throw new Error(`retained fork session cannot be confirmed yet: the session index is still reading ${page.reconciling.join(', ')}; no replacement was created`);
|
|
573
620
|
if(!target)throw new Error('retained fork session is not available; no replacement was created');
|
|
574
621
|
}else {
|
|
575
622
|
const loaded=await this.client.loadWindow(locator,{message_limit:1});
|
|
@@ -814,6 +861,7 @@ export class Orchestrator {
|
|
|
814
861
|
if (locator.storage) return locator;
|
|
815
862
|
const found = await this.client.request('harness.v1.sessions.discover', { query: locator.session_id, harnesses: [locator.harness], limit: 200 });
|
|
816
863
|
const whole = found.sessions?.find((item) => item.locator?.harness === locator.harness && item.locator?.session_id === locator.session_id)?.locator;
|
|
864
|
+
if (!whole && found.reconciling?.length) throw new Error(`${locator.harness}:${locator.session_id} cannot be found yet: the session index is still reading ${found.reconciling.join(', ')}`);
|
|
817
865
|
if (!whole) throw new Error(`${locator.harness}:${locator.session_id} is not in its harness catalogue`);
|
|
818
866
|
return whole;
|
|
819
867
|
}
|
package/mcp/bridge.mjs
CHANGED
|
@@ -123,7 +123,10 @@ async function dispatch(orch, call, log) {
|
|
|
123
123
|
async function dispatchOperator(orch, { op, args, profile }, log) {
|
|
124
124
|
const name = typeof profile === 'string' && profile ? profile : 'default';
|
|
125
125
|
let answer;
|
|
126
|
-
if (op === 'agents.
|
|
126
|
+
if (op === 'agents.main_plan') {
|
|
127
|
+
try { answer = { ok: true, result: await orch.prepareMainPane(args ?? {}) }; }
|
|
128
|
+
catch (error) { answer = { ok: false, error: error.message, ...(error.code ? { code: error.code } : {}) }; }
|
|
129
|
+
} else if (op === 'agents.fork') {
|
|
127
130
|
// Operation `fork`: a declared child agent, its profile a copy of the parent's, its session a fork of the parent's.
|
|
128
131
|
try {
|
|
129
132
|
const a = args ?? {};
|
|
@@ -46,7 +46,11 @@
|
|
|
46
46
|
},
|
|
47
47
|
"include_archived": {
|
|
48
48
|
"type": "boolean",
|
|
49
|
-
"description": "Include archived tasks. Defaults to false."
|
|
49
|
+
"description": "Include cancelled and archived cards and their tasks. Defaults to false."
|
|
50
|
+
},
|
|
51
|
+
"include_subtasks": {
|
|
52
|
+
"type": "boolean",
|
|
53
|
+
"description": "Include subtasks as separate rows. Defaults to false; use kanban_show to read a card's tasks inside it."
|
|
50
54
|
},
|
|
51
55
|
"limit": {
|
|
52
56
|
"type": "integer",
|
package/ontology.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// GENERATED from `supercode ontology schema` — do not edit; run node sdk/orchestrator/scripts/generate-types.mjs
|
|
2
2
|
//
|
|
3
|
-
// The
|
|
4
|
-
//
|
|
3
|
+
// The committed `sdk/interchange/ontology-schemas.json` contracts own these types.
|
|
4
|
+
// Native serde records remain only with the capabilities still using them.
|
|
5
5
|
|
|
6
6
|
/** The access record, as Hermes's stores hold it. */
|
|
7
7
|
export interface Access {
|
|
@@ -45,8 +45,6 @@ export interface AgentDecl {
|
|
|
45
45
|
label?: string | null;
|
|
46
46
|
/** The agent it is declared under, if not the home's root. */
|
|
47
47
|
parent?: string | null;
|
|
48
|
-
/** A person set on purpose as its sponsor; absent, the sponsor is inherited. */
|
|
49
|
-
sponsor?: string | null;
|
|
50
48
|
/** What a source format said about it that the IR has no field for, verbatim. */
|
|
51
49
|
residue?: Record<string, unknown>;
|
|
52
50
|
}
|
|
@@ -115,7 +113,7 @@ export interface Binding {
|
|
|
115
113
|
|
|
116
114
|
/**
|
|
117
115
|
* A per-action gate on a profile's agents: a start or resume over it is refused, with the reason,
|
|
118
|
-
* and the refusal is told to the manager
|
|
116
|
+
* and the refusal is told to the manager. It never pauses or ends anything itself.
|
|
119
117
|
*/
|
|
120
118
|
export interface Budget {
|
|
121
119
|
/** The window. */
|
|
@@ -390,19 +388,7 @@ export type FireStatus =
|
|
|
390
388
|
| 'unknown';
|
|
391
389
|
|
|
392
390
|
/** Observations a role can subscribe to, separate from a card's transition events. */
|
|
393
|
-
export type FleetEventKind =
|
|
394
|
-
/** A native session process first observed running. */
|
|
395
|
-
| 'session_started'
|
|
396
|
-
/** A known native session observed running in a new process incarnation. */
|
|
397
|
-
| 'session_resumed'
|
|
398
|
-
/** A native source reports a session process ended; unavailable sources are not ends. */
|
|
399
|
-
| 'session_ended'
|
|
400
|
-
/** More than one observed live process incarnation names the same native session. */
|
|
401
|
-
| 'duplicate_session_process'
|
|
402
|
-
/** A card's native session reports that it needs its user's input. */
|
|
403
|
-
| 'card_waiting_on_user'
|
|
404
|
-
/** A machine's health source reports a critical alarm. */
|
|
405
|
-
| 'machine_health_crit';
|
|
391
|
+
export type FleetEventKind = 'session_started' | 'session_resumed' | 'session_ended' | 'duplicate_session_process' | 'card_waiting_on_user' | 'machine_health_crit';
|
|
406
392
|
|
|
407
393
|
/** A conversation moved (or moving) to another surface — Hermes `handoff_*`. */
|
|
408
394
|
export interface Handoff {
|
|
@@ -836,7 +822,7 @@ export type SecretRef =
|
|
|
836
822
|
/** A key in the home's own `.env` file. */
|
|
837
823
|
| { dotenv: string };
|
|
838
824
|
|
|
839
|
-
/** A session's usage, kept on the session and summed upward (session → agent →
|
|
825
|
+
/** A session's usage, kept on the session and summed upward (session → agent → Org). */
|
|
840
826
|
export interface SessionUsage {
|
|
841
827
|
/** The agent the session belongs to, when it has one. */
|
|
842
828
|
agent?: string | null;
|
|
@@ -1134,11 +1120,6 @@ export interface Workflow {
|
|
|
1134
1120
|
params?: Record<string, unknown>;
|
|
1135
1121
|
/** Who acts on a card, each an expression naming a profile (`implementer: card.assignee`) or a person. */
|
|
1136
1122
|
roles?: Record<string, Expr>;
|
|
1137
|
-
/**
|
|
1138
|
-
* Opt-in fleet observations delivered as mail to each role's declared addresses. These are not card
|
|
1139
|
-
* transitions and grant no observation or machine authority. The manager is an implicit workflow role.
|
|
1140
|
-
*/
|
|
1141
|
-
listeners?: Record<string, RoleFleetListener>;
|
|
1142
1123
|
/** Where a new card starts: the first transition whose guard holds. */
|
|
1143
1124
|
initial: Transition[];
|
|
1144
1125
|
/** Every status, by name. */
|
|
@@ -1152,4 +1133,6 @@ export interface Workflow {
|
|
|
1152
1133
|
* are), the event kinds it hears and the template each is told in. A kind not named is not sent.
|
|
1153
1134
|
*/
|
|
1154
1135
|
notify?: Record<string, Record<string, string>>;
|
|
1136
|
+
/** Role fleet listeners; a declaration grants no machine or mailbox authority. */
|
|
1137
|
+
listeners?: Record<string, RoleFleetListener>;
|
|
1155
1138
|
}
|
package/orchestration.mjs
CHANGED
|
@@ -150,7 +150,7 @@ export async function load(root) {
|
|
|
150
150
|
const { orchestration, vault_keys } = await through((d) => d.orchestrationLoad({ root }));
|
|
151
151
|
let vault = readVault(orchestration);
|
|
152
152
|
if (vault_keys.some((k) => !vault.has(k))) {
|
|
153
|
-
await through((d) => d.orchestrationSave({ root, orchestration }));
|
|
153
|
+
await through((d) => d.orchestrationSave({ root, orchestration, preserve_workflow: true }));
|
|
154
154
|
vault = readVault(orchestration);
|
|
155
155
|
const missing = vault_keys.filter((k) => !vault.has(k));
|
|
156
156
|
if (missing.length) throw new Error(`${root}: ${missing.join(', ')} referenced but not in .env after normalization`);
|
|
@@ -164,12 +164,15 @@ export async function read(root) {
|
|
|
164
164
|
}
|
|
165
165
|
|
|
166
166
|
/**
|
|
167
|
-
* `save(state, root)` —
|
|
167
|
+
* `save(state, root)` — persist runtime state, preserving the home's workflow.yaml.
|
|
168
|
+
* Board policy belongs to the live home, not the runtime's earlier snapshot. Full
|
|
169
|
+
* home creation/replacement uses orchestrationSave or importFrom explicitly.
|
|
170
|
+
* Other artifacts are canonical and byte-stable when their record has not changed.
|
|
168
171
|
* Answers the entries the door could not read (`rejected`, each `{ collection, key, reason, kept_stored }`): each was
|
|
169
172
|
* written as the home last held it, or left out when it held none, and none refuses the rest of the save.
|
|
170
173
|
*/
|
|
171
174
|
export async function save(state, root = state.root) {
|
|
172
|
-
const saved = await through((d) => d.orchestrationSave({ root, orchestration: persistedView(state) }));
|
|
175
|
+
const saved = await through((d) => d.orchestrationSave({ root, orchestration: persistedView(state), preserve_workflow: true }));
|
|
173
176
|
return { rejected: Array.isArray(saved?.rejected) ? saved.rejected : [] };
|
|
174
177
|
}
|
|
175
178
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/supercode-orchestrator",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.208",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The orchestrator runtime over the Volter Harness ontology: one typed operational model whose folder is its serialization, read and written through the harness orchestration doors (docs/ORCHESTRATOR-IR.md)",
|
|
6
6
|
"exports": {
|
|
@@ -32,10 +32,10 @@
|
|
|
32
32
|
"check-types": "node scripts/generate-types.mjs --check"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@volter/supercode-teams": "^0.3.
|
|
35
|
+
"@volter/supercode-teams": "^0.3.345",
|
|
36
36
|
"@volter/supercode-terminal": "^0.3.10",
|
|
37
37
|
"@volter/supercode-harness-sdk": "^0.3.104",
|
|
38
|
-
"@volter/teams": "0.2.
|
|
38
|
+
"@volter/teams": "0.2.98",
|
|
39
39
|
"@volter/ztrack": "^2.4.23",
|
|
40
40
|
"yaml": "^2.9.0"
|
|
41
41
|
},
|
|
@@ -61,5 +61,6 @@
|
|
|
61
61
|
"board",
|
|
62
62
|
"apply",
|
|
63
63
|
"README.md"
|
|
64
|
-
]
|
|
64
|
+
],
|
|
65
|
+
"gitHead": "db4a88b4d2f670654fa8e57dbc3e93c7daa8d2bb"
|
|
65
66
|
}
|
package/worker-route.mjs
CHANGED
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
//
|
|
28
28
|
// A route this table does not have is refused by name at launch, never run on the worker's default:
|
|
29
29
|
// that would spend on a model nobody chose.
|
|
30
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
30
|
+
import { existsSync, lstatSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
31
31
|
import { join } from 'node:path';
|
|
32
|
-
import { MODEL_ARGUMENTS } from './activation.mjs';
|
|
32
|
+
import { MODEL_ARGUMENTS, materializePersona, materializeSkills } from './activation.mjs';
|
|
33
33
|
|
|
34
34
|
/** The Codex provider id a Hermes provider is served under. */
|
|
35
35
|
const PROVIDER_ID = { custom: 'hermes_custom', openai: 'openai', 'openai-codex': 'hermes_codex' };
|
|
@@ -75,6 +75,7 @@ export function workerRoute(profile, worker, vault) {
|
|
|
75
75
|
const forward = vault?.get?.('HERMES_CODEX_BASE_URL') ?? process.env.HERMES_CODEX_BASE_URL;
|
|
76
76
|
if (!forward) throw new Error('model.provider openai-codex needs the Codex forward (HERMES_CODEX_BASE_URL in the home\'s .env, which the kit\'s start writes); refused rather than run on a login the worker would have to hold');
|
|
77
77
|
table.base_url = forward;
|
|
78
|
+
table.requires_openai_auth = false;
|
|
78
79
|
table.env_key = LITERAL_KEY_ENV;
|
|
79
80
|
env[LITERAL_KEY_ENV] = 'valve';
|
|
80
81
|
writeProviderTable(join(profile.dir, 'codex'), id, table);
|
|
@@ -126,7 +127,7 @@ function writeProviderTable(home, id, table) {
|
|
|
126
127
|
const file = join(home, 'config.toml');
|
|
127
128
|
const text = existsSync(file) ? readFileSync(file, 'utf8') : '';
|
|
128
129
|
const header = `[model_providers.${id}]`;
|
|
129
|
-
const block = [header, ...Object.entries(table).map(([k, v]) => `${k} = ${JSON.stringify(String(v))}`)].join('\n');
|
|
130
|
+
const block = [header, ...Object.entries(table).map(([k, v]) => `${k} = ${typeof v === 'boolean' ? String(v) : JSON.stringify(String(v))}`)].join('\n');
|
|
130
131
|
const lines = text.split('\n');
|
|
131
132
|
const start = lines.findIndex((line) => line.trim() === header);
|
|
132
133
|
let next;
|
|
@@ -138,3 +139,30 @@ function writeProviderTable(home, id, table) {
|
|
|
138
139
|
}
|
|
139
140
|
if (next !== text) writeFileSync(file, next, { mode: 0o600 });
|
|
140
141
|
}
|
|
142
|
+
|
|
143
|
+
/** The headful Codex lane must never fall back to the shared ChatGPT login.
|
|
144
|
+
* This returns only the configured forward's stand-in key and provider flags;
|
|
145
|
+
* it neither copies a credential nor starts a program or login. */
|
|
146
|
+
export function codexPaneRoute(profile, worker, vault) {
|
|
147
|
+
if (worker?.harness !== 'codex' || hermesModel(profile)?.provider !== 'openai-codex')
|
|
148
|
+
throw new Error('headful Codex requires model.provider openai-codex and its configured HERMES_CODEX_BASE_URL; no shared-login pane was launched');
|
|
149
|
+
if (worker.home === 'user') throw new Error('headful Codex requires a profile home; no shared-login pane was launched');
|
|
150
|
+
const forward = vault?.get?.('HERMES_CODEX_BASE_URL') ?? process.env.HERMES_CODEX_BASE_URL;
|
|
151
|
+
if (!forward) throw new Error('headful Codex requires the configured HERMES_CODEX_BASE_URL; no shared-login pane was launched');
|
|
152
|
+
let endpoint;
|
|
153
|
+
try { endpoint = new URL(forward); } catch { throw new Error('the configured Codex forward is not an HTTP endpoint; no pane was launched'); }
|
|
154
|
+
if (!['http:', 'https:'].includes(endpoint.protocol) || endpoint.username || endpoint.password)
|
|
155
|
+
throw new Error('the configured Codex forward must be HTTP(S) without URL credentials; no pane was launched');
|
|
156
|
+
const home = join(profile.dir, 'codex');
|
|
157
|
+
if (Object.hasOwn(worker.env ?? {}, 'CODEX_HOME') && worker.env.CODEX_HOME !== home)
|
|
158
|
+
throw new Error('headful Codex has a different worker.env.CODEX_HOME; no shared-login pane was launched');
|
|
159
|
+
// lstat includes a dangling auth symlink. Do not read, unlink or migrate it.
|
|
160
|
+
try {
|
|
161
|
+
lstatSync(join(home, 'auth.json'));
|
|
162
|
+
throw new Error('the headful Codex profile home holds an auth.json entry; a login-free home is required and no credential was changed');
|
|
163
|
+
} catch (error) { if (error.code !== 'ENOENT') throw error; }
|
|
164
|
+
materializePersona(profile);
|
|
165
|
+
materializeSkills(profile);
|
|
166
|
+
const route = workerRoute(profile, worker, vault);
|
|
167
|
+
return { ...route, env: { CODEX_HOME: home, ...route.env } };
|
|
168
|
+
}
|