ns-omp-provider 0.0.0-stage → 0.2.0

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/CHANGELOG.md ADDED
@@ -0,0 +1,27 @@
1
+ # Changelog
2
+
3
+ ## 0.2.0
4
+
5
+ Bundles `@ngosangns/ns-pi-provider@0.2.0` (was 0.1.7).
6
+
7
+ - **Breaking (kiro):** the Kiro catalog now comes only from `ListAvailableModels`.
8
+ Without a Kiro credential or a saved catalog snapshot the `kiro` provider
9
+ registers with zero models, and derived `-1m` variants (e.g.
10
+ `claude-sonnet-4-6-1m`) are gone. Wire model ids are sent in Kiro's dot form
11
+ (`claude-sonnet-4.5`), fixing `INVALID_MODEL_ID` on discovered models. Users
12
+ with `NS_OMP_PROVIDER_DISABLE=kiro` are unaffected.
13
+ - fix(grok): `session_shutdown` disposes only that session's Grok ACP agent
14
+ (upstream pi-grok-sdk `45fde95`); switching or closing one session no longer
15
+ kills other sessions' agents, and one process-exit hook is installed per
16
+ process instead of per registration.
17
+ - No compat-shim changes needed: 0.2.0 adds no new `@earendil-works/pi-ai`
18
+ runtime imports.
19
+
20
+ ## 0.1.0
21
+
22
+ - Initial release: OMP wrapper around `@ngosangns/ns-pi-provider` 0.1.7.
23
+ - Bundles the Pi providers with shims for the missing Pi 1.0 transcript helpers
24
+ (`getCurrentSystemPrompt` / `getCurrentTools` / `withoutInitialSystemMessage`)
25
+ and adapts OMP contexts, `fetchDynamicModels`, apiKey syntax, and session events.
26
+ - Providers: `kiro`, `devin`, `grok` (+ `grok-sdk` alias). Opt out of `kiro` with
27
+ `NS_OMP_PROVIDER_DISABLE=kiro` when `ns-omp-provider-kiro` is already installed.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ngosangns
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/NOTICE ADDED
@@ -0,0 +1,23 @@
1
+ ns-omp-provider
2
+ Copyright (c) 2026 ngosangns
3
+
4
+ This package wraps and redistributes (via bundling) the MIT-licensed
5
+ @ngosangns/ns-pi-provider sources. Upstream NOTICE from that package:
6
+
7
+ ----
8
+ ns-pi-provider
9
+ Copyright (c) 2026 ngosangns
10
+
11
+ This package adapts patterns and portions of the following MIT-licensed projects:
12
+
13
+ - pi-kiro-provider (MasuRii) — https://github.com/MasuRii/pi-kiro-provider
14
+ - pi-kiro-api (satiyap) — https://github.com/satiyap/pi-kiro-api
15
+ (which itself vendors streaming core from pi-kiro under MIT)
16
+ - pi-devin-provider (fadlee) — https://github.com/fadlee/pi-devin-provider
17
+ - pi-grok-sdk (Ankit Chouhan) — https://github.com/ankitchouhan1020/pi-grok-sdk
18
+ - grok-pi — model-cache / CLI bridge patterns
19
+ - Pi custom-provider docs and examples from badlogic/pi-mono / earendil-works
20
+
21
+ See each upstream LICENSE for full copyright notices. Adaptations are under the
22
+ MIT license of this package; original authorship remains with upstream authors.
23
+ ----
package/README.md CHANGED
@@ -1,3 +1,126 @@
1
- # Temporary Holding Version
1
+ # ns-omp-provider
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [OMP](https://omp.sh) (oh-my-pi) plugin that exposes the **Kiro**, **Devin**, and
4
+ **Grok** providers from [`@ngosangns/ns-pi-provider`](https://github.com/ngosangns/ns-pi-provider).
5
+
6
+ `ns-pi-provider` targets Pi 1.0. OMP is a Pi fork whose extension API is close
7
+ but not identical — this package is the adapter. It does **not** modify
8
+ `ns-pi-provider`; the Pi sources are bundled at build time with a small
9
+ compat layer.
10
+
11
+ | Provider | Auth | Notes |
12
+ |----------|------|-------|
13
+ | `kiro` | OAuth (`/login kiro`) + `KIRO_API_KEY` / SSO cache | Overlaps with [`ns-omp-provider-kiro`](https://www.npmjs.com/package/ns-omp-provider-kiro) — see below |
14
+ | `devin` | OAuth (`/login devin`) + `credentials.toml` / env | Swe-2 catalog when authenticated |
15
+ | `grok` (+ `grok-sdk` alias) | Local `grok` CLI / `~/.grok` / `XAI_API_KEY` | Commands: `/grok status \| models \| refresh` |
16
+
17
+ Shared command: `/ns-pi status | refresh [all\|kiro\|devin\|grok]`.
18
+
19
+ ## Install
20
+
21
+ ```bash
22
+ # from npm
23
+ omp plugin install ns-omp-provider
24
+
25
+ # from git
26
+ omp plugin install git:github.com/ngosangns/ns-omp-provider
27
+
28
+ # local link (dev)
29
+ omp plugin link ~/Github/ngosangns/ns-omp-provider
30
+
31
+ # one-shot without installing
32
+ omp -e ./dist/index.js ...
33
+ # or: omp models --no-extensions -e ./dist/index.js
34
+ ```
35
+
36
+ Then `/login kiro` or `/login devin` as needed. Grok uses the local `grok`
37
+ CLI / `~/.grok` auth (or `XAI_API_KEY`).
38
+
39
+ ### Avoiding a duplicate `kiro` provider
40
+
41
+ If you already use [`ns-omp-provider-kiro`](https://www.npmjs.com/package/ns-omp-provider-kiro),
42
+ disable this package's kiro registration:
43
+
44
+ ```bash
45
+ # in the shell that launches omp, or via your shell profile / omp env
46
+ export NS_OMP_PROVIDER_DISABLE=kiro
47
+ ```
48
+
49
+ Or allow-list only what you want: `NS_OMP_PROVIDERS=devin,grok`.
50
+
51
+ ## How the wrapper works
52
+
53
+ 1. **Build-time** — esbuild bundles `@ngosangns/ns-pi-provider` into
54
+ `dist/index.js`, rewriting `@earendil-works/pi-ai*` imports onto
55
+ `src/compat/` (OMP's `@oh-my-pi/pi-ai` plus Pi 1.0 transcript helpers that
56
+ OMP does not ship: `getCurrentSystemPrompt`, `getCurrentTools`,
57
+ `withoutInitialSystemMessage`).
58
+ 2. **Runtime** — the OMP `ExtensionAPI` is wrapped so that:
59
+ - OMP contexts (`systemPrompt: string[]`, `developer` messages) become the
60
+ Pi shape (`systemPrompt: string`, no developer role) before
61
+ `streamSimple`.
62
+ - Pi's `refreshModels({credential, signal, allowNetwork})` is exposed as
63
+ OMP's `fetchDynamicModels(apiKey)`.
64
+ - Pi `$ENV` / `$$` apiKey syntax is translated to OMP's.
65
+ - Unsupported events (`session_info_changed`) are no-ops; OMP's
66
+ `session_switch` / `session_branch` also fire Pi's `session_start`
67
+ handlers (OMP only emits `session_start` once at startup).
68
+
69
+ `ns-pi-provider` itself is unchanged.
70
+
71
+ ## Known limitations
72
+
73
+ - **`session_info_changed`** — OMP has no equivalent; the grok handler is a
74
+ documented no-op (name changes do not re-key the agent pool).
75
+ - **Duplicate `kiro`** — see `NS_OMP_PROVIDER_DISABLE` above.
76
+ - **Unauthenticated catalogs** — without credentials, OMP may hide a provider's
77
+ models from `omp models` even though the provider is registered. After
78
+ `/login` (or with CLI creds present) the live catalog, including Devin
79
+ `swe-2-*`, appears via `fetchDynamicModels`.
80
+ - **Kiro catalog** — models come only from Kiro's `ListAvailableModels`
81
+ (since ns-pi-provider 0.2.0): with no Kiro credential and no saved catalog
82
+ snapshot, `kiro` registers zero models, and `-1m` long-context variants are
83
+ not offered.
84
+ - **Upstream pin** — this release bundles `@ngosangns/ns-pi-provider@0.2.0`.
85
+ Bumping it is a deliberate change (rebuild + retest).
86
+
87
+ ## Development
88
+
89
+ ```bash
90
+ npm install
91
+ npm test # hermetic unit + bundle load tests
92
+ npm run typecheck
93
+ npm run build
94
+ npm run smoke # optional: real `omp models -e dist/index.js` under a temp HOME
95
+ ```
96
+
97
+ Requires Node ≥ 20. The smoke script needs `omp` on `PATH` and does not touch
98
+ your real `~/.omp`.
99
+
100
+ ## Publish
101
+
102
+ Push a tag matching `package.json` (e.g. `v0.2.0`) to run
103
+ [`.github/workflows/publish.yml`](.github/workflows/publish.yml), which
104
+ publishes with **provenance** from a GitHub-hosted runner:
105
+
106
+ ```bash
107
+ # bump "version" in package.json first, then
108
+ npm run check
109
+ git tag v0.2.0 && git push origin v0.2.0
110
+ ```
111
+
112
+ Auth, in the order npm tries it:
113
+
114
+ 1. **Trusted publishing (OIDC)**, if a trusted publisher is attached to the
115
+ package. npm only allows that once the package exists:
116
+ ```bash
117
+ npm trust github ns-omp-provider --file publish.yml \
118
+ --repo ngosangns/ns-omp-provider --allow-publish
119
+ ```
120
+ 2. **`NPM_TOKEN` repository secret** (currently set): a granular npm token with
121
+ package write and "bypass 2FA", passed to npm as `NODE_AUTH_TOKEN`. This
122
+ covers the first publish. After trusted publishing is configured the secret
123
+ can be removed (`gh secret delete NPM_TOKEN --repo ngosangns/ns-omp-provider`).
124
+
125
+ Manual publish from a logged-in machine still works:
126
+ `npm run check && npm publish --access public`.
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Runtime adapters between ns-pi-provider (written for Pi 1.0) and OMP.
3
+ *
4
+ * Pi 1.0 and OMP (oh-my-pi) share the extension surface ns-pi-provider uses
5
+ * (registerProvider / registerCommand / on / ctx.ui / ctx.modelRegistry), but
6
+ * differ in a few contracts:
7
+ *
8
+ * - Context: OMP passes `systemPrompt?: string[]` and may include
9
+ * `role: "developer"` messages; Pi providers expect `systemPrompt?: string`
10
+ * and only user / assistant / toolResult (/ system) roles.
11
+ * - Model refresh: Pi calls `refreshModels({ credential, signal, allowNetwork })`;
12
+ * OMP calls `fetchDynamicModels(apiKey)`.
13
+ * - apiKey config: Pi resolves `$ENV` and unescapes `$$`; OMP treats the value
14
+ * as an exact env-var name, `!command`, or a literal.
15
+ * - Events: Pi 1.0 fires `session_start` on every session (new/resume/switch)
16
+ * and has `session_info_changed`; OMP fires `session_start` once at startup,
17
+ * `session_switch` / `session_branch` afterwards, and has no
18
+ * `session_info_changed`.
19
+ */
20
+ export type AnyRecord = Record<string, unknown>;
21
+ export interface OmpLikeContext {
22
+ systemPrompt?: string | string[];
23
+ messages: Array<{
24
+ role: string;
25
+ [key: string]: unknown;
26
+ }>;
27
+ tools?: unknown[];
28
+ [key: string]: unknown;
29
+ }
30
+ /** Events ns-pi-provider subscribes to that OMP never emits. */
31
+ export declare const UNSUPPORTED_EVENTS: ReadonlySet<string>;
32
+ /** OMP events that correspond to Pi 1.0's `session_start` (which OMP only fires at startup). */
33
+ export declare const SESSION_START_ALIASES: readonly string[];
34
+ /** Default timeout for a `fetchDynamicModels` call (OMP also bounds it). */
35
+ export declare const DYNAMIC_MODELS_TIMEOUT_MS = 30000;
36
+ /**
37
+ * Convert an OMP stream context into the Pi 1.0 shape the providers expect.
38
+ * Pi-shaped contexts (string systemPrompt, no developer messages) pass through
39
+ * untouched (same object), so behaviour under Pi-style callers is unchanged.
40
+ */
41
+ export declare function toPiContext<T extends OmpLikeContext>(context: T): T;
42
+ /**
43
+ * Translate a Pi apiKey config value into OMP's syntax.
44
+ * - `$NAME` → `NAME` when that env var is set (OMP reads it live); dropped otherwise,
45
+ * so OMP falls back to /login credentials instead of sending the
46
+ * literal string "NAME" as a key.
47
+ * - `$$` → `$` (Pi's escape for literal dollars).
48
+ * - anything else (literals, `!command`) passes through.
49
+ */
50
+ export declare function toOmpApiKey(value: unknown, env?: NodeJS.ProcessEnv): string | undefined;
51
+ type PiRefreshModels = (args: {
52
+ credential?: {
53
+ type: string;
54
+ key?: string;
55
+ access?: string;
56
+ };
57
+ signal: AbortSignal;
58
+ allowNetwork: boolean;
59
+ }) => unknown;
60
+ /** Build OMP's `fetchDynamicModels(apiKey)` from a Pi `refreshModels` hook. */
61
+ export declare function toFetchDynamicModels(refreshModels: PiRefreshModels, timeoutMs?: number): (apiKey: string | undefined) => Promise<unknown[]>;
62
+ /** Adapt one Pi `registerProvider` config to OMP's `ProviderConfig`. */
63
+ export declare function adaptProviderConfig(config: AnyRecord, env?: NodeJS.ProcessEnv): AnyRecord;
64
+ export interface WrapOptions {
65
+ env?: NodeJS.ProcessEnv;
66
+ }
67
+ /**
68
+ * Wrap OMP's ExtensionAPI so ns-pi-provider's Pi-targeted registration code
69
+ * runs unchanged: provider configs are adapted, unsupported events are dropped,
70
+ * and `session_start` handlers also run on OMP's session switch/branch events.
71
+ * Every other member is forwarded (methods bound to the real API object).
72
+ */
73
+ export declare function wrapExtensionApi<T extends object>(pi: T, options?: WrapOptions): T;
74
+ export {};
@@ -0,0 +1,36 @@
1
+ /**
2
+ * `calculateCost` as exported by @earendil-works/pi-ai 1.0. OMP moved it to
3
+ * `@oh-my-pi/pi-catalog/models`; reimplemented here (pure) so the bundle does
4
+ * not depend on a second OMP package or its legacy shim.
5
+ */
6
+ interface CostRates {
7
+ input: number;
8
+ output: number;
9
+ cacheRead: number;
10
+ cacheWrite: number;
11
+ }
12
+ interface CostTier extends CostRates {
13
+ inputTokensAbove: number;
14
+ }
15
+ export interface CostModel {
16
+ cost?: Partial<CostRates> & {
17
+ tiers?: CostTier[];
18
+ };
19
+ }
20
+ export interface CostUsage {
21
+ input: number;
22
+ output: number;
23
+ cacheRead: number;
24
+ cacheWrite: number;
25
+ cacheWrite1h?: number;
26
+ cost: {
27
+ input: number;
28
+ output: number;
29
+ cacheRead: number;
30
+ cacheWrite: number;
31
+ total: number;
32
+ };
33
+ [key: string]: unknown;
34
+ }
35
+ export declare function calculateCost(model: CostModel, usage: CostUsage): CostUsage["cost"];
36
+ export {};
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Build-time replacement for `@earendil-works/pi-ai/oauth`.
3
+ *
4
+ * ns-pi-provider's kiro module calls `registerOAuthProvider` best-effort so
5
+ * Pi's `/login` lists it. On OMP, `pi.registerProvider(..., { oauth })`
6
+ * already registers the OAuth provider (scoped to this extension's source id,
7
+ * so it is cleaned up on reload). A second, unscoped global registration
8
+ * would outlive the extension, so this is intentionally a no-op.
9
+ */
10
+ export declare function registerOAuthProvider(_provider: unknown): void;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Build-time replacement for `@earendil-works/pi-ai` and
3
+ * `@earendil-works/pi-ai/compat` inside the bundled ns-pi-provider sources.
4
+ *
5
+ * Deliberately NOT `export *`: listing every runtime symbol lets esbuild fail
6
+ * the build when ns-pi-provider starts importing something OMP lacks, instead
7
+ * of OMP failing at extension load ("Export named ... not found").
8
+ */
9
+ export { createAssistantMessageEventStream } from "@oh-my-pi/pi-ai";
10
+ export { calculateCost } from "./cost.js";
11
+ export { getCurrentSystemPrompt, getCurrentTools, withoutInitialSystemMessage } from "./transcript.js";
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Build-time replacement for `@earendil-works/pi-coding-agent`.
3
+ * ns-pi-provider only imports types from it; an empty module makes esbuild
4
+ * fail loudly if a runtime import ever appears.
5
+ */
6
+ export {};
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Pi 1.0 transcript helpers that OMP's pi-ai does not ship.
3
+ *
4
+ * Pi 1.0 folds the system prompt and tool set into `role: "system"` messages
5
+ * (`toolsAdded` / `toolsRemoved` deltas) and providers read them back with
6
+ * these helpers. OMP keeps them on `context.systemPrompt` / `context.tools`
7
+ * and never puts system messages in the transcript, so on OMP these resolve
8
+ * to "" / [] / the messages unchanged — and the providers fall through to
9
+ * the (normalized) `context.systemPrompt` / `context.tools` they check first.
10
+ *
11
+ * Semantics mirror @earendil-works/pi-ai 1.0 `dist/utils/transcript.js`.
12
+ */
13
+ export interface ToolLike {
14
+ name: string;
15
+ [key: string]: unknown;
16
+ }
17
+ export interface MessageLike {
18
+ role: string;
19
+ content?: unknown;
20
+ toolsAdded?: ToolLike[];
21
+ toolsRemoved?: Array<{
22
+ name: string;
23
+ }>;
24
+ sections?: Record<string, string | null>;
25
+ timestamp?: number;
26
+ [key: string]: unknown;
27
+ }
28
+ /** Drop the leading system message for APIs that carry the prompt outside the message list. */
29
+ export declare function withoutInitialSystemMessage<T extends {
30
+ role: string;
31
+ }>(messages: T[]): T[];
32
+ /** Resolve the tools available after applying every transcript delta in order. */
33
+ export declare function getCurrentTools<T = ToolLike>(messages: ReadonlyArray<{
34
+ role: string;
35
+ }>): T[];
36
+ /**
37
+ * Render the current system prompt after replaying every system message:
38
+ * base `content` blocks are concatenated, named `sections` are patched in
39
+ * order (null deletes) and appended after the base prompt.
40
+ */
41
+ export declare function getCurrentSystemPrompt(messages: ReadonlyArray<{
42
+ role: string;
43
+ }>): string;
@@ -0,0 +1,10 @@
1
+ /** Which wrapped providers to register, from the environment. */
2
+ export declare const PROVIDER_KEYS: readonly ["kiro", "devin", "grok"];
3
+ export type ProviderKey = (typeof PROVIDER_KEYS)[number];
4
+ export type ProviderSelection = Record<ProviderKey, boolean>;
5
+ /**
6
+ * - `NS_OMP_PROVIDERS=devin,grok` — allow-list (only these are registered).
7
+ * - `NS_OMP_PROVIDER_DISABLE=kiro` — deny-list, applied after the allow-list.
8
+ * Default: all three.
9
+ */
10
+ export declare function resolveProviderSelection(env?: NodeJS.ProcessEnv): ProviderSelection;
@@ -0,0 +1,3 @@
1
+ export { adaptProviderConfig, toPiContext, toOmpApiKey, toFetchDynamicModels, wrapExtensionApi } from "./adapter.js";
2
+ export { resolveProviderSelection } from "./config.js";
3
+ export default function nsOmpProvider(pi: object): Promise<void>;