switchroom 0.7.15 → 0.10.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/README.md +51 -59
- package/bin/run-hook.sh +27 -11
- package/bin/timezone-hook.sh +9 -7
- package/dist/agent-scheduler/index.js +410 -133
- package/dist/auth-broker/index.js +13932 -0
- package/dist/cli/switchroom.js +26937 -5601
- package/dist/host-control/main.js +12702 -0
- package/dist/vault/approvals/kernel-server.js +467 -184
- package/dist/vault/broker/server.js +1430 -724
- package/examples/minimal.yaml +63 -0
- package/examples/personal-google-workspace-mcp/.env.example +34 -0
- package/examples/personal-google-workspace-mcp/README.md +194 -0
- package/examples/personal-google-workspace-mcp/compose.yaml +66 -0
- package/examples/switchroom.yaml +220 -0
- package/package.json +7 -4
- package/profiles/_base/settings.json.hbs +20 -5
- package/profiles/_base/start.sh.hbs +16 -3
- package/profiles/_shared/agent-self-service.md.hbs +126 -0
- package/profiles/_shared/telegram-style.md.hbs +20 -90
- package/profiles/_shared/vault-protocol.md.hbs +68 -0
- package/profiles/default/CLAUDE.md +50 -96
- package/profiles/default/CLAUDE.md.hbs +36 -6
- package/profiles/default/workspace/SOUL.md.hbs +12 -5
- package/skills/buildkite-agent-infrastructure/SKILL.md +30 -11
- package/skills/buildkite-agent-runtime/SKILL.md +44 -11
- package/skills/buildkite-api/SKILL.md +31 -8
- package/skills/buildkite-cli/SKILL.md +27 -9
- package/skills/buildkite-migration/SKILL.md +22 -9
- package/skills/buildkite-pipelines/SKILL.md +26 -9
- package/skills/buildkite-secure-delivery/SKILL.md +23 -9
- package/skills/buildkite-test-engine/SKILL.md +25 -8
- package/skills/docx/SKILL.md +1 -1
- package/skills/docx/scripts/office/validators/__pycache__/__init__.cpython-313.pyc +0 -0
- package/skills/docx/scripts/office/validators/__pycache__/base.cpython-313.pyc +0 -0
- package/skills/file-bug/SKILL.md +34 -6
- package/skills/humanizer/SKILL.md +15 -0
- package/skills/humanizer-calibrate/SKILL.md +7 -1
- package/skills/mcp-builder/SKILL.md +1 -1
- package/skills/pdf/SKILL.md +1 -1
- package/skills/pptx/SKILL.md +1 -1
- package/skills/skill-creator/SKILL.md +21 -1
- package/skills/skill-creator/scripts/__pycache__/__init__.cpython-313.pyc +0 -0
- package/skills/skill-creator/scripts/__pycache__/generate_report.cpython-313.pyc +0 -0
- package/skills/skill-creator/scripts/__pycache__/improve_description.cpython-313.pyc +0 -0
- package/skills/skill-creator/scripts/__pycache__/run_eval.cpython-313.pyc +0 -0
- package/skills/skill-creator/scripts/__pycache__/run_loop.cpython-313.pyc +0 -0
- package/skills/skill-creator/scripts/__pycache__/utils.cpython-313.pyc +0 -0
- package/skills/switchroom-cli/SKILL.md +63 -64
- package/skills/switchroom-health/SKILL.md +23 -10
- package/skills/switchroom-install/SKILL.md +3 -3
- package/skills/switchroom-manage/SKILL.md +26 -19
- package/skills/switchroom-runtime/SKILL.md +191 -0
- package/skills/switchroom-status/SKILL.md +27 -2
- package/skills/telegram-test-harness/SKILL.md +3 -0
- package/skills/token-helpers/SKILL.md +24 -1
- package/skills/webapp-testing/SKILL.md +31 -1
- package/skills/xlsx/SKILL.md +1 -1
- package/telegram-plugin/admin-commands/index.ts +7 -5
- package/telegram-plugin/analytics-posthog.ts +191 -0
- package/telegram-plugin/bridge/bridge.ts +69 -0
- package/telegram-plugin/bridge/ipc-client.ts +4 -1
- package/telegram-plugin/dist/bridge/bridge.js +194 -119
- package/telegram-plugin/dist/gateway/gateway.js +23611 -19671
- package/telegram-plugin/dist/server.js +245 -189
- package/telegram-plugin/first-paint.ts +3 -24
- package/telegram-plugin/gateway/auth-add-flow.ts +326 -0
- package/telegram-plugin/gateway/auth-broker-client.ts +75 -0
- package/telegram-plugin/gateway/auth-command.ts +794 -0
- package/telegram-plugin/gateway/auth-line.ts +123 -0
- package/telegram-plugin/gateway/boot-card.ts +169 -40
- package/telegram-plugin/gateway/boot-issue-cache.ts +308 -0
- package/telegram-plugin/gateway/boot-probes.ts +166 -123
- package/telegram-plugin/gateway/boot-reason.ts +41 -7
- package/telegram-plugin/gateway/boot-version.ts +66 -0
- package/telegram-plugin/gateway/gateway.ts +3499 -1885
- package/telegram-plugin/gateway/hostd-dispatch.ts +117 -0
- package/telegram-plugin/gateway/ipc-protocol.ts +18 -0
- package/telegram-plugin/gateway/pending-inbound-buffer.ts +106 -0
- package/telegram-plugin/gateway/quarantine.ts +69 -0
- package/telegram-plugin/gateway/quota-cache.ts +9 -4
- package/telegram-plugin/gateway/reaction-trigger.ts +401 -0
- package/telegram-plugin/gateway/recent-denials.test.ts +103 -0
- package/telegram-plugin/gateway/recent-denials.ts +77 -0
- package/telegram-plugin/gateway/startup-network-retry.ts +109 -31
- package/telegram-plugin/gateway/vault-grant-inbound-builders.ts +125 -0
- package/telegram-plugin/history.ts +91 -0
- package/telegram-plugin/hooks/hooks.json +10 -0
- package/telegram-plugin/hooks/sandbox-hint-posttool.mjs +130 -0
- package/telegram-plugin/hooks/subagent-tracker-posttool.mjs +19 -2
- package/telegram-plugin/hooks/subagent-tracker-pretool.mjs +22 -2
- package/telegram-plugin/hooks/tool-label-pretool.mjs +11 -0
- package/telegram-plugin/hooks/wedge-detect-posttool.mjs +303 -0
- package/telegram-plugin/inbound-classifier.ts +50 -0
- package/telegram-plugin/inline-keyboard-callbacks.ts +136 -0
- package/telegram-plugin/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/telegram-plugin/package.json +4 -2
- package/telegram-plugin/permission-rule.ts +51 -0
- package/telegram-plugin/permission-title.ts +56 -0
- package/telegram-plugin/quota-check.ts +19 -41
- package/telegram-plugin/registry/reaper.ts +223 -0
- package/telegram-plugin/retry-api-call.ts +80 -0
- package/telegram-plugin/runtime-metrics.ts +177 -0
- package/telegram-plugin/scripts/build.mjs +0 -1
- package/telegram-plugin/secret-detect/index.ts +24 -0
- package/telegram-plugin/secret-detect/vault-error.test.ts +64 -12
- package/telegram-plugin/secret-detect/vault-error.ts +78 -11
- package/telegram-plugin/secret-detect/vault-write.ts +14 -2
- package/telegram-plugin/server.js +41795 -0
- package/telegram-plugin/session-tail.ts +6 -1
- package/telegram-plugin/shared/bot-runtime.ts +5 -4
- package/telegram-plugin/silence-poke.ts +420 -0
- package/telegram-plugin/silent-end.ts +174 -0
- package/telegram-plugin/stream-controller.ts +13 -0
- package/telegram-plugin/stream-reply-handler.ts +7 -0
- package/telegram-plugin/subagent-watcher.ts +213 -4
- package/telegram-plugin/tests/auth-add-flow.test.ts +559 -0
- package/telegram-plugin/tests/auth-code-redact.test.ts +8 -4
- package/telegram-plugin/tests/auth-command-vernacular.test.ts +531 -0
- package/telegram-plugin/tests/boot-card-issue-dedup.test.ts +247 -0
- package/telegram-plugin/tests/boot-card-reason-to-render.test.ts +182 -0
- package/telegram-plugin/tests/boot-card-reason.test.ts +65 -2
- package/telegram-plugin/tests/boot-card-render.test.ts +146 -0
- package/telegram-plugin/tests/boot-card-silent-on-operator.test.ts +103 -0
- package/telegram-plugin/tests/boot-probes.test.ts +216 -10
- package/telegram-plugin/tests/boot-version-string.test.ts +0 -0
- package/telegram-plugin/tests/finalize-callback.test.ts +190 -0
- package/telegram-plugin/tests/gateway-message-validator.test.ts +26 -0
- package/telegram-plugin/tests/gateway-secret-detect.test.ts +12 -3
- package/telegram-plugin/tests/gateway-startup-network-retry.test.ts +104 -0
- package/telegram-plugin/tests/history-reaper.test.ts +378 -0
- package/telegram-plugin/tests/hostd-dispatch.test.ts +129 -0
- package/telegram-plugin/tests/inbound-classifier.test.ts +76 -0
- package/telegram-plugin/tests/inbound-message-types.test.ts +267 -0
- package/telegram-plugin/tests/issues-card.test.ts +49 -0
- package/telegram-plugin/tests/pending-inbound-buffer.test.ts +132 -0
- package/telegram-plugin/tests/permission-rule.test.ts +80 -1
- package/telegram-plugin/tests/permission-title.test.ts +31 -0
- package/telegram-plugin/tests/quota-check.test.ts +5 -35
- package/telegram-plugin/tests/races.test.ts +179 -0
- package/telegram-plugin/tests/reaction-trigger-flow.test.ts +353 -0
- package/telegram-plugin/tests/reaction-trigger.test.ts +397 -0
- package/telegram-plugin/tests/retry-api-call.test.ts +152 -1
- package/telegram-plugin/tests/runtime-metrics.test.ts +145 -0
- package/telegram-plugin/tests/sandbox-hint-posttool.test.ts +155 -0
- package/telegram-plugin/tests/secret-detect-delete-must-surface-failures.test.ts +133 -0
- package/telegram-plugin/tests/secret-detect-false-positives.test.ts +137 -0
- package/telegram-plugin/tests/silence-poke.test.ts +493 -0
- package/telegram-plugin/tests/silent-end.test.ts +206 -0
- package/telegram-plugin/tests/subagent-tracker-hooks.test.ts +107 -0
- package/telegram-plugin/tests/subagent-watcher-env-thresholds.test.ts +224 -0
- package/telegram-plugin/tests/subagent-watcher-stall-terminal.test.ts +316 -0
- package/telegram-plugin/tests/subagent-watcher.test.ts +263 -0
- package/telegram-plugin/tests/turn-signal-tracker.test.ts +81 -0
- package/telegram-plugin/tests/vault-approval-posture.test.ts +256 -0
- package/telegram-plugin/tests/vault-grant-auto-resume.test.ts +73 -0
- package/telegram-plugin/tests/vault-grant-inbound-builders.test.ts +226 -0
- package/telegram-plugin/tests/vault-grant-union.test.ts +130 -0
- package/telegram-plugin/tests/vault-key-regex-allows-slash.test.ts +140 -0
- package/telegram-plugin/tests/vault-posture-quarantine.test.ts +104 -0
- package/telegram-plugin/tests/vault-request-access-tool.test.ts +114 -0
- package/telegram-plugin/tests/vault-request-access-unlock-resume.test.ts +106 -0
- package/telegram-plugin/turn-signal-tracker.ts +100 -24
- package/telegram-plugin/uat/SETUP.md +210 -35
- package/telegram-plugin/uat/assertions.ts +264 -37
- package/telegram-plugin/uat/driver-info.ts +57 -0
- package/telegram-plugin/uat/driver.ts +590 -51
- package/telegram-plugin/uat/harness.ts +140 -94
- package/telegram-plugin/uat/load-env.test.ts +72 -0
- package/telegram-plugin/uat/load-env.ts +48 -0
- package/telegram-plugin/uat/login.ts +96 -53
- package/telegram-plugin/uat/runners/agent-self-sufficiency.ts +457 -0
- package/telegram-plugin/uat/runners/paraphrases.ts +231 -0
- package/telegram-plugin/uat/runners/report.ts +150 -0
- package/telegram-plugin/uat/runners/run-agent-self-sufficiency.sh +50 -0
- package/telegram-plugin/uat/runners/scorer.test.ts +196 -0
- package/telegram-plugin/uat/runners/scorer.ts +106 -0
- package/telegram-plugin/uat/runners/skill-coverage.test.ts +100 -0
- package/telegram-plugin/uat/runners/skill-coverage.ts +620 -0
- package/telegram-plugin/uat/scenarios/ask-user-button-tap-dm.test.ts +141 -0
- package/telegram-plugin/uat/scenarios/bg-sub-agent-dispatch-dm.test.ts +191 -0
- package/telegram-plugin/uat/scenarios/fuzz-extended-dm.test.ts +255 -0
- package/telegram-plugin/uat/scenarios/fuzz-human-style-dm.test.ts +275 -0
- package/telegram-plugin/uat/scenarios/fuzz-random-prompts-dm.test.ts +146 -0
- package/telegram-plugin/uat/scenarios/fuzz-status-ask-dm.test.ts +486 -0
- package/telegram-plugin/uat/scenarios/jtbd-interrupt-marker-dm.test.ts +67 -0
- package/telegram-plugin/uat/scenarios/jtbd-rapid-followup-dm.test.ts +100 -0
- package/telegram-plugin/uat/scenarios/jtbd-soft-commit-dm.test.ts +67 -0
- package/telegram-plugin/uat/scenarios/jtbd-status-query-dm.test.ts +49 -0
- package/telegram-plugin/uat/scenarios/location-inbound-dm.test.ts +65 -0
- package/telegram-plugin/uat/scenarios/midturn-silent-dm.test.ts +175 -0
- package/telegram-plugin/uat/scenarios/reactions-dm.test.ts +142 -0
- package/telegram-plugin/uat/scenarios/reactions-trigger-turn-dm.test.ts +96 -0
- package/telegram-plugin/uat/scenarios/secret-redaction-deletes-original-dm.test.ts +123 -0
- package/telegram-plugin/uat/scenarios/secret-redaction-no-false-positive-dm.test.ts +87 -0
- package/telegram-plugin/uat/scenarios/silence-poke-soft-dm.test.ts +155 -0
- package/telegram-plugin/uat/scenarios/silent-end-recovery-dm.test.ts +95 -0
- package/telegram-plugin/uat/scenarios/smoke-dm-reply.test.ts +57 -0
- package/telegram-plugin/uat/scenarios/subagent-watcher-no-rerun-dm.test.ts +135 -0
- package/telegram-plugin/uat/scenarios/vault-approval-posture-telegram-id-dm.test.ts +191 -0
- package/telegram-plugin/uat/scenarios/vault-audit-allow-dm.test.ts +108 -0
- package/telegram-plugin/uat/scenarios/vault-grant-auto-resume-dm.test.ts +121 -0
- package/telegram-plugin/uat/scenarios/vault-request-access-concurrent-dm.test.ts +161 -0
- package/telegram-plugin/uat/scenarios/vault-request-access-end-to-end-dm.test.ts +158 -0
- package/telegram-plugin/uat/scenarios/voice-inbound-dm.test.ts +65 -0
- package/telegram-plugin/vault-approval-posture.ts +42 -0
- package/telegram-plugin/welcome-text.ts +1 -0
- package/telegram-plugin/active-pins-sweep.ts +0 -204
- package/telegram-plugin/active-pins.ts +0 -146
- package/telegram-plugin/auth-dashboard.ts +0 -1104
- package/telegram-plugin/auth-slot-parser.ts +0 -497
- package/telegram-plugin/card-event-log.ts +0 -138
- package/telegram-plugin/dist/foreman/foreman.js +0 -31106
- package/telegram-plugin/docs/multi-agent-card-design.md +0 -847
- package/telegram-plugin/docs/pinned-progress-card-reliability.md +0 -144
- package/telegram-plugin/foreman/foreman-create-flow.ts +0 -202
- package/telegram-plugin/foreman/foreman-handlers.ts +0 -493
- package/telegram-plugin/foreman/foreman.ts +0 -1165
- package/telegram-plugin/foreman/setup-flow.ts +0 -345
- package/telegram-plugin/foreman/setup-state.ts +0 -239
- package/telegram-plugin/foreman/state.ts +0 -203
- package/telegram-plugin/pin-event-log.ts +0 -76
- package/telegram-plugin/progress-card-driver.ts +0 -2886
- package/telegram-plugin/progress-card-pin-manager.ts +0 -589
- package/telegram-plugin/progress-card-pin-watchdog.ts +0 -98
- package/telegram-plugin/progress-card.ts +0 -1409
- package/telegram-plugin/tests/HARNESS.md +0 -340
- package/telegram-plugin/tests/_progress-card-harness.ts +0 -109
- package/telegram-plugin/tests/active-pins-boot-reaper.test.ts +0 -211
- package/telegram-plugin/tests/active-pins-sweep.test.ts +0 -309
- package/telegram-plugin/tests/active-pins.test.ts +0 -187
- package/telegram-plugin/tests/auth-account-identity-surface.test.ts +0 -118
- package/telegram-plugin/tests/auth-dashboard-edge-cases.test.ts +0 -260
- package/telegram-plugin/tests/auth-dashboard-restart-flow.test.ts +0 -140
- package/telegram-plugin/tests/auth-dashboard-v3b.test.ts +0 -559
- package/telegram-plugin/tests/auth-dashboard.test.ts +0 -1045
- package/telegram-plugin/tests/auth-slot-commands.test.ts +0 -640
- package/telegram-plugin/tests/bg-agent-progress-card-757.test.ts +0 -201
- package/telegram-plugin/tests/boot-card-account-quota.test.ts +0 -137
- package/telegram-plugin/tests/card-event-log.test.ts +0 -145
- package/telegram-plugin/tests/first-paint.test.ts +0 -257
- package/telegram-plugin/tests/foreman-create-flow.test.ts +0 -359
- package/telegram-plugin/tests/foreman-handlers.test.ts +0 -347
- package/telegram-plugin/tests/foreman-state.test.ts +0 -164
- package/telegram-plugin/tests/foreman-write-ops.test.ts +0 -214
- package/telegram-plugin/tests/harness-ordering-invariants.test.ts +0 -243
- package/telegram-plugin/tests/pin-event-log.test.ts +0 -124
- package/telegram-plugin/tests/progress-card-api-failure-during-deferred.test.ts +0 -73
- package/telegram-plugin/tests/progress-card-close-paths-converge.test.ts +0 -272
- package/telegram-plugin/tests/progress-card-cross-turn.test.ts +0 -258
- package/telegram-plugin/tests/progress-card-delay-842.test.ts +0 -160
- package/telegram-plugin/tests/progress-card-dispose-preservepending.test.ts +0 -81
- package/telegram-plugin/tests/progress-card-draft-flag.test.ts +0 -80
- package/telegram-plugin/tests/progress-card-driver-eviction.test.ts +0 -215
- package/telegram-plugin/tests/progress-card-driver-fleet-shadow.test.ts +0 -123
- package/telegram-plugin/tests/progress-card-driver-force-complete-parent-done.test.ts +0 -76
- package/telegram-plugin/tests/progress-card-edit-timestamps-budget.test.ts +0 -62
- package/telegram-plugin/tests/progress-card-memory-bounds.test.ts +0 -84
- package/telegram-plugin/tests/progress-card-pin-failure-paths.test.ts +0 -139
- package/telegram-plugin/tests/progress-card-pin-manager.test.ts +0 -773
- package/telegram-plugin/tests/progress-card-pin-race-fast-turn.test.ts +0 -66
- package/telegram-plugin/tests/progress-card-pin-sidecar-partial-write.test.ts +0 -64
- package/telegram-plugin/tests/progress-card-pin-watchdog.test.ts +0 -190
- package/telegram-plugin/tests/progress-card-sigterm-pin-flush.test.ts +0 -146
- package/telegram-plugin/tests/real-gateway-f1-ladder-integrity.test.ts +0 -123
- package/telegram-plugin/tests/real-gateway-f2-instant-draft.test.ts +0 -82
- package/telegram-plugin/tests/real-gateway-f3-late-card.test.ts +0 -114
- package/telegram-plugin/tests/real-gateway-harness.ts +0 -699
- package/telegram-plugin/tests/real-gateway-i6-turn-flush-replay-dedup.test.ts +0 -313
- package/telegram-plugin/tests/real-gateway-ipc-lifecycle.test.ts +0 -299
- package/telegram-plugin/tests/real-gateway-spec.test.ts +0 -487
- package/telegram-plugin/tests/real-gateway.smoke.test.ts +0 -101
- package/telegram-plugin/tests/setup-flow.test.ts +0 -510
- package/telegram-plugin/tests/setup-state.test.ts +0 -146
- package/telegram-plugin/tests/sync-chat-running-subagents.test.ts +0 -116
- package/telegram-plugin/tests/turn-end-regressions.test.ts +0 -489
- package/telegram-plugin/tests/turn-flush-card-takeover.test.ts +0 -218
- package/telegram-plugin/tests/turn-flush-prose-recovery.test.ts +0 -78
- package/telegram-plugin/tests/two-zone-bg-carry-full-lifecycle.test.ts +0 -131
- package/telegram-plugin/tests/two-zone-bg-detection.test.ts +0 -120
- package/telegram-plugin/tests/two-zone-bg-done-when-all-terminal.test.ts +0 -116
- package/telegram-plugin/tests/two-zone-bg-early-turn-end.test.ts +0 -87
- package/telegram-plugin/tests/two-zone-bg-survives-next-turn.test.ts +0 -211
- package/telegram-plugin/tests/two-zone-card-cap.test.ts +0 -62
- package/telegram-plugin/tests/two-zone-card-fleet-row.test.ts +0 -101
- package/telegram-plugin/tests/two-zone-card-header-phases.test.ts +0 -78
- package/telegram-plugin/tests/two-zone-card-html-balance.test.ts +0 -110
- package/telegram-plugin/tests/two-zone-card-lifecycle.test.ts +0 -128
- package/telegram-plugin/tests/two-zone-card-sanitise.test.ts +0 -58
- package/telegram-plugin/tests/two-zone-card-snapshot.test.ts +0 -133
- package/telegram-plugin/tests/two-zone-concurrent-turns-isolation.test.ts +0 -155
- package/telegram-plugin/tests/two-zone-phasefor-precedence.test.ts +0 -117
- package/telegram-plugin/tests/two-zone-snapshot-extras.test.ts +0 -187
- package/telegram-plugin/tests/two-zone-stuck-edit-throttle.test.ts +0 -149
- package/telegram-plugin/tests/two-zone-stuck-header-escalation.test.ts +0 -101
- package/telegram-plugin/tests/two-zone-stuck-per-member.test.ts +0 -114
- package/telegram-plugin/tests/two-zone-stuck-recovery.test.ts +0 -105
- package/telegram-plugin/tests/waiting-ux-harness.ts +0 -381
- package/telegram-plugin/tests/waiting-ux.e2e.test.ts +0 -233
- package/telegram-plugin/turn-flush-prose-recovery.ts +0 -40
- package/telegram-plugin/two-zone-card.ts +0 -269
- package/telegram-plugin/uat/scenarios/smoke-clerk-reply.test.ts +0 -61
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Foreman conversation state — SQLite-backed per-chat state for multi-turn
|
|
3
|
-
* flows. Survives foreman restarts so a create-agent flow started before a
|
|
4
|
-
* restart can resume cleanly.
|
|
5
|
-
*
|
|
6
|
-
* Location: ~/.switchroom/foreman/state.sqlite
|
|
7
|
-
* Override via SWITCHROOM_FOREMAN_DIR env var.
|
|
8
|
-
*
|
|
9
|
-
* Schema:
|
|
10
|
-
* CREATE TABLE IF NOT EXISTS create_flow (
|
|
11
|
-
* chat_id TEXT PRIMARY KEY,
|
|
12
|
-
* step TEXT NOT NULL, -- 'asked-name' | 'asked-profile' | 'asked-bot-token' | 'asked-oauth-code' | 'done'
|
|
13
|
-
* name TEXT,
|
|
14
|
-
* profile TEXT,
|
|
15
|
-
* bot_token TEXT,
|
|
16
|
-
* auth_session_name TEXT,
|
|
17
|
-
* login_url TEXT,
|
|
18
|
-
* started_at INTEGER NOT NULL,
|
|
19
|
-
* updated_at INTEGER NOT NULL
|
|
20
|
-
* );
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
import { Database } from 'bun:sqlite'
|
|
24
|
-
import { chmodSync, mkdirSync } from 'fs'
|
|
25
|
-
import { homedir } from 'os'
|
|
26
|
-
import { join } from 'path'
|
|
27
|
-
|
|
28
|
-
// ─── Types ────────────────────────────────────────────────────────────────
|
|
29
|
-
|
|
30
|
-
export type CreateFlowStep =
|
|
31
|
-
| 'asked-name'
|
|
32
|
-
| 'asked-profile'
|
|
33
|
-
| 'asked-bot-token'
|
|
34
|
-
| 'asked-oauth-code'
|
|
35
|
-
| 'done'
|
|
36
|
-
|
|
37
|
-
export interface CreateFlowState {
|
|
38
|
-
chatId: string
|
|
39
|
-
step: CreateFlowStep
|
|
40
|
-
name: string | null
|
|
41
|
-
profile: string | null
|
|
42
|
-
botToken: string | null
|
|
43
|
-
authSessionName: string | null
|
|
44
|
-
loginUrl: string | null
|
|
45
|
-
startedAt: number
|
|
46
|
-
updatedAt: number
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// ─── DB singleton ─────────────────────────────────────────────────────────
|
|
50
|
-
|
|
51
|
-
let _db: Database | null = null
|
|
52
|
-
|
|
53
|
-
function getDb(): Database {
|
|
54
|
-
if (_db) return _db
|
|
55
|
-
|
|
56
|
-
const foremanDir =
|
|
57
|
-
process.env.SWITCHROOM_FOREMAN_DIR ?? join(homedir(), '.switchroom', 'foreman')
|
|
58
|
-
|
|
59
|
-
// 0o700 on the directory + 0o600 on the DB: this file stores in-flight
|
|
60
|
-
// BotFather tokens during /create-agent flows. On a multi-user host,
|
|
61
|
-
// default umask (0o022) would leave tokens world-readable otherwise.
|
|
62
|
-
mkdirSync(foremanDir, { recursive: true, mode: 0o700 })
|
|
63
|
-
|
|
64
|
-
const dbPath = join(foremanDir, 'state.sqlite')
|
|
65
|
-
_db = new Database(dbPath)
|
|
66
|
-
try {
|
|
67
|
-
chmodSync(dbPath, 0o600)
|
|
68
|
-
} catch {
|
|
69
|
-
// best-effort — fall through if chmod isn't supported
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
_db.exec(`
|
|
73
|
-
CREATE TABLE IF NOT EXISTS create_flow (
|
|
74
|
-
chat_id TEXT PRIMARY KEY,
|
|
75
|
-
step TEXT NOT NULL,
|
|
76
|
-
name TEXT,
|
|
77
|
-
profile TEXT,
|
|
78
|
-
bot_token TEXT,
|
|
79
|
-
auth_session_name TEXT,
|
|
80
|
-
login_url TEXT,
|
|
81
|
-
started_at INTEGER NOT NULL,
|
|
82
|
-
updated_at INTEGER NOT NULL
|
|
83
|
-
);
|
|
84
|
-
`)
|
|
85
|
-
|
|
86
|
-
return _db
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// ─── Public API ───────────────────────────────────────────────────────────
|
|
90
|
-
|
|
91
|
-
/** Upsert the state for a given chat. */
|
|
92
|
-
export function setState(state: CreateFlowState): void {
|
|
93
|
-
const db = getDb()
|
|
94
|
-
db.prepare(`
|
|
95
|
-
INSERT INTO create_flow
|
|
96
|
-
(chat_id, step, name, profile, bot_token, auth_session_name, login_url, started_at, updated_at)
|
|
97
|
-
VALUES
|
|
98
|
-
($chatId, $step, $name, $profile, $botToken, $authSessionName, $loginUrl, $startedAt, $updatedAt)
|
|
99
|
-
ON CONFLICT(chat_id) DO UPDATE SET
|
|
100
|
-
step = excluded.step,
|
|
101
|
-
name = excluded.name,
|
|
102
|
-
profile = excluded.profile,
|
|
103
|
-
bot_token = excluded.bot_token,
|
|
104
|
-
auth_session_name = excluded.auth_session_name,
|
|
105
|
-
login_url = excluded.login_url,
|
|
106
|
-
updated_at = excluded.updated_at
|
|
107
|
-
`).run({
|
|
108
|
-
$chatId: state.chatId,
|
|
109
|
-
$step: state.step,
|
|
110
|
-
$name: state.name,
|
|
111
|
-
$profile: state.profile,
|
|
112
|
-
$botToken: state.botToken,
|
|
113
|
-
$authSessionName: state.authSessionName,
|
|
114
|
-
$loginUrl: state.loginUrl,
|
|
115
|
-
$startedAt: state.startedAt,
|
|
116
|
-
$updatedAt: state.updatedAt,
|
|
117
|
-
})
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/** Retrieve the state for a given chat, or null if none exists. */
|
|
121
|
-
export function getState(chatId: string): CreateFlowState | null {
|
|
122
|
-
const db = getDb()
|
|
123
|
-
const row = db.prepare<{
|
|
124
|
-
chat_id: string
|
|
125
|
-
step: string
|
|
126
|
-
name: string | null
|
|
127
|
-
profile: string | null
|
|
128
|
-
bot_token: string | null
|
|
129
|
-
auth_session_name: string | null
|
|
130
|
-
login_url: string | null
|
|
131
|
-
started_at: number
|
|
132
|
-
updated_at: number
|
|
133
|
-
}, [string]>(`
|
|
134
|
-
SELECT chat_id, step, name, profile, bot_token, auth_session_name, login_url, started_at, updated_at
|
|
135
|
-
FROM create_flow
|
|
136
|
-
WHERE chat_id = ?
|
|
137
|
-
`).get(chatId)
|
|
138
|
-
|
|
139
|
-
if (!row) return null
|
|
140
|
-
|
|
141
|
-
return {
|
|
142
|
-
chatId: row.chat_id,
|
|
143
|
-
step: row.step as CreateFlowStep,
|
|
144
|
-
name: row.name,
|
|
145
|
-
profile: row.profile,
|
|
146
|
-
botToken: row.bot_token,
|
|
147
|
-
authSessionName: row.auth_session_name,
|
|
148
|
-
loginUrl: row.login_url,
|
|
149
|
-
startedAt: row.started_at,
|
|
150
|
-
updatedAt: row.updated_at,
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/** Remove the state for a given chat (flow completed or cancelled). */
|
|
155
|
-
export function clearState(chatId: string): void {
|
|
156
|
-
const db = getDb()
|
|
157
|
-
db.prepare('DELETE FROM create_flow WHERE chat_id = ?').run(chatId)
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* List all in-progress flows updated within the last `maxAgeMs` ms.
|
|
162
|
-
* Used at foreman startup to resume flows that survived a restart.
|
|
163
|
-
*/
|
|
164
|
-
export function listActiveFlows(maxAgeMs = 60 * 60 * 1000): CreateFlowState[] {
|
|
165
|
-
const db = getDb()
|
|
166
|
-
const cutoff = Date.now() - maxAgeMs
|
|
167
|
-
const rows = db.prepare<{
|
|
168
|
-
chat_id: string
|
|
169
|
-
step: string
|
|
170
|
-
name: string | null
|
|
171
|
-
profile: string | null
|
|
172
|
-
bot_token: string | null
|
|
173
|
-
auth_session_name: string | null
|
|
174
|
-
login_url: string | null
|
|
175
|
-
started_at: number
|
|
176
|
-
updated_at: number
|
|
177
|
-
}, [number]>(`
|
|
178
|
-
SELECT chat_id, step, name, profile, bot_token, auth_session_name, login_url, started_at, updated_at
|
|
179
|
-
FROM create_flow
|
|
180
|
-
WHERE step != 'done' AND updated_at > ?
|
|
181
|
-
ORDER BY updated_at DESC
|
|
182
|
-
`).all(cutoff)
|
|
183
|
-
|
|
184
|
-
return rows.map(row => ({
|
|
185
|
-
chatId: row.chat_id,
|
|
186
|
-
step: row.step as CreateFlowStep,
|
|
187
|
-
name: row.name,
|
|
188
|
-
profile: row.profile,
|
|
189
|
-
botToken: row.bot_token,
|
|
190
|
-
authSessionName: row.auth_session_name,
|
|
191
|
-
loginUrl: row.login_url,
|
|
192
|
-
startedAt: row.started_at,
|
|
193
|
-
updatedAt: row.updated_at,
|
|
194
|
-
}))
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/** Reset the DB singleton (useful in tests to avoid sharing state). */
|
|
198
|
-
export function _resetDbForTest(): void {
|
|
199
|
-
if (_db) {
|
|
200
|
-
_db.close()
|
|
201
|
-
_db = null
|
|
202
|
-
}
|
|
203
|
-
}
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Structured logger for pin/unpin lifecycle events.
|
|
3
|
-
*
|
|
4
|
-
* Every call to `pinChatMessage` or `unpinChatMessage` — whether from the
|
|
5
|
-
* live turn path or the crash/restart sweeps — emits one JSON-encoded line
|
|
6
|
-
* on stderr, prefixed with `pin-event:`. This gives an out-of-band audit
|
|
7
|
-
* trail operators can grep without parsing free-form log text.
|
|
8
|
-
*
|
|
9
|
-
* Pure helper. No globals. The write target is injectable for tests.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
export type PinEventName =
|
|
13
|
-
| 'pin'
|
|
14
|
-
| 'unpin'
|
|
15
|
-
| 'unpin-retry'
|
|
16
|
-
| 'sweep-pin'
|
|
17
|
-
| 'sweep-auth'
|
|
18
|
-
| 'audit-orphan'
|
|
19
|
-
|
|
20
|
-
export type PinEventOutcome =
|
|
21
|
-
| 'ok'
|
|
22
|
-
| 'fail'
|
|
23
|
-
| 'rate-limited'
|
|
24
|
-
| 'forbidden'
|
|
25
|
-
| 'timeout'
|
|
26
|
-
| 'observed'
|
|
27
|
-
|
|
28
|
-
export interface PinEvent {
|
|
29
|
-
event: PinEventName
|
|
30
|
-
chatId: string
|
|
31
|
-
messageId?: number
|
|
32
|
-
turnKey?: string
|
|
33
|
-
outcome: PinEventOutcome
|
|
34
|
-
error?: string
|
|
35
|
-
durationMs?: number
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export type PinEventWriter = (line: string) => void
|
|
39
|
-
|
|
40
|
-
const defaultWriter: PinEventWriter = (line) => {
|
|
41
|
-
process.stderr.write(line)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export function logPinEvent(event: PinEvent, write: PinEventWriter = defaultWriter): void {
|
|
45
|
-
const payload = JSON.stringify(event)
|
|
46
|
-
write(`pin-event: ${payload}\n`)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Classify a thrown Error from the Telegram API into a stable outcome
|
|
51
|
-
* category. Grammy surfaces rate-limit errors with `error_code: 429` and
|
|
52
|
-
* permission errors with `error_code: 403`; everything else is generic
|
|
53
|
-
* failure. We match on the message body as a last resort for the error
|
|
54
|
-
* shapes that don't surface `error_code` (network timeouts, HTTP-level
|
|
55
|
-
* 429s in the wrapper, etc.) so the category is useful even when the
|
|
56
|
-
* Grammy wrapper hides the structured field.
|
|
57
|
-
*/
|
|
58
|
-
export function classifyPinError(err: unknown): PinEventOutcome {
|
|
59
|
-
if (err == null) return 'fail'
|
|
60
|
-
const anyErr = err as { error_code?: number; description?: string; message?: string }
|
|
61
|
-
const code = anyErr.error_code
|
|
62
|
-
if (code === 429) return 'rate-limited'
|
|
63
|
-
if (code === 403) return 'forbidden'
|
|
64
|
-
const msg = (anyErr.description ?? anyErr.message ?? String(err)).toLowerCase()
|
|
65
|
-
if (msg.includes('too many requests') || msg.includes('rate')) return 'rate-limited'
|
|
66
|
-
if (msg.includes('forbidden') || msg.includes('not enough rights')) return 'forbidden'
|
|
67
|
-
if (msg.includes('timeout') || msg.includes('etimedout')) return 'timeout'
|
|
68
|
-
return 'fail'
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export function errorMessage(err: unknown): string {
|
|
72
|
-
if (err == null) return ''
|
|
73
|
-
if (err instanceof Error) return err.message
|
|
74
|
-
const anyErr = err as { description?: string; message?: string }
|
|
75
|
-
return anyErr.description ?? anyErr.message ?? String(err)
|
|
76
|
-
}
|