@vultisig/cli 2.8.1 → 2.8.4
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/CHANGELOG.md +54 -0
- package/dist/index.js +144 -51
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,59 @@
|
|
|
1
1
|
# @vultisig/cli
|
|
2
2
|
|
|
3
|
+
## 2.8.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#879](https://github.com/vultisig/vultisig-sdk/pull/879) [`f34ef93`](https://github.com/vultisig/vultisig-sdk/commit/f34ef932ba18c19accccc716098c3c16b83da625) Thanks [@neavra](https://github.com/neavra)! - Fix `agent ask` error reporting so a real first-turn backend/stream error is no
|
|
8
|
+
longer masked by the initialize-time `SESSION_NOT_FOUND` signal. The
|
|
9
|
+
stale-`--session` fallback signal is now kept separately and used only as a
|
|
10
|
+
lowest-priority fallback when the turn produced no error of its own, so a
|
|
11
|
+
genuine turn error wins while a clean turn after a stale-session fallback still
|
|
12
|
+
reports `SESSION_NOT_FOUND` (non-zero exit).
|
|
13
|
+
|
|
14
|
+
- [#875](https://github.com/vultisig/vultisig-sdk/pull/875) [`a1d711b`](https://github.com/vultisig/vultisig-sdk/commit/a1d711beaf1694883688cd8e430c8b38162975b6) Thanks [@neavra](https://github.com/neavra)! - agent: surface a loop-depth overrun as a typed `LOOP_DEPTH_EXCEEDED` error instead of a success-shaped `done`.
|
|
15
|
+
|
|
16
|
+
When the agent message loop exceeds `MAX_MESSAGE_LOOP_DEPTH` (16), `processMessageLoop` previously cleared the queued tool results and called `ui.onDone()` — the same callback used on a clean finish — so a headless caller (`agent ask --json` / `--via-agent` pipe) could not distinguish a depth-capped truncation from a completed turn. It now emits `AgentErrorCode.LOOP_DEPTH_EXCEEDED` via `ui.onError` first (ask exits non-zero with an error envelope; pipe gets a typed `error` frame), then `onDone()` as the turn terminator.
|
|
17
|
+
|
|
18
|
+
- Updated dependencies [[`26dd218`](https://github.com/vultisig/vultisig-sdk/commit/26dd218282b198fdea9c1118e7fe5bc800c071fb), [`69bb830`](https://github.com/vultisig/vultisig-sdk/commit/69bb8307de72883f0c7693871a6ca040b7a0756c)]:
|
|
19
|
+
- @vultisig/client-shared@0.2.17
|
|
20
|
+
- @vultisig/core-chain@2.17.10
|
|
21
|
+
- @vultisig/sdk@2.8.4
|
|
22
|
+
|
|
23
|
+
## 2.8.2
|
|
24
|
+
|
|
25
|
+
### Patch Changes
|
|
26
|
+
|
|
27
|
+
- [#869](https://github.com/vultisig/vultisig-sdk/pull/869) [`2623ca0`](https://github.com/vultisig/vultisig-sdk/commit/2623ca0dd04c5ca3c65126b6008a93c1d7559793) Thanks [@neavra](https://github.com/neavra)! - Harden agent conversation auth across every path. A revoked-but-unexpired
|
|
28
|
+
cached token now recovers uniformly: a new `withAuthRetry` helper does
|
|
29
|
+
clear→reauth→retry-once and wraps the fresh-conversation `createConversation`
|
|
30
|
+
(previously unguarded — it hard-threw `Authentication failed`), the resume
|
|
31
|
+
`getConversation`, the send-message stream, and the `agent sessions
|
|
32
|
+
list`/`delete` commands.
|
|
33
|
+
|
|
34
|
+
A `--session-id` resume whose 401 survives the single retry — or that fails for
|
|
35
|
+
any other reason — now falls back to a fresh conversation instead of throwing
|
|
36
|
+
uncaught, and emits a typed, non-fatal `SESSION_NOT_FOUND` signal carrying the
|
|
37
|
+
new conversation id so a headless caller knows prior context was dropped. In
|
|
38
|
+
`agent ask`, that initialize-time signal is now preserved into the first turn's
|
|
39
|
+
result envelope (it was previously cleared at turn start, so a stale-session
|
|
40
|
+
`ask --json` returned a false-success envelope instead of the promised non-zero
|
|
41
|
+
`SESSION_NOT_FOUND`).
|
|
42
|
+
|
|
43
|
+
Models the backend's `refresh_token`/`access_token` in `AuthTokenResponse` and
|
|
44
|
+
persists the refresh token in the token cache (0o600) for a future
|
|
45
|
+
`POST /auth/refresh` exchange; the retry path re-auths via MPC re-sign today.
|
|
46
|
+
The retry preserves a previously cached refresh token when the re-auth response
|
|
47
|
+
omits one, and the cache directory is chmod'd to 0o700 on every write (not only
|
|
48
|
+
on create) so a pre-existing `~/.vultisig` can't retain looser perms on upgrade.
|
|
49
|
+
|
|
50
|
+
Adds the first `auth.ts` unit tests (EIP-191 hash cross-checked against viem,
|
|
51
|
+
DER→65-byte formatting, signing-retry classification) plus session
|
|
52
|
+
auth-retry/fallback coverage.
|
|
53
|
+
|
|
54
|
+
- Updated dependencies [[`0dc1620`](https://github.com/vultisig/vultisig-sdk/commit/0dc16206bedcdde8832a068b15383565a6b98896)]:
|
|
55
|
+
- @vultisig/sdk@2.8.2
|
|
56
|
+
|
|
3
57
|
## 2.8.1
|
|
4
58
|
|
|
5
59
|
### Patch Changes
|
package/dist/index.js
CHANGED
|
@@ -8074,6 +8074,8 @@ var AgentErrorCode = /* @__PURE__ */ ((AgentErrorCode3) => {
|
|
|
8074
8074
|
AgentErrorCode3["TRANSACTION_FAILED"] = "TRANSACTION_FAILED";
|
|
8075
8075
|
AgentErrorCode3["SIGNING_FAILED"] = "SIGNING_FAILED";
|
|
8076
8076
|
AgentErrorCode3["SESSION_NOT_INITIALIZED"] = "SESSION_NOT_INITIALIZED";
|
|
8077
|
+
AgentErrorCode3["LOOP_DEPTH_EXCEEDED"] = "LOOP_DEPTH_EXCEEDED";
|
|
8078
|
+
AgentErrorCode3["SESSION_NOT_FOUND"] = "SESSION_NOT_FOUND";
|
|
8077
8079
|
AgentErrorCode3["UNKNOWN_ERROR"] = "UNKNOWN_ERROR";
|
|
8078
8080
|
return AgentErrorCode3;
|
|
8079
8081
|
})(AgentErrorCode || {});
|
|
@@ -8081,6 +8083,10 @@ var AGENT_ERROR_CODE_VALUES = new Set(Object.values(AgentErrorCode));
|
|
|
8081
8083
|
function isAgentErrorCode(value) {
|
|
8082
8084
|
return AGENT_ERROR_CODE_VALUES.has(value);
|
|
8083
8085
|
}
|
|
8086
|
+
var TERMINAL_AGENT_ERROR_CODES = /* @__PURE__ */ new Set(["LOOP_DEPTH_EXCEEDED" /* LOOP_DEPTH_EXCEEDED */]);
|
|
8087
|
+
function isTerminalAgentErrorCode(code) {
|
|
8088
|
+
return TERMINAL_AGENT_ERROR_CODES.has(code);
|
|
8089
|
+
}
|
|
8084
8090
|
function mapVaultError(err) {
|
|
8085
8091
|
if (err.code === VaultErrorCode2.InvalidConfig && /failed to unlock vault/i.test(err.message)) {
|
|
8086
8092
|
return "AUTH_FAILED" /* AUTH_FAILED */;
|
|
@@ -8185,6 +8191,22 @@ var AskInterface = class {
|
|
|
8185
8191
|
transactions = [];
|
|
8186
8192
|
cards = [];
|
|
8187
8193
|
error;
|
|
8194
|
+
// Initialize-time error, kept SEPARATE from the turn error so it stays the
|
|
8195
|
+
// LOWEST-priority signal. initialize() drives getCallbacks() BEFORE the first
|
|
8196
|
+
// ask() — a stale --session fallback fires onError(SESSION_NOT_FOUND) there.
|
|
8197
|
+
// A real first-turn error must override it, so we never pre-set `this.error`
|
|
8198
|
+
// with the init signal; instead partialResult() falls back to `initError` only
|
|
8199
|
+
// when the turn produced no error of its own. Cleared after the first turn so
|
|
8200
|
+
// later turns don't carry the init signal.
|
|
8201
|
+
initError;
|
|
8202
|
+
// Tracks whether the currently-latched `error` is a terminal one (e.g. the
|
|
8203
|
+
// depth cap). A terminal error may overwrite a prior non-terminal one; once a
|
|
8204
|
+
// terminal error is recorded, later frames cannot replace it. See onError.
|
|
8205
|
+
errorIsTerminal = false;
|
|
8206
|
+
// Whether ask() has run at least once. Distinguishes init-time onError (sets
|
|
8207
|
+
// initError) from turn onError (sets error), and gates clearing initError so
|
|
8208
|
+
// the init signal only carries into the FIRST turn.
|
|
8209
|
+
hasAsked = false;
|
|
8188
8210
|
constructor(session, verbose = false, autoApprove = false) {
|
|
8189
8211
|
this.session = session;
|
|
8190
8212
|
this.verbose = verbose;
|
|
@@ -8237,8 +8259,12 @@ var AskInterface = class {
|
|
|
8237
8259
|
}
|
|
8238
8260
|
},
|
|
8239
8261
|
onError: (message, code) => {
|
|
8240
|
-
|
|
8262
|
+
const isTerminal = isTerminalAgentErrorCode(code);
|
|
8263
|
+
if (!this.hasAsked) {
|
|
8264
|
+
this.initError = { message, code };
|
|
8265
|
+
} else if (!this.error || isTerminal && !this.errorIsTerminal) {
|
|
8241
8266
|
this.error = { message, code };
|
|
8267
|
+
this.errorIsTerminal = isTerminal;
|
|
8242
8268
|
}
|
|
8243
8269
|
process.stderr.write(`[error] ${message} [${code}]
|
|
8244
8270
|
`);
|
|
@@ -8270,6 +8296,11 @@ var AskInterface = class {
|
|
|
8270
8296
|
this.transactions = [];
|
|
8271
8297
|
this.cards = [];
|
|
8272
8298
|
this.error = void 0;
|
|
8299
|
+
if (this.hasAsked) {
|
|
8300
|
+
this.initError = void 0;
|
|
8301
|
+
this.errorIsTerminal = false;
|
|
8302
|
+
}
|
|
8303
|
+
this.hasAsked = true;
|
|
8273
8304
|
const callbacks = this.getCallbacks();
|
|
8274
8305
|
await this.session.sendMessage(message, callbacks);
|
|
8275
8306
|
return this.partialResult();
|
|
@@ -8289,7 +8320,9 @@ var AskInterface = class {
|
|
|
8289
8320
|
toolCalls: this.toolCalls,
|
|
8290
8321
|
transactions: this.transactions,
|
|
8291
8322
|
cards: this.cards,
|
|
8292
|
-
error
|
|
8323
|
+
// A real turn error wins; fall back to the init-time signal (e.g. stale
|
|
8324
|
+
// --session SESSION_NOT_FOUND) only when the turn produced no error.
|
|
8325
|
+
error: this.error ?? this.initError
|
|
8293
8326
|
};
|
|
8294
8327
|
}
|
|
8295
8328
|
};
|
|
@@ -8624,7 +8657,13 @@ async function authenticateVault(client, vault, password, maxAttempts = 3) {
|
|
|
8624
8657
|
});
|
|
8625
8658
|
return {
|
|
8626
8659
|
token: authResponse.token,
|
|
8627
|
-
expiresAt: authResponse.expires_at
|
|
8660
|
+
expiresAt: authResponse.expires_at,
|
|
8661
|
+
// Captured + persisted by the session token cache. The backend exposes
|
|
8662
|
+
// POST /auth/refresh to exchange it for a fresh access token without a
|
|
8663
|
+
// new MPC round; wiring that exchange is a future enhancement — today
|
|
8664
|
+
// the CLI re-auths via a full MPC re-sign (authenticateVault), which is
|
|
8665
|
+
// always available and avoids depending on refresh-token rotation.
|
|
8666
|
+
refreshToken: authResponse.refresh_token
|
|
8628
8667
|
};
|
|
8629
8668
|
} catch (err) {
|
|
8630
8669
|
lastError = err;
|
|
@@ -12072,7 +12111,7 @@ var PipeInterface = class {
|
|
|
12072
12111
|
};
|
|
12073
12112
|
|
|
12074
12113
|
// src/agent/session.ts
|
|
12075
|
-
import { existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
12114
|
+
import { chmodSync, existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
12076
12115
|
import { homedir as homedir2 } from "node:os";
|
|
12077
12116
|
import { join as join2 } from "node:path";
|
|
12078
12117
|
import { MemoryStorage, PushNotificationService } from "@vultisig/sdk";
|
|
@@ -12144,7 +12183,7 @@ var AgentSession = class {
|
|
|
12144
12183
|
} else {
|
|
12145
12184
|
const auth = await authenticateVault(this.client, this.vault, this.config.password);
|
|
12146
12185
|
this.client.setAuthToken(auth.token);
|
|
12147
|
-
saveCachedToken(this.publicKey, auth.token, auth.expiresAt);
|
|
12186
|
+
saveCachedToken(this.publicKey, auth.token, auth.expiresAt, auth.refreshToken);
|
|
12148
12187
|
}
|
|
12149
12188
|
} catch (err) {
|
|
12150
12189
|
throw new Error(`Authentication failed: ${err.message}`);
|
|
@@ -12152,25 +12191,20 @@ var AgentSession = class {
|
|
|
12152
12191
|
if (this.config.sessionId) {
|
|
12153
12192
|
this.conversationId = this.config.sessionId;
|
|
12154
12193
|
try {
|
|
12155
|
-
const conv = await this.client.getConversation(this.conversationId, this.publicKey);
|
|
12194
|
+
const conv = await this.withAuthRetry(() => this.client.getConversation(this.conversationId, this.publicKey));
|
|
12156
12195
|
this.historyMessages = conv.messages || [];
|
|
12157
12196
|
} catch (err) {
|
|
12158
|
-
|
|
12159
|
-
|
|
12160
|
-
|
|
12161
|
-
|
|
12162
|
-
|
|
12163
|
-
|
|
12164
|
-
|
|
12165
|
-
|
|
12166
|
-
this.conversationId = null;
|
|
12167
|
-
this.historyMessages = [];
|
|
12168
|
-
const conv = await this.client.createConversation(this.publicKey);
|
|
12169
|
-
this.conversationId = conv.id;
|
|
12170
|
-
}
|
|
12197
|
+
this.conversationId = null;
|
|
12198
|
+
this.historyMessages = [];
|
|
12199
|
+
const conv = await this.withAuthRetry(() => this.client.createConversation(this.publicKey));
|
|
12200
|
+
this.conversationId = conv.id;
|
|
12201
|
+
ui.onError(
|
|
12202
|
+
`Session ${this.config.sessionId} could not be resumed (${err?.message ?? "unknown error"}); started a new conversation ${conv.id}`,
|
|
12203
|
+
"SESSION_NOT_FOUND" /* SESSION_NOT_FOUND */
|
|
12204
|
+
);
|
|
12171
12205
|
}
|
|
12172
12206
|
} else {
|
|
12173
|
-
const conv = await this.client.createConversation(this.publicKey);
|
|
12207
|
+
const conv = await this.withAuthRetry(() => this.client.createConversation(this.publicKey));
|
|
12174
12208
|
this.conversationId = conv.id;
|
|
12175
12209
|
}
|
|
12176
12210
|
this.cachedContext = this.config.viaAgent || this.config.askMode ? await buildMinimalContext(this.vault) : await buildMessageContext(this.vault);
|
|
@@ -12204,6 +12238,40 @@ var AgentSession = class {
|
|
|
12204
12238
|
}
|
|
12205
12239
|
}
|
|
12206
12240
|
}
|
|
12241
|
+
/**
|
|
12242
|
+
* Run an authenticated backend request and, on a 401/403, do a single
|
|
12243
|
+
* clear → re-auth → retry. This is the ONE chokepoint every conversation
|
|
12244
|
+
* request shares (resume fetch, fresh-convo create, error-fallback create,
|
|
12245
|
+
* and the send-message stream) so a revoked-but-unexpired cached token
|
|
12246
|
+
* recovers identically everywhere instead of throwing on some paths.
|
|
12247
|
+
*
|
|
12248
|
+
* The retry replays the EXACT same `request` closure, which matters for the
|
|
12249
|
+
* send-message path: the replayed body must carry the same content +
|
|
12250
|
+
* recent_actions or the LLM re-emits tool calls (runaway loop). Re-auth is a
|
|
12251
|
+
* full MPC re-sign via authenticateVault — the backend also exposes
|
|
12252
|
+
* POST /auth/refresh, but exchanging the refresh token is a future
|
|
12253
|
+
* enhancement (see auth.ts); the re-sign is always available.
|
|
12254
|
+
*
|
|
12255
|
+
* `onReauth` (optional) fires the instant a re-auth is committed to — BEFORE
|
|
12256
|
+
* authenticateVault runs — so a caller in a retry loop (recoverDisconnectedTurn)
|
|
12257
|
+
* can record that its single re-auth has been spent even if the MPC re-sign
|
|
12258
|
+
* itself then throws. Without this hook a re-auth that fails with a non-auth
|
|
12259
|
+
* error would let the caller re-enter and re-sign on every iteration.
|
|
12260
|
+
*/
|
|
12261
|
+
async withAuthRetry(request, onReauth) {
|
|
12262
|
+
try {
|
|
12263
|
+
return await request();
|
|
12264
|
+
} catch (err) {
|
|
12265
|
+
if (!isAuthError(err)) throw err;
|
|
12266
|
+
onReauth?.();
|
|
12267
|
+
const previousRefreshToken = readTokenStore()[this.publicKey]?.refreshToken;
|
|
12268
|
+
clearCachedToken(this.publicKey);
|
|
12269
|
+
const auth = await authenticateVault(this.client, this.vault, this.config.password);
|
|
12270
|
+
this.client.setAuthToken(auth.token);
|
|
12271
|
+
saveCachedToken(this.publicKey, auth.token, auth.expiresAt, auth.refreshToken ?? previousRefreshToken);
|
|
12272
|
+
return await request();
|
|
12273
|
+
}
|
|
12274
|
+
}
|
|
12207
12275
|
getConversationId() {
|
|
12208
12276
|
return this.conversationId;
|
|
12209
12277
|
}
|
|
@@ -12253,6 +12321,10 @@ var AgentSession = class {
|
|
|
12253
12321
|
`
|
|
12254
12322
|
);
|
|
12255
12323
|
this.pendingToolResults = [];
|
|
12324
|
+
ui.onError(
|
|
12325
|
+
`agent message loop exceeded ${MAX_MESSAGE_LOOP_DEPTH} turns; conversation truncated`,
|
|
12326
|
+
"LOOP_DEPTH_EXCEEDED" /* LOOP_DEPTH_EXCEEDED */
|
|
12327
|
+
);
|
|
12256
12328
|
ui.onDone();
|
|
12257
12329
|
return;
|
|
12258
12330
|
}
|
|
@@ -12328,31 +12400,15 @@ var AgentSession = class {
|
|
|
12328
12400
|
}
|
|
12329
12401
|
};
|
|
12330
12402
|
let streamResult;
|
|
12331
|
-
|
|
12332
|
-
|
|
12333
|
-
|
|
12334
|
-
|
|
12335
|
-
|
|
12336
|
-
|
|
12337
|
-
|
|
12338
|
-
this.abortController?.signal
|
|
12339
|
-
);
|
|
12340
|
-
break;
|
|
12341
|
-
} catch (err) {
|
|
12342
|
-
const isAuthErr = err.message?.includes("401") || err.message?.includes("403");
|
|
12343
|
-
if (isAuthErr && !authRetried) {
|
|
12344
|
-
authRetried = true;
|
|
12345
|
-
clearCachedToken(this.publicKey);
|
|
12346
|
-
const auth = await authenticateVault(this.client, this.vault, this.config.password);
|
|
12347
|
-
this.client.setAuthToken(auth.token);
|
|
12348
|
-
saveCachedToken(this.publicKey, auth.token, auth.expiresAt);
|
|
12349
|
-
continue;
|
|
12350
|
-
}
|
|
12351
|
-
if (flushedThisCall.length > 0) {
|
|
12352
|
-
this.pendingToolResults = [...flushedThisCall, ...this.pendingToolResults];
|
|
12353
|
-
}
|
|
12354
|
-
throw err;
|
|
12403
|
+
try {
|
|
12404
|
+
streamResult = await this.withAuthRetry(
|
|
12405
|
+
() => this.client.sendMessageStream(this.conversationId, request, callbacks, this.abortController?.signal)
|
|
12406
|
+
);
|
|
12407
|
+
} catch (err) {
|
|
12408
|
+
if (flushedThisCall.length > 0) {
|
|
12409
|
+
this.pendingToolResults = [...flushedThisCall, ...this.pendingToolResults];
|
|
12355
12410
|
}
|
|
12411
|
+
throw err;
|
|
12356
12412
|
}
|
|
12357
12413
|
if (pendingDispatches.length > 0) {
|
|
12358
12414
|
await Promise.all(pendingDispatches);
|
|
@@ -12415,10 +12471,16 @@ var AgentSession = class {
|
|
|
12415
12471
|
const since = serverAnchor ?? new Date(Date.now() - 2e3).toISOString();
|
|
12416
12472
|
const replaySignableCards = serverAnchor !== null;
|
|
12417
12473
|
let cursor;
|
|
12474
|
+
let authRecovered = false;
|
|
12418
12475
|
for (let attempt = 0; attempt < this.recoveryMaxPolls; attempt++) {
|
|
12419
12476
|
let resp;
|
|
12420
12477
|
try {
|
|
12421
|
-
resp = await this.client.messagesSince(this.conversationId, cursor ? { cursor } : { since })
|
|
12478
|
+
resp = authRecovered ? await this.client.messagesSince(this.conversationId, cursor ? { cursor } : { since }) : await this.withAuthRetry(
|
|
12479
|
+
() => this.client.messagesSince(this.conversationId, cursor ? { cursor } : { since }),
|
|
12480
|
+
() => {
|
|
12481
|
+
authRecovered = true;
|
|
12482
|
+
}
|
|
12483
|
+
);
|
|
12422
12484
|
} catch (err) {
|
|
12423
12485
|
if (this.config.verbose) {
|
|
12424
12486
|
process.stderr.write(`[session] recovery poll ${attempt + 1} failed: ${err?.message ?? err}
|
|
@@ -12708,6 +12770,10 @@ function serverNowToIso(serverNow) {
|
|
|
12708
12770
|
function hasTxReadyPart(parts) {
|
|
12709
12771
|
return !!parts?.some((p) => p.type === "data-tx_ready" && !!p.data);
|
|
12710
12772
|
}
|
|
12773
|
+
function isAuthError(err) {
|
|
12774
|
+
const msg = err instanceof Error ? err.message : String(err ?? "");
|
|
12775
|
+
return /\b(401|403)\b/.test(msg);
|
|
12776
|
+
}
|
|
12711
12777
|
function getTokenCachePath() {
|
|
12712
12778
|
const dir = process.env.VULTISIG_CONFIG_DIR ?? join2(homedir2(), ".vultisig");
|
|
12713
12779
|
return join2(dir, "agent-tokens.json");
|
|
@@ -12724,8 +12790,16 @@ function readTokenStore() {
|
|
|
12724
12790
|
function writeTokenStore(store) {
|
|
12725
12791
|
const path4 = getTokenCachePath();
|
|
12726
12792
|
const dir = join2(path4, "..");
|
|
12727
|
-
if (!existsSync(dir)) mkdirSync2(dir, { recursive: true });
|
|
12793
|
+
if (!existsSync(dir)) mkdirSync2(dir, { recursive: true, mode: 448 });
|
|
12794
|
+
try {
|
|
12795
|
+
chmodSync(dir, 448);
|
|
12796
|
+
} catch {
|
|
12797
|
+
}
|
|
12728
12798
|
writeFileSync2(path4, JSON.stringify(store, null, 2), { mode: 384 });
|
|
12799
|
+
try {
|
|
12800
|
+
chmodSync(path4, 384);
|
|
12801
|
+
} catch {
|
|
12802
|
+
}
|
|
12729
12803
|
}
|
|
12730
12804
|
function loadCachedToken(publicKey) {
|
|
12731
12805
|
const store = readTokenStore();
|
|
@@ -12743,9 +12817,13 @@ function loadCachedToken(publicKey) {
|
|
|
12743
12817
|
}
|
|
12744
12818
|
return entry.token;
|
|
12745
12819
|
}
|
|
12746
|
-
function saveCachedToken(publicKey, token, expiresAt) {
|
|
12820
|
+
function saveCachedToken(publicKey, token, expiresAt, refreshToken) {
|
|
12747
12821
|
const store = readTokenStore();
|
|
12748
|
-
store[publicKey] = {
|
|
12822
|
+
store[publicKey] = {
|
|
12823
|
+
token,
|
|
12824
|
+
expiresAt,
|
|
12825
|
+
refreshToken: refreshToken ?? store[publicKey]?.refreshToken
|
|
12826
|
+
};
|
|
12749
12827
|
try {
|
|
12750
12828
|
writeTokenStore(store);
|
|
12751
12829
|
} catch {
|
|
@@ -13293,7 +13371,12 @@ async function executeAgentSessionsList(ctx2, options) {
|
|
|
13293
13371
|
let totalCount = 0;
|
|
13294
13372
|
let skip = 0;
|
|
13295
13373
|
while (true) {
|
|
13296
|
-
const page = await
|
|
13374
|
+
const page = await withClientAuthRetry(
|
|
13375
|
+
client,
|
|
13376
|
+
vault,
|
|
13377
|
+
options.password,
|
|
13378
|
+
() => client.listConversations(publicKey, skip, PAGE_SIZE)
|
|
13379
|
+
);
|
|
13297
13380
|
totalCount = page.total_count;
|
|
13298
13381
|
allConversations.push(...page.conversations);
|
|
13299
13382
|
if (allConversations.length >= totalCount || page.conversations.length < PAGE_SIZE) break;
|
|
@@ -13335,7 +13418,7 @@ async function executeAgentSessionsDelete(ctx2, sessionId, options) {
|
|
|
13335
13418
|
const backendUrl = options.backendUrl || process.env.VULTISIG_AGENT_URL || "https://abe.vultisig.com";
|
|
13336
13419
|
const client = await createAuthenticatedClient(backendUrl, vault, options.password);
|
|
13337
13420
|
const publicKey = vault.publicKeys.ecdsa;
|
|
13338
|
-
await client.deleteConversation(sessionId, publicKey);
|
|
13421
|
+
await withClientAuthRetry(client, vault, options.password, () => client.deleteConversation(sessionId, publicKey));
|
|
13339
13422
|
if (isJsonOutput()) {
|
|
13340
13423
|
outputJson({ deleted: sessionId });
|
|
13341
13424
|
return;
|
|
@@ -13348,6 +13431,16 @@ async function createAuthenticatedClient(backendUrl, vault, password) {
|
|
|
13348
13431
|
client.setAuthToken(auth.token);
|
|
13349
13432
|
return client;
|
|
13350
13433
|
}
|
|
13434
|
+
async function withClientAuthRetry(client, vault, password, request) {
|
|
13435
|
+
try {
|
|
13436
|
+
return await request();
|
|
13437
|
+
} catch (err) {
|
|
13438
|
+
if (!isAuthError(err)) throw err;
|
|
13439
|
+
const auth = await authenticateVault(client, vault, password);
|
|
13440
|
+
client.setAuthToken(auth.token);
|
|
13441
|
+
return await request();
|
|
13442
|
+
}
|
|
13443
|
+
}
|
|
13351
13444
|
function formatDate(iso) {
|
|
13352
13445
|
try {
|
|
13353
13446
|
const d = new Date(iso);
|
|
@@ -13366,7 +13459,7 @@ var cachedVersion = null;
|
|
|
13366
13459
|
function getVersion() {
|
|
13367
13460
|
if (cachedVersion) return cachedVersion;
|
|
13368
13461
|
if (true) {
|
|
13369
|
-
cachedVersion = "2.8.
|
|
13462
|
+
cachedVersion = "2.8.4";
|
|
13370
13463
|
return cachedVersion;
|
|
13371
13464
|
}
|
|
13372
13465
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vultisig/cli",
|
|
3
|
-
"version": "2.8.
|
|
3
|
+
"version": "2.8.4",
|
|
4
4
|
"description": "The self-custody MPC wallet CLI for AI coding agents (Claude Code, Cursor, OpenCode). Natural-language agent mode, 36+ chains, DKLS23 threshold signatures. Seedless.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -73,10 +73,10 @@
|
|
|
73
73
|
"@cosmjs/stargate": "^0.39.0",
|
|
74
74
|
"@napi-rs/keyring": "^1.3.0",
|
|
75
75
|
"@noble/hashes": "^2.2.0",
|
|
76
|
-
"@vultisig/client-shared": "^0.2.
|
|
77
|
-
"@vultisig/core-chain": "^2.17.
|
|
76
|
+
"@vultisig/client-shared": "^0.2.17",
|
|
77
|
+
"@vultisig/core-chain": "^2.17.10",
|
|
78
78
|
"@vultisig/rujira": "^41.0.0",
|
|
79
|
-
"@vultisig/sdk": "^2.8.
|
|
79
|
+
"@vultisig/sdk": "^2.8.4",
|
|
80
80
|
"chalk": "^5.6.2",
|
|
81
81
|
"cli-table3": "^0.6.5",
|
|
82
82
|
"commander": "^15.0.0",
|