@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.
- package/LICENSE +21 -0
- package/README.md +64 -0
- package/dist/commands/autostart.d.ts +42 -0
- package/dist/commands/autostart.js +195 -0
- package/dist/commands/autostart.js.map +1 -0
- package/dist/commands/bridge.d.ts +54 -0
- package/dist/commands/bridge.js +145 -0
- package/dist/commands/bridge.js.map +1 -0
- package/dist/commands/connect.d.ts +56 -0
- package/dist/commands/connect.js +213 -0
- package/dist/commands/connect.js.map +1 -0
- package/dist/commands/doctor.d.ts +24 -0
- package/dist/commands/doctor.js +200 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/install-claude.d.ts +22 -0
- package/dist/commands/install-claude.js +56 -0
- package/dist/commands/install-claude.js.map +1 -0
- package/dist/commands/mcp.d.ts +5 -0
- package/dist/commands/mcp.js +23 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/commands/pair-url.d.ts +12 -0
- package/dist/commands/pair-url.js +23 -0
- package/dist/commands/pair-url.js.map +1 -0
- package/dist/commands/pair.d.ts +12 -0
- package/dist/commands/pair.js +24 -0
- package/dist/commands/pair.js.map +1 -0
- package/dist/commands/prompt.d.ts +5 -0
- package/dist/commands/prompt.js +24 -0
- package/dist/commands/prompt.js.map +1 -0
- package/dist/commands/telemetry-cmd.d.ts +8 -0
- package/dist/commands/telemetry-cmd.js +55 -0
- package/dist/commands/telemetry-cmd.js.map +1 -0
- package/dist/config.d.ts +63 -0
- package/dist/config.js +77 -0
- package/dist/config.js.map +1 -0
- package/dist/http/auth-middleware.d.ts +9 -0
- package/dist/http/auth-middleware.js +29 -0
- package/dist/http/auth-middleware.js.map +1 -0
- package/dist/http/chat-completions.d.ts +48 -0
- package/dist/http/chat-completions.js +117 -0
- package/dist/http/chat-completions.js.map +1 -0
- package/dist/http/cors.d.ts +9 -0
- package/dist/http/cors.js +35 -0
- package/dist/http/cors.js.map +1 -0
- package/dist/http/handshake.d.ts +43 -0
- package/dist/http/handshake.js +28 -0
- package/dist/http/handshake.js.map +1 -0
- package/dist/http/messages-endpoint.d.ts +67 -0
- package/dist/http/messages-endpoint.js +95 -0
- package/dist/http/messages-endpoint.js.map +1 -0
- package/dist/http/server.d.ts +37 -0
- package/dist/http/server.js +83 -0
- package/dist/http/server.js.map +1 -0
- package/dist/http/sse.d.ts +35 -0
- package/dist/http/sse.js +72 -0
- package/dist/http/sse.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +219 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/manifest.d.ts +16 -0
- package/dist/mcp/manifest.js +88 -0
- package/dist/mcp/manifest.js.map +1 -0
- package/dist/mcp/server.d.ts +25 -0
- package/dist/mcp/server.js +166 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/providers/base.d.ts +91 -0
- package/dist/providers/base.js +27 -0
- package/dist/providers/base.js.map +1 -0
- package/dist/providers/claude.d.ts +22 -0
- package/dist/providers/claude.js +157 -0
- package/dist/providers/claude.js.map +1 -0
- package/dist/providers/codex.d.ts +38 -0
- package/dist/providers/codex.js +179 -0
- package/dist/providers/codex.js.map +1 -0
- package/dist/providers/detect-util.d.ts +17 -0
- package/dist/providers/detect-util.js +68 -0
- package/dist/providers/detect-util.js.map +1 -0
- package/dist/providers/index.d.ts +14 -0
- package/dist/providers/index.js +21 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/telemetry.d.ts +68 -0
- package/dist/telemetry.js +118 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/version.d.ts +9 -0
- package/dist/version.js +10 -0
- package/dist/version.js.map +1 -0
- package/package.json +62 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { ClassifiedError, } from "./base.js";
|
|
3
|
+
import { runCapture, which } from "./detect-util.js";
|
|
4
|
+
const DEFAULT_BINARY = "codex";
|
|
5
|
+
/**
|
|
6
|
+
* Translate Anthropic Messages → OpenAI Chat Completions shape.
|
|
7
|
+
* Exported so tests can verify the translation surface directly.
|
|
8
|
+
*/
|
|
9
|
+
export function anthropicToOpenAI(req) {
|
|
10
|
+
const messages = [];
|
|
11
|
+
if (req.system) {
|
|
12
|
+
messages.push({ role: "system", content: req.system });
|
|
13
|
+
}
|
|
14
|
+
for (const m of req.messages) {
|
|
15
|
+
let content;
|
|
16
|
+
if (typeof m.content === "string") {
|
|
17
|
+
content = m.content;
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
content = m.content
|
|
21
|
+
.filter((block) => block.type === "text" && typeof block.text === "string")
|
|
22
|
+
.map((block) => block.text ?? "")
|
|
23
|
+
.join("\n");
|
|
24
|
+
}
|
|
25
|
+
if (m.role === "system") {
|
|
26
|
+
messages.push({ role: "system", content });
|
|
27
|
+
}
|
|
28
|
+
else if (m.role === "assistant") {
|
|
29
|
+
messages.push({ role: "assistant", content });
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
messages.push({ role: "user", content });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const result = {
|
|
36
|
+
model: req.model,
|
|
37
|
+
messages,
|
|
38
|
+
stream: req.stream,
|
|
39
|
+
};
|
|
40
|
+
if (req.max_tokens !== undefined)
|
|
41
|
+
result.max_tokens = req.max_tokens;
|
|
42
|
+
if (req.temperature !== undefined)
|
|
43
|
+
result.temperature = req.temperature;
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
46
|
+
const RATE_LIMIT_PATTERNS = [
|
|
47
|
+
/rate.?limit/i,
|
|
48
|
+
/429/,
|
|
49
|
+
/too many requests/i,
|
|
50
|
+
/quota.?exceeded/i,
|
|
51
|
+
];
|
|
52
|
+
export function detectRateLimit(text) {
|
|
53
|
+
return RATE_LIMIT_PATTERNS.some((re) => re.test(text));
|
|
54
|
+
}
|
|
55
|
+
export function parseRetryAfter(text) {
|
|
56
|
+
const re = /retry.?after[:\s]+(\d+)/i;
|
|
57
|
+
const m = re.exec(text);
|
|
58
|
+
if (m && m[1]) {
|
|
59
|
+
const n = Number(m[1]);
|
|
60
|
+
if (Number.isFinite(n) && n >= 0)
|
|
61
|
+
return n;
|
|
62
|
+
}
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
export class CodexProvider {
|
|
66
|
+
name = "codex";
|
|
67
|
+
deps;
|
|
68
|
+
constructor(deps = {}) {
|
|
69
|
+
this.deps = {
|
|
70
|
+
whichFn: deps.whichFn ?? which,
|
|
71
|
+
runCaptureFn: deps.runCaptureFn ?? runCapture,
|
|
72
|
+
spawnFn: deps.spawnFn ?? spawn,
|
|
73
|
+
binaryName: deps.binaryName ?? DEFAULT_BINARY,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
async detect() {
|
|
77
|
+
const binary = await this.deps.whichFn(this.deps.binaryName);
|
|
78
|
+
if (!binary) {
|
|
79
|
+
return { installed: false, reason: "codex binary not found in PATH" };
|
|
80
|
+
}
|
|
81
|
+
// Codex CLI's version flag varies; try common shapes and fall back to "present".
|
|
82
|
+
const versionRun = await this.deps.runCaptureFn(binary, ["--version"], {
|
|
83
|
+
timeoutMs: 5000,
|
|
84
|
+
});
|
|
85
|
+
const version = versionRun.stdout.trim().split(/\s+/).pop() || versionRun.stderr.trim();
|
|
86
|
+
return { installed: true, binary, version: version || undefined };
|
|
87
|
+
}
|
|
88
|
+
async *stream(request, signal) {
|
|
89
|
+
const detected = await this.detect();
|
|
90
|
+
if (!detected.installed || !detected.binary) {
|
|
91
|
+
throw new ClassifiedError("Codex CLI not installed", {
|
|
92
|
+
category: "cli_missing",
|
|
93
|
+
provider: this.name,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
// Translate inbound Anthropic-shape into OpenAI-shape (consumed via stdin
|
|
97
|
+
// JSON so the spawned Codex CLI receives a self-describing payload).
|
|
98
|
+
const openaiReq = anthropicToOpenAI(request);
|
|
99
|
+
const stdinPayload = JSON.stringify(openaiReq);
|
|
100
|
+
// Codex CLI invocation: use --json-input so the CLI reads the OpenAI
|
|
101
|
+
// chat completions payload from stdin and streams plain text to stdout.
|
|
102
|
+
// The flag set here is the canonical contract we ship against; if Codex
|
|
103
|
+
// ships a different shape we adapt the args, not the wire format.
|
|
104
|
+
const args = ["--json-input", "--model", request.model];
|
|
105
|
+
const child = this.deps.spawnFn(detected.binary, args, {
|
|
106
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
107
|
+
});
|
|
108
|
+
const stderrChunks = [];
|
|
109
|
+
child.stderr?.on("data", (chunk) => {
|
|
110
|
+
stderrChunks.push(chunk.toString());
|
|
111
|
+
});
|
|
112
|
+
const abortHandler = () => {
|
|
113
|
+
child.kill("SIGTERM");
|
|
114
|
+
};
|
|
115
|
+
signal?.addEventListener("abort", abortHandler);
|
|
116
|
+
if (child.stdin) {
|
|
117
|
+
child.stdin.end(stdinPayload);
|
|
118
|
+
}
|
|
119
|
+
const messageId = `msg_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
120
|
+
yield { type: "message_start", message: { id: messageId, model: request.model } };
|
|
121
|
+
yield {
|
|
122
|
+
type: "content_block_start",
|
|
123
|
+
index: 0,
|
|
124
|
+
content_block: { type: "text", text: "" },
|
|
125
|
+
};
|
|
126
|
+
try {
|
|
127
|
+
let totalOut = "";
|
|
128
|
+
if (child.stdout) {
|
|
129
|
+
for await (const chunk of child.stdout) {
|
|
130
|
+
const text = typeof chunk === "string" ? chunk : chunk.toString();
|
|
131
|
+
if (text.length === 0)
|
|
132
|
+
continue;
|
|
133
|
+
totalOut += text;
|
|
134
|
+
if (detectRateLimit(text)) {
|
|
135
|
+
const retryAfter = parseRetryAfter(text);
|
|
136
|
+
throw new ClassifiedError("Subscription rate limit", {
|
|
137
|
+
category: "subscription_limit",
|
|
138
|
+
retryAfterSeconds: retryAfter,
|
|
139
|
+
provider: this.name,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
yield {
|
|
143
|
+
type: "content_block_delta",
|
|
144
|
+
index: 0,
|
|
145
|
+
delta: { type: "text_delta", text },
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const exitCode = await new Promise((resolve) => {
|
|
150
|
+
if (child.exitCode !== null)
|
|
151
|
+
return resolve(child.exitCode);
|
|
152
|
+
child.once("close", (code) => resolve(code));
|
|
153
|
+
});
|
|
154
|
+
const stderr = stderrChunks.join("");
|
|
155
|
+
if (exitCode !== 0) {
|
|
156
|
+
if (detectRateLimit(stderr) || detectRateLimit(totalOut)) {
|
|
157
|
+
const retryAfter = parseRetryAfter(stderr) ?? parseRetryAfter(totalOut);
|
|
158
|
+
throw new ClassifiedError("Subscription rate limit", {
|
|
159
|
+
category: "subscription_limit",
|
|
160
|
+
retryAfterSeconds: retryAfter,
|
|
161
|
+
provider: this.name,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
throw new ClassifiedError(`Codex CLI exited with code ${exitCode}: ${stderr.slice(0, 500)}`, { category: "cli_crashed", provider: this.name });
|
|
165
|
+
}
|
|
166
|
+
yield { type: "content_block_stop", index: 0 };
|
|
167
|
+
yield {
|
|
168
|
+
type: "message_delta",
|
|
169
|
+
delta: { stop_reason: "end_turn" },
|
|
170
|
+
usage: { output_tokens: 0 },
|
|
171
|
+
};
|
|
172
|
+
yield { type: "message_stop" };
|
|
173
|
+
}
|
|
174
|
+
finally {
|
|
175
|
+
signal?.removeEventListener("abort", abortHandler);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=codex.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex.js","sourceRoot":"","sources":["../../src/providers/codex.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-platform binary lookup. Returns absolute path or null.
|
|
3
|
+
* Uses POSIX `which` / Windows `where`.
|
|
4
|
+
*/
|
|
5
|
+
export declare function which(binary: string): Promise<string | null>;
|
|
6
|
+
export interface RunResult {
|
|
7
|
+
code: number | null;
|
|
8
|
+
stdout: string;
|
|
9
|
+
stderr: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Run a binary with args, capturing stdout/stderr. Never throws.
|
|
13
|
+
*/
|
|
14
|
+
export declare function runCapture(binary: string, args: string[], opts?: {
|
|
15
|
+
input?: string;
|
|
16
|
+
timeoutMs?: number;
|
|
17
|
+
}): Promise<RunResult>;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
/**
|
|
3
|
+
* Cross-platform binary lookup. Returns absolute path or null.
|
|
4
|
+
* Uses POSIX `which` / Windows `where`.
|
|
5
|
+
*/
|
|
6
|
+
export async function which(binary) {
|
|
7
|
+
const cmd = process.platform === "win32" ? "where" : "which";
|
|
8
|
+
return new Promise((resolve) => {
|
|
9
|
+
const child = spawn(cmd, [binary], { stdio: ["ignore", "pipe", "ignore"] });
|
|
10
|
+
let out = "";
|
|
11
|
+
child.stdout.on("data", (chunk) => {
|
|
12
|
+
out += chunk.toString();
|
|
13
|
+
});
|
|
14
|
+
child.on("error", () => resolve(null));
|
|
15
|
+
child.on("close", (code) => {
|
|
16
|
+
if (code !== 0)
|
|
17
|
+
return resolve(null);
|
|
18
|
+
const first = out.split(/\r?\n/).find((line) => line.trim().length > 0);
|
|
19
|
+
resolve(first?.trim() ?? null);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Run a binary with args, capturing stdout/stderr. Never throws.
|
|
25
|
+
*/
|
|
26
|
+
export async function runCapture(binary, args, opts = {}) {
|
|
27
|
+
return new Promise((resolve) => {
|
|
28
|
+
let child;
|
|
29
|
+
try {
|
|
30
|
+
child = spawn(binary, args, {
|
|
31
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
resolve({ code: null, stdout: "", stderr: String(err) });
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
let stdout = "";
|
|
39
|
+
let stderr = "";
|
|
40
|
+
child.stdout.on("data", (chunk) => {
|
|
41
|
+
stdout += chunk.toString();
|
|
42
|
+
});
|
|
43
|
+
child.stderr.on("data", (chunk) => {
|
|
44
|
+
stderr += chunk.toString();
|
|
45
|
+
});
|
|
46
|
+
if (opts.input)
|
|
47
|
+
child.stdin.end(opts.input);
|
|
48
|
+
else
|
|
49
|
+
child.stdin.end();
|
|
50
|
+
let timer;
|
|
51
|
+
if (opts.timeoutMs) {
|
|
52
|
+
timer = setTimeout(() => {
|
|
53
|
+
child.kill("SIGTERM");
|
|
54
|
+
}, opts.timeoutMs);
|
|
55
|
+
}
|
|
56
|
+
child.on("error", (err) => {
|
|
57
|
+
if (timer)
|
|
58
|
+
clearTimeout(timer);
|
|
59
|
+
resolve({ code: null, stdout, stderr: stderr || String(err) });
|
|
60
|
+
});
|
|
61
|
+
child.on("close", (code) => {
|
|
62
|
+
if (timer)
|
|
63
|
+
clearTimeout(timer);
|
|
64
|
+
resolve({ code, stdout, stderr });
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=detect-util.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect-util.js","sourceRoot":"","sources":["../../src/providers/detect-util.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ProviderDetectResult } from "./base.js";
|
|
2
|
+
export * from "./base.js";
|
|
3
|
+
export { ClaudeProvider } from "./claude.js";
|
|
4
|
+
export { CodexProvider } from "./codex.js";
|
|
5
|
+
export interface AllProvidersDetected {
|
|
6
|
+
claude: ProviderDetectResult;
|
|
7
|
+
codex: ProviderDetectResult;
|
|
8
|
+
gemini: ProviderDetectResult;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Detect every provider concurrently. Gemini is a known-stub for v1.4.0;
|
|
12
|
+
* fast-follow in v1.4.x.
|
|
13
|
+
*/
|
|
14
|
+
export declare function detectAllProviders(): Promise<AllProvidersDetected>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ClaudeProvider } from "./claude.js";
|
|
2
|
+
import { CodexProvider } from "./codex.js";
|
|
3
|
+
export * from "./base.js";
|
|
4
|
+
export { ClaudeProvider } from "./claude.js";
|
|
5
|
+
export { CodexProvider } from "./codex.js";
|
|
6
|
+
/**
|
|
7
|
+
* Detect every provider concurrently. Gemini is a known-stub for v1.4.0;
|
|
8
|
+
* fast-follow in v1.4.x.
|
|
9
|
+
*/
|
|
10
|
+
export async function detectAllProviders() {
|
|
11
|
+
const [claude, codex] = await Promise.all([
|
|
12
|
+
new ClaudeProvider().detect(),
|
|
13
|
+
new CodexProvider().detect(),
|
|
14
|
+
]);
|
|
15
|
+
return {
|
|
16
|
+
claude,
|
|
17
|
+
codex,
|
|
18
|
+
gemini: { installed: false, reason: "not yet supported" },
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/providers/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,cAAc,WAAW,CAAC;AAC1B,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAQ3C;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB;IACtC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACxC,IAAI,cAAc,EAAE,CAAC,MAAM,EAAE;QAC7B,IAAI,aAAa,EAAE,CAAC,MAAM,EAAE;KAC7B,CAAC,CAAC;IACH,OAAO;QACL,MAAM;QACN,KAAK;QACL,MAAM,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,mBAAmB,EAAE;KAC1D,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export type CliTelemetryEvent = "cli_connect_start" | "cli_pair_complete" | "cli_first_analysis_complete";
|
|
2
|
+
export interface TelemetryContext {
|
|
3
|
+
configDir?: string;
|
|
4
|
+
/** Override the receiving URL (used by tests + dev). */
|
|
5
|
+
url?: string;
|
|
6
|
+
/** Override fetch (used by tests). */
|
|
7
|
+
fetchImpl?: typeof fetch;
|
|
8
|
+
/** Force disable for a single emit (used by --no-telemetry). */
|
|
9
|
+
forceDisable?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface TelemetryPayload {
|
|
12
|
+
event: CliTelemetryEvent;
|
|
13
|
+
correlation_id: string;
|
|
14
|
+
cli_version: string;
|
|
15
|
+
os: string;
|
|
16
|
+
node: string;
|
|
17
|
+
ts: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Returns the URL the telemetry events POST to, honoring env override.
|
|
21
|
+
*/
|
|
22
|
+
export declare function telemetryUrl(override?: string): string;
|
|
23
|
+
/**
|
|
24
|
+
* Returns true if telemetry should fire on this invocation.
|
|
25
|
+
* Order: --no-telemetry > env > config.
|
|
26
|
+
*/
|
|
27
|
+
export declare function isTelemetryEnabled(ctx?: TelemetryContext): Promise<boolean>;
|
|
28
|
+
/**
|
|
29
|
+
* Loads or generates a stable correlation id and persists it to config.
|
|
30
|
+
* Even with telemetry disabled, the id is generated lazily on first opt-in.
|
|
31
|
+
*/
|
|
32
|
+
export declare function ensureCorrelationId(ctx?: TelemetryContext): Promise<string>;
|
|
33
|
+
/**
|
|
34
|
+
* Build the JSON payload. Pure; no I/O.
|
|
35
|
+
*/
|
|
36
|
+
export declare function buildPayload(event: CliTelemetryEvent, correlationId: string): TelemetryPayload;
|
|
37
|
+
/**
|
|
38
|
+
* Emit a CLI telemetry event. No-op when telemetry is disabled. Network
|
|
39
|
+
* failures are swallowed; the caller never blocks.
|
|
40
|
+
*/
|
|
41
|
+
export declare function emit(event: CliTelemetryEvent, ctx?: TelemetryContext): Promise<{
|
|
42
|
+
sent: boolean;
|
|
43
|
+
reason?: string;
|
|
44
|
+
}>;
|
|
45
|
+
/**
|
|
46
|
+
* Public wrappers so call sites read cleanly.
|
|
47
|
+
*/
|
|
48
|
+
export declare function emitConnectStart(ctx?: TelemetryContext): Promise<{
|
|
49
|
+
sent: boolean;
|
|
50
|
+
reason?: string;
|
|
51
|
+
}>;
|
|
52
|
+
export declare function emitPairComplete(ctx?: TelemetryContext): Promise<{
|
|
53
|
+
sent: boolean;
|
|
54
|
+
reason?: string;
|
|
55
|
+
}>;
|
|
56
|
+
export declare function emitFirstAnalysisComplete(ctx?: TelemetryContext): Promise<{
|
|
57
|
+
sent: boolean;
|
|
58
|
+
reason?: string;
|
|
59
|
+
}>;
|
|
60
|
+
/**
|
|
61
|
+
* Returns a short human-readable summary used by `seanpropapp telemetry status`.
|
|
62
|
+
*/
|
|
63
|
+
export declare function telemetryStatus(ctx?: TelemetryContext): Promise<{
|
|
64
|
+
enabled: boolean;
|
|
65
|
+
correlation_id: string | undefined;
|
|
66
|
+
url: string;
|
|
67
|
+
os: string;
|
|
68
|
+
}>;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Opt-in CLI telemetry for the three TTHW events (TX7).
|
|
3
|
+
*
|
|
4
|
+
* Defaults to OFF: every emit returns immediately unless the user opts in
|
|
5
|
+
* via `seanpropapp telemetry enable` (which sets `telemetry_enabled=true`
|
|
6
|
+
* in config) or unless the SEANPROPAPP_TELEMETRY env var is set to "1".
|
|
7
|
+
*
|
|
8
|
+
* Network failures are swallowed silently. Telemetry must never block the
|
|
9
|
+
* core flow (connect/pair/bridge).
|
|
10
|
+
*/
|
|
11
|
+
import { randomUUID } from "node:crypto";
|
|
12
|
+
import os from "node:os";
|
|
13
|
+
import { loadConfig, updateConfig } from "./config.js";
|
|
14
|
+
import { CLI_VERSION } from "./version.js";
|
|
15
|
+
const DEFAULT_URL = "https://prop.seanoneill.com/api/telemetry";
|
|
16
|
+
/**
|
|
17
|
+
* Returns the URL the telemetry events POST to, honoring env override.
|
|
18
|
+
*/
|
|
19
|
+
export function telemetryUrl(override) {
|
|
20
|
+
if (override)
|
|
21
|
+
return override;
|
|
22
|
+
const env = process.env["SEANPROPAPP_TELEMETRY_URL"];
|
|
23
|
+
if (env)
|
|
24
|
+
return env;
|
|
25
|
+
return DEFAULT_URL;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Returns true if telemetry should fire on this invocation.
|
|
29
|
+
* Order: --no-telemetry > env > config.
|
|
30
|
+
*/
|
|
31
|
+
export async function isTelemetryEnabled(ctx = {}) {
|
|
32
|
+
if (ctx.forceDisable)
|
|
33
|
+
return false;
|
|
34
|
+
const env = process.env["SEANPROPAPP_TELEMETRY"];
|
|
35
|
+
if (env === "0" || env?.toLowerCase() === "off")
|
|
36
|
+
return false;
|
|
37
|
+
const cfg = await loadConfig(ctx.configDir);
|
|
38
|
+
return cfg.telemetry_enabled === true;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Loads or generates a stable correlation id and persists it to config.
|
|
42
|
+
* Even with telemetry disabled, the id is generated lazily on first opt-in.
|
|
43
|
+
*/
|
|
44
|
+
export async function ensureCorrelationId(ctx = {}) {
|
|
45
|
+
const cfg = await loadConfig(ctx.configDir);
|
|
46
|
+
if (cfg.correlation_id)
|
|
47
|
+
return cfg.correlation_id;
|
|
48
|
+
const id = randomUUID();
|
|
49
|
+
await updateConfig({ correlation_id: id }, ctx.configDir);
|
|
50
|
+
return id;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Build the JSON payload. Pure; no I/O.
|
|
54
|
+
*/
|
|
55
|
+
export function buildPayload(event, correlationId) {
|
|
56
|
+
return {
|
|
57
|
+
event,
|
|
58
|
+
correlation_id: correlationId,
|
|
59
|
+
cli_version: CLI_VERSION,
|
|
60
|
+
os: `${process.platform}/${process.arch}`,
|
|
61
|
+
node: process.version,
|
|
62
|
+
ts: new Date().toISOString(),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Emit a CLI telemetry event. No-op when telemetry is disabled. Network
|
|
67
|
+
* failures are swallowed; the caller never blocks.
|
|
68
|
+
*/
|
|
69
|
+
export async function emit(event, ctx = {}) {
|
|
70
|
+
if (!(await isTelemetryEnabled(ctx))) {
|
|
71
|
+
return { sent: false, reason: "telemetry_disabled" };
|
|
72
|
+
}
|
|
73
|
+
const correlationId = await ensureCorrelationId(ctx);
|
|
74
|
+
const payload = buildPayload(event, correlationId);
|
|
75
|
+
const url = telemetryUrl(ctx.url);
|
|
76
|
+
const fetchImpl = ctx.fetchImpl ?? fetch;
|
|
77
|
+
try {
|
|
78
|
+
const res = await fetchImpl(url, {
|
|
79
|
+
method: "POST",
|
|
80
|
+
headers: { "Content-Type": "application/json" },
|
|
81
|
+
body: JSON.stringify(payload),
|
|
82
|
+
});
|
|
83
|
+
if (!res.ok)
|
|
84
|
+
return { sent: false, reason: `http_${res.status}` };
|
|
85
|
+
return { sent: true };
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
return {
|
|
89
|
+
sent: false,
|
|
90
|
+
reason: `network: ${err instanceof Error ? err.message : String(err)}`,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Public wrappers so call sites read cleanly.
|
|
96
|
+
*/
|
|
97
|
+
export function emitConnectStart(ctx = {}) {
|
|
98
|
+
return emit("cli_connect_start", ctx);
|
|
99
|
+
}
|
|
100
|
+
export function emitPairComplete(ctx = {}) {
|
|
101
|
+
return emit("cli_pair_complete", ctx);
|
|
102
|
+
}
|
|
103
|
+
export function emitFirstAnalysisComplete(ctx = {}) {
|
|
104
|
+
return emit("cli_first_analysis_complete", ctx);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Returns a short human-readable summary used by `seanpropapp telemetry status`.
|
|
108
|
+
*/
|
|
109
|
+
export async function telemetryStatus(ctx = {}) {
|
|
110
|
+
const cfg = await loadConfig(ctx.configDir);
|
|
111
|
+
return {
|
|
112
|
+
enabled: cfg.telemetry_enabled === true,
|
|
113
|
+
correlation_id: cfg.correlation_id,
|
|
114
|
+
url: telemetryUrl(ctx.url),
|
|
115
|
+
os: `${process.platform}/${process.arch}/${os.release()}`,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=telemetry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telemetry.js","sourceRoot":"","sources":["../src/telemetry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of truth for the CLI's runtime version string.
|
|
3
|
+
*
|
|
4
|
+
* Kept in sync with `package.json` `version`. We don't dynamically import the
|
|
5
|
+
* package.json because the bundler-style tsconfig + Node ESM combo makes JSON
|
|
6
|
+
* imports awkward, and a hand-bumped constant is fine for a CLI whose version
|
|
7
|
+
* is also pinned by npm install.
|
|
8
|
+
*/
|
|
9
|
+
export declare const CLI_VERSION = "0.1.0-beta.1";
|
package/dist/version.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of truth for the CLI's runtime version string.
|
|
3
|
+
*
|
|
4
|
+
* Kept in sync with `package.json` `version`. We don't dynamically import the
|
|
5
|
+
* package.json because the bundler-style tsconfig + Node ESM combo makes JSON
|
|
6
|
+
* imports awkward, and a hand-bumped constant is fine for a CLI whose version
|
|
7
|
+
* is also pinned by npm install.
|
|
8
|
+
*/
|
|
9
|
+
export const CLI_VERSION = "0.1.0-beta.1";
|
|
10
|
+
//# sourceMappingURL=version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,cAAc,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@seanpropapp/cli",
|
|
3
|
+
"version": "0.1.0-beta.1",
|
|
4
|
+
"description": "Run SeanPropApp proposition analyses on your existing Claude Pro or ChatGPT Plus subscription via a local bridge.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "Sean O'Neill",
|
|
8
|
+
"homepage": "https://prop.seanoneill.com",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/seanomich/seanpropapp-cli.git"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/seanomich/seanpropapp-cli/issues"
|
|
15
|
+
},
|
|
16
|
+
"bin": {
|
|
17
|
+
"seanpropapp": "./dist/index.js"
|
|
18
|
+
},
|
|
19
|
+
"main": "./dist/index.js",
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"README.md",
|
|
23
|
+
"LICENSE"
|
|
24
|
+
],
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public",
|
|
27
|
+
"provenance": true
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsc",
|
|
31
|
+
"test": "vitest run",
|
|
32
|
+
"test:watch": "vitest",
|
|
33
|
+
"lint": "tsc --noEmit",
|
|
34
|
+
"prepublishOnly": "npm run lint && npm test && npm run build"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@hono/node-server": "^2.0.4",
|
|
38
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
39
|
+
"commander": "^12.1.0",
|
|
40
|
+
"hono": "^4.6.0",
|
|
41
|
+
"open": "^10.1.0",
|
|
42
|
+
"zod": "^3.23.8"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@types/node": "^20.14.0",
|
|
46
|
+
"typescript": "^5.5.0",
|
|
47
|
+
"vitest": "^4.0.0"
|
|
48
|
+
},
|
|
49
|
+
"engines": {
|
|
50
|
+
"node": ">=18"
|
|
51
|
+
},
|
|
52
|
+
"keywords": [
|
|
53
|
+
"seanpropapp",
|
|
54
|
+
"cli",
|
|
55
|
+
"claude",
|
|
56
|
+
"codex",
|
|
57
|
+
"bridge",
|
|
58
|
+
"anthropic",
|
|
59
|
+
"openai",
|
|
60
|
+
"mcp"
|
|
61
|
+
]
|
|
62
|
+
}
|