@zixt/host 0.0.11 → 0.0.13
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 +385 -89
- 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.13",
|
|
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. */
|
|
@@ -17217,6 +17232,9 @@ var SecretMeta = external_exports.object({
|
|
|
17217
17232
|
/** Absent on legacy rows/clients means `env`. */
|
|
17218
17233
|
kind: SecretKind.default("env"),
|
|
17219
17234
|
scope: SecretScope,
|
|
17235
|
+
/** Canonical availability set for teammate-scoped credentials. */
|
|
17236
|
+
agentIds: external_exports.array(AgentId).min(1).nullable().default(null),
|
|
17237
|
+
/** Legacy single-teammate projection, retained while older clients migrate. */
|
|
17220
17238
|
agentId: AgentId.nullable(),
|
|
17221
17239
|
/**
|
|
17222
17240
|
* What this credential is for — rendered into the agent's system prompt
|
|
@@ -17264,7 +17282,23 @@ var requireKindMatchingValue = (request, ctx) => {
|
|
|
17264
17282
|
};
|
|
17265
17283
|
var PutSecretRequest = external_exports.discriminatedUnion("scope", [
|
|
17266
17284
|
external_exports.object({ ...PutSecretFields, scope: external_exports.literal("org") }).strict().superRefine(requireKindMatchingValue),
|
|
17267
|
-
external_exports.object({
|
|
17285
|
+
external_exports.object({
|
|
17286
|
+
...PutSecretFields,
|
|
17287
|
+
scope: external_exports.literal("agent"),
|
|
17288
|
+
agentIds: external_exports.array(AgentId).min(1).max(100).optional(),
|
|
17289
|
+
agentId: AgentId.optional()
|
|
17290
|
+
}).strict().superRefine((request, ctx) => {
|
|
17291
|
+
if (request.agentIds === void 0 === (request.agentId === void 0)) {
|
|
17292
|
+
ctx.addIssue({
|
|
17293
|
+
code: "custom",
|
|
17294
|
+
path: ["agentIds"],
|
|
17295
|
+
message: "teammate credentials require exactly one of agentIds or legacy agentId"
|
|
17296
|
+
});
|
|
17297
|
+
}
|
|
17298
|
+
if (request.agentIds && new Set(request.agentIds).size !== request.agentIds.length) {
|
|
17299
|
+
ctx.addIssue({ code: "custom", path: ["agentIds"], message: "duplicate AI teammate" });
|
|
17300
|
+
}
|
|
17301
|
+
}).superRefine(requireKindMatchingValue)
|
|
17268
17302
|
]);
|
|
17269
17303
|
var AdminSecretsProjection = external_exports.object({
|
|
17270
17304
|
metadataRedacted: external_exports.literal(false),
|
|
@@ -18320,6 +18354,12 @@ var TaskWorkingContextReport = external_exports.object({
|
|
|
18320
18354
|
epoch: external_exports.number().int().min(1),
|
|
18321
18355
|
context: TaskWorkingContextObservation
|
|
18322
18356
|
});
|
|
18357
|
+
var TaskRunnerRuntimeReport = external_exports.object({
|
|
18358
|
+
type: external_exports.literal("task.runner_runtime"),
|
|
18359
|
+
taskId: TaskId,
|
|
18360
|
+
epoch: external_exports.number().int().min(1),
|
|
18361
|
+
runtime: TaskRunnerRuntimeObservation
|
|
18362
|
+
});
|
|
18323
18363
|
var TaskTitle = external_exports.object({
|
|
18324
18364
|
type: external_exports.literal("task.title"),
|
|
18325
18365
|
taskId: TaskId,
|
|
@@ -18401,6 +18441,7 @@ var AgentOpRequest = external_exports.object({
|
|
|
18401
18441
|
var UpMessage = external_exports.discriminatedUnion("type", [
|
|
18402
18442
|
TaskEvent,
|
|
18403
18443
|
TaskWorkingContextReport,
|
|
18444
|
+
TaskRunnerRuntimeReport,
|
|
18404
18445
|
TaskTitle,
|
|
18405
18446
|
TaskResult,
|
|
18406
18447
|
HostReport,
|
|
@@ -18440,7 +18481,9 @@ var HelloAckFrame = external_exports.object({
|
|
|
18440
18481
|
features: external_exports.object({
|
|
18441
18482
|
browserSessions: external_exports.literal(true).optional(),
|
|
18442
18483
|
/** Host may send task.working_context only after this advertisement. */
|
|
18443
|
-
taskWorkingContext: external_exports.literal(true).optional()
|
|
18484
|
+
taskWorkingContext: external_exports.literal(true).optional(),
|
|
18485
|
+
/** Host may send task.runner_runtime only after this advertisement. */
|
|
18486
|
+
taskRunnerRuntime: external_exports.literal(true).optional()
|
|
18444
18487
|
}).optional(),
|
|
18445
18488
|
/**
|
|
18446
18489
|
* Workspace paths configured for this organization's teammates. The Machine
|
|
@@ -19382,6 +19425,7 @@ var HostClient = class _HostClient {
|
|
|
19382
19425
|
/** True only after the connected cloud's helloAck advertised the feature. */
|
|
19383
19426
|
browserFeatureActive = false;
|
|
19384
19427
|
workingContextFeatureActive = false;
|
|
19428
|
+
runnerRuntimeFeatureActive = false;
|
|
19385
19429
|
/** TCP open is not protocol authority; only the current helloAck opens upstream delivery. */
|
|
19386
19430
|
protocolReady = false;
|
|
19387
19431
|
/** Workspace paths this organization configured, as of the last hello/update. */
|
|
@@ -19419,6 +19463,7 @@ var HostClient = class _HostClient {
|
|
|
19419
19463
|
this.protocolReady = false;
|
|
19420
19464
|
this.browserFeatureActive = false;
|
|
19421
19465
|
this.workingContextFeatureActive = false;
|
|
19466
|
+
this.runnerRuntimeFeatureActive = false;
|
|
19422
19467
|
this.opts.browser?.attach(null);
|
|
19423
19468
|
await Promise.allSettled([
|
|
19424
19469
|
this.unwindAllRuns("host client stopped", "client_shutdown"),
|
|
@@ -19678,6 +19723,7 @@ var HostClient = class _HostClient {
|
|
|
19678
19723
|
if (this.ws !== void 0) return;
|
|
19679
19724
|
this.browserFeatureActive = false;
|
|
19680
19725
|
this.workingContextFeatureActive = false;
|
|
19726
|
+
this.runnerRuntimeFeatureActive = false;
|
|
19681
19727
|
this.opts.browser?.attach(null);
|
|
19682
19728
|
void this.opts.browser?.closeAll("machine_offline");
|
|
19683
19729
|
if (code === CLOSE_CODES.revoked) {
|
|
@@ -19706,13 +19752,13 @@ var HostClient = class _HostClient {
|
|
|
19706
19752
|
);
|
|
19707
19753
|
if (this.stopped) return;
|
|
19708
19754
|
if (!this.stopped && this.opts.reconnect !== false) {
|
|
19709
|
-
const
|
|
19755
|
+
const delay4 = reconnectBackoffDelay(this.reconnectFailures, this.opts.backoffMs);
|
|
19710
19756
|
this.reconnectFailures++;
|
|
19711
19757
|
if (this.reconnectTimer) clearTimeout(this.reconnectTimer);
|
|
19712
19758
|
this.reconnectTimer = setTimeout(() => {
|
|
19713
19759
|
this.reconnectTimer = void 0;
|
|
19714
19760
|
this.connect();
|
|
19715
|
-
},
|
|
19761
|
+
}, delay4);
|
|
19716
19762
|
} else {
|
|
19717
19763
|
this.stopped = true;
|
|
19718
19764
|
}
|
|
@@ -19743,7 +19789,7 @@ var HostClient = class _HostClient {
|
|
|
19743
19789
|
this.send(frame);
|
|
19744
19790
|
}
|
|
19745
19791
|
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;
|
|
19792
|
+
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
19793
|
}
|
|
19748
19794
|
supersedesPendingUp(candidate, incoming) {
|
|
19749
19795
|
if (candidate.type !== incoming.type) return false;
|
|
@@ -19754,7 +19800,7 @@ var HostClient = class _HostClient {
|
|
|
19754
19800
|
if (incoming.type === "agent.op") {
|
|
19755
19801
|
return candidate.type === "agent.op" && candidate.taskId === incoming.taskId && candidate.epoch === incoming.epoch && candidate.requestId === incoming.requestId;
|
|
19756
19802
|
}
|
|
19757
|
-
if (incoming.type === "task.working_context" || incoming.type === "task.title") {
|
|
19803
|
+
if (incoming.type === "task.working_context" || incoming.type === "task.runner_runtime" || incoming.type === "task.title") {
|
|
19758
19804
|
return candidate.type === incoming.type && candidate.taskId === incoming.taskId && candidate.epoch === incoming.epoch;
|
|
19759
19805
|
}
|
|
19760
19806
|
return false;
|
|
@@ -19914,6 +19960,7 @@ var HostClient = class _HostClient {
|
|
|
19914
19960
|
this.configuredWorkspaces = frame.workspaces ?? [];
|
|
19915
19961
|
this.browserFeatureActive = frame.features?.browserSessions === true;
|
|
19916
19962
|
this.workingContextFeatureActive = frame.features?.taskWorkingContext === true;
|
|
19963
|
+
this.runnerRuntimeFeatureActive = frame.features?.taskRunnerRuntime === true;
|
|
19917
19964
|
if (this.browserFeatureActive && this.opts.browser) {
|
|
19918
19965
|
this.opts.browser.attach({
|
|
19919
19966
|
state: (state, openRequestId) => this.sendBrowserFrame({
|
|
@@ -20397,6 +20444,21 @@ var HostClient = class _HostClient {
|
|
|
20397
20444
|
throw new Error("Working-context evidence could not be delivered before execution.");
|
|
20398
20445
|
}
|
|
20399
20446
|
};
|
|
20447
|
+
const emitRunnerRuntime = (runtime) => {
|
|
20448
|
+
if (!this.runnerRuntimeFeatureActive) return;
|
|
20449
|
+
const parsed = TaskRunnerRuntimeObservation.safeParse({
|
|
20450
|
+
type: runtime.type,
|
|
20451
|
+
model: runtime.model === null ? null : safe(runtime.model, 120),
|
|
20452
|
+
effort: runtime.effort === null ? null : safe(runtime.effort, 40)
|
|
20453
|
+
});
|
|
20454
|
+
if (!parsed.success) return;
|
|
20455
|
+
this.sendUp({
|
|
20456
|
+
type: "task.runner_runtime",
|
|
20457
|
+
taskId: assign.taskId,
|
|
20458
|
+
epoch: assign.epoch,
|
|
20459
|
+
runtime: parsed.data
|
|
20460
|
+
});
|
|
20461
|
+
};
|
|
20400
20462
|
const secrets = () => {
|
|
20401
20463
|
if (authorityController.signal.aborted) return Promise.resolve({});
|
|
20402
20464
|
const entry = this.secretGrants.get(cancelKey) ?? { resolvers: [] };
|
|
@@ -20723,6 +20785,7 @@ var HostClient = class _HostClient {
|
|
|
20723
20785
|
spec: assign.spec,
|
|
20724
20786
|
event: emitEvent,
|
|
20725
20787
|
workingContext: emitWorkingContext,
|
|
20788
|
+
runnerRuntime: emitRunnerRuntime,
|
|
20726
20789
|
siblingTasks: () => [...this.liveTasks.values()].filter((live) => live.agentId === assign.agentId && live.taskId !== assign.taskId).map(({ taskId, title }) => ({ taskId, title })),
|
|
20727
20790
|
fetchAttachment: (attachmentId) => this.fetchAttachment(attachmentId, authorityController.signal),
|
|
20728
20791
|
secrets,
|
|
@@ -24403,7 +24466,7 @@ var BrowserManager = class {
|
|
|
24403
24466
|
session.pendingFrame = frame;
|
|
24404
24467
|
if (session.frameTimer) return;
|
|
24405
24468
|
const elapsed = Date.now() - session.lastFrameSentAt;
|
|
24406
|
-
const
|
|
24469
|
+
const delay4 = Math.max(0, this.frameMinIntervalMs - elapsed);
|
|
24407
24470
|
session.frameTimer = setTimeout(() => {
|
|
24408
24471
|
session.frameTimer = null;
|
|
24409
24472
|
const pending = session.pendingFrame;
|
|
@@ -24418,7 +24481,7 @@ var BrowserManager = class {
|
|
|
24418
24481
|
width: pending.width,
|
|
24419
24482
|
height: pending.height
|
|
24420
24483
|
});
|
|
24421
|
-
},
|
|
24484
|
+
}, delay4);
|
|
24422
24485
|
session.frameTimer.unref?.();
|
|
24423
24486
|
}
|
|
24424
24487
|
markActivity(agentId) {
|
|
@@ -33376,7 +33439,10 @@ function createCliRunner(adapter, opts = {}) {
|
|
|
33376
33439
|
// The trusted workspace facts describe where Zixt put provider clones,
|
|
33377
33440
|
// which is always the Zixt-owned root — never a configured workspace.
|
|
33378
33441
|
taskRoot,
|
|
33379
|
-
cwd
|
|
33442
|
+
cwd,
|
|
33443
|
+
command: resolvedCommand,
|
|
33444
|
+
commandPrefixArgs: prefixArgs,
|
|
33445
|
+
env
|
|
33380
33446
|
});
|
|
33381
33447
|
const runEnv = prepared.env ? { ...env, ...prepared.env } : env;
|
|
33382
33448
|
let attachmentSection = "";
|
|
@@ -34064,6 +34130,171 @@ function runCliProcess(options) {
|
|
|
34064
34130
|
|
|
34065
34131
|
// src/runners/claude-code.ts
|
|
34066
34132
|
import { randomUUID as randomUUID11 } from "node:crypto";
|
|
34133
|
+
|
|
34134
|
+
// src/runners/runtime-observation.ts
|
|
34135
|
+
import { open as open5, readdir as readdir4, realpath as realpath8 } from "node:fs/promises";
|
|
34136
|
+
import { homedir as homedir5 } from "node:os";
|
|
34137
|
+
import { join as join15 } from "node:path";
|
|
34138
|
+
var READ_WINDOW_BYTES = 1024 * 1024;
|
|
34139
|
+
var CATALOG_TIMEOUT_MS = 15e3;
|
|
34140
|
+
var CATALOG_OUTPUT_LIMIT_BYTES = 4 * 1024 * 1024;
|
|
34141
|
+
function homeFrom(env) {
|
|
34142
|
+
return env["HOME"] || env["USERPROFILE"] || homedir5();
|
|
34143
|
+
}
|
|
34144
|
+
async function readHead(path) {
|
|
34145
|
+
let handle;
|
|
34146
|
+
try {
|
|
34147
|
+
handle = await open5(path, "r");
|
|
34148
|
+
const buffer = Buffer.alloc(READ_WINDOW_BYTES);
|
|
34149
|
+
const { bytesRead } = await handle.read(buffer, 0, READ_WINDOW_BYTES, 0);
|
|
34150
|
+
return buffer.subarray(0, bytesRead).toString("utf8");
|
|
34151
|
+
} catch {
|
|
34152
|
+
return "";
|
|
34153
|
+
} finally {
|
|
34154
|
+
await handle?.close().catch(() => {
|
|
34155
|
+
});
|
|
34156
|
+
}
|
|
34157
|
+
}
|
|
34158
|
+
async function readTail(path) {
|
|
34159
|
+
let handle;
|
|
34160
|
+
try {
|
|
34161
|
+
handle = await open5(path, "r");
|
|
34162
|
+
const { size } = await handle.stat();
|
|
34163
|
+
const start = Math.max(0, size - READ_WINDOW_BYTES);
|
|
34164
|
+
const length = Math.min(size, READ_WINDOW_BYTES);
|
|
34165
|
+
const buffer = Buffer.alloc(length);
|
|
34166
|
+
const { bytesRead } = await handle.read(buffer, 0, length, start);
|
|
34167
|
+
const text = buffer.subarray(0, bytesRead).toString("utf8");
|
|
34168
|
+
return start === 0 ? text : text.slice(text.indexOf("\n") + 1);
|
|
34169
|
+
} catch {
|
|
34170
|
+
return "";
|
|
34171
|
+
} finally {
|
|
34172
|
+
await handle?.close().catch(() => {
|
|
34173
|
+
});
|
|
34174
|
+
}
|
|
34175
|
+
}
|
|
34176
|
+
function parseLines(text) {
|
|
34177
|
+
const records = [];
|
|
34178
|
+
for (const line of text.split(/\r?\n/)) {
|
|
34179
|
+
if (!line.trim()) continue;
|
|
34180
|
+
try {
|
|
34181
|
+
const value = JSON.parse(line);
|
|
34182
|
+
if (value && typeof value === "object") records.push(value);
|
|
34183
|
+
} catch {
|
|
34184
|
+
}
|
|
34185
|
+
}
|
|
34186
|
+
return records;
|
|
34187
|
+
}
|
|
34188
|
+
function displayValue(value, maxLength) {
|
|
34189
|
+
if (typeof value !== "string") return null;
|
|
34190
|
+
const trimmed = value.trim();
|
|
34191
|
+
if (!trimmed || trimmed.length > maxLength || /[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/u.test(trimmed)) {
|
|
34192
|
+
return null;
|
|
34193
|
+
}
|
|
34194
|
+
return trimmed;
|
|
34195
|
+
}
|
|
34196
|
+
function claudeTranscriptPath(input) {
|
|
34197
|
+
const configDir = input.env["CLAUDE_CONFIG_DIR"] || join15(homeFrom(input.env), ".claude");
|
|
34198
|
+
const slug = input.resolvedCwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
34199
|
+
return join15(configDir, "projects", slug, `${input.sessionId}.jsonl`);
|
|
34200
|
+
}
|
|
34201
|
+
async function readClaudeSessionEffort(input) {
|
|
34202
|
+
const resolvedCwd = await realpath8(input.cwd).catch(() => input.cwd);
|
|
34203
|
+
const path = claudeTranscriptPath({ env: input.env, resolvedCwd, sessionId: input.sessionId });
|
|
34204
|
+
const records = parseLines(await readTail(path));
|
|
34205
|
+
for (let index = records.length - 1; index >= 0; index -= 1) {
|
|
34206
|
+
const record2 = records[index];
|
|
34207
|
+
if (record2["type"] !== "assistant") continue;
|
|
34208
|
+
const effort = displayValue(record2["effort"], 40);
|
|
34209
|
+
if (effort) return effort;
|
|
34210
|
+
}
|
|
34211
|
+
return null;
|
|
34212
|
+
}
|
|
34213
|
+
async function newestDirectories(root, limit) {
|
|
34214
|
+
const entries = await readdir4(root, { withFileTypes: true }).catch(() => []);
|
|
34215
|
+
return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort((left, right) => right.localeCompare(left)).slice(0, limit).map((name) => join15(root, name));
|
|
34216
|
+
}
|
|
34217
|
+
async function findCodexRolloutPath(input) {
|
|
34218
|
+
const codexHome = input.env["CODEX_HOME"] || join15(homeFrom(input.env), ".codex");
|
|
34219
|
+
const sessions = join15(codexHome, "sessions");
|
|
34220
|
+
const suffix = `-${input.threadId}.jsonl`;
|
|
34221
|
+
for (const year of await newestDirectories(sessions, 2)) {
|
|
34222
|
+
for (const month of await newestDirectories(year, 2)) {
|
|
34223
|
+
for (const day of await newestDirectories(month, 3)) {
|
|
34224
|
+
const files = await readdir4(day).catch(() => []);
|
|
34225
|
+
const match = files.find((name) => name.endsWith(suffix));
|
|
34226
|
+
if (match) return join15(day, match);
|
|
34227
|
+
}
|
|
34228
|
+
}
|
|
34229
|
+
}
|
|
34230
|
+
return null;
|
|
34231
|
+
}
|
|
34232
|
+
async function readCodexSessionRuntime(input) {
|
|
34233
|
+
const path = await findCodexRolloutPath(input);
|
|
34234
|
+
if (!path) return null;
|
|
34235
|
+
const contexts = parseLines(await readHead(path)).filter(
|
|
34236
|
+
(record2) => record2["type"] === "turn_context"
|
|
34237
|
+
);
|
|
34238
|
+
const payload = contexts.at(-1)?.["payload"];
|
|
34239
|
+
if (!payload || typeof payload !== "object") return null;
|
|
34240
|
+
const context = payload;
|
|
34241
|
+
const settings = context["collaboration_mode"]?.["settings"];
|
|
34242
|
+
return {
|
|
34243
|
+
model: displayValue(context["model"] ?? settings?.["model"], 120),
|
|
34244
|
+
effort: displayValue(context["effort"] ?? settings?.["reasoning_effort"], 40)
|
|
34245
|
+
};
|
|
34246
|
+
}
|
|
34247
|
+
var codexCatalogCache = /* @__PURE__ */ new Map();
|
|
34248
|
+
async function loadCodexModelCatalog(command, prefixArgs, env) {
|
|
34249
|
+
const output = await new Promise((resolve13) => {
|
|
34250
|
+
const child = spawnCli(command, [...prefixArgs, "debug", "models"], {
|
|
34251
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
34252
|
+
windowsHide: true,
|
|
34253
|
+
env
|
|
34254
|
+
});
|
|
34255
|
+
let text = "";
|
|
34256
|
+
child.stdout?.on("data", (chunk) => {
|
|
34257
|
+
if (text.length < CATALOG_OUTPUT_LIMIT_BYTES) text += chunk.toString("utf8");
|
|
34258
|
+
});
|
|
34259
|
+
let settled = false;
|
|
34260
|
+
const finish = (value) => {
|
|
34261
|
+
if (settled) return;
|
|
34262
|
+
settled = true;
|
|
34263
|
+
clearTimeout(timer);
|
|
34264
|
+
resolve13(value);
|
|
34265
|
+
};
|
|
34266
|
+
const timer = setTimeout(() => {
|
|
34267
|
+
child.kill();
|
|
34268
|
+
finish("");
|
|
34269
|
+
}, CATALOG_TIMEOUT_MS);
|
|
34270
|
+
timer.unref?.();
|
|
34271
|
+
child.on("error", () => finish(""));
|
|
34272
|
+
child.on("close", () => finish(text));
|
|
34273
|
+
});
|
|
34274
|
+
const defaults = /* @__PURE__ */ new Map();
|
|
34275
|
+
try {
|
|
34276
|
+
const catalog2 = JSON.parse(output);
|
|
34277
|
+
for (const model of catalog2.models ?? []) {
|
|
34278
|
+
const slug = displayValue(model["slug"], 120);
|
|
34279
|
+
const effort = displayValue(model["default_reasoning_level"], 40);
|
|
34280
|
+
if (slug && effort) defaults.set(slug, effort);
|
|
34281
|
+
}
|
|
34282
|
+
} catch {
|
|
34283
|
+
}
|
|
34284
|
+
return defaults;
|
|
34285
|
+
}
|
|
34286
|
+
async function codexModelDefaultEffort(input) {
|
|
34287
|
+
const prefixArgs = input.commandPrefixArgs ?? [];
|
|
34288
|
+
const cacheKey = [input.command, ...prefixArgs].join(" ");
|
|
34289
|
+
let catalog2 = codexCatalogCache.get(cacheKey);
|
|
34290
|
+
if (!catalog2) {
|
|
34291
|
+
catalog2 = loadCodexModelCatalog(input.command, prefixArgs, input.env);
|
|
34292
|
+
codexCatalogCache.set(cacheKey, catalog2);
|
|
34293
|
+
}
|
|
34294
|
+
return (await catalog2).get(input.model) ?? null;
|
|
34295
|
+
}
|
|
34296
|
+
|
|
34297
|
+
// src/runners/claude-code.ts
|
|
34067
34298
|
var CLAUDE_SESSION_CRON_TOOLS = ["CronCreate", "CronList", "CronDelete"];
|
|
34068
34299
|
var CLAUDE_REINVOCATION_TOOLS = ["Monitor", "ScheduleWakeup"];
|
|
34069
34300
|
var SESSION_CONFLICT = /session/i;
|
|
@@ -34088,13 +34319,14 @@ var claudeCodeAdapter = {
|
|
|
34088
34319
|
input.cwd
|
|
34089
34320
|
);
|
|
34090
34321
|
const sessionId = input.task.spec.sessionKey ?? randomUUID11();
|
|
34322
|
+
const observeRuntime = createRuntimeReporter(input, sessionId);
|
|
34091
34323
|
return {
|
|
34092
34324
|
argsFor: (mode) => [
|
|
34093
34325
|
...baseArgs,
|
|
34094
34326
|
...mode === "resume" ? ["--resume", sessionId] : ["--session-id", sessionId]
|
|
34095
34327
|
],
|
|
34096
34328
|
prompt: buildRunnerPrompt(input.task),
|
|
34097
|
-
createParser: createStreamParser,
|
|
34329
|
+
createParser: (onStream) => createStreamParser(onStream, { onSessionModel: observeRuntime }),
|
|
34098
34330
|
// Self-heal both directions: a crashed first run leaves a transcript
|
|
34099
34331
|
// (new → conflict), a lost workspace breaks resume (resume → not
|
|
34100
34332
|
// found). One flip covers both.
|
|
@@ -34105,6 +34337,36 @@ var claudeCodeAdapter = {
|
|
|
34105
34337
|
function createClaudeCodeRunner(opts = {}) {
|
|
34106
34338
|
return createCliRunner(claudeCodeAdapter, opts);
|
|
34107
34339
|
}
|
|
34340
|
+
function createRuntimeReporter(input, sessionId) {
|
|
34341
|
+
const { task, runner, env, cwd } = input;
|
|
34342
|
+
const chosenEffort = runner.effort ?? null;
|
|
34343
|
+
let reported = false;
|
|
34344
|
+
return (model) => {
|
|
34345
|
+
if (reported) return;
|
|
34346
|
+
reported = true;
|
|
34347
|
+
task.runnerRuntime?.({ type: "claude-code", model, effort: chosenEffort });
|
|
34348
|
+
if (chosenEffort) return;
|
|
34349
|
+
void (async () => {
|
|
34350
|
+
for (let attempt = 0; attempt < EFFORT_READ_ATTEMPTS; attempt += 1) {
|
|
34351
|
+
await delay2(EFFORT_READ_INTERVAL_MS);
|
|
34352
|
+
if (task.cancelledNow()) return;
|
|
34353
|
+
const effort = await readClaudeSessionEffort({ env, cwd, sessionId }).catch(() => null);
|
|
34354
|
+
if (effort) {
|
|
34355
|
+
task.runnerRuntime?.({ type: "claude-code", model, effort });
|
|
34356
|
+
return;
|
|
34357
|
+
}
|
|
34358
|
+
}
|
|
34359
|
+
})();
|
|
34360
|
+
};
|
|
34361
|
+
}
|
|
34362
|
+
var EFFORT_READ_ATTEMPTS = 5;
|
|
34363
|
+
var EFFORT_READ_INTERVAL_MS = 3e3;
|
|
34364
|
+
function delay2(ms) {
|
|
34365
|
+
return new Promise((resolve13) => {
|
|
34366
|
+
const timer = setTimeout(resolve13, ms);
|
|
34367
|
+
timer.unref?.();
|
|
34368
|
+
});
|
|
34369
|
+
}
|
|
34108
34370
|
async function buildArgs(task, runner, artifacts, mcp, preparedWorkspace, taskRoot, workspacePath = taskRoot ?? "") {
|
|
34109
34371
|
const args = [
|
|
34110
34372
|
// Hermetic, non-interactive run — no local slash commands or global state.
|
|
@@ -34150,7 +34412,7 @@ async function buildArgs(task, runner, artifacts, mcp, preparedWorkspace, taskRo
|
|
|
34150
34412
|
);
|
|
34151
34413
|
return args;
|
|
34152
34414
|
}
|
|
34153
|
-
function createStreamParser(onStream) {
|
|
34415
|
+
function createStreamParser(onStream, hooks = {}) {
|
|
34154
34416
|
let buffer = "";
|
|
34155
34417
|
let responseText = "";
|
|
34156
34418
|
let inputTokens = 0;
|
|
@@ -34175,7 +34437,9 @@ function createStreamParser(onStream) {
|
|
|
34175
34437
|
return void 0;
|
|
34176
34438
|
}
|
|
34177
34439
|
const type = json2["type"];
|
|
34178
|
-
if (type === "
|
|
34440
|
+
if (type === "system" && json2["subtype"] === "init") {
|
|
34441
|
+
if (typeof json2["model"] === "string" && json2["model"]) hooks.onSessionModel?.(json2["model"]);
|
|
34442
|
+
} else if (type === "assistant") {
|
|
34179
34443
|
const message = json2["message"];
|
|
34180
34444
|
applyUsage(message?.["usage"]);
|
|
34181
34445
|
const content = message?.["content"] ?? [];
|
|
@@ -34290,16 +34554,16 @@ function improveErrorMessage(error52) {
|
|
|
34290
34554
|
|
|
34291
34555
|
// src/runners/codex.ts
|
|
34292
34556
|
import { mkdir as mkdir11, readFile as readFile7, writeFile as writeFile6 } from "node:fs/promises";
|
|
34293
|
-
import { homedir as
|
|
34294
|
-
import { join as
|
|
34557
|
+
import { homedir as homedir6 } from "node:os";
|
|
34558
|
+
import { join as join16 } from "node:path";
|
|
34295
34559
|
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.";
|
|
34296
34560
|
function defaultCodexThreadIndexRoot() {
|
|
34297
|
-
return
|
|
34561
|
+
return join16(homedir6(), ".zixt", "codex-threads");
|
|
34298
34562
|
}
|
|
34299
34563
|
var SAFE_SEGMENT3 = /^[A-Za-z0-9_-]{1,200}$/;
|
|
34300
34564
|
function threadIndexPath(root, agentId, sessionKey) {
|
|
34301
34565
|
if (!SAFE_SEGMENT3.test(agentId) || !SAFE_SEGMENT3.test(sessionKey)) return null;
|
|
34302
|
-
return
|
|
34566
|
+
return join16(root, agentId, `${sessionKey}.json`);
|
|
34303
34567
|
}
|
|
34304
34568
|
async function readThreadId(path) {
|
|
34305
34569
|
try {
|
|
@@ -34389,9 +34653,12 @@ ${buildRunnerPrompt(task)}` : buildRunnerPrompt(task);
|
|
|
34389
34653
|
const recordedThreadId = indexPath ? await readThreadId(indexPath) : null;
|
|
34390
34654
|
const rememberThread = (threadId) => {
|
|
34391
34655
|
if (!indexPath) return;
|
|
34392
|
-
void mkdir11(
|
|
34656
|
+
void mkdir11(join16(threadIndexRoot, task.agentId), { recursive: true }).then(() => writeFile6(indexPath, JSON.stringify({ threadId }), "utf8")).catch(() => {
|
|
34393
34657
|
});
|
|
34394
34658
|
};
|
|
34659
|
+
const observeRuntime = (threadId) => {
|
|
34660
|
+
void reportCodexRuntime({ input, threadId });
|
|
34661
|
+
};
|
|
34395
34662
|
return {
|
|
34396
34663
|
argsFor: (mode) => {
|
|
34397
34664
|
if (mode === "resume") {
|
|
@@ -34401,7 +34668,12 @@ ${buildRunnerPrompt(task)}` : buildRunnerPrompt(task);
|
|
|
34401
34668
|
},
|
|
34402
34669
|
prompt,
|
|
34403
34670
|
env,
|
|
34404
|
-
createParser: (onStream) => createCodexStreamParser(onStream, {
|
|
34671
|
+
createParser: (onStream) => createCodexStreamParser(onStream, {
|
|
34672
|
+
onThreadStarted: (threadId) => {
|
|
34673
|
+
rememberThread(threadId);
|
|
34674
|
+
observeRuntime(threadId);
|
|
34675
|
+
}
|
|
34676
|
+
}),
|
|
34405
34677
|
// Only one flip is meaningful: a resume whose rollout vanished starts
|
|
34406
34678
|
// over. Codex assigns new-session ids itself, so a new session can
|
|
34407
34679
|
// never collide the way a pre-specified id can.
|
|
@@ -34410,6 +34682,30 @@ ${buildRunnerPrompt(task)}` : buildRunnerPrompt(task);
|
|
|
34410
34682
|
}
|
|
34411
34683
|
};
|
|
34412
34684
|
}
|
|
34685
|
+
var RUNTIME_READ_ATTEMPTS = 5;
|
|
34686
|
+
var RUNTIME_READ_INTERVAL_MS = 2e3;
|
|
34687
|
+
function delay3(ms) {
|
|
34688
|
+
return new Promise((resolve13) => {
|
|
34689
|
+
const timer = setTimeout(resolve13, ms);
|
|
34690
|
+
timer.unref?.();
|
|
34691
|
+
});
|
|
34692
|
+
}
|
|
34693
|
+
async function reportCodexRuntime(args) {
|
|
34694
|
+
const { task, runner, env, command, commandPrefixArgs } = args.input;
|
|
34695
|
+
for (let attempt = 0; attempt < RUNTIME_READ_ATTEMPTS; attempt += 1) {
|
|
34696
|
+
if (task.cancelledNow()) return;
|
|
34697
|
+
const observed = await readCodexSessionRuntime({ env, threadId: args.threadId }).catch(
|
|
34698
|
+
() => null
|
|
34699
|
+
);
|
|
34700
|
+
const model = observed?.model ?? runner.model ?? null;
|
|
34701
|
+
if (model) {
|
|
34702
|
+
const effort = observed?.effort ?? runner.effort ?? (command ? await codexModelDefaultEffort({ command, commandPrefixArgs, env, model }) : null);
|
|
34703
|
+
task.runnerRuntime?.({ type: "codex", model, effort });
|
|
34704
|
+
return;
|
|
34705
|
+
}
|
|
34706
|
+
await delay3(RUNTIME_READ_INTERVAL_MS);
|
|
34707
|
+
}
|
|
34708
|
+
}
|
|
34413
34709
|
function createCodexRunner(opts = {}) {
|
|
34414
34710
|
const adapter = createCodexAdapter(opts.threadIndexRoot ?? defaultCodexThreadIndexRoot());
|
|
34415
34711
|
return createCliRunner(adapter, opts);
|
|
@@ -34559,7 +34855,7 @@ function improveCodexErrorMessage(error52) {
|
|
|
34559
34855
|
|
|
34560
34856
|
// src/runners/git-preflight.ts
|
|
34561
34857
|
import { spawn as spawn9 } from "node:child_process";
|
|
34562
|
-
import { realpath as
|
|
34858
|
+
import { realpath as realpath9 } from "node:fs/promises";
|
|
34563
34859
|
import { isAbsolute as isAbsolute13, resolve as resolve7 } from "node:path";
|
|
34564
34860
|
var OUTPUT_LIMIT = 8192;
|
|
34565
34861
|
var DEFAULT_TIMEOUT_MS4 = 1e4;
|
|
@@ -34579,7 +34875,7 @@ async function preflightGit(options = {}) {
|
|
|
34579
34875
|
if (configured !== void 0 && !isAbsolute13(configured)) {
|
|
34580
34876
|
return unavailable("configured git command must be an absolute file", checkedAt);
|
|
34581
34877
|
}
|
|
34582
|
-
const trustedCwd = await
|
|
34878
|
+
const trustedCwd = await realpath9(resolve7(options.trustedCwd ?? process.cwd())).catch(() => null);
|
|
34583
34879
|
if (!trustedCwd)
|
|
34584
34880
|
return unavailable("Host-owned git preflight directory is unavailable", checkedAt);
|
|
34585
34881
|
const executablePath = await resolveTrustedCliCommand(configured ?? "git", {
|
|
@@ -34831,9 +35127,9 @@ function run2(command, args) {
|
|
|
34831
35127
|
// src/linux-service.ts
|
|
34832
35128
|
import { spawn as spawn10 } from "node:child_process";
|
|
34833
35129
|
import { constants as constants2 } from "node:fs";
|
|
34834
|
-
import { access as access4, chmod as chmod7, mkdir as mkdir12, open as
|
|
34835
|
-
import { homedir as
|
|
34836
|
-
import { basename as basename3, dirname as dirname7, join as
|
|
35130
|
+
import { access as access4, chmod as chmod7, mkdir as mkdir12, open as open6, rename as rename5, rm as rm7 } from "node:fs/promises";
|
|
35131
|
+
import { homedir as homedir7, userInfo } from "node:os";
|
|
35132
|
+
import { basename as basename3, dirname as dirname7, join as join17, relative as relative8, resolve as resolve8, sep as sep4 } from "node:path";
|
|
34837
35133
|
var SERVICE_NAME = "zixt-host.service";
|
|
34838
35134
|
var SYSTEM_SERVICE_COMMAND_TIMEOUT_MS = 7e4;
|
|
34839
35135
|
var SERVICE_STABILITY_DELAY_MS = 2e3;
|
|
@@ -34921,7 +35217,7 @@ function systemdDirectiveValue(value) {
|
|
|
34921
35217
|
return systemdQuotedValue(value, false);
|
|
34922
35218
|
}
|
|
34923
35219
|
async function defaultSyncDirectory(path) {
|
|
34924
|
-
const directory = await
|
|
35220
|
+
const directory = await open6(path, "r");
|
|
34925
35221
|
try {
|
|
34926
35222
|
await directory.sync();
|
|
34927
35223
|
} finally {
|
|
@@ -34938,14 +35234,14 @@ async function ensureDirectory(path, mode, syncDirectory7) {
|
|
|
34938
35234
|
const descendants = relative8(first, target);
|
|
34939
35235
|
for (const part of descendants ? descendants.split(sep4) : []) {
|
|
34940
35236
|
await syncDirectory7(current);
|
|
34941
|
-
current =
|
|
35237
|
+
current = join17(current, part);
|
|
34942
35238
|
}
|
|
34943
35239
|
}
|
|
34944
35240
|
async function replacePrivateFile(path, contents, mode, syncDirectory7) {
|
|
34945
35241
|
const parent = dirname7(path);
|
|
34946
35242
|
await ensureDirectory(parent, 448, syncDirectory7);
|
|
34947
|
-
const temporary =
|
|
34948
|
-
const handle = await
|
|
35243
|
+
const temporary = join17(parent, `.${basename3(path)}.${process.pid}.${crypto.randomUUID()}.tmp`);
|
|
35244
|
+
const handle = await open6(temporary, "wx", mode);
|
|
34949
35245
|
try {
|
|
34950
35246
|
await handle.writeFile(contents, "utf8");
|
|
34951
35247
|
await handle.sync();
|
|
@@ -34988,7 +35284,7 @@ async function installLinuxService(options) {
|
|
|
34988
35284
|
throw new Error("Linux automatic startup is available only on Linux.");
|
|
34989
35285
|
}
|
|
34990
35286
|
const env = options.env ?? process.env;
|
|
34991
|
-
const home = options.home ??
|
|
35287
|
+
const home = options.home ?? homedir7();
|
|
34992
35288
|
const username = oneLine(options.username ?? userInfo().username, "user name");
|
|
34993
35289
|
const token2 = oneLine(options.token, "pairing code");
|
|
34994
35290
|
const path = oneLine(
|
|
@@ -34996,11 +35292,11 @@ async function installLinuxService(options) {
|
|
|
34996
35292
|
"command search path"
|
|
34997
35293
|
);
|
|
34998
35294
|
const cloudUrl = options.cloudUrl ? oneLine(options.cloudUrl, "Zixt Cloud address") : void 0;
|
|
34999
|
-
const xdgConfigHome = env.XDG_CONFIG_HOME ? oneLine(env.XDG_CONFIG_HOME, "Linux configuration path") :
|
|
35000
|
-
const configRoot = options.serviceConfigRoot ??
|
|
35001
|
-
const unitRoot = options.userUnitRoot ??
|
|
35002
|
-
const environmentPath =
|
|
35003
|
-
const unitPath =
|
|
35295
|
+
const xdgConfigHome = env.XDG_CONFIG_HOME ? oneLine(env.XDG_CONFIG_HOME, "Linux configuration path") : join17(home, ".config");
|
|
35296
|
+
const configRoot = options.serviceConfigRoot ?? join17(xdgConfigHome, "zixt");
|
|
35297
|
+
const unitRoot = options.userUnitRoot ?? join17(xdgConfigHome, "systemd", "user");
|
|
35298
|
+
const environmentPath = join17(configRoot, "host.env");
|
|
35299
|
+
const unitPath = join17(unitRoot, SERVICE_NAME);
|
|
35004
35300
|
const installVersion = options.installVersion ?? installRelease;
|
|
35005
35301
|
const activateVersion = options.activateVersion ?? (options.installVersion ? async (entry) => entry : activateInstalledRelease);
|
|
35006
35302
|
const resolveCommand = options.resolveCommand ?? defaultResolveCommand;
|
|
@@ -35119,9 +35415,9 @@ async function installLinuxService(options) {
|
|
|
35119
35415
|
// src/macos-service.ts
|
|
35120
35416
|
import { spawn as spawn11 } from "node:child_process";
|
|
35121
35417
|
import { constants as constants3 } from "node:fs";
|
|
35122
|
-
import { access as access5, chmod as chmod8, mkdir as mkdir13, open as
|
|
35123
|
-
import { homedir as
|
|
35124
|
-
import { basename as basename4, dirname as dirname8, join as
|
|
35418
|
+
import { access as access5, chmod as chmod8, mkdir as mkdir13, open as open7, rename as rename6, rm as rm8 } from "node:fs/promises";
|
|
35419
|
+
import { homedir as homedir8, userInfo as userInfo2 } from "node:os";
|
|
35420
|
+
import { basename as basename4, dirname as dirname8, join as join18, relative as relative9, resolve as resolve9, sep as sep5 } from "node:path";
|
|
35125
35421
|
var LAUNCH_AGENT_LABEL = "ai.zixt.host";
|
|
35126
35422
|
var SERVICE_STABILITY_DELAY_MS2 = 2e3;
|
|
35127
35423
|
var COMMAND_TIMEOUT_MS2 = 7e4;
|
|
@@ -35137,7 +35433,7 @@ function shellValue(value) {
|
|
|
35137
35433
|
return `'${oneLine2(value, "service setting").replaceAll("'", `'"'"'`)}'`;
|
|
35138
35434
|
}
|
|
35139
35435
|
async function syncDirectory4(path) {
|
|
35140
|
-
const directory = await
|
|
35436
|
+
const directory = await open7(path, "r");
|
|
35141
35437
|
try {
|
|
35142
35438
|
await directory.sync();
|
|
35143
35439
|
} finally {
|
|
@@ -35153,14 +35449,14 @@ async function ensureDirectory2(path, sync) {
|
|
|
35153
35449
|
let current = first;
|
|
35154
35450
|
for (const part of relative9(first, target).split(sep5).filter(Boolean)) {
|
|
35155
35451
|
await sync(current);
|
|
35156
|
-
current =
|
|
35452
|
+
current = join18(current, part);
|
|
35157
35453
|
}
|
|
35158
35454
|
}
|
|
35159
35455
|
async function replacePrivateFile2(path, contents, mode, sync) {
|
|
35160
35456
|
const parent = dirname8(path);
|
|
35161
35457
|
await ensureDirectory2(parent, sync);
|
|
35162
|
-
const temporary =
|
|
35163
|
-
const handle = await
|
|
35458
|
+
const temporary = join18(parent, `.${basename4(path)}.${process.pid}.${crypto.randomUUID()}.tmp`);
|
|
35459
|
+
const handle = await open7(temporary, "wx", mode);
|
|
35164
35460
|
try {
|
|
35165
35461
|
await handle.writeFile(contents, "utf8");
|
|
35166
35462
|
await handle.sync();
|
|
@@ -35247,7 +35543,7 @@ async function installMacosService(options) {
|
|
|
35247
35543
|
throw new Error("macOS automatic startup is available only on macOS.");
|
|
35248
35544
|
}
|
|
35249
35545
|
const env = options.env ?? process.env;
|
|
35250
|
-
const home = options.home ??
|
|
35546
|
+
const home = options.home ?? homedir8();
|
|
35251
35547
|
const uid = options.uid ?? userInfo2().uid;
|
|
35252
35548
|
if (!Number.isSafeInteger(uid) || uid < 0) throw new Error("macOS user id is invalid.");
|
|
35253
35549
|
const token2 = oneLine2(options.token, "pairing code");
|
|
@@ -35256,20 +35552,20 @@ async function installMacosService(options) {
|
|
|
35256
35552
|
options.path ?? env.PATH ?? "/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin",
|
|
35257
35553
|
"command search path"
|
|
35258
35554
|
);
|
|
35259
|
-
const configRoot = options.configRoot ??
|
|
35260
|
-
const launchAgentsRoot = options.launchAgentsRoot ??
|
|
35261
|
-
const logRoot = options.logRoot ??
|
|
35262
|
-
const configPath =
|
|
35263
|
-
const launcherPath =
|
|
35264
|
-
const plistPath =
|
|
35265
|
-
const stdoutPath =
|
|
35266
|
-
const stderrPath =
|
|
35555
|
+
const configRoot = options.configRoot ?? join18(home, "Library", "Application Support", "Zixt");
|
|
35556
|
+
const launchAgentsRoot = options.launchAgentsRoot ?? join18(home, "Library", "LaunchAgents");
|
|
35557
|
+
const logRoot = options.logRoot ?? join18(home, "Library", "Logs", "Zixt");
|
|
35558
|
+
const configPath = join18(configRoot, "host.env");
|
|
35559
|
+
const launcherPath = join18(configRoot, "host-launcher.sh");
|
|
35560
|
+
const plistPath = join18(launchAgentsRoot, `${LAUNCH_AGENT_LABEL}.plist`);
|
|
35561
|
+
const stdoutPath = join18(logRoot, "host.log");
|
|
35562
|
+
const stderrPath = join18(logRoot, "host-error.log");
|
|
35267
35563
|
const installVersion = options.installVersion ?? installRelease;
|
|
35268
35564
|
const activateVersion = options.activateVersion ?? (options.installVersion ? async (entry) => entry : (entry) => activateInstalledRelease(entry));
|
|
35269
35565
|
const resolveCommand = options.resolveCommand ?? (async () => defaultResolveCommand2());
|
|
35270
35566
|
const run3 = options.runCommand ?? ((command, args) => defaultRunCommand2(command, args, env));
|
|
35271
35567
|
const sync = options.syncDirectory ?? syncDirectory4;
|
|
35272
|
-
const
|
|
35568
|
+
const delay4 = options.delay ?? ((ms) => new Promise((done) => setTimeout(done, ms)));
|
|
35273
35569
|
const launchctl = await resolveCommand("launchctl");
|
|
35274
35570
|
if (!launchctl) throw new Error("macOS launchctl could not be found.");
|
|
35275
35571
|
const releaseEntry = await installVersion(options.hostVersion);
|
|
@@ -35331,7 +35627,7 @@ async function installMacosService(options) {
|
|
|
35331
35627
|
return status;
|
|
35332
35628
|
};
|
|
35333
35629
|
const first = await observe();
|
|
35334
|
-
await
|
|
35630
|
+
await delay4(options.serviceStabilityDelayMs ?? SERVICE_STABILITY_DELAY_MS2);
|
|
35335
35631
|
const second = await observe();
|
|
35336
35632
|
if (first.pid !== second.pid || first.runs !== second.runs) {
|
|
35337
35633
|
throw new Error("Zixt started but restarted during its readiness check.");
|
|
@@ -35350,9 +35646,9 @@ async function installMacosService(options) {
|
|
|
35350
35646
|
// src/windows-service.ts
|
|
35351
35647
|
import { spawn as spawn12 } from "node:child_process";
|
|
35352
35648
|
import { constants as constants4 } from "node:fs";
|
|
35353
|
-
import { access as access6, mkdir as mkdir14, open as
|
|
35354
|
-
import { homedir as
|
|
35355
|
-
import { basename as basename5, dirname as dirname9, isAbsolute as isAbsolute14, join as
|
|
35649
|
+
import { access as access6, mkdir as mkdir14, open as open8, readFile as readFile8, rename as rename7, rm as rm9 } from "node:fs/promises";
|
|
35650
|
+
import { homedir as homedir9 } from "node:os";
|
|
35651
|
+
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";
|
|
35356
35652
|
var TASK_NAME = "Zixt Host";
|
|
35357
35653
|
var COMMAND_TIMEOUT_MS3 = 7e4;
|
|
35358
35654
|
var SERVICE_STABILITY_DELAY_MS3 = 2e3;
|
|
@@ -35370,7 +35666,7 @@ function psLiteral(value) {
|
|
|
35370
35666
|
}
|
|
35371
35667
|
async function syncDirectory5(path) {
|
|
35372
35668
|
if (process.platform === "win32") return;
|
|
35373
|
-
const directory = await
|
|
35669
|
+
const directory = await open8(path, "r");
|
|
35374
35670
|
try {
|
|
35375
35671
|
await directory.sync();
|
|
35376
35672
|
} finally {
|
|
@@ -35386,14 +35682,14 @@ async function ensureDirectory3(path, sync) {
|
|
|
35386
35682
|
let current = first;
|
|
35387
35683
|
for (const part of relative10(first, target).split(sep6).filter(Boolean)) {
|
|
35388
35684
|
await sync(current);
|
|
35389
|
-
current =
|
|
35685
|
+
current = join19(current, part);
|
|
35390
35686
|
}
|
|
35391
35687
|
}
|
|
35392
35688
|
async function replacePrivateFile3(path, contents, sync) {
|
|
35393
35689
|
const parent = dirname9(path);
|
|
35394
35690
|
await ensureDirectory3(parent, sync);
|
|
35395
|
-
const temporary =
|
|
35396
|
-
const handle = await
|
|
35691
|
+
const temporary = join19(parent, `.${basename5(path)}.${process.pid}.${crypto.randomUUID()}.tmp`);
|
|
35692
|
+
const handle = await open8(temporary, "wx", 384);
|
|
35397
35693
|
try {
|
|
35398
35694
|
await handle.writeFile(contents, "utf8");
|
|
35399
35695
|
await handle.sync();
|
|
@@ -35447,7 +35743,7 @@ async function runChild(command, args, env, input) {
|
|
|
35447
35743
|
async function defaultResolveCommand3(name, env) {
|
|
35448
35744
|
const root = env.SYSTEMROOT ?? env.WINDIR;
|
|
35449
35745
|
if (!root || !isAbsolute14(root)) return null;
|
|
35450
|
-
const candidate = name === "powershell" ?
|
|
35746
|
+
const candidate = name === "powershell" ? join19(root, "System32", "WindowsPowerShell", "v1.0", "powershell.exe") : join19(root, "System32", `${name}.exe`);
|
|
35451
35747
|
return access6(candidate, constants4.X_OK).then(
|
|
35452
35748
|
() => candidate,
|
|
35453
35749
|
() => null
|
|
@@ -35576,7 +35872,7 @@ async function installWindowsService(options) {
|
|
|
35576
35872
|
throw new Error("Windows automatic startup is available only on Windows.");
|
|
35577
35873
|
}
|
|
35578
35874
|
const env = options.env ?? process.env;
|
|
35579
|
-
const home = options.home ??
|
|
35875
|
+
const home = options.home ?? homedir9();
|
|
35580
35876
|
const localAppData = options.localAppData ?? env.LOCALAPPDATA;
|
|
35581
35877
|
if (!localAppData || !isAbsolute14(localAppData)) {
|
|
35582
35878
|
throw new Error("Windows local application data path is unavailable.");
|
|
@@ -35584,17 +35880,17 @@ async function installWindowsService(options) {
|
|
|
35584
35880
|
const token2 = oneLine3(options.token, "pairing code");
|
|
35585
35881
|
const cloudUrl = options.cloudUrl ? oneLine3(options.cloudUrl, "Zixt Cloud address") : void 0;
|
|
35586
35882
|
const path = oneLine3(options.path ?? env.PATH ?? "", "command search path");
|
|
35587
|
-
const configRoot = options.configRoot ??
|
|
35588
|
-
const configPath =
|
|
35589
|
-
const launcherPath =
|
|
35590
|
-
const taskXmlPath =
|
|
35591
|
-
const statusPath =
|
|
35883
|
+
const configRoot = options.configRoot ?? join19(localAppData, "Zixt", "Host");
|
|
35884
|
+
const configPath = join19(configRoot, "host.json");
|
|
35885
|
+
const launcherPath = join19(configRoot, "host-launcher.ps1");
|
|
35886
|
+
const taskXmlPath = join19(configRoot, "host-task.xml");
|
|
35887
|
+
const statusPath = join19(configRoot, "host-status.json");
|
|
35592
35888
|
const installVersion = options.installVersion ?? installRelease;
|
|
35593
35889
|
const activateVersion = options.activateVersion ?? (options.installVersion ? async (entry) => entry : (entry) => activateInstalledRelease(entry));
|
|
35594
35890
|
const resolveCommand = options.resolveCommand ?? ((name) => defaultResolveCommand3(name, env));
|
|
35595
35891
|
const run3 = options.runCommand ?? ((command, args) => runChild(command, args, env));
|
|
35596
35892
|
const sync = options.syncDirectory ?? syncDirectory5;
|
|
35597
|
-
const
|
|
35893
|
+
const delay4 = options.delay ?? ((ms) => new Promise((done) => setTimeout(done, ms)));
|
|
35598
35894
|
const observe = options.observeStatus ?? defaultObserveStatus;
|
|
35599
35895
|
const [powershell, schtasks, icacls] = await Promise.all([
|
|
35600
35896
|
resolveCommand("powershell"),
|
|
@@ -35657,10 +35953,10 @@ async function installWindowsService(options) {
|
|
|
35657
35953
|
do {
|
|
35658
35954
|
first = await observe(statusPath, generation);
|
|
35659
35955
|
if (first) break;
|
|
35660
|
-
await
|
|
35956
|
+
await delay4(50);
|
|
35661
35957
|
} while (Date.now() < deadline);
|
|
35662
35958
|
if (!first) throw new Error("Zixt started but did not reach a running process state.");
|
|
35663
|
-
await
|
|
35959
|
+
await delay4(options.serviceStabilityDelayMs ?? SERVICE_STABILITY_DELAY_MS3);
|
|
35664
35960
|
const second = await observe(statusPath, generation);
|
|
35665
35961
|
if (!second || first.pid !== second.pid) {
|
|
35666
35962
|
throw new Error("Zixt started but restarted during its readiness check.");
|
|
@@ -35689,27 +35985,27 @@ async function installSystemService(options) {
|
|
|
35689
35985
|
}
|
|
35690
35986
|
|
|
35691
35987
|
// src/terminal-outcomes.ts
|
|
35692
|
-
import { chmod as chmod9, lstat as lstat10, mkdir as mkdir15, open as
|
|
35693
|
-
import { homedir as
|
|
35694
|
-
import { dirname as dirname10, join as
|
|
35988
|
+
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";
|
|
35989
|
+
import { homedir as homedir10 } from "node:os";
|
|
35990
|
+
import { dirname as dirname10, join as join20, relative as relative11, resolve as resolve11, sep as sep7 } from "node:path";
|
|
35695
35991
|
var DIRECTORY_MODE5 = 448;
|
|
35696
35992
|
var FILE_MODE4 = 384;
|
|
35697
35993
|
var MAX_OUTCOME_BYTES = 4 * 1024 * 1024;
|
|
35698
35994
|
var HOST_DIRECTORY = /^hst_[0-9a-f]{32}$/;
|
|
35699
35995
|
var OUTCOME_FILE = /^(tsk_[0-9a-f]{32})\.([1-9][0-9]*)\.json$/;
|
|
35700
35996
|
function defaultTerminalOutcomeRoot() {
|
|
35701
|
-
return
|
|
35997
|
+
return join20(homedir10(), ".zixt", "terminal-outcomes");
|
|
35702
35998
|
}
|
|
35703
35999
|
function hostOutcomeRoot(root, hostId) {
|
|
35704
36000
|
if (!HOST_DIRECTORY.test(hostId)) throw new Error("terminal outcome Host identity is malformed");
|
|
35705
|
-
return
|
|
36001
|
+
return join20(root, hostId);
|
|
35706
36002
|
}
|
|
35707
36003
|
function outcomePath(root, hostId, taskId, epoch) {
|
|
35708
|
-
return
|
|
36004
|
+
return join20(hostOutcomeRoot(root, hostId), `${taskId}.${epoch}.json`);
|
|
35709
36005
|
}
|
|
35710
36006
|
async function syncDirectory6(root) {
|
|
35711
36007
|
if (process.platform === "win32") return;
|
|
35712
|
-
const handle = await
|
|
36008
|
+
const handle = await open9(root, "r");
|
|
35713
36009
|
try {
|
|
35714
36010
|
await handle.sync();
|
|
35715
36011
|
} finally {
|
|
@@ -35725,7 +36021,7 @@ async function requirePrivateRoot(root, sync = syncDirectory6) {
|
|
|
35725
36021
|
let current = first;
|
|
35726
36022
|
for (const part of relative11(first, target).split(sep7).filter(Boolean)) {
|
|
35727
36023
|
await sync(current);
|
|
35728
|
-
current =
|
|
36024
|
+
current = join20(current, part);
|
|
35729
36025
|
}
|
|
35730
36026
|
}
|
|
35731
36027
|
const stat3 = await lstat10(root);
|
|
@@ -35765,13 +36061,13 @@ async function recordTerminalOutcome(hostId, input, root = defaultTerminalOutcom
|
|
|
35765
36061
|
} catch (error52) {
|
|
35766
36062
|
if (error52.code !== "ENOENT") throw error52;
|
|
35767
36063
|
}
|
|
35768
|
-
const temporary =
|
|
36064
|
+
const temporary = join20(
|
|
35769
36065
|
scopedRoot,
|
|
35770
36066
|
`.${outcome.taskId}.${outcome.epoch}.${process.pid}.${Date.now()}.${outcome.resultId}.tmp`
|
|
35771
36067
|
);
|
|
35772
36068
|
let handle;
|
|
35773
36069
|
try {
|
|
35774
|
-
handle = await
|
|
36070
|
+
handle = await open9(temporary, "wx", FILE_MODE4);
|
|
35775
36071
|
await handle.writeFile(JSON.stringify(outcome), "utf8");
|
|
35776
36072
|
await handle.sync();
|
|
35777
36073
|
await handle.close();
|
|
@@ -35798,7 +36094,7 @@ async function readTerminalOutcomesStrict(root = defaultTerminalOutcomeRoot()) {
|
|
|
35798
36094
|
throw new Error("terminal outcome journal root is not a trusted directory");
|
|
35799
36095
|
}
|
|
35800
36096
|
await chmod9(root, DIRECTORY_MODE5);
|
|
35801
|
-
const hostEntries = await
|
|
36097
|
+
const hostEntries = await readdir5(root, { withFileTypes: true });
|
|
35802
36098
|
const outcomes = [];
|
|
35803
36099
|
const resultIds = /* @__PURE__ */ new Set();
|
|
35804
36100
|
for (const hostEntry of hostEntries) {
|
|
@@ -35811,14 +36107,14 @@ async function readTerminalOutcomesStrict(root = defaultTerminalOutcomeRoot()) {
|
|
|
35811
36107
|
throw new Error("terminal outcome Host scope is not a trusted directory");
|
|
35812
36108
|
}
|
|
35813
36109
|
await chmod9(scopedRoot, DIRECTORY_MODE5);
|
|
35814
|
-
const entries = await
|
|
36110
|
+
const entries = await readdir5(scopedRoot, { withFileTypes: true });
|
|
35815
36111
|
for (const entry of entries) {
|
|
35816
36112
|
if (!entry.name.endsWith(".json")) continue;
|
|
35817
36113
|
const match = OUTCOME_FILE.exec(entry.name);
|
|
35818
36114
|
if (!match || !entry.isFile() || entry.isSymbolicLink()) {
|
|
35819
36115
|
throw new Error("committed terminal outcome is not a trusted regular file");
|
|
35820
36116
|
}
|
|
35821
|
-
const path =
|
|
36117
|
+
const path = join20(scopedRoot, entry.name);
|
|
35822
36118
|
const stat3 = await lstat10(path);
|
|
35823
36119
|
if (!stat3.isFile() || stat3.isSymbolicLink() || stat3.size > MAX_OUTCOME_BYTES) {
|
|
35824
36120
|
throw new Error("committed terminal outcome is not a trusted regular file");
|
|
@@ -35971,7 +36267,7 @@ function createHostLogger(options = {}) {
|
|
|
35971
36267
|
}
|
|
35972
36268
|
|
|
35973
36269
|
// src/demo-state.ts
|
|
35974
|
-
import { isAbsolute as isAbsolute15, join as
|
|
36270
|
+
import { isAbsolute as isAbsolute15, join as join21, parse as parse3, resolve as resolve12 } from "node:path";
|
|
35975
36271
|
var DEMO_STATE_ROOT_ENV = "ZIXT_DEMO_STATE_ROOT";
|
|
35976
36272
|
function resolveDemoHostStatePaths(env = process.env) {
|
|
35977
36273
|
const configured = env.ZIXT_RUNNER === "demo" ? env[DEMO_STATE_ROOT_ENV] : void 0;
|
|
@@ -35981,12 +36277,12 @@ function resolveDemoHostStatePaths(env = process.env) {
|
|
|
35981
36277
|
throw new Error(`${DEMO_STATE_ROOT_ENV} must be a dedicated absolute directory`);
|
|
35982
36278
|
}
|
|
35983
36279
|
return {
|
|
35984
|
-
runRegistryRoot:
|
|
35985
|
-
terminalOutcomeRoot:
|
|
35986
|
-
runArtifactRoot:
|
|
35987
|
-
browserProfileRoot:
|
|
35988
|
-
runnerWorkspaceRoot:
|
|
35989
|
-
codexThreadIndexRoot:
|
|
36280
|
+
runRegistryRoot: join21(root, "run-registry"),
|
|
36281
|
+
terminalOutcomeRoot: join21(root, "terminal-outcomes"),
|
|
36282
|
+
runArtifactRoot: join21(root, "run-artifacts"),
|
|
36283
|
+
browserProfileRoot: join21(root, "browser-profiles"),
|
|
36284
|
+
runnerWorkspaceRoot: join21(root, "workspaces"),
|
|
36285
|
+
codexThreadIndexRoot: join21(root, "codex-threads")
|
|
35990
36286
|
};
|
|
35991
36287
|
}
|
|
35992
36288
|
|