@superdoc-dev/sdk 1.0.0-alpha.1 → 1.0.0-alpha.3
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/package.json +9 -7
- package/skills/editing-docx.md +58 -14
- package/src/__tests__/skills.test.ts +166 -0
- package/src/__tests__/tools.test.ts +96 -0
- package/src/generated/client.ts +2958 -172
- package/src/generated/contract.ts +13345 -860
- package/src/index.ts +55 -1
- package/src/runtime/__tests__/process.test.ts +38 -0
- package/src/runtime/__tests__/transport-common.test.ts +174 -0
- package/src/runtime/embedded-cli.ts +10 -0
- package/src/runtime/errors.ts +17 -0
- package/src/runtime/host.ts +30 -12
- package/src/runtime/process.ts +11 -35
- package/src/runtime/transport-common.ts +47 -12
- package/src/skills.ts +127 -8
- package/src/tools.ts +701 -0
- package/skills/.gitkeep +0 -0
- package/src/runtime/spawn.ts +0 -190
package/src/runtime/spawn.ts
DELETED
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
import { spawn } from 'node:child_process';
|
|
2
|
-
import { CONTRACT } from '../generated/contract';
|
|
3
|
-
import { SuperDocCliError } from './errors';
|
|
4
|
-
import { buildOperationArgv, resolveInvocation, type InvokeOptions, type OperationSpec } from './transport-common';
|
|
5
|
-
|
|
6
|
-
type CliEnvelopeSuccess = {
|
|
7
|
-
ok: true;
|
|
8
|
-
command: string;
|
|
9
|
-
data: Record<string, unknown>;
|
|
10
|
-
meta?: {
|
|
11
|
-
version?: string;
|
|
12
|
-
};
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
type CliEnvelopeError = {
|
|
16
|
-
ok: false;
|
|
17
|
-
error: {
|
|
18
|
-
code: string;
|
|
19
|
-
message: string;
|
|
20
|
-
details?: unknown;
|
|
21
|
-
};
|
|
22
|
-
meta?: {
|
|
23
|
-
version?: string;
|
|
24
|
-
};
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
type CliEnvelope = CliEnvelopeSuccess | CliEnvelopeError;
|
|
28
|
-
|
|
29
|
-
function parseEnvelope(stdout: string, stderr: string): CliEnvelope {
|
|
30
|
-
const output = stdout || stderr;
|
|
31
|
-
if (!output.trim()) {
|
|
32
|
-
throw new SuperDocCliError('CLI returned no JSON envelope.', {
|
|
33
|
-
code: 'COMMAND_FAILED',
|
|
34
|
-
details: { stdout, stderr },
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const attempts: string[] = [output.trim()];
|
|
39
|
-
const lines = output.split(/\r?\n/);
|
|
40
|
-
for (let index = 0; index < lines.length; index += 1) {
|
|
41
|
-
if (!lines[index]?.trim().startsWith('{')) continue;
|
|
42
|
-
attempts.push(lines.slice(index).join('\n').trim());
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
for (const candidate of attempts) {
|
|
46
|
-
if (!candidate) continue;
|
|
47
|
-
try {
|
|
48
|
-
const parsed = JSON.parse(candidate) as CliEnvelope;
|
|
49
|
-
if (typeof parsed === 'object' && parsed != null && 'ok' in parsed) {
|
|
50
|
-
return parsed;
|
|
51
|
-
}
|
|
52
|
-
} catch {
|
|
53
|
-
// try next candidate
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
try {
|
|
58
|
-
return JSON.parse(output.trim()) as CliEnvelope;
|
|
59
|
-
} catch (error) {
|
|
60
|
-
throw new SuperDocCliError('CLI returned invalid JSON envelope.', {
|
|
61
|
-
code: 'JSON_PARSE_ERROR',
|
|
62
|
-
details: {
|
|
63
|
-
stdout,
|
|
64
|
-
stderr,
|
|
65
|
-
message: error instanceof Error ? error.message : String(error),
|
|
66
|
-
},
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function parseSemver(version: string): [number, number, number] | null {
|
|
72
|
-
const core = version.split('-', 1)[0];
|
|
73
|
-
const [majorText, minorText, patchText] = core.split('.');
|
|
74
|
-
if (!majorText || !minorText || !patchText) return null;
|
|
75
|
-
|
|
76
|
-
const major = Number(majorText);
|
|
77
|
-
const minor = Number(minorText);
|
|
78
|
-
const patch = Number(patchText);
|
|
79
|
-
if (!Number.isInteger(major) || !Number.isInteger(minor) || !Number.isInteger(patch)) {
|
|
80
|
-
return null;
|
|
81
|
-
}
|
|
82
|
-
return [major, minor, patch];
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function isVersionLessThan(actual: string, minimum: string): boolean {
|
|
86
|
-
const actualParsed = parseSemver(actual);
|
|
87
|
-
const minimumParsed = parseSemver(minimum);
|
|
88
|
-
if (!actualParsed || !minimumParsed) return false;
|
|
89
|
-
|
|
90
|
-
if (actualParsed[0] !== minimumParsed[0]) return actualParsed[0] < minimumParsed[0];
|
|
91
|
-
if (actualParsed[1] !== minimumParsed[1]) return actualParsed[1] < minimumParsed[1];
|
|
92
|
-
return actualParsed[2] < minimumParsed[2];
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function assertCompatibleCliVersion(envelope: CliEnvelope): void {
|
|
96
|
-
const actualVersion = envelope.meta?.version;
|
|
97
|
-
const minimumVersion = CONTRACT.cli.minVersion;
|
|
98
|
-
if (!actualVersion || !minimumVersion) return;
|
|
99
|
-
if (actualVersion === '0.0.0') return;
|
|
100
|
-
|
|
101
|
-
if (isVersionLessThan(actualVersion, minimumVersion)) {
|
|
102
|
-
throw new SuperDocCliError(
|
|
103
|
-
`CLI version ${actualVersion} is older than minimum required ${minimumVersion}.`,
|
|
104
|
-
{
|
|
105
|
-
code: 'CLI_VERSION_UNSUPPORTED',
|
|
106
|
-
details: {
|
|
107
|
-
cliVersion: actualVersion,
|
|
108
|
-
minVersion: minimumVersion,
|
|
109
|
-
},
|
|
110
|
-
},
|
|
111
|
-
);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
export class SpawnTransport {
|
|
116
|
-
private readonly cliBin: string;
|
|
117
|
-
private readonly env?: Record<string, string | undefined>;
|
|
118
|
-
|
|
119
|
-
constructor(options: { cliBin: string; env?: Record<string, string | undefined> }) {
|
|
120
|
-
this.cliBin = options.cliBin;
|
|
121
|
-
this.env = options.env;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
async invoke<TData extends Record<string, unknown>>(
|
|
125
|
-
operation: OperationSpec,
|
|
126
|
-
params: Record<string, unknown> = {},
|
|
127
|
-
options: InvokeOptions = {},
|
|
128
|
-
): Promise<TData> {
|
|
129
|
-
const { command, prefixArgs } = resolveInvocation(this.cliBin);
|
|
130
|
-
const commandArgs = buildOperationArgv(operation, params, options, undefined, true);
|
|
131
|
-
const args: string[] = [...prefixArgs, ...commandArgs];
|
|
132
|
-
|
|
133
|
-
const spawned = spawn(command, args, {
|
|
134
|
-
env: {
|
|
135
|
-
...process.env,
|
|
136
|
-
...(this.env ?? {}),
|
|
137
|
-
},
|
|
138
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
let stdout = '';
|
|
142
|
-
let stderr = '';
|
|
143
|
-
|
|
144
|
-
const stdoutPromise = new Promise<void>((resolve) => {
|
|
145
|
-
spawned.stdout.on('data', (chunk) => {
|
|
146
|
-
stdout += String(chunk);
|
|
147
|
-
});
|
|
148
|
-
spawned.stdout.on('end', () => resolve());
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
const stderrPromise = new Promise<void>((resolve) => {
|
|
152
|
-
spawned.stderr.on('data', (chunk) => {
|
|
153
|
-
stderr += String(chunk);
|
|
154
|
-
});
|
|
155
|
-
spawned.stderr.on('end', () => resolve());
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
if (options.stdinBytes) {
|
|
159
|
-
spawned.stdin.write(options.stdinBytes);
|
|
160
|
-
}
|
|
161
|
-
spawned.stdin.end();
|
|
162
|
-
|
|
163
|
-
const exitCode = await new Promise<number>((resolve, reject) => {
|
|
164
|
-
spawned.on('error', reject);
|
|
165
|
-
spawned.on('close', (code) => resolve(code ?? 1));
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
await Promise.all([stdoutPromise, stderrPromise]);
|
|
169
|
-
|
|
170
|
-
const envelope = parseEnvelope(stdout, stderr);
|
|
171
|
-
assertCompatibleCliVersion(envelope);
|
|
172
|
-
if (envelope.ok) {
|
|
173
|
-
return envelope.data as TData;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
throw new SuperDocCliError(envelope.error.message, {
|
|
177
|
-
code: envelope.error.code,
|
|
178
|
-
details: envelope.error.details,
|
|
179
|
-
exitCode,
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
async connect(): Promise<void> {
|
|
184
|
-
// no-op in one-shot spawn mode
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
async dispose(): Promise<void> {
|
|
188
|
-
// no-op in one-shot spawn mode
|
|
189
|
-
}
|
|
190
|
-
}
|