@knowsuchagency/fulcrum 2.7.1 → 2.8.2

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.
@@ -0,0 +1,27 @@
1
+ CREATE TABLE `memories` (
2
+ `id` text PRIMARY KEY NOT NULL,
3
+ `content` text NOT NULL,
4
+ `tags` text,
5
+ `created_at` text NOT NULL,
6
+ `updated_at` text NOT NULL
7
+ );--> statement-breakpoint
8
+ CREATE VIRTUAL TABLE `memories_fts` USING fts5(
9
+ content,
10
+ tags,
11
+ content=memories,
12
+ content_rowid=rowid
13
+ );--> statement-breakpoint
14
+ CREATE TRIGGER memories_ai AFTER INSERT ON `memories` BEGIN
15
+ INSERT INTO memories_fts(rowid, content, tags)
16
+ VALUES (new.rowid, new.content, new.tags);
17
+ END;--> statement-breakpoint
18
+ CREATE TRIGGER memories_ad AFTER DELETE ON `memories` BEGIN
19
+ INSERT INTO memories_fts(memories_fts, rowid, content, tags)
20
+ VALUES ('delete', old.rowid, old.content, old.tags);
21
+ END;--> statement-breakpoint
22
+ CREATE TRIGGER memories_au AFTER UPDATE ON `memories` BEGIN
23
+ INSERT INTO memories_fts(memories_fts, rowid, content, tags)
24
+ VALUES ('delete', old.rowid, old.content, old.tags);
25
+ INSERT INTO memories_fts(rowid, content, tags)
26
+ VALUES (new.rowid, new.content, new.tags);
27
+ END;
@@ -0,0 +1 @@
1
+ ALTER TABLE `memories` ADD `source` text;
@@ -365,6 +365,20 @@
365
365
  "when": 1770548000000,
366
366
  "tag": "0051_caldav_tables",
367
367
  "breakpoints": true
368
+ },
369
+ {
370
+ "idx": 52,
371
+ "version": "6",
372
+ "when": 1770634400000,
373
+ "tag": "0052_agent_memory",
374
+ "breakpoints": true
375
+ },
376
+ {
377
+ "idx": 53,
378
+ "version": "6",
379
+ "when": 1770720800000,
380
+ "tag": "0053_add_memory_source",
381
+ "breakpoints": true
368
382
  }
369
383
  ]
370
384
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knowsuchagency/fulcrum",
3
- "version": "2.7.1",
3
+ "version": "2.8.2",
4
4
  "description": "Harness Attention. Orchestrate Agents. Ship.",
5
5
  "license": "PolyForm-Perimeter-1.0.0",
6
6
  "repository": {
package/server/index.js CHANGED
@@ -4368,6 +4368,7 @@ __export(exports_schema, {
4368
4368
  projectAttachments: () => projectAttachments,
4369
4369
  messagingSessionMappings: () => messagingSessionMappings,
4370
4370
  messagingConnections: () => messagingConnections,
4371
+ memories: () => memories,
4371
4372
  emailAuthorizedThreads: () => emailAuthorizedThreads,
4372
4373
  deployments: () => deployments,
4373
4374
  chatSessions: () => chatSessions,
@@ -4380,7 +4381,7 @@ __export(exports_schema, {
4380
4381
  appServices: () => appServices,
4381
4382
  actionableEvents: () => actionableEvents
4382
4383
  });
4383
- var tasks, taskRelationships, taskDependencies, taskLinks, taskAttachments, projectLinks, projectAttachments, terminalTabs, terminals, terminalViewState, repositories, apps, appServices, deployments, tunnels, projects, projectRepositories, tags, taskTags, projectTags, chatSessions, chatMessages, artifacts, systemMetrics, messagingConnections, messagingSessionMappings, emailAuthorizedThreads, channelMessages, actionableEvents, sweepRuns, caldavCalendars, caldavEvents;
4384
+ var tasks, taskRelationships, taskDependencies, taskLinks, taskAttachments, projectLinks, projectAttachments, terminalTabs, terminals, terminalViewState, repositories, apps, appServices, deployments, tunnels, projects, projectRepositories, tags, taskTags, projectTags, chatSessions, chatMessages, artifacts, systemMetrics, messagingConnections, messagingSessionMappings, emailAuthorizedThreads, channelMessages, actionableEvents, sweepRuns, caldavCalendars, caldavEvents, memories;
4384
4385
  var init_schema = __esm(() => {
4385
4386
  init_sqlite_core();
4386
4387
  tasks = sqliteTable("tasks", {
@@ -4747,6 +4748,14 @@ var init_schema = __esm(() => {
4747
4748
  createdAt: text("created_at").notNull(),
4748
4749
  updatedAt: text("updated_at").notNull()
4749
4750
  });
4751
+ memories = sqliteTable("memories", {
4752
+ id: text("id").primaryKey(),
4753
+ content: text("content").notNull(),
4754
+ tags: text("tags"),
4755
+ source: text("source"),
4756
+ createdAt: text("created_at").notNull(),
4757
+ updatedAt: text("updated_at").notNull()
4758
+ });
4750
4759
  });
4751
4760
 
4752
4761
  // server/lib/settings/types.ts
@@ -70943,7 +70952,13 @@ function getDataModel() {
70943
70952
  - Calendars with display name, color, sync state
70944
70953
  - Events with summary, start/end times, location, all-day flag
70945
70954
  - Timezone-aware storage and display
70946
- - Used to give the assistant schedule awareness for planning`;
70955
+ - Used to give the assistant schedule awareness for planning
70956
+
70957
+ **Memories** - Persistent agent knowledge store
70958
+ - Content with optional tags for categorization
70959
+ - SQLite FTS5 full-text search (boolean operators, phrase matching, prefix queries)
70960
+ - Used to remember facts, preferences, decisions, and patterns across conversations
70961
+ - Browsable via Monitoring > Memory tab in the UI`;
70947
70962
  }
70948
70963
  function getMcpToolCapabilities() {
70949
70964
  return `## Available MCP Tools
@@ -71021,6 +71036,10 @@ You have access to Fulcrum's MCP tools. Use them proactively to help users.
71021
71036
  - \`get_assistant_stats\` - Get event counts and last sweep times
71022
71037
  - \`get_last_sweep\` - Check when last sweep ran
71023
71038
 
71039
+ **Memory Tools:**
71040
+ - \`memory_store\` - Store a piece of knowledge in persistent memory with optional tags
71041
+ - \`memory_search\` - Search memories using FTS5 full-text search (supports AND, OR, NOT, "phrases", prefix*)
71042
+
71024
71043
  **Utilities:**
71025
71044
  - \`list_tags\` - See all tags in use
71026
71045
  - \`get_task_dependency_graph\` - Visualize task dependencies
@@ -71205,6 +71224,7 @@ You can read and modify all Fulcrum settings using the settings MCP tools. Setti
71205
71224
  - \`assistant.model\` - Model tier: 'opus', 'sonnet', 'haiku'
71206
71225
  - \`assistant.customInstructions\` - Custom system prompt additions
71207
71226
  - \`assistant.documentsDir\` - Directory for assistant documents
71227
+ - \`assistant.observerModel\` - Model for observe-only messages (non-self WhatsApp, unauthorized emails), e.g., 'haiku' for cost savings
71208
71228
  - \`assistant.ritualsEnabled\` - Enable/disable daily rituals (morning/evening briefings)
71209
71229
  - \`assistant.morningRitual.time\` - Time for morning ritual (24h format, e.g., "09:00")
71210
71230
  - \`assistant.morningRitual.prompt\` - Custom prompt for morning ritual
@@ -71322,12 +71342,14 @@ Fulcrum is your digital concierge - a personal command center where you track ev
71322
71342
  - Deploying apps with Docker Compose
71323
71343
  - Sending notifications to Slack, Discord, Pushover
71324
71344
  - Calendar awareness via CalDAV sync (Google Calendar, etc.)
71345
+ - Persistent memory across conversations (store and search knowledge)
71325
71346
 
71326
71347
  **Key tools available:**
71327
71348
  - list_tasks, create_task, update_task, move_task
71328
71349
  - list_projects, create_project
71329
71350
  - execute_command (run any CLI command)
71330
71351
  - send_notification
71352
+ - memory_store, memory_search (persistent knowledge across sessions)
71331
71353
  - message (send to email/WhatsApp - concierge mode)
71332
71354
  - create_actionable_event, list_actionable_events (track decisions - concierge mode)
71333
71355
 
@@ -463771,7 +463793,8 @@ var ToolCategorySchema = exports_external.enum([
463771
463793
  "email",
463772
463794
  "messaging",
463773
463795
  "assistant",
463774
- "caldav"
463796
+ "caldav",
463797
+ "memory"
463775
463798
  ]);
463776
463799
  var AgentTypeSchema = exports_external.enum(["claude", "opencode"]);
463777
463800
 
@@ -464538,6 +464561,20 @@ var toolRegistry = [
464538
464561
  category: "caldav",
464539
464562
  keywords: ["calendar", "event", "delete", "remove", "cancel"],
464540
464563
  defer_loading: true
464564
+ },
464565
+ {
464566
+ name: "memory_store",
464567
+ description: "Store a piece of knowledge in persistent memory",
464568
+ category: "memory",
464569
+ keywords: ["memory", "store", "save", "remember", "knowledge", "persist", "fact"],
464570
+ defer_loading: false
464571
+ },
464572
+ {
464573
+ name: "memory_search",
464574
+ description: "Search persistent memory using full-text search",
464575
+ category: "memory",
464576
+ keywords: ["memory", "search", "find", "recall", "knowledge", "retrieve", "remember"],
464577
+ defer_loading: false
464541
464578
  }
464542
464579
  ];
464543
464580
  function searchTools(query) {
@@ -466103,6 +466140,44 @@ var registerCaldavTools = (server, client) => {
466103
466140
  });
466104
466141
  };
466105
466142
 
466143
+ // shared/types.ts
466144
+ var MEMORY_SOURCES = [
466145
+ "channel:whatsapp",
466146
+ "channel:slack",
466147
+ "channel:discord",
466148
+ "channel:telegram",
466149
+ "channel:email",
466150
+ "conversation:assistant"
466151
+ ];
466152
+
466153
+ // cli/src/mcp/tools/memory.ts
466154
+ var registerMemoryTools = (server, client) => {
466155
+ server.tool("memory_store", "Store a piece of knowledge in persistent memory. Use this to remember facts, preferences, decisions, patterns, or any information that should persist across conversations.", {
466156
+ content: exports_external.string().describe("The memory content to store. Be specific and self-contained."),
466157
+ tags: exports_external.optional(exports_external.array(exports_external.string())).describe('Optional tags for categorization (e.g., ["preference", "architecture", "decision"])'),
466158
+ source: exports_external.optional(exports_external.enum(MEMORY_SOURCES)).describe('Where this memory originated (e.g., "channel:whatsapp", "conversation:assistant")')
466159
+ }, async ({ content, tags: tags2, source }) => {
466160
+ try {
466161
+ const result = await client.storeMemory({ content, tags: tags2, source });
466162
+ return formatSuccess(result);
466163
+ } catch (err) {
466164
+ return handleToolError(err);
466165
+ }
466166
+ });
466167
+ server.tool("memory_search", 'Search persistent memory using full-text search. Supports boolean operators (AND, OR, NOT), phrase matching ("quoted phrases"), and prefix matching (term*). Try different search terms or synonyms if initial results are insufficient.', {
466168
+ query: exports_external.string().describe('FTS5 search query. Supports: AND, OR, NOT operators, "quoted phrases", prefix* matching. Example: "user preference" OR settings'),
466169
+ tags: exports_external.optional(exports_external.array(exports_external.string())).describe("Optional tag filter - only return memories with at least one of these tags"),
466170
+ limit: exports_external.optional(exports_external.number()).describe("Maximum results to return (default: 20)")
466171
+ }, async ({ query, tags: tags2, limit: limit2 }) => {
466172
+ try {
466173
+ const results = await client.searchMemories({ query, tags: tags2, limit: limit2 });
466174
+ return formatSuccess(results);
466175
+ } catch (err) {
466176
+ return handleToolError(err);
466177
+ }
466178
+ });
466179
+ };
466180
+
466106
466181
  // cli/src/mcp/tools/index.ts
466107
466182
  function registerTools(server, client) {
466108
466183
  registerCoreTools(server, client);
@@ -466118,6 +466193,7 @@ function registerTools(server, client) {
466118
466193
  registerEmailTools(server, client);
466119
466194
  registerAssistantEventTools(server, client);
466120
466195
  registerCaldavTools(server, client);
466196
+ registerMemoryTools(server, client);
466121
466197
  }
466122
466198
  // cli/src/utils/server.ts
466123
466199
  import { existsSync as existsSync27, readFileSync as readFileSync15, writeFileSync as writeFileSync12, mkdirSync as mkdirSync10, cpSync } from "fs";
@@ -466802,6 +466878,34 @@ class FulcrumClient {
466802
466878
  method: "DELETE"
466803
466879
  });
466804
466880
  }
466881
+ async storeMemory(input) {
466882
+ return this.fetch("/api/memory", {
466883
+ method: "POST",
466884
+ body: JSON.stringify(input)
466885
+ });
466886
+ }
466887
+ async searchMemories(input) {
466888
+ const params = new URLSearchParams({ q: input.query });
466889
+ if (input.tags?.length)
466890
+ params.set("tags", input.tags.join(","));
466891
+ if (input.limit)
466892
+ params.set("limit", String(input.limit));
466893
+ return this.fetch(`/api/memory/search?${params.toString()}`);
466894
+ }
466895
+ async listMemories(input) {
466896
+ const params = new URLSearchParams;
466897
+ if (input?.tags?.length)
466898
+ params.set("tags", input.tags.join(","));
466899
+ if (input?.limit)
466900
+ params.set("limit", String(input.limit));
466901
+ if (input?.offset)
466902
+ params.set("offset", String(input.offset));
466903
+ const query = params.toString();
466904
+ return this.fetch(`/api/memory${query ? `?${query}` : ""}`);
466905
+ }
466906
+ async deleteMemory(id) {
466907
+ return this.fetch(`/api/memory/${id}`, { method: "DELETE" });
466908
+ }
466805
466909
  async getAssistantStats() {
466806
466910
  return this.fetch("/api/assistant/stats");
466807
466911
  }
@@ -466818,7 +466922,7 @@ mcpRoutes.all("/", async (c) => {
466818
466922
  });
466819
466923
  const server = new McpServer({
466820
466924
  name: "fulcrum",
466821
- version: "2.7.1"
466925
+ version: "2.8.2"
466822
466926
  });
466823
466927
  const client = new FulcrumClient(`http://localhost:${port}`);
466824
466928
  registerTools(server, client);
@@ -472623,6 +472727,195 @@ caldavRoutes.delete("/events/:id", async (c) => {
472623
472727
  });
472624
472728
  var caldav_default = caldavRoutes;
472625
472729
 
472730
+ // server/services/memory-service.ts
472731
+ init_db2();
472732
+ init_schema();
472733
+ init_drizzle_orm();
472734
+ init_nanoid();
472735
+ function parseTags(tagsJson) {
472736
+ if (!tagsJson)
472737
+ return null;
472738
+ try {
472739
+ return JSON.parse(tagsJson);
472740
+ } catch {
472741
+ return null;
472742
+ }
472743
+ }
472744
+ function toResult(row, rank) {
472745
+ return {
472746
+ id: row.id,
472747
+ content: row.content,
472748
+ tags: parseTags(row.tags),
472749
+ source: row.source,
472750
+ createdAt: row.createdAt,
472751
+ updatedAt: row.updatedAt,
472752
+ ...rank !== undefined ? { rank } : {}
472753
+ };
472754
+ }
472755
+ async function storeMemory(input) {
472756
+ const now = new Date().toISOString();
472757
+ const id = nanoid();
472758
+ const tagsJson = input.tags?.length ? JSON.stringify(input.tags) : null;
472759
+ const [row] = await db2.insert(memories).values({
472760
+ id,
472761
+ content: input.content,
472762
+ tags: tagsJson,
472763
+ source: input.source ?? null,
472764
+ createdAt: now,
472765
+ updatedAt: now
472766
+ }).returning();
472767
+ return toResult(row);
472768
+ }
472769
+ async function searchMemories(input) {
472770
+ const limit2 = input.limit ?? 20;
472771
+ const ftsQuery = input.query;
472772
+ if (input.tags?.length) {
472773
+ const results2 = db2.all(sql`SELECT m.id, m.content, m.tags, m.source, m.created_at as "createdAt", m.updated_at as "updatedAt", bm25(memories_fts) as rank
472774
+ FROM memories_fts fts
472775
+ JOIN memories m ON m.rowid = fts.rowid
472776
+ WHERE memories_fts MATCH ${ftsQuery}
472777
+ AND EXISTS (
472778
+ SELECT 1 FROM json_each(m.tags) je
472779
+ WHERE je.value IN ${input.tags}
472780
+ )
472781
+ ORDER BY bm25(memories_fts)
472782
+ LIMIT ${limit2}`);
472783
+ return results2.map((r) => ({
472784
+ ...r,
472785
+ tags: parseTags(r.tags)
472786
+ }));
472787
+ }
472788
+ const results = db2.all(sql`SELECT m.id, m.content, m.tags, m.source, m.created_at as "createdAt", m.updated_at as "updatedAt", bm25(memories_fts) as rank
472789
+ FROM memories_fts fts
472790
+ JOIN memories m ON m.rowid = fts.rowid
472791
+ WHERE memories_fts MATCH ${ftsQuery}
472792
+ ORDER BY bm25(memories_fts)
472793
+ LIMIT ${limit2}`);
472794
+ return results.map((r) => ({
472795
+ ...r,
472796
+ tags: parseTags(r.tags)
472797
+ }));
472798
+ }
472799
+ async function updateMemory(id, input) {
472800
+ const now = new Date().toISOString();
472801
+ const updates = { updatedAt: now };
472802
+ if (input.content !== undefined)
472803
+ updates.content = input.content;
472804
+ if (input.tags !== undefined)
472805
+ updates.tags = input.tags?.length ? JSON.stringify(input.tags) : null;
472806
+ const [row] = await db2.update(memories).set({
472807
+ ...input.content !== undefined ? { content: input.content } : {},
472808
+ ...input.tags !== undefined ? { tags: input.tags?.length ? JSON.stringify(input.tags) : null } : {},
472809
+ ...input.source !== undefined ? { source: input.source } : {},
472810
+ updatedAt: now
472811
+ }).where(eq(memories.id, id)).returning();
472812
+ if (!row)
472813
+ return null;
472814
+ return toResult(row);
472815
+ }
472816
+ async function deleteMemory(id) {
472817
+ const result = await db2.delete(memories).where(eq(memories.id, id)).returning();
472818
+ return result.length > 0;
472819
+ }
472820
+ async function listMemories(input = {}) {
472821
+ const limit2 = input.limit ?? 50;
472822
+ const offset = input.offset ?? 0;
472823
+ if (input.tags?.length) {
472824
+ const rows2 = db2.all(sql`SELECT m.id, m.content, m.tags, m.source, m.created_at as "createdAt", m.updated_at as "updatedAt"
472825
+ FROM memories m
472826
+ WHERE EXISTS (
472827
+ SELECT 1 FROM json_each(m.tags) je
472828
+ WHERE je.value IN ${input.tags}
472829
+ )
472830
+ ORDER BY m.created_at DESC
472831
+ LIMIT ${limit2} OFFSET ${offset}`);
472832
+ const [countResult2] = db2.all(sql`SELECT COUNT(*) as count FROM memories m
472833
+ WHERE EXISTS (
472834
+ SELECT 1 FROM json_each(m.tags) je
472835
+ WHERE je.value IN ${input.tags}
472836
+ )`);
472837
+ return {
472838
+ memories: rows2.map((r) => ({
472839
+ ...r,
472840
+ tags: parseTags(r.tags)
472841
+ })),
472842
+ total: countResult2.count
472843
+ };
472844
+ }
472845
+ const rows = await db2.select().from(memories).orderBy(desc(memories.createdAt)).limit(limit2).offset(offset);
472846
+ const [countResult] = db2.all(sql`SELECT COUNT(*) as count FROM memories`);
472847
+ return {
472848
+ memories: rows.map((r) => toResult(r)),
472849
+ total: countResult.count
472850
+ };
472851
+ }
472852
+
472853
+ // server/routes/memory.ts
472854
+ var app25 = new Hono2;
472855
+ app25.post("/", async (c) => {
472856
+ const body = await c.req.json();
472857
+ if (!body.content?.trim()) {
472858
+ return c.json({ error: "content is required" }, 400);
472859
+ }
472860
+ const memory = await storeMemory({
472861
+ content: body.content.trim(),
472862
+ tags: body.tags,
472863
+ source: body.source
472864
+ });
472865
+ return c.json(memory, 201);
472866
+ });
472867
+ app25.get("/search", async (c) => {
472868
+ const query2 = c.req.query("q");
472869
+ if (!query2?.trim()) {
472870
+ return c.json({ error: "q query parameter is required" }, 400);
472871
+ }
472872
+ const tagsParam = c.req.query("tags");
472873
+ const tags2 = tagsParam ? tagsParam.split(",").map((t) => t.trim()).filter(Boolean) : undefined;
472874
+ const limitParam = c.req.query("limit");
472875
+ const limit2 = limitParam ? parseInt(limitParam, 10) : undefined;
472876
+ const results = await searchMemories({
472877
+ query: query2.trim(),
472878
+ tags: tags2,
472879
+ limit: limit2
472880
+ });
472881
+ return c.json(results);
472882
+ });
472883
+ app25.get("/", async (c) => {
472884
+ const tagsParam = c.req.query("tags");
472885
+ const tags2 = tagsParam ? tagsParam.split(",").map((t) => t.trim()).filter(Boolean) : undefined;
472886
+ const limitParam = c.req.query("limit");
472887
+ const limit2 = limitParam ? parseInt(limitParam, 10) : undefined;
472888
+ const offsetParam = c.req.query("offset");
472889
+ const offset = offsetParam ? parseInt(offsetParam, 10) : undefined;
472890
+ const result = await listMemories({ tags: tags2, limit: limit2, offset });
472891
+ return c.json(result);
472892
+ });
472893
+ app25.patch("/:id", async (c) => {
472894
+ const id = c.req.param("id");
472895
+ const body = await c.req.json();
472896
+ if (body.content !== undefined && !body.content.trim()) {
472897
+ return c.json({ error: "content cannot be empty" }, 400);
472898
+ }
472899
+ const updated = await updateMemory(id, {
472900
+ content: body.content?.trim(),
472901
+ tags: body.tags,
472902
+ source: body.source
472903
+ });
472904
+ if (!updated) {
472905
+ return c.json({ error: "Memory not found" }, 404);
472906
+ }
472907
+ return c.json(updated);
472908
+ });
472909
+ app25.delete("/:id", async (c) => {
472910
+ const id = c.req.param("id");
472911
+ const deleted = await deleteMemory(id);
472912
+ if (!deleted) {
472913
+ return c.json({ error: "Memory not found" }, 404);
472914
+ }
472915
+ return c.json({ success: true });
472916
+ });
472917
+ var memory_default = app25;
472918
+
472626
472919
  // server/app.ts
472627
472920
  init_logger3();
472628
472921
  function getDistPath() {
@@ -472632,51 +472925,52 @@ function getDistPath() {
472632
472925
  return join40(process.cwd(), "dist");
472633
472926
  }
472634
472927
  function createApp() {
472635
- const app25 = new Hono2;
472636
- app25.use("*", logger());
472637
- app25.use("*", cors({
472928
+ const app26 = new Hono2;
472929
+ app26.use("*", logger());
472930
+ app26.use("*", cors({
472638
472931
  origin: "*",
472639
472932
  allowMethods: ["GET", "POST", "PATCH", "PUT", "DELETE", "OPTIONS"],
472640
472933
  allowHeaders: ["Content-Type", "mcp-session-id", "Last-Event-ID", "mcp-protocol-version"],
472641
472934
  exposeHeaders: ["mcp-session-id", "mcp-protocol-version"]
472642
472935
  }));
472643
- app25.route("/health", health_default);
472644
- app25.route("/api/tasks", tasks_default);
472645
- app25.route("/api/git", git_default);
472646
- app25.route("/api/fs", filesystem_default);
472647
- app25.route("/api/config", config_default);
472648
- app25.route("/api/uploads", uploads_default);
472649
- app25.route("/api/worktrees", worktrees_default);
472650
- app25.route("/api/terminal-view-state", terminal_view_state_default);
472651
- app25.route("/api/repositories", repositories_default);
472652
- app25.route("/api/copier", copier_default);
472653
- app25.route("/api/github", github_default);
472654
- app25.route("/api/monitoring", monitoringRoutes);
472655
- app25.route("/api/system", system_default);
472656
- app25.route("/api/exec", exec_default);
472657
- app25.route("/api/apps", apps_default);
472658
- app25.route("/api/compose", compose_default);
472659
- app25.route("/api/deployment", deployment_default);
472660
- app25.route("/api/jobs", jobs_default);
472661
- app25.route("/api/opencode", opencode_default);
472662
- app25.route("/api/projects", projects_default);
472663
- app25.route("/api/task-dependencies", task_dependencies_default);
472664
- app25.route("/api/tags", tags_default);
472665
- app25.route("/api/version", version_default);
472666
- app25.route("/mcp", mcp_default);
472667
- app25.route("/api/chat", chat_default);
472668
- app25.route("/api/assistant", assistant_default);
472669
- app25.route("/api/messaging", messaging_default);
472670
- app25.route("/api/backup", backup_default);
472671
- app25.route("/api/caldav", caldav_default);
472672
- app25.post("/api/logs", async (c) => {
472936
+ app26.route("/health", health_default);
472937
+ app26.route("/api/tasks", tasks_default);
472938
+ app26.route("/api/git", git_default);
472939
+ app26.route("/api/fs", filesystem_default);
472940
+ app26.route("/api/config", config_default);
472941
+ app26.route("/api/uploads", uploads_default);
472942
+ app26.route("/api/worktrees", worktrees_default);
472943
+ app26.route("/api/terminal-view-state", terminal_view_state_default);
472944
+ app26.route("/api/repositories", repositories_default);
472945
+ app26.route("/api/copier", copier_default);
472946
+ app26.route("/api/github", github_default);
472947
+ app26.route("/api/monitoring", monitoringRoutes);
472948
+ app26.route("/api/system", system_default);
472949
+ app26.route("/api/exec", exec_default);
472950
+ app26.route("/api/apps", apps_default);
472951
+ app26.route("/api/compose", compose_default);
472952
+ app26.route("/api/deployment", deployment_default);
472953
+ app26.route("/api/jobs", jobs_default);
472954
+ app26.route("/api/opencode", opencode_default);
472955
+ app26.route("/api/projects", projects_default);
472956
+ app26.route("/api/task-dependencies", task_dependencies_default);
472957
+ app26.route("/api/tags", tags_default);
472958
+ app26.route("/api/version", version_default);
472959
+ app26.route("/mcp", mcp_default);
472960
+ app26.route("/api/chat", chat_default);
472961
+ app26.route("/api/assistant", assistant_default);
472962
+ app26.route("/api/messaging", messaging_default);
472963
+ app26.route("/api/backup", backup_default);
472964
+ app26.route("/api/caldav", caldav_default);
472965
+ app26.route("/api/memory", memory_default);
472966
+ app26.post("/api/logs", async (c) => {
472673
472967
  const { entries } = await c.req.json();
472674
472968
  for (const entry of entries) {
472675
472969
  writeEntry(entry);
472676
472970
  }
472677
472971
  return c.json({ ok: true });
472678
472972
  });
472679
- app25.post("/api/debug", async (c) => {
472973
+ app26.post("/api/debug", async (c) => {
472680
472974
  const body = await c.req.json();
472681
472975
  const entry = {
472682
472976
  ts: new Date().toISOString(),
@@ -472717,14 +473011,14 @@ function createApp() {
472717
473011
  }
472718
473012
  });
472719
473013
  };
472720
- app25.get("/assets/*", async (c) => {
473014
+ app26.get("/assets/*", async (c) => {
472721
473015
  const assetPath = join40(distPath, c.req.path);
472722
473016
  if (existsSync34(assetPath)) {
472723
473017
  return serveFile(assetPath, true);
472724
473018
  }
472725
473019
  return c.notFound();
472726
473020
  });
472727
- app25.get("/sounds/*", async (c) => {
473021
+ app26.get("/sounds/*", async (c) => {
472728
473022
  const soundPath = join40(distPath, c.req.path);
472729
473023
  if (existsSync34(soundPath)) {
472730
473024
  return serveFile(soundPath);
@@ -472733,7 +473027,7 @@ function createApp() {
472733
473027
  });
472734
473028
  const staticFiles = ["fulcrum-icon.png", "fulcrum-logo.jpeg", "vite.svg", "logo.png", "goat.jpeg"];
472735
473029
  for (const file2 of staticFiles) {
472736
- app25.get(`/${file2}`, async () => {
473030
+ app26.get(`/${file2}`, async () => {
472737
473031
  const filePath = join40(distPath, file2);
472738
473032
  if (existsSync34(filePath)) {
472739
473033
  return serveFile(filePath);
@@ -472741,7 +473035,7 @@ function createApp() {
472741
473035
  return new Response("Not Found", { status: 404 });
472742
473036
  });
472743
473037
  }
472744
- app25.get("*", async (c, next) => {
473038
+ app26.get("*", async (c, next) => {
472745
473039
  const path15 = c.req.path;
472746
473040
  if (path15.startsWith("/api/") || path15.startsWith("/ws/") || path15 === "/health") {
472747
473041
  return next();
@@ -472752,7 +473046,7 @@ function createApp() {
472752
473046
  });
472753
473047
  });
472754
473048
  }
472755
- return app25;
473049
+ return app26;
472756
473050
  }
472757
473051
 
472758
473052
  // server/index.ts
@@ -473109,11 +473403,11 @@ setBroadcastDestroyed((terminalId) => {
473109
473403
  payload: { terminalId }
473110
473404
  });
473111
473405
  });
473112
- var app25 = createApp();
473113
- var { injectWebSocket, upgradeWebSocket } = createNodeWebSocket({ app: app25 });
473114
- app25.get("/ws/terminal", upgradeWebSocket(() => terminalWebSocketHandlers));
473406
+ var app26 = createApp();
473407
+ var { injectWebSocket, upgradeWebSocket } = createNodeWebSocket({ app: app26 });
473408
+ app26.get("/ws/terminal", upgradeWebSocket(() => terminalWebSocketHandlers));
473115
473409
  var server2 = serve({
473116
- fetch: app25.fetch,
473410
+ fetch: app26.fetch,
473117
473411
  port: PORT,
473118
473412
  hostname: HOST
473119
473413
  }, (info) => {