@schuttdev/kon 0.3.4 → 0.3.5

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.
Files changed (2) hide show
  1. package/dist/index.js +95 -96
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -234,27 +234,37 @@ async function ensureDispatcher() {
234
234
  }
235
235
  return _dispatcher;
236
236
  }
237
- function createHttpClient(serverUrl, sessionToken) {
237
+ function createHttpClient(serverUrl, sessionToken, onAuthFailure) {
238
238
  const baseUrl = serverUrl.replace(/\/$/, "");
239
+ let currentToken = sessionToken;
240
+ async function rawFetch(url, init) {
241
+ const dispatcher = await ensureDispatcher();
242
+ const fetchOpts = { ...init };
243
+ if (dispatcher) fetchOpts.dispatcher = dispatcher;
244
+ return fetch(url, fetchOpts);
245
+ }
246
+ function authHeaders() {
247
+ const h = {};
248
+ if (currentToken) h["Authorization"] = `Bearer ${currentToken}`;
249
+ return h;
250
+ }
239
251
  async function request(path, init = {}) {
240
252
  const headers = {
241
- ...init.headers ?? {}
253
+ ...init.headers ?? {},
254
+ ...authHeaders()
242
255
  };
243
- if (sessionToken) {
244
- headers["Authorization"] = `Bearer ${sessionToken}`;
245
- }
246
256
  if (!headers["Content-Type"] && init.body && typeof init.body === "string") {
247
257
  headers["Content-Type"] = "application/json";
248
258
  }
249
- const dispatcher = await ensureDispatcher();
250
- const fetchOpts = {
251
- ...init,
252
- headers
253
- };
254
- if (dispatcher) {
255
- fetchOpts.dispatcher = dispatcher;
259
+ let res = await rawFetch(`${baseUrl}${path}`, { ...init, headers });
260
+ if (res.status === 401 && onAuthFailure) {
261
+ const newToken = await onAuthFailure();
262
+ if (newToken) {
263
+ currentToken = newToken;
264
+ headers["Authorization"] = `Bearer ${newToken}`;
265
+ res = await rawFetch(`${baseUrl}${path}`, { ...init, headers });
266
+ }
256
267
  }
257
- const res = await fetch(`${baseUrl}${path}`, fetchOpts);
258
268
  if (!res.ok) {
259
269
  let errorBody;
260
270
  try {
@@ -277,16 +287,20 @@ function createHttpClient(serverUrl, sessionToken) {
277
287
  });
278
288
  },
279
289
  async delete(path) {
280
- const headers = {};
281
- if (sessionToken) {
282
- headers["Authorization"] = `Bearer ${sessionToken}`;
283
- }
284
- const dispatcher = await ensureDispatcher();
285
- const fetchOpts = { method: "DELETE", headers };
286
- if (dispatcher) {
287
- fetchOpts.dispatcher = dispatcher;
290
+ let res = await rawFetch(`${baseUrl}${path}`, {
291
+ method: "DELETE",
292
+ headers: authHeaders()
293
+ });
294
+ if (res.status === 401 && onAuthFailure) {
295
+ const newToken = await onAuthFailure();
296
+ if (newToken) {
297
+ currentToken = newToken;
298
+ res = await rawFetch(`${baseUrl}${path}`, {
299
+ method: "DELETE",
300
+ headers: authHeaders()
301
+ });
302
+ }
288
303
  }
289
- const res = await fetch(`${baseUrl}${path}`, fetchOpts);
290
304
  if (!res.ok) {
291
305
  let errorBody;
292
306
  try {
@@ -297,20 +311,22 @@ function createHttpClient(serverUrl, sessionToken) {
297
311
  }
298
312
  },
299
313
  async postMultipart(path, formData) {
300
- const headers = {};
301
- if (sessionToken) {
302
- headers["Authorization"] = `Bearer ${sessionToken}`;
303
- }
304
- const dispatcher = await ensureDispatcher();
305
- const fetchOpts = {
314
+ let res = await rawFetch(`${baseUrl}${path}`, {
306
315
  method: "POST",
307
- headers,
316
+ headers: authHeaders(),
308
317
  body: formData
309
- };
310
- if (dispatcher) {
311
- fetchOpts.dispatcher = dispatcher;
318
+ });
319
+ if (res.status === 401 && onAuthFailure) {
320
+ const newToken = await onAuthFailure();
321
+ if (newToken) {
322
+ currentToken = newToken;
323
+ res = await rawFetch(`${baseUrl}${path}`, {
324
+ method: "POST",
325
+ headers: authHeaders(),
326
+ body: formData
327
+ });
328
+ }
312
329
  }
313
- const res = await fetch(`${baseUrl}${path}`, fetchOpts);
314
330
  if (!res.ok) {
315
331
  let errorBody;
316
332
  try {
@@ -322,16 +338,18 @@ function createHttpClient(serverUrl, sessionToken) {
322
338
  return res.json();
323
339
  },
324
340
  async getRaw(path) {
325
- const headers = {};
326
- if (sessionToken) {
327
- headers["Authorization"] = `Bearer ${sessionToken}`;
328
- }
329
- const dispatcher = await ensureDispatcher();
330
- const fetchOpts = { headers };
331
- if (dispatcher) {
332
- fetchOpts.dispatcher = dispatcher;
341
+ let res = await rawFetch(`${baseUrl}${path}`, {
342
+ headers: authHeaders()
343
+ });
344
+ if (res.status === 401 && onAuthFailure) {
345
+ const newToken = await onAuthFailure();
346
+ if (newToken) {
347
+ currentToken = newToken;
348
+ res = await rawFetch(`${baseUrl}${path}`, {
349
+ headers: authHeaders()
350
+ });
351
+ }
333
352
  }
334
- const res = await fetch(`${baseUrl}${path}`, fetchOpts);
335
353
  if (!res.ok) {
336
354
  throw new Error(`HTTP ${res.status}: ${res.statusText}`);
337
355
  }
@@ -341,7 +359,7 @@ function createHttpClient(serverUrl, sessionToken) {
341
359
  }
342
360
 
343
361
  // ../cli/src/version.ts
344
- var VERSION = "0.3.4";
362
+ var VERSION = "0.3.5";
345
363
 
346
364
  // ../cli/src/connect.ts
347
365
  async function connect(serverName) {
@@ -361,15 +379,26 @@ async function connect(serverName) {
361
379
  throw new Error("No server configured. Run 'gigai pair' first.");
362
380
  }
363
381
  const { name, entry } = active;
382
+ const onAuthFailure = async () => {
383
+ try {
384
+ const result2 = await doRefreshSession(name, entry.server, entry.token);
385
+ return result2.sessionToken;
386
+ } catch {
387
+ return void 0;
388
+ }
389
+ };
364
390
  if (entry.sessionToken && entry.sessionExpiresAt) {
365
391
  if (Date.now() < entry.sessionExpiresAt - 5 * 60 * 1e3) {
366
392
  const token = await checkAndUpdateServer(entry.server, entry.sessionToken, name, entry.token);
367
- return { serverUrl: entry.server, sessionToken: token };
393
+ const http2 = createHttpClient(entry.server, token, onAuthFailure);
394
+ return { serverUrl: entry.server, sessionToken: token, http: http2 };
368
395
  }
369
396
  }
370
- return refreshSession(name, entry.server, entry.token);
397
+ const result = await doRefreshSession(name, entry.server, entry.token);
398
+ const http = createHttpClient(entry.server, result.sessionToken, onAuthFailure);
399
+ return { serverUrl: entry.server, sessionToken: result.sessionToken, http };
371
400
  }
372
- async function refreshSession(serverName, serverUrl, encryptedToken) {
401
+ async function doRefreshSession(serverName, serverUrl, encryptedToken) {
373
402
  const orgUuid = getOrgUUID();
374
403
  const http = createHttpClient(serverUrl);
375
404
  const res = await http.post("/auth/connect", {
@@ -1029,43 +1058,21 @@ if (firstArg && !firstArg.startsWith("-") && !KNOWN_COMMANDS.has(firstArg)) {
1029
1058
  const toolName = firstArg;
1030
1059
  const toolArgs = process.argv.slice(3);
1031
1060
  try {
1032
- let { serverUrl, sessionToken } = await connect();
1033
- let http = createHttpClient(serverUrl, sessionToken);
1034
- const runTool = async () => {
1035
- const { tool: detail } = await fetchToolDetail(http, toolName);
1036
- if (detail.type === "mcp") {
1037
- const mcpToolName = toolArgs[0];
1038
- if (!mcpToolName) {
1039
- const toolNames = (detail.mcpTools ?? []).map((t) => ` ${t.name} \u2014 ${t.description}`);
1040
- console.log(`MCP tools for ${toolName}:
1061
+ const { http } = await connect();
1062
+ const { tool: detail } = await fetchToolDetail(http, toolName);
1063
+ if (detail.type === "mcp") {
1064
+ const mcpToolName = toolArgs[0];
1065
+ if (!mcpToolName) {
1066
+ const toolNames = (detail.mcpTools ?? []).map((t) => ` ${t.name} \u2014 ${t.description}`);
1067
+ console.log(`MCP tools for ${toolName}:
1041
1068
  ${toolNames.join("\n")}`);
1042
- } else {
1043
- const jsonArg = toolArgs.slice(1).join(" ");
1044
- const args = jsonArg ? JSON.parse(jsonArg) : {};
1045
- await execMcpTool(http, toolName, mcpToolName, args);
1046
- }
1047
- } else {
1048
- await execTool(http, toolName, toolArgs);
1049
- }
1050
- };
1051
- try {
1052
- await runTool();
1053
- } catch (e) {
1054
- const msg = e.message;
1055
- if (msg.includes("Invalid session") || msg.includes("Session expired") || msg.includes("Authorization")) {
1056
- const config = await readConfig();
1057
- const active = getActiveEntry(config);
1058
- if (active) {
1059
- const refreshed = await refreshSession(active.name, active.entry.server, active.entry.token);
1060
- sessionToken = refreshed.sessionToken;
1061
- http = createHttpClient(serverUrl, sessionToken);
1062
- await runTool();
1063
- } else {
1064
- throw e;
1065
- }
1066
1069
  } else {
1067
- throw e;
1070
+ const jsonArg = toolArgs.slice(1).join(" ");
1071
+ const args = jsonArg ? JSON.parse(jsonArg) : {};
1072
+ await execMcpTool(http, toolName, mcpToolName, args);
1068
1073
  }
1074
+ } else {
1075
+ await execTool(http, toolName, toolArgs);
1069
1076
  }
1070
1077
  } catch (e) {
1071
1078
  console.error(`Error: ${e.message}`);
@@ -1098,8 +1105,7 @@ function runCitty() {
1098
1105
  const listCommand = defineCommand({
1099
1106
  meta: { name: "list", description: "List available tools" },
1100
1107
  async run() {
1101
- const { serverUrl, sessionToken } = await connect();
1102
- const http = createHttpClient(serverUrl, sessionToken);
1108
+ const { http } = await connect();
1103
1109
  const tools = await fetchTools(http);
1104
1110
  console.log(formatToolList(tools));
1105
1111
  }
@@ -1110,8 +1116,7 @@ function runCitty() {
1110
1116
  tool: { type: "positional", description: "Tool name", required: true }
1111
1117
  },
1112
1118
  async run({ args }) {
1113
- const { serverUrl, sessionToken } = await connect();
1114
- const http = createHttpClient(serverUrl, sessionToken);
1119
+ const { http } = await connect();
1115
1120
  const { tool } = await fetchToolDetail(http, args.tool);
1116
1121
  console.log(formatToolDetail(tool));
1117
1122
  }
@@ -1129,8 +1134,7 @@ function runCitty() {
1129
1134
  file: { type: "positional", description: "File path", required: true }
1130
1135
  },
1131
1136
  async run({ args }) {
1132
- const { serverUrl, sessionToken } = await connect();
1133
- const http = createHttpClient(serverUrl, sessionToken);
1137
+ const { http } = await connect();
1134
1138
  await upload(http, args.file);
1135
1139
  }
1136
1140
  });
@@ -1141,8 +1145,7 @@ function runCitty() {
1141
1145
  dest: { type: "positional", description: "Destination path", required: true }
1142
1146
  },
1143
1147
  async run({ args }) {
1144
- const { serverUrl, sessionToken } = await connect();
1145
- const http = createHttpClient(serverUrl, sessionToken);
1148
+ const { http } = await connect();
1146
1149
  await download(http, args.id, args.dest);
1147
1150
  }
1148
1151
  });
@@ -1155,8 +1158,7 @@ function runCitty() {
1155
1158
  const skillCommand = defineCommand({
1156
1159
  meta: { name: "skill", description: "Regenerate the skill zip with current tool details" },
1157
1160
  async run() {
1158
- const { serverUrl, sessionToken } = await connect();
1159
- const http = createHttpClient(serverUrl, sessionToken);
1161
+ const { http } = await connect();
1160
1162
  const tools = await fetchTools(http);
1161
1163
  console.log(`Fetching details for ${tools.length} tool(s)...`);
1162
1164
  const toolDetails = await Promise.all(
@@ -1185,8 +1187,7 @@ Skill zip written to: ${outPath}`);
1185
1187
  at: { type: "string", description: "Human-readable time (e.g. '9:00 AM tomorrow')" }
1186
1188
  },
1187
1189
  async run({ args }) {
1188
- const { serverUrl, sessionToken } = await connect();
1189
- const http = createHttpClient(serverUrl, sessionToken);
1190
+ const { http } = await connect();
1190
1191
  const rawArgs = process.argv.slice(4);
1191
1192
  const positional = [];
1192
1193
  let atValue = args.at;
@@ -1243,8 +1244,7 @@ Skill zip written to: ${outPath}`);
1243
1244
  const cronListCommand = defineCommand({
1244
1245
  meta: { name: "list", description: "List scheduled jobs" },
1245
1246
  async run() {
1246
- const { serverUrl, sessionToken } = await connect();
1247
- const http = createHttpClient(serverUrl, sessionToken);
1247
+ const { http } = await connect();
1248
1248
  const res = await http.get("/cron");
1249
1249
  if (res.jobs.length === 0) {
1250
1250
  console.log("No scheduled jobs.");
@@ -1268,8 +1268,7 @@ Skill zip written to: ${outPath}`);
1268
1268
  id: { type: "positional", description: "Job ID", required: true }
1269
1269
  },
1270
1270
  async run({ args }) {
1271
- const { serverUrl, sessionToken } = await connect();
1272
- const http = createHttpClient(serverUrl, sessionToken);
1271
+ const { http } = await connect();
1273
1272
  await http.delete(`/cron/${encodeURIComponent(args.id)}`);
1274
1273
  console.log(`Removed: ${args.id}`);
1275
1274
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@schuttdev/kon",
3
- "version": "0.3.4",
3
+ "version": "0.3.5",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Lightweight gigai client for Claude code execution",