@schuttdev/kon 0.2.6
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/dist/index.js +908 -0
- package/package.json +29 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,908 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { defineCommand, runMain } from "citty";
|
|
5
|
+
|
|
6
|
+
// ../cli/src/config.ts
|
|
7
|
+
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
8
|
+
import { join } from "path";
|
|
9
|
+
import { homedir } from "os";
|
|
10
|
+
function getConfigDir() {
|
|
11
|
+
return process.env.GIGAI_CONFIG_DIR ?? join(homedir(), ".gigai");
|
|
12
|
+
}
|
|
13
|
+
function getConfigPath() {
|
|
14
|
+
return join(getConfigDir(), "config.json");
|
|
15
|
+
}
|
|
16
|
+
async function readConfig() {
|
|
17
|
+
try {
|
|
18
|
+
const raw = await readFile(getConfigPath(), "utf8");
|
|
19
|
+
const parsed = JSON.parse(raw);
|
|
20
|
+
if (parsed.server && parsed.token && !parsed.servers) {
|
|
21
|
+
const name = deriveServerName(parsed.server);
|
|
22
|
+
const migrated = {
|
|
23
|
+
activeServer: name,
|
|
24
|
+
servers: {
|
|
25
|
+
[name]: {
|
|
26
|
+
server: parsed.server,
|
|
27
|
+
token: parsed.token,
|
|
28
|
+
sessionToken: parsed.sessionToken,
|
|
29
|
+
sessionExpiresAt: parsed.sessionExpiresAt
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
await writeConfig(migrated);
|
|
34
|
+
return migrated;
|
|
35
|
+
}
|
|
36
|
+
return { activeServer: parsed.activeServer, servers: parsed.servers ?? {} };
|
|
37
|
+
} catch {
|
|
38
|
+
return { servers: {} };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
async function writeConfig(config) {
|
|
42
|
+
const dir = getConfigDir();
|
|
43
|
+
await mkdir(dir, { recursive: true });
|
|
44
|
+
await writeFile(getConfigPath(), JSON.stringify(config, null, 2) + "\n", { mode: 384 });
|
|
45
|
+
}
|
|
46
|
+
function getActiveEntry(config) {
|
|
47
|
+
if (!config.activeServer || !config.servers[config.activeServer]) return void 0;
|
|
48
|
+
return { name: config.activeServer, entry: config.servers[config.activeServer] };
|
|
49
|
+
}
|
|
50
|
+
async function addServer(name, server, token) {
|
|
51
|
+
const config = await readConfig();
|
|
52
|
+
for (const [existingName, entry] of Object.entries(config.servers)) {
|
|
53
|
+
if (normalizeUrl(entry.server) === normalizeUrl(server)) {
|
|
54
|
+
config.servers[existingName] = { server, token };
|
|
55
|
+
config.activeServer = existingName;
|
|
56
|
+
await writeConfig(config);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
config.servers[name] = { server, token };
|
|
61
|
+
config.activeServer = name;
|
|
62
|
+
await writeConfig(config);
|
|
63
|
+
}
|
|
64
|
+
async function updateServerSession(name, sessionToken, sessionExpiresAt) {
|
|
65
|
+
const config = await readConfig();
|
|
66
|
+
const entry = config.servers[name];
|
|
67
|
+
if (!entry) return;
|
|
68
|
+
entry.sessionToken = sessionToken;
|
|
69
|
+
entry.sessionExpiresAt = sessionExpiresAt;
|
|
70
|
+
await writeConfig(config);
|
|
71
|
+
}
|
|
72
|
+
function deriveServerName(url) {
|
|
73
|
+
try {
|
|
74
|
+
const hostname = new URL(url).hostname;
|
|
75
|
+
return hostname.split(".")[0];
|
|
76
|
+
} catch {
|
|
77
|
+
return "default";
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function normalizeUrl(url) {
|
|
81
|
+
try {
|
|
82
|
+
const u = new URL(url);
|
|
83
|
+
return u.hostname.toLowerCase();
|
|
84
|
+
} catch {
|
|
85
|
+
return url.toLowerCase();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ../shared/dist/index.mjs
|
|
90
|
+
import { z } from "zod";
|
|
91
|
+
var TailscaleHttpsConfigSchema = z.object({
|
|
92
|
+
provider: z.literal("tailscale"),
|
|
93
|
+
funnelPort: z.number().optional()
|
|
94
|
+
});
|
|
95
|
+
var CloudflareHttpsConfigSchema = z.object({
|
|
96
|
+
provider: z.literal("cloudflare"),
|
|
97
|
+
tunnelName: z.string(),
|
|
98
|
+
domain: z.string().optional()
|
|
99
|
+
});
|
|
100
|
+
var ManualHttpsConfigSchema = z.object({
|
|
101
|
+
provider: z.literal("manual"),
|
|
102
|
+
certPath: z.string(),
|
|
103
|
+
keyPath: z.string()
|
|
104
|
+
});
|
|
105
|
+
var HttpsConfigSchema = z.discriminatedUnion("provider", [
|
|
106
|
+
TailscaleHttpsConfigSchema,
|
|
107
|
+
CloudflareHttpsConfigSchema,
|
|
108
|
+
ManualHttpsConfigSchema
|
|
109
|
+
]);
|
|
110
|
+
var CliToolConfigSchema = z.object({
|
|
111
|
+
type: z.literal("cli"),
|
|
112
|
+
name: z.string(),
|
|
113
|
+
command: z.string(),
|
|
114
|
+
args: z.array(z.string()).optional(),
|
|
115
|
+
description: z.string(),
|
|
116
|
+
timeout: z.number().optional(),
|
|
117
|
+
cwd: z.string().optional(),
|
|
118
|
+
env: z.record(z.string()).optional()
|
|
119
|
+
});
|
|
120
|
+
var McpToolConfigSchema = z.object({
|
|
121
|
+
type: z.literal("mcp"),
|
|
122
|
+
name: z.string(),
|
|
123
|
+
command: z.string(),
|
|
124
|
+
args: z.array(z.string()).optional(),
|
|
125
|
+
description: z.string(),
|
|
126
|
+
env: z.record(z.string()).optional()
|
|
127
|
+
});
|
|
128
|
+
var ScriptToolConfigSchema = z.object({
|
|
129
|
+
type: z.literal("script"),
|
|
130
|
+
name: z.string(),
|
|
131
|
+
path: z.string(),
|
|
132
|
+
description: z.string(),
|
|
133
|
+
timeout: z.number().optional(),
|
|
134
|
+
interpreter: z.string().optional()
|
|
135
|
+
});
|
|
136
|
+
var BuiltinToolConfigSchema = z.object({
|
|
137
|
+
type: z.literal("builtin"),
|
|
138
|
+
name: z.string(),
|
|
139
|
+
builtin: z.enum(["filesystem", "shell"]),
|
|
140
|
+
description: z.string(),
|
|
141
|
+
config: z.record(z.unknown()).optional()
|
|
142
|
+
});
|
|
143
|
+
var ToolConfigSchema = z.discriminatedUnion("type", [
|
|
144
|
+
CliToolConfigSchema,
|
|
145
|
+
McpToolConfigSchema,
|
|
146
|
+
ScriptToolConfigSchema,
|
|
147
|
+
BuiltinToolConfigSchema
|
|
148
|
+
]);
|
|
149
|
+
var AuthConfigSchema = z.object({
|
|
150
|
+
encryptionKey: z.string().length(64),
|
|
151
|
+
pairingTtlSeconds: z.number().default(300),
|
|
152
|
+
sessionTtlSeconds: z.number().default(14400)
|
|
153
|
+
});
|
|
154
|
+
var ServerConfigSchema = z.object({
|
|
155
|
+
port: z.number().default(7443),
|
|
156
|
+
host: z.string().default("0.0.0.0"),
|
|
157
|
+
https: HttpsConfigSchema.optional()
|
|
158
|
+
});
|
|
159
|
+
var GigaiConfigSchema = z.object({
|
|
160
|
+
serverName: z.string().optional(),
|
|
161
|
+
server: ServerConfigSchema,
|
|
162
|
+
auth: AuthConfigSchema,
|
|
163
|
+
tools: z.array(ToolConfigSchema).default([])
|
|
164
|
+
});
|
|
165
|
+
function decodeJWTPayload(token) {
|
|
166
|
+
const parts = token.split(".");
|
|
167
|
+
if (parts.length !== 3) {
|
|
168
|
+
throw new Error("Invalid JWT format: expected 3 segments");
|
|
169
|
+
}
|
|
170
|
+
const payload = parts[1];
|
|
171
|
+
const base64 = payload.replace(/-/g, "+").replace(/_/g, "/");
|
|
172
|
+
const padded = base64 + "=".repeat((4 - base64.length % 4) % 4);
|
|
173
|
+
const decoded = Buffer.from(padded, "base64").toString("utf8");
|
|
174
|
+
return JSON.parse(decoded);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// ../cli/src/identity.ts
|
|
178
|
+
function getOrgUUID() {
|
|
179
|
+
if (process.env.GIGAI_ORG_UUID) {
|
|
180
|
+
return process.env.GIGAI_ORG_UUID;
|
|
181
|
+
}
|
|
182
|
+
const proxyUrl = process.env.HTTPS_PROXY || process.env.HTTP_PROXY || "";
|
|
183
|
+
const jwtMatch = proxyUrl.match(/jwt_([^@]+)/);
|
|
184
|
+
if (jwtMatch) {
|
|
185
|
+
try {
|
|
186
|
+
const payload = decodeJWTPayload(jwtMatch[1]);
|
|
187
|
+
if (payload.organization_uuid) {
|
|
188
|
+
return payload.organization_uuid;
|
|
189
|
+
}
|
|
190
|
+
} catch {
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
const anthropicProxy = process.env.ANTHROPIC_PROXY_URL ?? "";
|
|
194
|
+
const anthropicJwtMatch = anthropicProxy.match(/([A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+)/);
|
|
195
|
+
if (anthropicJwtMatch) {
|
|
196
|
+
try {
|
|
197
|
+
const payload = decodeJWTPayload(anthropicJwtMatch[1]);
|
|
198
|
+
if (payload.organization_uuid) {
|
|
199
|
+
return payload.organization_uuid;
|
|
200
|
+
}
|
|
201
|
+
} catch {
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
const apiKey = process.env.ANTHROPIC_API_KEY ?? "";
|
|
205
|
+
if (apiKey.includes(".")) {
|
|
206
|
+
try {
|
|
207
|
+
const payload = decodeJWTPayload(apiKey);
|
|
208
|
+
if (payload.organization_uuid) {
|
|
209
|
+
return payload.organization_uuid;
|
|
210
|
+
}
|
|
211
|
+
} catch {
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
throw new Error(
|
|
215
|
+
"Cannot determine organization UUID. Set GIGAI_ORG_UUID environment variable."
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// ../cli/src/http.ts
|
|
220
|
+
async function getProxyDispatcher() {
|
|
221
|
+
const proxyUrl = process.env.HTTPS_PROXY || process.env.HTTP_PROXY;
|
|
222
|
+
if (!proxyUrl) return void 0;
|
|
223
|
+
try {
|
|
224
|
+
const undici = await import("undici");
|
|
225
|
+
return new undici.ProxyAgent(proxyUrl);
|
|
226
|
+
} catch {
|
|
227
|
+
return void 0;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
var _dispatcher = null;
|
|
231
|
+
async function ensureDispatcher() {
|
|
232
|
+
if (_dispatcher === null) {
|
|
233
|
+
_dispatcher = await getProxyDispatcher();
|
|
234
|
+
}
|
|
235
|
+
return _dispatcher;
|
|
236
|
+
}
|
|
237
|
+
function createHttpClient(serverUrl, sessionToken) {
|
|
238
|
+
const baseUrl = serverUrl.replace(/\/$/, "");
|
|
239
|
+
async function request(path, init = {}) {
|
|
240
|
+
const headers = {
|
|
241
|
+
...init.headers ?? {}
|
|
242
|
+
};
|
|
243
|
+
if (sessionToken) {
|
|
244
|
+
headers["Authorization"] = `Bearer ${sessionToken}`;
|
|
245
|
+
}
|
|
246
|
+
if (!headers["Content-Type"] && init.body && typeof init.body === "string") {
|
|
247
|
+
headers["Content-Type"] = "application/json";
|
|
248
|
+
}
|
|
249
|
+
const dispatcher = await ensureDispatcher();
|
|
250
|
+
const fetchOpts = {
|
|
251
|
+
...init,
|
|
252
|
+
headers
|
|
253
|
+
};
|
|
254
|
+
if (dispatcher) {
|
|
255
|
+
fetchOpts.dispatcher = dispatcher;
|
|
256
|
+
}
|
|
257
|
+
const res = await fetch(`${baseUrl}${path}`, fetchOpts);
|
|
258
|
+
if (!res.ok) {
|
|
259
|
+
let errorBody;
|
|
260
|
+
try {
|
|
261
|
+
errorBody = await res.json();
|
|
262
|
+
} catch {
|
|
263
|
+
}
|
|
264
|
+
const message = errorBody?.error?.message ?? `HTTP ${res.status}: ${res.statusText}`;
|
|
265
|
+
throw new Error(message);
|
|
266
|
+
}
|
|
267
|
+
return res.json();
|
|
268
|
+
}
|
|
269
|
+
return {
|
|
270
|
+
get(path) {
|
|
271
|
+
return request(path);
|
|
272
|
+
},
|
|
273
|
+
post(path, body) {
|
|
274
|
+
return request(path, {
|
|
275
|
+
method: "POST",
|
|
276
|
+
body: body ? JSON.stringify(body) : void 0
|
|
277
|
+
});
|
|
278
|
+
},
|
|
279
|
+
async postMultipart(path, formData) {
|
|
280
|
+
const headers = {};
|
|
281
|
+
if (sessionToken) {
|
|
282
|
+
headers["Authorization"] = `Bearer ${sessionToken}`;
|
|
283
|
+
}
|
|
284
|
+
const dispatcher = await ensureDispatcher();
|
|
285
|
+
const fetchOpts = {
|
|
286
|
+
method: "POST",
|
|
287
|
+
headers,
|
|
288
|
+
body: formData
|
|
289
|
+
};
|
|
290
|
+
if (dispatcher) {
|
|
291
|
+
fetchOpts.dispatcher = dispatcher;
|
|
292
|
+
}
|
|
293
|
+
const res = await fetch(`${baseUrl}${path}`, fetchOpts);
|
|
294
|
+
if (!res.ok) {
|
|
295
|
+
let errorBody;
|
|
296
|
+
try {
|
|
297
|
+
errorBody = await res.json();
|
|
298
|
+
} catch {
|
|
299
|
+
}
|
|
300
|
+
throw new Error(errorBody?.error?.message ?? `HTTP ${res.status}`);
|
|
301
|
+
}
|
|
302
|
+
return res.json();
|
|
303
|
+
},
|
|
304
|
+
async getRaw(path) {
|
|
305
|
+
const headers = {};
|
|
306
|
+
if (sessionToken) {
|
|
307
|
+
headers["Authorization"] = `Bearer ${sessionToken}`;
|
|
308
|
+
}
|
|
309
|
+
const dispatcher = await ensureDispatcher();
|
|
310
|
+
const fetchOpts = { headers };
|
|
311
|
+
if (dispatcher) {
|
|
312
|
+
fetchOpts.dispatcher = dispatcher;
|
|
313
|
+
}
|
|
314
|
+
const res = await fetch(`${baseUrl}${path}`, fetchOpts);
|
|
315
|
+
if (!res.ok) {
|
|
316
|
+
throw new Error(`HTTP ${res.status}: ${res.statusText}`);
|
|
317
|
+
}
|
|
318
|
+
return res;
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// ../cli/src/version.ts
|
|
324
|
+
var VERSION = "0.2.6";
|
|
325
|
+
|
|
326
|
+
// ../cli/src/connect.ts
|
|
327
|
+
async function connect(serverName) {
|
|
328
|
+
const config = await readConfig();
|
|
329
|
+
if (serverName) {
|
|
330
|
+
if (!config.servers[serverName]) {
|
|
331
|
+
const available = Object.keys(config.servers);
|
|
332
|
+
throw new Error(
|
|
333
|
+
available.length > 0 ? `Unknown server "${serverName}". Available: ${available.join(", ")}` : `No servers configured. Run 'gigai pair' first.`
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
config.activeServer = serverName;
|
|
337
|
+
await writeConfig(config);
|
|
338
|
+
}
|
|
339
|
+
const active = getActiveEntry(config);
|
|
340
|
+
if (!active) {
|
|
341
|
+
throw new Error("No server configured. Run 'gigai pair' first.");
|
|
342
|
+
}
|
|
343
|
+
const { name, entry } = active;
|
|
344
|
+
if (entry.sessionToken && entry.sessionExpiresAt) {
|
|
345
|
+
if (Date.now() < entry.sessionExpiresAt - 5 * 60 * 1e3) {
|
|
346
|
+
await checkAndUpdateServer(entry.server, entry.sessionToken);
|
|
347
|
+
return { serverUrl: entry.server, sessionToken: entry.sessionToken };
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
const orgUuid = getOrgUUID();
|
|
351
|
+
const http = createHttpClient(entry.server);
|
|
352
|
+
const res = await http.post("/auth/connect", {
|
|
353
|
+
encryptedToken: entry.token,
|
|
354
|
+
orgUuid
|
|
355
|
+
});
|
|
356
|
+
await updateServerSession(name, res.sessionToken, res.expiresAt);
|
|
357
|
+
await checkAndUpdateServer(entry.server, res.sessionToken);
|
|
358
|
+
return { serverUrl: entry.server, sessionToken: res.sessionToken };
|
|
359
|
+
}
|
|
360
|
+
async function checkAndUpdateServer(serverUrl, sessionToken) {
|
|
361
|
+
try {
|
|
362
|
+
const http = createHttpClient(serverUrl);
|
|
363
|
+
const health = await http.get("/health");
|
|
364
|
+
if (isNewer(VERSION, health.version)) {
|
|
365
|
+
console.log(`Server is outdated (${health.version} \u2192 ${VERSION}). Updating...`);
|
|
366
|
+
const authedHttp = createHttpClient(serverUrl, sessionToken);
|
|
367
|
+
const res = await authedHttp.post("/admin/update");
|
|
368
|
+
if (res.updated) {
|
|
369
|
+
console.log("Server updated and restarting.");
|
|
370
|
+
await waitForServer(serverUrl, 15e3);
|
|
371
|
+
console.log("Server is back online.");
|
|
372
|
+
} else {
|
|
373
|
+
console.log(`Server update failed: ${res.error ?? "unknown error"}`);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
} catch {
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
async function waitForServer(serverUrl, timeoutMs) {
|
|
380
|
+
const start = Date.now();
|
|
381
|
+
const http = createHttpClient(serverUrl);
|
|
382
|
+
await new Promise((r) => setTimeout(r, 2e3));
|
|
383
|
+
while (Date.now() - start < timeoutMs) {
|
|
384
|
+
try {
|
|
385
|
+
await http.get("/health");
|
|
386
|
+
return;
|
|
387
|
+
} catch {
|
|
388
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
function isNewer(client, server) {
|
|
393
|
+
const parse = (v) => {
|
|
394
|
+
const [core, pre] = v.replace(/^v/, "").split("-");
|
|
395
|
+
const parts = core.split(".").map(Number);
|
|
396
|
+
const preNum = pre ? parseInt(pre.replace(/\D+/g, "")) || 0 : Infinity;
|
|
397
|
+
return [...parts, preNum];
|
|
398
|
+
};
|
|
399
|
+
const c = parse(client);
|
|
400
|
+
const s = parse(server);
|
|
401
|
+
for (let i = 0; i < Math.max(c.length, s.length); i++) {
|
|
402
|
+
const cv = c[i] ?? 0;
|
|
403
|
+
const sv = s[i] ?? 0;
|
|
404
|
+
if (cv > sv) return true;
|
|
405
|
+
if (cv < sv) return false;
|
|
406
|
+
}
|
|
407
|
+
return false;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// ../cli/src/skill.ts
|
|
411
|
+
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
412
|
+
import "path";
|
|
413
|
+
var SKILL_MD = `---
|
|
414
|
+
name: gigai
|
|
415
|
+
description: Access tools on the user's machine via the gigai CLI
|
|
416
|
+
---
|
|
417
|
+
|
|
418
|
+
# gigai
|
|
419
|
+
|
|
420
|
+
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.
|
|
421
|
+
|
|
422
|
+
## Setup (run once per conversation)
|
|
423
|
+
|
|
424
|
+
\`\`\`bash
|
|
425
|
+
npm install -g @schuttdev/kon 2>/dev/null
|
|
426
|
+
mkdir -p ~/.gigai
|
|
427
|
+
cp /mnt/skills/user/gigai/config.json ~/.gigai/config.json
|
|
428
|
+
gigai connect
|
|
429
|
+
\`\`\`
|
|
430
|
+
|
|
431
|
+
If \`gigai connect\` succeeds, you are ready to use tools. If it fails, tell the user.
|
|
432
|
+
|
|
433
|
+
## Usage
|
|
434
|
+
|
|
435
|
+
### List available tools
|
|
436
|
+
\`\`\`bash
|
|
437
|
+
gigai list
|
|
438
|
+
\`\`\`
|
|
439
|
+
|
|
440
|
+
### Run a tool
|
|
441
|
+
\`\`\`bash
|
|
442
|
+
gigai <tool-name> [args...]
|
|
443
|
+
\`\`\`
|
|
444
|
+
|
|
445
|
+
Examples:
|
|
446
|
+
\`\`\`bash
|
|
447
|
+
gigai shell date
|
|
448
|
+
gigai fs list Documents
|
|
449
|
+
gigai shell whoami
|
|
450
|
+
\`\`\`
|
|
451
|
+
|
|
452
|
+
### Get help for a tool
|
|
453
|
+
\`\`\`bash
|
|
454
|
+
gigai help <tool-name>
|
|
455
|
+
\`\`\`
|
|
456
|
+
|
|
457
|
+
### Switch server (if multiple are configured)
|
|
458
|
+
\`\`\`bash
|
|
459
|
+
gigai connect <server-name>
|
|
460
|
+
\`\`\`
|
|
461
|
+
|
|
462
|
+
### File transfer
|
|
463
|
+
\`\`\`bash
|
|
464
|
+
gigai upload <file>
|
|
465
|
+
gigai download <id> <dest>
|
|
466
|
+
\`\`\`
|
|
467
|
+
|
|
468
|
+
## Important
|
|
469
|
+
|
|
470
|
+
- Always run the setup block before first use in a new conversation
|
|
471
|
+
- All commands execute on the **user's machine**, not in this sandbox
|
|
472
|
+
- If you get auth errors, run \`gigai connect\` to refresh the session
|
|
473
|
+
- Tools are scoped to what the user has configured \u2014 if a tool is missing, tell the user
|
|
474
|
+
`;
|
|
475
|
+
async function hasExistingSkill() {
|
|
476
|
+
try {
|
|
477
|
+
await readFile2("/mnt/skills/user/gigai/config.json", "utf8");
|
|
478
|
+
return true;
|
|
479
|
+
} catch {
|
|
480
|
+
return false;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
async function generateSkillZip(serverName, serverUrl, token) {
|
|
484
|
+
let skillConfig = { servers: {} };
|
|
485
|
+
try {
|
|
486
|
+
const raw = await readFile2("/mnt/skills/user/gigai/config.json", "utf8");
|
|
487
|
+
const existing = JSON.parse(raw);
|
|
488
|
+
if (existing.servers) {
|
|
489
|
+
skillConfig = existing;
|
|
490
|
+
}
|
|
491
|
+
} catch {
|
|
492
|
+
}
|
|
493
|
+
let merged = false;
|
|
494
|
+
for (const [name, entry] of Object.entries(skillConfig.servers)) {
|
|
495
|
+
if (normalizeHost(entry.server) === normalizeHost(serverUrl)) {
|
|
496
|
+
skillConfig.servers[name] = { server: serverUrl, token };
|
|
497
|
+
skillConfig.activeServer = name;
|
|
498
|
+
merged = true;
|
|
499
|
+
break;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
if (!merged) {
|
|
503
|
+
skillConfig.servers[serverName] = { server: serverUrl, token };
|
|
504
|
+
skillConfig.activeServer = serverName;
|
|
505
|
+
}
|
|
506
|
+
const configJson = JSON.stringify(skillConfig, null, 2) + "\n";
|
|
507
|
+
return createZip([
|
|
508
|
+
{ path: "gigai/SKILL.md", data: Buffer.from(SKILL_MD, "utf8") },
|
|
509
|
+
{ path: "gigai/config.json", data: Buffer.from(configJson, "utf8") }
|
|
510
|
+
]);
|
|
511
|
+
}
|
|
512
|
+
async function writeSkillZip(zip) {
|
|
513
|
+
const outputsDir = "/mnt/user-data/outputs";
|
|
514
|
+
try {
|
|
515
|
+
await mkdir2(outputsDir, { recursive: true });
|
|
516
|
+
const outPath = `${outputsDir}/gigai.zip`;
|
|
517
|
+
await writeFile2(outPath, zip);
|
|
518
|
+
return outPath;
|
|
519
|
+
} catch {
|
|
520
|
+
const outPath = "gigai.zip";
|
|
521
|
+
await writeFile2(outPath, zip);
|
|
522
|
+
return outPath;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
function normalizeHost(url) {
|
|
526
|
+
try {
|
|
527
|
+
return new URL(url).hostname.toLowerCase();
|
|
528
|
+
} catch {
|
|
529
|
+
return url.toLowerCase();
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
var crc32Table = new Uint32Array(256);
|
|
533
|
+
for (let i = 0; i < 256; i++) {
|
|
534
|
+
let c = i;
|
|
535
|
+
for (let j = 0; j < 8; j++) {
|
|
536
|
+
c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
|
|
537
|
+
}
|
|
538
|
+
crc32Table[i] = c >>> 0;
|
|
539
|
+
}
|
|
540
|
+
function crc32(data) {
|
|
541
|
+
let crc = 4294967295;
|
|
542
|
+
for (const byte of data) {
|
|
543
|
+
crc = crc >>> 8 ^ crc32Table[(crc ^ byte) & 255];
|
|
544
|
+
}
|
|
545
|
+
return (crc ^ 4294967295) >>> 0;
|
|
546
|
+
}
|
|
547
|
+
function createZip(entries) {
|
|
548
|
+
const parts = [];
|
|
549
|
+
const centralParts = [];
|
|
550
|
+
let offset = 0;
|
|
551
|
+
for (const entry of entries) {
|
|
552
|
+
const name = Buffer.from(entry.path, "utf8");
|
|
553
|
+
const checksum = crc32(entry.data);
|
|
554
|
+
const local = Buffer.alloc(30);
|
|
555
|
+
local.writeUInt32LE(67324752, 0);
|
|
556
|
+
local.writeUInt16LE(20, 4);
|
|
557
|
+
local.writeUInt16LE(0, 6);
|
|
558
|
+
local.writeUInt16LE(0, 8);
|
|
559
|
+
local.writeUInt16LE(0, 10);
|
|
560
|
+
local.writeUInt16LE(0, 12);
|
|
561
|
+
local.writeUInt32LE(checksum, 14);
|
|
562
|
+
local.writeUInt32LE(entry.data.length, 18);
|
|
563
|
+
local.writeUInt32LE(entry.data.length, 22);
|
|
564
|
+
local.writeUInt16LE(name.length, 26);
|
|
565
|
+
local.writeUInt16LE(0, 28);
|
|
566
|
+
parts.push(local, name, entry.data);
|
|
567
|
+
const central = Buffer.alloc(46);
|
|
568
|
+
central.writeUInt32LE(33639248, 0);
|
|
569
|
+
central.writeUInt16LE(20, 4);
|
|
570
|
+
central.writeUInt16LE(20, 6);
|
|
571
|
+
central.writeUInt16LE(0, 8);
|
|
572
|
+
central.writeUInt16LE(0, 10);
|
|
573
|
+
central.writeUInt16LE(0, 12);
|
|
574
|
+
central.writeUInt16LE(0, 14);
|
|
575
|
+
central.writeUInt32LE(checksum, 16);
|
|
576
|
+
central.writeUInt32LE(entry.data.length, 20);
|
|
577
|
+
central.writeUInt32LE(entry.data.length, 24);
|
|
578
|
+
central.writeUInt16LE(name.length, 28);
|
|
579
|
+
central.writeUInt16LE(0, 30);
|
|
580
|
+
central.writeUInt16LE(0, 32);
|
|
581
|
+
central.writeUInt16LE(0, 34);
|
|
582
|
+
central.writeUInt16LE(0, 36);
|
|
583
|
+
central.writeUInt32LE(0, 38);
|
|
584
|
+
central.writeUInt32LE(offset, 42);
|
|
585
|
+
centralParts.push(central, name);
|
|
586
|
+
offset += 30 + name.length + entry.data.length;
|
|
587
|
+
}
|
|
588
|
+
const centralDir = Buffer.concat(centralParts);
|
|
589
|
+
const eocd = Buffer.alloc(22);
|
|
590
|
+
eocd.writeUInt32LE(101010256, 0);
|
|
591
|
+
eocd.writeUInt16LE(0, 4);
|
|
592
|
+
eocd.writeUInt16LE(0, 6);
|
|
593
|
+
eocd.writeUInt16LE(entries.length, 8);
|
|
594
|
+
eocd.writeUInt16LE(entries.length, 10);
|
|
595
|
+
eocd.writeUInt32LE(centralDir.length, 12);
|
|
596
|
+
eocd.writeUInt32LE(offset, 16);
|
|
597
|
+
eocd.writeUInt16LE(0, 20);
|
|
598
|
+
return Buffer.concat([...parts, centralDir, eocd]);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// ../cli/src/pair.ts
|
|
602
|
+
async function pair(code, serverUrl) {
|
|
603
|
+
const orgUuid = getOrgUUID();
|
|
604
|
+
const http = createHttpClient(serverUrl);
|
|
605
|
+
const res = await http.post("/auth/pair", {
|
|
606
|
+
pairingCode: code,
|
|
607
|
+
orgUuid
|
|
608
|
+
});
|
|
609
|
+
await addServer(res.serverName, serverUrl, res.encryptedToken);
|
|
610
|
+
console.log(`Paired with "${res.serverName}" successfully!`);
|
|
611
|
+
const existing = await hasExistingSkill();
|
|
612
|
+
const zip = await generateSkillZip(res.serverName, serverUrl, res.encryptedToken);
|
|
613
|
+
const outPath = await writeSkillZip(zip);
|
|
614
|
+
console.log(`
|
|
615
|
+
Skill zip written to: ${outPath}`);
|
|
616
|
+
if (existing) {
|
|
617
|
+
console.log("Skill file updated. Download and re-upload to Claude.");
|
|
618
|
+
} else {
|
|
619
|
+
console.log("Upload this file as a skill in Claude (Settings \u2192 Customize \u2192 Upload Skill).");
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// ../cli/src/discover.ts
|
|
624
|
+
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
|
|
625
|
+
import { join as join2 } from "path";
|
|
626
|
+
import { homedir as homedir2 } from "os";
|
|
627
|
+
var MANIFEST_TTL = 5 * 60 * 1e3;
|
|
628
|
+
function getManifestPath() {
|
|
629
|
+
const dir = process.env.GIGAI_CONFIG_DIR ?? join2(homedir2(), ".gigai");
|
|
630
|
+
return join2(dir, "tool-manifest.json");
|
|
631
|
+
}
|
|
632
|
+
async function fetchTools(http) {
|
|
633
|
+
try {
|
|
634
|
+
const raw = await readFile3(getManifestPath(), "utf8");
|
|
635
|
+
const cache = JSON.parse(raw);
|
|
636
|
+
if (Date.now() - cache.fetchedAt < MANIFEST_TTL) {
|
|
637
|
+
return cache.tools;
|
|
638
|
+
}
|
|
639
|
+
} catch {
|
|
640
|
+
}
|
|
641
|
+
const res = await http.get("/tools");
|
|
642
|
+
try {
|
|
643
|
+
const dir = process.env.GIGAI_CONFIG_DIR ?? join2(homedir2(), ".gigai");
|
|
644
|
+
await mkdir3(dir, { recursive: true });
|
|
645
|
+
const cache = { tools: res.tools, fetchedAt: Date.now() };
|
|
646
|
+
await writeFile3(getManifestPath(), JSON.stringify(cache));
|
|
647
|
+
} catch {
|
|
648
|
+
}
|
|
649
|
+
return res.tools;
|
|
650
|
+
}
|
|
651
|
+
async function fetchToolDetail(http, name) {
|
|
652
|
+
return http.get(`/tools/${encodeURIComponent(name)}`);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
// ../cli/src/exec.ts
|
|
656
|
+
async function execTool(http, name, args, timeout) {
|
|
657
|
+
const res = await http.post("/exec", {
|
|
658
|
+
tool: name,
|
|
659
|
+
args,
|
|
660
|
+
timeout
|
|
661
|
+
});
|
|
662
|
+
if (res.stdout) process.stdout.write(res.stdout);
|
|
663
|
+
if (res.stderr) process.stderr.write(res.stderr);
|
|
664
|
+
process.exitCode = res.exitCode;
|
|
665
|
+
}
|
|
666
|
+
async function execMcpTool(http, tool, mcpTool, args) {
|
|
667
|
+
const res = await http.post("/exec/mcp", {
|
|
668
|
+
tool,
|
|
669
|
+
mcpTool,
|
|
670
|
+
args
|
|
671
|
+
});
|
|
672
|
+
for (const content of res.content) {
|
|
673
|
+
if (content.type === "text" && content.text) {
|
|
674
|
+
process.stdout.write(content.text + "\n");
|
|
675
|
+
} else if (content.type === "image") {
|
|
676
|
+
console.log(`[Image: ${content.mimeType}]`);
|
|
677
|
+
} else if (content.type === "resource") {
|
|
678
|
+
console.log(`[Resource: ${content.mimeType}]`);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
if (res.isError) {
|
|
682
|
+
process.exitCode = 1;
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
// ../cli/src/transfer.ts
|
|
687
|
+
import { readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
|
|
688
|
+
import { basename } from "path";
|
|
689
|
+
import { Blob } from "buffer";
|
|
690
|
+
async function upload(http, filePath) {
|
|
691
|
+
const content = await readFile4(filePath);
|
|
692
|
+
const filename = basename(filePath);
|
|
693
|
+
const formData = new FormData();
|
|
694
|
+
const blob = new Blob([content]);
|
|
695
|
+
formData.append("file", blob, filename);
|
|
696
|
+
const res = await http.postMultipart(
|
|
697
|
+
"/transfer/upload",
|
|
698
|
+
formData
|
|
699
|
+
);
|
|
700
|
+
console.log(`Uploaded: ${res.id}`);
|
|
701
|
+
return res.id;
|
|
702
|
+
}
|
|
703
|
+
async function download(http, id, destPath) {
|
|
704
|
+
const res = await http.getRaw(`/transfer/${encodeURIComponent(id)}`);
|
|
705
|
+
const buffer = Buffer.from(await res.arrayBuffer());
|
|
706
|
+
await writeFile4(destPath, buffer);
|
|
707
|
+
console.log(`Downloaded to: ${destPath}`);
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
// ../cli/src/output.ts
|
|
711
|
+
function formatToolList(tools) {
|
|
712
|
+
if (tools.length === 0) return "No tools registered.";
|
|
713
|
+
const maxName = Math.max(...tools.map((t) => t.name.length));
|
|
714
|
+
const maxType = Math.max(...tools.map((t) => t.type.length));
|
|
715
|
+
const lines = tools.map((t) => {
|
|
716
|
+
const name = t.name.padEnd(maxName);
|
|
717
|
+
const type = t.type.padEnd(maxType);
|
|
718
|
+
return ` ${name} ${type} ${t.description}`;
|
|
719
|
+
});
|
|
720
|
+
return `Available tools:
|
|
721
|
+
${lines.join("\n")}`;
|
|
722
|
+
}
|
|
723
|
+
function formatToolDetail(detail) {
|
|
724
|
+
const lines = [];
|
|
725
|
+
lines.push(`${detail.name} (${detail.type})`);
|
|
726
|
+
lines.push(` ${detail.description}`);
|
|
727
|
+
if (detail.usage) {
|
|
728
|
+
lines.push(`
|
|
729
|
+
Usage: ${detail.usage}`);
|
|
730
|
+
}
|
|
731
|
+
if (detail.args?.length) {
|
|
732
|
+
lines.push("\nArguments:");
|
|
733
|
+
for (const arg of detail.args) {
|
|
734
|
+
const req = arg.required ? " (required)" : "";
|
|
735
|
+
const def = arg.default ? ` [default: ${arg.default}]` : "";
|
|
736
|
+
lines.push(` ${arg.name}${req}${def} \u2014 ${arg.description}`);
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
if (detail.mcpTools?.length) {
|
|
740
|
+
lines.push("\nMCP Tools:");
|
|
741
|
+
for (const t of detail.mcpTools) {
|
|
742
|
+
lines.push(` ${t.name} \u2014 ${t.description}`);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
return lines.join("\n");
|
|
746
|
+
}
|
|
747
|
+
function formatStatus(config) {
|
|
748
|
+
const serverNames = Object.keys(config.servers);
|
|
749
|
+
if (serverNames.length === 0) {
|
|
750
|
+
return "Not connected. Run 'gigai pair <code> <server-url>' to set up.";
|
|
751
|
+
}
|
|
752
|
+
const lines = [];
|
|
753
|
+
for (const name of serverNames) {
|
|
754
|
+
const entry = config.servers[name];
|
|
755
|
+
const active = name === config.activeServer ? " (active)" : "";
|
|
756
|
+
lines.push(` ${name}${active} ${entry.server}`);
|
|
757
|
+
if (entry.sessionExpiresAt) {
|
|
758
|
+
const remaining = entry.sessionExpiresAt - Date.now();
|
|
759
|
+
if (remaining > 0) {
|
|
760
|
+
lines.push(` Session expires in ${Math.floor(remaining / 6e4)} minutes`);
|
|
761
|
+
} else {
|
|
762
|
+
lines.push(" Session expired \u2014 will auto-renew on next command");
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
return `Servers:
|
|
767
|
+
${lines.join("\n")}`;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
// src/index.ts
|
|
771
|
+
var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
|
|
772
|
+
"pair",
|
|
773
|
+
"connect",
|
|
774
|
+
"list",
|
|
775
|
+
"help",
|
|
776
|
+
"status",
|
|
777
|
+
"upload",
|
|
778
|
+
"download",
|
|
779
|
+
"version",
|
|
780
|
+
"--help",
|
|
781
|
+
"-h"
|
|
782
|
+
]);
|
|
783
|
+
var firstArg = process.argv[2];
|
|
784
|
+
if (firstArg && !firstArg.startsWith("-") && !KNOWN_COMMANDS.has(firstArg)) {
|
|
785
|
+
const toolName = firstArg;
|
|
786
|
+
const toolArgs = process.argv.slice(3);
|
|
787
|
+
try {
|
|
788
|
+
const { serverUrl, sessionToken } = await connect();
|
|
789
|
+
const http = createHttpClient(serverUrl, sessionToken);
|
|
790
|
+
const { tool: detail } = await fetchToolDetail(http, toolName);
|
|
791
|
+
if (detail.type === "mcp") {
|
|
792
|
+
const mcpToolName = toolArgs[0];
|
|
793
|
+
if (!mcpToolName) {
|
|
794
|
+
const toolNames = (detail.mcpTools ?? []).map((t) => ` ${t.name} \u2014 ${t.description}`);
|
|
795
|
+
console.log(`MCP tools for ${toolName}:
|
|
796
|
+
${toolNames.join("\n")}`);
|
|
797
|
+
} else {
|
|
798
|
+
const jsonArg = toolArgs.slice(1).join(" ");
|
|
799
|
+
const args = jsonArg ? JSON.parse(jsonArg) : {};
|
|
800
|
+
await execMcpTool(http, toolName, mcpToolName, args);
|
|
801
|
+
}
|
|
802
|
+
} else {
|
|
803
|
+
await execTool(http, toolName, toolArgs);
|
|
804
|
+
}
|
|
805
|
+
} catch (e) {
|
|
806
|
+
console.error(`Error: ${e.message}`);
|
|
807
|
+
process.exitCode = 1;
|
|
808
|
+
}
|
|
809
|
+
} else {
|
|
810
|
+
runCitty();
|
|
811
|
+
}
|
|
812
|
+
function runCitty() {
|
|
813
|
+
const pairCommand = defineCommand({
|
|
814
|
+
meta: { name: "pair", description: "Pair with a gigai server" },
|
|
815
|
+
args: {
|
|
816
|
+
code: { type: "positional", description: "Pairing code", required: true },
|
|
817
|
+
server: { type: "positional", description: "Server URL", required: true }
|
|
818
|
+
},
|
|
819
|
+
async run({ args }) {
|
|
820
|
+
await pair(args.code, args.server);
|
|
821
|
+
}
|
|
822
|
+
});
|
|
823
|
+
const connectCommand = defineCommand({
|
|
824
|
+
meta: { name: "connect", description: "Establish a session with the server" },
|
|
825
|
+
args: {
|
|
826
|
+
name: { type: "positional", description: "Server name (optional)", required: false }
|
|
827
|
+
},
|
|
828
|
+
async run({ args }) {
|
|
829
|
+
const { serverUrl } = await connect(args.name);
|
|
830
|
+
console.log(`Connected to ${serverUrl}`);
|
|
831
|
+
}
|
|
832
|
+
});
|
|
833
|
+
const listCommand = defineCommand({
|
|
834
|
+
meta: { name: "list", description: "List available tools" },
|
|
835
|
+
async run() {
|
|
836
|
+
const { serverUrl, sessionToken } = await connect();
|
|
837
|
+
const http = createHttpClient(serverUrl, sessionToken);
|
|
838
|
+
const tools = await fetchTools(http);
|
|
839
|
+
console.log(formatToolList(tools));
|
|
840
|
+
}
|
|
841
|
+
});
|
|
842
|
+
const helpCommand = defineCommand({
|
|
843
|
+
meta: { name: "help", description: "Show help for a tool" },
|
|
844
|
+
args: {
|
|
845
|
+
tool: { type: "positional", description: "Tool name", required: true }
|
|
846
|
+
},
|
|
847
|
+
async run({ args }) {
|
|
848
|
+
const { serverUrl, sessionToken } = await connect();
|
|
849
|
+
const http = createHttpClient(serverUrl, sessionToken);
|
|
850
|
+
const { tool } = await fetchToolDetail(http, args.tool);
|
|
851
|
+
console.log(formatToolDetail(tool));
|
|
852
|
+
}
|
|
853
|
+
});
|
|
854
|
+
const statusCommand = defineCommand({
|
|
855
|
+
meta: { name: "status", description: "Show connection status" },
|
|
856
|
+
async run() {
|
|
857
|
+
const config = await readConfig();
|
|
858
|
+
console.log(formatStatus(config));
|
|
859
|
+
}
|
|
860
|
+
});
|
|
861
|
+
const uploadCommand = defineCommand({
|
|
862
|
+
meta: { name: "upload", description: "Upload a file to the server" },
|
|
863
|
+
args: {
|
|
864
|
+
file: { type: "positional", description: "File path", required: true }
|
|
865
|
+
},
|
|
866
|
+
async run({ args }) {
|
|
867
|
+
const { serverUrl, sessionToken } = await connect();
|
|
868
|
+
const http = createHttpClient(serverUrl, sessionToken);
|
|
869
|
+
await upload(http, args.file);
|
|
870
|
+
}
|
|
871
|
+
});
|
|
872
|
+
const downloadCommand = defineCommand({
|
|
873
|
+
meta: { name: "download", description: "Download a file from the server" },
|
|
874
|
+
args: {
|
|
875
|
+
id: { type: "positional", description: "Transfer ID", required: true },
|
|
876
|
+
dest: { type: "positional", description: "Destination path", required: true }
|
|
877
|
+
},
|
|
878
|
+
async run({ args }) {
|
|
879
|
+
const { serverUrl, sessionToken } = await connect();
|
|
880
|
+
const http = createHttpClient(serverUrl, sessionToken);
|
|
881
|
+
await download(http, args.id, args.dest);
|
|
882
|
+
}
|
|
883
|
+
});
|
|
884
|
+
const versionCommand = defineCommand({
|
|
885
|
+
meta: { name: "version", description: "Show version" },
|
|
886
|
+
run() {
|
|
887
|
+
console.log(`gigai v${VERSION} (kon)`);
|
|
888
|
+
}
|
|
889
|
+
});
|
|
890
|
+
const main = defineCommand({
|
|
891
|
+
meta: {
|
|
892
|
+
name: "gigai",
|
|
893
|
+
version: VERSION,
|
|
894
|
+
description: "gigai client \u2014 bridge CLI tools to Claude"
|
|
895
|
+
},
|
|
896
|
+
subCommands: {
|
|
897
|
+
pair: pairCommand,
|
|
898
|
+
connect: connectCommand,
|
|
899
|
+
list: listCommand,
|
|
900
|
+
help: helpCommand,
|
|
901
|
+
status: statusCommand,
|
|
902
|
+
upload: uploadCommand,
|
|
903
|
+
download: downloadCommand,
|
|
904
|
+
version: versionCommand
|
|
905
|
+
}
|
|
906
|
+
});
|
|
907
|
+
runMain(main);
|
|
908
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@schuttdev/kon",
|
|
3
|
+
"version": "0.2.6",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Lightweight gigai client for Claude code execution",
|
|
6
|
+
"bin": {
|
|
7
|
+
"gigai": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"exports": {
|
|
10
|
+
".": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsup",
|
|
17
|
+
"clean": "rm -rf dist"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"citty": "^0.1.6",
|
|
21
|
+
"zod": "^3.22.0",
|
|
22
|
+
"undici": "^6.0.0"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@gigai/shared": "*",
|
|
26
|
+
"tsup": "^8.0.1",
|
|
27
|
+
"typescript": "^5.3.3"
|
|
28
|
+
}
|
|
29
|
+
}
|