mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-28 12:04:41 +01:00
feat: add encrypted instance state snapshots
This commit is contained in:
1 parent
aac6ce82e1
commit
f9a60389d6
14 files changed
+496
-1
No files matched your search
@@ -0,0 +1,34 @@
|
||||
import * as p from "@clack/prompts";
|
||||
import pc from "picocolors";
|
||||
import { printPaperclipCliBanner } from "../utils/banner.js";
|
||||
|
||||
type StateApiOptions = { apiUrl?: string; token?: string; json?: boolean };
|
||||
|
||||
async function callStateApi(pathname: string, opts: StateApiOptions, body?: unknown) {
|
||||
const apiUrl = (opts.apiUrl?.trim() || process.env.PAPERCLIP_API_URL?.trim() || "http://127.0.0.1:3100/api").replace(/\/$/, "");
|
||||
const token = opts.token?.trim() || process.env.PAPERCLIP_API_KEY?.trim();
|
||||
const response = await fetch(`${apiUrl}${pathname}`, {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json", ...(token ? { authorization: `Bearer ${token}` } : {}) },
|
||||
body: JSON.stringify(body ?? {}),
|
||||
});
|
||||
const result = await response.json();
|
||||
if (!response.ok) throw new Error(typeof result?.error === "string" ? result.error : `Request failed (${response.status})`);
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function stateSnapshotCommand(opts: StateApiOptions) {
|
||||
printPaperclipCliBanner();
|
||||
p.intro(pc.bgCyan(pc.black(" paperclip state snapshot ")));
|
||||
const result = await callStateApi("/instance/state-snapshots", opts);
|
||||
if (opts.json) console.log(JSON.stringify(result, null, 2));
|
||||
else p.outro(pc.green(`Snapshot stored: ${result.objectKey}`));
|
||||
}
|
||||
|
||||
export async function stateRestoreCommand(objectKey: string, opts: StateApiOptions) {
|
||||
printPaperclipCliBanner();
|
||||
p.intro(pc.bgCyan(pc.black(" paperclip state restore ")));
|
||||
const result = await callStateApi("/instance/state-snapshots/restore", opts, { objectKey });
|
||||
if (opts.json) console.log(JSON.stringify(result, null, 2));
|
||||
else p.outro(pc.green(`Snapshot restored: ${objectKey}`));
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import { heartbeatRun } from "./commands/heartbeat-run.js";
|
||||
import { runCommand } from "./commands/run.js";
|
||||
import { bootstrapCeoInvite } from "./commands/auth-bootstrap-ceo.js";
|
||||
import { dbBackupCommand } from "./commands/db-backup.js";
|
||||
import { stateRestoreCommand, stateSnapshotCommand } from "./commands/state.js";
|
||||
import { registerEnvLabCommands } from "./commands/env-lab.js";
|
||||
import { registerContextCommands } from "./commands/client/context.js";
|
||||
import { registerCompanyCommands } from "./commands/client/company.js";
|
||||
@@ -160,6 +161,21 @@ program
|
||||
await dbBackupCommand(opts);
|
||||
});
|
||||
|
||||
const state = program.command("state").description("Inspect, snapshot, and restore instance state");
|
||||
state.command("snapshot")
|
||||
.description("Create an encrypted instance-state snapshot")
|
||||
.option("--api-url <url>", "Paperclip API base URL")
|
||||
.option("--token <token>", "Board/admin bearer token")
|
||||
.option("--json", "Print result as JSON")
|
||||
.action(stateSnapshotCommand);
|
||||
state.command("restore")
|
||||
.description("Restore an encrypted instance-state snapshot")
|
||||
.argument("<object-key>", "Storage-provider object key returned by state snapshot")
|
||||
.option("--api-url <url>", "Paperclip API base URL")
|
||||
.option("--token <token>", "Board/admin bearer token")
|
||||
.option("--json", "Print result as JSON")
|
||||
.action(stateRestoreCommand);
|
||||
|
||||
program
|
||||
.command("allowed-hostname")
|
||||
.description("Allow a hostname for authenticated/private mode access")
|
||||
|
||||
Reference in new issue
Block a user