@indigoai-us/hq-cli 5.309.0 → 5.309.1
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 +13 -0
- package/dist/command-catalog.generated.d.ts +4 -1
- package/dist/command-catalog.generated.js +5 -1
- package/dist/commands/agents.js +6 -0
- package/dist/commands/core-checkpoint-gate-setting.d.ts +0 -10
- package/dist/commands/core-checkpoint-gate-setting.js +3 -3
- package/dist/commands/core-ontology.d.ts +1 -9
- package/dist/commands/core-ontology.js +4 -4
- package/dist/commands/core-policy.d.ts +0 -3
- package/dist/commands/core-policy.js +3 -3
- package/dist/commands/files.js +1 -1
- package/dist/commands/index-cmd.js +1 -1
- package/dist/commands/integrations-api.d.ts +9 -4
- package/dist/commands/integrations-api.js +9 -6
- package/dist/commands/integrations-manage.js +11 -3
- package/dist/commands/integrations.d.ts +8 -0
- package/dist/commands/integrations.js +83 -0
- package/dist/commands/lanes-checks.d.ts +0 -2
- package/dist/commands/lanes-checks.js +2 -2
- package/dist/commands/lanes.d.ts +0 -11
- package/dist/commands/lanes.js +6 -6
- package/dist/commands/mcp-registration.d.ts +5 -11
- package/dist/commands/mcp-registration.js +2 -6
- package/dist/lib/core-utils/sentry-report.d.ts +5 -11
- package/dist/lib/core-utils/sentry-report.js +2 -2
- package/dist/lib/db/local.d.ts +0 -5
- package/dist/lib/db/local.js +1 -1
- package/dist/lib/db/migrate.d.ts +0 -12
- package/dist/lib/db/migrate.js +5 -5
- package/dist/lib/db/sql.d.ts +0 -10
- package/dist/lib/db/sql.js +4 -8
- package/dist/lib/doctor/checks/codex-wiring.js +1 -33
- package/dist/lib/doctor/checks/grok-wiring.js +1 -34
- package/dist/lib/doctor/deep/effects.js +1 -3
- package/dist/lib/doctor/deep/sandbox.js +1 -3
- package/dist/lib/doctor/fixtures/schema.js +1 -3
- package/dist/lib/doctor/shared.d.ts +8 -0
- package/dist/lib/doctor/shared.js +38 -0
- package/dist/lib/lanes/autopilot.js +6 -4
- package/dist/lib/monitor.d.ts +2 -6
- package/dist/lib/monitor.js +5 -5
- package/dist/lib/report-publish.d.ts +1 -10
- package/dist/lib/report-publish.js +3 -9
- package/dist/lib/search-index/index.d.ts +7 -0
- package/dist/lib/search-index/index.js +21 -2
- package/dist/lib/search-index/qmd-exit-diagnostics.d.ts +5 -0
- package/dist/lib/search-index/qmd-exit-diagnostics.js +40 -0
- package/dist/lib/service-manager/launchd.d.ts +0 -7
- package/dist/lib/service-manager/launchd.js +2 -2
- package/dist/main.js +7 -2
- package/dist/startup-registration.js +9 -1
- package/dist/utils/client-health.d.ts +0 -4
- package/dist/utils/client-health.js +2 -2
- package/dist/utils/feedback-diagnostics.d.ts +2 -1
- package/dist/utils/feedback-log-bundle.d.ts +0 -25
- package/dist/utils/feedback-log-bundle.js +5 -5
- package/dist/utils/feedback-logs.d.ts +4 -38
- package/dist/utils/feedback-logs.js +7 -7
- package/dist/utils/feedback-screenshots.d.ts +0 -1
- package/dist/utils/feedback-screenshots.js +1 -1
- package/dist/utils/install-tree-torn.d.ts +19 -13
- package/dist/utils/install-tree-torn.js +77 -2
- package/dist/utils/local-files-overview.d.ts +0 -6
- package/dist/utils/local-files-overview.js +1 -1
- package/dist/utils/qmd-model-download-error.d.ts +0 -8
- package/dist/utils/qmd-model-download-error.js +1 -1
- package/dist/utils/qmd-query-document-error.d.ts +0 -6
- package/dist/utils/qmd-query-document-error.js +1 -1
- package/dist/utils/scaffold-fast-import-guard.d.ts +0 -1
- package/dist/utils/scaffold-fast-import-guard.js +1 -1
- package/dist/utils/scaffold-sibling-references.d.ts +3 -2
- package/dist/utils/sentry-fingerprint.js +17 -0
- package/dist/utils/version-gate.d.ts +4 -0
- package/dist/utils/version-gate.js +13 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [5.309.1] — 2026-10-01
|
|
6
|
+
- Fixed `hq agents rm` to remove external agents through revoke rather than the hosted-agent teardown path.
|
|
7
|
+
|
|
8
|
+
- Wait for asynchronous integration approvals to reach a terminal queue state and report pending work accurately.
|
|
9
|
+
|
|
10
|
+
- Add privacy-safe qmd exit diagnostics to Sentry reports.
|
|
11
|
+
|
|
12
|
+
- `hq files delete` help now explains exact-key and wildcard-prefix matching.
|
|
13
|
+
|
|
14
|
+
- Move directly controlled Linux CI checks to ARM runners where their tools support ARM.
|
|
15
|
+
- Lead autopilot resolves a lead's company through its project when the lane record does not carry a company, and skips project paths registered to a different company.
|
|
16
|
+
- Install-tree recovery errors now report bounded sibling, target, version-gate phase, and updater-liveness enums.
|
|
17
|
+
|
|
5
18
|
## [5.309.0] — 2026-10-01
|
|
6
19
|
- `hq lanes autopilot run` now evaluates leads based on lane ownership rather than a host label, and reports registered leads assigned to another host as skipped.
|
|
7
20
|
- Allow non-overlapping story-prefix teams to share a project in lane checks and reports.
|
|
@@ -1734,7 +1734,7 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
1734
1734
|
readonly subcommands: readonly [];
|
|
1735
1735
|
}, {
|
|
1736
1736
|
readonly name: "delete";
|
|
1737
|
-
readonly description: "Delete vault objects
|
|
1737
|
+
readonly description: "Delete vault objects by exact key or bounded prefix. A trailing slash expands recursively; use * for an explicit wildcard prefix. Always previews the exact count first; prompts for confirmation unless --yes. Use --personal to target your own personal vault instead of a company.";
|
|
1738
1738
|
readonly aliases: readonly [];
|
|
1739
1739
|
readonly hidden: false;
|
|
1740
1740
|
readonly usage: "[options] <prefix>";
|
|
@@ -3492,6 +3492,9 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
3492
3492
|
}, {
|
|
3493
3493
|
readonly flags: "--json";
|
|
3494
3494
|
readonly description: "Machine-readable output";
|
|
3495
|
+
}, {
|
|
3496
|
+
readonly flags: "--history";
|
|
3497
|
+
readonly description: "Include recently decided calls";
|
|
3495
3498
|
}];
|
|
3496
3499
|
readonly subcommands: readonly [];
|
|
3497
3500
|
}, {
|
|
@@ -2213,7 +2213,7 @@ export const COMMAND_CATALOG = [
|
|
|
2213
2213
|
},
|
|
2214
2214
|
{
|
|
2215
2215
|
"name": "delete",
|
|
2216
|
-
"description": "Delete vault objects
|
|
2216
|
+
"description": "Delete vault objects by exact key or bounded prefix. A trailing slash expands recursively; use * for an explicit wildcard prefix. Always previews the exact count first; prompts for confirmation unless --yes. Use --personal to target your own personal vault instead of a company.",
|
|
2217
2217
|
"aliases": [],
|
|
2218
2218
|
"hidden": false,
|
|
2219
2219
|
"usage": "[options] <prefix>",
|
|
@@ -4500,6 +4500,10 @@ export const COMMAND_CATALOG = [
|
|
|
4500
4500
|
{
|
|
4501
4501
|
"flags": "--json",
|
|
4502
4502
|
"description": "Machine-readable output"
|
|
4503
|
+
},
|
|
4504
|
+
{
|
|
4505
|
+
"flags": "--history",
|
|
4506
|
+
"description": "Include recently decided calls"
|
|
4503
4507
|
}
|
|
4504
4508
|
],
|
|
4505
4509
|
"subcommands": []
|
package/dist/commands/agents.js
CHANGED
|
@@ -1946,6 +1946,12 @@ export function registerAgentsCommand(program) {
|
|
|
1946
1946
|
}
|
|
1947
1947
|
try {
|
|
1948
1948
|
const token = (await resolveVaultCredential()).token;
|
|
1949
|
+
const status = await getAgentStatus(token, agentUid);
|
|
1950
|
+
if (recordValue(status.agent)?.runtimeKind === "external") {
|
|
1951
|
+
await revokeAgent(token, agentUid, true);
|
|
1952
|
+
console.log(chalk.green(`Revoked and removed external agent ${agentUid}.`));
|
|
1953
|
+
return;
|
|
1954
|
+
}
|
|
1949
1955
|
const result = await deprovisionAgent(token, agentUid, opts.confirmDestroyAgentsV2);
|
|
1950
1956
|
if (result.terminal === false) {
|
|
1951
1957
|
console.log(chalk.yellow(`Deprovision in progress for agent ${agentUid} (not yet fully torn down — re-run to continue).`));
|
|
@@ -18,16 +18,6 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import { Command } from "commander";
|
|
20
20
|
export declare const CHECKPOINT_GATE_SETTING_FLAG = "checkpoint.gate.company-setting";
|
|
21
|
-
/** The refresh gives up after this long; the gate keeps the cached value. */
|
|
22
|
-
export declare const CHECKPOINT_GATE_REFRESH_TIMEOUT_MS = 3000;
|
|
23
|
-
export declare function assertCompanySlug(slug: string): void;
|
|
24
|
-
export interface CheckpointGateCacheEntry {
|
|
25
|
-
company: string;
|
|
26
|
-
checkpointGateEnabled: boolean;
|
|
27
|
-
/** Unix seconds. The gate treats an entry older than 600 s as stale. */
|
|
28
|
-
fetched_at: number;
|
|
29
|
-
}
|
|
30
|
-
export declare function checkpointGateCacheDir(home?: string): string;
|
|
31
21
|
export declare function checkpointGateCachePath(slug: string, home?: string): string;
|
|
32
22
|
export type CheckpointGateRefreshOutcome = {
|
|
33
23
|
status: "written";
|
|
@@ -25,15 +25,15 @@ import { readLanesRegistryFlag } from "../lib/lanes/registry-flag.js";
|
|
|
25
25
|
import { describeCoreCommand } from "./core-command-catalog.js";
|
|
26
26
|
export const CHECKPOINT_GATE_SETTING_FLAG = "checkpoint.gate.company-setting";
|
|
27
27
|
/** The refresh gives up after this long; the gate keeps the cached value. */
|
|
28
|
-
|
|
28
|
+
const CHECKPOINT_GATE_REFRESH_TIMEOUT_MS = 3_000;
|
|
29
29
|
/** Same character set the gate script accepts; anything else is refused. */
|
|
30
30
|
const SLUG_RE = /^[a-z0-9][a-z0-9-]*$/;
|
|
31
|
-
|
|
31
|
+
function assertCompanySlug(slug) {
|
|
32
32
|
if (!SLUG_RE.test(slug) || slug === "personal") {
|
|
33
33
|
throw new Error(`checkpoint-gate-setting: '${slug}' is not a company slug`);
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
|
-
|
|
36
|
+
function checkpointGateCacheDir(home = os.homedir()) {
|
|
37
37
|
return path.join(home, ".hq", "cache", "checkpoint-gate");
|
|
38
38
|
}
|
|
39
39
|
export function checkpointGateCachePath(slug, home = os.homedir()) {
|
|
@@ -9,11 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { Command } from "commander";
|
|
11
11
|
import { type FlagReader } from "../lib/checkpoint-ontology/plan.js";
|
|
12
|
-
import { type
|
|
13
|
-
import { type DeleteVaultKey, type ListedPair, type MergeCopiesReport } from "../lib/checkpoint-ontology/merge-copies.js";
|
|
14
|
-
export declare const ONTOLOGY_PAYLOAD_FILE = "payload.json";
|
|
15
|
-
/** The hq-flags read, scoped to the payload company. */
|
|
16
|
-
export declare const registryFlagReader: FlagReader;
|
|
12
|
+
import { type DeleteVaultKey, type MergeCopiesReport } from "../lib/checkpoint-ontology/merge-copies.js";
|
|
17
13
|
export interface OntologyCommandDeps {
|
|
18
14
|
readFlag?: FlagReader;
|
|
19
15
|
now?: () => Date;
|
|
@@ -41,8 +37,6 @@ export interface MergeCopiesCommandOptions {
|
|
|
41
37
|
json?: boolean;
|
|
42
38
|
}
|
|
43
39
|
export declare function mergeCopiesSummary(report: MergeCopiesReport): string;
|
|
44
|
-
/** Reads a `--only` file: a JSON array of `{ copy, original }` company-relative paths. */
|
|
45
|
-
export declare function readListedPairs(file: string): ListedPair[];
|
|
46
40
|
/**
|
|
47
41
|
* `hq core ontology merge-copies`: merge `<slug> <n>.md` copies into their
|
|
48
42
|
* original (`--apply`), then delete merged copies from the vault
|
|
@@ -58,8 +52,6 @@ export declare function readListedPairs(file: string): ListedPair[];
|
|
|
58
52
|
* which a person reviewed, instead of the pairs a finder produces.
|
|
59
53
|
*/
|
|
60
54
|
export declare function runOntologyMergeCopies(hqRoot: string, slug: string | undefined, options: MergeCopiesCommandOptions, deps?: OntologyCommandDeps): Promise<string>;
|
|
61
|
-
/** Reads a `drop-aliases --only` file: a JSON array of `{ entity, alias }`. */
|
|
62
|
-
export declare function readAliasDrops(file: string): AliasDrop[];
|
|
63
55
|
/**
|
|
64
56
|
* `hq core ontology drop-aliases`: remove the reviewed `{ entity, alias }`
|
|
65
57
|
* pairs in `--only` from entity files (`--apply`); without it only reports.
|
|
@@ -22,7 +22,7 @@ import { MERGE_COPIES_FLAG, MERGE_LISTED_PAIRS_FLAG, MERGE_REWRITE_REFS_FLAG, ME
|
|
|
22
22
|
import { callDeleteEndpoint } from "./files.js";
|
|
23
23
|
import { resolveVaultCredential } from "../utils/resolve-vault-credential.js";
|
|
24
24
|
import { registerCoreOntologyNativeCommands } from "./core-ontology-native.js";
|
|
25
|
-
|
|
25
|
+
const ONTOLOGY_PAYLOAD_FILE = "payload.json";
|
|
26
26
|
class OntologyUsageError extends Error {
|
|
27
27
|
}
|
|
28
28
|
function readPayload(runDir) {
|
|
@@ -49,7 +49,7 @@ function requireRunDir(runDir) {
|
|
|
49
49
|
return resolved;
|
|
50
50
|
}
|
|
51
51
|
/** The hq-flags read, scoped to the payload company. */
|
|
52
|
-
|
|
52
|
+
const registryFlagReader = (flagKey, company) => readLanesRegistryFlag(flagKey, {
|
|
53
53
|
company: company.cloudUid ? { slug: company.slug, uid: company.cloudUid } : null,
|
|
54
54
|
});
|
|
55
55
|
function companyBriefSuffix(brief) {
|
|
@@ -160,7 +160,7 @@ export function mergeCopiesSummary(report) {
|
|
|
160
160
|
return lines.join("\n");
|
|
161
161
|
}
|
|
162
162
|
/** Reads a `--only` file: a JSON array of `{ copy, original }` company-relative paths. */
|
|
163
|
-
|
|
163
|
+
function readListedPairs(file) {
|
|
164
164
|
let parsed;
|
|
165
165
|
try {
|
|
166
166
|
parsed = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
@@ -270,7 +270,7 @@ export async function runOntologyMergeCopies(hqRoot, slug, options, deps = {}) {
|
|
|
270
270
|
return options.json ? JSON.stringify(report, null, 2) : mergeCopiesSummary(report);
|
|
271
271
|
}
|
|
272
272
|
/** Reads a `drop-aliases --only` file: a JSON array of `{ entity, alias }`. */
|
|
273
|
-
|
|
273
|
+
function readAliasDrops(file) {
|
|
274
274
|
let parsed;
|
|
275
275
|
try {
|
|
276
276
|
parsed = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import type { Command } from 'commander';
|
|
2
2
|
import { type FrontmatterResult } from './core-policy-frontmatter.js';
|
|
3
|
-
export declare const POLICY_AGE_REPORT_ASSET = "core/scripts/policy-age-report.sh";
|
|
4
|
-
export declare const POLICY_RETIRE_ASSET = "core/scripts/policy-retire.sh";
|
|
5
|
-
export declare const POLICY_FRONTMATTER_ASSET = "core/scripts/read-policy-frontmatter.sh";
|
|
6
3
|
export type CorePolicyResult = {
|
|
7
4
|
code: number;
|
|
8
5
|
stdout: Buffer;
|
|
@@ -8,9 +8,9 @@ import { resolveLiveRoot, resolveBundledAsset } from '../utils/hq-roots.js';
|
|
|
8
8
|
import { runBundledScript } from '../utils/run-bundled-script.js';
|
|
9
9
|
import { describeCoreCommand } from './core-command-catalog.js';
|
|
10
10
|
import { runPolicyFrontmatter as runPolicyFrontmatterImpl } from './core-policy-frontmatter.js';
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
const POLICY_AGE_REPORT_ASSET = 'core/scripts/policy-age-report.sh';
|
|
12
|
+
const POLICY_RETIRE_ASSET = 'core/scripts/policy-retire.sh';
|
|
13
|
+
const POLICY_FRONTMATTER_ASSET = 'core/scripts/read-policy-frontmatter.sh';
|
|
14
14
|
const AGE_HELP = [
|
|
15
15
|
'# hq-core: public',
|
|
16
16
|
'# policy-age-report.sh — measure policy usage and flag retirement candidates',
|
package/dist/commands/files.js
CHANGED
|
@@ -511,7 +511,7 @@ export function registerFilesCommand(program) {
|
|
|
511
511
|
});
|
|
512
512
|
files
|
|
513
513
|
.command("delete <prefix>")
|
|
514
|
-
.description("Delete vault objects
|
|
514
|
+
.description("Delete vault objects by exact key or bounded prefix. A trailing slash expands recursively; use * for an explicit wildcard prefix. Always previews the exact count first; prompts for confirmation unless --yes. Use --personal to target your own personal vault instead of a company.")
|
|
515
515
|
.option("--dry-run", "List what WOULD be deleted without deleting anything")
|
|
516
516
|
.option("--personal", "Target your own personal vault instead of a company vault (mutually exclusive with --company)")
|
|
517
517
|
.option("-y, --yes", "Skip the confirmation prompt (for scripts)")
|
|
@@ -38,7 +38,7 @@ export function syncSearchIndex(hqRoot, embed, dependencies = defaults) {
|
|
|
38
38
|
catch {
|
|
39
39
|
/* capping is an optimisation; indexing is the job */
|
|
40
40
|
}
|
|
41
|
-
dependencies.runQmd(['update'], { cwd: hqRoot });
|
|
41
|
+
dependencies.runQmd(['update'], { cwd: hqRoot, entryPath: 'index-sync' });
|
|
42
42
|
};
|
|
43
43
|
const writeStderr = dependencies.writeStderr ?? ((text) => process.stderr.write(text));
|
|
44
44
|
const singleFlightEnabled = (dependencies.singleFlightEnabled ?? qmdReindexSingleFlightEnabled)();
|
|
@@ -288,13 +288,18 @@ export interface PendingApproval {
|
|
|
288
288
|
requestedByName?: string | null;
|
|
289
289
|
createdAt: string;
|
|
290
290
|
expiresAt: string;
|
|
291
|
+
/** Queue state is present on history rows; older servers may omit it. */
|
|
292
|
+
status?: string;
|
|
293
|
+
/** Owner-facing outcome projected by hq-pro for decided rows. */
|
|
294
|
+
outcome?: string;
|
|
291
295
|
}
|
|
292
296
|
/**
|
|
293
|
-
*
|
|
294
|
-
*
|
|
295
|
-
* that was approved/rejected/expired no longer appears here.
|
|
297
|
+
* Read open approvals from hq-pro's confirm-queue state (not the audit feed).
|
|
298
|
+
* Pass `includeHistory` to include recently decided rows alongside open ones.
|
|
296
299
|
*/
|
|
297
|
-
export declare function fetchPendingApprovals(token: string, companyUid: string
|
|
300
|
+
export declare function fetchPendingApprovals(token: string, companyUid: string, options?: {
|
|
301
|
+
includeHistory?: boolean;
|
|
302
|
+
}): Promise<PendingApproval[]>;
|
|
298
303
|
export declare function getConnectionAccess(token: string, companyUid: string, connectionId: string): Promise<ConnectionAccess>;
|
|
299
304
|
export declare function mutateConnectionAccess(token: string, companyUid: string, action: "grant" | "revoke", input: {
|
|
300
305
|
connectionId: string;
|
|
@@ -148,20 +148,23 @@ export async function setReadSafe(token, companyUid, connectionId, toolName, rea
|
|
|
148
148
|
await raiseForResponse(res, "Failed to update the tool's read-safe setting");
|
|
149
149
|
}
|
|
150
150
|
/**
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
* that was approved/rejected/expired no longer appears here.
|
|
151
|
+
* Read open approvals from hq-pro's confirm-queue state (not the audit feed).
|
|
152
|
+
* Pass `includeHistory` to include recently decided rows alongside open ones.
|
|
154
153
|
*/
|
|
155
|
-
export async function fetchPendingApprovals(token, companyUid) {
|
|
154
|
+
export async function fetchPendingApprovals(token, companyUid, options = {}) {
|
|
156
155
|
const res = await vaultApiFetch({
|
|
157
156
|
token,
|
|
158
157
|
path: "/v1/integrations/factory/pending",
|
|
159
|
-
query: {
|
|
158
|
+
query: {
|
|
159
|
+
companyUid,
|
|
160
|
+
...(options.includeHistory ? { include: "history" } : {}),
|
|
161
|
+
},
|
|
160
162
|
});
|
|
161
163
|
if (!res.ok)
|
|
162
164
|
await raiseForResponse(res, "Failed to list pending approvals");
|
|
163
165
|
const body = (await res.json());
|
|
164
|
-
|
|
166
|
+
const pending = body.pending ?? [];
|
|
167
|
+
return options.includeHistory ? [...pending, ...(body.history ?? [])] : pending;
|
|
165
168
|
}
|
|
166
169
|
export async function getConnectionAccess(token, companyUid, connectionId) {
|
|
167
170
|
const res = await vaultApiFetch({
|
|
@@ -562,6 +562,7 @@ export function registerManageCommands(integrations) {
|
|
|
562
562
|
.description("Calls that were queued for an owner's approval")
|
|
563
563
|
.option("--company <slug>", "Company slug, e.g. indigo")
|
|
564
564
|
.option("--json", "Machine-readable output")
|
|
565
|
+
.option("--history", "Include recently decided calls")
|
|
565
566
|
.action(async (opts) => {
|
|
566
567
|
const token = await ensureCognitoIdToken();
|
|
567
568
|
const companyUid = await getCompanyUid(token, opts.company);
|
|
@@ -570,7 +571,9 @@ export function registerManageCommands(integrations) {
|
|
|
570
571
|
// could only show that a call was once queued.
|
|
571
572
|
let pending;
|
|
572
573
|
try {
|
|
573
|
-
pending = await fetchPendingApprovals(token, companyUid
|
|
574
|
+
pending = await fetchPendingApprovals(token, companyUid, {
|
|
575
|
+
includeHistory: opts.history,
|
|
576
|
+
});
|
|
574
577
|
}
|
|
575
578
|
catch (err) {
|
|
576
579
|
// This CLI can ship ahead of the hq-pro deploy that adds the pending
|
|
@@ -586,14 +589,19 @@ export function registerManageCommands(integrations) {
|
|
|
586
589
|
return;
|
|
587
590
|
}
|
|
588
591
|
if (pending.length === 0) {
|
|
589
|
-
console.log("Nothing is waiting for approval.");
|
|
592
|
+
console.log(opts.history ? "No recent approval history." : "Nothing is waiting for approval.");
|
|
590
593
|
return;
|
|
591
594
|
}
|
|
592
595
|
for (const row of pending) {
|
|
593
596
|
const who = row.requestedByName ?? row.requestedBy;
|
|
594
597
|
const label = row.requestedTool ?? row.toolName;
|
|
595
598
|
console.log(`${chalk.bold(label)} ${chalk.dim(`${who} · ${row.createdAt}`)}`);
|
|
596
|
-
|
|
599
|
+
if (opts.history) {
|
|
600
|
+
console.log(chalk.dim(` ${row.queueId} · ${row.status ?? row.outcome ?? "status unavailable"}`));
|
|
601
|
+
}
|
|
602
|
+
else {
|
|
603
|
+
console.log(chalk.dim(` hq integrations approve ${row.queueId}${row.provider ? ` --provider ${bareProvider(row.provider)}` : ""}`));
|
|
604
|
+
}
|
|
597
605
|
}
|
|
598
606
|
});
|
|
599
607
|
integrations
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
* hq integrations tools --provider <p> What the connected app can do.
|
|
23
23
|
* hq integrations call <tool> --provider <p> --args '<json>'
|
|
24
24
|
* hq integrations pending Calls awaiting approval.
|
|
25
|
+
* hq integrations pending --history Recently decided calls, including status.
|
|
25
26
|
* hq integrations approve|reject <queueId> Decide a queued call.
|
|
26
27
|
*
|
|
27
28
|
* Govern and remove:
|
|
@@ -49,7 +50,14 @@
|
|
|
49
50
|
* `integrations-manage.ts`.
|
|
50
51
|
*/
|
|
51
52
|
import { Command } from "commander";
|
|
53
|
+
import { type PendingApproval } from "./integrations-api.js";
|
|
52
54
|
export { IntegrationsCliError, callGateway, fetchConnections, queuedOutcome, selectConnection, toolPrefixForProvider, unwrapGatewayResult, } from "./integrations-core.js";
|
|
53
55
|
export type { AdminConnection } from "./integrations-core.js";
|
|
56
|
+
/** Wait for a decided queue item without resending the approve request. */
|
|
57
|
+
export declare function waitForApprovalTerminal(token: string, companyUid: string, queueId: string, options?: {
|
|
58
|
+
timeoutMs?: number;
|
|
59
|
+
now?: () => number;
|
|
60
|
+
sleep?: (milliseconds: number) => Promise<void>;
|
|
61
|
+
}): Promise<PendingApproval | null>;
|
|
54
62
|
export declare function registerIntegrationsCommand(program: Command): void;
|
|
55
63
|
//# sourceMappingURL=integrations.d.ts.map
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
* hq integrations tools --provider <p> What the connected app can do.
|
|
23
23
|
* hq integrations call <tool> --provider <p> --args '<json>'
|
|
24
24
|
* hq integrations pending Calls awaiting approval.
|
|
25
|
+
* hq integrations pending --history Recently decided calls, including status.
|
|
25
26
|
* hq integrations approve|reject <queueId> Decide a queued call.
|
|
26
27
|
*
|
|
27
28
|
* Govern and remove:
|
|
@@ -57,6 +58,7 @@ import { IntegrationsCliError, bareProvider, callGateway, fetchConnections, isCl
|
|
|
57
58
|
import { registerConnectCommands } from "./integrations-connect.js";
|
|
58
59
|
import { registerImportCommands } from "./integrations-import.js";
|
|
59
60
|
import { registerManageCommands } from "./integrations-manage.js";
|
|
61
|
+
import { fetchPendingApprovals } from "./integrations-api.js";
|
|
60
62
|
export { IntegrationsCliError, callGateway, fetchConnections, queuedOutcome, selectConnection, toolPrefixForProvider, unwrapGatewayResult, } from "./integrations-core.js";
|
|
61
63
|
/**
|
|
62
64
|
* Gateway metadata is additive and may be absent when talking to an older
|
|
@@ -160,6 +162,55 @@ function reportRevokedConnection(connection, opts) {
|
|
|
160
162
|
}
|
|
161
163
|
return true;
|
|
162
164
|
}
|
|
165
|
+
const APPROVAL_POLL_TIMEOUT_MS = 5 * 60_000;
|
|
166
|
+
const APPROVAL_POLL_INITIAL_DELAY_MS = 1_000;
|
|
167
|
+
const APPROVAL_POLL_MAX_DELAY_MS = 15_000;
|
|
168
|
+
const APPROVAL_TERMINAL_STATUSES = new Set([
|
|
169
|
+
"completed",
|
|
170
|
+
"resumed",
|
|
171
|
+
"failed",
|
|
172
|
+
"write_unknown",
|
|
173
|
+
"rejected",
|
|
174
|
+
"expired",
|
|
175
|
+
]);
|
|
176
|
+
const APPROVAL_TERMINAL_OUTCOMES = new Set([
|
|
177
|
+
"ran",
|
|
178
|
+
"failed",
|
|
179
|
+
"unknown",
|
|
180
|
+
"turned_down",
|
|
181
|
+
"timed_out",
|
|
182
|
+
]);
|
|
183
|
+
function isTerminalApproval(row) {
|
|
184
|
+
return APPROVAL_TERMINAL_STATUSES.has(row.status ?? "") ||
|
|
185
|
+
APPROVAL_TERMINAL_OUTCOMES.has(row.outcome ?? "");
|
|
186
|
+
}
|
|
187
|
+
/** Wait for a decided queue item without resending the approve request. */
|
|
188
|
+
export async function waitForApprovalTerminal(token, companyUid, queueId, options = {}) {
|
|
189
|
+
const timeoutMs = options.timeoutMs ?? APPROVAL_POLL_TIMEOUT_MS;
|
|
190
|
+
const now = options.now ?? Date.now;
|
|
191
|
+
const sleep = options.sleep ?? ((milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)));
|
|
192
|
+
const deadline = now() + timeoutMs;
|
|
193
|
+
let delayMs = APPROVAL_POLL_INITIAL_DELAY_MS;
|
|
194
|
+
while (now() < deadline) {
|
|
195
|
+
const rows = await fetchPendingApprovals(token, companyUid, { includeHistory: true });
|
|
196
|
+
const row = rows.find((candidate) => candidate.queueId === queueId);
|
|
197
|
+
if (row && isTerminalApproval(row))
|
|
198
|
+
return row;
|
|
199
|
+
const remainingMs = deadline - now();
|
|
200
|
+
if (remainingMs <= 0)
|
|
201
|
+
break;
|
|
202
|
+
await sleep(Math.min(delayMs, remainingMs));
|
|
203
|
+
delayMs = Math.min(delayMs * 2, APPROVAL_POLL_MAX_DELAY_MS);
|
|
204
|
+
}
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
function isPendingApprovalResponse(status) {
|
|
208
|
+
return ["pending", "approved", "approved_pending_run", "in_progress"].includes(status ?? "");
|
|
209
|
+
}
|
|
210
|
+
function approvalOutcomeFailed(row) {
|
|
211
|
+
return ["failed", "write_unknown"].includes(row.status ?? "") ||
|
|
212
|
+
["failed", "unknown"].includes(row.outcome ?? "");
|
|
213
|
+
}
|
|
163
214
|
export function registerIntegrationsCommand(program) {
|
|
164
215
|
const integrations = program
|
|
165
216
|
.command("integrations")
|
|
@@ -347,6 +398,38 @@ export function registerIntegrationsCommand(program) {
|
|
|
347
398
|
raiseIfUpstreamUnavailable(res);
|
|
348
399
|
throw new IntegrationsCliError(body.error ?? `${decision} failed (HTTP ${res.status})`, { expected: isClientError(res.status) });
|
|
349
400
|
}
|
|
401
|
+
const pendingApprove = decision === "approve" &&
|
|
402
|
+
(res.status === 202 || isPendingApprovalResponse(body.status));
|
|
403
|
+
if (pendingApprove) {
|
|
404
|
+
const terminal = await waitForApprovalTerminal(token, companyUid, queueId);
|
|
405
|
+
const historyCommand = `hq integrations pending --history${opts.company ? ` --company ${opts.company}` : ""}`;
|
|
406
|
+
if (!terminal) {
|
|
407
|
+
if (opts.json) {
|
|
408
|
+
printJson({ ...body, status: "pending", pollTimedOut: true, checkCommand: historyCommand });
|
|
409
|
+
}
|
|
410
|
+
else {
|
|
411
|
+
console.log(chalk.yellow(`Approval is still running after 5 minutes. Check queue ${queueId} later with: ${historyCommand}`));
|
|
412
|
+
}
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
if (opts.json)
|
|
416
|
+
printJson(terminal);
|
|
417
|
+
else if (approvalOutcomeFailed(terminal)) {
|
|
418
|
+
const unknown = terminal.status === "write_unknown" || terminal.outcome === "unknown";
|
|
419
|
+
console.log(chalk.red(unknown
|
|
420
|
+
? `The provider outcome is unknown for ${queueId}. Check the provider before taking any action; ${historyCommand} shows the latest queue state.`
|
|
421
|
+
: `The approved call failed (${terminal.status ?? terminal.outcome}). See ${historyCommand} for its status.`));
|
|
422
|
+
}
|
|
423
|
+
else if (terminal.status === "resumed" || terminal.status === "completed" || terminal.outcome === "ran") {
|
|
424
|
+
console.log(chalk.green("Approved — the call completed."));
|
|
425
|
+
}
|
|
426
|
+
else {
|
|
427
|
+
console.log(chalk.yellow(`Approval ended with status ${terminal.status ?? terminal.outcome ?? "unknown"}.`));
|
|
428
|
+
}
|
|
429
|
+
if (approvalOutcomeFailed(terminal))
|
|
430
|
+
process.exitCode = 1;
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
350
433
|
if (opts.json) {
|
|
351
434
|
printJson(body);
|
|
352
435
|
return;
|
|
@@ -19,7 +19,6 @@ export interface ProjectMapping {
|
|
|
19
19
|
excludePrefix?: string;
|
|
20
20
|
}
|
|
21
21
|
export declare function mappingIncludesStory(mapping: ProjectMapping, storyId: string): boolean;
|
|
22
|
-
export declare function ambiguousProjectMappings(mappings: readonly ProjectMapping[]): Map<string, string[]>;
|
|
23
22
|
export interface ProjectReadResult {
|
|
24
23
|
stories: Array<Record<string, unknown>>;
|
|
25
24
|
mtime: number;
|
|
@@ -35,7 +34,6 @@ export declare function readProject(root: string, company: string, prdPath: stri
|
|
|
35
34
|
export declare function loadCompanyQuestionRecords(root: string, company: LaneCompanyRef, projectId?: string): QuestionRecord[];
|
|
36
35
|
export declare function readTeamInputs(root: string, company: LaneCompanyRef, projects: readonly ProjectMapping[], lanes: readonly LaneRecord[]): TrackerCheckTeam[];
|
|
37
36
|
export declare function pathForTrackerRecord(projectDirectory: string, kind: TrackerKind, id: string): string;
|
|
38
|
-
export declare function listTrackerRecords(projectDirectory: string, kind: TrackerKind): TrackerRecord[];
|
|
39
37
|
export interface TolerantTrackerReadIssue {
|
|
40
38
|
path: string;
|
|
41
39
|
field: string;
|
|
@@ -23,7 +23,7 @@ function mappingsOverlap(left, right) {
|
|
|
23
23
|
const commonPrefix = includes.sort((a, b) => b.length - a.length)[0] ?? "";
|
|
24
24
|
return ![left.excludePrefix, right.excludePrefix].some((excluded) => excluded !== undefined && commonPrefix.startsWith(excluded));
|
|
25
25
|
}
|
|
26
|
-
|
|
26
|
+
function ambiguousProjectMappings(mappings) {
|
|
27
27
|
const byProject = new Map();
|
|
28
28
|
for (const mapping of mappings)
|
|
29
29
|
byProject.set(mapping.projectId, [...(byProject.get(mapping.projectId) ?? []), mapping]);
|
|
@@ -641,7 +641,7 @@ function requiredRecordTimestamp(kind, record, projectDirectory, field, value) {
|
|
|
641
641
|
return parseTimestamp(value, field, pathForTrackerRecord(projectDirectory, kind, record.id));
|
|
642
642
|
return value;
|
|
643
643
|
}
|
|
644
|
-
|
|
644
|
+
function listTrackerRecords(projectDirectory, kind) {
|
|
645
645
|
try {
|
|
646
646
|
return createLocalTrackerStore(projectDirectory).list(kind);
|
|
647
647
|
}
|
package/dist/commands/lanes.d.ts
CHANGED
|
@@ -112,8 +112,6 @@ export declare function parseQuestionBody(raw: unknown): QuestionBody | null;
|
|
|
112
112
|
*/
|
|
113
113
|
export declare const ASK_BACKOFF_MS: readonly [5000, 15000];
|
|
114
114
|
export declare const ASK_ATTEMPTS: number;
|
|
115
|
-
/** Flip to true in the next release to make --priority mandatory. */
|
|
116
|
-
export declare const ASK_PRIORITY_REQUIRED = false;
|
|
117
115
|
export declare function casUpdateQuestion(hqRoot: string, questionId: string, updater: (current: QuestionRecord) => QuestionRecord): QuestionRecord;
|
|
118
116
|
export declare function casUpdateLane(hqRoot: string, laneId: string, updater: (current: LaneRecord) => LaneRecord): LaneRecord;
|
|
119
117
|
/**
|
|
@@ -128,12 +126,7 @@ export declare function composeWorkerBrief(worker: ResolvedLaneWorker, taskBrief
|
|
|
128
126
|
* and the foreground-only rule.
|
|
129
127
|
*/
|
|
130
128
|
export declare function composeGeneralBrief(taskBrief: string): string;
|
|
131
|
-
export declare function parseMaxRuntimeSeconds(raw: unknown, workerId: string): number | undefined;
|
|
132
|
-
export declare const PICKUP_TERMINAL_REASONS: readonly ["account_not_logged_in", "unknown_account", "account_disabled", "model_unsupported"];
|
|
133
129
|
export declare const DISPATCH_TERMINAL_REASON: "outpost_dispatch_failed";
|
|
134
|
-
export type PickupTerminalReason = (typeof PICKUP_TERMINAL_REASONS)[number];
|
|
135
|
-
/** Only explicit non-persistence evidence can prove a failed dispatch has no owner snapshot. */
|
|
136
|
-
export declare function isUndispatchedOutpostMirror(lane: LaneRecord): boolean;
|
|
137
130
|
/**
|
|
138
131
|
* The CAS updater a pickup failure applies. A lane that is already terminal
|
|
139
132
|
* for any reason (a cancel that landed after the caller's last read, an
|
|
@@ -149,9 +142,5 @@ export declare function pickupFailureUpdater(reasonRaw: string, details: Record<
|
|
|
149
142
|
export declare function applyOutpostAccountPickup(hqRoot: string, laneId: string, modelDeps?: KnownModelsDeps): LaneRecord;
|
|
150
143
|
export declare function cancelPendingQuestionsForLane(hqRoot: string, laneId: string, nowIso: string, operatorReason?: string): QuestionRecord[];
|
|
151
144
|
export declare function stopCommittedState(state: LaneRecord["state"]): "cancelled" | "killed";
|
|
152
|
-
/** Stderr notice that `stop` ends a lane for good, naming the resumable path. */
|
|
153
|
-
export declare function stopTerminalWarning(laneId: string): string;
|
|
154
|
-
/** Stderr notice when an interrupted lane has no session to resume from. */
|
|
155
|
-
export declare function interruptNotResumableWarning(laneId: string): string;
|
|
156
145
|
export declare function registerLanesCommand(parent: Command, deps?: LanesCommandDeps): Command;
|
|
157
146
|
//# sourceMappingURL=lanes.d.ts.map
|
package/dist/commands/lanes.js
CHANGED
|
@@ -2253,7 +2253,7 @@ function assertKnownOutpost(requested, known) {
|
|
|
2253
2253
|
export const ASK_BACKOFF_MS = [5_000, 15_000];
|
|
2254
2254
|
export const ASK_ATTEMPTS = ASK_BACKOFF_MS.length + 1;
|
|
2255
2255
|
/** Flip to true in the next release to make --priority mandatory. */
|
|
2256
|
-
|
|
2256
|
+
const ASK_PRIORITY_REQUIRED = false;
|
|
2257
2257
|
const ASK_PRIORITY_WARNING = "--priority will be required in the next release.";
|
|
2258
2258
|
const QUESTION_PRIORITIES = ["P0", "P1", "P2"];
|
|
2259
2259
|
function withCasRetry(fn, attempts = 5) {
|
|
@@ -2390,7 +2390,7 @@ export function composeGeneralBrief(taskBrief) {
|
|
|
2390
2390
|
"",
|
|
2391
2391
|
].join("\n");
|
|
2392
2392
|
}
|
|
2393
|
-
|
|
2393
|
+
function parseMaxRuntimeSeconds(raw, workerId) {
|
|
2394
2394
|
if (raw === undefined || raw === null || raw === "")
|
|
2395
2395
|
return undefined;
|
|
2396
2396
|
if (typeof raw === "number") {
|
|
@@ -2540,7 +2540,7 @@ async function storyBoardCheck(ctx, company, projectId, storyIds) {
|
|
|
2540
2540
|
throw err;
|
|
2541
2541
|
}
|
|
2542
2542
|
}
|
|
2543
|
-
|
|
2543
|
+
const PICKUP_TERMINAL_REASONS = [
|
|
2544
2544
|
"account_not_logged_in",
|
|
2545
2545
|
"unknown_account",
|
|
2546
2546
|
"account_disabled",
|
|
@@ -2548,7 +2548,7 @@ export const PICKUP_TERMINAL_REASONS = [
|
|
|
2548
2548
|
];
|
|
2549
2549
|
export const DISPATCH_TERMINAL_REASON = "outpost_dispatch_failed";
|
|
2550
2550
|
/** Only explicit non-persistence evidence can prove a failed dispatch has no owner snapshot. */
|
|
2551
|
-
|
|
2551
|
+
function isUndispatchedOutpostMirror(lane) {
|
|
2552
2552
|
return lane.host.kind === "outpost" &&
|
|
2553
2553
|
isTerminalLaneState(lane.state) &&
|
|
2554
2554
|
lane.terminal_reason === DISPATCH_TERMINAL_REASON &&
|
|
@@ -7302,7 +7302,7 @@ async function runStop(ctx, laneId, reason, quiet = false) {
|
|
|
7302
7302
|
});
|
|
7303
7303
|
}
|
|
7304
7304
|
/** Stderr notice that `stop` ends a lane for good, naming the resumable path. */
|
|
7305
|
-
|
|
7305
|
+
function stopTerminalWarning(laneId) {
|
|
7306
7306
|
return `hq lanes: warning: stop kills lane ${laneId} and it cannot be resumed. ` +
|
|
7307
7307
|
`To pause it and keep it resumable, use hq lanes interrupt ${laneId}.\n`;
|
|
7308
7308
|
}
|
|
@@ -7395,7 +7395,7 @@ function persistInterruptedSession(hqRoot, lane) {
|
|
|
7395
7395
|
: { ...current, provider_session_id: sessionId, provider_session_id_absent: undefined });
|
|
7396
7396
|
}
|
|
7397
7397
|
/** Stderr notice when an interrupted lane has no session to resume from. */
|
|
7398
|
-
|
|
7398
|
+
function interruptNotResumableWarning(laneId) {
|
|
7399
7399
|
return `hq lanes: warning: lane ${laneId} was interrupted before its provider reported a session id, ` +
|
|
7400
7400
|
`so hq lanes resume cannot continue it. Start a new lane for the remaining work.\n`;
|
|
7401
7401
|
}
|
|
@@ -74,8 +74,8 @@ export declare class ConfigParseError extends McpRegistrationError {
|
|
|
74
74
|
export declare class ConfigPermissionError extends McpRegistrationError {
|
|
75
75
|
readonly code: "ConfigPermissionError";
|
|
76
76
|
}
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
type CodexMcpCollisionOwner = 'hq' | 'foreign' | 'unknown';
|
|
78
|
+
type CodexMcpCollisionField = 'command' | 'args' | 'env' | 'url' | 'other';
|
|
79
79
|
/** Safe, value-free context for a Codex name collision reported to Sentry. */
|
|
80
80
|
export interface CodexMcpCollisionDiagnostics {
|
|
81
81
|
client: 'codex';
|
|
@@ -146,7 +146,7 @@ export declare function backupRoot(env: SafeWriteEnv): string;
|
|
|
146
146
|
/** `~/.hq/logs/mcp-registry.log` — the append-only JSONL audit log (mirrors backupRoot style). */
|
|
147
147
|
export declare function mcpRegistryLogPath(env: SafeWriteEnv): string;
|
|
148
148
|
/** The action recorded on an audit line. */
|
|
149
|
-
|
|
149
|
+
type AuditAction = 'register' | 'unregister';
|
|
150
150
|
/**
|
|
151
151
|
* The result recorded on an audit line:
|
|
152
152
|
* - `registered` — a register write that changed the file.
|
|
@@ -156,7 +156,7 @@ export type AuditAction = 'register' | 'unregister';
|
|
|
156
156
|
* foreign/unstamped entry left in place). PARTIAL is auditable.
|
|
157
157
|
* - `error` — the surface emit threw (the operation failed for this surface).
|
|
158
158
|
*/
|
|
159
|
-
|
|
159
|
+
type AuditResult = 'registered' | 'unregistered' | 'noop' | 'skipped' | 'error';
|
|
160
160
|
/**
|
|
161
161
|
* One append-only audit-log entry. EXACT shape (field order is the JSON key order):
|
|
162
162
|
* `{ ts, action, pack, server, transport, target, file, prevHash, newHash, result }`.
|
|
@@ -213,8 +213,6 @@ export declare function codexConfigPath(env: SafeWriteEnv): string;
|
|
|
213
213
|
export declare function isCodexInstalled(env: SafeWriteEnv): boolean;
|
|
214
214
|
/** `~/.claude.json` — the PINNED single Claude surface (top-level mcpServers). */
|
|
215
215
|
export declare function claudeConfigPath(env: SafeWriteEnv): string;
|
|
216
|
-
/** `~/.mcp.json` — the project-pinned Claude surface (a symlink on this host). */
|
|
217
|
-
export declare function mcpJsonPath(env: SafeWriteEnv): string;
|
|
218
216
|
/** Outcome of {@link readConfigDoc}: a parsed doc plus whether the file pre-existed. */
|
|
219
217
|
export interface ReadConfigResult<T> {
|
|
220
218
|
/** The parsed doc, or the `emptyDoc` when the file is absent/empty. */
|
|
@@ -284,10 +282,6 @@ export declare function readConfigDoc<T>(target: string, format: ConfigFormat<T>
|
|
|
284
282
|
* separate snippet block) cannot leak. The result never contains raw file content.
|
|
285
283
|
*/
|
|
286
284
|
export declare function sanitizeParserMessage(message: string): string;
|
|
287
|
-
/** Default stale-lock timeout — a lock older than this whose PID is dead is broken. */
|
|
288
|
-
export declare const DEFAULT_LOCK_STALE_MS = 30000;
|
|
289
|
-
/** Default total time to wait for a held lock before giving up. */
|
|
290
|
-
export declare const DEFAULT_LOCK_WAIT_MS = 10000;
|
|
291
285
|
/** A held advisory lock; release with {@link releaseLock} (idempotent). */
|
|
292
286
|
export interface ConfigLock {
|
|
293
287
|
/** The lockfile path (`~/.<basename>.hqlock`). */
|
|
@@ -752,7 +746,7 @@ export declare function mergeCodexServer(name: string, def: TomlTable): (doc: Co
|
|
|
752
746
|
*/
|
|
753
747
|
export declare function registerCodexServer(opts: RegisterServerOptions): RegisterSurfaceResult | RegisterSurfaceSkip;
|
|
754
748
|
/** Discriminates an un-registration outcome on one surface. */
|
|
755
|
-
|
|
749
|
+
type UnregisterOutcome =
|
|
756
750
|
/** The provenance-stamped entry was found and removed (file changed). */
|
|
757
751
|
'removed'
|
|
758
752
|
/** The entry was already absent / never installed — a clean idempotent no-op. */
|