@codyswann/lisa 4.85.0 → 4.86.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/dist/cli/doctor-session-manager-pending.d.ts +17 -0
- package/dist/cli/doctor-session-manager-pending.d.ts.map +1 -0
- package/dist/cli/doctor-session-manager-pending.js +130 -0
- package/dist/cli/doctor-session-manager-pending.js.map +1 -0
- package/dist/cli/doctor-session-manager-token.d.ts +69 -0
- package/dist/cli/doctor-session-manager-token.d.ts.map +1 -0
- package/dist/cli/doctor-session-manager-token.js +264 -0
- package/dist/cli/doctor-session-manager-token.js.map +1 -0
- package/dist/cli/doctor-session-manager.d.ts +3 -1
- package/dist/cli/doctor-session-manager.d.ts.map +1 -1
- package/dist/cli/doctor-session-manager.js +168 -146
- package/dist/cli/doctor-session-manager.js.map +1 -1
- 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 +158 -20
- package/dist/core/upstream-evidence-manifest.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-secrets-access/SKILL.md +5 -3
- package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/providers.mjs +121 -3
- package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/validate-config.mjs +47 -26
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/SKILL.md +197 -54
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/references/runbook-template.md +23 -10
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-cycle.mjs +21 -3
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-human.mjs +199 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-manager-token.mjs +99 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-manager.mjs +52 -52
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-queue.mjs +51 -139
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-runtime.mjs +5 -3
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-setup.mjs +94 -14
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-status.mjs +73 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-usage.mjs +105 -15
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/config.mjs +39 -11
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/human-answer.mjs +162 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/human-needed.mjs +198 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/json-input.mjs +34 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/launch-shell.mjs +115 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/launch.mjs +69 -60
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/ledger-questions.mjs +252 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/ledger.mjs +22 -56
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/queue-github.mjs +117 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/session-manager.mjs +17 -5
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/token-migrate.mjs +180 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/token-provider.mjs +150 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/token-read.mjs +166 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/token-store.mjs +248 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/token.mjs +39 -120
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-session-manager/SKILL.md +82 -19
- package/plugins/lisa/commands/session-manager.md +1 -1
- package/plugins/lisa/commands/setup/session-manager.md +1 -1
- package/plugins/lisa/rules/reference/config-resolution.md +39 -7
- package/plugins/lisa/skills/lisa-secrets-access/SKILL.md +5 -3
- package/plugins/lisa/skills/lisa-secrets-access/scripts/providers.mjs +121 -3
- package/plugins/lisa/skills/lisa-secrets-access/scripts/validate-config.mjs +47 -26
- package/plugins/lisa/skills/lisa-session-manager/SKILL.md +197 -54
- package/plugins/lisa/skills/lisa-session-manager/references/runbook-template.md +23 -10
- package/plugins/lisa/skills/lisa-session-manager/scripts/cli-cycle.mjs +21 -3
- package/plugins/lisa/skills/lisa-session-manager/scripts/cli-human.mjs +199 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/cli-manager-token.mjs +99 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/cli-manager.mjs +52 -52
- package/plugins/lisa/skills/lisa-session-manager/scripts/cli-queue.mjs +51 -139
- package/plugins/lisa/skills/lisa-session-manager/scripts/cli-runtime.mjs +5 -3
- package/plugins/lisa/skills/lisa-session-manager/scripts/cli-setup.mjs +94 -14
- package/plugins/lisa/skills/lisa-session-manager/scripts/cli-status.mjs +73 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/cli-usage.mjs +105 -15
- package/plugins/lisa/skills/lisa-session-manager/scripts/config.mjs +39 -11
- package/plugins/lisa/skills/lisa-session-manager/scripts/human-answer.mjs +162 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/human-needed.mjs +198 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/json-input.mjs +34 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/launch-shell.mjs +115 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/launch.mjs +69 -60
- package/plugins/lisa/skills/lisa-session-manager/scripts/ledger-questions.mjs +252 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/ledger.mjs +22 -56
- package/plugins/lisa/skills/lisa-session-manager/scripts/queue-github.mjs +117 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/session-manager.mjs +17 -5
- package/plugins/lisa/skills/lisa-session-manager/scripts/token-migrate.mjs +180 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/token-provider.mjs +150 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/token-read.mjs +166 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/token-store.mjs +248 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/token.mjs +39 -120
- package/plugins/lisa/skills/lisa-setup-session-manager/SKILL.md +82 -19
- package/plugins/lisa-agy/commands/lisa/session-manager.md +1 -1
- package/plugins/lisa-agy/commands/lisa/setup/session-manager.md +1 -1
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/skills/lisa-secrets-access/SKILL.md +5 -3
- package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/providers.mjs +121 -3
- package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/validate-config.mjs +47 -26
- package/plugins/lisa-agy/skills/lisa-session-manager/SKILL.md +197 -54
- package/plugins/lisa-agy/skills/lisa-session-manager/references/runbook-template.md +23 -10
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-cycle.mjs +21 -3
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-human.mjs +199 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-manager-token.mjs +99 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-manager.mjs +52 -52
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-queue.mjs +51 -139
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-runtime.mjs +5 -3
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-setup.mjs +94 -14
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-status.mjs +73 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-usage.mjs +105 -15
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/config.mjs +39 -11
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/human-answer.mjs +162 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/human-needed.mjs +198 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/json-input.mjs +34 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/launch-shell.mjs +115 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/launch.mjs +69 -60
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/ledger-questions.mjs +252 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/ledger.mjs +22 -56
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/queue-github.mjs +117 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/session-manager.mjs +17 -5
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/token-migrate.mjs +180 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/token-provider.mjs +150 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/token-read.mjs +166 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/token-store.mjs +248 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/token.mjs +39 -120
- package/plugins/lisa-agy/skills/lisa-setup-session-manager/SKILL.md +82 -19
- 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/session-manager.md +1 -1
- package/plugins/lisa-copilot/commands/lisa/setup/session-manager.md +1 -1
- package/plugins/lisa-copilot/rules/reference/config-resolution.md +39 -7
- package/plugins/lisa-copilot/skills/lisa-secrets-access/SKILL.md +5 -3
- package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/providers.mjs +121 -3
- package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/validate-config.mjs +47 -26
- package/plugins/lisa-copilot/skills/lisa-session-manager/SKILL.md +197 -54
- package/plugins/lisa-copilot/skills/lisa-session-manager/references/runbook-template.md +23 -10
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-cycle.mjs +21 -3
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-human.mjs +199 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-manager-token.mjs +99 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-manager.mjs +52 -52
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-queue.mjs +51 -139
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-runtime.mjs +5 -3
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-setup.mjs +94 -14
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-status.mjs +73 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-usage.mjs +105 -15
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/config.mjs +39 -11
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/human-answer.mjs +162 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/human-needed.mjs +198 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/json-input.mjs +34 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/launch-shell.mjs +115 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/launch.mjs +69 -60
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/ledger-questions.mjs +252 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/ledger.mjs +22 -56
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/queue-github.mjs +117 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/session-manager.mjs +17 -5
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/token-migrate.mjs +180 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/token-provider.mjs +150 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/token-read.mjs +166 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/token-store.mjs +248 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/token.mjs +39 -120
- package/plugins/lisa-copilot/skills/lisa-setup-session-manager/SKILL.md +82 -19
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/commands/lisa/session-manager.md +1 -1
- package/plugins/lisa-cursor/commands/lisa/setup/session-manager.md +1 -1
- package/plugins/lisa-cursor/rules/config-resolution-reference.mdc +39 -7
- package/plugins/lisa-cursor/skills/lisa-secrets-access/SKILL.md +5 -3
- package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/providers.mjs +121 -3
- package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/validate-config.mjs +47 -26
- package/plugins/lisa-cursor/skills/lisa-session-manager/SKILL.md +197 -54
- package/plugins/lisa-cursor/skills/lisa-session-manager/references/runbook-template.md +23 -10
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-cycle.mjs +21 -3
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-human.mjs +199 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-manager-token.mjs +99 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-manager.mjs +52 -52
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-queue.mjs +51 -139
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-runtime.mjs +5 -3
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-setup.mjs +94 -14
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-status.mjs +73 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-usage.mjs +105 -15
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/config.mjs +39 -11
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/human-answer.mjs +162 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/human-needed.mjs +198 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/json-input.mjs +34 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/launch-shell.mjs +115 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/launch.mjs +69 -60
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/ledger-questions.mjs +252 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/ledger.mjs +22 -56
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/queue-github.mjs +117 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/session-manager.mjs +17 -5
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/token-migrate.mjs +180 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/token-provider.mjs +150 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/token-read.mjs +166 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/token-store.mjs +248 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/token.mjs +39 -120
- package/plugins/lisa-cursor/skills/lisa-setup-session-manager/SKILL.md +82 -19
- 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/config-resolution.md +39 -7
- package/plugins/lisa-devin/skills/lisa-secrets-access/SKILL.md +5 -3
- package/plugins/lisa-devin/skills/lisa-secrets-access/scripts/providers.mjs +121 -3
- package/plugins/lisa-devin/skills/lisa-secrets-access/scripts/validate-config.mjs +47 -26
- package/plugins/lisa-devin/skills/lisa-session-manager/SKILL.md +197 -54
- package/plugins/lisa-devin/skills/lisa-session-manager/references/runbook-template.md +23 -10
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-cycle.mjs +21 -3
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-human.mjs +199 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-manager-token.mjs +99 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-manager.mjs +52 -52
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-queue.mjs +51 -139
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-runtime.mjs +5 -3
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-setup.mjs +94 -14
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-status.mjs +73 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-usage.mjs +105 -15
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/config.mjs +39 -11
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/human-answer.mjs +162 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/human-needed.mjs +198 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/json-input.mjs +34 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/launch-shell.mjs +115 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/launch.mjs +69 -60
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/ledger-questions.mjs +252 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/ledger.mjs +22 -56
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/queue-github.mjs +117 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/session-manager.mjs +17 -5
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/token-migrate.mjs +180 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/token-provider.mjs +150 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/token-read.mjs +166 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/token-store.mjs +248 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/token.mjs +39 -120
- package/plugins/lisa-devin/skills/lisa-setup-session-manager/SKILL.md +82 -19
- 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/session-manager.md +1 -1
- package/plugins/src/base/commands/setup/session-manager.md +1 -1
- package/plugins/src/base/rules/reference/config-resolution.md +39 -7
- package/plugins/src/base/skills/lisa-secrets-access/SKILL.md +5 -3
- package/plugins/src/base/skills/lisa-secrets-access/scripts/providers.mjs +121 -3
- package/plugins/src/base/skills/lisa-secrets-access/scripts/validate-config.mjs +47 -26
- package/plugins/src/base/skills/lisa-session-manager/SKILL.md +197 -54
- package/plugins/src/base/skills/lisa-session-manager/references/runbook-template.md +23 -10
- package/plugins/src/base/skills/lisa-session-manager/scripts/cli-cycle.mjs +21 -3
- package/plugins/src/base/skills/lisa-session-manager/scripts/cli-human.mjs +199 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/cli-manager-token.mjs +99 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/cli-manager.mjs +52 -52
- package/plugins/src/base/skills/lisa-session-manager/scripts/cli-queue.mjs +51 -139
- package/plugins/src/base/skills/lisa-session-manager/scripts/cli-runtime.mjs +5 -3
- package/plugins/src/base/skills/lisa-session-manager/scripts/cli-setup.mjs +94 -14
- package/plugins/src/base/skills/lisa-session-manager/scripts/cli-status.mjs +73 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/cli-usage.mjs +105 -15
- package/plugins/src/base/skills/lisa-session-manager/scripts/config.mjs +39 -11
- package/plugins/src/base/skills/lisa-session-manager/scripts/human-answer.mjs +162 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/human-needed.mjs +198 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/json-input.mjs +34 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/launch-shell.mjs +115 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/launch.mjs +69 -60
- package/plugins/src/base/skills/lisa-session-manager/scripts/ledger-questions.mjs +252 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/ledger.mjs +22 -56
- package/plugins/src/base/skills/lisa-session-manager/scripts/queue-github.mjs +117 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/session-manager.mjs +17 -5
- package/plugins/src/base/skills/lisa-session-manager/scripts/token-migrate.mjs +180 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/token-provider.mjs +150 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/token-read.mjs +166 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/token-store.mjs +248 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/token.mjs +39 -120
- package/plugins/src/base/skills/lisa-setup-session-manager/SKILL.md +82 -19
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The GitHub fetches behind the session-manager `queue` subcommand, split out
|
|
3
|
+
* of cli-queue.mjs to keep it under the line budget (#4555): the Ready tier
|
|
4
|
+
* ({@link fetchGithubQueue}) and the backlog tier ({@link fetchGithubBacklog}).
|
|
5
|
+
* Both read with `gh api --paginate --slurp`, so every page is read or the
|
|
6
|
+
* command fails, and both run gh with the scrubbed worker env.
|
|
7
|
+
* @module session-manager/queue-github
|
|
8
|
+
*/
|
|
9
|
+
import { normalizeGithubIssue } from "../../../scripts/ready-queue-order.mjs";
|
|
10
|
+
import { resolveRole } from "../../../scripts/resolve-lifecycle-role.mjs";
|
|
11
|
+
import { CliError, mustRun } from "./cli-runtime.mjs";
|
|
12
|
+
import { parseJson } from "./json-input.mjs";
|
|
13
|
+
import { scrubWorkerEnv } from "./workers.mjs";
|
|
14
|
+
|
|
15
|
+
const OWNER_REPO = /^[\w.-]+\/[\w.-]+$/;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The GitHub repo to read: `--github-repo`, else github.org/github.repo.
|
|
19
|
+
* @param {object} ctx - CLI context
|
|
20
|
+
* @param {{global?: object, local?: object}} project - Loaded project
|
|
21
|
+
* @returns {string} `owner/repo`
|
|
22
|
+
*/
|
|
23
|
+
function githubRepo(ctx, project) {
|
|
24
|
+
const merged = { ...project.global?.github, ...project.local?.github };
|
|
25
|
+
const repo =
|
|
26
|
+
ctx.values["github-repo"] ??
|
|
27
|
+
(merged.org && merged.repo ? `${merged.org}/${merged.repo}` : "");
|
|
28
|
+
if (!OWNER_REPO.test(repo)) {
|
|
29
|
+
throw new CliError(
|
|
30
|
+
"No GitHub repo: set github.org and github.repo, or pass --github-repo <owner/repo>.",
|
|
31
|
+
2
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
return repo;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Every string leaf of an object (label names, including env-keyed `done`).
|
|
39
|
+
* @param {unknown} value - Config subtree
|
|
40
|
+
* @returns {string[]} Leaves
|
|
41
|
+
*/
|
|
42
|
+
function stringLeaves(value) {
|
|
43
|
+
if (typeof value === "string") return value ? [value] : [];
|
|
44
|
+
if (!value || typeof value !== "object") return [];
|
|
45
|
+
return Object.values(value).flatMap(stringLeaves);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Fetch every open GitHub issue carrying no lifecycle label (build or PRD),
|
|
50
|
+
* all pages: the GitHub backlog tier.
|
|
51
|
+
* @param {object} ctx - CLI context
|
|
52
|
+
* @param {{global?: object, local?: object}} project - Loaded project
|
|
53
|
+
* @returns {{ items: object[], pages: number }} Normalized items
|
|
54
|
+
*/
|
|
55
|
+
export function fetchGithubBacklog(ctx, project) {
|
|
56
|
+
const repo = githubRepo(ctx, project);
|
|
57
|
+
const lifecycle = new Set(
|
|
58
|
+
[project.global?.github?.labels, project.local?.github?.labels]
|
|
59
|
+
.flatMap(labels => [
|
|
60
|
+
...stringLeaves(labels?.build),
|
|
61
|
+
...stringLeaves(labels?.prd),
|
|
62
|
+
])
|
|
63
|
+
.map(label => label.toLowerCase())
|
|
64
|
+
);
|
|
65
|
+
const stdout = mustRun(
|
|
66
|
+
"gh",
|
|
67
|
+
[
|
|
68
|
+
"api",
|
|
69
|
+
"--paginate",
|
|
70
|
+
"--slurp",
|
|
71
|
+
`repos/${repo}/issues?state=open&per_page=100`,
|
|
72
|
+
],
|
|
73
|
+
{ env: scrubWorkerEnv(ctx.env) }
|
|
74
|
+
);
|
|
75
|
+
const pages = parseJson(stdout, "gh api output");
|
|
76
|
+
const rows = Array.isArray(pages) ? pages.flat() : [];
|
|
77
|
+
return {
|
|
78
|
+
items: rows
|
|
79
|
+
.map(row => normalizeGithubIssue(row, { repo }))
|
|
80
|
+
.filter(Boolean)
|
|
81
|
+
.filter(
|
|
82
|
+
item => !item.labels.some(label => lifecycle.has(label.toLowerCase()))
|
|
83
|
+
),
|
|
84
|
+
pages: Array.isArray(pages) ? pages.length : 0,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Fetch every open GitHub issue carrying the ready label, all pages.
|
|
90
|
+
* @param {object} ctx - CLI context
|
|
91
|
+
* @param {{global?: object, local?: object}} project - Loaded project
|
|
92
|
+
* @returns {{ items: object[], pages: number }} Normalized items
|
|
93
|
+
*/
|
|
94
|
+
export function fetchGithubQueue(ctx, project) {
|
|
95
|
+
const repo = githubRepo(ctx, project);
|
|
96
|
+
const role = resolveRole({
|
|
97
|
+
role: "ready",
|
|
98
|
+
vendor: "github",
|
|
99
|
+
intent: "read",
|
|
100
|
+
local: project.local,
|
|
101
|
+
global: project.global,
|
|
102
|
+
});
|
|
103
|
+
const label = ctx.values.label ?? role.value;
|
|
104
|
+
if (!label) throw new CliError(role.message, 2);
|
|
105
|
+
const endpoint =
|
|
106
|
+
`repos/${repo}/issues?state=open&labels=${encodeURIComponent(label)}` +
|
|
107
|
+
`&per_page=100`;
|
|
108
|
+
const stdout = mustRun("gh", ["api", "--paginate", "--slurp", endpoint], {
|
|
109
|
+
env: scrubWorkerEnv(ctx.env),
|
|
110
|
+
});
|
|
111
|
+
const pages = parseJson(stdout, "gh api output");
|
|
112
|
+
const rows = Array.isArray(pages) ? pages.flat() : [];
|
|
113
|
+
return {
|
|
114
|
+
items: rows.map(row => normalizeGithubIssue(row, { repo })).filter(Boolean),
|
|
115
|
+
pages: Array.isArray(pages) ? pages.length : 0,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
@@ -18,7 +18,11 @@
|
|
|
18
18
|
* background sessions (`claude --bg`, `claude agents`), cross-session
|
|
19
19
|
* messaging and channels, which no other supported agent has. Codex, Cursor,
|
|
20
20
|
* OpenCode, Antigravity, Copilot and Devin can run the setup-time subcommands
|
|
21
|
-
* (init-config, store-token
|
|
21
|
+
* (init-config, store-token into the configured secrets provider,
|
|
22
|
+
* trust-status, coexistence, validate, start), `status` (pending questions
|
|
23
|
+
* and their answerCommand) and `answer` (an operator answering a pending
|
|
24
|
+
* question at the terminal), from the Claude Code plugin install or the npm
|
|
25
|
+
* package only (never the Codex overlay copy), and
|
|
22
26
|
* `start` hosts a Claude Code manager in tmux whatever agent ran it. Note that
|
|
23
27
|
* trust-status reads Claude Code's own trust record, which is the one that
|
|
24
28
|
* matters: workers are always Claude Code. They cannot run a manager cycle
|
|
@@ -41,6 +45,7 @@ import {
|
|
|
41
45
|
recordCycle,
|
|
42
46
|
route,
|
|
43
47
|
} from "./cli-cycle.mjs";
|
|
48
|
+
import { answer, itemAnswer, markPlan } from "./cli-human.mjs";
|
|
44
49
|
import { repoDir, start, status, stop, validate } from "./cli-manager.mjs";
|
|
45
50
|
import { queue } from "./cli-queue.mjs";
|
|
46
51
|
import { CliError, EXIT } from "./cli-runtime.mjs";
|
|
@@ -68,7 +73,7 @@ const COMMANDS = {
|
|
|
68
73
|
stop: { run: stop, options: {}, positionals: 0 },
|
|
69
74
|
"store-token": {
|
|
70
75
|
run: storeToken,
|
|
71
|
-
options: { replace: flag, "from-clipboard": flag },
|
|
76
|
+
options: { replace: flag, "from-clipboard": flag, "vault-project": text },
|
|
72
77
|
positionals: 0,
|
|
73
78
|
},
|
|
74
79
|
"trust-status": { run: trustStatus, options: {}, positionals: 0 },
|
|
@@ -81,7 +86,7 @@ const COMMANDS = {
|
|
|
81
86
|
validate: { run: validate, options: {}, positionals: 0 },
|
|
82
87
|
"init-config": {
|
|
83
88
|
run: initConfig,
|
|
84
|
-
options: { slug: text, write: flag },
|
|
89
|
+
options: { slug: text, write: flag, notify: text },
|
|
85
90
|
positionals: 0,
|
|
86
91
|
},
|
|
87
92
|
queue: {
|
|
@@ -106,6 +111,9 @@ const COMMANDS = {
|
|
|
106
111
|
options: { input: text, ref: text },
|
|
107
112
|
positionals: 0,
|
|
108
113
|
},
|
|
114
|
+
"mark-plan": { run: markPlan, options: { kind: text }, positionals: 1 },
|
|
115
|
+
answer: { run: answer, options: {}, positionals: 1 },
|
|
116
|
+
"item-answer": { run: itemAnswer, options: {}, positionals: 1 },
|
|
109
117
|
"record-cycle": {
|
|
110
118
|
run: recordCycle,
|
|
111
119
|
options: {
|
|
@@ -125,10 +133,14 @@ const LEDGER_SPECS = {
|
|
|
125
133
|
positionals: 1,
|
|
126
134
|
},
|
|
127
135
|
"set-question": {
|
|
128
|
-
options: { "telegram-message-id": text, sender: text },
|
|
136
|
+
options: { "telegram-message-id": text, sender: text, kind: text },
|
|
129
137
|
positionals: 1,
|
|
130
138
|
},
|
|
131
|
-
|
|
139
|
+
marked: {
|
|
140
|
+
options: { "marker-type": text, marker: text, "comment-id": text },
|
|
141
|
+
positionals: 1,
|
|
142
|
+
},
|
|
143
|
+
"clear-question": { options: { "reset-attempts": flag }, positionals: 1 },
|
|
132
144
|
finish: { options: { status: text, sender: text }, positionals: 1 },
|
|
133
145
|
"validation-failed": { options: { "updated-at": text }, positionals: 1 },
|
|
134
146
|
"validation-cleared": { options: {}, positionals: 1 },
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migrating the old plaintext Telegram bot token file into the secrets
|
|
3
|
+
* provider (#4555): reading `<notify.stateDir>/.env`, deciding whether it
|
|
4
|
+
* may be deleted, and the refusals `store-token` and `start` share.
|
|
5
|
+
*
|
|
6
|
+
* Split from token-store.mjs, which keeps the provider calls. The two sides
|
|
7
|
+
* of the migration must agree on what counts as a leftover token, or an
|
|
8
|
+
* operator is sent round a loop: `start` refuses only a file that really
|
|
9
|
+
* holds a `TELEGRAM_BOT_TOKEN` line ({@link holdsToken}), and `store-token`
|
|
10
|
+
* deletes a file with no token and nothing else in it (empty, or comments
|
|
11
|
+
* only) once the provider holds a verified secret ({@link removable}).
|
|
12
|
+
*
|
|
13
|
+
* Values are never kept beyond the comparison, returned in a message or
|
|
14
|
+
* logged; a file's other keys are reported by name only.
|
|
15
|
+
* @module session-manager/token-migrate
|
|
16
|
+
*/
|
|
17
|
+
import { createHash } from "node:crypto";
|
|
18
|
+
import path from "node:path";
|
|
19
|
+
|
|
20
|
+
import { validateBotToken } from "./token.mjs";
|
|
21
|
+
|
|
22
|
+
/** The plugin's plaintext token file in the state dir (the old storage). */
|
|
23
|
+
export const TOKEN_FILE = ".env";
|
|
24
|
+
|
|
25
|
+
/** The variable the Telegram plugin reads its token from. */
|
|
26
|
+
export const TOKEN_VAR = "TELEGRAM_BOT_TOKEN";
|
|
27
|
+
|
|
28
|
+
/** One `KEY=value` line of a dotenv file, optionally `export`ed. */
|
|
29
|
+
const ENV_LINE = /^(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)=(.*)$/;
|
|
30
|
+
|
|
31
|
+
/** A refusal or failure carrying the exit code the CLI should use. */
|
|
32
|
+
export class TokenStoreError extends Error {
|
|
33
|
+
/**
|
|
34
|
+
* @param {string} message - Operator-readable reason; never a value
|
|
35
|
+
* @param {number} exitCode - 1 (failure, retry) or 2 (setup)
|
|
36
|
+
*/
|
|
37
|
+
constructor(message, exitCode) {
|
|
38
|
+
super(message);
|
|
39
|
+
this.name = "TokenStoreError";
|
|
40
|
+
this.exitCode = exitCode;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Exit 2: something the operator has to change.
|
|
46
|
+
* @param {string} message - Reason
|
|
47
|
+
* @returns {TokenStoreError} Error
|
|
48
|
+
*/
|
|
49
|
+
export const setupError = message => new TokenStoreError(message, 2);
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* sha256 of a value, for comparing a written secret with its read-back.
|
|
53
|
+
* @param {string} value - Value
|
|
54
|
+
* @returns {string} Hex digest
|
|
55
|
+
*/
|
|
56
|
+
export function digest(value) {
|
|
57
|
+
return createHash("sha256").update(String(value)).digest("hex");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Read the plaintext token file, if there is one: its token, and the names
|
|
62
|
+
* of anything else in it (values are never kept or reported).
|
|
63
|
+
* @param {string} stateDir - Resolved state dir
|
|
64
|
+
* @param {Pick<import("node:fs"), "existsSync" | "readFileSync">} fs - Seam
|
|
65
|
+
* @returns {{ path: string, token: string | undefined, others: string[] } |
|
|
66
|
+
* null} The file, or null when absent
|
|
67
|
+
*/
|
|
68
|
+
export function readEnvFile(stateDir, fs) {
|
|
69
|
+
const file = path.join(stateDir, TOKEN_FILE);
|
|
70
|
+
if (!fs.existsSync(file)) return null;
|
|
71
|
+
const entries = fs
|
|
72
|
+
.readFileSync(file, "utf8")
|
|
73
|
+
.split(/\r?\n/)
|
|
74
|
+
.map((line, at) => ({ line: line.trim(), at }))
|
|
75
|
+
.filter(({ line }) => line !== "" && !line.startsWith("#"))
|
|
76
|
+
.map(({ line, at }) => ({ match: ENV_LINE.exec(line), at }));
|
|
77
|
+
const token = entries.find(({ match }) => match?.[1] === TOKEN_VAR)?.match;
|
|
78
|
+
return {
|
|
79
|
+
path: file,
|
|
80
|
+
token: token ? token[2].replace(/^(['"])(.*)\1$/, "$2") : undefined,
|
|
81
|
+
others: entries
|
|
82
|
+
.filter(({ match }) => match?.[1] !== TOKEN_VAR)
|
|
83
|
+
.map(({ match, at }) => match?.[1] ?? `an unrecognised line ${at + 1}`),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Whether a token file really holds a token: what `start` refuses.
|
|
89
|
+
* @param {ReturnType<typeof readEnvFile>} file - The file, if any
|
|
90
|
+
* @returns {boolean} True for a `TELEGRAM_BOT_TOKEN` line
|
|
91
|
+
*/
|
|
92
|
+
export function holdsToken(file) {
|
|
93
|
+
return file !== null && file.token !== undefined;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Whether a file holds nothing at all (empty, blank or comments only), so
|
|
98
|
+
* `store-token` may delete it once the provider holds a verified secret.
|
|
99
|
+
* @param {ReturnType<typeof readEnvFile>} file - The file, if any
|
|
100
|
+
* @returns {boolean} True for a removable empty file
|
|
101
|
+
*/
|
|
102
|
+
export function removable(file) {
|
|
103
|
+
return file !== null && file.token === undefined && file.others.length === 0;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Refuse a plaintext file that cannot be migrated as it is.
|
|
108
|
+
* @param {ReturnType<typeof readEnvFile>} file - The file, if any
|
|
109
|
+
*/
|
|
110
|
+
export function assertMigratable(file) {
|
|
111
|
+
if (file === null) return;
|
|
112
|
+
if (file.others.length > 0) {
|
|
113
|
+
throw setupError(
|
|
114
|
+
`${file.path} holds ${file.others.join(", ")} besides ${TOKEN_VAR}, so ` +
|
|
115
|
+
`it cannot be deleted after the token moves. Move the rest out of it, ` +
|
|
116
|
+
`then run store-token again. Nothing was changed.`
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
if (file.token !== undefined && !validateBotToken(file.token)) {
|
|
120
|
+
throw setupError(
|
|
121
|
+
`${file.path} holds a ${TOKEN_VAR} that does not look like a Telegram ` +
|
|
122
|
+
`bot token. Fix or delete the file, then run store-token again. ` +
|
|
123
|
+
`Nothing was changed.`
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Delete the plaintext file after the provider's copy is verified, and say
|
|
130
|
+
* so: `migratedFrom` when its token moved, `deleted` whenever it went.
|
|
131
|
+
* @param {ReturnType<typeof readEnvFile>} file - The file, if any
|
|
132
|
+
* @param {Pick<import("node:fs"), "rmSync">} fs - Seam
|
|
133
|
+
* @returns {{ migratedFrom?: string, deleted?: true }} What was removed
|
|
134
|
+
*/
|
|
135
|
+
export function removeAfterVerify(file, fs) {
|
|
136
|
+
if (holdsToken(file)) {
|
|
137
|
+
fs.rmSync(file.path);
|
|
138
|
+
return { migratedFrom: file.path, deleted: true };
|
|
139
|
+
}
|
|
140
|
+
if (removable(file)) {
|
|
141
|
+
fs.rmSync(file.path);
|
|
142
|
+
return { deleted: true };
|
|
143
|
+
}
|
|
144
|
+
return {};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* The result for a secret the provider already holds (no write): a matching
|
|
149
|
+
* or empty plaintext file is deleted; one holding a different token is kept
|
|
150
|
+
* and refused.
|
|
151
|
+
* @param {{ value: string }} existing - The stored secret
|
|
152
|
+
* @param {ReturnType<typeof readEnvFile>} file - The plaintext file, if any
|
|
153
|
+
* @param {{ cfg: object, name: string, fs: object }} input - Context
|
|
154
|
+
* @returns {object} Outcome
|
|
155
|
+
*/
|
|
156
|
+
export function keepExisting(existing, file, { cfg, name, fs }) {
|
|
157
|
+
const result = {
|
|
158
|
+
stored: true,
|
|
159
|
+
storage: "provider",
|
|
160
|
+
provider: cfg.provider,
|
|
161
|
+
secret: name,
|
|
162
|
+
verified: true,
|
|
163
|
+
alreadyStored: true,
|
|
164
|
+
};
|
|
165
|
+
if (!validateBotToken(existing.value)) {
|
|
166
|
+
throw setupError(
|
|
167
|
+
`${name} in ${cfg.provider} does not look like a Telegram bot token. ` +
|
|
168
|
+
`Run store-token --replace to store the right one.`
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
if (holdsToken(file) && digest(file.token) !== digest(existing.value)) {
|
|
172
|
+
throw setupError(
|
|
173
|
+
`${file.path} holds a different token from the one ${cfg.provider} ` +
|
|
174
|
+
`already stores as ${name}. Nothing was changed. To make the file's ` +
|
|
175
|
+
`token the stored one, run store-token --replace; to keep the stored ` +
|
|
176
|
+
`one, delete ${file.path}.`
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
return { ...result, ...removeAfterVerify(file, fs) };
|
|
180
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Talking to the secrets provider for the Telegram bot token (#4555): the
|
|
3
|
+
* adapter over `lisa-secrets-access` (`surfaces.mjs` for the config,
|
|
4
|
+
* `providers.mjs` for reads and writes), whether a provider is configured at
|
|
5
|
+
* all, and turning a provider error into fixed operator text.
|
|
6
|
+
*
|
|
7
|
+
* Split from token-store.mjs, which keeps the store and resolve flows. The
|
|
8
|
+
* adapter is injectable so tests run against a fake vault. A provider's raw
|
|
9
|
+
* error is matched, never repeated: a failed write's error can quote its
|
|
10
|
+
* argv, which holds the value.
|
|
11
|
+
* @module session-manager/token-provider
|
|
12
|
+
*/
|
|
13
|
+
import { TokenStoreError, setupError } from "./token-migrate.mjs";
|
|
14
|
+
|
|
15
|
+
/** A provider error that names a rate limit (HTTP 429). */
|
|
16
|
+
const RATE_LIMITED = /\b429\b|too many requests|rate.?limit/i;
|
|
17
|
+
|
|
18
|
+
/** The bootstrap prefix whose CLI each provider runs, for the preflight. */
|
|
19
|
+
const CLI_PREFIX = { bitwarden: "BWS_ACCESS_TOKEN", doppler: "DOPPLER_TOKEN" };
|
|
20
|
+
|
|
21
|
+
/** Operator-facing reasons, by {@link classifyProviderFailure} kind. */
|
|
22
|
+
const REASONS = {
|
|
23
|
+
"rate-limited": "it rate-limited the request (429 Too Many Requests)",
|
|
24
|
+
"timed-out": "it did not answer in time",
|
|
25
|
+
unavailable: "it could not be reached or refused the request",
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* What went wrong talking to the provider, read from the error's code and
|
|
30
|
+
* text only (the text is matched, never repeated).
|
|
31
|
+
* @param {unknown} error - Caught value
|
|
32
|
+
* @returns {"setup" | "missing-cli" | "timed-out" | "rate-limited" |
|
|
33
|
+
* "unsupported" | "duplicate" | "unavailable"} Kind
|
|
34
|
+
*/
|
|
35
|
+
export function classifyProviderFailure(error) {
|
|
36
|
+
const text = `${error?.message ?? ""} ${error?.stderr ?? ""}`;
|
|
37
|
+
if (error?.kind === "setup") return "setup";
|
|
38
|
+
if (error?.code === "ENOENT") return "missing-cli";
|
|
39
|
+
if (error?.code === "ETIMEDOUT" || /at its own deadline/.test(text)) {
|
|
40
|
+
return "timed-out";
|
|
41
|
+
}
|
|
42
|
+
if (RATE_LIMITED.test(text)) return "rate-limited";
|
|
43
|
+
if (/has no (bulk read|create|write) implemented/.test(text)) {
|
|
44
|
+
return "unsupported";
|
|
45
|
+
}
|
|
46
|
+
if (/duplicate secret key/.test(text)) return "duplicate";
|
|
47
|
+
return "unavailable";
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Turn a provider error into a {@link TokenStoreError} with fixed text.
|
|
52
|
+
* @param {unknown} error - Caught value
|
|
53
|
+
* @param {{ provider: string, name: string, retry: string }} where - The
|
|
54
|
+
* provider, the secret, and the retry advice for a transient failure
|
|
55
|
+
* @returns {TokenStoreError} Error
|
|
56
|
+
*/
|
|
57
|
+
export function providerError(error, { provider, name, retry }) {
|
|
58
|
+
const kind = classifyProviderFailure(error);
|
|
59
|
+
if (kind === "setup") return setupError(String(error.message));
|
|
60
|
+
if (kind === "missing-cli") {
|
|
61
|
+
return setupError(
|
|
62
|
+
`The ${provider} command-line tool is not on PATH. Install it with ` +
|
|
63
|
+
`/lisa:setup:workstation, then try again.`
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
if (kind === "unsupported") {
|
|
67
|
+
return setupError(
|
|
68
|
+
`Lisa cannot read or write ${name} in provider "${provider}" yet. ` +
|
|
69
|
+
`Create ${name} in ${provider} yourself, then run store-token to ` +
|
|
70
|
+
`verify it and remove any plaintext copy.`
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
if (kind === "duplicate") {
|
|
74
|
+
return setupError(
|
|
75
|
+
`${provider} shows more than one secret with the same name, so Lisa ` +
|
|
76
|
+
`will not guess which is ${name}. Remove the duplicate in ${provider}.`
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
return new TokenStoreError(`${retry.replace("<reason>", REASONS[kind])}`, 1);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* The lazily-loaded secrets adapter over `lisa-secrets-access`, by path
|
|
84
|
+
* relative to this file (the way token.mjs loads prompt-secret.mjs).
|
|
85
|
+
* @returns {Promise<{ readConfig: Function, preflight: Function,
|
|
86
|
+
* list: Function, create: Function, replace: Function }>} Adapter
|
|
87
|
+
*/
|
|
88
|
+
export async function defaultSecretsAdapter() {
|
|
89
|
+
const base = new URL("../../lisa-secrets-access/scripts/", import.meta.url);
|
|
90
|
+
const [surfaces, providers] = await Promise.all([
|
|
91
|
+
import(new URL("surfaces.mjs", base).href),
|
|
92
|
+
import(new URL("providers.mjs", base).href),
|
|
93
|
+
]);
|
|
94
|
+
return {
|
|
95
|
+
readConfig: (root, env) => surfaces.readConfig(root, env),
|
|
96
|
+
preflight: cfg => {
|
|
97
|
+
const prefix = CLI_PREFIX[cfg.provider];
|
|
98
|
+
if (prefix && providers.providerCliPresent(prefix) === false) {
|
|
99
|
+
throw Object.assign(new Error("missing"), { code: "ENOENT" });
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
providers.bootstrapToken(cfg.bootstrap);
|
|
103
|
+
} catch (error) {
|
|
104
|
+
throw Object.assign(new Error(error.message), { kind: "setup" });
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
list: cfg =>
|
|
108
|
+
[...providers.normalizeRows(providers.fetchRaw(cfg), cfg.narrow)].map(
|
|
109
|
+
([key, row]) => ({ key, ...row })
|
|
110
|
+
),
|
|
111
|
+
create: (cfg, secret) => providers.createSecret(cfg, secret),
|
|
112
|
+
replace: (cfg, { id, value }) => providers.writeSecret(cfg, id, value),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* The project's secrets config and adapter, or `null` when no provider is
|
|
118
|
+
* configured (no `secrets` block, or `provider: "env"`): then the token stays
|
|
119
|
+
* in the state dir's `.env` file.
|
|
120
|
+
* @param {string} root - Project root
|
|
121
|
+
* @param {Record<string, string | undefined>} env - Environment
|
|
122
|
+
* @param {object} [adapter] - Injected adapter (tests)
|
|
123
|
+
* @returns {Promise<{ cfg: object, adapter: object } | null>} Provider mode
|
|
124
|
+
*/
|
|
125
|
+
export async function loadTokenSecrets(root, env, adapter) {
|
|
126
|
+
const secrets = adapter ?? (await defaultSecretsAdapter());
|
|
127
|
+
let cfg;
|
|
128
|
+
try {
|
|
129
|
+
cfg = secrets.readConfig(root, env);
|
|
130
|
+
} catch (error) {
|
|
131
|
+
throw setupError(`Could not read the secrets config: ${error.message}`);
|
|
132
|
+
}
|
|
133
|
+
const configured =
|
|
134
|
+
typeof cfg?.provider === "string" && cfg.provider !== "env";
|
|
135
|
+
return configured ? { cfg, adapter: secrets } : null;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Call the adapter, turning a provider error into a {@link TokenStoreError}.
|
|
140
|
+
* @param {() => unknown} call - Adapter call
|
|
141
|
+
* @param {{ provider: string, name: string, retry: string }} where - Context
|
|
142
|
+
* @returns {Promise<unknown>} The call's result
|
|
143
|
+
*/
|
|
144
|
+
export async function guarded(call, where) {
|
|
145
|
+
try {
|
|
146
|
+
return await call();
|
|
147
|
+
} catch (error) {
|
|
148
|
+
throw providerError(error, where);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading the Telegram bot token from the operator, and the shell lines that
|
|
3
|
+
* name the `store-token` command: token shape validation, the echo-off
|
|
4
|
+
* prompt, the clipboard reader, and the command printed when there is no TTY.
|
|
5
|
+
* Nothing here writes a file or prints the token. Split out of token.mjs to
|
|
6
|
+
* keep it under the line budget (#4555); token.mjs re-exports every export,
|
|
7
|
+
* and token-store.mjs reuses the readers for the secrets-provider path.
|
|
8
|
+
* @module token-read
|
|
9
|
+
*/
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
|
|
12
|
+
import { readClipboard } from "./clipboard.mjs";
|
|
13
|
+
|
|
14
|
+
/** The shape of a BotFather token: `<bot id>:<secret>`. */
|
|
15
|
+
const BOT_TOKEN_PATTERN = /^\d+:[A-Za-z0-9_-]{30,}$/;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Whether `token` looks like a BotFather bot token.
|
|
19
|
+
* @param {unknown} token - Candidate
|
|
20
|
+
* @returns {boolean} True for `<digits>:<30+ of [A-Za-z0-9_-]>`
|
|
21
|
+
*/
|
|
22
|
+
export function validateBotToken(token) {
|
|
23
|
+
return typeof token === "string" && BOT_TOKEN_PATTERN.test(token);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Load `promptSecret`/`canPrompt` from the secrets-access skill, by path
|
|
28
|
+
* relative to this file, the same way other skills reach it. Lazy so the
|
|
29
|
+
* argv builders never pay for it.
|
|
30
|
+
* @returns {Promise<{ promptSecret: Function, canPrompt: Function }>} Module
|
|
31
|
+
*/
|
|
32
|
+
export function loadPromptSecret() {
|
|
33
|
+
return import(
|
|
34
|
+
new URL(
|
|
35
|
+
"../../lisa-secrets-access/scripts/prompt-secret.mjs",
|
|
36
|
+
import.meta.url
|
|
37
|
+
).href
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Quote one word for a POSIX shell.
|
|
43
|
+
* @param {string} word - Word to quote
|
|
44
|
+
* @returns {string} Single-quoted word
|
|
45
|
+
*/
|
|
46
|
+
export function shellQuote(word) {
|
|
47
|
+
const escaped = String(word).replaceAll("'", String.raw`'\''`);
|
|
48
|
+
return `'${escaped}'`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The command an operator pastes into their own terminal to store the token,
|
|
53
|
+
* for when the caller (an agent's Bash) has no TTY to prompt on. `start`
|
|
54
|
+
* also names it when a plaintext token file is left over, since the same
|
|
55
|
+
* command migrates that file into the secrets provider (#4555).
|
|
56
|
+
* @param {{ scriptsDir: string, projectRoot: string, replace?: boolean,
|
|
57
|
+
* fromClipboard?: boolean, vaultProject?: string }} input - Script and
|
|
58
|
+
* project locations, and the flags to carry
|
|
59
|
+
* @returns {string} One shell line
|
|
60
|
+
*/
|
|
61
|
+
export function buildStoreTokenCommand({
|
|
62
|
+
scriptsDir,
|
|
63
|
+
projectRoot,
|
|
64
|
+
replace,
|
|
65
|
+
fromClipboard = false,
|
|
66
|
+
vaultProject,
|
|
67
|
+
}) {
|
|
68
|
+
const script = shellQuote(path.join(scriptsDir, "session-manager.mjs"));
|
|
69
|
+
const flags =
|
|
70
|
+
(replace ? " --replace" : "") +
|
|
71
|
+
(fromClipboard ? " --from-clipboard" : "") +
|
|
72
|
+
(vaultProject ? ` --vault-project ${shellQuote(vaultProject)}` : "");
|
|
73
|
+
return `node ${script} store-token${flags} --project-root ${shellQuote(projectRoot)}`;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Why `store-token` cannot prompt here, and the two ways forward. */
|
|
77
|
+
const NO_TTY_HINT =
|
|
78
|
+
"No terminal to prompt on, and Claude Code's ! prefix has no TTY either. " +
|
|
79
|
+
"Run `command` in a separate terminal app, or copy the token from " +
|
|
80
|
+
"BotFather and run `clipboardCommand`.";
|
|
81
|
+
|
|
82
|
+
/** The `invalid` outcome; names the expected shape, never the input. */
|
|
83
|
+
const INVALID = Object.freeze({
|
|
84
|
+
stored: false,
|
|
85
|
+
reason: "invalid",
|
|
86
|
+
message:
|
|
87
|
+
"That does not look like a Telegram bot token " +
|
|
88
|
+
"(expected <digits>:<30 or more letters, digits, _ or ->).",
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Read a well-formed bot token from the system clipboard, with no TTY.
|
|
93
|
+
*
|
|
94
|
+
* The token is returned only to the caller that stores it, with a `clear`
|
|
95
|
+
* function to call after a successful store, so a failed attempt leaves the
|
|
96
|
+
* operator's copy in place to retry with. An invalid value is reported by
|
|
97
|
+
* shape, never echoed.
|
|
98
|
+
* @param {{ readClipboard?: typeof readClipboard }} [io] - Seams
|
|
99
|
+
* @returns {{ token: string, clear: () => boolean } |
|
|
100
|
+
* { stored: false, reason: "empty" | "invalid" | "unsupported" |
|
|
101
|
+
* "unavailable" | "failed", message: string }} Token, or why not
|
|
102
|
+
*/
|
|
103
|
+
export function readTokenFromClipboard(io = {}) {
|
|
104
|
+
const clip = (io.readClipboard ?? readClipboard)();
|
|
105
|
+
if (!clip.ok) {
|
|
106
|
+
return { stored: false, reason: clip.reason, message: clip.message };
|
|
107
|
+
}
|
|
108
|
+
const token = clip.text.trim();
|
|
109
|
+
if (!token) {
|
|
110
|
+
return {
|
|
111
|
+
stored: false,
|
|
112
|
+
reason: "empty",
|
|
113
|
+
message: "The clipboard is empty.",
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
if (!validateBotToken(token)) return { ...INVALID };
|
|
117
|
+
return { token, clear: clip.clear };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Prompt for a well-formed bot token on the operator's terminal (echo off).
|
|
122
|
+
*
|
|
123
|
+
* With no TTY this does not prompt and does not fail: it returns the exact
|
|
124
|
+
* command for the operator to run in a separate terminal app, and the
|
|
125
|
+
* `--from-clipboard` form of it, because an agent's Bash (and Claude Code's
|
|
126
|
+
* `!` prefix) can never answer a prompt and a hung setup looks like a slow
|
|
127
|
+
* one.
|
|
128
|
+
* @param {{ label: string, scriptsDir: string, projectRoot: string,
|
|
129
|
+
* replace?: boolean, vaultProject?: string, io?: {
|
|
130
|
+
* canPrompt?: () => boolean, promptSecret?: (message: string) => string } }}
|
|
131
|
+
* input - What the token is for (shown in the prompt), locations, the
|
|
132
|
+
* flags the printed commands carry, and seams
|
|
133
|
+
* @returns {Promise<{ token: string } |
|
|
134
|
+
* { stored: false, reason: "no-tty", message: string, command: string,
|
|
135
|
+
* clipboardCommand: string } |
|
|
136
|
+
* { stored: false, reason: "empty" | "invalid", message: string }>} Token,
|
|
137
|
+
* or why not
|
|
138
|
+
*/
|
|
139
|
+
export async function readTokenFromPrompt(input) {
|
|
140
|
+
const { label, io = {} } = input;
|
|
141
|
+
const loaded =
|
|
142
|
+
io.canPrompt && io.promptSecret ? null : await loadPromptSecret();
|
|
143
|
+
const canPrompt = io.canPrompt ?? (() => loaded.canPrompt());
|
|
144
|
+
const promptSecret = io.promptSecret ?? loaded.promptSecret;
|
|
145
|
+
|
|
146
|
+
if (!canPrompt()) {
|
|
147
|
+
return {
|
|
148
|
+
stored: false,
|
|
149
|
+
reason: "no-tty",
|
|
150
|
+
message: NO_TTY_HINT,
|
|
151
|
+
command: buildStoreTokenCommand(input),
|
|
152
|
+
clipboardCommand: buildStoreTokenCommand({
|
|
153
|
+
...input,
|
|
154
|
+
fromClipboard: true,
|
|
155
|
+
}),
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
const typed = promptSecret(
|
|
159
|
+
`Paste the Telegram bot token for ${label} (input is hidden): `
|
|
160
|
+
);
|
|
161
|
+
if (!typed) {
|
|
162
|
+
return { stored: false, reason: "empty", message: "Nothing entered." };
|
|
163
|
+
}
|
|
164
|
+
if (!validateBotToken(typed)) return { ...INVALID };
|
|
165
|
+
return { token: typed };
|
|
166
|
+
}
|