@drisp/cli 0.7.11 → 0.7.12
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-UVNJCK72.js → WorkflowInstallWizard-VPBOZJY5.js} +2 -2
- package/dist/{chunk-XITT6UQX.js → chunk-SACVOS6T.js} +83 -24
- package/dist/{chunk-YHXTTSJE.js → chunk-UUUCDIHI.js} +72 -24
- package/dist/{chunk-RSJVWETP.js → chunk-ZKL7NFOA.js} +2 -2
- package/dist/cli.js +352 -73
- package/dist/dashboard-daemon.js +3 -3
- package/dist/runner.js +3 -3
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -144,7 +144,7 @@ import {
|
|
|
144
144
|
visualPositionToOffset,
|
|
145
145
|
wrapText,
|
|
146
146
|
writeIdentityOnce
|
|
147
|
-
} from "./chunk-
|
|
147
|
+
} from "./chunk-UUUCDIHI.js";
|
|
148
148
|
import {
|
|
149
149
|
generateId as generateId2
|
|
150
150
|
} from "./chunk-PQIB66SG.js";
|
|
@@ -203,7 +203,7 @@ import {
|
|
|
203
203
|
workflowRegistryDir,
|
|
204
204
|
writeGlobalConfig,
|
|
205
205
|
writeProjectConfig
|
|
206
|
-
} from "./chunk-
|
|
206
|
+
} from "./chunk-SACVOS6T.js";
|
|
207
207
|
import {
|
|
208
208
|
__toESM
|
|
209
209
|
} from "./chunk-G3PMV62Z.js";
|
|
@@ -211,10 +211,10 @@ import {
|
|
|
211
211
|
// src/app/entry/cli.tsx
|
|
212
212
|
import { render } from "ink";
|
|
213
213
|
import meow from "meow";
|
|
214
|
-
import
|
|
214
|
+
import fs13 from "fs";
|
|
215
215
|
import { createRequire } from "module";
|
|
216
216
|
import os5 from "os";
|
|
217
|
-
import
|
|
217
|
+
import path13 from "path";
|
|
218
218
|
import { fileURLToPath } from "url";
|
|
219
219
|
|
|
220
220
|
// src/app/shell/sessionUiState.ts
|
|
@@ -5333,14 +5333,14 @@ function extractWebSearch(_input, response) {
|
|
|
5333
5333
|
return { type: "text", content: extractTextContent(response), maxLines: 10 };
|
|
5334
5334
|
}
|
|
5335
5335
|
function extractNotebookEdit(input, _response) {
|
|
5336
|
-
const
|
|
5336
|
+
const path14 = typeof input["notebook_path"] === "string" ? input["notebook_path"] : "";
|
|
5337
5337
|
const mode = typeof input["edit_mode"] === "string" ? input["edit_mode"] : "replace";
|
|
5338
5338
|
const source = typeof input["new_source"] === "string" ? input["new_source"] : "";
|
|
5339
|
-
if (!source) return { type: "text", content: `${mode} cell in ${
|
|
5339
|
+
if (!source) return { type: "text", content: `${mode} cell in ${path14}` };
|
|
5340
5340
|
return {
|
|
5341
5341
|
type: "code",
|
|
5342
5342
|
content: source,
|
|
5343
|
-
language: detectLanguage(
|
|
5343
|
+
language: detectLanguage(path14),
|
|
5344
5344
|
maxLines: 10
|
|
5345
5345
|
};
|
|
5346
5346
|
}
|
|
@@ -5866,10 +5866,10 @@ function extractToolSubject(toolName, toolInput) {
|
|
|
5866
5866
|
const parts = [];
|
|
5867
5867
|
const pattern = str(input, "pattern");
|
|
5868
5868
|
const glob = str(input, "glob");
|
|
5869
|
-
const
|
|
5869
|
+
const path14 = str(input, "path");
|
|
5870
5870
|
if (pattern) parts.push(`"${pattern}"`);
|
|
5871
5871
|
if (glob) parts.push(glob);
|
|
5872
|
-
if (
|
|
5872
|
+
if (path14) parts.push(`in ${path14}`);
|
|
5873
5873
|
return parts.length > 0 ? parts.join(" ") : void 0;
|
|
5874
5874
|
}
|
|
5875
5875
|
case "Skill":
|
|
@@ -13100,7 +13100,7 @@ var thisMachine = (installationId) => ({
|
|
|
13100
13100
|
os: `${platform()} ${release()}`.slice(0, 64),
|
|
13101
13101
|
softwareVersion: runnerSoftwareVersion
|
|
13102
13102
|
});
|
|
13103
|
-
var post = (origin,
|
|
13103
|
+
var post = (origin, path14, body) => fetch(new URL(path14, origin), {
|
|
13104
13104
|
method: "POST",
|
|
13105
13105
|
headers: { "Content-Type": "application/json" },
|
|
13106
13106
|
body: JSON.stringify(body)
|
|
@@ -13119,8 +13119,8 @@ async function alreadyConnected(existing, { forTeam, ownIdentity }) {
|
|
|
13119
13119
|
const problem = own ? await credentialAccepted(own.config) ? void 0 : "lapsed" : forTeam(existing.orgId).length ? "other_runner" : "missing";
|
|
13120
13120
|
const runner = { orgId: existing.orgId, runnerId: existing.runnerId, name: existing.name };
|
|
13121
13121
|
if (own && problem === void 0) return { status: "already_connected", ...runner, path: own.path };
|
|
13122
|
-
const
|
|
13123
|
-
return { status: "identity_unusable", ...runner, problem, ...
|
|
13122
|
+
const path14 = (own ?? forTeam(existing.orgId)[0])?.path;
|
|
13123
|
+
return { status: "identity_unusable", ...runner, problem, ...path14 ? { path: path14 } : {} };
|
|
13124
13124
|
}
|
|
13125
13125
|
async function connectWithCode({
|
|
13126
13126
|
code,
|
|
@@ -13192,20 +13192,20 @@ async function saveIdentity(origin, credential, { single, forTeam }, directory)
|
|
|
13192
13192
|
name: credential.name
|
|
13193
13193
|
});
|
|
13194
13194
|
const replaced = [];
|
|
13195
|
-
for (const { path:
|
|
13196
|
-
const aside = `${
|
|
13197
|
-
await rename(
|
|
13195
|
+
for (const { path: path15 } of forTeam(credential.orgId)) {
|
|
13196
|
+
const aside = `${path15}.replaced-${Date.now()}`;
|
|
13197
|
+
await rename(path15, aside);
|
|
13198
13198
|
replaced.push(aside);
|
|
13199
13199
|
}
|
|
13200
|
-
const
|
|
13200
|
+
const path14 = single ?? teamIdentityPath(origin, credential.orgId, directory);
|
|
13201
13201
|
try {
|
|
13202
|
-
await writeIdentityOnce(
|
|
13202
|
+
await writeIdentityOnce(path14, JSON.stringify(config, null, 2) + "\n");
|
|
13203
13203
|
} catch (error) {
|
|
13204
13204
|
if (error.code === "EEXIST")
|
|
13205
|
-
throw new Error(`Another Connect wrote a Runner identity for this Team at ${
|
|
13205
|
+
throw new Error(`Another Connect wrote a Runner identity for this Team at ${path14} meanwhile; it was left unchanged.`);
|
|
13206
13206
|
throw error;
|
|
13207
13207
|
}
|
|
13208
|
-
return { status: "connected", config, path:
|
|
13208
|
+
return { status: "connected", config, path: path14, replaced };
|
|
13209
13209
|
}
|
|
13210
13210
|
function pathSegment(value) {
|
|
13211
13211
|
const segment = encodeURIComponent(value);
|
|
@@ -13245,7 +13245,7 @@ async function refuseExisting(target) {
|
|
|
13245
13245
|
throw new Error(`This machine already has a Runner${name ? ` (${name})` : ""} configured at ${target}.`);
|
|
13246
13246
|
}
|
|
13247
13247
|
async function disconnectRunner({
|
|
13248
|
-
identity: { path:
|
|
13248
|
+
identity: { path: path14, config },
|
|
13249
13249
|
serviceManager,
|
|
13250
13250
|
othersRemain
|
|
13251
13251
|
}) {
|
|
@@ -13278,7 +13278,7 @@ async function disconnectRunner({
|
|
|
13278
13278
|
await restore();
|
|
13279
13279
|
throw new Error(`The Hub refused to disconnect ${name} (${body.error ?? response.status}); it is still connected.`);
|
|
13280
13280
|
}
|
|
13281
|
-
await forgetRunner(
|
|
13281
|
+
await forgetRunner(path14);
|
|
13282
13282
|
if (othersRemain) await restore();
|
|
13283
13283
|
else await serviceManager?.remove();
|
|
13284
13284
|
return result;
|
|
@@ -13431,18 +13431,18 @@ async function runRunnerCommandV2(input, options = {}) {
|
|
|
13431
13431
|
`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.`
|
|
13432
13432
|
);
|
|
13433
13433
|
}
|
|
13434
|
-
const { config, path:
|
|
13434
|
+
const { config, path: path14, replaced } = outcome;
|
|
13435
13435
|
for (const aside of replaced)
|
|
13436
13436
|
process.stdout.write(
|
|
13437
13437
|
`The Hub no longer recognised this machine's earlier identity for this Team; it was moved aside to ${aside}.
|
|
13438
13438
|
`
|
|
13439
13439
|
);
|
|
13440
13440
|
process.stdout.write(
|
|
13441
|
-
`Connected as ${config.name ?? config.runnerId}. Identity saved to ${
|
|
13441
|
+
`Connected as ${config.name ?? config.runnerId}. Identity saved to ${path14}.
|
|
13442
13442
|
`
|
|
13443
13443
|
);
|
|
13444
13444
|
if (services) {
|
|
13445
|
-
const service = await ensureService(services, config,
|
|
13445
|
+
const service = await ensureService(services, config, path14);
|
|
13446
13446
|
process.stdout.write(`${serviceStarted(service)}
|
|
13447
13447
|
`);
|
|
13448
13448
|
return 0;
|
|
@@ -13451,7 +13451,7 @@ async function runRunnerCommandV2(input, options = {}) {
|
|
|
13451
13451
|
process.stdout.write(
|
|
13452
13452
|
"No launchd or systemd here to run the Runner as a background service; running it in the foreground instead.\n"
|
|
13453
13453
|
);
|
|
13454
|
-
await runConfiguredRunner(
|
|
13454
|
+
await runConfiguredRunner(path14, options.signal);
|
|
13455
13455
|
return 0;
|
|
13456
13456
|
}
|
|
13457
13457
|
case "disconnect": {
|
|
@@ -13522,9 +13522,9 @@ async function runRunnerCommandV2(input, options = {}) {
|
|
|
13522
13522
|
configured: true,
|
|
13523
13523
|
// One entry per Team this machine serves; credentials are never printed.
|
|
13524
13524
|
teams: identities.map(
|
|
13525
|
-
({ path:
|
|
13525
|
+
({ path: path14, config: { token: _token, ...config } }) => ({
|
|
13526
13526
|
...config,
|
|
13527
|
-
path:
|
|
13527
|
+
path: path14
|
|
13528
13528
|
})
|
|
13529
13529
|
),
|
|
13530
13530
|
service: service ? {
|
|
@@ -13873,9 +13873,9 @@ var DrispClient = class _DrispClient {
|
|
|
13873
13873
|
if (!match) throw new Error("invalid_hub_url");
|
|
13874
13874
|
return new _DrispClient(url.origin, decodeURIComponent(match[1]), async () => token);
|
|
13875
13875
|
}
|
|
13876
|
-
async request(
|
|
13876
|
+
async request(path14, body) {
|
|
13877
13877
|
const response = await fetch(
|
|
13878
|
-
`${this.baseUrl}/v2/orgs/${encodeURIComponent(this.orgId)}${
|
|
13878
|
+
`${this.baseUrl}/v2/orgs/${encodeURIComponent(this.orgId)}${path14}`,
|
|
13879
13879
|
{
|
|
13880
13880
|
method: body === void 0 ? "GET" : "POST",
|
|
13881
13881
|
headers: {
|
|
@@ -14207,8 +14207,15 @@ var buildStateSchema = external_exports.strictObject({
|
|
|
14207
14207
|
* written before parses.
|
|
14208
14208
|
*/
|
|
14209
14209
|
done: external_exports.array(external_exports.string()).optional(),
|
|
14210
|
-
/**
|
|
14211
|
-
|
|
14210
|
+
/**
|
|
14211
|
+
* The merged issues the Orchestrator was asked to repair in Linear. No longer written since reconcile (DRI-277),
|
|
14212
|
+
* still read so a state written before parses.
|
|
14213
|
+
*/
|
|
14214
|
+
repaired: external_exports.array(external_exports.string()).optional(),
|
|
14215
|
+
/** Additive (DRI-277): the `once` keys of the comments reconcile already asked for, so each is asked once. */
|
|
14216
|
+
posted: external_exports.array(external_exports.string()).optional(),
|
|
14217
|
+
/** Additive (DRI-277): reconcile ticks in a row; at `RECONCILE_PASSES` the tick goes on without one. */
|
|
14218
|
+
reconcilePasses: external_exports.number().int().nonnegative().optional()
|
|
14212
14219
|
});
|
|
14213
14220
|
|
|
14214
14221
|
// src/core/build/childStart.ts
|
|
@@ -14266,10 +14273,13 @@ function outcomeOf(view) {
|
|
|
14266
14273
|
return "failed";
|
|
14267
14274
|
}
|
|
14268
14275
|
function buildLimits(own, delegation) {
|
|
14269
|
-
if (!delegation) return own;
|
|
14276
|
+
if (!delegation) return { ...own, maxChildren: own.maxChildren ?? 1 };
|
|
14270
14277
|
return {
|
|
14271
14278
|
...own,
|
|
14272
|
-
maxChildren: Math.min(
|
|
14279
|
+
maxChildren: Math.min(
|
|
14280
|
+
own.maxChildren ?? Infinity,
|
|
14281
|
+
delegation.maxChildren ?? own.maxChildren ?? 1
|
|
14282
|
+
),
|
|
14273
14283
|
tokenBudget: delegation.tokenBudget
|
|
14274
14284
|
};
|
|
14275
14285
|
}
|
|
@@ -14327,11 +14337,13 @@ function assessTick(facts) {
|
|
|
14327
14337
|
state.needsPerson[entry.id] = BUILD_STRINGS.rebaseFailed(branchOf(entry));
|
|
14328
14338
|
}
|
|
14329
14339
|
const pullRequests = /* @__PURE__ */ new Map();
|
|
14340
|
+
const byEntry = /* @__PURE__ */ new Map();
|
|
14330
14341
|
const prsOf = (entry) => {
|
|
14331
14342
|
const branch = attemptsOf(entry.id).at(-1)?.branch ?? issues.get(entry.id)?.branchName;
|
|
14332
14343
|
if (!branch) return void 0;
|
|
14333
14344
|
if (!pullRequests.has(branch))
|
|
14334
14345
|
pullRequests.set(branch, facts.pullRequests(branch));
|
|
14346
|
+
byEntry.set(entry.id, pullRequests.get(branch));
|
|
14335
14347
|
return pullRequests.get(branch);
|
|
14336
14348
|
};
|
|
14337
14349
|
const status = /* @__PURE__ */ new Map();
|
|
@@ -14339,8 +14351,6 @@ function assessTick(facts) {
|
|
|
14339
14351
|
const linear = issues.get(id)?.state.type;
|
|
14340
14352
|
return linear === "completed" ? "done" : "blocked";
|
|
14341
14353
|
};
|
|
14342
|
-
const repair = [];
|
|
14343
|
-
const repaired = new Set(state.repaired ?? []);
|
|
14344
14354
|
for (const entry of entries) {
|
|
14345
14355
|
const attempts2 = attemptsOf(entry.id);
|
|
14346
14356
|
const newest = attempts2.at(-1);
|
|
@@ -14349,19 +14359,7 @@ function assessTick(facts) {
|
|
|
14349
14359
|
const statusOf = () => {
|
|
14350
14360
|
if (linear === "completed" || linear === "canceled") return "done";
|
|
14351
14361
|
const prs = entry.profile === "investigation" ? void 0 : prsOf(entry);
|
|
14352
|
-
if (prs?.merged)
|
|
14353
|
-
if (!repaired.has(entry.id) && !(newest && occupies(view))) {
|
|
14354
|
-
const prUrl = prs.mergedUrl ?? branchOf(entry);
|
|
14355
|
-
repair.push({
|
|
14356
|
-
id: entry.id,
|
|
14357
|
-
identifier: entry.identifier,
|
|
14358
|
-
prUrl,
|
|
14359
|
-
comment: BUILD_STRINGS.repairComment(prUrl, orchestratorRunId)
|
|
14360
|
-
});
|
|
14361
|
-
repaired.add(entry.id);
|
|
14362
|
-
}
|
|
14363
|
-
return "done";
|
|
14364
|
-
}
|
|
14362
|
+
if (prs?.merged) return "done";
|
|
14365
14363
|
if (newest && (!view || !view.closed)) return "live";
|
|
14366
14364
|
const current = issues.get(entry.id);
|
|
14367
14365
|
if (!current || eligibilityReason(current)) {
|
|
@@ -14374,7 +14372,7 @@ function assessTick(facts) {
|
|
|
14374
14372
|
}
|
|
14375
14373
|
if (newest?.outcome === "template_mismatch") return "replan";
|
|
14376
14374
|
if (newest?.outcome === "completed")
|
|
14377
|
-
state.needsPerson[entry.id] ??= entry.profile === "investigation" ? "The investigation ended
|
|
14375
|
+
state.needsPerson[entry.id] ??= entry.profile === "investigation" ? "The investigation ended. Review its findings and evidence on the issue." : BUILD_STRINGS.completedUnmerged;
|
|
14378
14376
|
if (state.needsPerson[entry.id] || newest && !mayRetry(attempts2))
|
|
14379
14377
|
return "needs_person";
|
|
14380
14378
|
const blockers = [
|
|
@@ -14399,12 +14397,12 @@ function assessTick(facts) {
|
|
|
14399
14397
|
if (status.get(entry.id) === "done" && state.needsPerson[entry.id] === BUILD_STRINGS.completedUnmerged)
|
|
14400
14398
|
delete state.needsPerson[entry.id];
|
|
14401
14399
|
}
|
|
14402
|
-
if (repaired.size > 0) state.repaired = [...repaired];
|
|
14403
14400
|
const tokenBudget = facts.limits.tokenBudget;
|
|
14404
14401
|
const attempts = [
|
|
14405
14402
|
...facts.children.map((view) => ({
|
|
14406
14403
|
tokenCap: view.parent?.tokenCap ?? Number.NaN,
|
|
14407
|
-
closed: view.closed
|
|
14404
|
+
closed: view.closed,
|
|
14405
|
+
...view.usage ? { spent: view.usage.counted } : {}
|
|
14408
14406
|
})),
|
|
14409
14407
|
...Object.values(state.attempts).flat().filter((attempt) => !views.has(attempt.runId)).map((attempt) => ({ tokenCap: attempt.tokenCap, closed: false }))
|
|
14410
14408
|
];
|
|
@@ -14442,7 +14440,7 @@ function assessTick(facts) {
|
|
|
14442
14440
|
return {
|
|
14443
14441
|
state,
|
|
14444
14442
|
status,
|
|
14445
|
-
|
|
14443
|
+
pullRequests: byEntry,
|
|
14446
14444
|
queue,
|
|
14447
14445
|
slots: Math.max(0, facts.limits.maxChildren - occupying),
|
|
14448
14446
|
...park ? { park } : {}
|
|
@@ -14450,6 +14448,17 @@ function assessTick(facts) {
|
|
|
14450
14448
|
}
|
|
14451
14449
|
function tickAction(input) {
|
|
14452
14450
|
const live = input.children.filter((view) => !view.closed);
|
|
14451
|
+
const questions = live.filter(
|
|
14452
|
+
(view) => view.execution?.status === "awaiting_attention" && view.scheduling === "idle" && view.execution.interruption?.kind === "question" && view.execution.interruption.respondent === "orchestrator" && !view.cancellation
|
|
14453
|
+
);
|
|
14454
|
+
if (questions.length)
|
|
14455
|
+
return {
|
|
14456
|
+
action: "supervise",
|
|
14457
|
+
children: questions.map((view) => ({
|
|
14458
|
+
runId: view.runId,
|
|
14459
|
+
message: view.execution.interruption.message
|
|
14460
|
+
}))
|
|
14461
|
+
};
|
|
14453
14462
|
if (input.started > 0 || live.some((view) => occupies(view)))
|
|
14454
14463
|
return { action: "wait" };
|
|
14455
14464
|
const remaining = input.entries.some(
|
|
@@ -14472,13 +14481,126 @@ function tickAction(input) {
|
|
|
14472
14481
|
};
|
|
14473
14482
|
}
|
|
14474
14483
|
|
|
14484
|
+
// src/core/build/reconcile.ts
|
|
14485
|
+
var RECONCILE_PASSES = 3;
|
|
14486
|
+
var WEB_QA = /* @__PURE__ */ new Set(["web", "both"]);
|
|
14487
|
+
var NOT_HUMAN = "waiting";
|
|
14488
|
+
function teamStatus(status, issue) {
|
|
14489
|
+
const states = issue.teamStates;
|
|
14490
|
+
if (!states) return status;
|
|
14491
|
+
const type = status === "Todo" ? "unstarted" : status === "Released" ? "completed" : "started";
|
|
14492
|
+
const names = status === "Review" || status === "QA" || status === "Ready" ? [status, "In Review", "Review", "In Progress"] : status === "Released" ? [status, "Done"] : [status];
|
|
14493
|
+
for (const name of names) {
|
|
14494
|
+
const match = states.find(
|
|
14495
|
+
(s) => s.type === type && s.name.toLowerCase() === name.toLowerCase()
|
|
14496
|
+
);
|
|
14497
|
+
if (match) return match.name;
|
|
14498
|
+
}
|
|
14499
|
+
if (status === "Needs Input") return void 0;
|
|
14500
|
+
if (issue.state.type === type && states.some((s) => s.name === issue.state.name))
|
|
14501
|
+
return issue.state.name;
|
|
14502
|
+
return states.find((s) => s.type === type)?.name;
|
|
14503
|
+
}
|
|
14504
|
+
function desired(entry, facts, views) {
|
|
14505
|
+
const newest = facts.attempts[entry.id]?.at(-1);
|
|
14506
|
+
const view = newest && views.get(newest.runId);
|
|
14507
|
+
const live = newest !== void 0 && (!view || !view.closed);
|
|
14508
|
+
const prs = facts.pullRequests.get(entry.id);
|
|
14509
|
+
const interruption = view?.execution?.interruption;
|
|
14510
|
+
if (facts.released?.(entry.id)) return { status: "Released", comments: [] };
|
|
14511
|
+
if (live && view?.execution?.status === "awaiting_attention" && interruption && interruption.kind !== NOT_HUMAN && !(interruption.kind === "question" && interruption.respondent === "orchestrator")) {
|
|
14512
|
+
const step = facts.step?.(view.runId);
|
|
14513
|
+
return {
|
|
14514
|
+
status: "Needs Input",
|
|
14515
|
+
comments: [
|
|
14516
|
+
{
|
|
14517
|
+
body: BUILD_STRINGS.askComment({
|
|
14518
|
+
message: interruption.message,
|
|
14519
|
+
runId: view.runId,
|
|
14520
|
+
...step ? { step } : {}
|
|
14521
|
+
}),
|
|
14522
|
+
once: `ask:${view.runId}:${interruption.interruptionId}`
|
|
14523
|
+
}
|
|
14524
|
+
]
|
|
14525
|
+
};
|
|
14526
|
+
}
|
|
14527
|
+
if (prs?.merged) {
|
|
14528
|
+
const pr = prComment(prs.mergedUrl);
|
|
14529
|
+
if (!WEB_QA.has(entry.qa)) return { status: "Ready", comments: pr };
|
|
14530
|
+
const results = facts.qa?.(entry.id) ?? [];
|
|
14531
|
+
const last = results.at(-1);
|
|
14532
|
+
if (!last) return { status: "QA", comments: pr };
|
|
14533
|
+
if (last.passed) return { status: "Ready", comments: pr };
|
|
14534
|
+
const failures = results.filter((result) => !result.passed).length;
|
|
14535
|
+
return {
|
|
14536
|
+
status: failures >= 2 ? "Needs Input" : "Todo",
|
|
14537
|
+
comments: [...pr, { body: last.report, once: `qa:${last.runId}` }]
|
|
14538
|
+
};
|
|
14539
|
+
}
|
|
14540
|
+
if (prs?.open) return { status: "Review", comments: prComment(prs.openUrl) };
|
|
14541
|
+
if (live) return { status: "In Progress", comments: [] };
|
|
14542
|
+
if (entry.profile === "investigation" && newest?.outcome === "completed")
|
|
14543
|
+
return { status: "Review", comments: [] };
|
|
14544
|
+
if (newest) return { status: "Todo", comments: [] };
|
|
14545
|
+
return { comments: [] };
|
|
14546
|
+
}
|
|
14547
|
+
function prComment(url) {
|
|
14548
|
+
return url ? [{ body: BUILD_STRINGS.prComment(url), once: `pr:${url}` }] : [];
|
|
14549
|
+
}
|
|
14550
|
+
var closedIssue = (issue) => issue.state.type === "completed" || issue.state.type === "canceled";
|
|
14551
|
+
function projectStatus(facts) {
|
|
14552
|
+
const current = facts.project.status;
|
|
14553
|
+
const issues = facts.issues.filter((issue) => !issue.external);
|
|
14554
|
+
const completed = issues.length > 0 && issues.every(closedIssue);
|
|
14555
|
+
const started = facts.entries.some(
|
|
14556
|
+
(entry) => (facts.attempts[entry.id]?.length ?? 0) > 0
|
|
14557
|
+
);
|
|
14558
|
+
if (current === "Planned" && started) return "In Progress";
|
|
14559
|
+
if (current === "In Progress" && completed) return "Completed";
|
|
14560
|
+
if (current === "Completed" && !completed) return "In Progress";
|
|
14561
|
+
return void 0;
|
|
14562
|
+
}
|
|
14563
|
+
function reconcile(facts) {
|
|
14564
|
+
const views = new Map(facts.children.map((view) => [view.runId, view]));
|
|
14565
|
+
const issues = new Map(facts.issues.map((issue) => [issue.id, issue]));
|
|
14566
|
+
const posted = new Set(facts.posted);
|
|
14567
|
+
const actions = [];
|
|
14568
|
+
const asked = [];
|
|
14569
|
+
for (const entry of facts.entries) {
|
|
14570
|
+
const issue = issues.get(entry.id);
|
|
14571
|
+
if (!issue || issue.external || closedIssue(issue) || issue.labels.includes("ready-for-human"))
|
|
14572
|
+
continue;
|
|
14573
|
+
const implied = desired(entry, facts, views);
|
|
14574
|
+
const { comments } = implied;
|
|
14575
|
+
const status = implied.status ?? (issue.state.name === "In Progress" ? "Todo" : void 0);
|
|
14576
|
+
const target = status && teamStatus(status, issue);
|
|
14577
|
+
if (target && target !== issue.state.name)
|
|
14578
|
+
actions.push({ kind: "issue.state", issue: entry.id, state: target });
|
|
14579
|
+
for (const comment of comments) {
|
|
14580
|
+
if (posted.has(comment.once)) continue;
|
|
14581
|
+
posted.add(comment.once);
|
|
14582
|
+
asked.push(comment.once);
|
|
14583
|
+
actions.push({ kind: "issue.comment", issue: entry.id, ...comment });
|
|
14584
|
+
}
|
|
14585
|
+
}
|
|
14586
|
+
const project = projectStatus(facts);
|
|
14587
|
+
if (project)
|
|
14588
|
+
actions.push({
|
|
14589
|
+
kind: "project.state",
|
|
14590
|
+
project: facts.project.id,
|
|
14591
|
+
state: project
|
|
14592
|
+
});
|
|
14593
|
+
return { actions, posted: asked };
|
|
14594
|
+
}
|
|
14595
|
+
|
|
14475
14596
|
// src/core/build/tick.ts
|
|
14476
14597
|
var UNAVAILABLE_RETRY_MS = 2e3;
|
|
14477
14598
|
var UNAVAILABLE_RETRY_FOR_MS = 3e4;
|
|
14478
14599
|
var NEVER = (/* @__PURE__ */ new Date(0)).toISOString();
|
|
14479
14600
|
var notStarted = () => ({
|
|
14480
|
-
action: "
|
|
14481
|
-
|
|
14601
|
+
action: "park",
|
|
14602
|
+
reason: "attention",
|
|
14603
|
+
message: "This Build has not been started. Review the plan and choose Start; no child work has completed."
|
|
14482
14604
|
});
|
|
14483
14605
|
var guidanceFile = (artifacts) => path7.join(artifacts, "input", "guidance.json");
|
|
14484
14606
|
function replan(artifacts, guidance) {
|
|
@@ -14625,10 +14747,6 @@ async function runBuildTick(deps) {
|
|
|
14625
14747
|
))
|
|
14626
14748
|
push(worktree.path, worktree.branch);
|
|
14627
14749
|
};
|
|
14628
|
-
if (assessment.repair.length > 0) {
|
|
14629
|
-
record();
|
|
14630
|
-
return { action: "repair", issues: assessment.repair };
|
|
14631
|
-
}
|
|
14632
14750
|
const deferred = [];
|
|
14633
14751
|
const behind = [];
|
|
14634
14752
|
let started = 0;
|
|
@@ -14744,6 +14862,24 @@ async function runBuildTick(deps) {
|
|
|
14744
14862
|
}
|
|
14745
14863
|
if (installed) removeWorkflow(entry.workflowName);
|
|
14746
14864
|
}
|
|
14865
|
+
const linear = reconcile({
|
|
14866
|
+
project: input.project,
|
|
14867
|
+
entries,
|
|
14868
|
+
issues: input.issues,
|
|
14869
|
+
attempts: state.attempts,
|
|
14870
|
+
children: children.runs,
|
|
14871
|
+
pullRequests: assessment.pullRequests,
|
|
14872
|
+
posted: state.posted ?? []
|
|
14873
|
+
});
|
|
14874
|
+
const passes = state.reconcilePasses ?? 0;
|
|
14875
|
+
if (linear.actions.length > 0 && passes < RECONCILE_PASSES) {
|
|
14876
|
+
state.reconcilePasses = passes + 1;
|
|
14877
|
+
if (linear.posted.length > 0)
|
|
14878
|
+
state.posted = [...state.posted ?? [], ...linear.posted];
|
|
14879
|
+
record();
|
|
14880
|
+
return { action: "reconcile", actions: linear.actions };
|
|
14881
|
+
}
|
|
14882
|
+
delete state.reconcilePasses;
|
|
14747
14883
|
record();
|
|
14748
14884
|
if (behind.length > 0) return { action: "rebase", items: behind };
|
|
14749
14885
|
return tickAction({
|
|
@@ -15103,8 +15239,120 @@ function runBuildCheck(input) {
|
|
|
15103
15239
|
return code;
|
|
15104
15240
|
}
|
|
15105
15241
|
|
|
15242
|
+
// src/core/build/supervision.ts
|
|
15243
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
15244
|
+
async function superviseChild(input) {
|
|
15245
|
+
const { hub, parentId, operation } = input;
|
|
15246
|
+
if (operation === "children") return hub.runs({ parent: parentId });
|
|
15247
|
+
if (!input.childId) throw new BuildUsageError("A child Run id is required.");
|
|
15248
|
+
const child = await hub.run(input.childId);
|
|
15249
|
+
if (child.parent?.runId !== parentId)
|
|
15250
|
+
throw new BuildUsageError(
|
|
15251
|
+
"Only a direct child of this Build can be inspected or guided."
|
|
15252
|
+
);
|
|
15253
|
+
if (operation === "inspect") {
|
|
15254
|
+
const after = input.after ?? 0;
|
|
15255
|
+
if (!Number.isSafeInteger(after) || after < 0)
|
|
15256
|
+
throw new BuildUsageError(
|
|
15257
|
+
"The history cursor must be a nonnegative integer."
|
|
15258
|
+
);
|
|
15259
|
+
return { child, history: await hub.historyPage(child.runId, after) };
|
|
15260
|
+
}
|
|
15261
|
+
const text = input.text?.trim();
|
|
15262
|
+
if (!text || text.length > 12e4)
|
|
15263
|
+
throw new BuildUsageError("Guidance must contain 1\u2013120000 characters.");
|
|
15264
|
+
if (child.closed || child.cancellation)
|
|
15265
|
+
throw new BuildUsageError("The child is closed or cancelling.");
|
|
15266
|
+
const interruption = child.execution?.interruption;
|
|
15267
|
+
const message = `Orchestrator ${parentId} technical guidance (not a human approval):
|
|
15268
|
+
${text}`;
|
|
15269
|
+
const { hubEpoch } = await hub.runs({ parent: parentId });
|
|
15270
|
+
const base = {
|
|
15271
|
+
commandId: randomUUID3(),
|
|
15272
|
+
hubEpoch,
|
|
15273
|
+
runId: child.runId,
|
|
15274
|
+
expectedRecordRevision: child.recordRevision
|
|
15275
|
+
};
|
|
15276
|
+
if (child.execution?.status === "awaiting_attention") {
|
|
15277
|
+
if (interruption?.kind !== "question" || interruption.respondent !== "orchestrator" || interruption.requestId)
|
|
15278
|
+
throw new BuildUsageError(
|
|
15279
|
+
"This interruption is reserved for a member. Inspect it and escalate the specific decision."
|
|
15280
|
+
);
|
|
15281
|
+
return hub.submit({
|
|
15282
|
+
...base,
|
|
15283
|
+
intent: {
|
|
15284
|
+
kind: "answer",
|
|
15285
|
+
interruptionId: interruption.interruptionId,
|
|
15286
|
+
expectedInterruptionRevision: interruption.revision,
|
|
15287
|
+
answer: message
|
|
15288
|
+
}
|
|
15289
|
+
});
|
|
15290
|
+
}
|
|
15291
|
+
if (child.execution?.status !== "running")
|
|
15292
|
+
throw new BuildUsageError(
|
|
15293
|
+
"The child is not running; inspect its scheduling state before retrying."
|
|
15294
|
+
);
|
|
15295
|
+
return hub.submit({ ...base, intent: { kind: "steer", text: message } });
|
|
15296
|
+
}
|
|
15297
|
+
|
|
15298
|
+
// src/core/build/evidenceCopy.ts
|
|
15299
|
+
import fs11 from "fs";
|
|
15300
|
+
import path11 from "path";
|
|
15301
|
+
function copyEvidence(cwd, source, name) {
|
|
15302
|
+
if (!source || !name || !/^[a-zA-Z0-9][a-zA-Z0-9._-]*\.(png|jpe?g|webp)$/i.test(name))
|
|
15303
|
+
throw new BuildUsageError(
|
|
15304
|
+
"Usage: drisp build evidence-copy <screenshot> <filename.png|jpg|webp>"
|
|
15305
|
+
);
|
|
15306
|
+
const root = fs11.realpathSync(cwd);
|
|
15307
|
+
let directory = root;
|
|
15308
|
+
for (const part of [".athena", "evidence"]) {
|
|
15309
|
+
directory = path11.join(directory, part);
|
|
15310
|
+
try {
|
|
15311
|
+
fs11.mkdirSync(directory);
|
|
15312
|
+
} catch (error) {
|
|
15313
|
+
if (error.code !== "EEXIST") throw error;
|
|
15314
|
+
}
|
|
15315
|
+
const stat = fs11.lstatSync(directory);
|
|
15316
|
+
if (stat.isSymbolicLink() || !stat.isDirectory())
|
|
15317
|
+
throw new BuildUsageError(
|
|
15318
|
+
"Evidence directories must be real directories inside this worktree."
|
|
15319
|
+
);
|
|
15320
|
+
}
|
|
15321
|
+
const sourcePath = path11.resolve(root, source);
|
|
15322
|
+
const fd = fs11.openSync(
|
|
15323
|
+
sourcePath,
|
|
15324
|
+
fs11.constants.O_RDONLY | fs11.constants.O_NOFOLLOW
|
|
15325
|
+
);
|
|
15326
|
+
let bytes;
|
|
15327
|
+
try {
|
|
15328
|
+
const stat = fs11.fstatSync(fd);
|
|
15329
|
+
if (!stat.isFile() || stat.size > 25 * 1024 * 1024)
|
|
15330
|
+
throw new BuildUsageError(
|
|
15331
|
+
"A screenshot must be a regular image file of at most 25 MiB."
|
|
15332
|
+
);
|
|
15333
|
+
bytes = fs11.readFileSync(fd);
|
|
15334
|
+
} finally {
|
|
15335
|
+
fs11.closeSync(fd);
|
|
15336
|
+
}
|
|
15337
|
+
const png = bytes.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]));
|
|
15338
|
+
const jpg = bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255;
|
|
15339
|
+
const webp = bytes.toString("ascii", 0, 4) === "RIFF" && bytes.toString("ascii", 8, 12) === "WEBP";
|
|
15340
|
+
const extension = path11.extname(name).toLowerCase();
|
|
15341
|
+
if (!(extension === ".png" && png || [".jpg", ".jpeg"].includes(extension) && jpg || extension === ".webp" && webp))
|
|
15342
|
+
throw new BuildUsageError(
|
|
15343
|
+
"The source must be a PNG, JPEG or WebP screenshot matching the destination extension."
|
|
15344
|
+
);
|
|
15345
|
+
const target = path11.join(directory, name);
|
|
15346
|
+
fs11.writeFileSync(target, bytes, { flag: "wx" });
|
|
15347
|
+
return { path: target };
|
|
15348
|
+
}
|
|
15349
|
+
|
|
15106
15350
|
// src/app/entry/buildCommand.ts
|
|
15107
15351
|
var USAGE3 = `Usage: drisp build plan --input <path>
|
|
15352
|
+
drisp build children
|
|
15353
|
+
drisp build inspect <child-run-id> [history-cursor]
|
|
15354
|
+
drisp build guide <child-run-id> "<technical guidance>"
|
|
15355
|
+
drisp build evidence-copy <screenshot> <filename.png|jpg|webp>
|
|
15108
15356
|
drisp build tick --input <path> [--rebase-failed <IDENTIFIER>]...
|
|
15109
15357
|
drisp build checks
|
|
15110
15358
|
drisp build step <n> [--qa none|web|macos|both] [--profile implementation|documentation|investigation]
|
|
@@ -15147,10 +15395,12 @@ function githubPullRequests(folder, branch) {
|
|
|
15147
15395
|
function parsePullRequests(output) {
|
|
15148
15396
|
const prs = JSON.parse(output);
|
|
15149
15397
|
const merged = prs.find((pr) => pr.state === "MERGED");
|
|
15398
|
+
const open = prs.find((pr) => pr.state === "OPEN");
|
|
15150
15399
|
return {
|
|
15151
|
-
open:
|
|
15400
|
+
open: open !== void 0,
|
|
15152
15401
|
merged: merged !== void 0,
|
|
15153
|
-
...merged ? { mergedUrl: merged.url } : {}
|
|
15402
|
+
...merged ? { mergedUrl: merged.url } : {},
|
|
15403
|
+
...open ? { openUrl: open.url } : {}
|
|
15154
15404
|
};
|
|
15155
15405
|
}
|
|
15156
15406
|
function hostCapabilities(folder) {
|
|
@@ -15222,6 +15472,31 @@ async function runBuildCommand(input) {
|
|
|
15222
15472
|
try {
|
|
15223
15473
|
let output;
|
|
15224
15474
|
switch (input.subcommand) {
|
|
15475
|
+
case "evidence-copy":
|
|
15476
|
+
output = copyEvidence(
|
|
15477
|
+
input.cwd,
|
|
15478
|
+
input.positional?.[0],
|
|
15479
|
+
input.positional?.[1]
|
|
15480
|
+
);
|
|
15481
|
+
break;
|
|
15482
|
+
case "children":
|
|
15483
|
+
case "inspect":
|
|
15484
|
+
case "guide": {
|
|
15485
|
+
const parentId = requireEnv(env2, "DRISP_RUN_ID");
|
|
15486
|
+
const hub = input.supervisionHub ?? DrispClient.delegated(
|
|
15487
|
+
requireEnv(env2, "DRISP_HUB_URL"),
|
|
15488
|
+
requireEnv(env2, "DRISP_RUN_TOKEN")
|
|
15489
|
+
);
|
|
15490
|
+
output = await superviseChild({
|
|
15491
|
+
hub,
|
|
15492
|
+
parentId,
|
|
15493
|
+
operation: input.subcommand,
|
|
15494
|
+
childId: input.positional?.[0],
|
|
15495
|
+
text: input.positional?.[1],
|
|
15496
|
+
...input.subcommand === "inspect" && input.positional?.[1] !== void 0 ? { after: Number(input.positional[1]) } : {}
|
|
15497
|
+
});
|
|
15498
|
+
break;
|
|
15499
|
+
}
|
|
15225
15500
|
case "plan": {
|
|
15226
15501
|
const calibration = input.dependencies?.calibration ?? await measuredForecasts(env2);
|
|
15227
15502
|
output = runBuildPlan({
|
|
@@ -15315,9 +15590,9 @@ import process7 from "process";
|
|
|
15315
15590
|
|
|
15316
15591
|
// src/harnesses/claude/system/doctorEnvironment.ts
|
|
15317
15592
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
15318
|
-
import * as
|
|
15593
|
+
import * as fs12 from "fs";
|
|
15319
15594
|
import * as os4 from "os";
|
|
15320
|
-
import * as
|
|
15595
|
+
import * as path12 from "path";
|
|
15321
15596
|
var MANAGED_POLICY_KEYS = [
|
|
15322
15597
|
"forceLoginMethod",
|
|
15323
15598
|
"forceLoginOrgUUID",
|
|
@@ -15421,8 +15696,8 @@ function collectEnvironment(options = {}) {
|
|
|
15421
15696
|
const detectVersion = options.detectClaudeVersionFn ?? detectClaudeVersion;
|
|
15422
15697
|
const runAuthStatus = options.runClaudeAuthStatusFn ?? runClaudeAuthStatus;
|
|
15423
15698
|
const resolveForwarder = options.resolveHookForwarderCommandFn ?? resolveHookForwarderCommand;
|
|
15424
|
-
const statFn = options.statFn ?? ((p) =>
|
|
15425
|
-
const readFileFn = options.readFileFn ?? ((p) =>
|
|
15699
|
+
const statFn = options.statFn ?? ((p) => fs12.statSync(p));
|
|
15700
|
+
const readFileFn = options.readFileFn ?? ((p) => fs12.readFileSync(p, "utf8"));
|
|
15426
15701
|
const settingsPaths = resolveClaudeSettingsSurfacePaths(
|
|
15427
15702
|
homeDir,
|
|
15428
15703
|
cwd,
|
|
@@ -15467,7 +15742,7 @@ function collectEnvironment(options = {}) {
|
|
|
15467
15742
|
});
|
|
15468
15743
|
let globalConfigPresent = false;
|
|
15469
15744
|
try {
|
|
15470
|
-
statFn(
|
|
15745
|
+
statFn(path12.join(homeDir, ".claude.json"));
|
|
15471
15746
|
globalConfigPresent = true;
|
|
15472
15747
|
} catch {
|
|
15473
15748
|
globalConfigPresent = false;
|
|
@@ -15947,13 +16222,13 @@ function runRunsCommand(input) {
|
|
|
15947
16222
|
// src/app/entry/cli.tsx
|
|
15948
16223
|
import { jsx as jsx24 } from "react/jsx-runtime";
|
|
15949
16224
|
function resolvePackageJsonPath(entryUrl) {
|
|
15950
|
-
let currentDir =
|
|
16225
|
+
let currentDir = path13.dirname(fileURLToPath(entryUrl));
|
|
15951
16226
|
for (; ; ) {
|
|
15952
|
-
const candidatePath =
|
|
15953
|
-
if (
|
|
16227
|
+
const candidatePath = path13.join(currentDir, "package.json");
|
|
16228
|
+
if (fs13.existsSync(candidatePath)) {
|
|
15954
16229
|
return candidatePath;
|
|
15955
16230
|
}
|
|
15956
|
-
const parentDir =
|
|
16231
|
+
const parentDir = path13.dirname(currentDir);
|
|
15957
16232
|
if (parentDir === currentDir) {
|
|
15958
16233
|
throw new Error("Could not locate package.json for CLI metadata.");
|
|
15959
16234
|
}
|
|
@@ -16123,6 +16398,10 @@ var cli = meow(
|
|
|
16123
16398
|
runner [sub] Run the runner that connects this machine to a Team and executes its Runs
|
|
16124
16399
|
(no subcommand: foreground; connect [code] --hub <url> [--replace], disconnect [team], status, restart, recover, help)
|
|
16125
16400
|
build plan --input <path> Plan a Build inside an Orchestrator Run (prints one JSON object)
|
|
16401
|
+
build children List this Build's children and their interruptions
|
|
16402
|
+
build inspect <run> [cursor] Read a child's state and paged history
|
|
16403
|
+
build guide <run> "<text>" Guide a child or answer its delegated technical question
|
|
16404
|
+
build evidence-copy <image> <name> Copy a screenshot into this worktree's evidence directory
|
|
16126
16405
|
build tick --input <path> Run one step of a started Build (prints the next action; [--rebase-failed <ID>]...)
|
|
16127
16406
|
build checks The merge gate's verdict on HEAD's GitHub runs (in a child's worktree)
|
|
16128
16407
|
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)
|
|
@@ -16315,7 +16594,7 @@ var cli = meow(
|
|
|
16315
16594
|
}
|
|
16316
16595
|
);
|
|
16317
16596
|
async function main() {
|
|
16318
|
-
const projectDir =
|
|
16597
|
+
const projectDir = path13.resolve(cli.flags.projectDir);
|
|
16319
16598
|
const [command, ...commandArgs] = cli.input;
|
|
16320
16599
|
const isRunCommand = command === "run" || command === "exec";
|
|
16321
16600
|
if (command && !KNOWN_COMMANDS.has(command)) {
|
|
@@ -16369,7 +16648,7 @@ Available commands: ${[...KNOWN_COMMANDS].join(", ")}`
|
|
|
16369
16648
|
await exitWith(1);
|
|
16370
16649
|
return;
|
|
16371
16650
|
}
|
|
16372
|
-
const { default: WorkflowInstallWizard } = await import("./WorkflowInstallWizard-
|
|
16651
|
+
const { default: WorkflowInstallWizard } = await import("./WorkflowInstallWizard-VPBOZJY5.js");
|
|
16373
16652
|
const { waitUntilExit } = render(
|
|
16374
16653
|
/* @__PURE__ */ jsx24(
|
|
16375
16654
|
WorkflowInstallWizard,
|
|
@@ -16549,8 +16828,8 @@ Available commands: ${[...KNOWN_COMMANDS].join(", ")}`
|
|
|
16549
16828
|
const showSetup = isRunCommand ? false : shouldShowSetup({
|
|
16550
16829
|
cliInput: cli.input,
|
|
16551
16830
|
setupComplete: globalConfig.setupComplete,
|
|
16552
|
-
globalConfigExists:
|
|
16553
|
-
|
|
16831
|
+
globalConfigExists: fs13.existsSync(
|
|
16832
|
+
path13.join(os5.homedir(), ".config", "athena", "config.json")
|
|
16554
16833
|
)
|
|
16555
16834
|
});
|
|
16556
16835
|
let runtimeConfig;
|