jowork 0.2.5 → 0.3.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.
- package/dist/{chunk-ROIINI33.js → chunk-4PIT2GZ4.js} +13 -1
- package/dist/{chunk-XLYRHKG6.js → chunk-54SD5GBF.js} +1 -1
- package/dist/chunk-63AMINQC.js +156 -0
- package/dist/{chunk-XAEGXSEO.js → chunk-74AHY7X6.js} +4 -0
- package/dist/{chunk-7U3SXINY.js → chunk-ATAUWJYD.js} +320 -50
- package/dist/chunk-DQW74UCN.js +671 -0
- package/dist/chunk-EYP6WMFF.js +153 -0
- package/dist/{chunk-JSTXMDXI.js → chunk-FCFZCZHR.js} +1 -1
- package/dist/chunk-FX6Z3QHV.js +34 -0
- package/dist/chunk-HENAABEL.js +419 -0
- package/dist/chunk-OXWWOKC7.js +201 -0
- package/dist/{chunk-HUHDL7WV.js → chunk-QGHJ45PL.js} +276 -199
- package/dist/chunk-RO3KK5RC.js +132 -0
- package/dist/{chunk-JE6TOU7W.js → chunk-TFMF3EXE.js} +2 -7
- package/dist/{chunk-TN327MDF.js → chunk-VX662YLA.js} +3 -3
- package/dist/cli.js +308 -135
- package/dist/{config-AI6UIJJN.js → config-FH2XLN7A.js} +2 -2
- package/dist/content-reader-VPGTR2SF.js +10 -0
- package/dist/context-ZNI3WOB7.js +10 -0
- package/dist/{credential-store-ZRZCSRPC.js → credential-store-OS5ZY4OW.js} +2 -2
- package/dist/{feishu-A6YVFKEN.js → feishu-XW5T6ER2.js} +8 -3
- package/dist/{git-manager-N35XSG4Y.js → git-manager-RVWV2GSV.js} +2 -1
- package/dist/github-PQKAYTLO.js +11 -0
- package/dist/{paths-JXOMBYIT.js → paths-FFRET6F7.js} +7 -3
- package/dist/{server-5GVWN2NB.js → server-WEADPUST.js} +59 -66
- package/dist/{setup-SYBQIL2O.js → setup-S2S2CHB2.js} +76 -30
- package/dist/sync-SRLFR5NA.js +21 -0
- package/dist/transport.js +6 -4
- package/package.json +1 -1
- package/src/dashboard/public/app.js +34 -8
- package/src/dashboard/public/style.css +14 -0
- package/dist/chunk-L5ZR7TSK.js +0 -82
- package/dist/chunk-LS2AJM5A.js +0 -163
- package/dist/chunk-QMOFQX7X.js +0 -612
- package/dist/chunk-YJWTKFWX.js +0 -451
- package/dist/github-SHWUFNYB.js +0 -10
- package/dist/sync-KDSPGY4A.js +0 -18
|
@@ -1,27 +1,268 @@
|
|
|
1
1
|
import {
|
|
2
2
|
GoalManager
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-VX662YLA.js";
|
|
4
|
+
import {
|
|
5
|
+
loadCredential
|
|
6
|
+
} from "./chunk-54SD5GBF.js";
|
|
7
|
+
import {
|
|
8
|
+
readObjectContent
|
|
9
|
+
} from "./chunk-FX6Z3QHV.js";
|
|
10
|
+
import {
|
|
11
|
+
logError,
|
|
12
|
+
logInfo
|
|
13
|
+
} from "./chunk-MYDK7MWB.js";
|
|
4
14
|
import {
|
|
5
15
|
buildFtsQuery,
|
|
6
16
|
connectorConfigs,
|
|
7
17
|
createId,
|
|
8
18
|
detectSourceFromQuery,
|
|
9
19
|
memories,
|
|
10
|
-
objectBodies,
|
|
11
20
|
objects
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import {
|
|
14
|
-
logInfo
|
|
15
|
-
} from "./chunk-MYDK7MWB.js";
|
|
21
|
+
} from "./chunk-TFMF3EXE.js";
|
|
16
22
|
|
|
17
23
|
// src/mcp/server.ts
|
|
18
24
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
19
|
-
import { z } from "zod";
|
|
25
|
+
import { z as z2 } from "zod";
|
|
20
26
|
import Database from "better-sqlite3";
|
|
21
27
|
import { drizzle } from "drizzle-orm/better-sqlite3";
|
|
22
28
|
import { like, eq, or, desc } from "drizzle-orm";
|
|
23
29
|
import { existsSync, readFileSync } from "fs";
|
|
24
30
|
import { join } from "path";
|
|
31
|
+
|
|
32
|
+
// src/mcp/proxy.ts
|
|
33
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
34
|
+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
35
|
+
import { z } from "zod";
|
|
36
|
+
var PLUGIN_REGISTRY = {
|
|
37
|
+
feishu: {
|
|
38
|
+
package: "@larksuiteoapi/lark-mcp",
|
|
39
|
+
name: "Feishu (Lark)",
|
|
40
|
+
command: "npx",
|
|
41
|
+
args: ["-y", "@larksuiteoapi/lark-mcp", "--app-id", "{{appId}}", "--app-secret", "{{appSecret}}"],
|
|
42
|
+
credentialMap: { appId: "appId", appSecret: "appSecret" },
|
|
43
|
+
connectorName: "feishu"
|
|
44
|
+
},
|
|
45
|
+
figma: {
|
|
46
|
+
package: "@anthropic/claude-code-figma-mcp",
|
|
47
|
+
name: "Figma",
|
|
48
|
+
command: "npx",
|
|
49
|
+
args: ["-y", "@anthropic/claude-code-figma-mcp"],
|
|
50
|
+
credentialMap: { FIGMA_ACCESS_TOKEN: "token" },
|
|
51
|
+
connectorName: "figma"
|
|
52
|
+
},
|
|
53
|
+
github: {
|
|
54
|
+
package: "@modelcontextprotocol/server-github",
|
|
55
|
+
name: "GitHub",
|
|
56
|
+
command: "npx",
|
|
57
|
+
args: ["-y", "@modelcontextprotocol/server-github"],
|
|
58
|
+
credentialMap: { GITHUB_PERSONAL_ACCESS_TOKEN: "token" },
|
|
59
|
+
connectorName: "github"
|
|
60
|
+
},
|
|
61
|
+
linear: {
|
|
62
|
+
package: "@anthropic/claude-code-linear-mcp",
|
|
63
|
+
name: "Linear",
|
|
64
|
+
command: "npx",
|
|
65
|
+
args: ["-y", "@anthropic/claude-code-linear-mcp"],
|
|
66
|
+
credentialMap: { LINEAR_API_KEY: "apiKey" },
|
|
67
|
+
connectorName: "linear"
|
|
68
|
+
},
|
|
69
|
+
gitlab: {
|
|
70
|
+
package: "@modelcontextprotocol/server-gitlab",
|
|
71
|
+
name: "GitLab",
|
|
72
|
+
command: "npx",
|
|
73
|
+
args: ["-y", "@modelcontextprotocol/server-gitlab"],
|
|
74
|
+
credentialMap: { GITLAB_PERSONAL_ACCESS_TOKEN: "token", GITLAB_API_URL: "apiUrl" },
|
|
75
|
+
connectorName: "gitlab"
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
var MAX_RETRIES = 3;
|
|
79
|
+
var SPAWN_TIMEOUT_MS = 3e4;
|
|
80
|
+
var McpProxyManager = class {
|
|
81
|
+
plugins = /* @__PURE__ */ new Map();
|
|
82
|
+
nativeToolNames;
|
|
83
|
+
constructor(nativeToolNames) {
|
|
84
|
+
this.nativeToolNames = new Set(nativeToolNames);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Discover which plugins can be activated based on available credentials.
|
|
88
|
+
* Does NOT spawn any processes — just identifies what's available.
|
|
89
|
+
*/
|
|
90
|
+
discoverPlugins() {
|
|
91
|
+
const available = [];
|
|
92
|
+
for (const [source, def] of Object.entries(PLUGIN_REGISTRY)) {
|
|
93
|
+
const cred = loadCredential(def.connectorName);
|
|
94
|
+
if (cred) {
|
|
95
|
+
this.plugins.set(source, {
|
|
96
|
+
def,
|
|
97
|
+
client: null,
|
|
98
|
+
transport: null,
|
|
99
|
+
tools: /* @__PURE__ */ new Map(),
|
|
100
|
+
status: "idle",
|
|
101
|
+
failures: 0
|
|
102
|
+
});
|
|
103
|
+
available.push(source);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return available;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Register proxied tools on the JoWork MCP server.
|
|
110
|
+
* Creates a catch-all tool handler that routes to the correct plugin subprocess.
|
|
111
|
+
*/
|
|
112
|
+
registerProxyTools(server) {
|
|
113
|
+
for (const [source, plugin] of this.plugins) {
|
|
114
|
+
const toolName = `${source}_proxy`;
|
|
115
|
+
server.tool(
|
|
116
|
+
toolName,
|
|
117
|
+
{
|
|
118
|
+
tool_name: z.string().describe(`The tool to call on the ${plugin.def.name} MCP server (without prefix)`),
|
|
119
|
+
arguments: z.record(z.unknown()).optional().describe("Arguments to pass to the tool")
|
|
120
|
+
},
|
|
121
|
+
async ({ tool_name, arguments: args }) => {
|
|
122
|
+
try {
|
|
123
|
+
const result = await this.callPluginTool(source, tool_name, args ?? {});
|
|
124
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
125
|
+
} catch (err) {
|
|
126
|
+
return { content: [{ type: "text", text: `Error calling ${source}/${tool_name}: ${err}` }], isError: true };
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Call a tool on a plugin, spawning the subprocess if needed.
|
|
134
|
+
*/
|
|
135
|
+
async callPluginTool(source, toolName, args) {
|
|
136
|
+
const plugin = this.plugins.get(source);
|
|
137
|
+
if (!plugin) throw new Error(`Plugin not found: ${source}`);
|
|
138
|
+
if (plugin.status === "unhealthy") throw new Error(`Plugin ${source} is unhealthy after ${MAX_RETRIES} failures`);
|
|
139
|
+
if (!plugin.client || plugin.status === "idle") {
|
|
140
|
+
await this.startPlugin(source);
|
|
141
|
+
}
|
|
142
|
+
if (!plugin.client) throw new Error(`Failed to start plugin: ${source}`);
|
|
143
|
+
const result = await plugin.client.callTool({ name: toolName, arguments: args });
|
|
144
|
+
return result;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Start a plugin subprocess and discover its tools.
|
|
148
|
+
*/
|
|
149
|
+
async startPlugin(source) {
|
|
150
|
+
const plugin = this.plugins.get(source);
|
|
151
|
+
if (!plugin) return;
|
|
152
|
+
plugin.status = "starting";
|
|
153
|
+
const cred = loadCredential(plugin.def.connectorName);
|
|
154
|
+
if (!cred) {
|
|
155
|
+
plugin.status = "unhealthy";
|
|
156
|
+
throw new Error(`No credentials for ${source}`);
|
|
157
|
+
}
|
|
158
|
+
const env = { ...process.env };
|
|
159
|
+
for (const [envVar, credKey] of Object.entries(plugin.def.credentialMap)) {
|
|
160
|
+
const value = cred.data[credKey];
|
|
161
|
+
if (value) env[envVar] = value;
|
|
162
|
+
}
|
|
163
|
+
const args = plugin.def.args.map((arg) => {
|
|
164
|
+
const match = arg.match(/^\{\{(\w+)\}\}$/);
|
|
165
|
+
if (match) {
|
|
166
|
+
return cred.data[match[1]] ?? arg;
|
|
167
|
+
}
|
|
168
|
+
return arg;
|
|
169
|
+
});
|
|
170
|
+
try {
|
|
171
|
+
const transport = new StdioClientTransport({
|
|
172
|
+
command: plugin.def.command,
|
|
173
|
+
args,
|
|
174
|
+
env
|
|
175
|
+
});
|
|
176
|
+
const client = new Client({ name: `jowork-proxy-${source}`, version: "1.0.0" });
|
|
177
|
+
const connectPromise = client.connect(transport);
|
|
178
|
+
const timeoutPromise = new Promise(
|
|
179
|
+
(_, reject) => setTimeout(() => reject(new Error(`Spawn timeout for ${source}`)), SPAWN_TIMEOUT_MS)
|
|
180
|
+
);
|
|
181
|
+
await Promise.race([connectPromise, timeoutPromise]);
|
|
182
|
+
const toolsResult = await client.listTools();
|
|
183
|
+
plugin.tools.clear();
|
|
184
|
+
for (const tool of toolsResult.tools) {
|
|
185
|
+
if (this.nativeToolNames.has(tool.name)) {
|
|
186
|
+
logInfo("proxy", `Skipping conflicting tool: ${source}/${tool.name} (native tool has priority)`);
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
plugin.tools.set(tool.name, {
|
|
190
|
+
description: tool.description ?? "",
|
|
191
|
+
inputSchema: tool.inputSchema
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
plugin.client = client;
|
|
195
|
+
plugin.transport = transport;
|
|
196
|
+
plugin.status = "ready";
|
|
197
|
+
plugin.failures = 0;
|
|
198
|
+
logInfo("proxy", `Plugin ${source} started: ${plugin.tools.size} tools available`);
|
|
199
|
+
} catch (err) {
|
|
200
|
+
plugin.failures++;
|
|
201
|
+
if (plugin.failures >= MAX_RETRIES) {
|
|
202
|
+
plugin.status = "unhealthy";
|
|
203
|
+
logError("proxy", `Plugin ${source} unhealthy after ${MAX_RETRIES} failures: ${err}`);
|
|
204
|
+
} else {
|
|
205
|
+
plugin.status = "idle";
|
|
206
|
+
logError("proxy", `Plugin ${source} start failed (attempt ${plugin.failures}/${MAX_RETRIES}): ${err}`);
|
|
207
|
+
}
|
|
208
|
+
throw err;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
/** Shut down all plugin subprocesses. */
|
|
212
|
+
async shutdown() {
|
|
213
|
+
for (const [source, plugin] of this.plugins) {
|
|
214
|
+
if (plugin.client) {
|
|
215
|
+
try {
|
|
216
|
+
await plugin.client.close();
|
|
217
|
+
} catch {
|
|
218
|
+
logError("proxy", `Failed to close plugin ${source}`);
|
|
219
|
+
}
|
|
220
|
+
plugin.client = null;
|
|
221
|
+
plugin.transport = null;
|
|
222
|
+
plugin.status = "idle";
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
/** Get status of all plugins. */
|
|
227
|
+
getStatus() {
|
|
228
|
+
return Array.from(this.plugins.entries()).map(([source, plugin]) => ({
|
|
229
|
+
source,
|
|
230
|
+
name: plugin.def.name,
|
|
231
|
+
status: plugin.status,
|
|
232
|
+
tools: plugin.tools.size
|
|
233
|
+
}));
|
|
234
|
+
}
|
|
235
|
+
/** List all available proxied tools across all plugins. */
|
|
236
|
+
listAllTools() {
|
|
237
|
+
const tools = [];
|
|
238
|
+
for (const [source, plugin] of this.plugins) {
|
|
239
|
+
for (const [name, tool] of plugin.tools) {
|
|
240
|
+
tools.push({ source, name: `${source}__${name}`, description: tool.description });
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return tools;
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
// src/mcp/server.ts
|
|
248
|
+
var JOWORK_MCP_TOOLS = [
|
|
249
|
+
"search_data",
|
|
250
|
+
"list_sources",
|
|
251
|
+
"fetch_content",
|
|
252
|
+
"fetch_doc_map",
|
|
253
|
+
"fetch_chunk",
|
|
254
|
+
"read_memory",
|
|
255
|
+
"write_memory",
|
|
256
|
+
"search_memory",
|
|
257
|
+
"get_environment",
|
|
258
|
+
"get_goals",
|
|
259
|
+
"get_metrics",
|
|
260
|
+
"update_goal",
|
|
261
|
+
"push_to_channel",
|
|
262
|
+
"get_hot_context",
|
|
263
|
+
"get_briefing",
|
|
264
|
+
"sync_now"
|
|
265
|
+
];
|
|
25
266
|
var STALE_THRESHOLD_MS = 60 * 60 * 1e3;
|
|
26
267
|
function escapeLike(input) {
|
|
27
268
|
return input.replace(/[%_\\]/g, "\\$&");
|
|
@@ -59,7 +300,15 @@ function createJoWorkMcpServer(opts) {
|
|
|
59
300
|
const db = drizzle(sqlite);
|
|
60
301
|
const goalManager = new GoalManager(sqlite);
|
|
61
302
|
const server = new McpServer({ name: "jowork", version: "0.1.0" });
|
|
303
|
+
const proxyManager = new McpProxyManager([...JOWORK_MCP_TOOLS]);
|
|
304
|
+
const availablePlugins = proxyManager.discoverPlugins();
|
|
305
|
+
if (availablePlugins.length > 0) {
|
|
306
|
+
logInfo("mcp", `Proxy plugins available: ${availablePlugins.join(", ")}`);
|
|
307
|
+
proxyManager.registerProxyTools(server);
|
|
308
|
+
}
|
|
62
309
|
server.server.onclose = () => {
|
|
310
|
+
proxyManager.shutdown().catch(() => {
|
|
311
|
+
});
|
|
63
312
|
if (ownsSqlite) {
|
|
64
313
|
try {
|
|
65
314
|
sqlite.close();
|
|
@@ -90,23 +339,33 @@ function createJoWorkMcpServer(opts) {
|
|
|
90
339
|
const freshness = getDataFreshness(source);
|
|
91
340
|
if (!freshness.isStale) return false;
|
|
92
341
|
try {
|
|
93
|
-
const { loadCredential, listCredentials } = await import("./credential-store-
|
|
342
|
+
const { loadCredential: loadCredential2, listCredentials } = await import("./credential-store-OS5ZY4OW.js");
|
|
94
343
|
const sources = source ? [source] : listCredentials();
|
|
95
344
|
if (sources.length === 0) return false;
|
|
96
345
|
logInfo("mcp", `Auto-syncing stale data (${freshness.hint})`, { source });
|
|
97
346
|
for (const src of sources) {
|
|
98
|
-
const cred =
|
|
347
|
+
const cred = loadCredential2(src);
|
|
99
348
|
if (!cred) continue;
|
|
100
349
|
try {
|
|
101
350
|
switch (src) {
|
|
102
351
|
case "feishu": {
|
|
103
|
-
const { syncFeishu } = await import("./feishu-
|
|
104
|
-
await
|
|
352
|
+
const { syncFeishu } = await import("./feishu-XW5T6ER2.js");
|
|
353
|
+
const { SyncContext } = await import("./context-ZNI3WOB7.js");
|
|
354
|
+
const feishuCtx = new SyncContext(sqlite, { info: () => {
|
|
355
|
+
}, warn: () => {
|
|
356
|
+
}, error: () => {
|
|
357
|
+
} });
|
|
358
|
+
await syncFeishu(feishuCtx, cred.data);
|
|
105
359
|
break;
|
|
106
360
|
}
|
|
107
361
|
case "github": {
|
|
108
|
-
const { syncGitHub } = await import("./github-
|
|
109
|
-
await
|
|
362
|
+
const { syncGitHub } = await import("./github-PQKAYTLO.js");
|
|
363
|
+
const { SyncContext } = await import("./context-ZNI3WOB7.js");
|
|
364
|
+
const ghCtx = new SyncContext(sqlite, { info: () => {
|
|
365
|
+
}, warn: () => {
|
|
366
|
+
}, error: () => {
|
|
367
|
+
} });
|
|
368
|
+
await syncGitHub(ghCtx, cred.data);
|
|
110
369
|
break;
|
|
111
370
|
}
|
|
112
371
|
}
|
|
@@ -122,10 +381,10 @@ function createJoWorkMcpServer(opts) {
|
|
|
122
381
|
server.tool(
|
|
123
382
|
"search_data",
|
|
124
383
|
{
|
|
125
|
-
query:
|
|
126
|
-
source:
|
|
127
|
-
path:
|
|
128
|
-
limit:
|
|
384
|
+
query: z2.string().describe("Keywords to search across all synced data. JoWork auto-syncs if data is stale (>1 hour old)."),
|
|
385
|
+
source: z2.string().optional().describe("Filter by data source: github, feishu, gitlab, notion, slack, local"),
|
|
386
|
+
path: z2.string().optional().describe("Filter local files by path prefix (only applies to source=local)"),
|
|
387
|
+
limit: z2.number().optional().default(20).describe("Max results (default 20)")
|
|
129
388
|
},
|
|
130
389
|
async ({ query, source, path, limit }) => {
|
|
131
390
|
await autoSyncIfStale(source);
|
|
@@ -225,18 +484,18 @@ Showing ${rows.length} results (summaries only). Use fetch_content with a specif
|
|
|
225
484
|
server.tool(
|
|
226
485
|
"fetch_content",
|
|
227
486
|
{
|
|
228
|
-
uri:
|
|
487
|
+
uri: z2.string().describe("Object URI from search_data results")
|
|
229
488
|
},
|
|
230
489
|
async ({ uri }) => {
|
|
231
490
|
const obj = db.select().from(objects).where(eq(objects.uri, uri)).get();
|
|
232
491
|
if (!obj) {
|
|
233
492
|
return { content: [{ type: "text", text: `Object not found: ${uri}` }] };
|
|
234
493
|
}
|
|
235
|
-
const body =
|
|
494
|
+
const body = readObjectContent(sqlite, obj.id, obj.filePath);
|
|
236
495
|
return {
|
|
237
496
|
content: [{
|
|
238
497
|
type: "text",
|
|
239
|
-
text: JSON.stringify({ ...obj, body: body
|
|
498
|
+
text: JSON.stringify({ ...obj, body: body ?? null }, null, 2)
|
|
240
499
|
}]
|
|
241
500
|
};
|
|
242
501
|
}
|
|
@@ -244,7 +503,7 @@ Showing ${rows.length} results (summaries only). Use fetch_content with a specif
|
|
|
244
503
|
server.tool(
|
|
245
504
|
"fetch_doc_map",
|
|
246
505
|
{
|
|
247
|
-
id:
|
|
506
|
+
id: z2.string().describe("Object ID from search_data results")
|
|
248
507
|
},
|
|
249
508
|
async ({ id }) => {
|
|
250
509
|
const row = sqlite.prepare(`SELECT doc_map, title FROM objects WHERE id = ?`).get(id);
|
|
@@ -258,8 +517,8 @@ Showing ${rows.length} results (summaries only). Use fetch_content with a specif
|
|
|
258
517
|
server.tool(
|
|
259
518
|
"fetch_chunk",
|
|
260
519
|
{
|
|
261
|
-
id:
|
|
262
|
-
idx:
|
|
520
|
+
id: z2.string().describe("Object ID"),
|
|
521
|
+
idx: z2.number().describe("Chunk index (0-based, see fetch_doc_map output for available indices)")
|
|
263
522
|
},
|
|
264
523
|
async ({ id, idx }) => {
|
|
265
524
|
const chunk = sqlite.prepare(
|
|
@@ -276,8 +535,8 @@ Showing ${rows.length} results (summaries only). Use fetch_content with a specif
|
|
|
276
535
|
server.tool(
|
|
277
536
|
"read_memory",
|
|
278
537
|
{
|
|
279
|
-
query:
|
|
280
|
-
limit:
|
|
538
|
+
query: z2.string().describe("Search keywords -- matches against title, content, and tags"),
|
|
539
|
+
limit: z2.number().optional().default(10).describe("Max results")
|
|
281
540
|
},
|
|
282
541
|
async ({ query, limit }) => {
|
|
283
542
|
const pattern = `%${escapeLike(query)}%`;
|
|
@@ -302,10 +561,10 @@ Showing ${rows.length} results (summaries only). Use fetch_content with a specif
|
|
|
302
561
|
server.tool(
|
|
303
562
|
"write_memory",
|
|
304
563
|
{
|
|
305
|
-
title:
|
|
306
|
-
content:
|
|
307
|
-
tags:
|
|
308
|
-
scope:
|
|
564
|
+
title: z2.string().describe("Short descriptive title for the memory"),
|
|
565
|
+
content: z2.string().describe("Memory content -- what to remember"),
|
|
566
|
+
tags: z2.array(z2.string()).optional().describe('Tags for categorization (e.g. ["decision", "preference"])'),
|
|
567
|
+
scope: z2.enum(["personal", "team"]).optional().default("personal").describe("Scope")
|
|
309
568
|
},
|
|
310
569
|
async ({ title, content, tags, scope }) => {
|
|
311
570
|
const now = Date.now();
|
|
@@ -358,8 +617,8 @@ Showing ${rows.length} results (summaries only). Use fetch_content with a specif
|
|
|
358
617
|
server.tool(
|
|
359
618
|
"search_memory",
|
|
360
619
|
{
|
|
361
|
-
query:
|
|
362
|
-
limit:
|
|
620
|
+
query: z2.string().describe("Search query -- uses full-text search with time-weighted ranking"),
|
|
621
|
+
limit: z2.number().optional().default(10).describe("Max results")
|
|
363
622
|
},
|
|
364
623
|
async ({ query, limit }) => {
|
|
365
624
|
const t0 = Date.now();
|
|
@@ -567,7 +826,7 @@ Showing ${rows.length} results (summaries only). Use fetch_content with a specif
|
|
|
567
826
|
server.tool(
|
|
568
827
|
"get_goals",
|
|
569
828
|
{
|
|
570
|
-
status:
|
|
829
|
+
status: z2.string().optional().describe("Filter: active, paused, completed")
|
|
571
830
|
},
|
|
572
831
|
async ({ status }) => {
|
|
573
832
|
const goals = goalManager.listGoals({ status });
|
|
@@ -577,7 +836,7 @@ Showing ${rows.length} results (summaries only). Use fetch_content with a specif
|
|
|
577
836
|
server.tool(
|
|
578
837
|
"get_metrics",
|
|
579
838
|
{
|
|
580
|
-
goal_id:
|
|
839
|
+
goal_id: z2.string().optional().describe("Goal ID (shows all active if omitted)")
|
|
581
840
|
},
|
|
582
841
|
async ({ goal_id }) => {
|
|
583
842
|
const query = goal_id ? `SELECT s.*, m.threshold, m.comparison, m.current, m.met, g.title as goal_title
|
|
@@ -596,10 +855,10 @@ Showing ${rows.length} results (summaries only). Use fetch_content with a specif
|
|
|
596
855
|
server.tool(
|
|
597
856
|
"update_goal",
|
|
598
857
|
{
|
|
599
|
-
goal_id:
|
|
600
|
-
title:
|
|
601
|
-
description:
|
|
602
|
-
status:
|
|
858
|
+
goal_id: z2.string().describe("Goal ID"),
|
|
859
|
+
title: z2.string().optional().describe("New title"),
|
|
860
|
+
description: z2.string().optional().describe("New description"),
|
|
861
|
+
status: z2.enum(["active", "paused", "completed"]).optional().describe("New status")
|
|
603
862
|
},
|
|
604
863
|
async ({ goal_id, title, description, status }) => {
|
|
605
864
|
const existing = goalManager.getGoal(goal_id);
|
|
@@ -630,9 +889,9 @@ Showing ${rows.length} results (summaries only). Use fetch_content with a specif
|
|
|
630
889
|
server.tool(
|
|
631
890
|
"push_to_channel",
|
|
632
891
|
{
|
|
633
|
-
channel:
|
|
634
|
-
target:
|
|
635
|
-
message:
|
|
892
|
+
channel: z2.string().describe("Channel: feishu, slack, telegram"),
|
|
893
|
+
target: z2.string().describe("Target ID (chat_id for feishu, channel for slack)"),
|
|
894
|
+
message: z2.string().describe("Message content")
|
|
636
895
|
},
|
|
637
896
|
async ({ channel, target, message }) => {
|
|
638
897
|
if (!checkPushRateLimit(`${channel}:${target}`)) {
|
|
@@ -724,7 +983,7 @@ Showing ${rows.length} results (summaries only). Use fetch_content with a specif
|
|
|
724
983
|
server.tool(
|
|
725
984
|
"get_hot_context",
|
|
726
985
|
{
|
|
727
|
-
hours:
|
|
986
|
+
hours: z2.number().optional().default(24).describe("Time window in hours (default 24, max 72)")
|
|
728
987
|
},
|
|
729
988
|
async ({ hours }) => {
|
|
730
989
|
const h = Math.min(hours, 72);
|
|
@@ -783,7 +1042,7 @@ ${hot.summary}
|
|
|
783
1042
|
parts.push(`- ${s.source}: ${s.count} objects (last sync: ${ago} min ago${staleTag})`);
|
|
784
1043
|
}
|
|
785
1044
|
try {
|
|
786
|
-
const { listCredentials } = await import("./credential-store-
|
|
1045
|
+
const { listCredentials } = await import("./credential-store-OS5ZY4OW.js");
|
|
787
1046
|
const connected = listCredentials();
|
|
788
1047
|
const synced = new Set(sources.map((s) => s.source));
|
|
789
1048
|
const neverSynced = connected.filter((c) => !synced.has(c));
|
|
@@ -813,32 +1072,42 @@ ${hot.summary}
|
|
|
813
1072
|
server.tool(
|
|
814
1073
|
"sync_now",
|
|
815
1074
|
{
|
|
816
|
-
source:
|
|
1075
|
+
source: z2.string().optional().describe("Sync a specific source (feishu, github, etc.) or omit for all. Call this directly when data is stale \u2014 do NOT ask the user for permission to sync.")
|
|
817
1076
|
},
|
|
818
1077
|
async ({ source }) => {
|
|
819
1078
|
const freshness = getDataFreshness(source);
|
|
820
1079
|
try {
|
|
821
|
-
const { loadCredential, listCredentials } = await import("./credential-store-
|
|
1080
|
+
const { loadCredential: loadCredential2, listCredentials } = await import("./credential-store-OS5ZY4OW.js");
|
|
822
1081
|
const sources = source ? [source] : listCredentials();
|
|
823
1082
|
if (sources.length === 0) {
|
|
824
1083
|
return { content: [{ type: "text", text: "No data sources connected. Run `jowork connect <source>` first." }] };
|
|
825
1084
|
}
|
|
826
1085
|
const results = [];
|
|
827
1086
|
for (const src of sources) {
|
|
828
|
-
const cred =
|
|
1087
|
+
const cred = loadCredential2(src);
|
|
829
1088
|
if (!cred) continue;
|
|
830
1089
|
try {
|
|
831
1090
|
switch (src) {
|
|
832
1091
|
case "feishu": {
|
|
833
|
-
const { syncFeishu } = await import("./feishu-
|
|
834
|
-
const
|
|
1092
|
+
const { syncFeishu } = await import("./feishu-XW5T6ER2.js");
|
|
1093
|
+
const { SyncContext } = await import("./context-ZNI3WOB7.js");
|
|
1094
|
+
const feishuCtx = new SyncContext(sqlite, { info: () => {
|
|
1095
|
+
}, warn: () => {
|
|
1096
|
+
}, error: () => {
|
|
1097
|
+
} });
|
|
1098
|
+
const r = await syncFeishu(feishuCtx, cred.data);
|
|
835
1099
|
results.push(`feishu: ${r.newMessages} new messages`);
|
|
836
1100
|
break;
|
|
837
1101
|
}
|
|
838
1102
|
case "github": {
|
|
839
|
-
const { syncGitHub } = await import("./github-
|
|
840
|
-
const
|
|
841
|
-
|
|
1103
|
+
const { syncGitHub } = await import("./github-PQKAYTLO.js");
|
|
1104
|
+
const { SyncContext } = await import("./context-ZNI3WOB7.js");
|
|
1105
|
+
const ghCtx = new SyncContext(sqlite, { info: () => {
|
|
1106
|
+
}, warn: () => {
|
|
1107
|
+
}, error: () => {
|
|
1108
|
+
} });
|
|
1109
|
+
const r = await syncGitHub(ghCtx, cred.data);
|
|
1110
|
+
results.push(`github: ${r.newObjects} new, ${r.updatedObjects} updated`);
|
|
842
1111
|
break;
|
|
843
1112
|
}
|
|
844
1113
|
default:
|
|
@@ -892,5 +1161,6 @@ Previous data was ${freshness.hint}.` }] };
|
|
|
892
1161
|
}
|
|
893
1162
|
|
|
894
1163
|
export {
|
|
1164
|
+
PLUGIN_REGISTRY,
|
|
895
1165
|
createJoWorkMcpServer
|
|
896
1166
|
};
|