@indigoai-us/hq-cli 5.345.34 → 5.345.36
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 +8 -0
- package/dist/commands/cloud.js +6 -5
- package/dist/commands/core-resolve-company-native.d.ts +2 -0
- package/dist/commands/core-resolve-company-native.js +54 -17
- package/dist/commands/install-global.d.ts +1 -1
- package/dist/commands/install-global.js +5 -4
- package/dist/commands/pkg-install.js +5 -3
- package/dist/hq-cloud-sync-runner/bin/sync-runner.js +7226 -4092
- package/dist/hq-cloud-sync-runner/package.json +1 -1
- 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/utils/package-use-lease.d.ts +17 -0
- package/dist/utils/package-use-lease.js +36 -0
- package/dist/utils/self-update.d.ts +4 -3
- package/dist/utils/self-update.js +12 -5
- package/dist/utils/sync-operation-lock.d.ts +37 -0
- package/dist/utils/sync-operation-lock.js +40 -0
- package/dist/utils/version-gate.d.ts +20 -4
- package/dist/utils/version-gate.js +112 -11
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [5.345.36] — 2026-10-07
|
|
6
|
+
- A queued `hq sync` no longer pins the installed `hq` version while it waits its turn, so `hq pkg update` is no longer blocked for the length of somebody else's sync.
|
|
7
|
+
- The flag-registry background refresh no longer throws as it starts, so running any `hq` command stops filing a crash report that nothing in the command caused.
|
|
8
|
+
|
|
9
|
+
## [5.345.35] — 2026-10-07
|
|
10
|
+
- Claude plugin installs now use the HQ packages marketplace by default; local marketplace directory and environment overrides remain available.
|
|
11
|
+
- Pin self-updates to the pnpm version recorded by the global install; preserve store dirs on pnpm 11+ and omit pnpm 10 virtual-store flags.
|
|
12
|
+
|
|
5
13
|
## [5.345.34] — 2026-10-07
|
|
6
14
|
- Protect live remote-control tmux sessions with socket-scoped creator identity before cleanup.
|
|
7
15
|
|
package/dist/commands/cloud.js
CHANGED
|
@@ -16,7 +16,8 @@ import { isAccessDeniedError, accessDeniedKeyOf, formatAccessDenied, } from "../
|
|
|
16
16
|
import chalk from "chalk";
|
|
17
17
|
import * as fs from "fs";
|
|
18
18
|
import * as path from "path";
|
|
19
|
-
import { share,
|
|
19
|
+
import { share, getStateDir, listJournals, VaultClient, computePersonalVaultPaths, PERSONAL_VAULT_JOURNAL_SLUG, resolvePullScope, syncDoctor, } from "@indigoai-us/hq-cloud";
|
|
20
|
+
import { syncLeg } from "../utils/sync-operation-lock.js";
|
|
20
21
|
import { DEFAULT_HQ_ROOT, ensureCognitoToken, buildVaultConfig, loadCachedTokens, } from "../utils/cognito-session.js";
|
|
21
22
|
import { resolveUploadAuthor } from "../utils/upload-author.js";
|
|
22
23
|
import { companyFolderExceedsThreshold, emitNarrowHint, isStrictRefusal, resolveBannerLevel, resolveNarrowHintPresentationLevel, resolveNarrowHintMinBytes, } from "../lib/narrow-hint-banner.js";
|
|
@@ -1146,7 +1147,7 @@ export function registerCloudCommands(program) {
|
|
|
1146
1147
|
}
|
|
1147
1148
|
const excludePrefixes = readScopeExcludePrefixes(pullScope);
|
|
1148
1149
|
syncHealth = beginSyncHealthReport();
|
|
1149
|
-
const result = await
|
|
1150
|
+
const result = await syncLeg({
|
|
1150
1151
|
company: options.company,
|
|
1151
1152
|
onConflict: options.onConflict,
|
|
1152
1153
|
vaultConfig,
|
|
@@ -1437,7 +1438,7 @@ async function runPullAll(hqRoot, onConflict, modeAllOverride, skipPersonal, for
|
|
|
1437
1438
|
// via the shared resolver, so the actual pull is scoped — the CLI no
|
|
1438
1439
|
// longer seeds an all-mode PullRecord that wedges the menubar runner.
|
|
1439
1440
|
resolveScope: (companyUid, slug) => resolvePullScope(realClient, companyUid, slug, hqRoot),
|
|
1440
|
-
sync: (opts) =>
|
|
1441
|
+
sync: (opts) => syncLeg({
|
|
1441
1442
|
company: opts.company,
|
|
1442
1443
|
hqRoot: opts.hqRoot,
|
|
1443
1444
|
vaultConfig,
|
|
@@ -1508,7 +1509,7 @@ export async function runPullPersonal(hqRoot, onConflict) {
|
|
|
1508
1509
|
listPersonEntities: () => client.entity.listByType("person"),
|
|
1509
1510
|
getEntity: async () => null,
|
|
1510
1511
|
});
|
|
1511
|
-
const result = await
|
|
1512
|
+
const result = await syncLeg({
|
|
1512
1513
|
company: personalUid,
|
|
1513
1514
|
...(onConflict ? { onConflict } : {}),
|
|
1514
1515
|
vaultConfig,
|
|
@@ -1853,7 +1854,7 @@ async function runNowSingle(hqRoot, company, personal, message, onConflict, mode
|
|
|
1853
1854
|
}
|
|
1854
1855
|
console.log(chalk.dim(" → pull leg"));
|
|
1855
1856
|
const excludePrefixes = readScopeExcludePrefixes(pullScope);
|
|
1856
|
-
const pullResult = await
|
|
1857
|
+
const pullResult = await syncLeg({
|
|
1857
1858
|
company: targetCompany,
|
|
1858
1859
|
vaultConfig,
|
|
1859
1860
|
hqRoot,
|
|
@@ -19,6 +19,8 @@ export type ResolveCompanyDependencies = {
|
|
|
19
19
|
stdin?: string;
|
|
20
20
|
stdinIsTty?: boolean;
|
|
21
21
|
spawnHq?: (args: string[], env: NodeJS.ProcessEnv, timeoutMs: number, cwd?: string, command?: string) => Promise<HqResult>;
|
|
22
|
+
onHqSpawn?: (args: readonly string[]) => void;
|
|
23
|
+
onHqExit?: (args: readonly string[]) => void;
|
|
22
24
|
pid?: number;
|
|
23
25
|
random?: () => number;
|
|
24
26
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Native port of resolve-company.sh. This module stays on the fast import path. */
|
|
2
|
-
import { spawn
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
4
|
import * as path from "node:path";
|
|
5
5
|
import { awkCannotOpenFileDiagnostic } from "../lib/core-utils/awk-diagnostics.js";
|
|
@@ -272,30 +272,46 @@ function commandOnPath(name, env) {
|
|
|
272
272
|
}
|
|
273
273
|
return false;
|
|
274
274
|
}
|
|
275
|
-
function runHq(args, env, timeoutMs, cwd, command = "hq") {
|
|
275
|
+
function runHq(args, env, timeoutMs, cwd, command = "hq", onExitObserved) {
|
|
276
276
|
return new Promise((resolve) => {
|
|
277
277
|
let settled = false;
|
|
278
278
|
let stdout = "";
|
|
279
279
|
let child;
|
|
280
280
|
let timeoutKill;
|
|
281
281
|
let timedOut = false;
|
|
282
|
-
const signalGroup = (signal) => {
|
|
283
|
-
if (!child?.pid)
|
|
282
|
+
const signalGroup = (signal, onComplete) => {
|
|
283
|
+
if (!child?.pid) {
|
|
284
|
+
onComplete?.();
|
|
284
285
|
return;
|
|
286
|
+
}
|
|
285
287
|
try {
|
|
286
288
|
if (process.platform === "win32") {
|
|
287
289
|
const taskkill = path.join(env.SystemRoot ?? env.WINDIR ?? "C:\\Windows", "System32", "taskkill.exe");
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
290
|
+
let completed = false;
|
|
291
|
+
const complete = (code) => {
|
|
292
|
+
if (completed)
|
|
293
|
+
return;
|
|
294
|
+
completed = true;
|
|
295
|
+
if (code !== 0)
|
|
296
|
+
child?.kill(signal);
|
|
297
|
+
onComplete?.();
|
|
298
|
+
};
|
|
299
|
+
const killer = spawn(taskkill, ["/PID", String(child.pid), "/T", "/F"], {
|
|
300
|
+
stdio: "ignore",
|
|
301
|
+
windowsHide: true,
|
|
302
|
+
});
|
|
303
|
+
killer.once("error", () => complete(1));
|
|
304
|
+
killer.once("exit", (code) => complete(code));
|
|
291
305
|
}
|
|
292
306
|
else {
|
|
293
307
|
process.kill(-child.pid, signal);
|
|
308
|
+
onComplete?.();
|
|
294
309
|
}
|
|
295
310
|
}
|
|
296
311
|
catch (error) {
|
|
297
312
|
if (errorCode(error) !== "ESRCH")
|
|
298
313
|
finish({ code: 1, stdout });
|
|
314
|
+
onComplete?.();
|
|
299
315
|
}
|
|
300
316
|
};
|
|
301
317
|
const finish = (result) => {
|
|
@@ -310,13 +326,13 @@ function runHq(args, env, timeoutMs, cwd, command = "hq") {
|
|
|
310
326
|
const timer = setTimeout(() => {
|
|
311
327
|
timedOut = true;
|
|
312
328
|
if (child?.pid) {
|
|
313
|
-
signalGroup("SIGTERM")
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
}
|
|
329
|
+
signalGroup("SIGTERM", () => {
|
|
330
|
+
// The shell wrapper returns shortly after its 2 second deadline. Give
|
|
331
|
+
// the tree time to exit, then force-kill and wait for Windows cleanup.
|
|
332
|
+
timeoutKill = setTimeout(() => {
|
|
333
|
+
signalGroup("SIGKILL", () => finish({ code: 124, stdout }));
|
|
334
|
+
}, 250);
|
|
335
|
+
});
|
|
320
336
|
}
|
|
321
337
|
else {
|
|
322
338
|
finish({ code: 124, stdout });
|
|
@@ -349,9 +365,24 @@ function runHq(args, env, timeoutMs, cwd, command = "hq") {
|
|
|
349
365
|
child.stdout?.setEncoding("utf8");
|
|
350
366
|
child.stdout?.on("data", (chunk) => { stdout += chunk; });
|
|
351
367
|
child.once("error", () => finish({ code: 127, stdout }));
|
|
368
|
+
if (onExitObserved)
|
|
369
|
+
child.once("exit", onExitObserved);
|
|
352
370
|
child.once("exit", (code, signal) => {
|
|
353
|
-
|
|
354
|
-
|
|
371
|
+
if (timedOut)
|
|
372
|
+
return;
|
|
373
|
+
clearTimeout(timer);
|
|
374
|
+
const result = { code: typeof code === "number" ? code : signal ? 128 : 1, stdout };
|
|
375
|
+
if (process.platform === "win32") {
|
|
376
|
+
// Tree cleanup is asynchronous on Windows. Start it after the direct
|
|
377
|
+
// child exits, but let the resolver advance without waiting for
|
|
378
|
+
// taskkill; detached descendants must not hold up the next lookup.
|
|
379
|
+
signalGroup("SIGTERM");
|
|
380
|
+
finish(result);
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
signalGroup("SIGTERM", () => {
|
|
384
|
+
finish(result);
|
|
385
|
+
});
|
|
355
386
|
});
|
|
356
387
|
});
|
|
357
388
|
}
|
|
@@ -502,7 +533,13 @@ export async function resolveCompany(args, dependencies) {
|
|
|
502
533
|
hqEnv.HQ_ROOT = dependencies.hqRoot;
|
|
503
534
|
hqEnv.CLAUDE_PROJECT_DIR = dependencies.hqRoot;
|
|
504
535
|
}
|
|
505
|
-
const
|
|
536
|
+
const invokeImplementation = dependencies.spawnHq
|
|
537
|
+
? dependencies.spawnHq
|
|
538
|
+
: (args, childEnv, timeoutMs, cwd, command) => runHq(args, childEnv, timeoutMs, cwd, command, () => dependencies.onHqExit?.(args));
|
|
539
|
+
const invoke = (args, env, timeoutMs, cwd, command) => {
|
|
540
|
+
dependencies.onHqSpawn?.(args);
|
|
541
|
+
return invokeImplementation(args, env, timeoutMs, cwd, command);
|
|
542
|
+
};
|
|
506
543
|
const defaultResponse = await invoke(["mesh", "context", "default", "get", "--json"], hqEnv, 2_000, root);
|
|
507
544
|
const defaultSlug = defaultSlugFromJson(defaultResponse.stdout, knownSlugs);
|
|
508
545
|
if (!defaultSlug)
|
|
@@ -39,7 +39,7 @@ export interface ClaudePluginSource {
|
|
|
39
39
|
pluginName: string;
|
|
40
40
|
pluginId: string;
|
|
41
41
|
}
|
|
42
|
-
/** Resolve
|
|
42
|
+
/** Resolve local overrides first, then the published HQ marketplace. */
|
|
43
43
|
export declare function resolveClaudePluginSource(explicitDir?: string, env?: NodeJS.ProcessEnv, repository?: string): ClaudePluginSource;
|
|
44
44
|
export { AnywhereRuntimeDisabledError as AnywhereDisabledError };
|
|
45
45
|
export declare const isAnywhereEnabled: (_home: string, env?: NodeJS.ProcessEnv) => boolean;
|
|
@@ -46,14 +46,15 @@ export function hookTargetFor(hqRoot, home, daemonInstalled, env = process.env)
|
|
|
46
46
|
const daemonMatchesRoot = configuredRoot !== undefined && realpathOrSelf(configuredRoot) === realpathOrSelf(hqRoot);
|
|
47
47
|
return installed && daemonMatchesRoot && fs.existsSync(shimPath(hqRoot)) ? 'shim' : 'master';
|
|
48
48
|
}
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
const HQ_PACKAGES_MARKETPLACE_REPOSITORY = 'indigoai-us/hq-packages';
|
|
50
|
+
/** Resolve local overrides first, then the published HQ marketplace. */
|
|
51
|
+
export function resolveClaudePluginSource(explicitDir, env = process.env, repository = HQ_PACKAGES_MARKETPLACE_REPOSITORY) {
|
|
51
52
|
const configured = explicitDir?.trim() || env.HQ_CLAUDE_PLUGIN_MARKETPLACE?.trim();
|
|
52
53
|
if (!configured) {
|
|
53
|
-
if (repository ===
|
|
54
|
+
if (repository === HQ_PACKAGES_MARKETPLACE_REPOSITORY) {
|
|
54
55
|
return { sourceDir: repository, marketplaceName: 'hq-packages', pluginName: 'hq', pluginId: 'hq@hq-packages' };
|
|
55
56
|
}
|
|
56
|
-
throw Object.assign(new Error(
|
|
57
|
+
throw Object.assign(new Error(`Unsupported published HQ Claude plugin repository "${repository}". Pass --marketplace-dir <dir> or set HQ_CLAUDE_PLUGIN_MARKETPLACE for a local marketplace.`), { expected: true });
|
|
57
58
|
}
|
|
58
59
|
const sourceDir = path.resolve(configured);
|
|
59
60
|
const manifestPath = path.join(sourceDir, '.claude-plugin', 'marketplace.json');
|
|
@@ -72,10 +72,12 @@ export function registerPackageInstallCommand(parent) {
|
|
|
72
72
|
if (opts.via === 'plugin' &&
|
|
73
73
|
!marketplaceDir?.trim() &&
|
|
74
74
|
!process.env.HQ_CLAUDE_PLUGIN_MARKETPLACE?.trim()) {
|
|
75
|
-
if (await anywhereRuntimeAllowed(process.env)) {
|
|
76
|
-
|
|
77
|
-
|
|
75
|
+
if (!(await anywhereRuntimeAllowed(process.env))) {
|
|
76
|
+
console.error(chalk.red('Install failed:'), 'HQ Anywhere plugin installation requires both the hq-anywhere-runtime flag and your person setting.');
|
|
77
|
+
process.exit(1);
|
|
78
78
|
}
|
|
79
|
+
await runInstallGlobal({ ...opts, marketplaceRepository: 'indigoai-us/hq-packages' });
|
|
80
|
+
return;
|
|
79
81
|
}
|
|
80
82
|
await runInstallGlobal({ ...opts, marketplaceDir });
|
|
81
83
|
return;
|