@mtreeai/msapling-cli 2.3.6-beta.21 → 2.3.6-beta.23

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 +238 -19
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -171,15 +171,20 @@ var init_src = __esm({
171
171
  }
172
172
  }
173
173
  async me() {
174
- const data = await this.request("/api/projects/overview");
174
+ const [overviewData, billingData] = await Promise.all([
175
+ this.request("/api/projects/overview"),
176
+ this.request("/api/billing/settings").catch(() => null)
177
+ ]);
178
+ const data = overviewData;
179
+ const fuel = billingData?.fuel ?? {};
175
180
  return {
176
181
  user: {
177
182
  id: data.user_id,
178
183
  email: data.email,
179
- tier: data.tier,
184
+ tier: billingData?.tier ?? data.tier,
180
185
  is_pro: data.is_pro,
181
- fuel_credits: data.fuel_credits,
182
- fuel_credits_purchased: data.fuel_credits_purchased || 0,
186
+ fuel_credits: typeof fuel.total === "number" ? fuel.total : data.fuel_credits,
187
+ fuel_credits_purchased: typeof fuel.purchased === "number" ? fuel.purchased : data.fuel_credits_purchased || 0,
183
188
  monthly_spending_limit: data.monthly_spending_limit || null,
184
189
  cost_byok: data.cost_byok || 0,
185
190
  tokens_byok: data.tokens_byok || 0,
@@ -203,14 +208,19 @@ var init_src = __esm({
203
208
  };
204
209
  }
205
210
  async getLiveUsage() {
206
- const data = await this.request("/api/usage/live");
211
+ const [billingData, overviewData] = await Promise.all([
212
+ this.request("/api/billing/settings").catch(() => null),
213
+ this.request("/api/projects/overview").catch(() => ({}))
214
+ ]);
215
+ const fuel = billingData?.fuel ?? {};
216
+ const data = overviewData;
207
217
  return {
208
218
  id: data.user_id,
209
219
  email: data.email,
210
- tier: data.tier,
220
+ tier: billingData?.tier ?? data.tier,
211
221
  is_pro: data.is_pro,
212
- fuel_credits: data.fuel_credits,
213
- fuel_credits_purchased: data.fuel_credits_purchased || 0,
222
+ fuel_credits: typeof fuel.total === "number" ? fuel.total : data.fuel_credits,
223
+ fuel_credits_purchased: typeof fuel.purchased === "number" ? fuel.purchased : data.fuel_credits_purchased || 0,
214
224
  monthly_spending_limit: data.monthly_spending_limit || null,
215
225
  cost_byok: data.cost_byok || 0,
216
226
  tokens_byok: data.tokens_byok || 0,
@@ -256,8 +266,45 @@ var init_src = __esm({
256
266
  body: JSON.stringify(symbol)
257
267
  });
258
268
  }
269
+ // CLI-PROJECT-CREATE-01 (Iter 34): create a new project. Per LAB
270
+ // projects.py:239 — POST /api/projects/ with {project_name}.
271
+ async createProject(name) {
272
+ return await this.request("/api/projects/", {
273
+ method: "POST",
274
+ body: JSON.stringify({ project_name: name })
275
+ });
276
+ }
277
+ // CLI-CHAT-CREATE-01 (Iter 34): create a new chat in a project. Per LAB
278
+ // projects.py:365 — POST /api/projects/chat/new with
279
+ // {project_name, slot_label?, chat_name?, model?, client_type?}.
280
+ async createChat(opts) {
281
+ return await this.request("/api/projects/chat/new", {
282
+ method: "POST",
283
+ body: JSON.stringify({
284
+ project_name: opts.projectName,
285
+ slot_label: opts.title,
286
+ chat_name: opts.title,
287
+ model: opts.model,
288
+ client_type: "cli"
289
+ })
290
+ });
291
+ }
292
+ // CLI-BROADCAST-01 (Iter 34): broadcast a prompt to sibling chats in a
293
+ // project. Per LAB broadcast.py:145 — POST /api/broadcast/execute with
294
+ // {project_name, prompt, source_chat_id?, max_targets?}.
295
+ async broadcastExecute(opts) {
296
+ return await this.request("/api/broadcast/execute", {
297
+ method: "POST",
298
+ body: JSON.stringify({
299
+ project_name: opts.projectName,
300
+ prompt: opts.prompt,
301
+ source_chat_id: opts.sourceChatId,
302
+ max_targets: opts.maxTargets
303
+ })
304
+ });
305
+ }
259
306
  async getModels() {
260
- return await this.request("/api/models");
307
+ return await this.request("/api/benchmark/models");
261
308
  }
262
309
  /**
263
310
  * Fetch the canonical tool registry from the backend.
@@ -8578,7 +8625,41 @@ function resolveChat(arg, chats) {
8578
8625
  }
8579
8626
  return { kind: "error", message: `no chat matches '${trimmed}'. Run /chat (no args) for the list.` };
8580
8627
  }
8628
+ async function createChat(args2, context) {
8629
+ let title = "";
8630
+ let model;
8631
+ for (let i = 0; i < args2.length; i++) {
8632
+ if (args2[i] === "--model" && i + 1 < args2.length) {
8633
+ model = args2[i + 1];
8634
+ i++;
8635
+ } else {
8636
+ title += (title ? " " : "") + args2[i];
8637
+ }
8638
+ }
8639
+ try {
8640
+ const overview = await context.client.me();
8641
+ const currentId = context.getProjectId();
8642
+ const project = overview.projects.find((p) => p.id === currentId) ?? overview.projects[0];
8643
+ if (!project) {
8644
+ context.addMessage("error", "No active project. Use /project new <name> or /project <id> first.");
8645
+ return;
8646
+ }
8647
+ const result = await context.client.createChat({
8648
+ projectName: project.name,
8649
+ title: title || void 0,
8650
+ model: model || void 0
8651
+ });
8652
+ context.addMessage("system", `Created chat "${result.title}" (${result.chat_id}) using ${result.model}`);
8653
+ context.setActiveChatId(result.chat_id);
8654
+ await context.refreshOverview();
8655
+ } catch (e) {
8656
+ context.addMessage("error", `Failed to create chat: ${e.message}`);
8657
+ }
8658
+ }
8581
8659
  async function listChats(args2, context) {
8660
+ if (args2[0] === "new" || args2[0] === "create") {
8661
+ return createChat(args2.slice(1), context);
8662
+ }
8582
8663
  const arg = args2.join(" ").trim();
8583
8664
  const result = await fetchChatsForCurrentProject(context);
8584
8665
  if ("error" in result) {
@@ -8633,6 +8714,74 @@ var init_chat = __esm({
8633
8714
  }
8634
8715
  });
8635
8716
 
8717
+ // src/commands/broadcast.ts
8718
+ var broadcastCommand;
8719
+ var init_broadcast = __esm({
8720
+ "src/commands/broadcast.ts"() {
8721
+ "use strict";
8722
+ init_esm_shims();
8723
+ broadcastCommand = {
8724
+ name: "broadcast",
8725
+ aliases: ["bcast"],
8726
+ args: "[--max N] [--exclude-current] <prompt>",
8727
+ description: "Send a prompt to every sibling chat in the active project (rate-limited 10/min).",
8728
+ category: "chat",
8729
+ handler: async (args2, context) => {
8730
+ let maxTargets = 8;
8731
+ let excludeCurrent = false;
8732
+ const promptParts = [];
8733
+ for (let i = 0; i < args2.length; i++) {
8734
+ if (args2[i] === "--max" && i + 1 < args2.length) {
8735
+ const n = Number(args2[i + 1]);
8736
+ if (Number.isFinite(n) && n > 0) maxTargets = Math.min(32, Math.floor(n));
8737
+ i++;
8738
+ continue;
8739
+ }
8740
+ if (args2[i] === "--exclude-current") {
8741
+ excludeCurrent = true;
8742
+ continue;
8743
+ }
8744
+ promptParts.push(args2[i]);
8745
+ }
8746
+ const prompt4 = promptParts.join(" ").trim();
8747
+ if (!prompt4) {
8748
+ context.addMessage("system", "Usage: /broadcast [--max N] [--exclude-current] <prompt>");
8749
+ return;
8750
+ }
8751
+ try {
8752
+ const overview = await context.client.me();
8753
+ const currentId = context.getProjectId();
8754
+ const project = overview.projects.find((p) => p.id === currentId) ?? overview.projects[0];
8755
+ if (!project) {
8756
+ context.addMessage("error", "No active project. Use /project <id> first.");
8757
+ return;
8758
+ }
8759
+ const result = await context.client.broadcastExecute({
8760
+ projectName: project.name,
8761
+ prompt: prompt4,
8762
+ sourceChatId: excludeCurrent ? context.activeChatId ?? void 0 : void 0,
8763
+ maxTargets
8764
+ });
8765
+ context.addMessage(
8766
+ "system",
8767
+ `Broadcast queued: ${result.broadcast_id} \u2192 ${result.target_chat_ids?.length ?? 0} chat(s) in project '${project.name}'.`
8768
+ );
8769
+ if (Array.isArray(result.target_chat_ids) && result.target_chat_ids.length > 0) {
8770
+ for (const cid of result.target_chat_ids.slice(0, 8)) {
8771
+ context.addMessage("system", ` - ${cid}`);
8772
+ }
8773
+ if (result.target_chat_ids.length > 8) {
8774
+ context.addMessage("system", ` \u2026and ${result.target_chat_ids.length - 8} more.`);
8775
+ }
8776
+ }
8777
+ } catch (e) {
8778
+ context.addMessage("error", `Broadcast failed: ${e.message}`);
8779
+ }
8780
+ }
8781
+ };
8782
+ }
8783
+ });
8784
+
8636
8785
  // src/commands/clear.ts
8637
8786
  var clearCommand;
8638
8787
  var init_clear = __esm({
@@ -8790,8 +8939,47 @@ Current: ${current}`);
8790
8939
  }
8791
8940
  return;
8792
8941
  }
8793
- context.setModel(newModel);
8794
- context.addMessage("system", `Model switched to: ${newModel}`);
8942
+ try {
8943
+ const models = await context.client.getModels();
8944
+ const list = models.filter((m) => !!m.id);
8945
+ const trimmed = newModel.trim();
8946
+ const exact = list.find((m) => m.id === trimmed);
8947
+ if (exact) {
8948
+ context.setModel(exact.id);
8949
+ context.addMessage("system", `Model switched to: ${exact.id}`);
8950
+ return;
8951
+ }
8952
+ if (/^\d+$/.test(trimmed)) {
8953
+ const idx = parseInt(trimmed, 10) - 1;
8954
+ if (idx >= 0 && idx < list.length && list[idx].id) {
8955
+ context.setModel(list[idx].id);
8956
+ context.addMessage("system", `Model switched to: ${list[idx].id}`);
8957
+ return;
8958
+ }
8959
+ }
8960
+ const lower = trimmed.toLowerCase();
8961
+ const subs = list.filter((m) => (m.id ?? "").toLowerCase().includes(lower));
8962
+ if (subs.length === 1) {
8963
+ context.setModel(subs[0].id);
8964
+ context.addMessage("system", `Model switched to: ${subs[0].id}`);
8965
+ return;
8966
+ }
8967
+ if (subs.length > 1 && subs.length <= 10) {
8968
+ context.addMessage(
8969
+ "error",
8970
+ `'${trimmed}' matches ${subs.length} models: ${subs.map((m) => m.id).join(", ")}`
8971
+ );
8972
+ return;
8973
+ }
8974
+ if (subs.length > 10) {
8975
+ context.addMessage("error", `'${trimmed}' matches ${subs.length} models \u2014 be more specific.`);
8976
+ return;
8977
+ }
8978
+ context.addMessage("error", `No model matches '${trimmed}'. Run /model (no args) for the list of ${list.length} models.`);
8979
+ } catch (e) {
8980
+ context.setModel(newModel);
8981
+ context.addMessage("system", `Model switched to: ${newModel} (unverified \u2014 model catalog unreachable: ${e.message})`);
8982
+ }
8795
8983
  }
8796
8984
  };
8797
8985
  }
@@ -8936,6 +9124,22 @@ var init_project = __esm({
8936
9124
  handler: async (args2, context) => {
8937
9125
  const arg = args2.join(" ").trim();
8938
9126
  const currentId = context.getProjectId() || null;
9127
+ if (args2[0] === "new" || args2[0] === "create") {
9128
+ const name = args2.slice(1).join(" ").trim();
9129
+ if (!name) {
9130
+ context.addMessage("system", "Usage: /project new <name>");
9131
+ return;
9132
+ }
9133
+ try {
9134
+ const result = await context.client.createProject(name);
9135
+ context.addMessage("system", `Created project "${result.project}" (${result.project_id})`);
9136
+ context.setProjectId(result.project_id);
9137
+ await context.refreshOverview();
9138
+ } catch (e) {
9139
+ context.addMessage("error", `Failed to create project: ${e.message}`);
9140
+ }
9141
+ return;
9142
+ }
8939
9143
  let projects = [];
8940
9144
  try {
8941
9145
  const overview = await context.client.me();
@@ -9462,7 +9666,7 @@ var init_benchmark = __esm({
9462
9666
  let models = modelArg ?? [];
9463
9667
  if (!models.length) {
9464
9668
  try {
9465
- const resp = await context.client.get("/api/models");
9669
+ const resp = await context.client.request("/api/benchmark/models");
9466
9670
  const list = Array.isArray(resp) ? resp : resp?.models ?? [];
9467
9671
  models = list.slice(0, 4).map((m) => m.id ?? m.model_id ?? "").filter(Boolean);
9468
9672
  } catch {
@@ -9688,14 +9892,14 @@ var init_status = __esm({
9688
9892
  context.addMessage("error", `Health check failed: ${e.message}`);
9689
9893
  }
9690
9894
  try {
9691
- const countData = await context.client["request"]("/api/stream/active-count");
9895
+ const countData = await context.client["request"]("/api/chat/stream/active-count");
9692
9896
  const count = countData?.count ?? countData?.active_count ?? 0;
9693
9897
  context.addMessage("system", `[INFO] Active Streams: ${count}`);
9694
9898
  } catch (e) {
9695
9899
  context.addMessage("system", ` Active Streams: unavailable (${e.message})`);
9696
9900
  }
9697
9901
  try {
9698
- const providerData = await context.client["request"]("/api/admin/provider-health");
9902
+ const providerData = await context.client["request"]("/api/provider/circuit-summary");
9699
9903
  const providers = providerData?.providers ?? (Array.isArray(providerData) ? providerData : []);
9700
9904
  if (providers.length > 0) {
9701
9905
  context.addMessage("system", " Provider Circuits:");
@@ -9866,7 +10070,7 @@ var init_version = __esm({
9866
10070
  description: "Show version information for CLI and core packages",
9867
10071
  category: "debug",
9868
10072
  handler: async (_args, context) => {
9869
- const cliVersion = true ? "2.3.6-beta.21" : "(dev)";
10073
+ const cliVersion = true ? "2.3.6-beta.23" : "(dev)";
9870
10074
  const coreVersion = true ? "2.3.2" : "(dev)";
9871
10075
  const runtime = process.version;
9872
10076
  context.addMessage("system", "MSapling Version Info");
@@ -9875,7 +10079,7 @@ var init_version = __esm({
9875
10079
  context.addMessage("system", row2("Core (@msapling/core)", coreVersion));
9876
10080
  context.addMessage("system", row2("Runtime (Node/Bun)", runtime));
9877
10081
  try {
9878
- const ts = "2026-05-28T18:59:22.883Z";
10082
+ const ts = "2026-05-28T19:20:38.494Z";
9879
10083
  if (ts && ts !== "__BUILD_TIMESTAMP__") {
9880
10084
  context.addMessage("system", row2("Build Timestamp", ts));
9881
10085
  }
@@ -10614,6 +10818,7 @@ var init_commands = __esm({
10614
10818
  init_exit();
10615
10819
  init_help();
10616
10820
  init_chat();
10821
+ init_broadcast();
10617
10822
  init_clear();
10618
10823
  init_mode();
10619
10824
  init_model();
@@ -10649,6 +10854,7 @@ var init_commands = __esm({
10649
10854
  helpCommand,
10650
10855
  chatCommand,
10651
10856
  chatsCommand,
10857
+ broadcastCommand,
10652
10858
  clearCommand,
10653
10859
  modeCommand,
10654
10860
  modelCommand,
@@ -10874,9 +11080,22 @@ async function apiFetch(path2, opts = {}) {
10874
11080
  return fetch(`${getBaseURL()}${path2}`, { ...opts, headers, body });
10875
11081
  }
10876
11082
  async function fetchBalance() {
10877
- const res = await apiFetch("/api/billing/balance");
11083
+ const res = await apiFetch("/api/billing/settings");
10878
11084
  if (!res.ok) throw new Error(`Balance fetch failed: ${res.status}`);
10879
- return res.json();
11085
+ const data = await res.json();
11086
+ return {
11087
+ tier: data.tier ?? "free",
11088
+ is_pro: data.tier && !["free", "guest"].includes(String(data.tier).toLowerCase()),
11089
+ is_founder: false,
11090
+ fuel_credits: Number(data?.fuel?.total ?? 0),
11091
+ fuel_credits_purchased: Number(data?.fuel?.purchased ?? 0),
11092
+ auto_topup: {
11093
+ enabled: !!data?.auto_topup?.enabled,
11094
+ threshold_usd: Number(data?.auto_topup?.threshold_usd ?? 0),
11095
+ amount_usd: Number(data?.auto_topup?.amount_usd ?? 0),
11096
+ max_per_month_usd: Number(data?.auto_topup?.max_per_month_usd ?? 0)
11097
+ }
11098
+ };
10880
11099
  }
10881
11100
  async function fetchFounderStatus() {
10882
11101
  const res = await apiFetch("/api/billing/founder-status");
@@ -14265,7 +14484,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
14265
14484
  var Header = () => /* @__PURE__ */ jsxs(Box, { borderStyle: "single", borderColor: "cyan", paddingX: 1, marginBottom: 1, children: [
14266
14485
  /* @__PURE__ */ jsxs(Text, { bold: true, color: "cyan", children: [
14267
14486
  "\u25CF MSapling CLI v",
14268
- "2.3.6-beta.21"
14487
+ "2.3.6-beta.23"
14269
14488
  ] }),
14270
14489
  /* @__PURE__ */ jsx(Box, { marginLeft: 2, children: /* @__PURE__ */ jsx(Text, { color: "gray", children: "Platinum Tier Architecture" }) })
14271
14490
  ] });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mtreeai/msapling-cli",
3
- "version": "2.3.6-beta.21",
3
+ "version": "2.3.6-beta.23",
4
4
  "description": "MSapling CLI — React/Ink terminal client for the MSapling backend (chat, projects, MDrive, agent tools). Proprietary; redistribution prohibited.",
5
5
  "license": "UNLICENSED",
6
6
  "author": "MSapling Team",