@v587d/capital-generation 2.15.1 → 2.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/CHANGELOG.md +62 -0
- package/README.md +7 -7
- package/automation/client.js +47 -17
- package/automation/client.src.cjs +294 -83
- package/automation/contract.js +4 -0
- package/automation/defaults.js +2 -0
- package/automation/index.js +11 -7
- package/automation/package.json +2 -1
- package/automation/route.js +8 -1
- package/automation/runner.js +28 -6
- package/automation/service.js +89 -0
- package/automation/settings.js +6 -0
- package/automation/workspaces.js +113 -13
- package/chart-ui/package.json +1 -1
- package/config/client.js +12 -5
- package/config/client.src.cjs +223 -0
- package/config/index.js +2 -0
- package/config/package.json +1 -1
- package/lib/agents/root-tool-policy.d.ts +3 -3
- package/lib/agents/root-tool-policy.js +4 -2
- package/lib/data-collector/dataset-tools.js +22 -0
- package/lib/data-collector/describe.js +1 -0
- package/lib/data-collector/hub.js +5 -0
- package/lib/data-collector/store.d.ts +5 -0
- package/lib/data-collector/store.js +17 -0
- package/lib/data-collector/time-axis.d.ts +15 -0
- package/lib/data-collector/time-axis.js +73 -0
- package/lib/data-collector/tools.d.ts +1 -1
- package/lib/data-collector/tools.js +12 -1
- package/lib/index.d.ts +5 -0
- package/lib/index.js +2 -0
- package/lib/sources/tencent-http.js +7 -2
- package/lib/sources/tencent-public-core.d.ts +2 -0
- package/lib/sources/tencent-public-core.js +5 -0
- package/lib/sources/wind-mcp.js +3 -3
- package/lib/sources-entry.d.ts +1 -1
- package/lib/web-retriever/local-fetch.d.ts +1 -1
- package/lib/web-retriever/local-fetch.js +1 -1
- package/lib/web-retriever/sources.d.ts +58 -0
- package/lib/web-retriever/sources.js +290 -0
- package/lib/web-retriever/tools.js +199 -8
- package/lib/web-retriever/wind-client.d.ts +1 -1
- package/lib/web-retriever/wind-client.js +1 -1
- package/package.json +1 -1
- package/preset/capital-generation/agent.patch.yml +20 -19
- package/preset/capital-generation/skills/capital-data-protocol/SKILL.md +9 -2
- package/preset/capital-generation/skills/capital-orchestration/SKILL.md +2 -1
- package/preset/capital-generation/skills/capital-web-evidence/SKILL.md +18 -13
- package/preset/capital-generation/skills/capital-web-protocol/SKILL.md +30 -15
- package/watchlist/package.json +1 -1
|
@@ -72,7 +72,9 @@ const TIMEZONES = [
|
|
|
72
72
|
'America/Sao_Paulo',
|
|
73
73
|
'Australia/Sydney',
|
|
74
74
|
]
|
|
75
|
-
const HISTORY_DAYS =
|
|
75
|
+
const HISTORY_DAYS = 371
|
|
76
|
+
const HEATMAP_WEEKS = 53
|
|
77
|
+
const WEEKDAY_SHORT = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
|
|
76
78
|
const DETAIL_RECENT_RUN_LIMIT = 3
|
|
77
79
|
const TITLE_MAX_LENGTH = 12
|
|
78
80
|
const DESCRIPTION_MAX_LENGTH = 100
|
|
@@ -249,11 +251,19 @@ const zh = {
|
|
|
249
251
|
techInfo: '技术信息',
|
|
250
252
|
profileKey: 'profileKey',
|
|
251
253
|
runHistory: '运行历史',
|
|
252
|
-
|
|
254
|
+
last12Months: '最近 12 个月',
|
|
253
255
|
runCount: '{n} 次运行',
|
|
254
256
|
successCount: '{n} 成功',
|
|
255
257
|
failureCount: '{n} 失败',
|
|
256
|
-
|
|
258
|
+
activeCount: '{n} 进行中',
|
|
259
|
+
historyEmpty: '最近 12 个月还没有运行记录。',
|
|
260
|
+
heatLess: '少',
|
|
261
|
+
heatMore: '多',
|
|
262
|
+
heatFailed: '有失败',
|
|
263
|
+
heatActive: '进行中',
|
|
264
|
+
heatNoRuns: '没有运行',
|
|
265
|
+
heatFuture: '尚未到来',
|
|
266
|
+
heatSummary: '最近 12 个月共 {total} 次运行:{success} 成功,{failure} 失败。',
|
|
257
267
|
historyViewAll: '查看全部运行',
|
|
258
268
|
runFilterAll: '全部',
|
|
259
269
|
runFilterActive: '进行中',
|
|
@@ -370,8 +380,8 @@ function installStyles() {
|
|
|
370
380
|
const style = document.createElement('style')
|
|
371
381
|
style.setAttribute(`data-${STYLE_TAG}`, 'true')
|
|
372
382
|
style.textContent = `
|
|
373
|
-
.capital-automation-page, .capital-automation-modal { --ca-card: var(--dsw-alias-bg-layer-2, var(--dsw-alias-bg-layer-1)); --ca-line: var(--dsw-alias-border-l3, var(--dsw-alias-border-l1)); --ca-line-soft: var(--dsw-alias-border-l2, var(--dsw-alias-border-l1)); --ca-table-line: var(--ca-line); --ca-muted: var(--dsw-alias-label-tertiary); --ca-caption: var(--dsw-alias-label-caption, var(--dsw-alias-label-tertiary)); --ca-hover: var(--dsw-alias-interactive-bg-hover); --ca-field: var(--dsw-alias-bg-module-platform); --ca-business: var(--dsw-alias-state-business-primary, var(--dsw-alias-brand-primary)); --ca-success: var(--dsw-alias-state-success-primary); --ca-error: var(--dsw-alias-state-error-primary); --ca-warn: var(--dsw-alias-state-warn-label); --ca-business-text: var(--ca-business); --ca-success-text: var(--ca-success); --ca-error-text: var(--ca-error); --ca-warn-text: var(--ca-warn); }
|
|
374
|
-
body:not([data-ds-dark-theme]) .capital-automation-page, body:not([data-ds-dark-theme]) .capital-automation-modal { --ca-line: #0000001f; --ca-line-soft: #0000001a; --ca-table-line: #00000026; --ca-muted: #5d626a; --ca-caption: #6a6f77; --ca-hover: #eef1f5; --ca-field: #f7f8fa; --ca-business: #2f63d6; --ca-success: #16a34a; --ca-error: #dc2626; --ca-warn: #d97706; --ca-business-text: #2f63d6; --ca-success-text: #15803d; --ca-error-text: #c81e1e; --ca-warn-text: #b45309; --ca-tag-news: #2f6fd0; --ca-tag-market: #a85a08; --ca-tag-review: #6d4ac4; --ca-tag-research: #0f766e; --ca-tag-portfolio: #4d7c0f; --ca-tag-monitor: #0e7490; --ca-tag-custom: #b3246d; }
|
|
383
|
+
.capital-automation-page, .capital-automation-modal { --ca-card: var(--dsw-alias-bg-layer-2, var(--dsw-alias-bg-layer-1)); --ca-line: var(--dsw-alias-border-l3, var(--dsw-alias-border-l1)); --ca-line-soft: var(--dsw-alias-border-l2, var(--dsw-alias-border-l1)); --ca-table-line: var(--ca-line); --ca-muted: var(--dsw-alias-label-tertiary); --ca-caption: var(--dsw-alias-label-caption, var(--dsw-alias-label-tertiary)); --ca-hover: var(--dsw-alias-interactive-bg-hover); --ca-field: var(--dsw-alias-bg-module-platform); --ca-business: var(--dsw-alias-state-business-primary, var(--dsw-alias-brand-primary)); --ca-success: var(--dsw-alias-state-success-primary); --ca-error: var(--dsw-alias-state-error-primary); --ca-warn: var(--dsw-alias-state-warn-label); --ca-business-text: var(--ca-business); --ca-success-text: var(--ca-success); --ca-error-text: var(--ca-error); --ca-warn-text: var(--ca-warn); --ca-heat-0: #262626; --ca-heat-1: #1b5e3a; --ca-heat-2: #2f8a56; --ca-heat-3: #40b366; --ca-heat-4: #56d364; --ca-heat-fail: #f85149; }
|
|
384
|
+
body:not([data-ds-dark-theme]) .capital-automation-page, body:not([data-ds-dark-theme]) .capital-automation-modal { --ca-line: #0000001f; --ca-line-soft: #0000001a; --ca-table-line: #00000026; --ca-muted: #5d626a; --ca-caption: #6a6f77; --ca-hover: #eef1f5; --ca-field: #f7f8fa; --ca-business: #2f63d6; --ca-success: #16a34a; --ca-error: #dc2626; --ca-warn: #d97706; --ca-business-text: #2f63d6; --ca-success-text: #15803d; --ca-error-text: #c81e1e; --ca-warn-text: #b45309; --ca-tag-news: #2f6fd0; --ca-tag-market: #a85a08; --ca-tag-review: #6d4ac4; --ca-tag-research: #0f766e; --ca-tag-portfolio: #4d7c0f; --ca-tag-monitor: #0e7490; --ca-tag-custom: #b3246d; --ca-heat-0: #eef1f4; --ca-heat-1: #b7e4c0; --ca-heat-2: #74c98a; --ca-heat-3: #3ba55c; --ca-heat-4: #1f7a3a; --ca-heat-fail: #cf222e; }
|
|
375
385
|
.capital-automation-page { display: flex; flex-direction: column; min-height: 0; height: 100%; color: var(--dsw-alias-label-primary); }
|
|
376
386
|
.capital-automation-head { flex: none; padding: 18px 0 0; }
|
|
377
387
|
.capital-automation-headinner { box-sizing: border-box; width: min(1080px, 100%); margin: 0 auto; padding: 0 clamp(20px, 3vw, 36px); }
|
|
@@ -411,7 +421,7 @@ function installStyles() {
|
|
|
411
421
|
.capital-automation-scroll { flex: 1 1 auto; min-height: 0; overflow: auto; }
|
|
412
422
|
.capital-automation-container { box-sizing: border-box; width: min(1080px, 100%); margin: 0 auto; padding: 20px clamp(20px, 3vw, 36px) 72px; }
|
|
413
423
|
.capital-automation-card { box-sizing: border-box; border: .5px solid var(--ca-line); border-radius: 16px; background: var(--ca-card); }
|
|
414
|
-
.capital-automation-history { padding: 18px 20px 14px; }
|
|
424
|
+
.capital-automation-history { position: relative; padding: 18px 20px 14px; }
|
|
415
425
|
.capital-automation-historyhead { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: 10px 18px; }
|
|
416
426
|
.capital-automation-historytitle { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px; }
|
|
417
427
|
.capital-automation-historytitle h2 { margin: 0; font-size: 14px; line-height: 22px; font-weight: 600; }
|
|
@@ -420,18 +430,45 @@ function installStyles() {
|
|
|
420
430
|
.capital-automation-historystat { display: inline-flex; align-items: center; gap: 5px; font-variant-numeric: tabular-nums; }
|
|
421
431
|
.capital-automation-historystat[data-tone="ok"] { color: var(--ca-success-text); }
|
|
422
432
|
.capital-automation-historystat[data-tone="error"] { color: var(--ca-error-text); }
|
|
433
|
+
.capital-automation-historystat[data-tone="active"] { color: var(--ca-business-text); }
|
|
423
434
|
.capital-automation-statusdot { display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex: none; }
|
|
424
435
|
.capital-automation-historylink { display: inline-flex; align-items: center; gap: 2px; height: 24px; padding: 0 6px; border: 0; border-radius: 7px; background: transparent; color: var(--ca-muted); font: inherit; font-size: 12px; cursor: pointer; }
|
|
425
436
|
.capital-automation-historylink:hover { background: var(--ca-hover); color: var(--dsw-alias-label-primary); }
|
|
426
|
-
.capital-automation-
|
|
427
|
-
.capital-automation-
|
|
428
|
-
.capital-automation-
|
|
429
|
-
.capital-automation-
|
|
430
|
-
.capital-automation-
|
|
431
|
-
.capital-automation-
|
|
432
|
-
.capital-automation-
|
|
433
|
-
.capital-automation-
|
|
434
|
-
.capital-automation-
|
|
437
|
+
.capital-automation-heatmap { display: flex; align-items: flex-start; gap: 8px; margin-top: 16px; }
|
|
438
|
+
.capital-automation-heatweekdays { flex: none; display: grid; grid-template-rows: repeat(7, 1fr); gap: 3px; width: 28px; padding-top: 22px; color: var(--ca-caption); font-size: 10.5px; line-height: 1; align-self: stretch; }
|
|
439
|
+
.capital-automation-heatweekday { display: flex; align-items: center; }
|
|
440
|
+
.capital-automation-heatscroll { flex: 1 1 auto; min-width: 0; overflow-x: auto; overflow-y: hidden; padding-bottom: 2px; scrollbar-width: thin; }
|
|
441
|
+
.capital-automation-heatscroll[data-faded="true"] { -webkit-mask-image: linear-gradient(to right, transparent 0, #000 18px); mask-image: linear-gradient(to right, transparent 0, #000 18px); }
|
|
442
|
+
.capital-automation-heatcanvas { min-width: 620px; }
|
|
443
|
+
.capital-automation-heatmonths { display: grid; grid-template-columns: repeat(${HEATMAP_WEEKS}, minmax(0, 1fr)); gap: 3px; height: 16px; margin-bottom: 6px; }
|
|
444
|
+
.capital-automation-heatmonth { color: var(--ca-caption); font-size: 10.5px; line-height: 16px; white-space: nowrap; letter-spacing: .01em; }
|
|
445
|
+
.capital-automation-heatgrid { display: grid; grid-template-columns: repeat(${HEATMAP_WEEKS}, minmax(0, 1fr)); grid-template-rows: repeat(7, auto); grid-auto-flow: column; gap: 3px; }
|
|
446
|
+
.capital-automation-heatcell { display: block; min-width: 0; aspect-ratio: 1; border-radius: 2.5px; background: var(--ca-heat-0); box-shadow: inset 0 0 0 .5px color-mix(in srgb, var(--dsw-alias-label-primary) 4%, transparent); transition: background .12s, box-shadow .12s, outline-color .12s; }
|
|
447
|
+
.capital-automation-heatcell[data-level="1"] { background: var(--ca-heat-1); }
|
|
448
|
+
.capital-automation-heatcell[data-level="2"] { background: var(--ca-heat-2); }
|
|
449
|
+
.capital-automation-heatcell[data-level="3"] { background: var(--ca-heat-3); }
|
|
450
|
+
.capital-automation-heatcell[data-level="4"] { background: var(--ca-heat-4); }
|
|
451
|
+
.capital-automation-heatcell[data-future="true"] { opacity: .45; }
|
|
452
|
+
.capital-automation-heatcell[data-failed="true"] { box-shadow: inset 0 0 0 1.5px var(--ca-heat-fail); }
|
|
453
|
+
.capital-automation-heatcell[data-active="true"] { box-shadow: inset 0 0 0 1.5px var(--ca-business); animation: capital-automation-heatpulse 1.8s ease-in-out infinite; }
|
|
454
|
+
.capital-automation-heatcell:hover { outline: 1px solid var(--dsw-alias-label-primary); outline-offset: 1px; }
|
|
455
|
+
.capital-automation-heattip { position: absolute; z-index: 5; min-width: 128px; max-width: 220px; padding: 7px 9px; border: .5px solid var(--ca-line); border-radius: 9px; background: var(--dsw-alias-bg-layer-3, var(--ca-card)); box-shadow: 0 8px 22px rgba(0, 0, 0, .28); color: var(--dsw-alias-label-primary); pointer-events: none; transform: translate(-50%, -100%); }
|
|
456
|
+
.capital-automation-heattipdate { display: block; font-size: 11.5px; font-weight: 600; line-height: 16px; white-space: nowrap; }
|
|
457
|
+
.capital-automation-heattipmeta { display: block; margin-top: 2px; color: var(--ca-muted); font-size: 11px; line-height: 16px; white-space: nowrap; font-variant-numeric: tabular-nums; }
|
|
458
|
+
.capital-automation-heatfoot { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 14px; margin-top: 8px; color: var(--ca-caption); font-size: 11px; line-height: 16px; }
|
|
459
|
+
.capital-automation-heatrange { font-variant-numeric: tabular-nums; }
|
|
460
|
+
.capital-automation-heatlegend { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 5px; }
|
|
461
|
+
.capital-automation-heatlegendlabel { color: var(--ca-caption); }
|
|
462
|
+
.capital-automation-heatsample { width: 10px; height: 10px; border-radius: 2px; background: var(--ca-heat-0); box-shadow: inset 0 0 0 .5px color-mix(in srgb, var(--dsw-alias-label-primary) 5%, transparent); }
|
|
463
|
+
.capital-automation-heatsample[data-level="1"] { background: var(--ca-heat-1); }
|
|
464
|
+
.capital-automation-heatsample[data-level="2"] { background: var(--ca-heat-2); }
|
|
465
|
+
.capital-automation-heatsample[data-level="3"] { background: var(--ca-heat-3); }
|
|
466
|
+
.capital-automation-heatsample[data-level="4"] { background: var(--ca-heat-4); }
|
|
467
|
+
.capital-automation-heatkey { display: inline-flex; align-items: center; gap: 5px; margin-right: 7px; }
|
|
468
|
+
.capital-automation-heatfailbox { width: 10px; height: 10px; border-radius: 2px; background: var(--ca-heat-0); box-shadow: inset 0 0 0 1.5px var(--ca-heat-fail); }
|
|
469
|
+
.capital-automation-heatactivebox { width: 10px; height: 10px; border-radius: 2px; background: var(--ca-heat-0); box-shadow: inset 0 0 0 1.5px var(--ca-business); }
|
|
470
|
+
@keyframes capital-automation-heatpulse { 0%, 100% { opacity: 1; } 50% { opacity: .5; } }
|
|
471
|
+
@media (prefers-reduced-motion: reduce) { .capital-automation-heatcell[data-active="true"] { animation: none; } }
|
|
435
472
|
.capital-automation-section { margin-top: 26px; }
|
|
436
473
|
.capital-automation-sectionhead { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; margin-bottom: 10px; }
|
|
437
474
|
.capital-automation-sectionhead h2 { margin: 0; font-size: 14px; line-height: 22px; font-weight: 600; }
|
|
@@ -516,8 +553,11 @@ function installStyles() {
|
|
|
516
553
|
.capital-automation-loading { display: flex; flex-direction: column; pointer-events: none; }
|
|
517
554
|
.capital-automation-skeletonlabel { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
|
|
518
555
|
.capital-automation-skeleton { display: block; flex: none; max-width: 100%; border-radius: 6px; background: var(--dsw-alias-bg-skeleton); animation: capital-automation-breathe 1.8s ease-in-out infinite; }
|
|
519
|
-
.capital-automation-
|
|
520
|
-
.capital-automation-
|
|
556
|
+
.capital-automation-skeletonheat { margin-top: 16px; padding-left: 36px; }
|
|
557
|
+
.capital-automation-skeletonmonths { display: grid; grid-template-columns: repeat(${HEATMAP_WEEKS}, minmax(0, 1fr)); gap: 3px; height: 16px; margin-bottom: 6px; }
|
|
558
|
+
.capital-automation-skeletonmonths > .capital-automation-skeleton { width: 22px; height: 8px; border-radius: 3px; }
|
|
559
|
+
.capital-automation-skeletongrid { display: grid; grid-template-columns: repeat(${HEATMAP_WEEKS}, minmax(0, 1fr)); grid-template-rows: repeat(7, auto); grid-auto-flow: column; gap: 3px; }
|
|
560
|
+
.capital-automation-skeletongrid > .capital-automation-skeleton { width: 100%; aspect-ratio: 1; border-radius: 2.5px; }
|
|
521
561
|
.capital-automation-skeletonstack { display: flex; flex: 1 1 auto; flex-direction: column; gap: 6px; min-width: 0; }
|
|
522
562
|
.capital-automation-skeletonrow .capital-automation-trowmain { min-height: 57px; cursor: default; }
|
|
523
563
|
.capital-automation-skeletonrow .capital-automation-cell[data-cell] { display: flex; flex-direction: column; gap: 5px; }
|
|
@@ -843,6 +883,7 @@ function createApi() {
|
|
|
843
883
|
run: (id) => request(`/tasks/${encodeURIComponent(id)}/run`, { method: 'POST' }),
|
|
844
884
|
cancel: (id) => request(`/runs/${encodeURIComponent(id)}/cancel`, { method: 'POST' }),
|
|
845
885
|
runs: (query = '') => request(`/runs${query}`),
|
|
886
|
+
history: (query = '') => request(`/runs/history${query}`),
|
|
846
887
|
notifications: () => request('/notifications'),
|
|
847
888
|
markRead: (body) => request('/notifications/read', { method: 'POST', body }),
|
|
848
889
|
}
|
|
@@ -1077,10 +1118,6 @@ function formatRelative(value) {
|
|
|
1077
1118
|
return ''
|
|
1078
1119
|
}
|
|
1079
1120
|
|
|
1080
|
-
function formatRangeLabel(date) {
|
|
1081
|
-
return new Intl.DateTimeFormat('zh-CN', { month: 'short', day: 'numeric' }).format(date)
|
|
1082
|
-
}
|
|
1083
|
-
|
|
1084
1121
|
function weekdayText(trigger) {
|
|
1085
1122
|
return (trigger.weekdays ?? []).slice().sort((left, right) => left - right)
|
|
1086
1123
|
.map((day) => WEEKDAYS.find((item) => item.value === day)?.label ?? '')
|
|
@@ -1335,42 +1372,121 @@ function cloneTask(task) {
|
|
|
1335
1372
|
}
|
|
1336
1373
|
}
|
|
1337
1374
|
|
|
1338
|
-
function
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1375
|
+
function pad2(value) {
|
|
1376
|
+
return String(value).padStart(2, '0')
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
function localDateKey(date) {
|
|
1380
|
+
return `${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())}`
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
function addLocalDays(date, delta) {
|
|
1384
|
+
const next = new Date(date)
|
|
1385
|
+
next.setDate(next.getDate() + delta)
|
|
1386
|
+
return next
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
function startOfLocalWeek(date) {
|
|
1390
|
+
const start = new Date(date)
|
|
1391
|
+
start.setHours(0, 0, 0, 0)
|
|
1392
|
+
start.setDate(start.getDate() - ((start.getDay() + 6) % 7))
|
|
1393
|
+
return start
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
function parseLocalDateKey(key) {
|
|
1397
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(String(key ?? ''))
|
|
1398
|
+
if (match === null) return null
|
|
1399
|
+
return { year: Number(match[1]), month: Number(match[2]), day: Number(match[3]) }
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
function historyBucketMap(history) {
|
|
1403
|
+
const buckets = new Map()
|
|
1404
|
+
for (const bucket of history?.days ?? []) {
|
|
1405
|
+
if (typeof bucket?.date === 'string') buckets.set(bucket.date, bucket)
|
|
1351
1406
|
}
|
|
1407
|
+
return buckets
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1410
|
+
function heatLevel(total) {
|
|
1411
|
+
if (total <= 0) return 0
|
|
1412
|
+
if (total === 1) return 1
|
|
1413
|
+
if (total === 2) return 2
|
|
1414
|
+
if (total <= 4) return 3
|
|
1415
|
+
return 4
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
function buildHeatmap(history) {
|
|
1419
|
+
const byDate = historyBucketMap(history)
|
|
1420
|
+
const today = new Date()
|
|
1421
|
+
today.setHours(0, 0, 0, 0)
|
|
1422
|
+
const first = addLocalDays(startOfLocalWeek(today), -7 * (HEATMAP_WEEKS - 1))
|
|
1423
|
+
const weeks = []
|
|
1424
|
+
let total = 0
|
|
1352
1425
|
let success = 0
|
|
1353
1426
|
let failure = 0
|
|
1354
|
-
let
|
|
1355
|
-
for (
|
|
1356
|
-
const
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1427
|
+
let active = 0
|
|
1428
|
+
for (let weekIndex = 0; weekIndex < HEATMAP_WEEKS; weekIndex += 1) {
|
|
1429
|
+
const weekStart = addLocalDays(first, weekIndex * 7)
|
|
1430
|
+
const days = []
|
|
1431
|
+
let label = null
|
|
1432
|
+
for (let dayIndex = 0; dayIndex < 7; dayIndex += 1) {
|
|
1433
|
+
const date = addLocalDays(weekStart, dayIndex)
|
|
1434
|
+
const key = localDateKey(date)
|
|
1435
|
+
const bucket = byDate.get(key)
|
|
1436
|
+
const dayTotal = Number.isFinite(bucket?.total) ? bucket.total : 0
|
|
1437
|
+
total += dayTotal
|
|
1438
|
+
success += Number.isFinite(bucket?.success) ? bucket.success : 0
|
|
1439
|
+
failure += Number.isFinite(bucket?.failure) ? bucket.failure : 0
|
|
1440
|
+
active += Number.isFinite(bucket?.active) ? bucket.active : 0
|
|
1441
|
+
if (date.getDate() === 1) label = `${date.getMonth() + 1}月`
|
|
1442
|
+
days.push({
|
|
1443
|
+
date: key,
|
|
1444
|
+
total: dayTotal,
|
|
1445
|
+
success: Number.isFinite(bucket?.success) ? bucket.success : 0,
|
|
1446
|
+
failure: Number.isFinite(bucket?.failure) ? bucket.failure : 0,
|
|
1447
|
+
active: Number.isFinite(bucket?.active) ? bucket.active : 0,
|
|
1448
|
+
level: heatLevel(dayTotal),
|
|
1449
|
+
future: date.getTime() > today.getTime(),
|
|
1450
|
+
})
|
|
1451
|
+
}
|
|
1452
|
+
weeks.push({ key: localDateKey(weekStart), label, days })
|
|
1453
|
+
}
|
|
1454
|
+
// A trailing window starts mid-month; label it only when the next month is far enough away.
|
|
1455
|
+
const firstLabelIndex = weeks.findIndex((week) => week.label !== null)
|
|
1456
|
+
if (weeks[0].label === null && firstLabelIndex >= 3) weeks[0].label = `${first.getMonth() + 1}月`
|
|
1457
|
+
return {
|
|
1458
|
+
weeks,
|
|
1459
|
+
total: Number.isFinite(history?.total) ? history.total : total,
|
|
1460
|
+
success: Number.isFinite(history?.success) ? history.success : success,
|
|
1461
|
+
failure: Number.isFinite(history?.failure) ? history.failure : failure,
|
|
1462
|
+
active,
|
|
1463
|
+
first: localDateKey(first),
|
|
1464
|
+
last: localDateKey(addLocalDays(first, HEATMAP_WEEKS * 7 - 1)),
|
|
1364
1465
|
}
|
|
1365
|
-
return { days, success, failure, total }
|
|
1366
1466
|
}
|
|
1367
1467
|
|
|
1368
|
-
function
|
|
1369
|
-
|
|
1370
|
-
if (
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1468
|
+
function formatHeatDay(key) {
|
|
1469
|
+
const parts = parseLocalDateKey(key)
|
|
1470
|
+
if (parts === null) return String(key ?? '')
|
|
1471
|
+
const date = new Date(parts.year, parts.month - 1, parts.day)
|
|
1472
|
+
return `${parts.year}年${parts.month}月${parts.day}日 ${WEEKDAY_SHORT[date.getDay()]}`
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
function formatHeatRange(first, last) {
|
|
1476
|
+
const left = parseLocalDateKey(first)
|
|
1477
|
+
const right = parseLocalDateKey(last)
|
|
1478
|
+
if (left === null || right === null) return ''
|
|
1479
|
+
return `${left.year}.${pad2(left.month)} – ${right.year}.${pad2(right.month)}`
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
function heatDayMeta(day) {
|
|
1483
|
+
if (day.future) return zh.heatFuture
|
|
1484
|
+
if (day.total === 0) return zh.heatNoRuns
|
|
1485
|
+
const parts = [zh.runCount.replace('{n}', String(day.total))]
|
|
1486
|
+
if (day.success > 0) parts.push(zh.successCount.replace('{n}', String(day.success)))
|
|
1487
|
+
if (day.failure > 0) parts.push(zh.failureCount.replace('{n}', String(day.failure)))
|
|
1488
|
+
if (day.active > 0) parts.push(zh.activeCount.replace('{n}', String(day.active)))
|
|
1489
|
+
return parts.join(' · ')
|
|
1374
1490
|
}
|
|
1375
1491
|
|
|
1376
1492
|
function eventTone(kind) {
|
|
@@ -1449,13 +1565,57 @@ function limitValue(meta, key, fallback) {
|
|
|
1449
1565
|
}
|
|
1450
1566
|
|
|
1451
1567
|
function RunHistoryCard(props) {
|
|
1452
|
-
const {
|
|
1453
|
-
const
|
|
1454
|
-
|
|
1568
|
+
const { history, onViewAll, showLink = true } = props
|
|
1569
|
+
const model = buildHeatmap(history)
|
|
1570
|
+
const { total, success, failure, active, weeks } = model
|
|
1571
|
+
const cardRef = useRef(null)
|
|
1572
|
+
const scrollRef = useRef(null)
|
|
1573
|
+
const [tip, setTip] = useState(null)
|
|
1574
|
+
|
|
1575
|
+
useEffect(() => {
|
|
1576
|
+
const node = scrollRef.current
|
|
1577
|
+
if (node === null) return undefined
|
|
1578
|
+
node.scrollLeft = node.scrollWidth
|
|
1579
|
+
const syncFade = () => { node.dataset.faded = node.scrollLeft > 1 ? 'true' : 'false' }
|
|
1580
|
+
node.addEventListener('scroll', syncFade, { passive: true })
|
|
1581
|
+
syncFade()
|
|
1582
|
+
return () => node.removeEventListener('scroll', syncFade)
|
|
1583
|
+
}, [])
|
|
1584
|
+
|
|
1585
|
+
function showTip(day, event) {
|
|
1586
|
+
const card = cardRef.current
|
|
1587
|
+
if (card === null || event === undefined) return
|
|
1588
|
+
const cell = event.currentTarget.getBoundingClientRect()
|
|
1589
|
+
const base = card.getBoundingClientRect()
|
|
1590
|
+
const half = 110
|
|
1591
|
+
const left = Math.min(Math.max(cell.left - base.left + cell.width / 2, half), Math.max(half, base.width - half))
|
|
1592
|
+
setTip({ day, left, top: cell.top - base.top - 7 })
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
const monthLabels = weeks.map((week, index) => week.label === null ? null : h('span', {
|
|
1596
|
+
key: week.key,
|
|
1597
|
+
className: 'capital-automation-heatmonth',
|
|
1598
|
+
style: index >= HEATMAP_WEEKS - 2
|
|
1599
|
+
? { gridColumn: index + 1, justifySelf: 'end' }
|
|
1600
|
+
: { gridColumn: index + 1 },
|
|
1601
|
+
}, week.label))
|
|
1602
|
+
|
|
1603
|
+
const cells = weeks.flatMap((week) => week.days.map((day) => h('span', {
|
|
1604
|
+
key: day.date,
|
|
1605
|
+
className: 'capital-automation-heatcell',
|
|
1606
|
+
'data-level': day.level,
|
|
1607
|
+
'data-future': day.future ? 'true' : undefined,
|
|
1608
|
+
'data-failed': day.failure > 0 ? 'true' : undefined,
|
|
1609
|
+
'data-active': day.active > 0 ? 'true' : undefined,
|
|
1610
|
+
title: `${formatHeatDay(day.date)} · ${heatDayMeta(day)}`,
|
|
1611
|
+
onMouseEnter: (event) => showTip(day, event),
|
|
1612
|
+
})))
|
|
1613
|
+
|
|
1614
|
+
return h('section', { ref: cardRef, className: 'capital-automation-card capital-automation-history', 'aria-label': zh.runHistory },
|
|
1455
1615
|
h('div', { className: 'capital-automation-historyhead' },
|
|
1456
1616
|
h('div', { className: 'capital-automation-historytitle' },
|
|
1457
1617
|
h('h2', null, zh.runHistory),
|
|
1458
|
-
h('span', null, zh.
|
|
1618
|
+
h('span', null, zh.last12Months)),
|
|
1459
1619
|
h('div', { className: 'capital-automation-historystats' },
|
|
1460
1620
|
h('span', { className: 'capital-automation-historystat', 'data-tone': 'ok' },
|
|
1461
1621
|
h('span', { className: 'capital-automation-statusdot' }),
|
|
@@ -1463,21 +1623,56 @@ function RunHistoryCard(props) {
|
|
|
1463
1623
|
h('span', { className: 'capital-automation-historystat', 'data-tone': 'error' },
|
|
1464
1624
|
h('span', { className: 'capital-automation-statusdot' }),
|
|
1465
1625
|
zh.failureCount.replace('{n}', String(failure))),
|
|
1626
|
+
active > 0
|
|
1627
|
+
? h('span', { className: 'capital-automation-historystat', 'data-tone': 'active' },
|
|
1628
|
+
h('span', { className: 'capital-automation-statusdot' }),
|
|
1629
|
+
zh.activeCount.replace('{n}', String(active)))
|
|
1630
|
+
: null,
|
|
1466
1631
|
showLink && onViewAll !== undefined
|
|
1467
1632
|
? h('button', { type: 'button', className: 'capital-automation-historylink', onClick: onViewAll },
|
|
1468
1633
|
zh.runCount.replace('{n}', String(total)),
|
|
1469
1634
|
h(IconChevronRightOutlineRegular, { size: 13 }))
|
|
1470
1635
|
: h('span', { className: 'capital-automation-historystat' }, zh.runCount.replace('{n}', String(total))))),
|
|
1471
|
-
h('div', { className: 'capital-automation-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1636
|
+
h('div', { className: 'capital-automation-heatmap' },
|
|
1637
|
+
h('div', { className: 'capital-automation-heatweekdays', 'aria-hidden': 'true' },
|
|
1638
|
+
[0, 1, 2, 3, 4, 5, 6].map((index) => h('span', { key: index, className: 'capital-automation-heatweekday' },
|
|
1639
|
+
index === 0 ? '周一' : index === 2 ? '周三' : index === 4 ? '周五' : ''))),
|
|
1640
|
+
h('div', { ref: scrollRef, className: 'capital-automation-heatscroll' },
|
|
1641
|
+
h('div', { className: 'capital-automation-heatcanvas' },
|
|
1642
|
+
h('div', { className: 'capital-automation-heatmonths', 'aria-hidden': 'true' }, monthLabels),
|
|
1643
|
+
h('div', {
|
|
1644
|
+
className: 'capital-automation-heatgrid',
|
|
1645
|
+
role: 'img',
|
|
1646
|
+
'aria-label': zh.heatSummary
|
|
1647
|
+
.replace('{total}', String(total))
|
|
1648
|
+
.replace('{success}', String(success))
|
|
1649
|
+
.replace('{failure}', String(failure)),
|
|
1650
|
+
onMouseLeave: () => setTip(null),
|
|
1651
|
+
}, cells)))),
|
|
1652
|
+
h('div', { className: 'capital-automation-heatfoot' },
|
|
1653
|
+
h('span', { className: 'capital-automation-heatrange' },
|
|
1654
|
+
total === 0 ? zh.historyEmpty : formatHeatRange(model.first, model.last)),
|
|
1655
|
+
h('div', { className: 'capital-automation-heatlegend' },
|
|
1656
|
+
active > 0
|
|
1657
|
+
? h('span', { className: 'capital-automation-heatkey' },
|
|
1658
|
+
h('span', { className: 'capital-automation-heatactivebox' }),
|
|
1659
|
+
zh.heatActive)
|
|
1660
|
+
: null,
|
|
1661
|
+
failure > 0
|
|
1662
|
+
? h('span', { className: 'capital-automation-heatkey' },
|
|
1663
|
+
h('span', { className: 'capital-automation-heatfailbox' }),
|
|
1664
|
+
zh.heatFailed)
|
|
1665
|
+
: null,
|
|
1666
|
+
h('span', { className: 'capital-automation-heatlegendlabel' }, zh.heatLess),
|
|
1667
|
+
[0, 1, 2, 3, 4].map((level) => h('span', { key: level, className: 'capital-automation-heatsample', 'data-level': level })),
|
|
1668
|
+
h('span', { className: 'capital-automation-heatlegendlabel' }, zh.heatMore))),
|
|
1669
|
+
tip === null ? null : h('div', {
|
|
1670
|
+
className: 'capital-automation-heattip',
|
|
1671
|
+
role: 'tooltip',
|
|
1672
|
+
style: { left: `${tip.left}px`, top: `${tip.top}px` },
|
|
1673
|
+
},
|
|
1674
|
+
h('span', { className: 'capital-automation-heattipdate' }, formatHeatDay(tip.day.date)),
|
|
1675
|
+
h('span', { className: 'capital-automation-heattipmeta' }, heatDayMeta(tip.day))))
|
|
1481
1676
|
}
|
|
1482
1677
|
|
|
1483
1678
|
function RunStatus(props) {
|
|
@@ -1884,15 +2079,21 @@ function SkeletonHistory() {
|
|
|
1884
2079
|
h(SkeletonBlock, { width: 64, height: 12 }),
|
|
1885
2080
|
h(SkeletonBlock, { width: 64, height: 12 }),
|
|
1886
2081
|
h(SkeletonBlock, { width: 56, height: 12 }))),
|
|
1887
|
-
h('div', { className: 'capital-automation-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
h(
|
|
1895
|
-
|
|
2082
|
+
h('div', { className: 'capital-automation-skeletonheat' },
|
|
2083
|
+
h('div', { className: 'capital-automation-skeletonmonths' },
|
|
2084
|
+
Array.from({ length: 12 }, (_, index) => h('span', {
|
|
2085
|
+
key: index,
|
|
2086
|
+
className: 'capital-automation-skeleton',
|
|
2087
|
+
style: { gridColumn: 1 + index * 4, width: 22, height: 8 },
|
|
2088
|
+
}))),
|
|
2089
|
+
h('div', { className: 'capital-automation-skeletongrid' },
|
|
2090
|
+
Array.from({ length: HEATMAP_WEEKS * 7 }, (_, index) => h('span', {
|
|
2091
|
+
key: index,
|
|
2092
|
+
className: 'capital-automation-skeleton',
|
|
2093
|
+
})))),
|
|
2094
|
+
h('div', { className: 'capital-automation-heatfoot' },
|
|
2095
|
+
h(SkeletonBlock, { width: 108, height: 9 }),
|
|
2096
|
+
h(SkeletonBlock, { width: 96, height: 9 })))
|
|
1896
2097
|
}
|
|
1897
2098
|
|
|
1898
2099
|
function SkeletonSectionHead(props) {
|
|
@@ -1980,8 +2181,7 @@ function LoadingSkeleton(props) {
|
|
|
1980
2181
|
|
|
1981
2182
|
function TaskDashboardSkeleton() {
|
|
1982
2183
|
return h(LoadingSkeleton, null,
|
|
1983
|
-
h(
|
|
1984
|
-
h('section', { className: 'capital-automation-section' },
|
|
2184
|
+
h('section', { className: 'capital-automation-section', style: { marginTop: 0 } },
|
|
1985
2185
|
h(SkeletonSectionHead),
|
|
1986
2186
|
h(SkeletonTaskTable, { rows: 4 })),
|
|
1987
2187
|
h('section', { className: 'capital-automation-section' },
|
|
@@ -2001,7 +2201,7 @@ function RunsSkeleton() {
|
|
|
2001
2201
|
}
|
|
2002
2202
|
|
|
2003
2203
|
function TaskDashboard(props) {
|
|
2004
|
-
const { tasks, templates, runs, onOpenTask, onNewTask, onEnableTemplate, onAction, onRequestConfirm, onEdit,
|
|
2204
|
+
const { tasks, templates, runs, onOpenTask, onNewTask, onEnableTemplate, onAction, onRequestConfirm, onEdit, onRequestCancelRun, schedulerActive, storageReady, busyTaskId } = props
|
|
2005
2205
|
const [filter, setFilter] = useState('all')
|
|
2006
2206
|
const [query, setQuery] = useState('')
|
|
2007
2207
|
const runningTasks = tasks.filter((task) => isActiveRun(task.lastRun))
|
|
@@ -2026,8 +2226,7 @@ function TaskDashboard(props) {
|
|
|
2026
2226
|
}
|
|
2027
2227
|
const filters = ['all', 'custom', 'news', 'market', 'review', 'research', 'portfolio', 'monitor']
|
|
2028
2228
|
return h('div', null,
|
|
2029
|
-
h(
|
|
2030
|
-
h('section', { className: 'capital-automation-section' },
|
|
2229
|
+
h('section', { className: 'capital-automation-section', style: { marginTop: 0 } },
|
|
2031
2230
|
h('div', { className: 'capital-automation-sectionhead' },
|
|
2032
2231
|
h('h2', null, zh.taskSection),
|
|
2033
2232
|
h('span', null, zh.taskSectionHint),
|
|
@@ -2520,7 +2719,7 @@ function RunFilters(props) {
|
|
|
2520
2719
|
}
|
|
2521
2720
|
|
|
2522
2721
|
function RunsPage(props) {
|
|
2523
|
-
const { runs, tasks, onCancel, onOpenSession, onOpenTask, onGoTasks, onNewTask, busy } = props
|
|
2722
|
+
const { runs, history, tasks, onCancel, onOpenSession, onOpenTask, onGoTasks, onNewTask, busy } = props
|
|
2524
2723
|
const [filter, setFilter] = useState('all')
|
|
2525
2724
|
const counts = {
|
|
2526
2725
|
all: runs.length,
|
|
@@ -2535,7 +2734,7 @@ function RunsPage(props) {
|
|
|
2535
2734
|
return true
|
|
2536
2735
|
})
|
|
2537
2736
|
return h('div', null,
|
|
2538
|
-
h(RunHistoryCard, {
|
|
2737
|
+
h(RunHistoryCard, { history, showLink: false }),
|
|
2539
2738
|
h('section', { className: 'capital-automation-section' },
|
|
2540
2739
|
h('div', { className: 'capital-automation-sectionhead' },
|
|
2541
2740
|
h('h2', null, zh.runsTitle),
|
|
@@ -3401,11 +3600,21 @@ function InboxModal(props) {
|
|
|
3401
3600
|
}))))
|
|
3402
3601
|
}
|
|
3403
3602
|
|
|
3603
|
+
function browserTimeZone() {
|
|
3604
|
+
try {
|
|
3605
|
+
const zone = Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
3606
|
+
return typeof zone === 'string' && zone.length > 0 ? zone : 'Asia/Shanghai'
|
|
3607
|
+
} catch {
|
|
3608
|
+
return 'Asia/Shanghai'
|
|
3609
|
+
}
|
|
3610
|
+
}
|
|
3611
|
+
|
|
3404
3612
|
function AutomationPage(props) {
|
|
3405
3613
|
const { api, openSession } = props
|
|
3406
3614
|
const [meta, setMeta] = useState(null)
|
|
3407
3615
|
const [tasks, setTasks] = useState([])
|
|
3408
3616
|
const [runs, setRuns] = useState([])
|
|
3617
|
+
const [history, setHistory] = useState(null)
|
|
3409
3618
|
const [notifications, setNotifications] = useState([])
|
|
3410
3619
|
const [unread, setUnread] = useState(0)
|
|
3411
3620
|
const [selectedId, setSelectedId] = useState(null)
|
|
@@ -3441,10 +3650,11 @@ function AutomationPage(props) {
|
|
|
3441
3650
|
refreshSequenceRef.current = requestSequence
|
|
3442
3651
|
const request = (async () => {
|
|
3443
3652
|
try {
|
|
3444
|
-
const [nextMeta, nextTasks, nextRuns, nextNotifications] = await Promise.all([
|
|
3653
|
+
const [nextMeta, nextTasks, nextRuns, nextHistory, nextNotifications] = await Promise.all([
|
|
3445
3654
|
api.meta(),
|
|
3446
3655
|
api.tasks(),
|
|
3447
3656
|
api.runs('?limit=200'),
|
|
3657
|
+
api.history(`?days=${HISTORY_DAYS}&tz=${encodeURIComponent(browserTimeZone())}`),
|
|
3448
3658
|
api.notifications(),
|
|
3449
3659
|
])
|
|
3450
3660
|
if (!mountedRef.current || requestSequence !== refreshSequenceRef.current) return
|
|
@@ -3453,6 +3663,7 @@ function AutomationPage(props) {
|
|
|
3453
3663
|
tasksRef.current = nextTaskList
|
|
3454
3664
|
setTasks(nextTaskList)
|
|
3455
3665
|
setRuns(Array.isArray(nextRuns.runs) ? nextRuns.runs : [])
|
|
3666
|
+
setHistory(nextHistory ?? null)
|
|
3456
3667
|
setNotifications(Array.isArray(nextNotifications.notifications) ? nextNotifications.notifications : [])
|
|
3457
3668
|
setUnread(typeof nextNotifications.unread === 'number' ? nextNotifications.unread : 0)
|
|
3458
3669
|
setLoadError('')
|
|
@@ -3705,7 +3916,6 @@ function AutomationPage(props) {
|
|
|
3705
3916
|
onAction: (task, action) => { void performTaskAction(task, action) },
|
|
3706
3917
|
onRequestConfirm: requestConfirm,
|
|
3707
3918
|
onEdit: (id) => selectTask(id, 'config'),
|
|
3708
|
-
onGoRuns: () => switchTab('runs'),
|
|
3709
3919
|
onRequestCancelRun: (task, run) => requestConfirm('cancel-run', task, run),
|
|
3710
3920
|
schedulerActive,
|
|
3711
3921
|
storageReady,
|
|
@@ -3715,6 +3925,7 @@ function AutomationPage(props) {
|
|
|
3715
3925
|
? h(RunsSkeleton)
|
|
3716
3926
|
: h(RunsPage, {
|
|
3717
3927
|
runs,
|
|
3928
|
+
history,
|
|
3718
3929
|
tasks,
|
|
3719
3930
|
onCancel: (run) => requestConfirm('cancel-run', { id: run.taskId, title: run.taskTitle }, run),
|
|
3720
3931
|
onOpenSession: openSession,
|
package/automation/defaults.js
CHANGED
|
@@ -50,6 +50,7 @@ export const DEFAULT_AUTOMATION_SETTINGS = {
|
|
|
50
50
|
maxSteps: DEFAULT_MAX_STEPS,
|
|
51
51
|
maxToolCalls: DEFAULT_MAX_TOOL_CALLS,
|
|
52
52
|
inAppNotifications: true,
|
|
53
|
+
defaultWorkspaceId: '',
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
export const RUN_STATUSES = [
|
|
@@ -68,6 +69,7 @@ export const RUN_STATUSES = [
|
|
|
68
69
|
]
|
|
69
70
|
|
|
70
71
|
export const ACTIVE_RUN_STATUSES = ['queued', 'starting', 'running']
|
|
72
|
+
export const FAILURE_RUN_STATUSES = ['failed', 'timed_out', 'blocked_requires_user', 'unknown_after_restart']
|
|
71
73
|
export const TERMINAL_RUN_STATUSES = RUN_STATUSES.filter((status) => !ACTIVE_RUN_STATUSES.includes(status))
|
|
72
74
|
|
|
73
75
|
// A finished turn can be settled from its durable checkpoint; `unknown_after_restart`
|
package/automation/index.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* `ui-schedule` row and its `schedules` identities are untouched.
|
|
5
5
|
*/
|
|
6
6
|
import { readFileSync } from 'node:fs'
|
|
7
|
+
import { homedir } from 'node:os'
|
|
7
8
|
import { join } from 'node:path'
|
|
8
9
|
|
|
9
10
|
import { createRunRunner, buildRunPrompt, sessionTitleOf } from './runner.js'
|
|
@@ -14,7 +15,7 @@ import { createAutomationRouteHandler } from './route.js'
|
|
|
14
15
|
import { resolveAutomationSettings } from './settings.js'
|
|
15
16
|
import { installAutomationPolicy } from './policy.js'
|
|
16
17
|
import { createCalendarSource } from './calendar-source.js'
|
|
17
|
-
import { createWorkspaceCatalog } from './workspaces.js'
|
|
18
|
+
import { createWorkspaceCatalog, defaultWorkspacePath } from './workspaces.js'
|
|
18
19
|
import { sanitizeMessage } from './redact.js'
|
|
19
20
|
|
|
20
21
|
export const name = 'capital-automation'
|
|
@@ -28,10 +29,6 @@ function versionOf() {
|
|
|
28
29
|
}
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
function safeSegment(value) {
|
|
32
|
-
return String(value).replace(/[^A-Za-z0-9._-]+/g, '_').slice(0, 120)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
32
|
/**
|
|
36
33
|
* Build the runtime without Cordis services. Exported for tests and for the desktop carrier
|
|
37
34
|
* that supplies its own HTTP/auth plane.
|
|
@@ -42,7 +39,7 @@ export function createAutomationRuntime(options) {
|
|
|
42
39
|
const rawProfileName = String(profile.name)
|
|
43
40
|
const displayName = rawProfileName.slice(0, 200)
|
|
44
41
|
const home = profile.home ?? profile.dir ?? process.cwd()
|
|
45
|
-
const runDir = profile.runDir ??
|
|
42
|
+
const runDir = profile.runDir ?? defaultWorkspacePath(profile)
|
|
46
43
|
const ownerDir = profile.ownerDir ?? join(home, 'storages', 'capital-automation-locks')
|
|
47
44
|
const calendar = options.calendar ?? createCalendarSource({
|
|
48
45
|
cacheDir: join(home, 'storages', 'capital-automation-calendar'),
|
|
@@ -50,7 +47,14 @@ export function createAutomationRuntime(options) {
|
|
|
50
47
|
now,
|
|
51
48
|
})
|
|
52
49
|
const policyContext = options.policyContext ?? ctx
|
|
53
|
-
const workspaces = createWorkspaceCatalog({
|
|
50
|
+
const workspaces = createWorkspaceCatalog({
|
|
51
|
+
ctx,
|
|
52
|
+
displayName,
|
|
53
|
+
runDir,
|
|
54
|
+
displayHome: homedir(),
|
|
55
|
+
getDefaultWorkspaceId: () => getSettings()?.defaultWorkspaceId,
|
|
56
|
+
logger,
|
|
57
|
+
})
|
|
54
58
|
const store = createAutomationStore({
|
|
55
59
|
openDomain: () => storageDomain.open(domainSpec),
|
|
56
60
|
profile: { name: rawProfileName },
|