@indigoai-us/hq-cli 5.345.174 → 5.345.176
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 +29 -0
- package/dist/command-catalog.generated.d.ts +65 -0
- package/dist/command-catalog.generated.js +80 -0
- package/dist/commands/bot.d.ts +28 -2
- package/dist/commands/bot.js +32 -6
- package/dist/commands/daemon.js +16 -0
- package/dist/commands/dm.d.ts +7 -0
- package/dist/commands/dm.js +16 -4
- package/dist/commands/lanes-loop.d.ts +4 -1
- package/dist/commands/lanes-loop.js +23 -4
- package/dist/commands/lanes-shared.d.ts +5 -0
- package/dist/commands/lanes.d.ts +5 -7
- package/dist/commands/lanes.js +65 -5
- package/dist/commands/sync-core.d.ts +17 -0
- package/dist/commands/sync-core.js +46 -5
- package/dist/commands/sync-manifest.d.ts +6 -0
- package/dist/commands/sync-manifest.js +13 -0
- package/dist/hq-cloud-sync-runner/bin/sync-runner.js +448 -226
- package/dist/hq-cloud-sync-runner/package.json +1 -1
- package/dist/lib/bot/cli-update.d.ts +36 -0
- package/dist/lib/bot/cli-update.js +160 -42
- package/dist/lib/bot/run.d.ts +23 -0
- package/dist/lib/bot/run.js +174 -23
- package/dist/lib/bot/runtime/claude.d.ts +7 -1
- package/dist/lib/bot/runtime/claude.js +29 -0
- package/dist/lib/bot/runtime/codex.d.ts +5 -0
- package/dist/lib/bot/runtime/codex.js +13 -0
- package/dist/lib/bot/runtime/index.js +18 -2
- package/dist/lib/bot/runtime/types.d.ts +23 -0
- package/dist/lib/bot/runtime/types.js +13 -0
- package/dist/lib/bot/runtime-failure.d.ts +17 -6
- package/dist/lib/bot/runtime-failure.js +54 -9
- package/dist/lib/bot/runtime-sign-in-check.d.ts +32 -0
- package/dist/lib/bot/runtime-sign-in-check.js +53 -0
- package/dist/lib/bot/runtime-sign-in.d.ts +32 -3
- package/dist/lib/bot/runtime-sign-in.js +17 -2
- package/dist/lib/bot/status.d.ts +35 -1
- package/dist/lib/bot/status.js +28 -0
- package/dist/lib/daemon/catalog.d.ts +4 -0
- package/dist/lib/daemon/catalog.js +47 -19
- package/dist/lib/daemon/run.js +1 -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/loop-disposition.d.ts +44 -0
- package/dist/lib/lanes/loop-disposition.js +302 -0
- package/dist/lib/lanes/loop.d.ts +16 -3
- package/dist/lib/lanes/loop.js +167 -60
- package/dist/lib/lanes/reconcile.d.ts +5 -0
- package/dist/lib/lanes/reconcile.js +79 -4
- package/dist/lib/mesh/live/daemon/work-subscribe.d.ts +3 -0
- package/dist/lib/mesh/live/daemon/work-subscribe.js +41 -12
- package/dist/lib/sync-core/engine-home.d.ts +11 -4
- package/dist/lib/sync-core/engine-home.js +22 -21
- package/dist/lib/sync-core/manifest-scopes.d.ts +15 -0
- package/dist/lib/sync-core/manifest-scopes.js +26 -0
- package/dist/lib/sync-core/reconcile.d.ts +2 -2
- package/dist/lib/sync-core/reconcile.js +70 -46
- package/dist/lib/sync-core/release.d.ts +2 -2
- package/dist/lib/sync-core/release.js +31 -6
- package/dist/lib/sync-core/rollout-state.d.ts +6 -3
- package/dist/lib/sync-core/rollout-state.js +26 -22
- package/package.json +8 -8
package/dist/commands/lanes.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readSessionCompanyLock } from "../lib/session-company-lock.js";
|
|
1
2
|
/**
|
|
2
3
|
* `hq lanes` — worker lane management and decision queue (spec §5–§7, §10–§11).
|
|
3
4
|
*
|
|
@@ -50,7 +51,7 @@ import { LANE_FOCUS_GUARD_FLAG, LANE_FOCUS_DASHBOARD_FLAG, LANE_HIRE_GRANT_FINIS
|
|
|
50
51
|
import { applyContinuitySettings, findWorkerContinuityLane, sameLaneHost, workerContinuityPrompt } from "../lib/lanes/worker-continuity.js";
|
|
51
52
|
import { localHostIdsReferToSameMachine, observeLocalHostIdentity, resolveLocalHostIdentity } from "../lib/lanes/local-host-identity.js";
|
|
52
53
|
import { commitIdleLoopStop, enqueueLoopEnvelope, interruptLoopQueue, interruptLoopQueueForStory, loopControllerIsAlive, loopLaneIsIdle, loopStatus, validateLoopEnvelope } from "../lib/lanes/loop.js";
|
|
53
|
-
import { LOOP_CONTROLLER_RECOVERY_REASON, LOOP_STALL_RESTART_REASON, runLaneLoopController, startLaneLoopController } from "./lanes-loop.js";
|
|
54
|
+
import { LOOP_CONTROLLER_RECOVERY_REASON, LOOP_STALL_RESTART_REASON, runLaneLoopController, startLaneLoopController, startLaneLoopControllerWithAdmissionLockHeld } from "./lanes-loop.js";
|
|
54
55
|
import { configuredCodexRolloutHomes, retainCodexRollouts } from "../lib/lanes/disk-guard.js";
|
|
55
56
|
import { isControllerAliasOutpostId } from "../lib/lanes/admission-host.js";
|
|
56
57
|
import { runLeadAutopilotPass } from "../lib/lanes/autopilot.js";
|
|
@@ -76,6 +77,7 @@ import { CompanySelectionError } from "../utils/company-selection-error.js";
|
|
|
76
77
|
import { assertNoSecretText } from "../lib/lanes/text-scan.js";
|
|
77
78
|
import { createLaneMesh, emitLaneDecisionEvents, emitLaneTerminalStateEvent, laneMeshEventIdentity, } from "../lib/lanes/mesh-events.js";
|
|
78
79
|
import { sessionIdFromEnv } from "../lib/mesh/live/session-identity.js";
|
|
80
|
+
import { applyLoopDisposition, loopDispositionSnapshot } from "../lib/lanes/loop-disposition.js";
|
|
79
81
|
import { listLaneReviews } from "../lib/lanes/reviews.js";
|
|
80
82
|
import { effectiveAccountHome } from "../lib/lanes/account-home.js";
|
|
81
83
|
import { remoteControlSelectionConfig, remoteControlSessionHome } from "../lib/lanes/remote-control-lane.js";
|
|
@@ -446,6 +448,7 @@ function resolveDeps(deps = {}) {
|
|
|
446
448
|
killProcess: deps.killProcess,
|
|
447
449
|
isAlive: deps.isAlive,
|
|
448
450
|
readStarttime: deps.readStarttime,
|
|
451
|
+
controllerProcessIdentity: deps.controllerProcessIdentity,
|
|
449
452
|
watcherLoopDeps: deps.watcherLoopDeps,
|
|
450
453
|
stopSleepMs: deps.stopSleepMs,
|
|
451
454
|
livenessDeps: deps.livenessDeps,
|
|
@@ -544,7 +547,7 @@ function startAdmittedLane(ctx, root, candidate) {
|
|
|
544
547
|
queued_details: undefined,
|
|
545
548
|
admission_reserved_at: undefined,
|
|
546
549
|
}));
|
|
547
|
-
|
|
550
|
+
startLaneLoopControllerWithAdmissionLockHeld(ctx, candidate.lane_id, true);
|
|
548
551
|
return admitted;
|
|
549
552
|
}
|
|
550
553
|
/**
|
|
@@ -4552,7 +4555,7 @@ async function runCreate(ctx, opts) {
|
|
|
4552
4555
|
: { ...current });
|
|
4553
4556
|
started = lane;
|
|
4554
4557
|
if (admission.action === "start")
|
|
4555
|
-
loopControllerPid = startLaneLoopController(ctx, lane.lane_id, true);
|
|
4558
|
+
loopControllerPid = await startLaneLoopController(ctx, lane.lane_id, true);
|
|
4556
4559
|
}
|
|
4557
4560
|
else if (admission.action === "start") {
|
|
4558
4561
|
started = spawnLaneWorker(hqRoot, lane, {
|
|
@@ -9339,12 +9342,23 @@ async function runStop(ctx, laneId, reason, quiet = false) {
|
|
|
9339
9342
|
throw new LanesError("already_terminal", `Loop lane ${lane.lane_id} is already ${lane.state}`, { lane_id: lane.lane_id, state: lane.state });
|
|
9340
9343
|
}
|
|
9341
9344
|
const controllerAlive = loopControllerIsAlive(runDirPath);
|
|
9345
|
+
// An explicitly disposed lane may already contain the requested stop.
|
|
9346
|
+
// Commit that idle stop before enqueue rejects a second stop or any
|
|
9347
|
+
// controller is restarted. This does not certify the abandoned effects.
|
|
9348
|
+
if (!controllerAlive && lane.terminal_details?.recovery_decision) {
|
|
9349
|
+
const finalized = await finalizeIdleLoopStop(ctx, lane);
|
|
9350
|
+
if (finalized) {
|
|
9351
|
+
if (!quiet)
|
|
9352
|
+
printJson({ ok: true, action: "stop", lane_id: lane.lane_id, terminal: true, lane: finalized, external_effects: "not-certified" });
|
|
9353
|
+
return;
|
|
9354
|
+
}
|
|
9355
|
+
}
|
|
9342
9356
|
if (!controllerAlive && lane.state === "running") {
|
|
9343
9357
|
await runInterrupt(ctx, lane.lane_id, "loop controller exited; recovering queued work before stop", { preserveLoopQueue: true, quiet: true, terminalReason: LOOP_CONTROLLER_RECOVERY_REASON });
|
|
9344
9358
|
}
|
|
9345
9359
|
const queued = enqueueLoopEnvelope({ runDir: runDirPath, laneId: lane.lane_id, envelope: { kind: "stop" }, now: ctx.now });
|
|
9346
9360
|
if (!controllerAlive && lane.state !== "queued")
|
|
9347
|
-
startLaneLoopController(ctx, lane.lane_id);
|
|
9361
|
+
await startLaneLoopController(ctx, lane.lane_id);
|
|
9348
9362
|
const finalized = !controllerAlive ? await finalizeIdleLoopStop(ctx, readLane(hqRoot, lane.lane_id)) : undefined;
|
|
9349
9363
|
const admitted = await admitQueuedFromCtx(ctx);
|
|
9350
9364
|
if (!quiet)
|
|
@@ -10483,6 +10497,52 @@ export function registerLanesCommand(parent, deps = {}) {
|
|
|
10483
10497
|
emitTerminal: (lane) => emitLaneTerminalStateSoft(ctx, lane),
|
|
10484
10498
|
});
|
|
10485
10499
|
}));
|
|
10500
|
+
lanes
|
|
10501
|
+
.command("recover-loop")
|
|
10502
|
+
.description("Preview or explicitly fail/abandon uncertain loop effects; never replay work")
|
|
10503
|
+
.argument("<lane-id>", "Loop lane id")
|
|
10504
|
+
.option("--disposition <action>", "fail or abandon; omit for a read-only preview")
|
|
10505
|
+
.option("--lane-version <number>", "Exact lane version from preview")
|
|
10506
|
+
.option("--fingerprint <sha256>", "Exact preview envelope/lease fingerprint")
|
|
10507
|
+
.option("--reason <text>", "Audited reason; does not certify external effects")
|
|
10508
|
+
.option("--attest-owner-investigated", "Attest unknown lease ownership was investigated and no live owner exists")
|
|
10509
|
+
.action(wrap(async (laneId, opts) => {
|
|
10510
|
+
const hqRoot = ctx.hqRoot();
|
|
10511
|
+
const authorize = (lane) => {
|
|
10512
|
+
if (!lane.loop_mode)
|
|
10513
|
+
throw new Error("Not a loop lane");
|
|
10514
|
+
const authorization = callerSeniorAuthorization(ctx, lane.senior);
|
|
10515
|
+
if (!authorization.authorized)
|
|
10516
|
+
throw new Error(`Only the lane's senior may recover it: ${authorization.reason}`);
|
|
10517
|
+
assertLaneRunsHereForAdmission(ctx, lane);
|
|
10518
|
+
const lock = readSessionCompanyLock({ ...ctx.env, HQ_ROOT: hqRoot });
|
|
10519
|
+
const company = (lane.company ?? lane.mesh_company)?.slug ?? "personal";
|
|
10520
|
+
const sessionId = sessionIdFromEnv(ctx.env);
|
|
10521
|
+
if ((sessionId && (!lock || lock.sessionId !== sessionId)) || (lock && !lock.companies.includes(company))) {
|
|
10522
|
+
throw new LanesError("lane_recovery_company_locked", "This session is not bound to the lane's company", { lane_id: lane.lane_id, company });
|
|
10523
|
+
}
|
|
10524
|
+
};
|
|
10525
|
+
const lane = readLane(hqRoot, laneId);
|
|
10526
|
+
authorize(lane);
|
|
10527
|
+
const enabled = await readLanesRegistryFlagFailOpen(LANE_CONDUCT_LOOP_FLAG, { company: lane.company ?? lane.mesh_company, defaultValue: true, env: ctx.env });
|
|
10528
|
+
if (!enabled)
|
|
10529
|
+
throw new Error("Loop lanes are disabled by the kill switch");
|
|
10530
|
+
if (!opts.disposition) {
|
|
10531
|
+
printJson({ ok: true, action: "recover-loop-preview", snapshot: loopDispositionSnapshot(hqRoot, lane), external_effects: "not-certified" });
|
|
10532
|
+
return;
|
|
10533
|
+
}
|
|
10534
|
+
if (opts.disposition !== "fail" && opts.disposition !== "abandon")
|
|
10535
|
+
throw new Error("Disposition must be fail or abandon");
|
|
10536
|
+
assertNoSecretText("recovery reason", opts.reason ?? "");
|
|
10537
|
+
const version = Number(opts.laneVersion);
|
|
10538
|
+
if (!opts.laneVersion || !Number.isSafeInteger(version) || version < 1)
|
|
10539
|
+
throw new Error("An exact preview version is required");
|
|
10540
|
+
const result = await applyLoopDisposition({ hqRoot, laneId, expectedVersion: version,
|
|
10541
|
+
fingerprint: opts.fingerprint ?? "", disposition: opts.disposition,
|
|
10542
|
+
reason: opts.reason ?? "", ownershipAttested: opts.attestOwnerInvestigated,
|
|
10543
|
+
authorize, now: ctx.now });
|
|
10544
|
+
printJson({ ok: true, action: "recover-loop", ...result });
|
|
10545
|
+
}));
|
|
10486
10546
|
lanes
|
|
10487
10547
|
.command("enqueue")
|
|
10488
10548
|
.description("Validate and queue one phase envelope for a loop lane")
|
|
@@ -10526,7 +10586,7 @@ export function registerLanesCommand(parent, deps = {}) {
|
|
|
10526
10586
|
await runInterrupt(ctx, lane.lane_id, "loop controller exited; recovering the queued phase", { preserveLoopQueue: true, quiet: true, terminalReason: LOOP_CONTROLLER_RECOVERY_REASON });
|
|
10527
10587
|
}
|
|
10528
10588
|
const shouldRestart = !controllerAlive && lane.state !== "queued";
|
|
10529
|
-
const controllerPid = shouldRestart ? startLaneLoopController(ctx, lane.lane_id) : undefined;
|
|
10589
|
+
const controllerPid = shouldRestart ? await startLaneLoopController(ctx, lane.lane_id) : undefined;
|
|
10530
10590
|
printJson({ ok: true, action: "enqueue", ...result, lane: lane.lane_id, ...(controllerPid ? { controller_pid: controllerPid } : {}) });
|
|
10531
10591
|
}));
|
|
10532
10592
|
lanes
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { Command } from "commander";
|
|
2
2
|
import { type SyncCoreReconcileResult } from "../lib/sync-core/reconcile.js";
|
|
3
|
+
import { type EngineManifestScope } from "../lib/sync-core/manifest-scopes.js";
|
|
4
|
+
import type { RunSyncManifestOptions, RunSyncManifestOutcome, SyncManifestDeps } from "./sync-manifest.js";
|
|
3
5
|
export interface SyncCoreReconcileCommandOptions {
|
|
4
6
|
hqRoot?: string;
|
|
5
7
|
companiesOnly?: boolean;
|
|
@@ -15,6 +17,21 @@ export interface SyncCoreCommandDeps {
|
|
|
15
17
|
err: (line: string) => void;
|
|
16
18
|
}
|
|
17
19
|
export declare function runSyncCoreReconcile(opts: SyncCoreReconcileCommandOptions, d?: SyncCoreCommandDeps): Promise<SyncCoreReconcileResult>;
|
|
20
|
+
export interface SyncCoreManifestCommandOptions {
|
|
21
|
+
hqRoot?: string;
|
|
22
|
+
json?: boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface SyncCoreManifestDeps extends SyncCoreCommandDeps {
|
|
25
|
+
runManifest?: (options: RunSyncManifestOptions, deps: SyncManifestDeps) => Promise<RunSyncManifestOutcome>;
|
|
26
|
+
manifestDeps?: () => SyncManifestDeps;
|
|
27
|
+
}
|
|
28
|
+
export interface SyncCoreManifestResult {
|
|
29
|
+
scope: EngineManifestScope;
|
|
30
|
+
status: string;
|
|
31
|
+
mode?: string;
|
|
32
|
+
errorKind?: string;
|
|
33
|
+
}
|
|
34
|
+
export declare function runSyncCoreManifest(opts: SyncCoreManifestCommandOptions, d?: SyncCoreManifestDeps): Promise<SyncCoreManifestResult[]>;
|
|
18
35
|
export declare function runSyncCoreStatus(opts: {
|
|
19
36
|
json?: boolean;
|
|
20
37
|
}, d?: SyncCoreCommandDeps): void;
|
|
@@ -4,6 +4,7 @@ import * as path from "node:path";
|
|
|
4
4
|
import { ensureSyncCoreBinary } from "../lib/sync-core/release.js";
|
|
5
5
|
import { readCompanyRolloutFlag } from "../lib/sync-core/flag.js";
|
|
6
6
|
import { reconcileSyncCore } from "../lib/sync-core/reconcile.js";
|
|
7
|
+
import { engineOwnedManifestScopes, manifestScopeArg } from "../lib/sync-core/manifest-scopes.js";
|
|
7
8
|
import { readSyncCoreRolloutState, syncCoreRolloutStatePath } from "../lib/sync-core/rollout-state.js";
|
|
8
9
|
import { readVaultSyncSwitches } from "../lib/vault-sync-switches.js";
|
|
9
10
|
function defaultDeps() {
|
|
@@ -66,6 +67,45 @@ export async function runSyncCoreReconcile(opts, d = defaultDeps()) {
|
|
|
66
67
|
d.out("no vault on this machine is switched to the new sync engine");
|
|
67
68
|
return result;
|
|
68
69
|
}
|
|
70
|
+
export async function runSyncCoreManifest(opts, d = defaultDeps()) {
|
|
71
|
+
const hqRoot = await resolveHqRoot(opts.hqRoot, d.env);
|
|
72
|
+
const scopes = engineOwnedManifestScopes(readVaultSyncSwitches(d.home), readSyncCoreRolloutState(d.home)?.vaults);
|
|
73
|
+
if (scopes.length === 0) {
|
|
74
|
+
if (opts.json)
|
|
75
|
+
d.out("[]");
|
|
76
|
+
else
|
|
77
|
+
d.out("no vault on this machine is owned by the new sync engine; the current sync uploads its own manifests");
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
const manifest = await import("./sync-manifest.js");
|
|
81
|
+
const runManifest = d.runManifest ?? manifest.runSyncManifest;
|
|
82
|
+
const manifestDeps = d.manifestDeps ?? manifest.createDefaultDeps;
|
|
83
|
+
const results = [];
|
|
84
|
+
for (const scope of scopes) {
|
|
85
|
+
const label = manifestScopeArg(scope);
|
|
86
|
+
try {
|
|
87
|
+
const outcome = await runManifest({ scopeArg: manifestScopeArg(scope), hqRoot, respectThrottle: true, json: true, engineOwned: true }, { ...manifestDeps(), log: () => { } });
|
|
88
|
+
const { status, mode, error } = outcome.result;
|
|
89
|
+
results.push({
|
|
90
|
+
scope,
|
|
91
|
+
status,
|
|
92
|
+
...(mode ? { mode } : {}),
|
|
93
|
+
...(error?.kind ? { errorKind: error.kind } : {}),
|
|
94
|
+
});
|
|
95
|
+
if (!opts.json)
|
|
96
|
+
d.out(`${label}: ${status}${mode ? ` (${mode})` : ""}${error?.kind ? ` ${error.kind}` : ""}`);
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
results.push({ scope, status: "failed", errorKind: "command_error" });
|
|
100
|
+
d.err(`${label}: manifest pass failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (opts.json)
|
|
104
|
+
d.out(JSON.stringify(results, null, 2));
|
|
105
|
+
if (results.some((r) => r.status === "failed"))
|
|
106
|
+
process.exitCode = 1;
|
|
107
|
+
return results;
|
|
108
|
+
}
|
|
69
109
|
export function runSyncCoreStatus(opts, d = defaultDeps()) {
|
|
70
110
|
const state = readSyncCoreRolloutState(d.home);
|
|
71
111
|
if (opts.json) {
|
|
@@ -83,15 +123,16 @@ export function runSyncCoreStatus(opts, d = defaultDeps()) {
|
|
|
83
123
|
for (const vault of state.vaults) {
|
|
84
124
|
const legacyOff = vault.kind === "personal" ? switches.personalOff : switches.offCompanies.has(vault.slug ?? "");
|
|
85
125
|
d.out(` ${vault.uid} ${vault.kind}${vault.slug ? ` ${vault.slug}` : ""} ${vault.phase === "full" ? "full" : "read-only"} legacy sync ${legacyOff ? "off" : "still on"} home ${vault.engineHome}`);
|
|
86
|
-
if (vault.
|
|
87
|
-
const
|
|
88
|
-
d.out(`
|
|
126
|
+
if (vault.firstCycle) {
|
|
127
|
+
const c = vault.firstCycle;
|
|
128
|
+
d.out(` first full cycle ${c.at}: ${c.planned} planned, ${c.completed} completed, ${c.pending} pending, ${c.held} held, ${c.notApplied} not applied`);
|
|
129
|
+
d.out(` listed work${c.truncated ? ` (first ${c.listed} items only)` : ""}: ${c.uploads} uploads, ${c.downloads} downloads, ${c.deletes} deletes, ${c.conflicts} conflict or merge copies, ${c.deferred} deferred`);
|
|
89
130
|
}
|
|
90
|
-
if (vault.hold)
|
|
91
|
-
d.out(` held read-only: ${vault.hold}`);
|
|
92
131
|
}
|
|
93
132
|
for (const skip of state.skipped ?? [])
|
|
94
133
|
d.out(` ${skip.uid} skipped on this machine: ${skip.reason}`);
|
|
134
|
+
for (const hold of state.held ?? [])
|
|
135
|
+
d.out(` ${hold.uid} held: ${hold.reason}`);
|
|
95
136
|
if (state.vaults.length === 0)
|
|
96
137
|
d.out(" no vault is switched to the new sync engine");
|
|
97
138
|
}
|
|
@@ -110,7 +110,13 @@ export interface RunSyncManifestOptions {
|
|
|
110
110
|
* `resend_full_scheduled` too, and none of those uploaded anything.
|
|
111
111
|
*/
|
|
112
112
|
json?: boolean;
|
|
113
|
+
engineOwned?: boolean;
|
|
113
114
|
}
|
|
115
|
+
export declare const SYNC_CORE_OWNS_SCOPE_LEDGER_CLASS = "SyncCoreOwnsScope";
|
|
116
|
+
export declare class SyncCoreOwnsScope extends Error {
|
|
117
|
+
constructor();
|
|
118
|
+
}
|
|
119
|
+
export declare function engineOwnedLedger(): never;
|
|
114
120
|
/** The machine-readable outcome `--json` prints. Fixed tokens only, no paths. */
|
|
115
121
|
export interface ManifestOutcomeLine {
|
|
116
122
|
status: string;
|
|
@@ -64,6 +64,16 @@ export const MANIFEST_UPLOAD_PATH = "/v1/sync-manifest/upload";
|
|
|
64
64
|
export { MANIFEST_DISABLED_ENV, manifestUploadsDisabled };
|
|
65
65
|
/** The literal `--scope` value that selects the personal (non-company) tree. */
|
|
66
66
|
export const PERSONAL_SCOPE = "personal";
|
|
67
|
+
export const SYNC_CORE_OWNS_SCOPE_LEDGER_CLASS = "SyncCoreOwnsScope";
|
|
68
|
+
export class SyncCoreOwnsScope extends Error {
|
|
69
|
+
constructor() {
|
|
70
|
+
super("the new sync engine owns this scope; the legacy journal is not its ledger");
|
|
71
|
+
this.name = SYNC_CORE_OWNS_SCOPE_LEDGER_CLASS;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
export function engineOwnedLedger() {
|
|
75
|
+
throw new SyncCoreOwnsScope();
|
|
76
|
+
}
|
|
67
77
|
/** Build the `--json` line for one pass. The free-text error detail is left out. */
|
|
68
78
|
export function manifestOutcomeLine(result) {
|
|
69
79
|
return {
|
|
@@ -333,6 +343,7 @@ export async function runSyncManifest(options, deps) {
|
|
|
333
343
|
dryRun: print,
|
|
334
344
|
forceFull: options.full === true,
|
|
335
345
|
ignoreThrottle: options.respectThrottle !== true,
|
|
346
|
+
...(options.engineOwned === true ? { listJournalsImpl: engineOwnedLedger } : {}),
|
|
336
347
|
});
|
|
337
348
|
if (print) {
|
|
338
349
|
deps.log(JSON.stringify(result.chunks ?? [], null, 2));
|
|
@@ -358,6 +369,7 @@ export function registerSyncManifestCommand(syncCmd) {
|
|
|
358
369
|
.option("--print", "Build and print the chunks as JSON; upload nothing")
|
|
359
370
|
.option("--json", "Print the pass outcome as one JSON line instead of a summary")
|
|
360
371
|
.option("--respect-throttle", "Honour the 24h per-scope upload throttle instead of forcing a pass (for scheduled callers)")
|
|
372
|
+
.option("--engine-owned", "The new sync engine owns this scope here: build the manifest from disk alone and mark the legacy journal as not its ledger")
|
|
361
373
|
.option("--hq-root <path>", `Local HQ tree root (default: ${DEFAULT_HQ_ROOT})`, DEFAULT_HQ_ROOT)
|
|
362
374
|
.action(async (options) => {
|
|
363
375
|
// Capability gate BEFORE anything else: on a published hq-cloud that
|
|
@@ -376,6 +388,7 @@ export function registerSyncManifestCommand(syncCmd) {
|
|
|
376
388
|
print: options.print,
|
|
377
389
|
respectThrottle: options.respectThrottle,
|
|
378
390
|
json: options.json,
|
|
391
|
+
engineOwned: options.engineOwned,
|
|
379
392
|
}, createDefaultDeps());
|
|
380
393
|
if (outcome.exitCode !== 0)
|
|
381
394
|
process.exit(outcome.exitCode);
|