@north-light/cli 0.3.515 → 0.3.519

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 (51) hide show
  1. package/dist/api/contract.d.ts +1 -1
  2. package/dist/api/contract.js +1 -1
  3. package/dist/builtin-memory/crouter-plugin/bundles-and-memory.md +7 -1
  4. package/dist/builtin-memory/crouter-plugin/commands.md +18 -1
  5. package/dist/builtin-memory/crouter-sdk/release-notes.md +7 -0
  6. package/dist/clients/attach/render/doc-links.d.ts +5 -0
  7. package/dist/clients/attach/render/doc-links.js +1 -1
  8. package/dist/clients/attach/render/markdown-source.d.ts +5 -0
  9. package/dist/clients/attach/render/markdown-source.js +5 -5
  10. package/dist/clients/attach/session/document-links.js +1 -1
  11. package/dist/clients/attach/viewer.js +544 -535
  12. package/dist/clients/inbox/controller.js +1 -1
  13. package/dist/clients/inbox/tui/page-body.js +1 -1
  14. package/dist/clients/inbox/tui/render.js +2 -2
  15. package/dist/commands/sys/env.js +1 -1
  16. package/dist/core/command-plugins/bundle.js +1 -1
  17. package/dist/core/human/component-docs.js +5 -5
  18. package/dist/core/human/page-schema.d.ts +2 -0
  19. package/dist/core/human/page-schema.js +4 -1
  20. package/dist/core/human/page.js +6 -1
  21. package/dist/core/human/requests.d.ts +4 -1
  22. package/dist/core/human/requests.js +18 -18
  23. package/dist/core/peer/client.d.ts +2 -2
  24. package/dist/core/peer/client.js +1 -1
  25. package/dist/core/profiles/app-env-store.js +1 -1
  26. package/dist/core/profiles/env-store.d.ts +3 -0
  27. package/dist/core/profiles/env-store.js +2 -2
  28. package/dist/core/runs/operations.js +4 -4
  29. package/dist/core/runs/questions.d.ts +6 -1
  30. package/dist/core/runs/questions.js +3 -3
  31. package/dist/core/termrender/version.d.ts +1 -1
  32. package/dist/core/termrender/version.js +1 -1
  33. package/dist/core/usage/ledger.js +4 -4
  34. package/dist/daemon/api/handlers/docs.js +1 -1
  35. package/dist/daemon/api/handlers/questions.d.ts +1 -1
  36. package/dist/daemon/api/handlers/questions.js +1 -1
  37. package/dist/daemon/api/operations.js +1 -1
  38. package/dist/daemon/crtrd.js +8 -8
  39. package/dist/launcher/main.d.ts +1 -1
  40. package/dist/launcher/main.js +1 -3
  41. package/dist/launcher/run.d.ts +5 -0
  42. package/dist/launcher/run.js +3 -0
  43. package/dist/shared/env.d.ts +5 -0
  44. package/dist/shared/env.js +2 -2
  45. package/docs/ci-runner.md +4 -4
  46. package/docs/plugin/bundles-and-memory.md +7 -1
  47. package/docs/plugin/commands.md +18 -1
  48. package/docs/sdk/release-notes.md +7 -0
  49. package/package.json +2 -2
  50. package/packages/crouter-identity/package.json +1 -1
  51. package/runtime.lock.json +12 -12
@@ -1,2 +1,2 @@
1
- var p=Object.defineProperty;var e=(n,r)=>p(n,"name",{value:r,configurable:!0});import{lstatSync as f,readFileSync as c,readdirSync as E}from"node:fs";import{join as u}from"node:path";import{covers as _}from"@north-light/identity";function F(){return process.env.CRTR_NODE_ID}e(F,"envNodeId");function T(n){for(const r of["broker-launch.json","cron-launch.json"]){const t=u(n,r);if(f(t,{throwIfNoEntry:!1})?.isFile())return t}}e(T,"launchSnapshotFile");function d(){if(process.platform==="linux")try{const n="/run/crtr/nodes",r=new Set(c("/proc/self/mountinfo","utf8").split(`
2
- `).map(o=>o.split(" ")[4])),t=E(n,{withFileTypes:!0}).filter(o=>o.isDirectory()&&/^[a-zA-Z0-9][a-zA-Z0-9-]{0,127}$/.test(o.name)&&r.has(u(n,o.name))&&T(u(n,o.name))!==void 0);return t.length===1?t[0].name:void 0}catch{return}}e(d,"sandboxCliNodeId");function W(){return d()!==void 0}e(W,"isSandboxedNode");function h(){return process.env.CRTR_GRANTEE}e(h,"envGrantee");const v="CRTR_INBOX_BETWEEN_TURNS";function H(){return process.env[v]==="1"}e(H,"envInboxBetweenTurns");const C="CRTR_SCOPES_FILE";function G(){const n=process.env[C];if(n!==void 0&&n!==""){const t=JSON.parse(c(n,"utf8"));if(!Array.isArray(t)||t.some(o=>typeof o!="string"))throw new Error(`invalid scopes file ${n}`);return t}const r=process.env.CRTR_SCOPES;return r===void 0?null:r===""?[]:r.split(",")}e(G,"envScopes");function K(n,r){return n==null?!0:a(n,r)}e(K,"scopeAllowed");function a(n,r){return n.includes(r)?(_(r,r),!0):n.some(t=>_(t,r))}e(a,"coveredByAny");function y(){return process.env.CRTR_EXECUTION_ID}e(y,"envExecutionId");function b(){return process.env.CRTR_PROFILE_ID}e(b,"envProfileId");function k(){return process.env.CRTR_NODE_CWD}e(k,"envNodeCwd");function g(){return process.env.CRTR_MODEL_INTENT}e(g,"envModelIntent");function w(){return process.env.CRTR_MODEL_EXACT==="1"}e(w,"envModelExact");function V(){return process.env.CRTR_HOME}e(V,"envHomeOverride");function X(){return process.env.CRTR_RUNTIME_HOME}e(X,"envRuntimeHomeOverride");function s(n,r){if(n===void 0)return r;const t=Number(n);return Number.isFinite(t)&&t>0?t:r}e(s,"parsePositiveMs");const A=5*6e4;function j(){return s(process.env.CRTR_STREAM_WATCHDOG_MS,A)}e(j,"envStreamWatchdogMs");const S=800;function Y(){return s(process.env.CRTR_WATCHER_TICK_MS,S)}e(Y,"envWatcherTickMs");const M=6e4;function z(){return s(process.env.CRTR_WATCHER_BACKSTOP_MS,M)}e(z,"envWatcherBackstopMs");const x=1e3;function Z(){return s(process.env.CRTR_WATCHER_DEBOUNCE_MS,x)}e(Z,"envWatcherDebounceMs");const N=6e4;function $(){return s(process.env.CRTR_RECAP_IDLE_MS,N)}e($,"envRecapIdleMs");function J(){const n=process.env.CRTR_RECAP_MODEL;return n!==void 0&&n.trim()!==""?n.trim():void 0}e(J,"envRecapModelOverride");const D=300*1e3;function q(){return s(process.env.CRTR_BASH_VALVE_MS,D)}e(q,"envBashValveMs");const O=60*1e3;function Q(){return s(process.env.CRTR_BASH_AUTO_BG_DEADLINE_MS,O)}e(Q,"envBashAutoBgDeadlineMs");function ee(){const n=process.env.CRTR_PIDFILE;return n!==void 0&&n!==""?n:void 0}e(ee,"envPidfileOverride");const l="@earendil-works/pi-coding-agent";function ne(){return process.env.CRTR_BROKER_ENGINE??l}e(ne,"envBrokerEngine");function re(){return process.env.CRTR_PI_BINARY}e(re,"envPiBinaryOverride");function te(){return process.env.CRTR_NO_DAEMON_AUTOSTART==="1"}e(te,"envNoDaemonAutostart");function oe(){return process.env.CRTRD_TCP}e(oe,"envCrtrdTcp");function se(){const n=process.env.CRTRD_TOKEN;return n!==void 0&&n!==""?n:void 0}e(se,"envCrtrdToken");function ue(){return process.env.CRTR_NO_EXPORTS==="1"}e(ue,"envNoExports");function ie(){return process.env.CRTR_DEBUG==="1"}e(ie,"envDebug");function R(n){if(n===void 0||n==="")return;const r=Number(n);return Number.isSafeInteger(r)&&r>=1?r:void 0}e(R,"parsePositiveInteger");function ce(){return R(process.env.CRTR_MAX_LIVE_BROKERS)}e(ce,"envMaxLiveBrokers");function _e(){return R(process.env.CRTR_WARN_LIVE_BROKERS)}e(_e,"envWarnLiveBrokers");function i(n){if(n===void 0)return;const r=Number(n);return Number.isFinite(r)&&r>0?r:void 0}e(i,"parsePositiveMsNoDefault");function Re(){return i(process.env.CRTR_TEST_UNATTENDED_PARK_MS)}e(Re,"envTestUnattendedParkMs");function pe(){return i(process.env.CRTR_UNATTENDED_PARK_MS)}e(pe,"envUnattendedParkMs");function I(){return i(process.env.CRTR_TEST_PARK_SUMMARY_GRACE_MS)}e(I,"envTestParkSummaryGraceMs");const m=5*6e4;function fe(){return I()??m}e(fe,"parkSummaryGraceMs");export{v as INBOX_BETWEEN_TURNS_ENV,C as SCOPES_FILE_ENV,a as coveredByAny,Q as envBashAutoBgDeadlineMs,q as envBashValveMs,ne as envBrokerEngine,oe as envCrtrdTcp,se as envCrtrdToken,ie as envDebug,y as envExecutionId,h as envGrantee,V as envHomeOverride,H as envInboxBetweenTurns,ce as envMaxLiveBrokers,w as envModelExact,g as envModelIntent,te as envNoDaemonAutostart,ue as envNoExports,k as envNodeCwd,F as envNodeId,re as envPiBinaryOverride,ee as envPidfileOverride,b as envProfileId,$ as envRecapIdleMs,J as envRecapModelOverride,X as envRuntimeHomeOverride,G as envScopes,j as envStreamWatchdogMs,I as envTestParkSummaryGraceMs,Re as envTestUnattendedParkMs,pe as envUnattendedParkMs,_e as envWarnLiveBrokers,z as envWatcherBackstopMs,Z as envWatcherDebounceMs,Y as envWatcherTickMs,W as isSandboxedNode,T as launchSnapshotFile,fe as parkSummaryGraceMs,d as sandboxCliNodeId,K as scopeAllowed};
1
+ var p=Object.defineProperty;var n=(e,r)=>p(e,"name",{value:r,configurable:!0});import{lstatSync as f,readFileSync as c,readdirSync as d}from"node:fs";import{join as u}from"node:path";import{covers as _}from"@north-light/identity";function F(){return process.env.CRTR_NODE_ID}n(F,"envNodeId");function T(e){for(const r of["broker-launch.json","cron-launch.json"]){const t=u(e,r);if(f(t,{throwIfNoEntry:!1})?.isFile())return t}}n(T,"launchSnapshotFile");function E(){if(process.platform==="linux")try{const e="/run/crtr/nodes",r=new Set(c("/proc/self/mountinfo","utf8").split(`
2
+ `).map(o=>o.split(" ")[4])),t=d(e,{withFileTypes:!0}).filter(o=>o.isDirectory()&&/^[a-zA-Z0-9][a-zA-Z0-9-]{0,127}$/.test(o.name)&&r.has(u(e,o.name))&&T(u(e,o.name))!==void 0);return t.length===1?t[0].name:void 0}catch{return}}n(E,"sandboxCliNodeId");function W(){return E()!==void 0}n(W,"isSandboxedNode");function h(){return process.env.CRTR_GRANTEE}n(h,"envGrantee");const v="CRTR_INBOX_BETWEEN_TURNS";function H(){return process.env[v]==="1"}n(H,"envInboxBetweenTurns");const C="CRTR_SCOPES_FILE";function G(){const e=process.env[C];if(e!==void 0&&e!==""){const t=JSON.parse(c(e,"utf8"));if(!Array.isArray(t)||t.some(o=>typeof o!="string"))throw new Error(`invalid scopes file ${e}`);return t}const r=process.env.CRTR_SCOPES;return r===void 0?null:r===""?[]:r.split(",")}n(G,"envScopes");function K(e,r){return e==null?!0:a(e,r)}n(K,"scopeAllowed");function a(e,r){return e.includes(r)?(_(r,r),!0):e.some(t=>_(t,r))}n(a,"coveredByAny");function b(){return process.env.CRTR_EXECUTION_ID}n(b,"envExecutionId");function y(){return process.env.CRTR_PROFILE_ID}n(y,"envProfileId");function k(){return process.env.CRTR_NODE_CWD}n(k,"envNodeCwd");function g(){return process.env.CRTR_MODEL_INTENT}n(g,"envModelIntent");function w(){return process.env.CRTR_MODEL_EXACT==="1"}n(w,"envModelExact");function V(){return process.env.CRTR_HOME}n(V,"envHomeOverride");function X(){return process.env.CRTR_RUNTIME_HOME}n(X,"envRuntimeHomeOverride");function s(e,r){if(e===void 0)return r;const t=Number(e);return Number.isFinite(t)&&t>0?t:r}n(s,"parsePositiveMs");const A=5*6e4;function j(){return s(process.env.CRTR_STREAM_WATCHDOG_MS,A)}n(j,"envStreamWatchdogMs");const S=800;function Y(){return s(process.env.CRTR_WATCHER_TICK_MS,S)}n(Y,"envWatcherTickMs");const M=6e4;function $(){return s(process.env.CRTR_WATCHER_BACKSTOP_MS,M)}n($,"envWatcherBackstopMs");const N=1e3;function z(){return s(process.env.CRTR_WATCHER_DEBOUNCE_MS,N)}n(z,"envWatcherDebounceMs");const x=6e4;function Z(){return s(process.env.CRTR_RECAP_IDLE_MS,x)}n(Z,"envRecapIdleMs");function J(){const e=process.env.CRTR_RECAP_MODEL;return e!==void 0&&e.trim()!==""?e.trim():void 0}n(J,"envRecapModelOverride");const D=300*1e3;function q(){return s(process.env.CRTR_BASH_VALVE_MS,D)}n(q,"envBashValveMs");const O=60*1e3;function Q(){return s(process.env.CRTR_BASH_AUTO_BG_DEADLINE_MS,O)}n(Q,"envBashAutoBgDeadlineMs");function ee(){const e=process.env.CRTR_PIDFILE;return e!==void 0&&e!==""?e:void 0}n(ee,"envPidfileOverride");const m="@earendil-works/pi-coding-agent";function ne(){return process.env.CRTR_BROKER_ENGINE??m}n(ne,"envBrokerEngine");function re(){return process.env.CRTR_PI_BINARY}n(re,"envPiBinaryOverride");function te(){return process.env.CRTR_NO_DAEMON_AUTOSTART==="1"}n(te,"envNoDaemonAutostart");function oe(){const e=process.env.CRTR_DOCUMENT_PORT;if(e===void 0||!/^\d+$/.test(e))return;const r=Number(e);return r<=65535?r:void 0}n(oe,"envDocumentPort");function se(){return process.env.CRTRD_TCP}n(se,"envCrtrdTcp");function ue(){const e=process.env.CRTRD_TOKEN;return e!==void 0&&e!==""?e:void 0}n(ue,"envCrtrdToken");function ie(){return process.env.CRTR_NO_EXPORTS==="1"}n(ie,"envNoExports");function ce(){return process.env.CRTR_DEBUG==="1"}n(ce,"envDebug");function R(e){if(e===void 0||e==="")return;const r=Number(e);return Number.isSafeInteger(r)&&r>=1?r:void 0}n(R,"parsePositiveInteger");function _e(){return R(process.env.CRTR_MAX_LIVE_BROKERS)}n(_e,"envMaxLiveBrokers");function Re(){return R(process.env.CRTR_WARN_LIVE_BROKERS)}n(Re,"envWarnLiveBrokers");function i(e){if(e===void 0)return;const r=Number(e);return Number.isFinite(r)&&r>0?r:void 0}n(i,"parsePositiveMsNoDefault");function pe(){return i(process.env.CRTR_TEST_UNATTENDED_PARK_MS)}n(pe,"envTestUnattendedParkMs");function fe(){return i(process.env.CRTR_UNATTENDED_PARK_MS)}n(fe,"envUnattendedParkMs");function l(){return i(process.env.CRTR_TEST_PARK_SUMMARY_GRACE_MS)}n(l,"envTestParkSummaryGraceMs");const I=5*6e4;function de(){return l()??I}n(de,"parkSummaryGraceMs");export{v as INBOX_BETWEEN_TURNS_ENV,C as SCOPES_FILE_ENV,a as coveredByAny,Q as envBashAutoBgDeadlineMs,q as envBashValveMs,ne as envBrokerEngine,se as envCrtrdTcp,ue as envCrtrdToken,ce as envDebug,oe as envDocumentPort,b as envExecutionId,h as envGrantee,V as envHomeOverride,H as envInboxBetweenTurns,_e as envMaxLiveBrokers,w as envModelExact,g as envModelIntent,te as envNoDaemonAutostart,ie as envNoExports,k as envNodeCwd,F as envNodeId,re as envPiBinaryOverride,ee as envPidfileOverride,y as envProfileId,Z as envRecapIdleMs,J as envRecapModelOverride,X as envRuntimeHomeOverride,G as envScopes,j as envStreamWatchdogMs,l as envTestParkSummaryGraceMs,pe as envTestUnattendedParkMs,fe as envUnattendedParkMs,Re as envWarnLiveBrokers,$ as envWatcherBackstopMs,z as envWatcherDebounceMs,Y as envWatcherTickMs,W as isSandboxedNode,T as launchSnapshotFile,de as parkSummaryGraceMs,E as sandboxCliNodeId,K as scopeAllowed};
package/docs/ci-runner.md CHANGED
@@ -7,11 +7,11 @@ The suite runs on **GitHub-hosted Linux** (ubuntu, inside a root container shape
7
7
  On Linux crouter assumes it is the hosted guest (`src/core/layout.ts`): sockets in `/run/crtr`, data under `/runtime`, `/user` and `/apps`, brokers started through a root launcher. A plain non-root runner has none of that (every lane failed with EACCES, which is why the suite once moved to macOS). The `linux` jobs of `test.yml` therefore run as root in a container built from `docker/ci.Dockerfile`:
8
8
 
9
9
  - **Image.** `docker/ci.Dockerfile` is the guest's runner stage (same pinned `node:24-alpine` digest, same apk list, users `crtrd` 10002 / `crtr-data` 10001, the `/run/crtr` `/runtime` `/user` `/apps` directories) plus `build-base`, `linux-headers` (native addon) and GNU `tar`. It does not contain crouter; the job checks the source out. Chosen over `hosting/guest/Dockerfile` (needs sandbox-api, chrony, the python stack and a packed crouter, so it rebuilds per commit) and `docker/unit.Dockerfile` (bakes a packed crouter). `scripts/ci-image.test.mjs` (in `test:contract`) fails when the apk list, ids, directories or node base drift from `docker/base.Dockerfile`; keep the two in step by hand when the guest changes.
10
- - **Capabilities.** `--cap-add SYS_ADMIN --cap-add NET_ADMIN --security-opt seccomp=unconfined --security-opt apparmor=unconfined`: mount namespaces per test file and bubblewrap's namespaces.
11
- - **One `/run/crtr` per test file.** All test files would otherwise share `/run/crtr/api.sock`, `store.sock` and `nodes/`. `scripts/ci-test-files.mjs` runs each file under `unshare -m` with a tmpfs on `/run/crtr`, and its own `HOME`, `TMPDIR`, `CRTR_RUNTIME_HOME` and `CRTR_HOME`. A failing file is retried once and reported as flaky if the retry passes (as `scripts/test-retry.mjs` does on macOS).
10
+ - **Docker flags.** `--init --cgroupns=private --cap-add SYS_ADMIN --cap-add NET_ADMIN --security-opt seccomp=unconfined --security-opt apparmor=unconfined`, with the checkout mounted at `/opt/crouter` (where the guest has crouter, and the only crouter path a sandbox mounts). `--init` is tini as pid 1, which reaps the orphans the launcher and bubblewrap leave. The capabilities give mount namespaces per test file and bubblewrap's namespaces. `scripts/ci-linux-lane.sh` first delegates the memory and pids cgroup controllers as the guest entrypoint does and fails the job if it cannot, so the runner must be a cgroup2 host with `xt_owner`.
11
+ - **One `/run/crtr`, `/etc`, cgroup subtree and root launcher per test file.** All test files would otherwise share `/run/crtr/api.sock`, `store.sock` and `nodes/`. `scripts/ci-test-files.mjs` runs each file under `unshare -m` through `scripts/ci-file-setup.sh`, which mounts a tmpfs on `/run/crtr`, binds a private copy of `/etc` (the launcher rewrites `passwd` and `group`) and a private cgroup subtree, and starts the file's own root launcher (`scripts/ci-launcher.mjs`) before the test; each file also has its own `HOME`, `TMPDIR`, `CRTR_RUNTIME_HOME` and `CRTR_HOME`. The test process, running as root, plays the daemon: the CI launcher accepts a root peer and does not start `crtrd`. The log line of each file shows the milliseconds its launcher took to open its socket. A failing file is retried once and reported as flaky if the retry passes (as `scripts/test-retry.mjs` does on macOS).
12
12
  - **Running a list of files.** `ci-test-files.mjs <lane> --files <list>` (and `scripts/ci-linux-lane.sh <lane> <shard> --files <list>`) runs exactly the files in a text file of repo paths, one per line (seam: the `dist/…/*.test.js` path); the format is in the script header. A path that is not a test file of that lane exits 2.
13
- - **Flaky recording.** A file that fails and then passes on the rerun is written to `flaky.json`, which each `linux` and `macos` job uploads as artifact `flaky-<attempt>-<platform>-<lane>-<job index>`. the `record` job of `test.yml` (so also Publish's `tests` job on main) runs after the lanes and only when a lane reported `flaky=true` (the job-level `if` skips it otherwise, so a run with no flake starts no runner), and `scripts/record-flaky-tests.mjs` files one `flaky-test` issue per lane, file and failing test, or comments on the existing one.
14
- - **No real launcher.** The root launcher starts `crtrd` on the real `/runtime` and flips code paths on its socket, so it cannot run in the test container. Tests that need it are tagged `laptopOnly` and run on macOS.
13
+ - **Flaky recording.** A file that fails and then passes on the rerun is written to `flaky.json`, which each `linux` and `macos` job uploads as artifact `flaky-<attempt>-<platform>-<lane>-<job index>`. the `record` job of `test.yml` (so also Publish's `tests` job on main) runs after the lanes and only when a lane reported `flaky=true` (the job-level `if` skips it otherwise, so a run with no flake starts no runner), and `scripts/record-flaky-tests.mjs` records one `ci-flaky` signal in pm-system's intake per lane, file and failing test (secrets `PM_INTAKE_URL` and `PM_INTAKE_KEY`; the script fails the job without them).
14
+ - **Tags not yet moved.** Tests that were tagged `laptopOnly` for needing the root launcher still run on macOS until their tags are removed.
15
15
 
16
16
  ## The `laptopOnly` tag
17
17
 
@@ -19,7 +19,13 @@ await writeFile('acme.tar', bundle.tar);
19
19
 
20
20
  `buildBundle` returns `commandsJson`, `bundleJson`, generated `memory/` members, uncompressed tar bytes, and an ETag. It runs the manifest validator and throws `ManifestInvalidError` when the generated command manifest is invalid. `buildCommandManifest(plugin, { mountPath })` returns the manifest object when archive bytes are not needed.
21
21
 
22
- The tar has exactly `bundle.json`, `commands.json`, and `memory/<name>.md` members for the documents declared in the `memory` array of `definePlugin`. It does not contain `plugin.json`. For an endpoint install, crouter creates `.crouter-plugin/plugin.json` from the endpoint, `--name`, `--auth-env`, and archive hash.
22
+ The tar has exactly `bundle.json`, `commands.json`, and `memory/<name>.md` members for the documents declared in the `memory` array of `definePlugin`. `bundle.json` carries `bundleVersion`, plus `version` when the plugin declares one, `object_kinds` when it declares object kinds, and `groups` when a provider declares its tool groups. `groups` is keyed by group id, each value `{"consent_line": string, "who"?: "own" | "user" | "allowlist" | "any", "allowlist"?: string[]}`, with `who` and `allowlist` written only when declared:
23
+
24
+ ```json
25
+ {"bundleVersion":1,"version":1,"groups":{"read":{"consent_line":"Read your Acme apps and deployments."},"deploy":{"consent_line":"Start deployments on your Acme apps.","who":"allowlist","allowlist":["app:k3x9"]}}}
26
+ ```
27
+
28
+ When the directory serves a provider's bundle it writes the `provider` block from its own rows; `groups` in the archive you build is the declaration the directory reads from your endpoint. A local install ignores `groups`. The archive does not contain `plugin.json`. For an endpoint install, crouter creates `.crouter-plugin/plugin.json` from the endpoint, `--name`, `--auth-env`, and archive hash.
23
29
 
24
30
  ## Documents in the `memory` array
25
31
 
@@ -11,10 +11,27 @@ description: Define plugin command trees, branches, leaves, and agent-facing des
11
11
 
12
12
  Use `defineBranch` to group commands and give it `children`. Use `defineLeaf` for a request that returns one object. A leaf requires `params`, `output`, `effects`, and `handler`; `params` may be omitted when the command has no input. `effects` is a non-empty list shown to the agent. Say whether a command mutates an application, creates billable resources, or is read-only.
13
13
 
14
- A leaf may declare `group`, the capability-provider tool group it belongs to. A group matches `[A-Za-z0-9_-]{1,64}`, and `definePlugin` throws `PluginDefinitionError` naming the leaf for anything else. `buildCommandManifest` writes the group onto the leaf; a local install ignores it. The group's consent line and rate are set in the directory console, not in the plugin. `sideEffect: true` marks a leaf whose tool has a side effect, so a repeated request must not run it twice. It is never written into the manifest.
14
+ A leaf may declare `group`, the capability-provider tool group it belongs to. A group matches `[A-Za-z0-9_-]{1,64}`, and `definePlugin` throws `PluginDefinitionError` naming the leaf for anything else. `buildCommandManifest` writes the group onto the leaf; a local install ignores it. The group's consent line and who may be granted it are declared in `definePlugin({ groups })` (below); its rate is set in the directory console. `sideEffect: true` marks a leaf whose tool has a side effect, so a repeated request must not run it twice. It is never written into the manifest.
15
15
 
16
16
  `definePlugin` accepts `version`, the plugin's integer major (at least 1), written into both `commands.json` and `bundle.json`; a plugin without one is treated as major 1. `provider: true` marks a capability provider registered in the directory. It requires `version`, and it requires a `group` on every leaf: `definePlugin` throws naming the first leaf without one.
17
17
 
18
+ A provider declares its tool groups with `groups`, an object keyed by group id (the same `[A-Za-z0-9_-]{1,64}` as a leaf's `group`):
19
+
20
+ ```ts
21
+ definePlugin({
22
+ name: 'acme',
23
+ provider: true,
24
+ version: 1,
25
+ groups: {
26
+ read: { consentLine: 'Read your Acme apps and deployments.' },
27
+ deploy: { consentLine: 'Start deployments on your Acme apps.', who: 'allowlist', allowlist: ['app:k3x9'] },
28
+ },
29
+ // …
30
+ });
31
+ ```
32
+
33
+ Each group has `consentLine`, the line the directory's consent page shows, and optionally `who` (`own`, `user`, `allowlist` or `any`; the directory treats an omitted `who` as `user`) and `allowlist` (app principals `app:<id>`, required with `who: 'allowlist'` and refused otherwise). `buildBundle` writes the block into `bundle.json` as `groups`, and the directory applies it when it refreshes the provider's manifest. `groups` is optional while a provider migrates to it, but once declared every leaf's `group` must name a declared group and every declared group must be used by at least one leaf; `definePlugin` throws `PluginDefinitionError` otherwise. A plugin that is not `provider: true` must not declare `groups`.
34
+
18
35
  A branch or leaf may set `tier` to `common` or `important` to be promoted into its parent's `-h`. A command with more than 40 children lists only its promoted children in its own `-h`, followed by the count of the rest; the others still run and answer `-h` by name. Use this when a plugin mounts one command per entry of a large catalog. On such a command only promoted leaves reach the top-level `crtr -h`; a promoted branch shows in the command's own `-h`.
19
36
 
20
37
  `defineStreamingLeaf` has the same declaration shape, but its handler returns `AsyncIterable<object>`. It marks the generated REST mapping as streaming and is covered in [Errors and streaming](errors.md).
@@ -7,6 +7,13 @@ description: Behavior changes in @north-light/sdk that an app must act on, newes
7
7
 
8
8
  Changes to `@north-light/sdk` that can change what an existing app sees. Newest first.
9
9
 
10
+ ## 0.3.515
11
+
12
+ `questions.cancel` and notices. Additive: no request or response an existing app sends or reads changes.
13
+
14
+ - `questions.cancel(questionId)` (`POST /v1/questions/{question_id}/cancel`) cancels an open question of the app's own run, as the person dismissing it. The asking agent is told it was dismissed, and the run leaves `waiting_on_user` when no other decision is open. Another app's run is `other_app_run`; a question already answered or canceled is 409 `run_settled` with `details.state` and `details.result`. It returns the question as `questions.get` does, with `status: 'canceled'`.
15
+ - A run's agent can raise a notice: a page that takes no response. A notice does not make the run `waiting_on_user`, is not in `pending_question_ids`, and emits no `waiting_on_user` event. `RunObject.open_notice_ids` lists the open ones. `questions.get` returns `kind: 'decision' | 'notice'`; acknowledge a notice with `questions.answer(id, {})`, or cancel it.
16
+
10
17
  ## 0.3.512
11
18
 
12
19
  The runtime serves `GET /v1/ready` on its owner API and its app listener. Additive: no request or response an existing app sends or reads changes.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@north-light/cli",
3
- "version": "0.3.515",
3
+ "version": "0.3.519",
4
4
  "description": "crtr — agent runtime with memory, plugins, and marketplaces",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -139,7 +139,7 @@
139
139
  },
140
140
  "license": "UNLICENSED",
141
141
  "dependencies": {
142
- "@north-light/identity": "0.3.515",
142
+ "@north-light/identity": "0.3.519",
143
143
  "@earendil-works/pi-agent-core": "1.1.0",
144
144
  "@earendil-works/pi-ai": "1.1.0",
145
145
  "@earendil-works/pi-coding-agent": "1.1.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@north-light/identity",
3
- "version": "0.3.515",
3
+ "version": "0.3.519",
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.515",
3
+ "version": "0.3.519",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@north-light/cli",
9
- "version": "0.3.515",
9
+ "version": "0.3.519",
10
10
  "hasInstallScript": true,
11
11
  "license": "UNLICENSED",
12
12
  "workspaces": [
@@ -24,7 +24,7 @@
24
24
  "@milkdown/preset-gfm": "^7.22.3",
25
25
  "@milkdown/prose": "^7.22.3",
26
26
  "@milkdown/utils": "^7.22.3",
27
- "@north-light/identity": "0.3.515",
27
+ "@north-light/identity": "0.3.519",
28
28
  "cron-parser": "^5.6.0",
29
29
  "esbuild": "^0.27.7",
30
30
  "jose": "^6.2.12",
@@ -12468,17 +12468,17 @@
12468
12468
  },
12469
12469
  "packages/crouter-api": {
12470
12470
  "name": "@north-light/api",
12471
- "version": "0.3.515",
12471
+ "version": "0.3.519",
12472
12472
  "license": "UNLICENSED"
12473
12473
  },
12474
12474
  "packages/crouter-env-docker": {
12475
12475
  "name": "@north-light/env-docker",
12476
- "version": "0.3.515",
12476
+ "version": "0.3.519",
12477
12477
  "license": "UNLICENSED"
12478
12478
  },
12479
12479
  "packages/crouter-identity": {
12480
12480
  "name": "@north-light/identity",
12481
- "version": "0.3.515",
12481
+ "version": "0.3.519",
12482
12482
  "license": "UNLICENSED",
12483
12483
  "dependencies": {
12484
12484
  "jose": "^6.2.1"
@@ -12486,20 +12486,20 @@
12486
12486
  },
12487
12487
  "packages/crouter-plugin": {
12488
12488
  "name": "@north-light/plugin",
12489
- "version": "0.3.515",
12489
+ "version": "0.3.519",
12490
12490
  "license": "UNLICENSED",
12491
12491
  "dependencies": {
12492
- "@north-light/api": "0.3.515",
12493
- "@north-light/identity": "0.3.515"
12492
+ "@north-light/api": "0.3.519",
12493
+ "@north-light/identity": "0.3.519"
12494
12494
  }
12495
12495
  },
12496
12496
  "packages/crouter-sdk": {
12497
12497
  "name": "@north-light/sdk",
12498
- "version": "0.3.515",
12498
+ "version": "0.3.519",
12499
12499
  "license": "UNLICENSED",
12500
12500
  "dependencies": {
12501
- "@north-light/api": "0.3.515",
12502
- "@north-light/identity": "0.3.515",
12501
+ "@north-light/api": "0.3.519",
12502
+ "@north-light/identity": "0.3.519",
12503
12503
  "jose": "^6.2.1"
12504
12504
  },
12505
12505
  "bin": {