@sellable/install 0.1.207 → 0.1.208

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/README.md CHANGED
@@ -55,11 +55,13 @@ Agents should use the agent-readable handoff instead of guessing commands:
55
55
  Install Sellable CLI and skills using https://app.sellable.dev/agent-install.txt
56
56
  ```
57
57
 
58
- The direct npm installer remains available as a troubleshooting fallback when
59
- Node/npm/npx are already known-good:
58
+ The direct npm installer is only a package-level troubleshooting fallback for
59
+ maintainers. Public installs should keep using the curl endpoint above because
60
+ it bootstraps Node, repairs legacy state, installs skills, and runs runtime
61
+ verification in one path:
60
62
 
61
63
  ```bash
62
- npx -y @sellable/install@latest --host all
64
+ curl -fsSL "https://app.sellable.dev/api/v2/cli/install" | sh
63
65
  ```
64
66
 
65
67
  For CI/scripted installs, get a Sellable API token from:
@@ -1315,7 +1315,7 @@ local harness scripts, or read local prompt files to emulate this workflow.
1315
1315
 
1316
1316
  If the Sellable MCP tool is unavailable, stop and say this is a Codex
1317
1317
  install/reload problem. Tell the user to run
1318
- \`npx -y ${INSTALL_PACKAGE_SPEC} --host all\`, fully quit and reopen Codex
1318
+ \`curl -fsSL "https://app.sellable.dev/api/v2/cli/install" | sh\`, fully quit and reopen Codex
1319
1319
  Desktop, then start a new thread.
1320
1320
 
1321
1321
  1. Call \`mcp__sellable__get_auth_status({})\`.
@@ -1363,7 +1363,7 @@ emulate this workflow.
1363
1363
 
1364
1364
  If the Sellable MCP prompt tools are unavailable, stop and say this is a Codex
1365
1365
  install/reload problem. Tell the user to run
1366
- \`npx -y ${INSTALL_PACKAGE_SPEC} --host all\`, fully quit and reopen Codex
1366
+ \`curl -fsSL "https://app.sellable.dev/api/v2/cli/install" | sh\`, fully quit and reopen Codex
1367
1367
  Desktop, then start a new thread.
1368
1368
 
1369
1369
  ## Execute Workflow
@@ -1440,7 +1440,7 @@ emulate this workflow.
1440
1440
 
1441
1441
  If the Sellable MCP prompt tools are unavailable, stop and say this is a Codex
1442
1442
  install/reload problem. Tell the user to run
1443
- \`npx -y ${INSTALL_PACKAGE_SPEC} --host all\`, fully quit and reopen Codex
1443
+ \`curl -fsSL "https://app.sellable.dev/api/v2/cli/install" | sh\`, fully quit and reopen Codex
1444
1444
  Desktop, then start a new thread.
1445
1445
 
1446
1446
  ## Execute Workflow
@@ -1512,7 +1512,7 @@ emulate this workflow.
1512
1512
 
1513
1513
  If the Sellable MCP prompt tools are unavailable, stop and say this is a Codex
1514
1514
  install/reload problem. Tell the user to run
1515
- \`npx -y ${INSTALL_PACKAGE_SPEC} --host all\`, fully quit and reopen Codex
1515
+ \`curl -fsSL "https://app.sellable.dev/api/v2/cli/install" | sh\`, fully quit and reopen Codex
1516
1516
  Desktop, then start a new thread.
1517
1517
 
1518
1518
  ## Execute Workflow
@@ -2690,8 +2690,8 @@ async function verify(opts) {
2690
2690
  (runtimeRequired ? requiredCheck : warningCheck)(
2691
2691
  runtimeMcp.ok,
2692
2692
  runtimeMcp.ok
2693
- ? `MCP runtime exposes required tools (${runtimeMcp.availableTools.length} total)`
2694
- : `MCP runtime missing tools: ${runtimeMcp.missingTools.join(", ") || runtimeMcp.error || "unknown failure"}`
2693
+ ? `MCP runtime exposes required campaign tools (${runtimeMcp.availableTools.length} total) and create-campaign smoke passed`
2694
+ : `MCP runtime verification failed: ${runtimeMcp.missingTools.join(", ") || runtimeMcp.createCampaignSmoke?.error || runtimeMcp.error || "unknown failure"}`
2695
2695
  )
2696
2696
  );
2697
2697
  }
@@ -6,12 +6,64 @@ const DEFAULT_VERIFY_TIMEOUT_MS = 10_000;
6
6
 
7
7
  export const REQUIRED_SELLABLE_MCP_TOOLS = [
8
8
  "get_auth_status",
9
+ "start_cli_login",
10
+ "wait_for_cli_login",
9
11
  "bootstrap_create_campaign",
12
+ "get_subskill_prompt",
13
+ "get_subskill_asset",
14
+ "search_subskill_prompts",
10
15
  "create_campaign",
11
16
  "import_leads",
17
+ "confirm_lead_list",
18
+ "wait_for_lead_list_ready",
19
+ "wait_for_campaign_table_ready",
12
20
  "update_campaign",
13
- "get_subskill_prompt",
14
- "get_subskill_asset",
21
+ "update_campaign_brief",
22
+ "get_campaign",
23
+ "get_campaign_context",
24
+ "get_campaign_framework",
25
+ "get_campaign_navigation_state",
26
+ "get_campaign_messages_preview",
27
+ "attach_sequence",
28
+ "attach_recommended_sequence",
29
+ "start_campaign_message_preparation",
30
+ "get_campaign_message_preparation_status",
31
+ "cancel_campaign_message_preparation",
32
+ "start_campaign",
33
+ ];
34
+
35
+ export const CREATE_CAMPAIGN_SMOKE_CALLS = [
36
+ {
37
+ name: "get_auth_status",
38
+ arguments: {},
39
+ },
40
+ {
41
+ name: "bootstrap_create_campaign",
42
+ arguments: {
43
+ flowVersion: "v2",
44
+ includeTableCheck: false,
45
+ host: "sellable-install-verify",
46
+ model: "installer-smoke",
47
+ reasoningEffort: "none",
48
+ },
49
+ },
50
+ {
51
+ name: "get_subskill_prompt",
52
+ arguments: {
53
+ subskillName: "create-campaign-v2",
54
+ offset: 0,
55
+ limit: 1200,
56
+ },
57
+ },
58
+ {
59
+ name: "get_subskill_asset",
60
+ arguments: {
61
+ subskillName: "create-campaign-v2",
62
+ assetPath: "core/flow.v2.json",
63
+ offset: 0,
64
+ limit: 1200,
65
+ },
66
+ },
15
67
  ];
16
68
 
17
69
  function collectStderrLines(stream) {
@@ -73,6 +125,172 @@ export function missingRequiredTools(tools, requiredTools) {
73
125
  return requiredTools.filter((name) => !available.has(name));
74
126
  }
75
127
 
128
+ function textFromToolResult(result) {
129
+ return (result?.content || [])
130
+ .filter((part) => part?.type === "text" && typeof part.text === "string")
131
+ .map((part) => part.text)
132
+ .join("\n");
133
+ }
134
+
135
+ function parseJsonText(text) {
136
+ if (!text) return null;
137
+ try {
138
+ return JSON.parse(text);
139
+ } catch {
140
+ return null;
141
+ }
142
+ }
143
+
144
+ function summarizeToolResult(name, result) {
145
+ const text = redact(textFromToolResult(result));
146
+ const parsed = parseJsonText(text);
147
+ const summary = {
148
+ isError: result?.isError === true,
149
+ textChars: text.length,
150
+ };
151
+
152
+ if (name === "get_auth_status" && parsed) {
153
+ return {
154
+ ...summary,
155
+ authOk: parsed.ok === true,
156
+ errorType: parsed.error?.type || null,
157
+ activeWorkspaceId: parsed.activeWorkspaceId || null,
158
+ activeWorkspaceName: parsed.activeWorkspaceName || null,
159
+ };
160
+ }
161
+
162
+ if (name === "bootstrap_create_campaign" && parsed) {
163
+ return {
164
+ ...summary,
165
+ safeToProceed: parsed.safeToProceed === true,
166
+ flowVersion: parsed.flowVersion || null,
167
+ blockingChecks: Array.isArray(parsed.blockingErrors)
168
+ ? parsed.blockingErrors.map((entry) => entry?.check).filter(Boolean)
169
+ : [],
170
+ requiredChecks: Array.isArray(parsed.requiredChecks)
171
+ ? parsed.requiredChecks.map((entry) => ({
172
+ key: entry?.key || null,
173
+ ok: entry?.ok === true,
174
+ blocking: entry?.blocking === true,
175
+ }))
176
+ : [],
177
+ nextStepChars:
178
+ typeof parsed.nextStep === "string" ? parsed.nextStep.length : 0,
179
+ };
180
+ }
181
+
182
+ if (name === "get_subskill_prompt" && parsed) {
183
+ return {
184
+ ...summary,
185
+ subskillName: parsed.name || null,
186
+ promptChars: typeof parsed.prompt === "string" ? parsed.prompt.length : 0,
187
+ hasMore: parsed.hasMore === true,
188
+ nextOffset:
189
+ typeof parsed.nextOffset === "number" ? parsed.nextOffset : null,
190
+ };
191
+ }
192
+
193
+ if (name === "get_subskill_asset" && parsed) {
194
+ return {
195
+ ...summary,
196
+ subskillName: parsed.subskillName || null,
197
+ assetPath: parsed.assetPath || null,
198
+ assetChars: typeof parsed.content === "string" ? parsed.content.length : 0,
199
+ hasMore: parsed.hasMore === true,
200
+ nextOffset:
201
+ typeof parsed.nextOffset === "number" ? parsed.nextOffset : null,
202
+ };
203
+ }
204
+
205
+ return summary;
206
+ }
207
+
208
+ async function verifyCreateCampaignSmoke({
209
+ client,
210
+ availableTools,
211
+ timeoutMs,
212
+ smokeCalls = CREATE_CAMPAIGN_SMOKE_CALLS,
213
+ }) {
214
+ const startedAt = new Date().toISOString();
215
+ const available = new Set(availableTools || []);
216
+ const missingTools = smokeCalls
217
+ .map((call) => call.name)
218
+ .filter((name) => !available.has(name));
219
+ const calls = [];
220
+
221
+ if (missingTools.length > 0) {
222
+ return {
223
+ ok: false,
224
+ missingTools,
225
+ calls,
226
+ error: `Missing smoke tool(s): ${missingTools.join(", ")}`,
227
+ startedAt,
228
+ completedAt: new Date().toISOString(),
229
+ };
230
+ }
231
+
232
+ for (const call of smokeCalls) {
233
+ const callStartedAt = new Date().toISOString();
234
+ try {
235
+ const result = await client.callTool(
236
+ { name: call.name, arguments: call.arguments },
237
+ undefined,
238
+ {
239
+ timeout: timeoutMs,
240
+ maxTotalTimeout: timeoutMs,
241
+ }
242
+ );
243
+ const summary = summarizeToolResult(call.name, result);
244
+ calls.push({
245
+ name: call.name,
246
+ arguments: call.arguments,
247
+ ok: summary.isError !== true,
248
+ summary,
249
+ startedAt: callStartedAt,
250
+ completedAt: new Date().toISOString(),
251
+ });
252
+ if (summary.isError === true) {
253
+ return {
254
+ ok: false,
255
+ missingTools: [],
256
+ calls,
257
+ error: `${call.name} returned an MCP tool error`,
258
+ startedAt,
259
+ completedAt: new Date().toISOString(),
260
+ };
261
+ }
262
+ } catch (err) {
263
+ const error = err instanceof Error ? err.message : String(err);
264
+ calls.push({
265
+ name: call.name,
266
+ arguments: call.arguments,
267
+ ok: false,
268
+ summary: null,
269
+ error: redact(error),
270
+ startedAt: callStartedAt,
271
+ completedAt: new Date().toISOString(),
272
+ });
273
+ return {
274
+ ok: false,
275
+ missingTools: [],
276
+ calls,
277
+ error: redact(error),
278
+ startedAt,
279
+ completedAt: new Date().toISOString(),
280
+ };
281
+ }
282
+ }
283
+
284
+ return {
285
+ ok: true,
286
+ missingTools: [],
287
+ calls,
288
+ error: null,
289
+ startedAt,
290
+ completedAt: new Date().toISOString(),
291
+ };
292
+ }
293
+
76
294
  export async function verifySellableMcpRuntime({
77
295
  command,
78
296
  args = [],
@@ -116,6 +334,7 @@ export async function verifySellableMcpRuntime({
116
334
 
117
335
  let availableTools = [];
118
336
  let missingTools = [...requiredTools];
337
+ let createCampaignSmoke = null;
119
338
  let error = null;
120
339
 
121
340
  try {
@@ -126,6 +345,25 @@ export async function verifySellableMcpRuntime({
126
345
  });
127
346
  availableTools = toolList.tools.map((tool) => tool.name).sort();
128
347
  missingTools = missingRequiredTools(toolList.tools, requiredTools);
348
+ if (missingTools.length === 0) {
349
+ createCampaignSmoke = await verifyCreateCampaignSmoke({
350
+ client,
351
+ availableTools,
352
+ timeoutMs,
353
+ });
354
+ } else {
355
+ createCampaignSmoke = {
356
+ ok: false,
357
+ missingTools: CREATE_CAMPAIGN_SMOKE_CALLS.map((call) => call.name).filter(
358
+ (name) => !availableTools.includes(name)
359
+ ),
360
+ calls: [],
361
+ error:
362
+ "Skipping create-campaign smoke because required MCP tools are missing.",
363
+ startedAt: new Date().toISOString(),
364
+ completedAt: new Date().toISOString(),
365
+ };
366
+ }
129
367
  } catch (err) {
130
368
  error = err instanceof Error ? err.message : String(err);
131
369
  } finally {
@@ -137,13 +375,14 @@ export async function verifySellableMcpRuntime({
137
375
  }
138
376
 
139
377
  return {
140
- ok: !error && missingTools.length === 0,
378
+ ok: !error && missingTools.length === 0 && createCampaignSmoke?.ok === true,
141
379
  skipped: false,
142
380
  command,
143
381
  args,
144
382
  requiredTools,
145
383
  availableTools,
146
384
  missingTools,
385
+ createCampaignSmoke,
147
386
  stderrTail: stderrLines,
148
387
  error: error ? redact(error) : null,
149
388
  startedAt,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/install",
3
- "version": "0.1.207",
3
+ "version": "0.1.208",
4
4
  "type": "module",
5
5
  "description": "One-command installer for Sellable MCP in Claude Code and Codex",
6
6
  "bin": {