@north-light/crouter-api 0.3.192 → 0.3.194
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/api/client.d.ts +31 -10
- package/dist/api/client.js +83 -24
- package/dist/api/dto/bash-jobs.d.ts +24 -0
- package/dist/api/dto/bash-jobs.js +9 -0
- package/dist/api/dto/human.d.ts +3 -4
- package/dist/api/dto/inbox.d.ts +106 -65
- package/dist/api/dto/inbox.js +2 -8
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.js +1 -0
- package/dist/api/routes.d.ts +7 -0
- package/dist/api/routes.js +7 -0
- package/package.json +1 -1
package/dist/api/client.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { DaemonRestartDTO, HealthDTO, StatusDTO } from './dto/health.js';
|
|
2
|
+
import type { BashJobStatusDTO, BashJobStopResultDTO } from './dto/bash-jobs.js';
|
|
2
3
|
import type { ArtifactListDTO, ArtifactsQuery, ContextListDTO, CreateNodeRequest, ListNodesQuery, NodeDetailDTO, NodeSessionDTO, NodeSnapshotDTO, NodeSummaryDTO, TranscriptDTO, TranscriptQuery } from './dto/nodes.js';
|
|
3
4
|
import type { InterruptResultDTO, MessageResultDTO, SendMessageRequest } from './dto/messages.js';
|
|
4
5
|
import type { PushReportRequest, PushReportResultDTO, ReportDTO, ReportsQuery } from './dto/reports.js';
|
|
@@ -15,7 +16,7 @@ import type { CredentialRemovalResultDTO, CredentialResultDTO, InstallCredential
|
|
|
15
16
|
import type { CreateHumanBridgeRequest, HumanBridgeResultDTO, HumanCancelRequest, HumanCancelResultDTO, HumanResolveRequest, HumanResolveResultDTO } from './dto/human.js';
|
|
16
17
|
import type { CancelReviewRequest, CreateReviewRequest, ListReviewsQuery, ReviewCancelResultDTO, ReviewDocumentBaseDTO, ReviewDTO, ReviewListDTO, ReviewSubmitResultDTO } from './dto/reviews.js';
|
|
17
18
|
import type { CreateReviewCommentRequest, EditReviewCommentRequest, ListReviewCommentsQuery, ReadReviewCommentEventsQuery, ReviewCommentActionRequest, ReviewCommentDetailDTO, ReviewCommentEventsDTO, ReviewCommentListDTO, ReviewCommentMutationDTO, ReviewCommentRangeBatchRequest, ReviewCommentRangeBatchResultDTO } from './dto/review-comments.js';
|
|
18
|
-
import type { CancelInboxTicketRequest, CanceledTicketResultDTO,
|
|
19
|
+
import type { CancelInboxTicketRequest, CanceledTicketResultDTO, InboxListDTO, InboxPageDTO, InboxPageHistoryDTO, InboxPageRenderDTO, PagesBundleAssetDTO, InboxTicketIdDTO, PageResponsesDTO, PageTicketResultDTO, RespondInboxPageRequest } from './dto/inbox.js';
|
|
19
20
|
import type { AttentionCountsDTO, AttentionDTO, DashboardDTO, DashboardQuery, HistoryGrepQuery, HistoryGrepResultDTO, HistoryReadQuery, HistoryReadResultDTO, HistorySearchQuery, HistorySearchResultDTO, PruneRequest, PruneResultDTO, RebuildIndexResultDTO, RosterDTO, SnapshotDTO } from './dto/canvas.js';
|
|
20
21
|
import type { CloseWorktreeResultDTO } from './dto/worktree.js';
|
|
21
22
|
import type { BrokerExtensionStateDTO, BrokerGeneratedNameRequest, BrokerGeneratedNameResultDTO, BrokerInboxCursorDirective, BrokerInboxCursorRequest, BrokerModelCommitRequest, BrokerModelCommitResultDTO, BrokerPersonaAckRequest, BrokerSessionBoundRequest, BrokerSessionBoundResultDTO, BrokerSettleDirective, BrokerSettleRequest } from './dto/broker-ops.js';
|
|
@@ -79,6 +80,8 @@ export declare class CrtrClient {
|
|
|
79
80
|
createNode(req: CreateNodeRequest): Promise<NodeDetailDTO>;
|
|
80
81
|
listNodes(q?: ListNodesQuery): Promise<NodeSummaryDTO[]>;
|
|
81
82
|
getNode(id: string): Promise<NodeDetailDTO>;
|
|
83
|
+
listBashJobs(id: string): Promise<BashJobStatusDTO[]>;
|
|
84
|
+
stopBashJob(id: string, jobId: string): Promise<BashJobStopResultDTO>;
|
|
82
85
|
sendMessage(id: string, req: SendMessageRequest): Promise<MessageResultDTO>;
|
|
83
86
|
/** First-class interrupt (the human Esc): cancels pending undelivered
|
|
84
87
|
* human-send inbox entries, then aborts a live in-flight turn. NEVER
|
|
@@ -165,7 +168,7 @@ export declare class CrtrClient {
|
|
|
165
168
|
* (`spawnNode` server-side). Distinct from `createNode` (which launches a
|
|
166
169
|
* broker) precisely because a human bridge must never have one. */
|
|
167
170
|
createHumanBridge(req: CreateHumanBridgeRequest): Promise<HumanBridgeResultDTO>;
|
|
168
|
-
/** Resolve a
|
|
171
|
+
/** Resolve a page ticket answer. crtrd claims the ticket (taking over a live
|
|
169
172
|
* inbox claim where one exists), publishes the canonical result, delivers
|
|
170
173
|
* to the asking node, and retires the bridge before this resolves. */
|
|
171
174
|
resolveHumanTicket(nodeId: string, body: HumanResolveRequest): Promise<HumanResolveResultDTO>;
|
|
@@ -188,16 +191,28 @@ export declare class CrtrClient {
|
|
|
188
191
|
resolveReviewComment(commentId: string, req?: ReviewCommentActionRequest): Promise<ReviewCommentMutationDTO>;
|
|
189
192
|
reopenReviewComment(commentId: string, req?: ReviewCommentActionRequest): Promise<ReviewCommentMutationDTO>;
|
|
190
193
|
deleteReviewComment(commentId: string, req?: ReviewCommentActionRequest): Promise<ReviewCommentMutationDTO>;
|
|
191
|
-
/** Pending
|
|
194
|
+
/** Pending page/review tickets across every available crouter-owned
|
|
192
195
|
* humanloop root. */
|
|
193
196
|
listHumanInbox(): Promise<InboxListDTO>;
|
|
194
|
-
/** Read one
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
197
|
+
/** Read one page ticket by its opaque ticket id (pending, resolved, or canceled). */
|
|
198
|
+
getHumanInboxPage(ticketId: InboxTicketIdDTO): Promise<InboxPageDTO>;
|
|
199
|
+
/** All page tickets raised by one node, oldest first (pending, resolved, or canceled). */
|
|
200
|
+
getInboxHistory(nodeId: string): Promise<InboxPageHistoryDTO>;
|
|
201
|
+
/** Submit responses for a page ticket. Single-assignment server-side: a competing
|
|
202
|
+
* resolution races to `ticket_already_resolved`. */
|
|
203
|
+
respondHumanInboxPage(ticketId: InboxTicketIdDTO, request: RespondInboxPageRequest): Promise<PageTicketResultDTO>;
|
|
204
|
+
/** Autosave partial page work. Omitted slots are permitted in progress updates. */
|
|
205
|
+
postInboxProgress(ticketId: InboxTicketIdDTO, responses: PageResponsesDTO): Promise<void>;
|
|
206
|
+
/** Get the published response for a resolved page ticket. 404 if pending or canceled. */
|
|
207
|
+
getInboxResponse(ticketId: InboxTicketIdDTO): Promise<PageTicketResultDTO>;
|
|
208
|
+
/** The page's body-level HTML payload, for pending, resolved, and canceled
|
|
209
|
+
* tickets alike. The host wraps it and injects the page runtime bundle. */
|
|
210
|
+
renderHumanInboxPage(ticketId: InboxTicketIdDTO): Promise<InboxPageRenderDTO>;
|
|
211
|
+
/** One built page runtime asset. Pass the previously returned `etag` as
|
|
212
|
+
* `ifNoneMatch` to get a 304 (and no bytes) while the cached copy is
|
|
213
|
+
* current. The one non-JSON route on this surface. */
|
|
214
|
+
getPagesBundleAsset(asset: 'js' | 'css', ifNoneMatch?: string): Promise<PagesBundleAssetDTO>;
|
|
215
|
+
/** Cancel a ticket (terminal response, never deletion). */
|
|
201
216
|
cancelHumanInboxTicket(ticketId: InboxTicketIdDTO, request?: CancelInboxTicketRequest): Promise<CanceledTicketResultDTO>;
|
|
202
217
|
/** Composed client-side from `GET /v1/nodes` + `GET /v1/status` (spec §6.3 —
|
|
203
218
|
* the dashboard is absorbed into those two reads; there is no single route).
|
|
@@ -231,7 +246,13 @@ export declare class CrtrClient {
|
|
|
231
246
|
/** Raw request for routes not yet method-wrapped. Applies the same
|
|
232
247
|
* autostart + error-mapping semantics. */
|
|
233
248
|
request<T>(method: string, path: string, body?: unknown): Promise<T>;
|
|
249
|
+
/** The unparsed request: autostart + handover recovery, no JSON parse. Every
|
|
250
|
+
* wrapper goes through here; only the non-JSON routes call it directly. */
|
|
251
|
+
private send;
|
|
234
252
|
private nodePath;
|
|
253
|
+
/** Validate a background job id before route construction. Job ids arrive
|
|
254
|
+
* from the daemon's file-backed roster and must remain one path segment. */
|
|
255
|
+
private jobPath;
|
|
235
256
|
/** Validate a cron id before route construction — `routes.ts` interpolates
|
|
236
257
|
* it raw, so a value carrying `/`, whitespace or `?` would corrupt the
|
|
237
258
|
* request line rather than 404 cleanly. Mirrors `nodePath`. */
|
package/dist/api/client.js
CHANGED
|
@@ -16,6 +16,7 @@ import { join } from 'node:path';
|
|
|
16
16
|
import { ApiError, isErrorBody } from './errors.js';
|
|
17
17
|
import { routes } from './routes.js';
|
|
18
18
|
import { isSafeNodeId } from './dto/common.js';
|
|
19
|
+
import { isSafeBashJobId } from './dto/bash-jobs.js';
|
|
19
20
|
import { isSafeCronId, } from './dto/crons.js';
|
|
20
21
|
/** Filesystem/scope constants mirrored from `core/types.ts` (`CRTR_DIR_NAME`)
|
|
21
22
|
* and `core/canvas/paths.ts` (`crtrHome`/`apiSocketPath`). Duplicated — not
|
|
@@ -97,6 +98,12 @@ export class CrtrClient {
|
|
|
97
98
|
getNode(id) {
|
|
98
99
|
return this.request('GET', routes.node(this.nodePath(id)));
|
|
99
100
|
}
|
|
101
|
+
listBashJobs(id) {
|
|
102
|
+
return this.request('GET', routes.nodeJobs(this.nodePath(id)));
|
|
103
|
+
}
|
|
104
|
+
stopBashJob(id, jobId) {
|
|
105
|
+
return this.request('DELETE', routes.nodeJob(this.nodePath(id), this.jobPath(jobId)));
|
|
106
|
+
}
|
|
100
107
|
sendMessage(id, req) {
|
|
101
108
|
return this.request('POST', routes.nodeMessages(this.nodePath(id)), req);
|
|
102
109
|
}
|
|
@@ -302,7 +309,7 @@ export class CrtrClient {
|
|
|
302
309
|
createHumanBridge(req) {
|
|
303
310
|
return this.request('POST', routes.humanBridge(), req);
|
|
304
311
|
}
|
|
305
|
-
/** Resolve a
|
|
312
|
+
/** Resolve a page ticket answer. crtrd claims the ticket (taking over a live
|
|
306
313
|
* inbox claim where one exists), publishes the canonical result, delivers
|
|
307
314
|
* to the asking node, and retires the bridge before this resolves. */
|
|
308
315
|
resolveHumanTicket(nodeId, body) {
|
|
@@ -365,22 +372,61 @@ export class CrtrClient {
|
|
|
365
372
|
return this.request('POST', routes.humanCommentDelete(this.commentPath(commentId)), req);
|
|
366
373
|
}
|
|
367
374
|
// ---- Humanloop inbox (Northlight crouter-inbox v1, inbox-contract.md §A) --
|
|
368
|
-
/** Pending
|
|
375
|
+
/** Pending page/review tickets across every available crouter-owned
|
|
369
376
|
* humanloop root. */
|
|
370
377
|
listHumanInbox() {
|
|
371
378
|
return this.request('GET', routes.humanInbox());
|
|
372
379
|
}
|
|
373
|
-
/** Read one
|
|
374
|
-
|
|
375
|
-
getHumanInboxDeck(ticketId) {
|
|
380
|
+
/** Read one page ticket by its opaque ticket id (pending, resolved, or canceled). */
|
|
381
|
+
getHumanInboxPage(ticketId) {
|
|
376
382
|
return this.request('GET', routes.humanInboxTicket(this.ticketId(ticketId)));
|
|
377
383
|
}
|
|
378
|
-
/**
|
|
379
|
-
|
|
380
|
-
|
|
384
|
+
/** All page tickets raised by one node, oldest first (pending, resolved, or canceled). */
|
|
385
|
+
getInboxHistory(nodeId) {
|
|
386
|
+
if (typeof nodeId !== 'string' || nodeId === '') {
|
|
387
|
+
throw new TypeError(`invalid node id: ${JSON.stringify(nodeId)}`);
|
|
388
|
+
}
|
|
389
|
+
return this.request('GET', withQuery(routes.humanInbox() + '/history', { node_id: nodeId }));
|
|
390
|
+
}
|
|
391
|
+
/** Submit responses for a page ticket. Single-assignment server-side: a competing
|
|
392
|
+
* resolution races to `ticket_already_resolved`. */
|
|
393
|
+
respondHumanInboxPage(ticketId, request) {
|
|
381
394
|
return this.request('POST', routes.humanInboxRespond(this.ticketId(ticketId)), request);
|
|
382
395
|
}
|
|
383
|
-
/**
|
|
396
|
+
/** Autosave partial page work. Omitted slots are permitted in progress updates. */
|
|
397
|
+
postInboxProgress(ticketId, responses) {
|
|
398
|
+
return this.request('POST', routes.humanInboxProgress(this.ticketId(ticketId)), { responses });
|
|
399
|
+
}
|
|
400
|
+
/** Get the published response for a resolved page ticket. 404 if pending or canceled. */
|
|
401
|
+
getInboxResponse(ticketId) {
|
|
402
|
+
return this.request('GET', routes.humanInboxResponse(this.ticketId(ticketId)));
|
|
403
|
+
}
|
|
404
|
+
/** The page's body-level HTML payload, for pending, resolved, and canceled
|
|
405
|
+
* tickets alike. The host wraps it and injects the page runtime bundle. */
|
|
406
|
+
renderHumanInboxPage(ticketId) {
|
|
407
|
+
return this.request('GET', routes.humanInboxRender(this.ticketId(ticketId)));
|
|
408
|
+
}
|
|
409
|
+
/** One built page runtime asset. Pass the previously returned `etag` as
|
|
410
|
+
* `ifNoneMatch` to get a 304 (and no bytes) while the cached copy is
|
|
411
|
+
* current. The one non-JSON route on this surface. */
|
|
412
|
+
async getPagesBundleAsset(asset, ifNoneMatch) {
|
|
413
|
+
if (asset !== 'js' && asset !== 'css') {
|
|
414
|
+
throw new TypeError(`invalid page bundle asset: ${JSON.stringify(asset)}`);
|
|
415
|
+
}
|
|
416
|
+
const res = await this.send('GET', routes.humanPagesBundle(asset), undefined, ifNoneMatch === undefined ? undefined : { 'if-none-match': ifNoneMatch });
|
|
417
|
+
if (res.status === 304) {
|
|
418
|
+
return { status: 304, etag: res.headers['etag'] ?? '', content_type: res.headers['content-type'] ?? '', content: null };
|
|
419
|
+
}
|
|
420
|
+
if (res.status < 200 || res.status >= 300)
|
|
421
|
+
parse(res); // throws the mapped ApiError
|
|
422
|
+
return {
|
|
423
|
+
status: 200,
|
|
424
|
+
etag: res.headers['etag'] ?? '',
|
|
425
|
+
content_type: res.headers['content-type'] ?? '',
|
|
426
|
+
content: res.text,
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
/** Cancel a ticket (terminal response, never deletion). */
|
|
384
430
|
cancelHumanInboxTicket(ticketId, request) {
|
|
385
431
|
return this.request('POST', routes.humanInboxCancel(this.ticketId(ticketId)), request ?? {});
|
|
386
432
|
}
|
|
@@ -441,23 +487,23 @@ export class CrtrClient {
|
|
|
441
487
|
/** Raw request for routes not yet method-wrapped. Applies the same
|
|
442
488
|
* autostart + error-mapping semantics. */
|
|
443
489
|
async request(method, path, body) {
|
|
444
|
-
|
|
490
|
+
return parse(await this.send(method, path, body));
|
|
491
|
+
}
|
|
492
|
+
/** The unparsed request: autostart + handover recovery, no JSON parse. Every
|
|
493
|
+
* wrapper goes through here; only the non-JSON routes call it directly. */
|
|
494
|
+
async send(method, path, body, extraHeaders) {
|
|
445
495
|
try {
|
|
446
|
-
|
|
496
|
+
return await this.transport(method, path, body, extraHeaders);
|
|
447
497
|
}
|
|
448
498
|
catch (err) {
|
|
449
499
|
if (this.isColdSocketError(err)) {
|
|
450
500
|
await this.handleColdSocket();
|
|
451
|
-
|
|
452
|
-
}
|
|
453
|
-
else if (this.isHandoverHangup(err)) {
|
|
454
|
-
res = await this.rideOutHandover(method, path, body);
|
|
455
|
-
}
|
|
456
|
-
else {
|
|
457
|
-
throw toTransportApiError(err);
|
|
501
|
+
return await this.transport(method, path, body, extraHeaders);
|
|
458
502
|
}
|
|
503
|
+
if (this.isHandoverHangup(err))
|
|
504
|
+
return await this.rideOutHandover(method, path, body, extraHeaders);
|
|
505
|
+
throw toTransportApiError(err);
|
|
459
506
|
}
|
|
460
|
-
return parse(res);
|
|
461
507
|
}
|
|
462
508
|
// ---- internals ---------------------------------------------------------
|
|
463
509
|
nodePath(id) {
|
|
@@ -466,6 +512,14 @@ export class CrtrClient {
|
|
|
466
512
|
}
|
|
467
513
|
return id;
|
|
468
514
|
}
|
|
515
|
+
/** Validate a background job id before route construction. Job ids arrive
|
|
516
|
+
* from the daemon's file-backed roster and must remain one path segment. */
|
|
517
|
+
jobPath(id) {
|
|
518
|
+
if (!isSafeBashJobId(id)) {
|
|
519
|
+
throw new ApiError(400, 'invalid_job_id', `invalid background job id: ${JSON.stringify(id)}`);
|
|
520
|
+
}
|
|
521
|
+
return id;
|
|
522
|
+
}
|
|
469
523
|
/** Validate a cron id before route construction — `routes.ts` interpolates
|
|
470
524
|
* it raw, so a value carrying `/`, whitespace or `?` would corrupt the
|
|
471
525
|
* request line rather than 404 cleanly. Mirrors `nodePath`. */
|
|
@@ -506,11 +560,11 @@ export class CrtrClient {
|
|
|
506
560
|
}
|
|
507
561
|
return id;
|
|
508
562
|
}
|
|
509
|
-
transport(method, path, body) {
|
|
563
|
+
transport(method, path, body, extraHeaders) {
|
|
510
564
|
const usingHttps = this.baseUrl?.protocol === 'https:';
|
|
511
565
|
const doRequest = usingHttps ? httpsRequest : httpRequest;
|
|
512
566
|
const payload = body === undefined ? undefined : JSON.stringify(body);
|
|
513
|
-
const headers = { accept: 'application/json', ...this.headers };
|
|
567
|
+
const headers = { accept: 'application/json', ...this.headers, ...extraHeaders };
|
|
514
568
|
if (payload !== undefined) {
|
|
515
569
|
headers['content-type'] = 'application/json';
|
|
516
570
|
headers['content-length'] = String(Buffer.byteLength(payload));
|
|
@@ -535,7 +589,12 @@ export class CrtrClient {
|
|
|
535
589
|
const chunks = [];
|
|
536
590
|
res.on('data', (chunk) => chunks.push(chunk));
|
|
537
591
|
res.on('end', () => {
|
|
538
|
-
|
|
592
|
+
const received = {};
|
|
593
|
+
for (const [name, value] of Object.entries(res.headers)) {
|
|
594
|
+
if (value !== undefined)
|
|
595
|
+
received[name] = Array.isArray(value) ? value.join(', ') : value;
|
|
596
|
+
}
|
|
597
|
+
resolve({ status: res.statusCode ?? 0, text: Buffer.concat(chunks).toString('utf8'), headers: received });
|
|
539
598
|
});
|
|
540
599
|
res.on('error', reject);
|
|
541
600
|
});
|
|
@@ -572,7 +631,7 @@ export class CrtrClient {
|
|
|
572
631
|
* before the socket dropped, so it fails with `daemon_restarting` (retry),
|
|
573
632
|
* never `daemon_unavailable` ("start the daemon" is the wrong advice for a
|
|
574
633
|
* daemon that is mid-handover). */
|
|
575
|
-
async rideOutHandover(method, path, body) {
|
|
634
|
+
async rideOutHandover(method, path, body, extraHeaders) {
|
|
576
635
|
if (!(await this.awaitHandover())) {
|
|
577
636
|
throw new ApiError(503, 'daemon_unavailable', `crtrd went away mid-request and did not come back within ${this.coldStartPollWindowMs}ms.`);
|
|
578
637
|
}
|
|
@@ -580,7 +639,7 @@ export class CrtrClient {
|
|
|
580
639
|
throw new ApiError(503, 'daemon_restarting', `crtrd handed over to a new runtime generation mid-request; this ${method} may or may not have been applied.`);
|
|
581
640
|
}
|
|
582
641
|
try {
|
|
583
|
-
return await this.transport(method, path, body);
|
|
642
|
+
return await this.transport(method, path, body, extraHeaders);
|
|
584
643
|
}
|
|
585
644
|
catch (err) {
|
|
586
645
|
throw toTransportApiError(err);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { IsoTime, NodeIdDTO } from './common.js';
|
|
2
|
+
/** A still-live background bash job owned by one node. */
|
|
3
|
+
export interface BashJobStatusDTO {
|
|
4
|
+
job_id: string;
|
|
5
|
+
command: string;
|
|
6
|
+
/** Human-readable label supplied with the bash call, or null when absent. */
|
|
7
|
+
purpose: string | null;
|
|
8
|
+
started_at: IsoTime;
|
|
9
|
+
elapsed_ms: number;
|
|
10
|
+
/** Persisted supervisor process group, or null for legacy jobs. */
|
|
11
|
+
pgid: number | null;
|
|
12
|
+
/** Whether the persisted process group currently exists. */
|
|
13
|
+
pgid_alive: boolean;
|
|
14
|
+
}
|
|
15
|
+
/** Result of stopping one background bash job. `signaled` is false when the
|
|
16
|
+
* process group had already exited, but the job is still retired via its exit
|
|
17
|
+
* sentinel. */
|
|
18
|
+
export interface BashJobStopResultDTO {
|
|
19
|
+
node_id: NodeIdDTO;
|
|
20
|
+
job_id: string;
|
|
21
|
+
signaled: boolean;
|
|
22
|
+
}
|
|
23
|
+
/** Whether a job id is safe to interpolate as one API/filesystem segment. */
|
|
24
|
+
export declare function isSafeBashJobId(jobId: string): boolean;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Background bash job API shapes. The daemon projects the file-backed job
|
|
2
|
+
// control plane into these dependency-light DTOs for remote presenters.
|
|
3
|
+
/** Whether a job id is safe to interpolate as one API/filesystem segment. */
|
|
4
|
+
export function isSafeBashJobId(jobId) {
|
|
5
|
+
return typeof jobId === 'string'
|
|
6
|
+
&& jobId !== '' && jobId !== '.' && jobId !== '..'
|
|
7
|
+
&& /^[A-Za-z0-9._~-]+$/u.test(jobId)
|
|
8
|
+
&& Buffer.byteLength(jobId, 'utf8') <= 128;
|
|
9
|
+
}
|
package/dist/api/dto/human.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { NodeIdDTO } from './common.js';
|
|
2
|
-
import type { InteractionResponseDTO } from './inbox.js';
|
|
3
2
|
/** One anchored review comment, mirroring crouter's ticket-store shape as a
|
|
4
3
|
* plain structural wire type (no store import). */
|
|
5
4
|
export interface FeedbackCommentDTO {
|
|
@@ -37,9 +36,9 @@ export interface FeedbackResultDTO {
|
|
|
37
36
|
commentsTotal: number;
|
|
38
37
|
commentsUnresolved: number;
|
|
39
38
|
}
|
|
40
|
-
/** `POST /v1/human/tickets/{node_id}/resolve` body — a
|
|
39
|
+
/** `POST /v1/human/tickets/{node_id}/resolve` body — a page answer keyed by slot id. */
|
|
41
40
|
export interface HumanResolveRequest {
|
|
42
|
-
responses:
|
|
41
|
+
responses: Record<string, Record<string, unknown>>;
|
|
43
42
|
}
|
|
44
43
|
/** `POST /v1/human/tickets/{node_id}/cancel` body. */
|
|
45
44
|
export interface HumanCancelRequest {
|
|
@@ -49,7 +48,7 @@ export interface HumanCancelRequest {
|
|
|
49
48
|
/** `POST /v1/human/tickets/{node_id}/resolve` result. */
|
|
50
49
|
export interface HumanResolveResultDTO {
|
|
51
50
|
delivered: true;
|
|
52
|
-
kind: '
|
|
51
|
+
kind: 'page';
|
|
53
52
|
}
|
|
54
53
|
/** `POST /v1/human/tickets/{node_id}/cancel` result. */
|
|
55
54
|
export interface HumanCancelResultDTO {
|
package/dist/api/dto/inbox.d.ts
CHANGED
|
@@ -1,100 +1,141 @@
|
|
|
1
1
|
import type { IsoTime } from './common.js';
|
|
2
|
-
/** Opaque, stable, URL-safe ticket id: lowercase SHA-256 hex
|
|
3
|
-
* `canonicalRoot + "\0" + ticketBasename`. Clients must treat it as opaque —
|
|
4
|
-
* it discloses no home filesystem path. */
|
|
2
|
+
/** Opaque, stable, URL-safe ticket id: lowercase SHA-256 hex. */
|
|
5
3
|
export type InboxTicketIdDTO = string;
|
|
6
|
-
export
|
|
7
|
-
export interface DeckSourceDTO {
|
|
4
|
+
export interface TicketSourceDTO {
|
|
8
5
|
sessionName?: string;
|
|
9
6
|
askedBy?: string;
|
|
10
7
|
blockedSince?: IsoTime;
|
|
11
8
|
nodeId?: string;
|
|
12
9
|
}
|
|
13
|
-
export interface
|
|
10
|
+
export interface ReviewTicketSummaryDTO {
|
|
14
11
|
ticket_id: InboxTicketIdDTO;
|
|
15
|
-
kind: '
|
|
12
|
+
kind: 'review';
|
|
16
13
|
title: string;
|
|
17
14
|
subtitle: string;
|
|
18
15
|
blocked_since: IsoTime;
|
|
19
|
-
source:
|
|
20
|
-
interaction_kind?: InteractionKindDTO;
|
|
16
|
+
source: TicketSourceDTO;
|
|
21
17
|
}
|
|
22
|
-
export interface
|
|
18
|
+
export interface PageTicketSummaryDTO {
|
|
23
19
|
ticket_id: InboxTicketIdDTO;
|
|
24
|
-
kind: '
|
|
20
|
+
kind: 'page';
|
|
25
21
|
title: string;
|
|
26
|
-
subtitle
|
|
22
|
+
subtitle?: string;
|
|
23
|
+
placement: 'inline' | 'panel';
|
|
24
|
+
dialect: 'md' | 'html';
|
|
25
|
+
steps: number;
|
|
27
26
|
blocked_since: IsoTime;
|
|
28
|
-
source:
|
|
27
|
+
source: TicketSourceDTO;
|
|
28
|
+
slot_kinds: string[];
|
|
29
|
+
awaits_response: boolean;
|
|
30
|
+
state?: 'pending' | 'resolved' | 'canceled';
|
|
29
31
|
}
|
|
30
|
-
export type InboxTicketSummaryDTO =
|
|
31
|
-
/** `GET /v1/human/inbox` result. `tickets` is sorted newest `blocked_since`
|
|
32
|
-
* first, then `ticket_id` ascending. */
|
|
32
|
+
export type InboxTicketSummaryDTO = PageTicketSummaryDTO | ReviewTicketSummaryDTO;
|
|
33
33
|
export interface InboxListDTO {
|
|
34
34
|
tickets: InboxTicketSummaryDTO[];
|
|
35
35
|
}
|
|
36
|
-
export interface
|
|
36
|
+
export interface PageSlotDTO {
|
|
37
|
+
id?: string;
|
|
38
|
+
kind: string;
|
|
39
|
+
step: number;
|
|
40
|
+
config: Record<string, unknown>;
|
|
41
|
+
unvalidated?: true;
|
|
42
|
+
}
|
|
43
|
+
/** The nested protocol object is deliberately camelCase on the wire. */
|
|
44
|
+
export interface PageManifestDTO {
|
|
45
|
+
schema: 'crtr.page/v1';
|
|
46
|
+
dialect: 'md' | 'html';
|
|
47
|
+
title: string;
|
|
48
|
+
subtitle?: string;
|
|
49
|
+
placement: 'inline' | 'panel';
|
|
50
|
+
document: 'page.md' | 'page.html';
|
|
51
|
+
steps: number;
|
|
52
|
+
slots: PageSlotDTO[];
|
|
53
|
+
source?: TicketSourceDTO;
|
|
54
|
+
}
|
|
55
|
+
export interface PageCommentAnchorDTO {
|
|
56
|
+
kind: 'whole' | 'option' | 'row' | 'column' | 'card' | 'range';
|
|
57
|
+
optionId?: string;
|
|
58
|
+
rowId?: string;
|
|
59
|
+
columnId?: string;
|
|
60
|
+
cardId?: string;
|
|
61
|
+
start?: number;
|
|
62
|
+
end?: number;
|
|
63
|
+
quote?: string;
|
|
64
|
+
}
|
|
65
|
+
export interface PageCommentDTO {
|
|
37
66
|
id: string;
|
|
38
|
-
|
|
39
|
-
|
|
67
|
+
anchor: PageCommentAnchorDTO;
|
|
68
|
+
text: string;
|
|
40
69
|
}
|
|
41
|
-
export interface
|
|
42
|
-
|
|
43
|
-
|
|
70
|
+
export interface PageOptionsResponseDTO {
|
|
71
|
+
selectedOptionIds: string[];
|
|
72
|
+
comments: PageCommentDTO[];
|
|
44
73
|
freetext?: string;
|
|
45
|
-
label?: string;
|
|
46
74
|
}
|
|
47
|
-
export interface
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
freetextLabel?: string;
|
|
57
|
-
kind?: InteractionKindDTO;
|
|
58
|
-
preAnswered?: InteractionPreAnswerDTO;
|
|
59
|
-
}
|
|
60
|
-
export interface DeckDTO {
|
|
61
|
-
title: string;
|
|
62
|
-
source?: DeckSourceDTO;
|
|
63
|
-
interactions: InteractionDTO[];
|
|
75
|
+
export interface PageTextResponseDTO {
|
|
76
|
+
text: string;
|
|
77
|
+
edited: boolean;
|
|
78
|
+
comments: PageCommentDTO[];
|
|
79
|
+
}
|
|
80
|
+
export interface PageTableResponseDTO {
|
|
81
|
+
selectedRowIds: string[];
|
|
82
|
+
selectedColumnIds: string[];
|
|
83
|
+
comments: PageCommentDTO[];
|
|
64
84
|
}
|
|
65
|
-
|
|
66
|
-
|
|
85
|
+
export interface PageCardsResponseDTO {
|
|
86
|
+
selectedCardIds: string[];
|
|
87
|
+
comments: PageCommentDTO[];
|
|
88
|
+
}
|
|
89
|
+
export type SlotResponseDTO = PageOptionsResponseDTO | PageTextResponseDTO | PageTableResponseDTO | PageCardsResponseDTO | Record<string, unknown>;
|
|
90
|
+
export type PageResponsesDTO = Record<string, SlotResponseDTO>;
|
|
91
|
+
export interface InboxPageDTO {
|
|
67
92
|
ticket_id: InboxTicketIdDTO;
|
|
68
|
-
kind: '
|
|
69
|
-
|
|
93
|
+
kind: 'page';
|
|
94
|
+
state: 'pending' | 'resolved' | 'canceled';
|
|
95
|
+
page: PageManifestDTO;
|
|
96
|
+
document: string;
|
|
97
|
+
document_media_type: 'text/markdown' | 'text/html';
|
|
98
|
+
progress: {
|
|
99
|
+
responses: PageResponsesDTO;
|
|
100
|
+
} | null;
|
|
70
101
|
}
|
|
71
|
-
export interface
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
*
|
|
84
|
-
export interface
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
102
|
+
export interface InboxPageHistoryDTO {
|
|
103
|
+
tickets: PageTicketSummaryDTO[];
|
|
104
|
+
}
|
|
105
|
+
/** `GET /v1/human/inbox/:ticket_id/render` — the page's body-level HTML
|
|
106
|
+
* payload. It carries the page's own prose and `crtr-*` slot elements only;
|
|
107
|
+
* the host supplies the surrounding document and injects the page runtime
|
|
108
|
+
* bundle it chooses to serve. */
|
|
109
|
+
export interface InboxPageRenderDTO {
|
|
110
|
+
html: string;
|
|
111
|
+
}
|
|
112
|
+
/** A client-side projection of `GET /v1/human/pages/bundle.{js,css}` — the one
|
|
113
|
+
* non-JSON route on this surface. `content` is null on a 304, where the
|
|
114
|
+
* caller's cached copy is still current. */
|
|
115
|
+
export interface PagesBundleAssetDTO {
|
|
116
|
+
status: 200 | 304;
|
|
117
|
+
etag: string;
|
|
118
|
+
content_type: string;
|
|
119
|
+
content: string | null;
|
|
120
|
+
}
|
|
121
|
+
export interface RespondInboxPageRequest {
|
|
122
|
+
responses: PageResponsesDTO;
|
|
123
|
+
}
|
|
124
|
+
export interface InboxPageProgressDTO {
|
|
125
|
+
responses: PageResponsesDTO;
|
|
126
|
+
}
|
|
127
|
+
export interface PageTicketResultDTO {
|
|
128
|
+
schema: 'crtr.page-response/v1';
|
|
129
|
+
kind: 'page';
|
|
130
|
+
responses: PageResponsesDTO;
|
|
88
131
|
summary: string;
|
|
89
132
|
completedAt: IsoTime;
|
|
90
133
|
}
|
|
91
|
-
/** `POST /v1/human/inbox/:ticket_id/cancel` body.
|
|
92
|
-
* be nonempty after trim and at most 1000 characters. */
|
|
134
|
+
/** `POST /v1/human/inbox/:ticket_id/cancel` body. */
|
|
93
135
|
export interface CancelInboxTicketRequest {
|
|
94
136
|
reason?: string;
|
|
95
137
|
}
|
|
96
|
-
/** `POST /v1/human/inbox/:ticket_id/cancel` result
|
|
97
|
-
* `humanloop.cancel/v1` result, unchanged. `actor` is always `"human"`. */
|
|
138
|
+
/** `POST /v1/human/inbox/:ticket_id/cancel` result. */
|
|
98
139
|
export interface CanceledTicketResultDTO {
|
|
99
140
|
schema: 'humanloop.cancel/v1';
|
|
100
141
|
kind: 'canceled';
|
package/dist/api/dto/inbox.js
CHANGED
|
@@ -1,10 +1,4 @@
|
|
|
1
1
|
// Humanloop inbox DTOs — crtrd `/v1/human/inbox` (Northlight crouter-inbox v1,
|
|
2
|
-
// inbox-contract.md §A).
|
|
3
|
-
//
|
|
4
|
-
// canonical camelCase field names so they cross the wire without translation
|
|
5
|
-
// or loss. Optional fields are omitted when absent, never serialized as
|
|
6
|
-
// `null`. `bodyPath` is deliberately impossible on this wire — crtrd resolves
|
|
7
|
-
// it server-side via humanloop's `parseDeck` and returns inline `body`.
|
|
8
|
-
//
|
|
9
|
-
// PURITY (spec §3.1): Node built-ins + `src/api/*` only.
|
|
2
|
+
// inbox-contract.md §A). API envelope fields use snake_case; nested page
|
|
3
|
+
// protocol objects retain their canonical camelCase field names.
|
|
10
4
|
export {};
|
package/dist/api/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export { API_VERSION, routes } from './routes.js';
|
|
|
6
6
|
export * from './dto/common.js';
|
|
7
7
|
export * from './dto/health.js';
|
|
8
8
|
export * from './dto/nodes.js';
|
|
9
|
+
export * from './dto/bash-jobs.js';
|
|
9
10
|
export * from './dto/messages.js';
|
|
10
11
|
export * from './dto/reports.js';
|
|
11
12
|
export * from './dto/lifecycle.js';
|
package/dist/api/index.js
CHANGED
|
@@ -7,6 +7,7 @@ export { API_VERSION, routes } from './routes.js';
|
|
|
7
7
|
export * from './dto/common.js';
|
|
8
8
|
export * from './dto/health.js';
|
|
9
9
|
export * from './dto/nodes.js';
|
|
10
|
+
export * from './dto/bash-jobs.js';
|
|
10
11
|
export * from './dto/messages.js';
|
|
11
12
|
export * from './dto/reports.js';
|
|
12
13
|
export * from './dto/lifecycle.js';
|
package/dist/api/routes.d.ts
CHANGED
|
@@ -14,6 +14,8 @@ export declare const routes: {
|
|
|
14
14
|
readonly nodeContext: (id: string) => string;
|
|
15
15
|
readonly nodeArtifacts: (id: string) => string;
|
|
16
16
|
readonly nodeReports: (id: string) => string;
|
|
17
|
+
readonly nodeJobs: (id: string) => string;
|
|
18
|
+
readonly nodeJob: (id: string, jobId: string) => string;
|
|
17
19
|
readonly nodeMessages: (id: string) => string;
|
|
18
20
|
readonly nodeInterrupt: (id: string) => string;
|
|
19
21
|
readonly nodeFork: (id: string) => string;
|
|
@@ -76,7 +78,12 @@ export declare const routes: {
|
|
|
76
78
|
readonly humanInbox: () => string;
|
|
77
79
|
readonly humanInboxTicket: (ticketId: string) => string;
|
|
78
80
|
readonly humanInboxRespond: (ticketId: string) => string;
|
|
81
|
+
readonly humanInboxProgress: (ticketId: string) => string;
|
|
82
|
+
readonly humanInboxResponse: (ticketId: string) => string;
|
|
79
83
|
readonly humanInboxCancel: (ticketId: string) => string;
|
|
84
|
+
readonly humanInboxRender: (ticketId: string) => string;
|
|
85
|
+
/** The built page runtime assets, served for whatever host renders a page. */
|
|
86
|
+
readonly humanPagesBundle: (asset: "js" | "css") => string;
|
|
80
87
|
readonly profiles: () => string;
|
|
81
88
|
readonly profile: (name: string) => string;
|
|
82
89
|
readonly modelAuths: () => string;
|
package/dist/api/routes.js
CHANGED
|
@@ -30,6 +30,8 @@ export const routes = {
|
|
|
30
30
|
nodeContext: (id) => `${V}/nodes/${id}/context`,
|
|
31
31
|
nodeArtifacts: (id) => `${V}/nodes/${id}/artifacts`,
|
|
32
32
|
nodeReports: (id) => `${V}/nodes/${id}/reports`,
|
|
33
|
+
nodeJobs: (id) => `${V}/nodes/${id}/jobs`,
|
|
34
|
+
nodeJob: (id, jobId) => `${V}/nodes/${id}/jobs/${jobId}`,
|
|
33
35
|
// Node messages / feed
|
|
34
36
|
nodeMessages: (id) => `${V}/nodes/${id}/messages`,
|
|
35
37
|
nodeInterrupt: (id) => `${V}/nodes/${id}/interrupt`,
|
|
@@ -103,7 +105,12 @@ export const routes = {
|
|
|
103
105
|
humanInbox: () => `${V}/human/inbox`,
|
|
104
106
|
humanInboxTicket: (ticketId) => `${V}/human/inbox/${ticketId}`,
|
|
105
107
|
humanInboxRespond: (ticketId) => `${V}/human/inbox/${ticketId}/respond`,
|
|
108
|
+
humanInboxProgress: (ticketId) => `${V}/human/inbox/${ticketId}/progress`,
|
|
109
|
+
humanInboxResponse: (ticketId) => `${V}/human/inbox/${ticketId}/response`,
|
|
106
110
|
humanInboxCancel: (ticketId) => `${V}/human/inbox/${ticketId}/cancel`,
|
|
111
|
+
humanInboxRender: (ticketId) => `${V}/human/inbox/${ticketId}/render`,
|
|
112
|
+
/** The built page runtime assets, served for whatever host renders a page. */
|
|
113
|
+
humanPagesBundle: (asset) => `${V}/human/pages/bundle.${asset}`,
|
|
107
114
|
// Profiles (server-side for P2 Core; CLI profile verbs stay fs-local)
|
|
108
115
|
profiles: () => `${V}/profiles`,
|
|
109
116
|
profile: (name) => `${V}/profiles/${name}`,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@north-light/crouter-api",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.194",
|
|
4
4
|
"description": "Typed crtrd /v1 API contract — DTOs, route builders, the error contract, and the CrtrClient. Zero runtime dependencies.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/api/index.js",
|