@indigoai-us/hq-cli 5.345.105 → 5.345.107
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 +23 -0
- package/assets/scaffold/core/scripts/hq-task.mjs +259 -0
- package/assets/scaffold/core/scripts/hq-task.sh +31 -0
- package/assets/scaffold/core/scripts/pipeline-worker-table.sh +285 -0
- package/dist/command-catalog.generated.d.ts +135 -5
- package/dist/command-catalog.generated.js +161 -5
- package/dist/commands/auth.js +42 -0
- package/dist/commands/core-hq-task-fast.d.ts +4 -0
- package/dist/commands/core-hq-task-fast.js +11 -0
- package/dist/commands/core-hq-task-native.d.ts +20 -0
- package/dist/commands/core-hq-task-native.js +275 -0
- package/dist/commands/core-native-bucket-c-flag.d.ts +3 -0
- package/dist/commands/core-native-bucket-c-flag.js +6 -0
- package/dist/commands/core-native-family-flag.d.ts +0 -2
- package/dist/commands/core-native-family-flag.js +0 -4
- package/dist/commands/core-native-pipeline-flag.d.ts +3 -0
- package/dist/commands/core-native-pipeline-flag.js +6 -0
- package/dist/commands/core-ontology-native.js +24 -30
- package/dist/commands/core-pipeline-workers-command.d.ts +3 -0
- package/dist/commands/core-pipeline-workers-command.js +37 -0
- package/dist/commands/core-pipeline-workers-native.d.ts +13 -0
- package/dist/commands/core-pipeline-workers-native.js +361 -0
- package/dist/commands/core.js +12 -10
- package/dist/commands/lanes.js +32 -7
- package/dist/commands/logout.d.ts +6 -1
- package/dist/commands/logout.js +18 -2
- package/dist/commands/outposts.d.ts +5 -0
- package/dist/commands/outposts.js +5 -0
- package/dist/fast-scaffold.d.ts +14 -1
- package/dist/fast-scaffold.js +149 -5
- package/dist/hq-cloud-sync-runner/bin/sync-runner.js +56937 -64550
- package/dist/hq-cloud-sync-runner/package.json +1 -1
- package/dist/index.js +15 -1
- package/dist/lib/daemon/remote-control-pools.d.ts +2 -2
- 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/reconcile.js +4 -0
- package/dist/lib/lanes/remote-control-lane.d.ts +33 -0
- package/dist/lib/lanes/remote-control-lane.js +276 -0
- package/dist/lib/lanes/spawn.d.ts +5 -0
- package/dist/lib/lanes/spawn.js +65 -14
- package/dist/lib/lanes/store.js +7 -0
- package/dist/lib/lanes/types.d.ts +2 -0
- package/dist/lib/lanes/types.js +1 -1
- package/dist/lib/lanes/watcher.js +8 -1
- package/dist/utils/cognito-config.d.ts +5 -0
- package/dist/utils/cognito-config.js +14 -0
- package/dist/utils/cognito-revoke.d.ts +33 -0
- package/dist/utils/cognito-revoke.js +42 -0
- package/dist/utils/cognito-session.d.ts +12 -4
- package/dist/utils/cognito-session.js +42 -9
- package/dist/utils/token-keychain.d.ts +96 -0
- package/dist/utils/token-keychain.js +218 -0
- package/package.json +2 -2
- package/assets/scaffold/core/scripts/ontology-audience-grant.sh +0 -81
- package/assets/scaffold/core/scripts/ontology-brief.mjs +0 -93
- package/assets/scaffold/core/scripts/ontology-candidate.sh +0 -100
- package/assets/scaffold/core/scripts/ontology-garden.mjs +0 -327
- package/assets/scaffold/core/scripts/ontology-source-worklist.sh +0 -226
- package/assets/scaffold/core/scripts/source-yaml-validate.sh +0 -36
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
export const PIPELINE_WORKER_TABLE_ASSET = "core/scripts/pipeline-worker-table.sh";
|
|
4
|
+
export const CORE_NATIVE_PIPELINE_FLAG = "core-native-pipeline";
|
|
5
|
+
const USAGE = "# pipeline-worker-table.sh — the per-worker engine/model table behind\n# /run-project --pipeline.\n#\n# Each worker lane runs on its own engine and model. This script proposes that\n# table from a prd.json and, once the owner has confirmed it, turns it into the\n# launcher exports each lane starts with. It never reads an engine or model\n# default from a settings file (orchestrator.yaml, settings.json, ...): the only\n# seed is each worker's own worker.yaml, and anything that cannot be read off\n# it is left empty and flagged for the owner to answer.\n#\n# Usage:\n# pipeline-worker-table.sh [--workers-root <dir>]... [--overlay <file>] <prd.json>\n# pipeline-worker-table.sh [--workers-root <dir>]... [--overlay <file>] --confirmed <table.tsv> <prd.json>\n#\n# PROPOSE MODE (no --confirmed) prints tab-separated lines:\n#\n# row <worker> <engine> <model> <effort> <status> <stories> <note>\n# hint <worker> <story> <model>\n# unclassified <story>\n#\n# status is `ok` or `needs-answer`. The lane set is the union of every\n# story's worker sequence: one `row` per distinct worker that appears in any\n# sequence (architect and qa-tester included), in first-seen order. A `hint`\n# line is a story's model_hint, shown for the owner; a confirmed table pin wins\n# over it (see CONFIRMED MODE).\n#\n# WORKER SEQUENCE (story -> workers, the full phase sequence in order), first\n# match wins; pipeline-conductor.sh classify reads the same three sources:\n# 1. --overlay <file> (the run's <state>/overlay.json, or the preflight plan):\n# {id: {\"worker_sequence\": [...]}}, {id: [...]}, or\n# {\"ordered_stories\": [{\"id\": .., \"worker_sequence\": [...]}]}.\n# 2. worker_preference — a string or an array of worker ids, used as given.\n# 3. Fallback, only when both are empty: the keyword table from\n# .claude/skills/execute-task/SKILL.md step 3, matched case-insensitively\n# against title + description + labels:\n# schema_change (database|migration|schema|prisma|sql) -> database-dev\n# ui_component (component|page|form|button|react|ui) -> frontend-dev\n# api_development (endpoint|api|rest|graphql|route|service) -> backend-dev\n# giving architect, the matched implementers (backend-dev also for a\n# schema change), then qa-tester.\n# A story matching none is printed as `unclassified`; no worker is guessed.\n#\n# UNKNOWN WORKERS: a worker with no worker.yaml under the worker roots gets an\n# empty, needs-answer row noted \"no worker.yaml\", and a line on stderr naming\n# the id, the roots searched and every known id. pipeline-conductor.sh classify\n# refuses such a worker before launch.\n#\n# ENGINE SEED (from worker.yaml `execution:`):\n# model only -> engine claude, model = execution.model\n# codex_model only -> engine codex, model = execution.codex_model,\n# effort = `--reasoning <x>` from execution.codex_flags\n# both set -> ambiguous: engine and model empty, needs-answer, the\n# candidates listed in the note column\n# neither / no file -> engine and model empty, needs-answer\n# worker.yaml has no grok field, so grok is never seeded; the owner chooses it\n# in the confirmed table.\n#\n# CONFIRMED MODE: --confirmed <file> holds one lane per line,\n# <worker> <engine> <model> <effort> (tabs or spaces; # comments allowed;\n# effort may be omitted). engine must be claude, codex, or grok and model must\n# be non-empty, or the script exits 2. Output per lane, using the pins in\n# core/scripts/workflow-runner.mjs, plus HQ_CONDUCT_ENGINE=<engine> for\n# every lane (a loop lane runs pipeline phase envelopes on that engine):\n# claude -> HQ_WORKFLOW_CLAUDE_PLAN_MODEL, HQ_WORKFLOW_CLAUDE_EXEC_MODEL,\n# HQ_WORKFLOW_CLAUDE_EFFORT\n# codex|grok -> HQ_WORKFLOW_MODEL, HQ_WORKFLOW_EFFORT\n# The effort export is omitted when effort is empty (runner default applies).\n# A story's model_hint never overrides the lane: the lane's table pin wins.\n# Each hinted story on a confirmed lane gets a `# lane <w> story <id>\n# (model_hint)` block whose exports are commented out and that says \"hint\n# ignored, table pins <model>\". A bare alias (opus, sonnet, haiku, fable) is\n# never printed as a model.\n#\n# Worker roots: --workers-root (repeatable) or PIPELINE_WORKERS_ROOTS\n# (colon-separated). Default: <hq>/core/workers, <hq>/companies/*/workers,\n# <hq>/personal/workers. A worker is the directory named <id> holding worker.yaml.\n#\n# POSIX sh (dash-clean); JSON work is done by jq.\n";
|
|
6
|
+
function isRecord(value) {
|
|
7
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
8
|
+
}
|
|
9
|
+
function stringValue(value) {
|
|
10
|
+
if (value === null || value === undefined)
|
|
11
|
+
return "null";
|
|
12
|
+
if (typeof value === "string")
|
|
13
|
+
return value;
|
|
14
|
+
return JSON.stringify(value);
|
|
15
|
+
}
|
|
16
|
+
function sequence(value) {
|
|
17
|
+
if (Array.isArray(value))
|
|
18
|
+
return value.filter((item) => typeof item === "string" && item.length > 0);
|
|
19
|
+
if (typeof value === "string")
|
|
20
|
+
return value.split(",").map((item) => item.trim()).filter(Boolean);
|
|
21
|
+
if (isRecord(value))
|
|
22
|
+
return sequence(value.worker_sequence ?? []);
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
function asciiLower(text) {
|
|
26
|
+
return text.replace(/[A-Z]/g, (letter) => letter.toLowerCase());
|
|
27
|
+
}
|
|
28
|
+
function storyText(story) {
|
|
29
|
+
const title = story.title ?? "";
|
|
30
|
+
const description = story.description ?? "";
|
|
31
|
+
const labels = Array.isArray(story.labels) ? story.labels.join(" ") : "";
|
|
32
|
+
return asciiLower([stringValue(title === "" ? "" : title), stringValue(description === "" ? "" : description), labels].join(" "));
|
|
33
|
+
}
|
|
34
|
+
const WORD = {
|
|
35
|
+
schema: /\b(database|migration|schema|prisma|sql)\b/,
|
|
36
|
+
ui: /\b(component|page|form|button|react|ui)\b/,
|
|
37
|
+
api: /\b(endpoint|api|rest|graphql|route|service)\b/,
|
|
38
|
+
};
|
|
39
|
+
function fallbackSequence(story) {
|
|
40
|
+
const text = storyText(story);
|
|
41
|
+
const schema = WORD.schema.test(text);
|
|
42
|
+
const ui = WORD.ui.test(text);
|
|
43
|
+
const api = WORD.api.test(text);
|
|
44
|
+
if (!schema && !ui && !api)
|
|
45
|
+
return [];
|
|
46
|
+
return [
|
|
47
|
+
"architect",
|
|
48
|
+
...(schema ? ["database-dev"] : []),
|
|
49
|
+
...(api || schema ? ["backend-dev"] : []),
|
|
50
|
+
...(ui ? ["frontend-dev"] : []),
|
|
51
|
+
"qa-tester",
|
|
52
|
+
];
|
|
53
|
+
}
|
|
54
|
+
function storyId(story) {
|
|
55
|
+
return story.id === null || story.id === undefined ? "" : stringValue(story.id);
|
|
56
|
+
}
|
|
57
|
+
function deriveSequences(stories, overlay) {
|
|
58
|
+
const overrides = {};
|
|
59
|
+
if (Array.isArray(overlay.ordered_stories)) {
|
|
60
|
+
for (const row of overlay.ordered_stories) {
|
|
61
|
+
if (isRecord(row) && typeof row.id === "string")
|
|
62
|
+
overrides[row.id] = sequence(row.worker_sequence ?? []);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
for (const [id, value] of Object.entries(overlay))
|
|
67
|
+
overrides[id] = sequence(value);
|
|
68
|
+
}
|
|
69
|
+
return stories.map((story) => {
|
|
70
|
+
const id = storyId(story);
|
|
71
|
+
const override = overrides[id] ?? [];
|
|
72
|
+
const preferred = sequence(story.worker_preference ?? []);
|
|
73
|
+
const workers = override.length > 0 ? override : preferred.length > 0 ? preferred : fallbackSequence(story);
|
|
74
|
+
return { id, workers, story };
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
function parseArgs(args) {
|
|
78
|
+
const roots = [];
|
|
79
|
+
let confirmed = "";
|
|
80
|
+
let overlay = "";
|
|
81
|
+
let prd = "";
|
|
82
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
83
|
+
const arg = args[i];
|
|
84
|
+
if (arg === "--workers-root" || arg === "--confirmed" || arg === "--overlay") {
|
|
85
|
+
const value = args[i + 1];
|
|
86
|
+
if (value === undefined)
|
|
87
|
+
return `${arg} needs ${arg === "--workers-root" ? "a dir" : "a file"}`;
|
|
88
|
+
if (arg === "--workers-root")
|
|
89
|
+
roots.push(value);
|
|
90
|
+
if (arg === "--confirmed")
|
|
91
|
+
confirmed = value;
|
|
92
|
+
if (arg === "--overlay")
|
|
93
|
+
overlay = value;
|
|
94
|
+
i += 1;
|
|
95
|
+
}
|
|
96
|
+
else if (arg === "-h" || arg === "--help") {
|
|
97
|
+
return { roots, confirmed, overlay, prd, help: true };
|
|
98
|
+
}
|
|
99
|
+
else if (arg.startsWith("-")) {
|
|
100
|
+
return `unknown flag: ${arg}`;
|
|
101
|
+
}
|
|
102
|
+
else if (!prd) {
|
|
103
|
+
prd = arg;
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
return "only one prd.json";
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (!prd)
|
|
110
|
+
return "usage: pipeline-worker-table.sh [--workers-root <dir>] [--overlay <file>] [--confirmed <table.tsv>] <prd.json>";
|
|
111
|
+
return { roots, confirmed, overlay, prd, help: false };
|
|
112
|
+
}
|
|
113
|
+
function readJson(file) {
|
|
114
|
+
return JSON.parse(fs.readFileSync(file, "utf8"));
|
|
115
|
+
}
|
|
116
|
+
export function pipelineWorkerRoots(argsRoots, root, env) {
|
|
117
|
+
if (argsRoots.length > 0)
|
|
118
|
+
return argsRoots;
|
|
119
|
+
if (env.PIPELINE_WORKERS_ROOTS !== undefined)
|
|
120
|
+
return env.PIPELINE_WORKERS_ROOTS.split(":");
|
|
121
|
+
const roots = [path.join(root, "core/workers")];
|
|
122
|
+
const companies = path.join(root, "companies");
|
|
123
|
+
try {
|
|
124
|
+
for (const name of fs.readdirSync(companies).sort()) {
|
|
125
|
+
const workers = path.join(companies, name, "workers");
|
|
126
|
+
try {
|
|
127
|
+
if (fs.statSync(workers).isDirectory())
|
|
128
|
+
roots.push(workers);
|
|
129
|
+
}
|
|
130
|
+
catch { /* shell skips missing company worker roots */ }
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
catch { /* shell glob has no matches when companies is absent */ }
|
|
134
|
+
roots.push(path.join(root, "personal/workers"));
|
|
135
|
+
return roots;
|
|
136
|
+
}
|
|
137
|
+
export function pipelineWorkerFallbackArgs(args, root, env) {
|
|
138
|
+
if (args.includes("--workers-root") || env.PIPELINE_WORKERS_ROOTS !== undefined)
|
|
139
|
+
return args;
|
|
140
|
+
const roots = pipelineWorkerRoots([], root, env);
|
|
141
|
+
return [...roots.flatMap((workerRoot) => ["--workers-root", workerRoot]), ...args];
|
|
142
|
+
}
|
|
143
|
+
function listWorkerYaml(root) {
|
|
144
|
+
const results = [];
|
|
145
|
+
const stack = [root];
|
|
146
|
+
while (stack.length > 0) {
|
|
147
|
+
const current = stack.pop();
|
|
148
|
+
let entries;
|
|
149
|
+
try {
|
|
150
|
+
entries = fs.readdirSync(current, { withFileTypes: true });
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
for (let i = entries.length - 1; i >= 0; i -= 1) {
|
|
156
|
+
const entry = entries[i];
|
|
157
|
+
const file = path.join(current, entry.name);
|
|
158
|
+
if (entry.isDirectory())
|
|
159
|
+
stack.push(file);
|
|
160
|
+
else if (entry.isFile() && entry.name === "worker.yaml")
|
|
161
|
+
results.push(file);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return results;
|
|
165
|
+
}
|
|
166
|
+
function findWorkerYaml(worker, roots) {
|
|
167
|
+
for (const root of roots) {
|
|
168
|
+
for (const file of listWorkerYaml(root)) {
|
|
169
|
+
if (path.basename(path.dirname(file)) === worker)
|
|
170
|
+
return file;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return undefined;
|
|
174
|
+
}
|
|
175
|
+
function knownWorkers(roots) {
|
|
176
|
+
return [...new Set(roots.flatMap((root) => listWorkerYaml(root).map((file) => path.basename(path.dirname(file)))))].sort();
|
|
177
|
+
}
|
|
178
|
+
function executionField(file, key) {
|
|
179
|
+
let text;
|
|
180
|
+
try {
|
|
181
|
+
text = fs.readFileSync(file, "utf8");
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
return "";
|
|
185
|
+
}
|
|
186
|
+
let inExecution = false;
|
|
187
|
+
for (const line of text.split(/\r?\n/)) {
|
|
188
|
+
if (/^[^\s#]/.test(line)) {
|
|
189
|
+
inExecution = /^execution:\s*$/.test(line);
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
if (!inExecution)
|
|
193
|
+
continue;
|
|
194
|
+
const match = new RegExp(`^[\\t ]+${key}:[\\t ]*(.*)$`).exec(line);
|
|
195
|
+
if (!match)
|
|
196
|
+
continue;
|
|
197
|
+
let value = match[1].replace(/[\t ]+#.*$/, "").replace(/[\t ]+$/, "");
|
|
198
|
+
if (value.startsWith("\"") || value.startsWith("'"))
|
|
199
|
+
value = value.slice(1);
|
|
200
|
+
if (value.endsWith("\"") || value.endsWith("'"))
|
|
201
|
+
value = value.slice(0, -1);
|
|
202
|
+
return value;
|
|
203
|
+
}
|
|
204
|
+
return "";
|
|
205
|
+
}
|
|
206
|
+
function quoteShell(value) {
|
|
207
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
208
|
+
}
|
|
209
|
+
function emitExports(engine, model, effort, prefix = "") {
|
|
210
|
+
let output = `${prefix}export HQ_CONDUCT_ENGINE=${quoteShell(engine)}\n`;
|
|
211
|
+
if (engine === "claude") {
|
|
212
|
+
output += `${prefix}export HQ_WORKFLOW_CLAUDE_PLAN_MODEL=${quoteShell(model)}\n`;
|
|
213
|
+
output += `${prefix}export HQ_WORKFLOW_CLAUDE_EXEC_MODEL=${quoteShell(model)}\n`;
|
|
214
|
+
if (effort)
|
|
215
|
+
output += `${prefix}export HQ_WORKFLOW_CLAUDE_EFFORT=${quoteShell(effort)}\n`;
|
|
216
|
+
}
|
|
217
|
+
else if (engine === "codex" || engine === "grok") {
|
|
218
|
+
output += `${prefix}export HQ_WORKFLOW_MODEL=${quoteShell(model)}\n`;
|
|
219
|
+
if (effort)
|
|
220
|
+
output += `${prefix}export HQ_WORKFLOW_EFFORT=${quoteShell(effort)}\n`;
|
|
221
|
+
}
|
|
222
|
+
return output;
|
|
223
|
+
}
|
|
224
|
+
function bareAlias(hint) {
|
|
225
|
+
return ["opus", "sonnet", "haiku", "fable", "Opus", "Sonnet", "Haiku", "Fable"].includes(hint);
|
|
226
|
+
}
|
|
227
|
+
function confirmedOutput(file, stories, sequences) {
|
|
228
|
+
let text;
|
|
229
|
+
try {
|
|
230
|
+
text = fs.readFileSync(file, "utf8");
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
return { code: 1, stdout: "", stderr: `pipeline-worker-table: no such confirmed table: ${file}\n` };
|
|
234
|
+
}
|
|
235
|
+
let stdout = "";
|
|
236
|
+
for (const line of text.split(/\r?\n/)) {
|
|
237
|
+
const columns = line.trim().split(/\s+/);
|
|
238
|
+
if (!columns[0] || columns[0].startsWith("#") || columns[0] === "worker")
|
|
239
|
+
continue;
|
|
240
|
+
const [worker, engine, model, effort = ""] = columns;
|
|
241
|
+
if (!(engine === "claude" || engine === "codex" || engine === "grok")) {
|
|
242
|
+
return { code: 2, stdout: "", stderr: `pipeline-worker-table: lane ${worker}: engine must be claude, codex, or grok (got '${engine ?? ""}')\n` };
|
|
243
|
+
}
|
|
244
|
+
if (!model)
|
|
245
|
+
return { code: 2, stdout: "", stderr: `pipeline-worker-table: lane ${worker}: model is empty — answer it before launch\n` };
|
|
246
|
+
stdout += `# lane ${worker} (${engine})\n${emitExports(engine, model, effort)}`;
|
|
247
|
+
for (const story of stories) {
|
|
248
|
+
const id = storyId(story);
|
|
249
|
+
const hint = story.model_hint !== null && story.model_hint !== undefined && story.model_hint !== "" ? stringValue(story.model_hint) : "";
|
|
250
|
+
if (!hint || !sequences.some((row) => row.id === id && row.workers.includes(worker)))
|
|
251
|
+
continue;
|
|
252
|
+
if (bareAlias(hint))
|
|
253
|
+
stdout += `# lane ${worker} story ${id} (model_hint) bare alias ignored, table pins ${model}\n`;
|
|
254
|
+
else {
|
|
255
|
+
stdout += `# lane ${worker} story ${id} (model_hint) hint ignored, table pins ${model}\n`;
|
|
256
|
+
stdout += emitExports(engine, hint, effort, "# ");
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return { code: 0, stdout, stderr: "" };
|
|
261
|
+
}
|
|
262
|
+
export function runPipelineWorkerTable(args, root, env = process.env) {
|
|
263
|
+
const parsed = parseArgs(args);
|
|
264
|
+
if (typeof parsed === "string")
|
|
265
|
+
return { code: parsed.startsWith("usage:") ? 1 : 1, stdout: "", stderr: `pipeline-worker-table: ${parsed}\n` };
|
|
266
|
+
if (parsed.help)
|
|
267
|
+
return { code: 0, stdout: USAGE, stderr: "" };
|
|
268
|
+
let prd;
|
|
269
|
+
try {
|
|
270
|
+
prd = readJson(parsed.prd);
|
|
271
|
+
}
|
|
272
|
+
catch {
|
|
273
|
+
return { code: 1, stdout: "", stderr: `pipeline-worker-table: not a prd (no userStories array): ${parsed.prd}\n` };
|
|
274
|
+
}
|
|
275
|
+
if (!isRecord(prd) || !Array.isArray(prd.userStories))
|
|
276
|
+
return { code: 1, stdout: "", stderr: `pipeline-worker-table: not a prd (no userStories array): ${parsed.prd}\n` };
|
|
277
|
+
let overlay = {};
|
|
278
|
+
if (parsed.overlay) {
|
|
279
|
+
try {
|
|
280
|
+
overlay = readJson(parsed.overlay);
|
|
281
|
+
}
|
|
282
|
+
catch {
|
|
283
|
+
return { code: 1, stdout: "", stderr: `pipeline-worker-table: overlay is not a JSON object: ${parsed.overlay}\n` };
|
|
284
|
+
}
|
|
285
|
+
if (!isRecord(overlay))
|
|
286
|
+
return { code: 1, stdout: "", stderr: `pipeline-worker-table: overlay is not a JSON object: ${parsed.overlay}\n` };
|
|
287
|
+
}
|
|
288
|
+
if (parsed.confirmed) {
|
|
289
|
+
if (!fs.existsSync(parsed.confirmed))
|
|
290
|
+
return { code: 1, stdout: "", stderr: `pipeline-worker-table: no such confirmed table: ${parsed.confirmed}\n` };
|
|
291
|
+
return confirmedOutput(parsed.confirmed, prd.userStories, deriveSequences(prd.userStories, overlay));
|
|
292
|
+
}
|
|
293
|
+
const roots = pipelineWorkerRoots(parsed.roots, root, env);
|
|
294
|
+
const sequences = deriveSequences(prd.userStories, overlay);
|
|
295
|
+
const workers = [];
|
|
296
|
+
const seen = new Set();
|
|
297
|
+
for (const row of sequences)
|
|
298
|
+
for (const worker of row.workers)
|
|
299
|
+
if (!seen.has(worker)) {
|
|
300
|
+
seen.add(worker);
|
|
301
|
+
workers.push(worker);
|
|
302
|
+
}
|
|
303
|
+
let stdout = "";
|
|
304
|
+
let stderr = "";
|
|
305
|
+
for (const row of sequences)
|
|
306
|
+
if (row.workers.length === 0)
|
|
307
|
+
stdout += `unclassified\t${row.id}\n`;
|
|
308
|
+
for (const worker of workers) {
|
|
309
|
+
const file = findWorkerYaml(worker, roots);
|
|
310
|
+
let engine = "";
|
|
311
|
+
let model = "";
|
|
312
|
+
let effort = "";
|
|
313
|
+
let status = "needs-answer";
|
|
314
|
+
let note = "";
|
|
315
|
+
if (!file) {
|
|
316
|
+
note = "no worker.yaml";
|
|
317
|
+
const rootText = roots.join(" ");
|
|
318
|
+
const known = knownWorkers(roots).join(" ") || "none";
|
|
319
|
+
stderr += `pipeline-worker-table: unknown worker ${worker}: no worker.yaml under ${rootText}; known: ${known}\n`;
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
const claudeModel = executionField(file, "model");
|
|
323
|
+
const codexModel = executionField(file, "codex_model");
|
|
324
|
+
if (claudeModel && codexModel)
|
|
325
|
+
note = `ambiguous: claude:${claudeModel} codex:${codexModel}`;
|
|
326
|
+
else if (claudeModel) {
|
|
327
|
+
engine = "claude";
|
|
328
|
+
model = claudeModel;
|
|
329
|
+
status = "ok";
|
|
330
|
+
}
|
|
331
|
+
else if (codexModel) {
|
|
332
|
+
engine = "codex";
|
|
333
|
+
model = codexModel;
|
|
334
|
+
status = "ok";
|
|
335
|
+
const flags = executionField(file, "codex_flags");
|
|
336
|
+
effort = /.*--reasoning[= ]([A-Za-z]*).*/.exec(flags)?.[1] ?? "";
|
|
337
|
+
}
|
|
338
|
+
else
|
|
339
|
+
note = "no execution.model or execution.codex_model";
|
|
340
|
+
}
|
|
341
|
+
const storiesText = sequences.filter((row) => row.workers.includes(worker)).map((row) => row.id).join(" ");
|
|
342
|
+
stdout += `row\t${worker}\t${engine}\t${model}\t${effort}\t${status}\t${storiesText}\t${note}\n`;
|
|
343
|
+
}
|
|
344
|
+
for (const story of prd.userStories) {
|
|
345
|
+
const hint = story.model_hint !== null && story.model_hint !== undefined && story.model_hint !== "" ? stringValue(story.model_hint) : "";
|
|
346
|
+
if (!hint)
|
|
347
|
+
continue;
|
|
348
|
+
const id = storyId(story);
|
|
349
|
+
for (const worker of sequences.find((row) => row.id === id)?.workers ?? [])
|
|
350
|
+
stdout += `hint\t${worker}\t${id}\t${hint}\n`;
|
|
351
|
+
}
|
|
352
|
+
return { code: 0, stdout, stderr };
|
|
353
|
+
}
|
|
354
|
+
export function writePipelineWorkerTableResult(result) {
|
|
355
|
+
if (result.stdout)
|
|
356
|
+
process.stdout.write(result.stdout);
|
|
357
|
+
if (result.stderr)
|
|
358
|
+
process.stderr.write(result.stderr);
|
|
359
|
+
return result.code;
|
|
360
|
+
}
|
|
361
|
+
//# sourceMappingURL=core-pipeline-workers-native.js.map
|
package/dist/commands/core.js
CHANGED
|
@@ -50,6 +50,7 @@ import { registerCoreDeriveTriggerFactsCommand } from "./core-derive-trigger-fac
|
|
|
50
50
|
import { DERIVE_TRIGGER_FACTS_ASSET } from "./core-derive-trigger-facts-native.js";
|
|
51
51
|
import { registerCoreHookStateHelpers } from "./core-hook-state-helpers-native.js";
|
|
52
52
|
import { registerCoreHookBatchCommand } from "./core-hook-batch-command.js";
|
|
53
|
+
import { registerCorePipelineWorkerTableCommand } from "./core-pipeline-workers-command.js";
|
|
53
54
|
import { resolveLiveRoot } from "../utils/hq-roots.js";
|
|
54
55
|
import { interpreterFor, runBundledScript } from "../utils/run-bundled-script.js";
|
|
55
56
|
import { CompanyIndexUserError, renderIndexTarget } from "../lib/index-render/index.js";
|
|
@@ -218,6 +219,12 @@ export const REBUILD_INDEX_TARGETS = [
|
|
|
218
219
|
* would make the forwarders harder to audit against this table.
|
|
219
220
|
*/
|
|
220
221
|
export const SCAFFOLD_COMMANDS = [
|
|
222
|
+
{
|
|
223
|
+
name: "hq-task",
|
|
224
|
+
asset: "core/scripts/hq-task.sh",
|
|
225
|
+
root: "live",
|
|
226
|
+
summary: "List and update tasks on personal or company boards",
|
|
227
|
+
},
|
|
221
228
|
// Session/harness plumbing relocated out of the HQ tree so a single published
|
|
222
229
|
// CLI owns it. Both are `root: "live"`: they read and write the live HQ tree
|
|
223
230
|
// (workspace/sessions, companies/, workspace/orchestrator/hook-state), so the
|
|
@@ -243,6 +250,7 @@ export const SCAFFOLD_COMMANDS = [
|
|
|
243
250
|
* direct `hq core` command.
|
|
244
251
|
*/
|
|
245
252
|
export const SCAFFOLD_ONLY_ASSETS = [
|
|
253
|
+
{ asset: "core/scripts/hq-task.mjs", root: "live", summary: "Sibling implementation used by the hq-task shell rollback companion" },
|
|
246
254
|
{ asset: "core/scripts/session-journal.sh", root: "live", summary: "Rollback companion for native session journal" },
|
|
247
255
|
{ asset: "core/scripts/session-project.sh", root: "live", summary: "Rollback companion for native session project" },
|
|
248
256
|
{ asset: "core/scripts/session-title.sh", root: "live", summary: "Rollback companion for native session title" },
|
|
@@ -255,6 +263,7 @@ export const SCAFFOLD_ONLY_ASSETS = [
|
|
|
255
263
|
{ asset: "core/scripts/hq-job-run.sh", root: "live", summary: "Scheduled jobs runner used by generated systemd units" },
|
|
256
264
|
{ asset: "core/scripts/hq-job-notify.sh", root: "live", summary: "Scheduled jobs failure notification helper" },
|
|
257
265
|
{ asset: "core/scripts/hq-job-remediate.sh", root: "live", summary: "Scheduled jobs bounded remediation helper" },
|
|
266
|
+
{ asset: "core/scripts/pipeline-worker-table.sh", root: "live", summary: "Rollback companion for native pipeline worker-table" },
|
|
258
267
|
{ asset: "core/scripts/hq-dm-bind.sh", root: "live", summary: "Rollback shell for native dm-bind" },
|
|
259
268
|
{ asset: "core/scripts/work-mesh-live-bind-trusted.sh", root: "live", summary: "Rollback shell for native work-mesh-bind-trusted" },
|
|
260
269
|
{ asset: "core/scripts/share-suggestion-state.sh", root: "live", summary: "Rollback shell for native share-suggestion-state" },
|
|
@@ -295,16 +304,8 @@ export const SCAFFOLD_ONLY_ASSETS = [
|
|
|
295
304
|
{ asset: "core/scripts/hq-delegate-send.sh", root: "live", summary: "Rollback shell for native delegate send" },
|
|
296
305
|
{ asset: "core/scripts/hq-delegate-verify.sh", root: "live", summary: "Rollback shell for native delegate verify" },
|
|
297
306
|
{ asset: "core/scripts/lib/secret-patterns.sh", root: "live", summary: "Secret-pattern library sourced by the delegate rollback shells" },
|
|
298
|
-
//
|
|
299
|
-
|
|
300
|
-
{ asset: "core/scripts/ontology-audience-grant.sh", root: "live", summary: "Rollback companion for native ontology audience grant" },
|
|
301
|
-
{ asset: "core/scripts/ontology-brief.mjs", root: "live", summary: "Rollback companion for native ontology brief" },
|
|
302
|
-
{ asset: "core/scripts/ontology-candidate.sh", root: "live", summary: "Rollback companion for native ontology candidate" },
|
|
303
|
-
{ asset: "core/scripts/ontology-garden.mjs", root: "live", summary: "Rollback companion for native ontology garden" },
|
|
304
|
-
{ asset: "core/scripts/ontology-source-worklist.sh", root: "live", summary: "Rollback companion for native ontology source worklist" },
|
|
305
|
-
{ asset: "core/scripts/source-yaml-validate.sh", root: "cwd", summary: "Rollback companion for native source YAML validation" },
|
|
306
|
-
// Shared by native knowledge-prefs and the ontology candidate shell fallback.
|
|
307
|
-
{ asset: "core/scripts/knowledge-prefs.sh", root: "live", summary: "Sibling dependency for ontology candidate rollback companion" },
|
|
307
|
+
// Native knowledge-prefs retains its own shell companion until its family PR.
|
|
308
|
+
{ asset: "core/scripts/knowledge-prefs.sh", root: "live", summary: "Rollback companion for native knowledge-prefs" },
|
|
308
309
|
// Rollback companions for the default-off native knowledge and policy routes.
|
|
309
310
|
// policy-retire also runs the scaffold validator in-process when native, but
|
|
310
311
|
// its bundled shell fallback needs both helper assets at these exact paths.
|
|
@@ -412,6 +413,7 @@ export function registerCoreCommands(program) {
|
|
|
412
413
|
registerCoreKnowledgePrefsCommand(core);
|
|
413
414
|
registerCorePolicyCommand(core);
|
|
414
415
|
registerCoreJobsNativeCommand(core);
|
|
416
|
+
registerCorePipelineWorkerTableCommand(core);
|
|
415
417
|
registerCoreResolveCompanyNativeCommand(core);
|
|
416
418
|
registerCoreAuditLogNative(core);
|
|
417
419
|
registerCoreGitPackExtensionCheckNative(core);
|
package/dist/commands/lanes.js
CHANGED
|
@@ -73,6 +73,7 @@ import { createLaneMesh, emitLaneDecisionEvents, emitLaneTerminalStateEvent, lan
|
|
|
73
73
|
import { sessionIdFromEnv } from "../lib/mesh/live/session-identity.js";
|
|
74
74
|
import { listLaneReviews } from "../lib/lanes/reviews.js";
|
|
75
75
|
import { effectiveAccountHome } from "../lib/lanes/account-home.js";
|
|
76
|
+
import { remoteControlSelectionConfig, remoteControlSessionHome } from "../lib/lanes/remote-control-lane.js";
|
|
76
77
|
import { AccountResolutionError, compareLaneQueueOrder, LANE_PROVIDERS, LANE_STATES, QUESTION_STATUSES, SENIOR_KINDS, accountStatus, appendCappedHistory, canonicalizeHome, createLane, decideAdmission, defaultLivenessDeps, defaultSystemdUserDir, defaultThrottleDeps, installReconcileTimer, providerPathEnv, probeDrainHookRegistration, isLanesError, isLaneVersionConflict, isTerminalLaneState, isFinishedResumableHireChild, childrenHoldingHireGrantCarve, laneHoldsHireGrantCarve, assignedChildHireGrants, hireGrantCapacityError, planHireGrantBackfill, terminalChildResumeHireGrantError, withHireGrantAdmission, laneStoryLedgerEnabled, syncLaneToProjectStoryLedger, removeLaneFromProjectStoryLedger, validateProjectStories, buildLaneStatusSnapshot, formatLaneStatusHuman, listLaneIds, listQuestionIds, loadLanesConfig, laneRunsHere, MAX_LANE_DEPTH, MONITOR_ERROR_EXIT_CODE, readLiveMonitorEvidence, resolveMachineIdentity as lookupMachineIdentity, loadLivenessIndex, parseLaneProvider, parseLaneRecord, parseLaneState, parseQuestionRecord, parseSeniorKind, readAllLanes, readLane, sanitizeHostComponent, retryUpdateLane, syncLaneRecordFromOwner, readQuestion, resolveAccount, resolveLaneWorker, ignoredWorkerProfileFields, loadWorkerProfile, loadWorkerProfileWithRegistry, registryWorkerRecord, runReconcilePass, DEAD_HOST_MS, sessionLiveness, tryReadLane, uninstallReconcileTimer, updateLane, updateQuestion, dropboxDir, laneDeliveryMechanism, hasActiveAdmissionReservation, hasActivePendingResume, hasRecordedSessionId, hasEarlierEligibleQueuedLane, laneAdmissionCapReason, providerSupportsSessionResume, providerSessionPortable, isFailedQuotaKeepAliveLane, sessionNotPortableMessage, withLaneAdmissionLock, withAllLaneAdmissionLocks, readWorkerRunResult, resumeCommandRefusal, withLaneHireLocks, raiseLaneQuestion, sha256Text, } from "../lib/lanes/index.js";
|
|
77
78
|
import { laneStories, storyWarning, } from "../lib/lanes/stories.js";
|
|
78
79
|
import { resolveLaneKeepAlive } from "../lib/lanes/profile.js";
|
|
@@ -3350,6 +3351,9 @@ async function runCreate(ctx, opts) {
|
|
|
3350
3351
|
if (opts.loop === true && (opts.outpost?.trim() || requestedPlacement?.kind === "outpost" || defaultPlacement?.kind === "outpost")) {
|
|
3351
3352
|
throw new LanesError("loop_lane_outpost_unsupported", "Loop lanes currently require the local host", { outpost_id: opts.outpost?.trim() ?? (requestedPlacement?.kind === "outpost" ? requestedPlacement.id : defaultPlacement?.kind === "outpost" ? defaultPlacement.id : undefined) });
|
|
3352
3353
|
}
|
|
3354
|
+
if (opts.remoteControl === true && (opts.outpost?.trim() || requestedPlacement?.kind === "outpost" || defaultPlacement?.kind === "outpost")) {
|
|
3355
|
+
throw new LanesError("remote_control_outpost_unsupported", "--remote-control requires the local daemon account pools", {});
|
|
3356
|
+
}
|
|
3353
3357
|
let placementCompanyResolved = false;
|
|
3354
3358
|
let placementCompany;
|
|
3355
3359
|
let placementGate;
|
|
@@ -3391,8 +3395,11 @@ async function runCreate(ctx, opts) {
|
|
|
3391
3395
|
const profile = resolvedWorker?.profile;
|
|
3392
3396
|
const provider = explicitProvider ??
|
|
3393
3397
|
profile?.meta.provider ??
|
|
3394
|
-
parseLaneProvider("codex");
|
|
3395
|
-
|
|
3398
|
+
parseLaneProvider(opts.remoteControl === true ? "claude" : "codex");
|
|
3399
|
+
if (opts.remoteControl === true && provider !== "claude") {
|
|
3400
|
+
throw new LanesError("remote_control_requires_claude", "--remote-control requires a Claude lane", { provider });
|
|
3401
|
+
}
|
|
3402
|
+
const continuityEnabled = opts.remoteControl !== true && opts.worker !== undefined && await readLanesRegistryFlagFailOpen(LANE_WORKER_CONTINUITY_FLAG, { company: company ?? meshCompany, env: ctx.env, defaultValue: true });
|
|
3396
3403
|
let workerContinuity = continuityEnabled
|
|
3397
3404
|
? findWorkerContinuityLane(loadAllLanes(hqRoot), {
|
|
3398
3405
|
senior,
|
|
@@ -3424,7 +3431,10 @@ async function runCreate(ctx, opts) {
|
|
|
3424
3431
|
path: briefAbs,
|
|
3425
3432
|
});
|
|
3426
3433
|
}
|
|
3427
|
-
const
|
|
3434
|
+
const loadedConfig = loadLanesConfig(hqRoot);
|
|
3435
|
+
const config = opts.remoteControl === true
|
|
3436
|
+
? remoteControlSelectionConfig(loadedConfig, ctx.env)
|
|
3437
|
+
: loadedConfig;
|
|
3428
3438
|
let outpostId = opts.outpost?.trim() || undefined;
|
|
3429
3439
|
let placementHostOverride;
|
|
3430
3440
|
let placementBalanceHostOverride;
|
|
@@ -3458,7 +3468,9 @@ async function runCreate(ctx, opts) {
|
|
|
3458
3468
|
inheritedPlacement = callerLane.placement_default;
|
|
3459
3469
|
}
|
|
3460
3470
|
}
|
|
3461
|
-
const placementToApply =
|
|
3471
|
+
const placementToApply = opts.remoteControl === true
|
|
3472
|
+
? { kind: "local" }
|
|
3473
|
+
: requestedPlacement ?? inheritedPlacement;
|
|
3462
3474
|
const outpostTargets = new Set();
|
|
3463
3475
|
if (placementToApply?.kind === "outpost")
|
|
3464
3476
|
outpostTargets.add(placementToApply.id);
|
|
@@ -3785,6 +3797,9 @@ async function runCreate(ctx, opts) {
|
|
|
3785
3797
|
if (opts.loop === true && host.kind === "outpost") {
|
|
3786
3798
|
throw new LanesError("loop_lane_outpost_unsupported", "Loop lanes currently require the local host", { outpost_id: host.id });
|
|
3787
3799
|
}
|
|
3800
|
+
if (opts.remoteControl === true && host.kind === "outpost") {
|
|
3801
|
+
throw new LanesError("remote_control_outpost_unsupported", "--remote-control requires the local daemon account pools", { outpost_id: host.id });
|
|
3802
|
+
}
|
|
3788
3803
|
// Repeat the busy check while holding provider admission serialization so
|
|
3789
3804
|
// two concurrent creates cannot both claim one worker's provider thread.
|
|
3790
3805
|
if (continuityEnabled) {
|
|
@@ -3947,6 +3962,7 @@ async function runCreate(ctx, opts) {
|
|
|
3947
3962
|
...(opts.loop === true ? { loop_mode: true } : {}),
|
|
3948
3963
|
...(keepAlive ? { keep_alive_opted_in_at: createdAt } : {}),
|
|
3949
3964
|
provider,
|
|
3965
|
+
...(opts.remoteControl === true ? { remote_control: true } : {}),
|
|
3950
3966
|
...(workerContinuity.resumable && workerContinuity.previous
|
|
3951
3967
|
? {
|
|
3952
3968
|
provider_session_id: workerContinuity.previous.provider_session_id,
|
|
@@ -7208,13 +7224,18 @@ async function prepareAccountMove(ctx, lane, input) {
|
|
|
7208
7224
|
throw new LanesError("no_provider_session", `Lane ${laneId} has no recorded provider session id, so there is no session to move`, { lane_id: laneId, provider: lane.provider });
|
|
7209
7225
|
}
|
|
7210
7226
|
const sessionId = lane.provider_session_id;
|
|
7211
|
-
const
|
|
7227
|
+
const loadedConfig = loadLanesConfig(hqRoot);
|
|
7228
|
+
const config = lane.remote_control === true
|
|
7229
|
+
? remoteControlSelectionConfig(loadedConfig, ctx.env)
|
|
7230
|
+
: loadedConfig;
|
|
7212
7231
|
const providerCfg = config.providers[lane.provider];
|
|
7213
7232
|
const targetCfg = providerCfg.accounts[target];
|
|
7214
7233
|
if (targetCfg === undefined) {
|
|
7215
7234
|
throw new AccountResolutionError("unknown_account", `Unknown account ${JSON.stringify(target)} for ${lane.provider}. Configured: ${Object.keys(providerCfg.accounts).join(", ") || "(none)"}`, { provider: lane.provider, account: target, enabled: Object.keys(providerCfg.accounts).filter((id) => providerCfg.accounts[id]?.enabled) });
|
|
7216
7235
|
}
|
|
7217
|
-
const targetHome =
|
|
7236
|
+
const targetHome = lane.remote_control === true
|
|
7237
|
+
? remoteControlSessionHome(config, ctx.hostname())
|
|
7238
|
+
: effectiveAccountHome(targetCfg, ctx.hostname()).home;
|
|
7218
7239
|
if (!targetCfg.enabled) {
|
|
7219
7240
|
throw new AccountResolutionError("account_disabled", `Account ${JSON.stringify(target)} is disabled${targetCfg.disabled_reason ? `: ${targetCfg.disabled_reason}` : ""}`, { provider: lane.provider, account: target, reason: targetCfg.disabled_reason ?? "" });
|
|
7220
7241
|
}
|
|
@@ -7375,7 +7396,10 @@ async function selectAutomaticClaudeResumeAccount(ctx, lane, workerProfile, opti
|
|
|
7375
7396
|
if (lane.provider !== "claude")
|
|
7376
7397
|
return undefined;
|
|
7377
7398
|
const hqRoot = ctx.hqRoot();
|
|
7378
|
-
const
|
|
7399
|
+
const loadedConfig = loadLanesConfig(hqRoot);
|
|
7400
|
+
const config = lane.remote_control === true
|
|
7401
|
+
? remoteControlSelectionConfig(loadedConfig, ctx.env)
|
|
7402
|
+
: loadedConfig;
|
|
7379
7403
|
const currentConfig = lane.account === undefined
|
|
7380
7404
|
? undefined
|
|
7381
7405
|
: config.providers.claude.accounts[lane.account];
|
|
@@ -9646,6 +9670,7 @@ export function registerLanesCommand(parent, deps = {}) {
|
|
|
9646
9670
|
.option("--fresh-thread", "Skip worker continuity and start a cold provider session")
|
|
9647
9671
|
.option("--loop", "Keep one worker lane registered to process queued phase envelopes")
|
|
9648
9672
|
.option("--provider <name>", "claude | codex | grok")
|
|
9673
|
+
.option("--remote-control", "Open a Claude lane in Remote Control using account a for login and the selected account for inference")
|
|
9649
9674
|
.option("--account <id>", "Provider account id")
|
|
9650
9675
|
.option("--model <id>", "Model id for the provider CLI")
|
|
9651
9676
|
.option("--effort <level>", "Effort level; claude: low|medium|high|xhigh|max, codex: minimal|low|medium|high|xhigh|max|ultra, grok: low|medium|high|xhigh")
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* hq logout —
|
|
2
|
+
* hq logout — ends the Cognito session on the server, then clears the cached
|
|
3
|
+
* session on this device.
|
|
4
|
+
*
|
|
5
|
+
* The refresh token is revoked first so a copied token file stops working.
|
|
6
|
+
* Local tokens are always deleted, even when the server can't be reached.
|
|
3
7
|
*/
|
|
4
8
|
import { Command } from 'commander';
|
|
9
|
+
export declare const REVOKE_FAILED_MESSAGE = "Signed out on this device. Couldn't reach HQ to end the session everywhere; it will expire on its own.";
|
|
5
10
|
export declare function registerLogoutCommand(program: Command): void;
|
|
6
11
|
//# sourceMappingURL=logout.d.ts.map
|
package/dist/commands/logout.js
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* hq logout —
|
|
2
|
+
* hq logout — ends the Cognito session on the server, then clears the cached
|
|
3
|
+
* session on this device.
|
|
4
|
+
*
|
|
5
|
+
* The refresh token is revoked first so a copied token file stops working.
|
|
6
|
+
* Local tokens are always deleted, even when the server can't be reached.
|
|
3
7
|
*/
|
|
4
8
|
import chalk from 'chalk';
|
|
5
9
|
import { clearCachedTokens, loadCachedTokens } from '@indigoai-us/hq-cloud';
|
|
6
10
|
import { retainUsageAttributionForPerson } from '../lib/work-context/config.js';
|
|
7
11
|
import { workContextRoot } from '../lib/work-context/paths.js';
|
|
12
|
+
import { DEFAULT_COGNITO } from '../utils/cognito-config.js';
|
|
13
|
+
import { revokeRefreshToken } from '../utils/cognito-revoke.js';
|
|
14
|
+
export const REVOKE_FAILED_MESSAGE = "Signed out on this device. Couldn't reach HQ to end the session everywhere; it will expire on its own.";
|
|
8
15
|
export function registerLogoutCommand(program) {
|
|
9
16
|
program
|
|
10
17
|
.command('logout')
|
|
@@ -26,8 +33,17 @@ export function registerLogoutCommand(program) {
|
|
|
26
33
|
console.log(chalk.yellow('No cached HQ session'));
|
|
27
34
|
return;
|
|
28
35
|
}
|
|
36
|
+
// Machine-minted sessions carry no refresh token; nothing to revoke.
|
|
37
|
+
const revoked = existing.refreshToken
|
|
38
|
+
? (await revokeRefreshToken(existing.refreshToken, DEFAULT_COGNITO)).ok
|
|
39
|
+
: true;
|
|
29
40
|
clearCachedTokens();
|
|
30
|
-
|
|
41
|
+
if (revoked) {
|
|
42
|
+
console.log(chalk.green('Logged out successfully'));
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
console.log(chalk.yellow(REVOKE_FAILED_MESSAGE));
|
|
46
|
+
}
|
|
31
47
|
}
|
|
32
48
|
catch (error) {
|
|
33
49
|
console.error(chalk.red('Logout failed:'), error instanceof Error ? error.message : 'Unknown error');
|
|
@@ -12,6 +12,11 @@
|
|
|
12
12
|
* `vaultApiFetch` satisfies hq-cloud's `OutpostTransport` structurally — the
|
|
13
13
|
* option bags match field-for-field — so it is passed straight through with no
|
|
14
14
|
* adapter shim.
|
|
15
|
+
*
|
|
16
|
+
* Company Outposts (`--company`, `jobs register`) are part of that same command
|
|
17
|
+
* tree. This adapter does not grow a second copy. The accepted revision, the
|
|
18
|
+
* catalog price, and the company payer are printed by hq-cloud. The /schedule
|
|
19
|
+
* skill text is a follow-up and is not changed here.
|
|
15
20
|
*/
|
|
16
21
|
import type { Command } from "commander";
|
|
17
22
|
import type { ReplicaSyncDependencies } from "@indigoai-us/hq-cloud/outposts/node";
|
|
@@ -12,6 +12,11 @@
|
|
|
12
12
|
* `vaultApiFetch` satisfies hq-cloud's `OutpostTransport` structurally — the
|
|
13
13
|
* option bags match field-for-field — so it is passed straight through with no
|
|
14
14
|
* adapter shim.
|
|
15
|
+
*
|
|
16
|
+
* Company Outposts (`--company`, `jobs register`) are part of that same command
|
|
17
|
+
* tree. This adapter does not grow a second copy. The accepted revision, the
|
|
18
|
+
* catalog price, and the company payer are printed by hq-cloud. The /schedule
|
|
19
|
+
* skill text is a follow-up and is not changed here.
|
|
15
20
|
*/
|
|
16
21
|
import { loadCachedTokens } from "@indigoai-us/hq-cloud";
|
|
17
22
|
import { registerOutpostsCommand as registerOutpostsCommandFromCloud } from "@indigoai-us/hq-cloud/outposts/cli";
|
package/dist/fast-scaffold.d.ts
CHANGED
|
@@ -49,8 +49,21 @@ export type FastScaffoldScript = {
|
|
|
49
49
|
/** Whether the bundled fast-route module closure needs Node's network timeout patch. */
|
|
50
50
|
requiresNodeNetworkCompatibility?: boolean;
|
|
51
51
|
};
|
|
52
|
+
/**
|
|
53
|
+
* CLI core-command registry metadata for MSYS path operands. The exact rules
|
|
54
|
+
* were read from hq-core-staging core/scripts/cli-hosted.yaml and
|
|
55
|
+
* lib/forwarder.sh at d8c3e3f0f6f50db72b81970f7868f7beb4c88d0c.
|
|
56
|
+
* Kept in this already-loaded registry module so hook fast-route graphs stay
|
|
57
|
+
* unchanged.
|
|
58
|
+
*/
|
|
59
|
+
export type CorePathOperandRule = "all-nonoptions" | "first-nonoption" | `position:${number}` | `options:${string}` | `comma:${string}`;
|
|
60
|
+
export declare const CORE_COMMAND_PATH_OPERANDS: Readonly<Record<string, CorePathOperandRule>>;
|
|
61
|
+
/** True when argv names a command that declares path operands. */
|
|
62
|
+
export declare function coreCommandHasPathOperands(argv: readonly string[]): boolean;
|
|
63
|
+
/** Apply the manifest's operand rule to a copied process.argv vector. */
|
|
64
|
+
export declare function normalizeCorePathOperands(argv: readonly string[], platform?: NodeJS.Platform, convertPath?: (value: string) => string): string[];
|
|
52
65
|
/** Family kill switches whose owner-approved default is enabled. */
|
|
53
|
-
export declare const CORE_NATIVE_DEFAULT_ON_FAMILY_FLAGS: readonly ["core-native-session", "core-native-hook-helpers", "core-native-policy", "core-native-utilities", "core-native-jobs", "core-native-
|
|
66
|
+
export declare const CORE_NATIVE_DEFAULT_ON_FAMILY_FLAGS: readonly ["core-native-session", "core-native-hook-helpers", "core-native-policy", "core-native-utilities", "core-native-jobs", "core-native-handoff", "core-native-bucket-c", "core-native-delegate"];
|
|
54
67
|
export declare function isDefaultOnFastFamily(flagKey: string): boolean;
|
|
55
68
|
/** The relocated scripts that qualify for the entrypoint fast path. */
|
|
56
69
|
export declare const FAST_SCAFFOLD_SCRIPTS: readonly FastScaffoldScript[];
|