@north-light/cli 0.3.473 → 0.3.475
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/dist/builtin-memory/crouter-sdk/README.md +3 -46
- package/dist/builtin-memory/crouter-sdk/client.md +2 -231
- package/dist/builtin-memory/crouter-sdk/errors.md +9 -50
- package/dist/builtin-memory/crouter-sdk/getting-started.md +2 -64
- package/dist/builtin-memory/crouter-sdk/guides/README.md +1 -3
- package/dist/builtin-memory/crouter-sdk/guides/typed-extraction.md +1 -1
- package/dist/builtin-memory/crouter-sdk/streaming.md +1 -70
- package/dist/builtin-memory/northlight-apps/INDEX.md +15 -0
- package/dist/builtin-memory/northlight-apps/cli.md +41 -0
- package/dist/builtin-memory/northlight-apps/coding-agents.md +75 -0
- package/dist/builtin-memory/northlight-apps/concepts/apps-and-sign-in.md +109 -0
- package/dist/builtin-memory/northlight-apps/concepts/connections.md +109 -0
- package/dist/builtin-memory/northlight-apps/concepts/providers.md +76 -0
- package/dist/builtin-memory/northlight-apps/concepts/runs.md +101 -0
- package/dist/builtin-memory/northlight-apps/concepts/scopes-and-grants.md +64 -0
- package/dist/builtin-memory/northlight-apps/concepts/testing-mode.md +54 -0
- package/dist/builtin-memory/northlight-apps/errors.md +119 -0
- package/dist/builtin-memory/northlight-apps/guides/background-runs.md +73 -0
- package/dist/builtin-memory/northlight-apps/guides/files-and-documents.md +61 -0
- package/dist/builtin-memory/northlight-apps/guides/go-live.md +39 -0
- package/dist/builtin-memory/northlight-apps/guides/grant-changes.md +100 -0
- package/dist/builtin-memory/northlight-apps/guides/manage-your-app.md +66 -0
- package/dist/builtin-memory/northlight-apps/guides/many-users.md +63 -0
- package/dist/builtin-memory/northlight-apps/guides/stream-a-run.md +84 -0
- package/dist/builtin-memory/{crouter-sdk/testing.md → northlight-apps/guides/test-your-app.md} +6 -6
- package/dist/builtin-memory/northlight-apps/guides/typed-results.md +99 -0
- package/dist/builtin-memory/northlight-apps/how-it-fits.md +49 -0
- package/dist/builtin-memory/northlight-apps/providers/README.md +39 -0
- package/dist/builtin-memory/northlight-apps/providers/context.md +34 -0
- package/dist/builtin-memory/northlight-apps/providers/device.md +28 -0
- package/dist/builtin-memory/northlight-apps/providers/integrations.md +28 -0
- package/dist/builtin-memory/northlight-apps/providers/search.md +31 -0
- package/dist/builtin-memory/northlight-apps/providers/sms.md +39 -0
- package/dist/builtin-memory/northlight-apps/quickstart.md +210 -0
- package/docs/apps/guides/background-runs.md +1 -8
- package/docs/apps/guides/manage-your-app.md +7 -18
- package/docs/cli/README.md +1 -0
- package/docs/cli/join-northlight.md +58 -0
- package/docs/sdk/README.md +3 -46
- package/docs/sdk/client.md +2 -231
- package/docs/sdk/errors.md +8 -49
- package/docs/sdk/getting-started.md +2 -64
- package/docs/sdk/guides/README.md +1 -3
- package/docs/sdk/guides/typed-extraction.md +1 -1
- package/docs/sdk/streaming.md +1 -70
- package/package.json +2 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +12 -12
- package/dist/builtin-memory/crouter-sdk/error-envelope.md +0 -38
- package/dist/builtin-memory/crouter-sdk/guides/apps-on-northlight.md +0 -423
- package/dist/builtin-memory/crouter-sdk/guides/apps-with-many-users.md +0 -61
- package/dist/builtin-memory/crouter-sdk/memory.md +0 -49
- package/docs/sdk/error-envelope.md +0 -36
- package/docs/sdk/guides/apps-on-northlight.md +0 -419
- package/docs/sdk/guides/apps-with-many-users.md +0 -59
- package/docs/sdk/memory.md +0 -47
- package/docs/sdk/testing.md +0 -191
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: knowledge
|
|
3
|
+
when-and-why-to-read: When you need Quick setup, this knowledge should be read
|
|
4
|
+
because From no account to a running app that signs a person in with
|
|
5
|
+
Northlight, starts a run on their runtime and sends them a text.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Quick setup
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npx @north-light/sdk@latest init my-app
|
|
12
|
+
```
|
|
13
|
+
|
|
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
|
+
|
|
16
|
+
You need Node.js 22.19 or later and a phone that can receive iMessage.
|
|
17
|
+
|
|
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
|
+
|
|
20
|
+
Each step below ends with what you should see when it worked. A step marked "(not yet checked on production)" is written from the source code and has not been run end to end against `account.northlight.ai`.
|
|
21
|
+
|
|
22
|
+
## 1. Create an account
|
|
23
|
+
|
|
24
|
+
Open `https://account.northlight.ai/signup` and create your account. Signup texts a confirmation code to your phone, and that text is sent over iMessage. A number that cannot receive iMessage gets "We couldn't send the text", and retrying does not help. One phone number belongs to one account.
|
|
25
|
+
|
|
26
|
+
You should see: your account's page after you enter the code. Opening `https://account.northlight.ai/console` while signed in shows the console, where your apps are listed. Checked on production: `/signup` redirects to the phone step at `/signup/phone`, and `/console` while signed out redirects to sign-in. Completing signup and the signed-in console are not yet checked on production.
|
|
27
|
+
|
|
28
|
+
## 2. Scaffold the app
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npx @north-light/sdk@latest init my-app
|
|
32
|
+
cd my-app
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`init` writes these files into `my-app/`:
|
|
36
|
+
|
|
37
|
+
| File | What it is |
|
|
38
|
+
|---|---|
|
|
39
|
+
| `package.json` | Pins `@north-light/sdk` to the exact version of the CLI you ran, and adds `tsx`. |
|
|
40
|
+
| `server.ts` | The server shown at the end of this page. |
|
|
41
|
+
| `local/app-key.json` | Your app's private signing key, mode `0600`. Never commit it or paste it anywhere. |
|
|
42
|
+
| `local/app.env` | The settings the server reads: `CROUTER_ISSUER`, `APP_ORIGIN`, an empty `CROUTER_CLIENT_ID=` and `CROUTER_KEY_FILE`. |
|
|
43
|
+
| `.gitignore` | Keeps `local/` and `node_modules/` out of git. |
|
|
44
|
+
| `.agents/skills/northlight/SKILL.md` and `.claude/skills/northlight/SKILL.md` | The Northlight skill for your coding agent. |
|
|
45
|
+
|
|
46
|
+
You should see, on the terminal: `Created a Northlight app in my-app:` with the file names, the path of the private key, the next steps in order, and last, on its own line, the **public key** as one line of JSON:
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{"kty":"EC","x":"…","y":"…","crv":"P-256","kid":"…","alg":"ES256","use":"sig"}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Copy that line. You paste it into the console in the next step. If a file already exists, `init` refuses, names it and exits non-zero without writing anything.
|
|
53
|
+
|
|
54
|
+
Checked: this output and these files, from the SDK source built on this branch in an empty directory with no crtr on `PATH`. `npx @north-light/sdk@latest` fetches the npm `latest` release, and that release does not include `init` until the site goes live (not yet checked on production).
|
|
55
|
+
|
|
56
|
+
## 3. Register the app in the console
|
|
57
|
+
|
|
58
|
+
Open `https://account.northlight.ai/console/new`. The page is titled **Register an app**. Fill in:
|
|
59
|
+
|
|
60
|
+
| Field | Value |
|
|
61
|
+
|---|---|
|
|
62
|
+
| **Name** | Shown to people on the consent page and in their account. |
|
|
63
|
+
| **Origin URL** | `http://localhost:3000` |
|
|
64
|
+
| **Logo URL** | Required. A square image shown beside your app's name. |
|
|
65
|
+
| **Why your app needs access** | One sentence. It is quoted on the consent page as your words. |
|
|
66
|
+
| **Redirect URIs** | `http://localhost:3000/auth/callback`, one URL per line. Sign-in returns only to a URI listed here, matched exactly. `localhost` and `127.0.0.1` are different URIs, and the port counts. |
|
|
67
|
+
| **Public key (optional)** | The JSON line `init` printed. |
|
|
68
|
+
|
|
69
|
+
Choose **Register app**.
|
|
70
|
+
|
|
71
|
+
You should see: a page titled "*your app's name* is registered" with a **Client ID** and, under it, the key ID of the key you pasted. Copy the client ID. If it says "Next: add your app's public key on the Sign-in tab", the key was left out: open the app's **Sign-in** tab and choose **Add key**. The app starts asking for `crtr:llm` and `crtr:act`.
|
|
72
|
+
|
|
73
|
+
The field labels are from the console's source. Pasting a key at registration is not yet checked on production.
|
|
74
|
+
|
|
75
|
+
## 4. Add the permission to text
|
|
76
|
+
|
|
77
|
+
On the app's page, open the **What it asks for** tab and choose **Add permissions**. Add `sms:send` from the list.
|
|
78
|
+
|
|
79
|
+
You should see: `sms:send` under what your app asks for, next to `crtr:llm` and `crtr:act`. The server in this guide asks for all three, so without `sms:send` the consent page does not offer it and the text in step 6 is refused. Take scope strings from this list. Do not invent them. See [[northlight-apps/concepts/scopes-and-grants]].
|
|
80
|
+
|
|
81
|
+
Not yet checked on production.
|
|
82
|
+
|
|
83
|
+
## 5. Write the client ID into the env file
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
npx @north-light/sdk env --client-id <client id>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
You should see:
|
|
90
|
+
|
|
91
|
+
```text
|
|
92
|
+
Set in local/app.env:
|
|
93
|
+
CROUTER_ISSUER=https://account.northlight.ai
|
|
94
|
+
CROUTER_CLIENT_ID=<client id>
|
|
95
|
+
APP_ORIGIN=http://localhost:3000
|
|
96
|
+
Key file: local/app-key.json (set CROUTER_KEY_FILE to change it).
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`local/app.env` now has all four settings. The command makes no network call and keeps every other line in the file. Add `--origin <url>` if your app does not run on `http://localhost:3000`.
|
|
100
|
+
|
|
101
|
+
Checked from the SDK source built on this branch.
|
|
102
|
+
|
|
103
|
+
## 6. Run it
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
npm i
|
|
107
|
+
node --env-file=local/app.env --import tsx server.ts
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
(`npm start` runs the same command.) You should see `Listening on http://localhost:3000`.
|
|
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 your phone receives "Hello from my first Northlight app." from Northlight's number.
|
|
113
|
+
|
|
114
|
+
What was checked: the server starts, `/` 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, the callback, the run and the text are not yet checked on production.
|
|
115
|
+
|
|
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
|
+
|
|
118
|
+
## 7. What next
|
|
119
|
+
|
|
120
|
+
- Only you can sign in until you add testers: see [[northlight-apps/concepts/testing-mode]].
|
|
121
|
+
- Keep each person's sign-in across restarts and processes: see [[northlight-apps/concepts/connections]].
|
|
122
|
+
- See what `server.ts` does and where each piece runs: [[northlight-apps/how-it-fits]].
|
|
123
|
+
|
|
124
|
+
## The server `init` writes
|
|
125
|
+
|
|
126
|
+
This is `examples/apps/quickstart-server.ts`. The SDK reads no environment: the server passes the values in.
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
// The server `npx @north-light/sdk init` writes as server.ts: sign a person in with Northlight, then start a run on
|
|
130
|
+
// their runtime and send them a text. It keeps sign-in state in memory, so it is a sketch for one process, not production.
|
|
131
|
+
import {createServer, type IncomingMessage, type ServerResponse} from 'node:http';
|
|
132
|
+
import {randomBytes} from 'node:crypto';
|
|
133
|
+
import {readFile} from 'node:fs/promises';
|
|
134
|
+
import {MemoryConnectionStore, OAuth2Client, authorizationRefusalOf} from '@north-light/sdk';
|
|
135
|
+
|
|
136
|
+
const origin = process.env.APP_ORIGIN ?? 'http://localhost:3000';
|
|
137
|
+
|
|
138
|
+
const store = new MemoryConnectionStore(); // one process only; use a persistent ConnectionStore in production
|
|
139
|
+
const oauth = new OAuth2Client({
|
|
140
|
+
clientId: process.env.CROUTER_CLIENT_ID ?? '',
|
|
141
|
+
issuer: process.env.CROUTER_ISSUER ?? 'https://account.northlight.ai',
|
|
142
|
+
redirectUri: `${origin}/auth/callback`,
|
|
143
|
+
privateKey: JSON.parse(await readFile(process.env.CROUTER_KEY_FILE ?? 'local/app-key.json', 'utf8')),
|
|
144
|
+
store,
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
const pending = new Map<string, {codeVerifier: string; nonce: string}>(); // sign-ins in flight, keyed by state
|
|
148
|
+
const sessions = new Map<string, string>(); // session id -> userId; use a real session store in production
|
|
149
|
+
|
|
150
|
+
const escapeHtml = (text: string) => text.replace(/[&<>"']/g, (c) => `&#${c.charCodeAt(0)};`);
|
|
151
|
+
const page = (res: ServerResponse, status: number, body: string) => {
|
|
152
|
+
res.writeHead(status, {'content-type': 'text/html; charset=utf-8'}).end(`<!doctype html><meta charset="utf-8"><title>Northlight app</title><body style="font-family:system-ui;max-width:40rem;margin:3rem auto">${body}`);
|
|
153
|
+
};
|
|
154
|
+
const userOf = (req: IncomingMessage) => {
|
|
155
|
+
const sid = /(?:^|; )sid=([\w-]+)/.exec(req.headers.cookie ?? '')?.[1];
|
|
156
|
+
return sid ? sessions.get(sid) : undefined;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const server = createServer(async (req, res) => {
|
|
160
|
+
const url = new URL(req.url ?? '/', origin);
|
|
161
|
+
try {
|
|
162
|
+
if (!process.env.CROUTER_CLIENT_ID) {
|
|
163
|
+
return page(res, 200, '<h1>One step left</h1><p>Register the app in the console, then run <code>npx @north-light/sdk env --client-id <client id></code> and restart this server.</p>');
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (url.pathname === '/auth/login') {
|
|
167
|
+
const {url: authorizeUrl, codeVerifier, state, nonce} = await oauth.authorizeUrl({
|
|
168
|
+
scopes: ['openid', 'profile', 'crtr:llm', 'crtr:act', 'sms:send'],
|
|
169
|
+
state: randomBytes(16).toString('base64url'),
|
|
170
|
+
});
|
|
171
|
+
pending.set(state, {codeVerifier, nonce});
|
|
172
|
+
return void res.writeHead(303, {location: authorizeUrl}).end();
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (url.pathname === '/auth/callback') {
|
|
176
|
+
if (authorizationRefusalOf(url)?.code === 'app_in_testing') {
|
|
177
|
+
return page(res, 403, '<h1>This app is still in testing</h1><p>Ask its owner to add you on the Testers tab in the console.</p>');
|
|
178
|
+
}
|
|
179
|
+
const state = url.searchParams.get('state') ?? '';
|
|
180
|
+
const code = url.searchParams.get('code');
|
|
181
|
+
const saved = pending.get(state);
|
|
182
|
+
if (!saved || !code) return page(res, 400, '<p>Start sign-in again at <a href="/auth/login">/auth/login</a>.</p>');
|
|
183
|
+
pending.delete(state);
|
|
184
|
+
const connection = await oauth.exchangeCode({code, codeVerifier: saved.codeVerifier, state, expectedState: state, nonce: saved.nonce});
|
|
185
|
+
await store.save(connection);
|
|
186
|
+
const sid = randomBytes(16).toString('base64url');
|
|
187
|
+
sessions.set(sid, connection.userId);
|
|
188
|
+
return void res.writeHead(303, {location: '/', 'set-cookie': `sid=${sid}; HttpOnly; SameSite=Lax; Path=/`}).end();
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const userId = userOf(req);
|
|
192
|
+
if (!userId) return page(res, 200, '<h1>Northlight app</h1><p><a href="/auth/login">Sign in with Northlight</a></p>');
|
|
193
|
+
|
|
194
|
+
if (url.pathname === '/run' && req.method === 'POST') {
|
|
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']});
|
|
197
|
+
const run = await runtime.runs.wait(started.run_id); // status is 'idle' once the reply is done
|
|
198
|
+
// Texts the signed-in person's own verified phone through the sms provider (50 per person per UTC day).
|
|
199
|
+
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>`);
|
|
201
|
+
}
|
|
202
|
+
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
|
+
} catch (error) {
|
|
204
|
+
console.error(error);
|
|
205
|
+
return page(res, 500, `<h1>Something failed</h1><pre>${escapeHtml(error instanceof Error ? error.message : String(error))}</pre>`);
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
server.listen(Number(new URL(origin).port) || 3000, () => console.log(`Listening on ${origin}`));
|
|
210
|
+
```
|
|
@@ -68,11 +68,4 @@ app.post('/run-notices', async (req, res) => {
|
|
|
68
68
|
|
|
69
69
|
### Set the run notice URL
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
```bash
|
|
74
|
-
crtr cloud apps set-run-notice-url --app-id <client id> --url https://app.example.com/run-notices
|
|
75
|
-
crtr cloud apps set-run-notice-url --app-id <client id> --clear # stop notices
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
The URL must be https (plain http only on localhost in development) and at most 512 characters. `crtr cloud apps get --app-id <client id>` shows it as `run_notice_url`.
|
|
71
|
+
In the [console](https://account.northlight.ai/console), open your app, choose the **What it asks for** tab, and fill in **Notice URL** under **Run notice URL**. Choose **Save**. The URL must be `https` and at most 512 characters. To stop notices, empty the field and save. The full list of console fields is in [Manage your app](/docs/guides/manage-your-app#what-it-asks-for).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Manage your app
|
|
3
|
-
description: What each tab of the Northlight console does for an app you have registered,
|
|
3
|
+
description: What each tab of the Northlight console does for an app you have registered, including setting the run notice URL and deleting the app.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Manage your app
|
|
@@ -31,11 +31,13 @@ Until you retire the old key it is your rollback. After you retire it, a sign-in
|
|
|
31
31
|
- **What your app asks for.** The scopes people are asked to approve. **Add permissions** searches the catalog; take scope strings from it and from `providers.tools`, and do not invent them. `crtr:act` needs `crtr:llm`. See [Scopes and grants](/docs/concepts/scopes-and-grants).
|
|
32
32
|
- **What your app requires.** Providers the app cannot work without. A person is taken through that provider's setup the first time they sign in. See [Grant changes](/docs/guides/grant-changes#wait-on-a-provider-your-app-requires).
|
|
33
33
|
- **Monthly spend cap.** The most your app may spend of each person's AI subscription per month, in whole US dollars.
|
|
34
|
+
- **Run notice URL.** Where Northlight posts a signed notice when one of your app's background runs ends. Type it in **Notice URL** and choose **Save**; empty it and save to turn notices off. The URL must be `https`, at most 512 characters, with no `#fragment`. The directory refuses a plain `http` URL with `run_notice_url` as the field. The field's hint says `http://localhost` is accepted while developing, but the production directory refuses it (checked in the directory source, not yet checked on production), so test notices with a tunnel that gives you an `https` URL. What your app does with a notice is in [Background runs](/docs/guides/background-runs#run-notices).
|
|
34
35
|
|
|
35
36
|
## Profile
|
|
36
37
|
|
|
37
38
|
- **How people see your app**: Name, Origin URL, Logo URL, and **Why your app needs access** (quoted on the consent page as your words). **Save details**.
|
|
38
39
|
- **Marketplace**: **Listed in the marketplace**, Price, **Sign-in start URL** and **Text welcome**. Agents can install a listed app for a person by text. A testing app stays out of the marketplace even when listed. The sign-in start URL is where Install sends people; your `/auth/login` route is a valid one, and a `localhost` URL is accepted. The text welcome is one or two plain sentences, at most 120 characters, with no links.
|
|
40
|
+
- **Delete this app.** At the bottom of the tab; see [Delete an app](#delete-an-app).
|
|
39
41
|
|
|
40
42
|
## Testers
|
|
41
43
|
|
|
@@ -53,23 +55,10 @@ Once your app is live you can keep developing on your laptop against production
|
|
|
53
55
|
|
|
54
56
|
**Register a separate development app.** Do this when you are changing what the app asks for, or need grants and data that live people never touch. Register another app in the console, for example "Texty (dev)", with a `localhost` origin and redirect URI and its own key. It starts in testing and stays there, so only you and your testers can sign in, and it never appears in the marketplace. Point your laptop's environment file at its client ID.
|
|
55
57
|
|
|
56
|
-
##
|
|
58
|
+
## Delete an app
|
|
57
59
|
|
|
58
|
-
|
|
60
|
+
On the **Profile** tab, the **Delete this app** card lists what goes: every person's connection to the app, yours included (each person's runtime deletes the app's runs, space and schedules), the app's sign-ins and keys, and its testers and marketplace listing. This cannot be undone, and the client ID is never reused.
|
|
59
61
|
|
|
60
|
-
|
|
62
|
+
To delete, type the app's full name in **Type the app's name, *name*, to confirm**. **Delete app** stays disabled until what you typed matches the name (surrounding spaces are ignored), and the directory checks the name again when you submit. After it deletes, the console returns to your app list. The app's next refresh for any person fails with `invalid_grant`, its key stops working and its page answers 404.
|
|
61
63
|
|
|
62
|
-
|
|
63
|
-
crtr cloud apps delete --app-id <client id> --confirm <client id>
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
It answers `deleted: true` and `removed_grants`, the number of people's grants it removed. Each person's runtime deletes the app's runs, space and schedules, and the app's next refresh fails with `invalid_grant`. The app's key stops working, its page answers 404 and it disappears from your list. This cannot be undone. You must run it yourself in the terminal of the joined machine: an agent, including your chat agent, is refused with `provider_refused`. If you only want a clean slate for tests, changing the app's redirect URIs and keys is often simpler.
|
|
67
|
-
|
|
68
|
-
### Set the run notice URL
|
|
69
|
-
|
|
70
|
-
```bash
|
|
71
|
-
crtr cloud apps set-run-notice-url --app-id <client id> --url https://app.example.com/run-notices
|
|
72
|
-
crtr cloud apps set-run-notice-url --app-id <client id> --clear
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
The URL must be https (plain http only on localhost in development) and at most 512 characters. What the app does with notices is in [Background runs](/docs/guides/background-runs#run-notices).
|
|
64
|
+
The card is not shown for a provider or a first-party app, and the directory refuses to delete a provider (`app_is_provider`). If you only want a clean slate for tests, changing the app's redirect URIs and keys is often simpler.
|
package/docs/cli/README.md
CHANGED
|
@@ -14,4 +14,5 @@ Use these pages to start and return to an agent conversation, see the work runni
|
|
|
14
14
|
| [Canvas and dashboard](./canvas-and-dashboard.md) | See the node graph, find work that needs attention, and reopen a node. |
|
|
15
15
|
| [Human inbox](./human-inbox.md) | Answer decisions, review documents, and follow a live display. |
|
|
16
16
|
| [Documents and preferences](./documents-and-preferences.md) | Save knowledge and instructions that should shape later agents. |
|
|
17
|
+
| [Join a machine to your Northlight account](./join-northlight.md) | Run `crtr cloud apps` commands as you, from a joined laptop. |
|
|
17
18
|
| [The official plugin marketplace](./official-marketplace.md) | Find a plugin, see what it needs, and install it. |
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Join a machine to your Northlight account
|
|
3
|
+
description: Join a laptop to your Northlight account with crtr sys join so crtr cloud apps commands run as you, and the crtr cloud apps command list.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Join a machine to your Northlight account
|
|
7
|
+
|
|
8
|
+
Joining makes this machine one of your Northlight account's machines, so `crtr cloud …` commands run as you. Building an app does not need it: the [SDK docs site](https://sdk.docs.northlight.ai/docs/quickstart) takes you through the web console. Join when you want to run `crtr cloud apps` yourself.
|
|
9
|
+
|
|
10
|
+
Create your account at `https://account.northlight.ai/signup` first. Then install the CLI (`npm i -g @north-light/cli`). Joining needs `crtr` 0.3.436 or later (older versions need `crtr sys daemon stop` first and fail if a viewer is open); `crtr --version` shows yours.
|
|
11
|
+
|
|
12
|
+
Then join your laptop to the account:
|
|
13
|
+
|
|
14
|
+
1. Open `https://account.northlight.ai/account/machines` and choose **Create join token**. A token needs a sign-in within the last 10 minutes, so the page asks you to sign in again if yours is older.
|
|
15
|
+
2. The page shows the join command with the token filled in. On your laptop, run it from a plain terminal: `crtr sys join --directory https://account.northlight.ai --token <token>`. Leave your crtr windows open: join stops the daemon itself, holds it down while it writes your identity (so an open viewer cannot restart it early), and starts it again on the joined settings. Run it outside any crtr node, because a node's engine stops with the daemon; from inside one, join refuses with `join_inside_node`. Add `--name <label>` to name the machine on the page. The token works once and expires at the time the page shows; the page shows it only once.
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
crtr sys join --directory https://account.northlight.ai --token <token> --name my-laptop
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
It answers with `registered: true`, the machine id (`runtime:<id>`), your user id and `daemon: restarted` (`daemon: started` when crtr was not running: join starts the daemon, and its first grant sync installs `crtr cloud …` and your other provider plugins). Your open viewers and nodes reconnect on their own.
|
|
22
|
+
3. Check it: `crtr cloud apps mine` lists the apps you manage. Before you have joined, it answers `not_joined` and prints these same steps.
|
|
23
|
+
|
|
24
|
+
Things to know before you join:
|
|
25
|
+
|
|
26
|
+
- **Joining rewrites your crtr home.** Everything in your everyday crtr home that belonged to the local user (`user:local`) becomes your Northlight user, the next time the daemon starts. If you would rather not do that to your everyday home, join from a home you can throw away.
|
|
27
|
+
- **There is no unjoin.** The only undo is removing the machine on `/account/machines`.
|
|
28
|
+
- **Removing the machine stops it.** Once you choose **Remove** next to a machine, it gets no new tokens at once and the ones it holds expire within 5 minutes. After that its `cloud` commands answer `machine_revoked`: "This machine was removed from your Northlight account." It stays that way until you create a new join token and run `crtr sys join` again. Files on the computer stay on it.
|
|
29
|
+
- **Your hosted machine is unchanged.** `/account/machines` lists it as "Northlight (hosted)", the default. Your chat agent at `https://chat.northlight.ai` still runs `crtr cloud apps …` as before.
|
|
30
|
+
|
|
31
|
+
## `crtr cloud apps`
|
|
32
|
+
|
|
33
|
+
Every app-management leaf of Northlight's `cloud` provider runs as you and needs a joined machine. `--app-id` takes `app:<client id>` or the bare client id.
|
|
34
|
+
|
|
35
|
+
Your Northlight chat agent at `https://chat.northlight.ai` can run these commands as you, on your hosted runtime. It can list and change an app (`mine` and `set-scopes` were checked on production). It cannot delete one. Generate keys on your laptop and give an agent only a public JWK, never the private key.
|
|
36
|
+
|
|
37
|
+
| Command | What it does |
|
|
38
|
+
|---|---|
|
|
39
|
+
| `crtr cloud apps mine` | Lists the apps you manage. |
|
|
40
|
+
| `crtr cloud apps register --name … --origin … --logo … --justification … --redirect-uri …` | Registers an app and answers its registration, including `env` (the issuer, client id and origin). The `--redirect-uri` flag repeats. |
|
|
41
|
+
| `crtr cloud apps get --app-id <id>` | Shows the registration. |
|
|
42
|
+
| `crtr cloud apps update --app-id <id> --name … / --origin … / --logo … / --justification …` | Changes the profile fields you pass. |
|
|
43
|
+
| `crtr cloud apps set-redirect-uris --app-id <id> --redirect-uri …` | Replaces the whole redirect URI list; repeat the flag for each. |
|
|
44
|
+
| `crtr cloud apps asks --app-id <id>` | Lists what an app may ask for, with whether it asks now. Large sources such as `integrations` print as a count. Narrow it with `--query sms:send`, `--source sms`, `--group providers\|apps\|agents\|space`, or `--query <text>`; `--requested` lists only what the app asks for now. For example `crtr cloud apps asks --app-id <id> --source sms`. |
|
|
45
|
+
| `crtr cloud apps set-scopes --app-id <id> --add … --remove …` | Edits the scope list the app has now; repeat `--add`/`--remove` per scope. Removing a scope that is not in the list is refused. Or replace the whole list with repeated `--scope`; `--clear` empties it. `crtr:act` needs `crtr:llm`. |
|
|
46
|
+
| `crtr cloud apps set-requires --app-id <id> --add … --remove …` | Edits the providers your app does not work without (for example `context`). Or replace the list with repeated `--provider`; `--clear` empties it. |
|
|
47
|
+
| `crtr cloud apps keys add --app-id <id> --jwk '<public JWK>'` | Adds a signing key beside any existing one. Answers `created: false` if that key is already on the app. `keygen --app` calls it for you. |
|
|
48
|
+
| `crtr cloud apps keys list --app-id <id>` | Lists the keys with their `kid` and `active` or `retired` status. |
|
|
49
|
+
| `crtr cloud apps keys retire --app-id <id> --kid <kid>` | Retires a key so it no longer signs in. |
|
|
50
|
+
| `crtr cloud apps set-listing --app-id <id> --login-start-url … --text-welcome …` (also `--listed`, `--price`) | Sets the listing fields; pass at least one. A loopback `--login-start-url` is accepted. `--listed true --price free` lists the app, so `cloud apps install` finds it; while the app is in testing it still stays out of the marketplace list. |
|
|
51
|
+
| `crtr cloud apps install --app-id <id>` | Texts an install link to the signed-in person. If they already hold a grant it answers `status: already_have` and the install URL instead of texting. The app must be listed. |
|
|
52
|
+
| `crtr cloud apps testers add --app-id <id> --email …` (or `--phone …`) | Lets that person sign in while the app is in testing; at most 100. Not yet run on production. |
|
|
53
|
+
| `crtr cloud apps testers list --app-id <id>` | Lists the testers. |
|
|
54
|
+
| `crtr cloud apps testers remove --app-id <id> --email …` (or `--phone …`) | Removes a tester and revokes their grants for the app. Not yet run on production. |
|
|
55
|
+
| `crtr cloud apps set-run-notice-url --app-id <id> --url …` (or `--clear`) | Sets or clears the URL the runtime posts run notices to. https only, plain http on localhost for development, at most 512 characters. |
|
|
56
|
+
| `crtr cloud apps delete --app-id <id> --confirm <client id>` | Deletes the app and removes every person's grant for it. Only you at a joined machine's terminal can run it: an agent, including your chat agent, is refused with `provider_refused`. |
|
|
57
|
+
|
|
58
|
+
Deleting an app removes every person's grant for it and cannot be undone. Each runtime deletes the app's runs, space and schedules, and the app's next refresh fails with `invalid_grant`. Apps that hold the provider role cannot be deleted.
|
package/docs/sdk/README.md
CHANGED
|
@@ -24,48 +24,9 @@ const run = await client.nodes.parse({
|
|
|
24
24
|
if (run.kind === 'result') console.log(run.output_parsed.root_cause);
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
##
|
|
27
|
+
## Building an app for other people
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
```bash
|
|
32
|
-
npx @north-light/sdk keygen --app <client id>
|
|
33
|
-
npx @north-light/sdk env --app <client id>
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
`keygen` saves the private JWK to `local/app-key.json` (mode `0600`); keep that file out of git. With `--app`, it offers to add the public key to your registered app through your own `crtr`, as an additional key that never replaces the current one. Otherwise, add the public JWK printed on stdout with `crtr cloud apps keys add --app-id <client id> --jwk '<public JWK>'`. An app authenticates only with its key; the directory issues no client secret.
|
|
37
|
-
|
|
38
|
-
`env` reads the registration through `crtr cloud apps get` and writes `CROUTER_ISSUER`, `CROUTER_CLIENT_ID` and `APP_ORIGIN` into `local/app.env` (`--out` picks another file), keeping every other line. Both commands need this machine's `crtr` joined to your Northlight account; on a machine that isn't, they print a join step and exit 2. An app is configured from four variables:
|
|
39
|
-
|
|
40
|
-
| Variable | Value |
|
|
41
|
-
|---|---|
|
|
42
|
-
| `CROUTER_ISSUER` | The directory's issuer URL |
|
|
43
|
-
| `CROUTER_CLIENT_ID` | The app's client id |
|
|
44
|
-
| `APP_ORIGIN` | The app's origin; its redirect URI is `${APP_ORIGIN}/auth/callback` by convention |
|
|
45
|
-
| `CROUTER_KEY_FILE` | Path to the private JWK, default `local/app-key.json`. `env` never writes it |
|
|
46
|
-
|
|
47
|
-
The SDK reads no environment itself: load the values in your own config and pass them to `OAuth2Client`.
|
|
48
|
-
|
|
49
|
-
```ts
|
|
50
|
-
import {readFile} from 'node:fs/promises';
|
|
51
|
-
import {OAuth2Client} from '@north-light/sdk';
|
|
52
|
-
|
|
53
|
-
const privateKey = JSON.parse(await readFile(process.env.CROUTER_KEY_FILE ?? 'local/app-key.json', 'utf8'));
|
|
54
|
-
const oauth = new OAuth2Client({
|
|
55
|
-
clientId: process.env.CROUTER_CLIENT_ID!,
|
|
56
|
-
issuer: process.env.CROUTER_ISSUER!,
|
|
57
|
-
redirectUri: `${process.env.APP_ORIGIN}/auth/callback`,
|
|
58
|
-
privateKey,
|
|
59
|
-
store, // your ConnectionStore
|
|
60
|
-
});
|
|
61
|
-
const {url, state, codeVerifier, nonce} = await oauth.authorizeUrl({scopes: ['openid', 'crtr:llm', 'crtr:act'], state: sessionState});
|
|
62
|
-
// Save state, codeVerifier, nonce in the user's session, then redirect to url.
|
|
63
|
-
const connection = await oauth.exchangeCode({code: callbackCode, state: callbackState, expectedState: state, codeVerifier, nonce});
|
|
64
|
-
await store.save(connection);
|
|
65
|
-
const client = await oauth.client(connection.userId); // Refreshes through the store from here on.
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
The directory rejects the key until its public JWK is added to the app. To change keys later, follow [Rotating your key](./getting-started.md#rotating-your-key): add the new key, switch the app to it, verify a real sign-in, then retire the old key. An app can hold two active keys at once, so there is no downtime.
|
|
29
|
+
An app that signs people in with Northlight and runs agents on their runtimes is documented on the SDK docs site, which starts at [Quick setup](https://sdk.docs.northlight.ai/docs/quickstart). This section and the pages below cover driving a crouter daemon you run yourself.
|
|
69
30
|
|
|
70
31
|
## Pages
|
|
71
32
|
|
|
@@ -77,14 +38,10 @@ The directory rejects the key until its public JWK is added to the app. To chang
|
|
|
77
38
|
| [Streaming](./streaming.md) | The event table, `stream()` / `events()`, the activity helper, resume with `after`, `stream_gap` and `stream_dropped` |
|
|
78
39
|
| [Files](./files.md) | Absolute-path reads, writes, and one-level lists |
|
|
79
40
|
| [Bash](./bash.md) | One bounded command and its output result |
|
|
80
|
-
| [Memory](./memory.md) | `memory.list` and `memory.get`: an app reads the person's `public` documents with no scope |
|
|
81
41
|
| [Resource map](./resources.md) | Every namespace (including `customObjects`, `runs`, `memory`, `shares`, `providers`) with its routes, what is deliberately excluded, and the `client.request()` escape hatch |
|
|
82
|
-
| [Errors](./errors.md) | The
|
|
42
|
+
| [Errors](./errors.md) | The daemon's scoped-token and wire errors, identifier validation, and node retries |
|
|
83
43
|
| [Docker environment](./docker.md) | `start()`, `attach()`, `connection()` from `@north-light/env-docker` |
|
|
84
|
-
| [Testing your app](./testing.md) | `FakeCloud` from `@north-light/sdk/testing`: a fake directory and runtime your real `OAuth2Client` and `Crouter` talk to through `fetch` |
|
|
85
44
|
| [Release notes](https://sdk.docs.northlight.ai/docs/release-notes) | Behavior changes an existing app must act on, newest first |
|
|
86
|
-
| [Apps on Northlight](./guides/apps-on-northlight.md) | Building and registering an app against production (joining a laptop, `crtr cloud apps` registration, keys, env file, scopes, running locally, the `crtr cloud apps` commands); typed results from app runs with `runs.parse`, declines, bounding a run, `run_idle`, and waiting on a provider your app requires |
|
|
87
|
-
| [Apps with many users](./guides/apps-with-many-users.md) | Per-person runtime connections, app-owned user access, privacy, and spending |
|
|
88
45
|
|
|
89
46
|
## Phases
|
|
90
47
|
|