@superdoc-dev/sdk 1.0.0-alpha.2 → 1.0.0-alpha.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/generated/client.d.ts +1790 -0
- package/dist/generated/client.d.ts.map +1 -0
- package/dist/generated/client.js +66 -0
- package/dist/generated/contract.d.ts +13676 -0
- package/dist/generated/contract.d.ts.map +1 -0
- package/dist/generated/contract.js +17809 -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.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.d.ts +17 -0
- package/dist/runtime/errors.d.ts.map +1 -0
- package/dist/runtime/errors.js +18 -0
- package/dist/runtime/host.d.ts +36 -0
- package/dist/runtime/host.d.ts.map +1 -0
- package/dist/runtime/host.js +345 -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.d.ts +44 -0
- package/dist/runtime/transport-common.d.ts.map +1 -0
- package/dist/runtime/transport-common.js +65 -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.d.ts +113 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +360 -0
- package/package.json +20 -10
- package/tools/catalog.json +17128 -0
- package/tools/tool-name-map.json +96 -0
- package/tools/tools-policy.json +100 -0
- package/tools/tools.anthropic.json +3275 -0
- package/tools/tools.generic.json +16573 -0
- package/tools/tools.openai.json +3557 -0
- package/tools/tools.vercel.json +3557 -0
- package/skills/.gitkeep +0 -0
- package/skills/editing-docx.md +0 -153
- 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
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { createDocApi } from './generated/client.js';
|
|
2
|
+
import { type SuperDocClientOptions } from './runtime/process.js';
|
|
3
|
+
/**
|
|
4
|
+
* High-level client for interacting with SuperDoc documents via the CLI.
|
|
5
|
+
*
|
|
6
|
+
* Provides a typed `doc` API for opening, querying, and mutating documents.
|
|
7
|
+
* Call {@link connect} before operations and {@link dispose} when finished
|
|
8
|
+
* to manage the host process lifecycle.
|
|
9
|
+
*/
|
|
10
|
+
export declare class SuperDocClient {
|
|
11
|
+
private readonly runtime;
|
|
12
|
+
readonly doc: ReturnType<typeof createDocApi>;
|
|
13
|
+
constructor(options?: SuperDocClientOptions);
|
|
14
|
+
connect(): Promise<void>;
|
|
15
|
+
dispose(): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
export declare function createSuperDocClient(options?: SuperDocClientOptions): SuperDocClient;
|
|
18
|
+
export { getSkill, installSkill, listSkills } from './skills.js';
|
|
19
|
+
export { chooseTools, dispatchSuperDocTool, getToolCatalog, inferDocumentFeatures, listTools, resolveToolOperation, } from './tools.js';
|
|
20
|
+
export { SuperDocCliError } from './runtime/errors.js';
|
|
21
|
+
export type { InvokeOptions, OperationSpec, OperationParamSpec, SuperDocClientOptions, } from './runtime/process.js';
|
|
22
|
+
export type { DocumentFeatures, ToolChooserInput, ToolPhase, ToolProfile, ToolProvider, } from './tools.js';
|
|
23
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAmB,KAAK,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAEnF;;;;;;GAMG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkB;IAC1C,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;gBAElC,OAAO,GAAE,qBAA0B;IAKzC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAIxB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAG/B;AAED,wBAAgB,oBAAoB,CAAC,OAAO,GAAE,qBAA0B,GAAG,cAAc,CAExF;AAED,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EACL,WAAW,EACX,oBAAoB,EACpB,cAAc,EACd,qBAAqB,EACrB,SAAS,EACT,oBAAoB,GACrB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,YAAY,EACV,aAAa,EACb,aAAa,EACb,kBAAkB,EAClB,qBAAqB,GACtB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EACV,gBAAgB,EAChB,gBAAgB,EAChB,SAAS,EACT,WAAW,EACX,YAAY,GACb,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { createDocApi } from './generated/client.js';
|
|
2
|
+
import { SuperDocRuntime } from './runtime/process.js';
|
|
3
|
+
/**
|
|
4
|
+
* High-level client for interacting with SuperDoc documents via the CLI.
|
|
5
|
+
*
|
|
6
|
+
* Provides a typed `doc` API for opening, querying, and mutating documents.
|
|
7
|
+
* Call {@link connect} before operations and {@link dispose} when finished
|
|
8
|
+
* to manage the host process lifecycle.
|
|
9
|
+
*/
|
|
10
|
+
export class SuperDocClient {
|
|
11
|
+
runtime;
|
|
12
|
+
doc;
|
|
13
|
+
constructor(options = {}) {
|
|
14
|
+
this.runtime = new SuperDocRuntime(options);
|
|
15
|
+
this.doc = createDocApi(this.runtime);
|
|
16
|
+
}
|
|
17
|
+
async connect() {
|
|
18
|
+
await this.runtime.connect();
|
|
19
|
+
}
|
|
20
|
+
async dispose() {
|
|
21
|
+
await this.runtime.dispose();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export function createSuperDocClient(options = {}) {
|
|
25
|
+
return new SuperDocClient(options);
|
|
26
|
+
}
|
|
27
|
+
export { getSkill, installSkill, listSkills } from './skills.js';
|
|
28
|
+
export { chooseTools, dispatchSuperDocTool, getToolCatalog, inferDocumentFeatures, listTools, resolveToolOperation, } from './tools.js';
|
|
29
|
+
export { SuperDocCliError } from './runtime/errors.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embedded-cli.d.ts","sourceRoot":"","sources":["../../src/runtime/embedded-cli.ts"],"names":[],"mappings":"AAuEA;;GAEG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,CA2BjD"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { chmodSync, existsSync } from 'node:fs';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { SuperDocCliError } from './errors.js';
|
|
6
|
+
const require = createRequire(import.meta.url);
|
|
7
|
+
const TARGET_TO_PACKAGE = {
|
|
8
|
+
'darwin-arm64': '@superdoc-dev/sdk-darwin-arm64',
|
|
9
|
+
'darwin-x64': '@superdoc-dev/sdk-darwin-x64',
|
|
10
|
+
'linux-x64': '@superdoc-dev/sdk-linux-x64',
|
|
11
|
+
'linux-arm64': '@superdoc-dev/sdk-linux-arm64',
|
|
12
|
+
'windows-x64': '@superdoc-dev/sdk-windows-x64',
|
|
13
|
+
};
|
|
14
|
+
const TARGET_TO_DIR = {
|
|
15
|
+
'darwin-arm64': 'sdk-darwin-arm64',
|
|
16
|
+
'darwin-x64': 'sdk-darwin-x64',
|
|
17
|
+
'linux-x64': 'sdk-linux-x64',
|
|
18
|
+
'linux-arm64': 'sdk-linux-arm64',
|
|
19
|
+
'windows-x64': 'sdk-windows-x64',
|
|
20
|
+
};
|
|
21
|
+
function resolveTarget() {
|
|
22
|
+
const platform = process.platform;
|
|
23
|
+
const arch = process.arch;
|
|
24
|
+
if (platform === 'darwin' && arch === 'arm64')
|
|
25
|
+
return 'darwin-arm64';
|
|
26
|
+
if (platform === 'darwin' && arch === 'x64')
|
|
27
|
+
return 'darwin-x64';
|
|
28
|
+
if (platform === 'linux' && arch === 'x64')
|
|
29
|
+
return 'linux-x64';
|
|
30
|
+
if (platform === 'linux' && arch === 'arm64')
|
|
31
|
+
return 'linux-arm64';
|
|
32
|
+
if (platform === 'win32' && arch === 'x64')
|
|
33
|
+
return 'windows-x64';
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
function binaryNameForTarget(target) {
|
|
37
|
+
return target === 'windows-x64' ? 'superdoc.exe' : 'superdoc';
|
|
38
|
+
}
|
|
39
|
+
function ensureExecutable(binaryPath) {
|
|
40
|
+
if (process.platform === 'win32')
|
|
41
|
+
return;
|
|
42
|
+
try {
|
|
43
|
+
chmodSync(binaryPath, 0o755);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// Non-fatal: if chmod fails, spawn() will surface the real execution error.
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function resolveFromPlatformPackage(target) {
|
|
50
|
+
const pkg = TARGET_TO_PACKAGE[target];
|
|
51
|
+
const binaryName = binaryNameForTarget(target);
|
|
52
|
+
try {
|
|
53
|
+
return require.resolve(`${pkg}/bin/${binaryName}`);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function resolveFromWorkspaceFallback(target) {
|
|
60
|
+
const binaryName = binaryNameForTarget(target);
|
|
61
|
+
const dirName = TARGET_TO_DIR[target];
|
|
62
|
+
const filePath = path.resolve(fileURLToPath(new URL('../../platforms', import.meta.url)), dirName, 'bin', binaryName);
|
|
63
|
+
if (!existsSync(filePath))
|
|
64
|
+
return null;
|
|
65
|
+
return filePath;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Resolve the path to the embedded SuperDoc CLI binary for the current platform.
|
|
69
|
+
*/
|
|
70
|
+
export function resolveEmbeddedCliBinary() {
|
|
71
|
+
const target = resolveTarget();
|
|
72
|
+
if (!target) {
|
|
73
|
+
throw new SuperDocCliError('No embedded SuperDoc CLI binary is available for this platform.', {
|
|
74
|
+
code: 'UNSUPPORTED_PLATFORM',
|
|
75
|
+
details: {
|
|
76
|
+
platform: process.platform,
|
|
77
|
+
arch: process.arch,
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
const platformPackagePath = resolveFromPlatformPackage(target);
|
|
82
|
+
const resolvedPath = platformPackagePath ?? resolveFromWorkspaceFallback(target);
|
|
83
|
+
if (!resolvedPath) {
|
|
84
|
+
throw new SuperDocCliError('Embedded SuperDoc CLI binary is missing for this platform.', {
|
|
85
|
+
code: 'CLI_BINARY_MISSING',
|
|
86
|
+
details: {
|
|
87
|
+
target,
|
|
88
|
+
packageName: TARGET_TO_PACKAGE[target],
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
ensureExecutable(resolvedPath);
|
|
93
|
+
return resolvedPath;
|
|
94
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error thrown by the SuperDoc SDK when a CLI operation fails.
|
|
3
|
+
*
|
|
4
|
+
* Includes a machine-readable `code` for programmatic error handling
|
|
5
|
+
* and optional `details` with structured diagnostic context.
|
|
6
|
+
*/
|
|
7
|
+
export declare class SuperDocCliError extends Error {
|
|
8
|
+
readonly code: string;
|
|
9
|
+
readonly details?: unknown;
|
|
10
|
+
readonly exitCode?: number;
|
|
11
|
+
constructor(message: string, options: {
|
|
12
|
+
code: string;
|
|
13
|
+
details?: unknown;
|
|
14
|
+
exitCode?: number;
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/runtime/errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;gBAEf,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE;CAO7F"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error thrown by the SuperDoc SDK when a CLI operation fails.
|
|
3
|
+
*
|
|
4
|
+
* Includes a machine-readable `code` for programmatic error handling
|
|
5
|
+
* and optional `details` with structured diagnostic context.
|
|
6
|
+
*/
|
|
7
|
+
export class SuperDocCliError extends Error {
|
|
8
|
+
code;
|
|
9
|
+
details;
|
|
10
|
+
exitCode;
|
|
11
|
+
constructor(message, options) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.name = 'SuperDocCliError';
|
|
14
|
+
this.code = options.code;
|
|
15
|
+
this.details = options.details;
|
|
16
|
+
this.exitCode = options.exitCode;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type InvokeOptions, type OperationSpec, type SuperDocClientOptions } from './transport-common.js';
|
|
2
|
+
/**
|
|
3
|
+
* Transport that communicates with a long-lived CLI host process over JSON-RPC stdio.
|
|
4
|
+
*/
|
|
5
|
+
export declare class HostTransport {
|
|
6
|
+
private readonly cliBin;
|
|
7
|
+
private readonly env?;
|
|
8
|
+
private readonly startupTimeoutMs;
|
|
9
|
+
private readonly shutdownTimeoutMs;
|
|
10
|
+
private readonly requestTimeoutMs?;
|
|
11
|
+
private readonly watchdogTimeoutMs;
|
|
12
|
+
private readonly maxQueueDepth;
|
|
13
|
+
private readonly defaultChangeMode?;
|
|
14
|
+
private child;
|
|
15
|
+
private stdoutReader;
|
|
16
|
+
private readonly pending;
|
|
17
|
+
private nextRequestId;
|
|
18
|
+
private connecting;
|
|
19
|
+
private stopping;
|
|
20
|
+
constructor(options: {
|
|
21
|
+
cliBin: string;
|
|
22
|
+
} & SuperDocClientOptions);
|
|
23
|
+
connect(): Promise<void>;
|
|
24
|
+
dispose(): Promise<void>;
|
|
25
|
+
invoke<TData extends Record<string, unknown>>(operation: OperationSpec, params?: Record<string, unknown>, options?: InvokeOptions): Promise<TData>;
|
|
26
|
+
private ensureConnected;
|
|
27
|
+
private startHostProcess;
|
|
28
|
+
private assertCapabilities;
|
|
29
|
+
private resolveWatchdogTimeout;
|
|
30
|
+
private sendJsonRpcRequest;
|
|
31
|
+
private onStdoutLine;
|
|
32
|
+
private mapJsonRpcError;
|
|
33
|
+
private handleDisconnect;
|
|
34
|
+
private cleanupProcess;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=host.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../../src/runtime/host.ts"],"names":[],"mappings":"AAEA,OAAO,EAIL,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC3B,MAAM,uBAAuB,CAAC;AA4B/B;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAqC;IAC1D,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAa;IAEhD,OAAO,CAAC,KAAK,CAA+C;IAC5D,OAAO,CAAC,YAAY,CAAkC;IACtD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqC;IAC7D,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,UAAU,CAA8B;IAChD,OAAO,CAAC,QAAQ,CAAS;gBAEb,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,qBAAqB;IAkBzD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAIxB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IA4BxB,MAAM,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChD,SAAS,EAAE,aAAa,EACxB,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACpC,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,KAAK,CAAC;YA2BH,eAAe;YAkBf,gBAAgB;IAuE9B,OAAO,CAAC,kBAAkB;IAuC1B,OAAO,CAAC,sBAAsB;YAYhB,kBAAkB;IAkEhC,OAAO,CAAC,YAAY;IA2CpB,OAAO,CAAC,eAAe;IAmCvB,OAAO,CAAC,gBAAgB;IAIxB,OAAO,CAAC,cAAc;CA6BvB"}
|
|
@@ -0,0 +1,345 @@
|
|
|
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
|
+
child = null;
|
|
22
|
+
stdoutReader = null;
|
|
23
|
+
pending = new Map();
|
|
24
|
+
nextRequestId = 1;
|
|
25
|
+
connecting = null;
|
|
26
|
+
stopping = false;
|
|
27
|
+
constructor(options) {
|
|
28
|
+
this.cliBin = options.cliBin;
|
|
29
|
+
this.env = options.env;
|
|
30
|
+
this.startupTimeoutMs = options.startupTimeoutMs ?? 5_000;
|
|
31
|
+
this.shutdownTimeoutMs = options.shutdownTimeoutMs ?? 5_000;
|
|
32
|
+
this.requestTimeoutMs = options.requestTimeoutMs;
|
|
33
|
+
this.watchdogTimeoutMs = options.watchdogTimeoutMs ?? 30_000;
|
|
34
|
+
this.maxQueueDepth = options.maxQueueDepth ?? 100;
|
|
35
|
+
if (options.defaultChangeMode != null && !CHANGE_MODES.includes(options.defaultChangeMode)) {
|
|
36
|
+
throw new SuperDocCliError('defaultChangeMode must be "direct" or "tracked".', {
|
|
37
|
+
code: 'INVALID_ARGUMENT',
|
|
38
|
+
details: { defaultChangeMode: options.defaultChangeMode },
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
this.defaultChangeMode = options.defaultChangeMode;
|
|
42
|
+
}
|
|
43
|
+
async connect() {
|
|
44
|
+
await this.ensureConnected();
|
|
45
|
+
}
|
|
46
|
+
async dispose() {
|
|
47
|
+
if (!this.child)
|
|
48
|
+
return;
|
|
49
|
+
this.stopping = true;
|
|
50
|
+
const child = this.child;
|
|
51
|
+
try {
|
|
52
|
+
await this.sendJsonRpcRequest('host.shutdown', {}, this.shutdownTimeoutMs);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// ignore and force shutdown below
|
|
56
|
+
}
|
|
57
|
+
await new Promise((resolve) => {
|
|
58
|
+
const timer = setTimeout(() => {
|
|
59
|
+
child.kill('SIGKILL');
|
|
60
|
+
resolve();
|
|
61
|
+
}, this.shutdownTimeoutMs);
|
|
62
|
+
child.once('close', () => {
|
|
63
|
+
clearTimeout(timer);
|
|
64
|
+
resolve();
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
this.cleanupProcess(null);
|
|
68
|
+
this.stopping = false;
|
|
69
|
+
}
|
|
70
|
+
async invoke(operation, params = {}, options = {}) {
|
|
71
|
+
await this.ensureConnected();
|
|
72
|
+
const argv = buildOperationArgv(operation, params, options, this.requestTimeoutMs, this.defaultChangeMode);
|
|
73
|
+
const stdinBase64 = options.stdinBytes ? Buffer.from(options.stdinBytes).toString('base64') : '';
|
|
74
|
+
const watchdogTimeout = this.resolveWatchdogTimeout(options.timeoutMs);
|
|
75
|
+
const response = await this.sendJsonRpcRequest('cli.invoke', {
|
|
76
|
+
argv,
|
|
77
|
+
stdinBase64,
|
|
78
|
+
}, watchdogTimeout);
|
|
79
|
+
if (typeof response !== 'object' || response == null || Array.isArray(response)) {
|
|
80
|
+
throw new SuperDocCliError('Host returned invalid cli.invoke result.', {
|
|
81
|
+
code: 'HOST_PROTOCOL_ERROR',
|
|
82
|
+
details: { result: response },
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
const resultRecord = response;
|
|
86
|
+
return resultRecord.data;
|
|
87
|
+
}
|
|
88
|
+
async ensureConnected() {
|
|
89
|
+
if (this.child && !this.child.killed) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (this.connecting) {
|
|
93
|
+
await this.connecting;
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
this.connecting = this.startHostProcess();
|
|
97
|
+
try {
|
|
98
|
+
await this.connecting;
|
|
99
|
+
}
|
|
100
|
+
finally {
|
|
101
|
+
this.connecting = null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
async startHostProcess() {
|
|
105
|
+
const { command, prefixArgs } = resolveInvocation(this.cliBin);
|
|
106
|
+
const args = [...prefixArgs, 'host', '--stdio'];
|
|
107
|
+
const child = spawn(command, args, {
|
|
108
|
+
env: {
|
|
109
|
+
...process.env,
|
|
110
|
+
...(this.env ?? {}),
|
|
111
|
+
},
|
|
112
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
113
|
+
});
|
|
114
|
+
this.child = child;
|
|
115
|
+
const stdoutReader = createInterface({
|
|
116
|
+
input: child.stdout,
|
|
117
|
+
crlfDelay: Number.POSITIVE_INFINITY,
|
|
118
|
+
});
|
|
119
|
+
this.stdoutReader = stdoutReader;
|
|
120
|
+
stdoutReader.on('line', (line) => {
|
|
121
|
+
this.onStdoutLine(line);
|
|
122
|
+
});
|
|
123
|
+
child.stderr.on('data', () => {
|
|
124
|
+
// stderr intentionally ignored in host mode
|
|
125
|
+
});
|
|
126
|
+
child.on('error', (error) => {
|
|
127
|
+
this.handleDisconnect(new SuperDocCliError('Host process failed.', {
|
|
128
|
+
code: 'HOST_DISCONNECTED',
|
|
129
|
+
details: {
|
|
130
|
+
message: error instanceof Error ? error.message : String(error),
|
|
131
|
+
},
|
|
132
|
+
}));
|
|
133
|
+
});
|
|
134
|
+
child.on('close', (code, signal) => {
|
|
135
|
+
if (this.stopping) {
|
|
136
|
+
this.cleanupProcess(null);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
this.handleDisconnect(new SuperDocCliError('Host process disconnected.', {
|
|
140
|
+
code: 'HOST_DISCONNECTED',
|
|
141
|
+
details: { exitCode: code, signal },
|
|
142
|
+
}));
|
|
143
|
+
});
|
|
144
|
+
try {
|
|
145
|
+
const capabilities = await this.sendJsonRpcRequest('host.capabilities', {}, this.startupTimeoutMs);
|
|
146
|
+
this.assertCapabilities(capabilities);
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
const normalized = error instanceof SuperDocCliError
|
|
150
|
+
? error
|
|
151
|
+
: new SuperDocCliError('Host handshake failed.', {
|
|
152
|
+
code: 'HOST_HANDSHAKE_FAILED',
|
|
153
|
+
details: {
|
|
154
|
+
message: error instanceof Error ? error.message : String(error),
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
this.handleDisconnect(normalized);
|
|
158
|
+
throw normalized;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
assertCapabilities(response) {
|
|
162
|
+
if (typeof response !== 'object' || response == null || Array.isArray(response)) {
|
|
163
|
+
throw new SuperDocCliError('Host capabilities response is invalid.', {
|
|
164
|
+
code: 'HOST_HANDSHAKE_FAILED',
|
|
165
|
+
details: { response },
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
const record = response;
|
|
169
|
+
const protocolVersion = record.protocolVersion;
|
|
170
|
+
const features = record.features;
|
|
171
|
+
if (protocolVersion !== HOST_PROTOCOL_VERSION) {
|
|
172
|
+
throw new SuperDocCliError('Host protocol version is unsupported.', {
|
|
173
|
+
code: 'HOST_HANDSHAKE_FAILED',
|
|
174
|
+
details: {
|
|
175
|
+
expected: HOST_PROTOCOL_VERSION,
|
|
176
|
+
actual: protocolVersion,
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
if (!Array.isArray(features) || features.some((f) => typeof f !== 'string')) {
|
|
181
|
+
throw new SuperDocCliError('Host capabilities.features must be a string array.', {
|
|
182
|
+
code: 'HOST_HANDSHAKE_FAILED',
|
|
183
|
+
details: { features },
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
for (const requiredFeature of REQUIRED_FEATURES) {
|
|
187
|
+
if (!features.includes(requiredFeature)) {
|
|
188
|
+
throw new SuperDocCliError(`Host does not support required feature: ${requiredFeature}`, {
|
|
189
|
+
code: 'HOST_HANDSHAKE_FAILED',
|
|
190
|
+
details: { features },
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
resolveWatchdogTimeout(timeoutMsOverride) {
|
|
196
|
+
if (timeoutMsOverride != null) {
|
|
197
|
+
return Math.max(this.watchdogTimeoutMs, timeoutMsOverride + 1_000);
|
|
198
|
+
}
|
|
199
|
+
if (this.requestTimeoutMs != null) {
|
|
200
|
+
return Math.max(this.watchdogTimeoutMs, this.requestTimeoutMs + 1_000);
|
|
201
|
+
}
|
|
202
|
+
return this.watchdogTimeoutMs;
|
|
203
|
+
}
|
|
204
|
+
async sendJsonRpcRequest(method, params, watchdogTimeoutMs) {
|
|
205
|
+
const child = this.child;
|
|
206
|
+
if (!child || !child.stdin.writable) {
|
|
207
|
+
throw new SuperDocCliError('Host process is not available.', {
|
|
208
|
+
code: 'HOST_DISCONNECTED',
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
if (this.pending.size >= this.maxQueueDepth) {
|
|
212
|
+
throw new SuperDocCliError('Host request queue is full.', {
|
|
213
|
+
code: 'HOST_QUEUE_FULL',
|
|
214
|
+
details: { maxQueueDepth: this.maxQueueDepth },
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
const id = this.nextRequestId;
|
|
218
|
+
this.nextRequestId += 1;
|
|
219
|
+
const payload = JSON.stringify({
|
|
220
|
+
jsonrpc: '2.0',
|
|
221
|
+
id,
|
|
222
|
+
method,
|
|
223
|
+
params,
|
|
224
|
+
});
|
|
225
|
+
const promise = new Promise((resolve, reject) => {
|
|
226
|
+
const timer = setTimeout(() => {
|
|
227
|
+
this.pending.delete(id);
|
|
228
|
+
reject(new SuperDocCliError(`Host watchdog timed out waiting for ${method}.`, {
|
|
229
|
+
code: 'HOST_TIMEOUT',
|
|
230
|
+
details: { method, timeoutMs: watchdogTimeoutMs },
|
|
231
|
+
}));
|
|
232
|
+
this.handleDisconnect(new SuperDocCliError('Host watchdog timeout; host process will be restarted on next request.', {
|
|
233
|
+
code: 'HOST_DISCONNECTED',
|
|
234
|
+
details: { method, timeoutMs: watchdogTimeoutMs },
|
|
235
|
+
}));
|
|
236
|
+
}, watchdogTimeoutMs);
|
|
237
|
+
this.pending.set(id, { resolve, reject, timer });
|
|
238
|
+
child.stdin.write(`${payload}\n`, (error) => {
|
|
239
|
+
if (!error)
|
|
240
|
+
return;
|
|
241
|
+
const pending = this.pending.get(id);
|
|
242
|
+
if (!pending)
|
|
243
|
+
return;
|
|
244
|
+
clearTimeout(pending.timer);
|
|
245
|
+
this.pending.delete(id);
|
|
246
|
+
reject(new SuperDocCliError('Failed to write request to host process.', {
|
|
247
|
+
code: 'HOST_DISCONNECTED',
|
|
248
|
+
details: { method, message: error.message },
|
|
249
|
+
}));
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
return promise;
|
|
253
|
+
}
|
|
254
|
+
onStdoutLine(line) {
|
|
255
|
+
let parsed;
|
|
256
|
+
try {
|
|
257
|
+
parsed = JSON.parse(line);
|
|
258
|
+
}
|
|
259
|
+
catch {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
if (typeof parsed !== 'object' || parsed == null || Array.isArray(parsed)) {
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
const record = parsed;
|
|
266
|
+
if (record.jsonrpc !== '2.0') {
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
// Notification (no id) — reserved for future eventing
|
|
270
|
+
if ('method' in record && !('id' in record)) {
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
const idRaw = record.id;
|
|
274
|
+
if (typeof idRaw !== 'number') {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
const pending = this.pending.get(idRaw);
|
|
278
|
+
if (!pending) {
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
clearTimeout(pending.timer);
|
|
282
|
+
this.pending.delete(idRaw);
|
|
283
|
+
if ('error' in record) {
|
|
284
|
+
pending.reject(this.mapJsonRpcError(record.error));
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
pending.resolve(record.result);
|
|
288
|
+
}
|
|
289
|
+
mapJsonRpcError(rawError) {
|
|
290
|
+
if (typeof rawError !== 'object' || rawError == null || Array.isArray(rawError)) {
|
|
291
|
+
return new SuperDocCliError('Host returned an unknown JSON-RPC error.', {
|
|
292
|
+
code: 'HOST_PROTOCOL_ERROR',
|
|
293
|
+
details: { error: rawError },
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
const error = rawError;
|
|
297
|
+
const data = error.data;
|
|
298
|
+
const cliCode = typeof data?.cliCode === 'string' ? data.cliCode : undefined;
|
|
299
|
+
const cliMessage = typeof data?.message === 'string' ? data.message : undefined;
|
|
300
|
+
const exitCode = typeof data?.exitCode === 'number' ? data.exitCode : undefined;
|
|
301
|
+
if (cliCode) {
|
|
302
|
+
return new SuperDocCliError(cliMessage ?? error.message ?? 'Command failed.', {
|
|
303
|
+
code: cliCode,
|
|
304
|
+
details: data?.details,
|
|
305
|
+
exitCode,
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
if (error.code === JSON_RPC_TIMEOUT_CODE) {
|
|
309
|
+
return new SuperDocCliError(error.message, {
|
|
310
|
+
code: 'TIMEOUT',
|
|
311
|
+
details: data,
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
return new SuperDocCliError(error.message, {
|
|
315
|
+
code: 'COMMAND_FAILED',
|
|
316
|
+
details: data,
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
handleDisconnect(error) {
|
|
320
|
+
this.cleanupProcess(error);
|
|
321
|
+
}
|
|
322
|
+
cleanupProcess(error) {
|
|
323
|
+
const child = this.child;
|
|
324
|
+
if (child) {
|
|
325
|
+
child.removeAllListeners();
|
|
326
|
+
child.kill('SIGKILL');
|
|
327
|
+
}
|
|
328
|
+
this.child = null;
|
|
329
|
+
if (this.stdoutReader) {
|
|
330
|
+
this.stdoutReader.removeAllListeners();
|
|
331
|
+
this.stdoutReader.close();
|
|
332
|
+
this.stdoutReader = null;
|
|
333
|
+
}
|
|
334
|
+
const pendingEntries = Array.from(this.pending.values());
|
|
335
|
+
this.pending.clear();
|
|
336
|
+
const rejection = error ??
|
|
337
|
+
new SuperDocCliError('Host process was disposed while request was in flight.', {
|
|
338
|
+
code: 'HOST_DISCONNECTED',
|
|
339
|
+
});
|
|
340
|
+
for (const pending of pendingEntries) {
|
|
341
|
+
clearTimeout(pending.timer);
|
|
342
|
+
pending.reject(rejection);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
@@ -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 extends Record<string, 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,EACV,aAAa,EACb,kBAAkB,EAClB,aAAa,EACb,qBAAqB,EACtB,MAAM,uBAAuB,CAAC;AAE/B;;;;;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,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChD,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
|
+
}
|