11import { promises as fs , existsSync } from "node:fs" ;
22import { randomBytes } from "node:crypto" ;
3- import { dirname , join } from "node:path" ;
3+ import { basename , dirname , join } from "node:path" ;
44import { ACCOUNT_LIMITS } from "./constants.js" ;
55import { createLogger } from "./logger.js" ;
66import { MODEL_FAMILIES , type ModelFamily } from "./prompts/codex.js" ;
@@ -1210,17 +1210,12 @@ function analyzeImportedAccounts(
12101210 importedAccounts : AccountStorageV3 [ "accounts" ] ,
12111211) : ImportPreviewResult & { accounts : AccountMetadataV3 [ ] } {
12121212 const merged = [ ...existingAccounts , ...importedAccounts ] ;
1213-
1214- if ( merged . length > ACCOUNT_LIMITS . MAX_ACCOUNTS ) {
1215- const deduped = deduplicateAccountsForStorage ( merged ) ;
1216- if ( deduped . length > ACCOUNT_LIMITS . MAX_ACCOUNTS ) {
1217- throw new Error (
1218- `Import would exceed maximum of ${ ACCOUNT_LIMITS . MAX_ACCOUNTS } accounts (would have ${ deduped . length } )` ,
1219- ) ;
1220- }
1221- }
1222-
12231213 const accounts = deduplicateAccountsForStorage ( merged ) ;
1214+ if ( accounts . length > ACCOUNT_LIMITS . MAX_ACCOUNTS ) {
1215+ throw new Error (
1216+ `Import would exceed maximum of ${ ACCOUNT_LIMITS . MAX_ACCOUNTS } accounts (would have ${ accounts . length } )` ,
1217+ ) ;
1218+ }
12241219 const imported = Math . max ( 0 , accounts . length - existingAccounts . length ) ;
12251220 const skipped = Math . max ( 0 , importedAccounts . length - imported ) ;
12261221 return {
@@ -1231,6 +1226,14 @@ function analyzeImportedAccounts(
12311226 } ;
12321227}
12331228
1229+ /**
1230+ * Import preview/apply analysis is pure in-memory work: it does not touch disk
1231+ * and it does not log token or workspace values. The surrounding
1232+ * `withAccountStorageTransaction` caller keeps Windows lock-retry and
1233+ * serialized read-modify-write behavior; see `test/storage.test.ts` for the
1234+ * overlapping transaction regression and pre-import backup lock coverage.
1235+ */
1236+
12341237export async function previewImportAccounts (
12351238 filePath : string ,
12361239) : Promise < ImportPreviewResult > {
@@ -1313,6 +1316,7 @@ export async function importAccounts(
13131316 let backupStatus : ImportBackupStatus = "skipped" ;
13141317 let backupPath : string | undefined ;
13151318 let backupError : string | undefined ;
1319+ let backupLogError : string | undefined ;
13161320 if ( backupMode !== "none" && existingAccounts . length > 0 ) {
13171321 backupPath = createTimestampedBackupPath ( backupPrefix ) ;
13181322 try {
@@ -1321,12 +1325,20 @@ export async function importAccounts(
13211325 } catch ( error ) {
13221326 backupStatus = "failed" ;
13231327 backupError = error instanceof Error ? error . message : String ( error ) ;
1328+ const backupCode = ( error as NodeJS . ErrnoException ) ?. code ;
1329+ backupLogError = backupCode
1330+ ? `pre-import backup failed (${ backupCode } )`
1331+ : "pre-import backup failed" ;
13241332 if ( backupMode === "required" ) {
1325- throw new Error ( `Pre-import backup failed: ${ backupError } ` ) ;
1333+ throw new Error (
1334+ backupCode
1335+ ? `Pre-import backup failed (${ backupCode } )`
1336+ : "Pre-import backup failed" ,
1337+ ) ;
13261338 }
13271339 log . warn ( "Pre-import backup failed; continuing import apply" , {
1328- path : backupPath ,
1329- error : backupError ,
1340+ backupFile : backupPath ? basename ( backupPath ) : undefined ,
1341+ error : backupLogError ,
13301342 } ) ;
13311343 }
13321344 }
@@ -1384,8 +1396,8 @@ export async function importAccounts(
13841396 skipped : skippedCount ,
13851397 total,
13861398 backupStatus,
1387- backupPath,
1388- backupError,
1399+ backupFile : backupPath ? basename ( backupPath ) : undefined ,
1400+ backupError : backupError ? "available on command result" : undefined ,
13891401 } ) ;
13901402
13911403 return {
0 commit comments