@dotobokuri/fleet-console 1.28.0 → 1.30.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 (65) hide show
  1. package/dist/cli.mjs +209 -387
  2. package/dist/client/assets/{_baseUniq-0TpVlb56.js → _baseUniq-Dvbxpxj-.js} +1 -1
  3. package/dist/client/assets/{arc-BIRhKPNZ.js → arc-Cqerxs8I.js} +1 -1
  4. package/dist/client/assets/{architectureDiagram-Q4EWVU46-CJzkivmk.js → architectureDiagram-Q4EWVU46-gNhmCKnU.js} +1 -1
  5. package/dist/client/assets/{blockDiagram-DXYQGD6D-PkVBy6UB.js → blockDiagram-DXYQGD6D-CM5HRpKC.js} +1 -1
  6. package/dist/client/assets/{c4Diagram-AHTNJAMY-TOO0Zc07.js → c4Diagram-AHTNJAMY-DxLhQuvg.js} +1 -1
  7. package/dist/client/assets/channel-DKkQpLWI.js +1 -0
  8. package/dist/client/assets/{chunk-4BX2VUAB-Ugm2Ht26.js → chunk-4BX2VUAB-HX6C_GAC.js} +1 -1
  9. package/dist/client/assets/{chunk-4TB4RGXK-Byfwafb_.js → chunk-4TB4RGXK-CoBQUhS4.js} +1 -1
  10. package/dist/client/assets/{chunk-55IACEB6-2I_nwtuS.js → chunk-55IACEB6-DWsWxGBm.js} +1 -1
  11. package/dist/client/assets/{chunk-EDXVE4YY-JYP41aJo.js → chunk-EDXVE4YY-BXNRu1M4.js} +1 -1
  12. package/dist/client/assets/{chunk-FMBD7UC4-Duh-U1jY.js → chunk-FMBD7UC4-DzKgP1_A.js} +1 -1
  13. package/dist/client/assets/{chunk-OYMX7WX6-64cqkTOO.js → chunk-OYMX7WX6-aqXdvKUD.js} +1 -1
  14. package/dist/client/assets/{chunk-QZHKN3VN-engflZJB.js → chunk-QZHKN3VN-BbMqaUb0.js} +1 -1
  15. package/dist/client/assets/{chunk-YZCP3GAM-BwnK_1vX.js → chunk-YZCP3GAM-D8Mo0nv8.js} +1 -1
  16. package/dist/client/assets/classDiagram-6PBFFD2Q-CQurO5ix.js +1 -0
  17. package/dist/client/assets/classDiagram-v2-HSJHXN6E-CQurO5ix.js +1 -0
  18. package/dist/client/assets/clone-IodjsAGU.js +1 -0
  19. package/dist/client/assets/{cose-bilkent-S5V4N54A-D43_BnnE.js → cose-bilkent-S5V4N54A-BOuVjjXz.js} +1 -1
  20. package/dist/client/assets/{dagre-KV5264BT-Wh_p74YO.js → dagre-KV5264BT-BdUFzAj_.js} +1 -1
  21. package/dist/client/assets/{diagram-5BDNPKRD-BNcxA6wC.js → diagram-5BDNPKRD-C0iFrsGo.js} +1 -1
  22. package/dist/client/assets/{diagram-G4DWMVQ6-HQdA06kX.js → diagram-G4DWMVQ6-CGxGeQZt.js} +1 -1
  23. package/dist/client/assets/{diagram-MMDJMWI5-DlMdX_ng.js → diagram-MMDJMWI5-Dex5uqlA.js} +1 -1
  24. package/dist/client/assets/{diagram-TYMM5635-C1hKAjZD.js → diagram-TYMM5635-O48PEpaS.js} +1 -1
  25. package/dist/client/assets/{erDiagram-SMLLAGMA-CsSiuv9G.js → erDiagram-SMLLAGMA-B7PGw3YA.js} +1 -1
  26. package/dist/client/assets/{flowDiagram-DWJPFMVM-DfjQ3Ug9.js → flowDiagram-DWJPFMVM-CCXWJIMw.js} +1 -1
  27. package/dist/client/assets/{ganttDiagram-T4ZO3ILL-B0QHKePg.js → ganttDiagram-T4ZO3ILL-B26RghpR.js} +1 -1
  28. package/dist/client/assets/{gitGraphDiagram-UUTBAWPF-_zejoqTH.js → gitGraphDiagram-UUTBAWPF-BJsjMn6A.js} +1 -1
  29. package/dist/client/assets/{graph-Dr6IPcji.js → graph-DuEL0RWH.js} +1 -1
  30. package/dist/client/assets/index-Dj775lUe.css +1 -0
  31. package/dist/client/assets/index-r6rDZQCs.js +451 -0
  32. package/dist/client/assets/{infoDiagram-42DDH7IO-dhruAwPt.js → infoDiagram-42DDH7IO-D32dz1SM.js} +1 -1
  33. package/dist/client/assets/{ishikawaDiagram-UXIWVN3A-DPoZZiH0.js → ishikawaDiagram-UXIWVN3A-4YPQjln0.js} +1 -1
  34. package/dist/client/assets/{journeyDiagram-VCZTEJTY-DQk8gmrd.js → journeyDiagram-VCZTEJTY-BSvi22HM.js} +1 -1
  35. package/dist/client/assets/{kanban-definition-6JOO6SKY-B9rOgVFq.js → kanban-definition-6JOO6SKY-SozOXS7m.js} +1 -1
  36. package/dist/client/assets/{layout-D54BXsv2.js → layout-CNXntIRV.js} +1 -1
  37. package/dist/client/assets/{linear-DJxG7kFF.js → linear-9RZPFtO4.js} +1 -1
  38. package/dist/client/assets/{mermaid.core-BBFWA-zA.js → mermaid.core-kwH-UxKY.js} +4 -4
  39. package/dist/client/assets/{min-Dv_aLAYb.js → min-BdN2p8Q_.js} +1 -1
  40. package/dist/client/assets/{mindmap-definition-QFDTVHPH-Dv5woCon.js → mindmap-definition-QFDTVHPH-H-3dJmH9.js} +1 -1
  41. package/dist/client/assets/{pieDiagram-DEJITSTG-B_V_vCG8.js → pieDiagram-DEJITSTG-CspzYvcs.js} +1 -1
  42. package/dist/client/assets/{quadrantDiagram-34T5L4WZ-QHqq3tFD.js → quadrantDiagram-34T5L4WZ-D1C7e3-s.js} +1 -1
  43. package/dist/client/assets/{requirementDiagram-MS252O5E-BhPhfMzK.js → requirementDiagram-MS252O5E-BJwKRtMi.js} +1 -1
  44. package/dist/client/assets/{sankeyDiagram-XADWPNL6-D2-ZsaS_.js → sankeyDiagram-XADWPNL6-7oc42Z3P.js} +1 -1
  45. package/dist/client/assets/{sequenceDiagram-FGHM5R23-Di0qgM3v.js → sequenceDiagram-FGHM5R23-TP5H_DnF.js} +1 -1
  46. package/dist/client/assets/{stateDiagram-FHFEXIEX-BAuqrwY8.js → stateDiagram-FHFEXIEX-BWnrNgJY.js} +1 -1
  47. package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-Cmp8tobq.js +1 -0
  48. package/dist/client/assets/{timeline-definition-GMOUNBTQ-BcMGkC6T.js → timeline-definition-GMOUNBTQ-xXFvCsrr.js} +1 -1
  49. package/dist/client/assets/{vennDiagram-DHZGUBPP-C9dkwyKX.js → vennDiagram-DHZGUBPP-D5unm_iH.js} +1 -1
  50. package/dist/client/assets/{wardley-RL74JXVD-BLLCncPa.js → wardley-RL74JXVD-B-eT02rs.js} +1 -1
  51. package/dist/client/assets/{wardleyDiagram-NUSXRM2D-Yb9mlL0_.js → wardleyDiagram-NUSXRM2D-BTQ1Yqmb.js} +1 -1
  52. package/dist/client/assets/{xychartDiagram-5P7HB3ND-BAHLdje7.js → xychartDiagram-5P7HB3ND-Bialvg0l.js} +1 -1
  53. package/dist/client/index.html +2 -2
  54. package/dist/fleet-plugins/file-explorer/routes.mjs +199 -38
  55. package/dist/fleet-plugins/repository/routes.mjs +407 -108
  56. package/dist/fleet-plugins/skills/routes.mjs +60 -79
  57. package/dist/fleet-plugins/terminal/routes.mjs +577 -102
  58. package/package.json +1 -1
  59. package/dist/client/assets/channel-CqmujVSi.js +0 -1
  60. package/dist/client/assets/classDiagram-6PBFFD2Q-DLXkYur6.js +0 -1
  61. package/dist/client/assets/classDiagram-v2-HSJHXN6E-DLXkYur6.js +0 -1
  62. package/dist/client/assets/clone-KonJWRw9.js +0 -1
  63. package/dist/client/assets/index-CsmxZ8UQ.js +0 -453
  64. package/dist/client/assets/index-DC8tBAf_.css +0 -1
  65. package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-DhNebJyd.js +0 -1
@@ -6,12 +6,12 @@ import path2 from 'path';
6
6
  function definePlugin(definition) {
7
7
  return definition;
8
8
  }
9
- function registerRouter(ctx, path4, handler) {
10
- ctx.registerRouter(normalizePath(path4), handler);
9
+ function registerRouter(ctx, path5, handler) {
10
+ ctx.registerRouter(normalizePath(path5), handler);
11
11
  }
12
- function normalizePath(path4) {
13
- if (path4.length === 0 || path4 === "/") return "/";
14
- return path4;
12
+ function normalizePath(path5) {
13
+ if (path5.length === 0 || path5 === "/") return "/";
14
+ return path5;
15
15
  }
16
16
  var FileReadError = class extends Error {
17
17
  code;
@@ -301,9 +301,142 @@ function isWithinRoot2(resolved, root) {
301
301
  return resolved === root || resolved.startsWith(normalizedRoot);
302
302
  }
303
303
  var DEBOUNCE_MS = 200;
304
+ var LINUX_DIRECTORY_WATCH_CAP = 1024;
304
305
  var watcherRegistry = createWatcherRegistry();
305
- function createWatcherRegistry(watcherFactory = (p, opts, cb) => fs.watch(p, opts, (ev, fn) => cb(ev, typeof fn === "string" ? fn : null)), debounceMs = DEBOUNCE_MS) {
306
+ function createWatcherRegistry(watcherFactory = (p, opts, cb) => fs.watch(p, opts, (ev, fn) => cb(ev, typeof fn === "string" ? fn : null)), debounceMs = DEBOUNCE_MS, platform = process.platform) {
306
307
  const entries = /* @__PURE__ */ new Map();
308
+ function scheduleChange(entry, relDir) {
309
+ const pending = entry.debounceTimers.get(relDir);
310
+ if (pending) clearTimeout(pending);
311
+ entry.debounceTimers.set(
312
+ relDir,
313
+ setTimeout(() => {
314
+ entry.debounceTimers.delete(relDir);
315
+ for (const sub of entry.subscribers) sub(relDir);
316
+ }, debounceMs)
317
+ );
318
+ }
319
+ function closeEntry(entry) {
320
+ for (const timer of entry.debounceTimers.values()) clearTimeout(timer);
321
+ entry.debounceTimers.clear();
322
+ for (const watcher of entry.watchers.values()) {
323
+ try {
324
+ watcher.close();
325
+ } catch {
326
+ }
327
+ }
328
+ entry.watchers.clear();
329
+ entry.identityChecks.clear();
330
+ entry.pendingDirectories.clear();
331
+ }
332
+ async function rearmPendingDirectories(theaterId, entry) {
333
+ entry.rearmRequested = true;
334
+ if (entry.rearmRunning) return;
335
+ entry.rearmRunning = true;
336
+ try {
337
+ while (entry.rearmRequested) {
338
+ entry.rearmRequested = false;
339
+ for (const relativePath of entry.pendingDirectories) {
340
+ const target = await resolveTrackedDirectory(entry.theaterPath, relativePath);
341
+ if (!target) continue;
342
+ if (entries.get(theaterId) !== entry || !entry.pendingDirectories.has(relativePath) || entry.watchers.has(relativePath) || entry.watchers.size >= LINUX_DIRECTORY_WATCH_CAP) continue;
343
+ try {
344
+ startWatcher(theaterId, entry, relativePath, target.path, target.identity);
345
+ entry.pendingDirectories.delete(relativePath);
346
+ scheduleChange(entry, relativePath);
347
+ } catch {
348
+ for (const notify of entry.stateSubscribers) notify("degraded");
349
+ }
350
+ }
351
+ }
352
+ } finally {
353
+ entry.rearmRunning = false;
354
+ }
355
+ }
356
+ function invalidateWatcherTree(entry, watchKey) {
357
+ const descendantPrefix = watchKey + path2.sep;
358
+ for (const [candidateKey, candidateWatcher] of entry.watchers) {
359
+ if (candidateKey !== watchKey && !candidateKey.startsWith(descendantPrefix)) continue;
360
+ entry.watchers.delete(candidateKey);
361
+ entry.identityChecks.delete(candidateKey);
362
+ entry.pendingDirectories.add(candidateKey);
363
+ try {
364
+ candidateWatcher.close();
365
+ } catch {
366
+ }
367
+ }
368
+ }
369
+ function startWatcher(theaterId, entry, watchKey, watchPath, watchIdentity) {
370
+ let identityCheckRunning = false;
371
+ let identityCheckRequested = false;
372
+ let activeWatcher = null;
373
+ const lexicalWatchPath = path2.resolve(entry.theaterPath, watchKey);
374
+ const checkDirectoryIdentity = async (expectedWatcher) => {
375
+ identityCheckRequested = true;
376
+ if (identityCheckRunning || !watchIdentity) return;
377
+ identityCheckRunning = true;
378
+ try {
379
+ while (identityCheckRequested) {
380
+ identityCheckRequested = false;
381
+ let replaced = false;
382
+ try {
383
+ const stats = await fs.promises.stat(lexicalWatchPath);
384
+ replaced = stats.dev !== watchIdentity.device || stats.ino !== watchIdentity.inode;
385
+ } catch (error) {
386
+ replaced = error.code === "ENOENT";
387
+ }
388
+ if (entry.watchers.get(watchKey) !== expectedWatcher) return;
389
+ if (!replaced) continue;
390
+ invalidateWatcherTree(entry, watchKey);
391
+ await rearmPendingDirectories(theaterId, entry);
392
+ return;
393
+ }
394
+ } finally {
395
+ identityCheckRunning = false;
396
+ }
397
+ };
398
+ const watcher = watcherFactory(watchPath, { recursive: entry.recursive }, (event, filename) => {
399
+ const currentWatcher = activeWatcher;
400
+ if (!currentWatcher || entry.watchers.get(watchKey) !== currentWatcher) return;
401
+ scheduleChange(entry, entry.recursive ? computeRelDir(filename) : watchKey);
402
+ if (!entry.recursive && event === "rename") {
403
+ if (watchKey !== "" && watchIdentity) void checkDirectoryIdentity(currentWatcher);
404
+ if (filename) {
405
+ const changedChildKey = path2.join(watchKey, filename);
406
+ entry.identityChecks.get(changedChildKey)?.();
407
+ }
408
+ }
409
+ if (!entry.recursive && entry.pendingDirectories.size > 0) {
410
+ void rearmPendingDirectories(theaterId, entry);
411
+ }
412
+ });
413
+ activeWatcher = watcher;
414
+ entry.watchers.set(watchKey, watcher);
415
+ if (watchIdentity) {
416
+ entry.identityChecks.set(watchKey, () => {
417
+ const currentWatcher = activeWatcher;
418
+ if (currentWatcher) void checkDirectoryIdentity(currentWatcher);
419
+ });
420
+ }
421
+ watcher.on("error", () => {
422
+ if (entry.watchers.get(watchKey) !== watcher) return;
423
+ try {
424
+ watcher.close();
425
+ } catch {
426
+ }
427
+ entry.watchers.delete(watchKey);
428
+ entry.identityChecks.delete(watchKey);
429
+ if (watchKey === "") {
430
+ closeEntry(entry);
431
+ if (entries.get(theaterId) === entry) entries.delete(theaterId);
432
+ } else {
433
+ const pending = entry.debounceTimers.get(watchKey);
434
+ if (pending) clearTimeout(pending);
435
+ entry.debounceTimers.delete(watchKey);
436
+ }
437
+ for (const notify of entry.stateSubscribers) notify("degraded");
438
+ });
439
+ }
307
440
  function makeUnsubscribe(theaterId, onChange, onState, entry) {
308
441
  let called = false;
309
442
  return () => {
@@ -313,8 +446,7 @@ function createWatcherRegistry(watcherFactory = (p, opts, cb) => fs.watch(p, opt
313
446
  entry.stateSubscribers.delete(onState);
314
447
  entry.subscriberCount--;
315
448
  if (entry.subscriberCount <= 0) {
316
- for (const t of entry.debounceTimers.values()) clearTimeout(t);
317
- entry.watcher.close();
449
+ closeEntry(entry);
318
450
  if (entries.get(theaterId) === entry) entries.delete(theaterId);
319
451
  }
320
452
  };
@@ -328,46 +460,74 @@ function createWatcherRegistry(watcherFactory = (p, opts, cb) => fs.watch(p, opt
328
460
  onState("watching");
329
461
  return makeUnsubscribe(theaterId, onChange, onState, existing);
330
462
  }
331
- const debounceTimers = /* @__PURE__ */ new Map();
332
- const subscribers = /* @__PURE__ */ new Set();
333
- const stateSubscribers = /* @__PURE__ */ new Set();
334
- let watcher;
463
+ const entry = {
464
+ // Linux의 recursive fs.watch fallback은 구독 전체 트리를 동기 순회한다.
465
+ // 대신 root와 실제로 조회된 디렉터리만 개별 비재귀 watcher로 추적한다.
466
+ theaterPath,
467
+ recursive: platform !== "linux",
468
+ watchers: /* @__PURE__ */ new Map(),
469
+ identityChecks: /* @__PURE__ */ new Map(),
470
+ pendingDirectories: /* @__PURE__ */ new Set(),
471
+ subscribers: /* @__PURE__ */ new Set([onChange]),
472
+ stateSubscribers: /* @__PURE__ */ new Set([onState]),
473
+ subscriberCount: 1,
474
+ debounceTimers: /* @__PURE__ */ new Map(),
475
+ rearmRunning: false,
476
+ rearmRequested: false
477
+ };
335
478
  try {
336
- watcher = watcherFactory(theaterPath, { recursive: true }, (_, filename) => {
337
- const relDir = computeRelDir(filename);
338
- const pending = debounceTimers.get(relDir);
339
- if (pending) clearTimeout(pending);
340
- debounceTimers.set(
341
- relDir,
342
- setTimeout(() => {
343
- debounceTimers.delete(relDir);
344
- for (const sub of subscribers) sub(relDir);
345
- }, debounceMs)
346
- );
347
- });
479
+ startWatcher(theaterId, entry, "", theaterPath);
348
480
  } catch {
349
481
  onState("degraded");
350
482
  return () => {
351
483
  };
352
484
  }
353
- const entry = { watcher, subscribers, stateSubscribers, subscriberCount: 1, debounceTimers };
354
- watcher.on("error", () => {
355
- for (const t of debounceTimers.values()) clearTimeout(t);
356
- debounceTimers.clear();
357
- try {
358
- watcher.close();
359
- } catch {
360
- }
361
- if (entries.get(theaterId) === entry) entries.delete(theaterId);
362
- for (const notify of stateSubscribers) notify("degraded");
363
- });
364
485
  entries.set(theaterId, entry);
365
- subscribers.add(onChange);
366
- stateSubscribers.add(onState);
367
486
  onState("watching");
368
487
  return makeUnsubscribe(theaterId, onChange, onState, entry);
369
488
  }
370
- return { subscribe };
489
+ async function trackDirectory(theaterId, theaterPath, relativePath) {
490
+ const entry = entries.get(theaterId);
491
+ if (!entry || entry.recursive || relativePath === "" || entry.watchers.has(relativePath)) return;
492
+ if (entry.watchers.size >= LINUX_DIRECTORY_WATCH_CAP) {
493
+ for (const notify of entry.stateSubscribers) notify("degraded");
494
+ return;
495
+ }
496
+ const target = await resolveTrackedDirectory(theaterPath, relativePath);
497
+ if (!target) return;
498
+ if (entries.get(theaterId) !== entry || entry.watchers.has(relativePath) || entry.watchers.size >= LINUX_DIRECTORY_WATCH_CAP) return;
499
+ try {
500
+ startWatcher(theaterId, entry, relativePath, target.path, target.identity);
501
+ entry.pendingDirectories.delete(relativePath);
502
+ } catch {
503
+ for (const notify of entry.stateSubscribers) notify("degraded");
504
+ }
505
+ }
506
+ return { subscribe, trackDirectory };
507
+ }
508
+ async function resolveTrackedDirectory(theaterPath, relativePath) {
509
+ const resolvedRoot = path2.resolve(theaterPath);
510
+ const targetPath = path2.resolve(resolvedRoot, relativePath);
511
+ if (!isWithinRoot3(targetPath, resolvedRoot)) return null;
512
+ try {
513
+ const [realRoot, realTarget] = await Promise.all([
514
+ fs.promises.realpath(theaterPath),
515
+ fs.promises.realpath(targetPath)
516
+ ]);
517
+ if (!isWithinRoot3(realTarget, realRoot)) return null;
518
+ const stats = await fs.promises.stat(realTarget);
519
+ if (!stats.isDirectory()) return null;
520
+ return {
521
+ path: realTarget,
522
+ identity: { device: stats.dev, inode: stats.ino }
523
+ };
524
+ } catch {
525
+ return null;
526
+ }
527
+ }
528
+ function isWithinRoot3(candidate, root) {
529
+ const normalizedRoot = root.endsWith(path2.sep) ? root : root + path2.sep;
530
+ return candidate === root || candidate.startsWith(normalizedRoot);
371
531
  }
372
532
  function computeRelDir(filename) {
373
533
  if (!filename) return "";
@@ -415,6 +575,7 @@ async function handleFilesList(req, res, ctx) {
415
575
  }
416
576
  try {
417
577
  const result = await listTheaterContents(theaterPath, relPath);
578
+ await watcherRegistry.trackDirectory(rawTheaterId, theaterPath, relPath);
418
579
  ctx.host.http.writeJson(res, 200, result);
419
580
  } catch (error) {
420
581
  if (error instanceof FolderBrowserError) {