@premai/api-sdk 1.0.47 → 1.0.49

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 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. Spawns an encrypted proxy subprocess in Anthropic-only mode
194
- 3. Fetches the available model list and lets you pick one
195
- 4. Launches `claude` wired to the confidential gateway
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 is cleaned up when Claude Code exits.
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
- confidential-proxy --compat openai &
217
- # or with npx/bunx without a global install:
218
- bunx -p @premai/api-sdk confidential-proxy --compat openai &
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
 
@@ -14,7 +14,14 @@ export type AnthropicToolUseContentBlock = {
14
14
  name: string;
15
15
  input: unknown;
16
16
  };
17
- export type AnthropicAssistantContentBlock = AnthropicTextContentBlock | AnthropicToolUseContentBlock;
17
+ export type AnthropicAssistantContentBlock = AnthropicTextContentBlock | AnthropicToolUseContentBlock | {
18
+ type: "thinking";
19
+ thinking: string;
20
+ signature: string;
21
+ } | {
22
+ type: "redacted_thinking";
23
+ data: string;
24
+ };
18
25
  export type AnthropicMessagePayload = {
19
26
  id: string;
20
27
  type: "message";
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, { model: body.model, enabled: attest2 });
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, { model: body.model, enabled: attest2 });
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, { model: body.model, enabled: attest2 });
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 = ["generateImage", "audioGenerateFromText", "createFileForUser"];
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, { signal: controller.signal });
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, { model: body.model, enabled: attest2 });
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, { model: body.model, enabled: attest2 });
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, { model: body.model, enabled: attest2 });
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 = ["generateImage", "audioGenerateFromText", "createFileForUser"];
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, { signal: controller.signal });
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
  }