hackshop-mcp 0.0.4 → 0.0.5
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 +34 -34
- package/catalog.json +55 -41
- package/dist/build-plan/index.d.ts +10 -0
- package/dist/build-plan/index.js +550 -0
- package/dist/build-plan/index.js.map +1 -0
- package/dist/build-plan/types.d.ts +153 -0
- package/dist/build-plan/types.js +2 -0
- package/dist/build-plan/types.js.map +1 -0
- package/dist/catalog/schema.d.ts +13 -7
- package/dist/catalog/schema.js +2 -0
- package/dist/catalog/schema.js.map +1 -1
- package/dist/core/assess.d.ts +36 -0
- package/dist/core/assess.js +100 -0
- package/dist/core/assess.js.map +1 -0
- package/dist/core/errors.d.ts +2 -0
- package/dist/core/errors.js +70 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/links.d.ts +3 -0
- package/dist/core/links.js +19 -0
- package/dist/core/links.js.map +1 -0
- package/dist/core/plan-gadget.d.ts +85 -0
- package/dist/core/plan-gadget.js +619 -0
- package/dist/core/plan-gadget.js.map +1 -0
- package/dist/core/resources.d.ts +37 -0
- package/dist/core/resources.js +143 -0
- package/dist/core/resources.js.map +1 -0
- package/dist/core/safety.d.ts +7 -0
- package/dist/core/safety.js +28 -0
- package/dist/core/safety.js.map +1 -0
- package/dist/core/tools.d.ts +247 -0
- package/dist/core/tools.js +164 -0
- package/dist/core/tools.js.map +1 -0
- package/dist/core/types.d.ts +163 -0
- package/dist/core/types.js +17 -0
- package/dist/core/types.js.map +1 -0
- package/dist/platforms/index.d.ts +1 -1
- package/dist/platforms/index.js +3 -2
- package/dist/platforms/index.js.map +1 -1
- package/dist/platforms/schema.d.ts +204 -0
- package/dist/platforms/schema.js +11 -0
- package/dist/platforms/schema.js.map +1 -1
- package/dist/sampling.d.ts +1 -0
- package/dist/sampling.js +28 -3
- package/dist/sampling.js.map +1 -1
- package/dist/server.d.ts +14 -1
- package/dist/server.js +97 -85
- package/dist/server.js.map +1 -1
- package/dist/site-url.d.ts +1 -0
- package/dist/site-url.js +4 -0
- package/dist/site-url.js.map +1 -0
- package/dist/tools/assess_hackability.d.ts +2 -33
- package/dist/tools/assess_hackability.js +11 -83
- package/dist/tools/assess_hackability.js.map +1 -1
- package/dist/tools/get_build_plan.d.ts +6 -0
- package/dist/tools/get_build_plan.js +16 -0
- package/dist/tools/get_build_plan.js.map +1 -0
- package/dist/tools/plan_gadget.d.ts +2 -64
- package/dist/tools/plan_gadget.js +9 -401
- package/dist/tools/plan_gadget.js.map +1 -1
- package/dist/tools/propose_hardware.d.ts +3 -0
- package/dist/tools/propose_hardware.js +23 -10
- package/dist/tools/propose_hardware.js.map +1 -1
- package/package.json +1 -1
- package/platforms.json +352 -21
package/dist/server.js
CHANGED
|
@@ -1,28 +1,75 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { realpathSync } from "node:fs";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
2
4
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
5
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
-
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
6
|
+
import { CallToolRequestSchema, GetPromptRequestSchema, ListPromptsRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
5
7
|
import { loadCatalog } from "./catalog/load.js";
|
|
6
8
|
import { loadPlatforms } from "./platforms/load.js";
|
|
7
|
-
import { setLoadedPlatforms } from "./platforms/index.js";
|
|
9
|
+
import { printablesFor, setLoadedPlatforms } from "./platforms/index.js";
|
|
8
10
|
import { probeSamplingSupport } from "./sampling.js";
|
|
9
11
|
import { proposeHardware, proposeHardwareInput, } from "./tools/propose_hardware.js";
|
|
10
|
-
import { assessHackability, assessHackabilityInput, } from "./tools/assess_hackability.js";
|
|
11
12
|
import { simulateAssembly, simulateAssemblyInput, } from "./tools/simulate_assembly.js";
|
|
12
|
-
import {
|
|
13
|
+
import { siteUrlFromEnv } from "./site-url.js";
|
|
13
14
|
import { createTelemetry, errorKind } from "./telemetry.js";
|
|
15
|
+
import { coreToolDefinitions, executeCoreTool, } from "./core/tools.js";
|
|
16
|
+
import { getCorePrompt, listCorePrompts, listCoreResources, readCoreResource, } from "./core/resources.js";
|
|
14
17
|
const NAME = "hackshop-mcp";
|
|
15
|
-
const VERSION = "0.0.
|
|
18
|
+
const VERSION = "0.0.5";
|
|
19
|
+
const STDIO_INSTRUCTIONS = "Hackshop maps a natural-language project idea to hackable, repurposable, or protocol-native hardware. Use plan_gadget for Muse agent-body gadgets because it is deterministic, instant and key-free. Use propose_hardware for broader repurposing ideas and existing hardware, especially when the host can sample or ANTHROPIC_API_KEY is set. Hackshop never buys anything; show shopping lists and ask the human before any purchase. Anonymous usage telemetry (tool names and timings only) is on by default; set HACKSHOP_TELEMETRY=0 to turn it off.";
|
|
20
|
+
export function createToolRunner(context) {
|
|
21
|
+
return async (name, args) => {
|
|
22
|
+
if (name === "propose_hardware") {
|
|
23
|
+
if (!context.server) {
|
|
24
|
+
throw new Error("propose_hardware requires an MCP server context");
|
|
25
|
+
}
|
|
26
|
+
const input = proposeHardwareInput.parse(args);
|
|
27
|
+
const out = await proposeHardware(input, context.devices, context.server);
|
|
28
|
+
return { out, degraded: out.degraded };
|
|
29
|
+
}
|
|
30
|
+
if (name === "assess_hackability" ||
|
|
31
|
+
name === "plan_gadget" ||
|
|
32
|
+
name === "get_build_plan") {
|
|
33
|
+
const result = executeCoreTool(name, args, coreContext(context.devices, context.platforms, context.tags));
|
|
34
|
+
return runnerResult(result);
|
|
35
|
+
}
|
|
36
|
+
if (name === "simulate_assembly") {
|
|
37
|
+
const input = simulateAssemblyInput.parse(args);
|
|
38
|
+
return { out: await simulateAssembly(input) };
|
|
39
|
+
}
|
|
40
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function coreContext(catalog, platforms, tags) {
|
|
44
|
+
const siteUrl = siteUrlFromEnv();
|
|
45
|
+
return {
|
|
46
|
+
catalog,
|
|
47
|
+
platforms,
|
|
48
|
+
printablesFor: (device, baseUrl) => printablesFor(device, baseUrl),
|
|
49
|
+
siteUrl,
|
|
50
|
+
tags,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function runnerResult(result) {
|
|
54
|
+
if (result.isError) {
|
|
55
|
+
return { out: { error: result.text }, isError: true, text: result.text };
|
|
56
|
+
}
|
|
57
|
+
return { out: result.output };
|
|
58
|
+
}
|
|
16
59
|
async function main() {
|
|
17
60
|
// Boot validation. Refuses to start on bad catalog/tags.
|
|
18
61
|
const { devices, tags } = loadCatalog();
|
|
62
|
+
const sortedTags = [...tags].sort();
|
|
19
63
|
const platforms = loadPlatforms(devices);
|
|
20
64
|
setLoadedPlatforms(platforms);
|
|
21
65
|
process.stderr.write(`[hackshop-mcp] Catalog loaded: ${devices.length} devices, ${tags.size} tags, ${platforms.length} platforms.\n`);
|
|
22
66
|
// Note: sampling is a CLIENT capability, not a server one. We don't declare
|
|
23
67
|
// it here; we just call server.createMessage(...) and the host either
|
|
24
68
|
// supports it or doesn't. probeSamplingSupport() handles the check.
|
|
25
|
-
const server = new Server({ name: NAME, version: VERSION }, {
|
|
69
|
+
const server = new Server({ name: NAME, version: VERSION }, {
|
|
70
|
+
capabilities: { tools: {}, resources: {}, prompts: {} },
|
|
71
|
+
instructions: STDIO_INSTRUCTIONS,
|
|
72
|
+
});
|
|
26
73
|
const telemetry = createTelemetry(VERSION);
|
|
27
74
|
const clientInfo = () => {
|
|
28
75
|
const client = server.getClientVersion();
|
|
@@ -59,60 +106,7 @@ async function main() {
|
|
|
59
106
|
required: ["idea"],
|
|
60
107
|
},
|
|
61
108
|
},
|
|
62
|
-
|
|
63
|
-
name: "assess_hackability",
|
|
64
|
-
description: "Given a device name, return: hackable y/n, hack difficulty, brick risk (with safety rule applied), firmware links, community size, last verified date, and notes. Looks up by id, exact name, or substring.",
|
|
65
|
-
inputSchema: {
|
|
66
|
-
type: "object",
|
|
67
|
-
properties: {
|
|
68
|
-
device_name: {
|
|
69
|
-
type: "string",
|
|
70
|
-
description: "Device name or id.",
|
|
71
|
-
},
|
|
72
|
-
},
|
|
73
|
-
required: ["device_name"],
|
|
74
|
-
},
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
name: "plan_gadget",
|
|
78
|
-
description: "Plan a physical gadget for an AI agent (Meta Muse Gadgets today). Given an idea, returns the best supported boards with tier, what works on each (voice, images, touch, camera, sensors, home-network tunnel), build commands, setup steps, Muse SDK terms, and printable stand/enclosure files when available. Deterministic: no LLM or network calls.",
|
|
79
|
-
inputSchema: {
|
|
80
|
-
type: "object",
|
|
81
|
-
properties: {
|
|
82
|
-
idea: {
|
|
83
|
-
type: "string",
|
|
84
|
-
description: "Gadget idea or use case.",
|
|
85
|
-
},
|
|
86
|
-
platform: {
|
|
87
|
-
type: "string",
|
|
88
|
-
enum: ["muse-esp32", "muse-linux", "any"],
|
|
89
|
-
description: "Optional platform filter. Defaults to any.",
|
|
90
|
-
},
|
|
91
|
-
budget_usd: {
|
|
92
|
-
type: "number",
|
|
93
|
-
description: "Optional hardware budget in USD.",
|
|
94
|
-
},
|
|
95
|
-
owned_device_ids: {
|
|
96
|
-
type: "array",
|
|
97
|
-
items: { type: "string" },
|
|
98
|
-
description: "Catalog device ids the user already owns.",
|
|
99
|
-
},
|
|
100
|
-
needs: {
|
|
101
|
-
type: "array",
|
|
102
|
-
items: {
|
|
103
|
-
type: "string",
|
|
104
|
-
enum: NEED_VALUES,
|
|
105
|
-
},
|
|
106
|
-
description: "Optional explicit needs; otherwise inferred from idea.",
|
|
107
|
-
},
|
|
108
|
-
limit: {
|
|
109
|
-
type: "number",
|
|
110
|
-
description: "Number of picks to return, 1-5. Defaults to 3.",
|
|
111
|
-
},
|
|
112
|
-
},
|
|
113
|
-
required: ["idea"],
|
|
114
|
-
},
|
|
115
|
-
},
|
|
109
|
+
...coreToolDefinitions(),
|
|
116
110
|
{
|
|
117
111
|
name: "simulate_assembly",
|
|
118
112
|
description: "Drop a proposed robot Assembly into a MuJoCo physics world and run a bounded navigation rollout. Returns whether it reached the goal plus honest failure telemetry (stuck/tipped/collisions/heading-oscillation), a natural-language post-mortem, and artifact URLs (rendered mp4, scene.xml, control.py, telemetry.json). Today simulates the diff-drive 'navigate' slice; other goal kinds return an honest 'unsupported'. Requires a running sim-worker (SIM_WORKER_URL).",
|
|
@@ -133,41 +127,44 @@ async function main() {
|
|
|
133
127
|
},
|
|
134
128
|
],
|
|
135
129
|
}));
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
const input = assessHackabilityInput.parse(args);
|
|
144
|
-
return { out: assessHackability(input, devices) };
|
|
145
|
-
}
|
|
146
|
-
if (name === "plan_gadget") {
|
|
147
|
-
const input = planGadgetInput.parse(args);
|
|
148
|
-
return { out: planGadget(input, devices) };
|
|
130
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
|
131
|
+
resources: listCoreResources(),
|
|
132
|
+
}));
|
|
133
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
134
|
+
const resource = readCoreResource(request.params.uri, coreContext(devices, platforms, sortedTags));
|
|
135
|
+
if (!resource) {
|
|
136
|
+
throw new Error(`Unknown resource: ${request.params.uri}`);
|
|
149
137
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
138
|
+
return { contents: [resource] };
|
|
139
|
+
});
|
|
140
|
+
server.setRequestHandler(ListPromptsRequestSchema, async () => ({
|
|
141
|
+
prompts: listCorePrompts(),
|
|
142
|
+
}));
|
|
143
|
+
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
144
|
+
const prompt = getCorePrompt(request.params.name, request.params.arguments);
|
|
145
|
+
if (!prompt) {
|
|
146
|
+
throw new Error(`Unknown prompt: ${request.params.name}`);
|
|
153
147
|
}
|
|
154
|
-
|
|
155
|
-
};
|
|
148
|
+
return prompt;
|
|
149
|
+
});
|
|
150
|
+
const runTool = createToolRunner({ devices, platforms, server, tags: sortedTags });
|
|
156
151
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
157
152
|
const { name, arguments: args } = request.params;
|
|
158
153
|
const startedAt = Date.now();
|
|
159
154
|
try {
|
|
160
|
-
const { out, degraded } = await runTool(name, args);
|
|
155
|
+
const { out, degraded, isError, text } = await runTool(name, args);
|
|
161
156
|
telemetry.send({
|
|
162
157
|
event: "mcp_tool_called",
|
|
163
158
|
tool: name,
|
|
164
|
-
success:
|
|
159
|
+
success: !isError,
|
|
165
160
|
degraded,
|
|
161
|
+
...(isError ? { error_kind: "tool_error" } : {}),
|
|
166
162
|
duration_ms: Date.now() - startedAt,
|
|
167
163
|
...clientInfo(),
|
|
168
164
|
});
|
|
169
165
|
return {
|
|
170
|
-
|
|
166
|
+
...(isError ? { isError: true } : {}),
|
|
167
|
+
content: [{ type: "text", text: text ?? JSON.stringify(out, null, 2) }],
|
|
171
168
|
};
|
|
172
169
|
}
|
|
173
170
|
catch (err) {
|
|
@@ -204,8 +201,23 @@ async function main() {
|
|
|
204
201
|
}
|
|
205
202
|
process.stderr.write(`[hackshop-mcp] ${NAME} v${VERSION} ready.\n`);
|
|
206
203
|
}
|
|
207
|
-
|
|
208
|
-
process.
|
|
209
|
-
|
|
210
|
-
|
|
204
|
+
function isDirectRun() {
|
|
205
|
+
const entry = process.argv[1];
|
|
206
|
+
if (!entry)
|
|
207
|
+
return false;
|
|
208
|
+
if (import.meta.url === pathToFileURL(entry).href)
|
|
209
|
+
return true;
|
|
210
|
+
try {
|
|
211
|
+
return import.meta.url === pathToFileURL(realpathSync(entry)).href;
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (isDirectRun()) {
|
|
218
|
+
main().catch((err) => {
|
|
219
|
+
process.stderr.write(`[hackshop-mcp] Fatal: ${err.message}\n`);
|
|
220
|
+
process.exit(1);
|
|
221
|
+
});
|
|
222
|
+
}
|
|
211
223
|
//# sourceMappingURL=server.js.map
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EACL,qBAAqB,EACrB,sBAAsB,
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,wBAAwB,EACxB,0BAA0B,EAC1B,sBAAsB,EACtB,yBAAyB,GAC1B,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAEzE,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EACL,eAAe,EACf,oBAAoB,GACrB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EACL,mBAAmB,EACnB,eAAe,GAEhB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,qBAAqB,CAAC;AAG7B,MAAM,IAAI,GAAG,cAAc,CAAC;AAC5B,MAAM,OAAO,GAAG,OAAO,CAAC;AACxB,MAAM,kBAAkB,GACtB,6hBAA6hB,CAAC;AAEhiB,MAAM,UAAU,gBAAgB,CAAC,OAKhC;IAMC,OAAO,KAAK,EAAE,IAAY,EAAE,IAAa,EAAE,EAAE;QAC3C,IAAI,IAAI,KAAK,kBAAkB,EAAE,CAAC;YAChC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;gBACpB,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;YACrE,CAAC;YACD,MAAM,KAAK,GAAG,oBAAoB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC/C,MAAM,GAAG,GAAG,MAAM,eAAe,CAAC,KAAK,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;YAC1E,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,CAAC;QACzC,CAAC;QAED,IACE,IAAI,KAAK,oBAAoB;YAC7B,IAAI,KAAK,aAAa;YACtB,IAAI,KAAK,gBAAgB,EACzB,CAAC;YACD,MAAM,MAAM,GAAG,eAAe,CAC5B,IAAI,EACJ,IAAI,EACJ,WAAW,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC,IAAI,CAAC,CAC9D,CAAC;YACF,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC;QAC9B,CAAC;QAED,IAAI,IAAI,KAAK,mBAAmB,EAAE,CAAC;YACjC,MAAM,KAAK,GAAG,qBAAqB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAChD,OAAO,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC,KAAK,CAAC,EAAE,CAAC;QAChD,CAAC;QAED,MAAM,IAAI,KAAK,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC;IAC3C,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAClB,OAAsB,EACtB,SAAqB,EACrB,IAAe;IAEf,MAAM,OAAO,GAAG,cAAc,EAAE,CAAC;IACjC,OAAO;QACL,OAAO;QACP,SAAS;QACT,aAAa,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,CAAC,aAAa,CAAC,MAAqB,EAAE,OAAO,CAAC;QACjF,OAAO;QACP,IAAI;KACL,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,MAAsB;IAK1C,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,OAAO,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC;IAC3E,CAAC;IACD,OAAO,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;AAChC,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,yDAAyD;IACzD,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,WAAW,EAAE,CAAC;IACxC,MAAM,UAAU,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;IACpC,MAAM,SAAS,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;IACzC,kBAAkB,CAAC,SAAS,CAAC,CAAC;IAC9B,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,kCAAkC,OAAO,CAAC,MAAM,aAAa,IAAI,CAAC,IAAI,UAAU,SAAS,CAAC,MAAM,eAAe,CAChH,CAAC;IAEF,4EAA4E;IAC5E,sEAAsE;IACtE,oEAAoE;IACpE,MAAM,MAAM,GAAG,IAAI,MAAM,CACvB,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,EAChC;QACE,YAAY,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;QACvD,YAAY,EAAE,kBAAkB;KACjC,CACF,CAAC;IAEF,MAAM,SAAS,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;IAC3C,MAAM,UAAU,GAAG,GAAG,EAAE;QACtB,MAAM,MAAM,GAAG,MAAM,CAAC,gBAAgB,EAAE,CAAC;QACzC,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;IACxE,CAAC,CAAC;IACF,MAAM,CAAC,aAAa,GAAG,GAAG,EAAE;QAC1B,SAAS,CAAC,IAAI,CAAC;YACb,KAAK,EAAE,oBAAoB;YAC3B,GAAG,UAAU,EAAE;YACf,eAAe,EAAE,OAAO,CAAC,MAAM,CAAC,qBAAqB,EAAE,EAAE,QAAQ,CAAC;SACnE,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,MAAM,CAAC,iBAAiB,CAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;QAC5D,KAAK,EAAE;YACL;gBACE,IAAI,EAAE,kBAAkB;gBACxB,WAAW,EACT,wRAAwR;gBAC1R,WAAW,EAAE;oBACX,IAAI,EAAE,QAAQ;oBACd,UAAU,EAAE;wBACV,IAAI,EAAE;4BACJ,IAAI,EAAE,QAAQ;4BACd,WAAW,EACT,sEAAsE;yBACzE;wBACD,UAAU,EAAE;4BACV,IAAI,EAAE,QAAQ;4BACd,WAAW,EAAE,yDAAyD;yBACvE;wBACD,WAAW,EAAE;4BACX,IAAI,EAAE,QAAQ;4BACd,WAAW,EACT,qEAAqE;yBACxE;qBACF;oBACD,QAAQ,EAAE,CAAC,MAAM,CAAC;iBACnB;aACF;YACD,GAAG,mBAAmB,EAAE;YACxB;gBACE,IAAI,EAAE,mBAAmB;gBACzB,WAAW,EACT,8cAA8c;gBAChd,WAAW,EAAE;oBACX,IAAI,EAAE,QAAQ;oBACd,UAAU,EAAE;wBACV,QAAQ,EAAE;4BACR,IAAI,EAAE,QAAQ;4BACd,WAAW,EACT,0LAA0L;yBAC7L;wBACD,UAAU,EAAE;4BACV,IAAI,EAAE,QAAQ;4BACd,WAAW,EAAE,kDAAkD;yBAChE;qBACF;oBACD,QAAQ,EAAE,CAAC,UAAU,CAAC;iBACvB;aACF;SACF;KACF,CAAC,CAAC,CAAC;IAEJ,MAAM,CAAC,iBAAiB,CAAC,0BAA0B,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;QAChE,SAAS,EAAE,iBAAiB,EAAE;KAC/B,CAAC,CAAC,CAAC;IAEJ,MAAM,CAAC,iBAAiB,CAAC,yBAAyB,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QACpE,MAAM,QAAQ,GAAG,gBAAgB,CAC/B,OAAO,CAAC,MAAM,CAAC,GAAG,EAClB,WAAW,CAAC,OAAO,EAAE,SAAS,EAAE,UAAU,CAAC,CAC5C,CAAC;QACF,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CAAC,qBAAqB,OAAO,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC;QAC7D,CAAC;QACD,OAAO,EAAE,QAAQ,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,iBAAiB,CAAC,wBAAwB,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;QAC9D,OAAO,EAAE,eAAe,EAAE;KAC3B,CAAC,CAAC,CAAC;IAEJ,MAAM,CAAC,iBAAiB,CAAC,sBAAsB,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QACjE,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC5E,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,mBAAmB,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QAC5D,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,gBAAgB,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;IAEnF,MAAM,CAAC,iBAAiB,CAAC,qBAAqB,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QAChE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;QACjD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC7B,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YACnE,SAAS,CAAC,IAAI,CAAC;gBACb,KAAK,EAAE,iBAAiB;gBACxB,IAAI,EAAE,IAAI;gBACV,OAAO,EAAE,CAAC,OAAO;gBACjB,QAAQ;gBACR,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAChD,WAAW,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;gBACnC,GAAG,UAAU,EAAE;aAChB,CAAC,CAAC;YACH,OAAO;gBACL,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACrC,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,IAAI,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;aACxE,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,SAAS,CAAC,IAAI,CAAC;gBACb,KAAK,EAAE,iBAAiB;gBACxB,IAAI,EAAE,IAAI;gBACV,OAAO,EAAE,KAAK;gBACd,UAAU,EAAE,SAAS,CAAC,GAAG,CAAC;gBAC1B,WAAW,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;gBACnC,GAAG,UAAU,EAAE;aAChB,CAAC,CAAC;YACH,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAEhC,2EAA2E;IAC3E,+EAA+E;IAC/E,8EAA8E;IAC9E,6EAA6E;IAC7E,qEAAqE;IACrE,IAAI,OAAO,CAAC,GAAG,CAAC,uBAAuB,KAAK,GAAG,EAAE,CAAC;QAChD,KAAK,oBAAoB,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE;YAC5C,IAAI,CAAC,EAAE,EAAE,CAAC;gBACR,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,mEAAmE;oBACjE,gEAAgE;oBAChE,8DAA8D;oBAC9D,iEAAiE,CACpE,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,qCAAqC,CAAC,CAAC;YAC9D,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,kBAAkB,IAAI,KAAK,OAAO,WAAW,CAAC,CAAC;AACtE,CAAC;AAED,SAAS,WAAW;IAClB,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC9B,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IACzB,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,aAAa,CAAC,KAAK,CAAC,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IAC/D,IAAI,CAAC;QACH,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,aAAa,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IACrE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,IAAI,WAAW,EAAE,EAAE,CAAC;IAClB,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;QACnB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,yBAA0B,GAAa,CAAC,OAAO,IAAI,CAAC,CAAC;QAC1E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function siteUrlFromEnv(): string;
|
package/dist/site-url.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"site-url.js","sourceRoot":"","sources":["../src/site-url.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,cAAc;IAC5B,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,0BAA0B,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAC3F,CAAC"}
|
|
@@ -1,35 +1,4 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
1
|
import type { DeviceEntry } from "../catalog/schema.js";
|
|
3
|
-
import { type
|
|
4
|
-
|
|
5
|
-
export declare const assessHackabilityInput: z.ZodObject<{
|
|
6
|
-
device_name: z.ZodString;
|
|
7
|
-
}, "strip", z.ZodTypeAny, {
|
|
8
|
-
device_name: string;
|
|
9
|
-
}, {
|
|
10
|
-
device_name: string;
|
|
11
|
-
}>;
|
|
12
|
-
export type AssessInput = z.infer<typeof assessHackabilityInput>;
|
|
13
|
-
export interface AssessOutput {
|
|
14
|
-
found: boolean;
|
|
15
|
-
device?: {
|
|
16
|
-
id: string;
|
|
17
|
-
name: string;
|
|
18
|
-
category: string;
|
|
19
|
-
hackable: boolean;
|
|
20
|
-
hack_difficulty: number;
|
|
21
|
-
brick_risk: number | null;
|
|
22
|
-
brick_risk_label: string;
|
|
23
|
-
brick_risk_disclaimer: string | null;
|
|
24
|
-
firmware_links: string[];
|
|
25
|
-
community_size: string;
|
|
26
|
-
last_verified: string;
|
|
27
|
-
notes: string;
|
|
28
|
-
links: DeviceLinks;
|
|
29
|
-
agent_platforms: AgentPlatform[];
|
|
30
|
-
physical: DeviceEntry["physical"] | null;
|
|
31
|
-
printables: Printable[];
|
|
32
|
-
};
|
|
33
|
-
message?: string;
|
|
34
|
-
}
|
|
2
|
+
import { assessHackabilityInput, type AssessInput, type AssessOutput } from "../core/assess.js";
|
|
3
|
+
export { assessHackabilityInput, type AssessInput, type AssessOutput };
|
|
35
4
|
export declare function assessHackability(input: AssessInput, catalog: DeviceEntry[]): AssessOutput;
|
|
@@ -1,86 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
export const assessHackabilityInput = z.object({
|
|
6
|
-
device_name: z.string().min(1).max(200),
|
|
7
|
-
});
|
|
8
|
-
// Simple normalization for fuzzy lookup: lowercase, strip non-alnum.
|
|
9
|
-
function norm(s) {
|
|
10
|
-
return s.toLowerCase().replace(/[^a-z0-9]+/g, "");
|
|
11
|
-
}
|
|
1
|
+
import { getLoadedPlatforms, printablesFor } from "../platforms/index.js";
|
|
2
|
+
import { siteUrlFromEnv } from "../site-url.js";
|
|
3
|
+
import { assessHackability as assessHackabilityCore, assessHackabilityInput, } from "../core/assess.js";
|
|
4
|
+
export { assessHackabilityInput };
|
|
12
5
|
export function assessHackability(input, catalog) {
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
// name) over-matched short names. We collect ALL substring candidates and
|
|
21
|
-
// pick the MOST SPECIFIC one (closest normalized-length to the query) instead
|
|
22
|
-
// of returning the first array match, which was order-dependent.
|
|
23
|
-
if (!match && target.length > 0) {
|
|
24
|
-
const candidates = catalog.filter((d) => norm(d.name).includes(target));
|
|
25
|
-
if (candidates.length === 1) {
|
|
26
|
-
match = candidates[0];
|
|
27
|
-
}
|
|
28
|
-
else if (candidates.length > 1) {
|
|
29
|
-
// Most-specific = smallest name (tightest fit around the query). Ties
|
|
30
|
-
// resolve deterministically by name then id so results are stable.
|
|
31
|
-
const sorted = [...candidates].sort((a, b) => {
|
|
32
|
-
const la = norm(a.name).length;
|
|
33
|
-
const lb = norm(b.name).length;
|
|
34
|
-
if (la !== lb)
|
|
35
|
-
return la - lb;
|
|
36
|
-
if (a.name !== b.name)
|
|
37
|
-
return a.name.localeCompare(b.name);
|
|
38
|
-
return a.id.localeCompare(b.id);
|
|
39
|
-
});
|
|
40
|
-
match = sorted[0];
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
if (!match) {
|
|
44
|
-
return {
|
|
45
|
-
found: false,
|
|
46
|
-
message: `"${input.device_name}" is not in the catalog. Cannot assess hackability without verification. Try a more specific name, or check the firmware links section of similar devices.`,
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
const safety = applyBrickRiskSafety(match);
|
|
50
|
-
const links = buildLinks(match);
|
|
51
|
-
const agentPlatforms = safeAgentPlatformsFor(match.id);
|
|
52
|
-
// Derive hackability from a real signal rather than hardcoding true: a device
|
|
53
|
-
// is "hackable" if it has firmware/community resources OR its difficulty is
|
|
54
|
-
// moderate-or-lower (<=4 of 5). Presence in the catalog alone isn't proof.
|
|
55
|
-
const hackable = match.firmware_links.length > 0 || match.hack_difficulty <= 4;
|
|
56
|
-
return {
|
|
57
|
-
found: true,
|
|
58
|
-
device: {
|
|
59
|
-
id: match.id,
|
|
60
|
-
name: match.name,
|
|
61
|
-
category: match.category,
|
|
62
|
-
hackable,
|
|
63
|
-
hack_difficulty: match.hack_difficulty,
|
|
64
|
-
brick_risk: safety.brick_risk,
|
|
65
|
-
brick_risk_label: safety.brick_risk_label,
|
|
66
|
-
brick_risk_disclaimer: safety.brick_risk_disclaimer,
|
|
67
|
-
firmware_links: match.firmware_links,
|
|
68
|
-
community_size: match.community_size_bucket,
|
|
69
|
-
last_verified: match.last_verified,
|
|
70
|
-
notes: match.notes,
|
|
71
|
-
links,
|
|
72
|
-
agent_platforms: agentPlatforms,
|
|
73
|
-
physical: match.physical ?? null,
|
|
74
|
-
printables: printablesFor(match),
|
|
75
|
-
},
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
function safeAgentPlatformsFor(deviceId) {
|
|
79
|
-
try {
|
|
80
|
-
return agentPlatformsFor(deviceId);
|
|
81
|
-
}
|
|
82
|
-
catch {
|
|
83
|
-
return [];
|
|
84
|
-
}
|
|
6
|
+
const siteUrl = siteUrlFromEnv();
|
|
7
|
+
return assessHackabilityCore(input, {
|
|
8
|
+
catalog,
|
|
9
|
+
platforms: getLoadedPlatforms(),
|
|
10
|
+
printablesFor: (device, baseUrl) => printablesFor(device, baseUrl),
|
|
11
|
+
siteUrl,
|
|
12
|
+
});
|
|
85
13
|
}
|
|
86
14
|
//# sourceMappingURL=assess_hackability.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assess_hackability.js","sourceRoot":"","sources":["../../src/tools/assess_hackability.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"assess_hackability.js","sourceRoot":"","sources":["../../src/tools/assess_hackability.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC1E,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EACL,iBAAiB,IAAI,qBAAqB,EAC1C,sBAAsB,GAGvB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,sBAAsB,EAAuC,CAAC;AAEvE,MAAM,UAAU,iBAAiB,CAC/B,KAAkB,EAClB,OAAsB;IAEtB,MAAM,OAAO,GAAG,cAAc,EAAE,CAAC;IACjC,OAAO,qBAAqB,CAAC,KAAK,EAAE;QAClC,OAAO;QACP,SAAS,EAAE,kBAAkB,EAAE;QAC/B,aAAa,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,CAAC,aAAa,CAAC,MAAqB,EAAE,OAAO,CAAC;QACjF,OAAO;KACR,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { BuildPlan } from "../build-plan/types.js";
|
|
2
|
+
import type { DeviceEntry } from "../catalog/schema.js";
|
|
3
|
+
import { getBuildPlanInput, type GetBuildPlanInput } from "../core/tools.js";
|
|
4
|
+
import type { Platform } from "../platforms/schema.js";
|
|
5
|
+
export { getBuildPlanInput, type GetBuildPlanInput };
|
|
6
|
+
export declare function getBuildPlan(input: GetBuildPlanInput, catalog: DeviceEntry[], platforms: Platform[], siteUrl: string): BuildPlan;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { getBuildPlan as getBuildPlanCore, getBuildPlanInput } from "../core/tools.js";
|
|
2
|
+
import { printablesFor } from "../platforms/index.js";
|
|
3
|
+
export { getBuildPlanInput };
|
|
4
|
+
export function getBuildPlan(input, catalog, platforms, siteUrl) {
|
|
5
|
+
const result = getBuildPlanCore(input, {
|
|
6
|
+
catalog,
|
|
7
|
+
platforms,
|
|
8
|
+
printablesFor: (device, baseUrl) => printablesFor(device, baseUrl),
|
|
9
|
+
siteUrl,
|
|
10
|
+
});
|
|
11
|
+
if ("isError" in result) {
|
|
12
|
+
throw new Error(result.text);
|
|
13
|
+
}
|
|
14
|
+
return result;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=get_build_plan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get_build_plan.js","sourceRoot":"","sources":["../../src/tools/get_build_plan.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,IAAI,gBAAgB,EAAE,iBAAiB,EAA0B,MAAM,kBAAkB,CAAC;AAC/G,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAGtD,OAAO,EAAE,iBAAiB,EAA0B,CAAC;AAErD,MAAM,UAAU,YAAY,CAC1B,KAAwB,EACxB,OAAsB,EACtB,SAAqB,EACrB,OAAe;IAEf,MAAM,MAAM,GAAG,gBAAgB,CAAC,KAAK,EAAE;QACrC,OAAO;QACP,SAAS;QACT,aAAa,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,CAAC,aAAa,CAAC,MAAqB,EAAE,OAAO,CAAC;QACjF,OAAO;KACR,CAAC,CAAC;IACH,IAAI,SAAS,IAAI,MAAM,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -1,66 +1,4 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
1
|
import type { DeviceEntry } from "../catalog/schema.js";
|
|
3
|
-
import { type
|
|
4
|
-
export
|
|
5
|
-
declare const Need: z.ZodEnum<["voice", "screen", "images", "touch", "camera", "air-sensors", "e-ink", "battery", "round", "home-tunnel", "big-screen", "compute", "linux"]>;
|
|
6
|
-
export type Need = z.infer<typeof Need>;
|
|
7
|
-
type InferredPreference = "cheap";
|
|
8
|
-
export declare const planGadgetInput: z.ZodObject<{
|
|
9
|
-
idea: z.ZodString;
|
|
10
|
-
platform: z.ZodDefault<z.ZodEnum<["muse-esp32", "muse-linux", "any"]>>;
|
|
11
|
-
budget_usd: z.ZodOptional<z.ZodNumber>;
|
|
12
|
-
owned_device_ids: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
13
|
-
needs: z.ZodOptional<z.ZodArray<z.ZodEnum<["voice", "screen", "images", "touch", "camera", "air-sensors", "e-ink", "battery", "round", "home-tunnel", "big-screen", "compute", "linux"]>, "many">>;
|
|
14
|
-
limit: z.ZodDefault<z.ZodNumber>;
|
|
15
|
-
}, "strip", z.ZodTypeAny, {
|
|
16
|
-
idea: string;
|
|
17
|
-
platform: "muse-esp32" | "muse-linux" | "any";
|
|
18
|
-
limit: number;
|
|
19
|
-
budget_usd?: number | undefined;
|
|
20
|
-
owned_device_ids?: string[] | undefined;
|
|
21
|
-
needs?: ("round" | "e-ink" | "battery" | "voice" | "compute" | "screen" | "images" | "touch" | "camera" | "air-sensors" | "home-tunnel" | "big-screen" | "linux")[] | undefined;
|
|
22
|
-
}, {
|
|
23
|
-
idea: string;
|
|
24
|
-
budget_usd?: number | undefined;
|
|
25
|
-
platform?: "muse-esp32" | "muse-linux" | "any" | undefined;
|
|
26
|
-
owned_device_ids?: string[] | undefined;
|
|
27
|
-
needs?: ("round" | "e-ink" | "battery" | "voice" | "compute" | "screen" | "images" | "touch" | "camera" | "air-sensors" | "home-tunnel" | "big-screen" | "linux")[] | undefined;
|
|
28
|
-
limit?: number | undefined;
|
|
29
|
-
}>;
|
|
30
|
-
export type PlanGadgetInput = z.infer<typeof planGadgetInput>;
|
|
31
|
-
export interface PlanGadgetOutput {
|
|
32
|
-
inferred_needs: Need[];
|
|
33
|
-
inferred_preferences: InferredPreference[];
|
|
34
|
-
picks: Array<{
|
|
35
|
-
device_id: string;
|
|
36
|
-
name: string;
|
|
37
|
-
platform_id: string;
|
|
38
|
-
platform_name: string;
|
|
39
|
-
support: "official" | "possible";
|
|
40
|
-
tier: string;
|
|
41
|
-
tier_label: string;
|
|
42
|
-
score: number;
|
|
43
|
-
why: string;
|
|
44
|
-
gaps: string[];
|
|
45
|
-
price_label: string | null;
|
|
46
|
-
links: string[];
|
|
47
|
-
build_command: string;
|
|
48
|
-
setup_steps: string[];
|
|
49
|
-
caveats: string[];
|
|
50
|
-
fabrication: {
|
|
51
|
-
printables: Printable[];
|
|
52
|
-
note: string;
|
|
53
|
-
};
|
|
54
|
-
}>;
|
|
55
|
-
terms: Array<{
|
|
56
|
-
platform_id: string;
|
|
57
|
-
summary: string;
|
|
58
|
-
url: string;
|
|
59
|
-
max_devices_per_token: number;
|
|
60
|
-
selling_allowed: boolean;
|
|
61
|
-
}>;
|
|
62
|
-
next_steps: string[];
|
|
63
|
-
}
|
|
2
|
+
import { NEED_VALUES, planGadgetInput, type Need, type PlanGadgetInput, type PlanGadgetOutput, type Size } from "../core/plan-gadget.js";
|
|
3
|
+
export { NEED_VALUES, planGadgetInput, type Need, type PlanGadgetInput, type PlanGadgetOutput, type Size };
|
|
64
4
|
export declare function planGadget(input: PlanGadgetInput, catalog: DeviceEntry[]): PlanGadgetOutput;
|
|
65
|
-
export declare function inferNeeds(idea: string): Need[];
|
|
66
|
-
export {};
|