@zuzuucodes/cli 1.3.0 → 1.4.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/bin/zuzuu.mjs +4 -2
- package/package.json +1 -1
- package/web-app/dist/index.js +19 -0
- package/web-app/dist/instance-file.js +62 -0
- package/web-app/dist/zuzuu-api.js +12 -2
- package/web-app/web-dist/assets/{DiffTab-CihRJjzf.js → DiffTab-BuWonUNJ.js} +1 -1
- package/web-app/web-dist/assets/{MonacoFile-DJvpGyW2.js → MonacoFile-CL3DhFKG.js} +1 -1
- package/web-app/web-dist/assets/{cssMode-R1Bks9TO.js → cssMode-B9jnrWOz.js} +1 -1
- package/web-app/web-dist/assets/{dist-jCnX6g-O.js → dist-ChcDQ_7s.js} +1 -1
- package/web-app/web-dist/assets/{htmlMode-Csqnn3yv.js → htmlMode-Bi8vSvwb.js} +1 -1
- package/web-app/web-dist/assets/index--5yy8RbA.js +267 -0
- package/web-app/web-dist/assets/index-BVG4hgk7.css +2 -0
- package/web-app/web-dist/assets/{jsonMode-DRBg9jwi.js → jsonMode-C6ELX5GM.js} +1 -1
- package/web-app/web-dist/assets/{monaco-setup-Dszx738Y.js → monaco-setup-CsR6EfHe.js} +3 -3
- package/web-app/web-dist/assets/{tsMode-9YOHYiVQ.js → tsMode-a8OvovQd.js} +1 -1
- package/web-app/web-dist/index.html +2 -2
- package/zuzuu/commands/web.mjs +113 -8
- package/web-app/web-dist/assets/index-D_MPtALn.css +0 -2
- package/web-app/web-dist/assets/index-Ye54YyTn.js +0 -267
package/bin/zuzuu.mjs
CHANGED
|
@@ -61,7 +61,9 @@ function help() {
|
|
|
61
61
|
usage: zuzuu <command> [options]
|
|
62
62
|
|
|
63
63
|
code [dir] launch OpenCode as the bundled default host (faculty home + capture + gate + digest)
|
|
64
|
-
web [dir]
|
|
64
|
+
web [dir] [--stop|--status]
|
|
65
|
+
launch the visual workbench (reuses a running one;
|
|
66
|
+
--stop ends it, --status reports it)
|
|
65
67
|
init scaffold the faculty home (.zuzuu/) — git-style, idempotent
|
|
66
68
|
status detected hosts + recorded sessions
|
|
67
69
|
capture [--host NAME] capture a session → .zuzuu/.traces + .zuzuu/sessions.json
|
|
@@ -108,7 +110,7 @@ const args = parseArgs(rest);
|
|
|
108
110
|
|
|
109
111
|
switch (cmd) {
|
|
110
112
|
case 'code': process.exit(code(args)); break;
|
|
111
|
-
case 'web': web(args); break;
|
|
113
|
+
case 'web': await web(args); break;
|
|
112
114
|
case 'init': init(args); break;
|
|
113
115
|
case 'remember': remember(args); break;
|
|
114
116
|
case 'recall': await recall(args); break;
|
package/package.json
CHANGED
package/web-app/dist/index.js
CHANGED
|
@@ -7,6 +7,7 @@ import { fileURLToPath } from "node:url";
|
|
|
7
7
|
import crypto from "node:crypto";
|
|
8
8
|
import { WebcodeServer } from "./server.js";
|
|
9
9
|
import { addRecent } from "./config.js";
|
|
10
|
+
import { writeInstanceFile, removeInstanceFile } from "./instance-file.js";
|
|
10
11
|
const HERE = path.dirname(fileURLToPath(import.meta.url));
|
|
11
12
|
const DEFAULT_PORT = 7770;
|
|
12
13
|
function parseArgs(argv) {
|
|
@@ -147,14 +148,32 @@ async function main() {
|
|
|
147
148
|
console.log(`\n zuzuu-web v${pkg.version}`);
|
|
148
149
|
console.log(` workspace ${root}`);
|
|
149
150
|
console.log(` url ${url}\n`);
|
|
151
|
+
// Singleton contract: record this instance so `zuzuu web` can reuse it
|
|
152
|
+
// instead of spawning a duplicate (never in hosted mode; never fatal).
|
|
153
|
+
if (!hosted) {
|
|
154
|
+
writeInstanceFile({
|
|
155
|
+
root,
|
|
156
|
+
port: boundPort,
|
|
157
|
+
pid: process.pid,
|
|
158
|
+
token,
|
|
159
|
+
startedAt: new Date().toISOString(),
|
|
160
|
+
version: pkg.version,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
150
163
|
if (args.open)
|
|
151
164
|
openBrowser(url);
|
|
152
165
|
});
|
|
153
166
|
const shutdown = () => {
|
|
167
|
+
if (!hosted)
|
|
168
|
+
removeInstanceFile(root);
|
|
154
169
|
server.stop();
|
|
155
170
|
process.exit(0);
|
|
156
171
|
};
|
|
157
172
|
process.on("SIGINT", shutdown);
|
|
158
173
|
process.on("SIGTERM", shutdown);
|
|
174
|
+
process.on("exit", () => {
|
|
175
|
+
if (!hosted)
|
|
176
|
+
removeInstanceFile(root); // best-effort; idempotent after shutdown()
|
|
177
|
+
});
|
|
159
178
|
}
|
|
160
179
|
void main();
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// Per-workspace daemon instance state — the singleton contract with `zuzuu web`.
|
|
2
|
+
//
|
|
3
|
+
// After a successful listen the daemon writes
|
|
4
|
+
// ~/.webcode/instances/<sha256(realpath-root).slice(0,16)>.json
|
|
5
|
+
// and removes it on clean shutdown. The zuzuu CLI computes the same path for a
|
|
6
|
+
// workspace to discover (and reuse / stop) an already-running daemon instead of
|
|
7
|
+
// spawning a fresh one — so the port + token stay stable across `zuzuu web` runs.
|
|
8
|
+
//
|
|
9
|
+
// Security note: the file contains the auth token. That's acceptable here —
|
|
10
|
+
// it's 0600 in the user's own home directory, and the same token already
|
|
11
|
+
// appears in the daemon's own stdout URL. Hosted mode never writes this file.
|
|
12
|
+
import crypto from "node:crypto";
|
|
13
|
+
import fs from "node:fs";
|
|
14
|
+
import os from "node:os";
|
|
15
|
+
import path from "node:path";
|
|
16
|
+
export function instancesDir() {
|
|
17
|
+
return path.join(os.homedir(), ".webcode", "instances");
|
|
18
|
+
}
|
|
19
|
+
/** Deterministic per-workspace file path. `root` must already be realpath'd. */
|
|
20
|
+
export function instancePath(root, dir = instancesDir()) {
|
|
21
|
+
const id = crypto.createHash("sha256").update(root).digest("hex").slice(0, 16);
|
|
22
|
+
return path.join(dir, `${id}.json`);
|
|
23
|
+
}
|
|
24
|
+
/** Write the instance file (0600). Never throws — a failed write only costs reuse. */
|
|
25
|
+
export function writeInstanceFile(info, dir = instancesDir()) {
|
|
26
|
+
const file = instancePath(info.root, dir);
|
|
27
|
+
try {
|
|
28
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
29
|
+
fs.writeFileSync(file, JSON.stringify(info, null, 2) + "\n", { mode: 0o600 });
|
|
30
|
+
fs.chmodSync(file, 0o600); // mode option only applies on create; enforce on overwrite too
|
|
31
|
+
return file;
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
console.warn(`zuzuu-web: could not write instance state (${String(err)}) — continuing without it`);
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/** Best-effort read; null on missing/corrupt. */
|
|
39
|
+
export function readInstanceFile(root, dir = instancesDir()) {
|
|
40
|
+
try {
|
|
41
|
+
return JSON.parse(fs.readFileSync(instancePath(root, dir), "utf8"));
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Best-effort removal on shutdown. Only removes the file if it still belongs
|
|
49
|
+
* to `pid` — if another daemon raced us and overwrote it, leave theirs alone.
|
|
50
|
+
*/
|
|
51
|
+
export function removeInstanceFile(root, pid = process.pid, dir = instancesDir()) {
|
|
52
|
+
const file = instancePath(root, dir);
|
|
53
|
+
try {
|
|
54
|
+
const parsed = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
55
|
+
if (typeof parsed.pid === "number" && parsed.pid !== pid)
|
|
56
|
+
return; // not ours anymore
|
|
57
|
+
fs.unlinkSync(file);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
/* already gone or unreadable — nothing to do */
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -128,6 +128,16 @@ function binAvailable(binary) {
|
|
|
128
128
|
return false;
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
|
+
/** Count item FILES in a faculty dir — knowledge items are .md, others .json/.md. */
|
|
132
|
+
async function countItemFiles(dir) {
|
|
133
|
+
try {
|
|
134
|
+
const names = (await fsp.readdir(dir)).filter((n) => n.endsWith(".json") || n.endsWith(".md"));
|
|
135
|
+
return { count: names.length, names };
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
return { count: 0, names: [] };
|
|
139
|
+
}
|
|
140
|
+
}
|
|
131
141
|
/** Read every *.json in a dir into objects; missing dir → [], corrupt file → skipped. */
|
|
132
142
|
async function readJsonDir(dir) {
|
|
133
143
|
let names = [];
|
|
@@ -181,7 +191,7 @@ export function createZuzuuApi(getRoot, opts = {}) {
|
|
|
181
191
|
const faculties = [];
|
|
182
192
|
for (const key of FACULTIES) {
|
|
183
193
|
const itemsDir = itemsDirOf(agent, key);
|
|
184
|
-
const count = itemsDir ? (await
|
|
194
|
+
const count = itemsDir ? (await countItemFiles(itemsDir)).count : 0;
|
|
185
195
|
const pending = (await proposalsOf(agent, key)).length;
|
|
186
196
|
faculties.push({ key, count, pending });
|
|
187
197
|
}
|
|
@@ -194,7 +204,7 @@ export function createZuzuuApi(getRoot, opts = {}) {
|
|
|
194
204
|
const agent = await agentDir();
|
|
195
205
|
const itemsDir = itemsDirOf(agent, key);
|
|
196
206
|
const items = itemsDir
|
|
197
|
-
? (await
|
|
207
|
+
? (await countItemFiles(itemsDir)).names.map((n) => ({ id: n.replace(/\.(json|md)$/, ""), title: n.replace(/\.(json|md)$/, "") }))
|
|
198
208
|
: [];
|
|
199
209
|
const proposals = (await proposalsOf(agent, key)).map((p) => ({ id: String(p.id ?? "?"), faculty: key, title: proposalTitle(p) }));
|
|
200
210
|
return c.json({ key, items, proposals });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{d as e,f as t,u as n}from"./index
|
|
1
|
+
import{d as e,f as t,u as n}from"./index--5yy8RbA.js";import{i as r,n as i,t as a}from"./monaco-setup-CsR6EfHe.js";var o=t();function s({path:t,name:s}){let{data:l,isLoading:u,error:d}=e({queryKey:[`git`,`diff`,t],queryFn:async()=>{let[e,r]=await Promise.all([n.gitDiff(t),n.readFile(t).catch(()=>``)]);return{original:e.original,working:r}}});return d?(0,o.jsx)(c,{danger:!0,children:d.message}):u||!l?(0,o.jsx)(c,{children:`loading diff…`}):(0,o.jsx)(r,{original:l.original,modified:l.working,language:i(s),theme:a(),options:{readOnly:!0,renderSideBySide:!0,fontFamily:`"JetBrains Mono Variable", ui-monospace, monospace`,fontSize:13,minimap:{enabled:!1},automaticLayout:!0}})}function c({children:e,danger:t}){return(0,o.jsx)(`div`,{className:`flex h-full items-center justify-center text-ui ${t?`text-danger`:`text-ink-500`}`,children:e})}export{s as DiffTab,s as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{f as e,l as t}from"./index
|
|
1
|
+
import{f as e,l as t}from"./index--5yy8RbA.js";import{n,r,t as i}from"./monaco-setup-CsR6EfHe.js";var a=e();function o({path:e,name:o}){let c=t(t=>t.buffers[e]),l=t(e=>e.setValue),u=t(e=>e.save);return!c||c.loading?(0,a.jsx)(s,{children:`loading…`}):c.error?(0,a.jsx)(s,{danger:!0,children:c.error}):(0,a.jsx)(r,{path:e,language:n(o),theme:i(),value:c.value,onChange:t=>l(e,t??``),onMount:(t,n)=>{t.addCommand(n.KeyMod.CtrlCmd|n.KeyCode.KeyS,()=>{u(e)})},options:{fontFamily:`"JetBrains Mono Variable", ui-monospace, monospace`,fontSize:13,lineHeight:1.5,minimap:{enabled:!0,scale:1},scrollBeyondLastLine:!1,smoothScrolling:!0,renderWhitespace:`selection`,tabSize:2,automaticLayout:!0,padding:{top:8}}})}function s({children:e,danger:t}){return(0,a.jsx)(`div`,{className:`flex h-full items-center justify-center text-ui ${t?`text-danger`:`text-ink-500`}`,children:e})}export{o as MonacoFile,o as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{h as e}from"./editor.api2-BmGoRSl4.js";import{o as t}from"./monaco-setup-
|
|
1
|
+
import{h as e}from"./editor.api2-BmGoRSl4.js";import{o as t}from"./monaco-setup-CsR6EfHe.js";import{_ as n,a as r,c as i,d as a,f as o,g as s,h as c,i as l,l as u,m as d,n as f,o as p,p as m,r as h,s as g,t as _,u as v,v as y}from"./lspLanguageFeatures-gTnJsses.js";var b=120*1e3,x=class{constructor(e){this._defaults=e,this._worker=null,this._client=null,this._idleCheckInterval=window.setInterval(()=>this._checkIfIdle(),30*1e3),this._lastUsedTime=0,this._configChangeListener=this._defaults.onDidChange(()=>this._stopWorker())}_stopWorker(){this._worker&&=(this._worker.dispose(),null),this._client=null}dispose(){clearInterval(this._idleCheckInterval),this._configChangeListener.dispose(),this._stopWorker()}_checkIfIdle(){this._worker&&Date.now()-this._lastUsedTime>b&&this._stopWorker()}_getClient(){return this._lastUsedTime=Date.now(),this._client||=(this._worker=t({moduleId:`vs/language/css/cssWorker`,createWorker:()=>new Worker(new URL(`/assets/css.worker-CvXBzhp8.js`,``+import.meta.url),{type:`module`}),label:this._defaults.languageId,createData:{options:this._defaults.options,languageId:this._defaults.languageId}}),this._worker.getProxy()),this._client}getLanguageServiceWorker(...e){let t;return this._getClient().then(e=>{t=e}).then(t=>{if(this._worker)return this._worker.withSyncedResources(e)}).then(e=>t)}};function S(t){let n=[],s=[],c=new x(t);n.push(c);let g=(...e)=>c.getLanguageServiceWorker(...e);function y(){let{languageId:n,modeConfiguration:c}=t;w(s),c.completionItems&&s.push(e.registerCompletionItemProvider(n,new _(g,[`/`,`-`,`:`]))),c.hovers&&s.push(e.registerHoverProvider(n,new a(g))),c.documentHighlights&&s.push(e.registerDocumentHighlightProvider(n,new p(g))),c.definitions&&s.push(e.registerDefinitionProvider(n,new f(g))),c.references&&s.push(e.registerReferenceProvider(n,new o(g))),c.documentSymbols&&s.push(e.registerDocumentSymbolProvider(n,new u(g))),c.rename&&s.push(e.registerRenameProvider(n,new m(g))),c.colors&&s.push(e.registerColorProvider(n,new l(g))),c.foldingRanges&&s.push(e.registerFoldingRangeProvider(n,new v(g))),c.diagnostics&&s.push(new h(n,g,t.onDidChange)),c.selectionRanges&&s.push(e.registerSelectionRangeProvider(n,new d(g))),c.documentFormattingEdits&&s.push(e.registerDocumentFormattingEditProvider(n,new r(g))),c.documentRangeFormattingEdits&&s.push(e.registerDocumentRangeFormattingEditProvider(n,new i(g)))}return y(),n.push(C(s)),C(n)}function C(e){return{dispose:()=>w(e)}}function w(e){for(;e.length;)e.pop().dispose()}export{_ as CompletionAdapter,f as DefinitionAdapter,h as DiagnosticsAdapter,l as DocumentColorAdapter,r as DocumentFormattingEditProvider,p as DocumentHighlightAdapter,g as DocumentLinkAdapter,i as DocumentRangeFormattingEditProvider,u as DocumentSymbolAdapter,v as FoldingRangeAdapter,a as HoverAdapter,o as ReferenceAdapter,m as RenameAdapter,d as SelectionRangeAdapter,x as WorkerManager,c as fromPosition,s as fromRange,S as setupMode,n as toRange,y as toTextEdit};
|