@indigoai-us/hq-cli 5.345.134 → 5.345.136
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/CHANGELOG.md +48 -0
- package/assets/scaffold/core/scripts/worktree-gc.sh +98 -8
- package/dist/command-catalog.generated.d.ts +62 -6
- package/dist/command-catalog.generated.js +79 -7
- package/dist/command-registration-plan.d.ts +6 -0
- package/dist/command-registration-plan.js +1 -0
- package/dist/commands/core-bucket-c-handoff-knowledge-native.d.ts +3 -1
- package/dist/commands/core-bucket-c-handoff-knowledge-native.js +8 -4
- package/dist/commands/core-checkpoint-native.d.ts +10 -0
- package/dist/commands/core-checkpoint-native.js +60 -15
- package/dist/commands/core-handoff.d.ts +2 -0
- package/dist/commands/core-handoff.js +3 -1
- package/dist/commands/core-pipeline-workflow-runner-native.js +7 -5
- package/dist/commands/daemon.js +3 -1
- package/dist/commands/fingerprint.d.ts +10 -0
- package/dist/commands/fingerprint.js +191 -0
- package/dist/commands/integrations-core.d.ts +22 -0
- package/dist/commands/integrations-core.js +25 -0
- package/dist/commands/integrations-manage.d.ts +24 -1
- package/dist/commands/integrations-manage.js +92 -17
- package/dist/commands/integrations.d.ts +11 -0
- package/dist/commands/integrations.js +100 -19
- package/dist/commands/lanes-loop.d.ts +9 -0
- package/dist/commands/lanes-loop.js +9 -1
- package/dist/commands/lanes.js +78 -55
- package/dist/commands/meetings.d.ts +2 -0
- package/dist/commands/meetings.js +57 -14
- package/dist/commands/mesh.d.ts +19 -1
- package/dist/commands/mesh.js +158 -70
- package/dist/commands/reindex.js +80 -5
- package/dist/commands/storage-offload.d.ts +93 -28
- package/dist/commands/storage-offload.js +290 -34
- package/dist/commands/storage.js +60 -7
- package/dist/hq-cloud-sync-runner/bin/sync-runner.js +2550 -1748
- package/dist/hq-cloud-sync-runner/package.json +1 -1
- package/dist/index.js +4 -0
- package/dist/lib/capacity/route.js +24 -0
- package/dist/lib/core-utils/session-company.d.ts +11 -0
- package/dist/lib/core-utils/session-company.js +34 -0
- package/dist/lib/core-utils/worktree-resume.js +44 -4
- package/dist/lib/core-utils/worktree-session-owner.d.ts +27 -0
- package/dist/lib/core-utils/worktree-session-owner.js +157 -0
- package/dist/lib/daemon/catalog.d.ts +2 -0
- package/dist/lib/daemon/catalog.js +3 -3
- package/dist/lib/daemon/hq-child-bootstrap-stage.d.ts +5 -0
- package/dist/lib/daemon/hq-child-bootstrap-stage.js +3 -11
- package/dist/lib/daemon/server.js +10 -1
- package/dist/lib/daemon/supervisor.js +6 -0
- package/dist/lib/daemon/sync-runner-bundle.d.ts +5 -0
- package/dist/lib/daemon/sync-runner-bundle.js +4 -12
- package/dist/lib/doctor/checks/claude-wiring.js +2 -1
- package/dist/lib/doctor/fix/apply.d.ts +20 -0
- package/dist/lib/doctor/fix/apply.js +63 -3
- package/dist/lib/doctor/fix/remediation.d.ts +1 -1
- package/dist/lib/doctor/fix/remediation.js +16 -0
- package/dist/lib/doctor/shared.d.ts +11 -1
- package/dist/lib/doctor/shared.js +16 -2
- package/dist/lib/fingerprint/fixtures.d.ts +19 -0
- package/dist/lib/fingerprint/fixtures.js +61 -0
- package/dist/lib/fingerprint/format.d.ts +50 -0
- package/dist/lib/fingerprint/format.js +105 -0
- package/dist/lib/fingerprint/offline.d.ts +16 -0
- package/dist/lib/fingerprint/offline.js +44 -0
- package/dist/lib/fingerprint/registry-client.d.ts +74 -0
- package/dist/lib/fingerprint/registry-client.js +141 -0
- package/dist/lib/fingerprint/resolve-local.d.ts +14 -0
- package/dist/lib/fingerprint/resolve-local.js +67 -0
- package/dist/lib/fingerprint/self-core.d.ts +139 -0
- package/dist/lib/fingerprint/self-core.js +371 -0
- package/dist/lib/fingerprint/self.d.ts +44 -0
- package/dist/lib/fingerprint/self.js +191 -0
- package/dist/lib/flag-registry-worker-source.generated.d.ts +1 -1
- package/dist/lib/flag-registry-worker-source.generated.js +1 -1
- package/dist/lib/lanes/monitor.js +14 -2
- package/dist/lib/lanes/spawn.d.ts +19 -1
- package/dist/lib/lanes/spawn.js +50 -6
- package/dist/lib/lanes/story-ledger.d.ts +1 -0
- package/dist/lib/lanes/story-ledger.js +3 -1
- package/dist/lib/lanes/types.d.ts +12 -0
- package/dist/lib/lanes/types.js +12 -1
- package/dist/lib/mesh/live/daemon/install.d.ts +12 -0
- package/dist/lib/mesh/live/daemon/install.js +22 -0
- package/dist/lib/mesh/live/flush.js +106 -3
- package/dist/lib/mesh/live/session-events-client.d.ts +9 -0
- package/dist/lib/mesh/live/session-events-client.js +15 -0
- package/dist/lib/work-context/state.d.ts +10 -0
- package/dist/lib/work-context/state.js +5 -0
- package/dist/utils/cli-network-lifecycle.d.ts +2 -0
- package/dist/utils/cli-network-lifecycle.js +5 -1
- package/dist/utils/cognito-session.d.ts +2 -21
- package/dist/utils/cognito-session.js +3 -84
- package/dist/utils/fsync-directory.d.ts +17 -0
- package/dist/utils/fsync-directory.js +27 -0
- package/dist/utils/person-token-cache.d.ts +32 -0
- package/dist/utils/person-token-cache.js +95 -0
- package/dist/utils/vault-api.d.ts +21 -1
- package/dist/utils/vault-api.js +54 -5
- package/dist/utils/windows-hide-default.d.ts +42 -0
- package/dist/utils/windows-hide-default.js +98 -0
- package/package.json +2 -2
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
* hq integrations show [app] One app in full.
|
|
22
22
|
* hq integrations tools --provider <p> What the connected app can do.
|
|
23
23
|
* hq integrations call <tool> --provider <p> --args '<json>'
|
|
24
|
+
* hq integrations call <tool> --provider <p> --args-file args.json
|
|
24
25
|
* hq integrations pending Calls awaiting approval.
|
|
25
26
|
* hq integrations pending --history Recently decided calls, including status.
|
|
26
27
|
* hq integrations approve|reject <queueId> Decide a queued call.
|
|
@@ -51,6 +52,16 @@
|
|
|
51
52
|
*/
|
|
52
53
|
import { Command } from "commander";
|
|
53
54
|
import { type PendingApproval } from "./integrations-api.js";
|
|
55
|
+
/**
|
|
56
|
+
* Resolve `hq integrations call` tool arguments from `--args <json>`,
|
|
57
|
+
* `--args -` (stdin), or `--args-file <path>` (`-` also reads stdin). A file or
|
|
58
|
+
* stdin avoids shell quoting: zsh breaks inline JSON that contains apostrophes
|
|
59
|
+
* or parentheses. The result must be a JSON object.
|
|
60
|
+
*/
|
|
61
|
+
export declare function resolveCallArgs(opts: {
|
|
62
|
+
args?: string;
|
|
63
|
+
argsFile?: string;
|
|
64
|
+
}, stdin?: NodeJS.ReadableStream): Promise<Record<string, unknown>>;
|
|
54
65
|
export { IntegrationsCliError, callGateway, fetchConnections, fetchUsableApps, queuedOutcome, selectConnection, toolPrefixForProvider, unwrapGatewayResult, } from "./integrations-core.js";
|
|
55
66
|
export type { AdminConnection, UsableApp, UsableAppsResult } from "./integrations-core.js";
|
|
56
67
|
/** Wait for a decided queue item without resending the approve request. */
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
* hq integrations show [app] One app in full.
|
|
22
22
|
* hq integrations tools --provider <p> What the connected app can do.
|
|
23
23
|
* hq integrations call <tool> --provider <p> --args '<json>'
|
|
24
|
+
* hq integrations call <tool> --provider <p> --args-file args.json
|
|
24
25
|
* hq integrations pending Calls awaiting approval.
|
|
25
26
|
* hq integrations pending --history Recently decided calls, including status.
|
|
26
27
|
* hq integrations approve|reject <queueId> Decide a queued call.
|
|
@@ -50,6 +51,7 @@
|
|
|
50
51
|
* `integrations-manage.ts`.
|
|
51
52
|
*/
|
|
52
53
|
import { randomUUID } from "node:crypto";
|
|
54
|
+
import { readFile } from "node:fs/promises";
|
|
53
55
|
import chalk from "chalk";
|
|
54
56
|
import { ensureCognitoIdToken } from "../utils/cognito-session.js";
|
|
55
57
|
import { getCompanyUid, vaultApiFetch } from "../utils/vault-api.js";
|
|
@@ -60,6 +62,69 @@ import { registerImportCommands } from "./integrations-import.js";
|
|
|
60
62
|
import { registerManageCommands } from "./integrations-manage.js";
|
|
61
63
|
import { registerSlackCommands } from "./integrations-slack.js";
|
|
62
64
|
import { fetchPendingApprovals } from "./integrations-api.js";
|
|
65
|
+
const CALL_ARGS_EXAMPLE = `--args '{"assignee":"me"}' or --args-file args.json`;
|
|
66
|
+
function readStream(stream) {
|
|
67
|
+
return new Promise((resolve, reject) => {
|
|
68
|
+
let data = "";
|
|
69
|
+
stream.setEncoding("utf8");
|
|
70
|
+
stream.on("data", (chunk) => {
|
|
71
|
+
data += chunk;
|
|
72
|
+
});
|
|
73
|
+
stream.on("end", () => resolve(data));
|
|
74
|
+
stream.on("error", reject);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Resolve `hq integrations call` tool arguments from `--args <json>`,
|
|
79
|
+
* `--args -` (stdin), or `--args-file <path>` (`-` also reads stdin). A file or
|
|
80
|
+
* stdin avoids shell quoting: zsh breaks inline JSON that contains apostrophes
|
|
81
|
+
* or parentheses. The result must be a JSON object.
|
|
82
|
+
*/
|
|
83
|
+
export async function resolveCallArgs(opts, stdin = process.stdin) {
|
|
84
|
+
if (opts.args !== undefined && opts.argsFile !== undefined) {
|
|
85
|
+
throw new IntegrationsCliError("Use either --args or --args-file, not both.", { expected: true });
|
|
86
|
+
}
|
|
87
|
+
let text;
|
|
88
|
+
let source;
|
|
89
|
+
const fromStdin = opts.args === "-" || opts.argsFile === "-";
|
|
90
|
+
if (fromStdin) {
|
|
91
|
+
if (stdin.isTTY) {
|
|
92
|
+
throw new IntegrationsCliError("Reading tool arguments from stdin needs piped input, e.g. `hq integrations call <tool> --args - < args.json`.", { expected: true });
|
|
93
|
+
}
|
|
94
|
+
text = await readStream(stdin);
|
|
95
|
+
source = "stdin";
|
|
96
|
+
}
|
|
97
|
+
else if (opts.argsFile !== undefined) {
|
|
98
|
+
source = `--args-file ${opts.argsFile}`;
|
|
99
|
+
try {
|
|
100
|
+
text = await readFile(opts.argsFile, "utf8");
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
104
|
+
throw new IntegrationsCliError(`Could not read --args-file ${opts.argsFile}: ${reason}`, { expected: true });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
text = opts.args ?? "{}";
|
|
109
|
+
source = "--args";
|
|
110
|
+
}
|
|
111
|
+
let raw;
|
|
112
|
+
try {
|
|
113
|
+
raw = JSON.parse(text);
|
|
114
|
+
}
|
|
115
|
+
catch (err) {
|
|
116
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
117
|
+
throw new IntegrationsCliError(source === "--args"
|
|
118
|
+
? `--args must be a JSON object, e.g. ${CALL_ARGS_EXAMPLE}`
|
|
119
|
+
: `Tool arguments from ${source} are not valid JSON (${reason}). They must be a JSON object, e.g. {"assignee":"me"}.`, { expected: true });
|
|
120
|
+
}
|
|
121
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
122
|
+
throw new IntegrationsCliError(source === "--args"
|
|
123
|
+
? `--args must be a JSON object, e.g. ${CALL_ARGS_EXAMPLE}`
|
|
124
|
+
: `Tool arguments from ${source} must be a JSON object, e.g. {"assignee":"me"}.`, { expected: true });
|
|
125
|
+
}
|
|
126
|
+
return raw;
|
|
127
|
+
}
|
|
63
128
|
/** Routing ids hq sets on a built-in tool call; never accepted from --args. */
|
|
64
129
|
const BUILT_IN_ROUTING_ARGS = ["companyUid", "connectionId", "idempotencyKey"];
|
|
65
130
|
/**
|
|
@@ -382,7 +447,7 @@ export function registerIntegrationsCommand(program) {
|
|
|
382
447
|
const selector = sharedProvider
|
|
383
448
|
? `--connection ${connection.id}`
|
|
384
449
|
: `--provider ${bareProvider(connection.provider)}`;
|
|
385
|
-
console.log(chalk.dim(`\n${tools.length} tools. Describe inputs: hq integrations tools ${selector} --describe <tool>\nCall one with: hq integrations call <tool> ${selector} --args '<json>'`));
|
|
450
|
+
console.log(chalk.dim(`\n${tools.length} tools. Describe inputs: hq integrations tools ${selector} --describe <tool>\nCall one with: hq integrations call <tool> ${selector} --args '<json>'\nIf the JSON has apostrophes or parentheses, put it in a file: --args-file args.json (or --args - to read stdin)`));
|
|
386
451
|
});
|
|
387
452
|
integrations
|
|
388
453
|
.command("call <tool>")
|
|
@@ -390,21 +455,12 @@ export function registerIntegrationsCommand(program) {
|
|
|
390
455
|
.option("--provider <slug>", "Connected app (e.g. linear)")
|
|
391
456
|
.option("--connection <id>", "Connection id (acct_…)")
|
|
392
457
|
.option("--company <slug>", "Company slug, e.g. indigo")
|
|
393
|
-
.option("--args <json>", "Tool arguments as JSON
|
|
458
|
+
.option("--args <json>", "Tool arguments as a JSON object (default {}); use - to read the JSON from stdin")
|
|
459
|
+
.option("--args-file <path>", "Read tool arguments from a JSON file (use - for stdin); avoids shell quoting of apostrophes and parentheses")
|
|
394
460
|
.option("--idempotency-key <key>", "Stable key so retries execute at most once (default: generated)")
|
|
395
461
|
.option("--json", "Machine-readable output")
|
|
396
462
|
.action(async (tool, opts) => {
|
|
397
|
-
|
|
398
|
-
try {
|
|
399
|
-
const raw = JSON.parse(opts.args);
|
|
400
|
-
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
401
|
-
throw new Error("not an object");
|
|
402
|
-
}
|
|
403
|
-
parsedArgs = raw;
|
|
404
|
-
}
|
|
405
|
-
catch {
|
|
406
|
-
throw new IntegrationsCliError(`--args must be a JSON object, e.g. --args '{"assignee":"me"}'`, { expected: true });
|
|
407
|
-
}
|
|
463
|
+
const parsedArgs = await resolveCallArgs(opts);
|
|
408
464
|
const token = await ensureCognitoIdToken();
|
|
409
465
|
const companyUid = await getCompanyUid(token, opts.company);
|
|
410
466
|
const connection = selectConnection(await fetchConnections(token, companyUid), opts);
|
|
@@ -417,7 +473,7 @@ export function registerIntegrationsCommand(program) {
|
|
|
417
473
|
// arg with a routing name would redirect the call; refuse it.
|
|
418
474
|
const reserved = BUILT_IN_ROUTING_ARGS.filter((key) => key in parsedArgs);
|
|
419
475
|
if (reserved.length) {
|
|
420
|
-
throw new IntegrationsCliError(
|
|
476
|
+
throw new IntegrationsCliError(`Tool arguments cannot include ${reserved.join(", ")}: it is set by hq from the selected connection.`, { expected: true });
|
|
421
477
|
}
|
|
422
478
|
gatewayParams = {
|
|
423
479
|
companyUid,
|
|
@@ -525,16 +581,41 @@ export function registerIntegrationsCommand(program) {
|
|
|
525
581
|
process.exitCode = 1;
|
|
526
582
|
return;
|
|
527
583
|
}
|
|
584
|
+
if (decision === "reject") {
|
|
585
|
+
if (opts.json)
|
|
586
|
+
printJson(body);
|
|
587
|
+
else
|
|
588
|
+
console.log(chalk.yellow(`Rejected (status: ${body.status ?? "rejected"}).`));
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
// A synchronous approve runs the call inline. The gateway answers a
|
|
592
|
+
// provider-side tool error with { status: "rejected", outcome:
|
|
593
|
+
// "failed", rejectionReason: "provider_tool_error" }; an older
|
|
594
|
+
// gateway reports "resumed" with an isError result. Classify both as
|
|
595
|
+
// failures, the same way the polling path does.
|
|
596
|
+
const result = body.result === undefined ? undefined : unwrapGatewayResult(body.result);
|
|
597
|
+
const toolError = body.result !== undefined &&
|
|
598
|
+
(gatewayResultIsError(result) || gatewayResultIsError(body.result));
|
|
599
|
+
const failed = approvalOutcomeFailed(body) || toolError;
|
|
600
|
+
if (failed)
|
|
601
|
+
process.exitCode = 1;
|
|
528
602
|
if (opts.json) {
|
|
529
603
|
printJson(body);
|
|
530
604
|
return;
|
|
531
605
|
}
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
printJson(unwrapGatewayResult(body.result));
|
|
606
|
+
if (failed) {
|
|
607
|
+
const reason = body.rejectionReason ??
|
|
608
|
+
(toolError ? "provider_tool_error" : body.outcome ?? body.status ?? "failed");
|
|
609
|
+
console.log(chalk.red(`The approved call failed at the provider (${reason}).`));
|
|
537
610
|
}
|
|
611
|
+
else if (body.status === "resumed" || body.status === "completed" || body.outcome === "ran") {
|
|
612
|
+
console.log(chalk.green(`Approved — the call ran (status: ${body.status ?? body.outcome}).`));
|
|
613
|
+
}
|
|
614
|
+
else {
|
|
615
|
+
console.log(chalk.yellow(`Approval ended with status ${body.status ?? "unknown"}.`));
|
|
616
|
+
}
|
|
617
|
+
if (result !== undefined)
|
|
618
|
+
printJson(result);
|
|
538
619
|
});
|
|
539
620
|
}
|
|
540
621
|
registerConnectCommands(integrations);
|
|
@@ -18,9 +18,18 @@ export interface LoopControllerDeps {
|
|
|
18
18
|
resumeLane(laneId: string, options: LaneResumeOptions): Promise<unknown>;
|
|
19
19
|
interruptLane(laneId: string, reason: string, options: {
|
|
20
20
|
preserveLoopQueue: boolean;
|
|
21
|
+
terminalReason?: string;
|
|
21
22
|
}): Promise<void>;
|
|
22
23
|
emitTerminal(lane: LaneRecord): Promise<void>;
|
|
23
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* terminal_reason values for interrupts HQ issues itself. An operator
|
|
27
|
+
* interrupt (`hq lanes interrupt`) records `operator_interrupt`; these keep a
|
|
28
|
+
* system recovery from looking like an operator action in the lane record.
|
|
29
|
+
*/
|
|
30
|
+
export declare const LOOP_CONTROLLER_RECOVERY_REASON = "loop_controller_recovery";
|
|
31
|
+
export declare const LOOP_STALL_RESTART_REASON = "loop_stall_restart";
|
|
32
|
+
export declare const LOOP_PHASE_DEADLINE_REASON = "loop_phase_deadline";
|
|
24
33
|
export declare function startLaneLoopController(ctx: Ctx, laneId: string, initialAdmission?: boolean): number;
|
|
25
34
|
export declare function runLaneLoopController(ctx: Ctx, laneId: string, deps: LoopControllerDeps): Promise<void>;
|
|
26
35
|
//# sourceMappingURL=lanes-loop.d.ts.map
|
|
@@ -52,6 +52,14 @@ function casLoopLaneState(hqRoot, laneId, from, to) {
|
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* terminal_reason values for interrupts HQ issues itself. An operator
|
|
57
|
+
* interrupt (`hq lanes interrupt`) records `operator_interrupt`; these keep a
|
|
58
|
+
* system recovery from looking like an operator action in the lane record.
|
|
59
|
+
*/
|
|
60
|
+
export const LOOP_CONTROLLER_RECOVERY_REASON = "loop_controller_recovery";
|
|
61
|
+
export const LOOP_STALL_RESTART_REASON = "loop_stall_restart";
|
|
62
|
+
export const LOOP_PHASE_DEADLINE_REASON = "loop_phase_deadline";
|
|
55
63
|
export function startLaneLoopController(ctx, laneId, initialAdmission = false) {
|
|
56
64
|
const entry = process.argv[1];
|
|
57
65
|
if (!entry)
|
|
@@ -202,7 +210,7 @@ export async function runLaneLoopController(ctx, laneId, deps) {
|
|
|
202
210
|
}
|
|
203
211
|
let interruptPromise;
|
|
204
212
|
const abortPhase = () => {
|
|
205
|
-
interruptPromise = deps.interruptLane(laneId, "loop phase deadline exceeded", { preserveLoopQueue: true }).catch((error) => {
|
|
213
|
+
interruptPromise = deps.interruptLane(laneId, "loop phase deadline exceeded", { preserveLoopQueue: true, terminalReason: LOOP_PHASE_DEADLINE_REASON }).catch((error) => {
|
|
206
214
|
process.stderr.write(`hq lanes: could not interrupt stalled loop phase ${laneId}: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
207
215
|
});
|
|
208
216
|
};
|
package/dist/commands/lanes.js
CHANGED
|
@@ -48,7 +48,7 @@ import { appendLaneFocusOverride, assertFocusGuardAllows, makeFocusOverride } fr
|
|
|
48
48
|
import { LANE_FOCUS_GUARD_FLAG, LANE_FOCUS_DASHBOARD_FLAG, LANE_HIRE_GRANT_FINISHED_LANES_FLAG, LANE_LIST_SKIP_REMOTE_REFRESH_FLAG, LANE_MESSAGE_ROUTE_OWNER_HOST_FLAG, LANE_ORPHANED_BARE_ID_DROPBOX_REPAIR_FLAG, LANE_PROFILE_PIN_EXHAUSTED_FALLBACK_FLAG, LANE_STATUS_ROW_FIELDS_FLAG, LANE_REAP_STOP_FLAG, LANE_REUSE_REFRESHES_PROFILE_FLAG, LANE_WORKER_CONTINUITY_FLAG, LANE_CONDUCT_LOOP_FLAG, LANE_ENVELOPE_REPAIR_GUIDANCE_FLAG, } from "../lib/lanes/flag-keys.js";
|
|
49
49
|
import { applyContinuitySettings, findWorkerContinuityLane, sameLaneHost, workerContinuityPrompt } from "../lib/lanes/worker-continuity.js";
|
|
50
50
|
import { commitIdleLoopStop, enqueueLoopEnvelope, interruptLoopQueue, interruptLoopQueueForStory, loopControllerIsAlive, loopLaneIsIdle, loopStatus, validateLoopEnvelope } from "../lib/lanes/loop.js";
|
|
51
|
-
import { runLaneLoopController, startLaneLoopController } from "./lanes-loop.js";
|
|
51
|
+
import { LOOP_CONTROLLER_RECOVERY_REASON, LOOP_STALL_RESTART_REASON, runLaneLoopController, startLaneLoopController } from "./lanes-loop.js";
|
|
52
52
|
import { configuredCodexRolloutHomes, retainCodexRollouts } from "../lib/lanes/disk-guard.js";
|
|
53
53
|
import { isControllerAliasOutpostId } from "../lib/lanes/admission-host.js";
|
|
54
54
|
import { runLeadAutopilotPass } from "../lib/lanes/autopilot.js";
|
|
@@ -494,6 +494,41 @@ function livenessDepsFromCtx(ctx) {
|
|
|
494
494
|
hostname: ctx.hostname,
|
|
495
495
|
};
|
|
496
496
|
}
|
|
497
|
+
/**
|
|
498
|
+
* Start a lane that admission just let through. A queued loop lane is marked
|
|
499
|
+
* running and gets its loop controller, which works through the queued phase
|
|
500
|
+
* envelopes; every other lane spawns its worker directly. Every admission path
|
|
501
|
+
* (reconcile, in-process queue drain, hq lanes admit) goes through here so a
|
|
502
|
+
* loop lane never runs its brief without a controller.
|
|
503
|
+
*/
|
|
504
|
+
function startAdmittedLane(ctx, root, candidate) {
|
|
505
|
+
if (candidate.loop_mode !== true || candidate.state !== "queued") {
|
|
506
|
+
const admissionHost = reconcileHostFromCtx(ctx);
|
|
507
|
+
return spawnLaneWorker(root, candidate, {
|
|
508
|
+
env: ctx.env,
|
|
509
|
+
hostname: admissionHost.hostname,
|
|
510
|
+
admissionHost,
|
|
511
|
+
modelDeps: { readFileSync: ctx.readFileSync, probe: ctx.probeProvider, env: ctx.env },
|
|
512
|
+
existsSync: ctx.existsSync,
|
|
513
|
+
spawnProcess: ctx.spawnProcess,
|
|
514
|
+
proveDetached: ctx.proveDetached,
|
|
515
|
+
killProcess: ctx.killProcess,
|
|
516
|
+
now: ctx.now,
|
|
517
|
+
resolveMachineIdentity: ctx.resolveMachineIdentity,
|
|
518
|
+
runCompaction: ctx.runCompaction,
|
|
519
|
+
locateTranscript: ctx.locateTranscript,
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
const admitted = casUpdateLane(root, candidate.lane_id, (current) => ({
|
|
523
|
+
...current,
|
|
524
|
+
state: "running",
|
|
525
|
+
queued_reason: undefined,
|
|
526
|
+
queued_details: undefined,
|
|
527
|
+
admission_reserved_at: undefined,
|
|
528
|
+
}));
|
|
529
|
+
startLaneLoopController(ctx, candidate.lane_id, true);
|
|
530
|
+
return admitted;
|
|
531
|
+
}
|
|
497
532
|
/**
|
|
498
533
|
* Admit queued lanes in-process after a command frees or adds capacity, so
|
|
499
534
|
* the queue drains without `hq daemon`. Best effort: an admission failure is
|
|
@@ -516,6 +551,7 @@ async function admitQueuedFromCtx(ctx) {
|
|
|
516
551
|
runCompaction: ctx.runCompaction,
|
|
517
552
|
locateTranscript: ctx.locateTranscript,
|
|
518
553
|
},
|
|
554
|
+
spawn: (root, candidate) => startAdmittedLane(ctx, root, candidate),
|
|
519
555
|
throttleDeps: ctx.throttleDeps,
|
|
520
556
|
livenessDeps: livenessDepsFromCtx(ctx),
|
|
521
557
|
presenceConnected: ctx.presenceConnected,
|
|
@@ -2432,11 +2468,18 @@ function autopilotWorkerStarter(ctx) {
|
|
|
2432
2468
|
return { lane_id: started.lane_id };
|
|
2433
2469
|
};
|
|
2434
2470
|
}
|
|
2471
|
+
/**
|
|
2472
|
+
* The senior for a lane created without `--senior`. Inside a lane worker
|
|
2473
|
+
* (HQ_LANE_ID set) the worker's own session wins, because HQ_PARENT_SESSION_ID
|
|
2474
|
+
* there names the session that started the worker, one level too high.
|
|
2475
|
+
*/
|
|
2435
2476
|
function defaultSenior(ctx) {
|
|
2477
|
+
const sessionId = sessionIdFromEnv(ctx.env);
|
|
2478
|
+
if (ctx.env.HQ_LANE_ID?.trim() && sessionId)
|
|
2479
|
+
return { kind: "session", id: sessionId };
|
|
2436
2480
|
const parent = ctx.env.HQ_PARENT_SESSION_ID?.trim();
|
|
2437
2481
|
if (parent)
|
|
2438
2482
|
return { kind: "session", id: parent };
|
|
2439
|
-
const sessionId = sessionIdFromEnv(ctx.env);
|
|
2440
2483
|
if (sessionId)
|
|
2441
2484
|
return { kind: "session", id: sessionId };
|
|
2442
2485
|
return { kind: "user" };
|
|
@@ -3348,7 +3391,7 @@ async function runCreate(ctx, opts) {
|
|
|
3348
3391
|
const requestedHireGrant = opts.hireGrant === undefined
|
|
3349
3392
|
? undefined
|
|
3350
3393
|
: parseHireGrant(opts.hireGrant);
|
|
3351
|
-
|
|
3394
|
+
let requestedPlacement = opts.placement !== undefined
|
|
3352
3395
|
? parsePlacementPolicy(opts.placement)
|
|
3353
3396
|
: undefined;
|
|
3354
3397
|
const defaultPlacement = opts.defaultPlacement !== undefined
|
|
@@ -3568,6 +3611,13 @@ async function runCreate(ctx, opts) {
|
|
|
3568
3611
|
placementGate ??= readLanePlacementEnabled(ctx, resolvePlacementCompany());
|
|
3569
3612
|
return placementGate;
|
|
3570
3613
|
};
|
|
3614
|
+
if (requestedPlacement?.kind === "local" &&
|
|
3615
|
+
defaultPlacement === undefined &&
|
|
3616
|
+
!(await placementEnabled())) {
|
|
3617
|
+
// With placement off every lane already runs here, so --placement local
|
|
3618
|
+
// asks for the default. Treat it as omitted instead of refusing.
|
|
3619
|
+
requestedPlacement = undefined;
|
|
3620
|
+
}
|
|
3571
3621
|
if (requestedPlacement !== undefined || defaultPlacement !== undefined) {
|
|
3572
3622
|
await assertPlacementEnabled(ctx, resolvePlacementCompany(), placementEnabled);
|
|
3573
3623
|
}
|
|
@@ -6305,6 +6355,7 @@ async function runShow(ctx, laneId, hasPendingQuestionForLane, resolveLaneTarget
|
|
|
6305
6355
|
...(showRefresh.remoteRefresh === undefined ? {} : { remote_refresh: showRefresh.remoteRefresh }),
|
|
6306
6356
|
provider_session_id: lane.provider_session_id ?? null,
|
|
6307
6357
|
provider_session_id_absent: lane.provider_session_id_absent ?? null,
|
|
6358
|
+
fingerprint: lane.fingerprint ?? null,
|
|
6308
6359
|
envelope_repair_mode: lane.envelope_repair?.mode ?? null,
|
|
6309
6360
|
envelope_validation_errors: lane.envelope_validation_errors ?? [],
|
|
6310
6361
|
last_resume: lane.last_resume ?? null,
|
|
@@ -7151,22 +7202,7 @@ async function runAdmit(ctx, laneId, opts) {
|
|
|
7151
7202
|
});
|
|
7152
7203
|
return;
|
|
7153
7204
|
}
|
|
7154
|
-
const started =
|
|
7155
|
-
env: ctx.env,
|
|
7156
|
-
modelDeps: {
|
|
7157
|
-
readFileSync: ctx.readFileSync,
|
|
7158
|
-
probe: ctx.probeProvider,
|
|
7159
|
-
env: ctx.env,
|
|
7160
|
-
},
|
|
7161
|
-
existsSync: ctx.existsSync,
|
|
7162
|
-
spawnProcess: ctx.spawnProcess,
|
|
7163
|
-
proveDetached: ctx.proveDetached,
|
|
7164
|
-
killProcess: ctx.killProcess,
|
|
7165
|
-
now: ctx.now,
|
|
7166
|
-
resolveMachineIdentity: ctx.resolveMachineIdentity,
|
|
7167
|
-
runCompaction: ctx.runCompaction,
|
|
7168
|
-
locateTranscript: ctx.locateTranscript,
|
|
7169
|
-
});
|
|
7205
|
+
const started = startAdmittedLane(ctx, hqRoot, lane);
|
|
7170
7206
|
const admitted = opts.force && started.state === "running"
|
|
7171
7207
|
? casUpdateLane(hqRoot, laneId, (current) => ({ ...current, admitted_reason: "operator_force" }))
|
|
7172
7208
|
: started;
|
|
@@ -8803,7 +8839,7 @@ async function runStop(ctx, laneId, reason, quiet = false) {
|
|
|
8803
8839
|
}
|
|
8804
8840
|
const controllerAlive = loopControllerIsAlive(runDirPath);
|
|
8805
8841
|
if (!controllerAlive && lane.state === "running") {
|
|
8806
|
-
await runInterrupt(ctx, lane.lane_id, "loop controller exited; recovering queued work before stop", { preserveLoopQueue: true, quiet: true });
|
|
8842
|
+
await runInterrupt(ctx, lane.lane_id, "loop controller exited; recovering queued work before stop", { preserveLoopQueue: true, quiet: true, terminalReason: LOOP_CONTROLLER_RECOVERY_REASON });
|
|
8807
8843
|
}
|
|
8808
8844
|
const queued = enqueueLoopEnvelope({ runDir: runDirPath, laneId: lane.lane_id, envelope: { kind: "stop" }, now: ctx.now });
|
|
8809
8845
|
if (!controllerAlive && lane.state !== "queued")
|
|
@@ -8879,6 +8915,13 @@ function stopTerminalWarning(laneId) {
|
|
|
8879
8915
|
* session so `hq lanes resume` continues it. Keep-alive resumes only
|
|
8880
8916
|
* `exited_with_partial_output` interruptions, so it leaves this lane alone.
|
|
8881
8917
|
*/
|
|
8918
|
+
/** Stop-history text for an operator interrupt given without --reason. */
|
|
8919
|
+
function defaultOperatorInterruptReason(ctx) {
|
|
8920
|
+
const sessionId = sessionIdFromEnv(ctx.env);
|
|
8921
|
+
const laneId = ctx.env.HQ_LANE_ID?.trim();
|
|
8922
|
+
const caller = laneId ? ` by lane ${laneId}` : sessionId ? ` by session ${sessionId}` : "";
|
|
8923
|
+
return `hq lanes interrupt${caller} (no reason given)`;
|
|
8924
|
+
}
|
|
8882
8925
|
async function runInterrupt(ctx, laneId, reason, options = {}) {
|
|
8883
8926
|
const operatorReason = reason?.trim();
|
|
8884
8927
|
if (reason !== undefined && !operatorReason) {
|
|
@@ -8886,6 +8929,11 @@ async function runInterrupt(ctx, laneId, reason, options = {}) {
|
|
|
8886
8929
|
}
|
|
8887
8930
|
if (operatorReason !== undefined)
|
|
8888
8931
|
assertNoSecretText("interrupt reason", operatorReason);
|
|
8932
|
+
const terminalReason = options.terminalReason ?? "operator_interrupt";
|
|
8933
|
+
// Every interrupt leaves a stop-history entry, so an operator interrupt
|
|
8934
|
+
// given without --reason is still distinguishable from a system one.
|
|
8935
|
+
const historyReason = operatorReason ??
|
|
8936
|
+
(terminalReason === "operator_interrupt" ? defaultOperatorInterruptReason(ctx) : terminalReason);
|
|
8889
8937
|
const hqRoot = ctx.hqRoot();
|
|
8890
8938
|
const lane = readLane(hqRoot, laneId);
|
|
8891
8939
|
if (lane.host.kind === "outpost" && !laneRunsHereForOperatorMutation(ctx, lane)) {
|
|
@@ -8910,7 +8958,7 @@ async function runInterrupt(ctx, laneId, reason, options = {}) {
|
|
|
8910
8958
|
if (!reason)
|
|
8911
8959
|
reason = "story-scoped loop phase interrupt";
|
|
8912
8960
|
const remaining = scoped;
|
|
8913
|
-
await runInterrupt(ctx, laneId, reason, { preserveLoopQueue: true, quiet: true });
|
|
8961
|
+
await runInterrupt(ctx, laneId, reason, { preserveLoopQueue: true, quiet: true, terminalReason: options.terminalReason });
|
|
8914
8962
|
if (!options.quiet)
|
|
8915
8963
|
printJson({ ok: true, action: "interrupt", lane_id: lane.lane_id, terminal: false, resumable: true, ...remaining });
|
|
8916
8964
|
return;
|
|
@@ -8945,13 +8993,11 @@ async function runInterrupt(ctx, laneId, reason, options = {}) {
|
|
|
8945
8993
|
queued_reason: undefined,
|
|
8946
8994
|
queued_details: undefined,
|
|
8947
8995
|
interrupted_at: at,
|
|
8948
|
-
terminal_reason:
|
|
8949
|
-
|
|
8950
|
-
stop_history
|
|
8951
|
-
|
|
8952
|
-
|
|
8953
|
-
],
|
|
8954
|
-
}),
|
|
8996
|
+
terminal_reason: terminalReason,
|
|
8997
|
+
stop_history: [
|
|
8998
|
+
...(current.stop_history ?? []),
|
|
8999
|
+
{ at, reason: historyReason },
|
|
9000
|
+
],
|
|
8955
9001
|
};
|
|
8956
9002
|
});
|
|
8957
9003
|
stopLaneProcesses(hqRoot, laneId, {
|
|
@@ -9307,31 +9353,7 @@ async function runReconcile(ctx, opts, recoverQueuedMessages) {
|
|
|
9307
9353
|
runCompaction: ctx.runCompaction,
|
|
9308
9354
|
locateTranscript: ctx.locateTranscript,
|
|
9309
9355
|
},
|
|
9310
|
-
spawn: (root, candidate) =>
|
|
9311
|
-
if (candidate.loop_mode !== true || candidate.state !== "queued") {
|
|
9312
|
-
return spawnLaneWorker(root, candidate, {
|
|
9313
|
-
env: ctx.env,
|
|
9314
|
-
modelDeps: { readFileSync: ctx.readFileSync, probe: ctx.probeProvider, env: ctx.env },
|
|
9315
|
-
existsSync: ctx.existsSync,
|
|
9316
|
-
spawnProcess: ctx.spawnProcess,
|
|
9317
|
-
proveDetached: ctx.proveDetached,
|
|
9318
|
-
killProcess: ctx.killProcess,
|
|
9319
|
-
now: ctx.now,
|
|
9320
|
-
resolveMachineIdentity: ctx.resolveMachineIdentity,
|
|
9321
|
-
runCompaction: ctx.runCompaction,
|
|
9322
|
-
locateTranscript: ctx.locateTranscript,
|
|
9323
|
-
});
|
|
9324
|
-
}
|
|
9325
|
-
const admitted = casUpdateLane(root, candidate.lane_id, (current) => ({
|
|
9326
|
-
...current,
|
|
9327
|
-
state: "running",
|
|
9328
|
-
queued_reason: undefined,
|
|
9329
|
-
queued_details: undefined,
|
|
9330
|
-
admission_reserved_at: undefined,
|
|
9331
|
-
}));
|
|
9332
|
-
startLaneLoopController(ctx, candidate.lane_id, true);
|
|
9333
|
-
return admitted;
|
|
9334
|
-
},
|
|
9356
|
+
spawn: (root, candidate) => startAdmittedLane(ctx, root, candidate),
|
|
9335
9357
|
throttleDeps: ctx.throttleDeps,
|
|
9336
9358
|
livenessDeps: livenessDepsFromCtx(ctx),
|
|
9337
9359
|
presenceConnected: ctx.presenceConnected,
|
|
@@ -9693,7 +9715,7 @@ export function registerLanesCommand(parent, deps = {}) {
|
|
|
9693
9715
|
return deps.restartLoopController(handlerCtx, laneId);
|
|
9694
9716
|
const current = readLane(handlerCtx.hqRoot(), laneId);
|
|
9695
9717
|
if (current.state === "running") {
|
|
9696
|
-
await runInterrupt(handlerCtx, laneId, "loop stall question answered; restarting controller", { preserveLoopQueue: true, quiet: true });
|
|
9718
|
+
await runInterrupt(handlerCtx, laneId, "loop stall question answered; restarting controller", { preserveLoopQueue: true, quiet: true, terminalReason: LOOP_STALL_RESTART_REASON });
|
|
9697
9719
|
}
|
|
9698
9720
|
const runDirPath = runDir(handlerCtx.hqRoot(), laneId);
|
|
9699
9721
|
if (loopControllerIsAlive(runDirPath)) {
|
|
@@ -9983,7 +10005,7 @@ export function registerLanesCommand(parent, deps = {}) {
|
|
|
9983
10005
|
const controllerAlive = loopControllerIsAlive(runDirPath);
|
|
9984
10006
|
const result = enqueueLoopEnvelope({ runDir: runDirPath, laneId: lane.lane_id, envelope, now: ctx.now });
|
|
9985
10007
|
if (!controllerAlive && lane.state === "running") {
|
|
9986
|
-
await runInterrupt(ctx, lane.lane_id, "loop controller exited; recovering the queued phase", { preserveLoopQueue: true, quiet: true });
|
|
10008
|
+
await runInterrupt(ctx, lane.lane_id, "loop controller exited; recovering the queued phase", { preserveLoopQueue: true, quiet: true, terminalReason: LOOP_CONTROLLER_RECOVERY_REASON });
|
|
9987
10009
|
}
|
|
9988
10010
|
const shouldRestart = !controllerAlive && lane.state !== "queued";
|
|
9989
10011
|
const controllerPid = shouldRestart ? startLaneLoopController(ctx, lane.lane_id) : undefined;
|
|
@@ -10378,6 +10400,7 @@ export function registerLanesCommand(parent, deps = {}) {
|
|
|
10378
10400
|
}));
|
|
10379
10401
|
lanes
|
|
10380
10402
|
.command("stop")
|
|
10403
|
+
.alias("cancel")
|
|
10381
10404
|
.description("Cancel a queued lane or kill a running one (terminal; use interrupt to keep it resumable)")
|
|
10382
10405
|
.argument("<lane-id>", "Lane id")
|
|
10383
10406
|
.option("--reason <text>", "Record the reason in the lane's terminal history")
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
+
/** Upper bound on list pages one short-id lookup reads (per cursor chain). */
|
|
3
|
+
export declare const SHORT_ID_MAX_PAGES = 50;
|
|
2
4
|
export declare function registerMeetingsCommand(program: Command): void;
|
|
3
5
|
//# sourceMappingURL=meetings.d.ts.map
|
|
@@ -251,26 +251,69 @@ function renderSignals(signals) {
|
|
|
251
251
|
}
|
|
252
252
|
console.log();
|
|
253
253
|
}
|
|
254
|
+
/** The server caps one `/v1/meetings` page at this many rows. */
|
|
255
|
+
const MEETINGS_PAGE_SIZE = "100";
|
|
256
|
+
/** Upper bound on list pages one short-id lookup reads (per cursor chain). */
|
|
257
|
+
export const SHORT_ID_MAX_PAGES = 50;
|
|
258
|
+
/**
|
|
259
|
+
* Resolve the 8-character id `hq meetings list` prints to a full meeting id.
|
|
260
|
+
*
|
|
261
|
+
* `/v1/meetings` is paged: the server caps a page at 100 rows (it ignores a
|
|
262
|
+
* larger `limit`) and may end a page early, even empty, with
|
|
263
|
+
* `truncated: true` when it runs out of time. Reading only the first page
|
|
264
|
+
* missed any meeting past it, so follow `nextToken` (and `nativeNextToken`
|
|
265
|
+
* for HQ Meet calls) until the prefix is found or the list ends. A full
|
|
266
|
+
* 8-character prefix stops at the first page with a match; a shorter prefix
|
|
267
|
+
* reads every page so ambiguity is still reported.
|
|
268
|
+
*/
|
|
254
269
|
async function resolveShortId(token, prefix, query) {
|
|
255
270
|
if (prefix.includes("-") && prefix.length > 8)
|
|
256
271
|
return prefix;
|
|
257
|
-
const
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
272
|
+
const stopOnFirstMatch = prefix.length >= 8;
|
|
273
|
+
const matches = new Map();
|
|
274
|
+
let capped = false;
|
|
275
|
+
const fetchPage = async (extra) => {
|
|
276
|
+
const res = await vaultApiFetch({
|
|
277
|
+
token,
|
|
278
|
+
path: "/v1/meetings",
|
|
279
|
+
query: { ...query, limit: MEETINGS_PAGE_SIZE, ...extra },
|
|
280
|
+
});
|
|
281
|
+
if (!res.ok) {
|
|
282
|
+
throw new Error(`Could not resolve meeting ID "${prefix}" — failed to list meetings (${res.status}). Pass the full meeting id.`);
|
|
283
|
+
}
|
|
284
|
+
const data = (await res.json());
|
|
285
|
+
for (const m of data.meetings ?? []) {
|
|
286
|
+
if (typeof m.meetingId === "string" && m.meetingId.startsWith(prefix))
|
|
287
|
+
matches.set(m.meetingId, m);
|
|
288
|
+
}
|
|
289
|
+
return data;
|
|
290
|
+
};
|
|
291
|
+
const first = await fetchPage({});
|
|
292
|
+
let nextToken = first.nextToken;
|
|
293
|
+
for (let page = 1; nextToken && !(stopOnFirstMatch && matches.size > 0); page += 1) {
|
|
294
|
+
if (page >= SHORT_ID_MAX_PAGES) {
|
|
295
|
+
capped = true;
|
|
296
|
+
break;
|
|
297
|
+
}
|
|
298
|
+
nextToken = (await fetchPage({ nextToken })).nextToken;
|
|
299
|
+
}
|
|
300
|
+
let nativeCursor = first.nativeNextToken;
|
|
301
|
+
for (let page = 1; nativeCursor && !(stopOnFirstMatch && matches.size > 0); page += 1) {
|
|
302
|
+
if (page >= SHORT_ID_MAX_PAGES) {
|
|
303
|
+
capped = true;
|
|
304
|
+
break;
|
|
305
|
+
}
|
|
306
|
+
nativeCursor = (await fetchPage({ nativeCursor })).nativeNextToken;
|
|
264
307
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
if (matches.
|
|
268
|
-
|
|
269
|
-
if (matches.length > 1) {
|
|
270
|
-
console.error(chalk.red(`Ambiguous ID prefix "${prefix}" — matches ${matches.length} meetings. Use a longer prefix.`));
|
|
308
|
+
if (matches.size === 1)
|
|
309
|
+
return [...matches.keys()][0];
|
|
310
|
+
if (matches.size > 1) {
|
|
311
|
+
console.error(chalk.red(`Ambiguous ID prefix "${prefix}" — matches ${matches.size} meetings. Use a longer prefix.`));
|
|
271
312
|
process.exit(1);
|
|
272
313
|
}
|
|
273
|
-
console.error(chalk.red(
|
|
314
|
+
console.error(chalk.red(capped
|
|
315
|
+
? `No meeting matches ID "${prefix}" in the newest ${SHORT_ID_MAX_PAGES * Number(MEETINGS_PAGE_SIZE)} meetings. Pass the full meeting id (shown by \`hq meetings list --json\`).`
|
|
316
|
+
: `No meeting matches ID "${prefix}". It may still be processing or attributed to a different company. Pass the full meeting id, or add --company <slug>.`));
|
|
274
317
|
process.exit(1);
|
|
275
318
|
}
|
|
276
319
|
async function handleApiError(res, json = false) {
|
package/dist/commands/mesh.d.ts
CHANGED
|
@@ -6,10 +6,27 @@
|
|
|
6
6
|
* Distinct from `hq doctor` (hook guardrails).
|
|
7
7
|
*/
|
|
8
8
|
import { Command } from "commander";
|
|
9
|
-
import { type MeshCompany, type MeshThread } from "../lib/mesh/api.js";
|
|
9
|
+
import { type MeshCompany, type MeshProjectRepo, type MeshProjectStoryInput, type MeshThread } from "../lib/mesh/api.js";
|
|
10
|
+
import { UnknownProjectStoryStateError } from "../lib/mesh/story-status.js";
|
|
10
11
|
import { type DaemonInstallPlatform, type InstallPaths } from "../lib/mesh/live/daemon/install.js";
|
|
11
12
|
import { type RunCommand } from "../lib/service-manager/launchd.js";
|
|
12
13
|
export declare function formatCheckLines(threads: MeshThread[], company: MeshCompany, projectId?: string): string[];
|
|
14
|
+
/** 400 PROJECT_VIEW_INVALID "Unknown story ..." from a story PATCH. */
|
|
15
|
+
export declare function isUnknownBoardStory(error: unknown): boolean;
|
|
16
|
+
/** The local PRD for this project when it lists `storyId`; null otherwise. */
|
|
17
|
+
export declare function localPrdWithStory(company: {
|
|
18
|
+
companySlug?: string;
|
|
19
|
+
}, projectId: string, storyId: string, hqRoot?: string | undefined): {
|
|
20
|
+
prdPath: string;
|
|
21
|
+
source: ProjectPlanSource;
|
|
22
|
+
} | null;
|
|
23
|
+
type ProjectPlanSource = {
|
|
24
|
+
name?: string;
|
|
25
|
+
description?: string;
|
|
26
|
+
stories: MeshProjectStoryInput[];
|
|
27
|
+
repos?: MeshProjectRepo[];
|
|
28
|
+
rejectedStories: UnknownProjectStoryStateError[];
|
|
29
|
+
};
|
|
13
30
|
/** Test seam for the usage-attribution question asked by `default set`. */
|
|
14
31
|
export interface DefaultCompanyPromptDeps {
|
|
15
32
|
/** True when stdin and stdout are a terminal (a person can answer). */
|
|
@@ -61,4 +78,5 @@ export declare function runDaemonStatus(opts: {
|
|
|
61
78
|
run?: RunCommand;
|
|
62
79
|
uid?: number;
|
|
63
80
|
}): Promise<void>;
|
|
81
|
+
export {};
|
|
64
82
|
//# sourceMappingURL=mesh.d.ts.map
|