@indigoai-us/hq-cli 5.345.14 → 5.345.16
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 +6 -0
- package/dist/commands/core-sentry-report-fast.d.ts +10 -1
- package/dist/commands/core-sentry-report-fast.js +28 -6
- package/dist/commands/install-global.d.ts +5 -1
- package/dist/commands/install-global.js +12 -6
- package/dist/commands/lanes-message.js +6 -3
- package/dist/commands/lanes-resume.js +4 -2
- package/dist/commands/lanes.js +20 -23
- package/dist/commands/pkg-install.js +23 -1
- package/dist/fast-scaffold.js +2 -5
- package/dist/lib/lanes/claude-balance.d.ts +0 -2
- package/dist/lib/lanes/claude-balance.js +1 -6
- package/dist/main.js +2 -6
- package/dist/sentry-init.d.ts +10 -0
- package/dist/sentry-init.js +48 -0
- package/dist/sentry.js +5 -42
- package/dist/utils/command-breadcrumb.d.ts +7 -0
- package/dist/utils/command-breadcrumb.js +9 -0
- package/dist/utils/feedback-diagnostics.d.ts +1 -2
- package/dist/utils/feedback-diagnostics.js +2 -29
- package/dist/utils/sanitize-argv.d.ts +2 -0
- package/dist/utils/sanitize-argv.js +30 -0
- package/dist/utils/scaffold-fast-import-guard.d.ts +1 -1
- package/dist/utils/scaffold-fast-import-guard.js +7 -4
- package/docs/anywhere-runtime.md +8 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [5.345.16] — 2026-10-06
|
|
6
|
+
- Route live hook Sentry reports through the fast CLI path.
|
|
7
|
+
|
|
8
|
+
- Claude account model settings are defaults, so explicit lane models remain eligible; explicit account selections on create, resume, and message are recorded as CLI choices.
|
|
9
|
+
- When `hq-anywhere-runtime` is enabled, `hq install --global --runtime claude --via plugin` installs the `hq` plugin from the `indigoai-us/hq-packages` Claude marketplace, and `hq install @indigoai-us/hq-anywhere` installs the Codex pack from that repository. The flag-off path refuses the HQ Anywhere alias without fetching a package source.
|
|
10
|
+
|
|
5
11
|
## [5.345.14] — 2026-10-06
|
|
6
12
|
- Route frequently called native core commands through isolated fast handlers.
|
|
7
13
|
- Allow lane hierarchies up to four levels and include actionable senior-chain details when depth limits refuse a create or re-parent.
|
|
@@ -1,2 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
import type { NodeOptions } from "@sentry/node";
|
|
2
|
+
import { reportSentryEvent } from "../lib/core-utils/sentry-report.js";
|
|
3
|
+
export type FastSentryReportDependencies = {
|
|
4
|
+
initSentry: (overrides?: Partial<Pick<NodeOptions, "transport">>) => void;
|
|
5
|
+
finishSentrySession: () => void;
|
|
6
|
+
flushSentry: (timeoutMs: number) => PromiseLike<boolean>;
|
|
7
|
+
addCommandBreadcrumb: () => void;
|
|
8
|
+
report: typeof reportSentryEvent;
|
|
9
|
+
};
|
|
10
|
+
export declare function runSentryReportFast(args: readonly string[], stdin: string, _cwd: string, hqRoot: string | undefined, _commandName?: string, _hqRootArg?: string, _worktreeScopeFixEnabled?: boolean, dependencies?: FastSentryReportDependencies): Promise<number | undefined>;
|
|
2
11
|
//# sourceMappingURL=core-sentry-report-fast.d.ts.map
|
|
@@ -1,5 +1,17 @@
|
|
|
1
|
+
import { settleWithin } from "../utils/settle-with-timeout.js";
|
|
2
|
+
import { beforeSend } from "../sentry-before-send.js";
|
|
3
|
+
import { addSentryRuntimeBreadcrumb, flushSentryRuntime, finishSentryRuntimeSession, initSentryRuntime, } from "../sentry-init.js";
|
|
4
|
+
import { addCommandBreadcrumb } from "../utils/command-breadcrumb.js";
|
|
1
5
|
import { reportSentryEvent } from "../lib/core-utils/sentry-report.js";
|
|
2
|
-
|
|
6
|
+
const SENTRY_FINALIZATION_TIMEOUT_MS = 500;
|
|
7
|
+
const defaultDependencies = {
|
|
8
|
+
initSentry: (overrides) => { initSentryRuntime(beforeSend, overrides); },
|
|
9
|
+
finishSentrySession: finishSentryRuntimeSession,
|
|
10
|
+
flushSentry: flushSentryRuntime,
|
|
11
|
+
addCommandBreadcrumb: () => addCommandBreadcrumb(process.argv.slice(2), addSentryRuntimeBreadcrumb),
|
|
12
|
+
report: reportSentryEvent,
|
|
13
|
+
};
|
|
14
|
+
export function runSentryReportFast(args, stdin, _cwd, hqRoot, _commandName, _hqRootArg, _worktreeScopeFixEnabled, dependencies = defaultDependencies) {
|
|
3
15
|
let dryRun = false;
|
|
4
16
|
let timeoutMs;
|
|
5
17
|
for (let index = 0; index < args.length; index += 1) {
|
|
@@ -24,10 +36,20 @@ export function runSentryReportFast(args, stdin, _cwd, hqRoot) {
|
|
|
24
36
|
// lightweight parser does not own. This branch emits no output.
|
|
25
37
|
return Promise.resolve(undefined);
|
|
26
38
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
39
|
+
if (dryRun) {
|
|
40
|
+
return dependencies.report(stdin, { dryRun: true, timeoutMs, hqRoot }).then((result) => result.code);
|
|
41
|
+
}
|
|
42
|
+
// The report command's constrained message path uses the same DSN, release,
|
|
43
|
+
// environment, repo tag, user attribution, breadcrumb hook and release-health
|
|
44
|
+
// session as main.ts. The shared initializer keeps that setup in one place;
|
|
45
|
+
// the report payload itself is a message without an exception, so the shared
|
|
46
|
+
// fleet scrubber is the effective beforeSend behavior on this path.
|
|
47
|
+
dependencies.initSentry();
|
|
48
|
+
dependencies.addCommandBreadcrumb();
|
|
49
|
+
return dependencies.report(stdin, { dryRun: false, timeoutMs, hqRoot }).then(async (result) => {
|
|
50
|
+
dependencies.finishSentrySession();
|
|
51
|
+
await settleWithin([Promise.resolve(dependencies.flushSentry(SENTRY_FINALIZATION_TIMEOUT_MS))], SENTRY_FINALIZATION_TIMEOUT_MS);
|
|
52
|
+
return result.code;
|
|
53
|
+
});
|
|
32
54
|
}
|
|
33
55
|
//# sourceMappingURL=core-sentry-report-fast.js.map
|
|
@@ -21,6 +21,8 @@ export interface GlobalInstallOptions {
|
|
|
21
21
|
via?: 'direct' | 'plugin';
|
|
22
22
|
/** Local Claude Code marketplace directory (or HQ_CLAUDE_PLUGIN_MARKETPLACE). */
|
|
23
23
|
marketplaceDir?: string;
|
|
24
|
+
/** GitHub repository for the published HQ plugin marketplace. */
|
|
25
|
+
marketplaceRepository?: string;
|
|
24
26
|
/** Test seam for the Claude Code CLI; production uses `claude`. */
|
|
25
27
|
runClaude?: (args: string[], home: string, env: NodeJS.ProcessEnv) => void;
|
|
26
28
|
}
|
|
@@ -38,7 +40,7 @@ export interface ClaudePluginSource {
|
|
|
38
40
|
pluginId: string;
|
|
39
41
|
}
|
|
40
42
|
/** Resolve the local marketplace source; published hosting remains an operator decision. */
|
|
41
|
-
export declare function resolveClaudePluginSource(explicitDir?: string, env?: NodeJS.ProcessEnv): ClaudePluginSource;
|
|
43
|
+
export declare function resolveClaudePluginSource(explicitDir?: string, env?: NodeJS.ProcessEnv, repository?: string): ClaudePluginSource;
|
|
42
44
|
export { AnywhereRuntimeDisabledError as AnywhereDisabledError };
|
|
43
45
|
export declare const isAnywhereEnabled: (_home: string, env?: NodeJS.ProcessEnv) => boolean;
|
|
44
46
|
/** Install HQ into Claude Code user scope. Returns the files touched (or that would be). */
|
|
@@ -67,10 +69,12 @@ export interface GlobalCommandOpts {
|
|
|
67
69
|
hqRoot?: string;
|
|
68
70
|
via?: 'direct' | 'plugin';
|
|
69
71
|
marketplaceDir?: string;
|
|
72
|
+
marketplaceRepository?: string;
|
|
70
73
|
/** Dependency injection for tests. */
|
|
71
74
|
env?: NodeJS.ProcessEnv;
|
|
72
75
|
log?: (line: string) => void;
|
|
73
76
|
}
|
|
77
|
+
export declare function resolveInstallRoot(explicit: string | undefined): string;
|
|
74
78
|
/** Entry used by `hq install --global` (wired from pkg-install.ts). */
|
|
75
79
|
export declare function runInstallGlobal(opts: GlobalCommandOpts): void;
|
|
76
80
|
export declare function registerUninstallCommand(program: Command): void;
|
|
@@ -46,9 +46,12 @@ export function hookTargetFor(hqRoot, home, daemonInstalled, env = process.env)
|
|
|
46
46
|
return installed && daemonMatchesRoot && fs.existsSync(shimPath(hqRoot)) ? 'shim' : 'master';
|
|
47
47
|
}
|
|
48
48
|
/** Resolve the local marketplace source; published hosting remains an operator decision. */
|
|
49
|
-
export function resolveClaudePluginSource(explicitDir, env = process.env) {
|
|
49
|
+
export function resolveClaudePluginSource(explicitDir, env = process.env, repository) {
|
|
50
50
|
const configured = explicitDir?.trim() || env.HQ_CLAUDE_PLUGIN_MARKETPLACE?.trim();
|
|
51
51
|
if (!configured) {
|
|
52
|
+
if (repository === 'indigoai-us/hq-packages') {
|
|
53
|
+
return { sourceDir: repository, marketplaceName: 'hq-packages', pluginName: 'hq', pluginId: 'hq@hq-packages' };
|
|
54
|
+
}
|
|
52
55
|
throw Object.assign(new Error('Published HQ Claude plugin source is not configured; its repository/host awaits an operator decision. Pass --marketplace-dir <dir> or set HQ_CLAUDE_PLUGIN_MARKETPLACE.'), { expected: true });
|
|
53
56
|
}
|
|
54
57
|
const sourceDir = path.resolve(configured);
|
|
@@ -94,7 +97,9 @@ function removePluginSettings(doc, plugin, removePlugin, removeMarketplace, plug
|
|
|
94
97
|
if (marketplaces && typeof marketplaces === 'object' && !Array.isArray(marketplaces)) {
|
|
95
98
|
const entries = { ...marketplaces };
|
|
96
99
|
const registration = entries[plugin.marketplaceName];
|
|
97
|
-
|
|
100
|
+
const sameDirectory = registration?.source?.source === 'directory' && registration.source.path === plugin.sourceDir;
|
|
101
|
+
const sameRepository = registration?.source?.source === 'github' && registration.source.repo === plugin.sourceDir;
|
|
102
|
+
if (removeMarketplace && (sameDirectory || sameRepository))
|
|
98
103
|
delete entries[plugin.marketplaceName];
|
|
99
104
|
if (Object.keys(entries).length === 0)
|
|
100
105
|
delete next.extraKnownMarketplaces;
|
|
@@ -278,7 +283,7 @@ export function installGlobalClaude(opts) {
|
|
|
278
283
|
throw new AnywhereRuntimeDisabledError();
|
|
279
284
|
const t = claudeTargets(home);
|
|
280
285
|
const prior = readState(t);
|
|
281
|
-
const pluginSource = opts.via === 'plugin' ? resolveClaudePluginSource(opts.marketplaceDir, env) : undefined;
|
|
286
|
+
const pluginSource = opts.via === 'plugin' ? resolveClaudePluginSource(opts.marketplaceDir, env, opts.marketplaceRepository) : undefined;
|
|
282
287
|
if (prior?.plugin && !pluginSource) {
|
|
283
288
|
throw Object.assign(new Error('HQ is installed through a Claude plugin marketplace. Run `hq uninstall --global --runtime claude` before switching back to direct installation.'), { expected: true });
|
|
284
289
|
}
|
|
@@ -312,7 +317,8 @@ export function installGlobalClaude(opts) {
|
|
|
312
317
|
const enabledPlugins = settingsDoc.enabledPlugins;
|
|
313
318
|
const knownMarketplaces = settingsDoc.extraKnownMarketplaces;
|
|
314
319
|
const existingMarketplace = pluginSource ? knownMarketplaces?.[pluginSource.marketplaceName] : undefined;
|
|
315
|
-
if (pluginSource && existingMarketplace && (existingMarketplace.source?.source
|
|
320
|
+
if (pluginSource && existingMarketplace && !((existingMarketplace.source?.source === 'directory' && existingMarketplace.source.path === pluginSource.sourceDir) ||
|
|
321
|
+
(existingMarketplace.source?.source === 'github' && existingMarketplace.source.repo === pluginSource.sourceDir))) {
|
|
316
322
|
throw Object.assign(new Error(`Claude marketplace "${pluginSource.marketplaceName}" is already registered from a different source. Remove or rename that marketplace before installing HQ.`), { expected: true });
|
|
317
323
|
}
|
|
318
324
|
const hadPluginSetting = !!pluginSource && !!enabledPlugins && Object.prototype.hasOwnProperty.call(enabledPlugins, pluginSource.pluginId);
|
|
@@ -927,7 +933,7 @@ function assertRuntime(runtime) {
|
|
|
927
933
|
}
|
|
928
934
|
return r;
|
|
929
935
|
}
|
|
930
|
-
function resolveInstallRoot(explicit) {
|
|
936
|
+
export function resolveInstallRoot(explicit) {
|
|
931
937
|
const fromFlag = explicit?.trim() || process.env.HQ_ROOT?.trim();
|
|
932
938
|
const root = fromFlag ? path.resolve(fromFlag) : resolveHqRoot();
|
|
933
939
|
if (!root) {
|
|
@@ -948,7 +954,7 @@ export function runInstallGlobal(opts) {
|
|
|
948
954
|
return;
|
|
949
955
|
}
|
|
950
956
|
const install = runtime === 'codex' ? installGlobalCodex : installGlobalClaude;
|
|
951
|
-
install({ hqRoot: resolveInstallRoot(opts.hqRoot), dryRun: opts.dryRun, via: opts.via, marketplaceDir: opts.marketplaceDir });
|
|
957
|
+
install({ hqRoot: resolveInstallRoot(opts.hqRoot), dryRun: opts.dryRun, via: opts.via, marketplaceDir: opts.marketplaceDir, marketplaceRepository: opts.marketplaceRepository });
|
|
952
958
|
}
|
|
953
959
|
export function registerUninstallCommand(program) {
|
|
954
960
|
program
|
|
@@ -518,7 +518,7 @@ export function createLaneMessageHandlers(deps) {
|
|
|
518
518
|
automaticAccountSelection = undefined;
|
|
519
519
|
}
|
|
520
520
|
}
|
|
521
|
-
const profileRefreshAccount = refreshProfileSettings && resumeProfile !== undefined &&
|
|
521
|
+
const profileRefreshAccount = opts.account === undefined && refreshProfileSettings && resumeProfile !== undefined &&
|
|
522
522
|
lane.account_source !== undefined && lane.account_source !== "cli" &&
|
|
523
523
|
resumeProfile.profile.meta.provider === lane.provider
|
|
524
524
|
? resumeProfile.profile.meta.account ?? (resumeProfile.profile.meta.accounts !== undefined &&
|
|
@@ -665,9 +665,10 @@ export function createLaneMessageHandlers(deps) {
|
|
|
665
665
|
// lane (the dropbox mechanism) is refused inside prepareAccountMove.
|
|
666
666
|
move = await prepareAccountMove(ctx, lane, {
|
|
667
667
|
account: opts.account,
|
|
668
|
+
accountSource: "cli",
|
|
668
669
|
cliModel: opts.model?.trim() || undefined,
|
|
669
670
|
...(resumeProfile !== undefined ? { workerProfile: resumeProfile } : {}),
|
|
670
|
-
...(refreshProfileSettings ? { refreshProfileSettings: true
|
|
671
|
+
...(refreshProfileSettings ? { refreshProfileSettings: true } : {}),
|
|
671
672
|
...(resumeEffort !== undefined ? { effort: resumeEffort } : {}),
|
|
672
673
|
...(hireGrantContext !== undefined ? { grantContext: hireGrantContext } : {}),
|
|
673
674
|
pending: {
|
|
@@ -694,6 +695,7 @@ export function createLaneMessageHandlers(deps) {
|
|
|
694
695
|
account: selection.account,
|
|
695
696
|
cliModel: undefined,
|
|
696
697
|
...(automatic.historyReason !== undefined ? { accountHistoryReason: automatic.historyReason } : {}),
|
|
698
|
+
...(automatic.preservedModel !== undefined ? { preservedModel: automatic.preservedModel } : {}),
|
|
697
699
|
...(resumeProfile !== undefined ? { workerProfile: resumeProfile } : {}),
|
|
698
700
|
...(refreshProfileSettings ? { refreshProfileSettings: true, accountSource: "balanced" } : {}),
|
|
699
701
|
...(resumeEffort !== undefined ? { effort: resumeEffort } : {}),
|
|
@@ -746,8 +748,9 @@ export function createLaneMessageHandlers(deps) {
|
|
|
746
748
|
if (resumeProfile !== undefined && !refreshProfileSettings && (opts.account === undefined || keepCurrentAccount)) {
|
|
747
749
|
validateResumeProfileModel(ctx, lane, resumeProfile.profile);
|
|
748
750
|
}
|
|
749
|
-
if (move === null && (resumeProfile !== undefined || resumeEffort !== undefined ||
|
|
751
|
+
if (move === null && (resumeProfile !== undefined || resumeEffort !== undefined || keepCurrentAccount)) {
|
|
750
752
|
lane = recordResumeSetup(ctx, lane, {
|
|
753
|
+
...(keepCurrentAccount ? { accountSource: "cli" } : {}),
|
|
751
754
|
...(resumeProfile !== undefined ? { profile: resumeProfile } : {}),
|
|
752
755
|
...(refreshProfileSettings ? { refreshProfileSettings: true } : {}),
|
|
753
756
|
...(resumeEffort !== undefined ? { effort: resumeEffort } : {}),
|
|
@@ -279,7 +279,7 @@ export function createLaneResumeHandlers(deps) {
|
|
|
279
279
|
...(refreshProfileSettings ? { refreshProfileSettings: true } : {}),
|
|
280
280
|
})
|
|
281
281
|
: undefined;
|
|
282
|
-
const profileRefreshAccount = refreshProfileSettings && resumeProfile !== undefined &&
|
|
282
|
+
const profileRefreshAccount = opts.account === undefined && refreshProfileSettings && resumeProfile !== undefined &&
|
|
283
283
|
lane.account_source !== undefined && lane.account_source !== "cli" &&
|
|
284
284
|
resumeProfile.profile.meta.provider === lane.provider
|
|
285
285
|
? resumeProfile.profile.meta.account ?? (resumeProfile.profile.meta.accounts !== undefined &&
|
|
@@ -346,9 +346,10 @@ export function createLaneResumeHandlers(deps) {
|
|
|
346
346
|
if (opts.account !== undefined && !keepCurrentAccount) {
|
|
347
347
|
move = await prepareAccountMove(ctx, lane, {
|
|
348
348
|
account: opts.account,
|
|
349
|
+
accountSource: "cli",
|
|
349
350
|
cliModel: opts.model?.trim() || undefined,
|
|
350
351
|
...(resumeProfile !== undefined ? { workerProfile: resumeProfile } : {}),
|
|
351
|
-
...(refreshProfileSettings ? { refreshProfileSettings: true
|
|
352
|
+
...(refreshProfileSettings ? { refreshProfileSettings: true } : {}),
|
|
352
353
|
...(profileRepin !== undefined ? { profileRepin } : {}),
|
|
353
354
|
...(resumeEffort !== undefined ? { effort: resumeEffort } : {}),
|
|
354
355
|
...(hireGrantContext !== undefined ? { grantContext: hireGrantContext } : {}),
|
|
@@ -432,6 +433,7 @@ export function createLaneResumeHandlers(deps) {
|
|
|
432
433
|
...(move === null && profileRepin !== undefined ? { profileRepin } : {}),
|
|
433
434
|
...(move === null && resumeEffort !== undefined ? { effort: resumeEffort } : {}),
|
|
434
435
|
...(keepAlive !== undefined ? { keepAlive } : {}),
|
|
436
|
+
...(move === null && keepCurrentAccount ? { accountSource: "cli" } : {}),
|
|
435
437
|
...(move === null && keepCurrentAccount && opts.model !== undefined ? { cliModel: opts.model } : {}),
|
|
436
438
|
...(requestedMeshCompany !== undefined
|
|
437
439
|
? { meshCompany: requestedMeshCompany, threadId: meshThreadId }
|
package/dist/commands/lanes.js
CHANGED
|
@@ -6488,6 +6488,7 @@ function recordResumeSetup(ctx, lane, input) {
|
|
|
6488
6488
|
input.effort === undefined &&
|
|
6489
6489
|
input.keepAlive === undefined &&
|
|
6490
6490
|
input.cliModel === undefined &&
|
|
6491
|
+
input.accountSource === undefined &&
|
|
6491
6492
|
input.meshCompany === undefined &&
|
|
6492
6493
|
input.clearWorkerProfileChangedQueue !== true &&
|
|
6493
6494
|
input.refreshProfileSettings !== true &&
|
|
@@ -6516,7 +6517,11 @@ function recordResumeSetup(ctx, lane, input) {
|
|
|
6516
6517
|
// recorded, so keep-alive may continue it again after this turn.
|
|
6517
6518
|
if (next.keep_alive_blocked_reason === "terminal_state")
|
|
6518
6519
|
next.keep_alive_blocked_reason = undefined;
|
|
6520
|
+
if (input.accountSource !== undefined)
|
|
6521
|
+
next.account_source = input.accountSource;
|
|
6519
6522
|
applyResumeProfileAndEffort(current, next, { ...input, at: ctx.now().toISOString() });
|
|
6523
|
+
if (input.accountSource !== undefined)
|
|
6524
|
+
next.account_source = input.accountSource;
|
|
6520
6525
|
if (input.clearWorkerProfileChangedQueue === true) {
|
|
6521
6526
|
next.queued_reason = undefined;
|
|
6522
6527
|
next.queued_details = undefined;
|
|
@@ -6628,19 +6633,14 @@ async function prepareAccountMove(ctx, lane, input) {
|
|
|
6628
6633
|
}
|
|
6629
6634
|
// The model the moved run would use, resolved the way spawn resolves it:
|
|
6630
6635
|
// the operator's --model, else the lane's own, else the target account's
|
|
6631
|
-
// default, else the provider's. The
|
|
6632
|
-
//
|
|
6633
|
-
// here, before anything is copied. A target that pins a different model
|
|
6634
|
-
// refuses the lane's model unless the operator names one for the move.
|
|
6636
|
+
// default, else the provider's. The target account model is a default, not
|
|
6637
|
+
// a restriction; quota and provider validation use this effective model.
|
|
6635
6638
|
const model = resolveLaneModel({
|
|
6636
6639
|
cliModel: input.cliModel ?? pinnedModel,
|
|
6637
6640
|
profileModel,
|
|
6638
6641
|
accountModel: targetCfg.model,
|
|
6639
6642
|
providerModel: providerCfg.model,
|
|
6640
6643
|
});
|
|
6641
|
-
if (input.cliModel === undefined && lane.model !== undefined && targetCfg.model !== undefined && model !== targetCfg.model) {
|
|
6642
|
-
throw new LanesError("model_unsupported", `Account ${target} pins model ${targetCfg.model}; lane ${laneId} resolves to ${model ?? lane.model}. Pass --model ${targetCfg.model} to move it onto that model`, { provider: lane.provider, account: target, model: model ?? lane.model, account_model: targetCfg.model, lane_id: laneId });
|
|
6643
|
-
}
|
|
6644
6644
|
const block = quotaBlockFor(loadQuotaState(hqRoot), lane.provider, target, model, now);
|
|
6645
6645
|
if (block.blocked) {
|
|
6646
6646
|
throw new LanesError("account_quota_held", `Account ${target} for ${lane.provider} is held${block.quota_reset_at ? ` until ${block.quota_reset_at}` : ""}${block.marker ? ` (${block.marker})` : ""}`, { provider: lane.provider, account: target, quota_reset_at: block.quota_reset_at ?? null, marker: block.marker ?? null, model: model ?? null });
|
|
@@ -6785,26 +6785,26 @@ async function selectAutomaticClaudeResumeAccount(ctx, lane, workerProfile, opti
|
|
|
6785
6785
|
: lane.model_source === undefined || lane.model_source === "cli" || lane.model_source === "preserved"
|
|
6786
6786
|
? lane.model
|
|
6787
6787
|
: undefined;
|
|
6788
|
-
const
|
|
6789
|
-
(lane.model_source === "cli" || lane.model_source === "preserved")
|
|
6790
|
-
? lane.account
|
|
6791
|
-
: undefined;
|
|
6788
|
+
const profileModel = profileDefaultsApply ? profile?.meta.model : undefined;
|
|
6792
6789
|
const quotaModel = resolveLaneModel({
|
|
6793
|
-
cliModel: pinnedModel
|
|
6794
|
-
profileModel
|
|
6795
|
-
accountModel:
|
|
6790
|
+
cliModel: pinnedModel,
|
|
6791
|
+
profileModel,
|
|
6792
|
+
accountModel: currentConfig?.model,
|
|
6796
6793
|
providerModel: config.providers.claude.model,
|
|
6797
6794
|
});
|
|
6798
6795
|
const recordedAccountQuotaHeld = lane.account !== undefined &&
|
|
6799
6796
|
quotaBlockFor(loadQuotaState(hqRoot), "claude", lane.account, quotaModel, ctx.now()).blocked;
|
|
6800
6797
|
const accountNeedsRepair = recordedAccountUnavailable || recordedAccountQuotaHeld;
|
|
6801
6798
|
const repairUnavailableAccount = options.repairUnavailableAccount === true && accountNeedsRepair;
|
|
6802
|
-
const preservedModel = repairUnavailableAccount && lane.model_source === "account"
|
|
6799
|
+
const preservedModel = repairUnavailableAccount && recordedAccountUnavailable && lane.model_source === "account"
|
|
6803
6800
|
? lane.model ?? currentConfig?.model
|
|
6804
6801
|
: undefined;
|
|
6802
|
+
// Repair keeps an account-derived model pinned through the move; plain
|
|
6803
|
+
// automatic rebalancing resolves each candidate's account default itself.
|
|
6804
|
+
const selectionModel = preservedModel ?? pinnedModel ?? profileModel;
|
|
6805
6805
|
const model = resolveLaneModel({
|
|
6806
|
-
cliModel:
|
|
6807
|
-
profileModel
|
|
6806
|
+
cliModel: pinnedModel,
|
|
6807
|
+
profileModel,
|
|
6808
6808
|
accountModel: lane.account === undefined ? undefined : config.providers.claude.accounts[lane.account]?.model,
|
|
6809
6809
|
providerModel: config.providers.claude.model,
|
|
6810
6810
|
});
|
|
@@ -6824,9 +6824,7 @@ async function selectAutomaticClaudeResumeAccount(ctx, lane, workerProfile, opti
|
|
|
6824
6824
|
allowedAccounts?.includes(lane.account) &&
|
|
6825
6825
|
currentConfig?.enabled === true &&
|
|
6826
6826
|
currentTargetAccount?.enabled === true &&
|
|
6827
|
-
currentTargetAccount.logged_in === true
|
|
6828
|
-
(model === undefined || currentConfig.model === undefined || currentConfig.model === model ||
|
|
6829
|
-
lane.account === recordedModelAccount))
|
|
6827
|
+
currentTargetAccount.logged_in === true)
|
|
6830
6828
|
return undefined;
|
|
6831
6829
|
const selectionInput = {
|
|
6832
6830
|
hosts: balanceHosts.snapshots,
|
|
@@ -6835,8 +6833,7 @@ async function selectAutomaticClaudeResumeAccount(ctx, lane, workerProfile, opti
|
|
|
6835
6833
|
quotaState: loadQuotaState(hqRoot),
|
|
6836
6834
|
recentUsageEnabled: await readClaudeAccountBalanceSignalsEnabled(ctx, lane.company ?? undefined),
|
|
6837
6835
|
now: ctx.now(),
|
|
6838
|
-
...(
|
|
6839
|
-
...(recordedModelAccount !== undefined ? { recordedModelAccount } : {}),
|
|
6836
|
+
...(selectionModel !== undefined ? { model: selectionModel } : {}),
|
|
6840
6837
|
unavailableHosts: balanceHosts.unavailableHosts,
|
|
6841
6838
|
};
|
|
6842
6839
|
let selected;
|
|
@@ -6853,7 +6850,7 @@ async function selectAutomaticClaudeResumeAccount(ctx, lane, workerProfile, opti
|
|
|
6853
6850
|
Array.isArray(err.details.account_reasons)
|
|
6854
6851
|
? err.details.account_reasons.filter((reason) => reason !== null && typeof reason === "object" && !Array.isArray(reason))
|
|
6855
6852
|
: [];
|
|
6856
|
-
const unavailablePinReasons = new Set(["disabled", "home_missing", "not_logged_in", "
|
|
6853
|
+
const unavailablePinReasons = new Set(["disabled", "home_missing", "not_logged_in", "quota_held"]);
|
|
6857
6854
|
const everyProfileAccountUnavailable = profilePin !== undefined && profilePin.length > 0 &&
|
|
6858
6855
|
profilePin.every((account) => accountReasons.some((reason) => reason.account === account && typeof reason.reason === "string" && unavailablePinReasons.has(reason.reason)));
|
|
6859
6856
|
if (!everyProfileAccountUnavailable || lane.account === undefined ||
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { peekHqApiKey, requireApiKeyCapability, } from '../utils/resolve-vault-credential.js';
|
|
3
3
|
import { MARKETPLACE_PREFIX, installPack, sourceMatchesPackPattern, } from './pack-install.js';
|
|
4
|
+
import { anywhereRuntimeEnabled } from './anywhere-runtime-flag.js';
|
|
4
5
|
/**
|
|
5
6
|
* True when `source` installs through the marketplace — an explicit
|
|
6
7
|
* `marketplace:` source, or a bare slug (which the dispatch below rewrites into
|
|
@@ -67,7 +68,15 @@ export function registerPackageInstallCommand(parent) {
|
|
|
67
68
|
process.exitCode = 1;
|
|
68
69
|
return;
|
|
69
70
|
}
|
|
70
|
-
|
|
71
|
+
let marketplaceDir = opts.marketplaceDir;
|
|
72
|
+
if (opts.via === 'plugin' &&
|
|
73
|
+
!marketplaceDir?.trim() &&
|
|
74
|
+
!process.env.HQ_CLAUDE_PLUGIN_MARKETPLACE?.trim() &&
|
|
75
|
+
anywhereRuntimeEnabled(process.env)) {
|
|
76
|
+
runInstallGlobal({ ...opts, marketplaceRepository: 'indigoai-us/hq-packages' });
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
runInstallGlobal({ ...opts, marketplaceDir });
|
|
71
80
|
return;
|
|
72
81
|
}
|
|
73
82
|
if (!source) {
|
|
@@ -85,6 +94,19 @@ export function registerPackageInstallCommand(parent) {
|
|
|
85
94
|
// with packs:install (hq-pro serves POST /v1/keys/listings/…/installs).
|
|
86
95
|
requireApiKeyCapability('packs:install', 'hq install');
|
|
87
96
|
assertApiKeyCanAuthorizeInstall(source);
|
|
97
|
+
if (source === '@indigoai-us/hq-anywhere') {
|
|
98
|
+
if (!anywhereRuntimeEnabled()) {
|
|
99
|
+
console.error(chalk.red('Install failed:'), 'HQ Anywhere installation is disabled by the hq-anywhere-runtime flag.');
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
102
|
+
await installPack('github:indigoai-us/hq-packages#packages/hq-pack-hq-anywhere', {
|
|
103
|
+
company: opts.company,
|
|
104
|
+
allowHooks: opts.allowHooks,
|
|
105
|
+
allowMcp: opts.allowMcp,
|
|
106
|
+
followBranch: opts.branch,
|
|
107
|
+
});
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
88
110
|
if (sourceMatchesPackPattern(source)) {
|
|
89
111
|
await installPack(source, {
|
|
90
112
|
company: opts.company,
|
package/dist/fast-scaffold.js
CHANGED
|
@@ -19,13 +19,10 @@ export const FAST_SCAFFOLD_SCRIPTS = [
|
|
|
19
19
|
flagKey: "hq-core-native-fast-route",
|
|
20
20
|
root: "live",
|
|
21
21
|
readsStdinFor: (args) => {
|
|
22
|
-
let dryRun = false;
|
|
23
22
|
for (let index = 0; index < args.length; index += 1) {
|
|
24
23
|
const arg = args[index];
|
|
25
|
-
if (arg === "--dry-run")
|
|
26
|
-
dryRun = true;
|
|
24
|
+
if (arg === "--dry-run")
|
|
27
25
|
continue;
|
|
28
|
-
}
|
|
29
26
|
if (arg === "--timeout-ms") {
|
|
30
27
|
const value = args[index + 1];
|
|
31
28
|
if (value === undefined || value.startsWith("-"))
|
|
@@ -37,7 +34,7 @@ export const FAST_SCAFFOLD_SCRIPTS = [
|
|
|
37
34
|
continue;
|
|
38
35
|
return false;
|
|
39
36
|
}
|
|
40
|
-
return
|
|
37
|
+
return true;
|
|
41
38
|
},
|
|
42
39
|
stdinMaxBytes: 2_048,
|
|
43
40
|
requiresNodeNetworkCompatibility: true,
|
|
@@ -76,8 +76,6 @@ export declare function selectClaudeAccount(input: {
|
|
|
76
76
|
config: LanesConfig;
|
|
77
77
|
allowedAccounts?: readonly string[];
|
|
78
78
|
model?: string;
|
|
79
|
-
/** Keep the recorded account eligible for its already-running CLI model. */
|
|
80
|
-
recordedModelAccount?: string;
|
|
81
79
|
quotaState?: QuotaState;
|
|
82
80
|
/** Recent completed-run tiebreak; callers pass the default-off hq-flags result. */
|
|
83
81
|
recentUsageEnabled?: boolean;
|
|
@@ -131,12 +131,7 @@ function buildLoads(input) {
|
|
|
131
131
|
const quotaBlock = input.quotaState === undefined
|
|
132
132
|
? undefined
|
|
133
133
|
: quotaBlockFor(input.quotaState, "claude", account, quotaModel, input.now);
|
|
134
|
-
const ineligible = targetIneligibilityReason(account, configAccounts[account]?.enabled === true, input.allowedAccounts, targetLoad) ?? (
|
|
135
|
-
input.model !== undefined && configuredModel !== undefined && configuredModel !== input.model
|
|
136
|
-
? "model_mismatch"
|
|
137
|
-
: quotaBlock?.blocked === true
|
|
138
|
-
? "quota_held"
|
|
139
|
-
: undefined);
|
|
134
|
+
const ineligible = targetIneligibilityReason(account, configAccounts[account]?.enabled === true, input.allowedAccounts, targetLoad) ?? (quotaBlock?.blocked === true ? "quota_held" : undefined);
|
|
140
135
|
if (ineligible !== undefined)
|
|
141
136
|
skipped.push({ account, host: targetHost, reason: ineligible });
|
|
142
137
|
const knownCounts = hosts.flatMap((host) => host.live_lanes === null ? [] : [host.live_lanes]);
|
package/dist/main.js
CHANGED
|
@@ -11,7 +11,7 @@ import path from "node:path";
|
|
|
11
11
|
import { fileURLToPath } from "node:url";
|
|
12
12
|
import { Command } from "commander";
|
|
13
13
|
import { finishSentrySession, initSentry, Sentry } from "./sentry.js";
|
|
14
|
-
import {
|
|
14
|
+
import { addCommandBreadcrumb } from "./utils/command-breadcrumb.js";
|
|
15
15
|
import { environmentalFsErrorMessage, filesystemErrorCaptureContext, filesystemErrorMessage, } from "./utils/environmental-error.js";
|
|
16
16
|
import { syncStateLockMessage } from "./utils/sync-state-lock-error.js";
|
|
17
17
|
import { networkTransportErrorMessage } from "./utils/network-transport-error.js";
|
|
@@ -191,11 +191,7 @@ export async function runCli() {
|
|
|
191
191
|
let versionUpdateHandled = false;
|
|
192
192
|
let commandParsed = false;
|
|
193
193
|
try {
|
|
194
|
-
runGlobalNetworkLifecycle(process.argv, () => Sentry.addBreadcrumb(
|
|
195
|
-
category: "command",
|
|
196
|
-
message: sanitizeArgv(process.argv.slice(2)).join(" "),
|
|
197
|
-
level: "info",
|
|
198
|
-
}));
|
|
194
|
+
runGlobalNetworkLifecycle(process.argv, () => addCommandBreadcrumb(process.argv.slice(2), (breadcrumb) => Sentry.addBreadcrumb(breadcrumb)));
|
|
199
195
|
// Version handling, both halves. Skipped for inspection flags
|
|
200
196
|
// (`--version`, `--help`) so users debugging a broken install can still
|
|
201
197
|
// introspect what they have, and silent on any failure — neither half may
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { NodeOptions } from "@sentry/node";
|
|
2
|
+
type SentryModule = typeof import("@sentry/node");
|
|
3
|
+
type SentryBreadcrumb = Parameters<SentryModule["addBreadcrumb"]>[0];
|
|
4
|
+
/** Shared Sentry SDK setup for the command graph and isolated fast handlers. */
|
|
5
|
+
export declare function initSentryRuntime(beforeSend: NodeOptions["beforeSend"], overrides?: Partial<Pick<NodeOptions, "transport">>, pendingBreadcrumbs?: readonly SentryBreadcrumb[]): SentryModule | undefined;
|
|
6
|
+
export declare function finishSentryRuntimeSession(): void;
|
|
7
|
+
export declare function addSentryRuntimeBreadcrumb(breadcrumb: SentryBreadcrumb): void;
|
|
8
|
+
export declare function flushSentryRuntime(timeoutMs: number): PromiseLike<boolean>;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=sentry-init.d.ts.map
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { BUNDLED_DSN } from "./sentry-dsn.generated.js";
|
|
3
|
+
import { CLI_VERSION } from "./cli-version.js";
|
|
4
|
+
import { beforeBreadcrumb } from "./utils/breadcrumb-buffer.js";
|
|
5
|
+
import { getCachedSentryUser } from "./utils/sentry-identity.js";
|
|
6
|
+
const requireSentry = createRequire(import.meta.url);
|
|
7
|
+
let sentryModule;
|
|
8
|
+
function loadSentryModule() {
|
|
9
|
+
sentryModule ??= requireSentry("@sentry/node");
|
|
10
|
+
return sentryModule;
|
|
11
|
+
}
|
|
12
|
+
/** Shared Sentry SDK setup for the command graph and isolated fast handlers. */
|
|
13
|
+
export function initSentryRuntime(beforeSend, overrides = {}, pendingBreadcrumbs = []) {
|
|
14
|
+
const dsn = BUNDLED_DSN || process.env.SENTRY_DSN;
|
|
15
|
+
if (!dsn)
|
|
16
|
+
return undefined;
|
|
17
|
+
const sdk = loadSentryModule();
|
|
18
|
+
sdk.init({
|
|
19
|
+
dsn,
|
|
20
|
+
// This async ESM hook adds large startup cost. HQ retains its explicit
|
|
21
|
+
// breadcrumbs without enabling automatic third-party instrumentation.
|
|
22
|
+
registerEsmLoaderHooks: false,
|
|
23
|
+
release: `hq-cli@${CLI_VERSION}`,
|
|
24
|
+
environment: process.env.HQ_CLI_ENV ?? "production",
|
|
25
|
+
initialScope: { tags: { repo: "hq-cli" } },
|
|
26
|
+
beforeSend,
|
|
27
|
+
beforeBreadcrumb,
|
|
28
|
+
integrations: (defaults) => defaults.filter((integration) => integration.name !== "ProcessSession"),
|
|
29
|
+
...overrides,
|
|
30
|
+
});
|
|
31
|
+
const user = getCachedSentryUser();
|
|
32
|
+
if (user)
|
|
33
|
+
sdk.setUser(user);
|
|
34
|
+
for (const breadcrumb of pendingBreadcrumbs)
|
|
35
|
+
sdk.addBreadcrumb(breadcrumb);
|
|
36
|
+
sdk.startSession();
|
|
37
|
+
return sdk;
|
|
38
|
+
}
|
|
39
|
+
export function finishSentryRuntimeSession() {
|
|
40
|
+
sentryModule?.endSession();
|
|
41
|
+
}
|
|
42
|
+
export function addSentryRuntimeBreadcrumb(breadcrumb) {
|
|
43
|
+
sentryModule?.addBreadcrumb(breadcrumb);
|
|
44
|
+
}
|
|
45
|
+
export function flushSentryRuntime(timeoutMs) {
|
|
46
|
+
return sentryModule?.flush(timeoutMs) ?? Promise.resolve(true);
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=sentry-init.js.map
|
package/dist/sentry.js
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
|
-
import { BUNDLED_DSN } from "./sentry-dsn.generated.js";
|
|
3
2
|
import { beforeSend } from "./sentry-before-send.js";
|
|
4
|
-
import { beforeBreadcrumb } from "./utils/breadcrumb-buffer.js";
|
|
5
|
-
import { CLI_VERSION } from "./cli-version.js";
|
|
6
|
-
import { getCachedSentryUser } from "./utils/sentry-identity.js";
|
|
7
3
|
import { isEpipe } from "./utils/epipe.js";
|
|
8
4
|
import { companySkillWrapperSentryTagsFor } from "./lib/company-skill-wrapper.js";
|
|
9
5
|
import { environmentalFsErrorMessage } from "./utils/environmental-error.js";
|
|
10
6
|
import { incompleteInstallCaptureContext, incompleteInstallMessage, } from "./utils/incomplete-install-error.js";
|
|
11
7
|
import { installTreeTornSentryTagsFor, sentryFingerprintFor, sentryMcpCollisionContextFor, } from "./utils/sentry-fingerprint.js";
|
|
8
|
+
import { finishSentryRuntimeSession, initSentryRuntime } from "./sentry-init.js";
|
|
12
9
|
const inertSentryScope = {
|
|
13
10
|
setTag: () => inertSentryScope,
|
|
14
11
|
setUser: () => inertSentryScope,
|
|
@@ -17,11 +14,10 @@ const inertSentryScope = {
|
|
|
17
14
|
setLevel: () => inertSentryScope,
|
|
18
15
|
setFingerprint: () => inertSentryScope,
|
|
19
16
|
};
|
|
20
|
-
const requireSentry = createRequire(import.meta.url);
|
|
21
17
|
let sentryModule;
|
|
22
18
|
let pendingSentryBreadcrumbs = [];
|
|
23
19
|
function loadSentryModule() {
|
|
24
|
-
sentryModule ??=
|
|
20
|
+
sentryModule ??= createRequire(import.meta.url)("@sentry/node");
|
|
25
21
|
return sentryModule;
|
|
26
22
|
}
|
|
27
23
|
/** Load the SDK through the same resolver used by the lazy Sentry facade. */
|
|
@@ -227,46 +223,13 @@ function flagRegistryWorkerMissingFileTags(error, event) {
|
|
|
227
223
|
* exercised without an ingest request. Production callers pass nothing.
|
|
228
224
|
*/
|
|
229
225
|
export function initSentry(overrides = {}) {
|
|
230
|
-
|
|
231
|
-
if (!
|
|
226
|
+
sentryModule = initSentryRuntime(epipeAwareBeforeSend, overrides, pendingSentryBreadcrumbs);
|
|
227
|
+
if (!sentryModule)
|
|
232
228
|
return;
|
|
233
|
-
const sdk = loadSentryModule();
|
|
234
|
-
sdk.init({
|
|
235
|
-
dsn,
|
|
236
|
-
// import-in-the-middle's async ESM loader makes every later CLI import
|
|
237
|
-
// substantially slower. Keep HQ's explicit breadcrumbs, but give up
|
|
238
|
-
// automatic ESM third-party instrumentation (its spans/breadcrumbs).
|
|
239
|
-
registerEsmLoaderHooks: false,
|
|
240
|
-
// CLI_VERSION reads package.json at runtime; npm_package_version is only
|
|
241
|
-
// set under `npm run`, so an installed `hq` binary would always report
|
|
242
|
-
// 0.0.0 and never match the uploaded source maps.
|
|
243
|
-
release: `hq-cli@${CLI_VERSION}`,
|
|
244
|
-
environment: process.env.HQ_CLI_ENV ?? "production",
|
|
245
|
-
initialScope: {
|
|
246
|
-
tags: { repo: "hq-cli" },
|
|
247
|
-
},
|
|
248
|
-
beforeSend: epipeAwareBeforeSend,
|
|
249
|
-
beforeBreadcrumb,
|
|
250
|
-
// HQ owns this short-lived CLI session explicitly below and closes it from
|
|
251
|
-
// main.ts. Leaving the SDK default enabled starts a competing session.
|
|
252
|
-
integrations: (defaults) => defaults.filter((integration) => integration.name !== "ProcessSession"),
|
|
253
|
-
...overrides,
|
|
254
|
-
});
|
|
255
|
-
// Attribute events to the logged-in HQ identity (best-effort; null when not
|
|
256
|
-
// logged in). A CLI process is one user, so global setUser is correct here.
|
|
257
|
-
const user = getCachedSentryUser();
|
|
258
|
-
if (user)
|
|
259
|
-
sdk.setUser(user);
|
|
260
|
-
for (const breadcrumb of pendingSentryBreadcrumbs)
|
|
261
|
-
sdk.addBreadcrumb(breadcrumb);
|
|
262
229
|
pendingSentryBreadcrumbs = [];
|
|
263
|
-
// Release health: one session per CLI run. Ended + flushed in index.ts's
|
|
264
|
-
// finally (covers every exit path). A captured error auto-marks the session
|
|
265
|
-
// "errored", so crash-free numbers per release stay accurate.
|
|
266
|
-
sdk.startSession();
|
|
267
230
|
}
|
|
268
231
|
/** Finish HQ's one per-invocation release-health session. */
|
|
269
232
|
export function finishSentrySession() {
|
|
270
|
-
|
|
233
|
+
finishSentryRuntimeSession();
|
|
271
234
|
}
|
|
272
235
|
//# sourceMappingURL=sentry.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type CommandBreadcrumb = {
|
|
2
|
+
category: "command";
|
|
3
|
+
message: string;
|
|
4
|
+
level: "info";
|
|
5
|
+
};
|
|
6
|
+
export declare function addCommandBreadcrumb(argv: readonly string[], addBreadcrumb: (breadcrumb: CommandBreadcrumb) => void): void;
|
|
7
|
+
//# sourceMappingURL=command-breadcrumb.d.ts.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { sanitizeArgv } from "./sanitize-argv.js";
|
|
2
|
+
export function addCommandBreadcrumb(argv, addBreadcrumb) {
|
|
3
|
+
addBreadcrumb({
|
|
4
|
+
category: "command",
|
|
5
|
+
message: sanitizeArgv(argv).join(" "),
|
|
6
|
+
level: "info",
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=command-breadcrumb.js.map
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type VersionInfo } from "./feedback-versions.js";
|
|
2
2
|
import type { FeedbackLogsBlob } from "./feedback-logs.js";
|
|
3
|
+
export { sanitizeArgv } from "./sanitize-argv.js";
|
|
3
4
|
interface GitContext {
|
|
4
5
|
branch: string | null;
|
|
5
6
|
head: string | null;
|
|
@@ -37,7 +38,5 @@ export interface DiagnosticsBlob {
|
|
|
37
38
|
*/
|
|
38
39
|
logs?: FeedbackLogsBlob;
|
|
39
40
|
}
|
|
40
|
-
export declare function sanitizeArgv(argv: string[]): string[];
|
|
41
41
|
export declare function collectDiagnostics(): DiagnosticsBlob;
|
|
42
|
-
export {};
|
|
43
42
|
//# sourceMappingURL=feedback-diagnostics.d.ts.map
|
|
@@ -3,35 +3,8 @@ import { execFileSync } from "child_process";
|
|
|
3
3
|
import { getRecentBreadcrumbs } from "./breadcrumb-buffer.js";
|
|
4
4
|
import { CLI_VERSION } from "../cli-version.js";
|
|
5
5
|
import { collectVersions } from "./feedback-versions.js";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
"--secret",
|
|
9
|
-
"--password",
|
|
10
|
-
"--key",
|
|
11
|
-
"--api-key",
|
|
12
|
-
"--access-token",
|
|
13
|
-
"--auth-token",
|
|
14
|
-
]);
|
|
15
|
-
export function sanitizeArgv(argv) {
|
|
16
|
-
const result = [];
|
|
17
|
-
for (let i = 0; i < argv.length; i++) {
|
|
18
|
-
const arg = argv[i];
|
|
19
|
-
if (arg.startsWith("--") && arg.includes("=")) {
|
|
20
|
-
const eqIdx = arg.indexOf("=");
|
|
21
|
-
const flag = arg.slice(0, eqIdx);
|
|
22
|
-
if (SECRET_FLAGS.has(flag)) {
|
|
23
|
-
result.push(`${flag}=***`);
|
|
24
|
-
continue;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
result.push(arg);
|
|
28
|
-
if (SECRET_FLAGS.has(arg) && i + 1 < argv.length) {
|
|
29
|
-
result.push("***");
|
|
30
|
-
i++;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
return result;
|
|
34
|
-
}
|
|
6
|
+
import { sanitizeArgv } from "./sanitize-argv.js";
|
|
7
|
+
export { sanitizeArgv } from "./sanitize-argv.js";
|
|
35
8
|
function sanitizeRemoteUrl(url) {
|
|
36
9
|
return url.replace(/https?:\/\/[^@]+@/, "https://***@");
|
|
37
10
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const SECRET_FLAGS = new Set([
|
|
2
|
+
"--token",
|
|
3
|
+
"--secret",
|
|
4
|
+
"--password",
|
|
5
|
+
"--key",
|
|
6
|
+
"--api-key",
|
|
7
|
+
"--access-token",
|
|
8
|
+
"--auth-token",
|
|
9
|
+
]);
|
|
10
|
+
export function sanitizeArgv(argv) {
|
|
11
|
+
const result = [];
|
|
12
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
13
|
+
const arg = argv[i];
|
|
14
|
+
if (arg.startsWith("--") && arg.includes("=")) {
|
|
15
|
+
const eqIdx = arg.indexOf("=");
|
|
16
|
+
const flag = arg.slice(0, eqIdx);
|
|
17
|
+
if (SECRET_FLAGS.has(flag)) {
|
|
18
|
+
result.push(`${flag}=***`);
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
result.push(arg);
|
|
23
|
+
if (SECRET_FLAGS.has(arg) && i + 1 < argv.length) {
|
|
24
|
+
result.push("***");
|
|
25
|
+
i += 1;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=sanitize-argv.js.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare function assertFastPathStaysCommandGraphFree(loadedModules: readonly string[], distDirectory: string, allowedCommandModules?: readonly string[]): void;
|
|
1
|
+
export declare function assertFastPathStaysCommandGraphFree(loadedModules: readonly string[], distDirectory: string, allowedCommandModules?: readonly string[], allowedSentryPackageRoots?: readonly string[]): void;
|
|
2
2
|
//# sourceMappingURL=scaffold-fast-import-guard.d.ts.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
|
-
function forbiddenFastPathModules(loadedModules, distDirectory, allowedCommandModules = []) {
|
|
3
|
+
function forbiddenFastPathModules(loadedModules, distDirectory, allowedCommandModules = [], allowedSentryPackageRoots = []) {
|
|
4
4
|
const dist = path.resolve(distDirectory);
|
|
5
5
|
const main = path.join(dist, "main.js");
|
|
6
6
|
const commands = `${path.join(dist, "commands")}${path.sep}`;
|
|
@@ -9,6 +9,7 @@ function forbiddenFastPathModules(loadedModules, distDirectory, allowedCommandMo
|
|
|
9
9
|
const fullNetworkCompat = path.join(dist, "node-network-compat.js");
|
|
10
10
|
const sentryEntrypoint = path.join(dist, "sentry.js");
|
|
11
11
|
const allowedCommands = new Set(allowedCommandModules.map((module) => path.resolve(module)));
|
|
12
|
+
const allowedSentryRoots = allowedSentryPackageRoots.map((module) => path.resolve(module));
|
|
12
13
|
return [...new Set(loadedModules.map((loaded) => {
|
|
13
14
|
if (loaded.startsWith("file:")) {
|
|
14
15
|
try {
|
|
@@ -26,13 +27,15 @@ function forbiddenFastPathModules(loadedModules, distDirectory, allowedCommandMo
|
|
|
26
27
|
path.resolve(loaded) === coreCommand ||
|
|
27
28
|
path.resolve(loaded) === fullNetworkCompat ||
|
|
28
29
|
path.resolve(loaded) === sentryEntrypoint ||
|
|
29
|
-
path.resolve(loaded).includes(`${path.sep}node_modules${path.sep}@sentry${path.sep}`)
|
|
30
|
+
(path.resolve(loaded).includes(`${path.sep}node_modules${path.sep}@sentry${path.sep}`) &&
|
|
31
|
+
!allowedSentryRoots.some((root) => path.resolve(loaded) === root ||
|
|
32
|
+
path.resolve(loaded).startsWith(root + path.sep))))
|
|
30
33
|
.filter((loaded) => !allowedCommands.has(path.resolve(loaded)) ||
|
|
31
34
|
path.resolve(loaded) === main || path.resolve(loaded) === coreCommand)
|
|
32
35
|
.sort();
|
|
33
36
|
}
|
|
34
|
-
export function assertFastPathStaysCommandGraphFree(loadedModules, distDirectory, allowedCommandModules = []) {
|
|
35
|
-
const forbidden = forbiddenFastPathModules(loadedModules, distDirectory, allowedCommandModules);
|
|
37
|
+
export function assertFastPathStaysCommandGraphFree(loadedModules, distDirectory, allowedCommandModules = [], allowedSentryPackageRoots = []) {
|
|
38
|
+
const forbidden = forbiddenFastPathModules(loadedModules, distDirectory, allowedCommandModules, allowedSentryPackageRoots);
|
|
36
39
|
if (forbidden.length > 0) {
|
|
37
40
|
throw new Error(`fast core loaded forbidden command modules:\n${forbidden.join("\n")}`);
|
|
38
41
|
}
|
package/docs/anywhere-runtime.md
CHANGED
|
@@ -80,6 +80,14 @@ resident daemon does not support the health operation, doctor falls back to
|
|
|
80
80
|
`ping` for reachability, version and uptime, then warns that a restart is needed
|
|
81
81
|
for full diagnostics.
|
|
82
82
|
|
|
83
|
+
The hq-anywhere-runtime flag also gates the published Claude plugin marketplace
|
|
84
|
+
and Codex pack source. When enabled, hq-cli adds the `indigoai-us/hq-packages`
|
|
85
|
+
Claude marketplace and installs the `hq` plugin, or resolves the exact
|
|
86
|
+
`@indigoai-us/hq-anywhere` alias to
|
|
87
|
+
`github:indigoai-us/hq-packages#packages/hq-pack-hq-anywhere`. The flag-off
|
|
88
|
+
path does not resolve either published source. Use `--marketplace-dir` to
|
|
89
|
+
override the Claude marketplace with a local build.
|
|
90
|
+
|
|
83
91
|
## Link a repository
|
|
84
92
|
|
|
85
93
|
Run `hq link <company>` from a repository to associate it with a company. The
|