@bivy/bivy 0.10.1-staging.355 → 0.10.1-staging.356

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.
@@ -0,0 +1,131 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (c) 2026 Petter André Sjulstad
3
+ //
4
+ // The credential CRUD + presets command cluster, extracted from server.ts's
5
+ // RELAY_COMMANDS (server.ts decomposition; same shape as controllers/
6
+ // session-control.ts and fork-commands.ts). The credential vault operations
7
+ // already live in credentials/api.ts + runtime/credentials.ts and take the node's
8
+ // credsDir explicitly, so this controller imports them directly and only injects
9
+ // the handful of daemon bits (credsDir, event emit, and the post-auth refresh /
10
+ // provider-list helpers) as composition deps.
11
+ import { testProviderCredential } from "../runtime/credentials.js";
12
+ import { exportAccountApiKeys, exportRecordTombstones, listCredentialRecords, removeProviderCredential, setProviderApiKeyLabeled, setProviderReferenceLabeled, setCredentialSync, setCredentialUnattended, getCredentialPresets, setActiveCredentialPreset, setCredentialPresetMapping, } from "../credentials/api.js";
13
+ export function createCredentialCommands(deps) {
14
+ const { credsDir } = deps;
15
+ return {
16
+ async "credentials.list"() {
17
+ deps.sendEvent({ type: "credentials.records", records: await listCredentialRecords(credsDir) });
18
+ },
19
+ async "credentials.account.export"(_msg, ctx) {
20
+ // This reply travels inside the already-paired E2E node channel. It contains
21
+ // API keys only; OAuth/ref/node-local material is excluded by the API.
22
+ ctx.reply({ type: "credentials.account.export", requestId: _msg.requestId, entries: await exportAccountApiKeys(credsDir), records: await listCredentialRecords(credsDir), deletedAt: await exportRecordTombstones(credsDir) });
23
+ },
24
+ async "credential.set"(msg, ctx) {
25
+ try {
26
+ const provider = String(msg.provider ?? "").trim().toLowerCase();
27
+ const label = String(msg.label ?? "");
28
+ const ref = typeof msg.ref === "string" ? msg.ref.trim() : "";
29
+ const requestedSync = msg.sync === "account" || msg.sync === "node" ? msg.sync : undefined;
30
+ // Persist the secret and its requested tier atomically. Creating a
31
+ // machine-only credential must never leave an account-tier crash window.
32
+ if (ref)
33
+ await setProviderReferenceLabeled(credsDir, provider, label, ref, requestedSync);
34
+ else
35
+ await setProviderApiKeyLabeled(credsDir, provider, label, String(msg.key ?? ""), requestedSync);
36
+ await deps.pushModelAuthToControlPlane();
37
+ await deps.refreshSessionAfterAuth();
38
+ deps.sendEvent({ type: "credentials.records", records: await listCredentialRecords(credsDir) });
39
+ deps.broadcast({ type: "providers.list", providers: await deps.listProvidersUnified() });
40
+ ctx.reply({ type: "credential.set.ok", requestId: msg.requestId });
41
+ }
42
+ catch (error) {
43
+ const message = error instanceof Error ? error.message : String(error);
44
+ deps.sendEvent({ type: "session.error", error: message });
45
+ ctx.reply({ type: "credential.set.error", requestId: msg.requestId, error: message });
46
+ }
47
+ },
48
+ async "credential.remove"(msg, ctx) {
49
+ try {
50
+ await removeProviderCredential(credsDir, String(msg.provider ?? ""), String(msg.label ?? ""));
51
+ await deps.pushModelAuthToControlPlane();
52
+ await deps.refreshSessionAfterAuth();
53
+ deps.sendEvent({ type: "credentials.records", records: await listCredentialRecords(credsDir) });
54
+ deps.broadcast({ type: "providers.list", providers: await deps.listProvidersUnified() });
55
+ ctx.reply({ type: "credential.remove.ok", requestId: msg.requestId });
56
+ }
57
+ catch (error) {
58
+ const message = error instanceof Error ? error.message : String(error);
59
+ deps.sendEvent({ type: "session.error", error: message });
60
+ ctx.reply({ type: "credential.remove.error", requestId: msg.requestId, error: message });
61
+ }
62
+ },
63
+ async "credential.sync.set"(msg, ctx) {
64
+ try {
65
+ const sync = msg.sync === "node" ? "node" : "account";
66
+ await setCredentialSync(credsDir, String(msg.provider ?? ""), String(msg.label ?? ""), sync);
67
+ await deps.pushModelAuthToControlPlane();
68
+ deps.sendEvent({ type: "credentials.records", records: await listCredentialRecords(credsDir) });
69
+ ctx.reply({ type: "credential.sync.set.ok", requestId: msg.requestId });
70
+ }
71
+ catch (error) {
72
+ const message = error instanceof Error ? error.message : String(error);
73
+ deps.sendEvent({ type: "session.error", error: message });
74
+ ctx.reply({ type: "credential.sync.set.error", requestId: msg.requestId, error: message });
75
+ }
76
+ },
77
+ async "credential.unattended.set"(msg, ctx) {
78
+ try {
79
+ await setCredentialUnattended(credsDir, String(msg.provider ?? ""), String(msg.label ?? ""), msg.unattended === true);
80
+ await deps.pushModelAuthToControlPlane();
81
+ deps.sendEvent({ type: "credentials.records", records: await listCredentialRecords(credsDir) });
82
+ ctx.reply({ type: "credential.unattended.set.ok", requestId: msg.requestId });
83
+ }
84
+ catch (error) {
85
+ const message = error instanceof Error ? error.message : String(error);
86
+ ctx.reply({ type: "credential.unattended.set.error", requestId: msg.requestId, error: message });
87
+ }
88
+ },
89
+ // "Test connection": a bounded, non-secret liveness probe for one credential
90
+ // record (see credentials/api.ts testCredential). The reply carries only
91
+ // ok/at/reason — the credential's own token never leaves this handler.
92
+ async "credential.test"(msg, ctx) {
93
+ const provider = String(msg.provider ?? "").trim().toLowerCase();
94
+ const label = String(msg.label ?? "");
95
+ try {
96
+ const result = await testProviderCredential(credsDir, provider, label);
97
+ ctx.reply({ type: "credential.test.result", requestId: msg.requestId, provider, label, ...result });
98
+ deps.sendEvent({ type: "credentials.records", records: await listCredentialRecords(credsDir) });
99
+ }
100
+ catch (error) {
101
+ ctx.reply({ type: "credential.test.result", requestId: msg.requestId, provider, label, ok: false, at: Date.now(), reason: "network_error" });
102
+ deps.sendEvent({ type: "session.error", error: error instanceof Error ? error.message : String(error) });
103
+ }
104
+ },
105
+ async "credentials.presets.get"() {
106
+ deps.sendEvent({ type: "credentials.presets", presets: getCredentialPresets(credsDir) });
107
+ },
108
+ async "credentials.presets.setActive"(msg) {
109
+ try {
110
+ setActiveCredentialPreset(credsDir, String(msg.active ?? ""));
111
+ await deps.refreshSessionAfterAuth();
112
+ deps.sendEvent({ type: "credentials.presets", presets: getCredentialPresets(credsDir) });
113
+ }
114
+ catch (error) {
115
+ deps.sendEvent({ type: "session.error", error: error instanceof Error ? error.message : String(error) });
116
+ }
117
+ },
118
+ async "credentials.presets.setMapping"(msg, ctx) {
119
+ try {
120
+ setCredentialPresetMapping(credsDir, String(msg.preset ?? ""), String(msg.provider ?? ""), String(msg.label ?? ""));
121
+ await deps.refreshSessionAfterAuth();
122
+ deps.sendEvent({ type: "credentials.presets", presets: getCredentialPresets(credsDir) });
123
+ ctx.reply({ type: "credentials.presets.setMapping.ok", requestId: msg.requestId });
124
+ }
125
+ catch (error) {
126
+ const message = error instanceof Error ? error.message : String(error);
127
+ ctx.reply({ type: "credentials.presets.setMapping.error", requestId: msg.requestId, error: message });
128
+ }
129
+ },
130
+ };
131
+ }
package/dist/server.js CHANGED
@@ -98,6 +98,7 @@ import { createAccessDeviceController, createLinkedDeviceController } from "./co
98
98
  import { createSessionControlCommands } from "./controllers/session-control.js";
99
99
  import { createForkCommands } from "./controllers/fork-commands.js";
100
100
  import { createGithubCommands } from "./controllers/github-commands.js";
101
+ import { createCredentialCommands } from "./controllers/credential-commands.js";
101
102
  import { MetadataStore } from "./metadata.js";
102
103
  import { resolveResumeRef, resumeRefFor } from "./session-ref.js";
103
104
  import { materializeFork } from "./session/fork.js";
@@ -1952,6 +1953,15 @@ const RELAY_COMMANDS = {
1952
1953
  connectExistingApp,
1953
1954
  disconnectGithubApp,
1954
1955
  }),
1956
+ // Credential CRUD + presets cluster, extracted to controllers/credential-commands.ts.
1957
+ ...createCredentialCommands({
1958
+ credsDir,
1959
+ sendEvent: (event) => relay?.sendEvent(event),
1960
+ broadcast,
1961
+ pushModelAuthToControlPlane,
1962
+ refreshSessionAfterAuth,
1963
+ listProvidersUnified,
1964
+ }),
1955
1965
  // Live-stream gap recovery: replay the session.events a client missed after the
1956
1966
  // last seq it holds, or tell it to full-resync (mode:"reset") when the ring has
1957
1967
  // evicted past that point. Answers only the caller (ctx.reply); other clients
@@ -2581,121 +2591,6 @@ const RELAY_COMMANDS = {
2581
2591
  relay?.sendEvent({ type: "session.error", error: error instanceof Error ? error.message : String(error) });
2582
2592
  }
2583
2593
  },
2584
- // --- Multiple credentials per provider (labeled). ----------------------
2585
- async "credentials.list"() {
2586
- relay?.sendEvent({ type: "credentials.records", records: await listCredentialRecords(credsDir) });
2587
- },
2588
- async "credentials.account.export"(_msg, ctx) {
2589
- // This reply travels inside the already-paired E2E node channel. It contains
2590
- // API keys only; OAuth/ref/node-local material is excluded by the API.
2591
- ctx.reply({ type: "credentials.account.export", requestId: _msg.requestId, entries: await exportAccountApiKeys(credsDir), records: await listCredentialRecords(credsDir), deletedAt: await exportRecordTombstones(credsDir) });
2592
- },
2593
- async "credential.set"(msg, ctx) {
2594
- try {
2595
- const provider = String(msg.provider ?? "").trim().toLowerCase();
2596
- const label = String(msg.label ?? "");
2597
- const ref = typeof msg.ref === "string" ? msg.ref.trim() : "";
2598
- const requestedSync = msg.sync === "account" || msg.sync === "node" ? msg.sync : undefined;
2599
- // Persist the secret and its requested tier atomically. Creating a
2600
- // machine-only credential must never leave an account-tier crash window.
2601
- if (ref)
2602
- await setProviderReferenceLabeled(credsDir, provider, label, ref, requestedSync);
2603
- else
2604
- await setProviderApiKeyLabeled(credsDir, provider, label, String(msg.key ?? ""), requestedSync);
2605
- await pushModelAuthToControlPlane();
2606
- await refreshSessionAfterAuth();
2607
- relay?.sendEvent({ type: "credentials.records", records: await listCredentialRecords(credsDir) });
2608
- broadcast({ type: "providers.list", providers: await listProvidersUnified() });
2609
- ctx.reply({ type: "credential.set.ok", requestId: msg.requestId });
2610
- }
2611
- catch (error) {
2612
- const message = error instanceof Error ? error.message : String(error);
2613
- relay?.sendEvent({ type: "session.error", error: message });
2614
- ctx.reply({ type: "credential.set.error", requestId: msg.requestId, error: message });
2615
- }
2616
- },
2617
- async "credential.remove"(msg, ctx) {
2618
- try {
2619
- await removeProviderCredential(credsDir, String(msg.provider ?? ""), String(msg.label ?? ""));
2620
- await pushModelAuthToControlPlane();
2621
- await refreshSessionAfterAuth();
2622
- relay?.sendEvent({ type: "credentials.records", records: await listCredentialRecords(credsDir) });
2623
- broadcast({ type: "providers.list", providers: await listProvidersUnified() });
2624
- ctx.reply({ type: "credential.remove.ok", requestId: msg.requestId });
2625
- }
2626
- catch (error) {
2627
- const message = error instanceof Error ? error.message : String(error);
2628
- relay?.sendEvent({ type: "session.error", error: message });
2629
- ctx.reply({ type: "credential.remove.error", requestId: msg.requestId, error: message });
2630
- }
2631
- },
2632
- async "credential.sync.set"(msg, ctx) {
2633
- try {
2634
- const sync = msg.sync === "node" ? "node" : "account";
2635
- await setCredentialSync(credsDir, String(msg.provider ?? ""), String(msg.label ?? ""), sync);
2636
- await pushModelAuthToControlPlane();
2637
- relay?.sendEvent({ type: "credentials.records", records: await listCredentialRecords(credsDir) });
2638
- ctx.reply({ type: "credential.sync.set.ok", requestId: msg.requestId });
2639
- }
2640
- catch (error) {
2641
- const message = error instanceof Error ? error.message : String(error);
2642
- relay?.sendEvent({ type: "session.error", error: message });
2643
- ctx.reply({ type: "credential.sync.set.error", requestId: msg.requestId, error: message });
2644
- }
2645
- },
2646
- async "credential.unattended.set"(msg, ctx) {
2647
- try {
2648
- await setCredentialUnattended(credsDir, String(msg.provider ?? ""), String(msg.label ?? ""), msg.unattended === true);
2649
- await pushModelAuthToControlPlane();
2650
- relay?.sendEvent({ type: "credentials.records", records: await listCredentialRecords(credsDir) });
2651
- ctx.reply({ type: "credential.unattended.set.ok", requestId: msg.requestId });
2652
- }
2653
- catch (error) {
2654
- const message = error instanceof Error ? error.message : String(error);
2655
- ctx.reply({ type: "credential.unattended.set.error", requestId: msg.requestId, error: message });
2656
- }
2657
- },
2658
- // "Test connection": a bounded, non-secret liveness probe for one credential
2659
- // record (see credentials/api.ts testCredential). The reply carries only
2660
- // ok/at/reason — the credential's own token never leaves this handler.
2661
- async "credential.test"(msg, ctx) {
2662
- const provider = String(msg.provider ?? "").trim().toLowerCase();
2663
- const label = String(msg.label ?? "");
2664
- try {
2665
- const result = await testProviderCredential(credsDir, provider, label);
2666
- ctx.reply({ type: "credential.test.result", requestId: msg.requestId, provider, label, ...result });
2667
- relay?.sendEvent({ type: "credentials.records", records: await listCredentialRecords(credsDir) });
2668
- }
2669
- catch (error) {
2670
- ctx.reply({ type: "credential.test.result", requestId: msg.requestId, provider, label, ok: false, at: Date.now(), reason: "network_error" });
2671
- relay?.sendEvent({ type: "session.error", error: error instanceof Error ? error.message : String(error) });
2672
- }
2673
- },
2674
- async "credentials.presets.get"() {
2675
- relay?.sendEvent({ type: "credentials.presets", presets: getCredentialPresets(credsDir) });
2676
- },
2677
- async "credentials.presets.setActive"(msg) {
2678
- try {
2679
- setActiveCredentialPreset(credsDir, String(msg.active ?? ""));
2680
- await refreshSessionAfterAuth();
2681
- relay?.sendEvent({ type: "credentials.presets", presets: getCredentialPresets(credsDir) });
2682
- }
2683
- catch (error) {
2684
- relay?.sendEvent({ type: "session.error", error: error instanceof Error ? error.message : String(error) });
2685
- }
2686
- },
2687
- async "credentials.presets.setMapping"(msg, ctx) {
2688
- try {
2689
- setCredentialPresetMapping(credsDir, String(msg.preset ?? ""), String(msg.provider ?? ""), String(msg.label ?? ""));
2690
- await refreshSessionAfterAuth();
2691
- relay?.sendEvent({ type: "credentials.presets", presets: getCredentialPresets(credsDir) });
2692
- ctx.reply({ type: "credentials.presets.setMapping.ok", requestId: msg.requestId });
2693
- }
2694
- catch (error) {
2695
- const message = error instanceof Error ? error.message : String(error);
2696
- ctx.reply({ type: "credentials.presets.setMapping.error", requestId: msg.requestId, error: message });
2697
- }
2698
- },
2699
2594
  // --- Local / custom models (Bivy-owned registry; Pi is a projection). ---
2700
2595
  async "models.custom.list"() {
2701
2596
  relay?.sendEvent({ type: "models.custom.list", providers: await localModelSummaries() });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bivy/bivy",
3
- "version": "0.10.1-staging.355",
3
+ "version": "0.10.1-staging.356",
4
4
  "type": "module",
5
5
  "license": "AGPL-3.0-only",
6
6
  "description": "Run coding agents on machines you own. Open-source, self-hostable agent workspace.",