@indigoai-us/hq-cli 5.207.0 → 5.208.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.
- package/CHANGELOG.md +8 -0
- package/dist/command-catalog.generated.d.ts +6 -0
- package/dist/command-catalog.generated.js +8 -0
- package/dist/commands/lanes.d.ts +2 -0
- package/dist/commands/lanes.js +29 -5
- package/dist/lib/daemon/hq-cloud.d.ts +27 -3
- package/dist/lib/daemon/hq-cloud.js +71 -25
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [5.208.0] — 2026-09-26
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- `hq lanes accounts set-home` and `login` accept `--company <slug|uid>` and
|
|
10
|
+
scope the host-account-home flag read to that company.
|
|
11
|
+
- The `hq daemon` updater now installs new hq-cloud patch releases. It looks up the newest release in the bundled major version and installs that exact version. Before, `npm install` with a version range kept an installed copy that already matched the range, so the updater stayed on 6.18.1 after 6.18.2 was published. A failed lookup is reported as a failed update and installs nothing.
|
|
12
|
+
|
|
5
13
|
## [5.207.0] — 2026-09-26
|
|
6
14
|
|
|
7
15
|
### Added
|
|
@@ -8172,6 +8172,9 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
8172
8172
|
readonly options: readonly [{
|
|
8173
8173
|
readonly flags: "--host <hostname>";
|
|
8174
8174
|
readonly description: "Host key (defaults to this host's hostname)";
|
|
8175
|
+
}, {
|
|
8176
|
+
readonly flags: "--company <slug|uid>";
|
|
8177
|
+
readonly description: "Company scope for the host-account-home flag";
|
|
8175
8178
|
}, {
|
|
8176
8179
|
readonly flags: "--json";
|
|
8177
8180
|
readonly description: "JSON on stdout (always)";
|
|
@@ -8195,6 +8198,9 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
8195
8198
|
readonly options: readonly [{
|
|
8196
8199
|
readonly flags: "--home <path>";
|
|
8197
8200
|
readonly description: "Account home directory";
|
|
8201
|
+
}, {
|
|
8202
|
+
readonly flags: "--company <slug|uid>";
|
|
8203
|
+
readonly description: "Company scope for the host-account-home flag";
|
|
8198
8204
|
}, {
|
|
8199
8205
|
readonly flags: "--device-auth";
|
|
8200
8206
|
readonly description: "Use Codex device authentication instead of localhost callback";
|
|
@@ -10571,6 +10571,10 @@ export const COMMAND_CATALOG = [
|
|
|
10571
10571
|
"flags": "--host <hostname>",
|
|
10572
10572
|
"description": "Host key (defaults to this host's hostname)"
|
|
10573
10573
|
},
|
|
10574
|
+
{
|
|
10575
|
+
"flags": "--company <slug|uid>",
|
|
10576
|
+
"description": "Company scope for the host-account-home flag"
|
|
10577
|
+
},
|
|
10574
10578
|
{
|
|
10575
10579
|
"flags": "--json",
|
|
10576
10580
|
"description": "JSON on stdout (always)"
|
|
@@ -10601,6 +10605,10 @@ export const COMMAND_CATALOG = [
|
|
|
10601
10605
|
"flags": "--home <path>",
|
|
10602
10606
|
"description": "Account home directory"
|
|
10603
10607
|
},
|
|
10608
|
+
{
|
|
10609
|
+
"flags": "--company <slug|uid>",
|
|
10610
|
+
"description": "Company scope for the host-account-home flag"
|
|
10611
|
+
},
|
|
10604
10612
|
{
|
|
10605
10613
|
"flags": "--device-auth",
|
|
10606
10614
|
"description": "Use Codex device authentication instead of localhost callback"
|
package/dist/commands/lanes.d.ts
CHANGED
|
@@ -35,6 +35,8 @@ export interface LanesCommandDeps {
|
|
|
35
35
|
now?: () => Date;
|
|
36
36
|
hostname?: () => string;
|
|
37
37
|
resolveCompany?: (company?: string) => Promise<LaneCompanyRef>;
|
|
38
|
+
/** Membership-backed explicit company selection for account-home writers. */
|
|
39
|
+
resolveActiveCompany?: (company: string) => Promise<LaneCompanyRef | null>;
|
|
38
40
|
listOutpostIds?: () => Promise<string[]>;
|
|
39
41
|
readOutpostIdentity?: () => OutpostIdentity;
|
|
40
42
|
spawnLogin?: (opts: {
|
package/dist/commands/lanes.js
CHANGED
|
@@ -30,7 +30,9 @@ import { BALANCE_MOVABLE_STATES, CLAUDE_ACCOUNT_BALANCE_FLAG, accountLoadsForRep
|
|
|
30
30
|
import { parseUserYaml } from "../utils/user-yaml-error.js";
|
|
31
31
|
import { backfillOutpostIdentityMarker as backfillOutpostIdentityMarkerImpl, createOutpostIdentityMarkerIfMissing, validateOutpostIdentityId, } from "../lib/lanes/outpost-identity.js";
|
|
32
32
|
import { vaultApiFetch } from "../utils/vault-api.js";
|
|
33
|
-
import {
|
|
33
|
+
import { resolveActiveMembershipCompany } from "../lib/mesh/api.js";
|
|
34
|
+
import { openMeshTransport, requireToken } from "../lib/mesh/client.js";
|
|
35
|
+
import { CompanySelectionError } from "../utils/company-selection-error.js";
|
|
34
36
|
import { createLaneMesh, emitLaneDecisionEvents, emitLaneTerminalStateEvent, laneMeshEventIdentity, } from "../lib/lanes/mesh-events.js";
|
|
35
37
|
import { sessionIdFromEnv } from "../lib/mesh/live/session-identity.js";
|
|
36
38
|
import { effectiveAccountHome } from "../lib/lanes/account-home.js";
|
|
@@ -88,11 +90,18 @@ function defaultLaneOutpostQuestionVisibilityEnabled(company, env) {
|
|
|
88
90
|
function defaultLaneHostScopedAccountHomesEnabled(company, env) {
|
|
89
91
|
return readLanesRegistryFlag(LANE_HOST_SCOPED_ACCOUNT_HOMES_FLAG, { company, env });
|
|
90
92
|
}
|
|
91
|
-
async function hostScopedAccountHomesEnabled(ctx) {
|
|
93
|
+
async function hostScopedAccountHomesEnabled(ctx, company) {
|
|
92
94
|
const localOverride = readLocalOverride(LANE_HOST_SCOPED_ACCOUNT_HOMES_FLAG, ctx.env);
|
|
93
95
|
if (localOverride !== null)
|
|
94
96
|
return localOverride;
|
|
95
|
-
return ctx.laneHostScopedAccountHomesEnabled(await ctx.resolveCompany());
|
|
97
|
+
return ctx.laneHostScopedAccountHomesEnabled(company ?? await ctx.resolveCompany());
|
|
98
|
+
}
|
|
99
|
+
async function resolveSelectedAccountHomeCompany(ctx, selector) {
|
|
100
|
+
const company = await ctx.resolveActiveCompany(selector);
|
|
101
|
+
if (!company) {
|
|
102
|
+
throw new CompanySelectionError(`No unique active company membership for ${selector}. Pass an active company UID with --company.`);
|
|
103
|
+
}
|
|
104
|
+
return company;
|
|
96
105
|
}
|
|
97
106
|
function withoutProviderHome(value) {
|
|
98
107
|
if (Array.isArray(value))
|
|
@@ -158,6 +167,12 @@ async function defaultResolveCompany(company) {
|
|
|
158
167
|
const { company: mesh } = await openMeshTransport({ company });
|
|
159
168
|
return { slug: mesh.companySlug, uid: mesh.companyUid };
|
|
160
169
|
}
|
|
170
|
+
async function defaultResolveActiveCompany(company) {
|
|
171
|
+
const membership = await resolveActiveMembershipCompany(await requireToken(), company);
|
|
172
|
+
return membership
|
|
173
|
+
? { slug: membership.companySlug, uid: membership.companyUid }
|
|
174
|
+
: null;
|
|
175
|
+
}
|
|
161
176
|
function defaultOutpostsClient() {
|
|
162
177
|
return createOutpostsClient({
|
|
163
178
|
transport: vaultApiFetch,
|
|
@@ -243,6 +258,7 @@ function resolveDeps(deps = {}) {
|
|
|
243
258
|
now: deps.now ?? (() => new Date()),
|
|
244
259
|
hostname: deps.hostname ?? (() => os.hostname()),
|
|
245
260
|
resolveCompany: deps.resolveCompany ?? defaultResolveCompany,
|
|
261
|
+
resolveActiveCompany: deps.resolveActiveCompany ?? defaultResolveActiveCompany,
|
|
246
262
|
listOutpostIds: deps.listOutpostIds ?? defaultListOutpostIds,
|
|
247
263
|
readOutpostIdentity: deps.readOutpostIdentity ?? (() => readOutpostIdentity()),
|
|
248
264
|
spawnLogin: deps.spawnLogin ?? defaultSpawnLogin,
|
|
@@ -7922,8 +7938,11 @@ async function runAccountsLogin(ctx, providerRaw, accountId, opts) {
|
|
|
7922
7938
|
if (opts.home !== undefined && !requestedHome) {
|
|
7923
7939
|
throw new LanesError("invalid_account_home", "--home must be a non-empty path", { provider, account: id });
|
|
7924
7940
|
}
|
|
7941
|
+
const company = opts.company !== undefined
|
|
7942
|
+
? await resolveSelectedAccountHomeCompany(ctx, opts.company)
|
|
7943
|
+
: undefined;
|
|
7925
7944
|
const hostScopedWriterEnabled = registeredAccount !== undefined && requestedHome !== undefined
|
|
7926
|
-
? await hostScopedAccountHomesEnabled(ctx)
|
|
7945
|
+
? await hostScopedAccountHomesEnabled(ctx, company)
|
|
7927
7946
|
: false;
|
|
7928
7947
|
const homeRaw = requestedHome ??
|
|
7929
7948
|
(registeredAccount !== undefined
|
|
@@ -8017,7 +8036,10 @@ async function runAccountsLogin(ctx, providerRaw, accountId, opts) {
|
|
|
8017
8036
|
printJson({ ok: true, action: "accounts.login", account: status });
|
|
8018
8037
|
}
|
|
8019
8038
|
async function runAccountsSetHome(ctx, providerRaw, accountIdRaw, homeRaw, opts) {
|
|
8020
|
-
|
|
8039
|
+
const company = opts.company !== undefined
|
|
8040
|
+
? await resolveSelectedAccountHomeCompany(ctx, opts.company)
|
|
8041
|
+
: undefined;
|
|
8042
|
+
if (!(await hostScopedAccountHomesEnabled(ctx, company))) {
|
|
8021
8043
|
throw new LanesError("host_scoped_account_homes_disabled", "Setting a host-specific account home requires the lanes.host-scoped-account-homes flag", { flag: LANE_HOST_SCOPED_ACCOUNT_HOMES_FLAG });
|
|
8022
8044
|
}
|
|
8023
8045
|
const callerCwd = ctx.callerCwd();
|
|
@@ -8689,6 +8711,7 @@ export function registerLanesCommand(parent, deps = {}) {
|
|
|
8689
8711
|
.argument("<account-id>", "Existing account id")
|
|
8690
8712
|
.argument("<path>", "Home directory for this host")
|
|
8691
8713
|
.option("--host <hostname>", "Host key (defaults to this host's hostname)")
|
|
8714
|
+
.option("--company <slug|uid>", "Company scope for the host-account-home flag")
|
|
8692
8715
|
.option("--json", "JSON on stdout (always)")
|
|
8693
8716
|
.action(wrap(async (provider, accountId, home, opts) => {
|
|
8694
8717
|
await runAccountsSetHome(ctx, provider, accountId, home, opts);
|
|
@@ -8699,6 +8722,7 @@ export function registerLanesCommand(parent, deps = {}) {
|
|
|
8699
8722
|
.argument("<provider>", "claude | codex | grok")
|
|
8700
8723
|
.argument("<account-id>", "Open-ended account id")
|
|
8701
8724
|
.option("--home <path>", "Account home directory")
|
|
8725
|
+
.option("--company <slug|uid>", "Company scope for the host-account-home flag")
|
|
8702
8726
|
.option("--device-auth", "Use Codex device authentication instead of localhost callback")
|
|
8703
8727
|
.action(wrap(async (provider, accountId, opts) => {
|
|
8704
8728
|
await runAccountsLogin(ctx, provider, accountId, opts);
|
|
@@ -31,8 +31,21 @@ export interface HqCloudRunners {
|
|
|
31
31
|
sessionHost: string | null;
|
|
32
32
|
}
|
|
33
33
|
export declare function hqCloudRunners(root: string, exists?: (p: string) => boolean): HqCloudRunners;
|
|
34
|
-
/**
|
|
35
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Ask npm for every release of the bundled major version, no older than the
|
|
36
|
+
* bundled copy. The updater installs the newest one exactly: `npm install` with
|
|
37
|
+
* a range keeps an installed copy that already satisfies it, so a range alone
|
|
38
|
+
* never moves 6.18.1 to 6.18.2.
|
|
39
|
+
*/
|
|
40
|
+
export declare function hqCloudLatestCommand(bundledVersion: string, platform?: NodeJS.Platform): {
|
|
41
|
+
file: string;
|
|
42
|
+
args: string[];
|
|
43
|
+
shell: boolean;
|
|
44
|
+
};
|
|
45
|
+
/** The newest release in `npm view --json` output with the bundled major version, never older than it. */
|
|
46
|
+
export declare function latestSameMajor(npmViewJson: string, bundledVersion: string): string | null;
|
|
47
|
+
/** Install one exact hq-cloud version into the updater's folder. */
|
|
48
|
+
export declare function hqCloudInstallCommand(prefix: string, version: string, platform?: NodeJS.Platform): {
|
|
36
49
|
file: string;
|
|
37
50
|
args: string[];
|
|
38
51
|
shell: boolean;
|
|
@@ -48,6 +61,17 @@ export interface HqCloudUpdateOutcome {
|
|
|
48
61
|
to: string | null;
|
|
49
62
|
detail?: string;
|
|
50
63
|
}
|
|
64
|
+
export interface NpmRunResult {
|
|
65
|
+
status: number | null;
|
|
66
|
+
stdout?: string | null;
|
|
67
|
+
stderr?: string | null;
|
|
68
|
+
error?: Error;
|
|
69
|
+
}
|
|
70
|
+
export interface UpdateHqCloudDeps {
|
|
71
|
+
run?: (file: string, args: string[], shell: boolean, env: NodeJS.ProcessEnv) => NpmRunResult;
|
|
72
|
+
bundled?: HqCloudCopy | null;
|
|
73
|
+
platform?: NodeJS.Platform;
|
|
74
|
+
}
|
|
51
75
|
/** Install or update the updater's copy, as this (the login) user. */
|
|
52
|
-
export declare function updateManagedHqCloud(daemonRoot: string, env: NodeJS.ProcessEnv): HqCloudUpdateOutcome;
|
|
76
|
+
export declare function updateManagedHqCloud(daemonRoot: string, env: NodeJS.ProcessEnv, deps?: UpdateHqCloudDeps): HqCloudUpdateOutcome;
|
|
53
77
|
//# sourceMappingURL=hq-cloud.d.ts.map
|
|
@@ -61,18 +61,45 @@ export function hqCloudRunners(root, exists = fs.existsSync) {
|
|
|
61
61
|
sessionHost: bin("outpost-session-host.js"),
|
|
62
62
|
};
|
|
63
63
|
}
|
|
64
|
-
/**
|
|
65
|
-
|
|
64
|
+
/**
|
|
65
|
+
* Ask npm for every release of the bundled major version, no older than the
|
|
66
|
+
* bundled copy. The updater installs the newest one exactly: `npm install` with
|
|
67
|
+
* a range keeps an installed copy that already satisfies it, so a range alone
|
|
68
|
+
* never moves 6.18.1 to 6.18.2.
|
|
69
|
+
*/
|
|
70
|
+
export function hqCloudLatestCommand(bundledVersion, platform = process.platform) {
|
|
71
|
+
const plan = buildSpawnPlan("npm", ["view", `${HQ_CLOUD_PACKAGE}@^${bundledVersion}`, "version", "--json"], platform);
|
|
72
|
+
return { file: plan.cmd, args: plan.args, shell: plan.shell };
|
|
73
|
+
}
|
|
74
|
+
/** The newest release in `npm view --json` output with the bundled major version, never older than it. */
|
|
75
|
+
export function latestSameMajor(npmViewJson, bundledVersion) {
|
|
76
|
+
const b = parseVersion(bundledVersion);
|
|
77
|
+
if (!b)
|
|
78
|
+
return null;
|
|
79
|
+
let parsed;
|
|
80
|
+
try {
|
|
81
|
+
parsed = JSON.parse(npmViewJson);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
const versions = typeof parsed === "string" ? [parsed] : Array.isArray(parsed) ? parsed : [];
|
|
87
|
+
let best = null;
|
|
88
|
+
for (const text of versions) {
|
|
89
|
+
if (typeof text !== "string" || !/^\d+\.\d+\.\d+$/.test(text))
|
|
90
|
+
continue;
|
|
91
|
+
const v = parseVersion(text);
|
|
92
|
+
if (v[0] !== b[0] || newer(b, v))
|
|
93
|
+
continue;
|
|
94
|
+
if (!best || newer(v, best.v))
|
|
95
|
+
best = { text, v };
|
|
96
|
+
}
|
|
97
|
+
return best?.text ?? null;
|
|
98
|
+
}
|
|
99
|
+
/** Install one exact hq-cloud version into the updater's folder. */
|
|
100
|
+
export function hqCloudInstallCommand(prefix, version, platform = process.platform) {
|
|
66
101
|
// npm is a .cmd on Windows and needs the shell, so every argument is quoted for it.
|
|
67
|
-
const plan = buildSpawnPlan("npm", [
|
|
68
|
-
"install",
|
|
69
|
-
"--prefix",
|
|
70
|
-
prefix,
|
|
71
|
-
"--no-audit",
|
|
72
|
-
"--no-fund",
|
|
73
|
-
"--omit=dev",
|
|
74
|
-
`${HQ_CLOUD_PACKAGE}@${pinnedVersion ?? `^${bundledVersion}`}`,
|
|
75
|
-
], platform);
|
|
102
|
+
const plan = buildSpawnPlan("npm", ["install", "--prefix", prefix, "--no-audit", "--no-fund", "--omit=dev", `${HQ_CLOUD_PACKAGE}@${version}`], platform);
|
|
76
103
|
return { file: plan.cmd, args: plan.args, shell: plan.shell };
|
|
77
104
|
}
|
|
78
105
|
export function readHqCloudCopy(root) {
|
|
@@ -99,10 +126,26 @@ export function currentHqCloud(daemonRoot) {
|
|
|
99
126
|
const pinnedVersion = readDaemonConfig(path.join(daemonRoot, "config.json")).hqCloudVersion;
|
|
100
127
|
return pickHqCloud(bundledHqCloud(), readHqCloudCopy(managedHqCloudRoot(daemonRoot)), (copy) => hqCloudRunners(copy.root).syncRunner !== null, pinnedVersion);
|
|
101
128
|
}
|
|
129
|
+
function runNpm(file, args, shell, env) {
|
|
130
|
+
return spawnSync(file, args, {
|
|
131
|
+
encoding: "utf8",
|
|
132
|
+
env,
|
|
133
|
+
timeout: 10 * 60_000,
|
|
134
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
135
|
+
windowsHide: true,
|
|
136
|
+
shell,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
function npmFailure(r) {
|
|
140
|
+
const tail = `${r.stderr ?? ""}\n${r.stdout ?? ""}`.trim().split("\n").slice(-3).join(" ").slice(0, 400);
|
|
141
|
+
return tail || (r.error ? r.error.message : `npm exited ${r.status ?? "by signal"}`);
|
|
142
|
+
}
|
|
102
143
|
/** Install or update the updater's copy, as this (the login) user. */
|
|
103
|
-
export function updateManagedHqCloud(daemonRoot, env) {
|
|
144
|
+
export function updateManagedHqCloud(daemonRoot, env, deps = {}) {
|
|
145
|
+
const run = deps.run ?? runNpm;
|
|
146
|
+
const platform = deps.platform ?? process.platform;
|
|
104
147
|
const pinnedVersion = readDaemonConfig(path.join(daemonRoot, "config.json")).hqCloudVersion;
|
|
105
|
-
const bundled = bundledHqCloud();
|
|
148
|
+
const bundled = deps.bundled !== undefined ? deps.bundled : bundledHqCloud();
|
|
106
149
|
const from = currentHqCloud(daemonRoot)?.version ?? null;
|
|
107
150
|
if (!bundled)
|
|
108
151
|
return { ok: false, from, to: null, detail: "this CLI install has no bundled hq-cloud to match" };
|
|
@@ -113,19 +156,22 @@ export function updateManagedHqCloud(daemonRoot, env) {
|
|
|
113
156
|
catch (err) {
|
|
114
157
|
return { ok: false, from, to: null, detail: `could not create ${prefix}: ${err instanceof Error ? err.message : String(err)}` };
|
|
115
158
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
env
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
return { ok: false, from, to: null, detail: tail || (r.error ? r.error.message : `npm exited ${r.status ?? "by signal"}`) };
|
|
159
|
+
let version = pinnedVersion;
|
|
160
|
+
if (!version) {
|
|
161
|
+
const view = hqCloudLatestCommand(bundled.version, platform);
|
|
162
|
+
const r = run(view.file, view.args, view.shell, env);
|
|
163
|
+
if (r.status !== 0)
|
|
164
|
+
return { ok: false, from, to: null, detail: `could not look up the newest hq-cloud: ${npmFailure(r)}` };
|
|
165
|
+
const latest = latestSameMajor(r.stdout ?? "", bundled.version);
|
|
166
|
+
if (!latest) {
|
|
167
|
+
return { ok: false, from, to: null, detail: `npm listed no hq-cloud release at or above ${bundled.version} in the same major version` };
|
|
168
|
+
}
|
|
169
|
+
version = latest;
|
|
128
170
|
}
|
|
171
|
+
const cmd = hqCloudInstallCommand(prefix, version, platform);
|
|
172
|
+
const r = run(cmd.file, cmd.args, cmd.shell, env);
|
|
173
|
+
if (r.status !== 0)
|
|
174
|
+
return { ok: false, from, to: null, detail: npmFailure(r) };
|
|
129
175
|
return { ok: true, from, to: currentHqCloud(daemonRoot)?.version ?? null };
|
|
130
176
|
}
|
|
131
177
|
//# sourceMappingURL=hq-cloud.js.map
|