@mengyuly/dsh-ponytail 0.1.5 → 0.1.6

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 CHANGED
@@ -3,6 +3,34 @@
3
3
  All notable changes to `@mengyuly/dsh-ponytail` are documented here.
4
4
  Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
5
5
 
6
+ ## [0.1.6] - 2026-08-24
7
+
8
+ ### Fixed
9
+
10
+ - 移除发行产物中的动态代码执行:`new Function` 的调用点来自内联进 bundle 的
11
+ schemastery(其 schema DSL 会把字符串 `callback` 编译成函数)。已把
12
+ `@deepseek-ai/schemastery` 从 bundle **外置**为已发布的 peer(与 cordis
13
+ 同等对待),发行 `lib/index.js` 不再包含任何 `new Function` / `eval`
14
+ (CI 的 `check-bundle` 现在断言外链集合精确为
15
+ `cordis + schemastery`,且产物零动态执行)。该调用点在本插件运行时路径上
16
+ 本不可达(只构造、不解析),外置是消除扫描告警的根治,也是更诚实的依赖声明。
17
+
18
+ ### Changed
19
+
20
+ - 运行时依赖表述更新:bundle 现在 import `@deepseek-ai/cordis` +
21
+ `@deepseek-ai/schemastery`(两者都已发布);`dsh-llm` / `dsh-skill` 仍内联
22
+ (npm 无兼容版本)。
23
+ - `@deepseek-ai/schemastery` 加入 peerDependencies(宿主兼容声明)。
24
+ - `verify-dist` 的运行时导出检查改用链式可调用 stub 加载 bundle
25
+ (schemastery 在模块加载时被急切构建 schema,但本插件路径不解析它们)。
26
+ - 移除只做类型检查、职责与 `sync:dist` 重叠的 `scripts/build.sh`。
27
+
28
+ ### Tests
29
+
30
+ - Ubuntu + Windows(CI 矩阵):check-bundle(外链白名单 + 零动态执行)、
31
+ verify:dist、verify:pack、test:consumer、test:regressions。
32
+ - tarball 安装 smoke 现在显式安装 cordis + schemastery 两个运行时 peer。
33
+
6
34
  ## [0.1.5] - 2026-08-24
7
35
 
8
36
  ### Fixed
package/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # dsh-ponytail
2
2
 
3
3
  ![CI](https://github.com/MengYuil/dsh-ponytail/actions/workflows/ci.yml/badge.svg)
4
+ ![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)
5
+ [![npm](https://img.shields.io/npm/v/@mengyuly/dsh-ponytail)](https://www.npmjs.com/package/@mengyuly/dsh-ponytail)
4
6
 
5
7
  把 [DietrichGebert/ponytail](https://github.com/DietrichGebert/ponytail)(「懒惰资深开发者」最少代码心智)移植成 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 原生插件。功能与效率与上游一致:7 级阶梯规则集每轮注入、强度切换、`/ponytail-*` 斜杠命令。
6
8
 
@@ -24,9 +26,9 @@ dsh plugin --profile web add @mengyuly/dsh-ponytail
24
26
 
25
27
  装完重启 profile 生效(`dsh web` / `dsh tui`)。装载完成后,会话技能目录里会出现 6 个 `ponytail*` 技能,发 `/ponytail-help` 立即验证。
26
28
 
27
- > `lib/index.js` 是自包含 bundle(已内联 `dsh-llm` / `dsh-skill`),运行时只依赖 `@deepseek-ai/cordis` peer(registry 有 4.0.1),所以 GitHub / tgz / npm 三种安装方式都不需要 dsh 源码树。
29
+ > `lib/index.js` 是自包含 bundle(已内联 `dsh-llm` / `dsh-skill`——npm 无兼容版本),运行时依赖两个已发布的 peer:`@deepseek-ai/cordis`(4.0.1)与 `@deepseek-ai/schemastery`(3.18.x)。`schemastery` 刻意保持外置而非内联:其 schema DSL 用 `new Function` 编译 `callback` 字符串,外置后**发行产物不含任何动态代码执行**(CI 有专门检查)。GitHub / tgz / npm 三种安装方式都不需要 dsh 源码树。
28
30
 
29
- > 说明:`src/` 是源码、`lib/` 是预构建产物(开箱即可加载,无需编译)。源码主仓在 deepseek-harness 的 `packages/community/ponytail`,改源码后回主仓重建,再把 `lib/` 同步回本仓库即可发版。本地改 `src/` 想快速验证编译,可用 `DSH_CHECKOUT=/path/to/deepseek-harness ./scripts/build.sh`(只做类型检查)。
31
+ > 说明:`src/` 是源码、`lib/` 是预构建产物(开箱即可加载,无需编译)。源码主仓在 deepseek-harness 的 `packages/community/ponytail`;改源码后用 `DSH_CHECKOUT=/path/to/deepseek-harness npm run sync:dist` 重建并同步完整 `lib/`(见下「发行维护」)。
30
32
 
31
33
  ## 功能
32
34
 
package/lib/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { createRequire } from "node:module";
2
2
  import { Service } from "@deepseek-ai/cordis";
3
+ import z from "@deepseek-ai/schemastery";
3
4
  import { mkdirSync, readFileSync, renameSync, unlinkSync, unwatchFile, watchFile, writeFileSync } from "node:fs";
4
5
  import { homedir } from "node:os";
5
6
  import { dirname, join } from "node:path";
@@ -93,798 +94,6 @@ function createUserMessage(input) {
93
94
  });
94
95
  }
95
96
  //#endregion
96
- //#region ../../../vendor/cosmokit/src/misc.ts
97
- /** Return true when a value is `null` or `undefined`. */
98
- function isNullable(value) {
99
- return value === null || value === void 0;
100
- }
101
- /** Return true for non-array object values. */
102
- function isPlainObject(data) {
103
- return data && typeof data === "object" && !Array.isArray(data);
104
- }
105
- /** Filter object entries and return a new object. */
106
- function filterKeys(object, filter) {
107
- return Object.fromEntries(Object.entries(object).filter(([key, value]) => filter(key, value)));
108
- }
109
- /** Map object values while preserving the original key set. */
110
- function mapValues(object, transform) {
111
- return Object.fromEntries(Object.entries(object).map(([key, value]) => [key, transform(value, key)]));
112
- }
113
- /** Pick selected keys from an object, optionally including `undefined` values. */
114
- function pick(source, keys, forced) {
115
- if (!keys) return { ...source };
116
- const result = {};
117
- for (const key of keys) if (forced || source[key] !== void 0) result[key] = source[key];
118
- return result;
119
- }
120
- //#endregion
121
- //#region ../../../vendor/cosmokit/src/types.ts
122
- /** Test values using `instanceof` with a `toStringTag` fallback. */
123
- function is(type, value) {
124
- if (arguments.length === 1) return (value) => is(type, value);
125
- return type in globalThis && value instanceof globalThis[type] || Object.prototype.toString.call(value).slice(8, -1) === type;
126
- }
127
- function isArrayBufferLike(value) {
128
- return is("ArrayBuffer", value) || is("SharedArrayBuffer", value);
129
- }
130
- function isArrayBufferSource(value) {
131
- return isArrayBufferLike(value) || ArrayBuffer.isView(value);
132
- }
133
- let Binary;
134
- (function(_Binary) {
135
- _Binary.is = isArrayBufferLike;
136
- _Binary.isSource = isArrayBufferSource;
137
- function fromSource(source) {
138
- if (ArrayBuffer.isView(source)) return source.buffer.slice(source.byteOffset, source.byteOffset + source.byteLength);
139
- else return source;
140
- }
141
- _Binary.fromSource = fromSource;
142
- function toBase64(source) {
143
- source = fromSource(source);
144
- if (typeof Buffer !== "undefined") return Buffer.from(source).toString("base64");
145
- let binary = "";
146
- const bytes = new Uint8Array(source);
147
- for (let i = 0; i < bytes.byteLength; i++) binary += String.fromCharCode(bytes[i]);
148
- return btoa(binary);
149
- }
150
- _Binary.toBase64 = toBase64;
151
- function fromBase64(source) {
152
- if (typeof Buffer !== "undefined") return fromSource(Buffer.from(source, "base64"));
153
- return Uint8Array.from(atob(source), (c) => c.charCodeAt(0));
154
- }
155
- _Binary.fromBase64 = fromBase64;
156
- function toHex(source) {
157
- source = fromSource(source);
158
- if (typeof Buffer !== "undefined") return Buffer.from(source).toString("hex");
159
- return Array.from(new Uint8Array(source), (byte) => byte.toString(16).padStart(2, "0")).join("");
160
- }
161
- _Binary.toHex = toHex;
162
- function fromHex(source) {
163
- if (typeof Buffer !== "undefined") return fromSource(Buffer.from(source, "hex"));
164
- const hex = source.length % 2 === 0 ? source : source.slice(0, source.length - 1);
165
- const buffer = [];
166
- for (let i = 0; i < hex.length; i += 2) buffer.push(parseInt(`${hex[i]}${hex[i + 1]}`, 16));
167
- return Uint8Array.from(buffer).buffer;
168
- }
169
- _Binary.fromHex = fromHex;
170
- })(Binary || (Binary = {}));
171
- Binary.fromBase64;
172
- Binary.toBase64;
173
- Binary.fromHex;
174
- Binary.toHex;
175
- /** Deep-clone common JavaScript values while preserving prototypes and cycles. */
176
- function clone(source, refs = /* @__PURE__ */ new Map()) {
177
- if (!source || typeof source !== "object") return source;
178
- if (is("Date", source)) return new Date(source.valueOf());
179
- if (is("RegExp", source)) return new RegExp(source.source, source.flags);
180
- if (isArrayBufferLike(source)) return source.slice(0);
181
- if (ArrayBuffer.isView(source)) return source.buffer.slice(source.byteOffset, source.byteOffset + source.byteLength);
182
- const cached = refs.get(source);
183
- if (cached) return cached;
184
- if (Array.isArray(source)) {
185
- const result = [];
186
- refs.set(source, result);
187
- source.forEach((value, index) => {
188
- result[index] = Reflect.apply(clone, null, [value, refs]);
189
- });
190
- return result;
191
- }
192
- const result = Object.create(Object.getPrototypeOf(source));
193
- refs.set(source, result);
194
- for (const key of Reflect.ownKeys(source)) {
195
- const descriptor = { ...Reflect.getOwnPropertyDescriptor(source, key) };
196
- if ("value" in descriptor) descriptor.value = Reflect.apply(clone, null, [descriptor.value, refs]);
197
- Reflect.defineProperty(result, key, descriptor);
198
- }
199
- return result;
200
- }
201
- /** Deeply compare arrays, dates, regexps, buffers, and plain object fields. */
202
- function deepEqual(a, b, strict) {
203
- if (a === b) return true;
204
- if (!strict && isNullable(a) && isNullable(b)) return true;
205
- if (typeof a !== typeof b) return false;
206
- if (typeof a !== "object") return false;
207
- if (!a || !b) return false;
208
- function check(test, then) {
209
- return test(a) ? test(b) ? then(a, b) : false : test(b) ? false : void 0;
210
- }
211
- return check(Array.isArray, (a, b) => a.length === b.length && a.every((item, index) => deepEqual(item, b[index]))) ?? check(is("Date"), (a, b) => a.valueOf() === b.valueOf()) ?? check(is("RegExp"), (a, b) => a.source === b.source && a.flags === b.flags) ?? check(isArrayBufferLike, (a, b) => {
212
- if (a.byteLength !== b.byteLength) return false;
213
- const viewA = new Uint8Array(a);
214
- const viewB = new Uint8Array(b);
215
- for (let i = 0; i < viewA.length; i++) if (viewA[i] !== viewB[i]) return false;
216
- return true;
217
- }) ?? Object.keys({
218
- ...a,
219
- ...b
220
- }).every((key) => deepEqual(a[key], b[key], strict));
221
- }
222
- //#endregion
223
- //#region ../../../vendor/cosmokit/src/time.ts
224
- let Time;
225
- (function(_Time) {
226
- _Time.millisecond = 1;
227
- const second = _Time.second = 1e3;
228
- const minute = _Time.minute = second * 60;
229
- const hour = _Time.hour = minute * 60;
230
- const day = _Time.day = hour * 24;
231
- const week = _Time.week = day * 7;
232
- let timezoneOffset = (/* @__PURE__ */ new Date()).getTimezoneOffset();
233
- function setTimezoneOffset(offset) {
234
- timezoneOffset = offset;
235
- }
236
- _Time.setTimezoneOffset = setTimezoneOffset;
237
- function getTimezoneOffset() {
238
- return timezoneOffset;
239
- }
240
- _Time.getTimezoneOffset = getTimezoneOffset;
241
- function getDateNumber(date = /* @__PURE__ */ new Date(), offset) {
242
- if (typeof date === "number") date = new Date(date);
243
- if (offset === void 0) offset = timezoneOffset;
244
- return Math.floor((date.valueOf() / minute - offset) / 1440);
245
- }
246
- _Time.getDateNumber = getDateNumber;
247
- function fromDateNumber(value, offset) {
248
- const date = new Date(value * day);
249
- if (offset === void 0) offset = timezoneOffset;
250
- return new Date(+date + offset * minute);
251
- }
252
- _Time.fromDateNumber = fromDateNumber;
253
- const numeric = /\d+(?:\.\d+)?/.source;
254
- const timeRegExp = new RegExp(`^${[
255
- "w(?:eek(?:s)?)?",
256
- "d(?:ay(?:s)?)?",
257
- "h(?:our(?:s)?)?",
258
- "m(?:in(?:ute)?(?:s)?)?",
259
- "s(?:ec(?:ond)?(?:s)?)?"
260
- ].map((unit) => `(${numeric}${unit})?`).join("")}$`);
261
- function parseTime(source) {
262
- const capture = timeRegExp.exec(source);
263
- if (!capture) return 0;
264
- return (parseFloat(capture[1]) * week || 0) + (parseFloat(capture[2]) * day || 0) + (parseFloat(capture[3]) * hour || 0) + (parseFloat(capture[4]) * minute || 0) + (parseFloat(capture[5]) * second || 0);
265
- }
266
- _Time.parseTime = parseTime;
267
- function parseDate(date) {
268
- const parsed = parseTime(date);
269
- if (parsed) date = Date.now() + parsed;
270
- else if (/^\d{1,2}(:\d{1,2}){1,2}$/.test(date)) date = `${(/* @__PURE__ */ new Date()).toLocaleDateString()}-${date}`;
271
- else if (/^\d{1,2}-\d{1,2}-\d{1,2}(:\d{1,2}){1,2}$/.test(date)) date = `${(/* @__PURE__ */ new Date()).getFullYear()}-${date}`;
272
- return date ? new Date(date) : /* @__PURE__ */ new Date();
273
- }
274
- _Time.parseDate = parseDate;
275
- function format(ms) {
276
- const abs = Math.abs(ms);
277
- if (abs >= day - hour / 2) return Math.round(ms / day) + "d";
278
- else if (abs >= hour - minute / 2) return Math.round(ms / hour) + "h";
279
- else if (abs >= minute - second / 2) return Math.round(ms / minute) + "m";
280
- else if (abs >= second) return Math.round(ms / second) + "s";
281
- return ms + "ms";
282
- }
283
- _Time.format = format;
284
- function toDigits(source, length = 2) {
285
- return source.toString().padStart(length, "0");
286
- }
287
- _Time.toDigits = toDigits;
288
- function template(template, time = /* @__PURE__ */ new Date()) {
289
- return template.replace("yyyy", time.getFullYear().toString()).replace("yy", time.getFullYear().toString().slice(2)).replace("MM", toDigits(time.getMonth() + 1)).replace("dd", toDigits(time.getDate())).replace("hh", toDigits(time.getHours())).replace("mm", toDigits(time.getMinutes())).replace("ss", toDigits(time.getSeconds())).replace("SSS", toDigits(time.getMilliseconds(), 3));
290
- }
291
- _Time.template = template;
292
- })(Time || (Time = {}));
293
- //#endregion
294
- //#region ../../../vendor/schemastery/src/index.ts
295
- const kSchema = Symbol.for("schemastery");
296
- const kValidationError = Symbol.for("ValidationError");
297
- globalThis.__schemastery_index__ ??= 0;
298
- globalThis.__schemastery_refs__ = void 0;
299
- var ValidationError = class extends TypeError {
300
- options;
301
- name = "ValidationError";
302
- constructor(message, options) {
303
- let prefix = "$";
304
- for (const segment of options.path || []) if (typeof segment === "string") prefix += "." + segment;
305
- else if (typeof segment === "number") prefix += "[" + segment + "]";
306
- else if (typeof segment === "symbol") prefix += `[Symbol(${segment.toString()})]`;
307
- if (prefix.startsWith(".")) prefix = prefix.slice(1);
308
- super((prefix === "$" ? "" : `${prefix} `) + message);
309
- this.options = options;
310
- }
311
- static is(error) {
312
- return !!error?.[kValidationError];
313
- }
314
- };
315
- Object.defineProperty(ValidationError.prototype, kValidationError, { value: true });
316
- const Schema = function(options) {
317
- const schema = function(data, options = {}) {
318
- return Schema.resolve(data, schema, options)[0];
319
- };
320
- if (options.refs) {
321
- const refs = mapValues(options.refs, (options) => new Schema(options));
322
- const getRef = (uid) => refs[uid];
323
- for (const key in refs) {
324
- const options = refs[key];
325
- options.sKey = getRef(options.sKey);
326
- options.inner = getRef(options.inner);
327
- options.list = options.list && options.list.map(getRef);
328
- options.dict = options.dict && mapValues(options.dict, getRef);
329
- }
330
- return refs[options.uid];
331
- }
332
- Object.assign(schema, options);
333
- if (typeof schema.callback === "string") try {
334
- schema.callback = new Function("return " + schema.callback)();
335
- } catch {}
336
- Object.defineProperty(schema, "uid", { value: globalThis.__schemastery_index__++ });
337
- Object.setPrototypeOf(schema, Schema.prototype);
338
- schema.meta ||= {};
339
- schema.toString = schema.toString.bind(schema);
340
- return schema;
341
- };
342
- Schema.prototype = Object.create(Function.prototype);
343
- Schema.prototype[kSchema] = true;
344
- Object.defineProperty(Schema.prototype, "~standard", { get() {
345
- return {
346
- version: 1,
347
- vendor: "schemastery",
348
- validate: (value) => {
349
- try {
350
- return { value: Schema.resolve(value, this, {})[0] };
351
- } catch (error) {
352
- if (ValidationError.is(error)) return { issues: [{
353
- message: error.message,
354
- path: error.options.path
355
- }] };
356
- throw error;
357
- }
358
- }
359
- };
360
- } });
361
- Schema.ValidationError = ValidationError;
362
- Schema.prototype.toJSON = function toJSON() {
363
- if (globalThis.__schemastery_refs__) {
364
- globalThis.__schemastery_refs__[this.uid] ??= JSON.parse(JSON.stringify({ ...this }));
365
- return this.uid;
366
- }
367
- globalThis.__schemastery_refs__ = { [this.uid]: { ...this } };
368
- globalThis.__schemastery_refs__[this.uid] = JSON.parse(JSON.stringify({ ...this }));
369
- const result = {
370
- uid: this.uid,
371
- refs: globalThis.__schemastery_refs__
372
- };
373
- globalThis.__schemastery_refs__ = void 0;
374
- return result;
375
- };
376
- Schema.prototype.set = function set(key, value) {
377
- this.dict[key] = value;
378
- return this;
379
- };
380
- Schema.prototype.push = function push(value) {
381
- this.list.push(value);
382
- return this;
383
- };
384
- function mergeDesc(original, messages) {
385
- const result = typeof original === "string" ? { "": original } : { ...original };
386
- for (const locale in messages) {
387
- const value = messages[locale];
388
- if (value?.$description || value?.$desc) result[locale] = value.$description || value.$desc;
389
- else if (typeof value === "string") result[locale] = value;
390
- }
391
- return result;
392
- }
393
- function getInner(value) {
394
- return value?.$value ?? value?.$inner;
395
- }
396
- function extractKeys(data) {
397
- return filterKeys(data ?? {}, (key) => !key.startsWith("$"));
398
- }
399
- Schema.prototype.i18n = function i18n(messages) {
400
- const schema = Schema(this);
401
- const desc = mergeDesc(schema.meta.description, messages);
402
- if (Object.keys(desc).length) schema.meta.description = desc;
403
- if (schema.dict) schema.dict = mapValues(schema.dict, (inner, key) => {
404
- return inner.i18n(mapValues(messages, (data) => getInner(data)?.[key] ?? data?.[key]));
405
- });
406
- if (schema.list) schema.list = schema.list.map((inner, index) => {
407
- return inner.i18n(mapValues(messages, (data = {}) => {
408
- if (Array.isArray(getInner(data))) return getInner(data)[index];
409
- if (Array.isArray(data)) return data[index];
410
- return extractKeys(data);
411
- }));
412
- });
413
- if (schema.inner) schema.inner = schema.inner.i18n(mapValues(messages, (data) => {
414
- if (getInner(data)) return getInner(data);
415
- return extractKeys(data);
416
- }));
417
- if (schema.sKey) schema.sKey = schema.sKey.i18n(mapValues(messages, (data) => data?.$key));
418
- return schema;
419
- };
420
- Schema.prototype.extra = function extra(key, value) {
421
- const schema = Schema(this);
422
- schema.meta = {
423
- ...schema.meta,
424
- [key]: value
425
- };
426
- return schema;
427
- };
428
- for (const key of [
429
- "required",
430
- "disabled",
431
- "collapse",
432
- "hidden",
433
- "loose"
434
- ]) Object.assign(Schema.prototype, { [key](value = true) {
435
- const schema = Schema(this);
436
- schema.meta = {
437
- ...schema.meta,
438
- [key]: value
439
- };
440
- return schema;
441
- } });
442
- Schema.prototype.deprecated = function deprecated() {
443
- const schema = Schema(this);
444
- schema.meta.badges ||= [];
445
- schema.meta.badges.push({
446
- text: "deprecated",
447
- type: "danger"
448
- });
449
- return schema;
450
- };
451
- Schema.prototype.experimental = function experimental() {
452
- const schema = Schema(this);
453
- schema.meta.badges ||= [];
454
- schema.meta.badges.push({
455
- text: "experimental",
456
- type: "warning"
457
- });
458
- return schema;
459
- };
460
- Schema.prototype.pattern = function pattern(regexp) {
461
- const schema = Schema(this);
462
- const pattern = pick(regexp, ["source", "flags"]);
463
- schema.meta = {
464
- ...schema.meta,
465
- pattern
466
- };
467
- return schema;
468
- };
469
- Schema.prototype.simplify = function simplify(value) {
470
- if (deepEqual(value, this.meta.default, this.type === "dict")) return null;
471
- if (isNullable(value)) return value;
472
- if (this.type === "object" || this.type === "dict") {
473
- const result = {};
474
- for (const key in value) {
475
- const item = (this.type === "object" ? this.dict[key] : this.inner)?.simplify(value[key]);
476
- if (this.type === "dict" || !isNullable(item)) result[key] = item;
477
- }
478
- if (deepEqual(result, this.meta.default, this.type === "dict")) return null;
479
- return result;
480
- } else if (this.type === "array" || this.type === "tuple") {
481
- const result = [];
482
- value.forEach((value, index) => {
483
- const schema = this.type === "array" ? this.inner : this.list[index];
484
- const item = schema ? schema.simplify(value) : value;
485
- result.push(item);
486
- });
487
- return result;
488
- } else if (this.type === "intersect") {
489
- const result = {};
490
- for (const item of this.list) Object.assign(result, item.simplify(value));
491
- return result;
492
- } else if (this.type === "union") for (const schema of this.list) try {
493
- Schema.resolve(value, schema, {});
494
- return schema.simplify(value);
495
- } catch {}
496
- return value;
497
- };
498
- Schema.prototype.toString = function toString(inline) {
499
- return formatters[this.type]?.(this, inline) ?? `Schema<${this.type}>`;
500
- };
501
- Schema.prototype.role = function role(role, extra) {
502
- const schema = Schema(this);
503
- schema.meta = {
504
- ...schema.meta,
505
- role,
506
- extra
507
- };
508
- return schema;
509
- };
510
- for (const key of [
511
- "default",
512
- "link",
513
- "comment",
514
- "description",
515
- "max",
516
- "min",
517
- "step"
518
- ]) Object.assign(Schema.prototype, { [key](value) {
519
- const schema = Schema(this);
520
- schema.meta = {
521
- ...schema.meta,
522
- [key]: value
523
- };
524
- return schema;
525
- } });
526
- const resolvers = {};
527
- Schema.extend = function extend(type, resolve) {
528
- resolvers[type] = resolve;
529
- };
530
- Schema.resolve = function resolve(data, schema, options = {}, strict = false) {
531
- if (!schema) return [data];
532
- if (options.ignore?.(data, schema)) return [data];
533
- if (isNullable(data) && schema.type !== "lazy") {
534
- if (schema.meta.required) throw new ValidationError(`missing required value`, options);
535
- let current = schema;
536
- let fallback = schema.meta.default;
537
- while (current?.type === "intersect" && isNullable(fallback)) {
538
- current = current.list[0];
539
- fallback = current?.meta.default;
540
- }
541
- if (isNullable(fallback)) return [data];
542
- data = clone(fallback);
543
- }
544
- const callback = resolvers[schema.type];
545
- if (!callback) throw new ValidationError(`unsupported type "${schema.type}"`, options);
546
- try {
547
- return callback(data, schema, options, strict);
548
- } catch (error) {
549
- if (!schema.meta.loose) throw error;
550
- return [schema.meta.default];
551
- }
552
- };
553
- Schema.from = function from(source) {
554
- if (isNullable(source)) return Schema.any();
555
- else if ([
556
- "string",
557
- "number",
558
- "boolean"
559
- ].includes(typeof source)) return Schema.const(source).required();
560
- else if (source[kSchema]) return source;
561
- else if (typeof source === "function") switch (source) {
562
- case String: return Schema.string().required();
563
- case Number: return Schema.number().required();
564
- case Boolean: return Schema.boolean().required();
565
- case Function: return Schema.function().required();
566
- default: return Schema.is(source).required();
567
- }
568
- else throw new TypeError(`cannot infer schema from ${source}`);
569
- };
570
- Schema.lazy = function lazy(builder) {
571
- const toJSON = () => {
572
- if (!schema.inner[kSchema]) {
573
- schema.inner = schema.builder();
574
- schema.inner.meta = {
575
- ...schema.meta,
576
- ...schema.inner.meta
577
- };
578
- }
579
- return schema.inner.toJSON();
580
- };
581
- const schema = new Schema({
582
- type: "lazy",
583
- builder,
584
- inner: { toJSON }
585
- });
586
- return schema;
587
- };
588
- Schema.natural = function natural() {
589
- return Schema.number().step(1).min(0);
590
- };
591
- Schema.percent = function percent() {
592
- return Schema.number().step(.01).min(0).max(1).role("slider");
593
- };
594
- Schema.date = function date() {
595
- return Schema.union([Schema.is(Date), Schema.transform(Schema.string().role("datetime"), (value, options) => {
596
- const date = new Date(value);
597
- if (isNaN(+date)) throw new ValidationError(`invalid date "${value}"`, options);
598
- return date;
599
- }, true)]);
600
- };
601
- Schema.regExp = function regExp(flag = "") {
602
- return Schema.union([Schema.is(RegExp), Schema.transform(Schema.string().role("regexp", { flag }), (value, options) => {
603
- try {
604
- return new RegExp(value, flag);
605
- } catch (e) {
606
- throw new ValidationError(e.message, options);
607
- }
608
- }, true)]);
609
- };
610
- Schema.arrayBuffer = function arrayBuffer(encoding) {
611
- return Schema.union([
612
- Schema.is(ArrayBuffer),
613
- Schema.is(SharedArrayBuffer),
614
- Schema.transform(Schema.any(), (value, options) => {
615
- if (Binary.isSource(value)) return Binary.fromSource(value);
616
- throw new ValidationError(`expected ArrayBufferSource but got ${value}`, options);
617
- }, true),
618
- ...encoding ? [Schema.transform(Schema.string(), (value, options) => {
619
- try {
620
- return encoding === "base64" ? Binary.fromBase64(value) : Binary.fromHex(value);
621
- } catch (e) {
622
- throw new ValidationError(e.message, options);
623
- }
624
- }, true)] : []
625
- ]);
626
- };
627
- Schema.extend("lazy", (data, schema, options, strict) => {
628
- if (!schema.inner[kSchema]) {
629
- schema.inner = schema.builder();
630
- schema.inner.meta = {
631
- ...schema.meta,
632
- ...schema.inner.meta
633
- };
634
- }
635
- return Schema.resolve(data, schema.inner, options, strict);
636
- });
637
- Schema.extend("any", (data) => {
638
- return [data];
639
- });
640
- Schema.extend("never", (data, _, options) => {
641
- throw new ValidationError(`expected nullable but got ${data}`, options);
642
- });
643
- Schema.extend("const", (data, { value }, options) => {
644
- if (deepEqual(data, value)) return [value];
645
- throw new ValidationError(`expected ${value} but got ${data}`, options);
646
- });
647
- function checkWithinRange(data, meta, description, options, skipMin = false) {
648
- const { max = Infinity, min = -Infinity } = meta;
649
- if (data > max) throw new ValidationError(`expected ${description} <= ${max} but got ${data}`, options);
650
- if (data < min && !skipMin) throw new ValidationError(`expected ${description} >= ${min} but got ${data}`, options);
651
- }
652
- Schema.extend("string", (data, { meta }, options) => {
653
- if (typeof data !== "string") throw new ValidationError(`expected string but got ${data}`, options);
654
- if (meta.pattern) {
655
- const regexp = new RegExp(meta.pattern.source, meta.pattern.flags);
656
- if (!regexp.test(data)) throw new ValidationError(`expect string to match regexp ${regexp}`, options);
657
- }
658
- checkWithinRange(data.length, meta, "string length", options);
659
- return [data];
660
- });
661
- function decimalShift(data, digits) {
662
- const str = data.toString();
663
- if (str.includes("e")) return data * Math.pow(10, digits);
664
- const index = str.indexOf(".");
665
- if (index === -1) return data * Math.pow(10, digits);
666
- const frac = str.slice(index + 1);
667
- const integer = str.slice(0, index);
668
- if (frac.length <= digits) return +(integer + frac.padEnd(digits, "0"));
669
- return +(integer + frac.slice(0, digits) + "." + frac.slice(digits));
670
- }
671
- function isMultipleOf(data, min, step) {
672
- step = Math.abs(step);
673
- if (!/^\d+\.\d+$/.test(step.toString())) return (data - min) % step === 0;
674
- const index = step.toString().indexOf(".");
675
- const digits = step.toString().slice(index + 1).length;
676
- return Math.abs(decimalShift(data, digits) - decimalShift(min, digits)) % decimalShift(step, digits) === 0;
677
- }
678
- Schema.extend("number", (data, { meta }, options) => {
679
- if (typeof data !== "number") throw new ValidationError(`expected number but got ${data}`, options);
680
- checkWithinRange(data, meta, "number", options);
681
- const { step } = meta;
682
- if (step && !isMultipleOf(data, meta.min ?? 0, step)) throw new ValidationError(`expected number multiple of ${step} but got ${data}`, options);
683
- return [data];
684
- });
685
- Schema.extend("boolean", (data, _, options) => {
686
- if (typeof data === "boolean") return [data];
687
- throw new ValidationError(`expected boolean but got ${data}`, options);
688
- });
689
- Schema.extend("bitset", (data, { bits, meta }, options) => {
690
- let value = 0, keys = [];
691
- if (typeof data === "number") {
692
- value = data;
693
- for (const key in bits) if (data & bits[key]) keys.push(key);
694
- } else if (Array.isArray(data)) {
695
- keys = data;
696
- for (const key of keys) {
697
- if (typeof key !== "string") throw new ValidationError(`expected string but got ${key}`, options);
698
- if (key in bits) value |= bits[key];
699
- }
700
- } else throw new ValidationError(`expected number or array but got ${data}`, options);
701
- if (value === meta.default) return [value];
702
- return [value, keys];
703
- });
704
- Schema.extend("function", (data, _, options) => {
705
- if (typeof data === "function") return [data];
706
- throw new ValidationError(`expected function but got ${data}`, options);
707
- });
708
- Schema.extend("is", (data, { constructor }, options) => {
709
- if (typeof constructor === "function") {
710
- if (data instanceof constructor) return [data];
711
- throw new ValidationError(`expected ${constructor.name} but got ${data}`, options);
712
- } else {
713
- if (isNullable(data)) throw new ValidationError(`expected ${constructor} but got ${data}`, options);
714
- let prototype = Object.getPrototypeOf(data);
715
- while (prototype) {
716
- if (prototype.constructor?.name === constructor) return [data];
717
- prototype = Object.getPrototypeOf(prototype);
718
- }
719
- throw new ValidationError(`expected ${constructor} but got ${data}`, options);
720
- }
721
- });
722
- function property(data, key, schema, options) {
723
- try {
724
- const [value, adapted] = Schema.resolve(data[key], schema, {
725
- ...options,
726
- path: [...options.path || [], key]
727
- });
728
- if (adapted !== void 0) data[key] = adapted;
729
- return value;
730
- } catch (e) {
731
- if (!options?.autofix) throw e;
732
- delete data[key];
733
- return schema.meta.default;
734
- }
735
- }
736
- Schema.extend("array", (data, { inner, meta }, options) => {
737
- if (!Array.isArray(data)) throw new ValidationError(`expected array but got ${data}`, options);
738
- checkWithinRange(data.length, meta, "array length", options, !isNullable(inner.meta.default));
739
- return [data.map((_, index) => property(data, index, inner, options))];
740
- });
741
- Schema.extend("dict", (data, { inner, sKey }, options, strict) => {
742
- if (!isPlainObject(data)) throw new ValidationError(`expected object but got ${data}`, options);
743
- const result = {};
744
- for (const key in data) {
745
- let rKey;
746
- try {
747
- rKey = Schema.resolve(key, sKey, options)[0];
748
- } catch (error) {
749
- if (strict) continue;
750
- throw error;
751
- }
752
- result[rKey] = property(data, key, inner, options);
753
- data[rKey] = data[key];
754
- if (key !== rKey) delete data[key];
755
- }
756
- return [result];
757
- });
758
- Schema.extend("tuple", (data, { list }, options, strict) => {
759
- if (!Array.isArray(data)) throw new ValidationError(`expected array but got ${data}`, options);
760
- const result = list.map((inner, index) => property(data, index, inner, options));
761
- if (strict) return [result];
762
- result.push(...data.slice(list.length));
763
- return [result];
764
- });
765
- function merge(result, data) {
766
- for (const key in data) {
767
- if (key in result) continue;
768
- result[key] = data[key];
769
- }
770
- }
771
- Schema.extend("object", (data, { dict }, options, strict) => {
772
- if (!isPlainObject(data)) throw new ValidationError(`expected object but got ${data}`, options);
773
- const result = {};
774
- for (const key in dict) {
775
- const value = property(data, key, dict[key], options);
776
- if (!isNullable(value) || key in data) result[key] = value;
777
- }
778
- if (!strict) merge(result, data);
779
- return [result];
780
- });
781
- Schema.extend("union", (data, { list, toString }, options, strict) => {
782
- const messages = [];
783
- for (const inner of list) try {
784
- return Schema.resolve(data, inner, options, strict);
785
- } catch (error) {
786
- messages.push(error);
787
- }
788
- throw new ValidationError(`expected ${toString()} but got ${JSON.stringify(data)}`, options);
789
- });
790
- Schema.extend("intersect", (data, { list, toString }, options, strict) => {
791
- if (!list.length) return [data];
792
- let result;
793
- for (const inner of list) {
794
- const value = Schema.resolve(data, inner, options, true)[0];
795
- if (isNullable(value)) continue;
796
- if (isNullable(result)) result = value;
797
- else if (typeof result !== typeof value) throw new ValidationError(`expected ${toString()} but got ${JSON.stringify(data)}`, options);
798
- else if (typeof value === "object") merge(result ??= {}, value);
799
- else if (result !== value) throw new ValidationError(`expected ${toString()} but got ${JSON.stringify(data)}`, options);
800
- }
801
- if (!strict && isPlainObject(data)) merge(result, data);
802
- return [result];
803
- });
804
- Schema.extend("transform", (data, { inner, callback, preserve }, options) => {
805
- const [result, adapted = data] = Schema.resolve(data, inner, options, true);
806
- if (preserve) return [callback(result)];
807
- else return [callback(result), callback(adapted)];
808
- });
809
- const formatters = {};
810
- function defineMethod(name, keys, format) {
811
- formatters[name] = format;
812
- Object.assign(Schema, { [name](...args) {
813
- const schema = new Schema({ type: name });
814
- keys.forEach((key, index) => {
815
- switch (key) {
816
- case "sKey":
817
- schema.sKey = args[index] ?? Schema.string();
818
- break;
819
- case "inner":
820
- schema.inner = Schema.from(args[index]);
821
- break;
822
- case "list":
823
- schema.list = args[index].map(Schema.from);
824
- break;
825
- case "dict":
826
- schema.dict = mapValues(args[index], Schema.from);
827
- break;
828
- case "bits":
829
- schema.bits = {};
830
- for (const key in args[index]) {
831
- if (typeof args[index][key] !== "number") continue;
832
- schema.bits[key] = args[index][key];
833
- }
834
- break;
835
- case "callback": {
836
- const callback = schema.callback = args[index];
837
- callback["toJSON"] ||= () => callback.toString();
838
- break;
839
- }
840
- case "constructor": {
841
- const constructor = schema.constructor = args[index];
842
- if (typeof constructor === "function") constructor["toJSON"] ||= () => constructor["name"];
843
- break;
844
- }
845
- default: schema[key] = args[index];
846
- }
847
- });
848
- if (name === "object" || name === "dict") schema.meta.default = {};
849
- else if (name === "array" || name === "tuple") schema.meta.default = [];
850
- else if (name === "bitset") schema.meta.default = 0;
851
- return schema;
852
- } });
853
- }
854
- defineMethod("is", ["constructor"], ({ constructor }) => {
855
- if (typeof constructor === "function") return constructor.name;
856
- else return constructor;
857
- });
858
- defineMethod("any", [], () => "any");
859
- defineMethod("never", [], () => "never");
860
- defineMethod("const", ["value"], ({ value }) => typeof value === "string" ? JSON.stringify(value) : value);
861
- defineMethod("string", [], () => "string");
862
- defineMethod("number", [], () => "number");
863
- defineMethod("boolean", [], () => "boolean");
864
- defineMethod("bitset", ["bits"], () => "bitset");
865
- defineMethod("function", [], () => "function");
866
- defineMethod("array", ["inner"], ({ inner }) => `${inner.toString(true)}[]`);
867
- defineMethod("dict", ["inner", "sKey"], ({ inner, sKey }) => `{ [key: ${sKey.toString()}]: ${inner.toString()} }`);
868
- defineMethod("tuple", ["list"], ({ list }) => `[${list.map((inner) => inner.toString()).join(", ")}]`);
869
- defineMethod("object", ["dict"], ({ dict }) => {
870
- if (Object.keys(dict).length === 0) return "{}";
871
- return `{ ${Object.entries(dict).map(([key, inner]) => {
872
- return `${key}${inner.meta.required ? "" : "?"}: ${inner.toString()}`;
873
- }).join(", ")} }`;
874
- });
875
- defineMethod("union", ["list"], ({ list }, inline) => {
876
- const result = list.map(({ toString: format }) => format()).join(" | ");
877
- return inline ? `(${result})` : result;
878
- });
879
- defineMethod("intersect", ["list"], ({ list }) => {
880
- return `${list.map((inner) => inner.toString(true)).join(" & ")}`;
881
- });
882
- defineMethod("transform", [
883
- "inner",
884
- "callback",
885
- "preserve"
886
- ], ({ inner }, isInner) => inner.toString(isInner));
887
- //#endregion
888
97
  //#region ../../util/timeout/src/index.ts
889
98
  /** Largest delay Node schedules without clamping it to one millisecond. */
890
99
  const MAX_TIMER_DELAY_MS = 2147483647;
@@ -924,22 +133,22 @@ const DEFAULT_RETRYABLE_CODES = Object.freeze([
924
133
  "TIMEOUT",
925
134
  "TRANSPORT"
926
135
  ]);
927
- const backoffSchema = Schema.object({
928
- initialDelayMs: Schema.number().max(MAX_TIMER_DELAY_MS).default(DEFAULT_INITIAL_DELAY_MS),
929
- maxDelayMs: Schema.number().max(MAX_TIMER_DELAY_MS).default(DEFAULT_MAX_DELAY_MS),
930
- jitterRatio: Schema.number().min(0).max(1).default(DEFAULT_JITTER_RATIO)
136
+ const backoffSchema = z.object({
137
+ initialDelayMs: z.number().max(MAX_TIMER_DELAY_MS).default(DEFAULT_INITIAL_DELAY_MS),
138
+ maxDelayMs: z.number().max(MAX_TIMER_DELAY_MS).default(DEFAULT_MAX_DELAY_MS),
139
+ jitterRatio: z.number().min(0).max(1).default(DEFAULT_JITTER_RATIO)
931
140
  });
932
- const normalPolicySchema = Schema.object({
933
- mode: Schema.const("normal").required(),
934
- maxRetries: Schema.number().step(1).min(0).max(Number.MAX_SAFE_INTEGER).default(DEFAULT_MAX_RETRIES),
935
- retryableCodes: Schema.array(Schema.string()).default([...DEFAULT_RETRYABLE_CODES]),
141
+ const normalPolicySchema = z.object({
142
+ mode: z.const("normal").required(),
143
+ maxRetries: z.number().step(1).min(0).max(Number.MAX_SAFE_INTEGER).default(DEFAULT_MAX_RETRIES),
144
+ retryableCodes: z.array(z.string()).default([...DEFAULT_RETRYABLE_CODES]),
936
145
  backoff: backoffSchema
937
146
  });
938
- const alwaysPolicySchema = Schema.object({
939
- mode: Schema.const("always").required(),
147
+ const alwaysPolicySchema = z.object({
148
+ mode: z.const("always").required(),
940
149
  backoff: backoffSchema
941
150
  });
942
- Schema.union([normalPolicySchema, alwaysPolicySchema]);
151
+ z.union([normalPolicySchema, alwaysPolicySchema]);
943
152
  //#endregion
944
153
  //#region ../../llm/llm/src/attribution.ts
945
154
  /**
@@ -1261,7 +470,7 @@ var SkillLayer = class {
1261
470
  }
1262
471
  };
1263
472
  (class extends Service {
1264
- static Config = Schema.object({ collectCacheMaxEntries: Schema.number().default(DEFAULT_COLLECT_CACHE_ENTRIES) });
473
+ static Config = z.object({ collectCacheMaxEntries: z.number().default(DEFAULT_COLLECT_CACHE_ENTRIES) });
1265
474
  collectCacheMaxEntries;
1266
475
  layers = new ScopedLayers((scope) => new SkillLayer(scope), () => {
1267
476
  this.invalidateCache();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mengyuly/dsh-ponytail",
3
3
  "description": "Lazy senior dev mode for DeepSeek Harness: always-on minimal-code ruleset, intensity switching, and short review/audit/debt/gain/help skills",
4
- "version": "0.1.5",
4
+ "version": "0.1.6",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -44,7 +44,8 @@
44
44
  "@deepseek-ai/dsh-invariants": ">=0.0.1-rc <2",
45
45
  "@deepseek-ai/dsh-llm": ">=0.0.1-rc <2",
46
46
  "@deepseek-ai/dsh-skill": ">=0.0.1-rc <2",
47
- "@deepseek-ai/dsh-system-prompt": ">=0.0.1-rc <2"
47
+ "@deepseek-ai/dsh-system-prompt": ">=0.0.1-rc <2",
48
+ "@deepseek-ai/schemastery": "^3.18.0"
48
49
  },
49
50
  "scripts": {
50
51
  "sync:dist": "node scripts/sync-dist.mjs",