toolcraft 0.0.23 → 0.0.25
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 -2
- package/dist/cli.compile-check.js +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +50 -13
- package/dist/error-report.js +32 -3
- package/dist/human-in-loop/approval-tasks.d.ts +1 -0
- package/dist/human-in-loop/approval-tasks.js +7 -5
- package/dist/human-in-loop/approvals-commands.js +51 -8
- package/dist/human-in-loop/runner.js +24 -19
- package/dist/human-in-loop/state-machine.d.ts +3 -3
- package/dist/human-in-loop/state-machine.js +13 -5
- package/dist/index.d.ts +5 -0
- package/dist/index.js +6 -1
- package/dist/mcp-proxy.js +85 -19
- package/dist/mcp.compile-check.js +1 -0
- package/dist/mcp.d.ts +1 -0
- package/dist/mcp.js +50 -8
- package/dist/renderer.js +119 -13
- package/dist/sdk.compile-check.js +1 -0
- package/dist/sdk.d.ts +1 -0
- package/dist/sdk.js +56 -11
- package/node_modules/@poe-code/agent-defs/dist/registry.d.ts +1 -1
- package/node_modules/@poe-code/agent-defs/dist/registry.js +22 -11
- package/node_modules/@poe-code/agent-defs/package.json +1 -1
- package/node_modules/@poe-code/agent-human-in-loop/dist/providers/osascript-script.js +5 -1
- package/node_modules/@poe-code/agent-human-in-loop/dist/providers/osascript.js +1 -1
- package/node_modules/@poe-code/agent-human-in-loop/package.json +1 -1
- package/node_modules/@poe-code/agent-mcp-config/dist/apply.d.ts +1 -1
- package/node_modules/@poe-code/agent-mcp-config/dist/apply.js +41 -92
- package/node_modules/@poe-code/agent-mcp-config/dist/configs.js +4 -1
- package/node_modules/@poe-code/agent-mcp-config/dist/shapes.d.ts +14 -2
- package/node_modules/@poe-code/agent-mcp-config/dist/shapes.js +11 -4
- package/node_modules/@poe-code/agent-mcp-config/package.json +1 -1
- package/node_modules/@poe-code/config-mutations/dist/execution/apply-mutation.js +200 -22
- package/node_modules/@poe-code/config-mutations/dist/execution/path-utils.js +7 -1
- package/node_modules/@poe-code/config-mutations/dist/formats/index.js +1 -1
- package/node_modules/@poe-code/config-mutations/dist/formats/json.js +11 -7
- package/node_modules/@poe-code/config-mutations/dist/formats/object.d.ts +4 -0
- package/node_modules/@poe-code/config-mutations/dist/formats/object.js +27 -0
- package/node_modules/@poe-code/config-mutations/dist/formats/toml.js +12 -9
- package/node_modules/@poe-code/config-mutations/dist/formats/yaml.js +12 -9
- package/node_modules/@poe-code/config-mutations/dist/mutations/file-mutation.d.ts +11 -1
- package/node_modules/@poe-code/config-mutations/dist/mutations/file-mutation.js +10 -1
- package/node_modules/@poe-code/config-mutations/dist/testing/mock-fs.js +25 -1
- package/node_modules/@poe-code/config-mutations/dist/types.d.ts +12 -2
- package/node_modules/@poe-code/config-mutations/package.json +1 -1
- package/node_modules/@poe-code/design-system/dist/acp/components.js +3 -1
- package/node_modules/@poe-code/design-system/dist/components/browser.d.ts +1 -1
- package/node_modules/@poe-code/design-system/dist/components/browser.js +6 -1
- package/node_modules/@poe-code/design-system/dist/components/color.js +9 -8
- package/node_modules/@poe-code/design-system/dist/components/command-errors.js +3 -2
- package/node_modules/@poe-code/design-system/dist/components/detail-card.d.ts +22 -0
- package/node_modules/@poe-code/design-system/dist/components/detail-card.js +69 -0
- package/node_modules/@poe-code/design-system/dist/components/help-formatter.js +88 -11
- package/node_modules/@poe-code/design-system/dist/components/index.d.ts +1 -1
- package/node_modules/@poe-code/design-system/dist/components/index.js +1 -1
- package/node_modules/@poe-code/design-system/dist/components/table.d.ts +2 -0
- package/node_modules/@poe-code/design-system/dist/components/table.js +82 -5
- package/node_modules/@poe-code/design-system/dist/components/template.d.ts +4 -0
- package/node_modules/@poe-code/design-system/dist/components/template.js +198 -32
- package/node_modules/@poe-code/design-system/dist/components/text.js +29 -5
- package/node_modules/@poe-code/design-system/dist/dashboard/ansi.d.ts +2 -2
- package/node_modules/@poe-code/design-system/dist/dashboard/ansi.js +77 -32
- package/node_modules/@poe-code/design-system/dist/dashboard/buffer.js +28 -5
- package/node_modules/@poe-code/design-system/dist/dashboard/components/output-pane.js +45 -28
- package/node_modules/@poe-code/design-system/dist/dashboard/terminal-width.d.ts +4 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/terminal-width.js +71 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/types.d.ts +1 -0
- package/node_modules/@poe-code/design-system/dist/explorer/events.d.ts +6 -0
- package/node_modules/@poe-code/design-system/dist/explorer/reducer.js +32 -10
- package/node_modules/@poe-code/design-system/dist/explorer/render/detail.js +3 -0
- package/node_modules/@poe-code/design-system/dist/explorer/runtime.js +57 -6
- package/node_modules/@poe-code/design-system/dist/explorer/state.d.ts +1 -0
- package/node_modules/@poe-code/design-system/dist/explorer/state.js +12 -15
- package/node_modules/@poe-code/design-system/dist/index.d.ts +3 -1
- package/node_modules/@poe-code/design-system/dist/index.js +2 -1
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/intro.js +2 -1
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/log.js +8 -5
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/note.js +1 -1
- package/node_modules/@poe-code/design-system/dist/static/menu.js +8 -2
- package/node_modules/@poe-code/design-system/dist/static/spinner.js +10 -4
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/parser/frontmatter.js +9 -2
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/renderer.js +19 -2
- package/node_modules/@poe-code/design-system/package.json +2 -1
- package/node_modules/@poe-code/process-runner/dist/docker/args.d.ts +1 -0
- package/node_modules/@poe-code/process-runner/dist/docker/args.js +11 -3
- package/node_modules/@poe-code/process-runner/dist/docker/docker-execution-env.js +377 -130
- package/node_modules/@poe-code/process-runner/dist/docker/docker-runner.js +78 -10
- package/node_modules/@poe-code/process-runner/dist/docker/env-file.d.ts +6 -0
- package/node_modules/@poe-code/process-runner/dist/docker/env-file.js +49 -0
- package/node_modules/@poe-code/process-runner/dist/host/host-execution-env.js +3 -2
- package/node_modules/@poe-code/process-runner/dist/host/host-runner.js +21 -5
- package/node_modules/@poe-code/process-runner/dist/index.d.ts +1 -0
- package/node_modules/@poe-code/process-runner/dist/index.js +1 -0
- package/node_modules/@poe-code/process-runner/dist/testing/mock-runner.js +30 -8
- package/node_modules/@poe-code/process-runner/dist/types.d.ts +6 -0
- package/node_modules/@poe-code/process-runner/dist/workspace-transfer.d.ts +61 -0
- package/node_modules/@poe-code/process-runner/dist/workspace-transfer.js +503 -0
- package/node_modules/@poe-code/process-runner/package.json +1 -1
- package/node_modules/@poe-code/task-list/README.md +0 -2
- package/node_modules/@poe-code/task-list/dist/backends/gh-issues-client.js +3 -0
- package/node_modules/@poe-code/task-list/dist/backends/gh-issues-sync.js +89 -59
- package/node_modules/@poe-code/task-list/dist/backends/gh-issues.d.ts +9 -3
- package/node_modules/@poe-code/task-list/dist/backends/gh-issues.js +460 -99
- package/node_modules/@poe-code/task-list/dist/backends/markdown-dir.js +156 -154
- package/node_modules/@poe-code/task-list/dist/backends/utils.d.ts +2 -0
- package/node_modules/@poe-code/task-list/dist/backends/utils.js +79 -0
- package/node_modules/@poe-code/task-list/dist/backends/yaml-file.js +120 -132
- package/node_modules/@poe-code/task-list/dist/index.d.ts +3 -1
- package/node_modules/@poe-code/task-list/dist/index.js +2 -0
- package/node_modules/@poe-code/task-list/dist/move.d.ts +2 -0
- package/node_modules/@poe-code/task-list/dist/move.js +215 -0
- package/node_modules/@poe-code/task-list/dist/open.js +3 -4
- package/node_modules/@poe-code/task-list/dist/state-machine.js +3 -1
- package/node_modules/@poe-code/task-list/dist/state.js +9 -0
- package/node_modules/@poe-code/task-list/dist/types.d.ts +48 -13
- package/node_modules/@poe-code/task-list/package.json +1 -2
- package/node_modules/auth-store/dist/create-secret-store.js +4 -1
- package/node_modules/auth-store/dist/encrypted-file-store.d.ts +8 -0
- package/node_modules/auth-store/dist/encrypted-file-store.js +104 -8
- package/node_modules/auth-store/dist/index.d.ts +1 -1
- package/node_modules/auth-store/dist/keychain-store.d.ts +4 -1
- package/node_modules/auth-store/dist/keychain-store.js +18 -16
- package/node_modules/auth-store/dist/provider-store.d.ts +5 -1
- package/node_modules/auth-store/dist/provider-store.js +55 -7
- package/node_modules/auth-store/dist/types.d.ts +3 -1
- package/node_modules/auth-store/package.json +2 -1
- package/node_modules/mcp-oauth/dist/client/default-oauth-client-provider.js +46 -15
- package/node_modules/mcp-oauth/dist/client/loopback-authorization.js +49 -12
- package/node_modules/mcp-oauth/dist/client/token-endpoint.js +6 -1
- package/node_modules/mcp-oauth/dist/server/jwks-token-verifier.js +1 -1
- package/node_modules/mcp-oauth/package.json +1 -0
- package/node_modules/tiny-mcp-client/.turbo/turbo-build.log +1 -1
- package/node_modules/tiny-mcp-client/dist/internal.d.ts +9 -4
- package/node_modules/tiny-mcp-client/dist/internal.js +244 -66
- package/node_modules/tiny-mcp-client/dist/oauth-discovery.d.ts +1 -1
- package/node_modules/tiny-mcp-client/dist/oauth-discovery.js +4 -7
- package/node_modules/tiny-mcp-client/package.json +2 -1
- package/node_modules/tiny-mcp-client/src/http-oauth.integration.test.ts +1 -1
- package/node_modules/tiny-mcp-client/src/http-oauth.test.ts +46 -0
- package/node_modules/tiny-mcp-client/src/internal.ts +287 -76
- package/node_modules/tiny-mcp-client/src/mcp-client-sdk.test.ts +32 -0
- package/node_modules/tiny-mcp-client/src/mcp-client-tiny-stdio-test-server-tools.test.ts +1 -1
- package/node_modules/tiny-mcp-client/src/oauth-discovery.ts +5 -10
- package/node_modules/tiny-mcp-client/src/transports.test.ts +588 -6
- package/package.json +10 -12
- package/node_modules/@poe-code/file-lock/README.md +0 -52
- package/node_modules/@poe-code/file-lock/dist/index.d.ts +0 -1
- package/node_modules/@poe-code/file-lock/dist/index.js +0 -1
- package/node_modules/@poe-code/file-lock/dist/lock.d.ts +0 -27
- package/node_modules/@poe-code/file-lock/dist/lock.js +0 -203
- package/node_modules/@poe-code/file-lock/package.json +0 -23
package/README.md
CHANGED
|
@@ -211,8 +211,8 @@ defineCommand({
|
|
|
211
211
|
apiVersion: ">=1.2.0", // fails if runner reports older apiVersion
|
|
212
212
|
check: async (ctx) => ({
|
|
213
213
|
// arbitrary async gate
|
|
214
|
-
ok: ctx.fs.exists("
|
|
215
|
-
message: "
|
|
214
|
+
ok: ctx.fs.exists("READY") === true,
|
|
215
|
+
message: "READY marker missing, refusing to run"
|
|
216
216
|
})
|
|
217
217
|
}
|
|
218
218
|
});
|
package/dist/cli.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type { HumanInLoopRuntimeOptions } from "./human-in-loop/index.js";
|
|
|
4
4
|
type Casing = "kebab" | "snake";
|
|
5
5
|
export interface RunCLIOptions<TServices extends object = Record<string, unknown>> {
|
|
6
6
|
apiVersion?: string;
|
|
7
|
+
approvals?: boolean;
|
|
7
8
|
casing?: Casing;
|
|
8
9
|
humanInLoop?: HumanInLoopRuntimeOptions;
|
|
9
10
|
projectRoot?: string;
|
package/dist/cli.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { access, readFile, writeFile } from "node:fs/promises";
|
|
1
|
+
import { access, lstat, readFile, rename, unlink, writeFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { Command as CommanderCommand, CommanderError, InvalidArgumentError, Option } from "commander";
|
|
4
4
|
import { cancel, confirm, createLogger, formatCommandList, formatOptionList, getTheme, helpFormatterPlain, isCancel, note, promptText, renderTable, resetOutputFormatCache, select, text } from "@poe-code/design-system";
|
|
@@ -608,10 +608,27 @@ function createOption(field, globalLongOptionFlags) {
|
|
|
608
608
|
return [option];
|
|
609
609
|
}
|
|
610
610
|
const ALWAYS_GLOBAL_LONG_OPTION_FLAGS = ["--yes", "--output", "--debug", "--verbose"];
|
|
611
|
-
function getGlobalLongOptionFlags(presetsEnabled) {
|
|
612
|
-
|
|
611
|
+
function getGlobalLongOptionFlags(presetsEnabled, versionEnabled = false) {
|
|
612
|
+
const flags = presetsEnabled
|
|
613
613
|
? ["--preset", ...ALWAYS_GLOBAL_LONG_OPTION_FLAGS]
|
|
614
|
-
: ALWAYS_GLOBAL_LONG_OPTION_FLAGS
|
|
614
|
+
: [...ALWAYS_GLOBAL_LONG_OPTION_FLAGS];
|
|
615
|
+
if (versionEnabled) {
|
|
616
|
+
flags.push("--version");
|
|
617
|
+
}
|
|
618
|
+
return new Set(flags);
|
|
619
|
+
}
|
|
620
|
+
function validateUniqueOptionFlags(fields) {
|
|
621
|
+
const fieldsByFlag = new Map();
|
|
622
|
+
for (const field of fields) {
|
|
623
|
+
if (field.positionalIndex !== undefined) {
|
|
624
|
+
continue;
|
|
625
|
+
}
|
|
626
|
+
const existing = fieldsByFlag.get(field.optionFlag);
|
|
627
|
+
if (existing !== undefined) {
|
|
628
|
+
throw new UserError(`Parameters "${existing.displayPath}" and "${field.displayPath}" use conflicting CLI flag "${field.optionFlag}".`);
|
|
629
|
+
}
|
|
630
|
+
fieldsByFlag.set(field.optionFlag, field);
|
|
631
|
+
}
|
|
615
632
|
}
|
|
616
633
|
function createCommanderOption(flags, description, field) {
|
|
617
634
|
const option = new Option(flags, description);
|
|
@@ -1011,7 +1028,7 @@ function buildUsageLine(breadcrumb, rootUsageName, suffix) {
|
|
|
1011
1028
|
}
|
|
1012
1029
|
function renderGroupHelp(group, breadcrumb, scope, casing, globalOptions, rootUsageName, isRoot) {
|
|
1013
1030
|
const sections = [];
|
|
1014
|
-
const globalLongOptionFlags = getGlobalLongOptionFlags(globalOptions.presetsEnabled);
|
|
1031
|
+
const globalLongOptionFlags = getGlobalLongOptionFlags(globalOptions.presetsEnabled, globalOptions.showVersion);
|
|
1015
1032
|
const commandRows = formatCommandRows(group, scope, casing, globalLongOptionFlags);
|
|
1016
1033
|
if (commandRows.length > 0) {
|
|
1017
1034
|
sections.push(`${text.sectionHeader("Commands")}\n${formatHelpCommandList(commandRows)}`);
|
|
@@ -1037,7 +1054,7 @@ function renderGroupHelp(group, breadcrumb, scope, casing, globalOptions, rootUs
|
|
|
1037
1054
|
}
|
|
1038
1055
|
function renderLeafHelp(command, breadcrumb, casing, globalOptions, rootUsageName) {
|
|
1039
1056
|
const sections = [];
|
|
1040
|
-
const globalLongOptionFlags = getGlobalLongOptionFlags(globalOptions.presetsEnabled);
|
|
1057
|
+
const globalLongOptionFlags = getGlobalLongOptionFlags(globalOptions.presetsEnabled, globalOptions.showVersion);
|
|
1041
1058
|
const collected = collectFields(command.params, casing, globalLongOptionFlags);
|
|
1042
1059
|
const fields = assignPositionals(collected.fields, command.positional);
|
|
1043
1060
|
const optionRows = fields
|
|
@@ -1115,6 +1132,7 @@ function createNodeCommand(node, casing, globalLongOptionFlags, execute, presets
|
|
|
1115
1132
|
const command = new CommanderCommand(node.name);
|
|
1116
1133
|
const collected = collectFields(node.params, casing, globalLongOptionFlags);
|
|
1117
1134
|
const fields = assignPositionals(collected.fields, node.positional);
|
|
1135
|
+
validateUniqueOptionFlags(fields);
|
|
1118
1136
|
if (node.description !== undefined) {
|
|
1119
1137
|
command.description(node.description);
|
|
1120
1138
|
}
|
|
@@ -1213,18 +1231,30 @@ function setNestedValue(target, path, value) {
|
|
|
1213
1231
|
let cursor = target;
|
|
1214
1232
|
for (let index = 0; index < path.length - 1; index += 1) {
|
|
1215
1233
|
const segment = path[index] ?? "";
|
|
1216
|
-
const existing = cursor
|
|
1234
|
+
const existing = Object.prototype.hasOwnProperty.call(cursor, segment)
|
|
1235
|
+
? cursor[segment]
|
|
1236
|
+
: undefined;
|
|
1217
1237
|
if (typeof existing === "object" && existing !== null) {
|
|
1218
1238
|
cursor = existing;
|
|
1219
1239
|
continue;
|
|
1220
1240
|
}
|
|
1221
1241
|
const next = {};
|
|
1222
|
-
cursor
|
|
1242
|
+
Object.defineProperty(cursor, segment, {
|
|
1243
|
+
value: next,
|
|
1244
|
+
enumerable: true,
|
|
1245
|
+
configurable: true,
|
|
1246
|
+
writable: true
|
|
1247
|
+
});
|
|
1223
1248
|
cursor = next;
|
|
1224
1249
|
}
|
|
1225
1250
|
const leaf = path[path.length - 1];
|
|
1226
1251
|
if (leaf !== undefined) {
|
|
1227
|
-
cursor
|
|
1252
|
+
Object.defineProperty(cursor, leaf, {
|
|
1253
|
+
value,
|
|
1254
|
+
enumerable: true,
|
|
1255
|
+
configurable: true,
|
|
1256
|
+
writable: true
|
|
1257
|
+
});
|
|
1228
1258
|
}
|
|
1229
1259
|
}
|
|
1230
1260
|
function formatResolvedValue(value) {
|
|
@@ -1345,7 +1375,10 @@ function createFs() {
|
|
|
1345
1375
|
catch {
|
|
1346
1376
|
return false;
|
|
1347
1377
|
}
|
|
1348
|
-
}
|
|
1378
|
+
},
|
|
1379
|
+
lstat: async (path) => lstat(path),
|
|
1380
|
+
rename: async (fromPath, toPath) => rename(fromPath, toPath),
|
|
1381
|
+
unlink: async (path) => unlink(path)
|
|
1349
1382
|
};
|
|
1350
1383
|
}
|
|
1351
1384
|
function createEnv(values = process.env) {
|
|
@@ -1605,7 +1638,10 @@ function createFixtureFs(definition) {
|
|
|
1605
1638
|
return Boolean(existsEntries[filePath]);
|
|
1606
1639
|
}
|
|
1607
1640
|
return Object.prototype.hasOwnProperty.call(readFileEntries, filePath);
|
|
1608
|
-
}
|
|
1641
|
+
},
|
|
1642
|
+
lstat: async () => ({ isSymbolicLink: () => false }),
|
|
1643
|
+
rename: async () => undefined,
|
|
1644
|
+
unlink: async () => undefined
|
|
1609
1645
|
};
|
|
1610
1646
|
}
|
|
1611
1647
|
function resolveFixtureMethodResult(methodName, definition, args) {
|
|
@@ -2843,7 +2879,8 @@ function configureCommanderSuggestionOutput(command) {
|
|
|
2843
2879
|
}
|
|
2844
2880
|
export async function runCLI(roots, options = {}) {
|
|
2845
2881
|
enableSourceMaps();
|
|
2846
|
-
const
|
|
2882
|
+
const normalizedRoot = normalizeRoots(roots, process.argv);
|
|
2883
|
+
const root = options.approvals === false ? normalizedRoot : mergeApprovalsGroup(normalizedRoot);
|
|
2847
2884
|
await resolveMcpProxies(root, { projectRoot: options.projectRoot });
|
|
2848
2885
|
const casing = options.casing ?? "kebab";
|
|
2849
2886
|
const services = (options.services ?? {});
|
|
@@ -2869,7 +2906,7 @@ export async function runCLI(roots, options = {}) {
|
|
|
2869
2906
|
program.showHelpAfterError();
|
|
2870
2907
|
program.addHelpCommand(false);
|
|
2871
2908
|
const presetsEnabled = options.presets === true;
|
|
2872
|
-
const globalLongOptionFlags = getGlobalLongOptionFlags(presetsEnabled);
|
|
2909
|
+
const globalLongOptionFlags = getGlobalLongOptionFlags(presetsEnabled, version !== undefined);
|
|
2873
2910
|
addGlobalOptions(program, presetsEnabled);
|
|
2874
2911
|
if (version !== undefined) {
|
|
2875
2912
|
program.version(version, "--version");
|
package/dist/error-report.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { mkdir, writeFile } from "node:fs/promises";
|
|
1
|
+
import { mkdir, realpath, writeFile } from "node:fs/promises";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
2
3
|
import os from "node:os";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
import { CommanderError } from "commander";
|
|
@@ -46,6 +47,26 @@ function resolveReportDir(option, projectRoot) {
|
|
|
46
47
|
}
|
|
47
48
|
return path.isAbsolute(configuredDir) ? configuredDir : path.join(projectRoot, configuredDir);
|
|
48
49
|
}
|
|
50
|
+
function reportDirMustStayWithinProject(option) {
|
|
51
|
+
const configuredDir = typeof option === "object" ? option.dir : undefined;
|
|
52
|
+
return configuredDir === undefined || configuredDir.length === 0 || !path.isAbsolute(configuredDir);
|
|
53
|
+
}
|
|
54
|
+
function isWithinDirectory(parent, child) {
|
|
55
|
+
const relative = path.relative(parent, child);
|
|
56
|
+
return relative === "" || (!path.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path.sep}`));
|
|
57
|
+
}
|
|
58
|
+
async function assertReportDirWithinProject(projectRoot, reportDir) {
|
|
59
|
+
if (!isWithinDirectory(path.resolve(projectRoot), path.resolve(reportDir))) {
|
|
60
|
+
throw new Error("Error report directory resolves outside project root.");
|
|
61
|
+
}
|
|
62
|
+
const [canonicalProjectRoot, canonicalReportDir] = await Promise.all([
|
|
63
|
+
realpath(projectRoot),
|
|
64
|
+
realpath(reportDir)
|
|
65
|
+
]);
|
|
66
|
+
if (!isWithinDirectory(canonicalProjectRoot, canonicalReportDir)) {
|
|
67
|
+
throw new Error("Error report directory resolves outside project root.");
|
|
68
|
+
}
|
|
69
|
+
}
|
|
49
70
|
function resolveProjectRoot(projectRoot) {
|
|
50
71
|
if (projectRoot !== undefined) {
|
|
51
72
|
return projectRoot;
|
|
@@ -204,7 +225,12 @@ function ownStructuredFields(error) {
|
|
|
204
225
|
if (key === "name" || key === "message" || key === "stack" || key === "cause") {
|
|
205
226
|
continue;
|
|
206
227
|
}
|
|
207
|
-
fields
|
|
228
|
+
Object.defineProperty(fields, key, {
|
|
229
|
+
value: redactStructuredErrorField(key, error[key]),
|
|
230
|
+
enumerable: true,
|
|
231
|
+
configurable: true,
|
|
232
|
+
writable: true
|
|
233
|
+
});
|
|
208
234
|
}
|
|
209
235
|
return fields;
|
|
210
236
|
}
|
|
@@ -318,9 +344,12 @@ export async function writeErrorReport(context) {
|
|
|
318
344
|
}
|
|
319
345
|
const projectRoot = resolveProjectRoot(context.projectRoot);
|
|
320
346
|
const reportDir = resolveReportDir(context.errorReports, projectRoot);
|
|
321
|
-
const fileName = `${formatTimestamp(new Date())}-${slugifyCommandPath(context.commandPath)}.log`;
|
|
347
|
+
const fileName = `${formatTimestamp(new Date())}-${slugifyCommandPath(context.commandPath)}-${randomUUID()}.log`;
|
|
322
348
|
const absolutePath = path.join(reportDir, fileName);
|
|
323
349
|
await mkdir(reportDir, { recursive: true });
|
|
350
|
+
if (reportDirMustStayWithinProject(context.errorReports)) {
|
|
351
|
+
await assertReportDirWithinProject(projectRoot, reportDir);
|
|
352
|
+
}
|
|
324
353
|
await writeFile(absolutePath, buildReport(context));
|
|
325
354
|
return {
|
|
326
355
|
absolutePath,
|
|
@@ -12,6 +12,7 @@ export interface ApprovalPayload {
|
|
|
12
12
|
error?: unknown;
|
|
13
13
|
}
|
|
14
14
|
export declare function ensureApprovalList(runtimeOptions: HumanInLoopRuntimeOptions | undefined, deps?: {
|
|
15
|
+
create?: boolean;
|
|
15
16
|
openTaskList?: (options: OpenTaskListOptions) => Promise<TaskList>;
|
|
16
17
|
}): Promise<{
|
|
17
18
|
taskList: TaskList;
|
|
@@ -10,7 +10,7 @@ export async function ensureApprovalList(runtimeOptions, deps = {}) {
|
|
|
10
10
|
throw new UserError("humanInLoop.taskList required for async-mode commands");
|
|
11
11
|
}
|
|
12
12
|
const listName = runtimeOptions.listName ?? DEFAULT_LIST_NAME;
|
|
13
|
-
const taskList = await resolveTaskList(runtimeOptions, runtimeOptions.taskList, deps.openTaskList ?? openTaskList);
|
|
13
|
+
const taskList = await resolveTaskList(runtimeOptions, runtimeOptions.taskList, deps.openTaskList ?? openTaskList, deps.create ?? true);
|
|
14
14
|
const tasks = taskList.list(listName);
|
|
15
15
|
if (!isListValidated(runtimeOptions, listName)) {
|
|
16
16
|
if (!isApprovalStateMachine(tasks.stateMachine)) {
|
|
@@ -52,21 +52,23 @@ export async function loadApproval(ctx) {
|
|
|
52
52
|
throw error;
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
|
-
async function resolveTaskList(runtimeOptions, taskList, openTaskListFn) {
|
|
55
|
+
async function resolveTaskList(runtimeOptions, taskList, openTaskListFn, create) {
|
|
56
56
|
if (!isTaskListConfig(taskList)) {
|
|
57
57
|
return taskList;
|
|
58
58
|
}
|
|
59
|
-
const cachedTaskList = openedTaskListsByRuntime.get(runtimeOptions);
|
|
59
|
+
const cachedTaskList = create ? openedTaskListsByRuntime.get(runtimeOptions) : undefined;
|
|
60
60
|
if (cachedTaskList !== undefined) {
|
|
61
61
|
return cachedTaskList;
|
|
62
62
|
}
|
|
63
63
|
const openedTaskList = openTaskListFn({
|
|
64
|
-
create
|
|
64
|
+
create,
|
|
65
65
|
type: taskList.format,
|
|
66
66
|
path: taskList.dir,
|
|
67
67
|
stateMachine: approvalStateMachine
|
|
68
68
|
});
|
|
69
|
-
|
|
69
|
+
if (create) {
|
|
70
|
+
openedTaskListsByRuntime.set(runtimeOptions, openedTaskList);
|
|
71
|
+
}
|
|
70
72
|
return openedTaskList;
|
|
71
73
|
}
|
|
72
74
|
function cacheValidatedList(runtimeOptions, listName) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { TaskNotFoundError } from "@poe-code/task-list";
|
|
1
2
|
import { S } from "toolcraft-schema";
|
|
2
3
|
import { UserError, defineCommand, defineGroup } from "../index.js";
|
|
3
4
|
import { ensureApprovalList } from "./approval-tasks.js";
|
|
@@ -11,6 +12,13 @@ const listParams = S.Object({
|
|
|
11
12
|
const showParams = S.Object({
|
|
12
13
|
approvalId: S.String()
|
|
13
14
|
});
|
|
15
|
+
const runParams = S.Object({
|
|
16
|
+
approvalId: S.String(),
|
|
17
|
+
dryRun: S.Optional(S.Boolean({
|
|
18
|
+
description: "Preview the approval without prompting or executing it",
|
|
19
|
+
scope: ["cli"]
|
|
20
|
+
}))
|
|
21
|
+
});
|
|
14
22
|
export const approvalsGroup = markApprovalsBuiltIn(defineGroup({
|
|
15
23
|
name: "approvals",
|
|
16
24
|
description: "Inspect and execute queued approvals.",
|
|
@@ -21,8 +29,16 @@ export const approvalsGroup = markApprovalsBuiltIn(defineGroup({
|
|
|
21
29
|
scope: listScope,
|
|
22
30
|
params: listParams,
|
|
23
31
|
handler: async ({ params, runtimeOptions }) => {
|
|
24
|
-
|
|
25
|
-
|
|
32
|
+
try {
|
|
33
|
+
const { tasks } = await ensureApprovalList(runtimeOptions, { create: false });
|
|
34
|
+
return loadApprovals(tasks, params.state);
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
if (isMissingStateError(error)) {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
throw error;
|
|
41
|
+
}
|
|
26
42
|
},
|
|
27
43
|
render: {
|
|
28
44
|
rich: (result, primitives) => renderApprovalList(result, primitives),
|
|
@@ -36,8 +52,16 @@ export const approvalsGroup = markApprovalsBuiltIn(defineGroup({
|
|
|
36
52
|
scope: listScope,
|
|
37
53
|
params: showParams,
|
|
38
54
|
handler: async ({ params, runtimeOptions }) => {
|
|
39
|
-
|
|
40
|
-
|
|
55
|
+
try {
|
|
56
|
+
const { tasks } = await ensureApprovalList(runtimeOptions, { create: false });
|
|
57
|
+
return tasks.get(params.approvalId);
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
if (isMissingStateError(error)) {
|
|
61
|
+
throw new TaskNotFoundError(`Task "approvals/${params.approvalId}" not found.`);
|
|
62
|
+
}
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
41
65
|
},
|
|
42
66
|
render: {
|
|
43
67
|
rich: (result, primitives) => renderApprovalDetails(result, primitives),
|
|
@@ -49,8 +73,22 @@ export const approvalsGroup = markApprovalsBuiltIn(defineGroup({
|
|
|
49
73
|
name: "run",
|
|
50
74
|
description: "Run one queued approval.",
|
|
51
75
|
scope: runScope,
|
|
52
|
-
params:
|
|
53
|
-
handler: async ({ params, runtimeOptions, root }) =>
|
|
76
|
+
params: runParams,
|
|
77
|
+
handler: async ({ params, runtimeOptions, root }) => {
|
|
78
|
+
if (params.dryRun === true) {
|
|
79
|
+
const { tasks } = await ensureApprovalList(runtimeOptions, { create: false });
|
|
80
|
+
return tasks.get(params.approvalId);
|
|
81
|
+
}
|
|
82
|
+
return runApproval(params.approvalId, runtimeOptions, root);
|
|
83
|
+
},
|
|
84
|
+
render: {
|
|
85
|
+
rich: (result, primitives) => {
|
|
86
|
+
if (result)
|
|
87
|
+
renderApprovalDetails(result, primitives);
|
|
88
|
+
},
|
|
89
|
+
markdown: (result) => result ? renderApprovalDetailsMarkdown(result) : "",
|
|
90
|
+
json: (result) => result
|
|
91
|
+
}
|
|
54
92
|
})
|
|
55
93
|
]
|
|
56
94
|
}));
|
|
@@ -62,8 +100,10 @@ export function mergeApprovalsGroup(root) {
|
|
|
62
100
|
}
|
|
63
101
|
throw new UserError("'approvals' is reserved for human-in-loop built-ins");
|
|
64
102
|
}
|
|
65
|
-
|
|
66
|
-
|
|
103
|
+
return {
|
|
104
|
+
...root,
|
|
105
|
+
children: [...root.children, approvalsGroup]
|
|
106
|
+
};
|
|
67
107
|
}
|
|
68
108
|
function markApprovalsBuiltIn(group) {
|
|
69
109
|
Object.defineProperty(group, approvalsGroupSymbol, {
|
|
@@ -190,3 +230,6 @@ function stringifyValue(value) {
|
|
|
190
230
|
function escapeMarkdownCell(value) {
|
|
191
231
|
return value.replaceAll("|", "\\|");
|
|
192
232
|
}
|
|
233
|
+
function isMissingStateError(error) {
|
|
234
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
235
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { access, readFile, writeFile } from "node:fs/promises";
|
|
1
|
+
import { access, lstat, readFile, rename, unlink, writeFile } from "node:fs/promises";
|
|
2
|
+
import { InvalidTransitionError } from "@poe-code/task-list";
|
|
2
3
|
import { UserError, resolveCommandSecrets } from "../index.js";
|
|
3
4
|
import { ensureApprovalList } from "./approval-tasks.js";
|
|
4
5
|
import { resolveProvider } from "./gate.js";
|
|
@@ -11,6 +12,19 @@ export async function runApproval(approvalId, runtimeOptions, root) {
|
|
|
11
12
|
}
|
|
12
13
|
const approval = readApprovalPayload(task);
|
|
13
14
|
const provider = resolveProvider(runtimeOptions);
|
|
15
|
+
try {
|
|
16
|
+
await tasks.fire(approvalId, "claim", {
|
|
17
|
+
metadataPatch: {
|
|
18
|
+
pid: process.pid,
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
if (error instanceof InvalidTransitionError) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
throw error;
|
|
27
|
+
}
|
|
14
28
|
try {
|
|
15
29
|
const approvalResult = await provider.requestApproval({
|
|
16
30
|
message: approval.message,
|
|
@@ -28,14 +42,14 @@ export async function runApproval(approvalId, runtimeOptions, root) {
|
|
|
28
42
|
}
|
|
29
43
|
}
|
|
30
44
|
catch (error) {
|
|
31
|
-
await
|
|
45
|
+
await tasks.fire(approvalId, "fail", {
|
|
46
|
+
metadataPatch: {
|
|
47
|
+
error: errorMetadataFromUnknown(error),
|
|
48
|
+
},
|
|
49
|
+
});
|
|
32
50
|
return;
|
|
33
51
|
}
|
|
34
|
-
await tasks.fire(approvalId, "start"
|
|
35
|
-
metadataPatch: {
|
|
36
|
-
pid: process.pid,
|
|
37
|
-
},
|
|
38
|
-
});
|
|
52
|
+
await tasks.fire(approvalId, "start");
|
|
39
53
|
try {
|
|
40
54
|
const command = findCommand(root, approval.commandPath);
|
|
41
55
|
const ctx = createHandlerContext(command, approval.params);
|
|
@@ -182,6 +196,9 @@ function createFs() {
|
|
|
182
196
|
return false;
|
|
183
197
|
}
|
|
184
198
|
},
|
|
199
|
+
lstat: async (path) => lstat(path),
|
|
200
|
+
rename: async (fromPath, toPath) => rename(fromPath, toPath),
|
|
201
|
+
unlink: async (path) => unlink(path),
|
|
185
202
|
};
|
|
186
203
|
}
|
|
187
204
|
function createEnv(values = process.env) {
|
|
@@ -223,15 +240,3 @@ function errorMetadataFromUnknown(error) {
|
|
|
223
240
|
message: String(error),
|
|
224
241
|
};
|
|
225
242
|
}
|
|
226
|
-
async function failPendingApproval(tasks, approvalId, error) {
|
|
227
|
-
await tasks.fire(approvalId, "start", {
|
|
228
|
-
metadataPatch: {
|
|
229
|
-
pid: process.pid,
|
|
230
|
-
},
|
|
231
|
-
});
|
|
232
|
-
await tasks.fire(approvalId, "fail", {
|
|
233
|
-
metadataPatch: {
|
|
234
|
-
error: errorMetadataFromUnknown(error),
|
|
235
|
-
},
|
|
236
|
-
});
|
|
237
|
-
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { StateMachineDef } from "@poe-code/task-list";
|
|
2
|
-
export type ApprovalState = "pending" | "approved-running" | "approved-done" | "approved-failed" | "declined";
|
|
3
|
-
export type ApprovalEvent = "start" | "succeed" | "fail" | "decline";
|
|
4
|
-
export declare const approvalStateMachine: StateMachineDef<ApprovalState, ApprovalEvent
|
|
2
|
+
export type ApprovalState = "pending" | "prompting" | "approved-running" | "approved-done" | "approved-failed" | "declined";
|
|
3
|
+
export type ApprovalEvent = "claim" | "start" | "succeed" | "fail" | "decline";
|
|
4
|
+
export declare const approvalStateMachine: Readonly<StateMachineDef<ApprovalState, ApprovalEvent>>;
|
|
@@ -1,10 +1,18 @@
|
|
|
1
|
-
|
|
1
|
+
const approvalStateMachineDefinition = {
|
|
2
2
|
initial: "pending",
|
|
3
|
-
states: ["pending", "approved-running", "approved-done", "approved-failed", "declined"],
|
|
3
|
+
states: ["pending", "prompting", "approved-running", "approved-done", "approved-failed", "declined"],
|
|
4
4
|
events: {
|
|
5
|
-
|
|
5
|
+
claim: { from: ["pending"], to: "prompting" },
|
|
6
|
+
start: { from: ["prompting"], to: "approved-running" },
|
|
6
7
|
succeed: { from: ["approved-running"], to: "approved-done" },
|
|
7
|
-
fail: { from: ["approved-running"], to: "approved-failed" },
|
|
8
|
-
decline: { from: ["pending"], to: "declined" },
|
|
8
|
+
fail: { from: ["prompting", "approved-running"], to: "approved-failed" },
|
|
9
|
+
decline: { from: ["pending", "prompting"], to: "declined" },
|
|
9
10
|
},
|
|
10
11
|
};
|
|
12
|
+
Object.freeze(approvalStateMachineDefinition.states);
|
|
13
|
+
for (const event of Object.values(approvalStateMachineDefinition.events)) {
|
|
14
|
+
Object.freeze(event.from);
|
|
15
|
+
Object.freeze(event);
|
|
16
|
+
}
|
|
17
|
+
Object.freeze(approvalStateMachineDefinition.events);
|
|
18
|
+
export const approvalStateMachine = Object.freeze(approvalStateMachineDefinition);
|
package/dist/index.d.ts
CHANGED
|
@@ -35,6 +35,11 @@ export interface HandlerFs {
|
|
|
35
35
|
readFile(path: string, encoding?: BufferEncoding): Promise<string>;
|
|
36
36
|
writeFile(path: string, contents: string): Promise<void>;
|
|
37
37
|
exists(path: string): Promise<boolean>;
|
|
38
|
+
lstat(path: string): Promise<{
|
|
39
|
+
isSymbolicLink(): boolean;
|
|
40
|
+
}>;
|
|
41
|
+
rename(fromPath: string, toPath: string): Promise<void>;
|
|
42
|
+
unlink(path: string): Promise<void>;
|
|
38
43
|
}
|
|
39
44
|
export interface HandlerEnv {
|
|
40
45
|
get(key: string): string | undefined;
|
package/dist/index.js
CHANGED
|
@@ -211,7 +211,12 @@ export function resolveCommandSecrets(command, env = process.env) {
|
|
|
211
211
|
const suggestionLine = suggestions.length > 0 ? `\nDid you mean: ${suggestions.join(", ")}?` : "";
|
|
212
212
|
throw new UserError(`Missing required secret ${secret.env}${details}${suggestionLine}`);
|
|
213
213
|
}
|
|
214
|
-
secrets
|
|
214
|
+
Object.defineProperty(secrets, name, {
|
|
215
|
+
value,
|
|
216
|
+
enumerable: true,
|
|
217
|
+
configurable: true,
|
|
218
|
+
writable: true
|
|
219
|
+
});
|
|
215
220
|
}
|
|
216
221
|
return secrets;
|
|
217
222
|
}
|