@dsh-workbench/shared 0.2.3 → 0.2.4
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/host/opencli.d.ts +1 -1
- package/dist/lib/text.js +29 -14
- package/dist/testing/host-fakes.d.ts +1 -0
- package/dist/testing/host-fakes.js +1 -0
- package/package.json +1 -1
- package/src/host/opencli.ts +1 -1
- package/src/lib/text.test.ts +22 -0
- package/src/lib/text.ts +25 -11
- package/src/testing/host-fakes.ts +2 -0
package/dist/host/opencli.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ export interface OpencliResult {
|
|
|
16
16
|
readonly value: unknown;
|
|
17
17
|
readonly at: number;
|
|
18
18
|
}
|
|
19
|
-
export type OpencliSession = 'persistent' | 'ephemeral';
|
|
19
|
+
export type OpencliSession = 'persistent' | 'ephemeral' | 'none';
|
|
20
20
|
export declare function opencliCommandLine(command: string, argv: readonly string[], session?: OpencliSession): string;
|
|
21
21
|
export interface Opencli {
|
|
22
22
|
json(args: readonly string[], options?: {
|
package/dist/lib/text.js
CHANGED
|
@@ -7,6 +7,23 @@ export function tailLines(text, lines = 6) {
|
|
|
7
7
|
.split('\n');
|
|
8
8
|
return all.slice(-lines).join('\n');
|
|
9
9
|
}
|
|
10
|
+
function candidateStarts(raw) {
|
|
11
|
+
const starts = [];
|
|
12
|
+
for (let index = 0; index < raw.length; index += 1) {
|
|
13
|
+
const char = raw[index];
|
|
14
|
+
if (char !== '[' && char !== '{')
|
|
15
|
+
continue;
|
|
16
|
+
if (index === 0 || raw[index - 1] === '\n')
|
|
17
|
+
starts.push(index);
|
|
18
|
+
}
|
|
19
|
+
const earliest = [raw.indexOf('['), raw.indexOf('{')].filter((index) => index >= 0);
|
|
20
|
+
if (earliest.length > 0) {
|
|
21
|
+
const head = Math.min(...earliest);
|
|
22
|
+
if (!starts.includes(head))
|
|
23
|
+
starts.push(head);
|
|
24
|
+
}
|
|
25
|
+
return starts;
|
|
26
|
+
}
|
|
10
27
|
export function parseJsonLoose(text) {
|
|
11
28
|
const raw = String(text ?? '').trim();
|
|
12
29
|
if (!raw)
|
|
@@ -15,21 +32,19 @@ export function parseJsonLoose(text) {
|
|
|
15
32
|
return JSON.parse(raw);
|
|
16
33
|
}
|
|
17
34
|
catch {
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
-
catch {
|
|
31
|
-
return undefined;
|
|
35
|
+
for (const from of candidateStarts(raw)) {
|
|
36
|
+
const close = raw[from] === '[' ? ']' : '}';
|
|
37
|
+
const to = raw.lastIndexOf(close);
|
|
38
|
+
if (to <= from)
|
|
39
|
+
continue;
|
|
40
|
+
try {
|
|
41
|
+
return JSON.parse(raw.slice(from, to + 1));
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
32
46
|
}
|
|
47
|
+
return undefined;
|
|
33
48
|
}
|
|
34
49
|
}
|
|
35
50
|
const MARKDOWN_IMAGE = /!\[[^\]]*\]\([^)]*\)/g;
|
|
@@ -25,6 +25,7 @@ export interface OpencliCall {
|
|
|
25
25
|
args: readonly string[];
|
|
26
26
|
key?: string;
|
|
27
27
|
workdir?: string;
|
|
28
|
+
session?: string;
|
|
28
29
|
refresh?: boolean;
|
|
29
30
|
}
|
|
30
31
|
export declare function createFakeOpencli(replies?: Array<unknown | Error> | ((call: OpencliCall, index: number) => unknown | Error)): {
|
|
@@ -47,6 +47,7 @@ export function createFakeOpencli(replies = []) {
|
|
|
47
47
|
args,
|
|
48
48
|
...(options.key === undefined ? {} : { key: options.key }),
|
|
49
49
|
...(options.workdir === undefined ? {} : { workdir: options.workdir }),
|
|
50
|
+
...(options.session === undefined ? {} : { session: options.session }),
|
|
50
51
|
};
|
|
51
52
|
calls.push(call);
|
|
52
53
|
const index = calls.length - 1;
|
package/package.json
CHANGED
package/src/host/opencli.ts
CHANGED
package/src/lib/text.test.ts
CHANGED
|
@@ -58,6 +58,28 @@ describe('parseJsonLoose', () => {
|
|
|
58
58
|
expect(parseJsonLoose('[1,2]\n完成')).toEqual([1, 2]);
|
|
59
59
|
});
|
|
60
60
|
|
|
61
|
+
it('★ 噪音行自己带方括号时也不认错起点(真机:pagoda 的调试命令先打两行带 [] 的话)', () => {
|
|
62
|
+
const stdout = [
|
|
63
|
+
'数据源 [dify_dataset] 调试成功',
|
|
64
|
+
'结构类型: 3, 字段: code, name',
|
|
65
|
+
'[',
|
|
66
|
+
' {',
|
|
67
|
+
' "code": "dify_dataset",',
|
|
68
|
+
' "success": "成功",',
|
|
69
|
+
' "detail": "structure=3, keys=[code, name], fields=[{code=知识库ID}]"',
|
|
70
|
+
' }',
|
|
71
|
+
']',
|
|
72
|
+
].join('\n');
|
|
73
|
+
|
|
74
|
+
expect(parseJsonLoose(stdout)).toEqual([
|
|
75
|
+
{
|
|
76
|
+
code: 'dify_dataset',
|
|
77
|
+
success: '成功',
|
|
78
|
+
detail: 'structure=3, keys=[code, name], fields=[{code=知识库ID}]',
|
|
79
|
+
},
|
|
80
|
+
]);
|
|
81
|
+
});
|
|
82
|
+
|
|
61
83
|
it('解析不出来给 undefined,让调用方报 coding-bad-output', () => {
|
|
62
84
|
expect(parseJsonLoose('')).toBeUndefined();
|
|
63
85
|
expect(parseJsonLoose('not json at all')).toBeUndefined();
|
package/src/lib/text.ts
CHANGED
|
@@ -9,24 +9,38 @@ export function tailLines(text: string | null | undefined, lines = 6): string {
|
|
|
9
9
|
return all.slice(-lines).join('\n');
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
function candidateStarts(raw: string): number[] {
|
|
13
|
+
const starts: number[] = [];
|
|
14
|
+
for (let index = 0; index < raw.length; index += 1) {
|
|
15
|
+
const char = raw[index];
|
|
16
|
+
if (char !== '[' && char !== '{') continue;
|
|
17
|
+
if (index === 0 || raw[index - 1] === '\n') starts.push(index);
|
|
18
|
+
}
|
|
19
|
+
const earliest = [raw.indexOf('['), raw.indexOf('{')].filter((index) => index >= 0);
|
|
20
|
+
if (earliest.length > 0) {
|
|
21
|
+
const head = Math.min(...earliest);
|
|
22
|
+
if (!starts.includes(head)) starts.push(head);
|
|
23
|
+
}
|
|
24
|
+
return starts;
|
|
25
|
+
}
|
|
26
|
+
|
|
12
27
|
export function parseJsonLoose(text: string | null | undefined): unknown {
|
|
13
28
|
const raw = String(text ?? '').trim();
|
|
14
29
|
if (!raw) return undefined;
|
|
15
30
|
try {
|
|
16
31
|
return JSON.parse(raw) as unknown;
|
|
17
32
|
} catch {
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
} catch {
|
|
28
|
-
return undefined;
|
|
33
|
+
for (const from of candidateStarts(raw)) {
|
|
34
|
+
const close = raw[from] === '[' ? ']' : '}';
|
|
35
|
+
const to = raw.lastIndexOf(close);
|
|
36
|
+
if (to <= from) continue;
|
|
37
|
+
try {
|
|
38
|
+
return JSON.parse(raw.slice(from, to + 1)) as unknown;
|
|
39
|
+
} catch {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
29
42
|
}
|
|
43
|
+
return undefined;
|
|
30
44
|
}
|
|
31
45
|
}
|
|
32
46
|
|
|
@@ -70,6 +70,7 @@ export interface OpencliCall {
|
|
|
70
70
|
args: readonly string[];
|
|
71
71
|
key?: string;
|
|
72
72
|
workdir?: string;
|
|
73
|
+
session?: string;
|
|
73
74
|
refresh?: boolean;
|
|
74
75
|
}
|
|
75
76
|
|
|
@@ -84,6 +85,7 @@ export function createFakeOpencli(
|
|
|
84
85
|
args,
|
|
85
86
|
...(options.key === undefined ? {} : { key: options.key }),
|
|
86
87
|
...(options.workdir === undefined ? {} : { workdir: options.workdir }),
|
|
88
|
+
...(options.session === undefined ? {} : { session: options.session }),
|
|
87
89
|
};
|
|
88
90
|
calls.push(call);
|
|
89
91
|
const index = calls.length - 1;
|