@seanpropapp/cli 0.1.0-beta.1

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.
Files changed (87) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +64 -0
  3. package/dist/commands/autostart.d.ts +42 -0
  4. package/dist/commands/autostart.js +195 -0
  5. package/dist/commands/autostart.js.map +1 -0
  6. package/dist/commands/bridge.d.ts +54 -0
  7. package/dist/commands/bridge.js +145 -0
  8. package/dist/commands/bridge.js.map +1 -0
  9. package/dist/commands/connect.d.ts +56 -0
  10. package/dist/commands/connect.js +213 -0
  11. package/dist/commands/connect.js.map +1 -0
  12. package/dist/commands/doctor.d.ts +24 -0
  13. package/dist/commands/doctor.js +200 -0
  14. package/dist/commands/doctor.js.map +1 -0
  15. package/dist/commands/install-claude.d.ts +22 -0
  16. package/dist/commands/install-claude.js +56 -0
  17. package/dist/commands/install-claude.js.map +1 -0
  18. package/dist/commands/mcp.d.ts +5 -0
  19. package/dist/commands/mcp.js +23 -0
  20. package/dist/commands/mcp.js.map +1 -0
  21. package/dist/commands/pair-url.d.ts +12 -0
  22. package/dist/commands/pair-url.js +23 -0
  23. package/dist/commands/pair-url.js.map +1 -0
  24. package/dist/commands/pair.d.ts +12 -0
  25. package/dist/commands/pair.js +24 -0
  26. package/dist/commands/pair.js.map +1 -0
  27. package/dist/commands/prompt.d.ts +5 -0
  28. package/dist/commands/prompt.js +24 -0
  29. package/dist/commands/prompt.js.map +1 -0
  30. package/dist/commands/telemetry-cmd.d.ts +8 -0
  31. package/dist/commands/telemetry-cmd.js +55 -0
  32. package/dist/commands/telemetry-cmd.js.map +1 -0
  33. package/dist/config.d.ts +63 -0
  34. package/dist/config.js +77 -0
  35. package/dist/config.js.map +1 -0
  36. package/dist/http/auth-middleware.d.ts +9 -0
  37. package/dist/http/auth-middleware.js +29 -0
  38. package/dist/http/auth-middleware.js.map +1 -0
  39. package/dist/http/chat-completions.d.ts +48 -0
  40. package/dist/http/chat-completions.js +117 -0
  41. package/dist/http/chat-completions.js.map +1 -0
  42. package/dist/http/cors.d.ts +9 -0
  43. package/dist/http/cors.js +35 -0
  44. package/dist/http/cors.js.map +1 -0
  45. package/dist/http/handshake.d.ts +43 -0
  46. package/dist/http/handshake.js +28 -0
  47. package/dist/http/handshake.js.map +1 -0
  48. package/dist/http/messages-endpoint.d.ts +67 -0
  49. package/dist/http/messages-endpoint.js +95 -0
  50. package/dist/http/messages-endpoint.js.map +1 -0
  51. package/dist/http/server.d.ts +37 -0
  52. package/dist/http/server.js +83 -0
  53. package/dist/http/server.js.map +1 -0
  54. package/dist/http/sse.d.ts +35 -0
  55. package/dist/http/sse.js +72 -0
  56. package/dist/http/sse.js.map +1 -0
  57. package/dist/index.d.ts +2 -0
  58. package/dist/index.js +219 -0
  59. package/dist/index.js.map +1 -0
  60. package/dist/mcp/manifest.d.ts +16 -0
  61. package/dist/mcp/manifest.js +88 -0
  62. package/dist/mcp/manifest.js.map +1 -0
  63. package/dist/mcp/server.d.ts +25 -0
  64. package/dist/mcp/server.js +166 -0
  65. package/dist/mcp/server.js.map +1 -0
  66. package/dist/providers/base.d.ts +91 -0
  67. package/dist/providers/base.js +27 -0
  68. package/dist/providers/base.js.map +1 -0
  69. package/dist/providers/claude.d.ts +22 -0
  70. package/dist/providers/claude.js +157 -0
  71. package/dist/providers/claude.js.map +1 -0
  72. package/dist/providers/codex.d.ts +38 -0
  73. package/dist/providers/codex.js +179 -0
  74. package/dist/providers/codex.js.map +1 -0
  75. package/dist/providers/detect-util.d.ts +17 -0
  76. package/dist/providers/detect-util.js +68 -0
  77. package/dist/providers/detect-util.js.map +1 -0
  78. package/dist/providers/index.d.ts +14 -0
  79. package/dist/providers/index.js +21 -0
  80. package/dist/providers/index.js.map +1 -0
  81. package/dist/telemetry.d.ts +68 -0
  82. package/dist/telemetry.js +118 -0
  83. package/dist/telemetry.js.map +1 -0
  84. package/dist/version.d.ts +9 -0
  85. package/dist/version.js +10 -0
  86. package/dist/version.js.map +1 -0
  87. package/package.json +62 -0
@@ -0,0 +1,213 @@
1
+ import openImport from "open";
2
+ import { ClaudeProvider } from "../providers/claude.js";
3
+ import { CodexProvider } from "../providers/codex.js";
4
+ import { detectAllProviders } from "../providers/index.js";
5
+ import { startServer } from "../http/server.js";
6
+ import { updateConfig, loadConfig } from "../config.js";
7
+ import { generatePairToken, osc8Link, pairUrl, } from "./pair-url.js";
8
+ import { planClaudeInstall, runInstall } from "./install-claude.js";
9
+ import { confirm } from "./prompt.js";
10
+ import { spawnBackgroundBridge } from "./bridge.js";
11
+ import { emitConnectStart } from "../telemetry.js";
12
+ const HANDSHAKE_TIMEOUT_MS = 60_000;
13
+ const HANDSHAKE_POLL_MS = 1_000;
14
+ const SAMPLE_URL = "https://prop.seanoneill.com/workspace?sample=true";
15
+ function fmtSeconds(ms) {
16
+ return (ms / 1000).toFixed(1);
17
+ }
18
+ /**
19
+ * Persona A entry point. Runs the full first-time flow:
20
+ * 1. Detect Claude CLI; inline-guide install if missing.
21
+ * 2. Generate pair token.
22
+ * 3. Start bridge (background by default).
23
+ * 4. Print clickable + plain-text pair URL (TX13).
24
+ * 5. Open browser.
25
+ * 6. Poll config for paired_at (set by bridge once /pair confirms).
26
+ * 7. Print elapsed time + sample-analysis CTA on success (TX6).
27
+ */
28
+ export async function runConnect(opts = {}) {
29
+ const out = opts.stdout ?? ((s) => process.stdout.write(s));
30
+ const err = opts.stderr ?? ((s) => process.stderr.write(s));
31
+ const t0 = Date.now();
32
+ // Fire-and-forget telemetry. Swallowed silently if disabled or offline.
33
+ void emitConnectStart({
34
+ ...(opts.configDir !== undefined ? { configDir: opts.configDir } : {}),
35
+ ...(opts.noTelemetry ? { forceDisable: true } : {}),
36
+ });
37
+ // 1. Detect Claude CLI.
38
+ out("Looking for Claude CLI on your system...\n");
39
+ const claude = opts.providers?.claude ?? new ClaudeProvider();
40
+ const codex = opts.providers?.codex ?? new CodexProvider();
41
+ let detected;
42
+ if (opts.providers) {
43
+ detected = {
44
+ claude: await claude.detect(),
45
+ codex: await codex.detect(),
46
+ };
47
+ }
48
+ else {
49
+ const all = await detectAllProviders();
50
+ detected = { claude: all.claude, codex: all.codex };
51
+ }
52
+ if (!detected.claude.installed && !detected.codex.installed) {
53
+ out("\n Claude CLI not found.\n\n");
54
+ out(" SeanPropApp uses your Claude Pro subscription via the official Claude CLI.\n");
55
+ out(" You need to install it once.\n\n");
56
+ const plan = await planClaudeInstall();
57
+ if (plan.autoCommand && !opts.skipInstallPrompt) {
58
+ out(` ${plan.autoCommand.label}\n\n`);
59
+ const yes = await confirm(" Install now? [Y/n] ");
60
+ if (yes) {
61
+ out("\n");
62
+ const code = await runInstall(plan.autoCommand.binary, plan.autoCommand.args);
63
+ if (code !== 0) {
64
+ err(`\n Install failed (exit ${code ?? "unknown"}). See the output above and re-run \`seanpropapp connect\`.\n`);
65
+ return {
66
+ success: false,
67
+ elapsedSeconds: (Date.now() - t0) / 1000,
68
+ reason: "install_failed",
69
+ };
70
+ }
71
+ // Re-detect.
72
+ const after = await claude.detect();
73
+ detected.claude = after;
74
+ }
75
+ else {
76
+ out(`\n ${plan.manualMessage}\n`);
77
+ out(" Re-run `seanpropapp connect` once Claude CLI is installed.\n");
78
+ return {
79
+ success: false,
80
+ elapsedSeconds: (Date.now() - t0) / 1000,
81
+ reason: "user_declined_install",
82
+ };
83
+ }
84
+ }
85
+ else {
86
+ out(` ${plan.manualMessage}\n`);
87
+ out(" Re-run `seanpropapp connect` once Claude CLI is installed.\n");
88
+ return {
89
+ success: false,
90
+ elapsedSeconds: (Date.now() - t0) / 1000,
91
+ reason: "manual_install_required",
92
+ };
93
+ }
94
+ }
95
+ if (detected.claude.installed) {
96
+ const v = detected.claude.version ? ` (${detected.claude.version})` : "";
97
+ out(` Claude CLI detected${v}\n`);
98
+ }
99
+ if (detected.codex.installed) {
100
+ const v = detected.codex.version ? ` (${detected.codex.version})` : "";
101
+ out(` Codex CLI detected${v}\n`);
102
+ }
103
+ // 2. Generate pair token.
104
+ const token = generatePairToken();
105
+ // 3. Start bridge (background by default).
106
+ let bridgePort;
107
+ let stopInlineBridge;
108
+ if (opts.noBridgeFork) {
109
+ const running = await startServer({
110
+ token,
111
+ port: opts.port,
112
+ pairedAt: async () => {
113
+ const cfg = await loadConfig(opts.configDir);
114
+ return cfg.paired_at ?? null;
115
+ },
116
+ });
117
+ bridgePort = running.port;
118
+ stopInlineBridge = () => running.close();
119
+ }
120
+ else {
121
+ // Detached child. We still need to know what port it bound. Quick approach
122
+ // for v1.4.0: bind here just long enough to claim the port, then close
123
+ // and hand the port over to the child. This avoids a stdio handshake.
124
+ const probe = await startServer({
125
+ token,
126
+ port: opts.port,
127
+ pairedAt: () => null,
128
+ });
129
+ bridgePort = probe.port;
130
+ await probe.close();
131
+ await spawnBackgroundBridge({
132
+ port: bridgePort,
133
+ token,
134
+ ...(opts.configDir ? { configDir: opts.configDir } : {}),
135
+ ...(opts.skipBridgeHealthcheck ? { skipHealthcheck: true } : {}),
136
+ });
137
+ }
138
+ await updateConfig({
139
+ pair_token: token,
140
+ bridge_port: bridgePort,
141
+ bridge_url: `http://127.0.0.1:${bridgePort}`,
142
+ }, opts.configDir);
143
+ out(` Bridge ready on port ${bridgePort}\n`);
144
+ // 4. Print pair URL: clickable + plain (TX13).
145
+ const url = pairUrl(token);
146
+ out(`\n Pair URL: ${osc8Link(url, url)}\n`);
147
+ out(` Or paste in browser: ${url}\n`);
148
+ // 5. Open browser.
149
+ if (!opts.skipBrowserOpen) {
150
+ try {
151
+ // `open` is a default-export ESM module.
152
+ await openImport(url);
153
+ }
154
+ catch {
155
+ // Non-fatal: the user can still paste the plain URL.
156
+ }
157
+ }
158
+ out("\n Waiting for you to confirm pairing in the browser...\n");
159
+ // 6. Poll for paired_at.
160
+ let paired = false;
161
+ if (opts.fakePairedAt) {
162
+ await updateConfig({ paired_at: opts.fakePairedAt }, opts.configDir);
163
+ paired = true;
164
+ }
165
+ else {
166
+ const deadline = Date.now() + HANDSHAKE_TIMEOUT_MS;
167
+ while (Date.now() < deadline) {
168
+ const cfg = await loadConfig(opts.configDir);
169
+ if (cfg.paired_at) {
170
+ paired = true;
171
+ break;
172
+ }
173
+ await new Promise((r) => setTimeout(r, HANDSHAKE_POLL_MS));
174
+ }
175
+ }
176
+ if (!paired) {
177
+ if (stopInlineBridge)
178
+ await stopInlineBridge();
179
+ err("\n Timed out waiting for pairing (60s).\n" +
180
+ " Either re-run `seanpropapp connect` or click the pair URL again.\n");
181
+ return {
182
+ success: false,
183
+ elapsedSeconds: (Date.now() - t0) / 1000,
184
+ reason: "pair_timeout",
185
+ bridgePort,
186
+ pairUrl: url,
187
+ };
188
+ }
189
+ // 7. Success.
190
+ const elapsedMs = Date.now() - t0;
191
+ out(`\n Connected in ${fmtSeconds(elapsedMs)}s! Run a sample analysis at:\n` +
192
+ ` ${SAMPLE_URL}\n\n` +
193
+ " Next time, just run `npx @seanpropapp/cli connect` to reconnect.\n");
194
+ // When in --no-bridge-fork mode, hand control to the inline server so it
195
+ // keeps running until the user kills the process.
196
+ if (stopInlineBridge) {
197
+ await new Promise((resolve) => {
198
+ const shutdown = async () => {
199
+ await stopInlineBridge();
200
+ resolve();
201
+ };
202
+ process.once("SIGINT", shutdown);
203
+ process.once("SIGTERM", shutdown);
204
+ });
205
+ }
206
+ return {
207
+ success: true,
208
+ elapsedSeconds: elapsedMs / 1000,
209
+ bridgePort,
210
+ pairUrl: url,
211
+ };
212
+ }
213
+ //# sourceMappingURL=connect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connect.js","sourceRoot":"","sources":["../../src/commands/connect.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import { detectAllProviders } from "../providers/index.js";
2
+ export interface DoctorOptions {
3
+ configDir?: string;
4
+ /** Override fetch (used by tests). */
5
+ fetchImpl?: typeof fetch;
6
+ /** Override stdout writer (used by tests). */
7
+ stdout?: (line: string) => void;
8
+ /** Override provider detection (used by tests). */
9
+ detectFn?: typeof detectAllProviders;
10
+ /** Override the port-probe function (used by tests). */
11
+ probePortFn?: (port: number) => Promise<boolean>;
12
+ /** When true, emit a single JSON envelope instead of human-readable sections. */
13
+ json?: boolean;
14
+ }
15
+ export interface DoctorResult {
16
+ ok: boolean;
17
+ sections: Record<string, string[]>;
18
+ }
19
+ /**
20
+ * Returns true if the given local port can be bound (i.e., is free).
21
+ * Honors a short timeout so the doctor doesn't stall.
22
+ */
23
+ export declare function probePortAvailable(port: number): Promise<boolean>;
24
+ export declare function runDoctor(opts?: DoctorOptions): Promise<DoctorResult>;
@@ -0,0 +1,200 @@
1
+ /**
2
+ * `seanpropapp doctor`: self-diagnostic for the bridge + provider stack.
3
+ *
4
+ * Output is plain text with simple section headings, no emojis. Every WARN
5
+ * or FAIL line is followed by an actionable suggestion. The check ordering
6
+ * matches the typical first-run failure mode (no Claude CLI -> port taken ->
7
+ * not paired yet).
8
+ *
9
+ * No secrets are ever printed; we only report token PRESENCE.
10
+ */
11
+ import net from "node:net";
12
+ import os from "node:os";
13
+ import { detectAllProviders } from "../providers/index.js";
14
+ import { loadConfig, getConfigPath, } from "../config.js";
15
+ import { DEFAULT_BRIDGE_PORT, MAX_PORT_FALLBACK, } from "../http/server.js";
16
+ import { CLI_VERSION } from "../version.js";
17
+ /**
18
+ * Returns true if the given local port can be bound (i.e., is free).
19
+ * Honors a short timeout so the doctor doesn't stall.
20
+ */
21
+ export function probePortAvailable(port) {
22
+ return new Promise((resolve) => {
23
+ const srv = net.createServer();
24
+ let settled = false;
25
+ const cleanup = (ok) => {
26
+ if (settled)
27
+ return;
28
+ settled = true;
29
+ srv.removeAllListeners();
30
+ srv.close(() => resolve(ok));
31
+ };
32
+ srv.once("error", () => cleanup(false));
33
+ srv.once("listening", () => cleanup(true));
34
+ try {
35
+ srv.listen(port, "127.0.0.1");
36
+ }
37
+ catch {
38
+ cleanup(false);
39
+ }
40
+ setTimeout(() => cleanup(false), 500);
41
+ });
42
+ }
43
+ /**
44
+ * Pretty-print a number as `Xm Ys` for paired_at deltas.
45
+ */
46
+ function fmtAge(ms) {
47
+ if (ms < 0)
48
+ return "in the future";
49
+ const s = Math.floor(ms / 1000);
50
+ if (s < 60)
51
+ return `${s}s ago`;
52
+ const m = Math.floor(s / 60);
53
+ if (m < 60)
54
+ return `${m}m ${s % 60}s ago`;
55
+ const h = Math.floor(m / 60);
56
+ return `${h}h ${m % 60}m ago`;
57
+ }
58
+ export async function runDoctor(opts = {}) {
59
+ const out = opts.stdout ?? ((s) => process.stdout.write(s));
60
+ const fetchImpl = opts.fetchImpl ?? fetch;
61
+ const detectFn = opts.detectFn ?? detectAllProviders;
62
+ const probePort = opts.probePortFn ?? probePortAvailable;
63
+ const sections = {};
64
+ let ok = true;
65
+ function section(name, lines) {
66
+ sections[name] = lines;
67
+ out(`\n${name}\n`);
68
+ for (const line of lines)
69
+ out(` ${line}\n`);
70
+ }
71
+ // 1. CLI + system.
72
+ section("System", [
73
+ `seanpropapp v${CLI_VERSION}`,
74
+ `Node ${process.version} (${process.platform}/${process.arch})`,
75
+ `Host: ${os.hostname()}`,
76
+ ]);
77
+ // 2. Providers.
78
+ const providerLines = [];
79
+ const detected = await detectFn();
80
+ if (detected.claude.installed) {
81
+ providerLines.push(`Claude CLI: detected${detected.claude.version ? ` (${detected.claude.version})` : ""}`);
82
+ }
83
+ else {
84
+ providerLines.push(`Claude CLI: NOT FOUND. Install with: brew install anthropic/claude/claude (macOS) or see https://claude.ai/cli`);
85
+ ok = false;
86
+ }
87
+ if (detected.codex.installed) {
88
+ providerLines.push(`Codex CLI: detected${detected.codex.version ? ` (${detected.codex.version})` : ""}`);
89
+ }
90
+ else {
91
+ providerLines.push(`Codex CLI: not detected (optional)`);
92
+ }
93
+ if (!detected.gemini.installed) {
94
+ providerLines.push(`Gemini CLI: not yet supported`);
95
+ }
96
+ section("Providers", providerLines);
97
+ // 3. Port availability.
98
+ const portLines = [];
99
+ let firstFreePort = null;
100
+ for (let p = DEFAULT_BRIDGE_PORT; p <= MAX_PORT_FALLBACK; p++) {
101
+ const free = await probePort(p);
102
+ portLines.push(`Port ${p}: ${free ? "free" : "in use"}`);
103
+ if (free && firstFreePort === null)
104
+ firstFreePort = p;
105
+ }
106
+ if (firstFreePort === null) {
107
+ portLines.push(`WARN: every port in ${DEFAULT_BRIDGE_PORT}-${MAX_PORT_FALLBACK} is taken. Stop another process or pass --port to seanpropapp bridge.`);
108
+ ok = false;
109
+ }
110
+ else {
111
+ portLines.push(`Next free port: ${firstFreePort}`);
112
+ }
113
+ section("Bridge ports", portLines);
114
+ // 4. Config + pairing.
115
+ const cfg = await loadConfig(opts.configDir);
116
+ const configPath = getConfigPath(opts.configDir);
117
+ const configLines = [`Config: ${configPath}`];
118
+ if (cfg.pair_token) {
119
+ configLines.push("Pair token: present (value redacted)");
120
+ }
121
+ else {
122
+ configLines.push("Pair token: MISSING. Run `seanpropapp connect` to generate one.");
123
+ ok = false;
124
+ }
125
+ if (cfg.mcp_token) {
126
+ configLines.push("MCP token: present (value redacted)");
127
+ }
128
+ else {
129
+ configLines.push("MCP token: missing (only needed for `seanpropapp mcp`)");
130
+ }
131
+ if (cfg.paired_at) {
132
+ const parsed = Date.parse(cfg.paired_at);
133
+ if (Number.isFinite(parsed)) {
134
+ configLines.push(`Paired at: ${cfg.paired_at} (${fmtAge(Date.now() - parsed)})`);
135
+ }
136
+ else {
137
+ configLines.push(`Paired at: ${cfg.paired_at}`);
138
+ }
139
+ }
140
+ else {
141
+ configLines.push("Paired at: never. Complete pairing in the browser via `seanpropapp connect`.");
142
+ }
143
+ if (cfg.bridge_url) {
144
+ configLines.push(`Bridge URL: ${cfg.bridge_url}`);
145
+ }
146
+ if (cfg.bridge_port) {
147
+ configLines.push(`Bridge port: ${cfg.bridge_port}`);
148
+ }
149
+ if (cfg.telemetry_enabled === undefined ||
150
+ cfg.telemetry_enabled === false) {
151
+ configLines.push("Telemetry: opt-in (off)");
152
+ }
153
+ else {
154
+ configLines.push("Telemetry: opt-in (on)");
155
+ }
156
+ section("Config", configLines);
157
+ // 5. Bridge health check.
158
+ const healthLines = [];
159
+ if (cfg.bridge_url && cfg.pair_token) {
160
+ try {
161
+ const url = `${cfg.bridge_url.replace(/\/$/, "")}/v1/handshake`;
162
+ const res = await fetchImpl(url, {
163
+ method: "GET",
164
+ headers: { Authorization: `Bearer ${cfg.pair_token}` },
165
+ });
166
+ if (res.ok) {
167
+ healthLines.push(`Bridge reachable at ${url} (HTTP ${res.status})`);
168
+ }
169
+ else if (res.status === 401) {
170
+ healthLines.push(`Bridge reachable but rejected the saved pair token (HTTP 401). Re-pair with: \`seanpropapp connect\``);
171
+ ok = false;
172
+ }
173
+ else {
174
+ healthLines.push(`Bridge returned HTTP ${res.status} for ${url}. Restart with: \`seanpropapp connect\``);
175
+ ok = false;
176
+ }
177
+ }
178
+ catch (err) {
179
+ const msg = err instanceof Error ? err.message : String(err);
180
+ healthLines.push(`Bridge not reachable at ${cfg.bridge_url}: ${msg}. Try: \`seanpropapp connect\``);
181
+ ok = false;
182
+ }
183
+ }
184
+ else {
185
+ healthLines.push("Bridge not configured. Run `seanpropapp connect` to start the bridge.");
186
+ }
187
+ section("Bridge health", healthLines);
188
+ // 6. Summary.
189
+ if (opts.json) {
190
+ // Drain anything we accumulated by replaying as a JSON envelope.
191
+ // (We still printed the human sections above so JSON consumers piping
192
+ // through `tee` see both; clean callers should pipe stdout-only.)
193
+ out(`${JSON.stringify({ ok, sections })}\n`);
194
+ }
195
+ else {
196
+ out(`\n${ok ? "All checks passed." : "Some checks failed. See suggestions above."}\n`);
197
+ }
198
+ return { ok, sections };
199
+ }
200
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ export interface InstallGuidance {
2
+ platform: NodeJS.Platform;
3
+ /** Shell command we can run for an automated install (macOS Homebrew). */
4
+ autoCommand?: {
5
+ binary: string;
6
+ args: string[];
7
+ label: string;
8
+ };
9
+ /** Human-readable instructions to print when we can't or won't auto-install. */
10
+ manualMessage: string;
11
+ }
12
+ /**
13
+ * Produce OS-aware install guidance for Claude CLI. Persona A typically has
14
+ * Claude Pro via the web app but not the CLI; this is the inline guided
15
+ * install hook called by `connect`.
16
+ */
17
+ export declare function planClaudeInstall(): Promise<InstallGuidance>;
18
+ /**
19
+ * Spawn an interactive install command, piping its stdout/stderr to the
20
+ * user's terminal. Resolves with the exit code.
21
+ */
22
+ export declare function runInstall(binary: string, args: string[]): Promise<number | null>;
@@ -0,0 +1,56 @@
1
+ import { spawn } from "node:child_process";
2
+ import { which } from "../providers/detect-util.js";
3
+ /**
4
+ * Produce OS-aware install guidance for Claude CLI. Persona A typically has
5
+ * Claude Pro via the web app but not the CLI; this is the inline guided
6
+ * install hook called by `connect`.
7
+ */
8
+ export async function planClaudeInstall() {
9
+ if (process.platform === "darwin") {
10
+ const brew = await which("brew");
11
+ if (brew) {
12
+ return {
13
+ platform: "darwin",
14
+ autoCommand: {
15
+ binary: brew,
16
+ args: ["install", "anthropic-ai/claude/claude"],
17
+ label: "brew install anthropic-ai/claude/claude",
18
+ },
19
+ manualMessage: "Install Claude CLI via Homebrew: brew install anthropic-ai/claude/claude",
20
+ };
21
+ }
22
+ return {
23
+ platform: "darwin",
24
+ manualMessage: "Install Homebrew (https://brew.sh) then run: brew install anthropic-ai/claude/claude",
25
+ };
26
+ }
27
+ if (process.platform === "linux") {
28
+ return {
29
+ platform: "linux",
30
+ manualMessage: "See https://claude.ai/cli for the install command for your distro " +
31
+ "(apt, pacman, or curl-bash).",
32
+ };
33
+ }
34
+ if (process.platform === "win32") {
35
+ return {
36
+ platform: "win32",
37
+ manualMessage: "Install Claude CLI from https://claude.ai/cli, then re-run `seanpropapp connect`.",
38
+ };
39
+ }
40
+ return {
41
+ platform: process.platform,
42
+ manualMessage: "See https://claude.ai/cli for install instructions on your platform.",
43
+ };
44
+ }
45
+ /**
46
+ * Spawn an interactive install command, piping its stdout/stderr to the
47
+ * user's terminal. Resolves with the exit code.
48
+ */
49
+ export function runInstall(binary, args) {
50
+ return new Promise((resolve) => {
51
+ const child = spawn(binary, args, { stdio: "inherit" });
52
+ child.on("error", () => resolve(null));
53
+ child.on("close", (code) => resolve(code));
54
+ });
55
+ }
56
+ //# sourceMappingURL=install-claude.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install-claude.js","sourceRoot":"","sources":["../../src/commands/install-claude.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AAUpD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB;IACrC,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAClC,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,CAAC;QACjC,IAAI,IAAI,EAAE,CAAC;YACT,OAAO;gBACL,QAAQ,EAAE,QAAQ;gBAClB,WAAW,EAAE;oBACX,MAAM,EAAE,IAAI;oBACZ,IAAI,EAAE,CAAC,SAAS,EAAE,4BAA4B,CAAC;oBAC/C,KAAK,EAAE,yCAAyC;iBACjD;gBACD,aAAa,EACX,0EAA0E;aAC7E,CAAC;QACJ,CAAC;QACD,OAAO;YACL,QAAQ,EAAE,QAAQ;YAClB,aAAa,EACX,sFAAsF;SACzF,CAAC;IACJ,CAAC;IAED,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACjC,OAAO;YACL,QAAQ,EAAE,OAAO;YACjB,aAAa,EACX,oEAAoE;gBACpE,8BAA8B;SACjC,CAAC;IACJ,CAAC;IAED,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACjC,OAAO;YACL,QAAQ,EAAE,OAAO;YACjB,aAAa,EACX,mFAAmF;SACtF,CAAC;IACJ,CAAC;IAED,OAAO;QACL,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,aAAa,EACX,sEAAsE;KACzE,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU,CACxB,MAAc,EACd,IAAc;IAEd,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;QACxD,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QACvC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,5 @@
1
+ export interface McpCommandOptions {
2
+ configDir?: string;
3
+ baseUrl?: string;
4
+ }
5
+ export declare function runMcpCommand(opts?: McpCommandOptions): Promise<void>;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * `seanpropapp mcp` subcommand: launch the stdio MCP server.
3
+ *
4
+ * v0.1.0-alpha: minimal feature parity with the existing `@seanpropapp/mcp`
5
+ * package. The full migration (streaming + caching + telemetry) is the v1.4.0
6
+ * deliverable tracked in proposition-app#341.
7
+ */
8
+ import { runMcpServer } from "../mcp/server.js";
9
+ export async function runMcpCommand(opts = {}) {
10
+ const serverOpts = {};
11
+ if (opts.configDir !== undefined)
12
+ serverOpts.configDir = opts.configDir;
13
+ if (opts.baseUrl !== undefined)
14
+ serverOpts.baseUrl = opts.baseUrl;
15
+ try {
16
+ await runMcpServer(serverOpts);
17
+ }
18
+ catch (err) {
19
+ process.stderr.write(`mcp: ${err instanceof Error ? err.message : String(err)}\n`);
20
+ process.exit(1);
21
+ }
22
+ }
23
+ //# sourceMappingURL=mcp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.js","sourceRoot":"","sources":["../../src/commands/mcp.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,YAAY,EAAyB,MAAM,kBAAkB,CAAC;AAOvE,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,OAA0B,EAAE;IAE5B,MAAM,UAAU,GAAqB,EAAE,CAAC;IACxC,IAAI,IAAI,CAAC,SAAS,KAAK,SAAS;QAAE,UAAU,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;IACxE,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS;QAAE,UAAU,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;IAClE,IAAI,CAAC;QACH,MAAM,YAAY,CAAC,UAAU,CAAC,CAAC;IACjC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,QAAQ,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAC7D,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC"}
@@ -0,0 +1,12 @@
1
+ export declare const PAIR_BASE_URL = "https://prop.seanoneill.com/pair";
2
+ /** Generate a 32-byte hex pair token (64 hex chars). */
3
+ export declare function generatePairToken(): string;
4
+ /** Build the pair URL with token in the URL fragment (#t=...). */
5
+ export declare function pairUrl(token: string): string;
6
+ /**
7
+ * Wrap a URL in an OSC 8 hyperlink escape so supporting terminals render it
8
+ * clickable. Non-supporting terminals fall back to plain text (the label).
9
+ *
10
+ * Sequence shape: ESC ] 8 ; ; URL ESC \ LABEL ESC ] 8 ; ; ESC \
11
+ */
12
+ export declare function osc8Link(url: string, label: string): string;
@@ -0,0 +1,23 @@
1
+ import { randomBytes } from "node:crypto";
2
+ export const PAIR_BASE_URL = "https://prop.seanoneill.com/pair";
3
+ /** Generate a 32-byte hex pair token (64 hex chars). */
4
+ export function generatePairToken() {
5
+ return randomBytes(32).toString("hex");
6
+ }
7
+ /** Build the pair URL with token in the URL fragment (#t=...). */
8
+ export function pairUrl(token) {
9
+ return `${PAIR_BASE_URL}#t=${token}`;
10
+ }
11
+ const ESC = String.fromCharCode(0x1b);
12
+ /**
13
+ * Wrap a URL in an OSC 8 hyperlink escape so supporting terminals render it
14
+ * clickable. Non-supporting terminals fall back to plain text (the label).
15
+ *
16
+ * Sequence shape: ESC ] 8 ; ; URL ESC \ LABEL ESC ] 8 ; ; ESC \
17
+ */
18
+ export function osc8Link(url, label) {
19
+ const OSC = `${ESC}]8;;`;
20
+ const ST = `${ESC}\\`;
21
+ return `${OSC}${url}${ST}${label}${OSC}${ST}`;
22
+ }
23
+ //# sourceMappingURL=pair-url.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pair-url.js","sourceRoot":"","sources":["../../src/commands/pair-url.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,CAAC,MAAM,aAAa,GAAG,kCAAkC,CAAC;AAEhE,wDAAwD;AACxD,MAAM,UAAU,iBAAiB;IAC/B,OAAO,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACzC,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,OAAO,CAAC,KAAa;IACnC,OAAO,GAAG,aAAa,MAAM,KAAK,EAAE,CAAC;AACvC,CAAC;AAED,MAAM,GAAG,GAAG,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;AAEtC;;;;;GAKG;AACH,MAAM,UAAU,QAAQ,CAAC,GAAW,EAAE,KAAa;IACjD,MAAM,GAAG,GAAG,GAAG,GAAG,MAAM,CAAC;IACzB,MAAM,EAAE,GAAG,GAAG,GAAG,IAAI,CAAC;IACtB,OAAO,GAAG,GAAG,GAAG,GAAG,GAAG,EAAE,GAAG,KAAK,GAAG,GAAG,GAAG,EAAE,EAAE,CAAC;AAChD,CAAC"}
@@ -0,0 +1,12 @@
1
+ export interface PairOptions {
2
+ configDir?: string;
3
+ }
4
+ /**
5
+ * Escape-hatch: generate a fresh pair URL without starting a bridge.
6
+ * Assumes the bridge is already running and will pick up the new token
7
+ * from the shared config on its next handshake.
8
+ *
9
+ * NOTE: in v1.4.0 the bridge holds tokens in-memory at start; a fully live
10
+ * rotation flow is a v1.4.x follow-up (see Lane C-Polish doctor).
11
+ */
12
+ export declare function runPair(opts?: PairOptions): Promise<void>;