@north-light/cli 0.3.414 → 0.3.415

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 (88) hide show
  1. package/dist/api/client.d.ts +20 -0
  2. package/dist/api/client.js +3 -3
  3. package/dist/api/dto/common.d.ts +1 -1
  4. package/dist/api/dto/crons.d.ts +7 -1
  5. package/dist/api/dto/human-requests.d.ts +1 -1
  6. package/dist/api/dto/run-events.d.ts +1 -1
  7. package/dist/api/dto/usage.d.ts +27 -0
  8. package/dist/api/dto/usage.js +0 -0
  9. package/dist/api/errors.js +1 -1
  10. package/dist/api/index.d.ts +3 -2
  11. package/dist/api/index.js +1 -1
  12. package/dist/builtin-memory/crouter-sdk/README.md +22 -4
  13. package/dist/builtin-memory/crouter-sdk/client.md +32 -1
  14. package/dist/builtin-memory/crouter-sdk/errors.md +3 -0
  15. package/dist/builtin-memory/crouter-sdk/getting-started.md +26 -4
  16. package/dist/builtin-memory/crouter-sdk/release-notes.md +23 -0
  17. package/dist/builtin-memory/crouter-sdk/testing.md +9 -3
  18. package/dist/builtin-memory/internal/nodes-and-canvas.md +4 -0
  19. package/dist/clients/attach/viewer.js +606 -606
  20. package/dist/commands/cron.js +1 -1
  21. package/dist/commands/node/create.js +1 -1
  22. package/dist/core/canvas/crons.d.ts +15 -1
  23. package/dist/core/canvas/crons.js +13 -13
  24. package/dist/core/canvas/delivery-contract.d.ts +1 -1
  25. package/dist/core/canvas/migrations.js +73 -66
  26. package/dist/core/canvas/types.d.ts +1 -1
  27. package/dist/core/command.d.ts +7 -0
  28. package/dist/core/command.js +9 -9
  29. package/dist/core/grants/mirror.d.ts +4 -0
  30. package/dist/core/grants/mirror.js +2 -2
  31. package/dist/core/layout.d.ts +2 -0
  32. package/dist/core/layout.js +2 -2
  33. package/dist/core/llm/run.d.ts +5 -1
  34. package/dist/core/llm/run.js +3 -3
  35. package/dist/core/providers/call.d.ts +2 -0
  36. package/dist/core/providers/call.js +1 -1
  37. package/dist/core/providers/errors.js +1 -1
  38. package/dist/core/providers/receipts.d.ts +2 -0
  39. package/dist/core/providers/receipts.js +2 -3
  40. package/dist/core/runs/operations.d.ts +2 -0
  41. package/dist/core/runs/operations.js +4 -4
  42. package/dist/core/runtime/broker/model-adapter.js +2 -2
  43. package/dist/core/runtime/run-command.d.ts +6 -0
  44. package/dist/core/runtime/run-command.js +1 -1
  45. package/dist/core/spaces/mounts.d.ts +2 -0
  46. package/dist/core/spaces/mounts.js +1 -1
  47. package/dist/core/spaces/process.d.ts +14 -2
  48. package/dist/core/spaces/process.js +1 -1
  49. package/dist/core/usage/ledger.d.ts +49 -0
  50. package/dist/core/usage/ledger.js +10 -0
  51. package/dist/daemon/api/app-listener.js +12 -12
  52. package/dist/daemon/api/handlers/crons.js +1 -1
  53. package/dist/daemon/api/handlers/llm.d.ts +8 -0
  54. package/dist/daemon/api/handlers/llm.js +3 -3
  55. package/dist/daemon/api/map.d.ts +1 -1
  56. package/dist/daemon/api/map.js +1 -1
  57. package/dist/daemon/api/operations.js +1 -1
  58. package/dist/daemon/cron/armed-context.d.ts +21 -4
  59. package/dist/daemon/cron/armed-context.js +1 -1
  60. package/dist/daemon/cron/authority.d.ts +27 -0
  61. package/dist/daemon/cron/authority.js +1 -0
  62. package/dist/daemon/cron/disposition.d.ts +2 -0
  63. package/dist/daemon/cron/disposition.js +1 -1
  64. package/dist/daemon/cron/escalation.js +3 -3
  65. package/dist/daemon/cron/folders.d.ts +5 -0
  66. package/dist/daemon/cron/folders.js +1 -0
  67. package/dist/daemon/cron/lease-recovery.js +1 -1
  68. package/dist/daemon/cron/settle.d.ts +10 -0
  69. package/dist/daemon/cron/settle.js +1 -1
  70. package/dist/daemon/cron/sinks.d.ts +2 -1
  71. package/dist/daemon/cron/sinks.js +2 -2
  72. package/dist/daemon/cron-run.js +1 -1
  73. package/dist/daemon/reconcilers/action-lane.js +1 -1
  74. package/dist/daemon/reconcilers/broker-supervision.d.ts +10 -0
  75. package/dist/daemon/reconcilers/broker-supervision.js +4 -4
  76. package/dist/daemon/reconcilers/cron-lane.d.ts +1 -0
  77. package/dist/daemon/reconcilers/cron-lane.js +1 -1
  78. package/docs/sdk/README.md +22 -4
  79. package/docs/sdk/client.md +32 -1
  80. package/docs/sdk/errors.md +3 -0
  81. package/docs/sdk/getting-started.md +26 -4
  82. package/docs/sdk/release-notes.md +23 -0
  83. package/docs/sdk/testing.md +9 -3
  84. package/package.json +2 -2
  85. package/packages/crouter-identity/dist/index.d.ts +29 -2
  86. package/packages/crouter-identity/dist/index.js +81 -12
  87. package/packages/crouter-identity/package.json +1 -1
  88. package/runtime.lock.json +12 -12
@@ -71,7 +71,70 @@ export function covers(held, requested) {
71
71
  }
72
72
  return held === requested;
73
73
  }
74
- /** Keys are scoped to one configured issuer; failed refreshes keep the last successful set. */
74
+ /** A directory fetch ran past its deadline (or was aborted by its caller before that). `code` is `deadline_exceeded`;
75
+ * `deadlineAt` is the instant (epoch ms) the operation had to finish by. The `@north-light/sdk` maps it to its
76
+ * `DeadlineExceededError`. */
77
+ export class FetchDeadlineError extends Error {
78
+ deadlineAt;
79
+ url;
80
+ code = 'deadline_exceeded';
81
+ constructor(deadlineAt, url) {
82
+ super(`${url} did not answer by the deadline (${new Date(deadlineAt).toISOString()})`);
83
+ this.deadlineAt = deadlineAt;
84
+ this.url = url;
85
+ this.name = 'FetchDeadlineError';
86
+ }
87
+ }
88
+ /** The default deadline for one `JwksCache.load`, covering its discovery read and its key-set read together. */
89
+ export const DEFAULT_JWKS_TIMEOUT_MS = 10_000;
90
+ /**
91
+ * One `fetch` plus the reading of its answer (`read`, e.g. `response.json()`), bounded by one absolute deadline. The
92
+ * request is aborted when the deadline passes or `signal` fires, and the returned promise rejects at that moment
93
+ * even when `fetcher` ignores its signal, so a directory that accepts the connection and never answers cannot hold a
94
+ * caller (or a lock the caller holds) past `deadlineAt`. Rejects `FetchDeadlineError` at the deadline and the signal's
95
+ * reason when `signal` fired first.
96
+ */
97
+ export async function boundedFetch(fetcher, url, init, bounds, read) {
98
+ const { deadlineAt, signal } = bounds;
99
+ if (signal?.aborted)
100
+ throw signal.reason;
101
+ const remaining = deadlineAt - Date.now();
102
+ if (remaining <= 0)
103
+ throw new FetchDeadlineError(deadlineAt, url);
104
+ const controller = new AbortController();
105
+ let timer;
106
+ let onAbort;
107
+ const stop = new Promise((_resolve, reject) => {
108
+ timer = setTimeout(() => { const error = new FetchDeadlineError(deadlineAt, url); controller.abort(error); reject(error); }, remaining);
109
+ if (signal) {
110
+ onAbort = () => { controller.abort(signal.reason); reject(signal.reason); };
111
+ signal.addEventListener('abort', onAbort, { once: true });
112
+ }
113
+ });
114
+ const work = (async () => {
115
+ const response = await fetcher(url, { ...init, signal: controller.signal });
116
+ // A response that arrives after the deadline won is discarded: close it so its connection is not retained.
117
+ if (controller.signal.aborted) {
118
+ void response.body?.cancel().catch(() => { });
119
+ throw controller.signal.reason;
120
+ }
121
+ return read(response);
122
+ })();
123
+ // The loser of the race must not surface as an unhandled rejection.
124
+ work.catch(() => { });
125
+ stop.catch(() => { });
126
+ try {
127
+ return await Promise.race([work, stop]);
128
+ }
129
+ finally {
130
+ clearTimeout(timer);
131
+ if (onAbort)
132
+ signal?.removeEventListener('abort', onAbort);
133
+ }
134
+ }
135
+ /** Keys are scoped to one configured issuer; failed refreshes keep the last successful set. Every `load` is bounded by
136
+ * one deadline (`timeoutMs`, default `DEFAULT_JWKS_TIMEOUT_MS`) over its discovery read and its key-set read together;
137
+ * callers that join an in-flight load share that load's deadline. */
75
138
  export class JwksCache {
76
139
  issuer;
77
140
  fetcher;
@@ -79,16 +142,20 @@ export class JwksCache {
79
142
  lastUnknownRefresh = -Infinity;
80
143
  loading;
81
144
  discovery;
82
- constructor(issuer, fetcher = fetch, jwksUri) {
145
+ timeoutMs;
146
+ constructor(issuer, fetcher = fetch, jwksUri, options = {}) {
83
147
  this.issuer = issuer;
84
148
  this.fetcher = fetcher;
85
149
  if (jwksUri)
86
150
  this.discovery = { jwks_uri: jwksUri };
151
+ this.timeoutMs = options.timeoutMs ?? DEFAULT_JWKS_TIMEOUT_MS;
152
+ if (!Number.isFinite(this.timeoutMs) || this.timeoutMs <= 0 || this.timeoutMs > 2_147_483_647)
153
+ throw new RangeError('JwksCache timeoutMs must be a positive finite number of at most 2147483647');
87
154
  }
88
155
  async load() {
89
156
  if (this.loading)
90
157
  return this.loading;
91
- const loading = this.fetchKeys();
158
+ const loading = this.fetchKeys(Date.now() + this.timeoutMs);
92
159
  this.loading = loading;
93
160
  try {
94
161
  return await loading;
@@ -97,20 +164,22 @@ export class JwksCache {
97
164
  this.loading = undefined;
98
165
  }
99
166
  }
100
- async fetchKeys() {
167
+ async fetchKeys(deadlineAt) {
101
168
  if (!this.discovery) {
102
- const response = await this.fetcher(`${this.issuer.replace(/\/$/, '')}/.well-known/openid-configuration`);
103
- if (!response.ok)
104
- throw new Error(`Directory discovery failed: ${response.status}`);
105
- const value = await response.json();
169
+ const value = await boundedFetch(this.fetcher, `${this.issuer.replace(/\/$/, '')}/.well-known/openid-configuration`, {}, { deadlineAt }, async (response) => {
170
+ if (!response.ok)
171
+ throw new Error(`Directory discovery failed: ${response.status}`);
172
+ return await response.json();
173
+ });
106
174
  if (!value || typeof value !== 'object' || !('jwks_uri' in value) || typeof value.jwks_uri !== 'string')
107
175
  throw new Error('Directory discovery has no jwks_uri');
108
176
  this.discovery = { jwks_uri: value.jwks_uri };
109
177
  }
110
- const response = await this.fetcher(this.discovery.jwks_uri);
111
- if (!response.ok)
112
- throw new Error(`Directory JWKS failed: ${response.status}`);
113
- const keys = await response.json();
178
+ const keys = await boundedFetch(this.fetcher, this.discovery.jwks_uri, {}, { deadlineAt }, async (response) => {
179
+ if (!response.ok)
180
+ throw new Error(`Directory JWKS failed: ${response.status}`);
181
+ return await response.json();
182
+ });
114
183
  if (!keys || typeof keys !== 'object' || !('keys' in keys) || !Array.isArray(keys.keys))
115
184
  throw new Error('Invalid directory JWKS');
116
185
  this.keys = keys;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@north-light/identity",
3
- "version": "0.3.414",
3
+ "version": "0.3.415",
4
4
  "description": "crouter cloud 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.414",
3
+ "version": "0.3.415",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@north-light/cli",
9
- "version": "0.3.414",
9
+ "version": "0.3.415",
10
10
  "hasInstallScript": true,
11
11
  "license": "UNLICENSED",
12
12
  "workspaces": [
@@ -18,7 +18,7 @@
18
18
  "@earendil-works/pi-ai": "0.99.1",
19
19
  "@earendil-works/pi-coding-agent": "0.99.1",
20
20
  "@earendil-works/pi-tui": "0.99.1",
21
- "@north-light/identity": "0.3.414",
21
+ "@north-light/identity": "0.3.415",
22
22
  "cron-parser": "^5.6.0",
23
23
  "esbuild": "^0.27.7",
24
24
  "jose": "^6.2.12",
@@ -13814,17 +13814,17 @@
13814
13814
  },
13815
13815
  "packages/crouter-api": {
13816
13816
  "name": "@north-light/api",
13817
- "version": "0.3.414",
13817
+ "version": "0.3.415",
13818
13818
  "license": "UNLICENSED"
13819
13819
  },
13820
13820
  "packages/crouter-env-docker": {
13821
13821
  "name": "@north-light/env-docker",
13822
- "version": "0.3.414",
13822
+ "version": "0.3.415",
13823
13823
  "license": "UNLICENSED"
13824
13824
  },
13825
13825
  "packages/crouter-identity": {
13826
13826
  "name": "@north-light/identity",
13827
- "version": "0.3.414",
13827
+ "version": "0.3.415",
13828
13828
  "license": "UNLICENSED",
13829
13829
  "dependencies": {
13830
13830
  "jose": "^6.2.1"
@@ -13832,20 +13832,20 @@
13832
13832
  },
13833
13833
  "packages/crouter-plugin": {
13834
13834
  "name": "@north-light/plugin",
13835
- "version": "0.3.414",
13835
+ "version": "0.3.415",
13836
13836
  "license": "UNLICENSED",
13837
13837
  "dependencies": {
13838
- "@north-light/api": "0.3.414",
13839
- "@north-light/identity": "0.3.414"
13838
+ "@north-light/api": "0.3.415",
13839
+ "@north-light/identity": "0.3.415"
13840
13840
  }
13841
13841
  },
13842
13842
  "packages/crouter-sdk": {
13843
13843
  "name": "@north-light/sdk",
13844
- "version": "0.3.414",
13844
+ "version": "0.3.415",
13845
13845
  "license": "UNLICENSED",
13846
13846
  "dependencies": {
13847
- "@north-light/api": "0.3.414",
13848
- "@north-light/identity": "0.3.414",
13847
+ "@north-light/api": "0.3.415",
13848
+ "@north-light/identity": "0.3.415",
13849
13849
  "jose": "^6.2.1"
13850
13850
  },
13851
13851
  "bin": {