@zixt/host 0.0.136 → 0.0.138
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 +797 -180
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -28,7 +28,7 @@ import { homedir as homedir4 } from "node:os";
|
|
|
28
28
|
// package.json
|
|
29
29
|
var package_default = {
|
|
30
30
|
name: "@zixt/host",
|
|
31
|
-
version: "0.0.
|
|
31
|
+
version: "0.0.138",
|
|
32
32
|
type: "module",
|
|
33
33
|
exports: {
|
|
34
34
|
".": "./src/client.ts",
|
|
@@ -14676,6 +14676,8 @@ var ID_PREFIXES = {
|
|
|
14676
14676
|
managerApiAction: "maa",
|
|
14677
14677
|
/** One Manager-prepared Webhook Automation setup card (MG-21). */
|
|
14678
14678
|
managerWebhookAction: "mwa",
|
|
14679
|
+
/** One Manager-prepared Email Integration setup card (MG-24). */
|
|
14680
|
+
managerEmailSetup: "mes",
|
|
14679
14681
|
/** One Manager-authored questionnaire waiting in a Conversation. */
|
|
14680
14682
|
managerQuestionnaire: "mqu",
|
|
14681
14683
|
/** One Manager inference call's token-metering row (MG-8). */
|
|
@@ -14763,6 +14765,10 @@ var ManagerWebhookActionId = idSchema(
|
|
|
14763
14765
|
ID_PREFIXES.managerWebhookAction,
|
|
14764
14766
|
"Manager webhook action id"
|
|
14765
14767
|
);
|
|
14768
|
+
var ManagerEmailSetupId = idSchema(
|
|
14769
|
+
ID_PREFIXES.managerEmailSetup,
|
|
14770
|
+
"Manager email setup id"
|
|
14771
|
+
);
|
|
14766
14772
|
var ManagerQuestionnaireId = idSchema(
|
|
14767
14773
|
ID_PREFIXES.managerQuestionnaire,
|
|
14768
14774
|
"Manager questionnaire id"
|
|
@@ -19861,7 +19867,9 @@ var ConversationEventProjection = external_exports.discriminatedUnion("kind", [
|
|
|
19861
19867
|
/** Present only for a Webhook Automation secure setup card (MG-21). */
|
|
19862
19868
|
webhookActionId: ManagerWebhookActionId.nullable().optional(),
|
|
19863
19869
|
/** Present only for a Manager-authored questionnaire (MG-22). */
|
|
19864
|
-
questionnaireId: ManagerQuestionnaireId.nullable().optional()
|
|
19870
|
+
questionnaireId: ManagerQuestionnaireId.nullable().optional(),
|
|
19871
|
+
/** Present only for an Email Integration setup card (MG-24). */
|
|
19872
|
+
emailSetupId: ManagerEmailSetupId.nullable().optional()
|
|
19865
19873
|
}).strict(),
|
|
19866
19874
|
/** A mirrored child-Task event: what the teammate wrote or became. */
|
|
19867
19875
|
external_exports.object({
|
|
@@ -19934,6 +19942,44 @@ var ManagerIntegrationActionResponse = external_exports.object({
|
|
|
19934
19942
|
action: ManagerIntegrationActionProjection,
|
|
19935
19943
|
oauth: ManagerIntegrationOAuthStart.nullable()
|
|
19936
19944
|
}).strict();
|
|
19945
|
+
var ManagerEmailSetupPrefill = external_exports.object({
|
|
19946
|
+
/** Connection name as it will appear under Integrations. */
|
|
19947
|
+
name: external_exports.string().min(1).max(120).optional(),
|
|
19948
|
+
provider: EmailProviderPreset.optional(),
|
|
19949
|
+
mode: EmailConnectionMode.optional(),
|
|
19950
|
+
fromName: external_exports.string().max(200).optional(),
|
|
19951
|
+
fromAddress: external_exports.string().max(320).optional(),
|
|
19952
|
+
replyTo: external_exports.string().max(320).optional(),
|
|
19953
|
+
usageNotes: external_exports.string().max(4e3).optional(),
|
|
19954
|
+
/** custom provider only; presets carry their own servers. */
|
|
19955
|
+
smtpHost: external_exports.string().max(255).optional(),
|
|
19956
|
+
smtpPort: external_exports.number().int().min(1).max(65535).optional(),
|
|
19957
|
+
incomingHost: external_exports.string().max(255).optional(),
|
|
19958
|
+
incomingPort: external_exports.number().int().min(1).max(65535).optional(),
|
|
19959
|
+
/** Sign-in name for the mailbox, when the person said it. Never a password. */
|
|
19960
|
+
username: external_exports.string().max(320).optional(),
|
|
19961
|
+
watch: external_exports.object({
|
|
19962
|
+
enabled: external_exports.boolean(),
|
|
19963
|
+
senderContains: external_exports.string().max(200).optional(),
|
|
19964
|
+
recipientContains: external_exports.string().max(200).optional(),
|
|
19965
|
+
subjectContains: external_exports.string().max(200).optional(),
|
|
19966
|
+
instructions: external_exports.string().max(2e4).optional()
|
|
19967
|
+
}).strict().optional()
|
|
19968
|
+
}).strict();
|
|
19969
|
+
var ManagerEmailSetupStatus = external_exports.enum(["open", "completed", "dismissed"]);
|
|
19970
|
+
var ManagerEmailSetupProjection = external_exports.object({
|
|
19971
|
+
id: ManagerEmailSetupId,
|
|
19972
|
+
conversationId: ConversationId,
|
|
19973
|
+
prefill: ManagerEmailSetupPrefill,
|
|
19974
|
+
status: ManagerEmailSetupStatus,
|
|
19975
|
+
/** True when the requesting member is the person this card was prepared for. */
|
|
19976
|
+
mine: external_exports.boolean(),
|
|
19977
|
+
/** The Email connection the card produced; present once completed. */
|
|
19978
|
+
connectionId: external_exports.string().nullable(),
|
|
19979
|
+
connectionName: external_exports.string().max(120).nullable(),
|
|
19980
|
+
createdAt: external_exports.string()
|
|
19981
|
+
}).strict();
|
|
19982
|
+
var CompleteManagerEmailSetupRequest = external_exports.object({ connectionId: external_exports.string().min(1).max(100) }).strict();
|
|
19937
19983
|
var ManagerCredentialRequestStatus = external_exports.enum([
|
|
19938
19984
|
"awaiting_entry",
|
|
19939
19985
|
"storing",
|
|
@@ -22562,6 +22608,12 @@ var CompanySetupProgress = external_exports.object({
|
|
|
22562
22608
|
/** Every recommended step is done or skipped. */
|
|
22563
22609
|
recommendedSettled: external_exports.boolean()
|
|
22564
22610
|
});
|
|
22611
|
+
var COMPANY_SETUP_PLANNING_NOTE_MAX = 200;
|
|
22612
|
+
var COMPANY_SETUP_PLANNING_NOTES_MAX = 20;
|
|
22613
|
+
var CompanySetupPlanningNote = external_exports.object({
|
|
22614
|
+
at: IsoDate,
|
|
22615
|
+
text: external_exports.string().trim().min(1).max(COMPANY_SETUP_PLANNING_NOTE_MAX)
|
|
22616
|
+
});
|
|
22565
22617
|
var CompanySetupApproval = external_exports.object({
|
|
22566
22618
|
at: IsoDate,
|
|
22567
22619
|
by: MemberId.nullable(),
|
|
@@ -22614,7 +22666,12 @@ var CompanySetupView = external_exports.object({
|
|
|
22614
22666
|
* The plan is being proposed in the background (a planner call can take a
|
|
22615
22667
|
* minute); the page polls until the plan lands or an error is recorded.
|
|
22616
22668
|
*/
|
|
22617
|
-
planning: external_exports.object({
|
|
22669
|
+
planning: external_exports.object({
|
|
22670
|
+
startedAt: IsoDate,
|
|
22671
|
+
error: external_exports.string().max(500).nullable(),
|
|
22672
|
+
/** Oldest first; the planner's visible decisions so far. */
|
|
22673
|
+
notes: external_exports.array(CompanySetupPlanningNote).max(COMPANY_SETUP_PLANNING_NOTES_MAX).default([])
|
|
22674
|
+
}).nullable(),
|
|
22618
22675
|
approval: CompanySetupApproval.nullable(),
|
|
22619
22676
|
steps: external_exports.array(CompanySetupStep),
|
|
22620
22677
|
progress: CompanySetupProgress,
|
|
@@ -31705,11 +31762,11 @@ function createPlaywrightBrowserAdapterFactory(dependencies = {}) {
|
|
|
31705
31762
|
}
|
|
31706
31763
|
|
|
31707
31764
|
// src/runners/cli-runner.ts
|
|
31708
|
-
import { spawn as
|
|
31709
|
-
import { randomUUID as
|
|
31710
|
-
import { lstat as lstat11, mkdir as
|
|
31765
|
+
import { spawn as spawn11 } from "node:child_process";
|
|
31766
|
+
import { randomUUID as randomUUID12 } from "node:crypto";
|
|
31767
|
+
import { lstat as lstat11, mkdir as mkdir13, realpath as realpath8 } from "node:fs/promises";
|
|
31711
31768
|
import { homedir as homedir7 } from "node:os";
|
|
31712
|
-
import { dirname as dirname10, isAbsolute as isAbsolute16, join as
|
|
31769
|
+
import { dirname as dirname10, isAbsolute as isAbsolute16, join as join19, resolve as resolve10 } from "node:path";
|
|
31713
31770
|
|
|
31714
31771
|
// src/tool-packs/browser/authentication-wall.ts
|
|
31715
31772
|
var AUTH_PATH_SEGMENT = /(?:^|\/)(?:log[-_]?in|sign[-_]?in|sso|saml|auth|authorize|authenticate|oauth2?|session\/new|checkpoint)(?:\/|$)/i;
|
|
@@ -38096,6 +38153,415 @@ function renderAttachmentSection(files) {
|
|
|
38096
38153
|
|
|
38097
38154
|
// src/runners/ask-user-server.ts
|
|
38098
38155
|
import { createServer as createServer2 } from "node:http";
|
|
38156
|
+
|
|
38157
|
+
// src/runners/software-install.ts
|
|
38158
|
+
import { spawn as spawn9 } from "node:child_process";
|
|
38159
|
+
import { createHash as createHash6, randomUUID as randomUUID11 } from "node:crypto";
|
|
38160
|
+
import { chmod as chmod6, mkdir as mkdir11, readFile as readFile10, readdir as readdir5, rename as rename6, rm as rm10, symlink as symlink2, writeFile as writeFile7 } from "node:fs/promises";
|
|
38161
|
+
import { join as join17, relative as relative8, resolve as resolvePath, sep as sep5 } from "node:path";
|
|
38162
|
+
var SOFTWARE_INSTALL_METHODS = ["npm", "download"];
|
|
38163
|
+
var SOFTWARE_INSTALL_REFUSAL = "Zixt installs software only into its own folder on this Machine, using npm or a direct download. It never runs a system package manager, never asks for elevation, and never builds from source. Tell the person plainly what is missing, why this work needs it, and the exact command they would run themselves.";
|
|
38164
|
+
var MAX_DOWNLOAD_BYTES = 512 * 1024 * 1024;
|
|
38165
|
+
var PROCESS_TIMEOUT_MS = 10 * 6e4;
|
|
38166
|
+
var DOWNLOAD_TIMEOUT_MS = 10 * 6e4;
|
|
38167
|
+
var FAILURE_COOLDOWN_MS2 = 10 * 6e4;
|
|
38168
|
+
var MAX_ARCHIVE_SEARCH_DEPTH = 5;
|
|
38169
|
+
var MANIFEST_NAME = "installed.json";
|
|
38170
|
+
var COMMAND_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._+-]{0,63}$/;
|
|
38171
|
+
var NPM_SPEC_PATTERN = /^(?:@[a-z0-9~][a-z0-9._~-]*\/)?[a-z0-9~][a-z0-9._~-]*(?:@\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?)?$/;
|
|
38172
|
+
function defaultSoftwareToolsRoot() {
|
|
38173
|
+
return defaultRunnerToolsRoot();
|
|
38174
|
+
}
|
|
38175
|
+
function softwareSearchPathEntries(toolsRoot = defaultSoftwareToolsRoot()) {
|
|
38176
|
+
return [toolsRoot, join17(toolsRoot, "bin")];
|
|
38177
|
+
}
|
|
38178
|
+
function refuseSoftwareRequest(request) {
|
|
38179
|
+
if (!COMMAND_PATTERN.test(request.command)) {
|
|
38180
|
+
return "The command name must be a plain executable name with no path, spaces, or shell characters.";
|
|
38181
|
+
}
|
|
38182
|
+
if (!SOFTWARE_INSTALL_METHODS.includes(request.method)) {
|
|
38183
|
+
return SOFTWARE_INSTALL_REFUSAL;
|
|
38184
|
+
}
|
|
38185
|
+
if (request.method === "npm") {
|
|
38186
|
+
if (!NPM_SPEC_PATTERN.test(request.source)) {
|
|
38187
|
+
return "The npm source must be a package name, optionally with one exact version as name@1.2.3. Ranges, tags, git specs, URLs, and local paths are not installed.";
|
|
38188
|
+
}
|
|
38189
|
+
if (request.sha256 !== void 0) {
|
|
38190
|
+
return "An expected sha256 applies only to downloads; the npm registry names versions, not file digests.";
|
|
38191
|
+
}
|
|
38192
|
+
return null;
|
|
38193
|
+
}
|
|
38194
|
+
let url3;
|
|
38195
|
+
try {
|
|
38196
|
+
url3 = new URL(request.source);
|
|
38197
|
+
} catch {
|
|
38198
|
+
return "The download source must be an absolute HTTPS URL.";
|
|
38199
|
+
}
|
|
38200
|
+
if (url3.protocol !== "https:") return "The download source must use HTTPS.";
|
|
38201
|
+
if (url3.username || url3.password) {
|
|
38202
|
+
return "The download source must not carry credentials in the URL.";
|
|
38203
|
+
}
|
|
38204
|
+
if (request.sha256 !== void 0 && !/^[a-f0-9]{64}$/i.test(request.sha256)) {
|
|
38205
|
+
return "The expected sha256 must be 64 hexadecimal characters.";
|
|
38206
|
+
}
|
|
38207
|
+
return null;
|
|
38208
|
+
}
|
|
38209
|
+
async function runProcess(plan, label) {
|
|
38210
|
+
const env = sanitizedInstallerEnv(process.env);
|
|
38211
|
+
await new Promise((resolveRun, rejectRun) => {
|
|
38212
|
+
const child = plan.shellLine !== void 0 ? spawn9(plan.shellLine, {
|
|
38213
|
+
...plan.cwd ? { cwd: plan.cwd } : {},
|
|
38214
|
+
env,
|
|
38215
|
+
// stdin stays closed; output reaches the ordinary worker console, so
|
|
38216
|
+
// progress and failure land on the Host console like a runner install.
|
|
38217
|
+
stdio: ["ignore", "inherit", "inherit"],
|
|
38218
|
+
windowsHide: true,
|
|
38219
|
+
shell: true
|
|
38220
|
+
}) : spawn9(plan.command, plan.args, {
|
|
38221
|
+
...plan.cwd ? { cwd: plan.cwd } : {},
|
|
38222
|
+
env,
|
|
38223
|
+
stdio: ["ignore", "inherit", "inherit"],
|
|
38224
|
+
windowsHide: true
|
|
38225
|
+
});
|
|
38226
|
+
let settled = false;
|
|
38227
|
+
const finish = (error52) => {
|
|
38228
|
+
if (settled) return;
|
|
38229
|
+
settled = true;
|
|
38230
|
+
clearTimeout(timer);
|
|
38231
|
+
if (error52) rejectRun(error52);
|
|
38232
|
+
else resolveRun();
|
|
38233
|
+
};
|
|
38234
|
+
const timer = setTimeout(() => {
|
|
38235
|
+
child.kill();
|
|
38236
|
+
finish(new Error(`${label} did not finish within 10 minutes`));
|
|
38237
|
+
}, PROCESS_TIMEOUT_MS);
|
|
38238
|
+
timer.unref?.();
|
|
38239
|
+
child.once("error", (error52) => finish(error52));
|
|
38240
|
+
child.once("exit", (code, signal) => {
|
|
38241
|
+
if (code === 0) finish();
|
|
38242
|
+
else if (signal) finish(new Error(`${label} stopped with ${signal}`));
|
|
38243
|
+
else finish(new Error(`${label} exited with code ${code ?? "unknown"}`));
|
|
38244
|
+
});
|
|
38245
|
+
});
|
|
38246
|
+
}
|
|
38247
|
+
async function resolveTarCommand(platform) {
|
|
38248
|
+
if (platform !== "win32") return "tar";
|
|
38249
|
+
const root = process.env["SYSTEMROOT"] ?? process.env["WINDIR"];
|
|
38250
|
+
if (!root) return "tar";
|
|
38251
|
+
return await resolveTrustedCliCommand(join17(root, "System32", "tar.exe"), { platform }) ?? "tar";
|
|
38252
|
+
}
|
|
38253
|
+
function archiveKindFor(url3) {
|
|
38254
|
+
let path;
|
|
38255
|
+
try {
|
|
38256
|
+
path = new URL(url3).pathname.toLowerCase();
|
|
38257
|
+
} catch {
|
|
38258
|
+
return "binary";
|
|
38259
|
+
}
|
|
38260
|
+
if (/\.(?:tar\.(?:gz|xz|bz2)|tgz|tbz2|txz|tar)$/.test(path)) return "tar";
|
|
38261
|
+
if (path.endsWith(".zip")) return "zip";
|
|
38262
|
+
return "binary";
|
|
38263
|
+
}
|
|
38264
|
+
async function findExtractedExecutable(root, command, platform = process.platform) {
|
|
38265
|
+
const names = platform === "win32" ? [`${command}.exe`, `${command}.cmd`, `${command}.bat`, command] : [command];
|
|
38266
|
+
let frontier = [root];
|
|
38267
|
+
for (let depth = 0; depth <= MAX_ARCHIVE_SEARCH_DEPTH && frontier.length > 0; depth += 1) {
|
|
38268
|
+
const next = [];
|
|
38269
|
+
for (const directory of frontier) {
|
|
38270
|
+
let entries;
|
|
38271
|
+
try {
|
|
38272
|
+
entries = await readdir5(directory, { withFileTypes: true });
|
|
38273
|
+
} catch {
|
|
38274
|
+
continue;
|
|
38275
|
+
}
|
|
38276
|
+
for (const name of names) {
|
|
38277
|
+
if (entries.some((entry) => entry.name === name && entry.isFile())) {
|
|
38278
|
+
return join17(directory, name);
|
|
38279
|
+
}
|
|
38280
|
+
}
|
|
38281
|
+
for (const entry of entries) {
|
|
38282
|
+
if (entry.isDirectory()) next.push(join17(directory, entry.name));
|
|
38283
|
+
}
|
|
38284
|
+
}
|
|
38285
|
+
frontier = next;
|
|
38286
|
+
}
|
|
38287
|
+
return null;
|
|
38288
|
+
}
|
|
38289
|
+
function withinRoot(root, candidate) {
|
|
38290
|
+
const base = resolvePath(root);
|
|
38291
|
+
const target = resolvePath(candidate);
|
|
38292
|
+
return target === base || target.startsWith(base.endsWith(sep5) ? base : `${base}${sep5}`);
|
|
38293
|
+
}
|
|
38294
|
+
function downloadedBinaryName(command, source, platform = process.platform) {
|
|
38295
|
+
if (platform !== "win32") return command;
|
|
38296
|
+
const extension = /(\.[A-Za-z0-9]{1,8})$/.exec(new URL(source).pathname)?.[1]?.toLowerCase();
|
|
38297
|
+
if (extension !== ".exe" && extension !== ".com") {
|
|
38298
|
+
throw new Error(
|
|
38299
|
+
"a direct Windows download must be a .exe or .com file; use the tool\u2019s .zip release instead"
|
|
38300
|
+
);
|
|
38301
|
+
}
|
|
38302
|
+
return `${command}${extension}`;
|
|
38303
|
+
}
|
|
38304
|
+
async function placeDownloadedPayload(body, request, options) {
|
|
38305
|
+
const platform = options.platform ?? process.platform;
|
|
38306
|
+
const { toolsRoot } = options;
|
|
38307
|
+
const staging = join17(toolsRoot, ".staging", randomUUID11());
|
|
38308
|
+
const stagedPackage = join17(staging, "pkg");
|
|
38309
|
+
const packageRoot = join17(toolsRoot, "pkgs", request.command);
|
|
38310
|
+
const binDirectory = platform === "win32" ? toolsRoot : join17(toolsRoot, "bin");
|
|
38311
|
+
const kind = archiveKindFor(request.source);
|
|
38312
|
+
await mkdir11(stagedPackage, { recursive: true, mode: 448 });
|
|
38313
|
+
try {
|
|
38314
|
+
if (kind === "binary") {
|
|
38315
|
+
await writeFile7(
|
|
38316
|
+
join17(stagedPackage, downloadedBinaryName(request.command, request.source, platform)),
|
|
38317
|
+
body,
|
|
38318
|
+
{
|
|
38319
|
+
mode: 448
|
|
38320
|
+
}
|
|
38321
|
+
);
|
|
38322
|
+
} else {
|
|
38323
|
+
await writeFile7(join17(staging, "archive"), body, { mode: 448 });
|
|
38324
|
+
if (kind === "tar" || platform !== "linux") {
|
|
38325
|
+
await runProcess(
|
|
38326
|
+
{
|
|
38327
|
+
command: await resolveTarCommand(platform),
|
|
38328
|
+
args: ["-xf", "archive", "-C", "pkg"],
|
|
38329
|
+
cwd: staging
|
|
38330
|
+
},
|
|
38331
|
+
"the archive extraction"
|
|
38332
|
+
);
|
|
38333
|
+
} else {
|
|
38334
|
+
await runProcess(
|
|
38335
|
+
{ command: "unzip", args: ["-q", "-o", "archive", "-d", "pkg"], cwd: staging },
|
|
38336
|
+
"the archive extraction"
|
|
38337
|
+
);
|
|
38338
|
+
}
|
|
38339
|
+
await rm10(join17(staging, "archive"), { force: true }).catch(() => void 0);
|
|
38340
|
+
}
|
|
38341
|
+
const stagedExecutable = await findExtractedExecutable(
|
|
38342
|
+
stagedPackage,
|
|
38343
|
+
request.command,
|
|
38344
|
+
platform
|
|
38345
|
+
);
|
|
38346
|
+
if (!stagedExecutable) {
|
|
38347
|
+
throw new Error(`the download did not contain an executable named ${request.command}`);
|
|
38348
|
+
}
|
|
38349
|
+
if (!withinRoot(stagedPackage, stagedExecutable)) {
|
|
38350
|
+
throw new Error("the archive tried to place its executable outside the Zixt folder");
|
|
38351
|
+
}
|
|
38352
|
+
await mkdir11(join17(toolsRoot, "pkgs"), { recursive: true, mode: 448 });
|
|
38353
|
+
await rm10(packageRoot, { recursive: true, force: true });
|
|
38354
|
+
await rename6(stagedPackage, packageRoot);
|
|
38355
|
+
const executable = join17(packageRoot, relative8(stagedPackage, stagedExecutable));
|
|
38356
|
+
await mkdir11(binDirectory, { recursive: true, mode: 448 });
|
|
38357
|
+
if (platform === "win32") {
|
|
38358
|
+
const shim = join17(binDirectory, `${request.command}.cmd`);
|
|
38359
|
+
await writeFile7(shim, `@echo off\r
|
|
38360
|
+
${quoteForCmd(executable)} %*\r
|
|
38361
|
+
`, {
|
|
38362
|
+
encoding: "utf8",
|
|
38363
|
+
mode: 448
|
|
38364
|
+
});
|
|
38365
|
+
return shim;
|
|
38366
|
+
}
|
|
38367
|
+
await chmod6(executable, 493);
|
|
38368
|
+
const link = join17(binDirectory, request.command);
|
|
38369
|
+
await rm10(link, { force: true });
|
|
38370
|
+
await symlink2(executable, link);
|
|
38371
|
+
return link;
|
|
38372
|
+
} finally {
|
|
38373
|
+
await rm10(staging, { recursive: true, force: true }).catch(() => void 0);
|
|
38374
|
+
}
|
|
38375
|
+
}
|
|
38376
|
+
function createSoftwareInstaller(options = {}) {
|
|
38377
|
+
const platform = options.platform ?? process.platform;
|
|
38378
|
+
const toolsRoot = options.toolsRoot ?? defaultSoftwareToolsRoot();
|
|
38379
|
+
const now = options.now ?? Date.now;
|
|
38380
|
+
const manifestPath = join17(toolsRoot, MANIFEST_NAME);
|
|
38381
|
+
const inFlight = /* @__PURE__ */ new Map();
|
|
38382
|
+
const failures = /* @__PURE__ */ new Map();
|
|
38383
|
+
const searchPath = () => {
|
|
38384
|
+
const inherited = options.resolution?.searchPath ?? process.env.PATH ?? "";
|
|
38385
|
+
const separator = platform === "win32" ? ";" : ":";
|
|
38386
|
+
return [inherited, ...softwareSearchPathEntries(toolsRoot)].filter(Boolean).join(separator);
|
|
38387
|
+
};
|
|
38388
|
+
const resolveCommand = async (command) => resolveTrustedCliCommand(command, {
|
|
38389
|
+
...options.resolution ?? {},
|
|
38390
|
+
platform,
|
|
38391
|
+
searchPath: searchPath()
|
|
38392
|
+
});
|
|
38393
|
+
const readManifest = async () => {
|
|
38394
|
+
try {
|
|
38395
|
+
const parsed = JSON.parse(await readFile10(manifestPath, "utf8"));
|
|
38396
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
38397
|
+
} catch {
|
|
38398
|
+
return [];
|
|
38399
|
+
}
|
|
38400
|
+
};
|
|
38401
|
+
let manifestTurn = Promise.resolve();
|
|
38402
|
+
const recordInstall = (record2) => {
|
|
38403
|
+
const write = manifestTurn.then(async () => {
|
|
38404
|
+
const existing = (await readManifest()).filter((entry) => entry.command !== record2.command);
|
|
38405
|
+
const staged = `${manifestPath}.${randomUUID11()}`;
|
|
38406
|
+
await writeFile7(staged, `${JSON.stringify([...existing, record2], null, 2)}
|
|
38407
|
+
`, {
|
|
38408
|
+
encoding: "utf8",
|
|
38409
|
+
mode: 384
|
|
38410
|
+
});
|
|
38411
|
+
await rename6(staged, manifestPath);
|
|
38412
|
+
});
|
|
38413
|
+
manifestTurn = write.catch(() => void 0);
|
|
38414
|
+
return write;
|
|
38415
|
+
};
|
|
38416
|
+
const installFromNpm = async (request) => {
|
|
38417
|
+
await mkdir11(toolsRoot, { recursive: true, mode: 448 });
|
|
38418
|
+
const npm = await resolveInstallerCommand({ platform });
|
|
38419
|
+
const args = [
|
|
38420
|
+
"install",
|
|
38421
|
+
"-g",
|
|
38422
|
+
"--prefix",
|
|
38423
|
+
toolsRoot,
|
|
38424
|
+
"--no-audit",
|
|
38425
|
+
"--no-fund",
|
|
38426
|
+
"--ignore-scripts",
|
|
38427
|
+
// `--` ends npm's flag parsing, so even a spec that slipped the pattern
|
|
38428
|
+
// could not become an npm option.
|
|
38429
|
+
"--",
|
|
38430
|
+
request.source
|
|
38431
|
+
];
|
|
38432
|
+
if (platform === "win32") {
|
|
38433
|
+
await runProcess(
|
|
38434
|
+
{ shellLine: windowsInstallerCommandLine(npm, args), cwd: toolsRoot },
|
|
38435
|
+
"the npm install"
|
|
38436
|
+
);
|
|
38437
|
+
} else {
|
|
38438
|
+
await runProcess({ command: npm, args, cwd: toolsRoot }, "the npm install");
|
|
38439
|
+
}
|
|
38440
|
+
};
|
|
38441
|
+
const installFromDownload = async (request) => {
|
|
38442
|
+
const response = await fetch(request.source, {
|
|
38443
|
+
redirect: "follow",
|
|
38444
|
+
signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS)
|
|
38445
|
+
});
|
|
38446
|
+
if (!response.ok) throw new Error(`the download answered HTTP ${response.status}`);
|
|
38447
|
+
if (response.url && !response.url.startsWith("https:")) {
|
|
38448
|
+
throw new Error("the download redirected off HTTPS and was not installed");
|
|
38449
|
+
}
|
|
38450
|
+
const declared = Number(response.headers.get("content-length") ?? "0");
|
|
38451
|
+
if (Number.isFinite(declared) && declared > MAX_DOWNLOAD_BYTES) {
|
|
38452
|
+
throw new Error("the download is larger than Zixt installs unattended");
|
|
38453
|
+
}
|
|
38454
|
+
if (!response.body) throw new Error("the download had no body");
|
|
38455
|
+
const reader = response.body.getReader();
|
|
38456
|
+
const chunks = [];
|
|
38457
|
+
let total = 0;
|
|
38458
|
+
for (; ; ) {
|
|
38459
|
+
const { done, value } = await reader.read();
|
|
38460
|
+
if (done) break;
|
|
38461
|
+
total += value.byteLength;
|
|
38462
|
+
if (total > MAX_DOWNLOAD_BYTES) {
|
|
38463
|
+
await reader.cancel().catch(() => void 0);
|
|
38464
|
+
throw new Error("the download is larger than Zixt installs unattended");
|
|
38465
|
+
}
|
|
38466
|
+
chunks.push(Buffer.from(value));
|
|
38467
|
+
}
|
|
38468
|
+
const body = Buffer.concat(chunks);
|
|
38469
|
+
if (body.length === 0) throw new Error("the download was empty");
|
|
38470
|
+
const digest = createHash6("sha256").update(body).digest("hex");
|
|
38471
|
+
if (request.sha256 && digest !== request.sha256.toLowerCase()) {
|
|
38472
|
+
throw new Error("the download did not match the expected sha256");
|
|
38473
|
+
}
|
|
38474
|
+
await placeDownloadedPayload(body, request, { toolsRoot, platform });
|
|
38475
|
+
};
|
|
38476
|
+
const perform = options.perform ?? (async (request) => {
|
|
38477
|
+
if (request.method === "npm") await installFromNpm(request);
|
|
38478
|
+
else await installFromDownload(request);
|
|
38479
|
+
const resolved = await resolveCommand(request.command);
|
|
38480
|
+
if (!resolved) {
|
|
38481
|
+
throw new Error(
|
|
38482
|
+
`the install finished but ${request.command} is still not runnable on this Machine`
|
|
38483
|
+
);
|
|
38484
|
+
}
|
|
38485
|
+
return resolved;
|
|
38486
|
+
});
|
|
38487
|
+
const attempt = async (request) => {
|
|
38488
|
+
options.onEvent?.({ command: request.command, state: "started" });
|
|
38489
|
+
try {
|
|
38490
|
+
const path = await perform(request, toolsRoot);
|
|
38491
|
+
if (!withinRoot(toolsRoot, path)) {
|
|
38492
|
+
throw new Error(
|
|
38493
|
+
`${request.command} resolves outside the Zixt folder and was not installed by Zixt`
|
|
38494
|
+
);
|
|
38495
|
+
}
|
|
38496
|
+
const record2 = {
|
|
38497
|
+
command: request.command,
|
|
38498
|
+
method: request.method,
|
|
38499
|
+
source: request.source,
|
|
38500
|
+
path,
|
|
38501
|
+
sha256: request.sha256 ? request.sha256.toLowerCase() : null,
|
|
38502
|
+
installedAt: new Date(now()).toISOString()
|
|
38503
|
+
};
|
|
38504
|
+
await recordInstall(record2).catch(() => void 0);
|
|
38505
|
+
options.onEvent?.({ command: request.command, state: "completed" });
|
|
38506
|
+
return { ok: true, record: record2 };
|
|
38507
|
+
} catch (error52) {
|
|
38508
|
+
const message = error52 instanceof Error ? error52.message : "unknown software install error";
|
|
38509
|
+
options.onEvent?.({ command: request.command, state: "failed", error: message });
|
|
38510
|
+
return { ok: false, error: message };
|
|
38511
|
+
}
|
|
38512
|
+
};
|
|
38513
|
+
return {
|
|
38514
|
+
toolsRoot,
|
|
38515
|
+
async probe(command) {
|
|
38516
|
+
if (!COMMAND_PATTERN.test(command)) {
|
|
38517
|
+
return { command, present: false, path: null, managed: false };
|
|
38518
|
+
}
|
|
38519
|
+
const path = await resolveCommand(command);
|
|
38520
|
+
return {
|
|
38521
|
+
command,
|
|
38522
|
+
present: path !== null,
|
|
38523
|
+
path,
|
|
38524
|
+
managed: path !== null && withinRoot(toolsRoot, path)
|
|
38525
|
+
};
|
|
38526
|
+
},
|
|
38527
|
+
async install(request) {
|
|
38528
|
+
const refusal = refuseSoftwareRequest(request);
|
|
38529
|
+
if (refusal) return { ok: false, error: refusal };
|
|
38530
|
+
const key = [
|
|
38531
|
+
request.command,
|
|
38532
|
+
request.method,
|
|
38533
|
+
request.source,
|
|
38534
|
+
request.sha256?.toLowerCase() ?? ""
|
|
38535
|
+
].join("\n");
|
|
38536
|
+
const running = inFlight.get(key);
|
|
38537
|
+
if (running) return running;
|
|
38538
|
+
const failure2 = failures.get(key);
|
|
38539
|
+
if (failure2 && now() - failure2.at < FAILURE_COOLDOWN_MS2) {
|
|
38540
|
+
return {
|
|
38541
|
+
ok: false,
|
|
38542
|
+
error: `This exact install already failed on this Machine: ${failure2.error}. Do not repeat it. Finish the work another way, or tell the person what is missing and the command they would run.`
|
|
38543
|
+
};
|
|
38544
|
+
}
|
|
38545
|
+
const started = (async () => {
|
|
38546
|
+
const result = await attempt(request);
|
|
38547
|
+
if (result.ok) failures.delete(key);
|
|
38548
|
+
else failures.set(key, { at: now(), error: result.error });
|
|
38549
|
+
return result;
|
|
38550
|
+
})();
|
|
38551
|
+
inFlight.set(key, started);
|
|
38552
|
+
try {
|
|
38553
|
+
return await started;
|
|
38554
|
+
} finally {
|
|
38555
|
+
inFlight.delete(key);
|
|
38556
|
+
}
|
|
38557
|
+
},
|
|
38558
|
+
async installed() {
|
|
38559
|
+
return readManifest();
|
|
38560
|
+
}
|
|
38561
|
+
};
|
|
38562
|
+
}
|
|
38563
|
+
|
|
38564
|
+
// src/runners/ask-user-server.ts
|
|
38099
38565
|
var CADENCE_PROPS = {
|
|
38100
38566
|
cadence_kind: {
|
|
38101
38567
|
type: "string",
|
|
@@ -38211,6 +38677,55 @@ var TOOLS = [
|
|
|
38211
38677
|
additionalProperties: false
|
|
38212
38678
|
}
|
|
38213
38679
|
},
|
|
38680
|
+
{
|
|
38681
|
+
name: "check_software",
|
|
38682
|
+
description: "Check whether a command is already available on this Machine before assuming it is missing. Returns whether it is present, where it resolved, and whether Zixt is the one that installed it.",
|
|
38683
|
+
inputSchema: {
|
|
38684
|
+
type: "object",
|
|
38685
|
+
properties: {
|
|
38686
|
+
command: {
|
|
38687
|
+
type: "string",
|
|
38688
|
+
description: "The plain command name, for example ffmpeg or gh. No path, no arguments."
|
|
38689
|
+
}
|
|
38690
|
+
},
|
|
38691
|
+
required: ["command"],
|
|
38692
|
+
additionalProperties: false
|
|
38693
|
+
}
|
|
38694
|
+
},
|
|
38695
|
+
{
|
|
38696
|
+
name: "install_software",
|
|
38697
|
+
description: "Install a command this Machine is missing, into the folder Zixt owns, after the person approves it. Prefer not to call this. Installing software on someone else\u2019s computer is friction, so first try to finish the work with what the Machine already has: a different tool, a project dependency, or another approach. Call it only when the missing command genuinely blocks the work or is clearly the right way to do it, and never speculatively. Zixt can install an npm package, or download and unpack a release from an HTTPS URL. It cannot use a system package manager such as apt, brew, winget, or choco, cannot ask for elevation or sudo, and cannot build from source. When the tool needs any of those, or the install looks complicated, do not call this: tell the person what is missing, why this work needs it, and the exact command they would run on their operating system. The person sees your reason and the exact source before anything runs, and may say no.",
|
|
38698
|
+
inputSchema: {
|
|
38699
|
+
type: "object",
|
|
38700
|
+
properties: {
|
|
38701
|
+
command: {
|
|
38702
|
+
type: "string",
|
|
38703
|
+
description: "The plain command name the work needs on PATH, for example ffmpeg."
|
|
38704
|
+
},
|
|
38705
|
+
reason: {
|
|
38706
|
+
type: "string",
|
|
38707
|
+
minLength: 1,
|
|
38708
|
+
maxLength: 500,
|
|
38709
|
+
description: "Why this work needs it, in one plain sentence. The person reads this before deciding."
|
|
38710
|
+
},
|
|
38711
|
+
method: {
|
|
38712
|
+
type: "string",
|
|
38713
|
+
enum: [...SOFTWARE_INSTALL_METHODS],
|
|
38714
|
+
description: "npm for an npm package; download for a release archive or binary."
|
|
38715
|
+
},
|
|
38716
|
+
source: {
|
|
38717
|
+
type: "string",
|
|
38718
|
+
description: "npm: the package name, optionally name@version with an exact version. download: the HTTPS URL of the release archive or binary."
|
|
38719
|
+
},
|
|
38720
|
+
sha256: {
|
|
38721
|
+
type: "string",
|
|
38722
|
+
description: "download only: the published SHA-256 of that file, when the project publishes one. Include it whenever you can."
|
|
38723
|
+
}
|
|
38724
|
+
},
|
|
38725
|
+
required: ["command", "reason", "method", "source"],
|
|
38726
|
+
additionalProperties: false
|
|
38727
|
+
}
|
|
38728
|
+
},
|
|
38214
38729
|
{
|
|
38215
38730
|
name: "publish_file",
|
|
38216
38731
|
description: "Snapshot a file you created in this Task workspace into Zixt so the user can download it. This only creates the Zixt file; it does not send it anywhere externally. The result returns an artifact_id.",
|
|
@@ -38996,6 +39511,57 @@ function createAskUserServer() {
|
|
|
38996
39511
|
}
|
|
38997
39512
|
return;
|
|
38998
39513
|
}
|
|
39514
|
+
if (surface.platform && (name === "check_software" || name === "install_software")) {
|
|
39515
|
+
if (!handlers.software) {
|
|
39516
|
+
toolText("Software checks are unavailable for this runner.", true);
|
|
39517
|
+
return;
|
|
39518
|
+
}
|
|
39519
|
+
const command = typeof args["command"] === "string" ? args["command"].trim() : "";
|
|
39520
|
+
if (!command) {
|
|
39521
|
+
toolText("missing required argument `command`", true);
|
|
39522
|
+
return;
|
|
39523
|
+
}
|
|
39524
|
+
try {
|
|
39525
|
+
if (name === "check_software") {
|
|
39526
|
+
toolText(JSON.stringify(await handlers.software.check(command), null, 2));
|
|
39527
|
+
return;
|
|
39528
|
+
}
|
|
39529
|
+
const reason = typeof args["reason"] === "string" ? args["reason"].trim() : "";
|
|
39530
|
+
const method = typeof args["method"] === "string" ? args["method"] : "";
|
|
39531
|
+
const source = typeof args["source"] === "string" ? args["source"].trim() : "";
|
|
39532
|
+
const sha256 = typeof args["sha256"] === "string" && args["sha256"] ? args["sha256"] : void 0;
|
|
39533
|
+
if (!reason) {
|
|
39534
|
+
toolText(
|
|
39535
|
+
"missing required argument `reason`: the person decides by reading why this work needs it",
|
|
39536
|
+
true
|
|
39537
|
+
);
|
|
39538
|
+
return;
|
|
39539
|
+
}
|
|
39540
|
+
if (!SOFTWARE_INSTALL_METHODS.includes(method)) {
|
|
39541
|
+
toolText(`\`method\` must be one of ${SOFTWARE_INSTALL_METHODS.join(", ")}.`, true);
|
|
39542
|
+
return;
|
|
39543
|
+
}
|
|
39544
|
+
const request = {
|
|
39545
|
+
command,
|
|
39546
|
+
method,
|
|
39547
|
+
source,
|
|
39548
|
+
sha256
|
|
39549
|
+
};
|
|
39550
|
+
const refusal = refuseSoftwareRequest(request);
|
|
39551
|
+
if (refusal) {
|
|
39552
|
+
toolText(refusal, true);
|
|
39553
|
+
return;
|
|
39554
|
+
}
|
|
39555
|
+
const outcome = await handlers.software.install({ ...request, reason });
|
|
39556
|
+
toolText(outcome.detail, !outcome.ok);
|
|
39557
|
+
} catch (err) {
|
|
39558
|
+
toolText(
|
|
39559
|
+
`The software request could not be completed: ${String(err instanceof Error ? err.message : err)}`,
|
|
39560
|
+
true
|
|
39561
|
+
);
|
|
39562
|
+
}
|
|
39563
|
+
return;
|
|
39564
|
+
}
|
|
38999
39565
|
if (surface.platform && name === "publish_file") {
|
|
39000
39566
|
if (!handlers.publishFile) {
|
|
39001
39567
|
toolText("file publishing is unavailable for this runner", true);
|
|
@@ -39111,6 +39677,7 @@ function createAskUserServer() {
|
|
|
39111
39677
|
agentOp: input.agentOp,
|
|
39112
39678
|
..."requestApproval" in input && input.requestApproval ? { requestApproval: input.requestApproval } : {},
|
|
39113
39679
|
..."publishFile" in input && input.publishFile ? { publishFile: input.publishFile } : {},
|
|
39680
|
+
..."software" in input && input.software ? { software: input.software } : {},
|
|
39114
39681
|
...toolPacks.length > 0 ? { toolPacks } : {}
|
|
39115
39682
|
};
|
|
39116
39683
|
const toolOwners = /* @__PURE__ */ new Map();
|
|
@@ -39263,7 +39830,10 @@ function buildRunnerEnv(input) {
|
|
|
39263
39830
|
env[name] = value;
|
|
39264
39831
|
}
|
|
39265
39832
|
}
|
|
39266
|
-
const searchPath =
|
|
39833
|
+
const searchPath = [
|
|
39834
|
+
sanitizeInheritedSearchPath(inheritedValue(input.inherited, "PATH")),
|
|
39835
|
+
...(input.softwareToolsPath ?? []).filter((entry) => isAbsolute14(entry))
|
|
39836
|
+
].filter((entry) => entry !== "").join(delimiter2);
|
|
39267
39837
|
const gitConfig = input.githubShell ? [
|
|
39268
39838
|
["credential.helper", ""],
|
|
39269
39839
|
["credential.helper", input.githubShell.gitCredentialHelper],
|
|
@@ -39330,9 +39900,9 @@ function buildRunnerEnv(input) {
|
|
|
39330
39900
|
// src/runners/github-shell-auth.ts
|
|
39331
39901
|
import { execFile } from "node:child_process";
|
|
39332
39902
|
import { randomBytes as randomBytes2, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
|
|
39333
|
-
import { chmod as
|
|
39903
|
+
import { chmod as chmod7, lstat as lstat10, mkdir as mkdir12, realpath as realpath7, writeFile as writeFile8 } from "node:fs/promises";
|
|
39334
39904
|
import { createServer as createServer3 } from "node:http";
|
|
39335
|
-
import { isAbsolute as isAbsolute15, join as
|
|
39905
|
+
import { isAbsolute as isAbsolute15, join as join18, relative as relative9 } from "node:path";
|
|
39336
39906
|
var MAX_REQUEST_BYTES2 = 16 * 1024;
|
|
39337
39907
|
var DIRECTORY_MODE4 = 448;
|
|
39338
39908
|
var PRIVATE_FILE_MODE = 384;
|
|
@@ -39696,7 +40266,7 @@ function activationCredential(grant, now = Date.now()) {
|
|
|
39696
40266
|
return expiresAt.getTime() <= now ? null : { accessToken: grant.accessToken, expiresAt };
|
|
39697
40267
|
}
|
|
39698
40268
|
function assertChildPath2(parent, child) {
|
|
39699
|
-
const path =
|
|
40269
|
+
const path = relative9(parent, child);
|
|
39700
40270
|
if (!path || path === ".." || path.startsWith("../") || path.startsWith("..\\") || isAbsolute15(path)) {
|
|
39701
40271
|
throw new Error("GitHub shell helper path escaped its private run directory");
|
|
39702
40272
|
}
|
|
@@ -39708,11 +40278,11 @@ function quoteForPosixShell(value) {
|
|
|
39708
40278
|
return quoteForGitShell2(value);
|
|
39709
40279
|
}
|
|
39710
40280
|
async function writePrivate(path, content, executable = false) {
|
|
39711
|
-
await
|
|
40281
|
+
await writeFile8(path, content, {
|
|
39712
40282
|
flag: "wx",
|
|
39713
40283
|
mode: executable ? EXECUTABLE_FILE_MODE : PRIVATE_FILE_MODE
|
|
39714
40284
|
});
|
|
39715
|
-
await
|
|
40285
|
+
await chmod7(path, executable ? EXECUTABLE_FILE_MODE : PRIVATE_FILE_MODE);
|
|
39716
40286
|
}
|
|
39717
40287
|
async function prepareHelpers(input) {
|
|
39718
40288
|
const rootEntry = await lstat10(input.runRoot);
|
|
@@ -39720,7 +40290,7 @@ async function prepareHelpers(input) {
|
|
|
39720
40290
|
throw new Error("GitHub shell authentication requires a private real run directory");
|
|
39721
40291
|
}
|
|
39722
40292
|
const runRoot = await realpath7(input.runRoot);
|
|
39723
|
-
const helperPath =
|
|
40293
|
+
const helperPath = join18(runRoot, "github-shell-git-credential.cjs");
|
|
39724
40294
|
assertChildPath2(runRoot, helperPath);
|
|
39725
40295
|
await writePrivate(helperPath, GIT_HELPER_SOURCE);
|
|
39726
40296
|
if (!input.ghExecutablePath) {
|
|
@@ -39731,14 +40301,14 @@ async function prepareHelpers(input) {
|
|
|
39731
40301
|
wrapperSourcePath: null
|
|
39732
40302
|
};
|
|
39733
40303
|
}
|
|
39734
|
-
const shellToolsDirectory =
|
|
40304
|
+
const shellToolsDirectory = join18(runRoot, "shell-tools");
|
|
39735
40305
|
assertChildPath2(runRoot, shellToolsDirectory);
|
|
39736
|
-
await
|
|
39737
|
-
await
|
|
39738
|
-
const wrapperSourcePath =
|
|
40306
|
+
await mkdir12(shellToolsDirectory, { mode: DIRECTORY_MODE4 });
|
|
40307
|
+
await chmod7(shellToolsDirectory, DIRECTORY_MODE4);
|
|
40308
|
+
const wrapperSourcePath = join18(runRoot, "github-shell-gh-wrapper.cjs");
|
|
39739
40309
|
assertChildPath2(runRoot, wrapperSourcePath);
|
|
39740
40310
|
await writePrivate(wrapperSourcePath, GH_WRAPPER_SOURCE);
|
|
39741
|
-
const wrapperPath =
|
|
40311
|
+
const wrapperPath = join18(shellToolsDirectory, process.platform === "win32" ? "gh.cmd" : "gh");
|
|
39742
40312
|
assertChildPath2(runRoot, wrapperPath);
|
|
39743
40313
|
const launcher = process.platform === "win32" ? `@"${process.execPath.replaceAll('"', '""')}" "${wrapperSourcePath.replaceAll('"', '""')}" %*\r
|
|
39744
40314
|
` : `#!/bin/sh
|
|
@@ -39961,7 +40531,7 @@ password=${credential.accessToken}
|
|
|
39961
40531
|
}
|
|
39962
40532
|
|
|
39963
40533
|
// src/runners/working-context.ts
|
|
39964
|
-
import { spawn as
|
|
40534
|
+
import { spawn as spawn10 } from "node:child_process";
|
|
39965
40535
|
import { resolve as resolve9 } from "node:path";
|
|
39966
40536
|
var COMMAND_TIMEOUT_MS = 5e3;
|
|
39967
40537
|
var OUTPUT_LIMIT_BYTES = 128 * 1024;
|
|
@@ -40092,7 +40662,7 @@ async function stopWorkingContextCommand(child, childExited, options = {}) {
|
|
|
40092
40662
|
function run(command, args, cwd, env, signal) {
|
|
40093
40663
|
if (signal?.aborted) return Promise.resolve(null);
|
|
40094
40664
|
return new Promise((resolvePromise) => {
|
|
40095
|
-
const child =
|
|
40665
|
+
const child = spawn10(command, [...args], {
|
|
40096
40666
|
cwd,
|
|
40097
40667
|
env: { ...env, GIT_OPTIONAL_LOCKS: "0" },
|
|
40098
40668
|
detached: process.platform !== "win32",
|
|
@@ -40510,6 +41080,7 @@ var WorkingContextPullRequestCache = class {
|
|
|
40510
41080
|
};
|
|
40511
41081
|
|
|
40512
41082
|
// src/runners/cli-runner.ts
|
|
41083
|
+
var softwareInstaller = createSoftwareInstaller();
|
|
40513
41084
|
var PRIVATE_CLEANUP_FAILURE = "Private runner cleanup could not be completed. Restart the Zixt Host before accepting more work.";
|
|
40514
41085
|
var PROCESS_CLEANUP_FAILURE = "Runner process cleanup could not be confirmed. Restart the Zixt Host before accepting more work.";
|
|
40515
41086
|
var PROVIDER_CLEANUP_FAILURE = "Provider tool cleanup could not be completed. Restart the Zixt Host before accepting more work.";
|
|
@@ -40532,7 +41103,7 @@ async function settlesWithin(promise2, timeoutMs) {
|
|
|
40532
41103
|
}
|
|
40533
41104
|
}
|
|
40534
41105
|
function defaultRunnerWorkspaceRoot() {
|
|
40535
|
-
return
|
|
41106
|
+
return join19(homedir7(), ".zixt", "workspaces");
|
|
40536
41107
|
}
|
|
40537
41108
|
function defaultRunnerArtifactRoot() {
|
|
40538
41109
|
return defaultRunArtifactRoot();
|
|
@@ -40581,7 +41152,7 @@ function createCliRunner(adapter, opts = {}) {
|
|
|
40581
41152
|
const prefixArgs = opts.commandPrefixArgs ?? [];
|
|
40582
41153
|
const maxWallTimeMs = opts.maxWallTimeMs;
|
|
40583
41154
|
const workspaceRoot = opts.workspaceRoot ?? defaultRunnerWorkspaceRoot();
|
|
40584
|
-
const artifactRoot = opts.artifactRoot ?? (opts.workspaceRoot === void 0 ? defaultRunnerArtifactRoot() :
|
|
41155
|
+
const artifactRoot = opts.artifactRoot ?? (opts.workspaceRoot === void 0 ? defaultRunnerArtifactRoot() : join19(dirname10(workspaceRoot), "run-artifacts"));
|
|
40585
41156
|
const runRegistryRoot2 = opts.runRegistryRoot ?? defaultRunRegistryRoot();
|
|
40586
41157
|
const createArtifacts = opts.createArtifacts ?? createRunArtifacts;
|
|
40587
41158
|
const toolPackRegistry2 = opts.toolPackRegistry ?? createDefaultToolPackRegistry();
|
|
@@ -40599,7 +41170,7 @@ function createCliRunner(adapter, opts = {}) {
|
|
|
40599
41170
|
};
|
|
40600
41171
|
const askUserServer = createAskUserServer();
|
|
40601
41172
|
const windowsRoot = process.env.SystemRoot ?? process.env.WINDIR;
|
|
40602
|
-
const windowsComspecCandidate = process.platform === "win32" && windowsRoot ?
|
|
41173
|
+
const windowsComspecCandidate = process.platform === "win32" && windowsRoot ? join19(windowsRoot, "System32", "cmd.exe") : void 0;
|
|
40603
41174
|
let safetyFailure;
|
|
40604
41175
|
return async (task) => {
|
|
40605
41176
|
if (safetyFailure) {
|
|
@@ -40639,8 +41210,8 @@ function createCliRunner(adapter, opts = {}) {
|
|
|
40639
41210
|
usage: { inputTokens: 0, outputTokens: 0 }
|
|
40640
41211
|
};
|
|
40641
41212
|
}
|
|
40642
|
-
const taskRoot =
|
|
40643
|
-
await
|
|
41213
|
+
const taskRoot = join19(workspaceRoot, task.agentId);
|
|
41214
|
+
await mkdir13(taskRoot, { recursive: true });
|
|
40644
41215
|
if (task.cancelledNow()) return cancelledBeforeRun();
|
|
40645
41216
|
const configuredWorkspace = task.spec.workspace;
|
|
40646
41217
|
let cwd = taskRoot;
|
|
@@ -40687,7 +41258,7 @@ function createCliRunner(adapter, opts = {}) {
|
|
|
40687
41258
|
summaryEvidence: "host_observed"
|
|
40688
41259
|
};
|
|
40689
41260
|
}
|
|
40690
|
-
const runToken =
|
|
41261
|
+
const runToken = randomUUID12();
|
|
40691
41262
|
const artifacts = await createArtifacts({
|
|
40692
41263
|
root: artifactRoot,
|
|
40693
41264
|
agentId: task.agentId,
|
|
@@ -40743,7 +41314,8 @@ function createCliRunner(adapter, opts = {}) {
|
|
|
40743
41314
|
runner: { type: adapter.type, auth: runner.auth },
|
|
40744
41315
|
gitIdentity: githubCommitIdentity(providerGrants),
|
|
40745
41316
|
isolation: artifacts,
|
|
40746
|
-
githubShell
|
|
41317
|
+
githubShell,
|
|
41318
|
+
softwareToolsPath: softwareSearchPathEntries(softwareInstaller.toolsRoot)
|
|
40747
41319
|
});
|
|
40748
41320
|
toolPacks = await toolPackRegistry2.createInstances(providerGrants, {
|
|
40749
41321
|
taskId: task.taskId,
|
|
@@ -40836,6 +41408,51 @@ function createCliRunner(adapter, opts = {}) {
|
|
|
40836
41408
|
}
|
|
40837
41409
|
},
|
|
40838
41410
|
publishFile,
|
|
41411
|
+
// AG-4a. The person approves in the Task thread through the ordinary
|
|
41412
|
+
// approvals pipeline, so the wall clock pauses while they decide, and
|
|
41413
|
+
// a refusal is an answer the session can act on rather than a failure.
|
|
41414
|
+
software: {
|
|
41415
|
+
check: (command2) => softwareInstaller.probe(command2),
|
|
41416
|
+
install: async ({ reason, ...request }) => {
|
|
41417
|
+
pendingAsks++;
|
|
41418
|
+
let decision;
|
|
41419
|
+
try {
|
|
41420
|
+
decision = await task.requestApproval(
|
|
41421
|
+
"agent.question",
|
|
41422
|
+
`Install ${request.command} on this Machine: ${reason}`.slice(0, 500),
|
|
41423
|
+
JSON.stringify({
|
|
41424
|
+
command: request.command,
|
|
41425
|
+
reason,
|
|
41426
|
+
method: request.method,
|
|
41427
|
+
source: request.source,
|
|
41428
|
+
...request.sha256 ? { sha256: request.sha256 } : {},
|
|
41429
|
+
folder: softwareInstaller.toolsRoot
|
|
41430
|
+
}).slice(0, MAX_APPROVAL_PAYLOAD)
|
|
41431
|
+
);
|
|
41432
|
+
} finally {
|
|
41433
|
+
pendingAsks--;
|
|
41434
|
+
}
|
|
41435
|
+
if (!decision.approved) {
|
|
41436
|
+
return {
|
|
41437
|
+
ok: false,
|
|
41438
|
+
detail: decision.guidance ?? `The person did not approve installing ${request.command}. Do not ask again for this task. Finish the work another way, or explain what is blocked.`
|
|
41439
|
+
};
|
|
41440
|
+
}
|
|
41441
|
+
task.event("status", `Installing ${request.command} on this Machine`, {
|
|
41442
|
+
tool: "install_software"
|
|
41443
|
+
});
|
|
41444
|
+
const result2 = await softwareInstaller.install(request);
|
|
41445
|
+
task.event(
|
|
41446
|
+
"status",
|
|
41447
|
+
result2.ok ? `Installed ${request.command} on this Machine` : `Could not install ${request.command} on this Machine`,
|
|
41448
|
+
{ tool: "install_software" }
|
|
41449
|
+
);
|
|
41450
|
+
return result2.ok ? {
|
|
41451
|
+
ok: true,
|
|
41452
|
+
detail: `${request.command} is installed at ${result2.record.path} and is on your PATH. Continue the work.`
|
|
41453
|
+
} : { ok: false, detail: result2.error };
|
|
41454
|
+
}
|
|
41455
|
+
},
|
|
40839
41456
|
agentOp: (op) => task.agentOp(op),
|
|
40840
41457
|
// GitHub repository work belongs in the installed `git` and `gh`
|
|
40841
41458
|
// commands backed by GithubShellAuth. Do not advertise the bundled
|
|
@@ -41060,8 +41677,8 @@ ${attachmentSection}` : prompt;
|
|
|
41060
41677
|
}
|
|
41061
41678
|
comspec = resolvedWindowsComspec;
|
|
41062
41679
|
}
|
|
41063
|
-
const exitMarker = `__ZIXT_RUNNER_EXIT_${
|
|
41064
|
-
const guardianNonce =
|
|
41680
|
+
const exitMarker = `__ZIXT_RUNNER_EXIT_${randomUUID12()}__`;
|
|
41681
|
+
const guardianNonce = randomUUID12();
|
|
41065
41682
|
return runCliProcess({
|
|
41066
41683
|
command: resolvedCommand,
|
|
41067
41684
|
args,
|
|
@@ -41418,8 +42035,8 @@ function runCliProcess(options) {
|
|
|
41418
42035
|
}
|
|
41419
42036
|
return new Promise((resolve19) => {
|
|
41420
42037
|
const platform = options.platform ?? process.platform;
|
|
41421
|
-
const containmentGateNonce = options.guardian && platform === "win32" ?
|
|
41422
|
-
const child = options.guardian ?
|
|
42038
|
+
const containmentGateNonce = options.guardian && platform === "win32" ? randomUUID12() : void 0;
|
|
42039
|
+
const child = options.guardian ? spawn11(
|
|
41423
42040
|
options.guardian.nodeCommand,
|
|
41424
42041
|
[
|
|
41425
42042
|
options.guardian.scriptPath,
|
|
@@ -41707,12 +42324,12 @@ function runCliProcess(options) {
|
|
|
41707
42324
|
}
|
|
41708
42325
|
|
|
41709
42326
|
// src/runners/claude-code.ts
|
|
41710
|
-
import { randomUUID as
|
|
42327
|
+
import { randomUUID as randomUUID13 } from "node:crypto";
|
|
41711
42328
|
|
|
41712
42329
|
// src/runners/runtime-observation.ts
|
|
41713
|
-
import { open as open6, readdir as
|
|
42330
|
+
import { open as open6, readdir as readdir6, realpath as realpath9 } from "node:fs/promises";
|
|
41714
42331
|
import { homedir as homedir8 } from "node:os";
|
|
41715
|
-
import { join as
|
|
42332
|
+
import { join as join20 } from "node:path";
|
|
41716
42333
|
var READ_WINDOW_BYTES = 1024 * 1024;
|
|
41717
42334
|
var CATALOG_TIMEOUT_MS = 15e3;
|
|
41718
42335
|
var CATALOG_OUTPUT_LIMIT_BYTES = 4 * 1024 * 1024;
|
|
@@ -41772,9 +42389,9 @@ function displayValue(value, maxLength) {
|
|
|
41772
42389
|
return trimmed;
|
|
41773
42390
|
}
|
|
41774
42391
|
function claudeTranscriptPath(input) {
|
|
41775
|
-
const configDir = input.env["CLAUDE_CONFIG_DIR"] ||
|
|
42392
|
+
const configDir = input.env["CLAUDE_CONFIG_DIR"] || join20(homeFrom(input.env), ".claude");
|
|
41776
42393
|
const slug = input.resolvedCwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
41777
|
-
return
|
|
42394
|
+
return join20(configDir, "projects", slug, `${input.sessionId}.jsonl`);
|
|
41778
42395
|
}
|
|
41779
42396
|
async function readClaudeSessionEffort(input) {
|
|
41780
42397
|
const resolvedCwd = await realpath9(input.cwd).catch(() => input.cwd);
|
|
@@ -41789,19 +42406,19 @@ async function readClaudeSessionEffort(input) {
|
|
|
41789
42406
|
return null;
|
|
41790
42407
|
}
|
|
41791
42408
|
async function newestDirectories(root, limit) {
|
|
41792
|
-
const entries = await
|
|
41793
|
-
return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort((left, right) => right.localeCompare(left)).slice(0, limit).map((name) =>
|
|
42409
|
+
const entries = await readdir6(root, { withFileTypes: true }).catch(() => []);
|
|
42410
|
+
return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort((left, right) => right.localeCompare(left)).slice(0, limit).map((name) => join20(root, name));
|
|
41794
42411
|
}
|
|
41795
42412
|
async function findCodexRolloutPath(input) {
|
|
41796
|
-
const codexHome = input.env["CODEX_HOME"] ||
|
|
41797
|
-
const sessions =
|
|
42413
|
+
const codexHome = input.env["CODEX_HOME"] || join20(homeFrom(input.env), ".codex");
|
|
42414
|
+
const sessions = join20(codexHome, "sessions");
|
|
41798
42415
|
const suffix = `-${input.threadId}.jsonl`;
|
|
41799
42416
|
for (const year of await newestDirectories(sessions, 2)) {
|
|
41800
42417
|
for (const month of await newestDirectories(year, 2)) {
|
|
41801
42418
|
for (const day of await newestDirectories(month, 3)) {
|
|
41802
|
-
const files = await
|
|
42419
|
+
const files = await readdir6(day).catch(() => []);
|
|
41803
42420
|
const match = files.find((name) => name.endsWith(suffix));
|
|
41804
|
-
if (match) return
|
|
42421
|
+
if (match) return join20(day, match);
|
|
41805
42422
|
}
|
|
41806
42423
|
}
|
|
41807
42424
|
}
|
|
@@ -41899,7 +42516,7 @@ var claudeCodeAdapter = {
|
|
|
41899
42516
|
input.gitDetected,
|
|
41900
42517
|
liveInput
|
|
41901
42518
|
);
|
|
41902
|
-
const sessionId = input.task.spec.sessionKey ??
|
|
42519
|
+
const sessionId = input.task.spec.sessionKey ?? randomUUID13();
|
|
41903
42520
|
const observeRuntime = createRuntimeReporter(input, sessionId);
|
|
41904
42521
|
return {
|
|
41905
42522
|
argsFor: (mode) => [
|
|
@@ -42028,7 +42645,7 @@ function createClaudeLiveParser(onStream, onSessionModel) {
|
|
|
42028
42645
|
...parser,
|
|
42029
42646
|
async start(writer, prompt) {
|
|
42030
42647
|
write = writer;
|
|
42031
|
-
await writer(input(
|
|
42648
|
+
await writer(input(randomUUID13(), prompt));
|
|
42032
42649
|
},
|
|
42033
42650
|
async steer(followUp) {
|
|
42034
42651
|
if (!write) return false;
|
|
@@ -42195,22 +42812,22 @@ function improveErrorMessage(error52) {
|
|
|
42195
42812
|
}
|
|
42196
42813
|
|
|
42197
42814
|
// src/runners/codex.ts
|
|
42198
|
-
import { mkdir as
|
|
42199
|
-
import { randomUUID as
|
|
42815
|
+
import { mkdir as mkdir14, readFile as readFile11, writeFile as writeFile9 } from "node:fs/promises";
|
|
42816
|
+
import { randomUUID as randomUUID14 } from "node:crypto";
|
|
42200
42817
|
import { homedir as homedir9 } from "node:os";
|
|
42201
|
-
import { join as
|
|
42818
|
+
import { join as join21 } from "node:path";
|
|
42202
42819
|
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.";
|
|
42203
42820
|
function defaultCodexThreadIndexRoot() {
|
|
42204
|
-
return
|
|
42821
|
+
return join21(homedir9(), ".zixt", "codex-threads");
|
|
42205
42822
|
}
|
|
42206
42823
|
var SAFE_SEGMENT3 = /^[A-Za-z0-9_-]{1,200}$/;
|
|
42207
42824
|
function threadIndexPath(root, agentId, sessionKey) {
|
|
42208
42825
|
if (!SAFE_SEGMENT3.test(agentId) || !SAFE_SEGMENT3.test(sessionKey)) return null;
|
|
42209
|
-
return
|
|
42826
|
+
return join21(root, agentId, `${sessionKey}.json`);
|
|
42210
42827
|
}
|
|
42211
42828
|
async function readThreadId(path) {
|
|
42212
42829
|
try {
|
|
42213
|
-
const parsed = JSON.parse(await
|
|
42830
|
+
const parsed = JSON.parse(await readFile11(path, "utf8"));
|
|
42214
42831
|
return typeof parsed.threadId === "string" && /^[A-Za-z0-9-]{1,120}$/.test(parsed.threadId) ? parsed.threadId : null;
|
|
42215
42832
|
} catch {
|
|
42216
42833
|
return null;
|
|
@@ -42310,7 +42927,7 @@ ${value}` : value;
|
|
|
42310
42927
|
const recordedThreadId = indexPath ? await readThreadId(indexPath) : null;
|
|
42311
42928
|
const rememberThread = (threadId) => {
|
|
42312
42929
|
if (!indexPath) return;
|
|
42313
|
-
void
|
|
42930
|
+
void mkdir14(join21(threadIndexRoot, task.agentId), { recursive: true }).then(() => writeFile9(indexPath, JSON.stringify({ threadId }), "utf8")).catch(() => {
|
|
42314
42931
|
});
|
|
42315
42932
|
};
|
|
42316
42933
|
const observeRuntime = (threadId) => {
|
|
@@ -42414,7 +43031,7 @@ function createCodexAppServerParser(onStream, options) {
|
|
|
42414
43031
|
params: {
|
|
42415
43032
|
threadId,
|
|
42416
43033
|
input: [{ type: "text", text: prompt }],
|
|
42417
|
-
clientUserMessageId:
|
|
43034
|
+
clientUserMessageId: randomUUID14(),
|
|
42418
43035
|
...options.model ? { model: options.model } : {},
|
|
42419
43036
|
...options.effort ? { effort: options.effort } : {}
|
|
42420
43037
|
}
|
|
@@ -42773,7 +43390,7 @@ function improveCodexErrorMessage(error52) {
|
|
|
42773
43390
|
}
|
|
42774
43391
|
|
|
42775
43392
|
// src/runners/git-preflight.ts
|
|
42776
|
-
import { spawn as
|
|
43393
|
+
import { spawn as spawn12 } from "node:child_process";
|
|
42777
43394
|
import { realpath as realpath10 } from "node:fs/promises";
|
|
42778
43395
|
import { isAbsolute as isAbsolute17, resolve as resolve11 } from "node:path";
|
|
42779
43396
|
var OUTPUT_LIMIT = 8192;
|
|
@@ -42822,7 +43439,7 @@ async function preflightGit(options = {}) {
|
|
|
42822
43439
|
}
|
|
42823
43440
|
async function runVersionProbe(input) {
|
|
42824
43441
|
return new Promise((resolvePromise) => {
|
|
42825
|
-
const child =
|
|
43442
|
+
const child = spawn12(input.executablePath, input.args, {
|
|
42826
43443
|
cwd: input.cwd,
|
|
42827
43444
|
env: {
|
|
42828
43445
|
...process.env.SystemRoot ? { SystemRoot: process.env.SystemRoot } : {},
|
|
@@ -43044,16 +43661,16 @@ function run2(command, args) {
|
|
|
43044
43661
|
}
|
|
43045
43662
|
|
|
43046
43663
|
// src/linux-service.ts
|
|
43047
|
-
import { spawn as
|
|
43664
|
+
import { spawn as spawn13 } from "node:child_process";
|
|
43048
43665
|
import { constants as constants2 } from "node:fs";
|
|
43049
|
-
import { access as access5, chmod as
|
|
43666
|
+
import { access as access5, chmod as chmod9, mkdir as mkdir16, open as open7, rename as rename8, rm as rm12 } from "node:fs/promises";
|
|
43050
43667
|
import { homedir as homedir11, userInfo } from "node:os";
|
|
43051
|
-
import { basename as basename4, dirname as dirname11, join as
|
|
43668
|
+
import { basename as basename4, dirname as dirname11, join as join23, relative as relative10, resolve as resolve13, sep as sep7 } from "node:path";
|
|
43052
43669
|
|
|
43053
43670
|
// src/service-runtime.ts
|
|
43054
|
-
import { access as access4, chmod as
|
|
43671
|
+
import { access as access4, chmod as chmod8, copyFile, mkdir as mkdir15, rename as rename7, rm as rm11 } from "node:fs/promises";
|
|
43055
43672
|
import { homedir as homedir10 } from "node:os";
|
|
43056
|
-
import { join as
|
|
43673
|
+
import { join as join22, resolve as resolve12, sep as sep6 } from "node:path";
|
|
43057
43674
|
async function ensureDurableServiceNode(options = {}) {
|
|
43058
43675
|
const execPath = resolve12(options.execPath ?? process.execPath);
|
|
43059
43676
|
const home = options.home ?? homedir10();
|
|
@@ -43063,22 +43680,22 @@ async function ensureDurableServiceNode(options = {}) {
|
|
|
43063
43680
|
throw new Error("the Node runtime version is not a safe directory name");
|
|
43064
43681
|
}
|
|
43065
43682
|
const zixtRoot = resolve12(home, ".zixt");
|
|
43066
|
-
if (execPath === zixtRoot || execPath.startsWith(zixtRoot +
|
|
43067
|
-
const directory =
|
|
43068
|
-
const destination =
|
|
43683
|
+
if (execPath === zixtRoot || execPath.startsWith(zixtRoot + sep6)) return execPath;
|
|
43684
|
+
const directory = join22(zixtRoot, "runtime", `node-${version2}`);
|
|
43685
|
+
const destination = join22(directory, platform === "win32" ? "node.exe" : "node");
|
|
43069
43686
|
const alreadyCopied = await access4(destination).then(
|
|
43070
43687
|
() => true,
|
|
43071
43688
|
() => false
|
|
43072
43689
|
);
|
|
43073
43690
|
if (alreadyCopied) return destination;
|
|
43074
|
-
await
|
|
43691
|
+
await mkdir15(directory, { recursive: true, mode: 448 });
|
|
43075
43692
|
const temporary = `${destination}.${process.pid}.${crypto.randomUUID()}.tmp`;
|
|
43076
43693
|
try {
|
|
43077
43694
|
await copyFile(execPath, temporary);
|
|
43078
|
-
await
|
|
43079
|
-
await
|
|
43695
|
+
await chmod8(temporary, 493);
|
|
43696
|
+
await rename7(temporary, destination);
|
|
43080
43697
|
} catch (error52) {
|
|
43081
|
-
await
|
|
43698
|
+
await rm11(temporary, { force: true }).catch(() => void 0);
|
|
43082
43699
|
throw error52;
|
|
43083
43700
|
}
|
|
43084
43701
|
await options.syncDirectory?.(directory);
|
|
@@ -43114,7 +43731,7 @@ function boundedAppend(current, chunk) {
|
|
|
43114
43731
|
async function defaultRunCommand(command, args) {
|
|
43115
43732
|
const commandEnvironment3 = systemServiceCommandEnvironment();
|
|
43116
43733
|
return new Promise((resolve19) => {
|
|
43117
|
-
const child =
|
|
43734
|
+
const child = spawn13(command, [...args], {
|
|
43118
43735
|
stdio: ["ignore", "pipe", "pipe"],
|
|
43119
43736
|
env: commandEnvironment3,
|
|
43120
43737
|
windowsHide: true
|
|
@@ -43186,33 +43803,33 @@ async function defaultSyncDirectory(path) {
|
|
|
43186
43803
|
}
|
|
43187
43804
|
}
|
|
43188
43805
|
async function ensureDirectory(path, mode, syncDirectory8) {
|
|
43189
|
-
const firstCreated = await
|
|
43806
|
+
const firstCreated = await mkdir16(path, { recursive: true, mode });
|
|
43190
43807
|
if (!firstCreated) return;
|
|
43191
43808
|
const first = resolve13(firstCreated);
|
|
43192
43809
|
const target = resolve13(path);
|
|
43193
43810
|
await syncDirectory8(dirname11(first));
|
|
43194
43811
|
let current = first;
|
|
43195
|
-
const descendants =
|
|
43196
|
-
for (const part of descendants ? descendants.split(
|
|
43812
|
+
const descendants = relative10(first, target);
|
|
43813
|
+
for (const part of descendants ? descendants.split(sep7) : []) {
|
|
43197
43814
|
await syncDirectory8(current);
|
|
43198
|
-
current =
|
|
43815
|
+
current = join23(current, part);
|
|
43199
43816
|
}
|
|
43200
43817
|
}
|
|
43201
43818
|
async function replacePrivateFile(path, contents, mode, syncDirectory8) {
|
|
43202
43819
|
const parent = dirname11(path);
|
|
43203
43820
|
await ensureDirectory(parent, 448, syncDirectory8);
|
|
43204
|
-
const temporary =
|
|
43821
|
+
const temporary = join23(parent, `.${basename4(path)}.${process.pid}.${crypto.randomUUID()}.tmp`);
|
|
43205
43822
|
const handle = await open7(temporary, "wx", mode);
|
|
43206
43823
|
try {
|
|
43207
43824
|
await handle.writeFile(contents, "utf8");
|
|
43208
43825
|
await handle.sync();
|
|
43209
43826
|
await handle.close();
|
|
43210
|
-
await
|
|
43211
|
-
await
|
|
43827
|
+
await rename8(temporary, path);
|
|
43828
|
+
await chmod9(path, mode);
|
|
43212
43829
|
await syncDirectory8(parent);
|
|
43213
43830
|
} catch (error52) {
|
|
43214
43831
|
await handle.close().catch(() => void 0);
|
|
43215
|
-
await
|
|
43832
|
+
await rm12(temporary, { force: true }).catch(() => void 0);
|
|
43216
43833
|
throw error52;
|
|
43217
43834
|
}
|
|
43218
43835
|
}
|
|
@@ -43253,11 +43870,11 @@ async function installLinuxService(options) {
|
|
|
43253
43870
|
"command search path"
|
|
43254
43871
|
);
|
|
43255
43872
|
const cloudUrl = options.cloudUrl ? oneLine(options.cloudUrl, "Zixt Cloud address") : void 0;
|
|
43256
|
-
const xdgConfigHome = env.XDG_CONFIG_HOME ? oneLine(env.XDG_CONFIG_HOME, "Linux configuration path") :
|
|
43257
|
-
const configRoot = options.serviceConfigRoot ??
|
|
43258
|
-
const unitRoot = options.userUnitRoot ??
|
|
43259
|
-
const environmentPath =
|
|
43260
|
-
const unitPath =
|
|
43873
|
+
const xdgConfigHome = env.XDG_CONFIG_HOME ? oneLine(env.XDG_CONFIG_HOME, "Linux configuration path") : join23(home, ".config");
|
|
43874
|
+
const configRoot = options.serviceConfigRoot ?? join23(xdgConfigHome, "zixt");
|
|
43875
|
+
const unitRoot = options.userUnitRoot ?? join23(xdgConfigHome, "systemd", "user");
|
|
43876
|
+
const environmentPath = join23(configRoot, "host.env");
|
|
43877
|
+
const unitPath = join23(unitRoot, SERVICE_NAME);
|
|
43261
43878
|
const installVersion = options.installVersion ?? ((version2, onFailure) => installRelease(version2, onFailure ? { onFailure } : {}));
|
|
43262
43879
|
const activateVersion = options.activateVersion ?? (options.installVersion ? async (entry) => entry : activateInstalledRelease);
|
|
43263
43880
|
const resolveCommand = options.resolveCommand ?? defaultResolveCommand;
|
|
@@ -43297,7 +43914,7 @@ async function installLinuxService(options) {
|
|
|
43297
43914
|
}
|
|
43298
43915
|
}
|
|
43299
43916
|
await ensureDirectory(configRoot, 448, syncDirectory8);
|
|
43300
|
-
await
|
|
43917
|
+
await chmod9(configRoot, 448);
|
|
43301
43918
|
const serviceEnvironment = [
|
|
43302
43919
|
`ZIXT_HOST_TOKEN=${systemdEnvironmentValue(token2)}`,
|
|
43303
43920
|
...cloudUrl ? [`ZIXT_CLOUD_URL=${systemdEnvironmentValue(cloudUrl)}`] : [],
|
|
@@ -43385,11 +44002,11 @@ async function installLinuxService(options) {
|
|
|
43385
44002
|
}
|
|
43386
44003
|
|
|
43387
44004
|
// src/macos-service.ts
|
|
43388
|
-
import { spawn as
|
|
44005
|
+
import { spawn as spawn14 } from "node:child_process";
|
|
43389
44006
|
import { constants as constants3 } from "node:fs";
|
|
43390
|
-
import { access as access6, chmod as
|
|
44007
|
+
import { access as access6, chmod as chmod10, mkdir as mkdir17, open as open8, rename as rename9, rm as rm13 } from "node:fs/promises";
|
|
43391
44008
|
import { homedir as homedir12, userInfo as userInfo2 } from "node:os";
|
|
43392
|
-
import { basename as basename5, dirname as dirname12, join as
|
|
44009
|
+
import { basename as basename5, dirname as dirname12, join as join24, relative as relative11, resolve as resolve14, sep as sep8 } from "node:path";
|
|
43393
44010
|
var LAUNCH_AGENT_LABEL = "ai.zixt.host";
|
|
43394
44011
|
var SERVICE_STABILITY_DELAY_MS2 = 2e3;
|
|
43395
44012
|
var STATUS_WAIT_MS = 2e4;
|
|
@@ -43414,32 +44031,32 @@ async function syncDirectory4(path) {
|
|
|
43414
44031
|
}
|
|
43415
44032
|
}
|
|
43416
44033
|
async function ensureDirectory2(path, sync) {
|
|
43417
|
-
const firstCreated = await
|
|
44034
|
+
const firstCreated = await mkdir17(path, { recursive: true, mode: 448 });
|
|
43418
44035
|
if (!firstCreated) return;
|
|
43419
44036
|
const first = resolve14(firstCreated);
|
|
43420
44037
|
const target = resolve14(path);
|
|
43421
44038
|
await sync(dirname12(first));
|
|
43422
44039
|
let current = first;
|
|
43423
|
-
for (const part of
|
|
44040
|
+
for (const part of relative11(first, target).split(sep8).filter(Boolean)) {
|
|
43424
44041
|
await sync(current);
|
|
43425
|
-
current =
|
|
44042
|
+
current = join24(current, part);
|
|
43426
44043
|
}
|
|
43427
44044
|
}
|
|
43428
44045
|
async function replacePrivateFile2(path, contents, mode, sync) {
|
|
43429
44046
|
const parent = dirname12(path);
|
|
43430
44047
|
await ensureDirectory2(parent, sync);
|
|
43431
|
-
const temporary =
|
|
44048
|
+
const temporary = join24(parent, `.${basename5(path)}.${process.pid}.${crypto.randomUUID()}.tmp`);
|
|
43432
44049
|
const handle = await open8(temporary, "wx", mode);
|
|
43433
44050
|
try {
|
|
43434
44051
|
await handle.writeFile(contents, "utf8");
|
|
43435
44052
|
await handle.sync();
|
|
43436
44053
|
await handle.close();
|
|
43437
|
-
await
|
|
43438
|
-
await
|
|
44054
|
+
await rename9(temporary, path);
|
|
44055
|
+
await chmod10(path, mode);
|
|
43439
44056
|
await sync(parent);
|
|
43440
44057
|
} catch (error52) {
|
|
43441
44058
|
await handle.close().catch(() => void 0);
|
|
43442
|
-
await
|
|
44059
|
+
await rm13(temporary, { force: true }).catch(() => void 0);
|
|
43443
44060
|
throw error52;
|
|
43444
44061
|
}
|
|
43445
44062
|
}
|
|
@@ -43453,7 +44070,7 @@ function commandEnvironment(env) {
|
|
|
43453
44070
|
}
|
|
43454
44071
|
async function defaultRunCommand2(command, args, env) {
|
|
43455
44072
|
return new Promise((resolveResult) => {
|
|
43456
|
-
const child =
|
|
44073
|
+
const child = spawn14(command, [...args], {
|
|
43457
44074
|
stdio: ["ignore", "pipe", "pipe"],
|
|
43458
44075
|
env: commandEnvironment(env)
|
|
43459
44076
|
});
|
|
@@ -43525,14 +44142,14 @@ async function installMacosService(options) {
|
|
|
43525
44142
|
options.path ?? env.PATH ?? "/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin",
|
|
43526
44143
|
"command search path"
|
|
43527
44144
|
);
|
|
43528
|
-
const configRoot = options.configRoot ??
|
|
43529
|
-
const launchAgentsRoot = options.launchAgentsRoot ??
|
|
43530
|
-
const logRoot = options.logRoot ??
|
|
43531
|
-
const configPath =
|
|
43532
|
-
const launcherPath =
|
|
43533
|
-
const plistPath =
|
|
43534
|
-
const stdoutPath =
|
|
43535
|
-
const stderrPath =
|
|
44145
|
+
const configRoot = options.configRoot ?? join24(home, "Library", "Application Support", "Zixt");
|
|
44146
|
+
const launchAgentsRoot = options.launchAgentsRoot ?? join24(home, "Library", "LaunchAgents");
|
|
44147
|
+
const logRoot = options.logRoot ?? join24(home, "Library", "Logs", "Zixt");
|
|
44148
|
+
const configPath = join24(configRoot, "host.env");
|
|
44149
|
+
const launcherPath = join24(configRoot, "host-launcher.sh");
|
|
44150
|
+
const plistPath = join24(launchAgentsRoot, `${LAUNCH_AGENT_LABEL}.plist`);
|
|
44151
|
+
const stdoutPath = join24(logRoot, "host.log");
|
|
44152
|
+
const stderrPath = join24(logRoot, "host-error.log");
|
|
43536
44153
|
const installVersion = options.installVersion ?? ((version2, onFailure) => installRelease(version2, onFailure ? { onFailure } : {}));
|
|
43537
44154
|
const activateVersion = options.activateVersion ?? (options.installVersion ? async (entry) => entry : (entry) => activateInstalledRelease(entry));
|
|
43538
44155
|
const resolveCommand = options.resolveCommand ?? (async () => defaultResolveCommand2());
|
|
@@ -43555,7 +44172,7 @@ async function installMacosService(options) {
|
|
|
43555
44172
|
await ensureDirectory2(configRoot, sync);
|
|
43556
44173
|
await ensureDirectory2(launchAgentsRoot, sync);
|
|
43557
44174
|
await ensureDirectory2(logRoot, sync);
|
|
43558
|
-
await
|
|
44175
|
+
await chmod10(configRoot, 448);
|
|
43559
44176
|
const serviceEnvironment = [
|
|
43560
44177
|
`ZIXT_HOST_TOKEN=${shellValue(token2)}`,
|
|
43561
44178
|
...cloudUrl ? [`ZIXT_CLOUD_URL=${shellValue(cloudUrl)}`] : [],
|
|
@@ -43633,11 +44250,11 @@ async function installMacosService(options) {
|
|
|
43633
44250
|
}
|
|
43634
44251
|
|
|
43635
44252
|
// src/windows-service.ts
|
|
43636
|
-
import { spawn as
|
|
44253
|
+
import { spawn as spawn15 } from "node:child_process";
|
|
43637
44254
|
import { constants as constants4 } from "node:fs";
|
|
43638
|
-
import { access as access7, mkdir as
|
|
44255
|
+
import { access as access7, mkdir as mkdir18, open as open9, readFile as readFile12, rename as rename10, rm as rm14 } from "node:fs/promises";
|
|
43639
44256
|
import { homedir as homedir13 } from "node:os";
|
|
43640
|
-
import { basename as basename6, dirname as dirname13, isAbsolute as isAbsolute18, join as
|
|
44257
|
+
import { basename as basename6, dirname as dirname13, isAbsolute as isAbsolute18, join as join25, relative as relative12, resolve as resolve15, sep as sep9 } from "node:path";
|
|
43641
44258
|
var TASK_NAME = "Zixt Host";
|
|
43642
44259
|
var COMMAND_TIMEOUT_MS3 = 7e4;
|
|
43643
44260
|
var SERVICE_STABILITY_DELAY_MS3 = 2e3;
|
|
@@ -43663,31 +44280,31 @@ async function syncDirectory5(path) {
|
|
|
43663
44280
|
}
|
|
43664
44281
|
}
|
|
43665
44282
|
async function ensureDirectory3(path, sync) {
|
|
43666
|
-
const firstCreated = await
|
|
44283
|
+
const firstCreated = await mkdir18(path, { recursive: true, mode: 448 });
|
|
43667
44284
|
if (!firstCreated) return;
|
|
43668
44285
|
const first = resolve15(firstCreated);
|
|
43669
44286
|
const target = resolve15(path);
|
|
43670
44287
|
await sync(dirname13(first));
|
|
43671
44288
|
let current = first;
|
|
43672
|
-
for (const part of
|
|
44289
|
+
for (const part of relative12(first, target).split(sep9).filter(Boolean)) {
|
|
43673
44290
|
await sync(current);
|
|
43674
|
-
current =
|
|
44291
|
+
current = join25(current, part);
|
|
43675
44292
|
}
|
|
43676
44293
|
}
|
|
43677
44294
|
async function replacePrivateFile3(path, contents, sync, encoding = "utf8") {
|
|
43678
44295
|
const parent = dirname13(path);
|
|
43679
44296
|
await ensureDirectory3(parent, sync);
|
|
43680
|
-
const temporary =
|
|
44297
|
+
const temporary = join25(parent, `.${basename6(path)}.${process.pid}.${crypto.randomUUID()}.tmp`);
|
|
43681
44298
|
const handle = await open9(temporary, "wx", 384);
|
|
43682
44299
|
try {
|
|
43683
44300
|
await handle.writeFile(encoding === "utf16le" ? `\uFEFF${contents}` : contents, encoding);
|
|
43684
44301
|
await handle.sync();
|
|
43685
44302
|
await handle.close();
|
|
43686
|
-
await
|
|
44303
|
+
await rename10(temporary, path);
|
|
43687
44304
|
await sync(parent);
|
|
43688
44305
|
} catch (error52) {
|
|
43689
44306
|
await handle.close().catch(() => void 0);
|
|
43690
|
-
await
|
|
44307
|
+
await rm14(temporary, { force: true }).catch(() => void 0);
|
|
43691
44308
|
throw error52;
|
|
43692
44309
|
}
|
|
43693
44310
|
}
|
|
@@ -43698,7 +44315,7 @@ function commandEnvironment2(env) {
|
|
|
43698
44315
|
}
|
|
43699
44316
|
async function runChild(command, args, env, input) {
|
|
43700
44317
|
return new Promise((resolveResult) => {
|
|
43701
|
-
const child =
|
|
44318
|
+
const child = spawn15(command, [...args], {
|
|
43702
44319
|
stdio: [input === void 0 ? "ignore" : "pipe", "pipe", "pipe"],
|
|
43703
44320
|
env: commandEnvironment2(env),
|
|
43704
44321
|
windowsHide: true
|
|
@@ -43732,7 +44349,7 @@ async function runChild(command, args, env, input) {
|
|
|
43732
44349
|
async function defaultResolveCommand3(name, env) {
|
|
43733
44350
|
const root = env.SYSTEMROOT ?? env.WINDIR;
|
|
43734
44351
|
if (!root || !isAbsolute18(root)) return null;
|
|
43735
|
-
const candidate = name === "powershell" ?
|
|
44352
|
+
const candidate = name === "powershell" ? join25(root, "System32", "WindowsPowerShell", "v1.0", "powershell.exe") : join25(root, "System32", `${name}.exe`);
|
|
43736
44353
|
return access7(candidate, constants4.X_OK).then(
|
|
43737
44354
|
() => candidate,
|
|
43738
44355
|
() => null
|
|
@@ -43822,7 +44439,7 @@ exit $code
|
|
|
43822
44439
|
}
|
|
43823
44440
|
async function defaultObserveStatus(path, generation) {
|
|
43824
44441
|
try {
|
|
43825
|
-
const text = (await
|
|
44442
|
+
const text = (await readFile12(path, "utf8")).replace(/^\uFEFF/, "");
|
|
43826
44443
|
const value = JSON.parse(text);
|
|
43827
44444
|
if (value.schema !== 1 || value.generation !== generation || typeof value.pid !== "number" || !Number.isSafeInteger(value.pid) || value.pid <= 0) {
|
|
43828
44445
|
return null;
|
|
@@ -43883,12 +44500,12 @@ async function installWindowsService(options) {
|
|
|
43883
44500
|
const token2 = oneLine3(options.token, "pairing code");
|
|
43884
44501
|
const cloudUrl = options.cloudUrl ? oneLine3(options.cloudUrl, "Zixt Cloud address") : void 0;
|
|
43885
44502
|
const path = oneLine3(options.path ?? env.PATH ?? "", "command search path");
|
|
43886
|
-
const configRoot = options.configRoot ??
|
|
43887
|
-
const configPath =
|
|
43888
|
-
const launcherPath =
|
|
43889
|
-
const launchShimPath =
|
|
43890
|
-
const taskXmlPath =
|
|
43891
|
-
const statusPath =
|
|
44503
|
+
const configRoot = options.configRoot ?? join25(localAppData, "Zixt", "Host");
|
|
44504
|
+
const configPath = join25(configRoot, "host.json");
|
|
44505
|
+
const launcherPath = join25(configRoot, "host-launcher.ps1");
|
|
44506
|
+
const launchShimPath = join25(configRoot, "host-launch.vbs");
|
|
44507
|
+
const taskXmlPath = join25(configRoot, "host-task.xml");
|
|
44508
|
+
const statusPath = join25(configRoot, "host-status.json");
|
|
43892
44509
|
const installVersion = options.installVersion ?? ((version2, onFailure) => installRelease(version2, onFailure ? { onFailure } : {}));
|
|
43893
44510
|
const activateVersion = options.activateVersion ?? (options.installVersion ? async (entry) => entry : (entry) => activateInstalledRelease(entry));
|
|
43894
44511
|
const resolveCommand = options.resolveCommand ?? ((name) => defaultResolveCommand3(name, env));
|
|
@@ -43955,7 +44572,7 @@ async function installWindowsService(options) {
|
|
|
43955
44572
|
sync,
|
|
43956
44573
|
"utf16le"
|
|
43957
44574
|
);
|
|
43958
|
-
await
|
|
44575
|
+
await rm14(statusPath, { force: true });
|
|
43959
44576
|
const acl = await run3(icacls, [
|
|
43960
44577
|
configRoot,
|
|
43961
44578
|
"/inheritance:r",
|
|
@@ -44015,23 +44632,23 @@ async function installSystemService(options) {
|
|
|
44015
44632
|
}
|
|
44016
44633
|
|
|
44017
44634
|
// src/terminal-outcomes.ts
|
|
44018
|
-
import { chmod as
|
|
44635
|
+
import { chmod as chmod11, lstat as lstat12, mkdir as mkdir19, open as open10, readdir as readdir7, readFile as readFile13, rename as rename11, rm as rm15 } from "node:fs/promises";
|
|
44019
44636
|
import { homedir as homedir14 } from "node:os";
|
|
44020
|
-
import { dirname as dirname14, join as
|
|
44637
|
+
import { dirname as dirname14, join as join26, relative as relative13, resolve as resolve16, sep as sep10 } from "node:path";
|
|
44021
44638
|
var DIRECTORY_MODE5 = 448;
|
|
44022
44639
|
var FILE_MODE4 = 384;
|
|
44023
44640
|
var MAX_OUTCOME_BYTES = 4 * 1024 * 1024;
|
|
44024
44641
|
var HOST_DIRECTORY = /^hst_[0-9a-f]{32}$/;
|
|
44025
44642
|
var OUTCOME_FILE = /^(tsk_[0-9a-f]{32})\.([1-9][0-9]*)\.json$/;
|
|
44026
44643
|
function defaultTerminalOutcomeRoot() {
|
|
44027
|
-
return
|
|
44644
|
+
return join26(homedir14(), ".zixt", "terminal-outcomes");
|
|
44028
44645
|
}
|
|
44029
44646
|
function hostOutcomeRoot(root, hostId) {
|
|
44030
44647
|
if (!HOST_DIRECTORY.test(hostId)) throw new Error("terminal outcome Host identity is malformed");
|
|
44031
|
-
return
|
|
44648
|
+
return join26(root, hostId);
|
|
44032
44649
|
}
|
|
44033
44650
|
function outcomePath(root, hostId, taskId, epoch) {
|
|
44034
|
-
return
|
|
44651
|
+
return join26(hostOutcomeRoot(root, hostId), `${taskId}.${epoch}.json`);
|
|
44035
44652
|
}
|
|
44036
44653
|
async function syncDirectory6(root) {
|
|
44037
44654
|
if (process.platform === "win32") return;
|
|
@@ -44043,22 +44660,22 @@ async function syncDirectory6(root) {
|
|
|
44043
44660
|
}
|
|
44044
44661
|
}
|
|
44045
44662
|
async function requirePrivateRoot(root, sync = syncDirectory6) {
|
|
44046
|
-
const firstCreated = await
|
|
44663
|
+
const firstCreated = await mkdir19(root, { recursive: true, mode: DIRECTORY_MODE5 });
|
|
44047
44664
|
if (firstCreated) {
|
|
44048
44665
|
const first = resolve16(firstCreated);
|
|
44049
44666
|
const target = resolve16(root);
|
|
44050
44667
|
await sync(dirname14(first));
|
|
44051
44668
|
let current = first;
|
|
44052
|
-
for (const part of
|
|
44669
|
+
for (const part of relative13(first, target).split(sep10).filter(Boolean)) {
|
|
44053
44670
|
await sync(current);
|
|
44054
|
-
current =
|
|
44671
|
+
current = join26(current, part);
|
|
44055
44672
|
}
|
|
44056
44673
|
}
|
|
44057
44674
|
const stat4 = await lstat12(root);
|
|
44058
44675
|
if (stat4.isSymbolicLink() || !stat4.isDirectory()) {
|
|
44059
44676
|
throw new Error("terminal outcome journal root is not a trusted directory");
|
|
44060
44677
|
}
|
|
44061
|
-
await
|
|
44678
|
+
await chmod11(root, DIRECTORY_MODE5);
|
|
44062
44679
|
}
|
|
44063
44680
|
function parseCommittedOutcome(text, taskId, epoch) {
|
|
44064
44681
|
let json2;
|
|
@@ -44082,7 +44699,7 @@ async function recordTerminalOutcome(hostId, input, root = defaultTerminalOutcom
|
|
|
44082
44699
|
const destination = outcomePath(root, hostId, outcome.taskId, outcome.epoch);
|
|
44083
44700
|
try {
|
|
44084
44701
|
const existing = parseCommittedOutcome(
|
|
44085
|
-
await
|
|
44702
|
+
await readFile13(destination, { encoding: "utf8", flag: "r" }),
|
|
44086
44703
|
outcome.taskId,
|
|
44087
44704
|
outcome.epoch
|
|
44088
44705
|
);
|
|
@@ -44091,7 +44708,7 @@ async function recordTerminalOutcome(hostId, input, root = defaultTerminalOutcom
|
|
|
44091
44708
|
} catch (error52) {
|
|
44092
44709
|
if (error52.code !== "ENOENT") throw error52;
|
|
44093
44710
|
}
|
|
44094
|
-
const temporary =
|
|
44711
|
+
const temporary = join26(
|
|
44095
44712
|
scopedRoot,
|
|
44096
44713
|
`.${outcome.taskId}.${outcome.epoch}.${process.pid}.${Date.now()}.${outcome.resultId}.tmp`
|
|
44097
44714
|
);
|
|
@@ -44102,13 +44719,13 @@ async function recordTerminalOutcome(hostId, input, root = defaultTerminalOutcom
|
|
|
44102
44719
|
await handle.sync();
|
|
44103
44720
|
await handle.close();
|
|
44104
44721
|
handle = void 0;
|
|
44105
|
-
await
|
|
44722
|
+
await rename11(temporary, destination);
|
|
44106
44723
|
await sync(scopedRoot);
|
|
44107
44724
|
await sync(root);
|
|
44108
44725
|
} finally {
|
|
44109
44726
|
await handle?.close().catch(() => {
|
|
44110
44727
|
});
|
|
44111
|
-
await
|
|
44728
|
+
await rm15(temporary, { force: true }).catch(() => {
|
|
44112
44729
|
});
|
|
44113
44730
|
}
|
|
44114
44731
|
}
|
|
@@ -44123,8 +44740,8 @@ async function readTerminalOutcomesStrict(root = defaultTerminalOutcomeRoot()) {
|
|
|
44123
44740
|
if (rootStat.isSymbolicLink() || !rootStat.isDirectory()) {
|
|
44124
44741
|
throw new Error("terminal outcome journal root is not a trusted directory");
|
|
44125
44742
|
}
|
|
44126
|
-
await
|
|
44127
|
-
const hostEntries = await
|
|
44743
|
+
await chmod11(root, DIRECTORY_MODE5);
|
|
44744
|
+
const hostEntries = await readdir7(root, { withFileTypes: true });
|
|
44128
44745
|
const outcomes = [];
|
|
44129
44746
|
const resultIds = /* @__PURE__ */ new Set();
|
|
44130
44747
|
for (const hostEntry of hostEntries) {
|
|
@@ -44136,21 +44753,21 @@ async function readTerminalOutcomesStrict(root = defaultTerminalOutcomeRoot()) {
|
|
|
44136
44753
|
if (scopedStat.isSymbolicLink() || !scopedStat.isDirectory()) {
|
|
44137
44754
|
throw new Error("terminal outcome Host scope is not a trusted directory");
|
|
44138
44755
|
}
|
|
44139
|
-
await
|
|
44140
|
-
const entries = await
|
|
44756
|
+
await chmod11(scopedRoot, DIRECTORY_MODE5);
|
|
44757
|
+
const entries = await readdir7(scopedRoot, { withFileTypes: true });
|
|
44141
44758
|
for (const entry of entries) {
|
|
44142
44759
|
if (!entry.name.endsWith(".json")) continue;
|
|
44143
44760
|
const match = OUTCOME_FILE.exec(entry.name);
|
|
44144
44761
|
if (!match || !entry.isFile() || entry.isSymbolicLink()) {
|
|
44145
44762
|
throw new Error("committed terminal outcome is not a trusted regular file");
|
|
44146
44763
|
}
|
|
44147
|
-
const path =
|
|
44764
|
+
const path = join26(scopedRoot, entry.name);
|
|
44148
44765
|
const stat4 = await lstat12(path);
|
|
44149
44766
|
if (!stat4.isFile() || stat4.isSymbolicLink() || stat4.size > MAX_OUTCOME_BYTES) {
|
|
44150
44767
|
throw new Error("committed terminal outcome is not a trusted regular file");
|
|
44151
44768
|
}
|
|
44152
44769
|
const outcome = parseCommittedOutcome(
|
|
44153
|
-
await
|
|
44770
|
+
await readFile13(path, "utf8"),
|
|
44154
44771
|
match[1],
|
|
44155
44772
|
Number(match[2])
|
|
44156
44773
|
);
|
|
@@ -44177,7 +44794,7 @@ async function forgetAcknowledgedTerminalOutcomes(refs, root = defaultTerminalOu
|
|
|
44177
44794
|
if (acknowledged.get(`${hostId}:${outcome.taskId}:${outcome.epoch}`) !== outcome.resultId) {
|
|
44178
44795
|
continue;
|
|
44179
44796
|
}
|
|
44180
|
-
await
|
|
44797
|
+
await rm15(outcomePath(root, hostId, outcome.taskId, outcome.epoch), { force: true });
|
|
44181
44798
|
changedHostRoots.add(hostOutcomeRoot(root, hostId));
|
|
44182
44799
|
}
|
|
44183
44800
|
for (const scopedRoot of changedHostRoots) await syncDirectory6(scopedRoot);
|
|
@@ -44189,22 +44806,22 @@ async function forgetSupersededTerminalOutcomes(hostId, taskId, epoch, root = de
|
|
|
44189
44806
|
if (scoped.hostId !== hostId) continue;
|
|
44190
44807
|
const { outcome } = scoped;
|
|
44191
44808
|
if (outcome.taskId !== taskId || outcome.epoch >= epoch) continue;
|
|
44192
|
-
await
|
|
44809
|
+
await rm15(outcomePath(root, hostId, outcome.taskId, outcome.epoch), { force: true });
|
|
44193
44810
|
removed = true;
|
|
44194
44811
|
}
|
|
44195
44812
|
if (removed) await syncDirectory6(hostOutcomeRoot(root, hostId));
|
|
44196
44813
|
}
|
|
44197
44814
|
|
|
44198
44815
|
// src/accepted-assignments.ts
|
|
44199
|
-
import { chmod as
|
|
44816
|
+
import { chmod as chmod12, lstat as lstat13, mkdir as mkdir20, open as open11, readdir as readdir8, rename as rename12, rm as rm16 } from "node:fs/promises";
|
|
44200
44817
|
import { homedir as homedir15 } from "node:os";
|
|
44201
|
-
import { dirname as dirname15, join as
|
|
44818
|
+
import { dirname as dirname15, join as join27, relative as relative14, resolve as resolve17, sep as sep11 } from "node:path";
|
|
44202
44819
|
var DIRECTORY_MODE6 = 448;
|
|
44203
44820
|
var FILE_MODE5 = 384;
|
|
44204
44821
|
var CLAIM_FILE = /^(tsk_[0-9a-f]{32})\.([1-9][0-9]*)\.json$/;
|
|
44205
44822
|
var TASK_ID = /^tsk_[0-9a-f]{32}$/;
|
|
44206
44823
|
function defaultAcceptedAssignmentRoot() {
|
|
44207
|
-
return
|
|
44824
|
+
return join27(homedir15(), ".zixt", "accepted-assignments");
|
|
44208
44825
|
}
|
|
44209
44826
|
async function syncDirectory7(root) {
|
|
44210
44827
|
if (process.platform === "win32") return;
|
|
@@ -44216,29 +44833,29 @@ async function syncDirectory7(root) {
|
|
|
44216
44833
|
}
|
|
44217
44834
|
}
|
|
44218
44835
|
async function requirePrivateRoot2(root, sync = syncDirectory7) {
|
|
44219
|
-
const firstCreated = await
|
|
44836
|
+
const firstCreated = await mkdir20(root, { recursive: true, mode: DIRECTORY_MODE6 });
|
|
44220
44837
|
if (firstCreated) {
|
|
44221
44838
|
const first = resolve17(firstCreated);
|
|
44222
44839
|
const target = resolve17(root);
|
|
44223
44840
|
await sync(dirname15(first));
|
|
44224
44841
|
let current = first;
|
|
44225
|
-
for (const part of
|
|
44842
|
+
for (const part of relative14(first, target).split(sep11).filter(Boolean)) {
|
|
44226
44843
|
await sync(current);
|
|
44227
|
-
current =
|
|
44844
|
+
current = join27(current, part);
|
|
44228
44845
|
}
|
|
44229
44846
|
}
|
|
44230
44847
|
const stat4 = await lstat13(root);
|
|
44231
44848
|
if (stat4.isSymbolicLink() || !stat4.isDirectory()) {
|
|
44232
44849
|
throw new Error("accepted assignment journal root is not a trusted directory");
|
|
44233
44850
|
}
|
|
44234
|
-
await
|
|
44851
|
+
await chmod12(root, DIRECTORY_MODE6);
|
|
44235
44852
|
}
|
|
44236
44853
|
function claimPath(root, taskId, epoch) {
|
|
44237
44854
|
if (!TASK_ID.test(taskId)) throw new Error("accepted assignment Task identity is malformed");
|
|
44238
44855
|
if (!Number.isSafeInteger(epoch) || epoch < 1) {
|
|
44239
44856
|
throw new Error("accepted assignment epoch is malformed");
|
|
44240
44857
|
}
|
|
44241
|
-
return
|
|
44858
|
+
return join27(root, `${taskId}.${epoch}.json`);
|
|
44242
44859
|
}
|
|
44243
44860
|
async function recordAcceptedAssignment(assignment, root = defaultAcceptedAssignmentRoot(), options = {}) {
|
|
44244
44861
|
const sync = options.syncDirectory ?? syncDirectory7;
|
|
@@ -44248,7 +44865,7 @@ async function recordAcceptedAssignment(assignment, root = defaultAcceptedAssign
|
|
|
44248
44865
|
} catch {
|
|
44249
44866
|
return false;
|
|
44250
44867
|
}
|
|
44251
|
-
const temporary =
|
|
44868
|
+
const temporary = join27(
|
|
44252
44869
|
root,
|
|
44253
44870
|
`.${assignment.taskId}.${assignment.epoch}.${process.pid}.${Date.now()}.tmp`
|
|
44254
44871
|
);
|
|
@@ -44260,7 +44877,7 @@ async function recordAcceptedAssignment(assignment, root = defaultAcceptedAssign
|
|
|
44260
44877
|
await handle.sync();
|
|
44261
44878
|
await handle.close();
|
|
44262
44879
|
handle = void 0;
|
|
44263
|
-
await
|
|
44880
|
+
await rename12(temporary, destination);
|
|
44264
44881
|
if (process.platform !== "win32") await sync(root);
|
|
44265
44882
|
return true;
|
|
44266
44883
|
} catch {
|
|
@@ -44268,7 +44885,7 @@ async function recordAcceptedAssignment(assignment, root = defaultAcceptedAssign
|
|
|
44268
44885
|
} finally {
|
|
44269
44886
|
await handle?.close().catch(() => {
|
|
44270
44887
|
});
|
|
44271
|
-
await
|
|
44888
|
+
await rm16(temporary, { force: true }).catch(() => {
|
|
44272
44889
|
});
|
|
44273
44890
|
}
|
|
44274
44891
|
}
|
|
@@ -44283,9 +44900,9 @@ async function recoverAcceptedAssignments(root = defaultAcceptedAssignmentRoot()
|
|
|
44283
44900
|
if (rootStat.isSymbolicLink() || !rootStat.isDirectory()) {
|
|
44284
44901
|
throw new Error("accepted assignment journal root is not a trusted directory");
|
|
44285
44902
|
}
|
|
44286
|
-
await
|
|
44903
|
+
await chmod12(root, DIRECTORY_MODE6);
|
|
44287
44904
|
const claims = [];
|
|
44288
|
-
for (const entry of await
|
|
44905
|
+
for (const entry of await readdir8(root, { withFileTypes: true })) {
|
|
44289
44906
|
if (!entry.isFile()) continue;
|
|
44290
44907
|
const match = CLAIM_FILE.exec(entry.name);
|
|
44291
44908
|
if (!match) continue;
|
|
@@ -44302,7 +44919,7 @@ async function forgetAcceptedAssignment(assignment, root = defaultAcceptedAssign
|
|
|
44302
44919
|
} catch {
|
|
44303
44920
|
return;
|
|
44304
44921
|
}
|
|
44305
|
-
await
|
|
44922
|
+
await rm16(path, { force: true }).catch(() => {
|
|
44306
44923
|
});
|
|
44307
44924
|
}
|
|
44308
44925
|
async function forgetAcknowledgedAcceptedAssignments(assignments, root = defaultAcceptedAssignmentRoot()) {
|
|
@@ -44310,9 +44927,9 @@ async function forgetAcknowledgedAcceptedAssignments(assignments, root = default
|
|
|
44310
44927
|
}
|
|
44311
44928
|
|
|
44312
44929
|
// src/local-observability.ts
|
|
44313
|
-
import { appendFile, mkdir as
|
|
44930
|
+
import { appendFile, mkdir as mkdir21, open as open12, readdir as readdir9, rename as rename13, rm as rm17, stat as stat3 } from "node:fs/promises";
|
|
44314
44931
|
import { homedir as homedir16 } from "node:os";
|
|
44315
|
-
import { basename as basename7, dirname as dirname16, join as
|
|
44932
|
+
import { basename as basename7, dirname as dirname16, join as join28 } from "node:path";
|
|
44316
44933
|
|
|
44317
44934
|
// src/logger.ts
|
|
44318
44935
|
var ANSI = {
|
|
@@ -44424,11 +45041,11 @@ var LOCAL_STATUS_FILE = "status.json";
|
|
|
44424
45041
|
var LOCAL_REQUESTS_DIR = "requests";
|
|
44425
45042
|
var DEFAULT_CONSOLE_ROTATE_BYTES = 2 * 1024 * 1024;
|
|
44426
45043
|
function defaultLocalObservabilityRoot() {
|
|
44427
|
-
return
|
|
45044
|
+
return join28(homedir16(), ".zixt", "observability");
|
|
44428
45045
|
}
|
|
44429
45046
|
function createLocalConsoleSink(options = {}) {
|
|
44430
45047
|
const root = options.root ?? defaultLocalObservabilityRoot();
|
|
44431
|
-
const consolePath =
|
|
45048
|
+
const consolePath = join28(root, LOCAL_CONSOLE_FILE);
|
|
44432
45049
|
const rotateBytes = options.rotateBytes ?? DEFAULT_CONSOLE_ROTATE_BYTES;
|
|
44433
45050
|
let disabled = false;
|
|
44434
45051
|
let prepared = false;
|
|
@@ -44438,7 +45055,7 @@ function createLocalConsoleSink(options = {}) {
|
|
|
44438
45055
|
if (disabled) return;
|
|
44439
45056
|
try {
|
|
44440
45057
|
if (!prepared) {
|
|
44441
|
-
await
|
|
45058
|
+
await mkdir21(root, { recursive: true, mode: 448 });
|
|
44442
45059
|
approximateBytes = await stat3(consolePath).then(
|
|
44443
45060
|
(existing) => existing.size,
|
|
44444
45061
|
() => 0
|
|
@@ -44446,8 +45063,8 @@ function createLocalConsoleSink(options = {}) {
|
|
|
44446
45063
|
prepared = true;
|
|
44447
45064
|
}
|
|
44448
45065
|
if (approximateBytes >= rotateBytes) {
|
|
44449
|
-
await
|
|
44450
|
-
await
|
|
45066
|
+
await rm17(join28(root, LOCAL_CONSOLE_PREVIOUS_FILE), { force: true });
|
|
45067
|
+
await rename13(consolePath, join28(root, LOCAL_CONSOLE_PREVIOUS_FILE)).catch(
|
|
44451
45068
|
(error52) => {
|
|
44452
45069
|
if (error52.code !== "ENOENT") throw error52;
|
|
44453
45070
|
}
|
|
@@ -44478,11 +45095,11 @@ function createLocalConsoleSink(options = {}) {
|
|
|
44478
45095
|
};
|
|
44479
45096
|
}
|
|
44480
45097
|
async function consumeRunnerInstallRequests(root = defaultLocalObservabilityRoot()) {
|
|
44481
|
-
const directory =
|
|
45098
|
+
const directory = join28(root, LOCAL_REQUESTS_DIR);
|
|
44482
45099
|
const requested = /* @__PURE__ */ new Set();
|
|
44483
45100
|
let names;
|
|
44484
45101
|
try {
|
|
44485
|
-
names = await
|
|
45102
|
+
names = await readdir9(directory);
|
|
44486
45103
|
} catch {
|
|
44487
45104
|
return requested;
|
|
44488
45105
|
}
|
|
@@ -44490,7 +45107,7 @@ async function consumeRunnerInstallRequests(root = defaultLocalObservabilityRoot
|
|
|
44490
45107
|
const name = `install-runner-${type}.json`;
|
|
44491
45108
|
if (!names.includes(name)) continue;
|
|
44492
45109
|
try {
|
|
44493
|
-
await
|
|
45110
|
+
await rm17(join28(directory, name), { force: true });
|
|
44494
45111
|
requested.add(type);
|
|
44495
45112
|
} catch {
|
|
44496
45113
|
}
|
|
@@ -44498,13 +45115,13 @@ async function consumeRunnerInstallRequests(root = defaultLocalObservabilityRoot
|
|
|
44498
45115
|
return requested;
|
|
44499
45116
|
}
|
|
44500
45117
|
async function writeLocalStatus(status, root = defaultLocalObservabilityRoot()) {
|
|
44501
|
-
const destination =
|
|
44502
|
-
const temporary =
|
|
45118
|
+
const destination = join28(root, LOCAL_STATUS_FILE);
|
|
45119
|
+
const temporary = join28(
|
|
44503
45120
|
dirname16(destination),
|
|
44504
45121
|
`.${basename7(destination)}.${process.pid}.${crypto.randomUUID()}.tmp`
|
|
44505
45122
|
);
|
|
44506
45123
|
try {
|
|
44507
|
-
await
|
|
45124
|
+
await mkdir21(root, { recursive: true, mode: 448 });
|
|
44508
45125
|
const handle = await open12(temporary, "wx", 384);
|
|
44509
45126
|
try {
|
|
44510
45127
|
await handle.writeFile(`${JSON.stringify(status)}
|
|
@@ -44512,14 +45129,14 @@ async function writeLocalStatus(status, root = defaultLocalObservabilityRoot())
|
|
|
44512
45129
|
} finally {
|
|
44513
45130
|
await handle.close();
|
|
44514
45131
|
}
|
|
44515
|
-
await
|
|
45132
|
+
await rename13(temporary, destination);
|
|
44516
45133
|
} catch {
|
|
44517
|
-
await
|
|
45134
|
+
await rm17(temporary, { force: true }).catch(() => void 0);
|
|
44518
45135
|
}
|
|
44519
45136
|
}
|
|
44520
45137
|
|
|
44521
45138
|
// src/demo-state.ts
|
|
44522
|
-
import { isAbsolute as isAbsolute19, join as
|
|
45139
|
+
import { isAbsolute as isAbsolute19, join as join29, parse as parse3, resolve as resolve18 } from "node:path";
|
|
44523
45140
|
var DEMO_STATE_ROOT_ENV = "ZIXT_DEMO_STATE_ROOT";
|
|
44524
45141
|
function resolveDemoHostStatePaths(env = process.env) {
|
|
44525
45142
|
const configured = env.ZIXT_RUNNER === "demo" ? env[DEMO_STATE_ROOT_ENV] : void 0;
|
|
@@ -44529,14 +45146,14 @@ function resolveDemoHostStatePaths(env = process.env) {
|
|
|
44529
45146
|
throw new Error(`${DEMO_STATE_ROOT_ENV} must be a dedicated absolute directory`);
|
|
44530
45147
|
}
|
|
44531
45148
|
return {
|
|
44532
|
-
runRegistryRoot:
|
|
44533
|
-
terminalOutcomeRoot:
|
|
44534
|
-
acceptedAssignmentRoot:
|
|
44535
|
-
runArtifactRoot:
|
|
44536
|
-
browserProfileRoot:
|
|
44537
|
-
runnerWorkspaceRoot:
|
|
44538
|
-
codexThreadIndexRoot:
|
|
44539
|
-
localObservabilityRoot:
|
|
45149
|
+
runRegistryRoot: join29(root, "run-registry"),
|
|
45150
|
+
terminalOutcomeRoot: join29(root, "terminal-outcomes"),
|
|
45151
|
+
acceptedAssignmentRoot: join29(root, "accepted-assignments"),
|
|
45152
|
+
runArtifactRoot: join29(root, "run-artifacts"),
|
|
45153
|
+
browserProfileRoot: join29(root, "browser-profiles"),
|
|
45154
|
+
runnerWorkspaceRoot: join29(root, "workspaces"),
|
|
45155
|
+
codexThreadIndexRoot: join29(root, "codex-threads"),
|
|
45156
|
+
localObservabilityRoot: join29(root, "local-observability")
|
|
44540
45157
|
};
|
|
44541
45158
|
}
|
|
44542
45159
|
|