@dsh-workbench/shared 0.2.14 → 0.2.15

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/dist/api/dsh.d.ts CHANGED
@@ -152,6 +152,7 @@ export interface DshToolDefinition {
152
152
  export interface DshToolRunContext {
153
153
  readonly agent?: {
154
154
  readonly session?: {
155
+ readonly id?: string;
155
156
  readonly header?: {
156
157
  readonly cwd?: string;
157
158
  } | undefined;
@@ -61,6 +61,26 @@
61
61
  box-shadow: none;
62
62
  }
63
63
 
64
+ .wb-selectIconOnly {
65
+ width: var(--wb-len-36);
66
+ height: var(--wb-len-36);
67
+ min-height: 0;
68
+ gap: 0;
69
+ padding: 0;
70
+ justify-content: center;
71
+ }
72
+
73
+ .wb-selectReveal {
74
+ display: inline-flex;
75
+ align-items: center;
76
+ justify-content: center;
77
+ }
78
+
79
+ .wb-selectIconOnly .wb-selectIcon {
80
+ width: 100%;
81
+ height: 100%;
82
+ }
83
+
64
84
  .wb-selectAnchor {
65
85
  display: block;
66
86
  width: 100%;
@@ -42,6 +42,8 @@ export interface SelectFieldProps {
42
42
  readonly onFooter?: (() => void) | undefined;
43
43
  readonly selection?: 'check' | 'fill' | undefined;
44
44
  readonly variant?: 'ghost' | 'boxed' | undefined;
45
+ readonly iconOnly?: boolean | undefined;
46
+ readonly revealId?: string | undefined;
45
47
  readonly ariaLabel?: string | undefined;
46
48
  readonly title?: string | undefined;
47
49
  readonly id?: string | undefined;
@@ -54,5 +56,5 @@ export declare function defaultSelectFilter(option: SelectFieldOption, query: st
54
56
  export declare function filterSelectOptions(options: readonly SelectFieldOption[], query: string, filter?: (option: SelectFieldOption, query: string) => boolean): readonly SelectFieldOption[];
55
57
  export declare function selectCursorValue(shown: readonly SelectFieldOption[], cursor: number | null): string | null;
56
58
  export declare function stepSelectCursor(shown: readonly SelectFieldOption[], cursor: number | null, step: 1 | -1): number | null;
57
- export declare function selectEntries(shown: readonly SelectFieldOption[], emptyText: string | undefined): readonly MenuEntry[];
59
+ export declare function selectEntries(shown: readonly SelectFieldOption[], emptyText: string | undefined, revealId?: string | undefined): readonly MenuEntry[];
58
60
  export declare function SelectField(props: SelectFieldProps): ReactNode;
@@ -1,35 +1,51 @@
1
1
  import { h, useEffect, useMemo, useRef, useState } from '../lib/react.js';
2
- import { IconChevronDownOutlineRegular, IconSearchOutlineRegular, Menu, TextShimmer, } from '@deepseek-ai/dsh-client-ui-primitives';
2
+ import { IconSearchOutlineRegular, Menu } from '@deepseek-ai/dsh-client-ui-primitives';
3
+ import { selectLoadingNode, selectTriggerNode } from './SelectFieldNodes.js';
3
4
  import { Field } from './Field.js';
4
5
  import { TextField } from './TextField.js';
5
- import { Spinner } from './Spinner.js';
6
6
  import './SelectField.css';
7
7
  const FOOTER_ID = '__wb-select-footer__';
8
8
  const EMPTY_ID = '__wb-select-empty__';
9
9
  function mergeClass(...parts) {
10
10
  return parts.filter((one) => one !== undefined && one !== '').join(' ');
11
11
  }
12
- function itemOf(option) {
12
+ function triggerClassOf(variantClass, iconOnly) {
13
+ return mergeClass('wb-selectTrigger', variantClass, iconOnly ? 'wb-selectIconOnly' : undefined);
14
+ }
15
+ function itemOf(option, revealId) {
16
+ const mark = option.value === revealId;
17
+ if (!mark) {
18
+ return {
19
+ id: option.value,
20
+ label: option.label,
21
+ ...(option.icon === undefined ? {} : { icon: option.icon }),
22
+ ...(option.disabled === true ? { disabled: true } : {}),
23
+ };
24
+ }
25
+ const revealed = (node) => (h("span", { className: "wb-selectReveal", "data-wb-select-reveal": "" }, node));
13
26
  return {
14
27
  id: option.value,
15
- label: option.label,
16
- ...(option.icon === undefined ? {} : { icon: option.icon }),
28
+ label: option.icon === undefined ? revealed(option.label) : option.label,
29
+ ...(option.icon === undefined ? {} : { icon: revealed(option.icon) }),
17
30
  ...(option.disabled === true ? { disabled: true } : {}),
18
31
  };
19
32
  }
20
- function triggerNode(args) {
21
- return (h("button", { type: "button", id: args.id, className: args.className, disabled: args.disabled, "aria-haspopup": "listbox", "aria-expanded": args.open, "aria-invalid": args.invalid || undefined, "aria-label": args.ariaLabel, title: args.title, onClick: args.onOpen },
22
- args.icon === undefined ? null : h("span", { className: "wb-selectIcon" }, args.icon),
23
- h("span", { className: "wb-selectValue" }, args.value),
24
- h("span", { className: "wb-selectChevron" },
25
- h(IconChevronDownOutlineRegular, { size: 14 }))));
33
+ function revealNode() {
34
+ const found = document.querySelector('[data-wb-select-reveal]');
35
+ return found instanceof HTMLElement ? found : null;
36
+ }
37
+ function useRevealOnOpen(open, revealId) {
38
+ useEffect(() => {
39
+ if (!open || revealId === undefined)
40
+ return;
41
+ revealNode()?.scrollIntoView({ block: 'nearest' });
42
+ }, [open, revealId]);
26
43
  }
27
- function loadingNode(args) {
28
- return (h("button", { type: "button", id: args.id, className: `${args.className} wb-selectLoading`, disabled: true, "aria-busy": "true", "aria-invalid": args.invalid || undefined, "aria-label": args.ariaLabel, title: args.loadingText },
29
- h("span", { className: "wb-selectValue" },
30
- h(TextShimmer, { active: true }, args.loadingText ?? '…')),
31
- h("span", { className: "wb-selectChevron" },
32
- h(Spinner, { size: 14 }))));
44
+ function selectValueNode(current, renderValue, placeholder) {
45
+ if (current === undefined) {
46
+ return h("span", { className: "wb-selectPlaceholder" }, placeholder ?? '—');
47
+ }
48
+ return renderValue === undefined ? current.label : renderValue(current);
33
49
  }
34
50
  function searchNode(args) {
35
51
  return (h("span", { ref: args.boxRef, className: args.className },
@@ -80,7 +96,7 @@ export function stepSelectCursor(shown, cursor, step) {
80
96
  return step === 1 ? 0 : shown.length - 1;
81
97
  return (cursor + step + shown.length) % shown.length;
82
98
  }
83
- export function selectEntries(shown, emptyText) {
99
+ export function selectEntries(shown, emptyText, revealId) {
84
100
  if (shown.length === 0) {
85
101
  return emptyText === undefined ? [] : [{ type: 'label', id: EMPTY_ID, text: emptyText }];
86
102
  }
@@ -91,7 +107,7 @@ export function selectEntries(shown, emptyText) {
91
107
  out.push({ type: 'label', id: `__wb-select-group-${String(out.length)}`, text: option.group });
92
108
  group = option.group;
93
109
  }
94
- out.push(itemOf(option));
110
+ out.push(itemOf(option, revealId));
95
111
  }
96
112
  return out;
97
113
  }
@@ -155,18 +171,19 @@ function useSelectState(props) {
155
171
  };
156
172
  }
157
173
  export function SelectField(props) {
158
- const { value, options, onChange, label, hint, icon, renderValue, placeholder, search = true, searchLabel, searchPlaceholder, footer, onFooter, disabled = false, invalid = false, loading = false, loadingText, fallback, selection = 'fill', variant = 'boxed', ariaLabel, title, id, name, className, menuClassName, } = props;
174
+ const { value, options, onChange, label, hint, icon, renderValue, placeholder, search = true, searchLabel, searchPlaceholder, footer, onFooter, disabled = false, invalid = false, loading = false, loadingText, fallback, selection = 'fill', variant = 'boxed', iconOnly = false, revealId, ariaLabel, title, id, name, className, menuClassName, } = props;
159
175
  const state = useSelectState(props);
160
176
  const current = options.find((option) => option.value === value);
177
+ useRevealOnOpen(state.open, revealId);
161
178
  const variantClass = variant === 'ghost' ? 'wb-selectGhost' : undefined;
162
- const triggerClass = mergeClass('wb-selectTrigger', variantClass);
179
+ const triggerClass = triggerClassOf(variantClass, iconOnly);
163
180
  if (loading) {
164
- return framed(label, hint, className, loadingNode({ id, className: triggerClass, invalid, ariaLabel, loadingText }));
181
+ return framed(label, hint, className, selectLoadingNode({ id, className: triggerClass, invalid, ariaLabel, loadingText }));
165
182
  }
166
183
  if (fallback !== undefined && options.length === 0) {
167
184
  return framed(label, hint, className, h(TextField, { id: id, name: name, className: "wb-selectFallback", invalid: invalid, "aria-label": ariaLabel, value: fallback.value, onChange: (event) => fallback.onChange(event.currentTarget.value) }));
168
185
  }
169
- const trigger = triggerNode({
186
+ const trigger = selectTriggerNode({
170
187
  id,
171
188
  className: triggerClass,
172
189
  disabled,
@@ -175,7 +192,8 @@ export function SelectField(props) {
175
192
  ariaLabel,
176
193
  title,
177
194
  icon,
178
- value: current === undefined ? (h("span", { className: "wb-selectPlaceholder" }, placeholder ?? '—')) : renderValue === undefined ? (current.label) : (renderValue(current)),
195
+ value: selectValueNode(current, renderValue, placeholder),
196
+ iconOnly,
179
197
  onOpen: () => state.setOpen(true),
180
198
  });
181
199
  const anchor = state.open && search
@@ -215,7 +233,7 @@ export function SelectField(props) {
215
233
  anchor,
216
234
  selection,
217
235
  selectedId: selectCursorValue(state.shown, state.cursor) ?? undefined,
218
- items: selectEntries(state.shown, state.empty),
236
+ items: selectEntries(state.shown, state.empty, revealId),
219
237
  footer: footer === undefined
220
238
  ? undefined
221
239
  : [
@@ -0,0 +1,21 @@
1
+ import type { ReactNode } from 'react';
2
+ export declare function selectTriggerNode(args: {
3
+ readonly id: string | undefined;
4
+ readonly className: string;
5
+ readonly disabled: boolean;
6
+ readonly invalid: boolean;
7
+ readonly open: boolean;
8
+ readonly ariaLabel: string | undefined;
9
+ readonly title: string | undefined;
10
+ readonly icon: ReactNode | undefined;
11
+ readonly value: ReactNode;
12
+ readonly iconOnly: boolean;
13
+ readonly onOpen: () => void;
14
+ }): ReactNode;
15
+ export declare function selectLoadingNode(args: {
16
+ readonly id: string | undefined;
17
+ readonly className: string;
18
+ readonly invalid: boolean;
19
+ readonly ariaLabel: string | undefined;
20
+ readonly loadingText: string | undefined;
21
+ }): ReactNode;
@@ -0,0 +1,17 @@
1
+ import { Fragment, h } from '../lib/react.js';
2
+ import { IconChevronDownOutlineRegular, TextShimmer, } from '@deepseek-ai/dsh-client-ui-primitives';
3
+ import { Spinner } from './Spinner.js';
4
+ export function selectTriggerNode(args) {
5
+ return (h("button", { type: "button", id: args.id, className: args.className, disabled: args.disabled, "aria-haspopup": "listbox", "aria-expanded": args.open, "aria-invalid": args.invalid || undefined, "aria-label": args.ariaLabel, title: args.title, onClick: args.onOpen }, args.iconOnly ? (h("span", { className: "wb-selectIcon" }, args.icon)) : (h(Fragment, null,
6
+ args.icon === undefined ? null : h("span", { className: "wb-selectIcon" }, args.icon),
7
+ h("span", { className: "wb-selectValue" }, args.value),
8
+ h("span", { className: "wb-selectChevron" },
9
+ h(IconChevronDownOutlineRegular, { size: 14 }))))));
10
+ }
11
+ export function selectLoadingNode(args) {
12
+ return (h("button", { type: "button", id: args.id, className: `${args.className} wb-selectLoading`, disabled: true, "aria-busy": "true", "aria-invalid": args.invalid || undefined, "aria-label": args.ariaLabel, title: args.loadingText },
13
+ h("span", { className: "wb-selectValue" },
14
+ h(TextShimmer, { active: true }, args.loadingText ?? '…')),
15
+ h("span", { className: "wb-selectChevron" },
16
+ h(Spinner, { size: 14 }))));
17
+ }
@@ -12,6 +12,7 @@ export declare const CODING_ISSUES_SERVICE = "workbenchCodingIssues";
12
12
  export declare const PANELS_SERVICE = "workbenchPanels";
13
13
  export declare const UNITS_SERVICE = "workbenchUnits";
14
14
  export declare const PANEL_STATE_SERVICE = "workbenchPanelState";
15
+ export declare const DIY_DEBUG_SERVICE = "workbenchDiyDebug";
15
16
  export declare const PANEL_LIST_LIMIT = 50;
16
17
  export declare const OPENCLI_TIMEOUT_MS = 120000;
17
18
  export declare const OPENCLI_MAX_CONCURRENT = 4;
@@ -15,6 +15,7 @@ export const CODING_ISSUES_SERVICE = "workbenchCodingIssues";
15
15
  export const PANELS_SERVICE = "workbenchPanels";
16
16
  export const UNITS_SERVICE = "workbenchUnits";
17
17
  export const PANEL_STATE_SERVICE = "workbenchPanelState";
18
+ export const DIY_DEBUG_SERVICE = "workbenchDiyDebug";
18
19
  export const PANEL_LIST_LIMIT = 50;
19
20
  export const OPENCLI_TIMEOUT_MS = 120_000;
20
21
  export const OPENCLI_MAX_CONCURRENT = 4;
@@ -1,5 +1,6 @@
1
1
  import type { DshToolRunContext } from '../api/dsh.js';
2
2
  export declare function sessionWorkspace(exec: DshToolRunContext | undefined): string | null;
3
+ export declare function sessionIdOf(exec: DshToolRunContext | undefined): string | null;
3
4
  export declare function textOutput(text: string): Array<Record<string, unknown>>;
4
5
  export declare function textRender(_args: unknown, value: unknown): Array<Record<string, unknown>>;
5
6
  export declare const TEXT_OUTPUT: {
@@ -3,6 +3,10 @@ export function sessionWorkspace(exec) {
3
3
  const cwd = exec?.agent?.session?.header?.cwd;
4
4
  return typeof cwd === 'string' && cwd.trim() !== '' ? cwd.trim() : null;
5
5
  }
6
+ export function sessionIdOf(exec) {
7
+ const id = exec?.agent?.session?.id;
8
+ return typeof id === 'string' && id.trim() !== '' ? id.trim() : null;
9
+ }
6
10
  export function textOutput(text) {
7
11
  return [{ type: 'text', text }];
8
12
  }
package/dist/host.d.ts CHANGED
@@ -7,6 +7,7 @@ export * from './api/long-task.js';
7
7
  export * from './lib/text.js';
8
8
  export * from './lib/ui.js';
9
9
  export * from './lib/panel-state.js';
10
+ export * from './lib/diy-debug.js';
10
11
  export * from './api/blocker-table.js';
11
12
  export * from './host/browser-find.js';
12
13
  export * from './host/browser-read.js';
package/dist/host.js CHANGED
@@ -7,6 +7,7 @@ export * from './api/long-task.js';
7
7
  export * from './lib/text.js';
8
8
  export * from './lib/ui.js';
9
9
  export * from './lib/panel-state.js';
10
+ export * from './lib/diy-debug.js';
10
11
  export * from './api/blocker-table.js';
11
12
  export * from './host/browser-find.js';
12
13
  export * from './host/browser-read.js';
package/dist/i18n/en.js CHANGED
@@ -406,4 +406,12 @@ export const en = {
406
406
  'configSync.noSource': 'This machine has no other profile to sync from',
407
407
  'configSync.emptyProviders': 'The workbench has no model provider yet — sync the llm- rows from some profile, or configure one under Models.',
408
408
  'configSync.failed': 'The sync did not go through; try again.',
409
+ 'debug.status': 'Debugging',
410
+ 'debug.stalled': 'Debug stalled',
411
+ 'debug.tip.active': 'Debugging {unit} on the workshop board ({since}) · click to go back · tell the agent to pause when done',
412
+ 'debug.tip.stalled': 'The debug is stalled — go back to the 工作台DIY screen · click to return',
413
+ 'debug.back': 'Back to the workshop screen',
414
+ 'debug.strip.active': 'Debugging · {unit} · {since}',
415
+ 'debug.strip.note': 'Tell the agent to pause when you are done',
416
+ 'debug.stripStalled': 'Debug stalled — tell the agent to continue',
409
417
  };
@@ -408,8 +408,16 @@ export declare const dictionaries: {
408
408
  readonly 'configSync.noSource': "这台机器上只有工作台这一个 profile,没有可同步的来源";
409
409
  readonly 'configSync.emptyProviders': "工作台还没有任何模型供应商 —— 去「配置同步」从某个 profile 把 llm- 那几行搬过来,或自己去「模型」里配。";
410
410
  readonly 'configSync.failed': "同步没成功,再试一次。";
411
+ readonly 'debug.status': "调试中";
412
+ readonly 'debug.stalled': "调试搁置";
413
+ readonly 'debug.tip.active': "工坊那块板上正在调试 {unit}({since})· 点一下回到那一屏 · 要停就对 agent 说一句";
414
+ readonly 'debug.tip.stalled': "调试被搁置了 —— 要你回到工作台DIY 那一屏 · 点一下回去";
415
+ readonly 'debug.back': "回到工坊那一屏";
416
+ readonly 'debug.strip.active': "调试中 · {unit} · {since}";
417
+ readonly 'debug.strip.note': "要停就对 agent 说一句「暂停」";
418
+ readonly 'debug.stripStalled': "调试搁置 —— 对 agent 说一句「继续」";
411
419
  };
412
- en: Record<"time.now" | "time.ago" | "time.minutes" | "time.hours" | "time.days" | "blocker.retry" | "blocker.copy" | "blocker.unknown.title" | "blocker.login.go" | "blocker.login.titleGeneric" | "blocker.login.title" | "blocker.login.manual" | "blocker.login.hint" | "blocker.forbidden.open" | "blocker.forbidden.titleGeneric" | "blocker.forbidden.title" | "blocker.forbidden.hint" | "blocker.timeout.hint" | "blocker.exec.hint" | "blocker.fix.install" | "blocker.fix.update" | "blocker.market.open" | "env.unit.failed" | "env.unit.failedHint" | "env.unit.missingPlugin" | "env.unit.hint" | "env.unit.missingCommands" | "env.unit.missingCommandsHint" | "env.unit.unchecked" | "env.unit.uncheckedHint" | "brand.name" | "toggle.open" | "toggle.collapse" | "project.label" | "workspace.section" | "project.none" | "project.empty" | "project.add" | "project.add.busy" | "project.add.failed" | "project.search.label" | "project.search.placeholder" | "project.search.empty" | "project.group.workspaces" | "project.group.defaultWorkspace" | "project.group.workshop" | "project.rename.row" | "project.rename.current" | "project.rename.title" | "project.rename.description" | "project.rename.field" | "project.rename.placeholder" | "project.rename.hint" | "project.rename.save" | "project.rename.saving" | "project.rename.cancel" | "project.rename.failed" | "session.new" | "session.new.label" | "session.empty" | "session.noProject" | "session.list.label" | "session.copy" | "session.pinned" | "session.actions" | "status.idle" | "status.running" | "status.waitingApproval" | "status.planReview" | "status.waitingAnswer" | "status.compact.approval" | "status.compact.planReview" | "status.compact.answer" | "status.completed" | "status.subagentsRunning.one" | "status.subagentsRunning.other" | "project.attention.warning.one" | "project.attention.warning.other" | "project.attention.running.one" | "project.attention.running.other" | "project.attention.done.one" | "project.attention.done.other" | "project.attention.state.warning" | "project.attention.state.running" | "project.attention.state.done" | "hover.copied" | "time.short.minutes" | "time.short.hours" | "time.short.days" | "time.short.months" | "time.short.years" | "panels.title" | "panels.groupBoardless" | "panels.groupPlugins" | "panels.market.defaultHint" | "panels.market.defaultLabel" | "panels.market.defaultLocked" | "panels.followAll" | "panels.followAllConfirmTitle" | "panels.followAllConfirmDetail" | "panels.followAllConfirmAck" | "panels.followAllConfirmYes" | "panels.followAllConfirmNo" | "panels.enabled" | "panels.disabled" | "panels.choose" | "panels.buildModule" | "panels.failed" | "panels.dragHint" | "panels.unknownStep" | "panels.labelWithHint" | "panels.dep.failed" | "panels.dep.missingPlugin" | "panels.dep.missingCommands" | "panels.dep.unchecked" | "panels.tab.modules" | "panels.tab.market" | "panels.browseMarket" | "panels.noneInstalled" | "panels.noneInstalledHint" | "panels.market.installed" | "panels.market.notActive" | "panels.market.notUsable" | "panels.market.broken" | "panels.market.hasNew" | "panels.market.missing" | "panels.market.unchecked" | "panels.market.checking" | "panels.market.sectionInstalled" | "panels.market.sectionPending" | "panels.market.sectionShared" | "panels.market.sharedUsedBy" | "panels.market.listSeparator" | "panels.market.sharedMissing" | "panels.market.sharedOutdated" | "panels.market.sharedUnsatisfied" | "panels.market.sharedOk" | "panels.market.versionArrow" | "panels.market.lead" | "panels.market.install" | "panels.market.update" | "panels.market.swapSlow" | "panels.market.uninstall" | "panels.market.uninstallLabel" | "panels.market.installing" | "panels.market.updating" | "panels.market.uninstalling" | "panels.market.confirmUninstallTitle" | "panels.market.confirmUninstallDetail" | "panels.market.confirmUninstallAck" | "panels.market.confirmYes" | "panels.market.confirmNo" | "panels.market.failed" | "panels.market.needEnv" | "panels.market.needCommands" | "panels.market.localMark" | "panels.market.localHint" | "panels.market.noSource" | "panels.market.badSource" | "panels.market.empty" | "panels.market.sectionPlugins" | "panels.market.moduleOutdated" | "panels.market.moduleRefused" | "panels.market.moduleRefusedDetail" | "panels.market.moduleRestartHint" | "panels.market.pendingRestart" | "panels.market.workbenchName" | "panels.market.workbenchLocal" | "panels.market.pluginKeptInUse" | "panels.market.pluginKeptUnsure" | "panels.market.pluginKeptHeld" | "panels.market.pluginKeptUnnamed" | "panels.market.pluginBroken" | "panels.market.restartNeeded" | "panels.market.foot" | "panels.market.recheck" | "panels.market.checkUpdates" | "panels.market.checkedAt" | "panels.market.justNow" | "panels.market.minutesAgo" | "panels.market.hoursAgo" | "panels.market.add.description" | "panels.market.add.specPlaceholder" | "panels.market.add.guideToggle" | "panels.market.add.guideHide" | "panels.market.add.guideIdTitle" | "panels.market.add.guideIdExample" | "panels.market.add.guideIdHint" | "panels.market.add.guideGitTitle" | "panels.market.add.guideGitExample" | "panels.market.add.guideGitHint" | "panels.market.add.guidePathTitle" | "panels.market.add.guidePathExample" | "panels.market.add.guidePathHint" | "panels.market.add.guideExampleLabel" | "panels.market.add.guideFill" | "panels.market.add.guideSafety" | "panels.market.add.registryLegend" | "panels.market.add.registryOfficial" | "panels.market.add.registryMirror" | "panels.market.add.registryWithHost" | "panels.market.add.registryToggle" | "panels.market.add.installVersion" | "panels.market.add.registryDefault" | "panels.market.add.registryCustom" | "panels.market.add.registryCustomPlaceholder" | "panels.market.add.registryCustomHint" | "panels.market.add.registryCustomInvalid" | "panels.market.add.run" | "panels.market.add.edit" | "panels.market.add.changeRegistry" | "panels.market.add.installingTitle" | "panels.market.add.installedTitle" | "panels.market.add.failedTitle" | "panels.market.add.cancelledTitle" | "panels.market.add.failureNetworkHost" | "panels.market.add.problemInvalid" | "panels.market.add.problemInstalled" | "panels.market.add.problemNotFound" | "panels.market.add.problemNotPackage" | "panels.market.add.problemNotBundle" | "panels.market.add.problemNotWorkbenchModule" | "panels.market.add.problemNotModuleOrPlugin" | "panels.market.add.failureNotModuleOrPlugin" | "panels.market.add.installedPlugin" | "panels.market.add.pluginPreview" | "panels.market.add.pluginAlias" | "panels.market.add.problemNetwork" | "panels.market.add.problemUnknown" | "panels.market.add.failureNetwork" | "panels.market.add.failureNotFound" | "panels.market.add.failureNoMatchingVersion" | "panels.market.add.failureDiskFull" | "panels.market.add.failurePermission" | "panels.market.add.failureBuildBlocked" | "panels.market.add.failureIntegrity" | "panels.market.add.failureTimeout" | "panels.market.add.failurePnpmMissing" | "panels.market.add.failureInstalled" | "panels.market.add.failureGeneric" | "panels.market.add.approveTitle" | "panels.market.add.approveCaution" | "panels.market.add.approveRun" | "panels.market.add.restart" | "panels.market.add.tookEffect" | "panels.market.add.details" | "panels.market.add.open" | "panels.market.add.title" | "panels.market.add.intro" | "panels.market.add.specLabel" | "panels.market.add.specHint" | "panels.market.add.exampleTitle" | "panels.market.add.exampleFill" | "panels.market.add.registryLabel" | "panels.market.add.registryProfile" | "panels.market.add.registryPnpm" | "panels.market.add.check" | "panels.market.add.checking" | "panels.market.add.classifying" | "panels.market.add.change" | "panels.market.add.previewTitle" | "panels.market.add.fieldName" | "panels.market.add.fieldVersion" | "panels.market.add.fieldDescription" | "panels.market.add.fieldSource" | "panels.market.add.install" | "panels.market.add.installing" | "panels.market.add.cancel" | "panels.market.add.close" | "panels.market.add.retry" | "panels.market.add.changeSource" | "panels.market.add.approveBuilds" | "panels.market.add.approveHint" | "panels.market.add.doneApplied" | "panels.market.add.doneRestart" | "panels.market.add.restartWhy" | "panels.market.add.restartWhy.no-hmr" | "panels.market.add.restartWhy.package-moved" | "panels.market.add.restartWhy.host-module" | "panels.market.add.restartWhy.unmanaged-plugin" | "panels.market.add.restartWhy.module-comparison" | "panels.market.add.cancelled" | "panels.market.add.failed" | "panels.market.add.removed" | "panels.market.add.supplyPending" | "panels.market.add.supplyFailed" | "panels.market.add.notModule" | "panels.market.add.removedRestart" | "panels.market.add.remove" | "panels.market.add.problem.invalidSpec" | "panels.market.add.problem.alreadyInstalled" | "panels.market.add.problem.notFound" | "panels.market.add.problem.notAPackage" | "panels.market.add.problem.notABundle" | "panels.market.add.problem.network" | "panels.market.add.problem.unknown" | "panels.market.add.reason.invalidSpec" | "panels.market.add.reason.incompatible" | "panels.market.add.reason.notBundle" | "panels.market.add.reason.notRemovable" | "panels.market.add.reason.bundleInUse" | "panels.market.add.reason.stopProfile" | "panels.market.add.reason.staleApproval" | "panels.market.add.reason.operationError" | "panels.market.add.reason.buildBlocked" | "panels.market.add.reason.pnpmMissing" | "panels.market.add.reason.noMatchingVersion" | "panels.market.add.reason.diskFull" | "panels.market.add.reason.permission" | "panels.market.add.reason.integrity" | "panels.market.add.reason.timeout" | "panels.market.add.reason.network" | "panels.market.add.reason.notFound" | "panels.market.add.reason.unknown" | "blocker.fix.running" | "blocker.fix.failed" | "blocker.retry.running" | "panels.reading" | "panels.cardBroken" | "panels.cardRetry" | "panels.empty" | "panels.emptyHint" | "env.title" | "env.checking" | "env.installing" | "env.failed" | "env.elapsed" | "env.stepFailed.install" | "env.stepFailed.path" | "env.stepFailed.shell" | "env.bridge.ok" | "env.bridge.short" | "env.bridge.why" | "env.bridge.daemon" | "env.bridge.extension" | "env.bridge.extensionLink" | "env.bridge.profile" | "env.bridge.stale" | "env.bridge.unknown" | "env.fix.title" | "env.fix.why" | "env.fix.whyPath" | "env.fix.whyShell" | "env.fix.step" | "env.fix.stepPath" | "env.fix.stepShell" | "env.fix.permission" | "env.fix.network" | "env.fix.details" | "env.retry" | "env.copyCommand" | "env.copied" | "env.step.opencli" | "env.step.codingPlugin" | "env.step.bridge" | "env.step.warm" | "env.warm.done" | "env.warm.short" | "rightbar.label" | "rightbar.expand" | "rightbar.collapse" | "rightbar.column.label" | "rightbar.column.workbench" | "rightbar.column.dsh" | "rightbar.refresh" | "rightbar.refreshing" | "rightbar.updatedAt" | "rightbar.noProject" | "rightbar.openPlatform" | "coding.notARepo.title" | "error.unknown" | "board.title" | "panel.conversation" | "panels.label" | "configSync.nav" | "configSync.lead" | "configSync.source" | "configSync.sourceBroken" | "configSync.sourceRows" | "configSync.sourceDead" | "configSync.refresh" | "configSync.loading" | "configSync.readHint" | "configSync.tag.new" | "configSync.tag.differs" | "configSync.conflictNote" | "configSync.code.lang" | "configSync.code.copy" | "configSync.code.copied" | "configSync.code.wrap" | "configSync.code.unwrap" | "configSync.code.collapse" | "configSync.code.collapseAria" | "configSync.code.expandAria" | "configSync.code.expand" | "configSync.missing" | "configSync.apply" | "configSync.applyNone" | "configSync.applying" | "configSync.applied" | "configSync.appliedIds" | "configSync.skippedIds" | "configSync.nothing" | "configSync.unreadable" | "configSync.noSource" | "configSync.emptyProviders" | "configSync.failed", string>;
420
+ en: Record<"time.now" | "time.ago" | "time.minutes" | "time.hours" | "time.days" | "blocker.retry" | "blocker.copy" | "blocker.unknown.title" | "blocker.login.go" | "blocker.login.titleGeneric" | "blocker.login.title" | "blocker.login.manual" | "blocker.login.hint" | "blocker.forbidden.open" | "blocker.forbidden.titleGeneric" | "blocker.forbidden.title" | "blocker.forbidden.hint" | "blocker.timeout.hint" | "blocker.exec.hint" | "blocker.fix.install" | "blocker.fix.update" | "blocker.market.open" | "env.unit.failed" | "env.unit.failedHint" | "env.unit.missingPlugin" | "env.unit.hint" | "env.unit.missingCommands" | "env.unit.missingCommandsHint" | "env.unit.unchecked" | "env.unit.uncheckedHint" | "brand.name" | "toggle.open" | "toggle.collapse" | "project.label" | "workspace.section" | "project.none" | "project.empty" | "project.add" | "project.add.busy" | "project.add.failed" | "project.search.label" | "project.search.placeholder" | "project.search.empty" | "project.group.workspaces" | "project.group.defaultWorkspace" | "project.group.workshop" | "project.rename.row" | "project.rename.current" | "project.rename.title" | "project.rename.description" | "project.rename.field" | "project.rename.placeholder" | "project.rename.hint" | "project.rename.save" | "project.rename.saving" | "project.rename.cancel" | "project.rename.failed" | "session.new" | "session.new.label" | "session.empty" | "session.noProject" | "session.list.label" | "session.copy" | "session.pinned" | "session.actions" | "status.idle" | "status.running" | "status.waitingApproval" | "status.planReview" | "status.waitingAnswer" | "status.compact.approval" | "status.compact.planReview" | "status.compact.answer" | "status.completed" | "status.subagentsRunning.one" | "status.subagentsRunning.other" | "project.attention.warning.one" | "project.attention.warning.other" | "project.attention.running.one" | "project.attention.running.other" | "project.attention.done.one" | "project.attention.done.other" | "project.attention.state.warning" | "project.attention.state.running" | "project.attention.state.done" | "hover.copied" | "time.short.minutes" | "time.short.hours" | "time.short.days" | "time.short.months" | "time.short.years" | "panels.title" | "panels.groupBoardless" | "panels.groupPlugins" | "panels.market.defaultHint" | "panels.market.defaultLabel" | "panels.market.defaultLocked" | "panels.followAll" | "panels.followAllConfirmTitle" | "panels.followAllConfirmDetail" | "panels.followAllConfirmAck" | "panels.followAllConfirmYes" | "panels.followAllConfirmNo" | "panels.enabled" | "panels.disabled" | "panels.choose" | "panels.buildModule" | "panels.failed" | "panels.dragHint" | "panels.unknownStep" | "panels.labelWithHint" | "panels.dep.failed" | "panels.dep.missingPlugin" | "panels.dep.missingCommands" | "panels.dep.unchecked" | "panels.tab.modules" | "panels.tab.market" | "panels.browseMarket" | "panels.noneInstalled" | "panels.noneInstalledHint" | "panels.market.installed" | "panels.market.notActive" | "panels.market.notUsable" | "panels.market.broken" | "panels.market.hasNew" | "panels.market.missing" | "panels.market.unchecked" | "panels.market.checking" | "panels.market.sectionInstalled" | "panels.market.sectionPending" | "panels.market.sectionShared" | "panels.market.sharedUsedBy" | "panels.market.listSeparator" | "panels.market.sharedMissing" | "panels.market.sharedOutdated" | "panels.market.sharedUnsatisfied" | "panels.market.sharedOk" | "panels.market.versionArrow" | "panels.market.lead" | "panels.market.install" | "panels.market.update" | "panels.market.swapSlow" | "panels.market.uninstall" | "panels.market.uninstallLabel" | "panels.market.installing" | "panels.market.updating" | "panels.market.uninstalling" | "panels.market.confirmUninstallTitle" | "panels.market.confirmUninstallDetail" | "panels.market.confirmUninstallAck" | "panels.market.confirmYes" | "panels.market.confirmNo" | "panels.market.failed" | "panels.market.needEnv" | "panels.market.needCommands" | "panels.market.localMark" | "panels.market.localHint" | "panels.market.noSource" | "panels.market.badSource" | "panels.market.empty" | "panels.market.sectionPlugins" | "panels.market.moduleOutdated" | "panels.market.moduleRefused" | "panels.market.moduleRefusedDetail" | "panels.market.moduleRestartHint" | "panels.market.pendingRestart" | "panels.market.workbenchName" | "panels.market.workbenchLocal" | "panels.market.pluginKeptInUse" | "panels.market.pluginKeptUnsure" | "panels.market.pluginKeptHeld" | "panels.market.pluginKeptUnnamed" | "panels.market.pluginBroken" | "panels.market.restartNeeded" | "panels.market.foot" | "panels.market.recheck" | "panels.market.checkUpdates" | "panels.market.checkedAt" | "panels.market.justNow" | "panels.market.minutesAgo" | "panels.market.hoursAgo" | "panels.market.add.description" | "panels.market.add.specPlaceholder" | "panels.market.add.guideToggle" | "panels.market.add.guideHide" | "panels.market.add.guideIdTitle" | "panels.market.add.guideIdExample" | "panels.market.add.guideIdHint" | "panels.market.add.guideGitTitle" | "panels.market.add.guideGitExample" | "panels.market.add.guideGitHint" | "panels.market.add.guidePathTitle" | "panels.market.add.guidePathExample" | "panels.market.add.guidePathHint" | "panels.market.add.guideExampleLabel" | "panels.market.add.guideFill" | "panels.market.add.guideSafety" | "panels.market.add.registryLegend" | "panels.market.add.registryOfficial" | "panels.market.add.registryMirror" | "panels.market.add.registryWithHost" | "panels.market.add.registryToggle" | "panels.market.add.installVersion" | "panels.market.add.registryDefault" | "panels.market.add.registryCustom" | "panels.market.add.registryCustomPlaceholder" | "panels.market.add.registryCustomHint" | "panels.market.add.registryCustomInvalid" | "panels.market.add.run" | "panels.market.add.edit" | "panels.market.add.changeRegistry" | "panels.market.add.installingTitle" | "panels.market.add.installedTitle" | "panels.market.add.failedTitle" | "panels.market.add.cancelledTitle" | "panels.market.add.failureNetworkHost" | "panels.market.add.problemInvalid" | "panels.market.add.problemInstalled" | "panels.market.add.problemNotFound" | "panels.market.add.problemNotPackage" | "panels.market.add.problemNotBundle" | "panels.market.add.problemNotWorkbenchModule" | "panels.market.add.problemNotModuleOrPlugin" | "panels.market.add.failureNotModuleOrPlugin" | "panels.market.add.installedPlugin" | "panels.market.add.pluginPreview" | "panels.market.add.pluginAlias" | "panels.market.add.problemNetwork" | "panels.market.add.problemUnknown" | "panels.market.add.failureNetwork" | "panels.market.add.failureNotFound" | "panels.market.add.failureNoMatchingVersion" | "panels.market.add.failureDiskFull" | "panels.market.add.failurePermission" | "panels.market.add.failureBuildBlocked" | "panels.market.add.failureIntegrity" | "panels.market.add.failureTimeout" | "panels.market.add.failurePnpmMissing" | "panels.market.add.failureInstalled" | "panels.market.add.failureGeneric" | "panels.market.add.approveTitle" | "panels.market.add.approveCaution" | "panels.market.add.approveRun" | "panels.market.add.restart" | "panels.market.add.tookEffect" | "panels.market.add.details" | "panels.market.add.open" | "panels.market.add.title" | "panels.market.add.intro" | "panels.market.add.specLabel" | "panels.market.add.specHint" | "panels.market.add.exampleTitle" | "panels.market.add.exampleFill" | "panels.market.add.registryLabel" | "panels.market.add.registryProfile" | "panels.market.add.registryPnpm" | "panels.market.add.check" | "panels.market.add.checking" | "panels.market.add.classifying" | "panels.market.add.change" | "panels.market.add.previewTitle" | "panels.market.add.fieldName" | "panels.market.add.fieldVersion" | "panels.market.add.fieldDescription" | "panels.market.add.fieldSource" | "panels.market.add.install" | "panels.market.add.installing" | "panels.market.add.cancel" | "panels.market.add.close" | "panels.market.add.retry" | "panels.market.add.changeSource" | "panels.market.add.approveBuilds" | "panels.market.add.approveHint" | "panels.market.add.doneApplied" | "panels.market.add.doneRestart" | "panels.market.add.restartWhy" | "panels.market.add.restartWhy.no-hmr" | "panels.market.add.restartWhy.package-moved" | "panels.market.add.restartWhy.host-module" | "panels.market.add.restartWhy.unmanaged-plugin" | "panels.market.add.restartWhy.module-comparison" | "panels.market.add.cancelled" | "panels.market.add.failed" | "panels.market.add.removed" | "panels.market.add.supplyPending" | "panels.market.add.supplyFailed" | "panels.market.add.notModule" | "panels.market.add.removedRestart" | "panels.market.add.remove" | "panels.market.add.problem.invalidSpec" | "panels.market.add.problem.alreadyInstalled" | "panels.market.add.problem.notFound" | "panels.market.add.problem.notAPackage" | "panels.market.add.problem.notABundle" | "panels.market.add.problem.network" | "panels.market.add.problem.unknown" | "panels.market.add.reason.invalidSpec" | "panels.market.add.reason.incompatible" | "panels.market.add.reason.notBundle" | "panels.market.add.reason.notRemovable" | "panels.market.add.reason.bundleInUse" | "panels.market.add.reason.stopProfile" | "panels.market.add.reason.staleApproval" | "panels.market.add.reason.operationError" | "panels.market.add.reason.buildBlocked" | "panels.market.add.reason.pnpmMissing" | "panels.market.add.reason.noMatchingVersion" | "panels.market.add.reason.diskFull" | "panels.market.add.reason.permission" | "panels.market.add.reason.integrity" | "panels.market.add.reason.timeout" | "panels.market.add.reason.network" | "panels.market.add.reason.notFound" | "panels.market.add.reason.unknown" | "blocker.fix.running" | "blocker.fix.failed" | "blocker.retry.running" | "panels.reading" | "panels.cardBroken" | "panels.cardRetry" | "panels.empty" | "panels.emptyHint" | "env.title" | "env.checking" | "env.installing" | "env.failed" | "env.elapsed" | "env.stepFailed.install" | "env.stepFailed.path" | "env.stepFailed.shell" | "env.bridge.ok" | "env.bridge.short" | "env.bridge.why" | "env.bridge.daemon" | "env.bridge.extension" | "env.bridge.extensionLink" | "env.bridge.profile" | "env.bridge.stale" | "env.bridge.unknown" | "env.fix.title" | "env.fix.why" | "env.fix.whyPath" | "env.fix.whyShell" | "env.fix.step" | "env.fix.stepPath" | "env.fix.stepShell" | "env.fix.permission" | "env.fix.network" | "env.fix.details" | "env.retry" | "env.copyCommand" | "env.copied" | "env.step.opencli" | "env.step.codingPlugin" | "env.step.bridge" | "env.step.warm" | "env.warm.done" | "env.warm.short" | "rightbar.label" | "rightbar.expand" | "rightbar.collapse" | "rightbar.column.label" | "rightbar.column.workbench" | "rightbar.column.dsh" | "rightbar.refresh" | "rightbar.refreshing" | "rightbar.updatedAt" | "rightbar.noProject" | "rightbar.openPlatform" | "coding.notARepo.title" | "error.unknown" | "board.title" | "panel.conversation" | "panels.label" | "configSync.nav" | "configSync.lead" | "configSync.source" | "configSync.sourceBroken" | "configSync.sourceRows" | "configSync.sourceDead" | "configSync.refresh" | "configSync.loading" | "configSync.readHint" | "configSync.tag.new" | "configSync.tag.differs" | "configSync.conflictNote" | "configSync.code.lang" | "configSync.code.copy" | "configSync.code.copied" | "configSync.code.wrap" | "configSync.code.unwrap" | "configSync.code.collapse" | "configSync.code.collapseAria" | "configSync.code.expandAria" | "configSync.code.expand" | "configSync.missing" | "configSync.apply" | "configSync.applyNone" | "configSync.applying" | "configSync.applied" | "configSync.appliedIds" | "configSync.skippedIds" | "configSync.nothing" | "configSync.unreadable" | "configSync.noSource" | "configSync.emptyProviders" | "configSync.failed" | "debug.status" | "debug.stalled" | "debug.tip.active" | "debug.tip.stalled" | "debug.back" | "debug.strip.active" | "debug.strip.note" | "debug.stripStalled", string>;
413
421
  };
414
422
  export type { WorkbenchMessages };
415
423
  export interface Dictionaries {
package/dist/i18n/zh.d.ts CHANGED
@@ -406,5 +406,13 @@ export declare const zh: {
406
406
  readonly 'configSync.noSource': "这台机器上只有工作台这一个 profile,没有可同步的来源";
407
407
  readonly 'configSync.emptyProviders': "工作台还没有任何模型供应商 —— 去「配置同步」从某个 profile 把 llm- 那几行搬过来,或自己去「模型」里配。";
408
408
  readonly 'configSync.failed': "同步没成功,再试一次。";
409
+ readonly 'debug.status': "调试中";
410
+ readonly 'debug.stalled': "调试搁置";
411
+ readonly 'debug.tip.active': "工坊那块板上正在调试 {unit}({since})· 点一下回到那一屏 · 要停就对 agent 说一句";
412
+ readonly 'debug.tip.stalled': "调试被搁置了 —— 要你回到工作台DIY 那一屏 · 点一下回去";
413
+ readonly 'debug.back': "回到工坊那一屏";
414
+ readonly 'debug.strip.active': "调试中 · {unit} · {since}";
415
+ readonly 'debug.strip.note': "要停就对 agent 说一句「暂停」";
416
+ readonly 'debug.stripStalled': "调试搁置 —— 对 agent 说一句「继续」";
409
417
  };
410
418
  export type WorkbenchMessages = typeof zh;
package/dist/i18n/zh.js CHANGED
@@ -406,4 +406,12 @@ export const zh = {
406
406
  'configSync.noSource': '这台机器上只有工作台这一个 profile,没有可同步的来源',
407
407
  'configSync.emptyProviders': '工作台还没有任何模型供应商 —— 去「配置同步」从某个 profile 把 llm- 那几行搬过来,或自己去「模型」里配。',
408
408
  'configSync.failed': '同步没成功,再试一次。',
409
+ 'debug.status': '调试中',
410
+ 'debug.stalled': '调试搁置',
411
+ 'debug.tip.active': '工坊那块板上正在调试 {unit}({since})· 点一下回到那一屏 · 要停就对 agent 说一句',
412
+ 'debug.tip.stalled': '调试被搁置了 —— 要你回到工作台DIY 那一屏 · 点一下回去',
413
+ 'debug.back': '回到工坊那一屏',
414
+ 'debug.strip.active': '调试中 · {unit} · {since}',
415
+ 'debug.strip.note': '要停就对 agent 说一句「暂停」',
416
+ 'debug.stripStalled': '调试搁置 —— 对 agent 说一句「继续」',
409
417
  };
package/dist/index.d.ts CHANGED
@@ -45,6 +45,7 @@ export * from './i18n/zh.js';
45
45
  export * from './lib/blocker-action.js';
46
46
  export * from './lib/busy.js';
47
47
  export * from './lib/form-problem.js';
48
+ export * from './lib/diy-debug.js';
48
49
  export * from './lib/panel-state.js';
49
50
  export * from './lib/react.js';
50
51
  export * from './lib/text.js';
package/dist/index.js CHANGED
@@ -45,6 +45,7 @@ export * from './i18n/zh.js';
45
45
  export * from './lib/blocker-action.js';
46
46
  export * from './lib/busy.js';
47
47
  export * from './lib/form-problem.js';
48
+ export * from './lib/diy-debug.js';
48
49
  export * from './lib/panel-state.js';
49
50
  export * from './lib/react.js';
50
51
  export * from './lib/text.js';
@@ -0,0 +1,38 @@
1
+ export type DebugPhase = 'debugging' | 'paused';
2
+ export declare const DEBUG_STALE_MS: number;
3
+ export interface DebugState {
4
+ readonly phase: DebugPhase;
5
+ readonly sessionId: string;
6
+ readonly workspace: string;
7
+ readonly board: string;
8
+ readonly unit: string;
9
+ readonly name: string;
10
+ readonly startedAt: number;
11
+ readonly at: number;
12
+ }
13
+ export interface DebugPact {
14
+ readonly sessionId: string;
15
+ readonly workspace: string;
16
+ readonly board: string;
17
+ readonly unit: string;
18
+ readonly name: string;
19
+ readonly now: number;
20
+ }
21
+ export type DebugRefusal = 'already-held' | 'held-by-other' | 'not-owner' | 'none' | 'already-paused' | 'already-debugging';
22
+ export type DebugMove = {
23
+ readonly ok: true;
24
+ readonly state: DebugState;
25
+ } | {
26
+ readonly ok: false;
27
+ readonly code: DebugRefusal;
28
+ };
29
+ export interface DebugStore {
30
+ live(now: number): DebugState | null;
31
+ begin(pact: DebugPact): DebugMove;
32
+ stall(sessionId: string, now: number): DebugMove;
33
+ pause(sessionId: string, now: number): DebugMove;
34
+ resume(sessionId: string, now: number): DebugMove;
35
+ stop(sessionId: string, now: number): DebugMove;
36
+ }
37
+ export declare function createDebugStore(): DebugStore;
38
+ export declare function debugOwnerText(state: DebugState): string;
@@ -0,0 +1,77 @@
1
+ export const DEBUG_STALE_MS = 4 * 60 * 60 * 1000;
2
+ function alive(state, now) {
3
+ if (state === null)
4
+ return null;
5
+ return now - state.at <= DEBUG_STALE_MS ? state : null;
6
+ }
7
+ function ownerOf(state, sessionId, now) {
8
+ const held = alive(state, now);
9
+ if (held === null)
10
+ return 'none';
11
+ return held.sessionId === sessionId ? held : 'not-owner';
12
+ }
13
+ export function createDebugStore() {
14
+ let held = null;
15
+ const live = (now) => alive(held, now);
16
+ return {
17
+ live,
18
+ begin(pact) {
19
+ const current = alive(held, pact.now);
20
+ if (current !== null) {
21
+ return { ok: false, code: current.sessionId === pact.sessionId ? 'already-held' : 'held-by-other' };
22
+ }
23
+ const state = {
24
+ phase: 'debugging',
25
+ sessionId: pact.sessionId,
26
+ workspace: pact.workspace,
27
+ board: pact.board,
28
+ unit: pact.unit,
29
+ name: pact.name,
30
+ startedAt: pact.now,
31
+ at: pact.now,
32
+ };
33
+ held = state;
34
+ return { ok: true, state };
35
+ },
36
+ stall(sessionId, now) {
37
+ const found = ownerOf(held, sessionId, now);
38
+ if (typeof found === 'string')
39
+ return { ok: false, code: found };
40
+ if (found.phase === 'paused')
41
+ return { ok: false, code: 'already-paused' };
42
+ const state = { ...found, phase: 'paused', at: now };
43
+ held = state;
44
+ return { ok: true, state };
45
+ },
46
+ pause(sessionId, now) {
47
+ const found = ownerOf(held, sessionId, now);
48
+ if (typeof found === 'string')
49
+ return { ok: false, code: found };
50
+ if (found.phase === 'paused')
51
+ return { ok: false, code: 'already-paused' };
52
+ const state = { ...found, phase: 'paused', at: now };
53
+ held = state;
54
+ return { ok: true, state };
55
+ },
56
+ resume(sessionId, now) {
57
+ const found = ownerOf(held, sessionId, now);
58
+ if (typeof found === 'string')
59
+ return { ok: false, code: found };
60
+ if (found.phase === 'debugging')
61
+ return { ok: false, code: 'already-debugging' };
62
+ const state = { ...found, phase: 'debugging', at: now };
63
+ held = state;
64
+ return { ok: true, state };
65
+ },
66
+ stop(sessionId, now) {
67
+ const found = ownerOf(held, sessionId, now);
68
+ if (typeof found === 'string')
69
+ return { ok: false, code: found };
70
+ held = null;
71
+ return { ok: true, state: found };
72
+ },
73
+ };
74
+ }
75
+ export function debugOwnerText(state) {
76
+ return `会话 ${state.sessionId}(自 ${new Date(state.startedAt).toISOString().slice(11, 19)}Z 起在调试 ${state.unit})`;
77
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dsh-workbench/shared",
3
- "version": "0.2.14",
3
+ "version": "0.2.15",
4
4
  "description": "工作台与模块共享的纯代码与契约类型:类型、纯逻辑、哑组件、平台解析。宿主侧的东西从 /host 进(不影响浏览器产物)。",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/src/api/dsh.ts CHANGED
@@ -151,7 +151,12 @@ export interface DshToolDefinition {
151
151
  export interface DshToolRunContext {
152
152
  readonly agent?:
153
153
  | {
154
- readonly session?: { readonly header?: { readonly cwd?: string } | undefined } | undefined;
154
+ readonly session?:
155
+ | {
156
+ readonly id?: string;
157
+ readonly header?: { readonly cwd?: string } | undefined;
158
+ }
159
+ | undefined;
155
160
  }
156
161
  | undefined;
157
162
  }
@@ -61,6 +61,26 @@
61
61
  box-shadow: none;
62
62
  }
63
63
 
64
+ .wb-selectIconOnly {
65
+ width: var(--wb-len-36);
66
+ height: var(--wb-len-36);
67
+ min-height: 0;
68
+ gap: 0;
69
+ padding: 0;
70
+ justify-content: center;
71
+ }
72
+
73
+ .wb-selectReveal {
74
+ display: inline-flex;
75
+ align-items: center;
76
+ justify-content: center;
77
+ }
78
+
79
+ .wb-selectIconOnly .wb-selectIcon {
80
+ width: 100%;
81
+ height: 100%;
82
+ }
83
+
64
84
  .wb-selectAnchor {
65
85
  display: block;
66
86
  width: 100%;