@qlingzzy/qling 1.1.1 → 1.2.1

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.
Files changed (64) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.en.md +3 -3
  3. package/README.md +3 -3
  4. package/dist/agent/durable-session-supervisor.d.ts.map +1 -0
  5. package/dist/{session → agent}/durable-session-supervisor.js +3 -3
  6. package/dist/channels/types.d.ts +2 -12
  7. package/dist/channels/types.d.ts.map +1 -1
  8. package/dist/commands/runtime.d.ts +1 -62
  9. package/dist/commands/runtime.d.ts.map +1 -1
  10. package/dist/commands/runtime.js +2 -22
  11. package/dist/context-report.d.ts +1 -1
  12. package/dist/context-report.d.ts.map +1 -1
  13. package/dist/daemon.js +1 -1
  14. package/dist/doctor.d.ts +1 -1
  15. package/dist/doctor.d.ts.map +1 -1
  16. package/dist/eval/tasks.d.ts.map +1 -1
  17. package/dist/eval/tasks.js +8 -4
  18. package/dist/guard/approval.d.ts +1 -1
  19. package/dist/guard/approval.d.ts.map +1 -1
  20. package/dist/index.js +10 -0
  21. package/dist/local-storage-report.d.ts +1 -1
  22. package/dist/local-storage-report.d.ts.map +1 -1
  23. package/dist/pipeline/sections.d.ts +1 -1
  24. package/dist/pipeline/sections.d.ts.map +1 -1
  25. package/dist/privacy-report.d.ts +1 -1
  26. package/dist/privacy-report.d.ts.map +1 -1
  27. package/dist/recap.d.ts +1 -1
  28. package/dist/recap.d.ts.map +1 -1
  29. package/dist/repl.d.ts +3 -0
  30. package/dist/repl.d.ts.map +1 -1
  31. package/dist/repl.js +4 -1
  32. package/dist/session-export-index.d.ts +1 -1
  33. package/dist/session-export-index.d.ts.map +1 -1
  34. package/dist/session-export.d.ts +1 -1
  35. package/dist/session-export.d.ts.map +1 -1
  36. package/dist/skills/types.d.ts +1 -8
  37. package/dist/skills/types.d.ts.map +1 -1
  38. package/dist/skills/types.js +1 -1
  39. package/dist/slash-context.d.ts +63 -0
  40. package/dist/slash-context.d.ts.map +1 -0
  41. package/dist/slash-context.js +26 -0
  42. package/dist/slash-ports.d.ts +24 -0
  43. package/dist/slash-ports.d.ts.map +1 -0
  44. package/dist/slash-ports.js +31 -0
  45. package/dist/statusline.d.ts +1 -1
  46. package/dist/statusline.d.ts.map +1 -1
  47. package/dist/tui/streaming-repl.d.ts +4 -0
  48. package/dist/tui/streaming-repl.d.ts.map +1 -1
  49. package/dist/tui/streaming-repl.js +5 -2
  50. package/dist/tui/streaming-tui.d.ts +4 -0
  51. package/dist/tui/streaming-tui.d.ts.map +1 -1
  52. package/dist/tui/streaming-tui.js +34 -6
  53. package/dist/types.d.ts +20 -0
  54. package/dist/types.d.ts.map +1 -1
  55. package/docs/install.md +30 -27
  56. package/package.json +7 -2
  57. package/scripts/build-portable-win.mjs +193 -0
  58. package/scripts/dep-layers.mjs +50 -0
  59. package/scripts/sync-scoop-bucket.mjs +12 -0
  60. package/scripts/sync-winget-sha.mjs +56 -0
  61. package/scripts/validate-packaging.mjs +52 -14
  62. package/scripts/write-scoop-manifest.mjs +69 -0
  63. package/dist/session/durable-session-supervisor.d.ts.map +0 -1
  64. /package/dist/{session → agent}/durable-session-supervisor.d.ts +0 -0
@@ -0,0 +1,193 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Build a Windows portable zip that embeds a Node.js runtime (no system Node required).
4
+ * Usage: node scripts/build-portable-win.mjs [--skip-node]
5
+ *
6
+ * Output: dist-portable/qling-win-x64.zip
7
+ * Layout:
8
+ * qling-win-x64/
9
+ * runtime/node.exe # bundled Node (same major as build host by default)
10
+ * package/ # npm pack contents
11
+ * qling.cmd # launcher → runtime\node.exe package\dist\index.js
12
+ * README.txt
13
+ */
14
+ import { spawnSync, execFileSync } from "node:child_process";
15
+ import { createHash } from "node:crypto";
16
+ import {
17
+ mkdir,
18
+ rm,
19
+ readFile,
20
+ writeFile,
21
+ copyFile,
22
+ access,
23
+ } from "node:fs/promises";
24
+ import { createWriteStream, existsSync } from "node:fs";
25
+ import { join, dirname, resolve } from "node:path";
26
+ import { fileURLToPath } from "node:url";
27
+ import { pipeline } from "node:stream/promises";
28
+ import { get } from "node:https";
29
+
30
+ const __dirname = dirname(fileURLToPath(import.meta.url));
31
+ const ROOT = resolve(__dirname, "..");
32
+ const OUT_DIR = join(ROOT, "dist-portable");
33
+ const STAGE = join(OUT_DIR, "qling-win-x64");
34
+ const ZIP_PATH = join(OUT_DIR, "qling-win-x64.zip");
35
+ const skipNode = process.argv.includes("--skip-node");
36
+
37
+ const pkg = JSON.parse(await readFile(join(ROOT, "package.json"), "utf8"));
38
+ const version = pkg.version;
39
+ const nodeVersion = process.version; // e.g. v22.22.1
40
+ const nodeDistName = `node-${nodeVersion}-win-x64`;
41
+ const nodeZipUrl = `https://nodejs.org/dist/${nodeVersion}/${nodeDistName}.zip`;
42
+
43
+ console.log(`[portable] building qling ${version} for win-x64…`);
44
+ console.log(`[portable] host node ${nodeVersion} ${process.arch}`);
45
+
46
+ await rm(OUT_DIR, { recursive: true, force: true });
47
+ await mkdir(STAGE, { recursive: true });
48
+
49
+ // 1) npm pack current tree
50
+ const pack = spawnSync(
51
+ process.platform === "win32" ? "npm.cmd" : "npm",
52
+ ["pack", "--pack-destination", OUT_DIR],
53
+ { cwd: ROOT, encoding: "utf8", shell: true }
54
+ );
55
+ if (pack.status !== 0) {
56
+ console.error(pack.stdout, pack.stderr);
57
+ process.exit(1);
58
+ }
59
+ const tgzName = (pack.stdout || "")
60
+ .trim()
61
+ .split(/\r?\n/)
62
+ .filter(Boolean)
63
+ .pop();
64
+ if (!tgzName) {
65
+ console.error("npm pack produced no tarball name");
66
+ process.exit(1);
67
+ }
68
+ const tgzPath = join(OUT_DIR, tgzName);
69
+ console.log(`[portable] packed ${tgzName}`);
70
+
71
+ try {
72
+ execFileSync("tar", ["-xzf", tgzPath, "-C", STAGE], { stdio: "inherit" });
73
+ } catch (err) {
74
+ console.error("tar extract failed:", err instanceof Error ? err.message : err);
75
+ process.exit(1);
76
+ }
77
+
78
+ // 2) Bundle Node runtime
79
+ const runtimeDir = join(STAGE, "runtime");
80
+ await mkdir(runtimeDir, { recursive: true });
81
+ let bundledNode = false;
82
+ let nodeNote = "System Node.js >= 18 on PATH";
83
+
84
+ if (!skipNode && process.platform === "win32") {
85
+ const cacheDir = join(OUT_DIR, ".node-cache");
86
+ await mkdir(cacheDir, { recursive: true });
87
+ const nodeZipPath = join(cacheDir, `${nodeDistName}.zip`);
88
+ if (!existsSync(nodeZipPath)) {
89
+ console.log(`[portable] downloading ${nodeZipUrl}`);
90
+ await downloadFile(nodeZipUrl, nodeZipPath);
91
+ } else {
92
+ console.log(`[portable] using cached ${nodeZipPath}`);
93
+ }
94
+ const extractDir = join(cacheDir, nodeDistName);
95
+ await rm(extractDir, { recursive: true, force: true });
96
+ execFileSync("tar", ["-xf", nodeZipPath, "-C", cacheDir], { stdio: "inherit" });
97
+ const nodeExeSrc = join(extractDir, "node.exe");
98
+ await access(nodeExeSrc);
99
+ await copyFile(nodeExeSrc, join(runtimeDir, "node.exe"));
100
+ // LICENSE if present
101
+ const license = join(extractDir, "LICENSE");
102
+ if (existsSync(license)) {
103
+ await copyFile(license, join(runtimeDir, "NODE-LICENSE"));
104
+ }
105
+ bundledNode = true;
106
+ nodeNote = `Bundled Node ${nodeVersion} (runtime\\node.exe)`;
107
+ console.log(`[portable] bundled node.exe from ${nodeDistName}`);
108
+ } else if (!skipNode) {
109
+ console.warn("[portable] non-Windows host: skip embedding node.exe (use --skip-node to silence)");
110
+ }
111
+
112
+ const launcher = `@echo off
113
+ setlocal
114
+ set "ROOT=%~dp0"
115
+ set "NODE_EXE="
116
+ if exist "%ROOT%runtime\\node.exe" set "NODE_EXE=%ROOT%runtime\\node.exe"
117
+ if not defined NODE_EXE (
118
+ where node >nul 2>nul && set "NODE_EXE=node"
119
+ )
120
+ if not defined NODE_EXE (
121
+ echo [qling] No bundled runtime and no system Node on PATH.
122
+ echo [qling] Re-download the portable zip or install Node.js ^>= 18.
123
+ exit /b 1
124
+ )
125
+ "%NODE_EXE%" "%ROOT%package\\dist\\index.js" %*
126
+ `;
127
+ await writeFile(join(STAGE, "qling.cmd"), launcher, "utf8");
128
+
129
+ const readme = `Qling ${version} — Windows portable layout
130
+ ========================================
131
+
132
+ ${bundledNode ? "This build embeds a Node.js runtime — no system Node install required." : "This build expects system Node.js >= 18 on PATH."}
133
+
134
+ Runtime:
135
+ ${nodeNote}
136
+
137
+ Run:
138
+ .\\qling.cmd --version
139
+ .\\qling.cmd doctor
140
+ .\\qling.cmd setup
141
+
142
+ Notes:
143
+ - better-sqlite3 native binary is compiled for the Node ABI used at pack time.
144
+ - Prefer matching the bundled Node major version if you replace runtime\\node.exe.
145
+
146
+ npm alternative:
147
+ npm install -g @qlingzzy/qling@${version} --registry https://registry.npmjs.org/
148
+
149
+ Source: https://github.com/Zzy-min/qling
150
+ `;
151
+ await writeFile(join(STAGE, "README.txt"), readme, "utf8");
152
+
153
+ await rm(ZIP_PATH, { force: true });
154
+ execFileSync("tar", ["-a", "-cf", ZIP_PATH, "-C", OUT_DIR, "qling-win-x64"], {
155
+ stdio: "inherit",
156
+ });
157
+
158
+ const buf = await readFile(ZIP_PATH);
159
+ const sha256 = createHash("sha256").update(buf).digest("hex");
160
+ const meta = {
161
+ version,
162
+ zip: "qling-win-x64.zip",
163
+ path: ZIP_PATH,
164
+ size: buf.length,
165
+ sha256,
166
+ bundledNode,
167
+ nodeVersion: bundledNode ? nodeVersion : null,
168
+ createdAt: new Date().toISOString(),
169
+ };
170
+ await writeFile(join(OUT_DIR, "portable-meta.json"), JSON.stringify(meta, null, 2) + "\n");
171
+
172
+ console.log(`[portable] wrote ${ZIP_PATH}`);
173
+ console.log(`[portable] size=${buf.length} sha256=${sha256}`);
174
+ console.log(`[portable] bundledNode=${bundledNode}`);
175
+ console.log(`[portable] meta: dist-portable/portable-meta.json`);
176
+
177
+ function downloadFile(url, dest) {
178
+ return new Promise((resolvePromise, reject) => {
179
+ const file = createWriteStream(dest);
180
+ get(url, (res) => {
181
+ if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
182
+ file.close();
183
+ downloadFile(res.headers.location, dest).then(resolvePromise, reject);
184
+ return;
185
+ }
186
+ if (res.statusCode !== 200) {
187
+ reject(new Error(`download failed: HTTP ${res.statusCode}`));
188
+ return;
189
+ }
190
+ pipeline(res, file).then(resolvePromise, reject);
191
+ }).on("error", reject);
192
+ });
193
+ }
@@ -68,6 +68,9 @@ export function layerOf(rel) {
68
68
  p === "agent-loop.ts" ||
69
69
  p.startsWith("agent/") ||
70
70
  p.startsWith("tools/") ||
71
+ p.startsWith("execution/") ||
72
+ p === "slash-context.ts" ||
73
+ p === "slash-ports.ts" ||
71
74
  p === "repl.ts"
72
75
  ) {
73
76
  return "agent-runtime";
@@ -76,6 +79,7 @@ export function layerOf(rel) {
76
79
  p === "sdk.ts" ||
77
80
  p === "daemon.ts" ||
78
81
  p === "dashboard-server.ts" ||
82
+ p.startsWith("dashboard/") ||
79
83
  p.endsWith("-report.ts") ||
80
84
  p === "doctor.ts" ||
81
85
  p === "statusline.ts" ||
@@ -228,7 +232,14 @@ function formatMermaid(layerCounts, edges) {
228
232
 
229
233
  const asJson = process.argv.includes("--json");
230
234
  const strict = process.argv.includes("--strict");
235
+ const baselineMode = process.argv.includes("--baseline");
236
+ const writeBaseline = process.argv.includes("--write-baseline");
231
237
  const writeDoc = process.argv.includes("--write-doc");
238
+ const BASELINE_PATH = join(ROOT, "docs", "dependency-layers.baseline.json");
239
+
240
+ function edgeKey(f) {
241
+ return `${f.fromLayer}->${f.toLayer}::${f.from}::${f.to}`;
242
+ }
232
243
 
233
244
  const result = await scanLayers();
234
245
 
@@ -270,6 +281,45 @@ if (writeDoc) {
270
281
  console.error(`[dep-layers] wrote ${snap}`);
271
282
  }
272
283
 
284
+ if (writeBaseline) {
285
+ await mkdir(dirname(BASELINE_PATH), { recursive: true });
286
+ const keys = result.forbidden.map(edgeKey).sort();
287
+ await writeFile(
288
+ BASELINE_PATH,
289
+ JSON.stringify(
290
+ {
291
+ generatedAt: new Date().toISOString(),
292
+ note: "Known reverse-layer edges. --baseline fails only on NEW keys not listed here.",
293
+ forbiddenCount: keys.length,
294
+ forbidden: keys,
295
+ },
296
+ null,
297
+ 2
298
+ ) + "\n",
299
+ "utf8"
300
+ );
301
+ console.error(`[dep-layers] wrote baseline ${BASELINE_PATH} (${keys.length} edges)`);
302
+ }
303
+
304
+ if (baselineMode) {
305
+ let known = new Set();
306
+ if (existsSync(BASELINE_PATH)) {
307
+ const raw = JSON.parse(await readFile(BASELINE_PATH, "utf8"));
308
+ known = new Set(raw.forbidden || []);
309
+ }
310
+ const novel = result.forbidden.filter((f) => !known.has(edgeKey(f)));
311
+ console.log(
312
+ `baseline gate: known=${known.size} current=${result.forbiddenCount} novel=${novel.length}`
313
+ );
314
+ if (novel.length > 0) {
315
+ console.error("NEW forbidden edges (not in baseline):");
316
+ for (const f of novel) {
317
+ console.error(` ${f.fromLayer} -> ${f.toLayer}: ${f.from} imports ${f.to}`);
318
+ }
319
+ process.exit(1);
320
+ }
321
+ }
322
+
273
323
  if (strict && result.forbiddenCount > 0) {
274
324
  process.exit(1);
275
325
  }
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+ /** Copy packaging/scoop/qling.json → packaging/scoop-bucket/qling.json */
3
+ import { copyFile, readFile } from "node:fs/promises";
4
+ import { join, dirname } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+
7
+ const root = join(dirname(fileURLToPath(import.meta.url)), "..");
8
+ const src = join(root, "packaging", "scoop", "qling.json");
9
+ const dest = join(root, "packaging", "scoop-bucket", "qling.json");
10
+ await copyFile(src, dest);
11
+ const j = JSON.parse(await readFile(dest, "utf8"));
12
+ console.log(`sync-scoop-bucket OK → v${j.version} hash=${j.hash?.slice(0, 20)}…`);
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Fill winget InstallerSha256 from dist-portable/portable-meta.json
4
+ */
5
+ import { readFile, writeFile, readdir } from "node:fs/promises";
6
+ import { join, dirname } from "node:path";
7
+ import { fileURLToPath } from "node:url";
8
+ import { existsSync } from "node:fs";
9
+
10
+ const root = join(dirname(fileURLToPath(import.meta.url)), "..");
11
+ const metaPath = join(root, "dist-portable", "portable-meta.json");
12
+ if (!existsSync(metaPath)) {
13
+ console.error("missing dist-portable/portable-meta.json — run npm run build:portable-win first");
14
+ process.exit(1);
15
+ }
16
+ const meta = JSON.parse(await readFile(metaPath, "utf8"));
17
+ const sha = String(meta.sha256 || "").toLowerCase();
18
+ if (!/^[a-f0-9]{64}$/.test(sha)) {
19
+ console.error("invalid sha256 in portable-meta.json");
20
+ process.exit(1);
21
+ }
22
+
23
+ const version = meta.version || JSON.parse(await readFile(join(root, "package.json"), "utf8")).version;
24
+ const targets = [
25
+ join(root, "packaging", "winget", "Zzy-min.qling.yaml"),
26
+ join(root, "packaging", "winget", "manifests", "Zzy-min", "qling", version, "Zzy-min.qling.installer.yaml"),
27
+ ];
28
+
29
+ for (const file of targets) {
30
+ if (!existsSync(file)) {
31
+ console.warn("skip missing", file);
32
+ continue;
33
+ }
34
+ let text = await readFile(file, "utf8");
35
+ text = text.replace(/InstallerSha256:\s*[A-Za-z0-9_]+/g, `InstallerSha256: ${sha}`);
36
+ text = text.replace(/PackageVersion:\s*[\d.]+/g, `PackageVersion: ${version}`);
37
+ text = text.replace(/v[\d.]+\//g, `v${version}/`);
38
+ await writeFile(file, text, "utf8");
39
+ console.log("updated", file);
40
+ }
41
+
42
+ // Scoop: rewrite Extras-compatible 64bit manifest
43
+ const { spawnSync } = await import("node:child_process");
44
+ const scoopScript = join(root, "scripts", "write-scoop-manifest.mjs");
45
+ const r = spawnSync(process.execPath, [scoopScript], { cwd: root, encoding: "utf8" });
46
+ process.stdout.write(r.stdout || "");
47
+ process.stderr.write(r.stderr || "");
48
+ if (r.status !== 0) process.exit(r.status || 1);
49
+
50
+ // Multi-file winget for this version dir (create if missing)
51
+ const multiDir = join(root, "packaging", "winget", "manifests", "Zzy-min", "qling", version);
52
+ if (!existsSync(multiDir)) {
53
+ console.warn("note: multi-file winget dir missing for", version, "- create under packaging/winget/manifests");
54
+ }
55
+
56
+ console.log(`sync-winget-sha OK version=${version} sha=${sha.slice(0, 16)}… bundledNode=${meta.bundledNode}`);
@@ -1,11 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * Validate Scoop / winget draft manifests align with package.json version.
3
+ * Validate Scoop / winget manifests align with package.json version.
4
4
  * Usage: node scripts/validate-packaging.mjs
5
5
  */
6
6
  import { readFile } from "node:fs/promises";
7
7
  import { join, dirname } from "node:path";
8
8
  import { fileURLToPath } from "node:url";
9
+ import { existsSync } from "node:fs";
9
10
 
10
11
  const root = join(dirname(fileURLToPath(import.meta.url)), "..");
11
12
  const errors = [];
@@ -18,17 +19,26 @@ const scoop = JSON.parse(scoopRaw);
18
19
  if (scoop.version !== version) {
19
20
  errors.push(`scoop version ${scoop.version} != package ${version}`);
20
21
  }
21
- if (!String(scoop.url || "").includes(version)) {
22
- errors.push(`scoop url does not pin ${version}: ${scoop.url}`);
22
+ const scoopUrl =
23
+ scoop.architecture?.["64bit"]?.url || scoop.url || "";
24
+ const scoopHash =
25
+ scoop.architecture?.["64bit"]?.hash || scoop.hash || "";
26
+ if (!String(scoopUrl).includes(version)) {
27
+ errors.push(`scoop url does not pin ${version}: ${scoopUrl}`);
23
28
  }
24
- if (!Array.isArray(scoop.notes) || !scoop.notes.some((n) => /DRAFT|draft/i.test(n))) {
25
- errors.push("scoop notes should mark DRAFT status");
29
+ if (!scoop.architecture?.["64bit"]) {
30
+ errors.push("scoop should nest url/hash under architecture.64bit (Extras style)");
26
31
  }
27
- const hash = String(scoop.hash || "");
28
- if (!hash || /TODO|REPLACE/i.test(hash)) {
29
- errors.push("scoop hash still placeholder — fill real SHA256 after npm publish");
30
- } else if (!/^(sha256:)?[a-f0-9]{64}$/i.test(hash)) {
31
- errors.push(`scoop hash looks invalid: ${hash}`);
32
+ if (!scoopHash || /TODO|REPLACE|PLACEHOLDER/i.test(String(scoopHash))) {
33
+ errors.push("scoop hash still placeholder — run build:portable-win + sync-winget-sha");
34
+ } else if (!/^(sha256:)?[a-f0-9]{64}$/i.test(String(scoopHash))) {
35
+ errors.push(`scoop hash looks invalid: ${scoopHash}`);
36
+ }
37
+ if (/sha256:/i.test(String(scoopHash))) {
38
+ errors.push("scoop hash should be plain hex (no sha256: prefix) for Extras");
39
+ }
40
+ if (!String(scoop.bin || "").includes("qling")) {
41
+ errors.push("scoop bin should expose qling");
32
42
  }
33
43
 
34
44
  const winget = await readFile(
@@ -41,8 +51,36 @@ if (!winget.includes(`PackageVersion: ${version}`)) {
41
51
  if (!winget.includes("PackageIdentifier: Zzy-min.qling")) {
42
52
  errors.push("winget PackageIdentifier missing");
43
53
  }
44
- if (!/DRAFT|not submitted/i.test(winget)) {
45
- errors.push("winget should remain marked as DRAFT");
54
+ if (/InstallerSha256:\s*(0{64}|PLACEHOLDER)/i.test(winget)) {
55
+ errors.push("winget InstallerSha256 is still placeholder");
56
+ }
57
+
58
+ const multiInstaller = join(
59
+ root,
60
+ "packaging",
61
+ "winget",
62
+ "manifests",
63
+ "Zzy-min",
64
+ "qling",
65
+ version,
66
+ "Zzy-min.qling.installer.yaml"
67
+ );
68
+ if (existsSync(multiInstaller)) {
69
+ const mi = await readFile(multiInstaller, "utf8");
70
+ if (/InstallerSha256:\s*(0{64}|PLACEHOLDER)/i.test(mi)) {
71
+ errors.push("multi-file winget installer sha still placeholder");
72
+ }
73
+ if (!mi.includes(`PackageVersion: ${version}`)) {
74
+ errors.push("multi-file winget installer version mismatch");
75
+ }
76
+ }
77
+
78
+ const bucket = join(root, "packaging", "scoop-bucket", "qling.json");
79
+ if (existsSync(bucket)) {
80
+ const b = JSON.parse(await readFile(bucket, "utf8"));
81
+ if (b.version !== version) errors.push("scoop-bucket version mismatch");
82
+ const bHash = b.architecture?.["64bit"]?.hash || b.hash;
83
+ if (bHash !== scoopHash) errors.push("scoop-bucket hash out of sync with scoop/qling.json");
46
84
  }
47
85
 
48
86
  if (errors.length) {
@@ -52,6 +90,6 @@ if (errors.length) {
52
90
  }
53
91
 
54
92
  console.log(`validate-packaging OK (version ${version})`);
55
- console.log(" scoop:", scoop.url);
93
+ console.log(" scoop:", scoopUrl);
94
+ console.log(" scoop.hash:", String(scoopHash).slice(0, 20) + "…");
56
95
  console.log(" winget: Zzy-min.qling @", version);
57
- console.log(" note: still drafts — not published to official catalogs");
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Write Scoop-compatible manifest (Extras style):
4
+ * - 4-space indent
5
+ * - plain SHA256 (no sha256: prefix)
6
+ * - architecture.64bit for url/hash/extract_dir
7
+ * - autoupdate.architecture.64bit
8
+ *
9
+ * Reads dist-portable/portable-meta.json when present.
10
+ */
11
+ import { readFile, writeFile } from "node:fs/promises";
12
+ import { join, dirname } from "node:path";
13
+ import { fileURLToPath } from "node:url";
14
+ import { existsSync } from "node:fs";
15
+
16
+ const root = join(dirname(fileURLToPath(import.meta.url)), "..");
17
+ const pkg = JSON.parse(await readFile(join(root, "package.json"), "utf8"));
18
+ const version = pkg.version;
19
+
20
+ let hash = "PLACEHOLDER";
21
+ const metaPath = join(root, "dist-portable", "portable-meta.json");
22
+ if (existsSync(metaPath)) {
23
+ const meta = JSON.parse(await readFile(metaPath, "utf8"));
24
+ hash = String(meta.sha256 || "").toLowerCase();
25
+ }
26
+
27
+ const manifest = {
28
+ version,
29
+ description: "Local-first Chinese AI Agent CLI workbench (Qling / 轻灵).",
30
+ homepage: "https://github.com/Zzy-min/qling",
31
+ license: "MIT",
32
+ notes: [
33
+ "Portable zip embeds a Node.js runtime (no system Node required).",
34
+ "Set API keys as user environment variables (never commit secrets).",
35
+ "After install: run qling doctor, then qling setup.",
36
+ ],
37
+ architecture: {
38
+ "64bit": {
39
+ url: `https://github.com/Zzy-min/qling/releases/download/v${version}/qling-win-x64.zip`,
40
+ hash,
41
+ extract_dir: "qling-win-x64",
42
+ },
43
+ },
44
+ bin: "qling.cmd",
45
+ post_install: [
46
+ "Write-Host 'Run qling doctor, then qling setup.' -ForegroundColor Cyan",
47
+ ],
48
+ checkver: {
49
+ github: "https://github.com/Zzy-min/qling",
50
+ },
51
+ autoupdate: {
52
+ architecture: {
53
+ "64bit": {
54
+ url: "https://github.com/Zzy-min/qling/releases/download/v$version/qling-win-x64.zip",
55
+ },
56
+ },
57
+ },
58
+ };
59
+
60
+ const json = JSON.stringify(manifest, null, 4) + "\n";
61
+ const targets = [
62
+ join(root, "packaging", "scoop", "qling.json"),
63
+ join(root, "packaging", "scoop-bucket", "qling.json"),
64
+ ];
65
+ for (const t of targets) {
66
+ await writeFile(t, json, "utf8");
67
+ console.log("wrote", t);
68
+ }
69
+ console.log(`version=${version} hash=${hash.slice(0, 16)}…`);
@@ -1 +0,0 @@
1
- {"version":3,"file":"durable-session-supervisor.d.ts","sourceRoot":"","sources":["../../src/session/durable-session-supervisor.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC;AAED,qBAAa,wBAAwB;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAA4B;IAChD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAqB;IACrD,OAAO,CAAC,KAAK,CAA+C;IAC5D,OAAO,CAAC,OAAO,CAAS;gBAEZ,OAAO,EAAE,+BAA+B;IAMpD,KAAK,IAAI,IAAI;IAQP,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAOb,IAAI;YAyBJ,iBAAiB;YAcjB,iBAAiB;YAiBjB,sBAAsB;YAiCtB,cAAc;YAoCd,iBAAiB;YAyBjB,WAAW;CA8B1B"}