toolcraft 0.0.11 → 0.0.13
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/dist/cli.js +274 -160
- package/dist/renderer.d.ts +8 -2
- package/dist/renderer.js +71 -12
- package/node_modules/@poe-code/design-system/dist/components/help-formatter-plain.d.ts +4 -0
- package/node_modules/@poe-code/design-system/dist/components/help-formatter-plain.js +132 -0
- package/node_modules/@poe-code/design-system/dist/components/help-formatter.d.ts +13 -0
- package/node_modules/@poe-code/design-system/dist/components/help-formatter.js +116 -7
- package/node_modules/@poe-code/design-system/dist/components/index.d.ts +2 -2
- package/node_modules/@poe-code/design-system/dist/components/index.js +1 -1
- package/node_modules/@poe-code/design-system/dist/components/text.d.ts +1 -0
- package/node_modules/@poe-code/design-system/dist/components/text.js +8 -0
- package/node_modules/@poe-code/design-system/dist/index.d.ts +3 -2
- package/node_modules/@poe-code/design-system/dist/index.js +2 -1
- package/node_modules/@poe-code/process-runner/README.md +41 -0
- package/node_modules/@poe-code/process-runner/dist/docker/args.d.ts +2 -0
- package/node_modules/@poe-code/process-runner/dist/docker/args.js +40 -0
- package/node_modules/@poe-code/process-runner/dist/docker/context.d.ts +3 -0
- package/node_modules/@poe-code/process-runner/dist/docker/context.js +30 -0
- package/node_modules/@poe-code/process-runner/dist/docker/docker-execution-env.d.ts +28 -0
- package/node_modules/@poe-code/process-runner/dist/docker/docker-execution-env.js +428 -0
- package/node_modules/@poe-code/process-runner/dist/docker/docker-runner.d.ts +2 -0
- package/node_modules/@poe-code/process-runner/dist/docker/docker-runner.js +131 -0
- package/node_modules/@poe-code/process-runner/dist/docker/engine.d.ts +3 -0
- package/node_modules/@poe-code/process-runner/dist/docker/engine.js +24 -0
- package/node_modules/@poe-code/process-runner/dist/host/host-execution-env.d.ts +2 -0
- package/node_modules/@poe-code/process-runner/dist/host/host-execution-env.js +48 -0
- package/node_modules/@poe-code/process-runner/dist/host/host-runner.d.ts +3 -0
- package/node_modules/@poe-code/process-runner/dist/host/host-runner.js +74 -0
- package/node_modules/@poe-code/process-runner/dist/index.d.ts +8 -0
- package/node_modules/@poe-code/process-runner/dist/index.js +7 -0
- package/node_modules/@poe-code/process-runner/dist/testing/index.d.ts +2 -0
- package/node_modules/@poe-code/process-runner/dist/testing/index.js +1 -0
- package/node_modules/@poe-code/process-runner/dist/testing/mock-runner.d.ts +3 -0
- package/node_modules/@poe-code/process-runner/dist/testing/mock-runner.js +115 -0
- package/node_modules/@poe-code/process-runner/dist/testing/verify.d.ts +1 -0
- package/node_modules/@poe-code/process-runner/dist/testing/verify.js +359 -0
- package/node_modules/@poe-code/process-runner/dist/types.d.ts +180 -0
- package/node_modules/@poe-code/process-runner/dist/types.js +1 -0
- package/node_modules/@poe-code/process-runner/package.json +27 -0
- package/node_modules/@poe-code/task-list/README.md +49 -5
- package/node_modules/@poe-code/task-list/dist/backends/gh-issues-client.d.ts +19 -0
- package/node_modules/@poe-code/task-list/dist/backends/gh-issues-client.js +62 -0
- package/node_modules/@poe-code/task-list/dist/backends/gh-issues.d.ts +13 -0
- package/node_modules/@poe-code/task-list/dist/backends/gh-issues.js +627 -0
- package/node_modules/@poe-code/task-list/dist/backends/markdown-dir.js +253 -41
- package/node_modules/@poe-code/task-list/dist/backends/utils.d.ts +7 -1
- package/node_modules/@poe-code/task-list/dist/backends/utils.js +21 -0
- package/node_modules/@poe-code/task-list/dist/backends/yaml-file.js +171 -16
- package/node_modules/@poe-code/task-list/dist/index.d.ts +3 -1
- package/node_modules/@poe-code/task-list/dist/index.js +1 -1
- package/node_modules/@poe-code/task-list/dist/open.d.ts +4 -2
- package/node_modules/@poe-code/task-list/dist/open.js +27 -3
- package/node_modules/@poe-code/task-list/dist/types.d.ts +51 -3
- package/node_modules/@poe-code/task-list/dist/types.js +25 -0
- package/node_modules/@poe-code/task-list/package.json +1 -0
- package/package.json +11 -4
package/dist/renderer.d.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import type { Command, RenderPrimitives } from "./index.js";
|
|
2
2
|
export type OutputMode = "rich" | "md" | "json";
|
|
3
|
-
type
|
|
4
|
-
|
|
3
|
+
type WriteStream = "stdout" | "stderr";
|
|
4
|
+
type WriteFn = (chunk: string, stream?: WriteStream) => void;
|
|
5
|
+
export interface RenderResultStatus {
|
|
6
|
+
mcpError: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare function renderObjectTable(result: Record<string, unknown>, primitives: RenderPrimitives): string;
|
|
9
|
+
export declare function renderArrayTable(result: Array<Record<string, unknown>>, primitives: RenderPrimitives): string;
|
|
10
|
+
export declare function renderResult(command: Command<any, any, any, any>, result: unknown, output: OutputMode, primitives: RenderPrimitives, write?: WriteFn): RenderResultStatus;
|
|
5
11
|
export {};
|
package/dist/renderer.js
CHANGED
|
@@ -1,6 +1,47 @@
|
|
|
1
|
+
import YAML from "yaml";
|
|
1
2
|
function isObject(value) {
|
|
2
3
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
3
4
|
}
|
|
5
|
+
function isMcpCallToolResult(value) {
|
|
6
|
+
if (!isObject(value)) {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
const hasContent = Array.isArray(value.content);
|
|
10
|
+
const hasStructured = value.structuredContent !== undefined;
|
|
11
|
+
if (!hasContent && !hasStructured) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
return Object.keys(value).every((key) => key === "content" || key === "structuredContent" || key === "isError" || key === "_meta");
|
|
15
|
+
}
|
|
16
|
+
function isMcpTextContent(value) {
|
|
17
|
+
return isObject(value) && value.type === "text" && typeof value.text === "string";
|
|
18
|
+
}
|
|
19
|
+
function extractMcpPayload(envelope) {
|
|
20
|
+
const structuredContent = envelope.structuredContent;
|
|
21
|
+
if (isObject(structuredContent) && "result" in structuredContent) {
|
|
22
|
+
return structuredContent.result;
|
|
23
|
+
}
|
|
24
|
+
if (structuredContent !== undefined) {
|
|
25
|
+
return structuredContent;
|
|
26
|
+
}
|
|
27
|
+
if (Array.isArray(envelope.content)) {
|
|
28
|
+
const text = envelope.content
|
|
29
|
+
.filter(isMcpTextContent)
|
|
30
|
+
.map((block) => block.text)
|
|
31
|
+
.join("\n");
|
|
32
|
+
return text.length > 0 ? text : undefined;
|
|
33
|
+
}
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
function unwrapMcpEnvelope(result) {
|
|
37
|
+
if (!isMcpCallToolResult(result)) {
|
|
38
|
+
return { result, mcpError: false };
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
result: extractMcpPayload(result),
|
|
42
|
+
mcpError: result.isError === true,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
4
45
|
function isArrayOfObjects(value) {
|
|
5
46
|
return Array.isArray(value) && value.every((entry) => isObject(entry));
|
|
6
47
|
}
|
|
@@ -21,7 +62,7 @@ function stringifyJson(value, spaces) {
|
|
|
21
62
|
return String(value);
|
|
22
63
|
}
|
|
23
64
|
}
|
|
24
|
-
function renderObjectTable(result, primitives) {
|
|
65
|
+
export function renderObjectTable(result, primitives) {
|
|
25
66
|
const rows = Object.entries(result).map(([key, value]) => ({
|
|
26
67
|
key,
|
|
27
68
|
value: stringifyValue(value),
|
|
@@ -59,7 +100,7 @@ function getColumnNames(rows) {
|
|
|
59
100
|
}
|
|
60
101
|
return [...names];
|
|
61
102
|
}
|
|
62
|
-
function renderArrayTable(result, primitives) {
|
|
103
|
+
export function renderArrayTable(result, primitives) {
|
|
63
104
|
if (result.length === 0) {
|
|
64
105
|
return "[]";
|
|
65
106
|
}
|
|
@@ -87,7 +128,7 @@ function renderArrayMarkdown(result) {
|
|
|
87
128
|
.join(" | ")} |`);
|
|
88
129
|
return [header, separator, ...rows].join("\n");
|
|
89
130
|
}
|
|
90
|
-
function autoRender(result, output,
|
|
131
|
+
function autoRender(result, output, _primitives) {
|
|
91
132
|
if (result === null || result === undefined) {
|
|
92
133
|
if (output === "json") {
|
|
93
134
|
return stringifyJson({ ok: true }, 2);
|
|
@@ -100,14 +141,16 @@ function autoRender(result, output, primitives) {
|
|
|
100
141
|
}
|
|
101
142
|
return result;
|
|
102
143
|
}
|
|
144
|
+
if (output === "rich" && Array.isArray(result) && result.every((value) => typeof value === "string")) {
|
|
145
|
+
return result.join("\n");
|
|
146
|
+
}
|
|
103
147
|
if (isObject(result)) {
|
|
104
|
-
if (output === "rich") {
|
|
105
|
-
return renderObjectTable(result, primitives);
|
|
106
|
-
}
|
|
107
148
|
if (output === "md") {
|
|
108
149
|
return renderObjectMarkdown(result);
|
|
109
150
|
}
|
|
110
|
-
|
|
151
|
+
if (output === "json") {
|
|
152
|
+
return stringifyJson(result, 2);
|
|
153
|
+
}
|
|
111
154
|
}
|
|
112
155
|
if (isArrayOfObjects(result)) {
|
|
113
156
|
if (output === "md") {
|
|
@@ -116,33 +159,49 @@ function autoRender(result, output, primitives) {
|
|
|
116
159
|
if (output === "json") {
|
|
117
160
|
return stringifyJson(result, 2);
|
|
118
161
|
}
|
|
119
|
-
|
|
162
|
+
}
|
|
163
|
+
if (output === "rich") {
|
|
164
|
+
return YAML.stringify(result);
|
|
120
165
|
}
|
|
121
166
|
return stringifyJson(result, 2);
|
|
122
167
|
}
|
|
123
|
-
export function renderResult(command, result, output, primitives, write = (chunk) => {
|
|
168
|
+
export function renderResult(command, result, output, primitives, write = (chunk, stream = "stdout") => {
|
|
169
|
+
if (stream === "stderr") {
|
|
170
|
+
process.stderr.write(chunk);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
124
173
|
process.stdout.write(chunk);
|
|
125
174
|
}) {
|
|
175
|
+
const unwrapped = unwrapMcpEnvelope(result);
|
|
176
|
+
result = unwrapped.result;
|
|
177
|
+
if (unwrapped.mcpError) {
|
|
178
|
+
const payload = autoRender(result, output, primitives);
|
|
179
|
+
if (payload.length > 0) {
|
|
180
|
+
write(`${payload}\n`, "stderr");
|
|
181
|
+
}
|
|
182
|
+
return { mcpError: true };
|
|
183
|
+
}
|
|
126
184
|
if (output === "json" && command.render?.json) {
|
|
127
185
|
const payload = command.render.json(result, primitives);
|
|
128
186
|
if (payload !== undefined) {
|
|
129
187
|
write(`${stringifyJson(payload, 2)}\n`);
|
|
130
188
|
}
|
|
131
|
-
return;
|
|
189
|
+
return { mcpError: false };
|
|
132
190
|
}
|
|
133
191
|
if (output === "md" && command.render?.markdown) {
|
|
134
192
|
const payload = command.render.markdown(result, primitives);
|
|
135
193
|
if (typeof payload === "string" && payload.length > 0) {
|
|
136
194
|
write(`${payload}\n`);
|
|
137
195
|
}
|
|
138
|
-
return;
|
|
196
|
+
return { mcpError: false };
|
|
139
197
|
}
|
|
140
198
|
if (output === "rich" && command.render?.rich) {
|
|
141
199
|
command.render.rich(result, primitives);
|
|
142
|
-
return;
|
|
200
|
+
return { mcpError: false };
|
|
143
201
|
}
|
|
144
202
|
const payload = autoRender(result, output, primitives);
|
|
145
203
|
if (payload.length > 0) {
|
|
146
204
|
write(`${payload}\n`);
|
|
147
205
|
}
|
|
206
|
+
return { mcpError: false };
|
|
148
207
|
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { CommandInfo, FormatColumnsOptions, OptionInfo } from "./help-formatter.js";
|
|
2
|
+
export declare function formatColumns(opts: FormatColumnsOptions): string;
|
|
3
|
+
export declare function formatCommandList(commands: CommandInfo[]): string;
|
|
4
|
+
export declare function formatOptionList(options: OptionInfo[]): string;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
function stripAnsi(value) {
|
|
2
|
+
let output = "";
|
|
3
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
4
|
+
if (value[index] === "\u001b") {
|
|
5
|
+
if (value[index + 1] === "[") {
|
|
6
|
+
index += 2;
|
|
7
|
+
while (index < value.length) {
|
|
8
|
+
const code = value.charCodeAt(index);
|
|
9
|
+
if (code >= 0x40 && code <= 0x7e) {
|
|
10
|
+
break;
|
|
11
|
+
}
|
|
12
|
+
index += 1;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
output += value[index];
|
|
18
|
+
}
|
|
19
|
+
return output;
|
|
20
|
+
}
|
|
21
|
+
function toAscii(value) {
|
|
22
|
+
let output = "";
|
|
23
|
+
const stripped = stripAnsi(value);
|
|
24
|
+
for (let index = 0; index < stripped.length; index += 1) {
|
|
25
|
+
const code = stripped.charCodeAt(index);
|
|
26
|
+
output += code <= 0x7f ? stripped[index] : "?";
|
|
27
|
+
}
|
|
28
|
+
return output;
|
|
29
|
+
}
|
|
30
|
+
function clamp(value, min, max) {
|
|
31
|
+
return Math.min(Math.max(value, min), max);
|
|
32
|
+
}
|
|
33
|
+
function padEndVisible(value, width) {
|
|
34
|
+
return value + " ".repeat(Math.max(0, width - value.length));
|
|
35
|
+
}
|
|
36
|
+
function isWhitespace(char) {
|
|
37
|
+
return char === " " || char === "\n" || char === "\t" || char === "\r";
|
|
38
|
+
}
|
|
39
|
+
function splitWords(value) {
|
|
40
|
+
const words = [];
|
|
41
|
+
let word = "";
|
|
42
|
+
for (const char of value) {
|
|
43
|
+
if (isWhitespace(char)) {
|
|
44
|
+
if (word) {
|
|
45
|
+
words.push(word);
|
|
46
|
+
word = "";
|
|
47
|
+
}
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
word += char;
|
|
51
|
+
}
|
|
52
|
+
if (word) {
|
|
53
|
+
words.push(word);
|
|
54
|
+
}
|
|
55
|
+
return words;
|
|
56
|
+
}
|
|
57
|
+
function wrapWords(value, width) {
|
|
58
|
+
const words = splitWords(value);
|
|
59
|
+
if (words.length === 0) {
|
|
60
|
+
return [""];
|
|
61
|
+
}
|
|
62
|
+
const lines = [];
|
|
63
|
+
let line = "";
|
|
64
|
+
for (const word of words) {
|
|
65
|
+
if (!line) {
|
|
66
|
+
line = word;
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (line.length + 1 + word.length <= width) {
|
|
70
|
+
line += ` ${word}`;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
lines.push(line);
|
|
74
|
+
line = word;
|
|
75
|
+
}
|
|
76
|
+
lines.push(line);
|
|
77
|
+
return lines;
|
|
78
|
+
}
|
|
79
|
+
export function formatColumns(opts) {
|
|
80
|
+
const rows = opts.rows.map((row) => ({
|
|
81
|
+
left: toAscii(row.left),
|
|
82
|
+
right: toAscii(row.right)
|
|
83
|
+
}));
|
|
84
|
+
if (rows.length === 0) {
|
|
85
|
+
return "";
|
|
86
|
+
}
|
|
87
|
+
const totalWidth = opts.totalWidth ?? process.stdout.columns ?? 100;
|
|
88
|
+
const minLeftWidth = opts.minLeftWidth ?? 12;
|
|
89
|
+
const maxLeftWidth = opts.maxLeftWidth ?? 32;
|
|
90
|
+
const gap = opts.gap ?? 3;
|
|
91
|
+
const indent = opts.indent ?? 2;
|
|
92
|
+
const maxLeftContentWidth = Math.max(...rows.map((row) => row.left.length));
|
|
93
|
+
const leftWidth = clamp(maxLeftContentWidth + gap, minLeftWidth, maxLeftWidth);
|
|
94
|
+
const rightWidth = Math.max(20, totalWidth - leftWidth - indent);
|
|
95
|
+
const firstIndent = " ".repeat(indent);
|
|
96
|
+
const continuationIndent = " ".repeat(indent + leftWidth);
|
|
97
|
+
return rows
|
|
98
|
+
.flatMap((row) => {
|
|
99
|
+
if (row.right.length === 0) {
|
|
100
|
+
return [`${firstIndent}${row.left}`];
|
|
101
|
+
}
|
|
102
|
+
const rightLines = wrapWords(row.right, rightWidth);
|
|
103
|
+
if (row.left.length > leftWidth) {
|
|
104
|
+
return [
|
|
105
|
+
`${firstIndent}${row.left}`,
|
|
106
|
+
...rightLines.map((line) => `${continuationIndent}${line}`)
|
|
107
|
+
];
|
|
108
|
+
}
|
|
109
|
+
const firstLine = `${firstIndent}${padEndVisible(row.left, leftWidth)}${rightLines[0]}`;
|
|
110
|
+
const continuationLines = rightLines
|
|
111
|
+
.slice(1)
|
|
112
|
+
.map((line) => `${continuationIndent}${line}`);
|
|
113
|
+
return [firstLine, ...continuationLines];
|
|
114
|
+
})
|
|
115
|
+
.join("\n");
|
|
116
|
+
}
|
|
117
|
+
export function formatCommandList(commands) {
|
|
118
|
+
return formatColumns({
|
|
119
|
+
rows: commands.map((cmd) => ({
|
|
120
|
+
left: cmd.name,
|
|
121
|
+
right: cmd.description
|
|
122
|
+
}))
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
export function formatOptionList(options) {
|
|
126
|
+
return formatColumns({
|
|
127
|
+
rows: options.map((opt) => ({
|
|
128
|
+
left: opt.flags,
|
|
129
|
+
right: opt.description
|
|
130
|
+
}))
|
|
131
|
+
});
|
|
132
|
+
}
|
|
@@ -6,12 +6,25 @@ export interface OptionInfo {
|
|
|
6
6
|
flags: string;
|
|
7
7
|
description: string;
|
|
8
8
|
}
|
|
9
|
+
export interface FormatColumnsOptions {
|
|
10
|
+
rows: Array<{
|
|
11
|
+
left: string;
|
|
12
|
+
right: string;
|
|
13
|
+
}>;
|
|
14
|
+
totalWidth?: number;
|
|
15
|
+
minLeftWidth?: number;
|
|
16
|
+
maxLeftWidth?: number;
|
|
17
|
+
gap?: number;
|
|
18
|
+
indent?: number;
|
|
19
|
+
}
|
|
20
|
+
export declare function formatColumns(opts: FormatColumnsOptions): string;
|
|
9
21
|
export declare function formatCommand(name: string, description: string): string;
|
|
10
22
|
export declare function formatUsage(command: string, args?: string): string;
|
|
11
23
|
export declare function formatOption(flags: string, description: string): string;
|
|
12
24
|
export declare function formatCommandList(commands: CommandInfo[]): string;
|
|
13
25
|
export declare function formatOptionList(options: OptionInfo[]): string;
|
|
14
26
|
export declare const helpFormatter: {
|
|
27
|
+
readonly formatColumns: typeof formatColumns;
|
|
15
28
|
readonly formatCommand: typeof formatCommand;
|
|
16
29
|
readonly formatUsage: typeof formatUsage;
|
|
17
30
|
readonly formatOption: typeof formatOption;
|
|
@@ -1,24 +1,133 @@
|
|
|
1
1
|
import { text } from "./text.js";
|
|
2
|
-
|
|
2
|
+
function stripAnsi(value) {
|
|
3
|
+
let output = "";
|
|
4
|
+
for (let index = 0; index < value.length; index += 1)
|
|
5
|
+
if (value[index] === "\u001b" && value[index + 1] === "[")
|
|
6
|
+
while (index < value.length && value[index] !== "m")
|
|
7
|
+
index += 1;
|
|
8
|
+
else
|
|
9
|
+
output += value[index];
|
|
10
|
+
return output;
|
|
11
|
+
}
|
|
12
|
+
function visibleWidth(value) {
|
|
13
|
+
return stripAnsi(value).length;
|
|
14
|
+
}
|
|
15
|
+
function clamp(value, min, max) {
|
|
16
|
+
return Math.min(Math.max(value, min), max);
|
|
17
|
+
}
|
|
18
|
+
function padEndVisible(value, width) {
|
|
19
|
+
return value + " ".repeat(Math.max(0, width - visibleWidth(value)));
|
|
20
|
+
}
|
|
21
|
+
function isWhitespace(char) {
|
|
22
|
+
return char === " " || char === "\n" || char === "\t" || char === "\r";
|
|
23
|
+
}
|
|
24
|
+
function splitWords(value) {
|
|
25
|
+
const words = [];
|
|
26
|
+
let word = "";
|
|
27
|
+
for (const char of value) {
|
|
28
|
+
if (isWhitespace(char)) {
|
|
29
|
+
if (word) {
|
|
30
|
+
words.push(word);
|
|
31
|
+
word = "";
|
|
32
|
+
}
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
word += char;
|
|
36
|
+
}
|
|
37
|
+
if (word) {
|
|
38
|
+
words.push(word);
|
|
39
|
+
}
|
|
40
|
+
return words;
|
|
41
|
+
}
|
|
42
|
+
function wrapWords(value, width) {
|
|
43
|
+
const words = splitWords(value);
|
|
44
|
+
if (words.length === 0) {
|
|
45
|
+
return [""];
|
|
46
|
+
}
|
|
47
|
+
const lines = [];
|
|
48
|
+
let line = "";
|
|
49
|
+
for (const word of words) {
|
|
50
|
+
if (!line) {
|
|
51
|
+
line = word;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (visibleWidth(line) + 1 + visibleWidth(word) <= width) {
|
|
55
|
+
line += ` ${word}`;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
lines.push(line);
|
|
59
|
+
line = word;
|
|
60
|
+
}
|
|
61
|
+
lines.push(line);
|
|
62
|
+
return lines;
|
|
63
|
+
}
|
|
64
|
+
export function formatColumns(opts) {
|
|
65
|
+
const { rows } = opts;
|
|
66
|
+
if (rows.length === 0) {
|
|
67
|
+
return "";
|
|
68
|
+
}
|
|
69
|
+
const totalWidth = opts.totalWidth ?? process.stdout.columns ?? 100;
|
|
70
|
+
const minLeftWidth = opts.minLeftWidth ?? 12;
|
|
71
|
+
const maxLeftWidth = opts.maxLeftWidth ?? 32;
|
|
72
|
+
const gap = opts.gap ?? 3;
|
|
73
|
+
const indent = opts.indent ?? 2;
|
|
74
|
+
const maxLeftContentWidth = Math.max(...rows.map((row) => visibleWidth(row.left)));
|
|
75
|
+
const leftWidth = clamp(maxLeftContentWidth + gap, minLeftWidth, maxLeftWidth);
|
|
76
|
+
const rightWidth = Math.max(20, totalWidth - leftWidth - indent);
|
|
77
|
+
const firstIndent = " ".repeat(indent);
|
|
78
|
+
const continuationIndent = " ".repeat(indent + leftWidth);
|
|
79
|
+
return rows
|
|
80
|
+
.flatMap((row) => {
|
|
81
|
+
if (row.right.length === 0) {
|
|
82
|
+
return [`${firstIndent}${row.left}`];
|
|
83
|
+
}
|
|
84
|
+
const rightLines = wrapWords(row.right, rightWidth);
|
|
85
|
+
if (visibleWidth(row.left) > leftWidth) {
|
|
86
|
+
return [
|
|
87
|
+
`${firstIndent}${row.left}`,
|
|
88
|
+
...rightLines.map((line) => `${continuationIndent}${line}`)
|
|
89
|
+
];
|
|
90
|
+
}
|
|
91
|
+
const firstLine = `${firstIndent}${padEndVisible(row.left, leftWidth)}${rightLines[0]}`;
|
|
92
|
+
const continuationLines = rightLines
|
|
93
|
+
.slice(1)
|
|
94
|
+
.map((line) => `${continuationIndent}${line}`);
|
|
95
|
+
return [firstLine, ...continuationLines];
|
|
96
|
+
})
|
|
97
|
+
.join("\n");
|
|
98
|
+
}
|
|
3
99
|
export function formatCommand(name, description) {
|
|
4
|
-
|
|
5
|
-
|
|
100
|
+
return formatColumns({
|
|
101
|
+
rows: [{ left: text.command(name), right: description }]
|
|
102
|
+
});
|
|
6
103
|
}
|
|
7
104
|
export function formatUsage(command, args) {
|
|
8
105
|
const argsStr = args ? ` ${text.argument(args)}` : "";
|
|
9
106
|
return `${text.usageCommand(command)}${argsStr}`;
|
|
10
107
|
}
|
|
11
108
|
export function formatOption(flags, description) {
|
|
12
|
-
|
|
13
|
-
|
|
109
|
+
return formatColumns({
|
|
110
|
+
rows: [{ left: text.option(flags), right: description }]
|
|
111
|
+
});
|
|
14
112
|
}
|
|
15
113
|
export function formatCommandList(commands) {
|
|
16
|
-
return
|
|
114
|
+
return formatColumns({
|
|
115
|
+
rows: commands.map((cmd) => ({
|
|
116
|
+
left: text.command(cmd.name),
|
|
117
|
+
right: cmd.description
|
|
118
|
+
}))
|
|
119
|
+
});
|
|
17
120
|
}
|
|
18
121
|
export function formatOptionList(options) {
|
|
19
|
-
return
|
|
122
|
+
return formatColumns({
|
|
123
|
+
rows: options.map((opt) => ({
|
|
124
|
+
left: text.option(opt.flags),
|
|
125
|
+
right: opt.description
|
|
126
|
+
}))
|
|
127
|
+
});
|
|
20
128
|
}
|
|
21
129
|
export const helpFormatter = {
|
|
130
|
+
formatColumns,
|
|
22
131
|
formatCommand,
|
|
23
132
|
formatUsage,
|
|
24
133
|
formatOption,
|
|
@@ -2,8 +2,8 @@ export { text } from "./text.js";
|
|
|
2
2
|
export { symbols } from "./symbols.js";
|
|
3
3
|
export { createLogger, logger } from "./logger.js";
|
|
4
4
|
export type { LoggerOutput } from "./logger.js";
|
|
5
|
-
export { helpFormatter, formatCommand, formatUsage, formatOption, formatCommandList, formatOptionList } from "./help-formatter.js";
|
|
6
|
-
export type { CommandInfo, OptionInfo } from "./help-formatter.js";
|
|
5
|
+
export { helpFormatter, formatColumns, formatCommand, formatUsage, formatOption, formatCommandList, formatOptionList } from "./help-formatter.js";
|
|
6
|
+
export type { CommandInfo, OptionInfo, FormatColumnsOptions } from "./help-formatter.js";
|
|
7
7
|
export { formatCommandNotFound } from "./command-errors.js";
|
|
8
8
|
export { formatCommandNotFoundPanel } from "./command-errors.js";
|
|
9
9
|
export { renderTable } from "./table.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { text } from "./text.js";
|
|
2
2
|
export { symbols } from "./symbols.js";
|
|
3
3
|
export { createLogger, logger } from "./logger.js";
|
|
4
|
-
export { helpFormatter, formatCommand, formatUsage, formatOption, formatCommandList, formatOptionList } from "./help-formatter.js";
|
|
4
|
+
export { helpFormatter, formatColumns, formatCommand, formatUsage, formatOption, formatCommandList, formatOptionList } from "./help-formatter.js";
|
|
5
5
|
export { formatCommandNotFound } from "./command-errors.js";
|
|
6
6
|
export { formatCommandNotFoundPanel } from "./command-errors.js";
|
|
7
7
|
export { renderTable } from "./table.js";
|
|
@@ -2,6 +2,7 @@ export declare const text: {
|
|
|
2
2
|
readonly intro: (content: string) => string;
|
|
3
3
|
readonly heading: (content: string) => string;
|
|
4
4
|
readonly section: (content: string) => string;
|
|
5
|
+
readonly sectionHeader: (content: string) => string;
|
|
5
6
|
readonly command: (content: string) => string;
|
|
6
7
|
readonly argument: (content: string) => string;
|
|
7
8
|
readonly option: (content: string) => string;
|
|
@@ -27,6 +27,14 @@ export const text = {
|
|
|
27
27
|
return `**${content}**`;
|
|
28
28
|
return typography.bold(content);
|
|
29
29
|
},
|
|
30
|
+
sectionHeader(content) {
|
|
31
|
+
const format = resolveOutputFormat();
|
|
32
|
+
if (format === "json")
|
|
33
|
+
return content;
|
|
34
|
+
if (format === "markdown")
|
|
35
|
+
return `## ${content}`;
|
|
36
|
+
return typography.bold(content.toUpperCase());
|
|
37
|
+
},
|
|
30
38
|
command(content) {
|
|
31
39
|
const format = resolveOutputFormat();
|
|
32
40
|
if (format === "json")
|
|
@@ -8,8 +8,9 @@ export { text } from "./components/text.js";
|
|
|
8
8
|
export { symbols } from "./components/symbols.js";
|
|
9
9
|
export { createLogger, logger } from "./components/logger.js";
|
|
10
10
|
export type { LoggerOutput } from "./components/logger.js";
|
|
11
|
-
export { helpFormatter, formatCommand, formatUsage, formatOption, formatCommandList, formatOptionList } from "./components/help-formatter.js";
|
|
12
|
-
export
|
|
11
|
+
export { helpFormatter, formatColumns, formatCommand, formatUsage, formatOption, formatCommandList, formatOptionList } from "./components/help-formatter.js";
|
|
12
|
+
export * as helpFormatterPlain from "./components/help-formatter-plain.js";
|
|
13
|
+
export type { CommandInfo, OptionInfo, FormatColumnsOptions } from "./components/help-formatter.js";
|
|
13
14
|
export { formatCommandNotFound } from "./components/command-errors.js";
|
|
14
15
|
export { formatCommandNotFoundPanel } from "./components/command-errors.js";
|
|
15
16
|
export { renderTable } from "./components/table.js";
|
|
@@ -8,7 +8,8 @@ export { widths } from "./tokens/widths.js";
|
|
|
8
8
|
export { text } from "./components/text.js";
|
|
9
9
|
export { symbols } from "./components/symbols.js";
|
|
10
10
|
export { createLogger, logger } from "./components/logger.js";
|
|
11
|
-
export { helpFormatter, formatCommand, formatUsage, formatOption, formatCommandList, formatOptionList } from "./components/help-formatter.js";
|
|
11
|
+
export { helpFormatter, formatColumns, formatCommand, formatUsage, formatOption, formatCommandList, formatOptionList } from "./components/help-formatter.js";
|
|
12
|
+
export * as helpFormatterPlain from "./components/help-formatter-plain.js";
|
|
12
13
|
export { formatCommandNotFound } from "./components/command-errors.js";
|
|
13
14
|
export { formatCommandNotFoundPanel } from "./components/command-errors.js";
|
|
14
15
|
export { renderTable } from "./components/table.js";
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# @poe-code/process-runner
|
|
2
|
+
|
|
3
|
+
Low-level process execution abstraction. Single interface for launching processes on the host or inside Docker containers.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
- No external dependencies
|
|
8
|
+
- Consumed by `@poe-code/agent-spawn`
|
|
9
|
+
- Consumed by `process-launcher`
|
|
10
|
+
|
|
11
|
+
## Host Factory
|
|
12
|
+
|
|
13
|
+
- `createHostRunner(options)` returns a `Runner` that launches commands with `node:child_process.spawn`.
|
|
14
|
+
- `options.detached` starts the child as a detached process and uses process-group kill on Unix.
|
|
15
|
+
- `hostExecutionEnvFactory` implements the shared execution environment contract for local host execution.
|
|
16
|
+
- Host environments do not upload, download, detach, or reattach because the caller workspace is already local and there is no addressable remote environment.
|
|
17
|
+
|
|
18
|
+
## Docker Factories
|
|
19
|
+
|
|
20
|
+
- `createDockerRunner(options)` returns a one-shot `Runner` that executes each command in a Docker or Podman container.
|
|
21
|
+
- `dockerExecutionEnvFactory` creates an addressable long-lived container environment for Poe Code runtime jobs.
|
|
22
|
+
- Docker environments support workspace upload/download, command execution, interactive shell, detach, attach, and cleanup.
|
|
23
|
+
|
|
24
|
+
`DockerRunnerOptions`:
|
|
25
|
+
|
|
26
|
+
- `image`: container image to run.
|
|
27
|
+
- `engine`: `docker` or `podman`; detected when omitted.
|
|
28
|
+
- `context`: Docker context name; detected when omitted.
|
|
29
|
+
- `mounts`: container mounts.
|
|
30
|
+
- `ports`: port mappings.
|
|
31
|
+
- `network`: Docker network.
|
|
32
|
+
- `extraArgs`: additional runtime arguments.
|
|
33
|
+
- `containerName`: optional container name prefix.
|
|
34
|
+
|
|
35
|
+
## Environment variables
|
|
36
|
+
|
|
37
|
+
This package exposes no environment variables.
|
|
38
|
+
|
|
39
|
+
## Configuration
|
|
40
|
+
|
|
41
|
+
This package currently exposes no package-level configuration options.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
export function buildDockerRunArgs(input) {
|
|
3
|
+
const args = [input.engine];
|
|
4
|
+
if (input.engine === "docker" && input.context) {
|
|
5
|
+
args.push("--context", input.context);
|
|
6
|
+
}
|
|
7
|
+
args.push("run");
|
|
8
|
+
if (input.rm) {
|
|
9
|
+
args.push("--rm");
|
|
10
|
+
}
|
|
11
|
+
if (input.detached) {
|
|
12
|
+
args.push("-d");
|
|
13
|
+
}
|
|
14
|
+
if (input.interactive) {
|
|
15
|
+
args.push("-i");
|
|
16
|
+
}
|
|
17
|
+
if (input.tty) {
|
|
18
|
+
args.push("-t");
|
|
19
|
+
}
|
|
20
|
+
args.push("--name", input.containerName);
|
|
21
|
+
if (input.cwd !== undefined) {
|
|
22
|
+
args.push("-w", input.cwd);
|
|
23
|
+
}
|
|
24
|
+
for (const [key, value] of Object.entries(input.env ?? {})) {
|
|
25
|
+
args.push("-e", `${key}=${value}`);
|
|
26
|
+
}
|
|
27
|
+
for (const mount of input.mounts) {
|
|
28
|
+
const volume = `${path.resolve(mount.source)}:${mount.target}${mount.readonly ? ":ro" : ""}`;
|
|
29
|
+
args.push("-v", volume);
|
|
30
|
+
}
|
|
31
|
+
for (const port of input.ports) {
|
|
32
|
+
const mapping = `${port.host}:${port.container}${port.protocol === undefined || port.protocol === "tcp" ? "" : `/${port.protocol}`}`;
|
|
33
|
+
args.push("-p", mapping);
|
|
34
|
+
}
|
|
35
|
+
if (input.network !== undefined) {
|
|
36
|
+
args.push("--network", input.network);
|
|
37
|
+
}
|
|
38
|
+
args.push(...input.extraArgs, input.image, input.command, ...input.args);
|
|
39
|
+
return args;
|
|
40
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
export function detectContext() {
|
|
3
|
+
try {
|
|
4
|
+
const output = execSync("colima list --json", {
|
|
5
|
+
encoding: "utf-8",
|
|
6
|
+
stdio: ["pipe", "pipe", "ignore"]
|
|
7
|
+
});
|
|
8
|
+
const lines = output.trim().split("\n").filter(Boolean);
|
|
9
|
+
for (const line of lines) {
|
|
10
|
+
const profile = JSON.parse(line);
|
|
11
|
+
if (profile.status === "Running" && profile.runtime === "docker") {
|
|
12
|
+
const name = profile.name ?? profile.profile;
|
|
13
|
+
if (!name) {
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
return name === "default" ? "colima" : `colima-${name}`;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
export function buildContextArgs(engine, context) {
|
|
26
|
+
if (engine === "docker" && context) {
|
|
27
|
+
return ["--context", context];
|
|
28
|
+
}
|
|
29
|
+
return [];
|
|
30
|
+
}
|