@mtreeai/msapling-cli 2.3.6-beta.19 → 2.3.6-beta.20
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 +842 -42
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -457,9 +457,9 @@ var init_src = __esm({
|
|
|
457
457
|
* Single-turn chat: send a prompt, accumulate the streaming response into
|
|
458
458
|
* a single string. Used by the MCP `msapling_chat` tool.
|
|
459
459
|
*/
|
|
460
|
-
async chatOnce(
|
|
460
|
+
async chatOnce(prompt4, model, chatId) {
|
|
461
461
|
let acc = "";
|
|
462
|
-
for await (const chunk of this.streamChat({ content:
|
|
462
|
+
for await (const chunk of this.streamChat({ content: prompt4, model, chat_id: chatId ?? "" })) {
|
|
463
463
|
if (chunk.delta) acc += chunk.delta;
|
|
464
464
|
else if (chunk.content) acc += chunk.content;
|
|
465
465
|
}
|
|
@@ -470,10 +470,10 @@ var init_src = __esm({
|
|
|
470
470
|
* one row per model with status + response or error. Used by the MCP
|
|
471
471
|
* `msapling_multi_chat` tool and the existing /swarm slash command path.
|
|
472
472
|
*/
|
|
473
|
-
async multiChat(
|
|
473
|
+
async multiChat(prompt4, models) {
|
|
474
474
|
const results = await Promise.all(models.map(async (model) => {
|
|
475
475
|
try {
|
|
476
|
-
const response = await this.chatOnce(
|
|
476
|
+
const response = await this.chatOnce(prompt4, model);
|
|
477
477
|
return { model, status: "ok", response };
|
|
478
478
|
} catch (e) {
|
|
479
479
|
return { model, status: "error", error: e?.message ?? String(e) };
|
|
@@ -2365,7 +2365,7 @@ var init_DispatchAgentTool = __esm({
|
|
|
2365
2365
|
isError: true
|
|
2366
2366
|
};
|
|
2367
2367
|
}
|
|
2368
|
-
const
|
|
2368
|
+
const prompt4 = args2.prompt.trim().slice(0, MAX_PROMPT_CHARS);
|
|
2369
2369
|
const model = typeof args2.model === "string" && args2.model.trim() ? args2.model.trim() : DEFAULT_SUB_AGENT_MODEL;
|
|
2370
2370
|
const chatId = typeof args2.chat_id === "string" && args2.chat_id.trim() ? args2.chat_id.trim() : this.parentChatId;
|
|
2371
2371
|
const description = typeof args2.description === "string" && args2.description.trim() ? args2.description.trim() : "sub-task";
|
|
@@ -2378,7 +2378,7 @@ var init_DispatchAgentTool = __esm({
|
|
|
2378
2378
|
);
|
|
2379
2379
|
const streamPromise = (async () => {
|
|
2380
2380
|
const stream = this.client.streamChat({
|
|
2381
|
-
prompt,
|
|
2381
|
+
prompt: prompt4,
|
|
2382
2382
|
model,
|
|
2383
2383
|
// No tools — sub-agent is read/reasoning only.
|
|
2384
2384
|
tools: [],
|
|
@@ -4248,7 +4248,7 @@ var init_ShadowService = __esm({
|
|
|
4248
4248
|
* that regex might miss.
|
|
4249
4249
|
*/
|
|
4250
4250
|
async redact(text) {
|
|
4251
|
-
const
|
|
4251
|
+
const prompt4 = `You are a security shadow. Redact all API keys, secrets, and private credentials from the following text.
|
|
4252
4252
|
Return the text exactly as is, but replace secrets with [REDACTED].
|
|
4253
4253
|
If no secrets are found, return the text UNCHANGED.
|
|
4254
4254
|
|
|
@@ -4256,7 +4256,7 @@ var init_ShadowService = __esm({
|
|
|
4256
4256
|
${text}`;
|
|
4257
4257
|
let result = "";
|
|
4258
4258
|
for await (const chunk of this.client.streamChat({
|
|
4259
|
-
prompt,
|
|
4259
|
+
prompt: prompt4,
|
|
4260
4260
|
model: this.shadowModel,
|
|
4261
4261
|
history: []
|
|
4262
4262
|
})) {
|
|
@@ -4268,14 +4268,14 @@ var init_ShadowService = __esm({
|
|
|
4268
4268
|
* Shadow Verification: Double-check a primary model's plan or tool call.
|
|
4269
4269
|
*/
|
|
4270
4270
|
async verifyAction(action, context) {
|
|
4271
|
-
const
|
|
4271
|
+
const prompt4 = `Review this proposed agent action for safety and logical correctness.
|
|
4272
4272
|
CONTEXT: ${context}
|
|
4273
4273
|
PROPOSED ACTION: ${action}
|
|
4274
4274
|
|
|
4275
4275
|
Respond in JSON: { "approved": boolean, "reasoning": "string" }`;
|
|
4276
4276
|
let response = "";
|
|
4277
4277
|
for await (const chunk of this.client.streamChat({
|
|
4278
|
-
prompt,
|
|
4278
|
+
prompt: prompt4,
|
|
4279
4279
|
model: this.shadowModel,
|
|
4280
4280
|
history: []
|
|
4281
4281
|
})) {
|
|
@@ -4293,14 +4293,14 @@ var init_ShadowService = __esm({
|
|
|
4293
4293
|
* Double-Read: Read a file using the shadow model to verify findings.
|
|
4294
4294
|
*/
|
|
4295
4295
|
async doubleRead(filePath, content, query) {
|
|
4296
|
-
const
|
|
4296
|
+
const prompt4 = `Verification Read of ${filePath}.
|
|
4297
4297
|
The primary agent is looking for: ${query}
|
|
4298
4298
|
|
|
4299
4299
|
FILE CONTENT:
|
|
4300
4300
|
${content}`;
|
|
4301
4301
|
let response = "";
|
|
4302
4302
|
for await (const chunk of this.client.streamChat({
|
|
4303
|
-
prompt,
|
|
4303
|
+
prompt: prompt4,
|
|
4304
4304
|
model: this.shadowModel,
|
|
4305
4305
|
history: []
|
|
4306
4306
|
})) {
|
|
@@ -4458,7 +4458,15 @@ var init_ToolExecutor = __esm({
|
|
|
4458
4458
|
mdrive;
|
|
4459
4459
|
voice;
|
|
4460
4460
|
shadow;
|
|
4461
|
-
|
|
4461
|
+
// CLI-TOOLS-ENABLED-DEFAULT-ON-01 (Iter 30): default to true so the CLI's
|
|
4462
|
+
// 19 client-side tools are actually advertised to the backend in
|
|
4463
|
+
// ChatMessagePayload.tools. Previously false, and nothing in the CLI ever
|
|
4464
|
+
// called setToolsEnabled(true), so getToolSchemas() always returned [] and
|
|
4465
|
+
// the LLM had no idea tools existed — which is why every chat refused
|
|
4466
|
+
// file/shell access. Side effects are still gated by the permission mode
|
|
4467
|
+
// (default prompts before each call) and the persistent trust store.
|
|
4468
|
+
// Opt out via MSAPLING_TOOLS_ENABLED=false.
|
|
4469
|
+
toolsEnabled = process.env.MSAPLING_TOOLS_ENABLED !== "false";
|
|
4462
4470
|
mode = "default";
|
|
4463
4471
|
approvalCallback = null;
|
|
4464
4472
|
/**
|
|
@@ -4696,12 +4704,14 @@ Please approve the diff in the UI to sync this change locally.`
|
|
|
4696
4704
|
const builtin = Array.from(this.tools.values()).map((t) => ({
|
|
4697
4705
|
name: t.name,
|
|
4698
4706
|
description: t.description,
|
|
4699
|
-
parameters: t.parameters
|
|
4707
|
+
parameters: t.parameters,
|
|
4708
|
+
input_schema: t.parameters
|
|
4700
4709
|
}));
|
|
4701
4710
|
const mcp = this.getMCPTools().map(({ server, tool }) => ({
|
|
4702
4711
|
name: tool.name,
|
|
4703
4712
|
description: tool.description ? `[mcp:${server}] ${tool.description}` : `[mcp:${server}]`,
|
|
4704
|
-
parameters: tool.inputSchema ?? {}
|
|
4713
|
+
parameters: tool.inputSchema ?? {},
|
|
4714
|
+
input_schema: tool.inputSchema ?? {}
|
|
4705
4715
|
}));
|
|
4706
4716
|
return [...builtin, ...mcp];
|
|
4707
4717
|
}
|
|
@@ -5017,11 +5027,11 @@ var init_Agent = __esm({
|
|
|
5017
5027
|
* Returns null when the backend stream did not include a usage chunk (e.g.
|
|
5018
5028
|
* old backend versions or local/offline models that omit billing data).
|
|
5019
5029
|
*/
|
|
5020
|
-
async runWorkerTurn(chatId,
|
|
5030
|
+
async runWorkerTurn(chatId, prompt4, model, onContent) {
|
|
5021
5031
|
if (this.hooks) {
|
|
5022
5032
|
const outcomes = await this.hooks.fire({
|
|
5023
5033
|
event: "user-prompt-submit",
|
|
5024
|
-
payload:
|
|
5034
|
+
payload: prompt4,
|
|
5025
5035
|
cwd: this.projectRoot
|
|
5026
5036
|
});
|
|
5027
5037
|
const blocker = HookRunner.anyBlocked(outcomes);
|
|
@@ -5033,7 +5043,7 @@ var init_Agent = __esm({
|
|
|
5033
5043
|
}
|
|
5034
5044
|
const config = await this.getProjectConfig();
|
|
5035
5045
|
const MAX_WORKER_TURN_DEPTH = 25;
|
|
5036
|
-
const queue = [
|
|
5046
|
+
const queue = [prompt4];
|
|
5037
5047
|
let rounds = 0;
|
|
5038
5048
|
let streamUsage = null;
|
|
5039
5049
|
while (queue.length > 0 && rounds < MAX_WORKER_TURN_DEPTH) {
|
|
@@ -5122,9 +5132,9 @@ ${next}`;
|
|
|
5122
5132
|
}
|
|
5123
5133
|
return streamUsage;
|
|
5124
5134
|
}
|
|
5125
|
-
async run(
|
|
5135
|
+
async run(prompt4, model) {
|
|
5126
5136
|
let fullResponse = "";
|
|
5127
|
-
const stream = this.client.streamChat({ prompt, model });
|
|
5137
|
+
const stream = this.client.streamChat({ prompt: prompt4, model });
|
|
5128
5138
|
for await (const chunk of stream) {
|
|
5129
5139
|
if (chunk.content) fullResponse += chunk.content;
|
|
5130
5140
|
}
|
|
@@ -7976,6 +7986,47 @@ ${combinedData}`;
|
|
|
7976
7986
|
});
|
|
7977
7987
|
|
|
7978
7988
|
// ../core/src/index.ts
|
|
7989
|
+
var src_exports2 = {};
|
|
7990
|
+
__export(src_exports2, {
|
|
7991
|
+
APPROVAL_GATED: () => APPROVAL_GATED,
|
|
7992
|
+
Agent: () => Agent,
|
|
7993
|
+
AsyncMutex: () => AsyncMutex,
|
|
7994
|
+
BashTool: () => BashTool,
|
|
7995
|
+
ContextBudget: () => ContextBudget,
|
|
7996
|
+
DEFAULT_SETTINGS: () => DEFAULT_SETTINGS,
|
|
7997
|
+
DeleteFileTool: () => DeleteFileTool,
|
|
7998
|
+
DispatchAgentTool: () => DispatchAgentTool,
|
|
7999
|
+
GlobFilesTool: () => GlobFilesTool,
|
|
8000
|
+
GrepSearchTool: () => GrepSearchTool,
|
|
8001
|
+
HookRunner: () => HookRunner,
|
|
8002
|
+
ListDirectoryTool: () => ListDirectoryTool,
|
|
8003
|
+
MCPClient: () => MCPClient,
|
|
8004
|
+
MCPClientError: () => MCPClientError,
|
|
8005
|
+
MCPRegistry: () => MCPRegistry,
|
|
8006
|
+
MoveFileTool: () => MoveFileTool,
|
|
8007
|
+
MultiEditFileTool: () => MultiEditFileTool,
|
|
8008
|
+
NotebookEditTool: () => NotebookEditTool,
|
|
8009
|
+
NotebookReadTool: () => NotebookReadTool,
|
|
8010
|
+
PatchFileTool: () => PatchFileTool,
|
|
8011
|
+
StorageManager: () => StorageManager,
|
|
8012
|
+
SwarmManager: () => SwarmManager,
|
|
8013
|
+
TodoReadTool: () => TodoReadTool,
|
|
8014
|
+
TodoStore: () => TodoStore,
|
|
8015
|
+
TodoWriteTool: () => TodoWriteTool,
|
|
8016
|
+
ToolExecutor: () => ToolExecutor,
|
|
8017
|
+
TrustStore: () => TrustStore,
|
|
8018
|
+
WebFetchTool: () => WebFetchTool,
|
|
8019
|
+
WriteFileTool: () => WriteFileTool,
|
|
8020
|
+
buildCompactionPrompt: () => buildCompactionPrompt,
|
|
8021
|
+
buildHwContext: () => buildHwContext,
|
|
8022
|
+
ensureConfigDir: () => ensureConfigDir,
|
|
8023
|
+
formatCell: () => formatCell,
|
|
8024
|
+
formatNotebookHeader: () => formatNotebookHeader,
|
|
8025
|
+
formatTodos: () => formatTodos,
|
|
8026
|
+
loadProjectConfig: () => loadProjectConfig,
|
|
8027
|
+
loadSettings: () => loadSettings,
|
|
8028
|
+
takeSnapshot: () => takeSnapshot
|
|
8029
|
+
});
|
|
7979
8030
|
var init_src3 = __esm({
|
|
7980
8031
|
"../core/src/index.ts"() {
|
|
7981
8032
|
"use strict";
|
|
@@ -8023,11 +8074,11 @@ function setRawModeGuarded(stdin, mode) {
|
|
|
8023
8074
|
} catch (e) {
|
|
8024
8075
|
}
|
|
8025
8076
|
}
|
|
8026
|
-
async function promptPassword(
|
|
8077
|
+
async function promptPassword(prompt4) {
|
|
8027
8078
|
return new Promise((resolve18) => {
|
|
8028
8079
|
const stdin = process.stdin;
|
|
8029
8080
|
const stdout = process.stdout;
|
|
8030
|
-
stdout.write(
|
|
8081
|
+
stdout.write(prompt4);
|
|
8031
8082
|
const wasRaw = stdin.isRaw ?? false;
|
|
8032
8083
|
setRawModeGuarded(stdin, true);
|
|
8033
8084
|
let password = "";
|
|
@@ -8906,12 +8957,12 @@ var init_review = __esm({
|
|
|
8906
8957
|
} catch (e) {
|
|
8907
8958
|
content = `Review target: ${target}`;
|
|
8908
8959
|
}
|
|
8909
|
-
const
|
|
8960
|
+
const prompt4 = `Please review the following code/target for issues, best practices, and bugs:
|
|
8910
8961
|
|
|
8911
8962
|
${content}`;
|
|
8912
8963
|
context.addMessage("user", `/review ${target}`);
|
|
8913
8964
|
if (context.runAgent) {
|
|
8914
|
-
await context.runAgent(
|
|
8965
|
+
await context.runAgent(prompt4);
|
|
8915
8966
|
} else {
|
|
8916
8967
|
context.addMessage("error", "Agent execution from commands not wired up. Use regular prompt for now.");
|
|
8917
8968
|
}
|
|
@@ -8938,15 +8989,15 @@ var init_swarm = __esm({
|
|
|
8938
8989
|
description: `Run the prompt against ${DEFAULT_MODELS.length} models in parallel; synthesize.`,
|
|
8939
8990
|
category: "swarm",
|
|
8940
8991
|
handler: async (args2, context) => {
|
|
8941
|
-
const
|
|
8942
|
-
if (!
|
|
8992
|
+
const prompt4 = args2.join(" ").trim();
|
|
8993
|
+
if (!prompt4) {
|
|
8943
8994
|
context.addMessage("system", "Usage: /swarm <prompt>");
|
|
8944
8995
|
return;
|
|
8945
8996
|
}
|
|
8946
8997
|
const tasks = DEFAULT_MODELS.map((model, i) => ({
|
|
8947
8998
|
id: `w${i + 1}`,
|
|
8948
8999
|
name: `worker-${i + 1}`,
|
|
8949
|
-
prompt,
|
|
9000
|
+
prompt: prompt4,
|
|
8950
9001
|
model
|
|
8951
9002
|
}));
|
|
8952
9003
|
const initialWorkers = tasks.map((t) => ({
|
|
@@ -9046,13 +9097,13 @@ var init_recipe = __esm({
|
|
|
9046
9097
|
return;
|
|
9047
9098
|
}
|
|
9048
9099
|
const name = args2[0];
|
|
9049
|
-
const
|
|
9100
|
+
const prompt4 = args2.slice(1).join(" ").trim();
|
|
9050
9101
|
const path2 = findRecipe(name, cwd);
|
|
9051
9102
|
if (!path2) {
|
|
9052
9103
|
context.addMessage("error", `Recipe '${name}' not found. Tried: ${RECIPE_DIRS.map((d) => `${d}/${name}{,_workflow}.{yaml,yml}`).join(", ")}`);
|
|
9053
9104
|
return;
|
|
9054
9105
|
}
|
|
9055
|
-
if (!
|
|
9106
|
+
if (!prompt4) {
|
|
9056
9107
|
context.addMessage("system", `Usage: /recipe ${name} <prompt> (the prompt fills $SELECTION / $PROMPT in step templates)`);
|
|
9057
9108
|
return;
|
|
9058
9109
|
}
|
|
@@ -9086,7 +9137,7 @@ var init_recipe = __esm({
|
|
|
9086
9137
|
const sysPrompt = String(params.system_prompt ?? "");
|
|
9087
9138
|
const tpl = String(params.prompt_template ?? "$PROMPT");
|
|
9088
9139
|
const model = String(params.model ?? context.getModel());
|
|
9089
|
-
const rendered = renderTemplate(tpl, { prompt });
|
|
9140
|
+
const rendered = renderTemplate(tpl, { prompt: prompt4 });
|
|
9090
9141
|
context.addMessage("system", `${stepLabel} (model: ${model})`);
|
|
9091
9142
|
const fullPrompt = sysPrompt ? `[System: ${sysPrompt}]
|
|
9092
9143
|
|
|
@@ -9201,13 +9252,13 @@ var init_skill = __esm({
|
|
|
9201
9252
|
return;
|
|
9202
9253
|
}
|
|
9203
9254
|
const ref = args2[0];
|
|
9204
|
-
const
|
|
9255
|
+
const prompt4 = args2.slice(1).join(" ").trim();
|
|
9205
9256
|
const skill = findSkill(root, ref);
|
|
9206
9257
|
if (!skill) {
|
|
9207
9258
|
context.addMessage("error", `No skill matches '${ref}'. Run /skill (no args) for the list.`);
|
|
9208
9259
|
return;
|
|
9209
9260
|
}
|
|
9210
|
-
if (!
|
|
9261
|
+
if (!prompt4) {
|
|
9211
9262
|
context.addMessage("system", `Usage: /skill ${skill.domain}/${skill.name} <prompt>`);
|
|
9212
9263
|
return;
|
|
9213
9264
|
}
|
|
@@ -9224,7 +9275,7 @@ ${body.trim()}
|
|
|
9224
9275
|
|
|
9225
9276
|
---
|
|
9226
9277
|
|
|
9227
|
-
${
|
|
9278
|
+
${prompt4}`;
|
|
9228
9279
|
if (context.runAgent) {
|
|
9229
9280
|
context.addMessage("system", `Running /skill ${skill.domain}/${skill.name}...`);
|
|
9230
9281
|
await context.runAgent(fullPrompt);
|
|
@@ -9694,7 +9745,7 @@ var init_version = __esm({
|
|
|
9694
9745
|
description: "Show version information for CLI and core packages",
|
|
9695
9746
|
category: "debug",
|
|
9696
9747
|
handler: async (_args, context) => {
|
|
9697
|
-
const cliVersion = true ? "2.3.6-beta.
|
|
9748
|
+
const cliVersion = true ? "2.3.6-beta.20" : "(dev)";
|
|
9698
9749
|
const coreVersion = true ? "2.3.2" : "(dev)";
|
|
9699
9750
|
const runtime = process.version;
|
|
9700
9751
|
context.addMessage("system", "MSapling Version Info");
|
|
@@ -9703,7 +9754,7 @@ var init_version = __esm({
|
|
|
9703
9754
|
context.addMessage("system", row2("Core (@msapling/core)", coreVersion));
|
|
9704
9755
|
context.addMessage("system", row2("Runtime (Node/Bun)", runtime));
|
|
9705
9756
|
try {
|
|
9706
|
-
const ts = "2026-05-28T18:
|
|
9757
|
+
const ts = "2026-05-28T18:54:30.369Z";
|
|
9707
9758
|
if (ts && ts !== "__BUILD_TIMESTAMP__") {
|
|
9708
9759
|
context.addMessage("system", row2("Build Timestamp", ts));
|
|
9709
9760
|
}
|
|
@@ -10673,6 +10724,727 @@ var init_exec = __esm({
|
|
|
10673
10724
|
}
|
|
10674
10725
|
});
|
|
10675
10726
|
|
|
10727
|
+
// src/commands/billing/api.ts
|
|
10728
|
+
async function getAuthToken() {
|
|
10729
|
+
if (process.env.MSAPLING_AUTH_TOKEN) return process.env.MSAPLING_AUTH_TOKEN;
|
|
10730
|
+
try {
|
|
10731
|
+
const { StorageManager: StorageManager3 } = await Promise.resolve().then(() => (init_src3(), src_exports2));
|
|
10732
|
+
const sm = new StorageManager3();
|
|
10733
|
+
return await sm.loadToken();
|
|
10734
|
+
} catch {
|
|
10735
|
+
return null;
|
|
10736
|
+
}
|
|
10737
|
+
}
|
|
10738
|
+
function getBaseURL() {
|
|
10739
|
+
return (process.env.MSAPLING_API_URL ?? "https://api.msapling.com").replace(/\/$/, "");
|
|
10740
|
+
}
|
|
10741
|
+
async function apiFetch(path2, opts = {}) {
|
|
10742
|
+
const token = await getAuthToken();
|
|
10743
|
+
const headers = {
|
|
10744
|
+
"X-Surface": "cli",
|
|
10745
|
+
"Content-Type": "application/json",
|
|
10746
|
+
...opts.headers
|
|
10747
|
+
};
|
|
10748
|
+
if (token) {
|
|
10749
|
+
headers["Authorization"] = `Bearer ${token}`;
|
|
10750
|
+
}
|
|
10751
|
+
const body = opts.json !== void 0 ? JSON.stringify(opts.json) : opts.body;
|
|
10752
|
+
return fetch(`${getBaseURL()}${path2}`, { ...opts, headers, body });
|
|
10753
|
+
}
|
|
10754
|
+
async function fetchBalance() {
|
|
10755
|
+
const res = await apiFetch("/api/billing/balance");
|
|
10756
|
+
if (!res.ok) throw new Error(`Balance fetch failed: ${res.status}`);
|
|
10757
|
+
return res.json();
|
|
10758
|
+
}
|
|
10759
|
+
async function fetchFounderStatus() {
|
|
10760
|
+
const res = await apiFetch("/api/billing/founder-status");
|
|
10761
|
+
if (!res.ok) return { lifetime_total_spots: 50, lifetime_sold: 0, lifetime_spots_remaining: 50 };
|
|
10762
|
+
return res.json();
|
|
10763
|
+
}
|
|
10764
|
+
async function postCheckout(payload) {
|
|
10765
|
+
const res = await apiFetch("/api/billing/checkout", { method: "POST", json: payload });
|
|
10766
|
+
if (!res.ok) {
|
|
10767
|
+
const err = await res.json().catch(() => ({}));
|
|
10768
|
+
throw new Error(err.detail ?? `Checkout failed: ${res.status}`);
|
|
10769
|
+
}
|
|
10770
|
+
return res.json();
|
|
10771
|
+
}
|
|
10772
|
+
async function fetchCheckoutStatus(token) {
|
|
10773
|
+
const res = await apiFetch(`/api/billing/checkout/${token}/status`);
|
|
10774
|
+
if (!res.ok) return { state: "expired" };
|
|
10775
|
+
return res.json();
|
|
10776
|
+
}
|
|
10777
|
+
async function patchAutoTopup(payload) {
|
|
10778
|
+
const res = await apiFetch("/api/billing/auto-topup", { method: "PATCH", json: payload });
|
|
10779
|
+
if (!res.ok) throw new Error(`Auto-topup update failed: ${res.status}`);
|
|
10780
|
+
return res.json();
|
|
10781
|
+
}
|
|
10782
|
+
async function postSubscriptionCancel() {
|
|
10783
|
+
const res = await apiFetch("/subscription/cancel", { method: "POST" });
|
|
10784
|
+
if (!res.ok) throw new Error(`Cancel failed: ${res.status}`);
|
|
10785
|
+
return res.json();
|
|
10786
|
+
}
|
|
10787
|
+
async function fetchUserLookup(username) {
|
|
10788
|
+
const res = await apiFetch(`/api/users/lookup?username=${encodeURIComponent(username)}`);
|
|
10789
|
+
if (res.status === 404) return null;
|
|
10790
|
+
if (!res.ok) throw new Error(`User lookup failed: ${res.status}`);
|
|
10791
|
+
return res.json();
|
|
10792
|
+
}
|
|
10793
|
+
async function postGiftRedeem(code) {
|
|
10794
|
+
const res = await apiFetch("/api/billing/gift/redeem", { method: "POST", json: { code } });
|
|
10795
|
+
if (!res.ok) {
|
|
10796
|
+
const err = await res.json().catch(() => ({}));
|
|
10797
|
+
throw new Error(err.detail ?? `Redeem failed: ${res.status}`);
|
|
10798
|
+
}
|
|
10799
|
+
return res.json();
|
|
10800
|
+
}
|
|
10801
|
+
var init_api = __esm({
|
|
10802
|
+
"src/commands/billing/api.ts"() {
|
|
10803
|
+
"use strict";
|
|
10804
|
+
init_esm_shims();
|
|
10805
|
+
}
|
|
10806
|
+
});
|
|
10807
|
+
|
|
10808
|
+
// src/commands/billing/format.ts
|
|
10809
|
+
function formatTier(tier) {
|
|
10810
|
+
const map = {
|
|
10811
|
+
free: "Free",
|
|
10812
|
+
monthly: "Pro ($20/mo)",
|
|
10813
|
+
lifetime: "Lifetime ($129 once, Founder)",
|
|
10814
|
+
pro: "Pro"
|
|
10815
|
+
};
|
|
10816
|
+
return map[tier.toLowerCase()] ?? tier;
|
|
10817
|
+
}
|
|
10818
|
+
function formatUSD(amount) {
|
|
10819
|
+
return `$${amount.toFixed(2)}`;
|
|
10820
|
+
}
|
|
10821
|
+
function formatCredits(amount) {
|
|
10822
|
+
return `${amount.toFixed(4)} credits`;
|
|
10823
|
+
}
|
|
10824
|
+
function formatCreditsAsUSD(credits) {
|
|
10825
|
+
return formatUSD(credits * CREDIT_RATE_USD);
|
|
10826
|
+
}
|
|
10827
|
+
function formatSubSummary(info) {
|
|
10828
|
+
const tierLabel = formatTier(info.tier);
|
|
10829
|
+
const creditsStr = formatCredits(info.fuel_credits);
|
|
10830
|
+
const lines = [
|
|
10831
|
+
`Tier: ${tierLabel}`,
|
|
10832
|
+
`Credits: ${creditsStr} (${formatCreditsAsUSD(info.fuel_credits)})`
|
|
10833
|
+
];
|
|
10834
|
+
if (info.auto_topup.enabled) {
|
|
10835
|
+
lines.push(
|
|
10836
|
+
`Auto-topup: ON \u2014 tops up ${formatUSD(info.auto_topup.amount_usd)} when below ${formatUSD(info.auto_topup.threshold_usd)} (cap ${formatUSD(info.auto_topup.max_per_month_usd)}/mo)`
|
|
10837
|
+
);
|
|
10838
|
+
} else {
|
|
10839
|
+
lines.push("Auto-topup: OFF");
|
|
10840
|
+
}
|
|
10841
|
+
return lines.join("\n");
|
|
10842
|
+
}
|
|
10843
|
+
var CREDIT_RATE_USD, TOPUP_MENU;
|
|
10844
|
+
var init_format = __esm({
|
|
10845
|
+
"src/commands/billing/format.ts"() {
|
|
10846
|
+
"use strict";
|
|
10847
|
+
init_esm_shims();
|
|
10848
|
+
CREDIT_RATE_USD = 1.556;
|
|
10849
|
+
TOPUP_MENU = [
|
|
10850
|
+
{ key: "fuel_5", label: "5 credits \u2014 $7.78" },
|
|
10851
|
+
{ key: "fuel_10", label: "10 credits \u2014 $15.56" }
|
|
10852
|
+
];
|
|
10853
|
+
}
|
|
10854
|
+
});
|
|
10855
|
+
|
|
10856
|
+
// src/commands/billing/open-browser.ts
|
|
10857
|
+
import { spawn as spawn10 } from "child_process";
|
|
10858
|
+
async function openBrowser(url) {
|
|
10859
|
+
const platform4 = process.platform;
|
|
10860
|
+
let cmd;
|
|
10861
|
+
let args2;
|
|
10862
|
+
if (platform4 === "win32") {
|
|
10863
|
+
cmd = "cmd";
|
|
10864
|
+
args2 = ["/c", "start", "", url];
|
|
10865
|
+
} else if (platform4 === "darwin") {
|
|
10866
|
+
cmd = "open";
|
|
10867
|
+
args2 = [url];
|
|
10868
|
+
} else {
|
|
10869
|
+
cmd = "xdg-open";
|
|
10870
|
+
args2 = [url];
|
|
10871
|
+
}
|
|
10872
|
+
return new Promise((resolve18) => {
|
|
10873
|
+
try {
|
|
10874
|
+
const child = spawn10(cmd, args2, { stdio: "ignore", detached: true });
|
|
10875
|
+
child.unref();
|
|
10876
|
+
} catch {
|
|
10877
|
+
}
|
|
10878
|
+
resolve18();
|
|
10879
|
+
});
|
|
10880
|
+
}
|
|
10881
|
+
var init_open_browser = __esm({
|
|
10882
|
+
"src/commands/billing/open-browser.ts"() {
|
|
10883
|
+
"use strict";
|
|
10884
|
+
init_esm_shims();
|
|
10885
|
+
}
|
|
10886
|
+
});
|
|
10887
|
+
|
|
10888
|
+
// src/commands/billing/checkout.ts
|
|
10889
|
+
async function runCheckout(opts) {
|
|
10890
|
+
const surface = opts.surface ?? "cli";
|
|
10891
|
+
const log = opts.onStatus ?? (() => {
|
|
10892
|
+
});
|
|
10893
|
+
log("Creating checkout session...");
|
|
10894
|
+
const session = await postCheckout({
|
|
10895
|
+
product: opts.product,
|
|
10896
|
+
surface,
|
|
10897
|
+
recipient: opts.recipient,
|
|
10898
|
+
gift_method: opts.gift_method
|
|
10899
|
+
});
|
|
10900
|
+
log(`Opening browser: ${session.checkout_url}`);
|
|
10901
|
+
await openBrowser(session.checkout_url);
|
|
10902
|
+
log("Browser opened. Complete payment in the browser, then return here.");
|
|
10903
|
+
log("Waiting for payment confirmation (up to 15 minutes)...");
|
|
10904
|
+
const sseResult = await _watchViaSSE(session.checkout_token, log);
|
|
10905
|
+
if (sseResult !== null) return sseResult;
|
|
10906
|
+
return _watchViaPoll(session.checkout_token, log);
|
|
10907
|
+
}
|
|
10908
|
+
async function _watchViaSSE(token, log) {
|
|
10909
|
+
try {
|
|
10910
|
+
const res = await apiFetch(`/api/billing/checkout/${token}/watch`, {
|
|
10911
|
+
headers: { Accept: "text/event-stream" }
|
|
10912
|
+
});
|
|
10913
|
+
if (!res.ok || !res.body) return null;
|
|
10914
|
+
const reader = res.body.getReader();
|
|
10915
|
+
const decoder = new TextDecoder();
|
|
10916
|
+
const deadline = Date.now() + MAX_POLL_MS;
|
|
10917
|
+
let buffer = "";
|
|
10918
|
+
while (Date.now() < deadline) {
|
|
10919
|
+
const { value, done } = await reader.read();
|
|
10920
|
+
if (done) break;
|
|
10921
|
+
buffer += decoder.decode(value, { stream: true });
|
|
10922
|
+
const lines = buffer.split("\n");
|
|
10923
|
+
buffer = lines.pop() ?? "";
|
|
10924
|
+
for (const line of lines) {
|
|
10925
|
+
if (line.startsWith("data: ")) {
|
|
10926
|
+
try {
|
|
10927
|
+
const payload = JSON.parse(line.slice(6));
|
|
10928
|
+
if (TERMINAL_STATES.has(payload.state)) {
|
|
10929
|
+
return payload;
|
|
10930
|
+
}
|
|
10931
|
+
} catch {
|
|
10932
|
+
}
|
|
10933
|
+
}
|
|
10934
|
+
}
|
|
10935
|
+
}
|
|
10936
|
+
return null;
|
|
10937
|
+
} catch {
|
|
10938
|
+
return null;
|
|
10939
|
+
}
|
|
10940
|
+
}
|
|
10941
|
+
async function _watchViaPoll(token, log) {
|
|
10942
|
+
const deadline = Date.now() + MAX_POLL_MS;
|
|
10943
|
+
while (Date.now() < deadline) {
|
|
10944
|
+
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
10945
|
+
const status = await fetchCheckoutStatus(token);
|
|
10946
|
+
if (TERMINAL_STATES.has(status.state)) {
|
|
10947
|
+
return status;
|
|
10948
|
+
}
|
|
10949
|
+
}
|
|
10950
|
+
return { state: "expired" };
|
|
10951
|
+
}
|
|
10952
|
+
var TERMINAL_STATES, POLL_INTERVAL_MS, MAX_POLL_MS;
|
|
10953
|
+
var init_checkout = __esm({
|
|
10954
|
+
"src/commands/billing/checkout.ts"() {
|
|
10955
|
+
"use strict";
|
|
10956
|
+
init_esm_shims();
|
|
10957
|
+
init_api();
|
|
10958
|
+
init_open_browser();
|
|
10959
|
+
TERMINAL_STATES = /* @__PURE__ */ new Set(["completed", "cancelled", "expired"]);
|
|
10960
|
+
POLL_INTERVAL_MS = 2e3;
|
|
10961
|
+
MAX_POLL_MS = 14 * 60 * 1e3;
|
|
10962
|
+
}
|
|
10963
|
+
});
|
|
10964
|
+
|
|
10965
|
+
// src/commands/billing/sub.ts
|
|
10966
|
+
import * as readline from "readline";
|
|
10967
|
+
function prompt(rl, question) {
|
|
10968
|
+
return new Promise((resolve18) => rl.question(question, resolve18));
|
|
10969
|
+
}
|
|
10970
|
+
async function runSub(argv) {
|
|
10971
|
+
const subCmd = argv[0] ?? "";
|
|
10972
|
+
if (subCmd === "upgrade") {
|
|
10973
|
+
return runSubUpgrade(argv.slice(1));
|
|
10974
|
+
}
|
|
10975
|
+
if (subCmd === "manage") {
|
|
10976
|
+
return runSubManage();
|
|
10977
|
+
}
|
|
10978
|
+
if (subCmd === "cancel") {
|
|
10979
|
+
return runSubCancel();
|
|
10980
|
+
}
|
|
10981
|
+
try {
|
|
10982
|
+
const balance = await fetchBalance();
|
|
10983
|
+
console.log("\n--- MSapling Subscription ---");
|
|
10984
|
+
console.log(formatSubSummary(balance));
|
|
10985
|
+
console.log("");
|
|
10986
|
+
} catch (e) {
|
|
10987
|
+
console.error(`Error fetching subscription status: ${e.message}`);
|
|
10988
|
+
process.exit(1);
|
|
10989
|
+
}
|
|
10990
|
+
}
|
|
10991
|
+
async function runSubUpgrade(flags) {
|
|
10992
|
+
const wantPro = flags.includes("--pro");
|
|
10993
|
+
const wantLifetime = flags.includes("--lifetime");
|
|
10994
|
+
let product;
|
|
10995
|
+
if (wantPro) {
|
|
10996
|
+
product = "monthly";
|
|
10997
|
+
} else if (wantLifetime) {
|
|
10998
|
+
product = "lifetime";
|
|
10999
|
+
} else {
|
|
11000
|
+
let founderRemaining = null;
|
|
11001
|
+
try {
|
|
11002
|
+
const fs3 = await fetchFounderStatus();
|
|
11003
|
+
founderRemaining = fs3.lifetime_spots_remaining;
|
|
11004
|
+
} catch {
|
|
11005
|
+
founderRemaining = null;
|
|
11006
|
+
}
|
|
11007
|
+
console.log("\n--- Upgrade MSapling ---");
|
|
11008
|
+
console.log("1) Pro \u2014 $20.00/month");
|
|
11009
|
+
const lifetimeLabel = founderRemaining !== null ? `Lifetime \u2014 $129.00 one-time (${founderRemaining} founder spots remaining)` : "Lifetime \u2014 $129.00 one-time (50 founder spots)";
|
|
11010
|
+
console.log(`2) ${lifetimeLabel}`);
|
|
11011
|
+
console.log("0) Cancel");
|
|
11012
|
+
console.log("");
|
|
11013
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
11014
|
+
const choice = (await prompt(rl, "Select option: ")).trim();
|
|
11015
|
+
rl.close();
|
|
11016
|
+
if (choice === "1") {
|
|
11017
|
+
product = "monthly";
|
|
11018
|
+
} else if (choice === "2") {
|
|
11019
|
+
product = "lifetime";
|
|
11020
|
+
} else {
|
|
11021
|
+
console.log("Upgrade cancelled.");
|
|
11022
|
+
return;
|
|
11023
|
+
}
|
|
11024
|
+
}
|
|
11025
|
+
console.log(`
|
|
11026
|
+
Starting checkout for ${product}...`);
|
|
11027
|
+
try {
|
|
11028
|
+
const result = await runCheckout({
|
|
11029
|
+
product,
|
|
11030
|
+
surface: "cli",
|
|
11031
|
+
onStatus: (msg) => console.log(msg)
|
|
11032
|
+
});
|
|
11033
|
+
if (result.state === "completed") {
|
|
11034
|
+
console.log(`
|
|
11035
|
+
Payment confirmed. Your plan has been upgraded to ${formatTier(product)}.`);
|
|
11036
|
+
} else {
|
|
11037
|
+
console.log(`
|
|
11038
|
+
Checkout ${result.state}. No charges were made.`);
|
|
11039
|
+
}
|
|
11040
|
+
} catch (e) {
|
|
11041
|
+
console.error(`Checkout error: ${e.message}`);
|
|
11042
|
+
process.exit(1);
|
|
11043
|
+
}
|
|
11044
|
+
}
|
|
11045
|
+
async function runSubManage() {
|
|
11046
|
+
console.log(
|
|
11047
|
+
"\nCustomer Portal endpoint (/api/billing/portal) is not yet implemented.\nTo manage your subscription, visit: https://msapling.com/billing\n"
|
|
11048
|
+
);
|
|
11049
|
+
}
|
|
11050
|
+
async function runSubCancel() {
|
|
11051
|
+
try {
|
|
11052
|
+
const balance = await fetchBalance();
|
|
11053
|
+
if (!balance.is_pro && balance.tier !== "monthly") {
|
|
11054
|
+
console.log("You are not on a Pro subscription. Nothing to cancel.");
|
|
11055
|
+
return;
|
|
11056
|
+
}
|
|
11057
|
+
console.log(`
|
|
11058
|
+
Current plan: ${formatTier(balance.tier)}`);
|
|
11059
|
+
} catch (e) {
|
|
11060
|
+
console.error(`Error fetching subscription: ${e.message}`);
|
|
11061
|
+
process.exit(1);
|
|
11062
|
+
}
|
|
11063
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
11064
|
+
const confirm1 = (await prompt(rl, "Are you sure you want to cancel? [y/N] ")).trim().toLowerCase();
|
|
11065
|
+
if (confirm1 !== "y") {
|
|
11066
|
+
rl.close();
|
|
11067
|
+
console.log("Cancelled \u2014 no changes made.");
|
|
11068
|
+
return;
|
|
11069
|
+
}
|
|
11070
|
+
const confirm2 = (await prompt(rl, "Type 'cancel' to confirm: ")).trim().toLowerCase();
|
|
11071
|
+
rl.close();
|
|
11072
|
+
if (confirm2 !== "cancel") {
|
|
11073
|
+
console.log("Cancelled \u2014 no changes made.");
|
|
11074
|
+
return;
|
|
11075
|
+
}
|
|
11076
|
+
try {
|
|
11077
|
+
await postSubscriptionCancel();
|
|
11078
|
+
console.log("\nSubscription cancelled. You retain access until the end of your billing period.");
|
|
11079
|
+
} catch (e) {
|
|
11080
|
+
console.error(`Cancel failed: ${e.message}`);
|
|
11081
|
+
process.exit(1);
|
|
11082
|
+
}
|
|
11083
|
+
}
|
|
11084
|
+
var init_sub = __esm({
|
|
11085
|
+
"src/commands/billing/sub.ts"() {
|
|
11086
|
+
"use strict";
|
|
11087
|
+
init_esm_shims();
|
|
11088
|
+
init_api();
|
|
11089
|
+
init_format();
|
|
11090
|
+
init_checkout();
|
|
11091
|
+
}
|
|
11092
|
+
});
|
|
11093
|
+
|
|
11094
|
+
// src/commands/billing/topup.ts
|
|
11095
|
+
import * as readline2 from "readline";
|
|
11096
|
+
function prompt2(rl, question) {
|
|
11097
|
+
return new Promise((resolve18) => rl.question(question, resolve18));
|
|
11098
|
+
}
|
|
11099
|
+
function promptDefault(rl, question, defaultVal) {
|
|
11100
|
+
return new Promise(
|
|
11101
|
+
(resolve18) => rl.question(`${question} [${defaultVal}]: `, (ans) => resolve18(ans.trim() || defaultVal))
|
|
11102
|
+
);
|
|
11103
|
+
}
|
|
11104
|
+
async function runTopup(argv) {
|
|
11105
|
+
const subCmd = argv[0] ?? "";
|
|
11106
|
+
if (subCmd === "auto") {
|
|
11107
|
+
return runTopupAuto();
|
|
11108
|
+
}
|
|
11109
|
+
console.log("\n--- MSapling Fuel Top-Up ---");
|
|
11110
|
+
TOPUP_MENU.forEach((item, i) => console.log(`${i + 1}) ${item.label}`));
|
|
11111
|
+
console.log("0) Cancel");
|
|
11112
|
+
console.log("");
|
|
11113
|
+
const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
|
|
11114
|
+
const choice = (await prompt2(rl, "Select option: ")).trim();
|
|
11115
|
+
rl.close();
|
|
11116
|
+
const index = parseInt(choice, 10);
|
|
11117
|
+
if (!index || index < 1 || index > TOPUP_MENU.length) {
|
|
11118
|
+
console.log("Top-up cancelled.");
|
|
11119
|
+
return;
|
|
11120
|
+
}
|
|
11121
|
+
const product = TOPUP_MENU[index - 1].key;
|
|
11122
|
+
console.log(`
|
|
11123
|
+
Starting checkout for ${TOPUP_MENU[index - 1].label}...`);
|
|
11124
|
+
try {
|
|
11125
|
+
const result = await runCheckout({
|
|
11126
|
+
product,
|
|
11127
|
+
surface: "cli",
|
|
11128
|
+
onStatus: (msg) => console.log(msg)
|
|
11129
|
+
});
|
|
11130
|
+
if (result.state === "completed") {
|
|
11131
|
+
try {
|
|
11132
|
+
const balance = await fetchBalance();
|
|
11133
|
+
console.log(
|
|
11134
|
+
`
|
|
11135
|
+
Payment confirmed. New balance: ${formatCredits(balance.fuel_credits)} (${formatUSD(
|
|
11136
|
+
balance.fuel_credits
|
|
11137
|
+
)}).`
|
|
11138
|
+
);
|
|
11139
|
+
} catch {
|
|
11140
|
+
console.log("\nPayment confirmed. Run `msapling sub` to see updated balance.");
|
|
11141
|
+
}
|
|
11142
|
+
} else {
|
|
11143
|
+
console.log(`
|
|
11144
|
+
Checkout ${result.state}. No charges were made.`);
|
|
11145
|
+
}
|
|
11146
|
+
} catch (e) {
|
|
11147
|
+
console.error(`Checkout error: ${e.message}`);
|
|
11148
|
+
process.exit(1);
|
|
11149
|
+
}
|
|
11150
|
+
}
|
|
11151
|
+
async function runTopupAuto() {
|
|
11152
|
+
let balance;
|
|
11153
|
+
try {
|
|
11154
|
+
balance = await fetchBalance();
|
|
11155
|
+
} catch (e) {
|
|
11156
|
+
console.error(`Error fetching settings: ${e.message}`);
|
|
11157
|
+
process.exit(1);
|
|
11158
|
+
}
|
|
11159
|
+
const at = balance.auto_topup;
|
|
11160
|
+
console.log("\n--- Auto-Topup Settings ---");
|
|
11161
|
+
console.log(`Enabled: ${at.enabled ? "yes" : "no"}`);
|
|
11162
|
+
console.log(`Threshold: ${formatUSD(at.threshold_usd)} (topup when below this)`);
|
|
11163
|
+
console.log(`Amount: ${formatUSD(at.amount_usd)} per topup`);
|
|
11164
|
+
console.log(`Monthly cap: ${formatUSD(at.max_per_month_usd)}`);
|
|
11165
|
+
console.log("");
|
|
11166
|
+
const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
|
|
11167
|
+
const edit = (await prompt2(rl, "Edit settings? [y/N] ")).trim().toLowerCase();
|
|
11168
|
+
if (edit !== "y") {
|
|
11169
|
+
rl.close();
|
|
11170
|
+
return;
|
|
11171
|
+
}
|
|
11172
|
+
const enabledStr = await promptDefault(rl, "Enable auto-topup? (yes/no)", at.enabled ? "yes" : "no");
|
|
11173
|
+
const threshStr = await promptDefault(rl, "Threshold USD (min $1)", String(at.threshold_usd));
|
|
11174
|
+
const amountStr = await promptDefault(rl, "Topup amount USD (min $5)", String(at.amount_usd));
|
|
11175
|
+
const capStr = await promptDefault(rl, "Monthly cap USD (min $10)", String(at.max_per_month_usd));
|
|
11176
|
+
rl.close();
|
|
11177
|
+
const enabled = enabledStr.toLowerCase().startsWith("y") || enabledStr === "1";
|
|
11178
|
+
const threshold = parseFloat(threshStr);
|
|
11179
|
+
const amount = parseFloat(amountStr);
|
|
11180
|
+
const cap = parseFloat(capStr);
|
|
11181
|
+
if (isNaN(threshold) || isNaN(amount) || isNaN(cap)) {
|
|
11182
|
+
console.error("Invalid values \u2014 no changes made.");
|
|
11183
|
+
process.exit(1);
|
|
11184
|
+
}
|
|
11185
|
+
try {
|
|
11186
|
+
await patchAutoTopup({
|
|
11187
|
+
enabled,
|
|
11188
|
+
threshold_usd: threshold,
|
|
11189
|
+
amount_usd: amount,
|
|
11190
|
+
monthly_cap_usd: cap
|
|
11191
|
+
});
|
|
11192
|
+
console.log("Auto-topup settings updated.");
|
|
11193
|
+
} catch (e) {
|
|
11194
|
+
console.error(`Update failed: ${e.message}`);
|
|
11195
|
+
process.exit(1);
|
|
11196
|
+
}
|
|
11197
|
+
}
|
|
11198
|
+
var init_topup = __esm({
|
|
11199
|
+
"src/commands/billing/topup.ts"() {
|
|
11200
|
+
"use strict";
|
|
11201
|
+
init_esm_shims();
|
|
11202
|
+
init_api();
|
|
11203
|
+
init_format();
|
|
11204
|
+
init_checkout();
|
|
11205
|
+
}
|
|
11206
|
+
});
|
|
11207
|
+
|
|
11208
|
+
// src/commands/billing/redeem.ts
|
|
11209
|
+
var redeem_exports = {};
|
|
11210
|
+
__export(redeem_exports, {
|
|
11211
|
+
runRedeem: () => runRedeem
|
|
11212
|
+
});
|
|
11213
|
+
async function runRedeem(argv) {
|
|
11214
|
+
const code = (argv[0] ?? "").trim().toUpperCase();
|
|
11215
|
+
if (!code) {
|
|
11216
|
+
console.error("Usage: msapling gift redeem <code> (e.g. MSAPL-GIFT-ABCD-EFGH)");
|
|
11217
|
+
process.exit(1);
|
|
11218
|
+
}
|
|
11219
|
+
try {
|
|
11220
|
+
const result = await postGiftRedeem(code);
|
|
11221
|
+
if (result.applied) {
|
|
11222
|
+
console.log(`Gift applied: ${result.message}`);
|
|
11223
|
+
} else {
|
|
11224
|
+
console.log(`Could not apply gift: ${result.message}`);
|
|
11225
|
+
}
|
|
11226
|
+
} catch (e) {
|
|
11227
|
+
console.error(`Redeem failed: ${e.message}`);
|
|
11228
|
+
process.exit(1);
|
|
11229
|
+
}
|
|
11230
|
+
}
|
|
11231
|
+
var init_redeem = __esm({
|
|
11232
|
+
"src/commands/billing/redeem.ts"() {
|
|
11233
|
+
"use strict";
|
|
11234
|
+
init_esm_shims();
|
|
11235
|
+
init_api();
|
|
11236
|
+
}
|
|
11237
|
+
});
|
|
11238
|
+
|
|
11239
|
+
// src/commands/billing/gift.ts
|
|
11240
|
+
import * as readline3 from "readline";
|
|
11241
|
+
function prompt3(rl, question) {
|
|
11242
|
+
return new Promise((resolve18) => rl.question(question, resolve18));
|
|
11243
|
+
}
|
|
11244
|
+
async function runGift(argv) {
|
|
11245
|
+
const subCmd = argv[0] ?? "";
|
|
11246
|
+
if (subCmd === "sub") return runGiftSub(argv.slice(1));
|
|
11247
|
+
if (subCmd === "credits") return runGiftCredits(argv.slice(1));
|
|
11248
|
+
if (subCmd === "redeem") return runRedeem(argv.slice(1));
|
|
11249
|
+
console.error("Usage: msapling gift sub <user> | credits <user> <5|10> | redeem <code>");
|
|
11250
|
+
process.exit(1);
|
|
11251
|
+
}
|
|
11252
|
+
async function _resolveRecipient(username) {
|
|
11253
|
+
const clean = username.replace(/^@/, "");
|
|
11254
|
+
try {
|
|
11255
|
+
const user = await fetchUserLookup(clean);
|
|
11256
|
+
if (user === null) {
|
|
11257
|
+
console.error(`User @${clean} not found.`);
|
|
11258
|
+
process.exit(1);
|
|
11259
|
+
}
|
|
11260
|
+
} catch (e) {
|
|
11261
|
+
console.error(`User lookup failed: ${e.message}`);
|
|
11262
|
+
process.exit(1);
|
|
11263
|
+
}
|
|
11264
|
+
return clean;
|
|
11265
|
+
}
|
|
11266
|
+
async function _giftMethodMenu() {
|
|
11267
|
+
console.log("\n1) Direct \u2014 applied to recipient immediately after payment");
|
|
11268
|
+
console.log("2) Redeemable code \u2014 90-day expiry, share anywhere");
|
|
11269
|
+
console.log("");
|
|
11270
|
+
const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
|
|
11271
|
+
const choice = (await prompt3(rl, "Select gift method: ")).trim();
|
|
11272
|
+
rl.close();
|
|
11273
|
+
if (choice === "1") return "direct";
|
|
11274
|
+
if (choice === "2") return "code";
|
|
11275
|
+
console.error("Invalid choice.");
|
|
11276
|
+
process.exit(1);
|
|
11277
|
+
}
|
|
11278
|
+
async function runGiftSub(argv) {
|
|
11279
|
+
const usernameRaw = argv[0] ?? "";
|
|
11280
|
+
if (!usernameRaw) {
|
|
11281
|
+
console.error("Usage: msapling gift sub <username|@username>");
|
|
11282
|
+
process.exit(1);
|
|
11283
|
+
}
|
|
11284
|
+
const recipient = await _resolveRecipient(usernameRaw);
|
|
11285
|
+
console.log(`
|
|
11286
|
+
Gifting Pro subscription to @${recipient}`);
|
|
11287
|
+
const giftMethod = await _giftMethodMenu();
|
|
11288
|
+
try {
|
|
11289
|
+
const result = await runCheckout({
|
|
11290
|
+
product: "gift_monthly",
|
|
11291
|
+
surface: "cli",
|
|
11292
|
+
recipient,
|
|
11293
|
+
gift_method: giftMethod,
|
|
11294
|
+
onStatus: (msg) => console.log(msg)
|
|
11295
|
+
});
|
|
11296
|
+
if (result.state === "completed") {
|
|
11297
|
+
if (giftMethod === "direct") {
|
|
11298
|
+
console.log(`
|
|
11299
|
+
Gifted to @${recipient}. They will see the upgrade on next login.`);
|
|
11300
|
+
} else {
|
|
11301
|
+
const code = result.gift_code;
|
|
11302
|
+
if (code) {
|
|
11303
|
+
console.log(`
|
|
11304
|
+
Gift code generated:`);
|
|
11305
|
+
console.log(`
|
|
11306
|
+
${code}
|
|
11307
|
+
`);
|
|
11308
|
+
console.log(`Share this code with @${recipient}. It expires in 90 days.`);
|
|
11309
|
+
console.log(`They can redeem it with: msapling gift redeem ${code}`);
|
|
11310
|
+
} else {
|
|
11311
|
+
console.log(`
|
|
11312
|
+
Payment confirmed. Gift code will be emailed to you shortly.`);
|
|
11313
|
+
}
|
|
11314
|
+
}
|
|
11315
|
+
} else {
|
|
11316
|
+
console.log(`
|
|
11317
|
+
Checkout ${result.state}. No charges were made.`);
|
|
11318
|
+
}
|
|
11319
|
+
} catch (e) {
|
|
11320
|
+
console.error(`Gift checkout error: ${e.message}`);
|
|
11321
|
+
process.exit(1);
|
|
11322
|
+
}
|
|
11323
|
+
}
|
|
11324
|
+
async function runGiftCredits(argv) {
|
|
11325
|
+
const usernameRaw = argv[0] ?? "";
|
|
11326
|
+
const countStr = argv[1] ?? "";
|
|
11327
|
+
if (!usernameRaw || !countStr) {
|
|
11328
|
+
console.error("Usage: msapling gift credits <username|@username> <5|10>");
|
|
11329
|
+
process.exit(1);
|
|
11330
|
+
}
|
|
11331
|
+
const count = parseInt(countStr, 10);
|
|
11332
|
+
if (count !== 5 && count !== 10) {
|
|
11333
|
+
console.error("Credit amount must be 5 or 10.");
|
|
11334
|
+
process.exit(1);
|
|
11335
|
+
}
|
|
11336
|
+
const recipient = await _resolveRecipient(usernameRaw);
|
|
11337
|
+
const product = `gift_fuel_${count}`;
|
|
11338
|
+
const priceLabel = count === 5 ? "$7.78" : "$15.56";
|
|
11339
|
+
console.log(`
|
|
11340
|
+
Gifting ${count} credits (${priceLabel}) to @${recipient}`);
|
|
11341
|
+
const giftMethod = await _giftMethodMenu();
|
|
11342
|
+
try {
|
|
11343
|
+
const result = await runCheckout({
|
|
11344
|
+
product,
|
|
11345
|
+
surface: "cli",
|
|
11346
|
+
recipient,
|
|
11347
|
+
gift_method: giftMethod,
|
|
11348
|
+
onStatus: (msg) => console.log(msg)
|
|
11349
|
+
});
|
|
11350
|
+
if (result.state === "completed") {
|
|
11351
|
+
if (giftMethod === "direct") {
|
|
11352
|
+
console.log(`
|
|
11353
|
+
${count} credits gifted to @${recipient}.`);
|
|
11354
|
+
} else {
|
|
11355
|
+
const code = result.gift_code;
|
|
11356
|
+
if (code) {
|
|
11357
|
+
console.log(`
|
|
11358
|
+
Gift code generated:`);
|
|
11359
|
+
console.log(`
|
|
11360
|
+
${code}
|
|
11361
|
+
`);
|
|
11362
|
+
console.log(`Share this code with @${recipient}. It expires in 90 days.`);
|
|
11363
|
+
console.log(`They can redeem it with: msapling gift redeem ${code}`);
|
|
11364
|
+
} else {
|
|
11365
|
+
console.log(`
|
|
11366
|
+
Payment confirmed. Gift code will be emailed to you shortly.`);
|
|
11367
|
+
}
|
|
11368
|
+
}
|
|
11369
|
+
} else {
|
|
11370
|
+
console.log(`
|
|
11371
|
+
Checkout ${result.state}. No charges were made.`);
|
|
11372
|
+
}
|
|
11373
|
+
} catch (e) {
|
|
11374
|
+
console.error(`Gift checkout error: ${e.message}`);
|
|
11375
|
+
process.exit(1);
|
|
11376
|
+
}
|
|
11377
|
+
}
|
|
11378
|
+
var init_gift = __esm({
|
|
11379
|
+
"src/commands/billing/gift.ts"() {
|
|
11380
|
+
"use strict";
|
|
11381
|
+
init_esm_shims();
|
|
11382
|
+
init_api();
|
|
11383
|
+
init_checkout();
|
|
11384
|
+
init_redeem();
|
|
11385
|
+
}
|
|
11386
|
+
});
|
|
11387
|
+
|
|
11388
|
+
// src/commands/billing/index.ts
|
|
11389
|
+
var billing_exports = {};
|
|
11390
|
+
__export(billing_exports, {
|
|
11391
|
+
dispatchBillingCommand: () => dispatchBillingCommand,
|
|
11392
|
+
printBillingHelp: () => printBillingHelp
|
|
11393
|
+
});
|
|
11394
|
+
async function dispatchBillingCommand(cmd, argv) {
|
|
11395
|
+
switch (cmd) {
|
|
11396
|
+
case "sub":
|
|
11397
|
+
return runSub(argv);
|
|
11398
|
+
case "topup":
|
|
11399
|
+
return runTopup(argv);
|
|
11400
|
+
case "gift":
|
|
11401
|
+
return runGift(argv);
|
|
11402
|
+
default: {
|
|
11403
|
+
const _exhaustive = cmd;
|
|
11404
|
+
console.error(`Unknown billing command: ${_exhaustive}`);
|
|
11405
|
+
process.exit(1);
|
|
11406
|
+
}
|
|
11407
|
+
}
|
|
11408
|
+
}
|
|
11409
|
+
function printBillingHelp() {
|
|
11410
|
+
const lines = [
|
|
11411
|
+
"",
|
|
11412
|
+
"MSapling Billing Commands",
|
|
11413
|
+
"",
|
|
11414
|
+
" msapling sub Show current tier and credits balance",
|
|
11415
|
+
" msapling sub upgrade Interactive upgrade menu (Pro / Lifetime)",
|
|
11416
|
+
" msapling sub upgrade --pro Upgrade to Pro directly ($20/mo)",
|
|
11417
|
+
" msapling sub upgrade --lifetime Upgrade to Lifetime directly ($129)",
|
|
11418
|
+
" msapling sub manage Open Stripe Customer Portal in browser",
|
|
11419
|
+
" msapling sub cancel Cancel Pro subscription (double confirm)",
|
|
11420
|
+
"",
|
|
11421
|
+
" msapling topup Buy fuel credits (5 or 10)",
|
|
11422
|
+
" msapling topup auto View/edit auto-topup settings",
|
|
11423
|
+
"",
|
|
11424
|
+
" msapling gift sub <user> Gift a Pro subscription to @user",
|
|
11425
|
+
" msapling gift credits <user> <5|10> Gift credits to @user",
|
|
11426
|
+
" msapling gift redeem <code> Redeem a gift code (MSAPL-GIFT-XXXX-XXXX)",
|
|
11427
|
+
"",
|
|
11428
|
+
"Pricing:",
|
|
11429
|
+
" Pro: $20.00/month",
|
|
11430
|
+
" Lifetime: $129.00 one-time (50 founder spots)",
|
|
11431
|
+
" 5 credits: $7.78",
|
|
11432
|
+
" 10 credits: $15.56",
|
|
11433
|
+
" 1 credit = $1.556",
|
|
11434
|
+
""
|
|
11435
|
+
];
|
|
11436
|
+
console.log(lines.join("\n"));
|
|
11437
|
+
}
|
|
11438
|
+
var init_billing = __esm({
|
|
11439
|
+
"src/commands/billing/index.ts"() {
|
|
11440
|
+
"use strict";
|
|
11441
|
+
init_esm_shims();
|
|
11442
|
+
init_sub();
|
|
11443
|
+
init_topup();
|
|
11444
|
+
init_gift();
|
|
11445
|
+
}
|
|
11446
|
+
});
|
|
11447
|
+
|
|
10676
11448
|
// src/runtime/doctorRedact.ts
|
|
10677
11449
|
function redactSecrets(text) {
|
|
10678
11450
|
let out = text;
|
|
@@ -12234,7 +13006,7 @@ var init_registry_merger = __esm({
|
|
|
12234
13006
|
});
|
|
12235
13007
|
|
|
12236
13008
|
// ../core/src/mcp/local_tools.ts
|
|
12237
|
-
import { spawn as
|
|
13009
|
+
import { spawn as spawn11 } from "child_process";
|
|
12238
13010
|
import { readdir as readdir4, stat as stat4, realpath as realpath2 } from "fs/promises";
|
|
12239
13011
|
import { resolve as resolve16 } from "path";
|
|
12240
13012
|
function asResult(text, isError = false) {
|
|
@@ -12248,7 +13020,7 @@ async function runCommand(command, cwd) {
|
|
|
12248
13020
|
resolve18({ stdout: "", stderr: "Command timed out after 30s", exit_code: -1 });
|
|
12249
13021
|
}, 3e4);
|
|
12250
13022
|
try {
|
|
12251
|
-
p =
|
|
13023
|
+
p = spawn11("sh", ["-c", command], {
|
|
12252
13024
|
cwd: cwd || process.cwd(),
|
|
12253
13025
|
stdio: ["ignore", "pipe", "pipe"],
|
|
12254
13026
|
timeout: 3e4
|
|
@@ -13371,7 +14143,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
13371
14143
|
var Header = () => /* @__PURE__ */ jsxs(Box, { borderStyle: "single", borderColor: "cyan", paddingX: 1, marginBottom: 1, children: [
|
|
13372
14144
|
/* @__PURE__ */ jsxs(Text, { bold: true, color: "cyan", children: [
|
|
13373
14145
|
"\u25CF MSapling CLI v",
|
|
13374
|
-
"2.3.6-beta.
|
|
14146
|
+
"2.3.6-beta.20"
|
|
13375
14147
|
] }),
|
|
13376
14148
|
/* @__PURE__ */ jsx(Box, { marginLeft: 2, children: /* @__PURE__ */ jsx(Text, { color: "gray", children: "Platinum Tier Architecture" }) })
|
|
13377
14149
|
] });
|
|
@@ -13752,7 +14524,7 @@ function formatContextBudgetLabel(snap) {
|
|
|
13752
14524
|
init_esm_shims();
|
|
13753
14525
|
function createIdleAwarePoll(pollingIntervalRef, lastActivityRef, client, setUser) {
|
|
13754
14526
|
const IDLE_THRESHOLD_MS = 5 * 60 * 1e3;
|
|
13755
|
-
const
|
|
14527
|
+
const POLL_INTERVAL_MS2 = 3e4;
|
|
13756
14528
|
if (pollingIntervalRef.current !== null) return;
|
|
13757
14529
|
pollingIntervalRef.current = setInterval(async () => {
|
|
13758
14530
|
const timeSinceActivity = Date.now() - lastActivityRef.current;
|
|
@@ -13768,7 +14540,7 @@ function createIdleAwarePoll(pollingIntervalRef, lastActivityRef, client, setUse
|
|
|
13768
14540
|
setUser(liveUser);
|
|
13769
14541
|
} catch (e) {
|
|
13770
14542
|
}
|
|
13771
|
-
},
|
|
14543
|
+
}, POLL_INTERVAL_MS2);
|
|
13772
14544
|
}
|
|
13773
14545
|
|
|
13774
14546
|
// src/state/commandHandler.ts
|
|
@@ -13810,7 +14582,7 @@ function buildHandleCommand(ctx) {
|
|
|
13810
14582
|
setSwarmWorkers: ctx.setSwarmWorkers,
|
|
13811
14583
|
getPlan: ctx.getPlan,
|
|
13812
14584
|
setPlan: ctx.setPlan,
|
|
13813
|
-
runAgent: async (
|
|
14585
|
+
runAgent: async (prompt4) => {
|
|
13814
14586
|
ctx.setIsRunning(true);
|
|
13815
14587
|
let chatId = ctx.activeChatId;
|
|
13816
14588
|
if (!chatId) {
|
|
@@ -13820,7 +14592,7 @@ function buildHandleCommand(ctx) {
|
|
|
13820
14592
|
const plan = ctx.getPlan();
|
|
13821
14593
|
const finalPrompt = plan && plan.length > 0 ? `${buildPlanPrefix(plan)}
|
|
13822
14594
|
|
|
13823
|
-
${
|
|
14595
|
+
${prompt4}` : prompt4;
|
|
13824
14596
|
try {
|
|
13825
14597
|
const usage = await ctx.agent.runWorkerTurn(chatId, finalPrompt, ctx.activeModel, (chunk) => {
|
|
13826
14598
|
ctx.updateAssistantMessage(chunk);
|
|
@@ -14301,6 +15073,10 @@ function handleCliArgs(args2) {
|
|
|
14301
15073
|
console.log(" msapling mcp serve run as MCP stdio server (Claude Code / Cursor / Windsurf integration)");
|
|
14302
15074
|
console.log(" msapling doctor run diagnostic health checks");
|
|
14303
15075
|
console.log(" msapling doctor --debug run doctor with full environment dump");
|
|
15076
|
+
console.log(" msapling sub subscription & tier management (sub upgrade|manage|cancel)");
|
|
15077
|
+
console.log(" msapling topup buy fuel credits (5 or 10) or configure auto-topup");
|
|
15078
|
+
console.log(" msapling gift gift Pro / credits to another user, or redeem a code");
|
|
15079
|
+
console.log(" msapling redeem <code> redeem a gift code (alias for `msapling gift redeem`)");
|
|
14304
15080
|
console.log(" msapling --version print version and exit");
|
|
14305
15081
|
console.log(" msapling --help print this message");
|
|
14306
15082
|
console.log("Inside the REPL: type /help for slash-command help.");
|
|
@@ -14316,6 +15092,30 @@ function handleCliArgs(args2) {
|
|
|
14316
15092
|
process.exit(exitCode);
|
|
14317
15093
|
})().catch((e) => {
|
|
14318
15094
|
process.stderr.write(`[msapling-exec] fatal: ${e?.message ?? e}
|
|
15095
|
+
`);
|
|
15096
|
+
process.exit(1);
|
|
15097
|
+
});
|
|
15098
|
+
return false;
|
|
15099
|
+
}
|
|
15100
|
+
if (args2[0] === "sub" || args2[0] === "topup" || args2[0] === "gift") {
|
|
15101
|
+
(async () => {
|
|
15102
|
+
const { dispatchBillingCommand: dispatchBillingCommand2 } = await Promise.resolve().then(() => (init_billing(), billing_exports));
|
|
15103
|
+
await dispatchBillingCommand2(args2[0], args2.slice(1));
|
|
15104
|
+
process.exit(0);
|
|
15105
|
+
})().catch((e) => {
|
|
15106
|
+
process.stderr.write(`[msapling-billing] fatal: ${e?.message ?? e}
|
|
15107
|
+
`);
|
|
15108
|
+
process.exit(1);
|
|
15109
|
+
});
|
|
15110
|
+
return false;
|
|
15111
|
+
}
|
|
15112
|
+
if (args2[0] === "redeem") {
|
|
15113
|
+
(async () => {
|
|
15114
|
+
const { runRedeem: runRedeem2 } = await Promise.resolve().then(() => (init_redeem(), redeem_exports));
|
|
15115
|
+
await runRedeem2(args2.slice(1));
|
|
15116
|
+
process.exit(0);
|
|
15117
|
+
})().catch((e) => {
|
|
15118
|
+
process.stderr.write(`[msapling-billing] fatal: ${e?.message ?? e}
|
|
14319
15119
|
`);
|
|
14320
15120
|
process.exit(1);
|
|
14321
15121
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mtreeai/msapling-cli",
|
|
3
|
-
"version": "2.3.6-beta.
|
|
3
|
+
"version": "2.3.6-beta.20",
|
|
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",
|