@maximem/synap-js-sdk 0.1.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/.env.example +47 -0
- package/README.md +266 -0
- package/bin/synap-js-sdk.js +124 -0
- package/bridge/synap_bridge.py +493 -0
- package/package.json +38 -0
- package/src/bridge-manager.js +262 -0
- package/src/index.js +11 -0
- package/src/runtime.js +161 -0
- package/src/setup-typescript.js +141 -0
- package/src/synap-client.js +83 -0
- package/types/index.d.ts +170 -0
package/.env.example
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# ==============================
|
|
2
|
+
# REQUIRED FOR LIVE SDK CALLS
|
|
3
|
+
# ==============================
|
|
4
|
+
# Synap instance id (from dashboard/login flow)
|
|
5
|
+
SYNAP_INSTANCE_ID=
|
|
6
|
+
|
|
7
|
+
# One-time bootstrap key/token from dashboard.
|
|
8
|
+
# Required on first initialization for a new instance/cert bootstrap.
|
|
9
|
+
# Usually not required after credentials are already stored at ~/.synap/instances/<instance_id>.
|
|
10
|
+
SYNAP_BOOTSTRAP_TOKEN=
|
|
11
|
+
# Alias accepted by JS bridge (same value as SYNAP_BOOTSTRAP_TOKEN)
|
|
12
|
+
SYNAP_BOOTSTRAP_KEY=
|
|
13
|
+
|
|
14
|
+
# Synap HTTP endpoint
|
|
15
|
+
SYNAP_BASE_URL=http://<synap-api-host>:8000
|
|
16
|
+
|
|
17
|
+
# Synap gRPC endpoint
|
|
18
|
+
SYNAP_GRPC_HOST=<synap-grpc-host>
|
|
19
|
+
SYNAP_GRPC_PORT=50051
|
|
20
|
+
SYNAP_GRPC_TLS=false
|
|
21
|
+
|
|
22
|
+
# ==============================
|
|
23
|
+
# OPTIONAL
|
|
24
|
+
# ==============================
|
|
25
|
+
# Managed runtime home used by `synap-js-sdk setup`
|
|
26
|
+
SYNAP_JS_SDK_HOME=/Users/anish/.synap-js-sdk
|
|
27
|
+
|
|
28
|
+
# Optional: override Python binary used by bridge/runtime
|
|
29
|
+
SYNAP_PYTHON_BIN=python3
|
|
30
|
+
|
|
31
|
+
# Setup/install mode for the Python SDK from package indexes
|
|
32
|
+
SYNAP_PY_SDK_PACKAGE=maximem-synap
|
|
33
|
+
SYNAP_PY_SDK_VERSION=
|
|
34
|
+
SYNAP_PYTHON_BOOTSTRAP=python3
|
|
35
|
+
|
|
36
|
+
# ==============================
|
|
37
|
+
# ADVANCED / OPTIONAL
|
|
38
|
+
# ==============================
|
|
39
|
+
# Enable verbose telemetry event tracing in Python SDK
|
|
40
|
+
SYNAP_TELEMETRY_TRACE_EVENTS=
|
|
41
|
+
|
|
42
|
+
# Optional env-credential mode keys (only if Python SDK credentials_source=\"env\")
|
|
43
|
+
SYNAP_API_KEY=
|
|
44
|
+
SYNAP_REFRESH_TOKEN=
|
|
45
|
+
SYNAP_MTLS_CERT_PATH=
|
|
46
|
+
SYNAP_MTLS_KEY_PATH=
|
|
47
|
+
SYNAP_CLIENT_ID=
|
package/README.md
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
# @maximem/synap-js-sdk
|
|
2
|
+
|
|
3
|
+
JavaScript SDK wrapper for the Synap Python SDK.
|
|
4
|
+
|
|
5
|
+
This package gives Node.js apps a normal SDK interface, while internally running a Python bridge process that executes the real `maximem_synap` SDK.
|
|
6
|
+
|
|
7
|
+
## Why this wrapper exists
|
|
8
|
+
|
|
9
|
+
The Synap Python SDK currently has the full feature surface (gRPC anticipation, ingestion lifecycle, context fetch modes, telemetry hooks). This JS SDK keeps your app code in Node while delegating memory operations to Python.
|
|
10
|
+
|
|
11
|
+
## Architecture
|
|
12
|
+
|
|
13
|
+
```text
|
|
14
|
+
Your Node app
|
|
15
|
+
-> @maximem/synap-js-sdk (JS API)
|
|
16
|
+
-> BridgeManager (JSON-RPC over stdin/stdout)
|
|
17
|
+
-> synap_bridge.py
|
|
18
|
+
-> maximem_synap (Python SDK)
|
|
19
|
+
-> Synap API + gRPC
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Flow and failure map
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
[1] App calls createClient()/client.init()
|
|
26
|
+
->
|
|
27
|
+
[2] BridgeManager resolves python path + bridge path
|
|
28
|
+
-> FAIL A: python not found / bridge file missing
|
|
29
|
+
->
|
|
30
|
+
[3] Node spawns synap_bridge.py child process
|
|
31
|
+
-> FAIL B: process spawn/runtime error
|
|
32
|
+
->
|
|
33
|
+
[4] Node sends JSON-RPC "init"
|
|
34
|
+
->
|
|
35
|
+
[5] Python imports maximem_synap + builds SDK config
|
|
36
|
+
-> FAIL C: maximem_synap not installed in chosen python/venv
|
|
37
|
+
->
|
|
38
|
+
[6] SDK initialize() + optional gRPC listen
|
|
39
|
+
-> FAIL D: invalid instance/auth OR API/gRPC endpoint unreachable
|
|
40
|
+
->
|
|
41
|
+
[7] App calls add/search/get/delete
|
|
42
|
+
->
|
|
43
|
+
[8] Node sends JSON-RPC method
|
|
44
|
+
-> FAIL E: IPC timeout / broken pipe / bridge crash
|
|
45
|
+
->
|
|
46
|
+
[9] Python executes SDK operation
|
|
47
|
+
-> FAIL F: SDK/server error, bad input, ingestion timeout
|
|
48
|
+
->
|
|
49
|
+
[10] Python returns JSON result
|
|
50
|
+
->
|
|
51
|
+
[11] Node resolves promise and returns to app
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## What this means operationally
|
|
55
|
+
|
|
56
|
+
Because this is a wrapper SDK, your deployment must include both runtimes:
|
|
57
|
+
|
|
58
|
+
1. Node.js (for your app + wrapper)
|
|
59
|
+
2. Python (for the bridge + Synap Python SDK)
|
|
60
|
+
3. A virtualenv containing `maximem-synap`
|
|
61
|
+
|
|
62
|
+
This is the main difference versus pure JS SDKs.
|
|
63
|
+
|
|
64
|
+
## Install
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npm install @maximem/synap-js-sdk
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Then set up the Python runtime used by the wrapper (JS runtime flow):
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
npx synap-js-sdk setup --sdk-version <PY_SDK_VERSION>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
If you omit `--sdk-version`, it installs the latest package configured as `maximem-synap`.
|
|
77
|
+
This wrapper installs the Python SDK from package indexes (PyPI by default), not from local source paths.
|
|
78
|
+
|
|
79
|
+
If you want TypeScript support from the start, run:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
npm install @maximem/synap-js-sdk && npx synap-js-sdk setup --sdk-version <PY_SDK_VERSION> && npx synap-js-sdk setup-ts
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Quick start in a Node project
|
|
86
|
+
|
|
87
|
+
```js
|
|
88
|
+
const { createClient } = require('@maximem/synap-js-sdk');
|
|
89
|
+
|
|
90
|
+
const synap = createClient({
|
|
91
|
+
instanceId: process.env.SYNAP_INSTANCE_ID,
|
|
92
|
+
bootstrapToken: process.env.SYNAP_BOOTSTRAP_TOKEN,
|
|
93
|
+
baseUrl: process.env.SYNAP_BASE_URL,
|
|
94
|
+
grpcHost: process.env.SYNAP_GRPC_HOST,
|
|
95
|
+
grpcPort: Number(process.env.SYNAP_GRPC_PORT || 50051),
|
|
96
|
+
grpcUseTls: process.env.SYNAP_GRPC_TLS === 'true',
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
async function run() {
|
|
100
|
+
await synap.init();
|
|
101
|
+
|
|
102
|
+
await synap.addMemory({
|
|
103
|
+
userId: 'user-123',
|
|
104
|
+
messages: [
|
|
105
|
+
{ role: 'user', content: 'My name is Alex and I live in Austin.' },
|
|
106
|
+
{ role: 'assistant', content: 'Got it.' },
|
|
107
|
+
],
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const result = await synap.searchMemory({
|
|
111
|
+
userId: 'user-123',
|
|
112
|
+
query: 'Where does the user live?',
|
|
113
|
+
maxResults: 10,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
console.log(result);
|
|
117
|
+
await synap.shutdown();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
run().catch(console.error);
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## API
|
|
124
|
+
|
|
125
|
+
### `createClient(options)` / `new SynapClient(options)`
|
|
126
|
+
|
|
127
|
+
Options:
|
|
128
|
+
|
|
129
|
+
- `instanceId`: explicit Synap instance id. If omitted, the SDK tries `SYNAP_INSTANCE_ID`, then `~/.synap/instances/*/metadata.json`.
|
|
130
|
+
- `bootstrapToken`: one-time bootstrap token/key (env: `SYNAP_BOOTSTRAP_TOKEN` or `SYNAP_BOOTSTRAP_KEY`). Required on first initialization of a new instance.
|
|
131
|
+
- `baseUrl`, `grpcHost`, `grpcPort`, `grpcUseTls`: Synap endpoints.
|
|
132
|
+
- `pythonBin`: explicit Python path for the bridge.
|
|
133
|
+
- `bridgeScriptPath`: override Python bridge script path.
|
|
134
|
+
- `sdkHome`: home for managed runtime (default `~/.synap-js-sdk`).
|
|
135
|
+
- `venvPath`: override managed venv path.
|
|
136
|
+
- `pythonPackage`: pip package name (default `maximem-synap`).
|
|
137
|
+
- `pythonSdkVersion`: pip version pin.
|
|
138
|
+
- `noDeps`: use pip `--no-deps` during auto setup.
|
|
139
|
+
- `noBuildIsolation`: use pip `--no-build-isolation` during auto setup.
|
|
140
|
+
- `upgrade`: use pip `--upgrade` during auto setup.
|
|
141
|
+
- `forceRecreateVenv`: recreate managed venv during auto setup.
|
|
142
|
+
- `autoSetup`: if `true`, runtime setup is attempted automatically when Python is missing.
|
|
143
|
+
|
|
144
|
+
### Methods
|
|
145
|
+
|
|
146
|
+
- `init()`
|
|
147
|
+
- `addMemory({ userId, messages })`
|
|
148
|
+
- `searchMemory({ userId, query, maxResults })`
|
|
149
|
+
- `getMemories({ userId })`
|
|
150
|
+
- `deleteMemory({ userId, memoryId })`
|
|
151
|
+
- `shutdown()`
|
|
152
|
+
|
|
153
|
+
## CLI
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
synap-js-sdk setup [options]
|
|
157
|
+
synap-js-sdk setup-ts [options]
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Options:
|
|
161
|
+
|
|
162
|
+
`setup`:
|
|
163
|
+
- `--python <bin>`: bootstrap Python command (default `python3`)
|
|
164
|
+
- `--sdk-home <path>`: runtime home directory
|
|
165
|
+
- `--venv <path>`: virtualenv path
|
|
166
|
+
- `--package <name>`: Python package name
|
|
167
|
+
- `--sdk-version <ver>`: Python SDK version pin
|
|
168
|
+
- `--no-deps`: install package with no dependency resolution
|
|
169
|
+
- `--no-build-isolation`: disable pip build isolation
|
|
170
|
+
- `--upgrade`: use pip `--upgrade`
|
|
171
|
+
- `--force-recreate-venv`: recreate venv
|
|
172
|
+
|
|
173
|
+
`setup-ts`:
|
|
174
|
+
- `--project-dir <path>`: target Node project (default: current working directory)
|
|
175
|
+
- `--package-manager <name>`: one of `npm`, `pnpm`, `yarn`, `bun` (auto-detected by lockfile if omitted)
|
|
176
|
+
- `--skip-install`: skip TypeScript dependency install (`typescript`, `@types/node`)
|
|
177
|
+
- `--tsconfig-path <path>`: output path for generated tsconfig (default: `tsconfig.json`)
|
|
178
|
+
- `--wrapper-path <path>`: output path for generated typed wrapper (default: `src/synap.ts`)
|
|
179
|
+
- `--no-wrapper`: do not generate typed wrapper file
|
|
180
|
+
- `--force`: overwrite generated files if they already exist
|
|
181
|
+
|
|
182
|
+
## TypeScript Extension Flow
|
|
183
|
+
|
|
184
|
+
Use this when a project already has `@maximem/synap-js-sdk` installed and wants type safety without rewriting runtime JS:
|
|
185
|
+
|
|
186
|
+
1. Run `npx synap-js-sdk setup-ts`
|
|
187
|
+
2. This installs `typescript` and `@types/node` (unless `--skip-install`)
|
|
188
|
+
3. This generates `tsconfig.json` (if missing)
|
|
189
|
+
4. This generates `src/synap.ts` typed wrapper (unless `--no-wrapper`)
|
|
190
|
+
|
|
191
|
+
Programmatic alternative:
|
|
192
|
+
|
|
193
|
+
```js
|
|
194
|
+
const { setupTypeScriptExtension } = require('@maximem/synap-js-sdk');
|
|
195
|
+
|
|
196
|
+
setupTypeScriptExtension({ projectDir: process.cwd() });
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Environment template
|
|
200
|
+
|
|
201
|
+
Use the provided template:
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
cp .env.example .env
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Template file: `.env.example`
|
|
208
|
+
|
|
209
|
+
## EC2 + Docker flow (recommended)
|
|
210
|
+
|
|
211
|
+
Build time should install Node deps and Python runtime once, not on every container start.
|
|
212
|
+
|
|
213
|
+
```dockerfile
|
|
214
|
+
FROM node:20-bullseye
|
|
215
|
+
|
|
216
|
+
RUN apt-get update && apt-get install -y python3 python3-venv && rm -rf /var/lib/apt/lists/*
|
|
217
|
+
|
|
218
|
+
WORKDIR /app
|
|
219
|
+
COPY package*.json ./
|
|
220
|
+
RUN npm ci
|
|
221
|
+
|
|
222
|
+
# Install wrapper python runtime deterministically
|
|
223
|
+
RUN npx synap-js-sdk setup --python python3 --sdk-home /opt/synap-js-sdk --sdk-version <PY_SDK_VERSION>
|
|
224
|
+
|
|
225
|
+
COPY . .
|
|
226
|
+
|
|
227
|
+
ENV SYNAP_JS_SDK_HOME=/opt/synap-js-sdk
|
|
228
|
+
ENV SYNAP_BASE_URL=http://<synap-api-host>:8000
|
|
229
|
+
ENV SYNAP_GRPC_HOST=<synap-grpc-host>
|
|
230
|
+
ENV SYNAP_GRPC_PORT=50051
|
|
231
|
+
ENV SYNAP_GRPC_TLS=false
|
|
232
|
+
|
|
233
|
+
CMD ["node", "server.js"]
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Runtime flow in container:
|
|
237
|
+
|
|
238
|
+
1. Node app starts.
|
|
239
|
+
2. First Synap call starts Python bridge process.
|
|
240
|
+
3. Bridge sends `init` to Python SDK.
|
|
241
|
+
4. API methods run through JSON-RPC.
|
|
242
|
+
5. On shutdown, wrapper sends `shutdown` and stops bridge.
|
|
243
|
+
|
|
244
|
+
## Versioning and compatibility matrix
|
|
245
|
+
|
|
246
|
+
Recommended policy:
|
|
247
|
+
|
|
248
|
+
1. Keep JS and Python SDK on the same semantic version (`x.y.z`).
|
|
249
|
+
2. Publish JS only after Python package `x.y.z` is available.
|
|
250
|
+
3. CI release pipeline should verify `pip install maximem-synap==x.y.z` before npm publish.
|
|
251
|
+
|
|
252
|
+
Even with same-version policy, keep a compatibility table in docs for rollback safety:
|
|
253
|
+
|
|
254
|
+
```text
|
|
255
|
+
JS 1.3.x -> Python 1.3.x
|
|
256
|
+
JS 1.2.x -> Python 1.2.x
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Failure modes to expect
|
|
260
|
+
|
|
261
|
+
- Python not installed or not found
|
|
262
|
+
- Bridge process crash/restart
|
|
263
|
+
- pip install failure due to network/private index
|
|
264
|
+
- auth/instance id missing or expired
|
|
265
|
+
|
|
266
|
+
Treat this SDK as a managed dual-runtime component in production.
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { setupPythonRuntime } = require('../src/runtime');
|
|
4
|
+
const { setupTypeScriptExtension } = require('../src/setup-typescript');
|
|
5
|
+
|
|
6
|
+
function parseArgs(argv) {
|
|
7
|
+
const args = { _: [] };
|
|
8
|
+
|
|
9
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
10
|
+
const token = argv[i];
|
|
11
|
+
|
|
12
|
+
if (!token.startsWith('--')) {
|
|
13
|
+
args._.push(token);
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const key = token.slice(2);
|
|
18
|
+
const next = argv[i + 1];
|
|
19
|
+
|
|
20
|
+
if (!next || next.startsWith('--')) {
|
|
21
|
+
args[key] = true;
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
args[key] = next;
|
|
26
|
+
i += 1;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return args;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function printHelp() {
|
|
33
|
+
console.log(`synap-js-sdk
|
|
34
|
+
|
|
35
|
+
Usage:
|
|
36
|
+
synap-js-sdk setup [options]
|
|
37
|
+
synap-js-sdk setup-ts [options]
|
|
38
|
+
|
|
39
|
+
setup options:
|
|
40
|
+
--python <bin> Python bootstrap binary (default: python3)
|
|
41
|
+
--sdk-home <path> SDK home (default: ~/.synap-js-sdk)
|
|
42
|
+
--venv <path> Virtualenv path (default: <sdk-home>/.venv)
|
|
43
|
+
--package <name> Python package name (default: maximem-synap)
|
|
44
|
+
--sdk-version <ver> Python SDK version to install
|
|
45
|
+
--no-deps Install without dependencies
|
|
46
|
+
--no-build-isolation Disable pip build isolation
|
|
47
|
+
--upgrade Use pip --upgrade
|
|
48
|
+
--force-recreate-venv Recreate virtualenv
|
|
49
|
+
|
|
50
|
+
setup-ts options:
|
|
51
|
+
--project-dir <path> Target Node project directory (default: cwd)
|
|
52
|
+
--package-manager <name> npm | pnpm | yarn | bun (auto-detect if omitted)
|
|
53
|
+
--skip-install Skip installing typescript and @types/node
|
|
54
|
+
--tsconfig-path <path> tsconfig output path (default: tsconfig.json)
|
|
55
|
+
--wrapper-path <path> Typed wrapper output path (default: src/synap.ts)
|
|
56
|
+
--no-wrapper Do not generate the typed wrapper file
|
|
57
|
+
--force Overwrite generated files when they already exist
|
|
58
|
+
|
|
59
|
+
global:
|
|
60
|
+
--help Show this help
|
|
61
|
+
`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function run() {
|
|
65
|
+
const args = parseArgs(process.argv.slice(2));
|
|
66
|
+
const command = args._[0];
|
|
67
|
+
|
|
68
|
+
if (!command || args.help || command === 'help') {
|
|
69
|
+
printHelp();
|
|
70
|
+
process.exit(0);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (command === 'setup') {
|
|
74
|
+
try {
|
|
75
|
+
const result = await setupPythonRuntime({
|
|
76
|
+
pythonBootstrap: args.python,
|
|
77
|
+
sdkHome: args['sdk-home'],
|
|
78
|
+
venvPath: args.venv,
|
|
79
|
+
pythonPackage: args.package,
|
|
80
|
+
pythonSdkVersion: args['sdk-version'],
|
|
81
|
+
noDeps: !!args['no-deps'],
|
|
82
|
+
noBuildIsolation: !!args['no-build-isolation'],
|
|
83
|
+
upgrade: !!args.upgrade,
|
|
84
|
+
forceRecreateVenv: !!args['force-recreate-venv'],
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
console.log('Synap JS SDK Python runtime setup complete.');
|
|
88
|
+
console.log(JSON.stringify(result, null, 2));
|
|
89
|
+
return;
|
|
90
|
+
} catch (error) {
|
|
91
|
+
console.error('Setup failed:');
|
|
92
|
+
console.error(error.message || String(error));
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (command === 'setup-ts') {
|
|
98
|
+
try {
|
|
99
|
+
const result = await setupTypeScriptExtension({
|
|
100
|
+
projectDir: args['project-dir'],
|
|
101
|
+
packageManager: args['package-manager'],
|
|
102
|
+
skipInstall: !!args['skip-install'],
|
|
103
|
+
tsconfigPath: args['tsconfig-path'],
|
|
104
|
+
wrapperPath: args['wrapper-path'],
|
|
105
|
+
noWrapper: !!args['no-wrapper'],
|
|
106
|
+
force: !!args.force,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
console.log('Synap JS SDK TypeScript extension setup complete.');
|
|
110
|
+
console.log(JSON.stringify(result, null, 2));
|
|
111
|
+
return;
|
|
112
|
+
} catch (error) {
|
|
113
|
+
console.error('TypeScript setup failed:');
|
|
114
|
+
console.error(error.message || String(error));
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
console.error(`Unknown command: ${command}`);
|
|
120
|
+
printHelp();
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
run();
|