@codions/portta 0.0.0-develop.169.sha-64ee68c → 0.0.0-develop.172.sha-10ce987
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/assets/skills/portta-workflows/SKILL.md +1 -1
- package/dist/assets/workflows/bake-off.workflow.js +6 -5
- package/dist/assets/workflows/code-review.workflow.js +4 -4
- package/dist/assets/workflows/deep-research.workflow.js +4 -3
- package/dist/assets/workflows/multi-provider-review.workflow.js +5 -4
- package/dist/assets/workflows/provider-debate.workflow.js +7 -6
- package/dist/assets/workflows/second-opinion.workflow.js +4 -3
- package/dist/cli.js +960 -875
- package/dist/documentation.json +1 -1
- package/dist/host.js +111 -99
- package/dist/runtime/.env.example +8 -17
- package/dist/runtime/VERSION +1 -1
- package/dist/runtime/docker/compose/features/panel-host.yaml +4 -5
- package/dist/runtime/templates/README.md +9 -6
- package/dist/runtime/templates/overlays/07-worktree.env +1 -1
- package/dist/runtime/templates/project/PORTTA.md +3 -3
- package/dist/supervisor.js +0 -1
- package/package.json +2 -5
- package/dist/runtime/config/traefik/dynamic/auth.example.yaml.disabled +0 -24
- package/dist/runtime/docker/images/README.md +0 -12
- package/dist/runtime/docker/images/sandbox/Dockerfile +0 -79
- package/dist/runtime/docker/images/sandbox/README.md +0 -109
- package/dist/runtime/docker/images/sandbox/VERSION +0 -1
- package/dist/runtime/docker/images/sandbox/agents/claude-code.sh +0 -18
- package/dist/runtime/docker/images/sandbox/agents/codex.sh +0 -4
- package/dist/runtime/docker/images/sandbox/agents/opencode.sh +0 -4
- package/dist/runtime/docker/images/sandbox/agents/pi.sh +0 -4
- package/dist/runtime/docker/images/sandbox/scripts/entrypoint.sh +0 -8
- package/dist/runtime/docker/images/sandbox/scripts/generate-manifest.mjs +0 -81
- package/dist/runtime/docker/images/sandbox/scripts/install-tools.sh +0 -46
- package/dist/runtime/docker/images/sandbox/scripts/verify-image.sh +0 -18
package/dist/cli.js
CHANGED
|
@@ -19820,17 +19820,12 @@ function fitLabel(label3, limit = MAX_LABEL) {
|
|
|
19820
19820
|
const keep = limit - digest2.length - 1;
|
|
19821
19821
|
return `${label3.slice(0, keep).replace(/-+$/, "")}-${digest2}`;
|
|
19822
19822
|
}
|
|
19823
|
-
function hostLabel(parts2
|
|
19823
|
+
function hostLabel(parts2) {
|
|
19824
19824
|
const project2 = slug(parts2.project);
|
|
19825
19825
|
const service2 = slug(parts2.service);
|
|
19826
19826
|
const context = parts2.context ? slug(parts2.context) : "";
|
|
19827
|
-
|
|
19828
|
-
|
|
19829
|
-
return fitLabel(context ? `${base2}${COMPONENT_SEPARATOR}${context}` : base2);
|
|
19830
|
-
}
|
|
19831
|
-
const components = [service2, project2];
|
|
19832
|
-
if (context) components.push(context);
|
|
19833
|
-
return fitLabel(components.filter(Boolean).join(COMPONENT_SEPARATOR));
|
|
19827
|
+
const base2 = service2 ? `${project2}-${service2}` : project2;
|
|
19828
|
+
return fitLabel(context ? `${base2}${COMPONENT_SEPARATOR}${context}` : base2);
|
|
19834
19829
|
}
|
|
19835
19830
|
var MAX_LABEL, COMPONENT_SEPARATOR;
|
|
19836
19831
|
var init_hostname = __esm({
|
|
@@ -20014,10 +20009,6 @@ function composeFiles(config3) {
|
|
|
20014
20009
|
if (config3.tunnelEnabled) files.push("docker/compose/features/cloudflare-tunnel.yaml");
|
|
20015
20010
|
return files;
|
|
20016
20011
|
}
|
|
20017
|
-
function composeFilesForRoot(config3, root2) {
|
|
20018
|
-
void root2;
|
|
20019
|
-
return composeFiles(config3);
|
|
20020
|
-
}
|
|
20021
20012
|
var AUTH_BUILD_FILE, AUTH_DEV_FILE, TASKFLOW_FILE, PANEL_HOST_FILE, TRUTHY, GATEWAY_PROFILES, PANEL_ACCESS_MODES, PANEL_ACCESS_WITHOUT_AUTH, PANEL_AUTH_MODES;
|
|
20022
20013
|
var init_config = __esm({
|
|
20023
20014
|
"../core/src/config.ts"() {
|
|
@@ -20354,7 +20345,6 @@ var BRIDGE_IMAGE, KIND_RULES;
|
|
|
20354
20345
|
var init_discovery = __esm({
|
|
20355
20346
|
"../core/src/discovery.ts"() {
|
|
20356
20347
|
"use strict";
|
|
20357
|
-
init_namespace();
|
|
20358
20348
|
BRIDGE_IMAGE = "alpine/socat:1.8.1.3";
|
|
20359
20349
|
KIND_RULES = [
|
|
20360
20350
|
{ match: /postgres|postgis|timescale/, kind: "postgres", port: 5432 },
|
|
@@ -20373,7 +20363,6 @@ var init_discovery = __esm({
|
|
|
20373
20363
|
});
|
|
20374
20364
|
|
|
20375
20365
|
// ../core/src/endpoints.ts
|
|
20376
|
-
var PROVIDERS, PROVIDERS_BY_ID;
|
|
20377
20366
|
var init_endpoints = __esm({
|
|
20378
20367
|
"../core/src/endpoints.ts"() {
|
|
20379
20368
|
"use strict";
|
|
@@ -20381,19 +20370,6 @@ var init_endpoints = __esm({
|
|
|
20381
20370
|
init_discovery();
|
|
20382
20371
|
init_domain();
|
|
20383
20372
|
init_hostname();
|
|
20384
|
-
PROVIDERS = [
|
|
20385
|
-
{ id: "internal", scope: "internal", requires: null, optional: false },
|
|
20386
|
-
{ id: "local", scope: "local", requires: "localhost", optional: false },
|
|
20387
|
-
{ id: "lan", scope: "lan", requires: "lan", optional: true },
|
|
20388
|
-
{ id: "tailscale", scope: "private", requires: "tailscale", optional: true },
|
|
20389
|
-
{ id: "tailscale-serve", scope: "private", requires: "tailscale-https", optional: true },
|
|
20390
|
-
{ id: "public-ip", scope: "public", requires: "public-ipv4", optional: true },
|
|
20391
|
-
{ id: "auto-domain", scope: "public", requires: "auto-domain", optional: true },
|
|
20392
|
-
{ id: "custom-domain", scope: "public", requires: "custom-domain", optional: true },
|
|
20393
|
-
{ id: "cloudflare-tunnel", scope: "public", requires: "cloudflare-tunnel", optional: true },
|
|
20394
|
-
{ id: "bridge", scope: "local", requires: null, optional: true }
|
|
20395
|
-
];
|
|
20396
|
-
PROVIDERS_BY_ID = new Map(PROVIDERS.map((spec) => [spec.id, spec]));
|
|
20397
20373
|
}
|
|
20398
20374
|
});
|
|
20399
20375
|
|
|
@@ -20575,15 +20551,6 @@ var init_env = __esm({
|
|
|
20575
20551
|
}
|
|
20576
20552
|
});
|
|
20577
20553
|
|
|
20578
|
-
// ../core/src/host.ts
|
|
20579
|
-
var MIB;
|
|
20580
|
-
var init_host = __esm({
|
|
20581
|
-
"../core/src/host.ts"() {
|
|
20582
|
-
"use strict";
|
|
20583
|
-
MIB = 1024 * 1024;
|
|
20584
|
-
}
|
|
20585
|
-
});
|
|
20586
|
-
|
|
20587
20554
|
// ../core/src/host-security.ts
|
|
20588
20555
|
function permissionProblem(output2) {
|
|
20589
20556
|
return /permission denied|operation not permitted|must be root|need to be root|requires root|not authorized/i.test(output2) ? "could not be checked: this user does not have permission" : null;
|
|
@@ -21311,6 +21278,113 @@ var init_password = __esm({
|
|
|
21311
21278
|
}
|
|
21312
21279
|
});
|
|
21313
21280
|
|
|
21281
|
+
// ../core/src/project-config.ts
|
|
21282
|
+
function hostShape(value3) {
|
|
21283
|
+
if (/:\/\//.test(value3)) return "looks like a URL";
|
|
21284
|
+
if (value3.startsWith("//")) return "looks like a URL";
|
|
21285
|
+
if (value3.startsWith("/")) return "is an absolute path";
|
|
21286
|
+
if (/^[a-z]:[\\/]/i.test(value3)) return "is an absolute path";
|
|
21287
|
+
if (value3.startsWith("~")) return "is a home-relative path, which differs per machine";
|
|
21288
|
+
if (/^\d{1,5}:\d{1,5}$/.test(value3)) return "looks like a published host port";
|
|
21289
|
+
return null;
|
|
21290
|
+
}
|
|
21291
|
+
function defaultProjectConfig() {
|
|
21292
|
+
return {
|
|
21293
|
+
version: 1,
|
|
21294
|
+
name: null,
|
|
21295
|
+
mainBranch: null,
|
|
21296
|
+
worktrees: {
|
|
21297
|
+
root: PROJECT_CONFIG_DEFAULTS.worktreeRoot,
|
|
21298
|
+
branchPattern: PROJECT_CONFIG_DEFAULTS.branchPattern
|
|
21299
|
+
},
|
|
21300
|
+
stack: [],
|
|
21301
|
+
instructions: []
|
|
21302
|
+
};
|
|
21303
|
+
}
|
|
21304
|
+
function parseProjectConfig(document) {
|
|
21305
|
+
if (document === null || document === void 0) {
|
|
21306
|
+
return { ok: false, issues: [{ path: "", message: `${PROJECT_CONFIG_FILE} is empty` }] };
|
|
21307
|
+
}
|
|
21308
|
+
const parsed = Schema.safeParse(document);
|
|
21309
|
+
if (!parsed.success) {
|
|
21310
|
+
return {
|
|
21311
|
+
ok: false,
|
|
21312
|
+
issues: parsed.error.issues.map((issue2) => ({
|
|
21313
|
+
path: issue2.path.join("."),
|
|
21314
|
+
message: issue2.message
|
|
21315
|
+
}))
|
|
21316
|
+
};
|
|
21317
|
+
}
|
|
21318
|
+
const value3 = parsed.data;
|
|
21319
|
+
const defaults = defaultProjectConfig();
|
|
21320
|
+
return {
|
|
21321
|
+
ok: true,
|
|
21322
|
+
config: {
|
|
21323
|
+
version: 1,
|
|
21324
|
+
name: value3.name ?? null,
|
|
21325
|
+
mainBranch: value3.mainBranch ?? null,
|
|
21326
|
+
worktrees: {
|
|
21327
|
+
root: value3.worktrees?.root ?? defaults.worktrees.root,
|
|
21328
|
+
branchPattern: value3.worktrees?.branchPattern ?? defaults.worktrees.branchPattern
|
|
21329
|
+
},
|
|
21330
|
+
stack: value3.stack ?? [],
|
|
21331
|
+
instructions: value3.instructions ?? []
|
|
21332
|
+
}
|
|
21333
|
+
};
|
|
21334
|
+
}
|
|
21335
|
+
var PROJECT_CONFIG_FILE, PROJECT_CONFIG_PATH, PROJECT_CONFIG_DEFAULTS, BRANCH_PATTERN_PLACEHOLDERS, portable, repoRelativePath, branchPattern, Schema;
|
|
21336
|
+
var init_project_config = __esm({
|
|
21337
|
+
"../core/src/project-config.ts"() {
|
|
21338
|
+
"use strict";
|
|
21339
|
+
init_zod();
|
|
21340
|
+
PROJECT_CONFIG_FILE = "project.yaml";
|
|
21341
|
+
PROJECT_CONFIG_PATH = `.portta/${PROJECT_CONFIG_FILE}`;
|
|
21342
|
+
PROJECT_CONFIG_DEFAULTS = {
|
|
21343
|
+
worktreeRoot: ".portta/worktrees",
|
|
21344
|
+
branchPattern: "{type}/{slug}"
|
|
21345
|
+
};
|
|
21346
|
+
BRANCH_PATTERN_PLACEHOLDERS = ["type", "slug"];
|
|
21347
|
+
portable = (label3) => external_exports.string().trim().min(1).superRefine((value3, ctx) => {
|
|
21348
|
+
const shape = hostShape(value3);
|
|
21349
|
+
if (shape) {
|
|
21350
|
+
ctx.addIssue({
|
|
21351
|
+
code: "custom",
|
|
21352
|
+
message: `${label3} ${shape}; that belongs to the host, not to the project (ADR 0052)`
|
|
21353
|
+
});
|
|
21354
|
+
}
|
|
21355
|
+
});
|
|
21356
|
+
repoRelativePath = (label3) => portable(label3).superRefine((value3, ctx) => {
|
|
21357
|
+
const normalized = value3.replaceAll("\\", "/");
|
|
21358
|
+
if (normalized.split("/").includes("..")) {
|
|
21359
|
+
ctx.addIssue({ code: "custom", message: `${label3} escapes the repository with '..'` });
|
|
21360
|
+
}
|
|
21361
|
+
});
|
|
21362
|
+
branchPattern = portable("the branch pattern").superRefine((value3, ctx) => {
|
|
21363
|
+
const used = [...value3.matchAll(/\{([^}]*)\}/g)].map((match2) => match2[1] ?? "");
|
|
21364
|
+
const allowed = new Set(BRANCH_PATTERN_PLACEHOLDERS);
|
|
21365
|
+
for (const placeholder of used) {
|
|
21366
|
+
if (!allowed.has(placeholder)) {
|
|
21367
|
+
ctx.addIssue({
|
|
21368
|
+
code: "custom",
|
|
21369
|
+
message: `unknown placeholder {${placeholder}}; the branch pattern accepts ${BRANCH_PATTERN_PLACEHOLDERS.map((name) => `{${name}}`).join(" and ")}`
|
|
21370
|
+
});
|
|
21371
|
+
}
|
|
21372
|
+
}
|
|
21373
|
+
});
|
|
21374
|
+
Schema = external_exports.strictObject({
|
|
21375
|
+
version: external_exports.literal(1),
|
|
21376
|
+
name: portable("the project name").optional(),
|
|
21377
|
+
mainBranch: portable("the main branch").optional(),
|
|
21378
|
+
worktrees: external_exports.strictObject({
|
|
21379
|
+
root: repoRelativePath("the worktree root").optional(),
|
|
21380
|
+
branchPattern: branchPattern.optional()
|
|
21381
|
+
}).optional(),
|
|
21382
|
+
stack: external_exports.array(external_exports.string().trim().min(1).regex(/^[a-z0-9][a-z0-9.+-]*$/i, "a stack entry is a plain identifier such as node or mysql")).optional(),
|
|
21383
|
+
instructions: external_exports.array(repoRelativePath("an instruction path")).optional()
|
|
21384
|
+
});
|
|
21385
|
+
}
|
|
21386
|
+
});
|
|
21387
|
+
|
|
21314
21388
|
// ../core/src/project-order.ts
|
|
21315
21389
|
function parseDependsOn(label3) {
|
|
21316
21390
|
if (!label3 || label3.trim() === "") return [];
|
|
@@ -22317,7 +22391,6 @@ var init_src = __esm({
|
|
|
22317
22391
|
init_endpoints();
|
|
22318
22392
|
init_env();
|
|
22319
22393
|
init_hostname();
|
|
22320
|
-
init_host();
|
|
22321
22394
|
init_host_security();
|
|
22322
22395
|
init_images();
|
|
22323
22396
|
init_metrics();
|
|
@@ -22326,6 +22399,7 @@ var init_src = __esm({
|
|
|
22326
22399
|
init_namespace();
|
|
22327
22400
|
init_paths();
|
|
22328
22401
|
init_password();
|
|
22402
|
+
init_project_config();
|
|
22329
22403
|
init_project_order();
|
|
22330
22404
|
init_projects_home();
|
|
22331
22405
|
init_protections();
|
|
@@ -22393,7 +22467,7 @@ var init_package = __esm({
|
|
|
22393
22467
|
"package.json"() {
|
|
22394
22468
|
package_default = {
|
|
22395
22469
|
name: "@codions/portta",
|
|
22396
|
-
version: "0.0.0-develop.
|
|
22470
|
+
version: "0.0.0-develop.172.sha-10ce987",
|
|
22397
22471
|
description: "Machine-first CLI for a shared Docker development gateway",
|
|
22398
22472
|
license: "MIT",
|
|
22399
22473
|
type: "module",
|
|
@@ -22412,10 +22486,7 @@ var init_package = __esm({
|
|
|
22412
22486
|
typecheck: "tsc -p tsconfig.json --noEmit",
|
|
22413
22487
|
test: "vitest run",
|
|
22414
22488
|
prepack: "npm run build",
|
|
22415
|
-
prepublishOnly: "npm run typecheck && npm test"
|
|
22416
|
-
preversion: "node scripts/git-version.mjs check",
|
|
22417
|
-
version: "node scripts/git-version.mjs sync",
|
|
22418
|
-
postversion: "node scripts/git-version.mjs verify"
|
|
22489
|
+
prepublishOnly: "npm run typecheck && npm test"
|
|
22419
22490
|
},
|
|
22420
22491
|
engines: {
|
|
22421
22492
|
node: ">=24"
|
|
@@ -22467,7 +22538,7 @@ var init_version = __esm({
|
|
|
22467
22538
|
"use strict";
|
|
22468
22539
|
init_package();
|
|
22469
22540
|
CLI_VERSION = package_default.version;
|
|
22470
|
-
CLI_BUILD_DATE = true ? "2026-09-
|
|
22541
|
+
CLI_BUILD_DATE = true ? "2026-09-18T02:24:34Z" : void 0;
|
|
22471
22542
|
}
|
|
22472
22543
|
});
|
|
22473
22544
|
|
|
@@ -22483,12 +22554,13 @@ function versionForRoot(root2) {
|
|
|
22483
22554
|
if (existsSync4(generated)) return readFileSync4(generated, "utf8").trim();
|
|
22484
22555
|
const manifest = join5(root2, "packages", "cli", "package.json");
|
|
22485
22556
|
if (existsSync4(manifest)) return LOCAL_DEV_VERSION;
|
|
22486
|
-
throw new PreconditionError(`missing generated VERSION in ${root2}`, "reinstall the portta npm package");
|
|
22557
|
+
throw new PreconditionError(`missing generated VERSION in ${root2}`, "reinstall the @codions/portta npm package");
|
|
22487
22558
|
}
|
|
22488
22559
|
function wellKnownRoots() {
|
|
22489
22560
|
const home2 = process.env["HOME"];
|
|
22490
22561
|
return [
|
|
22491
22562
|
process.env["PORTTA_HOME"],
|
|
22563
|
+
home2 ? join5(home2, "portta") : void 0,
|
|
22492
22564
|
"/opt/portta",
|
|
22493
22565
|
home2 ? join5(home2, ".portta") : void 0,
|
|
22494
22566
|
"/var/lib/portta"
|
|
@@ -22528,7 +22600,7 @@ function gatewayContext(options = {}) {
|
|
|
22528
22600
|
const config3 = loadGatewayConfig(env);
|
|
22529
22601
|
env["PORTTA_DOMAIN"] = config3.domain;
|
|
22530
22602
|
env["PORTTA_BIND_ADDRESS"] = config3.bindAddress;
|
|
22531
|
-
const files =
|
|
22603
|
+
const files = composeFiles(config3);
|
|
22532
22604
|
for (const fileName of files) {
|
|
22533
22605
|
if (!existsSync4(join5(root2, fileName))) throw new PreconditionError(`missing compose file: ${fileName}`);
|
|
22534
22606
|
}
|
|
@@ -34233,6 +34305,138 @@ var init_execa = __esm({
|
|
|
34233
34305
|
}
|
|
34234
34306
|
});
|
|
34235
34307
|
|
|
34308
|
+
// src/process.ts
|
|
34309
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
34310
|
+
import { closeSync as closeSync2, openSync as openSync2 } from "node:fs";
|
|
34311
|
+
function setProcessReporter(options) {
|
|
34312
|
+
reporting = options;
|
|
34313
|
+
}
|
|
34314
|
+
function effectiveStdio(requested) {
|
|
34315
|
+
if (requested === "inherit") return "inherit";
|
|
34316
|
+
if (reporting.quiet) return "pipe";
|
|
34317
|
+
if (reporting.verbose) return "stream";
|
|
34318
|
+
return requested;
|
|
34319
|
+
}
|
|
34320
|
+
function outputTargets(mode) {
|
|
34321
|
+
if (mode !== "stream") return { stdio: mode };
|
|
34322
|
+
return {
|
|
34323
|
+
stdin: "pipe",
|
|
34324
|
+
stdout: reporting.json ? "pipe" : ["pipe", "inherit"],
|
|
34325
|
+
stderr: ["pipe", "inherit"]
|
|
34326
|
+
};
|
|
34327
|
+
}
|
|
34328
|
+
function elapsed(sinceMs) {
|
|
34329
|
+
const seconds = Math.round((Date.now() - sinceMs) / 1e3);
|
|
34330
|
+
if (seconds < 60) return `${seconds}s`;
|
|
34331
|
+
return `${Math.floor(seconds / 60)}m${String(seconds % 60).padStart(2, "0")}s`;
|
|
34332
|
+
}
|
|
34333
|
+
function describeCommand(file2, args, limit = 88) {
|
|
34334
|
+
const words = [];
|
|
34335
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
34336
|
+
const argument = args[index] ?? "";
|
|
34337
|
+
if (argument === "-f" || argument === "--file") {
|
|
34338
|
+
index += 1;
|
|
34339
|
+
continue;
|
|
34340
|
+
}
|
|
34341
|
+
words.push(argument);
|
|
34342
|
+
}
|
|
34343
|
+
const line4 = [file2, ...words].join(" ");
|
|
34344
|
+
return line4.length <= limit ? line4 : `${line4.slice(0, limit - 1)}\u2026`;
|
|
34345
|
+
}
|
|
34346
|
+
function startHeartbeat(file2, args) {
|
|
34347
|
+
if (reporting.quiet) return () => {
|
|
34348
|
+
};
|
|
34349
|
+
const started = Date.now();
|
|
34350
|
+
const label3 = describeCommand(file2, args);
|
|
34351
|
+
let hinted = false;
|
|
34352
|
+
const announce = () => {
|
|
34353
|
+
process.stderr.write(`wait still running: ${label3} (${elapsed(started)})
|
|
34354
|
+
`);
|
|
34355
|
+
if (!hinted && Date.now() - started >= HEARTBEAT_HINT_MS) {
|
|
34356
|
+
hinted = true;
|
|
34357
|
+
process.stderr.write(" -> a first build of the panel image takes several minutes; Ctrl-C is safe, and --verbose shows its output\n");
|
|
34358
|
+
}
|
|
34359
|
+
};
|
|
34360
|
+
let repeat;
|
|
34361
|
+
const first = setTimeout(() => {
|
|
34362
|
+
announce();
|
|
34363
|
+
repeat = setInterval(announce, HEARTBEAT_EVERY_MS);
|
|
34364
|
+
repeat.unref?.();
|
|
34365
|
+
}, HEARTBEAT_FIRST_MS);
|
|
34366
|
+
first.unref?.();
|
|
34367
|
+
return () => {
|
|
34368
|
+
clearTimeout(first);
|
|
34369
|
+
if (repeat) clearInterval(repeat);
|
|
34370
|
+
};
|
|
34371
|
+
}
|
|
34372
|
+
async function runProcess(file2, args = [], options = {}) {
|
|
34373
|
+
const mode = effectiveStdio(options.stdio ?? "pipe");
|
|
34374
|
+
const execaOptions = {
|
|
34375
|
+
cwd: options.cwd,
|
|
34376
|
+
env: options.env,
|
|
34377
|
+
input: options.input,
|
|
34378
|
+
reject: options.reject ?? true,
|
|
34379
|
+
shell: false,
|
|
34380
|
+
timeout: options.timeout,
|
|
34381
|
+
...outputTargets(mode)
|
|
34382
|
+
};
|
|
34383
|
+
const stopHeartbeat = mode === "pipe" ? startHeartbeat(file2, args) : () => {
|
|
34384
|
+
};
|
|
34385
|
+
try {
|
|
34386
|
+
const result = await execa(file2, [...args], execaOptions);
|
|
34387
|
+
const notStarted = result.failed && result.exitCode === void 0 && !result.signal && !result.timedOut;
|
|
34388
|
+
return {
|
|
34389
|
+
stdout: typeof result.stdout === "string" ? result.stdout : "",
|
|
34390
|
+
stderr: typeof result.stderr === "string" ? result.stderr : "",
|
|
34391
|
+
exitCode: result.exitCode ?? (result.failed ? 1 : 0),
|
|
34392
|
+
failed: result.failed,
|
|
34393
|
+
...notStarted && "originalMessage" in result ? { error: String(result.originalMessage) } : {}
|
|
34394
|
+
};
|
|
34395
|
+
} finally {
|
|
34396
|
+
stopHeartbeat();
|
|
34397
|
+
}
|
|
34398
|
+
}
|
|
34399
|
+
async function runForeground(file2, args, options = {}) {
|
|
34400
|
+
const child = execa(file2, [...args], { cwd: options.cwd, env: options.env, stdio: "inherit", reject: false, shell: false });
|
|
34401
|
+
const forwarded = ["SIGINT", "SIGTERM", "SIGHUP"].map((signal) => [signal, () => {
|
|
34402
|
+
child.kill(signal);
|
|
34403
|
+
}]);
|
|
34404
|
+
for (const [signal, forward] of forwarded) process.on(signal, forward);
|
|
34405
|
+
try {
|
|
34406
|
+
const result = await child;
|
|
34407
|
+
return result.exitCode ?? (result.signal ? 1 : 0);
|
|
34408
|
+
} finally {
|
|
34409
|
+
for (const [signal, forward] of forwarded) process.off(signal, forward);
|
|
34410
|
+
}
|
|
34411
|
+
}
|
|
34412
|
+
function spawnDetached(file2, args, options = {}) {
|
|
34413
|
+
const log3 = options.logFile ? openSync2(options.logFile, "a", 384) : null;
|
|
34414
|
+
try {
|
|
34415
|
+
const child = spawn2(file2, [...args], {
|
|
34416
|
+
cwd: options.cwd,
|
|
34417
|
+
env: options.env,
|
|
34418
|
+
shell: false,
|
|
34419
|
+
detached: true,
|
|
34420
|
+
stdio: log3 === null ? "ignore" : ["ignore", log3, log3]
|
|
34421
|
+
});
|
|
34422
|
+
child.unref();
|
|
34423
|
+
return child.pid;
|
|
34424
|
+
} finally {
|
|
34425
|
+
if (log3 !== null) closeSync2(log3);
|
|
34426
|
+
}
|
|
34427
|
+
}
|
|
34428
|
+
var reporting, HEARTBEAT_FIRST_MS, HEARTBEAT_EVERY_MS, HEARTBEAT_HINT_MS;
|
|
34429
|
+
var init_process = __esm({
|
|
34430
|
+
"src/process.ts"() {
|
|
34431
|
+
"use strict";
|
|
34432
|
+
init_execa();
|
|
34433
|
+
reporting = {};
|
|
34434
|
+
HEARTBEAT_FIRST_MS = 1e4;
|
|
34435
|
+
HEARTBEAT_EVERY_MS = 3e4;
|
|
34436
|
+
HEARTBEAT_HINT_MS = 18e4;
|
|
34437
|
+
}
|
|
34438
|
+
});
|
|
34439
|
+
|
|
34236
34440
|
// ../../node_modules/hono/dist/compose.js
|
|
34237
34441
|
var compose;
|
|
34238
34442
|
var init_compose = __esm({
|
|
@@ -36704,7 +36908,7 @@ var init_user_shell_env = __esm({
|
|
|
36704
36908
|
// ../host/src/modules/taskflow/lib/host-tools.ts
|
|
36705
36909
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
36706
36910
|
import { accessSync as accessSync2, constants as constants5, statSync as statSync3 } from "node:fs";
|
|
36707
|
-
import { delimiter as delimiter2, dirname as dirname5, isAbsolute, join as
|
|
36911
|
+
import { delimiter as delimiter2, dirname as dirname5, isAbsolute, join as join9 } from "node:path";
|
|
36708
36912
|
function defaultIsExecutableFile(candidate) {
|
|
36709
36913
|
try {
|
|
36710
36914
|
accessSync2(candidate, constants5.X_OK);
|
|
@@ -36718,20 +36922,20 @@ function firstExecutable(candidates, isExecutable) {
|
|
|
36718
36922
|
}
|
|
36719
36923
|
function pathCandidates(tool, pathEnv) {
|
|
36720
36924
|
if (!pathEnv) return [];
|
|
36721
|
-
return pathEnv.split(delimiter2).filter(Boolean).map((entry) =>
|
|
36925
|
+
return pathEnv.split(delimiter2).filter(Boolean).map((entry) => join9(entry, tool));
|
|
36722
36926
|
}
|
|
36723
36927
|
function wellKnownBinDirectories(input2) {
|
|
36724
36928
|
const directories = [];
|
|
36725
36929
|
const home2 = input2.env.HOME?.trim();
|
|
36726
36930
|
if (home2) {
|
|
36727
36931
|
directories.push(
|
|
36728
|
-
|
|
36729
|
-
|
|
36730
|
-
|
|
36731
|
-
|
|
36732
|
-
|
|
36733
|
-
|
|
36734
|
-
|
|
36932
|
+
join9(home2, ".local", "bin"),
|
|
36933
|
+
join9(home2, ".cargo", "bin"),
|
|
36934
|
+
join9(home2, ".bun", "bin"),
|
|
36935
|
+
join9(home2, ".volta", "bin"),
|
|
36936
|
+
join9(home2, ".asdf", "shims"),
|
|
36937
|
+
join9(home2, ".local", "share", "mise", "shims"),
|
|
36938
|
+
join9(home2, ".npm-global", "bin")
|
|
36735
36939
|
);
|
|
36736
36940
|
}
|
|
36737
36941
|
directories.push(dirname5(input2.execPath));
|
|
@@ -36742,7 +36946,7 @@ function wellKnownBinDirectories(input2) {
|
|
|
36742
36946
|
}
|
|
36743
36947
|
function wellKnownToolPaths(tool, env) {
|
|
36744
36948
|
const home2 = env.HOME?.trim();
|
|
36745
|
-
if (tool === "claude" && home2) return [
|
|
36949
|
+
if (tool === "claude" && home2) return [join9(home2, ".claude", "local", "claude")];
|
|
36746
36950
|
return [];
|
|
36747
36951
|
}
|
|
36748
36952
|
function loginWhichCommand(tool) {
|
|
@@ -36789,7 +36993,7 @@ function resolveNpmGlobalBin(input2) {
|
|
|
36789
36993
|
env: input2.env,
|
|
36790
36994
|
execPath: input2.execPath,
|
|
36791
36995
|
platform: input2.platform
|
|
36792
|
-
}).map((directory) =>
|
|
36996
|
+
}).map((directory) => join9(directory, "npm")),
|
|
36793
36997
|
input2.isExecutable
|
|
36794
36998
|
);
|
|
36795
36999
|
if (!npm) return null;
|
|
@@ -36801,7 +37005,7 @@ function resolveNpmGlobalBin(input2) {
|
|
|
36801
37005
|
});
|
|
36802
37006
|
if (result.error || result.status !== 0) return null;
|
|
36803
37007
|
const prefix = (result.stdout ?? "").trim().split(/\r?\n/u)[0]?.trim();
|
|
36804
|
-
return prefix ?
|
|
37008
|
+
return prefix ? join9(prefix, "bin") : null;
|
|
36805
37009
|
}
|
|
36806
37010
|
function createHostToolResolver(options = {}) {
|
|
36807
37011
|
const env = options.env ?? process.env;
|
|
@@ -36830,7 +37034,7 @@ function createHostToolResolver(options = {}) {
|
|
|
36830
37034
|
npmGlobalBin = resolveNpmGlobalBin({ env, execPath: execPath2, isExecutable, platform: platform4 });
|
|
36831
37035
|
}
|
|
36832
37036
|
const wellKnown = wellKnownBinDirectories({ env, execPath: execPath2, npmGlobalBin, platform: platform4 }).map(
|
|
36833
|
-
(directory) =>
|
|
37037
|
+
(directory) => join9(directory, name)
|
|
36834
37038
|
);
|
|
36835
37039
|
const wellKnownHit = firstExecutable(wellKnown, isExecutable);
|
|
36836
37040
|
if (wellKnownHit) return wellKnownHit;
|
|
@@ -36943,7 +37147,7 @@ function agentTemplatePlaceholder(variable) {
|
|
|
36943
37147
|
function hostPortFromEnv(env) {
|
|
36944
37148
|
return parseInt(env[ENV_NAMES.hostPort] || env.PORT || String(APP_DEFAULTS.port), 10);
|
|
36945
37149
|
}
|
|
36946
|
-
var APP_NAME, APP_SLUG, CLI_NAME, GLOBAL_CONFIG_DIR, HOST_STATE_DIR, PROJECT_CONFIG_DIR, PATH_NAMES,
|
|
37150
|
+
var APP_NAME, APP_SLUG, CLI_NAME, GLOBAL_CONFIG_DIR, HOST_STATE_DIR, PROJECT_CONFIG_DIR, PATH_NAMES, PROJECT_CONFIG_PATH2, PROJECT_LOCAL_CONFIG_PATH, APP_DEFAULTS, ENV_NAMES, RUNTIME_IDENTITY, SERVICE_IDENTITY, LINEAR_IDENTITY;
|
|
36947
37151
|
var init_config2 = __esm({
|
|
36948
37152
|
"../core/src/taskflow/config.ts"() {
|
|
36949
37153
|
"use strict";
|
|
@@ -36972,7 +37176,7 @@ var init_config2 = __esm({
|
|
|
36972
37176
|
archiveState: "archive.json",
|
|
36973
37177
|
openSessionsState: "open-sessions.json"
|
|
36974
37178
|
};
|
|
36975
|
-
|
|
37179
|
+
PROJECT_CONFIG_PATH2 = `${PROJECT_CONFIG_DIR}/${PATH_NAMES.projectConfig}`;
|
|
36976
37180
|
PROJECT_LOCAL_CONFIG_PATH = `${PROJECT_CONFIG_DIR}/${PATH_NAMES.projectLocalConfig}`;
|
|
36977
37181
|
APP_DEFAULTS = {
|
|
36978
37182
|
port: 5111,
|
|
@@ -37020,7 +37224,6 @@ var init_config2 = __esm({
|
|
|
37020
37224
|
tmuxBufferPrefix: `${APP_SLUG}-prompt`,
|
|
37021
37225
|
dockerContainerPrefix: APP_SLUG,
|
|
37022
37226
|
tempUploadDir: `${APP_SLUG}-uploads`,
|
|
37023
|
-
dockerImage: "portta-sandbox",
|
|
37024
37227
|
gitBaseConfigKey: `${APP_SLUG}.base`
|
|
37025
37228
|
};
|
|
37026
37229
|
SERVICE_IDENTITY = {
|
|
@@ -44388,48 +44591,48 @@ var init_main2 = __esm({
|
|
|
44388
44591
|
|
|
44389
44592
|
// ../core/src/taskflow/paths.ts
|
|
44390
44593
|
import { homedir as homedir2 } from "node:os";
|
|
44391
|
-
import { join as
|
|
44594
|
+
import { join as join10 } from "node:path";
|
|
44392
44595
|
function globalConfigDir(options = {}) {
|
|
44393
44596
|
const env = options.env ?? process.env;
|
|
44394
44597
|
const override = env[ENV_NAMES.home]?.trim();
|
|
44395
|
-
return override ||
|
|
44598
|
+
return override || join10(options.home ?? env.HOME ?? homedir2(), GLOBAL_CONFIG_DIR);
|
|
44396
44599
|
}
|
|
44397
44600
|
function globalPaths(options = {}) {
|
|
44398
44601
|
const home2 = globalConfigDir(options);
|
|
44399
44602
|
const env = options.env ?? process.env;
|
|
44400
|
-
const root2 = env[ENV_NAMES.hostStateDir]?.trim() ||
|
|
44603
|
+
const root2 = env[ENV_NAMES.hostStateDir]?.trim() || join10(home2, HOST_STATE_DIR);
|
|
44401
44604
|
return {
|
|
44402
44605
|
root: root2,
|
|
44403
|
-
env:
|
|
44404
|
-
controlToken:
|
|
44405
|
-
projectsRegistry:
|
|
44406
|
-
database:
|
|
44407
|
-
runs:
|
|
44408
|
-
workflows:
|
|
44606
|
+
env: join10(home2, PATH_NAMES.env),
|
|
44607
|
+
controlToken: join10(root2, PATH_NAMES.controlToken),
|
|
44608
|
+
projectsRegistry: join10(root2, PATH_NAMES.projectsRegistry),
|
|
44609
|
+
database: join10(root2, PATH_NAMES.database),
|
|
44610
|
+
runs: join10(root2, PATH_NAMES.runs),
|
|
44611
|
+
workflows: join10(root2, PATH_NAMES.workflows)
|
|
44409
44612
|
};
|
|
44410
44613
|
}
|
|
44411
44614
|
function projectPaths(projectRoot2) {
|
|
44412
|
-
const root2 =
|
|
44615
|
+
const root2 = join10(projectRoot2, PROJECT_CONFIG_DIR);
|
|
44413
44616
|
return {
|
|
44414
44617
|
root: root2,
|
|
44415
|
-
config:
|
|
44416
|
-
localConfig:
|
|
44417
|
-
configExample:
|
|
44418
|
-
workflows:
|
|
44419
|
-
worktrees:
|
|
44618
|
+
config: join10(root2, PATH_NAMES.projectConfig),
|
|
44619
|
+
localConfig: join10(root2, PATH_NAMES.projectLocalConfig),
|
|
44620
|
+
configExample: join10(root2, PATH_NAMES.projectConfigExample),
|
|
44621
|
+
workflows: join10(root2, PATH_NAMES.workflows),
|
|
44622
|
+
worktrees: join10(root2, PATH_NAMES.worktrees)
|
|
44420
44623
|
};
|
|
44421
44624
|
}
|
|
44422
44625
|
function gitRuntimePaths(gitDir) {
|
|
44423
|
-
const root2 =
|
|
44626
|
+
const root2 = join10(gitDir, PATH_NAMES.gitRuntime);
|
|
44424
44627
|
return {
|
|
44425
44628
|
root: root2,
|
|
44426
|
-
meta:
|
|
44427
|
-
runtimeEnv:
|
|
44428
|
-
controlEnv:
|
|
44429
|
-
pullRequests:
|
|
44430
|
-
archiveState:
|
|
44431
|
-
openSessionsState:
|
|
44432
|
-
agentControl:
|
|
44629
|
+
meta: join10(root2, PATH_NAMES.worktreeMeta),
|
|
44630
|
+
runtimeEnv: join10(root2, PATH_NAMES.runtimeEnv),
|
|
44631
|
+
controlEnv: join10(root2, PATH_NAMES.controlEnv),
|
|
44632
|
+
pullRequests: join10(root2, PATH_NAMES.pullRequests),
|
|
44633
|
+
archiveState: join10(root2, PATH_NAMES.archiveState),
|
|
44634
|
+
openSessionsState: join10(root2, PATH_NAMES.openSessionsState),
|
|
44635
|
+
agentControl: join10(root2, RUNTIME_IDENTITY.agentControlBinary)
|
|
44433
44636
|
};
|
|
44434
44637
|
}
|
|
44435
44638
|
var init_paths2 = __esm({
|
|
@@ -44441,29 +44644,29 @@ var init_paths2 = __esm({
|
|
|
44441
44644
|
|
|
44442
44645
|
// ../host/src/token.ts
|
|
44443
44646
|
import { randomBytes as randomBytes6 } from "node:crypto";
|
|
44444
|
-
import { chmodSync as
|
|
44445
|
-
import { dirname as dirname6, join as
|
|
44647
|
+
import { chmodSync as chmodSync6, mkdirSync as mkdirSync6, readFileSync as readFileSync9, statSync as statSync4, writeFileSync as writeFileSync6 } from "node:fs";
|
|
44648
|
+
import { dirname as dirname6, join as join11 } from "node:path";
|
|
44446
44649
|
function hostStateDir(root2) {
|
|
44447
|
-
return
|
|
44650
|
+
return join11(root2, HOST_STATE_DIR2);
|
|
44448
44651
|
}
|
|
44449
44652
|
function resolveHostStateDir(root2, env) {
|
|
44450
44653
|
return env["PORTTA_HOST_STATE_DIR"]?.trim() || hostStateDir(root2);
|
|
44451
44654
|
}
|
|
44452
44655
|
function hostTokenFile(stateDir) {
|
|
44453
|
-
return
|
|
44656
|
+
return join11(stateDir, "token");
|
|
44454
44657
|
}
|
|
44455
44658
|
function newToken() {
|
|
44456
44659
|
return randomBytes6(32).toString("base64url");
|
|
44457
44660
|
}
|
|
44458
44661
|
function readOrCreateToken(file2) {
|
|
44459
|
-
|
|
44662
|
+
mkdirSync6(dirname6(file2), { recursive: true, mode: 448 });
|
|
44460
44663
|
try {
|
|
44461
44664
|
writeFileSync6(file2, `${newToken()}
|
|
44462
44665
|
`, { flag: "wx", mode: 384 });
|
|
44463
44666
|
} catch (error61) {
|
|
44464
44667
|
if (error61.code !== "EEXIST") throw error61;
|
|
44465
44668
|
}
|
|
44466
|
-
if ((statSync4(file2).mode & 63) !== 0)
|
|
44669
|
+
if ((statSync4(file2).mode & 63) !== 0) chmodSync6(file2, 384);
|
|
44467
44670
|
const token = readFileSync9(file2, "utf8").trim();
|
|
44468
44671
|
if (token === "") throw new Error(`${file2} is empty; remove it and start the host daemon again`);
|
|
44469
44672
|
return token;
|
|
@@ -44472,7 +44675,7 @@ var HOST_STATE_DIR2;
|
|
|
44472
44675
|
var init_token = __esm({
|
|
44473
44676
|
"../host/src/token.ts"() {
|
|
44474
44677
|
"use strict";
|
|
44475
|
-
HOST_STATE_DIR2 =
|
|
44678
|
+
HOST_STATE_DIR2 = join11("state", "host");
|
|
44476
44679
|
}
|
|
44477
44680
|
});
|
|
44478
44681
|
|
|
@@ -47734,7 +47937,7 @@ var require_Schema = __commonJS({
|
|
|
47734
47937
|
var string4 = require_string();
|
|
47735
47938
|
var tags = require_tags();
|
|
47736
47939
|
var sortMapEntriesByKey = (a3, b) => a3.key < b.key ? -1 : a3.key > b.key ? 1 : 0;
|
|
47737
|
-
var
|
|
47940
|
+
var Schema2 = class _Schema {
|
|
47738
47941
|
constructor({ compat, customTags, merge: merge2, resolveKnownTags, schema, sortMapEntries, toStringDefaults }) {
|
|
47739
47942
|
this.compat = Array.isArray(compat) ? tags.getTags(compat, "compat") : compat ? tags.getTags(null, compat) : null;
|
|
47740
47943
|
this.name = typeof schema === "string" && schema || "core";
|
|
@@ -47752,7 +47955,7 @@ var require_Schema = __commonJS({
|
|
|
47752
47955
|
return copy;
|
|
47753
47956
|
}
|
|
47754
47957
|
};
|
|
47755
|
-
exports.Schema =
|
|
47958
|
+
exports.Schema = Schema2;
|
|
47756
47959
|
}
|
|
47757
47960
|
});
|
|
47758
47961
|
|
|
@@ -47845,7 +48048,7 @@ var require_Document = __commonJS({
|
|
|
47845
48048
|
var identity3 = require_identity();
|
|
47846
48049
|
var Pair = require_Pair();
|
|
47847
48050
|
var toJS = require_toJS();
|
|
47848
|
-
var
|
|
48051
|
+
var Schema2 = require_Schema();
|
|
47849
48052
|
var stringifyDocument = require_stringifyDocument();
|
|
47850
48053
|
var anchors = require_anchors();
|
|
47851
48054
|
var applyReviver = require_applyReviver();
|
|
@@ -48096,7 +48299,7 @@ var require_Document = __commonJS({
|
|
|
48096
48299
|
if (options.schema instanceof Object)
|
|
48097
48300
|
this.schema = options.schema;
|
|
48098
48301
|
else if (opt)
|
|
48099
|
-
this.schema = new
|
|
48302
|
+
this.schema = new Schema2.Schema(Object.assign(opt, options));
|
|
48100
48303
|
else
|
|
48101
48304
|
throw new Error(`With a null YAML version, the { schema: Schema } option is required`);
|
|
48102
48305
|
}
|
|
@@ -51760,7 +51963,7 @@ var require_dist = __commonJS({
|
|
|
51760
51963
|
"use strict";
|
|
51761
51964
|
var composer = require_composer();
|
|
51762
51965
|
var Document = require_Document();
|
|
51763
|
-
var
|
|
51966
|
+
var Schema2 = require_Schema();
|
|
51764
51967
|
var errors = require_errors();
|
|
51765
51968
|
var Alias = require_Alias();
|
|
51766
51969
|
var identity3 = require_identity();
|
|
@@ -51776,7 +51979,7 @@ var require_dist = __commonJS({
|
|
|
51776
51979
|
var visit2 = require_visit();
|
|
51777
51980
|
exports.Composer = composer.Composer;
|
|
51778
51981
|
exports.Document = Document.Document;
|
|
51779
|
-
exports.Schema =
|
|
51982
|
+
exports.Schema = Schema2.Schema;
|
|
51780
51983
|
exports.YAMLError = errors.YAMLError;
|
|
51781
51984
|
exports.YAMLParseError = errors.YAMLParseError;
|
|
51782
51985
|
exports.YAMLWarning = errors.YAMLWarning;
|
|
@@ -51816,7 +52019,6 @@ function parseWorktreeRoot(raw2, fallback) {
|
|
|
51816
52019
|
if (isRecord2(raw2.worktrees) && typeof raw2.worktrees.root === "string" && raw2.worktrees.root.trim()) {
|
|
51817
52020
|
return raw2.worktrees.root.trim();
|
|
51818
52021
|
}
|
|
51819
|
-
if (typeof raw2.worktreeRoot === "string" && raw2.worktreeRoot.trim()) return raw2.worktreeRoot.trim();
|
|
51820
52022
|
return fallback;
|
|
51821
52023
|
}
|
|
51822
52024
|
function parseExposure(raw2) {
|
|
@@ -51928,18 +52130,15 @@ function parseProfile(raw2, fallbackRuntime) {
|
|
|
51928
52130
|
...typeof raw2.environment.cwd === "string" && raw2.environment.cwd.trim() ? { cwd: raw2.environment.cwd.trim() } : {},
|
|
51929
52131
|
...typeof raw2.environment.stopCommand === "string" && raw2.environment.stopCommand.trim() ? { stopCommand: raw2.environment.stopCommand.trim() } : {}
|
|
51930
52132
|
} : void 0;
|
|
51931
|
-
const legacyRuntimePane = environment && (environment.provider === "host" || environment.provider === "auto") && !environment.command ? panes.find((pane) => pane.id === "app" && pane.kind === "command" && Boolean(pane.command)) : void 0;
|
|
51932
|
-
const effectiveEnvironment = legacyRuntimePane ? { ...environment, command: legacyRuntimePane.command } : environment;
|
|
51933
|
-
const effectivePanes = legacyRuntimePane ? panes.filter((pane) => pane !== legacyRuntimePane) : panes;
|
|
51934
52133
|
return {
|
|
51935
52134
|
runtime: runtime2,
|
|
51936
52135
|
envPassthrough,
|
|
51937
52136
|
...raw2.yolo === true ? { yolo: true } : {},
|
|
51938
|
-
panes
|
|
52137
|
+
panes,
|
|
51939
52138
|
...typeof raw2.systemPrompt === "string" && raw2.systemPrompt.length > 0 ? { systemPrompt: raw2.systemPrompt } : {},
|
|
51940
52139
|
...image ? { image } : {},
|
|
51941
52140
|
...mounts ? { mounts } : {},
|
|
51942
|
-
...
|
|
52141
|
+
...environment && Object.keys(environment).length > 0 ? { environment } : {}
|
|
51943
52142
|
};
|
|
51944
52143
|
}
|
|
51945
52144
|
function parseProfiles(raw2, includeDefaultProfile) {
|
|
@@ -52061,7 +52260,7 @@ function parseConfigDocument(text) {
|
|
|
52061
52260
|
const parsed = (0, import_yaml.parse)(text);
|
|
52062
52261
|
return isRecord2(parsed) ? parsed : {};
|
|
52063
52262
|
}
|
|
52064
|
-
function
|
|
52263
|
+
function parseProjectConfig2(parsed) {
|
|
52065
52264
|
const worktreeRoot = parseWorktreeRoot(parsed.workspace, DEFAULT_CONFIG.workspace.worktreeRoot);
|
|
52066
52265
|
return {
|
|
52067
52266
|
name: typeof parsed.name === "string" && parsed.name.trim() ? parsed.name.trim() : DEFAULT_CONFIG.name,
|
|
@@ -52091,7 +52290,7 @@ function parseProjectConfig(parsed) {
|
|
|
52091
52290
|
};
|
|
52092
52291
|
}
|
|
52093
52292
|
function defaultConfig() {
|
|
52094
|
-
return
|
|
52293
|
+
return parseProjectConfig2({});
|
|
52095
52294
|
}
|
|
52096
52295
|
function parseTeamKeyList(raw2) {
|
|
52097
52296
|
if (!Array.isArray(raw2)) return void 0;
|
|
@@ -52102,12 +52301,6 @@ function parseLinearIntegration(parsed) {
|
|
|
52102
52301
|
const defaults = DEFAULT_CONFIG.integrations.linear;
|
|
52103
52302
|
const linear = isRecord2(parsed.integrations) && isRecord2(parsed.integrations.linear) ? parsed.integrations.linear : null;
|
|
52104
52303
|
if (!linear) return { ...defaults };
|
|
52105
|
-
if (typeof linear.teamId === "string" && !warnedLegacyLinearTeamId) {
|
|
52106
|
-
warnedLegacyLinearTeamId = true;
|
|
52107
|
-
log.warn(
|
|
52108
|
-
"[config] integrations.linear.teamId is no longer used \u2014 the ticket team is now picked at creation time in the dashboard"
|
|
52109
|
-
);
|
|
52110
|
-
}
|
|
52111
52304
|
const watchTeams = parseTeamKeyList(linear.watchTeams);
|
|
52112
52305
|
return {
|
|
52113
52306
|
enabled: typeof linear.enabled === "boolean" ? linear.enabled : defaults.enabled,
|
|
@@ -52226,7 +52419,7 @@ function loadConfig(dir, options = {}) {
|
|
|
52226
52419
|
let projectConfig;
|
|
52227
52420
|
try {
|
|
52228
52421
|
const text = readConfigFile(root2).trim();
|
|
52229
|
-
projectConfig = text ?
|
|
52422
|
+
projectConfig = text ? parseProjectConfig2(parseConfigDocument(text)) : defaultConfig();
|
|
52230
52423
|
} catch {
|
|
52231
52424
|
projectConfig = defaultConfig();
|
|
52232
52425
|
}
|
|
@@ -52328,14 +52521,13 @@ async function removeLocalCustomAgent(dir, agentId2) {
|
|
|
52328
52521
|
function expandTemplate(template, env) {
|
|
52329
52522
|
return template.replace(/\$\{(\w+)\}/g, (_, key) => env[key] ?? "");
|
|
52330
52523
|
}
|
|
52331
|
-
var import_yaml, DEFAULT_PANES, DEFAULT_PROFILE, DEFAULT_CONFIG
|
|
52524
|
+
var import_yaml, DEFAULT_PANES, DEFAULT_PROFILE, DEFAULT_CONFIG;
|
|
52332
52525
|
var init_config5 = __esm({
|
|
52333
52526
|
"../host/src/modules/taskflow/adapters/config.ts"() {
|
|
52334
52527
|
"use strict";
|
|
52335
52528
|
init_config2();
|
|
52336
52529
|
init_paths2();
|
|
52337
52530
|
import_yaml = __toESM(require_dist(), 1);
|
|
52338
|
-
init_log2();
|
|
52339
52531
|
DEFAULT_PANES = [
|
|
52340
52532
|
{ id: "agent", kind: "agent", focus: true },
|
|
52341
52533
|
{ id: "runtime", kind: "runtime", split: "right", sizePct: 30, cwd: "worktree" }
|
|
@@ -52369,7 +52561,6 @@ var init_config5 = __esm({
|
|
|
52369
52561
|
autoName: null,
|
|
52370
52562
|
oneshot: { systemPrompt: DEFAULT_ONESHOT_SYSTEM_PROMPT() }
|
|
52371
52563
|
};
|
|
52372
|
-
warnedLegacyLinearTeamId = false;
|
|
52373
52564
|
}
|
|
52374
52565
|
});
|
|
52375
52566
|
|
|
@@ -52630,7 +52821,7 @@ var init_process_runner = __esm({
|
|
|
52630
52821
|
|
|
52631
52822
|
// ../host/src/modules/taskflow/adapters/worktree-git-mount.ts
|
|
52632
52823
|
import { access, readFile, realpath, rename, unlink, writeFile as writeFile2 } from "node:fs/promises";
|
|
52633
|
-
import { basename as basename3, isAbsolute as isAbsolute2, join as
|
|
52824
|
+
import { basename as basename3, isAbsolute as isAbsolute2, join as join12, posix as posix3, relative, resolve as resolve3, sep } from "node:path";
|
|
52634
52825
|
function repairCommand(workspacePath) {
|
|
52635
52826
|
return `git -C ${workspacePath} worktree repair --relative-paths`;
|
|
52636
52827
|
}
|
|
@@ -52680,7 +52871,7 @@ function linkTarget(content) {
|
|
|
52680
52871
|
return target.length > 0 ? target : null;
|
|
52681
52872
|
}
|
|
52682
52873
|
async function worktreeLink(workspacePath) {
|
|
52683
|
-
const content = await readRaw(
|
|
52874
|
+
const content = await readRaw(join12(workspacePath, ".git"));
|
|
52684
52875
|
return content === null ? null : linkTarget(content);
|
|
52685
52876
|
}
|
|
52686
52877
|
async function porttaOwned(runner, workspacePath, gitDir) {
|
|
@@ -52693,9 +52884,9 @@ async function porttaOwned(runner, workspacePath, gitDir) {
|
|
|
52693
52884
|
return result.exit.code === 0 && result.stdout.trim().length > 0;
|
|
52694
52885
|
}
|
|
52695
52886
|
async function planRelativeRelink(workspacePath, dirs) {
|
|
52696
|
-
const forwardPath =
|
|
52697
|
-
const backPath =
|
|
52698
|
-
if (
|
|
52887
|
+
const forwardPath = join12(workspacePath, ".git");
|
|
52888
|
+
const backPath = join12(dirs.gitDir, BACK_LINK);
|
|
52889
|
+
if (join12(dirs.commonDir, "worktrees", basename3(dirs.gitDir)) !== dirs.gitDir)
|
|
52699
52890
|
return {
|
|
52700
52891
|
ok: false,
|
|
52701
52892
|
reason: `its Git dir ${dirs.gitDir} is not <common dir>/worktrees/<id> under ${dirs.commonDir}`
|
|
@@ -52756,7 +52947,7 @@ async function worktreeGitMountArgs(input2) {
|
|
|
52756
52947
|
);
|
|
52757
52948
|
}
|
|
52758
52949
|
let link = await worktreeLink(workspacePath);
|
|
52759
|
-
const backLink = await readRaw(
|
|
52950
|
+
const backLink = await readRaw(join12(dirs.gitDir, BACK_LINK));
|
|
52760
52951
|
if (link !== null && isAbsolute2(link) || backLink !== null && isAbsolute2(backLink.trim())) {
|
|
52761
52952
|
if (!await porttaOwned(runner, workspacePath, dirs.gitDir)) {
|
|
52762
52953
|
throw new Error(
|
|
@@ -52812,7 +53003,7 @@ var init_worktree_git_mount = __esm({
|
|
|
52812
53003
|
import { createHash as createHash5 } from "node:crypto";
|
|
52813
53004
|
import { access as access2, readdir, readFile as readFile2 } from "node:fs/promises";
|
|
52814
53005
|
import { createRequire } from "node:module";
|
|
52815
|
-
import { dirname as dirname8, join as
|
|
53006
|
+
import { dirname as dirname8, join as join13, relative as relative2, resolve as resolve4 } from "node:path";
|
|
52816
53007
|
function hash2(value3) {
|
|
52817
53008
|
return createHash5("sha256").update(value3).digest("hex");
|
|
52818
53009
|
}
|
|
@@ -52828,16 +53019,16 @@ async function exists2(path7) {
|
|
|
52828
53019
|
}
|
|
52829
53020
|
}
|
|
52830
53021
|
async function findDevcontainerConfigs(workspacePath) {
|
|
52831
|
-
const rootConfig =
|
|
52832
|
-
const directory =
|
|
53022
|
+
const rootConfig = join13(workspacePath, ".devcontainer.json");
|
|
53023
|
+
const directory = join13(workspacePath, ".devcontainer");
|
|
52833
53024
|
const configs = [];
|
|
52834
53025
|
if (await exists2(rootConfig)) configs.push(rootConfig);
|
|
52835
|
-
if (await exists2(
|
|
53026
|
+
if (await exists2(join13(directory, "devcontainer.json"))) configs.push(join13(directory, "devcontainer.json"));
|
|
52836
53027
|
if (await exists2(directory)) {
|
|
52837
53028
|
const entries = await readdir(directory, { withFileTypes: true });
|
|
52838
53029
|
for (const entry of entries) {
|
|
52839
53030
|
if (!entry.isDirectory()) continue;
|
|
52840
|
-
const candidate =
|
|
53031
|
+
const candidate = join13(directory, entry.name, "devcontainer.json");
|
|
52841
53032
|
if (await exists2(candidate)) configs.push(candidate);
|
|
52842
53033
|
}
|
|
52843
53034
|
}
|
|
@@ -52991,7 +53182,7 @@ var init_devcontainer_environment = __esm({
|
|
|
52991
53182
|
}
|
|
52992
53183
|
const packagePath = createRequire(import.meta.url).resolve("@devcontainers/cli/package.json");
|
|
52993
53184
|
this.cli = process.execPath;
|
|
52994
|
-
this.cliPrefix = [
|
|
53185
|
+
this.cliPrefix = [join13(dirname8(packagePath), "devcontainer.js")];
|
|
52995
53186
|
}
|
|
52996
53187
|
args(args) {
|
|
52997
53188
|
return [...this.cliPrefix, ...args];
|
|
@@ -53058,7 +53249,7 @@ var init_devcontainer_environment = __esm({
|
|
|
53058
53249
|
this.workspaceMounts.set(config3, merged.workspaceMount);
|
|
53059
53250
|
else this.workspaceMounts.delete(config3);
|
|
53060
53251
|
const compose4 = await resolveComposeConfiguration(this.runner, config3, merged);
|
|
53061
|
-
const lockfile =
|
|
53252
|
+
const lockfile = join13(dirname8(config3), "devcontainer-lock.json");
|
|
53062
53253
|
const lockfileExists = await exists2(lockfile);
|
|
53063
53254
|
const lockfileContents = lockfileExists ? await readFile2(lockfile, "utf8") : "";
|
|
53064
53255
|
return {
|
|
@@ -53325,11 +53516,11 @@ exec ${argv.join(" ")} "$cwd" ${quoteShell(executable2)} "$@"
|
|
|
53325
53516
|
|
|
53326
53517
|
// ../host/src/modules/taskflow/adapters/dockerfile-environment.ts
|
|
53327
53518
|
import { createHash as createHash6 } from "node:crypto";
|
|
53328
|
-
import { existsSync as
|
|
53519
|
+
import { existsSync as existsSync8, readFileSync as readFileSync11 } from "node:fs";
|
|
53329
53520
|
import { resolve as resolve5 } from "node:path";
|
|
53330
53521
|
function dockerfileFor(context) {
|
|
53331
53522
|
const dockerfile = resolve5(context.workspacePath, context.configRef ?? "Dockerfile");
|
|
53332
|
-
if (!
|
|
53523
|
+
if (!existsSync8(dockerfile)) throw new Error(`Dockerfile does not exist: ${context.configRef ?? "Dockerfile"}`);
|
|
53333
53524
|
return dockerfile;
|
|
53334
53525
|
}
|
|
53335
53526
|
function imageTag(id) {
|
|
@@ -53364,10 +53555,10 @@ var init_dockerfile_environment = __esm({
|
|
|
53364
53555
|
const dockerfile = resolve5(context.workspacePath, "Dockerfile");
|
|
53365
53556
|
return {
|
|
53366
53557
|
provider: this.id,
|
|
53367
|
-
available: version3.exit.code === 0 &&
|
|
53368
|
-
configRefs:
|
|
53558
|
+
available: version3.exit.code === 0 && existsSync8(dockerfile),
|
|
53559
|
+
configRefs: existsSync8(dockerfile) ? [dockerfile] : [],
|
|
53369
53560
|
...version3.exit.code === 0 ? { version: version3.stdout.trim() } : {},
|
|
53370
|
-
diagnostics: version3.exit.code === 0 ?
|
|
53561
|
+
diagnostics: version3.exit.code === 0 ? existsSync8(dockerfile) ? [] : ["No Dockerfile was found"] : [version3.stderr.trim() || "Docker is unavailable"]
|
|
53371
53562
|
};
|
|
53372
53563
|
}
|
|
53373
53564
|
async resolve(context) {
|
|
@@ -53591,13 +53782,13 @@ var init_sqlite = __esm({
|
|
|
53591
53782
|
});
|
|
53592
53783
|
|
|
53593
53784
|
// ../host/src/modules/taskflow/adapters/environment-store.ts
|
|
53594
|
-
import { mkdirSync as
|
|
53785
|
+
import { mkdirSync as mkdirSync7 } from "node:fs";
|
|
53595
53786
|
import { dirname as dirname9 } from "node:path";
|
|
53596
53787
|
function parseEnvironmentRecord(value3) {
|
|
53597
53788
|
return environmentRecordSchema.parse(value3);
|
|
53598
53789
|
}
|
|
53599
53790
|
function createEnvironmentStore(path7) {
|
|
53600
|
-
|
|
53791
|
+
mkdirSync7(dirname9(path7), { recursive: true });
|
|
53601
53792
|
const database = openDatabase(path7);
|
|
53602
53793
|
database.pragma("journal_mode = WAL");
|
|
53603
53794
|
database.exec(`
|
|
@@ -53836,7 +54027,7 @@ exec ${JSON.stringify(executable2)} "$@"
|
|
|
53836
54027
|
});
|
|
53837
54028
|
|
|
53838
54029
|
// ../host/src/modules/taskflow/adapters/projects-registry.ts
|
|
53839
|
-
import { mkdirSync as
|
|
54030
|
+
import { mkdirSync as mkdirSync8, readFileSync as readFileSync12, renameSync as renameSync3, writeFileSync as writeFileSync7 } from "node:fs";
|
|
53840
54031
|
import { dirname as dirname10 } from "node:path";
|
|
53841
54032
|
function defaultRegistryFile() {
|
|
53842
54033
|
return globalPaths().projectsRegistry;
|
|
@@ -53860,7 +54051,7 @@ function createProjectsRegistry(file2 = defaultRegistryFile()) {
|
|
|
53860
54051
|
return parsed.filter(isProjectEntry);
|
|
53861
54052
|
}
|
|
53862
54053
|
function write4(entries) {
|
|
53863
|
-
|
|
54054
|
+
mkdirSync8(dirname10(file2), { recursive: true });
|
|
53864
54055
|
const tmpPath = `${file2}.${process.pid}.${Date.now()}.tmp`;
|
|
53865
54056
|
writeFileSync7(tmpPath, `${JSON.stringify(entries, null, 2)}
|
|
53866
54057
|
`);
|
|
@@ -54574,20 +54765,20 @@ var init_tmux = __esm({
|
|
|
54574
54765
|
});
|
|
54575
54766
|
|
|
54576
54767
|
// ../host/src/modules/taskflow/assets.ts
|
|
54577
|
-
import { existsSync as
|
|
54578
|
-
import { join as
|
|
54768
|
+
import { existsSync as existsSync9 } from "node:fs";
|
|
54769
|
+
import { join as join14, resolve as resolve7 } from "node:path";
|
|
54579
54770
|
function taskflowAssets(env = process.env, directory = import.meta.dirname) {
|
|
54580
|
-
const bundled =
|
|
54581
|
-
if (
|
|
54771
|
+
const bundled = join14(directory, "assets");
|
|
54772
|
+
if (existsSync9(join14(bundled, "workflows"))) {
|
|
54582
54773
|
return {
|
|
54583
|
-
workflowBuiltinsDir: env[ENV_NAMES.workflowBuiltinsDir] ||
|
|
54584
|
-
workflowSkill:
|
|
54774
|
+
workflowBuiltinsDir: env[ENV_NAMES.workflowBuiltinsDir] || join14(bundled, "workflows"),
|
|
54775
|
+
workflowSkill: join14(bundled, "skills", "portta-workflows", "SKILL.md")
|
|
54585
54776
|
};
|
|
54586
54777
|
}
|
|
54587
54778
|
const repository = resolve7(directory, "../../../../..");
|
|
54588
54779
|
return {
|
|
54589
|
-
workflowBuiltinsDir: env[ENV_NAMES.workflowBuiltinsDir] ||
|
|
54590
|
-
workflowSkill:
|
|
54780
|
+
workflowBuiltinsDir: env[ENV_NAMES.workflowBuiltinsDir] || join14(repository, "packages", "host", "workflows"),
|
|
54781
|
+
workflowSkill: join14(repository, "skills", "portta-workflows", "SKILL.md")
|
|
54591
54782
|
};
|
|
54592
54783
|
}
|
|
54593
54784
|
var init_assets = __esm({
|
|
@@ -54806,7 +54997,7 @@ var init_environment_service_discovery = __esm({
|
|
|
54806
54997
|
// ../host/src/modules/taskflow/adapters/git.ts
|
|
54807
54998
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
54808
54999
|
import { readdirSync as readdirSync2, realpathSync as realpathSync2, rmSync, statSync as statSync5 } from "node:fs";
|
|
54809
|
-
import { basename as basename5, dirname as dirname11, join as
|
|
55000
|
+
import { basename as basename5, dirname as dirname11, join as join15, resolve as resolve8 } from "node:path";
|
|
54810
55001
|
function canonicalizeFsPath(input2) {
|
|
54811
55002
|
const resolved2 = resolve8(input2);
|
|
54812
55003
|
try {
|
|
@@ -54818,7 +55009,7 @@ function canonicalizeFsPath(input2) {
|
|
|
54818
55009
|
missing.unshift(basename5(current));
|
|
54819
55010
|
current = dirname11(current);
|
|
54820
55011
|
try {
|
|
54821
|
-
return
|
|
55012
|
+
return join15(realpathSync2(current), ...missing);
|
|
54822
55013
|
} catch {
|
|
54823
55014
|
}
|
|
54824
55015
|
}
|
|
@@ -54910,7 +55101,7 @@ function resolveRepoRoot(dir) {
|
|
|
54910
55101
|
return null;
|
|
54911
55102
|
}
|
|
54912
55103
|
for (const entry of entries) {
|
|
54913
|
-
const child =
|
|
55104
|
+
const child = join15(dir, entry);
|
|
54914
55105
|
try {
|
|
54915
55106
|
if (!statSync5(child).isDirectory()) continue;
|
|
54916
55107
|
} catch {
|
|
@@ -55844,7 +56035,7 @@ var init_herdr = __esm({
|
|
|
55844
56035
|
|
|
55845
56036
|
// ../host/src/modules/taskflow/adapters/hooks.ts
|
|
55846
56037
|
import { access as access3 } from "node:fs/promises";
|
|
55847
|
-
import { join as
|
|
56038
|
+
import { join as join16 } from "node:path";
|
|
55848
56039
|
function buildErrorMessage(name, exitCode, stdout2, stderr2) {
|
|
55849
56040
|
const output2 = stderr2.trim() || stdout2.trim();
|
|
55850
56041
|
if (output2) {
|
|
@@ -55895,7 +56086,7 @@ var init_hooks = __esm({
|
|
|
55895
56086
|
}
|
|
55896
56087
|
async hasEnvrc(cwd) {
|
|
55897
56088
|
try {
|
|
55898
|
-
await access3(
|
|
56089
|
+
await access3(join16(cwd, ".envrc"));
|
|
55899
56090
|
return true;
|
|
55900
56091
|
} catch {
|
|
55901
56092
|
return false;
|
|
@@ -55994,7 +56185,7 @@ var init_type_guards = __esm({
|
|
|
55994
56185
|
|
|
55995
56186
|
// ../host/src/modules/taskflow/adapters/claude-cli.ts
|
|
55996
56187
|
import { readdir as readdir2, readFile as readFile3, stat as stat2 } from "node:fs/promises";
|
|
55997
|
-
import { basename as basename6, join as
|
|
56188
|
+
import { basename as basename6, join as join17 } from "node:path";
|
|
55998
56189
|
function isRecord5(raw2) {
|
|
55999
56190
|
return typeof raw2 === "object" && raw2 !== null && !Array.isArray(raw2);
|
|
56000
56191
|
}
|
|
@@ -56177,19 +56368,19 @@ function readClaudeProjectsRoot() {
|
|
|
56177
56368
|
if (!home2) {
|
|
56178
56369
|
throw new Error("HOME is required to resolve Claude sessions");
|
|
56179
56370
|
}
|
|
56180
|
-
return
|
|
56371
|
+
return join17(home2, ".claude", "projects");
|
|
56181
56372
|
}
|
|
56182
56373
|
async function listJsonlFiles(dir) {
|
|
56183
56374
|
try {
|
|
56184
56375
|
const entries = await readdir2(dir, { withFileTypes: true });
|
|
56185
|
-
return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl")).map((entry) =>
|
|
56376
|
+
return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl")).map((entry) => join17(dir, entry.name));
|
|
56186
56377
|
} catch {
|
|
56187
56378
|
return [];
|
|
56188
56379
|
}
|
|
56189
56380
|
}
|
|
56190
56381
|
async function findClaudeSessionPath(sessionId, cwd) {
|
|
56191
56382
|
const projectsRoot = readClaudeProjectsRoot();
|
|
56192
|
-
const primaryPath =
|
|
56383
|
+
const primaryPath = join17(projectsRoot, encodeClaudeProjectDir(cwd), `${sessionId}.jsonl`);
|
|
56193
56384
|
try {
|
|
56194
56385
|
await stat2(primaryPath);
|
|
56195
56386
|
return primaryPath;
|
|
@@ -56198,7 +56389,7 @@ async function findClaudeSessionPath(sessionId, cwd) {
|
|
|
56198
56389
|
const projectDirs2 = await readdir2(projectsRoot, { withFileTypes: true }).catch(() => []);
|
|
56199
56390
|
for (const entry of projectDirs2) {
|
|
56200
56391
|
if (!entry.isDirectory()) continue;
|
|
56201
|
-
const candidate =
|
|
56392
|
+
const candidate = join17(projectsRoot, entry.name, `${sessionId}.jsonl`);
|
|
56202
56393
|
try {
|
|
56203
56394
|
await stat2(candidate);
|
|
56204
56395
|
return candidate;
|
|
@@ -56341,7 +56532,7 @@ var init_claude_cli = __esm({
|
|
|
56341
56532
|
}
|
|
56342
56533
|
async listSessions(cwd) {
|
|
56343
56534
|
const projectsRoot = readClaudeProjectsRoot();
|
|
56344
|
-
const primaryDir =
|
|
56535
|
+
const primaryDir = join17(projectsRoot, encodeClaudeProjectDir(cwd));
|
|
56345
56536
|
const primaryFiles = await listJsonlFiles(primaryDir);
|
|
56346
56537
|
if (primaryFiles.length > 0) {
|
|
56347
56538
|
return await this.summarizeSessionFiles(primaryFiles, cwd);
|
|
@@ -56350,7 +56541,7 @@ var init_claude_cli = __esm({
|
|
|
56350
56541
|
const matchedFiles = [];
|
|
56351
56542
|
for (const entry of projectDirs2) {
|
|
56352
56543
|
if (!entry.isDirectory()) continue;
|
|
56353
|
-
const files = await listJsonlFiles(
|
|
56544
|
+
const files = await listJsonlFiles(join17(projectsRoot, entry.name));
|
|
56354
56545
|
for (const filePath of files) {
|
|
56355
56546
|
const session = await this.readSessionFile(filePath);
|
|
56356
56547
|
if (session?.cwd === cwd) {
|
|
@@ -56529,7 +56720,7 @@ var init_claude_cli = __esm({
|
|
|
56529
56720
|
|
|
56530
56721
|
// ../host/src/modules/taskflow/adapters/session-discovery.ts
|
|
56531
56722
|
import { open as open2, readdir as readdir3, stat as stat3 } from "node:fs/promises";
|
|
56532
|
-
import { basename as basename7, join as
|
|
56723
|
+
import { basename as basename7, join as join18 } from "node:path";
|
|
56533
56724
|
import { setTimeout as sleep3 } from "node:timers/promises";
|
|
56534
56725
|
function home() {
|
|
56535
56726
|
const value3 = process.env.HOME;
|
|
@@ -56540,11 +56731,11 @@ function newestFirst(sessions3) {
|
|
|
56540
56731
|
return sessions3.sort((left, right) => right.mtimeMs - left.mtimeMs).map((entry) => entry.sessionId);
|
|
56541
56732
|
}
|
|
56542
56733
|
async function listClaudeSessionIds(cwd) {
|
|
56543
|
-
const dir =
|
|
56734
|
+
const dir = join18(home(), ".claude", "projects", encodeClaudeProjectDir(cwd));
|
|
56544
56735
|
const names = await readdir3(dir).catch(() => []);
|
|
56545
56736
|
const stamped = await Promise.all(
|
|
56546
56737
|
names.filter((name) => name.endsWith(".jsonl")).map(async (name) => {
|
|
56547
|
-
const info = await stat3(
|
|
56738
|
+
const info = await stat3(join18(dir, name)).catch(() => null);
|
|
56548
56739
|
return info ? { sessionId: basename7(name, ".jsonl"), mtimeMs: info.mtimeMs } : null;
|
|
56549
56740
|
})
|
|
56550
56741
|
);
|
|
@@ -56580,7 +56771,7 @@ async function readCodexSessionCwdId(path7) {
|
|
|
56580
56771
|
}
|
|
56581
56772
|
}
|
|
56582
56773
|
async function listCodexSessionIds(cwd) {
|
|
56583
|
-
const root2 =
|
|
56774
|
+
const root2 = join18(home(), ".codex", "sessions");
|
|
56584
56775
|
const relPaths = await readdir3(root2, { recursive: true }).catch(() => []);
|
|
56585
56776
|
const rollouts = relPaths.filter((rel) => {
|
|
56586
56777
|
const name = basename7(rel);
|
|
@@ -56588,7 +56779,7 @@ async function listCodexSessionIds(cwd) {
|
|
|
56588
56779
|
});
|
|
56589
56780
|
const stamped = await Promise.all(
|
|
56590
56781
|
rollouts.map(async (rel) => {
|
|
56591
|
-
const path7 =
|
|
56782
|
+
const path7 = join18(root2, rel);
|
|
56592
56783
|
const meta3 = await readCodexSessionCwdId(path7);
|
|
56593
56784
|
if (!meta3 || meta3.cwd !== cwd) return null;
|
|
56594
56785
|
const info = await stat3(path7).catch(() => null);
|
|
@@ -56598,11 +56789,11 @@ async function listCodexSessionIds(cwd) {
|
|
|
56598
56789
|
return newestFirst(stamped.filter((entry) => entry !== null));
|
|
56599
56790
|
}
|
|
56600
56791
|
async function findCodexSessionPath(sessionId, cwd) {
|
|
56601
|
-
const root2 =
|
|
56792
|
+
const root2 = join18(home(), ".codex", "sessions");
|
|
56602
56793
|
const relPaths = await readdir3(root2, { recursive: true }).catch(() => []);
|
|
56603
56794
|
for (const rel of relPaths) {
|
|
56604
56795
|
if (!basename7(rel).endsWith(`${sessionId}.jsonl`)) continue;
|
|
56605
|
-
const path7 =
|
|
56796
|
+
const path7 = join18(root2, rel);
|
|
56606
56797
|
const meta3 = await readCodexSessionCwdId(path7);
|
|
56607
56798
|
if (meta3?.id === sessionId && meta3.cwd === cwd) return path7;
|
|
56608
56799
|
}
|
|
@@ -56692,7 +56883,7 @@ var init_session_discovery = __esm({
|
|
|
56692
56883
|
|
|
56693
56884
|
// ../host/src/modules/taskflow/adapters/fs.ts
|
|
56694
56885
|
import { mkdir as mkdir2, readFile as readFile4, writeFile as writeFile3 } from "node:fs/promises";
|
|
56695
|
-
import { join as
|
|
56886
|
+
import { join as join19 } from "node:path";
|
|
56696
56887
|
function stringifyAllocatedPorts(ports) {
|
|
56697
56888
|
const entries = Object.entries(ports).map(([key, value3]) => [key, String(value3)]);
|
|
56698
56889
|
return Object.fromEntries(entries);
|
|
@@ -56721,7 +56912,7 @@ function parseDotenv(content) {
|
|
|
56721
56912
|
}
|
|
56722
56913
|
async function loadDotenvLocal(worktreePath) {
|
|
56723
56914
|
try {
|
|
56724
|
-
const content = await readFile4(
|
|
56915
|
+
const content = await readFile4(join19(worktreePath, ".env.local"), "utf8");
|
|
56725
56916
|
return parseDotenv(content);
|
|
56726
56917
|
} catch {
|
|
56727
56918
|
return {};
|
|
@@ -56862,25 +57053,21 @@ function isRecord6(raw2) {
|
|
|
56862
57053
|
function normalizeConversationMeta(raw2) {
|
|
56863
57054
|
if (!raw2) return raw2;
|
|
56864
57055
|
if (raw2.provider === "codexAppServer") {
|
|
56865
|
-
|
|
56866
|
-
const threadId = raw2.threadId || raw2.conversationId;
|
|
56867
|
-
if (!conversationId2 || !threadId) return void 0;
|
|
57056
|
+
if (!raw2.conversationId || !raw2.threadId) return void 0;
|
|
56868
57057
|
const normalized2 = {
|
|
56869
57058
|
provider: "codexAppServer",
|
|
56870
|
-
conversationId:
|
|
56871
|
-
threadId,
|
|
57059
|
+
conversationId: raw2.conversationId,
|
|
57060
|
+
threadId: raw2.threadId,
|
|
56872
57061
|
cwd: raw2.cwd,
|
|
56873
57062
|
lastSeenAt: raw2.lastSeenAt
|
|
56874
57063
|
};
|
|
56875
57064
|
return normalized2;
|
|
56876
57065
|
}
|
|
56877
|
-
|
|
56878
|
-
const sessionId = raw2.sessionId || raw2.conversationId;
|
|
56879
|
-
if (!conversationId || !sessionId) return void 0;
|
|
57066
|
+
if (!raw2.conversationId || !raw2.sessionId) return void 0;
|
|
56880
57067
|
const normalized = {
|
|
56881
57068
|
provider: "claudeCode",
|
|
56882
|
-
conversationId,
|
|
56883
|
-
sessionId,
|
|
57069
|
+
conversationId: raw2.conversationId,
|
|
57070
|
+
sessionId: raw2.sessionId,
|
|
56884
57071
|
cwd: raw2.cwd,
|
|
56885
57072
|
lastSeenAt: raw2.lastSeenAt
|
|
56886
57073
|
};
|
|
@@ -57310,7 +57497,7 @@ ${stderr2}`;
|
|
|
57310
57497
|
|
|
57311
57498
|
// ../host/src/modules/taskflow/adapters/agent-runtime.ts
|
|
57312
57499
|
import { chmod, mkdir as mkdir3, readFile as readFile5, writeFile as writeFile4 } from "node:fs/promises";
|
|
57313
|
-
import { dirname as dirname12, join as
|
|
57500
|
+
import { dirname as dirname12, join as join20, resolve as resolve10 } from "node:path";
|
|
57314
57501
|
function shellQuote(value3) {
|
|
57315
57502
|
return `'${value3.replaceAll("'", "'\\''")}'`;
|
|
57316
57503
|
}
|
|
@@ -57763,7 +57950,7 @@ async function mergeCodexHooksFile(hooksPath, hookSettings, agentCtlPath) {
|
|
|
57763
57950
|
}
|
|
57764
57951
|
async function resolveGitCommonDir(gitDir) {
|
|
57765
57952
|
try {
|
|
57766
|
-
const commonDir = (await readFile5(
|
|
57953
|
+
const commonDir = (await readFile5(join20(gitDir, "commondir"), "utf8")).trim();
|
|
57767
57954
|
if (!commonDir) return gitDir;
|
|
57768
57955
|
return commonDir.startsWith("/") ? commonDir : resolve10(gitDir, commonDir);
|
|
57769
57956
|
} catch {
|
|
@@ -57772,7 +57959,7 @@ async function resolveGitCommonDir(gitDir) {
|
|
|
57772
57959
|
}
|
|
57773
57960
|
async function ensureGeneratedAgentFilesIgnored(gitDir) {
|
|
57774
57961
|
const commonDir = await resolveGitCommonDir(gitDir);
|
|
57775
|
-
const excludePath =
|
|
57962
|
+
const excludePath = join20(commonDir, "info", "exclude");
|
|
57776
57963
|
let existing = "";
|
|
57777
57964
|
try {
|
|
57778
57965
|
existing = await readFile5(excludePath, "utf8");
|
|
@@ -57790,9 +57977,9 @@ async function ensureGeneratedAgentFilesIgnored(gitDir) {
|
|
|
57790
57977
|
async function ensureAgentRuntimeArtifacts(input2) {
|
|
57791
57978
|
const storagePaths = getWorktreeStoragePaths(input2.gitDir);
|
|
57792
57979
|
const artifacts = {
|
|
57793
|
-
agentCtlPath:
|
|
57794
|
-
claudeSettingsPath:
|
|
57795
|
-
codexHooksPath:
|
|
57980
|
+
agentCtlPath: join20(storagePaths.taskflowDir, RUNTIME_IDENTITY.agentControlBinary),
|
|
57981
|
+
claudeSettingsPath: join20(input2.worktreePath, ".claude", "settings.local.json"),
|
|
57982
|
+
codexHooksPath: join20(input2.worktreePath, ".codex", "hooks.json")
|
|
57796
57983
|
};
|
|
57797
57984
|
await mkdir3(dirname12(artifacts.claudeSettingsPath), { recursive: true });
|
|
57798
57985
|
await mkdir3(dirname12(artifacts.codexHooksPath), { recursive: true });
|
|
@@ -57936,7 +58123,7 @@ var init_agent_registry = __esm({
|
|
|
57936
58123
|
});
|
|
57937
58124
|
|
|
57938
58125
|
// ../host/src/modules/taskflow/services/agent-service.ts
|
|
57939
|
-
import { dirname as dirname13, join as
|
|
58126
|
+
import { dirname as dirname13, join as join21 } from "node:path";
|
|
57940
58127
|
function quoteShell3(value3) {
|
|
57941
58128
|
return `'${value3.replaceAll("'", "'\\''")}'`;
|
|
57942
58129
|
}
|
|
@@ -57948,7 +58135,7 @@ function buildDockerRuntimeBootstrap(runtimeEnvPath) {
|
|
|
57948
58135
|
}
|
|
57949
58136
|
function buildCodexNotifyFlag(runtimeEnvPath) {
|
|
57950
58137
|
if (!runtimeEnvPath) return "";
|
|
57951
|
-
const agentCtlPath =
|
|
58138
|
+
const agentCtlPath = join21(dirname13(runtimeEnvPath), RUNTIME_IDENTITY.agentControlBinary);
|
|
57952
58139
|
return ` -c ${quoteShell3(`notify=${JSON.stringify([agentCtlPath, "codex-notify"])}`)}`;
|
|
57953
58140
|
}
|
|
57954
58141
|
function buildBuiltInAgentInvocation(input2) {
|
|
@@ -59413,10 +59600,7 @@ ${oneshotPrompt}` : oneshotPrompt ?? baseSystemPrompt;
|
|
|
59413
59600
|
return panes.length > 0 ? panes : [{ id: "runtime", kind: "runtime", cwd: "worktree" }];
|
|
59414
59601
|
}
|
|
59415
59602
|
if (panes.some((pane) => pane.kind === "runtime")) return panes;
|
|
59416
|
-
|
|
59417
|
-
const runtimePane = { id: "runtime", kind: "runtime", split: "right", sizePct: 30, cwd: "worktree" };
|
|
59418
|
-
if (legacyShell >= 0) panes.splice(legacyShell, 1, runtimePane);
|
|
59419
|
-
else panes.push(runtimePane);
|
|
59603
|
+
panes.push({ id: "runtime", kind: "runtime", split: "right", sizePct: 30, cwd: "worktree" });
|
|
59420
59604
|
return panes;
|
|
59421
59605
|
}
|
|
59422
59606
|
requireDockerProfile(profile) {
|
|
@@ -60368,7 +60552,6 @@ var DEFAULT_TERMINAL_TTL_MS, ProjectInitTracker;
|
|
|
60368
60552
|
var init_project_init_service = __esm({
|
|
60369
60553
|
"../host/src/modules/taskflow/services/project-init-service.ts"() {
|
|
60370
60554
|
"use strict";
|
|
60371
|
-
init_log2();
|
|
60372
60555
|
DEFAULT_TERMINAL_TTL_MS = 6e4;
|
|
60373
60556
|
ProjectInitTracker = class {
|
|
60374
60557
|
inits = /* @__PURE__ */ new Map();
|
|
@@ -68981,10 +69164,10 @@ var init_native_terminal_service = __esm({
|
|
|
68981
69164
|
});
|
|
68982
69165
|
|
|
68983
69166
|
// ../host/src/modules/taskflow/server/routes/worktrees.ts
|
|
68984
|
-
import { mkdirSync as
|
|
69167
|
+
import { mkdirSync as mkdirSync9 } from "node:fs";
|
|
68985
69168
|
import { writeFile as writeFile6 } from "node:fs/promises";
|
|
68986
69169
|
import { tmpdir } from "node:os";
|
|
68987
|
-
import { join as
|
|
69170
|
+
import { join as join22, resolve as resolve16 } from "node:path";
|
|
68988
69171
|
async function apiGetNativeTerminalLaunch(project2, branch) {
|
|
68989
69172
|
touchDashboardActivity();
|
|
68990
69173
|
project2.ensureBranchNotBusy(branch);
|
|
@@ -69318,8 +69501,8 @@ async function apiUploadFiles(project2, name, req) {
|
|
|
69318
69501
|
}
|
|
69319
69502
|
const entries = formData.getAll("files");
|
|
69320
69503
|
if (entries.length === 0) return errorResponse("No files provided", 400);
|
|
69321
|
-
const uploadDir =
|
|
69322
|
-
|
|
69504
|
+
const uploadDir = join22(tmpdir(), RUNTIME_IDENTITY.tempUploadDir, sanitizeFilename(name));
|
|
69505
|
+
mkdirSync9(uploadDir, { recursive: true });
|
|
69323
69506
|
const results = [];
|
|
69324
69507
|
for (const entry of entries) {
|
|
69325
69508
|
if (!(entry instanceof File)) continue;
|
|
@@ -69330,7 +69513,7 @@ async function apiUploadFiles(project2, name, req) {
|
|
|
69330
69513
|
return errorResponse(`File too large: ${entry.name} (max 10MB)`, 400);
|
|
69331
69514
|
}
|
|
69332
69515
|
const safeName = `${Date.now()}_${sanitizeFilename(entry.name)}`;
|
|
69333
|
-
const destPath =
|
|
69516
|
+
const destPath = join22(uploadDir, safeName);
|
|
69334
69517
|
if (!resolve16(destPath).startsWith(`${uploadDir}/`)) {
|
|
69335
69518
|
return errorResponse("Invalid filename", 400);
|
|
69336
69519
|
}
|
|
@@ -70066,7 +70249,7 @@ var init_codex_app_server = __esm({
|
|
|
70066
70249
|
});
|
|
70067
70250
|
|
|
70068
70251
|
// ../host/src/modules/taskflow/adapters/run-store.ts
|
|
70069
|
-
import { mkdirSync as
|
|
70252
|
+
import { mkdirSync as mkdirSync10 } from "node:fs";
|
|
70070
70253
|
import { dirname as dirname15 } from "node:path";
|
|
70071
70254
|
function defaultRunStorePath() {
|
|
70072
70255
|
return globalPaths().database;
|
|
@@ -70227,7 +70410,7 @@ function isConstraint(error61, target) {
|
|
|
70227
70410
|
return error61 instanceof Error && error61.message.includes(target);
|
|
70228
70411
|
}
|
|
70229
70412
|
function createRunStore(path7 = defaultRunStorePath()) {
|
|
70230
|
-
|
|
70413
|
+
mkdirSync10(dirname15(path7), { recursive: true });
|
|
70231
70414
|
const database = openDatabase(path7);
|
|
70232
70415
|
database.pragma("foreign_keys = ON");
|
|
70233
70416
|
database.pragma("journal_mode = WAL");
|
|
@@ -70647,10 +70830,15 @@ var init_run_store = __esm({
|
|
|
70647
70830
|
input_json TEXT NOT NULL,
|
|
70648
70831
|
status TEXT NOT NULL CHECK(status IN ('queued', 'provisioning', 'running', 'waiting_input', 'completed', 'failed', 'cancelled', 'interrupted')),
|
|
70649
70832
|
workspace_policy TEXT NOT NULL CHECK(workspace_policy IN ('none', 'run')),
|
|
70833
|
+
workspace_strategy TEXT CHECK(workspace_strategy IN ('isolated_worktree', 'new_branch', 'current_branch')),
|
|
70834
|
+
workspace_access TEXT CHECK(workspace_access IN ('shared_read', 'exclusive_write')),
|
|
70650
70835
|
workflow_snapshot_id TEXT,
|
|
70651
70836
|
workspace_id TEXT,
|
|
70652
70837
|
worktree_path TEXT,
|
|
70653
70838
|
canonical_workspace_path TEXT,
|
|
70839
|
+
checkout_canonical_path TEXT,
|
|
70840
|
+
checkout_branch TEXT,
|
|
70841
|
+
environment_id TEXT,
|
|
70654
70842
|
branch TEXT,
|
|
70655
70843
|
base_branch TEXT,
|
|
70656
70844
|
base_commit TEXT,
|
|
@@ -70678,6 +70866,7 @@ var init_run_store = __esm({
|
|
|
70678
70866
|
path TEXT NOT NULL,
|
|
70679
70867
|
content_hash TEXT NOT NULL,
|
|
70680
70868
|
engine_version TEXT NOT NULL,
|
|
70869
|
+
key_version TEXT NOT NULL DEFAULT 'v1',
|
|
70681
70870
|
source TEXT NOT NULL,
|
|
70682
70871
|
metadata_json TEXT NOT NULL,
|
|
70683
70872
|
created_at TEXT NOT NULL
|
|
@@ -70702,6 +70891,7 @@ var init_run_store = __esm({
|
|
|
70702
70891
|
error TEXT,
|
|
70703
70892
|
session_id TEXT,
|
|
70704
70893
|
capabilities_json TEXT,
|
|
70894
|
+
checkpoint_json TEXT,
|
|
70705
70895
|
started_at TEXT,
|
|
70706
70896
|
completed_at TEXT,
|
|
70707
70897
|
UNIQUE(run_id, node_key, attempt)
|
|
@@ -70713,10 +70903,7 @@ var init_run_store = __esm({
|
|
|
70713
70903
|
source TEXT NOT NULL,
|
|
70714
70904
|
updated_at TEXT NOT NULL
|
|
70715
70905
|
);
|
|
70716
|
-
|
|
70717
|
-
"ALTER TABLE executions ADD COLUMN checkpoint_json TEXT;",
|
|
70718
|
-
"ALTER TABLE workflow_snapshots ADD COLUMN key_version TEXT NOT NULL DEFAULT 'v1';",
|
|
70719
|
-
`
|
|
70906
|
+
|
|
70720
70907
|
CREATE TABLE IF NOT EXISTS run_events (
|
|
70721
70908
|
id TEXT PRIMARY KEY,
|
|
70722
70909
|
run_id TEXT NOT NULL REFERENCES runs(id),
|
|
@@ -70729,8 +70916,7 @@ var init_run_store = __esm({
|
|
|
70729
70916
|
payload_json TEXT NOT NULL,
|
|
70730
70917
|
UNIQUE(run_id, sequence)
|
|
70731
70918
|
);
|
|
70732
|
-
|
|
70733
|
-
`
|
|
70919
|
+
|
|
70734
70920
|
CREATE TABLE IF NOT EXISTS workspace_leases (
|
|
70735
70921
|
id TEXT PRIMARY KEY,
|
|
70736
70922
|
run_id TEXT NOT NULL REFERENCES runs(id),
|
|
@@ -70748,12 +70934,7 @@ var init_run_store = __esm({
|
|
|
70748
70934
|
updated_at TEXT NOT NULL,
|
|
70749
70935
|
released_at TEXT
|
|
70750
70936
|
);
|
|
70751
|
-
|
|
70752
|
-
"ALTER TABLE runs ADD COLUMN workspace_strategy TEXT CHECK(workspace_strategy IN ('isolated_worktree', 'new_branch', 'current_branch'));",
|
|
70753
|
-
"ALTER TABLE runs ADD COLUMN workspace_access TEXT CHECK(workspace_access IN ('shared_read', 'exclusive_write'));",
|
|
70754
|
-
"ALTER TABLE runs ADD COLUMN checkout_canonical_path TEXT;",
|
|
70755
|
-
"ALTER TABLE runs ADD COLUMN checkout_branch TEXT;",
|
|
70756
|
-
`
|
|
70937
|
+
|
|
70757
70938
|
CREATE TABLE IF NOT EXISTS checkout_claims (
|
|
70758
70939
|
run_id TEXT PRIMARY KEY REFERENCES runs(id),
|
|
70759
70940
|
project_id TEXT NOT NULL,
|
|
@@ -70762,8 +70943,7 @@ var init_run_store = __esm({
|
|
|
70762
70943
|
created_at TEXT NOT NULL
|
|
70763
70944
|
);
|
|
70764
70945
|
CREATE INDEX IF NOT EXISTS checkout_claims_path_idx ON checkout_claims(project_id, canonical_path);
|
|
70765
|
-
|
|
70766
|
-
"ALTER TABLE runs ADD COLUMN environment_id TEXT;"
|
|
70946
|
+
`
|
|
70767
70947
|
];
|
|
70768
70948
|
}
|
|
70769
70949
|
});
|
|
@@ -71016,26 +71196,26 @@ var init_types2 = __esm({
|
|
|
71016
71196
|
});
|
|
71017
71197
|
|
|
71018
71198
|
// ../host/src/modules/taskflow/workflows/runtime/journal.ts
|
|
71019
|
-
import { appendFileSync as appendFileSync2, existsSync as
|
|
71020
|
-
import { dirname as dirname16, join as
|
|
71199
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync10, mkdirSync as mkdirSync11, readdirSync as readdirSync3, readFileSync as readFileSync13, writeFileSync as writeFileSync8 } from "node:fs";
|
|
71200
|
+
import { dirname as dirname16, join as join23 } from "node:path";
|
|
71021
71201
|
function dataRoot(roots = {}) {
|
|
71022
71202
|
return roots.dataRoot ?? globalPaths().root;
|
|
71023
71203
|
}
|
|
71024
71204
|
function runDir(runId, roots = {}) {
|
|
71025
|
-
return
|
|
71205
|
+
return join23(dataRoot(roots), "runs", runId);
|
|
71026
71206
|
}
|
|
71027
71207
|
function journalPath(runId, roots = {}) {
|
|
71028
|
-
return
|
|
71208
|
+
return join23(runDir(runId, roots), "journal.jsonl");
|
|
71029
71209
|
}
|
|
71030
71210
|
function ensureRunDir(runId, roots = {}) {
|
|
71031
71211
|
const dir = runDir(runId, roots);
|
|
71032
|
-
|
|
71212
|
+
mkdirSync11(dir, { recursive: true });
|
|
71033
71213
|
return dir;
|
|
71034
71214
|
}
|
|
71035
71215
|
function listRunIds(roots = {}) {
|
|
71036
|
-
const dir =
|
|
71037
|
-
if (!
|
|
71038
|
-
return readdirSync3(dir, { withFileTypes: true }).filter((e) => e.isDirectory() &&
|
|
71216
|
+
const dir = join23(dataRoot(roots), "runs");
|
|
71217
|
+
if (!existsSync10(dir)) return [];
|
|
71218
|
+
return readdirSync3(dir, { withFileTypes: true }).filter((e) => e.isDirectory() && existsSync10(join23(dir, e.name, "journal.jsonl"))).map((e) => e.name).sort().reverse();
|
|
71039
71219
|
}
|
|
71040
71220
|
function checkResumePreconditions(meta3, current) {
|
|
71041
71221
|
if (!meta3) return;
|
|
@@ -71061,7 +71241,7 @@ function canonicalArgs(value3) {
|
|
|
71061
71241
|
}
|
|
71062
71242
|
function writeResult(runId, value3, roots = {}) {
|
|
71063
71243
|
const dir = ensureRunDir(runId, roots);
|
|
71064
|
-
writeFileSync8(
|
|
71244
|
+
writeFileSync8(join23(dir, "result.json"), JSON.stringify(value3, null, 2), "utf8");
|
|
71065
71245
|
}
|
|
71066
71246
|
var JournalNotFoundError, ResumePreconditionError, Journal;
|
|
71067
71247
|
var init_journal = __esm({
|
|
@@ -71089,7 +71269,7 @@ var init_journal = __esm({
|
|
|
71089
71269
|
path;
|
|
71090
71270
|
constructor(runId, roots = {}) {
|
|
71091
71271
|
this.path = journalPath(runId, roots);
|
|
71092
|
-
|
|
71272
|
+
mkdirSync11(dirname16(this.path), { recursive: true });
|
|
71093
71273
|
}
|
|
71094
71274
|
append(entry) {
|
|
71095
71275
|
appendFileSync2(this.path, `${JSON.stringify(entry)}
|
|
@@ -71097,12 +71277,12 @@ var init_journal = __esm({
|
|
|
71097
71277
|
}
|
|
71098
71278
|
/** True if a journal file exists for this run id (vs. a typo'd / absent run). */
|
|
71099
71279
|
static exists(runId, roots = {}) {
|
|
71100
|
-
return
|
|
71280
|
+
return existsSync10(journalPath(runId, roots));
|
|
71101
71281
|
}
|
|
71102
71282
|
static load(runId, roots = {}) {
|
|
71103
71283
|
const path7 = journalPath(runId, roots);
|
|
71104
71284
|
const out = { results: /* @__PURE__ */ new Map(), indexByKey: /* @__PURE__ */ new Map() };
|
|
71105
|
-
if (!
|
|
71285
|
+
if (!existsSync10(path7)) return out;
|
|
71106
71286
|
const text = readFileSync13(path7, "utf8");
|
|
71107
71287
|
for (const line4 of text.split("\n")) {
|
|
71108
71288
|
const trimmed = line4.trim();
|
|
@@ -71505,9 +71685,9 @@ var init_sandbox = __esm({
|
|
|
71505
71685
|
});
|
|
71506
71686
|
|
|
71507
71687
|
// ../host/src/modules/taskflow/workflows/runtime/registry.ts
|
|
71508
|
-
import { existsSync as
|
|
71688
|
+
import { existsSync as existsSync11, readdirSync as readdirSync4, readFileSync as readFileSync14, statSync as statSync6 } from "node:fs";
|
|
71509
71689
|
import { homedir as homedir4 } from "node:os";
|
|
71510
|
-
import { dirname as dirname17, join as
|
|
71690
|
+
import { dirname as dirname17, join as join24, resolve as resolve17 } from "node:path";
|
|
71511
71691
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
71512
71692
|
function builtinDir() {
|
|
71513
71693
|
const here = dirname17(fileURLToPath4(import.meta.url));
|
|
@@ -71516,10 +71696,10 @@ function builtinDir() {
|
|
|
71516
71696
|
["..", "..", "builtins"],
|
|
71517
71697
|
["..", "..", "..", "..", "..", "workflows"]
|
|
71518
71698
|
]) {
|
|
71519
|
-
const candidate =
|
|
71520
|
-
if (
|
|
71699
|
+
const candidate = join24(here, ...rel);
|
|
71700
|
+
if (existsSync11(candidate)) return candidate;
|
|
71521
71701
|
}
|
|
71522
|
-
return
|
|
71702
|
+
return join24(here, "..", "builtins");
|
|
71523
71703
|
}
|
|
71524
71704
|
function projectDirs(cwd) {
|
|
71525
71705
|
const dirs = [];
|
|
@@ -71527,8 +71707,8 @@ function projectDirs(cwd) {
|
|
|
71527
71707
|
let dir = resolve17(cwd);
|
|
71528
71708
|
for (; ; ) {
|
|
71529
71709
|
const workflows = projectPaths(dir).workflows;
|
|
71530
|
-
if (
|
|
71531
|
-
if (
|
|
71710
|
+
if (existsSync11(workflows)) dirs.push(workflows);
|
|
71711
|
+
if (existsSync11(join24(dir, ".git"))) break;
|
|
71532
71712
|
const parent = dirname17(dir);
|
|
71533
71713
|
if (dir === home2 || parent === dir) break;
|
|
71534
71714
|
dir = parent;
|
|
@@ -71542,7 +71722,7 @@ function workflowDirs(cwd, roots) {
|
|
|
71542
71722
|
tier: "project",
|
|
71543
71723
|
dir: resolve17(dir)
|
|
71544
71724
|
})),
|
|
71545
|
-
{ tier: "user", dir: roots.globalDir ??
|
|
71725
|
+
{ tier: "user", dir: roots.globalDir ?? join24(dataRoot({ dataRoot: roots.dataRoot }), "workflows") },
|
|
71546
71726
|
{ tier: "builtin", dir: roots.builtinDir ?? builtinDir() }
|
|
71547
71727
|
];
|
|
71548
71728
|
}
|
|
@@ -71562,7 +71742,7 @@ function scanDir(tier, dir) {
|
|
|
71562
71742
|
const entries = [];
|
|
71563
71743
|
for (const file2 of names) {
|
|
71564
71744
|
if (!file2.endsWith(".js")) continue;
|
|
71565
|
-
const filePath =
|
|
71745
|
+
const filePath = join24(dir, file2);
|
|
71566
71746
|
try {
|
|
71567
71747
|
if (statSync6(filePath).size > MAX_WORKFLOW_BYTES) continue;
|
|
71568
71748
|
const { meta: meta3 } = parseWorkflow(readFileSync14(filePath, "utf8"));
|
|
@@ -78622,7 +78802,7 @@ var init_schema = __esm({
|
|
|
78622
78802
|
// ../host/src/modules/taskflow/workflows/worker/claude.ts
|
|
78623
78803
|
import { readlinkSync, realpathSync as realpathSync3 } from "node:fs";
|
|
78624
78804
|
import { homedir as homedir5 } from "node:os";
|
|
78625
|
-
import { basename as basename9, dirname as dirname18, isAbsolute as isAbsolute3, join as
|
|
78805
|
+
import { basename as basename9, dirname as dirname18, isAbsolute as isAbsolute3, join as join25, relative as relative4, resolve as resolve18 } from "node:path";
|
|
78626
78806
|
function toClaudeEffort(effort) {
|
|
78627
78807
|
return effort === "none" || effort === "minimal" ? "low" : effort;
|
|
78628
78808
|
}
|
|
@@ -78661,7 +78841,7 @@ function realpathUpward(p) {
|
|
|
78661
78841
|
for (let hops = 0; hops < 40; hops++) {
|
|
78662
78842
|
try {
|
|
78663
78843
|
const real = realpathSync3(existing);
|
|
78664
|
-
return suffix ?
|
|
78844
|
+
return suffix ? join25(real, suffix) : real;
|
|
78665
78845
|
} catch {
|
|
78666
78846
|
}
|
|
78667
78847
|
try {
|
|
@@ -78672,7 +78852,7 @@ function realpathUpward(p) {
|
|
|
78672
78852
|
}
|
|
78673
78853
|
const parent = dirname18(existing);
|
|
78674
78854
|
if (parent === existing) return p;
|
|
78675
|
-
suffix = suffix ?
|
|
78855
|
+
suffix = suffix ? join25(basename9(existing), suffix) : basename9(existing);
|
|
78676
78856
|
existing = parent;
|
|
78677
78857
|
}
|
|
78678
78858
|
return p;
|
|
@@ -78766,7 +78946,7 @@ function unquote(s) {
|
|
|
78766
78946
|
}
|
|
78767
78947
|
function expandHome(p) {
|
|
78768
78948
|
if (p === "~") return homedir5();
|
|
78769
|
-
if (p.startsWith("~/")) return
|
|
78949
|
+
if (p.startsWith("~/")) return join25(homedir5(), p.slice(2));
|
|
78770
78950
|
return p;
|
|
78771
78951
|
}
|
|
78772
78952
|
function usageFromResult(last) {
|
|
@@ -79398,7 +79578,7 @@ ${tail}` : message;
|
|
|
79398
79578
|
|
|
79399
79579
|
// ../host/src/modules/taskflow/workflows/worker/codex.ts
|
|
79400
79580
|
import { copyFile, writeFile as writeFile7 } from "node:fs/promises";
|
|
79401
|
-
import { basename as basename10, join as
|
|
79581
|
+
import { basename as basename10, join as join26 } from "node:path";
|
|
79402
79582
|
function textInput(text) {
|
|
79403
79583
|
return [{ type: "text", text, text_elements: [] }];
|
|
79404
79584
|
}
|
|
@@ -79814,7 +79994,7 @@ var init_codex = __esm({
|
|
|
79814
79994
|
const id = basename10(rawId) || "image";
|
|
79815
79995
|
const savedPath = typeof item.savedPath === "string" ? item.savedPath : void 0;
|
|
79816
79996
|
const b64 = typeof item.result === "string" ? item.result : void 0;
|
|
79817
|
-
const dest =
|
|
79997
|
+
const dest = join26(state3.cwd, `${id}.png`);
|
|
79818
79998
|
const write4 = (async () => {
|
|
79819
79999
|
try {
|
|
79820
80000
|
if (savedPath) await copyFile(savedPath, dest);
|
|
@@ -80605,7 +80785,7 @@ __export(pi_exports, {
|
|
|
80605
80785
|
});
|
|
80606
80786
|
import { mkdtempSync, rmSync as rmSync2 } from "node:fs";
|
|
80607
80787
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
80608
|
-
import { join as
|
|
80788
|
+
import { join as join27 } from "node:path";
|
|
80609
80789
|
function extractionPrompt2(spec, workingText) {
|
|
80610
80790
|
return `Earlier you produced this answer:
|
|
80611
80791
|
|
|
@@ -80769,7 +80949,7 @@ var init_pi = __esm({
|
|
|
80769
80949
|
return this.versionCheck;
|
|
80770
80950
|
}
|
|
80771
80951
|
async checkVersion() {
|
|
80772
|
-
const scratch = mkdtempSync(
|
|
80952
|
+
const scratch = mkdtempSync(join27(tmpdir2(), `${APP_SLUG}-pi-version-`));
|
|
80773
80953
|
let out;
|
|
80774
80954
|
try {
|
|
80775
80955
|
out = await captureStdout({
|
|
@@ -80976,7 +81156,7 @@ var init_factory = __esm({
|
|
|
80976
81156
|
});
|
|
80977
81157
|
|
|
80978
81158
|
// ../host/src/modules/taskflow/workflows/runtime/jsonl-writer.ts
|
|
80979
|
-
import { createWriteStream as createWriteStream2, mkdirSync as
|
|
81159
|
+
import { createWriteStream as createWriteStream2, mkdirSync as mkdirSync12 } from "node:fs";
|
|
80980
81160
|
function dirOf(filePath) {
|
|
80981
81161
|
const i3 = Math.max(filePath.lastIndexOf("/"), filePath.lastIndexOf("\\"));
|
|
80982
81162
|
return i3 <= 0 ? "." : filePath.slice(0, i3);
|
|
@@ -80989,7 +81169,7 @@ var init_jsonl_writer = __esm({
|
|
|
80989
81169
|
stream;
|
|
80990
81170
|
dead = false;
|
|
80991
81171
|
constructor(filePath, opts = {}) {
|
|
80992
|
-
|
|
81172
|
+
mkdirSync12(dirOf(filePath), { recursive: true });
|
|
80993
81173
|
this.stream = createWriteStream2(filePath, { flags: opts.flags ?? "a" });
|
|
80994
81174
|
this.stream.on("error", (err2) => {
|
|
80995
81175
|
this.dead = true;
|
|
@@ -81030,7 +81210,7 @@ var init_jsonl_writer = __esm({
|
|
|
81030
81210
|
});
|
|
81031
81211
|
|
|
81032
81212
|
// ../host/src/modules/taskflow/workflows/runtime/event-sink.ts
|
|
81033
|
-
import { join as
|
|
81213
|
+
import { join as join28 } from "node:path";
|
|
81034
81214
|
var FileEventSink;
|
|
81035
81215
|
var init_event_sink = __esm({
|
|
81036
81216
|
"../host/src/modules/taskflow/workflows/runtime/event-sink.ts"() {
|
|
@@ -81042,7 +81222,7 @@ var init_event_sink = __esm({
|
|
|
81042
81222
|
listeners;
|
|
81043
81223
|
clock;
|
|
81044
81224
|
constructor(runId, opts = {}) {
|
|
81045
|
-
this.writer = new JsonlWriter(
|
|
81225
|
+
this.writer = new JsonlWriter(join28(runDir(runId, opts.roots), "events.jsonl"), { flags: "a" });
|
|
81046
81226
|
this.listeners = opts.listeners ?? [];
|
|
81047
81227
|
this.clock = opts.clock ?? (() => globalThis.Date.now());
|
|
81048
81228
|
}
|
|
@@ -81144,15 +81324,15 @@ var init_semaphore = __esm({
|
|
|
81144
81324
|
});
|
|
81145
81325
|
|
|
81146
81326
|
// ../host/src/modules/taskflow/workflows/runtime/transcript.ts
|
|
81147
|
-
import { join as
|
|
81327
|
+
import { join as join29 } from "node:path";
|
|
81148
81328
|
function agentsDir(runId, roots = {}) {
|
|
81149
|
-
return
|
|
81329
|
+
return join29(runDir(runId, roots), "agents");
|
|
81150
81330
|
}
|
|
81151
81331
|
function agentTranscriptPath(runId, index, roots = {}) {
|
|
81152
|
-
return
|
|
81332
|
+
return join29(agentsDir(runId, roots), `${index}.jsonl`);
|
|
81153
81333
|
}
|
|
81154
81334
|
function agentTranscriptPathByName(runId, name, roots = {}) {
|
|
81155
|
-
return
|
|
81335
|
+
return join29(agentsDir(runId, roots), `${sanitizeName(name)}.jsonl`);
|
|
81156
81336
|
}
|
|
81157
81337
|
function truncate2(s, max) {
|
|
81158
81338
|
if (s.length <= max) return s;
|
|
@@ -81241,8 +81421,8 @@ var init_transcript = __esm({
|
|
|
81241
81421
|
|
|
81242
81422
|
// ../host/src/modules/taskflow/workflows/runtime/worktree.ts
|
|
81243
81423
|
import { execFile } from "node:child_process";
|
|
81244
|
-
import { existsSync as
|
|
81245
|
-
import { join as
|
|
81424
|
+
import { existsSync as existsSync12, rmSync as rmSync3 } from "node:fs";
|
|
81425
|
+
import { join as join30 } from "node:path";
|
|
81246
81426
|
import { promisify as promisify3 } from "node:util";
|
|
81247
81427
|
async function git3(cwd, args) {
|
|
81248
81428
|
const { stdout: stdout2 } = await exec("git", args, { cwd, maxBuffer: 64 * 1024 * 1024 });
|
|
@@ -81258,7 +81438,7 @@ async function findGitRoot(cwd) {
|
|
|
81258
81438
|
async function createWorktree(args) {
|
|
81259
81439
|
const name = `${args.runId}-${args.index}`;
|
|
81260
81440
|
const branch = args.branch ?? `aw/${name}`;
|
|
81261
|
-
const path7 =
|
|
81441
|
+
const path7 = join30(projectPaths(args.gitRoot).worktrees, name);
|
|
81262
81442
|
const base2 = await git3(args.gitRoot, ["rev-parse", "HEAD"]);
|
|
81263
81443
|
await pruneStaleWorktree(args.gitRoot, path7);
|
|
81264
81444
|
await git3(args.gitRoot, ["worktree", "add", "--relative-paths", "--detach", path7, base2]);
|
|
@@ -81280,7 +81460,7 @@ async function pruneStaleWorktree(gitRoot2, path7) {
|
|
|
81280
81460
|
});
|
|
81281
81461
|
await git3(gitRoot2, ["worktree", "prune"]).catch(() => {
|
|
81282
81462
|
});
|
|
81283
|
-
if (
|
|
81463
|
+
if (existsSync12(path7)) {
|
|
81284
81464
|
try {
|
|
81285
81465
|
rmSync3(path7, { recursive: true, force: true });
|
|
81286
81466
|
} catch {
|
|
@@ -82013,7 +82193,7 @@ ${e.status === "completed" ? "\u2713" : "\u2717"} ${e.status}${e.error ? `: ${e.
|
|
|
82013
82193
|
// ../host/src/modules/taskflow/workflows/runtime/run.ts
|
|
82014
82194
|
import { createHash as createHash10, randomBytes as randomBytes7 } from "node:crypto";
|
|
82015
82195
|
import { readFileSync as readFileSync15, writeFileSync as writeFileSync9 } from "node:fs";
|
|
82016
|
-
import { join as
|
|
82196
|
+
import { join as join31, resolve as resolve19 } from "node:path";
|
|
82017
82197
|
async function runWorkflow(opts) {
|
|
82018
82198
|
const filePath = resolve19(opts.file);
|
|
82019
82199
|
const source = readFileSync15(filePath, "utf8");
|
|
@@ -82073,7 +82253,7 @@ async function runWorkflow(opts) {
|
|
|
82073
82253
|
if (opts.signal?.aborted) ac.abort();
|
|
82074
82254
|
else opts.signal?.addEventListener("abort", onSig, { once: true });
|
|
82075
82255
|
events.emit({ type: "run", status: "started", runId, workflowFile: filePath });
|
|
82076
|
-
const heartbeatFile =
|
|
82256
|
+
const heartbeatFile = join31(runDir(runId, opts.roots), ".heartbeat");
|
|
82077
82257
|
const beat = () => {
|
|
82078
82258
|
try {
|
|
82079
82259
|
writeFileSync9(heartbeatFile, String(Date.now()));
|
|
@@ -83274,9 +83454,9 @@ var init_environment_diagnostics_service = __esm({
|
|
|
83274
83454
|
});
|
|
83275
83455
|
|
|
83276
83456
|
// ../host/src/modules/taskflow/services/environment-worker-factory.ts
|
|
83277
|
-
import { chmodSync as
|
|
83457
|
+
import { chmodSync as chmodSync7, mkdtempSync as mkdtempSync2, rmSync as rmSync4, writeFileSync as writeFileSync10 } from "node:fs";
|
|
83278
83458
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
83279
|
-
import { join as
|
|
83459
|
+
import { join as join32, posix as posix4, relative as relative5 } from "node:path";
|
|
83280
83460
|
function mapAgentSpecToEnvironment(spec, hostWorkspace, containerWorkspace) {
|
|
83281
83461
|
const suffix = relative5(hostWorkspace, spec.cwd);
|
|
83282
83462
|
if (suffix.startsWith("..")) {
|
|
@@ -83329,12 +83509,12 @@ var init_environment_worker_factory = __esm({
|
|
|
83329
83509
|
this.handle = handle2;
|
|
83330
83510
|
const containerWorkspace = handle2.workspace.containerPath;
|
|
83331
83511
|
if (!containerWorkspace) throw new Error(`Environment has no container workspace: ${handle2.id}`);
|
|
83332
|
-
this.directory = mkdtempSync2(
|
|
83512
|
+
this.directory = mkdtempSync2(join32(tmpdir3(), "taskflow-environment-workers-"));
|
|
83333
83513
|
const paths3 = Object.fromEntries(
|
|
83334
83514
|
Object.entries(executableByProvider).map(([provider, executable2]) => {
|
|
83335
|
-
const path7 =
|
|
83515
|
+
const path7 = join32(this.directory, provider);
|
|
83336
83516
|
writeFileSync10(path7, transport.executableShim(handle2, executable2), { encoding: "utf8", mode: 448 });
|
|
83337
|
-
|
|
83517
|
+
chmodSync7(path7, 448);
|
|
83338
83518
|
return [provider, path7];
|
|
83339
83519
|
})
|
|
83340
83520
|
);
|
|
@@ -84406,7 +84586,7 @@ var init_run_presentation_service = __esm({
|
|
|
84406
84586
|
});
|
|
84407
84587
|
|
|
84408
84588
|
// ../host/src/modules/taskflow/services/workspace-facade.ts
|
|
84409
|
-
import { join as
|
|
84589
|
+
import { join as join33 } from "node:path";
|
|
84410
84590
|
function workspaceBindingFor(runId, projectRoot2, workspaceRoot, baseBranch, baseCommit, workspace2, access5) {
|
|
84411
84591
|
const segment3 = runId.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "run";
|
|
84412
84592
|
if (workspace2.strategy !== "isolated_worktree") {
|
|
@@ -84423,7 +84603,7 @@ function workspaceBindingFor(runId, projectRoot2, workspaceRoot, baseBranch, bas
|
|
|
84423
84603
|
};
|
|
84424
84604
|
}
|
|
84425
84605
|
const name = `${APP_SLUG}-run-${segment3}`;
|
|
84426
|
-
const path7 =
|
|
84606
|
+
const path7 = join33(workspaceRoot, name);
|
|
84427
84607
|
return {
|
|
84428
84608
|
workspaceId: `workspace_${runId}`,
|
|
84429
84609
|
path: path7,
|
|
@@ -84441,7 +84621,7 @@ function slugSegment(value3, fallback) {
|
|
|
84441
84621
|
function forkBindingFor(run2, workspaceRoot, slug2) {
|
|
84442
84622
|
const runSegment = slugSegment(run2.id, "run");
|
|
84443
84623
|
const forkSegment = slugSegment(slug2, "fork");
|
|
84444
|
-
const path7 =
|
|
84624
|
+
const path7 = join33(workspaceRoot, `${APP_SLUG}-run-${runSegment}-fork-${forkSegment}`);
|
|
84445
84625
|
return {
|
|
84446
84626
|
workspaceId: `workspace_lease_${run2.id}_${forkSegment}`,
|
|
84447
84627
|
path: path7,
|
|
@@ -84743,7 +84923,7 @@ var init_workspace_facade = __esm({
|
|
|
84743
84923
|
|
|
84744
84924
|
// ../host/src/modules/taskflow/services/run-service.ts
|
|
84745
84925
|
import { readFile as readFile9 } from "node:fs/promises";
|
|
84746
|
-
import { join as
|
|
84926
|
+
import { join as join34 } from "node:path";
|
|
84747
84927
|
function provisioningReason(reason) {
|
|
84748
84928
|
return reason === "branch_conflict" || reason === "workspace_path_conflict" || reason === "checkout_busy" || reason === "dirty_workspace" || reason === "detached_head" ? reason : "workspace_provision_failed";
|
|
84749
84929
|
}
|
|
@@ -85402,7 +85582,7 @@ var init_run_service = __esm({
|
|
|
85402
85582
|
root2,
|
|
85403
85583
|
terminal.cursor,
|
|
85404
85584
|
terminal.type === "completed" ? "completed" : active.cancelling ? "cancelled" : "failed",
|
|
85405
|
-
terminal.type === "completed" ? { resultPath:
|
|
85585
|
+
terminal.type === "completed" ? { resultPath: join34(root2.dataRoot, "runs", engineRunId, "result.json") } : null
|
|
85406
85586
|
);
|
|
85407
85587
|
const completedAt = this.now().toISOString();
|
|
85408
85588
|
if (terminal.type === "completed") {
|
|
@@ -85514,7 +85694,7 @@ var init_run_service = __esm({
|
|
|
85514
85694
|
async completedResult(engineRunId, dataRoot2) {
|
|
85515
85695
|
try {
|
|
85516
85696
|
const result = JsonValueSchema.safeParse(
|
|
85517
|
-
JSON.parse(await readFile9(
|
|
85697
|
+
JSON.parse(await readFile9(join34(dataRoot2, "runs", engineRunId, "result.json"), "utf8"))
|
|
85518
85698
|
);
|
|
85519
85699
|
return result.success ? result.data : null;
|
|
85520
85700
|
} catch {
|
|
@@ -85919,7 +86099,7 @@ var init_taskflow_direct_session_service = __esm({
|
|
|
85919
86099
|
// ../host/src/modules/taskflow/services/workflow-catalog-service.ts
|
|
85920
86100
|
import { createHash as createHash11 } from "node:crypto";
|
|
85921
86101
|
import { readdir as readdir4, readFile as readFile10, stat as stat4 } from "node:fs/promises";
|
|
85922
|
-
import { join as
|
|
86102
|
+
import { join as join35, resolve as resolve21 } from "node:path";
|
|
85923
86103
|
function hash3(value3) {
|
|
85924
86104
|
return `sha256:${createHash11("sha256").update(value3).digest("hex")}`;
|
|
85925
86105
|
}
|
|
@@ -85963,7 +86143,7 @@ function duplicateDiagnostic(name, origin) {
|
|
|
85963
86143
|
function rootsFor(config3) {
|
|
85964
86144
|
return [
|
|
85965
86145
|
{ origin: "project", directory: projectPaths(resolve21(config3.projectRoot)).workflows, priority: 0 },
|
|
85966
|
-
{ origin: "global", directory:
|
|
86146
|
+
{ origin: "global", directory: join35(resolve21(config3.taskflowHome), PATH_NAMES.workflows), priority: 1 },
|
|
85967
86147
|
{ origin: "builtin", directory: resolve21(config3.builtinRoot), priority: 2 }
|
|
85968
86148
|
];
|
|
85969
86149
|
}
|
|
@@ -86077,7 +86257,7 @@ var init_workflow_catalog_service = __esm({
|
|
|
86077
86257
|
return { candidates, diagnostics, invalidEntries };
|
|
86078
86258
|
}
|
|
86079
86259
|
for (const name of names) {
|
|
86080
|
-
const path7 =
|
|
86260
|
+
const path7 = join35(root2.directory, name);
|
|
86081
86261
|
try {
|
|
86082
86262
|
if ((await stat4(path7)).size > MAX_WORKFLOW_BYTES2) {
|
|
86083
86263
|
const diagnostic = {
|
|
@@ -86122,7 +86302,7 @@ var init_workflow_catalog_service = __esm({
|
|
|
86122
86302
|
});
|
|
86123
86303
|
|
|
86124
86304
|
// ../host/src/modules/taskflow/services/workflow-event-bridge.ts
|
|
86125
|
-
import { join as
|
|
86305
|
+
import { join as join36 } from "node:path";
|
|
86126
86306
|
function record5(value3) {
|
|
86127
86307
|
if (typeof value3 !== "object" || value3 === null || Array.isArray(value3)) return null;
|
|
86128
86308
|
const output2 = {};
|
|
@@ -86212,7 +86392,7 @@ function executionFor(run2, existing, details, context) {
|
|
|
86212
86392
|
key: details.key,
|
|
86213
86393
|
index: details.index,
|
|
86214
86394
|
phaseIndex: details.phaseIndex,
|
|
86215
|
-
transcriptPath: context ?
|
|
86395
|
+
transcriptPath: context ? join36(context.dataRoot, "runs", context.engineRunId, "agents", `${details.index}.jsonl`) : null
|
|
86216
86396
|
},
|
|
86217
86397
|
workspaceId: run2.workspaceId,
|
|
86218
86398
|
input: existing?.input ?? null,
|
|
@@ -86280,7 +86460,7 @@ var init_workflow_event_bridge = __esm({
|
|
|
86280
86460
|
|
|
86281
86461
|
// ../host/src/modules/taskflow/services/workflow-runner.ts
|
|
86282
86462
|
import { mkdir as mkdir5, writeFile as writeFile8 } from "node:fs/promises";
|
|
86283
|
-
import { join as
|
|
86463
|
+
import { join as join37 } from "node:path";
|
|
86284
86464
|
function envelope(spec, message) {
|
|
86285
86465
|
return {
|
|
86286
86466
|
...message,
|
|
@@ -86320,9 +86500,9 @@ var init_workflow_runner = __esm({
|
|
|
86320
86500
|
const pid = process.pid;
|
|
86321
86501
|
let heartbeat;
|
|
86322
86502
|
const done = (async () => {
|
|
86323
|
-
const runDirectory =
|
|
86503
|
+
const runDirectory = join37(spec.spec.dataRoot, "runs", spec.engineRunId);
|
|
86324
86504
|
await mkdir5(runDirectory, { recursive: true });
|
|
86325
|
-
const snapshotPath =
|
|
86505
|
+
const snapshotPath = join37(runDirectory, "workflow.js");
|
|
86326
86506
|
await writeFile8(snapshotPath, spec.spec.source, "utf8");
|
|
86327
86507
|
emit(envelope(spec, { type: "started", pid }));
|
|
86328
86508
|
heartbeat = setInterval(() => {
|
|
@@ -87009,15 +87189,15 @@ var init_worktree_conversation_service = __esm({
|
|
|
87009
87189
|
|
|
87010
87190
|
// ../host/src/modules/taskflow/adapters/docker-compose-environment.ts
|
|
87011
87191
|
import { createHash as createHash12 } from "node:crypto";
|
|
87012
|
-
import { existsSync as
|
|
87192
|
+
import { existsSync as existsSync13, readFileSync as readFileSync16 } from "node:fs";
|
|
87013
87193
|
import { resolve as resolve22 } from "node:path";
|
|
87014
87194
|
function composeFile(workspacePath, configured) {
|
|
87015
87195
|
if (configured) {
|
|
87016
87196
|
const candidate = resolve22(workspacePath, configured);
|
|
87017
|
-
if (
|
|
87197
|
+
if (existsSync13(candidate)) return candidate;
|
|
87018
87198
|
throw new Error(`Docker Compose file does not exist: ${configured}`);
|
|
87019
87199
|
}
|
|
87020
|
-
const found = COMPOSE_FILENAMES.map((file2) => resolve22(workspacePath, file2)).find(
|
|
87200
|
+
const found = COMPOSE_FILENAMES.map((file2) => resolve22(workspacePath, file2)).find(existsSync13);
|
|
87021
87201
|
if (!found) throw new Error("No Docker Compose file was found");
|
|
87022
87202
|
return found;
|
|
87023
87203
|
}
|
|
@@ -87046,7 +87226,7 @@ var init_docker_compose_environment = __esm({
|
|
|
87046
87226
|
}
|
|
87047
87227
|
async probe(context) {
|
|
87048
87228
|
const version3 = await runCaptured(this.runner, { command: "docker", args: ["compose", "version", "--short"] });
|
|
87049
|
-
const configs = COMPOSE_FILENAMES.map((file2) => resolve22(context.workspacePath, file2)).filter(
|
|
87229
|
+
const configs = COMPOSE_FILENAMES.map((file2) => resolve22(context.workspacePath, file2)).filter(existsSync13);
|
|
87050
87230
|
return {
|
|
87051
87231
|
provider: this.id,
|
|
87052
87232
|
available: version3.exit.code === 0 && configs.length > 0,
|
|
@@ -88393,8 +88573,8 @@ var init_project_environments = __esm({
|
|
|
88393
88573
|
// ../host/src/modules/taskflow/server/project-app.ts
|
|
88394
88574
|
import { spawn as spawn8 } from "node:child_process";
|
|
88395
88575
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
88396
|
-
import { existsSync as
|
|
88397
|
-
import { dirname as dirname20, join as
|
|
88576
|
+
import { existsSync as existsSync14 } from "node:fs";
|
|
88577
|
+
import { dirname as dirname20, join as join38, resolve as resolve23 } from "node:path";
|
|
88398
88578
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
88399
88579
|
function workflowAgentRuntimeConfig(value3) {
|
|
88400
88580
|
if (typeof value3 !== "object" || value3 === null || Array.isArray(value3)) {
|
|
@@ -88425,7 +88605,7 @@ function createProjectApp(runtime2, instancePrefix, host2) {
|
|
|
88425
88605
|
const reconciliationService = runtime2.reconciliationService;
|
|
88426
88606
|
const codexAppServerClient = new CodexAppServerClient({
|
|
88427
88607
|
clientName: RUNTIME_IDENTITY.codexClientName,
|
|
88428
|
-
clientVersion:
|
|
88608
|
+
clientVersion: PORTTA_FLOW_VERSION
|
|
88429
88609
|
});
|
|
88430
88610
|
const claudeCliClient = new ClaudeCliClient();
|
|
88431
88611
|
const worktreeConversationService = new WorktreeConversationService({
|
|
@@ -88478,14 +88658,14 @@ function createProjectApp(runtime2, instancePrefix, host2) {
|
|
|
88478
88658
|
const workflowEvents = new WorkflowEventBridge({ store: runStore, publisher: runEventPublisher });
|
|
88479
88659
|
const workspaces = new WorkspaceFacade({ store: runStore, git: git4 });
|
|
88480
88660
|
const workflowDataRoot = globalPaths().root;
|
|
88481
|
-
const supervisorSocket =
|
|
88661
|
+
const supervisorSocket = join38(workflowDataRoot, "runtime", "supervisor.sock");
|
|
88482
88662
|
const supervisor = new AgentSupervisorClient({
|
|
88483
88663
|
socketPath: supervisorSocket,
|
|
88484
88664
|
startDaemon: () => {
|
|
88485
88665
|
const directory = dirname20(fileURLToPath5(import.meta.url));
|
|
88486
|
-
const builtEntry =
|
|
88487
|
-
const sourceEntry =
|
|
88488
|
-
const child =
|
|
88666
|
+
const builtEntry = join38(directory, "supervisor.js");
|
|
88667
|
+
const sourceEntry = join38(directory, "supervisor.ts");
|
|
88668
|
+
const child = existsSync14(builtEntry) ? spawn8(process.execPath, [builtEntry], { detached: true, stdio: "ignore" }) : spawn8(process.execPath, ["--import", "tsx", sourceEntry], { detached: true, stdio: "ignore" });
|
|
88489
88669
|
child.unref();
|
|
88490
88670
|
}
|
|
88491
88671
|
});
|
|
@@ -89928,7 +90108,7 @@ var init_upgrade = __esm({
|
|
|
89928
90108
|
});
|
|
89929
90109
|
|
|
89930
90110
|
// ../host/src/modules/taskflow/server/host.ts
|
|
89931
|
-
import { existsSync as
|
|
90111
|
+
import { existsSync as existsSync15 } from "node:fs";
|
|
89932
90112
|
function createTaskflowHost(options) {
|
|
89933
90113
|
const apps = /* @__PURE__ */ new Map();
|
|
89934
90114
|
const manager = new ProjectManager({
|
|
@@ -89958,8 +90138,8 @@ function createTaskflowHost(options) {
|
|
|
89958
90138
|
manager,
|
|
89959
90139
|
apps,
|
|
89960
90140
|
allowlist: options.projectAllowlist,
|
|
89961
|
-
//
|
|
89962
|
-
//
|
|
90141
|
+
// Backs the project-setup status listing. Registration itself does not
|
|
90142
|
+
// scaffold or launch an agent; `taskflow init` is the authoring path.
|
|
89963
90143
|
initTracker: new ProjectInitTracker(),
|
|
89964
90144
|
closeSockets: (prefix) => sockets.closeProject(prefix)
|
|
89965
90145
|
});
|
|
@@ -89972,7 +90152,7 @@ function createTaskflowHost(options) {
|
|
|
89972
90152
|
if (!isGitRepo(cwd)) return;
|
|
89973
90153
|
const repoRoot = canonicalizeFsPath(projectRoot(cwd));
|
|
89974
90154
|
const paths3 = projectPaths(repoRoot);
|
|
89975
|
-
if (!
|
|
90155
|
+
if (!existsSync15(paths3.config) && !existsSync15(paths3.localConfig)) return;
|
|
89976
90156
|
try {
|
|
89977
90157
|
manager.addEphemeral(repoRoot);
|
|
89978
90158
|
} catch (err2) {
|
|
@@ -89987,7 +90167,7 @@ function createTaskflowHost(options) {
|
|
|
89987
90167
|
}
|
|
89988
90168
|
};
|
|
89989
90169
|
}
|
|
89990
|
-
var
|
|
90170
|
+
var init_host = __esm({
|
|
89991
90171
|
"../host/src/modules/taskflow/server/host.ts"() {
|
|
89992
90172
|
"use strict";
|
|
89993
90173
|
init_paths2();
|
|
@@ -90043,7 +90223,7 @@ function createDaemonTaskflowHost(context) {
|
|
|
90043
90223
|
port: hostListen(env).port,
|
|
90044
90224
|
workflowBuiltinsDir: taskflowAssets(env).workflowBuiltinsDir,
|
|
90045
90225
|
version: env.PORTTA_VERSION || "0.0.0-local",
|
|
90046
|
-
buildDate: true ? "2026-09-
|
|
90226
|
+
buildDate: true ? "2026-09-18T02:24:34Z" : (/* @__PURE__ */ new Date()).toISOString(),
|
|
90047
90227
|
environmentStore: createEnvironmentStore(paths3.database),
|
|
90048
90228
|
hostEnvironmentProvider: new HostEnvironmentProvider(),
|
|
90049
90229
|
devContainerProvider: new DevContainerProvider(),
|
|
@@ -90114,9 +90294,8 @@ var init_taskflow4 = __esm({
|
|
|
90114
90294
|
init_user_shell_env();
|
|
90115
90295
|
init_environment_service_discovery();
|
|
90116
90296
|
init_project_allowlist();
|
|
90117
|
-
|
|
90297
|
+
init_host();
|
|
90118
90298
|
init_host_projects();
|
|
90119
|
-
init_runtime();
|
|
90120
90299
|
TASKFLOW_SOCKET_MOUNT_PATH = `/ws/modules/${taskflowModule.id}`;
|
|
90121
90300
|
taskflowHostModule = createTaskflowHostModule();
|
|
90122
90301
|
}
|
|
@@ -90990,7 +91169,7 @@ var require_util2 = __commonJS({
|
|
|
90990
91169
|
function getFilesInPath(source) {
|
|
90991
91170
|
const lstatSync = fs.lstatSync;
|
|
90992
91171
|
const readdirSync11 = fs.readdirSync;
|
|
90993
|
-
const
|
|
91172
|
+
const join71 = path7.join;
|
|
90994
91173
|
function isDirectory2(source2) {
|
|
90995
91174
|
return lstatSync(source2).isDirectory();
|
|
90996
91175
|
}
|
|
@@ -90999,12 +91178,12 @@ var require_util2 = __commonJS({
|
|
|
90999
91178
|
}
|
|
91000
91179
|
function getDirectories(source2) {
|
|
91001
91180
|
return readdirSync11(source2).map((name) => {
|
|
91002
|
-
return
|
|
91181
|
+
return join71(source2, name);
|
|
91003
91182
|
}).filter(isDirectory2);
|
|
91004
91183
|
}
|
|
91005
91184
|
function getFiles(source2) {
|
|
91006
91185
|
return readdirSync11(source2).map((name) => {
|
|
91007
|
-
return
|
|
91186
|
+
return join71(source2, name);
|
|
91008
91187
|
}).filter(isFile);
|
|
91009
91188
|
}
|
|
91010
91189
|
function getFilesRecursively(source2) {
|
|
@@ -100639,7 +100818,7 @@ var require_network = __commonJS({
|
|
|
100639
100818
|
var exec3 = __require("child_process").exec;
|
|
100640
100819
|
var execSync = __require("child_process").execSync;
|
|
100641
100820
|
var execFileSync2 = __require("child_process").execFileSync;
|
|
100642
|
-
var
|
|
100821
|
+
var readFileSync37 = __require("fs").readFileSync;
|
|
100643
100822
|
var fs = __require("fs");
|
|
100644
100823
|
var util2 = require_util2();
|
|
100645
100824
|
var _platform = process.platform;
|
|
@@ -101127,7 +101306,7 @@ var require_network = __commonJS({
|
|
|
101127
101306
|
return result;
|
|
101128
101307
|
}
|
|
101129
101308
|
try {
|
|
101130
|
-
const content =
|
|
101309
|
+
const content = readFileSync37(file2, { encoding: "utf8" });
|
|
101131
101310
|
const lines = content.split("\n").filter((l) => /iface|source/.test(l));
|
|
101132
101311
|
lines.forEach((line4) => {
|
|
101133
101312
|
const parts2 = line4.replace(/\s+/g, " ").trim().split(" ");
|
|
@@ -101294,7 +101473,7 @@ var require_network = __commonJS({
|
|
|
101294
101473
|
return false;
|
|
101295
101474
|
}
|
|
101296
101475
|
}
|
|
101297
|
-
function
|
|
101476
|
+
function networkInterfaces(callback, rescan, defaultString) {
|
|
101298
101477
|
if (typeof callback === "string") {
|
|
101299
101478
|
defaultString = callback;
|
|
101300
101479
|
rescan = true;
|
|
@@ -101709,7 +101888,7 @@ var require_network = __commonJS({
|
|
|
101709
101888
|
});
|
|
101710
101889
|
});
|
|
101711
101890
|
}
|
|
101712
|
-
exports.networkInterfaces =
|
|
101891
|
+
exports.networkInterfaces = networkInterfaces;
|
|
101713
101892
|
function calcNetworkSpeed(iface, rx_bytes, tx_bytes, operstate, rx_dropped, rx_errors, tx_dropped, tx_errors) {
|
|
101714
101893
|
const result = {
|
|
101715
101894
|
iface,
|
|
@@ -101782,7 +101961,7 @@ var require_network = __commonJS({
|
|
|
101782
101961
|
const workload = [];
|
|
101783
101962
|
if (ifacesArray.length && ifacesArray[0].trim() === "*") {
|
|
101784
101963
|
ifacesArray = [];
|
|
101785
|
-
|
|
101964
|
+
networkInterfaces(false).then((allIFaces) => {
|
|
101786
101965
|
for (let iface of allIFaces) {
|
|
101787
101966
|
ifacesArray.push(iface.iface);
|
|
101788
101967
|
}
|
|
@@ -101948,7 +102127,7 @@ var require_network = __commonJS({
|
|
|
101948
102127
|
const psections = stdout2.toString().split(/\n\s*\n/);
|
|
101949
102128
|
perfData = parseLinesWindowsPerfData(psections);
|
|
101950
102129
|
}
|
|
101951
|
-
|
|
102130
|
+
networkInterfaces(false).then((interfaces) => {
|
|
101952
102131
|
rx_bytes = 0;
|
|
101953
102132
|
tx_bytes = 0;
|
|
101954
102133
|
perfData.forEach((detail2) => {
|
|
@@ -103405,7 +103584,7 @@ var require_processes = __commonJS({
|
|
|
103405
103584
|
}
|
|
103406
103585
|
exec3(cmd, { maxBuffer: 1024 * 102400 }, function(error61, stdout3) {
|
|
103407
103586
|
let curr_processes = stdout3.toString().split("\n");
|
|
103408
|
-
let all =
|
|
103587
|
+
let all = parseProcStat(curr_processes.shift());
|
|
103409
103588
|
let list_new = {};
|
|
103410
103589
|
let resultProcess = {};
|
|
103411
103590
|
curr_processes.forEach((element) => {
|
|
@@ -103583,7 +103762,7 @@ var require_processes = __commonJS({
|
|
|
103583
103762
|
});
|
|
103584
103763
|
}
|
|
103585
103764
|
exports.services = services;
|
|
103586
|
-
function
|
|
103765
|
+
function parseProcStat(line4) {
|
|
103587
103766
|
const parts2 = line4.replace(/ +/g, " ").split(" ");
|
|
103588
103767
|
const user = parts2.length >= 2 ? parseInt(parts2[1]) : 0;
|
|
103589
103768
|
const nice = parts2.length >= 3 ? parseInt(parts2[2]) : 0;
|
|
@@ -103923,7 +104102,7 @@ var require_processes = __commonJS({
|
|
|
103923
104102
|
});
|
|
103924
104103
|
exec3(cmd, { maxBuffer: 1024 * 102400 }, (error62, stdout3) => {
|
|
103925
104104
|
let curr_processes = stdout3.toString().split("\n");
|
|
103926
|
-
let all =
|
|
104105
|
+
let all = parseProcStat(curr_processes.shift());
|
|
103927
104106
|
let list_new = {};
|
|
103928
104107
|
let resultProcess = {};
|
|
103929
104108
|
curr_processes.forEach((element) => {
|
|
@@ -104378,7 +104557,7 @@ var require_processes = __commonJS({
|
|
|
104378
104557
|
}
|
|
104379
104558
|
exec3(cmd, { maxBuffer: 1024 * 102400 }, (error61, stdout3) => {
|
|
104380
104559
|
let curr_processes = stdout3.toString().split("\n");
|
|
104381
|
-
let all =
|
|
104560
|
+
let all = parseProcStat(curr_processes.shift());
|
|
104382
104561
|
let list_new = {};
|
|
104383
104562
|
let resultProcess = {};
|
|
104384
104563
|
curr_processes.forEach((element) => {
|
|
@@ -108452,7 +108631,7 @@ var require_lib = __commonJS({
|
|
|
108452
108631
|
});
|
|
108453
108632
|
|
|
108454
108633
|
// src/commands/flow/daemon.ts
|
|
108455
|
-
import { existsSync as existsSync25, readFileSync as
|
|
108634
|
+
import { existsSync as existsSync25, readFileSync as readFileSync23 } from "node:fs";
|
|
108456
108635
|
function flowStateDir(env = process.env) {
|
|
108457
108636
|
const configured = env[ENV_NAMES.hostStateDir]?.trim();
|
|
108458
108637
|
if (configured) return configured;
|
|
@@ -108470,7 +108649,7 @@ function daemonToken(env = process.env) {
|
|
|
108470
108649
|
const file2 = hostTokenFile(flowStateDir(env));
|
|
108471
108650
|
if (existsSync25(file2)) {
|
|
108472
108651
|
try {
|
|
108473
|
-
const token =
|
|
108652
|
+
const token = readFileSync23(file2, "utf8").trim();
|
|
108474
108653
|
if (token) return token;
|
|
108475
108654
|
} catch {
|
|
108476
108655
|
}
|
|
@@ -108573,8 +108752,8 @@ var init_server_port = __esm({
|
|
|
108573
108752
|
|
|
108574
108753
|
// ../host/src/modules/taskflow/lib/shell.ts
|
|
108575
108754
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
108576
|
-
import { existsSync as existsSync26, readFileSync as
|
|
108577
|
-
import { basename as basename16, join as
|
|
108755
|
+
import { existsSync as existsSync26, readFileSync as readFileSync24 } from "node:fs";
|
|
108756
|
+
import { basename as basename16, join as join49 } from "node:path";
|
|
108578
108757
|
function run(cmd, args, opts) {
|
|
108579
108758
|
const resolved2 = resolveHostTool(cmd) ?? cmd;
|
|
108580
108759
|
const result = spawnSync8(resolved2, args, {
|
|
@@ -108594,10 +108773,10 @@ function getGitRoot() {
|
|
|
108594
108773
|
return result.stdout.toString().trim();
|
|
108595
108774
|
}
|
|
108596
108775
|
function detectProjectName(gitRoot2) {
|
|
108597
|
-
const pkgPath =
|
|
108776
|
+
const pkgPath = join49(gitRoot2, "package.json");
|
|
108598
108777
|
if (existsSync26(pkgPath)) {
|
|
108599
108778
|
try {
|
|
108600
|
-
const pkg = JSON.parse(
|
|
108779
|
+
const pkg = JSON.parse(readFileSync24(pkgPath, "utf-8"));
|
|
108601
108780
|
if (pkg.name) return pkg.name;
|
|
108602
108781
|
} catch {
|
|
108603
108782
|
}
|
|
@@ -108752,36 +108931,6 @@ var init_project_env2 = __esm({
|
|
|
108752
108931
|
}
|
|
108753
108932
|
});
|
|
108754
108933
|
|
|
108755
|
-
// src/commands/flow/process.ts
|
|
108756
|
-
async function runProcess3(file2, args = [], options = {}) {
|
|
108757
|
-
const execaOptions = {
|
|
108758
|
-
cwd: options.cwd,
|
|
108759
|
-
env: options.env,
|
|
108760
|
-
input: options.input,
|
|
108761
|
-
reject: options.reject ?? true,
|
|
108762
|
-
shell: false,
|
|
108763
|
-
timeout: options.timeout,
|
|
108764
|
-
stdio: options.input !== void 0 ? ["pipe", options.stdio ?? "pipe", options.stdio ?? "pipe"] : options.stdio
|
|
108765
|
-
};
|
|
108766
|
-
const result = await execa(file2, [...args], execaOptions);
|
|
108767
|
-
const notStarted = result.failed && result.exitCode === void 0 && !result.signal && !result.timedOut;
|
|
108768
|
-
return {
|
|
108769
|
-
stdout: typeof result.stdout === "string" ? result.stdout : "",
|
|
108770
|
-
stderr: typeof result.stderr === "string" ? result.stderr : "",
|
|
108771
|
-
exitCode: result.exitCode ?? (result.failed ? 1 : 0),
|
|
108772
|
-
failed: result.failed,
|
|
108773
|
-
...notStarted && "originalMessage" in result ? { error: String(result.originalMessage) } : {}
|
|
108774
|
-
};
|
|
108775
|
-
}
|
|
108776
|
-
var PROCESS_POLICY2;
|
|
108777
|
-
var init_process = __esm({
|
|
108778
|
-
"src/commands/flow/process.ts"() {
|
|
108779
|
-
"use strict";
|
|
108780
|
-
init_execa();
|
|
108781
|
-
PROCESS_POLICY2 = Object.freeze({ shell: false, argumentArraysOnly: true });
|
|
108782
|
-
}
|
|
108783
|
-
});
|
|
108784
|
-
|
|
108785
108934
|
// src/commands/flow/completions.ts
|
|
108786
108935
|
var completions_exports = {};
|
|
108787
108936
|
__export(completions_exports, {
|
|
@@ -108814,7 +108963,7 @@ function completionTree(command) {
|
|
|
108814
108963
|
};
|
|
108815
108964
|
}
|
|
108816
108965
|
async function defaultRunGit(args) {
|
|
108817
|
-
const result = await
|
|
108966
|
+
const result = await runProcess("git", args, { reject: false });
|
|
108818
108967
|
return { exitCode: result.exitCode, stdout: result.stdout.trim() };
|
|
108819
108968
|
}
|
|
108820
108969
|
async function listWorktreeBranches(deps = { runGit: defaultRunGit }) {
|
|
@@ -110103,9 +110252,9 @@ ${f}
|
|
|
110103
110252
|
});
|
|
110104
110253
|
|
|
110105
110254
|
// ../host/src/modules/taskflow/services/init-authoring.ts
|
|
110106
|
-
import { existsSync as existsSync28, readFileSync as
|
|
110255
|
+
import { existsSync as existsSync28, readFileSync as readFileSync25, rmSync as rmSync8 } from "node:fs";
|
|
110107
110256
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
110108
|
-
import { join as
|
|
110257
|
+
import { join as join50 } from "node:path";
|
|
110109
110258
|
function isRecord8(value3) {
|
|
110110
110259
|
return typeof value3 === "object" && value3 !== null && !Array.isArray(value3);
|
|
110111
110260
|
}
|
|
@@ -110113,9 +110262,9 @@ function readString4(value3) {
|
|
|
110113
110262
|
return typeof value3 === "string" && value3.trim().length > 0 ? value3 : null;
|
|
110114
110263
|
}
|
|
110115
110264
|
function detectPackageManager(gitRoot2) {
|
|
110116
|
-
if (existsSync28(
|
|
110117
|
-
if (existsSync28(
|
|
110118
|
-
if (existsSync28(
|
|
110265
|
+
if (existsSync28(join50(gitRoot2, "bun.lock")) || existsSync28(join50(gitRoot2, "bun.lockb"))) return "bun";
|
|
110266
|
+
if (existsSync28(join50(gitRoot2, "pnpm-lock.yaml"))) return "pnpm";
|
|
110267
|
+
if (existsSync28(join50(gitRoot2, "yarn.lock"))) return "yarn";
|
|
110119
110268
|
return "npm";
|
|
110120
110269
|
}
|
|
110121
110270
|
function detectMainBranch(gitRoot2) {
|
|
@@ -110152,25 +110301,25 @@ function detectInitProjectContext(gitRoot2, defaultAgent) {
|
|
|
110152
110301
|
}
|
|
110153
110302
|
function detectInitEnvironment(gitRoot2, requested = "auto") {
|
|
110154
110303
|
const devcontainerConfigs = [".devcontainer.json", ".devcontainer/devcontainer.json"].filter(
|
|
110155
|
-
(path7) => existsSync28(
|
|
110304
|
+
(path7) => existsSync28(join50(gitRoot2, path7))
|
|
110156
110305
|
);
|
|
110157
110306
|
const composeFiles2 = ["compose.yaml", "compose.yml", "docker-compose.yaml", "docker-compose.yml"].filter(
|
|
110158
|
-
(path7) => existsSync28(
|
|
110307
|
+
(path7) => existsSync28(join50(gitRoot2, path7))
|
|
110159
110308
|
);
|
|
110160
|
-
const selectedRuntime = requested !== "auto" ? requested : devcontainerConfigs.length === 1 ? "devcontainer" : composeFiles2.length > 0 ? "compose" : existsSync28(
|
|
110309
|
+
const selectedRuntime = requested !== "auto" ? requested : devcontainerConfigs.length === 1 ? "devcontainer" : composeFiles2.length > 0 ? "compose" : existsSync28(join50(gitRoot2, "Dockerfile")) ? "dockerfile" : "host";
|
|
110161
110310
|
return {
|
|
110162
110311
|
devcontainerConfigs,
|
|
110163
110312
|
composeFiles: composeFiles2,
|
|
110164
|
-
dockerfile: existsSync28(
|
|
110165
|
-
packageJson: existsSync28(
|
|
110313
|
+
dockerfile: existsSync28(join50(gitRoot2, "Dockerfile")),
|
|
110314
|
+
packageJson: existsSync28(join50(gitRoot2, "package.json")),
|
|
110166
110315
|
selectedRuntime
|
|
110167
110316
|
};
|
|
110168
110317
|
}
|
|
110169
110318
|
function buildInitPromptSpec(context) {
|
|
110170
110319
|
const systemPrompt = [
|
|
110171
110320
|
`You are bootstrapping a local repository for ${APP_NAME}.`,
|
|
110172
|
-
`A starter \`${
|
|
110173
|
-
`Inspect the repository in the current working directory and edit that existing \`${
|
|
110321
|
+
`A starter \`${PROJECT_CONFIG_PATH2}\` already exists at the repo root.`,
|
|
110322
|
+
`Inspect the repository in the current working directory and edit that existing \`${PROJECT_CONFIG_PATH2}\` in place.`,
|
|
110174
110323
|
"Do not modify any other file.",
|
|
110175
110324
|
"Do not ask the user questions. Infer the config from the repository contents.",
|
|
110176
110325
|
"Be efficient: inspect only the files needed to determine the project name, main branch, service layout, dev commands, and ports.",
|
|
@@ -110183,7 +110332,7 @@ function buildInitPromptSpec(context) {
|
|
|
110183
110332
|
"workspace.mainBranch: infer from git",
|
|
110184
110333
|
`workspace.worktrees.root: keep ${APP_DEFAULTS.worktreeRoot} unless there is clear evidence of an existing alternative`,
|
|
110185
110334
|
"Do not copy detected Compose services or ports into config unless the user needs an explicit override.",
|
|
110186
|
-
"Preserve the commented services example: it is for Taskflow-managed host or
|
|
110335
|
+
"Preserve the commented services example: it is for Taskflow-managed host or Docker profile processes, not discovered Dev Container or Compose services.",
|
|
110187
110336
|
"profiles.default.environment.provider: auto unless the user chose a runtime override. If no Dev Container, Compose, or Dockerfile exists, set it to host.",
|
|
110188
110337
|
"For a host project, put its one managed development command in profiles.default.environment.command; never add a third app command pane for it.",
|
|
110189
110338
|
"Only declare services when a host command has a proven port and supports the injected port environment variable. When the port is uncertain, keep the command but omit services.",
|
|
@@ -110195,11 +110344,11 @@ function buildInitPromptSpec(context) {
|
|
|
110195
110344
|
"Only include optional sections like auto_name, lifecycleHooks, sandbox/docker config, mounts, or systemPrompt if the repository gives clear evidence they are needed.",
|
|
110196
110345
|
"Prefer editing the existing keys over replacing the file with a completely different shape.",
|
|
110197
110346
|
"Preserve the existing template structure and comments unless a specific change requires updating them.",
|
|
110198
|
-
`Before finishing, verify that \`${
|
|
110347
|
+
`Before finishing, verify that \`${PROJECT_CONFIG_PATH2}\` exists and contains the final YAML.`
|
|
110199
110348
|
].join("\n");
|
|
110200
110349
|
return {
|
|
110201
110350
|
systemPrompt,
|
|
110202
|
-
userPrompt: `Adapt the existing starter \`${
|
|
110351
|
+
userPrompt: `Adapt the existing starter \`${PROJECT_CONFIG_PATH2}\` for this repository.`
|
|
110203
110352
|
};
|
|
110204
110353
|
}
|
|
110205
110354
|
function buildInitAgentCommand(agent, prompt, outputPrefix = `${APP_SLUG}-init`) {
|
|
@@ -110228,7 +110377,7 @@ function buildInitAgentCommand(agent, prompt, outputPrefix = `${APP_SLUG}-init`)
|
|
|
110228
110377
|
]
|
|
110229
110378
|
};
|
|
110230
110379
|
}
|
|
110231
|
-
const summaryPath =
|
|
110380
|
+
const summaryPath = join50(tmpdir4(), `${outputPrefix}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.txt`);
|
|
110232
110381
|
return {
|
|
110233
110382
|
agent,
|
|
110234
110383
|
cmd: "codex",
|
|
@@ -110484,7 +110633,7 @@ function finalizeAgentRun(spec, exitCode, stdoutResult, stderr2) {
|
|
|
110484
110633
|
let summary = stdoutResult.assistantText.trim();
|
|
110485
110634
|
if (spec.summaryPath && existsSync28(spec.summaryPath)) {
|
|
110486
110635
|
try {
|
|
110487
|
-
summary =
|
|
110636
|
+
summary = readFileSync25(spec.summaryPath, "utf8").trim() || summary;
|
|
110488
110637
|
} finally {
|
|
110489
110638
|
rmSync8(spec.summaryPath, { force: true });
|
|
110490
110639
|
}
|
|
@@ -110519,7 +110668,7 @@ workspace:
|
|
|
110519
110668
|
# intervalSeconds: 300
|
|
110520
110669
|
|
|
110521
110670
|
# Taskflow discovers Dev Container, Compose, and Dockerfile services from the resolved runtime.
|
|
110522
|
-
# Declare services here only for a process started by a host or
|
|
110671
|
+
# Declare services here only for a process started by a host or Docker profile pane
|
|
110523
110672
|
# that accepts an injected port environment variable. Do not duplicate discovered services.
|
|
110524
110673
|
services:
|
|
110525
110674
|
# Example app service with a predictable port per worktree.
|
|
@@ -110545,8 +110694,8 @@ exposure:
|
|
|
110545
110694
|
# Profiles define runtime, permissions, and tmux pane layout.
|
|
110546
110695
|
profiles:
|
|
110547
110696
|
default:
|
|
110548
|
-
#
|
|
110549
|
-
#
|
|
110697
|
+
# Panes run on the host; the environment provider controls the development
|
|
110698
|
+
# runtime and is automatically resolved from Dev Container, Compose, or Dockerfile.
|
|
110550
110699
|
runtime: host
|
|
110551
110700
|
environment:
|
|
110552
110701
|
provider: ${environmentProvider}
|
|
@@ -110751,13 +110900,13 @@ var init_init_deps = __esm({
|
|
|
110751
110900
|
tool: "claude",
|
|
110752
110901
|
args: ["--version"],
|
|
110753
110902
|
required: false,
|
|
110754
|
-
hint: `Install the Claude Code CLI to let Claude scaffold ${
|
|
110903
|
+
hint: `Install the Claude Code CLI to let Claude scaffold ${PROJECT_CONFIG_PATH2}`
|
|
110755
110904
|
},
|
|
110756
110905
|
{
|
|
110757
110906
|
tool: "codex",
|
|
110758
110907
|
args: ["--version"],
|
|
110759
110908
|
required: false,
|
|
110760
|
-
hint: `Install the Codex CLI to let Codex scaffold ${
|
|
110909
|
+
hint: `Install the Codex CLI to let Codex scaffold ${PROJECT_CONFIG_PATH2}`
|
|
110761
110910
|
},
|
|
110762
110911
|
{ tool: "docker", args: ["--version"], required: false, hint: "https://docs.docker.com/get-started/get-docker/" }
|
|
110763
110912
|
];
|
|
@@ -110962,7 +111111,7 @@ async function runInit(options) {
|
|
|
110962
111111
|
let selectedAnalyze = resolveInitAnalysis(options, selectedRuntime);
|
|
110963
111112
|
const configExists = existsSync29(taskflowYaml);
|
|
110964
111113
|
if (configExists) {
|
|
110965
|
-
log2.info(`${
|
|
111114
|
+
log2.info(`${PROJECT_CONFIG_PATH2} already exists`);
|
|
110966
111115
|
} else if (needsInteractiveAnalysisFallback(options, selectedRuntime)) {
|
|
110967
111116
|
selectedAnalyze = await selectFallbackAgent();
|
|
110968
111117
|
}
|
|
@@ -110983,7 +111132,7 @@ async function runInit(options) {
|
|
|
110983
111132
|
environmentProvider: detection.selectedRuntime
|
|
110984
111133
|
})
|
|
110985
111134
|
);
|
|
110986
|
-
log2.success(`${
|
|
111135
|
+
log2.success(`${PROJECT_CONFIG_PATH2} created from deterministic detection`);
|
|
110987
111136
|
} else {
|
|
110988
111137
|
const available2 = isInitAgentSelectable(
|
|
110989
111138
|
depStatuses.find((status) => status.tool === selectedAnalyze) ?? { found: false }
|
|
@@ -111006,11 +111155,11 @@ async function runInit(options) {
|
|
|
111006
111155
|
const prompt = buildInitPromptSpec({ ...context, defaultAgent: selectedAnalyze });
|
|
111007
111156
|
const command = buildInitAgentCommand(selectedAnalyze, prompt);
|
|
111008
111157
|
const streamPrinter = createAgentStreamPrinter(label3);
|
|
111009
|
-
log2.step(`Running ${label3} to adapt ${
|
|
111158
|
+
log2.step(`Running ${label3} to adapt ${PROJECT_CONFIG_PATH2}...`);
|
|
111010
111159
|
const result = await runInitAgentCommand(command, gitRoot2, { onEvent: streamPrinter.onEvent });
|
|
111011
111160
|
streamPrinter.finish();
|
|
111012
111161
|
if (!existsSync29(taskflowYaml)) {
|
|
111013
|
-
log2.error(`${label3} removed ${
|
|
111162
|
+
log2.error(`${label3} removed ${PROJECT_CONFIG_PATH2}`);
|
|
111014
111163
|
const details = [
|
|
111015
111164
|
result.summary ? `Summary:
|
|
111016
111165
|
${result.summary}` : "",
|
|
@@ -111027,12 +111176,12 @@ ${result.stderr.trim()}` : ""
|
|
|
111027
111176
|
const finalYaml = await readFile12(taskflowYaml, "utf8");
|
|
111028
111177
|
const changedTemplate = configExists || finalYaml !== starterTemplate;
|
|
111029
111178
|
if (result.exitCode === 0 && changedTemplate) {
|
|
111030
|
-
log2.success(`${label3} adapted ${
|
|
111179
|
+
log2.success(`${label3} adapted ${PROJECT_CONFIG_PATH2}`);
|
|
111031
111180
|
} else if (result.exitCode === 0) {
|
|
111032
111181
|
log2.warning(`${label3} left the starter template unchanged`);
|
|
111033
|
-
log2.warning(`${label3} did not change the starter template. Review ${
|
|
111182
|
+
log2.warning(`${label3} did not change the starter template. Review ${PROJECT_CONFIG_PATH2} manually.`);
|
|
111034
111183
|
} else if (changedTemplate) {
|
|
111035
|
-
log2.warning(`${label3} updated ${
|
|
111184
|
+
log2.warning(`${label3} updated ${PROJECT_CONFIG_PATH2}`);
|
|
111036
111185
|
log2.warning(`${label3} exited with code ${result.exitCode}. Review the generated file before using it.`);
|
|
111037
111186
|
} else {
|
|
111038
111187
|
log2.warning(`${label3} left the starter template in place`);
|
|
@@ -111055,7 +111204,7 @@ ${result.stderr.trim()}` : ""
|
|
|
111055
111204
|
);
|
|
111056
111205
|
outro("You're all set! Next steps:");
|
|
111057
111206
|
console.log();
|
|
111058
|
-
console.log(` 1. Review ${
|
|
111207
|
+
console.log(` 1. Review ${PROJECT_CONFIG_PATH2} and adjust runtime and exposure overrides if needed`);
|
|
111059
111208
|
console.log(" 2. Keep the host daemon running: portta host serve --detach (or portta host service install)");
|
|
111060
111209
|
console.log(` 3. Enable tab completion: eval "$(${flowInvocation()} completion zsh)" (or bash)`);
|
|
111061
111210
|
console.log();
|
|
@@ -111913,7 +112062,7 @@ import { setTimeout as delay4 } from "node:timers/promises";
|
|
|
111913
112062
|
function projectSetupPhaseLabel(phase) {
|
|
111914
112063
|
switch (phase) {
|
|
111915
112064
|
case "creating_config":
|
|
111916
|
-
return `Creating ${
|
|
112065
|
+
return `Creating ${PROJECT_CONFIG_PATH2}`;
|
|
111917
112066
|
case "analyzing":
|
|
111918
112067
|
return "Analyzing project structure";
|
|
111919
112068
|
case "ready":
|
|
@@ -112240,7 +112389,7 @@ async function defaultConfirmPrune(worktreeCount) {
|
|
|
112240
112389
|
}
|
|
112241
112390
|
async function attachToHerdr() {
|
|
112242
112391
|
if (process.env.HERDR_PANE_ID || process.env.HERDR_ENV) return;
|
|
112243
|
-
const result = await
|
|
112392
|
+
const result = await runProcess("herdr", [], { stdio: "inherit", reject: false });
|
|
112244
112393
|
if (result.exitCode !== 0) {
|
|
112245
112394
|
console.error("Warning: failed to attach to herdr");
|
|
112246
112395
|
}
|
|
@@ -112263,12 +112412,12 @@ async function defaultSwitchToSessionWindow(projectDir, _branch, multiplexer) {
|
|
|
112263
112412
|
}
|
|
112264
112413
|
const sessionName = buildProjectSessionName(resolve30(projectDir));
|
|
112265
112414
|
if (process.env.TMUX) {
|
|
112266
|
-
const result = await
|
|
112415
|
+
const result = await runProcess("tmux", ["switch-client", "-t", sessionName], { reject: false });
|
|
112267
112416
|
if (result.exitCode !== 0) {
|
|
112268
112417
|
console.error(`Warning: failed to switch tmux client to ${sessionName}`);
|
|
112269
112418
|
}
|
|
112270
112419
|
} else {
|
|
112271
|
-
const result = await
|
|
112420
|
+
const result = await runProcess("tmux", ["attach-session", "-t", sessionName], { stdio: "inherit", reject: false });
|
|
112272
112421
|
if (result.exitCode !== 0) {
|
|
112273
112422
|
console.error(`Warning: failed to attach to tmux session ${sessionName}`);
|
|
112274
112423
|
}
|
|
@@ -112736,7 +112885,7 @@ function environmentBaseUrlFromControlUrl(value3) {
|
|
|
112736
112885
|
}
|
|
112737
112886
|
}
|
|
112738
112887
|
async function runInherited(invocation2) {
|
|
112739
|
-
const result = await
|
|
112888
|
+
const result = await runProcess(invocation2.command, invocation2.args, {
|
|
112740
112889
|
cwd: invocation2.cwd,
|
|
112741
112890
|
stdio: "inherit",
|
|
112742
112891
|
reject: false
|
|
@@ -112747,7 +112896,7 @@ async function runInherited(invocation2) {
|
|
|
112747
112896
|
async function writeClipboard(value3) {
|
|
112748
112897
|
const command = process.platform === "darwin" ? "pbcopy" : process.platform === "win32" ? "clip" : "xclip";
|
|
112749
112898
|
const args = process.platform === "linux" ? ["-selection", "clipboard"] : [];
|
|
112750
|
-
return (await
|
|
112899
|
+
return (await runProcess(command, args, { input: value3, reject: false })).exitCode === 0;
|
|
112751
112900
|
}
|
|
112752
112901
|
async function runEnvironmentCommand(request2, port, projectDir, stdout2 = console.log, stderr2 = console.error, dependencies = defaultEnvironmentCommandDependencies) {
|
|
112753
112902
|
try {
|
|
@@ -112809,7 +112958,7 @@ async function runEnvironmentCommand(request2, port, projectDir, stdout2 = conso
|
|
|
112809
112958
|
} else {
|
|
112810
112959
|
const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
|
|
112811
112960
|
const openerArgs = process.platform === "win32" ? ["/c", "start", "", endpoint.url] : [endpoint.url];
|
|
112812
|
-
if ((await
|
|
112961
|
+
if ((await runProcess(opener, openerArgs, { reject: false })).exitCode !== 0) {
|
|
112813
112962
|
throw new Error("Browser open command failed");
|
|
112814
112963
|
}
|
|
112815
112964
|
}
|
|
@@ -113074,10 +113223,10 @@ var init_tail = __esm({
|
|
|
113074
113223
|
import { createReadStream as createReadStream2, existsSync as existsSync30 } from "node:fs";
|
|
113075
113224
|
import { readdir as readdir5, readFile as readFile13, realpath as realpath2, stat as stat6, watch } from "node:fs/promises";
|
|
113076
113225
|
import { createServer as createServer2 } from "node:http";
|
|
113077
|
-
import { basename as basename19, dirname as dirname27, extname, join as
|
|
113226
|
+
import { basename as basename19, dirname as dirname27, extname, join as join51, normalize as normalize3, sep as sep2 } from "node:path";
|
|
113078
113227
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
113079
113228
|
function runsDir() {
|
|
113080
|
-
return
|
|
113229
|
+
return join51(dataRoot(), "runs");
|
|
113081
113230
|
}
|
|
113082
113231
|
function parseEvents(text) {
|
|
113083
113232
|
const out = [];
|
|
@@ -113093,7 +113242,7 @@ function parseEvents(text) {
|
|
|
113093
113242
|
}
|
|
113094
113243
|
async function readEvents(runId) {
|
|
113095
113244
|
try {
|
|
113096
|
-
const text = await readFile13(
|
|
113245
|
+
const text = await readFile13(join51(runsDir(), runId, "events.jsonl"), "utf8");
|
|
113097
113246
|
return parseEvents(text);
|
|
113098
113247
|
} catch {
|
|
113099
113248
|
return [];
|
|
@@ -113195,7 +113344,7 @@ function applyDeadman(status, startedAt, lastBeat) {
|
|
|
113195
113344
|
}
|
|
113196
113345
|
async function heartbeatMtime(runId) {
|
|
113197
113346
|
try {
|
|
113198
|
-
return (await stat6(
|
|
113347
|
+
return (await stat6(join51(runsDir(), runId, ".heartbeat"))).mtimeMs;
|
|
113199
113348
|
} catch {
|
|
113200
113349
|
return 0;
|
|
113201
113350
|
}
|
|
@@ -113226,7 +113375,7 @@ async function listRuns() {
|
|
|
113226
113375
|
seen.add(runId);
|
|
113227
113376
|
let eventsStat;
|
|
113228
113377
|
try {
|
|
113229
|
-
eventsStat = await stat6(
|
|
113378
|
+
eventsStat = await stat6(join51(runsDir(), runId, "events.jsonl"));
|
|
113230
113379
|
} catch {
|
|
113231
113380
|
eventsStat = void 0;
|
|
113232
113381
|
}
|
|
@@ -113242,7 +113391,7 @@ async function listRuns() {
|
|
|
113242
113391
|
if (events.length === 0) {
|
|
113243
113392
|
let mtime = 0;
|
|
113244
113393
|
try {
|
|
113245
|
-
mtime = (await stat6(
|
|
113394
|
+
mtime = (await stat6(join51(runsDir(), runId))).mtimeMs;
|
|
113246
113395
|
} catch {
|
|
113247
113396
|
}
|
|
113248
113397
|
summaries.push({ summary: { runId, status: "unknown", agents: 0 }, mtime });
|
|
@@ -113269,7 +113418,7 @@ async function listRuns() {
|
|
|
113269
113418
|
}
|
|
113270
113419
|
function serveStatic(res, fileName) {
|
|
113271
113420
|
const safe2 = normalize3(fileName).replace(/^(\.\.(?:[/\\]|$))+/, "");
|
|
113272
|
-
const full =
|
|
113421
|
+
const full = join51(WEB_DIR, safe2);
|
|
113273
113422
|
if (!full.startsWith(WEB_DIR + sep2) && full !== WEB_DIR) {
|
|
113274
113423
|
sendText(res, 403, "forbidden");
|
|
113275
113424
|
return;
|
|
@@ -113418,10 +113567,10 @@ function sleepUnlessClosed(ms, isClosed) {
|
|
|
113418
113567
|
});
|
|
113419
113568
|
}
|
|
113420
113569
|
function streamRun(req, res, runId) {
|
|
113421
|
-
return tailJsonl(req, res,
|
|
113570
|
+
return tailJsonl(req, res, join51(runsDir(), runId, "events.jsonl"));
|
|
113422
113571
|
}
|
|
113423
113572
|
function streamAgent(req, res, runId, index) {
|
|
113424
|
-
return tailJsonl(req, res,
|
|
113573
|
+
return tailJsonl(req, res, join51(runsDir(), runId, "agents", `${index}.jsonl`));
|
|
113425
113574
|
}
|
|
113426
113575
|
function parseChunks(text) {
|
|
113427
113576
|
const out = [];
|
|
@@ -113437,7 +113586,7 @@ function parseChunks(text) {
|
|
|
113437
113586
|
}
|
|
113438
113587
|
async function readAgentChunks(runId, index) {
|
|
113439
113588
|
try {
|
|
113440
|
-
const text = await readFile13(
|
|
113589
|
+
const text = await readFile13(join51(runsDir(), runId, "agents", `${index}.jsonl`), "utf8");
|
|
113441
113590
|
return parseChunks(text);
|
|
113442
113591
|
} catch {
|
|
113443
113592
|
return null;
|
|
@@ -113501,7 +113650,7 @@ async function handle(req, res) {
|
|
|
113501
113650
|
const events = await readEvents(runId);
|
|
113502
113651
|
if (events.length === 0) {
|
|
113503
113652
|
try {
|
|
113504
|
-
await stat6(
|
|
113653
|
+
await stat6(join51(runsDir(), runId));
|
|
113505
113654
|
} catch {
|
|
113506
113655
|
sendJson(res, 404, { error: "run not found", runId });
|
|
113507
113656
|
return;
|
|
@@ -113578,8 +113727,8 @@ var init_serve = __esm({
|
|
|
113578
113727
|
init_journal();
|
|
113579
113728
|
init_tail();
|
|
113580
113729
|
__dirname = dirname27(fileURLToPath6(import.meta.url));
|
|
113581
|
-
WEB_CANDIDATES = [
|
|
113582
|
-
WEB_DIR = WEB_CANDIDATES.find((path7) => existsSync30(path7)) ??
|
|
113730
|
+
WEB_CANDIDATES = [join51(__dirname, "web"), join51(__dirname, "..", "..", "viewer", "dist")];
|
|
113731
|
+
WEB_DIR = WEB_CANDIDATES.find((path7) => existsSync30(path7)) ?? join51(__dirname, "web");
|
|
113583
113732
|
sseClients = 0;
|
|
113584
113733
|
STALE_MS = 2e4;
|
|
113585
113734
|
summaryCache = /* @__PURE__ */ new Map();
|
|
@@ -113613,15 +113762,15 @@ import { spawn as spawn9 } from "node:child_process";
|
|
|
113613
113762
|
import {
|
|
113614
113763
|
copyFileSync as copyFileSync3,
|
|
113615
113764
|
existsSync as existsSync31,
|
|
113616
|
-
mkdirSync as
|
|
113765
|
+
mkdirSync as mkdirSync18,
|
|
113617
113766
|
readdirSync as readdirSync5,
|
|
113618
|
-
readFileSync as
|
|
113767
|
+
readFileSync as readFileSync26,
|
|
113619
113768
|
realpathSync as realpathSync6,
|
|
113620
113769
|
rmSync as rmSync9,
|
|
113621
113770
|
statSync as statSync10
|
|
113622
113771
|
} from "node:fs";
|
|
113623
113772
|
import { get as httpGet } from "node:http";
|
|
113624
|
-
import { dirname as dirname28, join as
|
|
113773
|
+
import { dirname as dirname28, join as join52, resolve as resolve31, sep as sep3 } from "node:path";
|
|
113625
113774
|
import { fileURLToPath as fileURLToPath7 } from "node:url";
|
|
113626
113775
|
function parseArgs(argv) {
|
|
113627
113776
|
const flags = { _: [] };
|
|
@@ -113725,8 +113874,6 @@ async function main(argv = process.argv.slice(2), options = {}) {
|
|
|
113725
113874
|
return cmdValidate(flags, options.registryRoots);
|
|
113726
113875
|
case "doctor":
|
|
113727
113876
|
return cmdDoctor();
|
|
113728
|
-
case "install-skill":
|
|
113729
|
-
return cmdInstallSkillRetired();
|
|
113730
113877
|
case "guide":
|
|
113731
113878
|
return cmdGuide(options.skillPath);
|
|
113732
113879
|
case void 0:
|
|
@@ -113756,7 +113903,7 @@ async function cmdServe(flags) {
|
|
|
113756
113903
|
}
|
|
113757
113904
|
});
|
|
113758
113905
|
process.stderr.write(
|
|
113759
|
-
`viewer: ${url2} (reading ${
|
|
113906
|
+
`viewer: ${url2} (reading ${join52(dataRoot(), "runs")})
|
|
113760
113907
|
${idleShutdown ? "idle-shutdown on; " : ""}ctrl-c to stop
|
|
113761
113908
|
`
|
|
113762
113909
|
);
|
|
@@ -113816,7 +113963,7 @@ function openBrowser(url2) {
|
|
|
113816
113963
|
}
|
|
113817
113964
|
}
|
|
113818
113965
|
async function cmdRuns(flags) {
|
|
113819
|
-
const dir =
|
|
113966
|
+
const dir = join52(dataRoot(), "runs");
|
|
113820
113967
|
if (!existsSync31(dir)) {
|
|
113821
113968
|
console.log("(no runs yet)");
|
|
113822
113969
|
return;
|
|
@@ -113824,12 +113971,12 @@ async function cmdRuns(flags) {
|
|
|
113824
113971
|
const ids = readdirSync5(dir).filter((d) => d.startsWith("wf_"));
|
|
113825
113972
|
if (flags.prune === true) {
|
|
113826
113973
|
const keep = intFlag(flags, "keep") ?? 50;
|
|
113827
|
-
const byAge = ids.map((id) => ({ id, at: statSync10(
|
|
113974
|
+
const byAge = ids.map((id) => ({ id, at: statSync10(join52(dir, id)).mtimeMs })).sort((a3, b) => b.at - a3.at);
|
|
113828
113975
|
const remove = byAge.slice(keep);
|
|
113829
113976
|
let bytes = 0;
|
|
113830
113977
|
for (const r2 of remove) {
|
|
113831
|
-
bytes += dirSize(
|
|
113832
|
-
rmSync9(
|
|
113978
|
+
bytes += dirSize(join52(dir, r2.id));
|
|
113979
|
+
rmSync9(join52(dir, r2.id), { recursive: true, force: true });
|
|
113833
113980
|
}
|
|
113834
113981
|
console.log(
|
|
113835
113982
|
`pruned ${remove.length} run(s) (${(bytes / 1e6).toFixed(1)} MB); kept ${Math.min(keep, byAge.length)} newest`
|
|
@@ -113840,8 +113987,8 @@ async function cmdRuns(flags) {
|
|
|
113840
113987
|
const stale2 = ids.filter((id) => isStaleRun(dir, id));
|
|
113841
113988
|
let bytes = 0;
|
|
113842
113989
|
for (const id of stale2) {
|
|
113843
|
-
bytes += dirSize(
|
|
113844
|
-
rmSync9(
|
|
113990
|
+
bytes += dirSize(join52(dir, id));
|
|
113991
|
+
rmSync9(join52(dir, id), { recursive: true, force: true });
|
|
113845
113992
|
}
|
|
113846
113993
|
console.log(`pruned ${stale2.length} stale run(s) (${(bytes / 1e6).toFixed(1)} MB)`);
|
|
113847
113994
|
return;
|
|
@@ -113850,7 +113997,7 @@ async function cmdRuns(flags) {
|
|
|
113850
113997
|
const loaded = Journal.load(id);
|
|
113851
113998
|
const name = loaded.meta?.workflowFile?.split("/").pop() ?? "";
|
|
113852
113999
|
const agents = loaded.results.size;
|
|
113853
|
-
const resultPath =
|
|
114000
|
+
const resultPath = join52(dir, id, "result.json");
|
|
113854
114001
|
const done = existsSync31(resultPath);
|
|
113855
114002
|
const status = done ? "completed" : isStaleRun(dir, id) ? "stale" : "?";
|
|
113856
114003
|
return { id, name, agents, status, at: loaded.meta?.createdAt ?? 0 };
|
|
@@ -113859,10 +114006,10 @@ async function cmdRuns(flags) {
|
|
|
113859
114006
|
for (const r2 of rows) console.log(`${r2.id} ${r2.status.padEnd(10)} ${String(r2.agents).padStart(3)} agents ${r2.name}`);
|
|
113860
114007
|
}
|
|
113861
114008
|
function isStaleRun(runsBase, id) {
|
|
113862
|
-
const rd =
|
|
114009
|
+
const rd = join52(runsBase, id);
|
|
113863
114010
|
let lastRunStatus;
|
|
113864
114011
|
try {
|
|
113865
|
-
const lines =
|
|
114012
|
+
const lines = readFileSync26(join52(rd, "events.jsonl"), "utf8").trim().split("\n");
|
|
113866
114013
|
for (let i3 = lines.length - 1; i3 >= 0; i3--) {
|
|
113867
114014
|
const m = lines[i3]?.match(/"type":"run","status":"([a-z]+)"/);
|
|
113868
114015
|
if (m) {
|
|
@@ -113876,7 +114023,7 @@ function isStaleRun(runsBase, id) {
|
|
|
113876
114023
|
if (lastRunStatus !== "started") return false;
|
|
113877
114024
|
let beat = 0;
|
|
113878
114025
|
try {
|
|
113879
|
-
beat = statSync10(
|
|
114026
|
+
beat = statSync10(join52(rd, ".heartbeat")).mtimeMs;
|
|
113880
114027
|
} catch {
|
|
113881
114028
|
}
|
|
113882
114029
|
return Date.now() - beat > 2e4;
|
|
@@ -113884,7 +114031,7 @@ function isStaleRun(runsBase, id) {
|
|
|
113884
114031
|
function dirSize(dir) {
|
|
113885
114032
|
let total = 0;
|
|
113886
114033
|
for (const e of readdirSync5(dir, { withFileTypes: true })) {
|
|
113887
|
-
const p =
|
|
114034
|
+
const p = join52(dir, e.name);
|
|
113888
114035
|
total += e.isDirectory() ? dirSize(p) : statSync10(p).size;
|
|
113889
114036
|
}
|
|
113890
114037
|
return total;
|
|
@@ -113900,7 +114047,7 @@ async function cmdRun(flags, registryRoots) {
|
|
|
113900
114047
|
}
|
|
113901
114048
|
const resolvedFile = resolveFileOrName(file2, registryRoots);
|
|
113902
114049
|
const overrides = {};
|
|
113903
|
-
const provider = enumFlag(flags, "provider",
|
|
114050
|
+
const provider = enumFlag(flags, "provider", PROVIDERS);
|
|
113904
114051
|
if (provider) overrides.provider = provider;
|
|
113905
114052
|
const model = str(flags.model);
|
|
113906
114053
|
if (model) overrides.model = model;
|
|
@@ -113929,7 +114076,7 @@ async function cmdRun(flags, registryRoots) {
|
|
|
113929
114076
|
if (argsFile) {
|
|
113930
114077
|
let raw2;
|
|
113931
114078
|
try {
|
|
113932
|
-
raw2 =
|
|
114079
|
+
raw2 = readFileSync26(resolve31(argsFile), "utf8");
|
|
113933
114080
|
} catch {
|
|
113934
114081
|
throw new UsageError2(`--args-file not found: ${argsFile}`);
|
|
113935
114082
|
}
|
|
@@ -113993,7 +114140,7 @@ async function cmdValidate(flags, registryRoots) {
|
|
|
113993
114140
|
process.exitCode = 1;
|
|
113994
114141
|
return;
|
|
113995
114142
|
}
|
|
113996
|
-
const source =
|
|
114143
|
+
const source = readFileSync26(resolveFileOrName(file2, registryRoots), "utf8");
|
|
113997
114144
|
const { meta: meta3 } = parseWorkflow(source);
|
|
113998
114145
|
console.log(`ok: "${meta3.name}" \u2014 ${meta3.description}`);
|
|
113999
114146
|
if (meta3.phases) console.log(`phases: ${meta3.phases.map((p) => p.title).join(" \u2192 ")}`);
|
|
@@ -114031,20 +114178,20 @@ async function cmdSave(flags) {
|
|
|
114031
114178
|
}
|
|
114032
114179
|
const absFile = resolve31(file2);
|
|
114033
114180
|
if (!existsSync31(absFile)) throw new UsageError2(`file not found: ${file2}`);
|
|
114034
|
-
const { meta: meta3 } = parseWorkflow(
|
|
114181
|
+
const { meta: meta3 } = parseWorkflow(readFileSync26(absFile, "utf8"));
|
|
114035
114182
|
const name = meta3.name;
|
|
114036
114183
|
if (name.includes("/") || name.includes("\\") || name.includes("..") || name.startsWith(".") || name.includes("\0")) {
|
|
114037
114184
|
throw new UsageError2(`meta.name is not a safe filename: "${name}"`);
|
|
114038
114185
|
}
|
|
114039
114186
|
const useProject = flags.project === true;
|
|
114040
114187
|
const destDir = useProject ? projectPaths(process.cwd()).workflows : globalPaths().workflows;
|
|
114041
|
-
const dest =
|
|
114188
|
+
const dest = join52(destDir, `${name}.workflow.js`);
|
|
114042
114189
|
if (existsSync31(dest) && flags.force !== true) {
|
|
114043
114190
|
throw new UsageError2(
|
|
114044
114191
|
`${useProject ? "project" : "user"} workflow "${name}" already exists at ${dest} \u2014 use --force to overwrite`
|
|
114045
114192
|
);
|
|
114046
114193
|
}
|
|
114047
|
-
|
|
114194
|
+
mkdirSync18(destDir, { recursive: true });
|
|
114048
114195
|
copyFileSync3(absFile, dest);
|
|
114049
114196
|
console.log(`saved "${name}" \u2192 ${dest}`);
|
|
114050
114197
|
}
|
|
@@ -114075,7 +114222,7 @@ async function cmdDoctor() {
|
|
|
114075
114222
|
const codexBin = process.env.CODEX_BIN ?? "codex";
|
|
114076
114223
|
const opencodeBin = process.env.OPENCODE_BIN ?? "opencode";
|
|
114077
114224
|
const piBin = process.env.PI_BIN ?? "pi";
|
|
114078
|
-
const piScratch = mkdtempSync4(
|
|
114225
|
+
const piScratch = mkdtempSync4(join52(tmpdir6(), `${APP_SLUG}-doctor-pi-`));
|
|
114079
114226
|
let piOut;
|
|
114080
114227
|
try {
|
|
114081
114228
|
piOut = check4(piBin, ["--version"], { env: { ...process.env, PI_CODING_AGENT_DIR: piScratch }, cwd: tmpdir6() });
|
|
@@ -114093,31 +114240,20 @@ async function cmdDoctor() {
|
|
|
114093
114240
|
console.log(` data dir : ${dataRoot()}`);
|
|
114094
114241
|
}
|
|
114095
114242
|
function readSkill(skillPath) {
|
|
114096
|
-
if (skillPath) return
|
|
114243
|
+
if (skillPath) return readFileSync26(skillPath, "utf8");
|
|
114097
114244
|
const here = dirname28(fileURLToPath7(import.meta.url));
|
|
114098
114245
|
const candidates = [
|
|
114099
|
-
|
|
114100
|
-
|
|
114101
|
-
|
|
114246
|
+
join52(here, "..", "skills", "portta-workflows", "SKILL.md"),
|
|
114247
|
+
join52(here, "..", "..", "..", "skills", "portta-workflows", "SKILL.md"),
|
|
114248
|
+
join52(here, "..", "..", "..", "..", "..", "..", "skills", "portta-workflows", "SKILL.md")
|
|
114102
114249
|
];
|
|
114103
114250
|
const src = candidates.find((candidate) => existsSync31(candidate));
|
|
114104
114251
|
if (!src) throw new Error(`skill source not found at ${candidates.join(" or ")}`);
|
|
114105
|
-
return
|
|
114252
|
+
return readFileSync26(src, "utf8");
|
|
114106
114253
|
}
|
|
114107
114254
|
async function cmdGuide(skillPath) {
|
|
114108
114255
|
process.stdout.write(readSkill(skillPath).replace(/^---\n[\s\S]*?\n---\n+/, ""));
|
|
114109
114256
|
}
|
|
114110
|
-
function cmdInstallSkillRetired() {
|
|
114111
|
-
console.error(
|
|
114112
|
-
`${CLI_NAME} workflows install-skill has been retired. Install the skill with the Skills CLI instead:
|
|
114113
|
-
|
|
114114
|
-
npx skills add codions-labs/portta --skill portta-workflows -g
|
|
114115
|
-
|
|
114116
|
-
It supports Claude Code, Codex, OpenCode, Cursor, Zed and others, symlinks by default,
|
|
114117
|
-
and \`npx skills update\` keeps every agent current. See https://www.skills.sh.`
|
|
114118
|
-
);
|
|
114119
|
-
process.exitCode = 1;
|
|
114120
|
-
}
|
|
114121
114257
|
function printHelp() {
|
|
114122
114258
|
console.log(`${APP_NAME} workflows \u2014 run JS workflow files that orchestrate coding agents
|
|
114123
114259
|
|
|
@@ -114178,7 +114314,7 @@ function invokedDirectly() {
|
|
|
114178
114314
|
return false;
|
|
114179
114315
|
}
|
|
114180
114316
|
}
|
|
114181
|
-
var UsageError2, BOOLEAN_FLAGS,
|
|
114317
|
+
var UsageError2, BOOLEAN_FLAGS, PROVIDERS, SANDBOXES, EFFORTS;
|
|
114182
114318
|
var init_cli = __esm({
|
|
114183
114319
|
"../host/src/modules/taskflow/workflows/cli.ts"() {
|
|
114184
114320
|
"use strict";
|
|
@@ -114213,7 +114349,7 @@ var init_cli = __esm({
|
|
|
114213
114349
|
"project",
|
|
114214
114350
|
"force"
|
|
114215
114351
|
]);
|
|
114216
|
-
|
|
114352
|
+
PROVIDERS = PROVIDER_IDS;
|
|
114217
114353
|
SANDBOXES = ["read-only", "workspace-write", "danger-full-access"];
|
|
114218
114354
|
EFFORTS = ["none", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
114219
114355
|
if (invokedDirectly()) {
|
|
@@ -116305,7 +116441,7 @@ var RemovalPreview = named(
|
|
|
116305
116441
|
"RemovalPreview"
|
|
116306
116442
|
);
|
|
116307
116443
|
var LiveEventKind = named(
|
|
116308
|
-
external_exports.enum(["container", "network", "bridge", "health", "project", "config", "hello", "
|
|
116444
|
+
external_exports.enum(["container", "network", "bridge", "health", "project", "config", "hello", "session", "activity", "repository"]),
|
|
116309
116445
|
"LiveEventKind"
|
|
116310
116446
|
);
|
|
116311
116447
|
var LiveEvent = named(
|
|
@@ -117341,131 +117477,7 @@ var {
|
|
|
117341
117477
|
|
|
117342
117478
|
// src/cli.ts
|
|
117343
117479
|
init_errors3();
|
|
117344
|
-
|
|
117345
|
-
// src/process.ts
|
|
117346
|
-
init_execa();
|
|
117347
|
-
import { spawn as spawn2 } from "node:child_process";
|
|
117348
|
-
import { closeSync as closeSync2, openSync as openSync2 } from "node:fs";
|
|
117349
|
-
var reporting = {};
|
|
117350
|
-
function setProcessReporter(options) {
|
|
117351
|
-
reporting = options;
|
|
117352
|
-
}
|
|
117353
|
-
function effectiveStdio(requested) {
|
|
117354
|
-
if (requested === "inherit") return "inherit";
|
|
117355
|
-
if (reporting.quiet) return "pipe";
|
|
117356
|
-
if (reporting.verbose) return "stream";
|
|
117357
|
-
return requested;
|
|
117358
|
-
}
|
|
117359
|
-
function outputTargets(mode) {
|
|
117360
|
-
if (mode !== "stream") return { stdio: mode };
|
|
117361
|
-
return {
|
|
117362
|
-
stdin: "pipe",
|
|
117363
|
-
stdout: reporting.json ? "pipe" : ["pipe", "inherit"],
|
|
117364
|
-
stderr: ["pipe", "inherit"]
|
|
117365
|
-
};
|
|
117366
|
-
}
|
|
117367
|
-
var HEARTBEAT_FIRST_MS = 1e4;
|
|
117368
|
-
var HEARTBEAT_EVERY_MS = 3e4;
|
|
117369
|
-
var HEARTBEAT_HINT_MS = 18e4;
|
|
117370
|
-
function elapsed(sinceMs) {
|
|
117371
|
-
const seconds = Math.round((Date.now() - sinceMs) / 1e3);
|
|
117372
|
-
if (seconds < 60) return `${seconds}s`;
|
|
117373
|
-
return `${Math.floor(seconds / 60)}m${String(seconds % 60).padStart(2, "0")}s`;
|
|
117374
|
-
}
|
|
117375
|
-
function describeCommand(file2, args, limit = 88) {
|
|
117376
|
-
const words = [];
|
|
117377
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
117378
|
-
const argument = args[index] ?? "";
|
|
117379
|
-
if (argument === "-f" || argument === "--file") {
|
|
117380
|
-
index += 1;
|
|
117381
|
-
continue;
|
|
117382
|
-
}
|
|
117383
|
-
words.push(argument);
|
|
117384
|
-
}
|
|
117385
|
-
const line4 = [file2, ...words].join(" ");
|
|
117386
|
-
return line4.length <= limit ? line4 : `${line4.slice(0, limit - 1)}\u2026`;
|
|
117387
|
-
}
|
|
117388
|
-
function startHeartbeat(file2, args) {
|
|
117389
|
-
if (reporting.quiet) return () => {
|
|
117390
|
-
};
|
|
117391
|
-
const started = Date.now();
|
|
117392
|
-
const label3 = describeCommand(file2, args);
|
|
117393
|
-
let hinted = false;
|
|
117394
|
-
const announce = () => {
|
|
117395
|
-
process.stderr.write(`wait still running: ${label3} (${elapsed(started)})
|
|
117396
|
-
`);
|
|
117397
|
-
if (!hinted && Date.now() - started >= HEARTBEAT_HINT_MS) {
|
|
117398
|
-
hinted = true;
|
|
117399
|
-
process.stderr.write(" -> a first build of the panel image takes several minutes; Ctrl-C is safe, and --verbose shows its output\n");
|
|
117400
|
-
}
|
|
117401
|
-
};
|
|
117402
|
-
let repeat;
|
|
117403
|
-
const first = setTimeout(() => {
|
|
117404
|
-
announce();
|
|
117405
|
-
repeat = setInterval(announce, HEARTBEAT_EVERY_MS);
|
|
117406
|
-
repeat.unref?.();
|
|
117407
|
-
}, HEARTBEAT_FIRST_MS);
|
|
117408
|
-
first.unref?.();
|
|
117409
|
-
return () => {
|
|
117410
|
-
clearTimeout(first);
|
|
117411
|
-
if (repeat) clearInterval(repeat);
|
|
117412
|
-
};
|
|
117413
|
-
}
|
|
117414
|
-
async function runProcess(file2, args = [], options = {}) {
|
|
117415
|
-
const mode = effectiveStdio(options.stdio ?? "pipe");
|
|
117416
|
-
const execaOptions = {
|
|
117417
|
-
cwd: options.cwd,
|
|
117418
|
-
env: options.env,
|
|
117419
|
-
input: options.input,
|
|
117420
|
-
reject: options.reject ?? true,
|
|
117421
|
-
shell: false,
|
|
117422
|
-
timeout: options.timeout,
|
|
117423
|
-
...outputTargets(mode)
|
|
117424
|
-
};
|
|
117425
|
-
const stopHeartbeat = mode === "pipe" ? startHeartbeat(file2, args) : () => {
|
|
117426
|
-
};
|
|
117427
|
-
try {
|
|
117428
|
-
const result = await execa(file2, [...args], execaOptions);
|
|
117429
|
-
return {
|
|
117430
|
-
stdout: typeof result.stdout === "string" ? result.stdout : "",
|
|
117431
|
-
stderr: typeof result.stderr === "string" ? result.stderr : "",
|
|
117432
|
-
exitCode: result.exitCode ?? (result.failed ? 1 : 0),
|
|
117433
|
-
failed: result.failed
|
|
117434
|
-
};
|
|
117435
|
-
} finally {
|
|
117436
|
-
stopHeartbeat();
|
|
117437
|
-
}
|
|
117438
|
-
}
|
|
117439
|
-
var PROCESS_POLICY = Object.freeze({ shell: false, argumentArraysOnly: true });
|
|
117440
|
-
async function runForeground(file2, args, options = {}) {
|
|
117441
|
-
const child = execa(file2, [...args], { cwd: options.cwd, env: options.env, stdio: "inherit", reject: false, shell: false });
|
|
117442
|
-
const forwarded = ["SIGINT", "SIGTERM", "SIGHUP"].map((signal) => [signal, () => {
|
|
117443
|
-
child.kill(signal);
|
|
117444
|
-
}]);
|
|
117445
|
-
for (const [signal, forward] of forwarded) process.on(signal, forward);
|
|
117446
|
-
try {
|
|
117447
|
-
const result = await child;
|
|
117448
|
-
return result.exitCode ?? (result.signal ? 1 : 0);
|
|
117449
|
-
} finally {
|
|
117450
|
-
for (const [signal, forward] of forwarded) process.off(signal, forward);
|
|
117451
|
-
}
|
|
117452
|
-
}
|
|
117453
|
-
function spawnDetached(file2, args, options = {}) {
|
|
117454
|
-
const log3 = options.logFile ? openSync2(options.logFile, "a", 384) : null;
|
|
117455
|
-
try {
|
|
117456
|
-
const child = spawn2(file2, [...args], {
|
|
117457
|
-
cwd: options.cwd,
|
|
117458
|
-
env: options.env,
|
|
117459
|
-
shell: false,
|
|
117460
|
-
detached: true,
|
|
117461
|
-
stdio: log3 === null ? "ignore" : ["ignore", log3, log3]
|
|
117462
|
-
});
|
|
117463
|
-
child.unref();
|
|
117464
|
-
return child.pid;
|
|
117465
|
-
} finally {
|
|
117466
|
-
if (log3 !== null) closeSync2(log3);
|
|
117467
|
-
}
|
|
117468
|
-
}
|
|
117480
|
+
init_process();
|
|
117469
117481
|
|
|
117470
117482
|
// src/commands/access.ts
|
|
117471
117483
|
init_src();
|
|
@@ -117493,6 +117505,7 @@ init_context();
|
|
|
117493
117505
|
// src/docker.ts
|
|
117494
117506
|
init_zod();
|
|
117495
117507
|
init_errors3();
|
|
117508
|
+
init_process();
|
|
117496
117509
|
var Identifier = /^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,127}$/;
|
|
117497
117510
|
function identifier(value3, kind = "Docker identifier") {
|
|
117498
117511
|
if (!Identifier.test(value3)) throw new UsageError(`invalid ${kind}: ${value3}`);
|
|
@@ -117510,12 +117523,10 @@ var InspectSchema = external_exports.object({
|
|
|
117510
117523
|
Ports: external_exports.record(external_exports.string(), external_exports.array(external_exports.object({ HostIp: external_exports.string().optional(), HostPort: external_exports.string().optional() })).nullable()).nullable().optional()
|
|
117511
117524
|
}).passthrough()
|
|
117512
117525
|
}).passthrough();
|
|
117513
|
-
async function dockerAvailable() {
|
|
117514
|
-
const result = await runProcess("docker", ["info"], { reject: false });
|
|
117515
|
-
return result.exitCode === 0;
|
|
117516
|
-
}
|
|
117517
117526
|
async function requireDocker() {
|
|
117518
|
-
|
|
117527
|
+
const result = await runProcess("docker", ["info"], { reject: false });
|
|
117528
|
+
if (result.error) throw new PreconditionError("the docker CLI is not installed", "install Docker Engine 24 or newer; Portta never installs Docker or invokes a system package manager");
|
|
117529
|
+
if (result.exitCode !== 0) throw new PreconditionError("cannot talk to the Docker daemon", "start Docker or check DOCKER_HOST");
|
|
117519
117530
|
}
|
|
117520
117531
|
async function dockerOperatingSystem() {
|
|
117521
117532
|
const result = await runProcess("docker", ["info", "--format", "{{.OperatingSystem}}"], { reject: false, timeout: 8e3 });
|
|
@@ -117585,6 +117596,7 @@ async function isManagedContainer(id) {
|
|
|
117585
117596
|
|
|
117586
117597
|
// src/commands/access.ts
|
|
117587
117598
|
init_errors3();
|
|
117599
|
+
init_process();
|
|
117588
117600
|
function globals(command) {
|
|
117589
117601
|
return command.optsWithGlobals();
|
|
117590
117602
|
}
|
|
@@ -117764,21 +117776,50 @@ async function serviceUnpublish(alias, options, command) {
|
|
|
117764
117776
|
}
|
|
117765
117777
|
|
|
117766
117778
|
// src/commands/clients.ts
|
|
117767
|
-
import { copyFileSync as copyFileSync2, existsSync as existsSync22, mkdirSync as
|
|
117768
|
-
import { basename as basename13, dirname as dirname22, join as
|
|
117779
|
+
import { copyFileSync as copyFileSync2, existsSync as existsSync22, mkdirSync as mkdirSync15, rmSync as rmSync5, statSync as statSync9 } from "node:fs";
|
|
117780
|
+
import { basename as basename13, dirname as dirname22, join as join46 } from "node:path";
|
|
117769
117781
|
|
|
117770
117782
|
// src/commands/web.ts
|
|
117771
117783
|
init_src();
|
|
117772
117784
|
init_src();
|
|
117773
117785
|
init_context();
|
|
117774
|
-
import {
|
|
117775
|
-
import { join as
|
|
117786
|
+
import { mkdirSync as mkdirSync14 } from "node:fs";
|
|
117787
|
+
import { join as join45 } from "node:path";
|
|
117776
117788
|
init_errors3();
|
|
117789
|
+
init_process();
|
|
117790
|
+
|
|
117791
|
+
// src/installation-directories.ts
|
|
117792
|
+
import { chmodSync as chmodSync4, existsSync as existsSync5, mkdirSync as mkdirSync4 } from "node:fs";
|
|
117793
|
+
import { join as join6 } from "node:path";
|
|
117794
|
+
var INSTALLATION_DIRECTORIES = [
|
|
117795
|
+
{ path: "config/traefik/dynamic", mode: 493 },
|
|
117796
|
+
{ path: "config/tls", mode: 493 },
|
|
117797
|
+
{ path: "state/auth", mode: 448 },
|
|
117798
|
+
{ path: "state/ssh", mode: 448 },
|
|
117799
|
+
{ path: "state/runner", mode: 448 },
|
|
117800
|
+
{ path: "state/cloudflared", mode: 448 },
|
|
117801
|
+
{ path: "state/traefik/acme", mode: 448 },
|
|
117802
|
+
{ path: "state/access", mode: 493 },
|
|
117803
|
+
{ path: "state/tailscale", mode: 493 },
|
|
117804
|
+
{ path: "state/git", mode: 493 },
|
|
117805
|
+
{ path: "state/metrics", mode: 493 },
|
|
117806
|
+
{ path: "state/environment", mode: 493 },
|
|
117807
|
+
{ path: "state/logs", mode: 493 }
|
|
117808
|
+
];
|
|
117809
|
+
function ensureInstallationDirectories(root2) {
|
|
117810
|
+
for (const { path: path7, mode } of INSTALLATION_DIRECTORIES) {
|
|
117811
|
+
const full = join6(root2, path7);
|
|
117812
|
+
const created = !existsSync5(full);
|
|
117813
|
+
mkdirSync4(full, { recursive: true });
|
|
117814
|
+
if (created || mode === 448) chmodSync4(full, mode);
|
|
117815
|
+
}
|
|
117816
|
+
}
|
|
117777
117817
|
|
|
117778
117818
|
// src/local-release.ts
|
|
117779
117819
|
init_src();
|
|
117780
117820
|
init_errors3();
|
|
117781
|
-
|
|
117821
|
+
init_process();
|
|
117822
|
+
import { join as join7 } from "node:path";
|
|
117782
117823
|
async function requireLocalRelease(context) {
|
|
117783
117824
|
if (context.env["PORTTA_LOCAL_RELEASE"] !== "true") return;
|
|
117784
117825
|
const missing = [];
|
|
@@ -117792,7 +117833,7 @@ async function requireLocalRelease(context) {
|
|
|
117792
117833
|
}
|
|
117793
117834
|
function selectLocalRelease(context) {
|
|
117794
117835
|
const image = porttaImages(context.version).runtime;
|
|
117795
|
-
patchEnvFile(
|
|
117836
|
+
patchEnvFile(join7(context.root, ".env"), {
|
|
117796
117837
|
PORTTA_AUTH_IMAGE: image,
|
|
117797
117838
|
PORTTA_WEB_IMAGE: image,
|
|
117798
117839
|
PORTTA_WEB_BUILD: "false",
|
|
@@ -117803,13 +117844,15 @@ function selectLocalRelease(context) {
|
|
|
117803
117844
|
// src/commands/repos.ts
|
|
117804
117845
|
init_src();
|
|
117805
117846
|
import { createHash as createHash2 } from "node:crypto";
|
|
117806
|
-
import { chmodSync as
|
|
117807
|
-
import { basename, join as
|
|
117847
|
+
import { chmodSync as chmodSync5, existsSync as existsSync7, mkdirSync as mkdirSync5, readdirSync, readFileSync as readFileSync8, realpathSync, statSync as statSync2, unlinkSync as unlinkSync4, writeFileSync as writeFileSync5 } from "node:fs";
|
|
117848
|
+
import { basename, join as join8 } from "node:path";
|
|
117808
117849
|
init_context();
|
|
117809
117850
|
init_errors3();
|
|
117851
|
+
init_process();
|
|
117810
117852
|
|
|
117811
117853
|
// src/commands/git.ts
|
|
117812
|
-
|
|
117854
|
+
init_process();
|
|
117855
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
117813
117856
|
async function git(cwd, args) {
|
|
117814
117857
|
const result = await runProcess("git", ["-C", cwd, ...args], { reject: false });
|
|
117815
117858
|
return result.exitCode === 0 ? result.stdout.trim() : null;
|
|
@@ -117860,7 +117903,7 @@ function normalizePulls(raw2) {
|
|
|
117860
117903
|
async function collectGitProject(project2, path7, declaredRemote = "", withPrs = false, cachedForge) {
|
|
117861
117904
|
const collectedAt = Math.floor(Date.now() / 1e3);
|
|
117862
117905
|
const base2 = { project: project2, workingDir: path7, collectedAt };
|
|
117863
|
-
if (!await git(path7, ["rev-parse", "--is-inside-work-tree"])) return { ...base2, git: null, reason:
|
|
117906
|
+
if (!await git(path7, ["rev-parse", "--is-inside-work-tree"])) return { ...base2, git: null, reason: existsSync6(path7) ? "not a git repository" : "directory not readable from the host" };
|
|
117864
117907
|
const branchName = await git(path7, ["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
117865
117908
|
const detached = branchName === "HEAD";
|
|
117866
117909
|
const sha = await git(path7, ["rev-parse", "HEAD"]) ?? "";
|
|
@@ -117982,7 +118025,7 @@ async function findCandidates(options, env, registered = []) {
|
|
|
117982
118025
|
}
|
|
117983
118026
|
if (!options.environment && !options.path) {
|
|
117984
118027
|
for (const path7 of registered) {
|
|
117985
|
-
if (!
|
|
118028
|
+
if (!existsSync7(path7)) continue;
|
|
117986
118029
|
const root2 = await gitRootOf(path7);
|
|
117987
118030
|
if (root2) add(root2);
|
|
117988
118031
|
}
|
|
@@ -117996,7 +118039,7 @@ async function findCandidates(options, env, registered = []) {
|
|
|
117996
118039
|
home2 = null;
|
|
117997
118040
|
}
|
|
117998
118041
|
}
|
|
117999
|
-
if (home2 && !options.environment && !options.path &&
|
|
118042
|
+
if (home2 && !options.environment && !options.path && existsSync7(home2)) {
|
|
118000
118043
|
let entries = [];
|
|
118001
118044
|
try {
|
|
118002
118045
|
entries = readdirSync(home2);
|
|
@@ -118005,9 +118048,9 @@ async function findCandidates(options, env, registered = []) {
|
|
|
118005
118048
|
}
|
|
118006
118049
|
for (const entry of entries) {
|
|
118007
118050
|
if (!firstLevelCandidateName(entry)) continue;
|
|
118008
|
-
const path7 =
|
|
118051
|
+
const path7 = join8(home2, entry);
|
|
118009
118052
|
if (!isDirectory(path7)) continue;
|
|
118010
|
-
if (
|
|
118053
|
+
if (existsSync7(join8(path7, ".git"))) {
|
|
118011
118054
|
const root2 = await gitRootOf(path7);
|
|
118012
118055
|
if (root2) add(root2);
|
|
118013
118056
|
continue;
|
|
@@ -118020,8 +118063,8 @@ async function findCandidates(options, env, registered = []) {
|
|
|
118020
118063
|
}
|
|
118021
118064
|
for (const child of children) {
|
|
118022
118065
|
if (!firstLevelCandidateName(child)) continue;
|
|
118023
|
-
const childPath =
|
|
118024
|
-
if (!isDirectory(childPath) || !
|
|
118066
|
+
const childPath = join8(path7, child);
|
|
118067
|
+
if (!isDirectory(childPath) || !existsSync7(join8(childPath, ".git"))) continue;
|
|
118025
118068
|
const root2 = await gitRootOf(childPath);
|
|
118026
118069
|
if (root2) add(root2);
|
|
118027
118070
|
}
|
|
@@ -118042,8 +118085,8 @@ function sha256(content) {
|
|
|
118042
118085
|
async function collectInstructions(root2) {
|
|
118043
118086
|
const paths3 = [...INSTRUCTION_FILES];
|
|
118044
118087
|
for (const { directory, extension } of INSTRUCTION_DIRECTORIES) {
|
|
118045
|
-
const dir =
|
|
118046
|
-
if (!
|
|
118088
|
+
const dir = join8(root2, directory);
|
|
118089
|
+
if (!existsSync7(dir)) continue;
|
|
118047
118090
|
try {
|
|
118048
118091
|
for (const entry of readdirSync(dir).sort()) {
|
|
118049
118092
|
if (entry.endsWith(extension)) paths3.push(`${directory}/${entry}`);
|
|
@@ -118054,8 +118097,8 @@ async function collectInstructions(root2) {
|
|
|
118054
118097
|
const files = [];
|
|
118055
118098
|
for (const relative6 of paths3) {
|
|
118056
118099
|
if (!isInstructionPath(relative6)) continue;
|
|
118057
|
-
const absolute =
|
|
118058
|
-
if (!
|
|
118100
|
+
const absolute = join8(root2, relative6);
|
|
118101
|
+
if (!existsSync7(absolute)) continue;
|
|
118059
118102
|
let size;
|
|
118060
118103
|
let modifiedAt;
|
|
118061
118104
|
try {
|
|
@@ -118104,7 +118147,7 @@ async function collectRepository(candidate, options, cachedForge) {
|
|
|
118104
118147
|
function writeSecret(path7, value3) {
|
|
118105
118148
|
writeFileSync5(path7, `${JSON.stringify(value3, null, 2)}
|
|
118106
118149
|
`, { mode: 384 });
|
|
118107
|
-
|
|
118150
|
+
chmodSync5(path7, 384);
|
|
118108
118151
|
}
|
|
118109
118152
|
function readJson(path7) {
|
|
118110
118153
|
try {
|
|
@@ -118116,18 +118159,18 @@ function readJson(path7) {
|
|
|
118116
118159
|
async function scanRepositories(options = {}, profile, output2 = new Output({ quiet: true })) {
|
|
118117
118160
|
if (options.forgeTtl !== void 0 && !/^\d+$/.test(options.forgeTtl)) throw new UsageError("--forge-ttl must be a number of seconds");
|
|
118118
118161
|
const context = gatewayContext({ profile });
|
|
118119
|
-
const directory =
|
|
118120
|
-
|
|
118162
|
+
const directory = join8(context.root, "state/git");
|
|
118163
|
+
mkdirSync5(directory, { recursive: true, mode: 448 });
|
|
118121
118164
|
const partial2 = Boolean(options.environment || options.path);
|
|
118122
118165
|
const registered = partial2 ? [] : await registeredRepositoryPaths(context, (line4) => output2.detail(line4));
|
|
118123
118166
|
const { candidates, home: home2 } = await findCandidates(options, context.env, registered);
|
|
118124
118167
|
if (candidates.size > 0) {
|
|
118125
|
-
output2.progress(`scanning ${candidates.size} ${candidates.size === 1 ? "repository" : "repositories"}${home2 &&
|
|
118168
|
+
output2.progress(`scanning ${candidates.size} ${candidates.size === 1 ? "repository" : "repositories"}${home2 && existsSync7(home2) ? ` under ${home2}` : ""}`);
|
|
118126
118169
|
}
|
|
118127
118170
|
const forgeTtl = Number(options.forgeTtl ?? 300);
|
|
118128
118171
|
const now = Math.floor(Date.now() / 1e3);
|
|
118129
118172
|
const homeReal = home2 ? safeRealpath(home2) : null;
|
|
118130
|
-
const previous = partial2 ? readJson(
|
|
118173
|
+
const previous = partial2 ? readJson(join8(directory, SCAN_INDEX_FILE)) : null;
|
|
118131
118174
|
const entries = /* @__PURE__ */ new Map();
|
|
118132
118175
|
const environments = {};
|
|
118133
118176
|
if (previous && previous.version === SCAN_VERSION) {
|
|
@@ -118137,9 +118180,9 @@ async function scanRepositories(options = {}, profile, output2 = new Output({ qu
|
|
|
118137
118180
|
const repositories = [];
|
|
118138
118181
|
for (const candidate of candidates.values()) {
|
|
118139
118182
|
const key = repositoryKey(candidate.root);
|
|
118140
|
-
const target =
|
|
118183
|
+
const target = join8(directory, `${key}.json`);
|
|
118141
118184
|
let cachedForge;
|
|
118142
|
-
if (options.withPrs && forgeTtl > 0 &&
|
|
118185
|
+
if (options.withPrs && forgeTtl > 0 && existsSync7(target)) {
|
|
118143
118186
|
const forge = readJson(target)?.["forge"];
|
|
118144
118187
|
if (forge && now - Number(forge["collectedAt"] ?? 0) < forgeTtl) cachedForge = forge;
|
|
118145
118188
|
}
|
|
@@ -118168,7 +118211,7 @@ async function scanRepositories(options = {}, profile, output2 = new Output({ qu
|
|
|
118168
118211
|
repositories: [...entries.values()].sort((a3, b) => a3.name.localeCompare(b.name)),
|
|
118169
118212
|
environments
|
|
118170
118213
|
};
|
|
118171
|
-
writeSecret(
|
|
118214
|
+
writeSecret(join8(directory, SCAN_INDEX_FILE), index);
|
|
118172
118215
|
return { index, repositories };
|
|
118173
118216
|
}
|
|
118174
118217
|
async function reposScan(options, command) {
|
|
@@ -118178,8 +118221,8 @@ async function reposScan(options, command) {
|
|
|
118178
118221
|
else output2.progress(`scanned ${result.repositories.length} repositor${result.repositories.length === 1 ? "y" : "ies"}`);
|
|
118179
118222
|
}
|
|
118180
118223
|
function readScanIndex(root2) {
|
|
118181
|
-
const path7 =
|
|
118182
|
-
if (!
|
|
118224
|
+
const path7 = join8(root2, "state/git", SCAN_INDEX_FILE);
|
|
118225
|
+
if (!existsSync7(path7)) return null;
|
|
118183
118226
|
const value3 = readJson(path7);
|
|
118184
118227
|
if (value3 === null) throw new CliError(`${path7} is not readable JSON`, void 0, "run `portta repos scan` to rewrite it");
|
|
118185
118228
|
return value3["version"] === SCAN_VERSION ? value3 : null;
|
|
@@ -118190,7 +118233,7 @@ async function reposStatus(command) {
|
|
|
118190
118233
|
const output2 = new Output(globals2(command));
|
|
118191
118234
|
const now = Math.floor(Date.now() / 1e3);
|
|
118192
118235
|
const repositories = (index?.repositories ?? []).map((entry) => {
|
|
118193
|
-
const scan = readJson(
|
|
118236
|
+
const scan = readJson(join8(context.root, "state/git", `${entry.key}.json`));
|
|
118194
118237
|
const gitInfo = scan?.["git"] ?? null;
|
|
118195
118238
|
return {
|
|
118196
118239
|
...entry,
|
|
@@ -118214,12 +118257,12 @@ async function reposStatus(command) {
|
|
|
118214
118257
|
}
|
|
118215
118258
|
async function reposClear(command) {
|
|
118216
118259
|
const context = gatewayContext({ profile: globals2(command).profile });
|
|
118217
|
-
const directory =
|
|
118218
|
-
if (!
|
|
118260
|
+
const directory = join8(context.root, "state/git");
|
|
118261
|
+
if (!existsSync7(directory)) return;
|
|
118219
118262
|
let count2 = 0;
|
|
118220
118263
|
for (const file2 of readdirSync(directory)) {
|
|
118221
118264
|
if (file2.endsWith(".json")) {
|
|
118222
|
-
unlinkSync4(
|
|
118265
|
+
unlinkSync4(join8(directory, file2));
|
|
118223
118266
|
count2++;
|
|
118224
118267
|
}
|
|
118225
118268
|
}
|
|
@@ -118237,9 +118280,11 @@ async function refreshRepositories(profile, output2) {
|
|
|
118237
118280
|
init_src();
|
|
118238
118281
|
init_src2();
|
|
118239
118282
|
init_context();
|
|
118283
|
+
init_version();
|
|
118240
118284
|
init_errors3();
|
|
118285
|
+
init_process();
|
|
118241
118286
|
import { existsSync as existsSync21 } from "node:fs";
|
|
118242
|
-
import { join as
|
|
118287
|
+
import { join as join44 } from "node:path";
|
|
118243
118288
|
|
|
118244
118289
|
// src/metrics/collect.ts
|
|
118245
118290
|
init_src();
|
|
@@ -118248,46 +118293,46 @@ import { loadavg } from "node:os";
|
|
|
118248
118293
|
|
|
118249
118294
|
// src/metrics/store.ts
|
|
118250
118295
|
init_src();
|
|
118251
|
-
import { chmodSync as
|
|
118296
|
+
import { chmodSync as chmodSync8, existsSync as existsSync16, mkdirSync as mkdirSync13, readFileSync as readFileSync17, renameSync as renameSync4, statSync as statSync7, writeFileSync as writeFileSync11 } from "node:fs";
|
|
118252
118297
|
import { dirname as dirname21 } from "node:path";
|
|
118253
118298
|
import { randomUUID as randomUUID8 } from "node:crypto";
|
|
118254
118299
|
|
|
118255
118300
|
// src/metrics/paths.ts
|
|
118256
|
-
import { join as
|
|
118301
|
+
import { join as join39 } from "node:path";
|
|
118257
118302
|
function metricsDir(root2) {
|
|
118258
|
-
return
|
|
118303
|
+
return join39(root2, "state/metrics");
|
|
118259
118304
|
}
|
|
118260
118305
|
function currentFile(root2) {
|
|
118261
|
-
return
|
|
118306
|
+
return join39(metricsDir(root2), "current.json");
|
|
118262
118307
|
}
|
|
118263
118308
|
function historyFile(root2) {
|
|
118264
|
-
return
|
|
118309
|
+
return join39(metricsDir(root2), "history.jsonl");
|
|
118265
118310
|
}
|
|
118266
118311
|
function instanceFile(root2) {
|
|
118267
|
-
return
|
|
118312
|
+
return join39(metricsDir(root2), "instance.json");
|
|
118268
118313
|
}
|
|
118269
118314
|
function pidFile(root2) {
|
|
118270
|
-
return
|
|
118315
|
+
return join39(metricsDir(root2), "collector.pid");
|
|
118271
118316
|
}
|
|
118272
118317
|
function logFile(root2) {
|
|
118273
|
-
return
|
|
118318
|
+
return join39(root2, "state/logs/host-metrics.log");
|
|
118274
118319
|
}
|
|
118275
118320
|
function environmentReportFile(root2) {
|
|
118276
|
-
return
|
|
118321
|
+
return join39(root2, "state/environment/report.json");
|
|
118277
118322
|
}
|
|
118278
118323
|
|
|
118279
118324
|
// src/metrics/store.ts
|
|
118280
118325
|
var LOG_LIMIT_BYTES = 256 * 1024;
|
|
118281
118326
|
function writeAtomic2(path7, text) {
|
|
118282
|
-
|
|
118327
|
+
mkdirSync13(dirname21(path7), { recursive: true });
|
|
118283
118328
|
const temporary = `${path7}.${process.pid}.tmp`;
|
|
118284
118329
|
writeFileSync11(temporary, text, { mode: 384 });
|
|
118285
118330
|
renameSync4(temporary, path7);
|
|
118286
|
-
|
|
118331
|
+
chmodSync8(path7, 384);
|
|
118287
118332
|
}
|
|
118288
118333
|
function loadInstance(root2, hostname4) {
|
|
118289
118334
|
const path7 = instanceFile(root2);
|
|
118290
|
-
if (
|
|
118335
|
+
if (existsSync16(path7)) {
|
|
118291
118336
|
try {
|
|
118292
118337
|
const parsed = JSON.parse(readFileSync17(path7, "utf8"));
|
|
118293
118338
|
if (typeof parsed.id === "string" && parsed.id !== "") {
|
|
@@ -118317,14 +118362,14 @@ function writeCurrent(root2, snapshot) {
|
|
|
118317
118362
|
}
|
|
118318
118363
|
function appendHistory(root2, snapshot) {
|
|
118319
118364
|
const path7 = historyFile(root2);
|
|
118320
|
-
|
|
118321
|
-
const existing =
|
|
118365
|
+
mkdirSync13(dirname21(path7), { recursive: true });
|
|
118366
|
+
const existing = existsSync16(path7) ? readFileSync17(path7, "utf8") : "";
|
|
118322
118367
|
const merged = mergeHistoryLines(existing, historyPointFrom(snapshot), snapshot.collectedAt);
|
|
118323
118368
|
writeAtomic2(path7, merged);
|
|
118324
118369
|
}
|
|
118325
118370
|
function readCurrent(root2) {
|
|
118326
118371
|
const path7 = currentFile(root2);
|
|
118327
|
-
if (!
|
|
118372
|
+
if (!existsSync16(path7)) return null;
|
|
118328
118373
|
try {
|
|
118329
118374
|
return JSON.parse(readFileSync17(path7, "utf8"));
|
|
118330
118375
|
} catch {
|
|
@@ -118333,8 +118378,8 @@ function readCurrent(root2) {
|
|
|
118333
118378
|
}
|
|
118334
118379
|
function appendLog(root2, line4) {
|
|
118335
118380
|
const path7 = logFile(root2);
|
|
118336
|
-
|
|
118337
|
-
if (
|
|
118381
|
+
mkdirSync13(dirname21(path7), { recursive: true });
|
|
118382
|
+
if (existsSync16(path7) && statSync7(path7).size > LOG_LIMIT_BYTES) {
|
|
118338
118383
|
renameSync4(path7, `${path7}.old`);
|
|
118339
118384
|
}
|
|
118340
118385
|
writeFileSync11(path7, `${(/* @__PURE__ */ new Date()).toISOString()} ${line4}
|
|
@@ -118437,17 +118482,18 @@ async function collectSnapshot(root2, now = Date.now()) {
|
|
|
118437
118482
|
|
|
118438
118483
|
// src/metrics/lifecycle.ts
|
|
118439
118484
|
init_src();
|
|
118440
|
-
import { existsSync as existsSync20, readFileSync as
|
|
118485
|
+
import { existsSync as existsSync20, readFileSync as readFileSync20, unlinkSync as unlinkSync5 } from "node:fs";
|
|
118486
|
+
init_process();
|
|
118441
118487
|
|
|
118442
118488
|
// src/environment/index.ts
|
|
118443
118489
|
init_src();
|
|
118444
118490
|
import { homedir as homedir8 } from "node:os";
|
|
118445
118491
|
|
|
118446
118492
|
// src/host.ts
|
|
118447
|
-
|
|
118448
|
-
import { homedir as homedir7
|
|
118449
|
-
import { accessSync as accessSync3, constants as constants7,
|
|
118450
|
-
import { join as
|
|
118493
|
+
init_process();
|
|
118494
|
+
import { homedir as homedir7 } from "node:os";
|
|
118495
|
+
import { accessSync as accessSync3, constants as constants7, globSync, statSync as statSync8 } from "node:fs";
|
|
118496
|
+
import { join as join40 } from "node:path";
|
|
118451
118497
|
function isTailscaleRange(address) {
|
|
118452
118498
|
const [a3, b] = address.split(".").map(Number);
|
|
118453
118499
|
return a3 === 100 && b !== void 0 && b >= 64 && b <= 127;
|
|
@@ -118476,19 +118522,19 @@ async function locate(tool) {
|
|
|
118476
118522
|
if (!onPath.failed && direct) return direct;
|
|
118477
118523
|
const home2 = homedir7();
|
|
118478
118524
|
for (const candidate of [
|
|
118479
|
-
|
|
118480
|
-
|
|
118481
|
-
|
|
118482
|
-
|
|
118483
|
-
|
|
118484
|
-
|
|
118485
|
-
|
|
118525
|
+
join40(home2, ".local/bin", tool),
|
|
118526
|
+
join40(home2, ".bun/bin", tool),
|
|
118527
|
+
join40(home2, ".cargo/bin", tool),
|
|
118528
|
+
join40(home2, ".deno/bin", tool),
|
|
118529
|
+
join40("/usr/local/bin", tool),
|
|
118530
|
+
join40("/opt/homebrew/bin", tool),
|
|
118531
|
+
join40(home2, ".volta/bin", tool)
|
|
118486
118532
|
]) {
|
|
118487
118533
|
if (executable(candidate)) return candidate;
|
|
118488
118534
|
}
|
|
118489
118535
|
for (const pattern of [
|
|
118490
|
-
|
|
118491
|
-
|
|
118536
|
+
join40(home2, ".nvm/versions/node/*/bin", tool),
|
|
118537
|
+
join40(home2, ".local/share/fnm/node-versions/*/installation/bin", tool)
|
|
118492
118538
|
]) {
|
|
118493
118539
|
for (const match2 of globSync(pattern)) if (executable(match2)) return match2;
|
|
118494
118540
|
}
|
|
@@ -118502,6 +118548,9 @@ function fileMode(path7) {
|
|
|
118502
118548
|
}
|
|
118503
118549
|
}
|
|
118504
118550
|
|
|
118551
|
+
// src/environment/index.ts
|
|
118552
|
+
init_process();
|
|
118553
|
+
|
|
118505
118554
|
// src/environment/agents.ts
|
|
118506
118555
|
init_src();
|
|
118507
118556
|
|
|
@@ -118704,7 +118753,7 @@ ${daemon.stdout}`);
|
|
|
118704
118753
|
// src/environment/ssh.ts
|
|
118705
118754
|
init_src();
|
|
118706
118755
|
import { existsSync as existsSync17 } from "node:fs";
|
|
118707
|
-
import { join as
|
|
118756
|
+
import { join as join41 } from "node:path";
|
|
118708
118757
|
var SSH_ID = "tools.ssh";
|
|
118709
118758
|
var TMUX_ID = "tools.tmux";
|
|
118710
118759
|
var sshProbe = {
|
|
@@ -118720,7 +118769,7 @@ var sshProbe = {
|
|
|
118720
118769
|
fix: "install an OpenSSH client for remote and repository workflows"
|
|
118721
118770
|
})];
|
|
118722
118771
|
}
|
|
118723
|
-
const directory =
|
|
118772
|
+
const directory = join41(context.homeDir, ".ssh");
|
|
118724
118773
|
const mode = context.fileMode(directory);
|
|
118725
118774
|
const agent = context.environment["SSH_AUTH_SOCK"] ? await context.run("ssh-add", ["-l"], { reject: false, timeout: PROBE_TIMEOUT_MS2 }) : null;
|
|
118726
118775
|
const details = [
|
|
@@ -118803,12 +118852,12 @@ function writeEnvironmentReport(root2, report) {
|
|
|
118803
118852
|
// src/environment/security/index.ts
|
|
118804
118853
|
init_src();
|
|
118805
118854
|
import { platform as platform2 } from "node:os";
|
|
118806
|
-
import { existsSync as existsSync19, readFileSync as
|
|
118807
|
-
import { join as
|
|
118855
|
+
import { existsSync as existsSync19, readFileSync as readFileSync19 } from "node:fs";
|
|
118856
|
+
import { join as join42 } from "node:path";
|
|
118808
118857
|
|
|
118809
118858
|
// src/environment/security/probes.ts
|
|
118810
118859
|
init_src();
|
|
118811
|
-
import { existsSync as existsSync18, readFileSync as
|
|
118860
|
+
import { existsSync as existsSync18, readFileSync as readFileSync18 } from "node:fs";
|
|
118812
118861
|
function emptySshd(state3, reason) {
|
|
118813
118862
|
return { state: state3, reason, source: null, port: null, permitRootLogin: null, passwordAuthentication: null, pubkeyAuthentication: null, usePam: null };
|
|
118814
118863
|
}
|
|
@@ -118826,7 +118875,7 @@ ${result.stdout}`);
|
|
|
118826
118875
|
for (const path7 of ["/etc/ssh/sshd_config", "/private/etc/ssh/sshd_config"]) {
|
|
118827
118876
|
if (!existsSync18(path7)) continue;
|
|
118828
118877
|
try {
|
|
118829
|
-
const parsed = parseSshdConfig(
|
|
118878
|
+
const parsed = parseSshdConfig(readFileSync18(path7, "utf8"), "file");
|
|
118830
118879
|
return parsed.state === "present" ? parsed : emptySshd("unknown", "the SSH configuration file could not be interpreted");
|
|
118831
118880
|
} catch {
|
|
118832
118881
|
return emptySshd("unknown", "could not be checked: this user cannot read the SSH configuration");
|
|
@@ -118877,7 +118926,7 @@ ${jail.stderr}`);
|
|
|
118877
118926
|
// src/environment/security/index.ts
|
|
118878
118927
|
function hostSecurityContext(root2, profileOverride) {
|
|
118879
118928
|
const snapshot = readCurrent(root2);
|
|
118880
|
-
const file2 = existsSync19(
|
|
118929
|
+
const file2 = existsSync19(join42(root2, ".env")) ? parseEnv(readFileSync19(join42(root2, ".env"), "utf8")) : /* @__PURE__ */ new Map();
|
|
118881
118930
|
const environment = mergeEnvironment(file2, process.env);
|
|
118882
118931
|
const profile = profileOverride ?? environment["PORTTA_PROFILE"] ?? "local";
|
|
118883
118932
|
return {
|
|
@@ -118897,7 +118946,7 @@ async function collectHostSecurityFacts(context, securityContext) {
|
|
|
118897
118946
|
}
|
|
118898
118947
|
async function collectHostSecurityChecks(root2, probeContext = defaultProbeContext(), securityContext = hostSecurityContext(root2)) {
|
|
118899
118948
|
const checks = securityVerdicts(await collectHostSecurityFacts(probeContext, securityContext), securityContext);
|
|
118900
|
-
const sshDirectory =
|
|
118949
|
+
const sshDirectory = join42(root2, "state/ssh");
|
|
118901
118950
|
const mode = probeContext.fileMode(sshDirectory);
|
|
118902
118951
|
checks.push({
|
|
118903
118952
|
...check2(
|
|
@@ -118925,9 +118974,9 @@ async function collectHostSecurityReport(root2, probeContext = defaultProbeConte
|
|
|
118925
118974
|
}
|
|
118926
118975
|
|
|
118927
118976
|
// src/environment/security/store.ts
|
|
118928
|
-
import { join as
|
|
118977
|
+
import { join as join43 } from "node:path";
|
|
118929
118978
|
function securityReportFile(root2) {
|
|
118930
|
-
return
|
|
118979
|
+
return join43(root2, "state/environment/security.json");
|
|
118931
118980
|
}
|
|
118932
118981
|
function writeHostSecurityReport(root2, report) {
|
|
118933
118982
|
const path7 = securityReportFile(root2);
|
|
@@ -118940,7 +118989,7 @@ function writeHostSecurityReport(root2, report) {
|
|
|
118940
118989
|
function readCollectorPid(root2) {
|
|
118941
118990
|
const path7 = pidFile(root2);
|
|
118942
118991
|
if (!existsSync20(path7)) return null;
|
|
118943
|
-
const parsed = Number(
|
|
118992
|
+
const parsed = Number(readFileSync20(path7, "utf8").trim());
|
|
118944
118993
|
return Number.isInteger(parsed) && parsed > 0 ? parsed : null;
|
|
118945
118994
|
}
|
|
118946
118995
|
function pidAlive(pid) {
|
|
@@ -118953,7 +119002,7 @@ function pidAlive(pid) {
|
|
|
118953
119002
|
}
|
|
118954
119003
|
function commandLine(pid) {
|
|
118955
119004
|
try {
|
|
118956
|
-
return
|
|
119005
|
+
return readFileSync20(`/proc/${pid}/cmdline`, "utf8").replace(/\0/g, " ");
|
|
118957
119006
|
} catch {
|
|
118958
119007
|
return "";
|
|
118959
119008
|
}
|
|
@@ -119168,10 +119217,10 @@ function ensureHostToken(context) {
|
|
|
119168
119217
|
return file2;
|
|
119169
119218
|
}
|
|
119170
119219
|
function hostDaemonEntry(directory = import.meta.dirname) {
|
|
119171
|
-
return
|
|
119220
|
+
return join44(directory, "host.js");
|
|
119172
119221
|
}
|
|
119173
119222
|
function hostDaemonLog(stateDir) {
|
|
119174
|
-
return
|
|
119223
|
+
return join44(stateDir, "daemon.log");
|
|
119175
119224
|
}
|
|
119176
119225
|
async function hostAnswers(listen) {
|
|
119177
119226
|
const host2 = listen.host === "0.0.0.0" || listen.host === "::" ? "127.0.0.1" : listen.host;
|
|
@@ -119186,7 +119235,9 @@ async function hostServe(command) {
|
|
|
119186
119235
|
const options = globals3(command);
|
|
119187
119236
|
const context = gatewayContext({ profile: options.profile });
|
|
119188
119237
|
const entry = hostDaemonEntry();
|
|
119189
|
-
if (!existsSync21(entry))
|
|
119238
|
+
if (!existsSync21(entry)) {
|
|
119239
|
+
throw new PreconditionError(`the host daemon is not built: ${entry}`, CLI_VERSION === LOCAL_DEV_VERSION ? "npm run build --workspace=@codions/portta" : `the copy under ${join44(context.root, "bin")} serves the applier only; run this with the CLI from npm: npm install -g @codions/portta`);
|
|
119240
|
+
}
|
|
119190
119241
|
const stateDir = resolveHostStateDir(context.root, context.env);
|
|
119191
119242
|
const listen = hostListen(context.env);
|
|
119192
119243
|
const output2 = new Output(options);
|
|
@@ -119213,16 +119264,16 @@ function globals4(command) {
|
|
|
119213
119264
|
return command.optsWithGlobals();
|
|
119214
119265
|
}
|
|
119215
119266
|
function setValues(root2, values) {
|
|
119216
|
-
const path7 =
|
|
119267
|
+
const path7 = join45(root2, ".env");
|
|
119217
119268
|
patchEnvFile(path7, values);
|
|
119218
119269
|
}
|
|
119219
119270
|
function syncForwardAuth(root2) {
|
|
119220
|
-
const storePath =
|
|
119271
|
+
const storePath = join45(root2, "state/auth/protections.json");
|
|
119221
119272
|
const store = readProtectionStore(storePath);
|
|
119222
119273
|
writeProtectionStore(storePath, store);
|
|
119223
|
-
const dynamic =
|
|
119224
|
-
|
|
119225
|
-
writeEnvFile(
|
|
119274
|
+
const dynamic = join45(root2, "config/traefik/dynamic");
|
|
119275
|
+
mkdirSync14(dynamic, { recursive: true });
|
|
119276
|
+
writeEnvFile(join45(dynamic, "portta-auth.yaml"), renderAuthDynamic(store));
|
|
119226
119277
|
}
|
|
119227
119278
|
var PANEL_OVERRIDES = { PORTTA_WEB: "true" };
|
|
119228
119279
|
async function webCompose(command, args, extraEnv = {}, stdio = "inherit") {
|
|
@@ -119230,7 +119281,7 @@ async function webCompose(command, args, extraEnv = {}, stdio = "inherit") {
|
|
|
119230
119281
|
return runProcess("docker", ["compose", ...composeArguments(context), ...args], { cwd: context.root, env: { ...context.env, ...extraEnv }, stdio });
|
|
119231
119282
|
}
|
|
119232
119283
|
function prepareWebUp(options, command) {
|
|
119233
|
-
prepareEnvFile(
|
|
119284
|
+
prepareEnvFile(join45(gatewayContext({ profile: globals4(command).profile }).root, ".env"));
|
|
119234
119285
|
if (options.localRelease) selectLocalRelease(gatewayContext({ profile: globals4(command).profile }));
|
|
119235
119286
|
const initial = gatewayContext({ profile: globals4(command).profile });
|
|
119236
119287
|
const expose = options.expose ?? initial.config.webExpose;
|
|
@@ -119282,16 +119333,7 @@ function prepareWebUp(options, command) {
|
|
|
119282
119333
|
options.port ? String(Number(options.port)) : String(initial.config.webPort)
|
|
119283
119334
|
);
|
|
119284
119335
|
setValues(initial.root, values);
|
|
119285
|
-
|
|
119286
|
-
mkdirSync13(join44(initial.root, "state/github"), { recursive: true });
|
|
119287
|
-
mkdirSync13(join44(initial.root, "state/metrics"), { recursive: true });
|
|
119288
|
-
mkdirSync13(join44(initial.root, "state/environment"), { recursive: true });
|
|
119289
|
-
mkdirSync13(join44(initial.root, "state/logs"), { recursive: true });
|
|
119290
|
-
mkdirSync13(join44(initial.root, "state/auth"), { recursive: true, mode: 448 });
|
|
119291
|
-
chmodSync8(join44(initial.root, "state/auth"), 448);
|
|
119292
|
-
mkdirSync13(join44(initial.root, "state/ssh"), { recursive: true, mode: 448 });
|
|
119293
|
-
chmodSync8(join44(initial.root, "state/ssh"), 448);
|
|
119294
|
-
mkdirSync13(join44(initial.root, "config/traefik/dynamic"), { recursive: true });
|
|
119336
|
+
ensureInstallationDirectories(initial.root);
|
|
119295
119337
|
syncForwardAuth(initial.root);
|
|
119296
119338
|
const context = gatewayContext({ profile: globals4(command).profile, overrides: values });
|
|
119297
119339
|
const output2 = new Output(globals4(command));
|
|
@@ -119307,7 +119349,7 @@ async function webUp(options, command) {
|
|
|
119307
119349
|
ensureHostToken(context);
|
|
119308
119350
|
output2.progress("pulling alpine/socat:1.8.1.3");
|
|
119309
119351
|
await runProcess("docker", ["pull", "alpine/socat:1.8.1.3"], { reject: false, stdio: "stream" });
|
|
119310
|
-
|
|
119352
|
+
mkdirSync14(join45(context.root, "state", "panel"), { recursive: true });
|
|
119311
119353
|
const services = ["portta-auth", "web", "web-socket-proxy"];
|
|
119312
119354
|
const buildArgs = context.config.webDev || context.config.webBuild ? ["--build"] : [];
|
|
119313
119355
|
output2.progress(buildArgs.length > 0 ? `starting ${services.join(", ")}, building the panel image` : `starting ${services.join(", ")}`);
|
|
@@ -119505,6 +119547,7 @@ async function webOpen(command) {
|
|
|
119505
119547
|
// src/commands/clients.ts
|
|
119506
119548
|
init_context();
|
|
119507
119549
|
init_errors3();
|
|
119550
|
+
init_process();
|
|
119508
119551
|
init_src();
|
|
119509
119552
|
function globals5(command) {
|
|
119510
119553
|
return command.optsWithGlobals();
|
|
@@ -119514,7 +119557,7 @@ async function ensureToolbox(command) {
|
|
|
119514
119557
|
const image = porttaImages(context.version).toolbox;
|
|
119515
119558
|
const present = await runProcess("docker", ["image", "inspect", image], { reject: false });
|
|
119516
119559
|
if (present.exitCode === 0) return image;
|
|
119517
|
-
await runProcess("docker", ["build", "-q", "--build-arg", `PORTTA_VERSION=${context.version}`, "-t", image,
|
|
119560
|
+
await runProcess("docker", ["build", "-q", "--build-arg", `PORTTA_VERSION=${context.version}`, "-t", image, join46(context.root, "docker", "images", "toolbox")], { stdio: "inherit" });
|
|
119518
119561
|
return image;
|
|
119519
119562
|
}
|
|
119520
119563
|
async function containerEnvironment(id) {
|
|
@@ -119645,7 +119688,7 @@ async function dbRestore(file2, command) {
|
|
|
119645
119688
|
);
|
|
119646
119689
|
}
|
|
119647
119690
|
await confirm(`replace ${path7} with ${file2}? The current panel data is discarded.`, globals5(command).yes === true);
|
|
119648
|
-
|
|
119691
|
+
mkdirSync15(dirname22(path7), { recursive: true });
|
|
119649
119692
|
for (const candidate of databaseFiles(path7)) rmSync5(candidate, { force: true });
|
|
119650
119693
|
copyFileSync2(file2, path7);
|
|
119651
119694
|
new Output(globals5(command)).progress(`panel database restored from ${file2}`);
|
|
@@ -119653,12 +119696,14 @@ async function dbRestore(file2, command) {
|
|
|
119653
119696
|
|
|
119654
119697
|
// src/commands/projects.ts
|
|
119655
119698
|
init_src();
|
|
119699
|
+
var import_yaml2 = __toESM(require_dist(), 1);
|
|
119656
119700
|
init_zod();
|
|
119657
|
-
import { existsSync as existsSync23, mkdirSync as
|
|
119701
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync16, readFileSync as readFileSync21, realpathSync as realpathSync4, renameSync as renameSync5, rmSync as rmSync6, writeFileSync as writeFileSync12 } from "node:fs";
|
|
119658
119702
|
import { createHash as createHash15 } from "node:crypto";
|
|
119659
|
-
import { basename as basename14, dirname as dirname23, join as
|
|
119703
|
+
import { basename as basename14, dirname as dirname23, join as join47, resolve as resolve24 } from "node:path";
|
|
119660
119704
|
init_context();
|
|
119661
119705
|
init_errors3();
|
|
119706
|
+
init_process();
|
|
119662
119707
|
function globals6(command) {
|
|
119663
119708
|
return command.optsWithGlobals();
|
|
119664
119709
|
}
|
|
@@ -119769,7 +119814,7 @@ function labelsOf(service2) {
|
|
|
119769
119814
|
return Object.fromEntries(Object.entries(service2.labels ?? {}).map(([key, value3]) => [key, String(value3)]));
|
|
119770
119815
|
}
|
|
119771
119816
|
function composeCandidates(path7) {
|
|
119772
|
-
return ["compose.yaml", "compose.yml", "docker-compose.yaml", "docker-compose.yml"].map((file2) =>
|
|
119817
|
+
return ["compose.yaml", "compose.yml", "docker-compose.yaml", "docker-compose.yml"].map((file2) => join47(path7, file2)).filter(existsSync23);
|
|
119773
119818
|
}
|
|
119774
119819
|
async function hostContainers() {
|
|
119775
119820
|
try {
|
|
@@ -119787,12 +119832,27 @@ function safeRealpath2(path7) {
|
|
|
119787
119832
|
}
|
|
119788
119833
|
function declaredComposeName(file2) {
|
|
119789
119834
|
try {
|
|
119790
|
-
const match2 = /^name:[ \t]*["']?([^"'\s#]+)/m.exec(
|
|
119835
|
+
const match2 = /^name:[ \t]*["']?([^"'\s#]+)/m.exec(readFileSync21(file2, "utf8"));
|
|
119791
119836
|
return match2?.[1] ?? null;
|
|
119792
119837
|
} catch {
|
|
119793
119838
|
return null;
|
|
119794
119839
|
}
|
|
119795
119840
|
}
|
|
119841
|
+
function readProjectConfig(root2) {
|
|
119842
|
+
const path7 = join47(root2, PROJECT_CONFIG_PATH);
|
|
119843
|
+
if (!existsSync23(path7)) {
|
|
119844
|
+
return { source: "defaults", path: PROJECT_CONFIG_PATH, config: defaultProjectConfig(), issues: [] };
|
|
119845
|
+
}
|
|
119846
|
+
let document;
|
|
119847
|
+
try {
|
|
119848
|
+
document = (0, import_yaml2.parse)(readFileSync21(path7, "utf8"));
|
|
119849
|
+
} catch (error61) {
|
|
119850
|
+
const message = error61 instanceof Error ? error61.message : String(error61);
|
|
119851
|
+
return { source: "file", path: PROJECT_CONFIG_PATH, config: null, issues: [{ path: "", message }] };
|
|
119852
|
+
}
|
|
119853
|
+
const parsed = parseProjectConfig(document);
|
|
119854
|
+
return parsed.ok ? { source: "file", path: PROJECT_CONFIG_PATH, config: parsed.config, issues: [] } : { source: "file", path: PROJECT_CONFIG_PATH, config: null, issues: parsed.issues };
|
|
119855
|
+
}
|
|
119796
119856
|
async function analyze(pathValue, options = {}, containers) {
|
|
119797
119857
|
const base2 = resolve24(pathValue);
|
|
119798
119858
|
let composeFile2;
|
|
@@ -119808,8 +119868,8 @@ async function analyze(pathValue, options = {}, containers) {
|
|
|
119808
119868
|
let result = await runProcess("docker", ["compose", "-f", composeFile2, "config", "--format", "json"], { cwd: path7, reject: false });
|
|
119809
119869
|
if (result.exitCode !== 0) result = await runProcess("docker", ["compose", "-f", composeFile2, "config", "--format", "json", "--no-interpolate"], { cwd: path7 });
|
|
119810
119870
|
const model = ComposeSchema.parse(JSON.parse(result.stdout));
|
|
119811
|
-
const overlay2 = ["compose.portta.yaml", "compose.portta.yml"].map((file2) =>
|
|
119812
|
-
const projectFromEnv = existsSync23(
|
|
119871
|
+
const overlay2 = ["compose.portta.yaml", "compose.portta.yml"].map((file2) => join47(".portta", file2)).find((file2) => existsSync23(join47(path7, file2))) ?? ["compose.portta.yaml", "compose.portta.yml"].find((file2) => existsSync23(join47(path7, file2))) ?? null;
|
|
119872
|
+
const projectFromEnv = existsSync23(join47(path7, ".env")) ? parseEnv(readFileSync21(join47(path7, ".env"), "utf8")).get("COMPOSE_PROJECT_NAME") : void 0;
|
|
119813
119873
|
const declaredName = declaredComposeName(composeFile2);
|
|
119814
119874
|
const project2 = projectFromEnv || composeNamespace(declaredName ?? model.name ?? basename14(path7));
|
|
119815
119875
|
const services = Object.entries(model.services).map(([name, service2]) => ({
|
|
@@ -119843,6 +119903,7 @@ async function analyze(pathValue, options = {}, containers) {
|
|
|
119843
119903
|
path: path7,
|
|
119844
119904
|
compose_file: basename14(composeFile2),
|
|
119845
119905
|
gateway_overlay: overlay2,
|
|
119906
|
+
project_config: readProjectConfig(path7),
|
|
119846
119907
|
project: { name: project2, source: projectFromEnv ? ".env" : declaredName ? "compose name:" : "directory name (implicit)" },
|
|
119847
119908
|
domain: gatewayContext({ required: false }).config.domain,
|
|
119848
119909
|
services,
|
|
@@ -119874,6 +119935,19 @@ async function analyzeCommand(path7, options, command) {
|
|
|
119874
119935
|
output2.line(` project namespace ${report.project.name} (${report.project.source})`);
|
|
119875
119936
|
const identities = [...new Set(report.services.flatMap((service2) => Object.entries(service2.labels).filter(([key]) => key.startsWith("portta.project") || key.startsWith("portta.repo") || key.startsWith("portta.git.root")).map(([key, value3]) => `${key}=${value3}`)))];
|
|
119876
119937
|
output2.line(` declared identity ${identities.length ? identities.join(" ") : "none (inferred from the Compose labels)"}`);
|
|
119938
|
+
const declared = report.project_config;
|
|
119939
|
+
if (declared.issues.length) {
|
|
119940
|
+
output2.line(` project.yaml invalid, so its defaults are not applied:`);
|
|
119941
|
+
for (const issue2 of declared.issues) output2.line(` ${issue2.path ? `${issue2.path}: ` : ""}${issue2.message}`);
|
|
119942
|
+
} else if (declared.config) {
|
|
119943
|
+
const summary = [
|
|
119944
|
+
declared.config.name ? `name ${declared.config.name}` : null,
|
|
119945
|
+
declared.config.mainBranch ? `main branch ${declared.config.mainBranch}` : null,
|
|
119946
|
+
`worktrees ${declared.config.worktrees.root}`,
|
|
119947
|
+
`branches ${declared.config.worktrees.branchPattern}`
|
|
119948
|
+
].filter((part) => part !== null);
|
|
119949
|
+
output2.line(` project.yaml ${declared.source === "file" ? summary.join(", ") : `absent, so defaults apply: ${summary.join(", ")}`}`);
|
|
119950
|
+
}
|
|
119877
119951
|
output2.line("\nServices");
|
|
119878
119952
|
for (const service2 of report.services) output2.line(` ${service2.name} ${service2.image ?? "<built>"} ${service2.kind} ${service2.published_ports.map((port) => `${port.host}:${port.container}`).join(",") || "-"}`);
|
|
119879
119953
|
output2.line("\nFindings");
|
|
@@ -119972,7 +120046,7 @@ async function initCommand(pathValue, options, command) {
|
|
|
119972
120046
|
}));
|
|
119973
120047
|
const content = renderOverlay(report.project.name, overlayServices, context.config.network, options.project);
|
|
119974
120048
|
const runtimeId = createHash15("sha256").update(safeRealpath2(report.path)).digest("hex").slice(0, 16);
|
|
119975
|
-
const destination = options.output ? resolve24(report.path, options.output) :
|
|
120049
|
+
const destination = options.output ? resolve24(report.path, options.output) : join47(context.root, "runtime", "compose", runtimeId, "compose.portta.yaml");
|
|
119976
120050
|
const output2 = new Output(globals6(command));
|
|
119977
120051
|
if (options.dryRun) {
|
|
119978
120052
|
output2.data(content);
|
|
@@ -119980,12 +120054,12 @@ async function initCommand(pathValue, options, command) {
|
|
|
119980
120054
|
}
|
|
119981
120055
|
if (existsSync23(destination) && !options.force) throw new RefusedError(`${destination} already exists`, "pass --force to replace it while keeping a backup");
|
|
119982
120056
|
await confirm(`write ${destination}?`, globals6(command).yes === true);
|
|
119983
|
-
|
|
120057
|
+
mkdirSync16(dirname23(destination), { recursive: true, mode: 493 });
|
|
119984
120058
|
if (existsSync23(destination)) renameSync5(destination, `${destination}.bak.${Date.now()}`);
|
|
119985
120059
|
const temporary = `${destination}.${process.pid}.tmp`;
|
|
119986
120060
|
writeFileSync12(temporary, content, { mode: 420 });
|
|
119987
120061
|
try {
|
|
119988
|
-
const validated = await runProcess("docker", ["compose", "-f",
|
|
120062
|
+
const validated = await runProcess("docker", ["compose", "-f", join47(report.path, report.compose_file), "-f", temporary, "config", "--format", "json"], { cwd: report.path, reject: false });
|
|
119989
120063
|
if (validated.exitCode !== 0) throw new RefusedError("generated runtime overlay is not valid Compose", validated.stderr.trim() || validated.stdout.trim());
|
|
119990
120064
|
ComposeSchema.parse(JSON.parse(validated.stdout));
|
|
119991
120065
|
renameSync5(temporary, destination);
|
|
@@ -120063,8 +120137,9 @@ init_zod();
|
|
|
120063
120137
|
init_context();
|
|
120064
120138
|
init_errors3();
|
|
120065
120139
|
import { createHash as createHash16 } from "node:crypto";
|
|
120066
|
-
import { existsSync as existsSync24, mkdirSync as
|
|
120067
|
-
import { basename as basename15, delimiter as delimiter3, isAbsolute as isAbsolute5, join as
|
|
120140
|
+
import { existsSync as existsSync24, mkdirSync as mkdirSync17, readFileSync as readFileSync22, renameSync as renameSync6, rmSync as rmSync7, writeFileSync as writeFileSync13 } from "node:fs";
|
|
120141
|
+
import { basename as basename15, delimiter as delimiter3, isAbsolute as isAbsolute5, join as join48, resolve as resolve25 } from "node:path";
|
|
120142
|
+
init_process();
|
|
120068
120143
|
var Manifest = external_exports.object({
|
|
120069
120144
|
schemaVersion: external_exports.literal(1),
|
|
120070
120145
|
driver: external_exports.literal("compose").default("compose"),
|
|
@@ -120097,25 +120172,25 @@ function idFor(workspace2) {
|
|
|
120097
120172
|
return createHash16("sha256").update(workspace2).digest("hex").slice(0, 16);
|
|
120098
120173
|
}
|
|
120099
120174
|
function digest(path7) {
|
|
120100
|
-
return createHash16("sha256").update(
|
|
120175
|
+
return createHash16("sha256").update(readFileSync22(path7)).digest("hex");
|
|
120101
120176
|
}
|
|
120102
120177
|
function digestIfPresent(path7) {
|
|
120103
120178
|
return existsSync24(path7) ? digest(path7) : null;
|
|
120104
120179
|
}
|
|
120105
120180
|
function runtimeDirectory(root2, id) {
|
|
120106
|
-
return
|
|
120181
|
+
return join48(root2, "runtime", "compose", id);
|
|
120107
120182
|
}
|
|
120108
120183
|
function planPath(root2, id) {
|
|
120109
|
-
return
|
|
120184
|
+
return join48(runtimeDirectory(root2, id), "plan.json");
|
|
120110
120185
|
}
|
|
120111
120186
|
function manifestPath(root2, id) {
|
|
120112
|
-
return
|
|
120187
|
+
return join48(runtimeDirectory(root2, id), "runtime.json");
|
|
120113
120188
|
}
|
|
120114
120189
|
function composeCandidates2(workspace2) {
|
|
120115
|
-
return ["compose.yaml", "compose.yml", "docker-compose.yaml", "docker-compose.yml"].map((file2) =>
|
|
120190
|
+
return ["compose.yaml", "compose.yml", "docker-compose.yaml", "docker-compose.yml"].map((file2) => join48(workspace2, file2)).filter(existsSync24);
|
|
120116
120191
|
}
|
|
120117
120192
|
function hasComposeRuntime(workspace2 = process.cwd()) {
|
|
120118
|
-
return composeCandidates2(resolve25(workspace2)).length > 0 && !existsSync24(
|
|
120193
|
+
return composeCandidates2(resolve25(workspace2)).length > 0 && !existsSync24(join48(resolve25(workspace2), "VERSION"));
|
|
120119
120194
|
}
|
|
120120
120195
|
function networks(value3) {
|
|
120121
120196
|
return Array.isArray(value3) ? value3 : Object.keys(value3 ?? {});
|
|
@@ -120174,22 +120249,22 @@ networks:
|
|
|
120174
120249
|
`;
|
|
120175
120250
|
}
|
|
120176
120251
|
function manifestPaths(workspace2, root2, id) {
|
|
120177
|
-
return { local:
|
|
120252
|
+
return { local: join48(workspace2, ".portta", "runtime.json"), stored: manifestPath(root2, id) };
|
|
120178
120253
|
}
|
|
120179
120254
|
function loadManifest(workspace2, root2, id) {
|
|
120180
120255
|
const { local, stored } = manifestPaths(workspace2, root2, id);
|
|
120181
120256
|
const file2 = existsSync24(local) ? local : existsSync24(stored) ? stored : null;
|
|
120182
|
-
return file2 ? Manifest.parse(JSON.parse(
|
|
120257
|
+
return file2 ? Manifest.parse(JSON.parse(readFileSync22(file2, "utf8"))) : Manifest.parse({ schemaVersion: 1 });
|
|
120183
120258
|
}
|
|
120184
120259
|
function inputFiles(workspace2, manifest) {
|
|
120185
120260
|
if (manifest.compose.files?.length) return manifest.compose.files.map((file2) => resolve25(workspace2, file2));
|
|
120186
|
-
const envFile =
|
|
120187
|
-
const env = envFile && existsSync24(envFile) ? parseEnv(
|
|
120261
|
+
const envFile = join48(workspace2, ".env");
|
|
120262
|
+
const env = envFile && existsSync24(envFile) ? parseEnv(readFileSync22(envFile, "utf8")) : /* @__PURE__ */ new Map();
|
|
120188
120263
|
const configured = process.env.COMPOSE_FILE ?? env.get("COMPOSE_FILE");
|
|
120189
120264
|
if (configured) return configured.split(delimiter3).map((file2) => isAbsolute5(file2) ? file2 : resolve25(workspace2, file2));
|
|
120190
120265
|
const base2 = composeCandidates2(workspace2)[0];
|
|
120191
120266
|
if (!base2) throw new UsageError(`no Compose file found in ${workspace2}`);
|
|
120192
|
-
const override = ["compose.override.yaml", "compose.override.yml"].map((file2) =>
|
|
120267
|
+
const override = ["compose.override.yaml", "compose.override.yml"].map((file2) => join48(workspace2, file2)).find(existsSync24);
|
|
120193
120268
|
return override ? [base2, override] : [base2];
|
|
120194
120269
|
}
|
|
120195
120270
|
function versionAtLeast2(value3, minimum = [2, 24, 4]) {
|
|
@@ -120220,7 +120295,7 @@ async function stale(plan) {
|
|
|
120220
120295
|
function readPlan(root2, workspace2) {
|
|
120221
120296
|
const path7 = planPath(root2, idFor(workspace2));
|
|
120222
120297
|
if (!existsSync24(path7)) throw new UsageError("no Runtime Plan exists for this workspace", "run `portta prepare` first");
|
|
120223
|
-
return JSON.parse(
|
|
120298
|
+
return JSON.parse(readFileSync22(path7, "utf8"));
|
|
120224
120299
|
}
|
|
120225
120300
|
function manifestWithOptions(manifest, options) {
|
|
120226
120301
|
return Manifest.parse({
|
|
@@ -120289,14 +120364,14 @@ async function prepareComposeRuntime(options, command) {
|
|
|
120289
120364
|
const unsupported = findings.filter((finding2) => finding2.code === "network_mode");
|
|
120290
120365
|
const outcome = unsupported.length ? "not_supported" : manifest.compose.mode === "manual" ? "manual" : pending.length ? "not_supported" : "isolated";
|
|
120291
120366
|
const directory = runtimeDirectory(context.root, id);
|
|
120292
|
-
const generatedOverlay =
|
|
120367
|
+
const generatedOverlay = join48(directory, "compose.portta.yaml");
|
|
120293
120368
|
const manifests = manifestPaths(workspace2, context.root, id);
|
|
120294
|
-
const envFile =
|
|
120369
|
+
const envFile = join48(workspace2, ".env");
|
|
120295
120370
|
const plan = { version: 1, driver: "compose", id, workspace: workspace2, createdAt: (/* @__PURE__ */ new Date()).toISOString(), inputs: [...files, ...existsSync24(envFile) ? [envFile] : []].map((file2) => ({ path: file2, digest: digest(file2) })), capabilities: { composeVersion }, manifest: { local: { path: manifests.local, digest: digestIfPresent(manifests.local) }, stored: { path: manifests.stored, digest: digestIfPresent(manifests.stored) } }, compose: { projectName, projectDirectory: workspace2, files, profiles: manifest.compose.profiles, mode: manifest.compose.mode, generatedOverlay }, outcome, compatibility: manifest.compatibility, pending, services, findings };
|
|
120296
120371
|
const content = manifest.compose.mode === "manual" ? "# Manual Compose integration; Portta adds no runtime mutations.\nservices: {}\n" : overlay(plan, context.config.network);
|
|
120297
120372
|
if (!options.dryRun) {
|
|
120298
120373
|
if (outcome === "not_supported") throw new RefusedError("environment is not ready for isolated Compose runtime", [...unsupported, ...pending].map((finding2) => finding2.detail).join("; "));
|
|
120299
|
-
|
|
120374
|
+
mkdirSync17(directory, { recursive: true, mode: 448 });
|
|
120300
120375
|
const temporary = `${generatedOverlay}.${process.pid}.tmp`;
|
|
120301
120376
|
writeFileSync13(temporary, content, { mode: 384 });
|
|
120302
120377
|
const final = await runProcess("docker", [...composeArgs(plan, temporary), "config", "--format", "json"], { cwd: workspace2, reject: false });
|
|
@@ -120336,7 +120411,7 @@ async function initComposeRuntime(options, command) {
|
|
|
120336
120411
|
const files = inputFiles(workspace2, Manifest.parse({ schemaVersion: 1 }));
|
|
120337
120412
|
const manifest = { schemaVersion: 1, driver: "compose", compose: { files: files.map((file2) => file2.startsWith(`${workspace2}/`) ? file2.slice(workspace2.length + 1) : file2), profiles: [], mode: options.manual ? "manual" : "auto" }, services, compatibility: { removeContainerNames: options.removeContainerName ?? [], allowSharedNetworks: options.allowSharedNetworks ?? false, allowSharedVolumes: options.allowSharedVolumes ?? false } };
|
|
120338
120413
|
const directory = runtimeDirectory(context.root, id);
|
|
120339
|
-
|
|
120414
|
+
mkdirSync17(directory, { recursive: true, mode: 448 });
|
|
120340
120415
|
writeFileSync13(manifestPath(context.root, id), `${JSON.stringify(manifest, null, 2)}
|
|
120341
120416
|
`, { mode: 384 });
|
|
120342
120417
|
return prepareComposeRuntime({ path: workspace2, project: options.project }, command);
|
|
@@ -120367,23 +120442,25 @@ async function runtimeAction(action, options, command) {
|
|
|
120367
120442
|
init_src();
|
|
120368
120443
|
init_src();
|
|
120369
120444
|
init_context();
|
|
120370
|
-
import { chmodSync as chmodSync9, existsSync as existsSync38, mkdirSync as
|
|
120371
|
-
import { join as
|
|
120445
|
+
import { chmodSync as chmodSync9, existsSync as existsSync38, mkdirSync as mkdirSync19, readFileSync as readFileSync29, readdirSync as readdirSync8, rmSync as rmSync10 } from "node:fs";
|
|
120446
|
+
import { join as join59 } from "node:path";
|
|
120372
120447
|
init_errors3();
|
|
120448
|
+
init_process();
|
|
120373
120449
|
init_version();
|
|
120374
120450
|
|
|
120375
120451
|
// src/doctor.ts
|
|
120376
120452
|
init_src();
|
|
120377
120453
|
init_context();
|
|
120378
120454
|
import { existsSync as existsSync33 } from "node:fs";
|
|
120379
|
-
import { join as
|
|
120455
|
+
import { join as join54 } from "node:path";
|
|
120456
|
+
init_process();
|
|
120380
120457
|
|
|
120381
120458
|
// src/modules/index.ts
|
|
120382
120459
|
init_src();
|
|
120383
120460
|
init_modules();
|
|
120384
120461
|
init_context();
|
|
120385
|
-
import { existsSync as existsSync32, readFileSync as
|
|
120386
|
-
import { join as
|
|
120462
|
+
import { existsSync as existsSync32, readFileSync as readFileSync27 } from "node:fs";
|
|
120463
|
+
import { join as join53 } from "node:path";
|
|
120387
120464
|
|
|
120388
120465
|
// src/modules/taskflow.ts
|
|
120389
120466
|
init_modules();
|
|
@@ -120399,8 +120476,7 @@ var WORKFLOW_ENGINE_ACTIONS = [
|
|
|
120399
120476
|
["validate", "Validate a workflow file or registered workflow"],
|
|
120400
120477
|
["save", "Save a workflow file into the registry"],
|
|
120401
120478
|
["doctor", "Check the standalone workflow engine"],
|
|
120402
|
-
["guide", "Print the workflow authoring guide"]
|
|
120403
|
-
["install-skill", "Retired \u2014 install with: npx skills add codions-labs/portta --skill portta-workflows -g"]
|
|
120479
|
+
["guide", "Print the workflow authoring guide"]
|
|
120404
120480
|
];
|
|
120405
120481
|
var USAGE_HINT = "(run with --help for usage)";
|
|
120406
120482
|
function parsePort2(value3) {
|
|
@@ -120416,7 +120492,7 @@ function helpAfter(command, text) {
|
|
|
120416
120492
|
${text(commandInvocation(context.command))}`);
|
|
120417
120493
|
}
|
|
120418
120494
|
function runOptions(command) {
|
|
120419
|
-
return command.addOption(new Option("--input <text>", "Run input text").conflicts("inputJson")).option("--input-json <json>", "Run input as a JSON object").option("--profile <name>", `Profile from ${
|
|
120495
|
+
return command.addOption(new Option("--input <text>", "Run input text").conflicts("inputJson")).option("--input-json <json>", "Run input as a JSON object").option("--profile <name>", `Profile from ${PROJECT_CONFIG_PATH2}`).addOption(
|
|
120420
120496
|
new Option("--workspace <strategy>", "Execution workspace (default: workflow policy or worktree)").choices([
|
|
120421
120497
|
"worktree",
|
|
120422
120498
|
"branch",
|
|
@@ -120486,12 +120562,12 @@ ${environment.map(([name = "", text]) => ` ${name.padEnd(width)} ${text}`).joi
|
|
|
120486
120562
|
Analysis: --analyze=auto uses the default agent only when runtime detection falls back to host.
|
|
120487
120563
|
--analyze=claude | --analyze=codex always run that agent.`
|
|
120488
120564
|
);
|
|
120489
|
-
leaf(flow, "add [branch]", "Create a worktree using the dashboard lifecycle").option("--existing", "Use an existing local or remote branch instead of creating a new one").option("--base <branch>", "Base branch for a new worktree (defaults to config)").option("--profile <name>", `Worktree profile from ${
|
|
120565
|
+
leaf(flow, "add [branch]", "Create a worktree using the dashboard lifecycle").option("--existing", "Use an existing local or remote branch instead of creating a new one").option("--base <branch>", "Base branch for a new worktree (defaults to config)").option("--profile <name>", `Worktree profile from ${PROJECT_CONFIG_PATH2}`).option("--agent <id>", "Agent id to launch (repeatable)", collect).option("--prompt <text>", "Initial agent prompt").option("--env <KEY=VALUE>", "Runtime env override (repeatable)", collect).option("--interface <mode>", "Session UI: terminal or web-chat").option("-d, --detach", "Create worktree without switching to it").option(
|
|
120490
120566
|
"--from-linear <issue-id>",
|
|
120491
120567
|
`Bootstrap from a Linear issue, plus any saved ${APP_NAME} session or linked PR`
|
|
120492
120568
|
).option("--branch <name>", "Override the branch when --from-linear resolves to one");
|
|
120493
120569
|
helpAfter(
|
|
120494
|
-
leaf(flow, "oneshot [branch]", "Run a worktree start-to-finish, streaming logs to stdout").option("--resume <branch>", "Resume an existing local worktree instead of creating one").option("--prompt <text>", "Initial agent prompt (required; follow-up nudge when --resume)").option("--agent <id>", "Agent id to launch").option("--base <branch>", "Base branch for a new worktree (defaults to config)").option("--profile <name>", `Worktree profile from ${
|
|
120570
|
+
leaf(flow, "oneshot [branch]", "Run a worktree start-to-finish, streaming logs to stdout").option("--resume <branch>", "Resume an existing local worktree instead of creating one").option("--prompt <text>", "Initial agent prompt (required; follow-up nudge when --resume)").option("--agent <id>", "Agent id to launch").option("--base <branch>", "Base branch for a new worktree (defaults to config)").option("--profile <name>", `Worktree profile from ${PROJECT_CONFIG_PATH2}`).option("--env <KEY=VALUE>", "Runtime env override (repeatable)", collect).option("--keep-open", "Don't auto-close the worktree session when the agent finishes").option(
|
|
120495
120571
|
"--linear <id|team>",
|
|
120496
120572
|
"Tie this oneshot to Linear: ENG-123 loads the issue and posts back; ENG creates an issue when done"
|
|
120497
120573
|
).option("--branch <name>", "Override the branch when --linear resolves to one"),
|
|
@@ -120524,7 +120600,7 @@ running, resume with \`${invocation2} --resume <branch> --prompt <text>\`).`
|
|
|
120524
120600
|
"--profile <name>",
|
|
120525
120601
|
"Profile name (alternative to positional arg)"
|
|
120526
120602
|
),
|
|
120527
|
-
() => `Switches the worktree to another profile from ${
|
|
120603
|
+
() => `Switches the worktree to another profile from ${PROJECT_CONFIG_PATH2}. An open
|
|
120528
120604
|
worktree is restarted with the new pane layout and commands; a closed
|
|
120529
120605
|
one picks the new profile up on its next open.`
|
|
120530
120606
|
);
|
|
@@ -120836,10 +120912,10 @@ var taskflowCliModule = {
|
|
|
120836
120912
|
// src/modules/index.ts
|
|
120837
120913
|
var CLI_MODULES = [taskflowCliModule];
|
|
120838
120914
|
function moduleEnvironment(root2 = findGatewayRoot()) {
|
|
120839
|
-
const file2 = root2 ?
|
|
120915
|
+
const file2 = root2 ? join53(root2, ".env") : null;
|
|
120840
120916
|
if (!file2 || !existsSync32(file2)) return { ...process.env };
|
|
120841
120917
|
try {
|
|
120842
|
-
return mergeEnvironment(parseEnv(
|
|
120918
|
+
return mergeEnvironment(parseEnv(readFileSync27(file2, "utf8")), process.env);
|
|
120843
120919
|
} catch {
|
|
120844
120920
|
return { ...process.env };
|
|
120845
120921
|
}
|
|
@@ -120893,7 +120969,7 @@ async function runDoctor(context) {
|
|
|
120893
120969
|
const env = context.env;
|
|
120894
120970
|
const config3 = context.config;
|
|
120895
120971
|
const root2 = context.root;
|
|
120896
|
-
const stateDir = env["PORTTA_STATE_DIR"] ||
|
|
120972
|
+
const stateDir = env["PORTTA_STATE_DIR"] || join54(root2, "state");
|
|
120897
120973
|
const add = (...entries) => {
|
|
120898
120974
|
for (const entry of entries) if (entry) checks.push(entry);
|
|
120899
120975
|
};
|
|
@@ -120905,7 +120981,7 @@ async function runDoctor(context) {
|
|
|
120905
120981
|
`unknown profile '${env["PORTTA_PROFILE"]}'`,
|
|
120906
120982
|
"set PORTTA_PROFILE to one of: local remote-private remote-public"
|
|
120907
120983
|
));
|
|
120908
|
-
const envPath =
|
|
120984
|
+
const envPath = join54(root2, ".env");
|
|
120909
120985
|
if (existsSync33(envPath)) {
|
|
120910
120986
|
add(check2("config.env", "pass", ".env", "present"));
|
|
120911
120987
|
add(envPermissionVerdict(fileMode(envPath)));
|
|
@@ -121047,10 +121123,10 @@ async function runDoctor(context) {
|
|
|
121047
121123
|
`portta up ${config3.profile}`
|
|
121048
121124
|
));
|
|
121049
121125
|
}
|
|
121050
|
-
add(existsSync33(
|
|
121126
|
+
add(existsSync33(join54(stateDir, "tailscale")) ? check2("tailscale.state.dir", "pass", "tailscale state", "persisted under state/tailscale") : check2("tailscale.state.dir", "warn", "tailscale state", "state directory missing", "portta bootstrap"));
|
|
121051
121127
|
}
|
|
121052
121128
|
const hasKey = Boolean(env["TS_AUTHKEY"]);
|
|
121053
|
-
add(!hasKey && !existsSync33(
|
|
121129
|
+
add(!hasKey && !existsSync33(join54(stateDir, "tailscale/tailscaled.state")) ? check2(
|
|
121054
121130
|
"tailscale.authkey",
|
|
121055
121131
|
"fail",
|
|
121056
121132
|
"tailscale auth",
|
|
@@ -121080,7 +121156,7 @@ async function runDoctor(context) {
|
|
|
121080
121156
|
"portta bootstrap (generates it without printing it)"
|
|
121081
121157
|
));
|
|
121082
121158
|
const auth2 = gatewayContainer(containers, "auth");
|
|
121083
|
-
const authStore =
|
|
121159
|
+
const authStore = join54(root2, "state/auth/protections.json");
|
|
121084
121160
|
add(authStoreVerdict(existsSync33(authStore), fileMode(authStore), Boolean(auth2)));
|
|
121085
121161
|
add(componentVerdict("auth.service", "authentication service", Boolean(auth2), auth2?.state ?? null, auth2?.health ?? null, "portta logs portta-auth"));
|
|
121086
121162
|
const gh = await locate("gh");
|
|
@@ -121230,7 +121306,7 @@ async function runDoctor(context) {
|
|
|
121230
121306
|
"set CF_DNS_API_TOKEN, or use ACME_CHALLENGE=http for per-hostname certificates"
|
|
121231
121307
|
));
|
|
121232
121308
|
}
|
|
121233
|
-
const store =
|
|
121309
|
+
const store = join54(stateDir, "traefik/acme/acme.json");
|
|
121234
121310
|
if (existsSync33(store)) {
|
|
121235
121311
|
const mode = fileMode(store);
|
|
121236
121312
|
add(mode === "600" ? check2("tls.acme.perms", "pass", "ACME store permissions", mode) : check2(
|
|
@@ -121432,8 +121508,9 @@ async function resolveIpv4(hostname4) {
|
|
|
121432
121508
|
|
|
121433
121509
|
// src/commands/apply.ts
|
|
121434
121510
|
init_src();
|
|
121511
|
+
init_process();
|
|
121435
121512
|
import { existsSync as existsSync34 } from "node:fs";
|
|
121436
|
-
import { join as
|
|
121513
|
+
import { join as join55 } from "node:path";
|
|
121437
121514
|
async function label(name) {
|
|
121438
121515
|
const result = await runProcess(
|
|
121439
121516
|
"docker",
|
|
@@ -121472,9 +121549,9 @@ async function ensureApplier(context) {
|
|
|
121472
121549
|
if (await label("portta.apply.spec") === spec) return { action: "kept" };
|
|
121473
121550
|
if (!await removeApplier()) return { action: "failed", reason: "the running applier could not be replaced" };
|
|
121474
121551
|
}
|
|
121475
|
-
const context_dir =
|
|
121552
|
+
const context_dir = join55(context.root, "docker", "images", "apply");
|
|
121476
121553
|
const image = porttaImages(context.version).apply;
|
|
121477
|
-
if (!existsSync34(
|
|
121554
|
+
if (!existsSync34(join55(context_dir, "Dockerfile"))) {
|
|
121478
121555
|
return { action: "failed", reason: `no applier image source at ${context_dir}` };
|
|
121479
121556
|
}
|
|
121480
121557
|
const present = await runProcess("docker", ["image", "inspect", image], { reject: false });
|
|
@@ -121488,8 +121565,9 @@ async function ensureApplier(context) {
|
|
|
121488
121565
|
|
|
121489
121566
|
// src/commands/runner.ts
|
|
121490
121567
|
init_src();
|
|
121568
|
+
init_process();
|
|
121491
121569
|
import { existsSync as existsSync35 } from "node:fs";
|
|
121492
|
-
import { join as
|
|
121570
|
+
import { join as join56 } from "node:path";
|
|
121493
121571
|
async function label2(name) {
|
|
121494
121572
|
const result = await runProcess(
|
|
121495
121573
|
"docker",
|
|
@@ -121528,9 +121606,9 @@ async function ensureRunner(context) {
|
|
|
121528
121606
|
if (await label2("portta.runner.spec") === spec) return { action: "kept" };
|
|
121529
121607
|
if (!await removeRunner()) return { action: "failed", reason: "the running runner could not be replaced" };
|
|
121530
121608
|
}
|
|
121531
|
-
const context_dir =
|
|
121609
|
+
const context_dir = join56(context.root, "docker", "images", "apply");
|
|
121532
121610
|
const image = porttaImages(context.version).apply;
|
|
121533
|
-
if (!existsSync35(
|
|
121611
|
+
if (!existsSync35(join56(context_dir, "Dockerfile"))) {
|
|
121534
121612
|
return { action: "failed", reason: `no runner image source at ${context_dir}` };
|
|
121535
121613
|
}
|
|
121536
121614
|
const present = await runProcess("docker", ["image", "inspect", image], { reject: false });
|
|
@@ -121543,20 +121621,22 @@ async function ensureRunner(context) {
|
|
|
121543
121621
|
}
|
|
121544
121622
|
|
|
121545
121623
|
// src/checkout-cli.ts
|
|
121624
|
+
init_process();
|
|
121546
121625
|
import { existsSync as existsSync36, readdirSync as readdirSync6, statSync as statSync11 } from "node:fs";
|
|
121547
|
-
import { join as
|
|
121626
|
+
import { join as join57 } from "node:path";
|
|
121548
121627
|
var CHECKOUT_CLI_SOURCES = [
|
|
121549
121628
|
"packages/cli/src",
|
|
121550
121629
|
"packages/cli/scripts",
|
|
121551
121630
|
"packages/cli/package.json",
|
|
121552
121631
|
"packages/core/src",
|
|
121553
|
-
"packages/contracts/src"
|
|
121632
|
+
"packages/contracts/src",
|
|
121633
|
+
"packages/host/src"
|
|
121554
121634
|
];
|
|
121555
121635
|
function checkoutCliDist(root2) {
|
|
121556
|
-
return
|
|
121636
|
+
return join57(root2, "packages/cli/dist/cli.js");
|
|
121557
121637
|
}
|
|
121558
121638
|
function isCheckout(root2) {
|
|
121559
|
-
return existsSync36(
|
|
121639
|
+
return existsSync36(join57(root2, "packages/cli/src/cli.ts")) && existsSync36(join57(root2, "package.json"));
|
|
121560
121640
|
}
|
|
121561
121641
|
function newestSourceMtime(path7) {
|
|
121562
121642
|
if (!existsSync36(path7)) return 0;
|
|
@@ -121564,7 +121644,7 @@ function newestSourceMtime(path7) {
|
|
|
121564
121644
|
if (info.isFile()) return /\.(ts|mjs|json)$/.test(path7) ? info.mtimeMs : 0;
|
|
121565
121645
|
let newest = 0;
|
|
121566
121646
|
for (const entry of readdirSync6(path7, { withFileTypes: true })) {
|
|
121567
|
-
newest = Math.max(newest, newestSourceMtime(
|
|
121647
|
+
newest = Math.max(newest, newestSourceMtime(join57(path7, entry.name)));
|
|
121568
121648
|
}
|
|
121569
121649
|
return newest;
|
|
121570
121650
|
}
|
|
@@ -121573,7 +121653,7 @@ function checkoutCliNeedsBuild(root2) {
|
|
|
121573
121653
|
const dist = checkoutCliDist(root2);
|
|
121574
121654
|
if (!existsSync36(dist)) return true;
|
|
121575
121655
|
const distTime = statSync11(dist).mtimeMs;
|
|
121576
|
-
return CHECKOUT_CLI_SOURCES.some((tree) => newestSourceMtime(
|
|
121656
|
+
return CHECKOUT_CLI_SOURCES.some((tree) => newestSourceMtime(join57(root2, tree)) > distTime);
|
|
121577
121657
|
}
|
|
121578
121658
|
async function ensureCheckoutCli(root2, output2) {
|
|
121579
121659
|
if (!checkoutCliNeedsBuild(root2)) return false;
|
|
@@ -121594,8 +121674,9 @@ async function reexecBuiltCli(root2) {
|
|
|
121594
121674
|
init_src();
|
|
121595
121675
|
init_context();
|
|
121596
121676
|
init_errors3();
|
|
121597
|
-
import { existsSync as existsSync37, readdirSync as readdirSync7, readFileSync as
|
|
121598
|
-
import { basename as basename20, join as
|
|
121677
|
+
import { existsSync as existsSync37, readdirSync as readdirSync7, readFileSync as readFileSync28 } from "node:fs";
|
|
121678
|
+
import { basename as basename20, join as join58 } from "node:path";
|
|
121679
|
+
init_process();
|
|
121599
121680
|
var PORTTA_DIRECTORY = ".portta";
|
|
121600
121681
|
var OVERLAY_FILE = "compose.portta.yaml";
|
|
121601
121682
|
var COMPOSE_FILE = "compose.yaml";
|
|
@@ -121605,17 +121686,17 @@ var DEMO_DIRECTORY_PREFIX = "portta-demo-";
|
|
|
121605
121686
|
var MANIFEST_FILE = "portta.example.json";
|
|
121606
121687
|
function demoDirectories(home2) {
|
|
121607
121688
|
if (!existsSync37(home2)) return [];
|
|
121608
|
-
return readdirSync7(home2, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.startsWith(DEMO_DIRECTORY_PREFIX)).map((entry) =>
|
|
121689
|
+
return readdirSync7(home2, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.startsWith(DEMO_DIRECTORY_PREFIX)).map((entry) => join58(home2, entry.name)).sort();
|
|
121609
121690
|
}
|
|
121610
121691
|
function findDemoStacks(home2) {
|
|
121611
121692
|
return demoDirectories(home2).map((stackDir) => {
|
|
121612
|
-
if (!existsSync37(
|
|
121613
|
-
const localOverlay =
|
|
121614
|
-
const overlay2 = existsSync37(
|
|
121693
|
+
if (!existsSync37(join58(stackDir, COMPOSE_FILE))) return null;
|
|
121694
|
+
const localOverlay = join58(PORTTA_DIRECTORY, OVERLAY_FILE);
|
|
121695
|
+
const overlay2 = existsSync37(join58(stackDir, localOverlay)) || existsSync37(join58(stackDir, OVERLAY_FILE));
|
|
121615
121696
|
return {
|
|
121616
121697
|
name: basename20(stackDir),
|
|
121617
121698
|
dir: stackDir,
|
|
121618
|
-
files: overlay2 ? [COMPOSE_FILE, existsSync37(
|
|
121699
|
+
files: overlay2 ? [COMPOSE_FILE, existsSync37(join58(stackDir, localOverlay)) ? localOverlay : OVERLAY_FILE] : [COMPOSE_FILE],
|
|
121619
121700
|
overlay: overlay2
|
|
121620
121701
|
};
|
|
121621
121702
|
}).filter((stack) => stack !== null).sort((left, right) => left.name.localeCompare(right.name));
|
|
@@ -121626,10 +121707,10 @@ function demoComposeArgs(stack, action) {
|
|
|
121626
121707
|
return ["compose", ...files, "down", "-v"];
|
|
121627
121708
|
}
|
|
121628
121709
|
function readDemoProject(stackDir) {
|
|
121629
|
-
const path7 =
|
|
121710
|
+
const path7 = join58(stackDir, PORTTA_DIRECTORY, MANIFEST_FILE);
|
|
121630
121711
|
if (!existsSync37(path7)) return null;
|
|
121631
121712
|
try {
|
|
121632
|
-
const manifest = JSON.parse(
|
|
121713
|
+
const manifest = JSON.parse(readFileSync28(path7, "utf8"));
|
|
121633
121714
|
const project2 = manifest.project;
|
|
121634
121715
|
if (typeof project2?.["slug"] !== "string" || typeof project2["name"] !== "string") return null;
|
|
121635
121716
|
const repositories = (Array.isArray(manifest.repositories) ? manifest.repositories : []).filter((repository) => typeof repository?.["name"] === "string" && typeof repository["relativePath"] === "string").map((repository) => ({
|
|
@@ -121794,7 +121875,7 @@ function checkoutLocalEnv() {
|
|
|
121794
121875
|
};
|
|
121795
121876
|
}
|
|
121796
121877
|
function persistEnv(root2, values) {
|
|
121797
|
-
const path7 =
|
|
121878
|
+
const path7 = join59(root2, ".env");
|
|
121798
121879
|
patchEnvFile(path7, values);
|
|
121799
121880
|
}
|
|
121800
121881
|
function buildsLocally(command) {
|
|
@@ -121810,10 +121891,10 @@ async function compose2(command, args, stdio = "inherit", extra = {}) {
|
|
|
121810
121891
|
return runProcess("docker", ["compose", ...composeArguments(context), ...args], { cwd: context.root, env: context.env, stdio, reject: extra.reject });
|
|
121811
121892
|
}
|
|
121812
121893
|
function ensureAuthState(root2) {
|
|
121813
|
-
const authDirectory =
|
|
121814
|
-
|
|
121894
|
+
const authDirectory = join59(root2, "state/auth");
|
|
121895
|
+
mkdirSync19(authDirectory, { recursive: true, mode: 448 });
|
|
121815
121896
|
chmodSync9(authDirectory, 448);
|
|
121816
|
-
const path7 =
|
|
121897
|
+
const path7 = join59(root2, ".env");
|
|
121817
121898
|
prepareEnvFile(path7);
|
|
121818
121899
|
syncForwardAuth(root2);
|
|
121819
121900
|
}
|
|
@@ -121865,7 +121946,7 @@ async function versionCommand(command) {
|
|
|
121865
121946
|
output2.line(` panel ${panel.detail}`);
|
|
121866
121947
|
if (!compatible) {
|
|
121867
121948
|
output2.warning(`this CLI is ${cli} and the installation is ${gateway}`);
|
|
121868
|
-
output2.hint(
|
|
121949
|
+
output2.hint(`update the installation with portta setup, or install the matching CLI: npm install -g @codions/portta@${gateway}`);
|
|
121869
121950
|
}
|
|
121870
121951
|
}
|
|
121871
121952
|
async function bootstrapCommand(options, command) {
|
|
@@ -121876,9 +121957,8 @@ async function bootstrapCommand(options, command) {
|
|
|
121876
121957
|
await requireDocker();
|
|
121877
121958
|
const composeVersion = await runProcess("docker", ["compose", "version", "--short"], { reject: false });
|
|
121878
121959
|
if (composeVersion.exitCode !== 0) throw new CliError("Docker Compose v2 is required", EXIT.precondition);
|
|
121879
|
-
prepareEnvFile(
|
|
121880
|
-
|
|
121881
|
-
chmodSync9(join58(context.root, "state/ssh"), 448);
|
|
121960
|
+
prepareEnvFile(join59(context.root, ".env"));
|
|
121961
|
+
ensureInstallationDirectories(context.root);
|
|
121882
121962
|
ensureAuthState(context.root);
|
|
121883
121963
|
const network2 = await ensureNetwork(context.config.network);
|
|
121884
121964
|
output2.progress(`${network2.padEnd(8)} shared network ${context.config.network}`);
|
|
@@ -121887,7 +121967,7 @@ async function bootstrapCommand(options, command) {
|
|
|
121887
121967
|
}
|
|
121888
121968
|
async function upCommand(profile, options, command) {
|
|
121889
121969
|
if (profile) command.setOptionValueWithSource("profile", profile, "cli");
|
|
121890
|
-
prepareEnvFile(
|
|
121970
|
+
prepareEnvFile(join59(gatewayContext({ profile: profile ?? globals8(command).profile }).root, ".env"));
|
|
121891
121971
|
if (options.localRelease) selectLocalRelease(gatewayContext({ profile: profile ?? globals8(command).profile }));
|
|
121892
121972
|
const context = gatewayContext({ profile: profile ?? globals8(command).profile });
|
|
121893
121973
|
if (options.localRelease) context.env["PORTTA_LOCAL_RELEASE"] = "true";
|
|
@@ -121951,7 +122031,7 @@ async function devCommand(profile, options, command) {
|
|
|
121951
122031
|
return;
|
|
121952
122032
|
}
|
|
121953
122033
|
if (options.demo) requireDemoStacks(command);
|
|
121954
|
-
const needsBootstrap = !existsSync38(
|
|
122034
|
+
const needsBootstrap = !existsSync38(join59(existing.root, ".env"));
|
|
121955
122035
|
output2.progress(`dev runs: ${[
|
|
121956
122036
|
...options.reset ? ["wipe Portta-managed containers and volumes"] : [],
|
|
121957
122037
|
...needsBootstrap ? ["prepare the checkout"] : [],
|
|
@@ -121989,10 +122069,10 @@ var REGENERABLE_STATE_DIRS = ["state/git", "state/metrics", "state/environment"]
|
|
|
121989
122069
|
function clearRegenerableState(root2) {
|
|
121990
122070
|
const cleared = [];
|
|
121991
122071
|
for (const relative6 of REGENERABLE_STATE_DIRS) {
|
|
121992
|
-
const directory =
|
|
122072
|
+
const directory = join59(root2, relative6);
|
|
121993
122073
|
if (!existsSync38(directory)) continue;
|
|
121994
122074
|
for (const entry of readdirSync8(directory, { withFileTypes: true })) {
|
|
121995
|
-
rmSync10(
|
|
122075
|
+
rmSync10(join59(directory, entry.name), { recursive: true, force: true });
|
|
121996
122076
|
}
|
|
121997
122077
|
cleared.push(relative6);
|
|
121998
122078
|
}
|
|
@@ -122071,7 +122151,7 @@ async function logsCommand(service2, options, command) {
|
|
|
122071
122151
|
} else await compose2(command, ["logs", ...options.follow === false ? [] : ["--follow"], "--tail", options.tail ?? "200", ...service2 ? [service2] : []]);
|
|
122072
122152
|
}
|
|
122073
122153
|
async function updateCommand(command) {
|
|
122074
|
-
prepareEnvFile(
|
|
122154
|
+
prepareEnvFile(join59(gatewayContext({ profile: globals8(command).profile }).root, ".env"));
|
|
122075
122155
|
await compose2(command, ["config", "--quiet"]);
|
|
122076
122156
|
await compose2(command, ["pull", "--ignore-buildable"]);
|
|
122077
122157
|
await confirm("recreate gateway components with the pulled images?", globals8(command).yes === true);
|
|
@@ -122133,7 +122213,7 @@ async function doctorCommand(command) {
|
|
|
122133
122213
|
...entry.fix ? { fix: entry.fix } : {}
|
|
122134
122214
|
}));
|
|
122135
122215
|
for (const file2 of context.composeFiles) {
|
|
122136
|
-
checks.push({ id: `compose:${file2}`, status: existsSync38(
|
|
122216
|
+
checks.push({ id: `compose:${file2}`, status: existsSync38(join59(context.root, file2)) ? "pass" : "fail", message: `${file2} exists` });
|
|
122137
122217
|
}
|
|
122138
122218
|
const aliases = readAliases(context.root);
|
|
122139
122219
|
if (aliases.length > 0) {
|
|
@@ -122151,10 +122231,10 @@ async function doctorCommand(command) {
|
|
|
122151
122231
|
if (failed.length) throw new CliError(`${failed.length} doctor check(s) failed`);
|
|
122152
122232
|
}
|
|
122153
122233
|
function readAliases(root2) {
|
|
122154
|
-
const path7 =
|
|
122234
|
+
const path7 = join59(root2, "config/traefik/dynamic/portta-aliases.yaml");
|
|
122155
122235
|
if (!existsSync38(path7)) return [];
|
|
122156
122236
|
try {
|
|
122157
|
-
return parseAliases(
|
|
122237
|
+
return parseAliases(readFileSync29(path7, "utf8"));
|
|
122158
122238
|
} catch {
|
|
122159
122239
|
return [];
|
|
122160
122240
|
}
|
|
@@ -122183,9 +122263,10 @@ async function urlsCommand(options, command) {
|
|
|
122183
122263
|
// src/commands/network.ts
|
|
122184
122264
|
init_src();
|
|
122185
122265
|
import { lookup } from "node:dns/promises";
|
|
122186
|
-
import { join as
|
|
122266
|
+
import { join as join60 } from "node:path";
|
|
122187
122267
|
init_context();
|
|
122188
122268
|
init_errors3();
|
|
122269
|
+
init_process();
|
|
122189
122270
|
function globals9(command) {
|
|
122190
122271
|
return command.optsWithGlobals();
|
|
122191
122272
|
}
|
|
@@ -122221,7 +122302,7 @@ async function publicStatus(command) {
|
|
|
122221
122302
|
}
|
|
122222
122303
|
async function writeSetting(key, value3, command) {
|
|
122223
122304
|
const context = gatewayContext({ profile: globals9(command).profile });
|
|
122224
|
-
const path7 =
|
|
122305
|
+
const path7 = join60(context.root, ".env");
|
|
122225
122306
|
patchEnvFile(path7, { [key]: value3 });
|
|
122226
122307
|
}
|
|
122227
122308
|
async function publicEnable(command) {
|
|
@@ -122307,18 +122388,19 @@ async function dnsSetup(options, command) {
|
|
|
122307
122388
|
|
|
122308
122389
|
// src/commands/host-service.ts
|
|
122309
122390
|
init_config2();
|
|
122310
|
-
import { chmodSync as chmodSync10, existsSync as existsSync39, mkdirSync as
|
|
122391
|
+
import { chmodSync as chmodSync10, existsSync as existsSync39, mkdirSync as mkdirSync20, readFileSync as readFileSync30, realpathSync as realpathSync7, unlinkSync as unlinkSync6, writeFileSync as writeFileSync14 } from "node:fs";
|
|
122311
122392
|
import { homedir as homedir9 } from "node:os";
|
|
122312
|
-
import { dirname as dirname29, join as
|
|
122393
|
+
import { dirname as dirname29, join as join61 } from "node:path";
|
|
122313
122394
|
init_context();
|
|
122314
122395
|
init_errors3();
|
|
122396
|
+
init_process();
|
|
122315
122397
|
var AUTO_PICKUP_ENV_VARS = ["LINEAR_API_KEY"];
|
|
122316
122398
|
var RESERVED_ENV_KEYS = /* @__PURE__ */ new Set(["PATH", "PORTTA_ROOT"]);
|
|
122317
122399
|
function logFile2() {
|
|
122318
|
-
return
|
|
122400
|
+
return join61(homedir9(), "Library", "Logs", SERVICE_IDENTITY.logFile);
|
|
122319
122401
|
}
|
|
122320
122402
|
function serviceFilePath(platform4, home2 = homedir9()) {
|
|
122321
|
-
return platform4 === "linux" ?
|
|
122403
|
+
return platform4 === "linux" ? join61(home2, ".config", "systemd", "user", `${SERVICE_IDENTITY.name}.service`) : join61(home2, "Library", "LaunchAgents", `${SERVICE_IDENTITY.launchdLabel}.plist`);
|
|
122322
122404
|
}
|
|
122323
122405
|
function escapePlistText(value3) {
|
|
122324
122406
|
return value3.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
@@ -122388,7 +122470,7 @@ var LAUNCHD_ENV_ENTRY_RE = /<key>([^<]+)<\/key>\s*<string>([^<]*)<\/string>/g;
|
|
|
122388
122470
|
function readEnvVarsFromUnit(filePath, platform4) {
|
|
122389
122471
|
let text;
|
|
122390
122472
|
try {
|
|
122391
|
-
text =
|
|
122473
|
+
text = readFileSync30(filePath, "utf8");
|
|
122392
122474
|
} catch {
|
|
122393
122475
|
return {};
|
|
122394
122476
|
}
|
|
@@ -122519,7 +122601,7 @@ async function hostService(action, command) {
|
|
|
122519
122601
|
${notes.join("\n")}`);
|
|
122520
122602
|
await confirm(`${installed ? "Reinstall" : "Install"} the ${SERVICE_IDENTITY.name} service?`, Boolean(options.yes));
|
|
122521
122603
|
if (installed) await runAll(serviceCommands("uninstall", platform4, filePath), output2, true);
|
|
122522
|
-
|
|
122604
|
+
mkdirSync20(dirname29(filePath), { recursive: true });
|
|
122523
122605
|
writeFileSync14(filePath, content);
|
|
122524
122606
|
if (Object.keys(envVars).length > 0) chmodSync10(filePath, 384);
|
|
122525
122607
|
await runAll(serviceCommands("install", platform4, filePath), output2);
|
|
@@ -122560,7 +122642,8 @@ init_src();
|
|
|
122560
122642
|
init_src();
|
|
122561
122643
|
init_context();
|
|
122562
122644
|
init_errors3();
|
|
122563
|
-
import { join as
|
|
122645
|
+
import { join as join62 } from "node:path";
|
|
122646
|
+
init_process();
|
|
122564
122647
|
function globals10(command) {
|
|
122565
122648
|
return command.optsWithGlobals();
|
|
122566
122649
|
}
|
|
@@ -122602,7 +122685,7 @@ function setting(name) {
|
|
|
122602
122685
|
return found;
|
|
122603
122686
|
}
|
|
122604
122687
|
function write2(root2, values) {
|
|
122605
|
-
const path7 =
|
|
122688
|
+
const path7 = join62(root2, ".env");
|
|
122606
122689
|
patchEnvFile(path7, values);
|
|
122607
122690
|
}
|
|
122608
122691
|
async function configList(command) {
|
|
@@ -122812,18 +122895,19 @@ async function configSet(name, value3, options, command) {
|
|
|
122812
122895
|
}
|
|
122813
122896
|
function configPrepare(command) {
|
|
122814
122897
|
const context = gatewayContext({ profile: globals10(command).profile });
|
|
122815
|
-
prepareEnvFile(
|
|
122898
|
+
prepareEnvFile(join62(context.root, ".env"));
|
|
122816
122899
|
new Output(globals10(command)).progress("configuration prepared from .env.example; existing values preserved");
|
|
122817
122900
|
}
|
|
122818
122901
|
|
|
122819
122902
|
// src/commands/setup.ts
|
|
122820
122903
|
init_src();
|
|
122821
122904
|
init_context();
|
|
122822
|
-
import { chmodSync as chmodSync11, copyFileSync as copyFileSync4, cpSync, existsSync as existsSync40, mkdirSync as
|
|
122905
|
+
import { chmodSync as chmodSync11, copyFileSync as copyFileSync4, cpSync, existsSync as existsSync40, mkdirSync as mkdirSync21, readdirSync as readdirSync9, realpathSync as realpathSync8, rmSync as rmSync11 } from "node:fs";
|
|
122823
122906
|
import { homedir as homedir10 } from "node:os";
|
|
122824
|
-
import { dirname as dirname30, join as
|
|
122907
|
+
import { dirname as dirname30, join as join63, resolve as resolve32 } from "node:path";
|
|
122825
122908
|
import { fileURLToPath as fileURLToPath8 } from "node:url";
|
|
122826
122909
|
init_errors3();
|
|
122910
|
+
init_process();
|
|
122827
122911
|
function globals11(command) {
|
|
122828
122912
|
return command.optsWithGlobals();
|
|
122829
122913
|
}
|
|
@@ -122837,14 +122921,14 @@ async function available(file2, args) {
|
|
|
122837
122921
|
function packagedRuntime() {
|
|
122838
122922
|
const explicit = process.env["PORTTA_RUNTIME_ROOT"];
|
|
122839
122923
|
if (explicit) return resolve32(explicit);
|
|
122840
|
-
const candidates = [
|
|
122841
|
-
const found = candidates.find((candidate) => existsSync40(
|
|
122842
|
-
if (!found) throw new PreconditionError("the Portta package does not contain its runtime assets", "reinstall the
|
|
122924
|
+
const candidates = [join63(import.meta.dirname, "runtime"), join63(import.meta.dirname, "..", "runtime")];
|
|
122925
|
+
const found = candidates.find((candidate) => existsSync40(join63(candidate, "VERSION")));
|
|
122926
|
+
if (!found) throw new PreconditionError("the Portta package does not contain its runtime assets", "reinstall the `@codions/portta` npm package");
|
|
122843
122927
|
return found;
|
|
122844
122928
|
}
|
|
122845
122929
|
function installRuntime(source, target) {
|
|
122846
|
-
|
|
122847
|
-
const cache2 =
|
|
122930
|
+
mkdirSync21(target, { recursive: true });
|
|
122931
|
+
const cache2 = join63(target, "runtime");
|
|
122848
122932
|
const sameCache = existsSync40(cache2) && realpathSync8(source) === realpathSync8(cache2);
|
|
122849
122933
|
if (!sameCache) {
|
|
122850
122934
|
rmSync11(cache2, { recursive: true, force: true });
|
|
@@ -122852,32 +122936,32 @@ function installRuntime(source, target) {
|
|
|
122852
122936
|
}
|
|
122853
122937
|
for (const entry of readdirSync9(source)) {
|
|
122854
122938
|
if (entry === "config") continue;
|
|
122855
|
-
const destination =
|
|
122939
|
+
const destination = join63(target, entry);
|
|
122856
122940
|
rmSync11(destination, { recursive: true, force: true });
|
|
122857
|
-
cpSync(
|
|
122941
|
+
cpSync(join63(source, entry), destination, { recursive: true, force: true });
|
|
122858
122942
|
}
|
|
122859
|
-
const defaults =
|
|
122860
|
-
const dynamic =
|
|
122861
|
-
|
|
122943
|
+
const defaults = join63(source, "config", "traefik", "dynamic");
|
|
122944
|
+
const dynamic = join63(target, "config", "traefik", "dynamic");
|
|
122945
|
+
mkdirSync21(dynamic, { recursive: true });
|
|
122862
122946
|
for (const entry of readdirSync9(defaults)) {
|
|
122863
|
-
const destination =
|
|
122864
|
-
if (!existsSync40(destination)) copyFileSync4(
|
|
122947
|
+
const destination = join63(dynamic, entry);
|
|
122948
|
+
if (!existsSync40(destination)) copyFileSync4(join63(defaults, entry), destination);
|
|
122865
122949
|
}
|
|
122866
|
-
const bin =
|
|
122867
|
-
|
|
122950
|
+
const bin = join63(target, "bin");
|
|
122951
|
+
mkdirSync21(bin, { recursive: true });
|
|
122868
122952
|
const executable2 = fileURLToPath8(import.meta.url);
|
|
122869
|
-
const installed =
|
|
122953
|
+
const installed = join63(bin, "portta");
|
|
122870
122954
|
if (resolve32(executable2) !== resolve32(installed)) copyFileSync4(executable2, installed);
|
|
122871
122955
|
chmodSync11(installed, 493);
|
|
122872
|
-
const docs2 =
|
|
122873
|
-
if (existsSync40(docs2)) copyFileSync4(docs2,
|
|
122956
|
+
const docs2 = join63(import.meta.dirname, "documentation.json");
|
|
122957
|
+
if (existsSync40(docs2)) copyFileSync4(docs2, join63(bin, "documentation.json"));
|
|
122874
122958
|
}
|
|
122875
122959
|
async function setupCommand(options, command) {
|
|
122876
122960
|
const global3 = globals11(command);
|
|
122877
122961
|
const output2 = new Output(global3);
|
|
122878
122962
|
if (process.platform === "win32") throw new PreconditionError("setup requires a POSIX host");
|
|
122879
122963
|
const currentRoot = findGatewayRoot();
|
|
122880
|
-
const target = resolve32(options.dir ?? currentRoot ??
|
|
122964
|
+
const target = resolve32(options.dir ?? currentRoot ?? join63(homedir10(), "portta"));
|
|
122881
122965
|
const profile = options.profile ?? global3.profile ?? "local";
|
|
122882
122966
|
const runtime2 = packagedRuntime();
|
|
122883
122967
|
const steps = [];
|
|
@@ -122886,8 +122970,9 @@ async function setupCommand(options, command) {
|
|
|
122886
122970
|
if (!output2.json) output2.progress(`${status.padEnd(8)} ${detail2}`);
|
|
122887
122971
|
};
|
|
122888
122972
|
const nodeVersion = process.versions.node;
|
|
122889
|
-
if (major(nodeVersion) < 24) throw new PreconditionError(`Node ${nodeVersion} is too old`, "Node 24 or newer is required
|
|
122973
|
+
if (major(nodeVersion) < 24) throw new PreconditionError(`Node ${nodeVersion} is too old`, "Node 24 or newer is required");
|
|
122890
122974
|
record6("node", "ok", `Node ${nodeVersion}`);
|
|
122975
|
+
await requireDocker();
|
|
122891
122976
|
const docker = await available("docker", ["version", "--format", "{{.Server.Version}}"]);
|
|
122892
122977
|
if (!docker.ok || major(docker.value) < 24) throw new PreconditionError("Docker Engine 24 or newer is required", "setup never installs Docker or invokes a system package manager");
|
|
122893
122978
|
const compose4 = await available("docker", ["compose", "version", "--short"]);
|
|
@@ -122895,25 +122980,24 @@ async function setupCommand(options, command) {
|
|
|
122895
122980
|
record6("docker", "ok", `Docker ${docker.value}, Compose ${compose4.value}`);
|
|
122896
122981
|
if (options.dryRun) {
|
|
122897
122982
|
record6("runtime", "planned", `install packaged runtime in ${target}`);
|
|
122898
|
-
record6("environment", "planned", `
|
|
122983
|
+
record6("environment", "planned", `prepare .env from .env.example in ${target}, keeping set values`);
|
|
122899
122984
|
record6("network", "planned", "ensure the shared gateway network");
|
|
122900
122985
|
record6("gateway", "planned", `pull pinned images and start profile ${profile}`);
|
|
122901
122986
|
if (output2.json) output2.data({ dryRun: true, target, steps });
|
|
122902
122987
|
return;
|
|
122903
122988
|
}
|
|
122904
122989
|
await confirm(`set up Portta in ${target}?`, global3.yes === true);
|
|
122905
|
-
if (existsSync40(target) && readdirSync9(target).length > 0 && !existsSync40(
|
|
122990
|
+
if (existsSync40(target) && readdirSync9(target).length > 0 && !existsSync40(join63(target, "docker", "compose", "compose.yaml"))) {
|
|
122906
122991
|
throw new RefusedError(`${target} exists and is not a Portta installation`, "choose an empty --dir; setup never overwrites an unrelated directory");
|
|
122907
122992
|
}
|
|
122908
|
-
|
|
122909
|
-
const updating = existsSync40(
|
|
122993
|
+
mkdirSync21(dirname30(target), { recursive: true });
|
|
122994
|
+
const updating = existsSync40(join63(target, "VERSION"));
|
|
122910
122995
|
installRuntime(runtime2, target);
|
|
122911
122996
|
record6("runtime", updating ? "updated" : "created", `${target}`);
|
|
122912
|
-
const envFile =
|
|
122997
|
+
const envFile = join63(target, ".env");
|
|
122913
122998
|
prepareEnvFile(envFile);
|
|
122914
122999
|
record6("environment", "updated", "environment prepared from .env.example");
|
|
122915
|
-
|
|
122916
|
-
for (const directory of ["state/auth", "state/ssh", "state/runner", "state/cloudflared"]) chmodSync11(join62(target, directory), 448);
|
|
123000
|
+
ensureInstallationDirectories(target);
|
|
122917
123001
|
record6("directories", "ok", "gateway state directories");
|
|
122918
123002
|
const context = gatewayContext({ root: target, profile });
|
|
122919
123003
|
const network2 = await ensureNetwork(context.config.network);
|
|
@@ -122928,13 +123012,21 @@ async function setupCommand(options, command) {
|
|
|
122928
123012
|
const running = await runProcess("docker", ["compose", ...composeArguments(context), "ps", "--status", "running", "--quiet"], { cwd: target, env: context.env });
|
|
122929
123013
|
if (!running.stdout.trim()) throw new PreconditionError("gateway started but no component remained running", `run portta doctor inside ${target}`);
|
|
122930
123014
|
record6("doctor", "ok", "gateway components are running");
|
|
122931
|
-
if (output2.json)
|
|
123015
|
+
if (output2.json) {
|
|
123016
|
+
output2.data({ dryRun: false, target, steps });
|
|
123017
|
+
return;
|
|
123018
|
+
}
|
|
123019
|
+
output2.progress("");
|
|
123020
|
+
output2.progress(`Portta is installed in ${target}.`);
|
|
123021
|
+
if (resolve32(target) !== resolve32(join63(homedir10(), "portta"))) output2.progress(`Run portta from anywhere with PORTTA_HOME=${target} exported, or from inside that directory.`);
|
|
123022
|
+
output2.progress(`The panel is off by default: portta web up starts it on http://127.0.0.1:${context.config.webPort}`);
|
|
123023
|
+
output2.progress("portta doctor checks the installation.");
|
|
122932
123024
|
}
|
|
122933
123025
|
|
|
122934
123026
|
// src/commands/auth.ts
|
|
122935
123027
|
init_context();
|
|
122936
123028
|
init_errors3();
|
|
122937
|
-
import { readFileSync as
|
|
123029
|
+
import { readFileSync as readFileSync31 } from "node:fs";
|
|
122938
123030
|
function globals12(command) {
|
|
122939
123031
|
return command.optsWithGlobals();
|
|
122940
123032
|
}
|
|
@@ -122949,7 +123041,7 @@ function panelUrlFor2(command) {
|
|
|
122949
123041
|
}
|
|
122950
123042
|
async function authBootstrap(options, command) {
|
|
122951
123043
|
if (!options.name || !options.email) throw new UsageError("--name and --email are required");
|
|
122952
|
-
const password = options.passwordStdin ?
|
|
123044
|
+
const password = options.passwordStdin ? readFileSync31(0, "utf8").trim() : "";
|
|
122953
123045
|
if (!password) throw new UsageError("the password is read from stdin", 'printf %s "$PASSWORD" | portta auth bootstrap --name \u2026 --email \u2026 --password-stdin');
|
|
122954
123046
|
if (password.length < 10) throw new UsageError("the password must be at least 10 characters");
|
|
122955
123047
|
const { client, output: output2 } = clientFor(command, { actor: void 0, actorKind: "human" });
|
|
@@ -122982,7 +123074,7 @@ async function authLogin(options, command) {
|
|
|
122982
123074
|
}
|
|
122983
123075
|
async function readSecret() {
|
|
122984
123076
|
if (!process.stdin.isTTY) {
|
|
122985
|
-
const piped =
|
|
123077
|
+
const piped = readFileSync31(0, "utf8").trim();
|
|
122986
123078
|
if (piped) return piped;
|
|
122987
123079
|
throw new UsageError("no token on stdin", "pass --token, or run this in a terminal");
|
|
122988
123080
|
}
|
|
@@ -123121,23 +123213,23 @@ init_src();
|
|
|
123121
123213
|
init_context();
|
|
123122
123214
|
init_errors3();
|
|
123123
123215
|
import { randomBytes as randomBytes8 } from "node:crypto";
|
|
123124
|
-
import { chmodSync as chmodSync12, mkdirSync as
|
|
123125
|
-
import { dirname as dirname31, join as
|
|
123216
|
+
import { chmodSync as chmodSync12, mkdirSync as mkdirSync22, readFileSync as readFileSync32 } from "node:fs";
|
|
123217
|
+
import { dirname as dirname31, join as join64 } from "node:path";
|
|
123126
123218
|
function globals13(command) {
|
|
123127
123219
|
return command.optsWithGlobals();
|
|
123128
123220
|
}
|
|
123129
123221
|
function paths(command) {
|
|
123130
123222
|
const root2 = gatewayContext({ profile: globals13(command).profile }).root;
|
|
123131
123223
|
return {
|
|
123132
|
-
store:
|
|
123133
|
-
dynamic:
|
|
123224
|
+
store: join64(root2, "state/auth/protections.json"),
|
|
123225
|
+
dynamic: join64(root2, "config/traefik/dynamic/portta-auth.yaml")
|
|
123134
123226
|
};
|
|
123135
123227
|
}
|
|
123136
123228
|
function save(command, store) {
|
|
123137
123229
|
const target = paths(command);
|
|
123138
|
-
|
|
123230
|
+
mkdirSync22(dirname31(target.store), { recursive: true, mode: 448 });
|
|
123139
123231
|
chmodSync12(dirname31(target.store), 448);
|
|
123140
|
-
|
|
123232
|
+
mkdirSync22(dirname31(target.dynamic), { recursive: true });
|
|
123141
123233
|
writeProtectionStore(target.store, store);
|
|
123142
123234
|
writeEnvFile(target.dynamic, renderAuthDynamic(store));
|
|
123143
123235
|
}
|
|
@@ -123153,7 +123245,7 @@ async function protectHost(hostInput, options, command) {
|
|
|
123153
123245
|
const entryPoint = options.entrypoint ?? (gatewayContext({ profile: globals13(command).profile }).config.tlsEnabled ? "websecure" : "web");
|
|
123154
123246
|
if (!/^[A-Za-z0-9_-]+$/.test(entryPoint)) throw new UsageError(`invalid entrypoint: ${entryPoint}`);
|
|
123155
123247
|
const generated = options.passwordStdin !== true;
|
|
123156
|
-
const password = generated ? randomBytes8(20).toString("base64url").slice(0, 20).match(/.{1,5}/g).join("-") :
|
|
123248
|
+
const password = generated ? randomBytes8(20).toString("base64url").slice(0, 20).match(/.{1,5}/g).join("-") : readFileSync32(0, "utf8").trim();
|
|
123157
123249
|
if (!password) throw new UsageError("no password on stdin");
|
|
123158
123250
|
const target = paths(command);
|
|
123159
123251
|
const existing = readProtectionStore(target.store);
|
|
@@ -123206,11 +123298,12 @@ async function protectStatus(hostInput, command) {
|
|
|
123206
123298
|
init_src();
|
|
123207
123299
|
init_errors3();
|
|
123208
123300
|
init_context();
|
|
123209
|
-
import { readFileSync as
|
|
123301
|
+
import { readFileSync as readFileSync33 } from "node:fs";
|
|
123210
123302
|
import { randomBytes as randomBytes9 } from "node:crypto";
|
|
123303
|
+
init_process();
|
|
123211
123304
|
function passwordFrom(options) {
|
|
123212
123305
|
if (options.passwordStdin) {
|
|
123213
|
-
const password2 =
|
|
123306
|
+
const password2 = readFileSync33(0, "utf8").trim();
|
|
123214
123307
|
if (!password2) throw new UsageError("no password on stdin");
|
|
123215
123308
|
if (password2.length < 10) throw new UsageError("the password must be at least 10 characters");
|
|
123216
123309
|
return { password: password2, generated: false };
|
|
@@ -123338,13 +123431,13 @@ init_zod();
|
|
|
123338
123431
|
init_context();
|
|
123339
123432
|
init_errors3();
|
|
123340
123433
|
import { existsSync as existsSync41 } from "node:fs";
|
|
123341
|
-
import { join as
|
|
123434
|
+
import { join as join65 } from "node:path";
|
|
123342
123435
|
var Share2 = external_exports.object({ id: external_exports.string(), mode: external_exports.enum(["public", "protected"]), host: external_exports.string(), entryPoint: external_exports.string(), container: external_exports.string(), port: external_exports.number(), expiresAt: external_exports.number(), user: external_exports.string().nullable().optional() }).passthrough();
|
|
123343
123436
|
function globals14(command) {
|
|
123344
123437
|
return command.optsWithGlobals();
|
|
123345
123438
|
}
|
|
123346
123439
|
function path6(command) {
|
|
123347
|
-
return
|
|
123440
|
+
return join65(gatewayContext({ profile: globals14(command).profile }).root, "config/traefik/dynamic/portta-shares.yaml");
|
|
123348
123441
|
}
|
|
123349
123442
|
function root(command) {
|
|
123350
123443
|
return gatewayContext({ profile: globals14(command).profile }).root;
|
|
@@ -123360,11 +123453,11 @@ function write3(command, shares) {
|
|
|
123360
123453
|
}
|
|
123361
123454
|
function removeScopes(command, scopes) {
|
|
123362
123455
|
const gateway = root(command);
|
|
123363
|
-
const storePath =
|
|
123456
|
+
const storePath = join65(gateway, "state/auth/protections.json");
|
|
123364
123457
|
const store = readProtectionStore(storePath);
|
|
123365
123458
|
const next = { ...store, protections: store.protections.filter((protection) => !scopes.has(protection.scope)) };
|
|
123366
123459
|
writeProtectionStore(storePath, next);
|
|
123367
|
-
writeEnvFile(
|
|
123460
|
+
writeEnvFile(join65(gateway, "config/traefik/dynamic/portta-auth.yaml"), renderAuthDynamic(next));
|
|
123368
123461
|
}
|
|
123369
123462
|
async function shareList(command) {
|
|
123370
123463
|
const shares = read(command);
|
|
@@ -123395,14 +123488,16 @@ async function shareGc(command) {
|
|
|
123395
123488
|
// src/commands/tls.ts
|
|
123396
123489
|
init_src();
|
|
123397
123490
|
init_context();
|
|
123398
|
-
import { chmodSync as chmodSync13, existsSync as existsSync42, mkdirSync as
|
|
123491
|
+
import { chmodSync as chmodSync13, existsSync as existsSync42, mkdirSync as mkdirSync23, writeFileSync as writeFileSync15 } from "node:fs";
|
|
123399
123492
|
import { platform as platform3 } from "node:os";
|
|
123400
|
-
import { join as
|
|
123493
|
+
import { join as join66 } from "node:path";
|
|
123401
123494
|
init_errors3();
|
|
123495
|
+
init_process();
|
|
123402
123496
|
|
|
123403
123497
|
// src/toolbox.ts
|
|
123404
123498
|
init_errors3();
|
|
123405
123499
|
init_src();
|
|
123500
|
+
init_process();
|
|
123406
123501
|
init_context();
|
|
123407
123502
|
async function toolboxExists(image) {
|
|
123408
123503
|
return !(await runProcess("docker", ["image", "inspect", image], { reject: false })).failed;
|
|
@@ -123432,14 +123527,14 @@ function globals15(command) {
|
|
|
123432
123527
|
return command.optsWithGlobals();
|
|
123433
123528
|
}
|
|
123434
123529
|
function tlsPaths(root2) {
|
|
123435
|
-
const directory =
|
|
123530
|
+
const directory = join66(root2, "config/tls");
|
|
123436
123531
|
return {
|
|
123437
123532
|
directory,
|
|
123438
|
-
caKey:
|
|
123439
|
-
caCertificate:
|
|
123440
|
-
leafKey:
|
|
123441
|
-
leafCertificate:
|
|
123442
|
-
dynamic:
|
|
123533
|
+
caKey: join66(directory, "portta-ca.key"),
|
|
123534
|
+
caCertificate: join66(directory, "portta-ca.crt"),
|
|
123535
|
+
leafKey: join66(directory, "wildcard.key"),
|
|
123536
|
+
leafCertificate: join66(directory, "wildcard.crt"),
|
|
123537
|
+
dynamic: join66(root2, "config/traefik/dynamic/local-tls.yaml")
|
|
123443
123538
|
};
|
|
123444
123539
|
}
|
|
123445
123540
|
function localTlsDynamic() {
|
|
@@ -123516,7 +123611,7 @@ async function tlsInit(command) {
|
|
|
123516
123611
|
await requireDocker();
|
|
123517
123612
|
const paths3 = tlsPaths(context.root);
|
|
123518
123613
|
const domain2 = context.config.domain;
|
|
123519
|
-
|
|
123614
|
+
mkdirSync23(paths3.directory, { recursive: true });
|
|
123520
123615
|
chmodSync13(paths3.directory, 448);
|
|
123521
123616
|
if (existsSync42(paths3.caKey)) {
|
|
123522
123617
|
output2.progress("reusing the existing local CA");
|
|
@@ -123547,7 +123642,7 @@ async function tlsInit(command) {
|
|
|
123547
123642
|
}
|
|
123548
123643
|
output2.progress(`issued ${paths3.leafCertificate} for *.${domain2}`);
|
|
123549
123644
|
writeFileSync15(paths3.dynamic, localTlsDynamic());
|
|
123550
|
-
const envPath =
|
|
123645
|
+
const envPath = join66(context.root, ".env");
|
|
123551
123646
|
patchEnvFile(envPath, { TLS_ENABLED: "true", TLS_MODE: "local" });
|
|
123552
123647
|
output2.progress("TLS enabled in .env");
|
|
123553
123648
|
output2.line("");
|
|
@@ -123596,22 +123691,23 @@ async function tlsUntrust(command) {
|
|
|
123596
123691
|
output2.line(`To delete the key material as well: rm -rf ${paths2(context.root)}`);
|
|
123597
123692
|
}
|
|
123598
123693
|
function paths2(root2) {
|
|
123599
|
-
return
|
|
123694
|
+
return join66(root2, "config/tls");
|
|
123600
123695
|
}
|
|
123601
123696
|
|
|
123602
123697
|
// src/commands/maintenance.ts
|
|
123603
123698
|
init_src();
|
|
123604
123699
|
init_context();
|
|
123605
123700
|
init_errors3();
|
|
123606
|
-
import { chmodSync as chmodSync14, copyFileSync as copyFileSync5, cpSync as cpSync2, existsSync as existsSync43, mkdirSync as
|
|
123701
|
+
import { chmodSync as chmodSync14, copyFileSync as copyFileSync5, cpSync as cpSync2, existsSync as existsSync43, mkdirSync as mkdirSync24, mkdtempSync as mkdtempSync3, readFileSync as readFileSync34, rmSync as rmSync12, statSync as statSync12, writeFileSync as writeFileSync16 } from "node:fs";
|
|
123607
123702
|
import { hostname as hostname3, tmpdir as tmpdir5 } from "node:os";
|
|
123608
|
-
import { dirname as dirname32, join as
|
|
123703
|
+
import { dirname as dirname32, join as join67 } from "node:path";
|
|
123704
|
+
init_process();
|
|
123609
123705
|
function globals16(command) {
|
|
123610
123706
|
return command.optsWithGlobals();
|
|
123611
123707
|
}
|
|
123612
123708
|
var BACKUP_VERSION = 1;
|
|
123613
123709
|
function backupPaths(root2) {
|
|
123614
|
-
return [".env", "VERSION", "config", "state"].filter((path7) => existsSync43(
|
|
123710
|
+
return [".env", "VERSION", "config", "state"].filter((path7) => existsSync43(join67(root2, path7)));
|
|
123615
123711
|
}
|
|
123616
123712
|
function renderManifest(manifest) {
|
|
123617
123713
|
return `${JSON.stringify(manifest)}
|
|
@@ -123646,11 +123742,11 @@ async function backupCommand(options, command) {
|
|
|
123646
123742
|
const output2 = new Output(global3);
|
|
123647
123743
|
const context = gatewayContext({ profile: global3.profile });
|
|
123648
123744
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "Z");
|
|
123649
|
-
const target = options.output ??
|
|
123650
|
-
const staging = mkdtempSync3(
|
|
123745
|
+
const target = options.output ?? join67(context.root, `portta-backup-${stamp}.tar.gz`);
|
|
123746
|
+
const staging = mkdtempSync3(join67(tmpdir5(), "portta-backup-"));
|
|
123651
123747
|
chmodSync14(staging, 448);
|
|
123652
123748
|
try {
|
|
123653
|
-
writeFileSync16(
|
|
123749
|
+
writeFileSync16(join67(staging, "portta-backup.json"), renderManifest({
|
|
123654
123750
|
version: BACKUP_VERSION,
|
|
123655
123751
|
portta: context.version,
|
|
123656
123752
|
created: (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d+Z$/, "Z"),
|
|
@@ -123658,12 +123754,12 @@ async function backupCommand(options, command) {
|
|
|
123658
123754
|
}));
|
|
123659
123755
|
const paths3 = backupPaths(context.root);
|
|
123660
123756
|
for (const path7 of paths3) {
|
|
123661
|
-
|
|
123662
|
-
cpSync2(
|
|
123757
|
+
mkdirSync24(join67(staging, "tree", dirname32(path7)), { recursive: true });
|
|
123758
|
+
cpSync2(join67(context.root, path7), join67(staging, "tree", path7), { recursive: true });
|
|
123663
123759
|
}
|
|
123664
123760
|
let database = false;
|
|
123665
123761
|
if (options.database !== false) {
|
|
123666
|
-
database = await dumpDatabase(context,
|
|
123762
|
+
database = await dumpDatabase(context, join67(staging, "panel.db"));
|
|
123667
123763
|
if (!database) {
|
|
123668
123764
|
output2.warning("the panel database was not included");
|
|
123669
123765
|
output2.hint("it exists once the panel has started, and copying it needs sqlite3 on this host; --no-database silences this");
|
|
@@ -123708,14 +123804,14 @@ async function restoreCommand(archive, options, command) {
|
|
|
123708
123804
|
const context = gatewayContext({ profile: global3.profile });
|
|
123709
123805
|
if (!archive) throw new UsageError("which backup?", "portta restore <file>");
|
|
123710
123806
|
if (!existsSync43(archive)) throw new UsageError(`no such file: ${archive}`);
|
|
123711
|
-
const staging = mkdtempSync3(
|
|
123807
|
+
const staging = mkdtempSync3(join67(tmpdir5(), "portta-restore-"));
|
|
123712
123808
|
chmodSync14(staging, 448);
|
|
123713
123809
|
try {
|
|
123714
123810
|
const extracted = await runProcess("tar", ["-xzf", archive, "-C", staging], { reject: false });
|
|
123715
123811
|
if (extracted.failed) throw new UsageError("that file is not a Portta backup");
|
|
123716
|
-
const manifestPath2 =
|
|
123812
|
+
const manifestPath2 = join67(staging, "portta-backup.json");
|
|
123717
123813
|
if (!existsSync43(manifestPath2)) throw new UsageError("that archive has no Portta manifest");
|
|
123718
|
-
const manifest = parseManifest(
|
|
123814
|
+
const manifest = parseManifest(readFileSync34(manifestPath2, "utf8"));
|
|
123719
123815
|
if (!manifest) throw new UsageError("that archive has an unreadable Portta manifest");
|
|
123720
123816
|
output2.progress(`restoring a backup taken from Portta ${manifest.portta}`);
|
|
123721
123817
|
if (!options.force && await containerRunning(`${context.env["PORTTA_PROJECT_NAME"] || "portta"}-traefik-1`)) {
|
|
@@ -123725,23 +123821,23 @@ async function restoreCommand(archive, options, command) {
|
|
|
123725
123821
|
);
|
|
123726
123822
|
}
|
|
123727
123823
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "Z");
|
|
123728
|
-
const safety =
|
|
123729
|
-
|
|
123824
|
+
const safety = join67(context.root, "state", `restore-${stamp}`);
|
|
123825
|
+
mkdirSync24(safety, { recursive: true });
|
|
123730
123826
|
for (const path7 of [".env", "config"]) {
|
|
123731
|
-
if (existsSync43(
|
|
123732
|
-
cpSync2(
|
|
123827
|
+
if (existsSync43(join67(context.root, path7))) {
|
|
123828
|
+
cpSync2(join67(context.root, path7), join67(safety, path7), { recursive: true });
|
|
123733
123829
|
}
|
|
123734
123830
|
}
|
|
123735
123831
|
output2.progress(`kept what was there under ${safety}`);
|
|
123736
|
-
const tree =
|
|
123832
|
+
const tree = join67(staging, "tree");
|
|
123737
123833
|
if (existsSync43(tree)) {
|
|
123738
123834
|
for (const path7 of backupPaths(tree)) {
|
|
123739
|
-
cpSync2(
|
|
123835
|
+
cpSync2(join67(tree, path7), join67(context.root, path7), { recursive: true, force: true });
|
|
123740
123836
|
}
|
|
123741
123837
|
output2.progress("configuration and state restored");
|
|
123742
123838
|
}
|
|
123743
|
-
if (existsSync43(
|
|
123744
|
-
const dump =
|
|
123839
|
+
if (existsSync43(join67(context.root, ".env"))) chmodSync14(join67(context.root, ".env"), 384);
|
|
123840
|
+
const dump = join67(staging, "panel.db");
|
|
123745
123841
|
if (existsSync43(dump)) {
|
|
123746
123842
|
const panelUp = await containerRunning(`${context.env["PORTTA_PROJECT_NAME"] || "portta"}-web-1`);
|
|
123747
123843
|
const target = databaseFileFor(context.root);
|
|
@@ -123749,7 +123845,7 @@ async function restoreCommand(archive, options, command) {
|
|
|
123749
123845
|
output2.warning("the panel is running, so its database was not replaced");
|
|
123750
123846
|
output2.hint(`portta web down then: portta restore ${archive} --force`);
|
|
123751
123847
|
} else {
|
|
123752
|
-
|
|
123848
|
+
mkdirSync24(dirname32(target), { recursive: true });
|
|
123753
123849
|
for (const file2 of databaseFiles(target)) rmSync12(file2, { force: true });
|
|
123754
123850
|
copyFileSync5(dump, target);
|
|
123755
123851
|
chmodSync14(target, 384);
|
|
@@ -123763,21 +123859,6 @@ async function restoreCommand(archive, options, command) {
|
|
|
123763
123859
|
rmSync12(staging, { recursive: true, force: true });
|
|
123764
123860
|
}
|
|
123765
123861
|
}
|
|
123766
|
-
var REPAIR_DIRECTORIES = [
|
|
123767
|
-
{ path: "config/traefik/dynamic", mode: 493 },
|
|
123768
|
-
{ path: "config/tls", mode: 493 },
|
|
123769
|
-
{ path: "state/traefik/acme", mode: 448 },
|
|
123770
|
-
{ path: "state/tailscale", mode: 493 },
|
|
123771
|
-
{ path: "state/access", mode: 493 },
|
|
123772
|
-
{ path: "state/git", mode: 493 },
|
|
123773
|
-
{ path: "state/github", mode: 493 },
|
|
123774
|
-
{ path: "state/ssh", mode: 448 },
|
|
123775
|
-
{ path: "state/metrics", mode: 493 },
|
|
123776
|
-
{ path: "state/environment", mode: 493 },
|
|
123777
|
-
{ path: "state/logs", mode: 493 },
|
|
123778
|
-
{ path: "state/runner", mode: 448 },
|
|
123779
|
-
{ path: "state/cloudflared", mode: 448 }
|
|
123780
|
-
];
|
|
123781
123862
|
var REPAIR_MODES = [
|
|
123782
123863
|
{ path: ".env", mode: 384 },
|
|
123783
123864
|
{ path: "state/traefik/acme", mode: 448 },
|
|
@@ -123785,7 +123866,8 @@ var REPAIR_MODES = [
|
|
|
123785
123866
|
{ path: "state/cloudflared", mode: 448 },
|
|
123786
123867
|
{ path: "state/cloudflared/credentials.json", mode: 384 },
|
|
123787
123868
|
{ path: "state/runner", mode: 448 },
|
|
123788
|
-
{ path: "state/ssh", mode: 448 }
|
|
123869
|
+
{ path: "state/ssh", mode: 448 },
|
|
123870
|
+
{ path: "state/auth", mode: 448 }
|
|
123789
123871
|
];
|
|
123790
123872
|
async function repairCommand2(options, command) {
|
|
123791
123873
|
const global3 = globals16(command);
|
|
@@ -123794,20 +123876,20 @@ async function repairCommand2(options, command) {
|
|
|
123794
123876
|
const dry = options.dryRun ?? false;
|
|
123795
123877
|
const changes = [];
|
|
123796
123878
|
output2.progress("Repair");
|
|
123797
|
-
for (const { path: path7, mode } of
|
|
123798
|
-
const full =
|
|
123879
|
+
for (const { path: path7, mode } of INSTALLATION_DIRECTORIES) {
|
|
123880
|
+
const full = join67(context.root, path7);
|
|
123799
123881
|
if (existsSync43(full)) continue;
|
|
123800
123882
|
changes.push(`create ${path7}`);
|
|
123801
123883
|
if (dry) {
|
|
123802
123884
|
output2.line(` would create ${path7}`);
|
|
123803
123885
|
continue;
|
|
123804
123886
|
}
|
|
123805
|
-
|
|
123887
|
+
mkdirSync24(full, { recursive: true });
|
|
123806
123888
|
chmodSync14(full, mode);
|
|
123807
123889
|
output2.progress(`created ${path7}`);
|
|
123808
123890
|
}
|
|
123809
123891
|
for (const { path: path7, mode } of REPAIR_MODES) {
|
|
123810
|
-
const full =
|
|
123892
|
+
const full = join67(context.root, path7);
|
|
123811
123893
|
if (!existsSync43(full)) continue;
|
|
123812
123894
|
const have = fileMode(full);
|
|
123813
123895
|
const want = mode.toString(8);
|
|
@@ -128790,7 +128872,8 @@ async function activityCommand(options, command) {
|
|
|
128790
128872
|
// src/commands/build.ts
|
|
128791
128873
|
init_src();
|
|
128792
128874
|
init_context();
|
|
128793
|
-
import { join as
|
|
128875
|
+
import { join as join68 } from "node:path";
|
|
128876
|
+
init_process();
|
|
128794
128877
|
function globals18(command) {
|
|
128795
128878
|
return command.optsWithGlobals();
|
|
128796
128879
|
}
|
|
@@ -128803,11 +128886,11 @@ function localReleaseBuilds(root2, version3) {
|
|
|
128803
128886
|
},
|
|
128804
128887
|
{
|
|
128805
128888
|
image: images.apply,
|
|
128806
|
-
args: ["build", "--build-arg", `PORTTA_VERSION=${version3}`, "-t", images.apply,
|
|
128889
|
+
args: ["build", "--build-arg", `PORTTA_VERSION=${version3}`, "-t", images.apply, join68(root2, "docker", "images", "apply")]
|
|
128807
128890
|
},
|
|
128808
128891
|
{
|
|
128809
128892
|
image: images.toolbox,
|
|
128810
|
-
args: ["build", "--build-arg", `PORTTA_VERSION=${version3}`, "-t", images.toolbox,
|
|
128893
|
+
args: ["build", "--build-arg", `PORTTA_VERSION=${version3}`, "-t", images.toolbox, join68(root2, "docker", "images", "toolbox")]
|
|
128811
128894
|
}
|
|
128812
128895
|
];
|
|
128813
128896
|
}
|
|
@@ -128827,12 +128910,13 @@ async function buildCommand(command) {
|
|
|
128827
128910
|
|
|
128828
128911
|
// src/commands/remote.ts
|
|
128829
128912
|
init_src();
|
|
128830
|
-
import { existsSync as existsSync44, mkdirSync as
|
|
128831
|
-
import { basename as basename21, join as
|
|
128913
|
+
import { existsSync as existsSync44, mkdirSync as mkdirSync25, readFileSync as readFileSync35, readdirSync as readdirSync10, rmSync as rmSync13, writeFileSync as writeFileSync17 } from "node:fs";
|
|
128914
|
+
import { basename as basename21, join as join69 } from "node:path";
|
|
128832
128915
|
import { createServer as createServer3 } from "node:net";
|
|
128833
128916
|
import { randomBytes as randomBytes10 } from "node:crypto";
|
|
128834
128917
|
init_context();
|
|
128835
128918
|
init_errors3();
|
|
128919
|
+
init_process();
|
|
128836
128920
|
function globals19(command) {
|
|
128837
128921
|
return command.optsWithGlobals();
|
|
128838
128922
|
}
|
|
@@ -129005,7 +129089,7 @@ async function remoteBootstrap(target, options, command) {
|
|
|
129005
129089
|
output2.progress(" ./bin/portta network status confirm what is exposed");
|
|
129006
129090
|
}
|
|
129007
129091
|
function tunnelStateDir(root2) {
|
|
129008
|
-
return
|
|
129092
|
+
return join69(root2, "state/access/tunnels");
|
|
129009
129093
|
}
|
|
129010
129094
|
function parseTunnelRecord(id, text) {
|
|
129011
129095
|
try {
|
|
@@ -129036,9 +129120,9 @@ function readTunnels(root2) {
|
|
|
129036
129120
|
if (!existsSync44(directory)) return [];
|
|
129037
129121
|
const records = [];
|
|
129038
129122
|
for (const name of readdirSync10(directory).filter((entry) => entry.endsWith(".json"))) {
|
|
129039
|
-
const path7 =
|
|
129123
|
+
const path7 = join69(directory, name);
|
|
129040
129124
|
try {
|
|
129041
|
-
const record6 = parseTunnelRecord(basename21(name, ".json"),
|
|
129125
|
+
const record6 = parseTunnelRecord(basename21(name, ".json"), readFileSync35(path7, "utf8"));
|
|
129042
129126
|
if (record6) records.push(record6);
|
|
129043
129127
|
} catch {
|
|
129044
129128
|
}
|
|
@@ -129090,7 +129174,7 @@ async function remoteAccessOpen(target, options, command) {
|
|
|
129090
129174
|
const localPort = options.localPort ? Number(options.localPort) : await freeLocalPort();
|
|
129091
129175
|
output2.progress("Opening the tunnel");
|
|
129092
129176
|
const directoryPath = tunnelStateDir(context.root);
|
|
129093
|
-
|
|
129177
|
+
mkdirSync25(directoryPath, { recursive: true });
|
|
129094
129178
|
const id = randomBytes10(3).toString("hex");
|
|
129095
129179
|
const pid = spawnDetached("ssh", sshArguments(target, context.env, [
|
|
129096
129180
|
"-o",
|
|
@@ -129107,7 +129191,7 @@ async function remoteAccessOpen(target, options, command) {
|
|
|
129107
129191
|
throw new PreconditionError("the SSH tunnel exited immediately", `check that ${target} is reachable and that ${localPort} is free locally`);
|
|
129108
129192
|
}
|
|
129109
129193
|
const record6 = { id, pid, target, project: options.project, service: options.service, remotePort, localPort, started: Math.floor(Date.now() / 1e3) };
|
|
129110
|
-
writeFileSync17(
|
|
129194
|
+
writeFileSync17(join69(directoryPath, `${id}.json`), renderTunnelRecord(record6));
|
|
129111
129195
|
if (output2.json) {
|
|
129112
129196
|
output2.data({ ...record6, address: `127.0.0.1:${localPort}` });
|
|
129113
129197
|
return;
|
|
@@ -129160,7 +129244,7 @@ async function remoteAccessClose(id, options, command) {
|
|
|
129160
129244
|
} catch {
|
|
129161
129245
|
}
|
|
129162
129246
|
}
|
|
129163
|
-
rmSync13(
|
|
129247
|
+
rmSync13(join69(directory, `${record6.id}.json`), { force: true });
|
|
129164
129248
|
output2.progress(`the bridge on ${record6.target} is still open; close it there if you are done:`);
|
|
129165
129249
|
output2.hint(`portta remote exec ${record6.target} -- 'cd portta && ./bin/portta access close --project ${record6.project}'`);
|
|
129166
129250
|
}
|
|
@@ -129172,15 +129256,16 @@ init_src();
|
|
|
129172
129256
|
init_src();
|
|
129173
129257
|
init_context();
|
|
129174
129258
|
init_errors3();
|
|
129175
|
-
import { chmodSync as chmodSync15, closeSync as closeSync3, existsSync as existsSync45, mkdirSync as
|
|
129259
|
+
import { chmodSync as chmodSync15, closeSync as closeSync3, existsSync as existsSync45, mkdirSync as mkdirSync26, openSync as openSync3, readFileSync as readFileSync36, rmSync as rmSync14, writeFileSync as writeFileSync18 } from "node:fs";
|
|
129176
129260
|
import { ReadStream } from "node:tty";
|
|
129177
|
-
import { join as
|
|
129261
|
+
import { join as join70 } from "node:path";
|
|
129262
|
+
init_process();
|
|
129178
129263
|
function globals20(command) {
|
|
129179
129264
|
return command.optsWithGlobals();
|
|
129180
129265
|
}
|
|
129181
129266
|
function tunnelPaths(root2) {
|
|
129182
|
-
const directory =
|
|
129183
|
-
return { directory, config:
|
|
129267
|
+
const directory = join70(root2, "state/cloudflared");
|
|
129268
|
+
return { directory, config: join70(directory, "config.yml"), credentials: join70(directory, "credentials.json") };
|
|
129184
129269
|
}
|
|
129185
129270
|
function tunnelContainer(env) {
|
|
129186
129271
|
return `${env["PORTTA_PROJECT_NAME"] || "portta"}-cloudflared-1`;
|
|
@@ -129315,13 +129400,13 @@ async function tunnelSetup(options, command) {
|
|
|
129315
129400
|
let token;
|
|
129316
129401
|
if (options.tokenFile) {
|
|
129317
129402
|
if (!existsSync45(options.tokenFile)) throw new UsageError(`no such file: ${options.tokenFile}`);
|
|
129318
|
-
token =
|
|
129403
|
+
token = readFileSync36(options.tokenFile, "utf8").replace(/\s+/g, "");
|
|
129319
129404
|
} else {
|
|
129320
129405
|
token = await promptForToken();
|
|
129321
129406
|
}
|
|
129322
129407
|
if (!token) throw new UsageError("no token was given");
|
|
129323
129408
|
const paths3 = tunnelPaths(context.root);
|
|
129324
|
-
|
|
129409
|
+
mkdirSync26(paths3.directory, { recursive: true });
|
|
129325
129410
|
chmodSync15(paths3.directory, 448);
|
|
129326
129411
|
let credentials;
|
|
129327
129412
|
try {
|
|
@@ -129337,7 +129422,7 @@ async function tunnelSetup(options, command) {
|
|
|
129337
129422
|
const origin = options.origin || defaultOrigin(context);
|
|
129338
129423
|
const config3 = { id: credentials.TunnelID, zone: options.zone, origin, credentialsFile: "/etc/cloudflared/credentials.json", includeApex: options.apex ?? false };
|
|
129339
129424
|
writeFileSync18(paths3.config, renderTunnelConfig(config3));
|
|
129340
|
-
const envPath =
|
|
129425
|
+
const envPath = join70(context.root, ".env");
|
|
129341
129426
|
patchEnvFile(envPath, { CLOUDFLARE_TUNNEL_ZONE: options.zone, CLOUDFLARE_TUNNEL_ID: credentials.TunnelID });
|
|
129342
129427
|
if (output2.json) {
|
|
129343
129428
|
output2.data({ zone: options.zone, tunnel: credentials.TunnelID, origin, routes: describeTunnel(config3), dns: { type: "CNAME", name: `*.${options.zone}`, target: tunnelDnsTarget(credentials.TunnelID), proxied: true } });
|
|
@@ -129366,7 +129451,7 @@ async function tunnelEnable(command) {
|
|
|
129366
129451
|
if (!tunnelConfigured(context.root)) {
|
|
129367
129452
|
throw new PreconditionError("the connector is not configured", "portta tunnel setup --zone <domain>");
|
|
129368
129453
|
}
|
|
129369
|
-
const envPath =
|
|
129454
|
+
const envPath = join70(context.root, ".env");
|
|
129370
129455
|
patchEnvFile(envPath, { CLOUDFLARE_TUNNEL_ENABLED: "true" });
|
|
129371
129456
|
output2.progress("Cloudflare Tunnel enabled");
|
|
129372
129457
|
output2.progress("starting the connector");
|
|
@@ -129389,7 +129474,7 @@ async function tunnelDisable(options, command) {
|
|
|
129389
129474
|
["compose", ...composeArguments(running), "rm", "-sf", "cloudflared"],
|
|
129390
129475
|
{ cwd: running.root, env: running.env, reject: false }
|
|
129391
129476
|
);
|
|
129392
|
-
const envPath =
|
|
129477
|
+
const envPath = join70(running.root, ".env");
|
|
129393
129478
|
const values = { CLOUDFLARE_TUNNEL_ENABLED: "false" };
|
|
129394
129479
|
if (options.forget) {
|
|
129395
129480
|
const paths3 = tunnelPaths(running.root);
|