@averyyy/pi-server 0.85.1-piclient.2 → 0.85.1-piclient.4
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/CHANGELOG.md +8 -0
- package/bin/pi-server.js +28 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +27 -1
- package/dist/cli.js.map +1 -1
- package/dist/error-codes.d.ts +1 -0
- package/dist/error-codes.d.ts.map +1 -1
- package/dist/error-codes.js +1 -0
- package/dist/error-codes.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +216 -35
- package/dist/server.js.map +1 -1
- package/dist/session-store.d.ts.map +1 -1
- package/dist/session-store.js +5 -1
- package/dist/session-store.js.map +1 -1
- package/package.json +3 -3
package/dist/server.js
CHANGED
|
@@ -15,6 +15,10 @@ const packageMetadata = createRequire(import.meta.url)("../package.json");
|
|
|
15
15
|
const PI_SERVER_VERSION = packageMetadata.version;
|
|
16
16
|
function createRequestModels(model, options) {
|
|
17
17
|
const models = createModels();
|
|
18
|
+
const requestStream = (requestModel, context, streamOptions) => streamSimple(requestModel, context, {
|
|
19
|
+
...streamOptions,
|
|
20
|
+
...(options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}),
|
|
21
|
+
});
|
|
18
22
|
models.setProvider(createProvider({
|
|
19
23
|
id: model.provider,
|
|
20
24
|
name: model.provider,
|
|
@@ -26,8 +30,8 @@ function createRequestModels(model, options) {
|
|
|
26
30
|
},
|
|
27
31
|
},
|
|
28
32
|
api: {
|
|
29
|
-
stream:
|
|
30
|
-
streamSimple:
|
|
33
|
+
stream: requestStream,
|
|
34
|
+
streamSimple: requestStream,
|
|
31
35
|
},
|
|
32
36
|
}));
|
|
33
37
|
return models;
|
|
@@ -91,27 +95,64 @@ function runKey(sessionId, runId) {
|
|
|
91
95
|
function getStreamRun(sessionId, runId) {
|
|
92
96
|
return streamRuns.get(runKey(sessionId, runId));
|
|
93
97
|
}
|
|
94
|
-
function
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
function createRunRecord(sessionId, runId, kind) {
|
|
99
|
+
let resolveSettled;
|
|
100
|
+
const settled = new Promise((resolve) => {
|
|
101
|
+
resolveSettled = resolve;
|
|
102
|
+
});
|
|
103
|
+
return {
|
|
100
104
|
sessionId,
|
|
101
105
|
runId,
|
|
106
|
+
kind,
|
|
102
107
|
status: "running",
|
|
103
|
-
createdAt: now,
|
|
104
|
-
updatedAt: now,
|
|
108
|
+
createdAt: Date.now(),
|
|
109
|
+
updatedAt: Date.now(),
|
|
110
|
+
cancelRequested: false,
|
|
111
|
+
controller: new AbortController(),
|
|
112
|
+
settled,
|
|
113
|
+
resolveSettled,
|
|
105
114
|
};
|
|
115
|
+
}
|
|
116
|
+
function settleRun(run) {
|
|
117
|
+
if (!run)
|
|
118
|
+
return;
|
|
119
|
+
const resolve = run.resolveSettled;
|
|
120
|
+
run.resolveSettled = undefined;
|
|
121
|
+
resolve?.();
|
|
122
|
+
}
|
|
123
|
+
function startStreamRun(sessionId, runId, kind = "stream") {
|
|
124
|
+
const existing = getStreamRun(sessionId, runId);
|
|
125
|
+
if (existing?.status === "completed" || existing?.status === "aborted")
|
|
126
|
+
return existing;
|
|
127
|
+
const run = existing ?? createRunRecord(sessionId, runId, kind);
|
|
106
128
|
if (existing) {
|
|
107
129
|
run.message = undefined;
|
|
108
130
|
run.errorMessage = undefined;
|
|
131
|
+
run.cancelRequested = false;
|
|
132
|
+
run.controller = new AbortController();
|
|
133
|
+
let resolveSettled;
|
|
134
|
+
run.settled = new Promise((resolve) => {
|
|
135
|
+
resolveSettled = resolve;
|
|
136
|
+
});
|
|
137
|
+
run.resolveSettled = resolveSettled;
|
|
109
138
|
}
|
|
139
|
+
run.kind = kind;
|
|
110
140
|
run.status = "running";
|
|
111
|
-
run.updatedAt = now;
|
|
141
|
+
run.updatedAt = Date.now();
|
|
112
142
|
streamRuns.set(runKey(sessionId, runId), run);
|
|
113
143
|
return run;
|
|
114
144
|
}
|
|
145
|
+
function streamRunResponseBody(run) {
|
|
146
|
+
return {
|
|
147
|
+
sessionId: run.sessionId,
|
|
148
|
+
runId: run.runId,
|
|
149
|
+
status: run.status,
|
|
150
|
+
createdAt: run.createdAt,
|
|
151
|
+
updatedAt: run.updatedAt,
|
|
152
|
+
...(run.message ? { message: run.message } : {}),
|
|
153
|
+
...(run.errorMessage ? { errorMessage: run.errorMessage } : {}),
|
|
154
|
+
};
|
|
155
|
+
}
|
|
115
156
|
function replayStreamRun(run) {
|
|
116
157
|
if (!run.message)
|
|
117
158
|
return [];
|
|
@@ -149,6 +190,13 @@ function replayStreamRun(run) {
|
|
|
149
190
|
function completeStreamRun(run, message) {
|
|
150
191
|
if (!run)
|
|
151
192
|
return;
|
|
193
|
+
if (run.cancelRequested) {
|
|
194
|
+
run.status = "aborted";
|
|
195
|
+
run.message = undefined;
|
|
196
|
+
run.errorMessage = "Stream run aborted";
|
|
197
|
+
run.updatedAt = Date.now();
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
152
200
|
run.status = "completed";
|
|
153
201
|
run.message = message;
|
|
154
202
|
run.errorMessage = undefined;
|
|
@@ -157,6 +205,12 @@ function completeStreamRun(run, message) {
|
|
|
157
205
|
function failStreamRun(run, errorMessage) {
|
|
158
206
|
if (!run)
|
|
159
207
|
return;
|
|
208
|
+
if (run.cancelRequested) {
|
|
209
|
+
run.status = "aborted";
|
|
210
|
+
run.errorMessage = "Stream run aborted";
|
|
211
|
+
run.updatedAt = Date.now();
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
160
214
|
run.status = "failed";
|
|
161
215
|
run.errorMessage = errorMessage;
|
|
162
216
|
run.updatedAt = Date.now();
|
|
@@ -377,17 +431,51 @@ function prepareSessionCompact(body) {
|
|
|
377
431
|
const options = body.options ?? {};
|
|
378
432
|
return {
|
|
379
433
|
session,
|
|
434
|
+
sessionId: session.sessionId,
|
|
435
|
+
revision: session.revision,
|
|
436
|
+
treeHash: session.treeHash,
|
|
437
|
+
leafId: session.leafId,
|
|
438
|
+
entryCount: session.entries.length,
|
|
380
439
|
preparation: preparationResult.value,
|
|
381
440
|
options,
|
|
382
441
|
};
|
|
383
442
|
}
|
|
384
|
-
|
|
385
|
-
|
|
443
|
+
function sessionCompactConflict(body, prepared, currentSession) {
|
|
444
|
+
return {
|
|
445
|
+
status: 409,
|
|
446
|
+
body: {
|
|
447
|
+
error: "Session changed while compaction was running",
|
|
448
|
+
code: PiServerErrorCode.SESSION_STATE_CONFLICT,
|
|
449
|
+
details: {
|
|
450
|
+
sessionId: body.sessionId,
|
|
451
|
+
expectedRevision: prepared.revision,
|
|
452
|
+
expectedTreeHash: prepared.treeHash,
|
|
453
|
+
expectedLeafId: prepared.leafId,
|
|
454
|
+
actualRevision: currentSession?.revision ?? null,
|
|
455
|
+
actualTreeHash: currentSession?.treeHash ?? null,
|
|
456
|
+
actualLeafId: currentSession?.leafId ?? null,
|
|
457
|
+
},
|
|
458
|
+
},
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
async function completeSessionCompact(config, body, prepared, run) {
|
|
462
|
+
const result = await compactLegacy(prepared.preparation, createRequestModels(body.model, prepared.options), body.model, body.customInstructions, run?.controller.signal, prepared.options.reasoning);
|
|
386
463
|
if (!result.ok) {
|
|
387
464
|
return { status: 500, body: { error: result.error.message, code: PiServerErrorCode.INTERNAL_ERROR } };
|
|
388
465
|
}
|
|
389
|
-
|
|
390
|
-
|
|
466
|
+
if (run?.cancelRequested || run?.controller.signal.aborted) {
|
|
467
|
+
return { status: 409, body: { error: "Compaction aborted", code: PiServerErrorCode.INVALID_REQUEST } };
|
|
468
|
+
}
|
|
469
|
+
const currentSession = getSession(prepared.sessionId);
|
|
470
|
+
if (!currentSession ||
|
|
471
|
+
currentSession !== prepared.session ||
|
|
472
|
+
currentSession.revision !== prepared.revision ||
|
|
473
|
+
currentSession.treeHash !== prepared.treeHash ||
|
|
474
|
+
currentSession.leafId !== prepared.leafId) {
|
|
475
|
+
return sessionCompactConflict(body, prepared, currentSession);
|
|
476
|
+
}
|
|
477
|
+
const baseTreeHash = prepared.treeHash;
|
|
478
|
+
const baseEntryCount = prepared.entryCount;
|
|
391
479
|
const compaction = result.value;
|
|
392
480
|
const { session: updatedSession, entry: compactionEntry } = appendCompactionEntry(body.sessionId, compaction);
|
|
393
481
|
persistSession(config, updatedSession);
|
|
@@ -427,7 +515,7 @@ function writeServerSentEvent(res, event, body) {
|
|
|
427
515
|
res.write(`event: ${event}\n`);
|
|
428
516
|
res.write(`data: ${JSON.stringify(body)}\n\n`);
|
|
429
517
|
}
|
|
430
|
-
async function handleSessionCompactStream(config, body, prepared, res) {
|
|
518
|
+
async function handleSessionCompactStream(config, body, prepared, res, run) {
|
|
431
519
|
res.writeHead(200, {
|
|
432
520
|
"Content-Type": "text/event-stream",
|
|
433
521
|
"Cache-Control": "no-cache",
|
|
@@ -436,25 +524,33 @@ async function handleSessionCompactStream(config, body, prepared, res) {
|
|
|
436
524
|
res.flushHeaders();
|
|
437
525
|
res.write(STREAM_HEARTBEAT);
|
|
438
526
|
const heartbeat = setInterval(() => {
|
|
527
|
+
touchStreamRun(run);
|
|
439
528
|
if (!res.writableEnded) {
|
|
440
529
|
res.write(STREAM_HEARTBEAT);
|
|
441
530
|
}
|
|
442
531
|
}, STREAM_HEARTBEAT_INTERVAL_MS);
|
|
443
532
|
heartbeat.unref();
|
|
444
533
|
try {
|
|
445
|
-
const result = await completeSessionCompact(config, body, prepared);
|
|
534
|
+
const result = await completeSessionCompact(config, body, prepared, run);
|
|
446
535
|
writeServerSentEvent(res, result.status >= 400 ? "error" : "result", result.body);
|
|
536
|
+
finishCompactRun(run, result);
|
|
447
537
|
}
|
|
448
538
|
catch (err) {
|
|
449
539
|
const message = err instanceof Error ? err.message : String(err);
|
|
540
|
+
if (run) {
|
|
541
|
+
run.status = run.cancelRequested ? "aborted" : "failed";
|
|
542
|
+
run.errorMessage = message;
|
|
543
|
+
run.updatedAt = Date.now();
|
|
544
|
+
}
|
|
450
545
|
writeServerSentEvent(res, "error", { error: message });
|
|
451
546
|
}
|
|
452
547
|
finally {
|
|
453
548
|
clearInterval(heartbeat);
|
|
549
|
+
settleRun(run);
|
|
454
550
|
res.end();
|
|
455
551
|
}
|
|
456
552
|
}
|
|
457
|
-
async function handleSessionCompactJsonStream(config, body, prepared, res) {
|
|
553
|
+
async function handleSessionCompactJsonStream(config, body, prepared, res, run) {
|
|
458
554
|
res.writeHead(200, {
|
|
459
555
|
"Content-Type": "application/json",
|
|
460
556
|
"Cache-Control": "no-cache",
|
|
@@ -463,35 +559,71 @@ async function handleSessionCompactJsonStream(config, body, prepared, res) {
|
|
|
463
559
|
res.flushHeaders();
|
|
464
560
|
res.write(JSON_HEARTBEAT);
|
|
465
561
|
const heartbeat = setInterval(() => {
|
|
562
|
+
touchStreamRun(run);
|
|
466
563
|
if (!res.writableEnded) {
|
|
467
564
|
res.write(JSON_HEARTBEAT);
|
|
468
565
|
}
|
|
469
566
|
}, STREAM_HEARTBEAT_INTERVAL_MS);
|
|
470
567
|
heartbeat.unref();
|
|
471
568
|
try {
|
|
472
|
-
const result = await completeSessionCompact(config, body, prepared);
|
|
569
|
+
const result = await completeSessionCompact(config, body, prepared, run);
|
|
473
570
|
res.write(JSON.stringify(result.body));
|
|
571
|
+
finishCompactRun(run, result);
|
|
474
572
|
}
|
|
475
573
|
catch (err) {
|
|
476
574
|
const message = err instanceof Error ? err.message : String(err);
|
|
575
|
+
if (run) {
|
|
576
|
+
run.status = run.cancelRequested ? "aborted" : "failed";
|
|
577
|
+
run.errorMessage = message;
|
|
578
|
+
run.updatedAt = Date.now();
|
|
579
|
+
}
|
|
477
580
|
res.write(JSON.stringify({ error: message }));
|
|
478
581
|
}
|
|
479
582
|
finally {
|
|
480
583
|
clearInterval(heartbeat);
|
|
584
|
+
settleRun(run);
|
|
481
585
|
res.end();
|
|
482
586
|
}
|
|
483
587
|
}
|
|
588
|
+
function finishCompactRun(run, result) {
|
|
589
|
+
if (!run)
|
|
590
|
+
return;
|
|
591
|
+
if (run.cancelRequested || run.controller.signal.aborted) {
|
|
592
|
+
run.status = "aborted";
|
|
593
|
+
run.errorMessage = "Compaction aborted";
|
|
594
|
+
}
|
|
595
|
+
else {
|
|
596
|
+
run.status = result.status >= 400 ? "failed" : "completed";
|
|
597
|
+
if (result.status >= 400 && "error" in result.body) {
|
|
598
|
+
run.errorMessage = result.body.error;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
run.updatedAt = Date.now();
|
|
602
|
+
}
|
|
484
603
|
async function handleSessionCompact(config, body, res) {
|
|
485
604
|
const prepared = prepareSessionCompact(body);
|
|
486
605
|
if ("status" in prepared) {
|
|
487
606
|
sendJson(res, prepared.status, prepared.body);
|
|
488
607
|
return;
|
|
489
608
|
}
|
|
609
|
+
let run;
|
|
610
|
+
if (body.runId) {
|
|
611
|
+
const existingRun = getStreamRun(body.sessionId, body.runId);
|
|
612
|
+
if (existingRun?.status === "aborted") {
|
|
613
|
+
sendJson(res, 409, { error: "Compaction run was aborted", code: PiServerErrorCode.INVALID_REQUEST });
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
if (existingRun?.status === "running") {
|
|
617
|
+
sendError(res, 409, "A run with this runId is already in progress", PiServerErrorCode.RUN_IN_PROGRESS);
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
run = startStreamRun(body.sessionId, body.runId, "compact");
|
|
621
|
+
}
|
|
490
622
|
if (body.streamResponse) {
|
|
491
|
-
await handleSessionCompactStream(config, body, prepared, res);
|
|
623
|
+
await handleSessionCompactStream(config, body, prepared, res, run);
|
|
492
624
|
return;
|
|
493
625
|
}
|
|
494
|
-
await handleSessionCompactJsonStream(config, body, prepared, res);
|
|
626
|
+
await handleSessionCompactJsonStream(config, body, prepared, res, run);
|
|
495
627
|
}
|
|
496
628
|
function handleSessionHistory(sessionId, from, entriesFrom, revision, baseTreeHash, res) {
|
|
497
629
|
const session = getSession(sessionId);
|
|
@@ -515,7 +647,35 @@ function handleSessionRun(sessionId, runId, res) {
|
|
|
515
647
|
sendError(res, 404, "run not found", PiServerErrorCode.RUN_NOT_FOUND);
|
|
516
648
|
return;
|
|
517
649
|
}
|
|
518
|
-
sendJson(res, 200, run);
|
|
650
|
+
sendJson(res, 200, streamRunResponseBody(run));
|
|
651
|
+
}
|
|
652
|
+
async function handleSessionRunAbort(sessionId, runId, res) {
|
|
653
|
+
if (!sessionId || !runId) {
|
|
654
|
+
sendError(res, 400, "sessionId and runId are required", PiServerErrorCode.REQUIRED_FIELD_MISSING);
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
let run = getStreamRun(sessionId, runId);
|
|
658
|
+
if (!run) {
|
|
659
|
+
run = createRunRecord(sessionId, runId, "stream");
|
|
660
|
+
run.status = "aborted";
|
|
661
|
+
run.cancelRequested = true;
|
|
662
|
+
run.errorMessage = "Stream run aborted before it started";
|
|
663
|
+
run.updatedAt = Date.now();
|
|
664
|
+
settleRun(run);
|
|
665
|
+
streamRuns.set(runKey(sessionId, runId), run);
|
|
666
|
+
sendJson(res, 200, streamRunResponseBody(run));
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
if (run.status !== "running") {
|
|
670
|
+
await run.settled;
|
|
671
|
+
sendJson(res, 200, streamRunResponseBody(run));
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
674
|
+
run.cancelRequested = true;
|
|
675
|
+
run.updatedAt = Date.now();
|
|
676
|
+
run.controller.abort();
|
|
677
|
+
await run.settled;
|
|
678
|
+
sendJson(res, 200, streamRunResponseBody(run));
|
|
519
679
|
}
|
|
520
680
|
export function buildStreamContext(session, body) {
|
|
521
681
|
const messages = body.contextOverlay ?? [...session.messages, ...(body.ephemeralMessages ?? [])];
|
|
@@ -555,7 +715,6 @@ function handleStream(config, body, res) {
|
|
|
555
715
|
const context = buildStreamContext(session, body);
|
|
556
716
|
const existingRun = body.runId ? getStreamRun(body.sessionId, body.runId) : undefined;
|
|
557
717
|
const resolvedModel = body.model;
|
|
558
|
-
const streamOptions = { ...(body.options ?? {}) };
|
|
559
718
|
if (existingRun?.status === "completed") {
|
|
560
719
|
res.writeHead(200, {
|
|
561
720
|
"Content-Type": "text/event-stream",
|
|
@@ -570,10 +729,19 @@ function handleStream(config, body, res) {
|
|
|
570
729
|
endStreamResponse(res);
|
|
571
730
|
return;
|
|
572
731
|
}
|
|
732
|
+
if (existingRun?.status === "aborted") {
|
|
733
|
+
sendError(res, 409, "The stream run was aborted", PiServerErrorCode.INVALID_REQUEST);
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
573
736
|
if (existingRun?.status === "running") {
|
|
574
737
|
sendError(res, 409, "A stream with this runId is already in progress", PiServerErrorCode.RUN_IN_PROGRESS);
|
|
575
738
|
return;
|
|
576
739
|
}
|
|
740
|
+
const run = body.runId ? startStreamRun(body.sessionId, body.runId) : undefined;
|
|
741
|
+
const streamOptions = {
|
|
742
|
+
...(body.options ?? {}),
|
|
743
|
+
...(run ? { signal: run.controller.signal } : {}),
|
|
744
|
+
};
|
|
577
745
|
res.writeHead(200, {
|
|
578
746
|
"Content-Type": "text/event-stream",
|
|
579
747
|
"Cache-Control": "no-cache",
|
|
@@ -581,7 +749,6 @@ function handleStream(config, body, res) {
|
|
|
581
749
|
});
|
|
582
750
|
res.flushHeaders();
|
|
583
751
|
res.write(STREAM_HEARTBEAT);
|
|
584
|
-
const run = body.runId ? startStreamRun(body.sessionId, body.runId) : undefined;
|
|
585
752
|
const heartbeat = setInterval(() => {
|
|
586
753
|
touchStreamRun(run);
|
|
587
754
|
if (!res.writableEnded && !res.destroyed) {
|
|
@@ -597,11 +764,12 @@ function handleStream(config, body, res) {
|
|
|
597
764
|
clearInterval(heartbeat);
|
|
598
765
|
const message = err instanceof Error ? err.message : String(err);
|
|
599
766
|
failStreamRun(run, message);
|
|
767
|
+
settleRun(run);
|
|
600
768
|
writeStreamError(res, message);
|
|
601
769
|
endStreamResponse(res);
|
|
602
770
|
return;
|
|
603
771
|
}
|
|
604
|
-
(async () => {
|
|
772
|
+
void (async () => {
|
|
605
773
|
try {
|
|
606
774
|
for await (const event of stream) {
|
|
607
775
|
const proxyEvent = toProxyEvent(event);
|
|
@@ -617,17 +785,20 @@ function handleStream(config, body, res) {
|
|
|
617
785
|
}
|
|
618
786
|
}
|
|
619
787
|
}
|
|
788
|
+
catch (err) {
|
|
789
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
790
|
+
failStreamRun(run, message);
|
|
791
|
+
writeStreamError(res, message);
|
|
792
|
+
}
|
|
620
793
|
finally {
|
|
621
794
|
clearInterval(heartbeat);
|
|
795
|
+
if (run?.status === "running") {
|
|
796
|
+
failStreamRun(run, run.cancelRequested ? "Stream run aborted" : "Stream ended before a terminal event");
|
|
797
|
+
}
|
|
798
|
+
settleRun(run);
|
|
799
|
+
endStreamResponse(res);
|
|
622
800
|
}
|
|
623
|
-
|
|
624
|
-
})().catch((err) => {
|
|
625
|
-
clearInterval(heartbeat);
|
|
626
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
627
|
-
failStreamRun(run, message);
|
|
628
|
-
writeStreamError(res, message);
|
|
629
|
-
endStreamResponse(res);
|
|
630
|
-
});
|
|
801
|
+
})();
|
|
631
802
|
}
|
|
632
803
|
async function handlePostRequest(config, pathname, body, res) {
|
|
633
804
|
if (pathname === "/api/receive") {
|
|
@@ -683,7 +854,7 @@ export function createPiServer(configOverride) {
|
|
|
683
854
|
const config = loadConfig(configOverride);
|
|
684
855
|
loadPersistedSessions(config.sessionStoreDir);
|
|
685
856
|
const server = createServer(async (req, res) => {
|
|
686
|
-
const url = new URL(req.url ?? "/",
|
|
857
|
+
const url = new URL(req.url ?? "/", "http://localhost");
|
|
687
858
|
if (req.method === "GET" && url.pathname === "/health") {
|
|
688
859
|
sendJson(res, 200, { status: "ok" });
|
|
689
860
|
return;
|
|
@@ -704,6 +875,12 @@ export function createPiServer(configOverride) {
|
|
|
704
875
|
handleSessionRun(decodeURIComponent(runMatch[1]), decodeURIComponent(runMatch[2]), res);
|
|
705
876
|
return;
|
|
706
877
|
}
|
|
878
|
+
const runAbortMatch = /^\/api\/session\/([^/]+)\/runs\/([^/]+)\/abort$/.exec(url.pathname);
|
|
879
|
+
if (req.method === "POST" && runAbortMatch) {
|
|
880
|
+
await readBody(req);
|
|
881
|
+
await handleSessionRunAbort(decodeURIComponent(runAbortMatch[1]), decodeURIComponent(runAbortMatch[2]), res);
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
707
884
|
if (req.method === "GET" && url.pathname.startsWith("/api/session/") && url.pathname.endsWith("/history")) {
|
|
708
885
|
const encodedSessionId = url.pathname.slice("/api/session/".length, -"/history".length);
|
|
709
886
|
const fromParam = url.searchParams.get("from");
|
|
@@ -835,6 +1012,7 @@ function toProxyEvent(event) {
|
|
|
835
1012
|
reason: event.reason,
|
|
836
1013
|
usage: event.message.usage,
|
|
837
1014
|
deferred: event.message.deferred,
|
|
1015
|
+
message: event.message,
|
|
838
1016
|
};
|
|
839
1017
|
case "error":
|
|
840
1018
|
return {
|
|
@@ -851,7 +1029,10 @@ export function startServer(configOverride) {
|
|
|
851
1029
|
const config = loadConfig(configOverride);
|
|
852
1030
|
const server = createPiServer(configOverride);
|
|
853
1031
|
server.listen(config.port, config.host, () => {
|
|
854
|
-
|
|
1032
|
+
const address = server.address();
|
|
1033
|
+
const port = typeof address === "object" && address !== null ? address.port : config.port;
|
|
1034
|
+
const host = config.host.includes(":") && !config.host.startsWith("[") ? `[${config.host}]` : config.host;
|
|
1035
|
+
console.log(`pi-server listening on ${host}:${port}`);
|
|
855
1036
|
});
|
|
856
1037
|
return server;
|
|
857
1038
|
}
|