@moor-sh/mcp 0.16.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/index.ts +86 -0
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -782,6 +782,92 @@ server.registerTool(
|
|
|
782
782
|
},
|
|
783
783
|
);
|
|
784
784
|
|
|
785
|
+
// #90: operator-initiated DB snapshot. Uses VACUUM INTO on the server so
|
|
786
|
+
// hot WAL state is captured safely (cp would copy a corrupt-looking file).
|
|
787
|
+
// Backups land next to moor.db; retention prunes to the N most recent.
|
|
788
|
+
// Pair with MOOR_DB_BACKUP_INTERVAL_HOURS for scheduled snapshots — this
|
|
789
|
+
// tool is for taking one right before a manual update.
|
|
790
|
+
server.registerTool(
|
|
791
|
+
"moor_db_backup",
|
|
792
|
+
{
|
|
793
|
+
title: "DB Backup (snapshot)",
|
|
794
|
+
description:
|
|
795
|
+
"Take a SQLite snapshot of moor.db via VACUUM INTO. The file lands next to the main DB as moor.db.backup-<epoch-ms>. Retention is enforced after each snapshot (keeps the 7 most recent by default; older ones are pruned). After this returns, moor_update_status' db_backup.age_seconds will read close to 0. Use before a manual `docker compose pull moor && up -d` if you don't have MOOR_DB_BACKUP_INTERVAL_HOURS scheduled.",
|
|
796
|
+
},
|
|
797
|
+
async () => {
|
|
798
|
+
const res = await apiPost("/api/server/backup", {});
|
|
799
|
+
if (!res.ok) throw new Error(`db backup failed: ${res.status} ${await res.text()}`);
|
|
800
|
+
const r = (await res.json()) as { path: string; sizeBytes: number; durationMs: number };
|
|
801
|
+
const mb = (r.sizeBytes / (1024 * 1024)).toFixed(2);
|
|
802
|
+
return {
|
|
803
|
+
content: [
|
|
804
|
+
{
|
|
805
|
+
type: "text",
|
|
806
|
+
text: `Snapshot written: ${r.path}\nsize: ${r.sizeBytes}B (${mb} MB)\nduration: ${r.durationMs}ms`,
|
|
807
|
+
},
|
|
808
|
+
],
|
|
809
|
+
};
|
|
810
|
+
},
|
|
811
|
+
);
|
|
812
|
+
|
|
813
|
+
// #80 PR #4: moor_update_apply — kick off a transient-respawner update of
|
|
814
|
+
// moor itself. The respawner runs async; this tool returns the audit_id
|
|
815
|
+
// immediately. Poll moor_update_status (or, eventually, moor_update_audit)
|
|
816
|
+
// for the outcome.
|
|
817
|
+
//
|
|
818
|
+
// IMPORTANT: PR #4 has no rollback. A failed compose-up / --wait timeout /
|
|
819
|
+
// health check writes a `failed` marker and exits; compose state is left
|
|
820
|
+
// as Compose left it. Recovery: manual `docker compose up`, OR wait for the
|
|
821
|
+
// 30-min stale-in_progress sweep to reclaim the audit row. Automatic
|
|
822
|
+
// rollback (retag + `--pull never`) lands in PR #5.
|
|
823
|
+
server.registerTool(
|
|
824
|
+
"moor_update_apply",
|
|
825
|
+
{
|
|
826
|
+
title: "Apply moor update (transient respawner)",
|
|
827
|
+
description:
|
|
828
|
+
"Update moor in-place via a transient respawner container. Runs preflight, enables drain, takes a fresh DB backup, then launches a one-shot Compose-aware respawner that pulls + re-creates the moor service. The respawner writes a marker file when done; this tool returns the audit_id immediately so the caller can poll. PR #4 happy path ONLY — no automatic rollback. A failed up/health writes `failed` and leaves the compose state; recovery may require manual `docker compose up`. Bypass is per-blocker: pass {bypass:['active_work']} to interrupt in-flight builds/execs/crons via the existing shutdown coordinator; {bypass:['unknown_digest']} when the registry comparison was inconclusive. Backup is mandatory and not bypassable.",
|
|
829
|
+
inputSchema: z.object({
|
|
830
|
+
target_digest: z
|
|
831
|
+
.string()
|
|
832
|
+
.regex(/^sha256:[0-9a-f]{64}$/, "target_digest must be sha256:<64 hex>")
|
|
833
|
+
.optional()
|
|
834
|
+
.describe(
|
|
835
|
+
"Pin the update to this exact image digest. Default: the registry's current `:latest` digest from moor_update_status.",
|
|
836
|
+
),
|
|
837
|
+
bypass: z
|
|
838
|
+
.array(z.enum(["active_work", "unknown_digest"]))
|
|
839
|
+
.optional()
|
|
840
|
+
.describe(
|
|
841
|
+
"Per-blocker bypass. `active_work` accepts that in-flight builds/execs/crons will be interrupted via the shutdown coordinator. `unknown_digest` accepts proceeding when the registry comparison is inconclusive (locally-built image, GHCR unreachable). Backup is mandatory and not in this list.",
|
|
842
|
+
),
|
|
843
|
+
}),
|
|
844
|
+
},
|
|
845
|
+
async (input) => {
|
|
846
|
+
const res = await apiPost("/api/server/update/apply", input ?? {});
|
|
847
|
+
if (res.status === 202) {
|
|
848
|
+
const { audit_id } = (await res.json()) as { audit_id: number };
|
|
849
|
+
return {
|
|
850
|
+
content: [
|
|
851
|
+
{
|
|
852
|
+
type: "text",
|
|
853
|
+
text: `Update started: audit_id=${audit_id}. Respawner is running async. Poll moor_update_status to watch the version change. Expected transitions: in_progress → success (clean apply) or → failed (compose/health failure; PR #4 has no rollback so the compose state is left where Compose left it). If the new moor never becomes healthy enough to ingest the marker, the audit row may stay in_progress until the 30-min stale sweep marks it crashed — at which point manual recovery (docker compose up) may be required.`,
|
|
854
|
+
},
|
|
855
|
+
],
|
|
856
|
+
};
|
|
857
|
+
}
|
|
858
|
+
// Error: surface the structured reason so callers can act on it.
|
|
859
|
+
const body = (await res.json().catch(() => ({}))) as {
|
|
860
|
+
error?: { code: string; reason?: string; unsafe_reasons?: string[] };
|
|
861
|
+
};
|
|
862
|
+
const code = body.error?.code ?? `HTTP ${res.status}`;
|
|
863
|
+
const reason = body.error?.reason ?? "no detail";
|
|
864
|
+
const extra = body.error?.unsafe_reasons
|
|
865
|
+
? `\nunsafe_reasons:\n - ${body.error.unsafe_reasons.join("\n - ")}`
|
|
866
|
+
: "";
|
|
867
|
+
throw new Error(`moor_update_apply refused [${code}]: ${reason}${extra}`);
|
|
868
|
+
},
|
|
869
|
+
);
|
|
870
|
+
|
|
785
871
|
server.registerTool(
|
|
786
872
|
"moor_cleanup_plan",
|
|
787
873
|
{
|