@indigoai-us/hq-cli 5.300.0 → 5.302.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 +42 -0
- package/assets/scaffold/core/scripts/rebuild-projects-index.sh +99 -12
- package/assets/scaffold/core/scripts/token-usage-report.sh +9 -0
- package/dist/command-catalog.generated.d.ts +32 -3
- package/dist/command-catalog.generated.js +39 -3
- package/dist/commands/__fixtures__/access-vault.d.ts +2 -0
- package/dist/commands/__fixtures__/access-vault.js +1 -1
- package/dist/commands/access.d.ts +2 -1
- package/dist/commands/access.js +58 -49
- package/dist/commands/bot.js +1 -0
- package/dist/commands/cloud-provision.js +51 -2
- package/dist/commands/core-checkpoint.js +1 -0
- package/dist/commands/core-company-index-scope-flag.d.ts +3 -0
- package/dist/commands/core-company-index-scope-flag.js +6 -0
- package/dist/commands/core-handoff-fast.js +1 -0
- package/dist/commands/core-handoff.js +2 -0
- package/dist/commands/core-job-native-utils.d.ts +6 -0
- package/dist/commands/core-job-native-utils.js +80 -0
- package/dist/commands/core-job-native.d.ts +2 -4
- package/dist/commands/core-job-native.js +6 -10
- package/dist/commands/core-job-notify-native.d.ts +36 -0
- package/dist/commands/core-job-notify-native.js +359 -0
- package/dist/commands/core-job-remediate-native.d.ts +26 -0
- package/dist/commands/core-job-remediate-native.js +374 -0
- package/dist/commands/core.js +26 -8
- package/dist/commands/deploy-comments.js +5 -2
- package/dist/commands/files.js +31 -14
- package/dist/commands/integrations-api.d.ts +6 -6
- package/dist/commands/integrations-api.js +4 -13
- package/dist/commands/integrations-connect.d.ts +3 -0
- package/dist/commands/integrations-connect.js +21 -2
- package/dist/commands/integrations-manage.js +1 -4
- package/dist/commands/lanes-message.js +43 -5
- package/dist/commands/lanes-questions.d.ts +4 -2
- package/dist/commands/lanes-questions.js +123 -2
- package/dist/commands/lanes-resume.js +25 -4
- package/dist/commands/lanes-shared.d.ts +15 -1
- package/dist/commands/lanes-watch.js +5 -1
- package/dist/commands/lanes.d.ts +2 -0
- package/dist/commands/lanes.js +92 -9
- package/dist/commands/meetings.js +35 -16
- package/dist/commands/monitor.js +17 -2
- package/dist/commands/report.js +5 -2
- package/dist/commands/rescue.d.ts +4 -0
- package/dist/commands/rescue.js +27 -17
- package/dist/commands/safe-extract.d.ts +4 -4
- package/dist/commands/safe-extract.js +6 -7
- package/dist/lib/agent-kit/fallback.d.ts +1 -0
- package/dist/lib/agent-kit/fallback.js +1 -0
- package/dist/lib/bot/daemon.d.ts +5 -1
- package/dist/lib/bot/daemon.js +4 -4
- package/dist/lib/core-utils/soft-timeout.js +1 -1
- package/dist/lib/core-utils/token-usage-report.js +17 -3
- package/dist/lib/deploy-apps.d.ts +13 -0
- package/dist/lib/deploy-apps.js +24 -0
- package/dist/lib/index-render/companies.d.ts +4 -1
- package/dist/lib/index-render/companies.js +66 -3
- package/dist/lib/index-render/index.d.ts +1 -0
- package/dist/lib/index-render/index.js +2 -1
- package/dist/lib/index-render/projects.js +82 -5
- package/dist/lib/index-render/shared.d.ts +1 -1
- package/dist/lib/index-render/shared.js +4 -2
- package/dist/lib/lanes/delivery.d.ts +7 -3
- package/dist/lib/lanes/delivery.js +18 -8
- package/dist/lib/lanes/index.d.ts +1 -1
- package/dist/lib/lanes/keep-alive.d.ts +13 -3
- package/dist/lib/lanes/keep-alive.js +29 -5
- package/dist/lib/lanes/outpost-dispatch.d.ts +5 -0
- package/dist/lib/lanes/outpost-dispatch.js +12 -0
- package/dist/lib/lanes/reconcile.d.ts +1 -0
- package/dist/lib/lanes/reconcile.js +358 -38
- package/dist/lib/lanes/spawn.d.ts +1 -0
- package/dist/lib/lanes/spawn.js +20 -10
- package/dist/lib/lanes/store.js +24 -0
- package/dist/lib/lanes/story-ledger.d.ts +2 -0
- package/dist/lib/lanes/story-ledger.js +8 -0
- package/dist/lib/lanes/types.d.ts +26 -4
- package/dist/lib/lanes/types.js +33 -1
- package/dist/lib/lanes/watch.d.ts +1 -0
- package/dist/lib/lanes/watch.js +19 -6
- package/dist/lib/lanes/watcher.js +139 -9
- package/dist/lib/mesh/live/daemon/mqtt-events.d.ts +7 -0
- package/dist/lib/mesh/live/daemon/mqtt-events.js +2 -0
- package/dist/lib/mesh/live/daemon/mqtt-events.test-d.d.ts +2 -0
- package/dist/lib/mesh/live/daemon/mqtt-events.test-d.js +10 -0
- package/dist/lib/mesh/live/daemon/presence.d.ts +2 -2
- package/dist/lib/mesh/live/daemon/run.js +36 -2
- package/dist/lib/mesh/live/daemon/work-subscribe.d.ts +2 -2
- package/dist/lib/search-index/background.d.ts +1 -0
- package/dist/lib/search-index/background.js +1 -0
- package/dist/utils/self-update.js +51 -4
- package/dist/utils/staged-npm-install.d.ts +16 -0
- package/dist/utils/staged-npm-install.js +264 -0
- package/dist/utils/update-command-supervisor.cjs +54 -1
- package/dist/utils/version-gate.d.ts +8 -0
- package/dist/utils/version-gate.js +83 -16
- package/package.json +2 -2
- package/dist/lib/install-path-compatibility-flag.d.ts +0 -9
- package/dist/lib/install-path-compatibility-flag.js +0 -12
package/dist/commands/lanes.js
CHANGED
|
@@ -55,11 +55,11 @@ import { createLaneMesh, emitLaneDecisionEvents, emitLaneTerminalStateEvent, lan
|
|
|
55
55
|
import { sessionIdFromEnv } from "../lib/mesh/live/session-identity.js";
|
|
56
56
|
import { listLaneReviews } from "../lib/lanes/reviews.js";
|
|
57
57
|
import { effectiveAccountHome } from "../lib/lanes/account-home.js";
|
|
58
|
-
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, aliveDirectChildren, assignedChildHireGrants, hireGrantCapacityError, planHireGrantBackfill, terminalChildResumeHireGrantError, withHireGrantAdmission, laneStoryLedgerEnabled, 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, sessionLiveness, tryReadLane, uninstallReconcileTimer, updateLane, updateQuestion, dropboxDir, laneDeliveryMechanism, hasActiveAdmissionReservation, hasActivePendingResume, hasRecordedSessionId, hasEarlierEligibleQueuedLane, laneAdmissionCapReason, providerSupportsSessionResume, providerSessionPortable, sessionNotPortableMessage, withLaneAdmissionLock, withAllLaneAdmissionLocks, readWorkerRunResult, resumeCommandRefusal, withLaneHireLocks, raiseLaneQuestion, sha256Text, } from "../lib/lanes/index.js";
|
|
58
|
+
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, aliveDirectChildren, assignedChildHireGrants, hireGrantCapacityError, planHireGrantBackfill, terminalChildResumeHireGrantError, withHireGrantAdmission, laneStoryLedgerEnabled, 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, sessionNotPortableMessage, withLaneAdmissionLock, withAllLaneAdmissionLocks, readWorkerRunResult, resumeCommandRefusal, withLaneHireLocks, raiseLaneQuestion, sha256Text, } from "../lib/lanes/index.js";
|
|
59
59
|
import { laneStories, storyWarning, } from "../lib/lanes/stories.js";
|
|
60
60
|
import { resolveLaneKeepAlive } from "../lib/lanes/profile.js";
|
|
61
61
|
import { resolveLaneCompany } from "../lib/lanes/lane-company.js";
|
|
62
|
-
import { doneKeepAliveVerdict, hasPendingKeepAliveWork, keepAliveExclusion, LANE_SUPERSESSION_LINEAGE_AWARE_FLAG, loadRetiredLaneIds, } from "../lib/lanes/keep-alive.js";
|
|
62
|
+
import { doneKeepAliveVerdict, hasPendingKeepAliveWork, keepAliveNextCheckAtMs, keepAliveExclusion, LANE_SUPERSESSION_LINEAGE_AWARE_FLAG, LEAD_LANE_KEEP_ALIVE_NEXT_CHECK_FLAG, loadRetiredLaneIds, } from "../lib/lanes/keep-alive.js";
|
|
63
63
|
import { questionBodyAbsPath, resolveQuestionCompanyWithFallback, resolveProjectQuestionCompany, } from "../lib/lanes/questions.js";
|
|
64
64
|
import { assertQuestionBodySecretSafe } from "../lib/lanes/questions.js";
|
|
65
65
|
import { compactionAfterUsage, } from "../lib/lanes/compaction.js";
|
|
@@ -1260,6 +1260,8 @@ async function refreshRemoteLaneMirrors(ctx, hqRoot, records, parentLaneIds, opt
|
|
|
1260
1260
|
unverifiedRemote: new Map(),
|
|
1261
1261
|
missingOnOwner: new Map(),
|
|
1262
1262
|
};
|
|
1263
|
+
const now = ctx.now();
|
|
1264
|
+
const nowMs = now.getTime();
|
|
1263
1265
|
const host = reconcileHostFromCtx(ctx);
|
|
1264
1266
|
if (ctx.env.HQ_LANES_REMOTE_READ === "1" || parentLaneIds?.size === 0)
|
|
1265
1267
|
return result;
|
|
@@ -1282,6 +1284,9 @@ async function refreshRemoteLaneMirrors(ctx, hqRoot, records, parentLaneIds, opt
|
|
|
1282
1284
|
((parentLaneIds === undefined || (lane.senior.kind === "lane" &&
|
|
1283
1285
|
lane.senior.id !== undefined &&
|
|
1284
1286
|
parentLaneIds.has(lane.senior.id))) || options.laneIds?.has(lane.lane_id) === true) &&
|
|
1287
|
+
!(isTerminalLaneState(lane.state) &&
|
|
1288
|
+
lane.terminal_reason === DISPATCH_TERMINAL_REASON &&
|
|
1289
|
+
lane.last_host_report_at === undefined) &&
|
|
1285
1290
|
(options.includeTerminalMirrors === true || !isTerminalLaneState(lane.state)))
|
|
1286
1291
|
.sort((a, b) => {
|
|
1287
1292
|
const terminalOrder = Number(isTerminalLaneState(a.state)) - Number(isTerminalLaneState(b.state));
|
|
@@ -1389,11 +1394,20 @@ async function refreshRemoteLaneMirrors(ctx, hqRoot, records, parentLaneIds, opt
|
|
|
1389
1394
|
result.updated.set(mirror.lane_id, before);
|
|
1390
1395
|
continue;
|
|
1391
1396
|
}
|
|
1397
|
+
const ownerReportAtMs = Date.parse(owner.last_host_report_at ?? "");
|
|
1398
|
+
const ownerReportIsFresh = owner.state === "running" &&
|
|
1399
|
+
Number.isFinite(ownerReportAtMs) &&
|
|
1400
|
+
ownerReportAtMs <= nowMs + 60_000 &&
|
|
1401
|
+
nowMs - ownerReportAtMs < DEAD_HOST_MS;
|
|
1402
|
+
const mirroredReportAt = ownerReportIsFresh
|
|
1403
|
+
? new Date(Math.min(ownerReportAtMs, nowMs)).toISOString()
|
|
1404
|
+
: undefined;
|
|
1392
1405
|
const needsUpdate = before.state !== owner.state ||
|
|
1393
1406
|
before.queued_reason !== (owner.state === "queued" ? owner.queued_reason : undefined) ||
|
|
1394
1407
|
JSON.stringify(before.queued_details) !== JSON.stringify(owner.state === "queued" ? owner.queued_details : undefined) ||
|
|
1395
1408
|
before.ended_at !== endedAt ||
|
|
1396
1409
|
before.envelope_summary !== envelopeSummary ||
|
|
1410
|
+
(mirroredReportAt !== undefined && before.last_host_report_at !== mirroredReportAt) ||
|
|
1397
1411
|
(isTerminalLaneState(owner.state) && (JSON.stringify(before.last_envelope) !== JSON.stringify(owner.last_envelope) ||
|
|
1398
1412
|
before.story_complete !== owner.story_complete ||
|
|
1399
1413
|
before.story_complete_reason !== owner.story_complete_reason ||
|
|
@@ -1420,6 +1434,8 @@ async function refreshRemoteLaneMirrors(ctx, hqRoot, records, parentLaneIds, opt
|
|
|
1420
1434
|
queued_details: owner.state === "queued" ? owner.queued_details : undefined,
|
|
1421
1435
|
ended_at: endedAt,
|
|
1422
1436
|
envelope_summary: envelopeSummary,
|
|
1437
|
+
// Preserve a fresh owner report; clamp small clock skew to the primary's time.
|
|
1438
|
+
...(mirroredReportAt !== undefined ? { last_host_report_at: mirroredReportAt } : {}),
|
|
1423
1439
|
...(isTerminalLaneState(owner.state) ? {
|
|
1424
1440
|
last_envelope: owner.last_envelope,
|
|
1425
1441
|
story_complete: owner.story_complete,
|
|
@@ -4261,10 +4277,29 @@ async function dispatchOutpostLaneCommand(ctx, lane, action, options, behavior =
|
|
|
4261
4277
|
remote.delivery_status === "queued_on_target_account" ||
|
|
4262
4278
|
remote.delivery_status === "queued_on_admission")) {
|
|
4263
4279
|
const ownerLane = dispatchedOwnerLane(ctx, lane.lane_id, outpostId, remote.lane, behavior);
|
|
4280
|
+
const questionCancellation = lane.state === "awaiting_input" && behavior.afterFallbackResume
|
|
4281
|
+
? await behavior.afterFallbackResume()
|
|
4282
|
+
: undefined;
|
|
4283
|
+
const ownerCancelledQuestions = Array.isArray(remote.cancelled_questions)
|
|
4284
|
+
? remote.cancelled_questions.filter((id) => typeof id === "string")
|
|
4285
|
+
: [];
|
|
4286
|
+
const ownerCancellationFailures = Array.isArray(remote.question_cancellation_failures)
|
|
4287
|
+
? remote.question_cancellation_failures
|
|
4288
|
+
: [];
|
|
4264
4289
|
printJson({
|
|
4265
4290
|
...remote,
|
|
4266
4291
|
lane_id: ownerLane.lane_id,
|
|
4267
4292
|
lane: ownerLane,
|
|
4293
|
+
...(questionCancellation === undefined ? {} : {
|
|
4294
|
+
cancelled_questions: [...new Set([
|
|
4295
|
+
...ownerCancelledQuestions,
|
|
4296
|
+
...questionCancellation.cancelled_question_ids,
|
|
4297
|
+
])],
|
|
4298
|
+
question_cancellation_failures: [
|
|
4299
|
+
...ownerCancellationFailures,
|
|
4300
|
+
...questionCancellation.failures,
|
|
4301
|
+
],
|
|
4302
|
+
}),
|
|
4268
4303
|
outpost_id: outpostId,
|
|
4269
4304
|
outpost_command_id: dispatched.commandId,
|
|
4270
4305
|
outpost_transport: "outposts_exec",
|
|
@@ -4281,10 +4316,29 @@ async function dispatchOutpostLaneCommand(ctx, lane, action, options, behavior =
|
|
|
4281
4316
|
descendants.push(syncLaneRecordFromOwner(ctx.hqRoot(), descendantId, outpostId, ownerDescendant, undefined, { allowNonTerminal: true }));
|
|
4282
4317
|
}
|
|
4283
4318
|
}
|
|
4319
|
+
const questionCancellation = action === "resume" && lane.state === "awaiting_input" && behavior.afterFallbackResume
|
|
4320
|
+
? await behavior.afterFallbackResume()
|
|
4321
|
+
: undefined;
|
|
4322
|
+
const ownerCancelledQuestions = Array.isArray(remote.cancelled_questions)
|
|
4323
|
+
? remote.cancelled_questions.filter((id) => typeof id === "string")
|
|
4324
|
+
: [];
|
|
4325
|
+
const ownerCancellationFailures = Array.isArray(remote.question_cancellation_failures)
|
|
4326
|
+
? remote.question_cancellation_failures
|
|
4327
|
+
: [];
|
|
4284
4328
|
printJson({
|
|
4285
4329
|
...remote,
|
|
4286
4330
|
lane_id: ownerLane.lane_id,
|
|
4287
4331
|
lane: ownerLane,
|
|
4332
|
+
...(questionCancellation === undefined ? {} : {
|
|
4333
|
+
cancelled_questions: [...new Set([
|
|
4334
|
+
...ownerCancelledQuestions,
|
|
4335
|
+
...questionCancellation.cancelled_question_ids,
|
|
4336
|
+
])],
|
|
4337
|
+
question_cancellation_failures: [
|
|
4338
|
+
...ownerCancellationFailures,
|
|
4339
|
+
...questionCancellation.failures,
|
|
4340
|
+
],
|
|
4341
|
+
}),
|
|
4288
4342
|
...(action === "reparent"
|
|
4289
4343
|
? { changed_descendant_ids: changedDescendantIds, descendants }
|
|
4290
4344
|
: {}),
|
|
@@ -5009,7 +5063,7 @@ async function refreshOutpostLaneCopiesForShow(ctx, lane, localLanes) {
|
|
|
5009
5063
|
const refreshedLane = refreshed[0] ?? null;
|
|
5010
5064
|
return refreshedLane ?? lane;
|
|
5011
5065
|
}
|
|
5012
|
-
async function runShow(ctx, laneId) {
|
|
5066
|
+
async function runShow(ctx, laneId, hasPendingQuestionForLane) {
|
|
5013
5067
|
const hqRoot = ctx.hqRoot();
|
|
5014
5068
|
const initialLane = readLane(hqRoot, laneId);
|
|
5015
5069
|
const initialLanes = loadAllLanes(hqRoot);
|
|
@@ -5023,10 +5077,24 @@ async function runShow(ctx, laneId) {
|
|
|
5023
5077
|
? { ...storedLane, state: "remote", remote_state: storedLane.state, queued_reason: undefined, queued_details: undefined }
|
|
5024
5078
|
: storedLane;
|
|
5025
5079
|
const askDeliveryFailed = readJsonFile(askDeliveryFailedPath(hqRoot, laneId));
|
|
5080
|
+
const delivery = storedLane.state === "awaiting_input" &&
|
|
5081
|
+
await hasPendingQuestionForLane(ctx, storedLane)
|
|
5082
|
+
? "questions"
|
|
5083
|
+
: laneDeliveryMechanism(storedLane);
|
|
5026
5084
|
const keepAliveState = resolveLaneKeepAlive(hqRoot, storedLane);
|
|
5027
5085
|
const keepAliveCompany = resolveLaneCompany(hqRoot, storedLane);
|
|
5086
|
+
const now = ctx.now().getTime();
|
|
5087
|
+
const nextCheckAtEnabled = storedLane.state === "done" && storedLane.keep_alive === true
|
|
5088
|
+
? await readLanesRegistryFlag(LEAD_LANE_KEEP_ALIVE_NEXT_CHECK_FLAG, {
|
|
5089
|
+
company: keepAliveCompany.company ?? undefined,
|
|
5090
|
+
env: ctx.env,
|
|
5091
|
+
})
|
|
5092
|
+
: false;
|
|
5093
|
+
const hasPendingWork = storedLane.keep_alive_standing === true || nextCheckAtEnabled
|
|
5094
|
+
? hasPendingKeepAliveWork(hqRoot, storedLane)
|
|
5095
|
+
: false;
|
|
5028
5096
|
const doneStateVerdict = storedLane.state === "done" && storedLane.keep_alive === true
|
|
5029
|
-
? doneKeepAliveVerdict(storedLane, { decision: storedLane.last_envelope?.decision, story_complete: storedLane.story_complete }, storedLane.keep_alive_standing === true &&
|
|
5097
|
+
? doneKeepAliveVerdict(storedLane, { decision: storedLane.last_envelope?.decision, story_complete: storedLane.story_complete, next_check_at: storedLane.last_envelope?.next_check_at }, storedLane.keep_alive_standing === true && hasPendingWork, { nextCheckAtEnabled, hasPendingWork, nowMs: now })
|
|
5030
5098
|
: null;
|
|
5031
5099
|
let managerStatusTarget;
|
|
5032
5100
|
let managerStatusGateError;
|
|
@@ -5048,7 +5116,15 @@ async function runShow(ctx, laneId) {
|
|
|
5048
5116
|
now: ctx.now().getTime(),
|
|
5049
5117
|
});
|
|
5050
5118
|
const laneIsSenior = allLanes.some((item) => item.senior.kind === "lane" && item.senior.id === laneId);
|
|
5051
|
-
const
|
|
5119
|
+
const requestedNextCheckMs = nextCheckAtEnabled && !hasPendingWork
|
|
5120
|
+
? keepAliveNextCheckAtMs(storedLane.last_envelope?.next_check_at, now, Date.parse(storedLane.last_envelope?.at ?? storedLane.updated_at))
|
|
5121
|
+
: undefined;
|
|
5122
|
+
const storedNextEligibleMs = Date.parse(storedLane.keep_alive_next_eligible_at ?? "");
|
|
5123
|
+
const nextEligibleAt = Number.isFinite(storedNextEligibleMs) && requestedNextCheckMs !== undefined
|
|
5124
|
+
? new Date(Math.max(storedNextEligibleMs, requestedNextCheckMs)).toISOString()
|
|
5125
|
+
: requestedNextCheckMs !== undefined
|
|
5126
|
+
? new Date(requestedNextCheckMs).toISOString()
|
|
5127
|
+
: storedLane.keep_alive_next_eligible_at ?? null;
|
|
5052
5128
|
const reviewListing = laneIsSenior
|
|
5053
5129
|
? listLaneReviews(hqRoot, laneId)
|
|
5054
5130
|
: { reviews: [], skipped: 0 };
|
|
@@ -5075,7 +5151,7 @@ async function runShow(ctx, laneId) {
|
|
|
5075
5151
|
stalled_at: lane.stalled_at ?? null,
|
|
5076
5152
|
stalled_reason: lane.stalled_reason ?? null,
|
|
5077
5153
|
ask_delivery_failed: askDeliveryFailed,
|
|
5078
|
-
delivery
|
|
5154
|
+
delivery,
|
|
5079
5155
|
provider_session_id: lane.provider_session_id ?? null,
|
|
5080
5156
|
provider_session_id_absent: lane.provider_session_id_absent ?? null,
|
|
5081
5157
|
envelope_repair_mode: lane.envelope_repair?.mode ?? null,
|
|
@@ -5108,7 +5184,7 @@ async function runShow(ctx, laneId) {
|
|
|
5108
5184
|
consecutive_auto_resumes: lane.keep_alive_resume_count ?? 0,
|
|
5109
5185
|
last_auto_resume_at: lane.keep_alive_last_resume_at ?? null,
|
|
5110
5186
|
keep_alive_history: lane.keep_alive_history ?? [],
|
|
5111
|
-
next_eligible_at:
|
|
5187
|
+
next_eligible_at: nextEligibleAt,
|
|
5112
5188
|
exhausted_at: lane.keep_alive_exhausted_at ?? null,
|
|
5113
5189
|
},
|
|
5114
5190
|
manager_status_gate: managerStatusGateError !== undefined
|
|
@@ -5134,6 +5210,7 @@ async function runShow(ctx, laneId) {
|
|
|
5134
5210
|
escalated: blockerFreshness.stale.length > 0 &&
|
|
5135
5211
|
storedLane.blocker_freshness_state?.escalated_fingerprint ===
|
|
5136
5212
|
blockerFreshnessFingerprint(blockerFreshness.stale),
|
|
5213
|
+
undeliverable_reason: storedLane.blocker_freshness_state?.undeliverable_reason ?? null,
|
|
5137
5214
|
}
|
|
5138
5215
|
: null,
|
|
5139
5216
|
kr_freshness: krFreshness,
|
|
@@ -7807,6 +7884,7 @@ export function registerLanesCommand(parent, deps = {}) {
|
|
|
7807
7884
|
assertQuestionAnswerAuthorized,
|
|
7808
7885
|
assertQuestionCompanyMatchesLane,
|
|
7809
7886
|
casUpdateQuestion,
|
|
7887
|
+
casUpdateLane,
|
|
7810
7888
|
effectiveLaneQuestionCompany,
|
|
7811
7889
|
emitLaneMeshEvent,
|
|
7812
7890
|
groupQuestionLanesByCompany,
|
|
@@ -7838,6 +7916,7 @@ export function registerLanesCommand(parent, deps = {}) {
|
|
|
7838
7916
|
});
|
|
7839
7917
|
const messageHandlerDeps = {
|
|
7840
7918
|
onLaneWorkerStarted: deps.onLaneWorkerStarted,
|
|
7919
|
+
onLaneMessageQueued: deps.onLaneMessageQueued,
|
|
7841
7920
|
archiveResumeMessage,
|
|
7842
7921
|
assertLaneRunsHereForAdmission,
|
|
7843
7922
|
assertLaneRunsHereForOperatorMutation,
|
|
@@ -7856,6 +7935,8 @@ export function registerLanesCommand(parent, deps = {}) {
|
|
|
7856
7935
|
recordResumeSetup,
|
|
7857
7936
|
resolveCommandLane: (commandCtx, laneId) => resolveCommandLaneTarget(commandCtx, laneId),
|
|
7858
7937
|
resolveActiveQuestionCompany,
|
|
7938
|
+
hasPendingQuestionForLane: questionHandlers.hasPendingQuestionForLane,
|
|
7939
|
+
cancelFallbackQuestionsForLane: questionHandlers.cancelFallbackQuestionsForLane,
|
|
7859
7940
|
resolveResumeProfile,
|
|
7860
7941
|
selectAutomaticClaudeResumeAccount,
|
|
7861
7942
|
validateResumeProfileModel,
|
|
@@ -7881,6 +7962,8 @@ export function registerLanesCommand(parent, deps = {}) {
|
|
|
7881
7962
|
printJson,
|
|
7882
7963
|
recordResumeSetup,
|
|
7883
7964
|
resolveCommandLane: (commandCtx, laneId) => resolveCommandLaneTarget(commandCtx, laneId),
|
|
7965
|
+
hasPendingQuestionForLane: questionHandlers.hasPendingQuestionForLane,
|
|
7966
|
+
cancelFallbackQuestionsForLane: questionHandlers.cancelFallbackQuestionsForLane,
|
|
7884
7967
|
resolveResumeProfile,
|
|
7885
7968
|
selectAutomaticClaudeResumeAccount,
|
|
7886
7969
|
validateResumeProfileModel,
|
|
@@ -8152,7 +8235,7 @@ export function registerLanesCommand(parent, deps = {}) {
|
|
|
8152
8235
|
.description("Show one lane")
|
|
8153
8236
|
.argument("<lane-id>", "Lane id")
|
|
8154
8237
|
.action(wrap(async (laneId) => {
|
|
8155
|
-
await runShow(ctx, laneId);
|
|
8238
|
+
await runShow(ctx, laneId, questionHandlers.hasPendingQuestionForLane);
|
|
8156
8239
|
}));
|
|
8157
8240
|
const stories = lanes
|
|
8158
8241
|
.command("story")
|
|
@@ -8289,7 +8372,7 @@ export function registerLanesCommand(parent, deps = {}) {
|
|
|
8289
8372
|
}));
|
|
8290
8373
|
lanes
|
|
8291
8374
|
.command("resume")
|
|
8292
|
-
.description("Resume an interrupted, done or blocked lane
|
|
8375
|
+
.description("Resume an interrupted, done or blocked lane, or an awaiting_input lane with no answerable question; recover failed lanes with the senior-only --from-failed option")
|
|
8293
8376
|
.argument("<lane-id>", "Lane id")
|
|
8294
8377
|
.option("--text <msg>", "Operator message for the resumed turn")
|
|
8295
8378
|
.option("--from-failed", "Explicitly resume a failed lane as its senior")
|
|
@@ -3,6 +3,25 @@ import { readFile } from "node:fs/promises";
|
|
|
3
3
|
import { ensureCognitoToken } from "../utils/cognito-session.js";
|
|
4
4
|
import { vaultApiFetch, getCompanyUid, resolveCallerPersonUid, } from "../utils/vault-api.js";
|
|
5
5
|
import { isPlanGateError, planGateErrorFromPayload, } from "../utils/plan-gate-error.js";
|
|
6
|
+
// Two minutes gives a large transcript time to arrive over a slow connection,
|
|
7
|
+
// while keeping the interactive command from waiting indefinitely.
|
|
8
|
+
const MEETING_DOCUMENT_DOWNLOAD_TIMEOUT_MS = 120_000;
|
|
9
|
+
async function downloadMeetingDocument(url, readBody) {
|
|
10
|
+
const signal = AbortSignal.timeout(MEETING_DOCUMENT_DOWNLOAD_TIMEOUT_MS);
|
|
11
|
+
try {
|
|
12
|
+
const response = await fetch(url, { signal });
|
|
13
|
+
if (!response.ok)
|
|
14
|
+
return { response, ok: false };
|
|
15
|
+
return { response, ok: true, body: await readBody(response) };
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
if ((signal.aborted && signal.reason instanceof DOMException && signal.reason.name === "TimeoutError") ||
|
|
19
|
+
(err instanceof DOMException && err.name === "TimeoutError")) {
|
|
20
|
+
throw new Error(`Meeting document download timed out after ${MEETING_DOCUMENT_DOWNLOAD_TIMEOUT_MS / 1000} seconds. Check your network connection and try again.`);
|
|
21
|
+
}
|
|
22
|
+
throw err;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
6
25
|
function formatDuration(seconds) {
|
|
7
26
|
const h = Math.floor(seconds / 3600);
|
|
8
27
|
const m = Math.floor((seconds % 3600) / 60);
|
|
@@ -495,12 +514,12 @@ export function registerMeetingsCommand(program) {
|
|
|
495
514
|
console.error(chalk.red("No document available for this meeting."));
|
|
496
515
|
process.exit(1);
|
|
497
516
|
}
|
|
498
|
-
const
|
|
499
|
-
if (!
|
|
500
|
-
console.error(chalk.red(`Failed to download meeting document (${
|
|
517
|
+
const download = await downloadMeetingDocument(documentUrl, (response) => response.text());
|
|
518
|
+
if (!download.ok) {
|
|
519
|
+
console.error(chalk.red(`Failed to download meeting document (${download.response.status})`));
|
|
501
520
|
process.exit(1);
|
|
502
521
|
}
|
|
503
|
-
const markdown =
|
|
522
|
+
const markdown = download.body;
|
|
504
523
|
if (meetings.opts().json) {
|
|
505
524
|
console.log(JSON.stringify({ meetingId: detail.meetingId, sourceShape: "markdown", markdown }, null, 2));
|
|
506
525
|
return;
|
|
@@ -513,12 +532,12 @@ export function registerMeetingsCommand(program) {
|
|
|
513
532
|
console.error(chalk.red("No document URL available for this meeting."));
|
|
514
533
|
process.exit(1);
|
|
515
534
|
}
|
|
516
|
-
const
|
|
517
|
-
if (!
|
|
518
|
-
console.error(chalk.red(`Failed to download meeting document (${
|
|
535
|
+
const download = await downloadMeetingDocument(detail.documentUrl, (response) => response.json());
|
|
536
|
+
if (!download.ok) {
|
|
537
|
+
console.error(chalk.red(`Failed to download meeting document (${download.response.status})`));
|
|
519
538
|
process.exit(1);
|
|
520
539
|
}
|
|
521
|
-
const doc =
|
|
540
|
+
const doc = download.body;
|
|
522
541
|
if (meetings.opts().json) {
|
|
523
542
|
console.log(JSON.stringify(doc.transcript, null, 2));
|
|
524
543
|
return;
|
|
@@ -573,12 +592,12 @@ export function registerMeetingsCommand(program) {
|
|
|
573
592
|
console.log(chalk.yellow("No notes available for this meeting."));
|
|
574
593
|
return;
|
|
575
594
|
}
|
|
576
|
-
const
|
|
577
|
-
if (!
|
|
578
|
-
console.error(chalk.red(`Failed to download meeting document (${
|
|
595
|
+
const download = await downloadMeetingDocument(documentUrl, (response) => response.text());
|
|
596
|
+
if (!download.ok) {
|
|
597
|
+
console.error(chalk.red(`Failed to download meeting document (${download.response.status})`));
|
|
579
598
|
process.exit(1);
|
|
580
599
|
}
|
|
581
|
-
const markdown =
|
|
600
|
+
const markdown = download.body;
|
|
582
601
|
if (meetings.opts().json) {
|
|
583
602
|
console.log(JSON.stringify({ meetingId: detail.meetingId, sourceShape: "markdown", markdown }, null, 2));
|
|
584
603
|
return;
|
|
@@ -591,12 +610,12 @@ export function registerMeetingsCommand(program) {
|
|
|
591
610
|
console.error(chalk.red("No document URL available for this meeting."));
|
|
592
611
|
process.exit(1);
|
|
593
612
|
}
|
|
594
|
-
const
|
|
595
|
-
if (!
|
|
596
|
-
console.error(chalk.red(`Failed to download meeting document (${
|
|
613
|
+
const download = await downloadMeetingDocument(detail.documentUrl, (response) => response.json());
|
|
614
|
+
if (!download.ok) {
|
|
615
|
+
console.error(chalk.red(`Failed to download meeting document (${download.response.status})`));
|
|
597
616
|
process.exit(1);
|
|
598
617
|
}
|
|
599
|
-
const doc =
|
|
618
|
+
const doc = download.body;
|
|
600
619
|
if (meetings.opts().json) {
|
|
601
620
|
console.log(JSON.stringify(doc.notes, null, 2));
|
|
602
621
|
return;
|
package/dist/commands/monitor.js
CHANGED
|
@@ -278,6 +278,7 @@ async function startMonitor(options, argv = []) {
|
|
|
278
278
|
cwd: meta.cwd,
|
|
279
279
|
env: { ...env, HQ_ROOT: hqRoot },
|
|
280
280
|
detached: true,
|
|
281
|
+
windowsHide: true,
|
|
281
282
|
stdio: "ignore",
|
|
282
283
|
});
|
|
283
284
|
child.once("error", (err) => {
|
|
@@ -347,7 +348,21 @@ async function showMonitor(id, json) {
|
|
|
347
348
|
const state = stateForDisplay(hqRoot, id, readJson(statePath(hqRoot, id)));
|
|
348
349
|
if (!meta || !state)
|
|
349
350
|
throw new LanesError("monitor_not_found", `Monitor ${id} was not found.`, { monitor_id: id });
|
|
350
|
-
const events =
|
|
351
|
+
const events = [];
|
|
352
|
+
for (const line of fs.readFileSync(path.join(dir, "events.jsonl"), "utf8").split("\n")) {
|
|
353
|
+
if (!line.trim())
|
|
354
|
+
continue;
|
|
355
|
+
let parsed;
|
|
356
|
+
try {
|
|
357
|
+
parsed = JSON.parse(line);
|
|
358
|
+
}
|
|
359
|
+
catch {
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed))
|
|
363
|
+
continue;
|
|
364
|
+
events.push(parsed);
|
|
365
|
+
}
|
|
351
366
|
if (json)
|
|
352
367
|
printJson({ meta, state, events });
|
|
353
368
|
else
|
|
@@ -712,7 +727,7 @@ export async function runSupervisor(id, deps = {}) {
|
|
|
712
727
|
const args = meta.command !== null ? childArgs : childArgs.slice(1);
|
|
713
728
|
if (!executable)
|
|
714
729
|
throw new LanesError("monitor_command_required", "No command was recorded for this monitor.");
|
|
715
|
-
commandChild = (deps.spawn ?? spawn)(executable, args, { cwd: meta.cwd, env: process.env, detached: true, stdio: ["ignore", "pipe", "pipe"] });
|
|
730
|
+
commandChild = (deps.spawn ?? spawn)(executable, args, { cwd: meta.cwd, env: process.env, detached: true, windowsHide: true, stdio: ["ignore", "pipe", "pipe"] });
|
|
716
731
|
if (commandChild.pid) {
|
|
717
732
|
state = { ...state, child_pid: commandChild.pid, child_starttime: readProcStarttime(commandChild.pid) };
|
|
718
733
|
writeJsonAtomic(statePath(hqRoot, id), state);
|
package/dist/commands/report.js
CHANGED
|
@@ -6,6 +6,7 @@ import * as path from "node:path";
|
|
|
6
6
|
import { ensureCognitoToken, } from "../utils/cognito-session.js";
|
|
7
7
|
import { vaultApiFetch } from "../utils/vault-api.js";
|
|
8
8
|
import { DEPLOY_API_URL, deployApiRequest, deployHeaders } from "../lib/deploy-api.js";
|
|
9
|
+
import { listDeployApps } from "../lib/deploy-apps.js";
|
|
9
10
|
import { listReportTemplates } from "../lib/report-templates.js";
|
|
10
11
|
import { adoptReport, buildReportTarball, conventionReportDir, deployOrgContext, ReportPublishError, plainFailureReason, publishReport, } from "../lib/report-publish.js";
|
|
11
12
|
const REPORT_CHANNEL_STATE_FILE = "report-channels.json";
|
|
@@ -259,8 +260,10 @@ async function orgFor(token, report) {
|
|
|
259
260
|
return deployOrgContext(report, slug);
|
|
260
261
|
}
|
|
261
262
|
export async function findOrCreateApp(token, slug, org) {
|
|
262
|
-
const
|
|
263
|
-
|
|
263
|
+
const apps = await listDeployApps((cursor) => {
|
|
264
|
+
const route = cursor ? `/api/apps?cursor=${encodeURIComponent(cursor)}` : "/api/apps";
|
|
265
|
+
return deployJson(token, route, "GET", undefined, org.headers);
|
|
266
|
+
});
|
|
264
267
|
const mine = (name) => apps.find((app) => !!app && typeof app === "object" && (app.subdomain === name || app.name === name));
|
|
265
268
|
let app;
|
|
266
269
|
for (const name of candidateSlugs(slug)) {
|
|
@@ -45,5 +45,9 @@ export declare function shouldAttemptSelfUpdate(opts: {
|
|
|
45
45
|
check?: boolean;
|
|
46
46
|
selfUpdate?: boolean;
|
|
47
47
|
}): boolean;
|
|
48
|
+
/** Latest release tag for a repo, e.g. `v12.3.0`. Throws on failure. */
|
|
49
|
+
export declare function getLatestReleaseTag(repo: string, token?: string): Promise<string>;
|
|
50
|
+
/** Resolve a tag to its commit SHA, dereferencing annotated tags. */
|
|
51
|
+
export declare function resolveTagSha(repo: string, tag: string, token?: string): Promise<string>;
|
|
48
52
|
export declare function registerRescueCommand(program: Command): void;
|
|
49
53
|
//# sourceMappingURL=rescue.d.ts.map
|
package/dist/commands/rescue.js
CHANGED
|
@@ -53,38 +53,48 @@ function ghHeaders(token) {
|
|
|
53
53
|
headers.Authorization = `Bearer ${token}`;
|
|
54
54
|
return headers;
|
|
55
55
|
}
|
|
56
|
+
// Thirty seconds gives GitHub time to respond while keeping the recovery command bounded.
|
|
57
|
+
const GITHUB_REQUEST_TIMEOUT_MS = 30_000;
|
|
58
|
+
async function githubJsonRequest(url, token) {
|
|
59
|
+
const signal = AbortSignal.timeout(GITHUB_REQUEST_TIMEOUT_MS);
|
|
60
|
+
try {
|
|
61
|
+
const response = await fetch(url, { headers: ghHeaders(token), signal });
|
|
62
|
+
if (!response.ok)
|
|
63
|
+
return { response };
|
|
64
|
+
const body = (await response.json());
|
|
65
|
+
return { response, body };
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
if (signal.aborted)
|
|
69
|
+
throw new Error('GitHub request timed out', { cause: error });
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
56
73
|
/** Latest release tag for a repo, e.g. `v12.3.0`. Throws on failure. */
|
|
57
|
-
async function getLatestReleaseTag(repo, token) {
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
if (!res.ok) {
|
|
62
|
-
throw new Error(`GitHub releases/latest for ${repo}: HTTP ${res.status}`);
|
|
74
|
+
export async function getLatestReleaseTag(repo, token) {
|
|
75
|
+
const { response, body } = await githubJsonRequest(`https://api.github.com/repos/${repo}/releases/latest`, token);
|
|
76
|
+
if (!response.ok) {
|
|
77
|
+
throw new Error(`GitHub releases/latest for ${repo}: HTTP ${response.status}`);
|
|
63
78
|
}
|
|
64
|
-
|
|
65
|
-
if (!body.tag_name)
|
|
79
|
+
if (!body?.tag_name)
|
|
66
80
|
throw new Error(`no tag_name in latest release for ${repo}`);
|
|
67
81
|
return body.tag_name;
|
|
68
82
|
}
|
|
69
83
|
/** Resolve a tag to its commit SHA, dereferencing annotated tags. */
|
|
70
|
-
async function resolveTagSha(repo, tag, token) {
|
|
71
|
-
const refRes = await
|
|
84
|
+
export async function resolveTagSha(repo, tag, token) {
|
|
85
|
+
const { response: refRes, body: ref } = await githubJsonRequest(`https://api.github.com/repos/${repo}/git/ref/tags/${encodeURIComponent(tag)}`, token);
|
|
72
86
|
if (!refRes.ok)
|
|
73
87
|
throw new Error(`git/ref/tags/${tag} for ${repo}: HTTP ${refRes.status}`);
|
|
74
|
-
const
|
|
75
|
-
const obj = ref.object;
|
|
88
|
+
const obj = ref?.object;
|
|
76
89
|
if (!obj?.sha)
|
|
77
90
|
throw new Error(`no object.sha for tag ${tag} in ${repo}`);
|
|
78
91
|
if (obj.type !== 'tag')
|
|
79
92
|
return obj.sha; // lightweight tag → already a commit
|
|
80
93
|
// Annotated tag → dereference to the commit it points at.
|
|
81
|
-
const tagRes = await
|
|
82
|
-
headers: ghHeaders(token),
|
|
83
|
-
});
|
|
94
|
+
const { response: tagRes, body: annotated } = await githubJsonRequest(`https://api.github.com/repos/${repo}/git/tags/${obj.sha}`, token);
|
|
84
95
|
if (!tagRes.ok)
|
|
85
96
|
throw new Error(`git/tags/${obj.sha} for ${repo}: HTTP ${tagRes.status}`);
|
|
86
|
-
|
|
87
|
-
if (!annotated.object?.sha)
|
|
97
|
+
if (!annotated?.object?.sha)
|
|
88
98
|
throw new Error(`annotated tag ${tag} has no target sha`);
|
|
89
99
|
return annotated.object.sha;
|
|
90
100
|
}
|
|
@@ -111,10 +111,10 @@ export declare function assertLinkTargetContained(packDir: string, entryAbs: str
|
|
|
111
111
|
/**
|
|
112
112
|
* List a gzip-compressed tarball's entries via `tar -tvf` (no extraction).
|
|
113
113
|
* argv form, never a shell string, keeps hostile filenames out of shell syntax.
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
114
|
+
* Windows drive paths use a basename relative to the archive directory by
|
|
115
|
+
* default. GNU tar cannot parse `C:\\...` as remote `host:path`, and Windows'
|
|
116
|
+
* bundled bsdtar accepts the same relative argv without GNU-only `--force-local`.
|
|
117
|
+
* Callers can still explicitly opt out with `useRelativeWindowsDrivePath: false`.
|
|
118
118
|
*/
|
|
119
119
|
export declare function listTarball(tarballPath: string, opts?: Pick<SafeExtractOptions, 'platform' | 'useRelativeWindowsDrivePath' | 'execFileSync'>): TarEntry[];
|
|
120
120
|
/**
|
|
@@ -35,7 +35,6 @@
|
|
|
35
35
|
import { execFileSync } from 'child_process';
|
|
36
36
|
import * as fs from 'fs';
|
|
37
37
|
import * as path from 'path';
|
|
38
|
-
import { installPathCompatibilityEnabled } from '../lib/install-path-compatibility-flag.js';
|
|
39
38
|
export const DEFAULT_CAPS = {
|
|
40
39
|
// A content pack is docs/skills/policies/small scripts. 256 MiB is already
|
|
41
40
|
// generous; anything larger is almost certainly a bomb or a mistake.
|
|
@@ -218,14 +217,14 @@ function tarballArgvLocation(tarballPath, platform, useRelativeWindowsDrivePath)
|
|
|
218
217
|
/**
|
|
219
218
|
* List a gzip-compressed tarball's entries via `tar -tvf` (no extraction).
|
|
220
219
|
* argv form, never a shell string, keeps hostile filenames out of shell syntax.
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
*
|
|
220
|
+
* Windows drive paths use a basename relative to the archive directory by
|
|
221
|
+
* default. GNU tar cannot parse `C:\\...` as remote `host:path`, and Windows'
|
|
222
|
+
* bundled bsdtar accepts the same relative argv without GNU-only `--force-local`.
|
|
223
|
+
* Callers can still explicitly opt out with `useRelativeWindowsDrivePath: false`.
|
|
225
224
|
*/
|
|
226
225
|
export function listTarball(tarballPath, opts = {}) {
|
|
227
226
|
const platform = opts.platform ?? process.platform;
|
|
228
|
-
const useRelativeWindowsDrivePath = opts.useRelativeWindowsDrivePath ??
|
|
227
|
+
const useRelativeWindowsDrivePath = opts.useRelativeWindowsDrivePath ?? true;
|
|
229
228
|
const location = tarballArgvLocation(tarballPath, platform, useRelativeWindowsDrivePath);
|
|
230
229
|
const out = (opts.execFileSync ?? execFileSync)('tar', ['-tvf', location.archiveArg], {
|
|
231
230
|
encoding: 'utf-8',
|
|
@@ -330,7 +329,7 @@ export function safeExtractTarball(tarballPath, finalDir, opts = {}) {
|
|
|
330
329
|
}
|
|
331
330
|
// Resolve once so listing and extraction use the same archive location.
|
|
332
331
|
const platform = opts.platform ?? process.platform;
|
|
333
|
-
const useRelativeWindowsDrivePath = opts.useRelativeWindowsDrivePath ??
|
|
332
|
+
const useRelativeWindowsDrivePath = opts.useRelativeWindowsDrivePath ?? true;
|
|
334
333
|
const exec = opts.execFileSync ?? execFileSync;
|
|
335
334
|
const location = tarballArgvLocation(tarballPath, platform, useRelativeWindowsDrivePath);
|
|
336
335
|
// 1. Pre-flight against headers (no bytes written yet).
|
|
@@ -37,6 +37,7 @@ export declare function stopFallback(paths: Pick<AgentKitPaths, "agentDir">, kil
|
|
|
37
37
|
export interface DetachedSpawn {
|
|
38
38
|
(cmd: string, args: string[], opts: {
|
|
39
39
|
detached: true;
|
|
40
|
+
windowsHide: true;
|
|
40
41
|
stdio: ["ignore", number, number];
|
|
41
42
|
env: NodeJS.ProcessEnv;
|
|
42
43
|
cwd: string;
|
|
@@ -113,6 +113,7 @@ export function startFallback(paths, host, deps = {}) {
|
|
|
113
113
|
try {
|
|
114
114
|
const child = doSpawn(cmd, args, {
|
|
115
115
|
detached: true,
|
|
116
|
+
windowsHide: true,
|
|
116
117
|
stdio: ["ignore", fd, fd],
|
|
117
118
|
env: { ...(deps.env ?? process.env), HOME: host.home, ...kitServiceEnv(paths) },
|
|
118
119
|
cwd: paths.agentDir,
|
package/dist/lib/bot/daemon.d.ts
CHANGED
|
@@ -78,7 +78,11 @@ export interface BotDaemonDeps {
|
|
|
78
78
|
*/
|
|
79
79
|
bootout?: "inline" | "detached";
|
|
80
80
|
/** Injectable detached spawn, so tests never reach the real service manager. */
|
|
81
|
-
spawnDetached?: (command: string, args: string[]
|
|
81
|
+
spawnDetached?: (command: string, args: string[], options: {
|
|
82
|
+
detached: true;
|
|
83
|
+
windowsHide: true;
|
|
84
|
+
stdio: "ignore";
|
|
85
|
+
}) => void;
|
|
82
86
|
/**
|
|
83
87
|
* Called once the removal is durable (plist gone) but before the bootout —
|
|
84
88
|
* the last moment a self-booting-out caller is still alive to say anything.
|
package/dist/lib/bot/daemon.js
CHANGED
|
@@ -149,8 +149,8 @@ function shQuote(value) {
|
|
|
149
149
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
150
150
|
}
|
|
151
151
|
/** Detached `launchctl bootout` that outlives the process asking for it. */
|
|
152
|
-
function defaultSpawnDetached(command, args) {
|
|
153
|
-
const child = spawn(command, args,
|
|
152
|
+
function defaultSpawnDetached(command, args, options) {
|
|
153
|
+
const child = spawn(command, args, options);
|
|
154
154
|
child.unref();
|
|
155
155
|
}
|
|
156
156
|
/**
|
|
@@ -165,7 +165,7 @@ function defaultSpawnDetached(command, args) {
|
|
|
165
165
|
function bootoutLaunchd(p, deps) {
|
|
166
166
|
const target = `${launchdDomain(deps.uid)}/${p.label}`;
|
|
167
167
|
if (deps.bootout === "detached") {
|
|
168
|
-
(deps.spawnDetached ?? defaultSpawnDetached)("/bin/sh", ["-c", `sleep 1; exec launchctl bootout ${shQuote(target)}`]);
|
|
168
|
+
(deps.spawnDetached ?? defaultSpawnDetached)("/bin/sh", ["-c", `sleep 1; exec launchctl bootout ${shQuote(target)}`], { detached: true, windowsHide: true, stdio: "ignore" });
|
|
169
169
|
return;
|
|
170
170
|
}
|
|
171
171
|
deps.launchctl?.(["bootout", target]);
|
|
@@ -336,7 +336,7 @@ export function healBotDaemonNodeFromJob(p, deps = {}) {
|
|
|
336
336
|
`for d in 0 1 2 4; do sleep $d; launchctl bootstrap ${shQuote(domain)} ${shQuote(p.plistPath)} >/dev/null 2>&1 && exit 0; done`,
|
|
337
337
|
"exit 1",
|
|
338
338
|
].join("; ");
|
|
339
|
-
(deps.spawnDetached ?? defaultSpawnDetached)("/bin/sh", ["-c", script]);
|
|
339
|
+
(deps.spawnDetached ?? defaultSpawnDetached)("/bin/sh", ["-c", script], { detached: true, windowsHide: true, stdio: "ignore" });
|
|
340
340
|
return fileNeedsRewrite ? { from: file.nodeBinary, to: fileTarget } : { from: loaded, to: loadedTarget };
|
|
341
341
|
}
|
|
342
342
|
/** `program = …` of the loaded job (`launchctl print`), or null when not loaded or unknown. */
|
|
@@ -110,7 +110,7 @@ export function runSoftTimeout(parsed, deps = {}) {
|
|
|
110
110
|
// orphaned grandchild (e.g. run-project's builder) keeps running after the
|
|
111
111
|
// wrapper returns. stdio inherited so the command's stdin/stdout/stderr —
|
|
112
112
|
// including piped or interactive input — pass through unchanged.
|
|
113
|
-
const child = spawnFn(command[0], command.slice(1), { stdio: "inherit", detached: true });
|
|
113
|
+
const child = spawnFn(command[0], command.slice(1), { stdio: "inherit", detached: true, windowsHide: true });
|
|
114
114
|
let capped = false;
|
|
115
115
|
let ticks = 0;
|
|
116
116
|
// Signal the child's process group when we can (detached leader), else the
|