mirror of
https://github.com/mvschwarz/openrig.git
synced 2026-11-12 01:41:39 -10:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
661a66e126 | ||
|
|
8854a217b9 | ||
|
|
d584b49003 |
No files matched your search
@@ -12,6 +12,7 @@ import { streamRetentionFloorSchema } from "./migrations/131_stream_retention_fl
|
||||
import { seatRuntimeSwitchesSchema } from "./migrations/132_seat_runtime_switches.js";
|
||||
import { streamReaderActionsSchema } from "./migrations/133_stream_reader_actions.js";
|
||||
import { seatIdentityProcessEvidenceSchema } from "./migrations/135_seat_identity_process_evidence.js";
|
||||
import { nodeClaudeTokenRefSchema } from "./migrations/136_node_claude_token_ref.js";
|
||||
import { bindingsSessionsSchema } from "./migrations/002_bindings_sessions.js";
|
||||
import { eventsSchema } from "./migrations/003_events.js";
|
||||
import { snapshotsSchema } from "./migrations/004_snapshots.js";
|
||||
@@ -269,4 +270,5 @@ export const ALL_MIGRATIONS: Migration[] = [
|
||||
streamReaderActionsSchema,
|
||||
claudeTokenReadingsSchema,
|
||||
seatIdentityProcessEvidenceSchema,
|
||||
nodeClaudeTokenRefSchema,
|
||||
];
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { Migration } from "../migrate.js";
|
||||
|
||||
// A Claude seat's own subscription credential, as a reference only: { label, tokenFile } JSON. Never the token.
|
||||
export const nodeClaudeTokenRefSchema: Migration = {
|
||||
name: "136_node_claude_token_ref.sql",
|
||||
sql: `
|
||||
ALTER TABLE nodes ADD COLUMN claude_token_ref TEXT;
|
||||
`,
|
||||
};
|
||||
@@ -12,6 +12,7 @@ import { parseClaudePermissionModes } from "./permission-drift.js";
|
||||
import { validateNativePermissionSelection } from "./native-permission-selection.js";
|
||||
import { seatLaunchAdditionsForLaunch } from "./seat-launch-additions.js";
|
||||
import { effectiveRuntimeHome } from "./seat-runtime-home.js";
|
||||
import { claudeSeatTokenStep, readClaudeSeatTokenRef } from "./claude-seat-token.js";
|
||||
|
||||
export interface ClaudeLaunchTarget {
|
||||
nodeId: string;
|
||||
@@ -246,6 +247,10 @@ export class ClaudeManagedLaunch {
|
||||
const otherwise = (key: string) => selectedKeys.includes(key) ? "" : ` else unset ${key};`;
|
||||
const terminal = terminalKeys.map(key => `if [ -n "\${${key}-}" ]; then export ${key};${otherwise(key)} fi`);
|
||||
terminal.push(`if [ -n "$${WAYLAND_DISPLAY_VAR}" ]; then export WAYLAND_DISPLAY="$${WAYLAND_DISPLAY_VAR}";${otherwise("WAYLAND_DISPLAY")} fi`);
|
||||
// A seat's own subscription token (rig provider switch --seat) is read from its file as Claude starts, after the
|
||||
// environment is reduced, so the value never passes through OpenRig; an unreadable file stops the launch.
|
||||
const tokenRef = readClaudeSeatTokenRef(this.db, target.nodeId);
|
||||
const tokenStep = tokenRef ? `${claudeSeatTokenStep(tokenRef, shellQuote)} && ` : "";
|
||||
return Object.freeze({ assertCurrent, configDir: context.configDir, executable: context.executable, executableIsScript: context.executableIsScript, command: (args: readonly string[], launchEnv: Readonly<Record<string, string>> = {}) => {
|
||||
assertCurrent();
|
||||
const launch = Object.entries(launchEnv).map(([key, value]) => `export ${shellQuote(`${key}=${value}`)}`);
|
||||
@@ -258,7 +263,7 @@ export class ClaudeManagedLaunch {
|
||||
const clear = `__openrig_remove=$(/usr/bin/env -0 | /usr/bin/env -i ${shellQuote(process.execPath)} -e ${shellQuote(removalArgs)}) && eval "$__openrig_remove"`;
|
||||
return `${WAYLAND_DISPLAY_ASSIGNMENT}; cd ${shellQuote(cwd)} && {
|
||||
${[...assignments, ...forwarded, ...seatForwarded, ...projectForwarded, ...terminal, ...launch].join("\n")}
|
||||
${clear} && exec /usr/bin/env "$@" ${[shellQuote(context.executable), ...args.map(shellQuote)].join(" ")}
|
||||
${clear} && ${tokenStep}exec /usr/bin/env "$@" ${[shellQuote(context.executable), ...args.map(shellQuote)].join(" ")}
|
||||
}`;
|
||||
} });
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import type Database from "better-sqlite3";
|
||||
import path from "node:path";
|
||||
|
||||
/** A Claude seat's own subscription credential: an operator label and an absolute token-file path. A reference only: the
|
||||
* launch script reads the file when Claude starts, so the value never passes through OpenRig. The label is the
|
||||
* operator's name for it, not an account the provider observed. */
|
||||
export interface ClaudeSeatTokenRef {
|
||||
label: string;
|
||||
tokenFile: string;
|
||||
}
|
||||
|
||||
const LABEL_RE = /^[A-Za-z0-9][A-Za-z0-9._@-]{0,63}$/;
|
||||
|
||||
/** Why a label or token-file path can't be a seat's reference, or null when it can. */
|
||||
export function claudeSeatTokenRefProblem(ref: ClaudeSeatTokenRef): string | null {
|
||||
if (!LABEL_RE.test(ref.label)) return "the label must start with a letter or digit, then use letters, digits, '.', '_', '@' or '-' (at most 64)";
|
||||
if (!path.isAbsolute(ref.tokenFile) || /[\0\n\r]/.test(ref.tokenFile)) return "the token file must be an absolute path on one line";
|
||||
return null;
|
||||
}
|
||||
|
||||
/** The stored reference, or null when it's absent or unreadable. */
|
||||
export function parseClaudeSeatTokenRef(value: unknown): ClaudeSeatTokenRef | null {
|
||||
if (typeof value !== "string" || value === "") return null;
|
||||
try {
|
||||
const { label, tokenFile } = JSON.parse(value) as { label?: unknown; tokenFile?: unknown };
|
||||
if (typeof label !== "string" || typeof tokenFile !== "string") return null;
|
||||
return claudeSeatTokenRefProblem({ label, tokenFile }) === null ? { label, tokenFile } : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** A seat's reference, read where a launch needs it; null before the column exists. */
|
||||
export function readClaudeSeatTokenRef(db: Database.Database, nodeId: string): ClaudeSeatTokenRef | null {
|
||||
const columns = db.prepare("PRAGMA table_info(nodes)").all() as Array<{ name: string }>;
|
||||
if (!columns.some((column) => column.name === "claude_token_ref")) return null;
|
||||
const row = db.prepare("SELECT claude_token_ref AS ref FROM nodes WHERE id = ?").get(nodeId) as { ref?: unknown } | undefined;
|
||||
return parseClaudeSeatTokenRef(row?.ref);
|
||||
}
|
||||
|
||||
/** The launch-script step that gives Claude the seat's token, read from its file as Claude starts. When the file can't be
|
||||
* read (or is empty), the step fails and Claude isn't started, rather than run on the config dir's stored login. */
|
||||
export function claudeSeatTokenStep(ref: ClaudeSeatTokenRef, quote: (value: string) => string): string {
|
||||
const refusal = `OpenRig: this seat's Claude token file (${ref.label}) can't be read: ${ref.tokenFile}. Claude wasn't started, `
|
||||
+ "so it doesn't run on another login. Fix the file, or choose another with rig provider switch.";
|
||||
return `{ CLAUDE_CODE_OAUTH_TOKEN=$(/bin/cat -- ${quote(ref.tokenFile)} 2>/dev/null) && [ -n "$CLAUDE_CODE_OAUTH_TOKEN" ] `
|
||||
+ `&& export CLAUDE_CODE_OAUTH_TOKEN || { printf '%s\\n' ${quote(refusal)} >&2; false; }; }`;
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import { deriveComposeProjectName } from "./compose-project-name.js";
|
||||
import type { ClaudeManagedBlockFile } from "./managed-blocks.js";
|
||||
import { mergeSeatEnv, parseStoredSeatEnv, type SeatEnv } from "./seat-env.js";
|
||||
import { withoutIgnoredRuntimeHomes } from "./seat-runtime-home.js";
|
||||
import { parseClaudeSeatTokenRef, type ClaudeSeatTokenRef } from "./claude-seat-token.js";
|
||||
import type {
|
||||
Rig,
|
||||
Node,
|
||||
@@ -588,6 +589,13 @@ export class RigRepository {
|
||||
return result.changes > 0;
|
||||
}
|
||||
|
||||
/** A Claude seat's own subscription credential, by reference only; null clears it. */
|
||||
setNodeClaudeTokenRef(nodeId: string, ref: ClaudeSeatTokenRef | null): boolean {
|
||||
if (!this.hasNodeColumn("claude_token_ref")) return false;
|
||||
return this.db.prepare("UPDATE nodes SET claude_token_ref = ? WHERE id = ?")
|
||||
.run(ref ? JSON.stringify({ label: ref.label, tokenFile: ref.tokenFile }) : null, nodeId).changes > 0;
|
||||
}
|
||||
|
||||
/** Persist the resolved Advisor; null removes a previous spec's selection. */
|
||||
setNodeAdvisor(nodeId: string, advisor: string | null): boolean {
|
||||
if (!this.hasNodeColumn("advisor")) return false;
|
||||
@@ -845,6 +853,7 @@ export class RigRepository {
|
||||
advisor: row.advisor ?? null,
|
||||
effortChoice: row.effort_choice ?? null,
|
||||
codexConfigProfile: row.codex_config_profile ?? null,
|
||||
claudeTokenRef: parseClaudeSeatTokenRef(row.claude_token_ref),
|
||||
permissionPolicy: row.permission_policy ?? null,
|
||||
env: parseStoredSeatEnv(row.env_json),
|
||||
cwd: row.cwd,
|
||||
@@ -937,6 +946,7 @@ interface NodeRow {
|
||||
advisor?: string | null;
|
||||
effort_choice?: string | null;
|
||||
codex_config_profile?: string | null;
|
||||
claude_token_ref?: string | null;
|
||||
permission_policy?: string | null;
|
||||
env_json?: string | null;
|
||||
cwd: string | null;
|
||||
|
||||
@@ -39,6 +39,8 @@ export interface Node {
|
||||
/** OPR.0.7.0.7: the seat's explicit effort choice (rig seat set-effort); null = none. */
|
||||
effortChoice?: string | null;
|
||||
codexConfigProfile?: string | null;
|
||||
/** A Claude seat's own subscription credential, as a reference (operator label and token-file path); null = none. */
|
||||
claudeTokenRef?: { label: string; tokenFile: string } | null;
|
||||
/** OPR.0.4.8.3 Seam B: attached permission_policy REF (builtin:<name> or spec-relative custom
|
||||
* path), or null when none is attached (= the floor). */
|
||||
permissionPolicy?: string | null;
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import Database from "better-sqlite3";
|
||||
import { ClaudeManagedLaunch } from "../src/domain/claude-managed-launch.js";
|
||||
|
||||
// Before a Claude seat moves to its own subscription token, OpenRig finds what Claude Code would rank above that token
|
||||
// (a cloud provider, ANTHROPIC_AUTH_TOKEN, ANTHROPIC_API_KEY, an apiKeyHelper): in the seat's launch environment and in the
|
||||
// settings files it loads. It names each one and its remedy, edits nothing, and says what it couldn't read.
|
||||
|
||||
const cleanup: Array<() => void> = [];
|
||||
afterEach(() => { for (const f of cleanup.splice(0)) f(); });
|
||||
|
||||
function fixture(opts: { daemonEnv?: Record<string, string>; memberEnv?: Record<string, string>; worktree?: boolean } = {}) {
|
||||
const root = mkdtempSync(path.join(tmpdir(), "claude-precedence-"));
|
||||
cleanup.push(() => rmSync(root, { recursive: true, force: true }));
|
||||
const home = path.join(root, "home"), main = path.join(root, "main"), cwd = opts.worktree ? path.join(root, "wt") : main;
|
||||
for (const dir of [home, main, cwd, path.join(home, ".claude"), path.join(cwd, ".claude"), path.join(main, ".claude"), path.join(main, ".git", "worktrees", "wt")]) {
|
||||
mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
if (opts.worktree) writeFileSync(path.join(cwd, ".git"), `gitdir: ${path.join(main, ".git", "worktrees", "wt")}\n`);
|
||||
const db = new Database(":memory:"); cleanup.push(() => { db.close(); });
|
||||
db.exec(`CREATE TABLE rigs(id TEXT, env_json TEXT);
|
||||
CREATE TABLE nodes(id TEXT, rig_id TEXT, runtime TEXT, cwd TEXT, env_json TEXT);
|
||||
CREATE TABLE bindings(id TEXT, node_id TEXT, tmux_session TEXT, tmux_pane TEXT);
|
||||
CREATE TABLE occupant_tenures(node_id TEXT, generation_uuid TEXT, generation_ordinal INTEGER);`);
|
||||
db.prepare("INSERT INTO rigs VALUES ('rig', NULL)").run();
|
||||
db.prepare("INSERT INTO nodes VALUES ('node', 'rig', 'claude-code', ?, ?)").run(cwd, opts.memberEnv ? JSON.stringify(opts.memberEnv) : null);
|
||||
db.exec("INSERT INTO bindings VALUES ('b', 'node', 'seat@rig', '%1'); INSERT INTO occupant_tenures VALUES ('node', 'gen', 1)");
|
||||
const managed = new ClaudeManagedLaunch(db, { PATH: "/usr/bin:/bin", HOME: home, ...opts.daemonEnv }, {}, async () => undefined);
|
||||
return { managed, home, main, cwd };
|
||||
}
|
||||
|
||||
describe("what would outrank a Claude seat's own subscription token", () => {
|
||||
it("names each one in the launch environment and the settings files the seat loads, with its remedy", async () => {
|
||||
const f = fixture({ daemonEnv: { ANTHROPIC_AUTH_TOKEN: "x" }, memberEnv: { ANTHROPIC_API_KEY: "y" }, worktree: true });
|
||||
writeFileSync(path.join(f.home, ".claude", "settings.json"), JSON.stringify({ apiKeyHelper: "/usr/local/bin/key" }));
|
||||
writeFileSync(path.join(f.cwd, ".claude", "settings.json"), JSON.stringify({ env: { CLAUDE_CODE_USE_BEDROCK: "1" } }));
|
||||
// In a worktree, Claude Code reads the local settings at the main checkout's root.
|
||||
writeFileSync(path.join(f.main, ".claude", "settings.local.json"), JSON.stringify({ env: { ANTHROPIC_API_KEY: "z" } }));
|
||||
const found = await f.managed.credentialPrecedence("node");
|
||||
expect(found.conflicts.map((c) => [c.name, c.source])).toEqual([
|
||||
["ANTHROPIC_AUTH_TOKEN", "the daemon's environment, which every managed Claude launch receives"],
|
||||
["ANTHROPIC_API_KEY", "this seat's env: (member or rig) or a one-off launch name"],
|
||||
["apiKeyHelper", `user settings ${path.join(f.home, ".claude", "settings.json")}`],
|
||||
["CLAUDE_CODE_USE_BEDROCK", `the env block of project settings ${path.join(f.cwd, ".claude", "settings.json")}`],
|
||||
["ANTHROPIC_API_KEY", `the env block of project local settings ${path.join(f.main, ".claude", "settings.local.json")}`],
|
||||
]);
|
||||
for (const conflict of found.conflicts) expect(conflict.remedy.length).toBeGreaterThan(0);
|
||||
// Values are never reported, only names and where they come from.
|
||||
expect(JSON.stringify(found)).not.toMatch(/"(x|y|z)"/);
|
||||
});
|
||||
|
||||
it("finds nothing on a seat with none, and says what it couldn't read", async () => {
|
||||
const f = fixture();
|
||||
writeFileSync(path.join(f.cwd, ".claude", "settings.json"), JSON.stringify({ env: { FOO: "1" }, model: "opus" }));
|
||||
const found = await f.managed.credentialPrecedence("node");
|
||||
expect(found.conflicts).toEqual([]);
|
||||
expect(found.checked).toContain("the launch environment");
|
||||
expect(found.notChecked).toEqual(["managed settings (a managed-settings.json file, MDM or the server)"]);
|
||||
});
|
||||
|
||||
it("a settings file that isn't valid JSON is named as not checked, never as clear", async () => {
|
||||
const f = fixture();
|
||||
writeFileSync(path.join(f.home, ".claude", "settings.json"), "{ not json");
|
||||
const found = await f.managed.credentialPrecedence("node");
|
||||
expect(found.notChecked).toContain(`user settings ${path.join(f.home, ".claude", "settings.json")} (not valid JSON)`);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,86 @@
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { chmodSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import { execFile } from "node:child_process";
|
||||
import { promisify } from "node:util";
|
||||
import Database from "better-sqlite3";
|
||||
import { ClaudeManagedLaunch } from "../src/domain/claude-managed-launch.js";
|
||||
import { RigRepository } from "../src/domain/rig-repository.js";
|
||||
import { createFullTestDb } from "./helpers/test-app.js";
|
||||
|
||||
// A Claude seat can be given its own subscription credential: an operator label and a token file. The managed launch
|
||||
// reads the file when Claude starts, so the value never passes through OpenRig (its records, the launch command, argv or
|
||||
// logs), and Claude doesn't start on another login when the file can't be read.
|
||||
|
||||
const exec = promisify(execFile);
|
||||
const cleanup: Array<() => void> = [];
|
||||
afterEach(() => { for (const f of cleanup.splice(0)) f(); });
|
||||
const SYNTHETIC = "synthetic-fixture-value-not-a-credential";
|
||||
|
||||
function fixture(ref?: { label: string; tokenFile: string }) {
|
||||
const root = mkdtempSync(path.join(tmpdir(), "claude-seat-token-"));
|
||||
cleanup.push(() => rmSync(root, { recursive: true, force: true }));
|
||||
const bin = path.join(root, "bin"), home = path.join(root, "home"), cwd = path.join(root, "work");
|
||||
for (const dir of [bin, home, cwd]) mkdirSync(dir);
|
||||
const claude = path.join(bin, "claude");
|
||||
writeFileSync(claude, `#!/bin/sh
|
||||
if [ "$1" = --help ]; then printf '%s\\n' '--permission-mode <mode> (choices: "auto", "default")'; exit 0; fi
|
||||
printf '{"TOKEN":"%s"}' "\${CLAUDE_CODE_OAUTH_TOKEN-absent}"
|
||||
`);
|
||||
chmodSync(claude, 0o700);
|
||||
const db = new Database(":memory:"); cleanup.push(() => { db.close(); });
|
||||
db.exec(`CREATE TABLE rigs(id TEXT, env_json TEXT);
|
||||
CREATE TABLE nodes(id TEXT, rig_id TEXT, runtime TEXT, cwd TEXT, env_json TEXT, claude_token_ref TEXT);
|
||||
CREATE TABLE bindings(id TEXT, node_id TEXT, tmux_session TEXT, tmux_pane TEXT);
|
||||
CREATE TABLE occupant_tenures(node_id TEXT, generation_uuid TEXT, generation_ordinal INTEGER);`);
|
||||
db.prepare("INSERT INTO rigs VALUES ('rig', NULL)").run();
|
||||
db.prepare("INSERT INTO nodes VALUES ('node', 'rig', 'claude-code', ?, NULL, ?)").run(cwd, ref ? JSON.stringify(ref) : null);
|
||||
db.exec("INSERT INTO bindings VALUES ('b', 'node', 'seat@rig', '%1'); INSERT INTO occupant_tenures VALUES ('node', 'gen', 1)");
|
||||
const managed = new ClaudeManagedLaunch(db, { PATH: `${bin}:/usr/bin:/bin`, HOME: home }, {}, async () => undefined);
|
||||
const command = async () => (await managed.prepare({ nodeId: "node", session: "seat@rig", pane: "%1" }, "auto")).command(["--permission-mode", "auto"]);
|
||||
const run = (cmd: string) => exec("/bin/sh", ["-c", cmd], { env: { PATH: "/usr/bin:/bin" }, timeout: 5000 });
|
||||
return { root, command, run };
|
||||
}
|
||||
|
||||
describe.skipIf(process.platform === "win32")("a Claude seat's own subscription token file", () => {
|
||||
it("reaches only that seat's Claude process; the value never enters the launch command", async () => {
|
||||
const root = mkdtempSync(path.join(tmpdir(), "claude-seat-token-file-")); cleanup.push(() => rmSync(root, { recursive: true, force: true }));
|
||||
const tokenFile = path.join(root, "claude-work.token");
|
||||
writeFileSync(tokenFile, `${SYNTHETIC}\n`, { mode: 0o600 });
|
||||
const f = fixture({ label: "work", tokenFile });
|
||||
const command = await f.command();
|
||||
expect(command).not.toContain(SYNTHETIC);
|
||||
expect(command).toContain(tokenFile);
|
||||
expect(JSON.parse((await f.run(command)).stdout)).toEqual({ TOKEN: SYNTHETIC });
|
||||
});
|
||||
|
||||
it("doesn't start Claude when the token file can't be read, and says why", async () => {
|
||||
const f = fixture({ label: "work", tokenFile: path.join(tmpdir(), "no-such-dir-claude-seat-token", "claude-work.token") });
|
||||
const result = await f.run(await f.command()).then(
|
||||
({ stdout, stderr }) => ({ code: 0, stdout, stderr }),
|
||||
(err: { code?: number; stdout?: string; stderr?: string }) => ({ code: err.code ?? -1, stdout: err.stdout ?? "", stderr: err.stderr ?? "" }));
|
||||
expect(result.code).not.toBe(0);
|
||||
expect(result.stdout).toBe("");
|
||||
expect(result.stderr).toContain("token file (work) can't be read");
|
||||
});
|
||||
|
||||
it("a seat without one launches exactly as before", async () => {
|
||||
const f = fixture();
|
||||
const command = await f.command();
|
||||
expect(command).not.toContain("CLAUDE_CODE_OAUTH_TOKEN");
|
||||
expect(JSON.parse((await f.run(command)).stdout)).toEqual({ TOKEN: "absent" });
|
||||
});
|
||||
|
||||
it("is kept on the seat as a reference only, and can be cleared", () => {
|
||||
const db = createFullTestDb(); cleanup.push(() => db.close());
|
||||
const repo = new RigRepository(db);
|
||||
const rig = repo.createRig("token-rig");
|
||||
const node = repo.addNode(rig.id, "owner", { runtime: "claude-code" });
|
||||
const read = () => repo.getRig(rig.id)?.nodes.find((n) => n.id === node.id)?.claudeTokenRef ?? null;
|
||||
expect(repo.setNodeClaudeTokenRef(node.id, { label: "work", tokenFile: "/secrets/claude-work.token" })).toBe(true);
|
||||
expect(read()).toEqual({ label: "work", tokenFile: "/secrets/claude-work.token" });
|
||||
expect(repo.setNodeClaudeTokenRef(node.id, null)).toBe(true);
|
||||
expect(read()).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -3,6 +3,7 @@ import { terminalPolicySchema } from "../../src/db/migrations/128_terminal_polic
|
||||
import { seatRuntimeSwitchesSchema } from "../../src/db/migrations/132_seat_runtime_switches.js";
|
||||
import { seatIdentityPanePidSchema } from "../../src/db/migrations/119_seat_identity_pane_pid.js";
|
||||
import { seatIdentityProcessEvidenceSchema } from "../../src/db/migrations/135_seat_identity_process_evidence.js";
|
||||
import { nodeClaudeTokenRefSchema } from "../../src/db/migrations/136_node_claude_token_ref.js";
|
||||
import { rigStartupLayersSchema } from "../../src/db/migrations/121_rig_startup_layers.js";
|
||||
import { humanOperatingModesSchema } from "../../src/db/migrations/122_human_operating_modes.js";
|
||||
import { queueDelegatedDecisionSchema } from "../../src/db/migrations/123_queue_delegated_decision.js";
|
||||
@@ -145,7 +146,7 @@ import fs from "node:fs";
|
||||
|
||||
/** Seam B R6: the canonical full-fixture migration list, exported so file-backed
|
||||
* DB-reopen tests migrate IDENTICALLY to createFullTestDb. */
|
||||
export const migrationsForFullTestDb = [coreSchema, bindingsSessionsSchema, eventsSchema, snapshotsSchema, checkpointsSchema, resumeMetadataSchema, nodeSpecFieldsSchema, packagesSchema, installJournalSchema, journalSeqSchema, bootstrapSchema, discoverySchema, discoveryFkFix, agentspecRebootSchema, startupContextSchema, chatMessagesSchema, podNamespaceSchema, contextUsageSchema, externalCliAttachmentSchema, rigServicesSchema, seatHandoverObservabilitySchema, nodeCodexConfigProfileSchema, nodePermissionPolicySchema, rigPermissionPolicySchema, nodePolicyProvenanceSchema, rigPolicyProvenanceSchema, streamItemsSchema, queueItemsSchema, queueTransitionsSchema, rigPolicySchema, rigArchiveSchema, resumeProvenanceSchema, resumeVerificationSchema, seatIdentityVerdictsSchema, selfHostIdentitySchema, occupantTenuresSchema, daemonLifecycleSchema, watchdogJobsSchema, occupantGenerationStampsSchema, projectionManifestSchema, watchdogTargetGenerationSchema, appliedLaunchObservationsSchema, appliedLaunchObservationInvalidationsSchema, threadSeatMapSchema, queueTransitionWakesSchema, nodeSessionSourceSchema, scopedOperatingPostureSchema, humanNotificationIntentSchema, reviewReadIndexesSchema, inventoryEventIndexesSchema, rigClaudeManagedBlockFileSchema, nodePermissionSelectionsSchema, humanReplyToSchema, humanQuestionsSchema, nodeEffortSchema, rigNonInterruptiveSchema, rigInstallRootSchema, threadPartMapSchema, queueTransitionsQitemIdOrderSchema, queueItemsRecoveryTagsSchema, resumeRotationSchema, sessionStopIntentSchema, nodeEffortChoiceSchema, modePostureCoexistSchema, seatEnvSchema, appliedLaunchDaemonOptOutSchema, sessionCommunityHarnessSchema, threadSeatMapChannelKeySchema, queueItemWakeInputBoxSchema, providerAccountReadingsSchema, humanQuietWindowsSchema, seatIdentityPanePidSchema, seatIdentityProcessEvidenceSchema, rigStartupLayersSchema, humanOperatingModesSchema, queueDelegatedDecisionSchema, nodeAdvisorSchema, operationCorrelationSchema, terminalPolicySchema, seatRuntimeSwitchesSchema, claudeTokenReadingsSchema];
|
||||
export const migrationsForFullTestDb = [coreSchema, bindingsSessionsSchema, eventsSchema, snapshotsSchema, checkpointsSchema, resumeMetadataSchema, nodeSpecFieldsSchema, packagesSchema, installJournalSchema, journalSeqSchema, bootstrapSchema, discoverySchema, discoveryFkFix, agentspecRebootSchema, startupContextSchema, chatMessagesSchema, podNamespaceSchema, contextUsageSchema, externalCliAttachmentSchema, rigServicesSchema, seatHandoverObservabilitySchema, nodeCodexConfigProfileSchema, nodePermissionPolicySchema, rigPermissionPolicySchema, nodePolicyProvenanceSchema, rigPolicyProvenanceSchema, streamItemsSchema, queueItemsSchema, queueTransitionsSchema, rigPolicySchema, rigArchiveSchema, resumeProvenanceSchema, resumeVerificationSchema, seatIdentityVerdictsSchema, selfHostIdentitySchema, occupantTenuresSchema, daemonLifecycleSchema, watchdogJobsSchema, occupantGenerationStampsSchema, projectionManifestSchema, watchdogTargetGenerationSchema, appliedLaunchObservationsSchema, appliedLaunchObservationInvalidationsSchema, threadSeatMapSchema, queueTransitionWakesSchema, nodeSessionSourceSchema, scopedOperatingPostureSchema, humanNotificationIntentSchema, reviewReadIndexesSchema, inventoryEventIndexesSchema, rigClaudeManagedBlockFileSchema, nodePermissionSelectionsSchema, humanReplyToSchema, humanQuestionsSchema, nodeEffortSchema, rigNonInterruptiveSchema, rigInstallRootSchema, threadPartMapSchema, queueTransitionsQitemIdOrderSchema, queueItemsRecoveryTagsSchema, resumeRotationSchema, sessionStopIntentSchema, nodeEffortChoiceSchema, modePostureCoexistSchema, seatEnvSchema, appliedLaunchDaemonOptOutSchema, sessionCommunityHarnessSchema, threadSeatMapChannelKeySchema, queueItemWakeInputBoxSchema, providerAccountReadingsSchema, humanQuietWindowsSchema, seatIdentityPanePidSchema, seatIdentityProcessEvidenceSchema, rigStartupLayersSchema, humanOperatingModesSchema, queueDelegatedDecisionSchema, nodeAdvisorSchema, operationCorrelationSchema, terminalPolicySchema, seatRuntimeSwitchesSchema, claudeTokenReadingsSchema, nodeClaudeTokenRefSchema];
|
||||
|
||||
/**
|
||||
* P24 — the DECLARED exclusions for {@link migrationsForFullTestDb}. That list is deliberately a
|
||||
|
||||
Reference in new issue
Block a user