rename(547): the server is grimoire — package, image, DB defaults, backups
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 12s
CI & Build / TypeScript typecheck (push) Successful in 56s
CI & Build / integration (push) Successful in 1m4s
CI & Build / Python tests (push) Successful in 1m55s
CI & Build / Build & push image (push) Successful in 44s
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 12s
CI & Build / TypeScript typecheck (push) Successful in 56s
CI & Build / integration (push) Successful in 1m4s
CI & Build / Python tests (push) Successful in 1m55s
CI & Build / Build & push image (push) Successful in 44s
Step 2 of the Scribe -> Grimoire rename. - src/scribe -> src/grimoire, and every import, patch target, module path, hypercorn entry point and `python -m grimoire.restore` with it; pyproject, uv.lock and the frontend package are named grimoire. - Image git.fabledsword.com/bvandeusen/fabledgrimoire; CI's Postgres service runs as grimoire / grimoire_test. - Compose files default the DB user, database and (prod) volume to grimoire, each overridable from .env, so an install created as Scribe keeps its data by setting POSTGRES_USER, POSTGRES_DB and PGDATA_VOLUME. - Backups are written as grimoire-<ts>.tar with format grimoire-backup-tar. Archives named scribe-* with the scribe-backup-tar manifest still restore and are still counted at the destination (folder and S3), with tests. - Exports, the MCP auth realm, the ASGI scope key and the persistent logger carry the new name. Kept on purpose: the secret-sealing key label stays "scribe.secrets.v1", because every stored secret was derived under it. Plugin identity (plugin name, hooks, env vars, .scribe marker, skills) is step 3. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+4
-1
@@ -1,4 +1,7 @@
|
||||
# An install created before the rename from Scribe keeps its database by
|
||||
# setting these, and PGDATA_VOLUME, to the names it was created with.
|
||||
POSTGRES_USER=fabled
|
||||
POSTGRES_PASSWORD=fabled
|
||||
POSTGRES_DB=scribe
|
||||
POSTGRES_DB=grimoire
|
||||
# PGDATA_VOLUME=grimoire_pgdata
|
||||
SECRET_KEY=dev-secret-change-me
|
||||
|
||||
@@ -55,7 +55,7 @@ permissions:
|
||||
|
||||
env:
|
||||
REGISTRY: git.fabledsword.com
|
||||
IMAGE: git.fabledsword.com/bvandeusen/fabledscribe
|
||||
IMAGE: git.fabledsword.com/bvandeusen/fabledgrimoire
|
||||
|
||||
jobs:
|
||||
typecheck:
|
||||
@@ -193,11 +193,11 @@ jobs:
|
||||
# Same image as the shipped compose files: Postgres 17 with pgvector.
|
||||
image: pgvector/pgvector:pg17
|
||||
env:
|
||||
POSTGRES_USER: scribe
|
||||
POSTGRES_USER: grimoire
|
||||
POSTGRES_PASSWORD: ci_integration
|
||||
POSTGRES_DB: scribe_test
|
||||
POSTGRES_DB: grimoire_test
|
||||
options: >-
|
||||
--health-cmd "pg_isready -U scribe"
|
||||
--health-cmd "pg_isready -U grimoire"
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 10
|
||||
@@ -235,7 +235,7 @@ jobs:
|
||||
test "$(printf '%s\n' "$PG" | grep -c .)" -eq 1
|
||||
PG_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$PG")
|
||||
test -n "$PG_IP"
|
||||
export DATABASE_URL="postgresql+asyncpg://scribe:ci_integration@${PG_IP}:5432/scribe_test"
|
||||
export DATABASE_URL="postgresql+asyncpg://grimoire:ci_integration@${PG_IP}:5432/grimoire_test"
|
||||
# Wait for Postgres. The run: shell is dash (no bash /dev/tcp), so use Python.
|
||||
/opt/venv/bin/python - "$PG_IP" <<'PY'
|
||||
import socket, sys, time
|
||||
|
||||
+2
-2
@@ -31,7 +31,7 @@ RUN --mount=type=cache,target=/root/.cache/uv \
|
||||
# alembic and hypercorn in CMD have to be found there.
|
||||
ENV PATH="/app/.venv/bin:$PATH"
|
||||
|
||||
COPY --from=build-frontend /build/dist/ src/scribe/static/
|
||||
COPY --from=build-frontend /build/dist/ src/grimoire/static/
|
||||
COPY alembic.ini .
|
||||
COPY alembic/ alembic/
|
||||
|
||||
@@ -57,4 +57,4 @@ ENV APP_CHANNEL=$BUILD_CHANNEL
|
||||
ENV APP_COMMIT=$BUILD_COMMIT
|
||||
|
||||
EXPOSE 5000
|
||||
CMD ["sh", "-c", "alembic upgrade head && hypercorn 'scribe.app:create_app()' --bind 0.0.0.0:5000 --keep-alive 600"]
|
||||
CMD ["sh", "-c", "alembic upgrade head && hypercorn 'grimoire.app:create_app()' --bind 0.0.0.0:5000 --keep-alive 600"]
|
||||
|
||||
+3
-3
@@ -4,9 +4,9 @@ from logging.config import fileConfig
|
||||
from alembic import context
|
||||
from sqlalchemy.ext.asyncio import create_async_engine
|
||||
|
||||
from scribe.config import Config
|
||||
from scribe.models import Base
|
||||
from scribe.services.db_ready import wait_for_database
|
||||
from grimoire.config import Config
|
||||
from grimoire.models import Base
|
||||
from grimoire.services.db_ready import wait_for_database
|
||||
|
||||
config = context.config
|
||||
if config.config_file_name is not None:
|
||||
|
||||
@@ -43,7 +43,7 @@ _LABEL_V1 = b"scribe.secrets.v1\x00"
|
||||
def _fernet():
|
||||
from cryptography.fernet import Fernet
|
||||
|
||||
from scribe.config import Config
|
||||
from grimoire.config import Config
|
||||
|
||||
digest = hashlib.sha256(_LABEL_V1 + Config.SECRET_KEY.encode()).digest()
|
||||
return Fernet(base64.urlsafe_b64encode(digest))
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
services:
|
||||
app:
|
||||
image: git.fabledsword.com/bvandeusen/fabledscribe:latest
|
||||
image: git.fabledsword.com/bvandeusen/fabledgrimoire:latest
|
||||
environment:
|
||||
DATABASE_URL: "postgresql+asyncpg://scribe:${DB_PASSWORD}@db:5432/scribe"
|
||||
DATABASE_URL: "postgresql+asyncpg://${POSTGRES_USER:-grimoire}:${DB_PASSWORD}@db:5432/${POSTGRES_DB:-grimoire}"
|
||||
SECRET_KEY: "${SECRET_KEY}"
|
||||
LOG_LEVEL: "${LOG_LEVEL:-INFO}"
|
||||
TRUST_PROXY_HEADERS: "true"
|
||||
SECURE_COOKIES: "true"
|
||||
networks:
|
||||
- scribe_backend
|
||||
- backend
|
||||
|
||||
db:
|
||||
# Postgres 17 with the vector extension Grimoire needs.
|
||||
@@ -17,15 +17,16 @@ services:
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
environment:
|
||||
POSTGRES_USER: scribe
|
||||
POSTGRES_USER: ${POSTGRES_USER:-grimoire}
|
||||
POSTGRES_PASSWORD: "${DB_PASSWORD}"
|
||||
POSTGRES_DB: scribe
|
||||
POSTGRES_DB: ${POSTGRES_DB:-grimoire}
|
||||
networks:
|
||||
- scribe_backend
|
||||
- backend
|
||||
|
||||
volumes:
|
||||
pgdata:
|
||||
name: ${PGDATA_VOLUME:-grimoire_pgdata}
|
||||
|
||||
networks:
|
||||
scribe_backend:
|
||||
backend:
|
||||
driver: overlay
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
|
||||
services:
|
||||
app:
|
||||
image: git.fabledsword.com/bvandeusen/fabledscribe:latest
|
||||
image: git.fabledsword.com/bvandeusen/fabledgrimoire:latest
|
||||
ports:
|
||||
- "5000:5000"
|
||||
environment:
|
||||
DATABASE_URL: "postgresql+asyncpg://scribe:scribe@db:5432/scribe"
|
||||
DATABASE_URL: "postgresql+asyncpg://${POSTGRES_USER:-grimoire}:${POSTGRES_PASSWORD:-grimoire}@db:5432/${POSTGRES_DB:-grimoire}"
|
||||
# Set SECRET_KEY in the environment or a .env file beside this one.
|
||||
SECRET_KEY: "${SECRET_KEY:-change-me-in-production}"
|
||||
LOG_LEVEL: "${LOG_LEVEL:-INFO}"
|
||||
@@ -26,9 +26,9 @@ services:
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
environment:
|
||||
POSTGRES_USER: scribe
|
||||
POSTGRES_PASSWORD: scribe
|
||||
POSTGRES_DB: scribe
|
||||
POSTGRES_USER: ${POSTGRES_USER:-grimoire}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-grimoire}
|
||||
POSTGRES_DB: ${POSTGRES_DB:-grimoire}
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
|
||||
+4
-4
@@ -11,7 +11,7 @@ services:
|
||||
# Or a bind mount, for direct host access to the app's data:
|
||||
# - ./data:/data
|
||||
environment:
|
||||
DATABASE_URL: "postgresql+asyncpg://${POSTGRES_USER:-scribe}:${POSTGRES_PASSWORD:-scribe}@db:5432/${POSTGRES_DB:-scribe}"
|
||||
DATABASE_URL: "postgresql+asyncpg://${POSTGRES_USER:-grimoire}:${POSTGRES_PASSWORD:-grimoire}@db:5432/${POSTGRES_DB:-grimoire}"
|
||||
SECRET_KEY: "${SECRET_KEY:-dev-secret-change-me}"
|
||||
# Optional: a SearXNG instance to list as the web-search backend in Integrations.
|
||||
# SEARXNG_URL: "http://searxng:8080"
|
||||
@@ -24,9 +24,9 @@ services:
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
environment:
|
||||
POSTGRES_USER: ${POSTGRES_USER:-scribe}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-scribe}
|
||||
POSTGRES_DB: ${POSTGRES_DB:-scribe}
|
||||
POSTGRES_USER: ${POSTGRES_USER:-grimoire}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-grimoire}
|
||||
POSTGRES_DB: ${POSTGRES_DB:-grimoire}
|
||||
|
||||
volumes:
|
||||
pgdata:
|
||||
|
||||
@@ -35,7 +35,7 @@ Click **Revoke** next to the key in the keys table and confirm. Revoked keys are
|
||||
Grimoire exposes itself as a set of MCP tools that Claude (and other MCP clients)
|
||||
can use to read and write your notes, tasks, projects, rulebooks, and more. The
|
||||
server is **built into the app** — it is mounted as a streamable-HTTP endpoint
|
||||
at **`/mcp`** on the running Grimoire instance (`src/scribe/mcp/server.py`). There
|
||||
at **`/mcp`** on the running Grimoire instance (`src/grimoire/mcp/server.py`). There
|
||||
is nothing to install: no wheel, no separate package, no CLI. You connect a
|
||||
client straight to the URL with a Bearer token.
|
||||
|
||||
@@ -90,7 +90,7 @@ specific project at a dev instance or an admin key.
|
||||
### Available Tools
|
||||
|
||||
There are well over a hundred tools and they change with the app, so the
|
||||
registration in **`src/scribe/mcp/tools/`** is the reference. By family:
|
||||
registration in **`src/grimoire/mcp/tools/`** is the reference. By family:
|
||||
|
||||
| Family | Examples | Purpose |
|
||||
|--------|----------|---------|
|
||||
@@ -111,4 +111,4 @@ registration in **`src/scribe/mcp/tools/`** is the reference. By family:
|
||||
Server-level usage guidance — when to reach for each entity, the
|
||||
recall-before-acting reflex, and the rulebook conventions — is delivered to the
|
||||
client automatically via the MCP server's `instructions` block (defined in
|
||||
`src/scribe/mcp/server.py`).
|
||||
`src/grimoire/mcp/server.py`).
|
||||
|
||||
@@ -294,7 +294,7 @@ These serve the plugin's hooks.
|
||||
## Grimoire MCP
|
||||
|
||||
Claude clients connect to the built-in MCP server at `POST /mcp` (streamable HTTP,
|
||||
Bearer auth with an `fmcp_` key), served by `src/scribe/mcp/`. It is not a REST
|
||||
Bearer auth with an `fmcp_` key), served by `src/grimoire/mcp/`. It is not a REST
|
||||
surface — it exposes the same data as typed tools (`create_note`, `create_task`,
|
||||
`start_planning`, `search`, `enter_project`, …) with
|
||||
server-level usage guidance delivered in the MCP `instructions` block. See
|
||||
|
||||
@@ -24,10 +24,10 @@ Grimoire is one app container and one PostgreSQL container. There is no language
|
||||
```
|
||||
|
||||
- **The web UI** is a Vue single-page app, built into the image and served by Quart. Every unknown path that isn't under `/api/` falls back to `index.html`; `/api/*` always answers JSON.
|
||||
- **The REST API** (`src/scribe/routes/`) backs the UI, one blueprint per resource.
|
||||
- **The MCP server** (`src/scribe/mcp/`) is how Claude reads and writes records. Each module in `mcp/tools/` registers its tools, and the server's instructions and each tool's docstring are the contract an agent reads. Requests authenticate with an API key as a Bearer token.
|
||||
- **The REST API** (`src/grimoire/routes/`) backs the UI, one blueprint per resource.
|
||||
- **The MCP server** (`src/grimoire/mcp/`) is how Claude reads and writes records. Each module in `mcp/tools/` registers its tools, and the server's instructions and each tool's docstring are the contract an agent reads. Requests authenticate with an API key as a Bearer token.
|
||||
- **The plugin** (`plugin/`) runs in the user's Claude Code. Its hooks call `/api/plugin/*` to put the right context in front of Claude at the right moment: the project at session start, matching rules and prior art before a prompt or a tool call, the rules mounted on a moment of work, and a check on the reply that ends a turn. Its skills describe the working practices. See [API Keys & MCP](api-keys-and-mcp.md) to install it.
|
||||
- **Services** (`src/scribe/services/`) hold the logic both doors share. Routes and MCP tools stay thin: parse the request, check access, call a service, return a plain dict.
|
||||
- **Services** (`src/grimoire/services/`) hold the logic both doors share. Routes and MCP tools stay thin: parse the request, check access, call a service, return a plain dict.
|
||||
|
||||
## Data model
|
||||
|
||||
@@ -80,14 +80,14 @@ Credentials Grimoire stores for outside services (forge tokens, the backup desti
|
||||
|
||||
```
|
||||
├── alembic/versions/ database migrations
|
||||
├── src/scribe/
|
||||
├── src/grimoire/
|
||||
│ ├── app.py app factory, blueprints, startup and shutdown
|
||||
│ ├── config.py environment configuration
|
||||
│ ├── models/ SQLAlchemy models
|
||||
│ ├── routes/ REST blueprints
|
||||
│ ├── mcp/ MCP server and its tool modules
|
||||
│ ├── services/ shared logic
|
||||
│ └── restore.py `python -m scribe.restore` for a fresh instance
|
||||
│ └── restore.py `python -m grimoire.restore` for a fresh instance
|
||||
├── frontend/src/ views, components, stores, composables, api client
|
||||
├── plugin/ Claude Code hooks, skills and commands
|
||||
├── scripts/ CI checks and the plugin version minter
|
||||
|
||||
+11
-8
@@ -11,9 +11,12 @@ Set it up under **Settings → Data → Scheduled backups** (admins only).
|
||||
One file per run, named for the moment it was taken in UTC:
|
||||
|
||||
```
|
||||
scribe-20261015T020000Z.tar
|
||||
grimoire-20261015T020000Z.tar
|
||||
```
|
||||
|
||||
Backups written while the app was called Scribe are named `scribe-…tar`.
|
||||
They are counted with the rest and restore the same way.
|
||||
|
||||
It is a plain, **uncompressed** tar:
|
||||
|
||||
- `manifest.json` comes first. It holds the backup format version, the Grimoire
|
||||
@@ -65,12 +68,12 @@ A path **inside the Grimoire container**, normally a share mounted there:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
scribe:
|
||||
grimoire:
|
||||
volumes:
|
||||
- /nfs/backup/scribe:/backups
|
||||
- /nfs/backup/grimoire:/backups
|
||||
```
|
||||
|
||||
Grimoire writes each file under a hidden name (`.scribe-….tar.part`) and renames
|
||||
Grimoire writes each file under a hidden name (`.grimoire-….tar.part`) and renames
|
||||
it once it is complete. A program watching the folder never sees half a
|
||||
backup.
|
||||
|
||||
@@ -115,7 +118,7 @@ page asks for the secret again.
|
||||
[Fabled Archives](https://git.fabledsword.com/bvandeusen/fabledarchives)
|
||||
keeps service backups as "backup targets". Either destination works with it.
|
||||
|
||||
1. On Archives' **Backups** page, make a target for Grimoire, named `scribe`
|
||||
1. On Archives' **Backups** page, make a target for Grimoire, named `grimoire`
|
||||
for example.
|
||||
2. Choose how Grimoire's files arrive:
|
||||
- **S3**: turn on the S3 way in for the target and make an **append-only**
|
||||
@@ -124,8 +127,8 @@ keeps service backups as "backup targets". Either destination works with it.
|
||||
- bucket: the target's name;
|
||||
- access key and secret key: the key you just made.
|
||||
- **A folder**: mount the share Archives watches into Grimoire as
|
||||
`/backups/scribe`, and point the target at that folder. In Grimoire, choose
|
||||
**A folder** with `/backups/scribe`.
|
||||
`/backups/grimoire`, and point the target at that folder. In Grimoire, choose
|
||||
**A folder** with `/backups/grimoire`.
|
||||
3. Set the target's **Retention** to **Archives**. Archives then keeps the
|
||||
newest backups and thins the rest by day, week and month. Grimoire only adds.
|
||||
4. Give the target an expected cadence of **daily**, so a missed backup turns
|
||||
@@ -141,7 +144,7 @@ Start Grimoire on an empty database, copy the backup file into the container,
|
||||
and run:
|
||||
|
||||
```sh
|
||||
docker exec -it scribe python -m scribe.restore /backups/scribe-20261015T020000Z.tar
|
||||
docker exec -it grimoire python -m grimoire.restore /backups/grimoire-20261015T020000Z.tar
|
||||
```
|
||||
|
||||
- The command refuses a database that already has an account. A restore
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Grimoire reads its configuration from environment variables. Most settings people change day to day (registration, SMTP, retrieval thresholds, backups, forge connections) live in **Settings** in the web UI instead.
|
||||
|
||||
Any variable marked *secret* can also be read from a file: set `<NAME>_FILE` to the path, for example a Docker secret at `/run/secrets/scribe_key`.
|
||||
Any variable marked *secret* can also be read from a file: set `<NAME>_FILE` to the path, for example a Docker secret at `/run/secrets/grimoire_key`.
|
||||
|
||||
## Environment variables
|
||||
|
||||
@@ -10,7 +10,7 @@ Any variable marked *secret* can also be read from a file: set `<NAME>_FILE` to
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `DATABASE_URL` *(secret)* | `postgresql+asyncpg://scribe:scribe@localhost:5432/scribe` | PostgreSQL connection string, using the `asyncpg` driver |
|
||||
| `DATABASE_URL` *(secret)* | `postgresql+asyncpg://grimoire:grimoire@localhost:5432/grimoire` | PostgreSQL connection string, using the `asyncpg` driver |
|
||||
| `SECRET_KEY` *(secret)* | `dev-secret-change-me` | Signs sessions and seals stored credentials. Set it before first use and keep it (see below) |
|
||||
| `BASE_URL` | `http://localhost:5000` | Public URL, used in email links and OIDC redirects |
|
||||
| `SECURE_COOKIES` | `false` | `true` when Grimoire is served over HTTPS |
|
||||
@@ -79,9 +79,11 @@ docker compose up --build
|
||||
[`docker-compose.prod.yml`](../docker-compose.prod.yml) is a stack file: the app and database on a private overlay network, with `SECURE_COOKIES` and `TRUST_PROXY_HEADERS` on. Provide `DB_PASSWORD` and `SECRET_KEY` in the environment:
|
||||
|
||||
```bash
|
||||
DB_PASSWORD=... SECRET_KEY=... docker stack deploy -c docker-compose.prod.yml scribe
|
||||
DB_PASSWORD=... SECRET_KEY=... docker stack deploy -c docker-compose.prod.yml grimoire
|
||||
```
|
||||
|
||||
The database user, database and volume default to `grimoire`. An install created under the name Scribe keeps its data by setting `POSTGRES_USER`, `POSTGRES_DB` and `PGDATA_VOLUME` to the names it was created with.
|
||||
|
||||
The shipped files leave out healthchecks and restart policies on purpose. The app waits for its database on startup, and the orchestrator's defaults restart it if it ever exits.
|
||||
|
||||
### Volumes
|
||||
|
||||
+1
-1
@@ -65,7 +65,7 @@ Work happens on `dev`. A release merges `dev` into `main` with a plain merge com
|
||||
|
||||
### Backend
|
||||
|
||||
- Services open their own session: `async with async_session() as session:` (from `scribe.models`).
|
||||
- Services open their own session: `async with async_session() as session:` (from `grimoire.models`).
|
||||
- Routes and MCP tools stay thin; the logic lives in `services/`, shared by both.
|
||||
- Access checks go through `services/access.py`, never inline ownership checks.
|
||||
- Every wait on something outside the process has a deadline.
|
||||
|
||||
+1
-1
@@ -86,7 +86,7 @@ Share a project, note or task with users or groups, as viewer, editor or admin.
|
||||
|
||||
- **Personal export**: your notes and tasks as a Markdown ZIP (with YAML frontmatter) or JSON.
|
||||
- **Admin backup and restore**, with ids remapped so a backup restores onto another instance.
|
||||
- **Scheduled backups**: a daily, dated, uncompressed tar to a folder or an S3-compatible bucket, laid out so a deduplicating tool such as restic stores only what changed. `python -m scribe.restore` brings a fresh instance back from one. See [Backups](backups.md).
|
||||
- **Scheduled backups**: a daily, dated, uncompressed tar to a folder or an S3-compatible bucket, laid out so a deduplicating tool such as restic stores only what changed. `python -m grimoire.restore` brings a fresh instance back from one. See [Backups](backups.md).
|
||||
|
||||
## Sign-in
|
||||
|
||||
|
||||
Generated
+2
-2
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "scribe-frontend",
|
||||
"name": "grimoire-frontend",
|
||||
"version": "0.1.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "scribe-frontend",
|
||||
"name": "grimoire-frontend",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@fullcalendar/core": "^6.1.20",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "scribe-frontend",
|
||||
"name": "grimoire-frontend",
|
||||
"private": true,
|
||||
"version": "0.1.0",
|
||||
"type": "module",
|
||||
|
||||
@@ -2,7 +2,7 @@ import { apiGet } from "./client";
|
||||
|
||||
/**
|
||||
* What `/api/version` answers — the client's half of `build_version_payload`
|
||||
* (`src/scribe/routes/api.py`), which is where the reasoning for the shape is
|
||||
* (`src/grimoire/routes/api.py`), which is where the reasoning for the shape is
|
||||
* written down.
|
||||
*
|
||||
* EVERY FIELD BUT `version` IS OPTIONAL, and an absent one means "this build
|
||||
|
||||
@@ -187,7 +187,7 @@ async function backUpNow() {
|
||||
</ul>
|
||||
|
||||
<p class="intro">
|
||||
Each run adds one dated file, <code>scribe-<date>.tar</code>, holding every table
|
||||
Each run adds one dated file, <code>grimoire-<date>.tar</code>, holding every table
|
||||
as plain lines. It is not compressed, so a deduplicating tool such as restic stores only what
|
||||
changed since the day before. Grimoire never deletes old backups — the destination keeps and
|
||||
thins the history. Sharing, API keys and forge connections are not included.
|
||||
@@ -237,7 +237,7 @@ async function backUpNow() {
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Bucket</span>
|
||||
<input v-model.trim="form.s3_bucket" class="fs-input" placeholder="scribe" />
|
||||
<input v-model.trim="form.s3_bucket" class="fs-input" placeholder="grimoire" />
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Access key</span>
|
||||
|
||||
@@ -1068,7 +1068,7 @@ async function exportData(scope: "user" | "full") {
|
||||
const blob = new Blob([JSON.stringify(data, null, 2)], { type: "application/json" });
|
||||
const a = document.createElement("a");
|
||||
a.href = URL.createObjectURL(blob);
|
||||
a.download = `scribe-backup-${scope}-${new Date().toISOString().slice(0, 10)}.json`;
|
||||
a.download = `grimoire-backup-${scope}-${new Date().toISOString().slice(0, 10)}.json`;
|
||||
a.click();
|
||||
URL.revokeObjectURL(a.href);
|
||||
toastStore.show("Backup downloaded");
|
||||
@@ -1091,7 +1091,7 @@ async function exportNotes(format: "markdown" | "json") {
|
||||
const stamp = new Date().toISOString().slice(0, 10);
|
||||
const a = document.createElement("a");
|
||||
a.href = URL.createObjectURL(blob);
|
||||
a.download = `scribe-${stamp}.${ext}`;
|
||||
a.download = `grimoire-${stamp}.${ext}`;
|
||||
a.click();
|
||||
URL.revokeObjectURL(a.href);
|
||||
toastStore.show("Export downloaded");
|
||||
@@ -1319,7 +1319,7 @@ async function handleRestoreFile(event: Event) {
|
||||
if (res.status === 413) {
|
||||
throw new Error(
|
||||
"the file is bigger than the web upload allows. Restore it with " +
|
||||
"`python -m scribe.restore <file>` inside the Grimoire container instead.",
|
||||
"`python -m grimoire.restore <file>` inside the Grimoire container instead.",
|
||||
);
|
||||
}
|
||||
if (!res.ok) {
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ requires = ["setuptools>=68.0", "setuptools-scm>=8.0"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "scribe"
|
||||
name = "grimoire"
|
||||
version = "0.1.0"
|
||||
description = "Self-hosted note-taking, project-management, and calendar app exposed to Claude via MCP"
|
||||
requires-python = ">=3.14"
|
||||
|
||||
@@ -103,7 +103,7 @@ async def run(args) -> None:
|
||||
print(f" - {r['title']}: {preview}{empty_flag}")
|
||||
return
|
||||
|
||||
from scribe.services import rulebooks as rb_service
|
||||
from grimoire.services import rulebooks as rb_service
|
||||
|
||||
existing = await rb_service.find_rulebook_by_title(args.user_id, args.rulebook_title)
|
||||
if existing is not None and not args.force:
|
||||
|
||||
@@ -5,38 +5,38 @@ from pathlib import Path
|
||||
|
||||
from quart import Quart, g, jsonify, make_response, request, send_from_directory
|
||||
|
||||
from scribe.config import Config
|
||||
from scribe.routes.admin import admin_bp
|
||||
from scribe.routes.api import api
|
||||
from scribe.routes.auth import auth_bp
|
||||
from scribe.routes.export import export_bp
|
||||
from scribe.routes.notes import notes_bp
|
||||
from scribe.routes.milestones import milestones_bp
|
||||
from scribe.routes.task_logs import task_logs_bp
|
||||
from scribe.routes.projects import projects_bp
|
||||
from scribe.routes.retrieval import retrieval_bp
|
||||
from scribe.routes.settings import settings_bp
|
||||
from scribe.routes.tasks import tasks_bp
|
||||
from scribe.routes.groups import groups_bp
|
||||
from scribe.routes.shares import shares_bp
|
||||
from scribe.routes.in_app_notifications import notifications_bp
|
||||
from scribe.routes.users import users_bp
|
||||
from scribe.routes.api_keys import api_keys_bp
|
||||
from scribe.routes.search import search_bp
|
||||
from scribe.routes.knowledge import knowledge_bp
|
||||
from scribe.routes.rulebooks import rulebooks_bp
|
||||
from scribe.routes.plugin import plugin_bp
|
||||
from scribe.routes.design_systems import design_systems_bp
|
||||
from scribe.routes.trash import trash_bp
|
||||
from scribe.routes.dashboard import dashboard_bp
|
||||
from scribe.routes.systems import systems_bp
|
||||
from scribe.routes.canonical_systems import canonical_systems_bp
|
||||
from scribe.routes.platforms import platforms_bp
|
||||
from scribe.routes.family import family_bp
|
||||
from scribe.routes.lessons import lessons_bp
|
||||
from scribe.routes.snippets import snippets_bp
|
||||
from scribe.routes.webhooks import webhooks_bp
|
||||
from scribe.mcp import mount_mcp
|
||||
from grimoire.config import Config
|
||||
from grimoire.routes.admin import admin_bp
|
||||
from grimoire.routes.api import api
|
||||
from grimoire.routes.auth import auth_bp
|
||||
from grimoire.routes.export import export_bp
|
||||
from grimoire.routes.notes import notes_bp
|
||||
from grimoire.routes.milestones import milestones_bp
|
||||
from grimoire.routes.task_logs import task_logs_bp
|
||||
from grimoire.routes.projects import projects_bp
|
||||
from grimoire.routes.retrieval import retrieval_bp
|
||||
from grimoire.routes.settings import settings_bp
|
||||
from grimoire.routes.tasks import tasks_bp
|
||||
from grimoire.routes.groups import groups_bp
|
||||
from grimoire.routes.shares import shares_bp
|
||||
from grimoire.routes.in_app_notifications import notifications_bp
|
||||
from grimoire.routes.users import users_bp
|
||||
from grimoire.routes.api_keys import api_keys_bp
|
||||
from grimoire.routes.search import search_bp
|
||||
from grimoire.routes.knowledge import knowledge_bp
|
||||
from grimoire.routes.rulebooks import rulebooks_bp
|
||||
from grimoire.routes.plugin import plugin_bp
|
||||
from grimoire.routes.design_systems import design_systems_bp
|
||||
from grimoire.routes.trash import trash_bp
|
||||
from grimoire.routes.dashboard import dashboard_bp
|
||||
from grimoire.routes.systems import systems_bp
|
||||
from grimoire.routes.canonical_systems import canonical_systems_bp
|
||||
from grimoire.routes.platforms import platforms_bp
|
||||
from grimoire.routes.family import family_bp
|
||||
from grimoire.routes.lessons import lessons_bp
|
||||
from grimoire.routes.snippets import snippets_bp
|
||||
from grimoire.routes.webhooks import webhooks_bp
|
||||
from grimoire.mcp import mount_mcp
|
||||
|
||||
STATIC_DIR = Path(__file__).parent / "static"
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -131,7 +131,7 @@ def create_app() -> Quart:
|
||||
# Log usage for API requests (skip logs endpoint to avoid recursion)
|
||||
if request.path.startswith("/api/") and not request.path.startswith("/api/admin/logs"):
|
||||
try:
|
||||
from scribe.services.logging import log_usage
|
||||
from grimoire.services.logging import log_usage
|
||||
|
||||
user = getattr(g, "user", None)
|
||||
await log_usage(
|
||||
@@ -173,13 +173,13 @@ def create_app() -> Quart:
|
||||
# `create_app`, which runs before either is true.
|
||||
_startup_finished = asyncio.Event()
|
||||
|
||||
from scribe.services.auth import start_auth_token_retention_loop
|
||||
from scribe.services.embeddings import (
|
||||
from grimoire.services.auth import start_auth_token_retention_loop
|
||||
from grimoire.services.embeddings import (
|
||||
backfill_milestone_embeddings, backfill_note_embeddings,
|
||||
backfill_rule_embeddings, backfill_system_embeddings,
|
||||
)
|
||||
from scribe.services.logging import start_log_retention_loop
|
||||
from scribe.services.notifications import start_notification_loop
|
||||
from grimoire.services.logging import start_log_retention_loop
|
||||
from grimoire.services.notifications import start_notification_loop
|
||||
|
||||
start_log_retention_loop()
|
||||
start_notification_loop()
|
||||
@@ -200,7 +200,7 @@ def create_app() -> Quart:
|
||||
# answer this exists to stop giving. Failure is logged and swallowed —
|
||||
# provenance is worth a lot and never worth refusing to boot.
|
||||
try:
|
||||
from scribe.services.retrieval_tuning import record_release_defaults
|
||||
from grimoire.services.retrieval_tuning import record_release_defaults
|
||||
moved = await record_release_defaults()
|
||||
for row in moved:
|
||||
if not row["baseline"]:
|
||||
@@ -255,7 +255,7 @@ def create_app() -> Quart:
|
||||
# row would read as "no snippet here" rather than as a gap. Separate
|
||||
# try block so neither backfill can skip the other.
|
||||
try:
|
||||
from scribe.services.snippets import backfill_snippet_data
|
||||
from grimoire.services.snippets import backfill_snippet_data
|
||||
await backfill_snippet_data()
|
||||
except Exception:
|
||||
logger.warning("Snippet data backfill failed", exc_info=True)
|
||||
@@ -272,17 +272,17 @@ def create_app() -> Quart:
|
||||
# living on as a coroutine nobody will ever wake.
|
||||
try:
|
||||
# Recurrence scheduler (recurring-task spawn every 15m)
|
||||
from scribe.services.recurrence_scheduler import start_recurrence_scheduler
|
||||
from grimoire.services.recurrence_scheduler import start_recurrence_scheduler
|
||||
start_recurrence_scheduler(asyncio.get_running_loop())
|
||||
|
||||
# Version-pinning scheduler (daily auto-pin scan at 03:00 UTC)
|
||||
from scribe.services.version_pinning_scheduler import (
|
||||
from grimoire.services.version_pinning_scheduler import (
|
||||
start_version_pinning_scheduler,
|
||||
)
|
||||
start_version_pinning_scheduler(asyncio.get_running_loop())
|
||||
|
||||
# Trash retention scheduler (daily expired-trash purge at 03:30 UTC)
|
||||
from scribe.services.trash_scheduler import start_trash_scheduler
|
||||
from grimoire.services.trash_scheduler import start_trash_scheduler
|
||||
start_trash_scheduler(asyncio.get_running_loop())
|
||||
|
||||
# DB maintenance scheduler (daily targeted VACUUM ANALYZE, default
|
||||
@@ -291,7 +291,7 @@ def create_app() -> Quart:
|
||||
# comment above `_STARTUP_READ_TIMEOUT`. Anything added here that
|
||||
# touches the database needs the same treatment: a lifespan hook
|
||||
# that does not return is a worker that never serves.
|
||||
from scribe.services.db_maintenance_scheduler import (
|
||||
from grimoire.services.db_maintenance_scheduler import (
|
||||
get_maintenance_hour,
|
||||
start_db_maintenance_scheduler,
|
||||
)
|
||||
@@ -301,13 +301,13 @@ def create_app() -> Quart:
|
||||
|
||||
# Scheduled backups. An hourly tick that reads its config when it
|
||||
# fires, so starting it reads nothing here.
|
||||
from scribe.services.backup_job import start_backup_scheduler
|
||||
from grimoire.services.backup_job import start_backup_scheduler
|
||||
start_backup_scheduler(asyncio.get_running_loop())
|
||||
|
||||
# Diagnostic instrumentation — heartbeat, signal handlers, asyncio
|
||||
# exception hook. Cheap (~1 log line/min), high diagnostic value when
|
||||
# the app crashes mysteriously. See services/diagnostics.py.
|
||||
from scribe.services.diagnostics import start_diagnostics
|
||||
from grimoire.services.diagnostics import start_diagnostics
|
||||
start_diagnostics(asyncio.get_running_loop())
|
||||
finally:
|
||||
# STARTUP IS OVER — release the backfill. Everything above
|
||||
@@ -318,21 +318,21 @@ def create_app() -> Quart:
|
||||
|
||||
@app.after_serving
|
||||
async def shutdown():
|
||||
from scribe.services.recurrence_scheduler import stop_recurrence_scheduler
|
||||
from grimoire.services.recurrence_scheduler import stop_recurrence_scheduler
|
||||
stop_recurrence_scheduler()
|
||||
from scribe.services.version_pinning_scheduler import (
|
||||
from grimoire.services.version_pinning_scheduler import (
|
||||
stop_version_pinning_scheduler,
|
||||
)
|
||||
stop_version_pinning_scheduler()
|
||||
from scribe.services.trash_scheduler import stop_trash_scheduler
|
||||
from grimoire.services.trash_scheduler import stop_trash_scheduler
|
||||
stop_trash_scheduler()
|
||||
from scribe.services.db_maintenance_scheduler import (
|
||||
from grimoire.services.db_maintenance_scheduler import (
|
||||
stop_db_maintenance_scheduler,
|
||||
)
|
||||
stop_db_maintenance_scheduler()
|
||||
from scribe.services.backup_job import stop_backup_scheduler
|
||||
from grimoire.services.backup_job import stop_backup_scheduler
|
||||
stop_backup_scheduler()
|
||||
from scribe.services.diagnostics import stop_diagnostics
|
||||
from grimoire.services.diagnostics import stop_diagnostics
|
||||
stop_diagnostics()
|
||||
|
||||
@app.route("/")
|
||||
@@ -382,7 +382,7 @@ def create_app() -> Quart:
|
||||
logger.exception("Internal server error on %s %s", request.method, request.path)
|
||||
|
||||
try:
|
||||
from scribe.services.logging import log_error
|
||||
from grimoire.services.logging import log_error
|
||||
|
||||
user = getattr(g, "user", None)
|
||||
await log_error(
|
||||
@@ -2,8 +2,8 @@ import functools
|
||||
|
||||
from quart import g, jsonify, request, session
|
||||
|
||||
from scribe.services.auth import get_user_by_id
|
||||
from scribe.services.api_keys import lookup_key
|
||||
from grimoire.services.auth import get_user_by_id
|
||||
from grimoire.services.api_keys import lookup_key
|
||||
|
||||
|
||||
def _check_auth(f, required_role: str | None = None):
|
||||
@@ -20,7 +20,7 @@ class Config:
|
||||
DATABASE_URL: str = _read_secret(
|
||||
"DATABASE_URL",
|
||||
"DATABASE_URL_FILE",
|
||||
"postgresql+asyncpg://scribe:scribe@localhost:5432/scribe",
|
||||
"postgresql+asyncpg://grimoire:grimoire@localhost:5432/grimoire",
|
||||
)
|
||||
SECRET_KEY: str = _read_secret("SECRET_KEY", "SECRET_KEY_FILE", "dev-secret-change-me")
|
||||
SECURE_COOKIES: bool = os.environ.get("SECURE_COOKIES", "").lower() in ("1", "true", "yes")
|
||||
@@ -3,6 +3,6 @@
|
||||
Auth uses the existing `api_keys` table: a Bearer token in the Authorization
|
||||
header resolves to a user, and every tool call acts on that user's data only.
|
||||
"""
|
||||
from scribe.mcp.server import build_mcp_server, mount_mcp
|
||||
from grimoire.mcp.server import build_mcp_server, mount_mcp
|
||||
|
||||
__all__ = ["build_mcp_server", "mount_mcp"]
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Per-request MCP context.
|
||||
|
||||
The ASGI middleware (mcp/server.py) populates `_user_id_ctx` from
|
||||
scope['scribe_user_id'] before dispatching to MCPServer tool handlers.
|
||||
scope['grimoire_user_id'] before dispatching to MCPServer tool handlers.
|
||||
Tool functions then call `current_user_id()` to retrieve it.
|
||||
|
||||
Tools must never fall back to a default user — `current_user_id()` raises
|
||||
@@ -9,7 +9,7 @@ if called outside a properly-authed MCP request.
|
||||
"""
|
||||
from contextvars import ContextVar
|
||||
|
||||
_user_id_ctx: ContextVar[int | None] = ContextVar("scribe_mcp_user_id", default=None)
|
||||
_user_id_ctx: ContextVar[int | None] = ContextVar("grimoire_mcp_user_id", default=None)
|
||||
|
||||
|
||||
def current_user_id() -> int:
|
||||
@@ -1,7 +1,7 @@
|
||||
"""MCP-side Bearer token resolution. Reuses the existing api_keys infrastructure."""
|
||||
from __future__ import annotations
|
||||
|
||||
from scribe.services.api_keys import lookup_key
|
||||
from grimoire.services.api_keys import lookup_key
|
||||
|
||||
|
||||
|
||||
@@ -354,7 +354,7 @@ def build_mcp_server() -> MCPServer:
|
||||
here: since SDK v2 they belong to the ASGI app, built in `mount_mcp`.
|
||||
"""
|
||||
mcp = StrictArgsMCPServer("scribe", instructions=_INSTRUCTIONS.strip())
|
||||
from scribe.mcp.tools import register_all
|
||||
from grimoire.mcp.tools import register_all
|
||||
register_all(mcp)
|
||||
return mcp
|
||||
|
||||
@@ -364,7 +364,7 @@ def mount_mcp(app: Quart) -> None:
|
||||
|
||||
A small ASGI middleware between Quart and the MCPServer sub-app validates the
|
||||
Bearer token against the api_keys table. Authenticated requests have their
|
||||
user_id attached to the ASGI scope under "scribe_user_id" for tool handlers
|
||||
user_id attached to the ASGI scope under "grimoire_user_id" for tool handlers
|
||||
to read.
|
||||
|
||||
The SDK's streamable_http session manager owns a task group that must be
|
||||
@@ -373,7 +373,7 @@ def mount_mcp(app: Quart) -> None:
|
||||
inside Quart, we hook the session manager's `run()` async context manager
|
||||
into Quart's serving lifecycle (before_serving / after_serving).
|
||||
"""
|
||||
from scribe.mcp.auth import resolve_bearer
|
||||
from grimoire.mcp.auth import resolve_bearer
|
||||
|
||||
mcp = build_mcp_server()
|
||||
# stateless_http=True: don't hand the client a persistent Mcp-Session-Id.
|
||||
@@ -427,7 +427,7 @@ def mount_mcp(app: Quart) -> None:
|
||||
"status": 401,
|
||||
"headers": [
|
||||
(b"content-type", b"application/json"),
|
||||
(b"www-authenticate", b'Bearer realm="scribe-mcp"'),
|
||||
(b"www-authenticate", b'Bearer realm="grimoire-mcp"'),
|
||||
],
|
||||
})
|
||||
await send({
|
||||
@@ -456,8 +456,8 @@ def mount_mcp(app: Quart) -> None:
|
||||
})
|
||||
return
|
||||
|
||||
scope["scribe_user_id"] = user_id
|
||||
from scribe.mcp._context import _user_id_ctx
|
||||
scope["grimoire_user_id"] = user_id
|
||||
from grimoire.mcp._context import _user_id_ctx
|
||||
token = _user_id_ctx.set(user_id)
|
||||
try:
|
||||
await mcp_asgi(scope, receive, send)
|
||||
@@ -4,7 +4,7 @@ Each tool module exposes a `register(mcp)` function that attaches its tools
|
||||
to an MCPServer instance. `register_all(mcp)` is the single entry point called
|
||||
from `mcp.server.build_mcp_server`.
|
||||
"""
|
||||
from scribe.mcp.tools import (
|
||||
from grimoire.mcp.tools import (
|
||||
design_systems, family, lessons, milestones, notes, processes, projects, recent, repos,
|
||||
moments, platforms, retrieval_review, retrieval_tuning,
|
||||
wide_net,
|
||||
@@ -19,10 +19,10 @@ Sentinels, matching the milestone/task tool conventions:
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from scribe.mcp._context import current_user_id
|
||||
from scribe.services import design_systems as ds_svc
|
||||
from scribe.services.design_systems import DesignSystemCycle
|
||||
from scribe.services.design_starter_roles import (
|
||||
from grimoire.mcp._context import current_user_id
|
||||
from grimoire.services import design_systems as ds_svc
|
||||
from grimoire.services.design_systems import DesignSystemCycle
|
||||
from grimoire.services.design_starter_roles import (
|
||||
ALL_GROUPS,
|
||||
DEFAULT_TOKEN_PREFIX,
|
||||
describe_groups,
|
||||
@@ -6,9 +6,9 @@ and enforced by the service, and every decision is logged with its reason.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from scribe.mcp._context import current_user_id
|
||||
from scribe.services import family as family_svc
|
||||
from scribe.services import family_adoption as adoption_svc
|
||||
from grimoire.mcp._context import current_user_id
|
||||
from grimoire.services import family as family_svc
|
||||
from grimoire.services import family_adoption as adoption_svc
|
||||
|
||||
|
||||
async def list_family_ideas(
|
||||
@@ -9,17 +9,17 @@ would be something the writer had to know to include.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from scribe.mcp._context import current_user_id
|
||||
from scribe.services import access as access_svc
|
||||
from scribe.services import dedup as dedup_svc
|
||||
from scribe.services import knowledge as knowledge_svc
|
||||
from scribe.services import lesson_rules as lesson_rules_svc
|
||||
from scribe.services import lessons as lessons_svc
|
||||
from scribe.services import systems as systems_svc
|
||||
from scribe.services import trash as trash_svc
|
||||
from scribe.mcp.tools import systems as systems_tools
|
||||
from scribe.services.note_usage import attach_usage, record_pulled
|
||||
from scribe.services import moment_delivery
|
||||
from grimoire.mcp._context import current_user_id
|
||||
from grimoire.services import access as access_svc
|
||||
from grimoire.services import dedup as dedup_svc
|
||||
from grimoire.services import knowledge as knowledge_svc
|
||||
from grimoire.services import lesson_rules as lesson_rules_svc
|
||||
from grimoire.services import lessons as lessons_svc
|
||||
from grimoire.services import systems as systems_svc
|
||||
from grimoire.services import trash as trash_svc
|
||||
from grimoire.mcp.tools import systems as systems_tools
|
||||
from grimoire.services.note_usage import attach_usage, record_pulled
|
||||
from grimoire.services import moment_delivery
|
||||
|
||||
|
||||
# The payload shape lives in the service (`lesson_to_dict`), shared with the
|
||||
@@ -11,16 +11,16 @@ Sentinels:
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from scribe.mcp._context import current_user_id
|
||||
from scribe.services import dedup as dedup_svc
|
||||
from scribe.services import family as family_svc
|
||||
from scribe.services import milestones as milestones_svc
|
||||
from scribe.services import notes as notes_svc
|
||||
from scribe.services import task_logs as task_logs_svc
|
||||
from scribe.services import rulebooks as rulebooks_svc
|
||||
from scribe.services import trash as trash_svc
|
||||
from scribe.services.record_refs import refuse_guessed_ids
|
||||
from scribe.services import moment_delivery
|
||||
from grimoire.mcp._context import current_user_id
|
||||
from grimoire.services import dedup as dedup_svc
|
||||
from grimoire.services import family as family_svc
|
||||
from grimoire.services import milestones as milestones_svc
|
||||
from grimoire.services import notes as notes_svc
|
||||
from grimoire.services import task_logs as task_logs_svc
|
||||
from grimoire.services import rulebooks as rulebooks_svc
|
||||
from grimoire.services import trash as trash_svc
|
||||
from grimoire.services.record_refs import refuse_guessed_ids
|
||||
from grimoire.services import moment_delivery
|
||||
|
||||
|
||||
async def list_milestones(project_id: int) -> dict:
|
||||
@@ -12,11 +12,11 @@ operator's yes.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from scribe.mcp._context import current_user_id
|
||||
from scribe.services import moment_actions as actions_svc
|
||||
from scribe.services import moments as moments_svc
|
||||
from scribe.services import reply_shapes as shapes_svc
|
||||
from scribe.services import rule_moment_judgments as judgments_svc
|
||||
from grimoire.mcp._context import current_user_id
|
||||
from grimoire.services import moment_actions as actions_svc
|
||||
from grimoire.services import moments as moments_svc
|
||||
from grimoire.services import reply_shapes as shapes_svc
|
||||
from grimoire.services import rule_moment_judgments as judgments_svc
|
||||
|
||||
|
||||
async def list_moments() -> dict:
|
||||
@@ -13,18 +13,18 @@ Sentinel conventions (inherited from existing fable-mcp tools):
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from scribe.mcp._context import current_user_id
|
||||
from scribe.mcp.tools import systems as systems_tools
|
||||
from scribe.services import access as access_svc
|
||||
from scribe.services import dedup as dedup_svc
|
||||
from scribe.services import family as family_svc
|
||||
from scribe.services import notes as notes_svc
|
||||
from scribe.services import supersession as supersession_svc
|
||||
from scribe.services import systems as systems_svc
|
||||
from scribe.services import trash as trash_svc
|
||||
from scribe.services.note_usage import record_pulled
|
||||
from scribe.services.record_refs import refuse_guessed_ids
|
||||
from scribe.services import moment_delivery
|
||||
from grimoire.mcp._context import current_user_id
|
||||
from grimoire.mcp.tools import systems as systems_tools
|
||||
from grimoire.services import access as access_svc
|
||||
from grimoire.services import dedup as dedup_svc
|
||||
from grimoire.services import family as family_svc
|
||||
from grimoire.services import notes as notes_svc
|
||||
from grimoire.services import supersession as supersession_svc
|
||||
from grimoire.services import systems as systems_svc
|
||||
from grimoire.services import trash as trash_svc
|
||||
from grimoire.services.note_usage import record_pulled
|
||||
from grimoire.services.record_refs import refuse_guessed_ids
|
||||
from grimoire.services import moment_delivery
|
||||
|
||||
|
||||
async def list_notes(
|
||||
@@ -5,8 +5,8 @@ platforms decide which family ideas reach it.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from scribe.mcp._context import current_user_id
|
||||
from scribe.services import platforms as platforms_svc
|
||||
from grimoire.mcp._context import current_user_id
|
||||
from grimoire.services import platforms as platforms_svc
|
||||
|
||||
|
||||
async def list_platforms(project_id: int = 0) -> dict:
|
||||
@@ -6,17 +6,17 @@ get_process is the fire mechanism: it returns the full prompt for Claude to run.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from scribe.mcp._context import current_user_id
|
||||
from scribe.services import access as access_svc
|
||||
from scribe.services import dedup as dedup_svc
|
||||
from scribe.services import knowledge as knowledge_svc
|
||||
from scribe.services import moment_actions
|
||||
from scribe.services import moments as moments_svc
|
||||
from scribe.services import notes as notes_svc
|
||||
from scribe.services import systems as systems_svc
|
||||
from scribe.services import trash as trash_svc
|
||||
from scribe.services.note_usage import record_pulled
|
||||
from scribe.services import moment_delivery
|
||||
from grimoire.mcp._context import current_user_id
|
||||
from grimoire.services import access as access_svc
|
||||
from grimoire.services import dedup as dedup_svc
|
||||
from grimoire.services import knowledge as knowledge_svc
|
||||
from grimoire.services import moment_actions
|
||||
from grimoire.services import moments as moments_svc
|
||||
from grimoire.services import notes as notes_svc
|
||||
from grimoire.services import systems as systems_svc
|
||||
from grimoire.services import trash as trash_svc
|
||||
from grimoire.services.note_usage import record_pulled
|
||||
from grimoire.services import moment_delivery
|
||||
|
||||
|
||||
async def list_processes(
|
||||
@@ -9,22 +9,22 @@ from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from scribe.mcp._context import current_user_id
|
||||
from scribe.mcp.tools import systems as systems_tools
|
||||
from scribe.services import coverage as coverage_svc
|
||||
from scribe.services import design_systems as design_systems_svc
|
||||
from scribe.services import family_adoption as family_adoption_svc
|
||||
from scribe.services import inception as inception_svc
|
||||
from scribe.services import milestones as milestones_svc
|
||||
from scribe.services import notes as notes_svc
|
||||
from scribe.services import platforms as platforms_svc
|
||||
from scribe.services import projects as projects_svc
|
||||
from scribe.services import reply_shapes as reply_shapes_svc
|
||||
from scribe.services import rulebooks as rulebooks_svc
|
||||
from scribe.services import systems as systems_svc
|
||||
from scribe.services import trash as trash_svc
|
||||
from scribe.services.background import spawn
|
||||
from scribe.services.note_usage import record_surfaced
|
||||
from grimoire.mcp._context import current_user_id
|
||||
from grimoire.mcp.tools import systems as systems_tools
|
||||
from grimoire.services import coverage as coverage_svc
|
||||
from grimoire.services import design_systems as design_systems_svc
|
||||
from grimoire.services import family_adoption as family_adoption_svc
|
||||
from grimoire.services import inception as inception_svc
|
||||
from grimoire.services import milestones as milestones_svc
|
||||
from grimoire.services import notes as notes_svc
|
||||
from grimoire.services import platforms as platforms_svc
|
||||
from grimoire.services import projects as projects_svc
|
||||
from grimoire.services import reply_shapes as reply_shapes_svc
|
||||
from grimoire.services import rulebooks as rulebooks_svc
|
||||
from grimoire.services import systems as systems_svc
|
||||
from grimoire.services import trash as trash_svc
|
||||
from grimoire.services.background import spawn
|
||||
from grimoire.services.note_usage import record_surfaced
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -13,10 +13,10 @@ from datetime import datetime, timedelta, timezone
|
||||
|
||||
from sqlalchemy import select
|
||||
|
||||
from scribe.mcp._context import current_user_id
|
||||
from scribe.models import async_session
|
||||
from scribe.models.note import Note
|
||||
from scribe.models.project import Project
|
||||
from grimoire.mcp._context import current_user_id
|
||||
from grimoire.models import async_session
|
||||
from grimoire.models.note import Note
|
||||
from grimoire.models.project import Project
|
||||
|
||||
|
||||
async def get_recent(days: int = 7, limit: int = 25) -> dict:
|
||||
@@ -8,9 +8,9 @@ it survives re-clones and ssh/https URL differences.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from scribe.mcp._context import current_user_id
|
||||
from scribe.services import projects as projects_svc
|
||||
from scribe.services import repo_bindings as repo_bindings_svc
|
||||
from grimoire.mcp._context import current_user_id
|
||||
from grimoire.services import projects as projects_svc
|
||||
from grimoire.services import repo_bindings as repo_bindings_svc
|
||||
|
||||
|
||||
async def bind_repo(repo_url: str, project_id: int, ref: str = "") -> dict:
|
||||
+2
-2
@@ -4,8 +4,8 @@
|
||||
not. These two say whether the line RELATED, which is a reading only someone
|
||||
looking at the query beside the line can do.
|
||||
"""
|
||||
from scribe.mcp._context import current_user_id
|
||||
from scribe.services import retrieval_review as review_svc
|
||||
from grimoire.mcp._context import current_user_id
|
||||
from grimoire.services import retrieval_review as review_svc
|
||||
|
||||
|
||||
async def menus_to_review(
|
||||
+3
-3
@@ -4,9 +4,9 @@ The write half of the loop `retrieval_telemetry` opens. That tool says what a
|
||||
bar did; these say what the bar IS, and let it be changed with the argument
|
||||
attached.
|
||||
"""
|
||||
from scribe.mcp._context import current_user_id
|
||||
from scribe.services import retrieval_migration as migration_svc
|
||||
from scribe.services import retrieval_tuning as tuning_svc
|
||||
from grimoire.mcp._context import current_user_id
|
||||
from grimoire.services import retrieval_migration as migration_svc
|
||||
from grimoire.services import retrieval_tuning as tuning_svc
|
||||
|
||||
|
||||
async def retrieval_surfaces() -> dict:
|
||||
@@ -10,15 +10,15 @@ preview-style warning.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from scribe.mcp._context import current_user_id
|
||||
from scribe.services import dedup as dedup_svc
|
||||
from scribe.services import moments as moments_svc
|
||||
from scribe.services import rulebooks as rulebooks_svc
|
||||
from scribe.services import trash as trash_svc
|
||||
from scribe.services.rule_usage import (
|
||||
from grimoire.mcp._context import current_user_id
|
||||
from grimoire.services import dedup as dedup_svc
|
||||
from grimoire.services import moments as moments_svc
|
||||
from grimoire.services import rulebooks as rulebooks_svc
|
||||
from grimoire.services import trash as trash_svc
|
||||
from grimoire.services.rule_usage import (
|
||||
record_rule_outcome, record_rule_pulled,
|
||||
)
|
||||
from scribe.services import moment_delivery
|
||||
from grimoire.services import moment_delivery
|
||||
|
||||
|
||||
# ── Rulebook CRUD ───────────────────────────────────────────────────────
|
||||
@@ -9,16 +9,16 @@ from __future__ import annotations
|
||||
|
||||
import time
|
||||
|
||||
from scribe.mcp._context import current_user_id
|
||||
from scribe.services.access import owner_names_for
|
||||
from scribe.services.knowledge import content_type_filters
|
||||
from scribe.services.text import MATCHED_PASSAGE, excerpt_fields
|
||||
from scribe.services.embeddings import (
|
||||
from grimoire.mcp._context import current_user_id
|
||||
from grimoire.services.access import owner_names_for
|
||||
from grimoire.services.knowledge import content_type_filters
|
||||
from grimoire.services.text import MATCHED_PASSAGE, excerpt_fields
|
||||
from grimoire.services.embeddings import (
|
||||
DEFAULT_SIMILARITY_THRESHOLD, semantic_search_milestones, semantic_search_notes,
|
||||
semantic_search_rules, semantic_search_systems,
|
||||
)
|
||||
from scribe.services import rulebooks as rulebooks_svc
|
||||
from scribe.services.retrieval_telemetry import record_retrieval, retrieval_summary
|
||||
from grimoire.services import rulebooks as rulebooks_svc
|
||||
from grimoire.services.retrieval_telemetry import record_retrieval, retrieval_summary
|
||||
|
||||
|
||||
# A matched chunk is at most _CHUNK_CHAR_BUDGET (1400) characters, and it is
|
||||
@@ -109,7 +109,7 @@ async def _search_milestones(uid: int, q: str, limit: int, project_id: int) -> d
|
||||
chunks = report.get("best_chunk") or {}
|
||||
progress: dict[int, dict] = {}
|
||||
if raw:
|
||||
from scribe.services import milestones as milestones_svc
|
||||
from grimoire.services import milestones as milestones_svc
|
||||
|
||||
for pid in {m.project_id for _s, m in raw}:
|
||||
for row in await milestones_svc.get_project_milestone_summary(uid, pid):
|
||||
@@ -9,9 +9,9 @@ turn that the sync has not seen yet.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from scribe.mcp._context import current_user_id
|
||||
from scribe.services import coverage as coverage_svc
|
||||
from scribe.services import shape_ledger as shape_ledger_svc
|
||||
from grimoire.mcp._context import current_user_id
|
||||
from grimoire.services import coverage as coverage_svc
|
||||
from grimoire.services import shape_ledger as shape_ledger_svc
|
||||
|
||||
|
||||
async def classify_shapes(
|
||||
@@ -10,15 +10,15 @@ on create, System association passthrough).
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from scribe.mcp._context import current_user_id
|
||||
from scribe.mcp.tools import systems as systems_tools
|
||||
from scribe.services import access as access_svc
|
||||
from scribe.services import dedup as dedup_svc
|
||||
from scribe.services import family as family_svc
|
||||
from scribe.services import snippets as snippets_svc
|
||||
from scribe.services.note_usage import attach_usage, record_pulled
|
||||
from scribe.services import systems as systems_svc
|
||||
from scribe.services import moment_delivery
|
||||
from grimoire.mcp._context import current_user_id
|
||||
from grimoire.mcp.tools import systems as systems_tools
|
||||
from grimoire.services import access as access_svc
|
||||
from grimoire.services import dedup as dedup_svc
|
||||
from grimoire.services import family as family_svc
|
||||
from grimoire.services import snippets as snippets_svc
|
||||
from grimoire.services.note_usage import attach_usage, record_pulled
|
||||
from grimoire.services import systems as systems_svc
|
||||
from grimoire.services import moment_delivery
|
||||
|
||||
|
||||
async def list_snippets(
|
||||
@@ -288,7 +288,7 @@ async def get_snippet(snippet_id: int, project_id: int = 0) -> dict:
|
||||
# The structured consumer map: ledger rows judged against this
|
||||
# snippet. Attached only when non-empty — and never for projects
|
||||
# the caller can't read.
|
||||
from scribe.services import shape_ledger as shape_ledger_svc
|
||||
from grimoire.services import shape_ledger as shape_ledger_svc
|
||||
|
||||
consumers = await shape_ledger_svc.snippet_consumers(uid, int(note.id))
|
||||
if consumers["instances"]:
|
||||
@@ -13,13 +13,13 @@ Sentinels (match the milestone/task tool conventions):
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from scribe.mcp._context import current_user_id
|
||||
from scribe.services import canonical_systems as canonical_systems_svc
|
||||
from scribe.services import milestones as milestones_svc
|
||||
from scribe.services import notes as notes_svc
|
||||
from scribe.services import systems as systems_svc
|
||||
from scribe.services import system_usage as system_usage_svc
|
||||
from scribe.services.system_usage import record_system_pulled
|
||||
from grimoire.mcp._context import current_user_id
|
||||
from grimoire.services import canonical_systems as canonical_systems_svc
|
||||
from grimoire.services import milestones as milestones_svc
|
||||
from grimoire.services import notes as notes_svc
|
||||
from grimoire.services import systems as systems_svc
|
||||
from grimoire.services import system_usage as system_usage_svc
|
||||
from grimoire.services.system_usage import record_system_pulled
|
||||
|
||||
# Below this, a project is young enough that the mild "which area is this
|
||||
# about?" question stays proportionate; at or above it, a zero-Systems project
|
||||
@@ -8,9 +8,9 @@ from __future__ import annotations
|
||||
|
||||
from sqlalchemy import select
|
||||
|
||||
from scribe.mcp._context import current_user_id
|
||||
from scribe.models import async_session
|
||||
from scribe.models.note import Note
|
||||
from grimoire.mcp._context import current_user_id
|
||||
from grimoire.models import async_session
|
||||
from grimoire.models.note import Note
|
||||
|
||||
|
||||
def _aggregate_tag_counts(tag_lists) -> dict[str, int]:
|
||||
@@ -18,30 +18,30 @@ Sentinels (preserved from existing fable-mcp):
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from scribe.mcp._context import current_user_id
|
||||
from scribe.mcp.tools import systems as systems_tools
|
||||
from scribe.services import access as access_svc
|
||||
from scribe.services import dedup as dedup_svc
|
||||
from scribe.services import family as family_svc
|
||||
from scribe.services import family_adoption as family_adoption_svc
|
||||
from scribe.services import milestones as milestones_svc
|
||||
from scribe.services import notes as notes_svc
|
||||
from grimoire.mcp._context import current_user_id
|
||||
from grimoire.mcp.tools import systems as systems_tools
|
||||
from grimoire.services import access as access_svc
|
||||
from grimoire.services import dedup as dedup_svc
|
||||
from grimoire.services import family as family_svc
|
||||
from grimoire.services import family_adoption as family_adoption_svc
|
||||
from grimoire.services import milestones as milestones_svc
|
||||
from grimoire.services import notes as notes_svc
|
||||
# Imported by NAME, not reached through notes_svc: minted_kind is pure
|
||||
# validation, not a service call, and a test that stubs the service module to
|
||||
# avoid the database would otherwise stub the validation too — turning a
|
||||
# guard into a MagicMock that approves anything.
|
||||
from scribe.services.notes import minted_kind
|
||||
from scribe.services import placement as placement_svc
|
||||
from scribe.services import planning as planning_svc
|
||||
from scribe.services import record_batch as batch_svc
|
||||
from scribe.services import rulebooks as rulebooks_svc
|
||||
from scribe.services import systems as systems_svc
|
||||
from scribe.services import task_logs as task_logs_svc
|
||||
from scribe.services import trash as trash_svc
|
||||
from scribe.services.note_usage import record_pulled
|
||||
from scribe.services.record_refs import refuse_guessed_ids
|
||||
from scribe.services.text import elide
|
||||
from scribe.services import moment_delivery
|
||||
from grimoire.services.notes import minted_kind
|
||||
from grimoire.services import placement as placement_svc
|
||||
from grimoire.services import planning as planning_svc
|
||||
from grimoire.services import record_batch as batch_svc
|
||||
from grimoire.services import rulebooks as rulebooks_svc
|
||||
from grimoire.services import systems as systems_svc
|
||||
from grimoire.services import task_logs as task_logs_svc
|
||||
from grimoire.services import trash as trash_svc
|
||||
from grimoire.services.note_usage import record_pulled
|
||||
from grimoire.services.record_refs import refuse_guessed_ids
|
||||
from grimoire.services.text import elide
|
||||
from grimoire.services import moment_delivery
|
||||
|
||||
|
||||
# A work log entry is prose, often long — the discipline asks for what was
|
||||
@@ -6,8 +6,8 @@ the deleted_batch_id that a delete operation returns.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from scribe.mcp._context import current_user_id
|
||||
from scribe.services import trash as trash_svc
|
||||
from grimoire.mcp._context import current_user_id
|
||||
from grimoire.services import trash as trash_svc
|
||||
|
||||
|
||||
async def list_trash() -> dict:
|
||||
@@ -35,9 +35,9 @@ from __future__ import annotations
|
||||
import textwrap
|
||||
import time
|
||||
|
||||
from scribe.mcp._context import current_user_id
|
||||
from scribe.services.embeddings import semantic_search_rules
|
||||
from scribe.services.retrieval_telemetry import record_retrieval
|
||||
from grimoire.mcp._context import current_user_id
|
||||
from grimoire.services.embeddings import semantic_search_rules
|
||||
from grimoire.services.retrieval_telemetry import record_retrieval
|
||||
|
||||
# The telemetry `source`, and a PULL — never added to AMBIENT_SOURCES (those
|
||||
# are deliveries nobody chose) and never confused with a push arm. Keeping it
|
||||
@@ -1,7 +1,7 @@
|
||||
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
|
||||
from sqlalchemy.orm import DeclarativeBase
|
||||
|
||||
from scribe.config import Config
|
||||
from grimoire.config import Config
|
||||
|
||||
# Named rather than inlined so the deadline below is READABLE. SQLAlchemy
|
||||
# captures `connect_args` in a closure and merges it at connect time, so an
|
||||
@@ -44,53 +44,53 @@ class Base(DeclarativeBase):
|
||||
pass
|
||||
|
||||
|
||||
from scribe.models.base import CreatedAtMixin, TimestampMixin # noqa: E402, F401
|
||||
from grimoire.models.base import CreatedAtMixin, TimestampMixin # noqa: E402, F401
|
||||
|
||||
|
||||
from scribe.models.note import Note, TaskPriority, TaskStatus # noqa: E402, F401
|
||||
from scribe.models.setting import Setting # noqa: E402, F401
|
||||
from scribe.models.user import User # noqa: E402, F401
|
||||
from scribe.models.app_log import AppLog # noqa: E402, F401
|
||||
from scribe.models.password_reset import PasswordResetToken # noqa: E402, F401
|
||||
from scribe.models.invitation import InvitationToken # noqa: E402, F401
|
||||
from scribe.models.embedding import ( # noqa: E402, F401
|
||||
from grimoire.models.note import Note, TaskPriority, TaskStatus # noqa: E402, F401
|
||||
from grimoire.models.setting import Setting # noqa: E402, F401
|
||||
from grimoire.models.user import User # noqa: E402, F401
|
||||
from grimoire.models.app_log import AppLog # noqa: E402, F401
|
||||
from grimoire.models.password_reset import PasswordResetToken # noqa: E402, F401
|
||||
from grimoire.models.invitation import InvitationToken # noqa: E402, F401
|
||||
from grimoire.models.embedding import ( # noqa: E402, F401
|
||||
MilestoneEmbedding, NoteEmbedding, RuleEmbedding, SystemEmbedding,
|
||||
)
|
||||
from scribe.models.retrieval_log import RetrievalLog # noqa: E402, F401
|
||||
from scribe.models.retrieval_tuning import RetrievalTuningEvent # noqa: E402, F401
|
||||
from scribe.models.note_usage import NoteUsageEvent # noqa: E402, F401
|
||||
from scribe.models.rule_usage import RuleUsageEvent # noqa: E402, F401
|
||||
from scribe.models.system_usage import SystemUsageEvent # noqa: E402, F401
|
||||
from scribe.models.retrieval_judgment import RetrievalJudgment # noqa: E402, F401
|
||||
from scribe.models.moment_mapping import MomentMapping # noqa: E402, F401
|
||||
from scribe.models.project import Project # noqa: E402, F401
|
||||
from scribe.models.milestone import Milestone # noqa: E402, F401
|
||||
from scribe.models.task_log import TaskLog # noqa: E402, F401
|
||||
from scribe.models.note_draft import NoteDraft # noqa: E402, F401
|
||||
from scribe.models.note_version import NoteVersion # noqa: E402, F401
|
||||
from scribe.models.rule_version import RuleVersion # noqa: E402, F401
|
||||
from scribe.models.note_supersession import NoteSupersession # noqa: E402, F401
|
||||
from scribe.models.group import Group, GroupMembership # noqa: E402, F401
|
||||
from scribe.models.share import NoteShare, ProjectShare # noqa: E402, F401
|
||||
from scribe.models.notification import Notification # noqa: E402, F401
|
||||
from scribe.models.api_key import ApiKey # noqa: E402, F401
|
||||
from grimoire.models.retrieval_log import RetrievalLog # noqa: E402, F401
|
||||
from grimoire.models.retrieval_tuning import RetrievalTuningEvent # noqa: E402, F401
|
||||
from grimoire.models.note_usage import NoteUsageEvent # noqa: E402, F401
|
||||
from grimoire.models.rule_usage import RuleUsageEvent # noqa: E402, F401
|
||||
from grimoire.models.system_usage import SystemUsageEvent # noqa: E402, F401
|
||||
from grimoire.models.retrieval_judgment import RetrievalJudgment # noqa: E402, F401
|
||||
from grimoire.models.moment_mapping import MomentMapping # noqa: E402, F401
|
||||
from grimoire.models.project import Project # noqa: E402, F401
|
||||
from grimoire.models.milestone import Milestone # noqa: E402, F401
|
||||
from grimoire.models.task_log import TaskLog # noqa: E402, F401
|
||||
from grimoire.models.note_draft import NoteDraft # noqa: E402, F401
|
||||
from grimoire.models.note_version import NoteVersion # noqa: E402, F401
|
||||
from grimoire.models.rule_version import RuleVersion # noqa: E402, F401
|
||||
from grimoire.models.note_supersession import NoteSupersession # noqa: E402, F401
|
||||
from grimoire.models.group import Group, GroupMembership # noqa: E402, F401
|
||||
from grimoire.models.share import NoteShare, ProjectShare # noqa: E402, F401
|
||||
from grimoire.models.notification import Notification # noqa: E402, F401
|
||||
from grimoire.models.api_key import ApiKey # noqa: E402, F401
|
||||
# Imported before rulebook: rule_systems foreign-keys canonical_systems.
|
||||
from scribe.models.canonical_system import CanonicalSystem # noqa: E402, F401
|
||||
from scribe.models.rulebook import ( # noqa: E402, F401
|
||||
from grimoire.models.canonical_system import CanonicalSystem # noqa: E402, F401
|
||||
from grimoire.models.rulebook import ( # noqa: E402, F401
|
||||
Rulebook, RulebookTopic, Rule, RuleRelation, rule_moments, rule_systems,
|
||||
)
|
||||
# After notes and rules: it foreign-keys both.
|
||||
from scribe.models.lesson_rule_link import LessonNoRule, LessonRuleLink # noqa: E402, F401
|
||||
from grimoire.models.lesson_rule_link import LessonNoRule, LessonRuleLink # noqa: E402, F401
|
||||
# After rules: it foreign-keys them.
|
||||
from scribe.models.rule_moment_judgment import RuleMomentJudgment # noqa: E402, F401
|
||||
from scribe.models.repo_binding import RepoBinding # noqa: E402, F401
|
||||
from scribe.models.forge_connection import ForgeConnection # noqa: E402, F401
|
||||
from scribe.models.code_shape import CodeShape, CodeShapeConsumer, CodeShapeEvent, CodeShapeUse # noqa: E402, F401
|
||||
from scribe.models.system import System, RecordSystem # noqa: E402, F401
|
||||
from scribe.models.design_system import DesignSystem, DesignToken # noqa: E402, F401
|
||||
from grimoire.models.rule_moment_judgment import RuleMomentJudgment # noqa: E402, F401
|
||||
from grimoire.models.repo_binding import RepoBinding # noqa: E402, F401
|
||||
from grimoire.models.forge_connection import ForgeConnection # noqa: E402, F401
|
||||
from grimoire.models.code_shape import CodeShape, CodeShapeConsumer, CodeShapeEvent, CodeShapeUse # noqa: E402, F401
|
||||
from grimoire.models.system import System, RecordSystem # noqa: E402, F401
|
||||
from grimoire.models.design_system import DesignSystem, DesignToken # noqa: E402, F401
|
||||
# After notes, projects and rulebook topics: family canon foreign-keys all
|
||||
# three.
|
||||
from scribe.models.family import ( # noqa: E402, F401
|
||||
from grimoire.models.family import ( # noqa: E402, F401
|
||||
FamilyAdoption, FamilyDecision, FamilyIdea, FamilyIdeaPlatform,
|
||||
FamilyIdeaReference, Platform, ProjectPlatform,
|
||||
)
|
||||
@@ -3,8 +3,8 @@ from datetime import datetime
|
||||
from sqlalchemy import DateTime, ForeignKey, Index, Integer, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import CreatedAtMixin, iso
|
||||
from grimoire.models import Base
|
||||
from grimoire.models.base import CreatedAtMixin, iso
|
||||
|
||||
|
||||
class ApiKey(Base, CreatedAtMixin):
|
||||
@@ -3,8 +3,8 @@ from datetime import datetime, timezone
|
||||
from sqlalchemy import DateTime, Float, Index, Integer, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import iso
|
||||
from grimoire.models import Base
|
||||
from grimoire.models.base import iso
|
||||
|
||||
|
||||
class AppLog(Base):
|
||||
@@ -1,8 +1,8 @@
|
||||
from sqlalchemy import Index, Integer, Text, text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import SoftDeleteMixin, TimestampMixin, iso
|
||||
from grimoire.models import Base
|
||||
from grimoire.models.base import SoftDeleteMixin, TimestampMixin, iso
|
||||
|
||||
|
||||
class CanonicalSystem(Base, TimestampMixin, SoftDeleteMixin):
|
||||
@@ -12,8 +12,8 @@ from sqlalchemy import (
|
||||
)
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import TimestampMixin, iso
|
||||
from grimoire.models import Base
|
||||
from grimoire.models.base import TimestampMixin, iso
|
||||
|
||||
# The classification vocabulary. `unclassified` is the default and
|
||||
# THE todo state; every other status is a judgment, stamped with who made it —
|
||||
@@ -19,8 +19,8 @@ from sqlalchemy import BigInteger, ForeignKey, Index, Integer, Text, text
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import SoftDeleteMixin, TimestampMixin, iso
|
||||
from grimoire.models import Base
|
||||
from grimoire.models.base import SoftDeleteMixin, TimestampMixin, iso
|
||||
|
||||
|
||||
class DesignSystem(Base, TimestampMixin, SoftDeleteMixin):
|
||||
@@ -4,7 +4,7 @@ from pgvector.sqlalchemy import Vector
|
||||
from sqlalchemy import BigInteger, DateTime, ForeignKey, Integer, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from scribe.models import Base
|
||||
from grimoire.models import Base
|
||||
|
||||
# bge-small-en-v1.5 produces 384-dim unit-normalized vectors. The column is a
|
||||
# native pgvector `vector(384)` (see migration 0067) so similarity is ranked in
|
||||
@@ -41,8 +41,8 @@ from sqlalchemy import (
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import CreatedAtMixin, SoftDeleteMixin, TimestampMixin, iso
|
||||
from grimoire.models import Base
|
||||
from grimoire.models.base import CreatedAtMixin, SoftDeleteMixin, TimestampMixin, iso
|
||||
|
||||
# Each tuple is the whitelist its CHECK constraint enforces (migration 0120).
|
||||
# A new value means DROP + ADD CONSTRAINT in the same migration.
|
||||
@@ -1,8 +1,8 @@
|
||||
from sqlalchemy import ForeignKey, Integer, Text, UniqueConstraint
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import TimestampMixin, iso
|
||||
from grimoire.models import Base
|
||||
from grimoire.models.base import TimestampMixin, iso
|
||||
|
||||
|
||||
class ForgeConnection(Base, TimestampMixin):
|
||||
@@ -3,8 +3,8 @@ from datetime import datetime, timezone
|
||||
from sqlalchemy import ForeignKey, Integer, Text, UniqueConstraint
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import CreatedAtMixin, TimestampMixin, iso
|
||||
from grimoire.models import Base
|
||||
from grimoire.models.base import CreatedAtMixin, TimestampMixin, iso
|
||||
|
||||
|
||||
class Group(Base, TimestampMixin):
|
||||
@@ -3,8 +3,8 @@ from datetime import datetime, timezone
|
||||
from sqlalchemy import Boolean, DateTime, ForeignKey, Index, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import CreatedAtMixin
|
||||
from grimoire.models import Base
|
||||
from grimoire.models.base import CreatedAtMixin
|
||||
|
||||
|
||||
class InvitationToken(Base, CreatedAtMixin):
|
||||
@@ -4,8 +4,8 @@ from sqlalchemy import BigInteger, DateTime, ForeignKey, Integer, Text, UniqueCo
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import CreatedAtMixin, iso
|
||||
from grimoire.models import Base
|
||||
from grimoire.models.base import CreatedAtMixin, iso
|
||||
|
||||
SUGGESTED = "suggested"
|
||||
CONFIRMED = "confirmed"
|
||||
@@ -1,8 +1,8 @@
|
||||
from sqlalchemy import ForeignKey, Integer, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import SoftDeleteMixin, TimestampMixin, iso
|
||||
from grimoire.models import Base
|
||||
from grimoire.models.base import SoftDeleteMixin, TimestampMixin, iso
|
||||
|
||||
|
||||
class Milestone(Base, TimestampMixin, SoftDeleteMixin):
|
||||
@@ -1,8 +1,8 @@
|
||||
from sqlalchemy import BigInteger, ForeignKey, Integer, Text, UniqueConstraint
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import CreatedAtMixin, iso
|
||||
from grimoire.models import Base
|
||||
from grimoire.models.base import CreatedAtMixin, iso
|
||||
|
||||
|
||||
class MomentMapping(Base, CreatedAtMixin):
|
||||
@@ -5,8 +5,8 @@ from sqlalchemy import Date, DateTime, ForeignKey, Index, Integer, Text
|
||||
from sqlalchemy.dialects.postgresql import ARRAY, JSONB
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import SoftDeleteMixin, TimestampMixin, iso
|
||||
from grimoire.models import Base
|
||||
from grimoire.models.base import SoftDeleteMixin, TimestampMixin, iso
|
||||
|
||||
|
||||
class TaskStatus(str, enum.Enum):
|
||||
@@ -212,6 +212,6 @@ class Note(Base, TimestampMixin, SoftDeleteMixin):
|
||||
|
||||
|
||||
def _claim_state(note: "Note") -> dict | None:
|
||||
from scribe.services.task_claims import claim_state
|
||||
from grimoire.services.task_claims import claim_state
|
||||
|
||||
return claim_state(note)
|
||||
@@ -1,8 +1,8 @@
|
||||
from sqlalchemy import ForeignKey, Integer, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import TimestampMixin, iso
|
||||
from grimoire.models import Base
|
||||
from grimoire.models.base import TimestampMixin, iso
|
||||
|
||||
|
||||
class NoteDraft(Base, TimestampMixin):
|
||||
@@ -1,8 +1,8 @@
|
||||
from sqlalchemy import ForeignKey, Index, Integer, UniqueConstraint
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import CreatedAtMixin, iso
|
||||
from grimoire.models import Base
|
||||
from grimoire.models.base import CreatedAtMixin, iso
|
||||
|
||||
|
||||
class NoteSupersession(Base, CreatedAtMixin):
|
||||
@@ -1,8 +1,8 @@
|
||||
from sqlalchemy import Index, Integer, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import CreatedAtMixin, iso
|
||||
from grimoire.models import Base
|
||||
from grimoire.models.base import CreatedAtMixin, iso
|
||||
|
||||
SURFACED = "surfaced"
|
||||
PULLED = "pulled"
|
||||
@@ -1,8 +1,8 @@
|
||||
from sqlalchemy import ARRAY, ForeignKey, Integer, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import CreatedAtMixin, iso
|
||||
from grimoire.models import Base
|
||||
from grimoire.models.base import CreatedAtMixin, iso
|
||||
|
||||
|
||||
class NoteVersion(Base, CreatedAtMixin):
|
||||
@@ -4,8 +4,8 @@ from sqlalchemy import DateTime, ForeignKey, Integer, Text
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import CreatedAtMixin, iso
|
||||
from grimoire.models import Base
|
||||
from grimoire.models.base import CreatedAtMixin, iso
|
||||
|
||||
|
||||
class Notification(Base, CreatedAtMixin):
|
||||
@@ -3,8 +3,8 @@ from datetime import datetime, timezone
|
||||
from sqlalchemy import Boolean, DateTime, ForeignKey, Index, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import CreatedAtMixin
|
||||
from grimoire.models import Base
|
||||
from grimoire.models.base import CreatedAtMixin
|
||||
|
||||
|
||||
class PasswordResetToken(Base, CreatedAtMixin):
|
||||
@@ -2,8 +2,8 @@ import enum
|
||||
from sqlalchemy import BigInteger, ForeignKey, Integer, Text
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import SoftDeleteMixin, TimestampMixin, iso
|
||||
from grimoire.models import Base
|
||||
from grimoire.models.base import SoftDeleteMixin, TimestampMixin, iso
|
||||
|
||||
|
||||
class ProjectStatus(str, enum.Enum):
|
||||
@@ -1,8 +1,8 @@
|
||||
from sqlalchemy import ForeignKey, Integer, Text, UniqueConstraint
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import TimestampMixin, iso
|
||||
from grimoire.models import Base
|
||||
from grimoire.models.base import TimestampMixin, iso
|
||||
|
||||
|
||||
class RepoBinding(Base, TimestampMixin):
|
||||
@@ -1,8 +1,8 @@
|
||||
from sqlalchemy import BigInteger, Boolean, Float, Index, Integer, Text, UniqueConstraint
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import CreatedAtMixin, iso
|
||||
from grimoire.models import Base
|
||||
from grimoire.models.base import CreatedAtMixin, iso
|
||||
|
||||
ON_POINT = "on_point"
|
||||
ADJACENT = "adjacent"
|
||||
@@ -4,8 +4,8 @@ from sqlalchemy import Boolean, DateTime, Float, Index, Integer, Text
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import iso
|
||||
from grimoire.models import Base
|
||||
from grimoire.models.base import iso
|
||||
|
||||
|
||||
class RetrievalLog(Base):
|
||||
@@ -3,7 +3,7 @@ from datetime import datetime, timezone
|
||||
from sqlalchemy import DateTime, Float, Index, Integer, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from scribe.models import Base
|
||||
from grimoire.models import Base
|
||||
|
||||
|
||||
class RetrievalTuningEvent(Base):
|
||||
+3
-3
@@ -4,9 +4,9 @@ from sqlalchemy import BigInteger, DateTime, ForeignKey, Text, UniqueConstraint
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import CreatedAtMixin, iso
|
||||
from scribe.models.lesson_rule_link import CONFIRMED, LINK_STATES, REJECTED, SUGGESTED
|
||||
from grimoire.models import Base
|
||||
from grimoire.models.base import CreatedAtMixin, iso
|
||||
from grimoire.models.lesson_rule_link import CONFIRMED, LINK_STATES, REJECTED, SUGGESTED
|
||||
|
||||
# The same three states as a lesson's link to a rule, for the same reasons —
|
||||
# re-exported so a reader of this table need not know where they were first
|
||||
@@ -1,8 +1,8 @@
|
||||
from sqlalchemy import BigInteger, Index, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import CreatedAtMixin, iso
|
||||
from grimoire.models import Base
|
||||
from grimoire.models.base import CreatedAtMixin, iso
|
||||
|
||||
SURFACED = "surfaced"
|
||||
PULLED = "pulled"
|
||||
@@ -3,8 +3,8 @@ from datetime import datetime
|
||||
from sqlalchemy import BigInteger, ForeignKey, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import CreatedAtMixin, iso
|
||||
from grimoire.models import Base
|
||||
from grimoire.models.base import CreatedAtMixin, iso
|
||||
|
||||
|
||||
class RuleVersion(Base, CreatedAtMixin):
|
||||
@@ -6,8 +6,8 @@ from sqlalchemy import (
|
||||
)
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import CreatedAtMixin, SoftDeleteMixin, TimestampMixin, iso
|
||||
from grimoire.models import Base
|
||||
from grimoire.models.base import CreatedAtMixin, SoftDeleteMixin, TimestampMixin, iso
|
||||
|
||||
|
||||
class Rulebook(Base, TimestampMixin, SoftDeleteMixin):
|
||||
@@ -1,7 +1,7 @@
|
||||
from sqlalchemy import ForeignKey, Integer, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from scribe.models import Base
|
||||
from grimoire.models import Base
|
||||
|
||||
|
||||
class Setting(Base):
|
||||
@@ -1,8 +1,8 @@
|
||||
from sqlalchemy import CheckConstraint, ForeignKey, Integer, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import TimestampMixin, iso
|
||||
from grimoire.models import Base
|
||||
from grimoire.models.base import TimestampMixin, iso
|
||||
|
||||
|
||||
class ProjectShare(Base, TimestampMixin):
|
||||
@@ -2,8 +2,8 @@ from sqlalchemy import ForeignKey, Index, Integer, Text, UniqueConstraint, text
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import CreatedAtMixin, SoftDeleteMixin, TimestampMixin, iso
|
||||
from grimoire.models import Base
|
||||
from grimoire.models.base import CreatedAtMixin, SoftDeleteMixin, TimestampMixin, iso
|
||||
|
||||
|
||||
class System(Base, TimestampMixin, SoftDeleteMixin):
|
||||
@@ -1,8 +1,8 @@
|
||||
from sqlalchemy import BigInteger, Index, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import CreatedAtMixin, iso
|
||||
from grimoire.models import Base
|
||||
from grimoire.models.base import CreatedAtMixin, iso
|
||||
|
||||
SURFACED = "surfaced"
|
||||
PULLED = "pulled"
|
||||
@@ -1,8 +1,8 @@
|
||||
from sqlalchemy import ForeignKey, Integer, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import TimestampMixin, iso
|
||||
from grimoire.models import Base
|
||||
from grimoire.models.base import TimestampMixin, iso
|
||||
|
||||
|
||||
class TaskLog(Base, TimestampMixin):
|
||||
@@ -1,8 +1,8 @@
|
||||
from sqlalchemy import Index, Integer, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import CreatedAtMixin, iso
|
||||
from grimoire.models import Base
|
||||
from grimoire.models.base import CreatedAtMixin, iso
|
||||
|
||||
|
||||
class User(Base, CreatedAtMixin):
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Restore a Grimoire backup into an empty instance, from the command line.
|
||||
|
||||
python -m scribe.restore /backups/scribe-20261015T020000Z.tar
|
||||
python -m grimoire.restore /backups/scribe-20261015T020000Z.tar
|
||||
|
||||
This is how a fresh instance is brought back after losing the old one. Run it
|
||||
inside the Grimoire container, which already has the database settings and has
|
||||
@@ -32,7 +32,7 @@ from sqlalchemy import func, select
|
||||
|
||||
def load_backup(path: Path) -> dict:
|
||||
"""The payload in `path`, whichever of the two backup formats it is."""
|
||||
from scribe.services.backup_file import is_archive, read_archive
|
||||
from grimoire.services.backup_file import is_archive, read_archive
|
||||
|
||||
with open(path, "rb") as f:
|
||||
if is_archive(f):
|
||||
@@ -41,17 +41,17 @@ def load_backup(path: Path) -> dict:
|
||||
|
||||
|
||||
async def _account_count() -> int:
|
||||
from scribe.models import async_session
|
||||
from scribe.models.user import User
|
||||
from grimoire.models import async_session
|
||||
from grimoire.models.user import User
|
||||
|
||||
async with async_session() as session:
|
||||
return (await session.execute(select(func.count()).select_from(User))).scalar_one()
|
||||
|
||||
|
||||
async def run(path: Path) -> int:
|
||||
from scribe.models import engine
|
||||
from scribe.services.backup import restore_full_backup
|
||||
from scribe.services.backup_file import ArchiveError
|
||||
from grimoire.models import engine
|
||||
from grimoire.services.backup import restore_full_backup
|
||||
from grimoire.services.backup_file import ArchiveError
|
||||
|
||||
try:
|
||||
try:
|
||||
@@ -83,7 +83,7 @@ async def run(path: Path) -> int:
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="python -m scribe.restore",
|
||||
prog="python -m grimoire.restore",
|
||||
description="Restore a Grimoire backup (.tar or .json) into an empty instance.",
|
||||
)
|
||||
parser.add_argument("file", type=Path, help="the backup file")
|
||||
@@ -3,8 +3,8 @@ import json
|
||||
|
||||
from quart import Blueprint, Response, g, jsonify, request
|
||||
|
||||
from scribe.auth import admin_required, login_required, get_current_user_id
|
||||
from scribe.services.auth import (
|
||||
from grimoire.auth import admin_required, login_required, get_current_user_id
|
||||
from grimoire.services.auth import (
|
||||
create_invitation,
|
||||
delete_user,
|
||||
is_registration_open,
|
||||
@@ -13,15 +13,15 @@ from scribe.services.auth import (
|
||||
revoke_invitation,
|
||||
set_registration_open,
|
||||
)
|
||||
from scribe.services.backup import (
|
||||
from grimoire.services.backup import (
|
||||
export_full_backup,
|
||||
export_user_backup,
|
||||
restore_full_backup,
|
||||
)
|
||||
from scribe.services.email import SMTP_SETTING_KEYS, get_base_url, get_smtp_config, is_smtp_configured, send_test_email
|
||||
from scribe.services.logging import get_logs, get_log_stats, log_audit
|
||||
from scribe.services.notifications import send_invitation_email
|
||||
from scribe.services.settings import (
|
||||
from grimoire.services.email import SMTP_SETTING_KEYS, get_base_url, get_smtp_config, is_smtp_configured, send_test_email
|
||||
from grimoire.services.logging import get_logs, get_log_stats, log_audit
|
||||
from grimoire.services.notifications import send_invitation_email
|
||||
from grimoire.services.settings import (
|
||||
SECRET_MASK,
|
||||
get_admin_setting,
|
||||
set_admin_setting,
|
||||
@@ -61,14 +61,14 @@ async def backup():
|
||||
async def restore():
|
||||
"""Restore from an uploaded backup: the JSON download (application/json)
|
||||
or the scheduled backup's tar (sent as the raw body). The upload is held
|
||||
to the app's request-size limit; `python -m scribe.restore` has none, and
|
||||
to the app's request-size limit; `python -m grimoire.restore` has none, and
|
||||
is the way in for a bigger file or a fresh instance."""
|
||||
if request.mimetype == "application/json":
|
||||
data = await request.get_json()
|
||||
else:
|
||||
import io
|
||||
|
||||
from scribe.services.backup_file import ArchiveError, read_archive
|
||||
from grimoire.services.backup_file import ArchiveError, read_archive
|
||||
|
||||
try:
|
||||
data = read_archive(io.BytesIO(await request.get_data()))
|
||||
@@ -180,8 +180,8 @@ async def test_smtp():
|
||||
@admin_bp.route("/forge-webhook", methods=["GET"])
|
||||
@admin_required
|
||||
async def get_forge_webhook_settings():
|
||||
from scribe.config import Config
|
||||
from scribe.routes.webhooks import FORGE_WEBHOOK_SECRET_KEY
|
||||
from grimoire.config import Config
|
||||
from grimoire.routes.webhooks import FORGE_WEBHOOK_SECRET_KEY
|
||||
|
||||
webhook_secret = (
|
||||
await get_admin_setting(FORGE_WEBHOOK_SECRET_KEY, "")
|
||||
@@ -197,7 +197,7 @@ async def get_forge_webhook_settings():
|
||||
@admin_bp.route("/forge-webhook", methods=["PUT"])
|
||||
@admin_required
|
||||
async def update_forge_webhook_settings():
|
||||
from scribe.routes.webhooks import FORGE_WEBHOOK_SECRET_KEY
|
||||
from grimoire.routes.webhooks import FORGE_WEBHOOK_SECRET_KEY
|
||||
|
||||
data = await request.get_json() or {}
|
||||
uid = get_current_user_id()
|
||||
@@ -270,8 +270,8 @@ async def update_base_url():
|
||||
@admin_required
|
||||
async def get_db_maintenance():
|
||||
"""Current DB-maintenance config + the last run's summary."""
|
||||
from scribe.services.db_maintenance import get_last_run
|
||||
from scribe.services.db_maintenance_scheduler import (
|
||||
from grimoire.services.db_maintenance import get_last_run
|
||||
from grimoire.services.db_maintenance_scheduler import (
|
||||
get_maintenance_hour,
|
||||
is_maintenance_enabled,
|
||||
)
|
||||
@@ -286,7 +286,7 @@ async def get_db_maintenance():
|
||||
@admin_required
|
||||
async def get_db_maintenance_health():
|
||||
"""Read-only per-table bloat/health stats from Postgres + total DB size."""
|
||||
from scribe.services.db_maintenance import get_table_health
|
||||
from grimoire.services.db_maintenance import get_table_health
|
||||
return jsonify(await get_table_health())
|
||||
|
||||
|
||||
@@ -294,7 +294,7 @@ async def get_db_maintenance_health():
|
||||
@admin_required
|
||||
async def update_db_maintenance():
|
||||
"""Set whether scheduled maintenance runs and at what UTC hour."""
|
||||
from scribe.services.db_maintenance_scheduler import reschedule_db_maintenance
|
||||
from grimoire.services.db_maintenance_scheduler import reschedule_db_maintenance
|
||||
data = await request.get_json() or {}
|
||||
enabled = bool(data.get("enabled", True))
|
||||
try:
|
||||
@@ -319,7 +319,7 @@ async def update_db_maintenance():
|
||||
@admin_required
|
||||
async def run_db_maintenance_now():
|
||||
"""Run a VACUUM (ANALYZE) sweep immediately and return its summary."""
|
||||
from scribe.services.db_maintenance import run_maintenance
|
||||
from grimoire.services.db_maintenance import run_maintenance
|
||||
uid = get_current_user_id()
|
||||
await log_audit(
|
||||
"db_maintenance_run", user_id=uid, username=g.user.username,
|
||||
@@ -342,8 +342,8 @@ _BACKUP_CHECK_TIMEOUT = 45.0
|
||||
async def _backups_payload() -> dict:
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from scribe.services import backup_destination as dest
|
||||
from scribe.services import backup_job as job
|
||||
from grimoire.services import backup_destination as dest
|
||||
from grimoire.services import backup_job as job
|
||||
|
||||
config = await dest.load_config()
|
||||
secret, stored = await dest.load_secret()
|
||||
@@ -373,7 +373,7 @@ async def get_backups():
|
||||
async def update_backups():
|
||||
"""Change the destination or schedule. `s3_secret` replaces the stored
|
||||
secret when it is sent and isn't the mask; `clear_s3_secret` removes it."""
|
||||
from scribe.services import backup_destination as dest
|
||||
from grimoire.services import backup_destination as dest
|
||||
|
||||
data = await request.get_json() or {}
|
||||
try:
|
||||
@@ -403,7 +403,7 @@ async def check_backups_destination():
|
||||
"""Ask the destination whether it would take a backup, without sending
|
||||
one. The body may carry unsaved form values, so a destination can be
|
||||
tested before it is saved; the stored secret is used unless one is sent."""
|
||||
from scribe.services import backup_destination as dest
|
||||
from grimoire.services import backup_destination as dest
|
||||
|
||||
data = await request.get_json() or {}
|
||||
try:
|
||||
@@ -431,7 +431,7 @@ async def check_backups_destination():
|
||||
async def run_backup_now():
|
||||
"""Start a backup now. It runs in the background; the page follows it
|
||||
through GET /backups (`running`, then the newest run)."""
|
||||
from scribe.services import backup_job as job
|
||||
from grimoire.services import backup_job as job
|
||||
|
||||
try:
|
||||
job.start_backup_now()
|
||||
@@ -1,7 +1,7 @@
|
||||
from quart import Blueprint, jsonify, request
|
||||
|
||||
from scribe.auth import login_required, get_current_user_id
|
||||
from scribe.services.api_keys import create_api_key, list_api_keys, revoke_api_key
|
||||
from grimoire.auth import login_required, get_current_user_id
|
||||
from grimoire.services.api_keys import create_api_key, list_api_keys, revoke_api_key
|
||||
|
||||
api_keys_bp = Blueprint("api_keys", __name__, url_prefix="/api/api-keys")
|
||||
|
||||
@@ -6,10 +6,10 @@ import secrets
|
||||
|
||||
from quart import Blueprint, g, jsonify, redirect, request, session
|
||||
|
||||
from scribe.auth import login_required, get_current_user_id
|
||||
from scribe.config import Config
|
||||
from scribe.rate_limit import is_rate_limited
|
||||
from scribe.services.auth import (
|
||||
from grimoire.auth import login_required, get_current_user_id
|
||||
from grimoire.config import Config
|
||||
from grimoire.rate_limit import is_rate_limited
|
||||
from grimoire.services.auth import (
|
||||
authenticate,
|
||||
change_password,
|
||||
create_password_reset_token,
|
||||
@@ -26,14 +26,14 @@ from scribe.services.auth import (
|
||||
validate_invitation_token,
|
||||
verify_password,
|
||||
)
|
||||
from scribe.services.logging import log_audit
|
||||
from scribe.services.notifications import (
|
||||
from grimoire.services.logging import log_audit
|
||||
from grimoire.services.notifications import (
|
||||
notify_security_event,
|
||||
send_password_reset_email,
|
||||
send_password_reset_success_email,
|
||||
)
|
||||
from scribe.services.email import get_base_url, is_smtp_configured
|
||||
from scribe.services.oauth import (
|
||||
from grimoire.services.email import get_base_url, is_smtp_configured
|
||||
from grimoire.services.oauth import (
|
||||
build_auth_url,
|
||||
exchange_code,
|
||||
get_userinfo,
|
||||
@@ -13,10 +13,10 @@ import logging
|
||||
|
||||
from quart import Blueprint, jsonify, request
|
||||
|
||||
from scribe.auth import admin_required, get_current_user_id, login_required
|
||||
from scribe.routes.utils import not_found
|
||||
from scribe.services import canonical_systems as canonical_svc
|
||||
from scribe.services.projects import get_project_for_user
|
||||
from grimoire.auth import admin_required, get_current_user_id, login_required
|
||||
from grimoire.routes.utils import not_found
|
||||
from grimoire.services import canonical_systems as canonical_svc
|
||||
from grimoire.services.projects import get_project_for_user
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"""Dashboard REST endpoint — the aggregated landing payload."""
|
||||
from quart import Blueprint, g, jsonify
|
||||
|
||||
from scribe.auth import login_required
|
||||
from scribe.services.dashboard import build_dashboard
|
||||
from grimoire.auth import login_required
|
||||
from grimoire.services.dashboard import build_dashboard
|
||||
|
||||
dashboard_bp = Blueprint("dashboard", __name__, url_prefix="/api/dashboard")
|
||||
|
||||
@@ -17,14 +17,14 @@ one and returns None for the other:
|
||||
"""
|
||||
from quart import Blueprint, jsonify, request
|
||||
|
||||
from scribe.auth import get_current_user_id, login_required
|
||||
from scribe.routes.utils import not_found
|
||||
from scribe.services import design_systems as ds_svc
|
||||
from scribe.services.design_starter_roles import (
|
||||
from grimoire.auth import get_current_user_id, login_required
|
||||
from grimoire.routes.utils import not_found
|
||||
from grimoire.services import design_systems as ds_svc
|
||||
from grimoire.services.design_starter_roles import (
|
||||
DEFAULT_TOKEN_PREFIX,
|
||||
describe_groups,
|
||||
)
|
||||
from scribe.services.design_systems import DesignSystemCycle
|
||||
from grimoire.services.design_systems import DesignSystemCycle
|
||||
|
||||
design_systems_bp = Blueprint("design_systems", __name__, url_prefix="/api")
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user