@dsh-workbench/core 0.3.5 → 0.3.7
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/client.js +1372 -1928
- package/dist/client/client.body.js +1372 -1928
- package/dist/client/index.js +1372 -1928
- package/dist/host/index.js +1569 -751
- package/dist/types/app/client.d.ts +76 -3355
- package/dist/types/app/layouts/env-gate/boot-gate.d.ts +8 -1
- package/dist/types/app/layouts/rightbar/market-busy.d.ts +28 -0
- package/dist/types/app/layouts/rightbar/market-hint.d.ts +7 -0
- package/dist/types/app/layouts/sidebar/DebugFooterAction.d.ts +2 -1
- package/dist/types/app/layouts/sidebar/RecordFooterAction.d.ts +2 -1
- package/dist/types/app/layouts/sidebar/SessionList.d.ts +3 -1
- package/dist/types/app/layouts/sidebar/session-pick.d.ts +2 -0
- package/dist/types/app/layouts/sidebar/use-attention.d.ts +7 -1
- package/dist/types/modules/panels/client.d.ts +1 -1
- package/dist/types/modules/panels/model/approval.d.ts +8 -0
- package/dist/types/modules/panels/model/diy-context.d.ts +9 -0
- package/dist/types/modules/panels/model/diy-record-beat.test.d.ts +1 -0
- package/dist/types/modules/panels/model/diy-record-close.test.d.ts +1 -0
- package/dist/types/modules/panels/model/diy-record-hook.test.d.ts +1 -0
- package/dist/types/modules/panels/model/diy-record-open.d.ts +28 -0
- package/dist/types/modules/panels/model/diy-record-open.test.d.ts +1 -0
- package/dist/types/modules/panels/model/diy-record-shot.test.d.ts +1 -0
- package/dist/types/modules/panels/model/diy-record-text.d.ts +16 -1
- package/dist/types/modules/panels/model/diy-record-tools.d.ts +2 -1
- package/dist/types/modules/panels/model/diy-record.d.ts +40 -1
- package/dist/types/modules/panels/model/diy-report.d.ts +1 -1
- package/dist/types/modules/panels/model/long-task-tools.d.ts +15 -0
- package/dist/types/modules/panels/model/long-task-tools.test.d.ts +1 -0
- package/dist/types/modules/panels/unit.d.ts +5 -3
- package/dist/types/modules/sessions/index.d.ts +1 -1
- package/dist/types/modules/sessions/model/attention-long-task.test.d.ts +1 -0
- package/dist/types/modules/sessions/model/attention.d.ts +23 -1
- package/dist/types/modules/sessions/model/session-node.d.ts +12 -1
- package/dist/types/shared/api/host/approval-call.d.ts +16 -0
- package/dist/types/shared/api/host/long-task-notice.d.ts +30 -0
- package/dist/types/shared/api/host/long-tasks.d.ts +10 -0
- package/dist/types/shared/api/host/mount-units.d.ts +3 -1
- package/dist/types/shared/api/host/preflight.d.ts +7 -1
- package/dist/types/shared/api/host/workshop-root.d.ts +9 -0
- package/dist/types/shared/api/long-tasks.d.ts +19 -0
- package/dist/types/shared/hooks/panel-state-report.d.ts +1 -1
- package/dist/types/shared/hooks/use-diy-debug.d.ts +1 -1
- package/dist/types/shared/hooks/use-diy-record.d.ts +1 -1
- package/dist/types/shared/hooks/use-long-task.d.ts +8 -2
- package/dist/types/shared/hooks/use-shell-long-tasks.d.ts +8 -0
- package/package.json +2 -2
- package/skills/workbench-module-diy/BUILD.md +1 -1
- package/skills/workbench-module-diy/FAMILY.md +1 -1
- package/skills/workbench-module-diy/KIT.md +26 -26
- package/skills/workbench-module-diy/PANEL.md +1 -1
- package/skills/workbench-module-diy/TOOLING.md +5 -1
- package/skills/workbench-module-diy/TOOLS.md +22 -2
- package/skills/workbench-module-diy/WRITES.md +20 -3
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import type { Translate } from '@dsh-workbench/shared';
|
|
2
2
|
import { apiGet } from '@dsh-workbench/shared';
|
|
3
|
+
/**
|
|
4
|
+
* 每一拍轮询带什么参数:平时什么都不带(宿主那边跟着正在跑的那一轮,刚跑出 `ready` 的直接回上一次),
|
|
5
|
+
* 用户按了「重试」之后的那一拍带 `hard=1`(不看新鲜度、一律重来)。首拍不再强探 —— 页面上一次
|
|
6
|
+
* 刚体检过就不必再跑一轮,两页同时刷新也只有一轮。
|
|
7
|
+
*/
|
|
3
8
|
export declare function createForcePlan(): {
|
|
4
|
-
next():
|
|
9
|
+
next(): {
|
|
10
|
+
hard?: 1;
|
|
11
|
+
};
|
|
5
12
|
again(): void;
|
|
6
13
|
};
|
|
7
14
|
export interface BootGate {
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 一条「市场自己正在动它」的事实最多认多久(毫秒):从发布那一刻起算,到点自动撤。
|
|
3
|
+
* 取的是工作台那一跳同一条窗口 —— 一次替换最长等这么久;正常落地会由发布方先撤掉。
|
|
4
|
+
*/
|
|
5
|
+
export declare const MARKET_BUSY_MS = 180000;
|
|
6
|
+
/**
|
|
7
|
+
* 这一刻哪些单元算「正在被动」:发布过、且没到 {@link MARKET_BUSY_MS} 的那些 id。
|
|
8
|
+
* 给纯函数与判据用;React 里读 {@link useMarketBusy}(同一份,只是走订阅)。
|
|
9
|
+
*/
|
|
10
|
+
export declare function marketBusyIds(): ReadonlySet<string>;
|
|
11
|
+
/**
|
|
12
|
+
* 发布「哪几个单元正被市场自己动」(每次整体替换):点下去那一刻发,落地/失败后发空数组。
|
|
13
|
+
* 同一个 id 连续发布不会顺延它的期限;空数组等于全撤。
|
|
14
|
+
*/
|
|
15
|
+
export declare function publishMarketBusy(ids: readonly string[]): void;
|
|
16
|
+
/**
|
|
17
|
+
* 订阅这份事实的变化(发布内容真变了、或有条目到期才通知)。返回退订函数。
|
|
18
|
+
*/
|
|
19
|
+
export declare function subscribeMarketBusy(listener: () => void): () => void;
|
|
20
|
+
/**
|
|
21
|
+
* 组件里读「正被市场自己动的单元」:返回的集合在两次通知之间是同一份引用,可以直接进渲染。
|
|
22
|
+
*/
|
|
23
|
+
export declare function useMarketBusy(): ReadonlySet<string>;
|
|
24
|
+
/**
|
|
25
|
+
* 把「市场正在动哪个单元」挂到这一拍上:`id` 变了就整体替换发布,`null` 等于撤。
|
|
26
|
+
* 卸载那一下**不清**(更新还在跑,徽标不该改口);到 {@link MARKET_BUSY_MS} 由自己到期兜底。
|
|
27
|
+
*/
|
|
28
|
+
export declare function usePublishMarketBusy(id: string | null): void;
|
|
@@ -12,5 +12,12 @@ export interface MarketHintInput {
|
|
|
12
12
|
readonly installed?: readonly InstalledWorkbenchModule[] | undefined;
|
|
13
13
|
readonly plugins?: readonly InstalledPlugin[] | null | undefined;
|
|
14
14
|
readonly workbench?: WorkbenchVersion | null | undefined;
|
|
15
|
+
readonly busy?: ReadonlySet<string> | undefined;
|
|
15
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* 左栏市场入口那枚徽标这一刻该说什么:`problem`(模块异常)> `restart`(重启后生效)> `updates`(有新版本),
|
|
19
|
+
* 都没有就 `null`。读法与市场页同一份数据、同一套造行函数。
|
|
20
|
+
* `busy` 里的单元(正被市场自己更新/卸载的那一单元)当它不在名单里:更新是「先卸再装」,
|
|
21
|
+
* 卸的那一半会让它从名册里消失,不排掉就会在跑的过程中报一次假的「重启后生效」。
|
|
22
|
+
*/
|
|
16
23
|
export declare function marketHintOf(input: MarketHintInput): PanelHint | null;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { SessionsSnapshot, SnapshotSelectorHook, Translate, WorkspaceSnapshot } from '@dsh-workbench/shared';
|
|
2
|
+
import type { DebugState } from '@dsh-workbench/shared/host';
|
|
2
3
|
export interface DebugFooterActionProps {
|
|
3
4
|
readonly wide?: boolean | undefined;
|
|
4
5
|
readonly t: Translate;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Translate } from '@dsh-workbench/shared';
|
|
2
|
+
import type { RecordIntent, RecordState } from '@dsh-workbench/shared/host';
|
|
2
3
|
export interface RecordFooterActionProps {
|
|
3
4
|
readonly wide?: boolean | undefined;
|
|
4
5
|
readonly t: Translate;
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import type { RenderSlot, SessionStatusSnapshot, SessionsSnapshot, SnapshotSelectorHook, Translate, WorkspaceView } from '@dsh-workbench/shared';
|
|
2
|
+
import { type LongTaskPresence } from '../../../modules/sessions';
|
|
2
3
|
export interface SessionListProps {
|
|
3
4
|
workspace: WorkspaceView | null;
|
|
4
5
|
pinnedIds: readonly string[];
|
|
5
6
|
archived: readonly string[];
|
|
6
7
|
panelActive: boolean;
|
|
8
|
+
longTasks: LongTaskPresence;
|
|
7
9
|
useSessions: SnapshotSelectorHook<SessionsSnapshot>;
|
|
8
10
|
useSessionStatus: SnapshotSelectorHook<SessionStatusSnapshot>;
|
|
9
11
|
onOpen: (sessionId: string) => void;
|
|
10
12
|
renderSlot: RenderSlot;
|
|
11
13
|
t: Translate;
|
|
12
14
|
}
|
|
13
|
-
export declare function SessionList({ workspace, pinnedIds, archived, panelActive, useSessions, useSessionStatus, onOpen, renderSlot, t, }: SessionListProps): JSX.Element;
|
|
15
|
+
export declare function SessionList({ workspace, pinnedIds, archived, panelActive, longTasks, useSessions, useSessionStatus, onOpen, renderSlot, t, }: SessionListProps): JSX.Element;
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type { SessionStatusSnapshot, SessionsSnapshot, WorkspaceView } from '@dsh-workbench/shared';
|
|
2
|
+
import { type LongTaskPresence } from '../../../modules/sessions';
|
|
2
3
|
export interface PickProjectInput {
|
|
3
4
|
readonly workspaceId: string;
|
|
4
5
|
readonly items: readonly WorkspaceView[];
|
|
5
6
|
readonly sessions: SessionsSnapshot;
|
|
6
7
|
readonly statusById: SessionStatusSnapshot;
|
|
7
8
|
readonly archivedIds: ReadonlySet<string>;
|
|
9
|
+
readonly longTasks: LongTaskPresence;
|
|
8
10
|
readonly openSession: (sessionId: string) => void;
|
|
9
11
|
readonly startSession: (workspaceId: string) => void;
|
|
10
12
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SessionStatusSnapshot, SessionsSnapshot, WorkspaceView } from '@dsh-workbench/shared';
|
|
2
|
-
import { type Attention } from '../../../modules/sessions';
|
|
2
|
+
import { type Attention, type LongTaskPresence } from '../../../modules/sessions';
|
|
3
3
|
export interface SidebarAttentionInput {
|
|
4
4
|
readonly items: readonly WorkspaceView[];
|
|
5
5
|
readonly sessions: SessionsSnapshot;
|
|
@@ -7,6 +7,7 @@ export interface SidebarAttentionInput {
|
|
|
7
7
|
readonly archived: readonly string[];
|
|
8
8
|
readonly storedId: string | null;
|
|
9
9
|
readonly panelActive: boolean;
|
|
10
|
+
readonly longTasks: LongTaskPresence;
|
|
10
11
|
}
|
|
11
12
|
export interface SidebarAttentionState {
|
|
12
13
|
readonly current: WorkspaceView | null;
|
|
@@ -14,3 +15,8 @@ export interface SidebarAttentionState {
|
|
|
14
15
|
readonly attention: Attention;
|
|
15
16
|
}
|
|
16
17
|
export declare function useSidebarAttention(input: SidebarAttentionInput): SidebarAttentionState;
|
|
18
|
+
/**
|
|
19
|
+
* 左栏要的那份长任务归属:外壳那条读口交回「全进程还在跑的长任务」,这里折成会话侧与工作区侧。
|
|
20
|
+
* @returns 两侧的归属(谁起了 / 哪个工作区里有几趟)。
|
|
21
|
+
*/
|
|
22
|
+
export declare function useLongTaskPresence(): LongTaskPresence;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { ALL_TOOLS, DIY_RECORD_PREPARE_TOOL, DIY_RECORD_READ_TOOL } from './unit';
|
|
1
|
+
export { ALL_TOOLS, DIY_RECORD_PREPARE_TOOL, DIY_RECORD_READ_TOOL, LONG_TASKS_STOP_TOOL } from './unit';
|
|
@@ -1,2 +1,10 @@
|
|
|
1
1
|
import type { ApprovalPolicy } from '@dsh-workbench/shared/host';
|
|
2
|
+
import type { LongTaskRunsLookup } from './long-task-tools';
|
|
2
3
|
export declare const panelsApproval: ApprovalPolicy;
|
|
4
|
+
/**
|
|
5
|
+
* 面板族的审批策略:`panelsApproval` 再认长任务那一档(写成功要用户点头)。
|
|
6
|
+
* `longTaskRuns` 用来查"这一趟是谁起的" —— 自己起的免问,别的会话起的 / 用户按的要问。
|
|
7
|
+
* @param longTaskRuns 当前挂着的单元各自那把长任务注册表;没给就按"别人的"问(保守的那一侧)。
|
|
8
|
+
* @returns 审批策略:认领的写工具回 ask / allow,不是本族的回 undefined。
|
|
9
|
+
*/
|
|
10
|
+
export declare function panelsApprovalFor(longTaskRuns?: LongTaskRunsLookup | undefined): ApprovalPolicy;
|
|
@@ -88,6 +88,15 @@ export declare function showOrReason(panels: PanelsService, unit: string, worksp
|
|
|
88
88
|
readonly ok: boolean;
|
|
89
89
|
readonly reason?: string;
|
|
90
90
|
};
|
|
91
|
+
/**
|
|
92
|
+
* 这一次 DIY 调用落在哪个工坊上:**先认工坊自己的家**(`$DSH_HOME/workbench/diy`,宿主就是按它把工坊
|
|
93
|
+
* 登记成工作区的),**不跟着会话 cwd 走** —— 用户把自己的项目设成工作区时,cwd 往上找会撞上任意一个
|
|
94
|
+
* 带 `packages/` 的仓库,模块列表与 `shots/` 落点会一起变样(真机踩过:录制落点跑到仓库根、截图 EPERM)。
|
|
95
|
+
* 只有拿不到工坊的家时,才退回"从会话工作区往上找像工坊的目录"。
|
|
96
|
+
* @param ctx - 宿主上下文。
|
|
97
|
+
* @param exec - 这次工具调用(退回那一档要读它的会话工作区)。
|
|
98
|
+
* @returns 工坊的 packages 目录、根路径与标题。
|
|
99
|
+
*/
|
|
91
100
|
export declare function workshopOf(ctx: DshHostContext, exec: DshToolRunContext): Promise<{
|
|
92
101
|
readonly packagesDir: string;
|
|
93
102
|
readonly workspace: string | null;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { DshShell } from '@dsh-workbench/shared';
|
|
2
|
+
/**
|
|
3
|
+
* 两个地址是不是"同一页":比之前先把查询串 / 锚点 / 末尾斜杠抹掉 —— `open` 导航之后
|
|
4
|
+
* 地址栏常常多一个斜杠或一串 hash,那不算开错了页。
|
|
5
|
+
* @param here - 那一页现在真实所在的地址(`location.href`)。@param want - 我们要它去的地址。@returns 同一页为 `true`。
|
|
6
|
+
*/
|
|
7
|
+
export declare function samePageOf(here: string, want: string): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* 把录制那一页开起来,并且**确认它真的开起来了**才回来。
|
|
10
|
+
*
|
|
11
|
+
* 先收掉这条会话的残留(否则新页会开进上一次留下的那扇旧窗里,不顶前台、看上去"根本没弹窗");
|
|
12
|
+
* 再 `tab new --window foreground` 开我们的页;**然后读一次 `location.href` 核实** —— 开出来是空白页
|
|
13
|
+
* 或别的地址时,回落到 opencli 的 `open`(它落在会话租约那个标签上)再核一次;两次都不对就
|
|
14
|
+
* `ok: false`,让调用方把这句话原样报给 agent,**不许在没开起来的页上装浮条说"已开始录制"**。
|
|
15
|
+
* @param input - 这条会话的名字 / 目标地址 / 捕获文件(收残留那一拍要用)。
|
|
16
|
+
* @returns `ok: false` 时 `at` 是那一页现在真实的地址(`''` = 空白页),`why` 是给 agent 看的一句话。
|
|
17
|
+
*/
|
|
18
|
+
export declare function openRecordPage(input: {
|
|
19
|
+
readonly shell: DshShell;
|
|
20
|
+
readonly command: string;
|
|
21
|
+
readonly session: string;
|
|
22
|
+
readonly url: string;
|
|
23
|
+
readonly captureFile: string;
|
|
24
|
+
}): Promise<{
|
|
25
|
+
readonly ok: boolean;
|
|
26
|
+
readonly at: string;
|
|
27
|
+
readonly why: string;
|
|
28
|
+
}>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { RecordExpect, RecordMove, RecordRefusal, RecordState } from '@dsh-workbench/shared';
|
|
1
|
+
import type { RecordExpect, RecordMove, RecordRefusal, RecordState } from '@dsh-workbench/shared/host';
|
|
2
2
|
export interface RecordCall {
|
|
3
3
|
readonly key: string;
|
|
4
4
|
readonly method: string;
|
|
@@ -20,8 +20,18 @@ export interface RecordPageRead {
|
|
|
20
20
|
readonly here: string;
|
|
21
21
|
readonly dropped: number;
|
|
22
22
|
readonly unhooked: boolean;
|
|
23
|
+
readonly shots: number;
|
|
24
|
+
readonly shotsAt: number | null;
|
|
25
|
+
readonly beat: boolean;
|
|
26
|
+
readonly beatKind: string;
|
|
23
27
|
readonly calls: readonly RecordCall[];
|
|
24
28
|
}
|
|
29
|
+
export interface RecordShotLine {
|
|
30
|
+
readonly n: number;
|
|
31
|
+
readonly at: number;
|
|
32
|
+
readonly path: string;
|
|
33
|
+
readonly error?: string;
|
|
34
|
+
}
|
|
25
35
|
export declare function recordPageOf(payload: Record<string, unknown> | undefined): RecordPageRead;
|
|
26
36
|
export declare function filterTextOf(expect: RecordExpect | null): string;
|
|
27
37
|
export declare function probeCommandOf(calls: readonly RecordCall[]): string;
|
|
@@ -61,8 +71,12 @@ export interface RecordReadFacts {
|
|
|
61
71
|
readonly nav: boolean;
|
|
62
72
|
readonly filter: string;
|
|
63
73
|
readonly captureRound: string;
|
|
74
|
+
readonly shots?: readonly RecordShotLine[];
|
|
75
|
+
readonly beats?: number;
|
|
76
|
+
readonly beatMs?: number;
|
|
64
77
|
}
|
|
65
78
|
export declare function renderRecordRead(input: RecordReadFacts): string;
|
|
79
|
+
/** 详情那一拍的原料:入参 / 出参 / 请求头那几块原文的渲染全靠它。 */
|
|
66
80
|
export interface RecordDetailFacts {
|
|
67
81
|
readonly found: boolean;
|
|
68
82
|
readonly where: string;
|
|
@@ -83,6 +97,7 @@ export interface RecordDetailFacts {
|
|
|
83
97
|
readonly maxBytes: number;
|
|
84
98
|
readonly note: string;
|
|
85
99
|
}
|
|
100
|
+
/** 把字段证据表插进详情正文:接在出参那一块之后、`请求头` 那一行(或第一条附注)之前;没有锚点就接在末尾。空串原样回。 */
|
|
86
101
|
export declare function renderRecordDetail(input: RecordDetailFacts): string;
|
|
87
102
|
export declare function moveOrThrow(move: RecordMove, held: RecordState | null): RecordState;
|
|
88
103
|
export declare function domainOf(url: string): string;
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
import type { DshHostContext
|
|
1
|
+
import type { DshHostContext } from '@dsh-workbench/shared';
|
|
2
|
+
import type { RecordStore } from '@dsh-workbench/shared/host';
|
|
2
3
|
export declare function registerRecordTools(ctx: DshHostContext, store: RecordStore): readonly (() => void)[];
|
|
@@ -1,16 +1,36 @@
|
|
|
1
|
-
import type { DshHostContext, DshShell
|
|
1
|
+
import type { DshHostContext, DshShell } from '@dsh-workbench/shared';
|
|
2
|
+
import type { RecordState, RecordStore } from '@dsh-workbench/shared/host';
|
|
2
3
|
import type { CaptureEntry } from './diy-record-capture';
|
|
3
4
|
import type { RecordCall, RecordListRow, RecordPageRead } from './diy-record-text';
|
|
4
5
|
export declare const OPEN_TIMEOUT_MS = 30000;
|
|
5
6
|
export declare const SETTLE_MS = 2000;
|
|
6
7
|
export declare const WAIT_CAP_MS: number;
|
|
7
8
|
export declare const POLL_MS = 1000;
|
|
9
|
+
/**
|
|
10
|
+
* 捕获那一层要滤掉的 URL:静态资源(`data:` 与常见后缀)。用法 `new RegExp(...).test(url)`,命中即丢。
|
|
11
|
+
*/
|
|
12
|
+
export declare const STATIC_URL_PATTERN = "^(data:)|(\\.(js|css|png|jpe?g|gif|svg|woff2?|ico|map|webp|mp4)(\\?|$))";
|
|
8
13
|
export declare const DETAIL_MAX_BYTES = 60000;
|
|
9
14
|
export declare const pauseFor: (ms: number) => Promise<void>;
|
|
10
15
|
export declare function waitOf(value: unknown): number;
|
|
11
16
|
export declare function foreignDomainText(design: unknown, url: string): string;
|
|
12
17
|
export declare function captureFileOf(packagesDir: string, unit: string): string;
|
|
13
18
|
export declare function lastCaptureFileOf(file: string): string;
|
|
19
|
+
/**
|
|
20
|
+
* 这一族自己的落图路径:跟 `rec-<unit>.jsonl` 同目录、`rec-shot-` 前缀(工坊那一页的图另有名字)。
|
|
21
|
+
* @param captureFile - 这一场捕获文件;图就落在它旁边。@param unit - 单元名。@param n - 页面「已拍」计数。
|
|
22
|
+
* @returns 形如 `…/shots/rec-shot-<unit>-<n>.png` 的路径。
|
|
23
|
+
*/
|
|
24
|
+
export declare function shotFileOf(captureFile: string, unit: string, n: number): string;
|
|
25
|
+
/**
|
|
26
|
+
* 把这一张现场图落进 `shots/`:argv **只有 `screenshot <path>`,一个视口参数都不给** —— `--full-page` 走
|
|
27
|
+
* `captureBeyondViewport`、`--width` / `--height` 走 `setDeviceMetricsOverride`:真机上按一下页面**看得见地闪**
|
|
28
|
+
* (2026-10-21 两次报过)、还可能让填了一半的表单跳位;调试族那把平时也是这一档(先 `eval` 量盒子再本地 `clip`)。
|
|
29
|
+
* 真机上这个平台整页高与视口一样(1512×698)—— `--full-page` 一点没多给,白闪。
|
|
30
|
+
* @param input - 看护那一拍的依赖(shell / 命令 / 会话 / 台账)。
|
|
31
|
+
* @param held - 当前那份约定(路径按它的 `unit` 拼)。@param n - 页面浮条上那个「已拍」计数。
|
|
32
|
+
*/
|
|
33
|
+
export declare function takeShotOf(input: WatchFinishDeps, held: RecordState, n: number): Promise<void>;
|
|
14
34
|
export declare function startRecordCapture(shell: DshShell, command: string, session: string, file: string): Promise<boolean>;
|
|
15
35
|
export interface WatchFinishDeps {
|
|
16
36
|
readonly ctx: DshHostContext;
|
|
@@ -21,10 +41,29 @@ export interface WatchFinishDeps {
|
|
|
21
41
|
readonly url: string;
|
|
22
42
|
readonly captureFile: string;
|
|
23
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* 看护的一拍(= 一圈循环):读数那一条 `eval` **自己在页面里最多等 `RECORD_WATCH_MS`**(浮条在他按
|
|
46
|
+
* 「开始 / 截图 / 结束」时会唤醒挂在页面上的那个 waiter ⇒ 一按就回来),所以这一轮 = **恰好一条 `eval`**:
|
|
47
|
+
* 命中就立刻做那一拍,没人按就照旧 3 秒一拍 —— 兜底周期一字未变。该落图就落图;认到「结束」就封口、
|
|
48
|
+
* **叫醒 agent**、**关掉录制窗**、收工(关窗排在叫醒之后:关窗慢 / 失败不许拖住叫醒,失败也不抛 ——
|
|
49
|
+
* 那一扇是我们为录制借来的,ADR-0064 补记)。
|
|
50
|
+
* ⚠️ 读数那条**不许**改成 opencli 的 `wait xhr`:它**读一次清一次**那只捕获环(`cdp.js:346-353`),而 reaper
|
|
51
|
+
* 也读同一只环才拿得到 body ⇒ 会把 `rec-<unit>.jsonl` 吃空。理由与实测见 `.scratch/diy-write-record/issues/06-doorbell.md` §1.1。
|
|
52
|
+
* @param input - 看护那一拍的依赖。@returns `stop` = 这条循环可以收了;`delivered` = 这一拍叫醒过 agent。
|
|
53
|
+
*/
|
|
24
54
|
export declare function watchFinishStepOf(input: WatchFinishDeps): Promise<{
|
|
25
55
|
readonly stop: boolean;
|
|
26
56
|
readonly delivered: boolean;
|
|
27
57
|
}>;
|
|
58
|
+
export interface WatchRoundOut {
|
|
59
|
+
readonly stop: boolean;
|
|
60
|
+
readonly delivered: boolean;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* 看护的一轮(就是 `watchFinishStepOf`,留着这个名字是给测试与调用点一个稳定的形)。
|
|
64
|
+
* @param input - 看护那一拍的依赖。@returns 收不收工、有没有叫醒。
|
|
65
|
+
*/
|
|
66
|
+
export declare function watchRoundOf(input: WatchFinishDeps): Promise<WatchRoundOut>;
|
|
28
67
|
export declare function watchRecordFinish(input: WatchFinishDeps): void;
|
|
29
68
|
export interface CaptureRead {
|
|
30
69
|
readonly text: string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PanelUnitReport } from '@dsh-workbench/shared';
|
|
1
|
+
import type { PanelUnitReport } from '@dsh-workbench/shared/host';
|
|
2
2
|
import { type WorkshopModule, type WorkshopSync, type WorkshopToolchain } from '../../../shared/api/host/diy-workshop';
|
|
3
3
|
export declare const SYNC_STALE_MINUTES = 15;
|
|
4
4
|
export declare const SYNC_STALE_MS: number;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { DshHostContext } from '@dsh-workbench/shared';
|
|
2
|
+
import type { LongTaskBase, LongTaskRegistry } from '@dsh-workbench/shared/host';
|
|
3
|
+
/**
|
|
4
|
+
* 当前挂着的单元各自那把长任务注册表(晚绑定的取值器,形状同 `UnitMountDeps.longTaskRuns`)。
|
|
5
|
+
*/
|
|
6
|
+
export type LongTaskRunsLookup = () => ReadonlyMap<string, LongTaskRegistry<LongTaskBase>>;
|
|
7
|
+
/**
|
|
8
|
+
* 注册长任务那两把外壳工具:看得见(`workbench_long_tasks_list`)与停得了任何单元的一趟
|
|
9
|
+
* (`workbench_long_tasks_stop`)。`longTaskRuns` 是晚绑定的取值器:两把都从它取注册表,
|
|
10
|
+
* 所以单元晚挂上 / 被摘掉都跟得上;没给的话停那把一律回"没有这一趟"。
|
|
11
|
+
* @param ctx 宿主上下文,`tools` 服务不在时什么都不注册。
|
|
12
|
+
* @param longTaskRuns 当前挂着的单元各自那把长任务注册表。
|
|
13
|
+
* @returns 注销函数(没有 tools 服务就是空数组)。
|
|
14
|
+
*/
|
|
15
|
+
export declare function registerLongTaskTools(ctx: DshHostContext, longTaskRuns?: LongTaskRunsLookup | undefined): Array<() => void>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -43,14 +43,16 @@ export declare const DIY_RECORD_PREPARE_TOOL = "workbench_diy_record_prepare";
|
|
|
43
43
|
export declare const DIY_RECORD_READ_TOOL = "workbench_diy_record_read";
|
|
44
44
|
export declare const DIY_RECORD_DETAIL_TOOL = "workbench_diy_record_detail";
|
|
45
45
|
export declare const DIY_RECORD_STOP_TOOL = "workbench_diy_record_stop";
|
|
46
|
+
export declare const LONG_TASKS_LIST_TOOL = "workbench_long_tasks_list";
|
|
47
|
+
export declare const LONG_TASKS_STOP_TOOL = "workbench_long_tasks_stop";
|
|
46
48
|
export declare const DIY_DEBUG_START_TOOL = "workbench_diy_debug_start";
|
|
47
49
|
export declare const DIY_DEBUG_PAUSE_TOOL = "workbench_diy_debug_pause";
|
|
48
50
|
export declare const DIY_DEBUG_RESUME_TOOL = "workbench_diy_debug_resume";
|
|
49
51
|
export declare const DIY_DEBUG_STOP_TOOL = "workbench_diy_debug_stop";
|
|
50
|
-
export declare const ALL_TOOLS: readonly ["workbench_panels_list", "workbench_panels_show", "workbench_panels_hide", "workbench_panels_move", "workbench_panels_reset", "workbench_panels_follow", "workbench_modules_list", "workbench_modules_install", "workbench_modules_update", "workbench_modules_uninstall", "workbench_modules_install_bundle", "workbench_modules_update_dependency", "workbench_modules_update_plugin", "workbench_modules_uninstall_plugin", "workbench_modules_update_workbench", "workbench_diy_list", "workbench_diy_status", "workbench_diy_panel", "workbench_diy_components", "workbench_diy_recon", "workbench_diy_spec", "workbench_diy_install", "workbench_diy_show", "workbench_diy_state", "workbench_diy_html", "workbench_diy_find", "workbench_diy_click", "workbench_diy_hover", "workbench_diy_type", "workbench_diy_fill", "workbench_diy_keys", "workbench_diy_scroll", "workbench_diy_wait", "workbench_diy_eval", "workbench_diy_screenshot", "workbench_diy_network", "workbench_diy_dialog", "workbench_diy_logs_watch", "workbench_diy_logs_read", "workbench_diy_logs_stop", "workbench_diy_record_prepare", "workbench_diy_record_read", "workbench_diy_record_detail", "workbench_diy_record_stop", "workbench_diy_debug_start", "workbench_diy_debug_pause", "workbench_diy_debug_resume", "workbench_diy_debug_stop"];
|
|
52
|
+
export declare const ALL_TOOLS: readonly ["workbench_panels_list", "workbench_panels_show", "workbench_panels_hide", "workbench_panels_move", "workbench_panels_reset", "workbench_panels_follow", "workbench_modules_list", "workbench_modules_install", "workbench_modules_update", "workbench_modules_uninstall", "workbench_modules_install_bundle", "workbench_modules_update_dependency", "workbench_modules_update_plugin", "workbench_modules_uninstall_plugin", "workbench_modules_update_workbench", "workbench_diy_list", "workbench_diy_status", "workbench_diy_panel", "workbench_diy_components", "workbench_diy_recon", "workbench_diy_spec", "workbench_diy_install", "workbench_diy_show", "workbench_diy_state", "workbench_diy_html", "workbench_diy_find", "workbench_diy_click", "workbench_diy_hover", "workbench_diy_type", "workbench_diy_fill", "workbench_diy_keys", "workbench_diy_scroll", "workbench_diy_wait", "workbench_diy_eval", "workbench_diy_screenshot", "workbench_diy_network", "workbench_diy_dialog", "workbench_diy_logs_watch", "workbench_diy_logs_read", "workbench_diy_logs_stop", "workbench_diy_record_prepare", "workbench_diy_record_read", "workbench_diy_record_detail", "workbench_diy_record_stop", "workbench_diy_debug_start", "workbench_diy_debug_pause", "workbench_diy_debug_resume", "workbench_diy_debug_stop", "workbench_long_tasks_list", "workbench_long_tasks_stop"];
|
|
51
53
|
export type PanelsToolName = (typeof ALL_TOOLS)[number];
|
|
52
|
-
export declare const READ_TOOLS: readonly ["workbench_panels_list", "workbench_modules_list", "workbench_diy_list", "workbench_diy_status", "workbench_diy_panel", "workbench_diy_components", "workbench_diy_recon", "workbench_diy_spec", "workbench_diy_state", "workbench_diy_html", "workbench_diy_find", "workbench_diy_click", "workbench_diy_hover", "workbench_diy_type", "workbench_diy_fill", "workbench_diy_keys", "workbench_diy_scroll", "workbench_diy_wait", "workbench_diy_eval", "workbench_diy_screenshot", "workbench_diy_network", "workbench_diy_dialog", "workbench_diy_logs_watch", "workbench_diy_logs_read", "workbench_diy_logs_stop", "workbench_diy_record_read", "workbench_diy_record_detail", "workbench_diy_record_stop"];
|
|
53
|
-
export declare const WRITE_TOOLS: readonly ["workbench_panels_show", "workbench_panels_hide", "workbench_panels_move", "workbench_panels_reset", "workbench_panels_follow", "workbench_modules_install", "workbench_modules_update", "workbench_modules_uninstall", "workbench_modules_install_bundle", "workbench_modules_update_dependency", "workbench_modules_update_plugin", "workbench_modules_uninstall_plugin", "workbench_modules_update_workbench", "workbench_diy_install", "workbench_diy_show", "workbench_diy_record_prepare", "workbench_diy_debug_start", "workbench_diy_debug_pause", "workbench_diy_debug_resume", "workbench_diy_debug_stop"];
|
|
54
|
+
export declare const READ_TOOLS: readonly ["workbench_panels_list", "workbench_modules_list", "workbench_diy_list", "workbench_diy_status", "workbench_diy_panel", "workbench_diy_components", "workbench_diy_recon", "workbench_diy_spec", "workbench_diy_state", "workbench_diy_html", "workbench_diy_find", "workbench_diy_click", "workbench_diy_hover", "workbench_diy_type", "workbench_diy_fill", "workbench_diy_keys", "workbench_diy_scroll", "workbench_diy_wait", "workbench_diy_eval", "workbench_diy_screenshot", "workbench_diy_network", "workbench_diy_dialog", "workbench_diy_logs_watch", "workbench_diy_logs_read", "workbench_diy_logs_stop", "workbench_diy_record_read", "workbench_diy_record_detail", "workbench_diy_record_stop", "workbench_long_tasks_list"];
|
|
55
|
+
export declare const WRITE_TOOLS: readonly ["workbench_panels_show", "workbench_panels_hide", "workbench_panels_move", "workbench_panels_reset", "workbench_panels_follow", "workbench_modules_install", "workbench_modules_update", "workbench_modules_uninstall", "workbench_modules_install_bundle", "workbench_modules_update_dependency", "workbench_modules_update_plugin", "workbench_modules_uninstall_plugin", "workbench_modules_update_workbench", "workbench_diy_install", "workbench_diy_show", "workbench_diy_record_prepare", "workbench_diy_debug_start", "workbench_diy_debug_pause", "workbench_diy_debug_resume", "workbench_diy_debug_stop", "workbench_long_tasks_stop"];
|
|
54
56
|
export declare const DIY_TOOLS: readonly ["workbench_diy_list", "workbench_diy_status", "workbench_diy_panel", "workbench_diy_components", "workbench_diy_recon", "workbench_diy_spec", "workbench_diy_install", "workbench_diy_show", "workbench_diy_state", "workbench_diy_html", "workbench_diy_find", "workbench_diy_click", "workbench_diy_hover", "workbench_diy_type", "workbench_diy_fill", "workbench_diy_keys", "workbench_diy_scroll", "workbench_diy_wait", "workbench_diy_eval", "workbench_diy_screenshot", "workbench_diy_network", "workbench_diy_dialog", "workbench_diy_logs_watch", "workbench_diy_logs_read", "workbench_diy_logs_stop"];
|
|
55
57
|
export declare const DIY_RECORD_TOOLS: readonly ["workbench_diy_record_prepare", "workbench_diy_record_read", "workbench_diy_record_detail", "workbench_diy_record_stop"];
|
|
56
58
|
export declare const DIY_DEBUG_TOOLS: readonly ["workbench_diy_debug_start", "workbench_diy_debug_pause", "workbench_diy_debug_resume", "workbench_diy_debug_stop"];
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { currentSessionId, orderByRecency, sessionNode, orderSessionRows, sessionRows, workspaceOfSession, sessionStatuses, visiblePendingKind, type SessionNode, type SessionStatusDot, } from './model/session-node';
|
|
2
2
|
export { scrollEdgesOf, type ScrollEdges } from './model/scroll-edges';
|
|
3
|
-
export { attentionLabel, attentionOf, attentionOfSession, attentionSessionIdOf, attentionWordLabel, type Attention, type AttentionInput, type AttentionSeverity, type WorkspaceAttention, } from './model/attention';
|
|
3
|
+
export { attentionLabel, attentionOf, attentionOfSession, attentionSessionIdOf, attentionWordLabel, longTaskPresenceOf, type Attention, type AttentionInput, type AttentionSeverity, type LongTaskPresence, type WorkspaceAttention, } from './model/attention';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,9 +1,29 @@
|
|
|
1
1
|
import type { SessionStatusSnapshot, SessionSummary, SessionsSnapshot, Translate, WorkspaceView } from '@dsh-workbench/shared';
|
|
2
2
|
export type AttentionSeverity = 'warning' | 'ongoing' | 'done';
|
|
3
|
+
/**
|
|
4
|
+
* 一个工作区那颗状态点:`count` 是**会话**条数(照旧,长任务没有会话可归时它是 0);
|
|
5
|
+
* `longTask` 为真表示这一档说的是长任务而不是会话 —— 只改那句人话的措辞。
|
|
6
|
+
*/
|
|
3
7
|
export interface WorkspaceAttention {
|
|
4
8
|
readonly severity: AttentionSeverity;
|
|
5
9
|
readonly count: number;
|
|
10
|
+
readonly longTask?: boolean | undefined;
|
|
6
11
|
}
|
|
12
|
+
/** 还在跑的长任务按两边归:会话行那颗 loading 点看 `bySession`,项目下拉每一行看 `byWorkspace`(按工作区路径数条数)。 */
|
|
13
|
+
export interface LongTaskPresence {
|
|
14
|
+
readonly bySession: ReadonlySet<string>;
|
|
15
|
+
readonly byWorkspace: ReadonlyMap<string, number>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 把外壳那条长任务读口交回的行折成两侧的归属:有会话可归的进 `bySession`(那一行自己亮),
|
|
19
|
+
* 每一条都按它属于哪个工作区进 `byWorkspace`(没有会话可归的那些只有这一侧看得见)。
|
|
20
|
+
* @param rows 还在跑的长任务;每行要有它属于哪个工作区,会话那格可以没有。
|
|
21
|
+
* @returns 两侧的归属;空输入就是两侧都空。
|
|
22
|
+
*/
|
|
23
|
+
export declare function longTaskPresenceOf(rows: readonly {
|
|
24
|
+
readonly workspace: string;
|
|
25
|
+
readonly session?: string | undefined;
|
|
26
|
+
}[]): LongTaskPresence;
|
|
7
27
|
export interface Attention {
|
|
8
28
|
readonly byWorkspace: ReadonlyMap<string, WorkspaceAttention>;
|
|
9
29
|
readonly elsewhere: WorkspaceAttention | null;
|
|
@@ -15,13 +35,15 @@ export interface AttentionInput {
|
|
|
15
35
|
readonly statusById: SessionStatusSnapshot;
|
|
16
36
|
readonly archivedIds: ReadonlySet<string>;
|
|
17
37
|
readonly currentWorkspaceId: string | null;
|
|
38
|
+
readonly longTasks?: LongTaskPresence | undefined;
|
|
18
39
|
}
|
|
19
|
-
export declare function attentionOfSession(summary: SessionSummary, statusById: SessionStatusSnapshot, sessions: SessionsSnapshot): AttentionSeverity | undefined;
|
|
40
|
+
export declare function attentionOfSession(summary: SessionSummary, statusById: SessionStatusSnapshot, sessions: SessionsSnapshot, longTaskIds?: ReadonlySet<string>): AttentionSeverity | undefined;
|
|
20
41
|
export interface AttentionPickInput {
|
|
21
42
|
readonly workspace: WorkspaceView | null;
|
|
22
43
|
readonly sessions: SessionsSnapshot;
|
|
23
44
|
readonly statusById: SessionStatusSnapshot;
|
|
24
45
|
readonly archivedIds: ReadonlySet<string>;
|
|
46
|
+
readonly longTasks?: LongTaskPresence | undefined;
|
|
25
47
|
}
|
|
26
48
|
export declare function attentionSessionIdOf(input: AttentionPickInput): string | null;
|
|
27
49
|
export declare function attentionOf(input: AttentionInput): Attention;
|
|
@@ -5,6 +5,7 @@ export interface SessionNode {
|
|
|
5
5
|
readonly blank: boolean;
|
|
6
6
|
readonly running: boolean;
|
|
7
7
|
readonly runningSubagentCount: number;
|
|
8
|
+
readonly longTask: boolean;
|
|
8
9
|
readonly completed: boolean;
|
|
9
10
|
readonly pinned: boolean;
|
|
10
11
|
readonly updatedAt: number;
|
|
@@ -16,7 +17,17 @@ export interface SessionStatusDot {
|
|
|
16
17
|
readonly trailingLabel?: string;
|
|
17
18
|
}
|
|
18
19
|
export declare function visiblePendingKind(kind: string | undefined): string | undefined;
|
|
19
|
-
|
|
20
|
+
/**
|
|
21
|
+
* 把一条会话读成行上要的那几格。
|
|
22
|
+
* @param summary 会话摘要。
|
|
23
|
+
* @param statusById 会话状态表。
|
|
24
|
+
* @param list 会话快照(子代理花名册在这里)。
|
|
25
|
+
* @param pinnedIds 置顶的那些。
|
|
26
|
+
* @param archivedIds 归档的那些。
|
|
27
|
+
* @param longTaskIds 有长任务在跑的那些会话(外壳那条读口按“这趟是谁起的”算出来的);不给就是都没有。
|
|
28
|
+
* @returns 行模型;`longTask` 为真表示这条会话起的某趟长任务还在跑(它自己那一轮可能早结束了)。
|
|
29
|
+
*/
|
|
30
|
+
export declare function sessionNode(summary: SessionSummary, statusById: SessionStatusSnapshot, list: SessionsSnapshot, pinnedIds: ReadonlySet<string>, archivedIds: ReadonlySet<string>, longTaskIds?: ReadonlySet<string>): SessionNode;
|
|
20
31
|
export declare function sessionStatuses(node: SessionNode, t: Translate): SessionStatusDot[];
|
|
21
32
|
export declare function currentSessionId(list: SessionsSnapshot, panelActive: boolean): string | undefined;
|
|
22
33
|
export declare function orderByRecency(rows: readonly SessionSummary[]): SessionSummary[];
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { DshToolRunContext } from '@dsh-workbench/shared';
|
|
2
|
+
import { type ApprovalCall } from '@dsh-workbench/shared/host';
|
|
3
|
+
/**
|
|
4
|
+
* 审批那一拍看到的这次工具调用:工具名 / 实参 / 所在工作区 / 发起的会话。
|
|
5
|
+
*/
|
|
6
|
+
export interface ApprovalExec {
|
|
7
|
+
readonly arguments?: unknown;
|
|
8
|
+
readonly agent?: DshToolRunContext['agent'];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* 把 dsh 交来的那次工具调用折成审批策略要看的那份 call:工作区与发起会话都从 `exec` 现取,取不到就不写那个键。
|
|
12
|
+
* @param exec dsh 交来的执行上下文;没有就只有实参这一格。
|
|
13
|
+
* @param name 工具名;`undefined` = 这次调用没名字,不写这个键。
|
|
14
|
+
* @returns 交给 `approvalFor` 的 `ApprovalCall`。
|
|
15
|
+
*/
|
|
16
|
+
export declare function approvalCallOf(exec: ApprovalExec | undefined, name: string | undefined): ApprovalCall;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { LongTaskBase, LongTaskChange } from '@dsh-workbench/shared/host';
|
|
2
|
+
import { type AgentsLookup, type NoticeDelivery, type NoticeMessage } from './diy-debug-act';
|
|
3
|
+
/**
|
|
4
|
+
* 这条 notice 的来源标识(`source.kind`)—— 长任务自己一格,与调试 / 录制分开。
|
|
5
|
+
*/
|
|
6
|
+
export declare const LONG_TASK_NOTICE_KIND = "workbench-long-task";
|
|
7
|
+
/**
|
|
8
|
+
* 一条"这一趟被停了"的 notice:发给谁(`session`)、说什么(`message` / `summary`)。
|
|
9
|
+
*/
|
|
10
|
+
export interface LongTaskStopNotice {
|
|
11
|
+
readonly session: string;
|
|
12
|
+
readonly message: NoticeMessage;
|
|
13
|
+
readonly summary: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* 判这一趟被停要不要回流给发起它的那个会话,并把话写好。
|
|
17
|
+
* 只有"结算 + `stopped` + 这一趟记得会话 + 不是它自己停的"才回一条;跑完 / 失败 / 界面起的(没记会话)/ 自己停的回 `null`。
|
|
18
|
+
* @param unit 这一趟属于哪个单元(进文案)。
|
|
19
|
+
* @param change 长任务那一次变更;只有 `kind: 'settle'` 且 `snapshot.status === 'stopped'` 才可能成。
|
|
20
|
+
* @returns 该发的那条(带收件会话),或者 `null`(不发)。
|
|
21
|
+
*/
|
|
22
|
+
export declare function longTaskStopNotice(unit: string, change: LongTaskChange<LongTaskBase>): LongTaskStopNotice | null;
|
|
23
|
+
/**
|
|
24
|
+
* 把"这一趟被停了"送出去:正在跑的会话 inject 一条、闲着的跳过、没有这个 agent 就当没发生(模式 `drop`)。
|
|
25
|
+
* @param agents 会话表(宿主从 `ctx.get('agents')` 拿);拿不到就什么都不发。
|
|
26
|
+
* @param unit 这一趟属于哪个单元。
|
|
27
|
+
* @param change 长任务那一次变更。
|
|
28
|
+
* @returns 送出去的结果;判据说不该发(`longTaskStopNotice` 回 `null`)就回 `null`。
|
|
29
|
+
*/
|
|
30
|
+
export declare function notifyLongTaskStop(agents: AgentsLookup | undefined, unit: string, change: LongTaskChange<LongTaskBase>): NoticeDelivery | null;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { LongTaskBase, LongTaskRegistry } from '@dsh-workbench/shared/host';
|
|
2
|
+
import type { ShellLongTaskRow } from '../long-tasks';
|
|
3
|
+
/**
|
|
4
|
+
* 把「单元 → 它那把长任务注册表」收成一条条**还在跑**的行(`live()` 已经只给活着的那几趟,
|
|
5
|
+
* 收尾的是面板那一行的事);注册表是上一版、没有 `live()` 就跳过这个单元 —— 与停止那条动作
|
|
6
|
+
* 同一个理由:那一版里没有这份读口,不是这一版坏了。
|
|
7
|
+
* @param runs 当前挂着的单元各自那把长任务注册表(外壳进程里就这一份)。
|
|
8
|
+
* @returns 正在跑的长任务(含 `needs-login` 那种),每条带上是哪个单元起的。
|
|
9
|
+
*/
|
|
10
|
+
export declare function liveLongTaskRows(runs: ReadonlyMap<string, LongTaskRegistry<LongTaskBase>>): ShellLongTaskRow[];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { DshHostContext } from '@dsh-workbench/shared';
|
|
2
2
|
import type { UnitManifest } from '@dsh-workbench/shared/host';
|
|
3
3
|
import { type RouteTable } from '@dsh-workbench/shared/host';
|
|
4
|
-
import type { ContextRenderer, UnitMountDeps, UnitMountResult, UnitCall } from '@dsh-workbench/shared/host';
|
|
4
|
+
import type { ContextRenderer, LongTaskBase, LongTaskChange, LongTaskRegistry, UnitMountDeps, UnitMountResult, UnitCall } from '@dsh-workbench/shared/host';
|
|
5
5
|
import type { HostUnit } from './unit-registry';
|
|
6
6
|
import { type UnitReadiness } from './unit-readiness';
|
|
7
7
|
export declare function declaredNotesOf(plans: Iterable<{
|
|
@@ -67,6 +67,8 @@ export declare function mountUnits(units: readonly HostUnit[], deps: {
|
|
|
67
67
|
longTasks?: {
|
|
68
68
|
readonly scope: string;
|
|
69
69
|
readonly bump: (facts: readonly string[]) => void;
|
|
70
|
+
readonly notify?: ((change: LongTaskChange<LongTaskBase>) => void) | undefined;
|
|
70
71
|
} | undefined;
|
|
72
|
+
longTaskRuns?: Map<string, LongTaskRegistry<LongTaskBase>> | undefined;
|
|
71
73
|
}): UnitsPlan;
|
|
72
74
|
export {};
|
|
@@ -26,9 +26,15 @@ export interface PreflightSnapshot {
|
|
|
26
26
|
readonly guidance?: PreflightGuidance;
|
|
27
27
|
readonly bridgeIssues?: readonly PreflightBridgeIssue[];
|
|
28
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* 环境体检:`run()` 跑一次并回快照,`snapshot()` 看眼下这一拍。`run` 的取舍是:
|
|
31
|
+
* 已经有在跑的那一轮就跟它(不再排第二轮);上一次跑出 `ready` 且不到 `PREFLIGHT_FRESH_MS`
|
|
32
|
+
* 也直接回那一份 —— 于是两页同时打开只体检一轮、第二页还不用等。`hard` 不看新鲜度、一律重来,
|
|
33
|
+
* 留给用户按的那颗「重试」;上一次是失败/没跑完的也一律重来。
|
|
34
|
+
*/
|
|
29
35
|
export interface Preflight {
|
|
30
36
|
run(options?: {
|
|
31
|
-
|
|
37
|
+
hard?: boolean;
|
|
32
38
|
}): Promise<PreflightSnapshot>;
|
|
33
39
|
snapshot(): PreflightSnapshot;
|
|
34
40
|
}
|
|
@@ -4,4 +4,13 @@ export interface WorkshopRoot {
|
|
|
4
4
|
readonly packagesDir: string;
|
|
5
5
|
readonly title: string;
|
|
6
6
|
}
|
|
7
|
+
/**
|
|
8
|
+
* 工坊**自己的家**(`$DSH_HOME/workbench/diy`)—— 宿主启动时就是按它把工坊登记成工作区的。
|
|
9
|
+
* DIY 会话的工坊根取这一份,**不随会话 cwd 漂**:cwd 一离开工坊(用户把自己的项目设成工作区),
|
|
10
|
+
* 从 cwd 往上找会撞上任意一个"带 `packages/` 的仓库",于是模块列表、`shots/` 落点、装机名单全跟着变样。
|
|
11
|
+
* 这一档拿不到(没有 `dshHomePath` 服务)时才退回 cwd 那套。
|
|
12
|
+
* @param ctx - 宿主上下文(要从里面取 `dshHomePath`)。
|
|
13
|
+
* @returns 工坊的家;取不到给 `null`。
|
|
14
|
+
*/
|
|
15
|
+
export declare function workshopHomeOf(ctx: DshHostContext): WorkshopRoot | null;
|
|
7
16
|
export declare function workshopRootOf(ctx: DshHostContext, workspace: string | null): Promise<WorkshopRoot | null>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { LongTaskStatus } from '@dsh-workbench/shared';
|
|
2
|
+
/**
|
|
3
|
+
* 外壳自己的长任务读口:一次把**全进程还在跑的**长任务交出来(不分单元、不分工作区)——
|
|
4
|
+
* 左栏拿它点会话行与项目下拉里那颗 loading 点。
|
|
5
|
+
*/
|
|
6
|
+
export declare const LONG_TASKS_ROUTE = "long-tasks";
|
|
7
|
+
/** 一条还在跑的长任务:左栏只要这几格,模块自己那份投影不参与。 */
|
|
8
|
+
export interface ShellLongTaskRow {
|
|
9
|
+
readonly unit: string;
|
|
10
|
+
readonly id: string;
|
|
11
|
+
readonly workspace: string;
|
|
12
|
+
readonly target: string;
|
|
13
|
+
readonly status: LongTaskStatus;
|
|
14
|
+
readonly session?: string | undefined;
|
|
15
|
+
}
|
|
16
|
+
/** 外壳长任务读口的载荷。 */
|
|
17
|
+
export interface ShellLongTasksPayload {
|
|
18
|
+
readonly runs: readonly ShellLongTaskRow[];
|
|
19
|
+
}
|