@bivy/bivy 0.16.18-staging.6 → 0.16.18-staging.7

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.
@@ -423,7 +423,7 @@ export class PiRuntime {
423
423
  modelsPath: path.join(piDir, "models.json"),
424
424
  allowModelNetwork,
425
425
  })
426
- : await createPiModelRuntime({ credsDir, piDir, allowModelNetwork });
426
+ : await createPiModelRuntime({ credsDir, piDir, allowModelNetwork, workspace: sessionManager.getCwd() || options.workspace });
427
427
  const backgroundShells = new BackgroundShellTracker();
428
428
  const createRuntime = async ({ cwd, agentDir, sessionManager, sessionStartEvent }) => {
429
429
  const sessionId = sessionManager.getSessionId();
@@ -10,23 +10,12 @@
10
10
  //
11
11
  // This keeps Bivy's hot credential path decoupled from Pi: Pi is just another
12
12
  // agent that reads the same store.
13
- import path from "node:path";
14
13
  import { createCredentialVault } from "./store.js";
15
- import { resolveCredential } from "./records.js";
14
+ import { selectCredential } from "./selection.js";
15
+ export { projectIdsFromWorkspace } from "./selection.js";
16
16
  import { loadPresets, defaultPresetsPath } from "./presets.js";
17
17
  /** Refresh an OAuth token this many ms before it expires (clock-skew guard). */
18
18
  const OAUTH_REFRESH_SKEW_MS = 60_000;
19
- /** Stable project identifiers discoverable without importing repo/session code. */
20
- export function projectIdsFromWorkspace(workspace) {
21
- const resolved = path.resolve(workspace);
22
- const ids = new Set([resolved, path.basename(resolved)]);
23
- for (const part of resolved.split(path.sep)) {
24
- const split = part.indexOf("__");
25
- if (split > 0 && split < part.length - 2)
26
- ids.add(`${part.slice(0, split)}/${part.slice(split + 2)}`);
27
- }
28
- return [...ids];
29
- }
30
19
  /** Resolver over Bivy's credential store, with OAuth refresh-on-read via the bridge. */
31
20
  export class NodeCredentialResolver {
32
21
  credsDir;
@@ -59,14 +48,7 @@ export class NodeCredentialResolver {
59
48
  // than guessing.
60
49
  const records = await this.store.listRecords().catch(() => []);
61
50
  const presets = this.presets();
62
- // Project assignments are ordinary preset mappings named `project:<id>`.
63
- // Bivy-managed clones encode owner/repo as owner__repo in their workspace
64
- // path; direct local workspaces also match their absolute path/basename.
65
- const explicitProject = context?.project?.trim();
66
- const workspace = context?.workspace?.trim();
67
- const projectCandidates = [explicitProject, ...(workspace ? projectIdsFromWorkspace(workspace) : [])].filter((value) => Boolean(value));
68
- const projectPreset = projectCandidates.map((value) => `project:${value}`).find((name) => presets.presets?.[name]?.[id]);
69
- const selection = resolveCredential(id, records, presets, { ...(projectPreset ? { preset: projectPreset } : {}), ...(context?.preferLabel ? { preferLabel: context.preferLabel } : {}) });
51
+ const selection = selectCredential(id, records, presets, context);
70
52
  if (!selection)
71
53
  return undefined;
72
54
  const source = selection.record.source;
@@ -0,0 +1,35 @@
1
+ import { defaultPresetsPath, loadPresets } from "./presets.js";
2
+ import { selectCredential } from "./selection.js";
3
+ /** A provider-addressed view of the vault for consumers without labeled accounts.
4
+ * Resolve on every operation; never copy a work credential into the default slot.
5
+ * Refresh writes stay attached to the selected record's label and metadata.
6
+ */
7
+ export function selectedCredentialStore(store, credsDir, context) {
8
+ const select = async (provider) => selectCredential(provider, await store.listRecords(), loadPresets(defaultPresetsPath(credsDir)), context)?.record;
9
+ return {
10
+ async read(provider) {
11
+ const record = await select(provider);
12
+ if (record?.source.kind !== "stored")
13
+ return undefined;
14
+ const { updatedAt: _updatedAt, ...credential } = record.source.cred;
15
+ return credential;
16
+ },
17
+ async list() {
18
+ const records = await store.listRecords();
19
+ const presets = loadPresets(defaultPresetsPath(credsDir));
20
+ return [...new Set(records.map((r) => r.provider))].flatMap((providerId) => {
21
+ const record = selectCredential(providerId, records, presets, context)?.record;
22
+ if (record?.source.kind !== "stored")
23
+ return [];
24
+ const credential = record.source.cred;
25
+ return [{ providerId, type: credential.type, ...(credential.type === "oauth" ? { expiresAt: credential.expires } : {}) }];
26
+ });
27
+ },
28
+ async modify(provider, fn) {
29
+ const record = await select(provider);
30
+ if (!record)
31
+ throw new Error(`No account selected for ${provider}`);
32
+ return store.modifyRecord(provider, record.label, fn);
33
+ },
34
+ };
35
+ }
@@ -0,0 +1,24 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ import path from "node:path";
3
+ import { resolveCredential } from "./records.js";
4
+ /** Stable project identifiers discoverable without importing repo/session code. */
5
+ export function projectIdsFromWorkspace(workspace) {
6
+ const resolved = path.resolve(workspace);
7
+ const ids = new Set([resolved, path.basename(resolved)]);
8
+ for (const part of resolved.split(path.sep)) {
9
+ const split = part.indexOf("__");
10
+ if (split > 0 && split < part.length - 2)
11
+ ids.add(`${part.slice(0, split)}/${part.slice(split + 2)}`);
12
+ }
13
+ return [...ids];
14
+ }
15
+ export function selectCredential(provider, records, presets, context) {
16
+ const id = provider.trim().toLowerCase();
17
+ const workspace = context?.workspace?.trim();
18
+ const projects = [context?.project?.trim(), ...(workspace ? projectIdsFromWorkspace(workspace) : [])].filter(Boolean);
19
+ const projectPreset = projects.map((value) => `project:${value}`).find((name) => presets.presets?.[name]?.[id]);
20
+ return resolveCredential(id, records, presets, {
21
+ ...(projectPreset ? { preset: projectPreset } : {}),
22
+ ...(context?.preferLabel ? { preferLabel: context.preferLabel } : {}),
23
+ });
24
+ }
@@ -11,6 +11,7 @@
11
11
  import path from "node:path";
12
12
  import { createCredentialVault } from "./credential-store.js";
13
13
  import { isNativeOAuthProvider } from "./oauth/model-oauth-providers.js";
14
+ import { selectedCredentialStore } from "../credentials/selected-store.js";
14
15
  /** Adapt Bivy's store to pi-ai's structurally-identical CredentialStore for injection. */
15
16
  export function piCredentialStore(store) {
16
17
  return store;
@@ -26,7 +27,7 @@ export async function createPiModelRuntime(opts) {
26
27
  const store = opts.store ?? createCredentialVault(opts.credsDir);
27
28
  const { ModelRuntime } = await import("@earendil-works/pi-coding-agent");
28
29
  return ModelRuntime.create({
29
- credentials: piCredentialStore(store),
30
+ credentials: piCredentialStore(selectedCredentialStore(store, opts.credsDir, { workspace: opts.workspace })),
30
31
  modelsPath: path.join(opts.piDir, "models.json"),
31
32
  allowModelNetwork: opts.allowModelNetwork ?? false,
32
33
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bivy/bivy",
3
- "version": "0.16.18-staging.6",
3
+ "version": "0.16.18-staging.7",
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.",