toolcraft 0.0.22 → 0.0.24
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 +57 -14
- 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/docker-execution-env.js +244 -110
- package/node_modules/@poe-code/process-runner/dist/docker/docker-runner.js +16 -4
- 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 +16 -1
- 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 +3 -0
- package/node_modules/@poe-code/process-runner/dist/workspace-transfer.d.ts +57 -0
- package/node_modules/@poe-code/process-runner/dist/workspace-transfer.js +484 -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 +7 -0
- package/node_modules/auth-store/dist/encrypted-file-store.js +69 -7
- 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 +8 -4
- package/node_modules/tiny-mcp-client/dist/internal.js +237 -67
- 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 +279 -77
- 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";
|
|
@@ -434,7 +434,9 @@ function getErrorMessage(error) {
|
|
|
434
434
|
}
|
|
435
435
|
function formatJsonParseUserErrorMessage(label, filePath, source, error, options) {
|
|
436
436
|
const location = getJsonParseErrorLocation(error, source);
|
|
437
|
-
const message =
|
|
437
|
+
const message = location === null
|
|
438
|
+
? getErrorMessage(error)
|
|
439
|
+
: removeNativeJsonParseLocation(getErrorMessage(error), location);
|
|
438
440
|
const positionText = location === null ? "" : ` at line ${location.line} column ${location.column}`;
|
|
439
441
|
const formattedPath = options.quotePath ? `"${filePath}"` : filePath;
|
|
440
442
|
const snippet = location === null
|
|
@@ -447,6 +449,10 @@ function formatJsonParseUserErrorMessage(label, filePath, source, error, options
|
|
|
447
449
|
})}`;
|
|
448
450
|
return `${label} ${formattedPath} is not valid JSON: ${message}${positionText}.${snippet}`;
|
|
449
451
|
}
|
|
452
|
+
function removeNativeJsonParseLocation(message, location) {
|
|
453
|
+
const nativeSuffix = ` (line ${location.line} column ${location.column})`;
|
|
454
|
+
return message.endsWith(nativeSuffix) ? message.slice(0, -nativeSuffix.length) : message;
|
|
455
|
+
}
|
|
450
456
|
function getJsonParseErrorLocation(error, source) {
|
|
451
457
|
const causeLocation = getJsonParseCauseLocation(error);
|
|
452
458
|
if (causeLocation !== null) {
|
|
@@ -602,10 +608,27 @@ function createOption(field, globalLongOptionFlags) {
|
|
|
602
608
|
return [option];
|
|
603
609
|
}
|
|
604
610
|
const ALWAYS_GLOBAL_LONG_OPTION_FLAGS = ["--yes", "--output", "--debug", "--verbose"];
|
|
605
|
-
function getGlobalLongOptionFlags(presetsEnabled) {
|
|
606
|
-
|
|
611
|
+
function getGlobalLongOptionFlags(presetsEnabled, versionEnabled = false) {
|
|
612
|
+
const flags = presetsEnabled
|
|
607
613
|
? ["--preset", ...ALWAYS_GLOBAL_LONG_OPTION_FLAGS]
|
|
608
|
-
: 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
|
+
}
|
|
609
632
|
}
|
|
610
633
|
function createCommanderOption(flags, description, field) {
|
|
611
634
|
const option = new Option(flags, description);
|
|
@@ -1005,7 +1028,7 @@ function buildUsageLine(breadcrumb, rootUsageName, suffix) {
|
|
|
1005
1028
|
}
|
|
1006
1029
|
function renderGroupHelp(group, breadcrumb, scope, casing, globalOptions, rootUsageName, isRoot) {
|
|
1007
1030
|
const sections = [];
|
|
1008
|
-
const globalLongOptionFlags = getGlobalLongOptionFlags(globalOptions.presetsEnabled);
|
|
1031
|
+
const globalLongOptionFlags = getGlobalLongOptionFlags(globalOptions.presetsEnabled, globalOptions.showVersion);
|
|
1009
1032
|
const commandRows = formatCommandRows(group, scope, casing, globalLongOptionFlags);
|
|
1010
1033
|
if (commandRows.length > 0) {
|
|
1011
1034
|
sections.push(`${text.sectionHeader("Commands")}\n${formatHelpCommandList(commandRows)}`);
|
|
@@ -1031,7 +1054,7 @@ function renderGroupHelp(group, breadcrumb, scope, casing, globalOptions, rootUs
|
|
|
1031
1054
|
}
|
|
1032
1055
|
function renderLeafHelp(command, breadcrumb, casing, globalOptions, rootUsageName) {
|
|
1033
1056
|
const sections = [];
|
|
1034
|
-
const globalLongOptionFlags = getGlobalLongOptionFlags(globalOptions.presetsEnabled);
|
|
1057
|
+
const globalLongOptionFlags = getGlobalLongOptionFlags(globalOptions.presetsEnabled, globalOptions.showVersion);
|
|
1035
1058
|
const collected = collectFields(command.params, casing, globalLongOptionFlags);
|
|
1036
1059
|
const fields = assignPositionals(collected.fields, command.positional);
|
|
1037
1060
|
const optionRows = fields
|
|
@@ -1109,6 +1132,7 @@ function createNodeCommand(node, casing, globalLongOptionFlags, execute, presets
|
|
|
1109
1132
|
const command = new CommanderCommand(node.name);
|
|
1110
1133
|
const collected = collectFields(node.params, casing, globalLongOptionFlags);
|
|
1111
1134
|
const fields = assignPositionals(collected.fields, node.positional);
|
|
1135
|
+
validateUniqueOptionFlags(fields);
|
|
1112
1136
|
if (node.description !== undefined) {
|
|
1113
1137
|
command.description(node.description);
|
|
1114
1138
|
}
|
|
@@ -1207,18 +1231,30 @@ function setNestedValue(target, path, value) {
|
|
|
1207
1231
|
let cursor = target;
|
|
1208
1232
|
for (let index = 0; index < path.length - 1; index += 1) {
|
|
1209
1233
|
const segment = path[index] ?? "";
|
|
1210
|
-
const existing = cursor
|
|
1234
|
+
const existing = Object.prototype.hasOwnProperty.call(cursor, segment)
|
|
1235
|
+
? cursor[segment]
|
|
1236
|
+
: undefined;
|
|
1211
1237
|
if (typeof existing === "object" && existing !== null) {
|
|
1212
1238
|
cursor = existing;
|
|
1213
1239
|
continue;
|
|
1214
1240
|
}
|
|
1215
1241
|
const next = {};
|
|
1216
|
-
cursor
|
|
1242
|
+
Object.defineProperty(cursor, segment, {
|
|
1243
|
+
value: next,
|
|
1244
|
+
enumerable: true,
|
|
1245
|
+
configurable: true,
|
|
1246
|
+
writable: true
|
|
1247
|
+
});
|
|
1217
1248
|
cursor = next;
|
|
1218
1249
|
}
|
|
1219
1250
|
const leaf = path[path.length - 1];
|
|
1220
1251
|
if (leaf !== undefined) {
|
|
1221
|
-
cursor
|
|
1252
|
+
Object.defineProperty(cursor, leaf, {
|
|
1253
|
+
value,
|
|
1254
|
+
enumerable: true,
|
|
1255
|
+
configurable: true,
|
|
1256
|
+
writable: true
|
|
1257
|
+
});
|
|
1222
1258
|
}
|
|
1223
1259
|
}
|
|
1224
1260
|
function formatResolvedValue(value) {
|
|
@@ -1339,7 +1375,10 @@ function createFs() {
|
|
|
1339
1375
|
catch {
|
|
1340
1376
|
return false;
|
|
1341
1377
|
}
|
|
1342
|
-
}
|
|
1378
|
+
},
|
|
1379
|
+
lstat: async (path) => lstat(path),
|
|
1380
|
+
rename: async (fromPath, toPath) => rename(fromPath, toPath),
|
|
1381
|
+
unlink: async (path) => unlink(path)
|
|
1343
1382
|
};
|
|
1344
1383
|
}
|
|
1345
1384
|
function createEnv(values = process.env) {
|
|
@@ -1599,7 +1638,10 @@ function createFixtureFs(definition) {
|
|
|
1599
1638
|
return Boolean(existsEntries[filePath]);
|
|
1600
1639
|
}
|
|
1601
1640
|
return Object.prototype.hasOwnProperty.call(readFileEntries, filePath);
|
|
1602
|
-
}
|
|
1641
|
+
},
|
|
1642
|
+
lstat: async () => ({ isSymbolicLink: () => false }),
|
|
1643
|
+
rename: async () => undefined,
|
|
1644
|
+
unlink: async () => undefined
|
|
1603
1645
|
};
|
|
1604
1646
|
}
|
|
1605
1647
|
function resolveFixtureMethodResult(methodName, definition, args) {
|
|
@@ -2837,7 +2879,8 @@ function configureCommanderSuggestionOutput(command) {
|
|
|
2837
2879
|
}
|
|
2838
2880
|
export async function runCLI(roots, options = {}) {
|
|
2839
2881
|
enableSourceMaps();
|
|
2840
|
-
const
|
|
2882
|
+
const normalizedRoot = normalizeRoots(roots, process.argv);
|
|
2883
|
+
const root = options.approvals === false ? normalizedRoot : mergeApprovalsGroup(normalizedRoot);
|
|
2841
2884
|
await resolveMcpProxies(root, { projectRoot: options.projectRoot });
|
|
2842
2885
|
const casing = options.casing ?? "kebab";
|
|
2843
2886
|
const services = (options.services ?? {});
|
|
@@ -2863,7 +2906,7 @@ export async function runCLI(roots, options = {}) {
|
|
|
2863
2906
|
program.showHelpAfterError();
|
|
2864
2907
|
program.addHelpCommand(false);
|
|
2865
2908
|
const presetsEnabled = options.presets === true;
|
|
2866
|
-
const globalLongOptionFlags = getGlobalLongOptionFlags(presetsEnabled);
|
|
2909
|
+
const globalLongOptionFlags = getGlobalLongOptionFlags(presetsEnabled, version !== undefined);
|
|
2867
2910
|
addGlobalOptions(program, presetsEnabled);
|
|
2868
2911
|
if (version !== undefined) {
|
|
2869
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
|
}
|