@indigoai-us/hq-cli 5.345.188 → 5.345.189
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 +97 -0
- package/dist/command-catalog.generated.d.ts +34 -0
- package/dist/command-catalog.generated.js +42 -0
- package/dist/commands/bot.js +15 -3
- package/dist/commands/core-company-index-scope-flag.d.ts +2 -1
- package/dist/commands/core-company-index-scope-flag.js +2 -1
- package/dist/commands/core.js +0 -5
- package/dist/commands/dm-attach.d.ts +73 -0
- package/dist/commands/dm-attach.js +104 -0
- package/dist/commands/dm.d.ts +10 -0
- package/dist/commands/dm.js +114 -2
- package/dist/commands/sources.js +15 -0
- package/dist/hq-cloud-sync-runner/bin/sync-runner.js +45054 -36140
- package/dist/hq-cloud-sync-runner/package.json +1 -1
- package/dist/lib/bot/fleet-local.d.ts +3 -1
- package/dist/lib/bot/fleet-local.js +9 -3
- package/dist/lib/bot/fleet-python.d.ts +6 -0
- package/dist/lib/bot/fleet-python.js +24 -5
- package/dist/lib/bot/probe.d.ts +7 -1
- package/dist/lib/bot/probe.js +11 -3
- package/dist/lib/bot/run.d.ts +9 -0
- package/dist/lib/bot/run.js +114 -8
- package/dist/lib/bot/runtime-failure.d.ts +3 -0
- package/dist/lib/bot/runtime-failure.js +20 -2
- 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/index-render/session-scope.d.ts +3 -3
- package/dist/lib/index-render/session-scope.js +11 -9
- package/dist/lib/index-render/shared.d.ts +6 -4
- package/dist/lib/index-render/shared.js +33 -4
- package/dist/utils/cognito-session.d.ts +4 -2
- package/dist/utils/cognito-session.js +27 -42
- package/dist/utils/hq-root-resolver.d.ts +27 -1
- package/dist/utils/hq-root-resolver.js +65 -33
- package/package.json +14 -14
|
@@ -60,9 +60,9 @@ export interface PlanOptions {
|
|
|
60
60
|
* - Unbound session: an explicit company is used as given; otherwise every
|
|
61
61
|
* company is rebuilt, with a warning unless --all-companies was passed.
|
|
62
62
|
* - Indeterminate session state: refused.
|
|
63
|
-
* -
|
|
64
|
-
*
|
|
65
|
-
*
|
|
63
|
+
* - Bound isolation is not optional. The kill switch may restore every-company
|
|
64
|
+
* writes only for an unbound session (automation). A bound session never
|
|
65
|
+
* writes another company's INDEX.md (feedback_31a4e992).
|
|
66
66
|
* - Every company is checked to be a real directory before anything runs.
|
|
67
67
|
*/
|
|
68
68
|
export declare function planScopedRebuild(target: IndexTarget, args: readonly string[], root: string, env: NodeJS.ProcessEnv, options: PlanOptions): RebuildPlan;
|
|
@@ -196,9 +196,9 @@ function companyArgs(target, slug) {
|
|
|
196
196
|
* - Unbound session: an explicit company is used as given; otherwise every
|
|
197
197
|
* company is rebuilt, with a warning unless --all-companies was passed.
|
|
198
198
|
* - Indeterminate session state: refused.
|
|
199
|
-
* -
|
|
200
|
-
*
|
|
201
|
-
*
|
|
199
|
+
* - Bound isolation is not optional. The kill switch may restore every-company
|
|
200
|
+
* writes only for an unbound session (automation). A bound session never
|
|
201
|
+
* writes another company's INDEX.md (feedback_31a4e992).
|
|
202
202
|
* - Every company is checked to be a real directory before anything runs.
|
|
203
203
|
*/
|
|
204
204
|
export function planScopedRebuild(target, args, root, env, options) {
|
|
@@ -261,17 +261,19 @@ export function planScopedRebuild(target, args, root, env, options) {
|
|
|
261
261
|
}
|
|
262
262
|
}
|
|
263
263
|
}
|
|
264
|
-
// cli.rebuild-index-company-scope is a kill switch (default on). Off,
|
|
265
|
-
// companies
|
|
266
|
-
//
|
|
264
|
+
// cli.rebuild-index-company-scope is a kill switch (default on). Off, an
|
|
265
|
+
// unbound `companies` rebuild goes back to every company (automation). A
|
|
266
|
+
// bound session still rebuilds only its bound companies.
|
|
267
267
|
const gateOff = !options.companyIndexScopeEnabled;
|
|
268
|
-
|
|
268
|
+
const bound = state.kind === "bound";
|
|
269
|
+
if (target === "companies" && gateOff && explicit.length === 0 && !bound) {
|
|
269
270
|
return { runs: [{ target, args: [] }], warning: COMPANY_SCOPE_FLAG_OFF_WARNING };
|
|
270
271
|
}
|
|
271
272
|
if (target === "all") {
|
|
273
|
+
const unscoped = gateOff && !bound;
|
|
272
274
|
return {
|
|
273
|
-
runs: [{ target, args: [...companies.flatMap((slug) => ["--company", slug]), ...(
|
|
274
|
-
warning:
|
|
275
|
+
runs: [{ target, args: [...companies.flatMap((slug) => ["--company", slug]), ...(unscoped ? [UNSCOPED_COMPANIES_INDEX] : [])] }],
|
|
276
|
+
warning: unscoped ? COMPANY_SCOPE_FLAG_OFF_WARNING : warning,
|
|
275
277
|
};
|
|
276
278
|
}
|
|
277
279
|
return { runs: companies.map((slug) => ({ target, args: companyArgs(target, slug) })), warning };
|
|
@@ -54,10 +54,12 @@ export declare function generatedStamp(content: string): string | undefined;
|
|
|
54
54
|
export declare function log(context: RenderContext, message: string): void;
|
|
55
55
|
export declare function projectStatus(root: string, project: string, prdPath: string, fallback: string, expectedStatePrdPath?: string): string;
|
|
56
56
|
/**
|
|
57
|
-
* Project status from
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
57
|
+
* Project status from files that sync with the company: sibling `state.json`
|
|
58
|
+
* (status written by skills such as demand-test), then prd.json `status` /
|
|
59
|
+
* `metadata.status`, then "complete" when every story passes, otherwise
|
|
60
|
+
* `fallback`. Unlike `projectStatus`, this never reads the machine-local
|
|
61
|
+
* `workspace/orchestrator/<project>/state.json`, so synced output renders
|
|
62
|
+
* the same on every machine.
|
|
61
63
|
*/
|
|
62
64
|
export declare function prdProjectStatus(prdPath: string, fallback: string): string;
|
|
63
65
|
export declare function basename(file: string): string;
|
|
@@ -166,14 +166,43 @@ export function projectStatus(root, project, prdPath, fallback, expectedStatePrd
|
|
|
166
166
|
}
|
|
167
167
|
return prdProjectStatus(prdPath, fallback);
|
|
168
168
|
}
|
|
169
|
+
function normalizeProjectStatus(raw) {
|
|
170
|
+
if (["completed", "complete"].includes(raw))
|
|
171
|
+
return "complete";
|
|
172
|
+
if (["in_progress", "active", "started"].includes(raw))
|
|
173
|
+
return "active";
|
|
174
|
+
return raw;
|
|
175
|
+
}
|
|
176
|
+
function explicitProjectStatus(record) {
|
|
177
|
+
if (!record)
|
|
178
|
+
return undefined;
|
|
179
|
+
if (typeof record.status === "string" && record.status.trim()) {
|
|
180
|
+
return normalizeProjectStatus(record.status.trim());
|
|
181
|
+
}
|
|
182
|
+
const metadata = record.metadata;
|
|
183
|
+
if (metadata && typeof metadata === "object" && !Array.isArray(metadata)) {
|
|
184
|
+
const nested = metadata.status;
|
|
185
|
+
if (typeof nested === "string" && nested.trim())
|
|
186
|
+
return normalizeProjectStatus(nested.trim());
|
|
187
|
+
}
|
|
188
|
+
return undefined;
|
|
189
|
+
}
|
|
169
190
|
/**
|
|
170
|
-
* Project status from
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
191
|
+
* Project status from files that sync with the company: sibling `state.json`
|
|
192
|
+
* (status written by skills such as demand-test), then prd.json `status` /
|
|
193
|
+
* `metadata.status`, then "complete" when every story passes, otherwise
|
|
194
|
+
* `fallback`. Unlike `projectStatus`, this never reads the machine-local
|
|
195
|
+
* `workspace/orchestrator/<project>/state.json`, so synced output renders
|
|
196
|
+
* the same on every machine.
|
|
174
197
|
*/
|
|
175
198
|
export function prdProjectStatus(prdPath, fallback) {
|
|
199
|
+
const localState = explicitProjectStatus(readJson(path.join(path.dirname(prdPath), "state.json")));
|
|
200
|
+
if (localState)
|
|
201
|
+
return localState;
|
|
176
202
|
const prd = readJson(prdPath);
|
|
203
|
+
const declared = explicitProjectStatus(prd);
|
|
204
|
+
if (declared)
|
|
205
|
+
return declared;
|
|
177
206
|
if (prd && Array.isArray(prd.userStories) && prd.userStories.length > 0 && prd.userStories.every((story) => Boolean(story.passes)))
|
|
178
207
|
return "complete";
|
|
179
208
|
return fallback;
|
|
@@ -52,7 +52,7 @@ export declare const DEFAULT_VAULT_API_URL: string;
|
|
|
52
52
|
* 5. Discovery: scan well-known locations (`~/HQ`, `~/hq`, `~/Documents/HQ`,
|
|
53
53
|
* …) for a folder carrying a valid `core.yaml` signature. Safety net for
|
|
54
54
|
* installs that never wrote the path back to menubar.json.
|
|
55
|
-
* 6. Fall back to `~/hq` (
|
|
55
|
+
* 6. Fall back to `~/hq` (`C:\hq` on Windows) — or throw, per
|
|
56
56
|
* `opts.onMissing`.
|
|
57
57
|
*
|
|
58
58
|
* Why tiers 3–5 exist:
|
|
@@ -71,7 +71,7 @@ export declare const DEFAULT_VAULT_API_URL: string;
|
|
|
71
71
|
* the walk from latching onto `<hqRoot>/core/` (Codex P2 on hq#146).
|
|
72
72
|
*
|
|
73
73
|
* `opts.onMissing` controls the final arm:
|
|
74
|
-
* - `'fallback'` (default) — return `~/hq` if nothing resolves. Preserves the
|
|
74
|
+
* - `'fallback'` (default) — return `~/hq` (`C:\hq` on Windows) if nothing resolves. Preserves the
|
|
75
75
|
* module-load contract of `DEFAULT_HQ_ROOT`, pinned by commander.js
|
|
76
76
|
* `.option()` callers at registration time.
|
|
77
77
|
* - `'throw'` — throw a user-actionable error. Used by module-management
|
|
@@ -88,6 +88,8 @@ export declare function resolveDefaultHqRoot(opts?: {
|
|
|
88
88
|
env?: NodeJS.ProcessEnv;
|
|
89
89
|
cwd?: string;
|
|
90
90
|
home?: string;
|
|
91
|
+
/** Defaults to process.platform; injectable so tests can exercise win32. */
|
|
92
|
+
platform?: NodeJS.Platform;
|
|
91
93
|
}): string;
|
|
92
94
|
export declare const DEFAULT_HQ_ROOT: string;
|
|
93
95
|
/**
|
|
@@ -30,6 +30,7 @@ import { hasPersonCachedSession, isMachineCredsFileEnvSet, probeMachineIdentity,
|
|
|
30
30
|
import { CLI_NAME, CLI_VERSION } from "../cli-version.js";
|
|
31
31
|
import { daemonDir } from "../lib/mesh/live/daemon/paths.js";
|
|
32
32
|
import { workMeshRoot } from "../lib/mesh/live/paths.js";
|
|
33
|
+
import { defaultHqRootFallback, hqDiscoveryCandidates, pathForPlatform, } from "./hq-root-resolver.js";
|
|
33
34
|
import { acquireLockFileExclusively } from "./exclusive-lock.js";
|
|
34
35
|
import { reportNoninteractivePersonRefreshRejection } from "./cognito-refresh-sentry.js";
|
|
35
36
|
import { decodeJwtPayload, loadCachedTokens, personTokenCacheFile, readPersonTokenFile, readTokenFile, } from "./person-token-cache.js";
|
|
@@ -78,7 +79,7 @@ export const DEFAULT_VAULT_API_URL = process.env.HQ_VAULT_API_URL ?? "https://hq
|
|
|
78
79
|
* 5. Discovery: scan well-known locations (`~/HQ`, `~/hq`, `~/Documents/HQ`,
|
|
79
80
|
* …) for a folder carrying a valid `core.yaml` signature. Safety net for
|
|
80
81
|
* installs that never wrote the path back to menubar.json.
|
|
81
|
-
* 6. Fall back to `~/hq` (
|
|
82
|
+
* 6. Fall back to `~/hq` (`C:\hq` on Windows) — or throw, per
|
|
82
83
|
* `opts.onMissing`.
|
|
83
84
|
*
|
|
84
85
|
* Why tiers 3–5 exist:
|
|
@@ -97,7 +98,7 @@ export const DEFAULT_VAULT_API_URL = process.env.HQ_VAULT_API_URL ?? "https://hq
|
|
|
97
98
|
* the walk from latching onto `<hqRoot>/core/` (Codex P2 on hq#146).
|
|
98
99
|
*
|
|
99
100
|
* `opts.onMissing` controls the final arm:
|
|
100
|
-
* - `'fallback'` (default) — return `~/hq` if nothing resolves. Preserves the
|
|
101
|
+
* - `'fallback'` (default) — return `~/hq` (`C:\hq` on Windows) if nothing resolves. Preserves the
|
|
101
102
|
* module-load contract of `DEFAULT_HQ_ROOT`, pinned by commander.js
|
|
102
103
|
* `.option()` callers at registration time.
|
|
103
104
|
* - `'throw'` — throw a user-actionable error. Used by module-management
|
|
@@ -111,32 +112,35 @@ export const DEFAULT_VAULT_API_URL = process.env.HQ_VAULT_API_URL ?? "https://hq
|
|
|
111
112
|
export function resolveDefaultHqRoot(opts = {}) {
|
|
112
113
|
const env = opts.env ?? process.env;
|
|
113
114
|
const home = opts.home ?? os.homedir();
|
|
115
|
+
const platform = opts.platform ?? process.platform;
|
|
116
|
+
const p = pathForPlatform(platform);
|
|
114
117
|
// Tier 1: explicit override.
|
|
115
118
|
if (env.HQ_ROOT)
|
|
116
|
-
return
|
|
119
|
+
return p.resolve(env.HQ_ROOT);
|
|
117
120
|
// Tier 2: working-tree walk-up — operate on the HQ tree you're inside.
|
|
118
|
-
let cur =
|
|
119
|
-
while (cur !==
|
|
120
|
-
if (isHqRoot(cur))
|
|
121
|
+
let cur = p.resolve(opts.cwd ?? process.cwd());
|
|
122
|
+
while (cur !== p.dirname(cur)) {
|
|
123
|
+
if (isHqRoot(cur, p))
|
|
121
124
|
return cur;
|
|
122
|
-
cur =
|
|
125
|
+
cur = p.dirname(cur);
|
|
123
126
|
}
|
|
124
127
|
// Tiers 3–4: the installer/menubar's canonical "where is HQ" records.
|
|
125
|
-
const fromConfig = readHqRootFromHqConfig(
|
|
128
|
+
const fromConfig = readHqRootFromHqConfig(p.join(home, ".hq"), p);
|
|
126
129
|
if (fromConfig)
|
|
127
|
-
return
|
|
128
|
-
// Tier 5: discover a valid HQ root in the well-known locations
|
|
129
|
-
|
|
130
|
+
return p.resolve(fromConfig);
|
|
131
|
+
// Tier 5: discover a valid HQ root in the well-known locations (on Windows
|
|
132
|
+
// the home-relative ones first, then %SystemDrive%\hq and %LOCALAPPDATA%\hq).
|
|
133
|
+
const discovered = hqDiscoveryCandidates({ platform, env, home }).find((dir) => isValidHqRootSignature(dir, p));
|
|
130
134
|
if (discovered)
|
|
131
135
|
return discovered;
|
|
132
|
-
// Tier 6: nothing resolved.
|
|
136
|
+
// Tier 6: nothing resolved. Fallback is ~/hq, or %SystemDrive%\hq on Windows.
|
|
133
137
|
if (opts.onMissing === "throw") {
|
|
134
138
|
throw new Error("Could not find HQ root. Run this command from within your HQ directory " +
|
|
135
139
|
"(must contain core.yaml AND a companies/ subdirectory), set $HQ_ROOT, " +
|
|
136
140
|
"or install HQ via the installer/menubar so ~/.hq/menubar.json records " +
|
|
137
141
|
"its location.");
|
|
138
142
|
}
|
|
139
|
-
return
|
|
143
|
+
return defaultHqRootFallback({ platform, env, home });
|
|
140
144
|
}
|
|
141
145
|
/**
|
|
142
146
|
* Read the configured HQ root from the installer/menubar records under
|
|
@@ -147,11 +151,11 @@ export function resolveDefaultHqRoot(opts = {}) {
|
|
|
147
151
|
* Returns the first non-empty string, or undefined. Best-effort: a missing or
|
|
148
152
|
* malformed file is treated as "not configured" (never throws).
|
|
149
153
|
*/
|
|
150
|
-
function readHqRootFromHqConfig(hqConfigDir) {
|
|
151
|
-
const fromMenubar = readJsonStringField(
|
|
154
|
+
function readHqRootFromHqConfig(hqConfigDir, p = path) {
|
|
155
|
+
const fromMenubar = readJsonStringField(p.join(hqConfigDir, "menubar.json"), "hqPath");
|
|
152
156
|
if (fromMenubar)
|
|
153
157
|
return fromMenubar;
|
|
154
|
-
return readJsonStringField(
|
|
158
|
+
return readJsonStringField(p.join(hqConfigDir, "config.json"), "hqFolderPath");
|
|
155
159
|
}
|
|
156
160
|
/** Read a top-level string field from a JSON file, or undefined on any miss. */
|
|
157
161
|
function readJsonStringField(file, field) {
|
|
@@ -164,25 +168,6 @@ function readJsonStringField(file, field) {
|
|
|
164
168
|
return undefined;
|
|
165
169
|
}
|
|
166
170
|
}
|
|
167
|
-
/** Well-known parent locations an HQ tree commonly lives in (mirrors hq-sync). */
|
|
168
|
-
function hqDiscoveryCandidates(home = os.homedir()) {
|
|
169
|
-
return [
|
|
170
|
-
path.join(home, "HQ"),
|
|
171
|
-
path.join(home, "hq"),
|
|
172
|
-
path.join(home, "Documents", "HQ"),
|
|
173
|
-
path.join(home, "Documents", "hq"),
|
|
174
|
-
path.join(home, "Desktop", "HQ"),
|
|
175
|
-
path.join(home, "Desktop", "hq"),
|
|
176
|
-
];
|
|
177
|
-
}
|
|
178
|
-
/**
|
|
179
|
-
* Scan the well-known locations for a folder carrying a valid `core.yaml`
|
|
180
|
-
* signature. First match wins; returns undefined if none qualify. Cheap — a
|
|
181
|
-
* few stats plus one small YAML parse on a hit.
|
|
182
|
-
*/
|
|
183
|
-
function discoverHqRootViaCoreYaml(home) {
|
|
184
|
-
return hqDiscoveryCandidates(home).find(isValidHqRootSignature);
|
|
185
|
-
}
|
|
186
171
|
/**
|
|
187
172
|
* True iff `dir` carries a valid hq-core `core.yaml` — canonical
|
|
188
173
|
* (`<dir>/core/core.yaml`, hq-core ≥v14) or legacy (`<dir>/core.yaml`) — that
|
|
@@ -190,9 +175,9 @@ function discoverHqRootViaCoreYaml(home) {
|
|
|
190
175
|
* field check (matching hq-sync's `is_valid_hq_root`) stops a stray `core.yaml`
|
|
191
176
|
* from another tool from false-matching.
|
|
192
177
|
*/
|
|
193
|
-
function isValidHqRootSignature(dir) {
|
|
194
|
-
const canonical =
|
|
195
|
-
const legacy =
|
|
178
|
+
function isValidHqRootSignature(dir, p = path) {
|
|
179
|
+
const canonical = p.join(dir, "core", "core.yaml");
|
|
180
|
+
const legacy = p.join(dir, "core.yaml");
|
|
196
181
|
let file;
|
|
197
182
|
if (fileExists(canonical))
|
|
198
183
|
file = canonical;
|
|
@@ -227,13 +212,13 @@ function fileExists(p) {
|
|
|
227
212
|
* the walk from latching onto the inner `core/` subtree, which carries its own
|
|
228
213
|
* `core.yaml` but no `companies/`.
|
|
229
214
|
*/
|
|
230
|
-
function isHqRoot(dir) {
|
|
231
|
-
const hasCoreYaml = fileExists(
|
|
232
|
-
fileExists(
|
|
215
|
+
function isHqRoot(dir, p = path) {
|
|
216
|
+
const hasCoreYaml = fileExists(p.join(dir, "core", "core.yaml")) ||
|
|
217
|
+
fileExists(p.join(dir, "core.yaml"));
|
|
233
218
|
if (!hasCoreYaml)
|
|
234
219
|
return false;
|
|
235
220
|
try {
|
|
236
|
-
return fs.statSync(
|
|
221
|
+
return fs.statSync(p.join(dir, "companies")).isDirectory();
|
|
237
222
|
}
|
|
238
223
|
catch {
|
|
239
224
|
return false;
|
|
@@ -1,13 +1,39 @@
|
|
|
1
1
|
/** Lightweight HQ-root resolver shared by cloud commands and fast flag scope. */
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
/** Path module matching the target platform's path shapes. */
|
|
4
|
+
export declare function pathForPlatform(platform?: NodeJS.Platform): path.PlatformPath;
|
|
5
|
+
/**
|
|
6
|
+
* The last-resort HQ root when nothing else resolves: `C:\hq` on Windows
|
|
7
|
+
* (`%SystemDrive%\hq`), `~/hq` elsewhere. Pure; all inputs injectable.
|
|
8
|
+
*/
|
|
9
|
+
export declare function defaultHqRootFallback(opts?: {
|
|
10
|
+
platform?: NodeJS.Platform;
|
|
11
|
+
env?: NodeJS.ProcessEnv;
|
|
12
|
+
home?: string;
|
|
13
|
+
}): string;
|
|
14
|
+
/**
|
|
15
|
+
* Well-known locations an HQ tree commonly lives in (mirrors hq-sync). On
|
|
16
|
+
* Windows `%SystemDrive%\hq` then `%LOCALAPPDATA%\hq` come AFTER the
|
|
17
|
+
* home-relative candidates, so an existing unrecorded `%USERPROFILE%\hq`
|
|
18
|
+
* install keeps winning over a stray `C:\hq` (avoids a split root).
|
|
19
|
+
*/
|
|
20
|
+
export declare function hqDiscoveryCandidates(opts?: {
|
|
21
|
+
platform?: NodeJS.Platform;
|
|
22
|
+
env?: NodeJS.ProcessEnv;
|
|
23
|
+
home?: string;
|
|
24
|
+
}): string[];
|
|
2
25
|
/**
|
|
3
26
|
* Resolve the HQ tree root using the same precedence as the Cognito helpers:
|
|
4
27
|
* explicit HQ_ROOT, nearest cwd root, installer records, known locations, then
|
|
5
|
-
* the
|
|
28
|
+
* the platform fallback (`~/hq`, or `C:\hq` on Windows), or an error when
|
|
29
|
+
* requested.
|
|
6
30
|
*/
|
|
7
31
|
export declare function resolveDefaultHqRoot(opts?: {
|
|
8
32
|
onMissing?: "throw" | "fallback";
|
|
9
33
|
env?: NodeJS.ProcessEnv;
|
|
10
34
|
cwd?: string;
|
|
11
35
|
home?: string;
|
|
36
|
+
platform?: NodeJS.Platform;
|
|
12
37
|
}): string;
|
|
38
|
+
export declare function isValidHqRootSignature(dir: string, p?: path.PlatformPath): boolean;
|
|
13
39
|
//# sourceMappingURL=hq-root-resolver.d.ts.map
|
|
@@ -3,26 +3,71 @@ 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 * as yaml from "js-yaml";
|
|
6
|
+
/** Path module matching the target platform's path shapes. */
|
|
7
|
+
export function pathForPlatform(platform = process.platform) {
|
|
8
|
+
return platform === "win32" ? path.win32 : path.posix;
|
|
9
|
+
}
|
|
10
|
+
/** `%SystemDrive%` (for example `C:`), defaulting to `C:` when unset. */
|
|
11
|
+
function windowsSystemDrive(env) {
|
|
12
|
+
const drive = env.SystemDrive ?? env.SYSTEMDRIVE;
|
|
13
|
+
return drive && drive.trim().length > 0 ? drive.trim() : "C:";
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* The last-resort HQ root when nothing else resolves: `C:\hq` on Windows
|
|
17
|
+
* (`%SystemDrive%\hq`), `~/hq` elsewhere. Pure; all inputs injectable.
|
|
18
|
+
*/
|
|
19
|
+
export function defaultHqRootFallback(opts = {}) {
|
|
20
|
+
const platform = opts.platform ?? process.platform;
|
|
21
|
+
const env = opts.env ?? process.env;
|
|
22
|
+
if (platform === "win32") {
|
|
23
|
+
return path.win32.join(`${windowsSystemDrive(env)}\\`, "hq");
|
|
24
|
+
}
|
|
25
|
+
return path.posix.join(opts.home ?? os.homedir(), "hq");
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Well-known locations an HQ tree commonly lives in (mirrors hq-sync). On
|
|
29
|
+
* Windows `%SystemDrive%\hq` then `%LOCALAPPDATA%\hq` come AFTER the
|
|
30
|
+
* home-relative candidates, so an existing unrecorded `%USERPROFILE%\hq`
|
|
31
|
+
* install keeps winning over a stray `C:\hq` (avoids a split root).
|
|
32
|
+
*/
|
|
33
|
+
export function hqDiscoveryCandidates(opts = {}) {
|
|
34
|
+
const platform = opts.platform ?? process.platform;
|
|
35
|
+
const env = opts.env ?? process.env;
|
|
36
|
+
const home = opts.home ?? os.homedir();
|
|
37
|
+
const p = pathForPlatform(platform);
|
|
38
|
+
const candidates = [];
|
|
39
|
+
candidates.push(p.join(home, "HQ"), p.join(home, "hq"), p.join(home, "Documents", "HQ"), p.join(home, "Documents", "hq"), p.join(home, "Desktop", "HQ"), p.join(home, "Desktop", "hq"));
|
|
40
|
+
if (platform === "win32") {
|
|
41
|
+
candidates.push(defaultHqRootFallback({ platform, env, home }));
|
|
42
|
+
const localAppData = env.LOCALAPPDATA?.trim();
|
|
43
|
+
if (localAppData)
|
|
44
|
+
candidates.push(p.join(localAppData, "hq"));
|
|
45
|
+
}
|
|
46
|
+
return [...new Set(candidates)];
|
|
47
|
+
}
|
|
6
48
|
/**
|
|
7
49
|
* Resolve the HQ tree root using the same precedence as the Cognito helpers:
|
|
8
50
|
* explicit HQ_ROOT, nearest cwd root, installer records, known locations, then
|
|
9
|
-
* the
|
|
51
|
+
* the platform fallback (`~/hq`, or `C:\hq` on Windows), or an error when
|
|
52
|
+
* requested.
|
|
10
53
|
*/
|
|
11
54
|
export function resolveDefaultHqRoot(opts = {}) {
|
|
12
55
|
const env = opts.env ?? process.env;
|
|
13
56
|
const home = opts.home ?? os.homedir();
|
|
57
|
+
const platform = opts.platform ?? process.platform;
|
|
58
|
+
const p = pathForPlatform(platform);
|
|
14
59
|
if (env.HQ_ROOT)
|
|
15
|
-
return
|
|
16
|
-
let cur =
|
|
17
|
-
while (cur !==
|
|
18
|
-
if (isHqRoot(cur))
|
|
60
|
+
return p.resolve(env.HQ_ROOT);
|
|
61
|
+
let cur = p.resolve(opts.cwd ?? process.cwd());
|
|
62
|
+
while (cur !== p.dirname(cur)) {
|
|
63
|
+
if (isHqRoot(cur, p))
|
|
19
64
|
return cur;
|
|
20
|
-
cur =
|
|
65
|
+
cur = p.dirname(cur);
|
|
21
66
|
}
|
|
22
|
-
const fromConfig = readHqRootFromHqConfig(
|
|
67
|
+
const fromConfig = readHqRootFromHqConfig(p.join(home, ".hq"), p);
|
|
23
68
|
if (fromConfig)
|
|
24
|
-
return
|
|
25
|
-
const discovered =
|
|
69
|
+
return p.resolve(fromConfig);
|
|
70
|
+
const discovered = hqDiscoveryCandidates({ platform, env, home }).find((dir) => isValidHqRootSignature(dir, p));
|
|
26
71
|
if (discovered)
|
|
27
72
|
return discovered;
|
|
28
73
|
if (opts.onMissing === "throw") {
|
|
@@ -31,13 +76,13 @@ export function resolveDefaultHqRoot(opts = {}) {
|
|
|
31
76
|
"or install HQ via the installer/menubar so ~/.hq/menubar.json records " +
|
|
32
77
|
"its location.");
|
|
33
78
|
}
|
|
34
|
-
return
|
|
79
|
+
return defaultHqRootFallback({ platform, env, home });
|
|
35
80
|
}
|
|
36
|
-
function readHqRootFromHqConfig(hqConfigDir) {
|
|
37
|
-
const fromMenubar = readJsonStringField(
|
|
81
|
+
function readHqRootFromHqConfig(hqConfigDir, p = path) {
|
|
82
|
+
const fromMenubar = readJsonStringField(p.join(hqConfigDir, "menubar.json"), "hqPath");
|
|
38
83
|
if (fromMenubar)
|
|
39
84
|
return fromMenubar;
|
|
40
|
-
return readJsonStringField(
|
|
85
|
+
return readJsonStringField(p.join(hqConfigDir, "config.json"), "hqFolderPath");
|
|
41
86
|
}
|
|
42
87
|
function readJsonStringField(file, field) {
|
|
43
88
|
try {
|
|
@@ -49,22 +94,9 @@ function readJsonStringField(file, field) {
|
|
|
49
94
|
return undefined;
|
|
50
95
|
}
|
|
51
96
|
}
|
|
52
|
-
function
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
path.join(home, "hq"),
|
|
56
|
-
path.join(home, "Documents", "HQ"),
|
|
57
|
-
path.join(home, "Documents", "hq"),
|
|
58
|
-
path.join(home, "Desktop", "HQ"),
|
|
59
|
-
path.join(home, "Desktop", "hq"),
|
|
60
|
-
];
|
|
61
|
-
}
|
|
62
|
-
function discoverHqRootViaCoreYaml(home) {
|
|
63
|
-
return hqDiscoveryCandidates(home).find(isValidHqRootSignature);
|
|
64
|
-
}
|
|
65
|
-
function isValidHqRootSignature(dir) {
|
|
66
|
-
const canonical = path.join(dir, "core", "core.yaml");
|
|
67
|
-
const legacy = path.join(dir, "core.yaml");
|
|
97
|
+
export function isValidHqRootSignature(dir, p = path) {
|
|
98
|
+
const canonical = p.join(dir, "core", "core.yaml");
|
|
99
|
+
const legacy = p.join(dir, "core.yaml");
|
|
68
100
|
let file;
|
|
69
101
|
if (fileExists(canonical))
|
|
70
102
|
file = canonical;
|
|
@@ -88,13 +120,13 @@ function fileExists(file) {
|
|
|
88
120
|
return false;
|
|
89
121
|
}
|
|
90
122
|
}
|
|
91
|
-
function isHqRoot(dir) {
|
|
92
|
-
const hasCoreYaml = fileExists(
|
|
93
|
-
fileExists(
|
|
123
|
+
function isHqRoot(dir, p = path) {
|
|
124
|
+
const hasCoreYaml = fileExists(p.join(dir, "core", "core.yaml")) ||
|
|
125
|
+
fileExists(p.join(dir, "core.yaml"));
|
|
94
126
|
if (!hasCoreYaml)
|
|
95
127
|
return false;
|
|
96
128
|
try {
|
|
97
|
-
return fs.statSync(
|
|
129
|
+
return fs.statSync(p.join(dir, "companies")).isDirectory();
|
|
98
130
|
}
|
|
99
131
|
catch {
|
|
100
132
|
return false;
|
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.189",
|
|
4
4
|
"description": "HQ by Indigo management CLI — modules and cloud sync",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -53,8 +53,8 @@
|
|
|
53
53
|
"@aws-sdk/client-s3": "^3.1049.0",
|
|
54
54
|
"@aws-sdk/dsql-signer": "^3.1146.0",
|
|
55
55
|
"@indigoai-us/hq-cloud": "6.18.107",
|
|
56
|
-
"@indigoai-us/hq-flags-client": "^0.
|
|
57
|
-
"@modelcontextprotocol/sdk": "1.
|
|
56
|
+
"@indigoai-us/hq-flags-client": "^0.2.0",
|
|
57
|
+
"@modelcontextprotocol/sdk": "1.32.2",
|
|
58
58
|
"@sentry/node": "^10.49.0",
|
|
59
59
|
"@tobilu/qmd": "2.5.3",
|
|
60
60
|
"ajv": "^8.20.0",
|
|
@@ -62,13 +62,13 @@
|
|
|
62
62
|
"chalk": "^5.3.0",
|
|
63
63
|
"commander": "^12.1.0",
|
|
64
64
|
"js-yaml": "^4.1.0",
|
|
65
|
-
"mqtt": "5.
|
|
65
|
+
"mqtt": "5.16.1",
|
|
66
66
|
"open": "^10.1.0",
|
|
67
67
|
"pg": "^8.23.1",
|
|
68
68
|
"semver": "^7.8.5",
|
|
69
69
|
"simple-git": "^3.27.0",
|
|
70
|
-
"smol-toml": "1.
|
|
71
|
-
"varlock": "1.
|
|
70
|
+
"smol-toml": "1.9.1",
|
|
71
|
+
"varlock": "1.23.0"
|
|
72
72
|
},
|
|
73
73
|
"devDependencies": {
|
|
74
74
|
"@eslint/js": "^10.0.1",
|
|
@@ -77,8 +77,8 @@
|
|
|
77
77
|
"@types/node": "^22.0.0",
|
|
78
78
|
"@types/pg": "^8.23.1",
|
|
79
79
|
"@types/semver": "^7.8.0",
|
|
80
|
-
"@vitest/coverage-v8": "4.1.
|
|
81
|
-
"aedes": "1.
|
|
80
|
+
"@vitest/coverage-v8": "4.1.12",
|
|
81
|
+
"aedes": "1.2.0",
|
|
82
82
|
"aws-sdk-client-mock": "^4.1.0",
|
|
83
83
|
"esbuild": "0.28.2",
|
|
84
84
|
"eslint": "^10.5.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.189",
|
|
118
|
+
"@indigoai-us/hq-hook-win32-arm64": "5.345.189",
|
|
119
|
+
"@indigoai-us/hq-hook-darwin-x64": "5.345.189",
|
|
120
|
+
"@indigoai-us/hq-hook-darwin-arm64": "5.345.189",
|
|
121
|
+
"@indigoai-us/hq-hook-linux-x64": "5.345.189",
|
|
122
|
+
"@indigoai-us/hq-hook-linux-arm64": "5.345.189"
|
|
123
123
|
}
|
|
124
124
|
}
|