@aliou/pi-ts-aperture 0.2.0 → 0.2.2

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/package.json CHANGED
@@ -1,18 +1,27 @@
1
1
  {
2
2
  "name": "@aliou/pi-ts-aperture",
3
3
  "description": "Route Pi LLM providers through Tailscale Aperture",
4
- "version": "0.2.0",
4
+ "version": "0.2.2",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "private": false,
8
+ "keywords": [
9
+ "pi-package",
10
+ "pi-extension",
11
+ "pi"
12
+ ],
5
13
  "repository": {
6
14
  "type": "git",
7
15
  "url": "https://github.com/aliou/pi-ts-aperture"
8
16
  },
9
- "keywords": [
10
- "pi-package"
11
- ],
12
17
  "publishConfig": {
13
18
  "access": "public",
14
19
  "provenance": true
15
20
  },
21
+ "files": [
22
+ "src",
23
+ "README.md"
24
+ ],
16
25
  "pi": {
17
26
  "extensions": [
18
27
  "./src/index.ts"
@@ -24,17 +33,8 @@
24
33
  },
25
34
  "peerDependencies": {
26
35
  "@mariozechner/pi-ai": ">=0.52.12",
27
- "@mariozechner/pi-coding-agent": ">=0.52.12"
28
- },
29
- "devDependencies": {
30
- "@biomejs/biome": "^2.3.13",
31
- "@changesets/cli": "^2.27.11",
32
- "@mariozechner/pi-coding-agent": "0.52.12",
33
- "@mariozechner/pi-tui": "0.52.12",
34
- "@sinclair/typebox": "^0.34.48",
35
- "@types/node": "^25.0.10",
36
- "husky": "^9.1.7",
37
- "typescript": "^5.9.3"
36
+ "@mariozechner/pi-coding-agent": ">=0.52.12",
37
+ "@mariozechner/pi-tui": ">=0.51.0"
38
38
  },
39
39
  "peerDependenciesMeta": {
40
40
  "@mariozechner/pi-coding-agent": {
@@ -42,14 +42,32 @@
42
42
  },
43
43
  "@mariozechner/pi-ai": {
44
44
  "optional": true
45
+ },
46
+ "@mariozechner/pi-tui": {
47
+ "optional": true
45
48
  }
46
49
  },
50
+ "devDependencies": {
51
+ "@aliou/biome-plugins": "^0.3.2",
52
+ "@biomejs/biome": "^2.3.13",
53
+ "@changesets/cli": "^2.27.11",
54
+ "@mariozechner/pi-coding-agent": "0.52.12",
55
+ "@sinclair/typebox": "^0.34.48",
56
+ "@types/node": "^25.0.10",
57
+ "@vitest/coverage-v8": "^4.0.18",
58
+ "husky": "^9.1.7",
59
+ "typescript": "^5.9.3",
60
+ "vitest": "^4.0.18"
61
+ },
47
62
  "scripts": {
48
63
  "typecheck": "tsc --noEmit",
49
64
  "lint": "biome check",
50
65
  "format": "biome check --write",
66
+ "check:lockfile": "pnpm install --frozen-lockfile --ignore-scripts",
51
67
  "changeset": "changeset",
52
68
  "version": "changeset version",
53
- "release": "pnpm changeset publish"
69
+ "release": "pnpm changeset publish",
70
+ "test": "vitest run tests/e2e.test.ts",
71
+ "test:watch": "vitest watch tests/"
54
72
  }
55
73
  }
package/src/index.ts CHANGED
@@ -14,46 +14,72 @@ import { registerApertureSettings } from "./commands/settings";
14
14
  import { registerSetupCommand } from "./commands/setup";
15
15
  import { configLoader } from "./config";
16
16
 
17
- function getApertureBaseUrl(): string | null {
18
- const config = configLoader.getConfig();
19
- if (!config.baseUrl || config.providers.length === 0) return null;
20
- return `${config.baseUrl.replace(/\/+$/, "")}/v1`;
17
+ /**
18
+ * Compute the full Aperture base URL from config, or null if not configured.
19
+ */
20
+ function resolveBaseUrl(): string | null {
21
+ const { baseUrl, providers } = configLoader.getConfig();
22
+ if (!baseUrl || providers.length === 0) return null;
23
+ return `${baseUrl.replace(/\/+$/, "")}/v1`;
24
+ }
25
+
26
+ /**
27
+ * Override provider registrations to route through Aperture.
28
+ * Preserves existing models so extensions that registered custom models
29
+ * before this runs don't lose them.
30
+ */
31
+ function overrideProviders(
32
+ registry: ExtensionContext["modelRegistry"],
33
+ providers: string[],
34
+ baseUrl: string,
35
+ ): void {
36
+ for (const provider of providers) {
37
+ const models = registry.getAll().filter((m) => m.provider === provider);
38
+
39
+ registry.registerProvider(provider, {
40
+ baseUrl,
41
+ apiKey: "-",
42
+ ...(models.length > 0 && { api: models[0].api, models }),
43
+ });
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Apply Aperture configuration to the model registry.
49
+ * Returns the list of providers that were overridden, or empty if no-op.
50
+ */
51
+ function applyAperture(registry: ExtensionContext["modelRegistry"]): string[] {
52
+ const url = resolveBaseUrl();
53
+ if (!url) return [];
54
+
55
+ const { providers } = configLoader.getConfig();
56
+ overrideProviders(registry, providers, url);
57
+ return providers;
21
58
  }
22
59
 
23
60
  export default async function (pi: ExtensionAPI): Promise<void> {
24
61
  await configLoader.load();
25
62
 
26
- const config = configLoader.getConfig();
27
- let lastRegisteredProviders = [...config.providers];
63
+ let lastRegisteredProviders = [...configLoader.getConfig().providers];
28
64
 
29
- // At load time, pi.registerProvider() queue is flushed by the runner.
30
- const baseUrl = getApertureBaseUrl();
31
- if (baseUrl) {
32
- for (const provider of config.providers) {
33
- pi.registerProvider(provider, { baseUrl, apiKey: "-" });
34
- }
35
- }
65
+ // Apply after all extensions have registered their providers and models.
66
+ pi.events.on("before_agent_start", async (data) => {
67
+ const ctx = data as ExtensionContext;
68
+ if (!ctx?.modelRegistry) return;
69
+ applyAperture(ctx.modelRegistry);
70
+ });
36
71
 
37
72
  const onSetupComplete = (ctx: ExtensionContext) => {
38
- const cfg = configLoader.getConfig();
39
- const removedProviders = lastRegisteredProviders.filter(
40
- (p) => !cfg.providers.includes(p),
73
+ const { providers } = configLoader.getConfig();
74
+ const removed = lastRegisteredProviders.filter(
75
+ (p) => !providers.includes(p),
41
76
  );
42
77
 
43
- const url = getApertureBaseUrl();
44
- if (url) {
45
- for (const provider of cfg.providers) {
46
- ctx.modelRegistry.registerProvider(provider, {
47
- baseUrl: url,
48
- apiKey: "-",
49
- });
50
- }
51
- }
52
- lastRegisteredProviders = [...cfg.providers];
78
+ applyAperture(ctx.modelRegistry);
79
+ lastRegisteredProviders = [...providers];
53
80
 
54
- // The active model is a snapshot. If it belongs to a provider we just
55
- // reconfigured, re-resolve it so the new baseUrl takes effect.
56
- if (ctx.model && cfg.providers.includes(ctx.model.provider)) {
81
+ // Re-resolve active model if it belongs to a reconfigured provider.
82
+ if (ctx.model && providers.includes(ctx.model.provider)) {
57
83
  const updated = ctx.modelRegistry.find(ctx.model.provider, ctx.model.id);
58
84
  if (updated) {
59
85
  ctx.ui.notify(
@@ -64,9 +90,9 @@ export default async function (pi: ExtensionAPI): Promise<void> {
64
90
  }
65
91
  }
66
92
 
67
- if (removedProviders.length > 0) {
93
+ if (removed.length > 0) {
68
94
  ctx.ui.notify(
69
- `Removed providers (${removedProviders.join(", ")}) will revert after /reload`,
95
+ `Removed providers (${removed.join(", ")}) will revert after /reload`,
70
96
  "warning",
71
97
  );
72
98
  }
@@ -1,10 +0,0 @@
1
- {
2
- "$schema": "https://unpkg.com/@changesets/config@3.1.1/schema.json",
3
- "changelog": "@changesets/cli/changelog",
4
- "commit": false,
5
- "fixed": [],
6
- "linked": [],
7
- "access": "public",
8
- "baseBranch": "main",
9
- "updateInternalDependencies": "patch"
10
- }
@@ -1,31 +0,0 @@
1
- name: CI
2
-
3
- on:
4
- push:
5
- branches:
6
- - main
7
- pull_request:
8
-
9
- jobs:
10
- check:
11
- runs-on: ubuntu-latest
12
- steps:
13
- - name: Checkout
14
- uses: actions/checkout@v4
15
-
16
- - name: Setup Node
17
- uses: actions/setup-node@v4
18
- with:
19
- node-version: 22
20
-
21
- - name: Setup pnpm
22
- uses: pnpm/action-setup@v4
23
-
24
- - name: Install dependencies
25
- run: pnpm install --frozen-lockfile
26
-
27
- - name: Lint
28
- run: pnpm run lint
29
-
30
- - name: Typecheck
31
- run: pnpm run typecheck
@@ -1,82 +0,0 @@
1
- name: Publish
2
-
3
- on:
4
- workflow_run:
5
- workflows: ["CI"]
6
- types: [completed]
7
-
8
- concurrency:
9
- group: ${{ github.workflow }}-${{ github.event.workflow_run.head_branch }}
10
- cancel-in-progress: true
11
-
12
- jobs:
13
- publish:
14
- name: Publish
15
- if: >-
16
- github.event.workflow_run.conclusion == 'success' &&
17
- github.event.workflow_run.head_branch == 'main'
18
- runs-on: ubuntu-latest
19
- permissions:
20
- contents: write
21
- packages: write
22
- pull-requests: write
23
- id-token: write
24
-
25
- steps:
26
- - name: Checkout
27
- uses: actions/checkout@v4
28
- with:
29
- ref: ${{ github.event.workflow_run.head_sha }}
30
- fetch-depth: 0
31
-
32
- - name: Setup pnpm
33
- uses: pnpm/action-setup@v4
34
-
35
- - name: Setup Node.js
36
- uses: actions/setup-node@v4
37
- with:
38
- node-version: "22"
39
- registry-url: "https://registry.npmjs.org"
40
- scope: "@aliou"
41
- cache: "pnpm"
42
-
43
- - name: Upgrade npm for OIDC support
44
- run: npm install -g npm@latest
45
-
46
- - name: Install dependencies
47
- run: pnpm install --frozen-lockfile
48
-
49
- - name: Get release info
50
- id: release-info
51
- run: |
52
- pnpm changeset status --output=release.json 2>/dev/null || echo '{"releases":[]}' > release.json
53
- node <<NODE
54
- const fs = require('fs');
55
- const release = JSON.parse(fs.readFileSync('release.json', 'utf8'));
56
- const releases = release.releases?.filter(r => r.type !== 'none') || [];
57
-
58
- let title = 'Version Packages';
59
- let commit = 'Version Packages';
60
- if (releases.length >= 1) {
61
- const version = releases[0].newVersion;
62
- title = version;
63
- commit = version;
64
- }
65
-
66
- fs.appendFileSync(process.env.GITHUB_OUTPUT, 'title=' + title + '\n');
67
- fs.appendFileSync(process.env.GITHUB_OUTPUT, 'commit=' + commit + '\n');
68
- NODE
69
- rm -f release.json
70
- continue-on-error: true
71
-
72
- - name: Create Release PR or Publish
73
- id: changesets
74
- uses: changesets/action@v1
75
- with:
76
- version: pnpm changeset version
77
- publish: pnpm changeset publish
78
- title: ${{ steps.release-info.outputs.title || 'Version Packages' }}
79
- commit: ${{ steps.release-info.outputs.commit || 'Version Packages' }}
80
- env:
81
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
82
- NPM_CONFIG_PROVENANCE: true
package/AGENTS.md DELETED
@@ -1,29 +0,0 @@
1
- # pi-ts-aperture
2
-
3
- Pi extension that routes LLM providers through Tailscale Aperture.
4
-
5
- ## Structure
6
-
7
- - `src/index.ts` - Entry point. Loads config, registers providers, registers commands.
8
- - `src/config.ts` - Config schema (`ApertureConfig`, `ResolvedConfig`) and `ConfigLoader` instance.
9
- - `src/commands/setup.ts` - `/aperture:setup` interactive wizard (URL input + provider multi-select).
10
- - `src/commands/settings.ts` - `/aperture:settings` settings UI via `registerSettingsCommand`.
11
-
12
- ## Key decisions
13
-
14
- - Config is global-only (no per-project scope). Aperture is a network-level concern.
15
- - Provider list comes from `getProviders()` in `@mariozechner/pi-ai`, not hardcoded.
16
- - `apiKey` is set to `"-"` because Aperture ignores client-provided keys.
17
- - URLs are normalized on input: `http://` is prepended if missing, trailing `/v1` is stripped (appended at registration time).
18
-
19
- ## Dependencies
20
-
21
- - `@aliou/pi-utils-settings` - Config loader and settings command infrastructure.
22
- - `@mariozechner/pi-ai` - `getProviders()` for the known provider list.
23
- - `@mariozechner/pi-coding-agent` - Extension API, `getSettingsListTheme`.
24
- - `@mariozechner/pi-tui` - TUI components (`Input`, `Key`, `matchesKey`, `FuzzySelector`).
25
-
26
- ## Publishing
27
-
28
- - Manual publish for 0.0.1, then changesets + GitHub Actions for subsequent versions.
29
- - CI runs lint + typecheck on push/PR. Publish workflow triggers after CI succeeds on main.
package/CHANGELOG.md DELETED
@@ -1,28 +0,0 @@
1
- # @aliou/pi-ts-aperture
2
-
3
- ## 0.2.0
4
-
5
- ### Minor Changes
6
-
7
- - 926f0a9: Improve `/aperture:setup` provider and connectivity flow.
8
-
9
- - Add URL health check during setup (`/v1/models`) before provider selection, with retry/cancel UX.
10
- - Build provider choices from Pi's runtime model registry so extension-registered providers (for example `pi-synthetic`) appear in the setup list.
11
-
12
- ### Patch Changes
13
-
14
- - 2263fc2: mark pi SDK peer deps as optional to prevent koffi OOM in Gondolin VMs
15
-
16
- ## 0.1.0
17
-
18
- ### Minor Changes
19
-
20
- - ebb9556: Initial release. Route Pi LLM providers through Tailscale Aperture.
21
-
22
- - `/aperture:setup` interactive wizard (base URL + provider multi-select)
23
- - `/aperture:settings` settings UI for updating configuration
24
- - Auto-registers selected providers with Aperture base URL on load
25
-
26
- ### Patch Changes
27
-
28
- - 7388139: Fix providers not taking effect immediately after setup/settings save. Register directly on modelRegistry and re-resolve the active model when it belongs to a reconfigured provider.
package/biome.json DELETED
@@ -1,30 +0,0 @@
1
- {
2
- "$schema": "https://biomejs.dev/schemas/2.4.2/schema.json",
3
- "vcs": {
4
- "enabled": true,
5
- "clientKind": "git",
6
- "useIgnoreFile": true
7
- },
8
- "files": {
9
- "includes": ["**/*.ts", "**/*.json"],
10
- "ignoreUnknown": true
11
- },
12
- "assist": {
13
- "actions": {
14
- "source": {
15
- "organizeImports": "on"
16
- }
17
- }
18
- },
19
- "linter": {
20
- "enabled": true,
21
- "rules": {
22
- "recommended": true
23
- }
24
- },
25
- "formatter": {
26
- "enabled": true,
27
- "indentStyle": "space",
28
- "indentWidth": 2
29
- }
30
- }
package/shell.nix DELETED
@@ -1,10 +0,0 @@
1
- {
2
- pkgs ? import <nixpkgs> { },
3
- }:
4
-
5
- pkgs.mkShell {
6
- buildInputs = with pkgs; [
7
- nodejs
8
- pnpm_10
9
- ];
10
- }
package/tsconfig.json DELETED
@@ -1,17 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "ESNext",
5
- "moduleResolution": "bundler",
6
- "strict": true,
7
- "esModuleInterop": true,
8
- "skipLibCheck": true,
9
- "forceConsistentCasingInFileNames": true,
10
- "resolveJsonModule": true,
11
- "noEmit": true,
12
- "jsx": "react-jsx",
13
- "jsxImportSource": "@mariozechner/pi-tui"
14
- },
15
- "include": ["src/**/*"],
16
- "exclude": ["node_modules"]
17
- }