@indigoai-us/hq-cli 5.311.0 → 5.311.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 +4 -0
- package/dist/commands/core-delegate-w1b.js +111 -14
- package/dist/commands/core-delegate.d.ts +9 -0
- package/dist/commands/core-delegate.js +44 -23
- package/dist/commands/integrations-core.d.ts +2 -0
- package/dist/commands/integrations-core.js +4 -0
- package/dist/commands/integrations.js +2 -2
- package/dist/lib/bot/runtime/windows.d.ts +2 -0
- package/dist/lib/bot/runtime/windows.js +1 -1
- package/dist/utils/run-bundled-script.d.ts +6 -0
- package/dist/utils/run-bundled-script.js +14 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [5.311.1] — 2026-10-01
|
|
6
|
+
- Preserve shell-compatible diagnostics when delegate pickup receives a malformed manifest.
|
|
7
|
+
- Print URLs returned by `hq integrations call` without redacting their paths or query values. Other integration JSON output keeps its existing URL redaction.
|
|
8
|
+
|
|
5
9
|
## [5.311.0] — 2026-10-01
|
|
6
10
|
- Add native `hq core hq-session` and `checkpoint-stop-gate` handlers on the fast route behind the default-off `core-native-session` flag, retaining bundled shell rollback companions.
|
|
7
11
|
|
|
@@ -6,7 +6,7 @@ import * as path from "node:path";
|
|
|
6
6
|
import { serializeJqJson } from "../lib/core-utils/jq-json-serializer.js";
|
|
7
7
|
import { matchingSecretPatterns } from "../lib/core-utils/secret-patterns.js";
|
|
8
8
|
import { describeCoreCommand } from "./core-command-catalog.js";
|
|
9
|
-
import { isRecord, jsonAt, jqFallback, jqRawPath, jqRawPaths, moveFile, readJsonFile, subprocess, writeJsonAtomically, } from "./core-delegate.js";
|
|
9
|
+
import { commandExists, isRecord, jsonAt, jqFallback, jqRawPath, jqRawPaths, moveFile, readJsonFile, subprocess, writeJsonAtomically, } from "./core-delegate.js";
|
|
10
10
|
import { resolveLiveRoot } from "../utils/hq-roots.js";
|
|
11
11
|
import { runBundledScript } from "../utils/run-bundled-script.js";
|
|
12
12
|
const FLAG = "core-native-delegate";
|
|
@@ -61,6 +61,15 @@ function jsonFile(file) {
|
|
|
61
61
|
return undefined;
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
|
+
function malformedJsonFile(file) {
|
|
65
|
+
try {
|
|
66
|
+
JSON.parse(fs.readFileSync(file, "utf8"));
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
return error instanceof SyntaxError;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
64
73
|
function jsonText(value) { return serializeJqJson(value, "pretty"); }
|
|
65
74
|
function checksum(file) {
|
|
66
75
|
return createHash("sha256").update(fs.readFileSync(file)).digest("hex");
|
|
@@ -125,7 +134,21 @@ function parseFlags(args, allowed) {
|
|
|
125
134
|
}
|
|
126
135
|
function usage(name) { return USAGE[name] ?? ""; }
|
|
127
136
|
function usageFailure(name) { err(usage(name)); return 1; }
|
|
128
|
-
function
|
|
137
|
+
function runShellFallback(name, args, context) {
|
|
138
|
+
const result = runBundledScript({
|
|
139
|
+
asset: ASSETS[name],
|
|
140
|
+
args,
|
|
141
|
+
cwd: context.cwd,
|
|
142
|
+
hqRoot: context.root ?? context.cwd,
|
|
143
|
+
captureOutput: true,
|
|
144
|
+
});
|
|
145
|
+
if (result.stdout?.length)
|
|
146
|
+
process.stdout.write(result.stdout);
|
|
147
|
+
if (result.stderr?.length)
|
|
148
|
+
process.stderr.write(result.stderr);
|
|
149
|
+
return result.code;
|
|
150
|
+
}
|
|
151
|
+
function runBundle(args, context, onMalformedWhoami) {
|
|
129
152
|
const root = context.root ?? context.cwd;
|
|
130
153
|
if (args[0] !== "build")
|
|
131
154
|
return usageFailure("bundle");
|
|
@@ -175,7 +198,9 @@ function runBundle(args, context) {
|
|
|
175
198
|
try {
|
|
176
199
|
identity = JSON.parse(who.stdout ?? "");
|
|
177
200
|
}
|
|
178
|
-
catch {
|
|
201
|
+
catch (error) {
|
|
202
|
+
if (error instanceof SyntaxError)
|
|
203
|
+
return onMalformedWhoami ? onMalformedWhoami() : runShellFallback("bundle", args, context);
|
|
179
204
|
identity = undefined;
|
|
180
205
|
}
|
|
181
206
|
const identityValue = isRecord(identity) ? identity : {};
|
|
@@ -291,6 +316,32 @@ function runBundle(args, context) {
|
|
|
291
316
|
function runHq(args, context, stdout = "capture") {
|
|
292
317
|
return subprocess("hq", args, context.cwd, { stdout });
|
|
293
318
|
}
|
|
319
|
+
function commandSubstitutionBytes(value) {
|
|
320
|
+
const bytes = value.stdoutBytes ?? Buffer.from(value.stdout ?? "");
|
|
321
|
+
let end = bytes.length;
|
|
322
|
+
while (end > 0 && bytes[end - 1] === 0x0a)
|
|
323
|
+
end -= 1;
|
|
324
|
+
return bytes.subarray(0, end);
|
|
325
|
+
}
|
|
326
|
+
function emitJqParseDiagnostic(input, args, context) {
|
|
327
|
+
if (!commandExists("jq", context.cwd))
|
|
328
|
+
return;
|
|
329
|
+
const result = subprocess("jq", args, context.cwd, { input, stdout: "ignore", stderr: "pipe" });
|
|
330
|
+
const diagnostic = result.stderrBytes ?? Buffer.from(result.stderr ?? "");
|
|
331
|
+
if (diagnostic.length)
|
|
332
|
+
err(diagnostic);
|
|
333
|
+
}
|
|
334
|
+
const RESOLVE_FOUND_FILTER = '.status == "found" and (.person.personUid | type == "string") and (.person.companyUid | type == "string")';
|
|
335
|
+
const RESOLVE_NOT_FOUND_FILTER = '.status == "not_found"';
|
|
336
|
+
const ACL_FILTER = `
|
|
337
|
+
.schemaVersion == 1 and
|
|
338
|
+
(
|
|
339
|
+
.prefix == $prefix or
|
|
340
|
+
(($prefix | endswith("/*")) and .prefix == ($prefix | sub("\\\\*$"; "")))
|
|
341
|
+
) and
|
|
342
|
+
(.companyUid | type == "string") and ($company == "" or .companyUid == $company) and
|
|
343
|
+
(.direct | type == "array") and (.exists | type == "boolean")
|
|
344
|
+
`;
|
|
294
345
|
function runGrant(args, context) {
|
|
295
346
|
const parsed = parseFlags(args, ["--manifest", "--yes"]);
|
|
296
347
|
if (parsed.help) {
|
|
@@ -397,7 +448,12 @@ function runGrant(args, context) {
|
|
|
397
448
|
try {
|
|
398
449
|
response = JSON.parse(resolved.stdout ?? "");
|
|
399
450
|
}
|
|
400
|
-
catch {
|
|
451
|
+
catch (error) {
|
|
452
|
+
if (error instanceof SyntaxError) {
|
|
453
|
+
const input = commandSubstitutionBytes(resolved);
|
|
454
|
+
emitJqParseDiagnostic(input, ["-e", RESOLVE_FOUND_FILTER], context);
|
|
455
|
+
emitJqParseDiagnostic(input, ["-e", RESOLVE_NOT_FOUND_FILTER], context);
|
|
456
|
+
}
|
|
401
457
|
response = undefined;
|
|
402
458
|
}
|
|
403
459
|
if (isRecord(response) && response.status === "found" && isRecord(response.person) && typeof response.person.personUid === "string" && typeof response.person.companyUid === "string") {
|
|
@@ -426,7 +482,11 @@ function runGrant(args, context) {
|
|
|
426
482
|
try {
|
|
427
483
|
value = JSON.parse(response.stdout ?? "");
|
|
428
484
|
}
|
|
429
|
-
catch {
|
|
485
|
+
catch (error) {
|
|
486
|
+
if (error instanceof SyntaxError) {
|
|
487
|
+
const input = commandSubstitutionBytes(response);
|
|
488
|
+
emitJqParseDiagnostic(input, ["-e", "--arg", "prefix", prefix, "--arg", "company", expectedCompany, ACL_FILTER], context);
|
|
489
|
+
}
|
|
430
490
|
value = undefined;
|
|
431
491
|
}
|
|
432
492
|
const rawPrefix = isRecord(value) ? value.prefix : undefined;
|
|
@@ -501,6 +561,22 @@ function runPublish(args, context) {
|
|
|
501
561
|
if (args[0] !== "--manifest" || !args[1] || !fs.existsSync(args[1]) || !fs.statSync(args[1]).isFile())
|
|
502
562
|
return fail("publish", "usage: --manifest <path>");
|
|
503
563
|
const manifestPath = args[1];
|
|
564
|
+
if (malformedJsonFile(manifestPath))
|
|
565
|
+
return runShellFallback("publish", args, context);
|
|
566
|
+
let preflightManifest;
|
|
567
|
+
try {
|
|
568
|
+
preflightManifest = readJsonFile(manifestPath);
|
|
569
|
+
}
|
|
570
|
+
catch {
|
|
571
|
+
preflightManifest = undefined;
|
|
572
|
+
}
|
|
573
|
+
if (isRecord(preflightManifest) && jqRawPath(preflightManifest, "mode") === "transfer") {
|
|
574
|
+
const root = context.root ?? context.cwd;
|
|
575
|
+
const prd = path.join(root, "companies", jqRawPath(preflightManifest, "company"), "projects", jqRawPath(preflightManifest, "project.name"), "prd.json");
|
|
576
|
+
if (fs.existsSync(prd) && fs.statSync(prd).isFile() && malformedJsonFile(prd)) {
|
|
577
|
+
return runShellFallback("publish", args, context);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
504
580
|
const lock = `${manifestPath}.send-lock`;
|
|
505
581
|
const lockHeld = args[2] === "--lock-held";
|
|
506
582
|
let ownLock = false;
|
|
@@ -658,6 +734,12 @@ function runSend(args, context) {
|
|
|
658
734
|
const manifest = jsonFile(manifestPath);
|
|
659
735
|
if (!manifest)
|
|
660
736
|
return fail("send", `manifest is not valid JSON: ${manifestPath}`);
|
|
737
|
+
if (send) {
|
|
738
|
+
const publishedPath = path.join(context.root ?? context.cwd, jqRawPath(manifest, "publication.manifestPath"));
|
|
739
|
+
if (fs.existsSync(publishedPath) && fs.statSync(publishedPath).isFile() && malformedJsonFile(publishedPath)) {
|
|
740
|
+
return runShellFallback("send", args, context);
|
|
741
|
+
}
|
|
742
|
+
}
|
|
661
743
|
const lock = `${manifestPath}.send-lock`;
|
|
662
744
|
if (send) {
|
|
663
745
|
try {
|
|
@@ -819,14 +901,14 @@ function sleepNative(milliseconds) {
|
|
|
819
901
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, milliseconds);
|
|
820
902
|
}
|
|
821
903
|
function captureRun(run) {
|
|
822
|
-
|
|
823
|
-
|
|
904
|
+
const stdout = [];
|
|
905
|
+
const stderr = [];
|
|
824
906
|
const stdoutWrite = process.stdout.write.bind(process.stdout);
|
|
825
907
|
const stderrWrite = process.stderr.write.bind(process.stderr);
|
|
826
|
-
process.stdout.write = ((chunk) => { stdout
|
|
827
|
-
process.stderr.write = ((chunk) => { stderr
|
|
908
|
+
process.stdout.write = ((chunk) => { stdout.push(typeof chunk === "string" ? Buffer.from(chunk) : Buffer.from(chunk)); return true; });
|
|
909
|
+
process.stderr.write = ((chunk) => { stderr.push(typeof chunk === "string" ? Buffer.from(chunk) : Buffer.from(chunk)); return true; });
|
|
828
910
|
try {
|
|
829
|
-
return { code: run(), stdout, stderr };
|
|
911
|
+
return { code: run(), stdout: Buffer.concat(stdout), stderr: Buffer.concat(stderr) };
|
|
830
912
|
}
|
|
831
913
|
finally {
|
|
832
914
|
process.stdout.write = stdoutWrite;
|
|
@@ -854,16 +936,29 @@ function runVerify(args, context) {
|
|
|
854
936
|
return fail("verify", "--dry-run requires --project");
|
|
855
937
|
if (!recipient)
|
|
856
938
|
return fail("verify", "--dry-run requires --to");
|
|
857
|
-
|
|
858
|
-
|
|
939
|
+
let malformedWhoamiFallbackCode;
|
|
940
|
+
const plan = captureRun(() => runBundle(["build", "--company", company, "--project", project, "--to", recipient, "--mode", mode, "--dry-run"], context, () => {
|
|
941
|
+
malformedWhoamiFallbackCode = runShellFallback("verify", args, context);
|
|
942
|
+
return malformedWhoamiFallbackCode;
|
|
943
|
+
}));
|
|
944
|
+
if (malformedWhoamiFallbackCode !== undefined) {
|
|
945
|
+
if (plan.stdout.length)
|
|
946
|
+
out(plan.stdout);
|
|
947
|
+
if (plan.stderr.length)
|
|
948
|
+
err(plan.stderr);
|
|
949
|
+
return malformedWhoamiFallbackCode;
|
|
950
|
+
}
|
|
951
|
+
if (plan.stderr.length)
|
|
859
952
|
err(plan.stderr);
|
|
860
953
|
if (plan.code !== 0)
|
|
861
954
|
return fail("verify", "could not derive the delegation plan (see builder error above)");
|
|
862
955
|
let manifest;
|
|
863
956
|
try {
|
|
864
|
-
manifest = JSON.parse(plan.stdout);
|
|
957
|
+
manifest = JSON.parse(plan.stdout.toString("utf8"));
|
|
865
958
|
}
|
|
866
|
-
catch {
|
|
959
|
+
catch (error) {
|
|
960
|
+
if (error instanceof SyntaxError)
|
|
961
|
+
return runShellFallback("verify", args, context);
|
|
867
962
|
return fail("verify", "could not derive the delegation plan (see builder error above)");
|
|
868
963
|
}
|
|
869
964
|
const repoRelative = jqRawPath(manifest, "repo.path");
|
|
@@ -893,6 +988,8 @@ function runVerify(args, context) {
|
|
|
893
988
|
return fail("verify", "--manifest is required (or --dry-run with --company/--project/--to)");
|
|
894
989
|
if (!fs.existsSync(manifestPath) || !fs.statSync(manifestPath).isFile())
|
|
895
990
|
return fail("verify", `manifest not found: ${manifestPath}`);
|
|
991
|
+
if (malformedJsonFile(manifestPath))
|
|
992
|
+
return runShellFallback("verify", args, context);
|
|
896
993
|
const manifest = jsonFile(manifestPath);
|
|
897
994
|
if (!manifest)
|
|
898
995
|
return fail("verify", `jq: error: ${manifestPath}: Invalid JSON`);
|
|
@@ -9,12 +9,21 @@ type CommandResult = {
|
|
|
9
9
|
code: number;
|
|
10
10
|
stdout?: string;
|
|
11
11
|
stderr?: string;
|
|
12
|
+
stdoutBytes?: Buffer;
|
|
13
|
+
stderrBytes?: Buffer;
|
|
12
14
|
};
|
|
15
|
+
export type CommandExistsOptions = {
|
|
16
|
+
platform?: NodeJS.Platform;
|
|
17
|
+
env?: NodeJS.ProcessEnv;
|
|
18
|
+
existsSync?: (candidate: string) => boolean;
|
|
19
|
+
};
|
|
20
|
+
export declare function commandExists(name: string, cwd?: string, options?: CommandExistsOptions): boolean;
|
|
13
21
|
export declare function subprocess(command: string, args: string[], cwd: string, options?: {
|
|
14
22
|
input?: Buffer | string;
|
|
15
23
|
stdinFd?: number;
|
|
16
24
|
stderr?: "inherit" | "ignore" | "pipe";
|
|
17
25
|
stdout?: "capture" | "inherit" | "ignore";
|
|
26
|
+
maxBuffer?: number;
|
|
18
27
|
}): CommandResult;
|
|
19
28
|
type JsonRecord = Record<string, unknown>;
|
|
20
29
|
export declare function isRecord(value: unknown): value is JsonRecord;
|
|
@@ -4,6 +4,7 @@ import * as fs from "node:fs";
|
|
|
4
4
|
import * as os from "node:os";
|
|
5
5
|
import * as path from "node:path";
|
|
6
6
|
import { serializeJqJson } from "../lib/core-utils/jq-json-serializer.js";
|
|
7
|
+
import { resolveWindowsExecutable } from "../lib/bot/runtime/windows.js";
|
|
7
8
|
import { describeCoreCommand } from "./core-command-catalog.js";
|
|
8
9
|
import { resolveLiveRoot } from "../utils/hq-roots.js";
|
|
9
10
|
import { runBundledScript } from "../utils/run-bundled-script.js";
|
|
@@ -28,6 +29,20 @@ function writeOut(value) {
|
|
|
28
29
|
function writeErr(value) {
|
|
29
30
|
process.stderr.write(value);
|
|
30
31
|
}
|
|
32
|
+
function runCapturedShell(asset, args, context) {
|
|
33
|
+
const result = runBundledScript({
|
|
34
|
+
asset,
|
|
35
|
+
args,
|
|
36
|
+
cwd: context.cwd,
|
|
37
|
+
...(context.root ? { hqRoot: context.root } : {}),
|
|
38
|
+
captureOutput: true,
|
|
39
|
+
});
|
|
40
|
+
if (result.stdout?.length)
|
|
41
|
+
process.stdout.write(result.stdout);
|
|
42
|
+
if (result.stderr?.length)
|
|
43
|
+
process.stderr.write(result.stderr);
|
|
44
|
+
return result.code;
|
|
45
|
+
}
|
|
31
46
|
const USAGE_TEXT = {
|
|
32
47
|
resolve: "hq-delegate-resolve.sh — resolve a delegation recipient to a confirmed\nprincipal (person email / prs_ uid, or fleet-agent agt_ uid). Read-only.\n\nUsage:\n core/scripts/hq-delegate-resolve.sh --company <slug> --to <token>\n\nOutput (stdout, JSON): {kind, principal, displayName, source}\n kind — \"person\" | \"agent\"\n principal — confirmed email, prs_… or agt_… uid\n source — \"verbatim\" | \"people-resolve\" | \"agents-list\"\n\nExit codes:\n 0 — resolved\n 1 — usage error (missing/invalid arguments)\n 3 — ambiguous: candidate list printed to stdout as JSON\n {status:\"ambiguous\", matches:[…]} — caller must present a picker\n 4 — not found (or found with no email): plain message on stderr\n\nTenancy: every lookup is scoped with an explicit --company. This helper\nnever enumerates or falls back across companies — single-company by\nconstruction, matching the /dm skill's resolution contract.\n\nFast path: a token containing \"@\" or starting with prs_ / agt_ passes\nthrough verbatim with NO lookup, preserving existing invocations exactly.\n",
|
|
33
48
|
// Preserve the companion usage function’s historical raw source lines.
|
|
@@ -39,8 +54,20 @@ const USAGE_TEXT = {
|
|
|
39
54
|
function usage(name) {
|
|
40
55
|
return USAGE_TEXT[name];
|
|
41
56
|
}
|
|
42
|
-
function commandPath(name, cwd = process.cwd()) {
|
|
43
|
-
const
|
|
57
|
+
function commandPath(name, cwd = process.cwd(), options = {}) {
|
|
58
|
+
const env = options.env ?? process.env;
|
|
59
|
+
if ((options.platform ?? process.platform) === "win32") {
|
|
60
|
+
const existsSync = options.existsSync ?? ((candidate) => {
|
|
61
|
+
try {
|
|
62
|
+
return fs.statSync(candidate).isFile();
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
return resolveWindowsExecutable(name, env, existsSync, cwd) ?? undefined;
|
|
69
|
+
}
|
|
70
|
+
const directories = (env.PATH ?? "").split(path.delimiter);
|
|
44
71
|
for (const directory of directories) {
|
|
45
72
|
const candidate = path.resolve(cwd, directory || ".", name);
|
|
46
73
|
try {
|
|
@@ -56,24 +83,30 @@ function commandPath(name, cwd = process.cwd()) {
|
|
|
56
83
|
}
|
|
57
84
|
return undefined;
|
|
58
85
|
}
|
|
59
|
-
function commandExists(name, cwd = process.cwd()) {
|
|
60
|
-
return commandPath(name, cwd) !== undefined;
|
|
86
|
+
export function commandExists(name, cwd = process.cwd(), options = {}) {
|
|
87
|
+
return commandPath(name, cwd, options) !== undefined;
|
|
61
88
|
}
|
|
62
89
|
export function subprocess(command, args, cwd, options = {}) {
|
|
63
90
|
const result = spawnSync(command, args, {
|
|
64
91
|
cwd,
|
|
65
92
|
env: process.env,
|
|
66
93
|
input: options.input,
|
|
67
|
-
|
|
68
|
-
|
|
94
|
+
maxBuffer: options.maxBuffer,
|
|
95
|
+
encoding: "buffer",
|
|
96
|
+
stdio: [options.input === undefined ? options.stdinFd ?? "inherit" : "pipe", options.stdout === "inherit" ? "inherit" : options.stdout === "ignore" ? "ignore" : "pipe", options.stderr === "pipe" ? "pipe" : options.stderr ?? "inherit"],
|
|
69
97
|
});
|
|
70
98
|
if (result.error) {
|
|
71
|
-
|
|
99
|
+
const stderr = Buffer.from(result.error.message);
|
|
100
|
+
return { code: 127, stderr: stderr.toString("utf8"), stderrBytes: stderr, stdout: "", stdoutBytes: Buffer.alloc(0) };
|
|
72
101
|
}
|
|
102
|
+
const stdoutBytes = Buffer.isBuffer(result.stdout) ? result.stdout : Buffer.alloc(0);
|
|
103
|
+
const stderrBytes = Buffer.isBuffer(result.stderr) ? result.stderr : Buffer.alloc(0);
|
|
73
104
|
return {
|
|
74
105
|
code: result.status ?? (result.signal ? 128 : 1),
|
|
75
|
-
stdout:
|
|
76
|
-
stderr:
|
|
106
|
+
stdout: stdoutBytes.toString("utf8"),
|
|
107
|
+
stderr: stderrBytes.toString("utf8"),
|
|
108
|
+
stdoutBytes,
|
|
109
|
+
stderrBytes,
|
|
77
110
|
};
|
|
78
111
|
}
|
|
79
112
|
export function isRecord(value) {
|
|
@@ -82,15 +115,6 @@ export function isRecord(value) {
|
|
|
82
115
|
export function readJsonText(text) {
|
|
83
116
|
return JSON.parse(text);
|
|
84
117
|
}
|
|
85
|
-
function jqParseDiagnostic(text) {
|
|
86
|
-
const bareToken = /^\s*([A-Za-z][A-Za-z0-9_-]*)\s*$/.exec(text);
|
|
87
|
-
if (bareToken && !["null", "true", "false"].includes(bareToken[1] ?? "")) {
|
|
88
|
-
const line = text.split("\n").length;
|
|
89
|
-
const column = Buffer.byteLength(text.slice(text.lastIndexOf("\n") + 1));
|
|
90
|
-
return `jq: parse error: Invalid numeric literal at line ${line}, column ${column}\n`;
|
|
91
|
-
}
|
|
92
|
-
return "jq: parse error: Invalid JSON text\n";
|
|
93
|
-
}
|
|
94
118
|
export function readJsonFile(file) {
|
|
95
119
|
return readJsonText(fs.readFileSync(file, "utf8"));
|
|
96
120
|
}
|
|
@@ -373,11 +397,8 @@ function runPickup(args, context) {
|
|
|
373
397
|
document = readJsonFile(file);
|
|
374
398
|
}
|
|
375
399
|
catch (error) {
|
|
376
|
-
if (error instanceof SyntaxError)
|
|
377
|
-
|
|
378
|
-
for (let index = 0; index < 4; index += 1)
|
|
379
|
-
writeErr(diagnostic);
|
|
380
|
-
}
|
|
400
|
+
if (error instanceof SyntaxError)
|
|
401
|
+
return runCapturedShell(SCRIPT_ASSETS.pickup, args, context);
|
|
381
402
|
document = {};
|
|
382
403
|
}
|
|
383
404
|
const status = jqRawPath(document, "status");
|
|
@@ -272,6 +272,8 @@ interface QueuedOutcome {
|
|
|
272
272
|
}
|
|
273
273
|
export declare function queuedOutcome(payload: unknown): QueuedOutcome | null;
|
|
274
274
|
export declare function printJson(value: unknown): void;
|
|
275
|
+
/** Print the successful payload from `hq integrations call` without altering URLs. */
|
|
276
|
+
export declare function printIntegrationCallResultJson(value: unknown): void;
|
|
275
277
|
/**
|
|
276
278
|
* Print why a connection is unusable and how to repair it, directly beneath its
|
|
277
279
|
* status line.
|
|
@@ -485,6 +485,10 @@ export function queuedOutcome(payload) {
|
|
|
485
485
|
export function printJson(value) {
|
|
486
486
|
console.log(JSON.stringify(redactSensitiveUrls(value), null, 2));
|
|
487
487
|
}
|
|
488
|
+
/** Print the successful payload from `hq integrations call` without altering URLs. */
|
|
489
|
+
export function printIntegrationCallResultJson(value) {
|
|
490
|
+
console.log(JSON.stringify(value, null, 2));
|
|
491
|
+
}
|
|
488
492
|
/**
|
|
489
493
|
* Print why a connection is unusable and how to repair it, directly beneath its
|
|
490
494
|
* status line.
|
|
@@ -54,7 +54,7 @@ import chalk from "chalk";
|
|
|
54
54
|
import { ensureCognitoIdToken } from "../utils/cognito-session.js";
|
|
55
55
|
import { getCompanyUid, vaultApiFetch } from "../utils/vault-api.js";
|
|
56
56
|
import { redactSensitiveUrl, redactSensitiveUrls, redactUrlsInText } from "../utils/redact-url.js";
|
|
57
|
-
import { IntegrationsCliError, bareProvider, callGateway, fetchConnections, isClientError, printHealthNotice, printJson, raiseIfUnauthorized, raiseIfUpstreamUnavailable, gatewayResultIsError, selectConnection, revokedConnectionDetails, toolPrefixForProvider, unwrapGatewayResult, queuedOutcome, } from "./integrations-core.js";
|
|
57
|
+
import { IntegrationsCliError, bareProvider, callGateway, fetchConnections, isClientError, printHealthNotice, printJson, printIntegrationCallResultJson, raiseIfUnauthorized, raiseIfUpstreamUnavailable, gatewayResultIsError, selectConnection, revokedConnectionDetails, toolPrefixForProvider, unwrapGatewayResult, queuedOutcome, } from "./integrations-core.js";
|
|
58
58
|
import { registerConnectCommands } from "./integrations-connect.js";
|
|
59
59
|
import { registerImportCommands } from "./integrations-import.js";
|
|
60
60
|
import { registerManageCommands } from "./integrations-manage.js";
|
|
@@ -367,7 +367,7 @@ export function registerIntegrationsCommand(program) {
|
|
|
367
367
|
// parsing output. The gateway double-wraps the provider result as a
|
|
368
368
|
// text-content string, so `isError` lands on the UNWRAPPED payload, not
|
|
369
369
|
// on the raw envelope; check both levels so either shape is caught.
|
|
370
|
-
|
|
370
|
+
printIntegrationCallResultJson(payload);
|
|
371
371
|
if (gatewayResultIsError(payload) || gatewayResultIsError(message.result)) {
|
|
372
372
|
process.exitCode = 1;
|
|
373
373
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { type SpawnPlan } from "../../../utils/windows-spawn.js";
|
|
2
2
|
type ExistsSync = (candidate: string) => boolean;
|
|
3
|
+
/** Resolve an npm-installed provider command using Windows PATH and PATHEXT. */
|
|
4
|
+
export declare function resolveWindowsExecutable(name: string, env: NodeJS.ProcessEnv, existsSync?: ExistsSync, cwd?: string): string | null;
|
|
3
5
|
/** Preserve non-Windows lookup. Windows returns the first PATH/PATHEXT match. */
|
|
4
6
|
export declare function resolveRuntimeBinary(name: string, env: NodeJS.ProcessEnv, platform?: NodeJS.Platform, existsSync?: ExistsSync, cwd?: string): string;
|
|
5
7
|
/** Directly spawn native files; pass only batch shims through cmd.exe. */
|
|
@@ -8,7 +8,7 @@ function envValue(env, name) {
|
|
|
8
8
|
return key ? env[key] : undefined;
|
|
9
9
|
}
|
|
10
10
|
/** Resolve an npm-installed provider command using Windows PATH and PATHEXT. */
|
|
11
|
-
function resolveWindowsExecutable(name, env, existsSync = fs.existsSync, cwd = process.cwd()) {
|
|
11
|
+
export function resolveWindowsExecutable(name, env, existsSync = fs.existsSync, cwd = process.cwd()) {
|
|
12
12
|
const pathValue = envValue(env, "PATH") ?? "";
|
|
13
13
|
const rawExtensions = (envValue(env, "PATHEXT") || DEFAULT_PATHEXT)
|
|
14
14
|
.split(";")
|
|
@@ -50,10 +50,16 @@ export type RunBundledScriptOptions = {
|
|
|
50
50
|
hqRoot?: string;
|
|
51
51
|
/** Extra environment for the child, applied last. */
|
|
52
52
|
env?: Record<string, string | undefined>;
|
|
53
|
+
/** Capture stdout and stderr as bytes instead of inheriting those streams. */
|
|
54
|
+
captureOutput?: boolean;
|
|
53
55
|
};
|
|
54
56
|
export type RunBundledScriptResult = {
|
|
55
57
|
/** The child's exit code, or 128+signal if it was terminated by one. */
|
|
56
58
|
code: number;
|
|
59
|
+
/** Present when captureOutput is true. */
|
|
60
|
+
stdout?: Buffer;
|
|
61
|
+
/** Present when captureOutput is true. */
|
|
62
|
+
stderr?: Buffer;
|
|
57
63
|
};
|
|
58
64
|
/**
|
|
59
65
|
* Execute a bundled scaffold script and return its exit code.
|
|
@@ -155,7 +155,8 @@ export function runBundledScript(opts) {
|
|
|
155
155
|
const result = spawnSync(executable, [script, ...(opts.args ?? [])], {
|
|
156
156
|
cwd: opts.cwd,
|
|
157
157
|
env,
|
|
158
|
-
stdio: "inherit",
|
|
158
|
+
stdio: opts.captureOutput ? ["inherit", "pipe", "pipe"] : "inherit",
|
|
159
|
+
...(opts.captureOutput ? { maxBuffer: Number.MAX_SAFE_INTEGER } : {}),
|
|
159
160
|
});
|
|
160
161
|
if (result.error) {
|
|
161
162
|
const spawnError = result.error;
|
|
@@ -166,6 +167,7 @@ export function runBundledScript(opts) {
|
|
|
166
167
|
}
|
|
167
168
|
throw new Error(`Failed to run bundled script ${opts.asset}: ${result.error.message}`);
|
|
168
169
|
}
|
|
170
|
+
let code;
|
|
169
171
|
if (result.signal) {
|
|
170
172
|
// Conventional shell encoding, so a caller checking for termination sees it.
|
|
171
173
|
const signals = {
|
|
@@ -175,8 +177,17 @@ export function runBundledScript(opts) {
|
|
|
175
177
|
SIGKILL: 9,
|
|
176
178
|
SIGTERM: 15,
|
|
177
179
|
};
|
|
178
|
-
|
|
180
|
+
code = 128 + (signals[result.signal] ?? 0);
|
|
179
181
|
}
|
|
180
|
-
|
|
182
|
+
else {
|
|
183
|
+
code = result.status ?? 0;
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
code,
|
|
187
|
+
...(opts.captureOutput ? {
|
|
188
|
+
stdout: result.stdout ?? Buffer.alloc(0),
|
|
189
|
+
stderr: result.stderr ?? Buffer.alloc(0),
|
|
190
|
+
} : {}),
|
|
191
|
+
};
|
|
181
192
|
}
|
|
182
193
|
//# sourceMappingURL=run-bundled-script.js.map
|