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
package/dist/cli.js
CHANGED
|
@@ -1,59 +1,70 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
FileWriter,
|
|
4
|
+
runSync,
|
|
4
5
|
syncCommand,
|
|
5
6
|
syncFirebase,
|
|
6
7
|
syncPostHog
|
|
7
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-QGHJ45PL.js";
|
|
8
9
|
import {
|
|
9
10
|
linkAllUnprocessed,
|
|
10
11
|
syncGitLab,
|
|
11
12
|
syncLinear
|
|
12
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-HENAABEL.js";
|
|
14
|
+
import {
|
|
15
|
+
syncGitHub
|
|
16
|
+
} from "./chunk-63AMINQC.js";
|
|
17
|
+
import {
|
|
18
|
+
GitManager
|
|
19
|
+
} from "./chunk-EYP6WMFF.js";
|
|
13
20
|
import {
|
|
14
21
|
DbManager
|
|
15
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-74AHY7X6.js";
|
|
16
23
|
import {
|
|
24
|
+
PLUGIN_REGISTRY,
|
|
17
25
|
createJoWorkMcpServer
|
|
18
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-ATAUWJYD.js";
|
|
19
27
|
import {
|
|
20
28
|
GoalManager
|
|
21
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-VX662YLA.js";
|
|
22
30
|
import {
|
|
23
31
|
readConfig,
|
|
24
32
|
writeConfig
|
|
25
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-FCFZCZHR.js";
|
|
26
34
|
import {
|
|
27
35
|
listCredentials,
|
|
28
36
|
loadCredential,
|
|
29
37
|
saveCredential
|
|
30
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-54SD5GBF.js";
|
|
39
|
+
import {
|
|
40
|
+
readObjectContents
|
|
41
|
+
} from "./chunk-FX6Z3QHV.js";
|
|
31
42
|
import {
|
|
32
43
|
configPath,
|
|
33
44
|
dbPath,
|
|
34
45
|
fileRepoDir,
|
|
35
46
|
joworkDir,
|
|
36
|
-
logsDir
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
syncGitHub
|
|
40
|
-
} from "./chunk-LS2AJM5A.js";
|
|
47
|
+
logsDir,
|
|
48
|
+
pluginsDir
|
|
49
|
+
} from "./chunk-4PIT2GZ4.js";
|
|
41
50
|
import {
|
|
42
51
|
syncFeishu,
|
|
43
52
|
syncFeishuApprovals,
|
|
44
53
|
syncFeishuDocs,
|
|
54
|
+
syncFeishuLinks,
|
|
45
55
|
syncFeishuMeetings
|
|
46
|
-
} from "./chunk-
|
|
47
|
-
import
|
|
48
|
-
createId
|
|
49
|
-
} from "./chunk-JE6TOU7W.js";
|
|
50
|
-
import {
|
|
51
|
-
GitManager
|
|
52
|
-
} from "./chunk-L5ZR7TSK.js";
|
|
56
|
+
} from "./chunk-DQW74UCN.js";
|
|
57
|
+
import "./chunk-RO3KK5RC.js";
|
|
53
58
|
import {
|
|
54
59
|
logError,
|
|
55
60
|
logInfo
|
|
56
61
|
} from "./chunk-MYDK7MWB.js";
|
|
62
|
+
import {
|
|
63
|
+
SyncContext
|
|
64
|
+
} from "./chunk-OXWWOKC7.js";
|
|
65
|
+
import {
|
|
66
|
+
createId
|
|
67
|
+
} from "./chunk-TFMF3EXE.js";
|
|
57
68
|
import {
|
|
58
69
|
__dirname
|
|
59
70
|
} from "./chunk-UJ4KEHGZ.js";
|
|
@@ -91,7 +102,7 @@ function initCommand(program2) {
|
|
|
91
102
|
default: true
|
|
92
103
|
}]);
|
|
93
104
|
if (continueSetup) {
|
|
94
|
-
const { runSetupWizard } = await import("./setup-
|
|
105
|
+
const { runSetupWizard } = await import("./setup-S2S2CHB2.js");
|
|
95
106
|
await runSetupWizard();
|
|
96
107
|
} else {
|
|
97
108
|
console.log("");
|
|
@@ -513,20 +524,25 @@ async function runCompaction(sqlite, provider) {
|
|
|
513
524
|
`SELECT id FROM memory_hot WHERE window_start = ? AND window_end = ?`
|
|
514
525
|
).get(window.start, window.end);
|
|
515
526
|
if (existing) continue;
|
|
516
|
-
const
|
|
517
|
-
SELECT
|
|
518
|
-
JOIN object_bodies ob ON ob.object_id = o.id
|
|
527
|
+
const windowObjs = sqlite.prepare(`
|
|
528
|
+
SELECT o.id, o.file_path FROM objects o
|
|
519
529
|
WHERE o.created_at >= ? AND o.created_at < ?
|
|
520
530
|
ORDER BY o.created_at DESC
|
|
521
531
|
LIMIT 100
|
|
522
532
|
`).all(window.start, window.end);
|
|
523
|
-
if (
|
|
524
|
-
const
|
|
533
|
+
if (windowObjs.length === 0) continue;
|
|
534
|
+
const contentMap = readObjectContents(
|
|
535
|
+
sqlite,
|
|
536
|
+
windowObjs.map((o) => ({ id: o.id, filePath: o.file_path }))
|
|
537
|
+
);
|
|
538
|
+
const contentTexts = windowObjs.map((o) => contentMap.get(o.id)).filter((c) => !!c);
|
|
539
|
+
if (contentTexts.length === 0) continue;
|
|
540
|
+
const summary = await provider.summarize(contentTexts);
|
|
525
541
|
const id = createId("hot");
|
|
526
542
|
sqlite.prepare(`
|
|
527
543
|
INSERT INTO memory_hot (id, window_start, window_end, summary, source_count, sources, created_at)
|
|
528
544
|
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
529
|
-
`).run(id, window.start, window.end, summary,
|
|
545
|
+
`).run(id, window.start, window.end, summary, windowObjs.length, null, now);
|
|
530
546
|
hotEntries++;
|
|
531
547
|
}
|
|
532
548
|
const goals = sqlite.prepare(
|
|
@@ -568,7 +584,7 @@ function serveCommand(program2) {
|
|
|
568
584
|
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
585
|
const resolvedDbPath = process.env["JOWORK_DB_PATH"] ?? dbPath();
|
|
570
586
|
if (!existsSync2(resolvedDbPath)) {
|
|
571
|
-
const { writeConfig: writeConfig2 } = await import("./config-
|
|
587
|
+
const { writeConfig: writeConfig2 } = await import("./config-FH2XLN7A.js");
|
|
572
588
|
const db = new DbManager(dbPath());
|
|
573
589
|
db.ensureTables();
|
|
574
590
|
db.close();
|
|
@@ -625,9 +641,9 @@ async function startDaemon() {
|
|
|
625
641
|
const intervalMinutes = config.syncIntervalMinutes ?? 15;
|
|
626
642
|
const cronExpr = `*/${intervalMinutes} * * * *`;
|
|
627
643
|
daemonLog("info", "Daemon started", { pid: process.pid, intervalMinutes });
|
|
628
|
-
await
|
|
644
|
+
await runSync2();
|
|
629
645
|
const _syncJob = new Cron(cronExpr, async () => {
|
|
630
|
-
await
|
|
646
|
+
await runSync2();
|
|
631
647
|
});
|
|
632
648
|
console.log(`Daemon started (PID ${process.pid})`);
|
|
633
649
|
console.log(` Sync: every ${intervalMinutes} minutes`);
|
|
@@ -651,7 +667,7 @@ function shouldSkipSource(sqlite, source, config) {
|
|
|
651
667
|
return false;
|
|
652
668
|
}
|
|
653
669
|
}
|
|
654
|
-
async function
|
|
670
|
+
async function runSync2() {
|
|
655
671
|
const sources = listCredentials();
|
|
656
672
|
if (sources.length === 0) {
|
|
657
673
|
daemonLog("info", "No sources connected, skipping sync");
|
|
@@ -685,51 +701,63 @@ async function runSync() {
|
|
|
685
701
|
try {
|
|
686
702
|
switch (source) {
|
|
687
703
|
case "feishu": {
|
|
688
|
-
const
|
|
704
|
+
const feishuCtx = new SyncContext(sqlite, daemonSyncLogger, fileWriter);
|
|
705
|
+
const r = await syncFeishu(feishuCtx, cred.data, daemonSyncLogger);
|
|
689
706
|
syncResults.push({ source: "feishu", newObjects: r.newMessages, label: "messages" });
|
|
690
707
|
try {
|
|
691
|
-
const mr = await syncFeishuMeetings(
|
|
708
|
+
const mr = await syncFeishuMeetings(feishuCtx, cred.data, daemonSyncLogger);
|
|
692
709
|
syncResults.push({ source: "feishu/meetings", newObjects: mr.newObjects, label: "events" });
|
|
693
710
|
} catch (e) {
|
|
694
711
|
daemonLog("warn", `Feishu meetings sync: ${e}`);
|
|
695
712
|
}
|
|
696
713
|
try {
|
|
697
|
-
const dr = await syncFeishuDocs(
|
|
714
|
+
const dr = await syncFeishuDocs(feishuCtx, cred.data, daemonSyncLogger);
|
|
698
715
|
syncResults.push({ source: "feishu/docs", newObjects: dr.newObjects, label: "docs" });
|
|
699
716
|
} catch (e) {
|
|
700
717
|
daemonLog("warn", `Feishu docs sync: ${e}`);
|
|
701
718
|
}
|
|
702
719
|
try {
|
|
703
|
-
const ar = await syncFeishuApprovals(
|
|
720
|
+
const ar = await syncFeishuApprovals(feishuCtx, cred.data, daemonSyncLogger);
|
|
704
721
|
syncResults.push({ source: "feishu/approvals", newObjects: ar.newObjects, label: "approvals" });
|
|
705
722
|
} catch (e) {
|
|
706
723
|
daemonLog("warn", `Feishu approvals sync: ${e}`);
|
|
707
724
|
}
|
|
725
|
+
try {
|
|
726
|
+
const lr = await syncFeishuLinks(feishuCtx, cred.data, daemonSyncLogger);
|
|
727
|
+
syncResults.push({ source: "feishu/links", newObjects: lr.fetched, label: "links" });
|
|
728
|
+
} catch (e) {
|
|
729
|
+
daemonLog("warn", `Feishu links sync: ${e}`);
|
|
730
|
+
}
|
|
708
731
|
break;
|
|
709
732
|
}
|
|
710
733
|
case "github": {
|
|
711
|
-
const
|
|
712
|
-
|
|
734
|
+
const ghCtx = new SyncContext(sqlite, daemonSyncLogger, fileWriter);
|
|
735
|
+
const r = await syncGitHub(ghCtx, cred.data, daemonSyncLogger);
|
|
736
|
+
syncResults.push({ source: "github", newObjects: r.newObjects + r.updatedObjects });
|
|
713
737
|
break;
|
|
714
738
|
}
|
|
715
739
|
case "gitlab": {
|
|
716
|
-
const
|
|
717
|
-
|
|
740
|
+
const glCtx = new SyncContext(sqlite, daemonSyncLogger, fileWriter);
|
|
741
|
+
const r = await syncGitLab(glCtx, cred.data, daemonSyncLogger);
|
|
742
|
+
syncResults.push({ source: "gitlab", newObjects: r.newObjects + r.updatedObjects });
|
|
718
743
|
break;
|
|
719
744
|
}
|
|
720
745
|
case "linear": {
|
|
721
|
-
const
|
|
722
|
-
|
|
746
|
+
const lnCtx = new SyncContext(sqlite, daemonSyncLogger, fileWriter);
|
|
747
|
+
const r = await syncLinear(lnCtx, cred.data, daemonSyncLogger);
|
|
748
|
+
syncResults.push({ source: "linear", newObjects: r.newObjects + r.updatedObjects, label: "issues" });
|
|
723
749
|
break;
|
|
724
750
|
}
|
|
725
751
|
case "posthog": {
|
|
726
|
-
const
|
|
727
|
-
|
|
752
|
+
const phCtx = new SyncContext(sqlite, daemonSyncLogger, fileWriter);
|
|
753
|
+
const r = await syncPostHog(phCtx, cred.data, daemonSyncLogger);
|
|
754
|
+
syncResults.push({ source: "posthog", newObjects: r.newObjects + r.updatedObjects });
|
|
728
755
|
break;
|
|
729
756
|
}
|
|
730
757
|
case "firebase": {
|
|
731
|
-
const
|
|
732
|
-
|
|
758
|
+
const fbCtx = new SyncContext(sqlite, daemonSyncLogger, fileWriter);
|
|
759
|
+
const r = await syncFirebase(fbCtx, cred.data, daemonSyncLogger);
|
|
760
|
+
syncResults.push({ source: "firebase", newObjects: r.newObjects + r.updatedObjects, label: "events" });
|
|
733
761
|
break;
|
|
734
762
|
}
|
|
735
763
|
default:
|
|
@@ -954,9 +982,8 @@ function statusCommand(program2) {
|
|
|
954
982
|
let sourceRows = [];
|
|
955
983
|
try {
|
|
956
984
|
sourceRows = sqlite.prepare(`
|
|
957
|
-
SELECT o.source, COUNT(*) as count, COALESCE(SUM(LENGTH(
|
|
985
|
+
SELECT o.source, COUNT(*) as count, COALESCE(SUM(LENGTH(o.summary)), 0) as size
|
|
958
986
|
FROM objects o
|
|
959
|
-
LEFT JOIN object_bodies ob ON ob.object_id = o.id
|
|
960
987
|
GROUP BY o.source
|
|
961
988
|
ORDER BY o.source
|
|
962
989
|
`).all();
|
|
@@ -1170,6 +1197,46 @@ function exportCommand(program2) {
|
|
|
1170
1197
|
}
|
|
1171
1198
|
|
|
1172
1199
|
// src/commands/connect.ts
|
|
1200
|
+
import { execFileSync } from "child_process";
|
|
1201
|
+
import { existsSync as existsSync7, writeFileSync as writeFileSync4, readFileSync as readFileSync4 } from "fs";
|
|
1202
|
+
import { join as join5 } from "path";
|
|
1203
|
+
var SYNCABLE_SOURCES = /* @__PURE__ */ new Set(["feishu", "github", "gitlab", "linear", "posthog", "firebase"]);
|
|
1204
|
+
async function autoSync(source) {
|
|
1205
|
+
if (!SYNCABLE_SOURCES.has(source)) return;
|
|
1206
|
+
console.log(`
|
|
1207
|
+
Running initial sync for ${source}...`);
|
|
1208
|
+
try {
|
|
1209
|
+
await runSync([source]);
|
|
1210
|
+
} catch {
|
|
1211
|
+
console.log(`\u26A0 Initial sync failed. You can retry with: jowork sync --source ${source}`);
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
function autoInstallPlugin(source) {
|
|
1215
|
+
const pluginDef = PLUGIN_REGISTRY[source];
|
|
1216
|
+
if (!pluginDef) return;
|
|
1217
|
+
const dir = pluginsDir();
|
|
1218
|
+
const pkgJsonPath = join5(dir, "package.json");
|
|
1219
|
+
if (!existsSync7(pkgJsonPath)) {
|
|
1220
|
+
writeFileSync4(pkgJsonPath, JSON.stringify({
|
|
1221
|
+
name: "jowork-plugins",
|
|
1222
|
+
private: true,
|
|
1223
|
+
dependencies: {}
|
|
1224
|
+
}, null, 2));
|
|
1225
|
+
}
|
|
1226
|
+
const pkgJson = JSON.parse(readFileSync4(pkgJsonPath, "utf-8"));
|
|
1227
|
+
if (pkgJson.dependencies?.[pluginDef.package]) return;
|
|
1228
|
+
console.log(` Installing MCP plugin: ${pluginDef.package}...`);
|
|
1229
|
+
try {
|
|
1230
|
+
execFileSync("npm", ["install", "--save", pluginDef.package], {
|
|
1231
|
+
cwd: dir,
|
|
1232
|
+
stdio: "pipe",
|
|
1233
|
+
timeout: 6e4
|
|
1234
|
+
});
|
|
1235
|
+
console.log(` \u2713 ${pluginDef.name} MCP plugin installed`);
|
|
1236
|
+
} catch (err) {
|
|
1237
|
+
console.log(` \u26A0 Plugin install failed. You can install manually: cd ${dir} && npm install ${pluginDef.package}`);
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1173
1240
|
function connectCommand(program2) {
|
|
1174
1241
|
program2.command("connect").description("Connect a data source").argument("<source>", "Data source: feishu, github, gitlab, linear, posthog, slack, telegram, firebase").option("--app-id <id>", "App ID (for Feishu)").option("--app-secret <secret>", "App Secret (for Feishu)").option("--token <token>", "Access token (for GitHub/GitLab)").option("--api-url <url>", "API base URL (for GitLab self-hosted)").option("--api-key <key>", "API key (for Linear/PostHog/Firebase)").option("--host <host>", "API host (for PostHog self-hosted)").option("--project-id <id>", "Project ID (for PostHog/Firebase)").option("--webhook-url <url>", "Webhook URL (for Slack)").option("--bot-token <token>", "Bot token (for Telegram)").option("--chat-id <id>", "Chat ID (for Telegram)").option("--property-id <id>", "Property ID (for Firebase GA4)").action(async (source, opts) => {
|
|
1175
1242
|
switch (source) {
|
|
@@ -1231,6 +1298,7 @@ async function connectFeishu(opts) {
|
|
|
1231
1298
|
const data = await res.json();
|
|
1232
1299
|
if (data.code !== 0) {
|
|
1233
1300
|
console.error(`Feishu auth failed: ${data.msg}`);
|
|
1301
|
+
console.error(" Hint: App ID/Secret invalid. Check at https://open.feishu.cn/app");
|
|
1234
1302
|
process.exit(1);
|
|
1235
1303
|
}
|
|
1236
1304
|
console.log("\u2713 Feishu credentials verified");
|
|
@@ -1244,7 +1312,9 @@ async function connectFeishu(opts) {
|
|
|
1244
1312
|
createdAt: Date.now(),
|
|
1245
1313
|
updatedAt: Date.now()
|
|
1246
1314
|
});
|
|
1247
|
-
console.log("\u2713 Feishu connected.
|
|
1315
|
+
console.log("\u2713 Feishu connected.");
|
|
1316
|
+
autoInstallPlugin("feishu");
|
|
1317
|
+
await autoSync("feishu");
|
|
1248
1318
|
}
|
|
1249
1319
|
async function connectGitHub(opts) {
|
|
1250
1320
|
let token = opts.token;
|
|
@@ -1260,6 +1330,22 @@ async function connectGitHub(opts) {
|
|
|
1260
1330
|
console.error("Error: GitHub token is required.");
|
|
1261
1331
|
process.exit(1);
|
|
1262
1332
|
}
|
|
1333
|
+
console.log("Verifying GitHub credentials...");
|
|
1334
|
+
try {
|
|
1335
|
+
const res = await fetch("https://api.github.com/user", {
|
|
1336
|
+
headers: { Authorization: `Bearer ${token}`, "User-Agent": "jowork" }
|
|
1337
|
+
});
|
|
1338
|
+
if (!res.ok) {
|
|
1339
|
+
console.error(`GitHub auth failed: HTTP ${res.status}`);
|
|
1340
|
+
console.error(" Hint: Token invalid or expired. Create a new one at https://github.com/settings/tokens");
|
|
1341
|
+
process.exit(1);
|
|
1342
|
+
}
|
|
1343
|
+
const user = await res.json();
|
|
1344
|
+
console.log(`\u2713 GitHub credentials verified (user: ${user.login})`);
|
|
1345
|
+
} catch (err) {
|
|
1346
|
+
console.error(`Network error: ${err}`);
|
|
1347
|
+
process.exit(1);
|
|
1348
|
+
}
|
|
1263
1349
|
saveCredential("github", {
|
|
1264
1350
|
type: "github",
|
|
1265
1351
|
data: { token },
|
|
@@ -1267,6 +1353,8 @@ async function connectGitHub(opts) {
|
|
|
1267
1353
|
updatedAt: Date.now()
|
|
1268
1354
|
});
|
|
1269
1355
|
console.log("\u2713 GitHub connected.");
|
|
1356
|
+
autoInstallPlugin("github");
|
|
1357
|
+
await autoSync("github");
|
|
1270
1358
|
}
|
|
1271
1359
|
async function connectGitLab(opts) {
|
|
1272
1360
|
let token = opts.token;
|
|
@@ -1294,6 +1382,7 @@ async function connectGitLab(opts) {
|
|
|
1294
1382
|
});
|
|
1295
1383
|
if (!res.ok) {
|
|
1296
1384
|
console.error(`GitLab auth failed: HTTP ${res.status}`);
|
|
1385
|
+
console.error(" Hint: Token invalid. Check at https://gitlab.com/-/profile/personal_access_tokens");
|
|
1297
1386
|
process.exit(1);
|
|
1298
1387
|
}
|
|
1299
1388
|
const user = await res.json();
|
|
@@ -1310,7 +1399,9 @@ async function connectGitLab(opts) {
|
|
|
1310
1399
|
createdAt: Date.now(),
|
|
1311
1400
|
updatedAt: Date.now()
|
|
1312
1401
|
});
|
|
1313
|
-
console.log("\u2713 GitLab connected.
|
|
1402
|
+
console.log("\u2713 GitLab connected.");
|
|
1403
|
+
autoInstallPlugin("gitlab");
|
|
1404
|
+
await autoSync("gitlab");
|
|
1314
1405
|
}
|
|
1315
1406
|
async function connectLinear(opts) {
|
|
1316
1407
|
let apiKey = opts.apiKey;
|
|
@@ -1338,11 +1429,13 @@ async function connectLinear(opts) {
|
|
|
1338
1429
|
});
|
|
1339
1430
|
if (!res.ok) {
|
|
1340
1431
|
console.error(`Linear auth failed: HTTP ${res.status}`);
|
|
1432
|
+
console.error(" Hint: API key invalid. Get one at https://linear.app/settings/api");
|
|
1341
1433
|
process.exit(1);
|
|
1342
1434
|
}
|
|
1343
1435
|
const data = await res.json();
|
|
1344
1436
|
if (data.errors?.length) {
|
|
1345
1437
|
console.error(`Linear auth failed: ${data.errors[0].message}`);
|
|
1438
|
+
console.error(" Hint: API key invalid. Get one at https://linear.app/settings/api");
|
|
1346
1439
|
process.exit(1);
|
|
1347
1440
|
}
|
|
1348
1441
|
console.log(`\u2713 Linear credentials verified (user: ${data.data?.viewer?.name})`);
|
|
@@ -1356,7 +1449,9 @@ async function connectLinear(opts) {
|
|
|
1356
1449
|
createdAt: Date.now(),
|
|
1357
1450
|
updatedAt: Date.now()
|
|
1358
1451
|
});
|
|
1359
|
-
console.log("\u2713 Linear connected.
|
|
1452
|
+
console.log("\u2713 Linear connected.");
|
|
1453
|
+
autoInstallPlugin("linear");
|
|
1454
|
+
await autoSync("linear");
|
|
1360
1455
|
}
|
|
1361
1456
|
async function connectPostHog(opts) {
|
|
1362
1457
|
let apiKey = opts.apiKey;
|
|
@@ -1379,7 +1474,11 @@ async function connectPostHog(opts) {
|
|
|
1379
1474
|
const res = await fetch(`${host}/api/projects/${projectId}/`, {
|
|
1380
1475
|
headers: { Authorization: `Bearer ${apiKey}` }
|
|
1381
1476
|
});
|
|
1382
|
-
if (!res.ok)
|
|
1477
|
+
if (!res.ok) {
|
|
1478
|
+
console.error(`PostHog auth failed: HTTP ${res.status}`);
|
|
1479
|
+
console.error(" Hint: API key invalid. Get one at https://app.posthog.com/project/settings");
|
|
1480
|
+
process.exit(1);
|
|
1481
|
+
}
|
|
1383
1482
|
console.log("\u2713 PostHog credentials verified");
|
|
1384
1483
|
} catch (err) {
|
|
1385
1484
|
console.error(`PostHog verification failed: ${err}`);
|
|
@@ -1391,7 +1490,9 @@ async function connectPostHog(opts) {
|
|
|
1391
1490
|
createdAt: Date.now(),
|
|
1392
1491
|
updatedAt: Date.now()
|
|
1393
1492
|
});
|
|
1394
|
-
console.log("\u2713 PostHog connected.
|
|
1493
|
+
console.log("\u2713 PostHog connected.");
|
|
1494
|
+
autoInstallPlugin("posthog");
|
|
1495
|
+
await autoSync("posthog");
|
|
1395
1496
|
}
|
|
1396
1497
|
async function connectSlack(opts) {
|
|
1397
1498
|
let webhookUrl = opts.webhookUrl;
|
|
@@ -1461,22 +1562,40 @@ async function connectFirebase(opts) {
|
|
|
1461
1562
|
console.error("Error: API key required.");
|
|
1462
1563
|
process.exit(1);
|
|
1463
1564
|
}
|
|
1464
|
-
const
|
|
1465
|
-
|
|
1565
|
+
const propId = propertyId || projectId;
|
|
1566
|
+
console.log("Verifying Firebase/GA4 credentials...");
|
|
1567
|
+
try {
|
|
1568
|
+
const res = await fetch(
|
|
1569
|
+
`https://analyticsdata.googleapis.com/v1beta/properties/${propId}/metadata?key=${apiKey}`
|
|
1570
|
+
);
|
|
1571
|
+
if (!res.ok) {
|
|
1572
|
+
console.error(`Firebase/GA4 auth failed: HTTP ${res.status}`);
|
|
1573
|
+
console.error(" Hint: Check your API key and Property ID at https://console.cloud.google.com");
|
|
1574
|
+
process.exit(1);
|
|
1575
|
+
}
|
|
1576
|
+
console.log("\u2713 Firebase/GA4 credentials verified");
|
|
1577
|
+
} catch (err) {
|
|
1578
|
+
console.error(`Network error: ${err}`);
|
|
1579
|
+
process.exit(1);
|
|
1580
|
+
}
|
|
1581
|
+
const credData = { projectId, apiKey };
|
|
1582
|
+
if (propertyId) credData.propertyId = propertyId;
|
|
1466
1583
|
saveCredential("firebase", {
|
|
1467
1584
|
type: "firebase",
|
|
1468
|
-
data,
|
|
1585
|
+
data: credData,
|
|
1469
1586
|
createdAt: Date.now(),
|
|
1470
1587
|
updatedAt: Date.now()
|
|
1471
1588
|
});
|
|
1472
|
-
console.log("\u2713 Firebase connected.
|
|
1589
|
+
console.log("\u2713 Firebase connected.");
|
|
1590
|
+
autoInstallPlugin("firebase");
|
|
1591
|
+
await autoSync("firebase");
|
|
1473
1592
|
}
|
|
1474
1593
|
|
|
1475
1594
|
// src/commands/search.ts
|
|
1476
|
-
import { existsSync as
|
|
1595
|
+
import { existsSync as existsSync8 } from "fs";
|
|
1477
1596
|
function searchCommand(program2) {
|
|
1478
1597
|
program2.command("search").description("Search across all synced data").argument("<query>", "Search keywords").option("--source <source>", "Filter by source").option("--limit <n>", "Max results", "20").action(async (query, opts) => {
|
|
1479
|
-
if (!
|
|
1598
|
+
if (!existsSync8(dbPath())) {
|
|
1480
1599
|
console.error("Error: JoWork not initialized. Run `jowork init` first.");
|
|
1481
1600
|
process.exit(1);
|
|
1482
1601
|
}
|
|
@@ -1627,8 +1746,8 @@ function goalCommand(program2) {
|
|
|
1627
1746
|
}
|
|
1628
1747
|
|
|
1629
1748
|
// src/commands/install-service.ts
|
|
1630
|
-
import { writeFileSync as
|
|
1631
|
-
import { join as
|
|
1749
|
+
import { writeFileSync as writeFileSync5, existsSync as existsSync9, unlinkSync as unlinkSync2, mkdirSync as mkdirSync3 } from "fs";
|
|
1750
|
+
import { join as join6 } from "path";
|
|
1632
1751
|
var HOME2 = process.env["HOME"] ?? "";
|
|
1633
1752
|
function installServiceCommand(program2) {
|
|
1634
1753
|
program2.command("install-service").description("Generate system service config (macOS LaunchAgent / Linux systemd)").option("--uninstall", "Remove the service").action(async (opts) => {
|
|
@@ -1642,9 +1761,9 @@ function installServiceCommand(program2) {
|
|
|
1642
1761
|
});
|
|
1643
1762
|
}
|
|
1644
1763
|
function installLaunchAgent(uninstall) {
|
|
1645
|
-
const plistPath =
|
|
1764
|
+
const plistPath = join6(HOME2, "Library", "LaunchAgents", "work.jowork.daemon.plist");
|
|
1646
1765
|
if (uninstall) {
|
|
1647
|
-
if (
|
|
1766
|
+
if (existsSync9(plistPath)) {
|
|
1648
1767
|
unlinkSync2(plistPath);
|
|
1649
1768
|
console.log(`\u2713 Removed ${plistPath}`);
|
|
1650
1769
|
console.log(" Run: launchctl unload work.jowork.daemon");
|
|
@@ -1655,7 +1774,7 @@ function installLaunchAgent(uninstall) {
|
|
|
1655
1774
|
}
|
|
1656
1775
|
const joworkBin = process.argv[0];
|
|
1657
1776
|
const joworkScript = process.argv[1];
|
|
1658
|
-
const logsPath =
|
|
1777
|
+
const logsPath = join6(joworkDir(), "logs");
|
|
1659
1778
|
mkdirSync3(logsPath, { recursive: true });
|
|
1660
1779
|
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
1661
1780
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
@@ -1687,7 +1806,7 @@ function installLaunchAgent(uninstall) {
|
|
|
1687
1806
|
</dict>
|
|
1688
1807
|
</dict>
|
|
1689
1808
|
</plist>`;
|
|
1690
|
-
|
|
1809
|
+
writeFileSync5(plistPath, plist);
|
|
1691
1810
|
console.log(`\u2713 LaunchAgent written to ${plistPath}`);
|
|
1692
1811
|
console.log("");
|
|
1693
1812
|
console.log("To start:");
|
|
@@ -1698,10 +1817,10 @@ function installLaunchAgent(uninstall) {
|
|
|
1698
1817
|
console.log(" jowork install-service --uninstall");
|
|
1699
1818
|
}
|
|
1700
1819
|
function installSystemd(uninstall) {
|
|
1701
|
-
const serviceDir =
|
|
1702
|
-
const servicePath =
|
|
1820
|
+
const serviceDir = join6(HOME2, ".config", "systemd", "user");
|
|
1821
|
+
const servicePath = join6(serviceDir, "jowork.service");
|
|
1703
1822
|
if (uninstall) {
|
|
1704
|
-
if (
|
|
1823
|
+
if (existsSync9(servicePath)) {
|
|
1705
1824
|
unlinkSync2(servicePath);
|
|
1706
1825
|
console.log(`\u2713 Removed ${servicePath}`);
|
|
1707
1826
|
console.log(" Run: systemctl --user disable jowork");
|
|
@@ -1725,7 +1844,7 @@ RestartSec=10
|
|
|
1725
1844
|
[Install]
|
|
1726
1845
|
WantedBy=default.target
|
|
1727
1846
|
`;
|
|
1728
|
-
|
|
1847
|
+
writeFileSync5(servicePath, service);
|
|
1729
1848
|
console.log(`\u2713 Systemd service written to ${servicePath}`);
|
|
1730
1849
|
console.log("");
|
|
1731
1850
|
console.log("To start:");
|
|
@@ -1734,10 +1853,11 @@ WantedBy=default.target
|
|
|
1734
1853
|
}
|
|
1735
1854
|
|
|
1736
1855
|
// src/commands/gc.ts
|
|
1737
|
-
import { existsSync as
|
|
1856
|
+
import { existsSync as existsSync10, statSync as statSync2, unlinkSync as unlinkSync3 } from "fs";
|
|
1857
|
+
import { join as join7 } from "path";
|
|
1738
1858
|
function gcCommand(program2) {
|
|
1739
1859
|
program2.command("gc").description("Garbage collect: remove old data, vacuum database").option("--retention-days <days>", "Override retention days (0 = keep all)").option("--dry-run", "Show what would be deleted without deleting").action(async (opts) => {
|
|
1740
|
-
if (!
|
|
1860
|
+
if (!existsSync10(dbPath())) {
|
|
1741
1861
|
console.error("Error: JoWork not initialized. Run `jowork init` first.");
|
|
1742
1862
|
process.exit(1);
|
|
1743
1863
|
}
|
|
@@ -1749,31 +1869,40 @@ function gcCommand(program2) {
|
|
|
1749
1869
|
const sqlite = db.getSqlite();
|
|
1750
1870
|
const sizeBefore = statSync2(dbPath()).size;
|
|
1751
1871
|
console.log(`Database size: ${(sizeBefore / 1024 / 1024).toFixed(2)} MB`);
|
|
1752
|
-
let deletedBodies = 0;
|
|
1753
1872
|
let deletedObjects = 0;
|
|
1873
|
+
let deletedFiles = 0;
|
|
1754
1874
|
if (retentionDays > 0) {
|
|
1755
1875
|
const cutoff = Date.now() - retentionDays * 24 * 60 * 60 * 1e3;
|
|
1756
1876
|
if (dryRun) {
|
|
1757
1877
|
const count = sqlite.prepare(
|
|
1758
|
-
`SELECT COUNT(*) as c FROM
|
|
1759
|
-
JOIN objects o ON o.id = ob.object_id
|
|
1760
|
-
WHERE o.created_at < ?`
|
|
1878
|
+
`SELECT COUNT(*) as c FROM objects WHERE created_at < ?`
|
|
1761
1879
|
).get(cutoff).c;
|
|
1762
|
-
console.log(`Would delete ${count}
|
|
1880
|
+
console.log(`Would delete ${count} objects older than ${retentionDays} days`);
|
|
1763
1881
|
} else {
|
|
1764
|
-
const
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
)`
|
|
1768
|
-
).run(cutoff);
|
|
1769
|
-
deletedBodies = result.changes;
|
|
1770
|
-
const objResult = sqlite.prepare(
|
|
1771
|
-
`DELETE FROM objects WHERE created_at < ?
|
|
1772
|
-
AND id NOT IN (SELECT object_id FROM object_bodies)
|
|
1882
|
+
const repoDir = fileRepoDir();
|
|
1883
|
+
const oldObjects = sqlite.prepare(
|
|
1884
|
+
`SELECT id, file_path FROM objects WHERE created_at < ?
|
|
1773
1885
|
AND id NOT IN (SELECT source_object_id FROM object_links)`
|
|
1774
|
-
).
|
|
1775
|
-
|
|
1776
|
-
|
|
1886
|
+
).all(cutoff);
|
|
1887
|
+
for (const obj of oldObjects) {
|
|
1888
|
+
if (obj.file_path) {
|
|
1889
|
+
try {
|
|
1890
|
+
unlinkSync3(join7(repoDir, obj.file_path));
|
|
1891
|
+
deletedFiles++;
|
|
1892
|
+
} catch {
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1896
|
+
if (oldObjects.length > 0) {
|
|
1897
|
+
const ids = oldObjects.map((o) => o.id);
|
|
1898
|
+
for (let i = 0; i < ids.length; i += 100) {
|
|
1899
|
+
const batch = ids.slice(i, i + 100);
|
|
1900
|
+
const placeholders = batch.map(() => "?").join(",");
|
|
1901
|
+
sqlite.prepare(`DELETE FROM objects WHERE id IN (${placeholders})`).run(...batch);
|
|
1902
|
+
}
|
|
1903
|
+
deletedObjects = oldObjects.length;
|
|
1904
|
+
}
|
|
1905
|
+
console.log(`Deleted ${deletedObjects} old objects, ${deletedFiles} files from disk`);
|
|
1777
1906
|
}
|
|
1778
1907
|
} else {
|
|
1779
1908
|
console.log("Retention: keep all (set retentionDays in config or use --retention-days)");
|
|
@@ -1785,8 +1914,8 @@ function gcCommand(program2) {
|
|
|
1785
1914
|
const saved = sizeBefore - sizeAfter;
|
|
1786
1915
|
console.log(`VACUUM complete: ${(sizeAfter / 1024 / 1024).toFixed(2)} MB (saved ${(saved / 1024).toFixed(0)} KB)`);
|
|
1787
1916
|
logInfo("gc", "Garbage collection complete", {
|
|
1788
|
-
deletedBodies,
|
|
1789
1917
|
deletedObjects,
|
|
1918
|
+
deletedFiles,
|
|
1790
1919
|
sizeBefore,
|
|
1791
1920
|
sizeAfter,
|
|
1792
1921
|
savedBytes: saved
|
|
@@ -1804,18 +1933,18 @@ function gcCommand(program2) {
|
|
|
1804
1933
|
}
|
|
1805
1934
|
|
|
1806
1935
|
// src/commands/device-sync.ts
|
|
1807
|
-
import { existsSync as
|
|
1936
|
+
import { existsSync as existsSync12, readFileSync as readFileSync6, writeFileSync as writeFileSync7 } from "fs";
|
|
1808
1937
|
|
|
1809
1938
|
// src/sync/device-sync.ts
|
|
1810
|
-
import { readFileSync as
|
|
1811
|
-
import { join as
|
|
1939
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync6, existsSync as existsSync11 } from "fs";
|
|
1940
|
+
import { join as join8 } from "path";
|
|
1812
1941
|
function getDeviceId() {
|
|
1813
|
-
const idPath =
|
|
1814
|
-
if (
|
|
1815
|
-
return
|
|
1942
|
+
const idPath = join8(joworkDir(), "device-id");
|
|
1943
|
+
if (existsSync11(idPath)) {
|
|
1944
|
+
return readFileSync5(idPath, "utf-8").trim();
|
|
1816
1945
|
}
|
|
1817
1946
|
const id = createId("dev");
|
|
1818
|
-
|
|
1947
|
+
writeFileSync6(idPath, id);
|
|
1819
1948
|
return id;
|
|
1820
1949
|
}
|
|
1821
1950
|
function getUnsyncedChanges(sqlite, limit = 100) {
|
|
@@ -1910,7 +2039,7 @@ function importSyncBundle(sqlite, bundleJson) {
|
|
|
1910
2039
|
function deviceSyncCommand(program2) {
|
|
1911
2040
|
const sync = program2.command("device-sync").description("Sync data between devices");
|
|
1912
2041
|
sync.command("status").description("Show device sync status").action(async () => {
|
|
1913
|
-
if (!
|
|
2042
|
+
if (!existsSync12(dbPath())) {
|
|
1914
2043
|
console.error("Error: JoWork not initialized. Run `jowork init` first.");
|
|
1915
2044
|
process.exit(1);
|
|
1916
2045
|
}
|
|
@@ -1925,7 +2054,7 @@ function deviceSyncCommand(program2) {
|
|
|
1925
2054
|
db.close();
|
|
1926
2055
|
});
|
|
1927
2056
|
sync.command("export").description("Export unsynced changes to a file").argument("<file>", "Output file path").action(async (file) => {
|
|
1928
|
-
if (!
|
|
2057
|
+
if (!existsSync12(dbPath())) {
|
|
1929
2058
|
console.error("Error: JoWork not initialized. Run `jowork init` first.");
|
|
1930
2059
|
process.exit(1);
|
|
1931
2060
|
}
|
|
@@ -1933,24 +2062,24 @@ function deviceSyncCommand(program2) {
|
|
|
1933
2062
|
db.ensureTables();
|
|
1934
2063
|
const sqlite = db.getSqlite();
|
|
1935
2064
|
const bundle = exportSyncBundle(sqlite);
|
|
1936
|
-
|
|
2065
|
+
writeFileSync7(file, bundle);
|
|
1937
2066
|
const parsed = JSON.parse(bundle);
|
|
1938
2067
|
console.log(`Exported ${parsed.changes.length} changes to ${file}`);
|
|
1939
2068
|
markSynced(sqlite, parsed.changes.map((c) => c.id));
|
|
1940
2069
|
db.close();
|
|
1941
2070
|
});
|
|
1942
2071
|
sync.command("import").description("Import changes from another device").argument("<file>", "Input file path").action(async (file) => {
|
|
1943
|
-
if (!
|
|
2072
|
+
if (!existsSync12(file)) {
|
|
1944
2073
|
console.error(`File not found: ${file}`);
|
|
1945
2074
|
process.exit(1);
|
|
1946
2075
|
}
|
|
1947
|
-
if (!
|
|
2076
|
+
if (!existsSync12(dbPath())) {
|
|
1948
2077
|
console.error("Error: JoWork not initialized. Run `jowork init` first.");
|
|
1949
2078
|
process.exit(1);
|
|
1950
2079
|
}
|
|
1951
2080
|
const db = new DbManager(dbPath());
|
|
1952
2081
|
db.ensureTables();
|
|
1953
|
-
const bundleJson =
|
|
2082
|
+
const bundleJson = readFileSync6(file, "utf-8");
|
|
1954
2083
|
const result = importSyncBundle(db.getSqlite(), bundleJson);
|
|
1955
2084
|
console.log(`Applied ${result.applied}, conflicts ${result.conflicts}, skipped ${result.skipped}`);
|
|
1956
2085
|
db.close();
|
|
@@ -1958,15 +2087,15 @@ function deviceSyncCommand(program2) {
|
|
|
1958
2087
|
}
|
|
1959
2088
|
|
|
1960
2089
|
// src/commands/dashboard.ts
|
|
1961
|
-
import { existsSync as
|
|
2090
|
+
import { existsSync as existsSync13 } from "fs";
|
|
1962
2091
|
import { exec } from "child_process";
|
|
1963
2092
|
function dashboardCommand(program2) {
|
|
1964
2093
|
program2.command("dashboard").description("Open the JoWork companion dashboard in your browser").option("-p, --port <port>", "Port number (default: 18801)").option("--no-open", "Do not auto-open browser").action(async (opts) => {
|
|
1965
|
-
if (!
|
|
2094
|
+
if (!existsSync13(dbPath())) {
|
|
1966
2095
|
console.error("Error: JoWork not initialized. Run `jowork init` first.");
|
|
1967
2096
|
process.exit(1);
|
|
1968
2097
|
}
|
|
1969
|
-
const { startDashboard } = await import("./server-
|
|
2098
|
+
const { startDashboard } = await import("./server-WEADPUST.js");
|
|
1970
2099
|
const port = opts.port ? parseInt(opts.port, 10) : void 0;
|
|
1971
2100
|
const dashboard = await startDashboard({ port });
|
|
1972
2101
|
const url = `http://127.0.0.1:${dashboard.port}`;
|
|
@@ -1991,12 +2120,12 @@ function dashboardCommand(program2) {
|
|
|
1991
2120
|
}
|
|
1992
2121
|
|
|
1993
2122
|
// src/commands/log.ts
|
|
1994
|
-
import { existsSync as
|
|
1995
|
-
import { join as
|
|
2123
|
+
import { existsSync as existsSync14 } from "fs";
|
|
2124
|
+
import { join as join9 } from "path";
|
|
1996
2125
|
function logCommand(program2) {
|
|
1997
2126
|
program2.command("log").description("Show data sync history").option("-n, --limit <n>", "Number of entries", "20").action(async (opts) => {
|
|
1998
2127
|
const repoDir = fileRepoDir();
|
|
1999
|
-
if (!
|
|
2128
|
+
if (!existsSync14(join9(repoDir, ".git"))) {
|
|
2000
2129
|
console.log("No sync history yet. Run `jowork sync` first.");
|
|
2001
2130
|
return;
|
|
2002
2131
|
}
|
|
@@ -2014,12 +2143,12 @@ function logCommand(program2) {
|
|
|
2014
2143
|
}
|
|
2015
2144
|
|
|
2016
2145
|
// src/commands/push.ts
|
|
2017
|
-
import { existsSync as
|
|
2018
|
-
import { join as
|
|
2146
|
+
import { existsSync as existsSync15 } from "fs";
|
|
2147
|
+
import { join as join11 } from "path";
|
|
2019
2148
|
|
|
2020
2149
|
// src/sync/push-back.ts
|
|
2021
|
-
import { readFileSync as
|
|
2022
|
-
import { join as
|
|
2150
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
2151
|
+
import { join as join10 } from "path";
|
|
2023
2152
|
function parseFilePath(filePath) {
|
|
2024
2153
|
const githubMatch = filePath.match(
|
|
2025
2154
|
/^github\/([^/]+)\/(issues|pulls)\/(\d+)\.md$/
|
|
@@ -2087,7 +2216,7 @@ async function pushChanges(repoDir) {
|
|
|
2087
2216
|
for (const file of modifiedFiles) {
|
|
2088
2217
|
const parsed = parseFilePath(file);
|
|
2089
2218
|
if (!parsed) continue;
|
|
2090
|
-
const content =
|
|
2219
|
+
const content = readFileSync7(join10(repoDir, file), "utf-8");
|
|
2091
2220
|
const fm = parseFrontmatter(content);
|
|
2092
2221
|
const body = getBody(content);
|
|
2093
2222
|
try {
|
|
@@ -2325,7 +2454,7 @@ async function pushLinear(parsed, fm, _body, file) {
|
|
|
2325
2454
|
function pushCommand(program2) {
|
|
2326
2455
|
program2.command("push").description("Push local file edits back to data sources (GitHub, GitLab, Linear)").option("--dry-run", "Show what would be pushed without making API calls").action(async (opts) => {
|
|
2327
2456
|
const repoDir = fileRepoDir();
|
|
2328
|
-
if (!
|
|
2457
|
+
if (!existsSync15(join11(repoDir, ".git"))) {
|
|
2329
2458
|
console.error(
|
|
2330
2459
|
"No data repo found. Run `jowork sync` first to create it."
|
|
2331
2460
|
);
|
|
@@ -2333,7 +2462,7 @@ function pushCommand(program2) {
|
|
|
2333
2462
|
}
|
|
2334
2463
|
console.log("Detecting local changes...");
|
|
2335
2464
|
if (opts.dryRun) {
|
|
2336
|
-
const { GitManager: GitManager2 } = await import("./git-manager-
|
|
2465
|
+
const { GitManager: GitManager2 } = await import("./git-manager-RVWV2GSV.js");
|
|
2337
2466
|
const gm = new GitManager2(repoDir);
|
|
2338
2467
|
const status = await gm.getStatus();
|
|
2339
2468
|
const modified = status.modified;
|
|
@@ -2400,8 +2529,8 @@ function configCommand(program2) {
|
|
|
2400
2529
|
}
|
|
2401
2530
|
|
|
2402
2531
|
// src/commands/setup-skill.ts
|
|
2403
|
-
import { existsSync as
|
|
2404
|
-
import { join as
|
|
2532
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync4, writeFileSync as writeFileSync8, readFileSync as readFileSync8 } from "fs";
|
|
2533
|
+
import { join as join12 } from "path";
|
|
2405
2534
|
import { execSync as execSync2 } from "child_process";
|
|
2406
2535
|
var HOME3 = process.env["HOME"] ?? process.env["USERPROFILE"] ?? "";
|
|
2407
2536
|
var SKILL_CONTENT = `---
|
|
@@ -2499,34 +2628,34 @@ function setupSkillCommand(program2) {
|
|
|
2499
2628
|
console.log(" JoWork Setup");
|
|
2500
2629
|
console.log(" ============");
|
|
2501
2630
|
console.log("");
|
|
2502
|
-
const skillDir =
|
|
2631
|
+
const skillDir = join12(HOME3, ".claude", "skills", "jowork");
|
|
2503
2632
|
console.log(" Installing skill to ~/.claude/skills/jowork/ ...");
|
|
2504
2633
|
mkdirSync4(skillDir, { recursive: true });
|
|
2505
|
-
|
|
2506
|
-
|
|
2634
|
+
writeFileSync8(join12(skillDir, "SKILL.md"), SKILL_CONTENT);
|
|
2635
|
+
writeFileSync8(join12(skillDir, "VERSION"), "0.2.0\n");
|
|
2507
2636
|
console.log(" \u2713 Skill installed (/jowork)");
|
|
2508
2637
|
const jDir = joworkDir();
|
|
2509
|
-
if (
|
|
2638
|
+
if (existsSync16(join12(jDir, "config.json"))) {
|
|
2510
2639
|
console.log(" \u2713 Database already initialized");
|
|
2511
2640
|
} else {
|
|
2512
2641
|
mkdirSync4(jDir, { recursive: true });
|
|
2513
|
-
mkdirSync4(
|
|
2514
|
-
mkdirSync4(
|
|
2515
|
-
mkdirSync4(
|
|
2642
|
+
mkdirSync4(join12(jDir, "data"), { recursive: true });
|
|
2643
|
+
mkdirSync4(join12(jDir, "logs"), { recursive: true });
|
|
2644
|
+
mkdirSync4(join12(jDir, "credentials"), { recursive: true });
|
|
2516
2645
|
const db = new DbManager(dbPath());
|
|
2517
2646
|
db.ensureTables();
|
|
2518
2647
|
db.close();
|
|
2519
|
-
|
|
2520
|
-
|
|
2648
|
+
writeFileSync8(
|
|
2649
|
+
join12(jDir, "config.json"),
|
|
2521
2650
|
JSON.stringify({ version: "0.1.0", initialized: true, connectors: {} }, null, 2)
|
|
2522
2651
|
);
|
|
2523
2652
|
console.log(" \u2713 Database initialized");
|
|
2524
2653
|
}
|
|
2525
|
-
const claudeJson =
|
|
2654
|
+
const claudeJson = join12(HOME3, ".claude.json");
|
|
2526
2655
|
let mcpRegistered = false;
|
|
2527
|
-
if (
|
|
2656
|
+
if (existsSync16(claudeJson)) {
|
|
2528
2657
|
try {
|
|
2529
|
-
const content = JSON.parse(
|
|
2658
|
+
const content = JSON.parse(readFileSync8(claudeJson, "utf-8"));
|
|
2530
2659
|
if (content.mcpServers?.jowork) mcpRegistered = true;
|
|
2531
2660
|
} catch {
|
|
2532
2661
|
}
|
|
@@ -2539,7 +2668,7 @@ function setupSkillCommand(program2) {
|
|
|
2539
2668
|
console.log(" \u2713 MCP server registered with Claude Code");
|
|
2540
2669
|
} catch {
|
|
2541
2670
|
try {
|
|
2542
|
-
const cliPath =
|
|
2671
|
+
const cliPath = join12(__dirname, "..", "cli.js");
|
|
2543
2672
|
execSync2(`node "${cliPath}" register claude-code`, { stdio: "pipe" });
|
|
2544
2673
|
console.log(" \u2713 MCP server registered with Claude Code");
|
|
2545
2674
|
} catch {
|
|
@@ -2556,7 +2685,7 @@ function setupSkillCommand(program2) {
|
|
|
2556
2685
|
}
|
|
2557
2686
|
} catch {
|
|
2558
2687
|
}
|
|
2559
|
-
const { listCredentials: listCredentials2 } = await import("./credential-store-
|
|
2688
|
+
const { listCredentials: listCredentials2 } = await import("./credential-store-OS5ZY4OW.js");
|
|
2560
2689
|
const connectedSources = listCredentials2();
|
|
2561
2690
|
console.log("");
|
|
2562
2691
|
console.log(" ============");
|
|
@@ -2611,6 +2740,49 @@ function setupSkillCommand(program2) {
|
|
|
2611
2740
|
});
|
|
2612
2741
|
}
|
|
2613
2742
|
|
|
2743
|
+
// src/commands/plugin.ts
|
|
2744
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
2745
|
+
import { existsSync as existsSync17 } from "fs";
|
|
2746
|
+
import { join as join13 } from "path";
|
|
2747
|
+
function pluginCommand(program2) {
|
|
2748
|
+
const cmd = program2.command("plugin").description("Manage MCP plugin packages");
|
|
2749
|
+
cmd.command("list").description("List available and installed MCP plugins").action(() => {
|
|
2750
|
+
console.log("\nMCP Plugins\n");
|
|
2751
|
+
for (const [source, def] of Object.entries(PLUGIN_REGISTRY)) {
|
|
2752
|
+
const cred = loadCredential(def.connectorName);
|
|
2753
|
+
const installed = isInstalled(def.package);
|
|
2754
|
+
const status = !cred ? "\u25CB not connected" : installed ? "\u2713 installed" : "\u26A0 connected but not installed";
|
|
2755
|
+
console.log(` ${source.padEnd(10)} ${def.name.padEnd(16)} ${def.package}`);
|
|
2756
|
+
console.log(` ${"".padEnd(10)} ${status}`);
|
|
2757
|
+
}
|
|
2758
|
+
console.log("");
|
|
2759
|
+
});
|
|
2760
|
+
cmd.command("update").description("Update all installed MCP plugin packages").action(() => {
|
|
2761
|
+
const dir = pluginsDir();
|
|
2762
|
+
const pkgJsonPath = join13(dir, "package.json");
|
|
2763
|
+
if (!existsSync17(pkgJsonPath)) {
|
|
2764
|
+
console.log("No plugins installed.");
|
|
2765
|
+
return;
|
|
2766
|
+
}
|
|
2767
|
+
console.log("Updating MCP plugins...");
|
|
2768
|
+
try {
|
|
2769
|
+
const output = execFileSync2("npm", ["update"], {
|
|
2770
|
+
cwd: dir,
|
|
2771
|
+
encoding: "utf-8",
|
|
2772
|
+
timeout: 12e4
|
|
2773
|
+
});
|
|
2774
|
+
if (output.trim()) console.log(output.trim());
|
|
2775
|
+
console.log("\u2713 Plugins updated.");
|
|
2776
|
+
} catch (err) {
|
|
2777
|
+
console.error(`\u2717 Update failed: ${err}`);
|
|
2778
|
+
}
|
|
2779
|
+
});
|
|
2780
|
+
}
|
|
2781
|
+
function isInstalled(packageName) {
|
|
2782
|
+
const modulePath = join13(pluginsDir(), "node_modules", packageName);
|
|
2783
|
+
return existsSync17(modulePath);
|
|
2784
|
+
}
|
|
2785
|
+
|
|
2614
2786
|
// src/cli.ts
|
|
2615
2787
|
process.env["I18NEXT_DISABLE_BANNER"] = "1";
|
|
2616
2788
|
var program = new Command();
|
|
@@ -2633,20 +2805,21 @@ logCommand(program);
|
|
|
2633
2805
|
pushCommand(program);
|
|
2634
2806
|
configCommand(program);
|
|
2635
2807
|
setupSkillCommand(program);
|
|
2808
|
+
pluginCommand(program);
|
|
2636
2809
|
program.action(async () => {
|
|
2637
|
-
const { existsSync:
|
|
2638
|
-
const { dbPath: dbPath2 } = await import("./paths-
|
|
2639
|
-
const { readConfig: readConfig2 } = await import("./config-
|
|
2810
|
+
const { existsSync: existsSync18 } = await import("fs");
|
|
2811
|
+
const { dbPath: dbPath2 } = await import("./paths-FFRET6F7.js");
|
|
2812
|
+
const { readConfig: readConfig2 } = await import("./config-FH2XLN7A.js");
|
|
2640
2813
|
const config = readConfig2();
|
|
2641
|
-
if (!config.initialized || !
|
|
2814
|
+
if (!config.initialized || !existsSync18(dbPath2())) {
|
|
2642
2815
|
if (process.stdin.isTTY) {
|
|
2643
|
-
const { runSetupWizard } = await import("./setup-
|
|
2816
|
+
const { runSetupWizard } = await import("./setup-S2S2CHB2.js");
|
|
2644
2817
|
await runSetupWizard();
|
|
2645
2818
|
} else {
|
|
2646
2819
|
console.log("JoWork is not initialized. Run `jowork init` in an interactive terminal.");
|
|
2647
2820
|
}
|
|
2648
2821
|
} else {
|
|
2649
|
-
const { listCredentials: listCredentials2 } = await import("./credential-store-
|
|
2822
|
+
const { listCredentials: listCredentials2 } = await import("./credential-store-OS5ZY4OW.js");
|
|
2650
2823
|
const creds = listCredentials2();
|
|
2651
2824
|
console.log(`JoWork v0.1.0 \u2014 ${creds.length} data source${creds.length !== 1 ? "s" : ""} connected`);
|
|
2652
2825
|
console.log("");
|