@vibeframe/cli 0.84.0 → 0.85.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.
Files changed (2) hide show
  1. package/dist/index.js +103 -17
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -51503,22 +51503,30 @@ var init_key_live_test = __esm({
51503
51503
  signal
51504
51504
  )
51505
51505
  },
51506
- // The three below still have no cheap authenticated GET as of v0.83:
51507
- // fal lacks a documented validation endpoint, Kling auth is per-
51508
- // request HMAC-signed, ImgBB's only authenticated route is image
51509
- // upload. Surface them as `skipped` so the user knows we didn't
51510
- // blank-check.
51506
+ // The three below still have no cheap authenticated GET as of
51507
+ // 2026-04-30 (re-checked while shipping v0.85). Surface them as
51508
+ // `skipped` so the user knows we didn't blank-check; revisit when a
51509
+ // provider publishes an account/me endpoint.
51511
51510
  fal: {
51511
+ // Probed: /api/account, /v1/account, /v1/me, /queue/health, /v1/users/me,
51512
+ // /api/users/me, /v1/billing — all 404 with the standard `Authorization:
51513
+ // Key <key>` header. The platform docs reference an "Accounts API" but
51514
+ // its endpoint paths are unpublished as of this check.
51512
51515
  name: "fal.ai",
51513
- test: async () => ({ ok: false, skipped: true, message: "no auth-only endpoint published" })
51516
+ test: async () => ({ ok: false, skipped: true, message: "no auth-only endpoint (checked 2026-04-30)" })
51514
51517
  },
51515
51518
  kling: {
51519
+ // Per-request HMAC signing means we'd need to sign every probe with the
51520
+ // key's secret half plus a timestamp — possible, but the cheapest
51521
+ // signed endpoint is also a generation request. Not worth the code.
51516
51522
  name: "Kling",
51517
- test: async () => ({ ok: false, skipped: true, message: "per-request HMAC; can't test cheaply" })
51523
+ test: async () => ({ ok: false, skipped: true, message: "HMAC-signed per request (checked 2026-04-30)" })
51518
51524
  },
51519
51525
  imgbb: {
51526
+ // Only documented route is POST /1/upload. Probing it would consume an
51527
+ // upload quota even if we send a 1×1 PNG, so SKIP is the right call.
51520
51528
  name: "ImgBB",
51521
- test: async () => ({ ok: false, skipped: true, message: "only auth route is image upload" })
51529
+ test: async () => ({ ok: false, skipped: true, message: "only route is upload (checked 2026-04-30)" })
51522
51530
  }
51523
51531
  };
51524
51532
  }
@@ -62757,6 +62765,10 @@ function toAgentDefinition(t) {
62757
62765
  return {
62758
62766
  name: t.name,
62759
62767
  description: t.description,
62768
+ // Propagate the manifest's cost tier so the agent's executor can
62769
+ // mandate a confirm prompt for high-spend tools without needing a
62770
+ // parallel SSOT.
62771
+ cost: t.cost,
62760
62772
  parameters: {
62761
62773
  type: "object",
62762
62774
  properties,
@@ -62825,7 +62837,7 @@ init_source();
62825
62837
  // package.json
62826
62838
  var package_default = {
62827
62839
  name: "@vibeframe/cli",
62828
- version: "0.84.0",
62840
+ version: "0.85.0",
62829
62841
  description: "VibeFrame CLI - natural language video editing from the terminal",
62830
62842
  private: false,
62831
62843
  license: "MIT",
@@ -72448,10 +72460,26 @@ function maskSensitiveArgs(args) {
72448
72460
  }
72449
72461
  return masked;
72450
72462
  }
72463
+ function estimateToolCost(cost) {
72464
+ if (!cost) return 0;
72465
+ switch (cost) {
72466
+ case "free":
72467
+ return 0;
72468
+ case "low":
72469
+ return 0.05;
72470
+ case "medium":
72471
+ return 0.5;
72472
+ case "high":
72473
+ return 3;
72474
+ case "very-high":
72475
+ return 25;
72476
+ }
72477
+ }
72451
72478
  var AgentExecutor = class {
72452
72479
  adapter = null;
72453
72480
  registry;
72454
72481
  memory;
72482
+ cumulativeUsd = 0;
72455
72483
  context;
72456
72484
  config;
72457
72485
  initialized = false;
@@ -72539,12 +72567,39 @@ var AgentExecutor = class {
72539
72567
  }
72540
72568
  /**
72541
72569
  * Execute a single tool
72570
+ *
72571
+ * Confirm-prompt logic:
72572
+ * - `noConfirm: true` → never prompt (automation / CI)
72573
+ * - `confirmAlways: true` → prompt for every tool call
72574
+ * - default → prompt only when the tool's
72575
+ * cost is high/very-high (the
72576
+ * `costGate`)
72577
+ *
72578
+ * The two non-trivial cases require `confirmCallback` to be set; if
72579
+ * it isn't, no prompt fires and the tool runs.
72542
72580
  */
72543
72581
  async executeTool(toolCall) {
72544
- if (this.config.confirmCallback) {
72582
+ const def = this.registry.getDefinition(toolCall.name);
72583
+ const cost = def?.cost;
72584
+ const isExpensive = cost === "high" || cost === "very-high";
72585
+ if (this.config.budgetUsd !== void 0) {
72586
+ const estimate = estimateToolCost(cost);
72587
+ if (this.cumulativeUsd + estimate > this.config.budgetUsd) {
72588
+ const remaining = (this.config.budgetUsd - this.cumulativeUsd).toFixed(2);
72589
+ return {
72590
+ toolCallId: toolCall.id,
72591
+ success: false,
72592
+ output: "",
72593
+ error: `Budget exceeded: ${toolCall.name} estimated at $${estimate.toFixed(2)} but only $${remaining} remains of $${this.config.budgetUsd.toFixed(2)} cap.`
72594
+ };
72595
+ }
72596
+ }
72597
+ const shouldConfirm = !this.config.noConfirm && this.config.confirmCallback && (this.config.confirmAlways || isExpensive);
72598
+ if (shouldConfirm && this.config.confirmCallback) {
72545
72599
  const confirmed = await this.config.confirmCallback(
72546
72600
  toolCall.name,
72547
- toolCall.arguments
72601
+ toolCall.arguments,
72602
+ cost
72548
72603
  );
72549
72604
  if (!confirmed) {
72550
72605
  return {
@@ -72560,8 +72615,18 @@ var AgentExecutor = class {
72560
72615
  this.context
72561
72616
  );
72562
72617
  result.toolCallId = toolCall.id;
72618
+ if (result.success) {
72619
+ this.cumulativeUsd += estimateToolCost(cost);
72620
+ }
72563
72621
  return result;
72564
72622
  }
72623
+ /**
72624
+ * Cumulative tier-estimated USD spent in this session. Updates after
72625
+ * every successful tool call. Useful for status output / `tools` REPL.
72626
+ */
72627
+ getCumulativeUsd() {
72628
+ return this.cumulativeUsd;
72629
+ }
72565
72630
  /**
72566
72631
  * Update context (e.g., when project changes)
72567
72632
  */
@@ -72601,7 +72666,7 @@ init_config();
72601
72666
  init_tty();
72602
72667
  init_api_key();
72603
72668
  init_output();
72604
- async function promptConfirm2(toolName, args) {
72669
+ async function promptConfirm2(toolName, args, cost) {
72605
72670
  const { createInterface: createInterface4 } = await import("node:readline");
72606
72671
  const rl = createInterface4({
72607
72672
  input: process.stdin,
@@ -72611,7 +72676,15 @@ async function promptConfirm2(toolName, args) {
72611
72676
  return new Promise((resolve64) => {
72612
72677
  const argsStr = JSON.stringify(args, null, 2);
72613
72678
  console.log();
72614
- console.log(source_default.yellow(`Execute ${source_default.bold(toolName)}?`));
72679
+ const tierColor = {
72680
+ "free": source_default.green,
72681
+ "low": source_default.cyan,
72682
+ "medium": source_default.yellow,
72683
+ "high": source_default.yellow,
72684
+ "very-high": source_default.red
72685
+ };
72686
+ const costBadge = cost ? ` ${tierColor[cost](`[${cost.toUpperCase()}]`)}` : "";
72687
+ console.log(source_default.yellow(`Execute ${source_default.bold(toolName)}?${costBadge}`));
72615
72688
  console.log(source_default.dim(argsStr));
72616
72689
  rl.question(source_default.cyan("(y/n): "), (answer) => {
72617
72690
  rl.close();
@@ -72621,7 +72694,12 @@ async function promptConfirm2(toolName, args) {
72621
72694
  }
72622
72695
  async function startAgent(options = {}) {
72623
72696
  const isNonInteractive = !!options.input;
72624
- const confirmMode = options.confirm || false;
72697
+ const confirmAlways = options.confirm || false;
72698
+ const noConfirm = options.noConfirm || false;
72699
+ const budgetUsd = options.budgetUsd ? parseFloat(options.budgetUsd) : void 0;
72700
+ if (budgetUsd !== void 0 && (Number.isNaN(budgetUsd) || budgetUsd < 0)) {
72701
+ exitWithError(generalError(`Invalid --budget-usd: ${options.budgetUsd}`, "Must be a non-negative number (e.g., --budget-usd 5)"));
72702
+ }
72625
72703
  if (!isNonInteractive && !hasTTY()) {
72626
72704
  exitWithError(generalError("Agent mode requires a terminal.", "Run 'vibe agent' directly from your terminal, or use --input <query> for non-interactive mode."));
72627
72705
  }
@@ -72658,7 +72736,13 @@ async function startAgent(options = {}) {
72658
72736
  maxTurns,
72659
72737
  verbose,
72660
72738
  projectPath: options.project,
72661
- confirmCallback: confirmMode ? promptConfirm2 : void 0
72739
+ // Always supply the callback so the cost gate (high/very-high
72740
+ // tools) can prompt even without `--confirm`. The executor
72741
+ // decides whether to invoke based on `confirmAlways` / `noConfirm`.
72742
+ confirmCallback: noConfirm ? void 0 : promptConfirm2,
72743
+ confirmAlways,
72744
+ noConfirm,
72745
+ budgetUsd
72662
72746
  });
72663
72747
  await agent.initialize();
72664
72748
  spinner2.succeed(source_default.green("Agent initialized"));
@@ -72695,9 +72779,11 @@ async function startAgent(options = {}) {
72695
72779
  console.log();
72696
72780
  console.log(source_default.dim(` v${version} \xB7 ${provider}${options.model ? ` \xB7 ${options.model}` : ""} \xB7 ${cwd}`));
72697
72781
  console.log();
72782
+ const confirmStatus = noConfirm ? source_default.dim("no-confirm") : confirmAlways ? source_default.yellow("confirm mode") : source_default.dim("cost gate (high/very-high)");
72698
72783
  const statusParts = [
72699
72784
  source_default.green(`${toolCount} tools`),
72700
- confirmMode ? source_default.yellow("confirm mode") : null,
72785
+ confirmStatus,
72786
+ budgetUsd !== void 0 ? source_default.magenta(`budget: $${budgetUsd.toFixed(2)}`) : null,
72701
72787
  options.project ? source_default.blue(`project: ${options.project}`) : null
72702
72788
  ].filter(Boolean);
72703
72789
  console.log(source_default.dim(" ") + statusParts.join(source_default.dim(" \xB7 ")));
@@ -72811,7 +72897,7 @@ async function startAgent(options = {}) {
72811
72897
  rl.prompt();
72812
72898
  });
72813
72899
  }
72814
- var agentCommand = new Command("agent").description("Start the AI agent with natural language interface").option("-p, --provider <provider>", "LLM provider (openai, claude, gemini, ollama, xai, openrouter)", "openai").option("-m, --model <model>", "Model to use (provider-specific)").option("--project <path>", "Timeline file or directory to load").option("-v, --verbose", "Show verbose output including tool calls").option("--max-turns <n>", "Maximum turns per request", "10").option("-i, --input <query>", "Run a single query and exit (non-interactive)").option("-c, --confirm", "Confirm before each tool execution").action(async (options) => {
72900
+ var agentCommand = new Command("agent").description("Start the AI agent with natural language interface").option("-p, --provider <provider>", "LLM provider (openai, claude, gemini, ollama, xai, openrouter)", "openai").option("-m, --model <model>", "Model to use (provider-specific)").option("--project <path>", "Timeline file or directory to load").option("-v, --verbose", "Show verbose output including tool calls").option("--max-turns <n>", "Maximum turns per request", "10").option("-i, --input <query>", "Run a single query and exit (non-interactive)").option("-c, --confirm", "Confirm before EVERY tool execution (overrides cost gate)").option("--no-confirm", "Disable all confirm prompts including the high/very-high cost gate (CI / automation)").option("--budget-usd <usd>", "Reject tool calls past this cumulative USD ceiling (tier-estimated, conservative)").action(async (options) => {
72815
72901
  await startAgent(options);
72816
72902
  });
72817
72903
  function getEnvVar(provider) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibeframe/cli",
3
- "version": "0.84.0",
3
+ "version": "0.85.0",
4
4
  "description": "VibeFrame CLI - natural language video editing from the terminal",
5
5
  "private": false,
6
6
  "license": "MIT",
@@ -183,8 +183,8 @@
183
183
  "tsx": "^4.21.0",
184
184
  "typescript": "^5.3.3",
185
185
  "vitest": "^1.2.2",
186
- "@vibeframe/ai-providers": "0.84.0",
187
- "@vibeframe/core": "0.84.0"
186
+ "@vibeframe/ai-providers": "0.85.0",
187
+ "@vibeframe/core": "0.85.0"
188
188
  },
189
189
  "engines": {
190
190
  "node": ">=20"