@drewpayment/mink 0.5.0 → 0.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 (95) hide show
  1. package/README.md +21 -0
  2. package/dashboard/out/404.html +1 -0
  3. package/dashboard/out/_next/static/Dw8C--0lGz5BIGsnG-e5H/_buildManifest.js +1 -0
  4. package/dashboard/out/_next/static/Dw8C--0lGz5BIGsnG-e5H/_ssgManifest.js +1 -0
  5. package/dashboard/out/_next/static/chunks/189-fe789442321eb5eb.js +1 -0
  6. package/dashboard/out/_next/static/chunks/255-6b79f309a27fb98b.js +1 -0
  7. package/dashboard/out/_next/static/chunks/4bd1b696-c023c6e3521b1417.js +1 -0
  8. package/dashboard/out/_next/static/chunks/738-11c31dcbdbb98d77.js +1 -0
  9. package/dashboard/out/_next/static/chunks/926-6421b9e9b03abc7b.js +1 -0
  10. package/dashboard/out/_next/static/chunks/app/(panels)/action-log/page-1f507b433af52d16.js +1 -0
  11. package/dashboard/out/_next/static/chunks/app/(panels)/activity/page-096a97ba539d5323.js +1 -0
  12. package/dashboard/out/_next/static/chunks/app/(panels)/bugs/page-449d31c133432458.js +1 -0
  13. package/dashboard/out/_next/static/chunks/app/(panels)/capture/page-c6617aa0a8a7333e.js +1 -0
  14. package/dashboard/out/_next/static/chunks/app/(panels)/config/page-aa0a0623b3fdd0d8.js +1 -0
  15. package/dashboard/out/_next/static/chunks/app/(panels)/daemon/page-7cd3fac2f5d87a0d.js +1 -0
  16. package/dashboard/out/_next/static/chunks/app/(panels)/design/page-5304675c96b6793b.js +1 -0
  17. package/dashboard/out/_next/static/chunks/app/(panels)/discord/page-9940dde80ba2a69e.js +1 -0
  18. package/dashboard/out/_next/static/chunks/app/(panels)/file-index/page-ecd8a753614e981e.js +1 -0
  19. package/dashboard/out/_next/static/chunks/app/(panels)/insights/page-7909d8beb8d8ef7a.js +1 -0
  20. package/dashboard/out/_next/static/chunks/app/(panels)/learning/page-b766adc79099adb4.js +1 -0
  21. package/dashboard/out/_next/static/chunks/app/(panels)/overview/page-7a9e86dcde67d6a9.js +1 -0
  22. package/dashboard/out/_next/static/chunks/app/(panels)/scheduler/page-a88f93204c9742a1.js +1 -0
  23. package/dashboard/out/_next/static/chunks/app/(panels)/sync/page-8a9ad4c36aa6cb65.js +1 -0
  24. package/dashboard/out/_next/static/chunks/app/(panels)/tokens/page-8dac7d50d4db2756.js +1 -0
  25. package/dashboard/out/_next/static/chunks/app/(panels)/waste/page-bcf56144faf7d133.js +1 -0
  26. package/dashboard/out/_next/static/chunks/app/(panels)/wiki/page-a32fdbd0bf58b30b.js +1 -0
  27. package/dashboard/out/_next/static/chunks/app/_not-found/page-dc2312ec30d73c4e.js +1 -0
  28. package/dashboard/out/_next/static/chunks/app/layout-782cd26e0ccc4514.js +1 -0
  29. package/dashboard/out/_next/static/chunks/app/page-6aca8457abc5d313.js +1 -0
  30. package/dashboard/out/_next/static/chunks/framework-050c1f32293f7182.js +1 -0
  31. package/dashboard/out/_next/static/chunks/main-app-c2dc0acf542ec1c6.js +1 -0
  32. package/dashboard/out/_next/static/chunks/main-ed79d05490604b83.js +1 -0
  33. package/dashboard/out/_next/static/chunks/pages/_app-7d307437aca18ad4.js +1 -0
  34. package/dashboard/out/_next/static/chunks/pages/_error-cb2a52f75f2162e2.js +1 -0
  35. package/dashboard/out/_next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
  36. package/dashboard/out/_next/static/chunks/webpack-4e3139a490df1cfe.js +1 -0
  37. package/dashboard/out/_next/static/css/5e43917ea49c5b3e.css +1 -0
  38. package/dashboard/out/_next/static/media/0aa834ed78bf6d07-s.woff2 +0 -0
  39. package/dashboard/out/_next/static/media/19cfc7226ec3afaa-s.woff2 +0 -0
  40. package/dashboard/out/_next/static/media/21350d82a1f187e9-s.woff2 +0 -0
  41. package/dashboard/out/_next/static/media/67957d42bae0796d-s.woff2 +0 -0
  42. package/dashboard/out/_next/static/media/886030b0b59bc5a7-s.woff2 +0 -0
  43. package/dashboard/out/_next/static/media/8e9860b6e62d6359-s.woff2 +0 -0
  44. package/dashboard/out/_next/static/media/939c4f875ee75fbb-s.woff2 +0 -0
  45. package/dashboard/out/_next/static/media/ba9851c3c22cd980-s.woff2 +0 -0
  46. package/dashboard/out/_next/static/media/bb3ef058b751a6ad-s.p.woff2 +0 -0
  47. package/dashboard/out/_next/static/media/c5fe6dc8356a8c31-s.woff2 +0 -0
  48. package/dashboard/out/_next/static/media/df0a9ae256c0569c-s.woff2 +0 -0
  49. package/dashboard/out/_next/static/media/e4af272ccee01ff0-s.p.woff2 +0 -0
  50. package/dashboard/out/_next/static/media/f911b923c6adde36-s.woff2 +0 -0
  51. package/dashboard/out/action-log.html +1 -0
  52. package/dashboard/out/action-log.txt +24 -0
  53. package/dashboard/out/activity.html +1 -0
  54. package/dashboard/out/activity.txt +24 -0
  55. package/dashboard/out/bugs.html +1 -0
  56. package/dashboard/out/bugs.txt +24 -0
  57. package/dashboard/out/capture.html +1 -0
  58. package/dashboard/out/capture.txt +24 -0
  59. package/dashboard/out/config.html +1 -0
  60. package/dashboard/out/config.txt +24 -0
  61. package/dashboard/out/daemon.html +1 -0
  62. package/dashboard/out/daemon.txt +24 -0
  63. package/dashboard/out/design.html +1 -0
  64. package/dashboard/out/design.txt +24 -0
  65. package/dashboard/out/discord.html +1 -0
  66. package/dashboard/out/discord.txt +24 -0
  67. package/dashboard/out/file-index.html +1 -0
  68. package/dashboard/out/file-index.txt +24 -0
  69. package/dashboard/out/index.html +1 -0
  70. package/dashboard/out/index.txt +24 -0
  71. package/dashboard/out/insights.html +1 -0
  72. package/dashboard/out/insights.txt +24 -0
  73. package/dashboard/out/learning.html +1 -0
  74. package/dashboard/out/learning.txt +24 -0
  75. package/dashboard/out/overview.html +1 -0
  76. package/dashboard/out/overview.txt +24 -0
  77. package/dashboard/out/scheduler.html +1 -0
  78. package/dashboard/out/scheduler.txt +24 -0
  79. package/dashboard/out/sync.html +1 -0
  80. package/dashboard/out/sync.txt +24 -0
  81. package/dashboard/out/tokens.html +1 -0
  82. package/dashboard/out/tokens.txt +24 -0
  83. package/dashboard/out/waste.html +1 -0
  84. package/dashboard/out/waste.txt +24 -0
  85. package/dashboard/out/wiki.html +1 -0
  86. package/dashboard/out/wiki.txt +24 -0
  87. package/dist/cli.js +1695 -892
  88. package/package.json +1 -1
  89. package/src/core/daemon.ts +5 -3
  90. package/src/core/dashboard-api.ts +764 -1
  91. package/src/core/dashboard-server.ts +270 -0
  92. package/src/core/runtime.ts +7 -4
  93. package/src/core/vault.ts +4 -4
  94. package/src/types/config.ts +9 -1
  95. package/src/types/dashboard.ts +84 -1
@@ -14,6 +14,25 @@ import {
14
14
  triggerTask,
15
15
  triggerDeadLetterRetry,
16
16
  triggerRescan,
17
+ triggerDaemonStart,
18
+ triggerDaemonStop,
19
+ triggerDaemonRestart,
20
+ loadConfigPanel,
21
+ triggerConfigSet,
22
+ triggerConfigReset,
23
+ loadSyncPanel,
24
+ triggerSyncPull,
25
+ triggerSyncPush,
26
+ triggerSyncDisconnect,
27
+ loadChannelPanel,
28
+ triggerChannelStart,
29
+ triggerChannelStop,
30
+ triggerChannelRestart,
31
+ loadWikiPanel,
32
+ loadWikiNote,
33
+ triggerCreateNote,
34
+ triggerAppendDaily,
35
+ triggerIngestFile,
17
36
  } from "./dashboard-api";
18
37
  import { listRegisteredProjects, getProjectMeta } from "./project-registry";
19
38
  import { generateProjectId } from "./project-id";
@@ -410,6 +429,82 @@ export async function startDashboardServer(
410
429
  return jsonResponse(getProjectsList(cwd, activeCwd));
411
430
  }
412
431
 
432
+ // GET /api/config — global config (no project scoping)
433
+ if (pathname === "/api/config") {
434
+ try {
435
+ return jsonResponse(loadConfigPanel());
436
+ } catch (err) {
437
+ return jsonResponse(
438
+ { error: err instanceof Error ? err.message : String(err) },
439
+ 500,
440
+ );
441
+ }
442
+ }
443
+
444
+ // GET /api/sync — global sync status (no project scoping)
445
+ if (pathname === "/api/sync") {
446
+ try {
447
+ return jsonResponse(loadSyncPanel());
448
+ } catch (err) {
449
+ return jsonResponse(
450
+ { error: err instanceof Error ? err.message : String(err) },
451
+ 500,
452
+ );
453
+ }
454
+ }
455
+
456
+ // GET /api/channel — global channel status + logs (no project scoping)
457
+ if (pathname === "/api/channel") {
458
+ try {
459
+ return jsonResponse(loadChannelPanel());
460
+ } catch (err) {
461
+ return jsonResponse(
462
+ { error: err instanceof Error ? err.message : String(err) },
463
+ 500,
464
+ );
465
+ }
466
+ }
467
+
468
+ // GET /api/wiki — global wiki vault summary (no project scoping)
469
+ if (pathname === "/api/wiki") {
470
+ try {
471
+ const limitRaw = url.searchParams.get("limit");
472
+ const categoryRaw = url.searchParams.get("category");
473
+ const limit = limitRaw ? Number(limitRaw) : undefined;
474
+ return jsonResponse(
475
+ loadWikiPanel({
476
+ limit: Number.isFinite(limit) ? limit : undefined,
477
+ category: (categoryRaw as "all" | undefined) ?? undefined,
478
+ }),
479
+ );
480
+ } catch (err) {
481
+ return jsonResponse(
482
+ { error: err instanceof Error ? err.message : String(err) },
483
+ 500,
484
+ );
485
+ }
486
+ }
487
+
488
+ // GET /api/wiki/note — single note body with backlinks
489
+ if (pathname === "/api/wiki/note") {
490
+ try {
491
+ const notePath = url.searchParams.get("path");
492
+ if (!notePath) {
493
+ return jsonResponse({ error: "Missing path parameter" }, 400);
494
+ }
495
+ const note = loadWikiNote(notePath);
496
+ if (!note) {
497
+ return jsonResponse({ error: "Note not found" }, 404);
498
+ }
499
+ return jsonResponse(note);
500
+ } catch (err) {
501
+ return jsonResponse(
502
+ { error: err instanceof Error ? err.message : String(err) },
503
+ 500,
504
+ );
505
+ }
506
+ }
507
+
413
508
  // Resolve project cwd from ?project=<id> query param
414
509
  const resolvedCwd = resolveProjectCwd(url, activeCwd);
415
510
  if (resolvedCwd === null) {
@@ -492,6 +587,181 @@ export async function startDashboardServer(
492
587
  }
493
588
  }
494
589
 
590
+ // POST /api/config/set — write a config value
591
+ if (pathname === "/api/config/set") {
592
+ try {
593
+ const body = (await req.json()) as {
594
+ key?: string;
595
+ value?: string;
596
+ };
597
+ if (!body.key || typeof body.value !== "string") {
598
+ return jsonResponse(
599
+ { success: false, error: "Missing key or value" },
600
+ 400,
601
+ );
602
+ }
603
+ const result = await triggerConfigSet(body.key, body.value);
604
+ if (result.success) {
605
+ sseManager.broadcast({
606
+ fileId: "config-changed" as StateFileId,
607
+ timestamp: new Date().toISOString(),
608
+ });
609
+ }
610
+ return jsonResponse(result, result.success ? 200 : 500);
611
+ } catch (err) {
612
+ return jsonResponse(
613
+ { success: false, error: err instanceof Error ? err.message : String(err) },
614
+ 500,
615
+ );
616
+ }
617
+ }
618
+
619
+ // POST /api/config/reset — clear one key (or all)
620
+ if (pathname === "/api/config/reset") {
621
+ try {
622
+ const body = (await req.json()) as { key?: string; all?: boolean };
623
+ const result = await triggerConfigReset(body.key, body.all);
624
+ if (result.success) {
625
+ sseManager.broadcast({
626
+ fileId: "config-changed" as StateFileId,
627
+ timestamp: new Date().toISOString(),
628
+ });
629
+ }
630
+ return jsonResponse(result, result.success ? 200 : 500);
631
+ } catch (err) {
632
+ return jsonResponse(
633
+ { success: false, error: err instanceof Error ? err.message : String(err) },
634
+ 500,
635
+ );
636
+ }
637
+ }
638
+
639
+ // Wiki writes — global (single user-level vault).
640
+ if (
641
+ pathname === "/api/wiki/notes" ||
642
+ pathname === "/api/wiki/daily" ||
643
+ pathname === "/api/wiki/ingest"
644
+ ) {
645
+ const dedupKey = req.headers.get("X-Mink-Dedup-Key") ?? undefined;
646
+ try {
647
+ const body = (await req.json()) as Record<string, unknown>;
648
+
649
+ let action: Promise<
650
+ { success: boolean; error?: string; filePath?: string }
651
+ >;
652
+
653
+ if (pathname === "/api/wiki/notes") {
654
+ const mode = body.mode === "structured" ? "structured" : "quick";
655
+ action = triggerCreateNote({
656
+ mode,
657
+ title: typeof body.title === "string" ? body.title : undefined,
658
+ category: typeof body.category === "string" ? body.category : undefined,
659
+ body: typeof body.body === "string" ? body.body : "",
660
+ tags: Array.isArray(body.tags) ? (body.tags as string[]) : undefined,
661
+ dedupKey,
662
+ });
663
+ } else if (pathname === "/api/wiki/daily") {
664
+ action = triggerAppendDaily(
665
+ typeof body.content === "string" ? body.content : "",
666
+ dedupKey,
667
+ );
668
+ } else {
669
+ action = triggerIngestFile(
670
+ typeof body.sourcePath === "string" ? body.sourcePath : "",
671
+ typeof body.category === "string" ? body.category : "inbox",
672
+ Array.isArray(body.tags) ? (body.tags as string[]) : undefined,
673
+ dedupKey,
674
+ );
675
+ }
676
+
677
+ return action.then((result) => {
678
+ if (result.success) {
679
+ sseManager.broadcast({
680
+ fileId: "vault-index" as StateFileId,
681
+ timestamp: new Date().toISOString(),
682
+ });
683
+ }
684
+ return jsonResponse(result, result.success ? 200 : 500);
685
+ });
686
+ } catch (err) {
687
+ return jsonResponse(
688
+ { success: false, error: err instanceof Error ? err.message : String(err) },
689
+ 500,
690
+ );
691
+ }
692
+ }
693
+
694
+ // Channel controls — global (screen session is per-vault, not per-project).
695
+ if (
696
+ pathname === "/api/channel/start" ||
697
+ pathname === "/api/channel/stop" ||
698
+ pathname === "/api/channel/restart"
699
+ ) {
700
+ const action =
701
+ pathname === "/api/channel/start"
702
+ ? triggerChannelStart()
703
+ : pathname === "/api/channel/stop"
704
+ ? triggerChannelStop()
705
+ : triggerChannelRestart();
706
+ return action.then((result) => {
707
+ if (result.success) {
708
+ sseManager.broadcast({
709
+ fileId: "channel-status" as StateFileId,
710
+ timestamp: new Date().toISOString(),
711
+ });
712
+ }
713
+ return jsonResponse(result, result.success ? 200 : 500);
714
+ });
715
+ }
716
+
717
+ // Sync controls — global (operate on ~/.mink/.git, not a project).
718
+ if (
719
+ pathname === "/api/sync/pull" ||
720
+ pathname === "/api/sync/push" ||
721
+ pathname === "/api/sync/disconnect"
722
+ ) {
723
+ const action =
724
+ pathname === "/api/sync/pull"
725
+ ? triggerSyncPull()
726
+ : pathname === "/api/sync/push"
727
+ ? triggerSyncPush()
728
+ : triggerSyncDisconnect();
729
+ return action.then((result) => {
730
+ if (result.success) {
731
+ sseManager.broadcast({
732
+ fileId: "sync-status" as StateFileId,
733
+ timestamp: new Date().toISOString(),
734
+ });
735
+ }
736
+ return jsonResponse(result, result.success ? 200 : 500);
737
+ });
738
+ }
739
+
740
+ // Daemon controls — global (operate on ~/.mink/ PID file, not a
741
+ // project state directory). Use activeCwd so the spawned daemon
742
+ // inherits the currently-active project.
743
+ if (
744
+ pathname === "/api/daemon/start" ||
745
+ pathname === "/api/daemon/stop" ||
746
+ pathname === "/api/daemon/restart"
747
+ ) {
748
+ const action =
749
+ pathname === "/api/daemon/start"
750
+ ? triggerDaemonStart(activeCwd)
751
+ : pathname === "/api/daemon/stop"
752
+ ? triggerDaemonStop()
753
+ : triggerDaemonRestart(activeCwd);
754
+ return action.then((result) => {
755
+ if (result.success) {
756
+ sseManager.broadcast({
757
+ fileId: "daemon-status" as StateFileId,
758
+ timestamp: new Date().toISOString(),
759
+ });
760
+ }
761
+ return jsonResponse(result, result.success ? 200 : 500);
762
+ });
763
+ }
764
+
495
765
  // Resolve project cwd for POST actions
496
766
  const resolvedCwd = resolveProjectCwd(url, activeCwd);
497
767
  if (resolvedCwd === null) {
@@ -46,15 +46,18 @@ export function runtimeFile(path: string): RuntimeFile {
46
46
 
47
47
  // ── Spawn helper ──────────────────────────────────────────────────────────
48
48
 
49
+ export type SpawnStdio = "ignore" | "pipe" | number;
50
+
49
51
  export interface SpawnOptions {
50
52
  cwd?: string;
51
53
  env?: Record<string, string | undefined>;
52
- stdout?: "ignore" | "pipe";
53
- stderr?: "ignore" | "pipe";
54
+ stdout?: SpawnStdio;
55
+ stderr?: SpawnStdio;
54
56
  stdin?: "ignore";
55
57
  }
56
58
 
57
59
  export interface SpawnedProcess {
60
+ pid: number;
58
61
  unref(): void;
59
62
  }
60
63
 
@@ -74,7 +77,7 @@ export function runtimeSpawn(
74
77
  stderr: opts.stderr ?? "ignore",
75
78
  stdin: opts.stdin ?? "ignore",
76
79
  });
77
- return { unref: () => proc.unref() };
80
+ return { pid: proc.pid, unref: () => proc.unref() };
78
81
  }
79
82
 
80
83
  const [bin, ...args] = cmd;
@@ -89,7 +92,7 @@ export function runtimeSpawn(
89
92
  detached: true,
90
93
  });
91
94
  proc.unref();
92
- return { unref: () => {} };
95
+ return { pid: proc.pid ?? -1, unref: () => {} };
93
96
  }
94
97
 
95
98
  // ── HTTP Server ───────────────────────────────────────────────────────────
package/src/core/vault.ts CHANGED
@@ -11,10 +11,10 @@ const DEFAULT_VAULT_PATH = join(homedir(), ".mink", "wiki");
11
11
  export function resolveVaultPath(): string {
12
12
  const resolved = resolveConfigValue("wiki.path");
13
13
  const raw = resolved.value;
14
- if (raw.startsWith("~/")) {
15
- return join(homedir(), raw.slice(2));
16
- }
17
- return raw;
14
+ // Normalize via path.resolve so downstream `slice(root.length + 1)` callers
15
+ // aren't fooled by a trailing slash (e.g. the default "~/.mink/wiki/").
16
+ const expanded = raw.startsWith("~/") ? join(homedir(), raw.slice(2)) : raw;
17
+ return resolve(expanded);
18
18
  }
19
19
 
20
20
  export function vaultRoot(): string {
@@ -11,6 +11,7 @@ export interface GlobalConfig {
11
11
  "sync.last-pull"?: string;
12
12
  "channel.discord.bot-token"?: string;
13
13
  "channel.discord.enabled"?: string;
14
+ "channel.discord.allowlist"?: string;
14
15
  "channel.default-platform"?: string;
15
16
  "channel.skip-permissions"?: string;
16
17
  }
@@ -42,7 +43,7 @@ export interface DeviceRegistry {
42
43
  export const CONFIG_KEYS: ConfigKeyMeta[] = [
43
44
  {
44
45
  key: "wiki.path",
45
- default: "~/.mink/wiki/",
46
+ default: "~/.mink/wiki",
46
47
  envVar: "MINK_WIKI_PATH",
47
48
  description: "Wiki vault location",
48
49
  scope: "local",
@@ -124,6 +125,13 @@ export const CONFIG_KEYS: ConfigKeyMeta[] = [
124
125
  description: "Auto-start Discord channel when daemon starts",
125
126
  scope: "local",
126
127
  },
128
+ {
129
+ key: "channel.discord.allowlist",
130
+ default: "",
131
+ envVar: "MINK_CHANNEL_DISCORD_ALLOWLIST",
132
+ description: "Comma-separated list of Discord user IDs permitted to DM the bot",
133
+ scope: "local",
134
+ },
127
135
  {
128
136
  key: "channel.default-platform",
129
137
  default: "discord",
@@ -5,6 +5,7 @@ import type { BugEntry } from "./bug-memory";
5
5
  import type { LearningMemory } from "./learning-memory";
6
6
  import type { ParsedSession } from "./action-log";
7
7
  import type { TaskDefinition, TaskRunRecord, DeadLetterEntry } from "./scheduler";
8
+ import type { VaultIndexEntry } from "./note";
8
9
 
9
10
  // ── State File Identifiers ─────────────────────────────────────────────────
10
11
 
@@ -18,7 +19,13 @@ export type StateFileId =
18
19
  | "session"
19
20
  | "project-meta"
20
21
  | "design-report"
21
- | "project-switched";
22
+ | "project-switched"
23
+ | "daemon-status"
24
+ | "config-changed"
25
+ | "sync-status"
26
+ | "channel-status"
27
+ | "channel-logs"
28
+ | "vault-index";
22
29
 
23
30
  // ── SSE Event ──────────────────────────────────────────────────────────────
24
31
 
@@ -102,3 +109,79 @@ export interface DesignImagePayload {
102
109
  export interface DesignPayload {
103
110
  images: DesignImagePayload[];
104
111
  }
112
+
113
+ export type ConfigValueSource = "default" | "shared" | "local" | "env";
114
+ export type ConfigValueType = "string" | "boolean" | "number";
115
+
116
+ export interface ConfigEntry {
117
+ key: string;
118
+ value: string;
119
+ source: ConfigValueSource;
120
+ type: ConfigValueType;
121
+ group: string;
122
+ scope: "shared" | "local";
123
+ description: string;
124
+ isSecret: boolean;
125
+ }
126
+
127
+ export interface ConfigPanelPayload {
128
+ entries: ConfigEntry[];
129
+ }
130
+
131
+ export interface SyncPendingChange {
132
+ op: "A" | "M" | "D" | "?";
133
+ file: string;
134
+ }
135
+
136
+ export interface SyncPanelPayload {
137
+ initialized: boolean;
138
+ enabled: boolean;
139
+ branch: string;
140
+ remote: string;
141
+ ahead: number;
142
+ behind: number;
143
+ lastPush: string;
144
+ lastPull: string;
145
+ pending: SyncPendingChange[];
146
+ }
147
+
148
+ export interface ChannelLogLine {
149
+ t: string;
150
+ m: string;
151
+ }
152
+
153
+ export interface ChannelPanelPayload {
154
+ status: "running" | "stopped";
155
+ platform: "discord" | "telegram" | null;
156
+ session: string;
157
+ startedAt: string;
158
+ uptimeSec: number;
159
+ autoStart: boolean;
160
+ tokenMasked: string;
161
+ allowlist: string[];
162
+ logs: ChannelLogLine[];
163
+ }
164
+
165
+ export interface WikiTreeNode {
166
+ name: string;
167
+ path: string;
168
+ count: number;
169
+ depth: number;
170
+ }
171
+
172
+ export interface WikiPanelPayload {
173
+ initialized: boolean;
174
+ vaultPath: string;
175
+ totalNotes: number;
176
+ inboxCount: number;
177
+ recent: VaultIndexEntry[];
178
+ tags: Array<[string, number]>;
179
+ tree: WikiTreeNode[];
180
+ }
181
+
182
+ export interface WikiNotePayload {
183
+ path: string;
184
+ frontmatter: Record<string, unknown>;
185
+ body: string;
186
+ backlinks: Array<{ path: string; title: string }>;
187
+ }