@codyswann/lisa 4.80.2 → 4.81.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/all/copy-contents/gitignore +5 -0
- package/dist/cli/doctor-session-manager-trust.d.ts +62 -0
- package/dist/cli/doctor-session-manager-trust.d.ts.map +1 -0
- package/dist/cli/doctor-session-manager-trust.js +174 -0
- package/dist/cli/doctor-session-manager-trust.js.map +1 -0
- package/dist/cli/doctor-session-manager.d.ts +35 -0
- package/dist/cli/doctor-session-manager.d.ts.map +1 -0
- package/dist/cli/doctor-session-manager.js +209 -0
- package/dist/cli/doctor-session-manager.js.map +1 -0
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +3 -0
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/lisa-test-run-bootstrap.js +28 -0
- package/dist/cli/lisa-test-run-bootstrap.js.map +1 -1
- package/dist/cli/session-manager-config-merge.d.ts +25 -0
- package/dist/cli/session-manager-config-merge.d.ts.map +1 -0
- package/dist/cli/session-manager-config-merge.js +54 -0
- package/dist/cli/session-manager-config-merge.js.map +1 -0
- package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +258 -12
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/dist/migrations/deploy-outcome-guard-edit.d.ts.map +1 -1
- package/dist/migrations/deploy-outcome-guard-edit.js +35 -14
- package/dist/migrations/deploy-outcome-guard-edit.js.map +1 -1
- package/dist/migrations/deploy-outcome-guard-yaml.d.ts +30 -1
- package/dist/migrations/deploy-outcome-guard-yaml.d.ts.map +1 -1
- package/dist/migrations/deploy-outcome-guard-yaml.js +88 -7
- package/dist/migrations/deploy-outcome-guard-yaml.js.map +1 -1
- package/package.json +4 -4
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-automation-status/SKILL.md +10 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-implement/SKILL.md +17 -2
- package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/validate-config.mjs +377 -5
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/SKILL.md +287 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/agents/openai.yaml +4 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/references/runbook-template.md +66 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/capacity.mjs +186 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-cycle.mjs +262 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-manager.mjs +308 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-queue.mjs +148 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-runtime.mjs +326 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-usage.mjs +114 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-workers.mjs +295 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/config.mjs +265 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/isolation.mjs +130 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/launch.mjs +234 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/ledger.mjs +445 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/reconcile.mjs +134 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/session-manager.mjs +274 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/state-dir.mjs +112 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/telegram-route.mjs +210 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/token.mjs +191 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/worker-launch.mjs +92 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/workers.mjs +406 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-session-manager/SKILL.md +158 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-session-manager/agents/openai.yaml +4 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-tear-down-automations/SKILL.md +10 -0
- package/plugins/lisa/commands/implement.md +2 -2
- package/plugins/lisa/commands/session-manager.md +7 -0
- package/plugins/lisa/commands/setup/session-manager.md +6 -0
- package/plugins/lisa/rules/reference/automation-runbook-contract.md +14 -0
- package/plugins/lisa/rules/reference/config-resolution.md +58 -0
- package/plugins/lisa/scripts/automation-status-claude-adapter.mjs +4 -1
- package/plugins/lisa/scripts/automation-status-codex-adapter.mjs +4 -1
- package/plugins/lisa/scripts/automation-status-expected-fleet.mjs +98 -0
- package/plugins/lisa/scripts/automation-status-run-history.mjs +29 -7
- package/plugins/lisa/scripts/ready-queue-order.mjs +352 -0
- package/plugins/lisa/skills/lisa-automation-status/SKILL.md +10 -0
- package/plugins/lisa/skills/lisa-implement/SKILL.md +17 -2
- package/plugins/lisa/skills/lisa-secrets-access/scripts/validate-config.mjs +377 -5
- package/plugins/lisa/skills/lisa-session-manager/SKILL.md +285 -0
- package/plugins/lisa/skills/lisa-session-manager/agents/openai.yaml +4 -0
- package/plugins/lisa/skills/lisa-session-manager/references/runbook-template.md +66 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/capacity.mjs +186 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/cli-cycle.mjs +262 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/cli-manager.mjs +308 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/cli-queue.mjs +148 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/cli-runtime.mjs +326 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/cli-usage.mjs +114 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/cli-workers.mjs +295 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/config.mjs +265 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/isolation.mjs +130 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/launch.mjs +234 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/ledger.mjs +445 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/reconcile.mjs +134 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/session-manager.mjs +274 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/state-dir.mjs +112 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/telegram-route.mjs +210 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/token.mjs +191 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/worker-launch.mjs +92 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/workers.mjs +406 -0
- package/plugins/lisa/skills/lisa-setup-session-manager/SKILL.md +156 -0
- package/plugins/lisa/skills/lisa-setup-session-manager/agents/openai.yaml +4 -0
- package/plugins/lisa/skills/lisa-tear-down-automations/SKILL.md +10 -0
- package/plugins/lisa-agy/commands/lisa/implement.md +2 -2
- package/plugins/lisa-agy/commands/lisa/session-manager.md +7 -0
- package/plugins/lisa-agy/commands/lisa/setup/session-manager.md +6 -0
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/scripts/automation-status-claude-adapter.mjs +4 -1
- package/plugins/lisa-agy/scripts/automation-status-codex-adapter.mjs +4 -1
- package/plugins/lisa-agy/scripts/automation-status-expected-fleet.mjs +98 -0
- package/plugins/lisa-agy/scripts/automation-status-run-history.mjs +29 -7
- package/plugins/lisa-agy/scripts/ready-queue-order.mjs +352 -0
- package/plugins/lisa-agy/skills/lisa-automation-status/SKILL.md +10 -0
- package/plugins/lisa-agy/skills/lisa-implement/SKILL.md +17 -2
- package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/validate-config.mjs +377 -5
- package/plugins/lisa-agy/skills/lisa-session-manager/SKILL.md +285 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/references/runbook-template.md +66 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/capacity.mjs +186 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-cycle.mjs +262 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-manager.mjs +308 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-queue.mjs +148 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-runtime.mjs +326 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-usage.mjs +114 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-workers.mjs +295 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/config.mjs +265 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/isolation.mjs +130 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/launch.mjs +234 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/ledger.mjs +445 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/reconcile.mjs +134 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/session-manager.mjs +274 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/state-dir.mjs +112 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/telegram-route.mjs +210 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/token.mjs +191 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/worker-launch.mjs +92 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/workers.mjs +406 -0
- package/plugins/lisa-agy/skills/lisa-setup-session-manager/SKILL.md +156 -0
- package/plugins/lisa-agy/skills/lisa-tear-down-automations/SKILL.md +10 -0
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/commands/lisa/implement.md +2 -2
- package/plugins/lisa-copilot/commands/lisa/session-manager.md +7 -0
- package/plugins/lisa-copilot/commands/lisa/setup/session-manager.md +6 -0
- package/plugins/lisa-copilot/rules/reference/automation-runbook-contract.md +14 -0
- package/plugins/lisa-copilot/rules/reference/config-resolution.md +58 -0
- package/plugins/lisa-copilot/scripts/automation-status-claude-adapter.mjs +4 -1
- package/plugins/lisa-copilot/scripts/automation-status-codex-adapter.mjs +4 -1
- package/plugins/lisa-copilot/scripts/automation-status-expected-fleet.mjs +98 -0
- package/plugins/lisa-copilot/scripts/automation-status-run-history.mjs +29 -7
- package/plugins/lisa-copilot/scripts/ready-queue-order.mjs +352 -0
- package/plugins/lisa-copilot/skills/lisa-automation-status/SKILL.md +10 -0
- package/plugins/lisa-copilot/skills/lisa-implement/SKILL.md +17 -2
- package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/validate-config.mjs +377 -5
- package/plugins/lisa-copilot/skills/lisa-session-manager/SKILL.md +285 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/references/runbook-template.md +66 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/capacity.mjs +186 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-cycle.mjs +262 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-manager.mjs +308 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-queue.mjs +148 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-runtime.mjs +326 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-usage.mjs +114 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-workers.mjs +295 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/config.mjs +265 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/isolation.mjs +130 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/launch.mjs +234 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/ledger.mjs +445 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/reconcile.mjs +134 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/session-manager.mjs +274 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/state-dir.mjs +112 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/telegram-route.mjs +210 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/token.mjs +191 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/worker-launch.mjs +92 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/workers.mjs +406 -0
- package/plugins/lisa-copilot/skills/lisa-setup-session-manager/SKILL.md +156 -0
- package/plugins/lisa-copilot/skills/lisa-tear-down-automations/SKILL.md +10 -0
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/commands/lisa/implement.md +2 -2
- package/plugins/lisa-cursor/commands/lisa/session-manager.md +7 -0
- package/plugins/lisa-cursor/commands/lisa/setup/session-manager.md +6 -0
- package/plugins/lisa-cursor/rules/automation-runbook-contract-reference.mdc +14 -0
- package/plugins/lisa-cursor/rules/config-resolution-reference.mdc +58 -0
- package/plugins/lisa-cursor/scripts/automation-status-claude-adapter.mjs +4 -1
- package/plugins/lisa-cursor/scripts/automation-status-codex-adapter.mjs +4 -1
- package/plugins/lisa-cursor/scripts/automation-status-expected-fleet.mjs +98 -0
- package/plugins/lisa-cursor/scripts/automation-status-run-history.mjs +29 -7
- package/plugins/lisa-cursor/scripts/ready-queue-order.mjs +352 -0
- package/plugins/lisa-cursor/skills/lisa-automation-status/SKILL.md +10 -0
- package/plugins/lisa-cursor/skills/lisa-implement/SKILL.md +17 -2
- package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/validate-config.mjs +377 -5
- package/plugins/lisa-cursor/skills/lisa-session-manager/SKILL.md +285 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/references/runbook-template.md +66 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/capacity.mjs +186 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-cycle.mjs +262 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-manager.mjs +308 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-queue.mjs +148 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-runtime.mjs +326 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-usage.mjs +114 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-workers.mjs +295 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/config.mjs +265 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/isolation.mjs +130 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/launch.mjs +234 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/ledger.mjs +445 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/reconcile.mjs +134 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/session-manager.mjs +274 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/state-dir.mjs +112 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/telegram-route.mjs +210 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/token.mjs +191 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/worker-launch.mjs +92 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/workers.mjs +406 -0
- package/plugins/lisa-cursor/skills/lisa-setup-session-manager/SKILL.md +156 -0
- package/plugins/lisa-cursor/skills/lisa-tear-down-automations/SKILL.md +10 -0
- package/plugins/lisa-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/lisa-devin/rules/reference/automation-runbook-contract.md +14 -0
- package/plugins/lisa-devin/rules/reference/config-resolution.md +58 -0
- package/plugins/lisa-devin/scripts/automation-status-claude-adapter.mjs +4 -1
- package/plugins/lisa-devin/scripts/automation-status-codex-adapter.mjs +4 -1
- package/plugins/lisa-devin/scripts/automation-status-expected-fleet.mjs +98 -0
- package/plugins/lisa-devin/scripts/automation-status-run-history.mjs +29 -7
- package/plugins/lisa-devin/scripts/ready-queue-order.mjs +352 -0
- package/plugins/lisa-devin/skills/lisa-automation-status/SKILL.md +10 -0
- package/plugins/lisa-devin/skills/lisa-implement/SKILL.md +17 -2
- package/plugins/lisa-devin/skills/lisa-secrets-access/scripts/validate-config.mjs +377 -5
- package/plugins/lisa-devin/skills/lisa-session-manager/SKILL.md +285 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/references/runbook-template.md +66 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/capacity.mjs +186 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-cycle.mjs +262 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-manager.mjs +308 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-queue.mjs +148 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-runtime.mjs +326 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-usage.mjs +114 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-workers.mjs +295 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/config.mjs +265 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/isolation.mjs +130 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/launch.mjs +234 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/ledger.mjs +445 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/reconcile.mjs +134 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/session-manager.mjs +274 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/state-dir.mjs +112 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/telegram-route.mjs +210 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/token.mjs +191 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/worker-launch.mjs +92 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/workers.mjs +406 -0
- package/plugins/lisa-devin/skills/lisa-setup-session-manager/SKILL.md +156 -0
- package/plugins/lisa-devin/skills/lisa-tear-down-automations/SKILL.md +10 -0
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/src/base/commands/implement.md +2 -2
- package/plugins/src/base/commands/session-manager.md +7 -0
- package/plugins/src/base/commands/setup/session-manager.md +6 -0
- package/plugins/src/base/rules/reference/automation-runbook-contract.md +14 -0
- package/plugins/src/base/rules/reference/config-resolution.md +58 -0
- package/plugins/src/base/scripts/automation-status-claude-adapter.mjs +4 -1
- package/plugins/src/base/scripts/automation-status-codex-adapter.mjs +4 -1
- package/plugins/src/base/scripts/automation-status-expected-fleet.mjs +98 -0
- package/plugins/src/base/scripts/automation-status-run-history.mjs +29 -7
- package/plugins/src/base/scripts/ready-queue-order.mjs +352 -0
- package/plugins/src/base/skills/lisa-automation-status/SKILL.md +10 -0
- package/plugins/src/base/skills/lisa-implement/SKILL.md +17 -2
- package/plugins/src/base/skills/lisa-secrets-access/scripts/validate-config.mjs +377 -5
- package/plugins/src/base/skills/lisa-session-manager/SKILL.md +285 -0
- package/plugins/src/base/skills/lisa-session-manager/references/runbook-template.md +66 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/capacity.mjs +186 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/cli-cycle.mjs +262 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/cli-manager.mjs +308 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/cli-queue.mjs +148 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/cli-runtime.mjs +326 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/cli-usage.mjs +114 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/cli-workers.mjs +295 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/config.mjs +265 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/isolation.mjs +130 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/launch.mjs +234 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/ledger.mjs +445 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/reconcile.mjs +134 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/session-manager.mjs +274 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/state-dir.mjs +112 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/telegram-route.mjs +210 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/token.mjs +191 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/worker-launch.mjs +92 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/workers.mjs +406 -0
- package/plugins/src/base/skills/lisa-setup-session-manager/SKILL.md +156 -0
- package/plugins/src/base/skills/lisa-tear-down-automations/SKILL.md +10 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decide which worker an inbound Telegram message is for, or whether it is an
|
|
3
|
+
* operator command.
|
|
4
|
+
*
|
|
5
|
+
* Several workers can be waiting on the operator at once, and an answer
|
|
6
|
+
* delivered to the wrong one is acted on with permissions skipped. So routing
|
|
7
|
+
* only ever uses an explicit signal, in this order:
|
|
8
|
+
*
|
|
9
|
+
* 1. a leading `[REF]`, `REF:` or `REF ` naming a ledger ref or its worker
|
|
10
|
+
* token (case-insensitive, exact token — `TEAM-1` never matches `TEAM-12`).
|
|
11
|
+
* A GitHub ref `owner/repo#12` also answers to `repo#12` and `#12`, which
|
|
12
|
+
* is what a phone user types; a short form that names more than one ledger
|
|
13
|
+
* item names none;
|
|
14
|
+
* 2. a Telegram reply to the message that carried a pending question. The
|
|
15
|
+
* Telegram channel plugin (0.0.7) does not pass the replied-to message id
|
|
16
|
+
* on, so the manager can only supply `--reply-to` from a plugin that does;
|
|
17
|
+
* the leading ref is what operators are told to use;
|
|
18
|
+
* 3. an operator command (`status`, `pause`, `resume`, `cap N`, `stop REF`).
|
|
19
|
+
* A recognised command with bad arguments (`cap 40`, `stop A B`) is
|
|
20
|
+
* `invalid-command`, never an answer;
|
|
21
|
+
* 4. the only pending question, when exactly one exists.
|
|
22
|
+
*
|
|
23
|
+
* Anything else is `ambiguous`, and the manager asks which item was meant.
|
|
24
|
+
* Commands sit below an explicit ref or reply, so "pause" sent as a reply to a
|
|
25
|
+
* question is an answer, but above the single-pending fallback, so a bare
|
|
26
|
+
* "pause" is never swallowed as an answer.
|
|
27
|
+
*
|
|
28
|
+
* The message text is untrusted data. It is sliced and returned, never
|
|
29
|
+
* evaluated, interpolated into a shell, or used as a pattern.
|
|
30
|
+
* @module telegram-route
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import { parseWorkerName, sanitizeRefToken } from "./workers.mjs";
|
|
34
|
+
|
|
35
|
+
/** Upper bound for `cap N`, matching the ledger's `control.maxWorkers`. */
|
|
36
|
+
const MAX_CAP = 32;
|
|
37
|
+
|
|
38
|
+
/** Commands that take no argument. */
|
|
39
|
+
const BARE_COMMANDS = new Set(["status", "pause", "resume"]);
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Leading-ref forms, tried in order. Each captures `[ref, body]`.
|
|
43
|
+
* @type {RegExp[]}
|
|
44
|
+
*/
|
|
45
|
+
const PREFIX_PATTERNS = [
|
|
46
|
+
/^\[([^\]\s]+)\]\s*([\s\S]*)$/,
|
|
47
|
+
/^([^\s:[\]]+)\s*:\s*([\s\S]*)$/,
|
|
48
|
+
/^(\S+)\s+([\s\S]*)$/,
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Every token that names a ledger item: the ref, its name-safe token, and the
|
|
53
|
+
* token in its worker's name.
|
|
54
|
+
* @param {string} ref - Ledger key
|
|
55
|
+
* @param {{worker?: string|null}} entry - Ledger item
|
|
56
|
+
* @returns {string[]} Lower-cased tokens
|
|
57
|
+
*/
|
|
58
|
+
function namesFor(ref, entry) {
|
|
59
|
+
const safeToken = (() => {
|
|
60
|
+
try {
|
|
61
|
+
return sanitizeRefToken(ref);
|
|
62
|
+
} catch {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
})();
|
|
66
|
+
return [
|
|
67
|
+
ref,
|
|
68
|
+
safeToken,
|
|
69
|
+
parseWorkerName(entry?.worker)?.token,
|
|
70
|
+
...shortForms(ref),
|
|
71
|
+
]
|
|
72
|
+
.filter(name => typeof name === "string")
|
|
73
|
+
.map(name => name.toLowerCase());
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* The phone-friendly short forms of a GitHub ref: `owner/repo#12` gives
|
|
78
|
+
* `repo#12` and `#12`. Jira and Linear keys have none.
|
|
79
|
+
* @param {string} ref - Ledger key
|
|
80
|
+
* @returns {string[]} Short forms, possibly empty
|
|
81
|
+
*/
|
|
82
|
+
function shortForms(ref) {
|
|
83
|
+
const github = /^[^/\s]+\/([^#\s]+)(#\d+)$/.exec(ref);
|
|
84
|
+
return github ? [`${github[1]}${github[2]}`, github[2]] : [];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Find the ledger ref a token names. A token that names several items (a
|
|
89
|
+
* `#12` shared by two repos) names none, so it can never pick one at random.
|
|
90
|
+
* @param {Record<string, object>} items - Ledger items
|
|
91
|
+
* @param {string} token - Candidate from the message
|
|
92
|
+
* @returns {string | null} Ledger key, or null
|
|
93
|
+
*/
|
|
94
|
+
function matchRef(items, token) {
|
|
95
|
+
const wanted = token.toLowerCase();
|
|
96
|
+
const matches = Object.keys(items).filter(ref =>
|
|
97
|
+
namesFor(ref, items[ref]).includes(wanted)
|
|
98
|
+
);
|
|
99
|
+
return matches.length === 1 ? matches[0] : null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Read a leading ref and the body after it.
|
|
104
|
+
* @param {Record<string, object>} items - Ledger items
|
|
105
|
+
* @param {string} text - Trimmed message
|
|
106
|
+
* @returns {{ref: string, body: string} | null} Match, or null
|
|
107
|
+
*/
|
|
108
|
+
function matchPrefix(items, text) {
|
|
109
|
+
return (
|
|
110
|
+
PREFIX_PATTERNS.map(pattern => pattern.exec(text))
|
|
111
|
+
.filter(match => match !== null)
|
|
112
|
+
.map(([, token, body]) => ({
|
|
113
|
+
ref: matchRef(items, token),
|
|
114
|
+
body: body.trim(),
|
|
115
|
+
}))
|
|
116
|
+
.find(({ ref, body }) => ref !== null && body !== "") ?? null
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Parse an operator command.
|
|
122
|
+
* @param {string} text - Message text
|
|
123
|
+
* @returns {{kind: "command", name: "status"|"pause"|"resume"} | {kind: "command", name: "cap", value: number} | {kind: "command", name: "stop", ref: string} | {kind: "not-a-command", reason?: string}} Command, or why not
|
|
124
|
+
*/
|
|
125
|
+
export function parseCommand(text) {
|
|
126
|
+
const words = (typeof text === "string" ? text : "")
|
|
127
|
+
.trim()
|
|
128
|
+
.replace(/^\//, "")
|
|
129
|
+
.split(/\s+/);
|
|
130
|
+
const name = words[0].toLowerCase();
|
|
131
|
+
const args = words.slice(1);
|
|
132
|
+
if (BARE_COMMANDS.has(name) && args.length === 0)
|
|
133
|
+
return { kind: "command", name };
|
|
134
|
+
if (name === "cap") {
|
|
135
|
+
const value =
|
|
136
|
+
args.length === 1 && /^\d+$/.test(args[0]) ? Number(args[0]) : Number.NaN;
|
|
137
|
+
return value <= MAX_CAP
|
|
138
|
+
? { kind: "command", name, value }
|
|
139
|
+
: {
|
|
140
|
+
kind: "not-a-command",
|
|
141
|
+
reason: `cap needs one whole number from 0 to ${MAX_CAP}`,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
if (name === "stop") {
|
|
145
|
+
return args.length === 1
|
|
146
|
+
? { kind: "command", name, ref: args[0] }
|
|
147
|
+
: { kind: "not-a-command", reason: "stop needs exactly one ref" };
|
|
148
|
+
}
|
|
149
|
+
return { kind: "not-a-command" };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Route one inbound message.
|
|
154
|
+
* @param {{text: string, replyToMessageId?: number|null, ledger: {items?: Record<string, {worker?: string|null, pendingQuestion?: {telegramMessageId?: number|null}|null}>}}} input - Message and current ledger
|
|
155
|
+
* @returns {{kind: "answer", ref: string, body: string} | ReturnType<typeof parseCommand> & {kind: "command"} | {kind: "invalid-command", reason: string} | {kind: "ambiguous", candidates: string[]}} Route
|
|
156
|
+
*/
|
|
157
|
+
export function routeInbound({ text, replyToMessageId, ledger }) {
|
|
158
|
+
const items = ledger?.items ?? {};
|
|
159
|
+
const message = typeof text === "string" ? text.trim() : "";
|
|
160
|
+
const pending = Object.keys(items)
|
|
161
|
+
.filter(ref => items[ref]?.pendingQuestion)
|
|
162
|
+
.sort();
|
|
163
|
+
const ambiguous = { kind: "ambiguous", candidates: pending };
|
|
164
|
+
if (message === "") return ambiguous;
|
|
165
|
+
|
|
166
|
+
const prefixed = matchPrefix(items, message);
|
|
167
|
+
if (prefixed)
|
|
168
|
+
return { kind: "answer", ref: prefixed.ref, body: prefixed.body };
|
|
169
|
+
|
|
170
|
+
const repliedTo =
|
|
171
|
+
replyToMessageId === undefined || replyToMessageId === null
|
|
172
|
+
? undefined
|
|
173
|
+
: pending.find(
|
|
174
|
+
ref =>
|
|
175
|
+
items[ref].pendingQuestion.telegramMessageId === replyToMessageId
|
|
176
|
+
);
|
|
177
|
+
if (repliedTo) return { kind: "answer", ref: repliedTo, body: message };
|
|
178
|
+
|
|
179
|
+
const command = parseCommand(message);
|
|
180
|
+
if (command.kind === "command") {
|
|
181
|
+
return command.name === "stop"
|
|
182
|
+
? { ...command, ref: matchRef(items, command.ref) ?? command.ref }
|
|
183
|
+
: command;
|
|
184
|
+
}
|
|
185
|
+
// A recognised command with bad arguments ("cap 40", "stop A B") is a
|
|
186
|
+
// mistyped command, not an answer. Falling through to the single-pending
|
|
187
|
+
// fallback would hand it to a worker that acts on it with permissions
|
|
188
|
+
// skipped, so the operator is told what was wrong instead.
|
|
189
|
+
if (command.reason) {
|
|
190
|
+
return { kind: "invalid-command", reason: command.reason };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return pending.length === 1
|
|
194
|
+
? { kind: "answer", ref: pending[0], body: message }
|
|
195
|
+
: ambiguous;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Wrap an operator's answer for delivery to a worker, so the worker can tell
|
|
200
|
+
* a human answer from the manager's own messages.
|
|
201
|
+
* @param {string} owner - Display name of the person who answered
|
|
202
|
+
* @param {string} body - Answer text, passed through as data
|
|
203
|
+
* @returns {string} Message for the worker
|
|
204
|
+
*/
|
|
205
|
+
export function formatAnswer(owner, body) {
|
|
206
|
+
if (typeof owner !== "string" || owner.trim() === "") {
|
|
207
|
+
throw new Error("owner must be a non-empty string");
|
|
208
|
+
}
|
|
209
|
+
return `${owner.trim()}'s answer: ${body}`;
|
|
210
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Telegram bot token the manager needs: validated, stored in
|
|
3
|
+
* `<stateDir>/.env` (dir 0700, file 0600), read from the operator's terminal
|
|
4
|
+
* with echo off, and never put in argv, a return value, an error message or a
|
|
5
|
+
* log line. An existing token is replaced only on request. Split out of
|
|
6
|
+
* launch.mjs, which re-exports everything here.
|
|
7
|
+
* @module token
|
|
8
|
+
*/
|
|
9
|
+
import { randomUUID } from "node:crypto";
|
|
10
|
+
import * as nodeFs from "node:fs";
|
|
11
|
+
import path from "node:path";
|
|
12
|
+
|
|
13
|
+
import { LaunchError, resolveStateDir } from "./state-dir.mjs";
|
|
14
|
+
|
|
15
|
+
/** The shape of a BotFather token: `<bot id>:<secret>`. */
|
|
16
|
+
const BOT_TOKEN_PATTERN = /^\d+:[A-Za-z0-9_-]{30,}$/;
|
|
17
|
+
|
|
18
|
+
/** The plugin reads its token from this file in the state dir. */
|
|
19
|
+
const TOKEN_FILE_NAME = ".env";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Whether `token` looks like a BotFather bot token.
|
|
23
|
+
* @param {unknown} token - Candidate
|
|
24
|
+
* @returns {boolean} True for `<digits>:<30+ of [A-Za-z0-9_-]>`
|
|
25
|
+
*/
|
|
26
|
+
export function validateBotToken(token) {
|
|
27
|
+
return typeof token === "string" && BOT_TOKEN_PATTERN.test(token);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Write `<stateDir>/.env` as `TELEGRAM_BOT_TOKEN=<token>`.
|
|
32
|
+
*
|
|
33
|
+
* The dir is chmod-ed to 0700 even when it already existed, because `mkdir`'s
|
|
34
|
+
* mode only applies to dirs it creates. The file is written to a temp name
|
|
35
|
+
* created 0600 and renamed over the target, so there is never a moment when a
|
|
36
|
+
* token sits in a file with looser permissions, and a replaced file's old
|
|
37
|
+
* mode never carries over.
|
|
38
|
+
* @param {string} stateDir - Absolute state dir (already resolved)
|
|
39
|
+
* @param {string} token - Bot token; validated, never echoed
|
|
40
|
+
* @param {{ fs?: Pick<typeof nodeFs, "mkdirSync" | "chmodSync" |
|
|
41
|
+
* "writeFileSync" | "renameSync"> }} [seams] - Injected fs
|
|
42
|
+
* @returns {{ path: string }} Where the token file is
|
|
43
|
+
*/
|
|
44
|
+
export function writeTokenFile(stateDir, token, seams = {}) {
|
|
45
|
+
const fs = seams.fs ?? nodeFs;
|
|
46
|
+
if (!validateBotToken(token)) {
|
|
47
|
+
throw new LaunchError(
|
|
48
|
+
"That does not look like a Telegram bot token " +
|
|
49
|
+
"(expected <digits>:<30 or more letters, digits, _ or ->). Nothing was stored."
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
fs.mkdirSync(stateDir, { recursive: true, mode: 0o700 });
|
|
53
|
+
fs.chmodSync(stateDir, 0o700);
|
|
54
|
+
const target = path.join(stateDir, TOKEN_FILE_NAME);
|
|
55
|
+
const temp = path.join(stateDir, `${TOKEN_FILE_NAME}.${randomUUID()}.tmp`);
|
|
56
|
+
fs.writeFileSync(temp, `TELEGRAM_BOT_TOKEN=${token}\n`, {
|
|
57
|
+
mode: 0o600,
|
|
58
|
+
flag: "wx",
|
|
59
|
+
});
|
|
60
|
+
fs.chmodSync(temp, 0o600);
|
|
61
|
+
fs.renameSync(temp, target);
|
|
62
|
+
return { path: target };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Load `promptSecret`/`canPrompt` from the secrets-access skill, by path
|
|
67
|
+
* relative to this file, the same way other skills reach it. Lazy so the
|
|
68
|
+
* argv builders never pay for it.
|
|
69
|
+
* @returns {Promise<{ promptSecret: Function, canPrompt: Function }>} Module
|
|
70
|
+
*/
|
|
71
|
+
export function loadPromptSecret() {
|
|
72
|
+
return import(
|
|
73
|
+
new URL(
|
|
74
|
+
"../../lisa-secrets-access/scripts/prompt-secret.mjs",
|
|
75
|
+
import.meta.url
|
|
76
|
+
).href
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Quote one word for a POSIX shell.
|
|
82
|
+
* @param {string} word - Word to quote
|
|
83
|
+
* @returns {string} Single-quoted word
|
|
84
|
+
*/
|
|
85
|
+
function shellQuote(word) {
|
|
86
|
+
const escaped = String(word).replaceAll("'", String.raw`'\''`);
|
|
87
|
+
return `'${escaped}'`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* The command an operator pastes into their own terminal to store the token,
|
|
92
|
+
* for when the caller (an agent's Bash) has no TTY to prompt on.
|
|
93
|
+
* @param {{ scriptsDir: string, projectRoot: string, replace?: boolean }}
|
|
94
|
+
* input - Script and project locations, and whether to carry `--replace`
|
|
95
|
+
* @returns {string} One shell line
|
|
96
|
+
*/
|
|
97
|
+
export function buildStoreTokenCommand({ scriptsDir, projectRoot, replace }) {
|
|
98
|
+
const script = shellQuote(path.join(scriptsDir, "session-manager.mjs"));
|
|
99
|
+
const flag = replace ? " --replace" : "";
|
|
100
|
+
return `node ${script} store-token${flag} --project-root ${shellQuote(projectRoot)}`;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Prompt for the bot token on the operator's terminal and store it.
|
|
105
|
+
*
|
|
106
|
+
* An existing token is never overwritten without `replace`: a second project
|
|
107
|
+
* that shares this state dir by mistake would otherwise silently take over the
|
|
108
|
+
* first project's bot. With no TTY this does not prompt and does not fail: it
|
|
109
|
+
* returns the exact command for the operator to run themselves, because an
|
|
110
|
+
* agent's Bash can never answer a prompt and a hung setup looks like a slow
|
|
111
|
+
* one.
|
|
112
|
+
* @param {{ stateDir: string, scriptsDir: string, projectRoot: string,
|
|
113
|
+
* replace?: boolean, env?: Record<string, string | undefined>,
|
|
114
|
+
* homedir?: string, io?: { canPrompt?: () => boolean,
|
|
115
|
+
* promptSecret?: (message: string) => string, fs?: object } }} input -
|
|
116
|
+
* Configured state dir, locations, the `--replace` flag and seams
|
|
117
|
+
* @returns {Promise<{ stored: true, path: string } |
|
|
118
|
+
* { stored: false, reason: "no-tty", command: string } |
|
|
119
|
+
* { stored: false, reason: "empty" | "invalid", message: string }>} Outcome
|
|
120
|
+
*/
|
|
121
|
+
export async function storeToken(input) {
|
|
122
|
+
const { scriptsDir, projectRoot, replace = false, io = {} } = input;
|
|
123
|
+
const stateDir = resolveStateDir(input.stateDir, input);
|
|
124
|
+
const existing = path.join(stateDir, TOKEN_FILE_NAME);
|
|
125
|
+
const exists = io.fs?.existsSync ?? nodeFs.existsSync;
|
|
126
|
+
if (!replace && exists(existing)) {
|
|
127
|
+
throw new LaunchError(
|
|
128
|
+
`A bot token already exists at ${existing}. Nothing was changed. ` +
|
|
129
|
+
`Re-run store-token with --replace to overwrite it.`
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
const loaded =
|
|
133
|
+
io.canPrompt && io.promptSecret ? null : await loadPromptSecret();
|
|
134
|
+
const canPrompt = io.canPrompt ?? (() => loaded.canPrompt());
|
|
135
|
+
const promptSecret = io.promptSecret ?? loaded.promptSecret;
|
|
136
|
+
|
|
137
|
+
if (!canPrompt()) {
|
|
138
|
+
return {
|
|
139
|
+
stored: false,
|
|
140
|
+
reason: "no-tty",
|
|
141
|
+
command: buildStoreTokenCommand({ scriptsDir, projectRoot, replace }),
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
const typed = promptSecret(
|
|
145
|
+
`Paste the Telegram bot token for ${stateDir} (input is hidden): `
|
|
146
|
+
);
|
|
147
|
+
if (!typed) {
|
|
148
|
+
return { stored: false, reason: "empty", message: "Nothing entered." };
|
|
149
|
+
}
|
|
150
|
+
if (!validateBotToken(typed)) {
|
|
151
|
+
return {
|
|
152
|
+
stored: false,
|
|
153
|
+
reason: "invalid",
|
|
154
|
+
message:
|
|
155
|
+
"That does not look like a Telegram bot token " +
|
|
156
|
+
"(expected <digits>:<30 or more letters, digits, _ or ->).",
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
return { stored: true, ...writeTokenFile(stateDir, typed, io) };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Refuse to start unless `<stateDir>/.env` exists and only its owner can read
|
|
164
|
+
* it. Only the file's metadata is read, never its contents.
|
|
165
|
+
* @param {string} stateDir - Absolute state dir (already resolved)
|
|
166
|
+
* @param {{ fs?: Pick<typeof nodeFs, "statSync"> }} [seams] - Injected fs
|
|
167
|
+
* @returns {{ path: string }} The token file
|
|
168
|
+
*/
|
|
169
|
+
export function assertTokenPresent(stateDir, seams = {}) {
|
|
170
|
+
const fs = seams.fs ?? nodeFs;
|
|
171
|
+
const file = path.join(stateDir, TOKEN_FILE_NAME);
|
|
172
|
+
let stat;
|
|
173
|
+
try {
|
|
174
|
+
stat = fs.statSync(file);
|
|
175
|
+
} catch {
|
|
176
|
+
throw new LaunchError(
|
|
177
|
+
`No Telegram bot token at ${file}. Run store-token first.`
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
if (!stat.isFile()) {
|
|
181
|
+
throw new LaunchError(`${file} is not a regular file.`);
|
|
182
|
+
}
|
|
183
|
+
const bits = stat.mode & 0o777;
|
|
184
|
+
if ((bits & 0o077) !== 0) {
|
|
185
|
+
throw new LaunchError(
|
|
186
|
+
`${file} is readable by other users (mode ${bits.toString(8)}). ` +
|
|
187
|
+
`Run: chmod 600 ${shellQuote(file)}`
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
return { path: file };
|
|
191
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Running `claude --bg` for `spawn` and `resume`, with the one failure that
|
|
3
|
+
* is an operator setup step rather than a worker failure called out.
|
|
4
|
+
*
|
|
5
|
+
* Claude Code asks once per directory whether to trust the workspace. Until
|
|
6
|
+
* someone has accepted that prompt in the repo dir, `claude --bg` there exits
|
|
7
|
+
* 1 with (2.1.296) "Workspace not trusted. Run `claude` in <dir> once and
|
|
8
|
+
* accept the trust prompt, then retry." A background session cannot show the
|
|
9
|
+
* prompt, so retrying never helps: that refusal becomes exit 2 (setup) with
|
|
10
|
+
* the exact step, and the manager skill escalates it instead of counting a
|
|
11
|
+
* failed attempt. Every other failure keeps the exit-1 message `mustRun`
|
|
12
|
+
* gives.
|
|
13
|
+
* @module session-manager/worker-launch
|
|
14
|
+
*/
|
|
15
|
+
import { CliError, EXIT, run } from "./cli-runtime.mjs";
|
|
16
|
+
|
|
17
|
+
/** How claude words the refusal; matched loosely across versions. */
|
|
18
|
+
const UNTRUSTED_WORKSPACE = /workspace (?:is )?not trusted/i;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Whether a failed `claude --bg` run was refused for workspace trust.
|
|
22
|
+
* @param {{ stdout: string, stderr: string }} result - Run outcome
|
|
23
|
+
* @returns {boolean} True for the trust refusal
|
|
24
|
+
*/
|
|
25
|
+
export function isUntrustedWorkspace(result) {
|
|
26
|
+
return UNTRUSTED_WORKSPACE.test(`${result.stderr}\n${result.stdout}`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Run a worker spawn or resume spec and return its stdout.
|
|
31
|
+
*
|
|
32
|
+
* Runs the command exactly once: a failure is reported, never retried, so a
|
|
33
|
+
* flaky refusal can never start a second worker.
|
|
34
|
+
* @param {{ command: string, args: string[], cwd: string,
|
|
35
|
+
* env: Record<string, string | undefined> }} spec - From
|
|
36
|
+
* `buildWorkerSpawn` or `buildWorkerResume`
|
|
37
|
+
* @returns {string} stdout
|
|
38
|
+
*/
|
|
39
|
+
export function runWorkerSession(spec) {
|
|
40
|
+
const result = run(spec.command, spec.args, spec);
|
|
41
|
+
if (result.missing) {
|
|
42
|
+
throw new CliError(`${spec.command} is not on PATH.`, EXIT.FAILURE);
|
|
43
|
+
}
|
|
44
|
+
if (result.status === 0) return result.stdout;
|
|
45
|
+
if (isUntrustedWorkspace(result)) {
|
|
46
|
+
throw new CliError(
|
|
47
|
+
`Claude Code's workspace trust has not been accepted in ${spec.cwd}, ` +
|
|
48
|
+
`so claude --bg refused to start a worker there. This is a one-time ` +
|
|
49
|
+
`step per repo: run \`claude\` once in ${spec.cwd}, accept the trust ` +
|
|
50
|
+
`prompt, exit, then retry.`,
|
|
51
|
+
EXIT.SETUP
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
const detail = result.stderr.trim() || `exit ${result.status}`;
|
|
55
|
+
throw new CliError(
|
|
56
|
+
`${spec.command} ${spec.args[0] ?? ""} failed: ${detail}`,
|
|
57
|
+
EXIT.FAILURE
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** A full session UUID; the only id `--resume` continues in place. */
|
|
62
|
+
const FULL_SESSION_ID =
|
|
63
|
+
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The full session id for a ledger item, for `claude --bg --resume`.
|
|
67
|
+
*
|
|
68
|
+
* `claude --bg` prints an 8-character short id, and that is what the ledger
|
|
69
|
+
* records. Given a short id, `--resume` (2.1.296) does not wake the session:
|
|
70
|
+
* it starts a copy of the conversation under a new id and names it after the
|
|
71
|
+
* message, which the manager cannot see. So a short id is mapped to the full
|
|
72
|
+
* `sessionId` of the matching `claude agents --json --all` row, by worker
|
|
73
|
+
* name or by short id.
|
|
74
|
+
* @param {Array<{name: string|null, id: string|null, sessionId: string|null}>}
|
|
75
|
+
* agents - Normalized rows from `listAgents(ctx, { all: true })`
|
|
76
|
+
* @param {{ worker?: string|null, sessionId?: string|null }} item - Ledger item
|
|
77
|
+
* @returns {string | null} Full lowercase session id, or null when no row
|
|
78
|
+
* carries one
|
|
79
|
+
*/
|
|
80
|
+
export function resolveFullSessionId(agents, item) {
|
|
81
|
+
const recorded = String(item?.sessionId ?? "").toLowerCase();
|
|
82
|
+
if (FULL_SESSION_ID.test(recorded)) return recorded;
|
|
83
|
+
const row = agents.find(
|
|
84
|
+
agent =>
|
|
85
|
+
FULL_SESSION_ID.test(agent.sessionId ?? "") &&
|
|
86
|
+
((item?.worker && agent.name === item.worker) ||
|
|
87
|
+
(recorded !== "" &&
|
|
88
|
+
(agent.id === recorded ||
|
|
89
|
+
agent.sessionId.toLowerCase().startsWith(`${recorded}-`))))
|
|
90
|
+
);
|
|
91
|
+
return row ? row.sessionId.toLowerCase() : null;
|
|
92
|
+
}
|