monomind 1.14.6 → 1.15.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/.claude/agents/reengineer-squad/boss.md +113 -0
- package/.claude/agents/reengineer-squad/critic-architect.md +132 -0
- package/.claude/agents/reengineer-squad/git-manager.md +145 -0
- package/.claude/agents/reengineer-squad/idea-generator.md +95 -0
- package/.claude/agents/reengineer-squad/implementer.md +112 -0
- package/.claude/agents/reengineer-squad/integration-planner.md +112 -0
- package/.claude/agents/reengineer-squad/source-analyst.md +103 -0
- package/.claude/agents/reengineer-squad/target-analyst.md +118 -0
- package/.claude/agents/reengineer-squad/tester.md +105 -0
- package/.claude/commands/mastermind/master.md +35 -14
- package/.claude/helpers/handlers/capture-handler.cjs +155 -18
- package/.claude/helpers/monolean-activate.cjs +20 -0
- package/.claude/helpers/monolean-config.cjs +76 -0
- package/.claude/helpers/monolean-instructions.cjs +109 -0
- package/.claude/helpers/monolean-propagate.cjs +9 -0
- package/.claude/helpers/monolean-tracker.cjs +18 -0
- package/.claude/helpers/skill-registry.json +2 -2
- package/.claude/settings.json +34 -2
- package/.claude/skills/agent-browser-testing/SKILL.md +301 -18
- package/.claude/skills/mastermind/runorg.md +69 -23
- package/.claude/skills/monodesign/SKILL.md +32 -1
- package/.claude/skills/monodesign/adapt.md +53 -0
- package/.claude/skills/monodesign/agents/monodesign-asset-producer.md +100 -0
- package/.claude/skills/monodesign/animate.md +65 -0
- package/.claude/skills/monodesign/audit.md +89 -0
- package/.claude/skills/monodesign/bolder.md +50 -0
- package/.claude/skills/monodesign/clarify.md +64 -0
- package/.claude/skills/monodesign/colorize.md +68 -0
- package/.claude/skills/monodesign/craft.md +51 -0
- package/.claude/skills/monodesign/critique.md +66 -0
- package/.claude/skills/monodesign/delight.md +47 -0
- package/.claude/skills/monodesign/distill.md +56 -0
- package/.claude/skills/monodesign/document.md +80 -0
- package/.claude/skills/monodesign/extract.md +74 -0
- package/.claude/skills/monodesign/harden.md +65 -0
- package/.claude/skills/monodesign/live.md +59 -0
- package/.claude/skills/monodesign/onboard.md +50 -0
- package/.claude/skills/monodesign/optimize.md +64 -0
- package/.claude/skills/monodesign/overdrive.md +56 -0
- package/.claude/skills/monodesign/polish.md +68 -0
- package/.claude/skills/monodesign/quieter.md +57 -0
- package/.claude/skills/monodesign/reference/antipatterns-catalog.md +248 -76
- package/.claude/skills/monodesign/reference/codex.md +107 -0
- package/.claude/skills/monodesign/reference/craft.md +3 -0
- package/.claude/skills/monodesign/reference/hooks.md +99 -0
- package/.claude/skills/monodesign/reference/image-prompts.md +12 -0
- package/.claude/skills/monodesign/shape.md +71 -0
- package/.claude/skills/monodesign/teach.md +69 -0
- package/.claude/skills/monodesign/typeset.md +59 -0
- package/.claude/skills/monolean/SKILL.md +118 -0
- package/.claude/skills/monolean-audit/SKILL.md +41 -0
- package/.claude/skills/monolean-debt/SKILL.md +46 -0
- package/.claude/skills/monolean-help/SKILL.md +60 -0
- package/.claude/skills/monolean-review/SKILL.md +57 -0
- package/package.json +8 -2
- package/packages/@monomind/cli/bin/cli.js +3 -1
- package/packages/@monomind/cli/dist/dashboard/server.js +137 -0
- package/packages/@monomind/cli/dist/src/__tests__/browse-adapters.test.d.ts +2 -0
- package/packages/@monomind/cli/dist/src/__tests__/browse-adapters.test.js +51 -0
- package/packages/@monomind/cli/dist/src/__tests__/browse-analyzer.test.d.ts +2 -0
- package/packages/@monomind/cli/dist/src/__tests__/browse-analyzer.test.js +68 -0
- package/packages/@monomind/cli/dist/src/__tests__/browse-builtin-handlers.test.d.ts +2 -0
- package/packages/@monomind/cli/dist/src/__tests__/browse-builtin-handlers.test.js +139 -0
- package/packages/@monomind/cli/dist/src/__tests__/browse-cdp.test.d.ts +2 -0
- package/packages/@monomind/cli/dist/src/__tests__/browse-cdp.test.js +169 -0
- package/packages/@monomind/cli/dist/src/__tests__/browse-dashboard.test.d.ts +2 -0
- package/packages/@monomind/cli/dist/src/__tests__/browse-dashboard.test.js +179 -0
- package/packages/@monomind/cli/dist/src/__tests__/browse-engine.test.d.ts +2 -0
- package/packages/@monomind/cli/dist/src/__tests__/browse-engine.test.js +122 -0
- package/packages/@monomind/cli/dist/src/__tests__/browse-expression.test.d.ts +2 -0
- package/packages/@monomind/cli/dist/src/__tests__/browse-expression.test.js +54 -0
- package/packages/@monomind/cli/dist/src/__tests__/browse-store.test.d.ts +2 -0
- package/packages/@monomind/cli/dist/src/__tests__/browse-store.test.js +99 -0
- package/packages/@monomind/cli/dist/src/__tests__/browse-workflow-types.test.d.ts +2 -0
- package/packages/@monomind/cli/dist/src/__tests__/browse-workflow-types.test.js +33 -0
- package/packages/@monomind/cli/dist/src/browser/action-builder/analyzer.d.ts +11 -0
- package/packages/@monomind/cli/dist/src/browser/action-builder/analyzer.js +71 -0
- package/packages/@monomind/cli/dist/src/browser/action-builder/types.d.ts +47 -0
- package/packages/@monomind/cli/dist/src/browser/action-builder/types.js +2 -0
- package/packages/@monomind/cli/dist/src/browser/adapters/gemini.d.ts +3 -0
- package/packages/@monomind/cli/dist/src/browser/adapters/gemini.js +16 -0
- package/packages/@monomind/cli/dist/src/browser/adapters/google.d.ts +3 -0
- package/packages/@monomind/cli/dist/src/browser/adapters/google.js +17 -0
- package/packages/@monomind/cli/dist/src/browser/adapters/index.d.ts +19 -0
- package/packages/@monomind/cli/dist/src/browser/adapters/index.js +23 -0
- package/packages/@monomind/cli/dist/src/browser/adapters/instagram.d.ts +3 -0
- package/packages/@monomind/cli/dist/src/browser/adapters/instagram.js +17 -0
- package/packages/@monomind/cli/dist/src/browser/adapters/linkedin.d.ts +3 -0
- package/packages/@monomind/cli/dist/src/browser/adapters/linkedin.js +19 -0
- package/packages/@monomind/cli/dist/src/browser/adapters/microsoft.d.ts +3 -0
- package/packages/@monomind/cli/dist/src/browser/adapters/microsoft.js +16 -0
- package/packages/@monomind/cli/dist/src/browser/adapters/x.d.ts +3 -0
- package/packages/@monomind/cli/dist/src/browser/adapters/x.js +19 -0
- package/packages/@monomind/cli/dist/src/browser/dashboard/api-types.d.ts +50 -0
- package/packages/@monomind/cli/dist/src/browser/dashboard/api-types.js +14 -0
- package/packages/@monomind/cli/dist/src/browser/dashboard/server.d.ts +9 -0
- package/packages/@monomind/cli/dist/src/browser/dashboard/server.js +62 -0
- package/packages/@monomind/cli/dist/src/browser/dashboard/ui.html +1811 -0
- package/packages/@monomind/cli/dist/src/browser/workflow/builtin-handlers.d.ts +3 -0
- package/packages/@monomind/cli/dist/src/browser/workflow/builtin-handlers.js +343 -0
- package/packages/@monomind/cli/dist/src/browser/workflow/engine.d.ts +15 -0
- package/packages/@monomind/cli/dist/src/browser/workflow/engine.js +127 -0
- package/packages/@monomind/cli/dist/src/browser/workflow/expression.d.ts +4 -0
- package/packages/@monomind/cli/dist/src/browser/workflow/expression.js +64 -0
- package/packages/@monomind/cli/dist/src/browser/workflow/store.d.ts +24 -0
- package/packages/@monomind/cli/dist/src/browser/workflow/store.js +145 -0
- package/packages/@monomind/cli/dist/src/browser/workflow/types.d.ts +48 -0
- package/packages/@monomind/cli/dist/src/browser/workflow/types.js +2 -0
- package/packages/@monomind/cli/dist/src/commands/browse-action.d.ts +4 -0
- package/packages/@monomind/cli/dist/src/commands/browse-action.js +151 -0
- package/packages/@monomind/cli/dist/src/commands/browse-platform.d.ts +4 -0
- package/packages/@monomind/cli/dist/src/commands/browse-platform.js +117 -0
- package/packages/@monomind/cli/dist/src/commands/browse-workflow.d.ts +4 -0
- package/packages/@monomind/cli/dist/src/commands/browse-workflow.js +153 -0
- package/packages/@monomind/cli/dist/src/commands/browse.d.ts +10 -6
- package/packages/@monomind/cli/dist/src/commands/browse.js +11 -2154
- package/packages/@monomind/cli/dist/src/commands/design-detect.d.ts +21 -0
- package/packages/@monomind/cli/dist/src/commands/design-detect.js +127 -0
- package/packages/@monomind/cli/dist/src/commands/design-palette.d.ts +22 -0
- package/packages/@monomind/cli/dist/src/commands/design-palette.js +539 -0
- package/packages/@monomind/cli/dist/src/commands/hooks-core-commands.d.ts +10 -0
- package/packages/@monomind/cli/dist/src/commands/hooks-core-commands.js +377 -0
- package/packages/@monomind/cli/dist/src/commands/hooks-coverage-commands.d.ts +12 -0
- package/packages/@monomind/cli/dist/src/commands/hooks-coverage-commands.js +1217 -0
- package/packages/@monomind/cli/dist/src/commands/hooks-coverage-utils.d.ts +42 -0
- package/packages/@monomind/cli/dist/src/commands/hooks-coverage-utils.js +220 -0
- package/packages/@monomind/cli/dist/src/commands/hooks-extended-commands.d.ts +14 -0
- package/packages/@monomind/cli/dist/src/commands/hooks-extended-commands.js +579 -0
- package/packages/@monomind/cli/dist/src/commands/hooks-formatting.d.ts +13 -0
- package/packages/@monomind/cli/dist/src/commands/hooks-formatting.js +42 -0
- package/packages/@monomind/cli/dist/src/commands/hooks-routing-commands.d.ts +15 -0
- package/packages/@monomind/cli/dist/src/commands/hooks-routing-commands.js +723 -0
- package/packages/@monomind/cli/dist/src/commands/hooks-workers.d.ts +9 -0
- package/packages/@monomind/cli/dist/src/commands/hooks-workers.js +782 -0
- package/packages/@monomind/cli/dist/src/commands/hooks.d.ts +8 -0
- package/packages/@monomind/cli/dist/src/commands/hooks.js +179 -4103
- package/packages/@monomind/cli/dist/src/commands/index.d.ts +1 -0
- package/packages/@monomind/cli/dist/src/commands/index.js +6 -0
- package/packages/@monomind/cli/dist/src/commands/org.js +14 -15
- package/packages/@monomind/cli/dist/src/commands/tokens.js +77 -1
- package/packages/@monomind/cli/dist/src/graph/enrich.mjs +362 -0
- package/packages/@monomind/cli/dist/src/init/executor.js +18 -8
- package/packages/@monomind/cli/dist/src/init/settings-generator.js +39 -5
- package/packages/@monomind/cli/dist/src/init/statusline-generator.js +25 -5
- package/packages/@monomind/cli/dist/src/mcp-tools/browser-tools.d.ts +3 -5
- package/packages/@monomind/cli/dist/src/mcp-tools/browser-tools.js +619 -326
- package/packages/@monomind/cli/dist/src/mcp-tools/hooks-embedding.d.ts +161 -0
- package/packages/@monomind/cli/dist/src/mcp-tools/hooks-embedding.js +506 -0
- package/packages/@monomind/cli/dist/src/mcp-tools/hooks-intelligence.d.ts +26 -0
- package/packages/@monomind/cli/dist/src/mcp-tools/hooks-intelligence.js +1328 -0
- package/packages/@monomind/cli/dist/src/mcp-tools/hooks-routing.d.ts +27 -0
- package/packages/@monomind/cli/dist/src/mcp-tools/hooks-routing.js +1591 -0
- package/packages/@monomind/cli/dist/src/mcp-tools/hooks-tools.d.ts +3 -38
- package/packages/@monomind/cli/dist/src/mcp-tools/hooks-tools.js +5 -3393
- package/packages/@monomind/cli/dist/src/mcp-tools/monograph-tools.js +24 -14
- package/packages/@monomind/cli/dist/src/mcp-tools/workflow-tools.js +54 -1
- package/packages/@monomind/cli/dist/src/memory/embedding-operations.d.ts +58 -0
- package/packages/@monomind/cli/dist/src/memory/embedding-operations.js +299 -0
- package/packages/@monomind/cli/dist/src/memory/ewc-consolidation.js +37 -3
- package/packages/@monomind/cli/dist/src/memory/hnsw-operations.d.ts +130 -0
- package/packages/@monomind/cli/dist/src/memory/hnsw-operations.js +400 -0
- package/packages/@monomind/cli/dist/src/memory/intelligence.js +42 -23
- package/packages/@monomind/cli/dist/src/memory/memory-bridge.js +52 -8
- package/packages/@monomind/cli/dist/src/memory/memory-crud.d.ts +67 -0
- package/packages/@monomind/cli/dist/src/memory/memory-crud.js +415 -0
- package/packages/@monomind/cli/dist/src/memory/memory-initializer.d.ts +9 -322
- package/packages/@monomind/cli/dist/src/memory/memory-initializer.js +17 -1794
- package/packages/@monomind/cli/dist/src/memory/memory-migrations.d.ts +30 -0
- package/packages/@monomind/cli/dist/src/memory/memory-migrations.js +134 -0
- package/packages/@monomind/cli/dist/src/memory/memory-read.d.ts +78 -0
- package/packages/@monomind/cli/dist/src/memory/memory-read.js +331 -0
- package/packages/@monomind/cli/dist/src/memory/memory-schema.d.ts +13 -0
- package/packages/@monomind/cli/dist/src/memory/memory-schema.js +167 -0
- package/packages/@monomind/cli/dist/src/memory/sona-optimizer.js +37 -4
- package/packages/@monomind/cli/dist/src/monovector/route-outcomes.js +16 -6
- package/packages/@monomind/cli/dist/src/pricing/model-pricing.d.ts +41 -0
- package/packages/@monomind/cli/dist/src/pricing/model-pricing.js +61 -0
- package/packages/@monomind/cli/dist/src/ui/.monomind/capture/active-run.json +1 -0
- package/packages/@monomind/cli/dist/src/ui/collector.mjs +799 -0
- package/packages/@monomind/cli/dist/src/ui/dashboard.html +13986 -0
- package/packages/@monomind/cli/dist/src/ui/data/agent-avatars.html +763 -0
- package/packages/@monomind/cli/dist/src/ui/data/agent-avatars.json +966 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/account-strategist.svg +58 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/accounts-payable.svg +54 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/adaptive-coordinator.svg +55 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/adaptive-coordinator2.svg +54 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/ai-citation.svg +57 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/ai-engineer.svg +61 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/analytics-reporter.svg +53 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/api-tester.svg +53 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/architecture.svg +54 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/automation-governance.svg +55 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/backend-dev.svg +53 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/benchmarker.svg +54 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/blockchain-auditor.svg +53 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/byzantine-coord.svg +57 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/case-analyst.svg +57 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/cicd-engineer.svg +55 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/cloud-architect.svg +54 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/code-review-swarm.svg +57 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/coder-v119.svg +57 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/coder.svg +58 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/collective-coord.svg +54 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/compliance-auditor.svg +58 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/consensus-coordinator.svg +54 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/content-creator.svg +54 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/crdt-synchronizer.svg +53 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/cro-specialist.svg +58 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/data-consolidator.svg +54 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/data-engineer.svg +53 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/database-optimizer.svg +61 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/deal-strategist.svg +54 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/defender.svg +53 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/devops-automator.svg +56 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/discovery-coach.svg +54 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/email-marketing.svg +57 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/embedded-firmware.svg +61 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/evidence-collector.svg +57 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/experiment-tracker.svg +53 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/feedback-synthesizer.svg +54 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/finance-tracker.svg +54 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/frontend-developer.svg +54 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/game-audio-engineer.svg +59 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/game-designer.svg +54 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/gossip-coordinator.svg +54 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/hierarchical-coord.svg +54 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/incident-commander.svg +57 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/infrastructure.svg +54 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/input-validator.svg +53 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/ios-developer.svg +54 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/issue-tracker.svg +53 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/judge.svg +55 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/launch-strategist.svg +54 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/legal-compliance.svg +53 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/level-designer.svg +53 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/load-balancer.svg +57 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/mcp-builder.svg +53 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/memory-coordinator.svg +55 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/mesh-coordinator.svg +55 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/ml-developer.svg +58 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/mobile-app-builder.svg +53 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/mobile-dev.svg +54 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/model-qa.svg +58 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/narrative-designer.svg +58 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/outbound-strategist.svg +55 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/path-validator.svg +54 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/payment-agent.svg +53 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/perf-analyzer.svg +58 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/pipeline-analyst.svg +54 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/planner.svg +55 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/pr-manager.svg +54 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/pricing-strategist.svg +54 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/product-manager.svg +54 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/production-validator.svg +54 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/project-shepherd.svg +54 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/proposal-strategist.svg +54 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/prosecutor.svg +57 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/pseudocode.svg +53 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/queen-coordinator.svg +55 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/quorum-manager.svg +53 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/raft-manager.svg +53 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/reality-checker.svg +58 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/recruitment.svg +58 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/refinement.svg +53 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/release-manager.svg +54 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/repo-architect.svg +54 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/researcher.svg +58 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/resource-allocator.svg +53 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/reviewer.svg +53 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/safe-executor.svg +53 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/sales-coach.svg +53 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/sales-engineer.svg +58 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/scout-explorer.svg +58 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/security-architect.svg +54 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/security-auditor.svg +55 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/senior-developer.svg +58 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/senior-pm.svg +58 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/seo-specialist.svg +57 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/social-media.svg +54 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/solidity-engineer.svg +58 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/sparc-coder.svg +58 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/sparc-coord.svg +56 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/specification.svg +57 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/sprint-prioritizer.svg +53 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/sre.svg +54 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/studio-operations.svg +53 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/studio-producer.svg +55 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/support-responder.svg +56 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/system-architect.svg +54 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/task-orchestrator.svg +56 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/technical-artist.svg +53 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/technical-writer.svg +59 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/tester.svg +53 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/threat-detection.svg +61 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/trend-researcher.svg +54 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/trial-director.svg +55 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/unity-architect.svg +54 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/visionos-engineer.svg +57 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/worker-specialist.svg +55 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/workflow-architect.svg +57 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/workflow-automation.svg +54 -0
- package/packages/@monomind/cli/dist/src/ui/data/avatars/zk-steward.svg +54 -0
- package/packages/@monomind/cli/dist/src/ui/data/known-projects.json +1 -0
- package/packages/@monomind/cli/dist/src/ui/data/mastermind-sessions.json +1 -0
- package/packages/@monomind/cli/dist/src/ui/data/sessions/_index.json +1 -0
- package/packages/@monomind/cli/dist/src/ui/orgs.html +2215 -0
- package/packages/@monomind/cli/dist/src/ui/server.mjs +6175 -0
- package/packages/@monomind/cli/dist/src/ui/sse-manager.mjs +119 -0
- package/packages/@monomind/cli/dist/src/update/checker.js +1 -1
- package/packages/@monomind/cli/dist/workflow/builtin-handlers.js +321 -0
- package/packages/@monomind/cli/dist/workflow/engine.js +253 -0
- package/packages/@monomind/cli/dist/workflow/expression.js +98 -0
- package/packages/@monomind/cli/dist/workflow/types.js +2 -0
- package/packages/@monomind/cli/package.json +8 -5
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { describe, it, expect, afterEach } from 'vitest';
|
|
2
|
+
import { startDashboard, getDashboard } from '@monoes/monobrowse';
|
|
3
|
+
import { WebSocket } from 'ws';
|
|
4
|
+
const PORT = 14242; // Use non-conflicting test port
|
|
5
|
+
const SSE_PORT = 14243; // Separate port for SSE streaming tests
|
|
6
|
+
afterEach(() => {
|
|
7
|
+
getDashboard()?.close();
|
|
8
|
+
});
|
|
9
|
+
function makeEvent(overrides = {}) {
|
|
10
|
+
return {
|
|
11
|
+
runId: 'run-1',
|
|
12
|
+
playbookId: 'wf-1',
|
|
13
|
+
playbookName: 'Test WF',
|
|
14
|
+
nodeId: 'node-1',
|
|
15
|
+
nodeName: 'Test Node',
|
|
16
|
+
eventType: 'step_completed',
|
|
17
|
+
timestamp: Date.now(),
|
|
18
|
+
...overrides,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
describe('startDashboard', () => {
|
|
22
|
+
it('returns a dashboard server instance', async () => {
|
|
23
|
+
const dash = startDashboard(PORT);
|
|
24
|
+
expect(dash).toBeDefined();
|
|
25
|
+
expect(dash.port).toBe(PORT);
|
|
26
|
+
});
|
|
27
|
+
it('returns the same instance on repeated calls', async () => {
|
|
28
|
+
const a = startDashboard(PORT);
|
|
29
|
+
const b = startDashboard(PORT);
|
|
30
|
+
expect(a).toBe(b);
|
|
31
|
+
});
|
|
32
|
+
it('serves HTML at GET /', async () => {
|
|
33
|
+
startDashboard(PORT);
|
|
34
|
+
const res = await fetch(`http://localhost:${PORT}/`);
|
|
35
|
+
expect(res.status).toBe(200);
|
|
36
|
+
expect(res.headers.get('content-type')).toContain('text/html');
|
|
37
|
+
const body = await res.text();
|
|
38
|
+
expect(body).toContain('monomind');
|
|
39
|
+
});
|
|
40
|
+
it('GET /runs returns JSON array', async () => {
|
|
41
|
+
const dash = startDashboard(PORT);
|
|
42
|
+
const run = { id: 'r1', playbookId: 'wf', playbookName: 'WF', status: 'completed', startedAt: Date.now(), itemsProcessed: 1, itemsTotal: 1 };
|
|
43
|
+
dash.addRunRecord(run);
|
|
44
|
+
const res = await fetch(`http://localhost:${PORT}/runs`);
|
|
45
|
+
expect(res.status).toBe(200);
|
|
46
|
+
const body = await res.json();
|
|
47
|
+
expect(body.some(r => r.id === 'r1')).toBe(true);
|
|
48
|
+
});
|
|
49
|
+
it('POST /stop/:runId marks run as stop-requested', async () => {
|
|
50
|
+
const dash = startDashboard(PORT);
|
|
51
|
+
const res = await fetch(`http://localhost:${PORT}/stop/run-abc`, { method: 'POST' });
|
|
52
|
+
expect(res.status).toBe(200);
|
|
53
|
+
expect(dash.isStopRequested('run-abc')).toBe(true);
|
|
54
|
+
});
|
|
55
|
+
it('isStopRequested returns false for unknown runId', () => {
|
|
56
|
+
const dash = startDashboard(PORT);
|
|
57
|
+
expect(dash.isStopRequested('nonexistent')).toBe(false);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
// SSE / WebSocket streaming tests
|
|
62
|
+
//
|
|
63
|
+
// The dashboard uses WebSocket (via 'ws') when the package is available, and
|
|
64
|
+
// falls back to Server-Sent Events when it is not. Since 'ws' IS installed in
|
|
65
|
+
// this repo the server runs in WebSocket mode. We test the streaming path
|
|
66
|
+
// (broadcast + ?dir= filter) through the WebSocket interface so the tests are
|
|
67
|
+
// consistent regardless of environment.
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
/**
|
|
70
|
+
* Open a WebSocket to the dashboard, wait for the initial 'history' message,
|
|
71
|
+
* then return a helper that collects subsequent messages.
|
|
72
|
+
*/
|
|
73
|
+
function connectWs(port, dir) {
|
|
74
|
+
return new Promise((resolve, reject) => {
|
|
75
|
+
const url = dir ? `ws://127.0.0.1:${port}/?dir=${encodeURIComponent(dir)}` : `ws://127.0.0.1:${port}/`;
|
|
76
|
+
const ws = new WebSocket(url);
|
|
77
|
+
const messages = [];
|
|
78
|
+
ws.on('open', () => {
|
|
79
|
+
// history message arrives; resolve after we have it
|
|
80
|
+
ws.on('message', (data) => {
|
|
81
|
+
const parsed = JSON.parse(data.toString());
|
|
82
|
+
messages.push(parsed);
|
|
83
|
+
if (messages.length === 1 && parsed.type === 'history') {
|
|
84
|
+
resolve({ ws, messages, close: () => ws.close() });
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
ws.on('error', reject);
|
|
89
|
+
setTimeout(() => reject(new Error('WS connect timeout')), 3000);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
describe('broadcast via WebSocket', () => {
|
|
93
|
+
it('delivers events to connected clients', async () => {
|
|
94
|
+
const dash = startDashboard(PORT);
|
|
95
|
+
// Wait a tick for the server to be ready
|
|
96
|
+
await new Promise(r => setTimeout(r, 50));
|
|
97
|
+
const { ws, messages, close } = await connectWs(PORT);
|
|
98
|
+
try {
|
|
99
|
+
// Wait for initial history message, then broadcast
|
|
100
|
+
dash.broadcast(makeEvent({ eventType: 'step_completed', nodeId: 'node-broadcast-1' }));
|
|
101
|
+
await new Promise(r => setTimeout(r, 100));
|
|
102
|
+
const events = messages.filter((m) => m.eventType !== undefined);
|
|
103
|
+
expect(events.length).toBeGreaterThan(0);
|
|
104
|
+
expect(events[0].nodeId).toBe('node-broadcast-1');
|
|
105
|
+
}
|
|
106
|
+
finally {
|
|
107
|
+
close();
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
it('?dir= filter — client with matching dir receives event', async () => {
|
|
111
|
+
const dash = startDashboard(PORT);
|
|
112
|
+
await new Promise(r => setTimeout(r, 50));
|
|
113
|
+
const dirA = '/projects/alpha';
|
|
114
|
+
const { messages, close } = await connectWs(PORT, dirA);
|
|
115
|
+
try {
|
|
116
|
+
dash.broadcast(makeEvent({ projectDir: dirA, nodeId: 'node-alpha' }));
|
|
117
|
+
await new Promise(r => setTimeout(r, 100));
|
|
118
|
+
const events = messages.filter((m) => m.eventType !== undefined);
|
|
119
|
+
expect(events.length).toBeGreaterThan(0);
|
|
120
|
+
expect(events[0].nodeId).toBe('node-alpha');
|
|
121
|
+
}
|
|
122
|
+
finally {
|
|
123
|
+
close();
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
it('?dir= filter — client with non-matching dir does NOT receive event', async () => {
|
|
127
|
+
const dash = startDashboard(PORT);
|
|
128
|
+
await new Promise(r => setTimeout(r, 50));
|
|
129
|
+
const { messages, close } = await connectWs(PORT, '/projects/beta');
|
|
130
|
+
try {
|
|
131
|
+
dash.broadcast(makeEvent({ projectDir: '/projects/alpha', nodeId: 'node-alpha-only' }));
|
|
132
|
+
await new Promise(r => setTimeout(r, 100));
|
|
133
|
+
const events = messages.filter((m) => m.eventType !== undefined);
|
|
134
|
+
expect(events.length).toBe(0);
|
|
135
|
+
}
|
|
136
|
+
finally {
|
|
137
|
+
close();
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
it('client without ?dir= receives all broadcasts', async () => {
|
|
141
|
+
const dash = startDashboard(PORT);
|
|
142
|
+
await new Promise(r => setTimeout(r, 50));
|
|
143
|
+
const { messages, close } = await connectWs(PORT);
|
|
144
|
+
try {
|
|
145
|
+
dash.broadcast(makeEvent({ projectDir: '/projects/alpha', nodeId: 'all-1' }));
|
|
146
|
+
dash.broadcast(makeEvent({ projectDir: '/projects/beta', nodeId: 'all-2' }));
|
|
147
|
+
await new Promise(r => setTimeout(r, 150));
|
|
148
|
+
const events = messages.filter((m) => m.eventType !== undefined);
|
|
149
|
+
expect(events.length).toBeGreaterThanOrEqual(2);
|
|
150
|
+
}
|
|
151
|
+
finally {
|
|
152
|
+
close();
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
describe('GET /events SSE endpoint', () => {
|
|
157
|
+
it('returns text/event-stream content-type when ws is unavailable', async () => {
|
|
158
|
+
// The dashboard is already started in ws mode on PORT.
|
|
159
|
+
// We verify the /events endpoint returns 404 in ws mode (ws is installed),
|
|
160
|
+
// which confirms the conditional branching works correctly.
|
|
161
|
+
const dash = startDashboard(PORT);
|
|
162
|
+
await new Promise(r => setTimeout(r, 50));
|
|
163
|
+
const res = await fetch(`http://127.0.0.1:${PORT}/events`, {
|
|
164
|
+
headers: { Accept: 'text/event-stream' },
|
|
165
|
+
});
|
|
166
|
+
// In ws mode the SSE endpoint is not registered — server returns 404
|
|
167
|
+
// In SSE-only mode it would return 200 with text/event-stream
|
|
168
|
+
// We test what the current mode actually does
|
|
169
|
+
if (res.status === 200) {
|
|
170
|
+
expect(res.headers.get('content-type')).toContain('text/event-stream');
|
|
171
|
+
await res.body?.cancel();
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
// ws mode: SSE not registered
|
|
175
|
+
expect(res.status).toBe(404);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
//# sourceMappingURL=browse-dashboard.test.js.map
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { runPlaybook } from '@monoes/monobrowse';
|
|
3
|
+
const triggerNode = { id: 'trigger', type: 'trigger.manual', config: {} };
|
|
4
|
+
describe('runPlaybook', () => {
|
|
5
|
+
it('runs a single-node trigger workflow', async () => {
|
|
6
|
+
const def = {
|
|
7
|
+
id: 'wf-1', name: 'Test', nodes: [triggerNode], connections: [],
|
|
8
|
+
};
|
|
9
|
+
const record = await runPlaybook(def);
|
|
10
|
+
expect(record.status).toBe('completed');
|
|
11
|
+
expect(record.playbookId).toBe('wf-1');
|
|
12
|
+
});
|
|
13
|
+
it('rejects cyclic workflow', async () => {
|
|
14
|
+
const def = {
|
|
15
|
+
id: 'cyclic', name: 'Cyclic',
|
|
16
|
+
nodes: [
|
|
17
|
+
{ id: 'a', type: 'core.set', config: {} },
|
|
18
|
+
{ id: 'b', type: 'core.set', config: {} },
|
|
19
|
+
],
|
|
20
|
+
connections: [{ from: 'a', to: 'b' }, { from: 'b', to: 'a' }],
|
|
21
|
+
};
|
|
22
|
+
await expect(runPlaybook(def)).rejects.toThrow('cycle');
|
|
23
|
+
});
|
|
24
|
+
it('calls node handlers for action nodes', async () => {
|
|
25
|
+
const handler = vi.fn().mockResolvedValue([{ data: { result: 'ok' } }]);
|
|
26
|
+
const def = {
|
|
27
|
+
id: 'wf-2', name: 'Action Test',
|
|
28
|
+
nodes: [
|
|
29
|
+
triggerNode,
|
|
30
|
+
{ id: 'act', type: 'action.linkedin.comment_post', config: { text: 'hi' } },
|
|
31
|
+
],
|
|
32
|
+
connections: [{ from: 'trigger', to: 'act' }],
|
|
33
|
+
};
|
|
34
|
+
const handlers = new Map([['action.linkedin.comment_post', handler]]);
|
|
35
|
+
const record = await runPlaybook(def, { handlers });
|
|
36
|
+
expect(handler).toHaveBeenCalled();
|
|
37
|
+
expect(record.status).toBe('completed');
|
|
38
|
+
});
|
|
39
|
+
it('emits step events in order', async () => {
|
|
40
|
+
const events = [];
|
|
41
|
+
const def = { id: 'wf-3', name: 'Events', nodes: [triggerNode], connections: [] };
|
|
42
|
+
await runPlaybook(def, { onEvent: e => events.push(e.eventType) });
|
|
43
|
+
expect(events[0]).toBe('run_started');
|
|
44
|
+
expect(events[events.length - 1]).toBe('run_completed');
|
|
45
|
+
});
|
|
46
|
+
it('skips failed node when onError is skip', async () => {
|
|
47
|
+
const handler = vi.fn().mockRejectedValue(new Error('fail'));
|
|
48
|
+
const def = {
|
|
49
|
+
id: 'wf-4', name: 'Skip',
|
|
50
|
+
nodes: [
|
|
51
|
+
triggerNode,
|
|
52
|
+
{ id: 'bad', type: 'action.x.fail', config: {}, onError: 'skip' },
|
|
53
|
+
],
|
|
54
|
+
connections: [{ from: 'trigger', to: 'bad' }],
|
|
55
|
+
};
|
|
56
|
+
const handlers = new Map([['action.x.fail', handler]]);
|
|
57
|
+
const record = await runPlaybook(def, { handlers });
|
|
58
|
+
expect(record.status).toBe('completed');
|
|
59
|
+
});
|
|
60
|
+
it('stops on node failure when onError is stop (default)', async () => {
|
|
61
|
+
const handler = vi.fn().mockRejectedValue(new Error('boom'));
|
|
62
|
+
const def = {
|
|
63
|
+
id: 'wf-5', name: 'Stop',
|
|
64
|
+
nodes: [
|
|
65
|
+
triggerNode,
|
|
66
|
+
{ id: 'bad', type: 'action.x.fail', config: {} },
|
|
67
|
+
],
|
|
68
|
+
connections: [{ from: 'trigger', to: 'bad' }],
|
|
69
|
+
};
|
|
70
|
+
const handlers = new Map([['action.x.fail', handler]]);
|
|
71
|
+
const record = await runPlaybook(def, { handlers });
|
|
72
|
+
expect(record.status).toBe('failed');
|
|
73
|
+
expect(record.error).toContain('boom');
|
|
74
|
+
});
|
|
75
|
+
it('stops when AbortSignal is aborted', async () => {
|
|
76
|
+
const controller = new AbortController();
|
|
77
|
+
controller.abort();
|
|
78
|
+
const def = { id: 'wf-6', name: 'Abort', nodes: [triggerNode], connections: [] };
|
|
79
|
+
const record = await runPlaybook(def, { signal: controller.signal });
|
|
80
|
+
expect(record.status).toBe('stopped');
|
|
81
|
+
});
|
|
82
|
+
it('core.set transforms item fields', async () => {
|
|
83
|
+
const def = {
|
|
84
|
+
id: 'wf-7', name: 'Set',
|
|
85
|
+
nodes: [
|
|
86
|
+
{ id: 'trigger', type: 'trigger.manual', config: { items: [{ data: { x: 1 } }] } },
|
|
87
|
+
{ id: 'setNode', type: 'core.set', config: { y: 'hardcoded' } },
|
|
88
|
+
],
|
|
89
|
+
connections: [{ from: 'trigger', to: 'setNode' }],
|
|
90
|
+
};
|
|
91
|
+
const events = [];
|
|
92
|
+
const record = await runPlaybook(def, { onEvent: e => events.push(e) });
|
|
93
|
+
expect(record.status).toBe('completed');
|
|
94
|
+
});
|
|
95
|
+
it('throws for unregistered action handler', async () => {
|
|
96
|
+
const def = {
|
|
97
|
+
id: 'wf-8', name: 'NoHandler',
|
|
98
|
+
nodes: [
|
|
99
|
+
triggerNode,
|
|
100
|
+
{ id: 'act', type: 'action.missing.thing', config: {} },
|
|
101
|
+
],
|
|
102
|
+
connections: [{ from: 'trigger', to: 'act' }],
|
|
103
|
+
};
|
|
104
|
+
const record = await runPlaybook(def);
|
|
105
|
+
expect(record.status).toBe('failed');
|
|
106
|
+
expect(record.error).toContain('No handler registered');
|
|
107
|
+
});
|
|
108
|
+
it('core.filter keeps matching items', async () => {
|
|
109
|
+
const def = {
|
|
110
|
+
id: 'wf-filter', name: 'Filter',
|
|
111
|
+
nodes: [
|
|
112
|
+
{ id: 'trigger', type: 'trigger.manual', config: { items: [{ data: { x: 1 } }, { data: { x: 2 } }, { data: { x: 3 } }] } },
|
|
113
|
+
{ id: 'filter', type: 'core.filter', config: { expression: '{{$json.x}}' } },
|
|
114
|
+
],
|
|
115
|
+
connections: [{ from: 'trigger', to: 'filter' }],
|
|
116
|
+
};
|
|
117
|
+
const record = await runPlaybook(def);
|
|
118
|
+
expect(record.status).toBe('completed');
|
|
119
|
+
expect(record.itemsProcessed).toBeGreaterThan(0);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
//# sourceMappingURL=browse-engine.test.js.map
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { describe, it, expect, afterEach, vi } from 'vitest';
|
|
2
|
+
import { resolveExpression, resolveConfig } from '@monoes/monobrowse';
|
|
3
|
+
const item = { data: { url: 'https://linkedin.com/post/123', comment: 'Great post!' } };
|
|
4
|
+
const nodeOutputs = {
|
|
5
|
+
'trigger': [{ data: { url: 'https://linkedin.com/post/123' } }],
|
|
6
|
+
};
|
|
7
|
+
const params = { text: 'Hello world', post_url: 'https://linkedin.com/post/123' };
|
|
8
|
+
describe('resolveExpression', () => {
|
|
9
|
+
it('resolves $json fields', () => {
|
|
10
|
+
expect(resolveExpression('{{$json.url}}', item, nodeOutputs, params)).toBe('https://linkedin.com/post/123');
|
|
11
|
+
});
|
|
12
|
+
it('resolves $env variables', () => {
|
|
13
|
+
vi.stubEnv('TEST_VAR', 'test-value');
|
|
14
|
+
expect(resolveExpression('{{$env.TEST_VAR}}', item, nodeOutputs, params)).toBe('test-value');
|
|
15
|
+
});
|
|
16
|
+
it('resolves params', () => {
|
|
17
|
+
expect(resolveExpression('{{params.text}}', item, nodeOutputs, params)).toBe('Hello world');
|
|
18
|
+
});
|
|
19
|
+
it('resolves node output references', () => {
|
|
20
|
+
expect(resolveExpression('{{$node.trigger.url}}', item, nodeOutputs, params)).toBe('https://linkedin.com/post/123');
|
|
21
|
+
});
|
|
22
|
+
it('resolves $node bracket notation', () => {
|
|
23
|
+
expect(resolveExpression('{{$node["trigger"].url}}', item, nodeOutputs, params)).toBe('https://linkedin.com/post/123');
|
|
24
|
+
});
|
|
25
|
+
it('returns raw string if no template markers', () => {
|
|
26
|
+
expect(resolveExpression('plain text', item, nodeOutputs, params)).toBe('plain text');
|
|
27
|
+
});
|
|
28
|
+
it('throws on unresolved expression', () => {
|
|
29
|
+
expect(() => resolveExpression('{{$json.missing}}', item, nodeOutputs, params)).toThrow('Unresolved');
|
|
30
|
+
});
|
|
31
|
+
it('replaces duplicate placeholders in a single template', () => {
|
|
32
|
+
const doubled = { data: { x: 'hello' } };
|
|
33
|
+
expect(resolveExpression('{{$json.x}} and {{$json.x}}', doubled, {}, {})).toBe('hello and hello');
|
|
34
|
+
});
|
|
35
|
+
it('throws when $node output array is empty', () => {
|
|
36
|
+
expect(() => resolveExpression('{{$node.empty.field}}', item, { empty: [] }, params)).toThrow('Unresolved');
|
|
37
|
+
});
|
|
38
|
+
it('throws when $node bracket field is missing', () => {
|
|
39
|
+
expect(() => resolveExpression('{{$node["trigger"].missing}}', item, nodeOutputs, params)).toThrow('Unresolved');
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
afterEach(() => {
|
|
43
|
+
vi.unstubAllEnvs();
|
|
44
|
+
});
|
|
45
|
+
describe('resolveConfig', () => {
|
|
46
|
+
it('resolves all string values in a config object', () => {
|
|
47
|
+
const config = { post_url: '{{$json.url}}', text: '{{params.text}}', count: 3 };
|
|
48
|
+
const result = resolveConfig(config, item, nodeOutputs, params);
|
|
49
|
+
expect(result.post_url).toBe('https://linkedin.com/post/123');
|
|
50
|
+
expect(result.text).toBe('Hello world');
|
|
51
|
+
expect(result.count).toBe(3);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
//# sourceMappingURL=browse-expression.test.js.map
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { readPlaybook, writePlaybookRun, listPlaybookRuns, readAction, clearRunStore } from '@monoes/monobrowse';
|
|
3
|
+
import { writeFileSync, mkdirSync, rmSync } from 'node:fs';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { tmpdir } from 'node:os';
|
|
6
|
+
const TMP = join(tmpdir(), 'browse-store-test-' + Date.now());
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
clearRunStore();
|
|
9
|
+
mkdirSync(join(TMP, 'playbooks'), { recursive: true });
|
|
10
|
+
mkdirSync(join(TMP, 'actions'), { recursive: true });
|
|
11
|
+
});
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
rmSync(TMP, { recursive: true, force: true });
|
|
14
|
+
});
|
|
15
|
+
describe('readPlaybook', () => {
|
|
16
|
+
it('parses a valid playbook JSON file', async () => {
|
|
17
|
+
const pb = {
|
|
18
|
+
id: 'test-pb',
|
|
19
|
+
name: 'Test Playbook',
|
|
20
|
+
nodes: [{ id: 'trigger', type: 'trigger.manual', config: {} }],
|
|
21
|
+
connections: [],
|
|
22
|
+
};
|
|
23
|
+
const p = join(TMP, 'playbooks', 'test.json');
|
|
24
|
+
writeFileSync(p, JSON.stringify(pb));
|
|
25
|
+
const result = await readPlaybook(p);
|
|
26
|
+
expect(result.id).toBe('test-pb');
|
|
27
|
+
expect(result.nodes).toHaveLength(1);
|
|
28
|
+
});
|
|
29
|
+
it('throws on missing file', async () => {
|
|
30
|
+
await expect(readPlaybook(join(TMP, 'nonexistent.json'))).rejects.toThrow();
|
|
31
|
+
});
|
|
32
|
+
it('throws on invalid JSON', async () => {
|
|
33
|
+
const p = join(TMP, 'playbooks', 'bad.json');
|
|
34
|
+
writeFileSync(p, 'not json');
|
|
35
|
+
await expect(readPlaybook(p)).rejects.toThrow();
|
|
36
|
+
});
|
|
37
|
+
it('throws if nodes array is missing', async () => {
|
|
38
|
+
const p = join(TMP, 'playbooks', 'no-nodes.json');
|
|
39
|
+
writeFileSync(p, JSON.stringify({ id: 'x', name: 'y', connections: [] }));
|
|
40
|
+
await expect(readPlaybook(p)).rejects.toThrow('nodes');
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
describe('readAction', () => {
|
|
44
|
+
it('parses a valid action JSON file', async () => {
|
|
45
|
+
const action = {
|
|
46
|
+
id: 'linkedin:comment_post',
|
|
47
|
+
platform: 'linkedin',
|
|
48
|
+
name: 'Comment on Post',
|
|
49
|
+
params: ['post_url', 'text'],
|
|
50
|
+
steps: [],
|
|
51
|
+
};
|
|
52
|
+
const p = join(TMP, 'actions', 'comment.json');
|
|
53
|
+
writeFileSync(p, JSON.stringify(action));
|
|
54
|
+
const result = await readAction(p);
|
|
55
|
+
expect(result.id).toBe('linkedin:comment_post');
|
|
56
|
+
expect(result.params).toContain('post_url');
|
|
57
|
+
});
|
|
58
|
+
it('throws on missing action file', async () => {
|
|
59
|
+
await expect(readAction(join(TMP, 'actions', 'nonexistent.json'))).rejects.toThrow();
|
|
60
|
+
});
|
|
61
|
+
it('throws on invalid action JSON', async () => {
|
|
62
|
+
const p = join(TMP, 'actions', 'bad.json');
|
|
63
|
+
writeFileSync(p, 'not json');
|
|
64
|
+
await expect(readAction(p)).rejects.toThrow();
|
|
65
|
+
});
|
|
66
|
+
it('throws if action steps array is missing', async () => {
|
|
67
|
+
const p = join(TMP, 'actions', 'no-steps.json');
|
|
68
|
+
writeFileSync(p, JSON.stringify({ id: 'x', platform: 'y', name: 'z', params: [] }));
|
|
69
|
+
await expect(readAction(p)).rejects.toThrow('steps');
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
describe('writePlaybookRun + listPlaybookRuns', () => {
|
|
73
|
+
it('stores and retrieves run records', async () => {
|
|
74
|
+
const record = {
|
|
75
|
+
id: 'run-1',
|
|
76
|
+
playbookId: 'pb-1',
|
|
77
|
+
playbookName: 'Test PB',
|
|
78
|
+
status: 'completed',
|
|
79
|
+
startedAt: Date.now(),
|
|
80
|
+
completedAt: Date.now() + 1000,
|
|
81
|
+
itemsProcessed: 5,
|
|
82
|
+
itemsTotal: 5,
|
|
83
|
+
};
|
|
84
|
+
await writePlaybookRun(record);
|
|
85
|
+
const runs = await listPlaybookRuns();
|
|
86
|
+
const found = runs.find(r => r.id === 'run-1');
|
|
87
|
+
expect(found).toBeDefined();
|
|
88
|
+
expect(found?.status).toBe('completed');
|
|
89
|
+
});
|
|
90
|
+
it('filters runs by playbookId', async () => {
|
|
91
|
+
const r1 = { id: 'r1', playbookId: 'pb-a', playbookName: 'A', status: 'completed', startedAt: 1, itemsProcessed: 1, itemsTotal: 1 };
|
|
92
|
+
const r2 = { id: 'r2', playbookId: 'pb-b', playbookName: 'B', status: 'failed', startedAt: 2, itemsProcessed: 0, itemsTotal: 1 };
|
|
93
|
+
await writePlaybookRun(r1);
|
|
94
|
+
await writePlaybookRun(r2);
|
|
95
|
+
const runs = await listPlaybookRuns('pb-a');
|
|
96
|
+
expect(runs.every(r => r.playbookId === 'pb-a')).toBe(true);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
//# sourceMappingURL=browse-store.test.js.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
describe('PlaybookDef', () => {
|
|
3
|
+
it('accepts a valid playbook definition', () => {
|
|
4
|
+
const wf = {
|
|
5
|
+
id: 'my-playbook',
|
|
6
|
+
name: 'My Playbook',
|
|
7
|
+
nodes: [
|
|
8
|
+
{ id: 'n1', type: 'trigger.manual', config: {} },
|
|
9
|
+
{ id: 'n2', type: 'action.linkedin.comment_post', config: { post_url: '{{$json.url}}', text: 'hi' } },
|
|
10
|
+
],
|
|
11
|
+
connections: [{ from: 'n1', to: 'n2' }],
|
|
12
|
+
};
|
|
13
|
+
expect(wf.nodes).toHaveLength(2);
|
|
14
|
+
expect(wf.connections[0].from).toBe('n1');
|
|
15
|
+
});
|
|
16
|
+
it('accepts a valid ActionDef with steps', () => {
|
|
17
|
+
const action = {
|
|
18
|
+
id: 'linkedin:comment_post',
|
|
19
|
+
platform: 'linkedin',
|
|
20
|
+
name: 'Comment on Post',
|
|
21
|
+
params: ['post_url', 'text'],
|
|
22
|
+
steps: [
|
|
23
|
+
{ type: 'navigate', url: '{{params.post_url}}' },
|
|
24
|
+
{ type: 'find', selectors: ['.comment-box', '[aria-label="Add a comment"]'], as: 'box' },
|
|
25
|
+
{ type: 'click', target: '{{box}}' },
|
|
26
|
+
{ type: 'type', target: '{{box}}', text: '{{params.text}}', humanDelay: true },
|
|
27
|
+
{ type: 'wait', condition: 'network_idle', timeout: 3000 },
|
|
28
|
+
],
|
|
29
|
+
};
|
|
30
|
+
expect(action.steps).toHaveLength(5);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
//# sourceMappingURL=browse-workflow-types.test.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ActionDef } from './types.js';
|
|
2
|
+
export declare function buildPrompt(task: string, domSnapshot: string): string;
|
|
3
|
+
export declare function parseActionResponse(response: string): ActionDef;
|
|
4
|
+
export declare function analyzeAndBuild(options: {
|
|
5
|
+
url: string;
|
|
6
|
+
task: string;
|
|
7
|
+
client: import('@monoes/monobrowse').CdpClient;
|
|
8
|
+
sessionId: string;
|
|
9
|
+
outputDir: string;
|
|
10
|
+
}): Promise<ActionDef>;
|
|
11
|
+
//# sourceMappingURL=analyzer.d.ts.map
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { writeFile, mkdir } from 'fs/promises';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
const SYSTEM_PROMPT = `You are a browser automation expert. Given a DOM snippet and a task description, generate a JSON action definition that automates the task.
|
|
4
|
+
|
|
5
|
+
Rules:
|
|
6
|
+
- Use ONLY these step types: navigate, find, click, type, wait, extract
|
|
7
|
+
- For "find" steps, provide 2-3 selector alternatives in order of preference (CSS, aria-label, XPath)
|
|
8
|
+
- For "type" steps, set humanDelay: true for form inputs
|
|
9
|
+
- Parameters use {{params.name}} syntax
|
|
10
|
+
- The action id format is "custom:<snake_case_name>"
|
|
11
|
+
- Output ONLY a JSON code block, no explanation before or after
|
|
12
|
+
|
|
13
|
+
JSON schema:
|
|
14
|
+
{
|
|
15
|
+
"id": "custom:action_name",
|
|
16
|
+
"platform": "custom",
|
|
17
|
+
"name": "Human Readable Name",
|
|
18
|
+
"params": ["param1", "param2"],
|
|
19
|
+
"steps": [...]
|
|
20
|
+
}`;
|
|
21
|
+
export function buildPrompt(task, domSnapshot) {
|
|
22
|
+
const truncated = domSnapshot.slice(0, 8000);
|
|
23
|
+
return `Task: ${task}\n\nDOM snapshot:\n${truncated}\n\nGenerate the action JSON.`;
|
|
24
|
+
}
|
|
25
|
+
export function parseActionResponse(response) {
|
|
26
|
+
const match = response.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
27
|
+
if (!match)
|
|
28
|
+
throw new Error('No JSON block found in Claude response');
|
|
29
|
+
try {
|
|
30
|
+
return JSON.parse(match[1].trim());
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
throw new Error(`Invalid JSON in response: ${err.message}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export async function analyzeAndBuild(options) {
|
|
37
|
+
const { url, task, client, sessionId, outputDir } = options;
|
|
38
|
+
// Navigate to the URL and wait for load
|
|
39
|
+
await client.send('Page.navigate', { url }, sessionId);
|
|
40
|
+
await new Promise(r => setTimeout(r, 2500)); // allow page to settle
|
|
41
|
+
// Capture DOM snapshot — accessibility tree + visible text
|
|
42
|
+
const domResult = await client.send('DOM.getDocument', { depth: 3, pierce: true }, sessionId);
|
|
43
|
+
const outerHtml = await client.send('DOM.getOuterHTML', { nodeId: domResult.root.nodeId }, sessionId);
|
|
44
|
+
// Strip scripts/styles, keep interactive elements
|
|
45
|
+
const cleanDom = outerHtml.outerHTML
|
|
46
|
+
.replace(/<script[\s\S]*?<\/script>/gi, '')
|
|
47
|
+
.replace(/<style[\s\S]*?<\/style>/gi, '')
|
|
48
|
+
.replace(/\s{2,}/g, ' ')
|
|
49
|
+
.slice(0, 12000);
|
|
50
|
+
// Call Claude API
|
|
51
|
+
const { Anthropic } = await import('@anthropic-ai/sdk');
|
|
52
|
+
const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
|
|
53
|
+
const message = await anthropic.messages.create({
|
|
54
|
+
model: 'claude-sonnet-4-6',
|
|
55
|
+
max_tokens: 2048,
|
|
56
|
+
system: SYSTEM_PROMPT,
|
|
57
|
+
messages: [{ role: 'user', content: buildPrompt(task, cleanDom) }],
|
|
58
|
+
});
|
|
59
|
+
const responseText = message.content
|
|
60
|
+
.filter(c => c.type === 'text')
|
|
61
|
+
.map(c => c.text)
|
|
62
|
+
.join('');
|
|
63
|
+
const action = parseActionResponse(responseText);
|
|
64
|
+
// Write to output directory
|
|
65
|
+
await mkdir(outputDir, { recursive: true });
|
|
66
|
+
const filename = action.id.replace(/[^a-z0-9_-]/gi, '_') + '.json';
|
|
67
|
+
const outPath = join(outputDir, filename);
|
|
68
|
+
await writeFile(outPath, JSON.stringify(action, null, 2));
|
|
69
|
+
return action;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=analyzer.js.map
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export type StepType = 'navigate' | 'find' | 'click' | 'type' | 'wait' | 'extract' | 'condition';
|
|
2
|
+
export interface NavigateStep {
|
|
3
|
+
type: 'navigate';
|
|
4
|
+
url: string;
|
|
5
|
+
}
|
|
6
|
+
export interface FindStep {
|
|
7
|
+
type: 'find';
|
|
8
|
+
selectors: string[];
|
|
9
|
+
as: string;
|
|
10
|
+
}
|
|
11
|
+
export interface ClickStep {
|
|
12
|
+
type: 'click';
|
|
13
|
+
target: string;
|
|
14
|
+
}
|
|
15
|
+
export interface TypeStep {
|
|
16
|
+
type: 'type';
|
|
17
|
+
target: string;
|
|
18
|
+
text: string;
|
|
19
|
+
humanDelay?: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface WaitStep {
|
|
22
|
+
type: 'wait';
|
|
23
|
+
condition: 'network_idle' | 'selector' | 'duration';
|
|
24
|
+
timeout?: number;
|
|
25
|
+
selector?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface ExtractStep {
|
|
28
|
+
type: 'extract';
|
|
29
|
+
target: string;
|
|
30
|
+
as: string;
|
|
31
|
+
attribute?: string;
|
|
32
|
+
}
|
|
33
|
+
export interface ConditionStep {
|
|
34
|
+
type: 'condition';
|
|
35
|
+
expression: string;
|
|
36
|
+
then: StepDef[];
|
|
37
|
+
else?: StepDef[];
|
|
38
|
+
}
|
|
39
|
+
export type StepDef = NavigateStep | FindStep | ClickStep | TypeStep | WaitStep | ExtractStep | ConditionStep;
|
|
40
|
+
export interface ActionDef {
|
|
41
|
+
id: string;
|
|
42
|
+
platform: string;
|
|
43
|
+
name: string;
|
|
44
|
+
params: string[];
|
|
45
|
+
steps: StepDef[];
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export const geminiAdapter = {
|
|
2
|
+
platform: 'gemini',
|
|
3
|
+
baseURL: 'https://gemini.google.com',
|
|
4
|
+
reservedPaths: ['/app', '/faq', '/privacy', '/terms', '/about'],
|
|
5
|
+
loginURL: () => 'https://accounts.google.com/signin',
|
|
6
|
+
async isLoggedIn(page) {
|
|
7
|
+
const url = await page.url();
|
|
8
|
+
if (url.includes('accounts.google.com'))
|
|
9
|
+
return false;
|
|
10
|
+
return page.evaluate(`!!document.querySelector('bard-sidenav, [data-test-id="bard-sidenav"]')`);
|
|
11
|
+
},
|
|
12
|
+
async extractUsername(page) {
|
|
13
|
+
return page.evaluate(`(document.querySelector('[data-email]')?.getAttribute('data-email') ?? 'unknown')`);
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=gemini.js.map
|