toolcraft 0.0.166 → 0.0.168
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/composition.json +2 -2
- package/dist/composition.json +2 -2
- package/node_modules/@poe-code/process-runner/dist/docker/docker-execution-env.js +48 -28
- package/node_modules/@poe-code/process-runner/dist/types.d.ts +4 -1
- package/node_modules/tiny-stdio-mcp-server/dist/composition.json +1 -1
- package/node_modules/toolcraft-design/dist/explorer/filter.js +14 -7
- package/node_modules/toolcraft-design/dist/explorer/reducer.js +10 -7
- package/node_modules/toolcraft-design/dist/prompts/interactive/core.js +26 -4
- package/node_modules/toolcraft-schema/package.json +1 -1
- package/package.json +2 -2
package/composition.json
CHANGED
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
},
|
|
114
114
|
{
|
|
115
115
|
"name": "toolcraft",
|
|
116
|
-
"version": "0.0.
|
|
116
|
+
"version": "0.0.168",
|
|
117
117
|
"license": "MIT"
|
|
118
118
|
},
|
|
119
119
|
{
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
},
|
|
124
124
|
{
|
|
125
125
|
"name": "toolcraft-schema",
|
|
126
|
-
"version": "0.0.
|
|
126
|
+
"version": "0.0.168",
|
|
127
127
|
"license": "MIT"
|
|
128
128
|
},
|
|
129
129
|
{
|
package/dist/composition.json
CHANGED
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
},
|
|
114
114
|
{
|
|
115
115
|
"name": "toolcraft",
|
|
116
|
-
"version": "0.0.
|
|
116
|
+
"version": "0.0.168",
|
|
117
117
|
"license": "MIT"
|
|
118
118
|
},
|
|
119
119
|
{
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
},
|
|
124
124
|
{
|
|
125
125
|
"name": "toolcraft-schema",
|
|
126
|
-
"version": "0.0.
|
|
126
|
+
"version": "0.0.168",
|
|
127
127
|
"license": "MIT"
|
|
128
128
|
},
|
|
129
129
|
{
|
|
@@ -3,6 +3,7 @@ import { mkdtempSync, rmSync } from "node:fs";
|
|
|
3
3
|
import { readFile, realpath, writeFile } from "node:fs/promises";
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
5
|
import path from "node:path";
|
|
6
|
+
import { setTimeout as sleep } from "node:timers/promises";
|
|
6
7
|
import { buildDockerEnvArgs, buildDockerRunArgs } from "./args.js";
|
|
7
8
|
import { readDockerBuildContextFiles } from "./build-context.js";
|
|
8
9
|
import { buildContextArgs, detectContext } from "./context.js";
|
|
@@ -316,17 +317,24 @@ async function runAndRead(runner, spec) {
|
|
|
316
317
|
}
|
|
317
318
|
return output;
|
|
318
319
|
}
|
|
319
|
-
async function
|
|
320
|
-
|
|
321
|
-
const
|
|
322
|
-
const
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
320
|
+
async function runJobRead(runner, spec) {
|
|
321
|
+
spec.signal?.throwIfAborted();
|
|
322
|
+
const controller = new AbortController();
|
|
323
|
+
const signal = spec.signal
|
|
324
|
+
? AbortSignal.any([spec.signal, controller.signal])
|
|
325
|
+
: controller.signal;
|
|
326
|
+
const handle = runner.exec({ ...spec, signal });
|
|
327
|
+
const reads = [handle.result, readStreamBytes(handle.stdout), readStream(handle.stderr)];
|
|
328
|
+
try {
|
|
329
|
+
const [result, stdout, stderr] = await Promise.all(reads);
|
|
330
|
+
signal.throwIfAborted();
|
|
331
|
+
return { exitCode: result.exitCode, stdout, stderr };
|
|
332
|
+
}
|
|
333
|
+
catch (error) {
|
|
334
|
+
controller.abort();
|
|
335
|
+
await Promise.allSettled(reads);
|
|
336
|
+
throw error;
|
|
328
337
|
}
|
|
329
|
-
return output;
|
|
330
338
|
}
|
|
331
339
|
async function runOrThrow(runner, spec) {
|
|
332
340
|
await runAndRead(runner, spec);
|
|
@@ -498,15 +506,15 @@ function createContainerJob(containerId, runner, engine, context, detachedJobCon
|
|
|
498
506
|
envId: containerId,
|
|
499
507
|
tool: detachedJobContext?.tool ?? "docker",
|
|
500
508
|
argv: detachedJobContext?.argv ?? ["attach", containerId],
|
|
501
|
-
async status() {
|
|
509
|
+
async status(opts) {
|
|
502
510
|
if (detachedJobContext !== null) {
|
|
503
|
-
const detached = await readDetachedState(containerId, jobId, runner, engine, context);
|
|
511
|
+
const detached = await readDetachedState(containerId, jobId, runner, engine, context, opts?.signal);
|
|
504
512
|
if (detached.kind === "unreachable") {
|
|
505
513
|
return "lost";
|
|
506
514
|
}
|
|
507
515
|
return detached.kind === "exited" ? "exited" : "running";
|
|
508
516
|
}
|
|
509
|
-
const
|
|
517
|
+
const result = await runJobRead(runner, {
|
|
510
518
|
command: engine,
|
|
511
519
|
args: [
|
|
512
520
|
...buildContextArgs(engine, context),
|
|
@@ -516,14 +524,13 @@ function createContainerJob(containerId, runner, engine, context, detachedJobCon
|
|
|
516
524
|
containerId
|
|
517
525
|
],
|
|
518
526
|
stdout: "pipe",
|
|
519
|
-
stderr: "pipe"
|
|
527
|
+
stderr: "pipe",
|
|
528
|
+
signal: opts?.signal
|
|
520
529
|
});
|
|
521
|
-
const stdout = await readStream(handle.stdout);
|
|
522
|
-
const result = await handle.result;
|
|
523
530
|
if (result.exitCode !== 0) {
|
|
524
531
|
return "lost";
|
|
525
532
|
}
|
|
526
|
-
return stdout.trim() === "exited" ? "exited" : "running";
|
|
533
|
+
return result.stdout.toString("utf8").trim() === "exited" ? "exited" : "running";
|
|
527
534
|
},
|
|
528
535
|
async *stream(opts) {
|
|
529
536
|
const logFile = shellQuote(`/tmp/poe-jobs/${jobId}.log`);
|
|
@@ -533,8 +540,9 @@ function createContainerJob(containerId, runner, engine, context, detachedJobCon
|
|
|
533
540
|
let byteOffset = opts?.sinceByte ?? 0;
|
|
534
541
|
let pendingBytes = Buffer.alloc(0);
|
|
535
542
|
let pendingByteOffset = byteOffset;
|
|
543
|
+
let finalRead = false;
|
|
536
544
|
while (true) {
|
|
537
|
-
const
|
|
545
|
+
const spec = {
|
|
538
546
|
command: engine,
|
|
539
547
|
args: [
|
|
540
548
|
...buildContextArgs(engine, context),
|
|
@@ -545,8 +553,13 @@ function createContainerJob(containerId, runner, engine, context, detachedJobCon
|
|
|
545
553
|
`test -f ${logFile}${sinceCondition} && tail -c +${byteOffset + 1} ${logFile} || true`
|
|
546
554
|
],
|
|
547
555
|
stdout: "pipe",
|
|
548
|
-
stderr: "pipe"
|
|
549
|
-
|
|
556
|
+
stderr: "pipe",
|
|
557
|
+
signal: opts?.signal
|
|
558
|
+
};
|
|
559
|
+
const { stdout, stderr, exitCode } = await runJobRead(runner, spec);
|
|
560
|
+
if (exitCode !== 0) {
|
|
561
|
+
throw new Error(`Command failed with exit code ${exitCode}: ${spec.command} ${(spec.args ?? []).join(" ")}${stderr ? `\n${stderr}` : ""}`);
|
|
562
|
+
}
|
|
550
563
|
if (stdout.byteLength > 0) {
|
|
551
564
|
const combined = pendingBytes.byteLength === 0
|
|
552
565
|
? stdout
|
|
@@ -560,10 +573,18 @@ function createContainerJob(containerId, runner, engine, context, detachedJobCon
|
|
|
560
573
|
pendingByteOffset += completeLength;
|
|
561
574
|
}
|
|
562
575
|
}
|
|
563
|
-
if (opts?.follow !== true ||
|
|
576
|
+
if (opts?.follow !== true || finalRead) {
|
|
564
577
|
return;
|
|
565
578
|
}
|
|
566
|
-
|
|
579
|
+
const status = await this.status({ signal: opts?.signal });
|
|
580
|
+
if (status === "exited" && detachedJobContext !== null) {
|
|
581
|
+
finalRead = true;
|
|
582
|
+
continue;
|
|
583
|
+
}
|
|
584
|
+
if (status !== "running") {
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
await sleep(250, undefined, { signal: opts?.signal });
|
|
567
588
|
}
|
|
568
589
|
},
|
|
569
590
|
async wait() {
|
|
@@ -660,9 +681,9 @@ function utf8SequenceLength(byte) {
|
|
|
660
681
|
}
|
|
661
682
|
return 0;
|
|
662
683
|
}
|
|
663
|
-
async function readDetachedState(containerId, jobId, runner, engine, context) {
|
|
684
|
+
async function readDetachedState(containerId, jobId, runner, engine, context, signal) {
|
|
664
685
|
const exitFile = shellQuote(`/tmp/poe-jobs/${jobId}.exit`);
|
|
665
|
-
const
|
|
686
|
+
const result = await runJobRead(runner, {
|
|
666
687
|
command: engine,
|
|
667
688
|
args: [
|
|
668
689
|
...buildContextArgs(engine, context),
|
|
@@ -673,14 +694,13 @@ async function readDetachedState(containerId, jobId, runner, engine, context) {
|
|
|
673
694
|
`test -f ${exitFile} && cat ${exitFile} || true`
|
|
674
695
|
],
|
|
675
696
|
stdout: "pipe",
|
|
676
|
-
stderr: "pipe"
|
|
697
|
+
stderr: "pipe",
|
|
698
|
+
signal
|
|
677
699
|
});
|
|
678
|
-
const stdout = await readStream(handle.stdout);
|
|
679
|
-
const result = await handle.result;
|
|
680
700
|
if (result.exitCode !== 0) {
|
|
681
701
|
return { kind: "unreachable" };
|
|
682
702
|
}
|
|
683
|
-
const text = stdout.trim();
|
|
703
|
+
const text = result.stdout.toString("utf8").trim();
|
|
684
704
|
if (text.length === 0) {
|
|
685
705
|
return { kind: "running" };
|
|
686
706
|
}
|
|
@@ -127,11 +127,14 @@ export interface JobHandle {
|
|
|
127
127
|
readonly envId: string;
|
|
128
128
|
readonly tool: string;
|
|
129
129
|
readonly argv: string[];
|
|
130
|
-
status(
|
|
130
|
+
status(opts?: {
|
|
131
|
+
signal?: AbortSignal;
|
|
132
|
+
}): Promise<JobStatus>;
|
|
131
133
|
stream(opts?: {
|
|
132
134
|
sinceByte?: number;
|
|
133
135
|
since?: Date;
|
|
134
136
|
follow?: boolean;
|
|
137
|
+
signal?: AbortSignal;
|
|
135
138
|
}): AsyncIterable<LogChunk>;
|
|
136
139
|
wait(): Promise<{
|
|
137
140
|
exitCode: number;
|
|
@@ -28,17 +28,24 @@ function stripAnsi(value) {
|
|
|
28
28
|
return value.replace(/\u001b\[[0-9;]*m/g, "");
|
|
29
29
|
}
|
|
30
30
|
function matchSubsequence(query, text) {
|
|
31
|
+
const queryCharacters = Array.from(query);
|
|
32
|
+
const textCharacters = Array.from(text);
|
|
31
33
|
let previousStates = [];
|
|
32
|
-
for (let queryIndex = 0; queryIndex <
|
|
34
|
+
for (let queryIndex = 0; queryIndex < queryCharacters.length; queryIndex += 1) {
|
|
33
35
|
const states = [];
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
let offset = 0;
|
|
37
|
+
for (let textIndex = 0; textIndex < textCharacters.length; textIndex += 1) {
|
|
38
|
+
const character = textCharacters[textIndex];
|
|
39
|
+
const position = offset;
|
|
40
|
+
offset += character.length;
|
|
41
|
+
if (character !== queryCharacters[queryIndex]) {
|
|
36
42
|
continue;
|
|
37
43
|
}
|
|
44
|
+
const positions = character.length === 2 ? [position, position + 1] : [position];
|
|
38
45
|
if (queryIndex === 0) {
|
|
39
46
|
states[textIndex] = {
|
|
40
|
-
score: characterScore(
|
|
41
|
-
positions
|
|
47
|
+
score: characterScore(textCharacters, textIndex, undefined) + Math.max(0, EARLY_MATCH_BONUS - position),
|
|
48
|
+
positions
|
|
42
49
|
};
|
|
43
50
|
continue;
|
|
44
51
|
}
|
|
@@ -48,8 +55,8 @@ function matchSubsequence(query, text) {
|
|
|
48
55
|
continue;
|
|
49
56
|
}
|
|
50
57
|
const next = {
|
|
51
|
-
score: previous.score + characterScore(
|
|
52
|
-
positions: [...previous.positions,
|
|
58
|
+
score: previous.score + characterScore(textCharacters, textIndex, previousIndex),
|
|
59
|
+
positions: [...previous.positions, ...positions]
|
|
53
60
|
};
|
|
54
61
|
if (isBetterMatch(next, states[textIndex])) {
|
|
55
62
|
states[textIndex] = next;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { graphemes } from "../dashboard/terminal-width.js";
|
|
1
2
|
import { buildActionContext, resolveAction } from "./actions.js";
|
|
2
3
|
import { filterRows } from "./filter.js";
|
|
3
4
|
import { REGION_ALL, REGION_DETAIL, REGION_FOOTER, REGION_HEADER, REGION_LIST, REGION_MODAL, resolveExplorerLayoutMode } from "./state.js";
|
|
@@ -47,6 +48,10 @@ export function step(state, event, runtimeHandles = DEFAULT_ACTION_HANDLES) {
|
|
|
47
48
|
}
|
|
48
49
|
function stepKey(state, key, runtimeHandles) {
|
|
49
50
|
const target = state.bindings.resolve(key);
|
|
51
|
+
if (target?.type === "builtin" && target.id === "quit") {
|
|
52
|
+
const next = state.modal === null ? state : modalDismissed(state, null, runtimeHandles).state;
|
|
53
|
+
return { state: markDirty(next, 0), effects: [{ type: "exit", result: null }] };
|
|
54
|
+
}
|
|
50
55
|
if (state.modal !== null) {
|
|
51
56
|
return stepModalKey(state, key, target, runtimeHandles);
|
|
52
57
|
}
|
|
@@ -66,8 +71,6 @@ function stepKey(state, key, runtimeHandles) {
|
|
|
66
71
|
}
|
|
67
72
|
if (target?.type === "builtin") {
|
|
68
73
|
switch (target.id) {
|
|
69
|
-
case "quit":
|
|
70
|
-
return { state: markDirty(state, 0), effects: [{ type: "exit", result: null }] };
|
|
71
74
|
case "filter":
|
|
72
75
|
return focusFilter(state);
|
|
73
76
|
case "help":
|
|
@@ -126,8 +129,8 @@ function stepKey(state, key, runtimeHandles) {
|
|
|
126
129
|
}
|
|
127
130
|
if (isBackspace(key)) {
|
|
128
131
|
if (isSecondListFocused(state))
|
|
129
|
-
return updateDetailFilter(state, (state.detail.filter ?? "").slice(0, -1));
|
|
130
|
-
return updateFilter(state, state.filter.slice(0, -1));
|
|
132
|
+
return updateDetailFilter(state, graphemes(state.detail.filter ?? "").slice(0, -1).join(""));
|
|
133
|
+
return updateFilter(state, graphemes(state.filter).slice(0, -1).join(""));
|
|
131
134
|
}
|
|
132
135
|
if (!state.multiSelect && isSelectionSpace(key)) {
|
|
133
136
|
return mark(state, 0);
|
|
@@ -150,7 +153,7 @@ function stepFilterKey(state, key, target, runtimeHandles) {
|
|
|
150
153
|
};
|
|
151
154
|
}
|
|
152
155
|
if (isBackspace(key)) {
|
|
153
|
-
return updateFilter(state, state.filter.slice(0, -1));
|
|
156
|
+
return updateFilter(state, graphemes(state.filter).slice(0, -1).join(""));
|
|
154
157
|
}
|
|
155
158
|
if (isPrintable(key)) {
|
|
156
159
|
return updateFilter(state, `${state.filter}${key.ch}`);
|
|
@@ -166,7 +169,7 @@ function stepModalKey(state, key, target, runtimeHandles) {
|
|
|
166
169
|
return modalDismissed(state, state.modal.value, runtimeHandles);
|
|
167
170
|
}
|
|
168
171
|
if (isBackspace(key)) {
|
|
169
|
-
return setModal(state, { ...state.modal, value: state.modal.value.slice(0, -1) });
|
|
172
|
+
return setModal(state, { ...state.modal, value: graphemes(state.modal.value).slice(0, -1).join("") });
|
|
170
173
|
}
|
|
171
174
|
if (isPrintable(key)) {
|
|
172
175
|
return setModal(state, { ...state.modal, value: `${state.modal.value}${key.ch}` });
|
|
@@ -726,7 +729,7 @@ function paletteInput(state, key) {
|
|
|
726
729
|
return mark(state, 0);
|
|
727
730
|
}
|
|
728
731
|
if (isBackspace(key)) {
|
|
729
|
-
return setPaletteQuery(state, state.modal.query.slice(0, -1));
|
|
732
|
+
return setPaletteQuery(state, graphemes(state.modal.query).slice(0, -1).join(""));
|
|
730
733
|
}
|
|
731
734
|
if (isPrintable(key)) {
|
|
732
735
|
return setPaletteQuery(state, `${state.modal.query}${key.ch}`);
|
|
@@ -73,7 +73,19 @@ export class Prompt extends EventEmitter {
|
|
|
73
73
|
return Promise.resolve(CANCEL);
|
|
74
74
|
}
|
|
75
75
|
if (this.input.isTTY !== true) {
|
|
76
|
-
return this.promptNonTty()
|
|
76
|
+
return this.promptNonTty().then((value) => {
|
|
77
|
+
if (value === CANCEL || !this.trackValue) {
|
|
78
|
+
return value;
|
|
79
|
+
}
|
|
80
|
+
this.setValue(value);
|
|
81
|
+
const error = this.validate?.(this.value);
|
|
82
|
+
if (error) {
|
|
83
|
+
throw error instanceof Error ? error : new Error(error);
|
|
84
|
+
}
|
|
85
|
+
this.state = "submit";
|
|
86
|
+
this.emit("finalize");
|
|
87
|
+
return this.value;
|
|
88
|
+
});
|
|
77
89
|
}
|
|
78
90
|
if (this.input.destroyed || this.input.readableEnded) {
|
|
79
91
|
this.state = "cancel";
|
|
@@ -116,7 +128,7 @@ export class Prompt extends EventEmitter {
|
|
|
116
128
|
this.state = "cancel";
|
|
117
129
|
return Promise.resolve(CANCEL);
|
|
118
130
|
}
|
|
119
|
-
return new Promise((resolve) => {
|
|
131
|
+
return new Promise((resolve, reject) => {
|
|
120
132
|
let rl = null;
|
|
121
133
|
let settled = false;
|
|
122
134
|
const settle = (value) => {
|
|
@@ -126,21 +138,31 @@ export class Prompt extends EventEmitter {
|
|
|
126
138
|
settled = true;
|
|
127
139
|
this.input.removeListener("close", onCancel);
|
|
128
140
|
this.signal?.removeEventListener("abort", onCancel);
|
|
141
|
+
rl?.removeListener("error", settle);
|
|
142
|
+
rl?.removeListener("line", settle);
|
|
143
|
+
rl?.removeListener("close", onClose);
|
|
129
144
|
rl?.close();
|
|
130
|
-
|
|
145
|
+
if (value instanceof Error) {
|
|
146
|
+
reject(value);
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
resolve(value);
|
|
150
|
+
}
|
|
131
151
|
};
|
|
132
152
|
const onCancel = () => {
|
|
133
153
|
this.state = "cancel";
|
|
134
154
|
settle(CANCEL);
|
|
135
155
|
};
|
|
156
|
+
const onClose = () => settle(rl?.line ?? "");
|
|
136
157
|
this.input.once("close", onCancel);
|
|
137
158
|
rl = readline.createInterface({ input: this.input, terminal: false });
|
|
138
159
|
if (settled) {
|
|
139
160
|
rl.close();
|
|
140
161
|
return;
|
|
141
162
|
}
|
|
163
|
+
rl.once("error", settle);
|
|
142
164
|
rl.once("line", settle);
|
|
143
|
-
rl.once("close",
|
|
165
|
+
rl.once("close", onClose);
|
|
144
166
|
this.signal?.addEventListener("abort", onCancel, { once: true });
|
|
145
167
|
if (this.signal?.aborted) {
|
|
146
168
|
onCancel();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "toolcraft",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.168",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -158,7 +158,7 @@
|
|
|
158
158
|
"yaml"
|
|
159
159
|
],
|
|
160
160
|
"optionalDependencies": {
|
|
161
|
-
"toolcraft-schema": "0.0.
|
|
161
|
+
"toolcraft-schema": "0.0.168",
|
|
162
162
|
"toolcraft-design": "*",
|
|
163
163
|
"@poe-code/frontmatter": "*",
|
|
164
164
|
"@poe-code/agent-mcp-config": "*",
|