@schuttdev/gigai 0.2.7 → 0.2.9

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.
@@ -239,7 +239,7 @@ var ScriptToolConfigSchema = z.object({
239
239
  var BuiltinToolConfigSchema = z.object({
240
240
  type: z.literal("builtin"),
241
241
  name: z.string(),
242
- builtin: z.enum(["filesystem", "shell"]),
242
+ builtin: z.enum(["filesystem", "shell", "read", "write", "edit", "glob", "grep", "bash"]),
243
243
  description: z.string(),
244
244
  config: z.record(z.unknown()).optional()
245
245
  });
@@ -280,24 +280,29 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
280
280
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
281
281
  import { readFileSync } from "fs";
282
282
  import { resolve } from "path";
283
- import { readFile as fsReadFile, readdir } from "fs/promises";
283
+ import {
284
+ readFile as fsReadFile,
285
+ writeFile as fsWriteFile,
286
+ readdir
287
+ } from "fs/promises";
284
288
  import { resolve as resolve2, relative, join } from "path";
285
289
  import { realpath } from "fs/promises";
286
290
  import { spawn as spawn2 } from "child_process";
291
+ import { spawn as spawn3 } from "child_process";
287
292
  import { writeFile, readFile, unlink, mkdir } from "fs/promises";
288
293
  import { join as join2 } from "path";
289
294
  import { tmpdir } from "os";
290
295
  import { nanoid as nanoid3 } from "nanoid";
291
- import { execFile, spawn as spawn3 } from "child_process";
296
+ import { execFile, spawn as spawn4 } from "child_process";
292
297
  import { promisify } from "util";
293
298
  import { readFile as readFile2 } from "fs/promises";
294
299
  import { resolve as resolve3 } from "path";
295
- import { spawn as spawn4 } from "child_process";
296
300
  import { spawn as spawn5 } from "child_process";
301
+ import { spawn as spawn6 } from "child_process";
297
302
  import { input, select, checkbox, confirm } from "@inquirer/prompts";
298
303
  import { writeFile as writeFile2 } from "fs/promises";
299
304
  import { resolve as resolve4 } from "path";
300
- import { execFile as execFile2, spawn as spawn6 } from "child_process";
305
+ import { execFile as execFile2, spawn as spawn7 } from "child_process";
301
306
  import { promisify as promisify2 } from "util";
302
307
  import { input as input2 } from "@inquirer/prompts";
303
308
  import { readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
@@ -841,6 +846,26 @@ async function toolRoutes(server) {
841
846
  server.get("/tools", async () => {
842
847
  return { tools: server.registry.list() };
843
848
  });
849
+ server.get("/tools/search", async (request) => {
850
+ const query = request.query.q?.toLowerCase().trim();
851
+ if (!query) {
852
+ return { tools: server.registry.list() };
853
+ }
854
+ const all = server.registry.list();
855
+ const keywords = query.split(/\s+/);
856
+ const scored = all.map((tool) => {
857
+ const text = `${tool.name} ${tool.description}`.toLowerCase();
858
+ let score = 0;
859
+ for (const kw of keywords) {
860
+ if (tool.name.toLowerCase() === kw) score += 10;
861
+ else if (tool.name.toLowerCase().includes(kw)) score += 5;
862
+ if (tool.description.toLowerCase().includes(kw)) score += 2;
863
+ }
864
+ return { tool, score };
865
+ });
866
+ const matches = scored.filter((s) => s.score > 0).sort((a, b) => b.score - a.score).slice(0, 10).map((s) => s.tool);
867
+ return { tools: matches };
868
+ });
844
869
  server.get("/tools/:name", async (request) => {
845
870
  const { name } = request.params;
846
871
  const detail = server.registry.getDetail(name);
@@ -864,6 +889,8 @@ async function toolRoutes(server) {
864
889
  return { tools: mcpTools };
865
890
  });
866
891
  }
892
+ var MAX_OUTPUT_SIZE2 = 10 * 1024 * 1024;
893
+ var MAX_READ_SIZE = 2 * 1024 * 1024;
867
894
  async function validatePath(targetPath, allowedPaths) {
868
895
  const resolved = resolve2(targetPath);
869
896
  let real;
@@ -921,6 +948,250 @@ async function searchFilesSafe(path, pattern, allowedPaths) {
921
948
  await walk(safePath);
922
949
  return results;
923
950
  }
951
+ async function readBuiltin(args, allowedPaths) {
952
+ const filePath = args[0];
953
+ if (!filePath) {
954
+ throw new GigaiError(ErrorCode.VALIDATION_ERROR, "Usage: read <file> [offset] [limit]");
955
+ }
956
+ const safePath = await validatePath(filePath, allowedPaths);
957
+ const content = await fsReadFile(safePath, "utf8");
958
+ if (content.length > MAX_READ_SIZE) {
959
+ throw new GigaiError(
960
+ ErrorCode.VALIDATION_ERROR,
961
+ `File too large (${(content.length / 1024 / 1024).toFixed(1)}MB). Max: ${MAX_READ_SIZE / 1024 / 1024}MB. Use offset/limit.`
962
+ );
963
+ }
964
+ const offset = args[1] ? parseInt(args[1], 10) : 0;
965
+ const limit = args[2] ? parseInt(args[2], 10) : 0;
966
+ if (offset || limit) {
967
+ const lines = content.split("\n");
968
+ const start = Math.max(0, offset);
969
+ const end = limit ? start + limit : lines.length;
970
+ const sliced = lines.slice(start, end);
971
+ return { stdout: sliced.join("\n"), stderr: "", exitCode: 0 };
972
+ }
973
+ return { stdout: content, stderr: "", exitCode: 0 };
974
+ }
975
+ async function writeBuiltin(args, allowedPaths) {
976
+ const filePath = args[0];
977
+ const content = args[1];
978
+ if (!filePath || content === void 0) {
979
+ throw new GigaiError(ErrorCode.VALIDATION_ERROR, "Usage: write <file> <content>");
980
+ }
981
+ const safePath = await validatePath(filePath, allowedPaths);
982
+ const { mkdir: mkdir2 } = await import("fs/promises");
983
+ const { dirname } = await import("path");
984
+ await mkdir2(dirname(safePath), { recursive: true });
985
+ await fsWriteFile(safePath, content, "utf8");
986
+ return { stdout: `Written: ${safePath}`, stderr: "", exitCode: 0 };
987
+ }
988
+ async function editBuiltin(args, allowedPaths) {
989
+ const filePath = args[0];
990
+ const oldStr = args[1];
991
+ const newStr = args[2];
992
+ const replaceAll = args.includes("--all");
993
+ if (!filePath || oldStr === void 0 || newStr === void 0) {
994
+ throw new GigaiError(ErrorCode.VALIDATION_ERROR, "Usage: edit <file> <old_string> <new_string> [--all]");
995
+ }
996
+ const safePath = await validatePath(filePath, allowedPaths);
997
+ const content = await fsReadFile(safePath, "utf8");
998
+ if (!content.includes(oldStr)) {
999
+ throw new GigaiError(ErrorCode.VALIDATION_ERROR, "old_string not found in file");
1000
+ }
1001
+ if (!replaceAll) {
1002
+ const firstIdx = content.indexOf(oldStr);
1003
+ const secondIdx = content.indexOf(oldStr, firstIdx + 1);
1004
+ if (secondIdx !== -1) {
1005
+ throw new GigaiError(
1006
+ ErrorCode.VALIDATION_ERROR,
1007
+ "old_string matches multiple locations. Use --all to replace all, or provide more context to make it unique."
1008
+ );
1009
+ }
1010
+ }
1011
+ const updated = replaceAll ? content.split(oldStr).join(newStr) : content.replace(oldStr, newStr);
1012
+ await fsWriteFile(safePath, updated, "utf8");
1013
+ const count = replaceAll ? content.split(oldStr).length - 1 : 1;
1014
+ return { stdout: `Replaced ${count} occurrence(s) in ${safePath}`, stderr: "", exitCode: 0 };
1015
+ }
1016
+ async function globBuiltin(args, allowedPaths) {
1017
+ const pattern = args[0];
1018
+ if (!pattern) {
1019
+ throw new GigaiError(ErrorCode.VALIDATION_ERROR, "Usage: glob <pattern> [path]");
1020
+ }
1021
+ const searchPath = args[1] ?? ".";
1022
+ const safePath = await validatePath(searchPath, allowedPaths);
1023
+ const results = [];
1024
+ const globRegex = globToRegex(pattern);
1025
+ async function walk(dir) {
1026
+ let entries;
1027
+ try {
1028
+ entries = await readdir(dir, { withFileTypes: true });
1029
+ } catch {
1030
+ return;
1031
+ }
1032
+ for (const entry of entries) {
1033
+ const fullPath = join(dir, entry.name);
1034
+ const relPath = relative(safePath, fullPath);
1035
+ if (globRegex.test(relPath) || globRegex.test(entry.name)) {
1036
+ results.push(relPath);
1037
+ }
1038
+ if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") {
1039
+ await walk(fullPath);
1040
+ }
1041
+ if (results.length >= 1e3) return;
1042
+ }
1043
+ }
1044
+ await walk(safePath);
1045
+ return { stdout: results.join("\n"), stderr: "", exitCode: 0 };
1046
+ }
1047
+ async function grepBuiltin(args, allowedPaths) {
1048
+ if (args.length === 0) {
1049
+ throw new GigaiError(ErrorCode.VALIDATION_ERROR, "Usage: grep <pattern> [path] [--glob <filter>] [-i] [-n] [-C <num>]");
1050
+ }
1051
+ const positional = [];
1052
+ const flags = [];
1053
+ let i = 0;
1054
+ while (i < args.length) {
1055
+ const arg = args[i];
1056
+ if (arg === "--glob" && args[i + 1]) {
1057
+ flags.push("--glob", args[i + 1]);
1058
+ i += 2;
1059
+ } else if (arg === "--type" && args[i + 1]) {
1060
+ flags.push("--type", args[i + 1]);
1061
+ i += 2;
1062
+ } else if (arg === "-C" && args[i + 1]) {
1063
+ flags.push("-C", args[i + 1]);
1064
+ i += 2;
1065
+ } else if (arg === "-i" || arg === "-n" || arg === "-l") {
1066
+ flags.push(arg);
1067
+ i++;
1068
+ } else {
1069
+ positional.push(arg);
1070
+ i++;
1071
+ }
1072
+ }
1073
+ const pattern = positional[0];
1074
+ if (!pattern) {
1075
+ throw new GigaiError(ErrorCode.VALIDATION_ERROR, "No search pattern provided");
1076
+ }
1077
+ const searchPath = positional[1] ?? ".";
1078
+ const safePath = await validatePath(searchPath, allowedPaths);
1079
+ try {
1080
+ return await spawnGrep("rg", [pattern, safePath, "-n", ...flags]);
1081
+ } catch {
1082
+ try {
1083
+ return await spawnGrep("grep", ["-rn", ...flags, pattern, safePath]);
1084
+ } catch {
1085
+ return jsGrep(pattern, safePath);
1086
+ }
1087
+ }
1088
+ }
1089
+ function spawnGrep(cmd, args) {
1090
+ return new Promise((resolve7, reject) => {
1091
+ const child = spawn2(cmd, args, { shell: false, stdio: ["ignore", "pipe", "pipe"] });
1092
+ const stdoutChunks = [];
1093
+ const stderrChunks = [];
1094
+ let totalSize = 0;
1095
+ child.stdout.on("data", (chunk) => {
1096
+ totalSize += chunk.length;
1097
+ if (totalSize <= MAX_OUTPUT_SIZE2) stdoutChunks.push(chunk);
1098
+ else child.kill("SIGTERM");
1099
+ });
1100
+ child.stderr.on("data", (chunk) => {
1101
+ totalSize += chunk.length;
1102
+ if (totalSize <= MAX_OUTPUT_SIZE2) stderrChunks.push(chunk);
1103
+ });
1104
+ child.on("error", () => reject(new Error(`${cmd} not available`)));
1105
+ child.on("close", (exitCode) => {
1106
+ resolve7({
1107
+ stdout: Buffer.concat(stdoutChunks).toString("utf8"),
1108
+ stderr: Buffer.concat(stderrChunks).toString("utf8"),
1109
+ exitCode: exitCode ?? 1
1110
+ });
1111
+ });
1112
+ });
1113
+ }
1114
+ async function jsGrep(pattern, searchPath) {
1115
+ let regex;
1116
+ try {
1117
+ regex = new RegExp(pattern);
1118
+ } catch {
1119
+ throw new GigaiError(ErrorCode.VALIDATION_ERROR, `Invalid pattern: ${pattern}`);
1120
+ }
1121
+ const results = [];
1122
+ async function walk(dir) {
1123
+ let entries;
1124
+ try {
1125
+ entries = await readdir(dir, { withFileTypes: true });
1126
+ } catch {
1127
+ return;
1128
+ }
1129
+ for (const entry of entries) {
1130
+ if (results.length >= 500) return;
1131
+ const fullPath = join(dir, entry.name);
1132
+ if (entry.isDirectory()) {
1133
+ if (!entry.name.startsWith(".") && entry.name !== "node_modules") {
1134
+ await walk(fullPath);
1135
+ }
1136
+ } else {
1137
+ try {
1138
+ const content = await fsReadFile(fullPath, "utf8");
1139
+ const lines = content.split("\n");
1140
+ for (let i = 0; i < lines.length; i++) {
1141
+ if (regex.test(lines[i])) {
1142
+ results.push(`${relative(searchPath, fullPath)}:${i + 1}:${lines[i]}`);
1143
+ if (results.length >= 500) return;
1144
+ }
1145
+ }
1146
+ } catch {
1147
+ }
1148
+ }
1149
+ }
1150
+ }
1151
+ await walk(searchPath);
1152
+ return {
1153
+ stdout: results.join("\n"),
1154
+ stderr: results.length >= 500 ? "Results truncated at 500 matches" : "",
1155
+ exitCode: results.length > 0 ? 0 : 1
1156
+ };
1157
+ }
1158
+ function globToRegex(pattern) {
1159
+ let regex = "";
1160
+ let i = 0;
1161
+ while (i < pattern.length) {
1162
+ const c = pattern[i];
1163
+ if (c === "*") {
1164
+ if (pattern[i + 1] === "*") {
1165
+ regex += ".*";
1166
+ i += 2;
1167
+ if (pattern[i] === "/") i++;
1168
+ } else {
1169
+ regex += "[^/]*";
1170
+ i++;
1171
+ }
1172
+ } else if (c === "?") {
1173
+ regex += "[^/]";
1174
+ i++;
1175
+ } else if (c === "{") {
1176
+ const end = pattern.indexOf("}", i);
1177
+ if (end !== -1) {
1178
+ const options = pattern.slice(i + 1, end).split(",");
1179
+ regex += `(?:${options.map(escapeRegex).join("|")})`;
1180
+ i = end + 1;
1181
+ } else {
1182
+ regex += escapeRegex(c);
1183
+ i++;
1184
+ }
1185
+ } else {
1186
+ regex += escapeRegex(c);
1187
+ i++;
1188
+ }
1189
+ }
1190
+ return new RegExp(regex);
1191
+ }
1192
+ function escapeRegex(s) {
1193
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1194
+ }
924
1195
  var SHELL_INTERPRETERS = /* @__PURE__ */ new Set([
925
1196
  "sh",
926
1197
  "bash",
@@ -936,7 +1207,7 @@ var SHELL_INTERPRETERS = /* @__PURE__ */ new Set([
936
1207
  "strace",
937
1208
  "ltrace"
938
1209
  ]);
939
- var MAX_OUTPUT_SIZE2 = 10 * 1024 * 1024;
1210
+ var MAX_OUTPUT_SIZE3 = 10 * 1024 * 1024;
940
1211
  async function execCommandSafe(command, args, config) {
941
1212
  if (!config.allowlist.includes(command)) {
942
1213
  throw new GigaiError(
@@ -959,7 +1230,7 @@ async function execCommandSafe(command, args, config) {
959
1230
  }
960
1231
  }
961
1232
  return new Promise((resolve7, reject) => {
962
- const child = spawn2(command, args, {
1233
+ const child = spawn3(command, args, {
963
1234
  shell: false,
964
1235
  stdio: ["ignore", "pipe", "pipe"]
965
1236
  });
@@ -968,12 +1239,12 @@ async function execCommandSafe(command, args, config) {
968
1239
  let totalSize = 0;
969
1240
  child.stdout.on("data", (chunk) => {
970
1241
  totalSize += chunk.length;
971
- if (totalSize <= MAX_OUTPUT_SIZE2) stdoutChunks.push(chunk);
1242
+ if (totalSize <= MAX_OUTPUT_SIZE3) stdoutChunks.push(chunk);
972
1243
  else child.kill("SIGTERM");
973
1244
  });
974
1245
  child.stderr.on("data", (chunk) => {
975
1246
  totalSize += chunk.length;
976
- if (totalSize <= MAX_OUTPUT_SIZE2) stderrChunks.push(chunk);
1247
+ if (totalSize <= MAX_OUTPUT_SIZE3) stderrChunks.push(chunk);
977
1248
  else child.kill("SIGTERM");
978
1249
  });
979
1250
  child.on("error", (err) => {
@@ -1047,6 +1318,7 @@ async function execRoutes(server) {
1047
1318
  async function handleBuiltin(config, args) {
1048
1319
  const builtinConfig = config.config ?? {};
1049
1320
  switch (config.builtin) {
1321
+ // Legacy combined filesystem tool
1050
1322
  case "filesystem": {
1051
1323
  const allowedPaths = builtinConfig.allowedPaths ?? ["."];
1052
1324
  const subcommand = args[0];
@@ -1062,6 +1334,7 @@ async function handleBuiltin(config, args) {
1062
1334
  throw new GigaiError(ErrorCode.VALIDATION_ERROR, `Unknown filesystem subcommand: ${subcommand}. Use: read, list, search`);
1063
1335
  }
1064
1336
  }
1337
+ // Legacy shell tool
1065
1338
  case "shell": {
1066
1339
  const allowlist = builtinConfig.allowlist ?? [];
1067
1340
  const allowSudo = builtinConfig.allowSudo ?? false;
@@ -1072,6 +1345,37 @@ async function handleBuiltin(config, args) {
1072
1345
  const result = await execCommandSafe(command, args.slice(1), { allowlist, allowSudo });
1073
1346
  return { ...result, durationMs: 0 };
1074
1347
  }
1348
+ // --- New builtins ---
1349
+ case "read": {
1350
+ const allowedPaths = builtinConfig.allowedPaths ?? ["."];
1351
+ return { ...await readBuiltin(args, allowedPaths), durationMs: 0 };
1352
+ }
1353
+ case "write": {
1354
+ const allowedPaths = builtinConfig.allowedPaths ?? ["."];
1355
+ return { ...await writeBuiltin(args, allowedPaths), durationMs: 0 };
1356
+ }
1357
+ case "edit": {
1358
+ const allowedPaths = builtinConfig.allowedPaths ?? ["."];
1359
+ return { ...await editBuiltin(args, allowedPaths), durationMs: 0 };
1360
+ }
1361
+ case "glob": {
1362
+ const allowedPaths = builtinConfig.allowedPaths ?? ["."];
1363
+ return { ...await globBuiltin(args, allowedPaths), durationMs: 0 };
1364
+ }
1365
+ case "grep": {
1366
+ const allowedPaths = builtinConfig.allowedPaths ?? ["."];
1367
+ return { ...await grepBuiltin(args, allowedPaths), durationMs: 0 };
1368
+ }
1369
+ case "bash": {
1370
+ const allowlist = builtinConfig.allowlist ?? [];
1371
+ const allowSudo = builtinConfig.allowSudo ?? false;
1372
+ const command = args[0];
1373
+ if (!command) {
1374
+ throw new GigaiError(ErrorCode.VALIDATION_ERROR, "No command specified");
1375
+ }
1376
+ const result = await execCommandSafe(command, args.slice(1), { allowlist, allowSudo });
1377
+ return { ...result, durationMs: 0 };
1378
+ }
1075
1379
  default:
1076
1380
  throw new GigaiError(ErrorCode.VALIDATION_ERROR, `Unknown builtin: ${config.builtin}`);
1077
1381
  }
@@ -1147,7 +1451,7 @@ async function adminRoutes(server) {
1147
1451
  }
1148
1452
  setTimeout(async () => {
1149
1453
  server.log.info("Restarting server after update...");
1150
- const args = ["server", "start"];
1454
+ const args = ["start"];
1151
1455
  const configIdx = process.argv.indexOf("--config");
1152
1456
  if (configIdx !== -1 && process.argv[configIdx + 1]) {
1153
1457
  args.push("--config", process.argv[configIdx + 1]);
@@ -1160,7 +1464,7 @@ async function adminRoutes(server) {
1160
1464
  args.push("--dev");
1161
1465
  }
1162
1466
  await server.close();
1163
- const child = spawn3("gigai", args, {
1467
+ const child = spawn4("gigai", args, {
1164
1468
  detached: true,
1165
1469
  stdio: "ignore",
1166
1470
  cwd: process.cwd()
@@ -1228,7 +1532,7 @@ async function loadConfig(path) {
1228
1532
  }
1229
1533
  function runCommand(command, args) {
1230
1534
  return new Promise((resolve7, reject) => {
1231
- const child = spawn4(command, args, { shell: false, stdio: ["ignore", "pipe", "pipe"] });
1535
+ const child = spawn5(command, args, { shell: false, stdio: ["ignore", "pipe", "pipe"] });
1232
1536
  const chunks = [];
1233
1537
  child.stdout.on("data", (chunk) => chunks.push(chunk));
1234
1538
  child.on("error", reject);
@@ -1269,7 +1573,7 @@ async function disableFunnel(port) {
1269
1573
  await runCommand("tailscale", ["funnel", "--bg", "off", `${port}`]);
1270
1574
  }
1271
1575
  function runTunnel(tunnelName, localPort) {
1272
- const child = spawn5("cloudflared", [
1576
+ const child = spawn6("cloudflared", [
1273
1577
  "tunnel",
1274
1578
  "--url",
1275
1579
  `http://localhost:${localPort}`,
@@ -1472,7 +1776,7 @@ async function runInit() {
1472
1776
  serverUrl = await ensureTailscaleFunnel(port);
1473
1777
  } catch (e) {
1474
1778
  console.error(` ${e.message}`);
1475
- console.log(" You can enable Funnel later and run 'gigai server start' manually.\n");
1779
+ console.log(" You can enable Funnel later and run 'gigai start' manually.\n");
1476
1780
  }
1477
1781
  } else if (httpsProvider === "cloudflare" && httpsConfig && "domain" in httpsConfig && httpsConfig.domain) {
1478
1782
  serverUrl = `https://${httpsConfig.domain}`;
@@ -1485,9 +1789,9 @@ async function runInit() {
1485
1789
  });
1486
1790
  }
1487
1791
  console.log("\n Starting server...");
1488
- const serverArgs = ["server", "start", "--config", configPath];
1792
+ const serverArgs = ["start", "--config", configPath];
1489
1793
  if (!httpsConfig) serverArgs.push("--dev");
1490
- const child = spawn6("gigai", serverArgs, {
1794
+ const child = spawn7("gigai", serverArgs, {
1491
1795
  detached: true,
1492
1796
  stdio: "ignore",
1493
1797
  cwd: resolve4(".")
@@ -1518,7 +1822,7 @@ async function runInit() {
1518
1822
  }
1519
1823
  if (!code) {
1520
1824
  console.log("\n Server is starting but not ready yet.");
1521
- console.log(" Run 'gigai server pair' once it's up to get a pairing code.\n");
1825
+ console.log(" Run 'gigai pair' once it's up to get a pairing code.\n");
1522
1826
  return;
1523
1827
  }
1524
1828
  console.log(`
@@ -1536,7 +1840,7 @@ async function runInit() {
1536
1840
  console.log(` \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`);
1537
1841
  console.log(`
1538
1842
  Pairing code expires in ${config.auth.pairingTtlSeconds / 60} minutes.`);
1539
- console.log(` Run 'gigai server pair' to generate a new one.
1843
+ console.log(` Run 'gigai pair' to generate a new one.
1540
1844
  `);
1541
1845
  }
1542
1846
  async function loadConfigFile(path) {
@@ -1663,7 +1967,7 @@ Pairing code: ${data.code}`);
1663
1967
  console.log(`Expires in ${data.expiresIn / 60} minutes.`);
1664
1968
  } catch (e) {
1665
1969
  if (e.message.includes("fetch failed") || e.message.includes("ECONNREFUSED")) {
1666
- console.error("Server is not running. Start it with: gigai server start");
1970
+ console.error("Server is not running. Start it with: gigai start");
1667
1971
  } else {
1668
1972
  console.error(`Error: ${e.message}`);
1669
1973
  }
@@ -1696,7 +2000,6 @@ function getLaunchdPlist(configPath) {
1696
2000
  <array>
1697
2001
  <string>${nodeBin}</string>
1698
2002
  <string>${bin}</string>
1699
- <string>server</string>
1700
2003
  <string>start</string>
1701
2004
  <string>--config</string>
1702
2005
  <string>${configPath}</string>
@@ -1723,7 +2026,7 @@ After=network.target
1723
2026
 
1724
2027
  [Service]
1725
2028
  Type=simple
1726
- ExecStart=${bin} server start --config ${configPath}
2029
+ ExecStart=${bin} start --config ${configPath}
1727
2030
  Restart=always
1728
2031
  RestartSec=5
1729
2032
  WorkingDirectory=${homedir()}
@@ -1766,7 +2069,7 @@ async function installDaemon(configPath) {
1766
2069
  console.log(` Remove: systemctl --user disable gigai`);
1767
2070
  } else {
1768
2071
  console.log(" Persistent daemon not supported on this platform.");
1769
- console.log(" Run 'gigai server start' manually.");
2072
+ console.log(" Run 'gigai start' manually.");
1770
2073
  }
1771
2074
  }
1772
2075
  async function uninstallDaemon() {
@@ -1804,7 +2107,7 @@ async function stopServer() {
1804
2107
  const { execFileSync } = await import("child_process");
1805
2108
  let pids = [];
1806
2109
  try {
1807
- const out = execFileSync("pgrep", ["-f", "gigai server start"], { encoding: "utf8" });
2110
+ const out = execFileSync("pgrep", ["-f", "gigai start"], { encoding: "utf8" });
1808
2111
  pids = out.trim().split("\n").map(Number).filter((pid) => pid && pid !== process.pid);
1809
2112
  } catch {
1810
2113
  }
package/dist/index.js CHANGED
@@ -4,109 +4,104 @@
4
4
  import { defineCommand, runMain } from "citty";
5
5
 
6
6
  // src/version.ts
7
- var VERSION = "0.2.7";
7
+ var VERSION = "0.2.9";
8
8
 
9
9
  // src/index.ts
10
10
  async function requireServer() {
11
11
  try {
12
- return await import("./dist-MGLETZTT.js");
12
+ return await import("./dist-YWMCMOTP.js");
13
13
  } catch {
14
14
  console.error("Server dependencies not installed.");
15
15
  console.error("Run: npm install -g @schuttdev/gigai");
16
16
  process.exit(1);
17
17
  }
18
18
  }
19
- var serverCommand = defineCommand({
20
- meta: { name: "server", description: "Server management commands" },
21
- subCommands: {
22
- start: defineCommand({
23
- meta: { name: "start", description: "Start the gigai server" },
24
- args: {
25
- config: { type: "string", alias: "c", description: "Config file path" },
26
- dev: { type: "boolean", description: "Development mode (no HTTPS)" }
27
- },
28
- async run({ args }) {
29
- const { startServer } = await requireServer();
30
- const extraArgs = [];
31
- if (args.config) extraArgs.push("--config", args.config);
32
- if (args.dev) extraArgs.push("--dev");
33
- process.argv.push(...extraArgs);
34
- await startServer();
35
- }
36
- }),
37
- init: defineCommand({
38
- meta: { name: "init", description: "Interactive setup wizard" },
39
- async run() {
40
- const { runInit } = await requireServer();
41
- await runInit();
42
- }
43
- }),
44
- pair: defineCommand({
45
- meta: { name: "pair", description: "Generate a pairing code" },
46
- args: {
47
- config: { type: "string", alias: "c", description: "Config file path" }
48
- },
49
- async run({ args }) {
50
- const { generateServerPairingCode } = await requireServer();
51
- await generateServerPairingCode(args.config);
52
- }
53
- }),
54
- install: defineCommand({
55
- meta: { name: "install", description: "Install as persistent background service" },
56
- args: {
57
- config: { type: "string", alias: "c", description: "Config file path" }
58
- },
59
- async run({ args }) {
60
- const { installDaemon } = await requireServer();
61
- await installDaemon(args.config);
62
- }
63
- }),
64
- uninstall: defineCommand({
65
- meta: { name: "uninstall", description: "Remove background service" },
66
- async run() {
67
- const { uninstallDaemon } = await requireServer();
68
- await uninstallDaemon();
69
- }
70
- }),
71
- stop: defineCommand({
72
- meta: { name: "stop", description: "Stop the running gigai server" },
73
- async run() {
74
- const { execFileSync } = await import("child_process");
75
- let pids = [];
76
- try {
77
- const out = execFileSync("pgrep", ["-f", "gigai server start"], { encoding: "utf8" });
78
- pids = out.trim().split("\n").map(Number).filter((pid) => pid && pid !== process.pid);
79
- } catch {
80
- }
81
- if (pids.length === 0) {
82
- console.log("No running gigai server found.");
83
- return;
84
- }
85
- for (const pid of pids) {
86
- try {
87
- process.kill(pid, "SIGTERM");
88
- console.log(`Stopped gigai server (PID ${pid})`);
89
- } catch (e) {
90
- console.error(`Failed to stop PID ${pid}: ${e.message}`);
91
- }
92
- }
93
- }
94
- }),
95
- status: defineCommand({
96
- meta: { name: "status", description: "Show server status" },
97
- async run() {
98
- console.log("Server status: checking...");
99
- try {
100
- const res = await fetch("http://localhost:7443/health");
101
- const data = await res.json();
102
- console.log(`Status: ${data.status}`);
103
- console.log(`Version: ${data.version}`);
104
- console.log(`Uptime: ${Math.floor(data.uptime / 1e3)}s`);
105
- } catch {
106
- console.log("Server is not running.");
107
- }
19
+ var initCommand = defineCommand({
20
+ meta: { name: "init", description: "Interactive setup wizard" },
21
+ async run() {
22
+ const { runInit } = await requireServer();
23
+ await runInit();
24
+ }
25
+ });
26
+ var startCommand = defineCommand({
27
+ meta: { name: "start", description: "Start the gigai server" },
28
+ args: {
29
+ config: { type: "string", alias: "c", description: "Config file path" },
30
+ dev: { type: "boolean", description: "Development mode (no HTTPS)" }
31
+ },
32
+ async run({ args }) {
33
+ const { startServer } = await requireServer();
34
+ const extraArgs = [];
35
+ if (args.config) extraArgs.push("--config", args.config);
36
+ if (args.dev) extraArgs.push("--dev");
37
+ process.argv.push(...extraArgs);
38
+ await startServer();
39
+ }
40
+ });
41
+ var stopCommand = defineCommand({
42
+ meta: { name: "stop", description: "Stop the running gigai server" },
43
+ async run() {
44
+ const { execFileSync } = await import("child_process");
45
+ let pids = [];
46
+ try {
47
+ const out = execFileSync("pgrep", ["-f", "gigai start"], { encoding: "utf8" });
48
+ pids = out.trim().split("\n").map(Number).filter((pid) => pid && pid !== process.pid);
49
+ } catch {
50
+ }
51
+ if (pids.length === 0) {
52
+ console.log("No running gigai server found.");
53
+ return;
54
+ }
55
+ for (const pid of pids) {
56
+ try {
57
+ process.kill(pid, "SIGTERM");
58
+ console.log(`Stopped gigai server (PID ${pid})`);
59
+ } catch (e) {
60
+ console.error(`Failed to stop PID ${pid}: ${e.message}`);
108
61
  }
109
- })
62
+ }
63
+ }
64
+ });
65
+ var statusCommand = defineCommand({
66
+ meta: { name: "status", description: "Show server status" },
67
+ async run() {
68
+ console.log("Server status: checking...");
69
+ try {
70
+ const res = await fetch("http://localhost:7443/health");
71
+ const data = await res.json();
72
+ console.log(`Status: ${data.status}`);
73
+ console.log(`Version: ${data.version}`);
74
+ console.log(`Uptime: ${Math.floor(data.uptime / 1e3)}s`);
75
+ } catch {
76
+ console.log("Server is not running.");
77
+ }
78
+ }
79
+ });
80
+ var pairCommand = defineCommand({
81
+ meta: { name: "pair", description: "Generate a pairing code" },
82
+ args: {
83
+ config: { type: "string", alias: "c", description: "Config file path" }
84
+ },
85
+ async run({ args }) {
86
+ const { generateServerPairingCode } = await requireServer();
87
+ await generateServerPairingCode(args.config);
88
+ }
89
+ });
90
+ var installCommand = defineCommand({
91
+ meta: { name: "install", description: "Install as persistent background service" },
92
+ args: {
93
+ config: { type: "string", alias: "c", description: "Config file path" }
94
+ },
95
+ async run({ args }) {
96
+ const { installDaemon } = await requireServer();
97
+ await installDaemon(args.config);
98
+ }
99
+ });
100
+ var uninstallCommand = defineCommand({
101
+ meta: { name: "uninstall", description: "Remove background service" },
102
+ async run() {
103
+ const { uninstallDaemon } = await requireServer();
104
+ await uninstallDaemon();
110
105
  }
111
106
  });
112
107
  var wrapCommand = defineCommand({
@@ -165,10 +160,16 @@ var main = defineCommand({
165
160
  meta: {
166
161
  name: "gigai",
167
162
  version: VERSION,
168
- description: "gigai server \u2014 bridge CLI tools to Claude"
163
+ description: "gigai \u2014 bridge CLI tools to Claude"
169
164
  },
170
165
  subCommands: {
171
- server: serverCommand,
166
+ init: initCommand,
167
+ start: startCommand,
168
+ stop: stopCommand,
169
+ status: statusCommand,
170
+ pair: pairCommand,
171
+ install: installCommand,
172
+ uninstall: uninstallCommand,
172
173
  wrap: wrapCommand,
173
174
  unwrap: unwrapCommand,
174
175
  version: versionCommand
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@schuttdev/gigai",
3
- "version": "0.2.7",
3
+ "version": "0.2.9",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "gigai": "dist/index.js"