reflectt-node 0.1.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/LICENSE +178 -0
- package/README.md +188 -0
- package/dist/activationEvents.d.ts +110 -0
- package/dist/activationEvents.d.ts.map +1 -0
- package/dist/activationEvents.js +378 -0
- package/dist/activationEvents.js.map +1 -0
- package/dist/activity-signal.d.ts +30 -0
- package/dist/activity-signal.d.ts.map +1 -0
- package/dist/activity-signal.js +93 -0
- package/dist/activity-signal.js.map +1 -0
- package/dist/alert-integrity.d.ts +100 -0
- package/dist/alert-integrity.d.ts.map +1 -0
- package/dist/alert-integrity.js +333 -0
- package/dist/alert-integrity.js.map +1 -0
- package/dist/alert-preflight.d.ts +40 -0
- package/dist/alert-preflight.d.ts.map +1 -0
- package/dist/alert-preflight.js +235 -0
- package/dist/alert-preflight.js.map +1 -0
- package/dist/analytics.d.ts +131 -0
- package/dist/analytics.d.ts.map +1 -0
- package/dist/analytics.js +371 -0
- package/dist/analytics.js.map +1 -0
- package/dist/artifact-mirror.d.ts +26 -0
- package/dist/artifact-mirror.d.ts.map +1 -0
- package/dist/artifact-mirror.js +170 -0
- package/dist/artifact-mirror.js.map +1 -0
- package/dist/artifact-resolver.d.ts +48 -0
- package/dist/artifact-resolver.d.ts.map +1 -0
- package/dist/artifact-resolver.js +164 -0
- package/dist/artifact-resolver.js.map +1 -0
- package/dist/assignment.d.ts +116 -0
- package/dist/assignment.d.ts.map +1 -0
- package/dist/assignment.js +475 -0
- package/dist/assignment.js.map +1 -0
- package/dist/auditLedger.d.ts +50 -0
- package/dist/auditLedger.d.ts.map +1 -0
- package/dist/auditLedger.js +136 -0
- package/dist/auditLedger.js.map +1 -0
- package/dist/boardHealthWorker.d.ts +134 -0
- package/dist/boardHealthWorker.d.ts.map +1 -0
- package/dist/boardHealthWorker.js +882 -0
- package/dist/boardHealthWorker.js.map +1 -0
- package/dist/bootstrap-team.d.ts +42 -0
- package/dist/bootstrap-team.d.ts.map +1 -0
- package/dist/bootstrap-team.js +111 -0
- package/dist/bootstrap-team.js.map +1 -0
- package/dist/buildInfo.d.ts +17 -0
- package/dist/buildInfo.d.ts.map +1 -0
- package/dist/buildInfo.js +56 -0
- package/dist/buildInfo.js.map +1 -0
- package/dist/calendar-events.d.ts +133 -0
- package/dist/calendar-events.d.ts.map +1 -0
- package/dist/calendar-events.js +615 -0
- package/dist/calendar-events.js.map +1 -0
- package/dist/calendar-ical.d.ts +41 -0
- package/dist/calendar-ical.d.ts.map +1 -0
- package/dist/calendar-ical.js +413 -0
- package/dist/calendar-ical.js.map +1 -0
- package/dist/calendar-reminder-engine.d.ts +10 -0
- package/dist/calendar-reminder-engine.d.ts.map +1 -0
- package/dist/calendar-reminder-engine.js +143 -0
- package/dist/calendar-reminder-engine.js.map +1 -0
- package/dist/calendar.d.ts +75 -0
- package/dist/calendar.d.ts.map +1 -0
- package/dist/calendar.js +391 -0
- package/dist/calendar.js.map +1 -0
- package/dist/canvas-multiplexer.d.ts +44 -0
- package/dist/canvas-multiplexer.d.ts.map +1 -0
- package/dist/canvas-multiplexer.js +150 -0
- package/dist/canvas-multiplexer.js.map +1 -0
- package/dist/canvas-slots.d.ts +83 -0
- package/dist/canvas-slots.d.ts.map +1 -0
- package/dist/canvas-slots.js +144 -0
- package/dist/canvas-slots.js.map +1 -0
- package/dist/canvas-types.d.ts +56 -0
- package/dist/canvas-types.d.ts.map +1 -0
- package/dist/canvas-types.js +54 -0
- package/dist/canvas-types.js.map +1 -0
- package/dist/cf-keepalive.d.ts +40 -0
- package/dist/cf-keepalive.d.ts.map +1 -0
- package/dist/cf-keepalive.js +153 -0
- package/dist/cf-keepalive.js.map +1 -0
- package/dist/changeFeed.d.ts +38 -0
- package/dist/changeFeed.d.ts.map +1 -0
- package/dist/changeFeed.js +324 -0
- package/dist/changeFeed.js.map +1 -0
- package/dist/channels.d.ts +28 -0
- package/dist/channels.d.ts.map +1 -0
- package/dist/channels.js +23 -0
- package/dist/channels.js.map +1 -0
- package/dist/chat-approval-detector.d.ts +47 -0
- package/dist/chat-approval-detector.d.ts.map +1 -0
- package/dist/chat-approval-detector.js +224 -0
- package/dist/chat-approval-detector.js.map +1 -0
- package/dist/chat.d.ts +119 -0
- package/dist/chat.d.ts.map +1 -0
- package/dist/chat.js +666 -0
- package/dist/chat.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +1142 -0
- package/dist/cli.js.map +1 -0
- package/dist/cloud.d.ts +45 -0
- package/dist/cloud.d.ts.map +1 -0
- package/dist/cloud.js +962 -0
- package/dist/cloud.js.map +1 -0
- package/dist/config.d.ts +17 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +33 -0
- package/dist/config.js.map +1 -0
- package/dist/connectivity.d.ts +59 -0
- package/dist/connectivity.d.ts.map +1 -0
- package/dist/connectivity.js +173 -0
- package/dist/connectivity.js.map +1 -0
- package/dist/contacts.d.ts +59 -0
- package/dist/contacts.d.ts.map +1 -0
- package/dist/contacts.js +183 -0
- package/dist/contacts.js.map +1 -0
- package/dist/content.d.ts +130 -0
- package/dist/content.d.ts.map +1 -0
- package/dist/content.js +186 -0
- package/dist/content.js.map +1 -0
- package/dist/context-budget.d.ts +87 -0
- package/dist/context-budget.d.ts.map +1 -0
- package/dist/context-budget.js +459 -0
- package/dist/context-budget.js.map +1 -0
- package/dist/continuity-loop.d.ts +55 -0
- package/dist/continuity-loop.d.ts.map +1 -0
- package/dist/continuity-loop.js +267 -0
- package/dist/continuity-loop.js.map +1 -0
- package/dist/dashboard.d.ts +6 -0
- package/dist/dashboard.d.ts.map +1 -0
- package/dist/dashboard.js +2348 -0
- package/dist/dashboard.js.map +1 -0
- package/dist/db.d.ts +44 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +648 -0
- package/dist/db.js.map +1 -0
- package/dist/doctor.d.ts +30 -0
- package/dist/doctor.d.ts.map +1 -0
- package/dist/doctor.js +159 -0
- package/dist/doctor.js.map +1 -0
- package/dist/duplicateClosureGuard.d.ts +31 -0
- package/dist/duplicateClosureGuard.d.ts.map +1 -0
- package/dist/duplicateClosureGuard.js +83 -0
- package/dist/duplicateClosureGuard.js.map +1 -0
- package/dist/embeddings.d.ts +13 -0
- package/dist/embeddings.d.ts.map +1 -0
- package/dist/embeddings.js +78 -0
- package/dist/embeddings.js.map +1 -0
- package/dist/escalation.d.ts +80 -0
- package/dist/escalation.d.ts.map +1 -0
- package/dist/escalation.js +213 -0
- package/dist/escalation.js.map +1 -0
- package/dist/events.d.ts +130 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +382 -0
- package/dist/events.js.map +1 -0
- package/dist/executionSweeper.d.ts +97 -0
- package/dist/executionSweeper.d.ts.map +1 -0
- package/dist/executionSweeper.js +875 -0
- package/dist/executionSweeper.js.map +1 -0
- package/dist/experiments.d.ts +47 -0
- package/dist/experiments.d.ts.map +1 -0
- package/dist/experiments.js +133 -0
- package/dist/experiments.js.map +1 -0
- package/dist/feedback.d.ts +179 -0
- package/dist/feedback.d.ts.map +1 -0
- package/dist/feedback.js +397 -0
- package/dist/feedback.js.map +1 -0
- package/dist/files.d.ts +52 -0
- package/dist/files.d.ts.map +1 -0
- package/dist/files.js +172 -0
- package/dist/files.js.map +1 -0
- package/dist/format-duration.d.ts +19 -0
- package/dist/format-duration.d.ts.map +1 -0
- package/dist/format-duration.js +33 -0
- package/dist/format-duration.js.map +1 -0
- package/dist/github-actor-auth.d.ts +20 -0
- package/dist/github-actor-auth.d.ts.map +1 -0
- package/dist/github-actor-auth.js +54 -0
- package/dist/github-actor-auth.js.map +1 -0
- package/dist/github-ci.d.ts +16 -0
- package/dist/github-ci.d.ts.map +1 -0
- package/dist/github-ci.js +37 -0
- package/dist/github-ci.js.map +1 -0
- package/dist/github-identity.d.ts +30 -0
- package/dist/github-identity.d.ts.map +1 -0
- package/dist/github-identity.js +96 -0
- package/dist/github-identity.js.map +1 -0
- package/dist/github-reviews.d.ts +24 -0
- package/dist/github-reviews.d.ts.map +1 -0
- package/dist/github-reviews.js +56 -0
- package/dist/github-reviews.js.map +1 -0
- package/dist/health.d.ts +391 -0
- package/dist/health.d.ts.map +1 -0
- package/dist/health.js +1841 -0
- package/dist/health.js.map +1 -0
- package/dist/host-keepalive.d.ts +22 -0
- package/dist/host-keepalive.d.ts.map +1 -0
- package/dist/host-keepalive.js +126 -0
- package/dist/host-keepalive.js.map +1 -0
- package/dist/host-registry.d.ts +43 -0
- package/dist/host-registry.d.ts.map +1 -0
- package/dist/host-registry.js +93 -0
- package/dist/host-registry.js.map +1 -0
- package/dist/inbox.d.ts +87 -0
- package/dist/inbox.d.ts.map +1 -0
- package/dist/inbox.js +410 -0
- package/dist/inbox.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +306 -0
- package/dist/index.js.map +1 -0
- package/dist/insight-mutation.d.ts +32 -0
- package/dist/insight-mutation.d.ts.map +1 -0
- package/dist/insight-mutation.js +160 -0
- package/dist/insight-mutation.js.map +1 -0
- package/dist/insight-promotion.d.ts +89 -0
- package/dist/insight-promotion.d.ts.map +1 -0
- package/dist/insight-promotion.js +278 -0
- package/dist/insight-promotion.js.map +1 -0
- package/dist/insight-task-bridge.d.ts +77 -0
- package/dist/insight-task-bridge.d.ts.map +1 -0
- package/dist/insight-task-bridge.js +556 -0
- package/dist/insight-task-bridge.js.map +1 -0
- package/dist/insights.d.ts +222 -0
- package/dist/insights.d.ts.map +1 -0
- package/dist/insights.js +871 -0
- package/dist/insights.js.map +1 -0
- package/dist/intake-pipeline.d.ts +74 -0
- package/dist/intake-pipeline.d.ts.map +1 -0
- package/dist/intake-pipeline.js +199 -0
- package/dist/intake-pipeline.js.map +1 -0
- package/dist/intensity.d.ts +31 -0
- package/dist/intensity.d.ts.map +1 -0
- package/dist/intensity.js +94 -0
- package/dist/intensity.js.map +1 -0
- package/dist/knowledge-auto-index.d.ts +37 -0
- package/dist/knowledge-auto-index.d.ts.map +1 -0
- package/dist/knowledge-auto-index.js +149 -0
- package/dist/knowledge-auto-index.js.map +1 -0
- package/dist/knowledge-docs.d.ts +45 -0
- package/dist/knowledge-docs.d.ts.map +1 -0
- package/dist/knowledge-docs.js +188 -0
- package/dist/knowledge-docs.js.map +1 -0
- package/dist/lane-config.d.ts +25 -0
- package/dist/lane-config.d.ts.map +1 -0
- package/dist/lane-config.js +105 -0
- package/dist/lane-config.js.map +1 -0
- package/dist/lineage.d.ts +86 -0
- package/dist/lineage.d.ts.map +1 -0
- package/dist/lineage.js +303 -0
- package/dist/lineage.js.map +1 -0
- package/dist/logStore.d.ts +25 -0
- package/dist/logStore.d.ts.map +1 -0
- package/dist/logStore.js +83 -0
- package/dist/logStore.js.map +1 -0
- package/dist/manage.d.ts +12 -0
- package/dist/manage.d.ts.map +1 -0
- package/dist/manage.js +253 -0
- package/dist/manage.js.map +1 -0
- package/dist/mcp.d.ts +5 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +604 -0
- package/dist/mcp.js.map +1 -0
- package/dist/memory.d.ts +47 -0
- package/dist/memory.d.ts.map +1 -0
- package/dist/memory.js +149 -0
- package/dist/memory.js.map +1 -0
- package/dist/mention-ack.d.ts +80 -0
- package/dist/mention-ack.d.ts.map +1 -0
- package/dist/mention-ack.js +175 -0
- package/dist/mention-ack.js.map +1 -0
- package/dist/messageRouter.d.ts +60 -0
- package/dist/messageRouter.d.ts.map +1 -0
- package/dist/messageRouter.js +309 -0
- package/dist/messageRouter.js.map +1 -0
- package/dist/mutationAlert.d.ts +44 -0
- package/dist/mutationAlert.d.ts.map +1 -0
- package/dist/mutationAlert.js +174 -0
- package/dist/mutationAlert.js.map +1 -0
- package/dist/noise-budget.d.ts +136 -0
- package/dist/noise-budget.d.ts.map +1 -0
- package/dist/noise-budget.js +340 -0
- package/dist/noise-budget.js.map +1 -0
- package/dist/notifications.d.ts +67 -0
- package/dist/notifications.d.ts.map +1 -0
- package/dist/notifications.js +253 -0
- package/dist/notifications.js.map +1 -0
- package/dist/openclaw.d.ts +34 -0
- package/dist/openclaw.d.ts.map +1 -0
- package/dist/openclaw.js +208 -0
- package/dist/openclaw.js.map +1 -0
- package/dist/pause-controls.d.ts +31 -0
- package/dist/pause-controls.d.ts.map +1 -0
- package/dist/pause-controls.js +130 -0
- package/dist/pause-controls.js.map +1 -0
- package/dist/pidlock.d.ts +25 -0
- package/dist/pidlock.d.ts.map +1 -0
- package/dist/pidlock.js +179 -0
- package/dist/pidlock.js.map +1 -0
- package/dist/policy.d.ts +139 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +264 -0
- package/dist/policy.js.map +1 -0
- package/dist/polls.d.ts +47 -0
- package/dist/polls.d.ts.map +1 -0
- package/dist/polls.js +162 -0
- package/dist/polls.js.map +1 -0
- package/dist/portability.d.ts +55 -0
- package/dist/portability.d.ts.map +1 -0
- package/dist/portability.js +292 -0
- package/dist/portability.js.map +1 -0
- package/dist/pr-integrity.d.ts +45 -0
- package/dist/pr-integrity.d.ts.map +1 -0
- package/dist/pr-integrity.js +124 -0
- package/dist/pr-integrity.js.map +1 -0
- package/dist/prAutoMerge.d.ts +62 -0
- package/dist/prAutoMerge.d.ts.map +1 -0
- package/dist/prAutoMerge.js +493 -0
- package/dist/prAutoMerge.js.map +1 -0
- package/dist/preflight.d.ts +66 -0
- package/dist/preflight.d.ts.map +1 -0
- package/dist/preflight.js +864 -0
- package/dist/preflight.js.map +1 -0
- package/dist/presence.d.ts +98 -0
- package/dist/presence.d.ts.map +1 -0
- package/dist/presence.js +347 -0
- package/dist/presence.js.map +1 -0
- package/dist/provisioning.d.ts +101 -0
- package/dist/provisioning.d.ts.map +1 -0
- package/dist/provisioning.js +430 -0
- package/dist/provisioning.js.map +1 -0
- package/dist/reflection-automation.d.ts +59 -0
- package/dist/reflection-automation.d.ts.map +1 -0
- package/dist/reflection-automation.js +350 -0
- package/dist/reflection-automation.js.map +1 -0
- package/dist/reflections.d.ts +65 -0
- package/dist/reflections.d.ts.map +1 -0
- package/dist/reflections.js +306 -0
- package/dist/reflections.js.map +1 -0
- package/dist/release.d.ts +67 -0
- package/dist/release.d.ts.map +1 -0
- package/dist/release.js +275 -0
- package/dist/release.js.map +1 -0
- package/dist/request-tracker.d.ts +36 -0
- package/dist/request-tracker.d.ts.map +1 -0
- package/dist/request-tracker.js +109 -0
- package/dist/request-tracker.js.map +1 -0
- package/dist/research.d.ts +75 -0
- package/dist/research.d.ts.map +1 -0
- package/dist/research.js +171 -0
- package/dist/research.js.map +1 -0
- package/dist/routing-approvals.d.ts +73 -0
- package/dist/routing-approvals.d.ts.map +1 -0
- package/dist/routing-approvals.js +88 -0
- package/dist/routing-approvals.js.map +1 -0
- package/dist/routing-override.d.ts +94 -0
- package/dist/routing-override.d.ts.map +1 -0
- package/dist/routing-override.js +290 -0
- package/dist/routing-override.js.map +1 -0
- package/dist/scope-routing.d.ts +18 -0
- package/dist/scope-routing.d.ts.map +1 -0
- package/dist/scope-routing.js +29 -0
- package/dist/scope-routing.js.map +1 -0
- package/dist/secrets.d.ts +77 -0
- package/dist/secrets.d.ts.map +1 -0
- package/dist/secrets.js +287 -0
- package/dist/secrets.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +10887 -0
- package/dist/server.js.map +1 -0
- package/dist/service-probe.d.ts +53 -0
- package/dist/service-probe.d.ts.map +1 -0
- package/dist/service-probe.js +225 -0
- package/dist/service-probe.js.map +1 -0
- package/dist/shared-workspace-api.d.ts +73 -0
- package/dist/shared-workspace-api.d.ts.map +1 -0
- package/dist/shared-workspace-api.js +281 -0
- package/dist/shared-workspace-api.js.map +1 -0
- package/dist/shipped-heartbeat.d.ts +91 -0
- package/dist/shipped-heartbeat.d.ts.map +1 -0
- package/dist/shipped-heartbeat.js +272 -0
- package/dist/shipped-heartbeat.js.map +1 -0
- package/dist/starter-team.d.ts +23 -0
- package/dist/starter-team.d.ts.map +1 -0
- package/dist/starter-team.js +88 -0
- package/dist/starter-team.js.map +1 -0
- package/dist/suppression-ledger.d.ts +73 -0
- package/dist/suppression-ledger.d.ts.map +1 -0
- package/dist/suppression-ledger.js +125 -0
- package/dist/suppression-ledger.js.map +1 -0
- package/dist/system-loop-state.d.ts +4 -0
- package/dist/system-loop-state.d.ts.map +1 -0
- package/dist/system-loop-state.js +40 -0
- package/dist/system-loop-state.js.map +1 -0
- package/dist/taskCommentIngest.d.ts +43 -0
- package/dist/taskCommentIngest.d.ts.map +1 -0
- package/dist/taskCommentIngest.js +59 -0
- package/dist/taskCommentIngest.js.map +1 -0
- package/dist/taskPrecheck.d.ts +20 -0
- package/dist/taskPrecheck.d.ts.map +1 -0
- package/dist/taskPrecheck.js +329 -0
- package/dist/taskPrecheck.js.map +1 -0
- package/dist/taskStateSync.d.ts +8 -0
- package/dist/taskStateSync.d.ts.map +1 -0
- package/dist/taskStateSync.js +79 -0
- package/dist/taskStateSync.js.map +1 -0
- package/dist/tasks.d.ts +140 -0
- package/dist/tasks.d.ts.map +1 -0
- package/dist/tasks.js +1281 -0
- package/dist/tasks.js.map +1 -0
- package/dist/team-config.d.ts +24 -0
- package/dist/team-config.d.ts.map +1 -0
- package/dist/team-config.js +221 -0
- package/dist/team-config.js.map +1 -0
- package/dist/team-doctor.d.ts +22 -0
- package/dist/team-doctor.d.ts.map +1 -0
- package/dist/team-doctor.js +270 -0
- package/dist/team-doctor.js.map +1 -0
- package/dist/team-pulse.d.ts +52 -0
- package/dist/team-pulse.d.ts.map +1 -0
- package/dist/team-pulse.js +176 -0
- package/dist/team-pulse.js.map +1 -0
- package/dist/telemetry.d.ts +74 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +256 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/test-task-filter.d.ts +21 -0
- package/dist/test-task-filter.d.ts.map +1 -0
- package/dist/test-task-filter.js +48 -0
- package/dist/test-task-filter.js.map +1 -0
- package/dist/types.d.ts +126 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +4 -0
- package/dist/types.js.map +1 -0
- package/dist/usage-tracking.d.ts +101 -0
- package/dist/usage-tracking.d.ts.map +1 -0
- package/dist/usage-tracking.js +325 -0
- package/dist/usage-tracking.js.map +1 -0
- package/dist/vector-store.d.ts +87 -0
- package/dist/vector-store.d.ts.map +1 -0
- package/dist/vector-store.js +247 -0
- package/dist/vector-store.js.map +1 -0
- package/dist/watchdog/idleNudgeLane.d.ts +22 -0
- package/dist/watchdog/idleNudgeLane.d.ts.map +1 -0
- package/dist/watchdog/idleNudgeLane.js +98 -0
- package/dist/watchdog/idleNudgeLane.js.map +1 -0
- package/dist/webhooks.d.ts +103 -0
- package/dist/webhooks.d.ts.map +1 -0
- package/dist/webhooks.js +398 -0
- package/dist/webhooks.js.map +1 -0
- package/dist/working-contract.d.ts +42 -0
- package/dist/working-contract.d.ts.map +1 -0
- package/dist/working-contract.js +228 -0
- package/dist/working-contract.js.map +1 -0
- package/dist/ws-heartbeat.d.ts +66 -0
- package/dist/ws-heartbeat.d.ts.map +1 -0
- package/dist/ws-heartbeat.js +174 -0
- package/dist/ws-heartbeat.js.map +1 -0
- package/package.json +87 -0
- package/plugins/reflectt-channel/README.md +96 -0
- package/plugins/reflectt-channel/index.ts +789 -0
- package/plugins/reflectt-channel/openclaw.plugin.json +23 -0
- package/plugins/reflectt-channel/package.json +23 -0
- package/plugins/reflectt-channel/src/channel.ts +433 -0
- package/plugins/reflectt-channel/src/types.ts +29 -0
- package/public/avatars/echo.png +0 -0
- package/public/avatars/harmony.png +0 -0
- package/public/avatars/kai.png +0 -0
- package/public/avatars/link.png +0 -0
- package/public/avatars/pixel.png +0 -0
- package/public/avatars/rhythm.png +0 -0
- package/public/avatars/ryan.png +0 -0
- package/public/avatars/sage.png +0 -0
- package/public/avatars/scout.png +0 -0
- package/public/avatars/spark.png +0 -0
- package/public/dashboard-animations.css +381 -0
- package/public/dashboard.js +3479 -0
- package/public/docs.md +1062 -0
- package/public/file-upload-mock.html +1097 -0
- package/public/og-card.png +0 -0
- package/public/ui-kit.html +318 -0
- package/public/widget/feedback.js +194 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,1142 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
// Copyright (c) Reflectt AI
|
|
4
|
+
/**
|
|
5
|
+
* reflectt CLI - Command line interface for reflectt-node
|
|
6
|
+
*/
|
|
7
|
+
import { Command } from 'commander';
|
|
8
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync, unlinkSync } from 'fs';
|
|
9
|
+
import { collectDoctorReport, formatDoctorHuman } from './doctor.js';
|
|
10
|
+
import { homedir, hostname } from 'os';
|
|
11
|
+
import { join, dirname } from 'path';
|
|
12
|
+
import { spawn, execSync } from 'child_process';
|
|
13
|
+
import { fileURLToPath } from 'url';
|
|
14
|
+
const REFLECTT_HOME = process.env.REFLECTT_HOME || join(homedir(), '.reflectt');
|
|
15
|
+
const CONFIG_PATH = join(REFLECTT_HOME, 'config.json');
|
|
16
|
+
const DATA_DIR = join(REFLECTT_HOME, 'data');
|
|
17
|
+
const PID_FILE = join(REFLECTT_HOME, 'server.pid');
|
|
18
|
+
function loadConfig() {
|
|
19
|
+
if (existsSync(CONFIG_PATH)) {
|
|
20
|
+
try {
|
|
21
|
+
return JSON.parse(readFileSync(CONFIG_PATH, 'utf-8'));
|
|
22
|
+
}
|
|
23
|
+
catch (err) {
|
|
24
|
+
console.error('⚠️ Failed to parse config.json, using defaults');
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return { port: 4445, host: '127.0.0.1' };
|
|
28
|
+
}
|
|
29
|
+
function saveConfig(config) {
|
|
30
|
+
writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
|
|
31
|
+
}
|
|
32
|
+
function ensureReflecttHome() {
|
|
33
|
+
mkdirSync(REFLECTT_HOME, { recursive: true });
|
|
34
|
+
mkdirSync(DATA_DIR, { recursive: true });
|
|
35
|
+
mkdirSync(join(DATA_DIR, 'inbox'), { recursive: true });
|
|
36
|
+
}
|
|
37
|
+
function isServerRunning() {
|
|
38
|
+
if (!existsSync(PID_FILE))
|
|
39
|
+
return false;
|
|
40
|
+
try {
|
|
41
|
+
const pid = Number(readFileSync(PID_FILE, 'utf-8').trim());
|
|
42
|
+
process.kill(pid, 0);
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function getRuntimePaths() {
|
|
50
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
51
|
+
const __dirname = dirname(__filename);
|
|
52
|
+
const projectRoot = join(__dirname, '..');
|
|
53
|
+
const serverPath = join(projectRoot, 'src', 'index.ts');
|
|
54
|
+
return { projectRoot, serverPath };
|
|
55
|
+
}
|
|
56
|
+
function buildServerEnv(config) {
|
|
57
|
+
const env = {
|
|
58
|
+
...process.env,
|
|
59
|
+
REFLECTT_HOME,
|
|
60
|
+
PORT: String(config.port),
|
|
61
|
+
HOST: config.host,
|
|
62
|
+
};
|
|
63
|
+
if (config.cloud) {
|
|
64
|
+
env.REFLECTT_CLOUD_URL = config.cloud.cloudUrl;
|
|
65
|
+
env.REFLECTT_HOST_NAME = config.cloud.hostName;
|
|
66
|
+
env.REFLECTT_HOST_TYPE = config.cloud.hostType;
|
|
67
|
+
env.REFLECTT_HOST_ID = config.cloud.hostId;
|
|
68
|
+
env.REFLECTT_HOST_CREDENTIAL = config.cloud.credential;
|
|
69
|
+
env.REFLECTT_HOST_TOKEN = config.cloud.credential;
|
|
70
|
+
}
|
|
71
|
+
return env;
|
|
72
|
+
}
|
|
73
|
+
function startServerDetached(config) {
|
|
74
|
+
const { projectRoot, serverPath } = getRuntimePaths();
|
|
75
|
+
if (!existsSync(serverPath)) {
|
|
76
|
+
throw new Error(`Server file not found: ${serverPath}`);
|
|
77
|
+
}
|
|
78
|
+
const child = spawn('npx', ['tsx', serverPath], {
|
|
79
|
+
env: buildServerEnv(config),
|
|
80
|
+
detached: true,
|
|
81
|
+
stdio: 'ignore',
|
|
82
|
+
cwd: projectRoot,
|
|
83
|
+
});
|
|
84
|
+
child.unref();
|
|
85
|
+
writeFileSync(PID_FILE, String(child.pid));
|
|
86
|
+
return child.pid ?? -1;
|
|
87
|
+
}
|
|
88
|
+
function stopServerIfRunning() {
|
|
89
|
+
if (!existsSync(PID_FILE))
|
|
90
|
+
return;
|
|
91
|
+
const pidRaw = readFileSync(PID_FILE, 'utf-8').trim();
|
|
92
|
+
const pid = Number(pidRaw);
|
|
93
|
+
if (!Number.isFinite(pid)) {
|
|
94
|
+
unlinkSync(PID_FILE);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
try {
|
|
98
|
+
process.kill(pid, 'SIGTERM');
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
// process already gone
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
unlinkSync(PID_FILE);
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
// ignore cleanup errors
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
async function tryApiRequest(path, options = {}) {
|
|
111
|
+
const config = loadConfig();
|
|
112
|
+
const url = `http://${config.host}:${config.port}${path}`;
|
|
113
|
+
try {
|
|
114
|
+
const response = await fetch(url, options);
|
|
115
|
+
if (!response.ok)
|
|
116
|
+
return null;
|
|
117
|
+
return await response.json();
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
async function enrollHostWithApiKey(input) {
|
|
124
|
+
const url = `${input.cloudUrl.replace(/\/+$/, '')}/api/hosts/enroll`;
|
|
125
|
+
const response = await fetch(url, {
|
|
126
|
+
method: 'POST',
|
|
127
|
+
headers: {
|
|
128
|
+
'content-type': 'application/json',
|
|
129
|
+
authorization: `Bearer ${input.apiKey}`,
|
|
130
|
+
},
|
|
131
|
+
body: JSON.stringify({
|
|
132
|
+
name: input.hostName,
|
|
133
|
+
capabilities: [input.hostType],
|
|
134
|
+
}),
|
|
135
|
+
});
|
|
136
|
+
const payload = await response.json().catch(() => ({}));
|
|
137
|
+
const hostId = payload?.host?.id;
|
|
138
|
+
const credential = payload?.credential?.token;
|
|
139
|
+
if (response.ok && hostId && credential) {
|
|
140
|
+
return { hostId: String(hostId), credential: String(credential) };
|
|
141
|
+
}
|
|
142
|
+
throw new Error(`API key enrollment failed: ${payload?.error || `${response.status} ${response.statusText}`}`);
|
|
143
|
+
}
|
|
144
|
+
async function registerHostWithCloud(input) {
|
|
145
|
+
const cloudBase = input.cloudUrl.replace(/\/+$/, '');
|
|
146
|
+
// New cloud API path (reflectt-cloud): /api/hosts/claim
|
|
147
|
+
// Legacy path (older API): /v1/hosts/register
|
|
148
|
+
const attempts = [
|
|
149
|
+
{
|
|
150
|
+
name: 'claim-join-token',
|
|
151
|
+
url: `${cloudBase}/api/hosts/claim`,
|
|
152
|
+
bearer: input.joinToken,
|
|
153
|
+
body: {
|
|
154
|
+
joinToken: input.joinToken,
|
|
155
|
+
name: input.hostName,
|
|
156
|
+
hostName: input.hostName,
|
|
157
|
+
hostType: input.hostType,
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
...(input.authToken
|
|
161
|
+
? [{
|
|
162
|
+
name: 'claim-user-jwt',
|
|
163
|
+
url: `${cloudBase}/api/hosts/claim`,
|
|
164
|
+
bearer: input.authToken,
|
|
165
|
+
body: {
|
|
166
|
+
joinToken: input.joinToken,
|
|
167
|
+
name: input.hostName,
|
|
168
|
+
hostName: input.hostName,
|
|
169
|
+
hostType: input.hostType,
|
|
170
|
+
},
|
|
171
|
+
}]
|
|
172
|
+
: []),
|
|
173
|
+
{
|
|
174
|
+
name: 'legacy-register',
|
|
175
|
+
url: `${cloudBase}/v1/hosts/register`,
|
|
176
|
+
bearer: input.joinToken,
|
|
177
|
+
body: {
|
|
178
|
+
joinToken: input.joinToken,
|
|
179
|
+
hostName: input.hostName,
|
|
180
|
+
hostType: input.hostType,
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
];
|
|
184
|
+
const errors = [];
|
|
185
|
+
for (const attempt of attempts) {
|
|
186
|
+
try {
|
|
187
|
+
const response = await fetch(attempt.url, {
|
|
188
|
+
method: 'POST',
|
|
189
|
+
headers: {
|
|
190
|
+
'content-type': 'application/json',
|
|
191
|
+
authorization: `Bearer ${attempt.bearer}`,
|
|
192
|
+
},
|
|
193
|
+
body: JSON.stringify(attempt.body),
|
|
194
|
+
});
|
|
195
|
+
const payload = await response.json().catch(() => ({}));
|
|
196
|
+
// Current cloud response shape
|
|
197
|
+
const hostId = payload?.host?.id || payload?.data?.hostId;
|
|
198
|
+
const credential = payload?.credential?.token || payload?.data?.credential;
|
|
199
|
+
if (response.ok && hostId && credential) {
|
|
200
|
+
return {
|
|
201
|
+
hostId: String(hostId),
|
|
202
|
+
credential: String(credential),
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
const detail = payload?.error || payload?.message || `${response.status} ${response.statusText}`;
|
|
206
|
+
errors.push(`${attempt.name}: ${detail}`);
|
|
207
|
+
}
|
|
208
|
+
catch (err) {
|
|
209
|
+
errors.push(`${attempt.name}: ${err?.message || 'request failed'}`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
throw new Error(`Cloud registration failed (${errors.join(' | ')})`);
|
|
213
|
+
}
|
|
214
|
+
async function waitForCloudHeartbeat(timeoutMs = 45_000) {
|
|
215
|
+
const started = Date.now();
|
|
216
|
+
while (Date.now() - started < timeoutMs) {
|
|
217
|
+
const status = await tryApiRequest('/cloud/status');
|
|
218
|
+
if (status?.registered && typeof status.hostId === 'string' && (status.heartbeatCount || 0) > 0) {
|
|
219
|
+
return { hostId: status.hostId, heartbeatCount: status.heartbeatCount };
|
|
220
|
+
}
|
|
221
|
+
await new Promise(resolve => setTimeout(resolve, 1500));
|
|
222
|
+
}
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
async function apiRequest(path, options = {}) {
|
|
226
|
+
const config = loadConfig();
|
|
227
|
+
const url = `http://${config.host}:${config.port}${path}`;
|
|
228
|
+
try {
|
|
229
|
+
const response = await fetch(url, options);
|
|
230
|
+
if (!response.ok) {
|
|
231
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
232
|
+
}
|
|
233
|
+
return await response.json();
|
|
234
|
+
}
|
|
235
|
+
catch (err) {
|
|
236
|
+
if (err.code === 'ECONNREFUSED') {
|
|
237
|
+
console.error('❌ Server is not running. Start it with: reflectt start');
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
console.error('❌ Request failed:', err.message);
|
|
241
|
+
}
|
|
242
|
+
process.exit(1);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
async function cloudRequest(url, token, method, body) {
|
|
246
|
+
const response = await fetch(url, {
|
|
247
|
+
method,
|
|
248
|
+
headers: {
|
|
249
|
+
'content-type': 'application/json',
|
|
250
|
+
authorization: `Bearer ${token}`,
|
|
251
|
+
},
|
|
252
|
+
body: body === undefined ? undefined : JSON.stringify(body),
|
|
253
|
+
});
|
|
254
|
+
const json = await response.json().catch(() => ({}));
|
|
255
|
+
return { status: response.status, json };
|
|
256
|
+
}
|
|
257
|
+
function printStep(name, pass, detail) {
|
|
258
|
+
const icon = pass ? '✅' : '❌';
|
|
259
|
+
console.log(`${icon} ${name} — ${detail}`);
|
|
260
|
+
}
|
|
261
|
+
const program = new Command();
|
|
262
|
+
program
|
|
263
|
+
.name('reflectt')
|
|
264
|
+
.description('CLI for reflectt-node - local agent communication server')
|
|
265
|
+
.version('0.1.0');
|
|
266
|
+
// ============ INIT COMMAND ============
|
|
267
|
+
program
|
|
268
|
+
.command('init')
|
|
269
|
+
.description('Initialize reflectt team ops directory (~/.reflectt/)')
|
|
270
|
+
.option('--no-git', 'Skip git init and initial commit')
|
|
271
|
+
.option('--force', 'Overwrite existing team files with defaults')
|
|
272
|
+
.action((opts) => {
|
|
273
|
+
const isNew = !existsSync(REFLECTT_HOME);
|
|
274
|
+
let filesCreated = 0;
|
|
275
|
+
let filesSkipped = 0;
|
|
276
|
+
// Create directories
|
|
277
|
+
mkdirSync(REFLECTT_HOME, { recursive: true });
|
|
278
|
+
mkdirSync(DATA_DIR, { recursive: true });
|
|
279
|
+
mkdirSync(join(DATA_DIR, 'inbox'), { recursive: true });
|
|
280
|
+
// Create default config if missing
|
|
281
|
+
if (!existsSync(CONFIG_PATH)) {
|
|
282
|
+
const config = { port: 4445, host: '127.0.0.1' };
|
|
283
|
+
saveConfig(config);
|
|
284
|
+
filesCreated++;
|
|
285
|
+
console.log(' ✅ config.json');
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
filesSkipped++;
|
|
289
|
+
console.log(' ⏭️ config.json (exists)');
|
|
290
|
+
}
|
|
291
|
+
// Copy default team files from defaults/ directory
|
|
292
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
293
|
+
const __dirname = dirname(__filename);
|
|
294
|
+
const defaultsDir = join(__dirname, '..', 'defaults');
|
|
295
|
+
const teamFiles = [
|
|
296
|
+
{ src: 'TEAM.md', desc: 'Team culture and principles' },
|
|
297
|
+
{ src: 'TEAM-ROLES.yaml', desc: 'Agent role registry' },
|
|
298
|
+
{ src: 'TEAM-STANDARDS.md', desc: 'Operational standards' },
|
|
299
|
+
{ src: '.gitignore', desc: 'Git exclusions for runtime data' },
|
|
300
|
+
];
|
|
301
|
+
for (const file of teamFiles) {
|
|
302
|
+
const destPath = join(REFLECTT_HOME, file.src);
|
|
303
|
+
const srcPath = join(defaultsDir, file.src);
|
|
304
|
+
if (existsSync(destPath) && !opts.force) {
|
|
305
|
+
filesSkipped++;
|
|
306
|
+
console.log(` ⏭️ ${file.src} (exists)`);
|
|
307
|
+
}
|
|
308
|
+
else if (existsSync(srcPath)) {
|
|
309
|
+
const content = readFileSync(srcPath, 'utf-8');
|
|
310
|
+
writeFileSync(destPath, content);
|
|
311
|
+
filesCreated++;
|
|
312
|
+
console.log(` ✅ ${file.src} — ${file.desc}`);
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
console.log(` ⚠️ ${file.src} — default template not found`);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
// Copy starter templates into ~/.reflectt/templates
|
|
319
|
+
const templatesSrcDir = join(__dirname, '..', 'templates');
|
|
320
|
+
const templatesDestDir = join(REFLECTT_HOME, 'templates');
|
|
321
|
+
mkdirSync(templatesDestDir, { recursive: true });
|
|
322
|
+
const templates = [
|
|
323
|
+
{ src: 'task-template.md', desc: 'Task template' },
|
|
324
|
+
{ src: 'review-packet.md', desc: 'Review packet template' },
|
|
325
|
+
{ src: 'incident-template.md', desc: 'Incident template' },
|
|
326
|
+
];
|
|
327
|
+
for (const file of templates) {
|
|
328
|
+
const destPath = join(templatesDestDir, file.src);
|
|
329
|
+
const srcPath = join(templatesSrcDir, file.src);
|
|
330
|
+
if (existsSync(destPath) && !opts.force) {
|
|
331
|
+
filesSkipped++;
|
|
332
|
+
console.log(` ⏭️ templates/${file.src} (exists)`);
|
|
333
|
+
}
|
|
334
|
+
else if (existsSync(srcPath)) {
|
|
335
|
+
const content = readFileSync(srcPath, 'utf-8');
|
|
336
|
+
writeFileSync(destPath, content);
|
|
337
|
+
filesCreated++;
|
|
338
|
+
console.log(` ✅ templates/${file.src} — ${file.desc}`);
|
|
339
|
+
}
|
|
340
|
+
else {
|
|
341
|
+
console.log(` ⚠️ templates/${file.src} — template not found in package`);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
// Git init
|
|
345
|
+
if (opts.git !== false) {
|
|
346
|
+
const gitDir = join(REFLECTT_HOME, '.git');
|
|
347
|
+
if (!existsSync(gitDir)) {
|
|
348
|
+
try {
|
|
349
|
+
execSync('git init', { cwd: REFLECTT_HOME, stdio: 'pipe' });
|
|
350
|
+
execSync('git add -A', { cwd: REFLECTT_HOME, stdio: 'pipe' });
|
|
351
|
+
execSync('git commit -m "chore: initialize team ops directory"', {
|
|
352
|
+
cwd: REFLECTT_HOME,
|
|
353
|
+
stdio: 'pipe',
|
|
354
|
+
env: {
|
|
355
|
+
...process.env,
|
|
356
|
+
GIT_AUTHOR_NAME: 'reflectt',
|
|
357
|
+
GIT_AUTHOR_EMAIL: 'init@reflectt.ai',
|
|
358
|
+
GIT_COMMITTER_NAME: 'reflectt',
|
|
359
|
+
GIT_COMMITTER_EMAIL: 'init@reflectt.ai',
|
|
360
|
+
},
|
|
361
|
+
});
|
|
362
|
+
console.log(' ✅ git repo initialized with initial commit');
|
|
363
|
+
}
|
|
364
|
+
catch (err) {
|
|
365
|
+
console.log(' ⚠️ git init failed (git may not be installed)');
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
else {
|
|
369
|
+
console.log(' ⏭️ .git (exists)');
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
console.log('');
|
|
373
|
+
if (isNew) {
|
|
374
|
+
console.log('🎉 Team ops directory initialized!');
|
|
375
|
+
}
|
|
376
|
+
else {
|
|
377
|
+
console.log(`✅ Init complete (${filesCreated} created, ${filesSkipped} existing)`);
|
|
378
|
+
}
|
|
379
|
+
console.log(` Home: ${REFLECTT_HOME}`);
|
|
380
|
+
console.log('');
|
|
381
|
+
console.log('Next steps:');
|
|
382
|
+
console.log(' 1. Edit TEAM.md with your team\'s mission and values');
|
|
383
|
+
console.log(' 2. Edit TEAM-ROLES.yaml with your agent roster');
|
|
384
|
+
console.log(' 3. Start the server: reflectt start');
|
|
385
|
+
});
|
|
386
|
+
// ============ START COMMAND ============
|
|
387
|
+
program
|
|
388
|
+
.command('start')
|
|
389
|
+
.description('Start the reflectt server')
|
|
390
|
+
.option('-d, --detach', 'Run in background')
|
|
391
|
+
.action(async (options) => {
|
|
392
|
+
if (!existsSync(REFLECTT_HOME)) {
|
|
393
|
+
console.error('❌ reflectt not initialized. Run: reflectt init');
|
|
394
|
+
process.exit(1);
|
|
395
|
+
}
|
|
396
|
+
// Check if already running
|
|
397
|
+
if (existsSync(PID_FILE)) {
|
|
398
|
+
const pid = readFileSync(PID_FILE, 'utf-8').trim();
|
|
399
|
+
try {
|
|
400
|
+
process.kill(Number(pid), 0); // Check if process exists
|
|
401
|
+
console.log(`⚠️ Server already running (PID: ${pid})`);
|
|
402
|
+
console.log(' Stop it first with: reflectt stop');
|
|
403
|
+
process.exit(1);
|
|
404
|
+
}
|
|
405
|
+
catch (err) {
|
|
406
|
+
// Process doesn't exist, clean up stale PID file
|
|
407
|
+
console.log('🧹 Cleaning up stale PID file...');
|
|
408
|
+
const { unlinkSync } = await import('fs');
|
|
409
|
+
unlinkSync(PID_FILE);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
const config = loadConfig();
|
|
413
|
+
const { projectRoot, serverPath } = getRuntimePaths();
|
|
414
|
+
if (!existsSync(serverPath)) {
|
|
415
|
+
console.error(`❌ Server file not found: ${serverPath}`);
|
|
416
|
+
process.exit(1);
|
|
417
|
+
}
|
|
418
|
+
const env = buildServerEnv(config);
|
|
419
|
+
if (options.detach) {
|
|
420
|
+
const pid = startServerDetached(config);
|
|
421
|
+
console.log('✅ Server started in background');
|
|
422
|
+
console.log(` PID: ${pid}`);
|
|
423
|
+
console.log(` URL: http://${config.host}:${config.port}`);
|
|
424
|
+
console.log(` Dashboard: http://${config.host}:${config.port}/dashboard`);
|
|
425
|
+
if (config.cloud) {
|
|
426
|
+
console.log(` Cloud: ${config.cloud.cloudUrl} (host: ${config.cloud.hostName})`);
|
|
427
|
+
}
|
|
428
|
+
console.log('\nCheck status: reflectt status');
|
|
429
|
+
}
|
|
430
|
+
else {
|
|
431
|
+
// Foreground mode
|
|
432
|
+
console.log('🚀 Starting reflectt server...');
|
|
433
|
+
console.log(` URL: http://${config.host}:${config.port}`);
|
|
434
|
+
console.log(` Dashboard: http://${config.host}:${config.port}/dashboard`);
|
|
435
|
+
if (config.cloud) {
|
|
436
|
+
console.log(` Cloud: ${config.cloud.cloudUrl} (host: ${config.cloud.hostName})`);
|
|
437
|
+
}
|
|
438
|
+
console.log(' Press Ctrl+C to stop\n');
|
|
439
|
+
const child = spawn('npx', ['tsx', serverPath], {
|
|
440
|
+
env,
|
|
441
|
+
stdio: 'inherit',
|
|
442
|
+
cwd: projectRoot,
|
|
443
|
+
});
|
|
444
|
+
writeFileSync(PID_FILE, String(child.pid));
|
|
445
|
+
child.on('exit', (code) => {
|
|
446
|
+
if (existsSync(PID_FILE)) {
|
|
447
|
+
unlinkSync(PID_FILE);
|
|
448
|
+
}
|
|
449
|
+
process.exit(code || 0);
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
});
|
|
453
|
+
// ============ STOP COMMAND ============
|
|
454
|
+
program
|
|
455
|
+
.command('stop')
|
|
456
|
+
.description('Stop the reflectt server')
|
|
457
|
+
.action(() => {
|
|
458
|
+
if (!existsSync(PID_FILE)) {
|
|
459
|
+
console.log('⚠️ Server is not running');
|
|
460
|
+
process.exit(0);
|
|
461
|
+
}
|
|
462
|
+
const pid = readFileSync(PID_FILE, 'utf-8').trim();
|
|
463
|
+
try {
|
|
464
|
+
process.kill(Number(pid), 'SIGTERM');
|
|
465
|
+
console.log('✅ Server stopped');
|
|
466
|
+
unlinkSync(PID_FILE);
|
|
467
|
+
}
|
|
468
|
+
catch (err) {
|
|
469
|
+
if (err.code === 'ESRCH') {
|
|
470
|
+
console.log('⚠️ Process not found (already stopped?)');
|
|
471
|
+
unlinkSync(PID_FILE);
|
|
472
|
+
}
|
|
473
|
+
else {
|
|
474
|
+
console.error('❌ Failed to stop server:', err.message);
|
|
475
|
+
process.exit(1);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
// ============ STATUS COMMAND ============
|
|
480
|
+
program
|
|
481
|
+
.command('status')
|
|
482
|
+
.description('Check server health and status')
|
|
483
|
+
.action(async () => {
|
|
484
|
+
const config = loadConfig();
|
|
485
|
+
// Check PID file
|
|
486
|
+
const isRunning = existsSync(PID_FILE);
|
|
487
|
+
const pid = isRunning ? readFileSync(PID_FILE, 'utf-8').trim() : null;
|
|
488
|
+
console.log('📊 reflectt Status');
|
|
489
|
+
console.log(` Config: ${CONFIG_PATH}`);
|
|
490
|
+
console.log(` URL: http://${config.host}:${config.port}`);
|
|
491
|
+
if (pid) {
|
|
492
|
+
try {
|
|
493
|
+
process.kill(Number(pid), 0); // Check if process exists
|
|
494
|
+
console.log(` Process: Running (PID: ${pid})`);
|
|
495
|
+
}
|
|
496
|
+
catch (err) {
|
|
497
|
+
console.log(` Process: Not found (stale PID file)`);
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
else {
|
|
502
|
+
console.log(` Process: Not running`);
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
// Try to get health status
|
|
506
|
+
try {
|
|
507
|
+
const health = await apiRequest('/health');
|
|
508
|
+
console.log('\n✅ Server Health');
|
|
509
|
+
console.log(` Status: ${health.status}`);
|
|
510
|
+
console.log(` Chat messages: ${health.chat?.messageCount || 0}`);
|
|
511
|
+
console.log(` Tasks: ${health.tasks?.taskCount || 0}`);
|
|
512
|
+
}
|
|
513
|
+
catch (err) {
|
|
514
|
+
console.log('\n⚠️ Server process exists but not responding');
|
|
515
|
+
}
|
|
516
|
+
});
|
|
517
|
+
// ============ DOCTOR COMMAND ============
|
|
518
|
+
program
|
|
519
|
+
.command('doctor')
|
|
520
|
+
.description('Run self-serve diagnostics (onboarding + support bundle)')
|
|
521
|
+
.option('--url <baseUrl>', 'Override base URL (default from ~/.reflectt/config.json)')
|
|
522
|
+
.option('--json', 'Print JSON output')
|
|
523
|
+
.option('--timeout <ms>', 'Per-request timeout in ms', '4000')
|
|
524
|
+
.action(async (options) => {
|
|
525
|
+
const config = loadConfig();
|
|
526
|
+
const clientHost = (config.host === '0.0.0.0' || config.host === '::') ? '127.0.0.1' : config.host;
|
|
527
|
+
const baseUrl = String(options.url || `http://${clientHost}:${config.port}`).replace(/\/+$/, '');
|
|
528
|
+
const timeoutMs = Math.max(250, Number(options.timeout || 4000));
|
|
529
|
+
const report = await collectDoctorReport({ baseUrl, timeoutMs });
|
|
530
|
+
if (options.json) {
|
|
531
|
+
console.log(JSON.stringify(report, null, 2));
|
|
532
|
+
if (report.overall === 'fail')
|
|
533
|
+
process.exit(2);
|
|
534
|
+
if (report.overall === 'warn')
|
|
535
|
+
process.exit(1);
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
console.log(formatDoctorHuman(report));
|
|
539
|
+
if (report.overall === 'fail')
|
|
540
|
+
process.exit(2);
|
|
541
|
+
if (report.overall === 'warn')
|
|
542
|
+
process.exit(1);
|
|
543
|
+
});
|
|
544
|
+
// ============ CHAT COMMANDS ============
|
|
545
|
+
const chat = program.command('chat').description('Chat commands');
|
|
546
|
+
chat
|
|
547
|
+
.command('send')
|
|
548
|
+
.description('Send a message')
|
|
549
|
+
.requiredOption('--from <agent>', 'Sender agent name')
|
|
550
|
+
.requiredOption('--content <text>', 'Message content')
|
|
551
|
+
.option('--to <agent>', 'Recipient agent')
|
|
552
|
+
.option('--channel <name>', 'Channel name')
|
|
553
|
+
.option('--thread <id>', 'Thread ID to reply to')
|
|
554
|
+
.action(async (options) => {
|
|
555
|
+
const body = {
|
|
556
|
+
from: options.from,
|
|
557
|
+
content: options.content,
|
|
558
|
+
};
|
|
559
|
+
if (options.to)
|
|
560
|
+
body.to = options.to;
|
|
561
|
+
if (options.channel)
|
|
562
|
+
body.channel = options.channel;
|
|
563
|
+
if (options.thread)
|
|
564
|
+
body.threadId = options.thread;
|
|
565
|
+
const result = await apiRequest('/chat/messages', {
|
|
566
|
+
method: 'POST',
|
|
567
|
+
headers: { 'Content-Type': 'application/json' },
|
|
568
|
+
body: JSON.stringify(body),
|
|
569
|
+
});
|
|
570
|
+
console.log('✅ Message sent');
|
|
571
|
+
console.log(` ID: ${result.message.id}`);
|
|
572
|
+
console.log(` From: ${result.message.from}`);
|
|
573
|
+
if (result.message.channel)
|
|
574
|
+
console.log(` Channel: ${result.message.channel}`);
|
|
575
|
+
});
|
|
576
|
+
chat
|
|
577
|
+
.command('list')
|
|
578
|
+
.description('List messages')
|
|
579
|
+
.option('--channel <name>', 'Filter by channel')
|
|
580
|
+
.option('--from <agent>', 'Filter by sender')
|
|
581
|
+
.option('--to <agent>', 'Filter by recipient')
|
|
582
|
+
.option('--limit <n>', 'Maximum number of messages', '20')
|
|
583
|
+
.action(async (options) => {
|
|
584
|
+
const query = new URLSearchParams();
|
|
585
|
+
if (options.channel)
|
|
586
|
+
query.set('channel', options.channel);
|
|
587
|
+
if (options.from)
|
|
588
|
+
query.set('from', options.from);
|
|
589
|
+
if (options.to)
|
|
590
|
+
query.set('to', options.to);
|
|
591
|
+
if (options.limit)
|
|
592
|
+
query.set('limit', options.limit);
|
|
593
|
+
const result = await apiRequest(`/chat/messages?${query}`);
|
|
594
|
+
if (result.messages.length === 0) {
|
|
595
|
+
console.log('No messages found');
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
console.log(`\n📬 Messages (${result.messages.length})\n`);
|
|
599
|
+
for (const msg of result.messages) {
|
|
600
|
+
const timestamp = new Date(msg.timestamp).toLocaleString();
|
|
601
|
+
const channel = msg.channel ? `[${msg.channel}]` : '';
|
|
602
|
+
console.log(`${timestamp} ${channel}`);
|
|
603
|
+
console.log(` ${msg.from} → ${msg.to || 'broadcast'}: ${msg.content}`);
|
|
604
|
+
console.log();
|
|
605
|
+
}
|
|
606
|
+
});
|
|
607
|
+
// ============ TASKS COMMANDS ============
|
|
608
|
+
const tasks = program.command('tasks').description('Task commands');
|
|
609
|
+
tasks
|
|
610
|
+
.command('list')
|
|
611
|
+
.description('List tasks')
|
|
612
|
+
.option('--status <status>', 'Filter by status (todo, doing, blocked, validating, done)')
|
|
613
|
+
.option('--assignee <agent>', 'Filter by assignee')
|
|
614
|
+
.option('--priority <p>', 'Filter by priority (P0, P1, P2, P3)')
|
|
615
|
+
.action(async (options) => {
|
|
616
|
+
const query = new URLSearchParams();
|
|
617
|
+
if (options.status)
|
|
618
|
+
query.set('status', options.status);
|
|
619
|
+
if (options.assignee)
|
|
620
|
+
query.set('assignee', options.assignee);
|
|
621
|
+
if (options.priority)
|
|
622
|
+
query.set('priority', options.priority);
|
|
623
|
+
const result = await apiRequest(`/tasks?${query}`);
|
|
624
|
+
if (result.tasks.length === 0) {
|
|
625
|
+
console.log('No tasks found');
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
console.log(`\n📋 Tasks (${result.tasks.length})\n`);
|
|
629
|
+
for (const task of result.tasks) {
|
|
630
|
+
const status = task.status.toUpperCase().padEnd(10);
|
|
631
|
+
const priority = task.priority ? `[${task.priority}]` : '';
|
|
632
|
+
const assignee = task.assignee ? `→ ${task.assignee}` : '';
|
|
633
|
+
console.log(`${status} ${priority} ${task.title} ${assignee}`);
|
|
634
|
+
if (task.description) {
|
|
635
|
+
console.log(` ${task.description}`);
|
|
636
|
+
}
|
|
637
|
+
console.log();
|
|
638
|
+
}
|
|
639
|
+
});
|
|
640
|
+
tasks
|
|
641
|
+
.command('next')
|
|
642
|
+
.description('Get next available task')
|
|
643
|
+
.option('--agent <name>', 'Agent requesting the task')
|
|
644
|
+
.action(async (options) => {
|
|
645
|
+
const query = new URLSearchParams();
|
|
646
|
+
if (options.agent)
|
|
647
|
+
query.set('agent', options.agent);
|
|
648
|
+
const result = await apiRequest(`/tasks/next?${query}`);
|
|
649
|
+
if (!result.task) {
|
|
650
|
+
console.log('🎉 No tasks available!');
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
const task = result.task;
|
|
654
|
+
console.log('\n📌 Next Task\n');
|
|
655
|
+
console.log(` ID: ${task.id}`);
|
|
656
|
+
console.log(` Title: ${task.title}`);
|
|
657
|
+
if (task.description)
|
|
658
|
+
console.log(` Description: ${task.description}`);
|
|
659
|
+
console.log(` Status: ${task.status}`);
|
|
660
|
+
if (task.priority)
|
|
661
|
+
console.log(` Priority: ${task.priority}`);
|
|
662
|
+
if (task.assignee)
|
|
663
|
+
console.log(` Assignee: ${task.assignee}`);
|
|
664
|
+
});
|
|
665
|
+
tasks
|
|
666
|
+
.command('create')
|
|
667
|
+
.description('Create a new task')
|
|
668
|
+
.requiredOption('--title <text>', 'Task title')
|
|
669
|
+
.requiredOption('--created-by <agent>', 'Agent creating the task')
|
|
670
|
+
.requiredOption('--assignee <agent>', 'Task owner/assignee')
|
|
671
|
+
.requiredOption('--reviewer <agent>', 'Task reviewer')
|
|
672
|
+
.requiredOption('--done-criteria <items...>', 'Done criteria (space-separated; quote each item)')
|
|
673
|
+
.requiredOption('--eta <text>', 'ETA (e.g., "30m" or "2026-02-15T18:00Z")')
|
|
674
|
+
.option('--description <text>', 'Task description')
|
|
675
|
+
.option('--status <status>', 'Initial status', 'todo')
|
|
676
|
+
.option('--priority <p>', 'Priority (P0, P1, P2, P3)')
|
|
677
|
+
.action(async (options) => {
|
|
678
|
+
const body = {
|
|
679
|
+
title: options.title,
|
|
680
|
+
createdBy: options.createdBy,
|
|
681
|
+
status: options.status,
|
|
682
|
+
assignee: options.assignee,
|
|
683
|
+
reviewer: options.reviewer,
|
|
684
|
+
done_criteria: options.doneCriteria,
|
|
685
|
+
eta: options.eta,
|
|
686
|
+
};
|
|
687
|
+
if (options.description)
|
|
688
|
+
body.description = options.description;
|
|
689
|
+
if (options.priority)
|
|
690
|
+
body.priority = options.priority;
|
|
691
|
+
const result = await apiRequest('/tasks', {
|
|
692
|
+
method: 'POST',
|
|
693
|
+
headers: { 'Content-Type': 'application/json' },
|
|
694
|
+
body: JSON.stringify(body),
|
|
695
|
+
});
|
|
696
|
+
console.log('✅ Task created');
|
|
697
|
+
console.log(` ID: ${result.task.id}`);
|
|
698
|
+
console.log(` Title: ${result.task.title}`);
|
|
699
|
+
console.log(` Status: ${result.task.status}`);
|
|
700
|
+
});
|
|
701
|
+
// ============ DOGFOOD COMMANDS ============
|
|
702
|
+
const dogfood = program.command('dogfood').description('Dogfood verification commands');
|
|
703
|
+
dogfood
|
|
704
|
+
.command('smoke')
|
|
705
|
+
.description('Run end-to-end cloud enrollment chain verification')
|
|
706
|
+
.requiredOption('--team-id <id>', 'Cloud team id to target')
|
|
707
|
+
.requiredOption('--token <jwt>', 'Bearer token for cloud API auth (team admin/owner)')
|
|
708
|
+
.option('--cloud-url <url>', 'Cloud API base URL', process.env.REFLECTT_CLOUD_URL || 'https://api.reflectt.ai')
|
|
709
|
+
.option('--dashboard-url <url>', 'Dashboard base URL', process.env.REFLECTT_APP_URL || 'https://app.reflectt.ai')
|
|
710
|
+
.option('--host-name <name>', 'Host name to register', `dogfood-${process.pid}`)
|
|
711
|
+
.option('--capability <value...>', 'Host capabilities', ['openclaw', 'dogfood-smoke'])
|
|
712
|
+
.action(async (options) => {
|
|
713
|
+
const cloudBase = String(options.cloudUrl || '').replace(/\/+$/, '');
|
|
714
|
+
const dashboardBase = String(options.dashboardUrl || '').replace(/\/+$/, '');
|
|
715
|
+
const teamId = String(options.teamId);
|
|
716
|
+
const token = String(options.token);
|
|
717
|
+
const hostName = String(options.hostName);
|
|
718
|
+
const capabilities = Array.isArray(options.capability) ? options.capability.map((v) => String(v).trim()).filter(Boolean) : ['openclaw'];
|
|
719
|
+
if (!cloudBase || !token || !teamId) {
|
|
720
|
+
console.error('❌ Missing required inputs: --cloud-url, --token, --team-id');
|
|
721
|
+
process.exit(1);
|
|
722
|
+
}
|
|
723
|
+
let failed = false;
|
|
724
|
+
let hostId = '';
|
|
725
|
+
console.log('🧪 Running dogfood smoke chain\n');
|
|
726
|
+
console.log(` Cloud API: ${cloudBase}`);
|
|
727
|
+
console.log(` Dashboard: ${dashboardBase}`);
|
|
728
|
+
console.log(` Team ID: ${teamId}`);
|
|
729
|
+
console.log(` Host Name: ${hostName}\n`);
|
|
730
|
+
// 1) register host token
|
|
731
|
+
const register = await cloudRequest(`${cloudBase}/api/hosts/register-token`, token, 'POST', { teamId });
|
|
732
|
+
const joinToken = register.json?.registerToken?.joinToken;
|
|
733
|
+
const registerOk = register.status === 201 && typeof joinToken === 'string' && joinToken.length > 0;
|
|
734
|
+
printStep('register host token', registerOk, registerOk ? 'join token issued' : `status ${register.status} ${(register.json?.error || '').toString()}`);
|
|
735
|
+
failed = failed || !registerOk;
|
|
736
|
+
// 2) claim host
|
|
737
|
+
let credential = '';
|
|
738
|
+
if (registerOk) {
|
|
739
|
+
const claim = await cloudRequest(`${cloudBase}/api/hosts/claim`, token, 'POST', {
|
|
740
|
+
joinToken,
|
|
741
|
+
name: hostName,
|
|
742
|
+
capabilities,
|
|
743
|
+
});
|
|
744
|
+
hostId = String(claim.json?.host?.id || '');
|
|
745
|
+
credential = String(claim.json?.credential?.token || '');
|
|
746
|
+
const claimOk = claim.status === 201 && Boolean(hostId);
|
|
747
|
+
printStep('claim host', claimOk, claimOk ? `hostId=${hostId}` : `status ${claim.status} ${(claim.json?.error || '').toString()}`);
|
|
748
|
+
failed = failed || !claimOk;
|
|
749
|
+
}
|
|
750
|
+
// 3) heartbeat
|
|
751
|
+
if (hostId) {
|
|
752
|
+
const heartbeat = await cloudRequest(`${cloudBase}/api/hosts/${encodeURIComponent(hostId)}/heartbeat`, token, 'POST', {
|
|
753
|
+
status: 'online',
|
|
754
|
+
agents: [{ id: 'dogfood-smoke', state: 'active' }],
|
|
755
|
+
activeTasks: [{ id: `smoke-${Date.now()}`, status: 'doing' }],
|
|
756
|
+
});
|
|
757
|
+
const heartbeatOk = heartbeat.status === 200 && String(heartbeat.json?.host?.status || '') === 'online';
|
|
758
|
+
printStep('heartbeat', heartbeatOk, heartbeatOk ? 'cloud accepted heartbeat' : `status ${heartbeat.status} ${(heartbeat.json?.error || '').toString()}`);
|
|
759
|
+
failed = failed || !heartbeatOk;
|
|
760
|
+
}
|
|
761
|
+
// 4) verify cloud state
|
|
762
|
+
let cloudHostSeen = false;
|
|
763
|
+
if (hostId) {
|
|
764
|
+
const verify = await cloudRequest(`${cloudBase}/api/hosts?teamId=${encodeURIComponent(teamId)}`, token, 'GET');
|
|
765
|
+
const hosts = Array.isArray(verify.json?.hosts) ? verify.json.hosts : [];
|
|
766
|
+
const match = hosts.find((h) => String(h?.id || '') === hostId);
|
|
767
|
+
cloudHostSeen = Boolean(match);
|
|
768
|
+
const verifyOk = verify.status === 200 && cloudHostSeen;
|
|
769
|
+
printStep('verify cloud state', verifyOk, verifyOk ? 'host visible in /api/hosts' : `status ${verify.status} host missing`);
|
|
770
|
+
failed = failed || !verifyOk;
|
|
771
|
+
}
|
|
772
|
+
// 5) verify dashboard reachability + data source alignment
|
|
773
|
+
if (hostId) {
|
|
774
|
+
const dashboardProbe = await fetch(`${dashboardBase}/dashboard/hosts`, { method: 'GET' })
|
|
775
|
+
.then((res) => ({ ok: res.status < 500, status: res.status }))
|
|
776
|
+
.catch(() => ({ ok: false, status: 0 }));
|
|
777
|
+
const dashboardOk = dashboardProbe.ok && cloudHostSeen;
|
|
778
|
+
printStep('verify dashboard reflection path', dashboardOk, dashboardOk
|
|
779
|
+
? `dashboard reachable (HTTP ${dashboardProbe.status}); host present in dashboard source endpoint`
|
|
780
|
+
: `dashboard probe HTTP ${dashboardProbe.status}; or host missing from source endpoint`);
|
|
781
|
+
failed = failed || !dashboardOk;
|
|
782
|
+
}
|
|
783
|
+
console.log('');
|
|
784
|
+
if (failed) {
|
|
785
|
+
console.error('❌ Dogfood smoke FAILED');
|
|
786
|
+
process.exit(1);
|
|
787
|
+
}
|
|
788
|
+
console.log('✅ Dogfood smoke PASSED');
|
|
789
|
+
if (hostId) {
|
|
790
|
+
console.log(` hostId: ${hostId}`);
|
|
791
|
+
if (credential) {
|
|
792
|
+
console.log(' credential: issued (shown once)');
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
});
|
|
796
|
+
// ============ HOST COMMANDS ============
|
|
797
|
+
// ============ BOOTSTRAP (one-shot install + connect + start) ============
|
|
798
|
+
program
|
|
799
|
+
.command('bootstrap')
|
|
800
|
+
.description('One-shot setup: init + connect to cloud + start server (for npx/agent use)')
|
|
801
|
+
.option('--join-token <token>', 'Cloud host join token')
|
|
802
|
+
.option('--api-key <key>', 'Team API key (agent flow)')
|
|
803
|
+
.option('--cloud-url <url>', 'Cloud API base URL', 'https://api.reflectt.ai')
|
|
804
|
+
.option('--name <hostName>', 'Host display name', hostname())
|
|
805
|
+
.option('--type <hostType>', 'Host type', 'openclaw')
|
|
806
|
+
.action(async (options) => {
|
|
807
|
+
try {
|
|
808
|
+
if (!options.joinToken && !options.apiKey) {
|
|
809
|
+
console.error('❌ Either --join-token or --api-key is required');
|
|
810
|
+
console.error('');
|
|
811
|
+
console.error('Usage:');
|
|
812
|
+
console.error(' npx reflectt-node bootstrap --join-token <token>');
|
|
813
|
+
console.error(' npx reflectt-node bootstrap --api-key <key>');
|
|
814
|
+
process.exit(1);
|
|
815
|
+
}
|
|
816
|
+
// Step 0: Preflight checks
|
|
817
|
+
console.log('🔍 Preflight checks...');
|
|
818
|
+
try {
|
|
819
|
+
const { runPreflight, formatPreflightReport } = await import('./preflight.js');
|
|
820
|
+
const report = await runPreflight({
|
|
821
|
+
cloudUrl: options.cloudUrl,
|
|
822
|
+
joinToken: options.joinToken,
|
|
823
|
+
apiKey: options.apiKey,
|
|
824
|
+
});
|
|
825
|
+
console.log(formatPreflightReport(report));
|
|
826
|
+
console.log('');
|
|
827
|
+
if (!report.allPassed && report.firstBlocker) {
|
|
828
|
+
console.error(`❌ Preflight failed: ${report.firstBlocker.message}`);
|
|
829
|
+
console.error('');
|
|
830
|
+
console.error('Fix the issue above and retry.');
|
|
831
|
+
process.exit(1);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
catch (err) {
|
|
835
|
+
console.log(' ⚠️ Preflight checks unavailable, proceeding...');
|
|
836
|
+
}
|
|
837
|
+
// Step 1: Init
|
|
838
|
+
console.log('📦 Step 1/3: Initializing reflectt home...');
|
|
839
|
+
ensureReflecttHome();
|
|
840
|
+
if (!existsSync(CONFIG_PATH)) {
|
|
841
|
+
saveConfig({ port: 4445, host: '127.0.0.1' });
|
|
842
|
+
}
|
|
843
|
+
console.log(` ✅ Home: ${REFLECTT_HOME}`);
|
|
844
|
+
// Step 2: Connect
|
|
845
|
+
console.log('☁️ Step 2/3: Connecting to Reflectt Cloud...');
|
|
846
|
+
const cloudUrl = String(options.cloudUrl || 'https://api.reflectt.ai').replace(/\/+$/, '');
|
|
847
|
+
const registered = options.apiKey
|
|
848
|
+
? await enrollHostWithApiKey({
|
|
849
|
+
cloudUrl,
|
|
850
|
+
apiKey: options.apiKey,
|
|
851
|
+
hostName: options.name,
|
|
852
|
+
hostType: options.type,
|
|
853
|
+
})
|
|
854
|
+
: await registerHostWithCloud({
|
|
855
|
+
cloudUrl,
|
|
856
|
+
joinToken: options.joinToken,
|
|
857
|
+
hostName: options.name,
|
|
858
|
+
hostType: options.type,
|
|
859
|
+
});
|
|
860
|
+
const config = loadConfig();
|
|
861
|
+
const nextConfig = {
|
|
862
|
+
...config,
|
|
863
|
+
cloud: {
|
|
864
|
+
cloudUrl,
|
|
865
|
+
hostName: options.name,
|
|
866
|
+
hostType: options.type,
|
|
867
|
+
hostId: registered.hostId,
|
|
868
|
+
credential: registered.credential,
|
|
869
|
+
connectedAt: Date.now(),
|
|
870
|
+
},
|
|
871
|
+
};
|
|
872
|
+
saveConfig(nextConfig);
|
|
873
|
+
console.log(` ✅ Registered (host: ${registered.hostId})`);
|
|
874
|
+
// Step 3: Start
|
|
875
|
+
console.log('🚀 Step 3/3: Starting reflectt server...');
|
|
876
|
+
if (isServerRunning()) {
|
|
877
|
+
console.log(' Server already running, reloading cloud config...');
|
|
878
|
+
const reloadResult = await tryApiRequest('/cloud/reload', { method: 'POST' });
|
|
879
|
+
if (reloadResult?.success) {
|
|
880
|
+
console.log(' ✅ Cloud config reloaded');
|
|
881
|
+
}
|
|
882
|
+
else {
|
|
883
|
+
console.log(' ⚠️ Reload failed, restarting...');
|
|
884
|
+
stopServerIfRunning();
|
|
885
|
+
startServerDetached(nextConfig);
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
else {
|
|
889
|
+
startServerDetached(nextConfig);
|
|
890
|
+
}
|
|
891
|
+
// Verify
|
|
892
|
+
const heartbeat = await waitForCloudHeartbeat();
|
|
893
|
+
if (heartbeat) {
|
|
894
|
+
const dashboardUrl = `http://127.0.0.1:${nextConfig?.port || 4445}/dashboard`;
|
|
895
|
+
console.log('');
|
|
896
|
+
console.log('✅ Bootstrap complete!');
|
|
897
|
+
console.log(` Host ID: ${registered.hostId}`);
|
|
898
|
+
console.log(` Cloud: ${cloudUrl}`);
|
|
899
|
+
console.log(` Heartbeats: ${heartbeat.heartbeatCount}`);
|
|
900
|
+
console.log('');
|
|
901
|
+
console.log('Your host is connected and reporting to Reflectt Cloud.');
|
|
902
|
+
console.log('');
|
|
903
|
+
console.log(`🖥️ Open your dashboard: ${dashboardUrl}`);
|
|
904
|
+
}
|
|
905
|
+
else {
|
|
906
|
+
const dashboardUrlTimeout = `http://127.0.0.1:${nextConfig?.port || 4445}/dashboard`;
|
|
907
|
+
console.log('');
|
|
908
|
+
console.log('⚠️ Bootstrap complete but heartbeat verification timed out.');
|
|
909
|
+
console.log(' Run `reflectt host status` to check.');
|
|
910
|
+
console.log('');
|
|
911
|
+
console.log(`🖥️ Open your dashboard: ${dashboardUrlTimeout}`);
|
|
912
|
+
process.exitCode = 1;
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
catch (err) {
|
|
916
|
+
console.error(`❌ Bootstrap failed: ${err?.message || err}`);
|
|
917
|
+
process.exit(1);
|
|
918
|
+
}
|
|
919
|
+
});
|
|
920
|
+
// ============ HOST COMMANDS ============
|
|
921
|
+
const host = program.command('host').description('Cloud host enrollment and status');
|
|
922
|
+
host
|
|
923
|
+
.command('connect')
|
|
924
|
+
.description('Enroll this reflectt-node host with Reflectt Cloud')
|
|
925
|
+
.option('--join-token <token>', 'Cloud host join token (from dashboard)')
|
|
926
|
+
.option('--api-key <key>', 'Team API key for agent-friendly enrollment (no browser needed)')
|
|
927
|
+
.option('--cloud-url <url>', 'Cloud API base URL', 'https://api.reflectt.ai')
|
|
928
|
+
.option('--name <hostName>', 'Host display name', hostname())
|
|
929
|
+
.option('--type <hostType>', 'Host type', 'openclaw')
|
|
930
|
+
.option('--auth-token <jwt>', 'Temporary user JWT for environments where claim endpoint is JWT-gated')
|
|
931
|
+
.option('--no-restart', 'Do not restart/start local reflectt server after enrollment')
|
|
932
|
+
.action(async (options) => {
|
|
933
|
+
try {
|
|
934
|
+
if (!options.joinToken && !options.apiKey) {
|
|
935
|
+
console.error('❌ Either --join-token or --api-key is required');
|
|
936
|
+
console.error(' --join-token <token> From dashboard (human flow)');
|
|
937
|
+
console.error(' --api-key <key> Team API key (agent flow)');
|
|
938
|
+
process.exit(1);
|
|
939
|
+
}
|
|
940
|
+
ensureReflecttHome();
|
|
941
|
+
const config = loadConfig();
|
|
942
|
+
const cloudUrl = String(options.cloudUrl || 'https://api.reflectt.ai').replace(/\/+$/, '');
|
|
943
|
+
console.log('☁️ Enrolling host with Reflectt Cloud...');
|
|
944
|
+
console.log(` Cloud: ${cloudUrl}`);
|
|
945
|
+
console.log(` Host: ${options.name} (${options.type})`);
|
|
946
|
+
console.log(` Method: ${options.apiKey ? 'API key' : 'join token'}`);
|
|
947
|
+
const registered = options.apiKey
|
|
948
|
+
? await enrollHostWithApiKey({
|
|
949
|
+
cloudUrl,
|
|
950
|
+
apiKey: options.apiKey,
|
|
951
|
+
hostName: options.name,
|
|
952
|
+
hostType: options.type,
|
|
953
|
+
})
|
|
954
|
+
: await registerHostWithCloud({
|
|
955
|
+
cloudUrl,
|
|
956
|
+
joinToken: options.joinToken,
|
|
957
|
+
hostName: options.name,
|
|
958
|
+
hostType: options.type,
|
|
959
|
+
authToken: options.authToken,
|
|
960
|
+
});
|
|
961
|
+
const nextConfig = {
|
|
962
|
+
...config,
|
|
963
|
+
cloud: {
|
|
964
|
+
cloudUrl,
|
|
965
|
+
hostName: options.name,
|
|
966
|
+
hostType: options.type,
|
|
967
|
+
hostId: registered.hostId,
|
|
968
|
+
credential: registered.credential,
|
|
969
|
+
connectedAt: Date.now(),
|
|
970
|
+
},
|
|
971
|
+
};
|
|
972
|
+
saveConfig(nextConfig);
|
|
973
|
+
console.log('✅ Cloud registration complete');
|
|
974
|
+
console.log(` Host ID: ${registered.hostId}`);
|
|
975
|
+
console.log(' Credential: received and stored in ~/.reflectt/config.json');
|
|
976
|
+
if (options.restart) {
|
|
977
|
+
if (isServerRunning()) {
|
|
978
|
+
// Try hot-reload first (avoids full restart)
|
|
979
|
+
console.log('🔄 Reloading cloud config on running server...');
|
|
980
|
+
const reloadResult = await tryApiRequest('/cloud/reload', { method: 'POST' });
|
|
981
|
+
if (reloadResult?.success) {
|
|
982
|
+
console.log('✅ Cloud config hot-reloaded');
|
|
983
|
+
const heartbeat = await waitForCloudHeartbeat();
|
|
984
|
+
if (heartbeat) {
|
|
985
|
+
console.log('✅ Heartbeat verified');
|
|
986
|
+
console.log(` Cloud host ID: ${heartbeat.hostId}`);
|
|
987
|
+
console.log(` Heartbeats sent: ${heartbeat.heartbeatCount}`);
|
|
988
|
+
}
|
|
989
|
+
else {
|
|
990
|
+
console.log('⚠️ Config reloaded, but heartbeat verification timed out');
|
|
991
|
+
console.log(' Check: reflectt host status');
|
|
992
|
+
process.exitCode = 1;
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
else {
|
|
996
|
+
// Fallback: full restart (older server without /cloud/reload)
|
|
997
|
+
console.log('⚠️ Hot-reload unavailable, falling back to full restart...');
|
|
998
|
+
stopServerIfRunning();
|
|
999
|
+
const pid = startServerDetached(nextConfig);
|
|
1000
|
+
console.log(` Server PID: ${pid}`);
|
|
1001
|
+
const heartbeat = await waitForCloudHeartbeat();
|
|
1002
|
+
if (heartbeat) {
|
|
1003
|
+
console.log('✅ Heartbeat verified');
|
|
1004
|
+
console.log(` Cloud host ID: ${heartbeat.hostId}`);
|
|
1005
|
+
console.log(` Heartbeats sent: ${heartbeat.heartbeatCount}`);
|
|
1006
|
+
}
|
|
1007
|
+
else {
|
|
1008
|
+
console.log('⚠️ Enrollment saved, but heartbeat verification timed out');
|
|
1009
|
+
console.log(' Check: reflectt status');
|
|
1010
|
+
console.log(' Check: reflectt host status');
|
|
1011
|
+
process.exitCode = 1;
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
else {
|
|
1016
|
+
console.log('🚀 Starting local reflectt server...');
|
|
1017
|
+
const pid = startServerDetached(nextConfig);
|
|
1018
|
+
console.log(` Server PID: ${pid}`);
|
|
1019
|
+
const heartbeat = await waitForCloudHeartbeat();
|
|
1020
|
+
if (heartbeat) {
|
|
1021
|
+
console.log('✅ Heartbeat verified');
|
|
1022
|
+
console.log(` Cloud host ID: ${heartbeat.hostId}`);
|
|
1023
|
+
console.log(` Heartbeats sent: ${heartbeat.heartbeatCount}`);
|
|
1024
|
+
}
|
|
1025
|
+
else {
|
|
1026
|
+
console.log('⚠️ Enrollment saved, but heartbeat verification timed out');
|
|
1027
|
+
console.log(' Check: reflectt status');
|
|
1028
|
+
console.log(' Check: reflectt host status');
|
|
1029
|
+
process.exitCode = 1;
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
else {
|
|
1034
|
+
console.log('ℹ️ Enrollment saved. Restart/start reflectt manually to begin heartbeats.');
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
catch (err) {
|
|
1038
|
+
console.error(`❌ Host connect failed: ${err?.message || err}`);
|
|
1039
|
+
process.exit(1);
|
|
1040
|
+
}
|
|
1041
|
+
});
|
|
1042
|
+
host
|
|
1043
|
+
.command('status')
|
|
1044
|
+
.description('Show local host cloud enrollment + heartbeat status')
|
|
1045
|
+
.action(async () => {
|
|
1046
|
+
const config = loadConfig();
|
|
1047
|
+
if (!config.cloud) {
|
|
1048
|
+
console.log('Cloud enrollment not configured.');
|
|
1049
|
+
console.log('Run: reflectt host connect --join-token <token>');
|
|
1050
|
+
return;
|
|
1051
|
+
}
|
|
1052
|
+
console.log('☁️ Cloud Enrollment');
|
|
1053
|
+
console.log(` Cloud URL: ${config.cloud.cloudUrl}`);
|
|
1054
|
+
console.log(` Host ID: ${config.cloud.hostId}`);
|
|
1055
|
+
console.log(` Host Name: ${config.cloud.hostName}`);
|
|
1056
|
+
console.log(` Host Type: ${config.cloud.hostType}`);
|
|
1057
|
+
console.log(` Connected At: ${new Date(config.cloud.connectedAt).toLocaleString()}`);
|
|
1058
|
+
const status = await tryApiRequest('/cloud/status');
|
|
1059
|
+
if (!status) {
|
|
1060
|
+
console.log('\n⚠️ Local server not reachable (cloud runtime status unavailable)');
|
|
1061
|
+
return;
|
|
1062
|
+
}
|
|
1063
|
+
console.log('\n📡 Runtime Cloud Status');
|
|
1064
|
+
console.log(` Configured: ${status.configured ? 'yes' : 'no'}`);
|
|
1065
|
+
console.log(` Registered: ${status.registered ? 'yes' : 'no'}`);
|
|
1066
|
+
console.log(` Running: ${status.running ? 'yes' : 'no'}`);
|
|
1067
|
+
console.log(` Heartbeats: ${status.heartbeatCount || 0}`);
|
|
1068
|
+
if (status.lastHeartbeat) {
|
|
1069
|
+
console.log(` Last Heartbeat: ${new Date(status.lastHeartbeat).toLocaleString()}`);
|
|
1070
|
+
}
|
|
1071
|
+
if (status.errors) {
|
|
1072
|
+
console.log(` Errors: ${status.errors}`);
|
|
1073
|
+
}
|
|
1074
|
+
});
|
|
1075
|
+
// ============ MEMORY COMMANDS ============
|
|
1076
|
+
const memory = program.command('memory').description('Memory commands');
|
|
1077
|
+
memory
|
|
1078
|
+
.command('read')
|
|
1079
|
+
.description('Read memory for an agent')
|
|
1080
|
+
.argument('<agent>', 'Agent name')
|
|
1081
|
+
.action(async (agent) => {
|
|
1082
|
+
const result = await apiRequest(`/memory/${agent}`);
|
|
1083
|
+
if (!result.success) {
|
|
1084
|
+
console.error('❌', result.error);
|
|
1085
|
+
process.exit(1);
|
|
1086
|
+
}
|
|
1087
|
+
if (result.memories.length === 0) {
|
|
1088
|
+
console.log(`No memory files for agent: ${agent}`);
|
|
1089
|
+
return;
|
|
1090
|
+
}
|
|
1091
|
+
console.log(`\n🧠 Memory for ${agent} (${result.memories.length} files)\n`);
|
|
1092
|
+
for (const mem of result.memories) {
|
|
1093
|
+
console.log(`📄 ${mem.file}`);
|
|
1094
|
+
console.log(` Size: ${mem.size} bytes`);
|
|
1095
|
+
console.log(` Modified: ${new Date(mem.mtime).toLocaleString()}`);
|
|
1096
|
+
console.log();
|
|
1097
|
+
}
|
|
1098
|
+
});
|
|
1099
|
+
memory
|
|
1100
|
+
.command('write')
|
|
1101
|
+
.description('Append to daily memory file')
|
|
1102
|
+
.argument('<agent>', 'Agent name')
|
|
1103
|
+
.requiredOption('--content <text>', 'Content to append')
|
|
1104
|
+
.action(async (agent, options) => {
|
|
1105
|
+
const result = await apiRequest(`/memory/${agent}`, {
|
|
1106
|
+
method: 'POST',
|
|
1107
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1108
|
+
body: JSON.stringify({ content: options.content }),
|
|
1109
|
+
});
|
|
1110
|
+
if (!result.success) {
|
|
1111
|
+
console.error('❌', result.error);
|
|
1112
|
+
process.exit(1);
|
|
1113
|
+
}
|
|
1114
|
+
console.log('✅ Memory written');
|
|
1115
|
+
console.log(` File: ${result.file}`);
|
|
1116
|
+
console.log(` Size: ${result.size} bytes`);
|
|
1117
|
+
});
|
|
1118
|
+
memory
|
|
1119
|
+
.command('search')
|
|
1120
|
+
.description('Search memory files')
|
|
1121
|
+
.argument('<agent>', 'Agent name')
|
|
1122
|
+
.requiredOption('--query <text>', 'Search query')
|
|
1123
|
+
.action(async (agent, options) => {
|
|
1124
|
+
const query = new URLSearchParams({ q: options.query });
|
|
1125
|
+
const result = await apiRequest(`/memory/${agent}/search?${query}`);
|
|
1126
|
+
if (!result.success) {
|
|
1127
|
+
console.error('❌', result.error);
|
|
1128
|
+
process.exit(1);
|
|
1129
|
+
}
|
|
1130
|
+
if (result.results.length === 0) {
|
|
1131
|
+
console.log('No matches found');
|
|
1132
|
+
return;
|
|
1133
|
+
}
|
|
1134
|
+
console.log(`\n🔍 Search Results (${result.count})\n`);
|
|
1135
|
+
for (const match of result.results) {
|
|
1136
|
+
console.log(`📄 ${match.file}`);
|
|
1137
|
+
console.log(` Line ${match.lineNumber}: ${match.line}`);
|
|
1138
|
+
console.log();
|
|
1139
|
+
}
|
|
1140
|
+
});
|
|
1141
|
+
program.parse();
|
|
1142
|
+
//# sourceMappingURL=cli.js.map
|