@north-light/cli 0.3.478 → 0.3.479

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.
@@ -39,7 +39,7 @@ npx @north-light/sdk skill
39
39
 
40
40
  The project copy is the default so it is committed with the project, and teammates get it from git. It matches the SDK version your project pins.
41
41
 
42
- Checked on production: the files `init` writes, that `init` in a directory that already has a `package.json` pins the SDK and writes the key and skill but no app code, and that `skill` rewrites both skill files. `init` in such a directory writes no `.gitignore`, so add `local/` to yours. Which agent loads which directory is from each agent's documentation and is not yet checked on production for every agent.
42
+ Checked on production: the files `init` writes, that `init` in a directory that already has a `package.json` pins the SDK and writes the key and skill but no app code, and that `skill` rewrites both skill files. In such a directory `init` creates a `.gitignore` with `local/`, or adds `local/` to the one you have, so the key stays out of git. Which agent loads which directory is from each agent's documentation and is not yet checked on production for every agent.
43
43
 
44
44
  ## Without a project: the hosted skill
45
45
 
@@ -12,11 +12,13 @@ A run is an agent that works on a person's Northlight runtime, on your behalf an
12
12
 
13
13
  ```ts
14
14
  const runtime = await oauth.client(userId); // a client on this person's runtime, from their stored connection
15
- const started = await runtime.runs.start({prompt: 'Reply with exactly the word: pong', scopes: ['crtr:llm', 'crtr:act']});
15
+ // The prompt is the instruction; the message stored with it joins the first turn, so `reply` can follow that turn.
16
+ const started = await runtime.runs.start({prompt: 'Reply with exactly the word: pong', message: 'ping', scopes: ['crtr:llm', 'crtr:act']});
17
+ const reply = await runtime.runs.reply(started.run_id, started.message!).collect(); // reply.text is what the agent said
16
18
  const run = await runtime.runs.wait(started.run_id); // status is 'idle' once the reply is done
17
19
  ```
18
20
 
19
- `runs.start` returns at once with a `run_id`. `runs.wait(runId)` long-polls until the run is no longer `running`.
21
+ `runs.start` returns at once with a `run_id`. Passing `message` stores a first message with the run, and `runs.reply(runId, started.message)` follows the turn that answers it (`collect()` gives its `text`). `runs.wait(runId)` long-polls until the run is no longer `running`.
20
22
 
21
23
  ## Status
22
24
 
@@ -43,7 +45,7 @@ A `settled` run has an `outcome`:
43
45
  | `deadline_exceeded` | A deadline ended it. |
44
46
  | `runtime_error` | The runtime failed. `details.crouter_reason` says why. |
45
47
 
46
- `last_report` is the agent's last report text. To get data back, give the run an `output_schema` and read it with `runs.parse`: see [[northlight-apps/guides/typed-results]]. Do not parse the assistant's text out of `runs.trace`. The trace is what the model said, not a value anything checked.
48
+ A run that is only `idle` has no `outcome`. Read what the agent said from `runs.reply(...).collect()`. `last_report` is present only if the agent pushed a report. To get data back, give the run an `output_schema` and read it with `runs.parse`: see [[northlight-apps/guides/typed-results]]. Do not parse the assistant's text out of `runs.trace`. The trace is what the model said, not a value anything checked.
47
49
 
48
50
  ## Scopes
49
51
 
@@ -13,7 +13,7 @@ npx @north-light/sdk@latest init my-app
13
13
 
14
14
  This command creates a small Node server, a signing key and an env file. About ten minutes later you have an app on your laptop that signs you in with Northlight, starts an agent run on your own Northlight runtime, and texts your phone. The app runs on your machine and talks to Northlight's production directory at `https://account.northlight.ai`. There is no local Northlight stack to run, and you do not need crouter.
15
15
 
16
- You need Node.js 22 or later and a phone that can receive iMessage. The quickstart ran on Node 22.14, 22.22 and 26.12; `--env-file` needs Node 20.6 or later.
16
+ You need Node.js 20.6 or later (the start command uses `--env-file`) and a phone that can receive iMessage. The server was run on Node 20.6, 20.20, 22.14, 22.22 and 26.12.
17
17
 
18
18
  Using a coding agent? Run the same `init` command, or tell your agent: *Read https://sdk.docs.northlight.ai/SKILL.md and follow it.* See [[northlight-apps/coding-agents]].
19
19
 
@@ -109,9 +109,9 @@ node --env-file=local/app.env --import tsx server.ts
109
109
 
110
110
  (`npm start` runs the same command.) You should see `Listening on http://localhost:3000`.
111
111
 
112
- Open `http://localhost:3000/auth/login`. Your browser goes to `account.northlight.ai`. Sign in if asked, read the consent page, and choose Allow. You land back on `http://localhost:3000/` with "Signed in" and your Northlight user id. Press **Start a run and send me a text**. The page answers "Done", shows the run's status `idle` and the JSON of the text request (a `message_id` and a `note` from the sms provider), and your phone receives "Hello from my first Northlight app." from Northlight's number.
112
+ Open `http://localhost:3000/auth/login`. Your browser goes to `account.northlight.ai`. Sign in if asked, read the consent page, and choose Allow. You land back on `http://localhost:3000/` with "Signed in" and your Northlight user id. Press **Start a run and send me a text**. The page answers "Done", shows the run's status `idle`, the run's reply (`pong`) and the text's `message_id`, and your phone receives "Hello from my first Northlight app." from Northlight's number.
113
113
 
114
- Checked on production, as the account that owns the app: the server starts (with `npm start` too), `/` shows the sign-in link, and `/auth/login` answers `303` to `https://account.northlight.ai/authorize?client_id=…&scope=openid+profile+crtr%3Allm+crtr%3Aact+sms%3Asend…&code_challenge_method=S256…`. The consent page listed the four permissions and the monthly cap, **Allow** returned to `http://localhost:3000/` with "Signed in" and the user id, and the button page answered "Done" with status `idle` and a `message_id` for the text. In the console the key then read "Last used just now". Not yet checked on production: that the text reached the phone (the `message_id` is the provider's answer, not a delivery receipt), that the run's reply was `pong`, and the `426` case. Signing in as a second person needs a second account and is not covered here.
114
+ Checked on production, as the account that owns the app: the server starts (with `npm start` too), `/` shows the sign-in link, and `/auth/login` answers `303` to `https://account.northlight.ai/authorize?client_id=…&scope=openid+profile+crtr%3Allm+crtr%3Aact+sms%3Asend…&code_challenge_method=S256…`. The consent page listed the four permissions and the monthly cap, **Allow** returned to `http://localhost:3000/` with "Signed in" and the user id, and the button page answered "Done" with status `idle` and a `message_id` for the text. In the console the key then read "Last used just now". Not yet checked on production: that the text reached the phone (the `message_id` is the provider's answer, not a delivery receipt), and the `426` case. The `pong` reply is read through `runs.reply` and has not been run on production since this example changed. Signing in as a second person needs a second account and is not covered here.
115
115
 
116
116
  If `/` shows "One step left", `CROUTER_CLIENT_ID` is empty: repeat step 5 and restart. If a call fails with `426 runtime_version_unsupported`, your SDK is newer than the Northlight runtimes. Install the previous version with `npm i --save-exact @north-light/sdk@<version>` and tell support@northlight.ai.
117
117
 
@@ -193,11 +193,14 @@ const server = createServer(async (req, res) => {
193
193
 
194
194
  if (url.pathname === '/run' && req.method === 'POST') {
195
195
  const runtime = await oauth.client(userId); // a client on this person's runtime, from their stored connection
196
- const started = await runtime.runs.start({prompt: 'Reply with exactly the word: pong', scopes: ['crtr:llm', 'crtr:act']});
196
+ // The prompt is the instruction; the message stored with it joins the first turn, so `reply` can follow that turn.
197
+ const started = await runtime.runs.start({prompt: 'Reply with exactly the word: pong', message: 'ping', scopes: ['crtr:llm', 'crtr:act']});
198
+ const reply = await runtime.runs.reply(started.run_id, started.message!).collect(); // reply.text is what the agent said
197
199
  const run = await runtime.runs.wait(started.run_id); // status is 'idle' once the reply is done
198
200
  // Texts the signed-in person's own verified phone through the sms provider (50 per person per UTC day).
199
201
  const sent = await runtime.providers.call({provider: 'sms', tool: 'send', arguments: {body: 'Hello from my first Northlight app.'}});
200
- return page(res, 200, `<h1>Done</h1><p>The run finished with status <code>${escapeHtml(run.status)}</code>. A text is on its way to your phone.</p><pre>${escapeHtml(JSON.stringify({run: run.run_id, outcome: run.outcome, report: run.last_report, text: sent}, null, 2))}</pre><p><a href="/">Back</a></p>`);
202
+ const {message_id} = sent as {message_id: string}; // the provider also returns a note meant for agents; it is not shown here
203
+ return page(res, 200, `<h1>Done</h1><p>The run finished with status <code>${escapeHtml(run.status)}</code> and replied <code>${escapeHtml(reply.text)}</code>. A text is on its way to your phone.</p><pre>${escapeHtml(JSON.stringify({run: run.run_id, status: run.status, reply: reply.text, message_id}, null, 2))}</pre><p><a href="/">Back</a></p>`);
201
204
  }
202
205
  return page(res, 200, `<h1>Signed in</h1><p>You are <code>${escapeHtml(userId)}</code>.</p><form method="post" action="/run"><button>Start a run and send me a text</button></form>`);
203
206
  } catch (error) {
@@ -36,7 +36,7 @@ npx @north-light/sdk skill
36
36
 
37
37
  The project copy is the default so it is committed with the project, and teammates get it from git. It matches the SDK version your project pins.
38
38
 
39
- Checked on production: the files `init` writes, that `init` in a directory that already has a `package.json` pins the SDK and writes the key and skill but no app code, and that `skill` rewrites both skill files. `init` in such a directory writes no `.gitignore`, so add `local/` to yours. Which agent loads which directory is from each agent's documentation and is not yet checked on production for every agent.
39
+ Checked on production: the files `init` writes, that `init` in a directory that already has a `package.json` pins the SDK and writes the key and skill but no app code, and that `skill` rewrites both skill files. In such a directory `init` creates a `.gitignore` with `local/`, or adds `local/` to the one you have, so the key stays out of git. Which agent loads which directory is from each agent's documentation and is not yet checked on production for every agent.
40
40
 
41
41
  ## Without a project: the hosted skill
42
42
 
@@ -9,7 +9,7 @@ A run is an agent that works on a person's Northlight runtime, on your behalf an
9
9
 
10
10
  <include lang="ts">../../../examples/apps/quickstart-server.ts#run</include>
11
11
 
12
- `runs.start` returns at once with a `run_id`. `runs.wait(runId)` long-polls until the run is no longer `running`.
12
+ `runs.start` returns at once with a `run_id`. Passing `message` stores a first message with the run, and `runs.reply(runId, started.message)` follows the turn that answers it (`collect()` gives its `text`). `runs.wait(runId)` long-polls until the run is no longer `running`.
13
13
 
14
14
  ## Status
15
15
 
@@ -36,7 +36,7 @@ A `settled` run has an `outcome`:
36
36
  | `deadline_exceeded` | A deadline ended it. |
37
37
  | `runtime_error` | The runtime failed. `details.crouter_reason` says why. |
38
38
 
39
- `last_report` is the agent's last report text. To get data back, give the run an `output_schema` and read it with `runs.parse`: see [Typed results](/docs/guides/typed-results). Do not parse the assistant's text out of `runs.trace`. The trace is what the model said, not a value anything checked.
39
+ A run that is only `idle` has no `outcome`. Read what the agent said from `runs.reply(...).collect()`. `last_report` is present only if the agent pushed a report. To get data back, give the run an `output_schema` and read it with `runs.parse`: see [Typed results](/docs/guides/typed-results). Do not parse the assistant's text out of `runs.trace`. The trace is what the model said, not a value anything checked.
40
40
 
41
41
  ## Scopes
42
42
 
@@ -11,7 +11,7 @@ npx @north-light/sdk@latest init my-app
11
11
 
12
12
  This command creates a small Node server, a signing key and an env file. About ten minutes later you have an app on your laptop that signs you in with Northlight, starts an agent run on your own Northlight runtime, and texts your phone. The app runs on your machine and talks to Northlight's production directory at `https://account.northlight.ai`. There is no local Northlight stack to run, and you do not need crouter.
13
13
 
14
- You need Node.js 22 or later and a phone that can receive iMessage. The quickstart ran on Node 22.14, 22.22 and 26.12; `--env-file` needs Node 20.6 or later.
14
+ You need Node.js 20.6 or later (the start command uses `--env-file`) and a phone that can receive iMessage. The server was run on Node 20.6, 20.20, 22.14, 22.22 and 26.12.
15
15
 
16
16
  Using a coding agent? Run the same `init` command, or tell your agent: *Read https://sdk.docs.northlight.ai/SKILL.md and follow it.* See [Use your coding agent](/docs/coding-agents).
17
17
 
@@ -107,9 +107,9 @@ node --env-file=local/app.env --import tsx server.ts
107
107
 
108
108
  (`npm start` runs the same command.) You should see `Listening on http://localhost:3000`.
109
109
 
110
- Open `http://localhost:3000/auth/login`. Your browser goes to `account.northlight.ai`. Sign in if asked, read the consent page, and choose Allow. You land back on `http://localhost:3000/` with "Signed in" and your Northlight user id. Press **Start a run and send me a text**. The page answers "Done", shows the run's status `idle` and the JSON of the text request (a `message_id` and a `note` from the sms provider), and your phone receives "Hello from my first Northlight app." from Northlight's number.
110
+ Open `http://localhost:3000/auth/login`. Your browser goes to `account.northlight.ai`. Sign in if asked, read the consent page, and choose Allow. You land back on `http://localhost:3000/` with "Signed in" and your Northlight user id. Press **Start a run and send me a text**. The page answers "Done", shows the run's status `idle`, the run's reply (`pong`) and the text's `message_id`, and your phone receives "Hello from my first Northlight app." from Northlight's number.
111
111
 
112
- Checked on production, as the account that owns the app: the server starts (with `npm start` too), `/` shows the sign-in link, and `/auth/login` answers `303` to `https://account.northlight.ai/authorize?client_id=…&scope=openid+profile+crtr%3Allm+crtr%3Aact+sms%3Asend…&code_challenge_method=S256…`. The consent page listed the four permissions and the monthly cap, **Allow** returned to `http://localhost:3000/` with "Signed in" and the user id, and the button page answered "Done" with status `idle` and a `message_id` for the text. In the console the key then read "Last used just now". Not yet checked on production: that the text reached the phone (the `message_id` is the provider's answer, not a delivery receipt), that the run's reply was `pong`, and the `426` case. Signing in as a second person needs a second account and is not covered here.
112
+ Checked on production, as the account that owns the app: the server starts (with `npm start` too), `/` shows the sign-in link, and `/auth/login` answers `303` to `https://account.northlight.ai/authorize?client_id=…&scope=openid+profile+crtr%3Allm+crtr%3Aact+sms%3Asend…&code_challenge_method=S256…`. The consent page listed the four permissions and the monthly cap, **Allow** returned to `http://localhost:3000/` with "Signed in" and the user id, and the button page answered "Done" with status `idle` and a `message_id` for the text. In the console the key then read "Last used just now". Not yet checked on production: that the text reached the phone (the `message_id` is the provider's answer, not a delivery receipt), and the `426` case. The `pong` reply is read through `runs.reply` and has not been run on production since this example changed. Signing in as a second person needs a second account and is not covered here.
113
113
 
114
114
  If `/` shows "One step left", `CROUTER_CLIENT_ID` is empty: repeat step 5 and restart. If a call fails with `426 runtime_version_unsupported`, your SDK is newer than the Northlight runtimes. Install the previous version with `npm i --save-exact @north-light/sdk@<version>` and tell support@northlight.ai.
115
115
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@north-light/cli",
3
- "version": "0.3.478",
3
+ "version": "0.3.479",
4
4
  "description": "crtr — agent runtime with memory, plugins, and marketplaces",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -137,7 +137,7 @@
137
137
  },
138
138
  "license": "UNLICENSED",
139
139
  "dependencies": {
140
- "@north-light/identity": "0.3.478",
140
+ "@north-light/identity": "0.3.479",
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",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@north-light/identity",
3
- "version": "0.3.478",
3
+ "version": "0.3.479",
4
4
  "description": "Northlight access-token verification and scope rules",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/runtime.lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@north-light/cli",
3
- "version": "0.3.478",
3
+ "version": "0.3.479",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@north-light/cli",
9
- "version": "0.3.478",
9
+ "version": "0.3.479",
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.478",
21
+ "@north-light/identity": "0.3.479",
22
22
  "cron-parser": "^5.6.0",
23
23
  "esbuild": "^0.27.7",
24
24
  "jose": "^6.2.12",
@@ -12116,17 +12116,17 @@
12116
12116
  },
12117
12117
  "packages/crouter-api": {
12118
12118
  "name": "@north-light/api",
12119
- "version": "0.3.478",
12119
+ "version": "0.3.479",
12120
12120
  "license": "UNLICENSED"
12121
12121
  },
12122
12122
  "packages/crouter-env-docker": {
12123
12123
  "name": "@north-light/env-docker",
12124
- "version": "0.3.478",
12124
+ "version": "0.3.479",
12125
12125
  "license": "UNLICENSED"
12126
12126
  },
12127
12127
  "packages/crouter-identity": {
12128
12128
  "name": "@north-light/identity",
12129
- "version": "0.3.478",
12129
+ "version": "0.3.479",
12130
12130
  "license": "UNLICENSED",
12131
12131
  "dependencies": {
12132
12132
  "jose": "^6.2.1"
@@ -12134,25 +12134,28 @@
12134
12134
  },
12135
12135
  "packages/crouter-plugin": {
12136
12136
  "name": "@north-light/plugin",
12137
- "version": "0.3.478",
12137
+ "version": "0.3.479",
12138
12138
  "license": "UNLICENSED",
12139
12139
  "dependencies": {
12140
- "@north-light/api": "0.3.478",
12141
- "@north-light/identity": "0.3.478"
12140
+ "@north-light/api": "0.3.479",
12141
+ "@north-light/identity": "0.3.479"
12142
12142
  }
12143
12143
  },
12144
12144
  "packages/crouter-sdk": {
12145
12145
  "name": "@north-light/sdk",
12146
- "version": "0.3.478",
12146
+ "version": "0.3.479",
12147
12147
  "license": "UNLICENSED",
12148
12148
  "dependencies": {
12149
- "@north-light/api": "0.3.478",
12150
- "@north-light/identity": "0.3.478",
12149
+ "@north-light/api": "0.3.479",
12150
+ "@north-light/identity": "0.3.479",
12151
12151
  "jose": "^6.2.1"
12152
12152
  },
12153
12153
  "bin": {
12154
12154
  "crouter-sdk": "dist/keygen-cli.js"
12155
12155
  },
12156
+ "engines": {
12157
+ "node": ">=20.6.0"
12158
+ },
12156
12159
  "peerDependencies": {
12157
12160
  "pg": "^8.0.0"
12158
12161
  },