dsh-bots 0.2.12 → 0.2.14

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.
Files changed (2) hide show
  1. package/lib/client.js +150 -3
  2. package/package.json +1 -1
package/lib/client.js CHANGED
@@ -272,7 +272,12 @@
272
272
  const StateDot = NATIVE.StateDot ?? null;
273
273
  // ---- CSS: own stable class names, values mirrored from the shell ----
274
274
  const CSS = `
275
- .dbs-nav{flex:1;min-height:0;display:flex;flex-direction:column;gap:2px;font-family:var(--dsw-font-family,inherit)}
275
+ /* The native regionArea bleeds right by --dsh-sidebar-inline-padding
276
+ (margin-right: -12px, ui-sidebar.js) for its own scroll model; our rows
277
+ don't follow that bleed, so pull the nav back inside the padded column —
278
+ verified in the live page: removing the parent's margin-right reads
279
+ "normal", this counter-margin is the same geometry from our side. */
280
+ .dbs-nav{flex:1;min-height:0;display:flex;flex-direction:column;gap:2px;font-family:var(--dsw-font-family,inherit);margin-right:var(--dsh-sidebar-inline-padding,12px)}
276
281
  .dbs-navGroup{display:flex;flex-direction:column;min-height:0}
277
282
  .dbs-navGroup[data-open="true"]{flex:1 1 auto}
278
283
  .dbs-navGroup[data-open="false"]{flex:none}
@@ -323,6 +328,7 @@
323
328
  .dbs-fileChip{display:inline-flex;align-items:center;gap:5px;padding:3px 10px;border-radius:999px;background:var(--dsw-alias-interactive-bg-hover);font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary);cursor:pointer;max-width:100%;user-select:none}
324
329
  .dbs-fileChip:hover{color:var(--dsw-alias-label-primary)}
325
330
  .dbs-fileChipName{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
331
+ .dbs-setLabel{font-size:12px;line-height:16px;color:var(--dsw-alias-label-tertiary);margin:8px 0 4px}
326
332
  .dbs-chatbar{flex:none;display:flex;align-items:center;gap:8px;height:44px;padding:0 12px;border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08))}
327
333
  .dbs-chatbarName{font-size:14px;line-height:20px;font-weight:600;color:var(--dsw-alias-label-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0}
328
334
  .dbs-scrollBody{scrollbar-gutter:stable;flex-direction:column;flex:1;min-height:0;display:flex;overflow:hidden auto}
@@ -458,6 +464,16 @@
458
464
  'chat.stop.noop': '当前没有进行中的生成',
459
465
  'media.openFailed': '打开失败,文件可能已移动或被删除',
460
466
  'chat.members.manage': '管理成员',
467
+ 'chat.settings': '设置',
468
+ 'chat.settings.title': '会话设置',
469
+ 'chat.settings.name': '名称',
470
+ 'chat.settings.desc': '简介',
471
+ 'chat.settings.workspace': '工作区隔离',
472
+ 'chat.settings.root': '工作区根目录',
473
+ 'chat.settings.rootPh': '留空 = 不启用',
474
+ 'chat.settings.paths': '额外可写路径(逗号分隔)',
475
+ 'chat.settings.pathsPh': '/tmp/xxx, /Users/you/shared',
476
+ 'chat.settings.hint': '写入被沙盒限制在工作区(含额外路径)内,读取不受限;同 slug 的 Bot 共享同一目录。',
461
477
  'modal.members.title': '管理群成员',
462
478
  'modal.members.hint': '勾选的 Bot 为群成员;保存后立即生效(可随时再改)。',
463
479
  'action.delete': '删除',
@@ -560,6 +576,16 @@
560
576
  'chat.stop.noop': 'No generation in progress',
561
577
  'media.openFailed': 'Open failed — the file may have moved or been deleted',
562
578
  'chat.members.manage': 'Manage members',
579
+ 'chat.settings': 'Settings',
580
+ 'chat.settings.title': 'Session settings',
581
+ 'chat.settings.name': 'Name',
582
+ 'chat.settings.desc': 'Description',
583
+ 'chat.settings.workspace': 'Workspace jail',
584
+ 'chat.settings.root': 'Workspace root',
585
+ 'chat.settings.rootPh': 'Empty = disabled',
586
+ 'chat.settings.paths': 'Extra writable paths (comma-separated)',
587
+ 'chat.settings.pathsPh': '/tmp/xxx, /Users/you/shared',
588
+ 'chat.settings.hint': 'Writes are sandboxed to the workspace (plus extra paths); reads stay unrestricted. Bots sharing a slug share one directory.',
563
589
  'modal.members.title': 'Manage group members',
564
590
  'modal.members.hint': 'Checked bots are members; changes apply immediately on save (editable again anytime).',
565
591
  'action.delete': 'Delete',
@@ -762,6 +788,7 @@
762
788
  confirmDelete: null,
763
789
  /** Manage-members dialog: group agentId | null (system modal). */
764
790
  manageMembers: null,
791
+ settingsAgentId: null,
765
792
  /** Bumped on a language switch so module-scope `t` output re-renders. */
766
793
  localeRev: 0,
767
794
  };
@@ -1380,6 +1407,13 @@
1380
1407
  return null;
1381
1408
  return e('div', { className: 'dbs-mediaRefs' }, imgs.map((r) => e(MediaImage, { key: r, path: r })), files.map((r) => e(FileChip, { key: r, path: r })));
1382
1409
  }
1410
+ /**
1411
+ * Settings glyph — three sliders, drawn inline (the primitives package
1412
+ * exposes no verified gear export; same policy as SendUpIcon).
1413
+ */
1414
+ function SettingsGlyph() {
1415
+ return e('svg', { viewBox: '0 0 16 16', width: '16', height: '16', 'aria-hidden': true }, e('g', { stroke: 'currentColor', strokeWidth: 1.5, strokeLinecap: 'round' }, e('line', { x1: 2, y1: 4, x2: 14, y2: 4 }), e('line', { x1: 2, y1: 8, x2: 14, y2: 8 }), e('line', { x1: 2, y1: 12, x2: 14, y2: 12 })), e('g', { fill: 'currentColor' }, e('circle', { cx: 10, cy: 4, r: 2 }), e('circle', { cx: 5.5, cy: 8, r: 2 }), e('circle', { cx: 11.5, cy: 12, r: 2 })));
1416
+ }
1383
1417
  function ToolCard(p) {
1384
1418
  const [open, setOpen] = React.useState(false);
1385
1419
  const en = p.entry;
@@ -1695,12 +1729,16 @@
1695
1729
  variant: 'ghost', size: 'sm', title: t('action.close'), 'aria-label': t('action.close'),
1696
1730
  icon: Ico('IconCloseOutline16', { size: 16 }),
1697
1731
  onClick: () => patch({ chatAgentId: null }),
1698
- }), agent !== null ? e(Avatar, { agent, size: 24 }) : null, e('span', { className: 'dbs-chatbarName' }, agent?.name ?? t('chat.loading')), e('span', { className: 'dbs-meta' }, isGroup ? t('chat.group', { n: memberNames.length }) : t('chat.single')), isGroup
1732
+ }), agent !== null ? e(Avatar, { agent, size: 24 }) : null, e('span', { className: 'dbs-chatbarName' }, agent?.name ?? t('chat.loading')), e('span', { className: 'dbs-meta' }, isGroup ? t('chat.group', { n: memberNames.length }) : t('chat.single')), e('span', { style: { flex: 1 } }), isGroup
1699
1733
  ? e(Button, {
1700
1734
  variant: 'ghost', size: 'sm', title: t('chat.members.manage'), 'aria-label': t('chat.members.manage'),
1701
1735
  onClick: () => patch({ manageMembers: p.agentId }),
1702
1736
  }, t('chat.members.manage'))
1703
- : null, e('span', { style: { flex: 1 } })), error !== null
1737
+ : null, e('button', {
1738
+ className: 'dbs-railBtn', type: 'button',
1739
+ title: t('chat.settings'), 'aria-label': t('chat.settings'),
1740
+ onClick: () => patch({ settingsAgentId: p.agentId }),
1741
+ }, e(SettingsGlyph, null))), error !== null
1704
1742
  ? e('div', { className: 'dbs-error', onClick: () => setError(null) }, error)
1705
1743
  : null, e('div', { className: 'dbs-scrollArea' }, e('div', { className: 'dbs-scrollBody', ref: scrollRef, onScroll: onScrollBody }, e('div', { className: 'dbs-scroll' }, e('div', { className: 'dbs-column' }, entries === null
1706
1744
  ? e('div', { className: 'dbs-skeleton' }, e('div', { className: 'dbs-skelRow', style: { width: '42%' } }), e('div', { className: 'dbs-skelRow', style: { width: '76%' } }), e('div', { className: 'dbs-skelRow', style: { width: '58%' } }))
@@ -2161,6 +2199,113 @@
2161
2199
  onClick: () => void submit(),
2162
2200
  }, working ? t('action.saving') : t('action.save')))));
2163
2201
  }
2202
+ /**
2203
+ * Per-session settings: name (+ description) for every chat; singles
2204
+ * additionally get the §14 workspace-jail editor (root + extra writable
2205
+ * paths). updateAgent merges only the name/description/title/avatar
2206
+ * fields engine-side, so saving cannot clobber the rest of a profile;
2207
+ * an empty root removes the jail (workspaceRoot: null).
2208
+ */
2209
+ function AgentSettingsModal() {
2210
+ const s = useStore();
2211
+ const agent = agentById(s.settingsAgentId);
2212
+ const isGroup = agent?.isGroup === true;
2213
+ const [name, setName] = React.useState('');
2214
+ const [desc, setDesc] = React.useState('');
2215
+ const [root, setRoot] = React.useState('');
2216
+ const [paths, setPaths] = React.useState('');
2217
+ const [wsLoaded, setWsLoaded] = React.useState(false);
2218
+ const [working, setWorking] = React.useState(false);
2219
+ const [err, setErr] = React.useState(null);
2220
+ React.useEffect(() => {
2221
+ if (agent === null || agent === undefined)
2222
+ return;
2223
+ setName(String(agent.name ?? ''));
2224
+ setDesc(String(agent.description ?? ''));
2225
+ if (agent.isGroup === true) {
2226
+ setWsLoaded(true);
2227
+ return;
2228
+ }
2229
+ let alive = true;
2230
+ botsCall('workspaceGet', { agentId: agent.id })
2231
+ .then((c) => {
2232
+ if (alive === false)
2233
+ return;
2234
+ setRoot(c.workspaceRoot ?? '');
2235
+ setPaths((c.allowPaths ?? []).join(', '));
2236
+ setWsLoaded(true);
2237
+ })
2238
+ .catch(() => { if (alive)
2239
+ setWsLoaded(true); });
2240
+ return () => { alive = false; };
2241
+ // eslint-disable-next-line react-hooks/exhaustive-deps
2242
+ }, [s.settingsAgentId]);
2243
+ if (agent === null || agent === undefined)
2244
+ return null;
2245
+ const valid = name.trim() !== '';
2246
+ async function save() {
2247
+ if (working || valid === false)
2248
+ return;
2249
+ setWorking(true);
2250
+ setErr(null);
2251
+ try {
2252
+ await botsCall('update', { id: agent.id, profile: { name: name.trim(), description: desc.trim() } });
2253
+ if (agent.isGroup !== true && wsLoaded === true) {
2254
+ const list = paths.split(/[,,]/).map((x) => x.trim()).filter((x) => x !== '');
2255
+ await botsCall('workspaceSet', {
2256
+ agentId: agent.id,
2257
+ workspaceRoot: root.trim() === '' ? null : root.trim(),
2258
+ allowPaths: list,
2259
+ });
2260
+ }
2261
+ patch({ settingsAgentId: null });
2262
+ await refreshAgents();
2263
+ return;
2264
+ }
2265
+ catch (e2) {
2266
+ setErr(String(e2?.message ?? e2));
2267
+ }
2268
+ setWorking(false);
2269
+ }
2270
+ return e('div', {
2271
+ className: 'dbs-modalBackdrop',
2272
+ onClick: () => { if (working === false)
2273
+ patch({ settingsAgentId: null }); },
2274
+ }, e('div', {
2275
+ className: 'dbs-modalCard', role: 'dialog', 'aria-modal': true,
2276
+ 'aria-label': t('chat.settings.title'),
2277
+ onClick: (ev) => { ev.stopPropagation(); },
2278
+ }, e('div', { className: 'dbs-modalTitleRow' }, e('span', { className: 'dbs-modalTitle' }, t('chat.settings.title')), e(Button, {
2279
+ variant: 'ghost', size: 'sm', title: t('action.close'), 'aria-label': t('action.close'),
2280
+ disabled: working,
2281
+ icon: Ico('IconCloseOutline16', { size: 16 }),
2282
+ onClick: () => patch({ settingsAgentId: null }),
2283
+ })), e('div', { className: 'dbs-modalBody' }, e('div', { className: 'dbs-setLabel' }, t('chat.settings.name')), e(Input, {
2284
+ value: name, autoFocus: true, disabled: working,
2285
+ onChange: (ev) => setName(ev.target.value),
2286
+ }), isGroup === false
2287
+ ? e('div', null, e('div', { className: 'dbs-setLabel' }, t('chat.settings.desc')), e(Input, {
2288
+ value: desc, disabled: working,
2289
+ onChange: (ev) => setDesc(ev.target.value),
2290
+ }), e('div', { className: 'dbs-setLabel', style: { marginTop: 12, fontWeight: 600 } }, t('chat.settings.workspace')), e('div', { className: 'dbs-setLabel' }, t('chat.settings.root')), e(Input, {
2291
+ value: root, disabled: working || wsLoaded === false,
2292
+ placeholder: t('chat.settings.rootPh'),
2293
+ onChange: (ev) => setRoot(ev.target.value),
2294
+ }), e('div', { className: 'dbs-setLabel' }, t('chat.settings.paths')), e(Input, {
2295
+ value: paths, disabled: working || wsLoaded === false,
2296
+ placeholder: t('chat.settings.pathsPh'),
2297
+ onChange: (ev) => setPaths(ev.target.value),
2298
+ }), e('div', { className: 'dbs-meta', style: { marginTop: 8 } }, t('chat.settings.hint')))
2299
+ : null, err !== null
2300
+ ? e('div', { className: 'dbs-error', onClick: () => { setErr(null); } }, err)
2301
+ : null), e('div', { className: 'dbs-modalFooter' }, e(Button, {
2302
+ variant: 'ghost', size: 'sm', disabled: working,
2303
+ onClick: () => patch({ settingsAgentId: null }),
2304
+ }, t('action.cancel')), e(Button, {
2305
+ variant: 'primary', size: 'sm', disabled: valid === false || working,
2306
+ onClick: () => void save(),
2307
+ }, working === true ? t('action.saving') : t('action.save')))));
2308
+ }
2164
2309
  /** Delete confirmation modal: same system-dialog chrome as CreateModal. */
2165
2310
  function ConfirmDeleteModal() {
2166
2311
  const s = useStore();
@@ -2239,6 +2384,8 @@
2239
2384
  layers.push(e(CreateModal, { key: 'create' }));
2240
2385
  if (s.manageMembers !== null)
2241
2386
  layers.push(e(ManageMembersModal, { key: 'manage-members' }));
2387
+ if (s.settingsAgentId !== null)
2388
+ layers.push(e(AgentSettingsModal, { key: 'agent-settings' }));
2242
2389
  if (s.confirmDelete !== null)
2243
2390
  layers.push(e(ConfirmDeleteModal, { key: 'confirm-delete' }));
2244
2391
  return layers.length === 0 ? null : e('div', null, layers);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-bots",
3
- "version": "0.2.12",
3
+ "version": "0.2.14",
4
4
  "description": "Multi-bot workbench for DeepSeek Harness: bridges the sdk-bots orchestration gateway (group chats, single bots) into the dsh web shell with official-styled UI.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",