@michengai/dsh-automation 0.1.10 → 0.1.12
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/docs/00-/344/272/244/346/216/245/345/205/245/345/217/243/01-/351/241/271/347/233/256/346/246/202/350/247/210.md +1 -1
- package/docs/01-/345/275/223/345/211/215/345/267/245/344/275/234/I001-/344/273/273/345/212/241/350/207/252/345/212/250/345/214/226/346/217/222/344/273/266/00-/350/277/255/344/273/243/346/200/273/350/247/210.md +2 -2
- package/lib/client.js +43 -102
- package/lib/client.js.map +3 -3
- package/lib/index.js +380 -315
- package/lib/types/client/AutomationView.d.ts +1 -1
- package/lib/types/client/contracts.d.ts +0 -4
- package/lib/types/client/create-modal-logic.d.ts +1 -6
- package/lib/types/client/create-modal.d.ts +1 -3
- package/lib/types/client/locales.d.ts +0 -1
- package/lib/types/client/protocol.d.ts +0 -4
- package/lib/types/client/runtime.d.ts +0 -3
- package/lib/types/executor.d.ts +2 -0
- package/lib/types/rpc.d.ts +1 -1
- package/lib/types/service.d.ts +6 -1
- package/package.json +1 -1
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
- 每次到期都创建全新 root Agent 和 Session,不继承来源对话。
|
|
14
14
|
- 规则、修订号和运行历史持久化。
|
|
15
15
|
- 同一条规则同时最多一个 active run;超时、重叠和 Host 中断都有明确终态。
|
|
16
|
-
-
|
|
16
|
+
- 权限预设与 Chat 保持一致,完整使用 Host 提供的列表与默认值;切换到 `danger-full-access` 时显示同款风险确认。
|
|
17
17
|
|
|
18
18
|
## 稳定边界
|
|
19
19
|
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
- [x] 原生侧栏定时页签:包裹官方任务树,不依赖 dsh-codex-ui
|
|
18
18
|
- [x] 单测与包契约
|
|
19
19
|
- [x] 定时侧栏会话菜单互斥,避免点第二条时与第一条重叠
|
|
20
|
-
- [x]
|
|
20
|
+
- [x] 新建弹窗只允许选择 Host 已注册工作区,新增目录统一从 Host 工作区入口完成
|
|
21
21
|
- [x] 修复任务树过滤导致的 Loading plugins / 点任务卡死
|
|
22
22
|
- [x] 会话创建定时任务按官方审批策略分流,并补系统提示避免走 crontab
|
|
23
23
|
- [x] 工作区标题栏对齐官方位置与搜索点击
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
|
|
37
37
|
## 已完成内容
|
|
38
38
|
|
|
39
|
-
仓库内已落地 Host 插件、Web
|
|
39
|
+
仓库内已落地 Host 插件、Web 客户端、原生侧栏定时页签、文档和单测。定时会话更多菜单改为列表级互斥,并用固定定位弹出,避免两条菜单叠在一起。新建弹窗只消费 Host 已注册工作区,避免插件 RPC 自行扩大目录边界;点遮罩不再关闭。
|
|
40
40
|
|
|
41
41
|
## 待处理事项
|
|
42
42
|
|
package/lib/client.js
CHANGED
|
@@ -193,19 +193,13 @@ function groupHistory(runs, range, now, t) {
|
|
|
193
193
|
const at = new Date(run.finishedAt ?? run.startedAt ?? run.scheduledFor);
|
|
194
194
|
if (Number.isNaN(at.getTime())) continue;
|
|
195
195
|
let key;
|
|
196
|
-
let label;
|
|
197
196
|
if (range === "month") {
|
|
198
197
|
key = `${at.getFullYear()}-${String(at.getMonth() + 1).padStart(2, "0")}`;
|
|
199
|
-
label = t("history.month", { month: `${at.getFullYear()}/${at.getMonth() + 1}` });
|
|
200
198
|
} else if (range === "week") {
|
|
201
199
|
const start = startOfWeek(at);
|
|
202
|
-
key = start
|
|
203
|
-
label = t("history.week", { date: `${start.getMonth() + 1}/${start.getDate()}` });
|
|
200
|
+
key = localDayKey(start);
|
|
204
201
|
} else {
|
|
205
202
|
key = localDayKey(at);
|
|
206
|
-
const today = localDayKey(now);
|
|
207
|
-
const yesterday = localDayKey(new Date(now.getTime() - 864e5));
|
|
208
|
-
label = key === today ? t("history.today") : key === yesterday ? t("history.yesterday") : t("history.date", { date: `${at.getMonth() + 1}/${at.getDate()}` });
|
|
209
203
|
}
|
|
210
204
|
const existing = buckets.get(key) ?? [];
|
|
211
205
|
existing.push(run);
|
|
@@ -415,11 +409,6 @@ function permissionLabel(option, t) {
|
|
|
415
409
|
function shouldConfirmFullAccess(current, next) {
|
|
416
410
|
return current !== next && next === "danger-full-access";
|
|
417
411
|
}
|
|
418
|
-
async function adoptPickedWorkspace(input) {
|
|
419
|
-
const path = await input.pick();
|
|
420
|
-
if (path == null || path.trim() === "") return null;
|
|
421
|
-
return input.add(path.trim());
|
|
422
|
-
}
|
|
423
412
|
|
|
424
413
|
// src/client/menu.tsx
|
|
425
414
|
var import_react = require("react");
|
|
@@ -647,9 +636,7 @@ function CreateModal({
|
|
|
647
636
|
draft,
|
|
648
637
|
editing,
|
|
649
638
|
onClose,
|
|
650
|
-
onSubmit
|
|
651
|
-
onAddWorkspace,
|
|
652
|
-
pickWorkspaceDirectory
|
|
639
|
+
onSubmit
|
|
653
640
|
}) {
|
|
654
641
|
const [form, setForm] = (0, import_react2.useState)(() => ({ ...defaultFormState(/* @__PURE__ */ new Date(), workspaces, defaultModel, defaultPermission), ...draft }));
|
|
655
642
|
const [validationError, setValidationError] = (0, import_react2.useState)();
|
|
@@ -672,8 +659,7 @@ function CreateModal({
|
|
|
672
659
|
setConfirmingPermission(void 0);
|
|
673
660
|
};
|
|
674
661
|
const confirmFullAccess = () => {
|
|
675
|
-
if (!fullAccessAcknowledged) return;
|
|
676
|
-
if (confirmingPermission === void 0) return;
|
|
662
|
+
if (!fullAccessAcknowledged || confirmingPermission === void 0) return;
|
|
677
663
|
update({ permission: confirmingPermission });
|
|
678
664
|
cancelFullAccessConfirmation();
|
|
679
665
|
};
|
|
@@ -819,30 +805,7 @@ function CreateModal({
|
|
|
819
805
|
onClick: () => update({ workspaceId: item.id })
|
|
820
806
|
},
|
|
821
807
|
item.id
|
|
822
|
-
))
|
|
823
|
-
pickWorkspaceDirectory !== void 0 && onAddWorkspace !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
824
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-st-menu-split" }),
|
|
825
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
826
|
-
MenuRow,
|
|
827
|
-
{
|
|
828
|
-
icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PlusIcon, { width: 14, height: 14 }),
|
|
829
|
-
label: t("form.addWorkspace"),
|
|
830
|
-
onClick: () => {
|
|
831
|
-
void (async () => {
|
|
832
|
-
try {
|
|
833
|
-
const id = await adoptPickedWorkspace({
|
|
834
|
-
pick: pickWorkspaceDirectory,
|
|
835
|
-
add: onAddWorkspace
|
|
836
|
-
});
|
|
837
|
-
if (id !== null) update({ workspaceId: id });
|
|
838
|
-
} catch (caught) {
|
|
839
|
-
setValidationError(caught instanceof Error ? caught.message : t("error.action"));
|
|
840
|
-
}
|
|
841
|
-
})();
|
|
842
|
-
}
|
|
843
|
-
}
|
|
844
|
-
)
|
|
845
|
-
] })
|
|
808
|
+
))
|
|
846
809
|
] }),
|
|
847
810
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(MenuPanel, { ghost: true, up: true, label: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
848
811
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SparkleIcon, { width: 14, height: 14 }),
|
|
@@ -1157,6 +1120,7 @@ function unwrapRpcResult(value) {
|
|
|
1157
1120
|
|
|
1158
1121
|
// src/client/runtime.ts
|
|
1159
1122
|
var CHANNEL = "/dsh-automation";
|
|
1123
|
+
var POLL_INTERVAL_MS = 15e3;
|
|
1160
1124
|
function isTransportError(error) {
|
|
1161
1125
|
const message = error instanceof Error ? error.message : String(error);
|
|
1162
1126
|
return /failed to fetch|networkerror|load failed|network request failed/i.test(message);
|
|
@@ -1164,6 +1128,7 @@ function isTransportError(error) {
|
|
|
1164
1128
|
function createAutomationRuntime(rpc) {
|
|
1165
1129
|
let state = { phase: "idle" };
|
|
1166
1130
|
let refreshPromise;
|
|
1131
|
+
let pollTimer;
|
|
1167
1132
|
const listeners2 = /* @__PURE__ */ new Set();
|
|
1168
1133
|
const publish = (next) => {
|
|
1169
1134
|
state = next;
|
|
@@ -1173,8 +1138,20 @@ function createAutomationRuntime(rpc) {
|
|
|
1173
1138
|
getSnapshot: () => state,
|
|
1174
1139
|
subscribe: (listener) => {
|
|
1175
1140
|
listeners2.add(listener);
|
|
1141
|
+
if (listeners2.size === 1) {
|
|
1142
|
+
queueMicrotask(() => {
|
|
1143
|
+
if (listeners2.size > 0) void refresh().catch(() => void 0);
|
|
1144
|
+
});
|
|
1145
|
+
pollTimer = setInterval(() => {
|
|
1146
|
+
void refresh().catch(() => void 0);
|
|
1147
|
+
}, POLL_INTERVAL_MS);
|
|
1148
|
+
}
|
|
1176
1149
|
return () => {
|
|
1177
1150
|
listeners2.delete(listener);
|
|
1151
|
+
if (listeners2.size === 0 && pollTimer !== void 0) {
|
|
1152
|
+
clearInterval(pollTimer);
|
|
1153
|
+
pollTimer = void 0;
|
|
1154
|
+
}
|
|
1178
1155
|
};
|
|
1179
1156
|
}
|
|
1180
1157
|
};
|
|
@@ -1274,25 +1251,18 @@ function createAutomationRuntime(rpc) {
|
|
|
1274
1251
|
return rest;
|
|
1275
1252
|
})
|
|
1276
1253
|
}));
|
|
1277
|
-
},
|
|
1278
|
-
async addWorkspace(path) {
|
|
1279
|
-
const payload = { sessionId: "settings", path };
|
|
1280
|
-
const value = unwrapRpcResult(await rpc.call(CHANNEL, "add-workspace", payload));
|
|
1281
|
-
await refresh();
|
|
1282
|
-
return value;
|
|
1283
1254
|
}
|
|
1284
1255
|
};
|
|
1285
1256
|
}
|
|
1286
1257
|
|
|
1287
1258
|
// src/client/AutomationView.tsx
|
|
1288
1259
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
1289
|
-
var POLL_INTERVAL_MS = 15e3;
|
|
1290
1260
|
var EXAMPLES = [
|
|
1291
1261
|
{ name: "\u6BCF\u65E5\u56DE\u5F52\u68C0\u67E5", scheduleKind: "daily", time: "09:00", weekdays: [1, 2, 3, 4, 5] },
|
|
1292
1262
|
{ name: "\u6BCF\u5468\u4F9D\u8D56\u5DE1\u68C0", scheduleKind: "weekly", time: "10:00", weekdays: [1] },
|
|
1293
1263
|
{ name: "\u5DE5\u4F5C\u65E5\u65E9\u62A5", scheduleKind: "weekly", time: "08:00", weekdays: [1, 2, 3, 4, 5] }
|
|
1294
1264
|
];
|
|
1295
|
-
function AutomationView({ t, permissionT, modelT, runtime, closeSettings
|
|
1265
|
+
function AutomationView({ t, permissionT, modelT, runtime, closeSettings }) {
|
|
1296
1266
|
const state = (0, import_react3.useSyncExternalStore)(runtime.source.subscribe, runtime.source.getSnapshot, runtime.source.getSnapshot);
|
|
1297
1267
|
const [tab, setTab] = (0, import_react3.useState)("mine");
|
|
1298
1268
|
const [query, setQuery] = (0, import_react3.useState)("");
|
|
@@ -1305,15 +1275,6 @@ function AutomationView({ t, permissionT, modelT, runtime, closeSettings, pickWo
|
|
|
1305
1275
|
const [historyTask, setHistoryTask] = (0, import_react3.useState)("all");
|
|
1306
1276
|
const [historyStatus, setHistoryStatus] = (0, import_react3.useState)("all");
|
|
1307
1277
|
const now = (0, import_react3.useMemo)(() => new Date(state.snapshot?.serverNow ?? Date.now()), [state.snapshot?.serverNow, state.refreshedAt]);
|
|
1308
|
-
(0, import_react3.useEffect)(() => {
|
|
1309
|
-
void runtime.refresh().catch(() => void 0);
|
|
1310
|
-
const timer = window.setInterval(() => {
|
|
1311
|
-
void runtime.refresh().catch(() => void 0);
|
|
1312
|
-
}, POLL_INTERVAL_MS);
|
|
1313
|
-
return () => {
|
|
1314
|
-
window.clearInterval(timer);
|
|
1315
|
-
};
|
|
1316
|
-
}, [runtime]);
|
|
1317
1278
|
const snapshot = state.snapshot;
|
|
1318
1279
|
const workspaces = snapshot?.workspaces ?? [];
|
|
1319
1280
|
const models = snapshot?.models ?? [];
|
|
@@ -1474,8 +1435,6 @@ function AutomationView({ t, permissionT, modelT, runtime, closeSettings, pickWo
|
|
|
1474
1435
|
skills: snapshot?.skills ?? [],
|
|
1475
1436
|
permissions,
|
|
1476
1437
|
defaultPermission,
|
|
1477
|
-
onAddWorkspace: async (path) => runtime.addWorkspace(path).then((value) => value.id),
|
|
1478
|
-
...pickWorkspaceDirectory === void 0 ? {} : { pickWorkspaceDirectory },
|
|
1479
1438
|
editing: editingId !== void 0,
|
|
1480
1439
|
...draft === void 0 ? {} : { draft },
|
|
1481
1440
|
onClose: closeModal,
|
|
@@ -1637,7 +1596,6 @@ var en = {
|
|
|
1637
1596
|
"tabs.runs": "Run history",
|
|
1638
1597
|
"sort.created": "Newest first",
|
|
1639
1598
|
"form.workspace": "Workspace",
|
|
1640
|
-
"form.addWorkspace": "Add workspace...",
|
|
1641
1599
|
"form.workspacePath": "Folder path",
|
|
1642
1600
|
"form.workspacePathPlaceholder": "D:\\work\\project",
|
|
1643
1601
|
"form.model": "Model",
|
|
@@ -1829,7 +1787,6 @@ var zh = {
|
|
|
1829
1787
|
"tabs.runs": "\u6267\u884C\u8BB0\u5F55",
|
|
1830
1788
|
"sort.created": "\u6309\u521B\u5EFA\u65F6\u95F4\u5012\u5E8F",
|
|
1831
1789
|
"form.workspace": "\u5DE5\u4F5C\u76EE\u5F55",
|
|
1832
|
-
"form.addWorkspace": "\u6DFB\u52A0\u5DE5\u4F5C\u533A...",
|
|
1833
1790
|
"form.workspacePath": "\u76EE\u5F55\u8DEF\u5F84",
|
|
1834
1791
|
"form.workspacePathPlaceholder": "D:\\work\\project",
|
|
1835
1792
|
"form.model": "\u6A21\u578B",
|
|
@@ -2486,15 +2443,6 @@ function NativeScheduleSessionList(props) {
|
|
|
2486
2443
|
const [query, setQuery] = (0, import_react5.useState)("");
|
|
2487
2444
|
const [sort, setSort] = (0, import_react5.useState)("time");
|
|
2488
2445
|
const [groupMode, setGroupMode] = (0, import_react5.useState)("workspace");
|
|
2489
|
-
(0, import_react5.useEffect)(() => {
|
|
2490
|
-
void runtime.refresh().catch(() => void 0);
|
|
2491
|
-
const timer = window.setInterval(() => {
|
|
2492
|
-
void runtime.refresh().catch(() => void 0);
|
|
2493
|
-
}, 15e3);
|
|
2494
|
-
return () => {
|
|
2495
|
-
window.clearInterval(timer);
|
|
2496
|
-
};
|
|
2497
|
-
}, [runtime]);
|
|
2498
2446
|
const archived = (0, import_react5.useMemo)(() => new Set(archivedIds), [archivedIds]);
|
|
2499
2447
|
const listedIds = useSessions ? useSessions((snap) => Array.isArray(snap?.ids) ? snap.ids : void 0) : void 0;
|
|
2500
2448
|
const presentIds = (0, import_react5.useMemo)(() => {
|
|
@@ -2729,15 +2677,6 @@ function ScheduleRail({
|
|
|
2729
2677
|
}) {
|
|
2730
2678
|
const state = (0, import_react6.useSyncExternalStore)(runtime.source.subscribe, runtime.source.getSnapshot, runtime.source.getSnapshot);
|
|
2731
2679
|
const [folded, setFolded] = (0, import_react6.useState)({});
|
|
2732
|
-
(0, import_react6.useEffect)(() => {
|
|
2733
|
-
void runtime.refresh().catch(() => void 0);
|
|
2734
|
-
const timer = window.setInterval(() => {
|
|
2735
|
-
void runtime.refresh().catch(() => void 0);
|
|
2736
|
-
}, 15e3);
|
|
2737
|
-
return () => {
|
|
2738
|
-
window.clearInterval(timer);
|
|
2739
|
-
};
|
|
2740
|
-
}, [runtime]);
|
|
2741
2680
|
const groups = (0, import_react6.useMemo)(() => {
|
|
2742
2681
|
const snapshot = state.snapshot;
|
|
2743
2682
|
if (snapshot === void 0) return [];
|
|
@@ -3068,20 +3007,30 @@ var name = "dsh-automation-client";
|
|
|
3068
3007
|
var inject = ["slots", "locale", "connection", "sessions"];
|
|
3069
3008
|
var SETTINGS_CLOCK_SVG = '<svg viewBox="0 0 16 16" width="16" height="16" fill="none" aria-hidden="true" data-dsh-schedule-icon="1"><path fill="currentColor" d="M8 1.15A6.85 6.85 0 1 0 8 14.85 6.85 6.85 0 0 0 8 1.15Zm0 1.4a5.45 5.45 0 1 1 0 10.9 5.45 5.45 0 0 1 0-10.9Z"/><path fill="currentColor" d="M8.62 4.35H7.28v4.2l3.02 1.78.67-1.13-2.35-1.39V4.35Z"/></svg>';
|
|
3070
3009
|
function installSettingsNavIcon(labels) {
|
|
3071
|
-
const
|
|
3072
|
-
|
|
3073
|
-
const
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3010
|
+
const wanted = new Set(labels().map((item) => item.trim()).filter((item) => item !== ""));
|
|
3011
|
+
const applyButton = (button) => {
|
|
3012
|
+
const text = (button.textContent ?? "").replace(/\s+/g, " ").trim();
|
|
3013
|
+
if (!wanted.has(text)) return;
|
|
3014
|
+
const svg = button.querySelector("svg");
|
|
3015
|
+
if (svg === null || svg.getAttribute("data-dsh-schedule-icon") === "1") return;
|
|
3016
|
+
svg.outerHTML = SETTINGS_CLOCK_SVG;
|
|
3017
|
+
};
|
|
3018
|
+
const scan = (root) => {
|
|
3019
|
+
if (root instanceof HTMLButtonElement) applyButton(root);
|
|
3020
|
+
for (const button of root.querySelectorAll?.("button") ?? []) applyButton(button);
|
|
3081
3021
|
};
|
|
3082
|
-
|
|
3083
|
-
const observer = new MutationObserver(
|
|
3084
|
-
|
|
3022
|
+
scan(document);
|
|
3023
|
+
const observer = new MutationObserver((mutations) => {
|
|
3024
|
+
for (const mutation of mutations) {
|
|
3025
|
+
const target = mutation.target instanceof Element ? mutation.target : mutation.target.parentElement;
|
|
3026
|
+
const owner = target?.closest("button");
|
|
3027
|
+
if (owner instanceof HTMLButtonElement) applyButton(owner);
|
|
3028
|
+
for (const node of mutation.addedNodes) {
|
|
3029
|
+
if (node instanceof Element) scan(node);
|
|
3030
|
+
}
|
|
3031
|
+
}
|
|
3032
|
+
});
|
|
3033
|
+
observer.observe(document.documentElement, { childList: true, characterData: true, subtree: true });
|
|
3085
3034
|
return () => {
|
|
3086
3035
|
observer.disconnect();
|
|
3087
3036
|
};
|
|
@@ -3102,8 +3051,7 @@ function apply(ctx) {
|
|
|
3102
3051
|
label: () => t("tab"),
|
|
3103
3052
|
icon: "schedule"
|
|
3104
3053
|
}, function ScheduledTasksSettings(props) {
|
|
3105
|
-
|
|
3106
|
-
return (0, import_react7.createElement)(AutomationView, { t, permissionT, modelT, runtime, ...props.close === void 0 ? {} : { closeSettings: props.close }, ...workspacesApi === void 0 ? {} : { pickWorkspaceDirectory: () => workspacesApi.pickDirectory() } });
|
|
3054
|
+
return (0, import_react7.createElement)(AutomationView, { t, permissionT, modelT, runtime, ...props.close === void 0 ? {} : { closeSettings: props.close } });
|
|
3107
3055
|
}));
|
|
3108
3056
|
ctx.slots.inject("sidebar.schedule", () => ctx.slots.register({
|
|
3109
3057
|
name: "sidebar.schedule",
|
|
@@ -3311,12 +3259,5 @@ function readSlotEntries(ctx, name2) {
|
|
|
3311
3259
|
function slotHasEntries(ctx, name2) {
|
|
3312
3260
|
return readSlotEntries(ctx, name2).length > 0;
|
|
3313
3261
|
}
|
|
3314
|
-
function readOptionalWorkspaces(ctx) {
|
|
3315
|
-
try {
|
|
3316
|
-
return ctx.workspaces;
|
|
3317
|
-
} catch {
|
|
3318
|
-
return void 0;
|
|
3319
|
-
}
|
|
3320
|
-
}
|
|
3321
3262
|
return module.exports; } });
|
|
3322
3263
|
//# sourceMappingURL=client.js.map
|