@superdoc-dev/sdk 1.0.0-alpha.2 → 1.0.0-alpha.22
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/README.md +126 -0
- package/dist/generated/client.cjs +236 -0
- package/dist/generated/client.d.ts +6318 -0
- package/dist/generated/client.d.ts.map +1 -0
- package/dist/generated/client.js +231 -0
- package/dist/generated/contract.cjs +62387 -0
- package/dist/generated/contract.d.ts +45999 -0
- package/dist/generated/contract.d.ts.map +1 -0
- package/dist/generated/contract.js +62402 -0
- package/dist/helpers/format.d.ts +79 -0
- package/dist/helpers/format.d.ts.map +1 -0
- package/dist/helpers/format.js +121 -0
- package/dist/index.cjs +45 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/runtime/embedded-cli.cjs +100 -0
- package/dist/runtime/embedded-cli.d.ts +5 -0
- package/dist/runtime/embedded-cli.d.ts.map +1 -0
- package/dist/runtime/embedded-cli.js +94 -0
- package/dist/runtime/errors.cjs +22 -0
- package/dist/runtime/errors.d.ts +17 -0
- package/dist/runtime/errors.d.ts.map +1 -0
- package/dist/runtime/errors.js +18 -0
- package/dist/runtime/host.cjs +352 -0
- package/dist/runtime/host.d.ts +37 -0
- package/dist/runtime/host.d.ts.map +1 -0
- package/dist/runtime/host.js +347 -0
- package/dist/runtime/process.cjs +32 -0
- package/dist/runtime/process.d.ts +16 -0
- package/dist/runtime/process.d.ts.map +1 -0
- package/dist/runtime/process.js +27 -0
- package/dist/runtime/transport-common.cjs +77 -0
- package/dist/runtime/transport-common.d.ts +49 -0
- package/dist/runtime/transport-common.d.ts.map +1 -0
- package/dist/runtime/transport-common.js +72 -0
- package/dist/skills.cjs +148 -0
- package/dist/skills.d.ts +25 -0
- package/dist/skills.d.ts.map +1 -0
- package/dist/skills.js +140 -0
- package/dist/tools.cjs +371 -0
- package/dist/tools.d.ts +113 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +360 -0
- package/package.json +31 -18
- package/skills/editing-docx.md +24 -146
- package/tools/catalog.json +66563 -0
- package/tools/tool-name-map.json +382 -0
- package/tools/tools-policy.json +100 -0
- package/tools/tools.anthropic.json +27979 -0
- package/tools/tools.generic.json +64075 -0
- package/tools/tools.openai.json +29119 -0
- package/tools/tools.vercel.json +29119 -0
- package/LICENSE +0 -661
- package/skills/.gitkeep +0 -0
- package/src/__tests__/skills.test.ts +0 -93
- package/src/generated/DO_NOT_EDIT +0 -2
- package/src/generated/client.ts +0 -3151
- package/src/generated/contract.ts +0 -13396
- package/src/index.ts +0 -72
- package/src/runtime/__tests__/transport-common.test.ts +0 -151
- package/src/runtime/embedded-cli.ts +0 -109
- package/src/runtime/errors.ts +0 -30
- package/src/runtime/host.ts +0 -465
- package/src/runtime/process.ts +0 -45
- package/src/runtime/transport-common.ts +0 -159
- package/src/skills.ts +0 -91
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { createInterface } from 'node:readline';
|
|
3
|
+
import { buildOperationArgv, resolveInvocation, } from './transport-common.js';
|
|
4
|
+
import { SuperDocCliError } from './errors.js';
|
|
5
|
+
const HOST_PROTOCOL_VERSION = '1.0';
|
|
6
|
+
const REQUIRED_FEATURES = ['cli.invoke', 'host.shutdown'];
|
|
7
|
+
const CHANGE_MODES = ['direct', 'tracked'];
|
|
8
|
+
const JSON_RPC_TIMEOUT_CODE = -32011;
|
|
9
|
+
/**
|
|
10
|
+
* Transport that communicates with a long-lived CLI host process over JSON-RPC stdio.
|
|
11
|
+
*/
|
|
12
|
+
export class HostTransport {
|
|
13
|
+
cliBin;
|
|
14
|
+
env;
|
|
15
|
+
startupTimeoutMs;
|
|
16
|
+
shutdownTimeoutMs;
|
|
17
|
+
requestTimeoutMs;
|
|
18
|
+
watchdogTimeoutMs;
|
|
19
|
+
maxQueueDepth;
|
|
20
|
+
defaultChangeMode;
|
|
21
|
+
user;
|
|
22
|
+
child = null;
|
|
23
|
+
stdoutReader = null;
|
|
24
|
+
pending = new Map();
|
|
25
|
+
nextRequestId = 1;
|
|
26
|
+
connecting = null;
|
|
27
|
+
stopping = false;
|
|
28
|
+
constructor(options) {
|
|
29
|
+
this.cliBin = options.cliBin;
|
|
30
|
+
this.env = options.env;
|
|
31
|
+
this.startupTimeoutMs = options.startupTimeoutMs ?? 5_000;
|
|
32
|
+
this.shutdownTimeoutMs = options.shutdownTimeoutMs ?? 5_000;
|
|
33
|
+
this.requestTimeoutMs = options.requestTimeoutMs;
|
|
34
|
+
this.watchdogTimeoutMs = options.watchdogTimeoutMs ?? 30_000;
|
|
35
|
+
this.maxQueueDepth = options.maxQueueDepth ?? 100;
|
|
36
|
+
if (options.defaultChangeMode != null && !CHANGE_MODES.includes(options.defaultChangeMode)) {
|
|
37
|
+
throw new SuperDocCliError('defaultChangeMode must be "direct" or "tracked".', {
|
|
38
|
+
code: 'INVALID_ARGUMENT',
|
|
39
|
+
details: { defaultChangeMode: options.defaultChangeMode },
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
this.defaultChangeMode = options.defaultChangeMode;
|
|
43
|
+
this.user = options.user;
|
|
44
|
+
}
|
|
45
|
+
async connect() {
|
|
46
|
+
await this.ensureConnected();
|
|
47
|
+
}
|
|
48
|
+
async dispose() {
|
|
49
|
+
if (!this.child)
|
|
50
|
+
return;
|
|
51
|
+
this.stopping = true;
|
|
52
|
+
const child = this.child;
|
|
53
|
+
try {
|
|
54
|
+
await this.sendJsonRpcRequest('host.shutdown', {}, this.shutdownTimeoutMs);
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// ignore and force shutdown below
|
|
58
|
+
}
|
|
59
|
+
await new Promise((resolve) => {
|
|
60
|
+
const timer = setTimeout(() => {
|
|
61
|
+
child.kill('SIGKILL');
|
|
62
|
+
resolve();
|
|
63
|
+
}, this.shutdownTimeoutMs);
|
|
64
|
+
child.once('close', () => {
|
|
65
|
+
clearTimeout(timer);
|
|
66
|
+
resolve();
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
this.cleanupProcess(null);
|
|
70
|
+
this.stopping = false;
|
|
71
|
+
}
|
|
72
|
+
async invoke(operation, params = {}, options = {}) {
|
|
73
|
+
await this.ensureConnected();
|
|
74
|
+
const argv = buildOperationArgv(operation, params, options, this.requestTimeoutMs, this.defaultChangeMode, this.user);
|
|
75
|
+
const stdinBase64 = options.stdinBytes ? Buffer.from(options.stdinBytes).toString('base64') : '';
|
|
76
|
+
const watchdogTimeout = this.resolveWatchdogTimeout(options.timeoutMs);
|
|
77
|
+
const response = await this.sendJsonRpcRequest('cli.invoke', {
|
|
78
|
+
argv,
|
|
79
|
+
stdinBase64,
|
|
80
|
+
}, watchdogTimeout);
|
|
81
|
+
if (typeof response !== 'object' || response == null || Array.isArray(response)) {
|
|
82
|
+
throw new SuperDocCliError('Host returned invalid cli.invoke result.', {
|
|
83
|
+
code: 'HOST_PROTOCOL_ERROR',
|
|
84
|
+
details: { result: response },
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
const resultRecord = response;
|
|
88
|
+
return resultRecord.data;
|
|
89
|
+
}
|
|
90
|
+
async ensureConnected() {
|
|
91
|
+
if (this.child && !this.child.killed) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (this.connecting) {
|
|
95
|
+
await this.connecting;
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
this.connecting = this.startHostProcess();
|
|
99
|
+
try {
|
|
100
|
+
await this.connecting;
|
|
101
|
+
}
|
|
102
|
+
finally {
|
|
103
|
+
this.connecting = null;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
async startHostProcess() {
|
|
107
|
+
const { command, prefixArgs } = resolveInvocation(this.cliBin);
|
|
108
|
+
const args = [...prefixArgs, 'host', '--stdio'];
|
|
109
|
+
const child = spawn(command, args, {
|
|
110
|
+
env: {
|
|
111
|
+
...process.env,
|
|
112
|
+
...(this.env ?? {}),
|
|
113
|
+
},
|
|
114
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
115
|
+
});
|
|
116
|
+
this.child = child;
|
|
117
|
+
const stdoutReader = createInterface({
|
|
118
|
+
input: child.stdout,
|
|
119
|
+
crlfDelay: Number.POSITIVE_INFINITY,
|
|
120
|
+
});
|
|
121
|
+
this.stdoutReader = stdoutReader;
|
|
122
|
+
stdoutReader.on('line', (line) => {
|
|
123
|
+
this.onStdoutLine(line);
|
|
124
|
+
});
|
|
125
|
+
child.stderr.on('data', () => {
|
|
126
|
+
// stderr intentionally ignored in host mode
|
|
127
|
+
});
|
|
128
|
+
child.on('error', (error) => {
|
|
129
|
+
this.handleDisconnect(new SuperDocCliError('Host process failed.', {
|
|
130
|
+
code: 'HOST_DISCONNECTED',
|
|
131
|
+
details: {
|
|
132
|
+
message: error instanceof Error ? error.message : String(error),
|
|
133
|
+
},
|
|
134
|
+
}));
|
|
135
|
+
});
|
|
136
|
+
child.on('close', (code, signal) => {
|
|
137
|
+
if (this.stopping) {
|
|
138
|
+
this.cleanupProcess(null);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
this.handleDisconnect(new SuperDocCliError('Host process disconnected.', {
|
|
142
|
+
code: 'HOST_DISCONNECTED',
|
|
143
|
+
details: { exitCode: code, signal },
|
|
144
|
+
}));
|
|
145
|
+
});
|
|
146
|
+
try {
|
|
147
|
+
const capabilities = await this.sendJsonRpcRequest('host.capabilities', {}, this.startupTimeoutMs);
|
|
148
|
+
this.assertCapabilities(capabilities);
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
const normalized = error instanceof SuperDocCliError
|
|
152
|
+
? error
|
|
153
|
+
: new SuperDocCliError('Host handshake failed.', {
|
|
154
|
+
code: 'HOST_HANDSHAKE_FAILED',
|
|
155
|
+
details: {
|
|
156
|
+
message: error instanceof Error ? error.message : String(error),
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
this.handleDisconnect(normalized);
|
|
160
|
+
throw normalized;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
assertCapabilities(response) {
|
|
164
|
+
if (typeof response !== 'object' || response == null || Array.isArray(response)) {
|
|
165
|
+
throw new SuperDocCliError('Host capabilities response is invalid.', {
|
|
166
|
+
code: 'HOST_HANDSHAKE_FAILED',
|
|
167
|
+
details: { response },
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
const record = response;
|
|
171
|
+
const protocolVersion = record.protocolVersion;
|
|
172
|
+
const features = record.features;
|
|
173
|
+
if (protocolVersion !== HOST_PROTOCOL_VERSION) {
|
|
174
|
+
throw new SuperDocCliError('Host protocol version is unsupported.', {
|
|
175
|
+
code: 'HOST_HANDSHAKE_FAILED',
|
|
176
|
+
details: {
|
|
177
|
+
expected: HOST_PROTOCOL_VERSION,
|
|
178
|
+
actual: protocolVersion,
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
if (!Array.isArray(features) || features.some((f) => typeof f !== 'string')) {
|
|
183
|
+
throw new SuperDocCliError('Host capabilities.features must be a string array.', {
|
|
184
|
+
code: 'HOST_HANDSHAKE_FAILED',
|
|
185
|
+
details: { features },
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
for (const requiredFeature of REQUIRED_FEATURES) {
|
|
189
|
+
if (!features.includes(requiredFeature)) {
|
|
190
|
+
throw new SuperDocCliError(`Host does not support required feature: ${requiredFeature}`, {
|
|
191
|
+
code: 'HOST_HANDSHAKE_FAILED',
|
|
192
|
+
details: { features },
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
resolveWatchdogTimeout(timeoutMsOverride) {
|
|
198
|
+
if (timeoutMsOverride != null) {
|
|
199
|
+
return Math.max(this.watchdogTimeoutMs, timeoutMsOverride + 1_000);
|
|
200
|
+
}
|
|
201
|
+
if (this.requestTimeoutMs != null) {
|
|
202
|
+
return Math.max(this.watchdogTimeoutMs, this.requestTimeoutMs + 1_000);
|
|
203
|
+
}
|
|
204
|
+
return this.watchdogTimeoutMs;
|
|
205
|
+
}
|
|
206
|
+
async sendJsonRpcRequest(method, params, watchdogTimeoutMs) {
|
|
207
|
+
const child = this.child;
|
|
208
|
+
if (!child || !child.stdin.writable) {
|
|
209
|
+
throw new SuperDocCliError('Host process is not available.', {
|
|
210
|
+
code: 'HOST_DISCONNECTED',
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
if (this.pending.size >= this.maxQueueDepth) {
|
|
214
|
+
throw new SuperDocCliError('Host request queue is full.', {
|
|
215
|
+
code: 'HOST_QUEUE_FULL',
|
|
216
|
+
details: { maxQueueDepth: this.maxQueueDepth },
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
const id = this.nextRequestId;
|
|
220
|
+
this.nextRequestId += 1;
|
|
221
|
+
const payload = JSON.stringify({
|
|
222
|
+
jsonrpc: '2.0',
|
|
223
|
+
id,
|
|
224
|
+
method,
|
|
225
|
+
params,
|
|
226
|
+
});
|
|
227
|
+
const promise = new Promise((resolve, reject) => {
|
|
228
|
+
const timer = setTimeout(() => {
|
|
229
|
+
this.pending.delete(id);
|
|
230
|
+
reject(new SuperDocCliError(`Host watchdog timed out waiting for ${method}.`, {
|
|
231
|
+
code: 'HOST_TIMEOUT',
|
|
232
|
+
details: { method, timeoutMs: watchdogTimeoutMs },
|
|
233
|
+
}));
|
|
234
|
+
this.handleDisconnect(new SuperDocCliError('Host watchdog timeout; host process will be restarted on next request.', {
|
|
235
|
+
code: 'HOST_DISCONNECTED',
|
|
236
|
+
details: { method, timeoutMs: watchdogTimeoutMs },
|
|
237
|
+
}));
|
|
238
|
+
}, watchdogTimeoutMs);
|
|
239
|
+
this.pending.set(id, { resolve, reject, timer });
|
|
240
|
+
child.stdin.write(`${payload}\n`, (error) => {
|
|
241
|
+
if (!error)
|
|
242
|
+
return;
|
|
243
|
+
const pending = this.pending.get(id);
|
|
244
|
+
if (!pending)
|
|
245
|
+
return;
|
|
246
|
+
clearTimeout(pending.timer);
|
|
247
|
+
this.pending.delete(id);
|
|
248
|
+
reject(new SuperDocCliError('Failed to write request to host process.', {
|
|
249
|
+
code: 'HOST_DISCONNECTED',
|
|
250
|
+
details: { method, message: error.message },
|
|
251
|
+
}));
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
return promise;
|
|
255
|
+
}
|
|
256
|
+
onStdoutLine(line) {
|
|
257
|
+
let parsed;
|
|
258
|
+
try {
|
|
259
|
+
parsed = JSON.parse(line);
|
|
260
|
+
}
|
|
261
|
+
catch {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
if (typeof parsed !== 'object' || parsed == null || Array.isArray(parsed)) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
const record = parsed;
|
|
268
|
+
if (record.jsonrpc !== '2.0') {
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
// Notification (no id) — reserved for future eventing
|
|
272
|
+
if ('method' in record && !('id' in record)) {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
const idRaw = record.id;
|
|
276
|
+
if (typeof idRaw !== 'number') {
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
const pending = this.pending.get(idRaw);
|
|
280
|
+
if (!pending) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
clearTimeout(pending.timer);
|
|
284
|
+
this.pending.delete(idRaw);
|
|
285
|
+
if ('error' in record) {
|
|
286
|
+
pending.reject(this.mapJsonRpcError(record.error));
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
pending.resolve(record.result);
|
|
290
|
+
}
|
|
291
|
+
mapJsonRpcError(rawError) {
|
|
292
|
+
if (typeof rawError !== 'object' || rawError == null || Array.isArray(rawError)) {
|
|
293
|
+
return new SuperDocCliError('Host returned an unknown JSON-RPC error.', {
|
|
294
|
+
code: 'HOST_PROTOCOL_ERROR',
|
|
295
|
+
details: { error: rawError },
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
const error = rawError;
|
|
299
|
+
const data = error.data;
|
|
300
|
+
const cliCode = typeof data?.cliCode === 'string' ? data.cliCode : undefined;
|
|
301
|
+
const cliMessage = typeof data?.message === 'string' ? data.message : undefined;
|
|
302
|
+
const exitCode = typeof data?.exitCode === 'number' ? data.exitCode : undefined;
|
|
303
|
+
if (cliCode) {
|
|
304
|
+
return new SuperDocCliError(cliMessage ?? error.message ?? 'Command failed.', {
|
|
305
|
+
code: cliCode,
|
|
306
|
+
details: data?.details,
|
|
307
|
+
exitCode,
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
if (error.code === JSON_RPC_TIMEOUT_CODE) {
|
|
311
|
+
return new SuperDocCliError(error.message, {
|
|
312
|
+
code: 'TIMEOUT',
|
|
313
|
+
details: data,
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
return new SuperDocCliError(error.message, {
|
|
317
|
+
code: 'COMMAND_FAILED',
|
|
318
|
+
details: data,
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
handleDisconnect(error) {
|
|
322
|
+
this.cleanupProcess(error);
|
|
323
|
+
}
|
|
324
|
+
cleanupProcess(error) {
|
|
325
|
+
const child = this.child;
|
|
326
|
+
if (child) {
|
|
327
|
+
child.removeAllListeners();
|
|
328
|
+
child.kill('SIGKILL');
|
|
329
|
+
}
|
|
330
|
+
this.child = null;
|
|
331
|
+
if (this.stdoutReader) {
|
|
332
|
+
this.stdoutReader.removeAllListeners();
|
|
333
|
+
this.stdoutReader.close();
|
|
334
|
+
this.stdoutReader = null;
|
|
335
|
+
}
|
|
336
|
+
const pendingEntries = Array.from(this.pending.values());
|
|
337
|
+
this.pending.clear();
|
|
338
|
+
const rejection = error ??
|
|
339
|
+
new SuperDocCliError('Host process was disposed while request was in flight.', {
|
|
340
|
+
code: 'HOST_DISCONNECTED',
|
|
341
|
+
});
|
|
342
|
+
for (const pending of pendingEntries) {
|
|
343
|
+
clearTimeout(pending.timer);
|
|
344
|
+
pending.reject(rejection);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var host = require('./host.cjs');
|
|
4
|
+
var embeddedCli = require('./embedded-cli.cjs');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Internal runtime that delegates CLI invocations to a persistent host transport.
|
|
8
|
+
*
|
|
9
|
+
* Resolves the CLI binary and creates a {@link HostTransport} that communicates
|
|
10
|
+
* with a long-lived `superdoc host --stdio` process.
|
|
11
|
+
*/
|
|
12
|
+
class SuperDocRuntime {
|
|
13
|
+
transport;
|
|
14
|
+
constructor(options = {}) {
|
|
15
|
+
const cliBin = options.env?.SUPERDOC_CLI_BIN ?? process.env.SUPERDOC_CLI_BIN ?? embeddedCli.resolveEmbeddedCliBinary();
|
|
16
|
+
this.transport = new host.HostTransport({
|
|
17
|
+
cliBin,
|
|
18
|
+
...options,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
async connect() {
|
|
22
|
+
await this.transport.connect();
|
|
23
|
+
}
|
|
24
|
+
async dispose() {
|
|
25
|
+
await this.transport.dispose();
|
|
26
|
+
}
|
|
27
|
+
async invoke(operation, params = {}, options = {}) {
|
|
28
|
+
return this.transport.invoke(operation, params, options);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
exports.SuperDocRuntime = SuperDocRuntime;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { InvokeOptions, OperationParamSpec, OperationSpec, SuperDocClientOptions } from './transport-common.js';
|
|
2
|
+
/**
|
|
3
|
+
* Internal runtime that delegates CLI invocations to a persistent host transport.
|
|
4
|
+
*
|
|
5
|
+
* Resolves the CLI binary and creates a {@link HostTransport} that communicates
|
|
6
|
+
* with a long-lived `superdoc host --stdio` process.
|
|
7
|
+
*/
|
|
8
|
+
export declare class SuperDocRuntime {
|
|
9
|
+
private readonly transport;
|
|
10
|
+
constructor(options?: SuperDocClientOptions);
|
|
11
|
+
connect(): Promise<void>;
|
|
12
|
+
dispose(): Promise<void>;
|
|
13
|
+
invoke<TData = unknown>(operation: OperationSpec, params?: Record<string, unknown>, options?: InvokeOptions): Promise<TData>;
|
|
14
|
+
}
|
|
15
|
+
export type { InvokeOptions, OperationParamSpec, OperationSpec, SuperDocClientOptions };
|
|
16
|
+
//# sourceMappingURL=process.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process.d.ts","sourceRoot":"","sources":["../../src/runtime/process.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAErH;;;;;GAKG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAgB;gBAE9B,OAAO,GAAE,qBAA0B;IASzC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAIxB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAIxB,MAAM,CAAC,KAAK,GAAG,OAAO,EAC1B,SAAS,EAAE,aAAa,EACxB,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACpC,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,KAAK,CAAC;CAGlB;AAED,YAAY,EAAE,aAAa,EAAE,kBAAkB,EAAE,aAAa,EAAE,qBAAqB,EAAE,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { HostTransport } from './host.js';
|
|
2
|
+
import { resolveEmbeddedCliBinary } from './embedded-cli.js';
|
|
3
|
+
/**
|
|
4
|
+
* Internal runtime that delegates CLI invocations to a persistent host transport.
|
|
5
|
+
*
|
|
6
|
+
* Resolves the CLI binary and creates a {@link HostTransport} that communicates
|
|
7
|
+
* with a long-lived `superdoc host --stdio` process.
|
|
8
|
+
*/
|
|
9
|
+
export class SuperDocRuntime {
|
|
10
|
+
transport;
|
|
11
|
+
constructor(options = {}) {
|
|
12
|
+
const cliBin = options.env?.SUPERDOC_CLI_BIN ?? process.env.SUPERDOC_CLI_BIN ?? resolveEmbeddedCliBinary();
|
|
13
|
+
this.transport = new HostTransport({
|
|
14
|
+
cliBin,
|
|
15
|
+
...options,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
async connect() {
|
|
19
|
+
await this.transport.connect();
|
|
20
|
+
}
|
|
21
|
+
async dispose() {
|
|
22
|
+
await this.transport.dispose();
|
|
23
|
+
}
|
|
24
|
+
async invoke(operation, params = {}, options = {}) {
|
|
25
|
+
return this.transport.invoke(operation, params, options);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function hasExtension(filePath, extension) {
|
|
4
|
+
return filePath.toLowerCase().endsWith(extension);
|
|
5
|
+
}
|
|
6
|
+
function resolveInvocation(cliBin) {
|
|
7
|
+
if (hasExtension(cliBin, '.js')) {
|
|
8
|
+
return { command: 'node', prefixArgs: [cliBin] };
|
|
9
|
+
}
|
|
10
|
+
if (hasExtension(cliBin, '.ts')) {
|
|
11
|
+
return { command: 'bun', prefixArgs: [cliBin] };
|
|
12
|
+
}
|
|
13
|
+
return { command: cliBin, prefixArgs: [] };
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Build the CLI argument vector for an operation invocation.
|
|
17
|
+
*
|
|
18
|
+
* Key design choices vs old SDK:
|
|
19
|
+
* - changeMode injection BEFORE argv loop, not after. changeMode is already a
|
|
20
|
+
* param in operationSpec.params (envelope param for mutations). Appending after
|
|
21
|
+
* the loop would duplicate it.
|
|
22
|
+
* - Booleans encoded as `--flag true`/`--flag false` explicitly, matching current CLI.
|
|
23
|
+
*/
|
|
24
|
+
function buildOperationArgv(operation, params, options, runtimeTimeoutMs, defaultChangeMode, user) {
|
|
25
|
+
// Inject defaultChangeMode into params BEFORE encoding — single source of truth.
|
|
26
|
+
let normalizedParams = defaultChangeMode != null && params.changeMode == null && operation.params.some((p) => p.name === 'changeMode')
|
|
27
|
+
? { ...params, changeMode: defaultChangeMode }
|
|
28
|
+
: params;
|
|
29
|
+
// Inject user identity for doc.open when not already specified.
|
|
30
|
+
if (user != null && operation.operationId === 'doc.open') {
|
|
31
|
+
if (normalizedParams.userName == null && user.name) {
|
|
32
|
+
normalizedParams = { ...normalizedParams, userName: user.name };
|
|
33
|
+
}
|
|
34
|
+
if (normalizedParams.userEmail == null && user.email) {
|
|
35
|
+
normalizedParams = { ...normalizedParams, userEmail: user.email };
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const argv = [...operation.commandTokens];
|
|
39
|
+
for (const spec of operation.params) {
|
|
40
|
+
const value = normalizedParams[spec.name];
|
|
41
|
+
if (value == null)
|
|
42
|
+
continue;
|
|
43
|
+
const flag = `--${spec.flag ?? spec.name}`;
|
|
44
|
+
switch (spec.kind) {
|
|
45
|
+
case 'doc':
|
|
46
|
+
argv.push(String(value));
|
|
47
|
+
break;
|
|
48
|
+
case 'flag':
|
|
49
|
+
if (spec.type === 'boolean') {
|
|
50
|
+
// Explicit true/false — matches current CLI operation-executor.ts.
|
|
51
|
+
argv.push(flag, value === true ? 'true' : 'false');
|
|
52
|
+
}
|
|
53
|
+
else if (spec.type === 'string[]') {
|
|
54
|
+
if (Array.isArray(value)) {
|
|
55
|
+
for (const entry of value)
|
|
56
|
+
argv.push(flag, String(entry));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
argv.push(flag, String(value));
|
|
61
|
+
}
|
|
62
|
+
break;
|
|
63
|
+
case 'jsonFlag':
|
|
64
|
+
argv.push(flag, JSON.stringify(value));
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const timeoutMs = options.timeoutMs ?? runtimeTimeoutMs;
|
|
69
|
+
if (timeoutMs != null) {
|
|
70
|
+
argv.push('--timeout-ms', String(timeoutMs));
|
|
71
|
+
}
|
|
72
|
+
argv.push('--output', 'json');
|
|
73
|
+
return argv;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
exports.buildOperationArgv = buildOperationArgv;
|
|
77
|
+
exports.resolveInvocation = resolveInvocation;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export type ParamType = 'string' | 'number' | 'boolean' | 'json' | 'string[]';
|
|
2
|
+
export type ParamKind = 'doc' | 'flag' | 'jsonFlag';
|
|
3
|
+
export interface OperationParamSpec {
|
|
4
|
+
readonly name: string;
|
|
5
|
+
readonly kind: ParamKind;
|
|
6
|
+
readonly flag?: string;
|
|
7
|
+
readonly type: ParamType;
|
|
8
|
+
readonly required?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface OperationSpec {
|
|
11
|
+
readonly operationId: string;
|
|
12
|
+
readonly commandTokens: readonly string[];
|
|
13
|
+
readonly params: readonly OperationParamSpec[];
|
|
14
|
+
}
|
|
15
|
+
export interface InvokeOptions {
|
|
16
|
+
timeoutMs?: number;
|
|
17
|
+
stdinBytes?: Uint8Array;
|
|
18
|
+
}
|
|
19
|
+
export type ChangeMode = 'direct' | 'tracked';
|
|
20
|
+
export interface UserIdentity {
|
|
21
|
+
name: string;
|
|
22
|
+
email?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface SuperDocClientOptions {
|
|
25
|
+
env?: Record<string, string | undefined>;
|
|
26
|
+
startupTimeoutMs?: number;
|
|
27
|
+
shutdownTimeoutMs?: number;
|
|
28
|
+
requestTimeoutMs?: number;
|
|
29
|
+
watchdogTimeoutMs?: number;
|
|
30
|
+
maxQueueDepth?: number;
|
|
31
|
+
defaultChangeMode?: ChangeMode;
|
|
32
|
+
user?: UserIdentity;
|
|
33
|
+
}
|
|
34
|
+
export interface CliInvocation {
|
|
35
|
+
command: string;
|
|
36
|
+
prefixArgs: string[];
|
|
37
|
+
}
|
|
38
|
+
export declare function resolveInvocation(cliBin: string): CliInvocation;
|
|
39
|
+
/**
|
|
40
|
+
* Build the CLI argument vector for an operation invocation.
|
|
41
|
+
*
|
|
42
|
+
* Key design choices vs old SDK:
|
|
43
|
+
* - changeMode injection BEFORE argv loop, not after. changeMode is already a
|
|
44
|
+
* param in operationSpec.params (envelope param for mutations). Appending after
|
|
45
|
+
* the loop would duplicate it.
|
|
46
|
+
* - Booleans encoded as `--flag true`/`--flag false` explicitly, matching current CLI.
|
|
47
|
+
*/
|
|
48
|
+
export declare function buildOperationArgv(operation: OperationSpec, params: Record<string, unknown>, options: InvokeOptions, runtimeTimeoutMs: number | undefined, defaultChangeMode?: ChangeMode, user?: UserIdentity): string[];
|
|
49
|
+
//# sourceMappingURL=transport-common.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport-common.d.ts","sourceRoot":"","sources":["../../src/runtime/transport-common.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,UAAU,CAAC;AAC9E,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,MAAM,GAAG,UAAU,CAAC;AAEpD,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,QAAQ,CAAC,MAAM,EAAE,SAAS,kBAAkB,EAAE,CAAC;CAChD;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB;AAED,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE9C,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,qBAAqB;IACpC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,UAAU,CAAC;IAC/B,IAAI,CAAC,EAAE,YAAY,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAMD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,CAU/D;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,aAAa,EACxB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,OAAO,EAAE,aAAa,EACtB,gBAAgB,EAAE,MAAM,GAAG,SAAS,EACpC,iBAAiB,CAAC,EAAE,UAAU,EAC9B,IAAI,CAAC,EAAE,YAAY,GAClB,MAAM,EAAE,CAsDV"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
function hasExtension(filePath, extension) {
|
|
2
|
+
return filePath.toLowerCase().endsWith(extension);
|
|
3
|
+
}
|
|
4
|
+
export function resolveInvocation(cliBin) {
|
|
5
|
+
if (hasExtension(cliBin, '.js')) {
|
|
6
|
+
return { command: 'node', prefixArgs: [cliBin] };
|
|
7
|
+
}
|
|
8
|
+
if (hasExtension(cliBin, '.ts')) {
|
|
9
|
+
return { command: 'bun', prefixArgs: [cliBin] };
|
|
10
|
+
}
|
|
11
|
+
return { command: cliBin, prefixArgs: [] };
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Build the CLI argument vector for an operation invocation.
|
|
15
|
+
*
|
|
16
|
+
* Key design choices vs old SDK:
|
|
17
|
+
* - changeMode injection BEFORE argv loop, not after. changeMode is already a
|
|
18
|
+
* param in operationSpec.params (envelope param for mutations). Appending after
|
|
19
|
+
* the loop would duplicate it.
|
|
20
|
+
* - Booleans encoded as `--flag true`/`--flag false` explicitly, matching current CLI.
|
|
21
|
+
*/
|
|
22
|
+
export function buildOperationArgv(operation, params, options, runtimeTimeoutMs, defaultChangeMode, user) {
|
|
23
|
+
// Inject defaultChangeMode into params BEFORE encoding — single source of truth.
|
|
24
|
+
let normalizedParams = defaultChangeMode != null && params.changeMode == null && operation.params.some((p) => p.name === 'changeMode')
|
|
25
|
+
? { ...params, changeMode: defaultChangeMode }
|
|
26
|
+
: params;
|
|
27
|
+
// Inject user identity for doc.open when not already specified.
|
|
28
|
+
if (user != null && operation.operationId === 'doc.open') {
|
|
29
|
+
if (normalizedParams.userName == null && user.name) {
|
|
30
|
+
normalizedParams = { ...normalizedParams, userName: user.name };
|
|
31
|
+
}
|
|
32
|
+
if (normalizedParams.userEmail == null && user.email) {
|
|
33
|
+
normalizedParams = { ...normalizedParams, userEmail: user.email };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const argv = [...operation.commandTokens];
|
|
37
|
+
for (const spec of operation.params) {
|
|
38
|
+
const value = normalizedParams[spec.name];
|
|
39
|
+
if (value == null)
|
|
40
|
+
continue;
|
|
41
|
+
const flag = `--${spec.flag ?? spec.name}`;
|
|
42
|
+
switch (spec.kind) {
|
|
43
|
+
case 'doc':
|
|
44
|
+
argv.push(String(value));
|
|
45
|
+
break;
|
|
46
|
+
case 'flag':
|
|
47
|
+
if (spec.type === 'boolean') {
|
|
48
|
+
// Explicit true/false — matches current CLI operation-executor.ts.
|
|
49
|
+
argv.push(flag, value === true ? 'true' : 'false');
|
|
50
|
+
}
|
|
51
|
+
else if (spec.type === 'string[]') {
|
|
52
|
+
if (Array.isArray(value)) {
|
|
53
|
+
for (const entry of value)
|
|
54
|
+
argv.push(flag, String(entry));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
argv.push(flag, String(value));
|
|
59
|
+
}
|
|
60
|
+
break;
|
|
61
|
+
case 'jsonFlag':
|
|
62
|
+
argv.push(flag, JSON.stringify(value));
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const timeoutMs = options.timeoutMs ?? runtimeTimeoutMs;
|
|
67
|
+
if (timeoutMs != null) {
|
|
68
|
+
argv.push('--timeout-ms', String(timeoutMs));
|
|
69
|
+
}
|
|
70
|
+
argv.push('--output', 'json');
|
|
71
|
+
return argv;
|
|
72
|
+
}
|