@michengai/dsh-automation 0.1.64 → 1.0.0
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 +20 -1
- package/CHANGELOG.zh-CN.md +20 -1
- package/README.md +2 -2
- package/README.zh-CN.md +2 -2
- package/lib/client.js +61 -331
- package/lib/client.js.map +3 -3
- package/lib/index.js +163 -5
- package/lib/types/client/contracts.d.ts +0 -2
- package/lib/types/client/host-icon-resolve.d.ts +1 -1
- package/lib/types/client/host-icons.d.ts +5 -2
- package/lib/types/client/icons.d.ts +2 -0
- package/lib/types/client/locales.d.ts +11 -1
- package/lib/types/client/menu-select.d.ts +30 -0
- package/lib/types/client/native-group-actions.d.ts +2 -0
- package/lib/types/client/native-session-menu.d.ts +2 -2
- package/lib/types/client/plugin-ui.d.ts +9 -0
- package/lib/types/client/schedule-rail-model.d.ts +2 -6
- package/lib/types/client/sort-menu.d.ts +1 -2
- package/lib/types/client/workspace-create.d.ts +17 -34
- package/lib/types/executor.d.ts +1 -1
- package/lib/types/plugin-updater.d.ts +3 -0
- package/package.json +40 -41
- package/lib/types/client/antd-locale.d.ts +0 -2
- package/lib/types/client/antd-ui.d.ts +0 -42
- package/lib/types/client/host-theme.d.ts +0 -4
package/lib/index.js
CHANGED
|
@@ -21746,8 +21746,8 @@ async function executeAutomationRun(ctx, definition, run, config2) {
|
|
|
21746
21746
|
setup: async (agentCtx, createdAgent) => {
|
|
21747
21747
|
await ctx.agentPresets.mount(agentCtx, target.agentPreset);
|
|
21748
21748
|
installModelSelection(agentCtx, { current: selection, assembled: void 0 });
|
|
21749
|
-
|
|
21750
|
-
|
|
21749
|
+
if (createdAgent === void 0) throw new Error("automation setup has no scoped Agent");
|
|
21750
|
+
const agent = createdAgent;
|
|
21751
21751
|
applyUnattendedPermission(ctx.permissionPresets, agent.session, target.permissionPreset);
|
|
21752
21752
|
}
|
|
21753
21753
|
}));
|
|
@@ -23364,7 +23364,7 @@ function registerAutomationTools(service, agent) {
|
|
|
23364
23364
|
// src/plugin-updater.ts
|
|
23365
23365
|
import { spawn } from "node:child_process";
|
|
23366
23366
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
|
|
23367
|
-
import { readFile } from "node:fs/promises";
|
|
23367
|
+
import { readFile, rename, unlink, writeFile } from "node:fs/promises";
|
|
23368
23368
|
import { homedir as homedir2 } from "node:os";
|
|
23369
23369
|
import { basename, delimiter, dirname, isAbsolute, resolve } from "node:path";
|
|
23370
23370
|
import { fileURLToPath } from "node:url";
|
|
@@ -23572,6 +23572,157 @@ async function status(options, target) {
|
|
|
23572
23572
|
canAutoUpdate: target.canAutoUpdate
|
|
23573
23573
|
};
|
|
23574
23574
|
}
|
|
23575
|
+
function releaseAgeSpec(packageSpec) {
|
|
23576
|
+
const at = packageSpec.lastIndexOf("@");
|
|
23577
|
+
if (at <= 0) return void 0;
|
|
23578
|
+
const name2 = packageSpec.slice(0, at);
|
|
23579
|
+
const version2 = packageSpec.slice(at + 1);
|
|
23580
|
+
if (!isPackageName(name2) || !isReleaseVersion(version2)) return void 0;
|
|
23581
|
+
return { name: name2, version: version2 };
|
|
23582
|
+
}
|
|
23583
|
+
function isPackageName(name2) {
|
|
23584
|
+
return /^@[^/\s]+\/[^/\s]+$/.test(name2) || /^[^@/\s]+$/.test(name2);
|
|
23585
|
+
}
|
|
23586
|
+
function isReleaseVersion(version2) {
|
|
23587
|
+
return /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(version2);
|
|
23588
|
+
}
|
|
23589
|
+
function unreadableReleaseAgeExclude() {
|
|
23590
|
+
return new Error("\u53D1\u5E03\u5E74\u9F84\u767D\u540D\u5355\u65E0\u6CD5\u5B89\u5168\u5408\u5E76\uFF0C\u5DF2\u505C\u6B62\u66F4\u65B0\uFF0C\u907F\u514D\u8FD9\u4E2A profile \u4E4B\u540E\u65E0\u6CD5\u5B89\u88C5\u63D2\u4EF6\u3002");
|
|
23591
|
+
}
|
|
23592
|
+
function excludeEntry(indent, name2, versions, comment = "") {
|
|
23593
|
+
const entry = `${indent}- '${name2}@${versions.join(" || ")}'`;
|
|
23594
|
+
return comment === "" ? entry : `${entry} ${comment}`;
|
|
23595
|
+
}
|
|
23596
|
+
function parseExcludeLine(line) {
|
|
23597
|
+
const dash = line.indexOf("-");
|
|
23598
|
+
if (!/^\s+-/.test(line) || dash < 0) return void 0;
|
|
23599
|
+
const indent = line.slice(0, dash);
|
|
23600
|
+
const raw = line.slice(dash + 1).trim();
|
|
23601
|
+
let body = raw;
|
|
23602
|
+
let comment = "";
|
|
23603
|
+
if (raw.startsWith("'") || raw.startsWith('"')) {
|
|
23604
|
+
const quote = raw[0] ?? "'";
|
|
23605
|
+
const end = raw.lastIndexOf(quote);
|
|
23606
|
+
if (end <= 0) return void 0;
|
|
23607
|
+
const rest = raw.slice(end + 1).trim();
|
|
23608
|
+
if (rest !== "" && !rest.startsWith("#")) return void 0;
|
|
23609
|
+
if (rest.startsWith("#")) comment = rest;
|
|
23610
|
+
body = raw.slice(1, end);
|
|
23611
|
+
} else if (raw.includes("#")) return void 0;
|
|
23612
|
+
if (body.includes("#")) return void 0;
|
|
23613
|
+
const versionAt = body.lastIndexOf("@");
|
|
23614
|
+
if (versionAt > 0) {
|
|
23615
|
+
const name2 = body.slice(0, versionAt);
|
|
23616
|
+
const versions = body.slice(versionAt + 1).split(" || ");
|
|
23617
|
+
if (isPackageName(name2) && versions.every(isReleaseVersion)) return { indent, name: name2, versions, comment };
|
|
23618
|
+
return void 0;
|
|
23619
|
+
}
|
|
23620
|
+
if (isPackageName(body) || /^@[^/\s]+\/\*$/.test(body)) return { indent, name: body, versions: void 0, comment };
|
|
23621
|
+
return void 0;
|
|
23622
|
+
}
|
|
23623
|
+
function mergeMinimumReleaseAgeExclude(yaml, packageSpec) {
|
|
23624
|
+
const spec = releaseAgeSpec(packageSpec);
|
|
23625
|
+
if (spec === void 0) throw new Error("\u66F4\u65B0\u7248\u672C\u65E0\u6CD5\u5199\u5165\u53D1\u5E03\u5E74\u9F84\u767D\u540D\u5355\uFF0C\u5DF2\u505C\u6B62\u66F4\u65B0\u3002");
|
|
23626
|
+
const eol = yaml.includes("\r\n") ? "\r\n" : "\n";
|
|
23627
|
+
const text = yaml.replaceAll("\r\n", "\n");
|
|
23628
|
+
const next = mergeReleaseAgeText(text, spec.name, spec.version);
|
|
23629
|
+
return eol === "\r\n" ? next.replaceAll("\n", "\r\n") : next;
|
|
23630
|
+
}
|
|
23631
|
+
function mergeReleaseAgeText(yaml, name2, version2) {
|
|
23632
|
+
if (/minimumReleaseAgeExclude:[^\n\r]/.test(yaml)) throw unreadableReleaseAgeExclude();
|
|
23633
|
+
const marker = /(?:^|\n)minimumReleaseAgeExclude:[ \t]*(?:\n|$)/.exec(yaml);
|
|
23634
|
+
if (marker === null) {
|
|
23635
|
+
const base = yaml.endsWith("\n") || yaml.length === 0 ? yaml : `${yaml}
|
|
23636
|
+
`;
|
|
23637
|
+
const gap = base.length === 0 || base.endsWith("\n\n") ? "" : "\n";
|
|
23638
|
+
return `${base}${gap}minimumReleaseAgeExclude:
|
|
23639
|
+
- '${name2}@${version2}'
|
|
23640
|
+
`;
|
|
23641
|
+
}
|
|
23642
|
+
const keyAt = (marker.index ?? 0) + (marker[0].startsWith("\n") ? 1 : 0);
|
|
23643
|
+
let cursor = keyAt + "minimumReleaseAgeExclude:".length;
|
|
23644
|
+
while (cursor < yaml.length && (yaml[cursor] === " " || yaml[cursor] === " ")) cursor += 1;
|
|
23645
|
+
if (yaml[cursor] !== "\n") throw unreadableReleaseAgeExclude();
|
|
23646
|
+
const bodyStart = cursor + 1;
|
|
23647
|
+
const lines = [];
|
|
23648
|
+
let bodyEnd = bodyStart;
|
|
23649
|
+
while (bodyEnd <= yaml.length) {
|
|
23650
|
+
const nextNl = yaml.indexOf("\n", bodyEnd);
|
|
23651
|
+
const lineEnd = nextNl === -1 ? yaml.length : nextNl;
|
|
23652
|
+
const line = yaml.slice(bodyEnd, lineEnd);
|
|
23653
|
+
const ended = line.trim() !== "" && !/^[ \t]*#/.test(line) && !/^[ \t]/.test(line);
|
|
23654
|
+
if (ended) break;
|
|
23655
|
+
if (line.trim() !== "" && !/^[ \t]*#/.test(line) && !/^[ \t]+-/.test(line)) throw unreadableReleaseAgeExclude();
|
|
23656
|
+
lines.push(line);
|
|
23657
|
+
if (nextNl === -1) {
|
|
23658
|
+
bodyEnd = yaml.length;
|
|
23659
|
+
break;
|
|
23660
|
+
}
|
|
23661
|
+
bodyEnd = nextNl + 1;
|
|
23662
|
+
if (bodyEnd === yaml.length) break;
|
|
23663
|
+
}
|
|
23664
|
+
let ours;
|
|
23665
|
+
const rewritten = lines.map((line, index) => {
|
|
23666
|
+
if (line.trim() === "" || /^[ \t]*#/.test(line)) return line;
|
|
23667
|
+
const parsed = parseExcludeLine(line);
|
|
23668
|
+
if (parsed === void 0) throw unreadableReleaseAgeExclude();
|
|
23669
|
+
if (parsed.name !== name2) return line;
|
|
23670
|
+
if (ours === void 0) {
|
|
23671
|
+
ours = { index, indent: parsed.indent, versions: parsed.versions, comment: parsed.comment };
|
|
23672
|
+
return line;
|
|
23673
|
+
}
|
|
23674
|
+
if (ours.versions !== void 0 && parsed.versions !== void 0) ours.versions = [...ours.versions, ...parsed.versions.filter((item) => !ours?.versions?.includes(item))];
|
|
23675
|
+
return "";
|
|
23676
|
+
});
|
|
23677
|
+
if (ours?.versions === void 0 && ours !== void 0) return yaml;
|
|
23678
|
+
const indent = ours?.indent ?? lines.find((line) => /^[ \t]+-/.test(line))?.match(/^([ \t]+)/)?.[1] ?? " ";
|
|
23679
|
+
const versions = ours?.versions === void 0 ? [version2] : ours.versions.includes(version2) ? ours.versions : [...ours.versions, version2];
|
|
23680
|
+
const entry = excludeEntry(indent, name2, versions, ours?.comment ?? "");
|
|
23681
|
+
if (ours === void 0) rewritten.push(entry);
|
|
23682
|
+
else rewritten[ours.index] = entry;
|
|
23683
|
+
const body = rewritten.filter((line) => line !== "").join("\n");
|
|
23684
|
+
const prefix = yaml.slice(0, bodyStart);
|
|
23685
|
+
const suffix = yaml.slice(bodyEnd);
|
|
23686
|
+
const joined = `${prefix}${body}${body.length === 0 || body.endsWith("\n") ? "" : "\n"}${suffix}`;
|
|
23687
|
+
return joined.endsWith("\n") ? joined : `${joined}
|
|
23688
|
+
`;
|
|
23689
|
+
}
|
|
23690
|
+
async function guardReleaseAgeExclude(profileDir, packageSpec) {
|
|
23691
|
+
const file2 = resolve(profileDir, "pnpm-workspace.yaml");
|
|
23692
|
+
const current = existsSync2(file2) ? await readFile(file2, "utf8") : "packages:\n - .\n";
|
|
23693
|
+
mergeMinimumReleaseAgeExclude(current, packageSpec);
|
|
23694
|
+
}
|
|
23695
|
+
async function replaceTextFile(file2, next) {
|
|
23696
|
+
const tmp = `${file2}.${process.pid}.tmp`;
|
|
23697
|
+
const backup = `${file2}.${process.pid}.bak`;
|
|
23698
|
+
await writeFile(tmp, next, "utf8");
|
|
23699
|
+
try {
|
|
23700
|
+
await rename(tmp, file2);
|
|
23701
|
+
return;
|
|
23702
|
+
} catch (error51) {
|
|
23703
|
+
const code = error51 && typeof error51 === "object" && "code" in error51 ? String(error51.code) : "";
|
|
23704
|
+
if (code !== "EEXIST" && code !== "EPERM" && code !== "EACCES") {
|
|
23705
|
+
await unlink(tmp).catch(() => void 0);
|
|
23706
|
+
throw error51;
|
|
23707
|
+
}
|
|
23708
|
+
}
|
|
23709
|
+
await unlink(backup).catch(() => void 0);
|
|
23710
|
+
await rename(file2, backup);
|
|
23711
|
+
try {
|
|
23712
|
+
await rename(tmp, file2);
|
|
23713
|
+
} catch (error51) {
|
|
23714
|
+
await rename(backup, file2).catch(() => void 0);
|
|
23715
|
+
await unlink(tmp).catch(() => void 0);
|
|
23716
|
+
throw error51;
|
|
23717
|
+
}
|
|
23718
|
+
await unlink(backup).catch(() => void 0);
|
|
23719
|
+
}
|
|
23720
|
+
async function recordReleaseAgeExclude(profileDir, packageSpec) {
|
|
23721
|
+
const file2 = resolve(profileDir, "pnpm-workspace.yaml");
|
|
23722
|
+
const current = existsSync2(file2) ? await readFile(file2, "utf8") : "packages:\n - .\n";
|
|
23723
|
+
const next = mergeMinimumReleaseAgeExclude(current, packageSpec);
|
|
23724
|
+
if (next !== current) await replaceTextFile(file2, next);
|
|
23725
|
+
}
|
|
23575
23726
|
async function runCliInstall(target, packageSpec) {
|
|
23576
23727
|
if (target.cliEntry === void 0) throw new Error("\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301\u81EA\u52A8\u66F4\u65B0\uFF0C\u8BF7\u4F7F\u7528\u624B\u5DE5\u66F4\u65B0\u547D\u4EE4\u3002");
|
|
23577
23728
|
await new Promise((resolvePromise, reject) => {
|
|
@@ -23604,10 +23755,12 @@ async function runCliInstall(target, packageSpec) {
|
|
|
23604
23755
|
});
|
|
23605
23756
|
}
|
|
23606
23757
|
async function install(target, packageSpec) {
|
|
23758
|
+
await guardReleaseAgeExclude(target.profileDir, packageSpec);
|
|
23607
23759
|
if (target.desktopPnpm !== void 0) {
|
|
23608
23760
|
const handle = target.desktopPnpm.runPlugin(["add", "--config.minimumReleaseAge=0", packageSpec, "--registry=https://registry.npmjs.org/"], target.profileDir);
|
|
23609
23761
|
const result = await handle.done;
|
|
23610
23762
|
if (result.exitCode !== 0) throw new Error(`\u66F4\u65B0\u8FDB\u7A0B\u9000\u51FA\u7801 ${String(result.exitCode)}\u3002`);
|
|
23763
|
+
await recordReleaseAgeExclude(target.profileDir, packageSpec);
|
|
23611
23764
|
return;
|
|
23612
23765
|
}
|
|
23613
23766
|
if (target.packageManager !== void 0) {
|
|
@@ -23640,14 +23793,19 @@ async function install(target, packageSpec) {
|
|
|
23640
23793
|
else reject(new Error(detail.trim() || `\u66F4\u65B0\u8FDB\u7A0B\u9000\u51FA\u7801 ${String(code)}`));
|
|
23641
23794
|
});
|
|
23642
23795
|
});
|
|
23796
|
+
await recordReleaseAgeExclude(target.profileDir, packageSpec);
|
|
23643
23797
|
return;
|
|
23644
23798
|
}
|
|
23645
23799
|
if (target.pluginManager !== void 0) {
|
|
23646
23800
|
const result = await target.pluginManager.installBundle(packageSpec);
|
|
23647
|
-
if (result.application === "applied" || result.application === "restart-required" || result.application === "overridden")
|
|
23801
|
+
if (result.application === "applied" || result.application === "restart-required" || result.application === "overridden") {
|
|
23802
|
+
await recordReleaseAgeExclude(target.profileDir, packageSpec);
|
|
23803
|
+
return;
|
|
23804
|
+
}
|
|
23648
23805
|
throw new Error(result.error?.message || result.packageResult?.output || "\u66F4\u65B0\u5931\u8D25\uFF0C\u8BF7\u67E5\u770B\u670D\u52A1\u7AEF\u65E5\u5FD7\u3002");
|
|
23649
23806
|
}
|
|
23650
|
-
|
|
23807
|
+
await runCliInstall(target, packageSpec);
|
|
23808
|
+
await recordReleaseAgeExclude(target.profileDir, packageSpec);
|
|
23651
23809
|
}
|
|
23652
23810
|
function json3(response, statusCode, value) {
|
|
23653
23811
|
response.writeHead(statusCode, { "content-type": "application/json; charset=utf-8", "cache-control": "no-store" });
|
|
@@ -42,13 +42,11 @@ export interface ClientContext {
|
|
|
42
42
|
readonly rpc: ClientRpc;
|
|
43
43
|
};
|
|
44
44
|
sessions?: {
|
|
45
|
-
open?(id: string): void;
|
|
46
45
|
refresh?: () => Promise<void>;
|
|
47
46
|
list?: {
|
|
48
47
|
getSnapshot(): {
|
|
49
48
|
ids?: readonly string[];
|
|
50
49
|
byId?: Record<string, unknown>;
|
|
51
|
-
current?: string | null;
|
|
52
50
|
};
|
|
53
51
|
};
|
|
54
52
|
};
|
|
@@ -2,7 +2,7 @@ export type HostIcon = (props: {
|
|
|
2
2
|
readonly size?: number;
|
|
3
3
|
readonly className?: string;
|
|
4
4
|
}) => JSX.Element | null;
|
|
5
|
-
/**
|
|
5
|
+
/** 没有对应导出时返回 undefined,调用方再退回自绘。 */
|
|
6
6
|
export declare function pickHostExport(source: Readonly<Record<string, unknown>>, name: string): unknown;
|
|
7
7
|
/** 宿主图标渲染出 null 时改用自绘。 */
|
|
8
8
|
export declare function hostOrFallback(icon: HostIcon, fallback: HostIcon): HostIcon;
|
|
@@ -14,10 +14,13 @@ export declare const IconSearchOutline: HostIcon;
|
|
|
14
14
|
export declare const IconCloseFill: HostIcon;
|
|
15
15
|
export declare const IconSlidersTwoOutline: HostIcon;
|
|
16
16
|
export declare const IconFolderClose: HostIcon;
|
|
17
|
+
export declare const IconFolderOpen: HostIcon;
|
|
18
|
+
export declare const IconTriangleRightFill: HostIcon;
|
|
19
|
+
export declare const IconUnarchiveOutline: HostIcon;
|
|
17
20
|
export declare const IconWorkspaceTreeOutline: HostIcon;
|
|
18
21
|
export declare const IconFlatListOutline: HostIcon;
|
|
19
22
|
export declare const IconChevronsUpDownOutline: HostIcon;
|
|
20
23
|
export declare const IconClockOutline: HostIcon;
|
|
24
|
+
export declare const IconCopyOutline: HostIcon;
|
|
25
|
+
export declare const IconArchiveOffOutline: HostIcon;
|
|
21
26
|
export declare const IconArchiveCheckOutline: HostIcon;
|
|
22
|
-
/** 0.1.7 的 Menu 会渲染 children。更早的 Menu 只认 items。样式和列表功能不再按宿主版本分叉。 */
|
|
23
|
-
export declare function hostMenuRendersChildren(): boolean;
|
|
@@ -9,6 +9,8 @@ export declare function InfoIcon(props: IconProps): JSX.Element;
|
|
|
9
9
|
export declare function ClockIcon(props: IconProps): JSX.Element;
|
|
10
10
|
export declare function ChatIcon(props: IconProps): JSX.Element;
|
|
11
11
|
export declare function GithubIcon(props: IconProps): JSX.Element;
|
|
12
|
+
/** 与技能管理器标题旁的问题反馈同一支 16px 笔,避免宿主图标把按钮撑高。 */
|
|
13
|
+
export declare function FeedbackIcon(props: IconProps): JSX.Element;
|
|
12
14
|
export declare function FolderIcon(props: IconProps): JSX.Element;
|
|
13
15
|
export declare function SparkleIcon(props: IconProps): JSX.Element;
|
|
14
16
|
export declare function UnarchiveOutlineIcon(props: IconProps): JSX.Element;
|
|
@@ -25,7 +25,8 @@ export declare const en: {
|
|
|
25
25
|
readonly "sidebar.sortManual": "Manual";
|
|
26
26
|
readonly "sidebar.sortTime": "Last updated";
|
|
27
27
|
readonly "sidebar.filterBy": "Filter sessions";
|
|
28
|
-
readonly "sidebar.
|
|
28
|
+
readonly "sidebar.hideArchived": "Hide archived";
|
|
29
|
+
readonly "sidebar.showArchived": "All conversations (show archived)";
|
|
29
30
|
readonly "sidebar.onlyArchived": "Archived only";
|
|
30
31
|
readonly "header.lead": "Plan automatic tasks, or trigger them by hand. Describe the recurring work in any chat to create it quickly.";
|
|
31
32
|
readonly "header.githubProject": "GitHub";
|
|
@@ -49,6 +50,7 @@ export declare const en: {
|
|
|
49
50
|
readonly "session.fork": "Fork session";
|
|
50
51
|
readonly "session.archive": "Archive session";
|
|
51
52
|
readonly "session.unarchive": "Restore";
|
|
53
|
+
readonly "session.copySessionId": "Copy Session ID";
|
|
52
54
|
readonly "session.moreActions": "More actions for {title}";
|
|
53
55
|
readonly "session.groupActions": "Actions for {name}";
|
|
54
56
|
readonly "session.taskSettings": "Task settings";
|
|
@@ -59,6 +61,13 @@ export declare const en: {
|
|
|
59
61
|
readonly "session.archiveGroupFailed": "Some conversations could not be archived: {message}";
|
|
60
62
|
readonly "session.archiveGroupClose": "Close";
|
|
61
63
|
readonly "session.archiveGroupCancel": "Cancel";
|
|
64
|
+
readonly "session.unarchiveGroup": "Restore all conversations";
|
|
65
|
+
readonly "session.unarchiveGroupConfirm": "Restore all";
|
|
66
|
+
readonly "session.unarchiveGroupDescription": "Restore all {count} conversations for “{name}”.";
|
|
67
|
+
readonly "session.unarchiveGroupPending": "Restoring conversations…";
|
|
68
|
+
readonly "session.unarchiveGroupFailed": "Some conversations could not be restored: {message}";
|
|
69
|
+
readonly "session.unarchiveGroupClose": "Close";
|
|
70
|
+
readonly "session.unarchiveGroupCancel": "Cancel";
|
|
62
71
|
readonly "session.deleteSession": "Delete session";
|
|
63
72
|
readonly "session.deleteSessionConfirm": "Delete session";
|
|
64
73
|
readonly "session.deleteSessionDescription": "This permanently deletes session “{name}”, its child agents (including any that are still running), and all of its records (conversation, stats, cache). This cannot be undone.";
|
|
@@ -109,6 +118,7 @@ export declare const en: {
|
|
|
109
118
|
readonly "form.workspace": "Workspace";
|
|
110
119
|
readonly "form.fieldWorkspace": "Workspace";
|
|
111
120
|
readonly "form.fieldModel": "Model";
|
|
121
|
+
readonly "form.providerAccount": "DeepSeek account";
|
|
112
122
|
readonly "form.fieldEffort": "Reasoning";
|
|
113
123
|
readonly "form.fieldPermission": "Permission";
|
|
114
124
|
readonly "form.slashHint": "Type / to insert a skill";
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export interface MenuSelectOption {
|
|
3
|
+
value: string;
|
|
4
|
+
label: React.ReactNode;
|
|
5
|
+
/** 当 label 是复杂 ReactNode 时作为 aria-label 和纯文本回退 */
|
|
6
|
+
text?: string;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
danger?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface MenuSelectGroup {
|
|
11
|
+
id: string;
|
|
12
|
+
label: string;
|
|
13
|
+
options: readonly MenuSelectOption[];
|
|
14
|
+
}
|
|
15
|
+
export interface MenuSelectProps {
|
|
16
|
+
value: string;
|
|
17
|
+
label?: string;
|
|
18
|
+
placeholder?: string;
|
|
19
|
+
options?: readonly MenuSelectOption[];
|
|
20
|
+
groups?: readonly MenuSelectGroup[];
|
|
21
|
+
footer?: readonly MenuSelectOption[];
|
|
22
|
+
onChange: (value: string) => void;
|
|
23
|
+
onFooter?: (id: string) => void;
|
|
24
|
+
disabled?: boolean;
|
|
25
|
+
compact?: boolean;
|
|
26
|
+
className?: string;
|
|
27
|
+
listClassName?: string;
|
|
28
|
+
}
|
|
29
|
+
/** 宿主 Menu 下拉。分组用标签行,底部动作走 footer,不走选择值。 */
|
|
30
|
+
export declare function MenuSelect(props: MenuSelectProps): React.ReactElement;
|
|
@@ -26,3 +26,5 @@ export declare function scheduledGroupShowsActiveFolder(sessionIds: readonly str
|
|
|
26
26
|
export declare function scheduledSessionOmitsStatusSlot(flat: boolean, running: boolean): boolean;
|
|
27
27
|
/** 串行归档,避免多个 workspace 状态写入相互覆盖。 */
|
|
28
28
|
export declare function archiveScheduledGroup(sessionIds: readonly string[], archiveSession: (sessionId: string) => void | Promise<void>): Promise<void>;
|
|
29
|
+
/** 串行恢复,避免多个 workspace 状态写入相互覆盖。 */
|
|
30
|
+
export declare function unarchiveScheduledGroup(sessionIds: readonly string[], unarchiveSession: (sessionId: string) => void | Promise<void>): Promise<void>;
|
|
@@ -4,7 +4,7 @@ type SlotRenderer = (name: string, props?: Record<string, unknown>, opts?: {
|
|
|
4
4
|
readonly hookContext?: unknown;
|
|
5
5
|
readonly only?: string;
|
|
6
6
|
}) => ReactNode;
|
|
7
|
-
/**
|
|
7
|
+
/** 宿主 renderSlot 在条目未声明该子插槽时抛 SlotOwnershipError。接住它,避免定时页整页失败。 */
|
|
8
8
|
export declare function renderOwnedSlot(renderSlot: SlotRenderer | undefined, name: string, props?: Record<string, unknown>, opts?: {
|
|
9
9
|
readonly hookContext?: unknown;
|
|
10
10
|
readonly only?: string;
|
|
@@ -32,7 +32,7 @@ export declare function scheduledSessionHoverStatuses(input: {
|
|
|
32
32
|
readonly runningSubagentCount?: number;
|
|
33
33
|
readonly completed?: boolean;
|
|
34
34
|
}, t: Translate): SessionHoverStatus[];
|
|
35
|
-
/**
|
|
35
|
+
/** 会话标题在悬停时匀速滚到末尾,并交给样式做两端淡出。 */
|
|
36
36
|
export declare function placeSessionTitle(title: HTMLElement, left: number, range: number): void;
|
|
37
37
|
export declare function restSessionTitle(title: HTMLElement): void;
|
|
38
38
|
export declare function startSessionTitleMarquee(title: HTMLElement | null, frame: {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Input as HostInput } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
3
|
+
/** 和技能行一致的 22px 描边图标按钮。尺寸写在 style 上,不靠额外 CSS 覆盖宿主按钮。 */
|
|
4
|
+
export declare const rowIconStyle: React.CSSProperties;
|
|
5
|
+
/** 宿主没有多行输入。行数由 rows 控制,不再接收 Ant 的 autoSize。 */
|
|
6
|
+
export declare const TextArea: React.ForwardRefExoticComponent<React.TextareaHTMLAttributes<HTMLTextAreaElement> & React.RefAttributes<HTMLTextAreaElement>>;
|
|
7
|
+
export declare const Input: typeof HostInput;
|
|
8
|
+
/** 宿主 Modal 点遮罩会调用 onClose。捕获阶段拦住指定弹窗的遮罩点击,保留「点遮罩不关」。 */
|
|
9
|
+
export declare function blockHostModalBackdrop(event: MouseEvent, dialogClass: string): void;
|
|
@@ -110,22 +110,18 @@ export declare function hasCodexUiSidebar(entries: readonly unknown[] | undefine
|
|
|
110
110
|
export interface SessionListState {
|
|
111
111
|
readonly ids?: readonly string[];
|
|
112
112
|
readonly byId?: Record<string, NativeSessionLike>;
|
|
113
|
-
readonly current?: string | null;
|
|
114
113
|
}
|
|
115
114
|
export declare function filterTaskSessionState<T extends SessionListState>(state: T | undefined, scheduledIds?: ReadonlySet<string>): T;
|
|
116
115
|
export interface WorkspaceListState {
|
|
117
116
|
readonly items?: readonly NativeWorkspaceLike[];
|
|
118
117
|
readonly archivedSessionIds?: readonly string[];
|
|
119
118
|
}
|
|
120
|
-
/**
|
|
119
|
+
/** 0.2 主视图由 retainedBy.mainView 标记。 */
|
|
121
120
|
export declare function resolveCurrentSessionId(snapshot: SessionListState | undefined): string | null;
|
|
122
121
|
export interface ClientSessionOpenAccess {
|
|
123
122
|
readonly uiWorkspace?: {
|
|
124
123
|
openSession(id: string): void;
|
|
125
124
|
};
|
|
126
|
-
readonly sessions?: {
|
|
127
|
-
open?(id: string): void;
|
|
128
|
-
};
|
|
129
125
|
readonly get?: (name: string) => unknown;
|
|
130
126
|
readonly reflect?: {
|
|
131
127
|
get?: (name: string) => unknown;
|
|
@@ -134,7 +130,7 @@ export interface ClientSessionOpenAccess {
|
|
|
134
130
|
/** 不硬读 ctx.uiWorkspace:Cordis 未 inject 时读属性会抛,只能 reflect.get / get。 */
|
|
135
131
|
export declare function resolveClientSessionOpenAccess(ctx: ClientSessionOpenAccess): ClientSessionOpenAccess;
|
|
136
132
|
export declare function canOpenClientSession(access: ClientSessionOpenAccess): boolean;
|
|
137
|
-
/**
|
|
133
|
+
/** 支持范围内的宿主都用 uiWorkspace.openSession。不硬读未注入的属性。 */
|
|
138
134
|
export declare function openClientSession(access: ClientSessionOpenAccess, id: string): void;
|
|
139
135
|
export declare function openScheduledSession(id: string, openRuntime?: (sessionId: string) => void, openHost?: (sessionId: string) => void): boolean;
|
|
140
136
|
export interface EnsureOpenScheduledSessionInput {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Translate } from './contracts.js';
|
|
2
2
|
import { type AutomationSortDirection, type AutomationSortKey, type SortPreferenceStorage } from './helpers.js';
|
|
3
|
-
/**
|
|
3
|
+
/** 设置页与侧栏总览共用的排序菜单。当前项可以存成默认。基于官方原语 Menu 重构。 */
|
|
4
4
|
export declare function SortMenu({ t, storage, storageKey, sortKey, sortDirection, onSelect, iconOnly, className, }: {
|
|
5
5
|
readonly t: Translate;
|
|
6
6
|
readonly storage?: SortPreferenceStorage;
|
|
@@ -8,7 +8,6 @@ export declare function SortMenu({ t, storage, storageKey, sortKey, sortDirectio
|
|
|
8
8
|
readonly sortKey: AutomationSortKey;
|
|
9
9
|
readonly sortDirection: AutomationSortDirection;
|
|
10
10
|
readonly onSelect: (key: AutomationSortKey, direction: AutomationSortDirection) => void;
|
|
11
|
-
readonly compact?: boolean;
|
|
12
11
|
readonly iconOnly?: boolean;
|
|
13
12
|
readonly className?: string;
|
|
14
13
|
}): JSX.Element;
|
|
@@ -1,37 +1,20 @@
|
|
|
1
1
|
/** 定时任务表单里新增工作区:只探测宿主服务,不硬读未注入的 ctx.workspaces。 */
|
|
2
|
-
export declare class
|
|
2
|
+
export declare class WorkspacePickerUnavailableError extends Error {
|
|
3
|
+
readonly code = "workspace/picker-unavailable";
|
|
3
4
|
constructor();
|
|
4
5
|
}
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
export declare function createdWorkspaceId(workspaces: readonly {
|
|
21
|
-
readonly id: string;
|
|
22
|
-
readonly path: string;
|
|
23
|
-
}[], path: string): string;
|
|
24
|
-
/** 只探测选目录。桌面优先用原生窗口;否则再调宿主服务。 */
|
|
25
|
-
export declare function resolveDirectoryPicker(ctx: WorkspaceProbe): {
|
|
26
|
-
pickDirectory(): Promise<string | undefined>;
|
|
27
|
-
} | undefined;
|
|
28
|
-
/** @deprecated 测试兼容。创建不再要求客户端 workspaces.create。 */
|
|
29
|
-
export declare function resolveHostWorkspaceCreator(ctx: WorkspaceProbe): HostWorkspaceCreator | undefined;
|
|
30
|
-
export declare function addRegisteredWorkspace(input: {
|
|
31
|
-
readonly creator: HostWorkspaceCreator;
|
|
32
|
-
readonly refresh: () => Promise<void>;
|
|
33
|
-
readonly listed: () => readonly {
|
|
34
|
-
readonly id: string;
|
|
35
|
-
readonly path: string;
|
|
36
|
-
}[];
|
|
37
|
-
}): Promise<string | undefined>;
|
|
6
|
+
/**
|
|
7
|
+
* 在运行期探测宿主服务。Cordis 未 inject 时直接读属性会抛,所以先走 reflect.get,
|
|
8
|
+
* 再走 ctx.get;两者都不可用时才回退到普通属性读取。
|
|
9
|
+
* reflect.get 存在却返回 undefined 时不越过它硬读属性,避免读未注入的服务。
|
|
10
|
+
*/
|
|
11
|
+
export declare function probeService(ctx: unknown, name: string): unknown;
|
|
12
|
+
/** 统一解析宿主选目录的返回值:字符串、{ path }、{ filePaths },取消则 undefined。 */
|
|
13
|
+
export declare function parsePickedPath(value: unknown): string | undefined;
|
|
14
|
+
/** 让宿主登记工作区;宿主没有该方法或拒绝时返回 false,调用方改走运行时登记。 */
|
|
15
|
+
export declare function createHostWorkspace(ctx: unknown, path: string): Promise<boolean>;
|
|
16
|
+
/**
|
|
17
|
+
* 选目录:桌面壳的原生窗口优先,其次依次尝试宿主暴露的选目录服务。
|
|
18
|
+
* 一个都不存在时抛 WorkspacePickerUnavailableError,让调用方决定如何提示。
|
|
19
|
+
*/
|
|
20
|
+
export declare function pickHostDirectory(ctx: unknown): Promise<string | undefined>;
|
package/lib/types/executor.d.ts
CHANGED
|
@@ -41,7 +41,7 @@ export interface ExecutorConfig {
|
|
|
41
41
|
}
|
|
42
42
|
/** 先应用官方预设的完整语义,再让无人值守审批 fail-closed。 */
|
|
43
43
|
export declare function applyUnattendedPermission(presets: PermissionPresetService, session: unknown, permission: AutomationDefinition['permissionPreset']): void;
|
|
44
|
-
/**
|
|
44
|
+
/** 优先读 deriveMessages;没有投影时再读 snapshotEvents。数组入参只给测试夹具。 */
|
|
45
45
|
export declare function hasAutomationSource(hint?: SessionOwnershipHint): boolean;
|
|
46
46
|
/** 已弃用的同步历史读取,只给 session/event 没有增量或不完整时兜底。 */
|
|
47
47
|
export declare function readSessionEvents(session: SessionEventReader): readonly SessionEventLike[];
|
|
@@ -64,5 +64,8 @@ export declare function resolveUpdateRuntime(ctx: {
|
|
|
64
64
|
}): Runtime;
|
|
65
65
|
export declare function shouldNotifyParent(target: Runtime, send?: unknown): boolean;
|
|
66
66
|
export declare function isNewerVersion(currentValue: string, candidateValue: string): boolean;
|
|
67
|
+
/** 把刚安装的精确版本并进 profile 白名单。pnpm 每个包名只认第一条,所以必须并进已有 union。 */
|
|
68
|
+
export declare function mergeMinimumReleaseAgeExclude(yaml: string, packageSpec: string): string;
|
|
69
|
+
export declare function recordReleaseAgeExclude(profileDir: string, packageSpec: string): Promise<void>;
|
|
67
70
|
export declare function registerPluginUpdater(ctx: Context, options: PluginUpdaterOptions): () => void;
|
|
68
71
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@michengai/dsh-automation",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "DSH Automation — 在独立 DSH Session 中按计划执行和管理编码任务 · Schedule and manage coding tasks in isolated DSH sessions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "pnpm@11.22.0",
|
|
@@ -68,28 +68,27 @@
|
|
|
68
68
|
"test:matrix": "pnpm build && node scripts/test-version-matrix.mjs"
|
|
69
69
|
},
|
|
70
70
|
"dependencies": {
|
|
71
|
-
"antd": "^6.6.5",
|
|
72
71
|
"luxon": "^3.7.2",
|
|
73
72
|
"zod": "^4.1.5"
|
|
74
73
|
},
|
|
75
74
|
"peerDependencies": {
|
|
76
|
-
"@deepseek-ai/cordis": ">=4.0.1 <5.0.0
|
|
77
|
-
"@deepseek-ai/dsh-agent": "0.
|
|
78
|
-
"@deepseek-ai/dsh-agent-default-model": "0.
|
|
79
|
-
"@deepseek-ai/dsh-client-connection": "0.
|
|
80
|
-
"@deepseek-ai/dsh-client-locale": "0.
|
|
81
|
-
"@deepseek-ai/dsh-client-ui-conversation": "0.
|
|
82
|
-
"@deepseek-ai/dsh-client-ui-primitives": "0.
|
|
83
|
-
"@deepseek-ai/dsh-client-ui-settings": "0.
|
|
84
|
-
"@deepseek-ai/dsh-host-webserver": "0.
|
|
85
|
-
"@deepseek-ai/dsh-llm": "0.
|
|
86
|
-
"@deepseek-ai/dsh-permission-presets": "0.
|
|
87
|
-
"@deepseek-ai/dsh-session": "0.
|
|
88
|
-
"@deepseek-ai/dsh-storage-domain": "0.
|
|
89
|
-
"@deepseek-ai/dsh-tools": "0.
|
|
90
|
-
"@deepseek-ai/dsh-user-approval": "0.
|
|
91
|
-
"@deepseek-ai/dsh-workspace": "0.
|
|
92
|
-
"@deepseek-ai/schemastery": ">=3.18.1 <4.0.0
|
|
75
|
+
"@deepseek-ai/cordis": ">=4.0.1 <5.0.0",
|
|
76
|
+
"@deepseek-ai/dsh-agent": "0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2",
|
|
77
|
+
"@deepseek-ai/dsh-agent-default-model": "0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2",
|
|
78
|
+
"@deepseek-ai/dsh-client-connection": "0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2",
|
|
79
|
+
"@deepseek-ai/dsh-client-locale": "0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2",
|
|
80
|
+
"@deepseek-ai/dsh-client-ui-conversation": "0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2",
|
|
81
|
+
"@deepseek-ai/dsh-client-ui-primitives": "0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2",
|
|
82
|
+
"@deepseek-ai/dsh-client-ui-settings": "0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2",
|
|
83
|
+
"@deepseek-ai/dsh-host-webserver": "0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2",
|
|
84
|
+
"@deepseek-ai/dsh-llm": "0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2",
|
|
85
|
+
"@deepseek-ai/dsh-permission-presets": "0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2",
|
|
86
|
+
"@deepseek-ai/dsh-session": "0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2",
|
|
87
|
+
"@deepseek-ai/dsh-storage-domain": "0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2",
|
|
88
|
+
"@deepseek-ai/dsh-tools": "0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2",
|
|
89
|
+
"@deepseek-ai/dsh-user-approval": "0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2",
|
|
90
|
+
"@deepseek-ai/dsh-workspace": "0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2",
|
|
91
|
+
"@deepseek-ai/schemastery": ">=3.18.1 <4.0.0",
|
|
93
92
|
"react": "^18.2.0"
|
|
94
93
|
},
|
|
95
94
|
"peerDependenciesMeta": {
|
|
@@ -98,28 +97,28 @@
|
|
|
98
97
|
}
|
|
99
98
|
},
|
|
100
99
|
"devDependencies": {
|
|
101
|
-
"@deepseek-ai/cordis": "4.0.
|
|
102
|
-
"@deepseek-ai/cordis-plugin-include": "1.0.
|
|
103
|
-
"@deepseek-ai/dsh-agent": "0.2.
|
|
104
|
-
"@deepseek-ai/dsh-agent-default-model": "0.2.
|
|
105
|
-
"@deepseek-ai/dsh-agent-loop": "0.2.
|
|
106
|
-
"@deepseek-ai/dsh-agent-preset-registry": "0.2.
|
|
107
|
-
"@deepseek-ai/dsh-client-connection": "0.2.
|
|
108
|
-
"@deepseek-ai/dsh-client-locale": "0.2.
|
|
109
|
-
"@deepseek-ai/dsh-client-ui-conversation": "0.2.
|
|
110
|
-
"@deepseek-ai/dsh-client-ui-primitives": "0.2.
|
|
111
|
-
"@deepseek-ai/dsh-client-ui-settings": "0.2.
|
|
112
|
-
"@deepseek-ai/dsh-host-webserver": "0.2.
|
|
113
|
-
"@deepseek-ai/dsh-llm": "0.2.
|
|
114
|
-
"@deepseek-ai/dsh-permission-presets": "0.2.
|
|
115
|
-
"@deepseek-ai/dsh-session": "0.2.
|
|
116
|
-
"@deepseek-ai/dsh-session-projection": "0.2.
|
|
117
|
-
"@deepseek-ai/dsh-storage-domain": "0.2.
|
|
118
|
-
"@deepseek-ai/dsh-system-prompt": "0.2.
|
|
119
|
-
"@deepseek-ai/dsh-tools": "0.2.
|
|
120
|
-
"@deepseek-ai/dsh-user-approval": "0.2.
|
|
121
|
-
"@deepseek-ai/dsh-workspace": "0.2.
|
|
122
|
-
"@deepseek-ai/schemastery": "3.18.
|
|
100
|
+
"@deepseek-ai/cordis": "4.0.5",
|
|
101
|
+
"@deepseek-ai/cordis-plugin-include": "1.0.10",
|
|
102
|
+
"@deepseek-ai/dsh-agent": "0.2.1-rc.2",
|
|
103
|
+
"@deepseek-ai/dsh-agent-default-model": "0.2.1-rc.2",
|
|
104
|
+
"@deepseek-ai/dsh-agent-loop": "0.2.1-rc.2",
|
|
105
|
+
"@deepseek-ai/dsh-agent-preset-registry": "0.2.1-rc.2",
|
|
106
|
+
"@deepseek-ai/dsh-client-connection": "0.2.1-rc.2",
|
|
107
|
+
"@deepseek-ai/dsh-client-locale": "0.2.1-rc.2",
|
|
108
|
+
"@deepseek-ai/dsh-client-ui-conversation": "0.2.1-rc.2",
|
|
109
|
+
"@deepseek-ai/dsh-client-ui-primitives": "0.2.1-rc.2",
|
|
110
|
+
"@deepseek-ai/dsh-client-ui-settings": "0.2.1-rc.2",
|
|
111
|
+
"@deepseek-ai/dsh-host-webserver": "0.2.1-rc.2",
|
|
112
|
+
"@deepseek-ai/dsh-llm": "0.2.1-rc.2",
|
|
113
|
+
"@deepseek-ai/dsh-permission-presets": "0.2.1-rc.2",
|
|
114
|
+
"@deepseek-ai/dsh-session": "0.2.1-rc.2",
|
|
115
|
+
"@deepseek-ai/dsh-session-projection": "0.2.1-rc.2",
|
|
116
|
+
"@deepseek-ai/dsh-storage-domain": "0.2.1-rc.2",
|
|
117
|
+
"@deepseek-ai/dsh-system-prompt": "0.2.1-rc.2",
|
|
118
|
+
"@deepseek-ai/dsh-tools": "0.2.1-rc.2",
|
|
119
|
+
"@deepseek-ai/dsh-user-approval": "0.2.1-rc.2",
|
|
120
|
+
"@deepseek-ai/dsh-workspace": "0.2.1-rc.2",
|
|
121
|
+
"@deepseek-ai/schemastery": "3.18.5",
|
|
123
122
|
"@types/js-yaml": "4.0.9",
|
|
124
123
|
"@types/luxon": "^3.7.1",
|
|
125
124
|
"@types/node": "^22.0.0",
|