@phnx-labs/agents-cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +316 -0
- package/LICENSE +21 -0
- package/README.md +537 -0
- package/dist/commands/__tests__/sessions.test.d.ts +2 -0
- package/dist/commands/__tests__/sessions.test.d.ts.map +1 -0
- package/dist/commands/__tests__/sessions.test.js +636 -0
- package/dist/commands/__tests__/sessions.test.js.map +1 -0
- package/dist/commands/cloud.d.ts +3 -0
- package/dist/commands/cloud.d.ts.map +1 -0
- package/dist/commands/cloud.js +322 -0
- package/dist/commands/cloud.js.map +1 -0
- package/dist/commands/commands.d.ts +3 -0
- package/dist/commands/commands.d.ts.map +1 -0
- package/dist/commands/commands.js +628 -0
- package/dist/commands/commands.js.map +1 -0
- package/dist/commands/daemon.d.ts +3 -0
- package/dist/commands/daemon.d.ts.map +1 -0
- package/dist/commands/daemon.js +110 -0
- package/dist/commands/daemon.js.map +1 -0
- package/dist/commands/drive.d.ts +3 -0
- package/dist/commands/drive.d.ts.map +1 -0
- package/dist/commands/drive.js +166 -0
- package/dist/commands/drive.js.map +1 -0
- package/dist/commands/exec.d.ts +3 -0
- package/dist/commands/exec.d.ts.map +1 -0
- package/dist/commands/exec.js +241 -0
- package/dist/commands/exec.js.map +1 -0
- package/dist/commands/fork.d.ts +3 -0
- package/dist/commands/fork.d.ts.map +1 -0
- package/dist/commands/fork.js +139 -0
- package/dist/commands/fork.js.map +1 -0
- package/dist/commands/hooks.d.ts +3 -0
- package/dist/commands/hooks.d.ts.map +1 -0
- package/dist/commands/hooks.js +689 -0
- package/dist/commands/hooks.js.map +1 -0
- package/dist/commands/init.d.ts +6 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +127 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/mcp.d.ts +3 -0
- package/dist/commands/mcp.d.ts.map +1 -0
- package/dist/commands/mcp.js +581 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/commands/models.d.ts +3 -0
- package/dist/commands/models.d.ts.map +1 -0
- package/dist/commands/models.js +158 -0
- package/dist/commands/models.js.map +1 -0
- package/dist/commands/packages.d.ts +3 -0
- package/dist/commands/packages.d.ts.map +1 -0
- package/dist/commands/packages.js +541 -0
- package/dist/commands/packages.js.map +1 -0
- package/dist/commands/permissions.d.ts +3 -0
- package/dist/commands/permissions.d.ts.map +1 -0
- package/dist/commands/permissions.js +723 -0
- package/dist/commands/permissions.js.map +1 -0
- package/dist/commands/plugins.d.ts +3 -0
- package/dist/commands/plugins.d.ts.map +1 -0
- package/dist/commands/plugins.js +382 -0
- package/dist/commands/plugins.js.map +1 -0
- package/dist/commands/profiles.d.ts +3 -0
- package/dist/commands/profiles.d.ts.map +1 -0
- package/dist/commands/profiles.js +242 -0
- package/dist/commands/profiles.js.map +1 -0
- package/dist/commands/pty.d.ts +20 -0
- package/dist/commands/pty.d.ts.map +1 -0
- package/dist/commands/pty.js +389 -0
- package/dist/commands/pty.js.map +1 -0
- package/dist/commands/pull.d.ts +3 -0
- package/dist/commands/pull.d.ts.map +1 -0
- package/dist/commands/pull.js +448 -0
- package/dist/commands/pull.js.map +1 -0
- package/dist/commands/push.d.ts +3 -0
- package/dist/commands/push.d.ts.map +1 -0
- package/dist/commands/push.js +180 -0
- package/dist/commands/push.js.map +1 -0
- package/dist/commands/refresh-memory.d.ts +9 -0
- package/dist/commands/refresh-memory.d.ts.map +1 -0
- package/dist/commands/refresh-memory.js +45 -0
- package/dist/commands/refresh-memory.js.map +1 -0
- package/dist/commands/resource-view.d.ts +31 -0
- package/dist/commands/resource-view.d.ts.map +1 -0
- package/dist/commands/resource-view.js +183 -0
- package/dist/commands/resource-view.js.map +1 -0
- package/dist/commands/routines.d.ts +3 -0
- package/dist/commands/routines.d.ts.map +1 -0
- package/dist/commands/routines.js +579 -0
- package/dist/commands/routines.js.map +1 -0
- package/dist/commands/rules.d.ts +3 -0
- package/dist/commands/rules.d.ts.map +1 -0
- package/dist/commands/rules.js +488 -0
- package/dist/commands/rules.js.map +1 -0
- package/dist/commands/secrets.d.ts +3 -0
- package/dist/commands/secrets.d.ts.map +1 -0
- package/dist/commands/secrets.js +339 -0
- package/dist/commands/secrets.js.map +1 -0
- package/dist/commands/sessions-picker.d.ts +16 -0
- package/dist/commands/sessions-picker.d.ts.map +1 -0
- package/dist/commands/sessions-picker.js +256 -0
- package/dist/commands/sessions-picker.js.map +1 -0
- package/dist/commands/sessions.d.ts +16 -0
- package/dist/commands/sessions.d.ts.map +1 -0
- package/dist/commands/sessions.js +1077 -0
- package/dist/commands/sessions.js.map +1 -0
- package/dist/commands/skills.d.ts +3 -0
- package/dist/commands/skills.d.ts.map +1 -0
- package/dist/commands/skills.js +716 -0
- package/dist/commands/skills.js.map +1 -0
- package/dist/commands/status.d.ts +3 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +19 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/subagents.d.ts +3 -0
- package/dist/commands/subagents.d.ts.map +1 -0
- package/dist/commands/subagents.js +350 -0
- package/dist/commands/subagents.js.map +1 -0
- package/dist/commands/sync.d.ts +3 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +62 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/commands/teams-picker.d.ts +14 -0
- package/dist/commands/teams-picker.d.ts.map +1 -0
- package/dist/commands/teams-picker.js +278 -0
- package/dist/commands/teams-picker.js.map +1 -0
- package/dist/commands/teams.d.ts +3 -0
- package/dist/commands/teams.d.ts.map +1 -0
- package/dist/commands/teams.js +917 -0
- package/dist/commands/teams.js.map +1 -0
- package/dist/commands/utils.d.ts +39 -0
- package/dist/commands/utils.d.ts.map +1 -0
- package/dist/commands/utils.js +100 -0
- package/dist/commands/utils.js.map +1 -0
- package/dist/commands/versions.d.ts +3 -0
- package/dist/commands/versions.d.ts.map +1 -0
- package/dist/commands/versions.js +700 -0
- package/dist/commands/versions.js.map +1 -0
- package/dist/commands/view.d.ts +8 -0
- package/dist/commands/view.d.ts.map +1 -0
- package/dist/commands/view.js +626 -0
- package/dist/commands/view.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +484 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/__tests__/bugfixes.test.d.ts +2 -0
- package/dist/lib/__tests__/bugfixes.test.d.ts.map +1 -0
- package/dist/lib/__tests__/bugfixes.test.js +192 -0
- package/dist/lib/__tests__/bugfixes.test.js.map +1 -0
- package/dist/lib/__tests__/exec.test.d.ts +2 -0
- package/dist/lib/__tests__/exec.test.d.ts.map +1 -0
- package/dist/lib/__tests__/exec.test.js +446 -0
- package/dist/lib/__tests__/exec.test.js.map +1 -0
- package/dist/lib/__tests__/git-sync.test.d.ts +2 -0
- package/dist/lib/__tests__/git-sync.test.d.ts.map +1 -0
- package/dist/lib/__tests__/git-sync.test.js +138 -0
- package/dist/lib/__tests__/git-sync.test.js.map +1 -0
- package/dist/lib/__tests__/hooks.test.d.ts +2 -0
- package/dist/lib/__tests__/hooks.test.d.ts.map +1 -0
- package/dist/lib/__tests__/hooks.test.js +203 -0
- package/dist/lib/__tests__/hooks.test.js.map +1 -0
- package/dist/lib/__tests__/memory-compile.test.d.ts +2 -0
- package/dist/lib/__tests__/memory-compile.test.d.ts.map +1 -0
- package/dist/lib/__tests__/memory-compile.test.js +95 -0
- package/dist/lib/__tests__/memory-compile.test.js.map +1 -0
- package/dist/lib/__tests__/models.test.d.ts +2 -0
- package/dist/lib/__tests__/models.test.d.ts.map +1 -0
- package/dist/lib/__tests__/models.test.js +239 -0
- package/dist/lib/__tests__/models.test.js.map +1 -0
- package/dist/lib/__tests__/rotate.test.d.ts +2 -0
- package/dist/lib/__tests__/rotate.test.d.ts.map +1 -0
- package/dist/lib/__tests__/rotate.test.js +80 -0
- package/dist/lib/__tests__/rotate.test.js.map +1 -0
- package/dist/lib/__tests__/secrets-bundles.test.d.ts +2 -0
- package/dist/lib/__tests__/secrets-bundles.test.d.ts.map +1 -0
- package/dist/lib/__tests__/secrets-bundles.test.js +104 -0
- package/dist/lib/__tests__/secrets-bundles.test.js.map +1 -0
- package/dist/lib/__tests__/secrets.test.d.ts +2 -0
- package/dist/lib/__tests__/secrets.test.d.ts.map +1 -0
- package/dist/lib/__tests__/secrets.test.js +90 -0
- package/dist/lib/__tests__/secrets.test.js.map +1 -0
- package/dist/lib/__tests__/shims.test.d.ts +2 -0
- package/dist/lib/__tests__/shims.test.d.ts.map +1 -0
- package/dist/lib/__tests__/shims.test.js +39 -0
- package/dist/lib/__tests__/shims.test.js.map +1 -0
- package/dist/lib/__tests__/usage.test.d.ts +2 -0
- package/dist/lib/__tests__/usage.test.d.ts.map +1 -0
- package/dist/lib/__tests__/usage.test.js +220 -0
- package/dist/lib/__tests__/usage.test.js.map +1 -0
- package/dist/lib/__tests__/versions.test.d.ts +2 -0
- package/dist/lib/__tests__/versions.test.d.ts.map +1 -0
- package/dist/lib/__tests__/versions.test.js +63 -0
- package/dist/lib/__tests__/versions.test.js.map +1 -0
- package/dist/lib/agents.d.ts +107 -0
- package/dist/lib/agents.d.ts.map +1 -0
- package/dist/lib/agents.js +1096 -0
- package/dist/lib/agents.js.map +1 -0
- package/dist/lib/artifact-actions.d.ts +22 -0
- package/dist/lib/artifact-actions.d.ts.map +1 -0
- package/dist/lib/artifact-actions.js +55 -0
- package/dist/lib/artifact-actions.js.map +1 -0
- package/dist/lib/cloud/codex.d.ts +19 -0
- package/dist/lib/cloud/codex.d.ts.map +1 -0
- package/dist/lib/cloud/codex.js +210 -0
- package/dist/lib/cloud/codex.js.map +1 -0
- package/dist/lib/cloud/factory.d.ts +26 -0
- package/dist/lib/cloud/factory.d.ts.map +1 -0
- package/dist/lib/cloud/factory.js +37 -0
- package/dist/lib/cloud/factory.js.map +1 -0
- package/dist/lib/cloud/registry.d.ts +6 -0
- package/dist/lib/cloud/registry.d.ts.map +1 -0
- package/dist/lib/cloud/registry.js +56 -0
- package/dist/lib/cloud/registry.js.map +1 -0
- package/dist/lib/cloud/rush.d.ts +15 -0
- package/dist/lib/cloud/rush.d.ts.map +1 -0
- package/dist/lib/cloud/rush.js +185 -0
- package/dist/lib/cloud/rush.js.map +1 -0
- package/dist/lib/cloud/store.d.ts +10 -0
- package/dist/lib/cloud/store.d.ts.map +1 -0
- package/dist/lib/cloud/store.js +96 -0
- package/dist/lib/cloud/store.js.map +1 -0
- package/dist/lib/cloud/stream.d.ts +18 -0
- package/dist/lib/cloud/stream.d.ts.map +1 -0
- package/dist/lib/cloud/stream.js +138 -0
- package/dist/lib/cloud/stream.js.map +1 -0
- package/dist/lib/cloud/types.d.ts +60 -0
- package/dist/lib/cloud/types.d.ts.map +1 -0
- package/dist/lib/cloud/types.js +2 -0
- package/dist/lib/cloud/types.js.map +1 -0
- package/dist/lib/commands.d.ts +121 -0
- package/dist/lib/commands.d.ts.map +1 -0
- package/dist/lib/commands.js +499 -0
- package/dist/lib/commands.js.map +1 -0
- package/dist/lib/convert.d.ts +11 -0
- package/dist/lib/convert.d.ts.map +1 -0
- package/dist/lib/convert.js +45 -0
- package/dist/lib/convert.js.map +1 -0
- package/dist/lib/daemon.d.ts +22 -0
- package/dist/lib/daemon.d.ts.map +1 -0
- package/dist/lib/daemon.js +311 -0
- package/dist/lib/daemon.js.map +1 -0
- package/dist/lib/drive-sync.d.ts +28 -0
- package/dist/lib/drive-sync.d.ts.map +1 -0
- package/dist/lib/drive-sync.js +193 -0
- package/dist/lib/drive-sync.js.map +1 -0
- package/dist/lib/exec.d.ts +85 -0
- package/dist/lib/exec.d.ts.map +1 -0
- package/dist/lib/exec.js +423 -0
- package/dist/lib/exec.js.map +1 -0
- package/dist/lib/factory.d.ts +57 -0
- package/dist/lib/factory.d.ts.map +1 -0
- package/dist/lib/factory.js +110 -0
- package/dist/lib/factory.js.map +1 -0
- package/dist/lib/git.d.ts +146 -0
- package/dist/lib/git.d.ts.map +1 -0
- package/dist/lib/git.js +635 -0
- package/dist/lib/git.js.map +1 -0
- package/dist/lib/help.d.ts +3 -0
- package/dist/lib/help.d.ts.map +1 -0
- package/dist/lib/help.js +63 -0
- package/dist/lib/help.js.map +1 -0
- package/dist/lib/hooks.d.ts +116 -0
- package/dist/lib/hooks.d.ts.map +1 -0
- package/dist/lib/hooks.js +837 -0
- package/dist/lib/hooks.js.map +1 -0
- package/dist/lib/manifest.d.ts +8 -0
- package/dist/lib/manifest.d.ts.map +1 -0
- package/dist/lib/manifest.js +36 -0
- package/dist/lib/manifest.js.map +1 -0
- package/dist/lib/markdown.d.ts +5 -0
- package/dist/lib/markdown.d.ts.map +1 -0
- package/dist/lib/markdown.js +11 -0
- package/dist/lib/markdown.js.map +1 -0
- package/dist/lib/mcp.d.ts +64 -0
- package/dist/lib/mcp.d.ts.map +1 -0
- package/dist/lib/mcp.js +327 -0
- package/dist/lib/mcp.js.map +1 -0
- package/dist/lib/memory-compile.d.ts +56 -0
- package/dist/lib/memory-compile.d.ts.map +1 -0
- package/dist/lib/memory-compile.js +167 -0
- package/dist/lib/memory-compile.js.map +1 -0
- package/dist/lib/memory.d.ts +56 -0
- package/dist/lib/memory.d.ts.map +1 -0
- package/dist/lib/memory.js +267 -0
- package/dist/lib/memory.js.map +1 -0
- package/dist/lib/models.d.ts +91 -0
- package/dist/lib/models.d.ts.map +1 -0
- package/dist/lib/models.js +706 -0
- package/dist/lib/models.js.map +1 -0
- package/dist/lib/permissions.d.ts +204 -0
- package/dist/lib/permissions.d.ts.map +1 -0
- package/dist/lib/permissions.js +1022 -0
- package/dist/lib/permissions.js.map +1 -0
- package/dist/lib/picker.d.ts +17 -0
- package/dist/lib/picker.d.ts.map +1 -0
- package/dist/lib/picker.js +95 -0
- package/dist/lib/picker.js.map +1 -0
- package/dist/lib/plugins.d.ts +73 -0
- package/dist/lib/plugins.d.ts.map +1 -0
- package/dist/lib/plugins.js +549 -0
- package/dist/lib/plugins.js.map +1 -0
- package/dist/lib/profiles-keychain.d.ts +3 -0
- package/dist/lib/profiles-keychain.d.ts.map +1 -0
- package/dist/lib/profiles-keychain.js +10 -0
- package/dist/lib/profiles-keychain.js.map +1 -0
- package/dist/lib/profiles-presets.d.ts +15 -0
- package/dist/lib/profiles-presets.d.ts.map +1 -0
- package/dist/lib/profiles-presets.js +95 -0
- package/dist/lib/profiles-presets.js.map +1 -0
- package/dist/lib/profiles.d.ts +35 -0
- package/dist/lib/profiles.d.ts.map +1 -0
- package/dist/lib/profiles.js +123 -0
- package/dist/lib/profiles.js.map +1 -0
- package/dist/lib/pty-client.d.ts +22 -0
- package/dist/lib/pty-client.d.ts.map +1 -0
- package/dist/lib/pty-client.js +181 -0
- package/dist/lib/pty-client.js.map +1 -0
- package/dist/lib/pty-server.d.ts +16 -0
- package/dist/lib/pty-server.d.ts.map +1 -0
- package/dist/lib/pty-server.js +422 -0
- package/dist/lib/pty-server.js.map +1 -0
- package/dist/lib/registry.d.ts +28 -0
- package/dist/lib/registry.d.ts.map +1 -0
- package/dist/lib/registry.js +203 -0
- package/dist/lib/registry.js.map +1 -0
- package/dist/lib/resources.d.ts +50 -0
- package/dist/lib/resources.d.ts.map +1 -0
- package/dist/lib/resources.js +103 -0
- package/dist/lib/resources.js.map +1 -0
- package/dist/lib/rotate.d.ts +52 -0
- package/dist/lib/rotate.d.ts.map +1 -0
- package/dist/lib/rotate.js +87 -0
- package/dist/lib/rotate.js.map +1 -0
- package/dist/lib/routines.d.ts +70 -0
- package/dist/lib/routines.d.ts.map +1 -0
- package/dist/lib/routines.js +325 -0
- package/dist/lib/routines.js.map +1 -0
- package/dist/lib/runner.d.ts +12 -0
- package/dist/lib/runner.d.ts.map +1 -0
- package/dist/lib/runner.js +311 -0
- package/dist/lib/runner.js.map +1 -0
- package/dist/lib/sandbox.d.ts +10 -0
- package/dist/lib/sandbox.d.ts.map +1 -0
- package/dist/lib/sandbox.js +201 -0
- package/dist/lib/sandbox.js.map +1 -0
- package/dist/lib/scheduler.d.ts +18 -0
- package/dist/lib/scheduler.d.ts.map +1 -0
- package/dist/lib/scheduler.js +69 -0
- package/dist/lib/scheduler.js.map +1 -0
- package/dist/lib/secrets-bundles.d.ts +29 -0
- package/dist/lib/secrets-bundles.d.ts.map +1 -0
- package/dist/lib/secrets-bundles.js +168 -0
- package/dist/lib/secrets-bundles.js.map +1 -0
- package/dist/lib/secrets.d.ts +27 -0
- package/dist/lib/secrets.d.ts.map +1 -0
- package/dist/lib/secrets.js +127 -0
- package/dist/lib/secrets.js.map +1 -0
- package/dist/lib/session/__tests__/db.test.d.ts +2 -0
- package/dist/lib/session/__tests__/db.test.d.ts.map +1 -0
- package/dist/lib/session/__tests__/db.test.js +54 -0
- package/dist/lib/session/__tests__/db.test.js.map +1 -0
- package/dist/lib/session/__tests__/discover.test.d.ts +2 -0
- package/dist/lib/session/__tests__/discover.test.d.ts.map +1 -0
- package/dist/lib/session/__tests__/discover.test.js +63 -0
- package/dist/lib/session/__tests__/discover.test.js.map +1 -0
- package/dist/lib/session/__tests__/prompt.test.d.ts +2 -0
- package/dist/lib/session/__tests__/prompt.test.d.ts.map +1 -0
- package/dist/lib/session/__tests__/prompt.test.js +44 -0
- package/dist/lib/session/__tests__/prompt.test.js.map +1 -0
- package/dist/lib/session/__tests__/render.test.d.ts +2 -0
- package/dist/lib/session/__tests__/render.test.d.ts.map +1 -0
- package/dist/lib/session/__tests__/render.test.js +602 -0
- package/dist/lib/session/__tests__/render.test.js.map +1 -0
- package/dist/lib/session/artifacts.d.ts +5 -0
- package/dist/lib/session/artifacts.d.ts.map +1 -0
- package/dist/lib/session/artifacts.js +75 -0
- package/dist/lib/session/artifacts.js.map +1 -0
- package/dist/lib/session/db.d.ts +118 -0
- package/dist/lib/session/db.d.ts.map +1 -0
- package/dist/lib/session/db.js +576 -0
- package/dist/lib/session/db.js.map +1 -0
- package/dist/lib/session/discover.d.ts +60 -0
- package/dist/lib/session/discover.d.ts.map +1 -0
- package/dist/lib/session/discover.js +1272 -0
- package/dist/lib/session/discover.js.map +1 -0
- package/dist/lib/session/parse.d.ts +23 -0
- package/dist/lib/session/parse.d.ts.map +1 -0
- package/dist/lib/session/parse.js +650 -0
- package/dist/lib/session/parse.js.map +1 -0
- package/dist/lib/session/prompt.d.ts +4 -0
- package/dist/lib/session/prompt.d.ts.map +1 -0
- package/dist/lib/session/prompt.js +64 -0
- package/dist/lib/session/prompt.js.map +1 -0
- package/dist/lib/session/prompt.test.d.ts +2 -0
- package/dist/lib/session/prompt.test.d.ts.map +1 -0
- package/dist/lib/session/prompt.test.js +57 -0
- package/dist/lib/session/prompt.test.js.map +1 -0
- package/dist/lib/session/render.d.ts +90 -0
- package/dist/lib/session/render.d.ts.map +1 -0
- package/dist/lib/session/render.js +778 -0
- package/dist/lib/session/render.js.map +1 -0
- package/dist/lib/session/team-filter.d.ts +26 -0
- package/dist/lib/session/team-filter.d.ts.map +1 -0
- package/dist/lib/session/team-filter.js +66 -0
- package/dist/lib/session/team-filter.js.map +1 -0
- package/dist/lib/session/team-filter.test.d.ts +2 -0
- package/dist/lib/session/team-filter.test.d.ts.map +1 -0
- package/dist/lib/session/team-filter.test.js +157 -0
- package/dist/lib/session/team-filter.test.js.map +1 -0
- package/dist/lib/session/types.d.ts +69 -0
- package/dist/lib/session/types.d.ts.map +1 -0
- package/dist/lib/session/types.js +2 -0
- package/dist/lib/session/types.js.map +1 -0
- package/dist/lib/shims.d.ts +228 -0
- package/dist/lib/shims.d.ts.map +1 -0
- package/dist/lib/shims.js +1170 -0
- package/dist/lib/shims.js.map +1 -0
- package/dist/lib/skills.d.ts +134 -0
- package/dist/lib/skills.d.ts.map +1 -0
- package/dist/lib/skills.js +783 -0
- package/dist/lib/skills.js.map +1 -0
- package/dist/lib/state.d.ts +53 -0
- package/dist/lib/state.d.ts.map +1 -0
- package/dist/lib/state.js +299 -0
- package/dist/lib/state.js.map +1 -0
- package/dist/lib/subagents.d.ts +75 -0
- package/dist/lib/subagents.d.ts.map +1 -0
- package/dist/lib/subagents.js +402 -0
- package/dist/lib/subagents.js.map +1 -0
- package/dist/lib/teams/agents.d.ts +146 -0
- package/dist/lib/teams/agents.d.ts.map +1 -0
- package/dist/lib/teams/agents.js +1072 -0
- package/dist/lib/teams/agents.js.map +1 -0
- package/dist/lib/teams/api.d.ts +77 -0
- package/dist/lib/teams/api.d.ts.map +1 -0
- package/dist/lib/teams/api.js +229 -0
- package/dist/lib/teams/api.js.map +1 -0
- package/dist/lib/teams/cloud.d.ts +11 -0
- package/dist/lib/teams/cloud.d.ts.map +1 -0
- package/dist/lib/teams/cloud.js +169 -0
- package/dist/lib/teams/cloud.js.map +1 -0
- package/dist/lib/teams/debug.d.ts +2 -0
- package/dist/lib/teams/debug.d.ts.map +1 -0
- package/dist/lib/teams/debug.js +6 -0
- package/dist/lib/teams/debug.js.map +1 -0
- package/dist/lib/teams/file_ops.d.ts +6 -0
- package/dist/lib/teams/file_ops.d.ts.map +1 -0
- package/dist/lib/teams/file_ops.js +59 -0
- package/dist/lib/teams/file_ops.js.map +1 -0
- package/dist/lib/teams/parsers.d.ts +5 -0
- package/dist/lib/teams/parsers.d.ts.map +1 -0
- package/dist/lib/teams/parsers.js +826 -0
- package/dist/lib/teams/parsers.js.map +1 -0
- package/dist/lib/teams/persistence.d.ts +28 -0
- package/dist/lib/teams/persistence.d.ts.map +1 -0
- package/dist/lib/teams/persistence.js +289 -0
- package/dist/lib/teams/persistence.js.map +1 -0
- package/dist/lib/teams/ralph.d.ts +8 -0
- package/dist/lib/teams/ralph.d.ts.map +1 -0
- package/dist/lib/teams/ralph.js +59 -0
- package/dist/lib/teams/ralph.js.map +1 -0
- package/dist/lib/teams/registry.d.ts +11 -0
- package/dist/lib/teams/registry.d.ts.map +1 -0
- package/dist/lib/teams/registry.js +56 -0
- package/dist/lib/teams/registry.js.map +1 -0
- package/dist/lib/teams/summarizer.d.ts +58 -0
- package/dist/lib/teams/summarizer.d.ts.map +1 -0
- package/dist/lib/teams/summarizer.js +766 -0
- package/dist/lib/teams/summarizer.js.map +1 -0
- package/dist/lib/template.d.ts +24 -0
- package/dist/lib/template.d.ts.map +1 -0
- package/dist/lib/template.js +57 -0
- package/dist/lib/template.js.map +1 -0
- package/dist/lib/types.d.ts +282 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/types.js +18 -0
- package/dist/lib/types.js.map +1 -0
- package/dist/lib/usage.d.ts +73 -0
- package/dist/lib/usage.d.ts.map +1 -0
- package/dist/lib/usage.js +623 -0
- package/dist/lib/usage.js.map +1 -0
- package/dist/lib/versions.d.ts +248 -0
- package/dist/lib/versions.d.ts.map +1 -0
- package/dist/lib/versions.js +1737 -0
- package/dist/lib/versions.js.map +1 -0
- package/package.json +82 -0
- package/scripts/postinstall.js +72 -0
- package/scripts/rebuild-sqlite.sh +46 -0
|
@@ -0,0 +1,1272 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as os from 'os';
|
|
4
|
+
import * as crypto from 'crypto';
|
|
5
|
+
import * as readline from 'readline';
|
|
6
|
+
import { execSync } from 'child_process';
|
|
7
|
+
import { AGENTS, getCliVersion } from '../agents.js';
|
|
8
|
+
import { getConfigSymlinkVersion } from '../shims.js';
|
|
9
|
+
import { SESSION_AGENTS } from './types.js';
|
|
10
|
+
import { extractSessionTopic } from './prompt.js';
|
|
11
|
+
import { getDB, getScanStampByPath, getScanStampsForPaths, recordScans, syncLabels, upsertSessionsBatch, querySessions, countSessions, ftsSearch, } from './db.js';
|
|
12
|
+
const HOME = os.homedir();
|
|
13
|
+
const AGENTS_DIR = path.join(HOME, '.agents');
|
|
14
|
+
/** How long OpenClaw channel/cron snapshots stay valid before we re-shell-out. */
|
|
15
|
+
const OPENCLAW_TTL_MS = 60_000;
|
|
16
|
+
let cachedOpenClawWorkspaces = null;
|
|
17
|
+
const cachedAgentVersions = new Map();
|
|
18
|
+
/**
|
|
19
|
+
* Discover sessions. Scans only files whose (mtime, size) have changed since
|
|
20
|
+
* the last run; everything else is served from the SQLite cache.
|
|
21
|
+
*/
|
|
22
|
+
export async function discoverSessions(options) {
|
|
23
|
+
// Touch the DB so the schema is ready and connection is cached for this run.
|
|
24
|
+
getDB();
|
|
25
|
+
const agents = options?.agent ? [options.agent] : SESSION_AGENTS;
|
|
26
|
+
const onProgress = options?.onProgress;
|
|
27
|
+
// Incrementally re-scan changed files across all selected agents in parallel.
|
|
28
|
+
await Promise.all(agents.map(agent => {
|
|
29
|
+
switch (agent) {
|
|
30
|
+
case 'claude': return scanClaudeIncremental(onProgress);
|
|
31
|
+
case 'codex': return scanCodexIncremental(onProgress);
|
|
32
|
+
case 'gemini': return scanGeminiIncremental(onProgress);
|
|
33
|
+
case 'opencode': return scanOpenCodeIncremental();
|
|
34
|
+
case 'openclaw': return scanOpenClawIncremental();
|
|
35
|
+
}
|
|
36
|
+
}));
|
|
37
|
+
const sessions = querySessions(buildQueryOptions(options, agents, { includeLimit: true }));
|
|
38
|
+
return sessions;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Count sessions in scope without running an incremental scan. Assumes the DB
|
|
42
|
+
* is already fresh (typically true because `discoverSessions` ran first this
|
|
43
|
+
* turn). Uses the exact same filter shape as the discover query.
|
|
44
|
+
*/
|
|
45
|
+
export function countSessionsInScope(options) {
|
|
46
|
+
const agents = options.agent ? [options.agent] : SESSION_AGENTS;
|
|
47
|
+
return countSessions(buildQueryOptions(options, agents, { includeLimit: false }));
|
|
48
|
+
}
|
|
49
|
+
function buildQueryOptions(options, agents, opts) {
|
|
50
|
+
const projectQuery = options?.project?.trim();
|
|
51
|
+
const sinceMs = options?.since ? parseTimeFilter(options.since) : undefined;
|
|
52
|
+
const untilMs = options?.until ? new Date(options.until).getTime() : undefined;
|
|
53
|
+
let cwdFilter;
|
|
54
|
+
let cwdPrefixFilter;
|
|
55
|
+
if (options?.cwdPrefix) {
|
|
56
|
+
cwdPrefixFilter = normalizeCwd(options.cwdPrefix);
|
|
57
|
+
}
|
|
58
|
+
else if (!options?.all && !projectQuery) {
|
|
59
|
+
cwdFilter = normalizeCwd(options?.cwd || process.cwd());
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
agent: options?.agent,
|
|
63
|
+
agents: options?.agent ? undefined : agents,
|
|
64
|
+
version: options?.version,
|
|
65
|
+
cwd: cwdFilter,
|
|
66
|
+
cwdPrefix: cwdPrefixFilter,
|
|
67
|
+
project: projectQuery,
|
|
68
|
+
sinceMs,
|
|
69
|
+
untilMs: Number.isFinite(untilMs) ? untilMs : undefined,
|
|
70
|
+
limit: opts.includeLimit ? (options?.limit ?? 50) : undefined,
|
|
71
|
+
excludeTeamOrigin: options?.excludeTeamOrigin,
|
|
72
|
+
onlyTeamOrigin: options?.onlyTeamOrigin,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function normalizeCwd(cwd) {
|
|
76
|
+
if (!cwd)
|
|
77
|
+
return '';
|
|
78
|
+
const resolved = path.resolve(cwd);
|
|
79
|
+
return safeRealpathSync(resolved) || resolved;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Resolve a session by full or short ID. Accepts a pre-loaded session list
|
|
83
|
+
* (fast path from discoverSessions) and falls back to a DB lookup for the
|
|
84
|
+
* "I only know the id" case.
|
|
85
|
+
*/
|
|
86
|
+
export function resolveSessionById(sessions, idQuery) {
|
|
87
|
+
const query = idQuery.toLowerCase();
|
|
88
|
+
const exact = sessions.filter(s => s.id.toLowerCase() === query || s.shortId.toLowerCase() === query);
|
|
89
|
+
if (exact.length > 0)
|
|
90
|
+
return exact;
|
|
91
|
+
return sessions.filter(s => s.id.toLowerCase().startsWith(query) || s.shortId.toLowerCase().startsWith(query));
|
|
92
|
+
}
|
|
93
|
+
// ---------------------------------------------------------------------------
|
|
94
|
+
// Content-index search (FTS5-backed)
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
/**
|
|
97
|
+
* Run an FTS5 search over the DB and intersect with the given session list,
|
|
98
|
+
* preserving the existing SessionMeta[] contract so sessions.ts is unchanged.
|
|
99
|
+
*/
|
|
100
|
+
export function searchContentIndex(sessions, query) {
|
|
101
|
+
if (!query.trim())
|
|
102
|
+
return new Map();
|
|
103
|
+
const hits = ftsSearch(query);
|
|
104
|
+
if (hits.length === 0)
|
|
105
|
+
return new Map();
|
|
106
|
+
const byId = new Map(sessions.map(s => [s.id, s]));
|
|
107
|
+
const result = new Map();
|
|
108
|
+
for (const hit of hits) {
|
|
109
|
+
const session = byId.get(hit.sessionId);
|
|
110
|
+
if (!session)
|
|
111
|
+
continue;
|
|
112
|
+
result.set(hit.sessionId, {
|
|
113
|
+
...session,
|
|
114
|
+
_matchedTerms: hit.matchedTerms,
|
|
115
|
+
_bm25Score: hit.score,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
return result;
|
|
119
|
+
}
|
|
120
|
+
// ---------------------------------------------------------------------------
|
|
121
|
+
// Incremental scan orchestration
|
|
122
|
+
// ---------------------------------------------------------------------------
|
|
123
|
+
/**
|
|
124
|
+
* For a list of files, stat each, compare to the DB ledger, and return only
|
|
125
|
+
* the ones that need rescanning. One bulk DB query for the whole list.
|
|
126
|
+
*/
|
|
127
|
+
function filterChangedFiles(filePaths) {
|
|
128
|
+
const ledger = getScanStampsForPaths(filePaths);
|
|
129
|
+
const out = [];
|
|
130
|
+
for (const filePath of filePaths) {
|
|
131
|
+
const stat = safeStatSync(filePath);
|
|
132
|
+
if (!stat)
|
|
133
|
+
continue;
|
|
134
|
+
const scan = {
|
|
135
|
+
fileMtimeMs: Math.floor(stat.mtimeMs),
|
|
136
|
+
fileSize: stat.size,
|
|
137
|
+
};
|
|
138
|
+
const prev = ledger.get(filePath);
|
|
139
|
+
if (prev && prev.fileMtimeMs === scan.fileMtimeMs && prev.fileSize === scan.fileSize) {
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
out.push({ filePath, scan });
|
|
143
|
+
}
|
|
144
|
+
return out;
|
|
145
|
+
}
|
|
146
|
+
// ---------------------------------------------------------------------------
|
|
147
|
+
// Multi-version directory scanning
|
|
148
|
+
// ---------------------------------------------------------------------------
|
|
149
|
+
/**
|
|
150
|
+
* Collect all directories to scan for an agent's sessions. Deduplicates by
|
|
151
|
+
* realpath to avoid double-counting symlinked version homes.
|
|
152
|
+
*/
|
|
153
|
+
export function getAgentSessionDirs(agent, subdir) {
|
|
154
|
+
const resolved = new Set();
|
|
155
|
+
const dirs = [];
|
|
156
|
+
function addDir(dir) {
|
|
157
|
+
if (!fs.existsSync(dir))
|
|
158
|
+
return;
|
|
159
|
+
const real = safeRealpathSync(dir);
|
|
160
|
+
const key = real || dir;
|
|
161
|
+
if (resolved.has(key))
|
|
162
|
+
return;
|
|
163
|
+
resolved.add(key);
|
|
164
|
+
dirs.push(dir);
|
|
165
|
+
}
|
|
166
|
+
addDir(path.join(HOME, `.${agent}`, subdir));
|
|
167
|
+
const versionsBase = path.join(AGENTS_DIR, 'versions', agent);
|
|
168
|
+
if (fs.existsSync(versionsBase)) {
|
|
169
|
+
try {
|
|
170
|
+
for (const version of fs.readdirSync(versionsBase)) {
|
|
171
|
+
addDir(path.join(versionsBase, version, 'home', `.${agent}`, subdir));
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
catch { /* dir unreadable */ }
|
|
175
|
+
}
|
|
176
|
+
const backupsBase = path.join(AGENTS_DIR, 'backups', agent);
|
|
177
|
+
if (fs.existsSync(backupsBase)) {
|
|
178
|
+
try {
|
|
179
|
+
for (const ts of fs.readdirSync(backupsBase)) {
|
|
180
|
+
addDir(path.join(backupsBase, ts, subdir));
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
catch { /* dir unreadable */ }
|
|
184
|
+
}
|
|
185
|
+
return dirs;
|
|
186
|
+
}
|
|
187
|
+
// ---------------------------------------------------------------------------
|
|
188
|
+
// Claude account info
|
|
189
|
+
// ---------------------------------------------------------------------------
|
|
190
|
+
let cachedClaudeAccount;
|
|
191
|
+
function getClaudeAccount() {
|
|
192
|
+
if (cachedClaudeAccount !== undefined)
|
|
193
|
+
return cachedClaudeAccount || undefined;
|
|
194
|
+
// Claude's active config lives at $CLAUDE_CONFIG_DIR/.claude.json; for our shim
|
|
195
|
+
// that's <version>/home/.claude/.claude.json. The home-level .claude.json is a
|
|
196
|
+
// legacy path used when Claude runs without CLAUDE_CONFIG_DIR set.
|
|
197
|
+
const candidates = [
|
|
198
|
+
path.join(HOME, '.claude', '.claude.json'),
|
|
199
|
+
path.join(HOME, '.claude.json'),
|
|
200
|
+
];
|
|
201
|
+
const versionsBase = path.join(AGENTS_DIR, 'versions', 'claude');
|
|
202
|
+
if (fs.existsSync(versionsBase)) {
|
|
203
|
+
try {
|
|
204
|
+
for (const version of fs.readdirSync(versionsBase)) {
|
|
205
|
+
candidates.push(path.join(versionsBase, version, 'home', '.claude', '.claude.json'));
|
|
206
|
+
candidates.push(path.join(versionsBase, version, 'home', '.claude.json'));
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
catch { /* versions dir unreadable */ }
|
|
210
|
+
}
|
|
211
|
+
for (const candidate of candidates) {
|
|
212
|
+
try {
|
|
213
|
+
if (!fs.existsSync(candidate))
|
|
214
|
+
continue;
|
|
215
|
+
const data = JSON.parse(fs.readFileSync(candidate, 'utf-8'));
|
|
216
|
+
const name = data.oauthAccount?.emailAddress || data.oauthAccount?.displayName;
|
|
217
|
+
if (name) {
|
|
218
|
+
cachedClaudeAccount = name;
|
|
219
|
+
return name;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
catch { /* auth file unreadable or malformed */ }
|
|
223
|
+
}
|
|
224
|
+
cachedClaudeAccount = '';
|
|
225
|
+
return undefined;
|
|
226
|
+
}
|
|
227
|
+
// ---------------------------------------------------------------------------
|
|
228
|
+
// Claude
|
|
229
|
+
// ---------------------------------------------------------------------------
|
|
230
|
+
/**
|
|
231
|
+
* Build a map of Claude sessionId -> user-given label from ~/.claude/sessions/*.json.
|
|
232
|
+
* Each JSON has shape { pid, sessionId, cwd, startedAt, name?, ... }. The
|
|
233
|
+
* `name` field only exists if the user ran /rename in that session.
|
|
234
|
+
* For sessionId collisions (re-resume of the same session), prefer the most
|
|
235
|
+
* recent startedAt.
|
|
236
|
+
*/
|
|
237
|
+
function buildClaudeLabelMap() {
|
|
238
|
+
const map = new Map();
|
|
239
|
+
const dir = path.join(HOME, '.claude', 'sessions');
|
|
240
|
+
if (!fs.existsSync(dir))
|
|
241
|
+
return new Map();
|
|
242
|
+
let files;
|
|
243
|
+
try {
|
|
244
|
+
files = fs.readdirSync(dir).filter(f => f.endsWith('.json'));
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
return new Map();
|
|
248
|
+
}
|
|
249
|
+
for (const f of files) {
|
|
250
|
+
try {
|
|
251
|
+
const data = JSON.parse(fs.readFileSync(path.join(dir, f), 'utf-8'));
|
|
252
|
+
if (typeof data.sessionId !== 'string')
|
|
253
|
+
continue;
|
|
254
|
+
const name = typeof data.name === 'string' && data.name.trim() ? data.name.trim() : null;
|
|
255
|
+
const startedAt = typeof data.startedAt === 'number' ? data.startedAt : 0;
|
|
256
|
+
const existing = map.get(data.sessionId);
|
|
257
|
+
if (!existing || startedAt > existing.startedAt) {
|
|
258
|
+
map.set(data.sessionId, { label: name, startedAt });
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
catch { /* unreadable session metadata file */ }
|
|
262
|
+
}
|
|
263
|
+
const out = new Map();
|
|
264
|
+
for (const [sid, { label }] of map)
|
|
265
|
+
out.set(sid, label);
|
|
266
|
+
return out;
|
|
267
|
+
}
|
|
268
|
+
async function scanClaudeIncremental(onProgress) {
|
|
269
|
+
const account = getClaudeAccount();
|
|
270
|
+
const labelMap = buildClaudeLabelMap();
|
|
271
|
+
const filePaths = [];
|
|
272
|
+
const seen = new Set();
|
|
273
|
+
for (const projectsDir of getAgentSessionDirs('claude', 'projects')) {
|
|
274
|
+
let projectDirs;
|
|
275
|
+
try {
|
|
276
|
+
projectDirs = fs.readdirSync(projectsDir);
|
|
277
|
+
}
|
|
278
|
+
catch {
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
for (const dirName of projectDirs) {
|
|
282
|
+
const dirPath = path.join(projectsDir, dirName);
|
|
283
|
+
const stat = safeStatSync(dirPath);
|
|
284
|
+
if (!stat?.isDirectory())
|
|
285
|
+
continue;
|
|
286
|
+
let files;
|
|
287
|
+
try {
|
|
288
|
+
files = fs.readdirSync(dirPath).filter(f => f.endsWith('.jsonl'));
|
|
289
|
+
}
|
|
290
|
+
catch {
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
for (const file of files) {
|
|
294
|
+
const sessionId = file.replace('.jsonl', '');
|
|
295
|
+
if (seen.has(sessionId))
|
|
296
|
+
continue;
|
|
297
|
+
seen.add(sessionId);
|
|
298
|
+
filePaths.push(path.join(dirPath, file));
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
const changed = filterChangedFiles(filePaths);
|
|
303
|
+
if (changed.length > 0) {
|
|
304
|
+
onProgress?.({ agent: 'claude', parsed: 0, total: changed.length });
|
|
305
|
+
const entries = [];
|
|
306
|
+
const touched = [];
|
|
307
|
+
let parsed = 0;
|
|
308
|
+
for (const { filePath, scan } of changed) {
|
|
309
|
+
try {
|
|
310
|
+
const sessionId = path.basename(filePath).replace('.jsonl', '');
|
|
311
|
+
const label = labelMap.get(sessionId) ?? undefined;
|
|
312
|
+
const result = await readClaudeMeta(filePath, sessionId, account, label);
|
|
313
|
+
if (result) {
|
|
314
|
+
entries.push({ meta: result.meta, content: result.content, scan });
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
touched.push({ filePath, scan });
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
catch {
|
|
321
|
+
touched.push({ filePath, scan });
|
|
322
|
+
}
|
|
323
|
+
parsed++;
|
|
324
|
+
onProgress?.({ agent: 'claude', parsed, total: changed.length });
|
|
325
|
+
}
|
|
326
|
+
upsertSessionsBatch(entries);
|
|
327
|
+
recordScans(touched);
|
|
328
|
+
}
|
|
329
|
+
// Pick up /rename changes on sessions whose JSONL didn't change.
|
|
330
|
+
// Only bother for sessions we actually have a Claude row for.
|
|
331
|
+
if (labelMap.size > 0)
|
|
332
|
+
syncLabels(labelMap);
|
|
333
|
+
}
|
|
334
|
+
async function readClaudeMeta(filePath, sessionId, account, label) {
|
|
335
|
+
const scan = await scanClaudeSession(filePath);
|
|
336
|
+
const isTeamOrigin = scan.entrypoint === 'sdk-cli';
|
|
337
|
+
let meta;
|
|
338
|
+
if (scan.timestamp) {
|
|
339
|
+
const cwd = normalizeCwd(scan.cwd || '');
|
|
340
|
+
meta = {
|
|
341
|
+
id: sessionId,
|
|
342
|
+
shortId: sessionId.slice(0, 8),
|
|
343
|
+
agent: 'claude',
|
|
344
|
+
timestamp: scan.timestamp,
|
|
345
|
+
project: cwd ? path.basename(cwd) : undefined,
|
|
346
|
+
cwd,
|
|
347
|
+
filePath,
|
|
348
|
+
gitBranch: scan.gitBranch,
|
|
349
|
+
version: scan.version,
|
|
350
|
+
account,
|
|
351
|
+
topic: scan.topic,
|
|
352
|
+
label,
|
|
353
|
+
messageCount: scan.messageCount,
|
|
354
|
+
tokenCount: scan.tokenCount,
|
|
355
|
+
isTeamOrigin,
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
const stat = safeStatSync(filePath);
|
|
360
|
+
meta = {
|
|
361
|
+
id: sessionId,
|
|
362
|
+
shortId: sessionId.slice(0, 8),
|
|
363
|
+
agent: 'claude',
|
|
364
|
+
timestamp: stat ? stat.mtime.toISOString() : new Date().toISOString(),
|
|
365
|
+
filePath,
|
|
366
|
+
account,
|
|
367
|
+
label,
|
|
368
|
+
messageCount: scan.messageCount,
|
|
369
|
+
tokenCount: scan.tokenCount,
|
|
370
|
+
topic: scan.topic,
|
|
371
|
+
isTeamOrigin,
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
return { meta, content: scan.contentText || '' };
|
|
375
|
+
}
|
|
376
|
+
// ---------------------------------------------------------------------------
|
|
377
|
+
// Codex account info
|
|
378
|
+
// ---------------------------------------------------------------------------
|
|
379
|
+
let cachedCodexAccount;
|
|
380
|
+
function getCodexAccount() {
|
|
381
|
+
if (cachedCodexAccount !== undefined)
|
|
382
|
+
return cachedCodexAccount || undefined;
|
|
383
|
+
const candidates = [path.join(HOME, '.codex', 'auth.json')];
|
|
384
|
+
const versionsBase = path.join(AGENTS_DIR, 'versions', 'codex');
|
|
385
|
+
if (fs.existsSync(versionsBase)) {
|
|
386
|
+
try {
|
|
387
|
+
for (const version of fs.readdirSync(versionsBase)) {
|
|
388
|
+
candidates.push(path.join(versionsBase, version, 'home', '.codex', 'auth.json'));
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
catch { /* versions dir unreadable */ }
|
|
392
|
+
}
|
|
393
|
+
for (const candidate of candidates) {
|
|
394
|
+
try {
|
|
395
|
+
if (!fs.existsSync(candidate))
|
|
396
|
+
continue;
|
|
397
|
+
const data = JSON.parse(fs.readFileSync(candidate, 'utf-8'));
|
|
398
|
+
const idToken = data.tokens?.id_token;
|
|
399
|
+
if (idToken) {
|
|
400
|
+
const parts = idToken.split('.');
|
|
401
|
+
if (parts.length >= 2) {
|
|
402
|
+
const payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf-8'));
|
|
403
|
+
if (payload.email) {
|
|
404
|
+
cachedCodexAccount = payload.email;
|
|
405
|
+
return payload.email;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
catch { /* auth file or JWT malformed */ }
|
|
411
|
+
}
|
|
412
|
+
cachedCodexAccount = '';
|
|
413
|
+
return undefined;
|
|
414
|
+
}
|
|
415
|
+
// ---------------------------------------------------------------------------
|
|
416
|
+
// Codex
|
|
417
|
+
// ---------------------------------------------------------------------------
|
|
418
|
+
async function scanCodexIncremental(onProgress) {
|
|
419
|
+
const account = getCodexAccount();
|
|
420
|
+
const currentVersion = await getCurrentAgentVersion('codex');
|
|
421
|
+
const filePaths = [];
|
|
422
|
+
for (const sessionsDir of getAgentSessionDirs('codex', 'sessions')) {
|
|
423
|
+
// High limit: we only stat files here, parsing is gated by ledger match.
|
|
424
|
+
for (const fp of walkForFiles(sessionsDir, '.jsonl', 100_000)) {
|
|
425
|
+
filePaths.push(fp);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
const changed = filterChangedFiles(filePaths);
|
|
429
|
+
if (changed.length === 0)
|
|
430
|
+
return;
|
|
431
|
+
onProgress?.({ agent: 'codex', parsed: 0, total: changed.length });
|
|
432
|
+
const entries = [];
|
|
433
|
+
const touched = [];
|
|
434
|
+
const seen = new Set();
|
|
435
|
+
let parsed = 0;
|
|
436
|
+
for (const { filePath, scan } of changed) {
|
|
437
|
+
try {
|
|
438
|
+
const result = await readCodexMeta(filePath, account, currentVersion);
|
|
439
|
+
if (result && !seen.has(result.meta.id)) {
|
|
440
|
+
seen.add(result.meta.id);
|
|
441
|
+
entries.push({ meta: result.meta, content: result.content, scan });
|
|
442
|
+
}
|
|
443
|
+
else {
|
|
444
|
+
touched.push({ filePath, scan });
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
catch {
|
|
448
|
+
touched.push({ filePath, scan });
|
|
449
|
+
}
|
|
450
|
+
parsed++;
|
|
451
|
+
onProgress?.({ agent: 'codex', parsed, total: changed.length });
|
|
452
|
+
}
|
|
453
|
+
upsertSessionsBatch(entries);
|
|
454
|
+
recordScans(touched);
|
|
455
|
+
}
|
|
456
|
+
async function readCodexMeta(filePath, account, currentVersion) {
|
|
457
|
+
const scan = await scanCodexSession(filePath);
|
|
458
|
+
const sessionId = scan.sessionId || '';
|
|
459
|
+
if (!sessionId)
|
|
460
|
+
return null;
|
|
461
|
+
const cwd = normalizeCwd(scan.cwd || '');
|
|
462
|
+
const meta = {
|
|
463
|
+
id: sessionId,
|
|
464
|
+
shortId: sessionId.slice(0, 8),
|
|
465
|
+
agent: 'codex',
|
|
466
|
+
timestamp: scan.timestamp || new Date().toISOString(),
|
|
467
|
+
project: cwd ? path.basename(cwd) : undefined,
|
|
468
|
+
cwd,
|
|
469
|
+
filePath,
|
|
470
|
+
gitBranch: scan.gitBranch,
|
|
471
|
+
version: resolveSessionVersion('codex', filePath, scan.version, currentVersion),
|
|
472
|
+
topic: scan.topic,
|
|
473
|
+
messageCount: scan.messageCount,
|
|
474
|
+
tokenCount: scan.tokenCount,
|
|
475
|
+
account,
|
|
476
|
+
};
|
|
477
|
+
return { meta, content: scan.contentText || '' };
|
|
478
|
+
}
|
|
479
|
+
// ---------------------------------------------------------------------------
|
|
480
|
+
// Gemini
|
|
481
|
+
// ---------------------------------------------------------------------------
|
|
482
|
+
async function scanGeminiIncremental(onProgress) {
|
|
483
|
+
const currentVersion = await getCurrentAgentVersion('gemini');
|
|
484
|
+
const projectMap = buildGeminiProjectMap();
|
|
485
|
+
const filePaths = [];
|
|
486
|
+
for (const tmpDir of getAgentSessionDirs('gemini', 'tmp')) {
|
|
487
|
+
let hashDirs;
|
|
488
|
+
try {
|
|
489
|
+
hashDirs = fs.readdirSync(tmpDir);
|
|
490
|
+
}
|
|
491
|
+
catch {
|
|
492
|
+
continue;
|
|
493
|
+
}
|
|
494
|
+
for (const hashDir of hashDirs) {
|
|
495
|
+
const chatsDir = path.join(tmpDir, hashDir, 'chats');
|
|
496
|
+
if (!fs.existsSync(chatsDir))
|
|
497
|
+
continue;
|
|
498
|
+
let chatFiles;
|
|
499
|
+
try {
|
|
500
|
+
chatFiles = fs.readdirSync(chatsDir).filter(f => f.endsWith('.json'));
|
|
501
|
+
}
|
|
502
|
+
catch {
|
|
503
|
+
continue;
|
|
504
|
+
}
|
|
505
|
+
for (const file of chatFiles) {
|
|
506
|
+
filePaths.push({ filePath: path.join(chatsDir, file), hashDir });
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
const changedPaths = filterChangedFiles(filePaths.map(f => f.filePath));
|
|
511
|
+
const changedByPath = new Map(changedPaths.map(c => [c.filePath, c.scan]));
|
|
512
|
+
if (changedByPath.size === 0)
|
|
513
|
+
return;
|
|
514
|
+
onProgress?.({ agent: 'gemini', parsed: 0, total: changedByPath.size });
|
|
515
|
+
const entries = [];
|
|
516
|
+
const touched = [];
|
|
517
|
+
const seen = new Set();
|
|
518
|
+
let parsed = 0;
|
|
519
|
+
for (const { filePath, hashDir } of filePaths) {
|
|
520
|
+
const scan = changedByPath.get(filePath);
|
|
521
|
+
if (!scan)
|
|
522
|
+
continue;
|
|
523
|
+
try {
|
|
524
|
+
const result = readGeminiMeta(filePath, hashDir, projectMap, currentVersion);
|
|
525
|
+
if (result && !seen.has(result.meta.id)) {
|
|
526
|
+
seen.add(result.meta.id);
|
|
527
|
+
entries.push({ meta: result.meta, content: result.content, scan });
|
|
528
|
+
}
|
|
529
|
+
else {
|
|
530
|
+
// Gemini file without a sessionId — record scan so we don't re-parse it next run.
|
|
531
|
+
touched.push({ filePath, scan });
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
catch {
|
|
535
|
+
touched.push({ filePath, scan });
|
|
536
|
+
}
|
|
537
|
+
parsed++;
|
|
538
|
+
onProgress?.({ agent: 'gemini', parsed, total: changedByPath.size });
|
|
539
|
+
}
|
|
540
|
+
upsertSessionsBatch(entries);
|
|
541
|
+
recordScans(touched);
|
|
542
|
+
}
|
|
543
|
+
function readGeminiMeta(filePath, hashDir, projectMap, currentVersion) {
|
|
544
|
+
let session;
|
|
545
|
+
try {
|
|
546
|
+
session = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
547
|
+
}
|
|
548
|
+
catch {
|
|
549
|
+
return null;
|
|
550
|
+
}
|
|
551
|
+
const sessionId = typeof session.sessionId === 'string' ? session.sessionId : '';
|
|
552
|
+
const startTime = typeof session.startTime === 'string' ? session.startTime : '';
|
|
553
|
+
const projectHash = typeof session.projectHash === 'string' ? session.projectHash : '';
|
|
554
|
+
const embeddedVersion = typeof session.version === 'string'
|
|
555
|
+
? session.version
|
|
556
|
+
: typeof session.cliVersion === 'string'
|
|
557
|
+
? session.cliVersion
|
|
558
|
+
: undefined;
|
|
559
|
+
if (!sessionId)
|
|
560
|
+
return null;
|
|
561
|
+
const projectInfo = projectMap.get(projectHash || hashDir);
|
|
562
|
+
const project = projectInfo?.name || hashDir.slice(0, 12);
|
|
563
|
+
const cwd = projectInfo?.path ? normalizeCwd(projectInfo.path) : undefined;
|
|
564
|
+
const stat = safeStatSync(filePath);
|
|
565
|
+
const messages = Array.isArray(session.messages) ? session.messages : [];
|
|
566
|
+
let topic;
|
|
567
|
+
let messageCount = 0;
|
|
568
|
+
let tokenCount = 0;
|
|
569
|
+
let sawTokenCount = false;
|
|
570
|
+
const userTexts = [];
|
|
571
|
+
for (const message of messages) {
|
|
572
|
+
if (message.type === 'user') {
|
|
573
|
+
const text = extractGeminiMessageText(message.content);
|
|
574
|
+
if (text) {
|
|
575
|
+
messageCount++;
|
|
576
|
+
userTexts.push(text);
|
|
577
|
+
if (!topic)
|
|
578
|
+
topic = extractSessionTopic(text);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
else if (message.type === 'gemini') {
|
|
582
|
+
if (extractGeminiMessageText(message.content)) {
|
|
583
|
+
messageCount++;
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
const total = getGeminiTokenCount(message.tokens);
|
|
587
|
+
if (total !== null) {
|
|
588
|
+
tokenCount += total;
|
|
589
|
+
sawTokenCount = true;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
const meta = {
|
|
593
|
+
id: sessionId,
|
|
594
|
+
shortId: sessionId.slice(0, 8),
|
|
595
|
+
agent: 'gemini',
|
|
596
|
+
timestamp: startTime || (stat ? stat.mtime.toISOString() : new Date().toISOString()),
|
|
597
|
+
project,
|
|
598
|
+
cwd,
|
|
599
|
+
filePath,
|
|
600
|
+
version: resolveSessionVersion('gemini', filePath, embeddedVersion, currentVersion),
|
|
601
|
+
topic,
|
|
602
|
+
messageCount,
|
|
603
|
+
tokenCount: sawTokenCount ? tokenCount : undefined,
|
|
604
|
+
};
|
|
605
|
+
return { meta, content: userTexts.join('\n') };
|
|
606
|
+
}
|
|
607
|
+
function buildGeminiProjectMap() {
|
|
608
|
+
const map = new Map();
|
|
609
|
+
const projectsJsonPath = path.join(HOME, '.gemini', 'projects.json');
|
|
610
|
+
if (fs.existsSync(projectsJsonPath)) {
|
|
611
|
+
try {
|
|
612
|
+
const data = JSON.parse(fs.readFileSync(projectsJsonPath, 'utf-8'));
|
|
613
|
+
const projects = data.projects;
|
|
614
|
+
if (typeof projects === 'object' && projects !== null) {
|
|
615
|
+
if (Array.isArray(projects)) {
|
|
616
|
+
for (const p of projects) {
|
|
617
|
+
if (typeof p === 'string') {
|
|
618
|
+
const hash = sha256(p);
|
|
619
|
+
map.set(hash, { name: path.basename(p), path: p });
|
|
620
|
+
map.set(p, { name: path.basename(p), path: p });
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
else {
|
|
625
|
+
for (const [p, name] of Object.entries(projects)) {
|
|
626
|
+
const hash = sha256(p);
|
|
627
|
+
map.set(hash, { name: String(name), path: p });
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
catch { /* projects.json missing or malformed */ }
|
|
633
|
+
}
|
|
634
|
+
const historyDir = path.join(HOME, '.gemini', 'history');
|
|
635
|
+
if (fs.existsSync(historyDir)) {
|
|
636
|
+
try {
|
|
637
|
+
for (const name of fs.readdirSync(historyDir)) {
|
|
638
|
+
const rootFile = path.join(historyDir, name, '.project_root');
|
|
639
|
+
if (fs.existsSync(rootFile)) {
|
|
640
|
+
try {
|
|
641
|
+
const projectPath = fs.readFileSync(rootFile, 'utf-8').trim();
|
|
642
|
+
if (projectPath) {
|
|
643
|
+
const hash = sha256(projectPath);
|
|
644
|
+
map.set(hash, { name, path: projectPath });
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
catch { /* history entry unreadable */ }
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
catch { /* history entry unreadable */ }
|
|
652
|
+
}
|
|
653
|
+
return map;
|
|
654
|
+
}
|
|
655
|
+
// ---------------------------------------------------------------------------
|
|
656
|
+
// OpenCode
|
|
657
|
+
// ---------------------------------------------------------------------------
|
|
658
|
+
const OPENCODE_DB = path.join(HOME, '.local', 'share', 'opencode', 'opencode.db');
|
|
659
|
+
let cachedOpenCodeAccount;
|
|
660
|
+
function getOpenCodeAccount() {
|
|
661
|
+
if (cachedOpenCodeAccount !== undefined)
|
|
662
|
+
return cachedOpenCodeAccount || undefined;
|
|
663
|
+
try {
|
|
664
|
+
if (fs.existsSync(OPENCODE_DB)) {
|
|
665
|
+
const out = execSync(`sqlite3 "${OPENCODE_DB}" "SELECT email FROM control_account WHERE active=1 LIMIT 1;"`, { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
|
|
666
|
+
if (out) {
|
|
667
|
+
cachedOpenCodeAccount = out;
|
|
668
|
+
return out;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
catch { /* sqlite3 unavailable or DB locked */ }
|
|
673
|
+
cachedOpenCodeAccount = '';
|
|
674
|
+
return undefined;
|
|
675
|
+
}
|
|
676
|
+
async function scanOpenCodeIncremental() {
|
|
677
|
+
if (!fs.existsSync(OPENCODE_DB))
|
|
678
|
+
return;
|
|
679
|
+
const stat = safeStatSync(OPENCODE_DB);
|
|
680
|
+
if (!stat)
|
|
681
|
+
return;
|
|
682
|
+
// OpenCode is one big DB; we use its mtime/size as the ledger for the
|
|
683
|
+
// entire fleet of OpenCode sessions.
|
|
684
|
+
const currentScan = {
|
|
685
|
+
fileMtimeMs: Math.floor(stat.mtimeMs),
|
|
686
|
+
fileSize: stat.size,
|
|
687
|
+
};
|
|
688
|
+
const prev = getScanStampByPath(OPENCODE_DB);
|
|
689
|
+
if (prev && prev.fileMtimeMs === currentScan.fileMtimeMs && prev.fileSize === currentScan.fileSize) {
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
const account = getOpenCodeAccount();
|
|
693
|
+
const currentVersion = await getCurrentAgentVersion('opencode');
|
|
694
|
+
try {
|
|
695
|
+
const query = `
|
|
696
|
+
SELECT
|
|
697
|
+
s.id,
|
|
698
|
+
s.title,
|
|
699
|
+
s.directory,
|
|
700
|
+
s.version,
|
|
701
|
+
s.time_created,
|
|
702
|
+
COALESCE(stats.message_count, 0),
|
|
703
|
+
stats.token_count,
|
|
704
|
+
COALESCE(stats.has_token_data, 0)
|
|
705
|
+
FROM session s
|
|
706
|
+
LEFT JOIN (
|
|
707
|
+
SELECT
|
|
708
|
+
session_id,
|
|
709
|
+
COUNT(*) AS message_count,
|
|
710
|
+
SUM(
|
|
711
|
+
COALESCE(json_extract(data, '$.tokens.input'), 0) +
|
|
712
|
+
COALESCE(json_extract(data, '$.tokens.output'), 0) +
|
|
713
|
+
COALESCE(json_extract(data, '$.tokens.reasoning'), 0) +
|
|
714
|
+
COALESCE(json_extract(data, '$.tokens.cache.read'), 0) +
|
|
715
|
+
COALESCE(json_extract(data, '$.tokens.cache.write'), 0)
|
|
716
|
+
) AS token_count,
|
|
717
|
+
MAX(CASE WHEN json_type(data, '$.tokens') IS NOT NULL THEN 1 ELSE 0 END) AS has_token_data
|
|
718
|
+
FROM message
|
|
719
|
+
GROUP BY session_id
|
|
720
|
+
) stats ON stats.session_id = s.id
|
|
721
|
+
WHERE s.parent_id IS NULL
|
|
722
|
+
ORDER BY time_created DESC
|
|
723
|
+
LIMIT 1000;
|
|
724
|
+
`.replace(/\n/g, ' ');
|
|
725
|
+
const out = execSync(`sqlite3 -separator '|||' "${OPENCODE_DB}"`, { encoding: 'utf-8', input: query, stdio: ['pipe', 'pipe', 'ignore'], timeout: 5000 });
|
|
726
|
+
const entries = [];
|
|
727
|
+
for (const line of out.split('\n')) {
|
|
728
|
+
if (!line.trim())
|
|
729
|
+
continue;
|
|
730
|
+
const [id, title, directory, version, timeCreatedStr, messageCountStr, tokenCountStr, hasTokenDataStr] = line.split('|||');
|
|
731
|
+
if (!id)
|
|
732
|
+
continue;
|
|
733
|
+
const timeCreated = parseInt(timeCreatedStr, 10);
|
|
734
|
+
const messageCount = parseInt(messageCountStr, 10);
|
|
735
|
+
const tokenCount = parseInt(tokenCountStr, 10);
|
|
736
|
+
const hasTokenData = hasTokenDataStr === '1';
|
|
737
|
+
const timestamp = isNaN(timeCreated) ? new Date().toISOString() : new Date(timeCreated).toISOString();
|
|
738
|
+
const topic = title || undefined;
|
|
739
|
+
const meta = {
|
|
740
|
+
id,
|
|
741
|
+
shortId: id.replace(/^ses_/, '').slice(0, 8),
|
|
742
|
+
agent: 'opencode',
|
|
743
|
+
timestamp,
|
|
744
|
+
project: directory ? path.basename(directory) : undefined,
|
|
745
|
+
cwd: directory ? normalizeCwd(directory) : undefined,
|
|
746
|
+
filePath: `${OPENCODE_DB}#${id}`,
|
|
747
|
+
version: resolveSessionVersion('opencode', OPENCODE_DB, version || undefined, currentVersion),
|
|
748
|
+
account,
|
|
749
|
+
topic,
|
|
750
|
+
messageCount: Number.isNaN(messageCount) ? undefined : messageCount,
|
|
751
|
+
tokenCount: hasTokenData && !Number.isNaN(tokenCount) ? tokenCount : undefined,
|
|
752
|
+
};
|
|
753
|
+
entries.push({ meta, content: topic || '', scan: currentScan });
|
|
754
|
+
}
|
|
755
|
+
upsertSessionsBatch(entries);
|
|
756
|
+
// Stamp the OpenCode DB itself so we can short-circuit on the next run.
|
|
757
|
+
recordScans([{ filePath: OPENCODE_DB, scan: currentScan }]);
|
|
758
|
+
}
|
|
759
|
+
catch (err) {
|
|
760
|
+
if (process.stderr.isTTY) {
|
|
761
|
+
console.error(`Warning: Could not query OpenCode sessions: ${err.message}`);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
// ---------------------------------------------------------------------------
|
|
766
|
+
// OpenClaw
|
|
767
|
+
// ---------------------------------------------------------------------------
|
|
768
|
+
async function scanOpenClawIncremental() {
|
|
769
|
+
// Check if openclaw is installed — silently skip if not.
|
|
770
|
+
try {
|
|
771
|
+
execSync('which openclaw', { stdio: 'ignore' });
|
|
772
|
+
}
|
|
773
|
+
catch {
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
// TTL cache: skip subprocess calls if we scanned recently. Stored in the
|
|
777
|
+
// meta table so we skip even when no channels/cron exist to produce rows.
|
|
778
|
+
const db = getDB();
|
|
779
|
+
const row = db.prepare(`SELECT value FROM meta WHERE key = 'openclaw_last_scan_ms'`).get();
|
|
780
|
+
const lastScanMs = row ? parseInt(row.value, 10) : 0;
|
|
781
|
+
if (lastScanMs && Date.now() - lastScanMs < OPENCLAW_TTL_MS) {
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
const currentVersion = await getCurrentAgentVersion('openclaw');
|
|
785
|
+
const now = Date.now();
|
|
786
|
+
const scan = { fileMtimeMs: now, fileSize: 0 };
|
|
787
|
+
const entries = [];
|
|
788
|
+
try {
|
|
789
|
+
const output = execSync('openclaw channels status', {
|
|
790
|
+
encoding: 'utf-8',
|
|
791
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
792
|
+
});
|
|
793
|
+
for (const line of output.split('\n')) {
|
|
794
|
+
const match = line.match(/^-\s+\w+\s+(\S+)\s+\((\w+)\):\s*(.+)/);
|
|
795
|
+
if (!match)
|
|
796
|
+
continue;
|
|
797
|
+
const [, agentId, name, statusStr] = match;
|
|
798
|
+
if (!statusStr.includes('running'))
|
|
799
|
+
continue;
|
|
800
|
+
entries.push({
|
|
801
|
+
meta: {
|
|
802
|
+
id: `openclaw-${agentId}`,
|
|
803
|
+
shortId: agentId.slice(0, 8),
|
|
804
|
+
agent: 'openclaw',
|
|
805
|
+
timestamp: new Date().toISOString(),
|
|
806
|
+
project: name,
|
|
807
|
+
cwd: getOpenClawSessionCwd(agentId),
|
|
808
|
+
version: currentVersion,
|
|
809
|
+
filePath: '',
|
|
810
|
+
},
|
|
811
|
+
content: `${name} ${agentId}`,
|
|
812
|
+
scan,
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
catch {
|
|
817
|
+
/* channels command failed */
|
|
818
|
+
}
|
|
819
|
+
try {
|
|
820
|
+
const output = execSync('openclaw cron list', {
|
|
821
|
+
encoding: 'utf-8',
|
|
822
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
823
|
+
});
|
|
824
|
+
const lines = output.split('\n');
|
|
825
|
+
for (let i = 1; i < lines.length; i++) {
|
|
826
|
+
const line = lines[i].trim();
|
|
827
|
+
if (!line)
|
|
828
|
+
continue;
|
|
829
|
+
const headMatch = line.match(/^([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\s+(\S+)/);
|
|
830
|
+
if (!headMatch)
|
|
831
|
+
continue;
|
|
832
|
+
const jobId = headMatch[1];
|
|
833
|
+
const jobName = headMatch[2];
|
|
834
|
+
const rest = line.slice(headMatch[0].length).trim();
|
|
835
|
+
const cols = rest.split(/\s{2,}/);
|
|
836
|
+
const agentId = cols[4] || '';
|
|
837
|
+
entries.push({
|
|
838
|
+
meta: {
|
|
839
|
+
id: `openclaw-cron-${jobId}`,
|
|
840
|
+
shortId: jobId.slice(0, 8),
|
|
841
|
+
agent: 'openclaw',
|
|
842
|
+
timestamp: new Date().toISOString(),
|
|
843
|
+
project: `${jobName} (${agentId || 'unknown'})`,
|
|
844
|
+
cwd: getOpenClawSessionCwd(agentId),
|
|
845
|
+
version: currentVersion,
|
|
846
|
+
filePath: '',
|
|
847
|
+
},
|
|
848
|
+
content: `${jobName} ${agentId}`,
|
|
849
|
+
scan,
|
|
850
|
+
});
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
catch {
|
|
854
|
+
/* cron command failed */
|
|
855
|
+
}
|
|
856
|
+
upsertSessionsBatch(entries);
|
|
857
|
+
db.prepare(`INSERT OR REPLACE INTO meta (key, value) VALUES ('openclaw_last_scan_ms', ?)`).run(String(Date.now()));
|
|
858
|
+
}
|
|
859
|
+
async function scanClaudeSession(filePath) {
|
|
860
|
+
const stream = fs.createReadStream(filePath, { encoding: 'utf-8' });
|
|
861
|
+
const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
|
|
862
|
+
let timestamp;
|
|
863
|
+
let cwd;
|
|
864
|
+
let gitBranch;
|
|
865
|
+
let version;
|
|
866
|
+
let topic;
|
|
867
|
+
let entrypoint;
|
|
868
|
+
let messageCount = 0;
|
|
869
|
+
let tokenCount = 0;
|
|
870
|
+
let sawTokenCount = false;
|
|
871
|
+
const seenAssistantIds = new Set();
|
|
872
|
+
const userTexts = [];
|
|
873
|
+
try {
|
|
874
|
+
for await (const line of rl) {
|
|
875
|
+
if (!line.trim())
|
|
876
|
+
continue;
|
|
877
|
+
let parsed;
|
|
878
|
+
try {
|
|
879
|
+
parsed = JSON.parse(line);
|
|
880
|
+
}
|
|
881
|
+
catch {
|
|
882
|
+
continue;
|
|
883
|
+
}
|
|
884
|
+
// entrypoint ships on the first envelope event (attachment/user/assistant)
|
|
885
|
+
// and is the clean structural signal for "was this a team spawn?"
|
|
886
|
+
if (!entrypoint && typeof parsed.entrypoint === 'string') {
|
|
887
|
+
entrypoint = parsed.entrypoint;
|
|
888
|
+
}
|
|
889
|
+
if (!timestamp && (parsed.type === 'user' || parsed.type === 'assistant') && parsed.timestamp) {
|
|
890
|
+
timestamp = parsed.timestamp;
|
|
891
|
+
cwd = parsed.cwd || '';
|
|
892
|
+
gitBranch = parsed.gitBranch || undefined;
|
|
893
|
+
version = parsed.version || undefined;
|
|
894
|
+
}
|
|
895
|
+
if (parsed.type === 'user') {
|
|
896
|
+
const text = extractClaudeUserText(parsed);
|
|
897
|
+
if (text) {
|
|
898
|
+
messageCount++;
|
|
899
|
+
userTexts.push(text);
|
|
900
|
+
if (!topic)
|
|
901
|
+
topic = extractSessionTopic(text);
|
|
902
|
+
}
|
|
903
|
+
continue;
|
|
904
|
+
}
|
|
905
|
+
if (parsed.type !== 'assistant')
|
|
906
|
+
continue;
|
|
907
|
+
const assistantId = typeof parsed.message?.id === 'string'
|
|
908
|
+
? parsed.message.id
|
|
909
|
+
: typeof parsed.uuid === 'string'
|
|
910
|
+
? parsed.uuid
|
|
911
|
+
: undefined;
|
|
912
|
+
const logicalId = assistantId || `${parsed.timestamp || ''}:${seenAssistantIds.size}`;
|
|
913
|
+
if (seenAssistantIds.has(logicalId))
|
|
914
|
+
continue;
|
|
915
|
+
seenAssistantIds.add(logicalId);
|
|
916
|
+
messageCount++;
|
|
917
|
+
const usage = getClaudeUsageTotal(parsed.message?.usage || parsed.usage);
|
|
918
|
+
if (usage !== null) {
|
|
919
|
+
tokenCount += usage;
|
|
920
|
+
sawTokenCount = true;
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
finally {
|
|
925
|
+
rl.close();
|
|
926
|
+
stream.destroy();
|
|
927
|
+
}
|
|
928
|
+
return {
|
|
929
|
+
timestamp,
|
|
930
|
+
cwd,
|
|
931
|
+
gitBranch,
|
|
932
|
+
version,
|
|
933
|
+
topic,
|
|
934
|
+
entrypoint,
|
|
935
|
+
messageCount,
|
|
936
|
+
tokenCount: sawTokenCount ? tokenCount : undefined,
|
|
937
|
+
contentText: userTexts.length > 0 ? userTexts.join('\n') : undefined,
|
|
938
|
+
};
|
|
939
|
+
}
|
|
940
|
+
async function scanCodexSession(filePath) {
|
|
941
|
+
const stream = fs.createReadStream(filePath, { encoding: 'utf-8' });
|
|
942
|
+
const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
|
|
943
|
+
let sessionId;
|
|
944
|
+
let timestamp;
|
|
945
|
+
let cwd;
|
|
946
|
+
let gitBranch;
|
|
947
|
+
let version;
|
|
948
|
+
let topic;
|
|
949
|
+
let messageCount = 0;
|
|
950
|
+
let tokenCount;
|
|
951
|
+
const userTexts = [];
|
|
952
|
+
try {
|
|
953
|
+
for await (const line of rl) {
|
|
954
|
+
if (!line.trim())
|
|
955
|
+
continue;
|
|
956
|
+
let parsed;
|
|
957
|
+
try {
|
|
958
|
+
parsed = JSON.parse(line);
|
|
959
|
+
}
|
|
960
|
+
catch {
|
|
961
|
+
continue;
|
|
962
|
+
}
|
|
963
|
+
if (parsed.type === 'session_meta') {
|
|
964
|
+
const payload = parsed.payload || {};
|
|
965
|
+
sessionId = payload.id || sessionId;
|
|
966
|
+
timestamp = payload.timestamp || parsed.timestamp || timestamp;
|
|
967
|
+
cwd = payload.cwd || cwd;
|
|
968
|
+
gitBranch = payload.git?.branch || gitBranch;
|
|
969
|
+
version = payload.cli_version || payload.version || version;
|
|
970
|
+
continue;
|
|
971
|
+
}
|
|
972
|
+
if (parsed.type === 'response_item' && parsed.payload?.type === 'message') {
|
|
973
|
+
const role = parsed.payload.role === 'user' || parsed.payload.role === 'developer'
|
|
974
|
+
? 'user'
|
|
975
|
+
: 'assistant';
|
|
976
|
+
const text = extractCodexMessageText(parsed.payload.content, role);
|
|
977
|
+
if (!text)
|
|
978
|
+
continue;
|
|
979
|
+
messageCount++;
|
|
980
|
+
if (role === 'user') {
|
|
981
|
+
userTexts.push(text);
|
|
982
|
+
if (!topic)
|
|
983
|
+
topic = extractSessionTopic(text);
|
|
984
|
+
}
|
|
985
|
+
continue;
|
|
986
|
+
}
|
|
987
|
+
if (parsed.type === 'event_msg' && parsed.payload?.type === 'token_count') {
|
|
988
|
+
const total = getCodexTokenCount(parsed.payload.info?.total_token_usage);
|
|
989
|
+
if (total !== null)
|
|
990
|
+
tokenCount = total;
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
finally {
|
|
995
|
+
rl.close();
|
|
996
|
+
stream.destroy();
|
|
997
|
+
}
|
|
998
|
+
return {
|
|
999
|
+
sessionId,
|
|
1000
|
+
timestamp,
|
|
1001
|
+
cwd,
|
|
1002
|
+
gitBranch,
|
|
1003
|
+
version,
|
|
1004
|
+
topic,
|
|
1005
|
+
messageCount,
|
|
1006
|
+
tokenCount,
|
|
1007
|
+
contentText: userTexts.length > 0 ? userTexts.join('\n') : undefined,
|
|
1008
|
+
};
|
|
1009
|
+
}
|
|
1010
|
+
function getOpenClawSessionCwd(agentId) {
|
|
1011
|
+
const workspace = agentId ? getOpenClawWorkspaceMap().get(agentId) : undefined;
|
|
1012
|
+
if (workspace)
|
|
1013
|
+
return workspace;
|
|
1014
|
+
const configDir = AGENTS.openclaw.configDir;
|
|
1015
|
+
return safeRealpathSync(configDir) || configDir;
|
|
1016
|
+
}
|
|
1017
|
+
function getOpenClawWorkspaceMap() {
|
|
1018
|
+
if (cachedOpenClawWorkspaces)
|
|
1019
|
+
return cachedOpenClawWorkspaces;
|
|
1020
|
+
const workspaces = new Map();
|
|
1021
|
+
const configPath = path.join(AGENTS.openclaw.configDir, 'openclaw.json');
|
|
1022
|
+
if (!fs.existsSync(configPath)) {
|
|
1023
|
+
cachedOpenClawWorkspaces = workspaces;
|
|
1024
|
+
return workspaces;
|
|
1025
|
+
}
|
|
1026
|
+
try {
|
|
1027
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
1028
|
+
for (const agent of config.agents?.list || []) {
|
|
1029
|
+
if (!agent.id || !agent.workspace)
|
|
1030
|
+
continue;
|
|
1031
|
+
workspaces.set(agent.id, safeRealpathSync(agent.workspace) || agent.workspace);
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
catch {
|
|
1035
|
+
// Ignore invalid OpenClaw config and fall back to ~/.openclaw.
|
|
1036
|
+
}
|
|
1037
|
+
cachedOpenClawWorkspaces = workspaces;
|
|
1038
|
+
return workspaces;
|
|
1039
|
+
}
|
|
1040
|
+
// ---------------------------------------------------------------------------
|
|
1041
|
+
// Utilities
|
|
1042
|
+
// ---------------------------------------------------------------------------
|
|
1043
|
+
export function readFirstLines(filePath, maxLines) {
|
|
1044
|
+
return new Promise((resolve) => {
|
|
1045
|
+
const lines = [];
|
|
1046
|
+
const stream = fs.createReadStream(filePath, { encoding: 'utf-8' });
|
|
1047
|
+
const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
|
|
1048
|
+
rl.on('line', (line) => {
|
|
1049
|
+
if (line.trim()) {
|
|
1050
|
+
lines.push(line);
|
|
1051
|
+
}
|
|
1052
|
+
if (lines.length >= maxLines) {
|
|
1053
|
+
rl.close();
|
|
1054
|
+
stream.destroy();
|
|
1055
|
+
}
|
|
1056
|
+
});
|
|
1057
|
+
rl.on('close', () => resolve(lines));
|
|
1058
|
+
rl.on('error', () => resolve(lines));
|
|
1059
|
+
});
|
|
1060
|
+
}
|
|
1061
|
+
/**
|
|
1062
|
+
* Walk a directory recursively for files with a given extension.
|
|
1063
|
+
*/
|
|
1064
|
+
export function walkForFiles(dir, ext, limit) {
|
|
1065
|
+
const results = [];
|
|
1066
|
+
function walk(d, depth) {
|
|
1067
|
+
if (depth > 5)
|
|
1068
|
+
return;
|
|
1069
|
+
let entries;
|
|
1070
|
+
try {
|
|
1071
|
+
entries = fs.readdirSync(d);
|
|
1072
|
+
}
|
|
1073
|
+
catch {
|
|
1074
|
+
return;
|
|
1075
|
+
}
|
|
1076
|
+
for (const entry of entries) {
|
|
1077
|
+
const full = path.join(d, entry);
|
|
1078
|
+
const stat = safeStatSync(full);
|
|
1079
|
+
if (!stat)
|
|
1080
|
+
continue;
|
|
1081
|
+
if (stat.isDirectory()) {
|
|
1082
|
+
walk(full, depth + 1);
|
|
1083
|
+
}
|
|
1084
|
+
else if (entry.endsWith(ext)) {
|
|
1085
|
+
results.push({ path: full, mtime: stat.mtimeMs });
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
walk(dir, 0);
|
|
1090
|
+
results.sort((a, b) => b.mtime - a.mtime);
|
|
1091
|
+
return results.slice(0, limit).map(r => r.path);
|
|
1092
|
+
}
|
|
1093
|
+
function sha256(input) {
|
|
1094
|
+
return crypto.createHash('sha256').update(input).digest('hex');
|
|
1095
|
+
}
|
|
1096
|
+
function safeStatSync(p) {
|
|
1097
|
+
try {
|
|
1098
|
+
return fs.statSync(p);
|
|
1099
|
+
}
|
|
1100
|
+
catch {
|
|
1101
|
+
return null;
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
function safeRealpathSync(p) {
|
|
1105
|
+
try {
|
|
1106
|
+
return fs.realpathSync(p);
|
|
1107
|
+
}
|
|
1108
|
+
catch {
|
|
1109
|
+
return null;
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
function extractClaudeUserText(parsed) {
|
|
1113
|
+
if (parsed.isMeta === true)
|
|
1114
|
+
return undefined;
|
|
1115
|
+
const content = parsed.message?.content;
|
|
1116
|
+
if (typeof content === 'string') {
|
|
1117
|
+
const text = content.trim();
|
|
1118
|
+
return isLocalCommandMessage(text) ? undefined : text || undefined;
|
|
1119
|
+
}
|
|
1120
|
+
if (!Array.isArray(content))
|
|
1121
|
+
return undefined;
|
|
1122
|
+
const text = content
|
|
1123
|
+
.filter((block) => block.type === 'text')
|
|
1124
|
+
.map((block) => String(block.text || '').trim())
|
|
1125
|
+
.find((value) => value && !value.startsWith('[Request interrupted'));
|
|
1126
|
+
if (!text || isLocalCommandMessage(text))
|
|
1127
|
+
return undefined;
|
|
1128
|
+
return text;
|
|
1129
|
+
}
|
|
1130
|
+
function isLocalCommandMessage(text) {
|
|
1131
|
+
return /<local-command-caveat>|<bash-(input|stdout|stderr)>/i.test(text);
|
|
1132
|
+
}
|
|
1133
|
+
function getClaudeUsageTotal(usage) {
|
|
1134
|
+
if (!usage || typeof usage !== 'object')
|
|
1135
|
+
return null;
|
|
1136
|
+
return sumKnownNumbers([
|
|
1137
|
+
usage.input_tokens,
|
|
1138
|
+
usage.output_tokens,
|
|
1139
|
+
usage.cache_creation_input_tokens,
|
|
1140
|
+
usage.cache_read_input_tokens,
|
|
1141
|
+
]);
|
|
1142
|
+
}
|
|
1143
|
+
function extractCodexMessageText(contentBlocks, role) {
|
|
1144
|
+
if (!Array.isArray(contentBlocks))
|
|
1145
|
+
return undefined;
|
|
1146
|
+
const matches = role === 'user'
|
|
1147
|
+
? contentBlocks.filter((block) => block.type === 'input_text')
|
|
1148
|
+
: contentBlocks.filter((block) => block.type === 'output_text');
|
|
1149
|
+
const text = matches
|
|
1150
|
+
.map((block) => String(block.text || '').trim())
|
|
1151
|
+
.find((value) => {
|
|
1152
|
+
if (!value)
|
|
1153
|
+
return false;
|
|
1154
|
+
if (role === 'user' && (value.length >= 2000 || value.includes('<permissions instructions>') || value.startsWith('# AGENTS.md instructions'))) {
|
|
1155
|
+
return false;
|
|
1156
|
+
}
|
|
1157
|
+
return true;
|
|
1158
|
+
});
|
|
1159
|
+
return text || undefined;
|
|
1160
|
+
}
|
|
1161
|
+
function normalizeVersion(version) {
|
|
1162
|
+
const trimmed = version?.trim();
|
|
1163
|
+
return trimmed ? trimmed : undefined;
|
|
1164
|
+
}
|
|
1165
|
+
function extractVersionFromManagedPath(agent, sourcePath) {
|
|
1166
|
+
if (!sourcePath)
|
|
1167
|
+
return undefined;
|
|
1168
|
+
const candidates = [sourcePath, safeRealpathSync(sourcePath) || ''];
|
|
1169
|
+
const marker = `/.agents/versions/${agent}/`;
|
|
1170
|
+
for (const candidate of candidates) {
|
|
1171
|
+
if (!candidate)
|
|
1172
|
+
continue;
|
|
1173
|
+
const normalized = candidate.split(path.sep).join('/');
|
|
1174
|
+
const start = normalized.indexOf(marker);
|
|
1175
|
+
if (start === -1)
|
|
1176
|
+
continue;
|
|
1177
|
+
const version = normalized.slice(start + marker.length).split('/')[0];
|
|
1178
|
+
if (version)
|
|
1179
|
+
return version;
|
|
1180
|
+
}
|
|
1181
|
+
return undefined;
|
|
1182
|
+
}
|
|
1183
|
+
async function getCurrentAgentVersion(agent) {
|
|
1184
|
+
const cached = cachedAgentVersions.get(agent);
|
|
1185
|
+
if (cached)
|
|
1186
|
+
return cached;
|
|
1187
|
+
const promise = (async () => {
|
|
1188
|
+
const symlinkVersion = normalizeVersion(getConfigSymlinkVersion(agent));
|
|
1189
|
+
if (symlinkVersion)
|
|
1190
|
+
return symlinkVersion;
|
|
1191
|
+
return normalizeVersion(await getCliVersion(agent));
|
|
1192
|
+
})();
|
|
1193
|
+
cachedAgentVersions.set(agent, promise);
|
|
1194
|
+
return promise;
|
|
1195
|
+
}
|
|
1196
|
+
function resolveSessionVersion(agent, sourcePath, embeddedVersion, currentVersion) {
|
|
1197
|
+
return normalizeVersion(embeddedVersion)
|
|
1198
|
+
|| extractVersionFromManagedPath(agent, sourcePath)
|
|
1199
|
+
|| normalizeVersion(currentVersion);
|
|
1200
|
+
}
|
|
1201
|
+
function getCodexTokenCount(totalTokenUsage) {
|
|
1202
|
+
if (!totalTokenUsage || typeof totalTokenUsage !== 'object')
|
|
1203
|
+
return null;
|
|
1204
|
+
return sumKnownNumbers([
|
|
1205
|
+
totalTokenUsage.input_tokens,
|
|
1206
|
+
totalTokenUsage.cached_input_tokens,
|
|
1207
|
+
totalTokenUsage.output_tokens,
|
|
1208
|
+
totalTokenUsage.reasoning_output_tokens,
|
|
1209
|
+
]);
|
|
1210
|
+
}
|
|
1211
|
+
function extractGeminiMessageText(content) {
|
|
1212
|
+
if (typeof content === 'string')
|
|
1213
|
+
return content.trim();
|
|
1214
|
+
if (Array.isArray(content)) {
|
|
1215
|
+
return content
|
|
1216
|
+
.map((part) => {
|
|
1217
|
+
if (typeof part === 'string')
|
|
1218
|
+
return part;
|
|
1219
|
+
if (typeof part?.text === 'string')
|
|
1220
|
+
return part.text;
|
|
1221
|
+
return '';
|
|
1222
|
+
})
|
|
1223
|
+
.join('\n')
|
|
1224
|
+
.trim();
|
|
1225
|
+
}
|
|
1226
|
+
return '';
|
|
1227
|
+
}
|
|
1228
|
+
function getGeminiTokenCount(tokens) {
|
|
1229
|
+
if (!tokens || typeof tokens !== 'object')
|
|
1230
|
+
return null;
|
|
1231
|
+
if (typeof tokens.total === 'number')
|
|
1232
|
+
return tokens.total;
|
|
1233
|
+
return sumKnownNumbers([
|
|
1234
|
+
tokens.input,
|
|
1235
|
+
tokens.output,
|
|
1236
|
+
tokens.cached,
|
|
1237
|
+
tokens.thoughts,
|
|
1238
|
+
tokens.tool,
|
|
1239
|
+
]);
|
|
1240
|
+
}
|
|
1241
|
+
function sumKnownNumbers(values) {
|
|
1242
|
+
let total = 0;
|
|
1243
|
+
let found = false;
|
|
1244
|
+
for (const value of values) {
|
|
1245
|
+
if (typeof value !== 'number' || Number.isNaN(value))
|
|
1246
|
+
continue;
|
|
1247
|
+
total += value;
|
|
1248
|
+
found = true;
|
|
1249
|
+
}
|
|
1250
|
+
return found ? total : null;
|
|
1251
|
+
}
|
|
1252
|
+
// ---------------------------------------------------------------------------
|
|
1253
|
+
// Time range parsing
|
|
1254
|
+
// ---------------------------------------------------------------------------
|
|
1255
|
+
export function parseTimeFilter(input) {
|
|
1256
|
+
const relativeMatch = input.match(/^(\d+)([mhdw])$/i);
|
|
1257
|
+
if (relativeMatch) {
|
|
1258
|
+
const value = parseInt(relativeMatch[1], 10);
|
|
1259
|
+
const unit = relativeMatch[2].toLowerCase();
|
|
1260
|
+
if (unit === 'm')
|
|
1261
|
+
return Date.now() - value * 60_000;
|
|
1262
|
+
if (unit === 'h')
|
|
1263
|
+
return Date.now() - value * 3_600_000;
|
|
1264
|
+
if (unit === 'd')
|
|
1265
|
+
return Date.now() - value * 86_400_000;
|
|
1266
|
+
if (unit === 'w')
|
|
1267
|
+
return Date.now() - value * 7 * 86_400_000;
|
|
1268
|
+
}
|
|
1269
|
+
const ts = new Date(input).getTime();
|
|
1270
|
+
return Number.isNaN(ts) ? 0 : ts;
|
|
1271
|
+
}
|
|
1272
|
+
//# sourceMappingURL=discover.js.map
|