jowork 0.2.3 → 0.2.5
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-YVPWTH6F.js → chunk-7U3SXINY.js} +47 -7
- package/dist/{chunk-AIXKXEYS.js → chunk-HUHDL7WV.js} +122 -85
- package/dist/cli.js +36 -20
- package/dist/{server-6WYDERK6.js → server-5GVWN2NB.js} +1 -1
- package/dist/{setup-IDQDPCEJ.js → setup-SYBQIL2O.js} +16 -3
- package/dist/{sync-7V54N62M.js → sync-KDSPGY4A.js} +1 -1
- package/dist/transport.js +1 -1
- package/package.json +1 -1
|
@@ -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);
|
|
@@ -322,8 +322,8 @@ var FileWriter = class {
|
|
|
322
322
|
existingHeaders = [...existing.matchAll(/^## .+/gm)].map((m) => m[0]);
|
|
323
323
|
}
|
|
324
324
|
const newMessages = messages.filter((m) => {
|
|
325
|
-
const
|
|
326
|
-
return !existingHeaders.includes(
|
|
325
|
+
const header2 = `## ${m.time} \u2014 ${m.sender}`;
|
|
326
|
+
return !existingHeaders.includes(header2);
|
|
327
327
|
});
|
|
328
328
|
if (newMessages.length === 0) return filePath;
|
|
329
329
|
if (!existsSync(absPath)) {
|
|
@@ -384,144 +384,181 @@ ${sanitizeContent(m.content)}`).join("\n");
|
|
|
384
384
|
};
|
|
385
385
|
|
|
386
386
|
// src/commands/sync.ts
|
|
387
|
+
var isTTY = process.stdout.isTTY;
|
|
388
|
+
var c = {
|
|
389
|
+
reset: isTTY ? "\x1B[0m" : "",
|
|
390
|
+
bold: isTTY ? "\x1B[1m" : "",
|
|
391
|
+
dim: isTTY ? "\x1B[2m" : "",
|
|
392
|
+
green: isTTY ? "\x1B[32m" : "",
|
|
393
|
+
yellow: isTTY ? "\x1B[33m" : "",
|
|
394
|
+
red: isTTY ? "\x1B[31m" : "",
|
|
395
|
+
cyan: isTTY ? "\x1B[36m" : "",
|
|
396
|
+
gray: isTTY ? "\x1B[90m" : "",
|
|
397
|
+
white: isTTY ? "\x1B[37m" : "",
|
|
398
|
+
bgGreen: isTTY ? "\x1B[42m" : "",
|
|
399
|
+
bgRed: isTTY ? "\x1B[41m" : "",
|
|
400
|
+
bgYellow: isTTY ? "\x1B[43m" : ""
|
|
401
|
+
};
|
|
402
|
+
var icon = {
|
|
403
|
+
ok: `${c.green}\u2713${c.reset}`,
|
|
404
|
+
warn: `${c.yellow}\u26A0${c.reset}`,
|
|
405
|
+
fail: `${c.red}\u2717${c.reset}`,
|
|
406
|
+
skip: `${c.gray}\u25CB${c.reset}`,
|
|
407
|
+
sync: `${c.cyan}\u21BB${c.reset}`,
|
|
408
|
+
link: `${c.cyan}\u27E1${c.reset}`,
|
|
409
|
+
git: `${c.gray}\u2387${c.reset}`
|
|
410
|
+
};
|
|
411
|
+
function header(text) {
|
|
412
|
+
console.log("");
|
|
413
|
+
console.log(` ${c.bold}${text}${c.reset}`);
|
|
414
|
+
console.log(` ${c.dim}${"\u2500".repeat(Math.min(text.length + 4, 50))}${c.reset}`);
|
|
415
|
+
}
|
|
416
|
+
function progressBar(current, total, width = 20) {
|
|
417
|
+
const pct = total > 0 ? current / total : 0;
|
|
418
|
+
const filled = Math.round(pct * width);
|
|
419
|
+
const empty = width - filled;
|
|
420
|
+
const bar = `${c.green}${"\u2588".repeat(filled)}${c.gray}${"\u2591".repeat(empty)}${c.reset}`;
|
|
421
|
+
return `${bar} ${c.dim}${current}/${total}${c.reset}`;
|
|
422
|
+
}
|
|
423
|
+
function sourceLabel(name) {
|
|
424
|
+
const colors = {
|
|
425
|
+
feishu: c.cyan,
|
|
426
|
+
github: c.white,
|
|
427
|
+
gitlab: c.yellow,
|
|
428
|
+
linear: c.cyan,
|
|
429
|
+
posthog: c.red,
|
|
430
|
+
firebase: c.yellow
|
|
431
|
+
};
|
|
432
|
+
return `${colors[name] ?? c.white}${c.bold}${name}${c.reset}`;
|
|
433
|
+
}
|
|
434
|
+
function resultLine(ok, msg) {
|
|
435
|
+
console.log(` ${ok ? icon.ok : icon.warn} ${msg}`);
|
|
436
|
+
}
|
|
437
|
+
function elapsed(start) {
|
|
438
|
+
const ms = Date.now() - start;
|
|
439
|
+
return ms < 1e3 ? `${ms}ms` : `${(ms / 1e3).toFixed(1)}s`;
|
|
440
|
+
}
|
|
387
441
|
async function runSync(sources) {
|
|
388
442
|
const db = new DbManager(dbPath());
|
|
389
443
|
db.ensureTables();
|
|
390
444
|
const fileWriter = new FileWriter();
|
|
391
445
|
const syncResults = [];
|
|
446
|
+
const t0 = Date.now();
|
|
447
|
+
let totalNew = 0;
|
|
392
448
|
let gitManager = null;
|
|
393
449
|
try {
|
|
394
450
|
gitManager = new GitManager(fileRepoDir());
|
|
395
451
|
await gitManager.init();
|
|
396
452
|
} catch {
|
|
397
453
|
}
|
|
398
|
-
|
|
454
|
+
header(`Syncing ${sources.length} source${sources.length > 1 ? "s" : ""}`);
|
|
455
|
+
for (let i = 0; i < sources.length; i++) {
|
|
456
|
+
const source = sources[i];
|
|
399
457
|
const cred = loadCredential(source);
|
|
400
458
|
if (!cred) {
|
|
401
|
-
console.log(
|
|
459
|
+
console.log(` ${icon.skip} ${sourceLabel(source)} ${c.dim}no credentials, skipping${c.reset}`);
|
|
402
460
|
continue;
|
|
403
461
|
}
|
|
404
|
-
|
|
462
|
+
const sourceStart = Date.now();
|
|
463
|
+
console.log(` ${icon.sync} ${sourceLabel(source)} ${c.dim}syncing...${c.reset}`);
|
|
464
|
+
const logger = {
|
|
465
|
+
info: (_msg) => {
|
|
466
|
+
},
|
|
467
|
+
warn: (msg) => resultLine(false, `${c.dim}${msg}${c.reset}`),
|
|
468
|
+
error: (msg) => console.error(` ${icon.fail} ${c.red}${msg}${c.reset}`)
|
|
469
|
+
};
|
|
405
470
|
try {
|
|
406
471
|
switch (source) {
|
|
407
472
|
case "feishu": {
|
|
408
|
-
const logger = {
|
|
409
|
-
info: (msg) => console.log(` ${msg}`),
|
|
410
|
-
warn: (msg) => console.log(` \u26A0 ${msg}`),
|
|
411
|
-
error: (msg) => console.error(` \u2717 ${msg}`)
|
|
412
|
-
};
|
|
413
473
|
const result = await syncFeishu(db.getSqlite(), cred.data, logger, fileWriter);
|
|
414
|
-
|
|
474
|
+
resultLine(true, `${result.newMessages} new messages from ${result.chats} chats`);
|
|
475
|
+
totalNew += result.newMessages;
|
|
415
476
|
syncResults.push({ source: "feishu", newObjects: result.newMessages, label: "messages" });
|
|
416
477
|
try {
|
|
417
|
-
const
|
|
418
|
-
if (
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
}
|
|
422
|
-
} catch (err) {
|
|
423
|
-
console.log(` \u26A0 Meeting sync: ${err}`);
|
|
478
|
+
const mr = await syncFeishuMeetings(db.getSqlite(), cred.data, logger, fileWriter);
|
|
479
|
+
if (mr.newObjects > 0) resultLine(true, `${mr.newObjects} calendar events`);
|
|
480
|
+
syncResults.push({ source: "feishu/meetings", newObjects: mr.newObjects, label: "events" });
|
|
481
|
+
} catch {
|
|
424
482
|
}
|
|
425
483
|
try {
|
|
426
|
-
const
|
|
427
|
-
if (
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
}
|
|
431
|
-
} catch (err) {
|
|
432
|
-
console.log(` \u26A0 Document sync: ${err}`);
|
|
484
|
+
const dr = await syncFeishuDocs(db.getSqlite(), cred.data, logger, fileWriter);
|
|
485
|
+
if (dr.newObjects > 0) resultLine(true, `${dr.newObjects} documents`);
|
|
486
|
+
syncResults.push({ source: "feishu/docs", newObjects: dr.newObjects, label: "docs" });
|
|
487
|
+
} catch {
|
|
433
488
|
}
|
|
434
489
|
try {
|
|
435
|
-
const
|
|
436
|
-
if (
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
}
|
|
440
|
-
} catch (err) {
|
|
441
|
-
console.log(` \u26A0 Approval sync: ${err}`);
|
|
490
|
+
const ar = await syncFeishuApprovals(db.getSqlite(), cred.data, logger, fileWriter);
|
|
491
|
+
if (ar.newObjects > 0) resultLine(true, `${ar.newObjects} approvals`);
|
|
492
|
+
syncResults.push({ source: "feishu/approvals", newObjects: ar.newObjects, label: "approvals" });
|
|
493
|
+
} catch {
|
|
442
494
|
}
|
|
443
495
|
break;
|
|
444
496
|
}
|
|
445
497
|
case "github": {
|
|
446
|
-
const
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
};
|
|
451
|
-
const result = await syncGitHub(db.getSqlite(), cred.data, ghLogger, fileWriter);
|
|
452
|
-
console.log(` \u2713 Synced ${result.repos} repos: ${result.issues} issues, ${result.prs} PRs (${result.newObjects} new)`);
|
|
453
|
-
syncResults.push({ source: "github", newObjects: result.newObjects });
|
|
498
|
+
const r = await syncGitHub(db.getSqlite(), cred.data, logger, fileWriter);
|
|
499
|
+
resultLine(true, `${r.repos} repos, ${r.prs} PRs, ${r.issues} issues ${c.dim}(${r.newObjects} new)${c.reset}`);
|
|
500
|
+
totalNew += r.newObjects;
|
|
501
|
+
syncResults.push({ source: "github", newObjects: r.newObjects });
|
|
454
502
|
break;
|
|
455
503
|
}
|
|
456
504
|
case "gitlab": {
|
|
457
|
-
const
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
};
|
|
462
|
-
const glResult = await syncGitLab(db.getSqlite(), cred.data, glLogger, fileWriter);
|
|
463
|
-
console.log(` \u2713 Synced ${glResult.projects} projects: ${glResult.issues} issues, ${glResult.mrs} MRs (${glResult.newObjects} new)`);
|
|
464
|
-
syncResults.push({ source: "gitlab", newObjects: glResult.newObjects });
|
|
505
|
+
const r = await syncGitLab(db.getSqlite(), cred.data, logger, fileWriter);
|
|
506
|
+
resultLine(true, `${r.projects} projects, ${r.mrs} MRs, ${r.issues} issues ${c.dim}(${r.newObjects} new)${c.reset}`);
|
|
507
|
+
totalNew += r.newObjects;
|
|
508
|
+
syncResults.push({ source: "gitlab", newObjects: r.newObjects });
|
|
465
509
|
break;
|
|
466
510
|
}
|
|
467
511
|
case "linear": {
|
|
468
|
-
const
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
};
|
|
473
|
-
const linResult = await syncLinear(db.getSqlite(), cred.data, linLogger, fileWriter);
|
|
474
|
-
console.log(` \u2713 Synced ${linResult.issues} Linear issues (${linResult.newObjects} new)`);
|
|
475
|
-
syncResults.push({ source: "linear", newObjects: linResult.newObjects, label: "issues" });
|
|
512
|
+
const r = await syncLinear(db.getSqlite(), cred.data, logger, fileWriter);
|
|
513
|
+
resultLine(true, `${r.issues} issues ${c.dim}(${r.newObjects} new)${c.reset}`);
|
|
514
|
+
totalNew += r.newObjects;
|
|
515
|
+
syncResults.push({ source: "linear", newObjects: r.newObjects, label: "issues" });
|
|
476
516
|
break;
|
|
477
517
|
}
|
|
478
518
|
case "posthog": {
|
|
479
|
-
const
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
};
|
|
484
|
-
const phResult = await syncPostHog(db.getSqlite(), cred.data, phLogger, fileWriter);
|
|
485
|
-
console.log(` \u2713 Synced ${phResult.insights} insights, ${phResult.events} events (${phResult.newObjects} new)`);
|
|
486
|
-
syncResults.push({ source: "posthog", newObjects: phResult.newObjects });
|
|
519
|
+
const r = await syncPostHog(db.getSqlite(), cred.data, logger, fileWriter);
|
|
520
|
+
resultLine(true, `${r.insights} insights, ${r.events} events ${c.dim}(${r.newObjects} new)${c.reset}`);
|
|
521
|
+
totalNew += r.newObjects;
|
|
522
|
+
syncResults.push({ source: "posthog", newObjects: r.newObjects });
|
|
487
523
|
break;
|
|
488
524
|
}
|
|
489
525
|
case "firebase": {
|
|
490
|
-
const
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
};
|
|
495
|
-
const fbResult = await syncFirebase(db.getSqlite(), cred.data, fbLogger, fileWriter);
|
|
496
|
-
console.log(` \u2713 Synced ${fbResult.events} Firebase events (${fbResult.newObjects} new)`);
|
|
497
|
-
syncResults.push({ source: "firebase", newObjects: fbResult.newObjects, label: "events" });
|
|
526
|
+
const r = await syncFirebase(db.getSqlite(), cred.data, logger, fileWriter);
|
|
527
|
+
resultLine(true, `${r.events} events ${c.dim}(${r.newObjects} new)${c.reset}`);
|
|
528
|
+
totalNew += r.newObjects;
|
|
529
|
+
syncResults.push({ source: "firebase", newObjects: r.newObjects, label: "events" });
|
|
498
530
|
break;
|
|
499
531
|
}
|
|
500
532
|
default:
|
|
501
|
-
console.log(`
|
|
533
|
+
console.log(` ${icon.skip} ${c.dim}unknown source${c.reset}`);
|
|
502
534
|
}
|
|
535
|
+
console.log(` ${c.dim}${elapsed(sourceStart)}${c.reset}`);
|
|
503
536
|
} catch (err) {
|
|
504
537
|
logError("sync", `Failed to sync ${source}`, { error: String(err) });
|
|
505
|
-
console.
|
|
538
|
+
console.log(` ${icon.fail} ${c.red}sync failed${c.reset} ${c.dim}${String(err).slice(0, 60)}${c.reset}`);
|
|
539
|
+
}
|
|
540
|
+
if (sources.length > 1) {
|
|
541
|
+
console.log(` ${progressBar(i + 1, sources.length)}`);
|
|
506
542
|
}
|
|
507
543
|
}
|
|
508
|
-
console.log("
|
|
544
|
+
console.log("");
|
|
545
|
+
console.log(` ${icon.link} ${c.dim}extracting links...${c.reset}`);
|
|
509
546
|
const { processed, linksCreated } = linkAllUnprocessed(db.getSqlite());
|
|
510
|
-
|
|
547
|
+
if (processed > 0) {
|
|
548
|
+
resultLine(true, `${linksCreated} links from ${processed} objects`);
|
|
549
|
+
}
|
|
511
550
|
db.close();
|
|
512
551
|
if (gitManager) {
|
|
513
552
|
try {
|
|
514
|
-
const sha = await gitManager.commitSync({
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
});
|
|
518
|
-
if (sha) {
|
|
519
|
-
console.log(` \u2713 Committed: ${sha.slice(0, 7)}`);
|
|
520
|
-
}
|
|
521
|
-
} catch (err) {
|
|
522
|
-
logError("sync", "Git commit failed", { error: String(err) });
|
|
553
|
+
const sha = await gitManager.commitSync({ timestamp: (/* @__PURE__ */ new Date()).toISOString(), sources: syncResults });
|
|
554
|
+
if (sha) console.log(` ${icon.git} ${c.dim}committed ${sha.slice(0, 7)}${c.reset}`);
|
|
555
|
+
} catch {
|
|
523
556
|
}
|
|
524
557
|
}
|
|
558
|
+
console.log("");
|
|
559
|
+
console.log(` ${c.bold}${c.green}Done${c.reset} ${c.dim}in ${elapsed(t0)}${c.reset}`);
|
|
560
|
+
console.log(` ${c.bold}${totalNew}${c.reset} new objects synced from ${c.bold}${syncResults.length}${c.reset} sources`);
|
|
561
|
+
console.log("");
|
|
525
562
|
}
|
|
526
563
|
function syncCommand(program) {
|
|
527
564
|
program.command("sync").description("Sync data from connected sources").option("--source <source>", "Sync specific source only").action(async (opts) => {
|
package/dist/cli.js
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
syncCommand,
|
|
5
5
|
syncFirebase,
|
|
6
6
|
syncPostHog
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-HUHDL7WV.js";
|
|
8
8
|
import {
|
|
9
9
|
linkAllUnprocessed,
|
|
10
10
|
syncGitLab,
|
|
@@ -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";
|
|
@@ -91,7 +91,7 @@ function initCommand(program2) {
|
|
|
91
91
|
default: true
|
|
92
92
|
}]);
|
|
93
93
|
if (continueSetup) {
|
|
94
|
-
const { runSetupWizard } = await import("./setup-
|
|
94
|
+
const { runSetupWizard } = await import("./setup-SYBQIL2O.js");
|
|
95
95
|
await runSetupWizard();
|
|
96
96
|
} else {
|
|
97
97
|
console.log("");
|
|
@@ -568,8 +568,11 @@ function serveCommand(program2) {
|
|
|
568
568
|
program2.command("serve").description("Start MCP server (stdio mode for agents, or --daemon for background)").option("--daemon", "Run as background daemon with cron sync").action(async (opts) => {
|
|
569
569
|
const resolvedDbPath = process.env["JOWORK_DB_PATH"] ?? dbPath();
|
|
570
570
|
if (!existsSync2(resolvedDbPath)) {
|
|
571
|
-
|
|
572
|
-
|
|
571
|
+
const { writeConfig: writeConfig2 } = await import("./config-AI6UIJJN.js");
|
|
572
|
+
const db = new DbManager(dbPath());
|
|
573
|
+
db.ensureTables();
|
|
574
|
+
db.close();
|
|
575
|
+
writeConfig2({ version: "0.1.0", initialized: true, connectors: {} });
|
|
573
576
|
}
|
|
574
577
|
if (opts.daemon) {
|
|
575
578
|
await startDaemon();
|
|
@@ -783,7 +786,16 @@ async function runSync() {
|
|
|
783
786
|
// src/commands/register.ts
|
|
784
787
|
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, copyFileSync, existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
|
|
785
788
|
import { join as join2 } from "path";
|
|
789
|
+
import { execSync } from "child_process";
|
|
786
790
|
var HOME = process.env["HOME"] ?? "";
|
|
791
|
+
function getMcpCommand() {
|
|
792
|
+
try {
|
|
793
|
+
execSync("which jowork", { stdio: "ignore" });
|
|
794
|
+
return { command: "jowork", args: ["serve"] };
|
|
795
|
+
} catch {
|
|
796
|
+
return { command: "npx", args: ["-y", "jowork@latest", "serve"] };
|
|
797
|
+
}
|
|
798
|
+
}
|
|
787
799
|
function registerCommand(program2) {
|
|
788
800
|
program2.command("register").description("Register JoWork MCP server with an AI agent engine").argument("<engine>", "Engine to register with: claude-code, codex, openclaw").action(async (engine) => {
|
|
789
801
|
switch (engine) {
|
|
@@ -820,9 +832,10 @@ function registerClaudeCode() {
|
|
|
820
832
|
}
|
|
821
833
|
}
|
|
822
834
|
if (!config.mcpServers) config.mcpServers = {};
|
|
835
|
+
const mcp = getMcpCommand();
|
|
823
836
|
config.mcpServers["jowork"] = {
|
|
824
|
-
command:
|
|
825
|
-
args:
|
|
837
|
+
command: mcp.command,
|
|
838
|
+
args: mcp.args,
|
|
826
839
|
env: { JOWORK_ENGINE: "claude-code" }
|
|
827
840
|
};
|
|
828
841
|
writeFileSync2(configPath2, JSON.stringify(config, null, 2));
|
|
@@ -849,9 +862,10 @@ function registerOpenClaw() {
|
|
|
849
862
|
}
|
|
850
863
|
}
|
|
851
864
|
if (!config["mcpServers"]) config["mcpServers"] = {};
|
|
865
|
+
const mcp = getMcpCommand();
|
|
852
866
|
config["mcpServers"]["jowork"] = {
|
|
853
|
-
command:
|
|
854
|
-
args:
|
|
867
|
+
command: mcp.command,
|
|
868
|
+
args: mcp.args,
|
|
855
869
|
env: { JOWORK_ENGINE: "openclaw" }
|
|
856
870
|
};
|
|
857
871
|
writeFileSync2(configPath2, JSON.stringify(config, null, 2));
|
|
@@ -876,10 +890,12 @@ function registerCodex() {
|
|
|
876
890
|
console.log("\u2713 JoWork already registered with Codex");
|
|
877
891
|
return;
|
|
878
892
|
}
|
|
893
|
+
const mcp = getMcpCommand();
|
|
894
|
+
const argsToml = mcp.args.map((a) => `"${a}"`).join(", ");
|
|
879
895
|
const mcpEntry = `
|
|
880
896
|
[mcp_servers.jowork]
|
|
881
|
-
command = "
|
|
882
|
-
args = [
|
|
897
|
+
command = "${mcp.command}"
|
|
898
|
+
args = [${argsToml}]
|
|
883
899
|
|
|
884
900
|
[mcp_servers.jowork.env]
|
|
885
901
|
JOWORK_ENGINE = "codex"
|
|
@@ -1950,7 +1966,7 @@ function dashboardCommand(program2) {
|
|
|
1950
1966
|
console.error("Error: JoWork not initialized. Run `jowork init` first.");
|
|
1951
1967
|
process.exit(1);
|
|
1952
1968
|
}
|
|
1953
|
-
const { startDashboard } = await import("./server-
|
|
1969
|
+
const { startDashboard } = await import("./server-5GVWN2NB.js");
|
|
1954
1970
|
const port = opts.port ? parseInt(opts.port, 10) : void 0;
|
|
1955
1971
|
const dashboard = await startDashboard({ port });
|
|
1956
1972
|
const url = `http://127.0.0.1:${dashboard.port}`;
|
|
@@ -2386,7 +2402,7 @@ function configCommand(program2) {
|
|
|
2386
2402
|
// src/commands/setup-skill.ts
|
|
2387
2403
|
import { existsSync as existsSync15, mkdirSync as mkdirSync4, writeFileSync as writeFileSync7, readFileSync as readFileSync7 } from "fs";
|
|
2388
2404
|
import { join as join10 } from "path";
|
|
2389
|
-
import { execSync } from "child_process";
|
|
2405
|
+
import { execSync as execSync2 } from "child_process";
|
|
2390
2406
|
var HOME3 = process.env["HOME"] ?? process.env["USERPROFILE"] ?? "";
|
|
2391
2407
|
var SKILL_CONTENT = `---
|
|
2392
2408
|
name: jowork
|
|
@@ -2519,12 +2535,12 @@ function setupSkillCommand(program2) {
|
|
|
2519
2535
|
console.log(" \u2713 MCP server registered with Claude Code");
|
|
2520
2536
|
} else {
|
|
2521
2537
|
try {
|
|
2522
|
-
|
|
2538
|
+
execSync2("jowork register claude-code", { stdio: "pipe" });
|
|
2523
2539
|
console.log(" \u2713 MCP server registered with Claude Code");
|
|
2524
2540
|
} catch {
|
|
2525
2541
|
try {
|
|
2526
2542
|
const cliPath = join10(__dirname, "..", "cli.js");
|
|
2527
|
-
|
|
2543
|
+
execSync2(`node "${cliPath}" register claude-code`, { stdio: "pipe" });
|
|
2528
2544
|
console.log(" \u2713 MCP server registered with Claude Code");
|
|
2529
2545
|
} catch {
|
|
2530
2546
|
console.log(" \u26A0 Could not auto-register. Run: jowork register claude-code");
|
|
@@ -2532,9 +2548,9 @@ function setupSkillCommand(program2) {
|
|
|
2532
2548
|
}
|
|
2533
2549
|
}
|
|
2534
2550
|
try {
|
|
2535
|
-
|
|
2551
|
+
execSync2("which codex", { stdio: "pipe" });
|
|
2536
2552
|
try {
|
|
2537
|
-
|
|
2553
|
+
execSync2("jowork register codex", { stdio: "pipe" });
|
|
2538
2554
|
console.log(" \u2713 Registered with Codex");
|
|
2539
2555
|
} catch {
|
|
2540
2556
|
}
|
|
@@ -2573,13 +2589,13 @@ function setupSkillCommand(program2) {
|
|
|
2573
2589
|
console.log(" Detected GITHUB_PERSONAL_ACCESS_TOKEN in environment.");
|
|
2574
2590
|
console.log(" Connecting GitHub automatically...");
|
|
2575
2591
|
try {
|
|
2576
|
-
|
|
2592
|
+
execSync2('jowork connect github --token "$GITHUB_PERSONAL_ACCESS_TOKEN"', {
|
|
2577
2593
|
stdio: "pipe",
|
|
2578
2594
|
env: process.env
|
|
2579
2595
|
});
|
|
2580
2596
|
console.log(" \u2713 GitHub connected! Running initial sync...");
|
|
2581
2597
|
try {
|
|
2582
|
-
const output =
|
|
2598
|
+
const output = execSync2("jowork sync --source github", {
|
|
2583
2599
|
encoding: "utf-8",
|
|
2584
2600
|
timeout: 3e4,
|
|
2585
2601
|
env: process.env
|
|
@@ -2624,7 +2640,7 @@ program.action(async () => {
|
|
|
2624
2640
|
const config = readConfig2();
|
|
2625
2641
|
if (!config.initialized || !existsSync16(dbPath2())) {
|
|
2626
2642
|
if (process.stdin.isTTY) {
|
|
2627
|
-
const { runSetupWizard } = await import("./setup-
|
|
2643
|
+
const { runSetupWizard } = await import("./setup-SYBQIL2O.js");
|
|
2628
2644
|
await runSetupWizard();
|
|
2629
2645
|
} else {
|
|
2630
2646
|
console.log("JoWork is not initialized. Run `jowork init` in an interactive terminal.");
|
|
@@ -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 = "";
|
|
@@ -194,7 +194,7 @@ async function runSetupWizard() {
|
|
|
194
194
|
console.log("");
|
|
195
195
|
console.log(zh ? ` \u6B63\u5728\u540C\u6B65 ${connectedSources.length} \u4E2A\u6570\u636E\u6E90...` : ` Syncing ${connectedSources.length} source${connectedSources.length > 1 ? "s" : ""}...`);
|
|
196
196
|
console.log("");
|
|
197
|
-
const { runSync } = await import("./sync-
|
|
197
|
+
const { runSync } = await import("./sync-KDSPGY4A.js");
|
|
198
198
|
try {
|
|
199
199
|
await runSync(connectedSources);
|
|
200
200
|
} catch {
|
|
@@ -242,7 +242,15 @@ async function registerEngine(engine) {
|
|
|
242
242
|
cursor: "Cursor",
|
|
243
243
|
openclaw: "OpenClaw"
|
|
244
244
|
};
|
|
245
|
-
|
|
245
|
+
let mcpCommand = "jowork";
|
|
246
|
+
let mcpArgs = ["serve"];
|
|
247
|
+
try {
|
|
248
|
+
execSync("which jowork", { stdio: "ignore" });
|
|
249
|
+
} catch {
|
|
250
|
+
mcpCommand = "npx";
|
|
251
|
+
mcpArgs = ["-y", "jowork@latest", "serve"];
|
|
252
|
+
}
|
|
253
|
+
const mcpEntry = { command: mcpCommand, args: mcpArgs };
|
|
246
254
|
switch (engine) {
|
|
247
255
|
case "claude-code": {
|
|
248
256
|
const p = join(HOME, ".claude.json");
|
|
@@ -266,7 +274,12 @@ async function registerEngine(engine) {
|
|
|
266
274
|
const p = join(dir, "config.toml");
|
|
267
275
|
let c = existsSync(p) ? readFileSync(p, "utf-8") : "";
|
|
268
276
|
if (!c.includes("[mcp_servers.jowork]")) {
|
|
269
|
-
|
|
277
|
+
const argsToml = mcpArgs.map((a) => `"${a}"`).join(", ");
|
|
278
|
+
c += `
|
|
279
|
+
[mcp_servers.jowork]
|
|
280
|
+
command = "${mcpCommand}"
|
|
281
|
+
args = [${argsToml}]
|
|
282
|
+
`;
|
|
270
283
|
writeFileSync(p, c);
|
|
271
284
|
}
|
|
272
285
|
break;
|
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.5",
|
|
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",
|