@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 +34 -16
- package/src/index.ts +57 -31
- package/.changeset/config.json +0 -10
- package/.github/workflows/ci.yml +0 -31
- package/.github/workflows/publish.yml +0 -82
- package/AGENTS.md +0 -29
- package/CHANGELOG.md +0 -28
- package/biome.json +0 -30
- package/shell.nix +0 -10
- package/tsconfig.json +0 -17
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.
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
27
|
-
let lastRegisteredProviders = [...config.providers];
|
|
63
|
+
let lastRegisteredProviders = [...configLoader.getConfig().providers];
|
|
28
64
|
|
|
29
|
-
//
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
|
39
|
-
const
|
|
40
|
-
(p) => !
|
|
73
|
+
const { providers } = configLoader.getConfig();
|
|
74
|
+
const removed = lastRegisteredProviders.filter(
|
|
75
|
+
(p) => !providers.includes(p),
|
|
41
76
|
);
|
|
42
77
|
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
//
|
|
55
|
-
|
|
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 (
|
|
93
|
+
if (removed.length > 0) {
|
|
68
94
|
ctx.ui.notify(
|
|
69
|
-
`Removed providers (${
|
|
95
|
+
`Removed providers (${removed.join(", ")}) will revert after /reload`,
|
|
70
96
|
"warning",
|
|
71
97
|
);
|
|
72
98
|
}
|
package/.changeset/config.json
DELETED
package/.github/workflows/ci.yml
DELETED
|
@@ -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
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
|
-
}
|