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

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 +377 -3
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -266,6 +266,88 @@ var init_src = __esm({
266
266
  body: JSON.stringify(symbol)
267
267
  });
268
268
  }
269
+ // CLI-OLLAMA-COMMANDS-01 (Iter 35): Ollama BYOK helpers. Per LAB ollama.py
270
+ // routes are GET/POST/DELETE under /api/ollama. The CLI uses these to wire
271
+ // up the user's local Ollama instance as a free-tier model provider.
272
+ async getOllamaStatus() {
273
+ return await this.request("/api/ollama/status");
274
+ }
275
+ async configureOllama(opts) {
276
+ return await this.request("/api/ollama/configure", {
277
+ method: "POST",
278
+ body: JSON.stringify({
279
+ base_url: opts.baseUrl,
280
+ enabled: opts.enabled,
281
+ action: opts.action,
282
+ model: opts.model
283
+ })
284
+ });
285
+ }
286
+ async testOllama(baseUrl) {
287
+ return await this.request("/api/ollama/test", {
288
+ method: "POST",
289
+ body: JSON.stringify({ base_url: baseUrl })
290
+ });
291
+ }
292
+ async deleteOllamaModel(modelName) {
293
+ return await this.request(`/api/ollama/models/${encodeURIComponent(modelName)}`, {
294
+ method: "DELETE"
295
+ });
296
+ }
297
+ async disableOllama() {
298
+ return await this.request("/api/ollama/disable", { method: "POST" });
299
+ }
300
+ // CLI-KEYS-BYOK-01 (Iter 35): BYOK provider-key management. Per LAB
301
+ // routers/keys.py — /api/keys/{status,save,{provider}}.
302
+ async getProviderKeys() {
303
+ return await this.request("/api/keys/status");
304
+ }
305
+ async saveProviderKey(provider, apiKey) {
306
+ return await this.request("/api/keys/save", {
307
+ method: "POST",
308
+ body: JSON.stringify({ provider, api_key: apiKey })
309
+ });
310
+ }
311
+ async deleteProviderKey(provider) {
312
+ return await this.request(`/api/keys/${encodeURIComponent(provider)}`, { method: "DELETE" });
313
+ }
314
+ // CLI-CHAT-LIFECYCLE-01 (Iter 35): rename/delete/fork/pin. Per LAB
315
+ // projects.py:404 (PATCH), 879 (DELETE), 534 (POST fork), 726 (PATCH pin).
316
+ async renameChat(chatId, title) {
317
+ return await this.request(`/api/projects/chat/${encodeURIComponent(chatId)}`, {
318
+ method: "PATCH",
319
+ body: JSON.stringify({ title })
320
+ });
321
+ }
322
+ async deleteChat(chatId) {
323
+ return await this.request(`/api/projects/chat/${encodeURIComponent(chatId)}`, { method: "DELETE" });
324
+ }
325
+ async forkChat(chatId, body) {
326
+ return await this.request(`/api/projects/chat/${encodeURIComponent(chatId)}/fork`, {
327
+ method: "POST",
328
+ body: JSON.stringify(body ?? {})
329
+ });
330
+ }
331
+ async pinChat(chatId, pinned) {
332
+ return await this.request(`/api/projects/chat/${encodeURIComponent(chatId)}/pin`, {
333
+ method: "PATCH",
334
+ body: JSON.stringify({ pinned })
335
+ });
336
+ }
337
+ // CLI-MEMORIES-01 (Iter 35): backend-managed memories (vs the local notes
338
+ // /memory currently surfaces). Per LAB memories.py — list / create / delete.
339
+ async listMemories() {
340
+ return await this.request("/api/memories");
341
+ }
342
+ async createMemory(content) {
343
+ return await this.request("/api/memories", {
344
+ method: "POST",
345
+ body: JSON.stringify({ content })
346
+ });
347
+ }
348
+ async deleteMemory(memoryId) {
349
+ return await this.request(`/api/memories/${encodeURIComponent(memoryId)}`, { method: "DELETE" });
350
+ }
269
351
  // CLI-PROJECT-CREATE-01 (Iter 34): create a new project. Per LAB
270
352
  // projects.py:239 — POST /api/projects/ with {project_name}.
271
353
  async createProject(name) {
@@ -8656,10 +8738,79 @@ async function createChat(args2, context) {
8656
8738
  context.addMessage("error", `Failed to create chat: ${e.message}`);
8657
8739
  }
8658
8740
  }
8741
+ async function resolveChatId(rest, context) {
8742
+ const candidate = rest[0]?.trim();
8743
+ if (candidate && /^[0-9a-fA-F-]{32,}$/.test(candidate)) return candidate;
8744
+ return context.activeChatId ?? null;
8745
+ }
8746
+ async function lifecycleSub(args2, context) {
8747
+ const sub = args2[0]?.toLowerCase();
8748
+ const rest = args2.slice(1);
8749
+ try {
8750
+ if (sub === "rename") {
8751
+ const newTitle = rest.slice(1).join(" ").trim() || rest[0]?.trim();
8752
+ const chatId = await resolveChatId(rest, context);
8753
+ if (!chatId || !newTitle) {
8754
+ context.addMessage("system", "Usage: /chat rename [<chat_id>] <new title>");
8755
+ return;
8756
+ }
8757
+ const r = await context.client.renameChat(chatId, newTitle);
8758
+ context.addMessage("system", `Renamed to "${r.title}"`);
8759
+ return;
8760
+ }
8761
+ if (sub === "delete" || sub === "rm") {
8762
+ const chatId = await resolveChatId(rest, context);
8763
+ if (!chatId) {
8764
+ context.addMessage("system", "Usage: /chat delete [<chat_id>]");
8765
+ return;
8766
+ }
8767
+ await context.client.deleteChat(chatId);
8768
+ context.addMessage("system", `Deleted chat ${chatId}`);
8769
+ if (chatId === context.activeChatId) context.setActiveChatId(null);
8770
+ return;
8771
+ }
8772
+ if (sub === "fork") {
8773
+ const chatId = await resolveChatId(rest, context);
8774
+ if (!chatId) {
8775
+ context.addMessage("system", "Usage: /chat fork [<chat_id>]");
8776
+ return;
8777
+ }
8778
+ const r = await context.client.forkChat(chatId);
8779
+ context.addMessage("system", `Forked \u2192 ${r.chat_id ?? r.title ?? "(ok)"}`);
8780
+ if (r.chat_id) context.setActiveChatId(r.chat_id);
8781
+ return;
8782
+ }
8783
+ if (sub === "pin") {
8784
+ const chatId = await resolveChatId(rest, context);
8785
+ if (!chatId) {
8786
+ context.addMessage("system", "Usage: /chat pin [<chat_id>]");
8787
+ return;
8788
+ }
8789
+ await context.client.pinChat(chatId, true);
8790
+ context.addMessage("system", `Pinned ${chatId}`);
8791
+ return;
8792
+ }
8793
+ if (sub === "unpin") {
8794
+ const chatId = await resolveChatId(rest, context);
8795
+ if (!chatId) {
8796
+ context.addMessage("system", "Usage: /chat unpin [<chat_id>]");
8797
+ return;
8798
+ }
8799
+ await context.client.pinChat(chatId, false);
8800
+ context.addMessage("system", `Unpinned ${chatId}`);
8801
+ return;
8802
+ }
8803
+ } catch (e) {
8804
+ context.addMessage("error", `Chat ${sub}: ${e.message}`);
8805
+ }
8806
+ }
8659
8807
  async function listChats(args2, context) {
8660
8808
  if (args2[0] === "new" || args2[0] === "create") {
8661
8809
  return createChat(args2.slice(1), context);
8662
8810
  }
8811
+ if (["rename", "delete", "rm", "fork", "pin", "unpin"].includes(args2[0])) {
8812
+ return lifecycleSub(args2, context);
8813
+ }
8663
8814
  const arg = args2.join(" ").trim();
8664
8815
  const result = await fetchChatsForCurrentProject(context);
8665
8816
  if ("error" in result) {
@@ -8782,6 +8933,223 @@ var init_broadcast = __esm({
8782
8933
  }
8783
8934
  });
8784
8935
 
8936
+ // src/commands/ollama.ts
8937
+ function formatSize(bytes) {
8938
+ if (!bytes) return "?";
8939
+ const gb = bytes / 1e9;
8940
+ if (gb >= 1) return `${gb.toFixed(1)} GB`;
8941
+ const mb = bytes / 1e6;
8942
+ return `${mb.toFixed(0)} MB`;
8943
+ }
8944
+ var DEFAULT_BASE_URL, ollamaCommand;
8945
+ var init_ollama = __esm({
8946
+ "src/commands/ollama.ts"() {
8947
+ "use strict";
8948
+ init_esm_shims();
8949
+ DEFAULT_BASE_URL = "http://localhost:11434";
8950
+ ollamaCommand = {
8951
+ name: "ollama",
8952
+ args: "[status|configure|enable|disable|test|models|pull|delete] [...args]",
8953
+ description: "Configure local Ollama integration (BYOK free tier) and manage local models.",
8954
+ category: "config",
8955
+ handler: async (args2, context) => {
8956
+ const sub = (args2[0] ?? "status").toLowerCase();
8957
+ const rest = args2.slice(1);
8958
+ try {
8959
+ if (sub === "status" || sub === "models") {
8960
+ const s = await context.client.getOllamaStatus();
8961
+ context.addMessage("system", "=== Ollama Status ===");
8962
+ context.addMessage("system", ` Enabled: ${s.enabled ? "yes" : "no"}`);
8963
+ context.addMessage("system", ` Base URL: ${s.base_url}`);
8964
+ context.addMessage("system", ` Connected: ${s.connected ? "yes" : "no"}${s.connection_message ? " \u2014 " + s.connection_message : ""}`);
8965
+ if (s.models?.length) {
8966
+ context.addMessage("system", ` Models (${s.models.length}):`);
8967
+ for (const m of s.models) {
8968
+ context.addMessage("system", ` - ${m.id} (${formatSize(m.size)})`);
8969
+ }
8970
+ } else if (s.enabled) {
8971
+ context.addMessage("system", " Models: (none pulled \u2014 try /ollama pull <name>, e.g. llama3.2)");
8972
+ } else {
8973
+ context.addMessage("system", "");
8974
+ context.addMessage("system", " Run '/ollama configure' to enable.");
8975
+ }
8976
+ return;
8977
+ }
8978
+ if (sub === "configure" || sub === "enable") {
8979
+ const url = rest[0] || DEFAULT_BASE_URL;
8980
+ const res = await context.client.configureOllama({ baseUrl: url, enabled: true });
8981
+ context.addMessage("system", `Ollama enabled at ${url}: ${res.message ?? res.status}`);
8982
+ return;
8983
+ }
8984
+ if (sub === "disable") {
8985
+ const res = await context.client.disableOllama();
8986
+ context.addMessage("system", `Ollama disabled: ${res.message ?? res.status}`);
8987
+ return;
8988
+ }
8989
+ if (sub === "test") {
8990
+ const url = rest[0] || DEFAULT_BASE_URL;
8991
+ const res = await context.client.testOllama(url);
8992
+ context.addMessage("system", `Connection to ${url}: ${res.connected ? "OK" : "FAILED"} \u2014 ${res.message}`);
8993
+ if (res.models?.length) {
8994
+ context.addMessage("system", ` Found ${res.models.length} model(s):`);
8995
+ for (const m of res.models.slice(0, 12)) {
8996
+ context.addMessage("system", ` - ${m.id}`);
8997
+ }
8998
+ if (res.models.length > 12) context.addMessage("system", ` \u2026and ${res.models.length - 12} more.`);
8999
+ }
9000
+ return;
9001
+ }
9002
+ if (sub === "pull") {
9003
+ const model = rest[0];
9004
+ if (!model) {
9005
+ context.addMessage("system", "Usage: /ollama pull <model> (e.g. /ollama pull llama3.2)");
9006
+ return;
9007
+ }
9008
+ const status = await context.client.getOllamaStatus();
9009
+ const res = await context.client.configureOllama({
9010
+ baseUrl: status.base_url || DEFAULT_BASE_URL,
9011
+ enabled: status.enabled !== false,
9012
+ action: "pull",
9013
+ model
9014
+ });
9015
+ context.addMessage("system", res.message ?? res.status);
9016
+ return;
9017
+ }
9018
+ if (sub === "delete" || sub === "remove" || sub === "rm") {
9019
+ const model = rest[0];
9020
+ if (!model) {
9021
+ context.addMessage("system", "Usage: /ollama delete <model>");
9022
+ return;
9023
+ }
9024
+ const res = await context.client.deleteOllamaModel(model);
9025
+ context.addMessage("system", res.message ?? res.status);
9026
+ return;
9027
+ }
9028
+ context.addMessage("error", `Unknown /ollama subcommand '${sub}'. Try: status, configure, disable, test, pull, delete.`);
9029
+ } catch (e) {
9030
+ context.addMessage("error", `Ollama: ${e.message}`);
9031
+ }
9032
+ }
9033
+ };
9034
+ }
9035
+ });
9036
+
9037
+ // src/commands/keys.ts
9038
+ var keysCommand;
9039
+ var init_keys = __esm({
9040
+ "src/commands/keys.ts"() {
9041
+ "use strict";
9042
+ init_esm_shims();
9043
+ keysCommand = {
9044
+ name: "keys",
9045
+ args: "[list|add|delete] [...args]",
9046
+ description: "Manage BYOK provider API keys (openai, anthropic, google, openrouter, \u2026).",
9047
+ category: "config",
9048
+ handler: async (args2, context) => {
9049
+ const sub = (args2[0] ?? "list").toLowerCase();
9050
+ const rest = args2.slice(1);
9051
+ try {
9052
+ if (sub === "list" || sub === "status") {
9053
+ const s = await context.client.getProviderKeys();
9054
+ const providers = s.providers ?? [];
9055
+ context.addMessage("system", `Provider Keys (${providers.length}):`);
9056
+ for (const p of providers) {
9057
+ const tag = p.configured ? "\u2713" : " ";
9058
+ const prefix = p.key_prefix ? ` (${p.key_prefix}\u2026)` : "";
9059
+ context.addMessage("system", ` [${tag}] ${p.provider}${prefix}`);
9060
+ }
9061
+ if (providers.length === 0) {
9062
+ context.addMessage("system", " (none \u2014 add via /keys add <provider> <key>)");
9063
+ }
9064
+ return;
9065
+ }
9066
+ if (sub === "add" || sub === "save") {
9067
+ const provider = rest[0];
9068
+ const key = rest.slice(1).join(" ").trim();
9069
+ if (!provider || !key) {
9070
+ context.addMessage("system", "Usage: /keys add <provider> <key> (e.g. /keys add openai sk-...)");
9071
+ return;
9072
+ }
9073
+ const res = await context.client.saveProviderKey(provider, key);
9074
+ context.addMessage("system", `Saved key for ${provider}: ${res.message ?? res.status}`);
9075
+ return;
9076
+ }
9077
+ if (sub === "delete" || sub === "rm" || sub === "remove") {
9078
+ const provider = rest[0];
9079
+ if (!provider) {
9080
+ context.addMessage("system", "Usage: /keys delete <provider>");
9081
+ return;
9082
+ }
9083
+ const res = await context.client.deleteProviderKey(provider);
9084
+ context.addMessage("system", `Removed key for ${provider}: ${res.message ?? res.status}`);
9085
+ return;
9086
+ }
9087
+ context.addMessage("error", `Unknown /keys subcommand '${sub}'. Try: list, add, delete.`);
9088
+ } catch (e) {
9089
+ context.addMessage("error", `Keys: ${e.message}`);
9090
+ }
9091
+ }
9092
+ };
9093
+ }
9094
+ });
9095
+
9096
+ // src/commands/memories.ts
9097
+ var memoriesCommand;
9098
+ var init_memories = __esm({
9099
+ "src/commands/memories.ts"() {
9100
+ "use strict";
9101
+ init_esm_shims();
9102
+ memoriesCommand = {
9103
+ name: "memories",
9104
+ args: "[list|add|delete] [...args]",
9105
+ description: "Manage the user-scoped memory bank (used across all chats).",
9106
+ category: "project",
9107
+ handler: async (args2, context) => {
9108
+ const sub = (args2[0] ?? "list").toLowerCase();
9109
+ const rest = args2.slice(1);
9110
+ try {
9111
+ if (sub === "list" || sub === "show") {
9112
+ const items = await context.client.listMemories();
9113
+ if (!items || items.length === 0) {
9114
+ context.addMessage("system", "No memories yet. Add one via /memories add <text>.");
9115
+ return;
9116
+ }
9117
+ context.addMessage("system", `Memories (${items.length}):`);
9118
+ for (const m of items) {
9119
+ const when = m.created_at ? new Date(m.created_at).toISOString().slice(0, 10) : "";
9120
+ context.addMessage("system", ` ${m.id?.slice(0, 8)} \xB7 ${when} ${m.content}`);
9121
+ }
9122
+ return;
9123
+ }
9124
+ if (sub === "add" || sub === "save") {
9125
+ const content = rest.join(" ").trim();
9126
+ if (!content) {
9127
+ context.addMessage("system", "Usage: /memories add <content>");
9128
+ return;
9129
+ }
9130
+ const m = await context.client.createMemory(content);
9131
+ context.addMessage("system", `Saved (${m.id?.slice(0, 8)}).`);
9132
+ return;
9133
+ }
9134
+ if (sub === "delete" || sub === "rm" || sub === "remove") {
9135
+ const id = rest[0]?.trim();
9136
+ if (!id) {
9137
+ context.addMessage("system", "Usage: /memories delete <id>");
9138
+ return;
9139
+ }
9140
+ await context.client.deleteMemory(id);
9141
+ context.addMessage("system", `Deleted ${id.slice(0, 8)}.`);
9142
+ return;
9143
+ }
9144
+ context.addMessage("error", `Unknown /memories subcommand '${sub}'. Try: list, add, delete.`);
9145
+ } catch (e) {
9146
+ context.addMessage("error", `Memories: ${e.message}`);
9147
+ }
9148
+ }
9149
+ };
9150
+ }
9151
+ });
9152
+
8785
9153
  // src/commands/clear.ts
8786
9154
  var clearCommand;
8787
9155
  var init_clear = __esm({
@@ -10070,7 +10438,7 @@ var init_version = __esm({
10070
10438
  description: "Show version information for CLI and core packages",
10071
10439
  category: "debug",
10072
10440
  handler: async (_args, context) => {
10073
- const cliVersion = true ? "2.3.6-beta.23" : "(dev)";
10441
+ const cliVersion = true ? "2.3.6-beta.24" : "(dev)";
10074
10442
  const coreVersion = true ? "2.3.2" : "(dev)";
10075
10443
  const runtime = process.version;
10076
10444
  context.addMessage("system", "MSapling Version Info");
@@ -10079,7 +10447,7 @@ var init_version = __esm({
10079
10447
  context.addMessage("system", row2("Core (@msapling/core)", coreVersion));
10080
10448
  context.addMessage("system", row2("Runtime (Node/Bun)", runtime));
10081
10449
  try {
10082
- const ts = "2026-05-28T19:20:38.494Z";
10450
+ const ts = "2026-05-28T19:31:59.228Z";
10083
10451
  if (ts && ts !== "__BUILD_TIMESTAMP__") {
10084
10452
  context.addMessage("system", row2("Build Timestamp", ts));
10085
10453
  }
@@ -10819,6 +11187,9 @@ var init_commands = __esm({
10819
11187
  init_help();
10820
11188
  init_chat();
10821
11189
  init_broadcast();
11190
+ init_ollama();
11191
+ init_keys();
11192
+ init_memories();
10822
11193
  init_clear();
10823
11194
  init_mode();
10824
11195
  init_model();
@@ -10855,6 +11226,9 @@ var init_commands = __esm({
10855
11226
  chatCommand,
10856
11227
  chatsCommand,
10857
11228
  broadcastCommand,
11229
+ ollamaCommand,
11230
+ keysCommand,
11231
+ memoriesCommand,
10858
11232
  clearCommand,
10859
11233
  modeCommand,
10860
11234
  modelCommand,
@@ -14484,7 +14858,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
14484
14858
  var Header = () => /* @__PURE__ */ jsxs(Box, { borderStyle: "single", borderColor: "cyan", paddingX: 1, marginBottom: 1, children: [
14485
14859
  /* @__PURE__ */ jsxs(Text, { bold: true, color: "cyan", children: [
14486
14860
  "\u25CF MSapling CLI v",
14487
- "2.3.6-beta.23"
14861
+ "2.3.6-beta.24"
14488
14862
  ] }),
14489
14863
  /* @__PURE__ */ jsx(Box, { marginLeft: 2, children: /* @__PURE__ */ jsx(Text, { color: "gray", children: "Platinum Tier Architecture" }) })
14490
14864
  ] });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mtreeai/msapling-cli",
3
- "version": "2.3.6-beta.23",
3
+ "version": "2.3.6-beta.24",
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",