@indigoai-us/hq-cli 5.345.203 → 5.345.204
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 +9 -0
- package/dist/commands/bot.js +7 -2
- package/dist/commands/cloud.d.ts +14 -2
- package/dist/commands/cloud.js +25 -16
- package/dist/commands/sync-core.js +2 -0
- package/dist/lib/bot/claude-login.d.ts +7 -1
- package/dist/lib/bot/claude-login.js +56 -16
- package/dist/lib/capacity/collectors/claude-oauth.d.ts +53 -5
- package/dist/lib/capacity/collectors/claude-oauth.js +251 -71
- package/dist/lib/daemon/catalog.js +2 -2
- package/dist/lib/daemon/run.js +2 -2
- package/dist/lib/lanes/config.js +13 -0
- package/dist/lib/lanes/throttle.d.ts +12 -1
- package/dist/lib/lanes/throttle.js +27 -8
- package/dist/lib/lanes/types.d.ts +5 -0
- package/dist/lib/signals/steps/extract.d.ts +110 -4
- package/dist/lib/signals/steps/extract.js +520 -39
- package/dist/lib/sync-core/engine-home.d.ts +2 -1
- package/dist/lib/sync-core/engine-home.js +25 -8
- package/dist/lib/sync-core/reconcile.d.ts +3 -1
- package/dist/lib/sync-core/reconcile.js +21 -8
- package/dist/lib/sync-core/release.d.ts +14 -5
- package/dist/lib/sync-core/release.js +58 -14
- package/package.json +7 -7
|
@@ -17,13 +17,14 @@ export declare const SYNC_CORE_REQUESTS_PER_DAY = 20000;
|
|
|
17
17
|
export declare function syncCoreEngineHome(home: string, vaultUid: string): string;
|
|
18
18
|
export declare function syncCoreUnitId(vaultUid: string): string;
|
|
19
19
|
export declare function isSyncCoreVaultUid(value: unknown): value is string;
|
|
20
|
-
export declare function engineConfigToml(vault: SyncCoreVault, hqRoot: string, phase: SyncCoreVaultPhase): string;
|
|
20
|
+
export declare function engineConfigToml(vault: SyncCoreVault, hqRoot: string, phase: SyncCoreVaultPhase, tokenPath?: string): string;
|
|
21
21
|
export interface WriteEngineHomeOptions {
|
|
22
22
|
home: string;
|
|
23
23
|
hqRoot: string;
|
|
24
24
|
vault: SyncCoreVault;
|
|
25
25
|
phase: SyncCoreVaultPhase;
|
|
26
26
|
tokenFile: string;
|
|
27
|
+
platform?: NodeJS.Platform;
|
|
27
28
|
}
|
|
28
29
|
export declare function writeEngineHome(opts: WriteEngineHomeOptions): {
|
|
29
30
|
engineHome: string;
|
|
@@ -11,7 +11,7 @@ export function syncCoreUnitId(vaultUid) {
|
|
|
11
11
|
export function isSyncCoreVaultUid(value) {
|
|
12
12
|
return typeof value === "string" && /^(prs|cmp)_[A-Za-z0-9][A-Za-z0-9_-]*$/.test(value);
|
|
13
13
|
}
|
|
14
|
-
export function engineConfigToml(vault, hqRoot, phase) {
|
|
14
|
+
export function engineConfigToml(vault, hqRoot, phase, tokenPath) {
|
|
15
15
|
const lines = [
|
|
16
16
|
"[sync]",
|
|
17
17
|
'engine = "rust-test"',
|
|
@@ -20,6 +20,7 @@ export function engineConfigToml(vault, hqRoot, phase) {
|
|
|
20
20
|
`vaults = [${JSON.stringify(vault.uid)}]`,
|
|
21
21
|
`hq_root = ${JSON.stringify(hqRoot)}`,
|
|
22
22
|
`requests_per_day = ${SYNC_CORE_REQUESTS_PER_DAY}`,
|
|
23
|
+
...(tokenPath ? [`token_path = ${JSON.stringify(tokenPath)}`] : []),
|
|
23
24
|
];
|
|
24
25
|
if (vault.kind === "company") {
|
|
25
26
|
lines.push("[sync.roots]", `${vault.uid} = ${JSON.stringify(`companies/${vault.slug}`)}`);
|
|
@@ -52,15 +53,24 @@ function writeIfChanged(file, text) {
|
|
|
52
53
|
fs.renameSync(tmp, file);
|
|
53
54
|
return true;
|
|
54
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* The files an engine home links to on macOS and Linux. Windows needs a privilege to create a
|
|
58
|
+
* symlink, so there the config names the token file and the engine reads `menubar.json` and
|
|
59
|
+
* `work-mesh/` from `HQ_STATE_DIR`, which the daemon sets.
|
|
60
|
+
*/
|
|
61
|
+
const ENGINE_HOME_LINKS = ["menubar.json", "work-mesh", "cognito-tokens.json"];
|
|
55
62
|
export function writeEngineHome(opts) {
|
|
56
63
|
const engineHome = syncCoreEngineHome(opts.home, opts.vault.uid);
|
|
57
64
|
const hq = path.join(engineHome, ".hq");
|
|
58
65
|
const root = path.join(hq, "sync-core");
|
|
59
66
|
fs.mkdirSync(path.join(root, "owned"), { recursive: true, mode: 0o700 });
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
67
|
+
const windows = (opts.platform ?? process.platform) === "win32";
|
|
68
|
+
if (!windows) {
|
|
69
|
+
replaceSymlink(path.join(hq, "menubar.json"), path.join(opts.home, ".hq", "menubar.json"));
|
|
70
|
+
replaceSymlink(path.join(hq, "work-mesh"), path.join(opts.home, ".hq", "work-mesh"));
|
|
71
|
+
replaceSymlink(path.join(hq, "cognito-tokens.json"), opts.tokenFile);
|
|
72
|
+
}
|
|
73
|
+
const configChanged = writeIfChanged(path.join(root, "config.toml"), engineConfigToml(opts.vault, opts.hqRoot, opts.phase, windows ? opts.tokenFile : undefined));
|
|
64
74
|
const ownedPath = path.join(root, "owned", `${opts.vault.uid}.json`);
|
|
65
75
|
let ownedChanged = false;
|
|
66
76
|
if (opts.phase === "full") {
|
|
@@ -123,8 +133,9 @@ function blocked(plan, blockedReason) {
|
|
|
123
133
|
return { ...plan, blockedReason };
|
|
124
134
|
}
|
|
125
135
|
export function inspectEngineHome(home, hqRoot, plan, platform) {
|
|
126
|
-
if (platform !== "darwin" && platform !== "linux")
|
|
127
|
-
return blocked(plan, "the sync engine requires macOS or
|
|
136
|
+
if (platform !== "darwin" && platform !== "linux" && platform !== "win32") {
|
|
137
|
+
return blocked(plan, "the sync engine requires macOS, Linux or Windows");
|
|
138
|
+
}
|
|
128
139
|
try {
|
|
129
140
|
if (!fs.statSync(plan.binaryPath).isFile())
|
|
130
141
|
return blocked(plan, "the sync engine binary is missing");
|
|
@@ -182,7 +193,13 @@ export function inspectEngineHome(home, hqRoot, plan, platform) {
|
|
|
182
193
|
else if (fs.existsSync(ownedPath)) {
|
|
183
194
|
return blocked(plan, `the engine ownership record for ${plan.uid} must not exist while the index warms`);
|
|
184
195
|
}
|
|
185
|
-
|
|
196
|
+
if (platform === "win32") {
|
|
197
|
+
if (typeof sync.token_path !== "string" || !path.isAbsolute(sync.token_path)) {
|
|
198
|
+
return blocked(plan, `the engine config for ${plan.uid} must name the token file`);
|
|
199
|
+
}
|
|
200
|
+
return plan;
|
|
201
|
+
}
|
|
202
|
+
for (const link of ENGINE_HOME_LINKS) {
|
|
186
203
|
try {
|
|
187
204
|
fs.readlinkSync(path.join(plan.engineHome, ".hq", link));
|
|
188
205
|
}
|
|
@@ -16,6 +16,8 @@ export interface SyncCoreReconcileDeps {
|
|
|
16
16
|
ensureBinary: (release: string) => Promise<SyncCoreBinaryInstall>;
|
|
17
17
|
/** A fixed release (tests). Otherwise readPin decides, then SYNC_CORE_RELEASE. */
|
|
18
18
|
release?: string;
|
|
19
|
+
platform?: NodeJS.Platform;
|
|
20
|
+
env?: NodeJS.ProcessEnv;
|
|
19
21
|
/** Reads the server-side engine pin; never throws (see readSyncCorePin). */
|
|
20
22
|
readPin?: () => Promise<SyncCorePin>;
|
|
21
23
|
now?: () => Date;
|
|
@@ -29,6 +31,6 @@ export interface SyncCoreReconcileResult {
|
|
|
29
31
|
kept: string[];
|
|
30
32
|
warnings: string[];
|
|
31
33
|
}
|
|
32
|
-
export declare function fullModeHold(vault: SyncCoreVault, release: string): string | undefined;
|
|
34
|
+
export declare function fullModeHold(vault: SyncCoreVault, release: string, platform?: NodeJS.Platform, env?: NodeJS.ProcessEnv): string | undefined;
|
|
33
35
|
export declare function reconcileSyncCore(deps: SyncCoreReconcileDeps): Promise<SyncCoreReconcileResult>;
|
|
34
36
|
//# sourceMappingURL=reconcile.d.ts.map
|
|
@@ -3,16 +3,21 @@ 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
5
|
import { clearEngineStatus, engineFirstCycle, isSyncCoreVaultUid, removeEngineOwnership, syncCoreEngineHome, writeEngineHome, } from "./engine-home.js";
|
|
6
|
-
import { SYNC_CORE_FULL_RELEASE, SYNC_CORE_RELEASE, compareSyncCoreReleases, fullModeAllowed, installedSyncCoreRelease, syncCoreBinaryPath, } from "./release.js";
|
|
6
|
+
import { SYNC_CORE_FULL_RELEASE, SYNC_CORE_RELEASE, compareSyncCoreReleases, fullModeAllowed, installedSyncCoreRelease, syncCoreBinaryPath, windowsFullRelease, } from "./release.js";
|
|
7
7
|
import { readSyncCoreSkips } from "./skip.js";
|
|
8
8
|
import { SYNC_RUST_ENGINE_ROLLOUT_FLAG } from "./flag.js";
|
|
9
9
|
import { readSyncCoreRolloutState, syncCoreRolloutStatePath, writeSyncCoreRolloutState, } from "./rollout-state.js";
|
|
10
10
|
function message(error) {
|
|
11
11
|
return error instanceof Error ? error.message : String(error);
|
|
12
12
|
}
|
|
13
|
-
export function fullModeHold(vault, release) {
|
|
14
|
-
if (fullModeAllowed(release))
|
|
13
|
+
export function fullModeHold(vault, release, platform = process.platform, env = process.env) {
|
|
14
|
+
if (fullModeAllowed(release, platform, env))
|
|
15
15
|
return undefined;
|
|
16
|
+
if (platform === "win32" && fullModeAllowed(release, "linux", env)) {
|
|
17
|
+
const windows = windowsFullRelease(env);
|
|
18
|
+
const needed = windows ? `engine release ${windows} or later` : "an engine release cleared for full sync on Windows";
|
|
19
|
+
return `${vault.kind} vaults on Windows move to the new sync engine with ${needed} (${release} available); staying on the current sync`;
|
|
20
|
+
}
|
|
16
21
|
const needed = SYNC_CORE_FULL_RELEASE ? `engine release ${SYNC_CORE_FULL_RELEASE} or later` : "an engine release cleared for full sync";
|
|
17
22
|
return `${vault.kind} vaults move to the new sync engine with ${needed} (${release} installed); staying on the current sync`;
|
|
18
23
|
}
|
|
@@ -65,6 +70,8 @@ function ownedVaultsWithoutState(home, hqRoot) {
|
|
|
65
70
|
export async function reconcileSyncCore(deps) {
|
|
66
71
|
const now = deps.now ?? (() => new Date());
|
|
67
72
|
const log = deps.log ?? (() => { });
|
|
73
|
+
const platform = deps.platform ?? process.platform;
|
|
74
|
+
const env = deps.env ?? process.env;
|
|
68
75
|
const warnings = [];
|
|
69
76
|
let release = deps.release ?? SYNC_CORE_RELEASE;
|
|
70
77
|
const previous = readSyncCoreRolloutState(deps.home);
|
|
@@ -114,8 +121,15 @@ export async function reconcileSyncCore(deps) {
|
|
|
114
121
|
catch (error) {
|
|
115
122
|
warnings.push(message(error));
|
|
116
123
|
}
|
|
117
|
-
let binaryPath = syncCoreBinaryPath(deps.home);
|
|
118
|
-
let installedRelease = installedSyncCoreRelease(deps.home);
|
|
124
|
+
let binaryPath = syncCoreBinaryPath(deps.home, platform);
|
|
125
|
+
let installedRelease = installedSyncCoreRelease(deps.home, platform);
|
|
126
|
+
const held = [];
|
|
127
|
+
const best = installedRelease && compareSyncCoreReleases(installedRelease, release) >= 0 ? installedRelease : release;
|
|
128
|
+
if (platform === "win32" && !fullModeAllowed(best, platform, env)) {
|
|
129
|
+
for (const vault of desired)
|
|
130
|
+
held.push({ uid: vault.uid, reason: fullModeHold(vault, best, platform, env) });
|
|
131
|
+
desired.length = 0;
|
|
132
|
+
}
|
|
119
133
|
if (desired.length > 0) {
|
|
120
134
|
if (deps.release === undefined && deps.readPin) {
|
|
121
135
|
const pin = await deps.readPin();
|
|
@@ -140,10 +154,9 @@ export async function reconcileSyncCore(deps) {
|
|
|
140
154
|
desired.length = 0;
|
|
141
155
|
}
|
|
142
156
|
}
|
|
143
|
-
const held = [];
|
|
144
157
|
const engineRelease = installedRelease ?? release;
|
|
145
158
|
for (let i = desired.length - 1; i >= 0; i -= 1) {
|
|
146
|
-
const reason = fullModeHold(desired[i], engineRelease);
|
|
159
|
+
const reason = fullModeHold(desired[i], engineRelease, platform, env);
|
|
147
160
|
if (!reason)
|
|
148
161
|
continue;
|
|
149
162
|
held.unshift({ uid: desired[i].uid, reason });
|
|
@@ -162,7 +175,7 @@ export async function reconcileSyncCore(deps) {
|
|
|
162
175
|
const engineHome = syncCoreEngineHome(deps.home, vault.uid);
|
|
163
176
|
if (!before)
|
|
164
177
|
clearEngineStatus(deps.home, vault.uid);
|
|
165
|
-
writeEngineHome({ home: deps.home, hqRoot: deps.hqRoot, vault, phase: "full", tokenFile: deps.tokenFile });
|
|
178
|
+
writeEngineHome({ home: deps.home, hqRoot: deps.hqRoot, vault, phase: "full", tokenFile: deps.tokenFile, platform });
|
|
166
179
|
const wasFull = before?.phase === "full";
|
|
167
180
|
const alreadyOff = vault.kind === "personal"
|
|
168
181
|
? switchesBefore.personalOff
|
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
export declare const SYNC_CORE_RELEASE = "v0.1.23";
|
|
2
2
|
export declare const SYNC_CORE_FULL_RELEASE: string | null;
|
|
3
|
+
/**
|
|
4
|
+
* The first engine release whose Windows build runs full sync. Until it is set, Windows vaults
|
|
5
|
+
* stay on the current sync and no Windows engine is downloaded. `HQ_SYNC_CORE_WINDOWS_FULL_RELEASE`
|
|
6
|
+
* overrides it on one machine, for testing a build before it is released.
|
|
7
|
+
*/
|
|
8
|
+
export declare const SYNC_CORE_WINDOWS_FULL_RELEASE: string | null;
|
|
9
|
+
export declare const SYNC_CORE_WINDOWS_FULL_RELEASE_ENV = "HQ_SYNC_CORE_WINDOWS_FULL_RELEASE";
|
|
3
10
|
export declare function compareSyncCoreReleases(a: string, b: string): number;
|
|
4
|
-
export declare function
|
|
11
|
+
export declare function windowsFullRelease(env?: NodeJS.ProcessEnv): string | null;
|
|
12
|
+
export declare function fullModeAllowed(release: string, platform?: NodeJS.Platform, env?: NodeJS.ProcessEnv): boolean;
|
|
5
13
|
export declare const SYNC_CORE_DOWNLOAD_BASE = "https://indigoai-public.s3.amazonaws.com/hq-sync-core";
|
|
6
14
|
export declare const SYNC_CORE_DOWNLOAD_BASE_ENV = "HQ_SYNC_CORE_DOWNLOAD_BASE";
|
|
7
15
|
/** The server-side engine pin, published next to the release folders on the download base. */
|
|
@@ -18,8 +26,8 @@ export interface SyncCorePin {
|
|
|
18
26
|
* The engine release to install, read from `<download base>/pin.json`
|
|
19
27
|
* (`{"release":"v0.1.24"}`), so an engine release reaches machines without an
|
|
20
28
|
* hq-cli release. Falls back to SYNC_CORE_RELEASE when the file is missing,
|
|
21
|
-
* unreadable or malformed. Never throws.
|
|
22
|
-
*
|
|
29
|
+
* unreadable or malformed. Never throws. The pin only picks a version: on
|
|
30
|
+
* Windows the release gate (fullModeAllowed) still decides whether it installs.
|
|
23
31
|
*/
|
|
24
32
|
export declare function readSyncCorePin(opts?: {
|
|
25
33
|
env?: NodeJS.ProcessEnv;
|
|
@@ -27,13 +35,14 @@ export declare function readSyncCorePin(opts?: {
|
|
|
27
35
|
fetch?: typeof fetch;
|
|
28
36
|
timeoutMs?: number;
|
|
29
37
|
}): Promise<SyncCorePin>;
|
|
30
|
-
export declare function
|
|
38
|
+
export declare function syncCoreBinaryName(platform?: NodeJS.Platform): string;
|
|
39
|
+
export declare function syncCoreBinaryPath(home?: string, platform?: NodeJS.Platform): string;
|
|
31
40
|
export declare function syncCoreReleaseMarkerPath(home?: string): string;
|
|
32
41
|
export declare function syncCoreAssetName(release: string, platform: NodeJS.Platform, arch: string): string | null;
|
|
33
42
|
export declare function syncCoreDownloadBase(env?: NodeJS.ProcessEnv): string;
|
|
34
43
|
export declare function parseSha256Sums(text: string): Map<string, string>;
|
|
35
44
|
export declare function syncCoreBinaryVersion(binaryPath: string): string | null;
|
|
36
|
-
export declare function installedSyncCoreRelease(home: string): string | null;
|
|
45
|
+
export declare function installedSyncCoreRelease(home: string, platform?: NodeJS.Platform): string | null;
|
|
37
46
|
export interface EnsureSyncCoreBinaryOptions {
|
|
38
47
|
home: string;
|
|
39
48
|
platform: NodeJS.Platform;
|
|
@@ -5,6 +5,13 @@ import * as path from "node:path";
|
|
|
5
5
|
import { execFileSync } from "node:child_process";
|
|
6
6
|
export const SYNC_CORE_RELEASE = "v0.1.23";
|
|
7
7
|
export const SYNC_CORE_FULL_RELEASE = "v0.1.23";
|
|
8
|
+
/**
|
|
9
|
+
* The first engine release whose Windows build runs full sync. Until it is set, Windows vaults
|
|
10
|
+
* stay on the current sync and no Windows engine is downloaded. `HQ_SYNC_CORE_WINDOWS_FULL_RELEASE`
|
|
11
|
+
* overrides it on one machine, for testing a build before it is released.
|
|
12
|
+
*/
|
|
13
|
+
export const SYNC_CORE_WINDOWS_FULL_RELEASE = null;
|
|
14
|
+
export const SYNC_CORE_WINDOWS_FULL_RELEASE_ENV = "HQ_SYNC_CORE_WINDOWS_FULL_RELEASE";
|
|
8
15
|
export function compareSyncCoreReleases(a, b) {
|
|
9
16
|
const parse = (release) => release.replace(/^v/, "").split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
10
17
|
const [x, y] = [parse(a), parse(b)];
|
|
@@ -15,8 +22,17 @@ export function compareSyncCoreReleases(a, b) {
|
|
|
15
22
|
}
|
|
16
23
|
return 0;
|
|
17
24
|
}
|
|
18
|
-
export function
|
|
19
|
-
|
|
25
|
+
export function windowsFullRelease(env = process.env) {
|
|
26
|
+
const override = env[SYNC_CORE_WINDOWS_FULL_RELEASE_ENV]?.trim();
|
|
27
|
+
return override && /^v?\d+\.\d+\.\d+$/.test(override) ? override : SYNC_CORE_WINDOWS_FULL_RELEASE;
|
|
28
|
+
}
|
|
29
|
+
export function fullModeAllowed(release, platform = process.platform, env = process.env) {
|
|
30
|
+
if (SYNC_CORE_FULL_RELEASE === null || compareSyncCoreReleases(release, SYNC_CORE_FULL_RELEASE) < 0)
|
|
31
|
+
return false;
|
|
32
|
+
if (platform !== "win32")
|
|
33
|
+
return true;
|
|
34
|
+
const windows = windowsFullRelease(env);
|
|
35
|
+
return windows !== null && compareSyncCoreReleases(release, windows) >= 0;
|
|
20
36
|
}
|
|
21
37
|
export const SYNC_CORE_DOWNLOAD_BASE = "https://indigoai-public.s3.amazonaws.com/hq-sync-core";
|
|
22
38
|
export const SYNC_CORE_DOWNLOAD_BASE_ENV = "HQ_SYNC_CORE_DOWNLOAD_BASE";
|
|
@@ -29,8 +45,8 @@ const RELEASE_PATTERN = /^v\d+\.\d+\.\d+$/;
|
|
|
29
45
|
* The engine release to install, read from `<download base>/pin.json`
|
|
30
46
|
* (`{"release":"v0.1.24"}`), so an engine release reaches machines without an
|
|
31
47
|
* hq-cli release. Falls back to SYNC_CORE_RELEASE when the file is missing,
|
|
32
|
-
* unreadable or malformed. Never throws.
|
|
33
|
-
*
|
|
48
|
+
* unreadable or malformed. Never throws. The pin only picks a version: on
|
|
49
|
+
* Windows the release gate (fullModeAllowed) still decides whether it installs.
|
|
34
50
|
*/
|
|
35
51
|
export async function readSyncCorePin(opts = {}) {
|
|
36
52
|
const builtIn = (warning) => ({ release: SYNC_CORE_RELEASE, source: "built-in", ...(warning ? { warning } : {}) });
|
|
@@ -60,8 +76,11 @@ export async function readSyncCorePin(opts = {}) {
|
|
|
60
76
|
}
|
|
61
77
|
return { release, source: "server" };
|
|
62
78
|
}
|
|
63
|
-
export function
|
|
64
|
-
return
|
|
79
|
+
export function syncCoreBinaryName(platform = process.platform) {
|
|
80
|
+
return platform === "win32" ? "hq-syncd.exe" : "hq-syncd";
|
|
81
|
+
}
|
|
82
|
+
export function syncCoreBinaryPath(home = os.homedir(), platform = process.platform) {
|
|
83
|
+
return path.join(home, ".hq", "bin", syncCoreBinaryName(platform));
|
|
65
84
|
}
|
|
66
85
|
export function syncCoreReleaseMarkerPath(home = os.homedir()) {
|
|
67
86
|
return path.join(home, ".hq", "bin", "hq-syncd.release");
|
|
@@ -73,6 +92,10 @@ export function syncCoreAssetName(release, platform, arch) {
|
|
|
73
92
|
return `hq-sync-core-${release}-linux-x64.tar.gz`;
|
|
74
93
|
if (platform === "linux" && arch === "arm64")
|
|
75
94
|
return `hq-sync-core-${release}-linux-arm64.tar.gz`;
|
|
95
|
+
if (platform === "win32" && arch === "x64")
|
|
96
|
+
return `hq-sync-core-${release}-windows-x64.zip`;
|
|
97
|
+
if (platform === "win32" && arch === "arm64")
|
|
98
|
+
return `hq-sync-core-${release}-windows-arm64.zip`;
|
|
76
99
|
return null;
|
|
77
100
|
}
|
|
78
101
|
export function syncCoreDownloadBase(env = process.env) {
|
|
@@ -98,9 +121,9 @@ export function syncCoreBinaryVersion(binaryPath) {
|
|
|
98
121
|
return null;
|
|
99
122
|
}
|
|
100
123
|
}
|
|
101
|
-
export function installedSyncCoreRelease(home) {
|
|
124
|
+
export function installedSyncCoreRelease(home, platform = process.platform) {
|
|
102
125
|
try {
|
|
103
|
-
const binary = syncCoreBinaryPath(home);
|
|
126
|
+
const binary = syncCoreBinaryPath(home, platform);
|
|
104
127
|
if (!fs.statSync(binary).isFile())
|
|
105
128
|
return null;
|
|
106
129
|
fs.accessSync(binary, fs.constants.X_OK);
|
|
@@ -130,6 +153,24 @@ function findFile(dir, name) {
|
|
|
130
153
|
}
|
|
131
154
|
return null;
|
|
132
155
|
}
|
|
156
|
+
/**
|
|
157
|
+
* Windows cannot replace an executable that is running, but it can rename one. The old engine
|
|
158
|
+
* keeps running from `<binary>.old` until its unit restarts; the copy left by the previous
|
|
159
|
+
* update is removed first when nothing runs from it any more.
|
|
160
|
+
*/
|
|
161
|
+
function moveRunningBinaryAside(binaryPath) {
|
|
162
|
+
if (!fs.existsSync(binaryPath))
|
|
163
|
+
return;
|
|
164
|
+
const aside = `${binaryPath}.old`;
|
|
165
|
+
try {
|
|
166
|
+
fs.rmSync(aside, { force: true });
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
// still running from the previous update; that copy is removed on a later one
|
|
170
|
+
}
|
|
171
|
+
const target = fs.existsSync(aside) ? `${binaryPath}.${process.pid}.old` : aside;
|
|
172
|
+
fs.renameSync(binaryPath, target);
|
|
173
|
+
}
|
|
133
174
|
async function download(fetcher, url) {
|
|
134
175
|
let response;
|
|
135
176
|
try {
|
|
@@ -144,8 +185,8 @@ async function download(fetcher, url) {
|
|
|
144
185
|
}
|
|
145
186
|
export async function ensureSyncCoreBinary(opts) {
|
|
146
187
|
const release = opts.release ?? SYNC_CORE_RELEASE;
|
|
147
|
-
const binaryPath = syncCoreBinaryPath(opts.home);
|
|
148
|
-
const installedRelease = installedSyncCoreRelease(opts.home);
|
|
188
|
+
const binaryPath = syncCoreBinaryPath(opts.home, opts.platform);
|
|
189
|
+
const installedRelease = installedSyncCoreRelease(opts.home, opts.platform);
|
|
149
190
|
if (installedRelease && compareSyncCoreReleases(installedRelease, release) >= 0 && fs.existsSync(binaryPath)) {
|
|
150
191
|
const marker = syncCoreReleaseMarkerPath(opts.home);
|
|
151
192
|
let current = "";
|
|
@@ -186,13 +227,16 @@ export async function ensureSyncCoreBinary(opts) {
|
|
|
186
227
|
fs.writeFileSync(archive, bytes, { mode: 0o600 });
|
|
187
228
|
const extracted = path.join(staging, "extracted");
|
|
188
229
|
fs.mkdirSync(extracted, { mode: 0o700 });
|
|
189
|
-
run("tar", ["-xzf", archive, "-C", extracted]);
|
|
190
|
-
const
|
|
230
|
+
run("tar", [asset.endsWith(".zip") ? "-xf" : "-xzf", archive, "-C", extracted]);
|
|
231
|
+
const name = syncCoreBinaryName(opts.platform);
|
|
232
|
+
const unpacked = findFile(extracted, name);
|
|
191
233
|
if (!unpacked)
|
|
192
|
-
throw new SyncCoreReleaseError(`${asset} does not contain
|
|
193
|
-
const prepared = path.join(staging,
|
|
234
|
+
throw new SyncCoreReleaseError(`${asset} does not contain ${name}`);
|
|
235
|
+
const prepared = path.join(staging, name);
|
|
194
236
|
fs.copyFileSync(unpacked, prepared);
|
|
195
237
|
fs.chmodSync(prepared, 0o700);
|
|
238
|
+
if (opts.platform === "win32")
|
|
239
|
+
moveRunningBinaryAside(binaryPath);
|
|
196
240
|
fs.renameSync(prepared, binaryPath);
|
|
197
241
|
const marker = syncCoreReleaseMarkerPath(opts.home);
|
|
198
242
|
fs.writeFileSync(`${marker}.tmp`, `${release}\n`, { mode: 0o600 });
|
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.204",
|
|
4
4
|
"description": "HQ by Indigo management CLI — modules and cloud sync",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -117,11 +117,11 @@
|
|
|
117
117
|
"*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}": "eslint --cache --no-error-on-unmatched-pattern"
|
|
118
118
|
},
|
|
119
119
|
"optionalDependencies": {
|
|
120
|
-
"@indigoai-us/hq-hook-win32-x64": "5.345.
|
|
121
|
-
"@indigoai-us/hq-hook-win32-arm64": "5.345.
|
|
122
|
-
"@indigoai-us/hq-hook-darwin-x64": "5.345.
|
|
123
|
-
"@indigoai-us/hq-hook-darwin-arm64": "5.345.
|
|
124
|
-
"@indigoai-us/hq-hook-linux-x64": "5.345.
|
|
125
|
-
"@indigoai-us/hq-hook-linux-arm64": "5.345.
|
|
120
|
+
"@indigoai-us/hq-hook-win32-x64": "5.345.204",
|
|
121
|
+
"@indigoai-us/hq-hook-win32-arm64": "5.345.204",
|
|
122
|
+
"@indigoai-us/hq-hook-darwin-x64": "5.345.204",
|
|
123
|
+
"@indigoai-us/hq-hook-darwin-arm64": "5.345.204",
|
|
124
|
+
"@indigoai-us/hq-hook-linux-x64": "5.345.204",
|
|
125
|
+
"@indigoai-us/hq-hook-linux-arm64": "5.345.204"
|
|
126
126
|
}
|
|
127
127
|
}
|