@vellumai/credential-executor 0.10.7 → 0.10.8-dev.202607102228.5945895

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.
Files changed (65) hide show
  1. package/Dockerfile +1 -1
  2. package/node_modules/@vellumai/service-contracts/package.json +1 -2
  3. package/node_modules/@vellumai/service-contracts/src/__tests__/attachment-naming.test.ts +104 -0
  4. package/node_modules/@vellumai/service-contracts/src/__tests__/contracts.test.ts +0 -2
  5. package/node_modules/@vellumai/service-contracts/src/attachment-naming.ts +118 -0
  6. package/node_modules/@vellumai/service-contracts/src/credential-rpc.ts +3 -5
  7. package/node_modules/@vellumai/service-contracts/src/index.ts +2 -4
  8. package/node_modules/@vellumai/service-contracts/src/rpc.ts +4 -447
  9. package/package.json +2 -3
  10. package/src/__tests__/bulk-set-credentials.test.ts +1 -1
  11. package/src/__tests__/local-standalone.test.ts +5 -36
  12. package/src/__tests__/managed-integration.test.ts +112 -91
  13. package/src/__tests__/managed-reconnect.test.ts +2 -2
  14. package/src/__tests__/transport.test.ts +23 -27
  15. package/src/cli.ts +1 -1
  16. package/src/index.ts +8 -88
  17. package/src/main.ts +228 -340
  18. package/src/paths.ts +4 -20
  19. package/src/server.ts +52 -469
  20. package/node_modules/@vellumai/service-contracts/src/__tests__/grants.test.ts +0 -686
  21. package/node_modules/@vellumai/service-contracts/src/grants.ts +0 -184
  22. package/node_modules/@vellumai/service-contracts/src/rendering.ts +0 -135
  23. package/src/__tests__/command-executor.test.ts +0 -1879
  24. package/src/__tests__/command-validator.test.ts +0 -1405
  25. package/src/__tests__/command-workspace.test.ts +0 -1050
  26. package/src/__tests__/grant-store.test.ts +0 -689
  27. package/src/__tests__/http-executor.test.ts +0 -1336
  28. package/src/__tests__/http-policy.test.ts +0 -1069
  29. package/src/__tests__/local-materializers.test.ts +0 -860
  30. package/src/__tests__/local-token-refresh.test.ts +0 -361
  31. package/src/__tests__/manage-secure-command-tool.test.ts +0 -134
  32. package/src/__tests__/managed-lazy-getters.test.ts +0 -359
  33. package/src/__tests__/managed-materializers.test.ts +0 -1028
  34. package/src/__tests__/managed-rejection.test.ts +0 -43
  35. package/src/__tests__/toolstore.test.ts +0 -773
  36. package/src/audit/store.ts +0 -188
  37. package/src/commands/auth-adapters.ts +0 -169
  38. package/src/commands/egress-hooks.ts +0 -203
  39. package/src/commands/executor.ts +0 -1155
  40. package/src/commands/output-scan.ts +0 -157
  41. package/src/commands/profiles.ts +0 -286
  42. package/src/commands/validator.ts +0 -702
  43. package/src/commands/workspace.ts +0 -550
  44. package/src/grants/index.ts +0 -17
  45. package/src/grants/persistent-store.ts +0 -309
  46. package/src/grants/rpc-handlers.ts +0 -293
  47. package/src/grants/temporary-store.ts +0 -289
  48. package/src/http/audit.ts +0 -84
  49. package/src/http/executor.ts +0 -684
  50. package/src/http/path-template.ts +0 -245
  51. package/src/http/policy.ts +0 -238
  52. package/src/http/response-filter.ts +0 -233
  53. package/src/managed-errors.ts +0 -9
  54. package/src/managed-lazy-getters.ts +0 -106
  55. package/src/managed-main.ts +0 -822
  56. package/src/materializers/local-oauth-lookup.ts +0 -98
  57. package/src/materializers/local-token-refresh.ts +0 -287
  58. package/src/materializers/local.ts +0 -316
  59. package/src/materializers/managed-platform.ts +0 -295
  60. package/src/subjects/local.ts +0 -177
  61. package/src/subjects/managed.ts +0 -311
  62. package/src/subjects/policy.ts +0 -79
  63. package/src/toolstore/integrity.ts +0 -94
  64. package/src/toolstore/manifest.ts +0 -154
  65. package/src/toolstore/publish.ts +0 -571
@@ -21,14 +21,17 @@ import { afterEach, describe, expect, test } from "bun:test";
21
21
  import { mkdirSync, mkdtempSync, rmSync, unlinkSync } from "node:fs";
22
22
  import { tmpdir } from "node:os";
23
23
  import { join } from "node:path";
24
- import { createConnection, createServer as createNetServer, type Socket } from "node:net";
24
+ import {
25
+ createConnection,
26
+ createServer as createNetServer,
27
+ type Socket,
28
+ } from "node:net";
25
29
  import { Readable, Writable } from "node:stream";
26
30
 
27
31
  import {
28
32
  CES_PROTOCOL_VERSION,
29
33
  CesRpcMethod,
30
34
  type HandshakeAck,
31
- type ListGrantsResponse,
32
35
  type GetCredentialResponse,
33
36
  type SetCredentialResponse,
34
37
  type DeleteCredentialResponse,
@@ -36,13 +39,11 @@ import {
36
39
  type RpcEnvelope,
37
40
  } from "@vellumai/service-contracts/credential-rpc";
38
41
 
39
- import { PersistentGrantStore } from "../grants/persistent-store.js";
40
- import { AuditStore } from "../audit/store.js";
41
42
  import {
42
- createListGrantsHandler,
43
- createListAuditRecordsHandler,
44
- } from "../grants/rpc-handlers.js";
45
- import { CesRpcServer, type RpcHandlerRegistry, type ServeEndReason } from "../server.js";
43
+ CesRpcServer,
44
+ type RpcHandlerRegistry,
45
+ type ServeEndReason,
46
+ } from "../server.js";
46
47
  import { createLocalSecureKeyBackend } from "../materializers/local-secure-key-backend.js";
47
48
 
48
49
  // ---------------------------------------------------------------------------
@@ -83,40 +84,11 @@ function restoreEnv(saved: SavedEnv): void {
83
84
  // Helpers
84
85
  // ---------------------------------------------------------------------------
85
86
 
86
- /**
87
- * Build a minimal RPC handler registry with `list_grants` and
88
- * `list_audit_records` backed by real stores in a temp directory.
89
- */
90
- function buildMinimalHandlers(dataDir: string): RpcHandlerRegistry {
91
- const grantsDir = join(dataDir, "grants");
92
- const auditDir = join(dataDir, "audit");
93
- mkdirSync(grantsDir, { recursive: true });
94
- mkdirSync(auditDir, { recursive: true });
95
-
96
- const persistentGrantStore = new PersistentGrantStore(grantsDir);
97
- persistentGrantStore.init();
98
-
99
- const auditStore = new AuditStore(auditDir);
100
- auditStore.init();
101
-
102
- const handlers: RpcHandlerRegistry = {};
103
-
104
- handlers[CesRpcMethod.ListGrants] = createListGrantsHandler({
105
- persistentGrantStore,
106
- }) as typeof handlers[string];
107
-
108
- handlers[CesRpcMethod.ListAuditRecords] = createListAuditRecordsHandler({
109
- auditStore,
110
- }) as typeof handlers[string];
111
-
112
- return handlers;
113
- }
114
-
115
87
  /**
116
88
  * Build an RPC handler registry with credential CRUD handlers backed by
117
89
  * a real SecureKeyBackend using a temp directory for credential storage.
118
90
  *
119
- * Mirrors the handler registration in managed-main.ts.
91
+ * Mirrors the handler registration in main.ts.
120
92
  */
121
93
  function buildCredentialHandlers(vellumRoot: string): RpcHandlerRegistry {
122
94
  const secureKeyBackend = createLocalSecureKeyBackend(vellumRoot);
@@ -125,22 +97,27 @@ function buildCredentialHandlers(vellumRoot: string): RpcHandlerRegistry {
125
97
  handlers[CesRpcMethod.GetCredential] = (async (req: { account: string }) => {
126
98
  const value = await secureKeyBackend.get(req.account);
127
99
  return { found: value !== undefined, value };
128
- }) as typeof handlers[string];
100
+ }) as (typeof handlers)[string];
129
101
 
130
- handlers[CesRpcMethod.SetCredential] = (async (req: { account: string; value: string }) => {
102
+ handlers[CesRpcMethod.SetCredential] = (async (req: {
103
+ account: string;
104
+ value: string;
105
+ }) => {
131
106
  const ok = await secureKeyBackend.set(req.account, req.value);
132
107
  return { ok };
133
- }) as typeof handlers[string];
108
+ }) as (typeof handlers)[string];
134
109
 
135
- handlers[CesRpcMethod.DeleteCredential] = (async (req: { account: string }) => {
110
+ handlers[CesRpcMethod.DeleteCredential] = (async (req: {
111
+ account: string;
112
+ }) => {
136
113
  const result = await secureKeyBackend.delete(req.account);
137
114
  return { result };
138
- }) as typeof handlers[string];
115
+ }) as (typeof handlers)[string];
139
116
 
140
117
  handlers[CesRpcMethod.ListCredentials] = (async () => {
141
118
  const accounts = await secureKeyBackend.list();
142
119
  return { accounts };
143
- }) as typeof handlers[string];
120
+ }) as (typeof handlers)[string];
144
121
 
145
122
  return handlers;
146
123
  }
@@ -149,10 +126,13 @@ function buildCredentialHandlers(vellumRoot: string): RpcHandlerRegistry {
149
126
  * Accept a single connection on a Unix socket and return
150
127
  * readable/writable streams plus cleanup helpers.
151
128
  *
152
- * Replicates the same accept-one-connection pattern from managed-main.ts
129
+ * Replicates the same accept-one-connection pattern from main.ts
153
130
  * but in a test-friendly form.
154
131
  */
155
- function acceptOneConnection(socketPath: string, signal: AbortSignal): Promise<{
132
+ function acceptOneConnection(
133
+ socketPath: string,
134
+ signal: AbortSignal,
135
+ ): Promise<{
156
136
  readable: Readable;
157
137
  writable: Writable;
158
138
  socket: Socket;
@@ -162,7 +142,11 @@ function acceptOneConnection(socketPath: string, signal: AbortSignal): Promise<{
162
142
 
163
143
  const cleanup = () => {
164
144
  netServer.close();
165
- try { unlinkSync(socketPath); } catch { /* ok */ }
145
+ try {
146
+ unlinkSync(socketPath);
147
+ } catch {
148
+ /* ok */
149
+ }
166
150
  };
167
151
 
168
152
  if (signal.aborted) {
@@ -170,10 +154,14 @@ function acceptOneConnection(socketPath: string, signal: AbortSignal): Promise<{
170
154
  return;
171
155
  }
172
156
 
173
- signal.addEventListener("abort", () => {
174
- cleanup();
175
- reject(new Error("Aborted while waiting for connection"));
176
- }, { once: true });
157
+ signal.addEventListener(
158
+ "abort",
159
+ () => {
160
+ cleanup();
161
+ reject(new Error("Aborted while waiting for connection"));
162
+ },
163
+ { once: true },
164
+ );
177
165
 
178
166
  netServer.on("error", (err: Error) => {
179
167
  cleanup();
@@ -186,11 +174,19 @@ function acceptOneConnection(socketPath: string, signal: AbortSignal): Promise<{
186
174
 
187
175
  netServer.on("connection", (sock: Socket) => {
188
176
  netServer.close();
189
- try { unlinkSync(socketPath); } catch { /* ok */ }
177
+ try {
178
+ unlinkSync(socketPath);
179
+ } catch {
180
+ /* ok */
181
+ }
190
182
 
191
183
  const readable = new Readable({ read() {} });
192
184
  const writable = new Writable({
193
- write(chunk: Buffer, _encoding: string, callback: (err?: Error | null) => void) {
185
+ write(
186
+ chunk: Buffer,
187
+ _encoding: string,
188
+ callback: (err?: Error | null) => void,
189
+ ) {
194
190
  if (sock.writable) {
195
191
  sock.write(chunk, callback);
196
192
  } else {
@@ -256,7 +252,11 @@ function connectToSocket(
256
252
  * Read newline-delimited JSON messages from a socket, collecting them
257
253
  * until the expected count is reached or a timeout fires.
258
254
  */
259
- function readMessages(sock: Socket, expectedCount: number, timeoutMs = 3000): Promise<unknown[]> {
255
+ function readMessages(
256
+ sock: Socket,
257
+ expectedCount: number,
258
+ timeoutMs = 3000,
259
+ ): Promise<unknown[]> {
260
260
  return new Promise((resolve, reject) => {
261
261
  const messages: unknown[] = [];
262
262
  let buffer = "";
@@ -329,7 +329,9 @@ afterEach(async () => {
329
329
  if (tmpDir) {
330
330
  try {
331
331
  rmSync(tmpDir, { recursive: true, force: true });
332
- } catch { /* ok */ }
332
+ } catch {
333
+ /* ok */
334
+ }
333
335
  }
334
336
  });
335
337
 
@@ -367,7 +369,7 @@ describe("managed CES integration (real Unix socket)", () => {
367
369
 
368
370
  // -- Start health server ---------------------------------------------------
369
371
  // NOTE: This uses a local Bun.serve mock rather than the production
370
- // `startHealthServer()` from managed-main.ts. The production function is
372
+ // `startHealthServer()` from main.ts. The production function is
371
373
  // not exported and depends on module-level mutable state (the
372
374
  // `rpcConnected` flag) that cannot be controlled from tests.
373
375
  //
@@ -377,7 +379,7 @@ describe("managed CES integration (real Unix socket)", () => {
377
379
  // - End-to-end socket + RPC + health plumbing in one integration flow.
378
380
  //
379
381
  // What this does NOT cover:
380
- // - The actual `startHealthServer()` implementation in managed-main.ts,
382
+ // - The actual `startHealthServer()` implementation in main.ts,
381
383
  // including the `rpcConnected` field in the /readyz response. If the
382
384
  // production health handler drifts (changes routes, status codes, or
383
385
  // response shape), this test will not catch it. To close that gap,
@@ -388,10 +390,9 @@ describe("managed CES integration (real Unix socket)", () => {
388
390
  fetch(req) {
389
391
  const url = new URL(req.url);
390
392
  if (url.pathname === "/healthz") {
391
- return new Response(
392
- JSON.stringify({ status: "ok" }),
393
- { headers: { "Content-Type": "application/json" } },
394
- );
393
+ return new Response(JSON.stringify({ status: "ok" }), {
394
+ headers: { "Content-Type": "application/json" },
395
+ });
395
396
  }
396
397
  if (url.pathname === "/readyz") {
397
398
  return new Response(
@@ -404,7 +405,10 @@ describe("managed CES integration (real Unix socket)", () => {
404
405
  });
405
406
 
406
407
  // -- Start accept-one-connection on the Unix socket ------------------------
407
- const connectionPromise = acceptOneConnection(socketPath, controller.signal);
408
+ const connectionPromise = acceptOneConnection(
409
+ socketPath,
410
+ controller.signal,
411
+ );
408
412
 
409
413
  // -- Client connects -------------------------------------------------------
410
414
  clientSocket = await connectToSocket(socketPath);
@@ -413,7 +417,7 @@ describe("managed CES integration (real Unix socket)", () => {
413
417
  const conn = await connectionPromise;
414
418
 
415
419
  let observedSessionId = "";
416
- const handlers = buildMinimalHandlers(dataDir);
420
+ const handlers = buildCredentialHandlers(dataDir);
417
421
 
418
422
  serverRpcServer = new CesRpcServer({
419
423
  input: conn.readable,
@@ -454,13 +458,13 @@ describe("managed CES integration (real Unix socket)", () => {
454
458
  expect(observedSessionId).toBe(handshakeSessionId);
455
459
  expect(serverRpcServer.currentSessionId).toBe(handshakeSessionId);
456
460
 
457
- // -- Step 2: RPC dispatch (list_grants) ------------------------------------
461
+ // -- Step 2: RPC dispatch (list_credentials) -------------------------------
458
462
  const rpcId = "rpc-1";
459
463
  sendMessage(clientSocket, {
460
464
  type: "rpc",
461
465
  id: rpcId,
462
466
  kind: "request",
463
- method: CesRpcMethod.ListGrants,
467
+ method: CesRpcMethod.ListCredentials,
464
468
  payload: {},
465
469
  timestamp: new Date().toISOString(),
466
470
  });
@@ -472,34 +476,33 @@ describe("managed CES integration (real Unix socket)", () => {
472
476
  expect(rpcResp.type).toBe("rpc");
473
477
  expect(rpcResp.id).toBe(rpcId);
474
478
  expect(rpcResp.kind).toBe("response");
475
- expect(rpcResp.method).toBe(CesRpcMethod.ListGrants);
479
+ expect(rpcResp.method).toBe(CesRpcMethod.ListCredentials);
476
480
 
477
- const grantsPayload = rpcResp.payload as ListGrantsResponse;
478
- expect(grantsPayload.grants).toEqual([]);
481
+ const listPayload = rpcResp.payload as { accounts: string[] };
482
+ expect(listPayload.accounts).toEqual([]);
479
483
 
480
- // -- Step 3: RPC dispatch (list_audit_records) -----------------------------
481
- const auditRpcId = "rpc-2";
484
+ // -- Step 3: RPC dispatch (get_credential, not found) ----------------------
485
+ const getRpcId = "rpc-2";
482
486
  sendMessage(clientSocket, {
483
487
  type: "rpc",
484
- id: auditRpcId,
488
+ id: getRpcId,
485
489
  kind: "request",
486
- method: CesRpcMethod.ListAuditRecords,
487
- payload: {},
490
+ method: CesRpcMethod.GetCredential,
491
+ payload: { account: "does-not-exist" },
488
492
  timestamp: new Date().toISOString(),
489
493
  });
490
494
 
491
- const auditMessages = await readMessages(clientSocket, 1);
492
- expect(auditMessages.length).toBe(1);
495
+ const getMessages = await readMessages(clientSocket, 1);
496
+ expect(getMessages.length).toBe(1);
493
497
 
494
- const auditResp = auditMessages[0] as RpcEnvelope & { type: "rpc" };
495
- expect(auditResp.type).toBe("rpc");
496
- expect(auditResp.id).toBe(auditRpcId);
497
- expect(auditResp.kind).toBe("response");
498
- expect(auditResp.method).toBe(CesRpcMethod.ListAuditRecords);
498
+ const getResp = getMessages[0] as RpcEnvelope & { type: "rpc" };
499
+ expect(getResp.type).toBe("rpc");
500
+ expect(getResp.id).toBe(getRpcId);
501
+ expect(getResp.kind).toBe("response");
502
+ expect(getResp.method).toBe(CesRpcMethod.GetCredential);
499
503
 
500
- const auditPayload = auditResp.payload as { records: unknown[]; nextCursor: string | null };
501
- expect(auditPayload.records).toEqual([]);
502
- expect(auditPayload.nextCursor).toBeNull();
504
+ const getPayload = getResp.payload as { found: boolean };
505
+ expect(getPayload.found).toBe(false);
503
506
 
504
507
  // -- Step 4: Health endpoint -----------------------------------------------
505
508
  const healthzResp = await fetch(`http://localhost:${healthPort}/healthz`);
@@ -529,7 +532,10 @@ describe("managed CES integration (real Unix socket)", () => {
529
532
  const unknownResp = unknownMessages[0] as RpcEnvelope & { type: "rpc" };
530
533
  expect(unknownResp.id).toBe(unknownRpcId);
531
534
  expect(unknownResp.kind).toBe("response");
532
- const unknownPayload = unknownResp.payload as { success: boolean; error: { code: string } };
535
+ const unknownPayload = unknownResp.payload as {
536
+ success: boolean;
537
+ error: { code: string };
538
+ };
533
539
  expect(unknownPayload.error.code).toBe("METHOD_NOT_FOUND");
534
540
 
535
541
  // -- Cleanup ---------------------------------------------------------------
@@ -552,7 +558,10 @@ describe("managed CES integration (real Unix socket)", () => {
552
558
 
553
559
  controller = new AbortController();
554
560
 
555
- const connectionPromise = acceptOneConnection(socketPath, controller.signal);
561
+ const connectionPromise = acceptOneConnection(
562
+ socketPath,
563
+ controller.signal,
564
+ );
556
565
  clientSocket = await connectToSocket(socketPath);
557
566
  const conn = await connectionPromise;
558
567
 
@@ -601,11 +610,14 @@ describe("managed CES integration (real Unix socket)", () => {
601
610
 
602
611
  controller = new AbortController();
603
612
 
604
- const connectionPromise = acceptOneConnection(socketPath, controller.signal);
613
+ const connectionPromise = acceptOneConnection(
614
+ socketPath,
615
+ controller.signal,
616
+ );
605
617
  clientSocket = await connectToSocket(socketPath);
606
618
  const conn = await connectionPromise;
607
619
 
608
- const handlers = buildMinimalHandlers(dataDir);
620
+ const handlers = buildCredentialHandlers(dataDir);
609
621
  serverRpcServer = new CesRpcServer({
610
622
  input: conn.readable,
611
623
  output: conn.writable,
@@ -621,7 +633,7 @@ describe("managed CES integration (real Unix socket)", () => {
621
633
  type: "rpc",
622
634
  id: "pre-hs-1",
623
635
  kind: "request",
624
- method: CesRpcMethod.ListGrants,
636
+ method: CesRpcMethod.ListCredentials,
625
637
  payload: {},
626
638
  timestamp: new Date().toISOString(),
627
639
  });
@@ -632,7 +644,10 @@ describe("managed CES integration (real Unix socket)", () => {
632
644
  const resp = messages[0] as RpcEnvelope & { type: "rpc" };
633
645
  expect(resp.id).toBe("pre-hs-1");
634
646
  expect(resp.kind).toBe("response");
635
- const payload = resp.payload as { success: boolean; error: { code: string } };
647
+ const payload = resp.payload as {
648
+ success: boolean;
649
+ error: { code: string };
650
+ };
636
651
  expect(payload.error.code).toBe("HANDSHAKE_REQUIRED");
637
652
 
638
653
  clientSocket.end();
@@ -649,7 +664,10 @@ describe("managed CES integration (real Unix socket)", () => {
649
664
 
650
665
  controller = new AbortController();
651
666
 
652
- const connectionPromise = acceptOneConnection(socketPath, controller.signal);
667
+ const connectionPromise = acceptOneConnection(
668
+ socketPath,
669
+ controller.signal,
670
+ );
653
671
 
654
672
  // First connection succeeds
655
673
  clientSocket = await connectToSocket(socketPath);
@@ -700,7 +718,10 @@ describe("credential CRUD RPC", () => {
700
718
 
701
719
  controller = new AbortController();
702
720
 
703
- const connectionPromise = acceptOneConnection(socketPath, controller.signal);
721
+ const connectionPromise = acceptOneConnection(
722
+ socketPath,
723
+ controller.signal,
724
+ );
704
725
  clientSocket = await connectToSocket(socketPath);
705
726
  const conn = await connectionPromise;
706
727
 
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Managed CES reconnection test (real entrypoint subprocess).
3
3
  *
4
- * Spawns the actual `managed-main.ts` entrypoint and verifies that the CES
4
+ * Spawns the actual `main.ts` entrypoint and verifies that the CES
5
5
  * sidecar survives the assistant disconnecting and accepts a reconnection,
6
6
  * rather than shutting down when the RPC stream ends.
7
7
  *
@@ -179,7 +179,7 @@ describe("managed CES reconnection (real entrypoint)", () => {
179
179
  mkdirSync(join(assistantDataMount, ".vellum"), { recursive: true });
180
180
 
181
181
  const healthPort = await pickFreePort();
182
- const managedMain = resolve(__dirname, "..", "managed-main.ts");
182
+ const managedMain = resolve(__dirname, "..", "main.ts");
183
183
 
184
184
  proc = Bun.spawn({
185
185
  cmd: [process.execPath, managedMain],
@@ -125,39 +125,28 @@ function createTestServer(handlers: RpcHandlerRegistry = {}) {
125
125
  // ---------------------------------------------------------------------------
126
126
 
127
127
  describe("managed entrypoint transport isolation", () => {
128
- test("managed-main.ts does not call Bun.serve for RPC (only health)", () => {
128
+ test("main.ts uses Bun.serve only for health probes (managed mode)", () => {
129
129
  const src = readFileSync(
130
- resolve(__dirname, "..", "managed-main.ts"),
130
+ resolve(__dirname, "..", "main.ts"),
131
131
  "utf-8",
132
132
  );
133
- // Bun.serve calls in managed-main should only be in startHealthServer
133
+ // Bun.serve is used for the health server in managed mode
134
134
  const bunServeMatches = src.match(/Bun\.serve\(/g);
135
135
  expect(bunServeMatches).not.toBeNull();
136
136
  // There should be exactly 1 Bun.serve call — for health probes only
137
137
  expect(bunServeMatches!.length).toBe(1);
138
138
  });
139
139
 
140
- test("managed-main.ts uses createNetServer for Unix socket, not Bun.serve", () => {
140
+ test("main.ts uses createNetServer for Unix socket transport", () => {
141
141
  const src = readFileSync(
142
- resolve(__dirname, "..", "managed-main.ts"),
142
+ resolve(__dirname, "..", "main.ts"),
143
143
  "utf-8",
144
144
  );
145
- // Uses node:net createServer for the bootstrap socket
145
+ // Uses node:net createServer for the socket transport (both modes)
146
146
  expect(src).toMatch(/createNetServer/);
147
147
  // The net server never listens on a TCP port (only a socket path)
148
148
  expect(src).not.toMatch(/netServer\.listen\(\d+/);
149
149
  });
150
-
151
- test("managed-main.ts unlinks socket after accepting one connection", () => {
152
- const src = readFileSync(
153
- resolve(__dirname, "..", "managed-main.ts"),
154
- "utf-8",
155
- );
156
- // Should unlink the socket path after connection
157
- expect(src).toMatch(/unlinkSync\(socketPath\)/);
158
- // Should close the net server after one connection
159
- expect(src).toMatch(/netServer\.close\(\)/);
160
- });
161
150
  });
162
151
 
163
152
  // ---------------------------------------------------------------------------
@@ -165,9 +154,9 @@ describe("managed entrypoint transport isolation", () => {
165
154
  // ---------------------------------------------------------------------------
166
155
 
167
156
  describe("health probes", () => {
168
- test("managed-main.ts serves /healthz and /readyz on a separate port", () => {
157
+ test("main.ts serves /healthz and /readyz in managed mode", () => {
169
158
  const src = readFileSync(
170
- resolve(__dirname, "..", "managed-main.ts"),
159
+ resolve(__dirname, "..", "main.ts"),
171
160
  "utf-8",
172
161
  );
173
162
  expect(src).toMatch(/\/healthz/);
@@ -203,18 +192,25 @@ describe("health probes", () => {
203
192
  });
204
193
 
205
194
  // ---------------------------------------------------------------------------
206
- // 3. Local stdio transport not inherited by subprocesses
195
+ // 3. Local socket transport not inherited by subprocesses
207
196
  // ---------------------------------------------------------------------------
208
197
 
209
198
  describe("local entrypoint transport isolation", () => {
210
- test("main.ts serves over stdio or a Unix socket, never a TCP listener", () => {
199
+ test("main.ts serves over a Unix socket, never stdio or a TCP listener", () => {
211
200
  const src = readFileSync(resolve(__dirname, "..", "main.ts"), "utf-8");
212
- // Serves the stdio-child transport (default mode).
213
- expect(src).toMatch(/process\.stdin/);
214
- expect(src).toMatch(/process\.stdout/);
215
- // Standalone mode (CES_STANDALONE=1) listens on a Unix socket path only —
216
- // never a numeric TCP port and never opens an HTTP server.
217
- expect(src).not.toMatch(/Bun\.serve\(/);
201
+ // Socket transport only — no stdio-child transport.
202
+ expect(src).not.toMatch(/process\.stdin/);
203
+ expect(src).not.toMatch(/process\.stdout/);
204
+ // Bun.serve is only used inside the managed-mode health server block
205
+ // (startHealthServer), never for RPC transport. The RPC transport uses
206
+ // node:net createServer listening on a Unix socket path.
207
+ const bunServeMatches = src.match(/Bun\.serve\(/g);
208
+ expect(bunServeMatches).not.toBeNull();
209
+ expect(bunServeMatches!.length).toBe(1);
210
+ // The Bun.serve call must be inside the startHealthServer function, which
211
+ // is only invoked in managed mode.
212
+ expect(src).toMatch(/function startHealthServer/);
213
+ // Never listens on a numeric TCP port for RPC transport.
218
214
  expect(src).not.toMatch(/\.listen\(\d+/);
219
215
  });
220
216
 
package/src/cli.ts CHANGED
@@ -25,7 +25,7 @@ import { homedir } from "node:os";
25
25
  import { createLocalSecureKeyBackend } from "./materializers/local-secure-key-backend.js";
26
26
 
27
27
  // ---------------------------------------------------------------------------
28
- // Path resolution (mirrors managed-main.ts)
28
+ // Path resolution (mirrors the managed-mode path in main.ts)
29
29
  // ---------------------------------------------------------------------------
30
30
 
31
31
  function resolveVellumRoot(): string {
package/src/index.ts CHANGED
@@ -2,108 +2,28 @@
2
2
  /**
3
3
  * @vellumai/credential-executor
4
4
  *
5
- * Credential Execution Service (CES) — an isolated runtime that executes
6
- * credential-bearing tool operations on behalf of untrusted agents. The CES
7
- * receives RPC requests from the assistant daemon, materialises credentials
8
- * from the local credential store, executes the requested operation through
9
- * the egress proxy, and returns sanitised results.
5
+ * Credential Execution Service (CES) — an isolated runtime that stores and
6
+ * serves credentials on behalf of the assistant daemon over RPC. The CES owns
7
+ * the credential store and exposes credential CRUD operations (get, set,
8
+ * delete, list, bulk-set) plus the managed-credential update handshake.
10
9
  *
11
- * This module re-exports the public API surface. For entrypoints see:
12
- * - `main.ts` — local mode (stdio transport, child process)
13
- * - `managed-main.ts` managed mode (Unix socket transport, sidecar)
10
+ * This module re-exports the public API surface. For the entrypoint see
11
+ * `main.ts` — unified for both local (bare-metal sibling) and managed
12
+ * (Kubernetes sidecar) modes, selected by `getCesMode()`.
14
13
  */
15
14
 
16
- export {
17
- CesRpcServer,
18
- createCesServer,
19
- createRunAuthenticatedCommandHandler,
20
- registerCommandExecutionHandler,
21
- createMakeAuthenticatedRequestHandler,
22
- createManageSecureCommandToolHandler,
23
- registerManageSecureCommandToolHandler,
24
- buildHandlersWithHttp,
25
- } from "./server.js";
15
+ export { CesRpcServer, createCesServer } from "./server.js";
26
16
  export type {
27
17
  CesServerOptions,
28
- ManageSecureCommandToolHandlerDeps,
29
18
  RpcHandlerRegistry,
30
19
  RpcMethodHandler,
31
- RunAuthenticatedCommandHandlerOptions,
32
20
  SessionContext,
33
21
  } from "./server.js";
34
22
 
35
23
  export {
36
24
  getCesDataRoot,
37
- getCesGrantsDir,
38
- getCesAuditDir,
39
- getCesToolStoreDir,
40
25
  getCesMode,
41
26
  getBootstrapSocketPath,
42
27
  getHealthPort,
43
28
  } from "./paths.js";
44
29
  export type { CesMode } from "./paths.js";
45
-
46
- export { PersistentGrantStore, TemporaryGrantStore } from "./grants/index.js";
47
- export type {
48
- PersistentGrant,
49
- TemporaryGrant,
50
- TemporaryGrantKind,
51
- } from "./grants/index.js";
52
-
53
- export { computeDigest, verifyDigest } from "./toolstore/integrity.js";
54
- export type { DigestVerificationResult } from "./toolstore/integrity.js";
55
-
56
- export {
57
- isValidSha256Hex,
58
- validateSourceUrl,
59
- isWorkspaceOriginPath,
60
- } from "./toolstore/manifest.js";
61
- export type {
62
- BundleOrigin,
63
- ToolstoreManifest,
64
- } from "./toolstore/manifest.js";
65
-
66
- export {
67
- publishBundle,
68
- readPublishedManifest,
69
- isBundlePublished,
70
- getBundleDir,
71
- getBundleManifestPath,
72
- getBundleContentPath,
73
- } from "./toolstore/publish.js";
74
- export type {
75
- PublishRequest,
76
- PublishResult,
77
- } from "./toolstore/publish.js";
78
-
79
- export { resolveLocalSubject } from "./subjects/local.js";
80
- export type {
81
- ResolvedStaticSubject,
82
- ResolvedOAuthSubject,
83
- ResolvedLocalSubject,
84
- SubjectResolutionResult,
85
- OAuthConnectionLookup,
86
- LocalSubjectResolverDeps,
87
- } from "./subjects/local.js";
88
-
89
- export { LocalMaterialiser } from "./materializers/local.js";
90
- export type {
91
- MaterialisedCredential,
92
- MaterialisationResult,
93
- TokenRefreshFn,
94
- LocalMaterialiserDeps,
95
- } from "./materializers/local.js";
96
-
97
- export { executeAuthenticatedCommand } from "./commands/executor.js";
98
- export type {
99
- ExecuteCommandRequest,
100
- ExecuteCommandResult,
101
- CommandExecutorDeps,
102
- MaterializeCredentialFn,
103
- MaterializeCredentialResult,
104
- } from "./commands/executor.js";
105
-
106
- export { executeAuthenticatedHttpRequest } from "./http/executor.js";
107
- export type { HttpExecutorDeps } from "./http/executor.js";
108
-
109
- export { MANAGED_LOCAL_STATIC_REJECTION_ERROR } from "./managed-errors.js";