@poncho-ai/cli 0.4.2 → 0.5.1
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/.turbo/turbo-build.log +6 -6
- package/CHANGELOG.md +30 -0
- package/dist/chunk-2AZ3Y6R2.js +4121 -0
- package/dist/chunk-2QSGKCWX.js +4194 -0
- package/dist/chunk-3273VMA7.js +4182 -0
- package/dist/chunk-3IQWS553.js +4178 -0
- package/dist/chunk-5JFBI2WN.js +4202 -0
- package/dist/chunk-6NN7D4YA.js +4179 -0
- package/dist/chunk-6REJ5J4T.js +4142 -0
- package/dist/chunk-BSW557BB.js +4058 -0
- package/dist/chunk-FFIQQ5RY.js +4172 -0
- package/dist/chunk-GJYE4S3D.js +4164 -0
- package/dist/chunk-HGZTVHBT.js +4089 -0
- package/dist/chunk-HNYADV2K.js +4164 -0
- package/dist/chunk-IE47LJ33.js +4166 -0
- package/dist/chunk-J65L5WSP.js +4187 -0
- package/dist/chunk-MFVXK3SX.js +4177 -0
- package/dist/chunk-N7ZAHMBR.js +4178 -0
- package/dist/chunk-RAN52NR2.js +4180 -0
- package/dist/chunk-RU5C6WL4.js +4186 -0
- package/dist/chunk-TYL4SGJE.js +4177 -0
- package/dist/chunk-VIX7Y2YC.js +4169 -0
- package/dist/chunk-WCIUVLV3.js +4171 -0
- package/dist/chunk-WXFCSFXF.js +4178 -0
- package/dist/chunk-YFNZJBPQ.js +4185 -0
- package/dist/chunk-ZBOX3JLJ.js +4197 -0
- package/dist/cli.js +1 -1
- package/dist/index.js +1 -1
- package/dist/run-interactive-ink-4EWW4AJ6.js +463 -0
- package/dist/run-interactive-ink-5YGICHDM.js +494 -0
- package/dist/run-interactive-ink-642LZIYZ.js +494 -0
- package/dist/run-interactive-ink-6NYRFZWP.js +494 -0
- package/dist/run-interactive-ink-6YNTYMPO.js +494 -0
- package/dist/run-interactive-ink-72H5IUTC.js +494 -0
- package/dist/run-interactive-ink-7DWI2HZB.js +494 -0
- package/dist/run-interactive-ink-7P4WWB2Y.js +494 -0
- package/dist/run-interactive-ink-C5NIVKAZ.js +494 -0
- package/dist/run-interactive-ink-EP7GIKLH.js +463 -0
- package/dist/run-interactive-ink-FASKW7SN.js +463 -0
- package/dist/run-interactive-ink-GO3OQ3BD.js +494 -0
- package/dist/run-interactive-ink-JTEKKDJW.js +494 -0
- package/dist/run-interactive-ink-LLNLDCES.js +494 -0
- package/dist/run-interactive-ink-MO6MGLEY.js +494 -0
- package/dist/run-interactive-ink-OFJCD2ZU.js +494 -0
- package/dist/run-interactive-ink-OQZN5DQE.js +463 -0
- package/dist/run-interactive-ink-QKB6CG3W.js +494 -0
- package/dist/run-interactive-ink-RJCA5IQA.js +494 -0
- package/dist/run-interactive-ink-RU2PH6R5.js +494 -0
- package/dist/run-interactive-ink-T36C6TJ2.js +463 -0
- package/dist/run-interactive-ink-XLNTYEIZ.js +494 -0
- package/dist/run-interactive-ink-YFY4HRAS.js +494 -0
- package/dist/run-interactive-ink-ZL6RAS2O.js +494 -0
- package/package.json +3 -2
- package/src/index.ts +80 -17
- package/src/init-onboarding.ts +4 -3
- package/src/run-interactive-ink.ts +45 -10
- package/src/web-ui.ts +235 -139
- package/test/cli.test.ts +115 -6
package/src/index.ts
CHANGED
|
@@ -830,11 +830,30 @@ export const createRequestHandler = async (options?: {
|
|
|
830
830
|
const conversationStore = createConversationStore(resolveStateConfig(config), { workingDir });
|
|
831
831
|
const sessionStore = new SessionStore();
|
|
832
832
|
const loginRateLimiter = new LoginRateLimiter();
|
|
833
|
-
|
|
833
|
+
|
|
834
|
+
// Unified authentication using PONCHO_AUTH_TOKEN for both Web UI and API
|
|
835
|
+
const authToken = process.env.PONCHO_AUTH_TOKEN ?? "";
|
|
836
|
+
const authRequired = config?.auth?.required ?? false;
|
|
837
|
+
const requireAuth = authRequired && authToken.length > 0;
|
|
838
|
+
|
|
834
839
|
const isProduction = resolveHarnessEnvironment() === "production";
|
|
835
|
-
const requireUiAuth = passphrase.length > 0;
|
|
836
840
|
const secureCookies = isProduction;
|
|
837
841
|
|
|
842
|
+
// Helper to extract and validate Bearer token from Authorization header
|
|
843
|
+
const validateBearerToken = (authHeader: string | string[] | undefined): boolean => {
|
|
844
|
+
if (!requireAuth || !authToken) {
|
|
845
|
+
return true; // No auth required
|
|
846
|
+
}
|
|
847
|
+
if (!authHeader || typeof authHeader !== "string") {
|
|
848
|
+
return false;
|
|
849
|
+
}
|
|
850
|
+
const match = authHeader.match(/^Bearer\s+(.+)$/i);
|
|
851
|
+
if (!match || !match[1]) {
|
|
852
|
+
return false;
|
|
853
|
+
}
|
|
854
|
+
return verifyPassphrase(match[1], authToken);
|
|
855
|
+
};
|
|
856
|
+
|
|
838
857
|
return async (request: IncomingMessage, response: ServerResponse) => {
|
|
839
858
|
if (!request.url || !request.method) {
|
|
840
859
|
writeJson(response, 404, { error: "Not found" });
|
|
@@ -888,7 +907,7 @@ export const createRequestHandler = async (options?: {
|
|
|
888
907
|
request.method !== "GET" && request.method !== "HEAD" && request.method !== "OPTIONS";
|
|
889
908
|
|
|
890
909
|
if (pathname === "/api/auth/session" && request.method === "GET") {
|
|
891
|
-
if (!
|
|
910
|
+
if (!requireAuth) {
|
|
892
911
|
writeJson(response, 200, { authenticated: true, csrfToken: "" });
|
|
893
912
|
return;
|
|
894
913
|
}
|
|
@@ -906,7 +925,7 @@ export const createRequestHandler = async (options?: {
|
|
|
906
925
|
}
|
|
907
926
|
|
|
908
927
|
if (pathname === "/api/auth/login" && request.method === "POST") {
|
|
909
|
-
if (!
|
|
928
|
+
if (!requireAuth) {
|
|
910
929
|
writeJson(response, 200, { authenticated: true, csrfToken: "" });
|
|
911
930
|
return;
|
|
912
931
|
}
|
|
@@ -922,7 +941,7 @@ export const createRequestHandler = async (options?: {
|
|
|
922
941
|
}
|
|
923
942
|
const body = (await readRequestBody(request)) as { passphrase?: string };
|
|
924
943
|
const provided = body.passphrase ?? "";
|
|
925
|
-
if (!verifyPassphrase(provided,
|
|
944
|
+
if (!verifyPassphrase(provided, authToken)) {
|
|
926
945
|
const failure = loginRateLimiter.registerFailure(ip);
|
|
927
946
|
writeJson(response, 401, {
|
|
928
947
|
code: "AUTH_ERROR",
|
|
@@ -963,15 +982,23 @@ export const createRequestHandler = async (options?: {
|
|
|
963
982
|
}
|
|
964
983
|
|
|
965
984
|
if (pathname.startsWith("/api/")) {
|
|
966
|
-
|
|
985
|
+
// Check authentication: either valid session (Web UI) or valid Bearer token (API)
|
|
986
|
+
const hasBearerToken = request.headers.authorization?.startsWith("Bearer ");
|
|
987
|
+
const isAuthenticated = !requireAuth || session || validateBearerToken(request.headers.authorization);
|
|
988
|
+
|
|
989
|
+
if (!isAuthenticated) {
|
|
967
990
|
writeJson(response, 401, {
|
|
968
991
|
code: "AUTH_ERROR",
|
|
969
992
|
message: "Authentication required",
|
|
970
993
|
});
|
|
971
994
|
return;
|
|
972
995
|
}
|
|
996
|
+
|
|
997
|
+
// CSRF validation only for session-based requests (not Bearer token requests)
|
|
973
998
|
if (
|
|
974
|
-
|
|
999
|
+
requireAuth &&
|
|
1000
|
+
session &&
|
|
1001
|
+
!hasBearerToken &&
|
|
975
1002
|
requiresCsrfValidation &&
|
|
976
1003
|
pathname !== "/api/auth/login" &&
|
|
977
1004
|
request.headers["x-csrf-token"] !== session?.csrfToken
|
|
@@ -1090,6 +1117,9 @@ export const createRequestHandler = async (options?: {
|
|
|
1090
1117
|
let latestRunId = conversation.runtimeRunId ?? "";
|
|
1091
1118
|
let assistantResponse = "";
|
|
1092
1119
|
const toolTimeline: string[] = [];
|
|
1120
|
+
const sections: Array<{ type: "text" | "tools"; content: string | string[] }> = [];
|
|
1121
|
+
let currentText = "";
|
|
1122
|
+
let currentTools: string[] = [];
|
|
1093
1123
|
try {
|
|
1094
1124
|
const recallCorpus = (await conversationStore.list(ownerId))
|
|
1095
1125
|
.filter((item) => item.conversationId !== conversationId)
|
|
@@ -1106,7 +1136,7 @@ export const createRequestHandler = async (options?: {
|
|
|
1106
1136
|
}))
|
|
1107
1137
|
.filter((item) => item.content.length > 0);
|
|
1108
1138
|
|
|
1109
|
-
for await (const event of harness.
|
|
1139
|
+
for await (const event of harness.runWithTelemetry({
|
|
1110
1140
|
task: messageText,
|
|
1111
1141
|
parameters: {
|
|
1112
1142
|
...(body.parameters ?? {}),
|
|
@@ -1119,25 +1149,48 @@ export const createRequestHandler = async (options?: {
|
|
|
1119
1149
|
latestRunId = event.runId;
|
|
1120
1150
|
}
|
|
1121
1151
|
if (event.type === "model:chunk") {
|
|
1152
|
+
// If we have tools accumulated and text starts again, push tools as a section
|
|
1153
|
+
if (currentTools.length > 0) {
|
|
1154
|
+
sections.push({ type: "tools", content: currentTools });
|
|
1155
|
+
currentTools = [];
|
|
1156
|
+
}
|
|
1122
1157
|
assistantResponse += event.content;
|
|
1158
|
+
currentText += event.content;
|
|
1123
1159
|
}
|
|
1124
1160
|
if (event.type === "tool:started") {
|
|
1125
|
-
|
|
1161
|
+
// If we have text accumulated, push it as a text section
|
|
1162
|
+
if (currentText.length > 0) {
|
|
1163
|
+
sections.push({ type: "text", content: currentText });
|
|
1164
|
+
currentText = "";
|
|
1165
|
+
}
|
|
1166
|
+
const toolText = `- start \`${event.tool}\``;
|
|
1167
|
+
toolTimeline.push(toolText);
|
|
1168
|
+
currentTools.push(toolText);
|
|
1126
1169
|
}
|
|
1127
1170
|
if (event.type === "tool:completed") {
|
|
1128
|
-
|
|
1171
|
+
const toolText = `- done \`${event.tool}\` (${event.duration}ms)`;
|
|
1172
|
+
toolTimeline.push(toolText);
|
|
1173
|
+
currentTools.push(toolText);
|
|
1129
1174
|
}
|
|
1130
1175
|
if (event.type === "tool:error") {
|
|
1131
|
-
|
|
1176
|
+
const toolText = `- error \`${event.tool}\`: ${event.error}`;
|
|
1177
|
+
toolTimeline.push(toolText);
|
|
1178
|
+
currentTools.push(toolText);
|
|
1132
1179
|
}
|
|
1133
1180
|
if (event.type === "tool:approval:required") {
|
|
1134
|
-
|
|
1181
|
+
const toolText = `- approval required \`${event.tool}\``;
|
|
1182
|
+
toolTimeline.push(toolText);
|
|
1183
|
+
currentTools.push(toolText);
|
|
1135
1184
|
}
|
|
1136
1185
|
if (event.type === "tool:approval:granted") {
|
|
1137
|
-
|
|
1186
|
+
const toolText = `- approval granted (${event.approvalId})`;
|
|
1187
|
+
toolTimeline.push(toolText);
|
|
1188
|
+
currentTools.push(toolText);
|
|
1138
1189
|
}
|
|
1139
1190
|
if (event.type === "tool:approval:denied") {
|
|
1140
|
-
|
|
1191
|
+
const toolText = `- approval denied (${event.approvalId})`;
|
|
1192
|
+
toolTimeline.push(toolText);
|
|
1193
|
+
currentTools.push(toolText);
|
|
1141
1194
|
}
|
|
1142
1195
|
if (
|
|
1143
1196
|
event.type === "run:completed" &&
|
|
@@ -1149,6 +1202,13 @@ export const createRequestHandler = async (options?: {
|
|
|
1149
1202
|
await telemetry.emit(event);
|
|
1150
1203
|
response.write(formatSseEvent(event));
|
|
1151
1204
|
}
|
|
1205
|
+
// Finalize sections
|
|
1206
|
+
if (currentTools.length > 0) {
|
|
1207
|
+
sections.push({ type: "tools", content: currentTools });
|
|
1208
|
+
}
|
|
1209
|
+
if (currentText.length > 0) {
|
|
1210
|
+
sections.push({ type: "text", content: currentText });
|
|
1211
|
+
}
|
|
1152
1212
|
conversation.messages = [
|
|
1153
1213
|
...conversation.messages,
|
|
1154
1214
|
{ role: "user", content: messageText },
|
|
@@ -1156,8 +1216,11 @@ export const createRequestHandler = async (options?: {
|
|
|
1156
1216
|
role: "assistant",
|
|
1157
1217
|
content: assistantResponse,
|
|
1158
1218
|
metadata:
|
|
1159
|
-
toolTimeline.length > 0
|
|
1160
|
-
? ({
|
|
1219
|
+
toolTimeline.length > 0 || sections.length > 0
|
|
1220
|
+
? ({
|
|
1221
|
+
toolActivity: toolTimeline,
|
|
1222
|
+
sections: sections.length > 0 ? sections : undefined,
|
|
1223
|
+
} as Message["metadata"])
|
|
1161
1224
|
: undefined,
|
|
1162
1225
|
},
|
|
1163
1226
|
];
|
|
@@ -1246,7 +1309,7 @@ export const runOnce = async (
|
|
|
1246
1309
|
return;
|
|
1247
1310
|
}
|
|
1248
1311
|
|
|
1249
|
-
for await (const event of harness.
|
|
1312
|
+
for await (const event of harness.runWithTelemetry(input)) {
|
|
1250
1313
|
await telemetry.emit(event);
|
|
1251
1314
|
if (event.type === "model:chunk") {
|
|
1252
1315
|
process.stdout.write(event.content);
|
package/src/init-onboarding.ts
CHANGED
|
@@ -441,11 +441,12 @@ const collectEnvFileLines = (answers: OnboardingAnswers): string[] => {
|
|
|
441
441
|
(answers["auth.type"] as "bearer" | "header" | "custom" | undefined) ?? "bearer";
|
|
442
442
|
const authHeaderName = String(answers["auth.headerName"] ?? "x-poncho-key");
|
|
443
443
|
if (authRequired) {
|
|
444
|
-
lines.push("# Auth (
|
|
444
|
+
lines.push("# Auth (protects both Web UI and API)");
|
|
445
|
+
lines.push("# Web UI: enter this token as the passphrase");
|
|
445
446
|
if (authType === "bearer") {
|
|
446
|
-
lines.push("#
|
|
447
|
+
lines.push("# API: include Authorization: Bearer <token> header");
|
|
447
448
|
} else if (authType === "header") {
|
|
448
|
-
lines.push(`#
|
|
449
|
+
lines.push(`# API: include ${authHeaderName}: <token> header`);
|
|
449
450
|
} else {
|
|
450
451
|
lines.push("# Custom auth mode: read this token in your auth.validate function.");
|
|
451
452
|
}
|
|
@@ -443,6 +443,9 @@ export const runInteractiveInk = async ({
|
|
|
443
443
|
let sawChunk = false;
|
|
444
444
|
let toolEvents = 0;
|
|
445
445
|
const toolTimeline: string[] = [];
|
|
446
|
+
const sections: Array<{ type: "text" | "tools"; content: string | string[] }> = [];
|
|
447
|
+
let currentText = "";
|
|
448
|
+
let currentTools: string[] = [];
|
|
446
449
|
let runFailed = false;
|
|
447
450
|
let usage: TokenUsage | undefined;
|
|
448
451
|
let latestRunId = "";
|
|
@@ -459,8 +462,14 @@ export const runInteractiveInk = async ({
|
|
|
459
462
|
}
|
|
460
463
|
if (event.type === "model:chunk") {
|
|
461
464
|
sawChunk = true;
|
|
465
|
+
// If we have tools accumulated and text starts again, push tools as a section
|
|
466
|
+
if (currentTools.length > 0) {
|
|
467
|
+
sections.push({ type: "tools", content: currentTools });
|
|
468
|
+
currentTools = [];
|
|
469
|
+
}
|
|
462
470
|
responseText += event.content;
|
|
463
471
|
streamedText += event.content;
|
|
472
|
+
currentText += event.content;
|
|
464
473
|
|
|
465
474
|
if (!thinkingCleared) {
|
|
466
475
|
clearThinking();
|
|
@@ -485,11 +494,18 @@ export const runInteractiveInk = async ({
|
|
|
485
494
|
clearThinking();
|
|
486
495
|
|
|
487
496
|
if (event.type === "tool:started") {
|
|
497
|
+
// If we have text accumulated, push it as a text section
|
|
498
|
+
if (currentText.length > 0) {
|
|
499
|
+
sections.push({ type: "text", content: currentText });
|
|
500
|
+
currentText = "";
|
|
501
|
+
}
|
|
488
502
|
const preview = showToolPayloads
|
|
489
503
|
? compactPreview(event.input, 400)
|
|
490
504
|
: compactPreview(event.input, 100);
|
|
491
505
|
console.log(yellow(`tools> start ${event.tool} input=${preview}`));
|
|
492
|
-
|
|
506
|
+
const toolText = `- start \`${event.tool}\``;
|
|
507
|
+
toolTimeline.push(toolText);
|
|
508
|
+
currentTools.push(toolText);
|
|
493
509
|
toolEvents += 1;
|
|
494
510
|
} else if (event.type === "tool:completed") {
|
|
495
511
|
const preview = showToolPayloads
|
|
@@ -503,29 +519,37 @@ export const runInteractiveInk = async ({
|
|
|
503
519
|
if (showToolPayloads) {
|
|
504
520
|
console.log(yellow(`tools> output ${preview}`));
|
|
505
521
|
}
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
);
|
|
522
|
+
const toolText = `- done \`${event.tool}\` in ${formatDuration(event.duration)}`;
|
|
523
|
+
toolTimeline.push(toolText);
|
|
524
|
+
currentTools.push(toolText);
|
|
509
525
|
} else if (event.type === "tool:error") {
|
|
510
526
|
console.log(
|
|
511
527
|
red(`tools> error ${event.tool}: ${event.error}`),
|
|
512
528
|
);
|
|
513
|
-
|
|
529
|
+
const toolText = `- error \`${event.tool}\`: ${event.error}`;
|
|
530
|
+
toolTimeline.push(toolText);
|
|
531
|
+
currentTools.push(toolText);
|
|
514
532
|
} else if (event.type === "tool:approval:required") {
|
|
515
533
|
console.log(
|
|
516
534
|
magenta(`tools> approval required for ${event.tool}`),
|
|
517
535
|
);
|
|
518
|
-
|
|
536
|
+
const toolText = `- approval required \`${event.tool}\``;
|
|
537
|
+
toolTimeline.push(toolText);
|
|
538
|
+
currentTools.push(toolText);
|
|
519
539
|
} else if (event.type === "tool:approval:granted") {
|
|
520
540
|
console.log(
|
|
521
541
|
gray(`tools> approval granted (${event.approvalId})`),
|
|
522
542
|
);
|
|
523
|
-
|
|
543
|
+
const toolText = `- approval granted (${event.approvalId})`;
|
|
544
|
+
toolTimeline.push(toolText);
|
|
545
|
+
currentTools.push(toolText);
|
|
524
546
|
} else if (event.type === "tool:approval:denied") {
|
|
525
547
|
console.log(
|
|
526
548
|
magenta(`tools> approval denied (${event.approvalId})`),
|
|
527
549
|
);
|
|
528
|
-
|
|
550
|
+
const toolText = `- approval denied (${event.approvalId})`;
|
|
551
|
+
toolTimeline.push(toolText);
|
|
552
|
+
currentTools.push(toolText);
|
|
529
553
|
}
|
|
530
554
|
} else if (event.type === "run:error") {
|
|
531
555
|
clearThinking();
|
|
@@ -588,13 +612,24 @@ export const runInteractiveInk = async ({
|
|
|
588
612
|
activeConversationId = created.conversationId;
|
|
589
613
|
}
|
|
590
614
|
|
|
615
|
+
// Finalize sections
|
|
616
|
+
if (currentTools.length > 0) {
|
|
617
|
+
sections.push({ type: "tools", content: currentTools });
|
|
618
|
+
}
|
|
619
|
+
if (currentText.length > 0) {
|
|
620
|
+
sections.push({ type: "text", content: currentText });
|
|
621
|
+
}
|
|
622
|
+
|
|
591
623
|
messages.push({ role: "user", content: trimmed });
|
|
592
624
|
messages.push({
|
|
593
625
|
role: "assistant",
|
|
594
626
|
content: responseText,
|
|
595
627
|
metadata:
|
|
596
|
-
toolTimeline.length > 0
|
|
597
|
-
? ({
|
|
628
|
+
toolTimeline.length > 0 || sections.length > 0
|
|
629
|
+
? ({
|
|
630
|
+
toolActivity: toolTimeline,
|
|
631
|
+
sections: sections.length > 0 ? sections : undefined,
|
|
632
|
+
} as Message["metadata"])
|
|
598
633
|
: undefined,
|
|
599
634
|
});
|
|
600
635
|
turn = computeTurn(messages);
|