jowork 0.2.4 → 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-QGHJ45PL.js +661 -0
- 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 +338 -149
- 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-IDQDPCEJ.js → setup-S2S2CHB2.js} +91 -32
- 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-AIXKXEYS.js +0 -547
- 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-7V54N62M.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,8 +584,11 @@ 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
|
-
|
|
572
|
-
|
|
587
|
+
const { writeConfig: writeConfig2 } = await import("./config-FH2XLN7A.js");
|
|
588
|
+
const db = new DbManager(dbPath());
|
|
589
|
+
db.ensureTables();
|
|
590
|
+
db.close();
|
|
591
|
+
writeConfig2({ version: "0.1.0", initialized: true, connectors: {} });
|
|
573
592
|
}
|
|
574
593
|
if (opts.daemon) {
|
|
575
594
|
await startDaemon();
|
|
@@ -622,9 +641,9 @@ async function startDaemon() {
|
|
|
622
641
|
const intervalMinutes = config.syncIntervalMinutes ?? 15;
|
|
623
642
|
const cronExpr = `*/${intervalMinutes} * * * *`;
|
|
624
643
|
daemonLog("info", "Daemon started", { pid: process.pid, intervalMinutes });
|
|
625
|
-
await
|
|
644
|
+
await runSync2();
|
|
626
645
|
const _syncJob = new Cron(cronExpr, async () => {
|
|
627
|
-
await
|
|
646
|
+
await runSync2();
|
|
628
647
|
});
|
|
629
648
|
console.log(`Daemon started (PID ${process.pid})`);
|
|
630
649
|
console.log(` Sync: every ${intervalMinutes} minutes`);
|
|
@@ -648,7 +667,7 @@ function shouldSkipSource(sqlite, source, config) {
|
|
|
648
667
|
return false;
|
|
649
668
|
}
|
|
650
669
|
}
|
|
651
|
-
async function
|
|
670
|
+
async function runSync2() {
|
|
652
671
|
const sources = listCredentials();
|
|
653
672
|
if (sources.length === 0) {
|
|
654
673
|
daemonLog("info", "No sources connected, skipping sync");
|
|
@@ -682,51 +701,63 @@ async function runSync() {
|
|
|
682
701
|
try {
|
|
683
702
|
switch (source) {
|
|
684
703
|
case "feishu": {
|
|
685
|
-
const
|
|
704
|
+
const feishuCtx = new SyncContext(sqlite, daemonSyncLogger, fileWriter);
|
|
705
|
+
const r = await syncFeishu(feishuCtx, cred.data, daemonSyncLogger);
|
|
686
706
|
syncResults.push({ source: "feishu", newObjects: r.newMessages, label: "messages" });
|
|
687
707
|
try {
|
|
688
|
-
const mr = await syncFeishuMeetings(
|
|
708
|
+
const mr = await syncFeishuMeetings(feishuCtx, cred.data, daemonSyncLogger);
|
|
689
709
|
syncResults.push({ source: "feishu/meetings", newObjects: mr.newObjects, label: "events" });
|
|
690
710
|
} catch (e) {
|
|
691
711
|
daemonLog("warn", `Feishu meetings sync: ${e}`);
|
|
692
712
|
}
|
|
693
713
|
try {
|
|
694
|
-
const dr = await syncFeishuDocs(
|
|
714
|
+
const dr = await syncFeishuDocs(feishuCtx, cred.data, daemonSyncLogger);
|
|
695
715
|
syncResults.push({ source: "feishu/docs", newObjects: dr.newObjects, label: "docs" });
|
|
696
716
|
} catch (e) {
|
|
697
717
|
daemonLog("warn", `Feishu docs sync: ${e}`);
|
|
698
718
|
}
|
|
699
719
|
try {
|
|
700
|
-
const ar = await syncFeishuApprovals(
|
|
720
|
+
const ar = await syncFeishuApprovals(feishuCtx, cred.data, daemonSyncLogger);
|
|
701
721
|
syncResults.push({ source: "feishu/approvals", newObjects: ar.newObjects, label: "approvals" });
|
|
702
722
|
} catch (e) {
|
|
703
723
|
daemonLog("warn", `Feishu approvals sync: ${e}`);
|
|
704
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
|
+
}
|
|
705
731
|
break;
|
|
706
732
|
}
|
|
707
733
|
case "github": {
|
|
708
|
-
const
|
|
709
|
-
|
|
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 });
|
|
710
737
|
break;
|
|
711
738
|
}
|
|
712
739
|
case "gitlab": {
|
|
713
|
-
const
|
|
714
|
-
|
|
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 });
|
|
715
743
|
break;
|
|
716
744
|
}
|
|
717
745
|
case "linear": {
|
|
718
|
-
const
|
|
719
|
-
|
|
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" });
|
|
720
749
|
break;
|
|
721
750
|
}
|
|
722
751
|
case "posthog": {
|
|
723
|
-
const
|
|
724
|
-
|
|
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 });
|
|
725
755
|
break;
|
|
726
756
|
}
|
|
727
757
|
case "firebase": {
|
|
728
|
-
const
|
|
729
|
-
|
|
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" });
|
|
730
761
|
break;
|
|
731
762
|
}
|
|
732
763
|
default:
|
|
@@ -783,7 +814,16 @@ async function runSync() {
|
|
|
783
814
|
// src/commands/register.ts
|
|
784
815
|
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, copyFileSync, existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
|
|
785
816
|
import { join as join2 } from "path";
|
|
817
|
+
import { execSync } from "child_process";
|
|
786
818
|
var HOME = process.env["HOME"] ?? "";
|
|
819
|
+
function getMcpCommand() {
|
|
820
|
+
try {
|
|
821
|
+
execSync("which jowork", { stdio: "ignore" });
|
|
822
|
+
return { command: "jowork", args: ["serve"] };
|
|
823
|
+
} catch {
|
|
824
|
+
return { command: "npx", args: ["-y", "jowork@latest", "serve"] };
|
|
825
|
+
}
|
|
826
|
+
}
|
|
787
827
|
function registerCommand(program2) {
|
|
788
828
|
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
829
|
switch (engine) {
|
|
@@ -820,9 +860,10 @@ function registerClaudeCode() {
|
|
|
820
860
|
}
|
|
821
861
|
}
|
|
822
862
|
if (!config.mcpServers) config.mcpServers = {};
|
|
863
|
+
const mcp = getMcpCommand();
|
|
823
864
|
config.mcpServers["jowork"] = {
|
|
824
|
-
command:
|
|
825
|
-
args:
|
|
865
|
+
command: mcp.command,
|
|
866
|
+
args: mcp.args,
|
|
826
867
|
env: { JOWORK_ENGINE: "claude-code" }
|
|
827
868
|
};
|
|
828
869
|
writeFileSync2(configPath2, JSON.stringify(config, null, 2));
|
|
@@ -849,9 +890,10 @@ function registerOpenClaw() {
|
|
|
849
890
|
}
|
|
850
891
|
}
|
|
851
892
|
if (!config["mcpServers"]) config["mcpServers"] = {};
|
|
893
|
+
const mcp = getMcpCommand();
|
|
852
894
|
config["mcpServers"]["jowork"] = {
|
|
853
|
-
command:
|
|
854
|
-
args:
|
|
895
|
+
command: mcp.command,
|
|
896
|
+
args: mcp.args,
|
|
855
897
|
env: { JOWORK_ENGINE: "openclaw" }
|
|
856
898
|
};
|
|
857
899
|
writeFileSync2(configPath2, JSON.stringify(config, null, 2));
|
|
@@ -876,10 +918,12 @@ function registerCodex() {
|
|
|
876
918
|
console.log("\u2713 JoWork already registered with Codex");
|
|
877
919
|
return;
|
|
878
920
|
}
|
|
921
|
+
const mcp = getMcpCommand();
|
|
922
|
+
const argsToml = mcp.args.map((a) => `"${a}"`).join(", ");
|
|
879
923
|
const mcpEntry = `
|
|
880
924
|
[mcp_servers.jowork]
|
|
881
|
-
command = "
|
|
882
|
-
args = [
|
|
925
|
+
command = "${mcp.command}"
|
|
926
|
+
args = [${argsToml}]
|
|
883
927
|
|
|
884
928
|
[mcp_servers.jowork.env]
|
|
885
929
|
JOWORK_ENGINE = "codex"
|
|
@@ -938,9 +982,8 @@ function statusCommand(program2) {
|
|
|
938
982
|
let sourceRows = [];
|
|
939
983
|
try {
|
|
940
984
|
sourceRows = sqlite.prepare(`
|
|
941
|
-
SELECT o.source, COUNT(*) as count, COALESCE(SUM(LENGTH(
|
|
985
|
+
SELECT o.source, COUNT(*) as count, COALESCE(SUM(LENGTH(o.summary)), 0) as size
|
|
942
986
|
FROM objects o
|
|
943
|
-
LEFT JOIN object_bodies ob ON ob.object_id = o.id
|
|
944
987
|
GROUP BY o.source
|
|
945
988
|
ORDER BY o.source
|
|
946
989
|
`).all();
|
|
@@ -1154,6 +1197,46 @@ function exportCommand(program2) {
|
|
|
1154
1197
|
}
|
|
1155
1198
|
|
|
1156
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
|
+
}
|
|
1157
1240
|
function connectCommand(program2) {
|
|
1158
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) => {
|
|
1159
1242
|
switch (source) {
|
|
@@ -1215,6 +1298,7 @@ async function connectFeishu(opts) {
|
|
|
1215
1298
|
const data = await res.json();
|
|
1216
1299
|
if (data.code !== 0) {
|
|
1217
1300
|
console.error(`Feishu auth failed: ${data.msg}`);
|
|
1301
|
+
console.error(" Hint: App ID/Secret invalid. Check at https://open.feishu.cn/app");
|
|
1218
1302
|
process.exit(1);
|
|
1219
1303
|
}
|
|
1220
1304
|
console.log("\u2713 Feishu credentials verified");
|
|
@@ -1228,7 +1312,9 @@ async function connectFeishu(opts) {
|
|
|
1228
1312
|
createdAt: Date.now(),
|
|
1229
1313
|
updatedAt: Date.now()
|
|
1230
1314
|
});
|
|
1231
|
-
console.log("\u2713 Feishu connected.
|
|
1315
|
+
console.log("\u2713 Feishu connected.");
|
|
1316
|
+
autoInstallPlugin("feishu");
|
|
1317
|
+
await autoSync("feishu");
|
|
1232
1318
|
}
|
|
1233
1319
|
async function connectGitHub(opts) {
|
|
1234
1320
|
let token = opts.token;
|
|
@@ -1244,6 +1330,22 @@ async function connectGitHub(opts) {
|
|
|
1244
1330
|
console.error("Error: GitHub token is required.");
|
|
1245
1331
|
process.exit(1);
|
|
1246
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
|
+
}
|
|
1247
1349
|
saveCredential("github", {
|
|
1248
1350
|
type: "github",
|
|
1249
1351
|
data: { token },
|
|
@@ -1251,6 +1353,8 @@ async function connectGitHub(opts) {
|
|
|
1251
1353
|
updatedAt: Date.now()
|
|
1252
1354
|
});
|
|
1253
1355
|
console.log("\u2713 GitHub connected.");
|
|
1356
|
+
autoInstallPlugin("github");
|
|
1357
|
+
await autoSync("github");
|
|
1254
1358
|
}
|
|
1255
1359
|
async function connectGitLab(opts) {
|
|
1256
1360
|
let token = opts.token;
|
|
@@ -1278,6 +1382,7 @@ async function connectGitLab(opts) {
|
|
|
1278
1382
|
});
|
|
1279
1383
|
if (!res.ok) {
|
|
1280
1384
|
console.error(`GitLab auth failed: HTTP ${res.status}`);
|
|
1385
|
+
console.error(" Hint: Token invalid. Check at https://gitlab.com/-/profile/personal_access_tokens");
|
|
1281
1386
|
process.exit(1);
|
|
1282
1387
|
}
|
|
1283
1388
|
const user = await res.json();
|
|
@@ -1294,7 +1399,9 @@ async function connectGitLab(opts) {
|
|
|
1294
1399
|
createdAt: Date.now(),
|
|
1295
1400
|
updatedAt: Date.now()
|
|
1296
1401
|
});
|
|
1297
|
-
console.log("\u2713 GitLab connected.
|
|
1402
|
+
console.log("\u2713 GitLab connected.");
|
|
1403
|
+
autoInstallPlugin("gitlab");
|
|
1404
|
+
await autoSync("gitlab");
|
|
1298
1405
|
}
|
|
1299
1406
|
async function connectLinear(opts) {
|
|
1300
1407
|
let apiKey = opts.apiKey;
|
|
@@ -1322,11 +1429,13 @@ async function connectLinear(opts) {
|
|
|
1322
1429
|
});
|
|
1323
1430
|
if (!res.ok) {
|
|
1324
1431
|
console.error(`Linear auth failed: HTTP ${res.status}`);
|
|
1432
|
+
console.error(" Hint: API key invalid. Get one at https://linear.app/settings/api");
|
|
1325
1433
|
process.exit(1);
|
|
1326
1434
|
}
|
|
1327
1435
|
const data = await res.json();
|
|
1328
1436
|
if (data.errors?.length) {
|
|
1329
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");
|
|
1330
1439
|
process.exit(1);
|
|
1331
1440
|
}
|
|
1332
1441
|
console.log(`\u2713 Linear credentials verified (user: ${data.data?.viewer?.name})`);
|
|
@@ -1340,7 +1449,9 @@ async function connectLinear(opts) {
|
|
|
1340
1449
|
createdAt: Date.now(),
|
|
1341
1450
|
updatedAt: Date.now()
|
|
1342
1451
|
});
|
|
1343
|
-
console.log("\u2713 Linear connected.
|
|
1452
|
+
console.log("\u2713 Linear connected.");
|
|
1453
|
+
autoInstallPlugin("linear");
|
|
1454
|
+
await autoSync("linear");
|
|
1344
1455
|
}
|
|
1345
1456
|
async function connectPostHog(opts) {
|
|
1346
1457
|
let apiKey = opts.apiKey;
|
|
@@ -1363,7 +1474,11 @@ async function connectPostHog(opts) {
|
|
|
1363
1474
|
const res = await fetch(`${host}/api/projects/${projectId}/`, {
|
|
1364
1475
|
headers: { Authorization: `Bearer ${apiKey}` }
|
|
1365
1476
|
});
|
|
1366
|
-
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
|
+
}
|
|
1367
1482
|
console.log("\u2713 PostHog credentials verified");
|
|
1368
1483
|
} catch (err) {
|
|
1369
1484
|
console.error(`PostHog verification failed: ${err}`);
|
|
@@ -1375,7 +1490,9 @@ async function connectPostHog(opts) {
|
|
|
1375
1490
|
createdAt: Date.now(),
|
|
1376
1491
|
updatedAt: Date.now()
|
|
1377
1492
|
});
|
|
1378
|
-
console.log("\u2713 PostHog connected.
|
|
1493
|
+
console.log("\u2713 PostHog connected.");
|
|
1494
|
+
autoInstallPlugin("posthog");
|
|
1495
|
+
await autoSync("posthog");
|
|
1379
1496
|
}
|
|
1380
1497
|
async function connectSlack(opts) {
|
|
1381
1498
|
let webhookUrl = opts.webhookUrl;
|
|
@@ -1445,22 +1562,40 @@ async function connectFirebase(opts) {
|
|
|
1445
1562
|
console.error("Error: API key required.");
|
|
1446
1563
|
process.exit(1);
|
|
1447
1564
|
}
|
|
1448
|
-
const
|
|
1449
|
-
|
|
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;
|
|
1450
1583
|
saveCredential("firebase", {
|
|
1451
1584
|
type: "firebase",
|
|
1452
|
-
data,
|
|
1585
|
+
data: credData,
|
|
1453
1586
|
createdAt: Date.now(),
|
|
1454
1587
|
updatedAt: Date.now()
|
|
1455
1588
|
});
|
|
1456
|
-
console.log("\u2713 Firebase connected.
|
|
1589
|
+
console.log("\u2713 Firebase connected.");
|
|
1590
|
+
autoInstallPlugin("firebase");
|
|
1591
|
+
await autoSync("firebase");
|
|
1457
1592
|
}
|
|
1458
1593
|
|
|
1459
1594
|
// src/commands/search.ts
|
|
1460
|
-
import { existsSync as
|
|
1595
|
+
import { existsSync as existsSync8 } from "fs";
|
|
1461
1596
|
function searchCommand(program2) {
|
|
1462
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) => {
|
|
1463
|
-
if (!
|
|
1598
|
+
if (!existsSync8(dbPath())) {
|
|
1464
1599
|
console.error("Error: JoWork not initialized. Run `jowork init` first.");
|
|
1465
1600
|
process.exit(1);
|
|
1466
1601
|
}
|
|
@@ -1611,8 +1746,8 @@ function goalCommand(program2) {
|
|
|
1611
1746
|
}
|
|
1612
1747
|
|
|
1613
1748
|
// src/commands/install-service.ts
|
|
1614
|
-
import { writeFileSync as
|
|
1615
|
-
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";
|
|
1616
1751
|
var HOME2 = process.env["HOME"] ?? "";
|
|
1617
1752
|
function installServiceCommand(program2) {
|
|
1618
1753
|
program2.command("install-service").description("Generate system service config (macOS LaunchAgent / Linux systemd)").option("--uninstall", "Remove the service").action(async (opts) => {
|
|
@@ -1626,9 +1761,9 @@ function installServiceCommand(program2) {
|
|
|
1626
1761
|
});
|
|
1627
1762
|
}
|
|
1628
1763
|
function installLaunchAgent(uninstall) {
|
|
1629
|
-
const plistPath =
|
|
1764
|
+
const plistPath = join6(HOME2, "Library", "LaunchAgents", "work.jowork.daemon.plist");
|
|
1630
1765
|
if (uninstall) {
|
|
1631
|
-
if (
|
|
1766
|
+
if (existsSync9(plistPath)) {
|
|
1632
1767
|
unlinkSync2(plistPath);
|
|
1633
1768
|
console.log(`\u2713 Removed ${plistPath}`);
|
|
1634
1769
|
console.log(" Run: launchctl unload work.jowork.daemon");
|
|
@@ -1639,7 +1774,7 @@ function installLaunchAgent(uninstall) {
|
|
|
1639
1774
|
}
|
|
1640
1775
|
const joworkBin = process.argv[0];
|
|
1641
1776
|
const joworkScript = process.argv[1];
|
|
1642
|
-
const logsPath =
|
|
1777
|
+
const logsPath = join6(joworkDir(), "logs");
|
|
1643
1778
|
mkdirSync3(logsPath, { recursive: true });
|
|
1644
1779
|
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
1645
1780
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
@@ -1671,7 +1806,7 @@ function installLaunchAgent(uninstall) {
|
|
|
1671
1806
|
</dict>
|
|
1672
1807
|
</dict>
|
|
1673
1808
|
</plist>`;
|
|
1674
|
-
|
|
1809
|
+
writeFileSync5(plistPath, plist);
|
|
1675
1810
|
console.log(`\u2713 LaunchAgent written to ${plistPath}`);
|
|
1676
1811
|
console.log("");
|
|
1677
1812
|
console.log("To start:");
|
|
@@ -1682,10 +1817,10 @@ function installLaunchAgent(uninstall) {
|
|
|
1682
1817
|
console.log(" jowork install-service --uninstall");
|
|
1683
1818
|
}
|
|
1684
1819
|
function installSystemd(uninstall) {
|
|
1685
|
-
const serviceDir =
|
|
1686
|
-
const servicePath =
|
|
1820
|
+
const serviceDir = join6(HOME2, ".config", "systemd", "user");
|
|
1821
|
+
const servicePath = join6(serviceDir, "jowork.service");
|
|
1687
1822
|
if (uninstall) {
|
|
1688
|
-
if (
|
|
1823
|
+
if (existsSync9(servicePath)) {
|
|
1689
1824
|
unlinkSync2(servicePath);
|
|
1690
1825
|
console.log(`\u2713 Removed ${servicePath}`);
|
|
1691
1826
|
console.log(" Run: systemctl --user disable jowork");
|
|
@@ -1709,7 +1844,7 @@ RestartSec=10
|
|
|
1709
1844
|
[Install]
|
|
1710
1845
|
WantedBy=default.target
|
|
1711
1846
|
`;
|
|
1712
|
-
|
|
1847
|
+
writeFileSync5(servicePath, service);
|
|
1713
1848
|
console.log(`\u2713 Systemd service written to ${servicePath}`);
|
|
1714
1849
|
console.log("");
|
|
1715
1850
|
console.log("To start:");
|
|
@@ -1718,10 +1853,11 @@ WantedBy=default.target
|
|
|
1718
1853
|
}
|
|
1719
1854
|
|
|
1720
1855
|
// src/commands/gc.ts
|
|
1721
|
-
import { existsSync as
|
|
1856
|
+
import { existsSync as existsSync10, statSync as statSync2, unlinkSync as unlinkSync3 } from "fs";
|
|
1857
|
+
import { join as join7 } from "path";
|
|
1722
1858
|
function gcCommand(program2) {
|
|
1723
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) => {
|
|
1724
|
-
if (!
|
|
1860
|
+
if (!existsSync10(dbPath())) {
|
|
1725
1861
|
console.error("Error: JoWork not initialized. Run `jowork init` first.");
|
|
1726
1862
|
process.exit(1);
|
|
1727
1863
|
}
|
|
@@ -1733,31 +1869,40 @@ function gcCommand(program2) {
|
|
|
1733
1869
|
const sqlite = db.getSqlite();
|
|
1734
1870
|
const sizeBefore = statSync2(dbPath()).size;
|
|
1735
1871
|
console.log(`Database size: ${(sizeBefore / 1024 / 1024).toFixed(2)} MB`);
|
|
1736
|
-
let deletedBodies = 0;
|
|
1737
1872
|
let deletedObjects = 0;
|
|
1873
|
+
let deletedFiles = 0;
|
|
1738
1874
|
if (retentionDays > 0) {
|
|
1739
1875
|
const cutoff = Date.now() - retentionDays * 24 * 60 * 60 * 1e3;
|
|
1740
1876
|
if (dryRun) {
|
|
1741
1877
|
const count = sqlite.prepare(
|
|
1742
|
-
`SELECT COUNT(*) as c FROM
|
|
1743
|
-
JOIN objects o ON o.id = ob.object_id
|
|
1744
|
-
WHERE o.created_at < ?`
|
|
1878
|
+
`SELECT COUNT(*) as c FROM objects WHERE created_at < ?`
|
|
1745
1879
|
).get(cutoff).c;
|
|
1746
|
-
console.log(`Would delete ${count}
|
|
1880
|
+
console.log(`Would delete ${count} objects older than ${retentionDays} days`);
|
|
1747
1881
|
} else {
|
|
1748
|
-
const
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
)`
|
|
1752
|
-
).run(cutoff);
|
|
1753
|
-
deletedBodies = result.changes;
|
|
1754
|
-
const objResult = sqlite.prepare(
|
|
1755
|
-
`DELETE FROM objects WHERE created_at < ?
|
|
1756
|
-
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 < ?
|
|
1757
1885
|
AND id NOT IN (SELECT source_object_id FROM object_links)`
|
|
1758
|
-
).
|
|
1759
|
-
|
|
1760
|
-
|
|
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`);
|
|
1761
1906
|
}
|
|
1762
1907
|
} else {
|
|
1763
1908
|
console.log("Retention: keep all (set retentionDays in config or use --retention-days)");
|
|
@@ -1769,8 +1914,8 @@ function gcCommand(program2) {
|
|
|
1769
1914
|
const saved = sizeBefore - sizeAfter;
|
|
1770
1915
|
console.log(`VACUUM complete: ${(sizeAfter / 1024 / 1024).toFixed(2)} MB (saved ${(saved / 1024).toFixed(0)} KB)`);
|
|
1771
1916
|
logInfo("gc", "Garbage collection complete", {
|
|
1772
|
-
deletedBodies,
|
|
1773
1917
|
deletedObjects,
|
|
1918
|
+
deletedFiles,
|
|
1774
1919
|
sizeBefore,
|
|
1775
1920
|
sizeAfter,
|
|
1776
1921
|
savedBytes: saved
|
|
@@ -1788,18 +1933,18 @@ function gcCommand(program2) {
|
|
|
1788
1933
|
}
|
|
1789
1934
|
|
|
1790
1935
|
// src/commands/device-sync.ts
|
|
1791
|
-
import { existsSync as
|
|
1936
|
+
import { existsSync as existsSync12, readFileSync as readFileSync6, writeFileSync as writeFileSync7 } from "fs";
|
|
1792
1937
|
|
|
1793
1938
|
// src/sync/device-sync.ts
|
|
1794
|
-
import { readFileSync as
|
|
1795
|
-
import { join as
|
|
1939
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync6, existsSync as existsSync11 } from "fs";
|
|
1940
|
+
import { join as join8 } from "path";
|
|
1796
1941
|
function getDeviceId() {
|
|
1797
|
-
const idPath =
|
|
1798
|
-
if (
|
|
1799
|
-
return
|
|
1942
|
+
const idPath = join8(joworkDir(), "device-id");
|
|
1943
|
+
if (existsSync11(idPath)) {
|
|
1944
|
+
return readFileSync5(idPath, "utf-8").trim();
|
|
1800
1945
|
}
|
|
1801
1946
|
const id = createId("dev");
|
|
1802
|
-
|
|
1947
|
+
writeFileSync6(idPath, id);
|
|
1803
1948
|
return id;
|
|
1804
1949
|
}
|
|
1805
1950
|
function getUnsyncedChanges(sqlite, limit = 100) {
|
|
@@ -1894,7 +2039,7 @@ function importSyncBundle(sqlite, bundleJson) {
|
|
|
1894
2039
|
function deviceSyncCommand(program2) {
|
|
1895
2040
|
const sync = program2.command("device-sync").description("Sync data between devices");
|
|
1896
2041
|
sync.command("status").description("Show device sync status").action(async () => {
|
|
1897
|
-
if (!
|
|
2042
|
+
if (!existsSync12(dbPath())) {
|
|
1898
2043
|
console.error("Error: JoWork not initialized. Run `jowork init` first.");
|
|
1899
2044
|
process.exit(1);
|
|
1900
2045
|
}
|
|
@@ -1909,7 +2054,7 @@ function deviceSyncCommand(program2) {
|
|
|
1909
2054
|
db.close();
|
|
1910
2055
|
});
|
|
1911
2056
|
sync.command("export").description("Export unsynced changes to a file").argument("<file>", "Output file path").action(async (file) => {
|
|
1912
|
-
if (!
|
|
2057
|
+
if (!existsSync12(dbPath())) {
|
|
1913
2058
|
console.error("Error: JoWork not initialized. Run `jowork init` first.");
|
|
1914
2059
|
process.exit(1);
|
|
1915
2060
|
}
|
|
@@ -1917,24 +2062,24 @@ function deviceSyncCommand(program2) {
|
|
|
1917
2062
|
db.ensureTables();
|
|
1918
2063
|
const sqlite = db.getSqlite();
|
|
1919
2064
|
const bundle = exportSyncBundle(sqlite);
|
|
1920
|
-
|
|
2065
|
+
writeFileSync7(file, bundle);
|
|
1921
2066
|
const parsed = JSON.parse(bundle);
|
|
1922
2067
|
console.log(`Exported ${parsed.changes.length} changes to ${file}`);
|
|
1923
2068
|
markSynced(sqlite, parsed.changes.map((c) => c.id));
|
|
1924
2069
|
db.close();
|
|
1925
2070
|
});
|
|
1926
2071
|
sync.command("import").description("Import changes from another device").argument("<file>", "Input file path").action(async (file) => {
|
|
1927
|
-
if (!
|
|
2072
|
+
if (!existsSync12(file)) {
|
|
1928
2073
|
console.error(`File not found: ${file}`);
|
|
1929
2074
|
process.exit(1);
|
|
1930
2075
|
}
|
|
1931
|
-
if (!
|
|
2076
|
+
if (!existsSync12(dbPath())) {
|
|
1932
2077
|
console.error("Error: JoWork not initialized. Run `jowork init` first.");
|
|
1933
2078
|
process.exit(1);
|
|
1934
2079
|
}
|
|
1935
2080
|
const db = new DbManager(dbPath());
|
|
1936
2081
|
db.ensureTables();
|
|
1937
|
-
const bundleJson =
|
|
2082
|
+
const bundleJson = readFileSync6(file, "utf-8");
|
|
1938
2083
|
const result = importSyncBundle(db.getSqlite(), bundleJson);
|
|
1939
2084
|
console.log(`Applied ${result.applied}, conflicts ${result.conflicts}, skipped ${result.skipped}`);
|
|
1940
2085
|
db.close();
|
|
@@ -1942,15 +2087,15 @@ function deviceSyncCommand(program2) {
|
|
|
1942
2087
|
}
|
|
1943
2088
|
|
|
1944
2089
|
// src/commands/dashboard.ts
|
|
1945
|
-
import { existsSync as
|
|
2090
|
+
import { existsSync as existsSync13 } from "fs";
|
|
1946
2091
|
import { exec } from "child_process";
|
|
1947
2092
|
function dashboardCommand(program2) {
|
|
1948
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) => {
|
|
1949
|
-
if (!
|
|
2094
|
+
if (!existsSync13(dbPath())) {
|
|
1950
2095
|
console.error("Error: JoWork not initialized. Run `jowork init` first.");
|
|
1951
2096
|
process.exit(1);
|
|
1952
2097
|
}
|
|
1953
|
-
const { startDashboard } = await import("./server-
|
|
2098
|
+
const { startDashboard } = await import("./server-WEADPUST.js");
|
|
1954
2099
|
const port = opts.port ? parseInt(opts.port, 10) : void 0;
|
|
1955
2100
|
const dashboard = await startDashboard({ port });
|
|
1956
2101
|
const url = `http://127.0.0.1:${dashboard.port}`;
|
|
@@ -1975,12 +2120,12 @@ function dashboardCommand(program2) {
|
|
|
1975
2120
|
}
|
|
1976
2121
|
|
|
1977
2122
|
// src/commands/log.ts
|
|
1978
|
-
import { existsSync as
|
|
1979
|
-
import { join as
|
|
2123
|
+
import { existsSync as existsSync14 } from "fs";
|
|
2124
|
+
import { join as join9 } from "path";
|
|
1980
2125
|
function logCommand(program2) {
|
|
1981
2126
|
program2.command("log").description("Show data sync history").option("-n, --limit <n>", "Number of entries", "20").action(async (opts) => {
|
|
1982
2127
|
const repoDir = fileRepoDir();
|
|
1983
|
-
if (!
|
|
2128
|
+
if (!existsSync14(join9(repoDir, ".git"))) {
|
|
1984
2129
|
console.log("No sync history yet. Run `jowork sync` first.");
|
|
1985
2130
|
return;
|
|
1986
2131
|
}
|
|
@@ -1998,12 +2143,12 @@ function logCommand(program2) {
|
|
|
1998
2143
|
}
|
|
1999
2144
|
|
|
2000
2145
|
// src/commands/push.ts
|
|
2001
|
-
import { existsSync as
|
|
2002
|
-
import { join as
|
|
2146
|
+
import { existsSync as existsSync15 } from "fs";
|
|
2147
|
+
import { join as join11 } from "path";
|
|
2003
2148
|
|
|
2004
2149
|
// src/sync/push-back.ts
|
|
2005
|
-
import { readFileSync as
|
|
2006
|
-
import { join as
|
|
2150
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
2151
|
+
import { join as join10 } from "path";
|
|
2007
2152
|
function parseFilePath(filePath) {
|
|
2008
2153
|
const githubMatch = filePath.match(
|
|
2009
2154
|
/^github\/([^/]+)\/(issues|pulls)\/(\d+)\.md$/
|
|
@@ -2071,7 +2216,7 @@ async function pushChanges(repoDir) {
|
|
|
2071
2216
|
for (const file of modifiedFiles) {
|
|
2072
2217
|
const parsed = parseFilePath(file);
|
|
2073
2218
|
if (!parsed) continue;
|
|
2074
|
-
const content =
|
|
2219
|
+
const content = readFileSync7(join10(repoDir, file), "utf-8");
|
|
2075
2220
|
const fm = parseFrontmatter(content);
|
|
2076
2221
|
const body = getBody(content);
|
|
2077
2222
|
try {
|
|
@@ -2309,7 +2454,7 @@ async function pushLinear(parsed, fm, _body, file) {
|
|
|
2309
2454
|
function pushCommand(program2) {
|
|
2310
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) => {
|
|
2311
2456
|
const repoDir = fileRepoDir();
|
|
2312
|
-
if (!
|
|
2457
|
+
if (!existsSync15(join11(repoDir, ".git"))) {
|
|
2313
2458
|
console.error(
|
|
2314
2459
|
"No data repo found. Run `jowork sync` first to create it."
|
|
2315
2460
|
);
|
|
@@ -2317,7 +2462,7 @@ function pushCommand(program2) {
|
|
|
2317
2462
|
}
|
|
2318
2463
|
console.log("Detecting local changes...");
|
|
2319
2464
|
if (opts.dryRun) {
|
|
2320
|
-
const { GitManager: GitManager2 } = await import("./git-manager-
|
|
2465
|
+
const { GitManager: GitManager2 } = await import("./git-manager-RVWV2GSV.js");
|
|
2321
2466
|
const gm = new GitManager2(repoDir);
|
|
2322
2467
|
const status = await gm.getStatus();
|
|
2323
2468
|
const modified = status.modified;
|
|
@@ -2384,9 +2529,9 @@ function configCommand(program2) {
|
|
|
2384
2529
|
}
|
|
2385
2530
|
|
|
2386
2531
|
// src/commands/setup-skill.ts
|
|
2387
|
-
import { existsSync as
|
|
2388
|
-
import { join as
|
|
2389
|
-
import { execSync } from "child_process";
|
|
2532
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync4, writeFileSync as writeFileSync8, readFileSync as readFileSync8 } from "fs";
|
|
2533
|
+
import { join as join12 } from "path";
|
|
2534
|
+
import { execSync as execSync2 } from "child_process";
|
|
2390
2535
|
var HOME3 = process.env["HOME"] ?? process.env["USERPROFILE"] ?? "";
|
|
2391
2536
|
var SKILL_CONTENT = `---
|
|
2392
2537
|
name: jowork
|
|
@@ -2483,34 +2628,34 @@ function setupSkillCommand(program2) {
|
|
|
2483
2628
|
console.log(" JoWork Setup");
|
|
2484
2629
|
console.log(" ============");
|
|
2485
2630
|
console.log("");
|
|
2486
|
-
const skillDir =
|
|
2631
|
+
const skillDir = join12(HOME3, ".claude", "skills", "jowork");
|
|
2487
2632
|
console.log(" Installing skill to ~/.claude/skills/jowork/ ...");
|
|
2488
2633
|
mkdirSync4(skillDir, { recursive: true });
|
|
2489
|
-
|
|
2490
|
-
|
|
2634
|
+
writeFileSync8(join12(skillDir, "SKILL.md"), SKILL_CONTENT);
|
|
2635
|
+
writeFileSync8(join12(skillDir, "VERSION"), "0.2.0\n");
|
|
2491
2636
|
console.log(" \u2713 Skill installed (/jowork)");
|
|
2492
2637
|
const jDir = joworkDir();
|
|
2493
|
-
if (
|
|
2638
|
+
if (existsSync16(join12(jDir, "config.json"))) {
|
|
2494
2639
|
console.log(" \u2713 Database already initialized");
|
|
2495
2640
|
} else {
|
|
2496
2641
|
mkdirSync4(jDir, { recursive: true });
|
|
2497
|
-
mkdirSync4(
|
|
2498
|
-
mkdirSync4(
|
|
2499
|
-
mkdirSync4(
|
|
2642
|
+
mkdirSync4(join12(jDir, "data"), { recursive: true });
|
|
2643
|
+
mkdirSync4(join12(jDir, "logs"), { recursive: true });
|
|
2644
|
+
mkdirSync4(join12(jDir, "credentials"), { recursive: true });
|
|
2500
2645
|
const db = new DbManager(dbPath());
|
|
2501
2646
|
db.ensureTables();
|
|
2502
2647
|
db.close();
|
|
2503
|
-
|
|
2504
|
-
|
|
2648
|
+
writeFileSync8(
|
|
2649
|
+
join12(jDir, "config.json"),
|
|
2505
2650
|
JSON.stringify({ version: "0.1.0", initialized: true, connectors: {} }, null, 2)
|
|
2506
2651
|
);
|
|
2507
2652
|
console.log(" \u2713 Database initialized");
|
|
2508
2653
|
}
|
|
2509
|
-
const claudeJson =
|
|
2654
|
+
const claudeJson = join12(HOME3, ".claude.json");
|
|
2510
2655
|
let mcpRegistered = false;
|
|
2511
|
-
if (
|
|
2656
|
+
if (existsSync16(claudeJson)) {
|
|
2512
2657
|
try {
|
|
2513
|
-
const content = JSON.parse(
|
|
2658
|
+
const content = JSON.parse(readFileSync8(claudeJson, "utf-8"));
|
|
2514
2659
|
if (content.mcpServers?.jowork) mcpRegistered = true;
|
|
2515
2660
|
} catch {
|
|
2516
2661
|
}
|
|
@@ -2519,12 +2664,12 @@ function setupSkillCommand(program2) {
|
|
|
2519
2664
|
console.log(" \u2713 MCP server registered with Claude Code");
|
|
2520
2665
|
} else {
|
|
2521
2666
|
try {
|
|
2522
|
-
|
|
2667
|
+
execSync2("jowork register claude-code", { stdio: "pipe" });
|
|
2523
2668
|
console.log(" \u2713 MCP server registered with Claude Code");
|
|
2524
2669
|
} catch {
|
|
2525
2670
|
try {
|
|
2526
|
-
const cliPath =
|
|
2527
|
-
|
|
2671
|
+
const cliPath = join12(__dirname, "..", "cli.js");
|
|
2672
|
+
execSync2(`node "${cliPath}" register claude-code`, { stdio: "pipe" });
|
|
2528
2673
|
console.log(" \u2713 MCP server registered with Claude Code");
|
|
2529
2674
|
} catch {
|
|
2530
2675
|
console.log(" \u26A0 Could not auto-register. Run: jowork register claude-code");
|
|
@@ -2532,15 +2677,15 @@ function setupSkillCommand(program2) {
|
|
|
2532
2677
|
}
|
|
2533
2678
|
}
|
|
2534
2679
|
try {
|
|
2535
|
-
|
|
2680
|
+
execSync2("which codex", { stdio: "pipe" });
|
|
2536
2681
|
try {
|
|
2537
|
-
|
|
2682
|
+
execSync2("jowork register codex", { stdio: "pipe" });
|
|
2538
2683
|
console.log(" \u2713 Registered with Codex");
|
|
2539
2684
|
} catch {
|
|
2540
2685
|
}
|
|
2541
2686
|
} catch {
|
|
2542
2687
|
}
|
|
2543
|
-
const { listCredentials: listCredentials2 } = await import("./credential-store-
|
|
2688
|
+
const { listCredentials: listCredentials2 } = await import("./credential-store-OS5ZY4OW.js");
|
|
2544
2689
|
const connectedSources = listCredentials2();
|
|
2545
2690
|
console.log("");
|
|
2546
2691
|
console.log(" ============");
|
|
@@ -2573,13 +2718,13 @@ function setupSkillCommand(program2) {
|
|
|
2573
2718
|
console.log(" Detected GITHUB_PERSONAL_ACCESS_TOKEN in environment.");
|
|
2574
2719
|
console.log(" Connecting GitHub automatically...");
|
|
2575
2720
|
try {
|
|
2576
|
-
|
|
2721
|
+
execSync2('jowork connect github --token "$GITHUB_PERSONAL_ACCESS_TOKEN"', {
|
|
2577
2722
|
stdio: "pipe",
|
|
2578
2723
|
env: process.env
|
|
2579
2724
|
});
|
|
2580
2725
|
console.log(" \u2713 GitHub connected! Running initial sync...");
|
|
2581
2726
|
try {
|
|
2582
|
-
const output =
|
|
2727
|
+
const output = execSync2("jowork sync --source github", {
|
|
2583
2728
|
encoding: "utf-8",
|
|
2584
2729
|
timeout: 3e4,
|
|
2585
2730
|
env: process.env
|
|
@@ -2595,6 +2740,49 @@ function setupSkillCommand(program2) {
|
|
|
2595
2740
|
});
|
|
2596
2741
|
}
|
|
2597
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
|
+
|
|
2598
2786
|
// src/cli.ts
|
|
2599
2787
|
process.env["I18NEXT_DISABLE_BANNER"] = "1";
|
|
2600
2788
|
var program = new Command();
|
|
@@ -2617,20 +2805,21 @@ logCommand(program);
|
|
|
2617
2805
|
pushCommand(program);
|
|
2618
2806
|
configCommand(program);
|
|
2619
2807
|
setupSkillCommand(program);
|
|
2808
|
+
pluginCommand(program);
|
|
2620
2809
|
program.action(async () => {
|
|
2621
|
-
const { existsSync:
|
|
2622
|
-
const { dbPath: dbPath2 } = await import("./paths-
|
|
2623
|
-
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");
|
|
2624
2813
|
const config = readConfig2();
|
|
2625
|
-
if (!config.initialized || !
|
|
2814
|
+
if (!config.initialized || !existsSync18(dbPath2())) {
|
|
2626
2815
|
if (process.stdin.isTTY) {
|
|
2627
|
-
const { runSetupWizard } = await import("./setup-
|
|
2816
|
+
const { runSetupWizard } = await import("./setup-S2S2CHB2.js");
|
|
2628
2817
|
await runSetupWizard();
|
|
2629
2818
|
} else {
|
|
2630
2819
|
console.log("JoWork is not initialized. Run `jowork init` in an interactive terminal.");
|
|
2631
2820
|
}
|
|
2632
2821
|
} else {
|
|
2633
|
-
const { listCredentials: listCredentials2 } = await import("./credential-store-
|
|
2822
|
+
const { listCredentials: listCredentials2 } = await import("./credential-store-OS5ZY4OW.js");
|
|
2634
2823
|
const creds = listCredentials2();
|
|
2635
2824
|
console.log(`JoWork v0.1.0 \u2014 ${creds.length} data source${creds.length !== 1 ? "s" : ""} connected`);
|
|
2636
2825
|
console.log("");
|