@quantiya/codevibe-core 2.0.7 → 2.0.9
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/dist/appsync/__tests__/dispose-watcher-socket.test.d.ts +1 -0
- package/dist/appsync/__tests__/refresh-open-prompt-ttl-chunk.test.d.ts +1 -0
- package/dist/appsync/appsync-client.d.ts +232 -30
- package/dist/appsync/queries.d.ts +2 -0
- package/dist/auth/__tests__/keychain-cli-nudge.test.d.ts +1 -0
- package/dist/auth/__tests__/keychain-cli.test.d.ts +1 -0
- package/dist/auth/keychain-cli.d.ts +14 -0
- package/dist/companion-mode/index.d.ts +14 -0
- package/dist/credential-broker/oauth-account-lock.d.ts +47 -1
- package/dist/crypto/__tests__/open-prompt-id.test.d.ts +1 -0
- package/dist/crypto/index.d.ts +1 -0
- package/dist/crypto/open-prompt-id.d.ts +36 -0
- package/dist/e1/__tests__/prompt-raise.test.d.ts +1 -0
- package/dist/e1/index.d.ts +2 -0
- package/dist/e1/prompt-raise.d.ts +65 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +4652 -252
- package/dist/keychain/__tests__/keychain-manager.test.d.ts +1 -0
- package/dist/keychain/__tests__/keychain-migrate.test.d.ts +1 -0
- package/dist/keychain/__tests__/migrate-nudge.test.d.ts +1 -0
- package/dist/keychain/keychain-backend.d.ts +105 -0
- package/dist/keychain/keychain-manager.d.ts +17 -3
- package/dist/keychain/keychain-migrate.d.ts +63 -0
- package/dist/keychain/migrate-nudge.d.ts +38 -0
- package/dist/local-executor/__tests__/anchored-fs.test.d.ts +1 -0
- package/dist/local-executor/__tests__/bounded-directory.test.d.ts +1 -0
- package/dist/local-executor/__tests__/bounded-output.test.d.ts +1 -0
- package/dist/local-executor/__tests__/durable-directory.test.d.ts +1 -0
- package/dist/local-executor/__tests__/hosted-class-a-sink.test.d.ts +1 -0
- package/dist/local-executor/__tests__/process-identity.test.d.ts +1 -0
- package/dist/local-executor/__tests__/process-tree.test.d.ts +1 -0
- package/dist/local-executor/__tests__/protocol-backlog-wiring.test.d.ts +1 -0
- package/dist/local-executor/__tests__/protocol-backlog.test.d.ts +1 -0
- package/dist/local-executor/__tests__/publication-authority-capability.test.d.ts +1 -0
- package/dist/local-executor/__tests__/snapshot-merge.test.d.ts +1 -0
- package/dist/local-executor/__tests__/workspace-authority-fixture.d.ts +3 -0
- package/dist/local-executor/__tests__/workspace-authority.test.d.ts +1 -0
- package/dist/local-executor/__tests__/workspace-cleanup-audit.test.d.ts +1 -0
- package/dist/local-executor/__tests__/workspace-materializer.test.d.ts +1 -0
- package/dist/local-executor/agentic-disposable-repo.d.ts +23 -6
- package/dist/local-executor/agentic-resolve.d.ts +16 -0
- package/dist/local-executor/anchored-fs.d.ts +250 -0
- package/dist/local-executor/bounded-directory.d.ts +78 -0
- package/dist/local-executor/bounded-helper.d.ts +22 -0
- package/dist/local-executor/bounded-output.d.ts +11 -0
- package/dist/local-executor/class-a-emit.d.ts +13 -1
- package/dist/local-executor/durable-directory.d.ts +25 -0
- package/dist/local-executor/durable-store.d.ts +217 -25
- package/dist/local-executor/hosted-class-a-sink.d.ts +27 -0
- package/dist/local-executor/index.d.ts +3 -2
- package/dist/local-executor/local-executor-impl.d.ts +159 -69
- package/dist/local-executor/process-identity.d.ts +46 -0
- package/dist/local-executor/process-tree.d.ts +123 -0
- package/dist/local-executor/protocol-backlog.d.ts +62 -0
- package/dist/local-executor/revert-reconcile.d.ts +4 -0
- package/dist/local-executor/revert-runner.d.ts +14 -1
- package/dist/local-executor/snapshot-merge.d.ts +124 -0
- package/dist/local-executor/task-group-types.d.ts +7 -6
- package/dist/local-executor/team-execution-flag.d.ts +2 -2
- package/dist/local-executor/track-registry.d.ts +2 -0
- package/dist/local-executor/types.d.ts +33 -2
- package/dist/local-executor/verification-gates/build.d.ts +5 -0
- package/dist/local-executor/verification-gates/deploy-preflight.d.ts +2 -0
- package/dist/local-executor/verification-gates/diff-sanity.d.ts +12 -0
- package/dist/local-executor/verification-gates/hostile-grep.d.ts +2 -0
- package/dist/local-executor/verification-gates/lint.d.ts +2 -0
- package/dist/local-executor/verification-gates/shell-runner.d.ts +5 -0
- package/dist/local-executor/verification-gates/source-traceability.d.ts +2 -0
- package/dist/local-executor/verification-gates/tests.d.ts +3 -0
- package/dist/local-executor/verification-gates/typecheck.d.ts +3 -0
- package/dist/local-executor/verification-runner.d.ts +6 -0
- package/dist/local-executor/workspace-authority.d.ts +45 -0
- package/dist/local-executor/workspace-cleanup-audit.d.ts +3 -0
- package/dist/local-executor/workspace-materializer.d.ts +48 -0
- package/dist/local-executor/workspace-shadow.d.ts +380 -85
- package/dist/orchestration-shell/__tests__/advisory-attachment-journal.test.d.ts +1 -0
- package/dist/orchestration-shell/__tests__/direct-file-delete.test.d.ts +1 -0
- package/dist/orchestration-shell/__tests__/flag-command.test.d.ts +1 -0
- package/dist/orchestration-shell/__tests__/halt-message-wire-content.test.d.ts +1 -0
- package/dist/orchestration-shell/__tests__/planner-offer-e1.test.d.ts +1 -0
- package/dist/orchestration-shell/__tests__/workspace-shutdown.test.d.ts +1 -0
- package/dist/orchestration-shell/advisory-attachment-journal.d.ts +165 -0
- package/dist/orchestration-shell/attachments.d.ts +47 -58
- package/dist/orchestration-shell/audit-browser.d.ts +54 -1
- package/dist/orchestration-shell/bounded-shutdown.d.ts +6 -0
- package/dist/orchestration-shell/cli.d.ts +10 -0
- package/dist/orchestration-shell/cli.js +38355 -27300
- package/dist/orchestration-shell/context-items.d.ts +3 -0
- package/dist/orchestration-shell/declared-test-runner.d.ts +21 -2
- package/dist/orchestration-shell/direct-file-delete.d.ts +22 -0
- package/dist/orchestration-shell/emit-shell-event.d.ts +7 -0
- package/dist/orchestration-shell/explicit-tty-exit.d.ts +1 -2
- package/dist/orchestration-shell/flag-command.d.ts +81 -0
- package/dist/orchestration-shell/gate-decision-submit.d.ts +11 -0
- package/dist/orchestration-shell/gate-prompts.d.ts +116 -0
- package/dist/orchestration-shell/index.d.ts +34 -6
- package/dist/orchestration-shell/planner-offer-e1.d.ts +97 -0
- package/dist/orchestration-shell/quorum-loop.d.ts +312 -61
- package/dist/orchestration-shell/reducer.d.ts +9 -0
- package/dist/orchestration-shell/task-progress.d.ts +18 -4
- package/dist/orchestration-shell/team-decompose.d.ts +10 -10
- package/dist/orchestration-shell/types.d.ts +90 -2
- package/dist/orchestration-shell/workspace-shutdown.d.ts +47 -0
- package/dist/reviewer/output-parser.d.ts +19 -0
- package/dist/reviewer/providers/codex.d.ts +5 -3
- package/dist/reviewer/token-usage.d.ts +41 -12
- package/dist/reviewer/types.d.ts +6 -0
- package/dist/reviewer/verdict-classifier.d.ts +1 -0
- package/dist/session/session-rekey.d.ts +4 -3
- package/dist/substrate/__tests__/command-runner.test.d.ts +1 -0
- package/dist/substrate/command-runner.d.ts +2 -0
- package/dist/substrate/docker.d.ts +7 -32
- package/dist/substrate/index.d.ts +0 -1
- package/dist/substrate/sandbox-exec.d.ts +0 -13
- package/dist/substrate/types.d.ts +15 -2
- package/dist/substrate-launch/__tests__/reviewer-credential-preflight.test.d.ts +1 -0
- package/dist/substrate-launch/engage-substrate.d.ts +3 -0
- package/dist/substrate-launch/index.d.ts +2 -0
- package/dist/substrate-launch/reviewer-credential-preflight.d.ts +50 -0
- package/dist/test-publication-authority-setup.d.ts +1 -0
- package/dist/tool-activity/replay-cursor.d.ts +42 -8
- package/dist/track-presentation.d.ts +2 -0
- package/dist/track-presentation.test.d.ts +1 -0
- package/dist/types/encryption.d.ts +5 -0
- package/dist/types/events.d.ts +5 -0
- package/package.json +3 -3
- package/dist/substrate/git-proxy.d.ts +0 -35
- /package/dist/{substrate/__tests__/git-proxy.test.d.ts → appsync/__tests__/appsync-client-flag-locator.test.d.ts} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
type KeytarApi = {
|
|
2
|
+
getPassword(service: string, account: string): Promise<string | null>;
|
|
3
|
+
setPassword(service: string, account: string, password: string): Promise<void>;
|
|
4
|
+
deletePassword(service: string, account: string): Promise<boolean>;
|
|
5
|
+
};
|
|
1
6
|
type BackendKind = 'keytar' | 'file';
|
|
2
7
|
/**
|
|
3
8
|
* Is the OS-native keyring service STRUCTURALLY UNREACHABLE in THIS process?
|
|
@@ -28,6 +33,10 @@ type BackendKind = 'keytar' | 'file';
|
|
|
28
33
|
* fallback at all.
|
|
29
34
|
*/
|
|
30
35
|
export declare function osKeyringStructurallyAbsent(): boolean;
|
|
36
|
+
/** Best-effort: remember that the OS keyring worked here (Linux only — only the
|
|
37
|
+
* Linux headless path ever reads it). Never throws. Exported so #642's
|
|
38
|
+
* migration can record it after moving credentials onto the keyring. */
|
|
39
|
+
export declare function markKeytarUsed(): void;
|
|
31
40
|
/**
|
|
32
41
|
* Do durable file-backend credentials already exist on this machine? True when
|
|
33
42
|
* ~/.codevibe holds any `<service>.json` credential file (written by a prior
|
|
@@ -65,6 +74,23 @@ export declare function wasAutoFileFallback(): boolean;
|
|
|
65
74
|
* outcome) for tests and diagnostics without triggering initialization.
|
|
66
75
|
*/
|
|
67
76
|
export declare function peekActiveBackendKind(): BackendKind | null;
|
|
77
|
+
/**
|
|
78
|
+
* Run `fn` while holding the per-service FILE-MUTATION lock — the single
|
|
79
|
+
* cross-process gate that serializes EVERY mutation of `~/.codevibe/<service>.json`
|
|
80
|
+
* (setPassword / deletePassword) against a concurrent `codevibe keychain migrate`.
|
|
81
|
+
* The migration CLI holds this SAME lock across the whole migration, so the file
|
|
82
|
+
* store cannot change mid-migration (the lock IS the freeze — it lets the migration
|
|
83
|
+
* do a single stable read instead of freeze-then-re-migrate).
|
|
84
|
+
*
|
|
85
|
+
* FAIL CLOSED: if the lock cannot be acquired (contention timeout / unwritable lock
|
|
86
|
+
* root) we THROW `KeychainBackendUnavailableError` and NEVER run `fn` unlocked — an
|
|
87
|
+
* unserialized read-modify-write could clobber a concurrent migration's
|
|
88
|
+
* tombstone/retire and re-fork the identity (the fork-incident harm). Reuses the
|
|
89
|
+
* portable, native-addon-free mkdir-lock from the credential broker (§626 §5 /
|
|
90
|
+
* AMEND-1) under a DISTINCT lockdir (`keychain-file-<service>`) from the
|
|
91
|
+
* per-provider vendor-key OAuth locks.
|
|
92
|
+
*/
|
|
93
|
+
export declare function withFileStoreLock<T>(service: string, fn: () => Promise<T> | T): Promise<T>;
|
|
68
94
|
/**
|
|
69
95
|
* The active backend kind (`'keytar'` = OS-native keyring, `'file'` = the
|
|
70
96
|
* plaintext `~/.codevibe/<service>.json` fallback). Initializes the backend if
|
|
@@ -79,7 +105,86 @@ export declare function peekActiveBackendKind(): BackendKind | null;
|
|
|
79
105
|
* backend could be initialized (keytar failed to load AND no file opt-in).
|
|
80
106
|
*/
|
|
81
107
|
export declare function getActiveBackendKind(): BackendKind;
|
|
108
|
+
/**
|
|
109
|
+
* LOCK-FREE read (F5). Identical to `getPassword` — a read never needs the file lock
|
|
110
|
+
* (atomic writes make a torn read impossible) — but named explicitly so a transaction
|
|
111
|
+
* already holding `withFileStoreLock` (device get-or-create) can read without any risk
|
|
112
|
+
* of re-entering the lock.
|
|
113
|
+
*/
|
|
114
|
+
export declare function getPasswordUnlocked(service: string, account: string): Promise<string | null>;
|
|
82
115
|
export declare function getPassword(service: string, account: string): Promise<string | null>;
|
|
116
|
+
/**
|
|
117
|
+
* LOCK-FREE write (F5) — the file-mutation body of `setPassword` WITHOUT acquiring
|
|
118
|
+
* `withFileStoreLock`. It STILL runs the fail-closed tombstone guard + unreadable-store
|
|
119
|
+
* guard + atomic write. For use ONLY by a caller that already holds the file lock (the
|
|
120
|
+
* device get-or-create transaction); acquiring the lock again would self-deadlock.
|
|
121
|
+
*/
|
|
122
|
+
export declare function setPasswordUnlocked(service: string, account: string, password: string): Promise<void>;
|
|
83
123
|
export declare function setPassword(service: string, account: string, password: string): Promise<void>;
|
|
84
124
|
export declare function deletePassword(service: string, account: string): Promise<boolean>;
|
|
125
|
+
/** Absolute path of the file-backend credential store for `service`. */
|
|
126
|
+
export declare function fileStorePathForService(service: string): string;
|
|
127
|
+
/**
|
|
128
|
+
* A DISCRIMINATED read of the file store — migration must distinguish "no file"
|
|
129
|
+
* (nothing to do) from "exists but I can't read it right now" (EACCES / transient
|
|
130
|
+
* I/O / mid-write) from "readable but corrupt JSON" from "readable credential map".
|
|
131
|
+
* Retiring a file we merely FAILED to read would lose a valid identity, so the
|
|
132
|
+
* migration never retires on `unreadable`. `raw` is the exact bytes, for a
|
|
133
|
+
* byte-exact concurrency compare (parsed-object equality would miss whitespace /
|
|
134
|
+
* duplicate-key rewrites).
|
|
135
|
+
*/
|
|
136
|
+
export type FileStoreRead = {
|
|
137
|
+
kind: 'absent';
|
|
138
|
+
} | {
|
|
139
|
+
kind: 'unreadable';
|
|
140
|
+
error: string;
|
|
141
|
+
} | {
|
|
142
|
+
kind: 'corrupt';
|
|
143
|
+
raw: string;
|
|
144
|
+
} | {
|
|
145
|
+
kind: 'store';
|
|
146
|
+
data: Record<string, string>;
|
|
147
|
+
raw: string;
|
|
148
|
+
};
|
|
149
|
+
export declare function readFileStoreState(service: string): FileStoreRead;
|
|
150
|
+
/**
|
|
151
|
+
* Rename the file store out of the way (`…/<service>.json.migrated-backup-<ts>`)
|
|
152
|
+
* so the NEXT process selects the OS keyring (fileStoreHasCredentials() → false).
|
|
153
|
+
* Reversible — the backup is kept, 0600. Returns the backup path, or null if
|
|
154
|
+
* there was no file store to retire. Only ever called AFTER the migration has
|
|
155
|
+
* written + verified every credential into the keyring (never loses the identity).
|
|
156
|
+
*
|
|
157
|
+
* R4-4: NO fail-open `existsSync` pre-probe (an EACCES there returned null → the CLI
|
|
158
|
+
* would exit 0 while the plaintext file remained). Attempt the atomic rename directly:
|
|
159
|
+
* an ENOENT/ENOTDIR means there was genuinely nothing to retire → null; ANY other
|
|
160
|
+
* error (EACCES/EIO/…) PROPAGATES so the caller surfaces the failure (non-zero exit /
|
|
161
|
+
* verify_failed) instead of silently leaving the file behind.
|
|
162
|
+
*/
|
|
163
|
+
export declare function retireFileStore(service: string): string | null;
|
|
164
|
+
/**
|
|
165
|
+
* Load the keytar native module directly (bypassing the pinned backend), or null
|
|
166
|
+
* if it cannot load. Used ONLY by the migration command to write into the OS
|
|
167
|
+
* keyring from a process otherwise committed to the file backend. Same indirect
|
|
168
|
+
* `eval('require')` as initBackends so esbuild does not resolve keytar statically.
|
|
169
|
+
*/
|
|
170
|
+
export declare function loadKeytarModule(): KeytarApi | null;
|
|
171
|
+
/** True iff this machine has migrated `service`'s credentials onto the OS keyring.
|
|
172
|
+
* Routed through `pathPresence` (F1); a probe 'error' reads as false here (the
|
|
173
|
+
* fail-closed callers use `migratedTombstoneStatus`, not this boolean). */
|
|
174
|
+
export declare function migratedTombstoneExists(service: string): boolean;
|
|
175
|
+
/**
|
|
176
|
+
* Tri-state probe of the per-service migration tombstone via `pathPresence` (F1) —
|
|
177
|
+
* `fs.statSync`, NOT `fs.existsSync` (which fails OPEN on EACCES/EIO). DISTINGUISHES a
|
|
178
|
+
* genuine probe error from a clean absence so `initBackends` / the in-lock mutation
|
|
179
|
+
* guard / the CLI resume gate all fail CLOSED on 'error' rather than treating an
|
|
180
|
+
* unreadable ~/.codevibe as "not migrated" (which could re-fork the device identity).
|
|
181
|
+
*/
|
|
182
|
+
export declare function migratedTombstoneStatus(service: string): 'present' | 'absent' | 'error';
|
|
183
|
+
/**
|
|
184
|
+
* Atomically create the per-service migration tombstone (temp-write + fsync +
|
|
185
|
+
* rename + best-effort dir fsync, 0600). Idempotent. Throws on failure — the
|
|
186
|
+
* migration MUST know the tombstone is durable before it retires the file (the
|
|
187
|
+
* tombstone is what makes stragglers fail closed).
|
|
188
|
+
*/
|
|
189
|
+
export declare function writeMigratedTombstone(service: string): void;
|
|
85
190
|
export {};
|
|
@@ -5,6 +5,7 @@ import { DeviceIdentity, TokenData, EncryptedSessionKey } from '../types';
|
|
|
5
5
|
export declare class KeychainError extends Error {
|
|
6
6
|
constructor(message: string);
|
|
7
7
|
}
|
|
8
|
+
export declare const DEVICE_IDENTITY_ACCOUNT = "device-identity";
|
|
8
9
|
/**
|
|
9
10
|
* Manages device identity and OAuth tokens using native keychain
|
|
10
11
|
*/
|
|
@@ -36,7 +37,18 @@ export declare class KeychainManager {
|
|
|
36
37
|
*/
|
|
37
38
|
setDeviceIdentity(identity: DeviceIdentity): Promise<void>;
|
|
38
39
|
/**
|
|
39
|
-
* Get or create device identity
|
|
40
|
+
* Get or create device identity — ATOMIC across processes (#642 F5).
|
|
41
|
+
*
|
|
42
|
+
* A plain read-then-create is a cross-process race: two fresh processes both read
|
|
43
|
+
* "no identity" outside any lock, generate distinct identities A and B, then each
|
|
44
|
+
* writes under its own lock — last-writer-wins, and the loser keeps using an
|
|
45
|
+
* identity that is NOT the one persisted (forking E2E state). The WHOLE
|
|
46
|
+
* read→validate→generate→write transaction is therefore serialized under the
|
|
47
|
+
* per-service file lock (`withFileStoreLock`), using the LOCK-FREE backend helpers
|
|
48
|
+
* (`getPasswordUnlocked`/`setPasswordUnlocked`) so the transaction never re-enters
|
|
49
|
+
* the lock and self-deadlocks. The lock serializes regardless of backend (keytar or
|
|
50
|
+
* file). The lock is taken ONLY on the slow path (no cached/persisted identity yet),
|
|
51
|
+
* so steady-state reads stay lock-free.
|
|
40
52
|
*/
|
|
41
53
|
getOrCreateDeviceIdentity(): Promise<DeviceIdentity>;
|
|
42
54
|
/**
|
|
@@ -103,8 +115,10 @@ export declare class KeychainManager {
|
|
|
103
115
|
*
|
|
104
116
|
* When `encryptedKeys` are NOT provided (cache-only callers — e.g. the
|
|
105
117
|
* orchestration QuorumLoop's `getSessionKey(sessionId)`), the cached key is
|
|
106
|
-
* returned
|
|
107
|
-
*
|
|
118
|
+
* returned as before. An explicitly supplied empty authoritative envelope is
|
|
119
|
+
* different: a generation-bearing row is non-decryptable and may never fall
|
|
120
|
+
* through to a stale cache. Empty-envelope fallback is permitted only for a
|
|
121
|
+
* genuinely generation-absent legacy row/cache.
|
|
108
122
|
*/
|
|
109
123
|
getSessionKey(sessionId: string, encryptedKeys?: EncryptedSessionKey[], sessionKeyGen?: string | null): Promise<string | null>;
|
|
110
124
|
/**
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { DEVICE_IDENTITY_ACCOUNT } from './keychain-manager';
|
|
2
|
+
import type { FileStoreRead } from './keychain-backend';
|
|
3
|
+
export { DEVICE_IDENTITY_ACCOUNT };
|
|
4
|
+
export type { FileStoreRead };
|
|
5
|
+
export interface MigrateIO {
|
|
6
|
+
print(line: string): void;
|
|
7
|
+
printErr(line: string): void;
|
|
8
|
+
}
|
|
9
|
+
export interface MigrateDeps {
|
|
10
|
+
/** Discriminated read of the file store (absent / unreadable / corrupt / store+raw). */
|
|
11
|
+
readFileState(): FileStoreRead;
|
|
12
|
+
/** Read an account from the OS keyring directly. MAY THROW (locked/denied keyring). */
|
|
13
|
+
keytarGet(account: string): Promise<string | null>;
|
|
14
|
+
/** Write an account to the OS keyring directly. */
|
|
15
|
+
keytarSet(account: string, password: string): Promise<void>;
|
|
16
|
+
/** Atomically create the durable "migrated to keyring" tombstone (throws on failure). */
|
|
17
|
+
writeTombstone(): void;
|
|
18
|
+
/** Retire the file store → backup path (or null if nothing to retire). */
|
|
19
|
+
retireFileStore(): string | null;
|
|
20
|
+
/** Record the Linux `.keyring-used` anti-fork marker (no-op off Linux). */
|
|
21
|
+
markKeyringChosen(): void;
|
|
22
|
+
io: MigrateIO;
|
|
23
|
+
}
|
|
24
|
+
export interface MigrateOptions {
|
|
25
|
+
discardFile: boolean;
|
|
26
|
+
force: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Crash-recovery RESUME (the CLI sets this when the migration tombstone already
|
|
29
|
+
* existed at entry). Post-tombstone the KEYRING is authoritative and a keyring-ABSENT
|
|
30
|
+
* account is an authoritative DELETION, NOT an incomplete copy — so a resume must
|
|
31
|
+
* copy ZERO accounts from the stale file (F3). It only verifies the keyring still
|
|
32
|
+
* holds a VALID identity and, if so, retires the leftover file (copying nothing); if
|
|
33
|
+
* the keyring lost its identity it REFUSES (never re-runs the copy/replace fork path,
|
|
34
|
+
* never strands the file). First-run (false) keeps the file-authoritative copy.
|
|
35
|
+
*/
|
|
36
|
+
resume: boolean;
|
|
37
|
+
}
|
|
38
|
+
export type MigrateOutcome = 'nothing_to_do' | 'file_unreadable' | 'keyring_unreachable' | 'retired_empty' | 'discarded' | 'discard_refused' | 'already_present' | 'migrated' | 'replaced' | 'fork_refused' | 'invalid_source_identity' | 'resume_keyring_lost' | 'verify_failed';
|
|
39
|
+
export interface MigrateResult {
|
|
40
|
+
outcome: MigrateOutcome;
|
|
41
|
+
backupPath?: string | null;
|
|
42
|
+
accountsMigrated?: string[];
|
|
43
|
+
fileDeviceId?: string | null;
|
|
44
|
+
keychainDeviceId?: string | null;
|
|
45
|
+
}
|
|
46
|
+
interface ParsedIdentity {
|
|
47
|
+
deviceId: string;
|
|
48
|
+
publicKey: string;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Parse a stored device-identity blob into a VALIDATED identity: a canonical-UUID
|
|
52
|
+
* `deviceId`, a non-empty `publicKey`, AND a non-empty `privateKey`. Anything else
|
|
53
|
+
* (missing fields, non-UUID deviceId, keyless, unparseable) → null = "not a
|
|
54
|
+
* recognized identity". Returning null here makes the fork guard treat a
|
|
55
|
+
* parseable-but-invalid blob as a CONFLICT, not a match — so a file identity missing
|
|
56
|
+
* its private key can never `--force`-replace a good keyring identity with a keyless
|
|
57
|
+
* one. (Full ECDH derive-validation is deliberately NOT done: identities come from
|
|
58
|
+
* our own P-256 keygen, and the NO-CLOBBER invariant already protects the keyring's
|
|
59
|
+
* copy in the sameIdentity path — a non-empty privateKey field is a sufficient
|
|
60
|
+
* structural check.)
|
|
61
|
+
*/
|
|
62
|
+
export declare function parseIdentity(raw: string | null | undefined): ParsedIdentity | null;
|
|
63
|
+
export declare function migrateKeychain(deps: MigrateDeps, opts: MigrateOptions): Promise<MigrateResult>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export interface FileStoreSnapshot {
|
|
2
|
+
kind: 'absent' | 'unreadable' | 'corrupt' | 'store';
|
|
3
|
+
/** Account count when kind === 'store'; 0 otherwise. */
|
|
4
|
+
accountCount: number;
|
|
5
|
+
/** True when kind === 'store' and the device-identity account parses as a valid identity. */
|
|
6
|
+
identityValid: boolean;
|
|
7
|
+
/**
|
|
8
|
+
* True when any account NAME or VALUE is not a non-empty string (Stage-2
|
|
9
|
+
* Codex r1+r2): migrate copies EVERY account via keytar.setPassword, whose
|
|
10
|
+
* checkRequired rejects empty accounts and empty passwords → verify_failed
|
|
11
|
+
* refusal (possibly after earlier accounts were written). Non-secret check —
|
|
12
|
+
* length/type only, never content.
|
|
13
|
+
*/
|
|
14
|
+
hasEmptyAccountEntry: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface NudgeDeps {
|
|
17
|
+
/** CODEVIBE_ALLOW_FILE_KEYCHAIN === '1' — the user explicitly chose file storage. */
|
|
18
|
+
fileKeychainOptIn(): boolean;
|
|
19
|
+
/** Tri-state migration-tombstone probe (statSync-based). */
|
|
20
|
+
tombstoneStatus(): 'present' | 'absent' | 'error';
|
|
21
|
+
/** ONE discriminated read of the file store for the active service. */
|
|
22
|
+
fileStoreSnapshot(): FileStoreSnapshot;
|
|
23
|
+
/** Platform-structural "no keyring here" (e.g. headless Linux, no keytar module). */
|
|
24
|
+
keyringStructurallyAbsent(): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* One live keytar read of the keyring's device-identity account. Resolves to
|
|
27
|
+
* the stored value or null; MAY THROW / reject (locked or denied keyring).
|
|
28
|
+
* The caller compares the result against null and nothing else (invariant 4).
|
|
29
|
+
*/
|
|
30
|
+
keytarProbe(): Promise<unknown>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* True when `login`/`status` should print the keychain-migration suggestion.
|
|
34
|
+
* Never throws.
|
|
35
|
+
*/
|
|
36
|
+
export declare function shouldSuggestKeychainMigration(deps: NudgeDeps): Promise<boolean>;
|
|
37
|
+
/** The advisory text, single-sourced so login/status/tests render identically. */
|
|
38
|
+
export declare const MIGRATION_NUDGE_LINES: readonly string[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type WorkspaceRootAuthority } from './workspace-authority';
|
|
1
2
|
/** A structured failure in disposable-repo materialization or fd-safe import. */
|
|
2
3
|
export declare class DisposableRepoError extends Error {
|
|
3
4
|
readonly reason: 'init' | 'ls-tree' | 'pack-objects' | 'dangerous-attributes' | 'read-tree' | 'checkout';
|
|
@@ -5,13 +6,27 @@ export declare class DisposableRepoError extends Error {
|
|
|
5
6
|
}
|
|
6
7
|
/** A structured rejection from {@link fdSafeReadFile} (fail-closed → human-gate). */
|
|
7
8
|
export declare class FdSafeImportError extends Error {
|
|
8
|
-
readonly reason: 'empty' | 'nul' | 'absolute' | 'traversal' | 'missing' | 'symlink-component' | 'not-regular' | 'hardlink';
|
|
9
|
-
constructor(reason: 'empty' | 'nul' | 'absolute' | 'traversal' | 'missing' | 'symlink-component' | 'not-regular' | 'hardlink', message: string);
|
|
9
|
+
readonly reason: 'empty' | 'nul' | 'absolute' | 'traversal' | 'missing' | 'symlink-component' | 'not-regular' | 'hardlink' | 'root-incarnation';
|
|
10
|
+
constructor(reason: 'empty' | 'nul' | 'absolute' | 'traversal' | 'missing' | 'symlink-component' | 'not-regular' | 'hardlink' | 'root-incarnation', message: string);
|
|
11
|
+
}
|
|
12
|
+
/** Exact post-rename authority returned by {@link stageDirForRead}. */
|
|
13
|
+
export interface StagedDirectory {
|
|
14
|
+
path: string;
|
|
15
|
+
identity: WorkspaceRootAuthority;
|
|
16
|
+
}
|
|
17
|
+
/** Staging failed after rename; retains the exact authority needed for cleanup. */
|
|
18
|
+
export declare class StageDirForReadError extends Error {
|
|
19
|
+
readonly retained?: StagedDirectory | undefined;
|
|
20
|
+
constructor(message: string, retained?: StagedDirectory | undefined);
|
|
21
|
+
get retainedPath(): string | undefined;
|
|
22
|
+
get retainedIdentity(): WorkspaceRootAuthority | undefined;
|
|
10
23
|
}
|
|
11
24
|
/** A materialized disposable repo + its cleanup. */
|
|
12
25
|
export interface DisposableRepo {
|
|
13
26
|
/** The working-tree root the agent resolves in (its own `.git` lives under it). */
|
|
14
27
|
repoDir: string;
|
|
28
|
+
/** Exact directory created by CodeVibe; cleanup never recaptures a replacement. */
|
|
29
|
+
repoAuthority: WorkspaceRootAuthority;
|
|
15
30
|
/** Remove the disposable repo (try/finally — even on timeout/crash). */
|
|
16
31
|
cleanup(): Promise<void>;
|
|
17
32
|
}
|
|
@@ -38,10 +53,12 @@ export declare function materializeDisposableRepo(opts: MaterializeOpts): Promis
|
|
|
38
53
|
* Atomically rename `dir` to a fresh RANDOM sibling path before reading it back —
|
|
39
54
|
* the exited agent / any disowned background process does NOT know the new path,
|
|
40
55
|
* so it cannot pre-place symlinks to win a TOCTOU race. The caller reads from the
|
|
41
|
-
* returned staged path (and is responsible for
|
|
42
|
-
* rename → atomic.
|
|
56
|
+
* returned staged path plus relocated cleanup identity (and is responsible for
|
|
57
|
+
* removing that exact authority). Same-filesystem rename → atomic.
|
|
43
58
|
*/
|
|
44
|
-
export declare function stageDirForRead(dir: string
|
|
59
|
+
export declare function stageDirForRead(dir: string, expectedAuthority: WorkspaceRootAuthority, seams?: {
|
|
60
|
+
afterRename?: (staged: string) => Promise<void>;
|
|
61
|
+
}): Promise<StagedDirectory>;
|
|
45
62
|
/**
|
|
46
63
|
* fd-safe read of ONE conflicted file at `relPath` under `rootDir` (CP-14 WB2 §2.1).
|
|
47
64
|
* `rootDir` SHOULD be a {@link stageDirForRead} staging path. Closes leaf+parent
|
|
@@ -49,4 +66,4 @@ export declare function stageDirForRead(dir: string): Promise<string>;
|
|
|
49
66
|
* dir/FIFO/device (`isFile`), and the read race (read-from-the-verified-fd). Any
|
|
50
67
|
* violation throws {@link FdSafeImportError} → the caller fails closed (human-gate).
|
|
51
68
|
*/
|
|
52
|
-
export declare function fdSafeReadFile(
|
|
69
|
+
export declare function fdSafeReadFile(staged: StagedDirectory, relPath: string): Promise<Buffer>;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { stageDirForRead, type StagedDirectory } from './agentic-disposable-repo';
|
|
2
|
+
import { type WorkspaceRootAuthority } from './workspace-authority';
|
|
1
3
|
/**
|
|
2
4
|
* CP-14 WB2 §2.1 path C (dispatch-time fail-closed, D3-A) — the live
|
|
3
5
|
* {@link DispatchImplementor} closure THROWS this when the per-resolve confinement
|
|
@@ -14,6 +16,8 @@ export declare class AgenticDispatchUnconfinedError extends Error {
|
|
|
14
16
|
export type DispatchImplementor = (args: {
|
|
15
17
|
/** The disposable repo working-tree root (its own `.git`). */
|
|
16
18
|
repoDir: string;
|
|
19
|
+
/** Original full incarnation authority; dispatch must bind launch to it. */
|
|
20
|
+
repoAuthority: WorkspaceRootAuthority;
|
|
17
21
|
/** The class-2 conflicted files the agent must resolve (markers removed). */
|
|
18
22
|
conflictedPaths: readonly string[];
|
|
19
23
|
/** The implementor agent kind + its per-spawn task identity. */
|
|
@@ -30,10 +34,22 @@ export interface AgenticResolveArgs {
|
|
|
30
34
|
implementorTaskId: string;
|
|
31
35
|
/** Under the task's writable workspace area. */
|
|
32
36
|
parentDir: string;
|
|
37
|
+
/**
|
|
38
|
+
* The caller created `parentDir` solely for this resolve and transfers
|
|
39
|
+
* cleanup ownership here. Direct/library callers may omit this when the
|
|
40
|
+
* parent contains other state.
|
|
41
|
+
*/
|
|
42
|
+
cleanupParentDir?: boolean;
|
|
33
43
|
/** scan-before-checkout (the live caller passes `scanIntegrationTreeAttributes`). */
|
|
34
44
|
scanTree: (repoDir: string, tree: string) => Promise<string[]>;
|
|
35
45
|
/** Real implementor spawn (LE seam); tests stub it. */
|
|
36
46
|
dispatchImplementor: DispatchImplementor;
|
|
47
|
+
/** @internal deterministic post-stage cleanup regression seam. */
|
|
48
|
+
stageDirectory?: typeof stageDirForRead;
|
|
49
|
+
/** @internal deterministic pathname-replacement seam before dispatch. */
|
|
50
|
+
beforeDispatch?: (repoDir: string, authority: WorkspaceRootAuthority) => Promise<void>;
|
|
51
|
+
/** @internal deterministic pathname-replacement seam after staging. */
|
|
52
|
+
beforeStagedRead?: (staged: StagedDirectory) => Promise<void>;
|
|
37
53
|
}
|
|
38
54
|
export interface AgenticResolveResolved {
|
|
39
55
|
status: 'resolved';
|