synthesisui 0.16.22 → 0.16.23
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/commands/sync.js +98 -0
- package/dist/index.js +4 -0
- package/package.json +1 -1
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
import { readToken, resolveRegistry } from "../config.js";
|
|
4
|
+
import { readEvents } from "../doctor/ledger.js";
|
|
5
|
+
import { readRequests } from "../doctor/requests.js";
|
|
6
|
+
import { body, section } from "../output.js";
|
|
7
|
+
/**
|
|
8
|
+
* `synthesisui sync` - the ledger travels, because a person said so.
|
|
9
|
+
*
|
|
10
|
+
* The hook writes the ledger and NEVER touches the network; the doctor reads
|
|
11
|
+
* it and never sends it. This command is the one place the record leaves the
|
|
12
|
+
* machine, and it only runs by hand. That split is load-bearing: a check that
|
|
13
|
+
* phones home is a check that gets uninstalled, and the entire governance
|
|
14
|
+
* stack stands on the check staying installed.
|
|
15
|
+
*
|
|
16
|
+
* What it buys: the dashboard answer to the question a design-system lead
|
|
17
|
+
* asks before paying for anything - is my team's drift going up or down? Run
|
|
18
|
+
* it after a session, from CI after a merge, or never; the local record is
|
|
19
|
+
* complete either way.
|
|
20
|
+
*
|
|
21
|
+
* Re-running is free and safe. The server dedupes events on their identity
|
|
22
|
+
* and treats the request queue as a snapshot, so the local files remain the
|
|
23
|
+
* single source of truth.
|
|
24
|
+
*/
|
|
25
|
+
export async function sync(opts) {
|
|
26
|
+
const root = resolve(opts.dir ?? process.cwd());
|
|
27
|
+
const base = resolveRegistry(opts.registry);
|
|
28
|
+
const token = await readToken();
|
|
29
|
+
if (!token) {
|
|
30
|
+
console.log(section("Sync"));
|
|
31
|
+
console.log(body("Not logged in. The record stays local until you say so:"));
|
|
32
|
+
console.log(body(" synthesisui login"));
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
// Which system does this project's record belong to? The .lock says.
|
|
36
|
+
const dsDir = join(root, "_synthesisui", "ds");
|
|
37
|
+
let slug = null;
|
|
38
|
+
try {
|
|
39
|
+
const slugs = (await readdir(dsDir, { withFileTypes: true }))
|
|
40
|
+
.filter((e) => e.isDirectory())
|
|
41
|
+
.map((e) => e.name);
|
|
42
|
+
for (const s of slugs) {
|
|
43
|
+
const raw = await readFile(join(dsDir, s, ".lock"), "utf8").catch(() => "");
|
|
44
|
+
try {
|
|
45
|
+
const lock = JSON.parse(raw);
|
|
46
|
+
// An adopted system is described, not owned on the platform - there is
|
|
47
|
+
// no dashboard row to sync into (yet).
|
|
48
|
+
if (lock.slug && !lock.adopted) {
|
|
49
|
+
slug = lock.slug;
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// unreadable lock - try the next
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// no _synthesisui at all
|
|
60
|
+
}
|
|
61
|
+
if (!slug) {
|
|
62
|
+
console.log(section("Sync"));
|
|
63
|
+
console.log(body("No installed system here, so there is nowhere to sync to."));
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const events = await readEvents(root);
|
|
67
|
+
const requests = await readRequests(root);
|
|
68
|
+
if (events.length === 0 && requests.length === 0) {
|
|
69
|
+
console.log(section("Sync"));
|
|
70
|
+
console.log(body("Nothing recorded yet. The record fills as the hook checks writes and doctor runs."));
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const res = await fetch(`${base}/api/ledger`, {
|
|
74
|
+
method: "POST",
|
|
75
|
+
headers: {
|
|
76
|
+
"content-type": "application/json",
|
|
77
|
+
Authorization: `Bearer ${token}`,
|
|
78
|
+
},
|
|
79
|
+
body: JSON.stringify({ slug, events, requests }),
|
|
80
|
+
}).catch(() => null);
|
|
81
|
+
if (!res) {
|
|
82
|
+
console.log(section("Sync"));
|
|
83
|
+
console.log(body("Could not reach the registry. The record is safe locally."));
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (!res.ok) {
|
|
87
|
+
const err = (await res.json().catch(() => ({})));
|
|
88
|
+
console.log(section("Sync"));
|
|
89
|
+
console.log(body(err.message ?? `The registry answered ${res.status}.`));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const out = (await res.json());
|
|
93
|
+
console.log(section("Sync"));
|
|
94
|
+
console.log(body(`${out.eventsReceived} checks sent, ${out.eventsNew} new. ${out.requestsNow} open request${out.requestsNow === 1 ? "" : "s"}.`));
|
|
95
|
+
console.log("");
|
|
96
|
+
console.log(body(`The record lives on your system's overview:`));
|
|
97
|
+
console.log(body(` ${base}/dashboard/mine/${slug}`));
|
|
98
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -15,6 +15,7 @@ import { login } from "./commands/login.js";
|
|
|
15
15
|
import { mcp } from "./commands/mcp.js";
|
|
16
16
|
import { refit } from "./commands/refit.js";
|
|
17
17
|
import { request } from "./commands/request.js";
|
|
18
|
+
import { sync } from "./commands/sync.js";
|
|
18
19
|
import { template } from "./commands/template.js";
|
|
19
20
|
import { upgrade } from "./commands/upgrade.js";
|
|
20
21
|
import { use } from "./commands/use.js";
|
|
@@ -331,6 +332,9 @@ async function main() {
|
|
|
331
332
|
case "clean":
|
|
332
333
|
await clean({ dir, force: flags.force === true });
|
|
333
334
|
break;
|
|
335
|
+
case "sync":
|
|
336
|
+
await sync({ dir, registry });
|
|
337
|
+
break;
|
|
334
338
|
case "request": {
|
|
335
339
|
// `synthesisui request` lists; `request component --name x --for "..."`
|
|
336
340
|
// files; `request --done <id>` closes. The agent's front door is the MCP
|
package/package.json
CHANGED