# pi-provider-utils

Shared provider mirror, stream, and agent-path helpers for Pi extension packages.

## Status

This package extracts duplicated helper code from `pi-credential-vault` and `pi-multicodex` into a single shared dependency. It provides generic provider and agent utilities — not vault-specific or multicodex-specific policy.

Current consumers:

- `@victor-software-house/pi-credential-vault`
- `@victor-software-house/pi-multicodex`

Current next steps:

1. publish this package to npm
2. switch consuming repos from `link:` to npm dependency
3. adopt pnpm workspace and Turborepo once the published package is the common dependency boundary

## Install

This package is a shared library, not a standalone Pi extension. Do not install it with `pi install`.

Consume it from another package instead:

```json
{
  "peerDependencies": {
    "@victor-software-house/pi-provider-utils": "*"
  },
  "devDependencies": {
    "@victor-software-house/pi-provider-utils": "*"
  }
}
```

For local multi-repo development before publication, use a `link:` dev dependency:

```json
{
  "devDependencies": {
    "@victor-software-house/pi-provider-utils": "link:../pi-provider-utils"
  }
}
```

## Entrypoints

### `@victor-software-house/pi-provider-utils/providers`

Provider mirror metadata and model-registry helpers.

```typescript
import {
  mirrorProvider,
  listProviderIdsWithModels,
  type MirroredProvider,
  type MirroredModelDef,
} from "@victor-software-house/pi-provider-utils/providers";

// Mirror an existing provider's configuration for re-registration
const mirror = mirrorProvider("openai");

// List all provider IDs that have registered models
const ids = listProviderIdsWithModels();
```

### `@victor-software-house/pi-provider-utils/streams`

Stream and error primitives for extension-owned provider wrappers.

```typescript
import {
  normalizeUnknownError,
  createErrorAssistantMessage,
  pushErrorEvent,
  createImmediateErrorStream,
  pipeAssistantStream,
  rewriteProviderOnEvent,
  createLinkedAbortController,
  createTimeoutController,
} from "@victor-software-house/pi-provider-utils/streams";

// Normalize an unknown thrown value into a string
const msg = normalizeUnknownError(error);

// Create a stream that immediately emits an error
const stream = createImmediateErrorStream(model, "no credentials");

// Pipe events from one stream to another
await pipeAssistantStream(source, target);

// Rewrite the provider field on stream events
const rewritten = rewriteProviderOnEvent(event, "my-provider");

// Create an AbortController linked to a parent signal
const controller = createLinkedAbortController(parentSignal);

// Create a linked controller that auto-aborts after a timeout
const { controller: tc, clear } = createTimeoutController(signal, 30_000);
```

### `@victor-software-house/pi-provider-utils/agent-paths`

Canonical `~/.pi/agent/*` path helpers and JSON file I/O.

```typescript
import {
  getAgentPath,
  getAgentSettingsPath,
  getAgentAuthPath,
  readJsonObjectFile,
  writeJsonObjectFile,
  ensureParentDir,
} from "@victor-software-house/pi-provider-utils/agent-paths";

// Resolve a path relative to ~/.pi/agent/
const vaultPath = getAgentPath("vault.age.json");

// Read and write JSON object files (sync)
const settings = readJsonObjectFile(getAgentSettingsPath());
settings["my-extension"] = { enabled: true };
writeJsonObjectFile(getAgentSettingsPath(), settings);

// Async variants are also available
import {
  readJsonObjectFileAsync,
  writeJsonObjectFileAsync,
} from "@victor-software-house/pi-provider-utils/agent-paths";
```

## What this package does NOT contain

- OAuth login or refresh orchestration
- Quota classification or retry policy
- Vault backend registry logic
- Multicodex account selection logic
- Footer rendering or settings-panel components

These belong in the owning extension packages (`pi-credential-vault`, `pi-multicodex`), not in shared utilities.

## Development

```bash
pnpm install
pnpm typecheck
pnpm test
npm pack --dry-run
```

Validation status at extraction time:

- 52 tests covering all three entrypoints
- `pnpm typecheck` passes
- `npm pack --dry-run` includes only the library contract and tests
