@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.
- package/CHANGELOG.md +23 -0
- package/README.en.md +3 -3
- package/README.md +3 -3
- package/dist/agent/durable-session-supervisor.d.ts.map +1 -0
- package/dist/{session → agent}/durable-session-supervisor.js +3 -3
- package/dist/channels/types.d.ts +2 -12
- package/dist/channels/types.d.ts.map +1 -1
- package/dist/commands/runtime.d.ts +1 -62
- package/dist/commands/runtime.d.ts.map +1 -1
- package/dist/commands/runtime.js +2 -22
- package/dist/context-report.d.ts +1 -1
- package/dist/context-report.d.ts.map +1 -1
- package/dist/daemon.js +1 -1
- package/dist/doctor.d.ts +1 -1
- package/dist/doctor.d.ts.map +1 -1
- package/dist/eval/tasks.d.ts.map +1 -1
- package/dist/eval/tasks.js +8 -4
- package/dist/guard/approval.d.ts +1 -1
- package/dist/guard/approval.d.ts.map +1 -1
- package/dist/index.js +10 -0
- package/dist/local-storage-report.d.ts +1 -1
- package/dist/local-storage-report.d.ts.map +1 -1
- package/dist/pipeline/sections.d.ts +1 -1
- package/dist/pipeline/sections.d.ts.map +1 -1
- package/dist/privacy-report.d.ts +1 -1
- package/dist/privacy-report.d.ts.map +1 -1
- package/dist/recap.d.ts +1 -1
- package/dist/recap.d.ts.map +1 -1
- package/dist/repl.d.ts +3 -0
- package/dist/repl.d.ts.map +1 -1
- package/dist/repl.js +4 -1
- package/dist/session-export-index.d.ts +1 -1
- package/dist/session-export-index.d.ts.map +1 -1
- package/dist/session-export.d.ts +1 -1
- package/dist/session-export.d.ts.map +1 -1
- package/dist/skills/types.d.ts +1 -8
- package/dist/skills/types.d.ts.map +1 -1
- package/dist/skills/types.js +1 -1
- package/dist/slash-context.d.ts +63 -0
- package/dist/slash-context.d.ts.map +1 -0
- package/dist/slash-context.js +26 -0
- package/dist/slash-ports.d.ts +24 -0
- package/dist/slash-ports.d.ts.map +1 -0
- package/dist/slash-ports.js +31 -0
- package/dist/statusline.d.ts +1 -1
- package/dist/statusline.d.ts.map +1 -1
- package/dist/tui/streaming-repl.d.ts +4 -0
- package/dist/tui/streaming-repl.d.ts.map +1 -1
- package/dist/tui/streaming-repl.js +5 -2
- package/dist/tui/streaming-tui.d.ts +4 -0
- package/dist/tui/streaming-tui.d.ts.map +1 -1
- package/dist/tui/streaming-tui.js +34 -6
- package/dist/types.d.ts +20 -0
- package/dist/types.d.ts.map +1 -1
- package/docs/install.md +30 -27
- package/package.json +7 -2
- package/scripts/build-portable-win.mjs +193 -0
- package/scripts/dep-layers.mjs +50 -0
- package/scripts/sync-scoop-bucket.mjs +12 -0
- package/scripts/sync-winget-sha.mjs +56 -0
- package/scripts/validate-packaging.mjs +52 -14
- package/scripts/write-scoop-manifest.mjs +69 -0
- package/dist/session/durable-session-supervisor.d.ts.map +0 -1
- /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
|
+
}
|
package/scripts/dep-layers.mjs
CHANGED
|
@@ -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
|
|
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
|
-
|
|
22
|
-
|
|
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 (!
|
|
25
|
-
errors.push("scoop
|
|
29
|
+
if (!scoop.architecture?.["64bit"]) {
|
|
30
|
+
errors.push("scoop should nest url/hash under architecture.64bit (Extras style)");
|
|
26
31
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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 (
|
|
45
|
-
errors.push("winget
|
|
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:",
|
|
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"}
|
|
File without changes
|