@cortexkit/anthropic-auth-core 2.4.0 → 2.4.2

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.
@@ -0,0 +1,253 @@
1
+ import { type NativeVaultPaths } from './native-vault-paths.ts';
2
+ import { type NativePoolPaths } from './pool-paths.ts';
3
+ import { publishOwned, type StateLayoutFs, type StateLayoutLockSpec, type StateLayoutLockStack } from './state-layout-io.ts';
4
+ import { type RelocationRole, type StateLayoutRecord } from './state-layout-record.ts';
5
+ export { movingJournalPath } from './state-layout-record.ts';
6
+ /** Lock name of the layout lock on `<sub>/layout.json`. */
7
+ export declare const LAYOUT_LOCK_NAME = "runtime-layout";
8
+ /** How long a process waits for the layout lock: a whole move at 15 s per contended lock. */
9
+ export declare const LAYOUT_LOCK_TIMEOUT_MS = 120000;
10
+ /** One file slot of a dataset in both layouts. */
11
+ export interface RelocationSlot {
12
+ readonly role: RelocationRole;
13
+ /** The canonical legacy-layout path, in the config folder. */
14
+ readonly from: string;
15
+ /**
16
+ * The legacy-layout path as configured: the config folder as given joined
17
+ * with the role's fixed file name. `from` follows a symlink at the leaf, so
18
+ * only this path shows whether the configured file is a link.
19
+ */
20
+ readonly configured: string;
21
+ /** The state-home path, in `<sub>`. */
22
+ readonly to: string;
23
+ }
24
+ /**
25
+ * OpenCode's default dataset of one config folder, resolved in both layouts.
26
+ * Only that dataset ever moves.
27
+ */
28
+ export interface RelocationDataset {
29
+ /** The config folder as configured (not canonicalized). */
30
+ readonly configDir: string;
31
+ readonly identity: string;
32
+ readonly stateRoot: string;
33
+ /** The canonical state-home folder. */
34
+ readonly sub: string;
35
+ /** The canonical config folder holding the legacy files. */
36
+ readonly oldDir: string;
37
+ readonly oldPaths: NativePoolPaths;
38
+ readonly oldVault: NativeVaultPaths;
39
+ readonly newPaths: NativePoolPaths;
40
+ readonly newVault: NativeVaultPaths;
41
+ readonly slots: readonly RelocationSlot[];
42
+ readonly storageIds: {
43
+ readonly local: string;
44
+ readonly vault: string;
45
+ };
46
+ }
47
+ /** The legacy-layout files of OpenCode's default dataset of one config folder. */
48
+ export interface LegacyDatasetFiles {
49
+ /** The canonical config folder holding the legacy files. */
50
+ readonly oldDir: string;
51
+ readonly oldPaths: NativePoolPaths;
52
+ readonly oldVault: NativeVaultPaths;
53
+ }
54
+ /**
55
+ * Resolve only the config folder's side of the dataset. Nothing under the
56
+ * state folder is resolved or read, so a start that stays legacy does not
57
+ * depend on the state folder being readable.
58
+ */
59
+ export declare function resolveLegacyDatasetFiles(configDir: string): Promise<LegacyDatasetFiles>;
60
+ export declare function resolveRelocationDataset(input: {
61
+ configDir: string;
62
+ identity: string;
63
+ stateRoot: string;
64
+ /** The canonical state-home folder. */
65
+ sub: string;
66
+ }): Promise<RelocationDataset>;
67
+ /** The named points of a transition; the step hook runs at each. */
68
+ export type StateLayoutStep = 'old-holders-locking' | 'locks-held' | 'fence-checked' | 'config-dir-written' | 'preparing-written' | `copied:${RelocationRole}` | 'copies-flushed' | 'artifacts-checked' | 'journal-set-aside' | 'local-runtime-tombstoned' | 'vault-runtime-tombstoned' | 'old-dir-flushed' | 'verified' | 'committed' | `cleanup:${RelocationRole}` | 'cleanup-recorded' | 'rollback:journal' | 'rollback:localRuntime' | 'rollback:vaultRuntime' | 'rollback:old-dir-flushed' | 'rollback:destination-removed' | 'rollback:sub-flushed' | 'rolled-back' | 'restore:locks-held' | 'restore:discarded' | 'restore:restoring-written' | `restore:staged:${RelocationRole}` | 'restore:staging-flushed' | 'restore:staged-written' | `restore:placed:${RelocationRole}` | 'restore:data-flushed' | 'restore:barriers-flushed' | 'restore:restored-written' | `restore:retired:${RelocationRole}` | 'restore:sub-flushed' | 'restore:done';
69
+ /**
70
+ * Thrown by a step hook to stop a transition exactly there, with no rollback
71
+ * and nothing else written, as a crash of the process would. Tests use it to
72
+ * leave every intermediate state on disk; the transition releases its locks
73
+ * so the next start needn't wait for their leases to expire.
74
+ */
75
+ export declare class StateLayoutCrash extends Error {
76
+ readonly step: StateLayoutStep;
77
+ constructor(step: StateLayoutStep);
78
+ }
79
+ /** Everything a transition needs besides the dataset. */
80
+ export interface StateLayoutTransitionContext {
81
+ readonly fs: StateLayoutFs;
82
+ readonly stack: StateLayoutLockStack;
83
+ readonly release: string;
84
+ readonly now: () => number;
85
+ readonly onStep?: (step: StateLayoutStep) => void | Promise<void>;
86
+ /** The longest wait for one data lock; the pool store's default otherwise. */
87
+ readonly dataLockTimeoutMs?: number;
88
+ /** The longest wait for a holder lock. */
89
+ readonly holderLockTimeoutMs?: number;
90
+ }
91
+ export declare function stateLayoutStep(ctx: StateLayoutTransitionContext, step: StateLayoutStep): Promise<void>;
92
+ /** The two holder files a transition locks, in the order it locks them. */
93
+ export declare function transitionHolderFiles(dataset: RelocationDataset): string[];
94
+ /**
95
+ * Take the `holders` lock on `holderFile`. Waiting too long throws
96
+ * `LockContentionError`, which callers report as busy. Any other failure to
97
+ * take it (the lock file can't be created: EACCES, EROFS, ENOSPC) throws
98
+ * `HolderRegistrationError('unavailable')`, whose fixed message names no
99
+ * file, so a host never shows the lock file's path. `timeoutMs` undefined
100
+ * waits the lock stack's default.
101
+ */
102
+ export declare function acquireHolderLock(ctx: Pick<StateLayoutTransitionContext, 'stack'>, holderFile: string, timeoutMs: number | undefined): Promise<void>;
103
+ /** Take the `holders` lock on both holder files, in sorted path order. */
104
+ export declare function acquireHolderLocks(dataset: RelocationDataset, ctx: StateLayoutTransitionContext): Promise<void>;
105
+ /** A pool's rows changed between the unlocked snapshot and its locks. */
106
+ export declare class PoolSnapshotChanged extends Error {
107
+ readonly statePath: string;
108
+ constructor(statePath: string);
109
+ }
110
+ /** A pool can't be read, so its row locks can't be named. */
111
+ export declare class PoolUnreadable extends Error {
112
+ readonly statePath: string;
113
+ readonly reason: string;
114
+ constructor(statePath: string, reason: string);
115
+ }
116
+ /**
117
+ * The refresh locks of one pool: every row lock (by id and by identity, since
118
+ * a refresh that started before the row learned its identity holds the id
119
+ * lock) and every per-identity provider lock, each list in the order it is
120
+ * taken. With the default provider lock between them, these are the locks
121
+ * common-auth's refresh holds from before the token exchange until the
122
+ * successor is written, so once they are held no refresh of this pool is
123
+ * between its exchange and its write.
124
+ */
125
+ export interface PoolRefreshLocks {
126
+ readonly rows: readonly string[];
127
+ readonly providers: readonly string[];
128
+ readonly identities: readonly string[];
129
+ }
130
+ /**
131
+ * The one order in which every layout transition takes the data locks of a
132
+ * dataset's old files, each lock once: `native-migration`, the local pool's
133
+ * locks, the custody roster and local runtime, the vault pool's locks, the
134
+ * vault roster, the vault runtime and the vault routing file.
135
+ *
136
+ * A move passes `refresh`, which adds each pool's row and provider locks
137
+ * before its store locks, so that no refresh is in flight while it copies.
138
+ * A restore passes none: the old pool states are absent after a move's
139
+ * cleanup and the old-layout barriers stop every refresh before its provider
140
+ * call, while no process may use the moved copy without a live registration,
141
+ * which restore refuses. The restore order is therefore a subsequence of the
142
+ * move order, and the two can't wait on each other in opposite orders.
143
+ */
144
+ export declare function stateLayoutDataLockOrder(dataset: RelocationDataset, options?: {
145
+ timeoutMs?: number;
146
+ refresh?: {
147
+ readonly local: PoolRefreshLocks;
148
+ readonly vault: PoolRefreshLocks;
149
+ };
150
+ }): StateLayoutLockSpec[];
151
+ /**
152
+ * Take every data lock of the dataset's old files in
153
+ * `stateLayoutDataLockOrder`. A move (`drainRefreshes`, the default) first
154
+ * reads each pool's rows without a lock to name their row and provider locks,
155
+ * then reads them again under the store locks: if a row changed while the
156
+ * locks were taken, everything is released and taken once more, and a second
157
+ * change throws `PoolSnapshotChanged`. A restore takes no row or provider
158
+ * lock.
159
+ */
160
+ export declare function acquireDataLocks(dataset: RelocationDataset, ctx: StateLayoutTransitionContext, options?: {
161
+ drainRefreshes?: boolean;
162
+ }): Promise<void>;
163
+ /**
164
+ * Commit the coalesced writes this process has queued for the dataset before
165
+ * any lock is taken: close every pool store open on the old pool states (a
166
+ * bounded drain of their quota readings) and drain the vault roster's quota
167
+ * writer. Both commit under locks the mover is about to hold, so they must
168
+ * finish first. Afterwards forget every cached file observation, so nothing
169
+ * this process cached about the old files answers for them after the move.
170
+ */
171
+ export declare function drainCoalescedWrites(dataset: RelocationDataset): Promise<void>;
172
+ /** A relocation tombstone as written over an old runtime file. */
173
+ export declare function relocationTombstone(record: Pick<StateLayoutRecord, 'sub' | 'stateRoot' | 'identity' | 'attempt' | 'by'>): string;
174
+ export declare function tombstoneAttempt(path: string): Promise<string | undefined>;
175
+ export declare function exists(path: string): Promise<boolean>;
176
+ export declare function removeOwnStaging(path: string): Promise<void>;
177
+ /**
178
+ * Why `record` doesn't list exactly the files of `dataset`, each role with the
179
+ * paths this start resolved, or undefined when it does. Recovery deletes and
180
+ * renames only files a record names, so a record naming any other file is
181
+ * never acted on.
182
+ */
183
+ export declare function recordFilesProblem(record: Pick<StateLayoutRecord, 'files' | 'restore'>, dataset: Pick<RelocationDataset, 'slots'>): string | undefined;
184
+ /** Why a move may not start, or undefined when it may. */
185
+ export declare function movePreconditionFailure(dataset: RelocationDataset, platform: NodeJS.Platform): Promise<string | undefined>;
186
+ /** A rollback met a state no supported writer produces; it changed nothing more. */
187
+ export declare class StateLayoutRollbackRefused extends Error {
188
+ readonly path: string;
189
+ constructor(path: string, detail: string);
190
+ }
191
+ /** Expect `path` to hold bytes hashing to `sha256`, or to be absent for null. */
192
+ export declare function expectDigest(path: string, sha256: string | null): () => Promise<string | undefined>;
193
+ export declare function expectTombstone(path: string, attempt: string): () => Promise<string | undefined>;
194
+ export declare function expectAbsent(path: string): () => Promise<string | undefined>;
195
+ /**
196
+ * Rename `from` to `to` (or unlink `from` when `to` is undefined) through
197
+ * `publishOwned`, treating a skipped retry as a failure.
198
+ */
199
+ export declare function owned(ctx: StateLayoutTransitionContext, step: Parameters<typeof publishOwned>[0], expect: () => Promise<string | undefined>): Promise<void>;
200
+ /**
201
+ * Undo a `preparing` record, under the mover's full lock set. Only this
202
+ * attempt's own artifacts are undone; any other state is left alone because
203
+ * the old layout is still authoritative. Every step is a no-op once done, so
204
+ * recovery may run again from any point. Throws `StateLayoutRollbackRefused`
205
+ * when a slot is in a state no supported writer produces.
206
+ */
207
+ export declare function rollbackPreparing(record: StateLayoutRecord, dataset: RelocationDataset, ctx: StateLayoutTransitionContext): Promise<StateLayoutRecord>;
208
+ /**
209
+ * Remove the verified old copies, local pool state first, while
210
+ * every data lock is still held. A file is unlinked only if it still hashes
211
+ * to the recorded value. A changed or reappeared old pool state or journal is
212
+ * a conflict: both copies are kept and the record says `conflict`, which
213
+ * stops every state-home process. Any other changed file is kept and listed.
214
+ * Running it again after a conflict was resolved by hand finishes the
215
+ * cleanup.
216
+ *
217
+ * Once a credential conflict is found it outlives any later failure: if a
218
+ * later step or the final record write fails, the conflict is still recorded
219
+ * when possible, and the call throws a `conflict` refusal rather than the
220
+ * failure, so no caller can mistake it for an ordinary pending cleanup.
221
+ */
222
+ export declare function cleanupOldCopies(record: StateLayoutRecord, dataset: RelocationDataset, ctx: StateLayoutTransitionContext): Promise<StateLayoutRecord>;
223
+ /** How a move ended. */
224
+ export type StateLayoutMoveOutcome = {
225
+ readonly status: 'committed';
226
+ readonly record: StateLayoutRecord;
227
+ } | {
228
+ readonly status: 'rolled-back';
229
+ readonly reason: string;
230
+ readonly record?: StateLayoutRecord;
231
+ };
232
+ /**
233
+ * The move itself, under the layout lock, both holder locks and every data
234
+ * lock, all held by `ctx.stack`. `checkArtifacts` repeats the fence's check
235
+ * for pid artifacts of older versions and returns a blocker description or
236
+ * undefined.
237
+ *
238
+ * Before the commit any failure rolls back and resolves `rolled-back`; a
239
+ * rollback that refuses throws `StateLayoutRollbackRefused`. A failure of the
240
+ * commit write itself is resolved from the record on disk. After the commit
241
+ * nothing goes back to legacy: a cleanup failure leaves cleanup `pending` and
242
+ * still resolves `committed`.
243
+ */
244
+ export declare function moveDataset(dataset: RelocationDataset, ctx: StateLayoutTransitionContext, checkArtifacts: () => Promise<string | undefined>): Promise<StateLayoutMoveOutcome>;
245
+ /**
246
+ * Flush `<sub>/layout.json` and `<sub>`. A process that died after renaming
247
+ * a new record into place but before flushing `<sub>` leaves a record that
248
+ * power loss can still revert, so a process that acts on a record it found
249
+ * makes it durable first.
250
+ */
251
+ export declare function syncLayoutRecord(dataset: Pick<RelocationDataset, 'sub'>, ctx: Pick<StateLayoutTransitionContext, 'fs'>): Promise<void>;
252
+ /** Flush a committed record and `<sub>`. */
253
+ export declare function syncCommitted(dataset: RelocationDataset, ctx: Pick<StateLayoutTransitionContext, 'fs'>): Promise<void>;
@@ -23,10 +23,19 @@ export declare const LEGACY_STATE_LAYOUT: StateLayout;
23
23
  * The layout a resolver uses when its caller passes none. A process decides it
24
24
  * once, before it resolves any pool path, and never switches: every
25
25
  * re-resolution must use the same layout as the first, or comparisons between
26
- * them fail. No process can choose the state-home layout yet, so this is
27
- * always the legacy layout.
26
+ * them fail. `legacyConfig` is the canonical account file the resolver works
27
+ * from. A dataset with no recorded decision, and every call without
28
+ * `legacyConfig`, uses the legacy layout.
28
29
  */
29
- export declare function processStateLayout(): StateLayout;
30
+ export declare function processStateLayout(legacyConfig?: string): StateLayout;
31
+ /**
32
+ * Record the layout decided for the dataset whose canonical account file is
33
+ * `legacyConfig`. A decision is final for the process: recording a different
34
+ * layout for the same dataset throws.
35
+ */
36
+ export declare function recordProcessStateLayout(legacyConfig: string, layout: StateLayout): void;
37
+ /** Forget every recorded layout decision; tests only. */
38
+ export declare function resetProcessStateLayoutsForTests(): void;
30
39
  /** The `node:path` functions the pure helpers use; tests pass `path.win32`. */
31
40
  export type StatePathApi = Pick<typeof nodePath, 'join' | 'isAbsolute'>;
32
41
  export interface StateRootInputs {
@@ -86,6 +95,16 @@ export declare function resolveStateHomeTarget(options?: {
86
95
  env?: NodeJS.ProcessEnv;
87
96
  home?: string;
88
97
  }): StateHomeTarget | undefined;
98
+ /**
99
+ * `resolveStateHomeTarget` for a config folder whose identity is already
100
+ * known. Naming the subfolder resolves the home directory's default config
101
+ * folder, which may be a different folder from the one in use, so a start
102
+ * that never looks at the state folder never calls this.
103
+ */
104
+ export declare function stateHomeTargetFor(identity: string, options?: {
105
+ env?: NodeJS.ProcessEnv;
106
+ home?: string;
107
+ }): StateHomeTarget;
89
108
  /**
90
109
  * Names of the files kept in a state-home subfolder. Data files keep their
91
110
  * default legacy names so the folder stays readable for manual recovery.
@@ -143,7 +162,12 @@ export interface ConfigDirMarker {
143
162
  export declare function readConfigDirMarker(dir: string): Promise<ConfigDirMarker | undefined>;
144
163
  export declare const STATE_LAYOUT_PHASES: readonly ['preparing', 'committed', 'rolled-back', 'restoring', 'restore-staged', 'restored'];
145
164
  export type StateLayoutPhase = (typeof STATE_LAYOUT_PHASES)[number];
146
- declare const CLEANUP_STATES: readonly ['pending', 'done', 'kept'];
165
+ /**
166
+ * `conflict`: cleanup found that an old copy of the local pool state or the
167
+ * journal changed after the move was verified, so both copies are kept and no
168
+ * process serves the dataset until a person removes one of them.
169
+ */
170
+ declare const CLEANUP_STATES: readonly ['pending', 'done', 'kept', 'conflict'];
147
171
  declare const RESTORE_CLEANUP_STATES: readonly ['pending', 'done'];
148
172
  /** The fields of `layout.json` that decide whether a process may use a layout. */
149
173
  export interface StateLayoutMarker {
@@ -0,0 +1,120 @@
1
+ import { publishOwned, type StateLayoutFs } from './state-layout-io.ts';
2
+ import { type StateLayoutPhase } from './state-layout-paths.ts';
3
+ /**
4
+ * The data files a move copies, in the order cleanup removes the old copies:
5
+ * the local pool state (refresh tokens) first.
6
+ */
7
+ export declare const RELOCATION_ROLES: readonly ['localPoolState', 'journal', 'localRoster', 'vaultStoreState', 'vaultRoster', 'vaultRouting', 'localRuntime', 'vaultRuntime'];
8
+ export type RelocationRole = (typeof RELOCATION_ROLES)[number];
9
+ /** Old files whose change or reappearance after a move is a fatal conflict. */
10
+ export declare const CREDENTIAL_AUTHORITY_ROLES: readonly RelocationRole[];
11
+ /** One file of a move: where it was, where it goes, and its bytes' hash. */
12
+ export interface RelocationFileRecord {
13
+ readonly role: RelocationRole;
14
+ readonly from: string;
15
+ readonly to: string;
16
+ /** Null when the file was absent at the move. */
17
+ readonly sha256: string | null;
18
+ }
19
+ export interface RelocationBarriers {
20
+ /** Whether the old journal existed and was set aside. */
21
+ readonly journal: 'present' | 'absent';
22
+ /** The hash of each old runtime before its tombstone, or null when absent. */
23
+ readonly localRuntime: {
24
+ readonly original: string | null;
25
+ };
26
+ readonly vaultRuntime: {
27
+ readonly original: string | null;
28
+ };
29
+ }
30
+ /** One file a restore copies back: from `to` (state folder) to `from` (config folder). */
31
+ export interface RestoreFileRecord {
32
+ readonly role: RelocationRole;
33
+ /** The legacy-layout path the file goes back to. */
34
+ readonly from: string;
35
+ /** The state-home copy it is read from. */
36
+ readonly to: string;
37
+ readonly sha256: string;
38
+ /** `<from>.restoring-<restore attempt>`: the name it is staged under. */
39
+ readonly staging: string;
40
+ }
41
+ /**
42
+ * What a restore to the legacy layout records in `layout.json`. A restore
43
+ * done by hand (the documented manual procedure) writes only
44
+ * `{ cleanup: 'done' }` with phase `restored`; every restore made by the
45
+ * command records all fields.
46
+ */
47
+ export interface StateLayoutRestoreRecord {
48
+ readonly attempt?: string;
49
+ readonly startedAt?: number;
50
+ readonly files?: readonly RestoreFileRecord[];
51
+ /**
52
+ * For each runtime: `copy` when the state folder holds one, which goes
53
+ * back over the tombstone; `unlink` when it holds none, so the tombstone
54
+ * is only removed.
55
+ */
56
+ readonly runtimes?: {
57
+ readonly local: 'copy' | 'unlink';
58
+ readonly vault: 'copy' | 'unlink';
59
+ };
60
+ /** Set from phase `restored` on: whether the state-folder copies are removed. */
61
+ readonly cleanup?: 'pending' | 'done';
62
+ /** State-folder copies that changed after they were staged; left in place. */
63
+ readonly kept?: readonly string[];
64
+ }
65
+ /** The staging name a restore uses for `from`. */
66
+ export declare function restoreStagingPath(from: string, attempt: string): string;
67
+ /** The full `layout.json` a move writes. */
68
+ export interface StateLayoutRecord {
69
+ readonly version: 1;
70
+ readonly phase: StateLayoutPhase;
71
+ readonly attempt: string;
72
+ readonly identity: string;
73
+ readonly stateRoot: string;
74
+ readonly sub: string;
75
+ readonly oldDir: string;
76
+ readonly storageIds: {
77
+ readonly local: string;
78
+ readonly vault: string;
79
+ };
80
+ readonly by: string;
81
+ readonly startedAt: number;
82
+ readonly files: readonly RelocationFileRecord[];
83
+ readonly barriers: RelocationBarriers;
84
+ readonly committedAt?: number;
85
+ readonly cleanup?: 'pending' | 'done' | 'kept' | 'conflict';
86
+ /** Old non-credential files whose bytes changed after the move; left in place. */
87
+ readonly kept?: readonly string[];
88
+ /** Old credential or journal files found changed or back after the move. */
89
+ readonly conflict?: readonly {
90
+ readonly old: string;
91
+ readonly new: string;
92
+ }[];
93
+ readonly rolledBackAt?: number;
94
+ /** Present while a restore is under way or finished; written by restore. */
95
+ readonly restore?: StateLayoutRestoreRecord;
96
+ }
97
+ /**
98
+ * Whether `name` is exactly the name a move sets the journal named
99
+ * `journalName` aside under: `<journalName>.moving-<attempt>`, with a valid
100
+ * attempt id and nothing after it.
101
+ */
102
+ export declare function isMovingJournalName(journalName: string, name: string): boolean;
103
+ export declare function layoutRecordPath(sub: string): string;
104
+ /** The name the journal is set aside under during a move. */
105
+ export declare function movingJournalPath(journal: string, attempt: string): string;
106
+ /**
107
+ * Read `<sub>/layout.json` in full. Undefined only when it is absent; any other
108
+ * read error, and any content that isn't a complete record, throws
109
+ * `StateLayoutFileError`. An unreadable marker never counts as "no move".
110
+ */
111
+ export declare function readStateLayoutRecord(sub: string): Promise<StateLayoutRecord | undefined>;
112
+ /**
113
+ * Publish `record` as `<sub>/layout.json` durably: stage it owner-only as
114
+ * `layout.json.<attempt>.<nonce>.tmp`, flush it, rename it into place through
115
+ * `publishOwned`, then flush `<sub>`. `expect` is the ownership check for the
116
+ * slot (for example "still this attempt's preparing record").
117
+ */
118
+ export declare function publishStateLayoutRecord(record: StateLayoutRecord, guard: Parameters<typeof publishOwned>[1], fs: StateLayoutFs): Promise<void>;
119
+ /** Whether `name` in `<sub>` is a staging file of a layout record. */
120
+ export declare function isLayoutRecordStage(name: string, attempt?: string): boolean;
@@ -0,0 +1,83 @@
1
+ import { type HolderLiveness } from './holder-registry.ts';
2
+ import { type PreNArtifactLocations } from './layout-fence.ts';
3
+ import type { NativeLockEvent } from './native-lock-observer.ts';
4
+ import { type StateLayoutFs } from './state-layout-io.ts';
5
+ import { type RelocationDataset, type StateLayoutStep, type StateLayoutTransitionContext } from './state-layout-move.ts';
6
+ import { type StateLayoutRecord } from './state-layout-record.ts';
7
+ export interface RestoreLegacyOptions {
8
+ env?: NodeJS.ProcessEnv;
9
+ home?: string;
10
+ /** Filesystem seam; `node:fs` for this platform otherwise. */
11
+ fs?: StateLayoutFs;
12
+ liveness?: HolderLiveness;
13
+ /** Where pid artifacts of older versions are looked for. */
14
+ artifacts?: PreNArtifactLocations;
15
+ onLockEvent?: (event: NativeLockEvent) => void;
16
+ /** Runs at each named step; tests inject faults here. */
17
+ onStep?: (step: StateLayoutStep) => void | Promise<void>;
18
+ now?: () => number;
19
+ /** Tests shorten the waits; production uses the defaults. */
20
+ timeouts?: {
21
+ layoutMs?: number;
22
+ holderMs?: number;
23
+ dataMs?: number;
24
+ };
25
+ }
26
+ /** How a restore ended. */
27
+ export type RestoreLegacyOutcome = {
28
+ readonly status: 'restored';
29
+ /** The config folder that holds the dataset again. */
30
+ readonly oldDir: string;
31
+ /** The state folder it came from. */
32
+ readonly sub: string;
33
+ /** State-folder copies that changed after staging and were left in place. */
34
+ readonly kept: readonly string[];
35
+ } | {
36
+ /** The dataset is already in the legacy layout; nothing was written. */
37
+ readonly status: 'not-moved';
38
+ readonly reason: string;
39
+ };
40
+ /**
41
+ * Make the record this run found durable, under the locks, before acting on
42
+ * it. The run that published it may have died after renaming it into place
43
+ * but before flushing `<sub>`: R3 or R5 acting on a record that power loss
44
+ * can still revert would leave legacy names or state-folder copies changed
45
+ * under an older phase, whose recovery goes the other way.
46
+ */
47
+ export declare function syncObservedRestoreRecord(dataset: RelocationDataset, ctx: StateLayoutTransitionContext): Promise<void>;
48
+ /**
49
+ * R5, under the restore's locks (or a start's layout and holder locks): remove
50
+ * each state-folder copy that still holds the bytes that were staged, then
51
+ * record the cleanup as done. A copy that changed, can't be read, or can't be
52
+ * removed is kept and listed: no process was admitted to the state folder
53
+ * since R1, so only a person can have changed it, and the legacy copy is
54
+ * already durable. Only the record write itself can fail this step.
55
+ */
56
+ export declare function finishRestoreRetirement(record: StateLayoutRecord, dataset: RelocationDataset, ctx: StateLayoutTransitionContext): Promise<StateLayoutRecord>;
57
+ /**
58
+ * Undo a restore that stopped before its boundary (phase `restoring`), under
59
+ * the layout lock and both holder locks: unlink this attempt's staging files,
60
+ * flush the config folder, then publish the move's record again as
61
+ * `committed`, as it was before the restore began. Nothing at a legacy name
62
+ * was touched before the boundary and the barriers stayed up, so no version
63
+ * served from the config folder, and the state-folder files were only read.
64
+ */
65
+ export declare function discardAbandonedRestore(record: StateLayoutRecord, dataset: RelocationDataset, ctx: StateLayoutTransitionContext): Promise<StateLayoutRecord>;
66
+ /**
67
+ * R3 to R5 of a restore whose staging is durable (phase `restore-staged`):
68
+ * classify every slot afresh, refuse before writing anything if one is in a
69
+ * state no supported writer produces, then put the data files in place, flush,
70
+ * remove the barriers, flush, publish `restored` and retire the state-folder
71
+ * copies. Running it again after a crash at any point converges.
72
+ */
73
+ export declare function finishStagedRestore(record: StateLayoutRecord, dataset: RelocationDataset, ctx: StateLayoutTransitionContext): Promise<StateLayoutRecord>;
74
+ /**
75
+ * Restore OpenCode's dataset from the state folder to the legacy layout in
76
+ * its config folder, or finish a restore that stopped. Run it only with every
77
+ * host stopped, behind setup's process fence; it also refuses while any
78
+ * process is registered beside either copy. Refuses with
79
+ * `StateLayoutRefusalError` (code `restore-refused` with a message naming
80
+ * what to fix, or a marker problem); resolves `not-moved` when there is
81
+ * nothing to restore.
82
+ */
83
+ export declare function restoreLegacyLayout(options?: RestoreLegacyOptions): Promise<RestoreLegacyOutcome>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cortexkit/anthropic-auth-core",
3
- "version": "2.4.0",
3
+ "version": "2.4.2",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -33,9 +33,9 @@
33
33
  "@cortexkit/common-auth": "0.28.0"
34
34
  },
35
35
  "dependencies": {
36
- "@cortexkit/claustrum-client": "0.12.0",
36
+ "@cortexkit/claustrum-client": "0.13.0",
37
37
  "@cortexkit/private-files": "0.2.1",
38
- "@cortexkit/subc-client": "^0.22.0",
38
+ "@cortexkit/subc-client": "^0.22.10",
39
39
  "jsonc-parser": "^3.3.1",
40
40
  "xxhash-wasm": "^1.1.0"
41
41
  }