@north-light/cli 0.3.470 → 0.3.472
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/README.md +6 -6
- package/dist/builtin-memory/crouter-sdk/release-notes.md +17 -5
- package/dist/commands/sys/panels/provider-panel.d.ts +2 -0
- package/dist/commands/sys/panels/provider-panel.js +1 -1
- package/dist/commands/sys/setup-wizard.js +1 -1
- package/docs/apps/quickstart.md +8 -0
- package/docs/sdk/release-notes.md +17 -5
- package/package.json +3 -3
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +32 -12
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ crouter is an agent runtime for Node.js. Each agent is a **node**: a record with
|
|
|
12
12
|
|
|
13
13
|
Closing a terminal does not stop an agent, and an agent that is waiting on something has no process at all. Nodes can spawn children, report to whoever subscribes to them, and share markdown documents that other agents are shown when they become relevant. crouter is not a model and does not bundle one: you sign in to a model provider during setup. It is also not a hosted service; it runs on your machine, or in a container you start.
|
|
14
14
|
|
|
15
|
-
[Docs](https://docs.northlight.ai) · [Getting started](https://docs.northlight.ai/docs/cli/first-session) · [Concepts](https://docs.northlight.ai/docs/concepts) · [SDK](https://docs.northlight.ai/docs/sdk) · [Plugins](https://docs.northlight.ai/docs/plugin) · [Marketplace](https://github.com/crouton-labs/crouter-official-marketplace) · [Discord](https://discord.gg/txKS3swtWY)
|
|
15
|
+
[Docs](https://crouter.docs.northlight.ai) · [Getting started](https://crouter.docs.northlight.ai/docs/cli/first-session) · [Concepts](https://crouter.docs.northlight.ai/docs/concepts) · [SDK](https://crouter.docs.northlight.ai/docs/sdk) · [Plugins](https://crouter.docs.northlight.ai/docs/plugin) · [Marketplace](https://github.com/crouton-labs/crouter-official-marketplace) · [Discord](https://discord.gg/txKS3swtWY)
|
|
16
16
|
|
|
17
17
|
## Install
|
|
18
18
|
|
|
@@ -62,7 +62,7 @@ crtr canvas browse # interactive navigator with tabs and search
|
|
|
62
62
|
crtr canvas attention list # nodes waiting on you
|
|
63
63
|
```
|
|
64
64
|
|
|
65
|
-
The [first-session guide](https://docs.northlight.ai/docs/cli/first-session) walks through this in order.
|
|
65
|
+
The [first-session guide](https://crouter.docs.northlight.ai/docs/cli/first-session) walks through this in order.
|
|
66
66
|
|
|
67
67
|
## What it does
|
|
68
68
|
|
|
@@ -99,7 +99,7 @@ flowchart LR
|
|
|
99
99
|
|
|
100
100
|
`crtrd` is the only writer of canvas state and the only launcher of brokers. The CLI, the viewer and SDK applications call its `/v1` API; none of them opens the canvas database or starts an engine of its own. On one machine they use an owner-only Unix socket. A browser or a remote process uses a TCP listener and a bearer token (`crtr sys connect`).
|
|
101
101
|
|
|
102
|
-
What this costs: a daemon has to be running (the CLI and SDK start it on first use), and a network failure on a remote client fails a request loudly rather than replaying it. What it buys is one owner for state, so a node outlives the terminal, the viewer, or the application that created it. [Why a daemon](https://docs.northlight.ai/docs/concepts/why-a-daemon) covers the reasoning.
|
|
102
|
+
What this costs: a daemon has to be running (the CLI and SDK start it on first use), and a network failure on a remote client fails a request loudly rather than replaying it. What it buys is one owner for state, so a node outlives the terminal, the viewer, or the application that created it. [Why a daemon](https://crouter.docs.northlight.ai/docs/concepts/why-a-daemon) covers the reasoning.
|
|
103
103
|
|
|
104
104
|
## Use crouter from an application
|
|
105
105
|
|
|
@@ -123,7 +123,7 @@ if (run.kind === 'result') console.log(run.output_parsed.name, run.output_parsed
|
|
|
123
123
|
else console.error(run.reason, run.detail);
|
|
124
124
|
```
|
|
125
125
|
|
|
126
|
-
`nodes.parse` returns a typed result or an explicit failure; it never hands back text you have to parse. `nodes.stream` pipes a run's text and tool calls to a UI. See the [SDK docs](https://docs.northlight.ai/docs/sdk) and the [recipes](https://docs.northlight.ai/docs/sdk/guides).
|
|
126
|
+
`nodes.parse` returns a typed result or an explicit failure; it never hands back text you have to parse. `nodes.stream` pipes a run's text and tool calls to a UI. See the [SDK docs](https://crouter.docs.northlight.ai/docs/sdk) and the [recipes](https://crouter.docs.northlight.ai/docs/sdk/guides).
|
|
127
127
|
|
|
128
128
|
## Run it in a container
|
|
129
129
|
|
|
@@ -137,7 +137,7 @@ The image runs `crtrd` headless behind TCP; every request needs the bearer token
|
|
|
137
137
|
|
|
138
138
|
## Extend crouter
|
|
139
139
|
|
|
140
|
-
An application exposes typed operations as native `crtr` commands with [`@north-light/plugin`](https://github.com/Northlight-Systems/crouter/tree/main/packages/crouter-plugin): define a command tree once and it produces the HTTP handler and the archive that `crtr pkg plugin install --endpoint <url> --name <name>` reads. The [plugin docs](https://docs.northlight.ai/docs/plugin) cover commands, parameters, output, errors and deployment.
|
|
140
|
+
An application exposes typed operations as native `crtr` commands with [`@north-light/plugin`](https://github.com/Northlight-Systems/crouter/tree/main/packages/crouter-plugin): define a command tree once and it produces the HTTP handler and the archive that `crtr pkg plugin install --endpoint <url> --name <name>` reads. The [plugin docs](https://crouter.docs.northlight.ai/docs/plugin) cover commands, parameters, output, errors and deployment.
|
|
141
141
|
|
|
142
142
|
Ready-made plugins live in the [official marketplace](https://github.com/crouton-labs/crouter-official-marketplace): `exa-search`, `capture`, `tsym`, `bird`, `github-asset`, `dev`, `vercel`, `fly`, `railway`, `cloudflare`, and others. The first-run installer offers to register it.
|
|
143
143
|
|
|
@@ -152,7 +152,7 @@ Ready-made plugins live in the [official marketplace](https://github.com/crouton
|
|
|
152
152
|
| [`@north-light/env-docker`](https://www.npmjs.com/package/@north-light/env-docker) | Start or attach to a `crtrd` container | [`packages/crouter-env-docker`](https://github.com/Northlight-Systems/crouter/tree/main/packages/crouter-env-docker) |
|
|
153
153
|
| [`@north-light/identity`](https://www.npmjs.com/package/@north-light/identity) | Verify access tokens and evaluate scopes | [`packages/crouter-identity`](https://github.com/Northlight-Systems/crouter/tree/main/packages/crouter-identity) |
|
|
154
154
|
|
|
155
|
-
Most applications need only `@north-light/sdk`. The other repository contents: [`docs/`](https://github.com/Northlight-Systems/crouter/tree/main/docs) is the documentation source, [`apps/docs`](https://github.com/Northlight-Systems/crouter/tree/main/apps/docs) is the TanStack Start site that publishes it at docs.northlight.ai, [`docker/`](https://github.com/Northlight-Systems/crouter/tree/main/docker) builds the `crtrd` image, and [`examples/`](https://github.com/Northlight-Systems/crouter/tree/main/examples) holds the SDK recipes and a localhost demo.
|
|
155
|
+
Most applications need only `@north-light/sdk`. The other repository contents: [`docs/`](https://github.com/Northlight-Systems/crouter/tree/main/docs) is the documentation source, [`apps/docs`](https://github.com/Northlight-Systems/crouter/tree/main/apps/docs) is the TanStack Start site that publishes it at crouter.docs.northlight.ai, [`docker/`](https://github.com/Northlight-Systems/crouter/tree/main/docker) builds the `crtrd` image, and [`examples/`](https://github.com/Northlight-Systems/crouter/tree/main/examples) holds the SDK recipes and a localhost demo.
|
|
156
156
|
|
|
157
157
|
## Develop
|
|
158
158
|
|
|
@@ -9,6 +9,18 @@ when-and-why-to-read: When you need Release notes, this knowledge should be read
|
|
|
9
9
|
|
|
10
10
|
Changes to `@north-light/sdk` that can change what an existing app sees. Newest first.
|
|
11
11
|
|
|
12
|
+
## 0.3.472
|
|
13
|
+
|
|
14
|
+
### `npx @north-light/sdk init`, `skill` and `env --client-id` (additive, no app change)
|
|
15
|
+
|
|
16
|
+
The `crouter-sdk` bin gains three commands that need neither crtr nor a joined machine:
|
|
17
|
+
|
|
18
|
+
- `init [dir] [--agent claude|agents|all] [--global-skill]` scaffolds an app in an empty or new directory (`package.json` with `@north-light/sdk` pinned to the CLI's own version plus `tsx`, `server.ts`, `.gitignore`, `local/app.env`, a signing key in `local/app-key.json`) and installs the `northlight` Agent Skill into `.agents/skills` and `.claude/skills`. In a directory that already has a `package.json` it writes no app code: it runs `npm i --save-exact @north-light/sdk@<this version>`, makes a key if `local/app-key.json` is missing, and installs the skill. It never overwrites a file; it exits 1 and names the file it refused.
|
|
19
|
+
- `skill [--agent claude|agents|all] [--global] [--print]` writes the skill that ships in the package, replacing an older copy. The skill's `metadata.sdk-version` is the package version; run `skill` again after changing the pinned SDK version.
|
|
20
|
+
- `env --client-id <id> [--origin <url>] [--issuer <url>] [--out <path>]` writes `CROUTER_ISSUER`, `CROUTER_CLIENT_ID` and `APP_ORIGIN` into `local/app.env` and keeps every other line, with no network call. `env --app <id>` still reads them through crtr; passing both is a usage error.
|
|
21
|
+
|
|
22
|
+
`keygen` is unchanged. The package still has one bin, so `npx @north-light/sdk <command>` keeps working. A bare `npx @north-light/sdk`, `--help` and an unknown command print the command list (exit 1 for the unknown command).
|
|
23
|
+
|
|
12
24
|
## 0.3.462
|
|
13
25
|
|
|
14
26
|
### `ProfileDTO.memory_path` is removed (profile objects carry only `profile_path`)
|
|
@@ -69,14 +81,14 @@ Not breaking: no type or wire shape an app reads is removed or changed.
|
|
|
69
81
|
|
|
70
82
|
### Monthly spend cap enforced; `usage.get` (additive; one behavior change)
|
|
71
83
|
|
|
72
|
-
- **Behavior change:** user-paid model calls (`/v1/llm`) and provider calls now count against the grant's `spend_cap` (from `grant.get()`;
|
|
84
|
+
- **Behavior change:** user-paid model calls (`/v1/llm`) and provider calls now count against the grant's `spend_cap` (from `grant.get()`; Northlight sets $50) for the current UTC month. Once the month reaches it, the next call answers 402 `cap_exhausted` (a `RateLimitError`, `retryable: false`, `userAction: 'raise_cap'`, `resetAt` the first instant of next month) before any model or provider is called. A run whose model call is refused this way ends with outcome reason `cap_exhausted`. The check runs before each call, so the call that crosses the cap still completes. App-paid usage never counts. A grant with `spend_cap: null` is not capped.
|
|
73
85
|
- New `client.usage.get({month?})` (`month` is `YYYY-MM`, UTC; default the current month) answers `SpendUsageDTO`: `usd_equivalent` (user-paid spend, provider receipts included), `by_model`, `app_paid`, `spend_cap` and `reset_at`. See [[crouter-sdk/client]].
|
|
74
86
|
- A completion's `usage.usd_equivalent` is now the served model's list-price cost; before it was always `null`. It stays `null` for a model with no list price, and that usage adds nothing to the month.
|
|
75
87
|
|
|
76
88
|
### `npx @north-light/sdk env` (additive); keygen on an unjoined machine exits 2
|
|
77
89
|
|
|
78
90
|
- New `npx @north-light/sdk env --app <client id> [--out <path>]` writes `CROUTER_ISSUER`, `CROUTER_CLIENT_ID` and `APP_ORIGIN` from `crtr cloud apps get` into `local/app.env`, keeping every other line. See [[crouter-sdk/getting-started]].
|
|
79
|
-
- `keygen --app` on a machine whose `crtr` isn't joined to a
|
|
91
|
+
- `keygen --app` on a machine whose `crtr` isn't joined to a Northlight account now prints the join step and the manual steps and exits 2. A missing `crtr` keeps its message and exit 0.
|
|
80
92
|
|
|
81
93
|
### One deadline per SDK call, and a deadline on every OAuth/JWKS fetch (additive, with one behaviour change)
|
|
82
94
|
|
|
@@ -92,7 +104,7 @@ Not breaking: no type or wire shape an app reads is removed or changed. Apps pin
|
|
|
92
104
|
|
|
93
105
|
### `@north-light/sdk/testing`: isolated runs start, `FakeCall.runId`, one `runs` shape (one breaking change)
|
|
94
106
|
|
|
95
|
-
- `runs.start({isolated: true})` now starts a run on `FakeCloud`, as it does on a
|
|
107
|
+
- `runs.start({isolated: true})` now starts a run on `FakeCloud`, as it does on a Northlight guest; it used to answer 503 `isolated_unavailable`. `run.params.isolated` records it, and a `cwd` outside the run's root folder is 400 `param: cwd`, as the runtime checks it. The new option `isolatedRuns: 'unavailable'` restores the 503 for testing that path. If you worked around the refusal by deleting `body.isolated` in an intercept, remove the intercept.
|
|
96
108
|
- `FakeCall` gains `runId` and `questionId` (the ids the path addresses, else `null`), so tests no longer split `path`.
|
|
97
109
|
- `FakeCall.userId` is now the bearer's person whenever the cloud knows them, including a person with no refresh token; it is `null` only for a missing or unknown bearer.
|
|
98
110
|
- **Breaking:** `cloud.user(id).runs` is now `{all, respond}`, the same shape as `cloud.runs`, instead of an array carrying `respond`. Replace `user.runs[i]`, `user.runs.find(...)` and `[...user.runs]` with `user.runs.all…`.
|
|
@@ -106,7 +118,7 @@ New `@north-light/sdk/testing`: `FakeCloud`, a fake directory and runtime your r
|
|
|
106
118
|
|
|
107
119
|
### `oauth.accountAppUrl()` (additive), and a failed discovery read is retried (behaviour fix)
|
|
108
120
|
|
|
109
|
-
- New `OAuth2Client.accountAppUrl()` returns the app's page on the signed-in person's
|
|
121
|
+
- New `OAuth2Client.accountAppUrl()` returns the app's page on the signed-in person's Northlight account, as the directory's discovery document advertises it (`account_app_uri_template`). Use it instead of building `<issuer>/account/apps/<client id>` yourself. It needs no user or connection and throws `CrouterError` naming the fault when the directory advertises no template or an invalid one. See [[crouter-sdk/client]].
|
|
110
122
|
- **Behaviour fix:** `OAuth2Client`'s discovery read now times out after 10 seconds, and a read that failed or timed out is retried by the next call that needs it. Before, one failed read was kept for the client's life, so every later `authorizeUrl`, `exchangeCode`, refresh and `verifyIdToken` failed until the process restarted, and a hung directory stalled them indefinitely. A successful read is still kept for the client's life.
|
|
111
123
|
|
|
112
124
|
## 0.3.405
|
|
@@ -199,7 +211,7 @@ The SDK now sends `Crouter-Minimum-Runtime-Version: <SDK_VERSION>` on every requ
|
|
|
199
211
|
- `refusalOf` returns a `Refusal` only for an error the SDK raised while refreshing a stored connection, not for every directory-origin `AuthenticationError`.
|
|
200
212
|
- Concurrent refreshes in one process are shared per user and refresh token, so a client holding a newer token never joins an older refusal.
|
|
201
213
|
|
|
202
|
-
**Callers to update:**
|
|
214
|
+
**Callers to update:** Northlight's chat app (`first-party-apps/chat/src/directory-client.ts`, which builds `oauth.client(connection, store)`) and the context app (`first-party-apps/context/req-run.mjs`). Any app that called `refreshIdleConnections(store, …)`, read `RefreshFailure.reason`, or imported `refreshRefusal` must move to the shapes above.
|
|
203
215
|
|
|
204
216
|
### `RunWatcher`: learn when a background run ends (additive)
|
|
205
217
|
|
|
@@ -51,6 +51,8 @@ export declare class ProviderPanel implements SetupPanel {
|
|
|
51
51
|
private openBrowser;
|
|
52
52
|
private openCatalog;
|
|
53
53
|
private startLogin;
|
|
54
|
+
/** True when the cursor sits on the `+ Add provider` row, where Enter opens the catalog. */
|
|
55
|
+
isAddRowSelected(): boolean;
|
|
54
56
|
isNestedFlowActive(): boolean;
|
|
55
57
|
dispose(): void;
|
|
56
58
|
private runMutation;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var R=Object.defineProperty;var
|
|
1
|
+
var R=Object.defineProperty;var g=(u,e)=>R(u,"name",{value:e,configurable:!0});import{LoginDialogComponent as E}from"@earendil-works/pi-coding-agent";import{Input as B,matchesKey as d,truncateToWidth as D}from"@earendil-works/pi-tui";import{execFile as N}from"node:child_process";import{dialogAuthInteraction as T}from"../../../core/auth-interaction.js";import{isProviderCredentialConfigured as F}from"../../../core/provider-credential.js";import{formatCooldowns as _,isManagedProvider as L,promoteSubscription as U,readSubscriptionPool as S,renameSubscription as V}from"../../../core/subscription-state.js";import{removeProviderRow as j}from"../../../core/provider-management.js";import{cliClient as q}from"../../api-client.js";import{loginProvider as K}from"../provider-login.js";import{fitTail as P,theme as a,visibleRange as k}from"../../../core/tui/panel.js";function M(u,e){const t=u.getProviders().filter(i=>i.auth.oauth!==void 0),n=new Set(t.map(i=>i.id)),o=new Set;return[...t.map(i=>({providerId:i.id,name:i.name,managed:L(i.id),authType:"oauth"})),...e.getAll().map(i=>({providerId:i.provider,name:e.getProviderDisplayName(i.provider),managed:L(i.provider),authType:n.has(i.provider)?"oauth":"api_key"}))].filter(i=>!o.has(i.providerId)&&!!o.add(i.providerId))}g(M,"buildProviderCatalog");function O(u,e){return e||u instanceof Error&&u.name==="AbortError"?"Login cancelled":`Login error: ${u instanceof Error?u.message:String(u)}`}g(O,"classifyLoginFailure");class ie{static{g(this,"ProviderPanel")}tui;agentDir;runtime;registry;renameInput=new B;rows=[];selected=0;mode="list";notice="";catalog=[];catalogIndex=0;loginProvider;loginFlow;mutationInFlight=!1;onChange;constructor(e,t,n,o){this.tui=e,this.agentDir=t,this.runtime=n,this.registry=o,this.renameInput.onSubmit=()=>this.rename(),this.renameInput.onEscape=()=>{this.mode="list"},this.reload()}notifyChange(){this.onChange?.(),this.tui.requestRender()}reloadAfterAuthChange(){this.reload(),this.registry.refresh().then(()=>{this.reload(),this.notifyChange()})}reload(){const e=this.registry,t=[];for(const n of M(this.runtime,e)){const o=e.getProviderAuthStatus(n.providerId);if(n.managed){const i=S(n.providerId);if(i.length===0&&!o.source)continue;t.push({...n,configured:F(n.providerId,e),source:o.source,sourceLabel:o.label}),i.forEach((r,c)=>t.push({...n,label:r.label,preferred:c===0,cooldownUntil:r.rateLimitedUntil,...r.modelCooldowns?{modelCooldowns:r.modelCooldowns}:{},accountId:r.accountId}));continue}o.source&&t.push({...n,configured:F(n.providerId,e),source:o.source,sourceLabel:o.label})}this.rows=t,this.selected=Math.min(this.selected,Math.max(0,t.length))}selectedRow(){return this.rows[this.selected]}openBrowser(e){const t=process.platform==="darwin"?"open":process.platform==="win32"?"cmd":"xdg-open",n=process.platform==="win32"?["/c","start","",e]:[e];N(t,n,{timeout:5e3},()=>{})}openCatalog(){this.catalog=M(this.runtime,this.registry),this.catalogIndex=0,this.mode="catalog"}startLogin(e,t){this.loginProvider=e;let n;const o=g(s=>{!n.active||this.loginFlow!==n||(n.active=!1,this.loginFlow=void 0,this.mode="list",this.notice=s,this.reloadAfterAuthChange(),this.notifyChange())},"complete"),i=new E(this.tui,e.providerId,(s,p)=>{s||o(p?`Login failed: ${p}`:"Login cancelled")},e.name);n={dialog:i,active:!0,committing:!1},this.loginFlow=n,this.mode="login";const r=i.handleInput.bind(i);i.handleInput=s=>{n.committing||r(s)};const c=g(()=>n.active&&this.loginFlow===n&&!i.signal.aborted,"isActive"),h=g(()=>c()?(n.committing=!0,i.showDetails(["Saving credentials\u2026"]),!0):!1,"beginCommit"),m=g(s=>{o(O(s,i.signal.aborted))},"fail");if(e.authType==="api_key"){i.showPrompt(`Enter API key for ${e.name}:`,"sk-...").then(async s=>{if(c()){if(!s.trim()){o("No key entered \u2014 login cancelled");return}h()&&(await q().installCredential(e.providerId,{kind:"api_key",api_key:s.trim()}),o(`API key saved for ${e.name}`))}}).catch(m);return}const $=T(i,{openBrowser:g(s=>this.openBrowser(s),"openBrowser"),onChange:g(()=>this.notifyChange(),"onChange"),isActive:c});K(this.runtime,e.providerId,$,{label:t,beforeInstall:h}).then(s=>{s&&c()&&o(`Logged in to ${e.name}`)}).catch(m)}isAddRowSelected(){return this.mode==="list"&&this.selectedRow()===void 0}isNestedFlowActive(){return this.mode==="login"}dispose(){const e=this.loginFlow;e&&(e.active=!1,this.loginFlow=void 0,e.dialog.handleInput("\x1B"))}runMutation(e){this.mutationInFlight||(this.mutationInFlight=!0,e().catch(t=>{this.notice=t instanceof Error?t.message:String(t)}).finally(()=>{this.mutationInFlight=!1,this.notifyChange()}))}suggestedAccountLabel(e){const t=new Set((L(e)?S(e):[]).map(o=>o.label.toLowerCase()));if(!t.has("default"))return"default";let n=2;for(;t.has(`account-${n}`);)n+=1;return`account-${n}`}rename(){const e=this.selectedRow();!e?.managed||!e.label||(this.mode="list",this.runMutation(async()=>{await V(e.providerId,e.label,this.renameInput.getValue()),this.notice=`Renamed ${e.name} account`,this.reload()}))}remove(e){this.runMutation(async()=>{this.notice=await j(this.agentDir,e),this.reloadAfterAuthChange()})}promote(e){this.runMutation(async()=>{await U(e.providerId,e.label),this.notice=`Preferred ${e.name} account`,this.reload()})}handleInput(e){if(this.mode==="login")return this.loginFlow?.dialog.handleInput(e),!0;if(this.mutationInFlight)return!0;if(this.mode==="rename"||this.mode==="label")return this.renameInput.handleInput(e),!0;if(this.mode==="catalog"){if(d(e,"escape"))return this.mode="list",!0;if(d(e,"j")||d(e,"down"))return this.catalogIndex=Math.min(this.catalog.length-1,this.catalogIndex+1),!0;if(d(e,"k")||d(e,"up"))return this.catalogIndex=Math.max(0,this.catalogIndex-1),!0;if(d(e,"enter")){const o=this.catalog[this.catalogIndex];if(o.managed){this.loginProvider=o;const i=this.suggestedAccountLabel(o.providerId);this.renameInput.setValue(i),this.renameInput.onSubmit=()=>{this.startLogin(o,this.renameInput.getValue().trim()||i)},this.mode="label"}else this.startLogin(o);return!0}return!0}if(this.mode==="remove"){if(d(e,"y")||d(e,"enter")){const o=this.selectedRow();o&&(o.managed||o.configured)&&this.remove(o)}return this.mode="list",!0}if(d(e,"j")||d(e,"down"))return this.selected=Math.min(this.rows.length,this.selected+1),!0;if(d(e,"k")||d(e,"up"))return this.selected=Math.max(0,this.selected-1),!0;const t=this.selectedRow();if(d(e,"p")&&t?.managed&&t.label)return this.promote(t),!0;if(d(e,"r")&&t?.managed&&t.label)return this.renameInput.setValue(t.label),this.renameInput.onSubmit=()=>this.rename(),this.mode="rename",!0;if(d(e,"x")&&(t?.managed||t?.configured))return this.mode="remove",!0;if(d(e,"a"))return this.openCatalog(),!0;const n=t?.source==="environment"||t?.source==="runtime";return d(e,"l")&&t&&!n?(t.managed&&t.label?this.startLogin(t,t.label):this.startLogin(t),!0):d(e,"enter")&&!t?(this.openCatalog(),!0):!1}badge(){return String(this.rows.filter(e=>e.label===void 0).length)}render(e,t){const n=a.fg("accent",a.bold("Providers")),o=a.fg("dim",`${this.rows.filter(c=>c.label===void 0).length} configured`),i=Math.max(1,t);let r;if(this.mode==="login")r=P(this.loginFlow?.dialog.render(e)??[],i);else if(this.mode==="rename"||this.mode==="label")r=P([n,o,"",a.fg("accent",this.mode==="rename"?"Rename account":`Name the ${this.loginProvider?.name??"provider"} account`),...this.mode==="label"?[a.fg("dim","Names this credential \u2014 keep work and personal accounts on one provider apart")]:[],...this.renameInput.render(Math.max(1,e-8))],i);else if(this.mode==="remove"){const c=this.selectedRow(),h=c?.label??(c?.managed?`${c.name} \xB7 all accounts`:c?.name??"account");r=P([n,o,"",a.fg("warning",`Remove ${h}? y/enter confirm \xB7 any other key cancel`)],i)}else{const c=[n,o].slice(0,Math.max(0,i-1)),h=i-c.length;if(r=[...c],this.mode==="catalog"){const m=this.catalog.length,s=m>h&&h>=2,p=s?h-1:h,{start:I,end:v}=k(m,this.catalogIndex,p);let w=h-(v-I)-(s?1:0);w>0&&(r.push(a.fg("dim","Select a provider to add \xB7 esc back")),w-=1);for(let f=I;f<v;f++){const C=this.catalog[f];r.push(`${f===this.catalogIndex?a.fg("accent","\u25B6"):" "} ${C.name}`)}s&&r.push(a.fg("dim",`(${this.catalogIndex+1}/${m})`))}else{const m=this.rows.length+1,s=m>h&&h>=2,p=s?h-1:h,{start:I,end:v}=k(m,this.selected,p);let w=h-(v-I)-(s?1:0);const f=this.rows.length===0&&w>0;f&&(w-=1);const C=!!this.notice&&w>0;f&&r.push(a.fg("warning","No provider configured yet."));for(let b=I;b<v;b++){if(b===this.rows.length){r.push(`${this.selected===this.rows.length?a.fg("accent","\u25B6"):" "} ${a.fg("accent","\uFF0B Add provider")}`);continue}const l=this.rows[b],A=b===this.selected?a.fg("accent","\u25B6"):" ";if(l.label){const x=_({rateLimitedUntil:l.cooldownUntil??0,modelCooldowns:l.modelCooldowns})??"ready",y=l.accountId?` \xB7 ${l.accountId.length>12?`${l.accountId.slice(0,8)}\u2026${l.accountId.slice(-4)}`:l.accountId}`:"";r.push(`${A} ${l.preferred?a.fg("success","\u25CF"):" "} ${l.label}${a.fg("dim",y)} ${a.fg("dim",x)}${l.preferred?a.fg("accent"," preferred"):""}`)}else{const x=l.managed?`${this.rows.filter(y=>y.providerId===l.providerId&&y.label).length} accounts`:l.source==="environment"||l.source==="runtime"?`${l.source}${l.sourceLabel?` \xB7 ${l.sourceLabel}`:""} (view only)`:"configured";r.push(`${A} ${a.fg("accent",l.name)}${a.fg("dim",` ${x}`)}`)}}s&&r.push(a.fg("dim",`(${this.selected+1}/${m})`)),C&&r.push(a.fg("dim",this.notice))}}return r.map(c=>D(c,e))}}export{ie as ProviderPanel,M as buildProviderCatalog,O as classifyLoginFailure};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var P=Object.defineProperty;var p=(h,e)=>P(h,"name",{value:e,configurable:!0});import{matchesKey as b,truncateToWidth as
|
|
1
|
+
var P=Object.defineProperty;var p=(h,e)=>P(h,"name",{value:e,configurable:!0});import{matchesKey as b,truncateToWidth as o,visibleWidth as g}from"@earendil-works/pi-tui";import{formatGesture as v,formatMovePair as k,matchesPiTuiInput as $}from"../../core/keybindings/index.js";import{buildSetupInitialState as y,executeSetupSubmission as I,formatSetupExecutionSummary as w,packagePlanAction as A,reconcileInboxFrontDoor as x,renderSetupStaticInstructions as T,setupPluginEnvFields as C}from"./setup-core.js";import{runSetupSettingsProcess as W}from"./setup-settings-runner.js";import{DependenciesPanel as D}from"./panels/dependencies-panel.js";import{PAGE_CHROME_ROWS as F,pageFrame as L,theme as r}from"../../core/tui/panel.js";import{PluginsPanel as H}from"./panels/plugins-panel.js";import{ProviderPanel as O}from"./panels/provider-panel.js";import{PluginEnvPanel as _}from"./panels/plugin-env-panel.js";import{TerminalPanel as N}from"./panels/terminal-panel.js";import{describeEdit as R,planOptionAsAlt as z}from"./terminal-option.js";import{homedir as M}from"node:os";import{cliClient as S}from"../api-client.js";const E=["Providers","Plugins","Plugin env","Dependencies"],j="Option key";class B{static{p(this,"SetupWizard")}bindings;terminalRows;providers;plugins;pluginEnv;dependencies;terminal;steps;step=0;confirming=!1;cachedWidth;cachedHeight;cachedLines;_focused=!1;installedPackageIds;onSubmit;onCancel;constructor(e,i,t,n,s,c,a,l,f,m,d=null){this.bindings=l,this.terminalRows=f,this.installedPackageIds=i;const u=p(()=>this.invalidate(),"invalidate");this.providers=new O(n,s,c,a),this.providers.onChange=u,this.plugins=new H(e,i,u),this.pluginEnv=new _(m,u),this.dependencies=new D(t,u),this.terminal=d===null?void 0:new N(d,u),this.steps=this.terminal===void 0?E:[...E,j]}set focused(e){this._focused=e}get focused(){return this._focused}invalidate(){this.cachedWidth=void 0,this.cachedHeight=void 0,this.cachedLines=void 0}matches(e,i){return $(this.bindings,e,i,(t,n)=>b(t,n))}label(e){const i=this.bindings.gestures(e);return i.length===0?"disabled":v(i[0].split(" > ").pop())}fieldSafeLabel(e){const i=this.bindings.gestures(e).filter(t=>{if(t.includes(" > "))return!0;const n=t.split("+").pop();return!(n==="up"||n==="down"||n==="left"||n==="right")&&(n.length>1||t.includes("+"))});return i.length===0?"Disabled":i.map(v).join(" / ")}setStep(e){e===2&&this.step!==2&&this.pluginEnv.setFields(C(this.plugins.selected)),this.step=e,this.invalidate()}moveStep(e){this.setStep((this.step+e+this.steps.length)%this.steps.length)}advance(){this.step===this.steps.length-1?this.finalize():(this.step!==2||this.pluginEnv.complete)&&this.moveStep(1)}cancel(){if(this.confirming){this.confirming=!1,this.invalidate();return}this.onCancel?.()}dispose(){this.providers.dispose()}hasPendingInstalls(){return this.plugins.selected.some(e=>!this.installedPackageIds.has(e.id))||this.dependencies.selected.length>0||this.terminal?.selectedEdit!==void 0}buildSubmission(){return{packages:[...this.plugins.selected],installedPackageIds:new Set(this.installedPackageIds),systemDeps:[...this.dependencies.selected],pluginEnv:new Map(this.pluginEnv.values),...this.terminal?.selectedEdit===void 0?{}:{terminalEdit:this.terminal.selectedEdit}}}finalize(){if(this.confirming||!this.hasPendingInstalls()){this.onSubmit?.(this.buildSubmission());return}this.confirming=!0,this.invalidate()}currentPanel(){return this.step===0?this.providers:this.step===1?this.plugins:this.step===2?this.pluginEnv:this.step===3?this.dependencies:this.terminal}handleInput(e){const i=this.currentPanel(),t=p((s,c)=>this.matches(s,c),"match");if(this.step===0&&this.providers.isNestedFlowActive()){i.handleInput(e,t)&&this.invalidate();return}if(this.confirming){this.matches("crtr.setup.confirm.accept",e)?this.onSubmit?.(this.buildSubmission()):this.matches("crtr.setup.confirm.back",e)&&(this.confirming=!1,this.invalidate());return}if(this.step===2&&this.pluginEnv.handleInput(e)){this.invalidate();return}if(this.matches("crtr.setup.cancel",e)){this.cancel();return}if(this.matches("crtr.setup.tab-next",e)){if(this.step===2&&!this.pluginEnv.complete)return;this.moveStep(1);return}if(this.matches("crtr.setup.tab-previous",e)){this.moveStep(-1);return}if(i.handleInput(e,t)){this.invalidate();return}this.matches("crtr.setup.advance",e)&&this.advance()}renderTrail(e){const t=this.steps.map((n,s)=>{if(s===this.step)return r.bg("selectedBg",` ${s+1} ${n} `);const c=s<this.step?r.fg("success","\u2713"):`${s+1}`;return r.fg("dim",` ${c} ${n} `)}).join(r.fg("dim","\u203A"));return g(t)<=e?[o(t,e),""]:[o(`${r.fg("dim",`Step ${this.step+1} of ${this.steps.length}`)} ${r.fg("accent",r.bold(this.steps[this.step]))}`,e),""]}footer(e){const i=`${this.fieldSafeLabel("crtr.setup.tab-next")}/${this.fieldSafeLabel("crtr.setup.tab-previous")} step \xB7 ${this.label("crtr.setup.cancel")} cancel`,t=k(this.bindings,"crtr.setup.list-down","crtr.setup.list-up"),n=this.label("crtr.setup.advance");let s,c;if(this.confirming)s=`${this.label("crtr.setup.confirm.accept")} confirm \xB7 ${this.label("crtr.setup.confirm.back")} go back`;else if(this.step===0){const a=this.providers.isAddRowSelected()?`${n} add provider`:`${n} next`;s=this.providers.isNestedFlowActive()?"OAuth in progress \xB7 esc cancel":`${t} select \xB7 ${a} \xB7 a add \xB7 p prefer \xB7 r rename \xB7 l re-login \xB7 x remove`,this.providers.isNestedFlowActive()||(c=`${t} select \xB7 ${a} \xB7 a add \xB7 l re-login`)}else this.step===1?s=`${t} move \xB7 ${this.label("crtr.setup.toggle")} toggle \xB7 ${this.label("crtr.setup.details")} details \xB7 ${this.label("crtr.setup.advance")} next`:this.step===2?s=`type value \xB7 Enter save/skip \xB7 ${this.pluginEnv.complete?`${this.label("crtr.setup.advance")} next`:"complete keys to continue"}`:this.step===3?s=`${t} move \xB7 ${this.label("crtr.setup.toggle")} toggle \xB7 ${this.label("crtr.setup.advance")} ${this.steps.length>4?"next":"finish"}`:s=`${this.terminal?.canApply?`${this.label("crtr.setup.toggle")} apply for me \xB7 `:""}${this.label("crtr.setup.advance")} finish`;for(const a of c===void 0?[s]:[s,c]){const l=e-g(a)-g(i);if(l>=2)return`${r.fg("dim",a)}${" ".repeat(l)}${r.fg("dim",i)}`}return r.fg("dim",c??s)}renderConfirm(e){const i=this.buildSubmission(),t=[o(r.fg("warning",r.bold("Confirm installs")),e),""];if(i.packages.length===0&&i.systemDeps.length===0&&i.terminalEdit===void 0)return[...t,o(r.fg("success",`Nothing to install; ${this.label("crtr.setup.confirm.accept")} finish.`),e)];if(i.packages.length){t.push(o(r.fg("accent","Packages"),e));for(const n of i.packages)t.push(o(` \u2022 ${n.name} ${r.fg("dim","\u2192")} ${this.installedPackageIds.has(n.id)?r.fg("success","skip (already installed)"):r.fg("muted",A(n))}`,e))}if(i.systemDeps.length){t.push(o(r.fg("accent","System deps"),e));for(const n of i.systemDeps)t.push(o(` \u2022 ${n.tool} ${r.fg("dim","\u2192")} ${r.fg("muted",n.command??n.hint)}`,e))}if(i.terminalEdit!==void 0){t.push(o(r.fg("accent","Terminal config"),e));for(const n of R(i.terminalEdit))t.push(o(r.fg("muted",` ${n}`),e))}return t}render(e){const i=this.step===0,t=this.terminalRows();if(!i&&this.cachedLines&&this.cachedWidth===e&&this.cachedHeight===t)return this.cachedLines;const n=this.currentPanel(),s=Math.max(1,t-F),c=this.confirming||s<=2?[]:this.renderTrail(e),a=Math.max(1,s-c.length),l=L({width:e,height:t,title:"crouter setup",meta:this.confirming?"review":`step ${this.step+1} of ${this.steps.length}`,body:[...c,...this.confirming?this.renderConfirm(e):n.render(e,a)],footer:this.footer(e)});return i||(this.cachedWidth=e,this.cachedHeight=t,this.cachedLines=l),l}}function G(){process.stdout.write("\x1B[2J\x1B[H")}p(G,"clearWizardFrame");async function re(){const h=y();let e=!1;if(await x(),!process.stdout.isTTY)return process.stdout.write(T(h)),"skipped";const i=new Set((await S().listAppEnv()).names),t=await W(({tui:s,terminal:c,agentDir:a,runtime:l,registry:f,bindings:m},d)=>{const u=new B(h.manifest,h.installedPackageIds,h.systemPlans,s,a,l,f,m,()=>c.rows,i,z({env:process.env,platform:process.platform,home:M()}));return u.onCancel=d.cancel,u.onSubmit=d.submit,u},(s,c)=>s===""?(e=!0,!0):s.length>0&&s.charCodeAt(0)<32&&$(c,"crtr.setup.cancel",s,(a,l)=>b(a,l)));if(G(),e){const s=new Error("Interrupted");throw s.name="AbortError",s}if(t===void 0)return"skipped";for(const[s,c]of t.pluginEnv)await S().setAppEnv(s,c);const n=await I(t);return process.stdout.write(w(n)),"completed"}p(re,"runSetup");export{B as SetupWizard,re as runSetup};
|
|
@@ -7,6 +7,18 @@ description: Behavior changes in @north-light/sdk that an app must act on, newes
|
|
|
7
7
|
|
|
8
8
|
Changes to `@north-light/sdk` that can change what an existing app sees. Newest first.
|
|
9
9
|
|
|
10
|
+
## 0.3.472
|
|
11
|
+
|
|
12
|
+
### `npx @north-light/sdk init`, `skill` and `env --client-id` (additive, no app change)
|
|
13
|
+
|
|
14
|
+
The `crouter-sdk` bin gains three commands that need neither crtr nor a joined machine:
|
|
15
|
+
|
|
16
|
+
- `init [dir] [--agent claude|agents|all] [--global-skill]` scaffolds an app in an empty or new directory (`package.json` with `@north-light/sdk` pinned to the CLI's own version plus `tsx`, `server.ts`, `.gitignore`, `local/app.env`, a signing key in `local/app-key.json`) and installs the `northlight` Agent Skill into `.agents/skills` and `.claude/skills`. In a directory that already has a `package.json` it writes no app code: it runs `npm i --save-exact @north-light/sdk@<this version>`, makes a key if `local/app-key.json` is missing, and installs the skill. It never overwrites a file; it exits 1 and names the file it refused.
|
|
17
|
+
- `skill [--agent claude|agents|all] [--global] [--print]` writes the skill that ships in the package, replacing an older copy. The skill's `metadata.sdk-version` is the package version; run `skill` again after changing the pinned SDK version.
|
|
18
|
+
- `env --client-id <id> [--origin <url>] [--issuer <url>] [--out <path>]` writes `CROUTER_ISSUER`, `CROUTER_CLIENT_ID` and `APP_ORIGIN` into `local/app.env` and keeps every other line, with no network call. `env --app <id>` still reads them through crtr; passing both is a usage error.
|
|
19
|
+
|
|
20
|
+
`keygen` is unchanged. The package still has one bin, so `npx @north-light/sdk <command>` keeps working. A bare `npx @north-light/sdk`, `--help` and an unknown command print the command list (exit 1 for the unknown command).
|
|
21
|
+
|
|
10
22
|
## 0.3.462
|
|
11
23
|
|
|
12
24
|
### `ProfileDTO.memory_path` is removed (profile objects carry only `profile_path`)
|
|
@@ -67,14 +79,14 @@ Not breaking: no type or wire shape an app reads is removed or changed.
|
|
|
67
79
|
|
|
68
80
|
### Monthly spend cap enforced; `usage.get` (additive; one behavior change)
|
|
69
81
|
|
|
70
|
-
- **Behavior change:** user-paid model calls (`/v1/llm`) and provider calls now count against the grant's `spend_cap` (from `grant.get()`;
|
|
82
|
+
- **Behavior change:** user-paid model calls (`/v1/llm`) and provider calls now count against the grant's `spend_cap` (from `grant.get()`; Northlight sets $50) for the current UTC month. Once the month reaches it, the next call answers 402 `cap_exhausted` (a `RateLimitError`, `retryable: false`, `userAction: 'raise_cap'`, `resetAt` the first instant of next month) before any model or provider is called. A run whose model call is refused this way ends with outcome reason `cap_exhausted`. The check runs before each call, so the call that crosses the cap still completes. App-paid usage never counts. A grant with `spend_cap: null` is not capped.
|
|
71
83
|
- New `client.usage.get({month?})` (`month` is `YYYY-MM`, UTC; default the current month) answers `SpendUsageDTO`: `usd_equivalent` (user-paid spend, provider receipts included), `by_model`, `app_paid`, `spend_cap` and `reset_at`. See [Show what a person's month has cost](./client.md#show-what-a-persons-month-has-cost).
|
|
72
84
|
- A completion's `usage.usd_equivalent` is now the served model's list-price cost; before it was always `null`. It stays `null` for a model with no list price, and that usage adds nothing to the month.
|
|
73
85
|
|
|
74
86
|
### `npx @north-light/sdk env` (additive); keygen on an unjoined machine exits 2
|
|
75
87
|
|
|
76
88
|
- New `npx @north-light/sdk env --app <client id> [--out <path>]` writes `CROUTER_ISSUER`, `CROUTER_CLIENT_ID` and `APP_ORIGIN` from `crtr cloud apps get` into `local/app.env`, keeping every other line. See [Go live with an app key](./getting-started.md#go-live-with-an-app-key).
|
|
77
|
-
- `keygen --app` on a machine whose `crtr` isn't joined to a
|
|
89
|
+
- `keygen --app` on a machine whose `crtr` isn't joined to a Northlight account now prints the join step and the manual steps and exits 2. A missing `crtr` keeps its message and exit 0.
|
|
78
90
|
|
|
79
91
|
### One deadline per SDK call, and a deadline on every OAuth/JWKS fetch (additive, with one behaviour change)
|
|
80
92
|
|
|
@@ -90,7 +102,7 @@ Not breaking: no type or wire shape an app reads is removed or changed. Apps pin
|
|
|
90
102
|
|
|
91
103
|
### `@north-light/sdk/testing`: isolated runs start, `FakeCall.runId`, one `runs` shape (one breaking change)
|
|
92
104
|
|
|
93
|
-
- `runs.start({isolated: true})` now starts a run on `FakeCloud`, as it does on a
|
|
105
|
+
- `runs.start({isolated: true})` now starts a run on `FakeCloud`, as it does on a Northlight guest; it used to answer 503 `isolated_unavailable`. `run.params.isolated` records it, and a `cwd` outside the run's root folder is 400 `param: cwd`, as the runtime checks it. The new option `isolatedRuns: 'unavailable'` restores the 503 for testing that path. If you worked around the refusal by deleting `body.isolated` in an intercept, remove the intercept.
|
|
94
106
|
- `FakeCall` gains `runId` and `questionId` (the ids the path addresses, else `null`), so tests no longer split `path`.
|
|
95
107
|
- `FakeCall.userId` is now the bearer's person whenever the cloud knows them, including a person with no refresh token; it is `null` only for a missing or unknown bearer.
|
|
96
108
|
- **Breaking:** `cloud.user(id).runs` is now `{all, respond}`, the same shape as `cloud.runs`, instead of an array carrying `respond`. Replace `user.runs[i]`, `user.runs.find(...)` and `[...user.runs]` with `user.runs.all…`.
|
|
@@ -104,7 +116,7 @@ New `@north-light/sdk/testing`: `FakeCloud`, a fake directory and runtime your r
|
|
|
104
116
|
|
|
105
117
|
### `oauth.accountAppUrl()` (additive), and a failed discovery read is retried (behaviour fix)
|
|
106
118
|
|
|
107
|
-
- New `OAuth2Client.accountAppUrl()` returns the app's page on the signed-in person's
|
|
119
|
+
- New `OAuth2Client.accountAppUrl()` returns the app's page on the signed-in person's Northlight account, as the directory's discovery document advertises it (`account_app_uri_template`). Use it instead of building `<issuer>/account/apps/<client id>` yourself. It needs no user or connection and throws `CrouterError` naming the fault when the directory advertises no template or an invalid one. See [Link to the app's settings on Northlight](./client.md#link-to-the-apps-settings-on-northlight).
|
|
108
120
|
- **Behaviour fix:** `OAuth2Client`'s discovery read now times out after 10 seconds, and a read that failed or timed out is retried by the next call that needs it. Before, one failed read was kept for the client's life, so every later `authorizeUrl`, `exchangeCode`, refresh and `verifyIdToken` failed until the process restarted, and a hung directory stalled them indefinitely. A successful read is still kept for the client's life.
|
|
109
121
|
|
|
110
122
|
## 0.3.405
|
|
@@ -197,7 +209,7 @@ The SDK now sends `Crouter-Minimum-Runtime-Version: <SDK_VERSION>` on every requ
|
|
|
197
209
|
- `refusalOf` returns a `Refusal` only for an error the SDK raised while refreshing a stored connection, not for every directory-origin `AuthenticationError`.
|
|
198
210
|
- Concurrent refreshes in one process are shared per user and refresh token, so a client holding a newer token never joins an older refusal.
|
|
199
211
|
|
|
200
|
-
**Callers to update:**
|
|
212
|
+
**Callers to update:** Northlight's chat app (`first-party-apps/chat/src/directory-client.ts`, which builds `oauth.client(connection, store)`) and the context app (`first-party-apps/context/req-run.mjs`). Any app that called `refreshIdleConnections(store, …)`, read `RefreshFailure.reason`, or imported `refreshRefusal` must move to the shapes above.
|
|
201
213
|
|
|
202
214
|
### `RunWatcher`: learn when a background run ends (additive)
|
|
203
215
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@north-light/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.472",
|
|
4
4
|
"description": "crtr — agent runtime with memory, plugins, and marketplaces",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
"activate:pull": "git pull && npm install && npm run activate",
|
|
94
94
|
"build:api": "npm run build:identity && tsc -p packages/crouter-api/tsconfig.json",
|
|
95
95
|
"build:env-docker": "tsc -p packages/crouter-env-docker/tsconfig.json",
|
|
96
|
-
"build:sdk": "npm run build:api && npm run build:identity &&
|
|
96
|
+
"build:sdk": "npm run build:api && npm run build:identity && npm run build --workspace packages/crouter-sdk",
|
|
97
97
|
"build:plugin": "tsc -p packages/crouter-plugin/tsconfig.json",
|
|
98
98
|
"typecheck:pi-packages": "npx tsc -p src/builtin-pi-packages/pi-crtr-extensions/tsconfig.json",
|
|
99
99
|
"test:runtime-packaging": "node --test scripts/runtime-smoke.test.mjs scripts/pack-args.test.mjs scripts/ensure-native-linux.test.mjs scripts/release-version.test.mjs",
|
|
@@ -137,7 +137,7 @@
|
|
|
137
137
|
},
|
|
138
138
|
"license": "UNLICENSED",
|
|
139
139
|
"dependencies": {
|
|
140
|
-
"@north-light/identity": "0.3.
|
|
140
|
+
"@north-light/identity": "0.3.472",
|
|
141
141
|
"@earendil-works/pi-agent-core": "1.1.0",
|
|
142
142
|
"@earendil-works/pi-ai": "1.1.0",
|
|
143
143
|
"@earendil-works/pi-coding-agent": "1.1.0",
|
package/runtime.lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@north-light/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.472",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@north-light/cli",
|
|
9
|
-
"version": "0.3.
|
|
9
|
+
"version": "0.3.472",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "UNLICENSED",
|
|
12
12
|
"workspaces": [
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"@earendil-works/pi-ai": "1.1.0",
|
|
19
19
|
"@earendil-works/pi-coding-agent": "1.1.0",
|
|
20
20
|
"@earendil-works/pi-tui": "1.1.0",
|
|
21
|
-
"@north-light/identity": "0.3.
|
|
21
|
+
"@north-light/identity": "0.3.472",
|
|
22
22
|
"cron-parser": "^5.6.0",
|
|
23
23
|
"esbuild": "^0.27.7",
|
|
24
24
|
"jose": "^6.2.12",
|
|
@@ -50,6 +50,8 @@
|
|
|
50
50
|
"name": "@north-light/docs",
|
|
51
51
|
"version": "0.0.0",
|
|
52
52
|
"dependencies": {
|
|
53
|
+
"@fontsource-variable/geist": "^5.3.0",
|
|
54
|
+
"@fontsource-variable/geist-mono": "^5.3.0",
|
|
53
55
|
"@fontsource-variable/inter": "^5.3.0",
|
|
54
56
|
"@tanstack/react-router": "^1.170.41",
|
|
55
57
|
"@tanstack/react-start": "^1.168.60",
|
|
@@ -2469,6 +2471,24 @@
|
|
|
2469
2471
|
"integrity": "sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==",
|
|
2470
2472
|
"license": "MIT"
|
|
2471
2473
|
},
|
|
2474
|
+
"node_modules/@fontsource-variable/geist": {
|
|
2475
|
+
"version": "5.3.0",
|
|
2476
|
+
"resolved": "https://registry.npmjs.org/@fontsource-variable/geist/-/geist-5.3.0.tgz",
|
|
2477
|
+
"integrity": "sha512-j0m+vLQuG5XAYoHtGCVu0spvlGreR3EzpECUVzkFmI1mTVnAO38l/NEPDCFgZ177JxzYJCLSmTQibIiYPilGrA==",
|
|
2478
|
+
"license": "OFL-1.1",
|
|
2479
|
+
"funding": {
|
|
2480
|
+
"url": "https://github.com/sponsors/ayuhito"
|
|
2481
|
+
}
|
|
2482
|
+
},
|
|
2483
|
+
"node_modules/@fontsource-variable/geist-mono": {
|
|
2484
|
+
"version": "5.3.0",
|
|
2485
|
+
"resolved": "https://registry.npmjs.org/@fontsource-variable/geist-mono/-/geist-mono-5.3.0.tgz",
|
|
2486
|
+
"integrity": "sha512-vBbuwDEo9AkrqADMXOrlAR3DFcJi4/JxeuU43FoiQERnNwsfXNnvxvReZG02cQKmyk4DZkZdBZX3oTDvy2zBAw==",
|
|
2487
|
+
"license": "OFL-1.1",
|
|
2488
|
+
"funding": {
|
|
2489
|
+
"url": "https://github.com/sponsors/ayuhito"
|
|
2490
|
+
}
|
|
2491
|
+
},
|
|
2472
2492
|
"node_modules/@fontsource-variable/inter": {
|
|
2473
2493
|
"version": "5.3.0",
|
|
2474
2494
|
"resolved": "https://registry.npmjs.org/@fontsource-variable/inter/-/inter-5.3.0.tgz",
|
|
@@ -12095,17 +12115,17 @@
|
|
|
12095
12115
|
},
|
|
12096
12116
|
"packages/crouter-api": {
|
|
12097
12117
|
"name": "@north-light/api",
|
|
12098
|
-
"version": "0.3.
|
|
12118
|
+
"version": "0.3.472",
|
|
12099
12119
|
"license": "UNLICENSED"
|
|
12100
12120
|
},
|
|
12101
12121
|
"packages/crouter-env-docker": {
|
|
12102
12122
|
"name": "@north-light/env-docker",
|
|
12103
|
-
"version": "0.3.
|
|
12123
|
+
"version": "0.3.472",
|
|
12104
12124
|
"license": "UNLICENSED"
|
|
12105
12125
|
},
|
|
12106
12126
|
"packages/crouter-identity": {
|
|
12107
12127
|
"name": "@north-light/identity",
|
|
12108
|
-
"version": "0.3.
|
|
12128
|
+
"version": "0.3.472",
|
|
12109
12129
|
"license": "UNLICENSED",
|
|
12110
12130
|
"dependencies": {
|
|
12111
12131
|
"jose": "^6.2.1"
|
|
@@ -12113,20 +12133,20 @@
|
|
|
12113
12133
|
},
|
|
12114
12134
|
"packages/crouter-plugin": {
|
|
12115
12135
|
"name": "@north-light/plugin",
|
|
12116
|
-
"version": "0.3.
|
|
12136
|
+
"version": "0.3.472",
|
|
12117
12137
|
"license": "UNLICENSED",
|
|
12118
12138
|
"dependencies": {
|
|
12119
|
-
"@north-light/api": "0.3.
|
|
12120
|
-
"@north-light/identity": "0.3.
|
|
12139
|
+
"@north-light/api": "0.3.472",
|
|
12140
|
+
"@north-light/identity": "0.3.472"
|
|
12121
12141
|
}
|
|
12122
12142
|
},
|
|
12123
12143
|
"packages/crouter-sdk": {
|
|
12124
12144
|
"name": "@north-light/sdk",
|
|
12125
|
-
"version": "0.3.
|
|
12145
|
+
"version": "0.3.472",
|
|
12126
12146
|
"license": "UNLICENSED",
|
|
12127
12147
|
"dependencies": {
|
|
12128
|
-
"@north-light/api": "0.3.
|
|
12129
|
-
"@north-light/identity": "0.3.
|
|
12148
|
+
"@north-light/api": "0.3.472",
|
|
12149
|
+
"@north-light/identity": "0.3.472",
|
|
12130
12150
|
"jose": "^6.2.1"
|
|
12131
12151
|
},
|
|
12132
12152
|
"bin": {
|