@sellable/install 0.1.6 → 0.1.7
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 +21 -0
- package/bin/sellable-install.mjs +129 -27
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -30,6 +30,27 @@ Auth is stored once at:
|
|
|
30
30
|
|
|
31
31
|
Claude Code and Codex are configured to launch the same packaged MCP server.
|
|
32
32
|
|
|
33
|
+
## Names
|
|
34
|
+
|
|
35
|
+
Use the same public entrypoint in both hosts:
|
|
36
|
+
|
|
37
|
+
- Claude Code: `/sellable:create-campaign`
|
|
38
|
+
- Codex: `$sellable:create-campaign`
|
|
39
|
+
- Codex Desktop plugin: `sellable@sellable`
|
|
40
|
+
- Codex visible skill: `Sellable Create Campaign`
|
|
41
|
+
- Internal MCP workflow prompt: `create-campaign-v2`
|
|
42
|
+
|
|
43
|
+
Do not ask users to run `/sellable:create-campaign-v2`,
|
|
44
|
+
`$sellable:create-campaign-v2`, or `$sellable:sellable:create-campaign`.
|
|
45
|
+
`create-campaign-v2` is loaded internally by the skill.
|
|
46
|
+
|
|
47
|
+
## Structured Questions
|
|
48
|
+
|
|
49
|
+
Claude Code uses `AskUserQuestion`. Codex uses `request_user_input` when that
|
|
50
|
+
tool is exposed in an interactive Plan/collaboration-mode session.
|
|
51
|
+
`codex exec` is non-interactive, so it cannot show the structured questionnaire
|
|
52
|
+
UI.
|
|
53
|
+
|
|
33
54
|
For Codex Desktop, the installer also writes a local Sellable plugin bundle into
|
|
34
55
|
`~/.sellable/codex-marketplace`, includes the Sellable skill entrypoints, and
|
|
35
56
|
enables it in `~/.codex/config.toml`.
|
package/bin/sellable-install.mjs
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { spawnSync } from "node:child_process";
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import {
|
|
4
|
+
existsSync,
|
|
5
|
+
mkdirSync,
|
|
6
|
+
readFileSync,
|
|
7
|
+
rmSync,
|
|
8
|
+
writeFileSync,
|
|
9
|
+
} from "node:fs";
|
|
5
10
|
import { homedir } from "node:os";
|
|
11
|
+
import { dirname, join } from "node:path";
|
|
6
12
|
import { stdin as input, stdout as output } from "node:process";
|
|
7
13
|
import { createInterface } from "node:readline/promises";
|
|
8
14
|
|
|
9
15
|
const DEFAULT_API_URL = "https://app.sellable.dev";
|
|
10
16
|
const DEFAULT_SERVER_PACKAGE =
|
|
11
17
|
process.env.SELLABLE_MCP_PACKAGE || "@sellable/mcp";
|
|
12
|
-
const CODEX_PLUGIN_VERSION = "0.1.
|
|
18
|
+
const CODEX_PLUGIN_VERSION = "0.1.7";
|
|
13
19
|
const INSTALL_PACKAGE_SPEC = `@sellable/install@${CODEX_PLUGIN_VERSION}`;
|
|
14
20
|
|
|
15
21
|
function usage() {
|
|
@@ -109,7 +115,9 @@ function redact(value) {
|
|
|
109
115
|
|
|
110
116
|
function run(command, args, opts = {}) {
|
|
111
117
|
const rendered = [command, ...args].join(" ");
|
|
112
|
-
console.log(
|
|
118
|
+
console.log(
|
|
119
|
+
`+ ${rendered.replace(opts.token || "__NO_TOKEN__", "[redacted-token]")}`
|
|
120
|
+
);
|
|
113
121
|
if (opts.dryRun) return { status: 0, stdout: "", stderr: "" };
|
|
114
122
|
const result = spawnSync(command, args, {
|
|
115
123
|
encoding: "utf8",
|
|
@@ -186,8 +194,12 @@ async function promptForMissingAuth(opts) {
|
|
|
186
194
|
}
|
|
187
195
|
|
|
188
196
|
console.log("");
|
|
189
|
-
console.log(
|
|
190
|
-
|
|
197
|
+
console.log(
|
|
198
|
+
"Sellable needs one API token to connect Claude/Codex to your workspace."
|
|
199
|
+
);
|
|
200
|
+
console.log(
|
|
201
|
+
`Open ${opts.apiUrl}/settings, create a token, then paste the values below.`
|
|
202
|
+
);
|
|
191
203
|
console.log(`Auth will be stored once at ${authPath()}.`);
|
|
192
204
|
console.log("");
|
|
193
205
|
|
|
@@ -211,7 +223,11 @@ async function promptForMissingAuth(opts) {
|
|
|
211
223
|
}
|
|
212
224
|
|
|
213
225
|
function writeJson(path, data, opts) {
|
|
214
|
-
const redacted = JSON.stringify(
|
|
226
|
+
const redacted = JSON.stringify(
|
|
227
|
+
{ ...data, token: redact(data.token) },
|
|
228
|
+
null,
|
|
229
|
+
2
|
|
230
|
+
);
|
|
215
231
|
console.log(`Writing ${path}: ${redacted}`);
|
|
216
232
|
if (opts.dryRun) return;
|
|
217
233
|
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
@@ -246,7 +262,10 @@ function upsertTomlTable(content, tableName, block) {
|
|
|
246
262
|
const normalizedBlock = block.trimEnd();
|
|
247
263
|
|
|
248
264
|
if (tablePattern.test(content)) {
|
|
249
|
-
return content.replace(
|
|
265
|
+
return content.replace(
|
|
266
|
+
tablePattern,
|
|
267
|
+
(_, prefix) => `${prefix}${normalizedBlock}`
|
|
268
|
+
);
|
|
250
269
|
}
|
|
251
270
|
|
|
252
271
|
return `${content.trimEnd()}\n\n${normalizedBlock}\n`;
|
|
@@ -410,6 +429,38 @@ ${allowedToolsYaml(CREATE_CAMPAIGN_ALLOWED_TOOLS)}
|
|
|
410
429
|
|
|
411
430
|
Use this as the customer-facing entrypoint for Sellable campaign creation.
|
|
412
431
|
|
|
432
|
+
## Names To Use
|
|
433
|
+
|
|
434
|
+
Use these exact public names so Claude Code and Codex do not drift:
|
|
435
|
+
|
|
436
|
+
- Claude Code command: \`/sellable:create-campaign\`
|
|
437
|
+
- Codex skill command: \`$sellable:create-campaign\`
|
|
438
|
+
- Codex Desktop plugin: \`sellable@sellable\`
|
|
439
|
+
- Codex visible skill: \`Sellable Create Campaign\`
|
|
440
|
+
- Codex skill frontmatter name: \`create-campaign\`
|
|
441
|
+
- MCP server name: \`sellable\`
|
|
442
|
+
- Internal workflow prompt: \`create-campaign-v2\`
|
|
443
|
+
|
|
444
|
+
Do not tell users to run \`/sellable:create-campaign-v2\`,
|
|
445
|
+
\`$sellable:create-campaign-v2\`, or \`$sellable:sellable:create-campaign\`.
|
|
446
|
+
\`create-campaign-v2\` is only the internal subskill loaded through
|
|
447
|
+
\`mcp__sellable__get_subskill_prompt({ subskillName: "create-campaign-v2" })\`.
|
|
448
|
+
|
|
449
|
+
## Structured Questions
|
|
450
|
+
|
|
451
|
+
Use the host-native structured question gate for intake and approval:
|
|
452
|
+
|
|
453
|
+
- Claude Code: \`AskUserQuestion\`
|
|
454
|
+
- Codex: \`request_user_input\` when exposed in an interactive
|
|
455
|
+
Plan/collaboration-mode session
|
|
456
|
+
|
|
457
|
+
Do not silently ask Codex intake or approval questions as plain chat when
|
|
458
|
+
\`request_user_input\` is unavailable in an interactive session. Stop and tell
|
|
459
|
+
the user to switch to Plan mode / collaboration mode, then rerun
|
|
460
|
+
\`$sellable:create-campaign\`. Plain chat questions are only acceptable in
|
|
461
|
+
non-interactive \`codex exec\` smoke/rehearsal runs because structured user
|
|
462
|
+
input is unavailable by design there.
|
|
463
|
+
|
|
413
464
|
## Bootstrap
|
|
414
465
|
|
|
415
466
|
MCP tool access is required. First call \`mcp__sellable__get_auth_status({})\`
|
|
@@ -557,7 +608,12 @@ function installCodexDesktopPlugin(opts) {
|
|
|
557
608
|
const home = codexHome();
|
|
558
609
|
const configPath = join(home, "config.toml");
|
|
559
610
|
const marketplaceRoot = join(homedir(), ".sellable", "codex-marketplace");
|
|
560
|
-
const marketplacePath = join(
|
|
611
|
+
const marketplacePath = join(
|
|
612
|
+
marketplaceRoot,
|
|
613
|
+
".agents",
|
|
614
|
+
"plugins",
|
|
615
|
+
"marketplace.json"
|
|
616
|
+
);
|
|
561
617
|
const pluginRoot = join(marketplaceRoot, "plugins", "sellable");
|
|
562
618
|
const cacheRoot = join(home, "plugins", "cache", "sellable", "sellable");
|
|
563
619
|
const pluginCache = join(cacheRoot, CODEX_PLUGIN_VERSION);
|
|
@@ -592,7 +648,11 @@ function installCodexDesktopPlugin(opts) {
|
|
|
592
648
|
`${JSON.stringify(manifest, null, 2)}\n`,
|
|
593
649
|
opts
|
|
594
650
|
);
|
|
595
|
-
writeFile(
|
|
651
|
+
writeFile(
|
|
652
|
+
join(pluginRoot, ".mcp.json"),
|
|
653
|
+
`${JSON.stringify(mcp, null, 2)}\n`,
|
|
654
|
+
opts
|
|
655
|
+
);
|
|
596
656
|
writeCodexPluginSkills(pluginRoot, opts);
|
|
597
657
|
|
|
598
658
|
if (!opts.dryRun) {
|
|
@@ -603,12 +663,18 @@ function installCodexDesktopPlugin(opts) {
|
|
|
603
663
|
`${JSON.stringify(manifest, null, 2)}\n`,
|
|
604
664
|
opts
|
|
605
665
|
);
|
|
606
|
-
writeFile(
|
|
666
|
+
writeFile(
|
|
667
|
+
join(pluginCache, ".mcp.json"),
|
|
668
|
+
`${JSON.stringify(mcp, null, 2)}\n`,
|
|
669
|
+
opts
|
|
670
|
+
);
|
|
607
671
|
writeCodexPluginSkills(pluginCache, opts);
|
|
608
672
|
|
|
609
673
|
if (!opts.dryRun) {
|
|
610
674
|
mkdirSync(home, { recursive: true, mode: 0o700 });
|
|
611
|
-
let content = existsSync(configPath)
|
|
675
|
+
let content = existsSync(configPath)
|
|
676
|
+
? readFileSync(configPath, "utf8")
|
|
677
|
+
: "";
|
|
612
678
|
content = upsertTomlTable(
|
|
613
679
|
content,
|
|
614
680
|
"marketplaces.sellable",
|
|
@@ -633,7 +699,9 @@ enabled = false`
|
|
|
633
699
|
} else {
|
|
634
700
|
console.log(`+ upsert [marketplaces.sellable] in ${configPath}`);
|
|
635
701
|
console.log(`+ enable [plugins."sellable@sellable"] in ${configPath}`);
|
|
636
|
-
console.log(
|
|
702
|
+
console.log(
|
|
703
|
+
`+ disable stale [plugins."sellable@sellable-local"] in ${configPath}`
|
|
704
|
+
);
|
|
637
705
|
}
|
|
638
706
|
|
|
639
707
|
console.log("Codex Desktop plugin installed:");
|
|
@@ -656,7 +724,9 @@ function writeAuth(opts) {
|
|
|
656
724
|
};
|
|
657
725
|
|
|
658
726
|
if (opts.authFromExistingConfig) {
|
|
659
|
-
console.log(
|
|
727
|
+
console.log(
|
|
728
|
+
`Leaving existing Sellable auth config unchanged: ${authPath()}`
|
|
729
|
+
);
|
|
660
730
|
return;
|
|
661
731
|
}
|
|
662
732
|
|
|
@@ -684,7 +754,8 @@ function mcpCommand(opts) {
|
|
|
684
754
|
|
|
685
755
|
function installClaude(opts) {
|
|
686
756
|
if (!commandExists("claude")) {
|
|
687
|
-
const message =
|
|
757
|
+
const message =
|
|
758
|
+
"Claude CLI not found. Install/login to Claude Code, then rerun: sellable --host claude";
|
|
688
759
|
if (opts.host === "all") {
|
|
689
760
|
console.log(`Skipping Claude Code: ${message}`);
|
|
690
761
|
return false;
|
|
@@ -692,7 +763,11 @@ function installClaude(opts) {
|
|
|
692
763
|
throw new Error(message);
|
|
693
764
|
}
|
|
694
765
|
if (opts.server === "hosted") {
|
|
695
|
-
run(
|
|
766
|
+
run(
|
|
767
|
+
"claude",
|
|
768
|
+
["mcp", "add", "--transport", "http", "sellable", opts.hostedUrl],
|
|
769
|
+
opts
|
|
770
|
+
);
|
|
696
771
|
return true;
|
|
697
772
|
}
|
|
698
773
|
const [command, args] = mcpCommand(opts);
|
|
@@ -701,13 +776,18 @@ function installClaude(opts) {
|
|
|
701
776
|
dryRun: opts.dryRun,
|
|
702
777
|
allowFail: true,
|
|
703
778
|
});
|
|
704
|
-
run(
|
|
779
|
+
run(
|
|
780
|
+
"claude",
|
|
781
|
+
["mcp", "add", "--transport", "stdio", "sellable", "--", command, ...args],
|
|
782
|
+
opts
|
|
783
|
+
);
|
|
705
784
|
return true;
|
|
706
785
|
}
|
|
707
786
|
|
|
708
787
|
function installCodex(opts) {
|
|
709
788
|
if (!commandExists("codex")) {
|
|
710
|
-
const message =
|
|
789
|
+
const message =
|
|
790
|
+
"Codex CLI not found. Install/login to Codex, then rerun: sellable --host codex";
|
|
711
791
|
if (opts.host === "all") {
|
|
712
792
|
console.log(`Skipping Codex: ${message}`);
|
|
713
793
|
return false;
|
|
@@ -733,14 +813,20 @@ function installCodex(opts) {
|
|
|
733
813
|
function verify(opts) {
|
|
734
814
|
const stored = readStoredAuth();
|
|
735
815
|
if (!stored?.token || !stored?.workspaceId) {
|
|
736
|
-
throw new Error(
|
|
816
|
+
throw new Error(
|
|
817
|
+
`Sellable auth config missing or incomplete: ${authPath()}`
|
|
818
|
+
);
|
|
737
819
|
}
|
|
738
820
|
console.log(`Sellable auth config present: ${authPath()}`);
|
|
739
821
|
if (opts.host === "claude" || opts.host === "all") {
|
|
740
|
-
console.log(
|
|
822
|
+
console.log(
|
|
823
|
+
commandExists("claude") ? "Claude CLI present" : "Claude CLI missing"
|
|
824
|
+
);
|
|
741
825
|
}
|
|
742
826
|
if (opts.host === "codex" || opts.host === "all") {
|
|
743
|
-
console.log(
|
|
827
|
+
console.log(
|
|
828
|
+
commandExists("codex") ? "Codex CLI present" : "Codex CLI missing"
|
|
829
|
+
);
|
|
744
830
|
const pluginPath = join(
|
|
745
831
|
codexHome(),
|
|
746
832
|
"plugins",
|
|
@@ -762,8 +848,16 @@ function verify(opts) {
|
|
|
762
848
|
"sellable-create-campaign",
|
|
763
849
|
"SKILL.md"
|
|
764
850
|
);
|
|
765
|
-
console.log(
|
|
766
|
-
|
|
851
|
+
console.log(
|
|
852
|
+
existsSync(pluginPath)
|
|
853
|
+
? "Codex Desktop plugin present"
|
|
854
|
+
: "Codex Desktop plugin missing"
|
|
855
|
+
);
|
|
856
|
+
console.log(
|
|
857
|
+
existsSync(skillPath)
|
|
858
|
+
? "Codex skill bundle present"
|
|
859
|
+
: "Codex skill bundle missing"
|
|
860
|
+
);
|
|
767
861
|
}
|
|
768
862
|
}
|
|
769
863
|
|
|
@@ -771,11 +865,17 @@ function printNextSteps(installedHosts) {
|
|
|
771
865
|
console.log("");
|
|
772
866
|
console.log("Next steps:");
|
|
773
867
|
if (installedHosts.length > 0) {
|
|
774
|
-
console.log(
|
|
868
|
+
console.log(
|
|
869
|
+
`1. Fully quit and reopen ${installedHosts.join(" and ")} so MCP tools reload.`
|
|
870
|
+
);
|
|
775
871
|
console.log("2. Start a new thread and choose Sellable Create Campaign.");
|
|
776
|
-
console.log(
|
|
872
|
+
console.log(
|
|
873
|
+
"3. If tools do not appear, run: sellable --verify-only --host all"
|
|
874
|
+
);
|
|
777
875
|
} else {
|
|
778
|
-
console.log(
|
|
876
|
+
console.log(
|
|
877
|
+
"1. Install Claude Code or Codex, then rerun this installer for that host."
|
|
878
|
+
);
|
|
779
879
|
console.log("2. Verify auth later with: sellable --verify-only --host all");
|
|
780
880
|
}
|
|
781
881
|
}
|
|
@@ -806,7 +906,9 @@ async function main() {
|
|
|
806
906
|
}
|
|
807
907
|
}
|
|
808
908
|
if (opts.dryRun) {
|
|
809
|
-
console.log(
|
|
909
|
+
console.log(
|
|
910
|
+
"Dry run complete; verification skipped because no files were written."
|
|
911
|
+
);
|
|
810
912
|
} else {
|
|
811
913
|
verify(opts);
|
|
812
914
|
}
|