@north-light/api 0.3.587 → 0.3.589

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.
@@ -50,6 +50,12 @@ export type RunnerStatus = {
50
50
  state: 'unknown';
51
51
  error: string;
52
52
  };
53
+ export interface RunnerStatusOptions {
54
+ /** The execution's broker has posted idle (`executions.idle_at`), so its environment may be frozen. A runner whose environment
55
+ * thaws or bills on any request must then answer from its control plane alone and send nothing to the workload: the Blaxel runner
56
+ * reports `running` for a sandbox that exists and `gone` for one that does not. The process and machine runners ignore it. */
57
+ idle?: boolean;
58
+ }
53
59
  export type RunnerWakeResult = 'signalled' | 'no_broker';
54
60
  export interface RunnerStopOptions {
55
61
  /** Why: written to `executions.stop_reason` by the caller; carried here for the runner's diagnostics. */
@@ -91,7 +97,7 @@ export interface Runner {
91
97
  wake(execution: RunnerExecution): Promise<RunnerWakeResult>;
92
98
  /** Stop the execution's broker. Repeating it on a stopped execution confirms. */
93
99
  stop(execution: RunnerExecution, options: RunnerStopOptions): Promise<RunnerStopResult>;
94
- status(execution: RunnerExecution): Promise<RunnerStatus>;
100
+ status(execution: RunnerExecution, options?: RunnerStatusOptions): Promise<RunnerStatus>;
95
101
  /** Remove everything the runner holds for the node: the run dir (process) or the sandbox (Blaxel). */
96
102
  delete(nodeId: string): Promise<void>;
97
103
  /** Where a viewer reaches this execution's broker. */
@@ -53,7 +53,8 @@ export interface BrokerViewLink {
53
53
  on(event: 'close', listener: (hadError: boolean) => void): this;
54
54
  }
55
55
  /** A broker's `/view` WebSocket as a {@link BrokerViewLink}. Each broker message is one frame, delivered as one
56
- * newline-terminated chunk; each line written is sent as one text message. The headers (the preview token) are
56
+ * newline-terminated chunk; each line written is sent as one text message that keeps its newline: the broker reads
57
+ * this WebSocket as a byte stream through its line decoder, which completes a frame only at a newline. The headers (the preview token) are
57
58
  * sent on the handshake only and kept nowhere else. */
58
59
  export declare function connectWssView(url: string, headers: Record<string, string>): BrokerViewLink;
59
60
  /** Resolve true once the broker's `/view` WebSocket accepts a handshake within `waitMs`, retrying while it refuses;
@@ -103,7 +103,8 @@ export async function waitForBrokerViewSocket(nodeId, exited, waitMs = BROKER_FO
103
103
  /** The port a sandboxed broker serves `/view` on (bound to `::`; reached only through the private preview and its per-attach token). */
104
104
  export const BROKER_VIEW_PORT = 4180;
105
105
  /** A broker's `/view` WebSocket as a {@link BrokerViewLink}. Each broker message is one frame, delivered as one
106
- * newline-terminated chunk; each line written is sent as one text message. The headers (the preview token) are
106
+ * newline-terminated chunk; each line written is sent as one text message that keeps its newline: the broker reads
107
+ * this WebSocket as a byte stream through its line decoder, which completes a frame only at a newline. The headers (the preview token) are
107
108
  * sent on the handshake only and kept nowhere else. */
108
109
  export function connectWssView(url, headers) {
109
110
  return new WssViewLink(url, headers);
@@ -148,7 +149,7 @@ class WssViewLink extends EventEmitter {
148
149
  }
149
150
  /** Like a view.sock write before connect, a line written while the handshake is in flight is queued and sent on open. */
150
151
  write(line) {
151
- const message = line.endsWith('\n') ? line.slice(0, -1) : line;
152
+ const message = line.endsWith('\n') ? line : `${line}\n`;
152
153
  if (this.ws.readyState === WebSocket.CONNECTING) {
153
154
  this.pending.push(message);
154
155
  return true;
@@ -26,7 +26,8 @@ export declare function imageForWake(environmentId: string): Promise<Environment
26
26
  * a new attempt on the accepted one. `lost` means no broker answered: the execution is ended and the caller starts a new attempt. */
27
27
  export declare function wakeResident(execution: Execution): Promise<'woken' | 'lost'>;
28
28
  /** A `running` remote execution is live while its runner reports the broker process running, frozen or not. When the runner
29
- * reports it gone (or ended non-zero), that exact execution is ended; a runner that cannot tell leaves it as it is. */
29
+ * reports it gone (or ended non-zero), that exact execution is ended; a runner that cannot tell leaves it as it is. An idle
30
+ * execution is asked without touching its workload (`RunnerStatusOptions.idle`), so a frozen sandbox is not woken by the question. */
30
31
  export declare function endRemoteExecutionIfGone(execution: Execution): Promise<boolean>;
31
32
  /** The recovery table for a `launching` Blaxel execution found by a claim after its starter is gone. */
32
33
  export declare function recoverLaunching(nodeId: string, now: number): Promise<void>;
@@ -77,9 +77,10 @@ export async function wakeResident(execution) {
77
77
  return 'woken';
78
78
  }
79
79
  /** A `running` remote execution is live while its runner reports the broker process running, frozen or not. When the runner
80
- * reports it gone (or ended non-zero), that exact execution is ended; a runner that cannot tell leaves it as it is. */
80
+ * reports it gone (or ended non-zero), that exact execution is ended; a runner that cannot tell leaves it as it is. An idle
81
+ * execution is asked without touching its workload (`RunnerStatusOptions.idle`), so a frozen sandbox is not woken by the question. */
81
82
  export async function endRemoteExecutionIfGone(execution) {
82
- const status = await runnerForExecution(execution.runner).status(execution);
83
+ const status = await runnerForExecution(execution.runner).status(execution, { idle: execution.idleAt !== null });
83
84
  if (status.state !== 'gone' && status.state !== 'failed')
84
85
  return false;
85
86
  const ended = await endExecution(execution.executionId, { reason: 'lost' });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@north-light/api",
3
- "version": "0.3.587",
3
+ "version": "0.3.589",
4
4
  "description": "Typed crtrd /v1 API contract — DTOs, route builders, the error contract, the CrtrClient, and the command-plugin manifest format. Zero runtime dependencies.",
5
5
  "type": "module",
6
6
  "main": "./dist/api/index.js",