@0xmaxma/claude-gateway 1.5.7 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +22 -0
  2. package/dist/agent/runner.d.ts +31 -0
  3. package/dist/agent/runner.d.ts.map +1 -1
  4. package/dist/agent/runner.js +224 -16
  5. package/dist/agent/runner.js.map +1 -1
  6. package/dist/api/apps-router.d.ts.map +1 -1
  7. package/dist/api/apps-router.js +7 -2
  8. package/dist/api/apps-router.js.map +1 -1
  9. package/dist/api/gateway-router.d.ts.map +1 -1
  10. package/dist/api/gateway-router.js +27 -0
  11. package/dist/api/gateway-router.js.map +1 -1
  12. package/dist/api/line-webhook-router.d.ts.map +1 -1
  13. package/dist/api/line-webhook-router.js +68 -0
  14. package/dist/api/line-webhook-router.js.map +1 -1
  15. package/dist/api/router.d.ts +2 -0
  16. package/dist/api/router.d.ts.map +1 -1
  17. package/dist/api/router.js +60 -4
  18. package/dist/api/router.js.map +1 -1
  19. package/dist/api/share-router.d.ts +11 -0
  20. package/dist/api/share-router.d.ts.map +1 -0
  21. package/dist/api/share-router.js +402 -0
  22. package/dist/api/share-router.js.map +1 -0
  23. package/dist/apps/installer.d.ts +55 -0
  24. package/dist/apps/installer.d.ts.map +1 -1
  25. package/dist/apps/installer.js +136 -1
  26. package/dist/apps/installer.js.map +1 -1
  27. package/dist/config/loader.d.ts.map +1 -1
  28. package/dist/config/loader.js +9 -0
  29. package/dist/config/loader.js.map +1 -1
  30. package/dist/config/public-url.d.ts +11 -0
  31. package/dist/config/public-url.d.ts.map +1 -0
  32. package/dist/config/public-url.js +41 -0
  33. package/dist/config/public-url.js.map +1 -0
  34. package/dist/config/watcher.d.ts.map +1 -1
  35. package/dist/config/watcher.js +1 -0
  36. package/dist/config/watcher.js.map +1 -1
  37. package/dist/history/db.d.ts +14 -0
  38. package/dist/history/db.d.ts.map +1 -1
  39. package/dist/history/db.js +48 -4
  40. package/dist/history/db.js.map +1 -1
  41. package/dist/history/types.d.ts +5 -0
  42. package/dist/history/types.d.ts.map +1 -1
  43. package/dist/session/process.d.ts +3 -0
  44. package/dist/session/process.d.ts.map +1 -1
  45. package/dist/session/process.js +27 -1
  46. package/dist/session/process.js.map +1 -1
  47. package/dist/session/store.d.ts +2 -1
  48. package/dist/session/store.d.ts.map +1 -1
  49. package/dist/session/store.js +10 -2
  50. package/dist/session/store.js.map +1 -1
  51. package/dist/share/session-image-catalog.d.ts +22 -0
  52. package/dist/share/session-image-catalog.d.ts.map +1 -0
  53. package/dist/share/session-image-catalog.js +151 -0
  54. package/dist/share/session-image-catalog.js.map +1 -0
  55. package/dist/share/share-store.d.ts +137 -0
  56. package/dist/share/share-store.d.ts.map +1 -0
  57. package/dist/share/share-store.js +340 -0
  58. package/dist/share/share-store.js.map +1 -0
  59. package/dist/types.d.ts +19 -7
  60. package/dist/types.d.ts.map +1 -1
  61. package/mcp/instructions.ts +1 -0
  62. package/mcp/server.ts +2 -0
  63. package/mcp/tools/agent/handlers.ts +55 -4
  64. package/mcp/tools/image/module.ts +240 -26
  65. package/mcp/tools/line/module.ts +186 -0
  66. package/mcp/tools/share-image/module.ts +120 -0
  67. package/mcp/tools/shared/share-client.ts +197 -0
  68. package/package.json +3 -1
@@ -33,6 +33,8 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.isValidSessionId = isValidSessionId;
37
+ exports.isValidAgentId = isValidAgentId;
36
38
  exports.createApiRouter = createApiRouter;
37
39
  const express_1 = require("express");
38
40
  const crypto_1 = require("crypto");
@@ -52,6 +54,21 @@ const MAX_MESSAGE_LENGTH = 10000;
52
54
  const DEFAULT_TIMEOUT_MS = 60000;
53
55
  const AGENT_ID_RE = /^[a-z][a-z0-9_-]{1,31}$/;
54
56
  const SAFE_FILENAME_RE = /^[a-zA-Z0-9._\-() ]+$/;
57
+ // session_id becomes a filesystem key (sessions/<id>.jsonl, .sessions/<id>/) so it
58
+ // must be constrained to the same safe charset as chat_id — no '/' or '.' that could
59
+ // escape the agent's directory. Clients may pass custom (non-UUID) session ids, so
60
+ // this preserves that while blocking path traversal.
61
+ const SESSION_ID_RE = /^[a-zA-Z0-9_-]{1,64}$/;
62
+ function isValidSessionId(v) {
63
+ return typeof v === 'string' && SESSION_ID_RE.test(v);
64
+ }
65
+ // agent_id likewise becomes a filesystem key (agents/<id>/media/…), so any
66
+ // endpoint that joins it into a path MUST format-validate it first — a bare
67
+ // non-empty check lets "../x" relocate the containment root itself. Exported so
68
+ // sibling routers (image share) apply the identical guard as the /api routes.
69
+ function isValidAgentId(v) {
70
+ return typeof v === 'string' && AGENT_ID_RE.test(v);
71
+ }
55
72
  function maskToken(token) {
56
73
  if (token.length <= 12)
57
74
  return '•'.repeat(token.length);
@@ -256,8 +273,8 @@ function createApiRouter(agentRunners, agentConfigs, apiKeys, configPath, models
256
273
  res.status(400).json({ error: 'chat_id must be 1-64 alphanumeric characters, hyphens, or underscores' });
257
274
  return;
258
275
  }
259
- if (session_id !== undefined && typeof session_id !== 'string') {
260
- res.status(400).json({ error: 'session_id must be a string if provided' });
276
+ if (session_id !== undefined && !isValidSessionId(session_id)) {
277
+ res.status(400).json({ error: 'session_id must be 1-64 alphanumeric characters, hyphens, or underscores' });
261
278
  return;
262
279
  }
263
280
  // Validate media_files
@@ -306,6 +323,26 @@ function createApiRouter(agentRunners, agentConfigs, apiKeys, configPath, models
306
323
  out[f] = v.trim();
307
324
  }
308
325
  }
326
+ // image_refs (#73) — reference images explicitly selected in the composer,
327
+ // order-significant. Each entry is a ref from GET /api/v1/image-catalog.
328
+ if (ip.image_refs !== undefined) {
329
+ const refs = ip.image_refs;
330
+ if (!Array.isArray(refs) || refs.some((r) => typeof r !== 'string' || !r.trim())) {
331
+ res.status(400).json({ error: 'image_params.image_refs must be an array of non-empty strings' });
332
+ return;
333
+ }
334
+ const trimmedRefs = refs.map((r) => r.trim());
335
+ if (trimmedRefs.length > 5) {
336
+ res.status(400).json({ error: 'image_params.image_refs allows at most 5 references' });
337
+ return;
338
+ }
339
+ if (new Set(trimmedRefs).size !== trimmedRefs.length) {
340
+ res.status(400).json({ error: 'image_params.image_refs must not contain duplicates' });
341
+ return;
342
+ }
343
+ if (trimmedRefs.length)
344
+ out.image_refs = trimmedRefs;
345
+ }
309
346
  if (ip.n !== undefined) {
310
347
  if (typeof ip.n !== 'number' || !Number.isFinite(ip.n) || ip.n < 1) {
311
348
  res.status(400).json({ error: 'image_params.n must be a positive number' });
@@ -566,7 +603,7 @@ function createApiRouter(agentRunners, agentConfigs, apiKeys, configPath, models
566
603
  description: cfg?.description ?? '',
567
604
  sessions: sessions.map((s) => {
568
605
  const meta = metaMap.get(s.sessionId);
569
- return { ...s, sessionName: meta?.name ?? null, imageConfig: meta?.imageConfig ?? null };
606
+ return { ...s, sessionName: meta?.name ?? null, imageConfig: meta?.imageConfig ?? null, model: meta?.model ?? null };
570
607
  }),
571
608
  };
572
609
  }));
@@ -2587,7 +2624,22 @@ function createApiRouter(agentRunners, agentConfigs, apiKeys, configPath, models
2587
2624
  const baseName = path.basename(rawFilename).replace(/\s+/g, '_');
2588
2625
  const safeBaseName = SAFE_FILENAME_RE.test(baseName) ? baseName : 'upload';
2589
2626
  const rawExt = path.extname(safeBaseName).replace(/[^a-zA-Z0-9.]/g, '').slice(0, 10);
2590
- const ext = rawExt || (mimeType.includes('pdf') ? '.pdf' : '.bin');
2627
+ // No usable extension (clipboard paste, non-ASCII filename sanitized to
2628
+ // 'upload') → derive it from the MIME type. Falling straight to .bin made
2629
+ // pasted images invisible to the session image catalog, which keys off the
2630
+ // file extension — so they could never be referenced (#74).
2631
+ const mimeExt = mimeType.includes('png')
2632
+ ? '.png'
2633
+ : mimeType.includes('jpeg') || mimeType.includes('jpg')
2634
+ ? '.jpeg'
2635
+ : mimeType.includes('webp')
2636
+ ? '.webp'
2637
+ : mimeType.includes('gif')
2638
+ ? '.gif'
2639
+ : mimeType.includes('pdf')
2640
+ ? '.pdf'
2641
+ : '.bin';
2642
+ const ext = rawExt || mimeExt;
2591
2643
  const tmpFile = path.join(os.tmpdir(), `gw-${Date.now()}${ext}`);
2592
2644
  try {
2593
2645
  await fsp.writeFile(tmpFile, buf);
@@ -3095,6 +3147,10 @@ function createApiRouter(agentRunners, agentConfigs, apiKeys, configPath, models
3095
3147
  res.status(400).json({ error: 'session_id is required' });
3096
3148
  return;
3097
3149
  }
3150
+ if (!isValidSessionId(sessionId)) {
3151
+ res.status(400).json({ error: 'session_id must be 1-64 alphanumeric characters, hyphens, or underscores' });
3152
+ return;
3153
+ }
3098
3154
  // chat_id is optional — provide the same value used when creating the session via POST /sessions
3099
3155
  // so the greeting message lands in the correct historyDb bucket (api-{chatId}).
3100
3156
  // If omitted, sessionId is used as the bucket key, which creates a secondary index entry.