@aliou/pi-ts-aperture 0.2.1 → 0.2.3

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.
Files changed (2) hide show
  1. package/package.json +11 -7
  2. package/src/index.ts +63 -34
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@aliou/pi-ts-aperture",
3
3
  "description": "Route Pi LLM providers through Tailscale Aperture",
4
- "version": "0.2.1",
4
+ "version": "0.2.3",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "private": false,
@@ -32,9 +32,9 @@
32
32
  "@aliou/pi-utils-settings": "^0.4.0"
33
33
  },
34
34
  "peerDependencies": {
35
- "@mariozechner/pi-ai": ">=0.52.12",
36
- "@mariozechner/pi-coding-agent": ">=0.52.12",
37
- "@mariozechner/pi-tui": ">=0.51.0"
35
+ "@mariozechner/pi-ai": ">=0.55.3",
36
+ "@mariozechner/pi-coding-agent": ">=0.55.3",
37
+ "@mariozechner/pi-tui": ">=0.55.3"
38
38
  },
39
39
  "peerDependenciesMeta": {
40
40
  "@mariozechner/pi-coding-agent": {
@@ -51,11 +51,13 @@
51
51
  "@aliou/biome-plugins": "^0.3.2",
52
52
  "@biomejs/biome": "^2.3.13",
53
53
  "@changesets/cli": "^2.27.11",
54
- "@mariozechner/pi-coding-agent": "0.52.12",
54
+ "@mariozechner/pi-coding-agent": "0.55.3",
55
55
  "@sinclair/typebox": "^0.34.48",
56
56
  "@types/node": "^25.0.10",
57
+ "@vitest/coverage-v8": "^4.0.18",
57
58
  "husky": "^9.1.7",
58
- "typescript": "^5.9.3"
59
+ "typescript": "^5.9.3",
60
+ "vitest": "^4.0.18"
59
61
  },
60
62
  "scripts": {
61
63
  "typecheck": "tsc --noEmit",
@@ -64,6 +66,8 @@
64
66
  "check:lockfile": "pnpm install --frozen-lockfile --ignore-scripts",
65
67
  "changeset": "changeset",
66
68
  "version": "changeset version",
67
- "release": "pnpm changeset publish"
69
+ "release": "pnpm changeset publish",
70
+ "test": "vitest run tests/e2e.test.ts",
71
+ "test:watch": "vitest watch tests/"
68
72
  }
69
73
  }
package/src/index.ts CHANGED
@@ -10,50 +10,82 @@ import type {
10
10
  ExtensionAPI,
11
11
  ExtensionContext,
12
12
  } from "@mariozechner/pi-coding-agent";
13
+ import { VERSION } from "@mariozechner/pi-coding-agent";
13
14
  import { registerApertureSettings } from "./commands/settings";
14
15
  import { registerSetupCommand } from "./commands/setup";
15
16
  import { configLoader } from "./config";
16
17
 
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`;
18
+ /**
19
+ * Compute the full Aperture base URL from config, or null if not configured.
20
+ */
21
+ function resolveBaseUrl(): string | null {
22
+ const { baseUrl, providers } = configLoader.getConfig();
23
+ if (!baseUrl || providers.length === 0) return null;
24
+ return `${baseUrl.replace(/\/+$/, "")}/v1`;
25
+ }
26
+
27
+ /**
28
+ * Override provider registrations to route through Aperture.
29
+ * Preserves existing models so extensions that registered custom models
30
+ * before this runs don't lose them.
31
+ */
32
+ function overrideProviders(
33
+ pi: ExtensionAPI,
34
+ registry: ExtensionContext["modelRegistry"],
35
+ providers: string[],
36
+ baseUrl: string,
37
+ ): void {
38
+ for (const provider of providers) {
39
+ const models = registry.getAll().filter((m) => m.provider === provider);
40
+
41
+ pi.registerProvider(provider, {
42
+ baseUrl,
43
+ apiKey: "-",
44
+ ...(models.length > 0 && { api: models[0].api, models }),
45
+ });
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Apply Aperture configuration to the model registry.
51
+ * Returns the list of providers that were overridden, or empty if no-op.
52
+ */
53
+ function applyAperture(
54
+ pi: ExtensionAPI,
55
+ registry: ExtensionContext["modelRegistry"],
56
+ ): string[] {
57
+ const url = resolveBaseUrl();
58
+ if (!url) return [];
59
+
60
+ const { providers } = configLoader.getConfig();
61
+ overrideProviders(pi, registry, providers, url);
62
+ return providers;
21
63
  }
22
64
 
23
65
  export default async function (pi: ExtensionAPI): Promise<void> {
66
+ console.log(`[pi-ts-aperture] loaded on pi ${VERSION}`);
24
67
  await configLoader.load();
25
68
 
26
- const config = configLoader.getConfig();
27
- let lastRegisteredProviders = [...config.providers];
69
+ let lastRegisteredProviders = [...configLoader.getConfig().providers];
28
70
 
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
- }
71
+ // Apply after all extensions have registered their providers and models.
72
+ pi.events.on("before_agent_start", async (data) => {
73
+ const ctx = data as ExtensionContext;
74
+ if (!ctx?.modelRegistry) return;
75
+ applyAperture(pi, ctx.modelRegistry);
76
+ });
36
77
 
37
78
  const onSetupComplete = (ctx: ExtensionContext) => {
38
- const cfg = configLoader.getConfig();
39
- const removedProviders = lastRegisteredProviders.filter(
40
- (p) => !cfg.providers.includes(p),
79
+ const { providers } = configLoader.getConfig();
80
+ const removed = lastRegisteredProviders.filter(
81
+ (p) => !providers.includes(p),
41
82
  );
42
83
 
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];
84
+ applyAperture(pi, ctx.modelRegistry);
85
+ lastRegisteredProviders = [...providers];
53
86
 
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)) {
87
+ // Re-resolve active model if it belongs to a reconfigured provider.
88
+ if (ctx.model && providers.includes(ctx.model.provider)) {
57
89
  const updated = ctx.modelRegistry.find(ctx.model.provider, ctx.model.id);
58
90
  if (updated) {
59
91
  ctx.ui.notify(
@@ -64,11 +96,8 @@ export default async function (pi: ExtensionAPI): Promise<void> {
64
96
  }
65
97
  }
66
98
 
67
- if (removedProviders.length > 0) {
68
- ctx.ui.notify(
69
- `Removed providers (${removedProviders.join(", ")}) will revert after /reload`,
70
- "warning",
71
- );
99
+ for (const p of removed) {
100
+ pi.unregisterProvider(p);
72
101
  }
73
102
  };
74
103