@sellable/install 0.1.164 → 0.1.166

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
  };
@@ -581,6 +585,8 @@ const CREATE_CAMPAIGN_ALLOWED_TOOLS = [
581
585
  "mcp__sellable__lookup_sales_nav_filter",
582
586
  "mcp__sellable__search_sales_nav",
583
587
  "mcp__sellable__search_prospeo",
588
+ "mcp__sellable__search_prospeo_companies",
589
+ "mcp__sellable__confirm_prospeo_company_accounts",
584
590
  "mcp__sellable__search_signals",
585
591
  "mcp__sellable__fetch_post_engagers",
586
592
  "mcp__sellable__enrich_with_prospeo",
@@ -802,20 +808,21 @@ that we are checking people from these posts to confirm the right people are
802
808
  actually engaging and the source is viable.
803
809
 
804
810
  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.
811
+ the readable inline content. Show the exact \`create_campaign.watchHandoff.markdown\`
812
+ block once, immediately after shell creation and before brief approval. Later
813
+ approval gates should describe what the already-open campaign app is showing and
814
+ rely on currentStep/watchNarration; do not print the URL again unless the user
815
+ asks or a recovery path must replace a missing/broken link. In normal customer
816
+ runs, do not show \`Open artifact:\` lines, raw filesystem paths, or local draft
817
+ filenames. Local artifacts are debug/UAT-only unless the user asks for them. The
818
+ link is for deeper inspection; never use it as a substitute for showing the
819
+ content in chat.
813
820
 
814
821
  Never mention MCP namespaces, prompt chunking, plugin cache paths, missing
815
822
  linked skill versions, runbooks, or local skill files in normal customer-facing
816
823
  copy.
817
824
 
818
- ## Codex Watch Link Handoff
825
+ ## Live Watch Link Handoff
819
826
 
820
827
  When a campaign tool returns \`watchUrl\`, treat it as a user-opened app link, not
821
828
  as permission to drive the browser. A valid first handoff link must be the exact
@@ -828,25 +835,32 @@ derive, shorten, reconstruct, or print a bare \`/campaign-builder/{campaignId}\`
828
835
  URL.
829
836
 
830
837
  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:
838
+ automation just because a watch link exists. If \`create_campaign\` returns
839
+ \`watchHandoff.markdown\`, print that exact value once, directly before the brief
840
+ approval question. It will use the URL mode to say Codex or Claude Code:
833
841
 
834
842
  \`\`\`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:
843
+ ╔══════════════════════════════════════════════════════════════╗
844
+ ║ OPEN THIS LINK TO WATCH CODEX BUILD THE CAMPAIGN LIVE ║
845
+ ╚══════════════════════════════════════════════════════════════╝
837
846
 
838
- Watch link: {watchUrl}
847
+ [Open live campaign builder]({watchUrl})
839
848
 
840
- Send changes here. Ill ask for your approval whenever I need your expertise or
841
- taste before moving forward.
849
+ Keep this chat open. I'll ask approval questions here before making decisions
850
+ that need your judgment.
842
851
  \`\`\`
843
852
 
853
+ The Markdown link is intentional: rendered chat clients turn it into a large
854
+ click target, and plain terminals still expose the tokenized URL inside the
855
+ Markdown target. Do not replace it with a shell command or a browser-opening
856
+ instruction.
857
+
844
858
  The watch link should auto-login through the token in the URL. If the user says
845
859
  the link lands on auth, 404, permission, blank, or a visible error state, recover
846
860
  a fresh watch link once with \`create_campaign({ campaignId })\` or \`get_campaign\`
847
861
  and print that link. Do not claim the browser was opened, inspected, or
848
862
  synchronized.
849
- Do not print another \`Watch link:\` line during source/provider selection or
863
+ Do not print another watch-link handoff during source/provider selection or
850
864
  normal approval turns unless the user explicitly asks for the link or you are
851
865
  recovering a missing/broken URL.
852
866
 
@@ -1365,12 +1379,13 @@ approval should show who we are targeting, why they should care, the offer/CTA,
1365
1379
  proof, lead source hypothesis, message angle, risks/assumptions, and what
1366
1380
  happens after approval.
1367
1381
 
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.
1382
+ Only the first brief approval handoff should print the exact
1383
+ \`create_campaign.watchHandoff.markdown\` block. After that, every approval should
1384
+ say what the already-open app is showing, but must not include another
1385
+ watch-link handoff unless the user explicitly asks for the link again or a
1386
+ recovery path needs to replace a broken/missing URL. Local artifacts are
1387
+ optional debug/UAT diagnostics only; do not surface file paths or local draft
1388
+ filenames in normal customer runs.
1374
1389
 
1375
1390
  This applies especially to message approvals. Never ask someone to approve a
1376
1391
  message they cannot see. Show the subject, tokenized template, a filled sample
@@ -1922,12 +1937,20 @@ const WATCH_MODE_DRIVER_ENV = {
1922
1937
  codex: "SELLABLE_WATCH_MODE_DRIVER=codex",
1923
1938
  };
1924
1939
 
1925
- function withWatchModeDriver(command, args, driver) {
1926
- const envArg = WATCH_MODE_DRIVER_ENV[driver];
1927
- if (!envArg) {
1940
+ function withHostedWatchModeDriver(rawUrl, driver) {
1941
+ if (!WATCH_MODE_DRIVER_ENV[driver]) {
1928
1942
  throw new Error(`Unknown watch mode driver: ${driver}`);
1929
1943
  }
1930
- return ["env", [envArg, command, ...args]];
1944
+ try {
1945
+ const url = new URL(rawUrl);
1946
+ if (!url.searchParams.has("driver")) {
1947
+ url.searchParams.set("driver", driver);
1948
+ }
1949
+ return url.toString();
1950
+ } catch {
1951
+ const separator = rawUrl.includes("?") ? "&" : "?";
1952
+ return `${rawUrl}${separator}driver=${encodeURIComponent(driver)}`;
1953
+ }
1931
1954
  }
1932
1955
 
1933
1956
  function mcpCommand(opts) {
@@ -1948,37 +1971,28 @@ function mcpCommand(opts) {
1948
1971
  return ["hosted", [opts.hostedUrl]];
1949
1972
  }
1950
1973
 
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
1974
  function codexMcpAddArgs(opts) {
1958
1975
  const [command, args] = mcpCommand(opts);
1959
1976
  if (command === "hosted") {
1960
- return ["mcp", "add", "sellable", "--url", args[0]];
1961
- }
1962
-
1963
- if (isWindowsPlatform()) {
1964
1977
  return [
1965
1978
  "mcp",
1966
1979
  "add",
1967
1980
  "sellable",
1968
- "--env",
1969
- WATCH_MODE_DRIVER_ENV.codex,
1970
- "--",
1971
- command,
1972
- ...args,
1981
+ "--url",
1982
+ withHostedWatchModeDriver(args[0], "codex"),
1973
1983
  ];
1974
1984
  }
1975
1985
 
1976
- const [wrappedCommand, wrappedArgs] = withWatchModeDriver(
1986
+ return [
1987
+ "mcp",
1988
+ "add",
1989
+ "sellable",
1990
+ "--env",
1991
+ WATCH_MODE_DRIVER_ENV.codex,
1992
+ "--",
1977
1993
  command,
1978
- args,
1979
- "codex"
1980
- );
1981
- return ["mcp", "add", "sellable", "--", wrappedCommand, ...wrappedArgs];
1994
+ ...args,
1995
+ ];
1982
1996
  }
1983
1997
 
1984
1998
  function installClaude(opts) {
@@ -1995,13 +2009,20 @@ function installClaude(opts) {
1995
2009
  if (opts.server === "hosted") {
1996
2010
  run(
1997
2011
  "claude",
1998
- ["mcp", "add", "--transport", "http", "sellable", opts.hostedUrl],
2012
+ [
2013
+ "mcp",
2014
+ "add",
2015
+ "--transport",
2016
+ "http",
2017
+ "sellable",
2018
+ withHostedWatchModeDriver(opts.hostedUrl, "claude"),
2019
+ ],
1999
2020
  opts
2000
2021
  );
2001
2022
  patchClaudeAlwaysLoad(opts);
2002
2023
  return true;
2003
2024
  }
2004
- const [command, args] = mcpCommandForHost(opts, "claude");
2025
+ const [command, args] = mcpCommand(opts);
2005
2026
  run("claude", ["mcp", "remove", "sellable"], {
2006
2027
  ...opts,
2007
2028
  dryRun: opts.dryRun,
@@ -2038,19 +2059,36 @@ function patchClaudeAlwaysLoad(opts) {
2038
2059
  const raw = readFileSync(claudeJsonPath, "utf8");
2039
2060
  const config = JSON.parse(raw);
2040
2061
  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;
2062
+ const patchSellableServer = (server) => {
2063
+ if (!server || typeof server !== "object") return;
2064
+ if (server.alwaysLoad !== true) {
2065
+ server.alwaysLoad = true;
2045
2066
  touched = true;
2046
2067
  }
2068
+ const isHttpServer =
2069
+ typeof server.url === "string" ||
2070
+ server.type === "http" ||
2071
+ server.transport === "http";
2072
+ if (!isHttpServer) {
2073
+ const existingDriver = server.env?.SELLABLE_WATCH_MODE_DRIVER;
2074
+ server.env = {
2075
+ ...(server.env && typeof server.env === "object" ? server.env : {}),
2076
+ SELLABLE_WATCH_MODE_DRIVER: "claude",
2077
+ };
2078
+ if (existingDriver !== "claude") {
2079
+ touched = true;
2080
+ }
2081
+ }
2082
+ };
2083
+ // Top-level mcpServers.sellable (older claude versions)
2084
+ if (config.mcpServers?.sellable) {
2085
+ patchSellableServer(config.mcpServers.sellable);
2047
2086
  }
2048
2087
  // Per-project mcpServers.sellable (current claude default)
2049
2088
  for (const projectPath of Object.keys(config.projects || {})) {
2050
2089
  const projServers = config.projects[projectPath]?.mcpServers;
2051
- if (projServers?.sellable && projServers.sellable.alwaysLoad !== true) {
2052
- projServers.sellable.alwaysLoad = true;
2053
- touched = true;
2090
+ if (projServers?.sellable) {
2091
+ patchSellableServer(projServers.sellable);
2054
2092
  }
2055
2093
  }
2056
2094
  if (touched) {
@@ -2099,6 +2137,11 @@ function installCodex(opts) {
2099
2137
  function verify(opts) {
2100
2138
  const stored = readStoredAuth();
2101
2139
  const checks = [];
2140
+ const hasWatchModeDriverMarker = (content, driver) =>
2141
+ new RegExp(`SELLABLE_WATCH_MODE_DRIVER[\\s\\S]{0,80}${driver}`).test(
2142
+ content
2143
+ ) || new RegExp(`[?&]driver=${driver}(?:\\b|&)`).test(content);
2144
+
2102
2145
  if (stored?.token && stored?.workspaceId) {
2103
2146
  checks.push({ ok: true, label: `Auth config: ${authPath()}` });
2104
2147
  } else {
@@ -2156,6 +2199,20 @@ function verify(opts) {
2156
2199
  ? "Legacy Claude Sellable host artifacts cleaned"
2157
2200
  : "Legacy Claude Sellable host artifacts still present",
2158
2201
  });
2202
+ const claudeJsonPath = join(homedir(), ".claude.json");
2203
+ const claudeConfigContent = existsSync(claudeJsonPath)
2204
+ ? readFileSync(claudeJsonPath, "utf8")
2205
+ : "";
2206
+ const hasClaudeWatchModeDriver = hasWatchModeDriverMarker(
2207
+ claudeConfigContent,
2208
+ "claude"
2209
+ );
2210
+ checks.push({
2211
+ ok: hasClaudeWatchModeDriver,
2212
+ label: hasClaudeWatchModeDriver
2213
+ ? "Claude watch mode driver pinned to claude"
2214
+ : "Claude watch mode driver missing",
2215
+ });
2159
2216
  }
2160
2217
  if (opts.host === "codex" || opts.host === "all") {
2161
2218
  checks.push({
@@ -2216,6 +2273,38 @@ function verify(opts) {
2216
2273
  const configContent = existsSync(configPath)
2217
2274
  ? readFileSync(configPath, "utf8")
2218
2275
  : "";
2276
+ const pluginMcpPath = join(
2277
+ codexHome(),
2278
+ "plugins",
2279
+ "cache",
2280
+ "sellable",
2281
+ "sellable",
2282
+ CODEX_PLUGIN_VERSION,
2283
+ ".mcp.json"
2284
+ );
2285
+ const pluginMcpContent = existsSync(pluginMcpPath)
2286
+ ? readFileSync(pluginMcpPath, "utf8")
2287
+ : "";
2288
+ const hasCodexMcpDriver = hasWatchModeDriverMarker(
2289
+ configContent,
2290
+ "codex"
2291
+ );
2292
+ const hasCodexPluginDriver = hasWatchModeDriverMarker(
2293
+ pluginMcpContent,
2294
+ "codex"
2295
+ );
2296
+ checks.push({
2297
+ ok: hasCodexMcpDriver,
2298
+ label: hasCodexMcpDriver
2299
+ ? "Codex CLI watch mode driver pinned to codex"
2300
+ : "Codex CLI watch mode driver missing",
2301
+ });
2302
+ checks.push({
2303
+ ok: hasCodexPluginDriver,
2304
+ label: hasCodexPluginDriver
2305
+ ? "Codex Desktop plugin watch mode driver pinned to codex"
2306
+ : "Codex Desktop plugin watch mode driver missing",
2307
+ });
2219
2308
  const hasCodexAgentRegistrations = codexCustomAgents().every((agent) =>
2220
2309
  configContent.includes(`[agents.${agent.name}]`)
2221
2310
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/install",
3
- "version": "0.1.164",
3
+ "version": "0.1.166",
4
4
  "type": "module",
5
5
  "description": "One-command installer for Sellable MCP in Claude Code and Codex",
6
6
  "bin": {
@@ -25,6 +25,8 @@ allowed-tools:
25
25
  - mcp__sellable__lookup_sales_nav_filter
26
26
  - mcp__sellable__search_sales_nav
27
27
  - mcp__sellable__search_prospeo
28
+ - mcp__sellable__search_prospeo_companies
29
+ - mcp__sellable__confirm_prospeo_company_accounts
28
30
  - mcp__sellable__search_signals
29
31
  - mcp__sellable__fetch_post_engagers
30
32
  - mcp__sellable__enrich_with_prospeo
@@ -204,6 +206,18 @@ are likely. Sales Nav is useful for recent LinkedIn activity, role/title
204
206
  precision, and referral paths, but it does not provide hiring-by-role filters;
205
207
  say that distinction plainly in the source-plan gate.
206
208
 
209
+ For company lookalikes, best-customer lookalikes, "companies like X",
210
+ lookalike accounts, companies that use AI, companies with API/SSO/Chrome
211
+ extension, news/award/integration/key-customer filters, or account discovery
212
+ before person search, use the Prospeo account approval flow:
213
+ `search_prospeo_companies -> confirm_prospeo_company_accounts -> search_prospeo`.
214
+ First return an account sample and ask the user to approve the account set.
215
+ Only call `confirm_prospeo_company_accounts` with the `companySearchToken`
216
+ returned by `search_prospeo_companies` and selected Prospeo company IDs; never
217
+ reconstruct raw account rows or domains manually. Account rows are not people
218
+ leads yet. The confirmation creates the `domainFilterId` that constrains the
219
+ follow-on `search_prospeo` people search.
220
+
207
221
  After scouting, ask for a second approval on Start Import. For
208
222
  LinkedIn engagement (`signal-discovery` internally), name how many
209
223
  recommended posts will be scraped and the target engager/source-candidate
@@ -296,22 +310,22 @@ lines short, use indexed section labels and bullets, and translate internal
296
310
  sourcing terms into plain language.
297
311
 
298
312
  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.
313
+ the readable inline content. Show the exact `create_campaign.watchHandoff.markdown`
314
+ block once, immediately after shell creation and before brief approval. Later
315
+ approval gates should describe what the already-open campaign app is showing and
316
+ rely on currentStep/watchNarration; do not print the URL again unless the user
317
+ asks or a recovery path must replace a missing/broken link. In normal customer
318
+ runs, do not show `Open artifact:` lines, raw filesystem paths, or local draft
319
+ filenames. Local artifacts are debug/UAT-only unless the user asks for them. The
320
+ link is for deeper inspection; never use it as a substitute for showing the
321
+ content in chat.
308
322
 
309
323
  Never mention MCP namespaces, prompt chunking, plugin cache paths, missing
310
324
  linked skill versions, runbooks, npm/package details, repo-local files, VPS or
311
325
  browser automation limitations, or local skill files in normal customer-facing
312
326
  copy.
313
327
 
314
- ## Codex Watch Link Handoff
328
+ ## Live Watch Link Handoff
315
329
 
316
330
  When a campaign tool returns `watchUrl`, treat it as a user-opened app link, not
317
331
  as permission to drive the browser. A valid first handoff link must be the exact
@@ -324,19 +338,26 @@ derive, shorten, reconstruct, or print a bare `/campaign-builder/{campaignId}`
324
338
  URL.
325
339
 
326
340
  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:
341
+ automation just because a watch link exists. If `create_campaign` returns
342
+ `watchHandoff.markdown`, print that exact value once, directly before the brief
343
+ approval question. It will use the URL mode to say Codex or Claude Code:
329
344
 
330
345
  ```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:
346
+ ╔══════════════════════════════════════════════════════════════╗
347
+ ║ OPEN THIS LINK TO WATCH CODEX BUILD THE CAMPAIGN LIVE ║
348
+ ╚══════════════════════════════════════════════════════════════╝
333
349
 
334
- Watch link: {watchUrl}
350
+ [Open live campaign builder]({watchUrl})
335
351
 
336
- Send changes here. Ill ask for your approval whenever I need your expertise or
337
- taste before moving forward.
352
+ Keep this chat open. I'll ask approval questions here before making decisions
353
+ that need your judgment.
338
354
  ```
339
355
 
356
+ The Markdown link is intentional: rendered chat clients turn it into a large
357
+ click target, and plain terminals still expose the tokenized URL inside the
358
+ Markdown target. Do not replace it with a shell command or a browser-opening
359
+ instruction.
360
+
340
361
  The watch link should auto-login through the token in the URL. If the user says
341
362
  the link lands on auth, 404, permission, blank, or a visible error state, recover
342
363
  a fresh watch link once with `create_campaign({ campaignId })` or `get_campaign`
@@ -347,7 +368,7 @@ Never print a placeholder watch link such as "Open campaign" or "link will
347
368
  update once the shell is created." If the shell is not created yet, call
348
369
  `create_campaign` first. If `create_campaign` does not return `watchUrl`, stop
349
370
  and surface the missing watch-link error before lead sourcing.
350
- Do not print another `Watch link:` line during source/provider selection or
371
+ Do not print another watch-link handoff during source/provider selection or
351
372
  normal approval turns unless the user explicitly asks for the link or you are
352
373
  recovering a missing/broken URL.
353
374