@premai/api-sdk 1.0.47 → 1.0.48
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 +57 -7
- package/dist/bare.cjs +20 -5
- package/dist/bare.mjs +20 -5
- package/dist/cli-claude.mjs +419 -3008
- package/dist/cli.mjs +2767 -2203
- package/dist/core.browser.cjs +26 -6
- package/dist/core.browser.mjs +20 -5
- package/dist/core.d.ts +2 -2
- package/dist/files/index.d.ts +3 -3
- package/dist/index.cjs +455 -36
- package/dist/index.mjs +467 -37
- package/dist/launcher/proxy-subprocess.d.ts +4 -2
- package/dist/server/create-app.d.ts +1 -0
- package/dist/server/create-drain-wrapper.d.ts +5 -0
- package/dist/server/discovery.d.ts +2 -0
- package/dist/server/request-debug.d.ts +2 -0
- package/dist/server/runtime.d.ts +2 -2
- package/dist/server/shutdown-route.d.ts +6 -0
- package/dist/server/start.d.ts +7 -4
- package/dist/server.d.ts +2 -0
- package/dist/tools/index.d.ts +1 -1
- package/dist/types.d.ts +17 -1
- package/dist/utils/crypto.d.ts +1 -1
- package/dist/utils/debug.d.ts +5 -0
- package/dist/utils/dek-store.d.ts +1 -1
- package/dist/utils/poll-ready.d.ts +2 -0
- package/dist/utils/state-file.d.ts +13 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -37,6 +37,7 @@ Environment Variables:
|
|
|
37
37
|
* JSON_BODY_LIMIT (optional, default: `32mb`)
|
|
38
38
|
* HOST (optional)
|
|
39
39
|
* PORT (optional)
|
|
40
|
+
* CONFIDENTIAL_PROXY_LOG_LEVEL (optional: `error`, `warn`, `info`, `http`, `verbose`, `debug`, `silly`; default: `info`)
|
|
40
41
|
|
|
41
42
|
Run as a standalone server with automatic DEK store management per API key:
|
|
42
43
|
|
|
@@ -55,8 +56,21 @@ bun i -g @premai/api-sdk
|
|
|
55
56
|
# Server runs on http://127.0.0.1:8000
|
|
56
57
|
```
|
|
57
58
|
|
|
59
|
+
### CLI Commands
|
|
60
|
+
|
|
61
|
+
The `confidential-proxy` CLI provides four commands:
|
|
62
|
+
|
|
63
|
+
| Command | Description |
|
|
64
|
+
|---------|-------------|
|
|
65
|
+
| `confidential-proxy` (default) | Run the server in the foreground (attached to the terminal) |
|
|
66
|
+
| `confidential-proxy start` | Start the server as a background daemon |
|
|
67
|
+
| `confidential-proxy stop` | Gracefully stop the running daemon |
|
|
68
|
+
| `confidential-proxy status` | Check if the daemon is running and reachable |
|
|
69
|
+
|
|
58
70
|
### CLI Options
|
|
59
71
|
|
|
72
|
+
All commands accept the same server options:
|
|
73
|
+
|
|
60
74
|
```bash
|
|
61
75
|
# Basic usage
|
|
62
76
|
confidential-proxy --host 127.0.0.1 --port 8000
|
|
@@ -78,8 +92,34 @@ confidential-proxy --kek your-kek
|
|
|
78
92
|
|
|
79
93
|
# JSON body size limit
|
|
80
94
|
confidential-proxy --json-body-limit 64mb
|
|
95
|
+
|
|
96
|
+
# Daemon-specific options (start/stop/status)
|
|
97
|
+
confidential-proxy start --pid-file /path/to/pid --log-file /path/to/logs
|
|
98
|
+
confidential-proxy start --shutdown-timeout 60000 --log-level debug
|
|
99
|
+
confidential-proxy stop --pid-file /path/to/pid
|
|
100
|
+
confidential-proxy status --host 127.0.0.1 --port 8787
|
|
81
101
|
```
|
|
82
102
|
|
|
103
|
+
#### Daemon Lifecycle
|
|
104
|
+
|
|
105
|
+
The `start` command runs the proxy in the background:
|
|
106
|
+
|
|
107
|
+
1. Checks for an existing PID file and ensures no duplicate process is running
|
|
108
|
+
2. Spawns itself as a child process with logs directed to a configurable log file
|
|
109
|
+
3. Writes a PID file and polls the HTTP endpoint until the server is reachable
|
|
110
|
+
4. Exits cleanly, leaving the child process running as a daemon
|
|
111
|
+
|
|
112
|
+
The `stop` command sends SIGTERM and waits up to 5 seconds for graceful shutdown. If the process does not exit, it escalates to SIGKILL and cleans up the PID file. The `status` command checks both process liveness and HTTP reachability.
|
|
113
|
+
|
|
114
|
+
**Daemon options:**
|
|
115
|
+
|
|
116
|
+
| Option | Default | Description |
|
|
117
|
+
|--------|---------|-------------|
|
|
118
|
+
| `--pid-file` | `<data-dir>/proxy.pid` | Custom PID file path |
|
|
119
|
+
| `--log-file` | stdout/stderr | File to write daemon logs (when using `start`) |
|
|
120
|
+
| `--log-level` | `info` | Log verbosity (`error`, `warn`, `info`, `http`, `verbose`, `debug`, `silly`) |
|
|
121
|
+
| `--shutdown-timeout` | `30000` | Max ms to wait for in-flight requests during graceful shutdown |
|
|
122
|
+
|
|
83
123
|
### Compatibility Modes
|
|
84
124
|
|
|
85
125
|
The server supports three compatibility modes via `--compat`:
|
|
@@ -190,11 +230,12 @@ confidential-claude
|
|
|
190
230
|
The launcher:
|
|
191
231
|
|
|
192
232
|
1. Reads `.env` from application directory or environment variables (prompts interactively if missing)
|
|
193
|
-
2.
|
|
194
|
-
3.
|
|
195
|
-
4.
|
|
233
|
+
2. Checks if a proxy is already running at the configured host/port; reuses it if available
|
|
234
|
+
3. If no running proxy is found, spawns one as a child subprocess in Anthropic-only mode (port 8787)
|
|
235
|
+
4. Fetches the available model list and lets you pick one
|
|
236
|
+
5. Launches `claude` wired to the confidential gateway
|
|
196
237
|
|
|
197
|
-
All traffic remains end-to-end encrypted. The proxy subprocess
|
|
238
|
+
All traffic remains end-to-end encrypted. The proxy subprocess persists after Claude Code exits — use `confidential-proxy stop` to clean it up or `confidential-proxy start` to pre-start it before launching.
|
|
198
239
|
|
|
199
240
|
Any extra arguments are forwarded to `claude` directly:
|
|
200
241
|
|
|
@@ -213,9 +254,14 @@ To use `confidential-proxy` as a custom provider in [Hermes](https://hermes-agen
|
|
|
213
254
|
**1. Start `confidential-proxy` in the background**
|
|
214
255
|
|
|
215
256
|
```bash
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
257
|
+
# Daemon mode (runs in background, keeps a PID file)
|
|
258
|
+
confidential-proxy start --compat openai
|
|
259
|
+
|
|
260
|
+
# Check it's running
|
|
261
|
+
confidential-proxy status
|
|
262
|
+
|
|
263
|
+
# Or with npx/bunx without a global install:
|
|
264
|
+
bunx -p @premai/api-sdk confidential-proxy start --compat openai
|
|
219
265
|
```
|
|
220
266
|
|
|
221
267
|
The proxy listens on `http://localhost:8000` by default.
|
|
@@ -251,6 +297,10 @@ Hermes will now route requests through the encrypted proxy at `localhost:8000`.
|
|
|
251
297
|
- RAG Support - Encrypted document retrieval with persistent RAG DEK
|
|
252
298
|
- Attestation - Enclave attestation is always enabled for security
|
|
253
299
|
- TypeScript - Full type safety
|
|
300
|
+
- Daemon Mode - Run as a background service with PID file lifecycle management
|
|
301
|
+
- Structured Logging - Request/response logging with configurable log levels (winston)
|
|
302
|
+
- Graceful Shutdown - Drain in-flight requests before stopping (configurable timeout)
|
|
303
|
+
- CLI Lifecycle - `start`, `stop`, `status` commands for managing the proxy daemon
|
|
254
304
|
|
|
255
305
|
## Chat Completions
|
|
256
306
|
|
package/dist/bare.cjs
CHANGED
|
@@ -5507,7 +5507,10 @@ function createAudioClient(apiKey, encryptionKeys, requestTimeoutMs = DEFAULT_RE
|
|
|
5507
5507
|
const controller = new AbortController;
|
|
5508
5508
|
const timeoutId = setTimeout(() => controller.abort(), requestTimeoutMs);
|
|
5509
5509
|
try {
|
|
5510
|
-
const sessionId = await attest(apiKey, {
|
|
5510
|
+
const sessionId = await attest(apiKey, {
|
|
5511
|
+
model: body.model,
|
|
5512
|
+
enabled: attest2
|
|
5513
|
+
});
|
|
5511
5514
|
const encryptedRequest = await preprocessAudioRequest(body, encryptionKeys);
|
|
5512
5515
|
const response = await fetch(`${endpoints.proxy}/rvenc/audio/transcriptions`, {
|
|
5513
5516
|
method: "POST",
|
|
@@ -5539,7 +5542,10 @@ function createAudioClient(apiKey, encryptionKeys, requestTimeoutMs = DEFAULT_RE
|
|
|
5539
5542
|
const controller = new AbortController;
|
|
5540
5543
|
const timeoutId = setTimeout(() => controller.abort(), requestTimeoutMs);
|
|
5541
5544
|
try {
|
|
5542
|
-
const sessionId = await attest(apiKey, {
|
|
5545
|
+
const sessionId = await attest(apiKey, {
|
|
5546
|
+
model: body.model,
|
|
5547
|
+
enabled: attest2
|
|
5548
|
+
});
|
|
5543
5549
|
const encryptedRequest = await preprocessAudioTranslationRequest(body, encryptionKeys);
|
|
5544
5550
|
const response = await fetch(`${endpoints.proxy}/rvenc/audio/translations`, {
|
|
5545
5551
|
method: "POST",
|
|
@@ -25491,7 +25497,10 @@ function createRvencChatClient(apiKey, encryptionKeys, requestTimeoutMs = DEFAUL
|
|
|
25491
25497
|
const controller = new AbortController;
|
|
25492
25498
|
const timeoutId = setTimeout(() => controller.abort(), requestTimeoutMs);
|
|
25493
25499
|
try {
|
|
25494
|
-
const sessionId = await attest(apiKey, {
|
|
25500
|
+
const sessionId = await attest(apiKey, {
|
|
25501
|
+
model: body.model,
|
|
25502
|
+
enabled: attest2
|
|
25503
|
+
});
|
|
25495
25504
|
const encryptedRequest = preprocessRequest(body, encryptionKeys);
|
|
25496
25505
|
const response = await fetch(`${endpoints.proxy}/rvenc/chat/completions`, {
|
|
25497
25506
|
method: "POST",
|
|
@@ -25612,7 +25621,11 @@ async function* createDecryptedStreamGenerator(reader, sharedSecret, nonce, maxB
|
|
|
25612
25621
|
}
|
|
25613
25622
|
|
|
25614
25623
|
// src/tools/index.ts
|
|
25615
|
-
var FILE_OUTPUT_TOOLS = [
|
|
25624
|
+
var FILE_OUTPUT_TOOLS = [
|
|
25625
|
+
"generateImage",
|
|
25626
|
+
"audioGenerateFromText",
|
|
25627
|
+
"createFileForUser"
|
|
25628
|
+
];
|
|
25616
25629
|
var FILE_INPUT_TOOLS = [
|
|
25617
25630
|
"imageDescribeAndCaption",
|
|
25618
25631
|
"imageDescribeAndCaptionFallback",
|
|
@@ -25671,7 +25684,9 @@ async function downloadEncryptedFile(fileId, apiKey, timeoutMs) {
|
|
|
25671
25684
|
if (!downloadUrl) {
|
|
25672
25685
|
throw new Error("No download URL in response");
|
|
25673
25686
|
}
|
|
25674
|
-
const fileResponse = await fetch(downloadUrl, {
|
|
25687
|
+
const fileResponse = await fetch(downloadUrl, {
|
|
25688
|
+
signal: controller.signal
|
|
25689
|
+
});
|
|
25675
25690
|
if (!fileResponse.ok) {
|
|
25676
25691
|
throw new Error(`Failed to download file: ${fileResponse.status}`);
|
|
25677
25692
|
}
|
package/dist/bare.mjs
CHANGED
|
@@ -5449,7 +5449,10 @@ function createAudioClient(apiKey, encryptionKeys, requestTimeoutMs = DEFAULT_RE
|
|
|
5449
5449
|
const controller = new AbortController;
|
|
5450
5450
|
const timeoutId = setTimeout(() => controller.abort(), requestTimeoutMs);
|
|
5451
5451
|
try {
|
|
5452
|
-
const sessionId = await attest(apiKey, {
|
|
5452
|
+
const sessionId = await attest(apiKey, {
|
|
5453
|
+
model: body.model,
|
|
5454
|
+
enabled: attest2
|
|
5455
|
+
});
|
|
5453
5456
|
const encryptedRequest = await preprocessAudioRequest(body, encryptionKeys);
|
|
5454
5457
|
const response = await fetch(`${endpoints.proxy}/rvenc/audio/transcriptions`, {
|
|
5455
5458
|
method: "POST",
|
|
@@ -5481,7 +5484,10 @@ function createAudioClient(apiKey, encryptionKeys, requestTimeoutMs = DEFAULT_RE
|
|
|
5481
5484
|
const controller = new AbortController;
|
|
5482
5485
|
const timeoutId = setTimeout(() => controller.abort(), requestTimeoutMs);
|
|
5483
5486
|
try {
|
|
5484
|
-
const sessionId = await attest(apiKey, {
|
|
5487
|
+
const sessionId = await attest(apiKey, {
|
|
5488
|
+
model: body.model,
|
|
5489
|
+
enabled: attest2
|
|
5490
|
+
});
|
|
5485
5491
|
const encryptedRequest = await preprocessAudioTranslationRequest(body, encryptionKeys);
|
|
5486
5492
|
const response = await fetch(`${endpoints.proxy}/rvenc/audio/translations`, {
|
|
5487
5493
|
method: "POST",
|
|
@@ -25433,7 +25439,10 @@ function createRvencChatClient(apiKey, encryptionKeys, requestTimeoutMs = DEFAUL
|
|
|
25433
25439
|
const controller = new AbortController;
|
|
25434
25440
|
const timeoutId = setTimeout(() => controller.abort(), requestTimeoutMs);
|
|
25435
25441
|
try {
|
|
25436
|
-
const sessionId = await attest(apiKey, {
|
|
25442
|
+
const sessionId = await attest(apiKey, {
|
|
25443
|
+
model: body.model,
|
|
25444
|
+
enabled: attest2
|
|
25445
|
+
});
|
|
25437
25446
|
const encryptedRequest = preprocessRequest(body, encryptionKeys);
|
|
25438
25447
|
const response = await fetch(`${endpoints.proxy}/rvenc/chat/completions`, {
|
|
25439
25448
|
method: "POST",
|
|
@@ -25554,7 +25563,11 @@ async function* createDecryptedStreamGenerator(reader, sharedSecret, nonce, maxB
|
|
|
25554
25563
|
}
|
|
25555
25564
|
|
|
25556
25565
|
// src/tools/index.ts
|
|
25557
|
-
var FILE_OUTPUT_TOOLS = [
|
|
25566
|
+
var FILE_OUTPUT_TOOLS = [
|
|
25567
|
+
"generateImage",
|
|
25568
|
+
"audioGenerateFromText",
|
|
25569
|
+
"createFileForUser"
|
|
25570
|
+
];
|
|
25558
25571
|
var FILE_INPUT_TOOLS = [
|
|
25559
25572
|
"imageDescribeAndCaption",
|
|
25560
25573
|
"imageDescribeAndCaptionFallback",
|
|
@@ -25613,7 +25626,9 @@ async function downloadEncryptedFile(fileId, apiKey, timeoutMs) {
|
|
|
25613
25626
|
if (!downloadUrl) {
|
|
25614
25627
|
throw new Error("No download URL in response");
|
|
25615
25628
|
}
|
|
25616
|
-
const fileResponse = await fetch(downloadUrl, {
|
|
25629
|
+
const fileResponse = await fetch(downloadUrl, {
|
|
25630
|
+
signal: controller.signal
|
|
25631
|
+
});
|
|
25617
25632
|
if (!fileResponse.ok) {
|
|
25618
25633
|
throw new Error(`Failed to download file: ${fileResponse.status}`);
|
|
25619
25634
|
}
|