@drisp/cli 0.7.7 → 0.7.8
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/{WorkflowInstallWizard-VVEEJW25.js → WorkflowInstallWizard-IOJD3YOA.js} +2 -2
- package/dist/{chunk-D246FR4I.js → chunk-HGMJ3YL2.js} +103 -27
- package/dist/{chunk-HZ6GRLOM.js → chunk-OYUBT6RS.js} +35 -8
- package/dist/{chunk-D5VZT7M3.js → chunk-TBUUNMC6.js} +2 -2
- package/dist/cli.js +219 -56
- package/dist/dashboard-daemon.js +3 -3
- package/dist/runner.js +3 -3
- package/package.json +1 -1
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
installWorkflowPlugins,
|
|
9
9
|
resolveWorkflow,
|
|
10
10
|
writeGlobalConfig
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-OYUBT6RS.js";
|
|
12
12
|
import "./chunk-G3PMV62Z.js";
|
|
13
13
|
|
|
14
14
|
// src/setup/steps/WorkflowInstallWizard.tsx
|
|
@@ -93,4 +93,4 @@ function WorkflowInstallWizard({ source, onDone }) {
|
|
|
93
93
|
export {
|
|
94
94
|
WorkflowInstallWizard as default
|
|
95
95
|
};
|
|
96
|
-
//# sourceMappingURL=WorkflowInstallWizard-
|
|
96
|
+
//# sourceMappingURL=WorkflowInstallWizard-IOJD3YOA.js.map
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
LAYOUT_MARKERS,
|
|
22
22
|
ORCHESTRATOR_LOOP,
|
|
23
23
|
WAITING_ON_CHILDREN_STOP_REASON,
|
|
24
|
+
budgetTokens,
|
|
24
25
|
compileWorkflowPlan,
|
|
25
26
|
createSteerQueue,
|
|
26
27
|
createWorkflowRunner,
|
|
@@ -54,7 +55,7 @@ import {
|
|
|
54
55
|
verifyRendered,
|
|
55
56
|
wakesFreshAfterHandover,
|
|
56
57
|
workflowRegistryDir
|
|
57
|
-
} from "./chunk-
|
|
58
|
+
} from "./chunk-OYUBT6RS.js";
|
|
58
59
|
import {
|
|
59
60
|
__commonJS,
|
|
60
61
|
__toESM
|
|
@@ -12688,7 +12689,7 @@ var RUN_EXIT_CODE = {
|
|
|
12688
12689
|
import { existsSync as existsSync4 } from "fs";
|
|
12689
12690
|
import { link, mkdir as mkdir6, readdir as readdir3, readFile as readFile5, unlink as unlink3, writeFile as writeFile4 } from "fs/promises";
|
|
12690
12691
|
import { homedir as homedir5 } from "os";
|
|
12691
|
-
import { dirname as dirname5, join as
|
|
12692
|
+
import { dirname as dirname5, join as join11 } from "path";
|
|
12692
12693
|
|
|
12693
12694
|
// ../../modules/runtime/src/runner-service.ts
|
|
12694
12695
|
import { execFile } from "child_process";
|
|
@@ -12880,7 +12881,7 @@ WantedBy=default.target
|
|
|
12880
12881
|
// ../../modules/runtime/src/service.ts
|
|
12881
12882
|
import { mkdir as mkdir5, readFile as readFile4, unlink as unlink2 } from "fs/promises";
|
|
12882
12883
|
import { homedir as homedir4 } from "os";
|
|
12883
|
-
import { join as
|
|
12884
|
+
import { join as join10, resolve as resolve6 } from "path";
|
|
12884
12885
|
|
|
12885
12886
|
// ../../packages/protocol/src/domain.ts
|
|
12886
12887
|
var RunStatusSchema = external_exports.enum([
|
|
@@ -14892,7 +14893,7 @@ async function restoreCheckpoint(store, input) {
|
|
|
14892
14893
|
|
|
14893
14894
|
// ../../modules/runtime/src/software-version.ts
|
|
14894
14895
|
import "fs";
|
|
14895
|
-
var runnerSoftwareVersion = true ? "0.7.
|
|
14896
|
+
var runnerSoftwareVersion = true ? "0.7.8" : JSON.parse(readFileSync2(new URL("../../../apps/cli/package.json", import.meta.url), "utf8")).version;
|
|
14896
14897
|
|
|
14897
14898
|
// ../../modules/runtime/src/connection.ts
|
|
14898
14899
|
var RunnerDisconnectedError = class extends Error {
|
|
@@ -20149,8 +20150,8 @@ function sessionStore(store, runId, options = {}) {
|
|
|
20149
20150
|
|
|
20150
20151
|
// ../../modules/runtime/src/workflow-executor.ts
|
|
20151
20152
|
import { execFileSync as execFileSync9 } from "child_process";
|
|
20152
|
-
import { statSync as statSync3, writeFileSync as
|
|
20153
|
-
import { join as
|
|
20153
|
+
import { statSync as statSync3, writeFileSync as writeFileSync5 } from "fs";
|
|
20154
|
+
import { join as join9 } from "path";
|
|
20154
20155
|
|
|
20155
20156
|
// src/core/build/workspace.ts
|
|
20156
20157
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
@@ -20204,6 +20205,11 @@ var fillSchema = external_exports.strictObject({
|
|
|
20204
20205
|
)
|
|
20205
20206
|
).min(1).optional(),
|
|
20206
20207
|
sizeClass: external_exports.enum(["S", "M", "L"]),
|
|
20208
|
+
/**
|
|
20209
|
+
* Who merges the pull request (root ADR-0009): `agent`, the default when absent, or `you`, which makes the Merge
|
|
20210
|
+
* step a Sign-off the member answers in the child's Thread. Filled from the member's words before Start.
|
|
20211
|
+
*/
|
|
20212
|
+
merge: external_exports.enum(["agent", "you"]).optional(),
|
|
20207
20213
|
instructions: external_exports.string().min(1).max(6e3).refine((text) => !TEMPLATE_STRUCTURE.test(text), {
|
|
20208
20214
|
error: INSTRUCTIONS_MESSAGE
|
|
20209
20215
|
}),
|
|
@@ -20293,6 +20299,8 @@ var issueEntrySchema = external_exports.strictObject({
|
|
|
20293
20299
|
mcpServers: external_exports.array(external_exports.string()),
|
|
20294
20300
|
instructionsSummary: external_exports.string(),
|
|
20295
20301
|
gates: external_exports.array(external_exports.string()),
|
|
20302
|
+
/** Who merges, from the fill; absent means agents merge (root ADR-0009). */
|
|
20303
|
+
merge: external_exports.enum(["agent", "you"]).optional(),
|
|
20296
20304
|
workflowName: external_exports.string().max(128)
|
|
20297
20305
|
});
|
|
20298
20306
|
var buildPlanSchema = external_exports.strictObject({
|
|
@@ -20504,6 +20512,7 @@ function scopeFingerprint(issue) {
|
|
|
20504
20512
|
}
|
|
20505
20513
|
|
|
20506
20514
|
// src/core/build/planner.ts
|
|
20515
|
+
var MIXED_MERGE = "The fills disagree on merge. Write the same value in every fill: you when the member asked to merge themselves, else agent.";
|
|
20507
20516
|
function skipReason(issue, pullRequests) {
|
|
20508
20517
|
const prs = issue.branchName ? pullRequests[issue.branchName] : void 0;
|
|
20509
20518
|
if (issue.labels.includes("ready-for-human")) return "ready-for-human";
|
|
@@ -20563,7 +20572,8 @@ function shownFromFill(fill) {
|
|
|
20563
20572
|
skills: renderedSkills(fill),
|
|
20564
20573
|
mcpServers: renderedMcpServers(fill),
|
|
20565
20574
|
instructionsSummary: summarizeInstructions(fill.instructions),
|
|
20566
|
-
gates: gateTitles(fill)
|
|
20575
|
+
gates: gateTitles(fill),
|
|
20576
|
+
...fill.merge ? { merge: fill.merge } : {}
|
|
20567
20577
|
};
|
|
20568
20578
|
}
|
|
20569
20579
|
function blockingGraph(entries) {
|
|
@@ -20629,6 +20639,16 @@ function planBuild(context) {
|
|
|
20629
20639
|
});
|
|
20630
20640
|
}
|
|
20631
20641
|
if (missing.length > 0) return { status: "needs_fills", fills: missing };
|
|
20642
|
+
if (new Set([...fills.values()].map((fill) => fill.merge ?? "agent")).size > 1)
|
|
20643
|
+
return {
|
|
20644
|
+
status: "needs_fills",
|
|
20645
|
+
fills: entries.map(({ identifier, title, url }) => ({
|
|
20646
|
+
identifier,
|
|
20647
|
+
title,
|
|
20648
|
+
url
|
|
20649
|
+
})),
|
|
20650
|
+
reason: MIXED_MERGE
|
|
20651
|
+
};
|
|
20632
20652
|
const hasSubIssues = new Set(input.issues.map((issue) => issue.parentId));
|
|
20633
20653
|
const parentOnly = new Set(
|
|
20634
20654
|
entries.filter(
|
|
@@ -20942,7 +20962,8 @@ function validateChildRun(grant, store, paths) {
|
|
|
20942
20962
|
if (!verified.ok) throw mismatch(`its ${verified.file} does not re-render from its fill`);
|
|
20943
20963
|
const shown = approval.plan.entries?.[name];
|
|
20944
20964
|
const derived = shownFromFill(fill.data);
|
|
20945
|
-
|
|
20965
|
+
const sameMerge = (shown?.merge ?? "agent") === (fill.data.merge ?? "agent");
|
|
20966
|
+
if (!shown || !sameMerge || !Object.entries(derived).every(([key, value]) => isDeepStrictEqual2(shown[key], value)))
|
|
20946
20967
|
throw mismatch("the plan the member approved showed something else for it");
|
|
20947
20968
|
return { permissions: renderedPermissions(fill.data) };
|
|
20948
20969
|
}
|
|
@@ -21337,6 +21358,50 @@ function runArtifactEmitter(directory, record, midTurnMinGapMs = MID_TURN_MIN_GA
|
|
|
21337
21358
|
};
|
|
21338
21359
|
}
|
|
21339
21360
|
|
|
21361
|
+
// ../../modules/runtime/src/run-usage.ts
|
|
21362
|
+
import { readFileSync as readFileSync5, renameSync, writeFileSync as writeFileSync4 } from "fs";
|
|
21363
|
+
import { join as join8 } from "path";
|
|
21364
|
+
var isMark = (value) => {
|
|
21365
|
+
const mark = value;
|
|
21366
|
+
return !!mark && Number.isInteger(mark.index) && typeof mark.title === "string" && typeof mark.tokens === "number" && Number.isFinite(mark.tokens) && mark.tokens >= 0;
|
|
21367
|
+
};
|
|
21368
|
+
var read2 = (path23) => {
|
|
21369
|
+
try {
|
|
21370
|
+
return JSON.parse(readFileSync5(path23, "utf8"));
|
|
21371
|
+
} catch {
|
|
21372
|
+
return void 0;
|
|
21373
|
+
}
|
|
21374
|
+
};
|
|
21375
|
+
function stepUsageRecorder(directory, counted) {
|
|
21376
|
+
const path23 = join8(directory, "usage.json");
|
|
21377
|
+
const stored = read2(path23);
|
|
21378
|
+
const marks = Array.isArray(stored) ? stored.filter(isMark) : [];
|
|
21379
|
+
return ({ closing = false } = {}) => {
|
|
21380
|
+
const step = read2(join8(directory, "step.json"));
|
|
21381
|
+
if (!step || !Number.isInteger(step.index) || typeof step.title !== "string") return;
|
|
21382
|
+
const figure = counted();
|
|
21383
|
+
if (figure === null) return;
|
|
21384
|
+
const last = marks.at(-1);
|
|
21385
|
+
const tokens = Math.max(figure, last?.tokens ?? 0);
|
|
21386
|
+
const sameStep = last?.index === step.index && last?.title === step.title;
|
|
21387
|
+
if (sameStep && !(closing && tokens > last.tokens)) return;
|
|
21388
|
+
marks.push({ index: step.index, title: step.title, tokens });
|
|
21389
|
+
let json = JSON.stringify(marks);
|
|
21390
|
+
if (Buffer.byteLength(json) > ARTIFACT_MAX_BYTES) {
|
|
21391
|
+
for (let i = marks.length - 2; i > 0; i--) {
|
|
21392
|
+
if (marks[i].index === marks[i - 1].index && marks[i].title === marks[i - 1].title) marks.splice(i, 1);
|
|
21393
|
+
}
|
|
21394
|
+
json = JSON.stringify(marks);
|
|
21395
|
+
if (Buffer.byteLength(json) > ARTIFACT_MAX_BYTES) {
|
|
21396
|
+
marks.pop();
|
|
21397
|
+
return;
|
|
21398
|
+
}
|
|
21399
|
+
}
|
|
21400
|
+
writeFileSync4(`${path23}.tmp`, json);
|
|
21401
|
+
renameSync(`${path23}.tmp`, path23);
|
|
21402
|
+
};
|
|
21403
|
+
}
|
|
21404
|
+
|
|
21340
21405
|
// ../../modules/runtime/src/workflow-executor.ts
|
|
21341
21406
|
var isBuildCloseReason = (code) => buildCloseReasonSchema.safeParse(code).success;
|
|
21342
21407
|
var continuationSchema = external_exports.discriminatedUnion("kind", [
|
|
@@ -21458,7 +21523,7 @@ function createWorkflowExecutor(dependencies = {}) {
|
|
|
21458
21523
|
dependencies.dataDirectory ?? runnerDataDirectory(store.sql.database.name),
|
|
21459
21524
|
grant.runId
|
|
21460
21525
|
);
|
|
21461
|
-
const
|
|
21526
|
+
const emitArtifacts = runArtifactEmitter(artifacts, record, (dependencies.artifactIntervalMs ?? 2e3) * 2.5);
|
|
21462
21527
|
const loop = bootstrapped.workflow?.loop;
|
|
21463
21528
|
const childLoop = grant.parent && loop ? { ...loop, maxRunTokens: Math.min(loop.maxRunTokens ?? Infinity, grant.parent.tokenCap) } : loop;
|
|
21464
21529
|
const config2 = {
|
|
@@ -21491,6 +21556,17 @@ function createWorkflowExecutor(dependencies = {}) {
|
|
|
21491
21556
|
}
|
|
21492
21557
|
} : {}
|
|
21493
21558
|
});
|
|
21559
|
+
const recordUsage = stepUsageRecorder(artifacts, () => {
|
|
21560
|
+
const memory = deserializeRunMemory(view2.getLatestRun()?.runMemoryJson);
|
|
21561
|
+
return memory ? budgetTokens(memory) : null;
|
|
21562
|
+
});
|
|
21563
|
+
const sweepArtifacts2 = (midTurn = false, closing = false) => {
|
|
21564
|
+
try {
|
|
21565
|
+
recordUsage({ closing });
|
|
21566
|
+
} catch {
|
|
21567
|
+
}
|
|
21568
|
+
emitArtifacts(midTurn);
|
|
21569
|
+
};
|
|
21494
21570
|
const previous = view2.getLatestRun();
|
|
21495
21571
|
const predecessor = grant.previousRun ? store.sessionValues(grant.sessionId, "workflow").find((snapshot) => snapshot.id === grant.previousRun.runId) : void 0;
|
|
21496
21572
|
const adapterResumeSessionId2 = grant.purpose === "resume" ? previous?.adapterSessionId : predecessor?.adapterSessionId;
|
|
@@ -21498,12 +21574,12 @@ function createWorkflowExecutor(dependencies = {}) {
|
|
|
21498
21574
|
if (continuation?.kind === "answer") {
|
|
21499
21575
|
const pending2 = pendingApproval(store, grant.runId);
|
|
21500
21576
|
if (pending2) {
|
|
21501
|
-
|
|
21577
|
+
writeFileSync5(join9(artifacts, "answer.json"), JSON.stringify(continuation.answer));
|
|
21502
21578
|
record(APPROVAL_ANSWERED, { answer: continuation.answer, plan: pending2.plan });
|
|
21503
21579
|
}
|
|
21504
21580
|
}
|
|
21505
21581
|
const guidedPlan = continuation?.kind === "resume" && continuation.guidance?.trim() && pendingApproval(store, grant.runId) ? continuation.guidance : void 0;
|
|
21506
|
-
if (guidedPlan !== void 0)
|
|
21582
|
+
if (guidedPlan !== void 0) writeFileSync5(join9(artifacts, "answer.json"), JSON.stringify(guidedPlan));
|
|
21507
21583
|
let storedAnswer2;
|
|
21508
21584
|
if (continuation?.kind === "answer") {
|
|
21509
21585
|
const answer = permissionAnswerSchema.safeParse(continuation.answer);
|
|
@@ -21534,7 +21610,7 @@ ${JSON.stringify(continuation.answer)}` : grant.spec.prompt;
|
|
|
21534
21610
|
Current human request:
|
|
21535
21611
|
${prompt2}`;
|
|
21536
21612
|
}
|
|
21537
|
-
return { workingDirectory: workingDirectory2, config: config2, view: view2, adapterResumeSessionId: adapterResumeSessionId2, storedAnswer: storedAnswer2, prompt: prompt2, displayText: displayText2,
|
|
21613
|
+
return { workingDirectory: workingDirectory2, config: config2, view: view2, adapterResumeSessionId: adapterResumeSessionId2, storedAnswer: storedAnswer2, prompt: prompt2, displayText: displayText2, sweepArtifacts: sweepArtifacts2 };
|
|
21538
21614
|
};
|
|
21539
21615
|
let setup2;
|
|
21540
21616
|
try {
|
|
@@ -21546,12 +21622,12 @@ ${prompt2}`;
|
|
|
21546
21622
|
if (error instanceof RunSetupError && isBuildCloseReason(error.code)) return { status: "failed", reason: error.code };
|
|
21547
21623
|
return { status: "failed" };
|
|
21548
21624
|
}
|
|
21549
|
-
const { workingDirectory, config, view, adapterResumeSessionId, storedAnswer, prompt, displayText,
|
|
21625
|
+
const { workingDirectory, config, view, adapterResumeSessionId, storedAnswer, prompt, displayText, sweepArtifacts } = setup2;
|
|
21550
21626
|
const queue = createSteerQueue();
|
|
21551
21627
|
queues.set(grant.runId, queue);
|
|
21552
21628
|
const sweep = setInterval(() => {
|
|
21553
21629
|
try {
|
|
21554
|
-
|
|
21630
|
+
sweepArtifacts(true);
|
|
21555
21631
|
} catch {
|
|
21556
21632
|
}
|
|
21557
21633
|
}, dependencies.artifactIntervalMs ?? 2e3);
|
|
@@ -21587,14 +21663,14 @@ ${prompt2}`;
|
|
|
21587
21663
|
}
|
|
21588
21664
|
});
|
|
21589
21665
|
},
|
|
21590
|
-
onTurnComplete:
|
|
21666
|
+
onTurnComplete: () => sweepArtifacts(false, true),
|
|
21591
21667
|
stdout: { write: () => true },
|
|
21592
21668
|
stderr: { write: (text) => process.stderr.write(text) },
|
|
21593
21669
|
json: false
|
|
21594
21670
|
})).finally(() => {
|
|
21595
21671
|
queues.delete(grant.runId);
|
|
21596
21672
|
clearInterval(sweep);
|
|
21597
|
-
|
|
21673
|
+
sweepArtifacts(false, true);
|
|
21598
21674
|
});
|
|
21599
21675
|
if (!result.workflowOutcome)
|
|
21600
21676
|
throw new Error(result.failure?.message ?? "Workflow returned no authoritative outcome");
|
|
@@ -21730,8 +21806,8 @@ var runnerConfigSchema = external_exports.object({
|
|
|
21730
21806
|
/** The Runner's name in the Team, as Connect recorded it (hostname by default). */
|
|
21731
21807
|
name: external_exports.string().optional()
|
|
21732
21808
|
});
|
|
21733
|
-
var defaultConfigPath = () => process.env.DRISP_RUNNER_CONFIG ??
|
|
21734
|
-
var expandHome = (value) => resolve6(value.startsWith("~/") ?
|
|
21809
|
+
var defaultConfigPath = () => process.env.DRISP_RUNNER_CONFIG ?? join10(homedir4(), ".config", "drisp", "runner-v2.json");
|
|
21810
|
+
var expandHome = (value) => resolve6(value.startsWith("~/") ? join10(homedir4(), value.slice(2)) : value);
|
|
21735
21811
|
async function readRunnerConfig(path23 = defaultConfigPath()) {
|
|
21736
21812
|
const config = runnerConfigSchema.parse(JSON.parse(await readFile4(expandHome(path23), "utf8")));
|
|
21737
21813
|
return { ...config, dataDirectory: expandHome(config.dataDirectory) };
|
|
@@ -21740,7 +21816,7 @@ async function startConfiguredRunner(path23 = defaultConfigPath()) {
|
|
|
21740
21816
|
const config = await readRunnerConfig(path23);
|
|
21741
21817
|
await mkdir5(config.dataDirectory, { recursive: true, mode: 448 });
|
|
21742
21818
|
const store = new ExecutionStore(
|
|
21743
|
-
|
|
21819
|
+
join10(config.dataDirectory, "execution.sqlite"),
|
|
21744
21820
|
{
|
|
21745
21821
|
orgId: config.orgId,
|
|
21746
21822
|
runnerId: config.runnerId,
|
|
@@ -21829,17 +21905,17 @@ async function runConfiguredRunners(paths, abort, { whenAllDisconnected } = {})
|
|
|
21829
21905
|
}
|
|
21830
21906
|
|
|
21831
21907
|
// ../../modules/runtime/src/identities.ts
|
|
21832
|
-
var runnerConfigDirectory = () =>
|
|
21833
|
-
var teamIdentityPath = (hub, orgId, directory = runnerConfigDirectory()) =>
|
|
21908
|
+
var runnerConfigDirectory = () => join11(homedir5(), ".config", "drisp");
|
|
21909
|
+
var teamIdentityPath = (hub, orgId, directory = runnerConfigDirectory()) => join11(directory, "runners", encodeURIComponent(hub.host), `${encodeURIComponent(orgId)}.json`);
|
|
21834
21910
|
function identityHubOrigin({ endpoint }) {
|
|
21835
21911
|
const url = new URL(endpoint);
|
|
21836
21912
|
url.protocol = url.protocol === "wss:" ? "https:" : "http:";
|
|
21837
21913
|
return url.origin;
|
|
21838
21914
|
}
|
|
21839
|
-
var legacyIdentityPath = (directory) =>
|
|
21915
|
+
var legacyIdentityPath = (directory) => join11(directory, "runner-v2.json");
|
|
21840
21916
|
var overriddenIdentityPath = () => process.env.DRISP_RUNNER_CONFIG;
|
|
21841
21917
|
async function machineInstallationId(directory = runnerConfigDirectory()) {
|
|
21842
|
-
const path23 =
|
|
21918
|
+
const path23 = join11(directory, "installation-id");
|
|
21843
21919
|
const existing = await readFile5(path23, "utf8").then((text) => text.trim(), () => "");
|
|
21844
21920
|
if (existing) return existing;
|
|
21845
21921
|
let installationId = crypto.randomUUID();
|
|
@@ -21859,13 +21935,13 @@ async function machineInstallationId(directory = runnerConfigDirectory()) {
|
|
|
21859
21935
|
}
|
|
21860
21936
|
async function runnerIdentities(directory = runnerConfigDirectory()) {
|
|
21861
21937
|
const override = overriddenIdentityPath();
|
|
21862
|
-
const runners =
|
|
21863
|
-
const identityFiles = async (folder) => (await readdir3(folder).catch(() => [])).filter((name) => name.endsWith(".json")).sort().map((name) =>
|
|
21938
|
+
const runners = join11(directory, "runners");
|
|
21939
|
+
const identityFiles = async (folder) => (await readdir3(folder).catch(() => [])).filter((name) => name.endsWith(".json")).sort().map((name) => join11(folder, name));
|
|
21864
21940
|
const hubs = (await readdir3(runners, { withFileTypes: true }).catch(() => [])).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
|
|
21865
21941
|
const paths = override ? [expandHome(override)] : [
|
|
21866
21942
|
legacyIdentityPath(directory),
|
|
21867
21943
|
...await identityFiles(runners),
|
|
21868
|
-
...(await Promise.all(hubs.map((hub) => identityFiles(
|
|
21944
|
+
...(await Promise.all(hubs.map((hub) => identityFiles(join11(runners, hub))))).flat()
|
|
21869
21945
|
];
|
|
21870
21946
|
const present = paths.filter((path23) => existsSync4(path23));
|
|
21871
21947
|
return Promise.all(present.map(async (path23) => ({ path: path23, config: await readRunnerConfig(path23) })));
|
|
@@ -22045,4 +22121,4 @@ export {
|
|
|
22045
22121
|
runMachineRunners,
|
|
22046
22122
|
writeIdentityOnce
|
|
22047
22123
|
};
|
|
22048
|
-
//# sourceMappingURL=chunk-
|
|
22124
|
+
//# sourceMappingURL=chunk-HGMJ3YL2.js.map
|
|
@@ -21227,6 +21227,11 @@ var BUILD_STRINGS = {
|
|
|
21227
21227
|
templatePark: "Drisp was updated during this Build. Build again to re-plan.",
|
|
21228
21228
|
notStarted: "Not started",
|
|
21229
21229
|
waiting: "Waiting for child Runs.",
|
|
21230
|
+
/**
|
|
21231
|
+
* The Merge step's Sign-off when the member merges (`merge: you`, root ADR-0009): the `NEEDS_HUMAN` text a child
|
|
21232
|
+
* parks with, `{sha}` being the full head SHA it asks about. The app reads it as "Blocked · Your merge".
|
|
21233
|
+
*/
|
|
21234
|
+
mergeSignOff: "Your merge: this pull request is ready to merge at {sha}. Reply in this Thread to sign off on that commit, or say what to change.",
|
|
21230
21235
|
notAGitHubRepository: "The project folder is not a git repository with a GitHub origin.",
|
|
21231
21236
|
cycle: (identifiers) => `These issues block each other in Linear: ${identifiers.join(", ")}.`,
|
|
21232
21237
|
/** The comment the Orchestrator posts with its one Linear write, moving a merged issue to Done. */
|
|
@@ -21255,7 +21260,7 @@ var GATES = [
|
|
|
21255
21260
|
},
|
|
21256
21261
|
{
|
|
21257
21262
|
title: "Checks",
|
|
21258
|
-
passesWhen: "{install}{checks} pass",
|
|
21263
|
+
passesWhen: "{install}{checks} pass, each check run through `drisp build check <name> -- <command>` (for example `drisp build check typecheck -- make typecheck`), which records its outcome for the Build page",
|
|
21259
21264
|
cannotPass: "This issue's checks still fail after repeated fixes. Open the Thread to see the failing output."
|
|
21260
21265
|
},
|
|
21261
21266
|
{
|
|
@@ -21281,7 +21286,7 @@ var GATES = [
|
|
|
21281
21286
|
];
|
|
21282
21287
|
|
|
21283
21288
|
// src/core/workflows/builtins/orchestrator/workflow.md
|
|
21284
|
-
var workflow_default = '# Orchestrator: plan and run a Build\n\nYou plan and run a Drisp Build of one Plan: the open issues of one Linear project. The Run\'s prompt names the\nLinear project, the Plan key and the project folder, which is your working directory. You never write code here:\neach issue is built by its own child Run, which `drisp build tick` starts.\n\nThe Runner sets these for you:\n\n- `DRISP_RUN_ARTIFACTS`: this Run\'s artifact directory. Files you write there are shown to the member.\n- `DRISP_RUN_ID`, `DRISP_RUNNER_ID`, `DRISP_RUN_TOKEN`, `DRISP_HUB_URL`: this Run, its Runner and its right to start\n child Runs; `drisp build` reads them itself.\n\n## 1. Read Linear (first Turn)\n\nWith the `linear` MCP server, read the project and every issue in it, then write\n`$DRISP_RUN_ARTIFACTS/input/plan-input.json` (create the `input` directory) in exactly this shape. Every issue carries\nevery field, empty when it has none. `readAt` is the output of\n`node -e "console.log(new Date().toISOString())"` run just before you read Linear; never write a time yourself:\n\n```json\n{\n "version": 1,\n "readAt": "<the command\'s output, e.g. 2026-10-11T09:30:00.000Z>",\n "project": {\n "id": "<the project\'s `uuid`, not its `id`>",\n "name": "<the project\'s `name`>",\n "slugId": "<the last `-`-separated part of the project\'s `url`>"\n },\n "issues": [\n {\n "id": "<the issue\'s `uuid`, not its `id`>",\n "identifier": "<the issue\'s `id`, e.g. DRI-12>",\n "title": "<the issue\'s `title`>",\n "description": "<full current description>",\n "acceptanceCriteria": "<acceptance criteria, empty when included in description>",\n "external": false,\n "url": "<the issue\'s `url`>",\n "branchName": "<the issue\'s `gitBranchName`, or null>",\n "state": {\n "type": "<the issue\'s `statusType`: backlog|unstarted|started|completed|canceled|triage>",\n "name": "<the issue\'s `status`>"\n },\n "labels": ["<each of the issue\'s `labels`>"],\n "parentId": "<the parent issue\'s `uuid`, or null>",\n "blockedBy": ["<the `uuid` of each issue in `relations.blockedBy`>"],\n "prioritySortOrder": 0\n }\n ]\n}\n```\n\nLinear\'s MCP names fields differently from this file, and the difference matters: the app finds each issue\'s row by\nits UUID, so a plan that carries identifiers shows no Build state on any row.\n\n- The MCP\'s `id` is the readable identifier (`DRI-12`, or `P-DRI-7` for a project); the UUID is in `uuid`. Every\n `id`, `parentId` and `blockedBy` value in `plan-input.json` is a UUID (`7f7b03f7-accf-46b2-98fb-abadfed76594`).\n- An issue\'s `parentId` and `relations.blockedBy` from the MCP name issues by identifier. Look each one up: use the\n `uuid` of the project\'s issue with that identifier, or `get_issue` it when it is outside the project.\n- Read relations with `get_issue` and `includeRelations: true`; read the project\'s issues with `list_issues` and\n `fields` including `uuid`.\n- The project\'s `url` ends in its `slugId` (`\u2026/project/build-sandbox-b7640301cd64` \u2192 `b7640301cd64`).\n\n`drisp build plan` rejects a plan with an identifier where a UUID belongs and names the field to fix.\n\nAlso include every referenced blocker outside the project, with `external: true`, its current status and UUID. Follow blocker relations recursively; deduplicate by UUID. Never mark a project issue external. External issues are read only and never scheduled. Preserve actual prioritySortOrder when available.\n\nOnly ready-for-agent issues without conflicting ready-for-human, needs-info, needs-triage or wontfix labels are eligible. Do not invent or change labels to make an issue eligible.\n\nInclude every issue of the project, done and canceled ones too: the plan decides what to skip. Then run:\n\n```sh\ndrisp build plan --input "$DRISP_RUN_ARTIFACTS/input/plan-input.json"\n```\n\nIt prints one JSON object. Act on its `status`.\n\n## 2. `needs_fills`\n\n`fills` lists issues that need a `fill.json` (`identifier`, `title`, `url`, `path`) and `templateVersion` is the value\neach fill must carry. For each listed issue: read it through `linear` (description, acceptance criteria, comments) and\nthe repo\'s ADRs and CONTEXT files it touches, then write its `fill.json` at `path`:\n\n```json\n{\n "version": 1,\n "templateVersion": "<templateVersion, copied exactly>",\n "areas": ["macos|web|cli|hub|protocol|docs", "\u2026"],\n "qa": "none|web|macos|both",\n "profile": "implementation|documentation|investigation",\n "rationale": "<why these steps are appropriate>",\n "acceptanceEvidence": [\n "<each acceptance criterion and the evidence that demonstrates it>"\n ],\n "sizeClass": "S|M|L",\n "instructions": "<what the child needs beyond the issue>",\n "ownWork": true\n}\n```\n\n- `areas`: the parts of the repo the change touches, each once.\n- `profile`: implementation for behavior changes, documentation for prose-only repository changes, investigation for findings with no repository changes. Implementation defaults to TDD and two-reviewer code review; documentation and investigation omit these by default. Choose the profile from the actual deliverable, not the issue size.\n- Optional `testFirst` and `review`: `{ "enabled": true|false, "reason": "<issue-specific justification>" }`. Retain tests and review for behavioral changes; omit only when not applicable and explain why. These selections also control required skills.\n- Optional `checks`: a nonempty list of actual validation commands from the repo, using the supported npm, vitest, make or git diff command families. Read repository tooling and add the relevant test/build checks: typechecking alone does not prove behavior. If a necessary command is unsupported, state that limitation in instructions and require human attention, never claim validation passed.\n- `acceptanceEvidence`: cover every criterion, including the method and expected outcome.\n- `qa`: `both` when both interfaces change, `web` when the change shows in a web page, `macos` when it shows in the macOS app, else `none`.\n- `sizeClass`: `S` for a change of a file or two, `M` for a feature across a few modules, `L` for a large or risky\n change.\n- `instructions` (at most 6000 characters): start with one short paragraph saying what to build, then restate the\n acceptance criteria, name the ADRs it touches and the parts of the codebase involved. Plain prose and `-` lists\n only: no headings, code fences or HTML comments (`drisp build plan` rejects them).\n- `ownWork`: `false` only for an issue with sub-issues whose own description asks for nothing beyond them, such as\n a PRD above its slices; the plan then skips it and waits for its sub-issues instead. Otherwise `true`.\n\nThen run `drisp build plan` again with the same `--input` path.\n\n## 3. `planned` or `failed`\n\nOn `planned` the Build plan is written and shown to the member, who decides whether to start it: end the Turn with\n`<!-- AWAITING_APPROVAL -->` as the Journal\'s last line. On `failed` the reason is shown instead: end with\n`<!-- WORKFLOW_COMPLETE -->`.\n\n## 4. Every later wake: tick\n\nYou are woken when the member answers the approval question (with Start, or with words of their own before it) and whenever one of your child Runs closes or parks.\nEach time: read Linear again into `$DRISP_RUN_ARTIFACTS/input/plan-input.json` as in step 1 (with a new `readAt`),\nincluding full descriptions, acceptance criteria, labels and external blocker status. A scope change holds the affected issue for a fresh Build and approval. Then run:\n\n```sh\ndrisp build tick --input "$DRISP_RUN_ARTIFACTS/input/plan-input.json"\n```\n\nIt prints one JSON object. Act on its `action`, and repeat until an action ends the Turn:\n\n- `refresh_input`: read Linear into the input again (a newer `readAt`) and tick again.\n- `replan`: the member wrote words before starting, and `guidance` lists every round of them, oldest first. Plan again\n with them as the member\'s wishes: read the issues they concern (and the repo) as in step 2, rewrite the `fill.json`\n of each issue the guidance changes (`instructions`, `areas`, `qa`, `sizeClass`, or `ownWork` to leave an issue out),\n and run `drisp build plan` again with the same `--input` path. Then end as in step 3: with\n `<!-- AWAITING_APPROVAL -->` on `planned`, `<!-- WORKFLOW_COMPLETE -->` on `failed`. Write nothing but those\n `fill.json` files.\n- `repair`: for each of `issues`, read it in Linear first and skip an issue Linear now shows Done or Canceled (its\n child got there first); otherwise move it to Done and post its `comment` verbatim through `linear`. Then read\n Linear into the input again and tick again.\n- `rebase`: for each of `items`, in its `worktree`: `git rebase origin/main`, resolve the conflicts, then\n `git push --force-with-lease origin <branch>` (the item\'s `branch`) if the branch is on the remote. Never push any\n other branch. If a rebase cannot be resolved, `git rebase --abort`.\n Then read Linear into the input again and tick again, adding `--rebase-failed <identifier>` for each that failed.\n- `wait`: end the Turn with `<!-- WAITING_ON_CHILDREN -->` as the Journal\'s last line.\n- `park`: end the Turn with `<!-- NEEDS_HUMAN: <message> -->` as the Journal\'s last line, copying `message`\n verbatim.\n- `complete`: end the Turn with `<!-- WORKFLOW_COMPLETE -->` as the Journal\'s last line.\n\n## Rules\n\n- Never start, cancel or edit Runs, Workflows, worktrees or branches except as an action says; `drisp build` does\n what is needed.\n- Never write to Linear except a `repair`; never open a pull request.\n- Write only `plan-input.json` and the `fill.json` files `drisp build plan` asks for. Never write `answer.json`.\n';
|
|
21289
|
+
var workflow_default = '# Orchestrator: plan and run a Build\n\nYou plan and run a Drisp Build of one Plan: the open issues of one Linear project. The Run\'s prompt names the\nLinear project, the Plan key and the project folder, which is your working directory. You never write code here:\neach issue is built by its own child Run, which `drisp build tick` starts.\n\nThe Runner sets these for you:\n\n- `DRISP_RUN_ARTIFACTS`: this Run\'s artifact directory. Files you write there are shown to the member.\n- `DRISP_RUN_ID`, `DRISP_RUNNER_ID`, `DRISP_RUN_TOKEN`, `DRISP_HUB_URL`: this Run, its Runner and its right to start\n child Runs; `drisp build` reads them itself.\n\n## 1. Read Linear (first Turn)\n\nWith the `linear` MCP server, read the project and every issue in it, then write\n`$DRISP_RUN_ARTIFACTS/input/plan-input.json` (create the `input` directory) in exactly this shape. Every issue carries\nevery field, empty when it has none. `readAt` is the output of\n`node -e "console.log(new Date().toISOString())"` run just before you read Linear; never write a time yourself:\n\n```json\n{\n "version": 1,\n "readAt": "<the command\'s output, e.g. 2026-10-11T09:30:00.000Z>",\n "project": {\n "id": "<the project\'s `uuid`, not its `id`>",\n "name": "<the project\'s `name`>",\n "slugId": "<the last `-`-separated part of the project\'s `url`>"\n },\n "issues": [\n {\n "id": "<the issue\'s `uuid`, not its `id`>",\n "identifier": "<the issue\'s `id`, e.g. DRI-12>",\n "title": "<the issue\'s `title`>",\n "description": "<full current description>",\n "acceptanceCriteria": "<acceptance criteria, empty when included in description>",\n "external": false,\n "url": "<the issue\'s `url`>",\n "branchName": "<the issue\'s `gitBranchName`, or null>",\n "state": {\n "type": "<the issue\'s `statusType`: backlog|unstarted|started|completed|canceled|triage>",\n "name": "<the issue\'s `status`>"\n },\n "labels": ["<each of the issue\'s `labels`>"],\n "parentId": "<the parent issue\'s `uuid`, or null>",\n "blockedBy": ["<the `uuid` of each issue in `relations.blockedBy`>"],\n "prioritySortOrder": 0\n }\n ]\n}\n```\n\nLinear\'s MCP names fields differently from this file, and the difference matters: the app finds each issue\'s row by\nits UUID, so a plan that carries identifiers shows no Build state on any row.\n\n- The MCP\'s `id` is the readable identifier (`DRI-12`, or `P-DRI-7` for a project); the UUID is in `uuid`. Every\n `id`, `parentId` and `blockedBy` value in `plan-input.json` is a UUID (`7f7b03f7-accf-46b2-98fb-abadfed76594`).\n- An issue\'s `parentId` and `relations.blockedBy` from the MCP name issues by identifier. Look each one up: use the\n `uuid` of the project\'s issue with that identifier, or `get_issue` it when it is outside the project.\n- Read relations with `get_issue` and `includeRelations: true`; read the project\'s issues with `list_issues` and\n `fields` including `uuid`.\n- The project\'s `url` ends in its `slugId` (`\u2026/project/build-sandbox-b7640301cd64` \u2192 `b7640301cd64`).\n\n`drisp build plan` rejects a plan with an identifier where a UUID belongs and names the field to fix.\n\nAlso include every referenced blocker outside the project, with `external: true`, its current status and UUID. Follow blocker relations recursively; deduplicate by UUID. Never mark a project issue external. External issues are read only and never scheduled. Preserve actual prioritySortOrder when available.\n\nOnly ready-for-agent issues without conflicting ready-for-human, needs-info, needs-triage or wontfix labels are eligible. Do not invent or change labels to make an issue eligible.\n\nInclude every issue of the project, done and canceled ones too: the plan decides what to skip. Then run:\n\n```sh\ndrisp build plan --input "$DRISP_RUN_ARTIFACTS/input/plan-input.json"\n```\n\nIt prints one JSON object. Act on its `status`.\n\n## 2. `needs_fills`\n\n`fills` lists issues that need a `fill.json` (`identifier`, `title`, `url`, `path`) and `templateVersion` is the value\neach fill must carry. A `reason`, when present, says what to correct in the listed fills (fills that disagree on\n`merge`, say); fix that and keep the rest of each fill. For each listed issue: read it through `linear` (description,\nacceptance criteria, comments) and the repo\'s ADRs and CONTEXT files it touches, then write its `fill.json` at `path`:\n\n```json\n{\n "version": 1,\n "templateVersion": "<templateVersion, copied exactly>",\n "areas": ["macos|web|cli|hub|protocol|docs", "\u2026"],\n "qa": "none|web|macos|both",\n "profile": "implementation|documentation|investigation",\n "rationale": "<why these steps are appropriate>",\n "acceptanceEvidence": [\n "<each acceptance criterion and the evidence that demonstrates it>"\n ],\n "sizeClass": "S|M|L",\n "merge": "agent|you",\n "instructions": "<what the child needs beyond the issue>",\n "ownWork": true\n}\n```\n\n- `areas`: the parts of the repo the change touches, each once.\n- `profile`: implementation for behavior changes, documentation for prose-only repository changes, investigation for findings with no repository changes. Implementation defaults to TDD and two-reviewer code review; documentation and investigation omit these by default. Choose the profile from the actual deliverable, not the issue size.\n- Optional `testFirst` and `review`: `{ "enabled": true|false, "reason": "<issue-specific justification>" }`. Retain tests and review for behavioral changes; omit only when not applicable and explain why. These selections also control required skills.\n- Optional `checks`: a nonempty list of actual validation commands from the repo, using the supported npm, vitest, make or git diff command families. Read repository tooling and add the relevant test/build checks: typechecking alone does not prove behavior. If a necessary command is unsupported, state that limitation in instructions and require human attention, never claim validation passed.\n- `acceptanceEvidence`: cover every criterion, including the method and expected outcome.\n- `qa`: `both` when both interfaces change, `web` when the change shows in a web page, `macos` when it shows in the macOS app, else `none`.\n- `sizeClass`: `S` for a change of a file or two, `M` for a feature across a few modules, `L` for a large or risky\n change.\n- `merge`: who merges each pull request. `agent` (the default) unless the member\'s own words in this chat, in the\n Run\'s prompt or a guidance reply, ask to merge themselves ("ask me before merging", "I\'ll merge"): then `you`, and\n the child\'s Merge step waits for the member\'s Sign-off in its Thread. It is one choice for the whole Build: write the\n same value in every fill. It is fixed once the member presses Start: if they ask to change it after Start, tell\n them to Cancel the Build and Build again.\n- `instructions` (at most 6000 characters): start with one short paragraph saying what to build, then restate the\n acceptance criteria, name the ADRs it touches and the parts of the codebase involved. Plain prose and `-` lists\n only: no headings, code fences or HTML comments (`drisp build plan` rejects them).\n- `ownWork`: `false` only for an issue with sub-issues whose own description asks for nothing beyond them, such as\n a PRD above its slices; the plan then skips it and waits for its sub-issues instead. Otherwise `true`.\n\nThen run `drisp build plan` again with the same `--input` path.\n\n## 3. `planned` or `failed`\n\nOn `planned` the Build plan is written and shown to the member, who decides whether to start it: end the Turn with\n`<!-- AWAITING_APPROVAL -->` as the Journal\'s last line. On `failed` the reason is shown instead: end with\n`<!-- WORKFLOW_COMPLETE -->`.\n\n## 4. Every later wake: tick\n\nYou are woken when the member answers the approval question (with Start, or with words of their own before it) and whenever one of your child Runs closes or parks.\nEach time: read Linear again into `$DRISP_RUN_ARTIFACTS/input/plan-input.json` as in step 1 (with a new `readAt`),\nincluding full descriptions, acceptance criteria, labels and external blocker status. A scope change holds the affected issue for a fresh Build and approval. Then run:\n\n```sh\ndrisp build tick --input "$DRISP_RUN_ARTIFACTS/input/plan-input.json"\n```\n\nIt prints one JSON object. Act on its `action`, and repeat until an action ends the Turn:\n\n- `refresh_input`: read Linear into the input again (a newer `readAt`) and tick again.\n- `replan`: the member wrote words before starting, and `guidance` lists every round of them, oldest first. Plan again\n with them as the member\'s wishes: read the issues they concern (and the repo) as in step 2, rewrite the `fill.json`\n of each issue the guidance changes (`instructions`, `areas`, `qa`, `sizeClass`, `merge` in every fill when the\n member says who merges, or `ownWork` to leave an issue out),\n and run `drisp build plan` again with the same `--input` path. Then end as in step 3: with\n `<!-- AWAITING_APPROVAL -->` on `planned`, `<!-- WORKFLOW_COMPLETE -->` on `failed`. Write nothing but those\n `fill.json` files.\n- `repair`: for each of `issues`, read it in Linear first and skip an issue Linear now shows Done or Canceled (its\n child got there first); otherwise move it to Done and post its `comment` verbatim through `linear`. Then read\n Linear into the input again and tick again.\n- `rebase`: for each of `items`, in its `worktree`: `git rebase origin/main`, resolve the conflicts, then\n `git push --force-with-lease origin <branch>` (the item\'s `branch`) if the branch is on the remote. Never push any\n other branch. If a rebase cannot be resolved, `git rebase --abort`.\n Then read Linear into the input again and tick again, adding `--rebase-failed <identifier>` for each that failed.\n- `wait`: end the Turn with `<!-- WAITING_ON_CHILDREN -->` as the Journal\'s last line.\n- `park`: end the Turn with `<!-- NEEDS_HUMAN: <message> -->` as the Journal\'s last line, copying `message`\n verbatim.\n- `complete`: end the Turn with `<!-- WORKFLOW_COMPLETE -->` as the Journal\'s last line.\n\n## Rules\n\n- Never start, cancel or edit Runs, Workflows, worktrees or branches except as an action says; `drisp build` does\n what is needed.\n- Never write to Linear except a `repair`; never open a pull request.\n- Write only `plan-input.json` and the `fill.json` files `drisp build plan` asks for. Never write `answer.json`.\n';
|
|
21285
21290
|
|
|
21286
21291
|
// src/core/workflows/builtins/orchestrator/index.ts
|
|
21287
21292
|
var ORCHESTRATOR_WORKFLOW_NAME = "orchestrator";
|
|
@@ -21472,7 +21477,7 @@ is in \`ignored\`: it does not block the merge and its name goes in the evidence
|
|
|
21472
21477
|
empty, wait a minute and run it again. When \`failed\` is not empty, fix the change; if it still fails, the Merge gate
|
|
21473
21478
|
cannot pass. Never judge a check by its timing.
|
|
21474
21479
|
|
|
21475
|
-
Immediately before merging, verify the current revision again: {mergeVerify}. If evidence is stale, rerun the
|
|
21480
|
+
{signOff}Immediately before merging, verify the current revision again: {mergeVerify}. If evidence is stale, rerun the
|
|
21476
21481
|
selected validation steps and record fresh receipts before continuing.
|
|
21477
21482
|
|
|
21478
21483
|
Merge with \`gh pr merge --squash --delete-branch --match-head-commit <sha>\`, where \`<sha>\` is HEAD after the last
|
|
@@ -21503,6 +21508,18 @@ Evidence. Never create issues and never edit an issue's title or description.
|
|
|
21503
21508
|
|
|
21504
21509
|
When every gate has passed, end with \`<!-- WORKFLOW_COMPLETE -->\` as the Journal's last line.
|
|
21505
21510
|
`;
|
|
21511
|
+
var MERGE_SIGN_OFF = {
|
|
21512
|
+
passesWhen: "the merge gate below holds, with the member\u2019s Sign-off on HEAD",
|
|
21513
|
+
section: `Sign-off: this Build's member merges, so the merge waits for their Sign-off on the exact head commit. When
|
|
21514
|
+
everything above holds and HEAD is pushed, end the Turn with \`<!-- NEEDS_HUMAN: {signOffText} -->\` as the Journal's
|
|
21515
|
+
last line, where \`<sha>\` is the full \`git rev-parse HEAD\`. The member answers in this Thread. When their reply signs
|
|
21516
|
+
off on that commit, record it with \`{signOffCommand}\`, which refuses any SHA but HEAD; it is your attestation of
|
|
21517
|
+
their reply, so never record one they did not give. Any other reply is guidance: act on it, then ask again. A
|
|
21518
|
+
Sign-off names one commit: after a rebase or any new commit, ask again before merging.
|
|
21519
|
+
When \`gh pr merge\` fails because the branch is protected, the Merge gate cannot pass.
|
|
21520
|
+
|
|
21521
|
+
`
|
|
21522
|
+
};
|
|
21506
21523
|
var STAGE_TEMPLATE = `### {n}. {title}
|
|
21507
21524
|
|
|
21508
21525
|
Start: run \`{stepCommand}\`.
|
|
@@ -21584,6 +21601,7 @@ var RENDERER_RULES = {
|
|
|
21584
21601
|
permissions: {
|
|
21585
21602
|
edit: ["Edit(./**)"],
|
|
21586
21603
|
shell: [
|
|
21604
|
+
"drisp build check",
|
|
21587
21605
|
"drisp build checks",
|
|
21588
21606
|
"drisp build step",
|
|
21589
21607
|
"drisp build merged",
|
|
@@ -21721,11 +21739,11 @@ function selectedGates(fill) {
|
|
|
21721
21739
|
).map(
|
|
21722
21740
|
(gate) => fill.profile === "investigation" && gate.title === "Checks" ? {
|
|
21723
21741
|
...gate,
|
|
21724
|
-
passesWhen: "{install}{checks} pass
|
|
21742
|
+
passesWhen: "{install}{checks} pass, each check run through `drisp build check <name> -- <command>`; each finding is supported by a source or reproducible observation and each acceptance criterion has evidence; unresolved questions are stated explicitly"
|
|
21725
21743
|
} : fill.profile === "investigation" && gate.title === "Evidence" ? {
|
|
21726
21744
|
...gate,
|
|
21727
21745
|
passesWhen: "findings and evidence for each acceptance criterion are posted on the Linear issue, with unresolved questions explicitly stated; the issue is moved to Done only when all acceptance criteria are satisfied"
|
|
21728
|
-
} : gate
|
|
21746
|
+
} : fill.merge === "you" && gate.title === "Merge" ? { ...gate, passesWhen: MERGE_SIGN_OFF.passesWhen } : gate
|
|
21729
21747
|
);
|
|
21730
21748
|
if (!usesTestFirst(fill))
|
|
21731
21749
|
gates.splice(1, 0, {
|
|
@@ -21753,6 +21771,7 @@ var fillSlots = (template, slots) => template.replace(/\{(\w+)\}/g, (whole, key)
|
|
|
21753
21771
|
function selectionFlags(fill) {
|
|
21754
21772
|
return `${fill.profile ? ` --profile ${fill.profile}` : ""}${fill.testFirst ? ` --test-first ${fill.testFirst.enabled}` : ""}${fill.review ? ` --review ${fill.review.enabled}` : ""}`;
|
|
21755
21773
|
}
|
|
21774
|
+
var mergeFlag = (fill) => fill.merge === "you" ? " --merge you" : "";
|
|
21756
21775
|
function renderStages(fill, checks, install) {
|
|
21757
21776
|
const gates = selectedGates(fill);
|
|
21758
21777
|
return gates.map((gate, index) => {
|
|
@@ -21768,11 +21787,12 @@ function renderStages(fill, checks, install) {
|
|
|
21768
21787
|
stepCommand: command,
|
|
21769
21788
|
passesWhen
|
|
21770
21789
|
});
|
|
21790
|
+
const finish = `${command}${gate.title === "Merge" ? mergeFlag(fill) : ""}`;
|
|
21771
21791
|
return stage + (["Merge", "Evidence"].includes(gate.title) ? `
|
|
21772
21792
|
|
|
21773
21793
|
Before doing this step: run \`${command} --verify\`. Missing or stale evidence must be refreshed first.` : "") + `
|
|
21774
21794
|
|
|
21775
|
-
Finish: write the actual result and supporting paths to \`.athena/evidence/step-${index + 1}.md\`, then run \`${
|
|
21795
|
+
Finish: write the actual result and supporting paths to \`.athena/evidence/step-${index + 1}.md\`, then run \`${finish} --complete --evidence .athena/evidence/step-${index + 1}.md\`. This records an agent attestation, not independent verification.` + (gate.cannotPass ? fillSlots(CANNOT_PASS_TEMPLATE, { cannotPass: gate.cannotPass }) : NO_GATE_TEXT_TEMPLATE);
|
|
21776
21796
|
}).join("\n\n");
|
|
21777
21797
|
}
|
|
21778
21798
|
function render(fill, workflowName, layout) {
|
|
@@ -21818,6 +21838,7 @@ function render(fill, workflowName, layout) {
|
|
|
21818
21838
|
"Your Linear writes are In Progress in Explore, findings and evidence, and Done in Evidence."
|
|
21819
21839
|
);
|
|
21820
21840
|
}
|
|
21841
|
+
const mergeCommand = `drisp build step ${gateTitles(fill).indexOf("Merge") + 1} --qa ${fill.qa}${selectionFlags(fill)}`;
|
|
21821
21842
|
const workflowMd = fillSlots(template, {
|
|
21822
21843
|
identifier: parsed.identifier,
|
|
21823
21844
|
instructions: [
|
|
@@ -21830,7 +21851,11 @@ function render(fill, workflowName, layout) {
|
|
|
21830
21851
|
)
|
|
21831
21852
|
].filter(Boolean).join("\n\n"),
|
|
21832
21853
|
gates: renderStages(fill, checks, installCommand(fill, layout)),
|
|
21833
|
-
|
|
21854
|
+
signOff: fill.merge === "you" ? fillSlots(MERGE_SIGN_OFF.section, {
|
|
21855
|
+
signOffText: BUILD_STRINGS.mergeSignOff.replace("{sha}", "<sha>"),
|
|
21856
|
+
signOffCommand: `${mergeCommand} --merge you --sign-off <sha>`
|
|
21857
|
+
}) : "",
|
|
21858
|
+
mergeVerify: `\`${mergeCommand}${mergeFlag(fill)} --verify\``,
|
|
21834
21859
|
checksEvidence: checks.map((command) => `${command} \u2713`).join(" \xB7 "),
|
|
21835
21860
|
shellCommands: checksText(shellCommands(fill))
|
|
21836
21861
|
});
|
|
@@ -24282,6 +24307,7 @@ function collectMcpServersWithOptions(pluginDirs) {
|
|
|
24282
24307
|
export {
|
|
24283
24308
|
WAITING_ON_CHILDREN_STOP_REASON,
|
|
24284
24309
|
AWAITING_APPROVAL_STOP_REASON,
|
|
24310
|
+
budgetTokens,
|
|
24285
24311
|
createSteerQueue,
|
|
24286
24312
|
DEFAULT_PERMISSION_GRACE_MS,
|
|
24287
24313
|
serializeRunMemory,
|
|
@@ -24321,6 +24347,7 @@ export {
|
|
|
24321
24347
|
DESCRIPTION_TEMPLATE,
|
|
24322
24348
|
QA_SECTIONS,
|
|
24323
24349
|
WORKFLOW_MD_TEMPLATE,
|
|
24350
|
+
MERGE_SIGN_OFF,
|
|
24324
24351
|
STAGE_TEMPLATE,
|
|
24325
24352
|
CANNOT_PASS_TEMPLATE,
|
|
24326
24353
|
NO_GATE_TEXT_TEMPLATE,
|
|
@@ -24347,4 +24374,4 @@ export {
|
|
|
24347
24374
|
compileWorkflowPlan,
|
|
24348
24375
|
collectMcpServersWithOptions
|
|
24349
24376
|
};
|
|
24350
|
-
//# sourceMappingURL=chunk-
|
|
24377
|
+
//# sourceMappingURL=chunk-OYUBT6RS.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
runMachineRunners
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-HGMJ3YL2.js";
|
|
4
4
|
|
|
5
5
|
// src/app/entry/runnerDaemon.ts
|
|
6
6
|
if (process.argv.includes("--help")) {
|
|
@@ -13,4 +13,4 @@ if (process.argv.includes("--help")) {
|
|
|
13
13
|
process.exitCode = 1;
|
|
14
14
|
});
|
|
15
15
|
}
|
|
16
|
-
//# sourceMappingURL=chunk-
|
|
16
|
+
//# sourceMappingURL=chunk-TBUUNMC6.js.map
|
package/dist/cli.js
CHANGED
|
@@ -142,7 +142,7 @@ import {
|
|
|
142
142
|
visualPositionToOffset,
|
|
143
143
|
wrapText,
|
|
144
144
|
writeIdentityOnce
|
|
145
|
-
} from "./chunk-
|
|
145
|
+
} from "./chunk-HGMJ3YL2.js";
|
|
146
146
|
import {
|
|
147
147
|
generateId as generateId2
|
|
148
148
|
} from "./chunk-PQIB66SG.js";
|
|
@@ -161,6 +161,7 @@ import {
|
|
|
161
161
|
DESCRIPTION_TEMPLATE,
|
|
162
162
|
GATES,
|
|
163
163
|
LAYOUT_MARKERS,
|
|
164
|
+
MERGE_SIGN_OFF,
|
|
164
165
|
NO_GATE_TEXT_TEMPLATE,
|
|
165
166
|
ORCHESTRATOR_LIMITS,
|
|
166
167
|
QA_SECTIONS,
|
|
@@ -200,7 +201,7 @@ import {
|
|
|
200
201
|
workflowRegistryDir,
|
|
201
202
|
writeGlobalConfig,
|
|
202
203
|
writeProjectConfig
|
|
203
|
-
} from "./chunk-
|
|
204
|
+
} from "./chunk-OYUBT6RS.js";
|
|
204
205
|
import {
|
|
205
206
|
__toESM
|
|
206
207
|
} from "./chunk-G3PMV62Z.js";
|
|
@@ -208,10 +209,10 @@ import {
|
|
|
208
209
|
// src/app/entry/cli.tsx
|
|
209
210
|
import { render } from "ink";
|
|
210
211
|
import meow from "meow";
|
|
211
|
-
import
|
|
212
|
+
import fs12 from "fs";
|
|
212
213
|
import { createRequire } from "module";
|
|
213
214
|
import os5 from "os";
|
|
214
|
-
import
|
|
215
|
+
import path12 from "path";
|
|
215
216
|
import { fileURLToPath } from "url";
|
|
216
217
|
|
|
217
218
|
// src/app/shell/sessionUiState.ts
|
|
@@ -5330,14 +5331,14 @@ function extractWebSearch(_input, response) {
|
|
|
5330
5331
|
return { type: "text", content: extractTextContent(response), maxLines: 10 };
|
|
5331
5332
|
}
|
|
5332
5333
|
function extractNotebookEdit(input, _response) {
|
|
5333
|
-
const
|
|
5334
|
+
const path13 = typeof input["notebook_path"] === "string" ? input["notebook_path"] : "";
|
|
5334
5335
|
const mode = typeof input["edit_mode"] === "string" ? input["edit_mode"] : "replace";
|
|
5335
5336
|
const source = typeof input["new_source"] === "string" ? input["new_source"] : "";
|
|
5336
|
-
if (!source) return { type: "text", content: `${mode} cell in ${
|
|
5337
|
+
if (!source) return { type: "text", content: `${mode} cell in ${path13}` };
|
|
5337
5338
|
return {
|
|
5338
5339
|
type: "code",
|
|
5339
5340
|
content: source,
|
|
5340
|
-
language: detectLanguage(
|
|
5341
|
+
language: detectLanguage(path13),
|
|
5341
5342
|
maxLines: 10
|
|
5342
5343
|
};
|
|
5343
5344
|
}
|
|
@@ -5863,10 +5864,10 @@ function extractToolSubject(toolName, toolInput) {
|
|
|
5863
5864
|
const parts = [];
|
|
5864
5865
|
const pattern = str(input, "pattern");
|
|
5865
5866
|
const glob = str(input, "glob");
|
|
5866
|
-
const
|
|
5867
|
+
const path13 = str(input, "path");
|
|
5867
5868
|
if (pattern) parts.push(`"${pattern}"`);
|
|
5868
5869
|
if (glob) parts.push(glob);
|
|
5869
|
-
if (
|
|
5870
|
+
if (path13) parts.push(`in ${path13}`);
|
|
5870
5871
|
return parts.length > 0 ? parts.join(" ") : void 0;
|
|
5871
5872
|
}
|
|
5872
5873
|
case "Skill":
|
|
@@ -13097,7 +13098,7 @@ var thisMachine = (installationId) => ({
|
|
|
13097
13098
|
os: `${platform()} ${release()}`.slice(0, 64),
|
|
13098
13099
|
softwareVersion: runnerSoftwareVersion
|
|
13099
13100
|
});
|
|
13100
|
-
var post = (origin,
|
|
13101
|
+
var post = (origin, path13, body) => fetch(new URL(path13, origin), {
|
|
13101
13102
|
method: "POST",
|
|
13102
13103
|
headers: { "Content-Type": "application/json" },
|
|
13103
13104
|
body: JSON.stringify(body)
|
|
@@ -13116,8 +13117,8 @@ async function alreadyConnected(existing, { forTeam, ownIdentity }) {
|
|
|
13116
13117
|
const problem = own ? await credentialAccepted(own.config) ? void 0 : "lapsed" : forTeam(existing.orgId).length ? "other_runner" : "missing";
|
|
13117
13118
|
const runner = { orgId: existing.orgId, runnerId: existing.runnerId, name: existing.name };
|
|
13118
13119
|
if (own && problem === void 0) return { status: "already_connected", ...runner, path: own.path };
|
|
13119
|
-
const
|
|
13120
|
-
return { status: "identity_unusable", ...runner, problem, ...
|
|
13120
|
+
const path13 = (own ?? forTeam(existing.orgId)[0])?.path;
|
|
13121
|
+
return { status: "identity_unusable", ...runner, problem, ...path13 ? { path: path13 } : {} };
|
|
13121
13122
|
}
|
|
13122
13123
|
async function connectWithCode({
|
|
13123
13124
|
code,
|
|
@@ -13189,20 +13190,20 @@ async function saveIdentity(origin, credential, { single, forTeam }, directory)
|
|
|
13189
13190
|
name: credential.name
|
|
13190
13191
|
});
|
|
13191
13192
|
const replaced = [];
|
|
13192
|
-
for (const { path:
|
|
13193
|
-
const aside = `${
|
|
13194
|
-
await rename(
|
|
13193
|
+
for (const { path: path14 } of forTeam(credential.orgId)) {
|
|
13194
|
+
const aside = `${path14}.replaced-${Date.now()}`;
|
|
13195
|
+
await rename(path14, aside);
|
|
13195
13196
|
replaced.push(aside);
|
|
13196
13197
|
}
|
|
13197
|
-
const
|
|
13198
|
+
const path13 = single ?? teamIdentityPath(origin, credential.orgId, directory);
|
|
13198
13199
|
try {
|
|
13199
|
-
await writeIdentityOnce(
|
|
13200
|
+
await writeIdentityOnce(path13, JSON.stringify(config, null, 2) + "\n");
|
|
13200
13201
|
} catch (error) {
|
|
13201
13202
|
if (error.code === "EEXIST")
|
|
13202
|
-
throw new Error(`Another Connect wrote a Runner identity for this Team at ${
|
|
13203
|
+
throw new Error(`Another Connect wrote a Runner identity for this Team at ${path13} meanwhile; it was left unchanged.`);
|
|
13203
13204
|
throw error;
|
|
13204
13205
|
}
|
|
13205
|
-
return { status: "connected", config, path:
|
|
13206
|
+
return { status: "connected", config, path: path13, replaced };
|
|
13206
13207
|
}
|
|
13207
13208
|
function pathSegment(value) {
|
|
13208
13209
|
const segment = encodeURIComponent(value);
|
|
@@ -13242,7 +13243,7 @@ async function refuseExisting(target) {
|
|
|
13242
13243
|
throw new Error(`This machine already has a Runner${name ? ` (${name})` : ""} configured at ${target}.`);
|
|
13243
13244
|
}
|
|
13244
13245
|
async function disconnectRunner({
|
|
13245
|
-
identity: { path:
|
|
13246
|
+
identity: { path: path13, config },
|
|
13246
13247
|
serviceManager,
|
|
13247
13248
|
othersRemain
|
|
13248
13249
|
}) {
|
|
@@ -13275,7 +13276,7 @@ async function disconnectRunner({
|
|
|
13275
13276
|
await restore();
|
|
13276
13277
|
throw new Error(`The Hub refused to disconnect ${name} (${body.error ?? response.status}); it is still connected.`);
|
|
13277
13278
|
}
|
|
13278
|
-
await forgetRunner(
|
|
13279
|
+
await forgetRunner(path13);
|
|
13279
13280
|
if (othersRemain) await restore();
|
|
13280
13281
|
else await serviceManager?.remove();
|
|
13281
13282
|
return result;
|
|
@@ -13428,18 +13429,18 @@ async function runRunnerCommandV2(input, options = {}) {
|
|
|
13428
13429
|
`This machine is already connected to this Team as ${outcome.name}, but ${why}. ` + (code ? `The code was not used: connect a new Runner in its place with ${retry}. ` : `Connect a new Runner in its place with a Connect code from Settings \u2192 Runners: ${retry}. `) + `The old ${outcome.name} stays listed in Settings \u2192 Runners, offline.`
|
|
13429
13430
|
);
|
|
13430
13431
|
}
|
|
13431
|
-
const { config, path:
|
|
13432
|
+
const { config, path: path13, replaced } = outcome;
|
|
13432
13433
|
for (const aside of replaced)
|
|
13433
13434
|
process.stdout.write(
|
|
13434
13435
|
`The Hub no longer recognised this machine's earlier identity for this Team; it was moved aside to ${aside}.
|
|
13435
13436
|
`
|
|
13436
13437
|
);
|
|
13437
13438
|
process.stdout.write(
|
|
13438
|
-
`Connected as ${config.name ?? config.runnerId}. Identity saved to ${
|
|
13439
|
+
`Connected as ${config.name ?? config.runnerId}. Identity saved to ${path13}.
|
|
13439
13440
|
`
|
|
13440
13441
|
);
|
|
13441
13442
|
if (services) {
|
|
13442
|
-
const service = await ensureService(services, config,
|
|
13443
|
+
const service = await ensureService(services, config, path13);
|
|
13443
13444
|
process.stdout.write(`${serviceStarted(service)}
|
|
13444
13445
|
`);
|
|
13445
13446
|
return 0;
|
|
@@ -13448,7 +13449,7 @@ async function runRunnerCommandV2(input, options = {}) {
|
|
|
13448
13449
|
process.stdout.write(
|
|
13449
13450
|
"No launchd or systemd here to run the Runner as a background service; running it in the foreground instead.\n"
|
|
13450
13451
|
);
|
|
13451
|
-
await runConfiguredRunner(
|
|
13452
|
+
await runConfiguredRunner(path13, options.signal);
|
|
13452
13453
|
return 0;
|
|
13453
13454
|
}
|
|
13454
13455
|
case "disconnect": {
|
|
@@ -13519,9 +13520,9 @@ async function runRunnerCommandV2(input, options = {}) {
|
|
|
13519
13520
|
configured: true,
|
|
13520
13521
|
// One entry per Team this machine serves; credentials are never printed.
|
|
13521
13522
|
teams: identities.map(
|
|
13522
|
-
({ path:
|
|
13523
|
+
({ path: path13, config: { token: _token, ...config } }) => ({
|
|
13523
13524
|
...config,
|
|
13524
|
-
path:
|
|
13525
|
+
path: path13
|
|
13525
13526
|
})
|
|
13526
13527
|
),
|
|
13527
13528
|
service: service ? {
|
|
@@ -13677,7 +13678,9 @@ function computeTemplateVersion() {
|
|
|
13677
13678
|
JSON.stringify(QA_SECTIONS),
|
|
13678
13679
|
JSON.stringify(GATES),
|
|
13679
13680
|
JSON.stringify(RENDERER_RULES),
|
|
13680
|
-
JSON.stringify(LAYOUT_MARKERS)
|
|
13681
|
+
JSON.stringify(LAYOUT_MARKERS),
|
|
13682
|
+
JSON.stringify(MERGE_SIGN_OFF),
|
|
13683
|
+
BUILD_STRINGS.mergeSignOff
|
|
13681
13684
|
];
|
|
13682
13685
|
const hash = createHash("sha256");
|
|
13683
13686
|
for (const part of parts) hash.update(`${part.length}:${part}`);
|
|
@@ -13782,7 +13785,8 @@ function runBuildPlan(deps) {
|
|
|
13782
13785
|
fills: result.fills.map((fill) => ({
|
|
13783
13786
|
...fill,
|
|
13784
13787
|
path: fillPath(fill.identifier)
|
|
13785
|
-
}))
|
|
13788
|
+
})),
|
|
13789
|
+
...result.reason ? { reason: result.reason } : {}
|
|
13786
13790
|
};
|
|
13787
13791
|
const host = deps.hostCapabilities();
|
|
13788
13792
|
for (const workflow of result.workflows) {
|
|
@@ -13866,9 +13870,9 @@ var DrispClient = class _DrispClient {
|
|
|
13866
13870
|
if (!match) throw new Error("invalid_hub_url");
|
|
13867
13871
|
return new _DrispClient(url.origin, decodeURIComponent(match[1]), async () => token);
|
|
13868
13872
|
}
|
|
13869
|
-
async request(
|
|
13873
|
+
async request(path13, body) {
|
|
13870
13874
|
const response = await fetch(
|
|
13871
|
-
`${this.baseUrl}/v2/orgs/${encodeURIComponent(this.orgId)}${
|
|
13875
|
+
`${this.baseUrl}/v2/orgs/${encodeURIComponent(this.orgId)}${path13}`,
|
|
13872
13876
|
{
|
|
13873
13877
|
method: body === void 0 ? "GET" : "POST",
|
|
13874
13878
|
headers: {
|
|
@@ -14776,6 +14780,15 @@ var receiptSchema = external_exports.strictObject({
|
|
|
14776
14780
|
evidence: external_exports.string().min(1).max(16e3),
|
|
14777
14781
|
kind: external_exports.literal("agent-attestation")
|
|
14778
14782
|
});
|
|
14783
|
+
var COMMIT_SHA = /^[0-9a-f]{40}$/;
|
|
14784
|
+
var gateEvidenceSchema = external_exports.strictObject({
|
|
14785
|
+
receipts: external_exports.array(receiptSchema).max(100),
|
|
14786
|
+
/**
|
|
14787
|
+
* The Sign-off on the Merge step (`merge: you`): the head commit the child attests the member signed off on in
|
|
14788
|
+
* its Thread. Like the receipts, an agent attestation.
|
|
14789
|
+
*/
|
|
14790
|
+
signOff: external_exports.strictObject({ sha: external_exports.string().regex(COMMIT_SHA) }).optional()
|
|
14791
|
+
});
|
|
14779
14792
|
var QA_KINDS = fillSchema.shape.qa.options;
|
|
14780
14793
|
function runBuildStep(input) {
|
|
14781
14794
|
const directory = requireEnv(input.env, "DRISP_RUN_ARTIFACTS");
|
|
@@ -14805,19 +14818,57 @@ function runBuildStep(input) {
|
|
|
14805
14818
|
throw new BuildUsageError(
|
|
14806
14819
|
`Step must be a whole number between 1 and ${titles.length}, not "${input.n ?? ""}".`
|
|
14807
14820
|
);
|
|
14821
|
+
if (input.merge !== void 0 && input.merge !== "agent" && input.merge !== "you")
|
|
14822
|
+
throw new BuildUsageError("--merge must be agent or you.");
|
|
14823
|
+
const mergeStep = titles.indexOf("Merge") + 1;
|
|
14824
|
+
const signsOff = input.merge === "you" && index === mergeStep;
|
|
14825
|
+
if (input.signOff !== void 0) {
|
|
14826
|
+
if (input.merge !== "you")
|
|
14827
|
+
throw new BuildUsageError(
|
|
14828
|
+
"--sign-off needs --merge you: agents merge this pull request."
|
|
14829
|
+
);
|
|
14830
|
+
if (mergeStep === 0)
|
|
14831
|
+
throw new BuildUsageError(
|
|
14832
|
+
"--sign-off is for the Merge step, and this workflow has none."
|
|
14833
|
+
);
|
|
14834
|
+
if (!signsOff)
|
|
14835
|
+
throw new BuildUsageError(
|
|
14836
|
+
`--sign-off is for the Merge step (${mergeStep}), not step ${index}.`
|
|
14837
|
+
);
|
|
14838
|
+
if (!COMMIT_SHA.test(input.signOff))
|
|
14839
|
+
throw new BuildUsageError(
|
|
14840
|
+
"--sign-off needs the full 40-character commit SHA the member signed off on."
|
|
14841
|
+
);
|
|
14842
|
+
}
|
|
14808
14843
|
if (!fs8.statSync(directory, { throwIfNoEntry: false })?.isDirectory())
|
|
14809
14844
|
throw new BuildUsageError(
|
|
14810
14845
|
`DRISP_RUN_ARTIFACTS (${directory}) is not a directory.`
|
|
14811
14846
|
);
|
|
14812
14847
|
const receiptsPath = path8.join(directory, "gate_evidence.json");
|
|
14813
14848
|
let receipts = [];
|
|
14849
|
+
let signOff;
|
|
14814
14850
|
if (fs8.existsSync(receiptsPath)) {
|
|
14815
14851
|
try {
|
|
14816
|
-
|
|
14852
|
+
({ receipts, signOff } = gateEvidenceSchema.parse(
|
|
14853
|
+
JSON.parse(fs8.readFileSync(receiptsPath, "utf8"))
|
|
14854
|
+
));
|
|
14817
14855
|
} catch {
|
|
14818
14856
|
throw new BuildUsageError("Gate evidence is invalid.");
|
|
14819
14857
|
}
|
|
14820
14858
|
}
|
|
14859
|
+
const save = () => fs8.writeFileSync(
|
|
14860
|
+
receiptsPath,
|
|
14861
|
+
JSON.stringify({ receipts, ...signOff ? { signOff } : {} }) + "\n"
|
|
14862
|
+
);
|
|
14863
|
+
const head = signsOff && (input.signOff || input.verify) ? headCommit(input.cwd ?? process.cwd()) : "";
|
|
14864
|
+
if (input.signOff !== void 0) {
|
|
14865
|
+
if (input.signOff !== head)
|
|
14866
|
+
throw new BuildUsageError(
|
|
14867
|
+
`The Sign-off names ${input.signOff}, but HEAD is ${head}. ${askForSignOff(head)}`
|
|
14868
|
+
);
|
|
14869
|
+
signOff = { sha: head };
|
|
14870
|
+
save();
|
|
14871
|
+
}
|
|
14821
14872
|
const revision = receipts.length || input.complete || input.verify ? workRevision(input.cwd ?? process.cwd()) : "";
|
|
14822
14873
|
const fresh = (receipt) => ["Explore", "Pull request", "Evidence", "Merge"].includes(receipt.title) || receipt.revision === revision;
|
|
14823
14874
|
if (input.verify || input.complete && titles[index - 1] === "Evidence") {
|
|
@@ -14827,6 +14878,20 @@ function runBuildStep(input) {
|
|
|
14827
14878
|
`Missing or stale evidence: ${missing.join(", ")}. Repeat those steps on the current revision.`
|
|
14828
14879
|
);
|
|
14829
14880
|
}
|
|
14881
|
+
if (signsOff && input.verify) {
|
|
14882
|
+
if (!signOff)
|
|
14883
|
+
throw new BuildUsageError(
|
|
14884
|
+
`No Sign-off on HEAD (${head}) yet. ${askForSignOff(head)}`
|
|
14885
|
+
);
|
|
14886
|
+
if (signOff.sha !== head)
|
|
14887
|
+
throw new BuildUsageError(
|
|
14888
|
+
`The Sign-off names ${signOff.sha}, but HEAD is now ${head}. ${askForSignOff(head)}`
|
|
14889
|
+
);
|
|
14890
|
+
}
|
|
14891
|
+
if (signsOff && input.complete && !signOff)
|
|
14892
|
+
throw new BuildUsageError(
|
|
14893
|
+
"No Sign-off was recorded: verify and merge only after the member signs off on HEAD."
|
|
14894
|
+
);
|
|
14830
14895
|
if (input.complete) {
|
|
14831
14896
|
if (!input.evidence)
|
|
14832
14897
|
throw new BuildUsageError(
|
|
@@ -14850,7 +14915,7 @@ function runBuildStep(input) {
|
|
|
14850
14915
|
evidence,
|
|
14851
14916
|
kind: "agent-attestation"
|
|
14852
14917
|
});
|
|
14853
|
-
|
|
14918
|
+
save();
|
|
14854
14919
|
}
|
|
14855
14920
|
const artifact = {
|
|
14856
14921
|
index,
|
|
@@ -14867,12 +14932,23 @@ function runBuildStep(input) {
|
|
|
14867
14932
|
);
|
|
14868
14933
|
return artifact;
|
|
14869
14934
|
}
|
|
14935
|
+
function askForSignOff(sha) {
|
|
14936
|
+
return `Ask the member: end the Turn with <!-- NEEDS_HUMAN: ${BUILD_STRINGS.mergeSignOff.replace("{sha}", sha)} -->`;
|
|
14937
|
+
}
|
|
14938
|
+
var gitIn = (cwd) => (...args) => execFileSync("git", args, {
|
|
14939
|
+
cwd,
|
|
14940
|
+
encoding: "utf8",
|
|
14941
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
14942
|
+
});
|
|
14943
|
+
function headCommit(cwd) {
|
|
14944
|
+
try {
|
|
14945
|
+
return gitIn(cwd)("rev-parse", "HEAD").trim();
|
|
14946
|
+
} catch {
|
|
14947
|
+
throw new BuildUsageError("A Sign-off must be recorded in a git worktree.");
|
|
14948
|
+
}
|
|
14949
|
+
}
|
|
14870
14950
|
function workRevision(cwd) {
|
|
14871
|
-
const git2 = (
|
|
14872
|
-
cwd,
|
|
14873
|
-
encoding: "utf8",
|
|
14874
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
14875
|
-
});
|
|
14951
|
+
const git2 = gitIn(cwd);
|
|
14876
14952
|
try {
|
|
14877
14953
|
const hash = createHash2("sha256").update(git2("rev-parse", "HEAD")).update(git2("diff", "HEAD", "--", ".", ":!.athena/evidence"));
|
|
14878
14954
|
for (const name of git2("ls-files", "--others", "--exclude-standard", "-z").split("\0").filter(Boolean).sort()) {
|
|
@@ -14939,22 +15015,87 @@ function runBuildMerged(input) {
|
|
|
14939
15015
|
return parsed.data;
|
|
14940
15016
|
}
|
|
14941
15017
|
|
|
15018
|
+
// src/core/build/check.ts
|
|
15019
|
+
import { spawnSync } from "child_process";
|
|
15020
|
+
import fs10 from "fs";
|
|
15021
|
+
import path10 from "path";
|
|
15022
|
+
var checkRecordSchema = external_exports.strictObject({
|
|
15023
|
+
name: external_exports.string().trim().min(1).max(64),
|
|
15024
|
+
outcome: external_exports.enum(["passed", "failed"])
|
|
15025
|
+
});
|
|
15026
|
+
var NAME_PATTERN = /^[^\p{Cc}]+$/u;
|
|
15027
|
+
var MAX_RECORDS = 50;
|
|
15028
|
+
function spawnCheck(command, cwd, onOutput) {
|
|
15029
|
+
const [program, ...args] = command;
|
|
15030
|
+
const result = spawnSync(program, args, {
|
|
15031
|
+
cwd,
|
|
15032
|
+
stdio: onOutput ? ["inherit", "pipe", "inherit"] : "inherit",
|
|
15033
|
+
maxBuffer: 64 * 1024 * 1024
|
|
15034
|
+
});
|
|
15035
|
+
if (onOutput) onOutput(result.stdout);
|
|
15036
|
+
if (result.error) return 127;
|
|
15037
|
+
return result.status ?? 1;
|
|
15038
|
+
}
|
|
15039
|
+
function readRecords(file) {
|
|
15040
|
+
try {
|
|
15041
|
+
const parsed = external_exports.array(checkRecordSchema).safeParse(JSON.parse(fs10.readFileSync(file, "utf8")));
|
|
15042
|
+
return parsed.success ? parsed.data : [];
|
|
15043
|
+
} catch {
|
|
15044
|
+
return [];
|
|
15045
|
+
}
|
|
15046
|
+
}
|
|
15047
|
+
function runBuildCheck(input) {
|
|
15048
|
+
const directory = requireEnv(input.env, "DRISP_RUN_ARTIFACTS");
|
|
15049
|
+
const name = (input.name ?? "").trim();
|
|
15050
|
+
if (!name || name.length > 64 || !NAME_PATTERN.test(name))
|
|
15051
|
+
throw new BuildUsageError(
|
|
15052
|
+
"A check needs a name of 1 to 64 characters on one line."
|
|
15053
|
+
);
|
|
15054
|
+
if (input.command.length === 0 || input.command.some((token) => !token))
|
|
15055
|
+
throw new BuildUsageError("A check needs a command after `--`.");
|
|
15056
|
+
if (!fs10.statSync(directory, { throwIfNoEntry: false })?.isDirectory())
|
|
15057
|
+
throw new BuildUsageError(
|
|
15058
|
+
`DRISP_RUN_ARTIFACTS (${directory}) is not a directory.`
|
|
15059
|
+
);
|
|
15060
|
+
const code = input.run(input.command, input.cwd);
|
|
15061
|
+
const file = path10.join(directory, "checks.json");
|
|
15062
|
+
const record = { name, outcome: code === 0 ? "passed" : "failed" };
|
|
15063
|
+
const records = readRecords(file);
|
|
15064
|
+
const at = records.findIndex((existing) => existing.name === name);
|
|
15065
|
+
if (at >= 0) records[at] = record;
|
|
15066
|
+
else records.push(record);
|
|
15067
|
+
const temporary = `${file}.${process.pid}.tmp`;
|
|
15068
|
+
fs10.writeFileSync(
|
|
15069
|
+
temporary,
|
|
15070
|
+
`${JSON.stringify(records.slice(-MAX_RECORDS))}
|
|
15071
|
+
`
|
|
15072
|
+
);
|
|
15073
|
+
fs10.renameSync(temporary, file);
|
|
15074
|
+
return code;
|
|
15075
|
+
}
|
|
15076
|
+
|
|
14942
15077
|
// src/app/entry/buildCommand.ts
|
|
14943
15078
|
var USAGE3 = `Usage: drisp build plan --input <path>
|
|
14944
15079
|
drisp build tick --input <path> [--rebase-failed <IDENTIFIER>]...
|
|
14945
15080
|
drisp build checks
|
|
14946
15081
|
drisp build step <n> [--qa none|web|macos|both] [--profile implementation|documentation|investigation]
|
|
14947
|
-
[--test-first true|false] [--review true|false] [--
|
|
15082
|
+
[--test-first true|false] [--review true|false] [--merge agent|you] [--sign-off <sha>]
|
|
15083
|
+
[--complete --evidence <file>] [--verify]
|
|
14948
15084
|
drisp build merged [<pr url or number>]
|
|
15085
|
+
drisp build check <name> -- <command>
|
|
14949
15086
|
plan and tick run inside an Orchestrator Run (DRISP_RUN_ID, DRISP_RUNNER_ID and DRISP_RUN_ARTIFACTS set; tick also
|
|
14950
15087
|
DRISP_RUN_TOKEN and DRISP_HUB_URL). DRISP_BUILD_PLAN_INPUT=<path> stands in for plan's --input, with
|
|
14951
15088
|
fills/<IDENTIFIER>.json beside it. checks runs in a child's worktree: the merge gate's verdict on HEAD's GitHub runs.
|
|
14952
15089
|
step runs in any Run with DRISP_RUN_ARTIFACTS set: it writes the step artifact {index, title, total} for gate <n> of the
|
|
14953
15090
|
selected build-child profile (QA is a gate unless --qa none) and refuses an index outside the list.
|
|
14954
15091
|
--complete --evidence <file> records an agent attestation bound to the source revision; --verify requires fresh
|
|
14955
|
-
receipts for all preceding steps. Keep evidence files under .athena/evidence/.
|
|
15092
|
+
receipts for all preceding steps. Keep evidence files under .athena/evidence/. With --merge you, the Merge step's
|
|
15093
|
+
--verify also needs the member's Sign-off on HEAD (--complete one at all), which --sign-off <sha> records for that full
|
|
15094
|
+
SHA only.
|
|
14956
15095
|
merged runs in a child's worktree after its pull request merged: it writes the pull_request artifact
|
|
14957
|
-
{url, number, sha, mergedAt} and refuses a pull request that is not merged
|
|
15096
|
+
{url, number, sha, mergedAt} and refuses a pull request that is not merged.
|
|
15097
|
+
check runs <command>, passes its output through, exits with its code and records {name, outcome} in the checks run artifact
|
|
15098
|
+
(DRISP_RUN_ARTIFACTS set); it refuses a missing name or command before running anything.`;
|
|
14958
15099
|
function githubPullRequests(folder, branch) {
|
|
14959
15100
|
const output = execFileSync2(
|
|
14960
15101
|
"gh",
|
|
@@ -15072,6 +15213,16 @@ async function runBuildCommand(input) {
|
|
|
15072
15213
|
case "checks":
|
|
15073
15214
|
output = runBuildChecks(input.cwd, input.checks ?? defaultChecks);
|
|
15074
15215
|
break;
|
|
15216
|
+
case "check": {
|
|
15217
|
+
const code = runBuildCheck({
|
|
15218
|
+
env: env2,
|
|
15219
|
+
cwd: input.cwd,
|
|
15220
|
+
name: input.positional?.[0],
|
|
15221
|
+
command: input.command ?? [],
|
|
15222
|
+
run: input.checkRunner ?? spawnCheck
|
|
15223
|
+
});
|
|
15224
|
+
return code;
|
|
15225
|
+
}
|
|
15075
15226
|
case "merged":
|
|
15076
15227
|
output = runBuildMerged({
|
|
15077
15228
|
env: env2,
|
|
@@ -15088,6 +15239,8 @@ async function runBuildCommand(input) {
|
|
|
15088
15239
|
profile: input.flags.profile,
|
|
15089
15240
|
testFirst: input.flags.testFirst,
|
|
15090
15241
|
review: input.flags.review,
|
|
15242
|
+
merge: input.flags.merge,
|
|
15243
|
+
signOff: input.flags.signOff,
|
|
15091
15244
|
complete: input.flags.complete,
|
|
15092
15245
|
verify: input.flags.verify,
|
|
15093
15246
|
evidence: input.flags.evidence,
|
|
@@ -15116,9 +15269,9 @@ import process7 from "process";
|
|
|
15116
15269
|
|
|
15117
15270
|
// src/harnesses/claude/system/doctorEnvironment.ts
|
|
15118
15271
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
15119
|
-
import * as
|
|
15272
|
+
import * as fs11 from "fs";
|
|
15120
15273
|
import * as os4 from "os";
|
|
15121
|
-
import * as
|
|
15274
|
+
import * as path11 from "path";
|
|
15122
15275
|
var MANAGED_POLICY_KEYS = [
|
|
15123
15276
|
"forceLoginMethod",
|
|
15124
15277
|
"forceLoginOrgUUID",
|
|
@@ -15222,8 +15375,8 @@ function collectEnvironment(options = {}) {
|
|
|
15222
15375
|
const detectVersion = options.detectClaudeVersionFn ?? detectClaudeVersion;
|
|
15223
15376
|
const runAuthStatus = options.runClaudeAuthStatusFn ?? runClaudeAuthStatus;
|
|
15224
15377
|
const resolveForwarder = options.resolveHookForwarderCommandFn ?? resolveHookForwarderCommand;
|
|
15225
|
-
const statFn = options.statFn ?? ((p) =>
|
|
15226
|
-
const readFileFn = options.readFileFn ?? ((p) =>
|
|
15378
|
+
const statFn = options.statFn ?? ((p) => fs11.statSync(p));
|
|
15379
|
+
const readFileFn = options.readFileFn ?? ((p) => fs11.readFileSync(p, "utf8"));
|
|
15227
15380
|
const settingsPaths = resolveClaudeSettingsSurfacePaths(
|
|
15228
15381
|
homeDir,
|
|
15229
15382
|
cwd,
|
|
@@ -15268,7 +15421,7 @@ function collectEnvironment(options = {}) {
|
|
|
15268
15421
|
});
|
|
15269
15422
|
let globalConfigPresent = false;
|
|
15270
15423
|
try {
|
|
15271
|
-
statFn(
|
|
15424
|
+
statFn(path11.join(homeDir, ".claude.json"));
|
|
15272
15425
|
globalConfigPresent = true;
|
|
15273
15426
|
} catch {
|
|
15274
15427
|
globalConfigPresent = false;
|
|
@@ -15748,13 +15901,13 @@ function runRunsCommand(input) {
|
|
|
15748
15901
|
// src/app/entry/cli.tsx
|
|
15749
15902
|
import { jsx as jsx24 } from "react/jsx-runtime";
|
|
15750
15903
|
function resolvePackageJsonPath(entryUrl) {
|
|
15751
|
-
let currentDir =
|
|
15904
|
+
let currentDir = path12.dirname(fileURLToPath(entryUrl));
|
|
15752
15905
|
for (; ; ) {
|
|
15753
|
-
const candidatePath =
|
|
15754
|
-
if (
|
|
15906
|
+
const candidatePath = path12.join(currentDir, "package.json");
|
|
15907
|
+
if (fs12.existsSync(candidatePath)) {
|
|
15755
15908
|
return candidatePath;
|
|
15756
15909
|
}
|
|
15757
|
-
const parentDir =
|
|
15910
|
+
const parentDir = path12.dirname(currentDir);
|
|
15758
15911
|
if (parentDir === currentDir) {
|
|
15759
15912
|
throw new Error("Could not locate package.json for CLI metadata.");
|
|
15760
15913
|
}
|
|
@@ -15926,7 +16079,8 @@ var cli = meow(
|
|
|
15926
16079
|
build plan --input <path> Plan a Build inside an Orchestrator Run (prints one JSON object)
|
|
15927
16080
|
build tick --input <path> Run one step of a started Build (prints the next action; [--rebase-failed <ID>]...)
|
|
15928
16081
|
build checks The merge gate's verdict on HEAD's GitHub runs (in a child's worktree)
|
|
15929
|
-
build step <n> Report the Run's current step: writes the step run artifact ([--qa none|web|macos|both], --profile, --complete --evidence, --verify)
|
|
16082
|
+
build step <n> Report the Run's current step: writes the step run artifact ([--qa none|web|macos|both], --profile, --merge you, --sign-off <sha>, --complete --evidence, --verify)
|
|
16083
|
+
build check <name> -- <cmd> Run a command and record its outcome: writes the checks run artifact
|
|
15930
16084
|
build merged Record the merged pull request: writes the pull_request run artifact
|
|
15931
16085
|
telemetry [action] Manage anonymous telemetry (enable/disable/status)
|
|
15932
16086
|
doctor Diagnose Claude headless setup (use with --harness=claude)
|
|
@@ -16068,6 +16222,8 @@ var cli = meow(
|
|
|
16068
16222
|
profile: { type: "string" },
|
|
16069
16223
|
testFirst: { type: "string" },
|
|
16070
16224
|
review: { type: "string" },
|
|
16225
|
+
merge: { type: "string" },
|
|
16226
|
+
signOff: { type: "string" },
|
|
16071
16227
|
complete: { type: "boolean" },
|
|
16072
16228
|
verify: { type: "boolean" },
|
|
16073
16229
|
evidence: { type: "string" },
|
|
@@ -16113,7 +16269,7 @@ var cli = meow(
|
|
|
16113
16269
|
}
|
|
16114
16270
|
);
|
|
16115
16271
|
async function main() {
|
|
16116
|
-
const projectDir =
|
|
16272
|
+
const projectDir = path12.resolve(cli.flags.projectDir);
|
|
16117
16273
|
const [command, ...commandArgs] = cli.input;
|
|
16118
16274
|
const isRunCommand = command === "run" || command === "exec";
|
|
16119
16275
|
if (command && !KNOWN_COMMANDS.has(command)) {
|
|
@@ -16167,7 +16323,7 @@ Available commands: ${[...KNOWN_COMMANDS].join(", ")}`
|
|
|
16167
16323
|
await exitWith(1);
|
|
16168
16324
|
return;
|
|
16169
16325
|
}
|
|
16170
|
-
const { default: WorkflowInstallWizard } = await import("./WorkflowInstallWizard-
|
|
16326
|
+
const { default: WorkflowInstallWizard } = await import("./WorkflowInstallWizard-IOJD3YOA.js");
|
|
16171
16327
|
const { waitUntilExit } = render(
|
|
16172
16328
|
/* @__PURE__ */ jsx24(
|
|
16173
16329
|
WorkflowInstallWizard,
|
|
@@ -16246,16 +16402,23 @@ Available commands: ${[...KNOWN_COMMANDS].join(", ")}`
|
|
|
16246
16402
|
return;
|
|
16247
16403
|
}
|
|
16248
16404
|
if (command === "build") {
|
|
16249
|
-
const
|
|
16405
|
+
const rawArgs = process.argv.slice(2);
|
|
16406
|
+
const dashIdx = rawArgs.indexOf("--");
|
|
16407
|
+
const checkTokens = dashIdx >= 0 ? rawArgs.slice(dashIdx + 1) : [];
|
|
16408
|
+
const cleanArgs = checkTokens.length > 0 ? commandArgs.slice(0, commandArgs.length - checkTokens.length) : commandArgs;
|
|
16409
|
+
const [subcommand = "", ...buildArgs] = cleanArgs;
|
|
16250
16410
|
await exitWith(
|
|
16251
16411
|
await runBuildCommand({
|
|
16252
16412
|
subcommand,
|
|
16253
16413
|
positional: buildArgs,
|
|
16414
|
+
command: checkTokens,
|
|
16254
16415
|
flags: {
|
|
16255
16416
|
...typeof cli.flags.qa === "string" ? { qa: cli.flags.qa } : {},
|
|
16256
16417
|
profile: cli.flags.profile,
|
|
16257
16418
|
testFirst: cli.flags.testFirst,
|
|
16258
16419
|
review: cli.flags.review,
|
|
16420
|
+
merge: cli.flags.merge,
|
|
16421
|
+
signOff: cli.flags.signOff,
|
|
16259
16422
|
complete: cli.flags.complete,
|
|
16260
16423
|
verify: cli.flags.verify,
|
|
16261
16424
|
evidence: cli.flags.evidence,
|
|
@@ -16340,8 +16503,8 @@ Available commands: ${[...KNOWN_COMMANDS].join(", ")}`
|
|
|
16340
16503
|
const showSetup = isRunCommand ? false : shouldShowSetup({
|
|
16341
16504
|
cliInput: cli.input,
|
|
16342
16505
|
setupComplete: globalConfig.setupComplete,
|
|
16343
|
-
globalConfigExists:
|
|
16344
|
-
|
|
16506
|
+
globalConfigExists: fs12.existsSync(
|
|
16507
|
+
path12.join(os5.homedir(), ".config", "athena", "config.json")
|
|
16345
16508
|
)
|
|
16346
16509
|
});
|
|
16347
16510
|
let runtimeConfig;
|
package/dist/dashboard-daemon.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import "./chunk-
|
|
2
|
-
import "./chunk-
|
|
1
|
+
import "./chunk-TBUUNMC6.js";
|
|
2
|
+
import "./chunk-HGMJ3YL2.js";
|
|
3
3
|
import "./chunk-PQIB66SG.js";
|
|
4
|
-
import "./chunk-
|
|
4
|
+
import "./chunk-OYUBT6RS.js";
|
|
5
5
|
import "./chunk-G3PMV62Z.js";
|
|
6
6
|
//# sourceMappingURL=dashboard-daemon.js.map
|
package/dist/runner.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import "./chunk-
|
|
2
|
-
import "./chunk-
|
|
1
|
+
import "./chunk-TBUUNMC6.js";
|
|
2
|
+
import "./chunk-HGMJ3YL2.js";
|
|
3
3
|
import "./chunk-PQIB66SG.js";
|
|
4
|
-
import "./chunk-
|
|
4
|
+
import "./chunk-OYUBT6RS.js";
|
|
5
5
|
import "./chunk-G3PMV62Z.js";
|
|
6
6
|
//# sourceMappingURL=runner.js.map
|