@danypops/vehicle-core 0.14.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/vehicle-contract.d.ts +38 -1
- package/dist/vehicle-jobs.d.ts +45 -0
- package/package.json +1 -1
- package/src/vehicle-contract.ts +44 -1
- package/src/vehicle-jobs.ts +50 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { VehicleJobWakeBudget } from "./vehicle-jobs.js";
|
|
1
|
+
import type { VehicleJobSnapshot, VehicleJobSubmitOptions, VehicleJobSubmitResult, VehicleJobTailResult, VehicleJobWakeBudget } from "./vehicle-jobs.js";
|
|
2
2
|
export type JsonPrimitive = string | number | boolean | null;
|
|
3
3
|
export type JsonValue = JsonPrimitive | readonly JsonValue[] | {
|
|
4
4
|
readonly [key: string]: JsonValue;
|
|
@@ -178,9 +178,24 @@ export interface VehiclePrincipal {
|
|
|
178
178
|
readonly id: string;
|
|
179
179
|
readonly claims?: Readonly<Record<string, JsonValue>>;
|
|
180
180
|
}
|
|
181
|
+
/**
|
|
182
|
+
* callerSessionId/callerProjectRoot identify the real host session (e.g. one Pi TUI process) that
|
|
183
|
+
* originated this call, and its working directory at call time -- a generic ownership/attribution
|
|
184
|
+
* hook any operation handler can read (e.g. scoping a background subscription to the session or
|
|
185
|
+
* project that created it), distinct from both:
|
|
186
|
+
* - correlationId: a caller-CHOSEN id deliberately meant to span several separate invoke() calls
|
|
187
|
+
* (a batch/business-transaction id), not an automatically-derived caller identity.
|
|
188
|
+
* - principal: broader identity/claims used for permission and approval decisions, usually a
|
|
189
|
+
* fixed per-extension value (e.g. {id: "pi-pipes"}), not a distinguishing per-session id.
|
|
190
|
+
* A Pi projection layer (see vehicle-client-pi's invokeVehicleOperation) auto-derives both from
|
|
191
|
+
* context.sessionManager.getSessionId()/context.cwd on every call, the same way it already
|
|
192
|
+
* auto-derives correlationId -- a handler that never reads them pays nothing extra.
|
|
193
|
+
*/
|
|
181
194
|
export interface VehicleInvocationOptions {
|
|
182
195
|
readonly operationId?: string;
|
|
183
196
|
readonly correlationId?: string;
|
|
197
|
+
readonly callerSessionId?: string;
|
|
198
|
+
readonly callerProjectRoot?: string;
|
|
184
199
|
readonly signal?: AbortSignal;
|
|
185
200
|
readonly deadline?: number;
|
|
186
201
|
readonly permissions?: readonly string[];
|
|
@@ -194,6 +209,10 @@ export interface VehicleOperationContext<Input> {
|
|
|
194
209
|
readonly input: Input;
|
|
195
210
|
readonly operationId: string;
|
|
196
211
|
readonly correlationId?: string;
|
|
212
|
+
/** See VehicleInvocationOptions's own doc comment. */
|
|
213
|
+
readonly callerSessionId?: string;
|
|
214
|
+
/** See VehicleInvocationOptions's own doc comment. */
|
|
215
|
+
readonly callerProjectRoot?: string;
|
|
197
216
|
readonly signal: AbortSignal;
|
|
198
217
|
readonly deadline: number;
|
|
199
218
|
readonly permissions: readonly string[];
|
|
@@ -305,6 +324,24 @@ export interface VehicleClient {
|
|
|
305
324
|
manifest(): Promise<VehicleManifest>;
|
|
306
325
|
invoke<Output = unknown>(name: string, version: number, input: unknown, options?: VehicleInvocationOptions): Promise<Output>;
|
|
307
326
|
close(): Promise<void>;
|
|
327
|
+
/**
|
|
328
|
+
* Vehicle Jobs -- submit a background-capable operation (one whose descriptor declares
|
|
329
|
+
* `background`, see {@link VehicleBackgroundCapability}) and get its jobId back immediately,
|
|
330
|
+
* without waiting for the operation itself to make any progress. Optional: a client that
|
|
331
|
+
* never talks to a job-capable Vehicle (or a hand-rolled test double) simply omits these five
|
|
332
|
+
* methods, exactly like this interface's own long-standing `subscribe()`-shaped extras --
|
|
333
|
+
* present on both LocalVehicleClient and RemoteVehicleClient, absent elsewhere. Feature-detect
|
|
334
|
+
* via the operation's own manifest `background` capability, not by probing for these methods.
|
|
335
|
+
*/
|
|
336
|
+
submitJob?(name: string, version: number, input: unknown, options?: VehicleJobSubmitOptions): Promise<VehicleJobSubmitResult>;
|
|
337
|
+
/** Never blocks -- current status, plus output/error once terminal. */
|
|
338
|
+
pollJob?(jobId: string): Promise<VehicleJobSnapshot>;
|
|
339
|
+
/** Progress entries strictly after `cursor` (0 for everything so far), plus the next cursor. Never blocks. */
|
|
340
|
+
tailJob?(jobId: string, cursor?: number): Promise<VehicleJobTailResult>;
|
|
341
|
+
/** Pushes new input to an already-running job's handler, if it opted in via context.steerInputs. */
|
|
342
|
+
steerJob?(jobId: string, input: unknown): Promise<void>;
|
|
343
|
+
/** Best-effort cancellation of a still-running job -- a no-op against an already-terminal one. */
|
|
344
|
+
cancelJob?(jobId: string): Promise<void>;
|
|
308
345
|
}
|
|
309
346
|
export declare function defineVehicleOperation<Input, Output>(options: DefineVehicleOperationOptions<Input, Output>): VehicleOperation<Input, Output>;
|
|
310
347
|
export declare function bindVehicleOperation<Input, Output>(operation: VehicleOperation<Input, Output>, bind: () => VehicleOperationHandler<Input, Output>): VehicleOperationBinding<Input, Output>;
|
package/dist/vehicle-jobs.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
/** Pure pieces of Vehicle Jobs: a termination-reason resolver and a bounded wake-log accumulator. Orchestration lives in vehicle-server's VehicleJobStore. */
|
|
2
|
+
import type { VehiclePrincipal } from "./vehicle-contract.js";
|
|
3
|
+
import type { VehicleFailure } from "./vehicle-errors.js";
|
|
2
4
|
export type VehicleJobStatus = "running" | "succeeded" | "failed" | "canceled";
|
|
3
5
|
/** Highest precedence first -- an explicit cancel always wins even if the handler also settled around the same time. "orphaned" is a restart-reconciliation outcome: a job that was still "running" when its process died, so nothing ever really failed or succeeded -- the record's own status just goes stale. */
|
|
4
6
|
export declare const VEHICLE_JOB_TERMINATION_PRECEDENCE: readonly ["canceled", "timeout", "orphaned", "failed", "succeeded"];
|
|
@@ -101,6 +103,49 @@ export interface VehicleJobRetentionOptions {
|
|
|
101
103
|
readonly deliveredRetentionMs: number;
|
|
102
104
|
readonly now: number;
|
|
103
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* The client-facing wire shapes for Vehicle Jobs -- submit/poll/tail options and results, shared by
|
|
108
|
+
* vehicle-server's VehicleJobStore (the orchestration side) and vehicle-client's job-capable clients
|
|
109
|
+
* (the calling side), so both halves of the wire agree on one definition instead of two structurally
|
|
110
|
+
*-identical copies drifting apart. Every field type referenced here already lives in vehicle-core
|
|
111
|
+
* (VehiclePrincipal, VehicleFailure, VehicleJobStatus, ...), which is what makes it safe for these
|
|
112
|
+
* shapes to live here too, alongside the rest of Vehicle Jobs' pure pieces.
|
|
113
|
+
*/
|
|
114
|
+
export interface VehicleJobSubmitOptions {
|
|
115
|
+
readonly permissions?: readonly string[];
|
|
116
|
+
readonly principal?: VehiclePrincipal;
|
|
117
|
+
readonly idempotencyKey?: string;
|
|
118
|
+
readonly expectedRevision?: string | number;
|
|
119
|
+
readonly approvalCapability?: string;
|
|
120
|
+
readonly correlationId?: string;
|
|
121
|
+
readonly callerSessionId?: string;
|
|
122
|
+
readonly callerProjectRoot?: string;
|
|
123
|
+
/** Defaults to "transition". */
|
|
124
|
+
readonly notifyMode?: VehicleJobNotifyMode;
|
|
125
|
+
/** Defaults to background.defaultWakeBudget; clamped to background.maxWakeBudget either way. */
|
|
126
|
+
readonly wakeBudget?: VehicleJobWakeBudget;
|
|
127
|
+
/** No default -- unset means the job runs until it settles or is canceled. */
|
|
128
|
+
readonly maxLifetimeMs?: number;
|
|
129
|
+
}
|
|
130
|
+
export interface VehicleJobSubmitResult {
|
|
131
|
+
readonly jobId: string;
|
|
132
|
+
}
|
|
133
|
+
export interface VehicleJobSnapshot {
|
|
134
|
+
readonly jobId: string;
|
|
135
|
+
readonly operationName: string;
|
|
136
|
+
readonly operationVersion: number;
|
|
137
|
+
readonly status: VehicleJobStatus;
|
|
138
|
+
readonly createdAt: number;
|
|
139
|
+
readonly updatedAt: number;
|
|
140
|
+
readonly delivered: boolean;
|
|
141
|
+
readonly terminationReason?: VehicleJobTerminationReason;
|
|
142
|
+
readonly output?: unknown;
|
|
143
|
+
readonly error?: VehicleFailure;
|
|
144
|
+
}
|
|
145
|
+
export interface VehicleJobTailResult {
|
|
146
|
+
readonly entries: readonly VehicleJobWakeEntry[];
|
|
147
|
+
readonly cursor: number;
|
|
148
|
+
}
|
|
104
149
|
/**
|
|
105
150
|
* Pure eviction-selection policy, kept separate from VehicleJobStore's own
|
|
106
151
|
* bookkeeping so the bounded-retention rule is independently testable.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@danypops/vehicle-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"description": "Vehicle's runtime-neutral wire contract: operation descriptors, schema codecs, failure shapes. Zero runtime dependencies, zero Bun-specific code -- the one thing every Vehicle client and server package depends on.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
package/src/vehicle-contract.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
VehicleJobSnapshot,
|
|
3
|
+
VehicleJobSubmitOptions,
|
|
4
|
+
VehicleJobSubmitResult,
|
|
5
|
+
VehicleJobTailResult,
|
|
6
|
+
VehicleJobWakeBudget,
|
|
7
|
+
} from "./vehicle-jobs.js";
|
|
2
8
|
|
|
3
9
|
export type JsonPrimitive = string | number | boolean | null;
|
|
4
10
|
export type JsonValue = JsonPrimitive | readonly JsonValue[] | { readonly [key: string]: JsonValue };
|
|
@@ -254,9 +260,24 @@ export interface VehiclePrincipal {
|
|
|
254
260
|
readonly claims?: Readonly<Record<string, JsonValue>>;
|
|
255
261
|
}
|
|
256
262
|
|
|
263
|
+
/**
|
|
264
|
+
* callerSessionId/callerProjectRoot identify the real host session (e.g. one Pi TUI process) that
|
|
265
|
+
* originated this call, and its working directory at call time -- a generic ownership/attribution
|
|
266
|
+
* hook any operation handler can read (e.g. scoping a background subscription to the session or
|
|
267
|
+
* project that created it), distinct from both:
|
|
268
|
+
* - correlationId: a caller-CHOSEN id deliberately meant to span several separate invoke() calls
|
|
269
|
+
* (a batch/business-transaction id), not an automatically-derived caller identity.
|
|
270
|
+
* - principal: broader identity/claims used for permission and approval decisions, usually a
|
|
271
|
+
* fixed per-extension value (e.g. {id: "pi-pipes"}), not a distinguishing per-session id.
|
|
272
|
+
* A Pi projection layer (see vehicle-client-pi's invokeVehicleOperation) auto-derives both from
|
|
273
|
+
* context.sessionManager.getSessionId()/context.cwd on every call, the same way it already
|
|
274
|
+
* auto-derives correlationId -- a handler that never reads them pays nothing extra.
|
|
275
|
+
*/
|
|
257
276
|
export interface VehicleInvocationOptions {
|
|
258
277
|
readonly operationId?: string;
|
|
259
278
|
readonly correlationId?: string;
|
|
279
|
+
readonly callerSessionId?: string;
|
|
280
|
+
readonly callerProjectRoot?: string;
|
|
260
281
|
readonly signal?: AbortSignal;
|
|
261
282
|
readonly deadline?: number;
|
|
262
283
|
readonly permissions?: readonly string[];
|
|
@@ -271,6 +292,10 @@ export interface VehicleOperationContext<Input> {
|
|
|
271
292
|
readonly input: Input;
|
|
272
293
|
readonly operationId: string;
|
|
273
294
|
readonly correlationId?: string;
|
|
295
|
+
/** See VehicleInvocationOptions's own doc comment. */
|
|
296
|
+
readonly callerSessionId?: string;
|
|
297
|
+
/** See VehicleInvocationOptions's own doc comment. */
|
|
298
|
+
readonly callerProjectRoot?: string;
|
|
274
299
|
readonly signal: AbortSignal;
|
|
275
300
|
readonly deadline: number;
|
|
276
301
|
readonly permissions: readonly string[];
|
|
@@ -419,6 +444,24 @@ export interface VehicleClient {
|
|
|
419
444
|
manifest(): Promise<VehicleManifest>;
|
|
420
445
|
invoke<Output = unknown>(name: string, version: number, input: unknown, options?: VehicleInvocationOptions): Promise<Output>;
|
|
421
446
|
close(): Promise<void>;
|
|
447
|
+
/**
|
|
448
|
+
* Vehicle Jobs -- submit a background-capable operation (one whose descriptor declares
|
|
449
|
+
* `background`, see {@link VehicleBackgroundCapability}) and get its jobId back immediately,
|
|
450
|
+
* without waiting for the operation itself to make any progress. Optional: a client that
|
|
451
|
+
* never talks to a job-capable Vehicle (or a hand-rolled test double) simply omits these five
|
|
452
|
+
* methods, exactly like this interface's own long-standing `subscribe()`-shaped extras --
|
|
453
|
+
* present on both LocalVehicleClient and RemoteVehicleClient, absent elsewhere. Feature-detect
|
|
454
|
+
* via the operation's own manifest `background` capability, not by probing for these methods.
|
|
455
|
+
*/
|
|
456
|
+
submitJob?(name: string, version: number, input: unknown, options?: VehicleJobSubmitOptions): Promise<VehicleJobSubmitResult>;
|
|
457
|
+
/** Never blocks -- current status, plus output/error once terminal. */
|
|
458
|
+
pollJob?(jobId: string): Promise<VehicleJobSnapshot>;
|
|
459
|
+
/** Progress entries strictly after `cursor` (0 for everything so far), plus the next cursor. Never blocks. */
|
|
460
|
+
tailJob?(jobId: string, cursor?: number): Promise<VehicleJobTailResult>;
|
|
461
|
+
/** Pushes new input to an already-running job's handler, if it opted in via context.steerInputs. */
|
|
462
|
+
steerJob?(jobId: string, input: unknown): Promise<void>;
|
|
463
|
+
/** Best-effort cancellation of a still-running job -- a no-op against an already-terminal one. */
|
|
464
|
+
cancelJob?(jobId: string): Promise<void>;
|
|
422
465
|
}
|
|
423
466
|
|
|
424
467
|
export function defineVehicleOperation<Input, Output>(
|
package/src/vehicle-jobs.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
/** Pure pieces of Vehicle Jobs: a termination-reason resolver and a bounded wake-log accumulator. Orchestration lives in vehicle-server's VehicleJobStore. */
|
|
2
2
|
|
|
3
|
+
import type { VehiclePrincipal } from "./vehicle-contract.js";
|
|
4
|
+
import type { VehicleFailure } from "./vehicle-errors.js";
|
|
5
|
+
|
|
3
6
|
export type VehicleJobStatus = "running" | "succeeded" | "failed" | "canceled";
|
|
4
7
|
|
|
5
8
|
/** Highest precedence first -- an explicit cancel always wins even if the handler also settled around the same time. "orphaned" is a restart-reconciliation outcome: a job that was still "running" when its process died, so nothing ever really failed or succeeded -- the record's own status just goes stale. */
|
|
@@ -212,6 +215,53 @@ export interface VehicleJobRetentionOptions {
|
|
|
212
215
|
readonly now: number;
|
|
213
216
|
}
|
|
214
217
|
|
|
218
|
+
/**
|
|
219
|
+
* The client-facing wire shapes for Vehicle Jobs -- submit/poll/tail options and results, shared by
|
|
220
|
+
* vehicle-server's VehicleJobStore (the orchestration side) and vehicle-client's job-capable clients
|
|
221
|
+
* (the calling side), so both halves of the wire agree on one definition instead of two structurally
|
|
222
|
+
*-identical copies drifting apart. Every field type referenced here already lives in vehicle-core
|
|
223
|
+
* (VehiclePrincipal, VehicleFailure, VehicleJobStatus, ...), which is what makes it safe for these
|
|
224
|
+
* shapes to live here too, alongside the rest of Vehicle Jobs' pure pieces.
|
|
225
|
+
*/
|
|
226
|
+
export interface VehicleJobSubmitOptions {
|
|
227
|
+
readonly permissions?: readonly string[];
|
|
228
|
+
readonly principal?: VehiclePrincipal;
|
|
229
|
+
readonly idempotencyKey?: string;
|
|
230
|
+
readonly expectedRevision?: string | number;
|
|
231
|
+
readonly approvalCapability?: string;
|
|
232
|
+
readonly correlationId?: string;
|
|
233
|
+
readonly callerSessionId?: string;
|
|
234
|
+
readonly callerProjectRoot?: string;
|
|
235
|
+
/** Defaults to "transition". */
|
|
236
|
+
readonly notifyMode?: VehicleJobNotifyMode;
|
|
237
|
+
/** Defaults to background.defaultWakeBudget; clamped to background.maxWakeBudget either way. */
|
|
238
|
+
readonly wakeBudget?: VehicleJobWakeBudget;
|
|
239
|
+
/** No default -- unset means the job runs until it settles or is canceled. */
|
|
240
|
+
readonly maxLifetimeMs?: number;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export interface VehicleJobSubmitResult {
|
|
244
|
+
readonly jobId: string;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export interface VehicleJobSnapshot {
|
|
248
|
+
readonly jobId: string;
|
|
249
|
+
readonly operationName: string;
|
|
250
|
+
readonly operationVersion: number;
|
|
251
|
+
readonly status: VehicleJobStatus;
|
|
252
|
+
readonly createdAt: number;
|
|
253
|
+
readonly updatedAt: number;
|
|
254
|
+
readonly delivered: boolean;
|
|
255
|
+
readonly terminationReason?: VehicleJobTerminationReason;
|
|
256
|
+
readonly output?: unknown;
|
|
257
|
+
readonly error?: VehicleFailure;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export interface VehicleJobTailResult {
|
|
261
|
+
readonly entries: readonly VehicleJobWakeEntry[];
|
|
262
|
+
readonly cursor: number;
|
|
263
|
+
}
|
|
264
|
+
|
|
215
265
|
/**
|
|
216
266
|
* Pure eviction-selection policy, kept separate from VehicleJobStore's own
|
|
217
267
|
* bookkeeping so the bounded-retention rule is independently testable.
|