@indigoai-us/hq-cli 5.319.0 → 5.321.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.
Files changed (32) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/assets/scaffold/core/scripts/repo-run-registry.sh +3 -1
  3. package/assets/scaffold/core/scripts/session-journal.sh +40 -2
  4. package/dist/bin/hq-auth-refresh.d.ts +3 -1
  5. package/dist/bin/hq-auth-refresh.js +41 -6
  6. package/dist/command-catalog.generated.d.ts +24 -0
  7. package/dist/command-catalog.generated.js +30 -0
  8. package/dist/commands/core-hook-batch-command.d.ts +3 -0
  9. package/dist/commands/core-hook-batch-command.js +27 -0
  10. package/dist/commands/core-hook-batch-flag.d.ts +4 -0
  11. package/dist/commands/core-hook-batch-flag.js +7 -0
  12. package/dist/commands/core-hook-batch-native.d.ts +10 -0
  13. package/dist/commands/core-hook-batch-native.js +85 -0
  14. package/dist/commands/core-repo-run-registry-native.js +6 -1
  15. package/dist/commands/core-session-journal-native.d.ts +8 -1
  16. package/dist/commands/core-session-journal-native.js +77 -18
  17. package/dist/commands/core.js +2 -0
  18. package/dist/commands/lanes.js +28 -5
  19. package/dist/fast-core.js +13 -0
  20. package/dist/fast-scaffold.d.ts +2 -0
  21. package/dist/fast-scaffold.js +10 -0
  22. package/dist/index.d.ts +9 -0
  23. package/dist/index.js +135 -63
  24. package/dist/lib/daemon/run.d.ts +2 -0
  25. package/dist/lib/daemon/run.js +10 -1
  26. package/dist/lib/lanes/flag-keys.d.ts +3 -0
  27. package/dist/lib/lanes/flag-keys.js +3 -0
  28. package/dist/lib/work-context/bind-best.d.ts +1 -1
  29. package/dist/lib/work-context/bind-best.js +3 -3
  30. package/dist/utils/package-use-lease.d.ts +25 -0
  31. package/dist/utils/package-use-lease.js +213 -0
  32. package/package.json +1 -1
@@ -0,0 +1,213 @@
1
+ import { createHash } from "node:crypto";
2
+ import { mkdirSync, readFileSync, realpathSync, renameSync, rmSync, writeFileSync, } from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { performance } from "node:perf_hooks";
7
+ const CLI_NAME = "@indigoai-us/hq-cli";
8
+ const UPDATE_REQUEST = "update.pending.json";
9
+ export class PackageUseUpdatePendingError extends Error {
10
+ constructor(message) {
11
+ super(message);
12
+ this.name = "PackageUseUpdatePendingError";
13
+ }
14
+ }
15
+ function errorLabel(error) {
16
+ if (error && typeof error === "object") {
17
+ const value = error;
18
+ if (typeof value.code === "string")
19
+ return value.code;
20
+ if (typeof value.name === "string")
21
+ return value.name;
22
+ }
23
+ return "unknown error";
24
+ }
25
+ function readPackage(directory) {
26
+ try {
27
+ return JSON.parse(readFileSync(path.join(directory, "package.json"), "utf8"));
28
+ }
29
+ catch (error) {
30
+ const code = error.code;
31
+ if (code === "ENOENT" || code === "ENOTDIR")
32
+ return null;
33
+ throw new Error(`Unable to inspect the HQ CLI install (${errorLabel(error)})`, { cause: error });
34
+ }
35
+ }
36
+ function findCliPackage(directory) {
37
+ while (true) {
38
+ const pkg = readPackage(directory);
39
+ if (pkg?.name === CLI_NAME) {
40
+ return { root: directory, version: typeof pkg.version === "string" ? pkg.version : "unknown" };
41
+ }
42
+ const parent = path.dirname(directory);
43
+ if (parent === directory)
44
+ return null;
45
+ directory = parent;
46
+ }
47
+ }
48
+ function isPnpmGlobal(root) {
49
+ const normalized = root.replace(/\\/g, "/").replace(/\/+$/, "");
50
+ const parts = normalized.split("/").filter(Boolean);
51
+ for (let i = 0; i < parts.length - 1; i += 1) {
52
+ if (parts[i] === "global" && (/^\d+$/.test(parts[i + 1]) || /^v\d+$/.test(parts[i + 1])))
53
+ return true;
54
+ }
55
+ const home = process.env.PNPM_HOME?.replace(/\\/g, "/").replace(/\/+$/, "");
56
+ const globalRoot = home ? `${home}/global` : null;
57
+ const left = process.platform === "win32" ? normalized.toLowerCase() : normalized;
58
+ const right = globalRoot && process.platform === "win32" ? globalRoot.toLowerCase() : globalRoot;
59
+ return Boolean(right && (left === right || left.startsWith(`${right}/`)));
60
+ }
61
+ function isBunGlobal(root) {
62
+ const parts = root.replace(/\\/g, "/").split("/").filter(Boolean);
63
+ return parts.some((part, i) => part === "install" && parts[i + 1] === "global" && parts[i + 2] === "node_modules");
64
+ }
65
+ export function npmPrefix(root, platform = process.platform) {
66
+ const normalized = root.replace(/\\/g, "/").replace(/\/+$/, "");
67
+ const parts = normalized.split("/");
68
+ const index = parts.lastIndexOf("node_modules");
69
+ if (index < 0)
70
+ return null;
71
+ const end = parts[index - 1] === "lib" ? index - 1 : index;
72
+ const prefix = parts.slice(0, end).join("/");
73
+ if (platform === "win32" && /^[a-z]:$/i.test(prefix))
74
+ return `${prefix}/`;
75
+ if (!prefix && normalized.startsWith("/"))
76
+ return "/";
77
+ return prefix || null;
78
+ }
79
+ function packageUseStateDirectory() {
80
+ if (process.platform === "win32") {
81
+ const localAppData = process.env.LOCALAPPDATA;
82
+ if (!localAppData)
83
+ throw new Error("LOCALAPPDATA is unavailable for the HQ CLI package-use lease");
84
+ return path.join(localAppData, "hq-cli", "state", "package-use");
85
+ }
86
+ return path.join(process.env.XDG_STATE_HOME?.trim() || path.join(os.homedir(), ".local", "state"), "hq-cli", "package-use");
87
+ }
88
+ function normalizedPrefix(prefix) {
89
+ let canonical = realpathSync.native(prefix).replace(/\\/g, "/");
90
+ canonical = canonical.replace(/^\\\\\?\\UNC\\/i, "//").replace(/^\\\\\?\\/, "");
91
+ return process.platform === "win32" ? canonical.toLowerCase() : canonical;
92
+ }
93
+ export function packageUseLeaseDirectory(prefix, stateDirectory = packageUseStateDirectory()) {
94
+ const digest = createHash("sha256").update(normalizedPrefix(prefix), "utf8").digest("hex");
95
+ return path.join(stateDirectory, digest);
96
+ }
97
+ export function packageUseUpdateRequestPath(prefix, stateDirectory) {
98
+ return path.join(packageUseLeaseDirectory(prefix, stateDirectory), UPDATE_REQUEST);
99
+ }
100
+ function waitForUpdateRequest(requestPath, timeoutMs) {
101
+ const started = Date.now();
102
+ const sleeper = new Int32Array(new SharedArrayBuffer(4));
103
+ while (true) {
104
+ try {
105
+ const request = JSON.parse(readFileSync(requestPath, "utf8"));
106
+ const pid = request.pid;
107
+ if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0) {
108
+ throw new Error("invalid update request");
109
+ }
110
+ try {
111
+ process.kill(pid, 0);
112
+ }
113
+ catch (error) {
114
+ if (error.code === "ESRCH") {
115
+ rmSync(requestPath, { force: true });
116
+ return;
117
+ }
118
+ }
119
+ }
120
+ catch (error) {
121
+ if (error.code === "ENOENT")
122
+ return;
123
+ throw new Error(`Unable to read the HQ CLI update request (${errorLabel(error)})`, { cause: error });
124
+ }
125
+ if (Date.now() - started >= timeoutMs) {
126
+ throw new PackageUseUpdatePendingError("The HQ CLI package is being updated; retry after the update finishes");
127
+ }
128
+ Atomics.wait(sleeper, 0, 0, 100);
129
+ }
130
+ }
131
+ export function acquirePackageUseLease(prefix, hqVersion, stateDirectory) {
132
+ const directory = packageUseLeaseDirectory(prefix, stateDirectory);
133
+ mkdirSync(directory, { recursive: true, mode: 0o700 });
134
+ const requestPath = path.join(directory, UPDATE_REQUEST);
135
+ const record = {
136
+ pid: process.pid,
137
+ start_time_ms: Math.floor(performance.timeOrigin),
138
+ hq_version: hqVersion,
139
+ };
140
+ const leasePath = path.join(directory, `${record.pid}-${record.start_time_ms}.json`);
141
+ const tempPath = `${leasePath}.${Math.random().toString(36).slice(2)}.tmp`;
142
+ let released = false;
143
+ const release = () => {
144
+ if (released)
145
+ return;
146
+ released = true;
147
+ process.removeListener("exit", releaseOnExit);
148
+ try {
149
+ rmSync(leasePath, { force: true });
150
+ }
151
+ catch (error) {
152
+ process.stderr.write(`hq-cli package-use lease release failed (${errorLabel(error)})\n`);
153
+ }
154
+ };
155
+ const releaseOnExit = () => release();
156
+ for (let attempt = 0; attempt < 2; attempt += 1) {
157
+ waitForUpdateRequest(requestPath, 30_000);
158
+ try {
159
+ writeFileSync(tempPath, `${JSON.stringify(record)}\n`, { flag: "wx", mode: 0o600 });
160
+ renameSync(tempPath, leasePath);
161
+ }
162
+ catch (error) {
163
+ rmSync(tempPath, { force: true });
164
+ throw new Error(`Unable to publish the HQ CLI package-use lease (${errorLabel(error)})`, { cause: error });
165
+ }
166
+ if (!exists(requestPath)) {
167
+ process.once("exit", releaseOnExit);
168
+ return { leasePath, release };
169
+ }
170
+ rmSync(leasePath, { force: true });
171
+ }
172
+ throw new PackageUseUpdatePendingError("An HQ CLI update is waiting for the package lease; retry when it finishes");
173
+ }
174
+ function exists(file) {
175
+ try {
176
+ readFileSync(file);
177
+ return true;
178
+ }
179
+ catch (error) {
180
+ if (error.code === "ENOENT")
181
+ return false;
182
+ throw error;
183
+ }
184
+ }
185
+ export function acquireCurrentPackageUseLease() {
186
+ const pkg = findCliPackage(path.dirname(fileURLToPath(import.meta.url)));
187
+ if (!pkg || isPnpmGlobal(pkg.root) || isBunGlobal(pkg.root))
188
+ return null;
189
+ const prefix = npmPrefix(pkg.root);
190
+ if (!prefix)
191
+ return null;
192
+ if (process.platform !== "win32") {
193
+ const parts = pkg.root.replace(/\\/g, "/").replace(/\/+$/, "").split("/").filter(Boolean);
194
+ const nodeModules = parts.lastIndexOf("node_modules");
195
+ if (nodeModules > 0 && parts[nodeModules - 1] !== "lib")
196
+ return null;
197
+ }
198
+ return acquirePackageUseLease(prefix, pkg.version);
199
+ }
200
+ /** True while the desktop updater owns the per-prefix update request. */
201
+ export function currentPackageUseUpdateRequested() {
202
+ const pkg = findCliPackage(path.dirname(fileURLToPath(import.meta.url)));
203
+ if (!pkg || isPnpmGlobal(pkg.root) || isBunGlobal(pkg.root))
204
+ return false;
205
+ const prefix = npmPrefix(pkg.root);
206
+ if (!prefix)
207
+ return false;
208
+ return exists(packageUseUpdateRequestPath(prefix));
209
+ }
210
+ export const __test__ = {
211
+ waitForUpdateRequest,
212
+ };
213
+ //# sourceMappingURL=package-use-lease.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indigoai-us/hq-cli",
3
- "version": "5.319.0",
3
+ "version": "5.321.0",
4
4
  "description": "HQ by Indigo management CLI — modules and cloud sync",
5
5
  "main": "dist/index.js",
6
6
  "bin": {