@poncho-ai/cli 0.30.0 → 0.30.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 +13 -0
- package/dist/{chunk-5OLH7U3C.js → chunk-UYZOJWGL.js} +487 -180
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +10 -0
- package/dist/index.js +1 -1
- package/dist/{run-interactive-ink-EMTC7MK7.js → run-interactive-ink-ZSIGWFLZ.js} +1 -1
- package/package.json +4 -4
- package/src/index.ts +249 -82
- package/src/web-ui-client.ts +130 -9
- package/src/web-ui-styles.ts +50 -0
package/src/web-ui-client.ts
CHANGED
|
@@ -30,6 +30,8 @@ export const getWebUiClientScript = (markedSource: string): string => `
|
|
|
30
30
|
parentConversationId: null,
|
|
31
31
|
todos: [],
|
|
32
32
|
todoPanelCollapsed: false,
|
|
33
|
+
cronSectionCollapsed: true,
|
|
34
|
+
cronShowAll: false,
|
|
33
35
|
};
|
|
34
36
|
|
|
35
37
|
const agentInitial = document.body.dataset.agentInitial || "A";
|
|
@@ -773,11 +775,86 @@ export const getWebUiClientScript = (markedSource: string): string => `
|
|
|
773
775
|
}
|
|
774
776
|
};
|
|
775
777
|
|
|
778
|
+
const cronCaretSvg = '<svg viewBox="0 0 12 12" fill="none" width="10" height="10"><path d="M4.5 2.75L8 6L4.5 9.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path></svg>';
|
|
779
|
+
|
|
780
|
+
const parseCronTitle = (title) => {
|
|
781
|
+
const rest = title.replace(/^\[cron\]\s*/, "");
|
|
782
|
+
const isoMatch = rest.match(/\s(\d{4}-\d{2}-\d{2}T[\d:.]+Z?)$/);
|
|
783
|
+
if (isoMatch) {
|
|
784
|
+
return { jobName: rest.slice(0, isoMatch.index).trim(), timestamp: isoMatch[1] };
|
|
785
|
+
}
|
|
786
|
+
return { jobName: rest, timestamp: "" };
|
|
787
|
+
};
|
|
788
|
+
|
|
789
|
+
const formatCronTimestamp = (isoStr) => {
|
|
790
|
+
if (!isoStr) return "";
|
|
791
|
+
try {
|
|
792
|
+
const d = new Date(isoStr);
|
|
793
|
+
if (isNaN(d.getTime())) return isoStr;
|
|
794
|
+
return d.toLocaleString(undefined, { month: "short", day: "numeric", hour: "numeric", minute: "2-digit" });
|
|
795
|
+
} catch { return isoStr; }
|
|
796
|
+
};
|
|
797
|
+
|
|
798
|
+
const CRON_PAGE_SIZE = 20;
|
|
799
|
+
|
|
800
|
+
const appendCronSection = (cronConvs, needsDivider) => {
|
|
801
|
+
if (needsDivider) {
|
|
802
|
+
const divider = document.createElement("div");
|
|
803
|
+
divider.className = "sidebar-section-divider";
|
|
804
|
+
elements.list.appendChild(divider);
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
cronConvs.sort((a, b) => (b.updatedAt || b.createdAt || 0) - (a.updatedAt || a.createdAt || 0));
|
|
808
|
+
|
|
809
|
+
const isOpen = !state.cronSectionCollapsed;
|
|
810
|
+
const header = document.createElement("div");
|
|
811
|
+
header.className = "cron-section-header";
|
|
812
|
+
header.innerHTML =
|
|
813
|
+
'<span class="cron-section-caret' + (isOpen ? ' open' : '') + '">' + cronCaretSvg + '</span>' +
|
|
814
|
+
'<span>Cron jobs</span>' +
|
|
815
|
+
'<span class="cron-section-count">' + cronConvs.length + '</span>';
|
|
816
|
+
header.onclick = () => {
|
|
817
|
+
state.cronSectionCollapsed = !state.cronSectionCollapsed;
|
|
818
|
+
state.cronShowAll = false;
|
|
819
|
+
renderConversationList();
|
|
820
|
+
};
|
|
821
|
+
elements.list.appendChild(header);
|
|
822
|
+
|
|
823
|
+
if (state.cronSectionCollapsed) return;
|
|
824
|
+
|
|
825
|
+
const limit = state.cronShowAll ? cronConvs.length : CRON_PAGE_SIZE;
|
|
826
|
+
const visible = cronConvs.slice(0, limit);
|
|
827
|
+
|
|
828
|
+
for (const c of visible) {
|
|
829
|
+
const { jobName, timestamp } = parseCronTitle(c.title);
|
|
830
|
+
const fmtTime = formatCronTimestamp(timestamp);
|
|
831
|
+
const displayTitle = fmtTime ? jobName + " \\u00b7 " + fmtTime : c.title;
|
|
832
|
+
elements.list.appendChild(buildConversationItem(Object.assign({}, c, { title: displayTitle })));
|
|
833
|
+
appendSubagentsIfActive(c.conversationId);
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
if (!state.cronShowAll && cronConvs.length > CRON_PAGE_SIZE) {
|
|
837
|
+
const remaining = cronConvs.length - CRON_PAGE_SIZE;
|
|
838
|
+
const viewMore = document.createElement("div");
|
|
839
|
+
viewMore.className = "cron-view-more";
|
|
840
|
+
viewMore.textContent = "View " + remaining + " more\\u2026";
|
|
841
|
+
viewMore.onclick = () => {
|
|
842
|
+
state.cronShowAll = true;
|
|
843
|
+
renderConversationList();
|
|
844
|
+
};
|
|
845
|
+
elements.list.appendChild(viewMore);
|
|
846
|
+
}
|
|
847
|
+
};
|
|
848
|
+
|
|
776
849
|
const renderConversationList = () => {
|
|
777
850
|
elements.list.innerHTML = "";
|
|
778
851
|
const pending = state.conversations.filter(c => c.hasPendingApprovals);
|
|
779
852
|
const rest = state.conversations.filter(c => !c.hasPendingApprovals);
|
|
780
853
|
|
|
854
|
+
const isCron = (c) => c.title && c.title.startsWith("[cron]");
|
|
855
|
+
const cronConvs = rest.filter(isCron);
|
|
856
|
+
const nonCron = rest.filter(c => !isCron(c));
|
|
857
|
+
|
|
781
858
|
if (pending.length > 0) {
|
|
782
859
|
const label = document.createElement("div");
|
|
783
860
|
label.className = "sidebar-section-label";
|
|
@@ -796,7 +873,7 @@ export const getWebUiClientScript = (markedSource: string): string => `
|
|
|
796
873
|
const latest = [];
|
|
797
874
|
const previous7 = [];
|
|
798
875
|
const older = [];
|
|
799
|
-
for (const c of
|
|
876
|
+
for (const c of nonCron) {
|
|
800
877
|
const ts = c.updatedAt || c.createdAt || 0;
|
|
801
878
|
if (ts >= startOfToday) {
|
|
802
879
|
latest.push(c);
|
|
@@ -808,6 +885,12 @@ export const getWebUiClientScript = (markedSource: string): string => `
|
|
|
808
885
|
}
|
|
809
886
|
|
|
810
887
|
let sectionRendered = pending.length > 0;
|
|
888
|
+
|
|
889
|
+
if (cronConvs.length > 0) {
|
|
890
|
+
appendCronSection(cronConvs, sectionRendered);
|
|
891
|
+
sectionRendered = true;
|
|
892
|
+
}
|
|
893
|
+
|
|
811
894
|
const appendSection = (items, labelText) => {
|
|
812
895
|
if (items.length === 0) return;
|
|
813
896
|
if (sectionRendered) {
|
|
@@ -2977,7 +3060,7 @@ export const getWebUiClientScript = (markedSource: string): string => `
|
|
|
2977
3060
|
state: "resolved",
|
|
2978
3061
|
resolvedDecision: decision,
|
|
2979
3062
|
}));
|
|
2980
|
-
api("/api/approvals/" + encodeURIComponent(approvalId), {
|
|
3063
|
+
return api("/api/approvals/" + encodeURIComponent(approvalId), {
|
|
2981
3064
|
method: "POST",
|
|
2982
3065
|
body: JSON.stringify({ approved: decision === "approve" }),
|
|
2983
3066
|
}).catch((error) => {
|
|
@@ -3025,16 +3108,54 @@ export const getWebUiClientScript = (markedSource: string): string => `
|
|
|
3025
3108
|
if (pending.length === 0) return;
|
|
3026
3109
|
const wasStreaming = state.isStreaming;
|
|
3027
3110
|
if (!wasStreaming) setStreaming(true);
|
|
3028
|
-
|
|
3111
|
+
// Mark all items as resolved in the UI immediately
|
|
3112
|
+
for (const aid of pending) {
|
|
3113
|
+
state.approvalRequestsInFlight[aid] = true;
|
|
3114
|
+
updatePendingApproval(aid, (request) => ({
|
|
3115
|
+
...request,
|
|
3116
|
+
state: "resolved",
|
|
3117
|
+
resolvedDecision: decision,
|
|
3118
|
+
}));
|
|
3119
|
+
}
|
|
3029
3120
|
renderMessages(state.activeMessages, state.isStreaming);
|
|
3030
3121
|
loadConversations();
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3122
|
+
const streamCid = !wasStreaming && state.activeConversationId
|
|
3123
|
+
? state.activeConversationId
|
|
3124
|
+
: null;
|
|
3125
|
+
if (streamCid) {
|
|
3126
|
+
streamConversationEvents(streamCid, { liveOnly: true }).finally(() => {
|
|
3127
|
+
if (state.activeConversationId === streamCid) {
|
|
3128
|
+
pollUntilRunIdle(streamCid);
|
|
3129
|
+
}
|
|
3130
|
+
});
|
|
3037
3131
|
}
|
|
3132
|
+
// Send API calls sequentially so each store write completes
|
|
3133
|
+
// before the next read (avoids last-writer-wins in serverless).
|
|
3134
|
+
void (async () => {
|
|
3135
|
+
for (const aid of pending) {
|
|
3136
|
+
await api("/api/approvals/" + encodeURIComponent(aid), {
|
|
3137
|
+
method: "POST",
|
|
3138
|
+
body: JSON.stringify({ approved: decision === "approve" }),
|
|
3139
|
+
}).catch((error) => {
|
|
3140
|
+
const isStale = error && error.payload && error.payload.code === "APPROVAL_NOT_FOUND";
|
|
3141
|
+
if (isStale) {
|
|
3142
|
+
updatePendingApproval(aid, () => null);
|
|
3143
|
+
} else {
|
|
3144
|
+
const errMsg = error instanceof Error ? error.message : String(error);
|
|
3145
|
+
updatePendingApproval(aid, (request) => ({
|
|
3146
|
+
...request,
|
|
3147
|
+
state: "pending",
|
|
3148
|
+
pendingDecision: null,
|
|
3149
|
+
resolvedDecision: null,
|
|
3150
|
+
_error: errMsg,
|
|
3151
|
+
}));
|
|
3152
|
+
}
|
|
3153
|
+
renderMessages(state.activeMessages, state.isStreaming);
|
|
3154
|
+
}).finally(() => {
|
|
3155
|
+
delete state.approvalRequestsInFlight[aid];
|
|
3156
|
+
});
|
|
3157
|
+
}
|
|
3158
|
+
})();
|
|
3038
3159
|
return;
|
|
3039
3160
|
}
|
|
3040
3161
|
|
package/src/web-ui-styles.ts
CHANGED
|
@@ -401,6 +401,41 @@ export const WEB_UI_STYLES = `
|
|
|
401
401
|
.conversation-item .delete-btn.confirming:hover {
|
|
402
402
|
color: var(--error-alt);
|
|
403
403
|
}
|
|
404
|
+
.cron-section-header {
|
|
405
|
+
display: flex;
|
|
406
|
+
align-items: center;
|
|
407
|
+
gap: 6px;
|
|
408
|
+
padding: 8px 10px 4px;
|
|
409
|
+
cursor: pointer;
|
|
410
|
+
font-size: 11px;
|
|
411
|
+
font-weight: 600;
|
|
412
|
+
color: var(--fg-7);
|
|
413
|
+
text-transform: uppercase;
|
|
414
|
+
letter-spacing: 0.04em;
|
|
415
|
+
user-select: none;
|
|
416
|
+
transition: color 0.15s;
|
|
417
|
+
}
|
|
418
|
+
.cron-section-header:hover { color: var(--fg-5); }
|
|
419
|
+
.cron-section-caret {
|
|
420
|
+
display: inline-flex;
|
|
421
|
+
transition: transform 0.15s;
|
|
422
|
+
}
|
|
423
|
+
.cron-section-caret.open { transform: rotate(90deg); }
|
|
424
|
+
.cron-section-count {
|
|
425
|
+
font-weight: 400;
|
|
426
|
+
color: var(--fg-8);
|
|
427
|
+
font-size: 11px;
|
|
428
|
+
}
|
|
429
|
+
.cron-view-more {
|
|
430
|
+
padding: 6px 10px;
|
|
431
|
+
font-size: 12px;
|
|
432
|
+
color: var(--fg-7);
|
|
433
|
+
cursor: pointer;
|
|
434
|
+
text-align: center;
|
|
435
|
+
transition: color 0.15s;
|
|
436
|
+
user-select: none;
|
|
437
|
+
}
|
|
438
|
+
.cron-view-more:hover { color: var(--fg-3); }
|
|
404
439
|
.sidebar-footer {
|
|
405
440
|
margin-top: auto;
|
|
406
441
|
padding-top: 8px;
|
|
@@ -1572,6 +1607,8 @@ export const WEB_UI_STYLES = `
|
|
|
1572
1607
|
line-height: 1.45;
|
|
1573
1608
|
color: var(--fg-tool-code);
|
|
1574
1609
|
width: 100%;
|
|
1610
|
+
min-width: 0;
|
|
1611
|
+
overflow: hidden;
|
|
1575
1612
|
}
|
|
1576
1613
|
.subagent-result-summary {
|
|
1577
1614
|
list-style: none;
|
|
@@ -1616,6 +1653,19 @@ export const WEB_UI_STYLES = `
|
|
|
1616
1653
|
display: grid;
|
|
1617
1654
|
gap: 6px;
|
|
1618
1655
|
padding: 0 12px 10px;
|
|
1656
|
+
min-width: 0;
|
|
1657
|
+
overflow-x: auto;
|
|
1658
|
+
overflow-wrap: break-word;
|
|
1659
|
+
word-break: break-word;
|
|
1660
|
+
}
|
|
1661
|
+
.subagent-result-body pre {
|
|
1662
|
+
max-width: 100%;
|
|
1663
|
+
overflow-x: auto;
|
|
1664
|
+
}
|
|
1665
|
+
.subagent-result-body table {
|
|
1666
|
+
max-width: 100%;
|
|
1667
|
+
overflow-x: auto;
|
|
1668
|
+
display: block;
|
|
1619
1669
|
}
|
|
1620
1670
|
|
|
1621
1671
|
/* Todo panel — inside composer-inner, above the input shell */
|