jowork 0.3.7 → 0.3.8
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.
|
@@ -317,8 +317,8 @@ var FileWriter = class {
|
|
|
317
317
|
existingHeaders = [...existing.matchAll(/^## .+/gm)].map((m) => m[0]);
|
|
318
318
|
}
|
|
319
319
|
const newMessages = messages.filter((m) => {
|
|
320
|
-
const
|
|
321
|
-
return !existingHeaders.includes(
|
|
320
|
+
const header = `## ${m.time} \u2014 ${m.sender}`;
|
|
321
|
+
return !existingHeaders.includes(header);
|
|
322
322
|
});
|
|
323
323
|
if (newMessages.length === 0) return filePath;
|
|
324
324
|
if (!existsSync(absPath)) {
|
|
@@ -381,70 +381,6 @@ ${sanitizeContent(m.content)}`).join("\n");
|
|
|
381
381
|
};
|
|
382
382
|
|
|
383
383
|
// src/commands/sync.ts
|
|
384
|
-
var isTTY = process.stdout.isTTY;
|
|
385
|
-
var c = {
|
|
386
|
-
reset: isTTY ? "\x1B[0m" : "",
|
|
387
|
-
bold: isTTY ? "\x1B[1m" : "",
|
|
388
|
-
dim: isTTY ? "\x1B[2m" : "",
|
|
389
|
-
green: isTTY ? "\x1B[32m" : "",
|
|
390
|
-
yellow: isTTY ? "\x1B[33m" : "",
|
|
391
|
-
red: isTTY ? "\x1B[31m" : "",
|
|
392
|
-
cyan: isTTY ? "\x1B[36m" : "",
|
|
393
|
-
gray: isTTY ? "\x1B[90m" : "",
|
|
394
|
-
white: isTTY ? "\x1B[37m" : "",
|
|
395
|
-
bgGreen: isTTY ? "\x1B[42m" : "",
|
|
396
|
-
bgRed: isTTY ? "\x1B[41m" : "",
|
|
397
|
-
bgYellow: isTTY ? "\x1B[43m" : ""
|
|
398
|
-
};
|
|
399
|
-
var icon = {
|
|
400
|
-
ok: `${c.green}\u2713${c.reset}`,
|
|
401
|
-
warn: `${c.yellow}\u26A0${c.reset}`,
|
|
402
|
-
fail: `${c.red}\u2717${c.reset}`,
|
|
403
|
-
skip: `${c.gray}\u25CB${c.reset}`,
|
|
404
|
-
sync: `${c.cyan}\u21BB${c.reset}`,
|
|
405
|
-
link: `${c.cyan}\u27E1${c.reset}`,
|
|
406
|
-
git: `${c.gray}\u2387${c.reset}`
|
|
407
|
-
};
|
|
408
|
-
function header(text) {
|
|
409
|
-
console.log("");
|
|
410
|
-
console.log(` ${c.bold}${text}${c.reset}`);
|
|
411
|
-
console.log(` ${c.dim}${"\u2500".repeat(Math.min(text.length + 4, 50))}${c.reset}`);
|
|
412
|
-
}
|
|
413
|
-
function progressBar(current, total, width = 20) {
|
|
414
|
-
const pct = total > 0 ? current / total : 0;
|
|
415
|
-
const filled = Math.round(pct * width);
|
|
416
|
-
const empty = width - filled;
|
|
417
|
-
const bar = `${c.green}${"\u2588".repeat(filled)}${c.gray}${"\u2591".repeat(empty)}${c.reset}`;
|
|
418
|
-
return `${bar} ${c.dim}${current}/${total}${c.reset}`;
|
|
419
|
-
}
|
|
420
|
-
function sourceLabel(name) {
|
|
421
|
-
const colors = {
|
|
422
|
-
feishu: c.cyan,
|
|
423
|
-
github: c.white,
|
|
424
|
-
gitlab: c.yellow,
|
|
425
|
-
linear: c.cyan,
|
|
426
|
-
posthog: c.red,
|
|
427
|
-
firebase: c.yellow
|
|
428
|
-
};
|
|
429
|
-
return `${colors[name] ?? c.white}${c.bold}${name}${c.reset}`;
|
|
430
|
-
}
|
|
431
|
-
function resultLine(ok, msg) {
|
|
432
|
-
if (isTTY) process.stdout.write("\r\x1B[K");
|
|
433
|
-
console.log(` ${ok ? icon.ok : icon.warn} ${msg}`);
|
|
434
|
-
}
|
|
435
|
-
var _currentSource = "";
|
|
436
|
-
function progressLine(msg) {
|
|
437
|
-
if (!isTTY) return;
|
|
438
|
-
process.stdout.write(`\r ${icon.sync} ${_currentSource} ${c.dim}${msg}${c.reset}\x1B[K`);
|
|
439
|
-
}
|
|
440
|
-
function clearProgress() {
|
|
441
|
-
if (!isTTY) return;
|
|
442
|
-
process.stdout.write("\r\x1B[K");
|
|
443
|
-
}
|
|
444
|
-
function elapsed(start) {
|
|
445
|
-
const ms = Date.now() - start;
|
|
446
|
-
return ms < 1e3 ? `${ms}ms` : `${(ms / 1e3).toFixed(1)}s`;
|
|
447
|
-
}
|
|
448
384
|
function getSyncErrorHint(source, error) {
|
|
449
385
|
const is401 = /401|unauthorized|auth.*fail/i.test(error);
|
|
450
386
|
const is403 = /403|forbidden/i.test(error);
|
|
@@ -474,7 +410,6 @@ async function runSync(sources) {
|
|
|
474
410
|
db.ensureTables();
|
|
475
411
|
const fileWriter = new FileWriter();
|
|
476
412
|
const syncResults = [];
|
|
477
|
-
const t0 = Date.now();
|
|
478
413
|
let totalNew = 0;
|
|
479
414
|
let gitManager = null;
|
|
480
415
|
try {
|
|
@@ -482,28 +417,32 @@ async function runSync(sources) {
|
|
|
482
417
|
await gitManager.init();
|
|
483
418
|
} catch {
|
|
484
419
|
}
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
420
|
+
const isTTY = process.stdout.isTTY;
|
|
421
|
+
let ui;
|
|
422
|
+
if (isTTY) {
|
|
423
|
+
const { renderSyncProgress } = await import("./render-TRVEKILS.js");
|
|
424
|
+
ui = renderSyncProgress(sources);
|
|
425
|
+
} else {
|
|
426
|
+
const { renderSyncProgressPlain } = await import("./render-TRVEKILS.js");
|
|
427
|
+
ui = renderSyncProgressPlain(sources);
|
|
428
|
+
}
|
|
429
|
+
for (const source of sources) {
|
|
488
430
|
const cred = loadCredential(source);
|
|
489
431
|
if (!cred) {
|
|
490
|
-
|
|
432
|
+
ui.updateSource(source, { status: "skipped" });
|
|
491
433
|
continue;
|
|
492
434
|
}
|
|
493
435
|
const sourceStart = Date.now();
|
|
494
|
-
|
|
495
|
-
progressLine("syncing...");
|
|
436
|
+
ui.updateSource(source, { status: "syncing", progress: "connecting..." });
|
|
496
437
|
const logger = {
|
|
497
438
|
info: (msg) => {
|
|
498
|
-
|
|
439
|
+
ui.updateSource(source, { progress: msg });
|
|
499
440
|
},
|
|
500
441
|
warn: (msg) => {
|
|
501
|
-
|
|
502
|
-
resultLine(false, `${c.dim}${msg}${c.reset}`);
|
|
442
|
+
ui.updateSource(source, { progress: `\u26A0 ${msg}` });
|
|
503
443
|
},
|
|
504
444
|
error: (msg) => {
|
|
505
|
-
|
|
506
|
-
console.error(` ${icon.fail} ${c.red}${msg}${c.reset}`);
|
|
445
|
+
ui.updateSource(source, { progress: `\u2717 ${msg}` });
|
|
507
446
|
}
|
|
508
447
|
};
|
|
509
448
|
try {
|
|
@@ -511,106 +450,129 @@ async function runSync(sources) {
|
|
|
511
450
|
case "feishu": {
|
|
512
451
|
const feishuCtx = new SyncContext(db.getSqlite(), logger, fileWriter);
|
|
513
452
|
const result = await syncFeishu(feishuCtx, cred.data, logger);
|
|
514
|
-
|
|
453
|
+
const parts = [`${result.newMessages} messages from ${result.chats} chats`];
|
|
515
454
|
totalNew += result.newMessages;
|
|
516
455
|
syncResults.push({ source: "feishu", newObjects: result.newMessages, label: "messages" });
|
|
517
456
|
try {
|
|
518
457
|
const mr = await syncFeishuMeetings(feishuCtx, cred.data, logger);
|
|
519
|
-
if (mr.newObjects > 0)
|
|
458
|
+
if (mr.newObjects > 0) parts.push(`${mr.newObjects} events`);
|
|
520
459
|
syncResults.push({ source: "feishu/meetings", newObjects: mr.newObjects, label: "events" });
|
|
521
460
|
} catch {
|
|
522
461
|
}
|
|
523
462
|
try {
|
|
524
463
|
const dr = await syncFeishuDocs(feishuCtx, cred.data, logger);
|
|
525
|
-
if (dr.newObjects > 0)
|
|
464
|
+
if (dr.newObjects > 0) parts.push(`${dr.newObjects} docs`);
|
|
526
465
|
syncResults.push({ source: "feishu/docs", newObjects: dr.newObjects, label: "docs" });
|
|
527
466
|
} catch {
|
|
528
467
|
}
|
|
529
468
|
try {
|
|
530
469
|
const ar = await syncFeishuApprovals(feishuCtx, cred.data, logger);
|
|
531
|
-
if (ar.newObjects > 0)
|
|
470
|
+
if (ar.newObjects > 0) parts.push(`${ar.newObjects} approvals`);
|
|
532
471
|
syncResults.push({ source: "feishu/approvals", newObjects: ar.newObjects, label: "approvals" });
|
|
533
472
|
} catch {
|
|
534
473
|
}
|
|
535
474
|
try {
|
|
536
475
|
const lr = await syncFeishuLinks(feishuCtx, cred.data, logger);
|
|
537
|
-
if (lr.fetched > 0)
|
|
538
|
-
else if (lr.extracted > 0) resultLine(false, `${lr.extracted} URLs found, ${lr.failed} failed to fetch`);
|
|
476
|
+
if (lr.fetched > 0) parts.push(`${lr.fetched} links`);
|
|
539
477
|
syncResults.push({ source: "feishu/links", newObjects: lr.fetched, label: "links" });
|
|
540
478
|
} catch {
|
|
541
479
|
}
|
|
480
|
+
ui.updateSource(source, {
|
|
481
|
+
status: "done",
|
|
482
|
+
result: parts.join(", "),
|
|
483
|
+
elapsedMs: Date.now() - sourceStart
|
|
484
|
+
});
|
|
542
485
|
break;
|
|
543
486
|
}
|
|
544
487
|
case "github": {
|
|
545
488
|
const ghCtx = new SyncContext(db.getSqlite(), logger, fileWriter);
|
|
546
489
|
const r = await syncGitHub(ghCtx, cred.data, logger);
|
|
547
490
|
const changeInfo = r.updatedObjects > 0 ? `, ${r.updatedObjects} updated` : "";
|
|
548
|
-
const cloneInfo = r.clonedRepos > 0 ? ` | ${r.clonedRepos}
|
|
549
|
-
resultLine(true, `${r.repos} repos, ${r.prs} PRs, ${r.issues} issues ${c.dim}(${r.newObjects} new${changeInfo})${cloneInfo}${c.reset}`);
|
|
491
|
+
const cloneInfo = r.clonedRepos > 0 ? ` | ${r.clonedRepos} cloned` : "";
|
|
550
492
|
totalNew += r.newObjects;
|
|
551
493
|
syncResults.push({ source: "github", newObjects: r.newObjects + r.updatedObjects });
|
|
494
|
+
ui.updateSource(source, {
|
|
495
|
+
status: "done",
|
|
496
|
+
result: `${r.repos} repos, ${r.prs} PRs, ${r.issues} issues (${r.newObjects} new${changeInfo})${cloneInfo}`,
|
|
497
|
+
elapsedMs: Date.now() - sourceStart
|
|
498
|
+
});
|
|
552
499
|
break;
|
|
553
500
|
}
|
|
554
501
|
case "gitlab": {
|
|
555
502
|
const glCtx = new SyncContext(db.getSqlite(), logger, fileWriter);
|
|
556
503
|
const r = await syncGitLab(glCtx, cred.data, logger);
|
|
557
504
|
const glChangeInfo = r.updatedObjects > 0 ? `, ${r.updatedObjects} updated` : "";
|
|
558
|
-
resultLine(true, `${r.projects} projects, ${r.mrs} MRs, ${r.issues} issues ${c.dim}(${r.newObjects} new${glChangeInfo})${c.reset}`);
|
|
559
505
|
totalNew += r.newObjects;
|
|
560
506
|
syncResults.push({ source: "gitlab", newObjects: r.newObjects + r.updatedObjects });
|
|
507
|
+
ui.updateSource(source, {
|
|
508
|
+
status: "done",
|
|
509
|
+
result: `${r.projects} projects, ${r.mrs} MRs, ${r.issues} issues (${r.newObjects} new${glChangeInfo})`,
|
|
510
|
+
elapsedMs: Date.now() - sourceStart
|
|
511
|
+
});
|
|
561
512
|
break;
|
|
562
513
|
}
|
|
563
514
|
case "linear": {
|
|
564
515
|
const lnCtx = new SyncContext(db.getSqlite(), logger, fileWriter);
|
|
565
516
|
const r = await syncLinear(lnCtx, cred.data, logger);
|
|
566
517
|
const lnChangeInfo = r.updatedObjects > 0 ? `, ${r.updatedObjects} updated` : "";
|
|
567
|
-
resultLine(true, `${r.issues} issues ${c.dim}(${r.newObjects} new${lnChangeInfo})${c.reset}`);
|
|
568
518
|
totalNew += r.newObjects;
|
|
569
519
|
syncResults.push({ source: "linear", newObjects: r.newObjects + r.updatedObjects, label: "issues" });
|
|
520
|
+
ui.updateSource(source, {
|
|
521
|
+
status: "done",
|
|
522
|
+
result: `${r.issues} issues (${r.newObjects} new${lnChangeInfo})`,
|
|
523
|
+
elapsedMs: Date.now() - sourceStart
|
|
524
|
+
});
|
|
570
525
|
break;
|
|
571
526
|
}
|
|
572
527
|
case "posthog": {
|
|
573
528
|
const phCtx = new SyncContext(db.getSqlite(), logger, fileWriter);
|
|
574
529
|
const r = await syncPostHog(phCtx, cred.data, logger);
|
|
575
530
|
const phChangeInfo = r.updatedObjects > 0 ? `, ${r.updatedObjects} updated` : "";
|
|
576
|
-
resultLine(true, `${r.insights} insights, ${r.events} events ${c.dim}(${r.newObjects} new${phChangeInfo})${c.reset}`);
|
|
577
531
|
totalNew += r.newObjects;
|
|
578
532
|
syncResults.push({ source: "posthog", newObjects: r.newObjects + r.updatedObjects });
|
|
533
|
+
ui.updateSource(source, {
|
|
534
|
+
status: "done",
|
|
535
|
+
result: `${r.insights} insights, ${r.events} events (${r.newObjects} new${phChangeInfo})`,
|
|
536
|
+
elapsedMs: Date.now() - sourceStart
|
|
537
|
+
});
|
|
579
538
|
break;
|
|
580
539
|
}
|
|
581
540
|
case "firebase": {
|
|
582
541
|
const fbCtx = new SyncContext(db.getSqlite(), logger, fileWriter);
|
|
583
542
|
const r = await syncFirebase(fbCtx, cred.data, logger);
|
|
584
543
|
const fbChangeInfo = r.updatedObjects > 0 ? `, ${r.updatedObjects} updated` : "";
|
|
585
|
-
resultLine(true, `${r.events} events ${c.dim}(${r.newObjects} new${fbChangeInfo})${c.reset}`);
|
|
586
544
|
totalNew += r.newObjects;
|
|
587
545
|
syncResults.push({ source: "firebase", newObjects: r.newObjects + r.updatedObjects, label: "events" });
|
|
546
|
+
ui.updateSource(source, {
|
|
547
|
+
status: "done",
|
|
548
|
+
result: `${r.events} events (${r.newObjects} new${fbChangeInfo})`,
|
|
549
|
+
elapsedMs: Date.now() - sourceStart
|
|
550
|
+
});
|
|
588
551
|
break;
|
|
589
552
|
}
|
|
590
553
|
default:
|
|
591
|
-
|
|
554
|
+
ui.updateSource(source, { status: "skipped" });
|
|
592
555
|
}
|
|
593
|
-
|
|
594
|
-
console.log(` ${c.dim}${elapsed(sourceStart)}${c.reset}`);
|
|
556
|
+
ui.setTotalNew(totalNew);
|
|
595
557
|
} catch (err) {
|
|
596
558
|
logError("sync", `Failed to sync ${source}`, { error: String(err) });
|
|
597
559
|
const errStr = String(err);
|
|
598
|
-
console.log(` ${icon.fail} ${c.red}sync failed${c.reset} ${c.dim}${errStr.slice(0, 60)}${c.reset}`);
|
|
599
560
|
const hint = getSyncErrorHint(source, errStr);
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
console.log(` ${progressBar(i + 1, sources.length)}`);
|
|
561
|
+
ui.updateSource(source, {
|
|
562
|
+
status: "error",
|
|
563
|
+
error: hint ? `${errStr.slice(0, 50)} \u2014 ${hint}` : errStr.slice(0, 80),
|
|
564
|
+
elapsedMs: Date.now() - sourceStart
|
|
565
|
+
});
|
|
606
566
|
}
|
|
607
567
|
}
|
|
608
|
-
|
|
609
|
-
console.log(` ${icon.link} ${c.dim}extracting links...${c.reset}`);
|
|
568
|
+
ui.updatePhase("linking");
|
|
610
569
|
const { processed, linksCreated } = linkAllUnprocessed(db.getSqlite());
|
|
611
570
|
if (processed > 0) {
|
|
612
|
-
|
|
571
|
+
ui.setLinkResult(`${linksCreated} links from ${processed} objects`);
|
|
572
|
+
} else {
|
|
573
|
+
ui.setLinkResult("no new links");
|
|
613
574
|
}
|
|
575
|
+
ui.updatePhase("backfilling");
|
|
614
576
|
try {
|
|
615
577
|
const sqlite = db.getSqlite();
|
|
616
578
|
const { readObjectContent } = await import("./content-reader-VPGTR2SF.js");
|
|
@@ -621,7 +583,6 @@ async function runSync(sources) {
|
|
|
621
583
|
LIMIT 500
|
|
622
584
|
`).all();
|
|
623
585
|
if (noFilePath.length > 0) {
|
|
624
|
-
console.log(` ${icon.sync} ${c.dim}backfilling ${noFilePath.length} objects without files...${c.reset}`);
|
|
625
586
|
let backfilled = 0;
|
|
626
587
|
for (const obj of noFilePath) {
|
|
627
588
|
try {
|
|
@@ -638,23 +599,22 @@ async function runSync(sources) {
|
|
|
638
599
|
}
|
|
639
600
|
}
|
|
640
601
|
if (backfilled > 0) {
|
|
641
|
-
|
|
602
|
+
ui.setBackfillResult(`${backfilled} files backfilled`);
|
|
642
603
|
}
|
|
643
604
|
}
|
|
644
605
|
} catch {
|
|
645
606
|
}
|
|
646
607
|
db.close();
|
|
647
608
|
if (gitManager) {
|
|
609
|
+
ui.updatePhase("committing");
|
|
648
610
|
try {
|
|
649
611
|
const sha = await gitManager.commitSync({ timestamp: (/* @__PURE__ */ new Date()).toISOString(), sources: syncResults });
|
|
650
|
-
if (sha)
|
|
612
|
+
if (sha) ui.setGitResult(`committed ${sha.slice(0, 7)}`);
|
|
651
613
|
} catch {
|
|
652
614
|
}
|
|
653
615
|
}
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
console.log(` ${c.bold}${totalNew}${c.reset} new objects synced from ${c.bold}${syncResults.length}${c.reset} sources`);
|
|
657
|
-
console.log("");
|
|
616
|
+
ui.setTotalNew(totalNew);
|
|
617
|
+
ui.done();
|
|
658
618
|
}
|
|
659
619
|
function syncCommand(program) {
|
|
660
620
|
program.command("sync").description("Sync data from connected sources").option("--source <source>", "Sync specific source only").action(async (opts) => {
|
package/dist/cli.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
syncCommand,
|
|
6
6
|
syncFirebase,
|
|
7
7
|
syncPostHog
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-NNCA4SWS.js";
|
|
9
9
|
import {
|
|
10
10
|
linkAllUnprocessed,
|
|
11
11
|
syncGitLab,
|
|
@@ -102,7 +102,7 @@ function initCommand(program2) {
|
|
|
102
102
|
default: true
|
|
103
103
|
}]);
|
|
104
104
|
if (continueSetup) {
|
|
105
|
-
const { runSetupWizard } = await import("./setup-
|
|
105
|
+
const { runSetupWizard } = await import("./setup-33MSPJXN.js");
|
|
106
106
|
await runSetupWizard();
|
|
107
107
|
} else {
|
|
108
108
|
console.log("");
|
|
@@ -2793,7 +2793,7 @@ function isInstalled(packageName) {
|
|
|
2793
2793
|
// src/cli.ts
|
|
2794
2794
|
process.env["I18NEXT_DISABLE_BANNER"] = "1";
|
|
2795
2795
|
var program = new Command();
|
|
2796
|
-
program.name("jowork").description("AI Agent Infrastructure \u2014 let AI agents truly understand your work").version("0.3.
|
|
2796
|
+
program.name("jowork").description("AI Agent Infrastructure \u2014 let AI agents truly understand your work").version("0.3.8");
|
|
2797
2797
|
initCommand(program);
|
|
2798
2798
|
serveCommand(program);
|
|
2799
2799
|
registerCommand(program);
|
|
@@ -2820,7 +2820,7 @@ program.action(async () => {
|
|
|
2820
2820
|
const config = readConfig2();
|
|
2821
2821
|
if (!config.initialized || !existsSync18(dbPath2())) {
|
|
2822
2822
|
if (process.stdin.isTTY) {
|
|
2823
|
-
const { runSetupWizard } = await import("./setup-
|
|
2823
|
+
const { runSetupWizard } = await import("./setup-33MSPJXN.js");
|
|
2824
2824
|
await runSetupWizard();
|
|
2825
2825
|
} else {
|
|
2826
2826
|
console.log("JoWork is not initialized. Run `jowork init` in an interactive terminal.");
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import "./chunk-UJ4KEHGZ.js";
|
|
2
|
+
|
|
3
|
+
// src/tui/render.tsx
|
|
4
|
+
import React2 from "react";
|
|
5
|
+
import { render } from "ink";
|
|
6
|
+
|
|
7
|
+
// src/tui/SyncProgress.tsx
|
|
8
|
+
import { useState, useEffect } from "react";
|
|
9
|
+
import { Box, Text } from "ink";
|
|
10
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
11
|
+
var SOURCE_COLORS = {
|
|
12
|
+
feishu: "cyan",
|
|
13
|
+
github: "white",
|
|
14
|
+
gitlab: "yellow",
|
|
15
|
+
linear: "cyan",
|
|
16
|
+
posthog: "red",
|
|
17
|
+
firebase: "yellow"
|
|
18
|
+
};
|
|
19
|
+
function sourceColor(name) {
|
|
20
|
+
return SOURCE_COLORS[name] ?? "white";
|
|
21
|
+
}
|
|
22
|
+
function ProgressBar({ value, width = 20 }) {
|
|
23
|
+
const filled = Math.round(Math.max(0, Math.min(1, value)) * width);
|
|
24
|
+
const empty = width - filled;
|
|
25
|
+
return /* @__PURE__ */ jsxs(Text, { children: [
|
|
26
|
+
/* @__PURE__ */ jsx(Text, { color: "green", children: "\u2588".repeat(filled) }),
|
|
27
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2591".repeat(empty) })
|
|
28
|
+
] });
|
|
29
|
+
}
|
|
30
|
+
function StatusIcon({ status }) {
|
|
31
|
+
switch (status) {
|
|
32
|
+
case "done":
|
|
33
|
+
return /* @__PURE__ */ jsx(Text, { color: "green", children: "\u2713" });
|
|
34
|
+
case "syncing":
|
|
35
|
+
return /* @__PURE__ */ jsx(Spinner, {});
|
|
36
|
+
case "error":
|
|
37
|
+
return /* @__PURE__ */ jsx(Text, { color: "red", children: "\u2717" });
|
|
38
|
+
case "skipped":
|
|
39
|
+
return /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u25CB" });
|
|
40
|
+
case "waiting":
|
|
41
|
+
return /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u25CB" });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
45
|
+
function Spinner() {
|
|
46
|
+
const [frame, setFrame] = useState(0);
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
const id = setInterval(() => setFrame((f) => (f + 1) % SPINNER_FRAMES.length), 80);
|
|
49
|
+
return () => clearInterval(id);
|
|
50
|
+
}, []);
|
|
51
|
+
return /* @__PURE__ */ jsx(Text, { color: "cyan", children: SPINNER_FRAMES[frame] });
|
|
52
|
+
}
|
|
53
|
+
function ElapsedTime({ ms }) {
|
|
54
|
+
const text = ms < 1e3 ? `${ms}ms` : `${(ms / 1e3).toFixed(1)}s`;
|
|
55
|
+
return /* @__PURE__ */ jsx(Text, { dimColor: true, children: text });
|
|
56
|
+
}
|
|
57
|
+
function PhaseIcon({ phase }) {
|
|
58
|
+
switch (phase) {
|
|
59
|
+
case "linking":
|
|
60
|
+
return /* @__PURE__ */ jsx(Text, { color: "cyan", children: "\u27E1" });
|
|
61
|
+
case "backfilling":
|
|
62
|
+
return /* @__PURE__ */ jsx(Spinner, {});
|
|
63
|
+
case "committing":
|
|
64
|
+
return /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2387" });
|
|
65
|
+
default:
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function SourceRow({ source }) {
|
|
70
|
+
const color = sourceColor(source.name);
|
|
71
|
+
const nameWidth = 10;
|
|
72
|
+
const padded = source.name.padEnd(nameWidth);
|
|
73
|
+
return /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: /* @__PURE__ */ jsxs(Box, { gap: 1, children: [
|
|
74
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
75
|
+
/* @__PURE__ */ jsx(StatusIcon, { status: source.status }),
|
|
76
|
+
/* @__PURE__ */ jsx(Text, { color, bold: true, children: padded }),
|
|
77
|
+
source.status === "syncing" && source.progress && /* @__PURE__ */ jsx(Text, { dimColor: true, children: source.progress }),
|
|
78
|
+
source.status === "done" && source.result && /* @__PURE__ */ jsx(Text, { children: source.result }),
|
|
79
|
+
source.status === "done" && source.elapsedMs != null && /* @__PURE__ */ jsx(ElapsedTime, { ms: source.elapsedMs }),
|
|
80
|
+
source.status === "error" && source.error && /* @__PURE__ */ jsx(Text, { color: "red", children: source.error.slice(0, 60) }),
|
|
81
|
+
source.status === "skipped" && /* @__PURE__ */ jsx(Text, { dimColor: true, children: "no credentials, skipping" })
|
|
82
|
+
] }) });
|
|
83
|
+
}
|
|
84
|
+
function SyncProgress({ state }) {
|
|
85
|
+
const [now, setNow] = useState(Date.now());
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
if (state.phase === "done") return;
|
|
88
|
+
const id = setInterval(() => setNow(Date.now()), 200);
|
|
89
|
+
return () => clearInterval(id);
|
|
90
|
+
}, [state.phase]);
|
|
91
|
+
const elapsedMs = now - state.startTime;
|
|
92
|
+
const doneCount = state.sources.filter((s) => s.status === "done").length;
|
|
93
|
+
const total = state.sources.length;
|
|
94
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingTop: 1, children: [
|
|
95
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
96
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
97
|
+
/* @__PURE__ */ jsxs(Text, { bold: true, children: [
|
|
98
|
+
"Syncing ",
|
|
99
|
+
total,
|
|
100
|
+
" source",
|
|
101
|
+
total > 1 ? "s" : ""
|
|
102
|
+
] })
|
|
103
|
+
] }),
|
|
104
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
105
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
106
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2500".repeat(40) })
|
|
107
|
+
] }),
|
|
108
|
+
/* @__PURE__ */ jsx(Box, { flexDirection: "column", paddingTop: 1, gap: 0, children: state.sources.map((source) => /* @__PURE__ */ jsx(SourceRow, { source }, source.name)) }),
|
|
109
|
+
total > 1 && /* @__PURE__ */ jsxs(Box, { paddingTop: 1, gap: 1, children: [
|
|
110
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
111
|
+
/* @__PURE__ */ jsx(ProgressBar, { value: doneCount / total }),
|
|
112
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
113
|
+
doneCount,
|
|
114
|
+
"/",
|
|
115
|
+
total
|
|
116
|
+
] })
|
|
117
|
+
] }),
|
|
118
|
+
(state.phase === "linking" || state.linkResult) && /* @__PURE__ */ jsxs(Box, { paddingTop: 1, gap: 1, children: [
|
|
119
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
120
|
+
/* @__PURE__ */ jsx(PhaseIcon, { phase: state.linkResult ? "done" : "linking" }),
|
|
121
|
+
state.linkResult ? /* @__PURE__ */ jsxs(Text, { children: [
|
|
122
|
+
/* @__PURE__ */ jsx(Text, { color: "green", children: "\u2713" }),
|
|
123
|
+
" ",
|
|
124
|
+
state.linkResult
|
|
125
|
+
] }) : /* @__PURE__ */ jsx(Text, { dimColor: true, children: "extracting links..." })
|
|
126
|
+
] }),
|
|
127
|
+
state.backfillResult && /* @__PURE__ */ jsxs(Box, { gap: 1, children: [
|
|
128
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
129
|
+
/* @__PURE__ */ jsx(Text, { color: "green", children: "\u2713" }),
|
|
130
|
+
/* @__PURE__ */ jsx(Text, { children: state.backfillResult })
|
|
131
|
+
] }),
|
|
132
|
+
state.gitResult && /* @__PURE__ */ jsxs(Box, { gap: 1, children: [
|
|
133
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
134
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2387" }),
|
|
135
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: state.gitResult })
|
|
136
|
+
] }),
|
|
137
|
+
state.phase === "done" && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingTop: 1, paddingBottom: 1, children: [
|
|
138
|
+
/* @__PURE__ */ jsxs(Box, { gap: 1, children: [
|
|
139
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
140
|
+
/* @__PURE__ */ jsx(Text, { bold: true, color: "green", children: "Done" }),
|
|
141
|
+
/* @__PURE__ */ jsx(ElapsedTime, { ms: elapsedMs })
|
|
142
|
+
] }),
|
|
143
|
+
/* @__PURE__ */ jsxs(Box, { gap: 1, children: [
|
|
144
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
145
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: state.totalNew }),
|
|
146
|
+
/* @__PURE__ */ jsx(Text, { children: "new objects synced from" }),
|
|
147
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: doneCount }),
|
|
148
|
+
/* @__PURE__ */ jsx(Text, { children: "sources" })
|
|
149
|
+
] })
|
|
150
|
+
] }),
|
|
151
|
+
state.phase !== "done" && /* @__PURE__ */ jsxs(Box, { paddingTop: 1, gap: 1, children: [
|
|
152
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
153
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "elapsed:" }),
|
|
154
|
+
/* @__PURE__ */ jsx(ElapsedTime, { ms: elapsedMs })
|
|
155
|
+
] })
|
|
156
|
+
] });
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// src/tui/render.tsx
|
|
160
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
161
|
+
function renderSyncProgress(sourceNames) {
|
|
162
|
+
const state = {
|
|
163
|
+
sources: sourceNames.map((name) => ({
|
|
164
|
+
name,
|
|
165
|
+
status: "waiting"
|
|
166
|
+
})),
|
|
167
|
+
totalNew: 0,
|
|
168
|
+
phase: "syncing",
|
|
169
|
+
startTime: Date.now()
|
|
170
|
+
};
|
|
171
|
+
let triggerUpdate;
|
|
172
|
+
function App() {
|
|
173
|
+
const [, setTick] = React2.useState(0);
|
|
174
|
+
triggerUpdate = () => setTick((t) => t + 1);
|
|
175
|
+
return /* @__PURE__ */ jsx2(SyncProgress, { state });
|
|
176
|
+
}
|
|
177
|
+
const instance = render(/* @__PURE__ */ jsx2(App, {}));
|
|
178
|
+
function rerender() {
|
|
179
|
+
triggerUpdate?.();
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
updateSource(name, patch) {
|
|
183
|
+
const src = state.sources.find((s) => s.name === name);
|
|
184
|
+
if (src) {
|
|
185
|
+
Object.assign(src, patch);
|
|
186
|
+
rerender();
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
updatePhase(phase) {
|
|
190
|
+
state.phase = phase;
|
|
191
|
+
rerender();
|
|
192
|
+
},
|
|
193
|
+
setTotalNew(n) {
|
|
194
|
+
state.totalNew = n;
|
|
195
|
+
rerender();
|
|
196
|
+
},
|
|
197
|
+
setLinkResult(msg) {
|
|
198
|
+
state.linkResult = msg;
|
|
199
|
+
rerender();
|
|
200
|
+
},
|
|
201
|
+
setBackfillResult(msg) {
|
|
202
|
+
state.backfillResult = msg;
|
|
203
|
+
rerender();
|
|
204
|
+
},
|
|
205
|
+
setGitResult(msg) {
|
|
206
|
+
state.gitResult = msg;
|
|
207
|
+
rerender();
|
|
208
|
+
},
|
|
209
|
+
done() {
|
|
210
|
+
state.phase = "done";
|
|
211
|
+
rerender();
|
|
212
|
+
setTimeout(() => instance.unmount(), 100);
|
|
213
|
+
},
|
|
214
|
+
getState() {
|
|
215
|
+
return state;
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
function renderSyncProgressPlain(sourceNames) {
|
|
220
|
+
const state = {
|
|
221
|
+
sources: sourceNames.map((name) => ({
|
|
222
|
+
name,
|
|
223
|
+
status: "waiting"
|
|
224
|
+
})),
|
|
225
|
+
totalNew: 0,
|
|
226
|
+
phase: "syncing",
|
|
227
|
+
startTime: Date.now()
|
|
228
|
+
};
|
|
229
|
+
console.log(`
|
|
230
|
+
Syncing ${sourceNames.length} source${sourceNames.length > 1 ? "s" : ""}`);
|
|
231
|
+
console.log(` ${"\u2500".repeat(40)}`);
|
|
232
|
+
return {
|
|
233
|
+
updateSource(name, patch) {
|
|
234
|
+
const src = state.sources.find((s) => s.name === name);
|
|
235
|
+
if (!src) return;
|
|
236
|
+
Object.assign(src, patch);
|
|
237
|
+
if (patch.status === "syncing" && !patch.result) return;
|
|
238
|
+
if (patch.status === "done" && patch.result) {
|
|
239
|
+
const elapsed = src.elapsedMs != null ? ` (${src.elapsedMs < 1e3 ? `${src.elapsedMs}ms` : `${(src.elapsedMs / 1e3).toFixed(1)}s`})` : "";
|
|
240
|
+
console.log(` \u2713 ${name} ${patch.result}${elapsed}`);
|
|
241
|
+
}
|
|
242
|
+
if (patch.status === "error") {
|
|
243
|
+
console.log(` \u2717 ${name} ${patch.error ?? "sync failed"}`);
|
|
244
|
+
}
|
|
245
|
+
if (patch.status === "skipped") {
|
|
246
|
+
console.log(` \u25CB ${name} no credentials, skipping`);
|
|
247
|
+
}
|
|
248
|
+
},
|
|
249
|
+
updatePhase(phase) {
|
|
250
|
+
state.phase = phase;
|
|
251
|
+
},
|
|
252
|
+
setTotalNew(n) {
|
|
253
|
+
state.totalNew = n;
|
|
254
|
+
},
|
|
255
|
+
setLinkResult(msg) {
|
|
256
|
+
console.log(` \u2713 ${msg}`);
|
|
257
|
+
state.linkResult = msg;
|
|
258
|
+
},
|
|
259
|
+
setBackfillResult(msg) {
|
|
260
|
+
console.log(` \u2713 ${msg}`);
|
|
261
|
+
state.backfillResult = msg;
|
|
262
|
+
},
|
|
263
|
+
setGitResult(msg) {
|
|
264
|
+
console.log(` \u2387 ${msg}`);
|
|
265
|
+
state.gitResult = msg;
|
|
266
|
+
},
|
|
267
|
+
done() {
|
|
268
|
+
state.phase = "done";
|
|
269
|
+
const ms = Date.now() - state.startTime;
|
|
270
|
+
const elapsed = ms < 1e3 ? `${ms}ms` : `${(ms / 1e3).toFixed(1)}s`;
|
|
271
|
+
console.log(`
|
|
272
|
+
Done in ${elapsed}`);
|
|
273
|
+
console.log(` ${state.totalNew} new objects synced
|
|
274
|
+
`);
|
|
275
|
+
},
|
|
276
|
+
getState() {
|
|
277
|
+
return state;
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
export {
|
|
282
|
+
renderSyncProgress,
|
|
283
|
+
renderSyncProgressPlain
|
|
284
|
+
};
|
|
@@ -212,7 +212,7 @@ async function runSetupWizard() {
|
|
|
212
212
|
console.log("");
|
|
213
213
|
console.log(zh ? ` \u6B63\u5728\u540C\u6B65 ${connectedSources.length} \u4E2A\u6570\u636E\u6E90...` : ` Syncing ${connectedSources.length} source${connectedSources.length > 1 ? "s" : ""}...`);
|
|
214
214
|
console.log("");
|
|
215
|
-
const { runSync } = await import("./sync-
|
|
215
|
+
const { runSync } = await import("./sync-E46TEV3R.js");
|
|
216
216
|
try {
|
|
217
217
|
await runSync(connectedSources);
|
|
218
218
|
} catch {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jowork",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.8",
|
|
4
4
|
"description": "AI Agent Infrastructure — let AI agents truly understand your work. Connect data sources, give agents awareness and goals. Local-first, one command.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "AGPL-3.0",
|
|
@@ -47,17 +47,23 @@
|
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@hono/node-server": "^1.14.0",
|
|
50
|
+
"@json-render/core": "^0.15.0",
|
|
51
|
+
"@json-render/ink": "^0.15.0",
|
|
50
52
|
"@modelcontextprotocol/sdk": "^1.27.0",
|
|
53
|
+
"@types/react": "^19.2.14",
|
|
51
54
|
"better-sqlite3": "^12.6.2",
|
|
52
55
|
"chalk": "^5.4.0",
|
|
53
56
|
"commander": "^13.0.0",
|
|
54
57
|
"croner": "^9.0.0",
|
|
55
58
|
"drizzle-orm": "^0.44.0",
|
|
56
59
|
"hono": "^4.7.0",
|
|
60
|
+
"ink": "^6.8.0",
|
|
57
61
|
"inquirer": "^12.0.0",
|
|
58
62
|
"ora": "^8.0.0",
|
|
59
63
|
"pino": "^9.0.0",
|
|
60
64
|
"pino-pretty": "^13.0.0",
|
|
65
|
+
"react": "^19.2.4",
|
|
66
|
+
"react-devtools-core": "^7.0.1",
|
|
61
67
|
"simple-git": "^3.33.0",
|
|
62
68
|
"ws": "^8.18.3",
|
|
63
69
|
"zod": "^3.24.0"
|