@indigoai-us/hq-cli 5.345.175 → 5.345.177
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 +25 -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 +29 -6
- package/dist/commands/daemon.js +16 -0
- 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 +21 -0
- package/dist/lib/bot/run.js +127 -10
- 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/types.d.ts +10 -0
- 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 +25 -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
|
@@ -72,10 +72,11 @@ export function writeEngineHome(opts) {
|
|
|
72
72
|
}
|
|
73
73
|
return { engineHome, changed: configChanged || ownedChanged };
|
|
74
74
|
}
|
|
75
|
+
export const SYNC_CORE_STATUS_WORK_LIMIT = 20;
|
|
75
76
|
function count(value) {
|
|
76
77
|
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : 0;
|
|
77
78
|
}
|
|
78
|
-
export function
|
|
79
|
+
export function engineFirstCycle(engineHome, vaultUid, since) {
|
|
79
80
|
let status;
|
|
80
81
|
try {
|
|
81
82
|
status = record(JSON.parse(fs.readFileSync(path.join(engineHome, ".hq", "sync-core", "status.json"), "utf8")));
|
|
@@ -83,30 +84,30 @@ export function engineReadOnlyPlan(engineHome, vaultUid) {
|
|
|
83
84
|
catch {
|
|
84
85
|
return null;
|
|
85
86
|
}
|
|
86
|
-
if (record(status?.mode)?.value !== "
|
|
87
|
+
if (record(status?.mode)?.value !== "full")
|
|
87
88
|
return null;
|
|
88
|
-
const
|
|
89
|
-
if (!
|
|
89
|
+
const cycle = record(status?.last_canary);
|
|
90
|
+
if (!cycle || cycle.vault !== vaultUid || cycle.full_vault !== true)
|
|
90
91
|
return null;
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
if (!run)
|
|
92
|
+
const sinceMs = Date.parse(since);
|
|
93
|
+
if (typeof cycle.at_ms !== "number" || cycle.at_ms <= 0 || !Number.isFinite(sinceMs) || cycle.at_ms < sinceMs)
|
|
94
94
|
return null;
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
const vault = record(e)?.vault;
|
|
98
|
-
return vault === vaultUid || typeof vault !== "string";
|
|
99
|
-
}))
|
|
100
|
-
return null;
|
|
101
|
-
const ops = record(record(run.plan)?.ops_by_action) ?? {};
|
|
95
|
+
const work = (Array.isArray(cycle.work) ? cycle.work : []).map(record).filter((item) => item !== undefined);
|
|
96
|
+
const actions = (...names) => work.filter((item) => names.includes(String(item.action))).length;
|
|
102
97
|
return {
|
|
103
|
-
at: new Date(
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
98
|
+
at: new Date(cycle.at_ms).toISOString(),
|
|
99
|
+
planned: count(cycle.planned),
|
|
100
|
+
completed: count(cycle.completed),
|
|
101
|
+
pending: count(cycle.pending),
|
|
102
|
+
held: count(cycle.held),
|
|
103
|
+
notApplied: count(cycle.not_applied),
|
|
104
|
+
uploads: actions("upload"),
|
|
105
|
+
downloads: actions("download"),
|
|
106
|
+
deletes: actions("delete_local", "delete_remote"),
|
|
107
|
+
conflicts: actions("conflict", "merge"),
|
|
108
|
+
deferred: actions("deferred"),
|
|
109
|
+
listed: work.length,
|
|
110
|
+
truncated: work.length >= SYNC_CORE_STATUS_WORK_LIMIT,
|
|
110
111
|
};
|
|
111
112
|
}
|
|
112
113
|
export function clearEngineStatus(home, vaultUid) {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { VaultSyncSwitches } from "../vault-sync-switches.js";
|
|
2
|
+
export interface EngineManifestVault {
|
|
3
|
+
kind: "personal" | "company";
|
|
4
|
+
slug?: string;
|
|
5
|
+
phase: "warming" | "full";
|
|
6
|
+
}
|
|
7
|
+
export type EngineManifestScope = {
|
|
8
|
+
kind: "personal";
|
|
9
|
+
} | {
|
|
10
|
+
kind: "company";
|
|
11
|
+
slug: string;
|
|
12
|
+
};
|
|
13
|
+
export declare function engineOwnedManifestScopes(switches: Pick<VaultSyncSwitches, "allOff" | "personalOff" | "offCompanies"> | undefined, vaults: readonly EngineManifestVault[] | undefined): EngineManifestScope[];
|
|
14
|
+
export declare function manifestScopeArg(scope: EngineManifestScope): string;
|
|
15
|
+
//# sourceMappingURL=manifest-scopes.d.ts.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export function engineOwnedManifestScopes(switches, vaults) {
|
|
2
|
+
if (!switches || switches.allOff)
|
|
3
|
+
return [];
|
|
4
|
+
let personal = false;
|
|
5
|
+
const companies = new Set();
|
|
6
|
+
for (const vault of vaults ?? []) {
|
|
7
|
+
if (vault.phase !== "full")
|
|
8
|
+
continue;
|
|
9
|
+
if (vault.kind === "personal") {
|
|
10
|
+
if (switches.personalOff)
|
|
11
|
+
personal = true;
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
const slug = vault.slug?.trim().toLowerCase();
|
|
15
|
+
if (slug && switches.offCompanies.has(slug))
|
|
16
|
+
companies.add(slug);
|
|
17
|
+
}
|
|
18
|
+
const scopes = personal ? [{ kind: "personal" }] : [];
|
|
19
|
+
for (const slug of [...companies].sort())
|
|
20
|
+
scopes.push({ kind: "company", slug });
|
|
21
|
+
return scopes;
|
|
22
|
+
}
|
|
23
|
+
export function manifestScopeArg(scope) {
|
|
24
|
+
return scope.kind === "personal" ? "personal" : scope.slug;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=manifest-scopes.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type SyncCoreVault } from "./engine-home.js";
|
|
2
2
|
import { type SyncCoreBinaryInstall } from "./release.js";
|
|
3
3
|
import { type SyncCoreRolloutState } from "./rollout-state.js";
|
|
4
4
|
export interface SyncCoreMembership {
|
|
@@ -26,6 +26,6 @@ export interface SyncCoreReconcileResult {
|
|
|
26
26
|
kept: string[];
|
|
27
27
|
warnings: string[];
|
|
28
28
|
}
|
|
29
|
-
export declare function
|
|
29
|
+
export declare function companyFullHold(vault: SyncCoreVault, release: string): string | undefined;
|
|
30
30
|
export declare function reconcileSyncCore(deps: SyncCoreReconcileDeps): Promise<SyncCoreReconcileResult>;
|
|
31
31
|
//# sourceMappingURL=reconcile.d.ts.map
|
|
@@ -2,28 +2,18 @@ import * as fs from "node:fs";
|
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { readCompaniesManifestMap } from "../work-context/repo-remote.js";
|
|
4
4
|
import { readVaultSyncSwitches, writeVaultSyncSwitches } from "../vault-sync-switches.js";
|
|
5
|
-
import { clearEngineStatus,
|
|
6
|
-
import { SYNC_CORE_COMPANY_FULL_RELEASE,
|
|
5
|
+
import { clearEngineStatus, engineFirstCycle, isSyncCoreVaultUid, removeEngineOwnership, syncCoreEngineHome, writeEngineHome, } from "./engine-home.js";
|
|
6
|
+
import { SYNC_CORE_COMPANY_FULL_RELEASE, SYNC_CORE_RELEASE, companyFullModeAllowed, installedSyncCoreRelease, syncCoreBinaryPath, } from "./release.js";
|
|
7
7
|
import { readSyncCoreSkips } from "./skip.js";
|
|
8
|
-
import { readSyncCoreRolloutState, writeSyncCoreRolloutState, } from "./rollout-state.js";
|
|
8
|
+
import { readSyncCoreRolloutState, syncCoreRolloutStatePath, writeSyncCoreRolloutState, } from "./rollout-state.js";
|
|
9
9
|
function message(error) {
|
|
10
10
|
return error instanceof Error ? error.message : String(error);
|
|
11
11
|
}
|
|
12
|
-
export function
|
|
13
|
-
if (vault.kind
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
if (plan.deletes > SYNC_CORE_PROMOTION_OP_LIMIT) {
|
|
18
|
-
return `the read-only plan would delete ${plan.deletes} paths (limit ${SYNC_CORE_PROMOTION_OP_LIMIT})`;
|
|
19
|
-
}
|
|
20
|
-
if (plan.conflicts > SYNC_CORE_PROMOTION_OP_LIMIT) {
|
|
21
|
-
return `the read-only plan would write ${plan.conflicts} conflict or merge copies (limit ${SYNC_CORE_PROMOTION_OP_LIMIT})`;
|
|
22
|
-
}
|
|
23
|
-
if (plan.uploads > SYNC_CORE_PROMOTION_OP_LIMIT) {
|
|
24
|
-
return `the read-only plan would upload ${plan.uploads} files against ${plan.remoteRows} server rows (limit ${SYNC_CORE_PROMOTION_OP_LIMIT})`;
|
|
25
|
-
}
|
|
26
|
-
return undefined;
|
|
12
|
+
export function companyFullHold(vault, release) {
|
|
13
|
+
if (vault.kind !== "company" || companyFullModeAllowed(release))
|
|
14
|
+
return undefined;
|
|
15
|
+
const needed = SYNC_CORE_COMPANY_FULL_RELEASE ? `engine release ${SYNC_CORE_COMPANY_FULL_RELEASE} or later` : "an engine release cleared for company vaults";
|
|
16
|
+
return `company vaults move to the new sync engine with ${needed} (${release} installed); staying on the current sync`;
|
|
27
17
|
}
|
|
28
18
|
function localCompanies(hqRoot, memberships) {
|
|
29
19
|
const manifest = readCompaniesManifestMap(hqRoot) ?? {};
|
|
@@ -44,13 +34,41 @@ function localCompanies(hqRoot, memberships) {
|
|
|
44
34
|
}
|
|
45
35
|
return candidates;
|
|
46
36
|
}
|
|
37
|
+
function ownedVaultsWithoutState(home, hqRoot) {
|
|
38
|
+
if (!fs.existsSync(syncCoreRolloutStatePath(home)))
|
|
39
|
+
return [];
|
|
40
|
+
const slugByUid = new Map();
|
|
41
|
+
for (const [slug, entry] of Object.entries(readCompaniesManifestMap(hqRoot) ?? {})) {
|
|
42
|
+
if (typeof entry?.cloud_uid === "string" && /^[a-z0-9][a-z0-9_-]*$/.test(slug))
|
|
43
|
+
slugByUid.set(entry.cloud_uid, slug);
|
|
44
|
+
}
|
|
45
|
+
let uids;
|
|
46
|
+
try {
|
|
47
|
+
uids = fs.readdirSync(path.join(home, ".hq", "sync-core", "engines")).filter(isSyncCoreVaultUid);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
const vaults = [];
|
|
53
|
+
for (const uid of uids) {
|
|
54
|
+
const engineHome = syncCoreEngineHome(home, uid);
|
|
55
|
+
if (!fs.existsSync(path.join(engineHome, ".hq", "sync-core", "owned", `${uid}.json`)))
|
|
56
|
+
continue;
|
|
57
|
+
const slug = slugByUid.get(uid);
|
|
58
|
+
if (uid.startsWith("cmp_") && !slug)
|
|
59
|
+
continue;
|
|
60
|
+
vaults.push({ uid, kind: uid.startsWith("prs_") ? "personal" : "company", ...(slug ? { slug } : {}), phase: "full", engineHome });
|
|
61
|
+
}
|
|
62
|
+
return vaults;
|
|
63
|
+
}
|
|
47
64
|
export async function reconcileSyncCore(deps) {
|
|
48
65
|
const now = deps.now ?? (() => new Date());
|
|
49
66
|
const log = deps.log ?? (() => { });
|
|
50
67
|
const release = deps.release ?? SYNC_CORE_RELEASE;
|
|
51
68
|
const warnings = [];
|
|
52
69
|
const previous = readSyncCoreRolloutState(deps.home);
|
|
53
|
-
const
|
|
70
|
+
const previousVaults = previous?.vaults ?? ownedVaultsWithoutState(deps.home, deps.hqRoot);
|
|
71
|
+
const previousByUid = new Map(previousVaults.map((vault) => [vault.uid, vault]));
|
|
54
72
|
const memberships = await deps.listMemberships();
|
|
55
73
|
const desired = [];
|
|
56
74
|
let anyCompanyOn = false;
|
|
@@ -111,6 +129,15 @@ export async function reconcileSyncCore(deps) {
|
|
|
111
129
|
desired.length = 0;
|
|
112
130
|
}
|
|
113
131
|
}
|
|
132
|
+
const held = [];
|
|
133
|
+
const engineRelease = installedRelease ?? release;
|
|
134
|
+
for (let i = desired.length - 1; i >= 0; i -= 1) {
|
|
135
|
+
const reason = companyFullHold(desired[i], engineRelease);
|
|
136
|
+
if (!reason)
|
|
137
|
+
continue;
|
|
138
|
+
held.unshift({ uid: desired[i].uid, reason });
|
|
139
|
+
desired.splice(i, 1);
|
|
140
|
+
}
|
|
114
141
|
const at = now().toISOString();
|
|
115
142
|
const switchesBefore = readVaultSyncSwitches(deps.home);
|
|
116
143
|
const vaults = [];
|
|
@@ -124,42 +151,36 @@ export async function reconcileSyncCore(deps) {
|
|
|
124
151
|
const engineHome = syncCoreEngineHome(deps.home, vault.uid);
|
|
125
152
|
if (!before)
|
|
126
153
|
clearEngineStatus(deps.home, vault.uid);
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
let hold;
|
|
130
|
-
if (phase === "warming" && plan) {
|
|
131
|
-
hold = promotionHold(vault, plan, installedRelease ?? release);
|
|
132
|
-
if (!hold)
|
|
133
|
-
phase = "full";
|
|
134
|
-
}
|
|
135
|
-
writeEngineHome({ home: deps.home, hqRoot: deps.hqRoot, vault, phase, tokenFile: deps.tokenFile });
|
|
154
|
+
writeEngineHome({ home: deps.home, hqRoot: deps.hqRoot, vault, phase: "full", tokenFile: deps.tokenFile });
|
|
155
|
+
const wasFull = before?.phase === "full";
|
|
136
156
|
const alreadyOff = vault.kind === "personal"
|
|
137
157
|
? switchesBefore.personalOff
|
|
138
158
|
: switchesBefore.offCompanies.has(vault.slug ?? "");
|
|
139
|
-
const legacyAlreadyOff =
|
|
140
|
-
const legacyOffSince =
|
|
159
|
+
const legacyAlreadyOff = wasFull ? before.legacyAlreadyOff === true : alreadyOff;
|
|
160
|
+
const legacyOffSince = (wasFull ? before.legacyOffSince : undefined) ?? at;
|
|
161
|
+
const recorded = wasFull ? before.firstCycle : undefined;
|
|
162
|
+
const firstCycle = recorded && recorded.planned > 0
|
|
163
|
+
? recorded
|
|
164
|
+
: engineFirstCycle(engineHome, vault.uid, legacyOffSince) ?? recorded;
|
|
141
165
|
vaults.push({
|
|
142
166
|
uid: vault.uid,
|
|
143
167
|
kind: vault.kind,
|
|
144
168
|
...(vault.slug ? { slug: vault.slug } : {}),
|
|
145
|
-
phase,
|
|
169
|
+
phase: "full",
|
|
146
170
|
engineHome,
|
|
147
|
-
|
|
171
|
+
legacyOffSince,
|
|
148
172
|
...(legacyAlreadyOff ? { legacyAlreadyOff: true } : {}),
|
|
149
|
-
...(
|
|
150
|
-
...(hold ? { hold } : {}),
|
|
173
|
+
...(firstCycle ? { firstCycle } : {}),
|
|
151
174
|
});
|
|
152
|
-
if (
|
|
153
|
-
log(`${vault.uid}:
|
|
154
|
-
if (
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
personalOff = true;
|
|
159
|
-
}
|
|
175
|
+
if (firstCycle && firstCycle !== recorded && (!recorded || firstCycle.planned > 0))
|
|
176
|
+
log(`${vault.uid}: first full cycle ${firstCycle.at}: ${firstCycle.planned} planned, ${firstCycle.completed} completed, ${firstCycle.held} held`);
|
|
177
|
+
if (vault.kind === "company" && vault.slug)
|
|
178
|
+
companies[vault.slug] = false;
|
|
179
|
+
else
|
|
180
|
+
personalOff = true;
|
|
160
181
|
if (!before)
|
|
161
182
|
started.push(vault.uid);
|
|
162
|
-
else if (
|
|
183
|
+
else if (!wasFull)
|
|
163
184
|
promoted.push(vault.uid);
|
|
164
185
|
else
|
|
165
186
|
kept.push(vault.uid);
|
|
@@ -170,7 +191,7 @@ export async function reconcileSyncCore(deps) {
|
|
|
170
191
|
if (desired.some((candidate) => candidate.uid === vault.uid))
|
|
171
192
|
continue;
|
|
172
193
|
stopped.push(vault.uid);
|
|
173
|
-
if (vault.phase !== "full"
|
|
194
|
+
if (vault.phase !== "full")
|
|
174
195
|
continue;
|
|
175
196
|
if (vault.kind === "company" && vault.slug)
|
|
176
197
|
companies[vault.slug] = true;
|
|
@@ -184,6 +205,7 @@ export async function reconcileSyncCore(deps) {
|
|
|
184
205
|
binaryPath,
|
|
185
206
|
vaults,
|
|
186
207
|
...(skipped.length > 0 ? { skipped } : {}),
|
|
208
|
+
...(held.length > 0 ? { held } : {}),
|
|
187
209
|
...(warnings.length > 0 ? { error: warnings.join("; ") } : {}),
|
|
188
210
|
};
|
|
189
211
|
writeSyncCoreRolloutState(deps.home, state);
|
|
@@ -203,13 +225,15 @@ export async function reconcileSyncCore(deps) {
|
|
|
203
225
|
writeSyncCoreRolloutState(deps.home, state);
|
|
204
226
|
}
|
|
205
227
|
for (const uid of started)
|
|
206
|
-
log(`${uid}:
|
|
228
|
+
log(`${uid}: moving to the new sync engine in full mode (home ${syncCoreEngineHome(deps.home, uid)})`);
|
|
207
229
|
for (const uid of promoted)
|
|
208
|
-
log(`${uid}:
|
|
230
|
+
log(`${uid}: moving from the read-only index to full mode on the new sync engine`);
|
|
209
231
|
for (const uid of stopped)
|
|
210
232
|
log(`${uid}: back on the current sync`);
|
|
211
233
|
for (const skip of skipped)
|
|
212
234
|
log(`${skip.uid}: skipped on this machine: ${skip.reason}`);
|
|
235
|
+
for (const hold of held)
|
|
236
|
+
log(`${hold.uid}: ${hold.reason}`);
|
|
213
237
|
for (const warning of warnings)
|
|
214
238
|
log(`warning: ${warning}`);
|
|
215
239
|
return { state, started, promoted, stopped, kept, warnings };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
export declare const SYNC_CORE_RELEASE = "v0.1.
|
|
1
|
+
export declare const SYNC_CORE_RELEASE = "v0.1.21";
|
|
2
2
|
export declare const SYNC_CORE_COMPANY_FULL_RELEASE: string | null;
|
|
3
|
-
export declare const SYNC_CORE_PROMOTION_OP_LIMIT = 100;
|
|
4
3
|
export declare function compareSyncCoreReleases(a: string, b: string): number;
|
|
5
4
|
export declare function companyFullModeAllowed(release: string): boolean;
|
|
6
5
|
export declare const SYNC_CORE_DOWNLOAD_BASE = "https://indigoai-public.s3.amazonaws.com/hq-sync-core";
|
|
@@ -10,6 +9,7 @@ export declare function syncCoreReleaseMarkerPath(home?: string): string;
|
|
|
10
9
|
export declare function syncCoreAssetName(release: string, platform: NodeJS.Platform, arch: string): string | null;
|
|
11
10
|
export declare function syncCoreDownloadBase(env?: NodeJS.ProcessEnv): string;
|
|
12
11
|
export declare function parseSha256Sums(text: string): Map<string, string>;
|
|
12
|
+
export declare function syncCoreBinaryVersion(binaryPath: string): string | null;
|
|
13
13
|
export declare function installedSyncCoreRelease(home: string): string | null;
|
|
14
14
|
export interface EnsureSyncCoreBinaryOptions {
|
|
15
15
|
home: string;
|
|
@@ -3,9 +3,8 @@ import * as fs from "node:fs";
|
|
|
3
3
|
import * as os from "node:os";
|
|
4
4
|
import * as path from "node:path";
|
|
5
5
|
import { execFileSync } from "node:child_process";
|
|
6
|
-
export const SYNC_CORE_RELEASE = "v0.1.
|
|
7
|
-
export const SYNC_CORE_COMPANY_FULL_RELEASE =
|
|
8
|
-
export const SYNC_CORE_PROMOTION_OP_LIMIT = 100;
|
|
6
|
+
export const SYNC_CORE_RELEASE = "v0.1.21";
|
|
7
|
+
export const SYNC_CORE_COMPANY_FULL_RELEASE = "v0.1.21";
|
|
9
8
|
export function compareSyncCoreReleases(a, b) {
|
|
10
9
|
const parse = (release) => release.replace(/^v/, "").split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
11
10
|
const [x, y] = [parse(a), parse(b)];
|
|
@@ -50,14 +49,27 @@ export function parseSha256Sums(text) {
|
|
|
50
49
|
}
|
|
51
50
|
return sums;
|
|
52
51
|
}
|
|
52
|
+
export function syncCoreBinaryVersion(binaryPath) {
|
|
53
|
+
try {
|
|
54
|
+
const out = execFileSync(binaryPath, ["--version"], { encoding: "utf8", timeout: 10_000, stdio: ["ignore", "pipe", "ignore"] });
|
|
55
|
+
const match = /hq-syncd\s+v?(\d+\.\d+\.\d+)/.exec(out);
|
|
56
|
+
return match ? `v${match[1]}` : null;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
53
62
|
export function installedSyncCoreRelease(home) {
|
|
54
63
|
try {
|
|
55
|
-
const marker = fs.readFileSync(syncCoreReleaseMarkerPath(home), "utf8").trim();
|
|
56
64
|
const binary = syncCoreBinaryPath(home);
|
|
57
|
-
if (!
|
|
65
|
+
if (!fs.statSync(binary).isFile())
|
|
58
66
|
return null;
|
|
59
67
|
fs.accessSync(binary, fs.constants.X_OK);
|
|
60
|
-
|
|
68
|
+
const reported = syncCoreBinaryVersion(binary);
|
|
69
|
+
if (reported)
|
|
70
|
+
return reported;
|
|
71
|
+
const marker = fs.readFileSync(syncCoreReleaseMarkerPath(home), "utf8").trim();
|
|
72
|
+
return marker || null;
|
|
61
73
|
}
|
|
62
74
|
catch {
|
|
63
75
|
return null;
|
|
@@ -96,6 +108,19 @@ export async function ensureSyncCoreBinary(opts) {
|
|
|
96
108
|
const binaryPath = syncCoreBinaryPath(opts.home);
|
|
97
109
|
const installedRelease = installedSyncCoreRelease(opts.home);
|
|
98
110
|
if (installedRelease && compareSyncCoreReleases(installedRelease, release) >= 0 && fs.existsSync(binaryPath)) {
|
|
111
|
+
const marker = syncCoreReleaseMarkerPath(opts.home);
|
|
112
|
+
let current = "";
|
|
113
|
+
try {
|
|
114
|
+
current = fs.readFileSync(marker, "utf8").trim();
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
current = "";
|
|
118
|
+
}
|
|
119
|
+
if (current !== installedRelease) {
|
|
120
|
+
const tmp = `${marker}.${process.pid}.tmp`;
|
|
121
|
+
fs.writeFileSync(tmp, `${installedRelease}\n`, { mode: 0o600 });
|
|
122
|
+
fs.renameSync(tmp, marker);
|
|
123
|
+
}
|
|
99
124
|
return { binaryPath, release: installedRelease, installed: false };
|
|
100
125
|
}
|
|
101
126
|
const asset = syncCoreAssetName(release, opts.platform, opts.arch);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type SyncCoreFirstCycle, type SyncCoreVaultKind, type SyncCoreVaultPhase } from "./engine-home.js";
|
|
2
2
|
export interface RolloutVault {
|
|
3
3
|
uid: string;
|
|
4
4
|
kind: SyncCoreVaultKind;
|
|
@@ -7,8 +7,7 @@ export interface RolloutVault {
|
|
|
7
7
|
engineHome: string;
|
|
8
8
|
legacyOffSince?: string;
|
|
9
9
|
legacyAlreadyOff?: true;
|
|
10
|
-
|
|
11
|
-
hold?: string;
|
|
10
|
+
firstCycle?: SyncCoreFirstCycle;
|
|
12
11
|
}
|
|
13
12
|
export interface SyncCoreRolloutState {
|
|
14
13
|
version: 1;
|
|
@@ -20,6 +19,10 @@ export interface SyncCoreRolloutState {
|
|
|
20
19
|
uid: string;
|
|
21
20
|
reason: string;
|
|
22
21
|
}[];
|
|
22
|
+
held?: {
|
|
23
|
+
uid: string;
|
|
24
|
+
reason: string;
|
|
25
|
+
}[];
|
|
23
26
|
error?: string;
|
|
24
27
|
}
|
|
25
28
|
export declare function syncCoreRolloutStatePath(home: string): string;
|
|
@@ -1,26 +1,29 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { isSyncCoreVaultUid } from "./engine-home.js";
|
|
4
|
-
|
|
4
|
+
const FIRST_CYCLE_COUNTS = [
|
|
5
|
+
"planned",
|
|
6
|
+
"completed",
|
|
7
|
+
"pending",
|
|
8
|
+
"held",
|
|
9
|
+
"notApplied",
|
|
10
|
+
"uploads",
|
|
11
|
+
"downloads",
|
|
12
|
+
"deletes",
|
|
13
|
+
"conflicts",
|
|
14
|
+
"deferred",
|
|
15
|
+
"listed",
|
|
16
|
+
];
|
|
17
|
+
function firstCycle(value) {
|
|
5
18
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
6
19
|
return null;
|
|
7
|
-
const
|
|
8
|
-
if (typeof
|
|
20
|
+
const c = value;
|
|
21
|
+
if (typeof c.at !== "string" || typeof c.truncated !== "boolean")
|
|
9
22
|
return null;
|
|
10
|
-
|
|
11
|
-
if (numbers.some((key) => typeof p[key] !== "number" || !Number.isFinite(p[key])))
|
|
23
|
+
if (FIRST_CYCLE_COUNTS.some((key) => typeof c[key] !== "number" || !Number.isFinite(c[key])))
|
|
12
24
|
return null;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
return {
|
|
16
|
-
at: p.at,
|
|
17
|
-
uploads: p.uploads,
|
|
18
|
-
downloads: p.downloads,
|
|
19
|
-
deletes: p.deletes,
|
|
20
|
-
conflicts: typeof p.conflicts === "number" ? p.conflicts : 0,
|
|
21
|
-
remoteRows: p.remoteRows,
|
|
22
|
-
localFiles: p.localFiles,
|
|
23
|
-
};
|
|
25
|
+
const counts = Object.fromEntries(FIRST_CYCLE_COUNTS.map((key) => [key, c[key]]));
|
|
26
|
+
return { at: c.at, ...counts, truncated: c.truncated };
|
|
24
27
|
}
|
|
25
28
|
function skippedEntries(value) {
|
|
26
29
|
if (value === undefined)
|
|
@@ -57,10 +60,8 @@ function vault(value) {
|
|
|
57
60
|
return null;
|
|
58
61
|
if (v.kind === "company" && (typeof v.slug !== "string" || !/^[a-z0-9][a-z0-9_-]*$/.test(v.slug)))
|
|
59
62
|
return null;
|
|
60
|
-
const
|
|
61
|
-
if (
|
|
62
|
-
return null;
|
|
63
|
-
if (v.hold !== undefined && typeof v.hold !== "string")
|
|
63
|
+
const cycle = v.firstCycle === undefined ? undefined : firstCycle(v.firstCycle);
|
|
64
|
+
if (cycle === null)
|
|
64
65
|
return null;
|
|
65
66
|
return {
|
|
66
67
|
uid: v.uid,
|
|
@@ -70,8 +71,7 @@ function vault(value) {
|
|
|
70
71
|
engineHome: v.engineHome,
|
|
71
72
|
...(typeof v.legacyOffSince === "string" ? { legacyOffSince: v.legacyOffSince } : {}),
|
|
72
73
|
...(v.legacyAlreadyOff === true ? { legacyAlreadyOff: true } : {}),
|
|
73
|
-
...(
|
|
74
|
-
...(typeof v.hold === "string" ? { hold: v.hold } : {}),
|
|
74
|
+
...(cycle ? { firstCycle: cycle } : {}),
|
|
75
75
|
};
|
|
76
76
|
}
|
|
77
77
|
export function readSyncCoreRolloutState(home) {
|
|
@@ -95,6 +95,9 @@ export function readSyncCoreRolloutState(home) {
|
|
|
95
95
|
const skipped = skippedEntries(obj.skipped);
|
|
96
96
|
if (skipped === null)
|
|
97
97
|
return null;
|
|
98
|
+
const held = skippedEntries(obj.held);
|
|
99
|
+
if (held === null)
|
|
100
|
+
return null;
|
|
98
101
|
return {
|
|
99
102
|
version: 1,
|
|
100
103
|
readAt: obj.readAt,
|
|
@@ -102,6 +105,7 @@ export function readSyncCoreRolloutState(home) {
|
|
|
102
105
|
binaryPath: obj.binaryPath,
|
|
103
106
|
vaults: vaults,
|
|
104
107
|
...(skipped.length > 0 ? { skipped } : {}),
|
|
108
|
+
...(held.length > 0 ? { held } : {}),
|
|
105
109
|
...(typeof obj.error === "string" ? { error: obj.error } : {}),
|
|
106
110
|
};
|
|
107
111
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@indigoai-us/hq-cli",
|
|
3
|
-
"version": "5.345.
|
|
3
|
+
"version": "5.345.177",
|
|
4
4
|
"description": "HQ by Indigo management CLI — modules and cloud sync",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"@aws-sdk/client-iot-data-plane": "^3.1096.0",
|
|
53
53
|
"@aws-sdk/client-s3": "^3.1049.0",
|
|
54
54
|
"@aws-sdk/dsql-signer": "^3.1146.0",
|
|
55
|
-
"@indigoai-us/hq-cloud": "6.18.
|
|
55
|
+
"@indigoai-us/hq-cloud": "6.18.104",
|
|
56
56
|
"@indigoai-us/hq-flags-client": "^0.1.2",
|
|
57
57
|
"@modelcontextprotocol/sdk": "1.29.0",
|
|
58
58
|
"@sentry/node": "^10.49.0",
|
|
@@ -114,11 +114,11 @@
|
|
|
114
114
|
"*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}": "eslint --cache --no-error-on-unmatched-pattern"
|
|
115
115
|
},
|
|
116
116
|
"optionalDependencies": {
|
|
117
|
-
"@indigoai-us/hq-hook-win32-x64": "5.345.
|
|
118
|
-
"@indigoai-us/hq-hook-win32-arm64": "5.345.
|
|
119
|
-
"@indigoai-us/hq-hook-darwin-x64": "5.345.
|
|
120
|
-
"@indigoai-us/hq-hook-darwin-arm64": "5.345.
|
|
121
|
-
"@indigoai-us/hq-hook-linux-x64": "5.345.
|
|
122
|
-
"@indigoai-us/hq-hook-linux-arm64": "5.345.
|
|
117
|
+
"@indigoai-us/hq-hook-win32-x64": "5.345.177",
|
|
118
|
+
"@indigoai-us/hq-hook-win32-arm64": "5.345.177",
|
|
119
|
+
"@indigoai-us/hq-hook-darwin-x64": "5.345.177",
|
|
120
|
+
"@indigoai-us/hq-hook-darwin-arm64": "5.345.177",
|
|
121
|
+
"@indigoai-us/hq-hook-linux-x64": "5.345.177",
|
|
122
|
+
"@indigoai-us/hq-hook-linux-arm64": "5.345.177"
|
|
123
123
|
}
|
|
124
124
|
}
|