@sellable/install 0.1.163 → 0.1.165

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.
@@ -31,7 +31,7 @@ function getInstallVersion() {
31
31
  }
32
32
  }
33
33
 
34
- const CODEX_PLUGIN_VERSION = "0.1.33";
34
+ const CODEX_PLUGIN_VERSION = "0.1.34";
35
35
  const CODEX_PLUGIN_COMPAT_VERSIONS = [
36
36
  "0.1.8",
37
37
  "0.1.9",
@@ -55,6 +55,10 @@ const CODEX_PLUGIN_COMPAT_VERSIONS = [
55
55
  "0.1.27",
56
56
  "0.1.28",
57
57
  "0.1.29",
58
+ "0.1.30",
59
+ "0.1.31",
60
+ "0.1.32",
61
+ "0.1.33",
58
62
  ];
59
63
  const INSTALL_PACKAGE_SPEC =
60
64
  process.env.SELLABLE_INSTALL_PACKAGE_SPEC || "@sellable/install@latest";
@@ -522,7 +526,7 @@ function codexPluginMcp(opts) {
522
526
  mcpServers: {
523
527
  sellable: {
524
528
  type: "http",
525
- url: opts.hostedUrl,
529
+ url: withHostedWatchModeDriver(opts.hostedUrl, "codex"),
526
530
  },
527
531
  },
528
532
  };
@@ -802,20 +806,21 @@ that we are checking people from these posts to confirm the right people are
802
806
  actually engaging and the source is viable.
803
807
 
804
808
  Only the first brief approval handoff should include live campaign access after
805
- the readable inline content. Show a short \`Watch link:\` line once, immediately
806
- after shell creation and before brief approval. Later approval gates should
807
- describe what the already-open campaign app is showing and rely on
808
- currentStep/watchNarration; do not print the URL again unless the user asks or a
809
- recovery path must replace a missing/broken link. In normal customer runs, do
810
- not show local draft filenames or filesystem paths unless the user asks for
811
- debug artifacts. The link is for deeper inspection; never use it as a substitute
812
- for showing the content in chat.
809
+ the readable inline content. Show the exact \`create_campaign.watchHandoff.markdown\`
810
+ block once, immediately after shell creation and before brief approval. Later
811
+ approval gates should describe what the already-open campaign app is showing and
812
+ rely on currentStep/watchNarration; do not print the URL again unless the user
813
+ asks or a recovery path must replace a missing/broken link. In normal customer
814
+ runs, do not show \`Open artifact:\` lines, raw filesystem paths, or local draft
815
+ filenames. Local artifacts are debug/UAT-only unless the user asks for them. The
816
+ link is for deeper inspection; never use it as a substitute for showing the
817
+ content in chat.
813
818
 
814
819
  Never mention MCP namespaces, prompt chunking, plugin cache paths, missing
815
820
  linked skill versions, runbooks, or local skill files in normal customer-facing
816
821
  copy.
817
822
 
818
- ## Codex Watch Link Handoff
823
+ ## Live Watch Link Handoff
819
824
 
820
825
  When a campaign tool returns \`watchUrl\`, treat it as a user-opened app link, not
821
826
  as permission to drive the browser. A valid first handoff link must be the exact
@@ -828,25 +833,32 @@ derive, shorten, reconstruct, or print a bare \`/campaign-builder/{campaignId}\`
828
833
  URL.
829
834
 
830
835
  Never call browser-opening tools, shell \`open\`, Computer Use, or in-app browser
831
- automation just because a watch link exists. Print the first watch link exactly
832
- once, directly before the brief approval question, in this shape:
836
+ automation just because a watch link exists. If \`create_campaign\` returns
837
+ \`watchHandoff.markdown\`, print that exact value once, directly before the brief
838
+ approval question. It will use the URL mode to say Codex or Claude Code:
833
839
 
834
840
  \`\`\`text
835
- Campaign shell is ready. We'll keep building the campaign in this chat.
836
- You can watch it being built in real time in the app here:
841
+ ╔══════════════════════════════════════════════════════════════╗
842
+ ║ OPEN THIS LINK TO WATCH CODEX BUILD THE CAMPAIGN LIVE ║
843
+ ╚══════════════════════════════════════════════════════════════╝
837
844
 
838
- Watch link: {watchUrl}
845
+ [Open live campaign builder]({watchUrl})
839
846
 
840
- Send changes here. Ill ask for your approval whenever I need your expertise or
841
- taste before moving forward.
847
+ Keep this chat open. I'll ask approval questions here before making decisions
848
+ that need your judgment.
842
849
  \`\`\`
843
850
 
851
+ The Markdown link is intentional: rendered chat clients turn it into a large
852
+ click target, and plain terminals still expose the tokenized URL inside the
853
+ Markdown target. Do not replace it with a shell command or a browser-opening
854
+ instruction.
855
+
844
856
  The watch link should auto-login through the token in the URL. If the user says
845
857
  the link lands on auth, 404, permission, blank, or a visible error state, recover
846
858
  a fresh watch link once with \`create_campaign({ campaignId })\` or \`get_campaign\`
847
859
  and print that link. Do not claim the browser was opened, inspected, or
848
860
  synchronized.
849
- Do not print another \`Watch link:\` line during source/provider selection or
861
+ Do not print another watch-link handoff during source/provider selection or
850
862
  normal approval turns unless the user explicitly asks for the link or you are
851
863
  recovering a missing/broken URL.
852
864
 
@@ -1365,12 +1377,13 @@ approval should show who we are targeting, why they should care, the offer/CTA,
1365
1377
  proof, lead source hypothesis, message angle, risks/assumptions, and what
1366
1378
  happens after approval.
1367
1379
 
1368
- Only the first brief approval handoff should print the campaign watch URL. After
1369
- that, every approval should say what the already-open app is showing, but must
1370
- not include another \`Watch link:\` line unless the user explicitly asks for the
1371
- link again or a recovery path needs to replace a broken/missing URL. Local
1372
- artifacts are optional debug/UAT diagnostics only; do not surface file paths or
1373
- local draft filenames in normal customer runs.
1380
+ Only the first brief approval handoff should print the exact
1381
+ \`create_campaign.watchHandoff.markdown\` block. After that, every approval should
1382
+ say what the already-open app is showing, but must not include another
1383
+ watch-link handoff unless the user explicitly asks for the link again or a
1384
+ recovery path needs to replace a broken/missing URL. Local artifacts are
1385
+ optional debug/UAT diagnostics only; do not surface file paths or local draft
1386
+ filenames in normal customer runs.
1374
1387
 
1375
1388
  This applies especially to message approvals. Never ask someone to approve a
1376
1389
  message they cannot see. Show the subject, tokenized template, a filled sample
@@ -1922,12 +1935,20 @@ const WATCH_MODE_DRIVER_ENV = {
1922
1935
  codex: "SELLABLE_WATCH_MODE_DRIVER=codex",
1923
1936
  };
1924
1937
 
1925
- function withWatchModeDriver(command, args, driver) {
1926
- const envArg = WATCH_MODE_DRIVER_ENV[driver];
1927
- if (!envArg) {
1938
+ function withHostedWatchModeDriver(rawUrl, driver) {
1939
+ if (!WATCH_MODE_DRIVER_ENV[driver]) {
1928
1940
  throw new Error(`Unknown watch mode driver: ${driver}`);
1929
1941
  }
1930
- return ["env", [envArg, command, ...args]];
1942
+ try {
1943
+ const url = new URL(rawUrl);
1944
+ if (!url.searchParams.has("driver")) {
1945
+ url.searchParams.set("driver", driver);
1946
+ }
1947
+ return url.toString();
1948
+ } catch {
1949
+ const separator = rawUrl.includes("?") ? "&" : "?";
1950
+ return `${rawUrl}${separator}driver=${encodeURIComponent(driver)}`;
1951
+ }
1931
1952
  }
1932
1953
 
1933
1954
  function mcpCommand(opts) {
@@ -1948,37 +1969,28 @@ function mcpCommand(opts) {
1948
1969
  return ["hosted", [opts.hostedUrl]];
1949
1970
  }
1950
1971
 
1951
- function mcpCommandForHost(opts, driver) {
1952
- const [command, args] = mcpCommand(opts);
1953
- if (command === "hosted") return [command, args];
1954
- return withWatchModeDriver(command, args, driver);
1955
- }
1956
-
1957
1972
  function codexMcpAddArgs(opts) {
1958
1973
  const [command, args] = mcpCommand(opts);
1959
1974
  if (command === "hosted") {
1960
- return ["mcp", "add", "sellable", "--url", args[0]];
1961
- }
1962
-
1963
- if (isWindowsPlatform()) {
1964
1975
  return [
1965
1976
  "mcp",
1966
1977
  "add",
1967
1978
  "sellable",
1968
- "--env",
1969
- WATCH_MODE_DRIVER_ENV.codex,
1970
- "--",
1971
- command,
1972
- ...args,
1979
+ "--url",
1980
+ withHostedWatchModeDriver(args[0], "codex"),
1973
1981
  ];
1974
1982
  }
1975
1983
 
1976
- const [wrappedCommand, wrappedArgs] = withWatchModeDriver(
1984
+ return [
1985
+ "mcp",
1986
+ "add",
1987
+ "sellable",
1988
+ "--env",
1989
+ WATCH_MODE_DRIVER_ENV.codex,
1990
+ "--",
1977
1991
  command,
1978
- args,
1979
- "codex"
1980
- );
1981
- return ["mcp", "add", "sellable", "--", wrappedCommand, ...wrappedArgs];
1992
+ ...args,
1993
+ ];
1982
1994
  }
1983
1995
 
1984
1996
  function installClaude(opts) {
@@ -1995,13 +2007,20 @@ function installClaude(opts) {
1995
2007
  if (opts.server === "hosted") {
1996
2008
  run(
1997
2009
  "claude",
1998
- ["mcp", "add", "--transport", "http", "sellable", opts.hostedUrl],
2010
+ [
2011
+ "mcp",
2012
+ "add",
2013
+ "--transport",
2014
+ "http",
2015
+ "sellable",
2016
+ withHostedWatchModeDriver(opts.hostedUrl, "claude"),
2017
+ ],
1999
2018
  opts
2000
2019
  );
2001
2020
  patchClaudeAlwaysLoad(opts);
2002
2021
  return true;
2003
2022
  }
2004
- const [command, args] = mcpCommandForHost(opts, "claude");
2023
+ const [command, args] = mcpCommand(opts);
2005
2024
  run("claude", ["mcp", "remove", "sellable"], {
2006
2025
  ...opts,
2007
2026
  dryRun: opts.dryRun,
@@ -2038,19 +2057,36 @@ function patchClaudeAlwaysLoad(opts) {
2038
2057
  const raw = readFileSync(claudeJsonPath, "utf8");
2039
2058
  const config = JSON.parse(raw);
2040
2059
  let touched = false;
2041
- // Top-level mcpServers.sellable (older claude versions)
2042
- if (config.mcpServers?.sellable) {
2043
- if (config.mcpServers.sellable.alwaysLoad !== true) {
2044
- config.mcpServers.sellable.alwaysLoad = true;
2060
+ const patchSellableServer = (server) => {
2061
+ if (!server || typeof server !== "object") return;
2062
+ if (server.alwaysLoad !== true) {
2063
+ server.alwaysLoad = true;
2045
2064
  touched = true;
2046
2065
  }
2066
+ const isHttpServer =
2067
+ typeof server.url === "string" ||
2068
+ server.type === "http" ||
2069
+ server.transport === "http";
2070
+ if (!isHttpServer) {
2071
+ const existingDriver = server.env?.SELLABLE_WATCH_MODE_DRIVER;
2072
+ server.env = {
2073
+ ...(server.env && typeof server.env === "object" ? server.env : {}),
2074
+ SELLABLE_WATCH_MODE_DRIVER: "claude",
2075
+ };
2076
+ if (existingDriver !== "claude") {
2077
+ touched = true;
2078
+ }
2079
+ }
2080
+ };
2081
+ // Top-level mcpServers.sellable (older claude versions)
2082
+ if (config.mcpServers?.sellable) {
2083
+ patchSellableServer(config.mcpServers.sellable);
2047
2084
  }
2048
2085
  // Per-project mcpServers.sellable (current claude default)
2049
2086
  for (const projectPath of Object.keys(config.projects || {})) {
2050
2087
  const projServers = config.projects[projectPath]?.mcpServers;
2051
- if (projServers?.sellable && projServers.sellable.alwaysLoad !== true) {
2052
- projServers.sellable.alwaysLoad = true;
2053
- touched = true;
2088
+ if (projServers?.sellable) {
2089
+ patchSellableServer(projServers.sellable);
2054
2090
  }
2055
2091
  }
2056
2092
  if (touched) {
@@ -2099,6 +2135,11 @@ function installCodex(opts) {
2099
2135
  function verify(opts) {
2100
2136
  const stored = readStoredAuth();
2101
2137
  const checks = [];
2138
+ const hasWatchModeDriverMarker = (content, driver) =>
2139
+ new RegExp(`SELLABLE_WATCH_MODE_DRIVER[\\s\\S]{0,80}${driver}`).test(
2140
+ content
2141
+ ) || new RegExp(`[?&]driver=${driver}(?:\\b|&)`).test(content);
2142
+
2102
2143
  if (stored?.token && stored?.workspaceId) {
2103
2144
  checks.push({ ok: true, label: `Auth config: ${authPath()}` });
2104
2145
  } else {
@@ -2156,6 +2197,20 @@ function verify(opts) {
2156
2197
  ? "Legacy Claude Sellable host artifacts cleaned"
2157
2198
  : "Legacy Claude Sellable host artifacts still present",
2158
2199
  });
2200
+ const claudeJsonPath = join(homedir(), ".claude.json");
2201
+ const claudeConfigContent = existsSync(claudeJsonPath)
2202
+ ? readFileSync(claudeJsonPath, "utf8")
2203
+ : "";
2204
+ const hasClaudeWatchModeDriver = hasWatchModeDriverMarker(
2205
+ claudeConfigContent,
2206
+ "claude"
2207
+ );
2208
+ checks.push({
2209
+ ok: hasClaudeWatchModeDriver,
2210
+ label: hasClaudeWatchModeDriver
2211
+ ? "Claude watch mode driver pinned to claude"
2212
+ : "Claude watch mode driver missing",
2213
+ });
2159
2214
  }
2160
2215
  if (opts.host === "codex" || opts.host === "all") {
2161
2216
  checks.push({
@@ -2216,6 +2271,38 @@ function verify(opts) {
2216
2271
  const configContent = existsSync(configPath)
2217
2272
  ? readFileSync(configPath, "utf8")
2218
2273
  : "";
2274
+ const pluginMcpPath = join(
2275
+ codexHome(),
2276
+ "plugins",
2277
+ "cache",
2278
+ "sellable",
2279
+ "sellable",
2280
+ CODEX_PLUGIN_VERSION,
2281
+ ".mcp.json"
2282
+ );
2283
+ const pluginMcpContent = existsSync(pluginMcpPath)
2284
+ ? readFileSync(pluginMcpPath, "utf8")
2285
+ : "";
2286
+ const hasCodexMcpDriver = hasWatchModeDriverMarker(
2287
+ configContent,
2288
+ "codex"
2289
+ );
2290
+ const hasCodexPluginDriver = hasWatchModeDriverMarker(
2291
+ pluginMcpContent,
2292
+ "codex"
2293
+ );
2294
+ checks.push({
2295
+ ok: hasCodexMcpDriver,
2296
+ label: hasCodexMcpDriver
2297
+ ? "Codex CLI watch mode driver pinned to codex"
2298
+ : "Codex CLI watch mode driver missing",
2299
+ });
2300
+ checks.push({
2301
+ ok: hasCodexPluginDriver,
2302
+ label: hasCodexPluginDriver
2303
+ ? "Codex Desktop plugin watch mode driver pinned to codex"
2304
+ : "Codex Desktop plugin watch mode driver missing",
2305
+ });
2219
2306
  const hasCodexAgentRegistrations = codexCustomAgents().every((agent) =>
2220
2307
  configContent.includes(`[agents.${agent.name}]`)
2221
2308
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/install",
3
- "version": "0.1.163",
3
+ "version": "0.1.165",
4
4
  "type": "module",
5
5
  "description": "One-command installer for Sellable MCP in Claude Code and Codex",
6
6
  "bin": {
@@ -296,22 +296,22 @@ lines short, use indexed section labels and bullets, and translate internal
296
296
  sourcing terms into plain language.
297
297
 
298
298
  Only the first brief approval handoff should include live campaign access after
299
- the readable inline content. Show a short `Watch link:` line once, immediately
300
- after shell creation and before brief approval. Later approval gates should
301
- describe what the already-open campaign app is showing and rely on
302
- currentStep/watchNarration; do not print the URL again unless the user asks or a
303
- recovery path must replace a missing/broken link. In normal customer runs, do
304
- not show `Open artifact:` lines, raw filesystem paths, or local draft filenames.
305
- Local artifacts are debug/UAT-only unless the user asks for them. The link is
306
- for deeper inspection; never use it as a substitute for showing the content in
307
- chat.
299
+ the readable inline content. Show the exact `create_campaign.watchHandoff.markdown`
300
+ block once, immediately after shell creation and before brief approval. Later
301
+ approval gates should describe what the already-open campaign app is showing and
302
+ rely on currentStep/watchNarration; do not print the URL again unless the user
303
+ asks or a recovery path must replace a missing/broken link. In normal customer
304
+ runs, do not show `Open artifact:` lines, raw filesystem paths, or local draft
305
+ filenames. Local artifacts are debug/UAT-only unless the user asks for them. The
306
+ link is for deeper inspection; never use it as a substitute for showing the
307
+ content in chat.
308
308
 
309
309
  Never mention MCP namespaces, prompt chunking, plugin cache paths, missing
310
310
  linked skill versions, runbooks, npm/package details, repo-local files, VPS or
311
311
  browser automation limitations, or local skill files in normal customer-facing
312
312
  copy.
313
313
 
314
- ## Codex Watch Link Handoff
314
+ ## Live Watch Link Handoff
315
315
 
316
316
  When a campaign tool returns `watchUrl`, treat it as a user-opened app link, not
317
317
  as permission to drive the browser. A valid first handoff link must be the exact
@@ -324,19 +324,26 @@ derive, shorten, reconstruct, or print a bare `/campaign-builder/{campaignId}`
324
324
  URL.
325
325
 
326
326
  Never call browser-opening tools, shell `open`, Computer Use, or in-app browser
327
- automation just because a watch link exists. Print the first watch link exactly
328
- once, directly before the brief approval question, in this shape:
327
+ automation just because a watch link exists. If `create_campaign` returns
328
+ `watchHandoff.markdown`, print that exact value once, directly before the brief
329
+ approval question. It will use the URL mode to say Codex or Claude Code:
329
330
 
330
331
  ```text
331
- Campaign shell is ready. We'll keep building the campaign in this chat.
332
- You can watch it being built in real time in the app here:
332
+ ╔══════════════════════════════════════════════════════════════╗
333
+ ║ OPEN THIS LINK TO WATCH CODEX BUILD THE CAMPAIGN LIVE ║
334
+ ╚══════════════════════════════════════════════════════════════╝
333
335
 
334
- Watch link: {watchUrl}
336
+ [Open live campaign builder]({watchUrl})
335
337
 
336
- Send changes here. Ill ask for your approval whenever I need your expertise or
337
- taste before moving forward.
338
+ Keep this chat open. I'll ask approval questions here before making decisions
339
+ that need your judgment.
338
340
  ```
339
341
 
342
+ The Markdown link is intentional: rendered chat clients turn it into a large
343
+ click target, and plain terminals still expose the tokenized URL inside the
344
+ Markdown target. Do not replace it with a shell command or a browser-opening
345
+ instruction.
346
+
340
347
  The watch link should auto-login through the token in the URL. If the user says
341
348
  the link lands on auth, 404, permission, blank, or a visible error state, recover
342
349
  a fresh watch link once with `create_campaign({ campaignId })` or `get_campaign`
@@ -347,7 +354,7 @@ Never print a placeholder watch link such as "Open campaign" or "link will
347
354
  update once the shell is created." If the shell is not created yet, call
348
355
  `create_campaign` first. If `create_campaign` does not return `watchUrl`, stop
349
356
  and surface the missing watch-link error before lead sourcing.
350
- Do not print another `Watch link:` line during source/provider selection or
357
+ Do not print another watch-link handoff during source/provider selection or
351
358
  normal approval turns unless the user explicitly asks for the link or you are
352
359
  recovering a missing/broken URL.
353
360