poe-code 3.0.305 → 3.0.307
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/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/metafile.json +1 -1
- package/package.json +1 -1
- package/packages/memory/dist/index.js.map +1 -1
- package/packages/tiny-stdio-mcp-server/dist/schema.d.ts +2 -2
- package/packages/tiny-stdio-mcp-test-server/dist/cli-support.d.ts +4 -0
- package/packages/tiny-stdio-mcp-test-server/dist/cli-support.js +23 -0
- package/packages/tiny-stdio-mcp-test-server/dist/cli.js +40 -11
- package/packages/tiny-stdio-mcp-test-server/dist/index.js +4 -1
- package/packages/toolcraft-landing-page/dist/render.js +99 -1
- package/packages/toolcraft-landing-page/dist/template.js +6 -4
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { JSONSchema } from "./types.js";
|
|
2
|
-
type SchemaPropertyType = "string" | "number" | "boolean" | "object" | "array";
|
|
2
|
+
type SchemaPropertyType = "string" | "number" | "integer" | "boolean" | "object" | "array";
|
|
3
3
|
interface SchemaPropertyDef {
|
|
4
4
|
type: SchemaPropertyType;
|
|
5
5
|
description?: string;
|
|
@@ -7,7 +7,7 @@ interface SchemaPropertyDef {
|
|
|
7
7
|
[keyword: string]: unknown;
|
|
8
8
|
}
|
|
9
9
|
type SchemaDefinition = Record<string, SchemaPropertyDef>;
|
|
10
|
-
type InferType<T extends SchemaPropertyType> = T extends "string" ? string : T extends "number" ? number : T extends "boolean" ? boolean : T extends "object" ? Record<string, unknown> : T extends "array" ? unknown[] : never;
|
|
10
|
+
type InferType<T extends SchemaPropertyType> = T extends "string" ? string : T extends "number" | "integer" ? number : T extends "boolean" ? boolean : T extends "object" ? Record<string, unknown> : T extends "array" ? unknown[] : never;
|
|
11
11
|
type InferSchema<T extends SchemaDefinition> = {
|
|
12
12
|
[K in keyof T as T[K]["optional"] extends true ? never : K]: InferType<T[K]["type"]>;
|
|
13
13
|
} & {
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const SERVE_TOOL_NAMES: readonly ["encrypt", "word-of-the-day"];
|
|
2
|
+
export type ServeToolName = typeof SERVE_TOOL_NAMES[number];
|
|
3
|
+
export declare function isServeToolName(value: string): value is ServeToolName;
|
|
4
|
+
export declare function getNextSpawnCount(currentValue: string | undefined): number;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export const SERVE_TOOL_NAMES = ["encrypt", "word-of-the-day"];
|
|
2
|
+
export function isServeToolName(value) {
|
|
3
|
+
return SERVE_TOOL_NAMES.includes(value);
|
|
4
|
+
}
|
|
5
|
+
function parseSpawnCount(value) {
|
|
6
|
+
const trimmed = value.trim();
|
|
7
|
+
if (trimmed === "") {
|
|
8
|
+
return 0;
|
|
9
|
+
}
|
|
10
|
+
for (const character of trimmed) {
|
|
11
|
+
if (character < "0" || character > "9") {
|
|
12
|
+
throw new Error("TOOLCRAFT_TEST_SPAWN_COUNT_FILE must contain a non-negative integer");
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
const count = Number(trimmed);
|
|
16
|
+
if (!Number.isSafeInteger(count)) {
|
|
17
|
+
throw new Error("TOOLCRAFT_TEST_SPAWN_COUNT_FILE must contain a non-negative integer");
|
|
18
|
+
}
|
|
19
|
+
return count;
|
|
20
|
+
}
|
|
21
|
+
export function getNextSpawnCount(currentValue) {
|
|
22
|
+
return (currentValue === undefined ? 0 : parseSpawnCount(currentValue)) + 1;
|
|
23
|
+
}
|
|
@@ -1446,7 +1446,7 @@ var SERVER_VERSION = package_default.version;
|
|
|
1446
1446
|
var caesarCipherSchema = defineSchema({
|
|
1447
1447
|
text: { type: "string", description: "The text to encrypt" },
|
|
1448
1448
|
shift: {
|
|
1449
|
-
type: "
|
|
1449
|
+
type: "integer",
|
|
1450
1450
|
description: "The shift amount (default: 3)",
|
|
1451
1451
|
optional: true
|
|
1452
1452
|
}
|
|
@@ -1502,16 +1502,51 @@ function createWordOfTheDayServer() {
|
|
|
1502
1502
|
"Returns the word of the day",
|
|
1503
1503
|
wordOfTheDaySchema,
|
|
1504
1504
|
() => {
|
|
1505
|
+
recordToolCall("word_of_the_day");
|
|
1505
1506
|
return "Bumfuzzle - to confuse or fluster someone";
|
|
1506
1507
|
}
|
|
1507
1508
|
);
|
|
1508
1509
|
}
|
|
1509
1510
|
|
|
1511
|
+
// packages/tiny-stdio-mcp-test-server/src/cli-support.ts
|
|
1512
|
+
var SERVE_TOOL_NAMES = ["encrypt", "word-of-the-day"];
|
|
1513
|
+
function isServeToolName(value) {
|
|
1514
|
+
return SERVE_TOOL_NAMES.includes(value);
|
|
1515
|
+
}
|
|
1516
|
+
function parseSpawnCount(value) {
|
|
1517
|
+
const trimmed = value.trim();
|
|
1518
|
+
if (trimmed === "") {
|
|
1519
|
+
return 0;
|
|
1520
|
+
}
|
|
1521
|
+
for (const character of trimmed) {
|
|
1522
|
+
if (character < "0" || character > "9") {
|
|
1523
|
+
throw new Error("TOOLCRAFT_TEST_SPAWN_COUNT_FILE must contain a non-negative integer");
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
const count = Number(trimmed);
|
|
1527
|
+
if (!Number.isSafeInteger(count)) {
|
|
1528
|
+
throw new Error("TOOLCRAFT_TEST_SPAWN_COUNT_FILE must contain a non-negative integer");
|
|
1529
|
+
}
|
|
1530
|
+
return count;
|
|
1531
|
+
}
|
|
1532
|
+
function getNextSpawnCount(currentValue) {
|
|
1533
|
+
return (currentValue === void 0 ? 0 : parseSpawnCount(currentValue)) + 1;
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1510
1536
|
// packages/tiny-stdio-mcp-test-server/src/cli.ts
|
|
1511
1537
|
var program = new Command();
|
|
1512
1538
|
program.name("tiny-stdio-mcp-test-server").description("Test MCP server with example tools for integration testing").version(package_default.version);
|
|
1513
1539
|
program.command("serve").description("Start an MCP server on stdin/stdout").argument("<tool>", "Tool to serve (encrypt, word-of-the-day)").action(async (tool) => {
|
|
1514
|
-
|
|
1540
|
+
if (!isServeToolName(tool)) {
|
|
1541
|
+
console.error(`Unknown tool: ${tool}. Available: ${SERVE_TOOL_NAMES.join(", ")}`);
|
|
1542
|
+
process.exit(1);
|
|
1543
|
+
}
|
|
1544
|
+
try {
|
|
1545
|
+
recordProcessStart();
|
|
1546
|
+
} catch (error) {
|
|
1547
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
1548
|
+
process.exit(1);
|
|
1549
|
+
}
|
|
1515
1550
|
const startupDelayMs = Number(process.env.TOOLCRAFT_TEST_STARTUP_DELAY_MS ?? "0");
|
|
1516
1551
|
if (startupDelayMs > 0) {
|
|
1517
1552
|
await new Promise((resolve) => setTimeout(resolve, startupDelayMs));
|
|
@@ -1525,20 +1560,14 @@ program.command("serve").description("Start an MCP server on stdin/stdout").argu
|
|
|
1525
1560
|
encrypt: () => createEncryptServer().listen(),
|
|
1526
1561
|
"word-of-the-day": () => createWordOfTheDayServer().listen()
|
|
1527
1562
|
});
|
|
1528
|
-
|
|
1529
|
-
if (!start) {
|
|
1530
|
-
const available = Object.keys(servers).join(", ");
|
|
1531
|
-
console.error(`Unknown tool: ${tool}. Available: ${available}`);
|
|
1532
|
-
process.exit(1);
|
|
1533
|
-
}
|
|
1534
|
-
await start();
|
|
1563
|
+
await servers[tool]();
|
|
1535
1564
|
});
|
|
1536
1565
|
program.parse();
|
|
1537
1566
|
function recordProcessStart() {
|
|
1538
1567
|
const countFile = process.env.TOOLCRAFT_TEST_SPAWN_COUNT_FILE;
|
|
1539
1568
|
if (countFile !== void 0) {
|
|
1540
|
-
const
|
|
1541
|
-
writeFileSync(countFile, String(
|
|
1569
|
+
const currentValue = existsSync(countFile) ? readFileSync(countFile, "utf8") : void 0;
|
|
1570
|
+
writeFileSync(countFile, String(getNextSpawnCount(currentValue)));
|
|
1542
1571
|
}
|
|
1543
1572
|
const pidFile = process.env.TOOLCRAFT_TEST_WRAPPER_PID_FILE;
|
|
1544
1573
|
if (pidFile !== void 0) {
|
|
@@ -6,7 +6,7 @@ const SERVER_VERSION = packageJson.version;
|
|
|
6
6
|
const caesarCipherSchema = defineSchema({
|
|
7
7
|
text: { type: "string", description: "The text to encrypt" },
|
|
8
8
|
shift: {
|
|
9
|
-
type: "
|
|
9
|
+
type: "integer",
|
|
10
10
|
description: "The shift amount (default: 3)",
|
|
11
11
|
optional: true,
|
|
12
12
|
},
|
|
@@ -55,6 +55,7 @@ export function createWordOfTheDayServer() {
|
|
|
55
55
|
})
|
|
56
56
|
// Deliberately text-only fixture: word of the day is human-readable prose.
|
|
57
57
|
.tool("word_of_the_day", "Returns the word of the day", wordOfTheDaySchema, () => {
|
|
58
|
+
recordToolCall("word_of_the_day");
|
|
58
59
|
return "Bumfuzzle - to confuse or fluster someone";
|
|
59
60
|
});
|
|
60
61
|
}
|
|
@@ -65,11 +66,13 @@ export function createTestServer() {
|
|
|
65
66
|
});
|
|
66
67
|
// Deliberately text-only fixture: encryption returns human-readable text, not structured data.
|
|
67
68
|
server.tool("caesar_cipher_encrypt", "Encrypts text using the Caesar cipher", caesarCipherSchema, ({ text, shift }) => {
|
|
69
|
+
recordToolCall("caesar_cipher_encrypt");
|
|
68
70
|
const actualShift = shift ?? 3;
|
|
69
71
|
return caesarEncrypt(text, actualShift);
|
|
70
72
|
});
|
|
71
73
|
// Deliberately text-only fixture: word of the day is human-readable prose.
|
|
72
74
|
server.tool("word_of_the_day", "Returns the word of the day", wordOfTheDaySchema, () => {
|
|
75
|
+
recordToolCall("word_of_the_day");
|
|
73
76
|
return "Bumfuzzle - to confuse or fluster someone";
|
|
74
77
|
});
|
|
75
78
|
return server;
|
|
@@ -3,10 +3,108 @@ import { highlight } from "./highlight.js";
|
|
|
3
3
|
import { SCRIPT } from "./script.js";
|
|
4
4
|
import { CSS } from "./styles.js";
|
|
5
5
|
import { TEMPLATE } from "./template.js";
|
|
6
|
+
const DEFAULT_ACCENT = "#2563eb";
|
|
7
|
+
function isAsciiHexDigit(value) {
|
|
8
|
+
const code = value.charCodeAt(0);
|
|
9
|
+
return ((code >= 48 && code <= 57) ||
|
|
10
|
+
(code >= 65 && code <= 70) ||
|
|
11
|
+
(code >= 97 && code <= 102));
|
|
12
|
+
}
|
|
13
|
+
function isHexColor(value) {
|
|
14
|
+
if (!value.startsWith("#")) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
if (![4, 5, 7, 9].includes(value.length)) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
for (const char of value.slice(1)) {
|
|
21
|
+
if (!isAsciiHexDigit(char)) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
function isNamedColor(value) {
|
|
28
|
+
if (value.length === 0) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
for (const char of value) {
|
|
32
|
+
const code = char.charCodeAt(0);
|
|
33
|
+
if (!((code >= 65 && code <= 90) || (code >= 97 && code <= 122))) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
function sanitizeAccent(value) {
|
|
40
|
+
const trimmed = value.trim();
|
|
41
|
+
if (isHexColor(trimmed) || isNamedColor(trimmed)) {
|
|
42
|
+
return trimmed;
|
|
43
|
+
}
|
|
44
|
+
return DEFAULT_ACCENT;
|
|
45
|
+
}
|
|
46
|
+
function hasUnsafeHrefCharacter(value) {
|
|
47
|
+
for (const char of value) {
|
|
48
|
+
const code = char.charCodeAt(0);
|
|
49
|
+
if (code <= 32 || code === 127) {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
function schemeDelimiterIndex(value) {
|
|
56
|
+
const colon = value.indexOf(":");
|
|
57
|
+
if (colon === -1) {
|
|
58
|
+
return -1;
|
|
59
|
+
}
|
|
60
|
+
const slash = value.indexOf("/");
|
|
61
|
+
const question = value.indexOf("?");
|
|
62
|
+
const hash = value.indexOf("#");
|
|
63
|
+
const precedingDelimiters = [slash, question, hash].filter((index) => index !== -1);
|
|
64
|
+
if (precedingDelimiters.some((index) => index < colon)) {
|
|
65
|
+
return -1;
|
|
66
|
+
}
|
|
67
|
+
return colon;
|
|
68
|
+
}
|
|
69
|
+
function safeHref(value) {
|
|
70
|
+
if (value === undefined) {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
const trimmed = value.trim();
|
|
74
|
+
if (trimmed.length === 0 || hasUnsafeHrefCharacter(trimmed)) {
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
const delimiter = schemeDelimiterIndex(trimmed);
|
|
78
|
+
if (delimiter === -1) {
|
|
79
|
+
return trimmed;
|
|
80
|
+
}
|
|
81
|
+
const scheme = trimmed.slice(0, delimiter).toLowerCase();
|
|
82
|
+
return scheme === "http" || scheme === "https" ? trimmed : undefined;
|
|
83
|
+
}
|
|
84
|
+
function stripFragment(value) {
|
|
85
|
+
const hash = value.indexOf("#");
|
|
86
|
+
return hash === -1 ? value : value.slice(0, hash);
|
|
87
|
+
}
|
|
88
|
+
function docsHref(value) {
|
|
89
|
+
return stripFragment(safeHref(value) ?? "#docs");
|
|
90
|
+
}
|
|
91
|
+
function docsAnchorHref(baseHref, anchor) {
|
|
92
|
+
return baseHref === "#docs" ? baseHref : `${baseHref}#${anchor}`;
|
|
93
|
+
}
|
|
6
94
|
export function renderLandingPage(page) {
|
|
7
|
-
const
|
|
95
|
+
const accent = sanitizeAccent(page.accent);
|
|
96
|
+
const docsUrl = docsHref(page.docsUrl);
|
|
97
|
+
const styles = renderTemplate(CSS, { accent });
|
|
8
98
|
const view = {
|
|
9
99
|
...page,
|
|
100
|
+
accent,
|
|
101
|
+
repoUrl: safeHref(page.repoUrl),
|
|
102
|
+
docsUrl,
|
|
103
|
+
docsHelloWorldUrl: docsAnchorHref(docsUrl, "hello-world"),
|
|
104
|
+
docsRuntimeUrl: docsAnchorHref(docsUrl, "one-binary-three-runtimes"),
|
|
105
|
+
docsSecretsUrl: docsAnchorHref(docsUrl, "secrets"),
|
|
106
|
+
docsMigrationUrl: docsAnchorHref(docsUrl, "migrating-from-a-folder-of-scripts"),
|
|
107
|
+
copyInstall: page.includeJs && page.install !== undefined,
|
|
10
108
|
installHtml: page.install === undefined ? undefined : highlight(page.install),
|
|
11
109
|
useCases: page.useCases.map((useCase) => ({
|
|
12
110
|
...useCase,
|
|
@@ -33,7 +33,9 @@ export const TEMPLATE = String.raw `<!doctype html>
|
|
|
33
33
|
{{#install}}
|
|
34
34
|
<div class="install">
|
|
35
35
|
<code><span aria-hidden="true">$ </span>{{{installHtml}}}</code>
|
|
36
|
+
{{#copyInstall}}
|
|
36
37
|
<button class="copy" type="button" data-copy="{{install}}" aria-label="Copy {{install}}">Copy</button>
|
|
38
|
+
{{/copyInstall}}
|
|
37
39
|
</div>
|
|
38
40
|
{{/install}}
|
|
39
41
|
</div>
|
|
@@ -112,10 +114,10 @@ export const TEMPLATE = String.raw `<!doctype html>
|
|
|
112
114
|
<a class="text-link" href="{{docsUrl}}">Read the Toolcraft README <span aria-hidden="true">→</span></a>
|
|
113
115
|
</div>
|
|
114
116
|
<div class="docs-grid">
|
|
115
|
-
<a class="doc-card" href="{{
|
|
116
|
-
<a class="doc-card" href="{{
|
|
117
|
-
<a class="doc-card" href="{{
|
|
118
|
-
<a class="doc-card" href="{{
|
|
117
|
+
<a class="doc-card" href="{{docsHelloWorldUrl}}"><span>01</span><strong>Start with one command</strong><small>Install, define, and run a typed CLI in five minutes.</small></a>
|
|
118
|
+
<a class="doc-card" href="{{docsRuntimeUrl}}"><span>02</span><strong>Choose a runtime</strong><small>CLI, MCP, and SDK from the same command tree.</small></a>
|
|
119
|
+
<a class="doc-card" href="{{docsSecretsUrl}}"><span>03</span><strong>Add safety controls</strong><small>Secrets, preconditions, services, and human approval.</small></a>
|
|
120
|
+
<a class="doc-card" href="{{docsMigrationUrl}}"><span>04</span><strong>Migrate existing scripts</strong><small>Adopt Toolcraft incrementally without rewriting useful logic.</small></a>
|
|
119
121
|
</div>
|
|
120
122
|
</div>
|
|
121
123
|
</section>
|