@dsh-workbench/shared 0.3.0 → 0.3.2
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/components/MockStrip.css +1 -11
- package/dist/config/constants.d.ts +1 -0
- package/dist/config/constants.js +1 -0
- package/dist/host/browser-find.js +2 -2
- package/dist/host/browser-read.d.ts +2 -1
- package/dist/host/browser-read.js +3 -2
- package/dist/lib/lint-probe.d.ts +1 -0
- package/dist/lib/lint-probe.js +6 -0
- package/package.json +2 -2
- package/src/config/constants.ts +2 -0
- package/src/host/__tests__/browser-find.test.ts +11 -0
- package/src/host/__tests__/browser-read.test.ts +33 -2
- package/src/host/browser-find.ts +2 -2
- package/src/host/browser-read.ts +3 -2
- package/dist/components/StateDot.d.ts +0 -5
- package/dist/components/StateDot.js +0 -10
- package/dist/host/__tests__/fakes.d.ts +0 -41
- package/dist/host/__tests__/fakes.js +0 -76
- package/dist/host/__tests__/preflight-fixtures.d.ts +0 -19
- package/dist/host/__tests__/preflight-fixtures.js +0 -47
- package/dist/host/context-inject.d.ts +0 -24
- package/dist/host/context-inject.js +0 -74
- package/dist/host/events.d.ts +0 -9
- package/dist/host/events.js +0 -70
- package/dist/host/mount-units.d.ts +0 -52
- package/dist/host/mount-units.js +0 -186
- package/dist/host/preflight.d.ts +0 -58
- package/dist/host/preflight.js +0 -233
- package/dist/host/probe-cache.d.ts +0 -6
- package/dist/host/probe-cache.js +0 -19
- package/dist/host/probe-libs.d.ts +0 -45
- package/dist/host/probe-libs.js +0 -61
- package/dist/host/probe-units.d.ts +0 -17
- package/dist/host/probe-units.js +0 -102
- package/dist/host/probe-verdict.d.ts +0 -7
- package/dist/host/probe-verdict.js +0 -143
- package/dist/host/tool-scope.d.ts +0 -32
- package/dist/host/tool-scope.js +0 -84
- package/dist/host/unit-readiness.d.ts +0 -7
- package/dist/host/unit-readiness.js +0 -5
- package/dist/host/unit-registry.d.ts +0 -9
- package/dist/host/unit-registry.js +0 -33
- package/dist/host/version-range.d.ts +0 -2
- package/dist/host/version-range.js +0 -73
|
@@ -36,14 +36,4 @@
|
|
|
36
36
|
border-color: transparent;
|
|
37
37
|
background: var(--dsw-alias-brand-primary);
|
|
38
38
|
color: var(--dsw-alias-label-primary-foreground);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
.wb-mockChipOn:hover {
|
|
42
|
-
border-color: transparent;
|
|
43
|
-
background: var(--dsw-alias-brand-primary);
|
|
44
|
-
color: var(--dsw-alias-label-primary-foreground);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
.wb-panelBody.wb-panelMany .wb-mockStrip {
|
|
48
|
-
display: none;
|
|
49
|
-
}
|
|
39
|
+
}
|
|
@@ -31,6 +31,7 @@ export declare const DEFAULT_WORKSPACE_TITLE = "default-workspace";
|
|
|
31
31
|
export declare const STORAGE_KEY_PROJECT = "dsh-workbench.project";
|
|
32
32
|
export declare const STORAGE_KEY_RIGHTBAR = "dsh-workbench.rightbar.expanded";
|
|
33
33
|
export declare const STORAGE_KEY_RIGHTBAR_COLUMN = "dsh-workbench.rightbar.column";
|
|
34
|
+
export declare const STORAGE_KEY_RIGHTBAR_WIDTH = "dsh-workbench.rightbar.width";
|
|
34
35
|
export declare const RIGHTBAR_FULLSCREEN_WIDTH = 768;
|
|
35
36
|
export declare const LOCALE_NS = "workbench";
|
|
36
37
|
export declare const LOCALES: readonly ["zh", "en"];
|
package/dist/config/constants.js
CHANGED
|
@@ -34,6 +34,7 @@ export const DEFAULT_WORKSPACE_TITLE = "default-workspace";
|
|
|
34
34
|
export const STORAGE_KEY_PROJECT = "dsh-workbench.project";
|
|
35
35
|
export const STORAGE_KEY_RIGHTBAR = "dsh-workbench.rightbar.expanded";
|
|
36
36
|
export const STORAGE_KEY_RIGHTBAR_COLUMN = "dsh-workbench.rightbar.column";
|
|
37
|
+
export const STORAGE_KEY_RIGHTBAR_WIDTH = "dsh-workbench.rightbar.width";
|
|
37
38
|
export const RIGHTBAR_FULLSCREEN_WIDTH = 768;
|
|
38
39
|
export const LOCALE_NS = "workbench";
|
|
39
40
|
export const LOCALES = ["zh", "en"];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { parseJsonLoose } from '../lib/text.js';
|
|
2
|
-
import {
|
|
2
|
+
import { BROWSER_READ_SESSION } from './browser-read.js';
|
|
3
3
|
import { backgroundLine } from './opencli-window.js';
|
|
4
4
|
import { runShell } from './shell.js';
|
|
5
5
|
const DEFAULT_TIMEOUT_MS = 25000;
|
|
@@ -152,7 +152,7 @@ async function openOnce(shell, command, session, url, timeoutMs) {
|
|
|
152
152
|
export async function browserFind(options) {
|
|
153
153
|
const { shell, css, url, open = true } = options;
|
|
154
154
|
const command = options.command ?? 'opencli';
|
|
155
|
-
const session = options.session ??
|
|
155
|
+
const session = options.session ?? BROWSER_READ_SESSION;
|
|
156
156
|
const limit = options.limit ?? DEFAULT_LIMIT;
|
|
157
157
|
const textMax = options.textMax ?? DEFAULT_TEXT_MAX;
|
|
158
158
|
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { DshShell } from '../api/dsh.js';
|
|
2
|
-
export declare const
|
|
2
|
+
export declare const BROWSER_SCREEN_SESSION = "workbench";
|
|
3
|
+
export declare const BROWSER_READ_SESSION = "workbench-read";
|
|
3
4
|
export interface BrowserReadResult {
|
|
4
5
|
readonly ok: boolean;
|
|
5
6
|
readonly code: string;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { parseJsonLoose, tailLines } from '../lib/text.js';
|
|
2
2
|
import { backgroundLine } from './opencli-window.js';
|
|
3
3
|
import { runShell } from './shell.js';
|
|
4
|
-
export const
|
|
4
|
+
export const BROWSER_SCREEN_SESSION = 'workbench';
|
|
5
|
+
export const BROWSER_READ_SESSION = 'workbench-read';
|
|
5
6
|
const DEFAULT_TIMEOUT_MS = 25000;
|
|
6
7
|
const DEFAULT_RETRY_MS = 1200;
|
|
7
8
|
const DEFAULT_SETTLE_MS = 900;
|
|
@@ -158,7 +159,7 @@ async function openOnce(shell, command, session, url, timeoutMs) {
|
|
|
158
159
|
export async function browserRead(options) {
|
|
159
160
|
const { shell, script, url, loginUrl, open = true } = options;
|
|
160
161
|
const command = options.command ?? 'opencli';
|
|
161
|
-
const session = options.session ??
|
|
162
|
+
const session = options.session ?? BROWSER_READ_SESSION;
|
|
162
163
|
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
163
164
|
const retryMs = options.retryMs ?? DEFAULT_RETRY_MS;
|
|
164
165
|
const settleMs = options.settleMs ?? DEFAULT_SETTLE_MS;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function useLintProbe(): number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dsh-workbench/shared",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "工作台与模块共享的纯代码与契约类型:类型、纯逻辑、哑组件、平台解析。宿主侧的东西从 /host 进(不影响浏览器产物)。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
},
|
|
41
41
|
"scripts": {
|
|
42
42
|
"build": "tsc -p tsconfig.build.json && node scripts/copy-css.mjs",
|
|
43
|
-
"typecheck": "tsc --noEmit",
|
|
43
|
+
"typecheck": "tsc --noEmit --incremental --tsBuildInfoFile node_modules/.cache/tsconfig.tsbuildinfo",
|
|
44
44
|
"test": "vitest run",
|
|
45
45
|
"coverage": "vitest run --coverage",
|
|
46
46
|
"barrels": "node scripts/barrels.mjs",
|
package/src/config/constants.ts
CHANGED
|
@@ -65,6 +65,8 @@ export const STORAGE_KEY_RIGHTBAR = "dsh-workbench.rightbar.expanded";
|
|
|
65
65
|
|
|
66
66
|
export const STORAGE_KEY_RIGHTBAR_COLUMN = "dsh-workbench.rightbar.column";
|
|
67
67
|
|
|
68
|
+
export const STORAGE_KEY_RIGHTBAR_WIDTH = "dsh-workbench.rightbar.width";
|
|
69
|
+
|
|
68
70
|
export const RIGHTBAR_FULLSCREEN_WIDTH = 768;
|
|
69
71
|
|
|
70
72
|
export const LOCALE_NS = "workbench";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { BROWSER_SCREEN_SESSION } from '../browser-read.js';
|
|
2
3
|
import {
|
|
3
4
|
browserFind,
|
|
4
5
|
cellRows,
|
|
@@ -317,3 +318,13 @@ describe('browserFind · 后台静默(问页 + 读 + 打开都不许抢焦点
|
|
|
317
318
|
expect(backgroundless(commands)).toEqual([]);
|
|
318
319
|
});
|
|
319
320
|
});
|
|
321
|
+
|
|
322
|
+
describe('browserFind · 读有它自己那条会话(不许碰屏那条)', () => {
|
|
323
|
+
it('默认那条命令里一条都不带屏那条会话名', async () => {
|
|
324
|
+
const { shell, commands } = fakeShell({ url: ['about:blank', SITE], find: [{ stdout: MATCH }] });
|
|
325
|
+
await browserFind({ shell, css: CSS, url: SITE, ...FAST });
|
|
326
|
+
|
|
327
|
+
expect(commands.length).toBeGreaterThan(0);
|
|
328
|
+
expect(commands.some((one) => one.includes(`'${BROWSER_SCREEN_SESSION}'`))).toBe(false);
|
|
329
|
+
});
|
|
330
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { browserRead } from '../browser-read.js';
|
|
2
|
+
import { BROWSER_READ_SESSION, BROWSER_SCREEN_SESSION, browserRead } from '../browser-read.js';
|
|
3
3
|
|
|
4
4
|
interface Step {
|
|
5
5
|
readonly stdout?: string;
|
|
@@ -45,7 +45,7 @@ describe('browserRead · 读成功', () => {
|
|
|
45
45
|
expect(out.ok).toBe(true);
|
|
46
46
|
expect(out.code).toBe('ok');
|
|
47
47
|
expect(out.value).toEqual({ ok: true, n: 3 });
|
|
48
|
-
expect(commands[0]).toContain(
|
|
48
|
+
expect(commands[0]).toContain(`'browser' '${BROWSER_READ_SESSION}' 'eval'`);
|
|
49
49
|
});
|
|
50
50
|
|
|
51
51
|
it('脚本没回 ok 字段(例如直接回一个数)也算读到了', async () => {
|
|
@@ -123,6 +123,37 @@ describe('browserRead · 两拍(不在站点上就先打开)', () => {
|
|
|
123
123
|
});
|
|
124
124
|
});
|
|
125
125
|
|
|
126
|
+
describe('browserRead · 读有它自己那条会话(不许碰屏那条)', () => {
|
|
127
|
+
it('默认那条不是工坊绑用户当前页用的那条', () => {
|
|
128
|
+
expect(BROWSER_READ_SESSION).not.toBe(BROWSER_SCREEN_SESSION);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('两拍三条命令里一条都不带屏那条会话名', async () => {
|
|
132
|
+
const { shell, commands } = fakeShell([
|
|
133
|
+
{ stdout: JSON.stringify({ ok: false, code: 'not-on-site' }) },
|
|
134
|
+
{ stdout: 'opened' },
|
|
135
|
+
{ stdout: JSON.stringify({ ok: true, n: 1 }) },
|
|
136
|
+
]);
|
|
137
|
+
const out = await browserRead({ shell, script, url: URL, ...FAST });
|
|
138
|
+
|
|
139
|
+
expect(out.ok).toBe(true);
|
|
140
|
+
expect(commands.some((one) => one.includes(`'${BROWSER_SCREEN_SESSION}'`))).toBe(false);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('调用点自己给的 session 说了算(模块可以自己开一条)', async () => {
|
|
144
|
+
const { shell, commands } = fakeShell([
|
|
145
|
+
{ stdout: JSON.stringify({ ok: false, code: 'not-on-site' }) },
|
|
146
|
+
{ stdout: 'opened' },
|
|
147
|
+
{ stdout: JSON.stringify({ ok: true }) },
|
|
148
|
+
]);
|
|
149
|
+
const out = await browserRead({ shell, script, url: URL, session: 'llm-wallet', ...FAST });
|
|
150
|
+
|
|
151
|
+
expect(out.ok).toBe(true);
|
|
152
|
+
expect(commands.every((one) => one.includes("'llm-wallet'"))).toBe(true);
|
|
153
|
+
expect(commands.some((one) => one.includes(`'${BROWSER_SCREEN_SESSION}'`))).toBe(false);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
126
157
|
describe('browserRead · 命令这一层的问题', () => {
|
|
127
158
|
it('刚重启时第一次失败:等一下再读一次就成功(实测的那个坑)', async () => {
|
|
128
159
|
const { shell, commands } = fakeShell([
|
package/src/host/browser-find.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { DshShell } from '../api/dsh.js';
|
|
2
2
|
import { parseJsonLoose } from '../lib/text.js';
|
|
3
|
-
import {
|
|
3
|
+
import { BROWSER_READ_SESSION } from './browser-read.js';
|
|
4
4
|
import { backgroundLine } from './opencli-window.js';
|
|
5
5
|
import { runShell } from './shell.js';
|
|
6
6
|
|
|
@@ -215,7 +215,7 @@ async function openOnce(
|
|
|
215
215
|
export async function browserFind(options: BrowserFindOptions): Promise<BrowserFindResult> {
|
|
216
216
|
const { shell, css, url, open = true } = options;
|
|
217
217
|
const command = options.command ?? 'opencli';
|
|
218
|
-
const session = options.session ??
|
|
218
|
+
const session = options.session ?? BROWSER_READ_SESSION;
|
|
219
219
|
const limit = options.limit ?? DEFAULT_LIMIT;
|
|
220
220
|
const textMax = options.textMax ?? DEFAULT_TEXT_MAX;
|
|
221
221
|
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
package/src/host/browser-read.ts
CHANGED
|
@@ -3,7 +3,8 @@ import { parseJsonLoose, tailLines } from '../lib/text.js';
|
|
|
3
3
|
import { backgroundLine } from './opencli-window.js';
|
|
4
4
|
import { runShell } from './shell.js';
|
|
5
5
|
|
|
6
|
-
export const
|
|
6
|
+
export const BROWSER_SCREEN_SESSION = 'workbench';
|
|
7
|
+
export const BROWSER_READ_SESSION = 'workbench-read';
|
|
7
8
|
|
|
8
9
|
const DEFAULT_TIMEOUT_MS = 25000;
|
|
9
10
|
const DEFAULT_RETRY_MS = 1200;
|
|
@@ -226,7 +227,7 @@ async function openOnce(
|
|
|
226
227
|
export async function browserRead(options: BrowserReadOptions): Promise<BrowserReadResult> {
|
|
227
228
|
const { shell, script, url, loginUrl, open = true } = options;
|
|
228
229
|
const command = options.command ?? 'opencli';
|
|
229
|
-
const session = options.session ??
|
|
230
|
+
const session = options.session ?? BROWSER_READ_SESSION;
|
|
230
231
|
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
231
232
|
const retryMs = options.retryMs ?? DEFAULT_RETRY_MS;
|
|
232
233
|
const settleMs = options.settleMs ?? DEFAULT_SETTLE_MS;
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { h } from '../lib/react.js';
|
|
2
|
-
export function StateDot({ state, size }) {
|
|
3
|
-
if (state === 'ongoing') {
|
|
4
|
-
return (h("svg", { className: "wb-loading", viewBox: "0 0 24 24", width: size, height: size, "aria-hidden": "true", style: { display: 'block' } },
|
|
5
|
-
h("g", { className: "wb-loadingMotion" },
|
|
6
|
-
h("circle", { className: "wb-loadingTrack", cx: 12, cy: 12, r: 9.5 }),
|
|
7
|
-
h("circle", { className: "wb-loadingArc", cx: 12, cy: 12, r: 9.5 }))));
|
|
8
|
-
}
|
|
9
|
-
return (h("span", { className: "wb-stateDot", "data-state": state, style: { width: size, height: size }, "aria-hidden": "true" }));
|
|
10
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import type { DshShell } from '../../api/dsh.js';
|
|
2
|
-
import type { Opencli } from '../../host/opencli.js';
|
|
3
|
-
export interface ShellCall {
|
|
4
|
-
command: string;
|
|
5
|
-
workdir?: string;
|
|
6
|
-
timeoutMs?: number;
|
|
7
|
-
onExpiry?: string;
|
|
8
|
-
stdoutMaxBytes?: number;
|
|
9
|
-
}
|
|
10
|
-
export interface ShellReply {
|
|
11
|
-
stdout?: string;
|
|
12
|
-
stderr?: string;
|
|
13
|
-
exitCode?: number | null;
|
|
14
|
-
timedOut?: boolean;
|
|
15
|
-
aborted?: boolean;
|
|
16
|
-
failWith?: Error;
|
|
17
|
-
}
|
|
18
|
-
export declare function createFakeShell(replies?: ShellReply[] | ((call: ShellCall, index: number) => ShellReply)): {
|
|
19
|
-
shell: DshShell;
|
|
20
|
-
calls: ShellCall[];
|
|
21
|
-
peak: () => number;
|
|
22
|
-
commands: () => string[];
|
|
23
|
-
};
|
|
24
|
-
export interface OpencliCall {
|
|
25
|
-
args: readonly string[];
|
|
26
|
-
key?: string;
|
|
27
|
-
workdir?: string;
|
|
28
|
-
refresh?: boolean;
|
|
29
|
-
}
|
|
30
|
-
export declare function createFakeOpencli(replies?: Array<unknown | Error> | ((call: OpencliCall, index: number) => unknown | Error)): {
|
|
31
|
-
opencli: Opencli;
|
|
32
|
-
calls: OpencliCall[];
|
|
33
|
-
keys: () => (string | undefined)[];
|
|
34
|
-
};
|
|
35
|
-
export declare function createFakeLogger(): {
|
|
36
|
-
logger: {
|
|
37
|
-
info: () => void;
|
|
38
|
-
warn: (message: string) => void;
|
|
39
|
-
};
|
|
40
|
-
warnings: string[];
|
|
41
|
-
};
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import { setTimeout as delay } from 'node:timers/promises';
|
|
2
|
-
const tick = () => delay(0);
|
|
3
|
-
export function createFakeShell(replies = []) {
|
|
4
|
-
const calls = [];
|
|
5
|
-
let running = 0;
|
|
6
|
-
let peak = 0;
|
|
7
|
-
const replyAt = (index) => typeof replies === 'function' ? replies(calls[index], index) : (replies[index] ?? {});
|
|
8
|
-
const shell = {
|
|
9
|
-
resolve(request) {
|
|
10
|
-
calls.push(request);
|
|
11
|
-
return request;
|
|
12
|
-
},
|
|
13
|
-
async execute() {
|
|
14
|
-
const index = calls.length - 1;
|
|
15
|
-
const reply = replyAt(index);
|
|
16
|
-
if (reply.failWith !== undefined)
|
|
17
|
-
throw reply.failWith;
|
|
18
|
-
running += 1;
|
|
19
|
-
peak = Math.max(peak, running);
|
|
20
|
-
return {
|
|
21
|
-
async result() {
|
|
22
|
-
await tick();
|
|
23
|
-
running -= 1;
|
|
24
|
-
return {
|
|
25
|
-
exitCode: reply.exitCode ?? 0,
|
|
26
|
-
timedOut: reply.timedOut ?? false,
|
|
27
|
-
aborted: reply.aborted ?? false,
|
|
28
|
-
stdout: { text: reply.stdout ?? '', truncated: false },
|
|
29
|
-
stderr: { text: reply.stderr ?? '', truncated: false },
|
|
30
|
-
};
|
|
31
|
-
},
|
|
32
|
-
};
|
|
33
|
-
},
|
|
34
|
-
};
|
|
35
|
-
return {
|
|
36
|
-
shell,
|
|
37
|
-
calls,
|
|
38
|
-
peak: () => peak,
|
|
39
|
-
commands: () => calls.map((call) => call.command),
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
export function createFakeOpencli(replies = []) {
|
|
43
|
-
const calls = [];
|
|
44
|
-
const opencli = {
|
|
45
|
-
async json(args, options = {}) {
|
|
46
|
-
const call = {
|
|
47
|
-
args,
|
|
48
|
-
...(options.key === undefined ? {} : { key: options.key }),
|
|
49
|
-
...(options.workdir === undefined ? {} : { workdir: options.workdir }),
|
|
50
|
-
};
|
|
51
|
-
calls.push(call);
|
|
52
|
-
const index = calls.length - 1;
|
|
53
|
-
const reply = typeof replies === 'function' ? replies(call, index) : (replies[index] ?? []);
|
|
54
|
-
if (reply instanceof Error)
|
|
55
|
-
throw reply;
|
|
56
|
-
return { value: reply, at: Date.now() };
|
|
57
|
-
},
|
|
58
|
-
};
|
|
59
|
-
return {
|
|
60
|
-
opencli,
|
|
61
|
-
calls,
|
|
62
|
-
keys: () => calls.map((call) => call.key),
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
export function createFakeLogger() {
|
|
66
|
-
const warnings = [];
|
|
67
|
-
return {
|
|
68
|
-
logger: {
|
|
69
|
-
info: () => { },
|
|
70
|
-
warn: (message) => {
|
|
71
|
-
warnings.push(message);
|
|
72
|
-
},
|
|
73
|
-
},
|
|
74
|
-
warnings,
|
|
75
|
-
};
|
|
76
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { createFakeShell } from './fakes';
|
|
2
|
-
export declare const GOOD_BRIDGE_REPORT: string;
|
|
3
|
-
export declare const ALL_OK: {
|
|
4
|
-
stdout: string;
|
|
5
|
-
}[];
|
|
6
|
-
export declare const STALE_DAEMON_BRIDGE: string;
|
|
7
|
-
export declare const BROKEN_BRIDGE: string;
|
|
8
|
-
export declare function build(replies: Parameters<typeof createFakeShell>[0], install?: () => Promise<{
|
|
9
|
-
ok: boolean;
|
|
10
|
-
detail: string;
|
|
11
|
-
}>, warmReads?: () => readonly (readonly string[])[]): {
|
|
12
|
-
fake: {
|
|
13
|
-
shell: import("../..").DshShell;
|
|
14
|
-
calls: import("./fakes").ShellCall[];
|
|
15
|
-
peak: () => number;
|
|
16
|
-
commands: () => string[];
|
|
17
|
-
};
|
|
18
|
-
preflight: import("../preflight").Preflight;
|
|
19
|
-
};
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { createPreflight } from '../preflight';
|
|
2
|
-
import { createFakeShell } from './fakes';
|
|
3
|
-
export const GOOD_BRIDGE_REPORT = [
|
|
4
|
-
'opencli v1.8.7 doctor (node v24.15.0)',
|
|
5
|
-
'',
|
|
6
|
-
'[OK] Daemon: running on port 19825 (v1.8.7)',
|
|
7
|
-
'[OK] Extension: connected (v1.0.24)',
|
|
8
|
-
'[OK] Connectivity: connected in 0.0s',
|
|
9
|
-
'',
|
|
10
|
-
'Everything looks good!',
|
|
11
|
-
].join('\n');
|
|
12
|
-
export const ALL_OK = [{ stdout: '1.8.8\n' }, { stdout: GOOD_BRIDGE_REPORT }];
|
|
13
|
-
export const STALE_DAEMON_BRIDGE = [
|
|
14
|
-
'opencli v1.8.7 doctor (node v24.15.0)',
|
|
15
|
-
'',
|
|
16
|
-
'[STALE] Daemon: running on port 19825 (v1.8.8 != CLI v1.8.7)',
|
|
17
|
-
'[OK] Extension: connected (v1.0.24)',
|
|
18
|
-
'[OK] Connectivity: connected in 0.0s',
|
|
19
|
-
'',
|
|
20
|
-
'Issues:',
|
|
21
|
-
' • Stale daemon detected: daemon v1.8.8 != CLI v1.8.7.',
|
|
22
|
-
' Run: opencli daemon restart',
|
|
23
|
-
].join('\n');
|
|
24
|
-
export const BROKEN_BRIDGE = [
|
|
25
|
-
'opencli v1.8.7 doctor (node v24.15.0)',
|
|
26
|
-
'',
|
|
27
|
-
'[MISSING] Daemon: not running',
|
|
28
|
-
'[MISSING] Extension: not connected',
|
|
29
|
-
'[FAIL] Connectivity: failed (daemon not reachable)',
|
|
30
|
-
'',
|
|
31
|
-
'Issues:',
|
|
32
|
-
" • Daemon is not running. It should start automatically when you run an opencli browser command.",
|
|
33
|
-
' • Daemon is running but the Chrome/Chromium extension is not connected.',
|
|
34
|
-
'If the extension is not installed:',
|
|
35
|
-
' 1. Download from https://github.com/jackwener/opencli/releases',
|
|
36
|
-
' 2. Open chrome://extensions/ → Enable Developer Mode',
|
|
37
|
-
' 3. Click "Load unpacked" → select the extension folder',
|
|
38
|
-
].join('\n');
|
|
39
|
-
export function build(replies, install, warmReads) {
|
|
40
|
-
const fake = createFakeShell(replies);
|
|
41
|
-
const preflight = createPreflight({
|
|
42
|
-
shell: fake.shell,
|
|
43
|
-
...(install === undefined ? {} : { install }),
|
|
44
|
-
...(warmReads === undefined ? {} : { warmReads }),
|
|
45
|
-
});
|
|
46
|
-
return { fake, preflight };
|
|
47
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { type ContextRef } from '@dsh-workbench/shared';
|
|
2
|
-
import type { ContextRenderer } from './unit-face';
|
|
3
|
-
export declare const CONTEXT_SOURCE_KIND = "workbench";
|
|
4
|
-
export interface ScannableMessage {
|
|
5
|
-
readonly source?: {
|
|
6
|
-
readonly kind?: string;
|
|
7
|
-
};
|
|
8
|
-
readonly content?: readonly {
|
|
9
|
-
readonly type?: string;
|
|
10
|
-
readonly text?: string;
|
|
11
|
-
}[];
|
|
12
|
-
}
|
|
13
|
-
export type { ContextRenderer };
|
|
14
|
-
export declare function collectContextRefs(messages: readonly ScannableMessage[]): ContextRef[];
|
|
15
|
-
export declare function buildContextMessage(text: string, summary: string): unknown;
|
|
16
|
-
export interface ContextInjector {
|
|
17
|
-
inject(messages: readonly ScannableMessage[], agent: unknown): Promise<unknown | undefined>;
|
|
18
|
-
}
|
|
19
|
-
export declare function createContextInjector(options: {
|
|
20
|
-
renderers: ReadonlyMap<string, ContextRenderer>;
|
|
21
|
-
logger?: {
|
|
22
|
-
warn?(message: string): void;
|
|
23
|
-
} | undefined;
|
|
24
|
-
}): ContextInjector;
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import { encodeRef, parseMarkers } from '@dsh-workbench/shared';
|
|
3
|
-
export const CONTEXT_SOURCE_KIND = 'workbench';
|
|
4
|
-
export function collectContextRefs(messages) {
|
|
5
|
-
const seen = new Set();
|
|
6
|
-
const refs = [];
|
|
7
|
-
for (const message of messages) {
|
|
8
|
-
if (message.source?.kind !== 'user')
|
|
9
|
-
continue;
|
|
10
|
-
for (const block of message.content ?? []) {
|
|
11
|
-
if (block.type !== 'text' || typeof block.text !== 'string')
|
|
12
|
-
continue;
|
|
13
|
-
for (const ref of parseMarkers(block.text)) {
|
|
14
|
-
const key = encodeRef(ref);
|
|
15
|
-
if (seen.has(key))
|
|
16
|
-
continue;
|
|
17
|
-
seen.add(key);
|
|
18
|
-
refs.push(ref);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
return refs;
|
|
23
|
-
}
|
|
24
|
-
export function buildContextMessage(text, summary) {
|
|
25
|
-
return {
|
|
26
|
-
role: 'user',
|
|
27
|
-
id: randomUUID(),
|
|
28
|
-
content: [{ type: 'text', text }],
|
|
29
|
-
source: { kind: CONTEXT_SOURCE_KIND, form: 'notice', summary },
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
export function createContextInjector(options) {
|
|
33
|
-
const done = new WeakMap();
|
|
34
|
-
return {
|
|
35
|
-
async inject(messages, agent) {
|
|
36
|
-
const refs = collectContextRefs(messages);
|
|
37
|
-
if (refs.length === 0)
|
|
38
|
-
return undefined;
|
|
39
|
-
const key = typeof agent === 'object' && agent !== null ? agent : undefined;
|
|
40
|
-
const seen = key === undefined ? undefined : (done.get(key) ?? new Set());
|
|
41
|
-
if (key !== undefined && seen !== undefined)
|
|
42
|
-
done.set(key, seen);
|
|
43
|
-
const fresh = [];
|
|
44
|
-
for (const ref of refs) {
|
|
45
|
-
const encoded = encodeRef(ref);
|
|
46
|
-
if (seen?.has(encoded) === true)
|
|
47
|
-
continue;
|
|
48
|
-
seen?.add(encoded);
|
|
49
|
-
fresh.push(ref);
|
|
50
|
-
}
|
|
51
|
-
if (fresh.length === 0)
|
|
52
|
-
return undefined;
|
|
53
|
-
const texts = [];
|
|
54
|
-
for (const ref of fresh) {
|
|
55
|
-
const render = options.renderers.get(ref.kind);
|
|
56
|
-
if (render === undefined)
|
|
57
|
-
continue;
|
|
58
|
-
let text;
|
|
59
|
-
try {
|
|
60
|
-
text = await render(ref);
|
|
61
|
-
}
|
|
62
|
-
catch (error) {
|
|
63
|
-
options.logger?.warn?.(`[dsh-workbench] 渲染对话引用失败:${String(error)}`);
|
|
64
|
-
text = `工作台面板引用了一条内容(${encodeRef(ref)}),但当前渲染不出来(项目未选中或数据源不可用)。`;
|
|
65
|
-
}
|
|
66
|
-
if (text !== '')
|
|
67
|
-
texts.push(text);
|
|
68
|
-
}
|
|
69
|
-
if (texts.length === 0)
|
|
70
|
-
return undefined;
|
|
71
|
-
return buildContextMessage(texts.join('\n\n'), `工作台引用:${fresh.length} 条`);
|
|
72
|
-
},
|
|
73
|
-
};
|
|
74
|
-
}
|
package/dist/host/events.d.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { DshHostContext, DshRequest, DshResponse } from '@dsh-workbench/shared';
|
|
2
|
-
import type { FactLog } from './facts';
|
|
3
|
-
export interface FactStream {
|
|
4
|
-
handler(req: DshRequest, res: DshResponse): void;
|
|
5
|
-
closeAll(): void;
|
|
6
|
-
size(): number;
|
|
7
|
-
}
|
|
8
|
-
export declare function createFactStream(log: FactLog): FactStream;
|
|
9
|
-
export declare function registerFactStream(ctx: DshHostContext, log: FactLog): () => void;
|
package/dist/host/events.js
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import { EVENTS_PATH } from '@dsh-workbench/shared';
|
|
2
|
-
const HEARTBEAT_MS = 25_000;
|
|
3
|
-
function frame(payload) {
|
|
4
|
-
return `data: ${JSON.stringify(payload)}\n\n`;
|
|
5
|
-
}
|
|
6
|
-
export function createFactStream(log) {
|
|
7
|
-
const connections = new Set();
|
|
8
|
-
const cleanups = new Map();
|
|
9
|
-
const drop = (res) => {
|
|
10
|
-
cleanups.get(res)?.();
|
|
11
|
-
cleanups.delete(res);
|
|
12
|
-
connections.delete(res);
|
|
13
|
-
};
|
|
14
|
-
return {
|
|
15
|
-
size: () => connections.size,
|
|
16
|
-
closeAll() {
|
|
17
|
-
for (const res of [...connections]) {
|
|
18
|
-
drop(res);
|
|
19
|
-
res.end?.();
|
|
20
|
-
}
|
|
21
|
-
},
|
|
22
|
-
handler(req, res) {
|
|
23
|
-
if (req.method !== undefined && req.method !== 'GET') {
|
|
24
|
-
res.statusCode = 405;
|
|
25
|
-
res.end();
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
const url = req.url ?? '/';
|
|
29
|
-
const cut = url.indexOf('?');
|
|
30
|
-
const since = Number(new URLSearchParams(cut === -1 ? '' : url.slice(cut + 1)).get('since') ?? 0) || 0;
|
|
31
|
-
const opening = log.since(since);
|
|
32
|
-
if (typeof res.write !== 'function') {
|
|
33
|
-
res.statusCode = 200;
|
|
34
|
-
res.setHeader('content-type', 'application/json; charset=utf-8');
|
|
35
|
-
res.end(JSON.stringify({ ok: true, ...opening }));
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
const headers = {
|
|
39
|
-
'content-type': 'text/event-stream; charset=utf-8',
|
|
40
|
-
'cache-control': 'no-store, no-transform',
|
|
41
|
-
connection: 'keep-alive',
|
|
42
|
-
};
|
|
43
|
-
if (typeof res.writeHead === 'function')
|
|
44
|
-
res.writeHead(200, headers);
|
|
45
|
-
else {
|
|
46
|
-
res.statusCode = 200;
|
|
47
|
-
for (const [name, value] of Object.entries(headers))
|
|
48
|
-
res.setHeader(name, value);
|
|
49
|
-
}
|
|
50
|
-
res.write(': connected\n\n');
|
|
51
|
-
res.write(frame({ type: 'sync', ...opening }));
|
|
52
|
-
connections.add(res);
|
|
53
|
-
const timer = setInterval(() => res.write?.(': ping\n\n'), HEARTBEAT_MS);
|
|
54
|
-
const unsubscribe = log.subscribe((change) => res.write?.(frame({ type: 'bump', ...change })));
|
|
55
|
-
cleanups.set(res, () => {
|
|
56
|
-
clearInterval(timer);
|
|
57
|
-
unsubscribe();
|
|
58
|
-
});
|
|
59
|
-
res.on?.('close', () => drop(res));
|
|
60
|
-
},
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
export function registerFactStream(ctx, log) {
|
|
64
|
-
const stream = createFactStream(log);
|
|
65
|
-
const unregister = ctx.webServer.register({ kind: 'exact', path: EVENTS_PATH, handler: stream.handler });
|
|
66
|
-
return () => {
|
|
67
|
-
stream.closeAll();
|
|
68
|
-
unregister();
|
|
69
|
-
};
|
|
70
|
-
}
|