hackshop-mcp 0.0.3 → 0.0.4

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.
@@ -1 +1 @@
1
- {"version":3,"file":"sampling.js","sourceRoot":"","sources":["../src/sampling.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sampling.js","sourceRoot":"","sources":["../src/sampling.ts"],"names":[],"mappings":"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"}
package/dist/server.js CHANGED
@@ -3,19 +3,38 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
4
  import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
5
5
  import { loadCatalog } from "./catalog/load.js";
6
+ import { loadPlatforms } from "./platforms/load.js";
7
+ import { setLoadedPlatforms } from "./platforms/index.js";
6
8
  import { probeSamplingSupport } from "./sampling.js";
7
9
  import { proposeHardware, proposeHardwareInput, } from "./tools/propose_hardware.js";
8
10
  import { assessHackability, assessHackabilityInput, } from "./tools/assess_hackability.js";
11
+ import { simulateAssembly, simulateAssemblyInput, } from "./tools/simulate_assembly.js";
12
+ import { NEED_VALUES, planGadget, planGadgetInput, } from "./tools/plan_gadget.js";
13
+ import { createTelemetry, errorKind } from "./telemetry.js";
9
14
  const NAME = "hackshop-mcp";
10
- const VERSION = "0.0.3";
15
+ const VERSION = "0.0.4";
11
16
  async function main() {
12
17
  // Boot validation. Refuses to start on bad catalog/tags.
13
18
  const { devices, tags } = loadCatalog();
14
- process.stderr.write(`[hackshop-mcp] Catalog loaded: ${devices.length} devices, ${tags.size} tags.\n`);
19
+ const platforms = loadPlatforms(devices);
20
+ setLoadedPlatforms(platforms);
21
+ process.stderr.write(`[hackshop-mcp] Catalog loaded: ${devices.length} devices, ${tags.size} tags, ${platforms.length} platforms.\n`);
15
22
  // Note: sampling is a CLIENT capability, not a server one. We don't declare
16
23
  // it here; we just call server.createMessage(...) and the host either
17
24
  // supports it or doesn't. probeSamplingSupport() handles the check.
18
25
  const server = new Server({ name: NAME, version: VERSION }, { capabilities: { tools: {} } });
26
+ const telemetry = createTelemetry(VERSION);
27
+ const clientInfo = () => {
28
+ const client = server.getClientVersion();
29
+ return { client_name: client?.name, client_version: client?.version };
30
+ };
31
+ server.oninitialized = () => {
32
+ telemetry.send({
33
+ event: "mcp_server_started",
34
+ ...clientInfo(),
35
+ client_sampling: Boolean(server.getClientCapabilities()?.sampling),
36
+ });
37
+ };
19
38
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
20
39
  tools: [
21
40
  {
@@ -54,42 +73,135 @@ async function main() {
54
73
  required: ["device_name"],
55
74
  },
56
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
+ },
116
+ {
117
+ name: "simulate_assembly",
118
+ 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).",
119
+ inputSchema: {
120
+ type: "object",
121
+ properties: {
122
+ assembly: {
123
+ type: "object",
124
+ description: "Assembly IR: { idea, components[{ref,device_id,name,role}], edges[], goal{kind,spec,success_metric}, world{template,goal_xy?} }. Build it with the site /api/assembly output or by hand.",
125
+ },
126
+ duration_s: {
127
+ type: "number",
128
+ description: "Optional sim seconds (<=10, bounded). Default 8.",
129
+ },
130
+ },
131
+ required: ["assembly"],
132
+ },
133
+ },
57
134
  ],
58
135
  }));
59
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
60
- const { name, arguments: args } = request.params;
136
+ const runTool = async (name, args) => {
61
137
  if (name === "propose_hardware") {
62
138
  const input = proposeHardwareInput.parse(args);
63
139
  const out = await proposeHardware(input, devices, server);
64
- return {
65
- content: [{ type: "text", text: JSON.stringify(out, null, 2) }],
66
- };
140
+ return { out, degraded: out.degraded };
67
141
  }
68
142
  if (name === "assess_hackability") {
69
143
  const input = assessHackabilityInput.parse(args);
70
- const out = assessHackability(input, devices);
144
+ return { out: assessHackability(input, devices) };
145
+ }
146
+ if (name === "plan_gadget") {
147
+ const input = planGadgetInput.parse(args);
148
+ return { out: planGadget(input, devices) };
149
+ }
150
+ if (name === "simulate_assembly") {
151
+ const input = simulateAssemblyInput.parse(args);
152
+ return { out: await simulateAssembly(input) };
153
+ }
154
+ throw new Error(`Unknown tool: ${name}`);
155
+ };
156
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
157
+ const { name, arguments: args } = request.params;
158
+ const startedAt = Date.now();
159
+ try {
160
+ const { out, degraded } = await runTool(name, args);
161
+ telemetry.send({
162
+ event: "mcp_tool_called",
163
+ tool: name,
164
+ success: true,
165
+ degraded,
166
+ duration_ms: Date.now() - startedAt,
167
+ ...clientInfo(),
168
+ });
71
169
  return {
72
170
  content: [{ type: "text", text: JSON.stringify(out, null, 2) }],
73
171
  };
74
172
  }
75
- throw new Error(`Unknown tool: ${name}`);
173
+ catch (err) {
174
+ telemetry.send({
175
+ event: "mcp_tool_called",
176
+ tool: name,
177
+ success: false,
178
+ error_kind: errorKind(err),
179
+ duration_ms: Date.now() - startedAt,
180
+ ...clientInfo(),
181
+ });
182
+ throw err;
183
+ }
76
184
  });
77
185
  const transport = new StdioServerTransport();
78
186
  await server.connect(transport);
79
- // Probe sampling support after connect. Logs to stderr; never fatal.
80
- // Some hosts (Claude Desktop, Claude Code) support sampling/createMessage;
81
- // some clients don't. If unsupported, propose_hardware degrades gracefully.
82
- void probeSamplingSupport(server).then((ok) => {
83
- if (!ok) {
84
- process.stderr.write("[hackshop-mcp] Warning: this MCP host does not appear to support " +
85
- "sampling/createMessage. The propose_hardware tool will return " +
86
- "degraded responses (raw catalog matches without reasoning). " +
87
- "Hosts known to support sampling: Claude Desktop, Claude Code.\n");
88
- }
89
- else {
90
- process.stderr.write("[hackshop-mcp] Sampling probe OK.\n");
91
- }
92
- });
187
+ // Probe sampling support after connect — but ONLY when explicitly opted in
188
+ // via HACKSHOP_PROBE_SAMPLING=1. Previously this fired a real createMessage on
189
+ // every boot just to log "Sampling probe OK", spending tokens on every start.
190
+ // propose_hardware already degrades gracefully when sampling is unsupported,
191
+ // so the probe is purely informational. Logs to stderr; never fatal.
192
+ if (process.env.HACKSHOP_PROBE_SAMPLING === "1") {
193
+ void probeSamplingSupport(server).then((ok) => {
194
+ if (!ok) {
195
+ process.stderr.write("[hackshop-mcp] Warning: this MCP host does not appear to support " +
196
+ "sampling/createMessage. The propose_hardware tool will return " +
197
+ "degraded responses (raw catalog matches without reasoning). " +
198
+ "Hosts known to support sampling: Claude Desktop, Claude Code.\n");
199
+ }
200
+ else {
201
+ process.stderr.write("[hackshop-mcp] Sampling probe OK.\n");
202
+ }
203
+ });
204
+ }
93
205
  process.stderr.write(`[hackshop-mcp] ${NAME} v${VERSION} ready.\n`);
94
206
  }
95
207
  main().catch((err) => {
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ export declare const DEFAULT_TELEMETRY_URL = "https://www.hackshop.dev/api/telemetry/mcp";
2
+ export type TelemetryEvent = {
3
+ event: "mcp_server_started";
4
+ client_name?: string;
5
+ client_version?: string;
6
+ client_sampling?: boolean;
7
+ } | {
8
+ event: "mcp_tool_called";
9
+ tool: string;
10
+ success: boolean;
11
+ duration_ms: number;
12
+ degraded?: boolean;
13
+ error_kind?: string;
14
+ client_name?: string;
15
+ client_version?: string;
16
+ };
17
+ export declare function telemetryEnabled(env?: NodeJS.ProcessEnv): boolean;
18
+ /**
19
+ * Stable random install id, so repeat use by one person counts once. Falls
20
+ * back to a per-process id if the config dir is not writable.
21
+ */
22
+ export declare function loadInstallId(env?: NodeJS.ProcessEnv, onCreate?: (path: string) => void): string;
23
+ export type Telemetry = {
24
+ enabled: boolean;
25
+ send(event: TelemetryEvent): void;
26
+ };
27
+ export declare function createTelemetry(version: string, env?: NodeJS.ProcessEnv, log?: (line: string) => void): Telemetry;
28
+ /** Coarse error bucket; the message itself is not sent. */
29
+ export declare function errorKind(err: unknown): string;
@@ -0,0 +1,100 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { homedir } from "node:os";
4
+ import { dirname, join } from "node:path";
5
+ // Anonymous usage telemetry for the MCP server. It exists so the maintainer
6
+ // can see that the server is being used at all: stdio servers run on users'
7
+ // machines and are otherwise invisible.
8
+ //
9
+ // Sent: event name, tool name, success/degraded, duration, hackshop-mcp
10
+ // version, MCP client name/version, OS platform, Node major version, and a
11
+ // random install id. NEVER sent: tool arguments, ideas, device names, results,
12
+ // API keys, file paths, or anything typed by the user.
13
+ //
14
+ // Opt out with HACKSHOP_TELEMETRY=0 (or DO_NOT_TRACK=1).
15
+ export const DEFAULT_TELEMETRY_URL = "https://www.hackshop.dev/api/telemetry/mcp";
16
+ const SEND_TIMEOUT_MS = 1_500;
17
+ const KNOWN_TOOLS = new Set(["propose_hardware", "assess_hackability", "simulate_assembly"]);
18
+ export function telemetryEnabled(env = process.env) {
19
+ const flag = env.HACKSHOP_TELEMETRY?.trim().toLowerCase();
20
+ if (flag && ["0", "false", "off", "no", "disabled"].includes(flag))
21
+ return false;
22
+ const dnt = env.DO_NOT_TRACK?.trim().toLowerCase();
23
+ if (dnt && dnt !== "0" && dnt !== "false")
24
+ return false;
25
+ // Test runners and CI are not real usage.
26
+ if (env.VITEST || env.CI)
27
+ return false;
28
+ return true;
29
+ }
30
+ function configPath(env) {
31
+ const base = env.XDG_CONFIG_HOME?.trim() || join(homedir(), ".config");
32
+ return join(base, "hackshop-mcp", "telemetry.json");
33
+ }
34
+ /**
35
+ * Stable random install id, so repeat use by one person counts once. Falls
36
+ * back to a per-process id if the config dir is not writable.
37
+ */
38
+ export function loadInstallId(env = process.env, onCreate = () => { }) {
39
+ const path = configPath(env);
40
+ try {
41
+ const stored = JSON.parse(readFileSync(path, "utf8"));
42
+ if (typeof stored.install_id === "string" && stored.install_id.length === 36) {
43
+ return stored.install_id;
44
+ }
45
+ }
46
+ catch {
47
+ // Missing or unreadable: create below.
48
+ }
49
+ const id = randomUUID();
50
+ try {
51
+ mkdirSync(dirname(path), { recursive: true });
52
+ writeFileSync(path, JSON.stringify({ install_id: id }) + "\n", "utf8");
53
+ onCreate(path);
54
+ }
55
+ catch {
56
+ // Read-only home, sandbox, etc. A per-process id is fine.
57
+ }
58
+ return id;
59
+ }
60
+ export function createTelemetry(version, env = process.env, log = (line) => process.stderr.write(line)) {
61
+ if (!telemetryEnabled(env)) {
62
+ return { enabled: false, send: () => { } };
63
+ }
64
+ const installId = loadInstallId(env, () => log("[hackshop-mcp] Anonymous usage telemetry is on (tool names and timings only; " +
65
+ "never your ideas or tool inputs). Opt out with HACKSHOP_TELEMETRY=0.\n"));
66
+ const url = env.HACKSHOP_TELEMETRY_URL?.trim() || DEFAULT_TELEMETRY_URL;
67
+ const base = {
68
+ install_id: installId,
69
+ mcp_version: version,
70
+ platform: process.platform,
71
+ node_version: process.versions.node.split(".")[0],
72
+ };
73
+ return {
74
+ enabled: true,
75
+ send(event) {
76
+ const payload = event.event === "mcp_tool_called" && !KNOWN_TOOLS.has(event.tool)
77
+ ? { ...event, tool: "unknown" }
78
+ : event;
79
+ // Fire and forget: never awaited, never throws, never blocks a tool call.
80
+ void fetch(url, {
81
+ method: "POST",
82
+ headers: { "Content-Type": "application/json" },
83
+ body: JSON.stringify({ ...base, ...payload }),
84
+ signal: AbortSignal.timeout(SEND_TIMEOUT_MS),
85
+ }).catch(() => { });
86
+ },
87
+ };
88
+ }
89
+ /** Coarse error bucket; the message itself is not sent. */
90
+ export function errorKind(err) {
91
+ if (err && typeof err === "object" && "name" in err) {
92
+ const name = String(err.name);
93
+ if (name === "ZodError")
94
+ return "invalid_input";
95
+ if (name === "AbortError" || name === "TimeoutError")
96
+ return "timeout";
97
+ }
98
+ return "error";
99
+ }
100
+ //# sourceMappingURL=telemetry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry.js","sourceRoot":"","sources":["../src/telemetry.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import type { DeviceEntry } from "../catalog/schema.js";
3
3
  import { type DeviceLinks } from "../links.js";
4
+ import { type AgentPlatform, type Printable } from "../platforms/index.js";
4
5
  export declare const assessHackabilityInput: z.ZodObject<{
5
6
  device_name: z.ZodString;
6
7
  }, "strip", z.ZodTypeAny, {
@@ -25,6 +26,9 @@ export interface AssessOutput {
25
26
  last_verified: string;
26
27
  notes: string;
27
28
  links: DeviceLinks;
29
+ agent_platforms: AgentPlatform[];
30
+ physical: DeviceEntry["physical"] | null;
31
+ printables: Printable[];
28
32
  };
29
33
  message?: string;
30
34
  }
@@ -1,6 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { applyBrickRiskSafety } from "../safety.js";
3
3
  import { buildLinks } from "../links.js";
4
+ import { agentPlatformsFor, printablesFor, } from "../platforms/index.js";
4
5
  export const assessHackabilityInput = z.object({
5
6
  device_name: z.string().min(1).max(200),
6
7
  });
@@ -10,12 +11,35 @@ function norm(s) {
10
11
  }
11
12
  export function assessHackability(input, catalog) {
12
13
  const target = norm(input.device_name);
13
- // Exact match by id, then by normalized name, then substring.
14
+ // Exact match by id, then by normalized name.
14
15
  let match = catalog.find((d) => d.id === input.device_name);
15
16
  if (!match)
16
17
  match = catalog.find((d) => norm(d.name) === target);
17
- if (!match)
18
- match = catalog.find((d) => norm(d.name).includes(target) || target.includes(norm(d.name)));
18
+ // Substring match: only the catalog-name-contains-query direction (e.g.
19
+ // "psp" -> "Sony PSP-1000"). The reverse direction (query contains a catalog
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
+ }
19
43
  if (!match) {
20
44
  return {
21
45
  found: false,
@@ -24,13 +48,18 @@ export function assessHackability(input, catalog) {
24
48
  }
25
49
  const safety = applyBrickRiskSafety(match);
26
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;
27
56
  return {
28
57
  found: true,
29
58
  device: {
30
59
  id: match.id,
31
60
  name: match.name,
32
61
  category: match.category,
33
- hackable: true, // presence in catalog implies hackable
62
+ hackable,
34
63
  hack_difficulty: match.hack_difficulty,
35
64
  brick_risk: safety.brick_risk,
36
65
  brick_risk_label: safety.brick_risk_label,
@@ -40,7 +69,18 @@ export function assessHackability(input, catalog) {
40
69
  last_verified: match.last_verified,
41
70
  notes: match.notes,
42
71
  links,
72
+ agent_platforms: agentPlatforms,
73
+ physical: match.physical ?? null,
74
+ printables: printablesFor(match),
43
75
  },
44
76
  };
45
77
  }
78
+ function safeAgentPlatformsFor(deviceId) {
79
+ try {
80
+ return agentPlatformsFor(deviceId);
81
+ }
82
+ catch {
83
+ return [];
84
+ }
85
+ }
46
86
  //# sourceMappingURL=assess_hackability.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"assess_hackability.js","sourceRoot":"","sources":["../../src/tools/assess_hackability.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,UAAU,EAAoB,MAAM,aAAa,CAAC;AAE3D,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7C,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;CACxC,CAAC,CAAC;AAwBH,qEAAqE;AACrE,SAAS,IAAI,CAAC,CAAS;IACrB,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,KAAkB,EAClB,OAAsB;IAEtB,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAEvC,8DAA8D;IAC9D,IAAI,KAAK,GAA4B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,WAAW,CAAC,CAAC;IACrF,IAAI,CAAC,KAAK;QAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,MAAM,CAAC,CAAC;IACjE,IAAI,CAAC,KAAK;QAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAExG,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO;YACL,KAAK,EAAE,KAAK;YACZ,OAAO,EAAE,IAAI,KAAK,CAAC,WAAW,4JAA4J;SAC3L,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;IAC3C,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;IAEhC,OAAO;QACL,KAAK,EAAE,IAAI;QACX,MAAM,EAAE;YACN,EAAE,EAAE,KAAK,CAAC,EAAE;YACZ,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,QAAQ,EAAE,IAAI,EAAE,uCAAuC;YACvD,eAAe,EAAE,KAAK,CAAC,eAAe;YACtC,UAAU,EAAE,MAAM,CAAC,UAAU;YAC7B,gBAAgB,EAAE,MAAM,CAAC,gBAAgB;YACzC,qBAAqB,EAAE,MAAM,CAAC,qBAAqB;YACnD,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,cAAc,EAAE,KAAK,CAAC,qBAAqB;YAC3C,aAAa,EAAE,KAAK,CAAC,aAAa;YAClC,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,KAAK;SACN;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"assess_hackability.js","sourceRoot":"","sources":["../../src/tools/assess_hackability.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ import { z } from "zod";
2
+ import type { DeviceEntry } from "../catalog/schema.js";
3
+ import { type Printable } from "../platforms/index.js";
4
+ export declare const NEED_VALUES: readonly ["voice", "screen", "images", "touch", "camera", "air-sensors", "e-ink", "battery", "round", "home-tunnel", "big-screen", "compute", "linux"];
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
+ }
64
+ export declare function planGadget(input: PlanGadgetInput, catalog: DeviceEntry[]): PlanGadgetOutput;
65
+ export declare function inferNeeds(idea: string): Need[];
66
+ export {};