@codions/portta 0.0.0-develop.149.sha-240655c → 0.0.0-develop.150.sha-ed65081
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/cli.js +22823 -23160
- package/dist/documentation.json +1 -1
- package/dist/host.js +663 -271
- package/dist/runtime/.env.example +33 -48
- package/dist/runtime/VERSION +1 -1
- package/dist/runtime/docker/compose/features/panel-host.yaml +44 -0
- package/dist/runtime/docker/compose/features/taskflow.yaml +5 -28
- package/dist/runtime/docker/compose/features/web.yaml +10 -26
- package/package.json +2 -2
- package/dist/runtime/docker/compose/features/db.yaml +0 -63
- package/dist/runtime/docker/compose/features/panel-webhook.yaml +0 -46
package/dist/host.js
CHANGED
|
@@ -23429,17 +23429,17 @@ var init_json_schema_processors = __esm({
|
|
|
23429
23429
|
minItems--;
|
|
23430
23430
|
}
|
|
23431
23431
|
const maxItems = def.items.length;
|
|
23432
|
-
const
|
|
23432
|
+
const isClosed2 = !def.rest;
|
|
23433
23433
|
if (ctx.target === "draft-2020-12") {
|
|
23434
23434
|
json3.prefixItems = prefixItems;
|
|
23435
|
-
if (
|
|
23435
|
+
if (isClosed2) {
|
|
23436
23436
|
json3.items = false;
|
|
23437
23437
|
} else if (rest) {
|
|
23438
23438
|
json3.items = rest;
|
|
23439
23439
|
}
|
|
23440
23440
|
if (minItems > 0)
|
|
23441
23441
|
json3.minItems = minItems;
|
|
23442
|
-
if (
|
|
23442
|
+
if (isClosed2)
|
|
23443
23443
|
json3.maxItems = maxItems;
|
|
23444
23444
|
} else if (ctx.target === "openapi-3.0") {
|
|
23445
23445
|
json3.items = {
|
|
@@ -23450,18 +23450,18 @@ var init_json_schema_processors = __esm({
|
|
|
23450
23450
|
}
|
|
23451
23451
|
if (minItems > 0)
|
|
23452
23452
|
json3.minItems = minItems;
|
|
23453
|
-
if (
|
|
23453
|
+
if (isClosed2)
|
|
23454
23454
|
json3.maxItems = maxItems;
|
|
23455
23455
|
} else {
|
|
23456
23456
|
json3.items = prefixItems;
|
|
23457
|
-
if (
|
|
23457
|
+
if (isClosed2) {
|
|
23458
23458
|
json3.additionalItems = false;
|
|
23459
23459
|
} else if (rest) {
|
|
23460
23460
|
json3.additionalItems = rest;
|
|
23461
23461
|
}
|
|
23462
23462
|
if (minItems > 0)
|
|
23463
23463
|
json3.minItems = minItems;
|
|
23464
|
-
if (
|
|
23464
|
+
if (isClosed2)
|
|
23465
23465
|
json3.maxItems = maxItems;
|
|
23466
23466
|
}
|
|
23467
23467
|
const { minimum, maximum } = schema._zod.bag;
|
|
@@ -34015,17 +34015,17 @@ var require_fast_uri = __commonJS({
|
|
|
34015
34015
|
if (baseMalformed || relativeMalformed || baseMalformedPercentEncoding || relativeMalformedPercentEncoding || baseMalformedSchemeSpecific || relativeMalformedSchemeSpecific || baseMalformedHost || relativeMalformedHost || baseMalformedScheme || relativeMalformedScheme) {
|
|
34016
34016
|
throw new Error(baseParsed.error || relativeParsed.error || "URI is malformed.");
|
|
34017
34017
|
}
|
|
34018
|
-
const
|
|
34019
|
-
const resolvedSchemeHandler = getSchemeHandler(options && options.scheme ||
|
|
34020
|
-
const resolvedHost =
|
|
34018
|
+
const resolved2 = resolveComponent(baseParsed, relativeParsed, schemelessOptions, true);
|
|
34019
|
+
const resolvedSchemeHandler = getSchemeHandler(options && options.scheme || resolved2.scheme);
|
|
34020
|
+
const resolvedHost = resolved2.host;
|
|
34021
34021
|
const resolvedHostIsIP = resolvedHost !== void 0 && resolvedHost !== "" && (isIPv4(resolvedHost) || normalizeIPv6(resolvedHost).isIPV6);
|
|
34022
|
-
canonicalizeHost(
|
|
34022
|
+
canonicalizeHost(resolved2, options || {}, resolvedSchemeHandler, resolvedHostIsIP);
|
|
34023
34023
|
const encodedASCIIHost = resolvedHost && resolvedHost.indexOf("%") !== -1 && !new RegExp("\\P{ASCII}", "u").test(resolvedHost);
|
|
34024
|
-
if (
|
|
34025
|
-
throw new Error(
|
|
34024
|
+
if (resolved2.error && !encodedASCIIHost) {
|
|
34025
|
+
throw new Error(resolved2.error);
|
|
34026
34026
|
}
|
|
34027
34027
|
schemelessOptions.skipEscape = true;
|
|
34028
|
-
return serialize2(
|
|
34028
|
+
return serialize2(resolved2, schemelessOptions);
|
|
34029
34029
|
}
|
|
34030
34030
|
function resolveComponent(base, relative5, options, skipNormalization) {
|
|
34031
34031
|
const target = {};
|
|
@@ -43704,8 +43704,8 @@ function convertBaseSchema(schema, ctx) {
|
|
|
43704
43704
|
});
|
|
43705
43705
|
}
|
|
43706
43706
|
ctx.processing.add(refPath);
|
|
43707
|
-
const
|
|
43708
|
-
const zodSchema2 = convertSchema(
|
|
43707
|
+
const resolved2 = resolveRef(refPath, ctx);
|
|
43708
|
+
const zodSchema2 = convertSchema(resolved2, ctx);
|
|
43709
43709
|
ctx.refs.set(refPath, zodSchema2);
|
|
43710
43710
|
ctx.processing.delete(refPath);
|
|
43711
43711
|
return zodSchema2;
|
|
@@ -44576,8 +44576,8 @@ function createHostShellPathRefresher(options = {}) {
|
|
|
44576
44576
|
if (pending) return pending;
|
|
44577
44577
|
if (expiresAt > now()) return Promise.resolve({ changed: false, path });
|
|
44578
44578
|
pending = (async () => {
|
|
44579
|
-
const
|
|
44580
|
-
const next =
|
|
44579
|
+
const resolved2 = await resolvePath();
|
|
44580
|
+
const next = resolved2 ? mergePaths(path, resolved2) : path;
|
|
44581
44581
|
const changed = next !== path;
|
|
44582
44582
|
path = next;
|
|
44583
44583
|
if (path) env2.PATH = path;
|
|
@@ -44771,8 +44771,8 @@ function resolveHostTool(tool, options) {
|
|
|
44771
44771
|
return defaultHostToolResolver()(tool);
|
|
44772
44772
|
}
|
|
44773
44773
|
function hostToolInvocation(tool, options) {
|
|
44774
|
-
const
|
|
44775
|
-
return /[\s'"\\]/.test(
|
|
44774
|
+
const resolved2 = resolveHostTool(tool, options) ?? tool;
|
|
44775
|
+
return /[\s'"\\]/.test(resolved2) ? `'${resolved2.replaceAll("'", "'\\''")}'` : resolved2;
|
|
44776
44776
|
}
|
|
44777
44777
|
function resolveWorkerBins(env2 = process.env) {
|
|
44778
44778
|
const resolve21 = (name, override) => {
|
|
@@ -48278,9 +48278,9 @@ function sanitizeNameSegment(value, maxLength = 24) {
|
|
|
48278
48278
|
return trimmed || "x";
|
|
48279
48279
|
}
|
|
48280
48280
|
function buildProjectSessionName(projectRoot2) {
|
|
48281
|
-
const
|
|
48282
|
-
const base = sanitizeNameSegment(basename2(
|
|
48283
|
-
const hash4 = createHash5("sha1").update(
|
|
48281
|
+
const resolved2 = resolve4(projectRoot2);
|
|
48282
|
+
const base = sanitizeNameSegment(basename2(resolved2), 18);
|
|
48283
|
+
const hash4 = createHash5("sha1").update(resolved2).digest("hex").slice(0, 8);
|
|
48284
48284
|
return `${RUNTIME_IDENTITY.tmuxPrefix}-${base}-${hash4}`;
|
|
48285
48285
|
}
|
|
48286
48286
|
function buildWorktreeWindowName(branch) {
|
|
@@ -48768,12 +48768,12 @@ import { spawnSync as spawnSync4 } from "node:child_process";
|
|
|
48768
48768
|
import { readdirSync, realpathSync, rmSync, statSync as statSync3 } from "node:fs";
|
|
48769
48769
|
import { basename as basename3, dirname as dirname7, join as join6, resolve as resolve6 } from "node:path";
|
|
48770
48770
|
function canonicalizeFsPath(input2) {
|
|
48771
|
-
const
|
|
48771
|
+
const resolved2 = resolve6(input2);
|
|
48772
48772
|
try {
|
|
48773
|
-
return realpathSync(
|
|
48773
|
+
return realpathSync(resolved2);
|
|
48774
48774
|
} catch {
|
|
48775
48775
|
const missing = [];
|
|
48776
|
-
let current =
|
|
48776
|
+
let current = resolved2;
|
|
48777
48777
|
while (current !== dirname7(current)) {
|
|
48778
48778
|
missing.unshift(basename3(current));
|
|
48779
48779
|
current = dirname7(current);
|
|
@@ -48782,7 +48782,7 @@ function canonicalizeFsPath(input2) {
|
|
|
48782
48782
|
} catch {
|
|
48783
48783
|
}
|
|
48784
48784
|
}
|
|
48785
|
-
return
|
|
48785
|
+
return resolved2;
|
|
48786
48786
|
}
|
|
48787
48787
|
}
|
|
48788
48788
|
function sameFsPath(left, right) {
|
|
@@ -49124,20 +49124,20 @@ function loadProjectAllowlist(options = {}) {
|
|
|
49124
49124
|
return [...new Set(roots.map((root) => canonicalizeFsPath(root)))];
|
|
49125
49125
|
}
|
|
49126
49126
|
function isPathInsideAllowlist(candidate, allowlist) {
|
|
49127
|
-
const
|
|
49127
|
+
const resolved2 = canonicalizeFsPath(candidate);
|
|
49128
49128
|
return allowlist.some((root) => {
|
|
49129
49129
|
const canonicalRoot = canonicalizeFsPath(root);
|
|
49130
|
-
if (
|
|
49130
|
+
if (resolved2 === canonicalRoot) return true;
|
|
49131
49131
|
const prefix = canonicalRoot.endsWith("/") ? canonicalRoot : `${canonicalRoot}/`;
|
|
49132
|
-
return
|
|
49132
|
+
return resolved2.startsWith(prefix);
|
|
49133
49133
|
});
|
|
49134
49134
|
}
|
|
49135
49135
|
function assertProjectRootAllowed(candidate, allowlist) {
|
|
49136
|
-
const
|
|
49137
|
-
if (!isPathInsideAllowlist(
|
|
49138
|
-
throw new ProjectAllowlistError(`Project root is outside the configured allowlist: ${
|
|
49136
|
+
const resolved2 = canonicalizeFsPath(candidate);
|
|
49137
|
+
if (!isPathInsideAllowlist(resolved2, allowlist)) {
|
|
49138
|
+
throw new ProjectAllowlistError(`Project root is outside the configured allowlist: ${resolved2}`);
|
|
49139
49139
|
}
|
|
49140
|
-
return
|
|
49140
|
+
return resolved2;
|
|
49141
49141
|
}
|
|
49142
49142
|
function registerableProjectRoot(inputPath, deps) {
|
|
49143
49143
|
const trimmed = inputPath.trim();
|
|
@@ -52328,8 +52328,8 @@ var LifecycleService = class {
|
|
|
52328
52328
|
}
|
|
52329
52329
|
async openWorktree(branch, options = {}) {
|
|
52330
52330
|
try {
|
|
52331
|
-
const
|
|
52332
|
-
let initialized =
|
|
52331
|
+
const resolved2 = await this.resolveExistingWorktree(branch);
|
|
52332
|
+
let initialized = resolved2.meta ? await this.refreshManagedArtifacts(resolved2) : await this.initializeUnmanagedWorktree(resolved2);
|
|
52333
52333
|
const { profileName, profile } = this.resolveProfile(initialized.meta.profile);
|
|
52334
52334
|
const agent = this.resolveAgentDefinition(initialized.meta.agent);
|
|
52335
52335
|
const interfaceMode = this.resolveInterfaceMode(
|
|
@@ -52346,11 +52346,11 @@ var LifecycleService = class {
|
|
|
52346
52346
|
await writeWorktreeMeta(initialized.paths.gitDir, nextMeta);
|
|
52347
52347
|
initialized = { ...initialized, meta: nextMeta };
|
|
52348
52348
|
}
|
|
52349
|
-
const launchMode =
|
|
52349
|
+
const launchMode = resolved2.meta && agent.capabilities.resume ? "resume" : "fresh";
|
|
52350
52350
|
const resumeConversationId = resolveCodexResumeConversationId(initialized.meta, agent, launchMode);
|
|
52351
52351
|
await ensureAgentRuntimeArtifacts({
|
|
52352
52352
|
gitDir: initialized.paths.gitDir,
|
|
52353
|
-
worktreePath:
|
|
52353
|
+
worktreePath: resolved2.entry.path
|
|
52354
52354
|
});
|
|
52355
52355
|
await this.materializeRuntimeSession({
|
|
52356
52356
|
branch,
|
|
@@ -52358,7 +52358,7 @@ var LifecycleService = class {
|
|
|
52358
52358
|
profile,
|
|
52359
52359
|
agent,
|
|
52360
52360
|
initialized,
|
|
52361
|
-
worktreePath:
|
|
52361
|
+
worktreePath: resolved2.entry.path,
|
|
52362
52362
|
launchMode,
|
|
52363
52363
|
followUpPrompt: options.prompt,
|
|
52364
52364
|
source: options.oneshot ? "oneshot" : initialized.meta.source,
|
|
@@ -52366,7 +52366,7 @@ var LifecycleService = class {
|
|
|
52366
52366
|
interfaceMode
|
|
52367
52367
|
});
|
|
52368
52368
|
if (initialized.meta.agentTerminalStale === true) {
|
|
52369
|
-
await writeWorktreeMeta(
|
|
52369
|
+
await writeWorktreeMeta(resolved2.gitDir, {
|
|
52370
52370
|
...initialized.meta,
|
|
52371
52371
|
agentTerminalStale: false
|
|
52372
52372
|
});
|
|
@@ -52374,8 +52374,8 @@ var LifecycleService = class {
|
|
|
52374
52374
|
if (interfaceMode === "terminal") {
|
|
52375
52375
|
await this.restoreWorktreeTabs({
|
|
52376
52376
|
branch,
|
|
52377
|
-
gitDir:
|
|
52378
|
-
worktreePath:
|
|
52377
|
+
gitDir: resolved2.gitDir,
|
|
52378
|
+
worktreePath: resolved2.entry.path,
|
|
52379
52379
|
profile,
|
|
52380
52380
|
profileName,
|
|
52381
52381
|
agent,
|
|
@@ -52393,11 +52393,11 @@ var LifecycleService = class {
|
|
|
52393
52393
|
}
|
|
52394
52394
|
async refreshAgentTerminal(branch) {
|
|
52395
52395
|
try {
|
|
52396
|
-
const
|
|
52397
|
-
if (!
|
|
52396
|
+
const resolved2 = await this.resolveExistingWorktree(branch);
|
|
52397
|
+
if (!resolved2.meta) {
|
|
52398
52398
|
throw new LifecycleError(`Worktree ${branch} has no managed metadata to refresh`, 409);
|
|
52399
52399
|
}
|
|
52400
|
-
const initialized = await this.refreshManagedArtifacts(
|
|
52400
|
+
const initialized = await this.refreshManagedArtifacts(resolved2);
|
|
52401
52401
|
const { profileName, profile } = this.resolveProfile(initialized.meta.profile);
|
|
52402
52402
|
const agent = this.resolveAgentDefinition(initialized.meta.agent);
|
|
52403
52403
|
if (agent.kind !== "builtin" || agent.implementation.agent !== "codex" && agent.implementation.agent !== "claude") {
|
|
@@ -52416,8 +52416,8 @@ var LifecycleService = class {
|
|
|
52416
52416
|
resumeConversationId = await this.ensureRootSessionId({
|
|
52417
52417
|
meta: initialized.meta,
|
|
52418
52418
|
agentKind: agent.implementation.agent,
|
|
52419
|
-
worktreePath:
|
|
52420
|
-
resolved
|
|
52419
|
+
worktreePath: resolved2.entry.path,
|
|
52420
|
+
resolved: resolved2
|
|
52421
52421
|
});
|
|
52422
52422
|
if (!resumeConversationId) {
|
|
52423
52423
|
throw new LifecycleError(`No ${agent.label} conversation is available to refresh`, 409);
|
|
@@ -52425,7 +52425,7 @@ var LifecycleService = class {
|
|
|
52425
52425
|
}
|
|
52426
52426
|
await ensureAgentRuntimeArtifacts({
|
|
52427
52427
|
gitDir: initialized.paths.gitDir,
|
|
52428
|
-
worktreePath:
|
|
52428
|
+
worktreePath: resolved2.entry.path
|
|
52429
52429
|
});
|
|
52430
52430
|
await this.materializeRuntimeSession({
|
|
52431
52431
|
branch,
|
|
@@ -52433,19 +52433,19 @@ var LifecycleService = class {
|
|
|
52433
52433
|
profile,
|
|
52434
52434
|
agent,
|
|
52435
52435
|
initialized,
|
|
52436
|
-
worktreePath:
|
|
52436
|
+
worktreePath: resolved2.entry.path,
|
|
52437
52437
|
launchMode: "resume",
|
|
52438
52438
|
resumeConversationId
|
|
52439
52439
|
});
|
|
52440
|
-
const refreshedMeta = await this.readMetaOrThrow(
|
|
52441
|
-
await writeWorktreeMeta(
|
|
52440
|
+
const refreshedMeta = await this.readMetaOrThrow(resolved2.gitDir);
|
|
52441
|
+
await writeWorktreeMeta(resolved2.gitDir, {
|
|
52442
52442
|
...refreshedMeta,
|
|
52443
52443
|
agentTerminalStale: false
|
|
52444
52444
|
});
|
|
52445
52445
|
await this.restoreWorktreeTabs({
|
|
52446
52446
|
branch,
|
|
52447
|
-
gitDir:
|
|
52448
|
-
worktreePath:
|
|
52447
|
+
gitDir: resolved2.gitDir,
|
|
52448
|
+
worktreePath: resolved2.entry.path,
|
|
52449
52449
|
profile,
|
|
52450
52450
|
profileName,
|
|
52451
52451
|
agent,
|
|
@@ -52550,27 +52550,27 @@ var LifecycleService = class {
|
|
|
52550
52550
|
return meta3;
|
|
52551
52551
|
}
|
|
52552
52552
|
async prepareTabContext(branch) {
|
|
52553
|
-
const
|
|
52554
|
-
if (!
|
|
52553
|
+
const resolved2 = await this.resolveExistingWorktree(branch);
|
|
52554
|
+
if (!resolved2.meta) throw new LifecycleError(`Worktree ${branch} has no managed metadata`, 409);
|
|
52555
52555
|
const sessionName = buildProjectSessionName(this.deps.projectRoot);
|
|
52556
52556
|
const windowName = buildWorktreeWindowName(branch);
|
|
52557
52557
|
if (!await this.deps.sessions.hasWindow(sessionName, windowName)) {
|
|
52558
52558
|
throw new LifecycleError(`Worktree ${branch} is not open`, 409);
|
|
52559
52559
|
}
|
|
52560
|
-
const { profileName, profile } = this.resolveProfile(
|
|
52560
|
+
const { profileName, profile } = this.resolveProfile(resolved2.meta.profile);
|
|
52561
52561
|
if (profile.runtime === "docker") {
|
|
52562
52562
|
throw new LifecycleError("Tabs are not supported for Docker worktrees", 409);
|
|
52563
52563
|
}
|
|
52564
|
-
const agent = this.resolveAgentDefinition(
|
|
52564
|
+
const agent = this.resolveAgentDefinition(resolved2.meta.agent);
|
|
52565
52565
|
if (agent.kind !== "builtin") {
|
|
52566
52566
|
throw new LifecycleError("Tabs are only available for the built-in Claude and Codex agents", 409);
|
|
52567
52567
|
}
|
|
52568
|
-
const initialized = await this.refreshManagedArtifacts(
|
|
52568
|
+
const initialized = await this.refreshManagedArtifacts(resolved2);
|
|
52569
52569
|
return {
|
|
52570
|
-
resolved,
|
|
52570
|
+
resolved: resolved2,
|
|
52571
52571
|
initialized,
|
|
52572
52572
|
meta: initialized.meta,
|
|
52573
|
-
worktreePath:
|
|
52573
|
+
worktreePath: resolved2.entry.path,
|
|
52574
52574
|
agent,
|
|
52575
52575
|
agentKind: agent.implementation.agent,
|
|
52576
52576
|
profile,
|
|
@@ -52644,16 +52644,16 @@ var LifecycleService = class {
|
|
|
52644
52644
|
* Idempotent: returns true when armed state was cleared, false otherwise.
|
|
52645
52645
|
* The server-side oneshot watcher calls this on any browser-originated interaction. */
|
|
52646
52646
|
async disarmOneshot(branch) {
|
|
52647
|
-
let
|
|
52647
|
+
let resolved2;
|
|
52648
52648
|
try {
|
|
52649
|
-
|
|
52649
|
+
resolved2 = await this.resolveExistingWorktree(branch);
|
|
52650
52650
|
} catch {
|
|
52651
52651
|
return false;
|
|
52652
52652
|
}
|
|
52653
|
-
if (!
|
|
52654
|
-
const nextMeta = { ...
|
|
52653
|
+
if (!resolved2.meta?.oneshot) return false;
|
|
52654
|
+
const nextMeta = { ...resolved2.meta };
|
|
52655
52655
|
delete nextMeta.oneshot;
|
|
52656
|
-
await writeWorktreeMeta(
|
|
52656
|
+
await writeWorktreeMeta(resolved2.gitDir, nextMeta);
|
|
52657
52657
|
return true;
|
|
52658
52658
|
}
|
|
52659
52659
|
async closeWorktree(branch) {
|
|
@@ -52666,8 +52666,8 @@ var LifecycleService = class {
|
|
|
52666
52666
|
}
|
|
52667
52667
|
async removeWorktree(branch) {
|
|
52668
52668
|
try {
|
|
52669
|
-
const
|
|
52670
|
-
await this.removeResolvedWorktree(
|
|
52669
|
+
const resolved2 = await this.resolveExistingWorktree(branch);
|
|
52670
|
+
await this.removeResolvedWorktree(resolved2);
|
|
52671
52671
|
} catch (error61) {
|
|
52672
52672
|
throw this.wrapOperationError(error61);
|
|
52673
52673
|
}
|
|
@@ -52677,12 +52677,12 @@ var LifecycleService = class {
|
|
|
52677
52677
|
const resolvedWorktrees = await this.resolveAllWorktrees();
|
|
52678
52678
|
const sessionName = buildProjectSessionName(this.deps.projectRoot);
|
|
52679
52679
|
const removedBranches = [];
|
|
52680
|
-
for (const
|
|
52681
|
-
const branch =
|
|
52680
|
+
for (const resolved2 of resolvedWorktrees) {
|
|
52681
|
+
const branch = resolved2.entry.branch ?? resolved2.entry.path;
|
|
52682
52682
|
if (await this.deps.sessions.hasWindow(sessionName, buildWorktreeWindowName(branch))) {
|
|
52683
52683
|
continue;
|
|
52684
52684
|
}
|
|
52685
|
-
await this.removeResolvedWorktree(
|
|
52685
|
+
await this.removeResolvedWorktree(resolved2);
|
|
52686
52686
|
removedBranches.push(branch);
|
|
52687
52687
|
}
|
|
52688
52688
|
return { removedBranches };
|
|
@@ -52692,8 +52692,8 @@ var LifecycleService = class {
|
|
|
52692
52692
|
}
|
|
52693
52693
|
async mergeWorktree(branch) {
|
|
52694
52694
|
try {
|
|
52695
|
-
const
|
|
52696
|
-
this.ensureNoUncommittedChanges(
|
|
52695
|
+
const resolved2 = await this.resolveExistingWorktree(branch);
|
|
52696
|
+
this.ensureNoUncommittedChanges(resolved2.entry);
|
|
52697
52697
|
mergeManagedWorktree(
|
|
52698
52698
|
{
|
|
52699
52699
|
repoRoot: this.deps.projectRoot,
|
|
@@ -52703,7 +52703,7 @@ var LifecycleService = class {
|
|
|
52703
52703
|
this.deps.git
|
|
52704
52704
|
);
|
|
52705
52705
|
try {
|
|
52706
|
-
await this.removeResolvedWorktree(
|
|
52706
|
+
await this.removeResolvedWorktree(resolved2);
|
|
52707
52707
|
} catch (error61) {
|
|
52708
52708
|
throw new LifecycleError(
|
|
52709
52709
|
`Merged ${branch} into ${this.deps.config.workspace.mainBranch} but cleanup failed: ${toErrorMessage2(error61)}`,
|
|
@@ -52716,11 +52716,11 @@ var LifecycleService = class {
|
|
|
52716
52716
|
}
|
|
52717
52717
|
async setWorktreeArchived(branch, archived) {
|
|
52718
52718
|
try {
|
|
52719
|
-
const
|
|
52719
|
+
const resolved2 = await this.resolveExistingWorktree(branch);
|
|
52720
52720
|
if (archived) {
|
|
52721
52721
|
await this.closeBranchWindow(branch);
|
|
52722
52722
|
}
|
|
52723
|
-
await this.updateWorktreeArchivedState(
|
|
52723
|
+
await this.updateWorktreeArchivedState(resolved2.entry.path, archived);
|
|
52724
52724
|
} catch (error61) {
|
|
52725
52725
|
throw this.wrapOperationError(error61);
|
|
52726
52726
|
}
|
|
@@ -52728,12 +52728,12 @@ var LifecycleService = class {
|
|
|
52728
52728
|
async setWorktreeLabel(branch, label) {
|
|
52729
52729
|
try {
|
|
52730
52730
|
const normalizedLabel = normalizeWorktreeLabel(label);
|
|
52731
|
-
const
|
|
52732
|
-
if (!
|
|
52731
|
+
const resolved2 = await this.resolveExistingWorktree(branch);
|
|
52732
|
+
if (!resolved2.meta) {
|
|
52733
52733
|
throw new LifecycleError(`Worktree ${branch} has no managed metadata to label`, 409);
|
|
52734
52734
|
}
|
|
52735
|
-
const nextMeta = this.withUpdatedLabel(
|
|
52736
|
-
await writeWorktreeMeta(
|
|
52735
|
+
const nextMeta = this.withUpdatedLabel(resolved2.meta, normalizedLabel);
|
|
52736
|
+
await writeWorktreeMeta(resolved2.gitDir, nextMeta);
|
|
52737
52737
|
await this.deps.reconciliation.reconcile(this.deps.projectRoot, { force: true });
|
|
52738
52738
|
return { label: normalizedLabel };
|
|
52739
52739
|
} catch (error61) {
|
|
@@ -52745,20 +52745,20 @@ var LifecycleService = class {
|
|
|
52745
52745
|
* conversation); when it is closed the new profile applies on the next open. */
|
|
52746
52746
|
async setWorktreeProfile(branch, profileName) {
|
|
52747
52747
|
try {
|
|
52748
|
-
const
|
|
52749
|
-
if (!
|
|
52748
|
+
const resolved2 = await this.resolveExistingWorktree(branch);
|
|
52749
|
+
if (!resolved2.meta) {
|
|
52750
52750
|
throw new LifecycleError(`Worktree ${branch} has no managed metadata to reprofile`, 409);
|
|
52751
52751
|
}
|
|
52752
52752
|
const { profileName: nextProfileName, profile } = this.resolveProfile(profileName);
|
|
52753
52753
|
const wasOpen = await isWorktreeOpen(this.deps.sessions, this.deps.projectRoot, branch);
|
|
52754
|
-
if (
|
|
52754
|
+
if (resolved2.meta.profile === nextProfileName) {
|
|
52755
52755
|
return { profile: nextProfileName, restarted: false };
|
|
52756
52756
|
}
|
|
52757
|
-
if (
|
|
52757
|
+
if (resolved2.meta.runtime === "docker") {
|
|
52758
52758
|
await this.deps.docker.removeContainer(branch);
|
|
52759
52759
|
}
|
|
52760
|
-
await writeWorktreeMeta(
|
|
52761
|
-
...
|
|
52760
|
+
await writeWorktreeMeta(resolved2.gitDir, {
|
|
52761
|
+
...resolved2.meta,
|
|
52762
52762
|
profile: nextProfileName,
|
|
52763
52763
|
runtime: profile.runtime
|
|
52764
52764
|
});
|
|
@@ -52942,30 +52942,30 @@ var LifecycleService = class {
|
|
|
52942
52942
|
})
|
|
52943
52943
|
);
|
|
52944
52944
|
}
|
|
52945
|
-
async initializeUnmanagedWorktree(
|
|
52945
|
+
async initializeUnmanagedWorktree(resolved2) {
|
|
52946
52946
|
const { profileName, profile } = this.resolveProfile(void 0);
|
|
52947
|
-
const dotenvValues = await loadDotenvLocal(
|
|
52947
|
+
const dotenvValues = await loadDotenvLocal(resolved2.entry.path);
|
|
52948
52948
|
return initializeManagedWorktree({
|
|
52949
|
-
gitDir:
|
|
52950
|
-
branch:
|
|
52949
|
+
gitDir: resolved2.gitDir,
|
|
52950
|
+
branch: resolved2.entry.branch ?? resolved2.entry.path,
|
|
52951
52951
|
profile: profileName,
|
|
52952
52952
|
agent: this.deps.config.workspace.defaultAgent,
|
|
52953
52953
|
runtime: profile.runtime,
|
|
52954
52954
|
startupEnvValues: await this.buildStartupEnvValues(void 0),
|
|
52955
52955
|
allocatedPorts: await this.allocatePorts(),
|
|
52956
|
-
runtimeEnvExtras: { [ENV_NAMES.worktreePath]:
|
|
52956
|
+
runtimeEnvExtras: { [ENV_NAMES.worktreePath]: resolved2.entry.path },
|
|
52957
52957
|
dotenvValues,
|
|
52958
52958
|
...await this.controlEnvFields(profile.runtime)
|
|
52959
52959
|
});
|
|
52960
52960
|
}
|
|
52961
|
-
async refreshManagedArtifacts(
|
|
52962
|
-
if (!
|
|
52961
|
+
async refreshManagedArtifacts(resolved2) {
|
|
52962
|
+
if (!resolved2.meta) {
|
|
52963
52963
|
throw new Error("Missing managed metadata");
|
|
52964
52964
|
}
|
|
52965
52965
|
return await this.refreshManagedArtifactsFromMeta({
|
|
52966
|
-
gitDir:
|
|
52967
|
-
meta:
|
|
52968
|
-
worktreePath:
|
|
52966
|
+
gitDir: resolved2.gitDir,
|
|
52967
|
+
meta: resolved2.meta,
|
|
52968
|
+
worktreePath: resolved2.entry.path
|
|
52969
52969
|
});
|
|
52970
52970
|
}
|
|
52971
52971
|
async refreshManagedArtifactsFromMeta(input2) {
|
|
@@ -53200,22 +53200,22 @@ ${oneshotPrompt}` : oneshotPrompt ?? baseSystemPrompt;
|
|
|
53200
53200
|
if (!controlUrl) return {};
|
|
53201
53201
|
return { controlUrl, controlToken: await this.deps.getControlToken() };
|
|
53202
53202
|
}
|
|
53203
|
-
async removeResolvedWorktree(
|
|
53203
|
+
async removeResolvedWorktree(resolved2) {
|
|
53204
53204
|
await this.runLifecycleHook({
|
|
53205
53205
|
name: "preRemove",
|
|
53206
53206
|
command: this.deps.config.lifecycleHooks.preRemove,
|
|
53207
|
-
meta:
|
|
53208
|
-
worktreePath:
|
|
53207
|
+
meta: resolved2.meta,
|
|
53208
|
+
worktreePath: resolved2.entry.path
|
|
53209
53209
|
});
|
|
53210
|
-
const branch =
|
|
53211
|
-
if (
|
|
53210
|
+
const branch = resolved2.entry.branch ?? resolved2.entry.path;
|
|
53211
|
+
if (resolved2.meta?.runtime === "docker") {
|
|
53212
53212
|
await this.deps.docker.removeContainer(branch);
|
|
53213
53213
|
}
|
|
53214
53214
|
await this.killWorktreeWindows(branch);
|
|
53215
53215
|
removeManagedWorktree(
|
|
53216
53216
|
{
|
|
53217
53217
|
repoRoot: this.deps.projectRoot,
|
|
53218
|
-
worktreePath:
|
|
53218
|
+
worktreePath: resolved2.entry.path,
|
|
53219
53219
|
branch,
|
|
53220
53220
|
force: true,
|
|
53221
53221
|
deleteBranch: true,
|
|
@@ -53223,7 +53223,7 @@ ${oneshotPrompt}` : oneshotPrompt ?? baseSystemPrompt;
|
|
|
53223
53223
|
},
|
|
53224
53224
|
this.deps.git
|
|
53225
53225
|
);
|
|
53226
|
-
await this.updateWorktreeArchivedState(
|
|
53226
|
+
await this.updateWorktreeArchivedState(resolved2.entry.path, false);
|
|
53227
53227
|
await this.deps.reconciliation.reconcile(this.deps.projectRoot, { force: true });
|
|
53228
53228
|
}
|
|
53229
53229
|
async runLifecycleHook(input2) {
|
|
@@ -55601,7 +55601,7 @@ var Hono = class _Hono {
|
|
|
55601
55601
|
return this.#handleError(err2, c);
|
|
55602
55602
|
}
|
|
55603
55603
|
return res instanceof Promise ? res.then(
|
|
55604
|
-
(
|
|
55604
|
+
(resolved2) => resolved2 || (c.finalized ? c.res : this.#notFoundHandler(c))
|
|
55605
55605
|
).catch((err2) => this.#handleError(err2, c)) : res ?? this.#notFoundHandler(c);
|
|
55606
55606
|
}
|
|
55607
55607
|
const composed = compose(matchResult[0], this.errorHandler, this.#notFoundHandler);
|
|
@@ -56465,11 +56465,11 @@ function contractRoute(app, key, tag, handler) {
|
|
|
56465
56465
|
app.on(route.method, route.path, describeContractRoute(key, tag), async (c, next) => {
|
|
56466
56466
|
const params = route.pathParams ? parseValue(route.pathParams, c.req.param(), "Invalid path parameters") : { ok: true, data: void 0 };
|
|
56467
56467
|
if (!params.ok) return params.response;
|
|
56468
|
-
const
|
|
56469
|
-
if (!
|
|
56468
|
+
const query2 = route.query ? parseValue(route.query, readSearchParams(new URL(c.req.url)), "Invalid query") : { ok: true, data: void 0 };
|
|
56469
|
+
if (!query2.ok) return query2.response;
|
|
56470
56470
|
const body = route.body ? parseJsonValue(route.body, await c.req.text()) : { ok: true, data: void 0 };
|
|
56471
56471
|
if (!body.ok) return body.response;
|
|
56472
|
-
const input2 = { params: params.data, query:
|
|
56472
|
+
const input2 = { params: params.data, query: query2.data, body: body.data };
|
|
56473
56473
|
return handler(c, input2, next);
|
|
56474
56474
|
});
|
|
56475
56475
|
}
|
|
@@ -56515,55 +56515,55 @@ function agentId(id) {
|
|
|
56515
56515
|
}
|
|
56516
56516
|
async function apiAttachAgentsWorktree(project, branch) {
|
|
56517
56517
|
touchDashboardActivity();
|
|
56518
|
-
const
|
|
56519
|
-
if (!
|
|
56520
|
-
const chatSupport = project.resolveWorktreeAgentChatSupport(
|
|
56518
|
+
const resolved2 = await project.resolveAgentsWorktree(branch);
|
|
56519
|
+
if (!resolved2.ok) return resolved2.response;
|
|
56520
|
+
const chatSupport = project.resolveWorktreeAgentChatSupport(resolved2.worktree, "chat");
|
|
56521
56521
|
if (!chatSupport.ok) {
|
|
56522
56522
|
return errorResponse(chatSupport.error, chatSupport.status);
|
|
56523
56523
|
}
|
|
56524
|
-
const result = chatSupport.data.provider === "claude" ? await project.claudeConversationService.attachWorktreeConversation(
|
|
56524
|
+
const result = chatSupport.data.provider === "claude" ? await project.claudeConversationService.attachWorktreeConversation(resolved2.worktree) : await project.worktreeConversationService.attachWorktreeConversation(resolved2.worktree);
|
|
56525
56525
|
return result.ok ? jsonResponse(project.withClaudeLiveConversation(result.data)) : errorResponse(result.error, result.status);
|
|
56526
56526
|
}
|
|
56527
56527
|
async function apiGetAgentsWorktreeHistory(project, branch) {
|
|
56528
56528
|
touchDashboardActivity();
|
|
56529
|
-
const
|
|
56530
|
-
if (!
|
|
56531
|
-
const chatSupport = project.resolveWorktreeAgentChatSupport(
|
|
56529
|
+
const resolved2 = await project.resolveAgentsWorktree(branch);
|
|
56530
|
+
if (!resolved2.ok) return resolved2.response;
|
|
56531
|
+
const chatSupport = project.resolveWorktreeAgentChatSupport(resolved2.worktree, "chat");
|
|
56532
56532
|
if (!chatSupport.ok) {
|
|
56533
56533
|
return errorResponse(chatSupport.error, chatSupport.status);
|
|
56534
56534
|
}
|
|
56535
|
-
const result = chatSupport.data.provider === "claude" ? await project.claudeConversationService.readWorktreeConversation(
|
|
56535
|
+
const result = chatSupport.data.provider === "claude" ? await project.claudeConversationService.readWorktreeConversation(resolved2.worktree) : await project.worktreeConversationService.readWorktreeConversation(resolved2.worktree);
|
|
56536
56536
|
return result.ok ? jsonResponse(project.withClaudeLiveConversation(result.data)) : errorResponse(result.error, result.status);
|
|
56537
56537
|
}
|
|
56538
56538
|
async function apiSendAgentsWorktreeMessage(project, branch, text) {
|
|
56539
56539
|
touchDashboardActivity();
|
|
56540
56540
|
await project.disarmOneshotIfArmed(branch, "agents-send-message");
|
|
56541
|
-
const
|
|
56542
|
-
if (!
|
|
56543
|
-
if (!
|
|
56541
|
+
const resolved2 = await project.resolveAgentsWorktree(branch);
|
|
56542
|
+
if (!resolved2.ok) return resolved2.response;
|
|
56543
|
+
if (!resolved2.worktree.mux) {
|
|
56544
56544
|
return errorResponse("Open this worktree in the main dashboard before sending messages here", 409);
|
|
56545
56545
|
}
|
|
56546
|
-
const chatSupport = project.resolveWorktreeAgentChatSupport(
|
|
56546
|
+
const chatSupport = project.resolveWorktreeAgentChatSupport(resolved2.worktree, "chat");
|
|
56547
56547
|
if (!chatSupport.ok) {
|
|
56548
56548
|
return errorResponse(chatSupport.error, chatSupport.status);
|
|
56549
56549
|
}
|
|
56550
56550
|
if (chatSupport.data.provider === "codex") {
|
|
56551
56551
|
const sendResult2 = await project.worktreeConversationService.sendWorktreeConversationMessage(
|
|
56552
|
-
|
|
56552
|
+
resolved2.worktree,
|
|
56553
56553
|
text
|
|
56554
56554
|
);
|
|
56555
56555
|
if (!sendResult2.ok) {
|
|
56556
56556
|
return errorResponse(sendResult2.error, sendResult2.status);
|
|
56557
56557
|
}
|
|
56558
|
-
await project.setAgentTerminalStale(
|
|
56558
|
+
await project.setAgentTerminalStale(resolved2.worktree, true);
|
|
56559
56559
|
return jsonResponse(sendResult2.data);
|
|
56560
56560
|
}
|
|
56561
|
-
const conversationResult = await project.claudeConversationService.readWorktreeConversation(
|
|
56561
|
+
const conversationResult = await project.claudeConversationService.readWorktreeConversation(resolved2.worktree);
|
|
56562
56562
|
if (!conversationResult.ok) {
|
|
56563
56563
|
return errorResponse(conversationResult.error, conversationResult.status);
|
|
56564
56564
|
}
|
|
56565
56565
|
const streamingResponse = await project.sendClaudeStreamingMessage({
|
|
56566
|
-
worktree:
|
|
56566
|
+
worktree: resolved2.worktree,
|
|
56567
56567
|
text,
|
|
56568
56568
|
conversationId: conversationResult.data.conversation.conversationId
|
|
56569
56569
|
});
|
|
@@ -56591,24 +56591,24 @@ async function apiSendAgentsWorktreeMessage(project, branch, text) {
|
|
|
56591
56591
|
async function apiInterruptAgentsWorktree(project, branch) {
|
|
56592
56592
|
touchDashboardActivity();
|
|
56593
56593
|
await project.disarmOneshotIfArmed(branch, "agents-interrupt");
|
|
56594
|
-
const
|
|
56595
|
-
if (!
|
|
56596
|
-
if (!
|
|
56594
|
+
const resolved2 = await project.resolveAgentsWorktree(branch);
|
|
56595
|
+
if (!resolved2.ok) return resolved2.response;
|
|
56596
|
+
if (!resolved2.worktree.mux) {
|
|
56597
56597
|
return errorResponse("Open this worktree in the main dashboard before interrupting it here", 409);
|
|
56598
56598
|
}
|
|
56599
|
-
const chatSupport = project.resolveWorktreeAgentChatSupport(
|
|
56599
|
+
const chatSupport = project.resolveWorktreeAgentChatSupport(resolved2.worktree, "interrupt");
|
|
56600
56600
|
if (!chatSupport.ok) {
|
|
56601
56601
|
return errorResponse(chatSupport.error, chatSupport.status);
|
|
56602
56602
|
}
|
|
56603
56603
|
if (chatSupport.data.provider === "codex") {
|
|
56604
|
-
const interruptResult2 = await project.worktreeConversationService.interruptWorktreeConversation(
|
|
56604
|
+
const interruptResult2 = await project.worktreeConversationService.interruptWorktreeConversation(resolved2.worktree);
|
|
56605
56605
|
if (!interruptResult2.ok) {
|
|
56606
56606
|
return errorResponse(interruptResult2.error, interruptResult2.status);
|
|
56607
56607
|
}
|
|
56608
|
-
await project.setAgentTerminalStale(
|
|
56608
|
+
await project.setAgentTerminalStale(resolved2.worktree, true);
|
|
56609
56609
|
return jsonResponse(interruptResult2.data);
|
|
56610
56610
|
}
|
|
56611
|
-
const conversationResult = await project.claudeConversationService.readWorktreeConversation(
|
|
56611
|
+
const conversationResult = await project.claudeConversationService.readWorktreeConversation(resolved2.worktree);
|
|
56612
56612
|
if (!conversationResult.ok) {
|
|
56613
56613
|
return errorResponse(conversationResult.error, conversationResult.status);
|
|
56614
56614
|
}
|
|
@@ -56616,7 +56616,7 @@ async function apiInterruptAgentsWorktree(project, branch) {
|
|
|
56616
56616
|
conversationResult.data.conversation.conversationId
|
|
56617
56617
|
);
|
|
56618
56618
|
if (activeClaudeInterrupt.ok) {
|
|
56619
|
-
await project.setAgentTerminalStale(
|
|
56619
|
+
await project.setAgentTerminalStale(resolved2.worktree, true);
|
|
56620
56620
|
return jsonResponse({
|
|
56621
56621
|
conversationId: conversationResult.data.conversation.conversationId,
|
|
56622
56622
|
turnId: activeClaudeInterrupt.turnId,
|
|
@@ -56733,8 +56733,8 @@ function branchRoutes(deps) {
|
|
|
56733
56733
|
deps,
|
|
56734
56734
|
"fetchAvailableBranches",
|
|
56735
56735
|
"Branches",
|
|
56736
|
-
({ runtime }, { query }) => jsonResponse({
|
|
56737
|
-
branches: runtime.lifecycleService.listAvailableBranches({ includeRemote:
|
|
56736
|
+
({ runtime }, { query: query2 }) => jsonResponse({
|
|
56737
|
+
branches: runtime.lifecycleService.listAvailableBranches({ includeRemote: query2.includeRemote === true })
|
|
56738
56738
|
})
|
|
56739
56739
|
);
|
|
56740
56740
|
projectRoute(
|
|
@@ -57601,7 +57601,7 @@ var CACHE_TTL_MS = 3e5;
|
|
|
57601
57601
|
var issueCache = null;
|
|
57602
57602
|
var viewerIdCache = null;
|
|
57603
57603
|
var inProgressStateIdCache = /* @__PURE__ */ new Map();
|
|
57604
|
-
async function postLinearGraphql(
|
|
57604
|
+
async function postLinearGraphql(query2, variables) {
|
|
57605
57605
|
const apiKey = process.env.LINEAR_API_KEY;
|
|
57606
57606
|
if (!apiKey) {
|
|
57607
57607
|
return { ok: false, error: "LINEAR_API_KEY not set" };
|
|
@@ -57613,7 +57613,7 @@ async function postLinearGraphql(query, variables) {
|
|
|
57613
57613
|
"Content-Type": "application/json",
|
|
57614
57614
|
Authorization: apiKey
|
|
57615
57615
|
},
|
|
57616
|
-
body: JSON.stringify(variables ? { query, variables } : { query })
|
|
57616
|
+
body: JSON.stringify(variables ? { query: query2, variables } : { query: query2 })
|
|
57617
57617
|
});
|
|
57618
57618
|
if (!res.ok) {
|
|
57619
57619
|
const text = await res.text();
|
|
@@ -58350,9 +58350,9 @@ function runRoutes(deps) {
|
|
|
58350
58350
|
if (detail === null || detail.run.projectId !== project.prefix) return errorResponse("Run not found", 404);
|
|
58351
58351
|
return jsonResponse(detail);
|
|
58352
58352
|
});
|
|
58353
|
-
projectRoute(app, deps, "fetchRunEvents", "Runs", (project, { params, query }) => {
|
|
58353
|
+
projectRoute(app, deps, "fetchRunEvents", "Runs", (project, { params, query: query2 }) => {
|
|
58354
58354
|
if (!projectRun(project, params.runId)) return errorResponse("Run not found", 404);
|
|
58355
|
-
return jsonResponse(project.runs.presentation.events(params.runId,
|
|
58355
|
+
return jsonResponse(project.runs.presentation.events(params.runId, query2.after));
|
|
58356
58356
|
});
|
|
58357
58357
|
projectRoute(app, deps, "cancelRun", "Runs", async (project, { params: { runId } }) => {
|
|
58358
58358
|
const detail = project.runs.presentation.detail(runId);
|
|
@@ -58376,9 +58376,9 @@ function runRoutes(deps) {
|
|
|
58376
58376
|
if (!result.ok) return errorResponse(result.reason, result.reason === "not_found" ? 404 : 409);
|
|
58377
58377
|
return jsonResponse(project.runs.presentation.detail(runId));
|
|
58378
58378
|
});
|
|
58379
|
-
projectRoute(app, deps, "fetchExecutionTranscript", "Runs", async (project, { params, query }) => {
|
|
58379
|
+
projectRoute(app, deps, "fetchExecutionTranscript", "Runs", async (project, { params, query: query2 }) => {
|
|
58380
58380
|
if (!projectExecution(project, params.executionId)) return errorResponse("Execution not found", 404);
|
|
58381
|
-
const result = await project.runs.transcripts.read(params.executionId,
|
|
58381
|
+
const result = await project.runs.transcripts.read(params.executionId, query2.after);
|
|
58382
58382
|
if (!result.ok)
|
|
58383
58383
|
return errorResponse(
|
|
58384
58384
|
result.reason === "not_found" ? "Execution not found" : "Transcript unavailable",
|
|
@@ -65202,13 +65202,13 @@ var ClaudeConversationService = class {
|
|
|
65202
65202
|
async attachWorktreeConversation(worktree) {
|
|
65203
65203
|
return await this.withResolvedConversation(
|
|
65204
65204
|
worktree,
|
|
65205
|
-
async (
|
|
65205
|
+
async (resolved2) => ok(toWorktreeConversationResponse(worktree, resolved2.conversationMeta, resolved2.session))
|
|
65206
65206
|
);
|
|
65207
65207
|
}
|
|
65208
65208
|
async readWorktreeConversation(worktree) {
|
|
65209
65209
|
return await this.withResolvedConversation(
|
|
65210
65210
|
worktree,
|
|
65211
|
-
async (
|
|
65211
|
+
async (resolved2) => ok(toWorktreeConversationResponse(worktree, resolved2.conversationMeta, resolved2.session))
|
|
65212
65212
|
);
|
|
65213
65213
|
}
|
|
65214
65214
|
async setWorktreeConversationSession(worktree, sessionId) {
|
|
@@ -65232,9 +65232,9 @@ var ClaudeConversationService = class {
|
|
|
65232
65232
|
return err(409, "Worktree chat is only available for Claude worktrees");
|
|
65233
65233
|
}
|
|
65234
65234
|
try {
|
|
65235
|
-
const
|
|
65236
|
-
if (!
|
|
65237
|
-
return await fn(
|
|
65235
|
+
const resolved2 = await this.resolveConversation(worktree);
|
|
65236
|
+
if (!resolved2.ok) return resolved2;
|
|
65237
|
+
return await fn(resolved2.data);
|
|
65238
65238
|
} catch (error61) {
|
|
65239
65239
|
const message = error61 instanceof Error ? error61.message : String(error61);
|
|
65240
65240
|
return err(502, message);
|
|
@@ -65596,11 +65596,11 @@ var EnvironmentDiagnosticsService = class {
|
|
|
65596
65596
|
};
|
|
65597
65597
|
}
|
|
65598
65598
|
async commandCheck(id, label, required2, command, args, remediation) {
|
|
65599
|
-
const
|
|
65600
|
-
if (!
|
|
65599
|
+
const resolved2 = this.resolveCommand(command);
|
|
65600
|
+
if (!resolved2) {
|
|
65601
65601
|
return check2(id, label, "error", required2, `${command} is unavailable`, remediation);
|
|
65602
65602
|
}
|
|
65603
|
-
const result = await this.runCommand(
|
|
65603
|
+
const result = await this.runCommand(resolved2, args);
|
|
65604
65604
|
const summary = result.output.split("\n")[0] ?? "";
|
|
65605
65605
|
if (result.ok) return check2(id, label, "ok", required2, summary || `${command} is available`);
|
|
65606
65606
|
return check2(
|
|
@@ -65628,8 +65628,8 @@ var EnvironmentDiagnosticsService = class {
|
|
|
65628
65628
|
}
|
|
65629
65629
|
async codexAutoReviewCheck() {
|
|
65630
65630
|
const required2 = this.deps.config.workspace.defaultAgent === "codex";
|
|
65631
|
-
const
|
|
65632
|
-
if (!
|
|
65631
|
+
const resolved2 = this.resolveCommand("codex");
|
|
65632
|
+
if (!resolved2) {
|
|
65633
65633
|
return check2(
|
|
65634
65634
|
"codex-auto-review",
|
|
65635
65635
|
"Codex oneshot",
|
|
@@ -65639,7 +65639,7 @@ var EnvironmentDiagnosticsService = class {
|
|
|
65639
65639
|
"Install Codex CLI and ensure it is on PATH"
|
|
65640
65640
|
);
|
|
65641
65641
|
}
|
|
65642
|
-
const result = await this.runCommand(
|
|
65642
|
+
const result = await this.runCommand(resolved2, ["--help"]);
|
|
65643
65643
|
if (result.ok && codexHelpSupportsAutoReview(result.output)) {
|
|
65644
65644
|
return check2("codex-auto-review", "Codex oneshot", "ok", required2, "Automatic approval review is supported");
|
|
65645
65645
|
}
|
|
@@ -68432,9 +68432,9 @@ var WorkflowCatalogService = class {
|
|
|
68432
68432
|
};
|
|
68433
68433
|
}
|
|
68434
68434
|
async createSnapshot(input2) {
|
|
68435
|
-
const
|
|
68436
|
-
if (!
|
|
68437
|
-
const definition =
|
|
68435
|
+
const resolved2 = await this.resolve(input2.name);
|
|
68436
|
+
if (!resolved2.ok) return resolved2;
|
|
68437
|
+
const definition = resolved2.definition;
|
|
68438
68438
|
return {
|
|
68439
68439
|
ok: true,
|
|
68440
68440
|
snapshot: {
|
|
@@ -69269,9 +69269,9 @@ var WorktreeConversationService = class {
|
|
|
69269
69269
|
return err(409, "Worktree chat is only available for Codex worktrees");
|
|
69270
69270
|
}
|
|
69271
69271
|
try {
|
|
69272
|
-
const
|
|
69273
|
-
if (!
|
|
69274
|
-
return await fn(
|
|
69272
|
+
const resolved2 = await this.resolveConversation(worktree, allowCreate);
|
|
69273
|
+
if (!resolved2.ok) return resolved2;
|
|
69274
|
+
return await fn(resolved2.data);
|
|
69275
69275
|
} catch (error61) {
|
|
69276
69276
|
const message = error61 instanceof Error ? error61.message : String(error61);
|
|
69277
69277
|
return err(502, message);
|
|
@@ -69529,7 +69529,7 @@ var DockerComposeEnvironmentProvider = class {
|
|
|
69529
69529
|
if (result.exit.code !== 0) throw new Error(result.stderr.trim());
|
|
69530
69530
|
}
|
|
69531
69531
|
async resume(handle) {
|
|
69532
|
-
const
|
|
69532
|
+
const resolved2 = {
|
|
69533
69533
|
provider: this.id,
|
|
69534
69534
|
configRef: typeof handle.providerRef.value.composeFile === "string" ? handle.providerRef.value.composeFile : null,
|
|
69535
69535
|
configHash: "",
|
|
@@ -69560,7 +69560,7 @@ var DockerComposeEnvironmentProvider = class {
|
|
|
69560
69560
|
isolationRisks: []
|
|
69561
69561
|
}
|
|
69562
69562
|
};
|
|
69563
|
-
return this.start(
|
|
69563
|
+
return this.start(resolved2, handle.scope);
|
|
69564
69564
|
}
|
|
69565
69565
|
async rebuild(environment, scope, previous) {
|
|
69566
69566
|
if (previous) await this.destroy(previous);
|
|
@@ -70219,34 +70219,34 @@ function createProjectEnvironments(deps) {
|
|
|
70219
70219
|
return { ok: false, reason: "selection_required", diagnostics: probe.configRefs };
|
|
70220
70220
|
}
|
|
70221
70221
|
const provider = selected === "devcontainer" ? devContainerProvider : selected === "docker" ? dockerEnvironmentProvider : selected === "compose" ? composeEnvironmentProvider : selected === "dockerfile" ? dockerfileEnvironmentProvider : hostEnvironmentProvider;
|
|
70222
|
-
const
|
|
70222
|
+
const resolved2 = await provider.resolve({
|
|
70223
70223
|
...context,
|
|
70224
70224
|
configRef: selected === "docker" ? input2.profileName ?? void 0 : profile?.environment?.config
|
|
70225
70225
|
});
|
|
70226
|
-
const id = environmentIdFor(scope,
|
|
70226
|
+
const id = environmentIdFor(scope, resolved2.provider, resolved2.configRef);
|
|
70227
70227
|
const existing = environmentStore.get(id);
|
|
70228
70228
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
70229
|
-
const trusted =
|
|
70229
|
+
const trusted = resolved2.provider !== "devcontainer" || resolved2.configRef === null || environmentStore.isTrusted(scope.projectId, resolved2.configRef, resolved2.configHash);
|
|
70230
70230
|
if (!trusted) {
|
|
70231
70231
|
environmentStore.save({
|
|
70232
70232
|
id,
|
|
70233
|
-
provider:
|
|
70233
|
+
provider: resolved2.provider,
|
|
70234
70234
|
scope,
|
|
70235
70235
|
status: "awaiting_trust",
|
|
70236
70236
|
desiredStatus: input2.start ? "ready" : "stopped",
|
|
70237
|
-
workspace:
|
|
70237
|
+
workspace: resolved2.workspace,
|
|
70238
70238
|
providerRef: { schemaVersion: 1, value: {} },
|
|
70239
|
-
configRef:
|
|
70240
|
-
configHash:
|
|
70241
|
-
capabilities:
|
|
70242
|
-
security: { ...
|
|
70239
|
+
configRef: resolved2.configRef,
|
|
70240
|
+
configHash: resolved2.configHash,
|
|
70241
|
+
capabilities: resolved2.capabilities,
|
|
70242
|
+
security: { ...resolved2.security, trusted: false },
|
|
70243
70243
|
error: null,
|
|
70244
70244
|
createdAt: existing?.createdAt ?? now,
|
|
70245
70245
|
updatedAt: now
|
|
70246
70246
|
});
|
|
70247
|
-
return { ok: false, reason: "trust_required", diagnostics:
|
|
70247
|
+
return { ok: false, reason: "trust_required", diagnostics: resolved2.security.reasons, environmentId: id };
|
|
70248
70248
|
}
|
|
70249
|
-
if (input2.start &&
|
|
70249
|
+
if (input2.start && resolved2.provider !== "host" && !hasEnvironmentCapacity(projectEnvironments(), id, MAX_CONCURRENT_PROJECT_ENVIRONMENTS)) {
|
|
70250
70250
|
return {
|
|
70251
70251
|
ok: false,
|
|
70252
70252
|
reason: "unavailable",
|
|
@@ -70254,26 +70254,26 @@ function createProjectEnvironments(deps) {
|
|
|
70254
70254
|
};
|
|
70255
70255
|
}
|
|
70256
70256
|
let handle;
|
|
70257
|
-
const reusable = existing && existing.configHash ===
|
|
70258
|
-
if (!input2.start &&
|
|
70257
|
+
const reusable = existing && existing.configHash === resolved2.configHash && (existing.provider === "host" || existing.providerRef.value.containerId || existing.providerRef.value.containerName);
|
|
70258
|
+
if (!input2.start && resolved2.provider !== "host") {
|
|
70259
70259
|
if (reusable) {
|
|
70260
70260
|
const observed = await provider.inspect(existing);
|
|
70261
70261
|
handle = { ...existing, scope, status: observed.status };
|
|
70262
70262
|
} else {
|
|
70263
70263
|
handle = {
|
|
70264
70264
|
id,
|
|
70265
|
-
provider:
|
|
70265
|
+
provider: resolved2.provider,
|
|
70266
70266
|
scope,
|
|
70267
70267
|
status: "detected",
|
|
70268
|
-
workspace:
|
|
70268
|
+
workspace: resolved2.workspace,
|
|
70269
70269
|
providerRef: { schemaVersion: 1, value: {} }
|
|
70270
70270
|
};
|
|
70271
70271
|
}
|
|
70272
70272
|
} else if (reusable) {
|
|
70273
70273
|
const observed = await provider.inspect(existing);
|
|
70274
|
-
handle = observed.status === "ready" ? { ...existing, scope, status: "ready" } : observed.status === "stopped" ? await provider.resume(existing) : await provider.start(
|
|
70274
|
+
handle = observed.status === "ready" ? { ...existing, scope, status: "ready" } : observed.status === "stopped" ? await provider.resume(existing) : await provider.start(resolved2, scope);
|
|
70275
70275
|
} else {
|
|
70276
|
-
handle = await provider.start(
|
|
70276
|
+
handle = await provider.start(resolved2, scope);
|
|
70277
70277
|
}
|
|
70278
70278
|
if (handle.provider !== "host" && handle.status === "ready") {
|
|
70279
70279
|
try {
|
|
@@ -70285,11 +70285,11 @@ function createProjectEnvironments(deps) {
|
|
|
70285
70285
|
}
|
|
70286
70286
|
environmentStore.save({
|
|
70287
70287
|
...handle,
|
|
70288
|
-
desiredStatus: input2.start ||
|
|
70289
|
-
configRef:
|
|
70290
|
-
configHash:
|
|
70291
|
-
capabilities:
|
|
70292
|
-
security: { ...
|
|
70288
|
+
desiredStatus: input2.start || resolved2.provider === "host" ? "ready" : "stopped",
|
|
70289
|
+
configRef: resolved2.configRef,
|
|
70290
|
+
configHash: resolved2.configHash,
|
|
70291
|
+
capabilities: resolved2.capabilities,
|
|
70292
|
+
security: { ...resolved2.security, trusted: true },
|
|
70293
70293
|
error: null,
|
|
70294
70294
|
createdAt: existing?.createdAt ?? now,
|
|
70295
70295
|
updatedAt: now
|
|
@@ -70597,22 +70597,22 @@ function createProjectEnvironments(deps) {
|
|
|
70597
70597
|
};
|
|
70598
70598
|
}
|
|
70599
70599
|
try {
|
|
70600
|
-
let
|
|
70600
|
+
let resolved2 = resolvedFromRecord(record5);
|
|
70601
70601
|
if (action !== "stop") {
|
|
70602
|
-
|
|
70602
|
+
resolved2 = await provider.resolve({
|
|
70603
70603
|
workspacePath: record5.workspace.hostPath,
|
|
70604
70604
|
projectPath: PROJECT_DIR,
|
|
70605
70605
|
scope: record5.scope,
|
|
70606
70606
|
configRef: record5.configRef ?? void 0
|
|
70607
70607
|
});
|
|
70608
|
-
if (
|
|
70609
|
-
const trusted =
|
|
70608
|
+
if (resolved2.configHash !== record5.configHash && resolved2.provider === "devcontainer") {
|
|
70609
|
+
const trusted = resolved2.configRef !== null && environmentStore.isTrusted(record5.scope.projectId, resolved2.configRef, resolved2.configHash);
|
|
70610
70610
|
if (!trusted) {
|
|
70611
70611
|
saveEnvironment(record5, {
|
|
70612
70612
|
status: "awaiting_trust",
|
|
70613
70613
|
desiredStatus: "ready",
|
|
70614
|
-
configHash:
|
|
70615
|
-
security: { ...
|
|
70614
|
+
configHash: resolved2.configHash,
|
|
70615
|
+
security: { ...resolved2.security, trusted: false }
|
|
70616
70616
|
});
|
|
70617
70617
|
return { ok: false, error: "Environment configuration changed and requires trust", status: 409 };
|
|
70618
70618
|
}
|
|
@@ -70637,13 +70637,13 @@ function createProjectEnvironments(deps) {
|
|
|
70637
70637
|
} else if (action === "rebuild") {
|
|
70638
70638
|
const rebuilding = saveEnvironment(record5, { status: "building", desiredStatus: "ready", error: null });
|
|
70639
70639
|
await deactivateEnvironmentAccess(rebuilding, false);
|
|
70640
|
-
handle = await provider.rebuild(
|
|
70640
|
+
handle = await provider.rebuild(resolved2, rebuilding.scope, rebuilding);
|
|
70641
70641
|
} else {
|
|
70642
70642
|
const observed = record5.status === "ready" ? await provider.inspect(record5) : { status: "missing" };
|
|
70643
70643
|
if (observed.status === "ready") handle = { ...record5, status: "ready" };
|
|
70644
70644
|
else {
|
|
70645
70645
|
const starting = saveEnvironment(record5, { status: "starting", desiredStatus: "ready", error: null });
|
|
70646
|
-
handle = observed.status === "stopped" ? await provider.resume(starting) : await provider.start(
|
|
70646
|
+
handle = observed.status === "stopped" ? await provider.resume(starting) : await provider.start(resolved2, starting.scope);
|
|
70647
70647
|
}
|
|
70648
70648
|
}
|
|
70649
70649
|
if (handle.provider !== "host") handle = await applyEnvironmentResourcePolicy(serverProcessRunner, handle);
|
|
@@ -71391,20 +71391,20 @@ function createProjectApp(runtime, instancePrefix, host) {
|
|
|
71391
71391
|
await reconciliationService.reconcile(PROJECT_DIR);
|
|
71392
71392
|
const state = projectRuntime.getWorktreeByBranch(branch);
|
|
71393
71393
|
if (!state) return { ok: false, error: `Worktree not found: ${branch}`, status: 404 };
|
|
71394
|
-
const
|
|
71395
|
-
if (!
|
|
71394
|
+
const resolved2 = await resolveAgentsWorktree(branch);
|
|
71395
|
+
if (!resolved2.ok) {
|
|
71396
71396
|
return { ok: false, error: `Worktree not found: ${branch}`, status: 404 };
|
|
71397
71397
|
}
|
|
71398
|
-
const chatSupport = resolveWorktreeAgentChatSupport(
|
|
71398
|
+
const chatSupport = resolveWorktreeAgentChatSupport(resolved2.worktree, "chat");
|
|
71399
71399
|
if (!chatSupport.ok) return { ok: false, error: chatSupport.error, status: chatSupport.status };
|
|
71400
|
-
const conversationResult = chatSupport.data.provider === "claude" ? await claudeConversationService.readWorktreeConversation(
|
|
71400
|
+
const conversationResult = chatSupport.data.provider === "claude" ? await claudeConversationService.readWorktreeConversation(resolved2.worktree) : await worktreeConversationService.readWorktreeConversation(resolved2.worktree);
|
|
71401
71401
|
if (!conversationResult.ok) return { ok: false, error: conversationResult.error, status: conversationResult.status };
|
|
71402
71402
|
const prUrl = (state.prs ?? []).find((pr) => pr.state === "open" || pr.state === "merged")?.url ?? null;
|
|
71403
71403
|
const exportInput = {
|
|
71404
71404
|
target,
|
|
71405
71405
|
branch,
|
|
71406
71406
|
baseBranch: state.baseBranch ?? null,
|
|
71407
|
-
agent:
|
|
71407
|
+
agent: resolved2.worktree.agentName ?? null,
|
|
71408
71408
|
prUrl,
|
|
71409
71409
|
conversation: conversationResult.data.conversation,
|
|
71410
71410
|
taskflowVersion: PORTTA_FLOW_VERSION
|
|
@@ -71799,21 +71799,21 @@ function nextConversationMessageOrder(messages) {
|
|
|
71799
71799
|
return messages.reduce((order2, message) => Math.max(order2, message.order + 1), 0);
|
|
71800
71800
|
}
|
|
71801
71801
|
async function loadAgentsConversationInitialState(project, branch) {
|
|
71802
|
-
const
|
|
71803
|
-
if (!
|
|
71802
|
+
const resolved2 = await project.resolveAgentsWorktree(branch);
|
|
71803
|
+
if (!resolved2.ok) {
|
|
71804
71804
|
return {
|
|
71805
71805
|
ok: false,
|
|
71806
|
-
message: await readErrorMessage(
|
|
71806
|
+
message: await readErrorMessage(resolved2.response)
|
|
71807
71807
|
};
|
|
71808
71808
|
}
|
|
71809
|
-
const chatSupport = project.resolveWorktreeAgentChatSupport(
|
|
71809
|
+
const chatSupport = project.resolveWorktreeAgentChatSupport(resolved2.worktree, "chat");
|
|
71810
71810
|
if (!chatSupport.ok) {
|
|
71811
71811
|
return {
|
|
71812
71812
|
ok: false,
|
|
71813
71813
|
message: chatSupport.error
|
|
71814
71814
|
};
|
|
71815
71815
|
}
|
|
71816
|
-
const result = chatSupport.data.provider === "claude" ? await project.claudeConversationService.readWorktreeConversation(
|
|
71816
|
+
const result = chatSupport.data.provider === "claude" ? await project.claudeConversationService.readWorktreeConversation(resolved2.worktree) : await project.worktreeConversationService.readWorktreeConversation(resolved2.worktree);
|
|
71817
71817
|
return result.ok ? { ok: true, data: project.withClaudeLiveConversation(result.data) } : { ok: false, message: result.error };
|
|
71818
71818
|
}
|
|
71819
71819
|
var agentsSocketRoute = {
|
|
@@ -72223,7 +72223,7 @@ function createDaemonTaskflowHost(context) {
|
|
|
72223
72223
|
port: hostListen(env2).port,
|
|
72224
72224
|
workflowBuiltinsDir: taskflowAssets(env2).workflowBuiltinsDir,
|
|
72225
72225
|
version: env2.PORTTA_VERSION || "0.0.0-local",
|
|
72226
|
-
buildDate: true ? "2026-09-
|
|
72226
|
+
buildDate: true ? "2026-09-16T05:05:26Z" : (/* @__PURE__ */ new Date()).toISOString(),
|
|
72227
72227
|
environmentStore: createEnvironmentStore(paths.database),
|
|
72228
72228
|
hostEnvironmentProvider: new HostEnvironmentProvider(),
|
|
72229
72229
|
devContainerProvider: new DevContainerProvider(),
|
|
@@ -73521,16 +73521,12 @@ function matchPath(pattern, path) {
|
|
|
73521
73521
|
|
|
73522
73522
|
// ../core/src/activity.ts
|
|
73523
73523
|
var BASE_ACTIVITY_KINDS = [
|
|
73524
|
-
"
|
|
73525
|
-
"
|
|
73526
|
-
"
|
|
73527
|
-
"
|
|
73528
|
-
"
|
|
73529
|
-
"
|
|
73530
|
-
"task.linked",
|
|
73531
|
-
"task.synced",
|
|
73532
|
-
"task.conflict",
|
|
73533
|
-
"task.deleted",
|
|
73524
|
+
"issue.created",
|
|
73525
|
+
"issue.updated",
|
|
73526
|
+
"issue.state",
|
|
73527
|
+
"issue.assigned",
|
|
73528
|
+
"issue.comment",
|
|
73529
|
+
"issue.linked",
|
|
73534
73530
|
"session.started",
|
|
73535
73531
|
"session.ended",
|
|
73536
73532
|
"session.abandoned",
|
|
@@ -73557,70 +73553,9 @@ var SESSION_ABANDON_AFTER_SECONDS = 6 * 60 * 60;
|
|
|
73557
73553
|
// ../core/src/repos-scan.ts
|
|
73558
73554
|
var INSTRUCTION_MAX_BYTES = 64 * 1024;
|
|
73559
73555
|
|
|
73560
|
-
// ../core/src/
|
|
73561
|
-
|
|
73562
|
-
var
|
|
73563
|
-
statuses: TASK_STATUSES.map((id) => ({ id, label: null })),
|
|
73564
|
-
defaultStatus: "backlog"
|
|
73565
|
-
};
|
|
73566
|
-
var TASK_PRIORITIES = ["low", "medium", "high", "urgent"];
|
|
73567
|
-
var TASK_DRAFT_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
73568
|
-
var TASK_TYPE_CATALOG = [
|
|
73569
|
-
{ id: "feature", tone: "accent", aliases: ["feat", "features", "enhancement-request", "story"] },
|
|
73570
|
-
{ id: "bug", tone: "danger", aliases: ["fix", "bugfix", "defect", "regression"] },
|
|
73571
|
-
{ id: "improvement", tone: "info", aliases: ["enhancement", "refactor", "perf", "performance"] },
|
|
73572
|
-
{ id: "chore", tone: "neutral", aliases: ["task", "maintenance", "build", "ci", "deps"] },
|
|
73573
|
-
{ id: "research", tone: "agent", aliases: ["spike", "investigation", "discovery"] },
|
|
73574
|
-
{ id: "documentation", tone: "outline", aliases: ["docs", "doc"] }
|
|
73575
|
-
];
|
|
73576
|
-
var TASK_TYPES = TASK_TYPE_CATALOG.map((entry) => entry.id);
|
|
73577
|
-
|
|
73578
|
-
// ../core/src/task-example.ts
|
|
73579
|
-
var PortableKey = external_exports.string().min(1).max(80).regex(/^[a-z0-9][a-z0-9._-]*$/i);
|
|
73580
|
-
var ProjectRelativePath = external_exports.string().min(1).max(255).regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/).refine((value) => !value.includes(".."));
|
|
73581
|
-
var RepositoryRelativePath = external_exports.string().min(1).max(255).regex(/^[A-Za-z0-9][A-Za-z0-9._-]*(\/[A-Za-z0-9][A-Za-z0-9._-]*)?$/).refine((value) => !value.includes(".."));
|
|
73582
|
-
var ExampleComment = external_exports.object({
|
|
73583
|
-
key: PortableKey,
|
|
73584
|
-
actor: external_exports.string().min(1).max(64),
|
|
73585
|
-
actorKind: external_exports.enum(["human", "agent", "system"]).optional(),
|
|
73586
|
-
body: external_exports.string().min(1).max(65536)
|
|
73587
|
-
}).strict();
|
|
73588
|
-
var ExampleTask = external_exports.lazy(() => external_exports.object({
|
|
73589
|
-
key: PortableKey,
|
|
73590
|
-
title: external_exports.string().min(1).max(200),
|
|
73591
|
-
description: external_exports.string().max(65536).nullable().optional(),
|
|
73592
|
-
status: external_exports.enum(TASK_STATUSES).optional(),
|
|
73593
|
-
priority: external_exports.enum(TASK_PRIORITIES).nullable().optional(),
|
|
73594
|
-
type: external_exports.string().max(32).nullable().optional(),
|
|
73595
|
-
labels: external_exports.array(external_exports.string().min(1).max(64)).max(32).optional(),
|
|
73596
|
-
assignee: external_exports.string().max(64).nullable().optional(),
|
|
73597
|
-
agent: external_exports.string().max(64).nullable().optional(),
|
|
73598
|
-
repository: external_exports.string().min(1).max(128).nullable().optional(),
|
|
73599
|
-
environment: external_exports.string().min(1).max(255).nullable().optional(),
|
|
73600
|
-
service: external_exports.string().max(64).nullable().optional(),
|
|
73601
|
-
dueAt: external_exports.string().regex(/^\d{4}-\d{2}-\d{2}(T[\d:.+-Z]+)?$/).nullable().optional(),
|
|
73602
|
-
parent: PortableKey.nullable().optional(),
|
|
73603
|
-
comments: external_exports.array(ExampleComment).max(32).optional(),
|
|
73604
|
-
subtasks: external_exports.array(ExampleTask).max(32).optional()
|
|
73605
|
-
}).strict());
|
|
73606
|
-
var ExampleRepository = external_exports.object({
|
|
73607
|
-
key: PortableKey,
|
|
73608
|
-
name: external_exports.string().min(1).max(128),
|
|
73609
|
-
role: external_exports.string().max(32).nullable().optional(),
|
|
73610
|
-
relativePath: RepositoryRelativePath.optional(),
|
|
73611
|
-
remoteUrl: external_exports.string().min(1).max(512).optional()
|
|
73612
|
-
}).strict();
|
|
73613
|
-
var ExampleDocument = external_exports.object({
|
|
73614
|
-
schemaVersion: external_exports.literal(1),
|
|
73615
|
-
project: external_exports.object({
|
|
73616
|
-
slug: external_exports.string().min(1).max(64).regex(/^[a-z0-9][a-z0-9-]*$/),
|
|
73617
|
-
name: external_exports.string().min(1).max(120),
|
|
73618
|
-
description: external_exports.string().max(2e3).nullable().optional(),
|
|
73619
|
-
relativePath: ProjectRelativePath.optional()
|
|
73620
|
-
}).strict(),
|
|
73621
|
-
repositories: external_exports.array(ExampleRepository).max(32).optional(),
|
|
73622
|
-
tasks: external_exports.array(ExampleTask).max(64)
|
|
73623
|
-
}).strict();
|
|
73556
|
+
// ../core/src/database-config.ts
|
|
73557
|
+
import { join as join29 } from "node:path";
|
|
73558
|
+
var DATABASE_RELATIVE_PATH = join29("state", "panel", "portta.db");
|
|
73624
73559
|
|
|
73625
73560
|
// ../../node_modules/ws/wrapper.mjs
|
|
73626
73561
|
var import_stream = __toESM(require_stream(), 1);
|
|
@@ -73629,6 +73564,462 @@ var import_sender = __toESM(require_sender(), 1);
|
|
|
73629
73564
|
var import_websocket = __toESM(require_websocket(), 1);
|
|
73630
73565
|
var import_websocket_server = __toESM(require_websocket_server(), 1);
|
|
73631
73566
|
|
|
73567
|
+
// ../host/src/forge/gh.ts
|
|
73568
|
+
import { execFile as execFile2 } from "node:child_process";
|
|
73569
|
+
var TIMEOUT_MS = 2e4;
|
|
73570
|
+
var ISSUE_PAGE_LIMIT = 100;
|
|
73571
|
+
var ForgeError = class extends Error {
|
|
73572
|
+
kind;
|
|
73573
|
+
hint;
|
|
73574
|
+
constructor(kind, message, hint) {
|
|
73575
|
+
super(message);
|
|
73576
|
+
this.name = "ForgeError";
|
|
73577
|
+
this.kind = kind;
|
|
73578
|
+
this.hint = hint;
|
|
73579
|
+
}
|
|
73580
|
+
};
|
|
73581
|
+
var FORGE_STATUS = {
|
|
73582
|
+
unavailable: 503,
|
|
73583
|
+
unauthenticated: 401,
|
|
73584
|
+
forbidden: 403,
|
|
73585
|
+
"not-found": 404,
|
|
73586
|
+
"rate-limited": 429,
|
|
73587
|
+
timeout: 504,
|
|
73588
|
+
failed: 502
|
|
73589
|
+
};
|
|
73590
|
+
function classify(stderr) {
|
|
73591
|
+
const text = stderr.toLowerCase();
|
|
73592
|
+
if (text.includes("gh auth login") || text.includes("not logged") || text.includes("authentication token")) {
|
|
73593
|
+
return "unauthenticated";
|
|
73594
|
+
}
|
|
73595
|
+
if (text.includes("rate limit") || text.includes("secondary rate")) return "rate-limited";
|
|
73596
|
+
if (text.includes("could not resolve to") || text.includes("not found") || text.includes("404")) return "not-found";
|
|
73597
|
+
if (text.includes("forbidden") || text.includes("403") || text.includes("must have admin")) return "forbidden";
|
|
73598
|
+
return "failed";
|
|
73599
|
+
}
|
|
73600
|
+
var resolved;
|
|
73601
|
+
function ghPath() {
|
|
73602
|
+
if (resolved === void 0) resolved = resolveHostToolThoroughly("gh");
|
|
73603
|
+
if (!resolved) {
|
|
73604
|
+
throw new ForgeError(
|
|
73605
|
+
"unavailable",
|
|
73606
|
+
"the GitHub CLI is not installed on this host",
|
|
73607
|
+
"install gh (https://cli.github.com) and run gh auth login, then reload"
|
|
73608
|
+
);
|
|
73609
|
+
}
|
|
73610
|
+
return resolved;
|
|
73611
|
+
}
|
|
73612
|
+
function runGh2(args, options = {}) {
|
|
73613
|
+
const binary = ghPath();
|
|
73614
|
+
return new Promise((resolve21, reject2) => {
|
|
73615
|
+
const child = execFile2(
|
|
73616
|
+
binary,
|
|
73617
|
+
args,
|
|
73618
|
+
{
|
|
73619
|
+
cwd: options.cwd,
|
|
73620
|
+
timeout: TIMEOUT_MS,
|
|
73621
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
73622
|
+
// `gh` colours and paginates when it thinks it is talking to a person.
|
|
73623
|
+
// Both would end up in the JSON the panel parses.
|
|
73624
|
+
env: { ...process.env, GH_PAGER: "cat", NO_COLOR: "1", GH_PROMPT_DISABLED: "1" }
|
|
73625
|
+
},
|
|
73626
|
+
(error61, stdout, stderr) => {
|
|
73627
|
+
if (!error61) return resolve21(stdout);
|
|
73628
|
+
if (error61.killed) {
|
|
73629
|
+
return reject2(new ForgeError("timeout", `gh ${args[0]} ${args[1] ?? ""} took longer than ${TIMEOUT_MS / 1e3}s`));
|
|
73630
|
+
}
|
|
73631
|
+
const detail = (stderr || error61.message).trim();
|
|
73632
|
+
reject2(new ForgeError(classify(detail), detail.split("\n")[0] ?? "gh failed", hintFor(classify(detail))));
|
|
73633
|
+
}
|
|
73634
|
+
);
|
|
73635
|
+
if (options.stdin !== void 0) {
|
|
73636
|
+
child.stdin?.end(options.stdin);
|
|
73637
|
+
}
|
|
73638
|
+
});
|
|
73639
|
+
}
|
|
73640
|
+
function hintFor(kind) {
|
|
73641
|
+
if (kind === "unauthenticated") return "run gh auth login on this host";
|
|
73642
|
+
if (kind === "rate-limited") return "GitHub is rate-limiting this account; try again shortly";
|
|
73643
|
+
if (kind === "forbidden") return "this account cannot see or write that repository";
|
|
73644
|
+
return void 0;
|
|
73645
|
+
}
|
|
73646
|
+
async function runGhJson(args, options) {
|
|
73647
|
+
const stdout = await runGh2(args, options);
|
|
73648
|
+
const text = stdout.trim();
|
|
73649
|
+
if (text === "") return [];
|
|
73650
|
+
try {
|
|
73651
|
+
return JSON.parse(text);
|
|
73652
|
+
} catch {
|
|
73653
|
+
throw new ForgeError("failed", `gh answered something that is not JSON: ${text.slice(0, 200)}`);
|
|
73654
|
+
}
|
|
73655
|
+
}
|
|
73656
|
+
async function ghStatus() {
|
|
73657
|
+
let binary;
|
|
73658
|
+
try {
|
|
73659
|
+
binary = ghPath();
|
|
73660
|
+
} catch (error61) {
|
|
73661
|
+
return { available: false, authenticated: false, account: null, detail: error61.message };
|
|
73662
|
+
}
|
|
73663
|
+
try {
|
|
73664
|
+
const user = await runGhJson(["api", "user", "--jq", "{login: .login}"]);
|
|
73665
|
+
return { available: true, authenticated: true, account: user.login, detail: null };
|
|
73666
|
+
} catch (error61) {
|
|
73667
|
+
const failure = error61 instanceof ForgeError ? error61 : null;
|
|
73668
|
+
return {
|
|
73669
|
+
available: true,
|
|
73670
|
+
authenticated: false,
|
|
73671
|
+
account: null,
|
|
73672
|
+
detail: failure?.message ?? `gh at ${binary} could not be asked who it is`
|
|
73673
|
+
};
|
|
73674
|
+
}
|
|
73675
|
+
}
|
|
73676
|
+
|
|
73677
|
+
// ../host/src/forge/issues.ts
|
|
73678
|
+
var LIST_FIELDS = [
|
|
73679
|
+
"number",
|
|
73680
|
+
"title",
|
|
73681
|
+
"state",
|
|
73682
|
+
"stateReason",
|
|
73683
|
+
"labels",
|
|
73684
|
+
"assignees",
|
|
73685
|
+
"milestone",
|
|
73686
|
+
"author",
|
|
73687
|
+
"comments",
|
|
73688
|
+
"createdAt",
|
|
73689
|
+
"updatedAt",
|
|
73690
|
+
"url"
|
|
73691
|
+
].join(",");
|
|
73692
|
+
var VIEW_FIELDS = `${LIST_FIELDS},body`;
|
|
73693
|
+
function listIssues(repo, query2 = {}) {
|
|
73694
|
+
const args = ["issue", "list", "--repo", repo, "--json", LIST_FIELDS, "--limit", String(query2.limit ?? ISSUE_PAGE_LIMIT)];
|
|
73695
|
+
args.push("--state", query2.state ?? "open");
|
|
73696
|
+
if (query2.assignee) args.push("--assignee", query2.assignee);
|
|
73697
|
+
if (query2.label) args.push("--label", query2.label);
|
|
73698
|
+
if (query2.milestone) args.push("--milestone", query2.milestone);
|
|
73699
|
+
if (query2.search) args.push("--search", query2.search);
|
|
73700
|
+
return runGhJson(args);
|
|
73701
|
+
}
|
|
73702
|
+
async function viewIssue(repo, number4) {
|
|
73703
|
+
const [issue2, comments] = await Promise.all([
|
|
73704
|
+
runGhJson(["issue", "view", String(number4), "--repo", repo, "--json", VIEW_FIELDS]),
|
|
73705
|
+
runGhJson([
|
|
73706
|
+
"api",
|
|
73707
|
+
`repos/${repo}/issues/${number4}/comments`,
|
|
73708
|
+
"--paginate",
|
|
73709
|
+
"--jq",
|
|
73710
|
+
"[.[] | {id: (.id|tostring), body: .body, createdAt: .created_at, updatedAt: .updated_at, url: .html_url, author: {login: .user.login, avatarUrl: .user.avatar_url}}]"
|
|
73711
|
+
]).catch(() => [])
|
|
73712
|
+
]);
|
|
73713
|
+
return { ...issue2, comments };
|
|
73714
|
+
}
|
|
73715
|
+
async function createIssue(repo, draft) {
|
|
73716
|
+
const args = ["issue", "create", "--repo", repo, "--title", draft.title, "--body", draft.body ?? ""];
|
|
73717
|
+
for (const label of draft.labels ?? []) args.push("--label", label);
|
|
73718
|
+
for (const assignee of draft.assignees ?? []) args.push("--assignee", assignee);
|
|
73719
|
+
if (draft.milestone) args.push("--milestone", draft.milestone);
|
|
73720
|
+
const url2 = (await runGh2(args)).trim().split("\n").pop() ?? "";
|
|
73721
|
+
const number4 = Number(/\/(\d+)\s*$/.exec(url2)?.[1]);
|
|
73722
|
+
if (!Number.isInteger(number4)) {
|
|
73723
|
+
throw new Error(`gh issue create did not answer with an issue URL: ${url2.slice(0, 200)}`);
|
|
73724
|
+
}
|
|
73725
|
+
return viewIssue(repo, number4);
|
|
73726
|
+
}
|
|
73727
|
+
async function editIssue(repo, number4, patch) {
|
|
73728
|
+
const fields = [];
|
|
73729
|
+
if (patch.title !== void 0) fields.push("--title", patch.title);
|
|
73730
|
+
if (patch.body !== void 0) fields.push("--body", patch.body);
|
|
73731
|
+
for (const label of patch.addLabels ?? []) fields.push("--add-label", label);
|
|
73732
|
+
for (const label of patch.removeLabels ?? []) fields.push("--remove-label", label);
|
|
73733
|
+
for (const login of patch.addAssignees ?? []) fields.push("--add-assignee", login);
|
|
73734
|
+
for (const login of patch.removeAssignees ?? []) fields.push("--remove-assignee", login);
|
|
73735
|
+
if (patch.milestone !== void 0) fields.push("--milestone", patch.milestone);
|
|
73736
|
+
if (fields.length === 0) return;
|
|
73737
|
+
await runGh2(["issue", "edit", String(number4), "--repo", repo, ...fields]);
|
|
73738
|
+
}
|
|
73739
|
+
async function setIssueState(repo, number4, state, reason) {
|
|
73740
|
+
const verb = state === "closed" ? "close" : "reopen";
|
|
73741
|
+
const why = state === "closed" && reason ? ["--reason", reason] : [];
|
|
73742
|
+
await runGh2(["issue", verb, String(number4), "--repo", repo, ...why]);
|
|
73743
|
+
}
|
|
73744
|
+
async function commentOnIssue(repo, number4, body) {
|
|
73745
|
+
await runGh2(["issue", "comment", String(number4), "--repo", repo, "--body-file", "-"], { stdin: body });
|
|
73746
|
+
}
|
|
73747
|
+
async function issueVocabulary(repo) {
|
|
73748
|
+
const [labels, assignees, milestones] = await Promise.all([
|
|
73749
|
+
runGhJson(["label", "list", "--repo", repo, "--json", "name,color,description", "--limit", "200"]).catch(() => []),
|
|
73750
|
+
runGhJson([
|
|
73751
|
+
"api",
|
|
73752
|
+
`repos/${repo}/assignees`,
|
|
73753
|
+
"--paginate",
|
|
73754
|
+
"--jq",
|
|
73755
|
+
"[.[] | {login: .login, avatarUrl: .avatar_url}]"
|
|
73756
|
+
]).catch(() => []),
|
|
73757
|
+
runGhJson([
|
|
73758
|
+
"api",
|
|
73759
|
+
`repos/${repo}/milestones?state=all`,
|
|
73760
|
+
"--paginate",
|
|
73761
|
+
"--jq",
|
|
73762
|
+
"[.[] | {number: .number, title: .title, state: .state, dueOn: .due_on}]"
|
|
73763
|
+
]).catch(() => [])
|
|
73764
|
+
]);
|
|
73765
|
+
return { labels, assignees, milestones };
|
|
73766
|
+
}
|
|
73767
|
+
function assignedIssues(limit = 50) {
|
|
73768
|
+
return runGhJson([
|
|
73769
|
+
"search",
|
|
73770
|
+
"issues",
|
|
73771
|
+
"--assignee",
|
|
73772
|
+
"@me",
|
|
73773
|
+
"--state",
|
|
73774
|
+
"open",
|
|
73775
|
+
"--limit",
|
|
73776
|
+
String(limit),
|
|
73777
|
+
"--json",
|
|
73778
|
+
"number,title,state,labels,assignees,author,createdAt,updatedAt,url,repository"
|
|
73779
|
+
]);
|
|
73780
|
+
}
|
|
73781
|
+
|
|
73782
|
+
// ../host/src/forge/linear.ts
|
|
73783
|
+
var CLOSED_TYPES = /* @__PURE__ */ new Set(["completed", "canceled"]);
|
|
73784
|
+
function isClosed(issue2) {
|
|
73785
|
+
return CLOSED_TYPES.has(issue2.state?.type ?? "");
|
|
73786
|
+
}
|
|
73787
|
+
var ISSUE_FIELDS = `
|
|
73788
|
+
id identifier title url createdAt updatedAt
|
|
73789
|
+
state { name type }
|
|
73790
|
+
assignee { name displayName avatarUrl }
|
|
73791
|
+
creator { name displayName avatarUrl }
|
|
73792
|
+
labels { nodes { name color } }
|
|
73793
|
+
project { name }
|
|
73794
|
+
`;
|
|
73795
|
+
var TEAM_ISSUES_QUERY = `
|
|
73796
|
+
query TeamIssues($key: String!, $first: Int!, $closed: Boolean!) {
|
|
73797
|
+
issues(
|
|
73798
|
+
filter: { team: { key: { eq: $key } }, state: { type: { nin: ["completed", "canceled"] } } }
|
|
73799
|
+
orderBy: updatedAt
|
|
73800
|
+
first: $first
|
|
73801
|
+
) @skip(if: $closed) { nodes { ${ISSUE_FIELDS} } }
|
|
73802
|
+
all: issues(filter: { team: { key: { eq: $key } } }, orderBy: updatedAt, first: $first)
|
|
73803
|
+
@include(if: $closed) { nodes { ${ISSUE_FIELDS} } }
|
|
73804
|
+
}
|
|
73805
|
+
`;
|
|
73806
|
+
var ISSUE_QUERY = `
|
|
73807
|
+
query Issue($id: String!) {
|
|
73808
|
+
issue(id: $id) {
|
|
73809
|
+
${ISSUE_FIELDS}
|
|
73810
|
+
description
|
|
73811
|
+
comments(first: 100) { nodes { id body createdAt updatedAt url user { name avatarUrl } } }
|
|
73812
|
+
}
|
|
73813
|
+
}
|
|
73814
|
+
`;
|
|
73815
|
+
async function query(document, variables) {
|
|
73816
|
+
const response = await postLinearGraphql(document, variables);
|
|
73817
|
+
if (!response.ok) {
|
|
73818
|
+
const kind = response.error.includes("LINEAR_API_KEY") ? "unauthenticated" : "failed";
|
|
73819
|
+
throw new ForgeError(kind, response.error, kind === "unauthenticated" ? "set LINEAR_API_KEY on this host" : void 0);
|
|
73820
|
+
}
|
|
73821
|
+
const body = response.data;
|
|
73822
|
+
if (body.errors?.length) throw new ForgeError("failed", body.errors.map((e) => e.message).join("; "));
|
|
73823
|
+
if (!body.data) throw new ForgeError("failed", "Linear answered with no data");
|
|
73824
|
+
return body.data;
|
|
73825
|
+
}
|
|
73826
|
+
var TEAM_KEY = /^[A-Z][A-Z0-9]{0,9}$/;
|
|
73827
|
+
function teamKeyOf(raw2) {
|
|
73828
|
+
const key = (raw2 ?? "").trim().toUpperCase();
|
|
73829
|
+
if (!TEAM_KEY.test(key)) throw new ForgeError("failed", `not a Linear team key: ${raw2 ?? "(none)"}`, "expected something like ENG");
|
|
73830
|
+
return key;
|
|
73831
|
+
}
|
|
73832
|
+
async function listTeamIssues(teamKey, options = {}) {
|
|
73833
|
+
const closed = options.state === "closed" || options.state === "all";
|
|
73834
|
+
const data = await query(
|
|
73835
|
+
TEAM_ISSUES_QUERY,
|
|
73836
|
+
{ key: teamKey, first: options.limit ?? 100, closed }
|
|
73837
|
+
);
|
|
73838
|
+
const nodes = (closed ? data.all?.nodes : data.issues?.nodes) ?? [];
|
|
73839
|
+
return options.state === "closed" ? nodes.filter(isClosed) : nodes;
|
|
73840
|
+
}
|
|
73841
|
+
function viewIssue2(identifier) {
|
|
73842
|
+
return query(ISSUE_QUERY, { id: identifier }).then((data) => {
|
|
73843
|
+
if (!data.issue) throw new ForgeError("not-found", `no Linear issue ${identifier}`);
|
|
73844
|
+
return data.issue;
|
|
73845
|
+
});
|
|
73846
|
+
}
|
|
73847
|
+
var CREATE_MUTATION = `
|
|
73848
|
+
mutation Create($input: IssueCreateInput!) {
|
|
73849
|
+
issueCreate(input: $input) { success issue { identifier } }
|
|
73850
|
+
}
|
|
73851
|
+
`;
|
|
73852
|
+
async function createIssue2(teamKey, draft) {
|
|
73853
|
+
const team = await query(
|
|
73854
|
+
"query Team($key: String!) { teams(filter: { key: { eq: $key } }, first: 1) { nodes { id } } }",
|
|
73855
|
+
{ key: teamKey }
|
|
73856
|
+
);
|
|
73857
|
+
const teamId = team.teams.nodes[0]?.id;
|
|
73858
|
+
if (!teamId) throw new ForgeError("not-found", `no Linear team ${teamKey}`);
|
|
73859
|
+
const created = await query(
|
|
73860
|
+
CREATE_MUTATION,
|
|
73861
|
+
{ input: { teamId, title: draft.title, description: draft.body ?? "" } }
|
|
73862
|
+
);
|
|
73863
|
+
const identifier = created.issueCreate.issue?.identifier;
|
|
73864
|
+
if (!created.issueCreate.success || !identifier) throw new ForgeError("failed", "Linear refused to create the issue");
|
|
73865
|
+
return viewIssue2(identifier);
|
|
73866
|
+
}
|
|
73867
|
+
var UPDATE_MUTATION = `
|
|
73868
|
+
mutation Update($id: String!, $input: IssueUpdateInput!) {
|
|
73869
|
+
issueUpdate(id: $id, input: $input) { success }
|
|
73870
|
+
}
|
|
73871
|
+
`;
|
|
73872
|
+
async function editIssue2(identifier, patch) {
|
|
73873
|
+
const issue2 = await viewIssue2(identifier);
|
|
73874
|
+
const input2 = {};
|
|
73875
|
+
if (patch.title !== void 0) input2["title"] = patch.title;
|
|
73876
|
+
if (patch.body !== void 0) input2["description"] = patch.body;
|
|
73877
|
+
if (patch.state && patch.state === "closed" !== isClosed(issue2)) {
|
|
73878
|
+
input2["stateId"] = await stateIdFor(identifier, patch.state);
|
|
73879
|
+
}
|
|
73880
|
+
if (Object.keys(input2).length > 0) {
|
|
73881
|
+
const result = await query(UPDATE_MUTATION, { id: issue2.id, input: input2 });
|
|
73882
|
+
if (!result.issueUpdate.success) throw new ForgeError("failed", "Linear refused the change");
|
|
73883
|
+
}
|
|
73884
|
+
return viewIssue2(identifier);
|
|
73885
|
+
}
|
|
73886
|
+
async function stateIdFor(identifier, state) {
|
|
73887
|
+
const wanted = state === "closed" ? ["completed"] : ["unstarted", "backlog", "started"];
|
|
73888
|
+
const data = await query(
|
|
73889
|
+
"query States($id: String!) { issue(id: $id) { team { states { nodes { id type position } } } } }",
|
|
73890
|
+
{ id: identifier }
|
|
73891
|
+
);
|
|
73892
|
+
const states2 = data.issue?.team.states.nodes ?? [];
|
|
73893
|
+
const match2 = wanted.flatMap((type) => states2.filter((s) => s.type === type)).sort((a, b) => a.position - b.position)[0];
|
|
73894
|
+
if (!match2) throw new ForgeError("failed", `this Linear team has no ${state === "closed" ? "completed" : "open"} state`);
|
|
73895
|
+
return match2.id;
|
|
73896
|
+
}
|
|
73897
|
+
var COMMENT_MUTATION = `
|
|
73898
|
+
mutation Comment($input: CommentCreateInput!) { commentCreate(input: $input) { success } }
|
|
73899
|
+
`;
|
|
73900
|
+
async function commentOnIssue2(identifier, body) {
|
|
73901
|
+
const issue2 = await viewIssue2(identifier);
|
|
73902
|
+
const result = await query(COMMENT_MUTATION, {
|
|
73903
|
+
input: { issueId: issue2.id, body }
|
|
73904
|
+
});
|
|
73905
|
+
if (!result.commentCreate.success) throw new ForgeError("failed", "Linear refused the comment");
|
|
73906
|
+
return viewIssue2(identifier);
|
|
73907
|
+
}
|
|
73908
|
+
async function linearStatus() {
|
|
73909
|
+
if (!process.env["LINEAR_API_KEY"]?.trim()) {
|
|
73910
|
+
return { available: false, authenticated: false, account: null, detail: "LINEAR_API_KEY is not set on this host" };
|
|
73911
|
+
}
|
|
73912
|
+
try {
|
|
73913
|
+
const data = await query("query { viewer { name } }", {});
|
|
73914
|
+
return { available: true, authenticated: true, account: data.viewer.name, detail: null };
|
|
73915
|
+
} catch (error61) {
|
|
73916
|
+
return { available: true, authenticated: false, account: null, detail: error61.message };
|
|
73917
|
+
}
|
|
73918
|
+
}
|
|
73919
|
+
|
|
73920
|
+
// ../host/src/forge/routes.ts
|
|
73921
|
+
var REPO = /^[A-Za-z0-9][A-Za-z0-9._-]*\/[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
73922
|
+
function repositoryOf(raw2) {
|
|
73923
|
+
const repo = raw2?.trim() ?? "";
|
|
73924
|
+
if (!REPO.test(repo)) throw new ForgeError("failed", `not a repository: ${repo || "(none)"}`, "expected owner/name");
|
|
73925
|
+
return repo;
|
|
73926
|
+
}
|
|
73927
|
+
var IDENTIFIER = /^[A-Z][A-Z0-9]{0,9}-\d{1,9}$/;
|
|
73928
|
+
function identifierOf(raw2) {
|
|
73929
|
+
const identifier = (raw2 ?? "").trim().toUpperCase();
|
|
73930
|
+
if (!IDENTIFIER.test(identifier)) throw new ForgeError("not-found", `not a Linear issue: ${raw2 ?? "(none)"}`);
|
|
73931
|
+
return identifier;
|
|
73932
|
+
}
|
|
73933
|
+
function numberOf(raw2) {
|
|
73934
|
+
const value = Number(raw2);
|
|
73935
|
+
if (!Number.isInteger(value) || value <= 0) throw new ForgeError("not-found", `not an issue number: ${raw2}`);
|
|
73936
|
+
return value;
|
|
73937
|
+
}
|
|
73938
|
+
function createForgeRoutes() {
|
|
73939
|
+
const api = new Hono2();
|
|
73940
|
+
api.onError((error61, c) => {
|
|
73941
|
+
if (error61 instanceof ForgeError) {
|
|
73942
|
+
return c.json({ error: error61.message, kind: error61.kind, hint: error61.hint ?? null }, { status: FORGE_STATUS[error61.kind] });
|
|
73943
|
+
}
|
|
73944
|
+
return c.json({ error: String(error61), kind: "failed", hint: null }, { status: 502 });
|
|
73945
|
+
});
|
|
73946
|
+
api.get("/status", async (c) => {
|
|
73947
|
+
const [github, linear] = await Promise.all([ghStatus(), linearStatus()]);
|
|
73948
|
+
return c.json({ github, linear });
|
|
73949
|
+
});
|
|
73950
|
+
api.get("/linear/issues", async (c) => {
|
|
73951
|
+
const state = c.req.query("state");
|
|
73952
|
+
const limit = Number(c.req.query("limit"));
|
|
73953
|
+
return c.json(
|
|
73954
|
+
await listTeamIssues(teamKeyOf(c.req.query("team")), {
|
|
73955
|
+
state: state === "closed" || state === "all" ? state : "open",
|
|
73956
|
+
limit: Number.isInteger(limit) && limit > 0 ? limit : void 0
|
|
73957
|
+
})
|
|
73958
|
+
);
|
|
73959
|
+
});
|
|
73960
|
+
api.get("/linear/issues/:identifier", async (c) => c.json(await viewIssue2(identifierOf(c.req.param("identifier")))));
|
|
73961
|
+
api.post("/linear/issues", async (c) => {
|
|
73962
|
+
const draft = await c.req.json();
|
|
73963
|
+
if (!draft?.title?.trim()) throw new ForgeError("failed", "an issue needs a title");
|
|
73964
|
+
return c.json(await createIssue2(teamKeyOf(c.req.query("team")), { title: draft.title, body: draft.body }), { status: 201 });
|
|
73965
|
+
});
|
|
73966
|
+
api.patch(
|
|
73967
|
+
"/linear/issues/:identifier",
|
|
73968
|
+
async (c) => c.json(await editIssue2(identifierOf(c.req.param("identifier")), await c.req.json()))
|
|
73969
|
+
);
|
|
73970
|
+
api.post("/linear/issues/:identifier/comments", async (c) => {
|
|
73971
|
+
const { body } = await c.req.json();
|
|
73972
|
+
if (!body?.trim()) throw new ForgeError("failed", "a comment needs a body");
|
|
73973
|
+
return c.json(await commentOnIssue2(identifierOf(c.req.param("identifier")), body));
|
|
73974
|
+
});
|
|
73975
|
+
api.get("/issues", async (c) => {
|
|
73976
|
+
const query2 = c.req.query();
|
|
73977
|
+
const state = query2["state"];
|
|
73978
|
+
return c.json(
|
|
73979
|
+
await listIssues(repositoryOf(query2["repo"]), {
|
|
73980
|
+
state: state === "closed" || state === "all" ? state : "open",
|
|
73981
|
+
assignee: query2["assignee"],
|
|
73982
|
+
label: query2["label"],
|
|
73983
|
+
milestone: query2["milestone"],
|
|
73984
|
+
search: query2["q"],
|
|
73985
|
+
limit: query2["limit"] ? Number(query2["limit"]) : void 0
|
|
73986
|
+
})
|
|
73987
|
+
);
|
|
73988
|
+
});
|
|
73989
|
+
api.get(
|
|
73990
|
+
"/issues/:number",
|
|
73991
|
+
async (c) => c.json(await viewIssue(repositoryOf(c.req.query("repo")), numberOf(c.req.param("number"))))
|
|
73992
|
+
);
|
|
73993
|
+
api.post("/issues", async (c) => {
|
|
73994
|
+
const draft = await c.req.json();
|
|
73995
|
+
if (!draft?.title?.trim()) throw new ForgeError("failed", "an issue needs a title");
|
|
73996
|
+
return c.json(await createIssue(repositoryOf(c.req.query("repo")), draft), { status: 201 });
|
|
73997
|
+
});
|
|
73998
|
+
api.patch("/issues/:number", async (c) => {
|
|
73999
|
+
const repo = repositoryOf(c.req.query("repo"));
|
|
74000
|
+
const number4 = numberOf(c.req.param("number"));
|
|
74001
|
+
const patch = await c.req.json();
|
|
74002
|
+
const { state, stateReason, ...fields } = patch;
|
|
74003
|
+
await editIssue(repo, number4, fields);
|
|
74004
|
+
if (state) await setIssueState(repo, number4, state, stateReason);
|
|
74005
|
+
return c.json(await viewIssue(repo, number4));
|
|
74006
|
+
});
|
|
74007
|
+
api.post("/issues/:number/comments", async (c) => {
|
|
74008
|
+
const repo = repositoryOf(c.req.query("repo"));
|
|
74009
|
+
const number4 = numberOf(c.req.param("number"));
|
|
74010
|
+
const { body } = await c.req.json();
|
|
74011
|
+
if (!body?.trim()) throw new ForgeError("failed", "a comment needs a body");
|
|
74012
|
+
await commentOnIssue(repo, number4, body);
|
|
74013
|
+
return c.json(await viewIssue(repo, number4));
|
|
74014
|
+
});
|
|
74015
|
+
api.get("/vocabulary", async (c) => c.json(await issueVocabulary(repositoryOf(c.req.query("repo")))));
|
|
74016
|
+
api.get("/assigned", async (c) => {
|
|
74017
|
+
const limit = Number(c.req.query("limit"));
|
|
74018
|
+
return c.json(await assignedIssues(Number.isInteger(limit) && limit > 0 ? limit : void 0));
|
|
74019
|
+
});
|
|
74020
|
+
return api;
|
|
74021
|
+
}
|
|
74022
|
+
|
|
73632
74023
|
// ../host/src/app.ts
|
|
73633
74024
|
var HOST_PUBLIC_ROUTES = /* @__PURE__ */ new Set(["GET /api/health"]);
|
|
73634
74025
|
function createHostApp(options) {
|
|
@@ -73644,6 +74035,7 @@ function createHostApp(options) {
|
|
|
73644
74035
|
return next();
|
|
73645
74036
|
});
|
|
73646
74037
|
app.get("/api/health", (c) => c.json({ ok: true }));
|
|
74038
|
+
app.route("/api/forge", createForgeRoutes());
|
|
73647
74039
|
for (const module of options.modules) {
|
|
73648
74040
|
if (module.routes) app.route(`/api/modules/${module.manifest.id}`, module.routes(options.context));
|
|
73649
74041
|
}
|