@qlingzzy/qling 1.2.1 → 1.2.2
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 +8 -0
- package/README.en.md +3 -3
- package/README.md +3 -3
- package/dist/execution/failure-classifier.d.ts.map +1 -1
- package/dist/execution/failure-classifier.js +4 -1
- package/dist/session/session-scheduler.d.ts.map +1 -1
- package/dist/session/session-scheduler.js +9 -1
- package/dist/skills/registry.js +1 -1
- package/docs/install.md +1 -1
- package/package.json +1 -1
- package/scripts/build-portable-win.mjs +93 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## v1.2.2 (2026-07-15)
|
|
6
|
+
|
|
7
|
+
- Fix CRLF skill frontmatter parsing on Windows CI.
|
|
8
|
+
- Normalize Windows and POSIX target paths consistently in recovery fingerprints.
|
|
9
|
+
- Persist session task state atomically to prevent partial JSON reads.
|
|
10
|
+
- Install locked production dependencies in Windows portable builds and verify the staged launcher with `--version` and `doctor` before packaging.
|
|
11
|
+
- Provision Playwright Chromium for the required Linux dashboard smoke test.
|
|
12
|
+
|
|
5
13
|
## v1.2.1 (2026-07-15)
|
|
6
14
|
|
|
7
15
|
### Official distribution + strict layers + self-contained portable
|
package/README.en.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](#requirements)
|
|
4
4
|
[](LICENSE)
|
|
5
|
-
[](CHANGELOG.md)
|
|
6
6
|
[](https://github.com/Zzy-min/qling/releases)
|
|
7
7
|
|
|
8
8
|
**Qling** is a **local-first** AI Agent CLI workbench. It keeps sessions, tools, skills, permissions, diagnostics, long-running missions, and recovery controls in one auditable terminal console.
|
|
@@ -178,14 +178,14 @@ QLING_EVAL_LLM=1 npm run eval:llm
|
|
|
178
178
|
| Channel | Status |
|
|
179
179
|
|---------|--------|
|
|
180
180
|
| Source + bootstrap | Ready |
|
|
181
|
-
| npm `@qlingzzy/qling` | **Published** `1.2.
|
|
181
|
+
| npm `@qlingzzy/qling` | **Published** `1.2.2` — install with `--registry https://registry.npmjs.org/` if your default is a mirror |
|
|
182
182
|
| Scoop | Draft + real tarball SHA256; not in official buckets |
|
|
183
183
|
| winget | Draft YAML only (needs portable zip + SHA256) |
|
|
184
184
|
| Portable zip / single binary | Not yet |
|
|
185
185
|
|
|
186
186
|
## Version
|
|
187
187
|
|
|
188
|
-
- Package: `1.2.
|
|
188
|
+
- Package: `1.2.2` (see latest tag on GitHub for hotfixes)
|
|
189
189
|
- Changelog: [CHANGELOG.md](CHANGELOG.md)
|
|
190
190
|
- Specs / plans: `docs/superpowers/`
|
|
191
191
|
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](#环境要求)
|
|
4
4
|
[](LICENSE)
|
|
5
|
-
[](CHANGELOG.md)
|
|
6
6
|
|
|
7
7
|
[English](README.en.md) · [安装指南](docs/install.md) · [CHANGELOG](CHANGELOG.md)
|
|
8
8
|
|
|
@@ -63,7 +63,7 @@ qling
|
|
|
63
63
|
# 或从 GitHub 直接全局安装
|
|
64
64
|
npm install -g github:Zzy-min/qling
|
|
65
65
|
|
|
66
|
-
# npm 官方包(已发布 1.2.
|
|
66
|
+
# npm 官方包(已发布 1.2.2;作用域名,CLI 仍是 qling)
|
|
67
67
|
# 若默认 registry 是国内镜像,请显式指定官方源
|
|
68
68
|
npm install -g @qlingzzy/qling --registry https://registry.npmjs.org/
|
|
69
69
|
```
|
|
@@ -402,7 +402,7 @@ npm audit --registry=https://registry.npmjs.org --audit-level=high
|
|
|
402
402
|
|
|
403
403
|
## 版本与变更
|
|
404
404
|
|
|
405
|
-
- 当前版本:`1.2.
|
|
405
|
+
- 当前版本:`1.2.2`
|
|
406
406
|
|
|
407
407
|
- 完整变更历史:见 [CHANGELOG.md](CHANGELOG.md)
|
|
408
408
|
- 安装与分发:见 [docs/install.md](docs/install.md)、[packaging/](packaging/)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"failure-classifier.d.ts","sourceRoot":"","sources":["../../src/execution/failure-classifier.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAmB,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAEzE,UAAU,cAAc;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;
|
|
1
|
+
{"version":3,"file":"failure-classifier.d.ts","sourceRoot":"","sources":["../../src/execution/failure-classifier.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAmB,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAEzE,UAAU,cAAc;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAqBD,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,GAAE,cAAmB,GAAG,qBAAqB,CAgCnG;AAED,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,IAAI,CAAC,qBAAqB,EAAE,aAAa,CAAC,GAAG,MAAM,CAUpG"}
|
|
@@ -13,6 +13,9 @@ function normalizeMessage(value) {
|
|
|
13
13
|
.replace(/\s+/g, " ")
|
|
14
14
|
.trim();
|
|
15
15
|
}
|
|
16
|
+
function portableBasename(value) {
|
|
17
|
+
return value.includes("\\") ? path.win32.basename(value) : path.posix.basename(value);
|
|
18
|
+
}
|
|
16
19
|
export function classifyFailure(error, context = {}) {
|
|
17
20
|
const message = errorMessage(error);
|
|
18
21
|
const lower = message.toLowerCase();
|
|
@@ -54,7 +57,7 @@ export function classifyFailure(error, context = {}) {
|
|
|
54
57
|
return { ...classification, fingerprint: createFailureFingerprint(classification) };
|
|
55
58
|
}
|
|
56
59
|
export function createFailureFingerprint(failure) {
|
|
57
|
-
const target = failure.targetPath ?
|
|
60
|
+
const target = failure.targetPath ? portableBasename(failure.targetPath).toLowerCase() : "";
|
|
58
61
|
const stable = [
|
|
59
62
|
failure.category,
|
|
60
63
|
failure.tool ?? "",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-scheduler.d.ts","sourceRoot":"","sources":["../../src/session/session-scheduler.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"session-scheduler.d.ts","sourceRoot":"","sources":["../../src/session/session-scheduler.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC;AACrC,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,SAAS,CAAC;AAClD,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,WAAW,GAAG,UAAU,CAAC;AACvG,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,QAAQ,CAAC;AAErD,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,eAAe,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,eAAe,CAAC;IACtB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,MAAM,EAAE,iBAAiB,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,SAAS,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IACnD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,iBAAiB,CAAC;CAC5B;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;CACnB;AAMD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAuC;IAC7D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IACrC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoB;IAC3C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAkC;IACxD,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,KAAK,CAA+C;gBAEhD,OAAO,EAAE,uBAAuB;IAStC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAK3B,KAAK,IAAI,IAAI;IAOP,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAO3B,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAIvB,cAAc,CAAC,OAAO,EAAE;QAC5B,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;QACnB,IAAI,EAAE,eAAe,CAAC;QACtB,MAAM,CAAC,EAAE,iBAAiB,CAAC;KAC5B,GAAG,OAAO,CAAC,WAAW,CAAC;IAqBlB,SAAS,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAOnC,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAU5C,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC;IAiBjC,eAAe,IAAI,OAAO,CAAC,YAAY,CAAC;IA4E9C,OAAO,CAAC,cAAc;YAQR,SAAS;YAaT,SAAS;CAYxB"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as fs from "fs/promises";
|
|
2
2
|
import { existsSync } from "fs";
|
|
3
3
|
import * as path from "path";
|
|
4
|
+
import { randomUUID } from "node:crypto";
|
|
4
5
|
function cloneTask(task) {
|
|
5
6
|
return { ...task };
|
|
6
7
|
}
|
|
@@ -190,7 +191,14 @@ export class SessionScheduler {
|
|
|
190
191
|
const payload = Array.from(this.tasks.values())
|
|
191
192
|
.sort((a, b) => a.createdAt - b.createdAt)
|
|
192
193
|
.map(cloneTask);
|
|
193
|
-
|
|
194
|
+
const tempFile = `${this.stateFile}.${process.pid}.${randomUUID()}.tmp`;
|
|
195
|
+
await fs.writeFile(tempFile, JSON.stringify(payload, null, 2), "utf-8");
|
|
196
|
+
try {
|
|
197
|
+
await fs.rename(tempFile, this.stateFile);
|
|
198
|
+
}
|
|
199
|
+
finally {
|
|
200
|
+
await fs.rm(tempFile, { force: true }).catch(() => undefined);
|
|
201
|
+
}
|
|
194
202
|
}
|
|
195
203
|
}
|
|
196
204
|
function sanitizeError(error) {
|
package/dist/skills/registry.js
CHANGED
|
@@ -92,7 +92,7 @@ export function parseFrontmatter(raw, filePath) {
|
|
|
92
92
|
triggers: [],
|
|
93
93
|
path: filePath,
|
|
94
94
|
};
|
|
95
|
-
const lines = raw.split("\n");
|
|
95
|
+
const lines = raw.replace(/\r\n?/g, "\n").split("\n");
|
|
96
96
|
const firstIdx = lines.findIndex((l) => l.trim() === "---");
|
|
97
97
|
if (firstIdx < 0)
|
|
98
98
|
return fallback;
|
package/docs/install.md
CHANGED
|
@@ -123,7 +123,7 @@ npm run build:portable-win
|
|
|
123
123
|
# → dist-portable/qling-win-x64.zip + portable-meta.json (sha256)
|
|
124
124
|
```
|
|
125
125
|
|
|
126
|
-
Release 资产示例:`https://github.com/Zzy-min/qling/releases/download/v1.2.
|
|
126
|
+
Release 资产示例:`https://github.com/Zzy-min/qling/releases/download/v1.2.2/qling-win-x64.zip`
|
|
127
127
|
清单:`packaging/winget/Zzy-min.qling.yaml`(含 InstallerSha256)。
|
|
128
128
|
|
|
129
129
|
### 本地校验
|
package/package.json
CHANGED
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
* qling-win-x64/
|
|
9
9
|
* runtime/node.exe # bundled Node (same major as build host by default)
|
|
10
10
|
* package/ # npm pack contents
|
|
11
|
-
* qling.
|
|
11
|
+
* qling.exe # WinGet portable entry (Framework csc launcher)
|
|
12
|
+
* qling.cmd # Scoop / shell-friendly launcher
|
|
12
13
|
* README.txt
|
|
13
14
|
*/
|
|
14
15
|
import { spawnSync, execFileSync } from "node:child_process";
|
|
@@ -75,6 +76,24 @@ try {
|
|
|
75
76
|
process.exit(1);
|
|
76
77
|
}
|
|
77
78
|
|
|
79
|
+
// npm pack intentionally excludes node_modules. Install only runtime dependencies
|
|
80
|
+
// into the staged package, then rebuild the native dependency with lifecycle
|
|
81
|
+
// scripts enabled only for that dependency.
|
|
82
|
+
const stagedPackage = join(STAGE, "package");
|
|
83
|
+
await copyFile(join(ROOT, "package-lock.json"), join(stagedPackage, "package-lock.json"));
|
|
84
|
+
runChecked(
|
|
85
|
+
process.platform === "win32" ? "npm.cmd" : "npm",
|
|
86
|
+
["ci", "--omit=dev", "--ignore-scripts", "--no-audit", "--no-fund"],
|
|
87
|
+
stagedPackage,
|
|
88
|
+
"install staged production dependencies"
|
|
89
|
+
);
|
|
90
|
+
runChecked(
|
|
91
|
+
process.platform === "win32" ? "npm.cmd" : "npm",
|
|
92
|
+
["rebuild", "better-sqlite3"],
|
|
93
|
+
stagedPackage,
|
|
94
|
+
"rebuild staged better-sqlite3"
|
|
95
|
+
);
|
|
96
|
+
|
|
78
97
|
// 2) Bundle Node runtime
|
|
79
98
|
const runtimeDir = join(STAGE, "runtime");
|
|
80
99
|
await mkdir(runtimeDir, { recursive: true });
|
|
@@ -126,6 +145,58 @@ if not defined NODE_EXE (
|
|
|
126
145
|
`;
|
|
127
146
|
await writeFile(join(STAGE, "qling.cmd"), launcher, "utf8");
|
|
128
147
|
|
|
148
|
+
// Native .exe launcher for WinGet portable (scripted .cmd/.bat not allowed)
|
|
149
|
+
const launcherCs = join(ROOT, "packaging", "win-launcher", "qling-launcher.cs");
|
|
150
|
+
const launcherExe = join(STAGE, "qling.exe");
|
|
151
|
+
if (!existsSync(launcherCs)) {
|
|
152
|
+
console.error(`[portable] missing launcher source: ${launcherCs}`);
|
|
153
|
+
process.exit(1);
|
|
154
|
+
}
|
|
155
|
+
const cscCandidates = [
|
|
156
|
+
process.env.QLING_CSC,
|
|
157
|
+
join(
|
|
158
|
+
process.env.WINDIR || "C:\\Windows",
|
|
159
|
+
"Microsoft.NET",
|
|
160
|
+
"Framework64",
|
|
161
|
+
"v4.0.30319",
|
|
162
|
+
"csc.exe"
|
|
163
|
+
),
|
|
164
|
+
join(
|
|
165
|
+
process.env.WINDIR || "C:\\Windows",
|
|
166
|
+
"Microsoft.NET",
|
|
167
|
+
"Framework",
|
|
168
|
+
"v4.0.30319",
|
|
169
|
+
"csc.exe"
|
|
170
|
+
),
|
|
171
|
+
].filter(Boolean);
|
|
172
|
+
const csc = cscCandidates.find((p) => existsSync(p));
|
|
173
|
+
if (!csc) {
|
|
174
|
+
console.error(
|
|
175
|
+
"[portable] csc.exe not found (need .NET Framework 4.x). Set QLING_CSC to override."
|
|
176
|
+
);
|
|
177
|
+
process.exit(1);
|
|
178
|
+
}
|
|
179
|
+
const cscResult = spawnSync(
|
|
180
|
+
csc,
|
|
181
|
+
[
|
|
182
|
+
"/nologo",
|
|
183
|
+
"/target:exe",
|
|
184
|
+
"/platform:anycpu",
|
|
185
|
+
"/optimize+",
|
|
186
|
+
`/out:${launcherExe}`,
|
|
187
|
+
launcherCs,
|
|
188
|
+
],
|
|
189
|
+
{ encoding: "utf8" }
|
|
190
|
+
);
|
|
191
|
+
if (cscResult.status !== 0 || !existsSync(launcherExe)) {
|
|
192
|
+
console.error("[portable] csc failed:", cscResult.stdout, cscResult.stderr);
|
|
193
|
+
process.exit(1);
|
|
194
|
+
}
|
|
195
|
+
console.log(`[portable] compiled qling.exe via ${csc}`);
|
|
196
|
+
|
|
197
|
+
runChecked(launcherExe, ["--version"], STAGE, "verify portable launcher version");
|
|
198
|
+
runChecked(launcherExe, ["doctor"], STAGE, "verify portable launcher doctor");
|
|
199
|
+
|
|
129
200
|
const readme = `Qling ${version} — Windows portable layout
|
|
130
201
|
========================================
|
|
131
202
|
|
|
@@ -135,11 +206,14 @@ Runtime:
|
|
|
135
206
|
${nodeNote}
|
|
136
207
|
|
|
137
208
|
Run:
|
|
209
|
+
.\\qling.exe --version
|
|
138
210
|
.\\qling.cmd --version
|
|
139
|
-
.\\qling.
|
|
140
|
-
.\\qling.
|
|
211
|
+
.\\qling.exe doctor
|
|
212
|
+
.\\qling.exe setup
|
|
141
213
|
|
|
142
214
|
Notes:
|
|
215
|
+
- qling.exe is the WinGet portable entry point (native launcher).
|
|
216
|
+
- qling.cmd remains for Scoop and interactive shells.
|
|
143
217
|
- better-sqlite3 native binary is compiled for the Node ABI used at pack time.
|
|
144
218
|
- Prefer matching the bundled Node major version if you replace runtime\\node.exe.
|
|
145
219
|
|
|
@@ -174,6 +248,22 @@ console.log(`[portable] size=${buf.length} sha256=${sha256}`);
|
|
|
174
248
|
console.log(`[portable] bundledNode=${bundledNode}`);
|
|
175
249
|
console.log(`[portable] meta: dist-portable/portable-meta.json`);
|
|
176
250
|
|
|
251
|
+
function runChecked(command, args, cwd, label) {
|
|
252
|
+
console.log(`[portable] ${label}…`);
|
|
253
|
+
const result = spawnSync(command, args, {
|
|
254
|
+
cwd,
|
|
255
|
+
encoding: "utf8",
|
|
256
|
+
shell: process.platform === "win32" && /\.cmd$/i.test(command),
|
|
257
|
+
});
|
|
258
|
+
if (result.stdout) process.stdout.write(result.stdout);
|
|
259
|
+
if (result.stderr) process.stderr.write(result.stderr);
|
|
260
|
+
if (result.status !== 0) {
|
|
261
|
+
if (result.error) console.error(`[portable] ${result.error.message}`);
|
|
262
|
+
console.error(`[portable] ${label} failed with exit code ${result.status}`);
|
|
263
|
+
process.exit(result.status ?? 1);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
177
267
|
function downloadFile(url, dest) {
|
|
178
268
|
return new Promise((resolvePromise, reject) => {
|
|
179
269
|
const file = createWriteStream(dest);
|