@t2000/cli 5.1.0 → 5.3.0

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/index.js CHANGED
@@ -13676,7 +13676,7 @@ var require_lib2 = __commonJS({
13676
13676
  return matches;
13677
13677
  };
13678
13678
  exports.analyse = analyse;
13679
- var detectFile = (filepath, opts = {}) => new Promise((resolve3, reject) => {
13679
+ var detectFile = (filepath, opts = {}) => new Promise((resolve2, reject) => {
13680
13680
  let fd;
13681
13681
  const fs = (0, node_1.default)();
13682
13682
  const handler = (err, buffer) => {
@@ -13686,7 +13686,7 @@ var require_lib2 = __commonJS({
13686
13686
  if (err) {
13687
13687
  reject(err);
13688
13688
  } else if (buffer) {
13689
- resolve3((0, exports.detect)(buffer));
13689
+ resolve2((0, exports.detect)(buffer));
13690
13690
  } else {
13691
13691
  reject(new Error("No error and no buffer received"));
13692
13692
  }
@@ -21930,10 +21930,10 @@ var require_browser = __commonJS({
21930
21930
  text = canvas;
21931
21931
  canvas = void 0;
21932
21932
  }
21933
- return new Promise(function(resolve3, reject) {
21933
+ return new Promise(function(resolve2, reject) {
21934
21934
  try {
21935
21935
  const data = QRCode.create(text, opts);
21936
- resolve3(renderFunc(data, canvas, opts));
21936
+ resolve2(renderFunc(data, canvas, opts));
21937
21937
  } catch (e) {
21938
21938
  reject(e);
21939
21939
  }
@@ -22015,11 +22015,11 @@ var require_server = __commonJS({
22015
22015
  }
22016
22016
  function render(renderFunc, text, params) {
22017
22017
  if (!params.cb) {
22018
- return new Promise(function(resolve3, reject) {
22018
+ return new Promise(function(resolve2, reject) {
22019
22019
  try {
22020
22020
  const data = QRCode.create(text, params.opts);
22021
22021
  return renderFunc(data, params.opts, function(err, data2) {
22022
- return err ? reject(err) : resolve3(data2);
22022
+ return err ? reject(err) : resolve2(data2);
22023
22023
  });
22024
22024
  } catch (e) {
22025
22025
  reject(e);
@@ -28159,164 +28159,194 @@ async function resolveSuinsViaRpc(rawName, ctx = {}) {
28159
28159
  return body.result ?? null;
28160
28160
  }
28161
28161
  init_errors();
28162
- var DEFAULT_SAFEGUARD_CONFIG = {
28163
- locked: false,
28164
- maxPerTx: 0,
28165
- maxDailySend: 0,
28166
- dailyUsed: 0,
28167
- dailyResetDate: ""
28168
- };
28169
- init_errors();
28170
- var SafeguardError = class extends T2000Error {
28171
- rule;
28172
- details;
28173
- constructor(rule, details, message) {
28174
- const msg = message ?? buildMessage(rule, details);
28175
- super("SAFEGUARD_BLOCKED", msg, { rule, ...details });
28176
- this.name = "SafeguardError";
28177
- this.rule = rule;
28178
- this.details = details;
28162
+ var DEFAULT_CONFIG_DIR = join2(homedir(), ".t2000");
28163
+ function resolveConfigPath(configDir) {
28164
+ return join2(configDir ?? DEFAULT_CONFIG_DIR, "config.json");
28165
+ }
28166
+ function todayUtc() {
28167
+ return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
28168
+ }
28169
+ function readLimitsFile(configDir) {
28170
+ const path2 = resolveConfigPath(configDir);
28171
+ try {
28172
+ return sanitize(JSON.parse(readFileSync(path2, "utf-8")));
28173
+ } catch {
28174
+ return {};
28175
+ }
28176
+ }
28177
+ function writeLimitsFile(file, configDir) {
28178
+ const path2 = resolveConfigPath(configDir);
28179
+ let existing = {};
28180
+ try {
28181
+ existing = JSON.parse(readFileSync(path2, "utf-8"));
28182
+ } catch {
28183
+ existing = {};
28184
+ }
28185
+ for (const dead of ["maxPerTx", "maxDailySend", "dailyUsed", "dailyResetDate", "locked"]) {
28186
+ delete existing[dead];
28187
+ }
28188
+ const merged = { ...existing };
28189
+ if (file.limits && (file.limits.perTxUsd !== void 0 || file.limits.dailyUsd !== void 0)) {
28190
+ merged.limits = file.limits;
28191
+ } else {
28192
+ delete merged.limits;
28193
+ }
28194
+ if (file.dailySpend) merged.dailySpend = file.dailySpend;
28195
+ else delete merged.dailySpend;
28196
+ const dir = dirname(path2);
28197
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
28198
+ writeFileSync(path2, JSON.stringify(merged, null, 2) + "\n", { mode: 384 });
28199
+ return path2;
28200
+ }
28201
+ function getLimits(configDir) {
28202
+ return readLimitsFile(configDir).limits;
28203
+ }
28204
+ function hasLimits(configDir) {
28205
+ const l = readLimitsFile(configDir).limits;
28206
+ return !!l && (l.perTxUsd !== void 0 || l.dailyUsd !== void 0);
28207
+ }
28208
+ function setLimits(limits, configDir) {
28209
+ const file = readLimitsFile(configDir);
28210
+ const merged = { ...file.limits };
28211
+ if (limits.perTxUsd !== void 0) merged.perTxUsd = limits.perTxUsd;
28212
+ if (limits.dailyUsd !== void 0) merged.dailyUsd = limits.dailyUsd;
28213
+ return writeLimitsFile({ ...file, limits: merged }, configDir);
28214
+ }
28215
+ function clearLimits(configDir) {
28216
+ const file = readLimitsFile(configDir);
28217
+ return writeLimitsFile({ ...file, limits: void 0 }, configDir);
28218
+ }
28219
+ function dailySpentToday(configDir) {
28220
+ const { dailySpend } = readLimitsFile(configDir);
28221
+ if (!dailySpend || dailySpend.date !== todayUtc()) return 0;
28222
+ return dailySpend.usd;
28223
+ }
28224
+ function recordDailySpend(usd, configDir) {
28225
+ if (!Number.isFinite(usd) || usd <= 0) return;
28226
+ const file = readLimitsFile(configDir);
28227
+ const today = todayUtc();
28228
+ const prior = file.dailySpend && file.dailySpend.date === today ? file.dailySpend.usd : 0;
28229
+ writeLimitsFile({ ...file, dailySpend: { date: today, usd: prior + usd } }, configDir);
28230
+ }
28231
+ function sanitize(raw) {
28232
+ if (typeof raw !== "object" || raw === null) return {};
28233
+ const r = raw;
28234
+ const out = {};
28235
+ if (typeof r.limits === "object" && r.limits !== null) {
28236
+ const l = r.limits;
28237
+ const limits = {};
28238
+ if (typeof l.perTxUsd === "number" && l.perTxUsd > 0) limits.perTxUsd = l.perTxUsd;
28239
+ if (typeof l.dailyUsd === "number" && l.dailyUsd > 0) limits.dailyUsd = l.dailyUsd;
28240
+ else if (typeof l.dailySendUsd === "number" && l.dailySendUsd > 0) limits.dailyUsd = l.dailySendUsd;
28241
+ if (limits.perTxUsd !== void 0 || limits.dailyUsd !== void 0) out.limits = limits;
28242
+ }
28243
+ if (typeof r.dailySpend === "object" && r.dailySpend !== null) {
28244
+ const d = r.dailySpend;
28245
+ if (typeof d.date === "string" && typeof d.usd === "number" && d.usd >= 0) {
28246
+ out.dailySpend = { date: d.date, usd: d.usd };
28247
+ }
28248
+ }
28249
+ return out;
28250
+ }
28251
+ var LimitExceededError = class extends Error {
28252
+ code = "LIMIT_EXCEEDED";
28253
+ operation;
28254
+ limitKind;
28255
+ limit;
28256
+ attempted;
28257
+ constructor(params) {
28258
+ const label = params.limitKind === "perTxUsd" ? "per-transaction limit" : "daily spend limit";
28259
+ super(
28260
+ `Exceeds ${label} ($${params.limit}). Attempted $${params.attempted.toFixed(2)}. Use --force / force:true to override.`
28261
+ );
28262
+ this.name = "LimitExceededError";
28263
+ this.operation = params.operation;
28264
+ this.limitKind = params.limitKind;
28265
+ this.limit = params.limit;
28266
+ this.attempted = params.attempted;
28179
28267
  }
28180
28268
  toJSON() {
28181
28269
  return {
28182
- error: "SAFEGUARD_BLOCKED",
28270
+ error: this.code,
28183
28271
  message: this.message,
28184
- retryable: this.retryable,
28185
- data: { rule: this.rule, ...this.details }
28272
+ operation: this.operation,
28273
+ limitKind: this.limitKind,
28274
+ limit: this.limit,
28275
+ attempted: this.attempted
28186
28276
  };
28187
28277
  }
28188
28278
  };
28189
- function buildMessage(rule, details) {
28190
- switch (rule) {
28191
- case "locked":
28192
- return "Agent is locked. All operations are frozen.";
28193
- case "maxPerTx":
28194
- return `Amount $${(details.attempted ?? 0).toFixed(2)} exceeds per-transaction limit ($${(details.limit ?? 0).toFixed(2)})`;
28195
- case "maxDailySend":
28196
- return `Daily send limit reached ($${(details.current ?? 0).toFixed(2)}/$${(details.limit ?? 0).toFixed(2)} used today)`;
28197
- }
28198
- }
28199
- var SafeguardEnforcer = class {
28200
- config;
28201
- configPath;
28202
- constructor(configDir) {
28203
- this.config = { ...DEFAULT_SAFEGUARD_CONFIG };
28204
- this.configPath = configDir ? join2(configDir, "config.json") : null;
28205
- }
28206
- load() {
28207
- if (!this.configPath) return;
28208
- try {
28209
- const raw = JSON.parse(readFileSync(this.configPath, "utf-8"));
28210
- this.config = {
28211
- ...DEFAULT_SAFEGUARD_CONFIG,
28212
- locked: raw.locked ?? false,
28213
- maxPerTx: raw.maxPerTx ?? 0,
28214
- maxDailySend: raw.maxDailySend ?? 0,
28215
- dailyUsed: raw.dailyUsed ?? 0,
28216
- dailyResetDate: raw.dailyResetDate ?? ""
28217
- };
28218
- } catch {
28219
- this.config = { ...DEFAULT_SAFEGUARD_CONFIG };
28220
- }
28279
+ function approxUsdValue(asset, amount) {
28280
+ const symbol = asset.toUpperCase();
28281
+ if (symbol === "USDC" || symbol === "USDSUI") return amount;
28282
+ return null;
28283
+ }
28284
+ function assertLimitConfig(input) {
28285
+ if (input.force) return;
28286
+ if (!Number.isFinite(input.amountUsd) || input.amountUsd <= 0) return;
28287
+ const { limits } = input;
28288
+ if (!limits) return;
28289
+ if (limits.perTxUsd !== void 0 && input.amountUsd > limits.perTxUsd) {
28290
+ throw new LimitExceededError({
28291
+ operation: input.operation,
28292
+ limitKind: "perTxUsd",
28293
+ limit: limits.perTxUsd,
28294
+ attempted: input.amountUsd
28295
+ });
28221
28296
  }
28222
- assertNotLocked() {
28223
- this.load();
28224
- if (this.config.locked) {
28225
- throw new SafeguardError("locked", {});
28226
- }
28297
+ if (limits.dailyUsd !== void 0 && input.spentTodayUsd + input.amountUsd > limits.dailyUsd) {
28298
+ throw new LimitExceededError({
28299
+ operation: input.operation,
28300
+ limitKind: "dailyUsd",
28301
+ // The cap is total/day; report the cap (attempted is THIS write's USD).
28302
+ limit: limits.dailyUsd,
28303
+ attempted: input.spentTodayUsd + input.amountUsd
28304
+ });
28227
28305
  }
28228
- check(metadata) {
28229
- this.load();
28230
- if (this.config.locked) {
28231
- throw new SafeguardError("locked", {});
28232
- }
28233
- const amount = metadata.amount ?? 0;
28234
- if (this.config.maxPerTx > 0 && amount > this.config.maxPerTx) {
28235
- throw new SafeguardError("maxPerTx", {
28236
- attempted: amount,
28237
- limit: this.config.maxPerTx
28238
- });
28239
- }
28240
- this.resetDailyIfNewDay();
28241
- if (this.config.maxDailySend > 0 && this.config.dailyUsed + amount > this.config.maxDailySend) {
28242
- throw new SafeguardError("maxDailySend", {
28243
- attempted: amount,
28244
- limit: this.config.maxDailySend,
28245
- current: this.config.dailyUsed
28246
- });
28247
- }
28306
+ }
28307
+ var LimitEnforcer = class {
28308
+ constructor(configDir) {
28309
+ this.configDir = configDir;
28248
28310
  }
28249
- recordUsage(amount) {
28250
- this.resetDailyIfNewDay();
28251
- this.config.dailyUsed += amount;
28252
- this.save();
28311
+ /** Throws `LimitExceededError` when the write exceeds an opted-in cap. */
28312
+ assert(input) {
28313
+ assertLimitConfig({
28314
+ limits: getLimits(this.configDir),
28315
+ spentTodayUsd: dailySpentToday(this.configDir),
28316
+ operation: input.operation,
28317
+ amountUsd: input.amountUsd,
28318
+ force: input.force
28319
+ });
28253
28320
  }
28254
- lock() {
28255
- this.config.locked = true;
28256
- this.save();
28321
+ /** Add a settled write's USD value to today's cumulative total. */
28322
+ record(amountUsd) {
28323
+ recordDailySpend(amountUsd, this.configDir);
28257
28324
  }
28258
- unlock() {
28259
- this.config.locked = false;
28260
- this.save();
28325
+ getLimits() {
28326
+ return getLimits(this.configDir);
28261
28327
  }
28262
- set(key, value) {
28263
- if (key === "locked" && typeof value === "boolean") {
28264
- this.config.locked = value;
28265
- } else if (key === "maxPerTx" && typeof value === "number") {
28266
- this.config.maxPerTx = value;
28267
- } else if (key === "maxDailySend" && typeof value === "number") {
28268
- this.config.maxDailySend = value;
28269
- }
28270
- this.save();
28328
+ hasLimits() {
28329
+ return hasLimits(this.configDir);
28271
28330
  }
28272
- getConfig() {
28273
- this.load();
28274
- this.resetDailyIfNewDay();
28275
- return { ...this.config };
28331
+ setLimits(limits) {
28332
+ setLimits(limits, this.configDir);
28276
28333
  }
28277
- isConfigured() {
28278
- return this.config.maxPerTx > 0 || this.config.maxDailySend > 0;
28334
+ clearLimits() {
28335
+ clearLimits(this.configDir);
28279
28336
  }
28280
- resetDailyIfNewDay() {
28281
- const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
28282
- if (this.config.dailyResetDate !== today) {
28283
- this.config.dailyUsed = 0;
28284
- this.config.dailyResetDate = today;
28285
- this.save();
28286
- }
28287
- }
28288
- save() {
28289
- if (!this.configPath) return;
28290
- try {
28291
- let existing = {};
28292
- try {
28293
- existing = JSON.parse(readFileSync(this.configPath, "utf-8"));
28294
- } catch {
28295
- }
28296
- const merged = {
28297
- ...existing,
28298
- locked: this.config.locked,
28299
- maxPerTx: this.config.maxPerTx,
28300
- maxDailySend: this.config.maxDailySend,
28301
- dailyUsed: this.config.dailyUsed,
28302
- dailyResetDate: this.config.dailyResetDate
28303
- };
28304
- const dir = this.configPath.replace(/[/\\][^/\\]+$/, "");
28305
- if (!existsSync(dir)) {
28306
- mkdirSync(dir, { recursive: true });
28307
- }
28308
- writeFileSync(this.configPath, JSON.stringify(merged, null, 2) + "\n");
28309
- } catch {
28310
- }
28337
+ dailySpentToday() {
28338
+ return dailySpentToday(this.configDir);
28311
28339
  }
28312
28340
  };
28313
- var DEFAULT_CONFIG_DIR = join2(homedir(), ".t2000");
28341
+ var DEFAULT_CONFIG_DIR2 = join2(homedir(), ".t2000");
28314
28342
  var T2000 = class _T2000 extends import_index2.default {
28315
28343
  _signer;
28316
28344
  _keypair;
28317
28345
  client;
28318
28346
  _address;
28319
- enforcer;
28347
+ /** Unified spending-limit gate (per-tx + cumulative daily, USD). Shared by
28348
+ * CLI + MCP + programmatic writes — one gate, no bypass (R-0 Finding 1). */
28349
+ limits;
28320
28350
  constructor(keypairOrSigner, client, configDir, isSignerMode) {
28321
28351
  super();
28322
28352
  if (isSignerMode) {
@@ -28330,8 +28360,7 @@ var T2000 = class _T2000 extends import_index2.default {
28330
28360
  this._address = getAddress(kp);
28331
28361
  }
28332
28362
  this.client = client;
28333
- this.enforcer = new SafeguardEnforcer(configDir);
28334
- this.enforcer.load();
28363
+ this.limits = new LimitEnforcer(configDir);
28335
28364
  }
28336
28365
  static async create(options = {}) {
28337
28366
  const { keyPath, rpcUrl } = options;
@@ -28344,7 +28373,7 @@ var T2000 = class _T2000 extends import_index2.default {
28344
28373
  );
28345
28374
  }
28346
28375
  const keypair = await loadKey(void 0, keyPath);
28347
- return new _T2000(keypair, client, DEFAULT_CONFIG_DIR);
28376
+ return new _T2000(keypair, client, DEFAULT_CONFIG_DIR2);
28348
28377
  }
28349
28378
  static fromPrivateKey(privateKey, options = {}) {
28350
28379
  const keypair = keypairFromPrivateKey(privateKey);
@@ -28355,7 +28384,7 @@ var T2000 = class _T2000 extends import_index2.default {
28355
28384
  const keypair = generateKeypair();
28356
28385
  await saveKey(keypair, void 0, options.keyPath);
28357
28386
  const client = getSuiClient();
28358
- const agent = new _T2000(keypair, client, DEFAULT_CONFIG_DIR);
28387
+ const agent = new _T2000(keypair, client, DEFAULT_CONFIG_DIR2);
28359
28388
  const address = agent.address();
28360
28389
  return { agent, address };
28361
28390
  }
@@ -28377,11 +28406,10 @@ var T2000 = class _T2000 extends import_index2.default {
28377
28406
  }
28378
28407
  // -- MPP Payments --
28379
28408
  async pay(options) {
28380
- this.enforcer.assertNotLocked();
28381
- this.enforcer.check({ operation: "pay", amount: options.maxPrice ?? 1 });
28409
+ this.limits.assert({ operation: "pay", amountUsd: options.maxPrice ?? 0, force: options.force });
28382
28410
  const result = await payWithMpp({ signer: this._signer, client: this.client, options });
28383
28411
  if (result.paid) {
28384
- this.enforcer.recordUsage(result.cost ?? options.maxPrice ?? 1);
28412
+ this.limits.record(result.cost ?? options.maxPrice ?? 0);
28385
28413
  }
28386
28414
  return result;
28387
28415
  }
@@ -28395,7 +28423,11 @@ var T2000 = class _T2000 extends import_index2.default {
28395
28423
  // and the S.323 build-tracker entry.
28396
28424
  // -- Swap --
28397
28425
  async swap(params) {
28398
- this.enforcer.assertNotLocked();
28426
+ this.limits.assert({
28427
+ operation: "swap",
28428
+ amountUsd: approxUsdValue(params.from, params.amount) ?? 0,
28429
+ force: params.force
28430
+ });
28399
28431
  const { findSwapRoute: findSwapRoute2, buildSwapTx: buildSwapTx2, resolveTokenType: resolveTokenType2 } = await Promise.resolve().then(() => (init_cetus_swap(), cetus_swap_exports));
28400
28432
  const fromType = resolveTokenType2(params.from);
28401
28433
  const toType = resolveTokenType2(params.to);
@@ -28487,6 +28519,7 @@ var T2000 = class _T2000 extends import_index2.default {
28487
28519
  const fromName = resolveSymbol(fromType);
28488
28520
  const toName = resolveSymbol(toType);
28489
28521
  const routeDesc = route.routerData.paths?.map((p) => p.provider).filter(Boolean).slice(0, 3).join(" + ") ?? "Cetus Aggregator";
28522
+ this.limits.record(approxUsdValue(params.from, params.amount) ?? 0);
28490
28523
  return {
28491
28524
  success: true,
28492
28525
  tx: gasResult.digest,
@@ -28543,7 +28576,6 @@ var T2000 = class _T2000 extends import_index2.default {
28543
28576
  * the "build via gRPC, execute via JSON-RPC" hybrid).
28544
28577
  */
28545
28578
  async send(params) {
28546
- this.enforcer.assertNotLocked();
28547
28579
  const asset = params.asset;
28548
28580
  if (!asset) {
28549
28581
  throw new T2000Error(
@@ -28553,6 +28585,11 @@ var T2000 = class _T2000 extends import_index2.default {
28553
28585
  }
28554
28586
  assertAllowedAsset("send", asset);
28555
28587
  const sendableAsset = asset;
28588
+ this.limits.assert({
28589
+ operation: "send",
28590
+ amountUsd: approxUsdValue(sendableAsset, params.amount) ?? 0,
28591
+ force: params.force
28592
+ });
28556
28593
  const resolved = await this.resolveRecipient(params.to);
28557
28594
  const sendAmount = params.amount;
28558
28595
  const sendTo = resolved.address;
@@ -28564,7 +28601,7 @@ var T2000 = class _T2000 extends import_index2.default {
28564
28601
  () => buildSendTx({ client: this.client, address: this._address, to: sendTo, amount: sendAmount, asset: sendableAsset }),
28565
28602
  { buildClient }
28566
28603
  );
28567
- this.enforcer.recordUsage(sendAmount);
28604
+ this.limits.record(approxUsdValue(sendableAsset, sendAmount) ?? 0);
28568
28605
  const balance = await this.balance();
28569
28606
  this.emitBalanceChange(sendableAsset, sendAmount, "send", gasResult.digest);
28570
28607
  return {
@@ -29753,13 +29790,13 @@ var PromisePolyfill = class extends Promise {
29753
29790
  // Available starting from Node 22
29754
29791
  // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/withResolvers
29755
29792
  static withResolver() {
29756
- let resolve3;
29793
+ let resolve2;
29757
29794
  let reject;
29758
29795
  const promise = new Promise((res, rej) => {
29759
- resolve3 = res;
29796
+ resolve2 = res;
29760
29797
  reject = rej;
29761
29798
  });
29762
- return { promise, resolve: resolve3, reject };
29799
+ return { promise, resolve: resolve2, reject };
29763
29800
  }
29764
29801
  };
29765
29802
 
@@ -29793,7 +29830,7 @@ function createPrompt(view) {
29793
29830
  output
29794
29831
  });
29795
29832
  const screen = new ScreenManager(rl);
29796
- const { promise, resolve: resolve3, reject } = PromisePolyfill.withResolver();
29833
+ const { promise, resolve: resolve2, reject } = PromisePolyfill.withResolver();
29797
29834
  const cancel = () => reject(new CancelPromptError());
29798
29835
  if (signal) {
29799
29836
  const abort = () => reject(new AbortPromptError({ cause: signal.reason }));
@@ -29820,7 +29857,7 @@ function createPrompt(view) {
29820
29857
  cycle(() => {
29821
29858
  try {
29822
29859
  const nextView = view(config, (value) => {
29823
- setImmediate(() => resolve3(value));
29860
+ setImmediate(() => resolve2(value));
29824
29861
  });
29825
29862
  if (nextView === void 0) {
29826
29863
  const callerFilename = callSites[1]?.getFileName();
@@ -31132,7 +31169,9 @@ async function askHidden(message) {
31132
31169
  }
31133
31170
 
31134
31171
  // src/commands/init.ts
31135
- var NO_LIMITS_FOOTER = "No spending limits set. Run `t2 limit set --daily <usd>` to add them.";
31172
+ var DEFAULT_PER_TX_USD = 25;
31173
+ var DEFAULT_DAILY_USD = 100;
31174
+ var limitsFooter = (perTx, daily) => `Spending limits ON: $${perTx}/tx, $${daily}/day (cumulative). Change with \`t2 limit set\`, or override a single call with --force.`;
31136
31175
  function registerInit(program3) {
31137
31176
  program3.command("init").description("Create a new Agent Wallet (no PIN; plain Bech32 key file with 0o600 perms)").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").option(
31138
31177
  "--import [secret]",
@@ -31159,8 +31198,16 @@ function registerInit(program3) {
31159
31198
  await saveKey(keypair, void 0, opts.key);
31160
31199
  address = getAddress(keypair);
31161
31200
  }
31201
+ if (!hasLimits()) {
31202
+ setLimits({ perTxUsd: DEFAULT_PER_TX_USD, dailyUsd: DEFAULT_DAILY_USD });
31203
+ }
31162
31204
  if (isJsonMode()) {
31163
- printJson({ address, imported, configPath: opts.key ?? "~/.t2000/wallet.key" });
31205
+ printJson({
31206
+ address,
31207
+ imported,
31208
+ configPath: opts.key ?? "~/.t2000/wallet.key",
31209
+ limits: { perTxUsd: DEFAULT_PER_TX_USD, dailyUsd: DEFAULT_DAILY_USD }
31210
+ });
31164
31211
  return;
31165
31212
  }
31166
31213
  printBlank();
@@ -31168,7 +31215,7 @@ function registerInit(program3) {
31168
31215
  printKeyValue("Address", address);
31169
31216
  printKeyValue("Path", opts.key ?? "~/.t2000/wallet.key");
31170
31217
  printBlank();
31171
- printLine(`\u26A0 ${NO_LIMITS_FOOTER}`);
31218
+ printLine(`\u26A0 ${limitsFooter(DEFAULT_PER_TX_USD, DEFAULT_DAILY_USD)}`);
31172
31219
  printBlank();
31173
31220
  try {
31174
31221
  await T2000.create({ keyPath: opts.key });
@@ -31387,127 +31434,6 @@ function registerHistory(program3) {
31387
31434
 
31388
31435
  // src/commands/send.ts
31389
31436
  var import_picocolors5 = __toESM(require_picocolors(), 1);
31390
-
31391
- // src/lib/config-store.ts
31392
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, access as access2 } from "fs/promises";
31393
- import { dirname as dirname2, resolve as resolve2 } from "path";
31394
- import { homedir as homedir2 } from "os";
31395
- var DEFAULT_CONFIG_PATH = "~/.t2000/config.json";
31396
- function expandPath2(p) {
31397
- if (p.startsWith("~")) return resolve2(homedir2(), p.slice(2));
31398
- return resolve2(p);
31399
- }
31400
- async function readConfig(configPath) {
31401
- const filePath = expandPath2(configPath ?? DEFAULT_CONFIG_PATH);
31402
- try {
31403
- const content = await readFile2(filePath, "utf-8");
31404
- const parsed = JSON.parse(content);
31405
- return sanitizeConfig(parsed);
31406
- } catch {
31407
- return {};
31408
- }
31409
- }
31410
- async function writeConfig(config, configPath) {
31411
- const filePath = expandPath2(configPath ?? DEFAULT_CONFIG_PATH);
31412
- await mkdir2(dirname2(filePath), { recursive: true });
31413
- const sanitized = sanitizeConfig(config);
31414
- await writeFile2(filePath, JSON.stringify(sanitized, null, 2), { mode: 384 });
31415
- return filePath;
31416
- }
31417
- function setLimits(config, limits) {
31418
- const merged = { ...config.limits };
31419
- if (limits.perTxUsd !== void 0) merged.perTxUsd = limits.perTxUsd;
31420
- if (limits.dailySendUsd !== void 0) merged.dailySendUsd = limits.dailySendUsd;
31421
- return { ...config, limits: merged };
31422
- }
31423
- function clearLimits(config) {
31424
- const { limits: _, ...rest } = config;
31425
- return rest;
31426
- }
31427
- function hasLimits(config) {
31428
- return config.limits !== void 0 && (config.limits.perTxUsd !== void 0 || config.limits.dailySendUsd !== void 0);
31429
- }
31430
- function sanitizeConfig(raw) {
31431
- if (typeof raw !== "object" || raw === null) return {};
31432
- const r = raw;
31433
- const out = {};
31434
- if (typeof r.limits === "object" && r.limits !== null) {
31435
- const l = r.limits;
31436
- const limits = {};
31437
- if (typeof l.perTxUsd === "number" && l.perTxUsd > 0) limits.perTxUsd = l.perTxUsd;
31438
- if (typeof l.dailySendUsd === "number" && l.dailySendUsd > 0) limits.dailySendUsd = l.dailySendUsd;
31439
- if (limits.perTxUsd !== void 0 || limits.dailySendUsd !== void 0) out.limits = limits;
31440
- }
31441
- return out;
31442
- }
31443
-
31444
- // src/commands/limit/enforce.ts
31445
- var LimitExceededError = class extends Error {
31446
- code = "LIMIT_EXCEEDED";
31447
- limit;
31448
- limitKind;
31449
- attempted;
31450
- operation;
31451
- constructor(params) {
31452
- const label = params.limitKind === "perTxUsd" ? "per-transaction limit" : "daily limit";
31453
- super(
31454
- `Exceeds ${label} ($${params.limit}). Attempted $${params.attempted.toFixed(2)}. Use --force to override.`
31455
- );
31456
- this.name = "LimitExceededError";
31457
- this.operation = params.operation;
31458
- this.limitKind = params.limitKind;
31459
- this.limit = params.limit;
31460
- this.attempted = params.attempted;
31461
- }
31462
- toJSON() {
31463
- return {
31464
- error: this.code,
31465
- message: this.message,
31466
- operation: this.operation,
31467
- limitKind: this.limitKind,
31468
- limit: this.limit,
31469
- attempted: this.attempted
31470
- };
31471
- }
31472
- };
31473
- function assertLimitConfig(input) {
31474
- if (input.force) return;
31475
- if (!Number.isFinite(input.amountUsd) || input.amountUsd <= 0) return;
31476
- const limits = input.config.limits;
31477
- if (!limits) return;
31478
- if (limits.perTxUsd !== void 0 && input.amountUsd > limits.perTxUsd) {
31479
- throw new LimitExceededError({
31480
- operation: input.operation,
31481
- limitKind: "perTxUsd",
31482
- limit: limits.perTxUsd,
31483
- attempted: input.amountUsd
31484
- });
31485
- }
31486
- if (input.operation === "send" && limits.dailySendUsd !== void 0 && input.amountUsd > limits.dailySendUsd) {
31487
- throw new LimitExceededError({
31488
- operation: input.operation,
31489
- limitKind: "dailySendUsd",
31490
- limit: limits.dailySendUsd,
31491
- attempted: input.amountUsd
31492
- });
31493
- }
31494
- }
31495
- async function assertWithinLimits(input) {
31496
- const config = await readConfig(input.configPath);
31497
- assertLimitConfig({
31498
- config,
31499
- operation: input.operation,
31500
- amountUsd: input.amountUsd,
31501
- force: input.force
31502
- });
31503
- }
31504
- function approxUsdValue(asset, amount) {
31505
- const symbol = asset.toUpperCase();
31506
- if (symbol === "USDC" || symbol === "USDSUI") return amount;
31507
- return null;
31508
- }
31509
-
31510
- // src/commands/send.ts
31511
31437
  var ACCEPTED_ASSETS = ["USDC", "USDsui", "SUI"];
31512
31438
  var ACCEPTED_ASSETS_LIST = ACCEPTED_ASSETS.join(", ");
31513
31439
  function parseSendArgs(args) {
@@ -31563,10 +31489,6 @@ Examples:
31563
31489
  ).action(async (amount, args, opts) => {
31564
31490
  try {
31565
31491
  const { amount: parsedAmount, asset, recipient } = parseSendArgs([amount, ...args]);
31566
- const usdValue = approxUsdValue(asset, parsedAmount);
31567
- if (usdValue !== null) {
31568
- await assertWithinLimits({ operation: "send", amountUsd: usdValue, force: opts.force });
31569
- }
31570
31492
  const agent = await withAgent({ keyPath: opts.key });
31571
31493
  const result = await agent.send({
31572
31494
  to: recipient,
@@ -31574,7 +31496,8 @@ Examples:
31574
31496
  // The CLI parser already narrowed asset to USDC / USDsui / SUI;
31575
31497
  // the SDK accepts `SupportedAsset` and re-validates via
31576
31498
  // `assertAllowedAsset('send', …)` at runtime.
31577
- asset
31499
+ asset,
31500
+ force: opts.force
31578
31501
  });
31579
31502
  if (isJsonMode()) {
31580
31503
  printJson({
@@ -31629,16 +31552,6 @@ Examples:
31629
31552
  async (amountStr, from, to, opts) => {
31630
31553
  try {
31631
31554
  const parsed = parseSwapArgs(amountStr, from, to);
31632
- if (!opts.quote) {
31633
- const usdValue = approxUsdValue(parsed.from, parsed.amount);
31634
- if (usdValue !== null) {
31635
- await assertWithinLimits({
31636
- operation: "swap",
31637
- amountUsd: usdValue,
31638
- force: opts.force
31639
- });
31640
- }
31641
- }
31642
31555
  const agent = await withAgent({ keyPath: opts.key });
31643
31556
  if (opts.quote) {
31644
31557
  const quote = await agent.swapQuote({
@@ -31665,7 +31578,8 @@ Examples:
31665
31578
  from: parsed.from,
31666
31579
  to: parsed.to,
31667
31580
  amount: parsed.amount,
31668
- slippage
31581
+ slippage,
31582
+ force: opts.force
31669
31583
  });
31670
31584
  if (isJsonMode()) {
31671
31585
  printJson(result);
@@ -31718,7 +31632,6 @@ Examples:
31718
31632
  if (Number.isNaN(maxPrice) || maxPrice <= 0) {
31719
31633
  throw new Error(`Invalid --max-price: "${opts.maxPrice}". Must be a positive number.`);
31720
31634
  }
31721
- await assertWithinLimits({ operation: "pay", amountUsd: maxPrice, force: opts.force });
31722
31635
  const agent = await withAgent({ keyPath: opts.key });
31723
31636
  const startTime = Date.now();
31724
31637
  const method = opts.data && opts.method === "GET" ? "POST" : opts.method;
@@ -31731,7 +31644,8 @@ Examples:
31731
31644
  method,
31732
31645
  headers: opts.header,
31733
31646
  body: opts.data,
31734
- maxPrice
31647
+ maxPrice,
31648
+ force: opts.force
31735
31649
  });
31736
31650
  const elapsed = Date.now() - startTime;
31737
31651
  if (isJsonMode()) {
@@ -32071,9 +31985,8 @@ var import_picocolors10 = __toESM(require_picocolors(), 1);
32071
31985
  function registerLimitShow(parent) {
32072
31986
  parent.command("show").description("Show current spending limits").action(async (opts) => {
32073
31987
  try {
32074
- const config = await readConfig(opts.configPath);
32075
- const limits = config.limits ?? {};
32076
- const hasAny = limits.perTxUsd !== void 0 || limits.dailySendUsd !== void 0;
31988
+ const limits = getLimits(opts.configDir) ?? {};
31989
+ const hasAny = limits.perTxUsd !== void 0 || limits.dailyUsd !== void 0;
32077
31990
  if (isJsonMode()) {
32078
31991
  printJson({
32079
31992
  limits: hasAny ? limits : null,
@@ -32093,8 +32006,8 @@ function registerLimitShow(parent) {
32093
32006
  if (limits.perTxUsd !== void 0) {
32094
32007
  printKeyValue("Per-transaction", import_picocolors10.default.green(`$${limits.perTxUsd}`));
32095
32008
  }
32096
- if (limits.dailySendUsd !== void 0) {
32097
- printKeyValue("Daily send", import_picocolors10.default.green(`$${limits.dailySendUsd}`));
32009
+ if (limits.dailyUsd !== void 0) {
32010
+ printKeyValue("Daily (cumulative)", import_picocolors10.default.green(`$${limits.dailyUsd}`));
32098
32011
  }
32099
32012
  printBlank();
32100
32013
  printInfo("Use `--force` on `t2 send` / `t2 swap` / `t2 pay` to override per-call.");
@@ -32117,7 +32030,7 @@ function parseLimitSetArgs(opts) {
32117
32030
  }
32118
32031
  const limits = {};
32119
32032
  if (perTx !== void 0) limits.perTxUsd = perTx;
32120
- if (daily !== void 0) limits.dailySendUsd = daily;
32033
+ if (daily !== void 0) limits.dailyUsd = daily;
32121
32034
  return limits;
32122
32035
  }
32123
32036
  function parseUsdFlag(flag, raw) {
@@ -32128,31 +32041,30 @@ function parseUsdFlag(flag, raw) {
32128
32041
  return value;
32129
32042
  }
32130
32043
  function registerLimitSet(parent) {
32131
- parent.command("set").description("Set spending limits (opt-in; either flag is optional)").option("--per-tx <usd>", "Max USD per single transaction (send | swap | pay)").option("--daily <usd>", "Max USD per send transaction (applies to `t2 send` only)").addHelpText(
32044
+ parent.command("set").description("Set spending limits (opt-in; either flag is optional)").option("--per-tx <usd>", "Max USD per single transaction (send | swap | pay)").option("--daily <usd>", "Max CUMULATIVE USD spend per day across all writes").addHelpText(
32132
32045
  "after",
32133
32046
  `
32134
32047
  Examples:
32135
32048
  $ t2 limit set --per-tx 50 Cap every write at $50
32136
- $ t2 limit set --daily 100 Cap every send at $100
32049
+ $ t2 limit set --daily 100 Cap total spend at $100/day (cumulative)
32137
32050
  $ t2 limit set --per-tx 50 --daily 100 Set both
32138
32051
  `
32139
32052
  ).action(async (opts) => {
32140
32053
  try {
32141
32054
  const limits = parseLimitSetArgs({ perTx: opts.perTx, daily: opts.daily });
32142
- const current = await readConfig(opts.configPath);
32143
- const next = setLimits(current, limits);
32144
- const filePath = await writeConfig(next, opts.configPath);
32055
+ setLimits(limits, opts.configDir);
32056
+ const next = getLimits(opts.configDir);
32145
32057
  if (isJsonMode()) {
32146
- printJson({ ok: true, configPath: filePath, limits: next.limits });
32058
+ printJson({ ok: true, limits: next });
32147
32059
  return;
32148
32060
  }
32149
32061
  printBlank();
32150
32062
  printSuccess("Spending limits updated.");
32151
- if (next.limits?.perTxUsd !== void 0) {
32152
- printKeyValue("Per-transaction", import_picocolors11.default.green(`$${next.limits.perTxUsd}`));
32063
+ if (next?.perTxUsd !== void 0) {
32064
+ printKeyValue("Per-transaction", import_picocolors11.default.green(`$${next.perTxUsd}`));
32153
32065
  }
32154
- if (next.limits?.dailySendUsd !== void 0) {
32155
- printKeyValue("Daily send", import_picocolors11.default.green(`$${next.limits.dailySendUsd}`));
32066
+ if (next?.dailyUsd !== void 0) {
32067
+ printKeyValue("Daily (cumulative)", import_picocolors11.default.green(`$${next.dailyUsd}`));
32156
32068
  }
32157
32069
  printBlank();
32158
32070
  printInfo("Use `t2 limit show` to view; `t2 limit reset` to clear.");
@@ -32167,8 +32079,7 @@ Examples:
32167
32079
  function registerLimitReset(parent) {
32168
32080
  parent.command("reset").description("Clear all spending limits").action(async (opts) => {
32169
32081
  try {
32170
- const current = await readConfig(opts.configPath);
32171
- if (!hasLimits(current)) {
32082
+ if (!hasLimits(opts.configDir)) {
32172
32083
  if (isJsonMode()) {
32173
32084
  printJson({ ok: true, cleared: false, reason: "no limits were set" });
32174
32085
  return;
@@ -32178,10 +32089,9 @@ function registerLimitReset(parent) {
32178
32089
  printBlank();
32179
32090
  return;
32180
32091
  }
32181
- const next = clearLimits(current);
32182
- const filePath = await writeConfig(next, opts.configPath);
32092
+ clearLimits(opts.configDir);
32183
32093
  if (isJsonMode()) {
32184
- printJson({ ok: true, cleared: true, configPath: filePath });
32094
+ printJson({ ok: true, cleared: true });
32185
32095
  return;
32186
32096
  }
32187
32097
  printBlank();
@@ -32216,7 +32126,7 @@ function registerMcpStart(parent) {
32216
32126
  parent.command("start", { isDefault: true }).description("Start MCP server (stdio transport \u2014 for AI client integration)").option("--key <path>", "Custom wallet path (default ~/.t2000/wallet.key)").action(async (opts) => {
32217
32127
  let mod2;
32218
32128
  try {
32219
- mod2 = await import("./dist-3PYU4RZC.js");
32129
+ mod2 = await import("./dist-U65BYMC5.js");
32220
32130
  } catch {
32221
32131
  console.error("MCP server not installed. Run:\n npm install -g @t2000/mcp");
32222
32132
  process.exit(1);
@@ -32226,9 +32136,9 @@ function registerMcpStart(parent) {
32226
32136
  }
32227
32137
 
32228
32138
  // src/commands/mcp/platforms.ts
32229
- import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
32230
- import { join as join3, dirname as dirname3 } from "path";
32231
- import { homedir as homedir3 } from "os";
32139
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
32140
+ import { join as join3, dirname as dirname2 } from "path";
32141
+ import { homedir as homedir2 } from "os";
32232
32142
  import { existsSync as existsSync2 } from "fs";
32233
32143
  var MCP_SERVER_ENTRY = {
32234
32144
  command: "t2000",
@@ -32236,7 +32146,7 @@ var MCP_SERVER_ENTRY = {
32236
32146
  };
32237
32147
  var MCP_SERVER_KEY = "t2000";
32238
32148
  function getPlatformConfigs() {
32239
- const home = homedir3();
32149
+ const home = homedir2();
32240
32150
  return [
32241
32151
  {
32242
32152
  name: "Claude Desktop",
@@ -32257,18 +32167,18 @@ function getPlatformConfigs() {
32257
32167
  }
32258
32168
  async function readJsonFile(path2) {
32259
32169
  try {
32260
- const content = await readFile3(path2, "utf-8");
32170
+ const content = await readFile2(path2, "utf-8");
32261
32171
  return JSON.parse(content);
32262
32172
  } catch {
32263
32173
  return {};
32264
32174
  }
32265
32175
  }
32266
32176
  async function writeJsonFile(path2, data) {
32267
- const dir = dirname3(path2);
32177
+ const dir = dirname2(path2);
32268
32178
  if (!existsSync2(dir)) {
32269
- await mkdir3(dir, { recursive: true });
32179
+ await mkdir2(dir, { recursive: true });
32270
32180
  }
32271
- await writeFile3(path2, JSON.stringify(data, null, 2) + "\n", "utf-8");
32181
+ await writeFile2(path2, JSON.stringify(data, null, 2) + "\n", "utf-8");
32272
32182
  }
32273
32183
  function withMcpEntry(config) {
32274
32184
  return {
@@ -32393,7 +32303,7 @@ Subcommands:
32393
32303
  // src/commands/skills/lib.ts
32394
32304
  import { stat } from "fs/promises";
32395
32305
  import { join as join4 } from "path";
32396
- import { homedir as homedir4 } from "os";
32306
+ import { homedir as homedir3 } from "os";
32397
32307
  var MANIFEST_URL = "https://t2000.ai/.well-known/agent-skills/index.json";
32398
32308
  var SKILL_PREFIXES = ["t2000-", "mpp-"];
32399
32309
  function isManagedSkillName(name) {
@@ -32401,7 +32311,7 @@ function isManagedSkillName(name) {
32401
32311
  }
32402
32312
  var SKILL_TARGETS = ["agents", "cursor", "claude-code"];
32403
32313
  function resolveTargetDir(target, useGlobal, cwd = process.cwd()) {
32404
- const root = useGlobal ? homedir4() : cwd;
32314
+ const root = useGlobal ? homedir3() : cwd;
32405
32315
  switch (target) {
32406
32316
  case "agents":
32407
32317
  return join4(root, ".agents", "skills");
@@ -32489,7 +32399,7 @@ function registerSkillsList(parent) {
32489
32399
  }
32490
32400
 
32491
32401
  // src/commands/skills/install.ts
32492
- import { writeFile as writeFile4, mkdir as mkdir4 } from "fs/promises";
32402
+ import { writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
32493
32403
  import { join as join5 } from "path";
32494
32404
  function registerSkillsInstall(parent) {
32495
32405
  parent.command("install [slug]").description("Install all skills (or one named slug) into a local directory").option("--target <name>", "Target client layout: agents (default), cursor, claude-code", "agents").option("--global", "Install to ~/.<target>/ instead of <cwd>/.<target>/").action(async (slug, opts) => {
@@ -32505,15 +32415,15 @@ function registerSkillsInstall(parent) {
32505
32415
  );
32506
32416
  }
32507
32417
  const targetDir = resolveTargetDir(target, useGlobal);
32508
- await mkdir4(targetDir, { recursive: true });
32418
+ await mkdir3(targetDir, { recursive: true });
32509
32419
  const installed = [];
32510
32420
  for (const s of toInstall) {
32511
32421
  const raw = await fetchSkill(s.url);
32512
32422
  const transformed = transformForTarget(raw, target, s.description);
32513
32423
  const relPath = filenameForTarget(s.name, target);
32514
32424
  const fullPath = join5(targetDir, relPath);
32515
- await mkdir4(join5(fullPath, ".."), { recursive: true });
32516
- await writeFile4(fullPath, transformed, "utf-8");
32425
+ await mkdir3(join5(fullPath, ".."), { recursive: true });
32426
+ await writeFile3(fullPath, transformed, "utf-8");
32517
32427
  installed.push({ name: s.name, path: fullPath });
32518
32428
  }
32519
32429
  if (isJsonMode()) {