@indigoai-us/hq-cli 5.290.0 → 5.292.0
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 +66 -3
- package/assets/scaffold/core/scripts/handoff-finalize.sh +18 -0
- package/assets/scaffold/core/scripts/handoff-post.sh +71 -6
- package/assets/scaffold/core/scripts/hook-lib.sh +345 -50
- package/assets/scaffold/core/scripts/hq-delegate-bundle.sh +307 -0
- package/assets/scaffold/core/scripts/hq-delegate-grant.sh +226 -0
- package/assets/scaffold/core/scripts/hq-delegate-publish.sh +91 -0
- package/assets/scaffold/core/scripts/hq-delegate-send.sh +259 -0
- package/assets/scaffold/core/scripts/hq-delegate-verify.sh +213 -0
- package/assets/scaffold/core/scripts/hq-job-notify.sh +389 -0
- package/assets/scaffold/core/scripts/hq-job-probe.sh +835 -0
- package/assets/scaffold/core/scripts/hq-job-remediate.sh +359 -0
- package/assets/scaffold/core/scripts/hq-job-run.sh +1025 -0
- package/assets/scaffold/core/scripts/hq-session.sh +182 -21
- package/assets/scaffold/core/scripts/jobs-schedule-parse.sh +310 -0
- package/assets/scaffold/core/scripts/jobs-validate.sh +451 -0
- package/assets/scaffold/core/scripts/lib/session-id.sh +3 -2
- package/assets/scaffold/core/scripts/lib/work-mesh-enqueue.sh +2 -1
- package/assets/scaffold/core/scripts/outpost-jobs-reconcile.sh +830 -0
- package/dist/command-catalog.generated.d.ts +165 -1
- package/dist/command-catalog.generated.js +205 -1
- package/dist/commands/cloud.d.ts +26 -14
- package/dist/commands/cloud.js +207 -56
- package/dist/commands/core-delegate-w1b.d.ts +8 -0
- package/dist/commands/core-delegate-w1b.js +1049 -0
- package/dist/commands/core-delegate.d.ts +28 -0
- package/dist/commands/core-delegate.js +15 -13
- package/dist/commands/core-handoff.js +152 -23
- package/dist/commands/core-jobs-native.d.ts +23 -0
- package/dist/commands/core-jobs-native.js +1331 -0
- package/dist/commands/core-native-jobs-flag.d.ts +4 -0
- package/dist/commands/core-native-jobs-flag.js +7 -0
- package/dist/commands/core-ontology.d.ts +22 -4
- package/dist/commands/core-ontology.js +90 -8
- package/dist/commands/core-policy.js +24 -5
- package/dist/commands/core.js +17 -0
- package/dist/commands/integrations-core.js +7 -0
- package/dist/commands/lanes.js +111 -35
- package/dist/commands/members.js +5 -0
- package/dist/commands/mesh.js +46 -25
- package/dist/commands/personal-targeted-push-flag.d.ts +4 -0
- package/dist/commands/personal-targeted-push-flag.js +7 -0
- package/dist/commands/personal-targeted-push-paths.d.ts +3 -0
- package/dist/commands/personal-targeted-push-paths.js +67 -0
- package/dist/commands/secrets.js +9 -0
- package/dist/commands/skill.d.ts +3 -0
- package/dist/commands/skill.js +28 -0
- package/dist/fast-core.js +1 -0
- package/dist/lib/billing/free-month.d.ts +18 -0
- package/dist/lib/billing/free-month.js +31 -0
- package/dist/lib/billing/plan-limit-refusals.d.ts +69 -0
- package/dist/lib/billing/plan-limit-refusals.js +187 -0
- package/dist/lib/billing/plan-lock.d.ts +2 -6
- package/dist/lib/billing/plan-lock.js +5 -16
- package/dist/lib/checkpoint-ontology/drop-aliases.d.ts +41 -0
- package/dist/lib/checkpoint-ontology/drop-aliases.js +76 -0
- package/dist/lib/checkpoint-ontology/entity-file.d.ts +6 -3
- package/dist/lib/checkpoint-ontology/entity-file.js +12 -6
- package/dist/lib/checkpoint-ontology/merge-copies.d.ts +27 -8
- package/dist/lib/checkpoint-ontology/merge-copies.js +64 -97
- package/dist/lib/core-utils/secret-patterns.d.ts +3 -1
- package/dist/lib/core-utils/secret-patterns.js +3 -2
- package/dist/lib/daemon/flag.d.ts +2 -0
- package/dist/lib/daemon/flag.js +2 -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/autopilot.d.ts +69 -0
- package/dist/lib/lanes/autopilot.js +418 -0
- package/dist/lib/lanes/mesh-events.js +2 -0
- package/dist/lib/lanes/store.js +11 -6
- package/dist/lib/mesh/api.js +4 -0
- package/dist/lib/mesh/live/daemon/state.d.ts +7 -1
- package/dist/lib/mesh/live/daemon/state.js +34 -2
- package/dist/main.js +23 -12
- package/dist/utils/plan-gate-error.d.ts +24 -0
- package/dist/utils/plan-gate-error.js +219 -9
- package/dist/utils/scaffold-sibling-references.js +4 -4
- package/dist/utils/self-update.d.ts +6 -0
- package/dist/utils/self-update.js +28 -6
- package/dist/utils/staged-install.d.ts +34 -0
- package/dist/utils/staged-install.js +296 -0
- package/package.json +2 -2
- package/scripts/mesh-daemon-if-stale.mjs +4 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Default-off rollout gate for the native scheduled-jobs helpers. */
|
|
2
|
+
export const CORE_NATIVE_JOBS_FLAG = "core-native-jobs";
|
|
3
|
+
export async function coreNativeJobsEnabled() {
|
|
4
|
+
const { resolveProcessFlagGate } = await import("../lib/flag-registry.js");
|
|
5
|
+
return resolveProcessFlagGate(CORE_NATIVE_JOBS_FLAG, { fallback: false }, () => false);
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=core-native-jobs-flag.js.map
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { Command } from "commander";
|
|
11
11
|
import { type FlagReader } from "../lib/checkpoint-ontology/plan.js";
|
|
12
|
-
import { type
|
|
12
|
+
import { type AliasDrop } from "../lib/checkpoint-ontology/drop-aliases.js";
|
|
13
|
+
import { type DeleteVaultKey, type ListedPair, type MergeCopiesReport } from "../lib/checkpoint-ontology/merge-copies.js";
|
|
13
14
|
export declare const ONTOLOGY_PAYLOAD_FILE = "payload.json";
|
|
14
15
|
/** The hq-flags read, scoped to the payload company. */
|
|
15
16
|
export declare const registryFlagReader: FlagReader;
|
|
@@ -31,12 +32,15 @@ export interface MergeCopiesCommandOptions {
|
|
|
31
32
|
apply?: boolean;
|
|
32
33
|
delete?: boolean;
|
|
33
34
|
variants?: boolean;
|
|
35
|
+
/** Merge exactly the `--only` pairs, whatever their names (flag merge-listed-pairs). */
|
|
36
|
+
listed?: boolean;
|
|
34
37
|
limit?: number;
|
|
35
38
|
only?: string;
|
|
36
39
|
/** Also re-point refs and links held today (flag merge-rewrite-refs). */
|
|
37
40
|
rewriteRefs?: boolean;
|
|
38
41
|
json?: boolean;
|
|
39
42
|
}
|
|
43
|
+
export declare function mergeCopiesSummary(report: MergeCopiesReport): string;
|
|
40
44
|
/** Reads a `--only` file: a JSON array of `{ copy, original }` company-relative paths. */
|
|
41
45
|
export declare function readListedPairs(file: string): ListedPair[];
|
|
42
46
|
/**
|
|
@@ -47,10 +51,24 @@ export declare function readListedPairs(file: string): ListedPair[];
|
|
|
47
51
|
* With `--variants`, merges two-file duplicate groups whose names differ only
|
|
48
52
|
* in case, spacing or punctuation instead, gated by
|
|
49
53
|
* `checkpoint.ontology.merge-variants`. `--rewrite-refs` (flag
|
|
50
|
-
* `checkpoint.ontology.merge-rewrite-refs`) also re-points
|
|
51
|
-
*
|
|
52
|
-
*
|
|
54
|
+
* `checkpoint.ontology.merge-rewrite-refs`) also re-points invoice and
|
|
55
|
+
* knowledge links to the survivor and carries external ids and web domains;
|
|
56
|
+
* pairs named by signals or sources stay held. `--listed` (flag
|
|
57
|
+
* `checkpoint.ontology.merge-listed-pairs`) merges exactly the `--only` pairs,
|
|
58
|
+
* which a person reviewed, instead of the pairs a finder produces.
|
|
53
59
|
*/
|
|
54
60
|
export declare function runOntologyMergeCopies(hqRoot: string, slug: string | undefined, options: MergeCopiesCommandOptions, deps?: OntologyCommandDeps): Promise<string>;
|
|
61
|
+
/** Reads a `drop-aliases --only` file: a JSON array of `{ entity, alias }`. */
|
|
62
|
+
export declare function readAliasDrops(file: string): AliasDrop[];
|
|
63
|
+
/**
|
|
64
|
+
* `hq core ontology drop-aliases`: remove the reviewed `{ entity, alias }`
|
|
65
|
+
* pairs in `--only` from entity files (`--apply`); without it only reports.
|
|
66
|
+
* Gated by `checkpoint.ontology.drop-aliases` and `checkpoint.ontology.multi-writer`.
|
|
67
|
+
*/
|
|
68
|
+
export declare function runOntologyDropAliases(hqRoot: string, slug: string | undefined, options: {
|
|
69
|
+
only?: string;
|
|
70
|
+
apply?: boolean;
|
|
71
|
+
json?: boolean;
|
|
72
|
+
}, deps?: OntologyCommandDeps): Promise<string>;
|
|
55
73
|
export declare function registerCoreOntologyCommand(core: Command): void;
|
|
56
74
|
//# sourceMappingURL=core-ontology.d.ts.map
|
|
@@ -17,7 +17,8 @@ import { applyOntologyRun, ApplyError } from "../lib/checkpoint-ontology/apply.j
|
|
|
17
17
|
import { resolveOntologyCompany, WriteBoundsError } from "../lib/checkpoint-ontology/company.js";
|
|
18
18
|
import { findDuplicateGroups, summarizeMergeBack, } from "../lib/checkpoint-ontology/multi-writer.js";
|
|
19
19
|
import { buildEntityIndex } from "../lib/checkpoint-ontology/tree.js";
|
|
20
|
-
import {
|
|
20
|
+
import { DROP_ALIASES_FLAG, dropAliases } from "../lib/checkpoint-ontology/drop-aliases.js";
|
|
21
|
+
import { MERGE_COPIES_FLAG, MERGE_LISTED_PAIRS_FLAG, MERGE_REWRITE_REFS_FLAG, MERGE_VARIANTS_FLAG, mergeCopies, } from "../lib/checkpoint-ontology/merge-copies.js";
|
|
21
22
|
import { callDeleteEndpoint } from "./files.js";
|
|
22
23
|
import { resolveVaultCredential } from "../utils/resolve-vault-credential.js";
|
|
23
24
|
import { registerCoreOntologyNativeCommands } from "./core-ontology-native.js";
|
|
@@ -142,7 +143,7 @@ async function companyVaultDeleter(companyUid) {
|
|
|
142
143
|
return (await callDeleteEndpoint({ token, companyUid, prefix: key, dryRun: false })).deleted;
|
|
143
144
|
};
|
|
144
145
|
}
|
|
145
|
-
function mergeCopiesSummary(report) {
|
|
146
|
+
export function mergeCopiesSummary(report) {
|
|
146
147
|
const held = report.plans.filter((plan) => plan.held).length;
|
|
147
148
|
const lines = [
|
|
148
149
|
`ontology merge-copies (${report.mode.replace("_", " ")}) in ${report.company} (${report.pair_kind}): ${report.plans.length} of ${report.copies_found} copies, ${held} held, ${report.skipped.length} skipped`,
|
|
@@ -150,7 +151,7 @@ function mergeCopiesSummary(report) {
|
|
|
150
151
|
` duplicate groups ${report.duplicate_groups_before} -> ${report.duplicate_groups_after}; broken entity links ${report.broken_links_before} -> ${report.broken_links_after}`,
|
|
151
152
|
];
|
|
152
153
|
for (const plan of report.plans.filter((entry) => entry.held)) {
|
|
153
|
-
lines.push(` held ${plan.copy}: ${plan.held}${plan.held === "external_id_conflict" ? ` (${plan.detail})` : ""}`);
|
|
154
|
+
lines.push(` held ${plan.copy}: ${plan.held}${plan.held === "external_id_conflict" || plan.held === "server_owned_refs" ? ` (${plan.detail})` : ""}`);
|
|
154
155
|
}
|
|
155
156
|
for (const error of report.errors)
|
|
156
157
|
lines.push(` error ${error.path}: ${error.error}`);
|
|
@@ -216,9 +217,11 @@ function readResolution(copy, raw) {
|
|
|
216
217
|
* With `--variants`, merges two-file duplicate groups whose names differ only
|
|
217
218
|
* in case, spacing or punctuation instead, gated by
|
|
218
219
|
* `checkpoint.ontology.merge-variants`. `--rewrite-refs` (flag
|
|
219
|
-
* `checkpoint.ontology.merge-rewrite-refs`) also re-points
|
|
220
|
-
*
|
|
221
|
-
*
|
|
220
|
+
* `checkpoint.ontology.merge-rewrite-refs`) also re-points invoice and
|
|
221
|
+
* knowledge links to the survivor and carries external ids and web domains;
|
|
222
|
+
* pairs named by signals or sources stay held. `--listed` (flag
|
|
223
|
+
* `checkpoint.ontology.merge-listed-pairs`) merges exactly the `--only` pairs,
|
|
224
|
+
* which a person reviewed, instead of the pairs a finder produces.
|
|
222
225
|
*/
|
|
223
226
|
export async function runOntologyMergeCopies(hqRoot, slug, options, deps = {}) {
|
|
224
227
|
if (options.apply && options.delete) {
|
|
@@ -227,13 +230,20 @@ export async function runOntologyMergeCopies(hqRoot, slug, options, deps = {}) {
|
|
|
227
230
|
if (options.limit !== undefined && (!Number.isInteger(options.limit) || options.limit < 1)) {
|
|
228
231
|
throw new OntologyUsageError("ontology: --limit must be a positive whole number");
|
|
229
232
|
}
|
|
233
|
+
if (options.listed && options.variants) {
|
|
234
|
+
throw new OntologyUsageError("ontology: use either --listed or --variants");
|
|
235
|
+
}
|
|
236
|
+
if (options.listed && options.only === undefined) {
|
|
237
|
+
throw new OntologyUsageError("ontology: --listed needs --only <file>");
|
|
238
|
+
}
|
|
230
239
|
const only = options.only !== undefined ? readListedPairs(options.only) : undefined;
|
|
231
240
|
const resolution = resolveOntologyCompany(hqRoot, slug);
|
|
232
241
|
if (!resolution.ok)
|
|
233
242
|
throw new OntologyUsageError(`ontology: company rejected: ${resolution.reason}`);
|
|
234
243
|
const company = resolution.company;
|
|
235
244
|
const readFlag = deps.readFlag ?? registryFlagReader;
|
|
236
|
-
|
|
245
|
+
const kindFlag = options.listed ? MERGE_LISTED_PAIRS_FLAG : options.variants ? MERGE_VARIANTS_FLAG : MERGE_COPIES_FLAG;
|
|
246
|
+
for (const flag of [kindFlag, MULTI_WRITER_FLAG, ...(options.rewriteRefs ? [MERGE_REWRITE_REFS_FLAG] : [])]) {
|
|
237
247
|
if (!(await readFlag(flag, company))) {
|
|
238
248
|
return options.json ? JSON.stringify({ company: company.slug, skipped: `${flag} off` }) : `ontology merge-copies: skipped (${flag} is off)`;
|
|
239
249
|
}
|
|
@@ -251,6 +261,7 @@ export async function runOntologyMergeCopies(hqRoot, slug, options, deps = {}) {
|
|
|
251
261
|
mode,
|
|
252
262
|
now: deps.now?.() ?? new Date(),
|
|
253
263
|
...(options.variants ? { pairKind: "variants" } : {}),
|
|
264
|
+
...(options.listed ? { pairKind: "listed" } : {}),
|
|
254
265
|
...(options.limit !== undefined ? { limit: options.limit } : {}),
|
|
255
266
|
...(only ? { only } : {}),
|
|
256
267
|
...(options.rewriteRefs ? { rewriteRefs: true } : {}),
|
|
@@ -258,6 +269,59 @@ export async function runOntologyMergeCopies(hqRoot, slug, options, deps = {}) {
|
|
|
258
269
|
});
|
|
259
270
|
return options.json ? JSON.stringify(report, null, 2) : mergeCopiesSummary(report);
|
|
260
271
|
}
|
|
272
|
+
/** Reads a `drop-aliases --only` file: a JSON array of `{ entity, alias }`. */
|
|
273
|
+
export function readAliasDrops(file) {
|
|
274
|
+
let parsed;
|
|
275
|
+
try {
|
|
276
|
+
parsed = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
277
|
+
}
|
|
278
|
+
catch (error) {
|
|
279
|
+
throw new OntologyUsageError(`ontology: --only file ${file} is not readable JSON: ${error.message}`);
|
|
280
|
+
}
|
|
281
|
+
const usage = "ontology: --only must be a non-empty JSON array of { entity, alias } strings";
|
|
282
|
+
if (!Array.isArray(parsed) || parsed.length === 0)
|
|
283
|
+
throw new OntologyUsageError(usage);
|
|
284
|
+
return parsed.map((item) => {
|
|
285
|
+
const record = item;
|
|
286
|
+
if (!record || typeof record.entity !== "string" || typeof record.alias !== "string" || record.alias === "") {
|
|
287
|
+
throw new OntologyUsageError(usage);
|
|
288
|
+
}
|
|
289
|
+
return { entity: record.entity, alias: record.alias };
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* `hq core ontology drop-aliases`: remove the reviewed `{ entity, alias }`
|
|
294
|
+
* pairs in `--only` from entity files (`--apply`); without it only reports.
|
|
295
|
+
* Gated by `checkpoint.ontology.drop-aliases` and `checkpoint.ontology.multi-writer`.
|
|
296
|
+
*/
|
|
297
|
+
export async function runOntologyDropAliases(hqRoot, slug, options, deps = {}) {
|
|
298
|
+
if (options.only === undefined)
|
|
299
|
+
throw new OntologyUsageError("ontology: drop-aliases needs --only <file>");
|
|
300
|
+
const drops = readAliasDrops(options.only);
|
|
301
|
+
const resolution = resolveOntologyCompany(hqRoot, slug);
|
|
302
|
+
if (!resolution.ok)
|
|
303
|
+
throw new OntologyUsageError(`ontology: company rejected: ${resolution.reason}`);
|
|
304
|
+
const company = resolution.company;
|
|
305
|
+
const readFlag = deps.readFlag ?? registryFlagReader;
|
|
306
|
+
for (const flag of [DROP_ALIASES_FLAG, MULTI_WRITER_FLAG]) {
|
|
307
|
+
if (!(await readFlag(flag, company))) {
|
|
308
|
+
return options.json ? JSON.stringify({ company: company.slug, skipped: `${flag} off` }) : `ontology drop-aliases: skipped (${flag} is off)`;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
const report = dropAliases({ hqRoot, company, mode: options.apply ? "apply" : "dry_run", drops, now: deps.now?.() ?? new Date() });
|
|
312
|
+
if (options.json)
|
|
313
|
+
return JSON.stringify(report, null, 2);
|
|
314
|
+
const planned = Object.values(report.planned).reduce((sum, list) => sum + list.length, 0);
|
|
315
|
+
const lines = [
|
|
316
|
+
`ontology drop-aliases (${report.mode.replace("_", " ")}) in ${report.company}: ${planned} alias(es) on ${Object.keys(report.planned).length} entities, written ${report.written.length}, skipped ${report.skipped.length}, errors ${report.errors.length}`,
|
|
317
|
+
` duplicate groups ${report.duplicate_groups_before} -> ${report.duplicate_groups_after}`,
|
|
318
|
+
];
|
|
319
|
+
for (const skip of report.skipped)
|
|
320
|
+
lines.push(` skipped ${skip.entity} "${skip.alias}": ${skip.reason}${skip.detail ? ` (${skip.detail})` : ""}`);
|
|
321
|
+
for (const error of report.errors)
|
|
322
|
+
lines.push(` error ${error.path}: ${error.error}`);
|
|
323
|
+
return lines.join("\n");
|
|
324
|
+
}
|
|
261
325
|
function fail(error) {
|
|
262
326
|
if (error instanceof OntologyUsageError || error instanceof ApplyError || error instanceof WriteBoundsError) {
|
|
263
327
|
process.stderr.write(`${error.message}\n`);
|
|
@@ -306,9 +370,10 @@ export function registerCoreOntologyCommand(core) {
|
|
|
306
370
|
.option("--apply", "merge each copy into its original and re-point links (writes entity files)")
|
|
307
371
|
.option("--delete", "delete fully merged copies from the vault by exact key (after sync pushed the merge)")
|
|
308
372
|
.option("--variants", "merge two-file duplicate groups whose names differ only in case, spacing or punctuation (checkpoint.ontology.merge-variants)")
|
|
373
|
+
.option("--listed", "merge exactly the pairs in the --only file, whatever their names (checkpoint.ontology.merge-listed-pairs)")
|
|
309
374
|
.option("--limit <n>", "handle at most this many copies", (value) => Number(value))
|
|
310
375
|
.option("--only <file>", "handle only the copy -> original pairs listed in this JSON file")
|
|
311
|
-
.option("--rewrite-refs", "also re-point
|
|
376
|
+
.option("--rewrite-refs", "also re-point invoice and knowledge links to the survivor and carry external ids and web domains; pairs named by signals or sources stay held (checkpoint.ontology.merge-rewrite-refs)")
|
|
312
377
|
.option("--json", "print JSON")
|
|
313
378
|
.option("--hq-root <path>", "HQ installation to operate on")
|
|
314
379
|
.action(async (options) => {
|
|
@@ -320,6 +385,23 @@ export function registerCoreOntologyCommand(core) {
|
|
|
320
385
|
}
|
|
321
386
|
});
|
|
322
387
|
describeCoreCommand(mergeCopies, { execution: "native", root: "live" });
|
|
388
|
+
const dropAliasesCommand = ontology
|
|
389
|
+
.command("drop-aliases")
|
|
390
|
+
.description("Remove reviewed wrong aliases from entity files (dry run by default)")
|
|
391
|
+
.requiredOption("--company <slug>", "company to change")
|
|
392
|
+
.option("--only <file>", "JSON array of { entity, alias } to remove; entity is a company-relative path")
|
|
393
|
+
.option("--apply", "write the entity files")
|
|
394
|
+
.option("--json", "print JSON")
|
|
395
|
+
.option("--hq-root <path>", "HQ installation to operate on")
|
|
396
|
+
.action(async (options) => {
|
|
397
|
+
try {
|
|
398
|
+
process.stdout.write(`${await runOntologyDropAliases(rootFor(options), options.company, options)}\n`);
|
|
399
|
+
}
|
|
400
|
+
catch (error) {
|
|
401
|
+
fail(error);
|
|
402
|
+
}
|
|
403
|
+
});
|
|
404
|
+
describeCoreCommand(dropAliasesCommand, { execution: "native", root: "live" });
|
|
323
405
|
const apply = ontology
|
|
324
406
|
.command("apply")
|
|
325
407
|
.description("Validate the run's entity candidates and write them to the payload company")
|
|
@@ -205,6 +205,28 @@ function extractRulePaths(file) {
|
|
|
205
205
|
}
|
|
206
206
|
return [...paths].sort();
|
|
207
207
|
}
|
|
208
|
+
function shellReferenceStem(reference) {
|
|
209
|
+
const lastDot = reference.lastIndexOf('.');
|
|
210
|
+
return lastDot === -1 ? reference : reference.slice(0, lastDot);
|
|
211
|
+
}
|
|
212
|
+
function shellLsExtensionGlobHasMatch(root, stem) {
|
|
213
|
+
const patternPath = path.join(root, `${stem}.*`);
|
|
214
|
+
const directory = path.dirname(patternPath);
|
|
215
|
+
const basenamePattern = path.basename(patternPath);
|
|
216
|
+
const literalPrefix = basenamePattern.slice(0, -1);
|
|
217
|
+
let entries;
|
|
218
|
+
try {
|
|
219
|
+
entries = fs.readdirSync(directory);
|
|
220
|
+
}
|
|
221
|
+
catch (error) {
|
|
222
|
+
if (expectedFsMiss(error))
|
|
223
|
+
return false;
|
|
224
|
+
throw error;
|
|
225
|
+
}
|
|
226
|
+
// Bash 5.2 enables globskipdots by default, so `.*` only matches when the
|
|
227
|
+
// directory has an actual hidden entry. readdir omits `.` and `..`.
|
|
228
|
+
return entries.some((entry) => entry.startsWith(literalPrefix));
|
|
229
|
+
}
|
|
208
230
|
function refSummary(file, root) {
|
|
209
231
|
const refs = extractRulePaths(file);
|
|
210
232
|
const judged = refs.filter((reference) => {
|
|
@@ -228,11 +250,8 @@ function refSummary(file, root) {
|
|
|
228
250
|
}
|
|
229
251
|
if (fs.existsSync(path.join(root, reference)))
|
|
230
252
|
continue;
|
|
231
|
-
const
|
|
232
|
-
const
|
|
233
|
-
const parent = path.join(root, path.posix.dirname(stem));
|
|
234
|
-
const basename = path.posix.basename(stem);
|
|
235
|
-
const hasSibling = listFiles(parent).some((name) => name.startsWith(`${basename}.`));
|
|
253
|
+
const stem = shellReferenceStem(reference);
|
|
254
|
+
const hasSibling = shellLsExtensionGlobHasMatch(root, stem);
|
|
236
255
|
if (hasSibling)
|
|
237
256
|
renamed.push(reference);
|
|
238
257
|
else
|
package/dist/commands/core.js
CHANGED
|
@@ -35,6 +35,7 @@ import { registerCoreHandoffCommand } from "./core-handoff.js";
|
|
|
35
35
|
import { registerCoreOntologyCommand } from "./core-ontology.js";
|
|
36
36
|
import { registerCoreKnowledgePrefsCommand } from "./core-knowledge-prefs.js";
|
|
37
37
|
import { registerCorePolicyCommand } from "./core-policy.js";
|
|
38
|
+
import { registerCoreJobsNativeCommand } from "./core-jobs-native.js";
|
|
38
39
|
import { registerCoreCheckpointGateSettingCommand } from "./core-checkpoint-gate-setting.js";
|
|
39
40
|
import { resolveLiveRoot } from "../utils/hq-roots.js";
|
|
40
41
|
import { interpreterFor, runBundledScript } from "../utils/run-bundled-script.js";
|
|
@@ -211,6 +212,13 @@ export const SCAFFOLD_COMMANDS = [
|
|
|
211
212
|
* direct `hq core` command.
|
|
212
213
|
*/
|
|
213
214
|
export const SCAFFOLD_ONLY_ASSETS = [
|
|
215
|
+
{ asset: "core/scripts/jobs-schedule-parse.sh", root: "live", summary: "Rollback companion for native jobs schedule-parse" },
|
|
216
|
+
{ asset: "core/scripts/jobs-validate.sh", root: "live", summary: "Rollback companion for native jobs validate" },
|
|
217
|
+
{ asset: "core/scripts/outpost-jobs-reconcile.sh", root: "live", summary: "Rollback companion for native jobs reconcile" },
|
|
218
|
+
{ asset: "core/scripts/hq-job-probe.sh", root: "live", summary: "Scheduled jobs readiness probe used by the reconcile rollback path" },
|
|
219
|
+
{ asset: "core/scripts/hq-job-run.sh", root: "live", summary: "Scheduled jobs runner used by generated systemd units" },
|
|
220
|
+
{ asset: "core/scripts/hq-job-notify.sh", root: "live", summary: "Scheduled jobs failure notification helper" },
|
|
221
|
+
{ asset: "core/scripts/hq-job-remediate.sh", root: "live", summary: "Scheduled jobs bounded remediation helper" },
|
|
214
222
|
// Shared shell libs sourced by the two relocated scripts (and hooks). Never
|
|
215
223
|
// dispatched on their own (so no `hq core` command), but they MUST ship in the
|
|
216
224
|
// tarball, or the bundled hq-session / checkpoint-stop-gate die at `source`
|
|
@@ -232,6 +240,11 @@ export const SCAFFOLD_ONLY_ASSETS = [
|
|
|
232
240
|
{ asset: "core/scripts/hq-delegate-repo.sh", root: "live", summary: "Rollback shell for native delegate repo" },
|
|
233
241
|
{ asset: "core/scripts/hq-delegate-secrets.sh", root: "live", summary: "Rollback shell for native delegate secrets" },
|
|
234
242
|
{ asset: "core/scripts/hq-delegate-transfer.sh", root: "live", summary: "Rollback shell for native delegate transfer" },
|
|
243
|
+
{ asset: "core/scripts/hq-delegate-bundle.sh", root: "live", summary: "Rollback shell for native delegate bundle" },
|
|
244
|
+
{ asset: "core/scripts/hq-delegate-grant.sh", root: "live", summary: "Rollback shell for native delegate grant" },
|
|
245
|
+
{ asset: "core/scripts/hq-delegate-publish.sh", root: "live", summary: "Rollback shell for native delegate publish" },
|
|
246
|
+
{ asset: "core/scripts/hq-delegate-send.sh", root: "live", summary: "Rollback shell for native delegate send" },
|
|
247
|
+
{ asset: "core/scripts/hq-delegate-verify.sh", root: "live", summary: "Rollback shell for native delegate verify" },
|
|
235
248
|
{ asset: "core/scripts/lib/secret-patterns.sh", root: "live", summary: "Secret-pattern library sourced by the delegate rollback shells" },
|
|
236
249
|
// Retained rollback companions for the gated native ontology commands. These
|
|
237
250
|
// are loose HQ-tree assets, but `hq core ontology` dispatches natively.
|
|
@@ -308,6 +321,9 @@ function runEntry(entry, scope, args) {
|
|
|
308
321
|
args,
|
|
309
322
|
cwd,
|
|
310
323
|
hqRoot,
|
|
324
|
+
env: entry.asset === "core/scripts/hq-session.sh"
|
|
325
|
+
? { HQ_HQ_SESSION_NO_CLI: "1" }
|
|
326
|
+
: undefined,
|
|
311
327
|
});
|
|
312
328
|
// Propagate verbatim. Several of these scripts use their exit code as their
|
|
313
329
|
// interface (drift detected, lint failed), so remapping it would break the
|
|
@@ -335,6 +351,7 @@ export function registerCoreCommands(program) {
|
|
|
335
351
|
registerCoreHandoffCommand(core);
|
|
336
352
|
registerCoreKnowledgePrefsCommand(core);
|
|
337
353
|
registerCorePolicyCommand(core);
|
|
354
|
+
registerCoreJobsNativeCommand(core);
|
|
338
355
|
// Wave 2 handoff/worktree commands retain their byte-identical shell assets
|
|
339
356
|
// as a default-off rollback path for one release. Keep the gate helper small
|
|
340
357
|
// so sibling Wave 2 work can reuse it without editing this command table.
|
|
@@ -12,6 +12,7 @@ import { randomUUID } from "node:crypto";
|
|
|
12
12
|
import chalk from "chalk";
|
|
13
13
|
import { vaultApiFetch } from "../utils/vault-api.js";
|
|
14
14
|
import { AuthError } from "../utils/auth-error.js";
|
|
15
|
+
import { planGateErrorFromPayload } from "../utils/plan-gate-error.js";
|
|
15
16
|
import { redactErrorText } from "../utils/redact-error-text.js";
|
|
16
17
|
import { redactSensitiveUrls, redactUrlsInText } from "../utils/redact-url.js";
|
|
17
18
|
import { connectionHealthNotice } from "../lib/integrations/health.js";
|
|
@@ -195,6 +196,12 @@ export async function raiseForResponse(res, fallback) {
|
|
|
195
196
|
raiseIfUnauthorized(res);
|
|
196
197
|
raiseIfUpstreamUnavailable(res);
|
|
197
198
|
const body = (await res.json().catch(() => ({})));
|
|
199
|
+
// A plan-limit 402 is normally decoded by vaultApiFetch before it gets here.
|
|
200
|
+
// Decode it again so no integration route can print the wire token
|
|
201
|
+
// `plan_limit_reached` as its whole message; main.ts renders the typed gate.
|
|
202
|
+
const planGate = planGateErrorFromPayload(res.status, body);
|
|
203
|
+
if (planGate)
|
|
204
|
+
throw planGate;
|
|
198
205
|
// hq-pro mints this error text, so it is untrusted input to a message we
|
|
199
206
|
// print verbatim — scrub credentials out before it can reach a terminal or a
|
|
200
207
|
// Sentry crash report (same reasoning as the gateway path below).
|
package/dist/commands/lanes.js
CHANGED
|
@@ -32,6 +32,7 @@ import { resolveLanesHqRoot } from "../lib/lanes/hq-root.js";
|
|
|
32
32
|
import { startOutpostLaneSnapshotRefresh } from "../lib/lanes/outpost-snapshot-refresh.js";
|
|
33
33
|
import { isSsmRunCommandProcess } from "../lib/lanes/ssm-command-context.js";
|
|
34
34
|
import { readLanesRegistryFlag } from "../lib/lanes/registry-flag.js";
|
|
35
|
+
import { runLeadAutopilotPass } from "../lib/lanes/autopilot.js";
|
|
35
36
|
import { getOrFetchOutpostSnapshot, OUTPOST_SNAPSHOT_CACHE_TTL_MS, outpostLaneListSnapshotKey, OutpostSnapshotInvocationMemo, } from "../lib/lanes/outpost-snapshot-cache.js";
|
|
36
37
|
import { createOutpostHireGrantContext, parseOutpostHireGrantContext, selectOutpostHireGrantHierarchy, sameHireGrantSenior, } from "../lib/lanes/hire-grant-dispatch.js";
|
|
37
38
|
import { createOutpostProfileRepinContext, parseOutpostProfileRepinContext, } from "../lib/lanes/outpost-profile-repin.js";
|
|
@@ -2369,6 +2370,8 @@ async function emitLaneStartedForStories(ctx, company, lane, threadId, knownAbse
|
|
|
2369
2370
|
failures.push(err);
|
|
2370
2371
|
continue;
|
|
2371
2372
|
}
|
|
2373
|
+
if (!story.project_id?.trim() || !story.story_id?.trim())
|
|
2374
|
+
continue;
|
|
2372
2375
|
if (knownAbsentStories.has(storyBoardStoryKey(story.project_id, story.story_id)))
|
|
2373
2376
|
continue;
|
|
2374
2377
|
try {
|
|
@@ -3453,40 +3456,42 @@ async function runCreate(ctx, opts) {
|
|
|
3453
3456
|
: undefined,
|
|
3454
3457
|
});
|
|
3455
3458
|
}
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3459
|
+
if (!opts.quiet)
|
|
3460
|
+
printJson({
|
|
3461
|
+
ok: true,
|
|
3462
|
+
action: "create",
|
|
3463
|
+
lane_id: started.lane_id,
|
|
3464
|
+
state: started.state,
|
|
3465
|
+
queued_reason: started.queued_reason ?? null,
|
|
3466
|
+
priority: started.priority,
|
|
3467
|
+
provider: started.provider,
|
|
3468
|
+
account: started.account ?? null,
|
|
3469
|
+
model: started.model ?? null,
|
|
3470
|
+
effort: started.effort ?? null,
|
|
3471
|
+
effort_source: started.effort_source ?? null,
|
|
3472
|
+
host: started.host,
|
|
3473
|
+
...(autoPlacementSkippedHosts.length > 0
|
|
3474
|
+
? { skipped_hosts: autoPlacementSkippedHosts }
|
|
3475
|
+
: {}),
|
|
3476
|
+
company: started.company,
|
|
3477
|
+
project_id: started.project_id,
|
|
3478
|
+
story_id: started.story_id,
|
|
3479
|
+
stories: laneStories(started),
|
|
3480
|
+
story_warning: combinedStoryWarning(started, createStoryWarnings),
|
|
3481
|
+
thread_id: started.thread_id ?? null,
|
|
3482
|
+
senior: started.senior,
|
|
3483
|
+
depth: started.depth,
|
|
3484
|
+
timeout_secs: timeoutSecs ?? null,
|
|
3485
|
+
...(validation ? { validation } : {}),
|
|
3486
|
+
...(started.outpost_command_id
|
|
3487
|
+
? {
|
|
3488
|
+
outpost_command_id: started
|
|
3489
|
+
.outpost_command_id,
|
|
3490
|
+
}
|
|
3491
|
+
: {}),
|
|
3492
|
+
lane: started,
|
|
3493
|
+
});
|
|
3494
|
+
return started;
|
|
3490
3495
|
}
|
|
3491
3496
|
async function dispatchCreatedOutpostLane(ctx, input) {
|
|
3492
3497
|
const { hqRoot, lane, outpostId, briefText, timeout, preserveQueuedOnFailure = false, } = input;
|
|
@@ -6464,7 +6469,7 @@ async function emitStoryTransition(ctx, lane, projectId, storyId, event, status,
|
|
|
6464
6469
|
...payload,
|
|
6465
6470
|
lane_status: lane.state,
|
|
6466
6471
|
});
|
|
6467
|
-
if (!skipStoryStatusPatch) {
|
|
6472
|
+
if (!skipStoryStatusPatch && projectId?.trim() && storyId?.trim()) {
|
|
6468
6473
|
await ctx.mesh.patchStoryStatus(company, projectId, storyId, status === "blocked" ? "in_progress" : status);
|
|
6469
6474
|
}
|
|
6470
6475
|
}
|
|
@@ -7824,8 +7829,44 @@ async function runReconcile(ctx, opts, recoverQueuedMessages) {
|
|
|
7824
7829
|
summary.ok = false;
|
|
7825
7830
|
}
|
|
7826
7831
|
const terminalizedRemoteIds = new Set(remoteLost.map(({ id }) => id));
|
|
7832
|
+
let autopilot;
|
|
7833
|
+
try {
|
|
7834
|
+
const host = reconcileHostFromCtx(ctx);
|
|
7835
|
+
autopilot = await runLeadAutopilotPass({
|
|
7836
|
+
hqRoot,
|
|
7837
|
+
host: host.outpostId ?? "local",
|
|
7838
|
+
hostname: host.hostname,
|
|
7839
|
+
outpostId: host.outpostId,
|
|
7840
|
+
now: ctx.now(),
|
|
7841
|
+
dryRun: Boolean(opts.dryRun),
|
|
7842
|
+
readFlag: readLanesRegistryFlag,
|
|
7843
|
+
keepAliveResumedLeads: new Set(summary.resumed.filter((entry) => entry.reason === "keep_alive").map((entry) => entry.id)),
|
|
7844
|
+
startWorker: async ({ lead, story }) => {
|
|
7845
|
+
const workerCtx = { ...ctx, env: { ...ctx.env, HQ_LANE_ID: lead.lane_id } };
|
|
7846
|
+
const started = await runCreate(workerCtx, {
|
|
7847
|
+
company: lead.company?.slug,
|
|
7848
|
+
project: lead.project_id,
|
|
7849
|
+
story: story.id,
|
|
7850
|
+
briefFile: story.briefPath,
|
|
7851
|
+
provider: "codex",
|
|
7852
|
+
model: "gpt-6-luna",
|
|
7853
|
+
effort: "high",
|
|
7854
|
+
senior: `lane:${lead.lane_id}`,
|
|
7855
|
+
quiet: true,
|
|
7856
|
+
});
|
|
7857
|
+
return { lane_id: started.lane_id };
|
|
7858
|
+
},
|
|
7859
|
+
});
|
|
7860
|
+
}
|
|
7861
|
+
catch (error) {
|
|
7862
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
7863
|
+
summary.ok = false;
|
|
7864
|
+
summary.errors.push({ code: "lead_autopilot_failed", message });
|
|
7865
|
+
process.stderr.write(`hq lanes autopilot: ${message}\n`);
|
|
7866
|
+
}
|
|
7827
7867
|
printJson({
|
|
7828
7868
|
...summary,
|
|
7869
|
+
...(autopilot ? { autopilot } : {}),
|
|
7829
7870
|
...(remoteLost.length > 0 ? { remote_lost: remoteLost } : {}),
|
|
7830
7871
|
...(mirrorRefresh.unverifiedRemote.size > 0
|
|
7831
7872
|
? {
|
|
@@ -8590,6 +8631,41 @@ export function registerLanesCommand(parent, deps = {}) {
|
|
|
8590
8631
|
.action(wrap(async (opts) => {
|
|
8591
8632
|
await runPickup(ctx, opts);
|
|
8592
8633
|
}));
|
|
8634
|
+
lanes
|
|
8635
|
+
.command("autopilot")
|
|
8636
|
+
.description("Check registered team leads and wake them for ready work")
|
|
8637
|
+
.command("run")
|
|
8638
|
+
.description("Run one lead autopilot check pass")
|
|
8639
|
+
.option("--dry-run", "Report proposed wakes without writing them")
|
|
8640
|
+
.option("--json", "Emit JSON on stdout (default)")
|
|
8641
|
+
.action(wrap(async (opts) => {
|
|
8642
|
+
const host = reconcileHostFromCtx(ctx);
|
|
8643
|
+
const result = await runLeadAutopilotPass({
|
|
8644
|
+
hqRoot: ctx.hqRoot(),
|
|
8645
|
+
host: host.outpostId ?? "local",
|
|
8646
|
+
hostname: host.hostname,
|
|
8647
|
+
outpostId: host.outpostId,
|
|
8648
|
+
now: ctx.now(),
|
|
8649
|
+
dryRun: Boolean(opts.dryRun),
|
|
8650
|
+
readFlag: readLanesRegistryFlag,
|
|
8651
|
+
startWorker: async ({ lead, story }) => {
|
|
8652
|
+
const workerCtx = { ...ctx, env: { ...ctx.env, HQ_LANE_ID: lead.lane_id } };
|
|
8653
|
+
const started = await runCreate(workerCtx, {
|
|
8654
|
+
company: lead.company?.slug,
|
|
8655
|
+
project: lead.project_id,
|
|
8656
|
+
story: story.id,
|
|
8657
|
+
briefFile: story.briefPath,
|
|
8658
|
+
provider: "codex",
|
|
8659
|
+
model: "gpt-6-luna",
|
|
8660
|
+
effort: "high",
|
|
8661
|
+
senior: `lane:${lead.lane_id}`,
|
|
8662
|
+
quiet: true,
|
|
8663
|
+
});
|
|
8664
|
+
return { lane_id: started.lane_id };
|
|
8665
|
+
},
|
|
8666
|
+
});
|
|
8667
|
+
printJson(result);
|
|
8668
|
+
}));
|
|
8593
8669
|
const reconcile = lanes
|
|
8594
8670
|
.command("reconcile", { hidden: true })
|
|
8595
8671
|
.description("Run one machine-owned reconcile pass and exit")
|
package/dist/commands/members.js
CHANGED
|
@@ -2,6 +2,7 @@ import chalk from "chalk";
|
|
|
2
2
|
import { resolveVaultCredential } from "../utils/resolve-vault-credential.js";
|
|
3
3
|
import { gateApiKeyCapabilities } from "../utils/api-key-command-gate.js";
|
|
4
4
|
import { vaultApiFetch, getCompanyUid } from "../utils/vault-api.js";
|
|
5
|
+
import { isPlanGateError } from "../utils/plan-gate-error.js";
|
|
5
6
|
const EMAIL_PATTERN = /^[^\s]+@[^\s]+$/;
|
|
6
7
|
const PERSON_UID_PATTERN = /^prs_[A-Za-z0-9_-]+$/;
|
|
7
8
|
const AGENT_UID_PATTERN = /^agt_[A-Za-z0-9_-]+$/i;
|
|
@@ -558,6 +559,10 @@ export function registerMembersCommand(program) {
|
|
|
558
559
|
}
|
|
559
560
|
}
|
|
560
561
|
catch (err) {
|
|
562
|
+
// A seat-limit refusal goes to main.ts's shared plan-gate renderer:
|
|
563
|
+
// reason, usage, fix options and upgrade link, then a non-zero exit.
|
|
564
|
+
if (isPlanGateError(err))
|
|
565
|
+
throw err;
|
|
561
566
|
// A 409 may refer to either an active membership or an existing
|
|
562
567
|
// pending invite. Probe both views so email targets get actionable
|
|
563
568
|
// guidance while preserving the terminal exit-0 behavior for bulk
|