@michengai/dsh-automation 0.1.6 → 0.1.8

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/README.md CHANGED
@@ -25,7 +25,7 @@
25
25
  - Create, pause, resume, run now, and delete rules from the Web UI or Agent tools.
26
26
  - Start each occurrence in a fresh root Agent and Session. Source-chat history is not inherited.
27
27
  - Support once, interval, hourly, daily, weekly, monthly, and custom-every-N-days schedules.
28
- - Pick workspace, model, skills, and `read-only` / `workspace-write` in the create dialog.
28
+ - Pick workspace, model, skills, and any permission preset exposed by the Host.
29
29
  - Create from chat: describe the schedule in any conversation, then confirm with the official approval card.
30
30
  - Keep durable run history: `queued`, `running`, `succeeded`, `failed`, `skipped`, `cancelled`.
31
31
  - Add a sidebar **Scheduled** tab. Folders are task names and child sessions are run times. On stock DSH it wraps the official workspace tree and does not depend on `dsh-codex-ui`.
@@ -124,8 +124,8 @@ Each dispatched run uses the saved prompt, workspace, model, and permission boun
124
124
 
125
125
  | Item | Behavior |
126
126
  | --- | --- |
127
- | Permission | Default is `read-only`. File writes require an explicit `workspace-write` choice. |
128
- | Full access | Unattended `danger-full-access` is not offered. |
127
+ | Permission | Options and the default come directly from the Host `permissionPresets` service, including custom presets. |
128
+ | Full access | The official `danger-full-access` option uses the same risk confirmation and orange warning as Chat. |
129
129
  | Approval | Chat create follows the session policy. Full access (`never`) proceeds; Workspace Write / Read Only (`ask`) shows the official card. Unattended runs stay fail-closed `never`. |
130
130
  | Retry | No automatic retry after a started run. |
131
131
  | Host restart | Leftover `queued` / `running` records become `failed(host_interrupted)`. |
package/README.zh-CN.md CHANGED
@@ -25,7 +25,7 @@
25
25
  - 支持 Web 设置页和 Agent 工具创建、暂停、恢复、立即运行和删除。
26
26
  - 每次到期都启动全新 root Agent 和 Session,不继承来源对话。
27
27
  - 计划类型包括不重复、间隔、每小时、每天、每周、每月和自定义间隔天数。
28
- - 新建弹窗可选择工作目录、模型、技能,以及 `read-only` / `workspace-write`。
28
+ - 新建弹窗可选择工作目录、模型、技能,以及 Host 官方提供的权限预设。
29
29
  - 在任意对话里描述定时任务即可创建;Full access 直接执行,其他权限走官方授权卡。
30
30
  - 运行状态包含 `queued`、`running`、`succeeded`、`failed`、`skipped`、`cancelled`。
31
31
  - 侧栏提供「定时」页签:文件夹是任务名称,子会话是执行时间。原生下只包裹官方任务树,不依赖 `dsh-codex-ui`。
@@ -124,8 +124,8 @@ dsh --profile web --dump-config
124
124
 
125
125
  | 项目 | 行为 |
126
126
  | --- | --- |
127
- | 权限 | 默认 `read-only`。改文件必须显式选择 `workspace-write`。 |
128
- | 完全访问 | 不提供无人值守 `danger-full-access`。 |
127
+ | 权限 | 列表和默认值直接来自 Host 官方 `permissionPresets` 服务;支持 Host 注册的自定义预设。 |
128
+ | 完全访问 | 选择官方 `danger-full-access` 时显示与 Chat 一致的风险确认和橙色提示。 |
129
129
  | 审批 | 对话创建跟随当前会话策略。Full access(`never`)直接创建;Workspace Write / Read Only(`ask`)走官方授权卡。无人值守运行仍是 fail-closed 的 `never`。 |
130
130
  | 重试 | 已经开始的运行不会自动重试。 |
131
131
  | Host 重启 | 遗留的 `queued` / `running` 会变成 `failed(host_interrupted)`。 |
package/lib/client.js CHANGED
@@ -63,7 +63,7 @@ function localDateTimeValue(date = /* @__PURE__ */ new Date()) {
63
63
  const offset = future.getTimezoneOffset() * 6e4;
64
64
  return new Date(future.getTime() - offset).toISOString().slice(0, 16);
65
65
  }
66
- function defaultFormState(now = /* @__PURE__ */ new Date(), workspaces = [], defaultModel) {
66
+ function defaultFormState(now = /* @__PURE__ */ new Date(), workspaces = [], defaultModel, defaultPermission = "") {
67
67
  return {
68
68
  name: "",
69
69
  prompt: "",
@@ -76,7 +76,7 @@ function defaultFormState(now = /* @__PURE__ */ new Date(), workspaces = [], def
76
76
  monthDay: "1",
77
77
  customDays: "2",
78
78
  timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone || "Asia/Shanghai",
79
- permission: "read-only",
79
+ permission: defaultPermission,
80
80
  workspaceId: workspaces[0]?.id ?? "",
81
81
  modelKey: defaultModel === void 0 || defaultModel === null ? "default" : `${defaultModel.provider}::${defaultModel.model}`,
82
82
  reasoningEffort: "high",
@@ -228,8 +228,8 @@ function startOfWeek(value) {
228
228
  next.setDate(next.getDate() - offset);
229
229
  return next;
230
230
  }
231
- function formFromAutomation(item, workspaces = [], defaultModel) {
232
- const base = defaultFormState(/* @__PURE__ */ new Date(), workspaces, defaultModel);
231
+ function formFromAutomation(item, workspaces = [], defaultModel, defaultPermission = item.permission) {
232
+ const base = defaultFormState(/* @__PURE__ */ new Date(), workspaces, defaultModel, defaultPermission);
233
233
  const schedule = item.schedule;
234
234
  const modelKey = item.provider && item.model ? `${item.provider}::${item.model}` : "default";
235
235
  const common = {
@@ -397,9 +397,23 @@ function RunningStateDot() {
397
397
  // src/client/create-modal.tsx
398
398
  var import_react2 = require("react");
399
399
 
400
+ // src/client/permissions.ts
401
+ var BUILT_IN_PERMISSION_LABELS = /* @__PURE__ */ new Map([
402
+ ["read-only", ["preset.readOnly", "Read Only"]],
403
+ ["workspace-write", ["preset.workspaceWrite", "Workspace Write"]],
404
+ ["danger-full-access", ["preset.fullAccess", "Full access"]]
405
+ ]);
406
+ function permissionLabel(option, t) {
407
+ const builtIn = BUILT_IN_PERMISSION_LABELS.get(option.value);
408
+ if (builtIn !== void 0 && (option.name === option.value || option.name === builtIn[1])) {
409
+ return t(builtIn[0]);
410
+ }
411
+ return option.name || option.value;
412
+ }
413
+
400
414
  // src/client/create-modal-logic.ts
401
415
  function shouldConfirmFullAccess(current, next) {
402
- return current !== "full-access" && next === "full-access";
416
+ return current !== next && next === "danger-full-access";
403
417
  }
404
418
  async function adoptPickedWorkspace(input) {
405
419
  const path = await input.pick();
@@ -618,11 +632,14 @@ var MINUTES = Array.from({ length: 60 }, (_, index) => String(index).padStart(2,
618
632
  var EFFORTS = ["none", "low", "medium", "high"];
619
633
  function CreateModal({
620
634
  t,
635
+ permissionT,
621
636
  busy,
622
637
  workspaces,
623
638
  models,
624
639
  defaultModel,
625
640
  skills,
641
+ permissions,
642
+ defaultPermission,
626
643
  draft,
627
644
  editing,
628
645
  onClose,
@@ -630,9 +647,9 @@ function CreateModal({
630
647
  onAddWorkspace,
631
648
  pickWorkspaceDirectory
632
649
  }) {
633
- const [form, setForm] = (0, import_react2.useState)(() => ({ ...defaultFormState(/* @__PURE__ */ new Date(), workspaces, defaultModel), ...draft }));
650
+ const [form, setForm] = (0, import_react2.useState)(() => ({ ...defaultFormState(/* @__PURE__ */ new Date(), workspaces, defaultModel, defaultPermission), ...draft }));
634
651
  const [validationError, setValidationError] = (0, import_react2.useState)();
635
- const [confirmingFullAccess, setConfirmingFullAccess] = (0, import_react2.useState)(false);
652
+ const [confirmingPermission, setConfirmingPermission] = (0, import_react2.useState)();
636
653
  const [fullAccessAcknowledged, setFullAccessAcknowledged] = (0, import_react2.useState)(false);
637
654
  const update = (patch) => {
638
655
  setForm((current) => ({ ...current, ...patch }));
@@ -641,18 +658,19 @@ function CreateModal({
641
658
  const choosePermission = (permission) => {
642
659
  if (shouldConfirmFullAccess(form.permission, permission)) {
643
660
  setFullAccessAcknowledged(false);
644
- setConfirmingFullAccess(true);
661
+ setConfirmingPermission(permission);
645
662
  return;
646
663
  }
647
664
  update({ permission });
648
665
  };
649
666
  const cancelFullAccessConfirmation = () => {
650
667
  setFullAccessAcknowledged(false);
651
- setConfirmingFullAccess(false);
668
+ setConfirmingPermission(void 0);
652
669
  };
653
670
  const confirmFullAccess = () => {
654
671
  if (!fullAccessAcknowledged) return;
655
- update({ permission: "full-access" });
672
+ if (confirmingPermission === void 0) return;
673
+ update({ permission: confirmingPermission });
656
674
  cancelFullAccessConfirmation();
657
675
  };
658
676
  (0, import_react2.useEffect)(() => {
@@ -843,11 +861,11 @@ function CreateModal({
843
861
  up: true,
844
862
  icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ShieldIcon, { width: 14, height: 14 }),
845
863
  value: form.permission,
846
- options: [
847
- { value: "read-only", label: t("form.readOnly"), icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ShieldIcon, { width: 14, height: 14 }) },
848
- { value: "workspace-write", label: t("form.workspaceWrite"), icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ShieldIcon, { width: 14, height: 14 }) },
849
- { value: "full-access", label: t("form.fullAccess"), icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ShieldIcon, { width: 14, height: 14 }) }
850
- ],
864
+ options: permissions.map((option) => ({
865
+ value: option.value,
866
+ label: permissionLabel(option, permissionT),
867
+ icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ShieldIcon, { width: 14, height: 14 })
868
+ })),
851
869
  onChange: choosePermission
852
870
  }
853
871
  )
@@ -876,12 +894,12 @@ function CreateModal({
876
894
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
877
895
  import_dsh_client_ui_primitives.RiskConfirmation,
878
896
  {
879
- open: confirmingFullAccess,
880
- title: t("access.confirm.title"),
881
- description: t("access.confirm.description"),
882
- acknowledgeLabel: t("access.confirm.acknowledge"),
883
- cancelLabel: t("access.confirm.cancel"),
884
- confirmLabel: t("access.confirm.enable"),
897
+ open: confirmingPermission !== void 0,
898
+ title: permissionT("confirm.title"),
899
+ description: permissionT("confirm.description"),
900
+ acknowledgeLabel: permissionT("confirm.acknowledge"),
901
+ cancelLabel: permissionT("confirm.cancel"),
902
+ confirmLabel: permissionT("confirm.enable"),
885
903
  acknowledged: fullAccessAcknowledged,
886
904
  onAcknowledgedChange: setFullAccessAcknowledged,
887
905
  onCancel: cancelFullAccessConfirmation,
@@ -904,16 +922,22 @@ function ModelPicker({
904
922
  const modelLabel = selected === void 0 ? t("form.modelDefault") : selected.label;
905
923
  const effortLabel = t(`form.effort.${effort}`);
906
924
  const trigger = effort === "none" ? modelLabel : `${modelLabel} ${effortLabel}`;
925
+ const modelGroups = Array.from(models.reduce((groups, item) => {
926
+ const group = groups.get(item.provider) ?? [];
927
+ group.push(item);
928
+ groups.set(item.provider, group);
929
+ return groups;
930
+ }, /* @__PURE__ */ new Map()));
907
931
  const open = (next) => {
908
932
  setPane(next);
909
933
  menu.setOpen(true);
910
934
  };
911
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: `dsh-st-select is-pill${menu.open ? " is-open" : ""}`, ref: menu.root, children: [
935
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: `dsh-st-model-select${menu.open ? " is-open" : ""}`, ref: menu.root, children: [
912
936
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
913
937
  "button",
914
938
  {
915
939
  type: "button",
916
- className: "dsh-st-select-btn",
940
+ className: "dsh-st-model-select-trigger",
917
941
  onMouseDown: (event) => event.stopPropagation(),
918
942
  onClick: () => {
919
943
  if (menu.open) {
@@ -928,38 +952,54 @@ function ModelPicker({
928
952
  ]
929
953
  }
930
954
  ),
931
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(MenuPopup, { open: menu.open, anchor: menu.root, menuRef: menu.menu, up: true, end: true, className: "dsh-st-select-menu is-composer is-up is-end", children: [
955
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(MenuPopup, { open: menu.open, anchor: menu.root, menuRef: menu.menu, up: true, end: true, className: "dsh-st-model-select-menu is-up is-end", children: [
932
956
  pane === "root" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
933
957
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MenuRow, { kv: true, label: t("form.model"), hint: modelLabel, chevron: true, onClick: () => setPane("model") }),
934
958
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MenuRow, { kv: true, label: t("form.effort"), hint: effortLabel, chevron: true, onClick: () => setPane("effort") })
935
959
  ] }),
936
960
  pane === "model" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
937
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
938
- MenuRow,
961
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
962
+ "button",
939
963
  {
940
- label: t("form.modelDefault"),
941
- active: modelKey === "default",
964
+ type: "button",
965
+ role: "menuitemradio",
966
+ "aria-checked": modelKey === "default",
967
+ className: "dsh-st-model-option",
942
968
  onClick: () => {
943
969
  onModelKey("default");
944
970
  menu.setOpen(false);
945
- }
971
+ },
972
+ children: [
973
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-st-model-option-copy", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-st-model-name", children: t("form.modelDefault") }) }),
974
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-st-model-check", children: modelKey === "default" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CheckOutlineIcon, { width: 16, height: 16 }) })
975
+ ]
946
976
  }
947
977
  ),
948
- models.map((item) => {
949
- const value = `${item.provider}::${item.model}`;
950
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
951
- MenuRow,
952
- {
953
- label: item.label,
954
- active: value === modelKey,
955
- onClick: () => {
956
- onModelKey(value);
957
- menu.setOpen(false);
958
- }
959
- },
960
- value
961
- );
962
- })
978
+ modelGroups.map(([provider, group]) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { role: "group", "aria-label": provider, className: "dsh-st-model-group", children: [
979
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-st-model-group-title", children: provider }),
980
+ group.map((item) => {
981
+ const value = `${item.provider}::${item.model}`;
982
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
983
+ "button",
984
+ {
985
+ type: "button",
986
+ role: "menuitemradio",
987
+ "aria-checked": value === modelKey,
988
+ className: "dsh-st-model-option",
989
+ title: item.label,
990
+ onClick: () => {
991
+ onModelKey(value);
992
+ menu.setOpen(false);
993
+ },
994
+ children: [
995
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-st-model-option-copy", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-st-model-name", children: item.label }) }),
996
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-st-model-check", children: value === modelKey && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CheckOutlineIcon, { width: 16, height: 16 }) })
997
+ ]
998
+ },
999
+ value
1000
+ );
1001
+ })
1002
+ ] }, provider))
963
1003
  ] }),
964
1004
  pane === "effort" && EFFORTS.map((item) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
965
1005
  MenuRow,
@@ -1193,7 +1233,7 @@ var EXAMPLES = [
1193
1233
  { name: "\u6BCF\u5468\u4F9D\u8D56\u5DE1\u68C0", scheduleKind: "weekly", time: "10:00", weekdays: [1] },
1194
1234
  { name: "\u5DE5\u4F5C\u65E5\u65E9\u62A5", scheduleKind: "weekly", time: "08:00", weekdays: [1, 2, 3, 4, 5] }
1195
1235
  ];
1196
- function AutomationView({ t, runtime, closeSettings, pickWorkspaceDirectory }) {
1236
+ function AutomationView({ t, permissionT, runtime, closeSettings, pickWorkspaceDirectory }) {
1197
1237
  const state = (0, import_react3.useSyncExternalStore)(runtime.source.subscribe, runtime.source.getSnapshot, runtime.source.getSnapshot);
1198
1238
  const [tab, setTab] = (0, import_react3.useState)("mine");
1199
1239
  const [query, setQuery] = (0, import_react3.useState)("");
@@ -1218,6 +1258,8 @@ function AutomationView({ t, runtime, closeSettings, pickWorkspaceDirectory }) {
1218
1258
  const snapshot = state.snapshot;
1219
1259
  const workspaces = snapshot?.workspaces ?? [];
1220
1260
  const models = snapshot?.models ?? [];
1261
+ const permissions = snapshot?.permissions ?? [];
1262
+ const defaultPermission = snapshot?.defaultPermission ?? "";
1221
1263
  const automations = (snapshot?.automations ?? []).filter((item) => query.trim() === "" || `${item.name} ${item.prompt}`.toLowerCase().includes(query.trim().toLowerCase())).slice().sort((left, right) => Date.parse(right.createdAt) - Date.parse(left.createdAt));
1222
1264
  const runs = (snapshot?.runs ?? []).filter((run) => {
1223
1265
  if (historyTask !== "all" && run.automationId !== historyTask) return false;
@@ -1242,13 +1284,14 @@ function AutomationView({ t, runtime, closeSettings, pickWorkspaceDirectory }) {
1242
1284
  setEditingId(void 0);
1243
1285
  };
1244
1286
  const openCreate = (partial) => {
1287
+ if (defaultPermission === "" || permissions.length === 0) return;
1245
1288
  setEditingId(void 0);
1246
1289
  setDraft(partial);
1247
1290
  setCreating(true);
1248
1291
  };
1249
1292
  const openEdit = (item) => {
1250
1293
  setEditingId(item.id);
1251
- setDraft(formFromAutomation(item, workspaces, snapshot?.defaultModel ?? null));
1294
+ setDraft(formFromAutomation(item, workspaces, snapshot?.defaultModel ?? null, snapshot?.defaultPermission ?? item.permission));
1252
1295
  setCreating(true);
1253
1296
  };
1254
1297
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dsh-st-shell", children: [
@@ -1266,7 +1309,7 @@ function AutomationView({ t, runtime, closeSettings, pickWorkspaceDirectory }) {
1266
1309
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ChatIcon, {}),
1267
1310
  t("action.chatCreate")
1268
1311
  ] }),
1269
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("button", { type: "button", className: "dsh-st-btn dsh-st-btn--primary", onClick: () => openCreate(), children: [
1312
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("button", { type: "button", className: "dsh-st-btn dsh-st-btn--primary", disabled: defaultPermission === "" || permissions.length === 0, onClick: () => openCreate(), children: [
1270
1313
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PlusIcon, {}),
1271
1314
  t("action.create")
1272
1315
  ] }),
@@ -1286,6 +1329,7 @@ function AutomationView({ t, runtime, closeSettings, pickWorkspaceDirectory }) {
1286
1329
  {
1287
1330
  type: "button",
1288
1331
  className: "dsh-st-example",
1332
+ disabled: defaultPermission === "" || permissions.length === 0,
1289
1333
  onClick: () => openCreate({
1290
1334
  name: t(`examples.${index + 1}.title`),
1291
1335
  prompt: t(`examples.${index + 1}.body`),
@@ -1361,11 +1405,14 @@ function AutomationView({ t, runtime, closeSettings, pickWorkspaceDirectory }) {
1361
1405
  CreateModal,
1362
1406
  {
1363
1407
  t,
1408
+ permissionT,
1364
1409
  busy,
1365
1410
  workspaces,
1366
1411
  models,
1367
1412
  defaultModel: snapshot?.defaultModel ?? null,
1368
1413
  skills: snapshot?.skills ?? [],
1414
+ permissions,
1415
+ defaultPermission,
1369
1416
  onAddWorkspace: async (path) => runtime.addWorkspace(path).then((value) => value.id),
1370
1417
  ...pickWorkspaceDirectory === void 0 ? {} : { pickWorkspaceDirectory },
1371
1418
  editing: editingId !== void 0,
@@ -1595,16 +1642,6 @@ var en = {
1595
1642
  "form.days": "Weekdays",
1596
1643
  "form.timeZone": "Time zone",
1597
1644
  "form.permission": "Permission boundary",
1598
- "form.readOnly": "Read Only",
1599
- "form.readOnlyHint": "Inspect the workspace without changing files.",
1600
- "form.workspaceWrite": "Workspace Write",
1601
- "form.fullAccess": "Full access",
1602
- "form.workspaceWriteHint": "May change files in this workspace; past approvals are not reused.",
1603
- "access.confirm.title": "Enable Full access?",
1604
- "access.confirm.description": "Full access reduces confirmation steps and allows the Agent to directly perform more operations, including sensitive operations, file changes, or external commands. Use it only when you trust this task.",
1605
- "access.confirm.acknowledge": "I understand the risks and want to continue",
1606
- "access.confirm.cancel": "Cancel",
1607
- "access.confirm.enable": "Enable Full access",
1608
1645
  "form.cancel": "Cancel",
1609
1646
  "form.submit": "Create automation",
1610
1647
  "form.submitting": "Creating\u2026",
@@ -1632,9 +1669,6 @@ var en = {
1632
1669
  "card.nextRun": "Next",
1633
1670
  "card.lastRun": "Last",
1634
1671
  "card.never": "Not yet run",
1635
- "card.permission.read-only": "Read-only",
1636
- "card.permission.workspace-write": "Workspace write",
1637
- "card.permission.full-access": "Full access",
1638
1672
  "schedule.onceAt": "Once \xB7 {time}",
1639
1673
  "schedule.everyMinutes": "Every {count} minutes",
1640
1674
  "schedule.dailyAt": "Daily \xB7 {time}",
@@ -1800,16 +1834,6 @@ var zh = {
1800
1834
  "form.days": "\u661F\u671F",
1801
1835
  "form.timeZone": "\u65F6\u533A",
1802
1836
  "form.permission": "\u6743\u9650\u8FB9\u754C",
1803
- "form.readOnly": "Read Only",
1804
- "form.readOnlyHint": "\u53EF\u4EE5\u68C0\u67E5\u5DE5\u4F5C\u533A\uFF0C\u4F46\u4E0D\u4F1A\u4FEE\u6539\u6587\u4EF6\u3002",
1805
- "form.workspaceWrite": "Workspace Write",
1806
- "form.fullAccess": "Full access",
1807
- "form.workspaceWriteHint": "\u53EF\u4EE5\u4FEE\u6539\u5F53\u524D\u5DE5\u4F5C\u533A\u6587\u4EF6\uFF1B\u4E0D\u4F1A\u7EE7\u627F\u5386\u53F2\u6279\u51C6\u3002",
1808
- "access.confirm.title": "\u786E\u8BA4\u542F\u7528 \u5B8C\u5168\u8BBF\u95EE\uFF1F",
1809
- "access.confirm.description": "\u542F\u7528 \u5B8C\u5168\u8BBF\u95EE \u540E\uFF0Cagent \u5C06\u51CF\u5C11\u786E\u8BA4\u6B65\u9AA4\uFF0C\u5E76\u4E14\u53EF\u4EE5\u76F4\u63A5\u6267\u884C\u66F4\u591A\u64CD\u4F5C\uFF0C\u5305\u62EC\u654F\u611F\u64CD\u4F5C\u3001\u6587\u4EF6\u4FEE\u6539\u6216\u5916\u90E8\u547D\u4EE4\u3002\u4EC5\u5EFA\u8BAE\u5728\u4F60\u4FE1\u4EFB\u5F53\u524D\u4EFB\u52A1\u65F6\u4F7F\u7528\u3002",
1810
- "access.confirm.acknowledge": "\u6211\u5DF2\u4E86\u89E3\u98CE\u9669\uFF0C\u5E76\u613F\u610F\u7EE7\u7EED",
1811
- "access.confirm.cancel": "\u53D6\u6D88",
1812
- "access.confirm.enable": "\u542F\u7528 \u5B8C\u5168\u8BBF\u95EE",
1813
1837
  "form.cancel": "\u53D6\u6D88",
1814
1838
  "form.submit": "\u521B\u5EFA\u81EA\u52A8\u5316",
1815
1839
  "form.submitting": "\u521B\u5EFA\u4E2D\u2026",
@@ -1837,9 +1861,6 @@ var zh = {
1837
1861
  "card.nextRun": "\u4E0B\u6B21",
1838
1862
  "card.lastRun": "\u6700\u8FD1",
1839
1863
  "card.never": "\u5C1A\u672A\u8FD0\u884C",
1840
- "card.permission.read-only": "\u53EA\u8BFB",
1841
- "card.permission.workspace-write": "\u53EF\u5199\u5DE5\u4F5C\u533A",
1842
- "card.permission.full-access": "Full access",
1843
1864
  "schedule.onceAt": "\u5355\u6B21 \xB7 {time}",
1844
1865
  "schedule.everyMinutes": "\u6BCF {count} \u5206\u949F",
1845
1866
  "schedule.dailyAt": "\u6BCF\u5929 \xB7 {time}",
@@ -2925,6 +2946,7 @@ var CSS_TEXT = `
2925
2946
  .dsh-st-chip-btn:hover{background:rgba(255,255,255,.06);color:var(--dsw-alias-label-primary)}
2926
2947
  .dsh-st-chip-btn.is-static{cursor:default;opacity:.78}
2927
2948
  .dsh-st-chip-btn em,.dsh-st-select.is-pill .dsh-st-select-btn em{width:6px;height:6px;margin-left:2px;opacity:.55}
2949
+ .dsh-st-model-select{position:relative;z-index:1;min-width:0;flex:none}.dsh-st-model-select.is-open{z-index:30}.dsh-st-model-select-trigger{display:flex;align-items:center;gap:4px;min-width:0;max-width:220px;height:28px;padding:0 4px 0 8px;border:0;border-radius:24px;background:transparent;color:var(--dsw-alias-label-secondary);font-size:13px;font-weight:500;line-height:20px;cursor:pointer}.dsh-st-model-select-trigger:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.dsh-st-model-select-trigger>span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dsh-st-model-select-trigger em{width:6px;height:6px;margin-left:2px;border-right:1.5px solid currentColor;border-bottom:1.5px solid currentColor;transform:rotate(45deg) translateY(-2px);opacity:.55;flex:none}.dsh-st-model-select-menu{z-index:30;width:min(240px,calc(100vw - 32px));max-height:min(360px,calc(100vh - 96px));overflow:hidden;padding:4px;border:1px solid var(--dsw-alias-border-inverted,var(--dsw-alias-border-l2));border-radius:12px;background:var(--dsw-specific-menu,var(--dsw-alias-bg-base));box-shadow:var(--dsw-shadow-lv3)}.dsh-st-model-select-menu.is-float{position:absolute;z-index:1200;box-sizing:border-box}.dsh-st-model-select-menu .dsh-st-menu-row{min-height:40px;padding:0 10px;border-radius:10px;font-size:14px}.dsh-st-model-select-menu .dsh-st-menu-row.is-kv .dsh-st-menu-row-side{font-size:13px;color:var(--dsw-alias-label-tertiary)}.dsh-st-model-group+.dsh-st-model-group{margin-top:4px}.dsh-st-model-group-title{position:sticky;top:0;z-index:1;padding:5px 8px 3px;background:var(--dsw-specific-menu,var(--dsw-alias-bg-base));color:var(--dsw-alias-label-tertiary);font-size:12px;font-weight:500;line-height:18px}.dsh-st-model-option{display:flex;width:100%;min-height:38px;align-items:center;gap:8px;padding:6px 8px;border:0;border-radius:10px;background:transparent;color:var(--dsw-alias-label-primary);text-align:left;cursor:pointer}.dsh-st-model-option:hover,.dsh-st-model-option:focus-visible{background:var(--dsw-alias-interactive-bg-hover);outline:none}.dsh-st-model-option-copy{display:flex;min-width:0;flex:1;flex-direction:column}.dsh-st-model-name{overflow:hidden;font-size:14px;font-weight:500;line-height:20px;text-overflow:ellipsis;white-space:nowrap}.dsh-st-model-check{display:grid;flex:0 0 18px;place-items:center;color:var(--dsw-alias-label-primary)}
2928
2950
  .dsh-st-suffix{color:var(--dsw-alias-label-secondary);font-size:13px}
2929
2951
  .dsh-st-inline input.is-narrow{width:72px;flex:none}
2930
2952
  .dsh-st-time{display:inline-flex;align-items:center;gap:2px;min-height:36px;padding:0 8px;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:rgba(255,255,255,.04)}
@@ -3007,6 +3029,7 @@ function apply(ctx) {
3007
3029
  ctx.effect(() => installStyles(), "dsh-automation: styles");
3008
3030
  ctx.effect(() => ctx.locale.register(NS, { zh, en }), "dsh-automation: locale");
3009
3031
  const t = ctx.locale.bind(NS);
3032
+ const permissionT = ctx.locale.bind("permission.access");
3010
3033
  const runtime = createAutomationRuntime(ctx.connection.rpc);
3011
3034
  ctx.effect(() => installSettingsNavIcon(() => [t("tab"), "Scheduled tasks", "\u5B9A\u65F6\u4EFB\u52A1"]), "dsh-automation: settings icon");
3012
3035
  ctx.slots.inject("settings.section", () => ctx.slots.register({
@@ -3018,7 +3041,7 @@ function apply(ctx) {
3018
3041
  icon: "schedule"
3019
3042
  }, function ScheduledTasksSettings(props) {
3020
3043
  const workspacesApi = readOptionalWorkspaces(ctx);
3021
- return (0, import_react7.createElement)(AutomationView, { t, runtime, ...props.close === void 0 ? {} : { closeSettings: props.close }, ...workspacesApi === void 0 ? {} : { pickWorkspaceDirectory: () => workspacesApi.pickDirectory() } });
3044
+ return (0, import_react7.createElement)(AutomationView, { t, permissionT, runtime, ...props.close === void 0 ? {} : { closeSettings: props.close }, ...workspacesApi === void 0 ? {} : { pickWorkspaceDirectory: () => workspacesApi.pickDirectory() } });
3022
3045
  }));
3023
3046
  ctx.slots.inject("sidebar.schedule", () => ctx.slots.register({
3024
3047
  name: "sidebar.schedule",