@mtreeai/msapling-cli 2.3.6-beta.26 → 2.3.6-beta.28

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 +722 -9
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -59,6 +59,28 @@ __export(src_exports, {
59
59
  MSaplingClient: () => MSaplingClient,
60
60
  MSaplingError: () => MSaplingError
61
61
  });
62
+ function normalizeDetail(detail) {
63
+ if (detail == null) return "";
64
+ if (typeof detail === "string") return detail;
65
+ if (Array.isArray(detail)) {
66
+ return detail.map((d) => {
67
+ if (!d || typeof d !== "object") return String(d);
68
+ const o = d;
69
+ const loc = Array.isArray(o.loc) ? o.loc.join(".") : "";
70
+ return [o.msg, loc && `(${loc})`].filter(Boolean).join(" ");
71
+ }).filter(Boolean).join("; ");
72
+ }
73
+ if (typeof detail === "object") {
74
+ const o = detail;
75
+ if (typeof o.msg === "string") return o.msg;
76
+ try {
77
+ return JSON.stringify(detail);
78
+ } catch {
79
+ return String(detail);
80
+ }
81
+ }
82
+ return String(detail);
83
+ }
62
84
  var MSaplingError, MSaplingClient;
63
85
  var init_src = __esm({
64
86
  "../api-client/src/index.ts"() {
@@ -143,7 +165,7 @@ var init_src = __esm({
143
165
  let code = "unknown";
144
166
  try {
145
167
  const err = await response.json();
146
- detail = err.detail || detail;
168
+ detail = normalizeDetail(err.detail) || detail;
147
169
  code = err.code || code;
148
170
  } catch (e) {
149
171
  }
@@ -334,6 +356,98 @@ var init_src = __esm({
334
356
  async disableOllama() {
335
357
  return await this.request("/api/ollama/disable", { method: "POST" });
336
358
  }
359
+ // Parity Task Card implementations
360
+ // Card 1: Usage Tracking (synced from /api/usage/sync-offline)
361
+ async syncOfflineUsage() {
362
+ return await this.request("/api/usage/sync-offline", { method: "POST" });
363
+ }
364
+ // Card 2: Budget Buckets (7 bucket types)
365
+ async getBillingBuckets() {
366
+ return await this.request("/api/billing/buckets");
367
+ }
368
+ // Card 4: Image Generation
369
+ async generateImage(prompt4, model) {
370
+ return await this.request("/api/image/generate", {
371
+ method: "POST",
372
+ body: JSON.stringify({ prompt: prompt4, model })
373
+ });
374
+ }
375
+ // Card 5: Voice/Audio Operations
376
+ async listVoices() {
377
+ return await this.request("/api/audio/voices");
378
+ }
379
+ async speak(text, voice) {
380
+ return await this.request("/api/audio/speak", {
381
+ method: "POST",
382
+ body: JSON.stringify({ text, voice })
383
+ });
384
+ }
385
+ // Card 8: Chat Sharing
386
+ async shareChat(chatId, expiryDays) {
387
+ return await this.request(`/api/share/chat/${encodeURIComponent(chatId)}/share`, {
388
+ method: "POST",
389
+ body: JSON.stringify({ expiry_days: expiryDays })
390
+ });
391
+ }
392
+ async listShares() {
393
+ return await this.request("/api/share");
394
+ }
395
+ async fetchSharedChat(token) {
396
+ return await this.request(`/api/shared/${encodeURIComponent(token)}`);
397
+ }
398
+ // Card 9: Notifications (preferences only)
399
+ async getNotificationPreferences() {
400
+ return await this.request("/api/notifications/preferences");
401
+ }
402
+ async setNotificationPreferences(prefs) {
403
+ return await this.request("/api/notifications/preferences", {
404
+ method: "PATCH",
405
+ body: JSON.stringify(prefs)
406
+ });
407
+ }
408
+ // Card 10: MLineage Events List
409
+ async listMLineageEvents() {
410
+ return await this.request("/api/mlineage/events");
411
+ }
412
+ // Card 11: Per-Project Model Config
413
+ async setProjectModel(projectId, model) {
414
+ return await this.request(`/api/projects/${encodeURIComponent(projectId)}`, {
415
+ method: "PATCH",
416
+ body: JSON.stringify({ default_model: model })
417
+ });
418
+ }
419
+ // Card 12: MFA/TOTP
420
+ async enrollMFA(method) {
421
+ return await this.request("/api/mfa/enroll", {
422
+ method: "POST",
423
+ body: JSON.stringify({ method })
424
+ });
425
+ }
426
+ async verifyMFA(code) {
427
+ return await this.request("/api/mfa/verify", {
428
+ method: "POST",
429
+ body: JSON.stringify({ code })
430
+ });
431
+ }
432
+ // Card 13: MCP Client Surface
433
+ async mcpConnect(serverUrl) {
434
+ return await this.request("/api/mcp/connect", {
435
+ method: "POST",
436
+ body: JSON.stringify({ server_url: serverUrl })
437
+ });
438
+ }
439
+ async mcpTools() {
440
+ return await this.request("/api/mcp/tools");
441
+ }
442
+ async mcpInvoke(method, args2) {
443
+ return await this.request("/api/mcp/invoke", {
444
+ method: "POST",
445
+ body: JSON.stringify({ method, args: args2 })
446
+ });
447
+ }
448
+ async mcpDisconnect() {
449
+ return await this.request("/api/mcp/disconnect", { method: "POST" });
450
+ }
337
451
  // CLI-KEYS-BYOK-01 (Iter 35): BYOK provider-key management. Per LAB
338
452
  // routers/keys.py — /api/keys/{status,save,{provider}}.
339
453
  async getProviderKeys() {
@@ -710,6 +824,32 @@ var init_src = __esm({
710
824
  async getActiveTasks() {
711
825
  return this.request("/api/task-monitor/active");
712
826
  }
827
+ /**
828
+ * Webhook Management (LAB-WEBHOOK-CRUD-01)
829
+ */
830
+ async listWebhooks() {
831
+ return this.request("/api/webhooks");
832
+ }
833
+ async createWebhook(url, events) {
834
+ return this.request("/api/webhooks", {
835
+ method: "POST",
836
+ body: JSON.stringify({ url, events })
837
+ });
838
+ }
839
+ async deleteWebhook(id) {
840
+ await this.request(`/api/webhooks/${encodeURIComponent(id)}`, {
841
+ method: "DELETE"
842
+ });
843
+ }
844
+ async listWebhookDeliveries(webhookId) {
845
+ const path2 = webhookId ? `/api/webhooks/deliveries?webhook_id=${encodeURIComponent(webhookId)}` : "/api/webhooks/deliveries";
846
+ return this.request(path2);
847
+ }
848
+ async retryWebhookDelivery(deliveryId) {
849
+ return this.request(`/api/webhooks/deliveries/${encodeURIComponent(deliveryId)}/retry`, {
850
+ method: "POST"
851
+ });
852
+ }
713
853
  /**
714
854
  * Wakeup Management (LAB-CHAT-PARITY-04)
715
855
  */
@@ -771,7 +911,10 @@ var init_src = __esm({
771
911
  }
772
912
  if (!response.ok) {
773
913
  const err = await response.json().catch(() => ({ detail: response.statusText }));
774
- throw new MSaplingError(err.detail || "Stream request failed", response.status);
914
+ throw new MSaplingError(
915
+ normalizeDetail(err.detail) || "Stream request failed",
916
+ response.status
917
+ );
775
918
  }
776
919
  if (!response.body) throw new Error("No response body");
777
920
  const reader = response.body.getReader();
@@ -9550,11 +9693,19 @@ var init_usage = __esm({
9550
9693
  init_esm_shims();
9551
9694
  usageCommand = {
9552
9695
  name: "usage",
9553
- description: "Show detailed usage, fuel balance, and daily quota",
9696
+ args: "[--json] [--since DATE]",
9697
+ description: "Show detailed usage, fuel balance, and daily quota; sync offline usage",
9554
9698
  category: "chat",
9555
9699
  handler: async (args2, context) => {
9556
9700
  try {
9701
+ const isJson = args2.includes("--json");
9557
9702
  const user = await context.client.getLiveUsage();
9703
+ await context.client.syncOfflineUsage().catch(() => {
9704
+ });
9705
+ if (isJson) {
9706
+ context.addMessage("system", JSON.stringify(user, null, 2));
9707
+ return;
9708
+ }
9558
9709
  context.addMessage("system", "MSapling Usage Dashboard:");
9559
9710
  context.addMessage("system", ` - Fuel Credits: $${user.fuel_credits.toFixed(4)}`);
9560
9711
  context.addMessage("system", ` - Daily Spending Limit: ${user.monthly_spending_limit ? "$" + user.monthly_spending_limit : "Unlimited"}`);
@@ -9655,12 +9806,31 @@ var init_project = __esm({
9655
9806
  LETTERS2 = "abcdefghijklmnopqrstuvwxyz";
9656
9807
  projectCommand = {
9657
9808
  name: "project",
9658
- args: "[number|letter|name|id]",
9659
- description: "List or switch active project",
9809
+ args: "[number|letter|name|id|config]",
9810
+ description: "List or switch active project, or set per-project config",
9660
9811
  category: "project",
9661
9812
  handler: async (args2, context) => {
9662
9813
  const arg = args2.join(" ").trim();
9663
9814
  const currentId = context.getProjectId() || null;
9815
+ if (args2[0] === "config") {
9816
+ const modelIdx = args2.indexOf("--model");
9817
+ const model = modelIdx >= 0 ? args2[modelIdx + 1] : void 0;
9818
+ if (!model) {
9819
+ context.addMessage("system", "Usage: /project config --model <MODEL>");
9820
+ return;
9821
+ }
9822
+ if (!currentId) {
9823
+ context.addMessage("error", "No active project. Use /project <id> first.");
9824
+ return;
9825
+ }
9826
+ try {
9827
+ await context.client.setProjectModel(currentId, model);
9828
+ context.addMessage("system", `Project default model set to: ${model}`);
9829
+ } catch (e) {
9830
+ context.addMessage("error", `Failed to set model: ${e.message}`);
9831
+ }
9832
+ return;
9833
+ }
9664
9834
  if (args2[0] === "new" || args2[0] === "create") {
9665
9835
  const name = args2.slice(1).join(" ").trim();
9666
9836
  if (!name) {
@@ -10607,7 +10777,7 @@ var init_version = __esm({
10607
10777
  description: "Show version information for CLI and core packages",
10608
10778
  category: "debug",
10609
10779
  handler: async (_args, context) => {
10610
- const cliVersion = true ? "2.3.6-beta.26" : "(dev)";
10780
+ const cliVersion = true ? "2.3.6-beta.28" : "(dev)";
10611
10781
  const coreVersion = true ? "2.3.2" : "(dev)";
10612
10782
  const runtime = process.version;
10613
10783
  context.addMessage("system", "MSapling Version Info");
@@ -10616,7 +10786,7 @@ var init_version = __esm({
10616
10786
  context.addMessage("system", row2("Core (@msapling/core)", coreVersion));
10617
10787
  context.addMessage("system", row2("Runtime (Node/Bun)", runtime));
10618
10788
  try {
10619
- const ts = "2026-05-28T23:01:36.725Z";
10789
+ const ts = "2026-05-29T17:03:26.902Z";
10620
10790
  if (ts && ts !== "__BUILD_TIMESTAMP__") {
10621
10791
  context.addMessage("system", row2("Build Timestamp", ts));
10622
10792
  }
@@ -11334,6 +11504,529 @@ var init_totp = __esm({
11334
11504
  }
11335
11505
  });
11336
11506
 
11507
+ // src/commands/budget.ts
11508
+ var budgetCommand;
11509
+ var init_budget = __esm({
11510
+ "src/commands/budget.ts"() {
11511
+ "use strict";
11512
+ init_esm_shims();
11513
+ budgetCommand = {
11514
+ name: "budget",
11515
+ description: "Show budget buckets (lifetime, sub, top_up, free, guest, byok, ollama)",
11516
+ category: "config",
11517
+ handler: async (args2, context) => {
11518
+ try {
11519
+ const data = await context.client.getBillingBuckets();
11520
+ context.addMessage("system", "Budget Buckets:");
11521
+ if (data && typeof data === "object") {
11522
+ for (const [key, val] of Object.entries(data)) {
11523
+ const bucket = val;
11524
+ const remaining = bucket.remaining ?? bucket.balance ?? 0;
11525
+ const limit = bucket.limit ?? bucket.cap ?? 0;
11526
+ context.addMessage("system", ` - ${key}: $${remaining.toFixed(4)} / $${limit.toFixed(4)}`);
11527
+ }
11528
+ } else {
11529
+ context.addMessage("system", JSON.stringify(data, null, 2));
11530
+ }
11531
+ } catch (e) {
11532
+ context.addMessage("error", `Failed to fetch budget: ${e.message}`);
11533
+ }
11534
+ }
11535
+ };
11536
+ }
11537
+ });
11538
+
11539
+ // src/commands/image.ts
11540
+ var imageCommand;
11541
+ var init_image = __esm({
11542
+ "src/commands/image.ts"() {
11543
+ "use strict";
11544
+ init_esm_shims();
11545
+ imageCommand = {
11546
+ name: "image",
11547
+ args: "<prompt> [--model MODEL] [--output FILE]",
11548
+ description: "Generate an image from a text prompt",
11549
+ category: "chat",
11550
+ handler: async (args2, context) => {
11551
+ let prompt4 = "";
11552
+ let model = "dall-e-3";
11553
+ let outputFile = null;
11554
+ for (let i = 0; i < args2.length; i++) {
11555
+ if (args2[i] === "--model" && i + 1 < args2.length) {
11556
+ model = args2[++i];
11557
+ continue;
11558
+ }
11559
+ if (args2[i] === "--output" && i + 1 < args2.length) {
11560
+ outputFile = args2[++i];
11561
+ continue;
11562
+ }
11563
+ prompt4 += (prompt4 ? " " : "") + args2[i];
11564
+ }
11565
+ if (!prompt4) {
11566
+ context.addMessage("system", "Usage: /image <prompt> [--model MODEL] [--output FILE]");
11567
+ return;
11568
+ }
11569
+ try {
11570
+ const result = await context.client.generateImage(prompt4, model);
11571
+ context.addMessage("system", `Image generated: ${result.url ?? result.image_url ?? "processing"}`);
11572
+ } catch (e) {
11573
+ context.addMessage("error", `Image generation failed: ${e.message}`);
11574
+ }
11575
+ }
11576
+ };
11577
+ }
11578
+ });
11579
+
11580
+ // src/commands/voice.ts
11581
+ var voiceCommand;
11582
+ var init_voice = __esm({
11583
+ "src/commands/voice.ts"() {
11584
+ "use strict";
11585
+ init_esm_shims();
11586
+ voiceCommand = {
11587
+ name: "voice",
11588
+ args: "[list|speak|transcribe] [args...]",
11589
+ description: "Voice operations: list, speak, or transcribe audio",
11590
+ category: "chat",
11591
+ handler: async (args2, context) => {
11592
+ const sub = (args2[0] ?? "list").toLowerCase();
11593
+ const rest = args2.slice(1);
11594
+ try {
11595
+ if (sub === "list") {
11596
+ const voices = await context.client.listVoices();
11597
+ context.addMessage("system", "Available voices:");
11598
+ if (Array.isArray(voices)) {
11599
+ for (const voice of voices) {
11600
+ context.addMessage("system", ` - ${voice.id ?? voice.name}: ${voice.name ?? voice.id}`);
11601
+ }
11602
+ } else if (typeof voices === "object") {
11603
+ for (const [key, val] of Object.entries(voices)) {
11604
+ context.addMessage("system", ` - ${key}`);
11605
+ }
11606
+ }
11607
+ return;
11608
+ }
11609
+ if (sub === "speak") {
11610
+ const text = rest.join(" ").trim();
11611
+ if (!text) {
11612
+ context.addMessage("system", "Usage: /voice speak --text <TEXT> --voice <VOICE>");
11613
+ return;
11614
+ }
11615
+ const voiceIdx = rest.indexOf("--voice");
11616
+ const voice = voiceIdx >= 0 ? rest[voiceIdx + 1] : "default";
11617
+ const result = await context.client.speak(text, voice);
11618
+ context.addMessage("system", `Audio generated: ${result.url ?? result.audio_url ?? "processing"}`);
11619
+ return;
11620
+ }
11621
+ if (sub === "transcribe") {
11622
+ context.addMessage("system", "Transcribe: pipe audio file to this command (not yet implemented)");
11623
+ return;
11624
+ }
11625
+ context.addMessage("error", `Unknown /voice subcommand '${sub}'. Try: list, speak, transcribe.`);
11626
+ } catch (e) {
11627
+ context.addMessage("error", `Voice: ${e.message}`);
11628
+ }
11629
+ }
11630
+ };
11631
+ }
11632
+ });
11633
+
11634
+ // src/commands/wakeup.ts
11635
+ var wakeupCommand;
11636
+ var init_wakeup = __esm({
11637
+ "src/commands/wakeup.ts"() {
11638
+ "use strict";
11639
+ init_esm_shims();
11640
+ wakeupCommand = {
11641
+ name: "wakeup",
11642
+ args: "[create|list|cancel] [...args]",
11643
+ description: "Manage wakeups (scheduled tasks): create, list, or cancel",
11644
+ category: "config",
11645
+ handler: async (args2, context) => {
11646
+ const sub = (args2[0] ?? "list").toLowerCase();
11647
+ const rest = args2.slice(1);
11648
+ try {
11649
+ if (sub === "list") {
11650
+ const wakeups = await context.client.listWakeups();
11651
+ context.addMessage("system", "Wakeups:");
11652
+ if (Array.isArray(wakeups)) {
11653
+ for (const wu of wakeups) {
11654
+ context.addMessage("system", ` - ${wu.id}: ${wu.reason ?? wu.prompt ?? "scheduled"}`);
11655
+ }
11656
+ } else {
11657
+ context.addMessage("system", JSON.stringify(wakeups, null, 2));
11658
+ }
11659
+ return;
11660
+ }
11661
+ if (sub === "create") {
11662
+ const delayIdx = rest.indexOf("--delay");
11663
+ const delay = delayIdx >= 0 ? Number(rest[delayIdx + 1]) : 60;
11664
+ const reasonIdx = rest.indexOf("--reason");
11665
+ const reason = reasonIdx >= 0 ? rest[reasonIdx + 1] : void 0;
11666
+ const promptIdx = rest.indexOf("--prompt");
11667
+ const prompt4 = promptIdx >= 0 ? rest.slice(promptIdx + 1).join(" ") : void 0;
11668
+ const result = await context.client.createWakeup({
11669
+ delay_seconds: delay,
11670
+ reason,
11671
+ prompt: prompt4
11672
+ });
11673
+ context.addMessage("system", `Wakeup created: ${result.id ?? "ok"}`);
11674
+ return;
11675
+ }
11676
+ if (sub === "cancel") {
11677
+ const id = rest[0];
11678
+ if (!id) {
11679
+ context.addMessage("system", "Usage: /wakeup cancel <ID>");
11680
+ return;
11681
+ }
11682
+ await context.client.cancelWakeup(id);
11683
+ context.addMessage("system", `Wakeup cancelled: ${id}`);
11684
+ return;
11685
+ }
11686
+ context.addMessage("error", `Unknown /wakeup subcommand '${sub}'. Try: list, create, cancel.`);
11687
+ } catch (e) {
11688
+ context.addMessage("error", `Wakeup: ${e.message}`);
11689
+ }
11690
+ }
11691
+ };
11692
+ }
11693
+ });
11694
+
11695
+ // src/commands/share.ts
11696
+ var shareCommand;
11697
+ var init_share = __esm({
11698
+ "src/commands/share.ts"() {
11699
+ "use strict";
11700
+ init_esm_shims();
11701
+ shareCommand = {
11702
+ name: "share",
11703
+ args: "[create|list|fetch] [...args]",
11704
+ description: "Share chats: create, list, or fetch shared conversations",
11705
+ category: "chat",
11706
+ handler: async (args2, context) => {
11707
+ const sub = (args2[0] ?? "list").toLowerCase();
11708
+ const rest = args2.slice(1);
11709
+ try {
11710
+ if (sub === "list") {
11711
+ const shares = await context.client.listShares();
11712
+ context.addMessage("system", "Shared Chats:");
11713
+ if (Array.isArray(shares)) {
11714
+ for (const share of shares) {
11715
+ const expiry = share.expiry_at ? ` (expires ${share.expiry_at})` : "";
11716
+ context.addMessage("system", ` - ${share.token}: chat ${share.chat_id}${expiry}`);
11717
+ }
11718
+ } else {
11719
+ context.addMessage("system", JSON.stringify(shares, null, 2));
11720
+ }
11721
+ return;
11722
+ }
11723
+ if (sub === "create") {
11724
+ const chatId = rest[0];
11725
+ const expiryIdx = rest.indexOf("--expiry");
11726
+ const expiry = expiryIdx >= 0 ? Number(rest[expiryIdx + 1]) : void 0;
11727
+ if (!chatId) {
11728
+ context.addMessage("system", "Usage: /share create <CHAT-ID> [--expiry DAYS]");
11729
+ return;
11730
+ }
11731
+ const result = await context.client.shareChat(chatId, expiry);
11732
+ context.addMessage("system", `Share created: ${result.token ?? result.url ?? "ok"}`);
11733
+ return;
11734
+ }
11735
+ if (sub === "fetch") {
11736
+ const token = rest[0];
11737
+ if (!token) {
11738
+ context.addMessage("system", "Usage: /share fetch <TOKEN>");
11739
+ return;
11740
+ }
11741
+ const result = await context.client.fetchSharedChat(token);
11742
+ context.addMessage("system", `Fetched shared chat: ${result.chat_id ?? result.id}`);
11743
+ return;
11744
+ }
11745
+ context.addMessage("error", `Unknown /share subcommand '${sub}'. Try: list, create, fetch.`);
11746
+ } catch (e) {
11747
+ context.addMessage("error", `Share: ${e.message}`);
11748
+ }
11749
+ }
11750
+ };
11751
+ }
11752
+ });
11753
+
11754
+ // src/commands/notify.ts
11755
+ var notifyCommand;
11756
+ var init_notify = __esm({
11757
+ "src/commands/notify.ts"() {
11758
+ "use strict";
11759
+ init_esm_shims();
11760
+ notifyCommand = {
11761
+ name: "notify",
11762
+ args: "[preferences] [--get|--set KEY=VALUE]",
11763
+ description: "Manage notification preferences (SSE stream not yet implemented)",
11764
+ category: "config",
11765
+ handler: async (args2, context) => {
11766
+ try {
11767
+ if (args2.length === 0 || args2[0] === "preferences" || args2[0] === "--get") {
11768
+ const prefs = await context.client.getNotificationPreferences();
11769
+ context.addMessage("system", "Notification Preferences:");
11770
+ context.addMessage("system", JSON.stringify(prefs, null, 2));
11771
+ return;
11772
+ }
11773
+ if (args2[0] === "--set") {
11774
+ const updates = {};
11775
+ for (let i = 1; i < args2.length; i++) {
11776
+ const pair = args2[i].split("=");
11777
+ if (pair.length === 2) {
11778
+ updates[pair[0]] = pair[1] === "true" ? true : pair[1] === "false" ? false : pair[1];
11779
+ }
11780
+ }
11781
+ const result = await context.client.setNotificationPreferences(updates);
11782
+ context.addMessage("system", `Preferences updated: ${result.status ?? "ok"}`);
11783
+ return;
11784
+ }
11785
+ context.addMessage("error", "Usage: /notify [--get] or /notify --set KEY=VALUE ...");
11786
+ } catch (e) {
11787
+ context.addMessage("error", `Notify: ${e.message}`);
11788
+ }
11789
+ }
11790
+ };
11791
+ }
11792
+ });
11793
+
11794
+ // src/commands/mlineage.ts
11795
+ var mlineageCommand;
11796
+ var init_mlineage = __esm({
11797
+ "src/commands/mlineage.ts"() {
11798
+ "use strict";
11799
+ init_esm_shims();
11800
+ mlineageCommand = {
11801
+ name: "mlineage",
11802
+ args: "[events] [--follow]",
11803
+ description: "List MLineage events (follow mode not yet implemented)",
11804
+ category: "chat",
11805
+ handler: async (args2, context) => {
11806
+ try {
11807
+ const follow = args2.includes("--follow");
11808
+ const events = await context.client.listMLineageEvents();
11809
+ context.addMessage("system", "MLineage Events:");
11810
+ if (Array.isArray(events)) {
11811
+ for (const evt of events) {
11812
+ context.addMessage("system", ` - ${evt.id}: ${evt.type} @ ${evt.timestamp}`);
11813
+ }
11814
+ } else {
11815
+ context.addMessage("system", JSON.stringify(events, null, 2));
11816
+ }
11817
+ if (follow) {
11818
+ context.addMessage("system", "(follow mode not yet implemented)");
11819
+ }
11820
+ } catch (e) {
11821
+ context.addMessage("error", `MLineage: ${e.message}`);
11822
+ }
11823
+ }
11824
+ };
11825
+ }
11826
+ });
11827
+
11828
+ // src/commands/mfa.ts
11829
+ var mfaCommand;
11830
+ var init_mfa = __esm({
11831
+ "src/commands/mfa.ts"() {
11832
+ "use strict";
11833
+ init_esm_shims();
11834
+ mfaCommand = {
11835
+ name: "mfa",
11836
+ args: "[enroll|verify] [...args]",
11837
+ description: "Enroll or verify MFA/TOTP",
11838
+ category: "config",
11839
+ handler: async (args2, context) => {
11840
+ const sub = (args2[0] ?? "status").toLowerCase();
11841
+ const rest = args2.slice(1);
11842
+ try {
11843
+ if (sub === "enroll") {
11844
+ const method = rest[0] ?? "totp";
11845
+ const result = await context.client.enrollMFA(method);
11846
+ context.addMessage("system", `MFA enrollment initiated: ${method}`);
11847
+ if (result.qr_code) {
11848
+ context.addMessage("system", `QR Code: ${result.qr_code}`);
11849
+ }
11850
+ if (result.secret) {
11851
+ context.addMessage("system", `Secret: ${result.secret}`);
11852
+ }
11853
+ return;
11854
+ }
11855
+ if (sub === "verify") {
11856
+ const code = rest[0];
11857
+ if (!code) {
11858
+ context.addMessage("system", "Usage: /mfa verify <CODE>");
11859
+ return;
11860
+ }
11861
+ const result = await context.client.verifyMFA(code);
11862
+ context.addMessage("system", `MFA verified: ${result.status ?? "ok"}`);
11863
+ return;
11864
+ }
11865
+ context.addMessage("error", `Unknown /mfa subcommand '${sub}'. Try: enroll, verify.`);
11866
+ } catch (e) {
11867
+ context.addMessage("error", `MFA: ${e.message}`);
11868
+ }
11869
+ }
11870
+ };
11871
+ }
11872
+ });
11873
+
11874
+ // src/commands/mcp.ts
11875
+ var mcpCommand;
11876
+ var init_mcp = __esm({
11877
+ "src/commands/mcp.ts"() {
11878
+ "use strict";
11879
+ init_esm_shims();
11880
+ mcpCommand = {
11881
+ name: "mcp",
11882
+ args: "[connect|invoke|tools|disconnect] [...args]",
11883
+ description: "MCP client surface: connect, invoke, list tools, disconnect",
11884
+ category: "config",
11885
+ handler: async (args2, context) => {
11886
+ const sub = (args2[0] ?? "tools").toLowerCase();
11887
+ const rest = args2.slice(1);
11888
+ try {
11889
+ if (sub === "connect") {
11890
+ const url = rest[0];
11891
+ if (!url) {
11892
+ context.addMessage("system", "Usage: /mcp connect <server-url>");
11893
+ return;
11894
+ }
11895
+ const result = await context.client.mcpConnect(url);
11896
+ context.addMessage("system", `MCP Connected: ${result.status ?? "ok"}`);
11897
+ return;
11898
+ }
11899
+ if (sub === "tools") {
11900
+ const tools = await context.client.mcpTools();
11901
+ context.addMessage("system", "Available MCP Tools:");
11902
+ if (Array.isArray(tools)) {
11903
+ for (const tool of tools) {
11904
+ context.addMessage("system", ` - ${tool.name}: ${tool.description ?? ""}`);
11905
+ }
11906
+ } else if (typeof tools === "object") {
11907
+ context.addMessage("system", JSON.stringify(tools, null, 2));
11908
+ }
11909
+ return;
11910
+ }
11911
+ if (sub === "invoke") {
11912
+ const method = rest[0];
11913
+ if (!method) {
11914
+ context.addMessage("system", "Usage: /mcp invoke <method> [args...]");
11915
+ return;
11916
+ }
11917
+ const argsStr = rest.slice(1).join(" ");
11918
+ let argsObj;
11919
+ try {
11920
+ argsObj = argsStr ? JSON.parse(argsStr) : void 0;
11921
+ } catch {
11922
+ argsObj = { raw: argsStr };
11923
+ }
11924
+ const result = await context.client.mcpInvoke(method, argsObj);
11925
+ context.addMessage("system", `MCP Result: ${JSON.stringify(result)}`);
11926
+ return;
11927
+ }
11928
+ if (sub === "disconnect") {
11929
+ const result = await context.client.mcpDisconnect();
11930
+ context.addMessage("system", `MCP Disconnected: ${result.status ?? "ok"}`);
11931
+ return;
11932
+ }
11933
+ context.addMessage("error", `Unknown /mcp subcommand '${sub}'. Try: connect, tools, invoke, disconnect.`);
11934
+ } catch (e) {
11935
+ context.addMessage("error", `MCP: ${e.message}`);
11936
+ }
11937
+ }
11938
+ };
11939
+ }
11940
+ });
11941
+
11942
+ // src/commands/webhook.ts
11943
+ var webhookCommand;
11944
+ var init_webhook = __esm({
11945
+ "src/commands/webhook.ts"() {
11946
+ "use strict";
11947
+ init_esm_shims();
11948
+ webhookCommand = {
11949
+ name: "webhook",
11950
+ args: "[list|add|rm|deliveries|retry] [...args]",
11951
+ description: "Manage webhooks: create, list, delete, and view delivery logs",
11952
+ category: "project",
11953
+ handler: async (args2, context) => {
11954
+ const sub = (args2[0] ?? "list").toLowerCase();
11955
+ const rest = args2.slice(1);
11956
+ try {
11957
+ if (sub === "list") {
11958
+ const webhooks = await context.client.listWebhooks();
11959
+ context.addMessage("system", "Webhooks:");
11960
+ if (Array.isArray(webhooks) && webhooks.length > 0) {
11961
+ for (const wh of webhooks) {
11962
+ const id = wh.id ?? wh.webhook_id ?? "?";
11963
+ const url = wh.url ?? "?";
11964
+ const events = wh.events ? wh.events.join(", ") : "";
11965
+ context.addMessage("system", ` - ${id}: ${url} (${events})`);
11966
+ }
11967
+ } else {
11968
+ context.addMessage("system", " (no webhooks)");
11969
+ }
11970
+ return;
11971
+ }
11972
+ if (sub === "add") {
11973
+ const url = rest[0];
11974
+ const events = rest.slice(1);
11975
+ if (!url || events.length === 0) {
11976
+ context.addMessage("system", "Usage: /webhook add <url> <event1> [event2] ...");
11977
+ return;
11978
+ }
11979
+ const result = await context.client.createWebhook(url, events);
11980
+ const id = result.id ?? result.webhook_id ?? "?";
11981
+ context.addMessage("system", `Webhook created: ${id} \u2192 ${url}`);
11982
+ return;
11983
+ }
11984
+ if (sub === "rm" || sub === "delete") {
11985
+ const id = rest[0];
11986
+ if (!id) {
11987
+ context.addMessage("system", "Usage: /webhook rm <id>");
11988
+ return;
11989
+ }
11990
+ await context.client.deleteWebhook(id);
11991
+ context.addMessage("system", `Deleted webhook: ${id}`);
11992
+ return;
11993
+ }
11994
+ if (sub === "deliveries") {
11995
+ const webhookId = rest[0] || void 0;
11996
+ const deliveries = await context.client.listWebhookDeliveries(webhookId);
11997
+ const title = webhookId ? `Deliveries for webhook ${webhookId}:` : "Recent deliveries:";
11998
+ context.addMessage("system", title);
11999
+ if (Array.isArray(deliveries) && deliveries.length > 0) {
12000
+ for (const d of deliveries) {
12001
+ const id = d.id ?? d.delivery_id ?? "?";
12002
+ const status = d.status ?? "?";
12003
+ const at = d.delivered_at ?? d.created_at ?? "?";
12004
+ context.addMessage("system", ` - ${id}: ${status} (${at})`);
12005
+ }
12006
+ } else {
12007
+ context.addMessage("system", " (no deliveries)");
12008
+ }
12009
+ return;
12010
+ }
12011
+ if (sub === "retry") {
12012
+ const deliveryId = rest[0];
12013
+ if (!deliveryId) {
12014
+ context.addMessage("system", "Usage: /webhook retry <delivery-id>");
12015
+ return;
12016
+ }
12017
+ const result = await context.client.retryWebhookDelivery(deliveryId);
12018
+ context.addMessage("system", `Retrying delivery: ${deliveryId} (${result.status ?? "queued"})`);
12019
+ return;
12020
+ }
12021
+ context.addMessage("error", `Unknown /webhook subcommand '${sub}'. Try: list, add, rm, deliveries, retry.`);
12022
+ } catch (e) {
12023
+ context.addMessage("error", `Webhook: ${e.message}`);
12024
+ }
12025
+ }
12026
+ };
12027
+ }
12028
+ });
12029
+
11337
12030
  // src/commands/index.ts
11338
12031
  var commands_exports = {};
11339
12032
  __export(commands_exports, {
@@ -11387,6 +12080,16 @@ var init_commands = __esm({
11387
12080
  init_todo();
11388
12081
  init_outputStyle();
11389
12082
  init_totp();
12083
+ init_budget();
12084
+ init_image();
12085
+ init_voice();
12086
+ init_wakeup();
12087
+ init_share();
12088
+ init_notify();
12089
+ init_mlineage();
12090
+ init_mfa();
12091
+ init_mcp();
12092
+ init_webhook();
11390
12093
  commands = [
11391
12094
  loginCommand,
11392
12095
  unlockCommand,
@@ -11426,7 +12129,17 @@ var init_commands = __esm({
11426
12129
  noteCommand,
11427
12130
  todoCommand,
11428
12131
  outputStyleCommand,
11429
- totpCommand
12132
+ totpCommand,
12133
+ budgetCommand,
12134
+ imageCommand,
12135
+ voiceCommand,
12136
+ wakeupCommand,
12137
+ shareCommand,
12138
+ notifyCommand,
12139
+ mlineageCommand,
12140
+ mfaCommand,
12141
+ mcpCommand,
12142
+ webhookCommand
11430
12143
  ];
11431
12144
  }
11432
12145
  });
@@ -15029,7 +15742,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
15029
15742
  var Header = () => /* @__PURE__ */ jsxs(Box, { borderStyle: "single", borderColor: "cyan", paddingX: 1, marginBottom: 1, children: [
15030
15743
  /* @__PURE__ */ jsxs(Text, { bold: true, color: "cyan", children: [
15031
15744
  "\u25CF MSapling CLI v",
15032
- "2.3.6-beta.26"
15745
+ "2.3.6-beta.28"
15033
15746
  ] }),
15034
15747
  /* @__PURE__ */ jsx(Box, { marginLeft: 2, children: /* @__PURE__ */ jsx(Text, { color: "gray", children: "Platinum Tier Architecture" }) })
15035
15748
  ] });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mtreeai/msapling-cli",
3
- "version": "2.3.6-beta.26",
3
+ "version": "2.3.6-beta.28",
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",