@maximem/synap-js-sdk 0.3.0 → 0.4.1
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/MIGRATING.md +147 -0
- package/README.md +95 -82
- package/bin/synap-js-sdk.js +45 -122
- package/dist/anticipation-cache-ARYEEKMU.cjs +10 -0
- package/dist/anticipation-cache-ARYEEKMU.cjs.map +1 -0
- package/dist/anticipation-cache-BMVF5sXb.d.cts +190 -0
- package/dist/anticipation-cache-BMVF5sXb.d.ts +190 -0
- package/dist/anticipation-cache-H7HEVWQC.js +10 -0
- package/dist/anticipation-cache-H7HEVWQC.js.map +1 -0
- package/dist/chunk-5SC3ELNK.js +856 -0
- package/dist/chunk-5SC3ELNK.js.map +1 -0
- package/dist/chunk-6LDUDKHS.js +832 -0
- package/dist/chunk-6LDUDKHS.js.map +1 -0
- package/dist/chunk-HR5Y3KXS.cjs +172 -0
- package/dist/chunk-HR5Y3KXS.cjs.map +1 -0
- package/dist/chunk-QBCVMPGK.js +28 -0
- package/dist/chunk-QBCVMPGK.js.map +1 -0
- package/dist/chunk-RB57I2IQ.cjs +832 -0
- package/dist/chunk-RB57I2IQ.cjs.map +1 -0
- package/dist/chunk-T3NZBIV2.js +172 -0
- package/dist/chunk-T3NZBIV2.js.map +1 -0
- package/dist/chunk-VXRCBMCY.cjs +856 -0
- package/dist/chunk-VXRCBMCY.cjs.map +1 -0
- package/dist/chunk-ZKOJDUN4.cjs +28 -0
- package/dist/chunk-ZKOJDUN4.cjs.map +1 -0
- package/dist/env-55ULHNJZ.cjs +11 -0
- package/dist/env-55ULHNJZ.cjs.map +1 -0
- package/dist/env-QCUKKW36.js +11 -0
- package/dist/env-QCUKKW36.js.map +1 -0
- package/dist/grpc/index.cjs +87 -0
- package/dist/grpc/index.cjs.map +1 -0
- package/dist/grpc/index.d.cts +162 -0
- package/dist/grpc/index.d.ts +162 -0
- package/dist/grpc/index.js +87 -0
- package/dist/grpc/index.js.map +1 -0
- package/dist/index.cjs +2702 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1491 -0
- package/dist/index.d.ts +1491 -0
- package/dist/index.js +2702 -0
- package/dist/index.js.map +1 -0
- package/dist/stream-client-ME5N5NK4.js +7 -0
- package/dist/stream-client-ME5N5NK4.js.map +1 -0
- package/dist/stream-client-ZN6ADZBR.cjs +7 -0
- package/dist/stream-client-ZN6ADZBR.cjs.map +1 -0
- package/package.json +68 -23
- package/.env.example +0 -39
- package/bridge/synap_bridge.py +0 -768
- package/src/bridge-manager.js +0 -262
- package/src/errors.js +0 -168
- package/src/index.js +0 -13
- package/src/runtime.js +0 -161
- package/src/setup-typescript.js +0 -161
- package/src/synap-client.js +0 -464
- package/types/index.d.ts +0 -475
package/src/bridge-manager.js
DELETED
|
@@ -1,262 +0,0 @@
|
|
|
1
|
-
const { spawn, spawnSync } = require('child_process');
|
|
2
|
-
const readline = require('readline');
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
const {
|
|
5
|
-
resolveBridgeScriptPath,
|
|
6
|
-
resolvePythonBin,
|
|
7
|
-
resolveInstanceId,
|
|
8
|
-
setupPythonRuntime,
|
|
9
|
-
} = require('./runtime');
|
|
10
|
-
const { createSynapError } = require('./errors');
|
|
11
|
-
|
|
12
|
-
class BridgeManager {
|
|
13
|
-
constructor(options = {}) {
|
|
14
|
-
this.options = {
|
|
15
|
-
requestTimeoutMs: 30_000,
|
|
16
|
-
initTimeoutMs: 45_000,
|
|
17
|
-
ingestTimeoutMs: 120_000,
|
|
18
|
-
autoSetup: false,
|
|
19
|
-
...options,
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
this.child = null;
|
|
23
|
-
this.stdoutReader = null;
|
|
24
|
-
this.stderrReader = null;
|
|
25
|
-
this.pending = new Map();
|
|
26
|
-
this.nextId = 1;
|
|
27
|
-
this.initPromise = null;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
async ensureStarted() {
|
|
31
|
-
if (this.initPromise) return this.initPromise;
|
|
32
|
-
|
|
33
|
-
this.initPromise = (async () => {
|
|
34
|
-
const bridgeScript = resolveBridgeScriptPath(this.options.bridgeScriptPath);
|
|
35
|
-
const pythonBin = await this.#resolvePythonBinary();
|
|
36
|
-
this.#preflightPythonImport(pythonBin);
|
|
37
|
-
|
|
38
|
-
if (!fs.existsSync(bridgeScript)) {
|
|
39
|
-
throw new Error(`Bridge script not found at ${bridgeScript}`);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const bridgeEnv = {
|
|
43
|
-
...process.env,
|
|
44
|
-
PYTHONUNBUFFERED: '1',
|
|
45
|
-
...(this.options.pythonEnv || {}),
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
this.child = spawn(pythonBin, [bridgeScript], {
|
|
49
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
50
|
-
env: bridgeEnv,
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
this.#attachReaders();
|
|
54
|
-
this.#attachLifecycle();
|
|
55
|
-
|
|
56
|
-
const instanceId = resolveInstanceId(this.options.instanceId);
|
|
57
|
-
|
|
58
|
-
await this.sendCommand(
|
|
59
|
-
'init',
|
|
60
|
-
{
|
|
61
|
-
instance_id: instanceId,
|
|
62
|
-
api_key: this.options.apiKey
|
|
63
|
-
|| process.env.SYNAP_API_KEY,
|
|
64
|
-
base_url: this.options.baseUrl || process.env.SYNAP_BASE_URL,
|
|
65
|
-
grpc_host: this.options.grpcHost || process.env.SYNAP_GRPC_HOST,
|
|
66
|
-
grpc_port: Number(this.options.grpcPort || process.env.SYNAP_GRPC_PORT || 50051),
|
|
67
|
-
grpc_use_tls: this.options.grpcUseTls ?? (process.env.SYNAP_GRPC_TLS === 'true'),
|
|
68
|
-
},
|
|
69
|
-
this.options.initTimeoutMs
|
|
70
|
-
);
|
|
71
|
-
})()
|
|
72
|
-
.catch((error) => {
|
|
73
|
-
this.initPromise = null;
|
|
74
|
-
throw error;
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
return this.initPromise;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
async sendCommand(method, params = {}, timeoutMs = this.options.requestTimeoutMs) {
|
|
81
|
-
if (!this.child || !this.child.stdin || !this.child.stdin.writable) {
|
|
82
|
-
throw new Error('Synap bridge process is not running');
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const id = this.nextId++;
|
|
86
|
-
|
|
87
|
-
return new Promise((resolve, reject) => {
|
|
88
|
-
const timer = setTimeout(() => {
|
|
89
|
-
this.pending.delete(id);
|
|
90
|
-
reject(new Error(`Bridge command '${method}' timed out after ${timeoutMs}ms`));
|
|
91
|
-
}, timeoutMs);
|
|
92
|
-
|
|
93
|
-
this.pending.set(id, { resolve, reject, timer });
|
|
94
|
-
|
|
95
|
-
try {
|
|
96
|
-
this.child.stdin.write(`${JSON.stringify({ id, method, params })}\n`);
|
|
97
|
-
} catch (error) {
|
|
98
|
-
clearTimeout(timer);
|
|
99
|
-
this.pending.delete(id);
|
|
100
|
-
reject(error);
|
|
101
|
-
}
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
async call(method, params = {}, timeoutMs) {
|
|
106
|
-
await this.ensureStarted();
|
|
107
|
-
return this.sendCommand(method, params, timeoutMs);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
async shutdown() {
|
|
111
|
-
if (!this.child) return;
|
|
112
|
-
|
|
113
|
-
try {
|
|
114
|
-
await this.sendCommand('shutdown', {}, 10_000);
|
|
115
|
-
} catch (_) {
|
|
116
|
-
// Ignore shutdown RPC errors and continue with forced process stop.
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const proc = this.child;
|
|
120
|
-
this.child = null;
|
|
121
|
-
|
|
122
|
-
setTimeout(() => {
|
|
123
|
-
if (proc && !proc.killed) proc.kill();
|
|
124
|
-
}, 1_500);
|
|
125
|
-
|
|
126
|
-
this.#resetState();
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
#attachReaders() {
|
|
130
|
-
this.stdoutReader = readline.createInterface({ input: this.child.stdout });
|
|
131
|
-
this.stderrReader = readline.createInterface({ input: this.child.stderr });
|
|
132
|
-
|
|
133
|
-
this.stdoutReader.on('line', (line) => {
|
|
134
|
-
let payload;
|
|
135
|
-
|
|
136
|
-
try {
|
|
137
|
-
payload = JSON.parse(line);
|
|
138
|
-
} catch (_) {
|
|
139
|
-
if (this.options.onLog) this.options.onLog('error', `Bad bridge JSON: ${line}`);
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const pending = this.pending.get(payload.id);
|
|
144
|
-
if (!pending) return;
|
|
145
|
-
|
|
146
|
-
clearTimeout(pending.timer);
|
|
147
|
-
this.pending.delete(payload.id);
|
|
148
|
-
|
|
149
|
-
if (payload.error) {
|
|
150
|
-
pending.reject(createSynapError(payload.error, payload.error_type));
|
|
151
|
-
} else {
|
|
152
|
-
pending.resolve(payload.result);
|
|
153
|
-
}
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
this.stderrReader.on('line', (line) => {
|
|
157
|
-
if (this.options.onLog) this.options.onLog('debug', line);
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
#attachLifecycle() {
|
|
162
|
-
this.child.on('exit', (code) => {
|
|
163
|
-
const err = new Error(`Synap bridge exited with code ${code}`);
|
|
164
|
-
for (const [, pending] of this.pending) {
|
|
165
|
-
clearTimeout(pending.timer);
|
|
166
|
-
pending.reject(err);
|
|
167
|
-
}
|
|
168
|
-
this.#resetState();
|
|
169
|
-
if (this.options.onLog) this.options.onLog('error', err.message);
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
this.child.on('error', (error) => {
|
|
173
|
-
for (const [, pending] of this.pending) {
|
|
174
|
-
clearTimeout(pending.timer);
|
|
175
|
-
pending.reject(error);
|
|
176
|
-
}
|
|
177
|
-
this.#resetState();
|
|
178
|
-
if (this.options.onLog) this.options.onLog('error', error.message);
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
#resetState() {
|
|
183
|
-
this.pending.clear();
|
|
184
|
-
this.initPromise = null;
|
|
185
|
-
|
|
186
|
-
if (this.stdoutReader) {
|
|
187
|
-
this.stdoutReader.close();
|
|
188
|
-
this.stdoutReader = null;
|
|
189
|
-
}
|
|
190
|
-
if (this.stderrReader) {
|
|
191
|
-
this.stderrReader.close();
|
|
192
|
-
this.stderrReader = null;
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
async #resolvePythonBinary() {
|
|
197
|
-
const candidates = resolvePythonBin(this.options);
|
|
198
|
-
|
|
199
|
-
for (const candidate of candidates) {
|
|
200
|
-
if (!candidate) continue;
|
|
201
|
-
if (candidate.includes('/') || candidate.includes('\\')) {
|
|
202
|
-
if (fs.existsSync(candidate)) return candidate;
|
|
203
|
-
continue;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// Command-style candidate (python3/python).
|
|
207
|
-
return candidate;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
if (this.options.autoSetup) {
|
|
211
|
-
const setupResult = await setupPythonRuntime({
|
|
212
|
-
sdkHome: this.options.sdkHome,
|
|
213
|
-
venvPath: this.options.venvPath,
|
|
214
|
-
pythonBootstrap: this.options.pythonBootstrap,
|
|
215
|
-
pythonPackage: this.options.pythonPackage,
|
|
216
|
-
pythonSdkVersion: this.options.pythonSdkVersion,
|
|
217
|
-
noDeps: this.options.noDeps,
|
|
218
|
-
noBuildIsolation: this.options.noBuildIsolation,
|
|
219
|
-
upgrade: this.options.upgrade,
|
|
220
|
-
forceRecreateVenv: this.options.forceRecreateVenv,
|
|
221
|
-
});
|
|
222
|
-
return setupResult.pythonBin;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
throw new Error(
|
|
226
|
-
'No usable Python runtime found. Run `npx synap-js-sdk setup` or pass `pythonBin` in SynapClient options.'
|
|
227
|
-
);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
#preflightPythonImport(pythonBin) {
|
|
231
|
-
const envBase = {
|
|
232
|
-
...process.env,
|
|
233
|
-
...(this.options.pythonEnv || {}),
|
|
234
|
-
};
|
|
235
|
-
|
|
236
|
-
const checkImport = (envExtra = {}) => {
|
|
237
|
-
const env = { ...envBase, ...envExtra };
|
|
238
|
-
return spawnSync(
|
|
239
|
-
pythonBin,
|
|
240
|
-
['-c', 'import maximem_synap,sys; print(maximem_synap.__file__)'],
|
|
241
|
-
{ env, encoding: 'utf8' }
|
|
242
|
-
);
|
|
243
|
-
};
|
|
244
|
-
|
|
245
|
-
const noPath = checkImport();
|
|
246
|
-
if (noPath.status !== 0) {
|
|
247
|
-
const details = [
|
|
248
|
-
`Python SDK import failed.`,
|
|
249
|
-
`pythonBin=${pythonBin}`,
|
|
250
|
-
`error=${(noPath.stderr || noPath.stdout || '').trim()}`,
|
|
251
|
-
`Hint: run 'synap-js-sdk setup --sdk-version <version>' to install maximem-synap from PyPI.`,
|
|
252
|
-
].join('\n');
|
|
253
|
-
throw new Error(details);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
if (this.options.onLog) {
|
|
257
|
-
this.options.onLog('debug', 'Python SDK import OK from installed package');
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
module.exports = { BridgeManager };
|
package/src/errors.js
DELETED
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
class SynapError extends Error {
|
|
4
|
-
constructor(message, correlationId) {
|
|
5
|
-
super(message);
|
|
6
|
-
this.name = 'SynapError';
|
|
7
|
-
this.correlationId = correlationId || null;
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
class SynapTransientError extends SynapError {
|
|
12
|
-
constructor(message, correlationId) {
|
|
13
|
-
super(message, correlationId);
|
|
14
|
-
this.name = 'SynapTransientError';
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
class SynapPermanentError extends SynapError {
|
|
19
|
-
constructor(message, correlationId) {
|
|
20
|
-
super(message, correlationId);
|
|
21
|
-
this.name = 'SynapPermanentError';
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
class NetworkTimeoutError extends SynapTransientError {
|
|
26
|
-
constructor(message, correlationId) {
|
|
27
|
-
super(message, correlationId);
|
|
28
|
-
this.name = 'NetworkTimeoutError';
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
class RateLimitError extends SynapTransientError {
|
|
33
|
-
constructor(message, retryAfterSeconds, correlationId) {
|
|
34
|
-
super(message, correlationId);
|
|
35
|
-
this.name = 'RateLimitError';
|
|
36
|
-
this.retryAfterSeconds = retryAfterSeconds || null;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
class ServiceUnavailableError extends SynapTransientError {
|
|
41
|
-
constructor(message, correlationId) {
|
|
42
|
-
super(message, correlationId);
|
|
43
|
-
this.name = 'ServiceUnavailableError';
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
class AgentUnavailableError extends SynapTransientError {
|
|
48
|
-
constructor(message, correlationId) {
|
|
49
|
-
super(message, correlationId);
|
|
50
|
-
this.name = 'AgentUnavailableError';
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
class InvalidInputError extends SynapPermanentError {
|
|
55
|
-
constructor(message, correlationId) {
|
|
56
|
-
super(message, correlationId);
|
|
57
|
-
this.name = 'InvalidInputError';
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
class InvalidInstanceIdError extends InvalidInputError {
|
|
62
|
-
constructor(message, correlationId) {
|
|
63
|
-
super(message, correlationId);
|
|
64
|
-
this.name = 'InvalidInstanceIdError';
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
class InvalidConversationIdError extends InvalidInputError {
|
|
69
|
-
constructor(message, correlationId) {
|
|
70
|
-
super(message, correlationId);
|
|
71
|
-
this.name = 'InvalidConversationIdError';
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
class AuthenticationError extends SynapPermanentError {
|
|
76
|
-
constructor(message, correlationId) {
|
|
77
|
-
super(message, correlationId);
|
|
78
|
-
this.name = 'AuthenticationError';
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
class InsufficientCreditsError extends SynapPermanentError {
|
|
83
|
-
constructor(message, details, correlationId) {
|
|
84
|
-
super(message, correlationId);
|
|
85
|
-
this.name = 'InsufficientCreditsError';
|
|
86
|
-
// Structured details from the server's 402 response body:
|
|
87
|
-
// { balance_credits, minimum_required_credits, recovery_url, redeem_url }
|
|
88
|
-
this.balanceCredits = details && details.balance_credits != null
|
|
89
|
-
? Number(details.balance_credits) : null;
|
|
90
|
-
this.minimumRequiredCredits = details && details.minimum_required_credits != null
|
|
91
|
-
? Number(details.minimum_required_credits) : null;
|
|
92
|
-
this.recoveryUrl = details && details.recovery_url ? details.recovery_url : null;
|
|
93
|
-
this.redeemUrl = details && details.redeem_url ? details.redeem_url : null;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
class ContextNotFoundError extends SynapPermanentError {
|
|
98
|
-
constructor(message, correlationId) {
|
|
99
|
-
super(message, correlationId);
|
|
100
|
-
this.name = 'ContextNotFoundError';
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
class SessionExpiredError extends SynapPermanentError {
|
|
105
|
-
constructor(message, correlationId) {
|
|
106
|
-
super(message, correlationId);
|
|
107
|
-
this.name = 'SessionExpiredError';
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
class ListeningAlreadyActiveError extends SynapPermanentError {
|
|
112
|
-
constructor(message, correlationId) {
|
|
113
|
-
super(message, correlationId);
|
|
114
|
-
this.name = 'ListeningAlreadyActiveError';
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
class ListeningNotActiveError extends SynapPermanentError {
|
|
119
|
-
constructor(message, correlationId) {
|
|
120
|
-
super(message, correlationId);
|
|
121
|
-
this.name = 'ListeningNotActiveError';
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const ERROR_MAP = {
|
|
126
|
-
SynapError,
|
|
127
|
-
SynapTransientError,
|
|
128
|
-
SynapPermanentError,
|
|
129
|
-
NetworkTimeoutError,
|
|
130
|
-
RateLimitError,
|
|
131
|
-
ServiceUnavailableError,
|
|
132
|
-
AgentUnavailableError,
|
|
133
|
-
InvalidInputError,
|
|
134
|
-
InvalidInstanceIdError,
|
|
135
|
-
InvalidConversationIdError,
|
|
136
|
-
AuthenticationError,
|
|
137
|
-
InsufficientCreditsError,
|
|
138
|
-
ContextNotFoundError,
|
|
139
|
-
SessionExpiredError,
|
|
140
|
-
ListeningAlreadyActiveError,
|
|
141
|
-
ListeningNotActiveError,
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
function createSynapError(message, errorType) {
|
|
145
|
-
const ErrorClass = ERROR_MAP[errorType];
|
|
146
|
-
if (ErrorClass) return new ErrorClass(message);
|
|
147
|
-
return new SynapError(message);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
module.exports = {
|
|
151
|
-
SynapError,
|
|
152
|
-
SynapTransientError,
|
|
153
|
-
SynapPermanentError,
|
|
154
|
-
NetworkTimeoutError,
|
|
155
|
-
RateLimitError,
|
|
156
|
-
ServiceUnavailableError,
|
|
157
|
-
AgentUnavailableError,
|
|
158
|
-
InvalidInputError,
|
|
159
|
-
InvalidInstanceIdError,
|
|
160
|
-
InvalidConversationIdError,
|
|
161
|
-
AuthenticationError,
|
|
162
|
-
InsufficientCreditsError,
|
|
163
|
-
ContextNotFoundError,
|
|
164
|
-
SessionExpiredError,
|
|
165
|
-
ListeningAlreadyActiveError,
|
|
166
|
-
ListeningNotActiveError,
|
|
167
|
-
createSynapError,
|
|
168
|
-
};
|
package/src/index.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
const { SynapClient } = require('./synap-client');
|
|
2
|
-
const { setupPythonRuntime, resolveInstanceId } = require('./runtime');
|
|
3
|
-
const { setupTypeScriptExtension } = require('./setup-typescript');
|
|
4
|
-
const errors = require('./errors');
|
|
5
|
-
|
|
6
|
-
module.exports = {
|
|
7
|
-
SynapClient,
|
|
8
|
-
createClient: (options = {}) => new SynapClient(options),
|
|
9
|
-
setupPythonRuntime,
|
|
10
|
-
setupTypeScriptExtension,
|
|
11
|
-
resolveInstanceId,
|
|
12
|
-
...errors,
|
|
13
|
-
};
|
package/src/runtime.js
DELETED
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
const os = require('os');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
const { spawn } = require('child_process');
|
|
5
|
-
|
|
6
|
-
function isWindows() {
|
|
7
|
-
return process.platform === 'win32';
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
function getSdkHome(customHome) {
|
|
11
|
-
if (customHome) return customHome;
|
|
12
|
-
if (process.env.SYNAP_JS_SDK_HOME) return process.env.SYNAP_JS_SDK_HOME;
|
|
13
|
-
return path.join(os.homedir(), '.synap-js-sdk');
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function getVenvPythonPath(venvPath) {
|
|
17
|
-
if (isWindows()) return path.join(venvPath, 'Scripts', 'python.exe');
|
|
18
|
-
return path.join(venvPath, 'bin', 'python');
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function resolveBridgeScriptPath(customBridgeScriptPath) {
|
|
22
|
-
if (customBridgeScriptPath) return customBridgeScriptPath;
|
|
23
|
-
return path.join(__dirname, '..', 'bridge', 'synap_bridge.py');
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function resolvePythonBin(options = {}) {
|
|
27
|
-
const candidates = [];
|
|
28
|
-
|
|
29
|
-
if (options.pythonBin) candidates.push(options.pythonBin);
|
|
30
|
-
if (process.env.SYNAP_PYTHON_BIN) candidates.push(process.env.SYNAP_PYTHON_BIN);
|
|
31
|
-
|
|
32
|
-
const sdkHome = getSdkHome(options.sdkHome);
|
|
33
|
-
const venvPath = options.venvPath || path.join(sdkHome, '.venv');
|
|
34
|
-
candidates.push(getVenvPythonPath(venvPath));
|
|
35
|
-
|
|
36
|
-
if (isWindows()) {
|
|
37
|
-
candidates.push('python.exe');
|
|
38
|
-
candidates.push('python');
|
|
39
|
-
} else {
|
|
40
|
-
candidates.push('python3');
|
|
41
|
-
candidates.push('python');
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return candidates;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function resolveInstanceId(explicitInstanceId) {
|
|
48
|
-
if (explicitInstanceId) return explicitInstanceId;
|
|
49
|
-
if (process.env.SYNAP_INSTANCE_ID) return process.env.SYNAP_INSTANCE_ID;
|
|
50
|
-
|
|
51
|
-
try {
|
|
52
|
-
const instancesDir = path.join(os.homedir(), '.synap', 'instances');
|
|
53
|
-
if (!fs.existsSync(instancesDir)) return '';
|
|
54
|
-
|
|
55
|
-
const now = new Date();
|
|
56
|
-
let best = null;
|
|
57
|
-
|
|
58
|
-
for (const entry of fs.readdirSync(instancesDir)) {
|
|
59
|
-
const metadataPath = path.join(instancesDir, entry, 'metadata.json');
|
|
60
|
-
if (!fs.existsSync(metadataPath)) continue;
|
|
61
|
-
|
|
62
|
-
try {
|
|
63
|
-
const metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf8'));
|
|
64
|
-
const expiresAt = new Date(metadata.expires_at);
|
|
65
|
-
const issuedAt = new Date(metadata.issued_at);
|
|
66
|
-
|
|
67
|
-
if (Number.isNaN(expiresAt.getTime()) || Number.isNaN(issuedAt.getTime())) continue;
|
|
68
|
-
if (expiresAt <= now) continue;
|
|
69
|
-
|
|
70
|
-
if (!best || issuedAt > new Date(best.issued_at)) best = metadata;
|
|
71
|
-
} catch (_) {
|
|
72
|
-
// Ignore malformed metadata and continue scanning.
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return best ? best.instance_id || '' : '';
|
|
77
|
-
} catch (_) {
|
|
78
|
-
return '';
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function runCommand(command, args, opts = {}) {
|
|
83
|
-
return new Promise((resolve, reject) => {
|
|
84
|
-
const child = spawn(command, args, {
|
|
85
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
86
|
-
...opts,
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
let stdout = '';
|
|
90
|
-
let stderr = '';
|
|
91
|
-
|
|
92
|
-
child.stdout.on('data', (chunk) => {
|
|
93
|
-
stdout += chunk.toString();
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
child.stderr.on('data', (chunk) => {
|
|
97
|
-
stderr += chunk.toString();
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
child.on('error', (error) => {
|
|
101
|
-
reject(error);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
child.on('exit', (code) => {
|
|
105
|
-
if (code === 0) {
|
|
106
|
-
resolve({ code, stdout, stderr });
|
|
107
|
-
} else {
|
|
108
|
-
const err = new Error(`Command failed: ${command} ${args.join(' ')} (exit ${code})\n${stderr || stdout}`);
|
|
109
|
-
err.code = code;
|
|
110
|
-
err.stdout = stdout;
|
|
111
|
-
err.stderr = stderr;
|
|
112
|
-
reject(err);
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
async function setupPythonRuntime(options = {}) {
|
|
119
|
-
const sdkHome = getSdkHome(options.sdkHome);
|
|
120
|
-
const venvPath = options.venvPath || path.join(sdkHome, '.venv');
|
|
121
|
-
const pythonBootstrap = options.pythonBootstrap || process.env.SYNAP_PYTHON_BOOTSTRAP || (isWindows() ? 'python' : 'python3');
|
|
122
|
-
const pythonPackage = options.pythonPackage || process.env.SYNAP_PY_SDK_PACKAGE || 'maximem-synap';
|
|
123
|
-
const pythonSdkVersion = options.pythonSdkVersion || process.env.SYNAP_PY_SDK_VERSION || '';
|
|
124
|
-
const packageSpec = pythonSdkVersion ? `${pythonPackage}==${pythonSdkVersion}` : pythonPackage;
|
|
125
|
-
const pythonBin = getVenvPythonPath(venvPath);
|
|
126
|
-
|
|
127
|
-
fs.mkdirSync(sdkHome, { recursive: true });
|
|
128
|
-
|
|
129
|
-
if (!fs.existsSync(pythonBin) || options.forceRecreateVenv) {
|
|
130
|
-
await runCommand(pythonBootstrap, ['-m', 'venv', venvPath], { env: process.env });
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
await runCommand(pythonBin, ['-m', 'pip', 'install', '--upgrade', 'pip'], { env: process.env });
|
|
134
|
-
|
|
135
|
-
const installArgs = ['-m', 'pip', 'install'];
|
|
136
|
-
if (options.upgrade) installArgs.push('--upgrade');
|
|
137
|
-
if (options.noDeps) installArgs.push('--no-deps');
|
|
138
|
-
if (options.noBuildIsolation) installArgs.push('--no-build-isolation');
|
|
139
|
-
installArgs.push(packageSpec);
|
|
140
|
-
|
|
141
|
-
await runCommand(pythonBin, installArgs, { env: process.env });
|
|
142
|
-
|
|
143
|
-
return {
|
|
144
|
-
sdkHome,
|
|
145
|
-
venvPath,
|
|
146
|
-
pythonBin,
|
|
147
|
-
pythonPackage,
|
|
148
|
-
pythonSdkVersion: pythonSdkVersion || null,
|
|
149
|
-
installTarget: packageSpec,
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
module.exports = {
|
|
154
|
-
getSdkHome,
|
|
155
|
-
getVenvPythonPath,
|
|
156
|
-
resolveBridgeScriptPath,
|
|
157
|
-
resolvePythonBin,
|
|
158
|
-
resolveInstanceId,
|
|
159
|
-
setupPythonRuntime,
|
|
160
|
-
runCommand,
|
|
161
|
-
};
|