@north-light/cli 0.3.472 → 0.3.474

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/dist/api/dto/nodes.d.ts +1 -1
  2. package/dist/builtin-memory/crouter-sdk/README.md +4 -47
  3. package/dist/builtin-memory/crouter-sdk/client.md +2 -231
  4. package/dist/builtin-memory/crouter-sdk/errors.md +9 -50
  5. package/dist/builtin-memory/crouter-sdk/getting-started.md +3 -65
  6. package/dist/builtin-memory/crouter-sdk/guides/README.md +1 -3
  7. package/dist/builtin-memory/crouter-sdk/guides/typed-extraction.md +1 -1
  8. package/dist/builtin-memory/crouter-sdk/nodes.md +1 -1
  9. package/dist/builtin-memory/crouter-sdk/release-notes.md +20 -20
  10. package/dist/builtin-memory/crouter-sdk/streaming.md +1 -70
  11. package/dist/builtin-memory/northlight-apps/INDEX.md +15 -0
  12. package/dist/builtin-memory/northlight-apps/cli.md +41 -0
  13. package/dist/builtin-memory/northlight-apps/coding-agents.md +75 -0
  14. package/dist/builtin-memory/northlight-apps/concepts/apps-and-sign-in.md +109 -0
  15. package/dist/builtin-memory/northlight-apps/concepts/connections.md +109 -0
  16. package/dist/builtin-memory/northlight-apps/concepts/providers.md +76 -0
  17. package/dist/builtin-memory/northlight-apps/concepts/runs.md +101 -0
  18. package/dist/builtin-memory/northlight-apps/concepts/scopes-and-grants.md +64 -0
  19. package/dist/builtin-memory/northlight-apps/concepts/testing-mode.md +54 -0
  20. package/dist/builtin-memory/northlight-apps/errors.md +119 -0
  21. package/dist/builtin-memory/northlight-apps/guides/background-runs.md +80 -0
  22. package/dist/builtin-memory/northlight-apps/guides/files-and-documents.md +61 -0
  23. package/dist/builtin-memory/northlight-apps/guides/go-live.md +39 -0
  24. package/dist/builtin-memory/northlight-apps/guides/grant-changes.md +100 -0
  25. package/dist/builtin-memory/northlight-apps/guides/manage-your-app.md +77 -0
  26. package/dist/builtin-memory/northlight-apps/guides/many-users.md +63 -0
  27. package/dist/builtin-memory/northlight-apps/guides/stream-a-run.md +84 -0
  28. package/dist/builtin-memory/{crouter-sdk/testing.md → northlight-apps/guides/test-your-app.md} +6 -6
  29. package/dist/builtin-memory/northlight-apps/guides/typed-results.md +99 -0
  30. package/dist/builtin-memory/northlight-apps/how-it-fits.md +49 -0
  31. package/dist/builtin-memory/northlight-apps/providers/README.md +39 -0
  32. package/dist/builtin-memory/northlight-apps/providers/context.md +34 -0
  33. package/dist/builtin-memory/northlight-apps/providers/device.md +28 -0
  34. package/dist/builtin-memory/northlight-apps/providers/integrations.md +28 -0
  35. package/dist/builtin-memory/northlight-apps/providers/search.md +31 -0
  36. package/dist/builtin-memory/northlight-apps/providers/sms.md +39 -0
  37. package/dist/builtin-memory/northlight-apps/quickstart.md +210 -0
  38. package/dist/commands/node/create.js +2 -2
  39. package/docs/apps/cli.md +40 -0
  40. package/docs/apps/coding-agents.md +72 -0
  41. package/docs/apps/concepts/apps-and-sign-in.md +82 -0
  42. package/docs/apps/concepts/connections.md +106 -0
  43. package/docs/apps/concepts/providers.md +59 -0
  44. package/docs/apps/concepts/runs.md +94 -0
  45. package/docs/apps/concepts/scopes-and-grants.md +62 -0
  46. package/docs/apps/concepts/testing-mode.md +36 -0
  47. package/docs/apps/errors.md +117 -0
  48. package/docs/apps/guides/background-runs.md +78 -0
  49. package/docs/apps/guides/files-and-documents.md +59 -0
  50. package/docs/apps/guides/go-live.md +37 -0
  51. package/docs/apps/guides/grant-changes.md +98 -0
  52. package/docs/apps/guides/manage-your-app.md +75 -0
  53. package/docs/apps/guides/many-users.md +61 -0
  54. package/docs/apps/guides/stream-a-run.md +82 -0
  55. package/docs/{sdk/testing.md → apps/guides/test-your-app.md} +5 -5
  56. package/docs/apps/guides/typed-results.md +72 -0
  57. package/docs/apps/how-it-fits.md +46 -0
  58. package/docs/apps/providers/README.md +26 -0
  59. package/docs/apps/providers/context.md +32 -0
  60. package/docs/apps/providers/device.md +26 -0
  61. package/docs/apps/providers/integrations.md +26 -0
  62. package/docs/apps/providers/search.md +29 -0
  63. package/docs/apps/providers/sms.md +37 -0
  64. package/docs/apps/quickstart.md +120 -2
  65. package/docs/cli/README.md +1 -0
  66. package/docs/cli/join-northlight.md +58 -0
  67. package/docs/sdk/README.md +4 -47
  68. package/docs/sdk/client.md +2 -231
  69. package/docs/sdk/errors.md +8 -49
  70. package/docs/sdk/getting-started.md +3 -65
  71. package/docs/sdk/guides/README.md +1 -3
  72. package/docs/sdk/guides/typed-extraction.md +1 -1
  73. package/docs/sdk/nodes.md +1 -1
  74. package/docs/sdk/release-notes.md +20 -20
  75. package/docs/sdk/streaming.md +1 -70
  76. package/package.json +2 -2
  77. package/packages/crouter-identity/package.json +1 -1
  78. package/runtime.lock.json +12 -12
  79. package/dist/builtin-memory/crouter-sdk/error-envelope.md +0 -38
  80. package/dist/builtin-memory/crouter-sdk/guides/apps-on-northlight.md +0 -423
  81. package/dist/builtin-memory/crouter-sdk/guides/apps-with-many-users.md +0 -61
  82. package/dist/builtin-memory/crouter-sdk/memory.md +0 -49
  83. package/docs/sdk/error-envelope.md +0 -36
  84. package/docs/sdk/guides/apps-on-northlight.md +0 -419
  85. package/docs/sdk/guides/apps-with-many-users.md +0 -59
  86. package/docs/sdk/memory.md +0 -47
@@ -0,0 +1,46 @@
1
+ ---
2
+ title: Where your app runs
3
+ description: Your app runs on your server, people sign in at account.northlight.ai, and the agent work runs on each person's own Northlight runtime. Northlight does not host your app's code.
4
+ ---
5
+
6
+ # Where your app runs
7
+
8
+ Your app runs on a server you choose. Northlight hosts two other things: the directory at `account.northlight.ai`, and each person's agent runtime. Northlight does not host your app's code, and there is no flow for deploying it to Northlight.
9
+
10
+ | Piece | Runs where | What it does |
11
+ |---|---|---|
12
+ | **Your server** | Anywhere you choose. Your laptop while you develop. | Your pages and logic. It holds the app's private signing key and a stored connection for each person. |
13
+ | **The directory** | `https://account.northlight.ai`, hosted by Northlight | Accounts, app registration (the console at `/console`), consent, tokens and the marketplace. |
14
+ | **The person's runtime** | Hosted by Northlight, one per person | Their agents, runs, files, documents and providers (sms, search and others), using their own AI subscription. |
15
+
16
+ "Build on Northlight" therefore means three things. Your app's agent work runs on the person's Northlight-hosted runtime. People sign in to your app with their Northlight account. And your app can be listed in the Northlight marketplace.
17
+
18
+ ```mermaid
19
+ sequenceDiagram
20
+ participant P as Person's browser
21
+ participant A as Your server
22
+ participant D as account.northlight.ai
23
+ participant R as Person's runtime
24
+ P->>A: GET /auth/login
25
+ A->>P: redirect to the directory (client ID, scopes, PKCE challenge)
26
+ P->>D: sign in, consent
27
+ D->>P: redirect to your /auth/callback with a code
28
+ P->>A: GET /auth/callback?code=…
29
+ A->>D: exchange the code, signed with your private key
30
+ D->>A: access token, refresh token, runtime URL
31
+ Note over A: store the connection for this person
32
+ A->>R: oauth.client(userId).runs.start(…)
33
+ R->>A: the run, its status and its result
34
+ ```
35
+
36
+ 1. **Sign-in.** Your `/auth/login` route sends the person to the directory with your client ID and the scopes you ask for. The directory shows its consent page and returns them to your registered redirect URI with a one-use code.
37
+ 2. **Token.** Your server exchanges the code for tokens. It proves it is your app by signing the request with its private key. No client secret exists. The result is a connection: a short-lived access token, a rotating refresh token, and the URL of that person's runtime. See [Apps and sign-in](/docs/concepts/apps-and-sign-in).
38
+ 3. **Calls.** `oauth.client(userId)` loads the stored connection, refreshes the access token when it needs to, and returns a client on that person's runtime. You call `runs.*` and `providers.*` on it. See [Connections](/docs/concepts/connections) and [Runs](/docs/concepts/runs).
39
+
40
+ The person's AI subscription pays for model calls, and what your app may do on their runtime is limited by the scopes they approved. See [Scopes and grants](/docs/concepts/scopes-and-grants).
41
+
42
+ ## What you operate
43
+
44
+ You operate your server, its sessions, its database and its `ConnectionStore`. You register the app in the console and keep its key pair. Northlight operates the directory and the runtimes; you do not run either, and there is no local Northlight to start. If you run crouter yourself and want a runtime of your own, that is a different path: see [crouter.docs.northlight.ai](https://crouter.docs.northlight.ai).
45
+
46
+ The directory's discovery document at `https://account.northlight.ai/.well-known/openid-configuration` lists the endpoints, the `S256` PKCE method and `private_key_jwt` as the only token authentication method (checked on production).
@@ -0,0 +1,26 @@
1
+ ---
2
+ title: Providers
3
+ description: The first-party providers an app can call on a person's runtime, their ids, how scope strings are named, and how to list a provider's tools.
4
+ ---
5
+
6
+ # Providers
7
+
8
+ A provider is a service the person's runtime calls for your app. Your app asks the runtime with `runtime.providers.call(...)`, and the runtime checks that the person approved the provider's scope for your app. See [Providers](/docs/concepts/providers) for the model.
9
+
10
+ | Provider id | What it does | Scopes | Page |
11
+ |---|---|---|---|
12
+ | `sms` | Texts the signed-in person's own verified phone | `sms:send`, `sms:status` | [Texting (sms)](/docs/providers/sms) |
13
+ | `search` | Web search, sourced answers, page contents and paid data endpoints | `search:<group>` | [Search](/docs/providers/search) |
14
+ | `integrations` | The person's own accounts at other services | `integrations:discover`, `integrations:<service>_read`, `integrations:<service>_write` | [Integrations](/docs/providers/integrations) |
15
+ | `context` | Ask about the person from their memory, and look up public profiles | `context:<group>` | [Context](/docs/providers/context) |
16
+ | `device` | The person's Mac: browser, files, Messages and shell | `device:<group>` | [Device](/docs/providers/device) |
17
+
18
+ ## Where names come from
19
+
20
+ - **Scope strings** come from the console's **What it asks for → Add permissions** list. A scope is `<provider id>:<group>`. Do not guess them.
21
+ - **Tool names and arguments** come from `runtime.providers.tools('<provider id>')`. It answers the provider's groups, and each group's tools with a description, parameters and output. It lists only the groups your app's grant holds, so ask for the scope first, then list.
22
+ - **Required providers.** If your app does not work without a provider being set up first, add it under **What your app requires** in the console. See [Grant changes](/docs/guides/grant-changes#wait-on-a-provider-your-app-requires).
23
+
24
+ <include lang="ts">../../../examples/apps/provider-tools.ts</include>
25
+
26
+ Each provider page below shows the tools of a provider as captured on **2026-10-16**. The capture is not an app's view: it came from the runtime of a person's own Northlight account (`GET /v1/providers/<id>/tools` on `crtr` 0.3.462, joined to production), which shows the groups that person's own grant holds. What `providers.tools` answers for your app depends on the scopes your app was granted. Tools can change between releases, so treat a table as a starting point and list the tools at run time.
@@ -0,0 +1,32 @@
1
+ ---
2
+ title: Context
3
+ description: Ask about the person from their memory, and look up public profiles, through the context provider.
4
+ ---
5
+
6
+ # Context
7
+
8
+ The `context` provider answers questions about the person from what their agent knows, and looks up public profiles.
9
+
10
+ ## Scopes and setup
11
+
12
+ Scopes are `context:<group>`. The console's **Add permissions** list shows `context:ask_informed` and `context:ask_public` on 2026-10-16; the tool list also shows a `research` group (`context:research`) that was not in that console list when we checked (not yet checked on production).
13
+
14
+ Context needs setup before it works: the person gives a profile URL on the provider's setup page, and its state stays `pending` for a few minutes afterwards. If your app depends on it, add `context` under **What your app requires** and follow the state with `grant.get().dependencies['context']`. See [Grant changes](/docs/guides/grant-changes#wait-on-a-provider-your-app-requires).
15
+
16
+ ## Tools
17
+
18
+ Captured 2026-10-16 from `GET /v1/providers/context/tools` on a joined runtime (provider major version 1). `*` marks a required parameter. Tool names with a space are written as the provider lists them; check how to pass a multi-word `tool` to `providers.call` against your own `providers.tools` output before relying on it (not yet checked on production).
19
+
20
+ | Tool | Group | Parameters | What it does |
21
+ |---|---|---|---|
22
+ | `ask informed` | `ask_informed` | `request`\*, `question` (repeatable, up to 10) | Advice about the person shaped by everything their agent knows, private memories included, without being told private details |
23
+ | `ask public` | `ask_public` | `request`\*, `question` (repeatable, up to 10) | Answers about the person from public memories only |
24
+ | `linkedin profile` | `research` | `profile`\* | A person's LinkedIn profile |
25
+ | `linkedin posts` | `research` | `profile`\*, `limit` | Recent LinkedIn posts |
26
+ | `x profile` | `research` | `profile`\* | A person's X profile |
27
+ | `x posts` | `research` | `profile`\*, `limit` | Not available |
28
+ | `github profile` | `research` | `profile`\* | A person's GitHub profile |
29
+ | `github repos` | `research` | `profile`\*, `limit` | Public repositories |
30
+ | `search people` | `research` | `query`\* | Search LinkedIn people |
31
+
32
+ `request` is the task you are doing, 1 to 4000 characters. Each `question` is 1 to 500 characters. Run `runtime.providers.tools('context')` for the current list and each tool's output shape.
@@ -0,0 +1,26 @@
1
+ ---
2
+ title: Device
3
+ description: "Reach the person's Mac from your app: browser, files, Messages and shell, each behind its own scope."
4
+ ---
5
+
6
+ # Device
7
+
8
+ The `device` provider reaches the person's own Mac. Its tools run on the person's machine (the provider's transport is `machine`), and each group is its own scope, so ask only for the groups your app needs. What happens when the Mac is asleep or offline is not yet checked on production.
9
+
10
+ ## Scopes
11
+
12
+ `device:browser`, `device:files`, `device:messages_read`, `device:messages_send` and `device:shell`. Add them under **What it asks for → Add permissions** (provider `device`).
13
+
14
+ ## Tools
15
+
16
+ Captured 2026-10-16 from `GET /v1/providers/device/tools` on a joined runtime (provider major version 2, transport `machine`). `*` marks a required parameter.
17
+
18
+ | Group (scope) | Tools |
19
+ |---|---|
20
+ | `browser` | `browser profiles`, `browser executor create` (`url`, `mode`, `profile`, `target-pattern`), `browser executor list`, `browser executor navigate` (`executor`\*, `url`\*), `browser executor exec` (`executor`\*, `code`\*, `timeout-ms`, `short-description`), `browser executor result` (`run`\*), `browser executor close` (`executor`\*), `browser libs` (`query`, `limit`, `service`, `fn`), `browser cdp` (`method`\*, `params`, `tab`) |
21
+ | `files` | `files list` (`path`\*, `limit`), `files stat` (`path`\*), `files read` (`path`\*, `encoding`, `offset`, `length`), `files write` (`path`\*, `text`, `base64`, `from`, `append`, `parents`), `files delete` (`path`\*, `recursive`), `files move` (`from`\*, `to`\*, `overwrite`), `files mkdir` (`path`\*) |
22
+ | `messages_read` | `messages chats` (`limit`), `messages history` (`chat`, `participant`, `since`, `until`, `limit`, `attachments`), `messages search` (`text`\*, `limit`), `messages contacts` (`query`\*, `limit`), `messages permissions` (`open`) |
23
+ | `messages_send` | `messages send` (`recipient`\*, `text`\*, `file`, `service`) |
24
+ | `shell` | `shell run` (`command`\*, `cwd`, `timeout`, `stdin`) |
25
+
26
+ Tool names with spaces are written as the provider lists them; check how to pass them to `providers.call` against your own `providers.tools` output (not yet checked on production). The capture is of a person's own grant, not an app's. Run `runtime.providers.tools('device')` for the list your app can use.
@@ -0,0 +1,26 @@
1
+ ---
2
+ title: Integrations
3
+ description: Call the person's own accounts at other services through the integrations provider, and find the tool you need.
4
+ ---
5
+
6
+ # Integrations
7
+
8
+ The `integrations` provider lets your app act in the person's own accounts at other services, with the person's consent for each service.
9
+
10
+ ## Scopes
11
+
12
+ - `integrations:discover` allows `search` and `schema`, which find services and their tools, and the `link` group's `accounts`, which lists the person's linked accounts.
13
+ - `integrations:<service>_read` and `integrations:<service>_write` gate each service's tools. For example, a service named `gmail` has `integrations:gmail_read` and `integrations:gmail_write`.
14
+
15
+ Scope strings come from **What it asks for → Add permissions** (provider `integrations`). The catalog is large, so search it by service name.
16
+
17
+ ## Find the tool you need
18
+
19
+ The provider has thousands of tools: on 2026-10-16 the runtime we captured from listed 3,061 tools across 3,060 groups, one `_read` group and one `_write` group per service, plus `discover` and `link`. This page does not table them. List them from your app:
20
+
21
+ ```ts
22
+ const integrations = await runtime.providers.tools('integrations');
23
+ for (const group of integrations.groups) console.log(group.group, group.leaves.map((leaf) => leaf.tool));
24
+ ```
25
+
26
+ `providers.tools('integrations')` is large (several megabytes on that runtime), and it lists only groups your app's grant holds, so it stays small until you add service scopes. To find a service or a tool, call the provider's `search` and `schema` tools (group `discover`) instead of reading the whole list.
@@ -0,0 +1,29 @@
1
+ ---
2
+ title: Search
3
+ description: Web search, sourced answers, page contents, and paid data endpoints through the search provider.
4
+ ---
5
+
6
+ # Search
7
+
8
+ The `search` provider runs web search and returns ranked pages, sourced answers, page contents and deep research, and it finds and calls paid data endpoints.
9
+
10
+ ## Scopes
11
+
12
+ Each scope is `search:<group>`: `search:search`, `search:answer`, `search:contents`, `search:discover`, `search:research` and `search:run`. Add them under **What it asks for → Add permissions** (provider `search`). A tool is available when your app holds its group's scope.
13
+
14
+ ## Tools
15
+
16
+ Captured 2026-10-16 from `GET /v1/providers/search/tools` on a joined runtime (provider major version 1). `*` marks a required parameter.
17
+
18
+ | Tool | Group (scope) | Parameters | What it does |
19
+ |---|---|---|---|
20
+ | `search` | `search` | `query`\*, `type`, `category`, `num-results`, `start-published-date`, `end-published-date`, `include-domains`, `exclude-domains`, `no-text` | Ranked web pages |
21
+ | `answer` | `answer` | `query`\*, `structured` | A sourced answer |
22
+ | `contents` | `contents` | `urls`\*, `no-text`, `highlights`, `summary`, `livecrawl` | Contents of known pages |
23
+ | `research` | `research` | `query`\*, `depth` | Deep web research |
24
+ | `discover` | `discover` | `query`\*, `limit`, `min-score` | Matching data endpoints and prices |
25
+ | `inspect` | `discover` | `provider`\*, `endpoint`\* | Data endpoint details and price |
26
+ | `run` | `run` | `provider`\*, `endpoint`\*, `input`\* | A paid data result, or a pending run ID |
27
+ | `status` | `run` | `run-id`\* | State and result of a data run |
28
+
29
+ The capture is of a person's own grant, not an app's. Run `runtime.providers.tools('search')` for what your app's grant holds. A call is billed to the person under your app's spend cap ([Grant changes](/docs/guides/grant-changes#show-what-a-persons-month-has-cost)).
@@ -0,0 +1,37 @@
1
+ ---
2
+ title: Texting (sms)
3
+ description: Text the signed-in person's own verified phone from your app with the sms provider.
4
+ ---
5
+
6
+ # Texting (sms)
7
+
8
+ The `sms` provider sends a text from Northlight's number to the verified phone of the person who signed in to your app. It has no recipient argument, so an app cannot text anyone else.
9
+
10
+ ## Scopes
11
+
12
+ | Scope | Consent sentence | Gates |
13
+ |---|---|---|
14
+ | `sms:send` | Send you text messages from Northlight's number | `send`, `react` |
15
+ | `sms:status` | Not captured | `status` |
16
+
17
+ Add them under **What it asks for → Add permissions** (provider `sms`). Without `sms:send` the consent page does not offer it and `providers.call` for `sms` is refused with `scope_missing`.
18
+
19
+ ## Send a text
20
+
21
+ ```ts
22
+ await runtime.providers.call({provider: 'sms', tool: 'send', arguments: {body: 'Your summary is ready.'}});
23
+ ```
24
+
25
+ `body` is required. The text goes from Northlight's number to the person's verified phone, and each person can receive at most 50 texts from your app per UTC day. These limits are the provider's documented behavior (the 50-per-day limit is not yet checked on production).
26
+
27
+ ## Tools
28
+
29
+ Captured 2026-10-16 from `GET /v1/providers/sms/tools` on a joined runtime (provider major version 1). `*` marks a required parameter.
30
+
31
+ | Tool | Group (scope) | Parameters | What it does |
32
+ |---|---|---|---|
33
+ | `send` | `send` (`sms:send`) | `body`\*, `file`, `reply-to` | Send a text to your verified phone. `file` is a signed URL of at most 5 MiB sent after the text. `reply-to` is the label of a message in the conversation, such as `m3`, to send a threaded reply. Answers `message_id`. |
34
+ | `react` | `send` (`sms:send`) | `label`\*, `emoji`\* | React to a text in the conversation with a tapback or any emoji. |
35
+ | `status` | `status` (`sms:status`) | `message-id`\* | Check the delivery state of a text. |
36
+
37
+ Parameter names are the provider's own. In a `providers.call`, `arguments` is an object keyed by those names, such as `{body, file}`. Run `runtime.providers.tools('sms')` for the current list.
@@ -1,8 +1,126 @@
1
1
  ---
2
2
  title: Quick setup
3
- description: Register an app, sign a person in with Northlight, and start a run on their runtime.
3
+ description: From no account to a running app that signs a person in with Northlight, starts a run on their runtime and sends them a text.
4
4
  ---
5
5
 
6
6
  # Quick setup
7
7
 
8
- This page is a placeholder. The quick setup path is written in a later change.
8
+ ```bash
9
+ npx @north-light/sdk@latest init my-app
10
+ ```
11
+
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
+
14
+ You need Node.js 22.19 or later and a phone that can receive iMessage.
15
+
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
+
18
+ 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`.
19
+
20
+ ## 1. Create an account
21
+
22
+ 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.
23
+
24
+ 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.
25
+
26
+ ## 2. Scaffold the app
27
+
28
+ ```bash
29
+ npx @north-light/sdk@latest init my-app
30
+ cd my-app
31
+ ```
32
+
33
+ `init` writes these files into `my-app/`:
34
+
35
+ | File | What it is |
36
+ |---|---|
37
+ | `package.json` | Pins `@north-light/sdk` to the exact version of the CLI you ran, and adds `tsx`. |
38
+ | `server.ts` | The server shown at the end of this page. |
39
+ | `local/app-key.json` | Your app's private signing key, mode `0600`. Never commit it or paste it anywhere. |
40
+ | `local/app.env` | The settings the server reads: `CROUTER_ISSUER`, `APP_ORIGIN`, an empty `CROUTER_CLIENT_ID=` and `CROUTER_KEY_FILE`. |
41
+ | `.gitignore` | Keeps `local/` and `node_modules/` out of git. |
42
+ | `.agents/skills/northlight/SKILL.md` and `.claude/skills/northlight/SKILL.md` | The Northlight skill for your coding agent. |
43
+
44
+ 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:
45
+
46
+ ```json
47
+ {"kty":"EC","x":"…","y":"…","crv":"P-256","kid":"…","alg":"ES256","use":"sig"}
48
+ ```
49
+
50
+ 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.
51
+
52
+ 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).
53
+
54
+ ## 3. Register the app in the console
55
+
56
+ Open `https://account.northlight.ai/console/new`. The page is titled **Register an app**. Fill in:
57
+
58
+ | Field | Value |
59
+ |---|---|
60
+ | **Name** | Shown to people on the consent page and in their account. |
61
+ | **Origin URL** | `http://localhost:3000` |
62
+ | **Logo URL** | Required. A square image shown beside your app's name. |
63
+ | **Why your app needs access** | One sentence. It is quoted on the consent page as your words. |
64
+ | **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. |
65
+ | **Public key (optional)** | The JSON line `init` printed. |
66
+
67
+ Choose **Register app**.
68
+
69
+ 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`.
70
+
71
+ The field labels are from the console's source. Pasting a key at registration is not yet checked on production.
72
+
73
+ ## 4. Add the permission to text
74
+
75
+ On the app's page, open the **What it asks for** tab and choose **Add permissions**. Add `sms:send` from the list.
76
+
77
+ 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 [Scopes and grants](/docs/concepts/scopes-and-grants).
78
+
79
+ Not yet checked on production.
80
+
81
+ ## 5. Write the client ID into the env file
82
+
83
+ ```bash
84
+ npx @north-light/sdk env --client-id <client id>
85
+ ```
86
+
87
+ You should see:
88
+
89
+ ```text
90
+ Set in local/app.env:
91
+ CROUTER_ISSUER=https://account.northlight.ai
92
+ CROUTER_CLIENT_ID=<client id>
93
+ APP_ORIGIN=http://localhost:3000
94
+ Key file: local/app-key.json (set CROUTER_KEY_FILE to change it).
95
+ ```
96
+
97
+ `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`.
98
+
99
+ Checked from the SDK source built on this branch.
100
+
101
+ ## 6. Run it
102
+
103
+ ```bash
104
+ npm i
105
+ node --env-file=local/app.env --import tsx server.ts
106
+ ```
107
+
108
+ (`npm start` runs the same command.) You should see `Listening on http://localhost:3000`.
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 your phone receives "Hello from my first Northlight app." from Northlight's number.
111
+
112
+ 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.
113
+
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
+
116
+ ## 7. What next
117
+
118
+ - Only you can sign in until you add testers: see [Testing mode](/docs/concepts/testing-mode).
119
+ - Keep each person's sign-in across restarts and processes: see [Connections](/docs/concepts/connections).
120
+ - See what `server.ts` does and where each piece runs: [Where your app runs](/docs/how-it-fits).
121
+
122
+ ## The server `init` writes
123
+
124
+ This is `examples/apps/quickstart-server.ts`. The SDK reads no environment: the server passes the values in.
125
+
126
+ <include lang="ts">../../examples/apps/quickstart-server.ts</include>
@@ -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. App builders need it for the two things the console cannot do yet: deleting an app and setting its run notice URL ([Manage your app](https://sdk.docs.northlight.ai/docs/guides/manage-your-app#two-things-the-console-cant-do-yet)). Everything else about building an app is on the [SDK docs site](https://sdk.docs.northlight.ai/docs/quickstart).
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. The console does the same work for all of them except `delete` and `set-run-notice-url`. `--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.
@@ -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
- ## Go live with an app key
27
+ ## Building an app for other people
28
28
 
29
- Once you have installed the SDK and tried the local flow in [Getting started](./getting-started.md), build against production: [Build and register an app](./guides/apps-on-northlight.md#build-and-register-an-app) takes you from signing up at `https://account.northlight.ai` and joining your laptop (`crtr sys join`) to a running app on your laptop, registered with `crtr cloud apps register`. Generate the key and pull the settings with:
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 error class table and the retry policy |
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
- | [Release notes](./release-notes.md) | 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 |
44
+ | [Release notes](https://sdk.docs.northlight.ai/docs/release-notes) | Behavior changes an existing app must act on, newest first |
88
45
 
89
46
  ## Phases
90
47