@temet/cli 0.2.0

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 ADDED
@@ -0,0 +1,57 @@
1
+ # @temet/cli
2
+
3
+ Temet CLI for MCP config setup.
4
+
5
+ ## Install / Run
6
+
7
+ ```bash
8
+ pnpm dlx @temet/cli install-handler
9
+ pnpm dlx @temet/cli connect --address <16hex> --token <token>
10
+ ```
11
+
12
+ ## Command
13
+
14
+ ```bash
15
+ temet connect --address <16hex> --token <token> [--config-path <path>] [--relay-url <url>] [--name temet] [--dry-run]
16
+ temet connect-url --url "temet://connect?address=<16hex>&token=<token>&relay=<url>&name=temet" [--config-path <path>] [--name temet] [--relay-url <url>] [--dry-run]
17
+ temet install-handler [--dry-run]
18
+ temet uninstall-handler [--dry-run]
19
+ ```
20
+
21
+ ## What it does
22
+
23
+ - Creates or updates an MCP config file (default: `.mcp.json`)
24
+ - Upserts a `temet` MCP HTTP server entry
25
+ - Preserves existing MCP server entries
26
+ - Accepts `temet://connect?...` deep links via `connect-url`
27
+ - Installs native `temet://` protocol handler (macOS, Linux, Windows)
28
+
29
+ ## Native handler quickstart
30
+
31
+ ```bash
32
+ pnpm dlx @temet/cli install-handler
33
+ ```
34
+
35
+ Then use a Quick connect button in Temet UI.
36
+
37
+ To remove:
38
+
39
+ ```bash
40
+ pnpm dlx @temet/cli uninstall-handler
41
+ ```
42
+
43
+ ## Defaults
44
+
45
+ - relay URL: `https://temet-relay.ramponneau.workers.dev/mcp`
46
+ - config path: `./.mcp.json`
47
+ - server name: `temet`
48
+
49
+ ## Notes
50
+
51
+ V1 does not handle login or token generation. Get `address` and `token` from Temet Agent Hub.
52
+
53
+ ## Release automation
54
+
55
+ - Versioning and changelog are managed by `release-please` for `packages/cli`.
56
+ - npm publication is automated from GitHub Actions when a CLI release is created.
57
+ - Required repo secret: `NPM_TOKEN` (npm token with publish rights for `@temet/cli`).
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,513 @@
1
+ #!/usr/bin/env node
2
+ import { execFile } from "node:child_process";
3
+ import { chmod, mkdir, readFile, rm, writeFile } from "node:fs/promises";
4
+ import { homedir } from "node:os";
5
+ import { dirname, resolve } from "node:path";
6
+ import { promisify } from "node:util";
7
+ const execFileAsync = promisify(execFile);
8
+ const DEFAULT_RELAY_URL = "https://temet-relay.ramponneau.workers.dev/mcp";
9
+ const DEFAULT_SERVER_NAME = "temet";
10
+ const DEFAULT_PROTOCOL_APP_NAME = "Temet Handler";
11
+ const LINUX_DESKTOP_ENTRY = "temet-handler.desktop";
12
+ const LINUX_HANDLER_SCRIPT = "temet-protocol-handler";
13
+ const HELP = `Temet CLI
14
+
15
+ Usage:
16
+ \ttemet connect --address <16hex> --token <token> [--config-path <path>] [--relay-url <url>] [--name temet] [--dry-run]
17
+ \ttemet connect-url --url "temet://connect?address=<16hex>&token=<token>&relay=<url>&name=temet" [--config-path <path>] [--name temet] [--relay-url <url>] [--dry-run]
18
+ \ttemet install-handler [--dry-run]
19
+ \ttemet uninstall-handler [--dry-run]
20
+
21
+ Examples:
22
+ \ttemet connect --address a3f8c2d19f0b7e41 --token <token>
23
+ \ttemet connect-url --url "temet://connect?address=a3f8c2d19f0b7e41&token=<token>&relay=https%3A%2F%2Ftemet-relay.ramponneau.workers.dev%2Fmcp"
24
+ \tpnpm dlx @temet/cli install-handler
25
+ \tpnpm dlx @temet/cli connect-url --url "temet://connect?address=a3f8c2d19f0b7e41&token=<token>"
26
+ `;
27
+ function printHelp(exitCode = 0) {
28
+ console.log(HELP);
29
+ process.exit(exitCode);
30
+ }
31
+ function isObject(value) {
32
+ return typeof value === "object" && value !== null && !Array.isArray(value);
33
+ }
34
+ function normalizeAddress(raw) {
35
+ const address = raw.trim().toLowerCase();
36
+ if (!/^[a-f0-9]{16}$/i.test(address)) {
37
+ throw new Error("Invalid address. Expected 16 hex chars.");
38
+ }
39
+ return address;
40
+ }
41
+ function parseFlagBag(args) {
42
+ const flags = new Map();
43
+ const positionals = [];
44
+ for (let i = 0; i < args.length; i++) {
45
+ const arg = args[i];
46
+ if (!arg.startsWith("--")) {
47
+ positionals.push(arg);
48
+ continue;
49
+ }
50
+ if (arg === "--dry-run") {
51
+ flags.set("dry-run", true);
52
+ continue;
53
+ }
54
+ const key = arg.slice(2);
55
+ const value = args[i + 1];
56
+ if (!value || value.startsWith("--")) {
57
+ throw new Error(`Missing value for --${key}`);
58
+ }
59
+ flags.set(key, value);
60
+ i += 1;
61
+ }
62
+ return { flags, positionals };
63
+ }
64
+ function readOptionalString(flags, key) {
65
+ const value = flags.get(key);
66
+ if (typeof value !== "string")
67
+ return null;
68
+ const trimmed = value.trim();
69
+ return trimmed.length > 0 ? trimmed : null;
70
+ }
71
+ function parseConnectOptions(flags) {
72
+ const address = normalizeAddress(String(flags.get("address") ?? ""));
73
+ const token = String(flags.get("token") ?? "").trim();
74
+ const configPath = resolve(String(flags.get("config-path") ?? ".mcp.json").trim());
75
+ const relayUrl = String(flags.get("relay-url") ?? DEFAULT_RELAY_URL).trim();
76
+ const serverName = String(flags.get("name") ?? DEFAULT_SERVER_NAME).trim();
77
+ const dryRun = Boolean(flags.get("dry-run"));
78
+ if (token.length === 0) {
79
+ throw new Error("Missing --token.");
80
+ }
81
+ if (serverName.length === 0) {
82
+ throw new Error("Invalid --name.");
83
+ }
84
+ if (relayUrl.length === 0) {
85
+ throw new Error("Invalid --relay-url.");
86
+ }
87
+ return {
88
+ address,
89
+ token,
90
+ configPath,
91
+ relayUrl,
92
+ serverName,
93
+ dryRun,
94
+ };
95
+ }
96
+ function parseTemetDeepLink(rawUrl) {
97
+ let url;
98
+ try {
99
+ url = new URL(rawUrl);
100
+ }
101
+ catch {
102
+ throw new Error("Invalid --url. Expected a valid temet:// URL.");
103
+ }
104
+ if (url.protocol !== "temet:") {
105
+ throw new Error("Invalid deep link protocol. Expected temet://");
106
+ }
107
+ const route = `${url.hostname}${url.pathname}`
108
+ .replace(/^\/+/, "")
109
+ .replace(/\/+$/, "")
110
+ .toLowerCase();
111
+ if (route !== "connect") {
112
+ throw new Error("Invalid deep link route. Expected temet://connect");
113
+ }
114
+ const address = normalizeAddress(url.searchParams.get("address") ?? "");
115
+ const token = (url.searchParams.get("token") ?? "").trim();
116
+ const relayUrl = (url.searchParams.get("relay") ?? DEFAULT_RELAY_URL).trim();
117
+ const serverName = (url.searchParams.get("name") ?? DEFAULT_SERVER_NAME).trim();
118
+ if (token.length === 0) {
119
+ throw new Error("Deep link missing token parameter.");
120
+ }
121
+ if (relayUrl.length === 0) {
122
+ throw new Error("Deep link has empty relay parameter.");
123
+ }
124
+ if (serverName.length === 0) {
125
+ throw new Error("Deep link has empty name parameter.");
126
+ }
127
+ return { address, token, relayUrl, serverName };
128
+ }
129
+ function parseConnectUrlOptions(flags, positionals) {
130
+ const deeplink = readOptionalString(flags, "url") ?? positionals[0] ?? "";
131
+ if (!deeplink) {
132
+ throw new Error("Missing --url for connect-url command.");
133
+ }
134
+ const parsed = parseTemetDeepLink(deeplink);
135
+ const configPath = resolve(String(flags.get("config-path") ?? ".mcp.json").trim());
136
+ const relayUrl = readOptionalString(flags, "relay-url") ??
137
+ parsed.relayUrl ??
138
+ DEFAULT_RELAY_URL;
139
+ const serverName = readOptionalString(flags, "name") ??
140
+ parsed.serverName ??
141
+ DEFAULT_SERVER_NAME;
142
+ const dryRun = Boolean(flags.get("dry-run"));
143
+ return {
144
+ address: parsed.address,
145
+ token: parsed.token,
146
+ configPath,
147
+ relayUrl,
148
+ serverName,
149
+ dryRun,
150
+ };
151
+ }
152
+ function parseArgs(argv) {
153
+ if (argv.length === 0 || argv.includes("-h") || argv.includes("--help")) {
154
+ printHelp(0);
155
+ }
156
+ const [command, ...rest] = argv;
157
+ const { flags, positionals } = parseFlagBag(rest);
158
+ const dryRun = Boolean(flags.get("dry-run"));
159
+ if (command === "connect") {
160
+ return {
161
+ command,
162
+ options: parseConnectOptions(flags),
163
+ };
164
+ }
165
+ if (command === "connect-url") {
166
+ return {
167
+ command,
168
+ options: parseConnectUrlOptions(flags, positionals),
169
+ };
170
+ }
171
+ if (command === "install-handler" || command === "uninstall-handler") {
172
+ return { command, dryRun };
173
+ }
174
+ console.error(`Unknown command: ${command}`);
175
+ printHelp(1);
176
+ }
177
+ async function readConfig(configPath) {
178
+ try {
179
+ const content = await readFile(configPath, "utf8");
180
+ if (!content.trim())
181
+ return {};
182
+ const parsed = JSON.parse(content);
183
+ if (!isObject(parsed)) {
184
+ throw new Error("MCP config must be a JSON object.");
185
+ }
186
+ return parsed;
187
+ }
188
+ catch (error) {
189
+ const e = error;
190
+ if (e.code === "ENOENT") {
191
+ return {};
192
+ }
193
+ throw error;
194
+ }
195
+ }
196
+ function buildServerEntry(opts) {
197
+ return {
198
+ type: "http",
199
+ url: opts.relayUrl,
200
+ headers: {
201
+ "X-Temet-Address": opts.address,
202
+ Authorization: `Bearer ${opts.token}`,
203
+ },
204
+ };
205
+ }
206
+ function upsertServer(config, serverName, entry) {
207
+ if (isObject(config.mcpServers)) {
208
+ const servers = config.mcpServers;
209
+ servers[serverName] = entry;
210
+ config.mcpServers = servers;
211
+ return config;
212
+ }
213
+ // Fresh or flat config — always wrap in mcpServers (Claude Code / Cursor expect this)
214
+ config.mcpServers = { [serverName]: entry };
215
+ return config;
216
+ }
217
+ async function writeConfig(configPath, config) {
218
+ await mkdir(dirname(configPath), { recursive: true });
219
+ await writeFile(configPath, `${JSON.stringify(config, null, "\t")}\n`, "utf8");
220
+ }
221
+ async function runWriteFlow(command, opts) {
222
+ const current = await readConfig(opts.configPath);
223
+ const next = upsertServer(current, opts.serverName, buildServerEntry(opts));
224
+ if (opts.dryRun) {
225
+ console.log("[temet] dry-run: config preview");
226
+ console.log(JSON.stringify(next, null, "\t"));
227
+ return;
228
+ }
229
+ await writeConfig(opts.configPath, next);
230
+ console.log(`[temet] config updated: ${opts.configPath}`);
231
+ console.log(`[temet] server: ${opts.serverName}`);
232
+ console.log(`[temet] source: ${command}`);
233
+ console.log("[temet] next: restart your MCP client (Claude Code/Cursor/Codex).");
234
+ }
235
+ async function resolveBinary(name) {
236
+ try {
237
+ if (process.platform === "win32") {
238
+ const { stdout } = await execFileAsync("where", [name]);
239
+ const line = stdout
240
+ .split(/\r?\n/)
241
+ .map((entry) => entry.trim())
242
+ .find((entry) => entry.length > 0);
243
+ return line ?? null;
244
+ }
245
+ const { stdout } = await execFileAsync("which", [name]);
246
+ const line = stdout
247
+ .split(/\r?\n/)
248
+ .map((entry) => entry.trim())
249
+ .find((entry) => entry.length > 0);
250
+ return line ?? null;
251
+ }
252
+ catch {
253
+ return null;
254
+ }
255
+ }
256
+ function shQuote(value) {
257
+ return `'${value.replace(/'/g, `'\"'\"'`)}'`;
258
+ }
259
+ function buildProtocolHandlerScript(temetBin, pnpmBin) {
260
+ const temetRunner = temetBin
261
+ ? `${shQuote(temetBin)} connect-url --url "$URL"`
262
+ : null;
263
+ const pnpmRunner = pnpmBin
264
+ ? `${shQuote(pnpmBin)} dlx @temet/cli connect-url --url "$URL"`
265
+ : null;
266
+ const runLine = temetRunner ??
267
+ pnpmRunner ??
268
+ `echo "[temet] no runner found (need temet or pnpm in PATH)"`;
269
+ return `#!/bin/sh
270
+ URL="$1"
271
+ LOG_DIR="$HOME/.temet"
272
+ LOG_FILE="$LOG_DIR/handler.log"
273
+
274
+ mkdir -p "$LOG_DIR"
275
+
276
+ {
277
+ echo "[$(date -u +"%Y-%m-%dT%H:%M:%SZ")] handler URL: $URL"
278
+ if [ -z "$URL" ]; then
279
+ echo "[temet] empty URL"
280
+ exit 1
281
+ fi
282
+ ${runLine}
283
+ STATUS=$?
284
+ if [ "$STATUS" -ne 0 ]; then
285
+ echo "[temet] handler failed with exit $STATUS"
286
+ exit "$STATUS"
287
+ fi
288
+ echo "[temet] handler success"
289
+ } >> "$LOG_FILE" 2>&1
290
+ `;
291
+ }
292
+ async function safeExec(command, args, errorHint) {
293
+ try {
294
+ await execFileAsync(command, args);
295
+ }
296
+ catch {
297
+ console.warn(`[temet] warning: ${errorHint}`);
298
+ }
299
+ }
300
+ async function installMacHandler(dryRun) {
301
+ const appDir = resolve(homedir(), "Applications", `${DEFAULT_PROTOCOL_APP_NAME}.app`);
302
+ const contentsDir = resolve(appDir, "Contents");
303
+ const macOSDir = resolve(contentsDir, "MacOS");
304
+ const executablePath = resolve(macOSDir, "temet-handler");
305
+ const infoPlistPath = resolve(contentsDir, "Info.plist");
306
+ const lsregisterPath = "/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister";
307
+ const temetBin = await resolveBinary("temet");
308
+ const pnpmBin = await resolveBinary("pnpm");
309
+ const script = buildProtocolHandlerScript(temetBin, pnpmBin);
310
+ const infoPlist = `<?xml version="1.0" encoding="UTF-8"?>
311
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
312
+ <plist version="1.0">
313
+ <dict>
314
+ <key>CFBundleName</key>
315
+ <string>${DEFAULT_PROTOCOL_APP_NAME}</string>
316
+ <key>CFBundleDisplayName</key>
317
+ <string>${DEFAULT_PROTOCOL_APP_NAME}</string>
318
+ <key>CFBundleIdentifier</key>
319
+ <string>com.temet.protocol-handler</string>
320
+ <key>CFBundleVersion</key>
321
+ <string>1</string>
322
+ <key>CFBundlePackageType</key>
323
+ <string>APPL</string>
324
+ <key>CFBundleExecutable</key>
325
+ <string>temet-handler</string>
326
+ <key>CFBundleURLTypes</key>
327
+ <array>
328
+ <dict>
329
+ <key>CFBundleURLName</key>
330
+ <string>Temet Protocol</string>
331
+ <key>CFBundleURLSchemes</key>
332
+ <array>
333
+ <string>temet</string>
334
+ </array>
335
+ </dict>
336
+ </array>
337
+ </dict>
338
+ </plist>
339
+ `;
340
+ if (dryRun) {
341
+ console.log(`[temet] dry-run macOS install path: ${appDir}`);
342
+ console.log(`[temet] dry-run executable: ${executablePath}`);
343
+ return;
344
+ }
345
+ await mkdir(macOSDir, { recursive: true });
346
+ await writeFile(infoPlistPath, infoPlist, "utf8");
347
+ await writeFile(executablePath, script, "utf8");
348
+ await chmod(executablePath, 0o755);
349
+ await safeExec(lsregisterPath, ["-f", appDir], "unable to refresh LaunchServices registration (you may need to open the app once)");
350
+ console.log(`[temet] macOS handler installed: ${appDir}`);
351
+ if (!temetBin && !pnpmBin) {
352
+ console.log("[temet] warning: neither 'temet' nor 'pnpm' was found in PATH during install.");
353
+ }
354
+ }
355
+ async function uninstallMacHandler(dryRun) {
356
+ const appDir = resolve(homedir(), "Applications", `${DEFAULT_PROTOCOL_APP_NAME}.app`);
357
+ const lsregisterPath = "/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister";
358
+ if (dryRun) {
359
+ console.log(`[temet] dry-run macOS uninstall path: ${appDir}`);
360
+ return;
361
+ }
362
+ await safeExec(lsregisterPath, ["-u", appDir], "unable to unregister LaunchServices entry");
363
+ await rm(appDir, { recursive: true, force: true });
364
+ console.log(`[temet] macOS handler removed: ${appDir}`);
365
+ }
366
+ async function installLinuxHandler(dryRun) {
367
+ const localShare = resolve(homedir(), ".local", "share", "applications");
368
+ const localBin = resolve(homedir(), ".local", "bin");
369
+ const desktopPath = resolve(localShare, LINUX_DESKTOP_ENTRY);
370
+ const scriptPath = resolve(localBin, LINUX_HANDLER_SCRIPT);
371
+ const temetBin = await resolveBinary("temet");
372
+ const pnpmBin = await resolveBinary("pnpm");
373
+ const script = buildProtocolHandlerScript(temetBin, pnpmBin);
374
+ const desktopEntry = `[Desktop Entry]
375
+ Name=Temet Protocol Handler
376
+ Exec=${scriptPath} %u
377
+ Type=Application
378
+ Terminal=false
379
+ NoDisplay=true
380
+ MimeType=x-scheme-handler/temet;
381
+ `;
382
+ if (dryRun) {
383
+ console.log(`[temet] dry-run linux desktop file: ${desktopPath}`);
384
+ console.log(`[temet] dry-run linux script path: ${scriptPath}`);
385
+ return;
386
+ }
387
+ await mkdir(localShare, { recursive: true });
388
+ await mkdir(localBin, { recursive: true });
389
+ await writeFile(scriptPath, script, "utf8");
390
+ await chmod(scriptPath, 0o755);
391
+ await writeFile(desktopPath, desktopEntry, "utf8");
392
+ await safeExec("xdg-mime", ["default", LINUX_DESKTOP_ENTRY, "x-scheme-handler/temet"], "xdg-mime registration failed. Try running it manually.");
393
+ await safeExec("update-desktop-database", [localShare], "update-desktop-database not available");
394
+ console.log(`[temet] linux handler installed: ${desktopPath}`);
395
+ }
396
+ async function uninstallLinuxHandler(dryRun) {
397
+ const localShare = resolve(homedir(), ".local", "share", "applications");
398
+ const localBin = resolve(homedir(), ".local", "bin");
399
+ const desktopPath = resolve(localShare, LINUX_DESKTOP_ENTRY);
400
+ const scriptPath = resolve(localBin, LINUX_HANDLER_SCRIPT);
401
+ if (dryRun) {
402
+ console.log(`[temet] dry-run linux uninstall desktop file: ${desktopPath}`);
403
+ console.log(`[temet] dry-run linux uninstall script path: ${scriptPath}`);
404
+ return;
405
+ }
406
+ await rm(desktopPath, { force: true });
407
+ await rm(scriptPath, { force: true });
408
+ console.log(`[temet] linux handler removed`);
409
+ }
410
+ async function installWindowsHandler(dryRun) {
411
+ const temetBin = await resolveBinary("temet");
412
+ const pnpmBin = await resolveBinary("pnpm");
413
+ const runner = temetBin
414
+ ? `"${temetBin}" connect-url --url "%1"`
415
+ : pnpmBin
416
+ ? `"${pnpmBin}" dlx @temet/cli connect-url --url "%1"`
417
+ : `temet connect-url --url "%1"`;
418
+ const commandValue = `cmd.exe /d /s /c ${runner}`;
419
+ if (dryRun) {
420
+ console.log(`[temet] dry-run windows command: ${commandValue}`);
421
+ return;
422
+ }
423
+ await execFileAsync("reg", [
424
+ "add",
425
+ "HKCU\\Software\\Classes\\temet",
426
+ "/ve",
427
+ "/d",
428
+ "URL:Temet Protocol",
429
+ "/f",
430
+ ]);
431
+ await execFileAsync("reg", [
432
+ "add",
433
+ "HKCU\\Software\\Classes\\temet",
434
+ "/v",
435
+ "URL Protocol",
436
+ "/d",
437
+ "",
438
+ "/f",
439
+ ]);
440
+ await execFileAsync("reg", [
441
+ "add",
442
+ "HKCU\\Software\\Classes\\temet\\shell\\open\\command",
443
+ "/ve",
444
+ "/d",
445
+ commandValue,
446
+ "/f",
447
+ ]);
448
+ console.log("[temet] windows handler installed (HKCU\\Software\\Classes\\temet)");
449
+ }
450
+ async function uninstallWindowsHandler(dryRun) {
451
+ if (dryRun) {
452
+ console.log("[temet] dry-run windows uninstall key: HKCU\\Software\\Classes\\temet");
453
+ return;
454
+ }
455
+ await execFileAsync("reg", [
456
+ "delete",
457
+ "HKCU\\Software\\Classes\\temet",
458
+ "/f",
459
+ ]);
460
+ console.log("[temet] windows handler removed (HKCU\\Software\\Classes\\temet)");
461
+ }
462
+ async function installProtocolHandler(dryRun) {
463
+ if (process.platform === "darwin") {
464
+ await installMacHandler(dryRun);
465
+ return;
466
+ }
467
+ if (process.platform === "linux") {
468
+ await installLinuxHandler(dryRun);
469
+ return;
470
+ }
471
+ if (process.platform === "win32") {
472
+ await installWindowsHandler(dryRun);
473
+ return;
474
+ }
475
+ throw new Error(`Unsupported platform: ${process.platform}`);
476
+ }
477
+ async function uninstallProtocolHandler(dryRun) {
478
+ if (process.platform === "darwin") {
479
+ await uninstallMacHandler(dryRun);
480
+ return;
481
+ }
482
+ if (process.platform === "linux") {
483
+ await uninstallLinuxHandler(dryRun);
484
+ return;
485
+ }
486
+ if (process.platform === "win32") {
487
+ await uninstallWindowsHandler(dryRun);
488
+ return;
489
+ }
490
+ throw new Error(`Unsupported platform: ${process.platform}`);
491
+ }
492
+ async function run() {
493
+ const parsed = parseArgs(process.argv.slice(2));
494
+ if (parsed.command === "connect" || parsed.command === "connect-url") {
495
+ await runWriteFlow(parsed.command, parsed.options);
496
+ return;
497
+ }
498
+ if (parsed.command === "install-handler") {
499
+ await installProtocolHandler(parsed.dryRun);
500
+ console.log("[temet] next: click a Quick connect button in Temet.");
501
+ return;
502
+ }
503
+ if (parsed.command === "uninstall-handler") {
504
+ await uninstallProtocolHandler(parsed.dryRun);
505
+ return;
506
+ }
507
+ throw new Error(`Unhandled command: ${parsed.command}`);
508
+ }
509
+ run().catch((error) => {
510
+ const message = error instanceof Error ? error.message : String(error);
511
+ console.error(`[temet] error: ${message}`);
512
+ process.exit(1);
513
+ });
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@temet/cli",
3
+ "version": "0.2.0",
4
+ "description": "Temet CLI for MCP configuration",
5
+ "keywords": [
6
+ "temet",
7
+ "mcp",
8
+ "byoa",
9
+ "cli",
10
+ "agent"
11
+ ],
12
+ "license": "MIT",
13
+ "homepage": "https://temetapp.com",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/ramponneau/temet.git",
17
+ "directory": "packages/cli"
18
+ },
19
+ "bugs": {
20
+ "url": "https://github.com/ramponneau/temet/issues"
21
+ },
22
+ "type": "module",
23
+ "bin": {
24
+ "temet": "dist/index.js"
25
+ },
26
+ "files": [
27
+ "dist"
28
+ ],
29
+ "scripts": {
30
+ "build": "tsc -p tsconfig.json",
31
+ "typecheck": "tsc --noEmit -p tsconfig.json"
32
+ },
33
+ "publishConfig": {
34
+ "access": "public"
35
+ }
36
+ }