makersclaw 0.7.0 → 0.8.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/README.md +17 -4
- package/package.json +1 -1
- package/r/activity-log.json +52 -0
- package/r/agent-changes.json +51 -0
- package/r/agent-chat.json +53 -0
- package/r/agent-column.json +85 -0
- package/r/agent-dock.json +57 -0
- package/r/agent-kit.json +49 -0
- package/r/agent-marks.json +49 -0
- package/r/agent-pane.json +93 -0
- package/r/ai-action.json +56 -0
- package/r/annotate.json +4 -4
- package/r/approval-request.json +2 -2
- package/r/approval.json +54 -0
- package/r/ask-bar.json +50 -0
- package/r/audio-player.json +2 -2
- package/r/auth-form.json +2 -2
- package/r/automation-editor.json +66 -0
- package/r/automation-list.json +57 -0
- package/r/automation-model.json +118 -0
- package/r/automations-screen.json +71 -0
- package/r/board.json +2 -2
- package/r/calendar-view.json +3 -3
- package/r/cart.json +2 -2
- package/r/chart.json +5 -5
- package/r/chat-pane.json +84 -0
- package/r/chat.json +3 -3
- package/r/code-editor.json +2 -2
- package/r/comment-box.json +3 -3
- package/r/connect-card.json +51 -0
- package/r/contact-section.json +2 -2
- package/r/core.json +18 -6
- package/r/cost.json +51 -0
- package/r/csv-import.json +3 -3
- package/r/data-picker.json +60 -0
- package/r/data-table.json +2 -2
- package/r/diagram.json +2 -2
- package/r/doc-editor.json +2 -2
- package/r/doc-page.json +3 -3
- package/r/email-render.json +2 -2
- package/r/faq.json +2 -2
- package/r/file-upload.json +2 -2
- package/r/first-run.json +63 -0
- package/r/fix-note.json +57 -0
- package/r/flow-canvas.json +3 -3
- package/r/flow-editor.json +79 -0
- package/r/footer.json +3 -3
- package/r/gantt.json +3 -3
- package/r/job-panel.json +2 -2
- package/r/json-editor.json +3 -3
- package/r/library-shelf.json +56 -0
- package/r/map.json +2 -2
- package/r/margin-notes.json +67 -0
- package/r/menubar.json +2 -2
- package/r/mind-map.json +4 -4
- package/r/navigation-menu.json +2 -2
- package/r/needs-you.json +55 -0
- package/r/order-detail.json +2 -2
- package/r/org-chart.json +4 -4
- package/r/pace-bar.json +69 -0
- package/r/page-builder.json +3 -3
- package/r/payment-methods.json +2 -2
- package/r/pdf-document.json +2 -2
- package/r/pdf-viewer.json +3 -3
- package/r/product-detail.json +2 -2
- package/r/product-grid.json +3 -3
- package/r/record-form.json +2 -2
- package/r/record-grid.json +52 -0
- package/r/record-notes.json +4 -4
- package/r/registry.json +4089 -1510
- package/r/run-history.json +52 -0
- package/r/run-view.json +80 -0
- package/r/schedule-card.json +54 -0
- package/r/schedule-engine.json +79 -0
- package/r/schedule-gantt.json +83 -0
- package/r/schedule-ruler.json +64 -0
- package/r/search-dialog.json +2 -2
- package/r/settings-list.json +2 -2
- package/r/setup-checklist.json +2 -2
- package/r/share-dialog.json +2 -2
- package/r/sidebar.json +3 -3
- package/r/signature-pad.json +2 -2
- package/r/sketch-canvas.json +3 -3
- package/r/source-receipt.json +52 -0
- package/r/spreadsheet.json +3 -3
- package/r/stat-cards.json +3 -3
- package/r/step-picker.json +53 -0
- package/r/step-settings.json +72 -0
- package/r/suggestion.json +58 -0
- package/r/sum-bar.json +64 -0
- package/r/task-list.json +3 -3
- package/r/task-model.json +2 -2
- package/r/testimonials.json +2 -2
- package/r/tier-map.json +67 -0
- package/r/time-grid.json +73 -0
- package/r/time-strip.json +64 -0
- package/r/top-bar.json +66 -0
- package/r/transaction-list.json +2 -2
- package/r/trigger-picker.json +61 -0
- package/r/whiteboard.json +4 -4
- package/r/workflow-view.json +55 -0
- package/registry/blocks/activity-log/README.md +112 -0
- package/registry/blocks/activity-log/activity-log.jsx +351 -0
- package/registry/blocks/activity-log/block.json +33 -0
- package/registry/blocks/activity-log/demo.jsx +48 -0
- package/registry/blocks/activity-log/index.js +4 -0
- package/registry/blocks/agent-changes/README.md +94 -0
- package/registry/blocks/agent-changes/agent-changes.jsx +118 -0
- package/registry/blocks/agent-changes/block.json +28 -0
- package/registry/blocks/agent-changes/demo.jsx +39 -0
- package/registry/blocks/agent-changes/index.js +3 -0
- package/registry/blocks/agent-chat/README.md +145 -0
- package/registry/blocks/agent-chat/agent-chat.jsx +415 -0
- package/registry/blocks/agent-chat/block.json +30 -0
- package/registry/blocks/agent-chat/demo.jsx +165 -0
- package/registry/blocks/agent-chat/index.js +3 -0
- package/registry/blocks/agent-column/README.md +197 -0
- package/registry/blocks/agent-column/agent-column.jsx +492 -0
- package/registry/blocks/agent-column/block.json +67 -0
- package/registry/blocks/agent-column/column-picker.jsx +163 -0
- package/registry/blocks/agent-column/demo.jsx +50 -0
- package/registry/blocks/agent-column/index.js +7 -0
- package/registry/blocks/agent-column/model.js +164 -0
- package/registry/blocks/agent-column/receipt.jsx +483 -0
- package/registry/blocks/agent-column/spreadsheet-type.jsx +24 -0
- package/registry/blocks/agent-dock/README.md +210 -0
- package/registry/blocks/agent-dock/agent-dock.jsx +356 -0
- package/registry/blocks/agent-dock/block.json +47 -0
- package/registry/blocks/agent-dock/demo.jsx +153 -0
- package/registry/blocks/agent-dock/dock-model.js +118 -0
- package/registry/blocks/agent-dock/index.js +5 -0
- package/registry/blocks/agent-kit/README.md +73 -0
- package/registry/blocks/agent-kit/agent-kit.jsx +86 -0
- package/registry/blocks/agent-kit/block.json +10 -0
- package/registry/blocks/agent-kit/demo.jsx +35 -0
- package/registry/blocks/agent-kit/index.js +3 -0
- package/registry/blocks/agent-marks/README.md +80 -0
- package/registry/blocks/agent-marks/agent-marks.jsx +133 -0
- package/registry/blocks/agent-marks/block.json +27 -0
- package/registry/blocks/agent-marks/demo.jsx +49 -0
- package/registry/blocks/agent-marks/index.js +3 -0
- package/registry/blocks/agent-pane/README.md +225 -0
- package/registry/blocks/agent-pane/agent-pane.jsx +244 -0
- package/registry/blocks/agent-pane/block.json +41 -0
- package/registry/blocks/agent-pane/composer.jsx +6 -0
- package/registry/blocks/agent-pane/demo.jsx +233 -0
- package/registry/blocks/agent-pane/dock-context.js +10 -0
- package/registry/blocks/agent-pane/index.js +4 -0
- package/registry/blocks/agent-pane/prose.jsx +84 -0
- package/registry/blocks/agent-pane/questions.jsx +134 -0
- package/registry/blocks/agent-pane/transcript.jsx +268 -0
- package/registry/blocks/agent-pane/work.jsx +368 -0
- package/registry/blocks/ai-action/README.md +94 -0
- package/registry/blocks/ai-action/ai-action.jsx +188 -0
- package/registry/blocks/ai-action/block.json +34 -0
- package/registry/blocks/ai-action/demo.jsx +93 -0
- package/registry/blocks/ai-action/index.js +3 -0
- package/registry/blocks/annotate/README.md +14 -11
- package/registry/blocks/approval/README.md +145 -0
- package/registry/blocks/approval/approval.jsx +163 -0
- package/registry/blocks/approval/block.json +31 -0
- package/registry/blocks/approval/demo.jsx +121 -0
- package/registry/blocks/approval/index.js +3 -0
- package/registry/blocks/ask-bar/README.md +105 -0
- package/registry/blocks/ask-bar/ask-bar.jsx +166 -0
- package/registry/blocks/ask-bar/block.json +26 -0
- package/registry/blocks/ask-bar/demo.jsx +78 -0
- package/registry/blocks/ask-bar/index.js +3 -0
- package/registry/blocks/automation-editor/README.md +291 -0
- package/registry/blocks/automation-editor/automation-editor.jsx +656 -0
- package/registry/blocks/automation-editor/block.json +39 -0
- package/registry/blocks/automation-editor/demo.jsx +284 -0
- package/registry/blocks/automation-editor/index.js +4 -0
- package/registry/blocks/automation-editor/run-inputs.jsx +82 -0
- package/registry/blocks/automation-list/README.md +129 -0
- package/registry/blocks/automation-list/automation-list.jsx +476 -0
- package/registry/blocks/automation-list/block.json +35 -0
- package/registry/blocks/automation-list/demo.jsx +67 -0
- package/registry/blocks/automation-list/index.js +3 -0
- package/registry/blocks/automation-model/README.md +109 -0
- package/registry/blocks/automation-model/block.json +135 -0
- package/registry/blocks/automation-model/catalog.js +256 -0
- package/registry/blocks/automation-model/check.js +161 -0
- package/registry/blocks/automation-model/demo.jsx +42 -0
- package/registry/blocks/automation-model/edit.js +262 -0
- package/registry/blocks/automation-model/fixes.js +287 -0
- package/registry/blocks/automation-model/glyph.jsx +13 -0
- package/registry/blocks/automation-model/graph.js +410 -0
- package/registry/blocks/automation-model/index.js +33 -0
- package/registry/blocks/automation-model/overlay.js +175 -0
- package/registry/blocks/automation-model/parts.jsx +94 -0
- package/registry/blocks/automation-model/schedule.js +53 -0
- package/registry/blocks/automation-model/templates.js +128 -0
- package/registry/blocks/automation-model/values.js +29 -0
- package/registry/blocks/automation-model/words.js +439 -0
- package/registry/blocks/automations-screen/README.md +207 -0
- package/registry/blocks/automations-screen/automations-screen.jsx +452 -0
- package/registry/blocks/automations-screen/block.json +47 -0
- package/registry/blocks/automations-screen/demo.jsx +364 -0
- package/registry/blocks/automations-screen/index.js +4 -0
- package/registry/blocks/automations-screen/screen-model.js +129 -0
- package/registry/blocks/chart/cartesian.jsx +1 -1
- package/registry/blocks/chart/core.jsx +33 -23
- package/registry/blocks/chart/pie.jsx +1 -1
- package/registry/blocks/chat/README.md +4 -1
- package/registry/blocks/chat/demo.jsx +2 -2
- package/registry/blocks/chat-pane/README.md +143 -0
- package/registry/blocks/chat-pane/block.json +48 -0
- package/registry/blocks/chat-pane/chat-attach.js +166 -0
- package/registry/blocks/chat-pane/chat-composer.jsx +195 -0
- package/registry/blocks/chat-pane/chat-context.js +17 -0
- package/registry/blocks/chat-pane/chat-model.js +43 -0
- package/registry/blocks/chat-pane/chat-pane.jsx +398 -0
- package/registry/blocks/chat-pane/chats-list.jsx +52 -0
- package/registry/blocks/chat-pane/demo.jsx +103 -0
- package/registry/blocks/chat-pane/index.js +9 -0
- package/registry/blocks/connect-card/README.md +99 -0
- package/registry/blocks/connect-card/block.json +10 -0
- package/registry/blocks/connect-card/connect-card.jsx +103 -0
- package/registry/blocks/connect-card/demo.jsx +49 -0
- package/registry/blocks/connect-card/index.js +3 -0
- package/registry/blocks/cost/README.md +115 -0
- package/registry/blocks/cost/block.json +10 -0
- package/registry/blocks/cost/cost.jsx +176 -0
- package/registry/blocks/cost/demo.jsx +66 -0
- package/registry/blocks/cost/index.js +3 -0
- package/registry/blocks/data-picker/README.md +81 -0
- package/registry/blocks/data-picker/block.json +38 -0
- package/registry/blocks/data-picker/data-picker.jsx +210 -0
- package/registry/blocks/data-picker/demo.jsx +34 -0
- package/registry/blocks/data-picker/fields.js +89 -0
- package/registry/blocks/data-picker/index.js +4 -0
- package/registry/blocks/data-table/demo.jsx +1 -1
- package/registry/blocks/first-run/README.md +118 -0
- package/registry/blocks/first-run/block.json +11 -0
- package/registry/blocks/first-run/demo.jsx +88 -0
- package/registry/blocks/first-run/first-run-model.js +51 -0
- package/registry/blocks/first-run/first-run.jsx +156 -0
- package/registry/blocks/first-run/index.js +5 -0
- package/registry/blocks/first-run/probe-rules.js +26 -0
- package/registry/blocks/fix-note/README.md +79 -0
- package/registry/blocks/fix-note/block.json +35 -0
- package/registry/blocks/fix-note/demo.jsx +30 -0
- package/registry/blocks/fix-note/fix-note.jsx +235 -0
- package/registry/blocks/fix-note/index.js +3 -0
- package/registry/blocks/flow-canvas/README.md +15 -8
- package/registry/blocks/flow-editor/README.md +108 -0
- package/registry/blocks/flow-editor/block.json +44 -0
- package/registry/blocks/flow-editor/demo.jsx +115 -0
- package/registry/blocks/flow-editor/flow-canvas.jsx +568 -0
- package/registry/blocks/flow-editor/flow-editor.jsx +154 -0
- package/registry/blocks/flow-editor/index.js +6 -0
- package/registry/blocks/flow-editor/minimap.jsx +78 -0
- package/registry/blocks/flow-editor/outline.jsx +134 -0
- package/registry/blocks/footer/README.md +3 -2
- package/registry/blocks/library-shelf/README.md +117 -0
- package/registry/blocks/library-shelf/block.json +28 -0
- package/registry/blocks/library-shelf/demo.jsx +68 -0
- package/registry/blocks/library-shelf/index.js +3 -0
- package/registry/blocks/library-shelf/library-shelf.jsx +211 -0
- package/registry/blocks/library-shelf/probe-rules.js +15 -0
- package/registry/blocks/margin-notes/README.md +222 -0
- package/registry/blocks/margin-notes/block.json +10 -0
- package/registry/blocks/margin-notes/demo.jsx +144 -0
- package/registry/blocks/margin-notes/index.js +20 -0
- package/registry/blocks/margin-notes/margin-model.js +224 -0
- package/registry/blocks/margin-notes/margin-notes.jsx +465 -0
- package/registry/blocks/mind-map/README.md +4 -2
- package/registry/blocks/needs-you/README.md +177 -0
- package/registry/blocks/needs-you/block.json +34 -0
- package/registry/blocks/needs-you/demo.jsx +143 -0
- package/registry/blocks/needs-you/index.js +3 -0
- package/registry/blocks/needs-you/needs-you.jsx +441 -0
- package/registry/blocks/org-chart/README.md +4 -2
- package/registry/blocks/pace-bar/README.md +230 -0
- package/registry/blocks/pace-bar/block.json +10 -0
- package/registry/blocks/pace-bar/demo.jsx +255 -0
- package/registry/blocks/pace-bar/index.js +22 -0
- package/registry/blocks/pace-bar/pace-bar.jsx +461 -0
- package/registry/blocks/pace-bar/pace-model.js +288 -0
- package/registry/blocks/page-builder/README.md +1 -1
- package/registry/blocks/page-builder/demo.jsx +3 -3
- package/registry/blocks/pdf-viewer/README.md +4 -1
- package/registry/blocks/record-grid/README.md +100 -0
- package/registry/blocks/record-grid/block.json +28 -0
- package/registry/blocks/record-grid/demo.jsx +43 -0
- package/registry/blocks/record-grid/index.js +3 -0
- package/registry/blocks/record-grid/record-grid.jsx +251 -0
- package/registry/blocks/run-history/README.md +138 -0
- package/registry/blocks/run-history/block.json +10 -0
- package/registry/blocks/run-history/demo.jsx +74 -0
- package/registry/blocks/run-history/index.js +3 -0
- package/registry/blocks/run-history/run-history.jsx +218 -0
- package/registry/blocks/run-view/README.md +174 -0
- package/registry/blocks/run-view/block.json +50 -0
- package/registry/blocks/run-view/demo.jsx +286 -0
- package/registry/blocks/run-view/index.js +5 -0
- package/registry/blocks/run-view/run-controls.jsx +266 -0
- package/registry/blocks/run-view/run-view.jsx +236 -0
- package/registry/blocks/run-view/runs-list.jsx +99 -0
- package/registry/blocks/schedule-card/README.md +121 -0
- package/registry/blocks/schedule-card/block.json +10 -0
- package/registry/blocks/schedule-card/demo.jsx +82 -0
- package/registry/blocks/schedule-card/index.js +3 -0
- package/registry/blocks/schedule-card/schedule-card.jsx +136 -0
- package/registry/blocks/schedule-engine/README.md +137 -0
- package/registry/blocks/schedule-engine/block.json +10 -0
- package/registry/blocks/schedule-engine/demo.jsx +82 -0
- package/registry/blocks/schedule-engine/engine.js +769 -0
- package/registry/blocks/schedule-engine/index.js +9 -0
- package/registry/blocks/schedule-engine/ops.js +71 -0
- package/registry/blocks/schedule-engine/place.js +294 -0
- package/registry/blocks/schedule-engine/tree.js +79 -0
- package/registry/blocks/schedule-engine/waits.js +80 -0
- package/registry/blocks/schedule-gantt/README.md +159 -0
- package/registry/blocks/schedule-gantt/block.json +10 -0
- package/registry/blocks/schedule-gantt/demo.jsx +223 -0
- package/registry/blocks/schedule-gantt/gantt-model.js +219 -0
- package/registry/blocks/schedule-gantt/geometry.js +176 -0
- package/registry/blocks/schedule-gantt/index.js +26 -0
- package/registry/blocks/schedule-gantt/schedule-gantt.jsx +882 -0
- package/registry/blocks/schedule-gantt/sheet.jsx +372 -0
- package/registry/blocks/schedule-gantt/track.jsx +190 -0
- package/registry/blocks/schedule-ruler/README.md +148 -0
- package/registry/blocks/schedule-ruler/block.json +10 -0
- package/registry/blocks/schedule-ruler/demo.jsx +181 -0
- package/registry/blocks/schedule-ruler/index.js +21 -0
- package/registry/blocks/schedule-ruler/ruler-model.js +239 -0
- package/registry/blocks/schedule-ruler/schedule-ruler.jsx +548 -0
- package/registry/blocks/sidebar/README.md +23 -13
- package/registry/blocks/sketch-canvas/README.md +15 -9
- package/registry/blocks/source-receipt/README.md +97 -0
- package/registry/blocks/source-receipt/block.json +30 -0
- package/registry/blocks/source-receipt/demo.jsx +71 -0
- package/registry/blocks/source-receipt/index.js +3 -0
- package/registry/blocks/source-receipt/source-receipt.jsx +107 -0
- package/registry/blocks/spreadsheet/spreadsheet.jsx +15 -1
- package/registry/blocks/step-picker/README.md +78 -0
- package/registry/blocks/step-picker/block.json +31 -0
- package/registry/blocks/step-picker/demo.jsx +23 -0
- package/registry/blocks/step-picker/index.js +3 -0
- package/registry/blocks/step-picker/step-picker.jsx +144 -0
- package/registry/blocks/step-settings/README.md +100 -0
- package/registry/blocks/step-settings/block.json +44 -0
- package/registry/blocks/step-settings/demo.jsx +93 -0
- package/registry/blocks/step-settings/fields.jsx +58 -0
- package/registry/blocks/step-settings/flow-settings.jsx +115 -0
- package/registry/blocks/step-settings/index.js +5 -0
- package/registry/blocks/step-settings/step-settings.jsx +290 -0
- package/registry/blocks/suggestion/README.md +171 -0
- package/registry/blocks/suggestion/block.json +40 -0
- package/registry/blocks/suggestion/demo.jsx +91 -0
- package/registry/blocks/suggestion/index.js +3 -0
- package/registry/blocks/suggestion/suggestion.jsx +436 -0
- package/registry/blocks/sum-bar/README.md +122 -0
- package/registry/blocks/sum-bar/block.json +10 -0
- package/registry/blocks/sum-bar/demo.jsx +110 -0
- package/registry/blocks/sum-bar/index.js +5 -0
- package/registry/blocks/sum-bar/sum-bar.jsx +209 -0
- package/registry/blocks/sum-bar/sum-model.js +126 -0
- package/registry/blocks/tier-map/README.md +217 -0
- package/registry/blocks/tier-map/block.json +10 -0
- package/registry/blocks/tier-map/demo.jsx +222 -0
- package/registry/blocks/tier-map/index.js +24 -0
- package/registry/blocks/tier-map/tier-map.jsx +574 -0
- package/registry/blocks/tier-map/tier-model.js +318 -0
- package/registry/blocks/time-grid/README.md +305 -0
- package/registry/blocks/time-grid/block.json +10 -0
- package/registry/blocks/time-grid/demo.jsx +279 -0
- package/registry/blocks/time-grid/grid-model.js +322 -0
- package/registry/blocks/time-grid/index.js +25 -0
- package/registry/blocks/time-grid/time-grid.jsx +1325 -0
- package/registry/blocks/time-strip/README.md +141 -0
- package/registry/blocks/time-strip/block.json +10 -0
- package/registry/blocks/time-strip/demo.jsx +129 -0
- package/registry/blocks/time-strip/index.js +19 -0
- package/registry/blocks/time-strip/strip-model.js +178 -0
- package/registry/blocks/time-strip/time-strip.jsx +260 -0
- package/registry/blocks/top-bar/README.md +149 -0
- package/registry/blocks/top-bar/block.json +35 -0
- package/registry/blocks/top-bar/demo.jsx +78 -0
- package/registry/blocks/top-bar/index.js +5 -0
- package/registry/blocks/top-bar/probe-rules.js +74 -0
- package/registry/blocks/top-bar/top-bar-model.js +152 -0
- package/registry/blocks/top-bar/top-bar.jsx +244 -0
- package/registry/blocks/trigger-picker/README.md +98 -0
- package/registry/blocks/trigger-picker/block.json +38 -0
- package/registry/blocks/trigger-picker/demo.jsx +59 -0
- package/registry/blocks/trigger-picker/index.js +3 -0
- package/registry/blocks/trigger-picker/trigger-picker.jsx +374 -0
- package/registry/blocks/whiteboard/README.md +20 -14
- package/registry/blocks/workflow-view/README.md +123 -0
- package/registry/blocks/workflow-view/block.json +10 -0
- package/registry/blocks/workflow-view/demo.jsx +90 -0
- package/registry/blocks/workflow-view/index.js +3 -0
- package/registry/blocks/workflow-view/workflow-view.jsx +203 -0
- package/src/cli.mjs +2 -2
- package/src/install.mjs +100 -22
- package/src/registry.mjs +37 -3
- package/templates/next/browser/lib/data.js +6 -1
- package/templates/next/files/README.md +909 -26
- package/templates/next/files/app/shell.jsx +54 -37
- package/templates/next/files/check/automation-format.ts +2103 -0
- package/templates/next/files/check/automation-package.ts +142 -0
- package/templates/next/files/check/run.mjs +1043 -12
- package/templates/next/files/components/ui/field.jsx +17 -4
- package/templates/next/files/components/ui/icon.jsx +13 -0
- package/templates/next/files/components/ui/index.js +3 -1
- package/templates/next/files/components/ui/layout.jsx +382 -0
- package/templates/next/files/components/ui/notice.jsx +70 -0
- package/templates/next/files/components/ui/page.jsx +124 -0
- package/templates/next/files/components/ui/stat.jsx +22 -7
- package/templates/next/files/components/ui/toast.jsx +6 -3
- package/templates/next/files/lib/automations.js +660 -0
- package/templates/next/files/lib/claw.js +244 -5
- package/templates/next/files/lib/door.js +92 -0
- package/templates/next/files/lib/hooks.js +1694 -6
- package/templates/next/files/lib/platform.js +611 -14
- package/templates/next/template.json +22 -3
|
@@ -0,0 +1,660 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
4
|
+
import { clawUrl } from "./claw";
|
|
5
|
+
|
|
6
|
+
// The automations engine, from an app's own page (0.8.1, D26, Phase 6.2):
|
|
7
|
+
// the doors under `_claw/automations…` and the hooks the automation blocks
|
|
8
|
+
// draw. An app lists its own automations, opens one, edits and saves it,
|
|
9
|
+
// keeps a version, switches it on and runs it, and reads a run step by
|
|
10
|
+
// step, all inside the app, in the app's theme. The platform decides who
|
|
11
|
+
// may do what (any member edits and runs, until roles); a hidden button is
|
|
12
|
+
// never the guard.
|
|
13
|
+
//
|
|
14
|
+
// useAutomations() the list, the switch, a new one, the starting points the platform
|
|
15
|
+
// keeps, and "Describe it" (an automation drafted from a sentence)
|
|
16
|
+
// useAutomation(id) one: its versions and runs; save, keep, go live, test (a dry run:
|
|
17
|
+
// it reads real data and changes nothing)
|
|
18
|
+
// useAutomationRun(runId) one run, followed while it goes
|
|
19
|
+
//
|
|
20
|
+
// Every call resolves { ok: true, status, data } or { ok: false, status,
|
|
21
|
+
// error, code?, problems?, unavailable } and never throws. `problems` is
|
|
22
|
+
// the platform's list of what is wrong with a definition it refused (one
|
|
23
|
+
// plain line each); `unavailable: true` (a claw from before the engine, or
|
|
24
|
+
// no gateway) is a quiet "not set up yet", never an error. The hooks poll
|
|
25
|
+
// only while the page is shown, back off while the door is missing, and
|
|
26
|
+
// abort on unmount, as the other kit hooks do (lib/hooks.js).
|
|
27
|
+
//
|
|
28
|
+
// Re-exported from lib/hooks.js, so a surface imports them from there.
|
|
29
|
+
|
|
30
|
+
const BACKOFF_MS = 60000;
|
|
31
|
+
/** How often a list or one automation looks again while it is idle. */
|
|
32
|
+
const IDLE_MS = 30000;
|
|
33
|
+
/** ...and while a run is going. */
|
|
34
|
+
const RUNNING_MS = 5000;
|
|
35
|
+
/** One run, followed. */
|
|
36
|
+
const RUN_FOLLOW_MS = 2500;
|
|
37
|
+
/** A run still going or waiting on its owner. */
|
|
38
|
+
const ACTIVE = new Set(["queued", "running", "waiting", "needs_you"]);
|
|
39
|
+
|
|
40
|
+
const listOf = (v) => (Array.isArray(v) ? v : []);
|
|
41
|
+
const segment = (id) => {
|
|
42
|
+
const text = typeof id === "string" || typeof id === "number" ? String(id).trim() : "";
|
|
43
|
+
return text ? encodeURIComponent(text) : null;
|
|
44
|
+
};
|
|
45
|
+
const noId = (what) => Promise.resolve({ ok: false, status: 0, error: `no ${what} to ask about`, unavailable: false });
|
|
46
|
+
|
|
47
|
+
/* ---- the doors ------------------------------------------------------------ */
|
|
48
|
+
|
|
49
|
+
/** One call to a _claw route. Like lib/claw.js's claw(), but a refusal keeps
|
|
50
|
+
* its `problems`: the lines the editor shows against the steps. */
|
|
51
|
+
async function call(path, { method = "GET", body, signal } = {}) {
|
|
52
|
+
let res;
|
|
53
|
+
try {
|
|
54
|
+
res = await fetch(clawUrl(path), {
|
|
55
|
+
method,
|
|
56
|
+
credentials: "same-origin",
|
|
57
|
+
cache: "no-store",
|
|
58
|
+
headers: { accept: "application/json", ...(body !== undefined ? { "content-type": "application/json" } : {}) },
|
|
59
|
+
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
60
|
+
signal,
|
|
61
|
+
});
|
|
62
|
+
} catch (error) {
|
|
63
|
+
if (error?.name === "AbortError") return { ok: false, status: 0, error: "aborted", aborted: true };
|
|
64
|
+
return { ok: false, status: 0, error: "not reachable", unavailable: true };
|
|
65
|
+
}
|
|
66
|
+
const text = await res.text().catch(() => "");
|
|
67
|
+
let parsed = null;
|
|
68
|
+
try {
|
|
69
|
+
parsed = JSON.parse(text);
|
|
70
|
+
} catch {
|
|
71
|
+
/* not JSON: the words are the text */
|
|
72
|
+
}
|
|
73
|
+
if (res.ok) return { ok: true, status: res.status, data: parsed ?? (text || null) };
|
|
74
|
+
const failure = parsed && typeof parsed === "object" ? parsed : {};
|
|
75
|
+
const error = failure.error || failure.message || text || res.statusText || `HTTP ${res.status}`;
|
|
76
|
+
const code = typeof failure.code === "string" && failure.code ? failure.code : undefined;
|
|
77
|
+
// A 404 with no code is a gateway with no such route (a claw from before
|
|
78
|
+
// the engine); with one it is the route refusing ("not_found").
|
|
79
|
+
if (res.status === 404 && !code) return { ok: false, status: 404, error: "not available yet", unavailable: true };
|
|
80
|
+
const problems = Array.isArray(failure.problems) ? failure.problems.filter((p) => typeof p === "string") : undefined;
|
|
81
|
+
// A refusal that asks something back ("Describe it" needs an answer) keeps its questions.
|
|
82
|
+
const questions = Array.isArray(failure.questions) ? failure.questions.filter((q) => typeof q === "string" && q.trim()) : undefined;
|
|
83
|
+
// A `changed` refusal says which version is the newest (`latest`).
|
|
84
|
+
const latest = Number.isInteger(failure.latest) ? failure.latest : undefined;
|
|
85
|
+
return { ok: false, status: res.status, error, ...(code ? { code } : {}), ...(latest !== undefined ? { latest } : {}), ...(problems ? { problems } : {}), ...(questions ? { questions } : {}), unavailable: false };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** GET _claw/automations → { automations: [{ id, key, name, status, live_version,
|
|
89
|
+
* draft_version, next_run_at, tz, trigger, last_run, active_runs, source, edited … }] }:
|
|
90
|
+
* this app's automations. */
|
|
91
|
+
export function automations({ signal } = {}) {
|
|
92
|
+
return call("automations", { signal });
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** GET _claw/automations/<id> → { automation, live, draft, versions, estimate,
|
|
96
|
+
* estimate_draft }: one, with the version that runs, the draft ahead of it
|
|
97
|
+
* and what a run is expected to cost. */
|
|
98
|
+
export function automation(id, { signal } = {}) {
|
|
99
|
+
const which = segment(id);
|
|
100
|
+
return which ? call(`automations/${which}`, { signal }) : noId("automation");
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** POST _claw/automations { definition, note?, go_live? } → { id, version, status }:
|
|
104
|
+
* a new automation, as a draft. A refusal carries `problems`. */
|
|
105
|
+
export function createAutomation(body) {
|
|
106
|
+
return call("automations", { method: "POST", body });
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** PATCH _claw/automations/<id>: { definition, expect_version?, note? } saves a draft
|
|
110
|
+
* version; { keep: true } makes the draft the version that runs; { status: "on" | "off" }
|
|
111
|
+
* is the Live switch. `keep` and `status: "on"` are refused with `needs_test` until a test of
|
|
112
|
+
* that version passed; `skip_test: true` on the same body is the member's own, deliberate
|
|
113
|
+
* override (an agent never sends it). A refusal carries its `code` (`invalid_automation`,
|
|
114
|
+
* `changed`, `no_live`, `needs_test`…) and, for a definition, `problems`. */
|
|
115
|
+
export function saveAutomation(id, body) {
|
|
116
|
+
const which = segment(id);
|
|
117
|
+
return which ? call(`automations/${which}`, { method: "PATCH", body }) : noId("automation");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** POST _claw/automations/<id>/run { inputs?, version?, dry?, sample? } → { run_id, test, dry,
|
|
121
|
+
* version }: one run, on the member's press. `dry: true` is a TEST: it reads real data and
|
|
122
|
+
* changes nothing (a version that is not the live one always runs as a test); `sample` is a
|
|
123
|
+
* pretend trigger payload. Say its price on the control (the automation's `estimate`). */
|
|
124
|
+
export function runAutomation(id, body = {}) {
|
|
125
|
+
const which = segment(id);
|
|
126
|
+
return which ? call(`automations/${which}/run`, { method: "POST", body }) : noId("automation");
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** GET _claw/automations/<id>/runs → { runs: [{ id, status, trigger_type, is_test,
|
|
130
|
+
* created_at, started_at, ended_at, credits, error }] }, newest first. */
|
|
131
|
+
export function automationRuns(id, { limit, signal } = {}) {
|
|
132
|
+
const which = segment(id);
|
|
133
|
+
if (!which) return noId("automation");
|
|
134
|
+
const n = Number.isInteger(limit) && limit >= 1 ? `?limit=${limit}` : "";
|
|
135
|
+
return call(`automations/${which}/runs${n}`, { signal });
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** GET _claw/automation-runs/<id> → { run, automation, steps: [{ path, step_id, kind,
|
|
139
|
+
* attempt, status, credits, error, input?, output?, fix? }], steps_visible }: one run
|
|
140
|
+
* with its steps. A step's data is for those who may read it (`steps_visible`). */
|
|
141
|
+
export function automationRun(runId, { signal } = {}) {
|
|
142
|
+
const which = segment(runId);
|
|
143
|
+
return which ? call(`automation-runs/${which}`, { signal }) : noId("run");
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** POST _claw/automation-runs/<id>/control { op, path?, input?, confirm_not_sent?,
|
|
147
|
+
* raise_cap_by?, confirm_resend? } → { run_id, status, paused, path? }: the controls of a
|
|
148
|
+
* run. `op`: pause · resume · stop · retry · skip · edit · retry_from (start over at a
|
|
149
|
+
* top-level step, running it and every step after it again, on a finished or parked run).
|
|
150
|
+
* A refusal's `code` is `not_found` · `not_active` · `not_blocked` · `running` ·
|
|
151
|
+
* `needs_confirm` · `unknown_step` · `output_needed` · `bad_request` · `bad_step` · `busy` ·
|
|
152
|
+
* `not_controllable` · `needs_confirm_resend` (a step from there on already sent: confirm
|
|
153
|
+
* with `confirm_resend: true`). */
|
|
154
|
+
export function controlRun(runId, body) {
|
|
155
|
+
const which = segment(runId);
|
|
156
|
+
return which ? call(`automation-runs/${which}/control`, { method: "POST", body }) : noId("run");
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** DELETE _claw/automations/<id> → { ok, id, name, was_status, followers: [{ id, key, name }], undo: true }:
|
|
160
|
+
* the automation is deleted (switched off first; its runs stay in history). `followers` are the
|
|
161
|
+
* automations that started when it finished and now never will. A refusal carries its `code` and
|
|
162
|
+
* plain words in `error`: `not_allowed` (owner or admin only), `running` (a run is going),
|
|
163
|
+
* `legacy` (an app's own job), `package` (shipped with the app), `not_found`. Resolved, never thrown. */
|
|
164
|
+
export function deleteAutomation(id) {
|
|
165
|
+
const which = segment(id);
|
|
166
|
+
return which ? call(`automations/${which}`, { method: "DELETE" }) : noId("automation");
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** POST _claw/automations/<id>/restore → { ok, id, name, status }: the Undo of a delete. A refusal's
|
|
170
|
+
* `code` is `key_taken` (another automation took its name since) · `not_allowed` · `not_found`. */
|
|
171
|
+
export function restoreAutomation(id) {
|
|
172
|
+
const which = segment(id);
|
|
173
|
+
return which ? call(`automations/${which}/restore`, { method: "POST", body: {} }) : noId("automation");
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** POST _claw/automations/<id>/fixes/<fix> { decision: "keep" | "drop" } → the answer:
|
|
177
|
+
* the change the agent drafted from a fix, kept (a NEW version that runs) or dropped.
|
|
178
|
+
* A refusal's `code` is `stale` (the live version moved) · `not_allowed` · `moved`
|
|
179
|
+
* (decided already) · `not_found`. */
|
|
180
|
+
export function decideFix(id, fixId, decision) {
|
|
181
|
+
const which = segment(id);
|
|
182
|
+
const fix = segment(fixId);
|
|
183
|
+
if (!which) return noId("automation");
|
|
184
|
+
return fix ? call(`automations/${which}/fixes/${fix}`, { method: "POST", body: { decision } }) : noId("fix");
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** The starting points the platform keeps are the desk's (`GET /api/automation-starts`); claw's
|
|
188
|
+
* app door (`_claw/…`) does not serve them, so asking it was a request that always failed and,
|
|
189
|
+
* as an unavailable read, was asked again every BACKOFF_MS (found by the store's app test
|
|
190
|
+
* suite, 2026-10-13). An app brings its own starting points (its `*-starts.js`), so this
|
|
191
|
+
* answers none without a request: { ok: true, data: { source: "code", starts: [] } }. When
|
|
192
|
+
* claw serves the route for apps, this is the one line that asks it again. */
|
|
193
|
+
export function automationStarts() {
|
|
194
|
+
return Promise.resolve({ ok: true, status: 200, data: { source: "code", starts: [] } });
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** POST _claw/automation-draft { ask, app? } → { ok, id, key, name, version, steps }: "Describe it",
|
|
198
|
+
* one model call as the member. A refusal is `{ ok: false, code, error, questions? }`;
|
|
199
|
+
* `needs_answer` carries the `questions` to ask back. */
|
|
200
|
+
export function draftAutomation(body) {
|
|
201
|
+
return call("automation-draft", { method: "POST", body });
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export const automationsApi = { automations, automation, createAutomation, saveAutomation, runAutomation, automationRuns, automationRun, controlRun, decideFix, automationStarts, draftAutomation, deleteAutomation, restoreAutomation };
|
|
205
|
+
|
|
206
|
+
/* ---- following a door ----------------------------------------------------- */
|
|
207
|
+
|
|
208
|
+
/** Is the page shown: the browser tab visible AND the App tab on screen?
|
|
209
|
+
* (lib/hooks.js watchShown, the same rule.) */
|
|
210
|
+
function watchShown(onShown) {
|
|
211
|
+
let onScreen = true;
|
|
212
|
+
const shown = () => document.visibilityState === "visible" && onScreen;
|
|
213
|
+
const onVisible = () => {
|
|
214
|
+
if (shown()) onShown();
|
|
215
|
+
};
|
|
216
|
+
let observer = null;
|
|
217
|
+
if (typeof IntersectionObserver === "function") {
|
|
218
|
+
observer = new IntersectionObserver((entries) => {
|
|
219
|
+
const was = onScreen;
|
|
220
|
+
onScreen = entries[entries.length - 1].isIntersecting;
|
|
221
|
+
if (onScreen && !was && shown()) onShown();
|
|
222
|
+
});
|
|
223
|
+
observer.observe(document.documentElement);
|
|
224
|
+
}
|
|
225
|
+
document.addEventListener("visibilitychange", onVisible);
|
|
226
|
+
return {
|
|
227
|
+
shown,
|
|
228
|
+
stop() {
|
|
229
|
+
observer?.disconnect();
|
|
230
|
+
document.removeEventListener("visibilitychange", onVisible);
|
|
231
|
+
},
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const READING = { key: null, data: null, loading: true, error: null, code: null, unavailable: false };
|
|
236
|
+
const NOTHING = { ...READING, loading: false };
|
|
237
|
+
|
|
238
|
+
/** One door, followed: read when `key` changes and on reload(), then again
|
|
239
|
+
* after `next(data)` ms while the page is shown (0 or nothing: no timer).
|
|
240
|
+
* A missing door is looked at once a minute and leaves `error` null; a
|
|
241
|
+
* refusal keeps the last answer and sets `error` and `code`. */
|
|
242
|
+
function useFollow(read, key, { enabled = true, next } = {}) {
|
|
243
|
+
const [state, setState] = useState(READING);
|
|
244
|
+
const [tick, setTick] = useState(0);
|
|
245
|
+
const readRef = useRef(read);
|
|
246
|
+
readRef.current = read;
|
|
247
|
+
const nextRef = useRef(next);
|
|
248
|
+
nextRef.current = next;
|
|
249
|
+
const reload = useCallback(() => setTick((t) => t + 1), []);
|
|
250
|
+
|
|
251
|
+
useEffect(() => {
|
|
252
|
+
if (!enabled) return undefined;
|
|
253
|
+
const controller = new AbortController();
|
|
254
|
+
let alive = true;
|
|
255
|
+
let timer = null;
|
|
256
|
+
let inflight = false;
|
|
257
|
+
let again = false;
|
|
258
|
+
let unavailable = false;
|
|
259
|
+
let last = null;
|
|
260
|
+
|
|
261
|
+
const schedule = () => {
|
|
262
|
+
clearTimeout(timer);
|
|
263
|
+
timer = null;
|
|
264
|
+
if (!alive || !screen.shown()) return;
|
|
265
|
+
if (again) {
|
|
266
|
+
again = false;
|
|
267
|
+
load();
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
const ms = unavailable ? BACKOFF_MS : Number(nextRef.current?.(last)) || 0;
|
|
271
|
+
if (ms > 0) timer = setTimeout(load, ms);
|
|
272
|
+
};
|
|
273
|
+
const load = async () => {
|
|
274
|
+
clearTimeout(timer);
|
|
275
|
+
timer = null;
|
|
276
|
+
if (!alive || !screen.shown()) return;
|
|
277
|
+
if (inflight) {
|
|
278
|
+
again = true;
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
inflight = true;
|
|
282
|
+
const r = await readRef.current(controller.signal);
|
|
283
|
+
inflight = false;
|
|
284
|
+
if (!alive || r?.aborted) return;
|
|
285
|
+
unavailable = Boolean(r?.unavailable);
|
|
286
|
+
if (r?.ok && r.data && typeof r.data === "object" && !Array.isArray(r.data)) {
|
|
287
|
+
last = r.data;
|
|
288
|
+
setState({ key, data: r.data, loading: false, error: null, code: null, unavailable: false });
|
|
289
|
+
} else {
|
|
290
|
+
const why = r?.ok
|
|
291
|
+
? { error: "the platform answered something unexpected", code: null, unavailable: false }
|
|
292
|
+
: unavailable
|
|
293
|
+
? { error: null, code: null, unavailable: true }
|
|
294
|
+
: { error: r?.error || "the platform did not answer", code: r?.code ?? null, unavailable: false };
|
|
295
|
+
setState((s) => ({ key, data: s.key === key ? s.data : null, loading: false, ...why }));
|
|
296
|
+
}
|
|
297
|
+
schedule();
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
const screen = watchShown(load);
|
|
301
|
+
load();
|
|
302
|
+
return () => {
|
|
303
|
+
alive = false;
|
|
304
|
+
controller.abort();
|
|
305
|
+
clearTimeout(timer);
|
|
306
|
+
screen.stop();
|
|
307
|
+
};
|
|
308
|
+
}, [key, enabled, tick]);
|
|
309
|
+
|
|
310
|
+
// A state kept for another read is not shown.
|
|
311
|
+
const current = enabled ? (state.key === key ? state : READING) : NOTHING;
|
|
312
|
+
return { data: current.data, loading: current.loading, error: current.error, code: current.code, unavailable: current.unavailable, reload };
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/** A press that is out: set while it runs, cleared after, only if still mounted. */
|
|
316
|
+
function useBusy() {
|
|
317
|
+
const [busy, setBusy] = useState(null);
|
|
318
|
+
const alive = useRef(true);
|
|
319
|
+
useEffect(() => {
|
|
320
|
+
alive.current = true;
|
|
321
|
+
return () => {
|
|
322
|
+
alive.current = false;
|
|
323
|
+
};
|
|
324
|
+
}, []);
|
|
325
|
+
const press = useCallback(async (what, work) => {
|
|
326
|
+
if (alive.current) setBusy(what);
|
|
327
|
+
try {
|
|
328
|
+
return await work();
|
|
329
|
+
} finally {
|
|
330
|
+
if (alive.current) setBusy(null);
|
|
331
|
+
}
|
|
332
|
+
}, []);
|
|
333
|
+
return [busy, press, alive];
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/* ---- the hooks ------------------------------------------------------------- */
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* This app's automations, for the `automation-list` block (GET _claw/automations):
|
|
340
|
+
*
|
|
341
|
+
* const { automations, busy, setStatus, create } = useAutomations();
|
|
342
|
+
*
|
|
343
|
+
* automations the rows, as the door sends them (see automations())
|
|
344
|
+
* busy { [id]: "switch" } while a press is out
|
|
345
|
+
* setStatus(id, on) the switch: resolves the answer, then looks again.
|
|
346
|
+
* Refusals: `no_live` (keep a version first), `not_found`
|
|
347
|
+
* create(definition, { note?, goLive? }) a new automation; resolves
|
|
348
|
+
* { ok, data: { id } } or { ok: false, error, problems }
|
|
349
|
+
* starts the starting points the platform keeps (the desk's GET automation-starts), raw:
|
|
350
|
+
* [{ id, name, blurb, needs, ai, credits, per?, glyph, definition }]. Give the list
|
|
351
|
+
* `startsFromPlatform(starts)` (automation-model). [] when the platform has none
|
|
352
|
+
* or this surface may not read them; never an error.
|
|
353
|
+
* describe(ask, app?) "Describe it": the platform drafts an automation from a sentence (one
|
|
354
|
+
* model call, as the member). Resolves { ok: true, id, key, name, version, steps },
|
|
355
|
+
* or { ok: false, error, code, questions? } (`needs_answer` carries `questions`),
|
|
356
|
+
* or { ok: false, code: "unavailable" } on a claw without it; never thrown.
|
|
357
|
+
* describing true while it drafts
|
|
358
|
+
* triggers what can start an automation here, for the `trigger-picker` block:
|
|
359
|
+
* { choices: [{ type, label, hint, available, why? }],
|
|
360
|
+
* apps: [{ name, actions: [{ name, description }], kinds, topics }],
|
|
361
|
+
* automations: [{ key, name }] }, or null from a platform without them
|
|
362
|
+
* loading, error, code, unavailable, reload
|
|
363
|
+
*
|
|
364
|
+
* Looks every 30 s, every 5 s while one is running, and not at all while
|
|
365
|
+
* the page is hidden. On a claw from before the engine it is `unavailable`.
|
|
366
|
+
*/
|
|
367
|
+
export function useAutomations({ every = IDLE_MS } = {}) {
|
|
368
|
+
const watched = useFollow((signal) => automations({ signal }), "automations", {
|
|
369
|
+
next: (data) => (listOf(data?.automations).some((a) => (a?.active_runs ?? 0) > 0) ? RUNNING_MS : every),
|
|
370
|
+
});
|
|
371
|
+
const { reload } = watched;
|
|
372
|
+
const [busyId, press] = useBusy();
|
|
373
|
+
const [switching, setSwitching] = useState({});
|
|
374
|
+
// The starting points are read once (they do not move while the page is open), and a
|
|
375
|
+
// surface that may not read them, or a claw without them, simply has none.
|
|
376
|
+
const startsRead = useFollow((signal) => automationStarts({ signal }), "automation-starts", { next: () => 0 });
|
|
377
|
+
const starts = useMemo(() => listOf(startsRead.data?.starts), [startsRead.data]);
|
|
378
|
+
|
|
379
|
+
const setStatus = useCallback(
|
|
380
|
+
async (id, on) => {
|
|
381
|
+
setSwitching((s) => ({ ...s, [id]: "switch" }));
|
|
382
|
+
const r = await saveAutomation(id, { status: on ? "on" : "off" });
|
|
383
|
+
setSwitching((s) => {
|
|
384
|
+
const next = { ...s };
|
|
385
|
+
delete next[id];
|
|
386
|
+
return next;
|
|
387
|
+
});
|
|
388
|
+
reload();
|
|
389
|
+
return r;
|
|
390
|
+
},
|
|
391
|
+
[reload],
|
|
392
|
+
);
|
|
393
|
+
|
|
394
|
+
const create = useCallback(
|
|
395
|
+
(definition, { note, goLive } = {}) =>
|
|
396
|
+
press("create", async () => {
|
|
397
|
+
const r = await createAutomation({ definition, ...(note ? { note } : {}), ...(goLive ? { go_live: true } : {}) });
|
|
398
|
+
if (r.ok) reload();
|
|
399
|
+
return r;
|
|
400
|
+
}),
|
|
401
|
+
[press, reload],
|
|
402
|
+
);
|
|
403
|
+
|
|
404
|
+
const describe = useCallback(
|
|
405
|
+
(ask, app) =>
|
|
406
|
+
press("describe", async () => {
|
|
407
|
+
const text = typeof ask === "string" ? ask.trim() : "";
|
|
408
|
+
if (!text) return { ok: false, error: "Say what it should do first.", code: "empty" };
|
|
409
|
+
const r = await draftAutomation({ ask: text, ...(typeof app === "string" && app ? { app } : {}) });
|
|
410
|
+
if (r.unavailable) return { ok: false, code: "unavailable", error: "Describing an automation is not set up here yet.", unavailable: true };
|
|
411
|
+
if (!r.ok) return { ok: false, error: r.error, code: r.code ?? "refused", ...(r.questions ? { questions: r.questions } : {}) };
|
|
412
|
+
const body = r.data && typeof r.data === "object" ? r.data : {};
|
|
413
|
+
// A refusal can arrive with a 200: the body says.
|
|
414
|
+
if (body.ok === false) {
|
|
415
|
+
const questions = Array.isArray(body.questions) ? body.questions.filter((q) => typeof q === "string" && q.trim()) : undefined;
|
|
416
|
+
return { ok: false, error: body.error || "That did not work.", code: typeof body.code === "string" ? body.code : "refused", ...(questions ? { questions } : {}) };
|
|
417
|
+
}
|
|
418
|
+
reload();
|
|
419
|
+
return { ...body, ok: true };
|
|
420
|
+
}),
|
|
421
|
+
[press, reload],
|
|
422
|
+
);
|
|
423
|
+
|
|
424
|
+
const rows = useMemo(() => listOf(watched.data?.automations), [watched.data]);
|
|
425
|
+
return { automations: rows, triggers: watched.data?.triggers ?? null, starts, describe, describing: busyId === "describe", busy: switching, creating: busyId === "create", setStatus, create, loading: watched.loading, error: watched.error, code: watched.code, unavailable: watched.unavailable, reload };
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* One automation, for the `automation-builder` and `run-view` blocks
|
|
430
|
+
* (GET _claw/automations/<id> and its runs):
|
|
431
|
+
*
|
|
432
|
+
* const a = useAutomation(id);
|
|
433
|
+
* a.automation the row: { id, key, name, status, live_version, draft_version, tz, owner_kind … }
|
|
434
|
+
* a.live, a.draft { version, definition, … } or null
|
|
435
|
+
* a.definition the draft if there is one, else the live one: what the builder opens
|
|
436
|
+
* a.versions [{ version, kind, author, note, created_at }]
|
|
437
|
+
* a.estimate, a.estimateDraft what a run is expected to cost: { credits, credits_max, cap, steps }
|
|
438
|
+
* a.runs [{ id, status, trigger_type, is_test, created_at, credits … }], newest first
|
|
439
|
+
* a.suggestions the changes the agent drafted from failed runs, still waiting for a person:
|
|
440
|
+
* [{ id, run_id, why, base_version, stale, patch, created_at }]
|
|
441
|
+
* a.keepSuggestion(id) keep one: a NEW version that runs (author: agent, kept by you);
|
|
442
|
+
* refused with `stale` when the automation moved on since
|
|
443
|
+
* a.dropSuggestion(id) forget one
|
|
444
|
+
* a.hook a webhook trigger's address: { path, has_secret, rotated_at, last_received_at },
|
|
445
|
+
* or null; never the secret
|
|
446
|
+
* a.rotateHook() make the webhook's secret (or replace it; the old one stops at once);
|
|
447
|
+
* resolves { ok: true, hook: { path, secret } }: the secret is shown once
|
|
448
|
+
* a.tests { live: TestView | null, draft: TestView | null }, TestView = { version, passed, exempt,
|
|
449
|
+
* last: { run_id, status, ended_at, credits, would } | null }: whether a test of that
|
|
450
|
+
* version passed (the editor's `tests` prop). Keep and Live need a passed test unless
|
|
451
|
+
* the member skips it. null from a platform without it.
|
|
452
|
+
* a.busy "save" | "keep" | "live" | "test" | "fix" | "hook" while a press is out
|
|
453
|
+
* a.save(definition, how?) a draft version; resolves the answer ({ ok: false, problems } on a refusal).
|
|
454
|
+
* `how` is a note (a string) or `{ baseVersion, note? }`: the editor passes
|
|
455
|
+
* `{ baseVersion }`, the version its working copy began on, and the save names THAT
|
|
456
|
+
* version (`expect_version`), so a newer draft (the agent's) is refused with
|
|
457
|
+
* `changed` and `latest` instead of being buried. With no base it names the newest
|
|
458
|
+
* version this hook has read.
|
|
459
|
+
* a.keep({ skipTest }?) the draft becomes the version that runs. Refused with `needs_test` until a
|
|
460
|
+
* test of it passed; `skipTest: true` is the member's own, deliberate second
|
|
461
|
+
* press (sends `skip_test`); an agent never passes it
|
|
462
|
+
* a.setLive(on, { skipTest }?) the Live switch; same refusal, same override
|
|
463
|
+
* a.test({ inputs, sample, dry }?) one TEST of the draft (or live) version: it reads real data and
|
|
464
|
+
* changes nothing (`dry` is true unless you pass `dry: false` for a real run);
|
|
465
|
+
* `sample` is a pretend trigger payload; resolves { data: { run_id, test, dry,
|
|
466
|
+
* version } }. A plain object with none of those keys is taken as the inputs
|
|
467
|
+
* themselves (as before).
|
|
468
|
+
* loading, error, code, unavailable, reload
|
|
469
|
+
*
|
|
470
|
+
* Nothing is read while `id` is empty. Each press looks again after.
|
|
471
|
+
*/
|
|
472
|
+
export function useAutomation(id, { every = IDLE_MS } = {}) {
|
|
473
|
+
const enabled = Boolean(segment(id));
|
|
474
|
+
const detail = useFollow((signal) => automation(id, { signal }), JSON.stringify(["automation", id ?? null]), { enabled, next: () => every });
|
|
475
|
+
const runsRead = useFollow((signal) => automationRuns(id, { limit: 30, signal }), JSON.stringify(["automation-runs", id ?? null]), {
|
|
476
|
+
enabled,
|
|
477
|
+
next: (data) => (listOf(data?.runs).some((r) => ACTIVE.has(r?.status)) ? RUNNING_MS : every),
|
|
478
|
+
});
|
|
479
|
+
const [busy, press] = useBusy();
|
|
480
|
+
const reloadAll = useCallback(() => {
|
|
481
|
+
detail.reload();
|
|
482
|
+
runsRead.reload();
|
|
483
|
+
}, [detail.reload, runsRead.reload]);
|
|
484
|
+
|
|
485
|
+
const d = detail.data;
|
|
486
|
+
const versions = useMemo(() => listOf(d?.versions), [d]);
|
|
487
|
+
const latest = versions.reduce((m, v) => Math.max(m, Number(v?.version) || 0), 0);
|
|
488
|
+
const live = d?.live ?? null;
|
|
489
|
+
const draft = d?.draft ?? null;
|
|
490
|
+
const suggestions = useMemo(() => listOf(d?.suggestions), [d]);
|
|
491
|
+
|
|
492
|
+
const change = useCallback(
|
|
493
|
+
(what, body) =>
|
|
494
|
+
press(what, async () => {
|
|
495
|
+
const r = await saveAutomation(id, body);
|
|
496
|
+
reloadAll();
|
|
497
|
+
return r;
|
|
498
|
+
}),
|
|
499
|
+
[id, press, reloadAll],
|
|
500
|
+
);
|
|
501
|
+
const save = useCallback(
|
|
502
|
+
(definition, how) => {
|
|
503
|
+
const own = how && typeof how === "object";
|
|
504
|
+
const note = own ? how.note : how;
|
|
505
|
+
const base = own && Number.isInteger(how.baseVersion) ? how.baseVersion : latest;
|
|
506
|
+
return change("save", { definition, expect_version: base, ...(typeof note === "string" && note ? { note } : {}) });
|
|
507
|
+
},
|
|
508
|
+
[change, latest],
|
|
509
|
+
);
|
|
510
|
+
const keep = useCallback((o) => change("keep", { keep: true, ...(o && o.skipTest === true ? { skip_test: true } : {}) }), [change]);
|
|
511
|
+
const setLive = useCallback((on, o) => change("live", { status: on ? "on" : "off", ...(o && o.skipTest === true ? { skip_test: true } : {}) }), [change]);
|
|
512
|
+
const decide = useCallback(
|
|
513
|
+
(fixId, decision) =>
|
|
514
|
+
press("fix", async () => {
|
|
515
|
+
const r = await decideFix(id, fixId, decision);
|
|
516
|
+
reloadAll();
|
|
517
|
+
return r;
|
|
518
|
+
}),
|
|
519
|
+
[id, press, reloadAll],
|
|
520
|
+
);
|
|
521
|
+
const rotateHook = useCallback(async () => {
|
|
522
|
+
const r = await change("hook", { hook: "rotate" });
|
|
523
|
+
return r.ok ? { ok: true, hook: r.data?.hook ?? null } : r;
|
|
524
|
+
}, [change]);
|
|
525
|
+
const keepSuggestion = useCallback((fixId) => decide(fixId, "keep"), [decide]);
|
|
526
|
+
const dropSuggestion = useCallback((fixId) => decide(fixId, "drop"), [decide]);
|
|
527
|
+
const test = useCallback(
|
|
528
|
+
(given = {}) =>
|
|
529
|
+
press("test", async () => {
|
|
530
|
+
const version = draft?.version ?? live?.version;
|
|
531
|
+
const options = given && typeof given === "object" && ("inputs" in given || "sample" in given || "dry" in given) ? given : { inputs: given };
|
|
532
|
+
const inputs = options.inputs && typeof options.inputs === "object" ? options.inputs : {};
|
|
533
|
+
const r = await runAutomation(id, {
|
|
534
|
+
inputs,
|
|
535
|
+
...(options.sample !== undefined ? { sample: options.sample } : {}),
|
|
536
|
+
dry: options.dry !== false,
|
|
537
|
+
...(version !== undefined ? { version } : {}),
|
|
538
|
+
});
|
|
539
|
+
reloadAll();
|
|
540
|
+
return r;
|
|
541
|
+
}),
|
|
542
|
+
[id, press, reloadAll, draft, live],
|
|
543
|
+
);
|
|
544
|
+
|
|
545
|
+
return {
|
|
546
|
+
automation: d?.automation ?? null,
|
|
547
|
+
live,
|
|
548
|
+
draft,
|
|
549
|
+
definition: draft?.definition ?? live?.definition ?? null,
|
|
550
|
+
versions,
|
|
551
|
+
estimate: d?.estimate_draft ?? d?.estimate ?? null,
|
|
552
|
+
estimateDraft: d?.estimate_draft ?? null,
|
|
553
|
+
tests: d?.tests && typeof d.tests === "object" ? { live: d.tests.live ?? null, draft: d.tests.draft ?? null } : null,
|
|
554
|
+
runs: listOf(runsRead.data?.runs),
|
|
555
|
+
suggestions,
|
|
556
|
+
keepSuggestion,
|
|
557
|
+
dropSuggestion,
|
|
558
|
+
hook: d?.hook ?? null,
|
|
559
|
+
rotateHook,
|
|
560
|
+
busy,
|
|
561
|
+
save,
|
|
562
|
+
keep,
|
|
563
|
+
setLive,
|
|
564
|
+
test,
|
|
565
|
+
loading: detail.loading,
|
|
566
|
+
error: detail.error,
|
|
567
|
+
code: detail.code,
|
|
568
|
+
unavailable: detail.unavailable,
|
|
569
|
+
reload: reloadAll,
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* One run's account, for the `run-view` block (GET _claw/automation-runs/<id>):
|
|
575
|
+
*
|
|
576
|
+
* const { run, steps, stepsVisible, automation, definition } = useAutomationRun(runId);
|
|
577
|
+
*
|
|
578
|
+
* run { id, status, version, trigger_type, is_test, dry, credits, cap_credits, result, error,
|
|
579
|
+
* paused, cap_bonus … }. `dry` is a TEST run (it read real data and changed nothing);
|
|
580
|
+
* its `result.dry` says what each step would have done.
|
|
581
|
+
* steps [{ path, step_id, kind, attempt, status, credits, error, input?, output?, fix? }]
|
|
582
|
+
* stepsVisible the member may read what went in and out of each step
|
|
583
|
+
* definition the definition the run was pinned to, when the platform sends it, else null
|
|
584
|
+
* fixes the fixer's attempts on this run, with the changes it drafted
|
|
585
|
+
* detail the answer as sent, for RunView's `detail` prop
|
|
586
|
+
* control(op, { path?, input?, confirmNotSent?, raiseBy?, confirmResend? })
|
|
587
|
+
* pause · resume · stop · retry · skip · edit · retry_from: RunView's `onControl`. Resolves
|
|
588
|
+
* the answer ({ ok: false, error, code } on a refusal, never thrown); the run
|
|
589
|
+
* is read again after.
|
|
590
|
+
* keepFix(stepId), dropFix(stepId) keep or drop the change the agent drafted for a step it
|
|
591
|
+
* fixed (RunView's `onKeepFix` / `onDropFix`); refused with `stale` when the
|
|
592
|
+
* automation moved on
|
|
593
|
+
* busy the op out ("pause", "retry", …, or "fix")
|
|
594
|
+
*
|
|
595
|
+
* Nothing is read while `runId` is empty. Looks every 2.5 s while the run
|
|
596
|
+
* goes; a run that has ended is read once.
|
|
597
|
+
*/
|
|
598
|
+
export function useAutomationRun(runId) {
|
|
599
|
+
const enabled = Boolean(segment(runId));
|
|
600
|
+
const watched = useFollow((signal) => automationRun(runId, { signal }), JSON.stringify(["automation-run", runId ?? null]), {
|
|
601
|
+
enabled,
|
|
602
|
+
next: (data) => (ACTIVE.has(data?.run?.status) ? RUN_FOLLOW_MS : 0),
|
|
603
|
+
});
|
|
604
|
+
const d = watched.data;
|
|
605
|
+
const fixes = useMemo(() => listOf(d?.fixes), [d]);
|
|
606
|
+
const [busy, press] = useBusy();
|
|
607
|
+
const { reload } = watched;
|
|
608
|
+
const control = useCallback(
|
|
609
|
+
(op, o = {}) =>
|
|
610
|
+
press(op, async () => {
|
|
611
|
+
const body = {
|
|
612
|
+
op,
|
|
613
|
+
...(o.path ? { path: o.path } : {}),
|
|
614
|
+
...(o.input !== undefined ? { input: o.input } : {}),
|
|
615
|
+
...(o.confirmNotSent ? { confirm_not_sent: true } : {}),
|
|
616
|
+
...(Number(o.raiseBy) > 0 ? { raise_cap_by: Number(o.raiseBy) } : {}),
|
|
617
|
+
...(o.confirmResend ? { confirm_resend: true } : {}),
|
|
618
|
+
};
|
|
619
|
+
const r = await controlRun(runId, body);
|
|
620
|
+
reload();
|
|
621
|
+
return r;
|
|
622
|
+
}),
|
|
623
|
+
[runId, press, reload],
|
|
624
|
+
);
|
|
625
|
+
const automationId = d?.run?.automation_id ?? d?.automation?.id ?? null;
|
|
626
|
+
const decide = useCallback(
|
|
627
|
+
(stepId, decision) => {
|
|
628
|
+
// The newest change the fixer drafted for the step that a person has not decided.
|
|
629
|
+
let draft = null;
|
|
630
|
+
for (const f of fixes) if (f.step_id === stepId) for (const x of f.drafts ?? []) if (x.status === "proposed") draft = x;
|
|
631
|
+
if (!draft || !automationId) return Promise.resolve({ ok: false, status: 0, error: "The agent drafted nothing to keep for that step.", unavailable: false });
|
|
632
|
+
return press("fix", async () => {
|
|
633
|
+
const r = await decideFix(automationId, draft.id, decision);
|
|
634
|
+
reload();
|
|
635
|
+
return r;
|
|
636
|
+
});
|
|
637
|
+
},
|
|
638
|
+
[fixes, automationId, press, reload],
|
|
639
|
+
);
|
|
640
|
+
const keepFix = useCallback((stepId) => decide(stepId, "keep"), [decide]);
|
|
641
|
+
const dropFix = useCallback((stepId) => decide(stepId, "drop"), [decide]);
|
|
642
|
+
return {
|
|
643
|
+
run: d?.run ?? null,
|
|
644
|
+
steps: listOf(d?.steps),
|
|
645
|
+
stepsVisible: Boolean(d?.steps_visible),
|
|
646
|
+
automation: d?.automation ?? null,
|
|
647
|
+
definition: d?.definition ?? null,
|
|
648
|
+
fixes,
|
|
649
|
+
detail: d && d.run ? { run: d.run, automation: d.automation ?? null, steps: listOf(d.steps), steps_visible: Boolean(d.steps_visible), fixes } : null,
|
|
650
|
+
control,
|
|
651
|
+
keepFix,
|
|
652
|
+
dropFix,
|
|
653
|
+
busy,
|
|
654
|
+
loading: watched.loading,
|
|
655
|
+
error: watched.error,
|
|
656
|
+
code: watched.code,
|
|
657
|
+
unavailable: watched.unavailable,
|
|
658
|
+
reload: watched.reload,
|
|
659
|
+
};
|
|
660
|
+
}
|