Compare commits
66 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 346b32236a | |||
| 8f7f682a92 | |||
| 2ece7074f6 | |||
| c7ab2de25e | |||
| c9b0259993 | |||
| 1f2c3dbe2a | |||
| d247e44985 | |||
| d60dec9d62 | |||
| 4913cc7297 | |||
| 86360661e9 | |||
| 1d09193652 | |||
| 4ced6141ae | |||
| 530225d37c | |||
| e6ab77d123 | |||
| 4e1477c4be | |||
| 2b5b8363ca | |||
| 1e0f95f8bd | |||
| 6a20c54da7 | |||
| db6ae38568 | |||
| 1e43ea48aa | |||
| efe5e4ae50 | |||
| 31f1ce00d3 | |||
| d395aaf574 | |||
| d78ca8a9d5 | |||
| 19291d6be9 | |||
| 5409ba2b13 | |||
| 8bde9a213c | |||
| 315cb33e3f | |||
| 474843ffa6 | |||
| 766f7ff0d1 | |||
| f8b162ec5f | |||
| 8a8a9bc9e4 | |||
| 61f9c52455 | |||
| 43d65840ea | |||
| ad5a4b5000 | |||
| 73088b75ba | |||
| f23fadf52b | |||
| 9101033019 | |||
| 72bf4ed962 | |||
| 3b772ac239 | |||
| 8c1948d226 | |||
| 43f3865f69 | |||
| 7ef8b0eb36 | |||
| c6073fe9d2 | |||
| 81b9a66ef5 | |||
| 79be4eb525 | |||
| 04225ef890 | |||
| 7f78d92fef | |||
| bbfcde3d1b | |||
| 9f4e9845c0 | |||
| ce8b5dc86d | |||
| efb62fe41a | |||
| c26e9eacfa | |||
| aa49efb236 | |||
| 6b947b7593 | |||
| 3da7ea6408 | |||
| 79aabd6f43 | |||
| 40ac931c65 | |||
| aa51ba219b | |||
| 7623f29ffb | |||
| 3979b1f4e7 | |||
| caadd59441 | |||
| 30549f4863 | |||
| 070cd4d5a5 | |||
| 44ef62271e | |||
| 7440720b7b |
@@ -0,0 +1,20 @@
|
|||||||
|
# .claude (index)
|
||||||
|
dir: .claude
|
||||||
|
|
||||||
|
## role
|
||||||
|
Configuration directory for Claude AI assistant integration and custom instructions.
|
||||||
|
## parent
|
||||||
|
index: ./.pi-map.index.md
|
||||||
|
map: ./.pi-map.md
|
||||||
|
## children
|
||||||
|
- .claude/skills
|
||||||
|
index: .claude/skills/.pi-map.index.md
|
||||||
|
map: .claude/skills/.pi-map.md
|
||||||
|
## files
|
||||||
|
## links
|
||||||
|
index: .claude/.pi-map.index.md
|
||||||
|
map: .claude/.pi-map.md
|
||||||
|
## workflows
|
||||||
|
-
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# .claude
|
||||||
|
dir: .claude
|
||||||
|
|
||||||
|
index: .claude/.pi-map.index.md
|
||||||
|
|
||||||
|
## role
|
||||||
|
Configuration directory for Claude AI assistant integration and custom instructions.
|
||||||
|
## files
|
||||||
|
## arch
|
||||||
|
Project-specific AI tooling configuration using convention-based file organization for assistant context and behavior customization.
|
||||||
|
## tags
|
||||||
|
-
|
||||||
|
## symbols
|
||||||
|
-
|
||||||
|
## workflows
|
||||||
|
-
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# .claude/skills (index)
|
||||||
|
dir: .claude/skills
|
||||||
|
|
||||||
|
## role
|
||||||
|
Contains reusable AI skill definitions and prompt templates that configure Claude's specialized capabilities for specific development tasks.
|
||||||
|
## parent
|
||||||
|
index: .claude/.pi-map.index.md
|
||||||
|
map: .claude/.pi-map.md
|
||||||
|
## children
|
||||||
|
- .claude/skills/sift-backlog
|
||||||
|
index: .claude/skills/sift-backlog/.pi-map.index.md
|
||||||
|
map: .claude/skills/sift-backlog/.pi-map.md
|
||||||
|
## files
|
||||||
|
## links
|
||||||
|
index: .claude/skills/.pi-map.index.md
|
||||||
|
map: .claude/skills/.pi-map.md
|
||||||
|
## workflows
|
||||||
|
-
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# .claude/skills
|
||||||
|
dir: .claude/skills
|
||||||
|
|
||||||
|
index: .claude/skills/.pi-map.index.md
|
||||||
|
|
||||||
|
## role
|
||||||
|
Contains reusable AI skill definitions and prompt templates that configure Claude's specialized capabilities for specific development tasks.
|
||||||
|
## files
|
||||||
|
## arch
|
||||||
|
Modular skill-based architecture using declarative configuration files (likely YAML/JSON) to define context-specific behaviors, tool access patterns, and system prompts for different operational modes.
|
||||||
|
## tags
|
||||||
|
-
|
||||||
|
## symbols
|
||||||
|
-
|
||||||
|
## workflows
|
||||||
|
-
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# .claude/skills/sift-backlog (index)
|
||||||
|
dir: .claude/skills/sift-backlog
|
||||||
|
|
||||||
|
## role
|
||||||
|
Defines a workflow skill for triaging, organizing, and activating backlog tasks into actionable plans using a custom CLI tool.
|
||||||
|
## parent
|
||||||
|
index: .claude/skills/.pi-map.index.md
|
||||||
|
map: .claude/skills/.pi-map.md
|
||||||
|
## children
|
||||||
|
-
|
||||||
|
## files
|
||||||
|
- SKILL.md
|
||||||
|
## links
|
||||||
|
index: .claude/skills/sift-backlog/.pi-map.index.md
|
||||||
|
map: .claude/skills/sift-backlog/.pi-map.md
|
||||||
|
## workflows
|
||||||
|
-
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# .claude/skills/sift-backlog
|
||||||
|
dir: .claude/skills/sift-backlog
|
||||||
|
|
||||||
|
index: .claude/skills/sift-backlog/.pi-map.index.md
|
||||||
|
|
||||||
|
## role
|
||||||
|
Defines a workflow skill for triaging, organizing, and activating backlog tasks into actionable plans using a custom CLI tool.
|
||||||
|
## files
|
||||||
|
- SKILL.md | Defines a workflow skill for triaging, organizing, and activating backlog tasks into actionable plans using a custom CLI tool. | dep: sf (custom CLI tool), task management system, plan management system
|
||||||
|
## arch
|
||||||
|
Documentation-driven skill definition using structured markdown with command specifications, workflow stages, and integration patterns for Claude CLI tooling.
|
||||||
|
## tags
|
||||||
|
skill, defines, workflow, triaging, organizing, activating, backlog, tasks
|
||||||
|
## symbols
|
||||||
|
-
|
||||||
|
## workflows
|
||||||
|
-
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -17,6 +17,7 @@ __pycache__/
|
|||||||
*.so
|
*.so
|
||||||
.python-version
|
.python-version
|
||||||
.venv/
|
.venv/
|
||||||
|
.venv-test/
|
||||||
venv/
|
venv/
|
||||||
env/
|
env/
|
||||||
.pytest_cache/
|
.pytest_cache/
|
||||||
@@ -55,3 +56,5 @@ Thumbs.db
|
|||||||
.sisyphus/
|
.sisyphus/
|
||||||
.pi-lens/
|
.pi-lens/
|
||||||
minerv3/
|
minerv3/
|
||||||
|
.cache/
|
||||||
|
openspec-audit-report.md
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# .opencode (index)
|
||||||
|
dir: .opencode
|
||||||
|
|
||||||
|
## role
|
||||||
|
Hidden directory for OpenCode IDE/editor configuration and workspace metadata
|
||||||
|
## parent
|
||||||
|
index: ./.pi-map.index.md
|
||||||
|
map: ./.pi-map.md
|
||||||
|
## children
|
||||||
|
- .opencode/commands
|
||||||
|
index: .opencode/commands/.pi-map.index.md
|
||||||
|
map: .opencode/commands/.pi-map.md
|
||||||
|
- .opencode/skills
|
||||||
|
index: .opencode/skills/.pi-map.index.md
|
||||||
|
map: .opencode/skills/.pi-map.md
|
||||||
|
## files
|
||||||
|
## links
|
||||||
|
index: .opencode/.pi-map.index.md
|
||||||
|
map: .opencode/.pi-map.md
|
||||||
|
## workflows
|
||||||
|
-
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# .opencode
|
||||||
|
dir: .opencode
|
||||||
|
|
||||||
|
index: .opencode/.pi-map.index.md
|
||||||
|
|
||||||
|
## role
|
||||||
|
Hidden directory for OpenCode IDE/editor configuration and workspace metadata
|
||||||
|
## files
|
||||||
|
## arch
|
||||||
|
IDE-specific dot-directory pattern, no active code architecture; stores tool preferences and ephemeral state
|
||||||
|
## tags
|
||||||
|
-
|
||||||
|
## symbols
|
||||||
|
-
|
||||||
|
## workflows
|
||||||
|
-
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# .opencode/commands (index)
|
||||||
|
dir: .opencode/commands
|
||||||
|
|
||||||
|
## role
|
||||||
|
Defines experimental workflow skills and AI assistant stances for an OpenSpec-based development system with structured change management.
|
||||||
|
## parent
|
||||||
|
index: .opencode/.pi-map.index.md
|
||||||
|
map: .opencode/.pi-map.md
|
||||||
|
## children
|
||||||
|
-
|
||||||
|
## files
|
||||||
|
- opsx-apply.md
|
||||||
|
- opsx-archive.md
|
||||||
|
- opsx-explore.md
|
||||||
|
- opsx-propose.md
|
||||||
|
## links
|
||||||
|
index: .opencode/commands/.pi-map.index.md
|
||||||
|
map: .opencode/commands/.pi-map.md
|
||||||
|
## workflows
|
||||||
|
-
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# .opencode/commands
|
||||||
|
dir: .opencode/commands
|
||||||
|
|
||||||
|
index: .opencode/commands/.pi-map.index.md
|
||||||
|
|
||||||
|
## role
|
||||||
|
Defines experimental workflow skills and AI assistant stances for an OpenSpec-based development system with structured change management.
|
||||||
|
## files
|
||||||
|
- opsx-apply.md | Defines an experimental workflow skill for implementing tasks from an OpenSpec change through a structured, interactive process with CLI integration and progress tracking. | dep: openspec CLI, AskUserQuestion tool, filesystem (for reading context files)
|
||||||
|
- opsx-archive.md | Defines a workflow for archiving completed changes in an experimental openspec-based development system | dep: openspec CLI, AskUserQuestion tool, Task tool, Skill tool, filesystem (mkdir, mv), JSON parsing
|
||||||
|
- opsx-explore.md | Defines the "explore mode" stance for an AI assistant - a thinking/discovery mode for investigating problems and clarifying requirements without implementing code | dep: OpenSpec system
|
||||||
|
- opsx-propose.md | Defines a workflow for proposing new changes in the openspec system by creating a change directory and generating all required artifacts (proposal.md, design.md, tasks.md) in dependency order | dep: openspec CLI, AskUserQuestion tool, TodoWrite tool, JSON parsing
|
||||||
|
## arch
|
||||||
|
Markdown-based command definitions using a workflow pattern with interactive CLI integration, progress tracking, and dependency-ordered artifact generation across explore/propose/apply/archive lifecycle phases.
|
||||||
|
## tags
|
||||||
|
opsx, defines, workflow, openspec, openspec cli, askuserquestion tool, explore, experimental
|
||||||
|
## symbols
|
||||||
|
-
|
||||||
|
## workflows
|
||||||
|
-
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
# .opencode/skills (index)
|
||||||
|
dir: .opencode/skills
|
||||||
|
|
||||||
|
## role
|
||||||
|
Contains reusable AI skill modules that provide specialized capabilities for the OpenCode assistant.
|
||||||
|
## parent
|
||||||
|
index: .opencode/.pi-map.index.md
|
||||||
|
map: .opencode/.pi-map.md
|
||||||
|
## children
|
||||||
|
- .opencode/skills/openspec-apply-change
|
||||||
|
index: .opencode/skills/openspec-apply-change/.pi-map.index.md
|
||||||
|
map: .opencode/skills/openspec-apply-change/.pi-map.md
|
||||||
|
- .opencode/skills/openspec-archive-change
|
||||||
|
index: .opencode/skills/openspec-archive-change/.pi-map.index.md
|
||||||
|
map: .opencode/skills/openspec-archive-change/.pi-map.md
|
||||||
|
- .opencode/skills/openspec-explore
|
||||||
|
index: .opencode/skills/openspec-explore/.pi-map.index.md
|
||||||
|
map: .opencode/skills/openspec-explore/.pi-map.md
|
||||||
|
- .opencode/skills/openspec-propose
|
||||||
|
index: .opencode/skills/openspec-propose/.pi-map.index.md
|
||||||
|
map: .opencode/skills/openspec-propose/.pi-map.md
|
||||||
|
## files
|
||||||
|
## links
|
||||||
|
index: .opencode/skills/.pi-map.index.md
|
||||||
|
map: .opencode/skills/.pi-map.md
|
||||||
|
## workflows
|
||||||
|
-
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# .opencode/skills
|
||||||
|
dir: .opencode/skills
|
||||||
|
|
||||||
|
index: .opencode/skills/.pi-map.index.md
|
||||||
|
|
||||||
|
## role
|
||||||
|
Contains reusable AI skill modules that provide specialized capabilities for the OpenCode assistant.
|
||||||
|
## files
|
||||||
|
## arch
|
||||||
|
Modular plugin-based architecture where each skill is a self-contained module with defined interfaces, enabling dynamic loading and composition of AI capabilities.
|
||||||
|
## tags
|
||||||
|
-
|
||||||
|
## symbols
|
||||||
|
-
|
||||||
|
## workflows
|
||||||
|
-
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# .opencode/skills/openspec-apply-change (index)
|
||||||
|
dir: .opencode/skills/openspec-apply-change
|
||||||
|
|
||||||
|
## role
|
||||||
|
Defines an AI assistant skill that implements OpenSpec changes through a spec-driven workflow with structured planning, validation, and execution phases.
|
||||||
|
## parent
|
||||||
|
index: .opencode/skills/.pi-map.index.md
|
||||||
|
map: .opencode/skills/.pi-map.md
|
||||||
|
## children
|
||||||
|
-
|
||||||
|
## files
|
||||||
|
- SKILL.md
|
||||||
|
## links
|
||||||
|
index: .opencode/skills/openspec-apply-change/.pi-map.index.md
|
||||||
|
map: .opencode/skills/openspec-apply-change/.pi-map.md
|
||||||
|
## workflows
|
||||||
|
-
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# .opencode/skills/openspec-apply-change
|
||||||
|
dir: .opencode/skills/openspec-apply-change
|
||||||
|
|
||||||
|
index: .opencode/skills/openspec-apply-change/.pi-map.index.md
|
||||||
|
|
||||||
|
## role
|
||||||
|
Defines an AI assistant skill that implements OpenSpec changes through a spec-driven workflow with structured planning, validation, and execution phases.
|
||||||
|
## files
|
||||||
|
- SKILL.md | Defines an AI assistant skill for implementing tasks from an OpenSpec change using a spec-driven workflow | dep: openspec CLI, AskUserQuestion tool, filesystem access
|
||||||
|
## arch
|
||||||
|
Template-based skill definition using markdown documentation with structured workflow phases (planning, validation, execution) and integration points for external tools (OpenSpec CLI, OpenCode agent).
|
||||||
|
## tags
|
||||||
|
skill, defines, assistant, implementing, tasks, openspec, change, spec
|
||||||
|
## symbols
|
||||||
|
-
|
||||||
|
## workflows
|
||||||
|
-
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# .opencode/skills/openspec-archive-change (index)
|
||||||
|
dir: .opencode/skills/openspec-archive-change
|
||||||
|
|
||||||
|
## role
|
||||||
|
Provides a reusable automation skill for archiving completed experimental changes via the openspec CLI
|
||||||
|
## parent
|
||||||
|
index: .opencode/skills/.pi-map.index.md
|
||||||
|
map: .opencode/skills/.pi-map.md
|
||||||
|
## children
|
||||||
|
-
|
||||||
|
## files
|
||||||
|
- SKILL.md
|
||||||
|
## links
|
||||||
|
index: .opencode/skills/openspec-archive-change/.pi-map.index.md
|
||||||
|
map: .opencode/skills/openspec-archive-change/.pi-map.md
|
||||||
|
## workflows
|
||||||
|
-
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# .opencode/skills/openspec-archive-change
|
||||||
|
dir: .opencode/skills/openspec-archive-change
|
||||||
|
|
||||||
|
index: .opencode/skills/openspec-archive-change/.pi-map.index.md
|
||||||
|
|
||||||
|
## role
|
||||||
|
Provides a reusable automation skill for archiving completed experimental changes via the openspec CLI
|
||||||
|
## files
|
||||||
|
- SKILL.md | Defines a skill for archiving completed changes in an experimental workflow using the openspec CLI. | dep: openspec CLI, AskUserQuestion tool, Task tool, file system (mkdir, mv, read), JSON parsing
|
||||||
|
## arch
|
||||||
|
Skill-based modular automation pattern using markdown-defined CLI operations with structured metadata and command templates
|
||||||
|
## tags
|
||||||
|
skill, defines, archiving, completed, changes, experimental, workflow, openspec
|
||||||
|
## symbols
|
||||||
|
-
|
||||||
|
## workflows
|
||||||
|
-
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# .opencode/skills/openspec-explore (index)
|
||||||
|
dir: .opencode/skills/openspec-explore
|
||||||
|
|
||||||
|
## role
|
||||||
|
Defines a conversational AI skill/persona for "explore mode" that serves as a thinking partner for exploring ideas, investigating problems, and clarifying requirements without implementing code.
|
||||||
|
## parent
|
||||||
|
index: .opencode/skills/.pi-map.index.md
|
||||||
|
map: .opencode/skills/.pi-map.md
|
||||||
|
## children
|
||||||
|
-
|
||||||
|
## files
|
||||||
|
- SKILL.md
|
||||||
|
## links
|
||||||
|
index: .opencode/skills/openspec-explore/.pi-map.index.md
|
||||||
|
map: .opencode/skills/openspec-explore/.pi-map.md
|
||||||
|
## workflows
|
||||||
|
-
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# .opencode/skills/openspec-explore
|
||||||
|
dir: .opencode/skills/openspec-explore
|
||||||
|
|
||||||
|
index: .opencode/skills/openspec-explore/.pi-map.index.md
|
||||||
|
|
||||||
|
## role
|
||||||
|
Defines a conversational AI skill/persona for "explore mode" that serves as a thinking partner for exploring ideas, investigating problems, and clarifying requirements without implementing code.
|
||||||
|
## files
|
||||||
|
- SKILL.md | Defines a conversational AI skill/persona for "explore mode" - a thinking partner for exploring ideas, investigating problems, and clarifying requirements without implementing code. | dep: openspec CLI
|
||||||
|
## arch
|
||||||
|
Single-file skill definition using markdown-based persona specification with structured sections for description, usage guidelines, and behavioral constraints.
|
||||||
|
## tags
|
||||||
|
skill, defines, conversational, persona, explore, mode, thinking, partner
|
||||||
|
## symbols
|
||||||
|
-
|
||||||
|
## workflows
|
||||||
|
-
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# .opencode/skills/openspec-propose (index)
|
||||||
|
dir: .opencode/skills/openspec-propose
|
||||||
|
|
||||||
|
## role
|
||||||
|
Provides a structured workflow skill for proposing new changes using the openspec CLI with artifact generation in dependency order.
|
||||||
|
## parent
|
||||||
|
index: .opencode/skills/.pi-map.index.md
|
||||||
|
map: .opencode/skills/.pi-map.md
|
||||||
|
## children
|
||||||
|
-
|
||||||
|
## files
|
||||||
|
- SKILL.md
|
||||||
|
## links
|
||||||
|
index: .opencode/skills/openspec-propose/.pi-map.index.md
|
||||||
|
map: .opencode/skills/openspec-propose/.pi-map.md
|
||||||
|
## workflows
|
||||||
|
-
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# .opencode/skills/openspec-propose
|
||||||
|
dir: .opencode/skills/openspec-propose
|
||||||
|
|
||||||
|
index: .opencode/skills/openspec-propose/.pi-map.index.md
|
||||||
|
|
||||||
|
## role
|
||||||
|
Provides a structured workflow skill for proposing new changes using the openspec CLI with artifact generation in dependency order.
|
||||||
|
## files
|
||||||
|
- SKILL.md | Defines a structured workflow for proposing new changes using the openspec CLI, generating proposal, design, and task artifacts in dependency order. | dep: openspec CLI, AskUserQuestion tool, TodoWrite tool
|
||||||
|
## arch
|
||||||
|
Template-based skill definition using markdown documentation with sequential artifact generation (proposal → design → tasks) following dependency ordering.
|
||||||
|
## tags
|
||||||
|
skill, defines, structured, workflow, proposing, new, changes, openspec
|
||||||
|
## symbols
|
||||||
|
-
|
||||||
|
## workflows
|
||||||
|
-
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
# . (index)
|
||||||
|
dir: .
|
||||||
|
|
||||||
|
## Project Map Protocol
|
||||||
|
|
||||||
|
1. Read this protocol and the root `.pi-map.index.md` first.
|
||||||
|
2. Use `index:` / `map:` references to open relevant directory indexes and maps.
|
||||||
|
3. Load indexes before rich maps during task-start navigation.
|
||||||
|
4. Read the local rich map and actual source before editing.
|
||||||
|
5. Treat non-empty `## dirty` sections in either artifact as stale.
|
||||||
|
6. If source and generated artifacts disagree, trust source.
|
||||||
|
7. If map and index disagree, trust neither blindly; verify from source and regenerate the pair.
|
||||||
|
8. After editing source, run `project_map_patch` for each changed file.
|
||||||
|
9. Before broad architectural claims or final handoff, run `project_map_validate` when freshness matters.
|
||||||
|
|
||||||
|
Trust boundary: index routes, map orients, source decides.
|
||||||
|
|
||||||
|
## role
|
||||||
|
Infrastructure and deployment configuration package for a self-hosted project management platform with OAuth2 authentication, providing containerized orchestration, environment templates, and development tooling.
|
||||||
|
## parent
|
||||||
|
-
|
||||||
|
## children
|
||||||
|
- .atl
|
||||||
|
index: .atl/.pi-map.index.md
|
||||||
|
map: .atl/.pi-map.md
|
||||||
|
- .claude
|
||||||
|
index: .claude/.pi-map.index.md
|
||||||
|
map: .claude/.pi-map.md
|
||||||
|
- .opencode
|
||||||
|
index: .opencode/.pi-map.index.md
|
||||||
|
map: .opencode/.pi-map.md
|
||||||
|
- .pi
|
||||||
|
index: .pi/.pi-map.index.md
|
||||||
|
map: .pi/.pi-map.md
|
||||||
|
- .sisyphus
|
||||||
|
index: .sisyphus/.pi-map.index.md
|
||||||
|
map: .sisyphus/.pi-map.md
|
||||||
|
- .stoneforge
|
||||||
|
index: .stoneforge/.pi-map.index.md
|
||||||
|
map: .stoneforge/.pi-map.md
|
||||||
|
- apps
|
||||||
|
index: apps/.pi-map.index.md
|
||||||
|
map: apps/.pi-map.md
|
||||||
|
- docs
|
||||||
|
index: docs/.pi-map.index.md
|
||||||
|
map: docs/.pi-map.md
|
||||||
|
- e2e
|
||||||
|
index: e2e/.pi-map.index.md
|
||||||
|
map: e2e/.pi-map.md
|
||||||
|
- minerv3
|
||||||
|
index: minerv3/.pi-map.index.md
|
||||||
|
map: minerv3/.pi-map.md
|
||||||
|
- openspec
|
||||||
|
index: openspec/.pi-map.index.md
|
||||||
|
map: openspec/.pi-map.md
|
||||||
|
- scripts
|
||||||
|
index: scripts/.pi-map.index.md
|
||||||
|
map: scripts/.pi-map.md
|
||||||
|
- tool-images
|
||||||
|
index: tool-images/.pi-map.index.md
|
||||||
|
map: tool-images/.pi-map.md
|
||||||
|
- uploads
|
||||||
|
index: uploads/.pi-map.index.md
|
||||||
|
map: uploads/.pi-map.md
|
||||||
|
## files
|
||||||
|
- .env.example
|
||||||
|
- .gitignore
|
||||||
|
- AGENTS.md
|
||||||
|
- CHANGELOG.md
|
||||||
|
- Makefile
|
||||||
|
- README.md
|
||||||
|
- docker-compose.traefik.yml
|
||||||
|
- docker-compose.yml
|
||||||
|
- progress.md
|
||||||
|
- swap-pane
|
||||||
|
## links
|
||||||
|
index: ./.pi-map.index.md
|
||||||
|
map: ./.pi-map.md
|
||||||
|
## workflows
|
||||||
|
-
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
# .
|
||||||
|
dir: .
|
||||||
|
|
||||||
|
index: ./.pi-map.index.md
|
||||||
|
|
||||||
|
## Project Map Protocol
|
||||||
|
|
||||||
|
1. Read this protocol and the root `.pi-map.index.md` first.
|
||||||
|
2. Use `index:` / `map:` references to open relevant directory indexes and maps.
|
||||||
|
3. Load indexes before rich maps during task-start navigation.
|
||||||
|
4. Read the local rich map and actual source before editing.
|
||||||
|
5. Treat non-empty `## dirty` sections in either artifact as stale.
|
||||||
|
6. If source and generated artifacts disagree, trust source.
|
||||||
|
7. If map and index disagree, trust neither blindly; verify from source and regenerate the pair.
|
||||||
|
8. After editing source, run `project_map_patch` for each changed file.
|
||||||
|
9. Before broad architectural claims or final handoff, run `project_map_validate` when freshness matters.
|
||||||
|
|
||||||
|
Trust boundary: index routes, map orients, source decides.
|
||||||
|
|
||||||
|
## role
|
||||||
|
Infrastructure and deployment configuration package for a self-hosted project management platform with OAuth2 authentication, providing containerized orchestration, environment templates, and development tooling.
|
||||||
|
## files
|
||||||
|
- .env.example | Provides a template of environment variables for configuring a Headquarter application with PostgreSQL, Redis, Authentik SSO, and Docker/Traefik deployment
|
||||||
|
- .gitignore | Specifies files and directories for Git to ignore across a multi-language project with Python, Node, and custom tooling | dep: Git
|
||||||
|
- AGENTS.md | Defines operational rules, workflows, and constraints for AI agents working within an OpenSpec-driven software development project. | dep: OpenSpec, superpowers, git, docker compose, conventional commits
|
||||||
|
- CHANGELOG.md | Documents version history and notable changes for a Git-based project management web application
|
||||||
|
- Makefile | Provides standard development commands for containerized web application lifecycle management via Docker Compose | dep: docker compose, alembic, pytest, ruff, mypy, playwright, npm, postgres, redis
|
||||||
|
- README.md | A self-hosted platform for managing projects, git repositories, and development tools with OAuth2 authentication. | dep: FastAPI, SQLAlchemy, Pydantic, Alembic, python-jose, React, TypeScript, Vite, React Router, Docker, PostgreSQL, Traefik, Authentik, Git
|
||||||
|
- docker-compose.traefik.yml | Deploys a multi-service web application (frontend, API, PostgreSQL, Redis) behind an existing Traefik reverse proxy with TLS termination and environment-configurable domains. | dep: docker, traefik, postgres, redis, authentik, docker-compose
|
||||||
|
- docker-compose.yml | Defines a multi-service Docker Compose stack with PostgreSQL, Redis, API backend, and web frontend services for a "headquarter" application | dep: Docker, PostgreSQL, Redis, Vite, asyncpg, nginx
|
||||||
|
- progress.md | Tracks completed and remaining tasks for a backend-frontend code refactoring project organized in 7 phases
|
||||||
|
- swap-pane | Empty file with no functionality
|
||||||
|
## arch
|
||||||
|
Docker Compose-based microservices architecture with frontend/backend separation, PostgreSQL/Redis data layer, Traefik reverse proxy integration, and environment-driven configuration management following twelve-factor app principles.
|
||||||
|
## tags
|
||||||
|
docker, redis, git, application, postgresql, compose, traefik, project
|
||||||
|
## symbols
|
||||||
|
-
|
||||||
|
## workflows
|
||||||
|
-
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# .stoneforge (index)
|
||||||
|
dir: .stoneforge
|
||||||
|
|
||||||
|
## role
|
||||||
|
Internal configuration and state tracking directory for the Stoneforge application
|
||||||
|
## parent
|
||||||
|
index: ./.pi-map.index.md
|
||||||
|
map: ./.pi-map.md
|
||||||
|
## children
|
||||||
|
- .stoneforge/sync
|
||||||
|
index: .stoneforge/sync/.pi-map.index.md
|
||||||
|
map: .stoneforge/sync/.pi-map.md
|
||||||
|
## files
|
||||||
|
- .dashboard-opened
|
||||||
|
- .gitignore
|
||||||
|
- config.yaml
|
||||||
|
## links
|
||||||
|
index: .stoneforge/.pi-map.index.md
|
||||||
|
map: .stoneforge/.pi-map.md
|
||||||
|
## workflows
|
||||||
|
-
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# .stoneforge
|
||||||
|
dir: .stoneforge
|
||||||
|
|
||||||
|
index: .stoneforge/.pi-map.index.md
|
||||||
|
|
||||||
|
## role
|
||||||
|
Internal configuration and state tracking directory for the Stoneforge application
|
||||||
|
## files
|
||||||
|
- .dashboard-opened | Stores timestamp and identifier data for tracking when a dashboard was opened
|
||||||
|
- .gitignore | Specifies files and patterns for Git to ignore in version control
|
||||||
|
- config.yaml | Configuration file for the Stoneforge application defining database, sync, playbook, identity, merge, workflow, and agent settings.
|
||||||
|
## arch
|
||||||
|
Simple dot-directory pattern storing metadata (.dashboard-opened), version control exclusions (.gitignore), and hierarchical YAML configuration (config.yaml) with domain-separated settings
|
||||||
|
## tags
|
||||||
|
config, stores, timestamp, identifier, data, tracking, dashboard, was
|
||||||
|
## symbols
|
||||||
|
-
|
||||||
|
## workflows
|
||||||
|
-
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# .stoneforge/sync (index)
|
||||||
|
dir: .stoneforge/sync
|
||||||
|
|
||||||
|
## role
|
||||||
|
Persists distributed task execution state by storing dependency graphs and ephemeral worker agent records for a collaborative workflow system.
|
||||||
|
## parent
|
||||||
|
index: .stoneforge/.pi-map.index.md
|
||||||
|
map: .stoneforge/.pi-map.md
|
||||||
|
## children
|
||||||
|
-
|
||||||
|
## files
|
||||||
|
- dependencies.jsonl
|
||||||
|
- elements.jsonl
|
||||||
|
## links
|
||||||
|
index: .stoneforge/sync/.pi-map.index.md
|
||||||
|
map: .stoneforge/sync/.pi-map.md
|
||||||
|
## workflows
|
||||||
|
-
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# .stoneforge/sync
|
||||||
|
dir: .stoneforge/sync
|
||||||
|
|
||||||
|
index: .stoneforge/sync/.pi-map.index.md
|
||||||
|
|
||||||
|
## role
|
||||||
|
Persists distributed task execution state by storing dependency graphs and ephemeral worker agent records for a collaborative workflow system.
|
||||||
|
## files
|
||||||
|
- dependencies.jsonl | Stores a sequence of dependency relationships between entities in JSON Lines format, tracking parent-child, blocking, and reply relationships with timestamps and creators.
|
||||||
|
- elements.jsonl | Stores JSONL records of ephemeral worker agents with their session history, worktree assignments, and lifecycle metadata for a distributed task execution system.
|
||||||
|
## arch
|
||||||
|
Event-sourced JSONL append-only logs with entity-relationship modeling (parent-child, blocking, reply) and session-based worker lifecycle tracking.
|
||||||
|
## tags
|
||||||
|
stores, relationships, dependencies, elements, sequence, dependency, entities, json
|
||||||
|
## symbols
|
||||||
|
-
|
||||||
|
## workflows
|
||||||
|
-
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# apps (index)
|
||||||
|
dir: apps
|
||||||
|
|
||||||
|
## role
|
||||||
|
Contains the main deployable application modules or entry points for the project.
|
||||||
|
## parent
|
||||||
|
index: ./.pi-map.index.md
|
||||||
|
map: ./.pi-map.md
|
||||||
|
## children
|
||||||
|
- apps/api
|
||||||
|
index: apps/api/.pi-map.index.md
|
||||||
|
map: apps/api/.pi-map.md
|
||||||
|
- apps/web
|
||||||
|
index: apps/web/.pi-map.index.md
|
||||||
|
map: apps/web/.pi-map.md
|
||||||
|
## files
|
||||||
|
## links
|
||||||
|
index: apps/.pi-map.index.md
|
||||||
|
map: apps/.pi-map.md
|
||||||
|
## workflows
|
||||||
|
-
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# apps
|
||||||
|
dir: apps
|
||||||
|
|
||||||
|
index: apps/.pi-map.index.md
|
||||||
|
|
||||||
|
## role
|
||||||
|
Contains the main deployable application modules or entry points for the project.
|
||||||
|
## files
|
||||||
|
## arch
|
||||||
|
Modular monolith or microservices architecture with separate application boundaries, each potentially having its own configuration, dependencies, and lifecycle.
|
||||||
|
## tags
|
||||||
|
-
|
||||||
|
## symbols
|
||||||
|
-
|
||||||
|
## workflows
|
||||||
|
-
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
# apps/api (index)
|
||||||
|
dir: apps/api
|
||||||
|
|
||||||
|
## role
|
||||||
|
Self-hosted FastAPI backend API that manages projects, git repositories, and development tools via Docker instances.
|
||||||
|
## parent
|
||||||
|
index: apps/.pi-map.index.md
|
||||||
|
map: apps/.pi-map.md
|
||||||
|
## children
|
||||||
|
- apps/api/.pi-lens
|
||||||
|
index: apps/api/.pi-lens/.pi-map.index.md
|
||||||
|
map: apps/api/.pi-lens/.pi-map.md
|
||||||
|
- apps/api/.pytest_cache
|
||||||
|
index: apps/api/.pytest_cache/.pi-map.index.md
|
||||||
|
map: apps/api/.pytest_cache/.pi-map.md
|
||||||
|
- apps/api/.venv-test
|
||||||
|
index: apps/api/.venv-test/.pi-map.index.md
|
||||||
|
map: apps/api/.venv-test/.pi-map.md
|
||||||
|
- apps/api/alembic
|
||||||
|
index: apps/api/alembic/.pi-map.index.md
|
||||||
|
map: apps/api/alembic/.pi-map.md
|
||||||
|
- apps/api/src
|
||||||
|
index: apps/api/src/.pi-map.index.md
|
||||||
|
map: apps/api/src/.pi-map.md
|
||||||
|
- apps/api/tests
|
||||||
|
index: apps/api/tests/.pi-map.index.md
|
||||||
|
map: apps/api/tests/.pi-map.md
|
||||||
|
- apps/api/uploads
|
||||||
|
index: apps/api/uploads/.pi-map.index.md
|
||||||
|
map: apps/api/uploads/.pi-map.md
|
||||||
|
## files
|
||||||
|
- .dockerignore
|
||||||
|
- Dockerfile
|
||||||
|
- README.md
|
||||||
|
- alembic.ini
|
||||||
|
- pyproject.toml
|
||||||
|
- uv.lock
|
||||||
|
- wait-for-db.sh
|
||||||
|
## links
|
||||||
|
index: apps/api/.pi-map.index.md
|
||||||
|
map: apps/api/.pi-map.md
|
||||||
|
## workflows
|
||||||
|
-
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# apps/api
|
||||||
|
dir: apps/api
|
||||||
|
|
||||||
|
index: apps/api/.pi-map.index.md
|
||||||
|
|
||||||
|
## role
|
||||||
|
Self-hosted FastAPI backend API that manages projects, git repositories, and development tools via Docker instances.
|
||||||
|
## files
|
||||||
|
- .dockerignore | Specifies files and directories to exclude from Docker build context to reduce image size and avoid copying unnecessary files into containers. | dep: Docker
|
||||||
|
- Dockerfile | Multi-stage Docker build for a Python application with Docker socket access, Cloudflare tunneling, and database dependency waiting | dep: python:3.11-slim, gcc, libpq-dev, docker-ce-cli, docker-compose-plugin, cloudflared, uvicorn, pyproject.toml dependencies
|
||||||
|
- README.md | Documentation for a self-hosted FastAPI backend API that manages projects, git repositories, and development tools via Docker instances. | dep: FastAPI, SQLAlchemy, PostgreSQL, asyncpg, Alembic, Docker, Docker Compose, Authentik, uvicorn, pytest, ruff, mypy
|
||||||
|
- alembic.ini | Configuration file for Alembic database migration tool connecting to a PostgreSQL database with async driver | dep: alembic, sqlalchemy, asyncpg, PostgreSQL
|
||||||
|
- pyproject.toml | Defines Python project metadata, dependencies, and tool configurations for a FastAPI-based backend API called "headquarter-api" | dep: fastapi, uvicorn, sqlalchemy, asyncpg, alembic, pydantic, pydantic-settings, python-multipart, httpx, structlog, cryptography, pytest, pytest-asyncio, mypy, ruff, aiosqlite
|
||||||
|
- uv.lock | Lock file for the uv Python package manager that pins exact dependency versions and their artifact hashes for reproducible installations | dep: uv, Python 3.11+, aiosqlite, alembic, annotated-doc, annotated-types, anyio, ast-serialize, asyncpg, and many other PyPI packages
|
||||||
|
- wait-for-db.sh | Wait for a PostgreSQL database to become available before executing a command, with configurable retry logic. | dep: nc (netcat), sh (POSIX shell), sleep
|
||||||
|
## arch
|
||||||
|
Async Python/FastAPI with PostgreSQL (Alembic migrations), multi-stage Docker deployment with Cloudflare tunneling, uv package management, and containerized service orchestration.
|
||||||
|
## tags
|
||||||
|
docker, alembic, python, database, fastapi, postgresql, asyncpg, uvicorn
|
||||||
|
## symbols
|
||||||
|
-
|
||||||
|
## workflows
|
||||||
|
-
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# apps/api/alembic (index)
|
||||||
|
dir: apps/api/alembic
|
||||||
|
|
||||||
|
## role
|
||||||
|
Database migration infrastructure for the API application, providing version-controlled schema evolution with async SQLAlchemy support.
|
||||||
|
## parent
|
||||||
|
index: apps/api/.pi-map.index.md
|
||||||
|
map: apps/api/.pi-map.md
|
||||||
|
## children
|
||||||
|
- apps/api/alembic/versions
|
||||||
|
index: apps/api/alembic/versions/.pi-map.index.md
|
||||||
|
map: apps/api/alembic/versions/.pi-map.md
|
||||||
|
## files
|
||||||
|
- env.py
|
||||||
|
- script.py.mako
|
||||||
|
## links
|
||||||
|
index: apps/api/alembic/.pi-map.index.md
|
||||||
|
map: apps/api/alembic/.pi-map.md
|
||||||
|
## workflows
|
||||||
|
- change alembic behavior
|
||||||
|
read: env.py, script.py.mako
|
||||||
|
- explore alembic subdirectories
|
||||||
|
index: apps/api/alembic/versions/.pi-map.index.md
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# apps/api/alembic
|
||||||
|
dir: apps/api/alembic
|
||||||
|
|
||||||
|
index: apps/api/alembic/.pi-map.index.md
|
||||||
|
|
||||||
|
## role
|
||||||
|
Database migration infrastructure for the API application, providing version-controlled schema evolution with async SQLAlchemy support.
|
||||||
|
## files
|
||||||
|
- env.py | Configures Alembic database migration environment with async SQLAlchemy support for a project. | exp: func:run_migrations_offline() → None, call:context.configure, call:context.begin_transaction, call:context.run_migrations, func:do_run_migrations(connection: Connection) → None, call:context.configure, call:context.begin_transaction, call:context.run_migrations, func:run_async_migrations() → None, call:async_engine_from_config, call:config.get_section, call:connectable.connect, call:connection.run_sync, call:connectable.dispose, func:run_migrations_online() → None, call:asyncio.run, call:run_async_migrations | dep: logging.config, alembic, sqlalchemy, sqlalchemy.engine, sqlalchemy.ext.asyncio, src.config, src.models, asyncio
|
||||||
|
- script.py.mako | Alembic database migration script template that generates upgrade/downgrade functions for SQLAlchemy schema migrations | dep: alembic, sqlalchemy
|
||||||
|
## arch
|
||||||
|
Alembic migration framework with Mako templating for generating revision scripts, async SQLAlchemy engine configuration, and autogenerate capabilities for schema change tracking.
|
||||||
|
## tags
|
||||||
|
migrations, run, sqlalchemy, async, alembic, call:context.configure, call:context.begin, transaction
|
||||||
|
## symbols
|
||||||
|
- run_migrations_offline
|
||||||
|
- do_run_migrations
|
||||||
|
- run_async_migrations
|
||||||
|
- run_migrations_online
|
||||||
|
- call:context.configure
|
||||||
|
- call:context.begin_transaction
|
||||||
|
- call:context.run_migrations
|
||||||
|
- call:async_engine_from_config
|
||||||
|
## workflows
|
||||||
|
- change alembic behavior
|
||||||
|
read: env.py, script.py.mako
|
||||||
|
- explore alembic subdirectories
|
||||||
|
index: apps/api/alembic/versions/.pi-map.index.md
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
# apps/api/alembic/versions (index)
|
||||||
|
dir: apps/api/alembic/versions
|
||||||
|
|
||||||
|
## role
|
||||||
|
Manages incremental database schema evolution for the API application using Alembic migrations, tracking all table creations, column additions, relationship changes, and data transformations over the project's lifecycle.
|
||||||
|
## parent
|
||||||
|
index: apps/api/alembic/.pi-map.index.md
|
||||||
|
map: apps/api/alembic/.pi-map.md
|
||||||
|
## children
|
||||||
|
-
|
||||||
|
## files
|
||||||
|
- 0001_initial_schema.py
|
||||||
|
- 0002_refresh_tokens.py
|
||||||
|
- 0003_user_configs.py
|
||||||
|
- 0004_tool_types.py
|
||||||
|
- 0005_ssh_keys_timestamps.py
|
||||||
|
- 0006_tool_instances.py
|
||||||
|
- 0007_instance_container_name.py
|
||||||
|
- 0008_tool_type_category.py
|
||||||
|
- 0009_tool_configs.py
|
||||||
|
- 0010_tool_type_default_port.py
|
||||||
|
- 0011_tool_instance_tunnel_fields.py
|
||||||
|
- 0012_default_port_req.py
|
||||||
|
- 0013_add_config_profiles.py
|
||||||
|
- 0013_add_probe_result.py
|
||||||
|
- 0014_add_profile_resolver_fields.py
|
||||||
|
- 0014_merge_heads.py
|
||||||
|
- 0015_single_interface.py
|
||||||
|
- 069d3da4dc9b_add_ssh_key_id_to_config_profiles.py
|
||||||
|
- 20260527160017_add_pi_agent_tool_type.py
|
||||||
|
- 2026_05_22_add_clone_mode.py
|
||||||
|
- 2026_05_23_remove_is_builtin.py
|
||||||
|
- 2026_05_24_220141_add_startup_command.py
|
||||||
|
- 2026_05_24_add_config_profiles.py
|
||||||
|
- 2026_05_26_add_git_mounts.py
|
||||||
|
- 2026_05_27_external_repos.py
|
||||||
|
- 2026_05_28_add_monitoring_tables.py
|
||||||
|
- 2026_05_28_add_terminal_sessions_table.py
|
||||||
|
- 2026_05_28_add_tool_definition_manifests.py
|
||||||
|
- 2026_05_28_drop_tool_configs_and_config_folders.py
|
||||||
|
- 2026_05_29_add_notifications_table.py
|
||||||
|
- 2026_05_29_add_ssh_key_ids_to_tool_instances.py
|
||||||
|
- 2026_05_29_drop_ssh_key_id_from_config_profiles.py
|
||||||
|
- 2026_05_29_fix_code_server_bind_addr.py
|
||||||
|
- 2026_05_29_fix_code_server_bind_addr_port.py
|
||||||
|
- 2026_05_29_fix_web_tool_bind_address.py
|
||||||
|
- 2026_05_29_remove_lsio_command_override.py
|
||||||
|
- 2026_05_29_remove_ssh_keys_mount_from_manifest.py
|
||||||
|
- 2026_06_01_add_workspaces.py
|
||||||
|
- 2026_06_13_make_clone_mode_nullable.py
|
||||||
|
- 398082499c30_add_tool_config_fields.py
|
||||||
|
- 6fc7bfcf199f_merge_remove_is_builtin_and_add_config_.py
|
||||||
|
- 86cec91fdb00_merge_profile_resolver_and_workspaces_.py
|
||||||
|
- 8c6d1dbd4798_remove_pi_config_and_state_mounts_from_.py
|
||||||
|
- 8ed7dd80973d_create_config_folders_table.py
|
||||||
|
- af8512103d67_add_tool_type_fields.py
|
||||||
|
- f3d2dc90ba3a_merge_single_interface_and_clone_mode.py
|
||||||
|
## links
|
||||||
|
index: apps/api/alembic/versions/.pi-map.index.md
|
||||||
|
map: apps/api/alembic/versions/.pi-map.md
|
||||||
|
## workflows
|
||||||
|
- change versions behavior
|
||||||
|
read: 0001_initial_schema.py, 0002_refresh_tokens.py, 0003_user_configs.py
|
||||||
|
- change versions CLI
|
||||||
|
read: 2026_05_24_220141_add_startup_command.py, 2026_05_29_remove_lsio_command_override.py
|
||||||
|
- change versions config
|
||||||
|
read: 0003_user_configs.py, 0009_tool_configs.py, 0013_add_config_profiles.py
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
# apps/api/alembic/versions
|
||||||
|
dir: apps/api/alembic/versions
|
||||||
|
|
||||||
|
index: apps/api/alembic/versions/.pi-map.index.md
|
||||||
|
|
||||||
|
## role
|
||||||
|
Manages incremental database schema evolution for the API application using Alembic migrations, tracking all table creations, column additions, relationship changes, and data transformations over the project's lifecycle.
|
||||||
|
## files
|
||||||
|
- 0001_initial_schema.py | Defines the initial database schema migration creating five tables (users, ssh_keys, projects, git_repositories, user_configs) with relationships, indexes, and constraints using Alembic. | exp: func:upgrade() → None, call:op.create_table, call:sa.Column, call:sa.String, call:postgresql.UUID, call:sa.DateTime, call:sa.func.now, call:sa.PrimaryKeyConstraint, call:sa.UniqueConstraint, call:op.create_index, call:op.f, call:sa.Text, call:sa.ForeignKeyConstraint, call:sa.Boolean, call:postgresql.JSONB, func:downgrade() → None, call:op.drop_table, call:op.drop_index, call:op.f | dep: alembic, sqlalchemy.dialects, sqlalchemy, postgresql dialect
|
||||||
|
- 0002_refresh_tokens.py | Alembic database migration that creates a refresh_tokens table with indexes for user authentication token management | exp: func:upgrade() → None, call:op.get_bind, call:sa.inspect, call:inspector.has_table, call:op.create_table, call:sa.Column, call:postgresql.UUID, call:sa.String, call:sa.DateTime, call:sa.ForeignKeyConstraint, call:sa.PrimaryKeyConstraint, call:sa.UniqueConstraint, call:inspector.get_indexes, call:op.f, call:op.create_index, func:downgrade() → None, call:op.get_bind, call:sa.inspect, call:inspector.has_table, call:inspector.get_indexes, call:op.f, call:op.drop_index, call:op.drop_table | dep: alembic, sqlalchemy.dialects, sqlalchemy, sqlalchemy.dialects.postgresql
|
||||||
|
- 0003_user_configs.py | Alembic database migration that creates a user_configs table with JSON configuration storage linked to users | exp: func:upgrade() → None, call:op.create_table, call:sa.Column, call:sa.UUID, call:sa.JSON, call:sa.DateTime, call:sa.text, call:sa.ForeignKeyConstraint, call:sa.PrimaryKeyConstraint, call:sa.UniqueConstraint, func:downgrade() → None, call:op.drop_table | dep: typing, alembic, sqlalchemy
|
||||||
|
- 0004_tool_types.py | Alembic database migration that creates a tool_types table with metadata, templates, and versioning columns for a tool management system. | exp: func:upgrade() → None, call:op.create_table, call:sa.Column, call:sa.Uuid, call:sa.String, call:sa.Text, call:sa.JSON, call:sa.Boolean, call:sa.ForeignKey, call:sa.DateTime, call:sa.text, func:downgrade() → None, call:op.drop_table | dep: typing, alembic, sqlalchemy
|
||||||
|
- 0005_ssh_keys_timestamps.py | Alembic database migration that adds created_at and updated_at timestamp columns to the ssh_keys table | exp: func:upgrade() → None, call:op.add_column, call:sa.Column, call:sa.DateTime, call:sa.text, func:downgrade() → None, call:op.drop_column | dep: typing, alembic, sqlalchemy
|
||||||
|
- 0006_tool_instances.py | Alembic database migration that creates a tool_instances table with columns for tracking deployed tool instances, their status, container info, and foreign key relationships to tool_types, git_repositories, projects, and users. | exp: func:upgrade() → None, call:op.create_table, call:sa.Column, call:postgresql.UUID, call:sa.text, call:sa.String, call:sa.Integer, call:sa.DateTime, call:sa.ForeignKeyConstraint, call:sa.PrimaryKeyConstraint, call:op.create_index, func:downgrade() → None, call:op.drop_index, call:op.drop_table | dep: typing, alembic, sqlalchemy.dialects, sqlalchemy, sqlalchemy.dialects.postgresql
|
||||||
|
- 0007_instance_container_name.py | Alembic database migration that adds a nullable container_name column to the tool_instances table | exp: func:upgrade() → None, call:op.add_column, call:sa.Column, call:sa.String, func:downgrade() → None, call:op.drop_column | dep: typing, alembic, sqlalchemy
|
||||||
|
- 0008_tool_type_category.py | Alembic database migration that adds `category` and `interfaces` columns to the `tool_types` table | exp: func:upgrade() → None, call:op.add_column, call:sa.Column, call:sa.String, call:sa.JSON, func:downgrade() → None, call:op.drop_column | dep: typing, alembic, sqlalchemy
|
||||||
|
- 0009_tool_configs.py | Alembic database migration that creates a tool_configs table with UUID keys, foreign key relationships, and indexes for storing user/project tool configuration settings. | exp: func:upgrade() → None, call:op.create_table, call:sa.Column, call:postgresql.UUID, call:sa.text, call:sa.String, call:sa.Text, call:sa.DateTime, call:sa.ForeignKeyConstraint, call:sa.PrimaryKeyConstraint, call:op.create_index, func:downgrade() → None, call:op.drop_index, call:op.drop_table | dep: typing, alembic, sqlalchemy.dialects, sqlalchemy, postgresql dialect
|
||||||
|
- 0010_tool_type_default_port.py | Alembic database migration that adds a nullable default_port column to the tool_types table. | exp: func:upgrade() → None, call:op.add_column, call:sa.Column, call:sa.Integer, func:downgrade() → None, call:op.drop_column | dep: typing, alembic, sqlalchemy
|
||||||
|
- 0011_tool_instance_tunnel_fields.py | Alembic database migration that adds tunnel-related fields (public_url and tunnel_id) to the tool_instances table | exp: func:upgrade() → None, call:op.add_column, call:sa.Column, call:sa.String, func:downgrade() → None, call:op.drop_column | dep: typing, alembic, sqlalchemy
|
||||||
|
- 0012_default_port_req.py | Alembic database migration that populates null default_port values for existing tool types and then makes the column non-nullable | exp: func:upgrade() → None, call:op.execute, call:op.alter_column, call:sa.Integer, func:downgrade() → None, call:op.alter_column, call:sa.Integer | dep: typing, alembic, sqlalchemy
|
||||||
|
- 0013_add_config_profiles.py | Alembic database migration that adds config profiles, includes, mounts tables and links tool instances to profiles with defensive idempotent checks | exp: func:_table_exists(table_name: str) → bool, call:sa.inspect(op.get_bind()).has_table, call:op.get_bind, func:_column_exists(table_name: str, column_name: str) → bool, call:_table_exists, call:sa.inspect(op.get_bind()).get_columns, call:op.get_bind, func:_index_exists(table_name: str, index_name: str) → bool, call:_table_exists, call:sa.inspect(op.get_bind()).get_indexes, call:op.get_bind, func:_foreign_key_exists(table_name: str, constrained_columns: list[str], referred_table: str) → bool, call:_table_exists, call:sa.inspect(op.get_bind()).get_foreign_keys, call:op.get_bind, call:foreign_key.get, func:upgrade() → None, call:_table_exists, call:op.create_table, call:sa.Column, call:postgresql.UUID, call:sa.String, call:sa.Text, call:sa.DateTime, call:sa.text, call:sa.ForeignKeyConstraint, call:sa.PrimaryKeyConstraint, call:sa.UniqueConstraint, call:_index_exists, call:op.create_index, call:sa.Integer, call:_column_exists, call:op.add_column, call:_foreign_key_exists, call:op.create_foreign_key, func:downgrade() → None, call:op.drop_index, call:op.drop_constraint, call:op.drop_column, call:op.drop_table | dep: collections.abc, alembic, sqlalchemy.dialects, sqlalchemy, sqlalchemy.dialects.postgresql
|
||||||
|
- 0013_add_probe_result.py | Alembic database migration that adds a JSON probe_result column to the tool_instances table | exp: func:upgrade() → None, call:op.add_column, call:sa.Column, func:downgrade() → None, call:op.drop_column | dep: typing, alembic, sqlalchemy.dialects, sqlalchemy, sqlalchemy.dialects.postgresql
|
||||||
|
- 0014_add_profile_resolver_fields.py | Alembic database migration that adds profile resolver fields (project_id, tool_type_id, environment_variables, etc.) to config_profiles table and restructures config_mounts table (renaming mount_path to target_path, adding mode/files, removing content/source_profile_id). | exp: func:_table_exists(table_name: str) → bool, call:sa.inspect(op.get_bind()).has_table, call:op.get_bind, func:_column_exists(table_name: str, column_name: str) → bool, call:_table_exists, call:sa.inspect(op.get_bind()).get_columns, call:op.get_bind, func:_index_exists(table_name: str, index_name: str) → bool, call:_table_exists, call:sa.inspect(op.get_bind()).get_indexes, call:op.get_bind, func:_foreign_key_exists(table_name: str, constrained_columns: list[str], referred_table: str) → bool, call:_table_exists, call:sa.inspect(op.get_bind()).get_foreign_keys, call:op.get_bind, call:foreign_key.get, func:_foreign_key_names_for_column(table_name: str, column_name: str) → list[str], call:_table_exists, call:sa.inspect(op.get_bind()).get_foreign_keys, call:op.get_bind, call:foreign_key.get, call:names.append, func:upgrade() → None, call:_column_exists, call:op.add_column, call:sa.Column, call:postgresql.UUID, call:sa.JSON, call:sa.Text, call:sa.Integer, call:sa.Boolean, call:_foreign_key_exists, call:op.create_foreign_key, call:_index_exists, call:op.create_index, call:op.alter_column, call:sa.String, call:_foreign_key_names_for_column, call:op.drop_constraint, call:op.drop_column, func:downgrade() → None, call:op.add_column, call:sa.Column, call:postgresql.UUID, call:sa.Text, call:op.drop_column, call:op.alter_column, call:op.drop_index, call:op.drop_constraint | dep: collections.abc, alembic, sqlalchemy.dialects, sqlalchemy, sqlalchemy.dialects.postgresql
|
||||||
|
- 0014_merge_heads.py | Alembic merge migration that reconciles two divergent migration branches into a single history line | exp: func:upgrade() → None, func:downgrade() → None | dep: typing, alembic
|
||||||
|
- 0015_single_interface.py | Alembic database migration that replaces a JSON array `interfaces` column with `interface_type` string and `requires_port` boolean columns in the `tool_types` table, with dialect-specific data migration for PostgreSQL and SQLite. | exp: func:_get_dialect() → str, call:op.get_bind, func:upgrade() → None, call:_get_dialect, call:op.add_column, call:sa.Column, call:sa.String, call:sa.Boolean, call:op.execute, call:op.alter_column, call:op.drop_column, call:op.create_check_constraint, call:sa.text, func:downgrade() → None, call:_get_dialect, call:op.drop_constraint, call:op.add_column, call:sa.Column, call:postgresql.JSONB, call:sa.Text, call:op.execute, call:sa.JSON, call:op.drop_column | dep: typing, alembic, sqlalchemy.dialects, sqlalchemy, postgresql (dialect)
|
||||||
|
- 069d3da4dc9b_add_ssh_key_id_to_config_profiles.py | Alembic database migration that adds a nullable UUID foreign key column `ssh_key_id` to the `config_profiles` table referencing `ssh_keys.id` with SET NULL on delete | exp: func:upgrade() → None, call:op.add_column, call:sa.Column, call:sa.Uuid, call:sa.ForeignKey, func:downgrade() → None, call:op.drop_column | dep: alembic, sqlalchemy
|
||||||
|
- 20260527160017_add_pi_agent_tool_type.py | Alembic database migration that adds a "pi-agent" terminal-based coding tool type to a tool_types table with Docker configuration templates | exp: func:upgrade() → None, call:op.get_bind, call:conn.execute( sa.text("SELECT id FROM tool_types WHERE name = 'pi-agent'") ).fetchone, call:sa.text, call:json.dumps, func:downgrade() → None, call:op.get_bind, call:conn.execute, call:sa.text | dep: json, typing, alembic, uuid, sqlalchemy
|
||||||
|
- 2026_05_22_add_clone_mode.py | Alembic database migration that adds ssh_key_id foreign key to git_repositories table and clone_mode/branch columns to tool_instances table | exp: func:upgrade() → None, call:op.add_column, call:sa.Column, call:postgresql.UUID, call:op.create_foreign_key, call:sa.String, func:downgrade() → None, call:op.drop_column, call:op.drop_constraint | dep: alembic, sqlalchemy.dialects, sqlalchemy, sqlalchemy.dialects.postgresql
|
||||||
|
- 2026_05_23_remove_is_builtin.py | Alembic database migration to remove the `is_builtin` column from the `tool_types` table | exp: func:upgrade() → None, call:op.execute, func:downgrade() → None, call:op.add_column, call:sa.Column, call:sa.Boolean | dep: alembic, sqlalchemy
|
||||||
|
- 2026_05_24_220141_add_startup_command.py | Alembic database migration that adds a nullable `startup_command` text column to the `tool_types` table. | exp: func:upgrade() → None, call:op.add_column, call:sa.Column, call:sa.Text, func:downgrade() → None, call:op.drop_column | dep: typing, alembic, sqlalchemy
|
||||||
|
- 2026_05_24_add_config_profiles.py | Alembic database migration that creates config_profiles and config_profile_includes tables with indexes, and adds a foreign key column to tool_instances for managing user configuration profiles. | exp: func:upgrade() → None, call:op.create_table, call:sa.Column, call:postgresql.UUID, call:sa.text, call:sa.ForeignKey, call:sa.String, call:sa.Text, call:postgresql.JSONB, call:sa.Boolean, call:sa.DateTime, call:sa.PrimaryKeyConstraint, call:sa.UniqueConstraint, call:op.create_index, call:sa.Integer, call:op.add_column, func:downgrade() → None, call:op.drop_index, call:op.drop_column, call:op.drop_table | dep: typing, alembic, sqlalchemy.dialects, sqlalchemy, postgresql dialect
|
||||||
|
- 2026_05_26_add_git_mounts.py | Alembic database migration that adds a git_mounts JSON column to the config_profiles table | exp: func:upgrade() → None, call:op.add_column, call:sa.Column, call:sa.JSON, func:downgrade() → None, call:op.drop_column | dep: typing, alembic, sqlalchemy
|
||||||
|
- 2026_05_27_external_repos.py | Alembic database migration that makes project_id nullable in git_repositories table to support external repositories and expands alembic_version version_num column to 64 characters. | exp: func:upgrade() → None, call:op.execute, call:op.alter_column, call:sa.UUID, func:downgrade() → None, call:op.alter_column, call:sa.UUID, call:op.execute | dep: typing, alembic, sqlalchemy
|
||||||
|
- 2026_05_28_add_monitoring_tables.py | Alembic database migration that creates monitoring tables (instance_events and health_checks) with indexes for tracking tool instance events and health checks | exp: func:upgrade() → None, call:op.create_table, call:sa.Column, call:sa.Uuid, call:sa.String, call:sa.Text, call:sa.JSON, call:sa.DateTime, call:sa.func.now, call:sa.ForeignKeyConstraint, call:sa.PrimaryKeyConstraint, call:op.create_index, call:sa.Boolean, call:sa.Integer, func:downgrade() → None, call:op.drop_index, call:op.drop_table | dep: collections.abc, alembic, sqlalchemy
|
||||||
|
- 2026_05_28_add_terminal_sessions_table.py | Alembic database migration that creates a terminal_sessions table with tracking columns and foreign key to tool_instances | exp: func:upgrade() → None, call:op.create_table, call:sa.Column, call:sa.UUID, call:sa.String, call:sa.DateTime, call:sa.text, call:sa.ForeignKeyConstraint, call:sa.PrimaryKeyConstraint, call:op.create_index, call:op.f, func:downgrade() → None, call:op.drop_index, call:op.f, call:op.drop_table | dep: collections.abc, alembic, sqlalchemy
|
||||||
|
- 2026_05_28_add_tool_definition_manifests.py | Alembic database migration that creates a tool_definition_manifests table, adds manifest-related columns to tool_types and tool_instances, and migrates the pi-agent tool from Dockerfile-based to manifest-based definitions with seed data. | exp: func:upgrade() → None, call:op.get_bind, call:op.create_table, call:sa.Column, call:sa.UUID, call:sa.String, call:sa.Text, call:sa.JSON, call:sa.Boolean, call:sa.TIMESTAMP, call:sa.func.now, call:sa.PrimaryKeyConstraint, call:sa.UniqueConstraint, call:sa.ForeignKeyConstraint, call:sa.CheckConstraint, call:conn.execute, call:sa.text, call:result.fetchone, call:op.add_column, call:op.create_foreign_key, call:op.drop_constraint, call:op.execute, call:json.dumps, call:str, func:downgrade() → None, call:op.get_bind, call:conn.execute, call:sa.text, call:result.fetchone, call:op.drop_column, call:op.drop_constraint, call:op.drop_table | dep: json, uuid, typing, alembic, sqlalchemy
|
||||||
|
- 2026_05_28_drop_tool_configs_and_config_folders.py | Alembic database migration that drops `tool_configs` and `config_folders` tables with conditional existence checks and full downgrade recreation | exp: func:upgrade() → None, call:op.get_bind, call:conn.execute, call:sa.text, call:result.fetchone, call:op.drop_table, func:downgrade() → None, call:op.create_table, call:sa.Column, call:sa.UUID, call:sa.String, call:sa.Text, call:sa.JSON, call:sa.Boolean, call:sa.TIMESTAMP, call:sa.func.now, call:sa.PrimaryKeyConstraint, call:sa.Integer | dep: typing, alembic, sqlalchemy
|
||||||
|
- 2026_05_29_add_notifications_table.py | Alembic database migration that creates a notifications table with user-linked, categorized, severity-graded messages supporting read/dismissed tracking and optimized querying indexes. | exp: func:upgrade() → None, call:op.create_table, call:sa.Column, call:sa.Uuid, call:sa.String, call:sa.Text, call:sa.JSON, call:sa.DateTime, call:sa.func.now, call:sa.ForeignKeyConstraint, call:sa.PrimaryKeyConstraint, call:op.create_index, call:sa.text, func:downgrade() → None, call:op.drop_index, call:op.drop_table | dep: collections.abc, alembic, sqlalchemy
|
||||||
|
- 2026_05_29_add_ssh_key_ids_to_tool_instances.py | Alembic database migration that adds a JSON column named ssh_key_ids to the tool_instances table | exp: func:upgrade() → None, call:op.add_column, call:sa.Column, call:sa.JSON, func:downgrade() → None, call:op.drop_column | dep: alembic, sqlalchemy
|
||||||
|
- 2026_05_29_drop_ssh_key_id_from_config_profiles.py | Alembic database migration that removes the ssh_key_id column from the config_profiles table | exp: func:upgrade() → None, call:op.drop_column, func:downgrade() → None, call:op.add_column, call:sa.Column, call:sa.Uuid, call:sa.ForeignKey | dep: alembic, sqlalchemy
|
||||||
|
- 2026_05_29_fix_code_server_bind_addr.py | Alembic database migration that fixes code-server tool type compose templates by replacing deprecated `--bind-addr` flag with `--host` flag | exp: func:upgrade() → None, call:op.get_bind, call:conn.execute( sa.text(""" SELECT id, compose_template FROM tool_types WHERE name = 'code-server' AND compose_template LIKE '%--bind-addr%' """) ).fetchall, call:sa.text, call:compose_template.replace( "--bind-addr 0.0.0.0:8443", "--host 0.0.0.0" ).replace, call:print, func:downgrade() → None | dep: typing, alembic, sqlalchemy
|
||||||
|
- 2026_05_29_fix_code_server_bind_addr_port.py | Alembic database migration that fixes code-server Docker compose templates and instance files by replacing broken `--host` flags with correct `--bind-addr 0.0.0.0:port` configurations | exp: func:_fix_tool_type_templates(conn) → None, call:conn.execute( sa.text(""" SELECT id, compose_template, default_port FROM tool_types WHERE name = 'code-server' AND compose_template LIKE '%--host%' """) ).fetchall, call:sa.text, call:compose_template.split, call:len, call:line.lstrip, call:new_lines.append, call:"\n".join, call:print, func:_fix_instance_compose_files(conn) → None, call:conn.execute( sa.text(""" SELECT column_name FROM information_schema.columns WHERE table_name = 'tool_instances' AND column_name = 'compose_path' """) ).fetchone, call:sa.text, call:print, call:conn.execute( sa.text(""" SELECT id, compose_path, tool_type_id FROM tool_instances WHERE compose_path IS NOT NULL """) ).fetchall, call:Path, call:path.exists, call:path.read_text, call:conn.execute( sa.text(""" SELECT default_port FROM tool_types WHERE id = :id """), {"id": tool_type_id}, ).fetchone, call:yaml.safe_load, call:data["services"].values, call:path.write_text, call:yaml.dump, func:upgrade() → None, call:op.get_bind, call:_fix_tool_type_templates, call:_fix_instance_compose_files, func:downgrade() → None | dep: typing, alembic, yaml, pathlib, sqlalchemy
|
||||||
|
- 2026_05_29_fix_web_tool_bind_address.py | Alembic database migration that updates code-server and jupyter-notebook tool type compose templates to bind to 0.0.0.0 | exp: func:_fix_code_server_compose(conn) → None, call:conn.execute( sa.text(""" SELECT id, compose_template, definition_type FROM tool_types WHERE name = 'code-server' """) ).fetchone, call:sa.text, call:compose_template.split, call:enumerate, call:len, call:line.lstrip, call:new_lines.append, call:image_line.lstrip, call:new_lines.index, call:new_lines.insert, call:"\n".join, call:print, func:_fix_jupyter_compose(conn) → None, call:conn.execute( sa.text(""" SELECT id, compose_template, definition_type FROM tool_types WHERE name = 'jupyter-notebook' """) ).fetchone, call:sa.text, call:compose_template.split, call:enumerate, call:new_lines.append, call:len, call:line.lstrip, call:"\n".join, call:print, func:upgrade() → None, call:op.get_bind, call:_fix_code_server_compose, call:_fix_jupyter_compose, func:downgrade() → None | dep: typing, alembic, sqlalchemy
|
||||||
|
- 2026_05_29_remove_lsio_command_override.py | Alembic database migration that removes broken command overrides containing --bind-addr or --host flags from LinuxServer.io code-server Docker Compose templates in both database tool_types records and on-disk instance compose files. | exp: func:upgrade() → None, call:op.get_bind, call:conn.execute( sa.text(""" SELECT id, compose_template FROM tool_types WHERE name = 'code-server' """) ).fetchall, call:sa.text, call:yaml.safe_load, call:data["services"].values, call:svc.get, call:yaml.dump, call:print, call:conn.execute( sa.text(""" SELECT column_name FROM information_schema.columns WHERE table_name = 'tool_instances' AND column_name = 'compose_path' """) ).fetchone, call:conn.execute( sa.text(""" SELECT id, compose_path FROM tool_instances WHERE compose_path IS NOT NULL """) ).fetchall, call:Path, call:path.exists, call:path.read_text, call:path.write_text, func:downgrade() → None | dep: collections.abc, alembic, yaml, pathlib, sqlalchemy, pathlib.Path, information_schema
|
||||||
|
- 2026_05_29_remove_ssh_keys_mount_from_manifest.py | Alembic database migration that removes (or restores) the ssh_keys mount from a JSON manifest stored in the tool_definition_manifests table for the pi-agent tool definition. | exp: func:upgrade() → None, call:op.get_bind, call:conn.execute, call:sa.text, call:result.fetchone, call:isinstance, call:json.loads, call:manifest.get, call:len, call:m.get, call:json.dumps, func:downgrade() → None, call:op.get_bind, call:conn.execute, call:sa.text, call:result.fetchone, call:isinstance, call:json.loads, call:manifest.get, call:any, call:m.get, call:mounts.append, call:json.dumps | dep: json, typing, alembic, sqlalchemy
|
||||||
|
- 2026_06_01_add_workspaces.py | Alembic database migration that creates a workspaces table with foreign keys to git_repositories and users, adds indexes, and adds a workspace_id column to tool_instances | exp: func:upgrade() → None, call:op.create_table, call:sa.Column, call:sa.Uuid, call:sa.String, call:sa.ForeignKey, call:sa.DateTime, call:sa.text, call:sa.UniqueConstraint, call:op.create_index, call:op.add_column, func:downgrade() → None, call:op.drop_index, call:op.drop_column, call:op.drop_table | dep: collections.abc, alembic, sqlalchemy
|
||||||
|
- 2026_06_13_make_clone_mode_nullable.py | Alembic database migration that makes the `clone_mode` column in `tool_instances` table nullable to allow NULL values for new rows | exp: func:upgrade() → None, call:op.alter_column, call:sa.String, func:downgrade() → None, call:op.alter_column, call:sa.String | dep: alembic, sqlalchemy
|
||||||
|
- 398082499c30_add_tool_config_fields.py | Alembic database migration that adds five new columns (port_override, start_command, working_directory, environment_variables, volumes) to the tool_configs table with a port range check constraint. | exp: func:upgrade() → None, call:op.add_column, call:sa.Column, call:sa.Integer, call:sa.Text, call:postgresql.JSONB, call:op.create_check_constraint, call:sa.text, func:downgrade() → None, call:op.drop_constraint, call:op.drop_column | dep: alembic, sqlalchemy.dialects, sqlalchemy, sqlalchemy.dialects.postgresql
|
||||||
|
- 6fc7bfcf199f_merge_remove_is_builtin_and_add_config_.py | Alembic database migration that merges two parallel revision branches (removing is_builtin and adding config_profiles) into a single history line | exp: func:upgrade() → None, func:downgrade() → None | dep: alembic
|
||||||
|
- 86cec91fdb00_merge_profile_resolver_and_workspaces_.py | Alembic database migration that merges two divergent migration branches (profile resolver and workspaces) into a single head | exp: func:upgrade() → None, func:downgrade() → None | dep: alembic
|
||||||
|
- 8c6d1dbd4798_remove_pi_config_and_state_mounts_from_.py | Alembic database migration that removes pi_state and pi_config mounts from the pi-agent manifest in upgrade, and restores them in downgrade | exp: func:_load_manifest(manifest_json), call:isinstance, call:json.loads, func:upgrade() → None, call:op.get_bind, call:conn.execute, call:sa.text, call:result.fetchone, call:_load_manifest, call:manifest.get, call:len, call:m.get, call:json.dumps, func:downgrade() → None, call:op.get_bind, call:conn.execute, call:sa.text, call:result.fetchone, call:_load_manifest, call:manifest.get, call:m.get, call:mounts.append, call:json.dumps | dep: json, alembic, sqlalchemy
|
||||||
|
- 8ed7dd80973d_create_config_folders_table.py | Alembic database migration that creates a config_folders table with user-owned configuration folders supporting JSONB file storage and project overrides | exp: func:upgrade() → None, call:op.create_table, call:sa.Column, call:postgresql.UUID, call:sa.text, call:sa.ForeignKey, call:sa.String, call:sa.Text, call:postgresql.JSONB, call:sa.Boolean, call:sa.DateTime, call:sa.UniqueConstraint, call:op.create_index, func:downgrade() → None, call:op.drop_index, call:op.drop_table | dep: alembic, sqlalchemy.dialects, sqlalchemy, sqlalchemy.dialects.postgresql
|
||||||
|
- af8512103d67_add_tool_type_fields.py | Alembic database migration that adds new columns (definition_type, dockerfile_template, build_context, readiness_probe) to the tool_types table with a CHECK constraint on definition_type. | exp: func:upgrade() → None, call:op.add_column, call:sa.Column, call:sa.String, call:sa.Text, call:postgresql.JSONB, call:op.create_check_constraint, call:sa.text, func:downgrade() → None, call:op.drop_constraint, call:op.drop_column | dep: alembic, sqlalchemy.dialects, sqlalchemy, sqlalchemy.dialects.postgresql
|
||||||
|
- f3d2dc90ba3a_merge_single_interface_and_clone_mode.py | Alembic database migration that merges two prior revisions (single_interface and clone_mode) into a single migration path | exp: func:upgrade() → None, func:downgrade() → None | dep: typing, alembic
|
||||||
|
## arch
|
||||||
|
Linear and branched migration pattern using Alembic's revision system with merge migrations to reconcile divergent branches; each migration is an imperative upgrade/downgrade script containing raw SQL/DDL operations, with some migrations including data seeding and dialect-specific logic (PostgreSQL/SQLite), but lacks consistent naming convention (mixed timestamp and numeric prefixes) indicating organic evolution rather than planned schema design.
|
||||||
|
## tags
|
||||||
|
column, table, call:op.drop, downgrade, alembic, upgrade, key, call:sa.text
|
||||||
|
## symbols
|
||||||
|
- upgrade
|
||||||
|
- downgrade
|
||||||
|
- _table_exists
|
||||||
|
- _column_exists
|
||||||
|
- _index_exists
|
||||||
|
- _foreign_key_exists
|
||||||
|
- _foreign_key_names_for_column
|
||||||
|
- _get_dialect
|
||||||
|
## workflows
|
||||||
|
- change versions behavior
|
||||||
|
read: 0001_initial_schema.py, 0002_refresh_tokens.py, 0003_user_configs.py
|
||||||
|
- change versions CLI
|
||||||
|
read: 2026_05_24_220141_add_startup_command.py, 2026_05_29_remove_lsio_command_override.py
|
||||||
|
- change versions config
|
||||||
|
read: 0003_user_configs.py, 0009_tool_configs.py, 0013_add_config_profiles.py
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -232,20 +232,6 @@ def upgrade() -> None:
|
|||||||
"writable": True,
|
"writable": True,
|
||||||
"owner": "user",
|
"owner": "user",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "pi_state",
|
|
||||||
"target": "/tmp/.pi/agents",
|
|
||||||
"source_type": "instance",
|
|
||||||
"writable": True,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "pi_config",
|
|
||||||
"target": "/home/user/.pi",
|
|
||||||
"source_type": "git_mount",
|
|
||||||
"git_mount_ref": "dotfiles",
|
|
||||||
"writable": True,
|
|
||||||
"owner": "user",
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
"runtime": {
|
"runtime": {
|
||||||
"command": ["/bin/bash"],
|
"command": ["/bin/bash"],
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
"""make clone_mode nullable
|
||||||
|
|
||||||
|
Revision ID: 2026_06_13_make_clone_mode_nullable
|
||||||
|
Revises: 86cec91fdb00
|
||||||
|
Create Date: 2026-06-13 10:00:00.000000
|
||||||
|
"""
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = "2026_06_13_make_clone_mode_nullable"
|
||||||
|
down_revision = "86cec91fdb00"
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
# The workspace-first cleanup no longer writes clone_mode; existing rows
|
||||||
|
# keep their value, but new rows may be NULL.
|
||||||
|
op.alter_column(
|
||||||
|
"tool_instances",
|
||||||
|
"clone_mode",
|
||||||
|
existing_type=sa.String(20),
|
||||||
|
nullable=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
op.alter_column(
|
||||||
|
"tool_instances",
|
||||||
|
"clone_mode",
|
||||||
|
existing_type=sa.String(20),
|
||||||
|
nullable=False,
|
||||||
|
)
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
"""remove pi config and state mounts from pi-agent manifest
|
||||||
|
|
||||||
|
Revision ID: 8c6d1dbd4798
|
||||||
|
Revises: 2026_06_13_make_clone_mode_nullable
|
||||||
|
Create Date: 2026-06-13 11:21:25.983178
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '8c6d1dbd4798'
|
||||||
|
down_revision = '2026_06_13_make_clone_mode_nullable'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def _load_manifest(manifest_json):
|
||||||
|
return manifest_json if isinstance(manifest_json, dict) else json.loads(manifest_json)
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
"""Remove pi_state and pi_config mounts from the pi-agent manifest."""
|
||||||
|
conn = op.get_bind()
|
||||||
|
|
||||||
|
result = conn.execute(
|
||||||
|
sa.text(
|
||||||
|
"SELECT id, manifest FROM tool_definition_manifests WHERE name = 'pi-agent'"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
row = result.fetchone()
|
||||||
|
if not row:
|
||||||
|
return
|
||||||
|
|
||||||
|
manifest_id, manifest_json = row
|
||||||
|
manifest = _load_manifest(manifest_json)
|
||||||
|
|
||||||
|
mounts = manifest.get("mounts", [])
|
||||||
|
original_count = len(mounts)
|
||||||
|
|
||||||
|
filtered_mounts = [
|
||||||
|
m for m in mounts if m.get("name") not in ("pi_state", "pi_config")
|
||||||
|
]
|
||||||
|
|
||||||
|
if len(filtered_mounts) < original_count:
|
||||||
|
manifest["mounts"] = filtered_mounts
|
||||||
|
conn.execute(
|
||||||
|
sa.text(
|
||||||
|
"UPDATE tool_definition_manifests SET manifest = :manifest WHERE id = :id"
|
||||||
|
),
|
||||||
|
{
|
||||||
|
"manifest": json.dumps(manifest),
|
||||||
|
"id": manifest_id,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
"""Restore pi_state and pi_config mounts to the pi-agent manifest."""
|
||||||
|
conn = op.get_bind()
|
||||||
|
|
||||||
|
result = conn.execute(
|
||||||
|
sa.text(
|
||||||
|
"SELECT id, manifest FROM tool_definition_manifests WHERE name = 'pi-agent'"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
row = result.fetchone()
|
||||||
|
if not row:
|
||||||
|
return
|
||||||
|
|
||||||
|
manifest_id, manifest_json = row
|
||||||
|
manifest = _load_manifest(manifest_json)
|
||||||
|
|
||||||
|
mounts = manifest.get("mounts", [])
|
||||||
|
existing_names = {m.get("name") for m in mounts}
|
||||||
|
|
||||||
|
if "pi_state" not in existing_names:
|
||||||
|
mounts.append(
|
||||||
|
{
|
||||||
|
"name": "pi_state",
|
||||||
|
"target": "/tmp/.pi/agents",
|
||||||
|
"source_type": "instance",
|
||||||
|
"writable": True,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if "pi_config" not in existing_names:
|
||||||
|
mounts.append(
|
||||||
|
{
|
||||||
|
"name": "pi_config",
|
||||||
|
"target": "/home/user/.pi",
|
||||||
|
"source_type": "git_mount",
|
||||||
|
"git_mount_ref": "dotfiles",
|
||||||
|
"writable": True,
|
||||||
|
"owner": "user",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
manifest["mounts"] = mounts
|
||||||
|
conn.execute(
|
||||||
|
sa.text(
|
||||||
|
"UPDATE tool_definition_manifests SET manifest = :manifest WHERE id = :id"
|
||||||
|
),
|
||||||
|
{
|
||||||
|
"manifest": json.dumps(manifest),
|
||||||
|
"id": manifest_id,
|
||||||
|
},
|
||||||
|
)
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
# apps/api/src (index)
|
||||||
|
dir: apps/api/src
|
||||||
|
|
||||||
|
## role
|
||||||
|
Core API application package that initializes and configures the Headquarter FastAPI backend with database, authentication, logging, and middleware infrastructure.
|
||||||
|
## parent
|
||||||
|
index: apps/api/.pi-map.index.md
|
||||||
|
map: apps/api/.pi-map.md
|
||||||
|
## children
|
||||||
|
- apps/api/src/api
|
||||||
|
index: apps/api/src/api/.pi-map.index.md
|
||||||
|
map: apps/api/src/api/.pi-map.md
|
||||||
|
- apps/api/src/auth
|
||||||
|
index: apps/api/src/auth/.pi-map.index.md
|
||||||
|
map: apps/api/src/auth/.pi-map.md
|
||||||
|
- apps/api/src/headquarter_api.egg-info
|
||||||
|
index: apps/api/src/headquarter_api.egg-info/.pi-map.index.md
|
||||||
|
map: apps/api/src/headquarter_api.egg-info/.pi-map.md
|
||||||
|
- apps/api/src/models
|
||||||
|
index: apps/api/src/models/.pi-map.index.md
|
||||||
|
map: apps/api/src/models/.pi-map.md
|
||||||
|
- apps/api/src/schemas
|
||||||
|
index: apps/api/src/schemas/.pi-map.index.md
|
||||||
|
map: apps/api/src/schemas/.pi-map.md
|
||||||
|
- apps/api/src/scripts
|
||||||
|
index: apps/api/src/scripts/.pi-map.index.md
|
||||||
|
map: apps/api/src/scripts/.pi-map.md
|
||||||
|
- apps/api/src/seeds
|
||||||
|
index: apps/api/src/seeds/.pi-map.index.md
|
||||||
|
map: apps/api/src/seeds/.pi-map.md
|
||||||
|
- apps/api/src/services
|
||||||
|
index: apps/api/src/services/.pi-map.index.md
|
||||||
|
map: apps/api/src/services/.pi-map.md
|
||||||
|
- apps/api/src/utils
|
||||||
|
index: apps/api/src/utils/.pi-map.index.md
|
||||||
|
map: apps/api/src/utils/.pi-map.md
|
||||||
|
## files
|
||||||
|
- __init__.py
|
||||||
|
- config.py
|
||||||
|
- database.py
|
||||||
|
- logging_config.py
|
||||||
|
- main.py
|
||||||
|
## links
|
||||||
|
index: apps/api/src/.pi-map.index.md
|
||||||
|
map: apps/api/src/.pi-map.md
|
||||||
|
## workflows
|
||||||
|
- change src behavior
|
||||||
|
read: __init__.py, config.py, database.py
|
||||||
|
- change src config
|
||||||
|
read: config.py, logging_config.py
|
||||||
|
- explore src subdirectories
|
||||||
|
index: apps/api/src/api/.pi-map.index.md, apps/api/src/auth/.pi-map.index.md, apps/api/src/headquarter_api.egg-info/.pi-map.index.md
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
# apps/api/src
|
||||||
|
dir: apps/api/src
|
||||||
|
|
||||||
|
index: apps/api/src/.pi-map.index.md
|
||||||
|
|
||||||
|
## role
|
||||||
|
Core API application package that initializes and configures the Headquarter FastAPI backend with database, authentication, logging, and middleware infrastructure.
|
||||||
|
## files
|
||||||
|
- __init__.py | Marks the directory as a Python package for the Headquarter API.
|
||||||
|
- config.py | Defines application configuration settings with environment-based overrides using Pydantic, including database URLs, service domains, OAuth/Authentik integration, JWT/session settings, and computed properties for environment-specific behavior. | exp: class:Settings, func:build_database_url(user: str, password: str, host: str, port: int, database: str) → str | dep: pydantic, pydantic_settings
|
||||||
|
- database.py | Sets up async SQLAlchemy database engine and session factory, with retry logic for database connectivity testing and Alembic migration execution via subprocess. | exp: func:init_database(max_retries, retry_delay) → bool, call:range, call:engine.connect, call:test_conn.execute, call:text, call:test_conn.close, call:logger.info, call:asyncio.get_event_loop().run_in_executor, call:subprocess.run, call:os.path.dirname, call:os.path.abspath, call:logger.debug, call:logger.error, call:asyncio.sleep, call:str(exc).lower, call:logger.warning | dep: asyncio, logging, os, subprocess, sqlalchemy.ext.asyncio, sqlalchemy.pool, src.config, sqlalchemy
|
||||||
|
- logging_config.py | Configures structured JSON logging with correlation ID injection, custom formatters, and HTTP request/exception middleware for a FastAPI application. | exp: class:CorrelationIdFilter, method:filter(self, record: logging.LogRecord) → bool, call:get_correlation_id, class:JSONFormatter, method:format(self, record: logging.LogRecord) → str, call:self.formatTime, call:record.getMessage, call:getattr, call:self.formatException, call:json.dumps, method:formatTime(self, record: logging.LogRecord, datefmt) → str, call:time.strftime, call:time.gmtime, class:RequestLoggingMiddleware, method:dispatch(self, request: Request, call_next: Callable) → Response, call:time.time, call:logger.info, call:call_next, call:int, call:logger.error, call:type, call:traceback.format_exc, class:ExceptionLoggingMiddleware, method:dispatch(self, request: Request, call_next: Callable) → Response, call:call_next, call:logger.critical, call:traceback.format_exc, func:configure_logging(level) → None, call:JSONFormatter, call:logging.StreamHandler, call:console_handler.setFormatter, call:console_handler.addFilter, call:CorrelationIdFilter, call:root_logger.setLevel, call:logging.getLogger("uvicorn").setLevel, call:logging.getLogger("uvicorn.access").setLevel, call:logging.getLogger("sqlalchemy.engine").setLevel, call:logger.info, call:logging.getLevelName | dep: json, logging, sys, time, traceback, collections.abc, fastapi, starlette.middleware.base, src.services.shared.correlation
|
||||||
|
- main.py | Initializes and configures a FastAPI application for the "Headquarter API" with database setup, middleware, routing, and background services. | exp: func:_sanitize_validation_errors(errors), call:error.get, call:str, call:ctx.items, call:isinstance, call:type, call:sanitized.append, func:validation_exception_handler(request: Request, exc: RequestValidationError), call:exc.errors, call:logger.warning, call:_sanitize_validation_errors, call:JSONResponse, func:on_startup(), call:logger.info, call:init_database, call:logger.error, call:sys.exit, call:_health_monitor.start, call:seed_builtin_tool_types, func:on_shutdown(), call:logger.info, call:_health_monitor.stop | dep: logging, os, fastapi, fastapi.exceptions, fastapi.middleware.cors, fastapi.responses, fastapi.staticfiles, src.api.config, src.api.project, src.api.system, src.api.tool, src.api.user, src.api.workspace, src.config, src.models, src.database, src.logging_config, src.seeds.builtin_tool_types, src.services.instance, src.services.shared, sys, src.api.*
|
||||||
|
## arch
|
||||||
|
Layered architecture using Pydantic for environment-based configuration, async SQLAlchemy with Alembic migrations, structured JSON logging with correlation IDs, and FastAPI middleware/routing pattern for a service-oriented backend.
|
||||||
|
## tags
|
||||||
|
src, database, logging, call:logger.info, api, middleware, fastapi, filter
|
||||||
|
## symbols
|
||||||
|
- Settings
|
||||||
|
- CorrelationIdFilter
|
||||||
|
- JSONFormatter
|
||||||
|
- RequestLoggingMiddleware
|
||||||
|
- ExceptionLoggingMiddleware
|
||||||
|
- build_database_url
|
||||||
|
- init_database
|
||||||
|
- filter
|
||||||
|
## workflows
|
||||||
|
- change src behavior
|
||||||
|
read: __init__.py, config.py, database.py
|
||||||
|
- change src config
|
||||||
|
read: config.py, logging_config.py
|
||||||
|
- explore src subdirectories
|
||||||
|
index: apps/api/src/api/.pi-map.index.md, apps/api/src/auth/.pi-map.index.md, apps/api/src/headquarter_api.egg-info/.pi-map.index.md
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# apps/api/src/api (index)
|
||||||
|
dir: apps/api/src/api
|
||||||
|
|
||||||
|
## role
|
||||||
|
Defines shared API infrastructure including reusable Pydantic validators for consistent input validation across API endpoints.
|
||||||
|
## parent
|
||||||
|
index: apps/api/src/.pi-map.index.md
|
||||||
|
map: apps/api/src/.pi-map.md
|
||||||
|
## children
|
||||||
|
- apps/api/src/api/config
|
||||||
|
index: apps/api/src/api/config/.pi-map.index.md
|
||||||
|
map: apps/api/src/api/config/.pi-map.md
|
||||||
|
- apps/api/src/api/project
|
||||||
|
index: apps/api/src/api/project/.pi-map.index.md
|
||||||
|
map: apps/api/src/api/project/.pi-map.md
|
||||||
|
- apps/api/src/api/system
|
||||||
|
index: apps/api/src/api/system/.pi-map.index.md
|
||||||
|
map: apps/api/src/api/system/.pi-map.md
|
||||||
|
- apps/api/src/api/tool
|
||||||
|
index: apps/api/src/api/tool/.pi-map.index.md
|
||||||
|
map: apps/api/src/api/tool/.pi-map.md
|
||||||
|
- apps/api/src/api/user
|
||||||
|
index: apps/api/src/api/user/.pi-map.index.md
|
||||||
|
map: apps/api/src/api/user/.pi-map.md
|
||||||
|
- apps/api/src/api/workspace
|
||||||
|
index: apps/api/src/api/workspace/.pi-map.index.md
|
||||||
|
map: apps/api/src/api/workspace/.pi-map.md
|
||||||
|
## files
|
||||||
|
- __init__.py
|
||||||
|
- shared_validators.py
|
||||||
|
## links
|
||||||
|
index: apps/api/src/api/.pi-map.index.md
|
||||||
|
map: apps/api/src/api/.pi-map.md
|
||||||
|
## workflows
|
||||||
|
- change api behavior
|
||||||
|
read: __init__.py, shared_validators.py
|
||||||
|
- explore api subdirectories
|
||||||
|
index: apps/api/src/api/config/.pi-map.index.md, apps/api/src/api/project/.pi-map.index.md, apps/api/src/api/system/.pi-map.index.md
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# apps/api/src/api
|
||||||
|
dir: apps/api/src/api
|
||||||
|
|
||||||
|
index: apps/api/src/api/.pi-map.index.md
|
||||||
|
|
||||||
|
## role
|
||||||
|
Defines shared API infrastructure including reusable Pydantic validators for consistent input validation across API endpoints.
|
||||||
|
## files
|
||||||
|
- __init__.py | Marks the directory as a Python package for API routers.
|
||||||
|
- shared_validators.py | Provides reusable Pydantic validator functions for API schema validation including mount paths, files, environment variables, and volume mounts. | exp: func:validate_mount_path(v: str | None) → str | None, call:v.startswith, raise:ValueError, func:validate_files(v: dict | None, max_size_bytes) → dict | None, call:v.items, call:path.startswith, call:len, call:content.encode, raise:ValueError, func:validate_env_vars(v: dict | None) → dict | None, call:isinstance, raise:ValueError, func:validate_volumes(v: list | None) → list | None, call:isinstance, call:enumerate, raise:ValueError
|
||||||
|
## arch
|
||||||
|
Modular utility package with functional validation helpers using Pydantic for declarative schema enforcement.
|
||||||
|
## tags
|
||||||
|
validate, raise:value, error, call:isinstance, mount, api, init, path
|
||||||
|
## symbols
|
||||||
|
- validate_mount_path
|
||||||
|
- validate_files
|
||||||
|
- validate_env_vars
|
||||||
|
- validate_volumes
|
||||||
|
- call:v.startswith
|
||||||
|
- raise:ValueError
|
||||||
|
- call:v.items
|
||||||
|
- call:path.startswith
|
||||||
|
## workflows
|
||||||
|
- change api behavior
|
||||||
|
read: __init__.py, shared_validators.py
|
||||||
|
- explore api subdirectories
|
||||||
|
index: apps/api/src/api/config/.pi-map.index.md, apps/api/src/api/project/.pi-map.index.md, apps/api/src/api/system/.pi-map.index.md
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# apps/api/src/api/config (index)
|
||||||
|
dir: apps/api/src/api/config
|
||||||
|
|
||||||
|
## role
|
||||||
|
Provides FastAPI API endpoints for managing user configuration settings and configuration profiles.
|
||||||
|
## parent
|
||||||
|
index: apps/api/src/api/.pi-map.index.md
|
||||||
|
map: apps/api/src/api/.pi-map.md
|
||||||
|
## children
|
||||||
|
-
|
||||||
|
## files
|
||||||
|
- __init__.py
|
||||||
|
- config_profiles.py
|
||||||
|
- user_config.py
|
||||||
|
## links
|
||||||
|
index: apps/api/src/api/config/.pi-map.index.md
|
||||||
|
map: apps/api/src/api/config/.pi-map.md
|
||||||
|
## workflows
|
||||||
|
- change config behavior
|
||||||
|
read: __init__.py, config_profiles.py, user_config.py
|
||||||
|
- change config config
|
||||||
|
read: config_profiles.py, user_config.py
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# apps/api/src/api/config
|
||||||
|
dir: apps/api/src/api/config
|
||||||
|
|
||||||
|
index: apps/api/src/api/config/.pi-map.index.md
|
||||||
|
|
||||||
|
## role
|
||||||
|
Provides FastAPI API endpoints for managing user configuration settings and configuration profiles.
|
||||||
|
## files
|
||||||
|
- __init__.py | Aggregates and exports configuration-related API routers for the config module. | dep: src.api.config.config_profiles, src.api.config.user_config
|
||||||
|
- config_profiles.py | FastAPI router providing CRUD endpoints for user config profiles with includes, resolution, defaults, and git URL validation | exp: func:list_config_profiles(project_id, tool_type_id, current_user_id, session), call:select(ConfigProfile) .where(ConfigProfile.user_id == user_uuid) .options, call:selectinload, call:uuid.UUID, call:conditions.append, call:ConfigProfile.project_id.is_, call:ConfigProfile.tool_type_id.is_, call:query.where, call:or_, call:session.execute, call:result.scalars().all, call:profile_to_response, func:create_config_profile(data: ConfigProfileCreate, current_user_id, session), call:create_profile, call:logger.debug, call:profile_to_response, func:get_config_profile(profile_id: str, current_user_id, session), call:get_profile_with_includes, call:uuid.UUID, call:profile_to_response, raise:HTTPException, func:update_config_profile(profile_id: str, data: ConfigProfileUpdate, current_user_id, session), call:get_profile_with_includes, call:uuid.UUID, call:update_profile, call:logger.debug, call:profile_to_response, raise:HTTPException, func:delete_config_profile(profile_id: str, current_user_id, session), call:get_profile_with_includes, call:uuid.UUID, call:session.delete, call:session.commit, call:logger.debug, raise:HTTPException, func:update_profile_includes_endpoint(profile_id: str, data: ConfigProfileIncludeUpdate, current_user_id, session), call:get_profile_with_includes, call:uuid.UUID, call:update_includes, call:session.execute, call:select(ConfigProfileInclude).where, call:inc_result.scalars().all, call:logger.debug, call:profile_to_response, call:list, raise:HTTPException, func:preview_config_profile(profile_id: str, current_user_id, session), call:get_profile_with_includes, call:uuid.UUID, call:resolve_profile, call:resolved_profile_to_dict, raise:HTTPException, func:resolve_default_profile_endpoint(project_id, tool_type_id, current_user_id, session), call:resolve_default_profile, call:uuid.UUID, func:get_default_profiles_endpoint(user_id, session) → dict, call:session.execute, call:select(UserConfig).where, call:result.scalar_one_or_none, func:set_default_profiles_endpoint(data: DefaultProfilesUpdate, user_id, session) → dict, call:validate_default_profiles, call:get_or_create_user_config, call:session.commit, call:session.refresh, func:get_default_profile_for_tool_type_endpoint(tool_type_id: str, user_id, session) → dict, call:session.execute, call:select(UserConfig).where, call:result.scalar_one_or_none, call:user_config.default_profiles.get, func:validate_git_url_endpoint(data: ValidateGitUrlRequest, current_user_id, session) → ValidateGitUrlResponse, call:validate_git_url | dep: logging, uuid, fastapi, sqlalchemy, sqlalchemy.ext.asyncio, sqlalchemy.orm, src.auth.dependencies, src.models, src.schemas.config, src.services.config.config_profile_resolver, src.services.config.crud_service, src.services.config.resolver_service
|
||||||
|
- user_config.py | Provides FastAPI endpoints to get and update the current user's configuration settings, creating a default config if none exists. | exp: func:_get_or_create_config(session: AsyncSession, user_id: uuid.UUID) → UserConfig, call:session.execute, call:select(UserConfig).where, call:result.scalar_one_or_none, call:UserConfig, call:session.add, call:session.commit, call:session.refresh, func:get_user_config(user_id, session) → UserConfigResponse, call:_get_user, call:_get_or_create_config, call:UserConfigResponse.model_validate, func:update_user_config(data: UserConfigUpdate, user_id, session) → UserConfigResponse, call:_get_user, call:_get_or_create_config, call:data.model_dump, call:logger.debug, call:session.commit, call:session.refresh, call:UserConfigResponse.model_validate | dep: logging, uuid, fastapi, sqlalchemy, sqlalchemy.ext.asyncio, src.auth.dependencies, src.models, src.schemas.user
|
||||||
|
## arch
|
||||||
|
Modular FastAPI router pattern with separate route modules for distinct resource domains (user config vs config profiles), each implementing standard CRUD operations with validation and default initialization logic.
|
||||||
|
## tags
|
||||||
|
config, profile, get, user, includes, call:uuid.uuid, or, default
|
||||||
|
## symbols
|
||||||
|
- list_config_profiles
|
||||||
|
- create_config_profile
|
||||||
|
- get_config_profile
|
||||||
|
- update_config_profile
|
||||||
|
- delete_config_profile
|
||||||
|
- update_profile_includes_endpoint
|
||||||
|
- preview_config_profile
|
||||||
|
- resolve_default_profile_endpoint
|
||||||
|
## workflows
|
||||||
|
- change config behavior
|
||||||
|
read: __init__.py, config_profiles.py, user_config.py
|
||||||
|
- change config config
|
||||||
|
read: config_profiles.py, user_config.py
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# apps/api/src/api/project (index)
|
||||||
|
dir: apps/api/src/api/project
|
||||||
|
|
||||||
|
## role
|
||||||
|
Provides FastAPI REST API endpoints for project and Git repository management, serving as the HTTP interface layer for the project's core domain operations.
|
||||||
|
## parent
|
||||||
|
index: apps/api/src/api/.pi-map.index.md
|
||||||
|
map: apps/api/src/api/.pi-map.md
|
||||||
|
## children
|
||||||
|
-
|
||||||
|
## files
|
||||||
|
- __init__.py
|
||||||
|
- git_repositories.py
|
||||||
|
- projects.py
|
||||||
|
## links
|
||||||
|
index: apps/api/src/api/project/.pi-map.index.md
|
||||||
|
map: apps/api/src/api/project/.pi-map.md
|
||||||
|
## workflows
|
||||||
|
- change project behavior
|
||||||
|
read: __init__.py, git_repositories.py, projects.py
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,26 @@
|
|||||||
|
# apps/api/src/api/system (index)
|
||||||
|
dir: apps/api/src/api/system
|
||||||
|
|
||||||
|
## role
|
||||||
|
Provides system-level API endpoints for monitoring, administration, and infrastructure interaction including dashboards, health checks, event streaming, notifications, and container terminal access.
|
||||||
|
## parent
|
||||||
|
index: apps/api/src/api/.pi-map.index.md
|
||||||
|
map: apps/api/src/api/.pi-map.md
|
||||||
|
## children
|
||||||
|
-
|
||||||
|
## files
|
||||||
|
- __init__.py
|
||||||
|
- dashboard.py
|
||||||
|
- events.py
|
||||||
|
- health.py
|
||||||
|
- instance_proxy.py
|
||||||
|
- notifications.py
|
||||||
|
- terminal.py
|
||||||
|
## links
|
||||||
|
index: apps/api/src/api/system/.pi-map.index.md
|
||||||
|
map: apps/api/src/api/system/.pi-map.md
|
||||||
|
## workflows
|
||||||
|
- change system behavior
|
||||||
|
read: __init__.py, dashboard.py, events.py
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# apps/api/src/api/system
|
||||||
|
dir: apps/api/src/api/system
|
||||||
|
|
||||||
|
index: apps/api/src/api/system/.pi-map.index.md
|
||||||
|
|
||||||
|
## role
|
||||||
|
Provides system-level API endpoints for monitoring, administration, and infrastructure interaction including dashboards, health checks, event streaming, notifications, and container terminal access.
|
||||||
|
## files
|
||||||
|
- __init__.py | Aggregates and re-exports system API router modules from submodules for centralized access. | dep: src.api.system.dashboard, src.api.system.events, src.api.system.health, src.api.system.instance_proxy, src.api.system.notifications, src.api.system.terminal
|
||||||
|
- dashboard.py | Provides a FastAPI endpoint that returns a dashboard summary with aggregated counts of projects, repositories, SSH keys, and recent activity for the authenticated user. | exp: func:get_dashboard_summary(user_id, session) → dict, call:session.execute, call:select(func.count()).select_from(Project).where, call:func.count, call:projects_result.scalar, call:select(func.count()).select_from(GitRepository).where, call:repos_result.scalar, call:select(func.count()).select_from(SSHKey).where, call:ssh_keys_result.scalar, call:select(Project) .where(Project.owner_id == user_id) .order_by(Project.created_at.desc()) .limit, call:Project.created_at.desc, call:recent_projects.scalars().all | dep: uuid, fastapi, sqlalchemy, sqlalchemy.ext.asyncio, src.auth.dependencies, src.models, src.models.project, src.models (GitRepository, Project, SSHKey)
|
||||||
|
- events.py | Implements an SSE streaming endpoint that delivers instance events to authenticated users with per-user connection limits and keep-alive pings. | exp: func:events_stream(request: Request, user_id) → StreamingResponse, call:_connection_counts.get, call:InstanceEventBus, call:asyncio.Queue, call:queue.put_nowait, call:contextlib.suppress, call:queue.get_nowait, call:event_bus.subscribe, call:asyncio.wait_for, call:queue.get, call:json.dumps, call:unsubscribe, call:max, call:_connection_counts.pop, call:StreamingResponse, call:event_generator, raise:HTTPException, func:event_generator() → AsyncGenerator[str, None], call:InstanceEventBus, call:asyncio.Queue, call:queue.put_nowait, call:contextlib.suppress, call:queue.get_nowait, call:event_bus.subscribe, call:asyncio.wait_for, call:queue.get, call:json.dumps, call:unsubscribe, call:max, call:_connection_counts.get, call:_connection_counts.pop, func:on_event(payload: InstanceEventPayload) → None, call:queue.put_nowait, call:contextlib.suppress, call:queue.get_nowait | dep: asyncio, contextlib, json, uuid, collections.abc, fastapi, fastapi.responses, src.auth.dependencies, src.services.instance.event_bus
|
||||||
|
- health.py | Implements FastAPI health check endpoints for monitoring system status including database connectivity and disk space. | exp: func:health_check() → dict[str, Any], call:HealthChecks, call:time_module.perf_counter, call:SessionLocal, call:session.execute, call:text, call:DatabaseHealth, call:round, call:shutil.disk_usage, call:DiskHealth, call:HealthResponse( status=overall_status, timestamp=datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"), version="0.1.0", checks=checks, uptime_seconds=round(time.time() - _start_time, 2), ).model_dump, call:datetime.now(timezone.utc).isoformat().replace, call:time.time, func:health_check_db() → dict[str, Any], call:time_module.perf_counter, call:SessionLocal, call:session.execute, call:text, call:DatabaseHealthResponse( status="healthy", response_time_ms=round(db_time, 2), ).model_dump, call:round, call:DatabaseHealthResponse( status="unhealthy", response_time_ms=0.0, ).model_dump | dep: time, datetime, typing, fastapi, sqlalchemy, src.database, src.schemas.system, shutil
|
||||||
|
- instance_proxy.py | Proxies HTTP requests from FastAPI endpoints to running containerized tool instances after verifying ownership and status. | exp: func:_proxy_request(request: Request, instance_id: uuid.UUID, path: str, user_id: uuid.UUID, session: AsyncSession) → Response, call:session.get, call:str, call:request.headers.items, call:key.lower, call:httpx.AsyncClient, call:request.body, call:client.request, call:logger.error, call:dict, call:response_headers.pop, call:Response, raise:HTTPException, func:proxy_to_instance(request: Request, instance_id: uuid.UUID, path, user_id, session) → Response, call:_proxy_request | dep: logging, uuid, httpx, fastapi, sqlalchemy.ext.asyncio, src.auth.dependencies, src.models
|
||||||
|
- notifications.py | Defines FastAPI REST endpoints for managing user notifications (list, unread count, mark read, dismiss/clear) with support for muted categories from user config. | exp: class:NotificationItem, class:NotificationListResponse, class:UnreadCountResponse, class:MarkAllReadResponse, class:ClearAllResponse, func:_get_mute_categories(session: AsyncSession, user_id: uuid.UUID) → list[str], call:session.execute, call:select(UserConfig).where, call:result.scalar_one_or_none, call:config.config.get, call:isinstance, func:list_notifications(limit, offset, unread_only, user, session) → NotificationListResponse, call:_get_mute_categories, call:notification_service.list_notifications, call:NotificationListResponse, call:NotificationItem.model_validate, func:get_unread_count(user, session) → UnreadCountResponse, call:notification_service.get_unread_count, call:UnreadCountResponse, func:mark_notification_read(notification_id: uuid.UUID, user, session) → NotificationItem, call:notification_service.mark_read, call:NotificationItem.model_validate, raise:HTTPException, func:mark_all_read(user, session) → MarkAllReadResponse, call:notification_service.mark_all_read, call:MarkAllReadResponse, func:clear_all_notifications(user, session) → ClearAllResponse, call:notification_service.dismiss_all, call:ClearAllResponse, func:dismiss_notification(notification_id: uuid.UUID, user, session) → None, call:notification_service.dismiss, raise:HTTPException | dep: uuid, datetime, fastapi, pydantic, sqlalchemy.ext.asyncio, src.auth.dependencies, src.models.user, src.models, src.services.shared.notification_service, sqlalchemy
|
||||||
|
- terminal.py | Provides WebSocket and HTTP endpoints for managing interactive terminal sessions attached to running Docker container tool instances, including session creation, attachment, input/output streaming, resize/reset control messages, and session listing. | exp: class:SessionRef, method:__init__(self, session, slot_session_id), func:terminal_websocket_default(websocket: WebSocket, instance_id: str, db_session) → None, call:_handle_terminal_websocket, func:terminal_websocket_specific(websocket: WebSocket, instance_id: str, session_id: str, db_session) → None, call:_handle_terminal_websocket, func:_handle_terminal_websocket(websocket: WebSocket, instance_id: str, target_session_id: str | None, db_session: AsyncSession) → None, call:logger.debug, call:websocket.accept, call:uuid.UUID, call:logger.error, call:websocket.close, call:_get_user_from_websocket, call:logger.warning, call:db_session.get, call:get_container_status, call:terminal_manager.get_or_create_session, call:terminal_manager.get_session, call:logger.info, call:terminal_manager.create_session, call:terminal_manager._find_key_by_internal_id, call:terminal_manager.attach_websocket, call:websocket.send_json, call:SessionRef, call:asyncio.create_task, call:_write_loop, call:_heartbeat_loop, call:asyncio.wait, call:len, call:task.cancel, call:str, call:suppress, call:terminal_manager.detach_websocket, func:_write_loop(session_ref: SessionRef, websocket, instance_id: str) → None, call:session.is_alive, call:asyncio.sleep, call:websocket.receive, call:session.write_input, call:text.startswith, call:json.loads, call:ctrl.get, call:logger.debug, call:session.resize, call:session.acknowledge_data, call:websocket.send_json, call:terminal_manager.reset_session, call:terminal_manager.attach_websocket, call:text.encode, func:_heartbeat_loop(websocket: WebSocket) → None, call:asyncio.sleep, call:websocket.send_json, func:_get_terminal_instance(instance_id: uuid.UUID, user_id: uuid.UUID, db_session: AsyncSession) → ToolInstance, call:db_session.get, raise:HTTPException, func:list_terminal_sessions(instance_id: uuid.UUID, user_id, db_session) → dict, call:_get_terminal_instance, call:db_session.execute, call:select(TerminalSessionModel) .where(TerminalSessionModel.instance_id == instance_id) .where(TerminalSessionModel.status != "closed") .order_by, call:TerminalSessionModel.created_at.asc, call:result.scalars().all, call:terminal_manager.get_session, call:str, call:sessions.append, call:live_session.has_websockets, call:row.created_at.isoformat, call:row.last_activity_at.isoformat, func:create_terminal_session(instance_id: uuid.UUID, data: dict, user_id, db_session) → dict, call:_get_terminal_instance, call:db_session.get, call:data.get, call:terminal_manager.create_session, raise:HTTPException, func:close_terminal_session(instance_id: uuid.UUID, session_id: str, user_id, db_session) → dict, call:_get_terminal_instance, call:terminal_manager._find_key_by_internal_id, call:str, call:terminal_manager.get_session, call:terminal_manager.close_session, raise:HTTPException, func:reset_specific_terminal_session(instance_id: uuid.UUID, session_id: str, user_id, db_session) → dict, call:_get_terminal_instance, call:terminal_manager._find_key_by_internal_id, call:str, call:terminal_manager.get_session, call:db_session.get, call:terminal_manager.reset_session, raise:HTTPException, func:rename_terminal_session(instance_id: uuid.UUID, session_id: str, data: dict, user_id, db_session) → dict, call:_get_terminal_instance, call:data.get, call:isinstance, call:terminal_manager.get_session, call:str, call:db_session.get, call:uuid.UUID, call:db_session.commit, raise:HTTPException, func:reset_terminal_session(instance_id: uuid.UUID, user_id, db_session) → dict, call:_get_terminal_instance, call:db_session.get, call:terminal_manager.reset_session, call:logger.info, call:str, call:logger.error, raise:HTTPException, func:_get_user_from_websocket(websocket: WebSocket, db_session: AsyncSession) → uuid.UUID | None, call:websocket.cookies.get, call:Settings, call:decode_session_cookie, call:uuid.UUID, call:str | dep: asyncio, json, logging, uuid, contextlib, fastapi, sqlalchemy, sqlalchemy.ext.asyncio, starlette.websockets, src.auth.dependencies, src.models, src.services.terminal.terminal_manager, src.services.docker, src.auth.session, src.config, starlette
|
||||||
|
## arch
|
||||||
|
FastAPI router modules organized by domain concern with async/await patterns, SSE/WebSocket for real-time streaming, proxy pattern for container instance forwarding, and per-user authentication/authorization with connection limiting.
|
||||||
|
## tags
|
||||||
|
session, terminal, call:terminal, call:, src, get, response, websocket
|
||||||
|
## symbols
|
||||||
|
- NotificationItem
|
||||||
|
- NotificationListResponse
|
||||||
|
- UnreadCountResponse
|
||||||
|
- MarkAllReadResponse
|
||||||
|
- ClearAllResponse
|
||||||
|
- SessionRef
|
||||||
|
- get_dashboard_summary
|
||||||
|
- events_stream
|
||||||
|
## workflows
|
||||||
|
- change system behavior
|
||||||
|
read: __init__.py, dashboard.py, events.py
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# apps/api/src/api/tool (index)
|
||||||
|
dir: apps/api/src/api/tool
|
||||||
|
|
||||||
|
## role
|
||||||
|
Provides FastAPI REST API endpoints for managing Docker-based tools, including their definitions, types, instances, lifecycle operations, and active sessions.
|
||||||
|
## parent
|
||||||
|
index: apps/api/src/api/.pi-map.index.md
|
||||||
|
map: apps/api/src/api/.pi-map.md
|
||||||
|
## children
|
||||||
|
-
|
||||||
|
## files
|
||||||
|
- __init__.py
|
||||||
|
- sessions.py
|
||||||
|
- tool_definitions.py
|
||||||
|
- tool_instances.py
|
||||||
|
- tool_lifecycle.py
|
||||||
|
- tool_types.py
|
||||||
|
- tool_types_validation.py
|
||||||
|
## links
|
||||||
|
index: apps/api/src/api/tool/.pi-map.index.md
|
||||||
|
map: apps/api/src/api/tool/.pi-map.md
|
||||||
|
## workflows
|
||||||
|
- change tool behavior
|
||||||
|
read: __init__.py, sessions.py, tool_definitions.py
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# apps/api/src/api/tool
|
||||||
|
dir: apps/api/src/api/tool
|
||||||
|
|
||||||
|
index: apps/api/src/api/tool/.pi-map.index.md
|
||||||
|
|
||||||
|
## role
|
||||||
|
Provides FastAPI REST API endpoints for managing Docker-based tools, including their definitions, types, instances, lifecycle operations, and active sessions.
|
||||||
|
## files
|
||||||
|
- __init__.py | Aggregates and exports all tool-related API routers from submodules as a unified module interface. | dep: src.api.tool.sessions, src.api.tool.tool_definitions, src.api.tool.tool_instances, src.api.tool.tool_lifecycle, src.api.tool.tool_types
|
||||||
|
- sessions.py | Provides a FastAPI endpoint to retrieve all active tool instances (sessions) for the currently authenticated user with related metadata. | exp: func:get_user_sessions(user_id, session) → dict, call:_get_user, call:session.execute, call:select(ToolInstance) .where(ToolInstance.owner_id == user_id) .where( ToolInstance.status.in_( ["running", "building", "pending", "stopped", "error"] ) ) .order_by, call:ToolInstance.status.in_, call:ToolInstance.created_at.desc, call:result.scalars().all, call:session.get, call:sessions.append, call:str, call:instance.created_at.isoformat | dep: uuid, fastapi, sqlalchemy, sqlalchemy.ext.asyncio, src.auth.dependencies, src.models, src.models (GitRepository, Project, ToolInstance, ToolType, Workspace)
|
||||||
|
- tool_definitions.py | FastAPI router providing CRUD endpoints and manifest compilation for tool definition manifests stored in a database. | exp: class:CreateToolDefinitionRequest, class:UpdateToolDefinitionRequest, func:create_tool_definition(data: CreateToolDefinitionRequest, user_id, session) → dict, call:uuid.UUID, call:session.get, call:session.execute, call:select(ToolDefinitionManifest).where, call:existing.scalar_one_or_none, call:ToolDefinitionManifest, call:session.add, call:session.commit, call:session.refresh, call:logger.info, call:str, call:definition.created_at.isoformat, raise:HTTPException, func:list_tool_definitions(user_id, session, include_bases) → dict, call:select, call:query.where, call:ToolDefinitionManifest.is_base.is_, call:session.execute, call:query.order_by, call:ToolDefinitionManifest.created_at.desc, call:result.scalars().all, call:str, call:d.created_at.isoformat, func:get_tool_definition(definition_id: uuid.UUID, user_id, session) → dict, call:session.get, call:str, call:definition.created_at.isoformat, call:definition.updated_at.isoformat, raise:HTTPException, func:update_tool_definition(definition_id: uuid.UUID, data: UpdateToolDefinitionRequest, user_id, session) → dict, call:session.get, call:session.commit, call:session.refresh, call:logger.info, call:str, call:definition.updated_at.isoformat, raise:HTTPException, func:delete_tool_definition(definition_id: uuid.UUID, user_id, session) → dict, call:session.get, call:session.execute, call:select(ToolType).where, call:result.scalars().all, call:", ".join, call:session.delete, call:session.commit, call:logger.info, call:str, raise:HTTPException, func:compile_tool_definition(definition_id: uuid.UUID, user_id, session) → dict, call:session.get, call:dict, call:resolve_base, call:deep_merge, call:compile_dockerfile, call:compile_entrypoint, call:compute_image_tag, call:compile_compose, call:session.commit, call:str, raise:HTTPException | dep: logging, uuid, fastapi, pydantic, sqlalchemy, sqlalchemy.ext.asyncio, src.auth.dependencies, src.models, src.services.build.manifest_compiler
|
||||||
|
- tool_instances.py | Provides FastAPI REST endpoints for managing tool instances including CRUD operations, logs, health checks, tunnel recreation, event history, and HTTP proxying to running containers. | exp: func:create_instance(project_id: uuid.UUID, repo_id: uuid.UUID, data: CreateInstanceRequest, user_id, session) → dict, call:_get_user, call:_get_owned_project, call:create_tool_instance, call:str, call:instance.created_at.isoformat, raise:HTTPException, func:list_instances(project_id: uuid.UUID, repo_id: uuid.UUID, user_id, session) → list[dict], call:_get_user, call:_get_owned_project, call:session.execute, call:select(ToolInstance) .where(ToolInstance.repository_id == repo_id) .where(ToolInstance.owner_id == user_id) .order_by, call:ToolInstance.created_at.desc, call:result.scalars().all, call:str, call:i.created_at.isoformat, func:get_instance(project_id: uuid.UUID, repo_id: uuid.UUID, instance_id: uuid.UUID, user_id, session) → dict, call:_get_user, call:_get_owned_project, call:session.get, call:get_container_status, call:str, call:instance.created_at.isoformat, call:instance.last_started_at.isoformat, raise:HTTPException, func:rename_instance(project_id: uuid.UUID, repo_id: uuid.UUID, instance_id: uuid.UUID, data: dict, user_id, session) → dict, call:_get_user, call:_get_owned_project, call:data.get("display_name", "").strip, call:rename_tool_instance, call:str, raise:HTTPException, func:get_instance_logs(project_id: uuid.UUID, repo_id: uuid.UUID, instance_id: uuid.UUID, tail, user_id, session) → dict, call:_get_user, call:_get_owned_project, call:session.get, call:get_container_logs, raise:HTTPException, func:recreate_tunnel_endpoint(project_id: uuid.UUID, repo_id: uuid.UUID, instance_id: uuid.UUID, user_id, session) → dict, call:_get_user, call:_get_owned_project, call:recreate_instance_tunnel, raise:HTTPException, func:check_instance_tunnel_health(project_id: uuid.UUID, repo_id: uuid.UUID, instance_id: uuid.UUID, user_id, session) → dict, call:_get_user, call:_get_owned_project, call:session.get, call:get_container_status, call:instance.probe_result.get, call:"\n".join, call:check_tunnel_health, call:tunnel_health.get, raise:HTTPException, func:get_instance_events(project_id: uuid.UUID, repo_id: uuid.UUID, instance_id: uuid.UUID, limit, user_id, session) → list[dict], call:_get_user, call:_get_owned_project, call:session.get, call:session.execute, call:select(InstanceEvent) .where(InstanceEvent.instance_id == instance_id) .order_by(InstanceEvent.created_at.desc()) .limit, call:InstanceEvent.created_at.desc, call:result.scalars().all, call:str, call:row.created_at.isoformat, raise:HTTPException, func:proxy_to_instance(request: Request, project_id: uuid.UUID, repo_id: uuid.UUID, instance_id: uuid.UUID, path, user_id, session) → Response, call:session.get, call:str, call:dict, call:headers.pop, call:httpx.AsyncClient, call:request.body, call:client.request, call:logger.error, call:response_headers.pop, call:Response, raise:HTTPException | dep: logging, uuid, httpx, fastapi, sqlalchemy, sqlalchemy.ext.asyncio, src.auth.dependencies, src.models, src.services.docker, src.services.shared.tunnel, src.schemas.tool, src.services.tool.instance_service
|
||||||
|
- tool_lifecycle.py | FastAPI router providing REST endpoints for managing Docker-based tool instance lifecycle operations (start, stop, restart, delete). | exp: func:start_instance(project_id: uuid.UUID, repo_id: uuid.UUID, instance_id: uuid.UUID, data, user_id, session) → dict, call:_get_user, call:_get_owned_project, call:start_tool_instance, raise:HTTPException, func:stop_instance(project_id: uuid.UUID, repo_id: uuid.UUID, instance_id: uuid.UUID, user_id, session) → dict, call:_get_user, call:_get_owned_project, call:stop_tool_instance, raise:HTTPException, func:restart_instance(project_id: uuid.UUID, repo_id: uuid.UUID, instance_id: uuid.UUID, user_id, session) → dict, call:_get_user, call:_get_owned_project, call:restart_tool_instance, raise:HTTPException, func:delete_instance(project_id: uuid.UUID, repo_id: uuid.UUID, instance_id: uuid.UUID, force, user_id, session) → None, call:_get_user, call:_get_owned_project, call:delete_tool_instance, call:str, call:detail.lower, raise:HTTPException | dep: logging, uuid, fastapi, sqlalchemy.ext.asyncio, src.auth.dependencies, src.schemas.tool, src.services.tool.instance_service
|
||||||
|
- tool_types.py | Defines FastAPI routes for CRUD operations and validation of Docker tool types with compose/dockerfile/manifest definitions. | exp: func:_require_admin(user: User) → None, func:create_tool_type(data: ToolTypeCreate, user_id, session) → ToolType, call:_get_user, call:_require_admin, call:session.scalar, call:select(ToolType).where, call:ToolType, call:session.add, call:session.commit, call:session.refresh, raise:HTTPException, func:list_tool_types(user_id, session) → list[ToolType], call:_get_user, call:session.execute, call:select(ToolType).order_by, call:list, call:result.scalars().all, func:get_tool_type(tool_type_id: uuid.UUID, user_id, session) → ToolType, call:_get_user, call:session.get, raise:HTTPException, func:update_tool_type(tool_type_id: uuid.UUID, data: ToolTypeUpdate, user_id, session) → ToolType, call:_get_user, call:_require_admin, call:session.get, call:data.model_dump, call:update_data.get, call:validate_compose_yaml, call:check_port_exposed, call:validate_required_variables, call:update_data.items, call:setattr, call:session.commit, call:session.refresh, raise:HTTPException, func:validate_tool_type_template(data: ToolTypeValidateRequest, user_id, session) → dict, call:_get_user, call:errors.append, call:validate_compose_yaml, call:str, call:data.dockerfile_template.strip().startswith, call:len, func:validate_tool_type(tool_type_id: uuid.UUID, user_id, session) → dict, call:_get_user, call:session.get, call:errors.append, call:validate_compose_yaml, call:str, call:tool_type.dockerfile_template.strip().startswith, call:len, raise:HTTPException, func:delete_tool_type(tool_type_id: uuid.UUID, user_id, session) → None, call:_get_user, call:_require_admin, call:session.get, call:session.delete, call:session.commit, raise:HTTPException | dep: uuid, fastapi, sqlalchemy, sqlalchemy.ext.asyncio, src.api.tool.tool_types_validation, src.auth.dependencies, src.models, src.models.user, src.schemas.tool
|
||||||
|
- tool_types_validation.py | Validates Docker Compose YAML templates by sanitizing template variables, parsing YAML structure, checking port exposure, and verifying required variables exist. | exp: func:sanitize_template_vars(template: str) → str, call:re.sub, func:validate_compose_yaml(template: str) → dict, call:sanitize_template_vars, call:yaml.safe_load, call:isinstance, raise:ValueError, func:check_port_exposed(parsed: dict, port: int) → bool, call:str, call:isinstance, call:parsed["services"].values, call:port_mapping.split, call:mapping.split, func:validate_required_variables(template: str, variables: list[str]) → None, raise:HTTPException | dep: re, yaml, fastapi
|
||||||
|
## arch
|
||||||
|
Modular FastAPI router organization with clear separation of concerns across CRUD operations, Docker lifecycle management, YAML/Compose validation, and container proxying/tunneling.
|
||||||
|
## tags
|
||||||
|
get, call:, tool, raise:httpexception, user, instance, call:str, call:session.get
|
||||||
|
## symbols
|
||||||
|
- CreateToolDefinitionRequest
|
||||||
|
- UpdateToolDefinitionRequest
|
||||||
|
- get_user_sessions
|
||||||
|
- create_tool_definition
|
||||||
|
- list_tool_definitions
|
||||||
|
- get_tool_definition
|
||||||
|
- update_tool_definition
|
||||||
|
- delete_tool_definition
|
||||||
|
## workflows
|
||||||
|
- change tool behavior
|
||||||
|
read: __init__.py, sessions.py, tool_definitions.py
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -3,11 +3,13 @@
|
|||||||
from src.api.tool.sessions import sessions_router
|
from src.api.tool.sessions import sessions_router
|
||||||
from src.api.tool.tool_definitions import router as tool_definitions_router
|
from src.api.tool.tool_definitions import router as tool_definitions_router
|
||||||
from src.api.tool.tool_instances import router as tool_instances_router
|
from src.api.tool.tool_instances import router as tool_instances_router
|
||||||
|
from src.api.tool.tool_lifecycle import router as tool_lifecycle_router
|
||||||
from src.api.tool.tool_types import router as tool_types_router
|
from src.api.tool.tool_types import router as tool_types_router
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"sessions_router",
|
"sessions_router",
|
||||||
"tool_definitions_router",
|
"tool_definitions_router",
|
||||||
"tool_instances_router",
|
"tool_instances_router",
|
||||||
|
"tool_lifecycle_router",
|
||||||
"tool_types_router",
|
"tool_types_router",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -74,8 +74,6 @@ async def get_user_sessions(
|
|||||||
"workspace_name": workspace_name,
|
"workspace_name": workspace_name,
|
||||||
"status": instance.status,
|
"status": instance.status,
|
||||||
"url": instance.url,
|
"url": instance.url,
|
||||||
"clone_mode": instance.clone_mode,
|
|
||||||
"branch": instance.branch,
|
|
||||||
"selected_config_profile_id": str(instance.selected_config_profile_id)
|
"selected_config_profile_id": str(instance.selected_config_profile_id)
|
||||||
if instance.selected_config_profile_id
|
if instance.selected_config_profile_id
|
||||||
else None,
|
else None,
|
||||||
|
|||||||
@@ -24,15 +24,11 @@ from src.auth.dependencies import (
|
|||||||
from src.models import ToolInstance
|
from src.models import ToolInstance
|
||||||
from src.services.docker import get_container_logs, get_container_status
|
from src.services.docker import get_container_logs, get_container_status
|
||||||
from src.services.shared.tunnel import check_tunnel_health
|
from src.services.shared.tunnel import check_tunnel_health
|
||||||
from src.schemas.tool import CreateInstanceRequest, StartInstanceRequest
|
from src.schemas.tool import CreateInstanceRequest
|
||||||
from src.services.tool.instance_service import (
|
from src.services.tool.instance_service import (
|
||||||
create_tool_instance,
|
create_tool_instance,
|
||||||
delete_tool_instance,
|
|
||||||
recreate_instance_tunnel,
|
recreate_instance_tunnel,
|
||||||
rename_tool_instance,
|
rename_tool_instance,
|
||||||
restart_tool_instance,
|
|
||||||
start_tool_instance,
|
|
||||||
stop_tool_instance,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -68,8 +64,7 @@ async def create_instance(
|
|||||||
"display_name": instance.display_name,
|
"display_name": instance.display_name,
|
||||||
"tool_type_id": str(instance.tool_type_id),
|
"tool_type_id": str(instance.tool_type_id),
|
||||||
"status": instance.status,
|
"status": instance.status,
|
||||||
"clone_mode": instance.clone_mode,
|
"workspace_id": str(instance.workspace_id) if instance.workspace_id else None,
|
||||||
"branch": instance.branch,
|
|
||||||
"selected_config_profile_id": str(instance.selected_config_profile_id) if instance.selected_config_profile_id else None,
|
"selected_config_profile_id": str(instance.selected_config_profile_id) if instance.selected_config_profile_id else None,
|
||||||
"created_at": instance.created_at.isoformat(),
|
"created_at": instance.created_at.isoformat(),
|
||||||
}
|
}
|
||||||
@@ -143,8 +138,6 @@ async def get_instance(
|
|||||||
"container_id": instance.container_id,
|
"container_id": instance.container_id,
|
||||||
"container_name": instance.container_name,
|
"container_name": instance.container_name,
|
||||||
"compose_path": instance.compose_path,
|
"compose_path": instance.compose_path,
|
||||||
"clone_mode": instance.clone_mode,
|
|
||||||
"branch": instance.branch,
|
|
||||||
"workspace_id": str(instance.workspace_id) if instance.workspace_id else None,
|
"workspace_id": str(instance.workspace_id) if instance.workspace_id else None,
|
||||||
"selected_config_profile_id": str(instance.selected_config_profile_id) if instance.selected_config_profile_id else None,
|
"selected_config_profile_id": str(instance.selected_config_profile_id) if instance.selected_config_profile_id else None,
|
||||||
"ssh_key_ids": instance.ssh_key_ids,
|
"ssh_key_ids": instance.ssh_key_ids,
|
||||||
@@ -193,116 +186,6 @@ async def rename_instance(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@router.post(
|
|
||||||
"/{project_id}/repositories/{repo_id}/instances/{instance_id}/start",
|
|
||||||
summary="Start instance",
|
|
||||||
description="Start a tool instance using Docker Compose.",
|
|
||||||
)
|
|
||||||
async def start_instance(
|
|
||||||
project_id: uuid.UUID,
|
|
||||||
repo_id: uuid.UUID,
|
|
||||||
instance_id: uuid.UUID,
|
|
||||||
data: StartInstanceRequest | None = None,
|
|
||||||
user_id: uuid.UUID = Depends(get_current_user_id),
|
|
||||||
session: AsyncSession = Depends(get_db_session),
|
|
||||||
) -> dict:
|
|
||||||
_user = await _get_user(session, user_id)
|
|
||||||
_project = await _get_owned_project(project_id, user_id, session)
|
|
||||||
try:
|
|
||||||
return await start_tool_instance(
|
|
||||||
session, user_id, project_id, repo_id, instance_id, data
|
|
||||||
)
|
|
||||||
except ValueError as exc:
|
|
||||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc))
|
|
||||||
except RuntimeError as exc:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(exc)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@router.post(
|
|
||||||
"/{project_id}/repositories/{repo_id}/instances/{instance_id}/stop",
|
|
||||||
summary="Stop instance",
|
|
||||||
description="Stop a running tool instance.",
|
|
||||||
)
|
|
||||||
async def stop_instance(
|
|
||||||
project_id: uuid.UUID,
|
|
||||||
repo_id: uuid.UUID,
|
|
||||||
instance_id: uuid.UUID,
|
|
||||||
user_id: uuid.UUID = Depends(get_current_user_id),
|
|
||||||
session: AsyncSession = Depends(get_db_session),
|
|
||||||
) -> dict:
|
|
||||||
_user = await _get_user(session, user_id)
|
|
||||||
_project = await _get_owned_project(project_id, user_id, session)
|
|
||||||
try:
|
|
||||||
return await stop_tool_instance(session, user_id, project_id, repo_id, instance_id)
|
|
||||||
except ValueError as exc:
|
|
||||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(exc))
|
|
||||||
|
|
||||||
|
|
||||||
@router.post(
|
|
||||||
"/{project_id}/repositories/{repo_id}/instances/{instance_id}/restart",
|
|
||||||
summary="Restart instance",
|
|
||||||
description="Restart a tool instance.",
|
|
||||||
)
|
|
||||||
async def restart_instance(
|
|
||||||
project_id: uuid.UUID,
|
|
||||||
repo_id: uuid.UUID,
|
|
||||||
instance_id: uuid.UUID,
|
|
||||||
user_id: uuid.UUID = Depends(get_current_user_id),
|
|
||||||
session: AsyncSession = Depends(get_db_session),
|
|
||||||
) -> dict:
|
|
||||||
_user = await _get_user(session, user_id)
|
|
||||||
_project = await _get_owned_project(project_id, user_id, session)
|
|
||||||
try:
|
|
||||||
return await restart_tool_instance(
|
|
||||||
session, user_id, project_id, repo_id, instance_id
|
|
||||||
)
|
|
||||||
except ValueError as exc:
|
|
||||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc))
|
|
||||||
except RuntimeError as exc:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(exc)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@router.delete(
|
|
||||||
"/{project_id}/repositories/{repo_id}/instances/{instance_id}",
|
|
||||||
summary="Delete instance",
|
|
||||||
description="Delete a tool instance and remove its Docker containers and files.",
|
|
||||||
)
|
|
||||||
async def delete_instance(
|
|
||||||
project_id: uuid.UUID,
|
|
||||||
repo_id: uuid.UUID,
|
|
||||||
instance_id: uuid.UUID,
|
|
||||||
force: bool = False,
|
|
||||||
user_id: uuid.UUID = Depends(get_current_user_id),
|
|
||||||
session: AsyncSession = Depends(get_db_session),
|
|
||||||
) -> None:
|
|
||||||
_user = await _get_user(session, user_id)
|
|
||||||
_project = await _get_owned_project(project_id, user_id, session)
|
|
||||||
try:
|
|
||||||
await delete_tool_instance(
|
|
||||||
session, user_id, project_id, repo_id, instance_id, force
|
|
||||||
)
|
|
||||||
except ValueError as exc:
|
|
||||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(exc))
|
|
||||||
except RuntimeError as exc:
|
|
||||||
detail = str(exc)
|
|
||||||
if "uncommitted changes" in detail.lower():
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=status.HTTP_409_CONFLICT,
|
|
||||||
detail={
|
|
||||||
"message": "Repository has uncommitted changes",
|
|
||||||
"changed_files": detail,
|
|
||||||
"force_required": True,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=detail
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/{project_id}/repositories/{repo_id}/instances/{instance_id}/logs",
|
"/{project_id}/repositories/{repo_id}/instances/{instance_id}/logs",
|
||||||
summary="Get instance logs",
|
summary="Get instance logs",
|
||||||
|
|||||||
@@ -0,0 +1,135 @@
|
|||||||
|
"""Tool instance lifecycle API endpoints."""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
from fastapi import APIRouter, Depends, HTTPException, status
|
||||||
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
|
from src.auth.dependencies import (
|
||||||
|
_get_owned_project,
|
||||||
|
_get_user,
|
||||||
|
get_current_user_id,
|
||||||
|
get_db_session,
|
||||||
|
)
|
||||||
|
from src.schemas.tool import StartInstanceRequest
|
||||||
|
from src.services.tool.instance_service import (
|
||||||
|
delete_tool_instance,
|
||||||
|
restart_tool_instance,
|
||||||
|
start_tool_instance,
|
||||||
|
stop_tool_instance,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
router = APIRouter(prefix="/projects", tags=["tool-instances"])
|
||||||
|
|
||||||
|
|
||||||
|
@router.post(
|
||||||
|
"/{project_id}/repositories/{repo_id}/instances/{instance_id}/start",
|
||||||
|
summary="Start instance",
|
||||||
|
description="Start a tool instance using Docker Compose.",
|
||||||
|
)
|
||||||
|
async def start_instance(
|
||||||
|
project_id: uuid.UUID,
|
||||||
|
repo_id: uuid.UUID,
|
||||||
|
instance_id: uuid.UUID,
|
||||||
|
data: StartInstanceRequest | None = None,
|
||||||
|
user_id: uuid.UUID = Depends(get_current_user_id),
|
||||||
|
session: AsyncSession = Depends(get_db_session),
|
||||||
|
) -> dict:
|
||||||
|
_user = await _get_user(session, user_id)
|
||||||
|
_project = await _get_owned_project(project_id, user_id, session)
|
||||||
|
try:
|
||||||
|
return await start_tool_instance(
|
||||||
|
session, user_id, project_id, repo_id, instance_id, data
|
||||||
|
)
|
||||||
|
except ValueError as exc:
|
||||||
|
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc))
|
||||||
|
except RuntimeError as exc:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(exc)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.post(
|
||||||
|
"/{project_id}/repositories/{repo_id}/instances/{instance_id}/stop",
|
||||||
|
summary="Stop instance",
|
||||||
|
description="Stop a running tool instance.",
|
||||||
|
)
|
||||||
|
async def stop_instance(
|
||||||
|
project_id: uuid.UUID,
|
||||||
|
repo_id: uuid.UUID,
|
||||||
|
instance_id: uuid.UUID,
|
||||||
|
user_id: uuid.UUID = Depends(get_current_user_id),
|
||||||
|
session: AsyncSession = Depends(get_db_session),
|
||||||
|
) -> dict:
|
||||||
|
_user = await _get_user(session, user_id)
|
||||||
|
_project = await _get_owned_project(project_id, user_id, session)
|
||||||
|
try:
|
||||||
|
return await stop_tool_instance(session, user_id, project_id, repo_id, instance_id)
|
||||||
|
except ValueError as exc:
|
||||||
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(exc))
|
||||||
|
|
||||||
|
|
||||||
|
@router.post(
|
||||||
|
"/{project_id}/repositories/{repo_id}/instances/{instance_id}/restart",
|
||||||
|
summary="Restart instance",
|
||||||
|
description="Restart a tool instance.",
|
||||||
|
)
|
||||||
|
async def restart_instance(
|
||||||
|
project_id: uuid.UUID,
|
||||||
|
repo_id: uuid.UUID,
|
||||||
|
instance_id: uuid.UUID,
|
||||||
|
user_id: uuid.UUID = Depends(get_current_user_id),
|
||||||
|
session: AsyncSession = Depends(get_db_session),
|
||||||
|
) -> dict:
|
||||||
|
_user = await _get_user(session, user_id)
|
||||||
|
_project = await _get_owned_project(project_id, user_id, session)
|
||||||
|
try:
|
||||||
|
return await restart_tool_instance(
|
||||||
|
session, user_id, project_id, repo_id, instance_id
|
||||||
|
)
|
||||||
|
except ValueError as exc:
|
||||||
|
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc))
|
||||||
|
except RuntimeError as exc:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(exc)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.delete(
|
||||||
|
"/{project_id}/repositories/{repo_id}/instances/{instance_id}",
|
||||||
|
summary="Delete instance",
|
||||||
|
description="Delete a tool instance and remove its Docker containers and files.",
|
||||||
|
)
|
||||||
|
async def delete_instance(
|
||||||
|
project_id: uuid.UUID,
|
||||||
|
repo_id: uuid.UUID,
|
||||||
|
instance_id: uuid.UUID,
|
||||||
|
force: bool = False,
|
||||||
|
user_id: uuid.UUID = Depends(get_current_user_id),
|
||||||
|
session: AsyncSession = Depends(get_db_session),
|
||||||
|
) -> None:
|
||||||
|
_user = await _get_user(session, user_id)
|
||||||
|
_project = await _get_owned_project(project_id, user_id, session)
|
||||||
|
try:
|
||||||
|
await delete_tool_instance(
|
||||||
|
session, user_id, project_id, repo_id, instance_id, force
|
||||||
|
)
|
||||||
|
except ValueError as exc:
|
||||||
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(exc))
|
||||||
|
except RuntimeError as exc:
|
||||||
|
detail = str(exc)
|
||||||
|
if "uncommitted changes" in detail.lower():
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_409_CONFLICT,
|
||||||
|
detail={
|
||||||
|
"message": "Repository has uncommitted changes",
|
||||||
|
"changed_files": detail,
|
||||||
|
"force_required": True,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=detail
|
||||||
|
)
|
||||||
@@ -60,8 +60,14 @@ def check_port_exposed(parsed: dict, port: int) -> bool:
|
|||||||
for service_config in parsed["services"].values():
|
for service_config in parsed["services"].values():
|
||||||
if isinstance(service_config, dict) and "ports" in service_config:
|
if isinstance(service_config, dict) and "ports" in service_config:
|
||||||
for port_mapping in service_config["ports"]:
|
for port_mapping in service_config["ports"]:
|
||||||
if isinstance(port_mapping, str) and port_str in port_mapping:
|
if isinstance(port_mapping, str):
|
||||||
return True
|
# Port mappings can be "host:container", "ip:host:container",
|
||||||
|
# "container", or ".../protocol". The container port is the
|
||||||
|
# last numeric segment before any protocol suffix.
|
||||||
|
mapping = port_mapping.split("/")[0]
|
||||||
|
parts = mapping.split(":")
|
||||||
|
if parts[-1] == port_str:
|
||||||
|
return True
|
||||||
elif isinstance(port_mapping, int) and port_mapping == port:
|
elif isinstance(port_mapping, int) and port_mapping == port:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# apps/api/src/api/user (index)
|
||||||
|
dir: apps/api/src/api/user
|
||||||
|
|
||||||
|
## role
|
||||||
|
Provides authentication, user profile management, and SSH key operations for the API backend.
|
||||||
|
## parent
|
||||||
|
index: apps/api/src/api/.pi-map.index.md
|
||||||
|
map: apps/api/src/api/.pi-map.md
|
||||||
|
## children
|
||||||
|
-
|
||||||
|
## files
|
||||||
|
- __init__.py
|
||||||
|
- auth.py
|
||||||
|
- ssh_keys.py
|
||||||
|
- users.py
|
||||||
|
## links
|
||||||
|
index: apps/api/src/api/user/.pi-map.index.md
|
||||||
|
map: apps/api/src/api/user/.pi-map.md
|
||||||
|
## workflows
|
||||||
|
- change user behavior
|
||||||
|
read: __init__.py, auth.py, ssh_keys.py
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# apps/api/src/api/user
|
||||||
|
dir: apps/api/src/api/user
|
||||||
|
|
||||||
|
index: apps/api/src/api/user/.pi-map.index.md
|
||||||
|
|
||||||
|
## role
|
||||||
|
Provides authentication, user profile management, and SSH key operations for the API backend.
|
||||||
|
## files
|
||||||
|
- __init__.py | Aggregates and exports user-related API routers from submodules for easy importing | dep: src.api.user.auth, src.api.user.ssh_keys, src.api.user.users
|
||||||
|
- auth.py | Implements OAuth2/OIDC authentication endpoints for a FastAPI application using Authentik as the identity provider, including login initiation, callback handling, logout, and current user retrieval. | exp: func:get_db_session() → AsyncGenerator[AsyncSession, None], call:SessionLocal, func:login(next) → RedirectResponse, call:Settings, call:token_urlsafe, call:build_login_redirect_url, call:logger.debug, call:RedirectResponse, call:response.set_cookie, func:callback(code: str, state: str, auth_state, auth_next, session) → RedirectResponse, call:logger.debug, call:logger.warning, call:Settings, call:httpx.AsyncClient, call:exchange_code_for_tokens, call:logger.info, call:logger.error, call:fetch_user_info, call:str, call:user_info.get, call:session.scalar, call:select(User).where, call:User, call:session.add, call:session.commit, call:session.refresh, call:create_session_cookie, call:build_cookie_options, call:cast, call:bool, call:cookie_options.get, call:RedirectResponse, call:redirect_response.set_cookie, call:redirect_response.delete_cookie, raise:HTTPException, func:logout(response: Response) → dict[str, str], call:Settings, call:build_cookie_options, call:cast, call:bool, call:str, call:cookie_options.get, call:response.delete_cookie, func:me(session_cookie, session) → dict[str, Any], call:logger.debug, call:bool, call:logger.warning, call:Settings, call:decode_session_cookie, call:session.get, call:logger.info, call:str, raise:HTTPException | dep: logging, secrets, typing, httpx, fastapi, fastapi.responses, sqlalchemy, sqlalchemy.ext.asyncio, src.auth.cookies, src.auth.oidc, src.auth.session, src.config, src.database, src.models.user
|
||||||
|
- ssh_keys.py | Implements a FastAPI router for managing Ed25519 SSH key pairs, including generation, storage with encrypted private keys, listing, deletion, and cryptographic signing/verification operations. | exp: func:_get_fernet() → Fernet, call:Settings, call:hashlib.sha256(settings.session_secret.encode()).digest, call:settings.session_secret.encode, call:base64.urlsafe_b64encode, call:Fernet, func:generate_ssh_key_pair() → tuple[str, str], call:Ed25519PrivateKey.generate, call:private_key.public_key, call:private_key.private_bytes, call:serialization.NoEncryption, call:public_key.public_bytes, call:private_bytes.decode, call:public_bytes.decode, func:create_ssh_key(data: SSHKeyCreate, user_id, session) → SSHKey, call:_get_user, call:generate_ssh_key_pair, call:_get_fernet, call:fernet.encrypt(private_key.encode()).decode, call:private_key.encode, call:SSHKey, call:session.add, call:session.commit, call:session.refresh, func:list_ssh_keys(user_id, session) → list[SSHKey], call:_get_user, call:session.execute, call:select(SSHKey).where, call:list, call:result.scalars().all, func:delete_ssh_key(key_id: uuid.UUID, user_id, session) → None, call:_get_user, call:session.get, call:session.delete, call:session.commit, raise:HTTPException, func:sign_payload(key_id: uuid.UUID, data: SignPayloadRequest, user_id, session) → SignatureResponse, call:_get_user, call:session.get, call:_get_fernet, call:fernet.decrypt(ssh_key.private_key_encrypted.encode()).decode, call:ssh_key.private_key_encrypted.encode, call:serialization.load_ssh_private_key, call:private_key_pem.encode, call:private_key.sign, call:data.payload.encode, call:SignatureResponse, call:base64.b64encode(signature).decode, raise:HTTPException, func:verify_signature(key_id: uuid.UUID, data: VerifySignatureRequest, user_id, session) → VerifySignatureResponse, call:_get_user, call:session.get, call:serialization.load_ssh_public_key, call:ssh_key.public_key.encode, call:base64.b64decode, call:public_key.verify, call:data.payload.encode, call:VerifySignatureResponse, raise:HTTPException | dep: base64, uuid, cryptography.fernet, cryptography.hazmat.primitives, cryptography.hazmat.primitives.asymmetric.ed25519, fastapi, sqlalchemy, sqlalchemy.ext.asyncio, src.auth.dependencies, src.config, src.models, src.schemas.project, hashlib, cryptography
|
||||||
|
- users.py | Implements FastAPI endpoints for authenticated user profile management including retrieval, updates, and avatar uploads. | exp: func:get_profile(user_id, session) → User, call:_get_user, func:update_profile(data: UserProfileUpdate, user_id, session) → User, call:_get_user, call:len, call:data.name.strip, call:data.email.strip, call:session.commit, call:session.refresh, raise:HTTPException, func:upload_avatar(file: UploadFile, user_id, session) → User, call:_get_user, call:file.read, call:len, call:Path, call:old_path.exists, call:old_path.unlink, call:filename_part.split(".")[-1].lower, call:uuid.uuid4, call:file_path.write_bytes, call:session.commit, call:session.refresh, raise:HTTPException | dep: uuid, pathlib, fastapi, sqlalchemy.ext.asyncio, src.auth.dependencies, src.models.user, src.schemas.user
|
||||||
|
## arch
|
||||||
|
Modular FastAPI router pattern with OAuth2/OIDC external identity provider integration, encrypted key storage, and file-based avatar handling.
|
||||||
|
## tags
|
||||||
|
user, get, call:, key, ssh, src, cookie, raise:httpexception
|
||||||
|
## symbols
|
||||||
|
- get_db_session
|
||||||
|
- login
|
||||||
|
- callback
|
||||||
|
- logout
|
||||||
|
- me
|
||||||
|
- _get_fernet
|
||||||
|
- generate_ssh_key_pair
|
||||||
|
- create_ssh_key
|
||||||
|
## workflows
|
||||||
|
- change user behavior
|
||||||
|
read: __init__.py, auth.py, ssh_keys.py
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# apps/api/src/api/workspace (index)
|
||||||
|
dir: apps/api/src/api/workspace
|
||||||
|
|
||||||
|
## role
|
||||||
|
Provides FastAPI REST endpoints for workspace management, file operations, git integration, and tool instance management within user workspaces.
|
||||||
|
## parent
|
||||||
|
index: apps/api/src/api/.pi-map.index.md
|
||||||
|
map: apps/api/src/api/.pi-map.md
|
||||||
|
## children
|
||||||
|
-
|
||||||
|
## files
|
||||||
|
- __init__.py
|
||||||
|
- workspace_files.py
|
||||||
|
- workspace_git.py
|
||||||
|
- workspace_instances.py
|
||||||
|
- workspaces.py
|
||||||
|
## links
|
||||||
|
index: apps/api/src/api/workspace/.pi-map.index.md
|
||||||
|
map: apps/api/src/api/workspace/.pi-map.md
|
||||||
|
## workflows
|
||||||
|
- change workspace behavior
|
||||||
|
read: __init__.py, workspace_files.py, workspace_git.py
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# apps/api/src/api/workspace
|
||||||
|
dir: apps/api/src/api/workspace
|
||||||
|
|
||||||
|
index: apps/api/src/api/workspace/.pi-map.index.md
|
||||||
|
|
||||||
|
## role
|
||||||
|
Provides FastAPI REST endpoints for workspace management, file operations, git integration, and tool instance management within user workspaces.
|
||||||
|
## files
|
||||||
|
- __init__.py | Aggregates and re-exports workspace API router modules for centralized access | dep: src.api.workspace.workspace_files, src.api.workspace.workspace_git, src.api.workspace.workspace_instances, src.api.workspace.workspaces
|
||||||
|
- workspace_files.py | Provides FastAPI endpoints for listing, reading, and writing files within user workspaces with optional git commit integration. | exp: func:_get_workspace(session: AsyncSession, workspace_id: uuid.UUID, user_id: uuid.UUID) → Workspace, call:session.execute, call:select(Workspace).where, call:result.scalar_one_or_none, raise:HTTPException, func:list_files(workspace_id: uuid.UUID, path, user_id, session) → dict, call:_get_workspace, call:FileService, call:service.list_directory, raise:HTTPException, func:get_file_content(workspace_id: uuid.UUID, path: str, user_id, session) → dict, call:_get_workspace, call:FileService, call:service.read_file, raise:HTTPException, func:write_file(workspace_id: uuid.UUID, data: dict, user_id, session) → dict, call:_get_workspace, call:FileService, call:data.get("path", "").strip, call:data.get("message", "").strip, call:service.write_file, call:GitOperations, call:git.commit, raise:HTTPException | dep: uuid, fastapi, sqlalchemy.ext.asyncio, src.auth.dependencies, src.models, src.services.shared.file_service, sqlalchemy, src.services.git.git_operations
|
||||||
|
- workspace_git.py | Provides FastAPI REST endpoints for git operations (status, branches, commit, push, pull, fetch, checkout, history) scoped to user workspaces. | exp: func:_get_workspace(session: AsyncSession, workspace_id: uuid.UUID, user_id: uuid.UUID) → Workspace, call:session.execute, call:select(Workspace).where, call:result.scalar_one_or_none, raise:HTTPException, func:git_status(workspace_id: uuid.UUID, user_id, session) → dict, call:_get_workspace, call:GitOperations, call:git.status, raise:HTTPException, func:git_branches(workspace_id: uuid.UUID, user_id, session) → dict, call:_get_workspace, call:GitOperations, call:git.branches, raise:HTTPException, func:git_commit(workspace_id: uuid.UUID, data: dict, user_id, session) → dict, call:_get_workspace, call:data.get("message", "").strip, call:GitOperations, call:git.commit, raise:HTTPException, func:git_push(workspace_id: uuid.UUID, user_id, session) → dict, call:_get_workspace, call:GitOperations, call:git.push, raise:HTTPException, func:git_pull(workspace_id: uuid.UUID, user_id, session) → dict, call:_get_workspace, call:GitOperations, call:git.pull, raise:HTTPException, func:git_fetch(workspace_id: uuid.UUID, user_id, session) → dict, call:_get_workspace, call:GitOperations, call:git.fetch, raise:HTTPException, func:git_checkout(workspace_id: uuid.UUID, data: dict, user_id, session) → dict, call:_get_workspace, call:data.get("branch", "").strip, call:GitOperations, call:git.checkout, call:session.commit, raise:HTTPException, func:git_history(workspace_id: uuid.UUID, path, limit, user_id, session) → dict, call:_get_workspace, call:GitOperations, call:git.history, raise:HTTPException | dep: uuid, fastapi, sqlalchemy.ext.asyncio, src.auth.dependencies, src.models, src.services.git.git_operations, sqlalchemy
|
||||||
|
- workspace_instances.py | FastAPI router providing endpoints to create and list tool instances associated with a specific workspace. | exp: func:_get_workspace(session: AsyncSession, workspace_id: uuid.UUID, user_id: uuid.UUID) → Workspace, call:session.execute, call:select(Workspace).where, call:result.scalar_one_or_none, raise:HTTPException, func:create_workspace_instance(workspace_id: uuid.UUID, data: CreateWorkspaceInstanceRequest, user_id, session) → dict, call:_get_workspace, call:session.get, call:CreateInstanceRequest, call:str, call:create_tool_instance, call:instance.created_at.isoformat, raise:HTTPException, func:list_workspace_instances(workspace_id: uuid.UUID, user_id, session) → list[dict], call:_get_workspace, call:session.execute, call:select(ToolInstance) .where(ToolInstance.workspace_id == workspace_id) .order_by, call:ToolInstance.created_at.desc, call:result.scalars().all, call:str, call:i.created_at.isoformat | dep: uuid, fastapi, sqlalchemy, sqlalchemy.ext.asyncio, src.auth.dependencies, src.models, src.schemas.tool, src.services.tool.instance_service
|
||||||
|
- workspaces.py | FastAPI router providing CRUD endpoints for workspace management with nested and top-level URL structures, including listing, creating, updating, deleting, and syncing workspaces tied to Git repositories. | exp: func:list_all_workspaces(user_id, session) → list[dict], call:select(func.count(ToolInstance.id)) .where(ToolInstance.workspace_id == Workspace.id) .correlate(Workspace) .scalar_subquery, call:func.count, call:session.execute, call:select( Workspace, GitRepository, instance_count.label("instance_count"), ) .join(GitRepository, Workspace.repo_id == GitRepository.id) .options(selectinload(GitRepository.project)) .where(Workspace.user_id == user_id) .order_by, call:instance_count.label, call:selectinload, call:Workspace.created_at.desc, call:result.all, call:str, call:ws.last_sync_at.isoformat, call:ws.created_at.isoformat, call:ws.updated_at.isoformat, func:delete_workspace_top_level(workspace_id: uuid.UUID, force, user_id, session) → dict, call:session.get, call:WorkspaceManager, call:manager.delete, call:session.commit, call:session.rollback, call:logger.error, raise:HTTPException, func:create_workspace_top_level(data: dict, user_id, session) → dict, call:data.get("repo_id", "").strip, call:uuid.UUID, call:session.get, call:data.get("name", "").strip, call:data.get("branch", "main").strip, call:WorkspaceManager, call:manager.create, call:session.add, call:session.commit, call:session.rollback, call:logger.error, call:session.refresh, call:str, call:workspace.created_at.isoformat, raise:HTTPException, func:list_workspaces(project_id: uuid.UUID, repo_id: uuid.UUID, user_id, session) → list[dict], call:_get_repo, call:select(func.count(ToolInstance.id)) .where(ToolInstance.workspace_id == Workspace.id) .correlate(Workspace) .scalar_subquery, call:func.count, call:session.execute, call:select( Workspace, instance_count.label("instance_count"), ) .where(Workspace.repo_id == repo_id) .order_by, call:instance_count.label, call:Workspace.created_at.desc, call:result.all, call:str, call:ws.last_sync_at.isoformat, call:ws.created_at.isoformat, call:ws.updated_at.isoformat, func:create_workspace(project_id: uuid.UUID, repo_id: uuid.UUID, data: dict, user_id, session) → dict, call:_get_repo, call:data.get("name", "").strip, call:data.get("branch", "main").strip, call:WorkspaceManager, call:manager.create, call:session.add, call:session.commit, call:session.rollback, call:logger.error, call:session.refresh, call:str, call:workspace.created_at.isoformat, raise:HTTPException, func:get_workspace_detail(project_id: uuid.UUID, repo_id: uuid.UUID, workspace_id: uuid.UUID, user_id, session) → dict, call:_get_repo, call:_get_workspace, call:session.execute, call:select(func.count(ToolInstance.id)).where, call:func.count, call:result.scalar, call:str, call:workspace.last_sync_at.isoformat, call:workspace.created_at.isoformat, call:workspace.updated_at.isoformat, func:update_workspace(project_id: uuid.UUID, repo_id: uuid.UUID, workspace_id: uuid.UUID, data: dict, user_id, session) → dict, call:_get_repo, call:_get_workspace, call:data.get("name", "").strip, call:data.get("branch", "").strip, call:session.commit, call:session.rollback, call:logger.error, call:str, raise:HTTPException, func:delete_workspace(project_id: uuid.UUID, repo_id: uuid.UUID, workspace_id: uuid.UUID, force, user_id, session) → dict, call:_get_repo, call:_get_workspace, call:WorkspaceManager, call:manager.delete, call:session.commit, call:session.rollback, call:logger.error, raise:HTTPException, func:sync_workspace(project_id: uuid.UUID, repo_id: uuid.UUID, workspace_id: uuid.UUID, user_id, session) → dict, call:_get_repo, call:_get_workspace, call:WorkspaceManager, call:manager.sync, call:session.commit, call:workspace.last_sync_at.isoformat, raise:HTTPException, func:_get_repo(session: AsyncSession, repo_id: uuid.UUID, project_id: uuid.UUID, user_id: uuid.UUID) → GitRepository, call:session.execute, call:select(GitRepository) .where( GitRepository.id == repo_id, GitRepository.project_id == project_id, ) .options, call:selectinload, call:result.scalar_one_or_none, raise:HTTPException, func:_get_workspace(session: AsyncSession, workspace_id: uuid.UUID, repo_id: uuid.UUID) → Workspace, call:session.execute, call:select(Workspace).where, call:result.scalar_one_or_none, raise:HTTPException | dep: logging, uuid, fastapi, sqlalchemy, sqlalchemy.ext.asyncio, sqlalchemy.orm, src.auth.dependencies, src.models, src.services.shared.workspace_manager
|
||||||
|
## arch
|
||||||
|
Modular FastAPI router pattern with domain-driven separation (files, git, instances, workspaces) using nested URL structures and git-backed workspace synchronization.
|
||||||
|
## tags
|
||||||
|
workspace, get, raise:httpexception, call:, at.isoformat, git, call:select, call:data.get
|
||||||
|
## symbols
|
||||||
|
- _get_workspace
|
||||||
|
- list_files
|
||||||
|
- get_file_content
|
||||||
|
- write_file
|
||||||
|
- git_status
|
||||||
|
- git_branches
|
||||||
|
- git_commit
|
||||||
|
- git_push
|
||||||
|
## workflows
|
||||||
|
- change workspace behavior
|
||||||
|
read: __init__.py, workspace_files.py, workspace_git.py
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -2,13 +2,16 @@
|
|||||||
|
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, HTTPException
|
from fastapi import APIRouter, Depends, HTTPException, status
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
from src.auth.dependencies import get_current_user_id, get_db_session
|
from src.auth.dependencies import get_current_user_id, get_db_session
|
||||||
|
from src.models import GitRepository
|
||||||
from src.models import ToolInstance
|
from src.models import ToolInstance
|
||||||
from src.models import Workspace
|
from src.models import Workspace
|
||||||
|
from src.schemas.tool import CreateInstanceRequest, CreateWorkspaceInstanceRequest
|
||||||
|
from src.services.tool.instance_service import create_tool_instance
|
||||||
|
|
||||||
router = APIRouter(prefix="/workspaces/{workspace_id}/instances")
|
router = APIRouter(prefix="/workspaces/{workspace_id}/instances")
|
||||||
|
|
||||||
@@ -30,6 +33,63 @@ async def _get_workspace(
|
|||||||
return workspace
|
return workspace
|
||||||
|
|
||||||
|
|
||||||
|
@router.post(
|
||||||
|
"/",
|
||||||
|
summary="Create instance from workspace",
|
||||||
|
description="Create a new tool instance mounted on this workspace.",
|
||||||
|
status_code=status.HTTP_201_CREATED,
|
||||||
|
)
|
||||||
|
async def create_workspace_instance(
|
||||||
|
workspace_id: uuid.UUID,
|
||||||
|
data: CreateWorkspaceInstanceRequest,
|
||||||
|
user_id: uuid.UUID = Depends(get_current_user_id),
|
||||||
|
session: AsyncSession = Depends(get_db_session),
|
||||||
|
) -> dict:
|
||||||
|
"""Create a tool instance directly on a workspace."""
|
||||||
|
workspace = await _get_workspace(session, workspace_id, user_id)
|
||||||
|
|
||||||
|
repo = await session.get(GitRepository, workspace.repo_id)
|
||||||
|
if repo is None:
|
||||||
|
raise HTTPException(status_code=404, detail="Repository not found")
|
||||||
|
if repo.project_id is None:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
|
detail="Repository is not associated with a project",
|
||||||
|
)
|
||||||
|
|
||||||
|
request = CreateInstanceRequest(
|
||||||
|
tool_type_id=data.tool_type_id,
|
||||||
|
display_name=data.display_name,
|
||||||
|
workspace_id=str(workspace.id),
|
||||||
|
config_profile_id=data.config_profile_id,
|
||||||
|
ssh_key_ids=data.ssh_key_ids,
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
instance = await create_tool_instance(
|
||||||
|
session, user_id, repo.project_id, repo.id, request
|
||||||
|
)
|
||||||
|
except ValueError as exc:
|
||||||
|
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc))
|
||||||
|
except RuntimeError as exc:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(exc)
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"id": str(instance.id),
|
||||||
|
"name": instance.name,
|
||||||
|
"display_name": instance.display_name,
|
||||||
|
"tool_type_id": str(instance.tool_type_id),
|
||||||
|
"status": instance.status,
|
||||||
|
"workspace_id": str(instance.workspace_id) if instance.workspace_id else None,
|
||||||
|
"selected_config_profile_id": str(instance.selected_config_profile_id)
|
||||||
|
if instance.selected_config_profile_id
|
||||||
|
else None,
|
||||||
|
"created_at": instance.created_at.isoformat(),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@router.get("/")
|
@router.get("/")
|
||||||
async def list_workspace_instances(
|
async def list_workspace_instances(
|
||||||
workspace_id: uuid.UUID,
|
workspace_id: uuid.UUID,
|
||||||
|
|||||||
@@ -36,12 +36,11 @@ async def list_all_workspaces(
|
|||||||
result = await session.execute(
|
result = await session.execute(
|
||||||
select(
|
select(
|
||||||
Workspace,
|
Workspace,
|
||||||
GitRepository.name.label("repo_name"),
|
GitRepository,
|
||||||
GitRepository.project_id,
|
|
||||||
GitRepository.ssh_key_id.label("repo_ssh_key_id"),
|
|
||||||
instance_count.label("instance_count"),
|
instance_count.label("instance_count"),
|
||||||
)
|
)
|
||||||
.join(GitRepository, Workspace.repo_id == GitRepository.id)
|
.join(GitRepository, Workspace.repo_id == GitRepository.id)
|
||||||
|
.options(selectinload(GitRepository.project))
|
||||||
.where(Workspace.user_id == user_id)
|
.where(Workspace.user_id == user_id)
|
||||||
.order_by(Workspace.created_at.desc())
|
.order_by(Workspace.created_at.desc())
|
||||||
)
|
)
|
||||||
@@ -51,11 +50,11 @@ async def list_all_workspaces(
|
|||||||
{
|
{
|
||||||
"id": str(ws.id),
|
"id": str(ws.id),
|
||||||
"name": ws.name,
|
"name": ws.name,
|
||||||
"repo_id": str(ws.repo_id),
|
"repo_id": str(repo.id),
|
||||||
"repo_name": repo_name or "",
|
"repo_name": repo.name or "",
|
||||||
"repo_ssh_key_id": str(ssh_key_id) if ssh_key_id else None,
|
"repo_ssh_key_id": str(repo.ssh_key_id) if repo.ssh_key_id else None,
|
||||||
"project_id": str(project_id) if project_id else "",
|
"project_id": str(repo.project_id) if repo.project_id else "",
|
||||||
"project_name": "",
|
"project_name": repo.project.name if repo.project else "",
|
||||||
"user_id": str(ws.user_id),
|
"user_id": str(ws.user_id),
|
||||||
"branch": ws.branch,
|
"branch": ws.branch,
|
||||||
"path": ws.path,
|
"path": ws.path,
|
||||||
@@ -65,7 +64,7 @@ async def list_all_workspaces(
|
|||||||
"updated_at": ws.updated_at.isoformat() if ws.updated_at else None,
|
"updated_at": ws.updated_at.isoformat() if ws.updated_at else None,
|
||||||
"instance_count": count or 0,
|
"instance_count": count or 0,
|
||||||
}
|
}
|
||||||
for ws, repo_name, project_id, ssh_key_id, count in rows
|
for ws, repo, count in rows
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# apps/api/src/auth (index)
|
||||||
|
dir: apps/api/src/auth
|
||||||
|
|
||||||
|
## role
|
||||||
|
Provides authentication and authorization infrastructure for the API using OIDC/OAuth2 with Authentik, session cookies, and FastAPI dependencies.
|
||||||
|
## parent
|
||||||
|
index: apps/api/src/.pi-map.index.md
|
||||||
|
map: apps/api/src/.pi-map.md
|
||||||
|
## children
|
||||||
|
-
|
||||||
|
## files
|
||||||
|
- __init__.py
|
||||||
|
- cookies.py
|
||||||
|
- dependencies.py
|
||||||
|
- oidc.py
|
||||||
|
- session.py
|
||||||
|
## links
|
||||||
|
index: apps/api/src/auth/.pi-map.index.md
|
||||||
|
map: apps/api/src/auth/.pi-map.md
|
||||||
|
## workflows
|
||||||
|
- change auth behavior
|
||||||
|
read: __init__.py, cookies.py, dependencies.py
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# apps/api/src/auth
|
||||||
|
dir: apps/api/src/auth
|
||||||
|
|
||||||
|
index: apps/api/src/auth/.pi-map.index.md
|
||||||
|
|
||||||
|
## role
|
||||||
|
Provides authentication and authorization infrastructure for the API using OIDC/OAuth2 with Authentik, session cookies, and FastAPI dependencies.
|
||||||
|
## files
|
||||||
|
- __init__.py | Re-exports authentication utility functions from submodules to provide a unified public API for the auth package | dep: src.auth.cookies, src.auth.oidc, src.auth.session
|
||||||
|
- cookies.py | Builds a dictionary of HTTP cookie options from application settings for secure cookie configuration. | exp: func:build_cookie_options(settings: Settings) → dict[str, str | bool | None] | dep: src.config, src.config.Settings
|
||||||
|
- dependencies.py | Provides FastAPI dependency functions for database sessions, user authentication, and project authorization. | exp: func:get_db_session(), call:SessionLocal, func:get_current_user_id(session_cookie) → uuid.UUID, call:Settings, call:decode_session_cookie, call:uuid.UUID, call:str, raise:HTTPException, func:get_current_user(session_cookie, db_session) → User, call:Settings, call:decode_session_cookie, call:uuid.UUID, call:str, call:db_session.get, raise:HTTPException, func:_get_user(session: AsyncSession, user_id: uuid.UUID) → User, call:session.get, raise:HTTPException, func:_get_owned_project(project_id: uuid.UUID, user_id: uuid.UUID, session: AsyncSession) → "Project", call:session.get, raise:HTTPException | dep: uuid, typing, fastapi, sqlalchemy.ext.asyncio, src.auth.session, src.config, src.database, src.models.project, src.models.user, sqlalchemy
|
||||||
|
- oidc.py | Implements OIDC/OAuth2 authentication flow with Authentik, including login URL construction, authorization code exchange for tokens, and user info retrieval. | exp: func:build_login_redirect_url(settings: Settings, redirect_uri: str, state: str) → str, call:urlencode, func:exchange_code_for_tokens(settings: Settings, code: str, redirect_uri: str, client: httpx.AsyncClient) → dict[str, str], call:client.post, call:response.raise_for_status, call:response.json, call:payload.get, func:fetch_user_info(settings: Settings, access_token: str, client: httpx.AsyncClient) → dict[str, Any], call:client.get, call:response.raise_for_status, call:response.json | dep: typing, urllib.parse, httpx, src.config
|
||||||
|
- session.py | Implements creation and verification of signed session cookies using HMAC-SHA256 in a JWS-like format. | exp: func:_base64url_encode(data: bytes) → str, call:base64.urlsafe_b64encode(data).rstrip(b"=").decode, func:_base64url_decode(data: str) → bytes, call:len, call:base64.urlsafe_b64decode, func:create_session_cookie(settings: Settings, user_id: str) → str, call:int, call:(datetime.now(timezone.utc) + timedelta(hours=settings.session_ttl_hours)).timestamp, call:datetime.now, call:timedelta, call:_base64url_encode, call:json.dumps({"alg": "HS256", "typ": "session"}).encode, call:json.dumps(payload).encode, call:hmac.new( settings.session_secret.encode(), message.encode(), hashlib.sha256, ).digest, call:settings.session_secret.encode, call:message.encode, func:decode_session_cookie(settings: Settings, cookie_value: str) → dict[str, Any], call:cookie_value.split, call:len, call:hmac.new( settings.session_secret.encode(), message.encode(), hashlib.sha256, ).digest, call:settings.session_secret.encode, call:message.encode, call:_base64url_encode, call:hmac.compare_digest, call:_base64url_decode, call:json.loads, call:payload.get, call:int, call:datetime.now(timezone.utc).timestamp, raise:ValueError | dep: hmac, hashlib, json, base64, datetime, typing, src.config, src.config.Settings
|
||||||
|
## arch
|
||||||
|
Modular security layer with separation of concerns: protocol handling (OIDC), transport security (cookies/sessions), framework integration (dependencies), and clean public API (re-exports).
|
||||||
|
## tags
|
||||||
|
session, src, cookie, user, get, base64url, raise:httpexception, call:
|
||||||
|
## symbols
|
||||||
|
- build_cookie_options
|
||||||
|
- get_db_session
|
||||||
|
- get_current_user_id
|
||||||
|
- get_current_user
|
||||||
|
- _get_user
|
||||||
|
- _get_owned_project
|
||||||
|
- build_login_redirect_url
|
||||||
|
- exchange_code_for_tokens
|
||||||
|
## workflows
|
||||||
|
- change auth behavior
|
||||||
|
read: __init__.py, cookies.py, dependencies.py
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -47,6 +47,11 @@ class Settings(BaseSettings):
|
|||||||
session_secret: str = "change-me-session-secret"
|
session_secret: str = "change-me-session-secret"
|
||||||
session_ttl_hours: int = 24
|
session_ttl_hours: int = 24
|
||||||
|
|
||||||
|
# Internal JWT configuration (used for service-to-service tokens)
|
||||||
|
jwt_algorithm: str = "HS256"
|
||||||
|
access_token_ttl_minutes: int = 15
|
||||||
|
refresh_token_ttl_days: int = 7
|
||||||
|
|
||||||
# Repository storage
|
# Repository storage
|
||||||
repo_base_path: str = "/data/repos"
|
repo_base_path: str = "/data/repos"
|
||||||
|
|
||||||
@@ -122,7 +127,7 @@ class Settings(BaseSettings):
|
|||||||
@property
|
@property
|
||||||
def cookie_samesite(self) -> str:
|
def cookie_samesite(self) -> str:
|
||||||
if self.app_env == "production":
|
if self.app_env == "production":
|
||||||
return "none"
|
return "strict"
|
||||||
|
|
||||||
return "lax"
|
return "lax"
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
|
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
|
||||||
@@ -54,10 +55,10 @@ async def init_database(
|
|||||||
result = await asyncio.get_event_loop().run_in_executor(
|
result = await asyncio.get_event_loop().run_in_executor(
|
||||||
None,
|
None,
|
||||||
lambda: subprocess.run(
|
lambda: subprocess.run(
|
||||||
["alembic", "upgrade", "head"],
|
["python3", "-m", "alembic", "upgrade", "head"],
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
cwd="/app",
|
cwd=os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ from src.api.tool import (
|
|||||||
sessions_router,
|
sessions_router,
|
||||||
tool_definitions_router,
|
tool_definitions_router,
|
||||||
tool_instances_router,
|
tool_instances_router,
|
||||||
|
tool_lifecycle_router,
|
||||||
tool_types_router,
|
tool_types_router,
|
||||||
)
|
)
|
||||||
from src.api.user import auth_router, ssh_keys_router, users_router
|
from src.api.user import auth_router, ssh_keys_router, users_router
|
||||||
@@ -165,6 +166,7 @@ app.include_router(tool_types_router)
|
|||||||
app.include_router(tool_definitions_router)
|
app.include_router(tool_definitions_router)
|
||||||
app.include_router(config_profiles_router)
|
app.include_router(config_profiles_router)
|
||||||
app.include_router(tool_instances_router)
|
app.include_router(tool_instances_router)
|
||||||
|
app.include_router(tool_lifecycle_router)
|
||||||
app.include_router(sessions_router)
|
app.include_router(sessions_router)
|
||||||
app.include_router(instance_proxy_router)
|
app.include_router(instance_proxy_router)
|
||||||
app.include_router(terminal_router)
|
app.include_router(terminal_router)
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
# apps/api/src/models (index)
|
||||||
|
dir: apps/api/src/models
|
||||||
|
|
||||||
|
## role
|
||||||
|
Centralized database model definitions and shared infrastructure for the API's data layer.
|
||||||
|
## parent
|
||||||
|
index: apps/api/src/.pi-map.index.md
|
||||||
|
map: apps/api/src/.pi-map.md
|
||||||
|
## children
|
||||||
|
- apps/api/src/models/config
|
||||||
|
index: apps/api/src/models/config/.pi-map.index.md
|
||||||
|
map: apps/api/src/models/config/.pi-map.md
|
||||||
|
- apps/api/src/models/project
|
||||||
|
index: apps/api/src/models/project/.pi-map.index.md
|
||||||
|
map: apps/api/src/models/project/.pi-map.md
|
||||||
|
- apps/api/src/models/system
|
||||||
|
index: apps/api/src/models/system/.pi-map.index.md
|
||||||
|
map: apps/api/src/models/system/.pi-map.md
|
||||||
|
- apps/api/src/models/tool
|
||||||
|
index: apps/api/src/models/tool/.pi-map.index.md
|
||||||
|
map: apps/api/src/models/tool/.pi-map.md
|
||||||
|
- apps/api/src/models/user
|
||||||
|
index: apps/api/src/models/user/.pi-map.index.md
|
||||||
|
map: apps/api/src/models/user/.pi-map.md
|
||||||
|
## files
|
||||||
|
- __init__.py
|
||||||
|
- base.py
|
||||||
|
## links
|
||||||
|
index: apps/api/src/models/.pi-map.index.md
|
||||||
|
map: apps/api/src/models/.pi-map.md
|
||||||
|
## workflows
|
||||||
|
- change models behavior
|
||||||
|
read: __init__.py, base.py
|
||||||
|
- explore models subdirectories
|
||||||
|
index: apps/api/src/models/config/.pi-map.index.md, apps/api/src/models/project/.pi-map.index.md, apps/api/src/models/system/.pi-map.index.md
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# apps/api/src/models
|
||||||
|
dir: apps/api/src/models
|
||||||
|
|
||||||
|
index: apps/api/src/models/.pi-map.index.md
|
||||||
|
|
||||||
|
## role
|
||||||
|
Centralized database model definitions and shared infrastructure for the API's data layer.
|
||||||
|
## files
|
||||||
|
- __init__.py | Re-exports model classes from submodules to provide a centralized public API for the src.models package | dep: src.models.base, src.models.config.config_profile, src.models.project.git_repository, src.models.project.project, src.models.project.workspace, src.models.system.health_check, src.models.system.instance_event, src.models.system.notification, src.models.system.terminal_session, src.models.tool.tool_definition_manifest, src.models.tool.tool_instance, src.models.tool.tool_type, src.models.user.ssh_key, src.models.user.user, src.models.user.user_config
|
||||||
|
- base.py | Defines SQLAlchemy base model and reusable mixins for UUID primary keys and automatic timestamp tracking in database models. | exp: class:Base, class:UUIDPrimaryKeyMixin, class:TimestampMixin | dep: uuid, datetime, sqlalchemy, sqlalchemy.orm
|
||||||
|
## arch
|
||||||
|
SQLAlchemy ORM with declarative base, mixin-based composition for cross-cutting concerns (UUIDs, timestamps), and explicit package-level re-exports for clean public API surface.
|
||||||
|
## tags
|
||||||
|
models, src, base, project, system, user, mixin, tool
|
||||||
|
## symbols
|
||||||
|
- Base
|
||||||
|
- UUIDPrimaryKeyMixin
|
||||||
|
- TimestampMixin
|
||||||
|
## workflows
|
||||||
|
- change models behavior
|
||||||
|
read: __init__.py, base.py
|
||||||
|
- explore models subdirectories
|
||||||
|
index: apps/api/src/models/config/.pi-map.index.md, apps/api/src/models/project/.pi-map.index.md, apps/api/src/models/system/.pi-map.index.md
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# apps/api/src/models/config (index)
|
||||||
|
dir: apps/api/src/models/config
|
||||||
|
|
||||||
|
## role
|
||||||
|
Provides SQLAlchemy ORM models for configuration profiles that manage environment variables, mounts, files, and git mounts with support for profile inheritance and user/project/tool associations.
|
||||||
|
## parent
|
||||||
|
index: apps/api/src/models/.pi-map.index.md
|
||||||
|
map: apps/api/src/models/.pi-map.md
|
||||||
|
## children
|
||||||
|
-
|
||||||
|
## files
|
||||||
|
- __init__.py
|
||||||
|
- config_profile.py
|
||||||
|
## links
|
||||||
|
index: apps/api/src/models/config/.pi-map.index.md
|
||||||
|
map: apps/api/src/models/config/.pi-map.md
|
||||||
|
## workflows
|
||||||
|
- change config behavior
|
||||||
|
read: __init__.py, config_profile.py
|
||||||
|
- change config config
|
||||||
|
read: config_profile.py
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# apps/api/src/models/config
|
||||||
|
dir: apps/api/src/models/config
|
||||||
|
|
||||||
|
index: apps/api/src/models/config/.pi-map.index.md
|
||||||
|
|
||||||
|
## role
|
||||||
|
Provides SQLAlchemy ORM models for configuration profiles that manage environment variables, mounts, files, and git mounts with support for profile inheritance and user/project/tool associations.
|
||||||
|
## files
|
||||||
|
- __init__.py | Exports config model classes from a subpackage for simplified importing | dep: src.models.config.config_profile
|
||||||
|
- config_profile.py | Defines SQLAlchemy ORM models for configuration profiles and their includes, storing environment variables, runtime hints, mounts, files, and git mounts with user/project/tool associations and self-referential inclusion relationships. | exp: class:ConfigProfile, class:ConfigProfileInclude | dep: uuid, typing, sqlalchemy, sqlalchemy.orm, src.models.base, src.models.project, src.models, src.models.user, src.models (ToolType)
|
||||||
|
## arch
|
||||||
|
Uses SQLAlchemy ORM with declarative base pattern, implementing self-referential many-to-many relationships for profile inclusion, association tables for user/project/tool ownership, and polymorphic-like configuration through profile inheritance rather than class inheritance.
|
||||||
|
## tags
|
||||||
|
config, src, models, profile, sqlalchemy, init, orm, mounts
|
||||||
|
## symbols
|
||||||
|
- ConfigProfile
|
||||||
|
- ConfigProfileInclude
|
||||||
|
## workflows
|
||||||
|
- change config behavior
|
||||||
|
read: __init__.py, config_profile.py
|
||||||
|
- change config config
|
||||||
|
read: config_profile.py
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# apps/api/src/models/project (index)
|
||||||
|
dir: apps/api/src/models/project
|
||||||
|
|
||||||
|
## role
|
||||||
|
Defines the core domain data models for project management, representing Git repositories, projects, and workspaces as SQLAlchemy ORM entities with their relationships.
|
||||||
|
## parent
|
||||||
|
index: apps/api/src/models/.pi-map.index.md
|
||||||
|
map: apps/api/src/models/.pi-map.md
|
||||||
|
## children
|
||||||
|
-
|
||||||
|
## files
|
||||||
|
- __init__.py
|
||||||
|
- git_repository.py
|
||||||
|
- project.py
|
||||||
|
- workspace.py
|
||||||
|
## links
|
||||||
|
index: apps/api/src/models/project/.pi-map.index.md
|
||||||
|
map: apps/api/src/models/project/.pi-map.md
|
||||||
|
## workflows
|
||||||
|
- change project behavior
|
||||||
|
read: __init__.py, git_repository.py, project.py
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# apps/api/src/models/project
|
||||||
|
dir: apps/api/src/models/project
|
||||||
|
|
||||||
|
index: apps/api/src/models/project/.pi-map.index.md
|
||||||
|
|
||||||
|
## role
|
||||||
|
Defines the core domain data models for project management, representing Git repositories, projects, and workspaces as SQLAlchemy ORM entities with their relationships.
|
||||||
|
## files
|
||||||
|
- __init__.py | Exports the three main project model classes (GitRepository, Project, Workspace) as the public API for the models.project package. | dep: src.models.project.git_repository, src.models.project.project, src.models.project.workspace
|
||||||
|
- git_repository.py | Defines a SQLAlchemy ORM model for Git repositories with relationships to projects, users, and SSH keys. | exp: class:GitRepository | dep: uuid, datetime, typing, sqlalchemy, sqlalchemy.orm, src.models.base, src.models.project, src.models, src.models.user, src.models.ssh_key
|
||||||
|
- project.py | Defines a SQLAlchemy ORM model for a Project entity with relationships to users, Git repositories, and SSH keys. | exp: class:Project | dep: uuid, typing, sqlalchemy, sqlalchemy.orm, src.models.base, src.models, src.models.user, src.models.GitRepository, src.models.SSHKey, src.models.user.User
|
||||||
|
- workspace.py | Defines a SQLAlchemy ORM model for persistent writable Git repository clones associated with users. | exp: class:Workspace | dep: uuid, datetime, typing, sqlalchemy, sqlalchemy.orm, src.models.base, src.models, src.models.user, src.models.GitRepository, src.models.user.User
|
||||||
|
## arch
|
||||||
|
Standard SQLAlchemy ORM model layer with declarative base pattern, using __init__.py as a public API facade to expose the three main entity classes while keeping implementation details in separate modules.
|
||||||
|
## tags
|
||||||
|
models, src, project, sqlalchemy, git, user, workspace, orm
|
||||||
|
## symbols
|
||||||
|
- GitRepository
|
||||||
|
- Project
|
||||||
|
- Workspace
|
||||||
|
## workflows
|
||||||
|
- change project behavior
|
||||||
|
read: __init__.py, git_repository.py, project.py
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# apps/api/src/models/system (index)
|
||||||
|
dir: apps/api/src/models/system
|
||||||
|
|
||||||
|
## role
|
||||||
|
Provides database persistence models for system-level operational concerns including health monitoring, audit logging, user notifications, and terminal session management.
|
||||||
|
## parent
|
||||||
|
index: apps/api/src/models/.pi-map.index.md
|
||||||
|
map: apps/api/src/models/.pi-map.md
|
||||||
|
## children
|
||||||
|
-
|
||||||
|
## files
|
||||||
|
- __init__.py
|
||||||
|
- health_check.py
|
||||||
|
- instance_event.py
|
||||||
|
- notification.py
|
||||||
|
- terminal_session.py
|
||||||
|
## links
|
||||||
|
index: apps/api/src/models/system/.pi-map.index.md
|
||||||
|
map: apps/api/src/models/system/.pi-map.md
|
||||||
|
## workflows
|
||||||
|
- change system behavior
|
||||||
|
read: __init__.py, health_check.py, instance_event.py
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# apps/api/src/models/system
|
||||||
|
dir: apps/api/src/models/system
|
||||||
|
|
||||||
|
index: apps/api/src/models/system/.pi-map.index.md
|
||||||
|
|
||||||
|
## role
|
||||||
|
Provides database persistence models for system-level operational concerns including health monitoring, audit logging, user notifications, and terminal session management.
|
||||||
|
## files
|
||||||
|
- __init__.py | Exports system-related model classes from a package for convenient importing. | dep: src.models.system.health_check, src.models.system.instance_event, src.models.system.notification, src.models.system.terminal_session
|
||||||
|
- health_check.py | Defines a SQLAlchemy ORM model for storing health check snapshots of tool instances with various status and probe fields. | exp: class:HealthCheck | dep: uuid, datetime, sqlalchemy, sqlalchemy.orm, src.models.base
|
||||||
|
- instance_event.py | Defines a SQLAlchemy ORM model for auditing instance lifecycle events with metadata, status tracking, and user attribution. | exp: class:InstanceEvent | dep: uuid, datetime, typing, sqlalchemy, sqlalchemy.orm, src.models.base
|
||||||
|
- notification.py | Defines a SQLAlchemy ORM model for storing user notifications with metadata, read/dismissed tracking, and polymorphic source references. | exp: class:Notification | dep: datetime, typing, uuid, sqlalchemy, sqlalchemy.orm, sqlalchemy.sql, src.models.base
|
||||||
|
- terminal_session.py | Defines a SQLAlchemy database model for storing terminal session metadata with lifecycle tracking. | exp: class:TerminalSessionModel | dep: uuid, datetime, sqlalchemy, sqlalchemy.orm, src.models.base
|
||||||
|
## arch
|
||||||
|
Uses SQLAlchemy ORM declarative models with consistent patterns including timestamp tracking, JSON metadata columns, enum-based status fields, and polymorphic relationships for extensible source attribution.
|
||||||
|
## tags
|
||||||
|
sqlalchemy, src, models, model, orm, system, notification, health
|
||||||
|
## symbols
|
||||||
|
- HealthCheck
|
||||||
|
- InstanceEvent
|
||||||
|
- Notification
|
||||||
|
- TerminalSessionModel
|
||||||
|
## workflows
|
||||||
|
- change system behavior
|
||||||
|
read: __init__.py, health_check.py, instance_event.py
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# apps/api/src/models/tool (index)
|
||||||
|
dir: apps/api/src/models/tool
|
||||||
|
|
||||||
|
## role
|
||||||
|
Database models for containerized tool lifecycle management, covering tool definitions, deployment instances, and categorization types.
|
||||||
|
## parent
|
||||||
|
index: apps/api/src/models/.pi-map.index.md
|
||||||
|
map: apps/api/src/models/.pi-map.md
|
||||||
|
## children
|
||||||
|
-
|
||||||
|
## files
|
||||||
|
- __init__.py
|
||||||
|
- tool_definition_manifest.py
|
||||||
|
- tool_instance.py
|
||||||
|
- tool_type.py
|
||||||
|
## links
|
||||||
|
index: apps/api/src/models/tool/.pi-map.index.md
|
||||||
|
map: apps/api/src/models/tool/.pi-map.md
|
||||||
|
## workflows
|
||||||
|
- change tool behavior
|
||||||
|
read: __init__.py, tool_definition_manifest.py, tool_instance.py
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# apps/api/src/models/tool
|
||||||
|
dir: apps/api/src/models/tool
|
||||||
|
|
||||||
|
index: apps/api/src/models/tool/.pi-map.index.md
|
||||||
|
|
||||||
|
## role
|
||||||
|
Database models for containerized tool lifecycle management, covering tool definitions, deployment instances, and categorization types.
|
||||||
|
## files
|
||||||
|
- __init__.py | Exports the public API for the tool models module by re-exporting three key classes. | dep: src.models.tool.tool_definition_manifest, src.models.tool.tool_instance, src.models.tool.tool_type
|
||||||
|
- tool_definition_manifest.py | Defines a SQLAlchemy ORM model for storing tool definition manifests that compile to Dockerfiles and Compose files, supporting both base definitions and tool-specific definitions with inheritance. | exp: class:ToolDefinitionManifest | dep: uuid, typing, sqlalchemy, sqlalchemy.orm, src.models.base, src.models.user
|
||||||
|
- tool_instance.py | Defines a SQLAlchemy ORM model for tool instances that represent deployed tools with container metadata, status tracking, and relationships to users, projects, workspaces, and other entities. | exp: class:ToolInstance | dep: uuid, datetime, typing, sqlalchemy, sqlalchemy.orm, src.models.base, src.models, src.models.project, src.models.user, src.models (ConfigProfile, GitRepository, Project, ToolType, User, Workspace)
|
||||||
|
- tool_type.py | Defines a SQLAlchemy ORM model for tool types that represent configurable categories of tools with deployment templates, manifest references, and metadata. | exp: class:ToolType | dep: uuid, typing, sqlalchemy, sqlalchemy.orm, src.models.base, src.models.tool.tool_definition_manifest, src.models.user
|
||||||
|
## arch
|
||||||
|
SQLAlchemy ORM with declarative models using inheritance hierarchies, relationship mappings, and polymorphic manifest compilation for Docker/Compose deployment.
|
||||||
|
## tags
|
||||||
|
tool, models, src, sqlalchemy, orm, definition, manifest, base
|
||||||
|
## symbols
|
||||||
|
- ToolDefinitionManifest
|
||||||
|
- ToolInstance
|
||||||
|
- ToolType
|
||||||
|
## workflows
|
||||||
|
- change tool behavior
|
||||||
|
read: __init__.py, tool_definition_manifest.py, tool_instance.py
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -53,9 +53,11 @@ class ToolInstance(UUIDPrimaryKeyMixin, TimestampMixin, Base):
|
|||||||
)
|
)
|
||||||
image_tag: Mapped[str | None] = mapped_column(String(256), nullable=True)
|
image_tag: Mapped[str | None] = mapped_column(String(256), nullable=True)
|
||||||
probe_result: Mapped[dict | None] = mapped_column(JSON, nullable=True)
|
probe_result: Mapped[dict | None] = mapped_column(JSON, nullable=True)
|
||||||
clone_mode: Mapped[str] = mapped_column(String(20), nullable=False, default="mount")
|
clone_mode: Mapped[str | None] = mapped_column(
|
||||||
|
String(20), nullable=True, default=None
|
||||||
|
)
|
||||||
branch: Mapped[str | None] = mapped_column(
|
branch: Mapped[str | None] = mapped_column(
|
||||||
String(255), nullable=True, default="main"
|
String(255), nullable=True, default=None
|
||||||
)
|
)
|
||||||
selected_config_profile_id: Mapped[uuid.UUID | None] = mapped_column(
|
selected_config_profile_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||||
UUID(), ForeignKey("config_profiles.id", ondelete="SET NULL"), nullable=True
|
UUID(), ForeignKey("config_profiles.id", ondelete="SET NULL"), nullable=True
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# apps/api/src/models/user (index)
|
||||||
|
dir: apps/api/src/models/user
|
||||||
|
|
||||||
|
## role
|
||||||
|
Provides SQLAlchemy ORM data models for user authentication, SSH key storage, and per-user configuration management.
|
||||||
|
## parent
|
||||||
|
index: apps/api/src/models/.pi-map.index.md
|
||||||
|
map: apps/api/src/models/.pi-map.md
|
||||||
|
## children
|
||||||
|
-
|
||||||
|
## files
|
||||||
|
- __init__.py
|
||||||
|
- ssh_key.py
|
||||||
|
- user.py
|
||||||
|
- user_config.py
|
||||||
|
## links
|
||||||
|
index: apps/api/src/models/user/.pi-map.index.md
|
||||||
|
map: apps/api/src/models/user/.pi-map.md
|
||||||
|
## workflows
|
||||||
|
- change user behavior
|
||||||
|
read: __init__.py, ssh_key.py, user.py
|
||||||
|
- change user config
|
||||||
|
read: user_config.py
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# apps/api/src/models/user
|
||||||
|
dir: apps/api/src/models/user
|
||||||
|
|
||||||
|
index: apps/api/src/models/user/.pi-map.index.md
|
||||||
|
|
||||||
|
## role
|
||||||
|
Provides SQLAlchemy ORM data models for user authentication, SSH key storage, and per-user configuration management.
|
||||||
|
## files
|
||||||
|
- __init__.py | Exports the user-related model classes (SSHKey, User, UserConfig) from a package for convenient importing. | dep: src.models.user.ssh_key, src.models.user.user, src.models.user.user_config
|
||||||
|
- ssh_key.py | Defines an SSHKey SQLAlchemy ORM model for storing encrypted SSH key pairs with user and optional project associations. | exp: class:SSHKey | dep: uuid, typing, sqlalchemy, sqlalchemy.orm, src.models.base, src.models.project, src.models.user
|
||||||
|
- user.py | Defines a SQLAlchemy ORM model for a User entity with authentication fields and relationships to projects, SSH keys, and user configuration. | exp: class:User | dep: typing, sqlalchemy, sqlalchemy.orm, src.models.base, src.models.project, src.models, src.models.SSHKey, src.models.UserConfig
|
||||||
|
- user_config.py | Defines a SQLAlchemy model for storing per-user JSON configuration with typed property accessors for default profile IDs. | exp: class:UserConfig | dep: uuid, typing, sqlalchemy, sqlalchemy.orm, src.models.base, src.models.user
|
||||||
|
## arch
|
||||||
|
Classic SQLAlchemy ORM model package pattern with declarative models, explicit table relationships, and a package-level __init__.py facade for clean imports.
|
||||||
|
## tags
|
||||||
|
user, src, models, sqlalchemy, sshkey, orm, model, ssh
|
||||||
|
## symbols
|
||||||
|
- SSHKey
|
||||||
|
- User
|
||||||
|
- UserConfig
|
||||||
|
## workflows
|
||||||
|
- change user behavior
|
||||||
|
read: __init__.py, ssh_key.py, user.py
|
||||||
|
- change user config
|
||||||
|
read: user_config.py
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
# apps/api/src/schemas (index)
|
||||||
|
dir: apps/api/src/schemas
|
||||||
|
|
||||||
|
## role
|
||||||
|
Defines Pydantic schemas for data validation and serialization in the API layer.
|
||||||
|
## parent
|
||||||
|
index: apps/api/src/.pi-map.index.md
|
||||||
|
map: apps/api/src/.pi-map.md
|
||||||
|
## children
|
||||||
|
- apps/api/src/schemas/config
|
||||||
|
index: apps/api/src/schemas/config/.pi-map.index.md
|
||||||
|
map: apps/api/src/schemas/config/.pi-map.md
|
||||||
|
- apps/api/src/schemas/project
|
||||||
|
index: apps/api/src/schemas/project/.pi-map.index.md
|
||||||
|
map: apps/api/src/schemas/project/.pi-map.md
|
||||||
|
- apps/api/src/schemas/system
|
||||||
|
index: apps/api/src/schemas/system/.pi-map.index.md
|
||||||
|
map: apps/api/src/schemas/system/.pi-map.md
|
||||||
|
- apps/api/src/schemas/tool
|
||||||
|
index: apps/api/src/schemas/tool/.pi-map.index.md
|
||||||
|
map: apps/api/src/schemas/tool/.pi-map.md
|
||||||
|
- apps/api/src/schemas/user
|
||||||
|
index: apps/api/src/schemas/user/.pi-map.index.md
|
||||||
|
map: apps/api/src/schemas/user/.pi-map.md
|
||||||
|
## files
|
||||||
|
- __init__.py
|
||||||
|
## links
|
||||||
|
index: apps/api/src/schemas/.pi-map.index.md
|
||||||
|
map: apps/api/src/schemas/.pi-map.md
|
||||||
|
## workflows
|
||||||
|
- change schemas behavior
|
||||||
|
read: __init__.py
|
||||||
|
- explore schemas subdirectories
|
||||||
|
index: apps/api/src/schemas/config/.pi-map.index.md, apps/api/src/schemas/project/.pi-map.index.md, apps/api/src/schemas/system/.pi-map.index.md
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# apps/api/src/schemas
|
||||||
|
dir: apps/api/src/schemas
|
||||||
|
|
||||||
|
index: apps/api/src/schemas/.pi-map.index.md
|
||||||
|
|
||||||
|
## role
|
||||||
|
Defines Pydantic schemas for data validation and serialization in the API layer.
|
||||||
|
## files
|
||||||
|
- __init__.py | Empty file with no functionality
|
||||||
|
## arch
|
||||||
|
Standard Python package structure using __init__.py to mark the directory as an importable package, likely intended for request/response models following FastAPI/Pydantic patterns.
|
||||||
|
## tags
|
||||||
|
init, empty, functionality
|
||||||
|
## symbols
|
||||||
|
-
|
||||||
|
## workflows
|
||||||
|
- change schemas behavior
|
||||||
|
read: __init__.py
|
||||||
|
- explore schemas subdirectories
|
||||||
|
index: apps/api/src/schemas/config/.pi-map.index.md, apps/api/src/schemas/project/.pi-map.index.md, apps/api/src/schemas/system/.pi-map.index.md
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# apps/api/src/schemas/config (index)
|
||||||
|
dir: apps/api/src/schemas/config
|
||||||
|
|
||||||
|
## role
|
||||||
|
Defines Pydantic validation schemas for configuration profile API request/response models with strict input validation.
|
||||||
|
## parent
|
||||||
|
index: apps/api/src/schemas/.pi-map.index.md
|
||||||
|
map: apps/api/src/schemas/.pi-map.md
|
||||||
|
## children
|
||||||
|
-
|
||||||
|
## files
|
||||||
|
- __init__.py
|
||||||
|
- config_profile.py
|
||||||
|
## links
|
||||||
|
index: apps/api/src/schemas/config/.pi-map.index.md
|
||||||
|
map: apps/api/src/schemas/config/.pi-map.md
|
||||||
|
## workflows
|
||||||
|
- change config behavior
|
||||||
|
read: __init__.py, config_profile.py
|
||||||
|
- change config config
|
||||||
|
read: config_profile.py
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# apps/api/src/schemas/config
|
||||||
|
dir: apps/api/src/schemas/config
|
||||||
|
|
||||||
|
index: apps/api/src/schemas/config/.pi-map.index.md
|
||||||
|
|
||||||
|
## role
|
||||||
|
Defines Pydantic validation schemas for configuration profile API request/response models with strict input validation.
|
||||||
|
## files
|
||||||
|
- __init__.py | Re-exports Pydantic schemas for configuration profile operations from a submodule to provide a clean public API. | dep: src.schemas.config.config_profile
|
||||||
|
- config_profile.py | Defines Pydantic request/response schemas for config profile API operations with validation for mounts, git URLs, UUIDs, and file paths. | exp: class:GitMountMapping, class:GitMountItem, class:MountItem, class:ConfigProfileCreate, class:ConfigProfileUpdate, class:ConfigProfileIncludeUpdate, class:ConfigProfileResponse, class:DefaultProfilesUpdate, class:ValidateGitUrlRequest, class:ValidateGitUrlResponse, func:_validate_uuid(v: str | None) → str | None, call:uuid.UUID, raise:ValueError | dep: uuid, pydantic, src.api.shared_validators
|
||||||
|
## arch
|
||||||
|
Standard Python package pattern using `__init__.py` re-exports to expose a clean public API from internal modules, with Pydantic-based schema-per-resource organization.
|
||||||
|
## tags
|
||||||
|
profile, config, git, mount, update, validate, response, item
|
||||||
|
## symbols
|
||||||
|
- GitMountMapping
|
||||||
|
- GitMountItem
|
||||||
|
- MountItem
|
||||||
|
- ConfigProfileCreate
|
||||||
|
- ConfigProfileUpdate
|
||||||
|
- ConfigProfileIncludeUpdate
|
||||||
|
- ConfigProfileResponse
|
||||||
|
- DefaultProfilesUpdate
|
||||||
|
## workflows
|
||||||
|
- change config behavior
|
||||||
|
read: __init__.py, config_profile.py
|
||||||
|
- change config config
|
||||||
|
read: config_profile.py
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# apps/api/src/schemas/project (index)
|
||||||
|
dir: apps/api/src/schemas/project
|
||||||
|
|
||||||
|
## role
|
||||||
|
Defines Pydantic data validation schemas for project-related API endpoints, serving as the contract layer between HTTP requests/responses and internal business logic.
|
||||||
|
## parent
|
||||||
|
index: apps/api/src/schemas/.pi-map.index.md
|
||||||
|
map: apps/api/src/schemas/.pi-map.md
|
||||||
|
## children
|
||||||
|
-
|
||||||
|
## files
|
||||||
|
- __init__.py
|
||||||
|
- git_repository.py
|
||||||
|
- project.py
|
||||||
|
- ssh_key.py
|
||||||
|
## links
|
||||||
|
index: apps/api/src/schemas/project/.pi-map.index.md
|
||||||
|
map: apps/api/src/schemas/project/.pi-map.md
|
||||||
|
## workflows
|
||||||
|
- change project behavior
|
||||||
|
read: __init__.py, git_repository.py, project.py
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# apps/api/src/schemas/project
|
||||||
|
dir: apps/api/src/schemas/project
|
||||||
|
|
||||||
|
index: apps/api/src/schemas/project/.pi-map.index.md
|
||||||
|
|
||||||
|
## role
|
||||||
|
Defines Pydantic data validation schemas for project-related API endpoints, serving as the contract layer between HTTP requests/responses and internal business logic.
|
||||||
|
## files
|
||||||
|
- __init__.py | Re-exports Pydantic schema classes from submodules to provide a unified public API for the project schemas module | dep: src.schemas.project.git_repository, src.schemas.project.project, src.schemas.project.ssh_key
|
||||||
|
- git_repository.py | Defines Pydantic request/response schemas for Git repository operations in an API. | exp: class:GitRepositoryCreate, class:URLParseRequest, class:URLParseResponse, class:GitRepositoryResponse, class:UpdateSSHKeyRequest, class:FileListResponse, class:FileContentResponse, class:BranchesResponse, class:FileUpdateRequest, class:FileUpdateResponse, class:StatusResponse, class:BranchCreateRequest, class:CheckoutRequest, class:CommitRequest, class:CommitResponse, class:FetchResponse, class:PullResponse, class:PushResponse, class:MergeRequest, class:MergeResponse | dep: uuid, datetime, pydantic
|
||||||
|
- project.py | Defines Pydantic request/response schemas for project-related API operations. | exp: class:ProjectCreate, class:ProjectUpdate, class:ProjectResponse, class:SetDefaultSSHKeyRequest | dep: uuid, pydantic
|
||||||
|
- ssh_key.py | Defines Pydantic schemas for SSH key creation, response, signing, and signature verification in an API. | exp: class:SSHKeyCreate, class:SSHKeyResponse, class:SignPayloadRequest, class:SignatureResponse, class:VerifySignatureRequest, class:VerifySignatureResponse | dep: uuid, datetime, pydantic
|
||||||
|
## arch
|
||||||
|
Modular schema decomposition with submodule-per-domain pattern (project, git_repository, ssh_key) aggregated through __init__.py barrel exports, using Pydantic models for declarative validation and serialization.
|
||||||
|
## tags
|
||||||
|
response, request, project, create, update, sshkey, pydantic, schemas
|
||||||
|
## symbols
|
||||||
|
- GitRepositoryCreate
|
||||||
|
- URLParseRequest
|
||||||
|
- URLParseResponse
|
||||||
|
- GitRepositoryResponse
|
||||||
|
- UpdateSSHKeyRequest
|
||||||
|
- FileListResponse
|
||||||
|
- FileContentResponse
|
||||||
|
- BranchesResponse
|
||||||
|
## workflows
|
||||||
|
- change project behavior
|
||||||
|
read: __init__.py, git_repository.py, project.py
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# apps/api/src/schemas/system (index)
|
||||||
|
dir: apps/api/src/schemas/system
|
||||||
|
|
||||||
|
## role
|
||||||
|
Provides Pydantic schema definitions for system health monitoring API responses.
|
||||||
|
## parent
|
||||||
|
index: apps/api/src/schemas/.pi-map.index.md
|
||||||
|
map: apps/api/src/schemas/.pi-map.md
|
||||||
|
## children
|
||||||
|
-
|
||||||
|
## files
|
||||||
|
- __init__.py
|
||||||
|
- health.py
|
||||||
|
## links
|
||||||
|
index: apps/api/src/schemas/system/.pi-map.index.md
|
||||||
|
map: apps/api/src/schemas/system/.pi-map.md
|
||||||
|
## workflows
|
||||||
|
- change system behavior
|
||||||
|
read: __init__.py, health.py
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# apps/api/src/schemas/system
|
||||||
|
dir: apps/api/src/schemas/system
|
||||||
|
|
||||||
|
index: apps/api/src/schemas/system/.pi-map.index.md
|
||||||
|
|
||||||
|
## role
|
||||||
|
Provides Pydantic schema definitions for system health monitoring API responses.
|
||||||
|
## files
|
||||||
|
- __init__.py | Re-exports health check schema classes from a submodule to provide a unified interface for the system schemas module. | dep: src.schemas.system.health
|
||||||
|
- health.py | Defines Pydantic data models for health check API responses including database, disk, and overall system health status. | exp: class:DatabaseHealth, class:DiskHealth, class:HealthChecks, class:HealthResponse, class:DatabaseHealthResponse | dep: pydantic
|
||||||
|
## arch
|
||||||
|
Simple modular schema layer with submodule organization and re-export pattern for clean API surface.
|
||||||
|
## tags
|
||||||
|
health, database, response, system, disk, check, schemas, init
|
||||||
|
## symbols
|
||||||
|
- DatabaseHealth
|
||||||
|
- DiskHealth
|
||||||
|
- HealthChecks
|
||||||
|
- HealthResponse
|
||||||
|
- DatabaseHealthResponse
|
||||||
|
## workflows
|
||||||
|
- change system behavior
|
||||||
|
read: __init__.py, health.py
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# apps/api/src/schemas/tool (index)
|
||||||
|
dir: apps/api/src/schemas/tool
|
||||||
|
|
||||||
|
## role
|
||||||
|
Defines Pydantic data validation schemas for tool instance and tool type API operations in the API layer.
|
||||||
|
## parent
|
||||||
|
index: apps/api/src/schemas/.pi-map.index.md
|
||||||
|
map: apps/api/src/schemas/.pi-map.md
|
||||||
|
## children
|
||||||
|
-
|
||||||
|
## files
|
||||||
|
- __init__.py
|
||||||
|
- tool_instance.py
|
||||||
|
- tool_type.py
|
||||||
|
## links
|
||||||
|
index: apps/api/src/schemas/tool/.pi-map.index.md
|
||||||
|
map: apps/api/src/schemas/tool/.pi-map.md
|
||||||
|
## workflows
|
||||||
|
- change tool behavior
|
||||||
|
read: __init__.py, tool_instance.py, tool_type.py
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# apps/api/src/schemas/tool
|
||||||
|
dir: apps/api/src/schemas/tool
|
||||||
|
|
||||||
|
index: apps/api/src/schemas/tool/.pi-map.index.md
|
||||||
|
|
||||||
|
## role
|
||||||
|
Defines Pydantic data validation schemas for tool instance and tool type API operations in the API layer.
|
||||||
|
## files
|
||||||
|
- __init__.py | Package initialization file that exposes tool-related schema classes for import convenience. | dep: src.schemas.tool.tool_instance, src.schemas.tool.tool_type
|
||||||
|
- tool_instance.py | Defines Pydantic request/response schemas for tool instance lifecycle operations (create, create-on-workspace, start). | exp: class:CreateInstanceRequest, class:CreateWorkspaceInstanceRequest, class:StartInstanceRequest | dep: pydantic
|
||||||
|
- tool_type.py | Defines Pydantic request/response schemas for tool type CRUD operations with conditional validation logic based on definition type (compose/dockerfile/manifest). | exp: class:ToolTypeCreate, class:ToolTypeUpdate, class:ToolTypeResponse, class:ToolTypeValidateRequest | dep: uuid, datetime, pydantic, src.api.tool.tool_types_validation
|
||||||
|
## arch
|
||||||
|
Standard Pydantic schema package pattern with type-specific conditional validation using discriminated unions or field validators based on definition type.
|
||||||
|
## tags
|
||||||
|
tool, type, request, instance, create, schemas, pydantic, response
|
||||||
|
## symbols
|
||||||
|
- CreateInstanceRequest
|
||||||
|
- CreateWorkspaceInstanceRequest
|
||||||
|
- StartInstanceRequest
|
||||||
|
- ToolTypeCreate
|
||||||
|
- ToolTypeUpdate
|
||||||
|
- ToolTypeResponse
|
||||||
|
- ToolTypeValidateRequest
|
||||||
|
## workflows
|
||||||
|
- change tool behavior
|
||||||
|
read: __init__.py, tool_instance.py, tool_type.py
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
"""Tool schemas module."""
|
"""Tool schemas module."""
|
||||||
|
|
||||||
from src.schemas.tool.tool_instance import CreateInstanceRequest, StartInstanceRequest
|
from src.schemas.tool.tool_instance import (
|
||||||
|
CreateInstanceRequest,
|
||||||
|
CreateWorkspaceInstanceRequest,
|
||||||
|
StartInstanceRequest,
|
||||||
|
)
|
||||||
from src.schemas.tool.tool_type import (
|
from src.schemas.tool.tool_type import (
|
||||||
ToolTypeCreate,
|
ToolTypeCreate,
|
||||||
ToolTypeResponse,
|
ToolTypeResponse,
|
||||||
@@ -10,6 +14,7 @@ from src.schemas.tool.tool_type import (
|
|||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"CreateInstanceRequest",
|
"CreateInstanceRequest",
|
||||||
|
"CreateWorkspaceInstanceRequest",
|
||||||
"StartInstanceRequest",
|
"StartInstanceRequest",
|
||||||
"ToolTypeCreate",
|
"ToolTypeCreate",
|
||||||
"ToolTypeResponse",
|
"ToolTypeResponse",
|
||||||
|
|||||||
@@ -13,16 +13,24 @@ class CreateInstanceRequest(BaseModel):
|
|||||||
default=None, description="Optional display name for the instance"
|
default=None, description="Optional display name for the instance"
|
||||||
)
|
)
|
||||||
workspace_id: str | None = Field(
|
workspace_id: str | None = Field(
|
||||||
default=None, description="UUID of workspace to mount (replaces clone_mode)"
|
default=None, description="UUID of workspace to mount"
|
||||||
)
|
)
|
||||||
clone_mode: str = Field(
|
config_profile_id: str | None = Field(
|
||||||
default="mount", description="Repository access mode: 'mount' or 'clone'"
|
default=None, description="Optional config profile ID for launch"
|
||||||
)
|
)
|
||||||
branch: str | None = Field(
|
ssh_key_ids: list[str] = Field(
|
||||||
default="main", description="Branch to clone (when clone_mode='clone')"
|
default_factory=list, description="SSH key IDs to mount into container ~/.ssh"
|
||||||
)
|
)
|
||||||
new_branch: str | None = Field(
|
|
||||||
default=None, description="Create a new local branch after cloning"
|
|
||||||
|
class CreateWorkspaceInstanceRequest(BaseModel):
|
||||||
|
"""Request body for creating a tool instance directly on a workspace."""
|
||||||
|
|
||||||
|
model_config = {"extra": "ignore"}
|
||||||
|
|
||||||
|
tool_type_id: str = Field(description="UUID of the tool type to instantiate")
|
||||||
|
display_name: str | None = Field(
|
||||||
|
default=None, description="Optional display name for the instance"
|
||||||
)
|
)
|
||||||
config_profile_id: str | None = Field(
|
config_profile_id: str | None = Field(
|
||||||
default=None, description="Optional config profile ID for launch"
|
default=None, description="Optional config profile ID for launch"
|
||||||
|
|||||||
@@ -82,8 +82,8 @@ class ToolTypeCreate(BaseModel):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def validate_default_port(cls, v: int, info) -> int:
|
def validate_default_port(cls, v: int, info) -> int:
|
||||||
data = info.data
|
data = info.data
|
||||||
requires_port = data.get("requires_port", True)
|
requires_port = data.get("requires_port")
|
||||||
if not requires_port:
|
if requires_port is False:
|
||||||
return v
|
return v
|
||||||
if v <= 0 or v > 65535:
|
if v <= 0 or v > 65535:
|
||||||
raise ValueError("Port must be between 1 and 65535")
|
raise ValueError("Port must be between 1 and 65535")
|
||||||
@@ -140,7 +140,7 @@ class ToolTypeCreate(BaseModel):
|
|||||||
|
|
||||||
if not check_port_exposed(parsed, self.default_port):
|
if not check_port_exposed(parsed, self.default_port):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Port {self.default_port} is not exposed in the compose template. Add it to the 'ports' section."
|
f"default_port {self.default_port} is not exposed in the compose template. Add it to the 'ports' section."
|
||||||
)
|
)
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# apps/api/src/schemas/user (index)
|
||||||
|
dir: apps/api/src/schemas/user
|
||||||
|
|
||||||
|
## role
|
||||||
|
Provides Pydantic data validation schemas for user-related API requests and responses.
|
||||||
|
## parent
|
||||||
|
index: apps/api/src/schemas/.pi-map.index.md
|
||||||
|
map: apps/api/src/schemas/.pi-map.md
|
||||||
|
## children
|
||||||
|
-
|
||||||
|
## files
|
||||||
|
- __init__.py
|
||||||
|
- user.py
|
||||||
|
- user_config.py
|
||||||
|
## links
|
||||||
|
index: apps/api/src/schemas/user/.pi-map.index.md
|
||||||
|
map: apps/api/src/schemas/user/.pi-map.md
|
||||||
|
## workflows
|
||||||
|
- change user behavior
|
||||||
|
read: __init__.py, user.py, user_config.py
|
||||||
|
- change user config
|
||||||
|
read: user_config.py
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# apps/api/src/schemas/user
|
||||||
|
dir: apps/api/src/schemas/user
|
||||||
|
|
||||||
|
index: apps/api/src/schemas/user/.pi-map.index.md
|
||||||
|
|
||||||
|
## role
|
||||||
|
Provides Pydantic data validation schemas for user-related API requests and responses.
|
||||||
|
## files
|
||||||
|
- __init__.py | Re-exports user-related Pydantic schemas from submodules to provide a unified public API for the user schemas package. | dep: src.schemas.user.user, src.schemas.user.user_config
|
||||||
|
- user.py | Defines Pydantic response and update schemas for user profile data | exp: class:UserProfileResponse, class:UserProfileUpdate | dep: uuid, pydantic
|
||||||
|
- user_config.py | Defines Pydantic models for user configuration data transfer objects (response and update schemas). | exp: class:UserConfigResponse, class:UserConfigUpdate | dep: pydantic
|
||||||
|
## arch
|
||||||
|
Modular schema organization with submodule separation of concerns and unified package-level re-exports for clean imports.
|
||||||
|
## tags
|
||||||
|
user, schemas, response, update, config, pydantic, profile, src
|
||||||
|
## symbols
|
||||||
|
- UserProfileResponse
|
||||||
|
- UserProfileUpdate
|
||||||
|
- UserConfigResponse
|
||||||
|
- UserConfigUpdate
|
||||||
|
## workflows
|
||||||
|
- change user behavior
|
||||||
|
read: __init__.py, user.py, user_config.py
|
||||||
|
- change user config
|
||||||
|
read: user_config.py
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# apps/api/src/scripts (index)
|
||||||
|
dir: apps/api/src/scripts
|
||||||
|
|
||||||
|
## role
|
||||||
|
Provides utility scripts for administrative and setup tasks for the API application, specifically database seeding.
|
||||||
|
## parent
|
||||||
|
index: apps/api/src/.pi-map.index.md
|
||||||
|
map: apps/api/src/.pi-map.md
|
||||||
|
## children
|
||||||
|
-
|
||||||
|
## files
|
||||||
|
- __init__.py
|
||||||
|
- seed.py
|
||||||
|
## links
|
||||||
|
index: apps/api/src/scripts/.pi-map.index.md
|
||||||
|
map: apps/api/src/scripts/.pi-map.md
|
||||||
|
## workflows
|
||||||
|
- change scripts behavior
|
||||||
|
read: __init__.py, seed.py
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# apps/api/src/scripts
|
||||||
|
dir: apps/api/src/scripts
|
||||||
|
|
||||||
|
index: apps/api/src/scripts/.pi-map.index.md
|
||||||
|
|
||||||
|
## role
|
||||||
|
Provides utility scripts for administrative and setup tasks for the API application, specifically database seeding.
|
||||||
|
## files
|
||||||
|
- __init__.py | Marks the directory as a Python package and provides a docstring describing it as utility scripts for the API package.
|
||||||
|
- seed.py | Seeds a development user into the database using SQLAlchemy async operations, with idempotency check to avoid duplicates. | exp: func:build_seed_user() → Mapping[str, str | None], func:seed_database(session: AsyncSession) → User, call:build_seed_user, call:session.scalar, call:select(User).where, call:User, call:session.add, call:session.commit, call:session.refresh, func:run() → None, call:SessionLocal, call:seed_database, call:print, call:str, func:main() → None, call:asyncio.run, call:run | dep: collections.abc, sqlalchemy, sqlalchemy.ext.asyncio, src.database, src.models.user, asyncio
|
||||||
|
## arch
|
||||||
|
Simple procedural script architecture with async SQLAlchemy database operations and idempotent initialization patterns.
|
||||||
|
## tags
|
||||||
|
seed, user, database, sqlalchemy, package, init, call:build, call:session.scalar
|
||||||
|
## symbols
|
||||||
|
- build_seed_user
|
||||||
|
- seed_database
|
||||||
|
- run
|
||||||
|
- main
|
||||||
|
- call:build_seed_user
|
||||||
|
- call:session.scalar
|
||||||
|
- call:select(User).where
|
||||||
|
- call:User
|
||||||
|
## workflows
|
||||||
|
- change scripts behavior
|
||||||
|
read: __init__.py, seed.py
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# apps/api/src/seeds (index)
|
||||||
|
dir: apps/api/src/seeds
|
||||||
|
|
||||||
|
## role
|
||||||
|
Provides database seeding utilities for initializing built-in tool type configurations in the API application.
|
||||||
|
## parent
|
||||||
|
index: apps/api/src/.pi-map.index.md
|
||||||
|
map: apps/api/src/.pi-map.md
|
||||||
|
## children
|
||||||
|
-
|
||||||
|
## files
|
||||||
|
- __init__.py
|
||||||
|
- builtin_tool_types.py
|
||||||
|
## links
|
||||||
|
index: apps/api/src/seeds/.pi-map.index.md
|
||||||
|
map: apps/api/src/seeds/.pi-map.md
|
||||||
|
## workflows
|
||||||
|
- change seeds behavior
|
||||||
|
read: __init__.py, builtin_tool_types.py
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# apps/api/src/seeds
|
||||||
|
dir: apps/api/src/seeds
|
||||||
|
|
||||||
|
index: apps/api/src/seeds/.pi-map.index.md
|
||||||
|
|
||||||
|
## role
|
||||||
|
Provides database seeding utilities for initializing built-in tool type configurations in the API application.
|
||||||
|
## files
|
||||||
|
- __init__.py | Marks the directory as a Python package for database seeding utilities.
|
||||||
|
- builtin_tool_types.py | Seeds built-in tool types (code-server, jupyter-notebook, opencode) into a database with upsert logic, creating or updating Docker Compose-based development environment templates. | exp: func:_table_exists(session, table_name: str) → bool, call:session.execute, call:text, call:result.scalar, func:seed_builtin_tool_types(), call:SessionLocal, call:_table_exists, call:logger.warning, call:session.scalar, call:select(ToolType).where, call:ToolType, call:tool_data.get, call:session.add, call:logger.info, call:session.commit | dep: logging, sqlalchemy, src.database, src.models
|
||||||
|
## arch
|
||||||
|
Simple procedural seeding script using SQLAlchemy upsert operations to populate reference data for containerized development environment templates.
|
||||||
|
## tags
|
||||||
|
tool, types, table, exists, builtin, call:tool, database, init
|
||||||
|
## symbols
|
||||||
|
- _table_exists
|
||||||
|
- seed_builtin_tool_types
|
||||||
|
- call:session.execute
|
||||||
|
- call:text
|
||||||
|
- call:result.scalar
|
||||||
|
- call:SessionLocal
|
||||||
|
- call:_table_exists
|
||||||
|
- call:logger.warning
|
||||||
|
## workflows
|
||||||
|
- change seeds behavior
|
||||||
|
read: __init__.py, builtin_tool_types.py
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
@@ -103,11 +103,8 @@ services:
|
|||||||
image: node:20-slim
|
image: node:20-slim
|
||||||
container_name: {{TOOL_NAME}}
|
container_name: {{TOOL_NAME}}
|
||||||
working_dir: /workspace
|
working_dir: /workspace
|
||||||
environment:
|
|
||||||
- HOME=/tmp
|
|
||||||
volumes:
|
volumes:
|
||||||
- {{REPO_PATH}}:/workspace
|
- {{REPO_PATH}}:/workspace
|
||||||
- opencode_home:/tmp
|
|
||||||
ports:
|
ports:
|
||||||
- "3000:3000"
|
- "3000:3000"
|
||||||
command: >
|
command: >
|
||||||
@@ -125,10 +122,7 @@ services:
|
|||||||
exec tail -f /dev/null"
|
exec tail -f /dev/null"
|
||||||
stdin_open: true
|
stdin_open: true
|
||||||
tty: true
|
tty: true
|
||||||
restart: unless-stopped
|
restart: unless-stopped""",
|
||||||
|
|
||||||
volumes:
|
|
||||||
opencode_home:""",
|
|
||||||
"required_variables": ["REPO_PATH", "TOOL_NAME"],
|
"required_variables": ["REPO_PATH", "TOOL_NAME"],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
# apps/api/src/services (index)
|
||||||
|
dir: apps/api/src/services
|
||||||
|
|
||||||
|
## role
|
||||||
|
Marks the services directory as a Python package for business logic layer components.
|
||||||
|
## parent
|
||||||
|
index: apps/api/src/.pi-map.index.md
|
||||||
|
map: apps/api/src/.pi-map.md
|
||||||
|
## children
|
||||||
|
- apps/api/src/services/config
|
||||||
|
index: apps/api/src/services/config/.pi-map.index.md
|
||||||
|
map: apps/api/src/services/config/.pi-map.md
|
||||||
|
- apps/api/src/services/docker
|
||||||
|
index: apps/api/src/services/docker/.pi-map.index.md
|
||||||
|
map: apps/api/src/services/docker/.pi-map.md
|
||||||
|
- apps/api/src/services/git
|
||||||
|
index: apps/api/src/services/git/.pi-map.index.md
|
||||||
|
map: apps/api/src/services/git/.pi-map.md
|
||||||
|
- apps/api/src/services/instance
|
||||||
|
index: apps/api/src/services/instance/.pi-map.index.md
|
||||||
|
map: apps/api/src/services/instance/.pi-map.md
|
||||||
|
- apps/api/src/services/shared
|
||||||
|
index: apps/api/src/services/shared/.pi-map.index.md
|
||||||
|
map: apps/api/src/services/shared/.pi-map.md
|
||||||
|
- apps/api/src/services/terminal
|
||||||
|
index: apps/api/src/services/terminal/.pi-map.index.md
|
||||||
|
map: apps/api/src/services/terminal/.pi-map.md
|
||||||
|
- apps/api/src/services/tool
|
||||||
|
index: apps/api/src/services/tool/.pi-map.index.md
|
||||||
|
map: apps/api/src/services/tool/.pi-map.md
|
||||||
|
## files
|
||||||
|
- __init__.py
|
||||||
|
## links
|
||||||
|
index: apps/api/src/services/.pi-map.index.md
|
||||||
|
map: apps/api/src/services/.pi-map.md
|
||||||
|
## workflows
|
||||||
|
- change services behavior
|
||||||
|
read: __init__.py
|
||||||
|
- explore services subdirectories
|
||||||
|
index: apps/api/src/services/config/.pi-map.index.md, apps/api/src/services/docker/.pi-map.index.md, apps/api/src/services/git/.pi-map.index.md
|
||||||
|
## dirty
|
||||||
|
-
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user