@zixt/host 0.0.10 → 0.0.12
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/dist/index.js +386 -90
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -31,7 +31,7 @@ import { homedir } from "node:os";
|
|
|
31
31
|
// package.json
|
|
32
32
|
var package_default = {
|
|
33
33
|
name: "@zixt/host",
|
|
34
|
-
version: "0.0.
|
|
34
|
+
version: "0.0.12",
|
|
35
35
|
type: "module",
|
|
36
36
|
exports: {
|
|
37
37
|
".": "./src/client.ts",
|
|
@@ -15047,6 +15047,15 @@ var TaskRunnerSelection = external_exports.object({
|
|
|
15047
15047
|
model: runnerModel,
|
|
15048
15048
|
effort: TaskRunnerEffort.optional()
|
|
15049
15049
|
}).strict();
|
|
15050
|
+
var RunnerRuntimeBase = {
|
|
15051
|
+
type: RunnerType,
|
|
15052
|
+
/** Resolved model id as the CLI reports it (`gpt-5.6-sol`, `claude-opus-5`). */
|
|
15053
|
+
model: external_exports.string().min(1).max(120).refine(isSafeSingleLineDisplayText, "model must be a single display line").nullable(),
|
|
15054
|
+
/** Resolved reasoning effort; the CLI's own default when nothing was set. */
|
|
15055
|
+
effort: external_exports.string().min(1).max(40).refine(isSafeSingleLineDisplayText, "effort must be a single display line").nullable()
|
|
15056
|
+
};
|
|
15057
|
+
var TaskRunnerRuntimeObservation = external_exports.object(RunnerRuntimeBase).strict();
|
|
15058
|
+
var TaskRunnerRuntime = external_exports.object({ ...RunnerRuntimeBase, observedAt: IsoDate2 }).strict();
|
|
15050
15059
|
var RunnerConfig = external_exports.discriminatedUnion("type", [
|
|
15051
15060
|
external_exports.object({
|
|
15052
15061
|
type: external_exports.literal("claude-code"),
|
|
@@ -15840,6 +15849,12 @@ var Task = external_exports.object({
|
|
|
15840
15849
|
requestedHostId: HostId.nullable().default(null),
|
|
15841
15850
|
/** TS-16: per-Task runner/model/effort selection; null runs the teammate's configuration. */
|
|
15842
15851
|
runner: TaskRunnerSelection.nullable().default(null),
|
|
15852
|
+
/**
|
|
15853
|
+
* TS-16: what the runner CLI actually used on its newest attempt, as the
|
|
15854
|
+
* CLI itself reported it. Null until a run starts (or when the Machine
|
|
15855
|
+
* could not read the CLI's record), and replaced on every new attempt.
|
|
15856
|
+
*/
|
|
15857
|
+
runnerRuntime: TaskRunnerRuntime.nullable().default(null),
|
|
15843
15858
|
disposition: TaskDisposition.nullable(),
|
|
15844
15859
|
/** Follow-ups accepted but not yet folded into a run; the thread renders
|
|
15845
15860
|
* exactly this many trailing prompts as queued rather than answered. */
|
|
@@ -18320,6 +18335,12 @@ var TaskWorkingContextReport = external_exports.object({
|
|
|
18320
18335
|
epoch: external_exports.number().int().min(1),
|
|
18321
18336
|
context: TaskWorkingContextObservation
|
|
18322
18337
|
});
|
|
18338
|
+
var TaskRunnerRuntimeReport = external_exports.object({
|
|
18339
|
+
type: external_exports.literal("task.runner_runtime"),
|
|
18340
|
+
taskId: TaskId,
|
|
18341
|
+
epoch: external_exports.number().int().min(1),
|
|
18342
|
+
runtime: TaskRunnerRuntimeObservation
|
|
18343
|
+
});
|
|
18323
18344
|
var TaskTitle = external_exports.object({
|
|
18324
18345
|
type: external_exports.literal("task.title"),
|
|
18325
18346
|
taskId: TaskId,
|
|
@@ -18401,6 +18422,7 @@ var AgentOpRequest = external_exports.object({
|
|
|
18401
18422
|
var UpMessage = external_exports.discriminatedUnion("type", [
|
|
18402
18423
|
TaskEvent,
|
|
18403
18424
|
TaskWorkingContextReport,
|
|
18425
|
+
TaskRunnerRuntimeReport,
|
|
18404
18426
|
TaskTitle,
|
|
18405
18427
|
TaskResult,
|
|
18406
18428
|
HostReport,
|
|
@@ -18440,7 +18462,9 @@ var HelloAckFrame = external_exports.object({
|
|
|
18440
18462
|
features: external_exports.object({
|
|
18441
18463
|
browserSessions: external_exports.literal(true).optional(),
|
|
18442
18464
|
/** Host may send task.working_context only after this advertisement. */
|
|
18443
|
-
taskWorkingContext: external_exports.literal(true).optional()
|
|
18465
|
+
taskWorkingContext: external_exports.literal(true).optional(),
|
|
18466
|
+
/** Host may send task.runner_runtime only after this advertisement. */
|
|
18467
|
+
taskRunnerRuntime: external_exports.literal(true).optional()
|
|
18444
18468
|
}).optional(),
|
|
18445
18469
|
/**
|
|
18446
18470
|
* Workspace paths configured for this organization's teammates. The Machine
|
|
@@ -19382,6 +19406,7 @@ var HostClient = class _HostClient {
|
|
|
19382
19406
|
/** True only after the connected cloud's helloAck advertised the feature. */
|
|
19383
19407
|
browserFeatureActive = false;
|
|
19384
19408
|
workingContextFeatureActive = false;
|
|
19409
|
+
runnerRuntimeFeatureActive = false;
|
|
19385
19410
|
/** TCP open is not protocol authority; only the current helloAck opens upstream delivery. */
|
|
19386
19411
|
protocolReady = false;
|
|
19387
19412
|
/** Workspace paths this organization configured, as of the last hello/update. */
|
|
@@ -19419,6 +19444,7 @@ var HostClient = class _HostClient {
|
|
|
19419
19444
|
this.protocolReady = false;
|
|
19420
19445
|
this.browserFeatureActive = false;
|
|
19421
19446
|
this.workingContextFeatureActive = false;
|
|
19447
|
+
this.runnerRuntimeFeatureActive = false;
|
|
19422
19448
|
this.opts.browser?.attach(null);
|
|
19423
19449
|
await Promise.allSettled([
|
|
19424
19450
|
this.unwindAllRuns("host client stopped", "client_shutdown"),
|
|
@@ -19678,6 +19704,7 @@ var HostClient = class _HostClient {
|
|
|
19678
19704
|
if (this.ws !== void 0) return;
|
|
19679
19705
|
this.browserFeatureActive = false;
|
|
19680
19706
|
this.workingContextFeatureActive = false;
|
|
19707
|
+
this.runnerRuntimeFeatureActive = false;
|
|
19681
19708
|
this.opts.browser?.attach(null);
|
|
19682
19709
|
void this.opts.browser?.closeAll("machine_offline");
|
|
19683
19710
|
if (code === CLOSE_CODES.revoked) {
|
|
@@ -19706,13 +19733,13 @@ var HostClient = class _HostClient {
|
|
|
19706
19733
|
);
|
|
19707
19734
|
if (this.stopped) return;
|
|
19708
19735
|
if (!this.stopped && this.opts.reconnect !== false) {
|
|
19709
|
-
const
|
|
19736
|
+
const delay4 = reconnectBackoffDelay(this.reconnectFailures, this.opts.backoffMs);
|
|
19710
19737
|
this.reconnectFailures++;
|
|
19711
19738
|
if (this.reconnectTimer) clearTimeout(this.reconnectTimer);
|
|
19712
19739
|
this.reconnectTimer = setTimeout(() => {
|
|
19713
19740
|
this.reconnectTimer = void 0;
|
|
19714
19741
|
this.connect();
|
|
19715
|
-
},
|
|
19742
|
+
}, delay4);
|
|
19716
19743
|
} else {
|
|
19717
19744
|
this.stopped = true;
|
|
19718
19745
|
}
|
|
@@ -19743,7 +19770,7 @@ var HostClient = class _HostClient {
|
|
|
19743
19770
|
this.send(frame);
|
|
19744
19771
|
}
|
|
19745
19772
|
replaceablePendingUp(message) {
|
|
19746
|
-
return message.type === "host.report" || message.type === "task.working_context" || message.type === "task.title" || message.type === "task.event" && message.event.ephemeral === true;
|
|
19773
|
+
return message.type === "host.report" || message.type === "task.working_context" || message.type === "task.runner_runtime" || message.type === "task.title" || message.type === "task.event" && message.event.ephemeral === true;
|
|
19747
19774
|
}
|
|
19748
19775
|
supersedesPendingUp(candidate, incoming) {
|
|
19749
19776
|
if (candidate.type !== incoming.type) return false;
|
|
@@ -19754,7 +19781,7 @@ var HostClient = class _HostClient {
|
|
|
19754
19781
|
if (incoming.type === "agent.op") {
|
|
19755
19782
|
return candidate.type === "agent.op" && candidate.taskId === incoming.taskId && candidate.epoch === incoming.epoch && candidate.requestId === incoming.requestId;
|
|
19756
19783
|
}
|
|
19757
|
-
if (incoming.type === "task.working_context" || incoming.type === "task.title") {
|
|
19784
|
+
if (incoming.type === "task.working_context" || incoming.type === "task.runner_runtime" || incoming.type === "task.title") {
|
|
19758
19785
|
return candidate.type === incoming.type && candidate.taskId === incoming.taskId && candidate.epoch === incoming.epoch;
|
|
19759
19786
|
}
|
|
19760
19787
|
return false;
|
|
@@ -19914,6 +19941,7 @@ var HostClient = class _HostClient {
|
|
|
19914
19941
|
this.configuredWorkspaces = frame.workspaces ?? [];
|
|
19915
19942
|
this.browserFeatureActive = frame.features?.browserSessions === true;
|
|
19916
19943
|
this.workingContextFeatureActive = frame.features?.taskWorkingContext === true;
|
|
19944
|
+
this.runnerRuntimeFeatureActive = frame.features?.taskRunnerRuntime === true;
|
|
19917
19945
|
if (this.browserFeatureActive && this.opts.browser) {
|
|
19918
19946
|
this.opts.browser.attach({
|
|
19919
19947
|
state: (state, openRequestId) => this.sendBrowserFrame({
|
|
@@ -20397,6 +20425,21 @@ var HostClient = class _HostClient {
|
|
|
20397
20425
|
throw new Error("Working-context evidence could not be delivered before execution.");
|
|
20398
20426
|
}
|
|
20399
20427
|
};
|
|
20428
|
+
const emitRunnerRuntime = (runtime) => {
|
|
20429
|
+
if (!this.runnerRuntimeFeatureActive) return;
|
|
20430
|
+
const parsed = TaskRunnerRuntimeObservation.safeParse({
|
|
20431
|
+
type: runtime.type,
|
|
20432
|
+
model: runtime.model === null ? null : safe(runtime.model, 120),
|
|
20433
|
+
effort: runtime.effort === null ? null : safe(runtime.effort, 40)
|
|
20434
|
+
});
|
|
20435
|
+
if (!parsed.success) return;
|
|
20436
|
+
this.sendUp({
|
|
20437
|
+
type: "task.runner_runtime",
|
|
20438
|
+
taskId: assign.taskId,
|
|
20439
|
+
epoch: assign.epoch,
|
|
20440
|
+
runtime: parsed.data
|
|
20441
|
+
});
|
|
20442
|
+
};
|
|
20400
20443
|
const secrets = () => {
|
|
20401
20444
|
if (authorityController.signal.aborted) return Promise.resolve({});
|
|
20402
20445
|
const entry = this.secretGrants.get(cancelKey) ?? { resolvers: [] };
|
|
@@ -20723,6 +20766,7 @@ var HostClient = class _HostClient {
|
|
|
20723
20766
|
spec: assign.spec,
|
|
20724
20767
|
event: emitEvent,
|
|
20725
20768
|
workingContext: emitWorkingContext,
|
|
20769
|
+
runnerRuntime: emitRunnerRuntime,
|
|
20726
20770
|
siblingTasks: () => [...this.liveTasks.values()].filter((live) => live.agentId === assign.agentId && live.taskId !== assign.taskId).map(({ taskId, title }) => ({ taskId, title })),
|
|
20727
20771
|
fetchAttachment: (attachmentId) => this.fetchAttachment(attachmentId, authorityController.signal),
|
|
20728
20772
|
secrets,
|
|
@@ -24403,7 +24447,7 @@ var BrowserManager = class {
|
|
|
24403
24447
|
session.pendingFrame = frame;
|
|
24404
24448
|
if (session.frameTimer) return;
|
|
24405
24449
|
const elapsed = Date.now() - session.lastFrameSentAt;
|
|
24406
|
-
const
|
|
24450
|
+
const delay4 = Math.max(0, this.frameMinIntervalMs - elapsed);
|
|
24407
24451
|
session.frameTimer = setTimeout(() => {
|
|
24408
24452
|
session.frameTimer = null;
|
|
24409
24453
|
const pending = session.pendingFrame;
|
|
@@ -24418,7 +24462,7 @@ var BrowserManager = class {
|
|
|
24418
24462
|
width: pending.width,
|
|
24419
24463
|
height: pending.height
|
|
24420
24464
|
});
|
|
24421
|
-
},
|
|
24465
|
+
}, delay4);
|
|
24422
24466
|
session.frameTimer.unref?.();
|
|
24423
24467
|
}
|
|
24424
24468
|
markActivity(agentId) {
|
|
@@ -25784,6 +25828,7 @@ var GITHUB_TOOL_DEFINITIONS = {
|
|
|
25784
25828
|
description: "Open one exact GitHub repository for this task, prepare its local workspace, and enable its granted tools.",
|
|
25785
25829
|
inputSchema: {
|
|
25786
25830
|
type: "object",
|
|
25831
|
+
description: "Provide exactly one of repository_id or full_name.",
|
|
25787
25832
|
properties: {
|
|
25788
25833
|
repository_id: repositoryId,
|
|
25789
25834
|
full_name: {
|
|
@@ -25791,7 +25836,6 @@ var GITHUB_TOOL_DEFINITIONS = {
|
|
|
25791
25836
|
pattern: "^[A-Za-z0-9_.-]{1,100}/[A-Za-z0-9_.-]{1,100}$"
|
|
25792
25837
|
}
|
|
25793
25838
|
},
|
|
25794
|
-
oneOf: [{ required: ["repository_id"] }, { required: ["full_name"] }],
|
|
25795
25839
|
additionalProperties: false
|
|
25796
25840
|
}
|
|
25797
25841
|
},
|
|
@@ -33342,7 +33386,26 @@ function createCliRunner(adapter, opts = {}) {
|
|
|
33342
33386
|
}
|
|
33343
33387
|
},
|
|
33344
33388
|
agentOp: (op) => task.agentOp(op),
|
|
33345
|
-
|
|
33389
|
+
// GitHub repository work belongs in the installed `git` and `gh`
|
|
33390
|
+
// commands backed by GithubShellAuth. Do not advertise the bundled
|
|
33391
|
+
// repository/Issue/PR/Actions MCP surface to the model: besides being
|
|
33392
|
+
// redundant, provider schema dialect changes can otherwise prevent a
|
|
33393
|
+
// Claude request before any command runs. Repository creation remains
|
|
33394
|
+
// the one bounded exception because it uses Zixt's durable admin-intent
|
|
33395
|
+
// reservation and ambiguity reconciliation rather than ordinary repo
|
|
33396
|
+
// authority.
|
|
33397
|
+
toolPacks: toolPacks.flatMap((pack) => {
|
|
33398
|
+
if (pack.provider !== "github") return [pack];
|
|
33399
|
+
const tools = pack.tools.filter(({ name }) => name === "github_create_repository");
|
|
33400
|
+
if (tools.length === 0) return [];
|
|
33401
|
+
return [
|
|
33402
|
+
{
|
|
33403
|
+
...pack,
|
|
33404
|
+
tools,
|
|
33405
|
+
call: (name, args) => name === "github_create_repository" ? pack.call(name, args) : Promise.resolve({ ok: false, error: "Unknown GitHub operation." })
|
|
33406
|
+
}
|
|
33407
|
+
];
|
|
33408
|
+
})
|
|
33346
33409
|
});
|
|
33347
33410
|
const prepared = await adapter.prepareRun({
|
|
33348
33411
|
task,
|
|
@@ -33357,7 +33420,10 @@ function createCliRunner(adapter, opts = {}) {
|
|
|
33357
33420
|
// The trusted workspace facts describe where Zixt put provider clones,
|
|
33358
33421
|
// which is always the Zixt-owned root — never a configured workspace.
|
|
33359
33422
|
taskRoot,
|
|
33360
|
-
cwd
|
|
33423
|
+
cwd,
|
|
33424
|
+
command: resolvedCommand,
|
|
33425
|
+
commandPrefixArgs: prefixArgs,
|
|
33426
|
+
env
|
|
33361
33427
|
});
|
|
33362
33428
|
const runEnv = prepared.env ? { ...env, ...prepared.env } : env;
|
|
33363
33429
|
let attachmentSection = "";
|
|
@@ -34045,6 +34111,171 @@ function runCliProcess(options) {
|
|
|
34045
34111
|
|
|
34046
34112
|
// src/runners/claude-code.ts
|
|
34047
34113
|
import { randomUUID as randomUUID11 } from "node:crypto";
|
|
34114
|
+
|
|
34115
|
+
// src/runners/runtime-observation.ts
|
|
34116
|
+
import { open as open5, readdir as readdir4, realpath as realpath8 } from "node:fs/promises";
|
|
34117
|
+
import { homedir as homedir5 } from "node:os";
|
|
34118
|
+
import { join as join15 } from "node:path";
|
|
34119
|
+
var READ_WINDOW_BYTES = 1024 * 1024;
|
|
34120
|
+
var CATALOG_TIMEOUT_MS = 15e3;
|
|
34121
|
+
var CATALOG_OUTPUT_LIMIT_BYTES = 4 * 1024 * 1024;
|
|
34122
|
+
function homeFrom(env) {
|
|
34123
|
+
return env["HOME"] || env["USERPROFILE"] || homedir5();
|
|
34124
|
+
}
|
|
34125
|
+
async function readHead(path) {
|
|
34126
|
+
let handle;
|
|
34127
|
+
try {
|
|
34128
|
+
handle = await open5(path, "r");
|
|
34129
|
+
const buffer = Buffer.alloc(READ_WINDOW_BYTES);
|
|
34130
|
+
const { bytesRead } = await handle.read(buffer, 0, READ_WINDOW_BYTES, 0);
|
|
34131
|
+
return buffer.subarray(0, bytesRead).toString("utf8");
|
|
34132
|
+
} catch {
|
|
34133
|
+
return "";
|
|
34134
|
+
} finally {
|
|
34135
|
+
await handle?.close().catch(() => {
|
|
34136
|
+
});
|
|
34137
|
+
}
|
|
34138
|
+
}
|
|
34139
|
+
async function readTail(path) {
|
|
34140
|
+
let handle;
|
|
34141
|
+
try {
|
|
34142
|
+
handle = await open5(path, "r");
|
|
34143
|
+
const { size } = await handle.stat();
|
|
34144
|
+
const start = Math.max(0, size - READ_WINDOW_BYTES);
|
|
34145
|
+
const length = Math.min(size, READ_WINDOW_BYTES);
|
|
34146
|
+
const buffer = Buffer.alloc(length);
|
|
34147
|
+
const { bytesRead } = await handle.read(buffer, 0, length, start);
|
|
34148
|
+
const text = buffer.subarray(0, bytesRead).toString("utf8");
|
|
34149
|
+
return start === 0 ? text : text.slice(text.indexOf("\n") + 1);
|
|
34150
|
+
} catch {
|
|
34151
|
+
return "";
|
|
34152
|
+
} finally {
|
|
34153
|
+
await handle?.close().catch(() => {
|
|
34154
|
+
});
|
|
34155
|
+
}
|
|
34156
|
+
}
|
|
34157
|
+
function parseLines(text) {
|
|
34158
|
+
const records = [];
|
|
34159
|
+
for (const line of text.split(/\r?\n/)) {
|
|
34160
|
+
if (!line.trim()) continue;
|
|
34161
|
+
try {
|
|
34162
|
+
const value = JSON.parse(line);
|
|
34163
|
+
if (value && typeof value === "object") records.push(value);
|
|
34164
|
+
} catch {
|
|
34165
|
+
}
|
|
34166
|
+
}
|
|
34167
|
+
return records;
|
|
34168
|
+
}
|
|
34169
|
+
function displayValue(value, maxLength) {
|
|
34170
|
+
if (typeof value !== "string") return null;
|
|
34171
|
+
const trimmed = value.trim();
|
|
34172
|
+
if (!trimmed || trimmed.length > maxLength || /[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/u.test(trimmed)) {
|
|
34173
|
+
return null;
|
|
34174
|
+
}
|
|
34175
|
+
return trimmed;
|
|
34176
|
+
}
|
|
34177
|
+
function claudeTranscriptPath(input) {
|
|
34178
|
+
const configDir = input.env["CLAUDE_CONFIG_DIR"] || join15(homeFrom(input.env), ".claude");
|
|
34179
|
+
const slug = input.resolvedCwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
34180
|
+
return join15(configDir, "projects", slug, `${input.sessionId}.jsonl`);
|
|
34181
|
+
}
|
|
34182
|
+
async function readClaudeSessionEffort(input) {
|
|
34183
|
+
const resolvedCwd = await realpath8(input.cwd).catch(() => input.cwd);
|
|
34184
|
+
const path = claudeTranscriptPath({ env: input.env, resolvedCwd, sessionId: input.sessionId });
|
|
34185
|
+
const records = parseLines(await readTail(path));
|
|
34186
|
+
for (let index = records.length - 1; index >= 0; index -= 1) {
|
|
34187
|
+
const record2 = records[index];
|
|
34188
|
+
if (record2["type"] !== "assistant") continue;
|
|
34189
|
+
const effort = displayValue(record2["effort"], 40);
|
|
34190
|
+
if (effort) return effort;
|
|
34191
|
+
}
|
|
34192
|
+
return null;
|
|
34193
|
+
}
|
|
34194
|
+
async function newestDirectories(root, limit) {
|
|
34195
|
+
const entries = await readdir4(root, { withFileTypes: true }).catch(() => []);
|
|
34196
|
+
return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort((left, right) => right.localeCompare(left)).slice(0, limit).map((name) => join15(root, name));
|
|
34197
|
+
}
|
|
34198
|
+
async function findCodexRolloutPath(input) {
|
|
34199
|
+
const codexHome = input.env["CODEX_HOME"] || join15(homeFrom(input.env), ".codex");
|
|
34200
|
+
const sessions = join15(codexHome, "sessions");
|
|
34201
|
+
const suffix = `-${input.threadId}.jsonl`;
|
|
34202
|
+
for (const year of await newestDirectories(sessions, 2)) {
|
|
34203
|
+
for (const month of await newestDirectories(year, 2)) {
|
|
34204
|
+
for (const day of await newestDirectories(month, 3)) {
|
|
34205
|
+
const files = await readdir4(day).catch(() => []);
|
|
34206
|
+
const match = files.find((name) => name.endsWith(suffix));
|
|
34207
|
+
if (match) return join15(day, match);
|
|
34208
|
+
}
|
|
34209
|
+
}
|
|
34210
|
+
}
|
|
34211
|
+
return null;
|
|
34212
|
+
}
|
|
34213
|
+
async function readCodexSessionRuntime(input) {
|
|
34214
|
+
const path = await findCodexRolloutPath(input);
|
|
34215
|
+
if (!path) return null;
|
|
34216
|
+
const contexts = parseLines(await readHead(path)).filter(
|
|
34217
|
+
(record2) => record2["type"] === "turn_context"
|
|
34218
|
+
);
|
|
34219
|
+
const payload = contexts.at(-1)?.["payload"];
|
|
34220
|
+
if (!payload || typeof payload !== "object") return null;
|
|
34221
|
+
const context = payload;
|
|
34222
|
+
const settings = context["collaboration_mode"]?.["settings"];
|
|
34223
|
+
return {
|
|
34224
|
+
model: displayValue(context["model"] ?? settings?.["model"], 120),
|
|
34225
|
+
effort: displayValue(context["effort"] ?? settings?.["reasoning_effort"], 40)
|
|
34226
|
+
};
|
|
34227
|
+
}
|
|
34228
|
+
var codexCatalogCache = /* @__PURE__ */ new Map();
|
|
34229
|
+
async function loadCodexModelCatalog(command, prefixArgs, env) {
|
|
34230
|
+
const output = await new Promise((resolve13) => {
|
|
34231
|
+
const child = spawnCli(command, [...prefixArgs, "debug", "models"], {
|
|
34232
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
34233
|
+
windowsHide: true,
|
|
34234
|
+
env
|
|
34235
|
+
});
|
|
34236
|
+
let text = "";
|
|
34237
|
+
child.stdout?.on("data", (chunk) => {
|
|
34238
|
+
if (text.length < CATALOG_OUTPUT_LIMIT_BYTES) text += chunk.toString("utf8");
|
|
34239
|
+
});
|
|
34240
|
+
let settled = false;
|
|
34241
|
+
const finish = (value) => {
|
|
34242
|
+
if (settled) return;
|
|
34243
|
+
settled = true;
|
|
34244
|
+
clearTimeout(timer);
|
|
34245
|
+
resolve13(value);
|
|
34246
|
+
};
|
|
34247
|
+
const timer = setTimeout(() => {
|
|
34248
|
+
child.kill();
|
|
34249
|
+
finish("");
|
|
34250
|
+
}, CATALOG_TIMEOUT_MS);
|
|
34251
|
+
timer.unref?.();
|
|
34252
|
+
child.on("error", () => finish(""));
|
|
34253
|
+
child.on("close", () => finish(text));
|
|
34254
|
+
});
|
|
34255
|
+
const defaults = /* @__PURE__ */ new Map();
|
|
34256
|
+
try {
|
|
34257
|
+
const catalog2 = JSON.parse(output);
|
|
34258
|
+
for (const model of catalog2.models ?? []) {
|
|
34259
|
+
const slug = displayValue(model["slug"], 120);
|
|
34260
|
+
const effort = displayValue(model["default_reasoning_level"], 40);
|
|
34261
|
+
if (slug && effort) defaults.set(slug, effort);
|
|
34262
|
+
}
|
|
34263
|
+
} catch {
|
|
34264
|
+
}
|
|
34265
|
+
return defaults;
|
|
34266
|
+
}
|
|
34267
|
+
async function codexModelDefaultEffort(input) {
|
|
34268
|
+
const prefixArgs = input.commandPrefixArgs ?? [];
|
|
34269
|
+
const cacheKey = [input.command, ...prefixArgs].join(" ");
|
|
34270
|
+
let catalog2 = codexCatalogCache.get(cacheKey);
|
|
34271
|
+
if (!catalog2) {
|
|
34272
|
+
catalog2 = loadCodexModelCatalog(input.command, prefixArgs, input.env);
|
|
34273
|
+
codexCatalogCache.set(cacheKey, catalog2);
|
|
34274
|
+
}
|
|
34275
|
+
return (await catalog2).get(input.model) ?? null;
|
|
34276
|
+
}
|
|
34277
|
+
|
|
34278
|
+
// src/runners/claude-code.ts
|
|
34048
34279
|
var CLAUDE_SESSION_CRON_TOOLS = ["CronCreate", "CronList", "CronDelete"];
|
|
34049
34280
|
var CLAUDE_REINVOCATION_TOOLS = ["Monitor", "ScheduleWakeup"];
|
|
34050
34281
|
var SESSION_CONFLICT = /session/i;
|
|
@@ -34069,13 +34300,14 @@ var claudeCodeAdapter = {
|
|
|
34069
34300
|
input.cwd
|
|
34070
34301
|
);
|
|
34071
34302
|
const sessionId = input.task.spec.sessionKey ?? randomUUID11();
|
|
34303
|
+
const observeRuntime = createRuntimeReporter(input, sessionId);
|
|
34072
34304
|
return {
|
|
34073
34305
|
argsFor: (mode) => [
|
|
34074
34306
|
...baseArgs,
|
|
34075
34307
|
...mode === "resume" ? ["--resume", sessionId] : ["--session-id", sessionId]
|
|
34076
34308
|
],
|
|
34077
34309
|
prompt: buildRunnerPrompt(input.task),
|
|
34078
|
-
createParser: createStreamParser,
|
|
34310
|
+
createParser: (onStream) => createStreamParser(onStream, { onSessionModel: observeRuntime }),
|
|
34079
34311
|
// Self-heal both directions: a crashed first run leaves a transcript
|
|
34080
34312
|
// (new → conflict), a lost workspace breaks resume (resume → not
|
|
34081
34313
|
// found). One flip covers both.
|
|
@@ -34086,6 +34318,36 @@ var claudeCodeAdapter = {
|
|
|
34086
34318
|
function createClaudeCodeRunner(opts = {}) {
|
|
34087
34319
|
return createCliRunner(claudeCodeAdapter, opts);
|
|
34088
34320
|
}
|
|
34321
|
+
function createRuntimeReporter(input, sessionId) {
|
|
34322
|
+
const { task, runner, env, cwd } = input;
|
|
34323
|
+
const chosenEffort = runner.effort ?? null;
|
|
34324
|
+
let reported = false;
|
|
34325
|
+
return (model) => {
|
|
34326
|
+
if (reported) return;
|
|
34327
|
+
reported = true;
|
|
34328
|
+
task.runnerRuntime?.({ type: "claude-code", model, effort: chosenEffort });
|
|
34329
|
+
if (chosenEffort) return;
|
|
34330
|
+
void (async () => {
|
|
34331
|
+
for (let attempt = 0; attempt < EFFORT_READ_ATTEMPTS; attempt += 1) {
|
|
34332
|
+
await delay2(EFFORT_READ_INTERVAL_MS);
|
|
34333
|
+
if (task.cancelledNow()) return;
|
|
34334
|
+
const effort = await readClaudeSessionEffort({ env, cwd, sessionId }).catch(() => null);
|
|
34335
|
+
if (effort) {
|
|
34336
|
+
task.runnerRuntime?.({ type: "claude-code", model, effort });
|
|
34337
|
+
return;
|
|
34338
|
+
}
|
|
34339
|
+
}
|
|
34340
|
+
})();
|
|
34341
|
+
};
|
|
34342
|
+
}
|
|
34343
|
+
var EFFORT_READ_ATTEMPTS = 5;
|
|
34344
|
+
var EFFORT_READ_INTERVAL_MS = 3e3;
|
|
34345
|
+
function delay2(ms) {
|
|
34346
|
+
return new Promise((resolve13) => {
|
|
34347
|
+
const timer = setTimeout(resolve13, ms);
|
|
34348
|
+
timer.unref?.();
|
|
34349
|
+
});
|
|
34350
|
+
}
|
|
34089
34351
|
async function buildArgs(task, runner, artifacts, mcp, preparedWorkspace, taskRoot, workspacePath = taskRoot ?? "") {
|
|
34090
34352
|
const args = [
|
|
34091
34353
|
// Hermetic, non-interactive run — no local slash commands or global state.
|
|
@@ -34131,7 +34393,7 @@ async function buildArgs(task, runner, artifacts, mcp, preparedWorkspace, taskRo
|
|
|
34131
34393
|
);
|
|
34132
34394
|
return args;
|
|
34133
34395
|
}
|
|
34134
|
-
function createStreamParser(onStream) {
|
|
34396
|
+
function createStreamParser(onStream, hooks = {}) {
|
|
34135
34397
|
let buffer = "";
|
|
34136
34398
|
let responseText = "";
|
|
34137
34399
|
let inputTokens = 0;
|
|
@@ -34156,7 +34418,9 @@ function createStreamParser(onStream) {
|
|
|
34156
34418
|
return void 0;
|
|
34157
34419
|
}
|
|
34158
34420
|
const type = json2["type"];
|
|
34159
|
-
if (type === "
|
|
34421
|
+
if (type === "system" && json2["subtype"] === "init") {
|
|
34422
|
+
if (typeof json2["model"] === "string" && json2["model"]) hooks.onSessionModel?.(json2["model"]);
|
|
34423
|
+
} else if (type === "assistant") {
|
|
34160
34424
|
const message = json2["message"];
|
|
34161
34425
|
applyUsage(message?.["usage"]);
|
|
34162
34426
|
const content = message?.["content"] ?? [];
|
|
@@ -34271,16 +34535,16 @@ function improveErrorMessage(error52) {
|
|
|
34271
34535
|
|
|
34272
34536
|
// src/runners/codex.ts
|
|
34273
34537
|
import { mkdir as mkdir11, readFile as readFile7, writeFile as writeFile6 } from "node:fs/promises";
|
|
34274
|
-
import { homedir as
|
|
34275
|
-
import { join as
|
|
34538
|
+
import { homedir as homedir6 } from "node:os";
|
|
34539
|
+
import { join as join16 } from "node:path";
|
|
34276
34540
|
var CODEX_NOT_FOUND_MESSAGE = "The `codex` CLI was not found on this Machine. Install it (npm install -g @openai/codex) and sign in with `codex login`, or switch the agent to API-key auth.";
|
|
34277
34541
|
function defaultCodexThreadIndexRoot() {
|
|
34278
|
-
return
|
|
34542
|
+
return join16(homedir6(), ".zixt", "codex-threads");
|
|
34279
34543
|
}
|
|
34280
34544
|
var SAFE_SEGMENT3 = /^[A-Za-z0-9_-]{1,200}$/;
|
|
34281
34545
|
function threadIndexPath(root, agentId, sessionKey) {
|
|
34282
34546
|
if (!SAFE_SEGMENT3.test(agentId) || !SAFE_SEGMENT3.test(sessionKey)) return null;
|
|
34283
|
-
return
|
|
34547
|
+
return join16(root, agentId, `${sessionKey}.json`);
|
|
34284
34548
|
}
|
|
34285
34549
|
async function readThreadId(path) {
|
|
34286
34550
|
try {
|
|
@@ -34370,9 +34634,12 @@ ${buildRunnerPrompt(task)}` : buildRunnerPrompt(task);
|
|
|
34370
34634
|
const recordedThreadId = indexPath ? await readThreadId(indexPath) : null;
|
|
34371
34635
|
const rememberThread = (threadId) => {
|
|
34372
34636
|
if (!indexPath) return;
|
|
34373
|
-
void mkdir11(
|
|
34637
|
+
void mkdir11(join16(threadIndexRoot, task.agentId), { recursive: true }).then(() => writeFile6(indexPath, JSON.stringify({ threadId }), "utf8")).catch(() => {
|
|
34374
34638
|
});
|
|
34375
34639
|
};
|
|
34640
|
+
const observeRuntime = (threadId) => {
|
|
34641
|
+
void reportCodexRuntime({ input, threadId });
|
|
34642
|
+
};
|
|
34376
34643
|
return {
|
|
34377
34644
|
argsFor: (mode) => {
|
|
34378
34645
|
if (mode === "resume") {
|
|
@@ -34382,7 +34649,12 @@ ${buildRunnerPrompt(task)}` : buildRunnerPrompt(task);
|
|
|
34382
34649
|
},
|
|
34383
34650
|
prompt,
|
|
34384
34651
|
env,
|
|
34385
|
-
createParser: (onStream) => createCodexStreamParser(onStream, {
|
|
34652
|
+
createParser: (onStream) => createCodexStreamParser(onStream, {
|
|
34653
|
+
onThreadStarted: (threadId) => {
|
|
34654
|
+
rememberThread(threadId);
|
|
34655
|
+
observeRuntime(threadId);
|
|
34656
|
+
}
|
|
34657
|
+
}),
|
|
34386
34658
|
// Only one flip is meaningful: a resume whose rollout vanished starts
|
|
34387
34659
|
// over. Codex assigns new-session ids itself, so a new session can
|
|
34388
34660
|
// never collide the way a pre-specified id can.
|
|
@@ -34391,6 +34663,30 @@ ${buildRunnerPrompt(task)}` : buildRunnerPrompt(task);
|
|
|
34391
34663
|
}
|
|
34392
34664
|
};
|
|
34393
34665
|
}
|
|
34666
|
+
var RUNTIME_READ_ATTEMPTS = 5;
|
|
34667
|
+
var RUNTIME_READ_INTERVAL_MS = 2e3;
|
|
34668
|
+
function delay3(ms) {
|
|
34669
|
+
return new Promise((resolve13) => {
|
|
34670
|
+
const timer = setTimeout(resolve13, ms);
|
|
34671
|
+
timer.unref?.();
|
|
34672
|
+
});
|
|
34673
|
+
}
|
|
34674
|
+
async function reportCodexRuntime(args) {
|
|
34675
|
+
const { task, runner, env, command, commandPrefixArgs } = args.input;
|
|
34676
|
+
for (let attempt = 0; attempt < RUNTIME_READ_ATTEMPTS; attempt += 1) {
|
|
34677
|
+
if (task.cancelledNow()) return;
|
|
34678
|
+
const observed = await readCodexSessionRuntime({ env, threadId: args.threadId }).catch(
|
|
34679
|
+
() => null
|
|
34680
|
+
);
|
|
34681
|
+
const model = observed?.model ?? runner.model ?? null;
|
|
34682
|
+
if (model) {
|
|
34683
|
+
const effort = observed?.effort ?? runner.effort ?? (command ? await codexModelDefaultEffort({ command, commandPrefixArgs, env, model }) : null);
|
|
34684
|
+
task.runnerRuntime?.({ type: "codex", model, effort });
|
|
34685
|
+
return;
|
|
34686
|
+
}
|
|
34687
|
+
await delay3(RUNTIME_READ_INTERVAL_MS);
|
|
34688
|
+
}
|
|
34689
|
+
}
|
|
34394
34690
|
function createCodexRunner(opts = {}) {
|
|
34395
34691
|
const adapter = createCodexAdapter(opts.threadIndexRoot ?? defaultCodexThreadIndexRoot());
|
|
34396
34692
|
return createCliRunner(adapter, opts);
|
|
@@ -34540,7 +34836,7 @@ function improveCodexErrorMessage(error52) {
|
|
|
34540
34836
|
|
|
34541
34837
|
// src/runners/git-preflight.ts
|
|
34542
34838
|
import { spawn as spawn9 } from "node:child_process";
|
|
34543
|
-
import { realpath as
|
|
34839
|
+
import { realpath as realpath9 } from "node:fs/promises";
|
|
34544
34840
|
import { isAbsolute as isAbsolute13, resolve as resolve7 } from "node:path";
|
|
34545
34841
|
var OUTPUT_LIMIT = 8192;
|
|
34546
34842
|
var DEFAULT_TIMEOUT_MS4 = 1e4;
|
|
@@ -34560,7 +34856,7 @@ async function preflightGit(options = {}) {
|
|
|
34560
34856
|
if (configured !== void 0 && !isAbsolute13(configured)) {
|
|
34561
34857
|
return unavailable("configured git command must be an absolute file", checkedAt);
|
|
34562
34858
|
}
|
|
34563
|
-
const trustedCwd = await
|
|
34859
|
+
const trustedCwd = await realpath9(resolve7(options.trustedCwd ?? process.cwd())).catch(() => null);
|
|
34564
34860
|
if (!trustedCwd)
|
|
34565
34861
|
return unavailable("Host-owned git preflight directory is unavailable", checkedAt);
|
|
34566
34862
|
const executablePath = await resolveTrustedCliCommand(configured ?? "git", {
|
|
@@ -34812,9 +35108,9 @@ function run2(command, args) {
|
|
|
34812
35108
|
// src/linux-service.ts
|
|
34813
35109
|
import { spawn as spawn10 } from "node:child_process";
|
|
34814
35110
|
import { constants as constants2 } from "node:fs";
|
|
34815
|
-
import { access as access4, chmod as chmod7, mkdir as mkdir12, open as
|
|
34816
|
-
import { homedir as
|
|
34817
|
-
import { basename as basename3, dirname as dirname7, join as
|
|
35111
|
+
import { access as access4, chmod as chmod7, mkdir as mkdir12, open as open6, rename as rename5, rm as rm7 } from "node:fs/promises";
|
|
35112
|
+
import { homedir as homedir7, userInfo } from "node:os";
|
|
35113
|
+
import { basename as basename3, dirname as dirname7, join as join17, relative as relative8, resolve as resolve8, sep as sep4 } from "node:path";
|
|
34818
35114
|
var SERVICE_NAME = "zixt-host.service";
|
|
34819
35115
|
var SYSTEM_SERVICE_COMMAND_TIMEOUT_MS = 7e4;
|
|
34820
35116
|
var SERVICE_STABILITY_DELAY_MS = 2e3;
|
|
@@ -34902,7 +35198,7 @@ function systemdDirectiveValue(value) {
|
|
|
34902
35198
|
return systemdQuotedValue(value, false);
|
|
34903
35199
|
}
|
|
34904
35200
|
async function defaultSyncDirectory(path) {
|
|
34905
|
-
const directory = await
|
|
35201
|
+
const directory = await open6(path, "r");
|
|
34906
35202
|
try {
|
|
34907
35203
|
await directory.sync();
|
|
34908
35204
|
} finally {
|
|
@@ -34919,14 +35215,14 @@ async function ensureDirectory(path, mode, syncDirectory7) {
|
|
|
34919
35215
|
const descendants = relative8(first, target);
|
|
34920
35216
|
for (const part of descendants ? descendants.split(sep4) : []) {
|
|
34921
35217
|
await syncDirectory7(current);
|
|
34922
|
-
current =
|
|
35218
|
+
current = join17(current, part);
|
|
34923
35219
|
}
|
|
34924
35220
|
}
|
|
34925
35221
|
async function replacePrivateFile(path, contents, mode, syncDirectory7) {
|
|
34926
35222
|
const parent = dirname7(path);
|
|
34927
35223
|
await ensureDirectory(parent, 448, syncDirectory7);
|
|
34928
|
-
const temporary =
|
|
34929
|
-
const handle = await
|
|
35224
|
+
const temporary = join17(parent, `.${basename3(path)}.${process.pid}.${crypto.randomUUID()}.tmp`);
|
|
35225
|
+
const handle = await open6(temporary, "wx", mode);
|
|
34930
35226
|
try {
|
|
34931
35227
|
await handle.writeFile(contents, "utf8");
|
|
34932
35228
|
await handle.sync();
|
|
@@ -34969,7 +35265,7 @@ async function installLinuxService(options) {
|
|
|
34969
35265
|
throw new Error("Linux automatic startup is available only on Linux.");
|
|
34970
35266
|
}
|
|
34971
35267
|
const env = options.env ?? process.env;
|
|
34972
|
-
const home = options.home ??
|
|
35268
|
+
const home = options.home ?? homedir7();
|
|
34973
35269
|
const username = oneLine(options.username ?? userInfo().username, "user name");
|
|
34974
35270
|
const token2 = oneLine(options.token, "pairing code");
|
|
34975
35271
|
const path = oneLine(
|
|
@@ -34977,11 +35273,11 @@ async function installLinuxService(options) {
|
|
|
34977
35273
|
"command search path"
|
|
34978
35274
|
);
|
|
34979
35275
|
const cloudUrl = options.cloudUrl ? oneLine(options.cloudUrl, "Zixt Cloud address") : void 0;
|
|
34980
|
-
const xdgConfigHome = env.XDG_CONFIG_HOME ? oneLine(env.XDG_CONFIG_HOME, "Linux configuration path") :
|
|
34981
|
-
const configRoot = options.serviceConfigRoot ??
|
|
34982
|
-
const unitRoot = options.userUnitRoot ??
|
|
34983
|
-
const environmentPath =
|
|
34984
|
-
const unitPath =
|
|
35276
|
+
const xdgConfigHome = env.XDG_CONFIG_HOME ? oneLine(env.XDG_CONFIG_HOME, "Linux configuration path") : join17(home, ".config");
|
|
35277
|
+
const configRoot = options.serviceConfigRoot ?? join17(xdgConfigHome, "zixt");
|
|
35278
|
+
const unitRoot = options.userUnitRoot ?? join17(xdgConfigHome, "systemd", "user");
|
|
35279
|
+
const environmentPath = join17(configRoot, "host.env");
|
|
35280
|
+
const unitPath = join17(unitRoot, SERVICE_NAME);
|
|
34985
35281
|
const installVersion = options.installVersion ?? installRelease;
|
|
34986
35282
|
const activateVersion = options.activateVersion ?? (options.installVersion ? async (entry) => entry : activateInstalledRelease);
|
|
34987
35283
|
const resolveCommand = options.resolveCommand ?? defaultResolveCommand;
|
|
@@ -35100,9 +35396,9 @@ async function installLinuxService(options) {
|
|
|
35100
35396
|
// src/macos-service.ts
|
|
35101
35397
|
import { spawn as spawn11 } from "node:child_process";
|
|
35102
35398
|
import { constants as constants3 } from "node:fs";
|
|
35103
|
-
import { access as access5, chmod as chmod8, mkdir as mkdir13, open as
|
|
35104
|
-
import { homedir as
|
|
35105
|
-
import { basename as basename4, dirname as dirname8, join as
|
|
35399
|
+
import { access as access5, chmod as chmod8, mkdir as mkdir13, open as open7, rename as rename6, rm as rm8 } from "node:fs/promises";
|
|
35400
|
+
import { homedir as homedir8, userInfo as userInfo2 } from "node:os";
|
|
35401
|
+
import { basename as basename4, dirname as dirname8, join as join18, relative as relative9, resolve as resolve9, sep as sep5 } from "node:path";
|
|
35106
35402
|
var LAUNCH_AGENT_LABEL = "ai.zixt.host";
|
|
35107
35403
|
var SERVICE_STABILITY_DELAY_MS2 = 2e3;
|
|
35108
35404
|
var COMMAND_TIMEOUT_MS2 = 7e4;
|
|
@@ -35118,7 +35414,7 @@ function shellValue(value) {
|
|
|
35118
35414
|
return `'${oneLine2(value, "service setting").replaceAll("'", `'"'"'`)}'`;
|
|
35119
35415
|
}
|
|
35120
35416
|
async function syncDirectory4(path) {
|
|
35121
|
-
const directory = await
|
|
35417
|
+
const directory = await open7(path, "r");
|
|
35122
35418
|
try {
|
|
35123
35419
|
await directory.sync();
|
|
35124
35420
|
} finally {
|
|
@@ -35134,14 +35430,14 @@ async function ensureDirectory2(path, sync) {
|
|
|
35134
35430
|
let current = first;
|
|
35135
35431
|
for (const part of relative9(first, target).split(sep5).filter(Boolean)) {
|
|
35136
35432
|
await sync(current);
|
|
35137
|
-
current =
|
|
35433
|
+
current = join18(current, part);
|
|
35138
35434
|
}
|
|
35139
35435
|
}
|
|
35140
35436
|
async function replacePrivateFile2(path, contents, mode, sync) {
|
|
35141
35437
|
const parent = dirname8(path);
|
|
35142
35438
|
await ensureDirectory2(parent, sync);
|
|
35143
|
-
const temporary =
|
|
35144
|
-
const handle = await
|
|
35439
|
+
const temporary = join18(parent, `.${basename4(path)}.${process.pid}.${crypto.randomUUID()}.tmp`);
|
|
35440
|
+
const handle = await open7(temporary, "wx", mode);
|
|
35145
35441
|
try {
|
|
35146
35442
|
await handle.writeFile(contents, "utf8");
|
|
35147
35443
|
await handle.sync();
|
|
@@ -35228,7 +35524,7 @@ async function installMacosService(options) {
|
|
|
35228
35524
|
throw new Error("macOS automatic startup is available only on macOS.");
|
|
35229
35525
|
}
|
|
35230
35526
|
const env = options.env ?? process.env;
|
|
35231
|
-
const home = options.home ??
|
|
35527
|
+
const home = options.home ?? homedir8();
|
|
35232
35528
|
const uid = options.uid ?? userInfo2().uid;
|
|
35233
35529
|
if (!Number.isSafeInteger(uid) || uid < 0) throw new Error("macOS user id is invalid.");
|
|
35234
35530
|
const token2 = oneLine2(options.token, "pairing code");
|
|
@@ -35237,20 +35533,20 @@ async function installMacosService(options) {
|
|
|
35237
35533
|
options.path ?? env.PATH ?? "/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin",
|
|
35238
35534
|
"command search path"
|
|
35239
35535
|
);
|
|
35240
|
-
const configRoot = options.configRoot ??
|
|
35241
|
-
const launchAgentsRoot = options.launchAgentsRoot ??
|
|
35242
|
-
const logRoot = options.logRoot ??
|
|
35243
|
-
const configPath =
|
|
35244
|
-
const launcherPath =
|
|
35245
|
-
const plistPath =
|
|
35246
|
-
const stdoutPath =
|
|
35247
|
-
const stderrPath =
|
|
35536
|
+
const configRoot = options.configRoot ?? join18(home, "Library", "Application Support", "Zixt");
|
|
35537
|
+
const launchAgentsRoot = options.launchAgentsRoot ?? join18(home, "Library", "LaunchAgents");
|
|
35538
|
+
const logRoot = options.logRoot ?? join18(home, "Library", "Logs", "Zixt");
|
|
35539
|
+
const configPath = join18(configRoot, "host.env");
|
|
35540
|
+
const launcherPath = join18(configRoot, "host-launcher.sh");
|
|
35541
|
+
const plistPath = join18(launchAgentsRoot, `${LAUNCH_AGENT_LABEL}.plist`);
|
|
35542
|
+
const stdoutPath = join18(logRoot, "host.log");
|
|
35543
|
+
const stderrPath = join18(logRoot, "host-error.log");
|
|
35248
35544
|
const installVersion = options.installVersion ?? installRelease;
|
|
35249
35545
|
const activateVersion = options.activateVersion ?? (options.installVersion ? async (entry) => entry : (entry) => activateInstalledRelease(entry));
|
|
35250
35546
|
const resolveCommand = options.resolveCommand ?? (async () => defaultResolveCommand2());
|
|
35251
35547
|
const run3 = options.runCommand ?? ((command, args) => defaultRunCommand2(command, args, env));
|
|
35252
35548
|
const sync = options.syncDirectory ?? syncDirectory4;
|
|
35253
|
-
const
|
|
35549
|
+
const delay4 = options.delay ?? ((ms) => new Promise((done) => setTimeout(done, ms)));
|
|
35254
35550
|
const launchctl = await resolveCommand("launchctl");
|
|
35255
35551
|
if (!launchctl) throw new Error("macOS launchctl could not be found.");
|
|
35256
35552
|
const releaseEntry = await installVersion(options.hostVersion);
|
|
@@ -35312,7 +35608,7 @@ async function installMacosService(options) {
|
|
|
35312
35608
|
return status;
|
|
35313
35609
|
};
|
|
35314
35610
|
const first = await observe();
|
|
35315
|
-
await
|
|
35611
|
+
await delay4(options.serviceStabilityDelayMs ?? SERVICE_STABILITY_DELAY_MS2);
|
|
35316
35612
|
const second = await observe();
|
|
35317
35613
|
if (first.pid !== second.pid || first.runs !== second.runs) {
|
|
35318
35614
|
throw new Error("Zixt started but restarted during its readiness check.");
|
|
@@ -35331,9 +35627,9 @@ async function installMacosService(options) {
|
|
|
35331
35627
|
// src/windows-service.ts
|
|
35332
35628
|
import { spawn as spawn12 } from "node:child_process";
|
|
35333
35629
|
import { constants as constants4 } from "node:fs";
|
|
35334
|
-
import { access as access6, mkdir as mkdir14, open as
|
|
35335
|
-
import { homedir as
|
|
35336
|
-
import { basename as basename5, dirname as dirname9, isAbsolute as isAbsolute14, join as
|
|
35630
|
+
import { access as access6, mkdir as mkdir14, open as open8, readFile as readFile8, rename as rename7, rm as rm9 } from "node:fs/promises";
|
|
35631
|
+
import { homedir as homedir9 } from "node:os";
|
|
35632
|
+
import { basename as basename5, dirname as dirname9, isAbsolute as isAbsolute14, join as join19, relative as relative10, resolve as resolve10, sep as sep6 } from "node:path";
|
|
35337
35633
|
var TASK_NAME = "Zixt Host";
|
|
35338
35634
|
var COMMAND_TIMEOUT_MS3 = 7e4;
|
|
35339
35635
|
var SERVICE_STABILITY_DELAY_MS3 = 2e3;
|
|
@@ -35351,7 +35647,7 @@ function psLiteral(value) {
|
|
|
35351
35647
|
}
|
|
35352
35648
|
async function syncDirectory5(path) {
|
|
35353
35649
|
if (process.platform === "win32") return;
|
|
35354
|
-
const directory = await
|
|
35650
|
+
const directory = await open8(path, "r");
|
|
35355
35651
|
try {
|
|
35356
35652
|
await directory.sync();
|
|
35357
35653
|
} finally {
|
|
@@ -35367,14 +35663,14 @@ async function ensureDirectory3(path, sync) {
|
|
|
35367
35663
|
let current = first;
|
|
35368
35664
|
for (const part of relative10(first, target).split(sep6).filter(Boolean)) {
|
|
35369
35665
|
await sync(current);
|
|
35370
|
-
current =
|
|
35666
|
+
current = join19(current, part);
|
|
35371
35667
|
}
|
|
35372
35668
|
}
|
|
35373
35669
|
async function replacePrivateFile3(path, contents, sync) {
|
|
35374
35670
|
const parent = dirname9(path);
|
|
35375
35671
|
await ensureDirectory3(parent, sync);
|
|
35376
|
-
const temporary =
|
|
35377
|
-
const handle = await
|
|
35672
|
+
const temporary = join19(parent, `.${basename5(path)}.${process.pid}.${crypto.randomUUID()}.tmp`);
|
|
35673
|
+
const handle = await open8(temporary, "wx", 384);
|
|
35378
35674
|
try {
|
|
35379
35675
|
await handle.writeFile(contents, "utf8");
|
|
35380
35676
|
await handle.sync();
|
|
@@ -35428,7 +35724,7 @@ async function runChild(command, args, env, input) {
|
|
|
35428
35724
|
async function defaultResolveCommand3(name, env) {
|
|
35429
35725
|
const root = env.SYSTEMROOT ?? env.WINDIR;
|
|
35430
35726
|
if (!root || !isAbsolute14(root)) return null;
|
|
35431
|
-
const candidate = name === "powershell" ?
|
|
35727
|
+
const candidate = name === "powershell" ? join19(root, "System32", "WindowsPowerShell", "v1.0", "powershell.exe") : join19(root, "System32", `${name}.exe`);
|
|
35432
35728
|
return access6(candidate, constants4.X_OK).then(
|
|
35433
35729
|
() => candidate,
|
|
35434
35730
|
() => null
|
|
@@ -35557,7 +35853,7 @@ async function installWindowsService(options) {
|
|
|
35557
35853
|
throw new Error("Windows automatic startup is available only on Windows.");
|
|
35558
35854
|
}
|
|
35559
35855
|
const env = options.env ?? process.env;
|
|
35560
|
-
const home = options.home ??
|
|
35856
|
+
const home = options.home ?? homedir9();
|
|
35561
35857
|
const localAppData = options.localAppData ?? env.LOCALAPPDATA;
|
|
35562
35858
|
if (!localAppData || !isAbsolute14(localAppData)) {
|
|
35563
35859
|
throw new Error("Windows local application data path is unavailable.");
|
|
@@ -35565,17 +35861,17 @@ async function installWindowsService(options) {
|
|
|
35565
35861
|
const token2 = oneLine3(options.token, "pairing code");
|
|
35566
35862
|
const cloudUrl = options.cloudUrl ? oneLine3(options.cloudUrl, "Zixt Cloud address") : void 0;
|
|
35567
35863
|
const path = oneLine3(options.path ?? env.PATH ?? "", "command search path");
|
|
35568
|
-
const configRoot = options.configRoot ??
|
|
35569
|
-
const configPath =
|
|
35570
|
-
const launcherPath =
|
|
35571
|
-
const taskXmlPath =
|
|
35572
|
-
const statusPath =
|
|
35864
|
+
const configRoot = options.configRoot ?? join19(localAppData, "Zixt", "Host");
|
|
35865
|
+
const configPath = join19(configRoot, "host.json");
|
|
35866
|
+
const launcherPath = join19(configRoot, "host-launcher.ps1");
|
|
35867
|
+
const taskXmlPath = join19(configRoot, "host-task.xml");
|
|
35868
|
+
const statusPath = join19(configRoot, "host-status.json");
|
|
35573
35869
|
const installVersion = options.installVersion ?? installRelease;
|
|
35574
35870
|
const activateVersion = options.activateVersion ?? (options.installVersion ? async (entry) => entry : (entry) => activateInstalledRelease(entry));
|
|
35575
35871
|
const resolveCommand = options.resolveCommand ?? ((name) => defaultResolveCommand3(name, env));
|
|
35576
35872
|
const run3 = options.runCommand ?? ((command, args) => runChild(command, args, env));
|
|
35577
35873
|
const sync = options.syncDirectory ?? syncDirectory5;
|
|
35578
|
-
const
|
|
35874
|
+
const delay4 = options.delay ?? ((ms) => new Promise((done) => setTimeout(done, ms)));
|
|
35579
35875
|
const observe = options.observeStatus ?? defaultObserveStatus;
|
|
35580
35876
|
const [powershell, schtasks, icacls] = await Promise.all([
|
|
35581
35877
|
resolveCommand("powershell"),
|
|
@@ -35638,10 +35934,10 @@ async function installWindowsService(options) {
|
|
|
35638
35934
|
do {
|
|
35639
35935
|
first = await observe(statusPath, generation);
|
|
35640
35936
|
if (first) break;
|
|
35641
|
-
await
|
|
35937
|
+
await delay4(50);
|
|
35642
35938
|
} while (Date.now() < deadline);
|
|
35643
35939
|
if (!first) throw new Error("Zixt started but did not reach a running process state.");
|
|
35644
|
-
await
|
|
35940
|
+
await delay4(options.serviceStabilityDelayMs ?? SERVICE_STABILITY_DELAY_MS3);
|
|
35645
35941
|
const second = await observe(statusPath, generation);
|
|
35646
35942
|
if (!second || first.pid !== second.pid) {
|
|
35647
35943
|
throw new Error("Zixt started but restarted during its readiness check.");
|
|
@@ -35670,27 +35966,27 @@ async function installSystemService(options) {
|
|
|
35670
35966
|
}
|
|
35671
35967
|
|
|
35672
35968
|
// src/terminal-outcomes.ts
|
|
35673
|
-
import { chmod as chmod9, lstat as lstat10, mkdir as mkdir15, open as
|
|
35674
|
-
import { homedir as
|
|
35675
|
-
import { dirname as dirname10, join as
|
|
35969
|
+
import { chmod as chmod9, lstat as lstat10, mkdir as mkdir15, open as open9, readdir as readdir5, readFile as readFile9, rename as rename8, rm as rm10 } from "node:fs/promises";
|
|
35970
|
+
import { homedir as homedir10 } from "node:os";
|
|
35971
|
+
import { dirname as dirname10, join as join20, relative as relative11, resolve as resolve11, sep as sep7 } from "node:path";
|
|
35676
35972
|
var DIRECTORY_MODE5 = 448;
|
|
35677
35973
|
var FILE_MODE4 = 384;
|
|
35678
35974
|
var MAX_OUTCOME_BYTES = 4 * 1024 * 1024;
|
|
35679
35975
|
var HOST_DIRECTORY = /^hst_[0-9a-f]{32}$/;
|
|
35680
35976
|
var OUTCOME_FILE = /^(tsk_[0-9a-f]{32})\.([1-9][0-9]*)\.json$/;
|
|
35681
35977
|
function defaultTerminalOutcomeRoot() {
|
|
35682
|
-
return
|
|
35978
|
+
return join20(homedir10(), ".zixt", "terminal-outcomes");
|
|
35683
35979
|
}
|
|
35684
35980
|
function hostOutcomeRoot(root, hostId) {
|
|
35685
35981
|
if (!HOST_DIRECTORY.test(hostId)) throw new Error("terminal outcome Host identity is malformed");
|
|
35686
|
-
return
|
|
35982
|
+
return join20(root, hostId);
|
|
35687
35983
|
}
|
|
35688
35984
|
function outcomePath(root, hostId, taskId, epoch) {
|
|
35689
|
-
return
|
|
35985
|
+
return join20(hostOutcomeRoot(root, hostId), `${taskId}.${epoch}.json`);
|
|
35690
35986
|
}
|
|
35691
35987
|
async function syncDirectory6(root) {
|
|
35692
35988
|
if (process.platform === "win32") return;
|
|
35693
|
-
const handle = await
|
|
35989
|
+
const handle = await open9(root, "r");
|
|
35694
35990
|
try {
|
|
35695
35991
|
await handle.sync();
|
|
35696
35992
|
} finally {
|
|
@@ -35706,7 +36002,7 @@ async function requirePrivateRoot(root, sync = syncDirectory6) {
|
|
|
35706
36002
|
let current = first;
|
|
35707
36003
|
for (const part of relative11(first, target).split(sep7).filter(Boolean)) {
|
|
35708
36004
|
await sync(current);
|
|
35709
|
-
current =
|
|
36005
|
+
current = join20(current, part);
|
|
35710
36006
|
}
|
|
35711
36007
|
}
|
|
35712
36008
|
const stat3 = await lstat10(root);
|
|
@@ -35746,13 +36042,13 @@ async function recordTerminalOutcome(hostId, input, root = defaultTerminalOutcom
|
|
|
35746
36042
|
} catch (error52) {
|
|
35747
36043
|
if (error52.code !== "ENOENT") throw error52;
|
|
35748
36044
|
}
|
|
35749
|
-
const temporary =
|
|
36045
|
+
const temporary = join20(
|
|
35750
36046
|
scopedRoot,
|
|
35751
36047
|
`.${outcome.taskId}.${outcome.epoch}.${process.pid}.${Date.now()}.${outcome.resultId}.tmp`
|
|
35752
36048
|
);
|
|
35753
36049
|
let handle;
|
|
35754
36050
|
try {
|
|
35755
|
-
handle = await
|
|
36051
|
+
handle = await open9(temporary, "wx", FILE_MODE4);
|
|
35756
36052
|
await handle.writeFile(JSON.stringify(outcome), "utf8");
|
|
35757
36053
|
await handle.sync();
|
|
35758
36054
|
await handle.close();
|
|
@@ -35779,7 +36075,7 @@ async function readTerminalOutcomesStrict(root = defaultTerminalOutcomeRoot()) {
|
|
|
35779
36075
|
throw new Error("terminal outcome journal root is not a trusted directory");
|
|
35780
36076
|
}
|
|
35781
36077
|
await chmod9(root, DIRECTORY_MODE5);
|
|
35782
|
-
const hostEntries = await
|
|
36078
|
+
const hostEntries = await readdir5(root, { withFileTypes: true });
|
|
35783
36079
|
const outcomes = [];
|
|
35784
36080
|
const resultIds = /* @__PURE__ */ new Set();
|
|
35785
36081
|
for (const hostEntry of hostEntries) {
|
|
@@ -35792,14 +36088,14 @@ async function readTerminalOutcomesStrict(root = defaultTerminalOutcomeRoot()) {
|
|
|
35792
36088
|
throw new Error("terminal outcome Host scope is not a trusted directory");
|
|
35793
36089
|
}
|
|
35794
36090
|
await chmod9(scopedRoot, DIRECTORY_MODE5);
|
|
35795
|
-
const entries = await
|
|
36091
|
+
const entries = await readdir5(scopedRoot, { withFileTypes: true });
|
|
35796
36092
|
for (const entry of entries) {
|
|
35797
36093
|
if (!entry.name.endsWith(".json")) continue;
|
|
35798
36094
|
const match = OUTCOME_FILE.exec(entry.name);
|
|
35799
36095
|
if (!match || !entry.isFile() || entry.isSymbolicLink()) {
|
|
35800
36096
|
throw new Error("committed terminal outcome is not a trusted regular file");
|
|
35801
36097
|
}
|
|
35802
|
-
const path =
|
|
36098
|
+
const path = join20(scopedRoot, entry.name);
|
|
35803
36099
|
const stat3 = await lstat10(path);
|
|
35804
36100
|
if (!stat3.isFile() || stat3.isSymbolicLink() || stat3.size > MAX_OUTCOME_BYTES) {
|
|
35805
36101
|
throw new Error("committed terminal outcome is not a trusted regular file");
|
|
@@ -35952,7 +36248,7 @@ function createHostLogger(options = {}) {
|
|
|
35952
36248
|
}
|
|
35953
36249
|
|
|
35954
36250
|
// src/demo-state.ts
|
|
35955
|
-
import { isAbsolute as isAbsolute15, join as
|
|
36251
|
+
import { isAbsolute as isAbsolute15, join as join21, parse as parse3, resolve as resolve12 } from "node:path";
|
|
35956
36252
|
var DEMO_STATE_ROOT_ENV = "ZIXT_DEMO_STATE_ROOT";
|
|
35957
36253
|
function resolveDemoHostStatePaths(env = process.env) {
|
|
35958
36254
|
const configured = env.ZIXT_RUNNER === "demo" ? env[DEMO_STATE_ROOT_ENV] : void 0;
|
|
@@ -35962,12 +36258,12 @@ function resolveDemoHostStatePaths(env = process.env) {
|
|
|
35962
36258
|
throw new Error(`${DEMO_STATE_ROOT_ENV} must be a dedicated absolute directory`);
|
|
35963
36259
|
}
|
|
35964
36260
|
return {
|
|
35965
|
-
runRegistryRoot:
|
|
35966
|
-
terminalOutcomeRoot:
|
|
35967
|
-
runArtifactRoot:
|
|
35968
|
-
browserProfileRoot:
|
|
35969
|
-
runnerWorkspaceRoot:
|
|
35970
|
-
codexThreadIndexRoot:
|
|
36261
|
+
runRegistryRoot: join21(root, "run-registry"),
|
|
36262
|
+
terminalOutcomeRoot: join21(root, "terminal-outcomes"),
|
|
36263
|
+
runArtifactRoot: join21(root, "run-artifacts"),
|
|
36264
|
+
browserProfileRoot: join21(root, "browser-profiles"),
|
|
36265
|
+
runnerWorkspaceRoot: join21(root, "workspaces"),
|
|
36266
|
+
codexThreadIndexRoot: join21(root, "codex-threads")
|
|
35971
36267
|
};
|
|
35972
36268
|
}
|
|
35973
36269
|
|