@blogic-cz/agent-tools 1.7.5 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/types.d.ts +1 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/credential-guard/index.d.ts.map +1 -1
- package/dist/db-tool/service.d.ts.map +1 -1
- package/dist/gh-tool/branch.d.ts +2 -3
- package/dist/gh-tool/branch.d.ts.map +1 -1
- package/dist/gh-tool/environment.d.ts +1 -1
- package/dist/gh-tool/environment.d.ts.map +1 -1
- package/dist/gh-tool/gist.d.ts +8 -28
- package/dist/gh-tool/gist.d.ts.map +1 -1
- package/dist/gh-tool/issue/commands.d.ts +11 -9
- package/dist/gh-tool/issue/commands.d.ts.map +1 -1
- package/dist/gh-tool/issue/core.d.ts +116 -9
- package/dist/gh-tool/issue/core.d.ts.map +1 -1
- package/dist/gh-tool/issue/triage.d.ts +6 -6
- package/dist/gh-tool/issue/triage.d.ts.map +1 -1
- package/dist/gh-tool/label.d.ts +20 -0
- package/dist/gh-tool/label.d.ts.map +1 -0
- package/dist/gh-tool/pr/commands.d.ts +53 -46
- package/dist/gh-tool/pr/commands.d.ts.map +1 -1
- package/dist/gh-tool/pr/core.d.ts +156 -86
- package/dist/gh-tool/pr/core.d.ts.map +1 -1
- package/dist/gh-tool/pr/review.d.ts +16 -16
- package/dist/gh-tool/pr/review.d.ts.map +1 -1
- package/dist/gh-tool/pr/snapshot.d.ts +9 -0
- package/dist/gh-tool/pr/snapshot.d.ts.map +1 -0
- package/dist/gh-tool/pr/stack-read.d.ts +1 -6
- package/dist/gh-tool/pr/stack-read.d.ts.map +1 -1
- package/dist/gh-tool/pr/stack.d.ts +3 -3
- package/dist/gh-tool/pr/stack.d.ts.map +1 -1
- package/dist/gh-tool/{api.d.ts → read/api.d.ts} +9 -3
- package/dist/gh-tool/read/api.d.ts.map +1 -0
- package/dist/gh-tool/read/cache.d.ts +44 -0
- package/dist/gh-tool/read/cache.d.ts.map +1 -0
- package/dist/gh-tool/read/catalog.d.ts +170 -0
- package/dist/gh-tool/read/catalog.d.ts.map +1 -0
- package/dist/gh-tool/read/classes.d.ts +98 -0
- package/dist/gh-tool/read/classes.d.ts.map +1 -0
- package/dist/gh-tool/read/commands.d.ts +3 -0
- package/dist/gh-tool/read/commands.d.ts.map +1 -0
- package/dist/gh-tool/read/commits.d.ts +10 -0
- package/dist/gh-tool/read/commits.d.ts.map +1 -0
- package/dist/gh-tool/read/gated.d.ts +14 -0
- package/dist/gh-tool/read/gated.d.ts.map +1 -0
- package/dist/gh-tool/read/index.d.ts +5 -0
- package/dist/gh-tool/read/index.d.ts.map +1 -0
- package/dist/gh-tool/read/issues.d.ts +80 -0
- package/dist/gh-tool/read/issues.d.ts.map +1 -0
- package/dist/gh-tool/read/keys.d.ts +64 -0
- package/dist/gh-tool/read/keys.d.ts.map +1 -0
- package/dist/gh-tool/read/porcelain.d.ts +68 -0
- package/dist/gh-tool/read/porcelain.d.ts.map +1 -0
- package/dist/gh-tool/read/pulls.d.ts +137 -0
- package/dist/gh-tool/read/pulls.d.ts.map +1 -0
- package/dist/gh-tool/read/reads.d.ts +220 -0
- package/dist/gh-tool/read/reads.d.ts.map +1 -0
- package/dist/gh-tool/read/rest.d.ts +49 -0
- package/dist/gh-tool/read/rest.d.ts.map +1 -0
- package/dist/gh-tool/read/review-guards.d.ts +20 -0
- package/dist/gh-tool/read/review-guards.d.ts.map +1 -0
- package/dist/gh-tool/read/review-model.d.ts +190 -0
- package/dist/gh-tool/read/review-model.d.ts.map +1 -0
- package/dist/gh-tool/read/reviews.d.ts +9 -0
- package/dist/gh-tool/read/reviews.d.ts.map +1 -0
- package/dist/gh-tool/read/stacks.d.ts +7 -0
- package/dist/gh-tool/read/stacks.d.ts.map +1 -0
- package/dist/gh-tool/read/store.d.ts +70 -0
- package/dist/gh-tool/read/store.d.ts.map +1 -0
- package/dist/gh-tool/{service.d.ts → read/transport.d.ts} +5 -3
- package/dist/gh-tool/read/transport.d.ts.map +1 -0
- package/dist/gh-tool/read/waits.d.ts +22 -0
- package/dist/gh-tool/read/waits.d.ts.map +1 -0
- package/dist/gh-tool/read/writes.d.ts +16 -0
- package/dist/gh-tool/read/writes.d.ts.map +1 -0
- package/dist/gh-tool/release.d.ts +10 -26
- package/dist/gh-tool/release.d.ts.map +1 -1
- package/dist/gh-tool/repo.d.ts +9 -4
- package/dist/gh-tool/repo.d.ts.map +1 -1
- package/dist/gh-tool/types.d.ts +1 -1
- package/dist/gh-tool/types.d.ts.map +1 -1
- package/dist/gh-tool/workflow.d.ts +43 -21
- package/dist/gh-tool/workflow.d.ts.map +1 -1
- package/dist/observability-tool/shared.d.ts.map +1 -1
- package/dist/shared/content-security.d.ts +2 -0
- package/dist/shared/content-security.d.ts.map +1 -1
- package/package.json +7 -6
- package/schemas/agent-tools.schema.json +5 -0
- package/src/api-tool/proxy-entry.ts +3 -1
- package/src/config/loader.ts +1 -0
- package/src/config/types.ts +1 -0
- package/src/credential-guard/index.ts +169 -1
- package/src/db-tool/service.ts +4 -1
- package/src/gh-tool/branch.ts +5 -8
- package/src/gh-tool/environment.ts +2 -1
- package/src/gh-tool/gist.ts +8 -35
- package/src/gh-tool/issue/commands.ts +12 -2
- package/src/gh-tool/issue/core.ts +58 -122
- package/src/gh-tool/issue/triage.ts +4 -26
- package/src/gh-tool/label.ts +57 -0
- package/src/gh-tool/main.ts +24 -4
- package/src/gh-tool/pr/commands.ts +17 -15
- package/src/gh-tool/pr/core.ts +486 -582
- package/src/gh-tool/pr/review.ts +106 -454
- package/src/gh-tool/pr/snapshot.ts +23 -0
- package/src/gh-tool/pr/stack-read.ts +1 -73
- package/src/gh-tool/pr/stack.ts +37 -36
- package/src/gh-tool/{api.ts → read/api.ts} +40 -7
- package/src/gh-tool/read/cache.ts +192 -0
- package/src/gh-tool/read/catalog.ts +310 -0
- package/src/gh-tool/read/classes.ts +31 -0
- package/src/gh-tool/read/commands.ts +25 -0
- package/src/gh-tool/read/commits.ts +28 -0
- package/src/gh-tool/read/gated.ts +56 -0
- package/src/gh-tool/read/index.ts +4 -0
- package/src/gh-tool/read/issues.ts +124 -0
- package/src/gh-tool/read/keys.ts +104 -0
- package/src/gh-tool/read/porcelain.ts +58 -0
- package/src/gh-tool/read/pulls.ts +266 -0
- package/src/gh-tool/read/reads.ts +306 -0
- package/src/gh-tool/read/rest.ts +111 -0
- package/src/gh-tool/read/review-guards.ts +61 -0
- package/src/gh-tool/read/review-model.ts +286 -0
- package/src/gh-tool/read/reviews.ts +114 -0
- package/src/gh-tool/read/stacks.ts +73 -0
- package/src/gh-tool/read/store.ts +504 -0
- package/src/gh-tool/{service.ts → read/transport.ts} +26 -6
- package/src/gh-tool/read/waits.ts +52 -0
- package/src/gh-tool/read/writes.ts +37 -0
- package/src/gh-tool/release.ts +15 -168
- package/src/gh-tool/repo.ts +26 -78
- package/src/gh-tool/types.ts +1 -1
- package/src/gh-tool/workflow.ts +123 -214
- package/src/observability-tool/shared.ts +2 -1
- package/src/shared/content-security.ts +10 -1
- package/src/shared/credential-patterns.ts +1 -1
- package/dist/gh-tool/api.d.ts.map +0 -1
- package/dist/gh-tool/pr/check-status-cache.d.ts +0 -6
- package/dist/gh-tool/pr/check-status-cache.d.ts.map +0 -1
- package/dist/gh-tool/service.d.ts.map +0 -1
- package/src/gh-tool/pr/check-status-cache.ts +0 -142
|
@@ -3,7 +3,9 @@ import { startProxy } from "./server";
|
|
|
3
3
|
import { readPrivateFile } from "./store";
|
|
4
4
|
import { safeError } from "./errors";
|
|
5
5
|
|
|
6
|
-
if (Bun.argv.length
|
|
6
|
+
if (Bun.argv.length === 3 && (Bun.argv[2] === "--help" || Bun.argv[2] === "-h")) {
|
|
7
|
+
process.stdout.write("Usage: api-proxy --config /operator/private/operator.json\n");
|
|
8
|
+
} else if (Bun.argv.length !== 4 || Bun.argv[2] !== "--config") {
|
|
7
9
|
process.stderr.write("Usage: api-proxy --config /operator/private/operator.json\n");
|
|
8
10
|
process.exitCode = 1;
|
|
9
11
|
} else {
|
package/src/config/loader.ts
CHANGED
|
@@ -11,6 +11,7 @@ const CliToolOverrideSchema = Schema.Struct({
|
|
|
11
11
|
});
|
|
12
12
|
|
|
13
13
|
const CredentialGuardConfigSchema = Schema.Struct({
|
|
14
|
+
chainCopyDestinations: Schema.optionalKey(Schema.Array(Schema.String)),
|
|
14
15
|
allowedEnvironmentVariables: Schema.optionalKey(Schema.Array(Schema.String)),
|
|
15
16
|
additionalBlockedPaths: Schema.optionalKey(Schema.Array(Schema.String)),
|
|
16
17
|
additionalAllowedPaths: Schema.optionalKey(Schema.Array(Schema.String)),
|
package/src/config/types.ts
CHANGED
|
@@ -187,6 +187,7 @@ export type CliToolOverride = {
|
|
|
187
187
|
export type CredentialGuardConfig = {
|
|
188
188
|
/** Exact, case-sensitive names approved for static printenv reads. Empty by default. */
|
|
189
189
|
allowedEnvironmentVariables?: string[];
|
|
190
|
+
chainCopyDestinations?: string[];
|
|
190
191
|
additionalBlockedPaths?: string[];
|
|
191
192
|
additionalAllowedPaths?: string[];
|
|
192
193
|
additionalBlockedCliTools?: CliToolOverride[];
|
|
@@ -18,6 +18,7 @@ import type { CliToolOverride, CredentialGuardConfig } from "#config/types";
|
|
|
18
18
|
import { redactSensitiveText } from "#shared/content-security";
|
|
19
19
|
import { SECRET_PATTERNS } from "#shared/credential-patterns";
|
|
20
20
|
import { posix } from "node:path";
|
|
21
|
+
import { lstatSync, realpathSync } from "node:fs";
|
|
21
22
|
import { passiveJqProgramIndex } from "./passive-jq";
|
|
22
23
|
|
|
23
24
|
export { findSecretMatches } from "#shared/credential-patterns";
|
|
@@ -2558,6 +2559,131 @@ function hasStaticEnvironmentRead(
|
|
|
2558
2559
|
);
|
|
2559
2560
|
}
|
|
2560
2561
|
|
|
2562
|
+
function commandChainSources(command: string): string[] | undefined {
|
|
2563
|
+
const parts: string[] = [];
|
|
2564
|
+
let start = 0;
|
|
2565
|
+
let quote: "'" | '"' | "`" | undefined;
|
|
2566
|
+
let depth = 0;
|
|
2567
|
+
for (let index = 0; index < command.length; index++) {
|
|
2568
|
+
const char = command[index];
|
|
2569
|
+
if (char === "\\" && quote !== "'") {
|
|
2570
|
+
index++;
|
|
2571
|
+
} else if (quote) {
|
|
2572
|
+
if (char === quote) quote = undefined;
|
|
2573
|
+
} else if (char === "'" || char === '"' || char === "`") {
|
|
2574
|
+
quote = char;
|
|
2575
|
+
} else if (char === "#") {
|
|
2576
|
+
return undefined;
|
|
2577
|
+
} else if (char === "(") {
|
|
2578
|
+
depth++;
|
|
2579
|
+
} else if (char === ")") {
|
|
2580
|
+
if (--depth < 0) return undefined;
|
|
2581
|
+
} else if (!depth && (char === ";" || command.startsWith("&&", index))) {
|
|
2582
|
+
const part = command.slice(start, index).trim();
|
|
2583
|
+
if (!part || parts.length >= 31) return undefined;
|
|
2584
|
+
parts.push(part);
|
|
2585
|
+
if (char === "&") index++;
|
|
2586
|
+
start = index + 1;
|
|
2587
|
+
}
|
|
2588
|
+
}
|
|
2589
|
+
const last = command.slice(start).trim();
|
|
2590
|
+
return !quote && !depth && last && parts.length ? [...parts, last] : undefined;
|
|
2591
|
+
}
|
|
2592
|
+
|
|
2593
|
+
type LiteralChainPart = { source: string; argv: string[]; redirects: boolean };
|
|
2594
|
+
|
|
2595
|
+
function literalCommandChain(command: string): LiteralChainPart[] | undefined {
|
|
2596
|
+
const literal = literalBindingText(command, "", "");
|
|
2597
|
+
if (!literal || /[|&\r\n]/.test(literal.syntax.replaceAll("&&", ""))) return undefined;
|
|
2598
|
+
const boundaries = [...literal.syntax.matchAll(/&&|;/g)];
|
|
2599
|
+
if (!boundaries.length || boundaries.length > 31) return undefined;
|
|
2600
|
+
const parts: LiteralChainPart[] = [];
|
|
2601
|
+
let start = 0;
|
|
2602
|
+
for (const end of [...boundaries.map((match) => match.index), command.length]) {
|
|
2603
|
+
const source = command.slice(start, end).trim();
|
|
2604
|
+
const parsed = parseStaticShellCommands(source);
|
|
2605
|
+
if (
|
|
2606
|
+
!parsed ||
|
|
2607
|
+
typeof parsed === "string" ||
|
|
2608
|
+
parsed.pipelines.length !== 1 ||
|
|
2609
|
+
parsed.pipelines[0]?.length !== 1
|
|
2610
|
+
)
|
|
2611
|
+
return undefined;
|
|
2612
|
+
const argv = unwrapProofCommand(parsed.pipelines[0][0] ?? [], "\0");
|
|
2613
|
+
if (!argv) return undefined;
|
|
2614
|
+
parts.push({ source, argv, redirects: parsed.redirects.length > 0 });
|
|
2615
|
+
start = end + (literal.syntax.startsWith("&&", end) ? 2 : 1);
|
|
2616
|
+
}
|
|
2617
|
+
return parts;
|
|
2618
|
+
}
|
|
2619
|
+
|
|
2620
|
+
function isIndependentHerdrOperation(argv: string[]): boolean {
|
|
2621
|
+
if (isLiteralHerdrPrompt(argv)) return argv.length === 5;
|
|
2622
|
+
if (argv[0] !== "herdr") return false;
|
|
2623
|
+
if (argv[1] === "agent" && argv[2] === "start") {
|
|
2624
|
+
const separator = argv.indexOf("--", 3);
|
|
2625
|
+
const options = argv.slice(3, separator < 0 ? undefined : separator);
|
|
2626
|
+
const kinds = options.filter((arg) => arg === "--kind" || arg.startsWith("--kind="));
|
|
2627
|
+
return (
|
|
2628
|
+
kinds.length === 1 &&
|
|
2629
|
+
kinds[0] === "--kind" &&
|
|
2630
|
+
options[options.indexOf("--kind") + 1] === "codex"
|
|
2631
|
+
);
|
|
2632
|
+
}
|
|
2633
|
+
if (argv[1] !== "pane") return false;
|
|
2634
|
+
if (argv[2] === "run")
|
|
2635
|
+
return argv.length === 5 && argv[4] === "exec bash ~/.codex/codex-clean.sh bash --norc -i";
|
|
2636
|
+
if (argv[2] !== "wait-output" || argv.length < 6) return false;
|
|
2637
|
+
const seen = new Set<string>();
|
|
2638
|
+
for (let index = 4; index < argv.length; index += 2) {
|
|
2639
|
+
const option = argv[index] ?? "";
|
|
2640
|
+
if (seen.has(option)) return false;
|
|
2641
|
+
seen.add(option);
|
|
2642
|
+
if (!["--match", "--regex", "--timeout"].includes(option) || !argv[index + 1]) return false;
|
|
2643
|
+
if (argv[index] === "--timeout" && !/^\d+$/.test(argv[index + 1] ?? "")) return false;
|
|
2644
|
+
}
|
|
2645
|
+
return true;
|
|
2646
|
+
}
|
|
2647
|
+
|
|
2648
|
+
function isIndependentDataCopy(argv: string[], destinations: string[]): boolean {
|
|
2649
|
+
if (!destinations.length || argv[0] !== "cp" || argv.length !== 3) return false;
|
|
2650
|
+
const source = argv[1] ?? "";
|
|
2651
|
+
const target = argv[2] ?? "";
|
|
2652
|
+
const literalPath = (path: string) =>
|
|
2653
|
+
/^\/[A-Za-z0-9_./ -]+$/.test(path) &&
|
|
2654
|
+
!path.split("/").some((part) => part === "." || part === "..");
|
|
2655
|
+
if (!/^(?:~\/)?[A-Za-z0-9_./-]+$/.test(source) || source.startsWith("-") || !literalPath(target))
|
|
2656
|
+
return false;
|
|
2657
|
+
try {
|
|
2658
|
+
const targetPath = target.replace(/\/+$/, "");
|
|
2659
|
+
const targetStat = lstatSync(targetPath, { throwIfNoEntry: false });
|
|
2660
|
+
if (target.endsWith("/") && !targetStat?.isDirectory()) return false;
|
|
2661
|
+
const destination = targetStat?.isDirectory()
|
|
2662
|
+
? posix.join(targetPath, posix.basename(source))
|
|
2663
|
+
: targetPath;
|
|
2664
|
+
for (const directory of destinations) {
|
|
2665
|
+
if (!literalPath(directory)) continue;
|
|
2666
|
+
const root = directory.replace(/\/+$/, "");
|
|
2667
|
+
if (!destination.startsWith(root + "/")) continue;
|
|
2668
|
+
if (!lstatSync(root).isDirectory() || realpathSync(root) !== root) return false;
|
|
2669
|
+
const parts = destination.slice(root.length + 1).split("/");
|
|
2670
|
+
let current = root;
|
|
2671
|
+
for (const [index, part] of parts.entries()) {
|
|
2672
|
+
current += "/" + part;
|
|
2673
|
+
const stat = lstatSync(current, { throwIfNoEntry: false });
|
|
2674
|
+
const last = index === parts.length - 1;
|
|
2675
|
+
if (!stat) return last;
|
|
2676
|
+
if (stat.isSymbolicLink() || (!last && !stat.isDirectory())) return false;
|
|
2677
|
+
if (realpathSync(current) !== current) return false;
|
|
2678
|
+
}
|
|
2679
|
+
return true;
|
|
2680
|
+
}
|
|
2681
|
+
} catch {
|
|
2682
|
+
return false;
|
|
2683
|
+
}
|
|
2684
|
+
return false;
|
|
2685
|
+
}
|
|
2686
|
+
|
|
2561
2687
|
function isLiteralHerdrPrompt(argv: string[]): boolean {
|
|
2562
2688
|
return argv[0] === "herdr" && argv[1] === "agent" && argv[2] === "prompt" && argv.length >= 5;
|
|
2563
2689
|
}
|
|
@@ -3730,6 +3856,31 @@ export function createCredentialGuard(config?: CredentialGuardConfig): Credentia
|
|
|
3730
3856
|
});
|
|
3731
3857
|
if (unterminatedQuote)
|
|
3732
3858
|
return "cannot prove shell syntax: an unterminated quoted argument was found";
|
|
3859
|
+
const chain = literalCommandChain(command);
|
|
3860
|
+
if (chain?.some(({ argv }) => isLiteralHerdrPrompt(argv)) && !isStaticHerdrPrompt(command)) {
|
|
3861
|
+
let copied = false;
|
|
3862
|
+
for (const [index, part] of chain.entries()) {
|
|
3863
|
+
const reason = getDangerousBashReason(part.source, shellDepth);
|
|
3864
|
+
if (reason) return `cannot prove chain part ${index + 1} (${part.source}): ${reason}`;
|
|
3865
|
+
if (
|
|
3866
|
+
!part.redirects &&
|
|
3867
|
+
isIndependentDataCopy(part.argv, config?.chainCopyDestinations ?? []) &&
|
|
3868
|
+
!copied
|
|
3869
|
+
) {
|
|
3870
|
+
copied = true;
|
|
3871
|
+
continue;
|
|
3872
|
+
}
|
|
3873
|
+
if (
|
|
3874
|
+
part.redirects ||
|
|
3875
|
+
!isIndependentHerdrOperation(part.argv) ||
|
|
3876
|
+
(copied && !(isLiteralHerdrPrompt(part.argv) && part.argv.length === 5))
|
|
3877
|
+
)
|
|
3878
|
+
return `cannot prove independent chain part ${index + 1} (${part.source}) under the environment/execution policy`;
|
|
3879
|
+
}
|
|
3880
|
+
if (dangerousBashPatterns.some((pattern) => pattern.test(originalCommand)))
|
|
3881
|
+
return "matches a configured dangerous-command pattern";
|
|
3882
|
+
return null;
|
|
3883
|
+
}
|
|
3733
3884
|
const allowPackageInventory = hasProvedPackageInventoryInvocation(originalCommand);
|
|
3734
3885
|
const promptWords = isStaticHerdrPrompt(command)
|
|
3735
3886
|
? parseStaticShellCommands(command)
|
|
@@ -4140,7 +4291,24 @@ export function createCredentialGuard(config?: CredentialGuardConfig): Credentia
|
|
|
4140
4291
|
if (tool === "bash") {
|
|
4141
4292
|
const command = extractCommand(args);
|
|
4142
4293
|
|
|
4143
|
-
|
|
4294
|
+
let dangerousReason = getDangerousBashReason(command);
|
|
4295
|
+
if (dangerousReason && !dangerousReason.includes("chain part")) {
|
|
4296
|
+
let unproved: string | undefined;
|
|
4297
|
+
for (const [index, source] of (commandChainSources(command) ?? []).entries()) {
|
|
4298
|
+
const reason = getDangerousBashReason(source);
|
|
4299
|
+
const parsed = parseStaticShellCommands(source);
|
|
4300
|
+
const literal = literalBindingText(source, "", "");
|
|
4301
|
+
const words = parsed && typeof parsed !== "string" ? parsed.pipelines[0]?.[0] : undefined;
|
|
4302
|
+
const argv = words ? unwrapProofCommand(words, "\0") : undefined;
|
|
4303
|
+
if (reason) {
|
|
4304
|
+
dangerousReason += `; cannot prove chain part ${index + 1} (${source}): ${reason}`;
|
|
4305
|
+
break;
|
|
4306
|
+
}
|
|
4307
|
+
if (!unproved && (!literal || /[<>]/.test(literal.syntax) || !argv))
|
|
4308
|
+
unproved = `cannot prove chain part ${index + 1} (${source}): independent composition is unproved`;
|
|
4309
|
+
}
|
|
4310
|
+
if (!dangerousReason.includes("chain part") && unproved) dangerousReason += `; ${unproved}`;
|
|
4311
|
+
}
|
|
4144
4312
|
if (dangerousReason) {
|
|
4145
4313
|
throw new Error(
|
|
4146
4314
|
`\u{1F6AB} Command blocked: ${dangerousReason}.\n\n` +
|
package/src/db-tool/service.ts
CHANGED
|
@@ -6,7 +6,7 @@ import type { DbConfig, DbMutationOperation, QueryResult, SchemaMode } from "./t
|
|
|
6
6
|
import { ConfigService } from "#config";
|
|
7
7
|
import { isPrerequisiteRunError } from "#shared/prerequisites/errors";
|
|
8
8
|
import { resolveEnvTemplate } from "#shared/env-template";
|
|
9
|
-
import { unsafeOutboundTextReason } from "#shared/content-security";
|
|
9
|
+
import { registerResolvedSecret, unsafeOutboundTextReason } from "#shared/content-security";
|
|
10
10
|
import { resolveEnvironmentScopedPrerequisites } from "#shared/prerequisites/config";
|
|
11
11
|
import { runWithProfilePrerequisites } from "#shared/prerequisites/runtime";
|
|
12
12
|
import { buildApiProbeArgs } from "#shared/k8s-probe";
|
|
@@ -227,18 +227,21 @@ export class DbService extends Context.Service<
|
|
|
227
227
|
env: string,
|
|
228
228
|
) {
|
|
229
229
|
if (config.password) {
|
|
230
|
+
registerResolvedSecret(config.password);
|
|
230
231
|
return config.password;
|
|
231
232
|
}
|
|
232
233
|
|
|
233
234
|
if (config.passwordEnvVar) {
|
|
234
235
|
const fromEnv = process.env[config.passwordEnvVar];
|
|
235
236
|
if (fromEnv) {
|
|
237
|
+
registerResolvedSecret(fromEnv);
|
|
236
238
|
return fromEnv;
|
|
237
239
|
}
|
|
238
240
|
|
|
239
241
|
const zshrcEnv = yield* loadEnvFromZshrc();
|
|
240
242
|
const fromZsh = zshrcEnv[config.passwordEnvVar];
|
|
241
243
|
if (fromZsh) {
|
|
244
|
+
registerResolvedSecret(fromZsh);
|
|
242
245
|
return fromZsh;
|
|
243
246
|
}
|
|
244
247
|
|
package/src/gh-tool/branch.ts
CHANGED
|
@@ -3,7 +3,9 @@ import { Effect, Option } from "effect";
|
|
|
3
3
|
|
|
4
4
|
import { formatOption, logFormatted } from "#shared";
|
|
5
5
|
import type { BranchRenameResult } from "./types";
|
|
6
|
-
import {
|
|
6
|
+
import { writes } from "./read/writes";
|
|
7
|
+
import { resolveRepo } from "./read/reads";
|
|
8
|
+
import { repoSlug } from "./read/keys";
|
|
7
9
|
|
|
8
10
|
// ---------------------------------------------------------------------------
|
|
9
11
|
// Internal handlers
|
|
@@ -15,8 +17,6 @@ export const renameBranch = Effect.fn("branch.renameBranch")(function* (opts: {
|
|
|
15
17
|
confirm: boolean;
|
|
16
18
|
repo: string | null;
|
|
17
19
|
}) {
|
|
18
|
-
const gh = yield* GitHubService;
|
|
19
|
-
|
|
20
20
|
if (!opts.confirm) {
|
|
21
21
|
const scope = opts.repo !== null ? ` in ${opts.repo}` : "";
|
|
22
22
|
|
|
@@ -31,10 +31,7 @@ export const renameBranch = Effect.fn("branch.renameBranch")(function* (opts: {
|
|
|
31
31
|
return dryRun;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
const repoInfo =
|
|
35
|
-
opts.repo !== null
|
|
36
|
-
? opts.repo
|
|
37
|
-
: yield* gh.getRepoInfo().pipe(Effect.map((r) => `${r.owner}/${r.name}`));
|
|
34
|
+
const repoInfo = opts.repo !== null ? opts.repo : repoSlug(yield* resolveRepo(null));
|
|
38
35
|
|
|
39
36
|
const args = [
|
|
40
37
|
"api",
|
|
@@ -45,7 +42,7 @@ export const renameBranch = Effect.fn("branch.renameBranch")(function* (opts: {
|
|
|
45
42
|
`new_name=${opts.newName}`,
|
|
46
43
|
];
|
|
47
44
|
|
|
48
|
-
yield*
|
|
45
|
+
yield* writes.run(args, [`repo:${repoInfo.toLowerCase()}`]);
|
|
49
46
|
|
|
50
47
|
const result: BranchRenameResult = {
|
|
51
48
|
renamed: true,
|
|
@@ -41,10 +41,11 @@ const GH_ENVIRONMENT_NAMES = [
|
|
|
41
41
|
|
|
42
42
|
export const ghEnvironment = (
|
|
43
43
|
environment: Record<string, string | undefined> = process.env,
|
|
44
|
+
credential: string | null = null,
|
|
44
45
|
): Record<string, string> =>
|
|
45
46
|
Object.fromEntries(
|
|
46
47
|
GH_ENVIRONMENT_NAMES.flatMap((name) => {
|
|
47
|
-
const value = environment[name];
|
|
48
|
+
const value = name === "GH_TOKEN" && credential !== null ? credential : environment[name];
|
|
48
49
|
return value === undefined ? [] : [[name, value]];
|
|
49
50
|
}),
|
|
50
51
|
);
|
package/src/gh-tool/gist.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { passthrough } from "./read/catalog";
|
|
2
|
+
import type { GistApi } from "./read/catalog";
|
|
3
|
+
import { writes } from "./read/writes";
|
|
1
4
|
import { Command, Flag } from "effect/cli";
|
|
2
5
|
import { Effect, Option } from "effect";
|
|
3
6
|
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
@@ -11,33 +14,12 @@ import {
|
|
|
11
14
|
resolveOptionalTextInput,
|
|
12
15
|
} from "#gh/text-input";
|
|
13
16
|
import { GitHubCommandError } from "./errors";
|
|
14
|
-
import { GitHubService } from "./service";
|
|
15
17
|
|
|
16
18
|
// ---------------------------------------------------------------------------
|
|
17
19
|
// Types
|
|
18
20
|
// ---------------------------------------------------------------------------
|
|
19
21
|
|
|
20
22
|
// `gh gist list/view` have no --json, so reads go through `gh api gists` (REST shape).
|
|
21
|
-
type GistApiFile = {
|
|
22
|
-
filename: string;
|
|
23
|
-
language: string | null;
|
|
24
|
-
type: string;
|
|
25
|
-
size: number;
|
|
26
|
-
truncated?: boolean;
|
|
27
|
-
content?: string;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
type GistApi = {
|
|
31
|
-
id: string;
|
|
32
|
-
description: string | null;
|
|
33
|
-
public: boolean;
|
|
34
|
-
html_url: string;
|
|
35
|
-
created_at: string;
|
|
36
|
-
updated_at: string;
|
|
37
|
-
owner: { login: string } | null;
|
|
38
|
-
files: Record<string, GistApiFile>;
|
|
39
|
-
};
|
|
40
|
-
|
|
41
23
|
type GistListItem = {
|
|
42
24
|
id: string;
|
|
43
25
|
description: string | null;
|
|
@@ -247,9 +229,7 @@ const listGists = Effect.fn("gist.listGists")(function* (opts: {
|
|
|
247
229
|
limit: number;
|
|
248
230
|
visibility: string | null;
|
|
249
231
|
}) {
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
const gists = yield* gh.runGhJson<GistApi[]>(["api", `gists?per_page=${opts.limit}`]);
|
|
232
|
+
const gists = yield* passthrough.gistList(opts.limit);
|
|
253
233
|
|
|
254
234
|
return gists
|
|
255
235
|
.filter(
|
|
@@ -264,9 +244,7 @@ const viewGist = Effect.fn("gist.viewGist")(function* (opts: {
|
|
|
264
244
|
filename: string | null;
|
|
265
245
|
withContent: boolean;
|
|
266
246
|
}) {
|
|
267
|
-
const
|
|
268
|
-
|
|
269
|
-
const gist = yield* gh.runGhJson<GistApi>(["api", `gists/${opts.id}`]);
|
|
247
|
+
const gist = yield* passthrough.gistView(opts.id);
|
|
270
248
|
|
|
271
249
|
return toGistDetail(gist, { filename: opts.filename, withContent: opts.withContent });
|
|
272
250
|
});
|
|
@@ -276,7 +254,6 @@ export const createGist = Effect.fn("gist.createGist")(function* (opts: {
|
|
|
276
254
|
description: string | null;
|
|
277
255
|
public: boolean;
|
|
278
256
|
}) {
|
|
279
|
-
const gh = yield* GitHubService;
|
|
280
257
|
return yield* Effect.scoped(
|
|
281
258
|
Effect.gen(function* () {
|
|
282
259
|
const snapshots: string[] = [];
|
|
@@ -298,7 +275,7 @@ export const createGist = Effect.fn("gist.createGist")(function* (opts: {
|
|
|
298
275
|
args.push("--public");
|
|
299
276
|
}
|
|
300
277
|
|
|
301
|
-
const result = yield*
|
|
278
|
+
const result = yield* writes.run(args, []);
|
|
302
279
|
const url =
|
|
303
280
|
result.stdout
|
|
304
281
|
.trim()
|
|
@@ -333,8 +310,6 @@ export const editGist = Effect.fn("gist.editGist")(function* (opts: {
|
|
|
333
310
|
filename: string | null;
|
|
334
311
|
sourcePath: string | null;
|
|
335
312
|
}) {
|
|
336
|
-
const gh = yield* GitHubService;
|
|
337
|
-
|
|
338
313
|
const args = ["gist", "edit", opts.id];
|
|
339
314
|
const changes: string[] = [];
|
|
340
315
|
|
|
@@ -371,7 +346,7 @@ export const editGist = Effect.fn("gist.editGist")(function* (opts: {
|
|
|
371
346
|
args.push("--filename", opts.filename);
|
|
372
347
|
}
|
|
373
348
|
|
|
374
|
-
yield*
|
|
349
|
+
yield* writes.run(args, []);
|
|
375
350
|
|
|
376
351
|
const edited: GistEditResult = { edited: true, id: opts.id, changes };
|
|
377
352
|
|
|
@@ -379,8 +354,6 @@ export const editGist = Effect.fn("gist.editGist")(function* (opts: {
|
|
|
379
354
|
});
|
|
380
355
|
|
|
381
356
|
const deleteGist = Effect.fn("gist.deleteGist")(function* (opts: { id: string; confirm: boolean }) {
|
|
382
|
-
const gh = yield* GitHubService;
|
|
383
|
-
|
|
384
357
|
if (!opts.confirm) {
|
|
385
358
|
const dryRun: GistDeleteResult = {
|
|
386
359
|
deleted: false,
|
|
@@ -392,7 +365,7 @@ const deleteGist = Effect.fn("gist.deleteGist")(function* (opts: { id: string; c
|
|
|
392
365
|
return dryRun;
|
|
393
366
|
}
|
|
394
367
|
|
|
395
|
-
yield*
|
|
368
|
+
yield* writes.run(["gist", "delete", opts.id, "--yes"], []);
|
|
396
369
|
|
|
397
370
|
const deleted: GistDeleteResult = { deleted: true, id: opts.id };
|
|
398
371
|
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
reopenIssue,
|
|
19
19
|
viewIssue,
|
|
20
20
|
} from "./core";
|
|
21
|
-
import { GitHubService } from "#gh/
|
|
21
|
+
import { GitHubService } from "#gh/read/transport";
|
|
22
22
|
|
|
23
23
|
const repoOption = Flag.String("repo").pipe(
|
|
24
24
|
Flag.withDescription("Target repository profile name or owner/name"),
|
|
@@ -35,6 +35,14 @@ export const issueListCommand = Command.make(
|
|
|
35
35
|
"list",
|
|
36
36
|
{
|
|
37
37
|
format: formatOption,
|
|
38
|
+
includeBody: Flag.Boolean("include-body").pipe(
|
|
39
|
+
Flag.withDescription("Include each issue's body in the result"),
|
|
40
|
+
Flag.withDefault(false),
|
|
41
|
+
),
|
|
42
|
+
fresh: Flag.Boolean("fresh").pipe(
|
|
43
|
+
Flag.withDescription("Read from GitHub instead of the shared cache"),
|
|
44
|
+
Flag.withDefault(false),
|
|
45
|
+
),
|
|
38
46
|
labels: Flag.String("labels").pipe(
|
|
39
47
|
Flag.withDescription("Filter by label (comma-separated)"),
|
|
40
48
|
Flag.optional,
|
|
@@ -49,12 +57,14 @@ export const issueListCommand = Command.make(
|
|
|
49
57
|
Flag.withDefault("open"),
|
|
50
58
|
),
|
|
51
59
|
},
|
|
52
|
-
({ format, labels, limit, repo, state }) =>
|
|
60
|
+
({ format, fresh, includeBody, labels, limit, repo, state }) =>
|
|
53
61
|
withRepo(
|
|
54
62
|
repo,
|
|
55
63
|
Effect.gen(function* () {
|
|
56
64
|
const issues = yield* listIssues({
|
|
57
65
|
labels: Option.getOrNull(labels),
|
|
66
|
+
includeBody,
|
|
67
|
+
fresh,
|
|
58
68
|
limit,
|
|
59
69
|
state,
|
|
60
70
|
});
|