@indigoai-us/hq-cli 5.308.0 → 5.309.1
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/dist/command-catalog.generated.d.ts +4 -1
- package/dist/command-catalog.generated.js +5 -1
- package/dist/commands/agents.js +6 -0
- package/dist/commands/core-checkpoint-gate-setting.d.ts +0 -10
- package/dist/commands/core-checkpoint-gate-setting.js +3 -3
- package/dist/commands/core-ontology.d.ts +1 -9
- package/dist/commands/core-ontology.js +4 -4
- package/dist/commands/core-policy.d.ts +0 -3
- package/dist/commands/core-policy.js +3 -3
- package/dist/commands/files.js +1 -1
- package/dist/commands/index-cmd.js +1 -1
- package/dist/commands/integrations-api.d.ts +9 -4
- package/dist/commands/integrations-api.js +9 -6
- package/dist/commands/integrations-manage.js +11 -3
- package/dist/commands/integrations.d.ts +8 -0
- package/dist/commands/integrations.js +83 -0
- package/dist/commands/lanes-checks.d.ts +6 -2
- package/dist/commands/lanes-checks.js +69 -19
- package/dist/commands/lanes-report.js +5 -2
- package/dist/commands/lanes.d.ts +0 -11
- package/dist/commands/lanes.js +6 -6
- package/dist/commands/mcp-registration.d.ts +5 -11
- package/dist/commands/mcp-registration.js +2 -6
- package/dist/lib/core-utils/sentry-report.d.ts +5 -11
- package/dist/lib/core-utils/sentry-report.js +2 -2
- package/dist/lib/db/local.d.ts +0 -5
- package/dist/lib/db/local.js +1 -1
- package/dist/lib/db/migrate.d.ts +0 -12
- package/dist/lib/db/migrate.js +5 -5
- package/dist/lib/db/sql.d.ts +0 -10
- package/dist/lib/db/sql.js +4 -8
- package/dist/lib/doctor/checks/codex-wiring.d.ts +0 -4
- package/dist/lib/doctor/checks/codex-wiring.js +3 -35
- package/dist/lib/doctor/checks/grok-wiring.js +1 -34
- package/dist/lib/doctor/checks/runtime-health.d.ts +0 -17
- package/dist/lib/doctor/checks/runtime-health.js +4 -4
- package/dist/lib/doctor/checks/scaffold-cli-floor.d.ts +0 -1
- package/dist/lib/doctor/checks/scaffold-cli-floor.js +1 -1
- package/dist/lib/doctor/compat.d.ts +0 -16
- package/dist/lib/doctor/compat.js +3 -3
- package/dist/lib/doctor/deep/effects.js +1 -3
- package/dist/lib/doctor/deep/sandbox.js +1 -3
- package/dist/lib/doctor/fixtures/schema.js +1 -3
- package/dist/lib/doctor/platform.d.ts +0 -26
- package/dist/lib/doctor/platform.js +5 -5
- package/dist/lib/doctor/shared.d.ts +8 -0
- package/dist/lib/doctor/shared.js +38 -0
- package/dist/lib/lanes/autopilot.js +125 -103
- package/dist/lib/monitor.d.ts +2 -6
- package/dist/lib/monitor.js +5 -5
- package/dist/lib/onboarding/errors.d.ts +2 -1
- package/dist/lib/onboarding/errors.js +1 -1
- package/dist/lib/onboarding/index.d.ts +2 -6
- package/dist/lib/onboarding/index.js +2 -5
- package/dist/lib/report-publish.d.ts +1 -10
- package/dist/lib/report-publish.js +3 -9
- package/dist/lib/search-index/embed-lock.d.ts +0 -7
- package/dist/lib/search-index/embed-lock.js +1 -1
- package/dist/lib/search-index/index.d.ts +7 -0
- package/dist/lib/search-index/index.js +21 -2
- package/dist/lib/search-index/load-gate.d.ts +0 -8
- package/dist/lib/search-index/load-gate.js +3 -3
- package/dist/lib/search-index/max-doc-bytes.d.ts +2 -3
- package/dist/lib/search-index/max-doc-bytes.js +1 -1
- package/dist/lib/search-index/qmd-exit-diagnostics.d.ts +5 -0
- package/dist/lib/search-index/qmd-exit-diagnostics.js +40 -0
- package/dist/lib/service-manager/launchd.d.ts +0 -7
- package/dist/lib/service-manager/launchd.js +2 -2
- package/dist/main.js +7 -2
- package/dist/startup-registration.js +9 -1
- package/dist/utils/client-health.d.ts +0 -4
- package/dist/utils/client-health.js +2 -2
- package/dist/utils/feedback-diagnostics.d.ts +2 -1
- package/dist/utils/feedback-log-bundle.d.ts +0 -25
- package/dist/utils/feedback-log-bundle.js +5 -5
- package/dist/utils/feedback-logs.d.ts +4 -38
- package/dist/utils/feedback-logs.js +7 -7
- package/dist/utils/feedback-screenshots.d.ts +0 -1
- package/dist/utils/feedback-screenshots.js +1 -1
- package/dist/utils/install-tree-torn.d.ts +19 -13
- package/dist/utils/install-tree-torn.js +77 -2
- package/dist/utils/local-files-overview.d.ts +0 -6
- package/dist/utils/local-files-overview.js +1 -1
- package/dist/utils/qmd-model-download-error.d.ts +0 -8
- package/dist/utils/qmd-model-download-error.js +1 -1
- package/dist/utils/qmd-query-document-error.d.ts +0 -6
- package/dist/utils/qmd-query-document-error.js +1 -1
- package/dist/utils/scaffold-fast-import-guard.d.ts +0 -1
- package/dist/utils/scaffold-fast-import-guard.js +1 -1
- package/dist/utils/scaffold-sibling-references.d.ts +3 -2
- package/dist/utils/sentry-fingerprint.js +17 -0
- package/dist/utils/version-gate.d.ts +4 -0
- package/dist/utils/version-gate.js +13 -0
- package/package.json +1 -1
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
+
import { laneRunsOnHost } from "./admission.js";
|
|
3
4
|
import { appendDropboxMessage } from "./dropbox.js";
|
|
4
5
|
import { LanesError } from "./errors.js";
|
|
6
|
+
import { resolveLaneCompany } from "./lane-company.js";
|
|
5
7
|
import { readLanesRegistryFlag } from "./registry-flag.js";
|
|
6
8
|
import { readAllLanes } from "./store.js";
|
|
7
9
|
export const LEAD_AUTOPILOT_FLAG = "lanes.lead-autopilot";
|
|
@@ -406,7 +408,7 @@ function recordOperatorEscalation(hqRoot, record) {
|
|
|
406
408
|
async function runLeadAutopilotPassUnlocked(input) {
|
|
407
409
|
const { hqRoot, host, now } = input;
|
|
408
410
|
const result = { ok: true, host, evaluated: 0, sent: [], started: [], escalated: [], skipped: [] };
|
|
409
|
-
const leads = parseRegistry(hqRoot)
|
|
411
|
+
const leads = parseRegistry(hqRoot);
|
|
410
412
|
const lanes = readAllLanes(hqRoot);
|
|
411
413
|
const byId = new Map(lanes.map((lane) => [lane.lane_id, lane]));
|
|
412
414
|
const stateFile = path.join(hqRoot, "workspace", "lane-briefs", "autopilot-state.json");
|
|
@@ -417,136 +419,156 @@ async function runLeadAutopilotPassUnlocked(input) {
|
|
|
417
419
|
result.skipped.push({ lead_lane: registered.lane, reason: "lead_lane_missing" });
|
|
418
420
|
continue;
|
|
419
421
|
}
|
|
420
|
-
|
|
422
|
+
const company = resolveLaneCompany(hqRoot, lead).company;
|
|
423
|
+
if (!company) {
|
|
421
424
|
result.skipped.push({ lead_lane: lead.lane_id, reason: "lead_company_unresolved" });
|
|
422
425
|
continue;
|
|
423
426
|
}
|
|
424
427
|
const registeredLocalHost = registered.host === "local" || registered.host === input.hostname;
|
|
425
|
-
|
|
426
|
-
|
|
428
|
+
const hostMatchesRegistry = registeredLocalHost
|
|
429
|
+
? lead.host.kind === "local"
|
|
430
|
+
: lead.host.kind === "outpost" && lead.host.id === registered.host && input.outpostId === registered.host;
|
|
431
|
+
if (!hostMatchesRegistry ||
|
|
432
|
+
!laneRunsOnHost(lead, { hostname: input.hostname, outpostId: input.outpostId })) {
|
|
427
433
|
result.skipped.push({ lead_lane: lead.lane_id, reason: "lead_host_mismatch" });
|
|
428
434
|
continue;
|
|
429
435
|
}
|
|
430
|
-
const enabled = await (input.readFlag ?? readLanesRegistryFlag)(LEAD_AUTOPILOT_FLAG, { company: lead.company });
|
|
431
|
-
if (!enabled) {
|
|
432
|
-
result.skipped.push({ lead_lane: lead.lane_id, reason: "flag_off" });
|
|
433
|
-
continue;
|
|
434
|
-
}
|
|
435
|
-
const status = readStatus(hqRoot, registered.status, lead.company.slug);
|
|
436
|
-
const statusUpdatedAt = typeof status.updated_at === "string" && Number.isFinite(Date.parse(status.updated_at))
|
|
437
|
-
? new Date(Date.parse(status.updated_at)).toISOString()
|
|
438
|
-
: undefined;
|
|
439
|
-
result.evaluated += 1;
|
|
440
|
-
const stories = projectStories(hqRoot, registered.prd, lead.company.slug, lead.project_id);
|
|
441
|
-
const children = lanes.filter((lane) => lane.senior.kind === "lane" && lane.senior.id === lead.lane_id);
|
|
442
|
-
const planned = planLeadAutopilotWakes({
|
|
443
|
-
lead,
|
|
444
|
-
children,
|
|
445
|
-
stories,
|
|
446
|
-
now,
|
|
447
|
-
managerStatus: status,
|
|
448
|
-
keepAliveResumed: input.keepAliveResumedLeads?.has(lead.lane_id) === true,
|
|
449
|
-
});
|
|
450
436
|
let stateChanged = false;
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
if (
|
|
454
|
-
|
|
455
|
-
|
|
437
|
+
try {
|
|
438
|
+
const enabled = await (input.readFlag ?? readLanesRegistryFlag)(LEAD_AUTOPILOT_FLAG, { company });
|
|
439
|
+
if (!enabled) {
|
|
440
|
+
result.skipped.push({ lead_lane: lead.lane_id, reason: "flag_off" });
|
|
441
|
+
continue;
|
|
456
442
|
}
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
state.sent[key]
|
|
443
|
+
const status = readStatus(hqRoot, registered.status, company.slug);
|
|
444
|
+
const statusUpdatedAt = typeof status.updated_at === "string" && Number.isFinite(Date.parse(status.updated_at))
|
|
445
|
+
? new Date(Date.parse(status.updated_at)).toISOString()
|
|
446
|
+
: undefined;
|
|
447
|
+
const stories = projectStories(hqRoot, registered.prd, company.slug, lead.project_id);
|
|
448
|
+
const children = lanes.filter((lane) => lane.senior.kind === "lane" && lane.senior.id === lead.lane_id);
|
|
449
|
+
const planned = planLeadAutopilotWakes({
|
|
450
|
+
lead,
|
|
451
|
+
children,
|
|
452
|
+
stories,
|
|
453
|
+
now,
|
|
454
|
+
managerStatus: status,
|
|
455
|
+
keepAliveResumed: input.keepAliveResumedLeads?.has(lead.lane_id) === true,
|
|
456
|
+
});
|
|
457
|
+
const activeKeys = new Set(planned.map((wake) => `${lead.lane_id}:${wake.condition}`));
|
|
458
|
+
for (const key of Object.keys(state.sent)) {
|
|
459
|
+
if (key.startsWith(`${lead.lane_id}:`) && !activeKeys.has(key)) {
|
|
460
|
+
delete state.sent[key];
|
|
475
461
|
stateChanged = true;
|
|
476
462
|
}
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
463
|
+
}
|
|
464
|
+
for (const wake of planned) {
|
|
465
|
+
const key = `${lead.lane_id}:${wake.condition}`;
|
|
466
|
+
const prior = state.sent[key];
|
|
467
|
+
if (prior?.fingerprint === wake.fingerprint) {
|
|
468
|
+
if (input.dryRun)
|
|
469
|
+
continue;
|
|
470
|
+
let current = prior;
|
|
471
|
+
const lastWakeAt = Date.parse(prior.last_wake_at ?? prior.at);
|
|
472
|
+
if (!Number.isFinite(lastWakeAt)) {
|
|
473
|
+
throw new LanesError("lead_autopilot_state_invalid", `Invalid wake time for ${lead.lane_id}`, { lead_lane: lead.lane_id, story_id: wake.story_id });
|
|
474
|
+
}
|
|
475
|
+
if (statusUpdatedAt === undefined || prior.status_updated_at === undefined) {
|
|
476
|
+
throw new LanesError("lead_autopilot_status_activity_unverifiable", `Cannot verify whether lead ${lead.lane_id} acted after its autopilot wake`, { lead_lane: lead.lane_id, story_id: wake.story_id });
|
|
477
|
+
}
|
|
478
|
+
if (!prior.acted && Date.parse(statusUpdatedAt) > Date.parse(prior.status_updated_at)) {
|
|
479
|
+
current = { ...prior, acted: true };
|
|
480
|
+
state.sent[key] = current;
|
|
481
|
+
stateChanged = true;
|
|
481
482
|
}
|
|
482
|
-
if (
|
|
483
|
-
const
|
|
484
|
-
if (!
|
|
485
|
-
throw new LanesError("
|
|
483
|
+
if (wake.condition === "fill_grant" && input.startWorker && !current.acted && !current.auto_started) {
|
|
484
|
+
const firstWakeAt = Date.parse(current.at);
|
|
485
|
+
if (!Number.isFinite(firstWakeAt)) {
|
|
486
|
+
throw new LanesError("lead_autopilot_state_invalid", `Invalid fill-grant wake time for ${lead.lane_id}`, { lead_lane: lead.lane_id, story_id: wake.story_id });
|
|
486
487
|
}
|
|
487
|
-
if (
|
|
488
|
-
|
|
488
|
+
if (now.getTime() - firstWakeAt >= LEAD_AUTOPILOT_FILL_DELAY_MS) {
|
|
489
|
+
const story = stories.find((candidate) => candidate.id === wake.story_id && candidate.status === "todo");
|
|
490
|
+
if (!story?.briefPath) {
|
|
491
|
+
throw new LanesError("lead_autopilot_story_brief_missing", `Cannot auto-start story ${wake.story_id}: its project record has no valid brief path`, { lead_lane: lead.lane_id, story_id: wake.story_id, project_id: lead.project_id });
|
|
492
|
+
}
|
|
493
|
+
if (lead.state !== "running" && lead.state !== "queued") {
|
|
494
|
+
current = { ...current, acted: true };
|
|
495
|
+
state.sent[key] = current;
|
|
496
|
+
stateChanged = true;
|
|
497
|
+
result.skipped.push({ lead_lane: lead.lane_id, reason: "auto_start_lead_not_active" });
|
|
498
|
+
}
|
|
499
|
+
else {
|
|
500
|
+
const started = await input.startWorker({ lead, story });
|
|
501
|
+
current = { ...current, auto_started: true, worker_lane: started.lane_id };
|
|
502
|
+
state.sent[key] = current;
|
|
503
|
+
stateChanged = true;
|
|
504
|
+
result.started.push({ lead_lane: lead.lane_id, story_id: story.id, worker_lane: started.lane_id });
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
if (!current.acted && !current.auto_started && current.escalated_at === undefined && now.getTime() - lastWakeAt >= LEAD_AUTOPILOT_FILL_DELAY_MS) {
|
|
509
|
+
const wakeCount = current.wake_count ?? 1;
|
|
510
|
+
if (wakeCount < 2) {
|
|
511
|
+
appendDropboxMessage(hqRoot, lead.lane_id, {
|
|
512
|
+
at: now.toISOString(),
|
|
513
|
+
message_id: `lead-autopilot:${wake.fingerprint}:wake-2`,
|
|
514
|
+
kind: "system_notice",
|
|
515
|
+
text: wake.prompt,
|
|
516
|
+
});
|
|
517
|
+
current = { ...current, wake_count: 2, last_wake_at: now.toISOString() };
|
|
489
518
|
state.sent[key] = current;
|
|
490
519
|
stateChanged = true;
|
|
491
|
-
result.
|
|
520
|
+
result.sent.push(wake);
|
|
492
521
|
}
|
|
493
522
|
else {
|
|
494
|
-
const
|
|
495
|
-
|
|
523
|
+
const escalation = { team: registered.team, lead_lane: lead.lane_id, condition: wake.condition, story_id: wake.story_id, at: now.toISOString(), fingerprint: wake.fingerprint };
|
|
524
|
+
recordOperatorEscalation(hqRoot, escalation);
|
|
525
|
+
current = { ...current, escalated_at: now.toISOString() };
|
|
496
526
|
state.sent[key] = current;
|
|
497
527
|
stateChanged = true;
|
|
498
|
-
result.
|
|
528
|
+
result.escalated.push(escalation);
|
|
499
529
|
}
|
|
500
530
|
}
|
|
531
|
+
continue;
|
|
501
532
|
}
|
|
502
|
-
if (!
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
const escalation = { team: registered.team, lead_lane: lead.lane_id, condition: wake.condition, story_id: wake.story_id, at: now.toISOString(), fingerprint: wake.fingerprint };
|
|
518
|
-
recordOperatorEscalation(hqRoot, escalation);
|
|
519
|
-
current = { ...current, escalated_at: now.toISOString() };
|
|
520
|
-
state.sent[key] = current;
|
|
521
|
-
stateChanged = true;
|
|
522
|
-
result.escalated.push(escalation);
|
|
523
|
-
}
|
|
533
|
+
if (!input.dryRun) {
|
|
534
|
+
appendDropboxMessage(hqRoot, lead.lane_id, {
|
|
535
|
+
at: now.toISOString(),
|
|
536
|
+
message_id: `lead-autopilot:${wake.fingerprint}`,
|
|
537
|
+
kind: "system_notice",
|
|
538
|
+
text: wake.prompt,
|
|
539
|
+
});
|
|
540
|
+
state.sent[key] = {
|
|
541
|
+
fingerprint: wake.fingerprint,
|
|
542
|
+
at: now.toISOString(),
|
|
543
|
+
...(statusUpdatedAt ? { status_updated_at: statusUpdatedAt } : {}),
|
|
544
|
+
wake_count: 1,
|
|
545
|
+
last_wake_at: now.toISOString(),
|
|
546
|
+
};
|
|
547
|
+
stateChanged = true;
|
|
524
548
|
}
|
|
525
|
-
|
|
549
|
+
result.sent.push(wake);
|
|
526
550
|
}
|
|
527
|
-
if (!input.dryRun)
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
...(statusUpdatedAt ? { status_updated_at: statusUpdatedAt } : {}),
|
|
538
|
-
wake_count: 1,
|
|
539
|
-
last_wake_at: now.toISOString(),
|
|
540
|
-
};
|
|
541
|
-
stateChanged = true;
|
|
542
|
-
}
|
|
543
|
-
result.sent.push(wake);
|
|
551
|
+
if (!input.dryRun && stateChanged)
|
|
552
|
+
writeState(stateFile, state);
|
|
553
|
+
result.evaluated += 1;
|
|
554
|
+
}
|
|
555
|
+
catch (error) {
|
|
556
|
+
if (!input.dryRun && stateChanged)
|
|
557
|
+
writeState(stateFile, state);
|
|
558
|
+
if (!(error instanceof LanesError) || LEAD_AUTOPILOT_PASS_FATAL_ERRORS.has(error.error))
|
|
559
|
+
throw error;
|
|
560
|
+
result.skipped.push({ lead_lane: lead.lane_id, reason: error.error });
|
|
544
561
|
}
|
|
545
|
-
if (!input.dryRun && stateChanged)
|
|
546
|
-
writeState(stateFile, state);
|
|
547
562
|
}
|
|
548
563
|
return result;
|
|
549
564
|
}
|
|
565
|
+
// Tenant-boundary and path-safety failures stop the whole pass; other per-lead
|
|
566
|
+
// data errors skip only that lead.
|
|
567
|
+
const LEAD_AUTOPILOT_PASS_FATAL_ERRORS = new Set([
|
|
568
|
+
"lead_autopilot_tenant_mismatch",
|
|
569
|
+
"lead_autopilot_status_path_invalid",
|
|
570
|
+
"lead_autopilot_escalation_record_invalid",
|
|
571
|
+
]);
|
|
550
572
|
const LEAD_AUTOPILOT_STALE_LOCK_MAX_AGE_MS = 60 * 60_000;
|
|
551
573
|
function processStartIdentity(pid) {
|
|
552
574
|
try {
|
package/dist/lib/monitor.d.ts
CHANGED
|
@@ -4,15 +4,12 @@ export declare const MONITOR_FLAG = "monitor.enabled";
|
|
|
4
4
|
export declare const MONITOR_FLAG_DEFAULT = false;
|
|
5
5
|
export declare const MONITOR_DEFAULT_TIMEOUT_MS: number;
|
|
6
6
|
export declare const MONITOR_DEFAULT_CHECKIN_MS: number;
|
|
7
|
-
export declare const MONITOR_PROMPT_CACHE_TTL_MS: number;
|
|
8
7
|
export declare const MONITOR_BATCH_WINDOW_MS = 200;
|
|
9
8
|
export declare const MONITOR_POLL_INTERVAL_MS = 1000;
|
|
10
9
|
export declare const MONITOR_RATE_WINDOW_MS = 10000;
|
|
11
10
|
/** Sustained stdout floods stop after 3 windows; the per-window cap can be overridden with HQ_MONITOR_MAX_EVENTS_PER_WINDOW. */
|
|
12
11
|
export declare const MONITOR_MAX_EVENTS_PER_WINDOW = 60;
|
|
13
12
|
export declare const MONITOR_MAX_FLOOD_WINDOWS = 3;
|
|
14
|
-
export declare const MONITOR_LOCK_STALE_MS = 30000;
|
|
15
|
-
export declare const MONITOR_MAX_TIMER_DELAY_MS = 2147000000;
|
|
16
13
|
export type MonitorTarget = {
|
|
17
14
|
kind: "lane";
|
|
18
15
|
laneId: string;
|
|
@@ -38,7 +35,7 @@ export interface NearestMonitorRuntime {
|
|
|
38
35
|
export interface MonitorRuntimeIdentity extends NearestMonitorRuntime {
|
|
39
36
|
starttime: number;
|
|
40
37
|
}
|
|
41
|
-
|
|
38
|
+
interface MonitorSessionOwner {
|
|
42
39
|
pid: number;
|
|
43
40
|
starttime: number | null;
|
|
44
41
|
}
|
|
@@ -107,7 +104,6 @@ export interface MonitorCheckinResult {
|
|
|
107
104
|
/** This guard is first in monitor start so a conflicting invocation has no side effects. */
|
|
108
105
|
export declare function assertMonitorTimeoutCompatibility(persistent: boolean, timeoutProvided: boolean): void;
|
|
109
106
|
export declare function parseMonitorDuration(value: string, kind: "timeout" | "checkin"): number;
|
|
110
|
-
export declare function assertMonitorSessionId(sessionId: string): void;
|
|
111
107
|
export declare function parseMonitorTarget(value: string): MonitorTarget;
|
|
112
108
|
export declare function monitorTargetKey(target: MonitorTarget): string;
|
|
113
109
|
export declare function detectMonitorTarget(options: DetectMonitorTargetOptions): MonitorTarget;
|
|
@@ -119,7 +115,6 @@ export declare function findNearestMonitorRuntime(options: {
|
|
|
119
115
|
export declare function monitorSessionDir(hqRoot: string, provider: LaneProvider, sessionId: string): string;
|
|
120
116
|
export declare function monitorSessionHasActiveMarkers(hqRoot: string, provider: LaneProvider, sessionId: string): boolean;
|
|
121
117
|
export declare function monitorSessionHasActiveMarkerForOwner(hqRoot: string, provider: LaneProvider, sessionId: string, owner: MonitorRuntimeIdentity): boolean;
|
|
122
|
-
export declare function monitorSessionHasInboxFile(hqRoot: string, provider: LaneProvider, sessionId: string): boolean;
|
|
123
118
|
export declare function monitorSessionInboxHasEvents(hqRoot: string, provider: LaneProvider, sessionId: string): boolean;
|
|
124
119
|
export declare function monitorSessionHasWork(hqRoot: string, provider: LaneProvider, sessionId: string): boolean;
|
|
125
120
|
export declare function monitorDir(hqRoot: string, monitorId: string): string;
|
|
@@ -150,4 +145,5 @@ export declare function recordMonitorTargetDeliveryAtPath(activityPath: string,
|
|
|
150
145
|
export declare function formatElapsed(ms: number): string;
|
|
151
146
|
export declare function runMonitorCheckinTick(hqRoot: string, targetKey: string, deps: MonitorCheckinDeps): Promise<MonitorCheckinResult>;
|
|
152
147
|
export declare function readMonitorPidLiveness(pid: number | null, starttime: number | null, readStarttime?: (pid: number) => number | null): boolean;
|
|
148
|
+
export {};
|
|
153
149
|
//# sourceMappingURL=monitor.d.ts.map
|
package/dist/lib/monitor.js
CHANGED
|
@@ -9,15 +9,15 @@ export const MONITOR_FLAG = "monitor.enabled";
|
|
|
9
9
|
export const MONITOR_FLAG_DEFAULT = false;
|
|
10
10
|
export const MONITOR_DEFAULT_TIMEOUT_MS = 5 * 60_000;
|
|
11
11
|
export const MONITOR_DEFAULT_CHECKIN_MS = 55 * 60_000;
|
|
12
|
-
|
|
12
|
+
const MONITOR_PROMPT_CACHE_TTL_MS = 60 * 60_000;
|
|
13
13
|
export const MONITOR_BATCH_WINDOW_MS = 200;
|
|
14
14
|
export const MONITOR_POLL_INTERVAL_MS = 1_000;
|
|
15
15
|
export const MONITOR_RATE_WINDOW_MS = 10_000;
|
|
16
16
|
/** Sustained stdout floods stop after 3 windows; the per-window cap can be overridden with HQ_MONITOR_MAX_EVENTS_PER_WINDOW. */
|
|
17
17
|
export const MONITOR_MAX_EVENTS_PER_WINDOW = 60;
|
|
18
18
|
export const MONITOR_MAX_FLOOD_WINDOWS = 3;
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
const MONITOR_LOCK_STALE_MS = 30_000;
|
|
20
|
+
const MONITOR_MAX_TIMER_DELAY_MS = 2_147_000_000;
|
|
21
21
|
const SESSION_ID_RE = /^[A-Za-z0-9_-]{1,128}$/;
|
|
22
22
|
const SAFE_LANE_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
23
23
|
const RUNTIME_ENV = {
|
|
@@ -59,7 +59,7 @@ function assertLaneTargetId(laneId) {
|
|
|
59
59
|
throw monitorError("monitor_target_invalid", `Invalid lane target ${JSON.stringify(laneId)}.`, { lane_id: laneId });
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
|
-
|
|
62
|
+
function assertMonitorSessionId(sessionId) {
|
|
63
63
|
if (!SESSION_ID_RE.test(sessionId)) {
|
|
64
64
|
throw monitorError("monitor_session_id_invalid", "Session ids must contain only letters, numbers, underscores, or hyphens and be 1–128 characters long.", { session_id: sessionId });
|
|
65
65
|
}
|
|
@@ -154,7 +154,7 @@ export function monitorSessionHasActiveMarkerForOwner(hqRoot, provider, sessionI
|
|
|
154
154
|
&& markerOwner.starttime === owner.starttime;
|
|
155
155
|
});
|
|
156
156
|
}
|
|
157
|
-
|
|
157
|
+
function monitorSessionHasInboxFile(hqRoot, provider, sessionId) {
|
|
158
158
|
return fs.existsSync(path.join(monitorSessionDir(hqRoot, provider, sessionId), "dropbox", "inbox.jsonl"));
|
|
159
159
|
}
|
|
160
160
|
export function monitorSessionInboxHasEvents(hqRoot, provider, sessionId) {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Each failure class maps to a distinct step in the onboarding flow,
|
|
5
5
|
* making it easy for the installer to show targeted recovery hints.
|
|
6
6
|
*/
|
|
7
|
-
|
|
7
|
+
declare class OnboardingError extends Error {
|
|
8
8
|
readonly step: string;
|
|
9
9
|
readonly cause?: Error | undefined;
|
|
10
10
|
constructor(message: string, step: string, cause?: Error | undefined);
|
|
@@ -30,4 +30,5 @@ export declare class InviteAcceptError extends OnboardingError {
|
|
|
30
30
|
export declare class StsVerifyError extends OnboardingError {
|
|
31
31
|
constructor(message: string, cause?: Error);
|
|
32
32
|
}
|
|
33
|
+
export {};
|
|
33
34
|
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Each failure class maps to a distinct step in the onboarding flow,
|
|
5
5
|
* making it easy for the installer to show targeted recovery hints.
|
|
6
6
|
*/
|
|
7
|
-
|
|
7
|
+
class OnboardingError extends Error {
|
|
8
8
|
step;
|
|
9
9
|
cause;
|
|
10
10
|
constructor(message, step, cause) {
|
|
@@ -7,11 +7,7 @@
|
|
|
7
7
|
* invite/accept into create-company and join-company flows with
|
|
8
8
|
* checkpoint/resume.
|
|
9
9
|
*/
|
|
10
|
-
export {
|
|
11
|
-
export {
|
|
12
|
-
export { OnboardingError, PersonCreationError, CompanyCreationError, ProvisioningError, MembershipBootstrapError, FirstSyncError, InviteAcceptError, StsVerifyError, } from "./errors.js";
|
|
13
|
-
export type { OnboardingInput, CreateCompanyInput, JoinCompanyInput, OnboardingConfig, OnboardingResult, OnboardingProgress, OnboardingStep, StepStatus, ProgressCallback, OnboardingCheckpoint, DesktopInstallerContract, HqConfig, } from "./types.js";
|
|
10
|
+
export { readCheckpoint } from "./checkpoint.js";
|
|
11
|
+
export type { OnboardingCheckpoint } from "./types.js";
|
|
14
12
|
export { runOnboardCli } from "./cli/onboard.js";
|
|
15
|
-
export type { OnboardCliOptions, OnboardCliResult } from "./cli/onboard.js";
|
|
16
|
-
export { formatProgress, formatSummary, formatError, validateSlug, validateEmail } from "./cli/prompts.js";
|
|
17
13
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -7,10 +7,7 @@
|
|
|
7
7
|
* invite/accept into create-company and join-company flows with
|
|
8
8
|
* checkpoint/resume.
|
|
9
9
|
*/
|
|
10
|
-
|
|
11
|
-
export { readCheckpoint
|
|
12
|
-
export { OnboardingError, PersonCreationError, CompanyCreationError, ProvisioningError, MembershipBootstrapError, FirstSyncError, InviteAcceptError, StsVerifyError, } from "./errors.js";
|
|
13
|
-
// CLI
|
|
10
|
+
// The CLI command and identity guard are the only consumers of this barrel.
|
|
11
|
+
export { readCheckpoint } from "./checkpoint.js";
|
|
14
12
|
export { runOnboardCli } from "./cli/onboard.js";
|
|
15
|
-
export { formatProgress, formatSummary, formatError, validateSlug, validateEmail } from "./cli/prompts.js";
|
|
16
13
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
/** Mirrors the deploy skill's static-report guardrails. */
|
|
2
|
-
export declare const REPORT_SOURCE_MAX_BYTES: number;
|
|
3
|
-
export declare const REPORT_BUNDLE_MAX_BYTES: number;
|
|
4
|
-
export declare const REPORT_SOURCE_MAX_FILES = 100;
|
|
5
1
|
export declare const REPORT_CSP = "default-src 'none'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'; connect-src 'none'; frame-src 'none'; form-action 'none'";
|
|
6
|
-
|
|
2
|
+
type ReportLastRun = {
|
|
7
3
|
at: string;
|
|
8
4
|
status: "running" | "succeeded" | "failed";
|
|
9
5
|
reason?: string;
|
|
@@ -175,10 +171,5 @@ type BuiltTarball = {
|
|
|
175
171
|
};
|
|
176
172
|
/** hq-deploy static payload format: index.html stays at the archive root. */
|
|
177
173
|
export declare function buildReportTarball(files: StaticFile[]): BuiltTarball;
|
|
178
|
-
/** Isolated test helper for creating a compliant report source. */
|
|
179
|
-
export declare function makeReportFixture(html: string): {
|
|
180
|
-
directory: string;
|
|
181
|
-
cleanup: () => void;
|
|
182
|
-
};
|
|
183
174
|
export {};
|
|
184
175
|
//# sourceMappingURL=report-publish.d.ts.map
|
|
@@ -13,9 +13,9 @@ import { gzipSync } from "node:zlib";
|
|
|
13
13
|
import { REPORT_PREVIEW_FILE } from "./report-snapshot.js";
|
|
14
14
|
import { loadUpdateReportRunbook } from "./report-templates.js";
|
|
15
15
|
/** Mirrors the deploy skill's static-report guardrails. */
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
const REPORT_SOURCE_MAX_BYTES = 25 * 1024 * 1024;
|
|
17
|
+
const REPORT_BUNDLE_MAX_BYTES = 10 * 1024 * 1024;
|
|
18
|
+
const REPORT_SOURCE_MAX_FILES = 100;
|
|
19
19
|
export const REPORT_CSP = "default-src 'none'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'; connect-src 'none'; frame-src 'none'; form-action 'none'";
|
|
20
20
|
/**
|
|
21
21
|
* Write the files exactly as they will be published, protected index.html
|
|
@@ -418,10 +418,4 @@ function writeOctal(buffer, value, offset, length) {
|
|
|
418
418
|
buffer.write(value.toString(8).padStart(length - 1, "0").slice(-length + 1), offset, length - 1, "ascii");
|
|
419
419
|
buffer[offset + length - 1] = 0;
|
|
420
420
|
}
|
|
421
|
-
/** Isolated test helper for creating a compliant report source. */
|
|
422
|
-
export function makeReportFixture(html) {
|
|
423
|
-
const directory = fs.mkdtempSync(path.join(os.tmpdir(), "hq-report-test-"));
|
|
424
|
-
fs.writeFileSync(path.join(directory, "index.html"), html);
|
|
425
|
-
return { directory, cleanup: () => fs.rmSync(directory, { recursive: true, force: true }) };
|
|
426
|
-
}
|
|
427
421
|
//# sourceMappingURL=report-publish.js.map
|
|
@@ -11,13 +11,6 @@ export type EmbedLockProcessEvents = Pick<NodeJS.Process, 'once'> & {
|
|
|
11
11
|
removeListener?: NodeJS.Process['removeListener'];
|
|
12
12
|
};
|
|
13
13
|
export type EmbedLockRunResult = 'ran' | 'skipped' | 'busy' | 'unavailable';
|
|
14
|
-
/**
|
|
15
|
-
* Serializes QMD writes started through HQ-managed hq-cli paths. A bare `qmd
|
|
16
|
-
* update` / `qmd embed` (or another tool that does not call this module) cannot
|
|
17
|
-
* participate, because qmd is a third-party package and does not know about
|
|
18
|
-
* HQ's lock protocol.
|
|
19
|
-
*/
|
|
20
|
-
export declare const EMBED_LOCK_NAME = "qmd-reindex-bg.lock";
|
|
21
14
|
export declare function defaultEmbedLockDependencies(env?: NodeJS.ProcessEnv): EmbedLockDependencies;
|
|
22
15
|
export declare function lockRoot(home: string): string;
|
|
23
16
|
export declare function lockPath(home: string): string;
|
|
@@ -6,7 +6,7 @@ import * as path from 'node:path';
|
|
|
6
6
|
* participate, because qmd is a third-party package and does not know about
|
|
7
7
|
* HQ's lock protocol.
|
|
8
8
|
*/
|
|
9
|
-
|
|
9
|
+
const EMBED_LOCK_NAME = 'qmd-reindex-bg.lock';
|
|
10
10
|
export function defaultEmbedLockDependencies(env = process.env) {
|
|
11
11
|
return {
|
|
12
12
|
env,
|
|
@@ -11,6 +11,7 @@ export type QmdProcessResult = {
|
|
|
11
11
|
*/
|
|
12
12
|
signal?: NodeJS.Signals | string | null;
|
|
13
13
|
};
|
|
14
|
+
export type QmdUpdateEntryPath = 'index-sync';
|
|
14
15
|
export type QmdProcessRunner = (bin: string, args: string[], options: {
|
|
15
16
|
cwd?: string;
|
|
16
17
|
env?: NodeJS.ProcessEnv;
|
|
@@ -113,6 +114,10 @@ export declare class QmdExitError extends Error {
|
|
|
113
114
|
readonly stdout: string;
|
|
114
115
|
readonly stderr: string;
|
|
115
116
|
name: string;
|
|
117
|
+
/** Safe route labels for telemetry; raw qmd streams remain local to the error. */
|
|
118
|
+
entryPath?: QmdUpdateEntryPath;
|
|
119
|
+
/** Present only when the invoked executable is the bundled qmd. */
|
|
120
|
+
qmdVersion?: string;
|
|
116
121
|
constructor(message: string, args: string[], status: number | null, stdout: string, stderr: string);
|
|
117
122
|
}
|
|
118
123
|
export declare class QmdCollectionMissingError extends QmdExitError {
|
|
@@ -507,6 +512,8 @@ export type ResolveQmdInvocationOptions = {
|
|
|
507
512
|
export declare function resolveQmdInvocation(options?: ResolveQmdInvocationOptions): QmdInvocation;
|
|
508
513
|
export type RunQmdOptions = {
|
|
509
514
|
bin?: string;
|
|
515
|
+
/** Source of an update failure, used only for bounded Sentry tags. */
|
|
516
|
+
entryPath?: QmdUpdateEntryPath;
|
|
510
517
|
cwd?: string;
|
|
511
518
|
env?: NodeJS.ProcessEnv;
|
|
512
519
|
runner?: QmdProcessRunner;
|
|
@@ -76,6 +76,10 @@ export class QmdExitError extends Error {
|
|
|
76
76
|
stdout;
|
|
77
77
|
stderr;
|
|
78
78
|
name = 'QmdExitError';
|
|
79
|
+
/** Safe route labels for telemetry; raw qmd streams remain local to the error. */
|
|
80
|
+
entryPath;
|
|
81
|
+
/** Present only when the invoked executable is the bundled qmd. */
|
|
82
|
+
qmdVersion;
|
|
79
83
|
constructor(message, args, status, stdout, stderr) {
|
|
80
84
|
super(message);
|
|
81
85
|
this.args = args;
|
|
@@ -1239,6 +1243,21 @@ function finishRunQmd(result, bin, args, attribution) {
|
|
|
1239
1243
|
}
|
|
1240
1244
|
throw exitError;
|
|
1241
1245
|
}
|
|
1246
|
+
function finishRunQmdWithDiagnostics(result, bin, args, options, attribution) {
|
|
1247
|
+
try {
|
|
1248
|
+
return finishRunQmd(result, bin, args, attribution);
|
|
1249
|
+
}
|
|
1250
|
+
catch (error) {
|
|
1251
|
+
if (error instanceof QmdExitError) {
|
|
1252
|
+
if (args[0] === 'update' && options.entryPath)
|
|
1253
|
+
error.entryPath = options.entryPath;
|
|
1254
|
+
const bundledBins = [packageLocalBin(), packageLocalNodeEntry()].filter((candidate) => Boolean(candidate));
|
|
1255
|
+
if (bundledBins.includes(bin))
|
|
1256
|
+
error.qmdVersion = resolveQmdVersion();
|
|
1257
|
+
}
|
|
1258
|
+
throw error;
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1242
1261
|
/** Run qmd with captured output and typed failures. */
|
|
1243
1262
|
export function runQmd(args, options = {}) {
|
|
1244
1263
|
// Legacy string-bin runner seam (tests and callers that inject a fake process):
|
|
@@ -1246,7 +1265,7 @@ export function runQmd(args, options = {}) {
|
|
|
1246
1265
|
if (options.runner) {
|
|
1247
1266
|
const bin = options.bin ?? resolveQmdBin({ env: options.env });
|
|
1248
1267
|
const result = options.runner(bin, args, { cwd: options.cwd, env: options.env });
|
|
1249
|
-
return
|
|
1268
|
+
return finishRunQmdWithDiagnostics(result, bin, args, options);
|
|
1250
1269
|
}
|
|
1251
1270
|
// Default path: resolve the full invocation (shell-free Node launcher preferred)
|
|
1252
1271
|
// and spawn it through the shared Windows-aware plan. An explicit string bin is
|
|
@@ -1279,7 +1298,7 @@ export function runQmd(args, options = {}) {
|
|
|
1279
1298
|
// genuinely missing qmd — instead of blaming the launcher for all three
|
|
1280
1299
|
// (HQ-CLI-1A). `command` is the process actually spawned; `launcher` is qmd's
|
|
1281
1300
|
// file identity.
|
|
1282
|
-
return
|
|
1301
|
+
return finishRunQmdWithDiagnostics(result, launcher, args, options, {
|
|
1283
1302
|
command: invocation.command,
|
|
1284
1303
|
launcher,
|
|
1285
1304
|
cwd: options.cwd,
|
|
@@ -22,10 +22,6 @@ export type LoadGateDependencies = {
|
|
|
22
22
|
freemem: () => number;
|
|
23
23
|
totalmem: () => number;
|
|
24
24
|
};
|
|
25
|
-
type CpuCounters = {
|
|
26
|
-
total: number;
|
|
27
|
-
idle: number;
|
|
28
|
-
};
|
|
29
25
|
export type EmbedLoadDecision = {
|
|
30
26
|
action: 'embed';
|
|
31
27
|
forced: false;
|
|
@@ -44,11 +40,8 @@ export declare class LoadGateConfigurationError extends Error {
|
|
|
44
40
|
constructor(value: string);
|
|
45
41
|
}
|
|
46
42
|
export declare function defaultLoadGateDependencies(env?: NodeJS.ProcessEnv): LoadGateDependencies;
|
|
47
|
-
export declare function parseProcStatAggregate(contents: string): CpuCounters | undefined;
|
|
48
|
-
export declare function cpuBusyPercent(before: CpuCounters, after: CpuCounters): number | undefined;
|
|
49
43
|
/** Sample aggregate CPU busy time over 200 ms, falling back only without /proc/stat. */
|
|
50
44
|
export declare function measureCpuPercent(dependencies: LoadGateDependencies): number;
|
|
51
|
-
export declare function memAvailableFraction(contents: string): number | undefined;
|
|
52
45
|
/** Prefer Linux MemAvailable, because MemFree alone treats reclaimable cache as used memory. */
|
|
53
46
|
export declare function measureMemoryPercent(dependencies: LoadGateDependencies): number;
|
|
54
47
|
export declare function parseLoadThreshold(env: NodeJS.ProcessEnv): number | undefined;
|
|
@@ -61,5 +54,4 @@ export declare function loadGateStatePath(home: string): string;
|
|
|
61
54
|
export declare function checkEmbedLoad(home: string, dependencies: LoadGateDependencies): EmbedLoadDecision;
|
|
62
55
|
/** Reset the starvation counter only after qmd embed has actually returned successfully. */
|
|
63
56
|
export declare function recordSuccessfulEmbed(home: string, dependencies: LoadGateDependencies): void;
|
|
64
|
-
export {};
|
|
65
57
|
//# sourceMappingURL=load-gate.d.ts.map
|
|
@@ -46,7 +46,7 @@ function errorCode(error) {
|
|
|
46
46
|
function missing(error) {
|
|
47
47
|
return errorCode(error) === 'ENOENT';
|
|
48
48
|
}
|
|
49
|
-
|
|
49
|
+
function parseProcStatAggregate(contents) {
|
|
50
50
|
const line = contents.split('\n').find((candidate) => /^cpu\s+/.test(candidate));
|
|
51
51
|
if (!line)
|
|
52
52
|
return undefined;
|
|
@@ -58,7 +58,7 @@ export function parseProcStatAggregate(contents) {
|
|
|
58
58
|
const idle = values[3] + values[4];
|
|
59
59
|
return total > 0 && Number.isFinite(idle) ? { total, idle } : undefined;
|
|
60
60
|
}
|
|
61
|
-
|
|
61
|
+
function cpuBusyPercent(before, after) {
|
|
62
62
|
const totalDelta = after.total - before.total;
|
|
63
63
|
const idleDelta = after.idle - before.idle;
|
|
64
64
|
if (totalDelta <= 0 || idleDelta < 0)
|
|
@@ -117,7 +117,7 @@ export function measureCpuPercent(dependencies) {
|
|
|
117
117
|
throw new Error('Cannot determine CPU busy time from /proc/stat.');
|
|
118
118
|
return busy;
|
|
119
119
|
}
|
|
120
|
-
|
|
120
|
+
function memAvailableFraction(contents) {
|
|
121
121
|
const fields = new Map();
|
|
122
122
|
for (const line of contents.split('\n')) {
|
|
123
123
|
const match = /^(MemTotal|MemAvailable):\s+(\d+)\s+kB$/.exec(line.trim());
|