@minimalcorp/tsunagi 0.3.1 → 0.3.3

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 (52) hide show
  1. package/.next/standalone/apps/web/.next/BUILD_ID +1 -1
  2. package/.next/standalone/apps/web/.next/build-manifest.json +3 -3
  3. package/.next/standalone/apps/web/.next/prerender-manifest.json +3 -3
  4. package/.next/standalone/apps/web/.next/server/app/_global-error.html +1 -1
  5. package/.next/standalone/apps/web/.next/server/app/_global-error.rsc +1 -1
  6. package/.next/standalone/apps/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  7. package/.next/standalone/apps/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  8. package/.next/standalone/apps/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  9. package/.next/standalone/apps/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  10. package/.next/standalone/apps/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  11. package/.next/standalone/apps/web/.next/server/app/_not-found.html +1 -1
  12. package/.next/standalone/apps/web/.next/server/app/_not-found.rsc +1 -1
  13. package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  14. package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  15. package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  16. package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  17. package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  18. package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  19. package/.next/standalone/apps/web/.next/server/app/index.html +1 -1
  20. package/.next/standalone/apps/web/.next/server/app/index.rsc +1 -1
  21. package/.next/standalone/apps/web/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  22. package/.next/standalone/apps/web/.next/server/app/index.segments/_full.segment.rsc +1 -1
  23. package/.next/standalone/apps/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
  24. package/.next/standalone/apps/web/.next/server/app/index.segments/_index.segment.rsc +1 -1
  25. package/.next/standalone/apps/web/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  26. package/.next/standalone/apps/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  27. package/.next/standalone/apps/web/.next/server/app/settings.html +1 -1
  28. package/.next/standalone/apps/web/.next/server/app/settings.rsc +2 -2
  29. package/.next/standalone/apps/web/.next/server/app/settings.segments/_full.segment.rsc +2 -2
  30. package/.next/standalone/apps/web/.next/server/app/settings.segments/_head.segment.rsc +1 -1
  31. package/.next/standalone/apps/web/.next/server/app/settings.segments/_index.segment.rsc +1 -1
  32. package/.next/standalone/apps/web/.next/server/app/settings.segments/_tree.segment.rsc +1 -1
  33. package/.next/standalone/apps/web/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +2 -2
  34. package/.next/standalone/apps/web/.next/server/app/settings.segments/settings.segment.rsc +1 -1
  35. package/.next/standalone/apps/web/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
  36. package/.next/standalone/apps/web/.next/server/chunks/ssr/_0g79jqo._.js +1 -1
  37. package/.next/standalone/apps/web/.next/server/chunks/ssr/_0kflgfj._.js +1 -1
  38. package/.next/standalone/apps/web/.next/server/middleware-build-manifest.js +3 -3
  39. package/.next/standalone/apps/web/.next/server/pages/404.html +1 -1
  40. package/.next/standalone/apps/web/.next/server/pages/500.html +1 -1
  41. package/.next/standalone/apps/web/.next/server/server-reference-manifest.js +1 -1
  42. package/.next/standalone/apps/web/.next/server/server-reference-manifest.json +1 -1
  43. package/.next/standalone/apps/web/.next/static/chunks/{0~qk-51yuw4mm.js → 053h220fng_68.js} +9 -9
  44. package/.next/standalone/apps/web/.next/static/chunks/{08e.dbdz18nd2.js → 0oricprxoxb_d.js} +9 -9
  45. package/dist/server/pty-manager.d.ts +12 -0
  46. package/dist/server/pty-manager.js +25 -0
  47. package/dist/server/routes/terminal.js +100 -19
  48. package/npm-shrinkwrap.json +21 -21
  49. package/package.json +1 -1
  50. /package/.next/standalone/apps/web/.next/static/{Qv73X7lHL0PkQ5DtK6UEF → 7x81GlNKt9yqIkeI7U1BX}/_buildManifest.js +0 -0
  51. /package/.next/standalone/apps/web/.next/static/{Qv73X7lHL0PkQ5DtK6UEF → 7x81GlNKt9yqIkeI7U1BX}/_clientMiddlewareManifest.js +0 -0
  52. /package/.next/standalone/apps/web/.next/static/{Qv73X7lHL0PkQ5DtK6UEF → 7x81GlNKt9yqIkeI7U1BX}/_ssgManifest.js +0 -0
@@ -35,6 +35,10 @@ export interface PtySession {
35
35
  /** GCインターバルタイマー */
36
36
  gcTimer: ReturnType<typeof setInterval> | null;
37
37
  }
38
+ export interface TerminalSize {
39
+ cols: number;
40
+ rows: number;
41
+ }
38
42
  declare class PtyManager {
39
43
  private sessions;
40
44
  /** sessionId → 最後に input を送信した socket.id */
@@ -42,6 +46,14 @@ declare class PtyManager {
42
46
  setActiveSocket(sessionId: string, socketId: string): void;
43
47
  getActiveSocket(sessionId: string): string | undefined;
44
48
  clearActiveSocket(sessionId: string, socketId: string): void;
49
+ /** sessionId → (socket.id → その socket の xterm サイズ)。挿入順 = 最後に resize/claim した順 */
50
+ private terminalSizes;
51
+ setTerminalSize(sessionId: string, socketId: string, size: TerminalSize): void;
52
+ /** socket の登録を外し、残っている socket のうち最後に使われたものを返す */
53
+ removeTerminalSocket(sessionId: string, socketId: string): {
54
+ socketId: string;
55
+ size: TerminalSize;
56
+ } | undefined;
45
57
  /**
46
58
  * @param unsetKeys 外側 Terminal や DB 由来でも内部ターミナルに渡さない環境変数キー
47
59
  * (例: Ollama タブでは Anthropic の認証トークンを取り除く)
@@ -59,6 +59,31 @@ class PtyManager {
59
59
  this.activeSocketIds.delete(sessionId);
60
60
  }
61
61
  }
62
+ /** sessionId → (socket.id → その socket の xterm サイズ)。挿入順 = 最後に resize/claim した順 */
63
+ terminalSizes = new Map();
64
+ setTerminalSize(sessionId, socketId, size) {
65
+ let sizes = this.terminalSizes.get(sessionId);
66
+ if (!sizes) {
67
+ sizes = new Map();
68
+ this.terminalSizes.set(sessionId, sizes);
69
+ }
70
+ // 末尾に移して「最後に使われた socket」を末尾で表す
71
+ sizes.delete(socketId);
72
+ sizes.set(socketId, size);
73
+ }
74
+ /** socket の登録を外し、残っている socket のうち最後に使われたものを返す */
75
+ removeTerminalSocket(sessionId, socketId) {
76
+ const sizes = this.terminalSizes.get(sessionId);
77
+ if (!sizes)
78
+ return undefined;
79
+ sizes.delete(socketId);
80
+ const last = Array.from(sizes.entries()).at(-1);
81
+ if (!last) {
82
+ this.terminalSizes.delete(sessionId);
83
+ return undefined;
84
+ }
85
+ return { socketId: last[0], size: last[1] };
86
+ }
62
87
  /**
63
88
  * @param unsetKeys 外側 Terminal や DB 由来でも内部ターミナルに渡さない環境変数キー
64
89
  * (例: Ollama タブでは Anthropic の認証トークンを取り除く)
@@ -27,8 +27,34 @@ function stripTerminalQueries(s) {
27
27
  .replace(/\x1b\[[?>]?[0-9;]*n/g, '') // DSR: ESC[?6n(主犯) / ESC[6n / ESC[5n など
28
28
  .replace(/\x1b\[[?>=]?[0-9;]*c/g, ''); // DA: ESC[c / ESC[0c / ESC[>c / ESC[=c
29
29
  }
30
+ /**
31
+ * 表示中のクライアントがいなくなった PTY の cols をこの幅に縮める。
32
+ * 一定の幅を下回ると Claude(Ink) が full-frame redraw を行い <Static> を再 emit するという
33
+ * 観測に基づくしきい値。元 cols が既にこの値の場合のみ -1 にして必ず cols を変化させる。
34
+ *
35
+ * 再表示時は replay 後にクライアントが実サイズへ resize するため、必ず cols が変わり
36
+ * SIGWINCH が飛んで Claude が現在の画面を描き直す(同一サイズの resize は no-op で
37
+ * SIGWINCH が飛ばない)。縮める処理を切断時に済ませておくことで、縮めた時の再描画が
38
+ * 再表示より前に scrollback に入り、replay と描き直しの順序がタイミングに依存しない。
39
+ */
40
+ const REDRAW_BUMP_COLS = 64;
41
+ function bumpColsForRedraw(session) {
42
+ const { pty: ptyProcess } = session;
43
+ const cols = ptyProcess.cols === REDRAW_BUMP_COLS ? REDRAW_BUMP_COLS - 1 : REDRAW_BUMP_COLS;
44
+ try {
45
+ ptyProcess.resize(cols, ptyProcess.rows);
46
+ }
47
+ catch {
48
+ // 終了済みの PTY は無視する
49
+ }
50
+ }
30
51
  export async function terminalRoutes(fastify) {
31
52
  const io = fastify.io;
53
+ // 作成中のセッション(sessionId → 作成処理)。存在確認から PTY 作成までの間に await を挟むため、
54
+ // 同じ sessionId の作成要求が並行すると両方が存在確認を通過し、後発が "Session already exists" で
55
+ // 失敗する(開発時の StrictMode による effect の二重実行で必ず起きる)。後発は作成中の処理を待って
56
+ // 再利用として返す。
57
+ const pendingCreations = new Map();
32
58
  initPtyEnvSync(io);
33
59
  // socket.io イベントハンドラ
34
60
  io.on('connection', (socket) => {
@@ -52,6 +78,10 @@ export async function terminalRoutes(fastify) {
52
78
  socket.join(room);
53
79
  return;
54
80
  }
81
+ // 別セッションへ join し直した場合は、前のセッションのサイズ登録を外す
82
+ if (boundSessionId && boundSessionId !== sessionId) {
83
+ ptyManager.removeTerminalSocket(boundSessionId, socket.id);
84
+ }
55
85
  boundSessionId = sessionId;
56
86
  const session = ptyManager.getSession(sessionId);
57
87
  if (!session) {
@@ -66,6 +96,7 @@ export async function terminalRoutes(fastify) {
66
96
  dataHandlerDispose = null;
67
97
  exitHandlerDispose = null;
68
98
  socket.removeAllListeners('resize');
99
+ socket.removeAllListeners('claim');
69
100
  socket.removeAllListeners('input');
70
101
  socket.join(room);
71
102
  // 環境変数の変更が未反映ならクライアントに伝える(反映待ちバッジ)
@@ -82,16 +113,19 @@ export async function terminalRoutes(fastify) {
82
113
  ptyManager.setActiveSocket(sessionId, socket.id);
83
114
  const { pty: ptyProcess } = session;
84
115
  const isReused = session.scrollback.length > 0;
85
- // リングバッファの内容を一括送信(再接続時の画面復元)
86
- if (isReused) {
87
- // 端末クエリ列(ESC[?6n / ESC[c 等)を除去してから replay する。
88
- // これを怠ると xterm.js が応答を生成し PTY へ注入され、入力欄に "1;2c64;3R..." が
89
- // 勝手に入力される(stripTerminalQueries のコメント参照)。
90
- const buffered = stripTerminalQueries(session.scrollback.join(''));
116
+ // リングバッファの内容を一括送信(画面復元)。初回接続・再接続を問わず join のたびに送る。
117
+ // クライアントは replay を受けたら画面をクリアし、xterm を PTY と同じサイズにしてから
118
+ // 書き込み、完了後に実サイズへ resize する(新規セッションは data が空)。
119
+ // 端末クエリ列(ESC[?6n / ESC[c 等)を除去してから replay する。
120
+ // これを怠ると xterm.js が応答を生成し PTY へ注入され、入力欄に "1;2c64;3R..." が
121
+ // 勝手に入力される(stripTerminalQueries のコメント参照)。
122
+ const buffered = isReused ? stripTerminalQueries(session.scrollback.join('')) : '';
123
+ socket.emit('replay', {
91
124
  // 末尾の \r\n / \n / \r をトリムする。
92
- const trimmed = buffered.replace(/[\r\n]+$/, '');
93
- socket.emit('output', { data: trimmed });
94
- }
125
+ data: buffered.replace(/[\r\n]+$/, ''),
126
+ cols: ptyProcess.cols,
127
+ rows: ptyProcess.rows,
128
+ });
95
129
  // reused の場合、最初の resize まで onData 出力をバッファリング
96
130
  let initialResizeHandled = !isReused;
97
131
  const pendingOutput = [];
@@ -125,11 +159,16 @@ export async function terminalRoutes(fastify) {
125
159
  ptyManager.deleteSession(sessionId);
126
160
  });
127
161
  exitHandlerDispose = () => exitHandler.dispose();
128
- // resize イベント: PTYリサイズ
162
+ // resize イベント: この socket のサイズを記録し、所有者 socket の場合のみ PTY へ反映する。
163
+ // 同一タスクを複数ウィンドウで開いた場合に、フォアグラウンドでない側のサイズで
164
+ // PTY が変わり Claude の表示幅がずれるのを防ぐ(所有者の切り替えは claim で行う)。
129
165
  socket.on('resize', ({ sessionId: sid, cols, rows }) => {
130
166
  if (sid !== sessionId)
131
167
  return;
132
- ptyProcess.resize(cols, rows);
168
+ ptyManager.setTerminalSize(sessionId, socket.id, { cols, rows });
169
+ if (ptyManager.getActiveSocket(sessionId) === socket.id) {
170
+ ptyProcess.resize(cols, rows);
171
+ }
133
172
  // reused時: 初回 resize 後にバッファリングしていた出力をフラッシュ
134
173
  if (!initialResizeHandled) {
135
174
  initialResizeHandled = true;
@@ -140,6 +179,17 @@ export async function terminalRoutes(fastify) {
140
179
  }
141
180
  }
142
181
  });
182
+ // claim イベント: フォアグラウンドになった socket が所有者になり、PTY を自分のサイズに合わせる。
183
+ // サイズが変われば SIGWINCH で Claude が描き直す。
184
+ socket.on('claim', ({ sessionId: sid, cols, rows }) => {
185
+ if (sid !== sessionId)
186
+ return;
187
+ ptyManager.setTerminalSize(sessionId, socket.id, { cols, rows });
188
+ ptyManager.setActiveSocket(sessionId, socket.id);
189
+ if (ptyProcess.cols !== cols || ptyProcess.rows !== rows) {
190
+ ptyProcess.resize(cols, rows);
191
+ }
192
+ });
143
193
  // input イベント: 所有者 socket からの入力のみ PTY へ書き込む。
144
194
  // ゾンビ/非所有 socket(再接続前の旧 socket・誤バインド socket 等)からの重複 input は
145
195
  // 破棄し、1 キーストロークの多重書き込み(例: /clear が連続入力される)を防ぐ。
@@ -170,8 +220,26 @@ export async function terminalRoutes(fastify) {
170
220
  // 遅延切断では GC を張らない(現役セッションが誤って GC 対象になるのを防ぐ)。
171
221
  const wasOwner = ptyManager.getActiveSocket(boundSessionId) === socket.id;
172
222
  ptyManager.clearActiveSocket(boundSessionId, socket.id);
173
- if (wasOwner) {
223
+ const next = ptyManager.removeTerminalSocket(boundSessionId, socket.id);
224
+ if (wasOwner && next) {
225
+ // 同じ PTY を表示している socket が残っている → 最後に使われた socket に所有権と
226
+ // サイズを引き継ぐ(表示中なので GC も cols の縮小も行わない)
227
+ ptyManager.setActiveSocket(boundSessionId, next.socketId);
228
+ const session = ptyManager.getSession(boundSessionId);
229
+ try {
230
+ session?.pty.resize(next.size.cols, next.size.rows);
231
+ }
232
+ catch {
233
+ // 終了済みの PTY は無視する
234
+ }
235
+ }
236
+ else if (wasOwner) {
174
237
  ptyManager.scheduleGc(boundSessionId);
238
+ // 次に表示されたとき Claude に描き直させるため、表示中のクライアントがいない間に
239
+ // cols を縮めておく(REDRAW_BUMP_COLS 参照)
240
+ const session = ptyManager.getSession(boundSessionId);
241
+ if (session)
242
+ bumpColsForRedraw(session);
175
243
  }
176
244
  }
177
245
  });
@@ -190,14 +258,27 @@ export async function terminalRoutes(fastify) {
190
258
  fastify.log.info({ sessionId }, 'Reusing existing PTY session');
191
259
  return reply.status(200).send({ sessionId, reused: true });
192
260
  }
193
- const launchRequest = { cwd, env };
261
+ const pending = pendingCreations.get(sessionId);
262
+ const creation = pending ??
263
+ (async () => {
264
+ const launchRequest = { cwd, env };
265
+ const launch = await resolvePtyLaunch(sessionId, launchRequest);
266
+ await startPtySession(sessionId, launch, {
267
+ request: launchRequest,
268
+ launchClaude: Boolean(claude),
269
+ command,
270
+ });
271
+ })();
272
+ if (!pending) {
273
+ pendingCreations.set(sessionId, creation);
274
+ void creation.catch(() => undefined).finally(() => pendingCreations.delete(sessionId));
275
+ }
194
276
  try {
195
- const launch = await resolvePtyLaunch(sessionId, launchRequest);
196
- await startPtySession(sessionId, launch, {
197
- request: launchRequest,
198
- launchClaude: Boolean(claude),
199
- command,
200
- });
277
+ await creation;
278
+ if (pending) {
279
+ fastify.log.info({ sessionId }, 'Reusing PTY session created by a concurrent request');
280
+ return reply.status(200).send({ sessionId, reused: true });
281
+ }
201
282
  return reply.status(201).send({ sessionId, reused: false });
202
283
  }
203
284
  catch (err) {
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@minimalcorp/tsunagi",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@minimalcorp/tsunagi",
9
- "version": "0.3.1",
9
+ "version": "0.3.3",
10
10
  "license": "SEE LICENSE IN LICENSE",
11
11
  "os": [
12
12
  "darwin",
@@ -107,9 +107,9 @@
107
107
  "license": "MIT"
108
108
  },
109
109
  "node_modules/@fastify/cors": {
110
- "version": "11.3.0",
111
- "resolved": "https://registry.npmjs.org/@fastify/cors/-/cors-11.3.0.tgz",
112
- "integrity": "sha512-ggQGua+xHv1MvePbPr0v//xLYEsCXbWspquXCJS9Ot5YoRXq8J8ZWzHnxDBVnbtXosvistXo6LtNzOJswf64Fw==",
110
+ "version": "11.3.1",
111
+ "resolved": "https://registry.npmjs.org/@fastify/cors/-/cors-11.3.1.tgz",
112
+ "integrity": "sha512-gZXNzd6FLHzOIfvoK4+Az3Wko2V6Y0MIsLxpyo4AEYl7a9q1JBPwPaxLea4nkr++8FCiJiPpYYo6E02uZW0dqw==",
113
113
  "funding": [
114
114
  {
115
115
  "type": "github",
@@ -127,9 +127,9 @@
127
127
  }
128
128
  },
129
129
  "node_modules/@fastify/deepmerge": {
130
- "version": "3.2.1",
131
- "resolved": "https://registry.npmjs.org/@fastify/deepmerge/-/deepmerge-3.2.1.tgz",
132
- "integrity": "sha512-N5Oqvltoa2r9z1tbx4xjky0oRR60v+T47Ic4J1ukoVQcptLOrIdRnCSdTGmOmajZuHVKlTnfcmrjyqsGEW1ztA==",
130
+ "version": "3.2.2",
131
+ "resolved": "https://registry.npmjs.org/@fastify/deepmerge/-/deepmerge-3.2.2.tgz",
132
+ "integrity": "sha512-155eqA/WMQdN6gUD4RrhS2K0Pcz2Ou9pQ86L0sipOpP9ONbTA6oYw/hW4VdBjR4egcDHqmWUK9x80oUX7S1mDQ==",
133
133
  "funding": [
134
134
  {
135
135
  "type": "github",
@@ -194,9 +194,9 @@
194
194
  "license": "MIT"
195
195
  },
196
196
  "node_modules/@fastify/http-proxy": {
197
- "version": "11.6.3",
198
- "resolved": "https://registry.npmjs.org/@fastify/http-proxy/-/http-proxy-11.6.3.tgz",
199
- "integrity": "sha512-yrPdZD9vdBoMtuRhzlTuVpvoyUrqW6/VXqFrSbRBrbUN+x5+PjddnBVbooY7DILZ0QixSuU7a9sG96JVbAbESg==",
197
+ "version": "11.6.4",
198
+ "resolved": "https://registry.npmjs.org/@fastify/http-proxy/-/http-proxy-11.6.4.tgz",
199
+ "integrity": "sha512-JPsbLX9D5X5yES1A6M2yx1i2PGf8eNyKT48j+6pgLIwxfRYpj1MEzR1CwASRvqdZOWhcPmdsZiIKehSgioW6ww==",
200
200
  "funding": [
201
201
  {
202
202
  "type": "github",
@@ -303,15 +303,15 @@
303
303
  }
304
304
  },
305
305
  "node_modules/@hono/node-server": {
306
- "version": "2.1.3",
307
- "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-2.1.3.tgz",
308
- "integrity": "sha512-TA//nWMqPhbfdfneACk6t5a9eqbS9lABEPyKn0/xZTah3H3U2XaVg85rJFl0/Fyit0I552YDHgXGVSf3GwqbUw==",
306
+ "version": "2.1.4",
307
+ "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-2.1.4.tgz",
308
+ "integrity": "sha512-2g1qeHl4BduwDn6L8nkAvDrPUWPMlkQ//a5NhP/zIyaXKMjZNRV+cG9Csu+BiJFf6+68pqzjf4Hi5sJGKHuS1g==",
309
309
  "license": "MIT",
310
310
  "engines": {
311
311
  "node": ">=20"
312
312
  },
313
313
  "peerDependencies": {
314
- "hono": "^4"
314
+ "hono": "^4 || ^5.0.0-0"
315
315
  }
316
316
  },
317
317
  "node_modules/@kwsites/file-exists": {
@@ -500,9 +500,9 @@
500
500
  ]
501
501
  },
502
502
  "node_modules/@modelcontextprotocol/sdk": {
503
- "version": "1.32.0",
504
- "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.32.0.tgz",
505
- "integrity": "sha512-8BviX/hK4Gd2eL1KdTwm0gfl4d3wdoErW0Jd00h6nomUAtk6IJk5vwQJc6kfWtJTzN5OEV/lgIiJKI8fiDGAlA==",
503
+ "version": "1.32.1",
504
+ "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.32.1.tgz",
505
+ "integrity": "sha512-2DdE+SJDtzLEEWzY1ZjY7Q+VcPhcV1KisD3zI4u0XZyktsjHum1mwbMI+JaulUBi2OZk+KJAi2uPXzxichPkdw==",
506
506
  "license": "MIT",
507
507
  "dependencies": {
508
508
  "@hono/node-server": "^1.19.9 || ^2.0.5",
@@ -2233,9 +2233,9 @@
2233
2233
  }
2234
2234
  },
2235
2235
  "node_modules/express-rate-limit": {
2236
- "version": "8.7.0",
2237
- "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.7.0.tgz",
2238
- "integrity": "sha512-hOwV7WOxXfjRpAM1DSJWZDXx3GhplwD8IfwuwvogD8i1Qnkgosw/H45s4ZnFAUHDAhPjlY9hLBvJhKmGMyY26g==",
2236
+ "version": "8.7.1",
2237
+ "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.7.1.tgz",
2238
+ "integrity": "sha512-2FXbqkygil3mZiXzEtM0IjE/UBmBNot0J1GEVrJT4JbrvwOSoEtqtO+wHbiMQELWe1eNvHoYRg2dkjOid+6JkA==",
2239
2239
  "license": "MIT",
2240
2240
  "dependencies": {
2241
2241
  "debug": "^4.4.3",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@minimalcorp/tsunagi",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "Multi-repo GitHub project management with Claude AI integration, designed for visualizing and controlling AI-driven development locally.",
5
5
  "keywords": [
6
6
  "tsunagi",