@linxin666/dsh-client-ui-task-board 0.3.24 → 0.4.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/icon.svg +3 -0
- package/lib/client.js +154 -115
- package/lib/client.js.map +1 -1
- package/lib/index.js +109 -898
- package/lib/types/client/PluginSettingsCard.d.ts +4 -0
- package/lib/types/client/TaskBoardSettingsCard.d.ts +5 -5
- package/lib/types/client/index.d.ts +43 -10
- package/lib/types/client/main-session.d.ts +32 -0
- package/lib/types/client/plugin-card-seat.d.ts +23 -16
- package/lib/types/client/settings-form.d.ts +17 -17
- package/lib/types/client/sidebar-entry-core.d.ts +0 -32
- package/lib/types/core/controller.d.ts +10 -8
- package/lib/types/http.d.ts +18 -1
- package/lib/types/index.d.ts +72 -17
- package/package.json +30 -25
- package/src/client/PluginSettingsCard.tsx +7 -2
- package/src/client/TaskBoardSettingsCard.tsx +7 -5
- package/src/client/board.module.css +31 -12
- package/src/client/index.ts +112 -23
- package/src/client/main-session.ts +43 -0
- package/src/client/plugin-card-seat.ts +24 -17
- package/src/client/settings-form.ts +34 -31
- package/src/client/sidebar-entry-core.ts +10 -94
- package/src/client/sidebar-entry.ts +2 -2
- package/src/core/controller.ts +12 -8
- package/src/http.ts +23 -1
- package/src/index.ts +97 -52
package/lib/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
|
-
import z from "schemastery";
|
|
2
|
+
import z from "@deepseek-ai/schemastery";
|
|
3
3
|
import { spawn, spawnSync } from "node:child_process";
|
|
4
4
|
import { createHash, timingSafeEqual } from "node:crypto";
|
|
5
5
|
import { chmodSync, closeSync, existsSync, fsyncSync, mkdirSync, openSync, readFileSync, readdirSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
6
6
|
import { dirname, isAbsolute, join, win32 } from "node:path";
|
|
7
7
|
import { homedir } from "node:os";
|
|
8
8
|
import { isAbsolute as isAbsolute$1, join as join$1 } from "node:path/posix";
|
|
9
|
-
import { Service } from "@deepseek-ai/cordis";
|
|
9
|
+
import { Context, Service } from "@deepseek-ai/cordis";
|
|
10
10
|
//#region src/core/schedule.ts
|
|
11
11
|
/** Inclusive ranges per field, in cron order. */
|
|
12
12
|
const FIELD_RANGES = [
|
|
@@ -3079,7 +3079,7 @@ function safeConsoleError(message, ...args) {
|
|
|
3079
3079
|
} catch {}
|
|
3080
3080
|
}
|
|
3081
3081
|
//#endregion
|
|
3082
|
-
//#region ../../node_modules/.pnpm/@deepseek-ai+dsh-typert-protocol@0.1.
|
|
3082
|
+
//#region ../../node_modules/.pnpm/@deepseek-ai+dsh-typert-protocol@0.1.7-rc.1_@deepseek-ai+cordis@4.0.4/node_modules/@deepseek-ai/dsh-typert-protocol/lib/index.js
|
|
3083
3083
|
/** The one Remote failure class shared by owners, the Gateway, and consumers. */
|
|
3084
3084
|
/**
|
|
3085
3085
|
* One Remote call failure: a real Error carrying its stable code and typed
|
|
@@ -3123,7 +3123,10 @@ function isTypertRemoteSegment(value) {
|
|
|
3123
3123
|
}
|
|
3124
3124
|
const REMOTE_METHOD_DESCRIPTOR = "@deepseek-ai/dsh-typert-protocol/remote-methods";
|
|
3125
3125
|
/**
|
|
3126
|
-
* Bind one visible Service field to a Cordis key and Remote namespace.
|
|
3126
|
+
* Bind one visible Service field to a Cordis key and Remote namespace. A
|
|
3127
|
+
* service that owns a Cordis Context also gives its tree `ctx.invocation`,
|
|
3128
|
+
* `undefined` outside a Remote call, so no `TypertRemoteService` is needed for
|
|
3129
|
+
* a Host composition to read it.
|
|
3127
3130
|
* @param service - owning Service instance, normally `this`.
|
|
3128
3131
|
* @param serviceKey - exact Cordis service key.
|
|
3129
3132
|
* @param options - optional distinct wire namespace.
|
|
@@ -3133,6 +3136,8 @@ function bindTypertRemote(service, serviceKey, options = {}) {
|
|
|
3133
3136
|
validateName("service key", serviceKey);
|
|
3134
3137
|
const namespace = options.namespace ?? serviceKey;
|
|
3135
3138
|
validateName("namespace", namespace);
|
|
3139
|
+
const ctx = Reflect.get(service, "ctx");
|
|
3140
|
+
if (ctx instanceof Context) provideInvocationAccessor(ctx);
|
|
3136
3141
|
return Object.freeze({
|
|
3137
3142
|
service,
|
|
3138
3143
|
serviceKey,
|
|
@@ -3154,6 +3159,16 @@ var TypertRemoteService = class extends Service {
|
|
|
3154
3159
|
this.typertRemote = bindTypertRemote(this, this.name, options);
|
|
3155
3160
|
}
|
|
3156
3161
|
};
|
|
3162
|
+
/**
|
|
3163
|
+
* Make `ctx.invocation` read as `undefined` outside a Remote call instead of the
|
|
3164
|
+
* reflect service's "cannot get property" error; a call-derived Context shadows
|
|
3165
|
+
* the accessor with its own property. The first Remote Service constructed in a
|
|
3166
|
+
* tree registers it on the root, where it outlives any one Service.
|
|
3167
|
+
*/
|
|
3168
|
+
function provideInvocationAccessor(ctx) {
|
|
3169
|
+
if (Object.hasOwn(ctx.root.reflect.props, "invocation")) return;
|
|
3170
|
+
ctx.root.accessor("invocation", { get: () => void 0 });
|
|
3171
|
+
}
|
|
3157
3172
|
function Remote(methodExportOrOptions, context) {
|
|
3158
3173
|
if (typeof methodExportOrOptions === "string") {
|
|
3159
3174
|
validateName("Remote export name", methodExportOrOptions);
|
|
@@ -3224,7 +3239,7 @@ function validateName(subject, value) {
|
|
|
3224
3239
|
if (!isTypertRemoteSegment(value)) throw new TypeError(`typert-protocol: ${subject} must contain only RPC endpoint segment characters`);
|
|
3225
3240
|
}
|
|
3226
3241
|
//#endregion
|
|
3227
|
-
//#region ../../node_modules/.pnpm/@deepseek-ai+dsh-util-values@0.1.
|
|
3242
|
+
//#region ../../node_modules/.pnpm/@deepseek-ai+dsh-util-values@0.1.7-rc.1_@deepseek-ai+cordis@4.0.4/node_modules/@deepseek-ai/dsh-util-values/lib/index.js
|
|
3228
3243
|
/**
|
|
3229
3244
|
* Deep-freeze an object graph in place while leaving live AbortSignal objects mutable.
|
|
3230
3245
|
* @param value - value to freeze.
|
|
@@ -3268,7 +3283,7 @@ function deepFreeze(value) {
|
|
|
3268
3283
|
return value;
|
|
3269
3284
|
}
|
|
3270
3285
|
//#endregion
|
|
3271
|
-
//#region ../../node_modules/.pnpm/@deepseek-ai+dsh-util-crypto@0.1.
|
|
3286
|
+
//#region ../../node_modules/.pnpm/@deepseek-ai+dsh-util-crypto@0.1.7-rc.1_@deepseek-ai+cordis@4.0.4/node_modules/@deepseek-ai/dsh-util-crypto/lib/index.js
|
|
3272
3287
|
/**
|
|
3273
3288
|
* Random v4 UUID, minted from `crypto.getRandomValues`.
|
|
3274
3289
|
* @returns the UUID string.
|
|
@@ -3281,7 +3296,7 @@ function randomUUID() {
|
|
|
3281
3296
|
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
3282
3297
|
}
|
|
3283
3298
|
//#endregion
|
|
3284
|
-
//#region ../../node_modules/.pnpm/@deepseek-ai+dsh-brand@0.1.
|
|
3299
|
+
//#region ../../node_modules/.pnpm/@deepseek-ai+dsh-brand@0.1.7-rc.1_@deepseek-ai+cordis@4.0.4/node_modules/@deepseek-ai/dsh-brand/lib/index.js
|
|
3285
3300
|
/**
|
|
3286
3301
|
* Duplicate-install-safe nominal primitive helpers.
|
|
3287
3302
|
*
|
|
@@ -3304,801 +3319,11 @@ function brandString(value) {
|
|
|
3304
3319
|
return value;
|
|
3305
3320
|
}
|
|
3306
3321
|
//#endregion
|
|
3307
|
-
//#region ../../node_modules/.pnpm/@deepseek-ai+
|
|
3308
|
-
/** Return true when a value is `null` or `undefined`. */
|
|
3309
|
-
function isNullable(value) {
|
|
3310
|
-
return value === null || value === void 0;
|
|
3311
|
-
}
|
|
3312
|
-
/** Return true for non-array object values. */
|
|
3313
|
-
function isPlainObject(data) {
|
|
3314
|
-
return data && typeof data === "object" && !Array.isArray(data);
|
|
3315
|
-
}
|
|
3316
|
-
/** Filter object entries and return a new object. */
|
|
3317
|
-
function filterKeys(object, filter) {
|
|
3318
|
-
return Object.fromEntries(Object.entries(object).filter(([key, value]) => filter(key, value)));
|
|
3319
|
-
}
|
|
3320
|
-
/** Map object values while preserving the original key set. */
|
|
3321
|
-
function mapValues(object, transform) {
|
|
3322
|
-
return Object.fromEntries(Object.entries(object).map(([key, value]) => [key, transform(value, key)]));
|
|
3323
|
-
}
|
|
3324
|
-
/** Pick selected keys from an object, optionally including `undefined` values. */
|
|
3325
|
-
function pick(source, keys, forced) {
|
|
3326
|
-
if (!keys) return { ...source };
|
|
3327
|
-
const result = {};
|
|
3328
|
-
for (const key of keys) if (forced || source[key] !== void 0) result[key] = source[key];
|
|
3329
|
-
return result;
|
|
3330
|
-
}
|
|
3331
|
-
/** Test values using `instanceof` with a `toStringTag` fallback. */
|
|
3332
|
-
function is(type, value) {
|
|
3333
|
-
if (arguments.length === 1) return (value) => is(type, value);
|
|
3334
|
-
return type in globalThis && value instanceof globalThis[type] || Object.prototype.toString.call(value).slice(8, -1) === type;
|
|
3335
|
-
}
|
|
3336
|
-
function isArrayBufferLike(value) {
|
|
3337
|
-
return is("ArrayBuffer", value) || is("SharedArrayBuffer", value);
|
|
3338
|
-
}
|
|
3339
|
-
function isArrayBufferSource(value) {
|
|
3340
|
-
return isArrayBufferLike(value) || ArrayBuffer.isView(value);
|
|
3341
|
-
}
|
|
3342
|
-
/** Binary source detection and base64/hex conversion helpers. */
|
|
3343
|
-
var Binary;
|
|
3344
|
-
(function(Binary) {
|
|
3345
|
-
Binary.is = isArrayBufferLike;
|
|
3346
|
-
Binary.isSource = isArrayBufferSource;
|
|
3347
|
-
function fromSource(source) {
|
|
3348
|
-
if (ArrayBuffer.isView(source)) return source.buffer.slice(source.byteOffset, source.byteOffset + source.byteLength);
|
|
3349
|
-
else return source;
|
|
3350
|
-
}
|
|
3351
|
-
Binary.fromSource = fromSource;
|
|
3352
|
-
function toBase64(source) {
|
|
3353
|
-
source = fromSource(source);
|
|
3354
|
-
if (typeof Buffer !== "undefined") return Buffer.from(source).toString("base64");
|
|
3355
|
-
let binary = "";
|
|
3356
|
-
const bytes = new Uint8Array(source);
|
|
3357
|
-
for (let i = 0; i < bytes.byteLength; i++) binary += String.fromCharCode(bytes[i]);
|
|
3358
|
-
return btoa(binary);
|
|
3359
|
-
}
|
|
3360
|
-
Binary.toBase64 = toBase64;
|
|
3361
|
-
function fromBase64(source) {
|
|
3362
|
-
if (typeof Buffer !== "undefined") return fromSource(Buffer.from(source, "base64"));
|
|
3363
|
-
return Uint8Array.from(atob(source), (c) => c.charCodeAt(0));
|
|
3364
|
-
}
|
|
3365
|
-
Binary.fromBase64 = fromBase64;
|
|
3366
|
-
function toHex(source) {
|
|
3367
|
-
source = fromSource(source);
|
|
3368
|
-
if (typeof Buffer !== "undefined") return Buffer.from(source).toString("hex");
|
|
3369
|
-
return Array.from(new Uint8Array(source), (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
3370
|
-
}
|
|
3371
|
-
Binary.toHex = toHex;
|
|
3372
|
-
function fromHex(source) {
|
|
3373
|
-
if (typeof Buffer !== "undefined") return fromSource(Buffer.from(source, "hex"));
|
|
3374
|
-
const hex = source.length % 2 === 0 ? source : source.slice(0, source.length - 1);
|
|
3375
|
-
const buffer = [];
|
|
3376
|
-
for (let i = 0; i < hex.length; i += 2) buffer.push(parseInt(`${hex[i]}${hex[i + 1]}`, 16));
|
|
3377
|
-
return Uint8Array.from(buffer).buffer;
|
|
3378
|
-
}
|
|
3379
|
-
Binary.fromHex = fromHex;
|
|
3380
|
-
})(Binary || (Binary = {}));
|
|
3381
|
-
Binary.fromBase64;
|
|
3382
|
-
Binary.toBase64;
|
|
3383
|
-
Binary.fromHex;
|
|
3384
|
-
Binary.toHex;
|
|
3385
|
-
/** Deep-clone common JavaScript values while preserving prototypes and cycles. */
|
|
3386
|
-
function clone(source, refs = /* @__PURE__ */ new Map()) {
|
|
3387
|
-
if (!source || typeof source !== "object") return source;
|
|
3388
|
-
if (is("Date", source)) return new Date(source.valueOf());
|
|
3389
|
-
if (is("RegExp", source)) return new RegExp(source.source, source.flags);
|
|
3390
|
-
if (isArrayBufferLike(source)) return source.slice(0);
|
|
3391
|
-
if (ArrayBuffer.isView(source)) return source.buffer.slice(source.byteOffset, source.byteOffset + source.byteLength);
|
|
3392
|
-
const cached = refs.get(source);
|
|
3393
|
-
if (cached) return cached;
|
|
3394
|
-
if (Array.isArray(source)) {
|
|
3395
|
-
const result = [];
|
|
3396
|
-
refs.set(source, result);
|
|
3397
|
-
source.forEach((value, index) => {
|
|
3398
|
-
result[index] = Reflect.apply(clone, null, [value, refs]);
|
|
3399
|
-
});
|
|
3400
|
-
return result;
|
|
3401
|
-
}
|
|
3402
|
-
const result = Object.create(Object.getPrototypeOf(source));
|
|
3403
|
-
refs.set(source, result);
|
|
3404
|
-
for (const key of Reflect.ownKeys(source)) {
|
|
3405
|
-
const descriptor = { ...Reflect.getOwnPropertyDescriptor(source, key) };
|
|
3406
|
-
if ("value" in descriptor) descriptor.value = Reflect.apply(clone, null, [descriptor.value, refs]);
|
|
3407
|
-
Reflect.defineProperty(result, key, descriptor);
|
|
3408
|
-
}
|
|
3409
|
-
return result;
|
|
3410
|
-
}
|
|
3411
|
-
/** Deeply compare arrays, dates, regexps, buffers, and plain object fields. */
|
|
3412
|
-
function deepEqual(a, b, strict) {
|
|
3413
|
-
if (a === b) return true;
|
|
3414
|
-
if (!strict && isNullable(a) && isNullable(b)) return true;
|
|
3415
|
-
if (typeof a !== typeof b) return false;
|
|
3416
|
-
if (typeof a !== "object") return false;
|
|
3417
|
-
if (!a || !b) return false;
|
|
3418
|
-
function check(test, then) {
|
|
3419
|
-
return test(a) ? test(b) ? then(a, b) : false : test(b) ? false : void 0;
|
|
3420
|
-
}
|
|
3421
|
-
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) => {
|
|
3422
|
-
if (a.byteLength !== b.byteLength) return false;
|
|
3423
|
-
const viewA = new Uint8Array(a);
|
|
3424
|
-
const viewB = new Uint8Array(b);
|
|
3425
|
-
for (let i = 0; i < viewA.length; i++) if (viewA[i] !== viewB[i]) return false;
|
|
3426
|
-
return true;
|
|
3427
|
-
}) ?? Object.keys({
|
|
3428
|
-
...a,
|
|
3429
|
-
...b
|
|
3430
|
-
}).every((key) => deepEqual(a[key], b[key], strict));
|
|
3431
|
-
}
|
|
3432
|
-
/** Time constants plus parsing and formatting helpers. */
|
|
3433
|
-
var Time;
|
|
3434
|
-
(function(Time) {
|
|
3435
|
-
Time.millisecond = 1;
|
|
3436
|
-
Time.second = 1e3;
|
|
3437
|
-
Time.minute = Time.second * 60;
|
|
3438
|
-
Time.hour = Time.minute * 60;
|
|
3439
|
-
Time.day = Time.hour * 24;
|
|
3440
|
-
Time.week = Time.day * 7;
|
|
3441
|
-
let timezoneOffset = (/* @__PURE__ */ new Date()).getTimezoneOffset();
|
|
3442
|
-
function setTimezoneOffset(offset) {
|
|
3443
|
-
timezoneOffset = offset;
|
|
3444
|
-
}
|
|
3445
|
-
Time.setTimezoneOffset = setTimezoneOffset;
|
|
3446
|
-
function getTimezoneOffset() {
|
|
3447
|
-
return timezoneOffset;
|
|
3448
|
-
}
|
|
3449
|
-
Time.getTimezoneOffset = getTimezoneOffset;
|
|
3450
|
-
function getDateNumber(date = /* @__PURE__ */ new Date(), offset) {
|
|
3451
|
-
if (typeof date === "number") date = new Date(date);
|
|
3452
|
-
if (offset === void 0) offset = timezoneOffset;
|
|
3453
|
-
return Math.floor((date.valueOf() / Time.minute - offset) / 1440);
|
|
3454
|
-
}
|
|
3455
|
-
Time.getDateNumber = getDateNumber;
|
|
3456
|
-
function fromDateNumber(value, offset) {
|
|
3457
|
-
const date = new Date(value * Time.day);
|
|
3458
|
-
if (offset === void 0) offset = timezoneOffset;
|
|
3459
|
-
return new Date(+date + offset * Time.minute);
|
|
3460
|
-
}
|
|
3461
|
-
Time.fromDateNumber = fromDateNumber;
|
|
3462
|
-
const numeric = /\d+(?:\.\d+)?/.source;
|
|
3463
|
-
const timeRegExp = new RegExp(`^${[
|
|
3464
|
-
"w(?:eek(?:s)?)?",
|
|
3465
|
-
"d(?:ay(?:s)?)?",
|
|
3466
|
-
"h(?:our(?:s)?)?",
|
|
3467
|
-
"m(?:in(?:ute)?(?:s)?)?",
|
|
3468
|
-
"s(?:ec(?:ond)?(?:s)?)?"
|
|
3469
|
-
].map((unit) => `(${numeric}${unit})?`).join("")}$`);
|
|
3470
|
-
function parseTime(source) {
|
|
3471
|
-
const capture = timeRegExp.exec(source);
|
|
3472
|
-
if (!capture) return 0;
|
|
3473
|
-
return (parseFloat(capture[1]) * Time.week || 0) + (parseFloat(capture[2]) * Time.day || 0) + (parseFloat(capture[3]) * Time.hour || 0) + (parseFloat(capture[4]) * Time.minute || 0) + (parseFloat(capture[5]) * Time.second || 0);
|
|
3474
|
-
}
|
|
3475
|
-
Time.parseTime = parseTime;
|
|
3476
|
-
function parseDate(date) {
|
|
3477
|
-
const parsed = parseTime(date);
|
|
3478
|
-
if (parsed) date = Date.now() + parsed;
|
|
3479
|
-
else if (/^\d{1,2}(:\d{1,2}){1,2}$/.test(date)) date = `${(/* @__PURE__ */ new Date()).toLocaleDateString()}-${date}`;
|
|
3480
|
-
else if (/^\d{1,2}-\d{1,2}-\d{1,2}(:\d{1,2}){1,2}$/.test(date)) date = `${(/* @__PURE__ */ new Date()).getFullYear()}-${date}`;
|
|
3481
|
-
return date ? new Date(date) : /* @__PURE__ */ new Date();
|
|
3482
|
-
}
|
|
3483
|
-
Time.parseDate = parseDate;
|
|
3484
|
-
function format(ms) {
|
|
3485
|
-
const abs = Math.abs(ms);
|
|
3486
|
-
if (abs >= Time.day - Time.hour / 2) return Math.round(ms / Time.day) + "d";
|
|
3487
|
-
else if (abs >= Time.hour - Time.minute / 2) return Math.round(ms / Time.hour) + "h";
|
|
3488
|
-
else if (abs >= Time.minute - Time.second / 2) return Math.round(ms / Time.minute) + "m";
|
|
3489
|
-
else if (abs >= Time.second) return Math.round(ms / Time.second) + "s";
|
|
3490
|
-
return ms + "ms";
|
|
3491
|
-
}
|
|
3492
|
-
Time.format = format;
|
|
3493
|
-
function toDigits(source, length = 2) {
|
|
3494
|
-
return source.toString().padStart(length, "0");
|
|
3495
|
-
}
|
|
3496
|
-
Time.toDigits = toDigits;
|
|
3497
|
-
function template(template, time = /* @__PURE__ */ new Date()) {
|
|
3498
|
-
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));
|
|
3499
|
-
}
|
|
3500
|
-
Time.template = template;
|
|
3501
|
-
})(Time || (Time = {}));
|
|
3502
|
-
//#endregion
|
|
3503
|
-
//#region ../../node_modules/.pnpm/@deepseek-ai+schemastery@3.18.2/node_modules/@deepseek-ai/schemastery/lib/index.mjs
|
|
3504
|
-
const kSchema = Symbol.for("schemastery");
|
|
3505
|
-
const kValidationError = Symbol.for("ValidationError");
|
|
3506
|
-
globalThis.__schemastery_index__ ??= 0;
|
|
3507
|
-
globalThis.__schemastery_refs__ = void 0;
|
|
3508
|
-
var ValidationError = class extends TypeError {
|
|
3509
|
-
options;
|
|
3510
|
-
name = "ValidationError";
|
|
3511
|
-
constructor(message, options) {
|
|
3512
|
-
let prefix = "$";
|
|
3513
|
-
for (const segment of options.path || []) if (typeof segment === "string") prefix += "." + segment;
|
|
3514
|
-
else if (typeof segment === "number") prefix += "[" + segment + "]";
|
|
3515
|
-
else if (typeof segment === "symbol") prefix += `[Symbol(${segment.toString()})]`;
|
|
3516
|
-
if (prefix.startsWith(".")) prefix = prefix.slice(1);
|
|
3517
|
-
super((prefix === "$" ? "" : `${prefix} `) + message);
|
|
3518
|
-
this.options = options;
|
|
3519
|
-
}
|
|
3520
|
-
static is(error) {
|
|
3521
|
-
return !!error?.[kValidationError];
|
|
3522
|
-
}
|
|
3523
|
-
};
|
|
3524
|
-
Object.defineProperty(ValidationError.prototype, kValidationError, { value: true });
|
|
3525
|
-
const Schema = function(options) {
|
|
3526
|
-
const schema = function(data, options = {}) {
|
|
3527
|
-
return Schema.resolve(data, schema, options)[0];
|
|
3528
|
-
};
|
|
3529
|
-
if (options.refs) {
|
|
3530
|
-
const refs = mapValues(options.refs, (options) => new Schema(options));
|
|
3531
|
-
const getRef = (uid) => refs[uid];
|
|
3532
|
-
for (const key in refs) {
|
|
3533
|
-
const options = refs[key];
|
|
3534
|
-
options.sKey = getRef(options.sKey);
|
|
3535
|
-
options.inner = getRef(options.inner);
|
|
3536
|
-
options.list = options.list && options.list.map(getRef);
|
|
3537
|
-
options.dict = options.dict && mapValues(options.dict, getRef);
|
|
3538
|
-
}
|
|
3539
|
-
return refs[options.uid];
|
|
3540
|
-
}
|
|
3541
|
-
Object.assign(schema, options);
|
|
3542
|
-
if (typeof schema.callback === "string") try {
|
|
3543
|
-
schema.callback = new Function("return " + schema.callback)();
|
|
3544
|
-
} catch {}
|
|
3545
|
-
Object.defineProperty(schema, "uid", { value: globalThis.__schemastery_index__++ });
|
|
3546
|
-
Object.setPrototypeOf(schema, Schema.prototype);
|
|
3547
|
-
schema.meta ||= {};
|
|
3548
|
-
schema.toString = schema.toString.bind(schema);
|
|
3549
|
-
return schema;
|
|
3550
|
-
};
|
|
3551
|
-
Schema.prototype = Object.create(Function.prototype);
|
|
3552
|
-
Schema.prototype[kSchema] = true;
|
|
3553
|
-
Object.defineProperty(Schema.prototype, "~standard", { get() {
|
|
3554
|
-
return {
|
|
3555
|
-
version: 1,
|
|
3556
|
-
vendor: "schemastery",
|
|
3557
|
-
validate: (value) => {
|
|
3558
|
-
try {
|
|
3559
|
-
return { value: Schema.resolve(value, this, {})[0] };
|
|
3560
|
-
} catch (error) {
|
|
3561
|
-
if (ValidationError.is(error)) return { issues: [{
|
|
3562
|
-
message: error.message,
|
|
3563
|
-
path: error.options.path
|
|
3564
|
-
}] };
|
|
3565
|
-
throw error;
|
|
3566
|
-
}
|
|
3567
|
-
}
|
|
3568
|
-
};
|
|
3569
|
-
} });
|
|
3570
|
-
Schema.ValidationError = ValidationError;
|
|
3571
|
-
Schema.prototype.toJSON = function toJSON() {
|
|
3572
|
-
if (globalThis.__schemastery_refs__) {
|
|
3573
|
-
globalThis.__schemastery_refs__[this.uid] ??= JSON.parse(JSON.stringify({ ...this }));
|
|
3574
|
-
return this.uid;
|
|
3575
|
-
}
|
|
3576
|
-
globalThis.__schemastery_refs__ = { [this.uid]: { ...this } };
|
|
3577
|
-
globalThis.__schemastery_refs__[this.uid] = JSON.parse(JSON.stringify({ ...this }));
|
|
3578
|
-
const result = {
|
|
3579
|
-
uid: this.uid,
|
|
3580
|
-
refs: globalThis.__schemastery_refs__
|
|
3581
|
-
};
|
|
3582
|
-
globalThis.__schemastery_refs__ = void 0;
|
|
3583
|
-
return result;
|
|
3584
|
-
};
|
|
3585
|
-
Schema.prototype.set = function set(key, value) {
|
|
3586
|
-
this.dict[key] = value;
|
|
3587
|
-
return this;
|
|
3588
|
-
};
|
|
3589
|
-
Schema.prototype.push = function push(value) {
|
|
3590
|
-
this.list.push(value);
|
|
3591
|
-
return this;
|
|
3592
|
-
};
|
|
3593
|
-
function mergeDesc(original, messages) {
|
|
3594
|
-
const result = typeof original === "string" ? { "": original } : { ...original };
|
|
3595
|
-
for (const locale in messages) {
|
|
3596
|
-
const value = messages[locale];
|
|
3597
|
-
if (value?.$description || value?.$desc) result[locale] = value.$description || value.$desc;
|
|
3598
|
-
else if (typeof value === "string") result[locale] = value;
|
|
3599
|
-
}
|
|
3600
|
-
return result;
|
|
3601
|
-
}
|
|
3602
|
-
function getInner(value) {
|
|
3603
|
-
return value?.$value ?? value?.$inner;
|
|
3604
|
-
}
|
|
3605
|
-
function extractKeys(data) {
|
|
3606
|
-
return filterKeys(data ?? {}, (key) => !key.startsWith("$"));
|
|
3607
|
-
}
|
|
3608
|
-
Schema.prototype.i18n = function i18n(messages) {
|
|
3609
|
-
const schema = Schema(this);
|
|
3610
|
-
const desc = mergeDesc(schema.meta.description, messages);
|
|
3611
|
-
if (Object.keys(desc).length) schema.meta.description = desc;
|
|
3612
|
-
if (schema.dict) schema.dict = mapValues(schema.dict, (inner, key) => {
|
|
3613
|
-
return inner.i18n(mapValues(messages, (data) => getInner(data)?.[key] ?? data?.[key]));
|
|
3614
|
-
});
|
|
3615
|
-
if (schema.list) schema.list = schema.list.map((inner, index) => {
|
|
3616
|
-
return inner.i18n(mapValues(messages, (data = {}) => {
|
|
3617
|
-
if (Array.isArray(getInner(data))) return getInner(data)[index];
|
|
3618
|
-
if (Array.isArray(data)) return data[index];
|
|
3619
|
-
return extractKeys(data);
|
|
3620
|
-
}));
|
|
3621
|
-
});
|
|
3622
|
-
if (schema.inner) schema.inner = schema.inner.i18n(mapValues(messages, (data) => {
|
|
3623
|
-
if (getInner(data)) return getInner(data);
|
|
3624
|
-
return extractKeys(data);
|
|
3625
|
-
}));
|
|
3626
|
-
if (schema.sKey) schema.sKey = schema.sKey.i18n(mapValues(messages, (data) => data?.$key));
|
|
3627
|
-
return schema;
|
|
3628
|
-
};
|
|
3629
|
-
Schema.prototype.extra = function extra(key, value) {
|
|
3630
|
-
const schema = Schema(this);
|
|
3631
|
-
schema.meta = {
|
|
3632
|
-
...schema.meta,
|
|
3633
|
-
[key]: value
|
|
3634
|
-
};
|
|
3635
|
-
return schema;
|
|
3636
|
-
};
|
|
3637
|
-
for (const key of [
|
|
3638
|
-
"required",
|
|
3639
|
-
"disabled",
|
|
3640
|
-
"collapse",
|
|
3641
|
-
"hidden",
|
|
3642
|
-
"loose"
|
|
3643
|
-
]) Object.assign(Schema.prototype, { [key](value = true) {
|
|
3644
|
-
const schema = Schema(this);
|
|
3645
|
-
schema.meta = {
|
|
3646
|
-
...schema.meta,
|
|
3647
|
-
[key]: value
|
|
3648
|
-
};
|
|
3649
|
-
return schema;
|
|
3650
|
-
} });
|
|
3651
|
-
Schema.prototype.deprecated = function deprecated() {
|
|
3652
|
-
const schema = Schema(this);
|
|
3653
|
-
schema.meta.badges ||= [];
|
|
3654
|
-
schema.meta.badges.push({
|
|
3655
|
-
text: "deprecated",
|
|
3656
|
-
type: "danger"
|
|
3657
|
-
});
|
|
3658
|
-
return schema;
|
|
3659
|
-
};
|
|
3660
|
-
Schema.prototype.experimental = function experimental() {
|
|
3661
|
-
const schema = Schema(this);
|
|
3662
|
-
schema.meta.badges ||= [];
|
|
3663
|
-
schema.meta.badges.push({
|
|
3664
|
-
text: "experimental",
|
|
3665
|
-
type: "warning"
|
|
3666
|
-
});
|
|
3667
|
-
return schema;
|
|
3668
|
-
};
|
|
3669
|
-
Schema.prototype.pattern = function pattern(regexp) {
|
|
3670
|
-
const schema = Schema(this);
|
|
3671
|
-
const pattern = pick(regexp, ["source", "flags"]);
|
|
3672
|
-
schema.meta = {
|
|
3673
|
-
...schema.meta,
|
|
3674
|
-
pattern
|
|
3675
|
-
};
|
|
3676
|
-
return schema;
|
|
3677
|
-
};
|
|
3678
|
-
Schema.prototype.simplify = function simplify(value) {
|
|
3679
|
-
if (deepEqual(value, this.meta.default, this.type === "dict")) return null;
|
|
3680
|
-
if (isNullable(value)) return value;
|
|
3681
|
-
if (this.type === "object" || this.type === "dict") {
|
|
3682
|
-
const result = {};
|
|
3683
|
-
for (const key in value) {
|
|
3684
|
-
const item = (this.type === "object" ? this.dict[key] : this.inner)?.simplify(value[key]);
|
|
3685
|
-
if (this.type === "dict" || !isNullable(item)) result[key] = item;
|
|
3686
|
-
}
|
|
3687
|
-
if (deepEqual(result, this.meta.default, this.type === "dict")) return null;
|
|
3688
|
-
return result;
|
|
3689
|
-
} else if (this.type === "array" || this.type === "tuple") {
|
|
3690
|
-
const result = [];
|
|
3691
|
-
value.forEach((value, index) => {
|
|
3692
|
-
const schema = this.type === "array" ? this.inner : this.list[index];
|
|
3693
|
-
const item = schema ? schema.simplify(value) : value;
|
|
3694
|
-
result.push(item);
|
|
3695
|
-
});
|
|
3696
|
-
return result;
|
|
3697
|
-
} else if (this.type === "intersect") {
|
|
3698
|
-
const result = {};
|
|
3699
|
-
for (const item of this.list) Object.assign(result, item.simplify(value));
|
|
3700
|
-
return result;
|
|
3701
|
-
} else if (this.type === "union") for (const schema of this.list) try {
|
|
3702
|
-
Schema.resolve(value, schema, {});
|
|
3703
|
-
return schema.simplify(value);
|
|
3704
|
-
} catch {}
|
|
3705
|
-
return value;
|
|
3706
|
-
};
|
|
3707
|
-
Schema.prototype.toString = function toString(inline) {
|
|
3708
|
-
return formatters[this.type]?.(this, inline) ?? `Schema<${this.type}>`;
|
|
3709
|
-
};
|
|
3710
|
-
Schema.prototype.role = function role(role, extra) {
|
|
3711
|
-
const schema = Schema(this);
|
|
3712
|
-
schema.meta = {
|
|
3713
|
-
...schema.meta,
|
|
3714
|
-
role,
|
|
3715
|
-
extra
|
|
3716
|
-
};
|
|
3717
|
-
return schema;
|
|
3718
|
-
};
|
|
3719
|
-
for (const key of [
|
|
3720
|
-
"default",
|
|
3721
|
-
"link",
|
|
3722
|
-
"comment",
|
|
3723
|
-
"description",
|
|
3724
|
-
"max",
|
|
3725
|
-
"min",
|
|
3726
|
-
"step"
|
|
3727
|
-
]) Object.assign(Schema.prototype, { [key](value) {
|
|
3728
|
-
const schema = Schema(this);
|
|
3729
|
-
schema.meta = {
|
|
3730
|
-
...schema.meta,
|
|
3731
|
-
[key]: value
|
|
3732
|
-
};
|
|
3733
|
-
return schema;
|
|
3734
|
-
} });
|
|
3735
|
-
const resolvers = {};
|
|
3736
|
-
Schema.extend = function extend(type, resolve) {
|
|
3737
|
-
resolvers[type] = resolve;
|
|
3738
|
-
};
|
|
3739
|
-
Schema.resolve = function resolve(data, schema, options = {}, strict = false) {
|
|
3740
|
-
if (!schema) return [data];
|
|
3741
|
-
if (options.ignore?.(data, schema)) return [data];
|
|
3742
|
-
if (isNullable(data) && schema.type !== "lazy") {
|
|
3743
|
-
if (schema.meta.required) throw new ValidationError(`missing required value`, options);
|
|
3744
|
-
let current = schema;
|
|
3745
|
-
let fallback = schema.meta.default;
|
|
3746
|
-
while (current?.type === "intersect" && isNullable(fallback)) {
|
|
3747
|
-
current = current.list[0];
|
|
3748
|
-
fallback = current?.meta.default;
|
|
3749
|
-
}
|
|
3750
|
-
if (isNullable(fallback)) return [data];
|
|
3751
|
-
data = clone(fallback);
|
|
3752
|
-
}
|
|
3753
|
-
const callback = resolvers[schema.type];
|
|
3754
|
-
if (!callback) throw new ValidationError(`unsupported type "${schema.type}"`, options);
|
|
3755
|
-
try {
|
|
3756
|
-
return callback(data, schema, options, strict);
|
|
3757
|
-
} catch (error) {
|
|
3758
|
-
if (!schema.meta.loose) throw error;
|
|
3759
|
-
return [schema.meta.default];
|
|
3760
|
-
}
|
|
3761
|
-
};
|
|
3762
|
-
Schema.from = function from(source) {
|
|
3763
|
-
if (isNullable(source)) return Schema.any();
|
|
3764
|
-
else if ([
|
|
3765
|
-
"string",
|
|
3766
|
-
"number",
|
|
3767
|
-
"boolean"
|
|
3768
|
-
].includes(typeof source)) return Schema.const(source).required();
|
|
3769
|
-
else if (source[kSchema]) return source;
|
|
3770
|
-
else if (typeof source === "function") switch (source) {
|
|
3771
|
-
case String: return Schema.string().required();
|
|
3772
|
-
case Number: return Schema.number().required();
|
|
3773
|
-
case Boolean: return Schema.boolean().required();
|
|
3774
|
-
case Function: return Schema.function().required();
|
|
3775
|
-
default: return Schema.is(source).required();
|
|
3776
|
-
}
|
|
3777
|
-
else throw new TypeError(`cannot infer schema from ${source}`);
|
|
3778
|
-
};
|
|
3779
|
-
Schema.lazy = function lazy(builder) {
|
|
3780
|
-
const toJSON = () => {
|
|
3781
|
-
if (!schema.inner[kSchema]) {
|
|
3782
|
-
schema.inner = schema.builder();
|
|
3783
|
-
schema.inner.meta = {
|
|
3784
|
-
...schema.meta,
|
|
3785
|
-
...schema.inner.meta
|
|
3786
|
-
};
|
|
3787
|
-
}
|
|
3788
|
-
return schema.inner.toJSON();
|
|
3789
|
-
};
|
|
3790
|
-
const schema = new Schema({
|
|
3791
|
-
type: "lazy",
|
|
3792
|
-
builder,
|
|
3793
|
-
inner: { toJSON }
|
|
3794
|
-
});
|
|
3795
|
-
return schema;
|
|
3796
|
-
};
|
|
3797
|
-
Schema.natural = function natural() {
|
|
3798
|
-
return Schema.number().step(1).min(0);
|
|
3799
|
-
};
|
|
3800
|
-
Schema.percent = function percent() {
|
|
3801
|
-
return Schema.number().step(.01).min(0).max(1).role("slider");
|
|
3802
|
-
};
|
|
3803
|
-
Schema.date = function date() {
|
|
3804
|
-
return Schema.union([Schema.is(Date), Schema.transform(Schema.string().role("datetime"), (value, options) => {
|
|
3805
|
-
const date = new Date(value);
|
|
3806
|
-
if (isNaN(+date)) throw new ValidationError(`invalid date "${value}"`, options);
|
|
3807
|
-
return date;
|
|
3808
|
-
}, true)]);
|
|
3809
|
-
};
|
|
3810
|
-
Schema.regExp = function regExp(flag = "") {
|
|
3811
|
-
return Schema.union([Schema.is(RegExp), Schema.transform(Schema.string().role("regexp", { flag }), (value, options) => {
|
|
3812
|
-
try {
|
|
3813
|
-
return new RegExp(value, flag);
|
|
3814
|
-
} catch (e) {
|
|
3815
|
-
throw new ValidationError(e.message, options);
|
|
3816
|
-
}
|
|
3817
|
-
}, true)]);
|
|
3818
|
-
};
|
|
3819
|
-
Schema.arrayBuffer = function arrayBuffer(encoding) {
|
|
3820
|
-
return Schema.union([
|
|
3821
|
-
Schema.is(ArrayBuffer),
|
|
3822
|
-
Schema.is(SharedArrayBuffer),
|
|
3823
|
-
Schema.transform(Schema.any(), (value, options) => {
|
|
3824
|
-
if (Binary.isSource(value)) return Binary.fromSource(value);
|
|
3825
|
-
throw new ValidationError(`expected ArrayBufferSource but got ${value}`, options);
|
|
3826
|
-
}, true),
|
|
3827
|
-
...encoding ? [Schema.transform(Schema.string(), (value, options) => {
|
|
3828
|
-
try {
|
|
3829
|
-
return encoding === "base64" ? Binary.fromBase64(value) : Binary.fromHex(value);
|
|
3830
|
-
} catch (e) {
|
|
3831
|
-
throw new ValidationError(e.message, options);
|
|
3832
|
-
}
|
|
3833
|
-
}, true)] : []
|
|
3834
|
-
]);
|
|
3835
|
-
};
|
|
3836
|
-
Schema.extend("lazy", (data, schema, options, strict) => {
|
|
3837
|
-
if (!schema.inner[kSchema]) {
|
|
3838
|
-
schema.inner = schema.builder();
|
|
3839
|
-
schema.inner.meta = {
|
|
3840
|
-
...schema.meta,
|
|
3841
|
-
...schema.inner.meta
|
|
3842
|
-
};
|
|
3843
|
-
}
|
|
3844
|
-
return Schema.resolve(data, schema.inner, options, strict);
|
|
3845
|
-
});
|
|
3846
|
-
Schema.extend("any", (data) => {
|
|
3847
|
-
return [data];
|
|
3848
|
-
});
|
|
3849
|
-
Schema.extend("never", (data, _, options) => {
|
|
3850
|
-
throw new ValidationError(`expected nullable but got ${data}`, options);
|
|
3851
|
-
});
|
|
3852
|
-
Schema.extend("const", (data, { value }, options) => {
|
|
3853
|
-
if (deepEqual(data, value)) return [value];
|
|
3854
|
-
throw new ValidationError(`expected ${value} but got ${data}`, options);
|
|
3855
|
-
});
|
|
3856
|
-
function checkWithinRange(data, meta, description, options, skipMin = false) {
|
|
3857
|
-
const { max = Infinity, min = -Infinity } = meta;
|
|
3858
|
-
if (data > max) throw new ValidationError(`expected ${description} <= ${max} but got ${data}`, options);
|
|
3859
|
-
if (data < min && !skipMin) throw new ValidationError(`expected ${description} >= ${min} but got ${data}`, options);
|
|
3860
|
-
}
|
|
3861
|
-
Schema.extend("string", (data, { meta }, options) => {
|
|
3862
|
-
if (typeof data !== "string") throw new ValidationError(`expected string but got ${data}`, options);
|
|
3863
|
-
if (meta.pattern) {
|
|
3864
|
-
const regexp = new RegExp(meta.pattern.source, meta.pattern.flags);
|
|
3865
|
-
if (!regexp.test(data)) throw new ValidationError(`expect string to match regexp ${regexp}`, options);
|
|
3866
|
-
}
|
|
3867
|
-
checkWithinRange(data.length, meta, "string length", options);
|
|
3868
|
-
return [data];
|
|
3869
|
-
});
|
|
3870
|
-
function decimalShift(data, digits) {
|
|
3871
|
-
const str = data.toString();
|
|
3872
|
-
if (str.includes("e")) return data * Math.pow(10, digits);
|
|
3873
|
-
const index = str.indexOf(".");
|
|
3874
|
-
if (index === -1) return data * Math.pow(10, digits);
|
|
3875
|
-
const frac = str.slice(index + 1);
|
|
3876
|
-
const integer = str.slice(0, index);
|
|
3877
|
-
if (frac.length <= digits) return +(integer + frac.padEnd(digits, "0"));
|
|
3878
|
-
return +(integer + frac.slice(0, digits) + "." + frac.slice(digits));
|
|
3879
|
-
}
|
|
3880
|
-
function isMultipleOf(data, min, step) {
|
|
3881
|
-
step = Math.abs(step);
|
|
3882
|
-
if (!/^\d+\.\d+$/.test(step.toString())) return (data - min) % step === 0;
|
|
3883
|
-
const index = step.toString().indexOf(".");
|
|
3884
|
-
const digits = step.toString().slice(index + 1).length;
|
|
3885
|
-
return Math.abs(decimalShift(data, digits) - decimalShift(min, digits)) % decimalShift(step, digits) === 0;
|
|
3886
|
-
}
|
|
3887
|
-
Schema.extend("number", (data, { meta }, options) => {
|
|
3888
|
-
if (typeof data !== "number") throw new ValidationError(`expected number but got ${data}`, options);
|
|
3889
|
-
checkWithinRange(data, meta, "number", options);
|
|
3890
|
-
const { step } = meta;
|
|
3891
|
-
if (step && !isMultipleOf(data, meta.min ?? 0, step)) throw new ValidationError(`expected number multiple of ${step} but got ${data}`, options);
|
|
3892
|
-
return [data];
|
|
3893
|
-
});
|
|
3894
|
-
Schema.extend("boolean", (data, _, options) => {
|
|
3895
|
-
if (typeof data === "boolean") return [data];
|
|
3896
|
-
throw new ValidationError(`expected boolean but got ${data}`, options);
|
|
3897
|
-
});
|
|
3898
|
-
Schema.extend("bitset", (data, { bits, meta }, options) => {
|
|
3899
|
-
let value = 0, keys = [];
|
|
3900
|
-
if (typeof data === "number") {
|
|
3901
|
-
value = data;
|
|
3902
|
-
for (const key in bits) if (data & bits[key]) keys.push(key);
|
|
3903
|
-
} else if (Array.isArray(data)) {
|
|
3904
|
-
keys = data;
|
|
3905
|
-
for (const key of keys) {
|
|
3906
|
-
if (typeof key !== "string") throw new ValidationError(`expected string but got ${key}`, options);
|
|
3907
|
-
if (key in bits) value |= bits[key];
|
|
3908
|
-
}
|
|
3909
|
-
} else throw new ValidationError(`expected number or array but got ${data}`, options);
|
|
3910
|
-
if (value === meta.default) return [value];
|
|
3911
|
-
return [value, keys];
|
|
3912
|
-
});
|
|
3913
|
-
Schema.extend("function", (data, _, options) => {
|
|
3914
|
-
if (typeof data === "function") return [data];
|
|
3915
|
-
throw new ValidationError(`expected function but got ${data}`, options);
|
|
3916
|
-
});
|
|
3917
|
-
Schema.extend("is", (data, { constructor }, options) => {
|
|
3918
|
-
if (typeof constructor === "function") {
|
|
3919
|
-
if (data instanceof constructor) return [data];
|
|
3920
|
-
throw new ValidationError(`expected ${constructor.name} but got ${data}`, options);
|
|
3921
|
-
} else {
|
|
3922
|
-
if (isNullable(data)) throw new ValidationError(`expected ${constructor} but got ${data}`, options);
|
|
3923
|
-
let prototype = Object.getPrototypeOf(data);
|
|
3924
|
-
while (prototype) {
|
|
3925
|
-
if (prototype.constructor?.name === constructor) return [data];
|
|
3926
|
-
prototype = Object.getPrototypeOf(prototype);
|
|
3927
|
-
}
|
|
3928
|
-
throw new ValidationError(`expected ${constructor} but got ${data}`, options);
|
|
3929
|
-
}
|
|
3930
|
-
});
|
|
3931
|
-
function property(data, key, schema, options) {
|
|
3932
|
-
try {
|
|
3933
|
-
const [value, adapted] = Schema.resolve(data[key], schema, {
|
|
3934
|
-
...options,
|
|
3935
|
-
path: [...options.path || [], key]
|
|
3936
|
-
});
|
|
3937
|
-
if (adapted !== void 0) data[key] = adapted;
|
|
3938
|
-
return value;
|
|
3939
|
-
} catch (e) {
|
|
3940
|
-
if (!options?.autofix) throw e;
|
|
3941
|
-
delete data[key];
|
|
3942
|
-
return schema.meta.default;
|
|
3943
|
-
}
|
|
3944
|
-
}
|
|
3945
|
-
Schema.extend("array", (data, { inner, meta }, options) => {
|
|
3946
|
-
if (!Array.isArray(data)) throw new ValidationError(`expected array but got ${data}`, options);
|
|
3947
|
-
checkWithinRange(data.length, meta, "array length", options, !isNullable(inner.meta.default));
|
|
3948
|
-
return [data.map((_, index) => property(data, index, inner, options))];
|
|
3949
|
-
});
|
|
3950
|
-
Schema.extend("dict", (data, { inner, sKey }, options, strict) => {
|
|
3951
|
-
if (!isPlainObject(data)) throw new ValidationError(`expected object but got ${data}`, options);
|
|
3952
|
-
const result = {};
|
|
3953
|
-
for (const key in data) {
|
|
3954
|
-
let rKey;
|
|
3955
|
-
try {
|
|
3956
|
-
rKey = Schema.resolve(key, sKey, options)[0];
|
|
3957
|
-
} catch (error) {
|
|
3958
|
-
if (strict) continue;
|
|
3959
|
-
throw error;
|
|
3960
|
-
}
|
|
3961
|
-
result[rKey] = property(data, key, inner, options);
|
|
3962
|
-
data[rKey] = data[key];
|
|
3963
|
-
if (key !== rKey) delete data[key];
|
|
3964
|
-
}
|
|
3965
|
-
return [result];
|
|
3966
|
-
});
|
|
3967
|
-
Schema.extend("tuple", (data, { list }, options, strict) => {
|
|
3968
|
-
if (!Array.isArray(data)) throw new ValidationError(`expected array but got ${data}`, options);
|
|
3969
|
-
const result = list.map((inner, index) => property(data, index, inner, options));
|
|
3970
|
-
if (strict) return [result];
|
|
3971
|
-
result.push(...data.slice(list.length));
|
|
3972
|
-
return [result];
|
|
3973
|
-
});
|
|
3974
|
-
function merge(result, data) {
|
|
3975
|
-
for (const key in data) {
|
|
3976
|
-
if (key in result) continue;
|
|
3977
|
-
result[key] = data[key];
|
|
3978
|
-
}
|
|
3979
|
-
}
|
|
3980
|
-
Schema.extend("object", (data, { dict }, options, strict) => {
|
|
3981
|
-
if (!isPlainObject(data)) throw new ValidationError(`expected object but got ${data}`, options);
|
|
3982
|
-
const result = {};
|
|
3983
|
-
for (const key in dict) {
|
|
3984
|
-
const value = property(data, key, dict[key], options);
|
|
3985
|
-
if (!isNullable(value) || key in data) result[key] = value;
|
|
3986
|
-
}
|
|
3987
|
-
if (!strict) merge(result, data);
|
|
3988
|
-
return [result];
|
|
3989
|
-
});
|
|
3990
|
-
Schema.extend("union", (data, { list, toString }, options, strict) => {
|
|
3991
|
-
const messages = [];
|
|
3992
|
-
for (const inner of list) try {
|
|
3993
|
-
return Schema.resolve(data, inner, options, strict);
|
|
3994
|
-
} catch (error) {
|
|
3995
|
-
messages.push(error);
|
|
3996
|
-
}
|
|
3997
|
-
throw new ValidationError(`expected ${toString()} but got ${JSON.stringify(data)}`, options);
|
|
3998
|
-
});
|
|
3999
|
-
Schema.extend("intersect", (data, { list, toString }, options, strict) => {
|
|
4000
|
-
if (!list.length) return [data];
|
|
4001
|
-
let result;
|
|
4002
|
-
for (const inner of list) {
|
|
4003
|
-
const value = Schema.resolve(data, inner, options, true)[0];
|
|
4004
|
-
if (isNullable(value)) continue;
|
|
4005
|
-
if (isNullable(result)) result = value;
|
|
4006
|
-
else if (typeof result !== typeof value) throw new ValidationError(`expected ${toString()} but got ${JSON.stringify(data)}`, options);
|
|
4007
|
-
else if (typeof value === "object") merge(result ??= {}, value);
|
|
4008
|
-
else if (result !== value) throw new ValidationError(`expected ${toString()} but got ${JSON.stringify(data)}`, options);
|
|
4009
|
-
}
|
|
4010
|
-
if (!strict && isPlainObject(data)) merge(result, data);
|
|
4011
|
-
return [result];
|
|
4012
|
-
});
|
|
4013
|
-
Schema.extend("transform", (data, { inner, callback, preserve }, options) => {
|
|
4014
|
-
const [result, adapted = data] = Schema.resolve(data, inner, options, true);
|
|
4015
|
-
if (preserve) return [callback(result)];
|
|
4016
|
-
else return [callback(result), callback(adapted)];
|
|
4017
|
-
});
|
|
4018
|
-
const formatters = {};
|
|
4019
|
-
function defineMethod(name, keys, format) {
|
|
4020
|
-
formatters[name] = format;
|
|
4021
|
-
Object.assign(Schema, { [name](...args) {
|
|
4022
|
-
const schema = new Schema({ type: name });
|
|
4023
|
-
keys.forEach((key, index) => {
|
|
4024
|
-
switch (key) {
|
|
4025
|
-
case "sKey":
|
|
4026
|
-
schema.sKey = args[index] ?? Schema.string();
|
|
4027
|
-
break;
|
|
4028
|
-
case "inner":
|
|
4029
|
-
schema.inner = Schema.from(args[index]);
|
|
4030
|
-
break;
|
|
4031
|
-
case "list":
|
|
4032
|
-
schema.list = args[index].map(Schema.from);
|
|
4033
|
-
break;
|
|
4034
|
-
case "dict":
|
|
4035
|
-
schema.dict = mapValues(args[index], Schema.from);
|
|
4036
|
-
break;
|
|
4037
|
-
case "bits":
|
|
4038
|
-
schema.bits = {};
|
|
4039
|
-
for (const key in args[index]) {
|
|
4040
|
-
if (typeof args[index][key] !== "number") continue;
|
|
4041
|
-
schema.bits[key] = args[index][key];
|
|
4042
|
-
}
|
|
4043
|
-
break;
|
|
4044
|
-
case "callback": {
|
|
4045
|
-
const callback = schema.callback = args[index];
|
|
4046
|
-
callback["toJSON"] ||= () => callback.toString();
|
|
4047
|
-
break;
|
|
4048
|
-
}
|
|
4049
|
-
case "constructor": {
|
|
4050
|
-
const constructor = schema.constructor = args[index];
|
|
4051
|
-
if (typeof constructor === "function") constructor["toJSON"] ||= () => constructor["name"];
|
|
4052
|
-
break;
|
|
4053
|
-
}
|
|
4054
|
-
default: schema[key] = args[index];
|
|
4055
|
-
}
|
|
4056
|
-
});
|
|
4057
|
-
if (name === "object" || name === "dict") schema.meta.default = {};
|
|
4058
|
-
else if (name === "array" || name === "tuple") schema.meta.default = [];
|
|
4059
|
-
else if (name === "bitset") schema.meta.default = 0;
|
|
4060
|
-
return schema;
|
|
4061
|
-
} });
|
|
4062
|
-
}
|
|
4063
|
-
defineMethod("is", ["constructor"], ({ constructor }) => {
|
|
4064
|
-
if (typeof constructor === "function") return constructor.name;
|
|
4065
|
-
else return constructor;
|
|
4066
|
-
});
|
|
4067
|
-
defineMethod("any", [], () => "any");
|
|
4068
|
-
defineMethod("never", [], () => "never");
|
|
4069
|
-
defineMethod("const", ["value"], ({ value }) => typeof value === "string" ? JSON.stringify(value) : value);
|
|
4070
|
-
defineMethod("string", [], () => "string");
|
|
4071
|
-
defineMethod("number", [], () => "number");
|
|
4072
|
-
defineMethod("boolean", [], () => "boolean");
|
|
4073
|
-
defineMethod("bitset", ["bits"], () => "bitset");
|
|
4074
|
-
defineMethod("function", [], () => "function");
|
|
4075
|
-
defineMethod("array", ["inner"], ({ inner }) => `${inner.toString(true)}[]`);
|
|
4076
|
-
defineMethod("dict", ["inner", "sKey"], ({ inner, sKey }) => `{ [key: ${sKey.toString()}]: ${inner.toString()} }`);
|
|
4077
|
-
defineMethod("tuple", ["list"], ({ list }) => `[${list.map((inner) => inner.toString()).join(", ")}]`);
|
|
4078
|
-
defineMethod("object", ["dict"], ({ dict }) => {
|
|
4079
|
-
if (Object.keys(dict).length === 0) return "{}";
|
|
4080
|
-
return `{ ${Object.entries(dict).map(([key, inner]) => {
|
|
4081
|
-
return `${key}${inner.meta.required ? "" : "?"}: ${inner.toString()}`;
|
|
4082
|
-
}).join(", ")} }`;
|
|
4083
|
-
});
|
|
4084
|
-
defineMethod("union", ["list"], ({ list }, inline) => {
|
|
4085
|
-
const result = list.map(({ toString: format }) => format()).join(" | ");
|
|
4086
|
-
return inline ? `(${result})` : result;
|
|
4087
|
-
});
|
|
4088
|
-
defineMethod("intersect", ["list"], ({ list }) => {
|
|
4089
|
-
return `${list.map((inner) => inner.toString(true)).join(" & ")}`;
|
|
4090
|
-
});
|
|
4091
|
-
defineMethod("transform", [
|
|
4092
|
-
"inner",
|
|
4093
|
-
"callback",
|
|
4094
|
-
"preserve"
|
|
4095
|
-
], ({ inner }, isInner) => inner.toString(isInner));
|
|
4096
|
-
//#endregion
|
|
4097
|
-
//#region ../../node_modules/.pnpm/@deepseek-ai+dsh-timeout@0.1.5-rc.1_@deepseek-ai+cordis@4.0.2/node_modules/@deepseek-ai/dsh-timeout/lib/index.js
|
|
3322
|
+
//#region ../../node_modules/.pnpm/@deepseek-ai+dsh-timeout@0.1.7-rc.1_@deepseek-ai+cordis@4.0.4/node_modules/@deepseek-ai/dsh-timeout/lib/index.js
|
|
4098
3323
|
/** Largest delay Node schedules without clamping it to one millisecond. */
|
|
4099
3324
|
const MAX_TIMER_DELAY_MS = 2147483647;
|
|
4100
3325
|
//#endregion
|
|
4101
|
-
//#region ../../node_modules/.pnpm/@deepseek-ai+dsh-llm@0.1.
|
|
3326
|
+
//#region ../../node_modules/.pnpm/@deepseek-ai+dsh-llm@0.1.7-rc.1_@deepseek-ai+cordis@4.0.4/node_modules/@deepseek-ai/dsh-llm/lib/index.js
|
|
4102
3327
|
/**
|
|
4103
3328
|
* Detach and deep-freeze a message whose identity already exists.
|
|
4104
3329
|
* @param message - complete message, including its stable identity.
|
|
@@ -4113,10 +3338,10 @@ function freezeMessage(message) {
|
|
|
4113
3338
|
* @returns an immutable message with a fresh stable identity.
|
|
4114
3339
|
*/
|
|
4115
3340
|
function createMessage(input) {
|
|
4116
|
-
return
|
|
3341
|
+
return deepFreeze(structuredClone({
|
|
4117
3342
|
...input,
|
|
4118
3343
|
id: brandString(randomUUID())
|
|
4119
|
-
});
|
|
3344
|
+
}));
|
|
4120
3345
|
}
|
|
4121
3346
|
/**
|
|
4122
3347
|
* Create one identified user-role message and freeze it before publication.
|
|
@@ -4181,22 +3406,22 @@ const DEFAULT_RETRYABLE_CODES = Object.freeze([
|
|
|
4181
3406
|
"TIMEOUT",
|
|
4182
3407
|
"TRANSPORT"
|
|
4183
3408
|
]);
|
|
4184
|
-
const backoffSchema =
|
|
4185
|
-
initialDelayMs:
|
|
4186
|
-
maxDelayMs:
|
|
4187
|
-
jitterRatio:
|
|
3409
|
+
const backoffSchema = z.object({
|
|
3410
|
+
initialDelayMs: z.number().max(MAX_TIMER_DELAY_MS).default(DEFAULT_INITIAL_DELAY_MS),
|
|
3411
|
+
maxDelayMs: z.number().max(MAX_TIMER_DELAY_MS).default(DEFAULT_MAX_DELAY_MS),
|
|
3412
|
+
jitterRatio: z.number().min(0).max(1).default(DEFAULT_JITTER_RATIO)
|
|
4188
3413
|
});
|
|
4189
|
-
const normalPolicySchema =
|
|
4190
|
-
mode:
|
|
4191
|
-
maxRetries:
|
|
4192
|
-
retryableCodes:
|
|
3414
|
+
const normalPolicySchema = z.object({
|
|
3415
|
+
mode: z.const("normal").required(),
|
|
3416
|
+
maxRetries: z.number().step(1).min(0).max(Number.MAX_SAFE_INTEGER).default(DEFAULT_MAX_RETRIES),
|
|
3417
|
+
retryableCodes: z.array(z.string()).default([...DEFAULT_RETRYABLE_CODES]),
|
|
4193
3418
|
backoff: backoffSchema
|
|
4194
3419
|
});
|
|
4195
|
-
const alwaysPolicySchema =
|
|
4196
|
-
mode:
|
|
3420
|
+
const alwaysPolicySchema = z.object({
|
|
3421
|
+
mode: z.const("always").required(),
|
|
4197
3422
|
backoff: backoffSchema
|
|
4198
3423
|
});
|
|
4199
|
-
|
|
3424
|
+
z.union([normalPolicySchema, alwaysPolicySchema]);
|
|
4200
3425
|
const NORMAL_POLICY_KEYS = /* @__PURE__ */ new Set([
|
|
4201
3426
|
"mode",
|
|
4202
3427
|
"maxRetries",
|
|
@@ -4340,13 +3565,15 @@ function failureSnapshot(value) {
|
|
|
4340
3565
|
const status = candidate.status;
|
|
4341
3566
|
const providerRetryAfterMs = candidate.providerRetryAfterMs;
|
|
4342
3567
|
const requestId = candidate.requestId;
|
|
4343
|
-
|
|
3568
|
+
const offloadImages = candidate.offloadImages;
|
|
3569
|
+
if (typeof message !== "string" || message.length === 0 || typeof code !== "string" || code.length === 0 || status !== void 0 && (!Number.isInteger(status) || status < 100 || status > 599) || providerRetryAfterMs !== void 0 && (!Number.isFinite(providerRetryAfterMs) || providerRetryAfterMs <= 0) || requestId !== void 0 && (typeof requestId !== "string" || requestId.length === 0) || offloadImages !== void 0 && (!Number.isSafeInteger(offloadImages) || offloadImages <= 0)) return void 0;
|
|
4344
3570
|
return Object.freeze({
|
|
4345
3571
|
message,
|
|
4346
3572
|
code,
|
|
4347
3573
|
...status === void 0 ? {} : { status },
|
|
4348
3574
|
...providerRetryAfterMs === void 0 ? {} : { providerRetryAfterMs },
|
|
4349
|
-
...requestId === void 0 ? {} : { requestId }
|
|
3575
|
+
...requestId === void 0 ? {} : { requestId },
|
|
3576
|
+
...offloadImages === void 0 ? {} : { offloadImages }
|
|
4350
3577
|
});
|
|
4351
3578
|
} catch (_sdkFailureGetter) {
|
|
4352
3579
|
return;
|
|
@@ -4376,25 +3603,23 @@ function textOnlyImageText(ref) {
|
|
|
4376
3603
|
return `[image omitted because this model accepts text only; attachment sha256:${String(ref.attachmentId).slice(7, 15)}]`;
|
|
4377
3604
|
}
|
|
4378
3605
|
/**
|
|
4379
|
-
* True when typed model content contains an image block
|
|
4380
|
-
*
|
|
4381
|
-
*
|
|
4382
|
-
* survey), so a consumer cannot silently diverge on nesting depth.
|
|
3606
|
+
* True when typed model content contains an image block. This is the one image
|
|
3607
|
+
* walk shared by every image policy (capability gating, text-only
|
|
3608
|
+
* serialization, compaction survey), so a consumer cannot silently diverge.
|
|
4383
3609
|
* @param content - typed model content blocks.
|
|
4384
|
-
* @returns whether any
|
|
3610
|
+
* @returns whether any block is an image.
|
|
4385
3611
|
*/
|
|
4386
3612
|
function contentHasImage(content) {
|
|
4387
|
-
return content.some((block) => block.type === "image"
|
|
3613
|
+
return content.some((block) => block.type === "image");
|
|
4388
3614
|
}
|
|
4389
3615
|
/**
|
|
4390
|
-
* True when typed model content contains a file block
|
|
4391
|
-
* tool-result content on the same recursion every file policy shares.
|
|
3616
|
+
* True when typed model content contains a file block.
|
|
4392
3617
|
* Reads current content on every call without retaining scan results.
|
|
4393
3618
|
* @param content - typed model content blocks.
|
|
4394
|
-
* @returns whether any
|
|
3619
|
+
* @returns whether any block is a file.
|
|
4395
3620
|
*/
|
|
4396
3621
|
function contentHasFile(content) {
|
|
4397
|
-
for (const block of content) if (block.type === "file"
|
|
3622
|
+
for (const block of content) if (block.type === "file") return true;
|
|
4398
3623
|
return false;
|
|
4399
3624
|
}
|
|
4400
3625
|
/**
|
|
@@ -4411,7 +3636,7 @@ function fileHandleText(ref, readonlyPath) {
|
|
|
4411
3636
|
if (readonlyPath === void 0) return `[${identity} was uploaded, but the current execution environment cannot access a readable path. Report that limitation if its contents are needed; do not claim to have read it.]`;
|
|
4412
3637
|
return `[${identity}: verbatim read-only copy saved at ${quoted(readonlyPath)}. Read that path with your file tools when its contents are needed; copy it to a writable location before modifying it. When delegating file work, include this saved path in the delegation prompt; only subagents sharing this execution environment can read it.]`;
|
|
4413
3638
|
}
|
|
4414
|
-
/** Replace every file occurrence
|
|
3639
|
+
/** Replace every file occurrence with handle text. */
|
|
4415
3640
|
function replaceFilesWithHandles(blocks, resolvePath) {
|
|
4416
3641
|
let next;
|
|
4417
3642
|
for (const [index, block] of blocks.entries()) {
|
|
@@ -4423,29 +3648,10 @@ function replaceFilesWithHandles(blocks, resolvePath) {
|
|
|
4423
3648
|
});
|
|
4424
3649
|
continue;
|
|
4425
3650
|
}
|
|
4426
|
-
if (block.type === "tool-result") {
|
|
4427
|
-
const content = replaceFilesWithHandles(block.content, resolvePath);
|
|
4428
|
-
if (content !== block.content) {
|
|
4429
|
-
next ??= blocks.slice(0, index);
|
|
4430
|
-
next.push({
|
|
4431
|
-
...block,
|
|
4432
|
-
content
|
|
4433
|
-
});
|
|
4434
|
-
continue;
|
|
4435
|
-
}
|
|
4436
|
-
}
|
|
4437
3651
|
next?.push(block);
|
|
4438
3652
|
}
|
|
4439
3653
|
return next ?? blocks;
|
|
4440
3654
|
}
|
|
4441
|
-
/**
|
|
4442
|
-
* Project durable file history into deterministic handle text for every model
|
|
4443
|
-
* route. Unlike images, no provider receives file blocks natively, so this
|
|
4444
|
-
* projection is unconditional in request assembly.
|
|
4445
|
-
* @param messages - complete request history.
|
|
4446
|
-
* @param resolvePath - resolve one reference's current execution-world read path.
|
|
4447
|
-
* @returns the original list without files, otherwise shallow message copies with handle text.
|
|
4448
|
-
*/
|
|
4449
3655
|
function projectFilesToText(messages, resolvePath) {
|
|
4450
3656
|
if (!messages.some((message) => contentHasFile(message.content))) return messages;
|
|
4451
3657
|
return messages.map((message) => {
|
|
@@ -4456,7 +3662,7 @@ function projectFilesToText(messages, resolvePath) {
|
|
|
4456
3662
|
};
|
|
4457
3663
|
});
|
|
4458
3664
|
}
|
|
4459
|
-
/** Replace every image occurrence
|
|
3665
|
+
/** Replace every image occurrence for a text-only model. */
|
|
4460
3666
|
function replaceImagesForTextModel(blocks) {
|
|
4461
3667
|
let next;
|
|
4462
3668
|
for (const [index, block] of blocks.entries()) {
|
|
@@ -4468,26 +3674,10 @@ function replaceImagesForTextModel(blocks) {
|
|
|
4468
3674
|
});
|
|
4469
3675
|
continue;
|
|
4470
3676
|
}
|
|
4471
|
-
if (block.type === "tool-result") {
|
|
4472
|
-
const content = replaceImagesForTextModel(block.content);
|
|
4473
|
-
if (content !== block.content) {
|
|
4474
|
-
next ??= blocks.slice(0, index);
|
|
4475
|
-
next.push({
|
|
4476
|
-
...block,
|
|
4477
|
-
content
|
|
4478
|
-
});
|
|
4479
|
-
continue;
|
|
4480
|
-
}
|
|
4481
|
-
}
|
|
4482
3677
|
next?.push(block);
|
|
4483
3678
|
}
|
|
4484
3679
|
return next ?? blocks;
|
|
4485
3680
|
}
|
|
4486
|
-
/**
|
|
4487
|
-
* Project durable image history into deterministic text for an exact text-only model.
|
|
4488
|
-
* @param messages - complete request history.
|
|
4489
|
-
* @returns the original list without images, otherwise shallow message copies with stable placeholders.
|
|
4490
|
-
*/
|
|
4491
3681
|
function projectImagesForTextModel(messages) {
|
|
4492
3682
|
if (!messages.some((message) => contentHasImage(message.content))) return messages;
|
|
4493
3683
|
return messages.map((message) => {
|
|
@@ -4577,7 +3767,8 @@ var LlmError = class extends HarnessError {
|
|
|
4577
3767
|
code,
|
|
4578
3768
|
...options?.status === void 0 ? {} : { status: options.status },
|
|
4579
3769
|
...options?.providerRetryAfterMs === void 0 ? {} : { providerRetryAfterMs: options.providerRetryAfterMs },
|
|
4580
|
-
...options?.requestId === void 0 ? {} : { requestId: options.requestId }
|
|
3770
|
+
...options?.requestId === void 0 ? {} : { requestId: options.requestId },
|
|
3771
|
+
...options?.offloadImages === void 0 ? {} : { offloadImages: options.offloadImages }
|
|
4581
3772
|
});
|
|
4582
3773
|
}
|
|
4583
3774
|
};
|
|
@@ -4845,7 +4036,8 @@ var LlmError = class extends HarnessError {
|
|
|
4845
4036
|
id: model.id,
|
|
4846
4037
|
...model.name === void 0 ? {} : { name: model.name },
|
|
4847
4038
|
...model.contextWindow === void 0 ? {} : { contextWindow: model.contextWindow },
|
|
4848
|
-
...model.maxTokens === void 0 ? {} : { maxTokens: model.maxTokens }
|
|
4039
|
+
...model.maxTokens === void 0 ? {} : { maxTokens: model.maxTokens },
|
|
4040
|
+
...model.inputModalities === void 0 ? {} : { inputModalities: [...model.inputModalities] }
|
|
4849
4041
|
});
|
|
4850
4042
|
}
|
|
4851
4043
|
return models;
|
|
@@ -5074,8 +4266,9 @@ var LlmError = class extends HarnessError {
|
|
|
5074
4266
|
/** Remove replay state whose historical route is owned by another adapter. */
|
|
5075
4267
|
forAdapter(options, adapter) {
|
|
5076
4268
|
const messages = options.messages.map((message) => {
|
|
4269
|
+
if (message.role !== "assistant") return message;
|
|
5077
4270
|
const source = message.source;
|
|
5078
|
-
if (
|
|
4271
|
+
if (source.replayState === void 0) return message;
|
|
5079
4272
|
if (this.adapters.get(source.provider)?.adapter === adapter) return message;
|
|
5080
4273
|
return freezeMessage({
|
|
5081
4274
|
...message,
|
|
@@ -5731,19 +4924,35 @@ const inject = [
|
|
|
5731
4924
|
/** Model-facing announcement: plugin presence, capabilities, and limits. */
|
|
5732
4925
|
const TASK_BOARD_GUIDANCE = "本机已安装 dsh-task-board 插件(DSH Web GUI 的任务看板):侧边栏「任务看板」入口;在 dsh-web 插件全家桶仓库(packages/dsh-task-board)统一维护,经聚合包 web-ui-all 一键安装。能力:多列看板管理任务;Host 权威账本;关闭浏览器后仍由 Host 执行和结算;任务可钉住工作区、agent 预设和权限;支持 Host 本地时区的 5 段 cron,错过的触发点不补跑;可选且默认关闭的空闲系统睡眠保护允许屏幕熄灭,但不承诺拦截合盖、手动睡眠、休眠、关机或唤醒已睡眠机器。执行消耗 API 额度。用户提到「任务看板 / 看板 / 定时任务」时即指本插件,请据此协作。若你同时用 todo_write 维护会话顶部的可见计划列表,最终回复前必须再次调用 todo_write 收尾:没有剩余工作时不要保留 in_progress,已完成的最后一步要标为 completed。";
|
|
5733
4926
|
/**
|
|
5734
|
-
*
|
|
5735
|
-
*
|
|
5736
|
-
*
|
|
4927
|
+
* The schema is left to inference rather than annotated with `z<Config>`: a
|
|
4928
|
+
* volatile field's parsed output is a `Volatile` reference while its accepted
|
|
4929
|
+
* input stays the plain value, so the two sides no longer share one shape and
|
|
4930
|
+
* the annotation would reject the schema the Host must be given.
|
|
5737
4931
|
*/
|
|
5738
|
-
const TASK_BOARD_SETTINGS_NAMESPACE = "task-board";
|
|
5739
4932
|
const Config = z.object({
|
|
5740
|
-
announceToAgent: z.boolean().default(false),
|
|
5741
|
-
enabled: z.boolean().default(true),
|
|
5742
|
-
preventIdleSleep: z.boolean().default(false),
|
|
4933
|
+
announceToAgent: z.boolean().default(false).volatile(),
|
|
4934
|
+
enabled: z.boolean().default(true).volatile(),
|
|
4935
|
+
preventIdleSleep: z.boolean().default(false).volatile(),
|
|
5743
4936
|
trustedProxyHosts: z.array(z.string()).default([]),
|
|
5744
4937
|
proxyTokenEnv: z.string().min(1).default(DEFAULT_PROXY_TOKEN_ENV),
|
|
5745
4938
|
sessionDefaultPermission: z.union(TASK_PERMISSIONS).default(DEFAULT_SESSION_PERMISSION)
|
|
5746
4939
|
});
|
|
4940
|
+
/**
|
|
4941
|
+
* Read one config field's current value.
|
|
4942
|
+
*
|
|
4943
|
+
* The Loader hands schema-volatile fields as stable references it commits in
|
|
4944
|
+
* place, so a live value must be read at use time rather than captured when
|
|
4945
|
+
* the plugin activates; a plain value (a programmatic mount, or a field the
|
|
4946
|
+
* schema does not mark volatile) is returned as it stands.
|
|
4947
|
+
* @param field - the config field as the Loader handed it.
|
|
4948
|
+
* @param fallback - value to use when the field is absent.
|
|
4949
|
+
* @returns the effective field value.
|
|
4950
|
+
*/
|
|
4951
|
+
function readConfigField(field, fallback) {
|
|
4952
|
+
if (field === void 0) return fallback;
|
|
4953
|
+
if (typeof field === "object" && field !== null && typeof field.get === "function") return field.get();
|
|
4954
|
+
return field;
|
|
4955
|
+
}
|
|
5747
4956
|
/** Resolve proxy access without ever placing the token value in plugin config. */
|
|
5748
4957
|
function resolveProxyAccess(config, env = process.env) {
|
|
5749
4958
|
const trustedProxyHosts = config?.trustedProxyHosts ?? [];
|
|
@@ -5775,15 +4984,26 @@ function resolveLlmRuntime(ctx) {
|
|
|
5775
4984
|
}
|
|
5776
4985
|
}
|
|
5777
4986
|
/**
|
|
5778
|
-
*
|
|
5779
|
-
*
|
|
5780
|
-
*
|
|
5781
|
-
*
|
|
4987
|
+
* Activate the board's host half: ledger, routes, cron scheduler, power
|
|
4988
|
+
* inhibitor, and the model-facing announcement.
|
|
4989
|
+
*
|
|
4990
|
+
* The effective settings are the config the Host hands this row. The three
|
|
4991
|
+
* fields the browser card edits are schema-volatile, so the Loader commits an
|
|
4992
|
+
* edit into the running fiber's references instead of remounting the row —
|
|
4993
|
+
* `sync` therefore reads them at use time and follows
|
|
4994
|
+
* `loader/volatile-update`, the event the Loader emits once it has committed
|
|
4995
|
+
* them. Every other field reloads the row, which re-runs this activation.
|
|
5782
4996
|
* @param ctx - the plugin context (systemPrompt injected).
|
|
5783
4997
|
* @param config - resolved plugin config (schema defaults applied by the loader).
|
|
5784
4998
|
*/
|
|
5785
4999
|
const apply = mountOnce("@linxin666/dsh-client-ui-task-board", applyImpl);
|
|
5786
5000
|
function applyImpl(ctx, config) {
|
|
5001
|
+
/** Current master switch (the browser half reads the same field from its own form). */
|
|
5002
|
+
const enabled = () => readConfigField(config?.enabled, true);
|
|
5003
|
+
/** Current announcement flag. */
|
|
5004
|
+
const announceToAgent = () => readConfigField(config?.announceToAgent, DEFAULT_ANNOUNCE);
|
|
5005
|
+
/** Current idle-sleep protection flag. */
|
|
5006
|
+
const preventIdleSleep = () => readConfigField(config?.preventIdleSleep, false);
|
|
5787
5007
|
const host = new TaskBoardHostService(ctx.typertGateway, {
|
|
5788
5008
|
workspaceRegistry: ctx.workspaceRegistry,
|
|
5789
5009
|
sessionDefaultPermission: config?.sessionDefaultPermission ?? "read-only",
|
|
@@ -5793,7 +5013,7 @@ function applyImpl(ctx, config) {
|
|
|
5793
5013
|
return (await ctx.commands.execute(agent, line, [], signal))?.result;
|
|
5794
5014
|
} }
|
|
5795
5015
|
});
|
|
5796
|
-
host.setConfiguration(
|
|
5016
|
+
host.setConfiguration(enabled(), preventIdleSleep());
|
|
5797
5017
|
host.start();
|
|
5798
5018
|
ctx.effect(() => {
|
|
5799
5019
|
const disposers = [];
|
|
@@ -5814,41 +5034,32 @@ function applyImpl(ctx, config) {
|
|
|
5814
5034
|
host.dispose();
|
|
5815
5035
|
};
|
|
5816
5036
|
}, "task-board: host ledger, scheduler, and routes");
|
|
5817
|
-
let current = () => config ?? {};
|
|
5818
5037
|
let disposeSection;
|
|
5038
|
+
let applied;
|
|
5819
5039
|
const sync = () => {
|
|
5040
|
+
const next = {
|
|
5041
|
+
enabled: enabled(),
|
|
5042
|
+
announceToAgent: announceToAgent(),
|
|
5043
|
+
preventIdleSleep: preventIdleSleep()
|
|
5044
|
+
};
|
|
5045
|
+
if (applied !== void 0 && applied.enabled === next.enabled && applied.announceToAgent === next.announceToAgent && applied.preventIdleSleep === next.preventIdleSleep) return;
|
|
5046
|
+
applied = next;
|
|
5047
|
+
host.setConfiguration(next.enabled, next.preventIdleSleep);
|
|
5820
5048
|
if (disposeSection !== void 0) {
|
|
5821
5049
|
disposeSection();
|
|
5822
5050
|
disposeSection = void 0;
|
|
5823
5051
|
}
|
|
5824
|
-
|
|
5825
|
-
host.setConfiguration(active, current().preventIdleSleep ?? false);
|
|
5826
|
-
if (!active) return;
|
|
5827
|
-
if ((current().announceToAgent ?? DEFAULT_ANNOUNCE) === false) return;
|
|
5052
|
+
if (!next.enabled || !next.announceToAgent) return;
|
|
5828
5053
|
disposeSection = ctx.systemPrompt.section({
|
|
5829
5054
|
name: "plugin:task-board",
|
|
5830
5055
|
order: SECTION_ORDER,
|
|
5831
5056
|
text: TASK_BOARD_GUIDANCE
|
|
5832
5057
|
});
|
|
5833
5058
|
};
|
|
5834
|
-
ctx.
|
|
5835
|
-
|
|
5836
|
-
if (typeof settingsCtx.settings?.installSection === "function") settingsCtx.settings.installSection(ctx, TASK_BOARD_SETTINGS_NAMESPACE, Config, config ?? {}, {
|
|
5837
|
-
setSource: (source) => {
|
|
5838
|
-
current = source;
|
|
5839
|
-
},
|
|
5840
|
-
onChange: sync
|
|
5841
|
-
});
|
|
5842
|
-
else if (typeof settingsCtx.settings?.register === "function") {
|
|
5843
|
-
const scope = settingsCtx.settings.register(TASK_BOARD_SETTINGS_NAMESPACE, Config, { base: config ?? {} });
|
|
5844
|
-
current = () => scope?.get?.() ?? config ?? {};
|
|
5845
|
-
scope?.watch?.(() => {
|
|
5846
|
-
sync();
|
|
5847
|
-
});
|
|
5848
|
-
}
|
|
5849
|
-
} catch {}
|
|
5059
|
+
ctx.on("loader/volatile-update", () => {
|
|
5060
|
+
sync();
|
|
5850
5061
|
});
|
|
5851
5062
|
sync();
|
|
5852
5063
|
}
|
|
5853
5064
|
//#endregion
|
|
5854
|
-
export { Config, DEFAULT_PROXY_TOKEN_ENV, TASK_BOARD_GUIDANCE,
|
|
5065
|
+
export { Config, DEFAULT_PROXY_TOKEN_ENV, TASK_BOARD_GUIDANCE, apply, inject, readConfigField, resolveLlmRuntime, resolveProxyAccess };
|