@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.
- package/bin/sellable-install.mjs +145 -58
- package/package.json +1 -1
- package/skill-templates/create-campaign.md +25 -18
package/bin/sellable-install.mjs
CHANGED
|
@@ -31,7 +31,7 @@ function getInstallVersion() {
|
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
const CODEX_PLUGIN_VERSION = "0.1.
|
|
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
|
|
806
|
-
after shell creation and before brief approval. Later
|
|
807
|
-
describe what the already-open campaign app is showing and
|
|
808
|
-
currentStep/watchNarration; do not print the URL again unless the user
|
|
809
|
-
recovery path must replace a missing/broken link. In normal customer
|
|
810
|
-
not show
|
|
811
|
-
|
|
812
|
-
for
|
|
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
|
-
##
|
|
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.
|
|
832
|
-
once, directly before the brief
|
|
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
|
-
|
|
836
|
-
|
|
841
|
+
╔══════════════════════════════════════════════════════════════╗
|
|
842
|
+
║ OPEN THIS LINK TO WATCH CODEX BUILD THE CAMPAIGN LIVE ║
|
|
843
|
+
╚══════════════════════════════════════════════════════════════╝
|
|
837
844
|
|
|
838
|
-
|
|
845
|
+
[Open live campaign builder]({watchUrl})
|
|
839
846
|
|
|
840
|
-
|
|
841
|
-
|
|
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
|
|
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
|
|
1369
|
-
that, every approval should
|
|
1370
|
-
|
|
1371
|
-
link
|
|
1372
|
-
|
|
1373
|
-
|
|
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
|
|
1926
|
-
|
|
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
|
-
|
|
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
|
-
"--
|
|
1969
|
-
|
|
1970
|
-
"--",
|
|
1971
|
-
command,
|
|
1972
|
-
...args,
|
|
1979
|
+
"--url",
|
|
1980
|
+
withHostedWatchModeDriver(args[0], "codex"),
|
|
1973
1981
|
];
|
|
1974
1982
|
}
|
|
1975
1983
|
|
|
1976
|
-
|
|
1984
|
+
return [
|
|
1985
|
+
"mcp",
|
|
1986
|
+
"add",
|
|
1987
|
+
"sellable",
|
|
1988
|
+
"--env",
|
|
1989
|
+
WATCH_MODE_DRIVER_ENV.codex,
|
|
1990
|
+
"--",
|
|
1977
1991
|
command,
|
|
1978
|
-
args,
|
|
1979
|
-
|
|
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
|
-
[
|
|
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] =
|
|
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
|
-
|
|
2042
|
-
|
|
2043
|
-
if (
|
|
2044
|
-
|
|
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
|
|
2052
|
-
projServers.sellable
|
|
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
|
@@ -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
|
|
300
|
-
after shell creation and before brief approval. Later
|
|
301
|
-
describe what the already-open campaign app is showing and
|
|
302
|
-
currentStep/watchNarration; do not print the URL again unless the user
|
|
303
|
-
recovery path must replace a missing/broken link. In normal customer
|
|
304
|
-
not show `Open artifact:` lines, raw filesystem paths, or local draft
|
|
305
|
-
Local artifacts are debug/UAT-only unless the user asks for them. The
|
|
306
|
-
for deeper inspection; never use it as a substitute for showing the
|
|
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
|
-
##
|
|
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.
|
|
328
|
-
once, directly before the brief
|
|
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
|
-
|
|
332
|
-
|
|
332
|
+
╔══════════════════════════════════════════════════════════════╗
|
|
333
|
+
║ OPEN THIS LINK TO WATCH CODEX BUILD THE CAMPAIGN LIVE ║
|
|
334
|
+
╚══════════════════════════════════════════════════════════════╝
|
|
333
335
|
|
|
334
|
-
|
|
336
|
+
[Open live campaign builder]({watchUrl})
|
|
335
337
|
|
|
336
|
-
|
|
337
|
-
|
|
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
|
|
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
|
|