@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.
- package/bin/sellable-install.mjs +147 -58
- package/package.json +1 -1
- package/skill-templates/create-campaign.md +39 -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
|
};
|
|
@@ -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
|
|
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
|
|
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
|
-
##
|
|
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.
|
|
832
|
-
once, directly before the brief
|
|
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
|
-
|
|
836
|
-
|
|
843
|
+
╔══════════════════════════════════════════════════════════════╗
|
|
844
|
+
║ OPEN THIS LINK TO WATCH CODEX BUILD THE CAMPAIGN LIVE ║
|
|
845
|
+
╚══════════════════════════════════════════════════════════════╝
|
|
837
846
|
|
|
838
|
-
|
|
847
|
+
[Open live campaign builder]({watchUrl})
|
|
839
848
|
|
|
840
|
-
|
|
841
|
-
|
|
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
|
|
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
|
|
1369
|
-
that, every approval should
|
|
1370
|
-
|
|
1371
|
-
link
|
|
1372
|
-
|
|
1373
|
-
|
|
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
|
|
1926
|
-
|
|
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
|
-
|
|
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
|
-
"--
|
|
1969
|
-
|
|
1970
|
-
"--",
|
|
1971
|
-
command,
|
|
1972
|
-
...args,
|
|
1981
|
+
"--url",
|
|
1982
|
+
withHostedWatchModeDriver(args[0], "codex"),
|
|
1973
1983
|
];
|
|
1974
1984
|
}
|
|
1975
1985
|
|
|
1976
|
-
|
|
1986
|
+
return [
|
|
1987
|
+
"mcp",
|
|
1988
|
+
"add",
|
|
1989
|
+
"sellable",
|
|
1990
|
+
"--env",
|
|
1991
|
+
WATCH_MODE_DRIVER_ENV.codex,
|
|
1992
|
+
"--",
|
|
1977
1993
|
command,
|
|
1978
|
-
args,
|
|
1979
|
-
|
|
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
|
-
[
|
|
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] =
|
|
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
|
-
|
|
2042
|
-
|
|
2043
|
-
if (
|
|
2044
|
-
|
|
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
|
|
2052
|
-
projServers.sellable
|
|
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
|
@@ -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
|
|
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.
|
|
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
|
-
##
|
|
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.
|
|
328
|
-
once, directly before the brief
|
|
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
|
-
|
|
332
|
-
|
|
346
|
+
╔══════════════════════════════════════════════════════════════╗
|
|
347
|
+
║ OPEN THIS LINK TO WATCH CODEX BUILD THE CAMPAIGN LIVE ║
|
|
348
|
+
╚══════════════════════════════════════════════════════════════╝
|
|
333
349
|
|
|
334
|
-
|
|
350
|
+
[Open live campaign builder]({watchUrl})
|
|
335
351
|
|
|
336
|
-
|
|
337
|
-
|
|
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
|
|
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
|
|