@bivy/bivy 0.20.9-staging.33 → 0.20.9-staging.35
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 +1 -2
- package/dist/apps/styles.css +1 -1
- package/dist/config-cli.js +2 -0
- package/dist/controllers/credential-commands.js +3 -27
- package/dist/credentials/api.js +2 -43
- package/dist/credentials/document.js +6 -14
- package/dist/credentials/store.js +5 -6
- package/dist/node-config.js +4 -3
- package/dist/server.js +73 -217
- package/dist/work-instructions.js +50 -0
- package/package.json +2 -2
- package/web/assets/AutomationsView-BN4WXo-s.js +51 -0
- package/web/assets/ChangesCard-BLxRc1SD.js +4 -0
- package/web/assets/LibraryView-BG-p4ZVG.js +2 -0
- package/web/assets/{ProviderConnect-Djmg4kDE.js → ProviderConnect-D_cUR8Nn.js} +1 -1
- package/web/assets/SessionList-uP86Sp-P.js +1 -0
- package/web/assets/Settings-QdmsBSqy.js +10 -0
- package/web/assets/{Sheet-CpsmpYNx.js → Sheet-DxeBQ9hh.js} +1 -1
- package/web/assets/{StatusDot-dpbztK53.js → Spinner-CPdRfDcT.js} +1 -1
- package/web/assets/{Terminal-CiIel9SY.js → Terminal-BA-H41j5.js} +1 -1
- package/web/assets/{VoiceSettings-BwuhW6YG.js → VoiceSettings-Bzo9CUrH.js} +1 -1
- package/web/assets/{abnfDiagram-VCTEODGH-D1AR8gPX.js → abnfDiagram-VCTEODGH-DSJSYU79.js} +1 -1
- package/web/assets/architecture-7GRP2DOG-B9W62Fx_.js +1 -0
- package/web/assets/{architectureDiagram-5GKGNRK7-BYW9fZLp.js → architectureDiagram-5GKGNRK7-C5CVECpy.js} +1 -1
- package/web/assets/{blockDiagram-I7D4REHJ-Beiu9CFg.js → blockDiagram-I7D4REHJ-lfmzpHvp.js} +1 -1
- package/web/assets/{c4Diagram-7LVT6UL2-Db8Um9y8.js → c4Diagram-7LVT6UL2-lcarA-nH.js} +1 -1
- package/web/assets/channel-BXl-2oyN.js +1 -0
- package/web/assets/{chunk-4HAMMTFA-BhHRogsa.js → chunk-4HAMMTFA-8WD1Igor.js} +1 -1
- package/web/assets/{chunk-75Z2AOVW-Bow_oUfm.js → chunk-75Z2AOVW-Jh9IYxee.js} +1 -1
- package/web/assets/{chunk-DU6HZSFF-BjMVMJBV.js → chunk-DU6HZSFF-C-F06-i-.js} +1 -1
- package/web/assets/{chunk-F27PBJKO-Crg8_U0M.js → chunk-F27PBJKO-BfheyUJr.js} +1 -1
- package/web/assets/{chunk-GMAD6QVW-ClFxSykH.js → chunk-GMAD6QVW-C4QeycFz.js} +1 -1
- package/web/assets/{chunk-GVQU2GXP-mJM3YX9W.js → chunk-GVQU2GXP-BZllZVzj.js} +1 -1
- package/web/assets/{chunk-IMKFNOWR-a9U56ns6.js → chunk-IMKFNOWR-CmYszTC0.js} +1 -1
- package/web/assets/{chunk-L3NEJ4N5-R_HGKzoe.js → chunk-L3NEJ4N5-HYod6GBQ.js} +1 -1
- package/web/assets/{chunk-OSK3NFVY-CsVyixxW.js → chunk-OSK3NFVY-BCin4kBb.js} +1 -1
- package/web/assets/{chunk-P2QGCYS3-DXU7vZsa.js → chunk-P2QGCYS3-hfC5iZ2m.js} +1 -1
- package/web/assets/{chunk-POPQ4Y6H-D2vS40y1.js → chunk-POPQ4Y6H-DDFroJcO.js} +1 -1
- package/web/assets/{chunk-PWAF6VOD-DYQz8dj4.js → chunk-PWAF6VOD-CzGDhfaI.js} +1 -1
- package/web/assets/{chunk-SHT3W25Y-DbyHCPSj.js → chunk-SHT3W25Y-C2OsFNUA.js} +1 -1
- package/web/assets/{chunk-SVP7TREG-DSs2M3u5.js → chunk-SVP7TREG-DpgjbZ29.js} +1 -1
- package/web/assets/{chunk-TICWLB2K-mHq6dwoq.js → chunk-TICWLB2K-DCfsEWvj.js} +1 -1
- package/web/assets/classDiagram-ZZMXUADV-eeGC4O7K.js +1 -0
- package/web/assets/classDiagram-v2-VYDZK3BY-eeGC4O7K.js +1 -0
- package/web/assets/{cynefin-OW5HDTMX-CNeeS9oT.js → cynefin-OW5HDTMX-Dw490MV0.js} +1 -1
- package/web/assets/{cynefinDiagram-5FMLGOSQ-B1i27Cjz.js → cynefinDiagram-5FMLGOSQ-MRqdtb3R.js} +1 -1
- package/web/assets/{dagre-GXQ25YYZ-B6yTtRTd.js → dagre-GXQ25YYZ-GVsrjWt8.js} +1 -1
- package/web/assets/{diagram-S7CK7UJ4--nM7nSjZ.js → diagram-S7CK7UJ4-dBynfuwz.js} +1 -1
- package/web/assets/{diagram-UQ7AKVKN-B5D5n-C9.js → diagram-UQ7AKVKN-CvzMkQIm.js} +1 -1
- package/web/assets/{diagram-VSXAHHWV-DK497GbZ.js → diagram-VSXAHHWV-DM0Y3A03.js} +1 -1
- package/web/assets/{diagram-VX7I27RA-BYJk7lmK.js → diagram-VX7I27RA-B8QuZJJ1.js} +1 -1
- package/web/assets/{diagram-Z3DM3KII-DWlj2KWL.js → diagram-Z3DM3KII-DsHpaaVD.js} +1 -1
- package/web/assets/{ebnfDiagram-PWID7BFC-C4C2RGzZ.js → ebnfDiagram-PWID7BFC-D5d-fRlp.js} +1 -1
- package/web/assets/{erDiagram-RLTQ6QDP-DOUd8J7_.js → erDiagram-RLTQ6QDP-BntqSa-X.js} +1 -1
- package/web/assets/eventmodeling-NTZA5JFV-DHY9JONZ.js +1 -0
- package/web/assets/flowDiagram-HODETNUW-b1Y1u8zB.js +1 -0
- package/web/assets/{ganttDiagram-EL5Y4UJY-EmHqwzdY.js → ganttDiagram-EL5Y4UJY-BiSe6RPt.js} +1 -1
- package/web/assets/{gitGraph-4MIJSDKK-CWL4Zrd7.js → gitGraph-4MIJSDKK-crNXcNI6.js} +1 -1
- package/web/assets/{gitGraphDiagram-WWUBYQGX-8eiWtQP8.js → gitGraphDiagram-WWUBYQGX-Gq8bbUrV.js} +1 -1
- package/web/assets/index-BKSbod42.css +1 -0
- package/web/assets/{index-vtdRDm_9.js → index-C2l0RrVm.js} +2 -2
- package/web/assets/{info-A6RAGUB7-FUHBa47R.js → info-A6RAGUB7-D7ku5afp.js} +1 -1
- package/web/assets/{infoDiagram-27XIBGKW-B4VKPznj.js → infoDiagram-27XIBGKW-DKOdetB1.js} +1 -1
- package/web/assets/{ishikawaDiagram-5VMMS53U-LRAGe50t.js → ishikawaDiagram-5VMMS53U-BBXgWpq6.js} +1 -1
- package/web/assets/{journeyDiagram-3NMN7TZE-CPeXtzWr.js → journeyDiagram-3NMN7TZE-C50GQXpS.js} +1 -1
- package/web/assets/{kanban-definition-UXKFOSKX-ChebVHas.js → kanban-definition-UXKFOSKX-DX4E75a1.js} +1 -1
- package/web/assets/{mermaid-parser.core-C6-j8EoB.js → mermaid-parser.core-ZebqYrQW.js} +3 -3
- package/web/assets/{mermaid.core-E04eZncM.js → mermaid.core-CEOL8xFx.js} +4 -4
- package/web/assets/{mindmap-definition-YA3MSWOX-Bg4A7LOK.js → mindmap-definition-YA3MSWOX-DQXDkaGH.js} +1 -1
- package/web/assets/mount-shoNM5J3.js +36 -0
- package/web/assets/{packet-AYTQ26CC-CSFjkE9G.js → packet-AYTQ26CC-rr9WW6AZ.js} +1 -1
- package/web/assets/{pegDiagram-XKGWAZYB-De1brJ3O.js → pegDiagram-XKGWAZYB-DC6KGFpM.js} +1 -1
- package/web/assets/{pie-WAS4IAKB-BemgYnS8.js → pie-WAS4IAKB-Chrwh9jb.js} +1 -1
- package/web/assets/{pieDiagram-E7YTZNPT-CHPyOMzG.js → pieDiagram-E7YTZNPT-BxH2hKmV.js} +1 -1
- package/web/assets/{quadrantDiagram-AXDQQJYC-asjJ6NLr.js → quadrantDiagram-AXDQQJYC-DP_tHDta.js} +1 -1
- package/web/assets/{radar-RG4KPBEZ-BrUtVrXu.js → radar-RG4KPBEZ-B3m2rpw6.js} +1 -1
- package/web/assets/{railroad-74A4TZTK-CVI71rHD.js → railroad-74A4TZTK-CuMQsitQ.js} +1 -1
- package/web/assets/railroad-abnf-HS5TGJTU--xUmAXKs.js +1 -0
- package/web/assets/railroad-ebnf-LZEXJU2U-BLsOkOHg.js +1 -0
- package/web/assets/railroad-peg-WCYAUIDC-CL2DhE_z.js +1 -0
- package/web/assets/{railroadDiagram-O6MQD6OU-DE1is5LP.js → railroadDiagram-O6MQD6OU-D6n_hu74.js} +1 -1
- package/web/assets/{requirementDiagram-BXWQKSXE-DLDVgQV6.js → requirementDiagram-BXWQKSXE-TCkLe8CH.js} +1 -1
- package/web/assets/{sankeyDiagram-P5KCCOFB-CQmWGMSr.js → sankeyDiagram-P5KCCOFB-B2dVDgg8.js} +1 -1
- package/web/assets/{sequenceDiagram-WJ2MYXX4-BgQw-Jly.js → sequenceDiagram-WJ2MYXX4-B3fjTjZH.js} +1 -1
- package/web/assets/{settingsRoute-xC3YeEI6.js → settingsRoute-C1RuP_I9.js} +1 -1
- package/web/assets/{stateDiagram-D77RDMKH-CPz2u1PP.js → stateDiagram-D77RDMKH-CpIdCYNP.js} +1 -1
- package/web/assets/stateDiagram-v2-MP3YSRHH-DKes1G1a.js +1 -0
- package/web/assets/{swimlanes-42K2YHIH-BwqxQ33P.js → swimlanes-42K2YHIH-BMxrWwFI.js} +1 -1
- package/web/assets/swimlanesDiagram-VR7AAH4N-Diu117lT.js +8 -0
- package/web/assets/{timeline-definition-24CTP7MA-lp4dggLF.js → timeline-definition-24CTP7MA-Dm4tF8yN.js} +1 -1
- package/web/assets/{treeView-Q6P3EWNA-DCLFtlc0.js → treeView-Q6P3EWNA-l-13N1XB.js} +1 -1
- package/web/assets/{treemap-WGGIJYW6-JU9eCMfp.js → treemap-WGGIJYW6-BxdHeQGQ.js} +1 -1
- package/web/assets/useStore-BR0SJhpC.js +16 -0
- package/web/assets/{vennDiagram-4TSXK5OY-Bg5thDxs.js → vennDiagram-4TSXK5OY-e0kljBOl.js} +1 -1
- package/web/assets/{wardley-WFR3VGLG-CztmBvl1.js → wardley-WFR3VGLG-BtVNA4a-.js} +1 -1
- package/web/assets/{wardleyDiagram-VM6X3IG4-CHGGBXUf.js → wardleyDiagram-VM6X3IG4-Bb2YkT8Z.js} +1 -1
- package/web/assets/{xychartDiagram-S5SC5T6Z-C_SIwLAH.js → xychartDiagram-S5SC5T6Z-CdeQ4ZTU.js} +1 -1
- package/web/index.html +2 -2
- package/web/sw.js +1 -1
- package/dist/hosted-custody.js +0 -30
- package/web/assets/AutomationsView-267dLsQL.js +0 -51
- package/web/assets/ChangesCard-CtqkfiTy.js +0 -4
- package/web/assets/LibraryView-5lnRxA2q.js +0 -2
- package/web/assets/SessionList-D9inDuK9.js +0 -1
- package/web/assets/Settings-CFR6kO3x.js +0 -10
- package/web/assets/architecture-7GRP2DOG-BbTspOBS.js +0 -1
- package/web/assets/channel-C7WyIm34.js +0 -1
- package/web/assets/classDiagram-ZZMXUADV-WRkZ6qcZ.js +0 -1
- package/web/assets/classDiagram-v2-VYDZK3BY-WRkZ6qcZ.js +0 -1
- package/web/assets/eventmodeling-NTZA5JFV-BWV7-Dxz.js +0 -1
- package/web/assets/flowDiagram-HODETNUW-D0Ayl_mD.js +0 -1
- package/web/assets/index-DtEJd4mp.css +0 -1
- package/web/assets/mount-nzOni7XG.js +0 -36
- package/web/assets/railroad-abnf-HS5TGJTU-totY__KY.js +0 -1
- package/web/assets/railroad-ebnf-LZEXJU2U-DK1Lra9N.js +0 -1
- package/web/assets/railroad-peg-WCYAUIDC-BhYnbj9W.js +0 -1
- package/web/assets/stateDiagram-v2-MP3YSRHH-DaJr0emA.js +0 -1
- package/web/assets/swimlanesDiagram-VR7AAH4N-CuosQUhF.js +0 -8
- package/web/assets/useStore-B7fA4LtS.js +0 -16
package/README.md
CHANGED
|
@@ -228,8 +228,7 @@ bivy secrets ref github.repo-token op://Bivy/GitHub/repo-token
|
|
|
228
228
|
|
|
229
229
|
**Not every CLI login syncs.** Native agent logins may still be per-machine;
|
|
230
230
|
GitHub App private-key sync is separately opt-in. If you lose every node and
|
|
231
|
-
device able to unwrap a vault, you must sign in to providers again.
|
|
232
|
-
hosted-provisioning custody grants are separate from ordinary encrypted sync.
|
|
231
|
+
device able to unwrap a vault, you must sign in to providers again.
|
|
233
232
|
|
|
234
233
|
[Credential sync and runtime coverage →](docs/credential-sync.md) ·
|
|
235
234
|
[Credentials guide →](docs/credentials-guide.md) ·
|
package/dist/apps/styles.css
CHANGED
|
@@ -1517,7 +1517,6 @@ button.sheet-action:last-child { border-bottom: 0; }
|
|
|
1517
1517
|
.vault-detail-grid { display: grid; grid-template-columns: minmax(110px, auto) 1fr; gap: 10px 18px; padding: 14px; border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--surface-2); }
|
|
1518
1518
|
.vault-detail-grid strong { min-width: 0; overflow-wrap: anywhere; }
|
|
1519
1519
|
.vault-detail-grid code { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
1520
|
-
.vault-custody-note { padding: 12px 14px; border-left: 3px solid var(--accent); border-radius: var(--radius-sm) var(--radius-md) var(--radius-md) var(--radius-sm); background: var(--surface-2); line-height: 1.5; }
|
|
1521
1520
|
.vault-advanced { padding: 10px 0; border-top: 1px solid var(--line); }
|
|
1522
1521
|
.vault-advanced summary { cursor: pointer; color: var(--ink); font-weight: 600; }
|
|
1523
1522
|
.vault-advanced[open] summary { margin-bottom: 10px; }
|
|
@@ -1916,6 +1915,7 @@ button.template-card.is-featured .template-card-icon {
|
|
|
1916
1915
|
.autom-runner-select-copy span { color: var(--muted); font-size: 12px; line-height: 1.35; }
|
|
1917
1916
|
.autom-runner-select-row .autom-inline-select { flex: none; width: min(42%, 190px); }
|
|
1918
1917
|
.autom-runner-help {
|
|
1918
|
+
display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-2);
|
|
1919
1919
|
padding: 10px 12px; border-top: 1px solid var(--line);
|
|
1920
1920
|
color: var(--ink); background: color-mix(in srgb, var(--warn) 9%, var(--surface));
|
|
1921
1921
|
font-size: 12px; line-height: 1.45;
|
package/dist/config-cli.js
CHANGED
|
@@ -112,6 +112,7 @@ const ENV_FOR_KEY = {
|
|
|
112
112
|
"sessions.previewBadge": "BIVY_PREVIEW_BADGE",
|
|
113
113
|
"automation.checks": "BIVY_AUTOMATION_CHECKS",
|
|
114
114
|
"automation.checkTimeoutMinutes": "BIVY_AUTOMATION_CHECK_TIMEOUT_MS",
|
|
115
|
+
"automation.slackPrompts": "BIVY_SLACK_PROMPTS",
|
|
115
116
|
};
|
|
116
117
|
const BUILTIN_VALUES = {
|
|
117
118
|
"node.port": 4317,
|
|
@@ -128,6 +129,7 @@ const BUILTIN_VALUES = {
|
|
|
128
129
|
"sessions.previewBadge": true,
|
|
129
130
|
"automation.checks": ["test", "lint", "typecheck"],
|
|
130
131
|
"automation.checkTimeoutMinutes": 10,
|
|
132
|
+
"automation.slackPrompts": false,
|
|
131
133
|
};
|
|
132
134
|
function baseEffectiveValue(config, key) {
|
|
133
135
|
const configured = getConfigValue(config, key);
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// provider-list helpers) as composition deps.
|
|
11
11
|
import { testProviderCredential } from "../runtime/credentials.js";
|
|
12
12
|
import { NativeAuthPreviewService } from "../runtime/native-auth-preview.js";
|
|
13
|
-
import { exportAccountApiKeys, exportAccountOAuthCredentials, importAccountOAuthCredentials, exportRecordTombstones, listCredentialRecords, removeProviderCredential, setProviderApiKeyLabeled, setProviderReferenceLabeled, setCredentialSync,
|
|
13
|
+
import { exportAccountApiKeys, exportAccountOAuthCredentials, importAccountOAuthCredentials, exportRecordTombstones, listCredentialRecords, removeProviderCredential, setProviderApiKeyLabeled, setProviderReferenceLabeled, setCredentialSync, getCredentialPresets, setActiveCredentialPreset, setCredentialPresetMapping, } from "../credentials/api.js";
|
|
14
14
|
export function createCredentialCommands(deps) {
|
|
15
15
|
const { credsDir } = deps;
|
|
16
16
|
const nativeAuth = new NativeAuthPreviewService(credsDir);
|
|
@@ -95,8 +95,7 @@ export function createCredentialCommands(deps) {
|
|
|
95
95
|
async "credential.remove"(msg, ctx) {
|
|
96
96
|
try {
|
|
97
97
|
await removeProviderCredential(credsDir, String(msg.provider ?? ""), String(msg.label ?? ""));
|
|
98
|
-
|
|
99
|
-
await deps.pushModelAuthToControlPlane({ allowEmptyHostedEscrow: true });
|
|
98
|
+
await deps.pushModelAuthToControlPlane();
|
|
100
99
|
await deps.refreshSessionAfterAuth();
|
|
101
100
|
deps.sendEvent({ type: "credentials.records", records: await listCredentialRecords(credsDir) });
|
|
102
101
|
deps.broadcast({ type: "providers.list", providers: await deps.listProvidersUnified() });
|
|
@@ -112,8 +111,7 @@ export function createCredentialCommands(deps) {
|
|
|
112
111
|
try {
|
|
113
112
|
const sync = msg.sync === "node" ? "node" : "account";
|
|
114
113
|
await setCredentialSync(credsDir, String(msg.provider ?? ""), String(msg.label ?? ""), sync);
|
|
115
|
-
|
|
116
|
-
await deps.pushModelAuthToControlPlane({ allowEmptyHostedEscrow: sync === "node" });
|
|
114
|
+
await deps.pushModelAuthToControlPlane();
|
|
117
115
|
deps.sendEvent({ type: "credentials.records", records: await listCredentialRecords(credsDir) });
|
|
118
116
|
ctx.reply({ type: "credential.sync.set.ok", requestId: msg.requestId });
|
|
119
117
|
}
|
|
@@ -123,28 +121,6 @@ export function createCredentialCommands(deps) {
|
|
|
123
121
|
ctx.reply({ type: "credential.sync.set.error", requestId: msg.requestId, error: message });
|
|
124
122
|
}
|
|
125
123
|
},
|
|
126
|
-
async "credential.unattended.set"(msg, ctx) {
|
|
127
|
-
const provider = String(msg.provider ?? "");
|
|
128
|
-
const label = String(msg.label ?? "");
|
|
129
|
-
const previous = (await listCredentialRecords(credsDir)).find((record) => record.provider === provider.trim().toLowerCase() && record.label === label)?.unattended === true;
|
|
130
|
-
try {
|
|
131
|
-
await setCredentialUnattended(credsDir, provider, label, msg.unattended === true);
|
|
132
|
-
// Fail loud: the grant reply must reflect whether the encrypted cloud
|
|
133
|
-
// copy actually reached the control plane. An explicit disable is the
|
|
134
|
-
// one caller allowed to publish an empty escrow snapshot.
|
|
135
|
-
await deps.pushModelAuthToControlPlane({ throwOnFailure: true, allowEmptyHostedEscrow: msg.unattended !== true });
|
|
136
|
-
deps.sendEvent({ type: "credentials.records", records: await listCredentialRecords(credsDir) });
|
|
137
|
-
ctx.reply({ type: "credential.unattended.set.ok", requestId: msg.requestId });
|
|
138
|
-
}
|
|
139
|
-
catch (error) {
|
|
140
|
-
// The UI must never claim an encrypted Cloud copy exists when custody
|
|
141
|
-
// publication failed. Restore the prior local grant before rejecting.
|
|
142
|
-
await setCredentialUnattended(credsDir, provider, label, previous).catch(() => { });
|
|
143
|
-
deps.sendEvent({ type: "credentials.records", records: await listCredentialRecords(credsDir) });
|
|
144
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
145
|
-
ctx.reply({ type: "credential.unattended.set.error", requestId: msg.requestId, error: message });
|
|
146
|
-
}
|
|
147
|
-
},
|
|
148
124
|
// "Test connection": a bounded, non-secret liveness probe for one credential
|
|
149
125
|
// record (see credentials/api.ts testCredential). The reply carries only
|
|
150
126
|
// ok/at/reason — the credential's own token never leaves this handler.
|
package/dist/credentials/api.js
CHANGED
|
@@ -132,18 +132,6 @@ export async function importCredentialRecords(credsDir, records, deletedAt = {})
|
|
|
132
132
|
}
|
|
133
133
|
return createCredentialVault(credsDir).importRecords(list, tombstones);
|
|
134
134
|
}
|
|
135
|
-
/** Apply a hosted custody snapshot as an authoritative filtered set. */
|
|
136
|
-
export async function reconcileHostedCredentialRecords(credsDir, records, previous) {
|
|
137
|
-
const current = Object.values(records ?? {}).filter((record) => Boolean(record) && typeof record === "object" && typeof record.provider === "string" && typeof record.label === "string");
|
|
138
|
-
const currentIds = new Set(current.map((record) => `${record.provider}\u0000${record.label}`));
|
|
139
|
-
const store = createCredentialVault(credsDir);
|
|
140
|
-
for (const prior of previous) {
|
|
141
|
-
if (!currentIds.has(`${prior.provider}\u0000${prior.label}`))
|
|
142
|
-
await store.deleteRecord(prior.provider, prior.label);
|
|
143
|
-
}
|
|
144
|
-
await importCredentialRecords(credsDir, records);
|
|
145
|
-
return current.map((record) => ({ provider: record.provider, label: record.label }));
|
|
146
|
-
}
|
|
147
135
|
/** Export provider revocations for cross-node convergence. */
|
|
148
136
|
export async function exportProviderAuthTombstones(credsDir) {
|
|
149
137
|
return createCredentialVault(credsDir).exportTombstones();
|
|
@@ -225,7 +213,7 @@ export async function listCredentialRecords(credsDir) {
|
|
|
225
213
|
const verification = await vault.readVerification(record.provider, record.label).catch(() => undefined);
|
|
226
214
|
const verified = verification ? { lastVerifiedAt: verification.at, lastVerifiedOk: verification.ok } : {};
|
|
227
215
|
if (source.kind === "reference") {
|
|
228
|
-
return { provider: record.provider, label: record.label, kind: "reference", sync: record.sync, origin: record.origin,
|
|
216
|
+
return { provider: record.provider, label: record.label, kind: "reference", sync: record.sync, origin: record.origin, ref: source.ref, testable: false, ...verified };
|
|
229
217
|
}
|
|
230
218
|
const cred = source.cred;
|
|
231
219
|
const summary = {
|
|
@@ -234,7 +222,6 @@ export async function listCredentialRecords(credsDir) {
|
|
|
234
222
|
kind: cred.type,
|
|
235
223
|
sync: record.sync,
|
|
236
224
|
origin: record.origin,
|
|
237
|
-
unattended: record.unattended === true,
|
|
238
225
|
testable: isTestableProvider(record.provider),
|
|
239
226
|
...verified,
|
|
240
227
|
};
|
|
@@ -253,7 +240,6 @@ async function labeledMeta(credsDir, provider, label) {
|
|
|
253
240
|
return {
|
|
254
241
|
sync: existing?.sync ?? defaultSyncFor("bivy"),
|
|
255
242
|
origin: existing?.origin ?? "bivy",
|
|
256
|
-
...(existing?.unattended === true ? { unattended: true } : {}),
|
|
257
243
|
};
|
|
258
244
|
}
|
|
259
245
|
/** Store an API key under a specific label (multi-account). */
|
|
@@ -404,33 +390,6 @@ export function getCredentialIngestPolicy(credsDir) {
|
|
|
404
390
|
export function setCredentialIngestPolicy(credsDir, policy) {
|
|
405
391
|
setIngestPolicyFile(defaultPresetsPath(credsDir), policy);
|
|
406
392
|
}
|
|
407
|
-
/** Explicitly grant/revoke use by separately escrowed unattended runners. */
|
|
408
|
-
export async function setCredentialUnattended(credsDir, provider, label, unattended) {
|
|
409
|
-
const id = provider.trim().toLowerCase();
|
|
410
|
-
if (!id)
|
|
411
|
-
throw new Error("Provider is required");
|
|
412
|
-
const store = createCredentialVault(credsDir);
|
|
413
|
-
const existing = await store.readRecord(id, label);
|
|
414
|
-
if (!existing)
|
|
415
|
-
throw new Error(`No credential for ${id}:${normalizeLabel(label)}`);
|
|
416
|
-
if (existing.sync !== "account" && unattended)
|
|
417
|
-
throw new Error("A machine-only credential cannot be granted to unattended runners");
|
|
418
|
-
if (existing.source.kind === "reference" && unattended)
|
|
419
|
-
throw new Error("Password-manager references cannot be resolved by unattended runners");
|
|
420
|
-
await store.putRecord({ ...existing, unattended });
|
|
421
|
-
}
|
|
422
|
-
/** Credentials included in the explicit hosted custody snapshot. */
|
|
423
|
-
export async function exportUnattendedRecords(credsDir) {
|
|
424
|
-
const records = await createCredentialVault(credsDir).exportSyncableRecords();
|
|
425
|
-
return Object.fromEntries(Object.entries(records).filter(([, record]) => record.unattended === true && record.source.kind === "stored"));
|
|
426
|
-
}
|
|
427
|
-
/** Monotonic logical revision for the filtered custody projection. */
|
|
428
|
-
export async function unattendedCredentialRevision(credsDir) {
|
|
429
|
-
const store = createCredentialVault(credsDir);
|
|
430
|
-
const records = await store.listRecords();
|
|
431
|
-
const deletedAt = await store.exportRecordTombstones();
|
|
432
|
-
return Math.max(0, ...records.map((record) => Number(record.updatedAt) || 0), ...Object.values(deletedAt).map((stamp) => Number(stamp) || 0));
|
|
433
|
-
}
|
|
434
393
|
/** Set a labeled credential's sync tier — the per-credential opt-out toggle. */
|
|
435
394
|
export async function setCredentialSync(credsDir, provider, label, sync) {
|
|
436
395
|
const id = provider.trim().toLowerCase();
|
|
@@ -445,5 +404,5 @@ export async function setCredentialSync(credsDir, provider, label, sync) {
|
|
|
445
404
|
if (existing.source.kind === "reference" && existing.source.backend === "command" && sync === "account") {
|
|
446
405
|
throw new Error("A cmd:// reference is node-local (it runs a command on this machine) and cannot be synced.");
|
|
447
406
|
}
|
|
448
|
-
await store.putRecord({ ...existing, sync
|
|
407
|
+
await store.putRecord({ ...existing, sync });
|
|
449
408
|
}
|
|
@@ -137,12 +137,11 @@ export function preferIncomingRecord(local, incoming) {
|
|
|
137
137
|
const incomingOauth = oauthOf(incoming);
|
|
138
138
|
if (!localOauth || !incomingOauth)
|
|
139
139
|
return true;
|
|
140
|
-
// Identical token content means this is a metadata-only change (
|
|
141
|
-
//
|
|
142
|
-
//
|
|
143
|
-
//
|
|
144
|
-
//
|
|
145
|
-
// record content so both merge directions still converge.
|
|
140
|
+
// Identical token content means this is a metadata-only change (a sync-tier
|
|
141
|
+
// move). Token freshness cannot order those, so the newer record write wins —
|
|
142
|
+
// without this, a change made on one machine never reaches its peers. Equal
|
|
143
|
+
// stamps fall back to canonical record content so both merge directions still
|
|
144
|
+
// converge.
|
|
146
145
|
if (canonicalCredential(localOauth) === canonicalCredential(incomingOauth)) {
|
|
147
146
|
const localAt = Number(local.updatedAt) || 0;
|
|
148
147
|
const incomingAt = Number(incoming.updatedAt) || 0;
|
|
@@ -269,14 +268,7 @@ export function mergeDocuments(local, incomingCredentials, incomingDeletedAt = {
|
|
|
269
268
|
continue;
|
|
270
269
|
if (!(key in credentials))
|
|
271
270
|
imported += 1;
|
|
272
|
-
|
|
273
|
-
// v2 wire and plaintext ingest never carry the flag) must not strip an
|
|
274
|
-
// explicit local grant/revoke: agent-side token refreshes flow through this
|
|
275
|
-
// merge, and dropping the flag would make the node's next escrow push
|
|
276
|
-
// silently revoke the encrypted cloud copy the user still relies on.
|
|
277
|
-
credentials[key] = localRecord && incoming.unattended === undefined && localRecord.unattended !== undefined
|
|
278
|
-
? { ...incoming, unattended: localRecord.unattended }
|
|
279
|
-
: incoming;
|
|
271
|
+
credentials[key] = incoming;
|
|
280
272
|
const incomingUpdatedAt = Number(incoming.updatedAt);
|
|
281
273
|
if (!Number.isFinite(deletedAt[key]) || incomingUpdatedAt > deletedAt[key])
|
|
282
274
|
delete deletedAt[key];
|
|
@@ -555,11 +555,11 @@ export class BivyCredentialStore {
|
|
|
555
555
|
// tombstone-newer-wins — see document.ts).
|
|
556
556
|
const incoming = migrateToV3({ v: 2, providers: snapshot, deletedAt });
|
|
557
557
|
// The v2 wire carries no record metadata, so migrateToV3 synthesizes
|
|
558
|
-
// defaults (sync: "account", origin: "bivy"
|
|
559
|
-
//
|
|
560
|
-
//
|
|
561
|
-
//
|
|
562
|
-
//
|
|
558
|
+
// defaults (sync: "account", origin: "bivy"). Those defaults must not
|
|
559
|
+
// clobber local intent — a machine-only sync tier — when only the token
|
|
560
|
+
// content is newer (e.g. an agent's own TUI refreshed the OAuth token
|
|
561
|
+
// set). Inherit the existing record's metadata; the merge engine still
|
|
562
|
+
// decides freshness.
|
|
563
563
|
for (const [key, record] of Object.entries(incoming.credentials)) {
|
|
564
564
|
const existing = document.credentials[key];
|
|
565
565
|
if (!existing)
|
|
@@ -568,7 +568,6 @@ export class BivyCredentialStore {
|
|
|
568
568
|
...record,
|
|
569
569
|
sync: existing.sync,
|
|
570
570
|
origin: existing.origin,
|
|
571
|
-
...(existing.unattended !== undefined ? { unattended: existing.unattended } : {}),
|
|
572
571
|
};
|
|
573
572
|
}
|
|
574
573
|
const result = mergeDocuments(document, incoming.credentials, incoming.deletedAt);
|
package/dist/node-config.js
CHANGED
|
@@ -76,7 +76,7 @@ export function validateNodeConfig(value) {
|
|
|
76
76
|
const safety = section(root, "safety", ["maxSandbox", "approvalFloor"], errors);
|
|
77
77
|
const sessions = section(root, "sessions", ["sync", "worktreeSync", "standbyNodeId", "resume", "autoAttachToolImages", "appScreenshots", "previewBadge", "forkWorkspaceMaxBytes", "wedgedTurnMinutes"], errors);
|
|
78
78
|
const github = section(root, "github", ["issuePrompt"], errors);
|
|
79
|
-
const automation = section(root, "automation", ["checks", "checkTimeoutMinutes"], errors);
|
|
79
|
+
const automation = section(root, "automation", ["checks", "checkTimeoutMinutes", "slackPrompts"], errors);
|
|
80
80
|
const agentsRaw = section(root, "agents", Object.keys(record(root.agents) ?? {}), errors);
|
|
81
81
|
const environment = section(root, "environment", Object.keys(record(root.environment) ?? {}), errors);
|
|
82
82
|
const workspace = optionalString(node.workspace, "node.workspace", errors);
|
|
@@ -157,6 +157,7 @@ export function validateNodeConfig(value) {
|
|
|
157
157
|
}
|
|
158
158
|
}
|
|
159
159
|
const checkTimeoutMinutes = optionalInteger(automation.checkTimeoutMinutes, "automation.checkTimeoutMinutes", errors, 1, 30);
|
|
160
|
+
const slackPrompts = optionalBoolean(automation.slackPrompts, "automation.slackPrompts", errors);
|
|
160
161
|
const agents = {};
|
|
161
162
|
for (const [id, raw] of Object.entries(agentsRaw)) {
|
|
162
163
|
const at = `agents.${id}`;
|
|
@@ -220,7 +221,7 @@ export function validateNodeConfig(value) {
|
|
|
220
221
|
...(Object.keys(safety).length ? { safety: { maxSandbox, approvalFloor } } : {}),
|
|
221
222
|
...(Object.keys(sessions).length ? { sessions: { sync, worktreeSync, standbyNodeId, resume, autoAttachToolImages, appScreenshots, previewBadge, forkWorkspaceMaxBytes, wedgedTurnMinutes } } : {}),
|
|
222
223
|
...(Object.keys(github).length ? { github: { issuePrompt } } : {}),
|
|
223
|
-
...(Object.keys(automation).length ? { automation: { checks, checkTimeoutMinutes } } : {}),
|
|
224
|
+
...(Object.keys(automation).length ? { automation: { checks, checkTimeoutMinutes, slackPrompts } } : {}),
|
|
224
225
|
...(Object.keys(agents).length ? { agents } : {}),
|
|
225
226
|
...(Object.keys(envOut).length ? { environment: envOut } : {}),
|
|
226
227
|
};
|
|
@@ -368,7 +369,7 @@ export function setConfigValue(config, dotted, value) {
|
|
|
368
369
|
"defaults.agent", "defaults.model", "defaults.sandbox", "defaults.approval",
|
|
369
370
|
"safety.maxSandbox", "safety.approvalFloor",
|
|
370
371
|
"sessions.sync", "sessions.worktreeSync", "sessions.standbyNodeId", "sessions.resume", "sessions.autoAttachToolImages", "sessions.appScreenshots", "sessions.previewBadge", "sessions.forkWorkspaceMaxBytes", "sessions.wedgedTurnMinutes",
|
|
371
|
-
"github.issuePrompt", "automation.checks", "automation.checkTimeoutMinutes",
|
|
372
|
+
"github.issuePrompt", "automation.checks", "automation.checkTimeoutMinutes", "automation.slackPrompts",
|
|
372
373
|
]);
|
|
373
374
|
if (!allowed.has(dotted) && !/^agents\.[a-z][a-z0-9-]{1,47}$/.test(dotted) && !/^environment\.[A-Z][A-Z0-9_]+$/.test(dotted))
|
|
374
375
|
throw new Error(`Unknown configuration key: ${dotted}`);
|