@schuttdev/gigai 0.1.0-beta.20 → 0.1.0-beta.21

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.
@@ -253,6 +253,7 @@ var ServerConfigSchema = z.object({
253
253
  https: HttpsConfigSchema.optional()
254
254
  });
255
255
  var GigaiConfigSchema = z.object({
256
+ serverName: z.string().optional(),
256
257
  server: ServerConfigSchema,
257
258
  auth: AuthConfigSchema,
258
259
  tools: z.array(ToolConfigSchema).default([])
@@ -6,7 +6,7 @@ import {
6
6
  decrypt,
7
7
  encrypt,
8
8
  generateEncryptionKey
9
- } from "./chunk-HIKBVSBK.js";
9
+ } from "./chunk-7C3UYEKE.js";
10
10
 
11
11
  // ../server/dist/index.mjs
12
12
  import { parseArgs } from "util";
@@ -17,6 +17,7 @@ import multipart from "@fastify/multipart";
17
17
  import fp from "fastify-plugin";
18
18
  import { nanoid } from "nanoid";
19
19
  import { randomBytes } from "crypto";
20
+ import { hostname } from "os";
20
21
  import { nanoid as nanoid2 } from "nanoid";
21
22
  import fp2 from "fastify-plugin";
22
23
  import fp3 from "fastify-plugin";
@@ -185,6 +186,7 @@ function validateAndPair(store, code, orgUuid, encryptionKey, serverFingerprint)
185
186
  }
186
187
  function registerAuthRoutes(server, store, config) {
187
188
  const serverFingerprint = randomBytes(16).toString("hex");
189
+ const serverName = config.serverName ?? hostname();
188
190
  server.post("/auth/pair", {
189
191
  config: {
190
192
  rateLimit: { max: 5, timeWindow: "1 hour" }
@@ -208,7 +210,7 @@ function registerAuthRoutes(server, store, config) {
208
210
  config.auth.encryptionKey,
209
211
  serverFingerprint
210
212
  );
211
- return { encryptedToken: JSON.stringify(encryptedToken) };
213
+ return { encryptedToken: JSON.stringify(encryptedToken), serverName };
212
214
  });
213
215
  server.post("/auth/connect", {
214
216
  config: {
@@ -1131,8 +1133,25 @@ async function runInit() {
1131
1133
  config: { allowlist, allowSudo }
1132
1134
  });
1133
1135
  }
1136
+ let serverName;
1137
+ if (httpsProvider === "tailscale") {
1138
+ const dnsName = await getTailscaleDnsName();
1139
+ if (dnsName) {
1140
+ serverName = dnsName.split(".")[0];
1141
+ }
1142
+ } else if (httpsProvider === "cloudflare") {
1143
+ serverName = await input({
1144
+ message: "Server name (identifies this machine):",
1145
+ required: true
1146
+ });
1147
+ }
1148
+ if (!serverName) {
1149
+ const { hostname: osHostname } = await import("os");
1150
+ serverName = osHostname();
1151
+ }
1134
1152
  const encryptionKey = generateEncryptionKey();
1135
1153
  const config = {
1154
+ serverName,
1136
1155
  server: {
1137
1156
  port,
1138
1157
  host: "0.0.0.0",
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  decodeJWTPayload
4
- } from "./chunk-HIKBVSBK.js";
4
+ } from "./chunk-7C3UYEKE.js";
5
5
 
6
6
  // src/index.ts
7
7
  import { defineCommand, runMain } from "citty";
@@ -29,9 +29,26 @@ function getConfigPath() {
29
29
  async function readConfig() {
30
30
  try {
31
31
  const raw = await readFile(getConfigPath(), "utf8");
32
- return JSON.parse(raw);
32
+ const parsed = JSON.parse(raw);
33
+ if (parsed.server && parsed.token && !parsed.servers) {
34
+ const name = deriveServerName(parsed.server);
35
+ const migrated = {
36
+ activeServer: name,
37
+ servers: {
38
+ [name]: {
39
+ server: parsed.server,
40
+ token: parsed.token,
41
+ sessionToken: parsed.sessionToken,
42
+ sessionExpiresAt: parsed.sessionExpiresAt
43
+ }
44
+ }
45
+ };
46
+ await writeConfig(migrated);
47
+ return migrated;
48
+ }
49
+ return { activeServer: parsed.activeServer, servers: parsed.servers ?? {} };
33
50
  } catch {
34
- return {};
51
+ return { servers: {} };
35
52
  }
36
53
  }
37
54
  async function writeConfig(config) {
@@ -39,11 +56,47 @@ async function writeConfig(config) {
39
56
  await mkdir(dir, { recursive: true });
40
57
  await writeFile(getConfigPath(), JSON.stringify(config, null, 2) + "\n", { mode: 384 });
41
58
  }
42
- async function updateConfig(updates) {
59
+ function getActiveEntry(config) {
60
+ if (!config.activeServer || !config.servers[config.activeServer]) return void 0;
61
+ return { name: config.activeServer, entry: config.servers[config.activeServer] };
62
+ }
63
+ async function addServer(name, server, token) {
64
+ const config = await readConfig();
65
+ for (const [existingName, entry] of Object.entries(config.servers)) {
66
+ if (normalizeUrl(entry.server) === normalizeUrl(server)) {
67
+ config.servers[existingName] = { server, token };
68
+ config.activeServer = existingName;
69
+ await writeConfig(config);
70
+ return;
71
+ }
72
+ }
73
+ config.servers[name] = { server, token };
74
+ config.activeServer = name;
75
+ await writeConfig(config);
76
+ }
77
+ async function updateServerSession(name, sessionToken, sessionExpiresAt) {
43
78
  const config = await readConfig();
44
- Object.assign(config, updates);
79
+ const entry = config.servers[name];
80
+ if (!entry) return;
81
+ entry.sessionToken = sessionToken;
82
+ entry.sessionExpiresAt = sessionExpiresAt;
45
83
  await writeConfig(config);
46
- return config;
84
+ }
85
+ function deriveServerName(url) {
86
+ try {
87
+ const hostname = new URL(url).hostname;
88
+ return hostname.split(".")[0];
89
+ } catch {
90
+ return "default";
91
+ }
92
+ }
93
+ function normalizeUrl(url) {
94
+ try {
95
+ const u = new URL(url);
96
+ return u.hostname.toLowerCase();
97
+ } catch {
98
+ return url.toLowerCase();
99
+ }
47
100
  }
48
101
 
49
102
  // src/identity.ts
@@ -193,56 +246,42 @@ function createHttpClient(serverUrl, sessionToken) {
193
246
  }
194
247
 
195
248
  // src/connect.ts
196
- async function connect() {
249
+ async function connect(serverName) {
197
250
  const config = await readConfig();
198
- if (!config.server) {
199
- throw new Error("No server configured. Run 'gigai pair' first.");
251
+ if (serverName) {
252
+ if (!config.servers[serverName]) {
253
+ const available = Object.keys(config.servers);
254
+ throw new Error(
255
+ available.length > 0 ? `Unknown server "${serverName}". Available: ${available.join(", ")}` : `No servers configured. Run 'gigai pair' first.`
256
+ );
257
+ }
258
+ config.activeServer = serverName;
259
+ await writeConfig(config);
200
260
  }
201
- if (!config.token) {
202
- throw new Error("No auth token found. Run 'gigai pair' first.");
261
+ const active = getActiveEntry(config);
262
+ if (!active) {
263
+ throw new Error("No server configured. Run 'gigai pair' first.");
203
264
  }
204
- if (config.sessionToken && config.sessionExpiresAt) {
205
- if (Date.now() < config.sessionExpiresAt - 5 * 60 * 1e3) {
206
- return { serverUrl: config.server, sessionToken: config.sessionToken };
265
+ const { name, entry } = active;
266
+ if (entry.sessionToken && entry.sessionExpiresAt) {
267
+ if (Date.now() < entry.sessionExpiresAt - 5 * 60 * 1e3) {
268
+ return { serverUrl: entry.server, sessionToken: entry.sessionToken };
207
269
  }
208
270
  }
209
271
  const orgUuid = getOrgUUID();
210
- const http = createHttpClient(config.server);
272
+ const http = createHttpClient(entry.server);
211
273
  const res = await http.post("/auth/connect", {
212
- encryptedToken: config.token,
274
+ encryptedToken: entry.token,
213
275
  orgUuid
214
276
  });
215
- await updateConfig({
216
- sessionToken: res.sessionToken,
217
- sessionExpiresAt: res.expiresAt
218
- });
219
- return { serverUrl: config.server, sessionToken: res.sessionToken };
277
+ await updateServerSession(name, res.sessionToken, res.expiresAt);
278
+ return { serverUrl: entry.server, sessionToken: res.sessionToken };
220
279
  }
221
280
 
222
- // src/pair.ts
223
- async function pair(code, serverUrl) {
224
- const orgUuid = getOrgUUID();
225
- const http = createHttpClient(serverUrl);
226
- const res = await http.post("/auth/pair", {
227
- pairingCode: code,
228
- orgUuid
229
- });
230
- await updateConfig({
231
- server: serverUrl,
232
- token: res.encryptedToken,
233
- sessionToken: void 0,
234
- sessionExpiresAt: void 0
235
- });
236
- console.log("Paired successfully!\n");
237
- const tokenStr = JSON.stringify(res.encryptedToken);
238
- const skillMd = generateSkill(serverUrl, tokenStr);
239
- console.log("Save the following as a Claude Desktop skill:\n");
240
- console.log("\u2500".repeat(50));
241
- console.log(skillMd);
242
- console.log("\u2500".repeat(50));
243
- }
244
- function generateSkill(serverUrl, token) {
245
- return `---
281
+ // src/skill.ts
282
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
283
+ import "path";
284
+ var SKILL_MD = `---
246
285
  name: gigai
247
286
  description: Access tools on the user's machine via the gigai CLI
248
287
  ---
@@ -251,21 +290,12 @@ description: Access tools on the user's machine via the gigai CLI
251
290
 
252
291
  You have access to tools running on the user's local machine through the gigai CLI. This CLI proxies commands over HTTPS to a server on the user's machine.
253
292
 
254
- ## Setup (run once per session)
255
-
256
- Before using any gigai commands, you must install and configure the CLI in your code execution environment. Run this setup block:
293
+ ## Setup (run once per conversation)
257
294
 
258
295
  \`\`\`bash
259
296
  npm install -g @schuttdev/gigai 2>/dev/null
260
-
261
297
  mkdir -p ~/.gigai
262
- cat > ~/.gigai/config.json << 'GIGAI_EOF'
263
- {
264
- "server": "${serverUrl}",
265
- "token": ${token}
266
- }
267
- GIGAI_EOF
268
-
298
+ cp /mnt/skills/user/gigai/config.json ~/.gigai/config.json
269
299
  gigai connect
270
300
  \`\`\`
271
301
 
@@ -278,14 +308,26 @@ If \`gigai connect\` succeeds, you are ready to use tools. If it fails, tell the
278
308
  gigai list
279
309
  \`\`\`
280
310
 
311
+ ### Run a tool
312
+ \`\`\`bash
313
+ gigai <tool-name> [args...]
314
+ \`\`\`
315
+
316
+ Examples:
317
+ \`\`\`bash
318
+ gigai shell date
319
+ gigai fs list Documents
320
+ gigai shell whoami
321
+ \`\`\`
322
+
281
323
  ### Get help for a tool
282
324
  \`\`\`bash
283
325
  gigai help <tool-name>
284
326
  \`\`\`
285
327
 
286
- ### Run a tool
328
+ ### Switch server (if multiple are configured)
287
329
  \`\`\`bash
288
- gigai <tool-name> [args...]
330
+ gigai connect <server-name>
289
331
  \`\`\`
290
332
 
291
333
  ### File transfer
@@ -296,15 +338,148 @@ gigai download <id> <dest>
296
338
 
297
339
  ## Important
298
340
 
299
- - Always run the setup block before first use in a new session
341
+ - Always run the setup block before first use in a new conversation
300
342
  - All commands execute on the **user's machine**, not in this sandbox
301
343
  - If you get auth errors, run \`gigai connect\` to refresh the session
302
344
  - Tools are scoped to what the user has configured \u2014 if a tool is missing, tell the user
303
345
  `;
346
+ async function generateSkillZip(serverName, serverUrl, token) {
347
+ let skillConfig = { servers: {} };
348
+ try {
349
+ const raw = await readFile2("/mnt/skills/user/gigai/config.json", "utf8");
350
+ const existing = JSON.parse(raw);
351
+ if (existing.servers) {
352
+ skillConfig = existing;
353
+ }
354
+ } catch {
355
+ }
356
+ let merged = false;
357
+ for (const [name, entry] of Object.entries(skillConfig.servers)) {
358
+ if (normalizeHost(entry.server) === normalizeHost(serverUrl)) {
359
+ skillConfig.servers[name] = { server: serverUrl, token };
360
+ skillConfig.activeServer = name;
361
+ merged = true;
362
+ break;
363
+ }
364
+ }
365
+ if (!merged) {
366
+ skillConfig.servers[serverName] = { server: serverUrl, token };
367
+ skillConfig.activeServer = serverName;
368
+ }
369
+ const configJson = JSON.stringify(skillConfig, null, 2) + "\n";
370
+ return createZip([
371
+ { path: "gigai/SKILL.md", data: Buffer.from(SKILL_MD, "utf8") },
372
+ { path: "gigai/config.json", data: Buffer.from(configJson, "utf8") }
373
+ ]);
374
+ }
375
+ async function writeSkillZip(zip) {
376
+ const outputsDir = "/mnt/user-data/outputs";
377
+ try {
378
+ await mkdir2(outputsDir, { recursive: true });
379
+ const outPath = `${outputsDir}/gigai.zip`;
380
+ await writeFile2(outPath, zip);
381
+ return outPath;
382
+ } catch {
383
+ const outPath = "gigai.zip";
384
+ await writeFile2(outPath, zip);
385
+ return outPath;
386
+ }
387
+ }
388
+ function normalizeHost(url) {
389
+ try {
390
+ return new URL(url).hostname.toLowerCase();
391
+ } catch {
392
+ return url.toLowerCase();
393
+ }
394
+ }
395
+ var crc32Table = new Uint32Array(256);
396
+ for (let i = 0; i < 256; i++) {
397
+ let c = i;
398
+ for (let j = 0; j < 8; j++) {
399
+ c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
400
+ }
401
+ crc32Table[i] = c >>> 0;
402
+ }
403
+ function crc32(data) {
404
+ let crc = 4294967295;
405
+ for (const byte of data) {
406
+ crc = crc >>> 8 ^ crc32Table[(crc ^ byte) & 255];
407
+ }
408
+ return (crc ^ 4294967295) >>> 0;
409
+ }
410
+ function createZip(entries) {
411
+ const parts = [];
412
+ const centralParts = [];
413
+ let offset = 0;
414
+ for (const entry of entries) {
415
+ const name = Buffer.from(entry.path, "utf8");
416
+ const checksum = crc32(entry.data);
417
+ const local = Buffer.alloc(30);
418
+ local.writeUInt32LE(67324752, 0);
419
+ local.writeUInt16LE(20, 4);
420
+ local.writeUInt16LE(0, 6);
421
+ local.writeUInt16LE(0, 8);
422
+ local.writeUInt16LE(0, 10);
423
+ local.writeUInt16LE(0, 12);
424
+ local.writeUInt32LE(checksum, 14);
425
+ local.writeUInt32LE(entry.data.length, 18);
426
+ local.writeUInt32LE(entry.data.length, 22);
427
+ local.writeUInt16LE(name.length, 26);
428
+ local.writeUInt16LE(0, 28);
429
+ parts.push(local, name, entry.data);
430
+ const central = Buffer.alloc(46);
431
+ central.writeUInt32LE(33639248, 0);
432
+ central.writeUInt16LE(20, 4);
433
+ central.writeUInt16LE(20, 6);
434
+ central.writeUInt16LE(0, 8);
435
+ central.writeUInt16LE(0, 10);
436
+ central.writeUInt16LE(0, 12);
437
+ central.writeUInt16LE(0, 14);
438
+ central.writeUInt32LE(checksum, 16);
439
+ central.writeUInt32LE(entry.data.length, 20);
440
+ central.writeUInt32LE(entry.data.length, 24);
441
+ central.writeUInt16LE(name.length, 28);
442
+ central.writeUInt16LE(0, 30);
443
+ central.writeUInt16LE(0, 32);
444
+ central.writeUInt16LE(0, 34);
445
+ central.writeUInt16LE(0, 36);
446
+ central.writeUInt32LE(0, 38);
447
+ central.writeUInt32LE(offset, 42);
448
+ centralParts.push(central, name);
449
+ offset += 30 + name.length + entry.data.length;
450
+ }
451
+ const centralDir = Buffer.concat(centralParts);
452
+ const eocd = Buffer.alloc(22);
453
+ eocd.writeUInt32LE(101010256, 0);
454
+ eocd.writeUInt16LE(0, 4);
455
+ eocd.writeUInt16LE(0, 6);
456
+ eocd.writeUInt16LE(entries.length, 8);
457
+ eocd.writeUInt16LE(entries.length, 10);
458
+ eocd.writeUInt32LE(centralDir.length, 12);
459
+ eocd.writeUInt32LE(offset, 16);
460
+ eocd.writeUInt16LE(0, 20);
461
+ return Buffer.concat([...parts, centralDir, eocd]);
462
+ }
463
+
464
+ // src/pair.ts
465
+ async function pair(code, serverUrl) {
466
+ const orgUuid = getOrgUUID();
467
+ const http = createHttpClient(serverUrl);
468
+ const res = await http.post("/auth/pair", {
469
+ pairingCode: code,
470
+ orgUuid
471
+ });
472
+ await addServer(res.serverName, serverUrl, res.encryptedToken);
473
+ console.log(`Paired with "${res.serverName}" successfully!
474
+ `);
475
+ const zip = await generateSkillZip(res.serverName, serverUrl, res.encryptedToken);
476
+ const outPath = await writeSkillZip(zip);
477
+ console.log(`Skill zip written to: ${outPath}`);
478
+ console.log("Upload this file as a skill in Claude Desktop (Settings \u2192 Customize \u2192 Upload Skill).");
304
479
  }
305
480
 
306
481
  // src/discover.ts
307
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
482
+ import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
308
483
  import { join as join2 } from "path";
309
484
  import { homedir as homedir2 } from "os";
310
485
  var MANIFEST_TTL = 5 * 60 * 1e3;
@@ -314,7 +489,7 @@ function getManifestPath() {
314
489
  }
315
490
  async function fetchTools(http) {
316
491
  try {
317
- const raw = await readFile2(getManifestPath(), "utf8");
492
+ const raw = await readFile3(getManifestPath(), "utf8");
318
493
  const cache = JSON.parse(raw);
319
494
  if (Date.now() - cache.fetchedAt < MANIFEST_TTL) {
320
495
  return cache.tools;
@@ -324,9 +499,9 @@ async function fetchTools(http) {
324
499
  const res = await http.get("/tools");
325
500
  try {
326
501
  const dir = process.env.GIGAI_CONFIG_DIR ?? join2(homedir2(), ".gigai");
327
- await mkdir2(dir, { recursive: true });
502
+ await mkdir3(dir, { recursive: true });
328
503
  const cache = { tools: res.tools, fetchedAt: Date.now() };
329
- await writeFile2(getManifestPath(), JSON.stringify(cache));
504
+ await writeFile3(getManifestPath(), JSON.stringify(cache));
330
505
  } catch {
331
506
  }
332
507
  return res.tools;
@@ -367,11 +542,11 @@ async function execMcpTool(http, tool, mcpTool, args) {
367
542
  }
368
543
 
369
544
  // src/transfer.ts
370
- import { readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
545
+ import { readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
371
546
  import { basename } from "path";
372
547
  import { Blob } from "buffer";
373
548
  async function upload(http, filePath) {
374
- const content = await readFile3(filePath);
549
+ const content = await readFile4(filePath);
375
550
  const filename = basename(filePath);
376
551
  const formData = new FormData();
377
552
  const blob = new Blob([content]);
@@ -386,7 +561,7 @@ async function upload(http, filePath) {
386
561
  async function download(http, id, destPath) {
387
562
  const res = await http.getRaw(`/transfer/${encodeURIComponent(id)}`);
388
563
  const buffer = Buffer.from(await res.arrayBuffer());
389
- await writeFile3(destPath, buffer);
564
+ await writeFile4(destPath, buffer);
390
565
  console.log(`Downloaded to: ${destPath}`);
391
566
  }
392
567
 
@@ -427,26 +602,32 @@ Usage: ${detail.usage}`);
427
602
  }
428
603
  return lines.join("\n");
429
604
  }
430
- function formatStatus(connected, serverUrl, sessionExpiresAt) {
431
- if (!connected) {
605
+ function formatStatus(config) {
606
+ const serverNames = Object.keys(config.servers);
607
+ if (serverNames.length === 0) {
432
608
  return "Not connected. Run 'gigai pair <code> <server-url>' to set up.";
433
609
  }
434
- const lines = [`Connected to: ${serverUrl}`];
435
- if (sessionExpiresAt) {
436
- const remaining = sessionExpiresAt - Date.now();
437
- if (remaining > 0) {
438
- const mins = Math.floor(remaining / 6e4);
439
- lines.push(`Session expires in: ${mins} minutes`);
440
- } else {
441
- lines.push("Session expired. Will auto-renew on next command.");
610
+ const lines = [];
611
+ for (const name of serverNames) {
612
+ const entry = config.servers[name];
613
+ const active = name === config.activeServer ? " (active)" : "";
614
+ lines.push(` ${name}${active} ${entry.server}`);
615
+ if (entry.sessionExpiresAt) {
616
+ const remaining = entry.sessionExpiresAt - Date.now();
617
+ if (remaining > 0) {
618
+ lines.push(` Session expires in ${Math.floor(remaining / 6e4)} minutes`);
619
+ } else {
620
+ lines.push(" Session expired \u2014 will auto-renew on next command");
621
+ }
442
622
  }
443
623
  }
444
- return lines.join("\n");
624
+ return `Servers:
625
+ ${lines.join("\n")}`;
445
626
  }
446
627
 
447
628
  // src/index.ts
448
629
  var mode = detectMode();
449
- var CLIENT_COMMANDS = /* @__PURE__ */ new Set([
630
+ var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
450
631
  "pair",
451
632
  "connect",
452
633
  "list",
@@ -456,37 +637,36 @@ var CLIENT_COMMANDS = /* @__PURE__ */ new Set([
456
637
  "download",
457
638
  "version",
458
639
  "--help",
459
- "-h"
640
+ "-h",
641
+ "server",
642
+ "wrap",
643
+ "unwrap"
460
644
  ]);
461
- if (mode === "client") {
462
- const firstArg = process.argv[2];
463
- if (firstArg && !firstArg.startsWith("-") && !CLIENT_COMMANDS.has(firstArg)) {
464
- const toolName = firstArg;
465
- const toolArgs = process.argv.slice(3);
466
- try {
467
- const { serverUrl, sessionToken } = await connect();
468
- const http = createHttpClient(serverUrl, sessionToken);
469
- const { tool: detail } = await fetchToolDetail(http, toolName);
470
- if (detail.type === "mcp") {
471
- const mcpToolName = toolArgs[0];
472
- if (!mcpToolName) {
473
- const toolNames = (detail.mcpTools ?? []).map((t) => ` ${t.name} \u2014 ${t.description}`);
474
- console.log(`MCP tools for ${toolName}:
645
+ var firstArg = process.argv[2];
646
+ if (firstArg && !firstArg.startsWith("-") && !KNOWN_COMMANDS.has(firstArg)) {
647
+ const toolName = firstArg;
648
+ const toolArgs = process.argv.slice(3);
649
+ try {
650
+ const { serverUrl, sessionToken } = await connect();
651
+ const http = createHttpClient(serverUrl, sessionToken);
652
+ const { tool: detail } = await fetchToolDetail(http, toolName);
653
+ if (detail.type === "mcp") {
654
+ const mcpToolName = toolArgs[0];
655
+ if (!mcpToolName) {
656
+ const toolNames = (detail.mcpTools ?? []).map((t) => ` ${t.name} \u2014 ${t.description}`);
657
+ console.log(`MCP tools for ${toolName}:
475
658
  ${toolNames.join("\n")}`);
476
- } else {
477
- const jsonArg = toolArgs.slice(1).join(" ");
478
- const args = jsonArg ? JSON.parse(jsonArg) : {};
479
- await execMcpTool(http, toolName, mcpToolName, args);
480
- }
481
659
  } else {
482
- await execTool(http, toolName, toolArgs);
660
+ const jsonArg = toolArgs.slice(1).join(" ");
661
+ const args = jsonArg ? JSON.parse(jsonArg) : {};
662
+ await execMcpTool(http, toolName, mcpToolName, args);
483
663
  }
484
- } catch (e) {
485
- console.error(`Error: ${e.message}`);
486
- process.exitCode = 1;
664
+ } else {
665
+ await execTool(http, toolName, toolArgs);
487
666
  }
488
- } else {
489
- runCitty();
667
+ } catch (e) {
668
+ console.error(`Error: ${e.message}`);
669
+ process.exitCode = 1;
490
670
  }
491
671
  } else {
492
672
  runCitty();
@@ -504,8 +684,11 @@ function runCitty() {
504
684
  });
505
685
  const connectCommand = defineCommand({
506
686
  meta: { name: "connect", description: "Establish a session with the server" },
507
- async run() {
508
- const { serverUrl } = await connect();
687
+ args: {
688
+ name: { type: "positional", description: "Server name (optional)", required: false }
689
+ },
690
+ async run({ args }) {
691
+ const { serverUrl } = await connect(args.name);
509
692
  console.log(`Connected to ${serverUrl}`);
510
693
  }
511
694
  });
@@ -534,8 +717,7 @@ function runCitty() {
534
717
  meta: { name: "status", description: "Show connection status" },
535
718
  async run() {
536
719
  const config = await readConfig();
537
- const connected = Boolean(config.server && config.token);
538
- console.log(formatStatus(connected, config.server, config.sessionExpiresAt));
720
+ console.log(formatStatus(config));
539
721
  }
540
722
  });
541
723
  const uploadCommand = defineCommand({
@@ -577,7 +759,7 @@ function runCitty() {
577
759
  dev: { type: "boolean", description: "Development mode (no HTTPS)" }
578
760
  },
579
761
  async run({ args }) {
580
- const { startServer } = await import("./dist-IQVGU6KW.js");
762
+ const { startServer } = await import("./dist-BH5MOHNB.js");
581
763
  const extraArgs = [];
582
764
  if (args.config) extraArgs.push("--config", args.config);
583
765
  if (args.dev) extraArgs.push("--dev");
@@ -588,7 +770,7 @@ function runCitty() {
588
770
  init: defineCommand({
589
771
  meta: { name: "init", description: "Interactive setup wizard" },
590
772
  async run() {
591
- const { runInit } = await import("./dist-IQVGU6KW.js");
773
+ const { runInit } = await import("./dist-BH5MOHNB.js");
592
774
  await runInit();
593
775
  }
594
776
  }),
@@ -598,7 +780,7 @@ function runCitty() {
598
780
  config: { type: "string", alias: "c", description: "Config file path" }
599
781
  },
600
782
  async run({ args }) {
601
- const { generateServerPairingCode } = await import("./dist-IQVGU6KW.js");
783
+ const { generateServerPairingCode } = await import("./dist-BH5MOHNB.js");
602
784
  await generateServerPairingCode(args.config);
603
785
  }
604
786
  }),
@@ -608,14 +790,14 @@ function runCitty() {
608
790
  config: { type: "string", alias: "c", description: "Config file path" }
609
791
  },
610
792
  async run({ args }) {
611
- const { installDaemon } = await import("./dist-IQVGU6KW.js");
793
+ const { installDaemon } = await import("./dist-BH5MOHNB.js");
612
794
  await installDaemon(args.config);
613
795
  }
614
796
  }),
615
797
  uninstall: defineCommand({
616
798
  meta: { name: "uninstall", description: "Remove background service" },
617
799
  async run() {
618
- const { uninstallDaemon } = await import("./dist-IQVGU6KW.js");
800
+ const { uninstallDaemon } = await import("./dist-BH5MOHNB.js");
619
801
  await uninstallDaemon();
620
802
  }
621
803
  }),
@@ -666,21 +848,21 @@ function runCitty() {
666
848
  cli: defineCommand({
667
849
  meta: { name: "cli", description: "Wrap a CLI command" },
668
850
  async run() {
669
- const { wrapCli } = await import("./dist-IQVGU6KW.js");
851
+ const { wrapCli } = await import("./dist-BH5MOHNB.js");
670
852
  await wrapCli();
671
853
  }
672
854
  }),
673
855
  mcp: defineCommand({
674
856
  meta: { name: "mcp", description: "Wrap an MCP server" },
675
857
  async run() {
676
- const { wrapMcp } = await import("./dist-IQVGU6KW.js");
858
+ const { wrapMcp } = await import("./dist-BH5MOHNB.js");
677
859
  await wrapMcp();
678
860
  }
679
861
  }),
680
862
  script: defineCommand({
681
863
  meta: { name: "script", description: "Wrap a script" },
682
864
  async run() {
683
- const { wrapScript } = await import("./dist-IQVGU6KW.js");
865
+ const { wrapScript } = await import("./dist-BH5MOHNB.js");
684
866
  await wrapScript();
685
867
  }
686
868
  }),
@@ -690,7 +872,7 @@ function runCitty() {
690
872
  path: { type: "positional", description: "Path to config file", required: true }
691
873
  },
692
874
  async run({ args }) {
693
- const { wrapImport } = await import("./dist-IQVGU6KW.js");
875
+ const { wrapImport } = await import("./dist-BH5MOHNB.js");
694
876
  await wrapImport(args.path);
695
877
  }
696
878
  })
@@ -702,7 +884,7 @@ function runCitty() {
702
884
  name: { type: "positional", description: "Tool name", required: true }
703
885
  },
704
886
  async run({ args }) {
705
- const { unwrapTool } = await import("./dist-IQVGU6KW.js");
887
+ const { unwrapTool } = await import("./dist-BH5MOHNB.js");
706
888
  await unwrapTool(args.name);
707
889
  }
708
890
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@schuttdev/gigai",
3
- "version": "0.1.0-beta.20",
3
+ "version": "0.1.0-beta.21",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "gigai": "dist/index.js"