jowork 0.2.3 → 0.2.4
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.
|
@@ -83,7 +83,10 @@ function createJoWorkMcpServer(opts) {
|
|
|
83
83
|
return { isStale: true, lastSync: null, agoMinutes: null, hint: "cannot check freshness" };
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
|
+
let lastAutoSyncAt = 0;
|
|
87
|
+
const AUTO_SYNC_COOLDOWN_MS = 5 * 60 * 1e3;
|
|
86
88
|
async function autoSyncIfStale(source) {
|
|
89
|
+
if (Date.now() - lastAutoSyncAt < AUTO_SYNC_COOLDOWN_MS) return false;
|
|
87
90
|
const freshness = getDataFreshness(source);
|
|
88
91
|
if (!freshness.isStale) return false;
|
|
89
92
|
try {
|
|
@@ -110,6 +113,7 @@ function createJoWorkMcpServer(opts) {
|
|
|
110
113
|
} catch {
|
|
111
114
|
}
|
|
112
115
|
}
|
|
116
|
+
lastAutoSyncAt = Date.now();
|
|
113
117
|
return true;
|
|
114
118
|
} catch {
|
|
115
119
|
return false;
|
|
@@ -132,7 +136,8 @@ function createJoWorkMcpServer(opts) {
|
|
|
132
136
|
if (path) {
|
|
133
137
|
source = "local";
|
|
134
138
|
}
|
|
135
|
-
const pathFilter = path ? ` AND o.uri LIKE
|
|
139
|
+
const pathFilter = path ? ` AND o.uri LIKE ?` : "";
|
|
140
|
+
const pathParam = path ? `local://${escapeLike(path)}%` : null;
|
|
136
141
|
const ftsMatchQuery = buildFtsQuery(query);
|
|
137
142
|
if (ftsMatchQuery) {
|
|
138
143
|
try {
|
|
@@ -141,7 +146,9 @@ function createJoWorkMcpServer(opts) {
|
|
|
141
146
|
WHERE objects_fts MATCH ? AND o.source = ?${pathFilter} LIMIT ?` : `SELECT o.id, o.title, o.summary, o.source, o.source_type, o.uri, o.tags, o.file_path
|
|
142
147
|
FROM objects_fts JOIN objects o ON o.rowid = objects_fts.rowid
|
|
143
148
|
WHERE objects_fts MATCH ?${pathFilter} LIMIT ?`;
|
|
144
|
-
const ftsArgs = source ? [ftsMatchQuery, source
|
|
149
|
+
const ftsArgs = source ? [ftsMatchQuery, source] : [ftsMatchQuery];
|
|
150
|
+
if (pathParam) ftsArgs.push(pathParam);
|
|
151
|
+
ftsArgs.push(limit);
|
|
145
152
|
const ftsResults = sqlite.prepare(ftsQuery).all(...ftsArgs);
|
|
146
153
|
if (ftsResults.length > 0) {
|
|
147
154
|
logInfo("mcp", `search_data: "${query}" (FTS)`, { source, path, resultCount: ftsResults.length, ms: Date.now() - t0 });
|
|
@@ -155,7 +162,7 @@ Showing ${ftsResults.length} results (summaries only). Use fetch_content with a
|
|
|
155
162
|
}
|
|
156
163
|
}
|
|
157
164
|
const cleanedQuery = query.replace(/飞书|feishu|lark|github|gitlab|notion|slack/gi, "").replace(/群里|最近|在|讨论|什么|话题|有哪些|是什么|怎么样|帮我|告诉我|查一下/g, "").trim();
|
|
158
|
-
const likePathFilter = path ? ` AND uri LIKE
|
|
165
|
+
const likePathFilter = path ? ` AND uri LIKE ?` : "";
|
|
159
166
|
let rows = [];
|
|
160
167
|
if (source && cleanedQuery.length >= 2) {
|
|
161
168
|
const segments = cleanedQuery.split(/\s+/).filter((s) => s.length >= 2);
|
|
@@ -166,7 +173,9 @@ Showing ${ftsResults.length} results (summaries only). Use fetch_content with a
|
|
|
166
173
|
const p = `%${escapeLike(seg)}%`;
|
|
167
174
|
params.push(p, p, p);
|
|
168
175
|
}
|
|
169
|
-
params.push(source
|
|
176
|
+
params.push(source);
|
|
177
|
+
if (pathParam) params.push(pathParam);
|
|
178
|
+
params.push(limit);
|
|
170
179
|
rows = sqlite.prepare(`
|
|
171
180
|
SELECT id, title, summary, source, source_type, uri, tags, file_path FROM objects
|
|
172
181
|
WHERE (${conditions}) AND source = ?${likePathFilter} ORDER BY last_synced_at DESC LIMIT ?
|
|
@@ -175,10 +184,13 @@ Showing ${ftsResults.length} results (summaries only). Use fetch_content with a
|
|
|
175
184
|
rows = [];
|
|
176
185
|
}
|
|
177
186
|
} else if (source) {
|
|
187
|
+
const srcParams = [source];
|
|
188
|
+
if (pathParam) srcParams.push(pathParam);
|
|
189
|
+
srcParams.push(limit);
|
|
178
190
|
rows = sqlite.prepare(`
|
|
179
191
|
SELECT id, title, summary, source, source_type, uri, tags, file_path FROM objects
|
|
180
192
|
WHERE source = ?${likePathFilter} ORDER BY last_synced_at DESC LIMIT ?
|
|
181
|
-
`).all(
|
|
193
|
+
`).all(...srcParams);
|
|
182
194
|
} else if (cleanedQuery.length >= 2) {
|
|
183
195
|
const pattern = `%${escapeLike(cleanedQuery)}%`;
|
|
184
196
|
rows = sqlite.prepare(`
|
|
@@ -763,9 +775,37 @@ ${hot.summary}
|
|
|
763
775
|
`SELECT source, COUNT(*) as count, MAX(last_synced_at) as last_sync FROM objects GROUP BY source`
|
|
764
776
|
).all();
|
|
765
777
|
parts.push("## Data Sources\n");
|
|
778
|
+
const staleSources = [];
|
|
766
779
|
for (const s of sources) {
|
|
767
780
|
const ago = Math.round((now - s.last_sync) / 6e4);
|
|
768
|
-
|
|
781
|
+
const staleTag = ago > 60 ? " \u26A0 STALE" : "";
|
|
782
|
+
if (ago > 60) staleSources.push(s.source);
|
|
783
|
+
parts.push(`- ${s.source}: ${s.count} objects (last sync: ${ago} min ago${staleTag})`);
|
|
784
|
+
}
|
|
785
|
+
try {
|
|
786
|
+
const { listCredentials } = await import("./credential-store-ZRZCSRPC.js");
|
|
787
|
+
const connected = listCredentials();
|
|
788
|
+
const synced = new Set(sources.map((s) => s.source));
|
|
789
|
+
const neverSynced = connected.filter((c) => !synced.has(c));
|
|
790
|
+
for (const ns of neverSynced) {
|
|
791
|
+
parts.push(`- ${ns}: \u26A0 NEVER SYNCED`);
|
|
792
|
+
staleSources.push(ns);
|
|
793
|
+
}
|
|
794
|
+
} catch {
|
|
795
|
+
}
|
|
796
|
+
parts.push("\n## Recommended Actions\n");
|
|
797
|
+
if (staleSources.length > 0) {
|
|
798
|
+
parts.push(`- **SYNC NOW**: ${staleSources.join(", ")} data is stale. Call sync_now tool immediately.`);
|
|
799
|
+
}
|
|
800
|
+
if (goals.length === 0) {
|
|
801
|
+
parts.push("- **SET GOALS**: No goals configured. Ask user about their key objectives and create goals with update_goal.");
|
|
802
|
+
}
|
|
803
|
+
const memCount = sqlite.prepare("SELECT COUNT(*) as c FROM memories").get().c;
|
|
804
|
+
if (memCount === 0) {
|
|
805
|
+
parts.push("- **START MEMORY**: No memories saved yet. Save user preferences and decisions as you learn them via write_memory.");
|
|
806
|
+
}
|
|
807
|
+
if (staleSources.length === 0 && goals.length > 0 && memCount > 0) {
|
|
808
|
+
parts.push("- All systems healthy. No action needed.");
|
|
769
809
|
}
|
|
770
810
|
return { content: [{ type: "text", text: parts.join("\n") }] };
|
|
771
811
|
}
|
|
@@ -773,7 +813,7 @@ ${hot.summary}
|
|
|
773
813
|
server.tool(
|
|
774
814
|
"sync_now",
|
|
775
815
|
{
|
|
776
|
-
source: z.string().optional().describe("Sync a specific source (feishu, github, etc.) or omit for all")
|
|
816
|
+
source: z.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.")
|
|
777
817
|
},
|
|
778
818
|
async ({ source }) => {
|
|
779
819
|
const freshness = getDataFreshness(source);
|
package/dist/cli.js
CHANGED
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
} from "./chunk-XAEGXSEO.js";
|
|
16
16
|
import {
|
|
17
17
|
createJoWorkMcpServer
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-7U3SXINY.js";
|
|
19
19
|
import {
|
|
20
20
|
GoalManager
|
|
21
21
|
} from "./chunk-TN327MDF.js";
|
|
@@ -1950,7 +1950,7 @@ function dashboardCommand(program2) {
|
|
|
1950
1950
|
console.error("Error: JoWork not initialized. Run `jowork init` first.");
|
|
1951
1951
|
process.exit(1);
|
|
1952
1952
|
}
|
|
1953
|
-
const { startDashboard } = await import("./server-
|
|
1953
|
+
const { startDashboard } = await import("./server-5GVWN2NB.js");
|
|
1954
1954
|
const port = opts.port ? parseInt(opts.port, 10) : void 0;
|
|
1955
1955
|
const dashboard = await startDashboard({ port });
|
|
1956
1956
|
const url = `http://127.0.0.1:${dashboard.port}`;
|
|
@@ -793,7 +793,7 @@ function focusMacOS(pid) {
|
|
|
793
793
|
} catch {
|
|
794
794
|
return { focused: false, method: "no-process" };
|
|
795
795
|
}
|
|
796
|
-
if (!tty || tty === "??") {
|
|
796
|
+
if (!tty || tty === "??" || !/^[a-zA-Z0-9/]+$/.test(tty)) {
|
|
797
797
|
return { focused: false, method: "no-tty" };
|
|
798
798
|
}
|
|
799
799
|
let parentComm = "";
|
package/dist/transport.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jowork",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "AI Agent Infrastructure — let AI agents truly understand your work. Connect data sources, give agents awareness and goals. Local-first, one command.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "AGPL-3.0",
|