@jonaspauleta/cursor-bridge 0.2.0
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 +140 -0
- package/dist/classify.d.ts +8 -0
- package/dist/classify.js +62 -0
- package/dist/classify.js.map +1 -0
- package/dist/codex.classify.d.ts +15 -0
- package/dist/codex.classify.js +34 -0
- package/dist/codex.classify.js.map +1 -0
- package/dist/codex.d.ts +20 -0
- package/dist/codex.js +158 -0
- package/dist/codex.js.map +1 -0
- package/dist/codex.preflight.d.ts +14 -0
- package/dist/codex.preflight.js +27 -0
- package/dist/codex.preflight.js.map +1 -0
- package/dist/cursor.d.ts +17 -0
- package/dist/cursor.js +88 -0
- package/dist/cursor.js.map +1 -0
- package/dist/errors.d.ts +36 -0
- package/dist/errors.js +8 -0
- package/dist/errors.js.map +1 -0
- package/dist/imagegen.tool.d.ts +57 -0
- package/dist/imagegen.tool.js +79 -0
- package/dist/imagegen.tool.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +16 -0
- package/dist/logger.js +27 -0
- package/dist/logger.js.map +1 -0
- package/dist/preflight.d.ts +11 -0
- package/dist/preflight.js +35 -0
- package/dist/preflight.js.map +1 -0
- package/dist/runner.d.ts +25 -0
- package/dist/runner.js +35 -0
- package/dist/runner.js.map +1 -0
- package/dist/server.d.ts +5 -0
- package/dist/server.js +19 -0
- package/dist/server.js.map +1 -0
- package/dist/tool.d.ts +38 -0
- package/dist/tool.js +41 -0
- package/dist/tool.js.map +1 -0
- package/dist/validate.d.ts +23 -0
- package/dist/validate.js +112 -0
- package/dist/validate.js.map +1 -0
- package/package.json +50 -0
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** The complete, closed set of outcomes for a cursor-agent invocation. */
|
|
2
|
+
export type CursorStatus = "SUCCESS" | "AUTH_REQUIRED" | "TIMEOUT" | "TOOL_ERROR" | "MALFORMED";
|
|
3
|
+
export interface CursorUsage {
|
|
4
|
+
inputTokens: number;
|
|
5
|
+
outputTokens: number;
|
|
6
|
+
cacheReadTokens: number;
|
|
7
|
+
cacheWriteTokens: number;
|
|
8
|
+
}
|
|
9
|
+
export interface CursorResult {
|
|
10
|
+
status: CursorStatus;
|
|
11
|
+
result?: string;
|
|
12
|
+
usage?: CursorUsage;
|
|
13
|
+
sessionId?: string;
|
|
14
|
+
requestId?: string;
|
|
15
|
+
error?: string;
|
|
16
|
+
/** ANSI-stripped captured output, for logging/debugging only — never returned to the model. */
|
|
17
|
+
raw?: string;
|
|
18
|
+
}
|
|
19
|
+
/** Thrown by validators in validate.ts. Caught at the tool boundary -> TOOL_ERROR. */
|
|
20
|
+
export declare class ValidationError extends Error {
|
|
21
|
+
constructor(message: string);
|
|
22
|
+
}
|
|
23
|
+
/** The closed set of outcomes for a generate_image invocation. */
|
|
24
|
+
export type ImageStatus = "SUCCESS" | "NO_IMAGE" | "AUTH_REQUIRED" | "TIMEOUT" | "TOOL_ERROR";
|
|
25
|
+
export interface ImageResult {
|
|
26
|
+
status: ImageStatus;
|
|
27
|
+
/** Absolute paths to generated image files. */
|
|
28
|
+
images?: string[];
|
|
29
|
+
/** Codex's final message (from --output-last-message). */
|
|
30
|
+
summary?: string;
|
|
31
|
+
count?: number;
|
|
32
|
+
sessionId?: string;
|
|
33
|
+
error?: string;
|
|
34
|
+
/** ANSI-stripped captured output, for logging/debugging only — never returned to the model. */
|
|
35
|
+
raw?: string;
|
|
36
|
+
}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AA0BA,sFAAsF;AACtF,MAAM,OAAO,eAAgB,SAAQ,KAAK;IACxC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAChC,CAAC;CACF"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { type GenerateImageOptions } from "./codex.js";
|
|
3
|
+
import type { ImageResult } from "./errors.js";
|
|
4
|
+
export declare const TOOL_NAME = "generate_image";
|
|
5
|
+
export declare const inputSchema: {
|
|
6
|
+
prompt: z.ZodString;
|
|
7
|
+
outputDir: z.ZodOptional<z.ZodString>;
|
|
8
|
+
referenceImages: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
9
|
+
count: z.ZodDefault<z.ZodNumber>;
|
|
10
|
+
size: z.ZodDefault<z.ZodEnum<{
|
|
11
|
+
auto: "auto";
|
|
12
|
+
"1024x1024": "1024x1024";
|
|
13
|
+
"1536x1024": "1536x1024";
|
|
14
|
+
"1024x1536": "1024x1536";
|
|
15
|
+
}>>;
|
|
16
|
+
quality: z.ZodDefault<z.ZodEnum<{
|
|
17
|
+
auto: "auto";
|
|
18
|
+
low: "low";
|
|
19
|
+
medium: "medium";
|
|
20
|
+
high: "high";
|
|
21
|
+
}>>;
|
|
22
|
+
inlineImages: z.ZodDefault<z.ZodBoolean>;
|
|
23
|
+
timeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
24
|
+
};
|
|
25
|
+
export interface ImageToolArgs {
|
|
26
|
+
prompt: string;
|
|
27
|
+
outputDir?: string;
|
|
28
|
+
referenceImages?: string[];
|
|
29
|
+
count?: number;
|
|
30
|
+
size?: "auto" | "1024x1024" | "1536x1024" | "1024x1536";
|
|
31
|
+
quality?: "auto" | "low" | "medium" | "high";
|
|
32
|
+
inlineImages?: boolean;
|
|
33
|
+
timeoutMs?: number;
|
|
34
|
+
}
|
|
35
|
+
export type ImageGenRunner = (prompt: string, opts: GenerateImageOptions) => Promise<ImageResult>;
|
|
36
|
+
type ContentBlock = {
|
|
37
|
+
type: "text";
|
|
38
|
+
text: string;
|
|
39
|
+
} | {
|
|
40
|
+
type: "image";
|
|
41
|
+
data: string;
|
|
42
|
+
mimeType: string;
|
|
43
|
+
};
|
|
44
|
+
export interface ImageToolResult {
|
|
45
|
+
[x: string]: unknown;
|
|
46
|
+
content: ContentBlock[];
|
|
47
|
+
isError?: boolean;
|
|
48
|
+
structuredContent?: {
|
|
49
|
+
status: string;
|
|
50
|
+
images?: string[];
|
|
51
|
+
summary?: string;
|
|
52
|
+
count?: number;
|
|
53
|
+
sessionId?: string;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
export declare function makeImageGenHandler(gen?: ImageGenRunner): (args: ImageToolArgs) => Promise<ImageToolResult>;
|
|
57
|
+
export {};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { extname } from "node:path";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { generateImage } from "./codex.js";
|
|
5
|
+
export const TOOL_NAME = "generate_image";
|
|
6
|
+
export const inputSchema = {
|
|
7
|
+
prompt: z.string().min(1).describe("The image task. Free-form; may include edit/iterate instructions."),
|
|
8
|
+
outputDir: z
|
|
9
|
+
.string()
|
|
10
|
+
.optional()
|
|
11
|
+
.describe("Absolute dir for generated images (validated inside the allowed root). Defaults to <allowedRoot>/generated-images."),
|
|
12
|
+
referenceImages: z
|
|
13
|
+
.array(z.string())
|
|
14
|
+
.max(8)
|
|
15
|
+
.optional()
|
|
16
|
+
.describe("Absolute paths to reference images (png/jpg/jpeg/webp, inside the allowed root) for editing/iteration. Max 8."),
|
|
17
|
+
count: z.number().int().min(1).max(10).default(1).describe("How many images to generate (1-10)."),
|
|
18
|
+
size: z.enum(["auto", "1024x1024", "1536x1024", "1024x1536"]).default("auto").describe("Size hint (best-effort directive)."),
|
|
19
|
+
quality: z.enum(["auto", "low", "medium", "high"]).default("auto").describe("Quality hint (best-effort directive)."),
|
|
20
|
+
inlineImages: z.boolean().default(false).describe("Also return generated images as base64 content blocks (first 4)."),
|
|
21
|
+
timeoutMs: z.number().int().positive().default(300_000).describe("Wall-clock timeout before the wrapper kills codex."),
|
|
22
|
+
};
|
|
23
|
+
const MIME = {
|
|
24
|
+
".png": "image/png",
|
|
25
|
+
".jpg": "image/jpeg",
|
|
26
|
+
".jpeg": "image/jpeg",
|
|
27
|
+
".webp": "image/webp",
|
|
28
|
+
};
|
|
29
|
+
const MAX_INLINE = 4;
|
|
30
|
+
function toImageBlock(path) {
|
|
31
|
+
try {
|
|
32
|
+
const data = readFileSync(path).toString("base64");
|
|
33
|
+
const mimeType = MIME[extname(path).toLowerCase()] ?? "image/png";
|
|
34
|
+
return { type: "image", data, mimeType };
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export function makeImageGenHandler(gen = generateImage) {
|
|
41
|
+
return async (args) => {
|
|
42
|
+
let r;
|
|
43
|
+
try {
|
|
44
|
+
r = await gen(args.prompt, {
|
|
45
|
+
outputDir: args.outputDir,
|
|
46
|
+
referenceImages: args.referenceImages,
|
|
47
|
+
count: args.count,
|
|
48
|
+
size: args.size,
|
|
49
|
+
quality: args.quality,
|
|
50
|
+
timeoutMs: args.timeoutMs,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
catch (e) {
|
|
54
|
+
// Never let an exception escape, and never echo the prompt body.
|
|
55
|
+
return { content: [{ type: "text", text: `TOOL_ERROR: ${e.message}` }], isError: true };
|
|
56
|
+
}
|
|
57
|
+
if (r.status === "SUCCESS") {
|
|
58
|
+
const paths = r.images ?? [];
|
|
59
|
+
const text = `${(r.summary ?? "Generated image(s)").trim()}\n\nFiles:\n${paths.map((p) => `- ${p}`).join("\n")}`;
|
|
60
|
+
const content = [{ type: "text", text }];
|
|
61
|
+
if (args.inlineImages) {
|
|
62
|
+
for (const p of paths.slice(0, MAX_INLINE)) {
|
|
63
|
+
const block = toImageBlock(p);
|
|
64
|
+
if (block)
|
|
65
|
+
content.push(block);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
content,
|
|
70
|
+
structuredContent: { status: r.status, images: paths, summary: r.summary, count: r.count, sessionId: r.sessionId },
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
content: [{ type: "text", text: `${r.status}: ${r.error ?? "no image generated"}` }],
|
|
75
|
+
isError: true,
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=imagegen.tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"imagegen.tool.js","sourceRoot":"","sources":["../src/imagegen.tool.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { realpathSync } from "node:fs";
|
|
3
|
+
import { isAbsolute } from "node:path";
|
|
4
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
import { createServer } from "./server.js";
|
|
6
|
+
/** Fail fast (and audibly) if the workspace allow-root is missing/relative/nonexistent. */
|
|
7
|
+
function resolveAllowedRoot() {
|
|
8
|
+
const root = process.env.CURSOR_BRIDGE_ALLOWED_ROOT;
|
|
9
|
+
if (!root || !isAbsolute(root)) {
|
|
10
|
+
throw new Error("CURSOR_BRIDGE_ALLOWED_ROOT must be set to an ABSOLUTE path");
|
|
11
|
+
}
|
|
12
|
+
return realpathSync(root); // throws if it doesn't exist
|
|
13
|
+
}
|
|
14
|
+
async function main() {
|
|
15
|
+
const root = resolveAllowedRoot();
|
|
16
|
+
console.error(`cursor-bridge: allowed workspace root = ${root}`); // stderr audit line
|
|
17
|
+
const server = createServer();
|
|
18
|
+
const transport = new StdioServerTransport();
|
|
19
|
+
await server.connect(transport);
|
|
20
|
+
}
|
|
21
|
+
main().catch((err) => {
|
|
22
|
+
// stderr only — stdout is the JSON-RPC channel
|
|
23
|
+
console.error("cursor-bridge fatal:", err);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
});
|
|
26
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,2FAA2F;AAC3F,SAAS,kBAAkB;IACzB,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC;IACpD,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,4DAA4D,CAAC,CAAC;IAChF,CAAC;IACD,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,6BAA6B;AAC1D,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,IAAI,GAAG,kBAAkB,EAAE,CAAC;IAClC,OAAO,CAAC,KAAK,CAAC,2CAA2C,IAAI,EAAE,CAAC,CAAC,CAAC,oBAAoB;IACtF,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;IAC9B,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,+CAA+C;IAC/C,OAAO,CAAC,KAAK,CAAC,sBAAsB,EAAE,GAAG,CAAC,CAAC;IAC3C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
package/dist/logger.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type LogLevel = "info" | "warn" | "error";
|
|
2
|
+
export interface LogFields {
|
|
3
|
+
event: string;
|
|
4
|
+
level?: LogLevel;
|
|
5
|
+
status?: string;
|
|
6
|
+
durationMs?: number;
|
|
7
|
+
sessionId?: string;
|
|
8
|
+
requestId?: string;
|
|
9
|
+
model?: string;
|
|
10
|
+
mode?: string;
|
|
11
|
+
tokens?: Record<string, number>;
|
|
12
|
+
[k: string]: unknown;
|
|
13
|
+
}
|
|
14
|
+
export type Sink = (line: string) => void;
|
|
15
|
+
/** Default sink writes to STDERR only — stdout is the JSON-RPC channel. */
|
|
16
|
+
export declare function createLogger(sink?: Sink): (fields: LogFields) => void;
|
package/dist/logger.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const SECRET_RE = /\b(sk-[A-Za-z0-9._-]{6,}|key[_-]?[A-Za-z0-9]{8,})\b/gi;
|
|
2
|
+
const REDACTED = "[REDACTED]";
|
|
3
|
+
/** Fields we never emit verbatim: prompt bodies, raw captured output, secrets/env. */
|
|
4
|
+
const DROP_FIELDS = new Set(["prompt", "raw", "apiKey", "CURSOR_API_KEY", "env"]);
|
|
5
|
+
function redact(v) {
|
|
6
|
+
if (typeof v === "string")
|
|
7
|
+
return v.replace(SECRET_RE, REDACTED);
|
|
8
|
+
if (v && typeof v === "object") {
|
|
9
|
+
return JSON.parse(JSON.stringify(v).replace(SECRET_RE, REDACTED));
|
|
10
|
+
}
|
|
11
|
+
return v;
|
|
12
|
+
}
|
|
13
|
+
/** Default sink writes to STDERR only — stdout is the JSON-RPC channel. */
|
|
14
|
+
export function createLogger(sink = (l) => process.stderr.write(l + "\n")) {
|
|
15
|
+
return function log(fields) {
|
|
16
|
+
const safe = { ts: new Date().toISOString(), level: fields.level ?? "info" };
|
|
17
|
+
for (const [k, v] of Object.entries(fields)) {
|
|
18
|
+
if (k === "level")
|
|
19
|
+
continue;
|
|
20
|
+
if (DROP_FIELDS.has(k))
|
|
21
|
+
continue;
|
|
22
|
+
safe[k] = redact(v);
|
|
23
|
+
}
|
|
24
|
+
sink(JSON.stringify(safe));
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAiBA,MAAM,SAAS,GAAG,uDAAuD,CAAC;AAC1E,MAAM,QAAQ,GAAG,YAAY,CAAC;AAE9B,sFAAsF;AACtF,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,gBAAgB,EAAE,KAAK,CAAC,CAAC,CAAC;AAElF,SAAS,MAAM,CAAC,CAAU;IACxB,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IACjE,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;QAC/B,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC;IACpE,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,YAAY,CAAC,OAAa,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC;IAC7E,OAAO,SAAS,GAAG,CAAC,MAAiB;QACnC,MAAM,IAAI,GAA4B,EAAE,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,MAAM,EAAE,CAAC;QACtG,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAC5C,IAAI,CAAC,KAAK,OAAO;gBAAE,SAAS;YAC5B,IAAI,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;gBAAE,SAAS;YACjC,IAAI,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QACtB,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;IAC7B,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type Runner } from "./runner.js";
|
|
2
|
+
export interface PreflightResult {
|
|
3
|
+
ok: boolean;
|
|
4
|
+
reason?: string;
|
|
5
|
+
email?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function preflightAuth(opts?: {
|
|
8
|
+
env?: NodeJS.ProcessEnv;
|
|
9
|
+
runner?: Runner;
|
|
10
|
+
bin?: string;
|
|
11
|
+
}): Promise<PreflightResult>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { realRunner, collectStream } from "./runner.js";
|
|
2
|
+
import { stripAnsi } from "./classify.js";
|
|
3
|
+
const AUTH_FAIL_MARKERS = /API key is invalid|not logged in|\bauthenticate\b|unauthorized|session expired/i;
|
|
4
|
+
export async function preflightAuth(opts = {}) {
|
|
5
|
+
const runner = opts.runner ?? realRunner;
|
|
6
|
+
const bin = opts.bin ?? process.env.CURSOR_AGENT_BIN ?? "cursor-agent";
|
|
7
|
+
const child = runner(bin, ["status", "--format", "json"], { env: opts.env ?? process.env });
|
|
8
|
+
const outP = collectStream(child.stdout);
|
|
9
|
+
const errP = collectStream(child.stderr);
|
|
10
|
+
try {
|
|
11
|
+
await child.done;
|
|
12
|
+
}
|
|
13
|
+
catch (e) {
|
|
14
|
+
return { ok: false, reason: e.message };
|
|
15
|
+
}
|
|
16
|
+
const [out, err] = await Promise.all([outP.done, errP.done]);
|
|
17
|
+
const clean = stripAnsi(out);
|
|
18
|
+
const jsonLine = clean.split("\n").map((l) => l.trim()).find((l) => l.startsWith("{"));
|
|
19
|
+
if (jsonLine) {
|
|
20
|
+
try {
|
|
21
|
+
const obj = JSON.parse(jsonLine);
|
|
22
|
+
if (obj.isAuthenticated === true)
|
|
23
|
+
return { ok: true, email: obj.userInfo?.email };
|
|
24
|
+
return { ok: false, reason: "cursor-agent reports not authenticated" };
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
/* fall through to marker scan */
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (AUTH_FAIL_MARKERS.test(stripAnsi(`${out}\n${err}`))) {
|
|
31
|
+
return { ok: false, reason: "auth failure markers present" };
|
|
32
|
+
}
|
|
33
|
+
return { ok: false, reason: "could not parse cursor-agent status output" };
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=preflight.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preflight.js","sourceRoot":"","sources":["../src/preflight.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,aAAa,EAAe,MAAM,aAAa,CAAC;AACrE,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAQ1C,MAAM,iBAAiB,GAAG,iFAAiF,CAAC;AAE5G,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,OAAmE,EAAE;IAErE,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,UAAU,CAAC;IACzC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,cAAc,CAAC;IACvE,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAC5F,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACzC,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAEzC,IAAI,CAAC;QACH,MAAM,KAAK,CAAC,IAAI,CAAC;IACnB,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAG,CAAW,CAAC,OAAO,EAAE,CAAC;IACrD,CAAC;IACD,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC7D,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;IAE7B,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;IACvF,IAAI,QAAQ,EAAE,CAAC;QACb,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAiE,CAAC;YACjG,IAAI,GAAG,CAAC,eAAe,KAAK,IAAI;gBAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,QAAQ,EAAE,KAAK,EAAE,CAAC;YAClF,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,wCAAwC,EAAE,CAAC;QACzE,CAAC;QAAC,MAAM,CAAC;YACP,iCAAiC;QACnC,CAAC;IACH,CAAC;IACD,IAAI,iBAAiB,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,GAAG,KAAK,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC;QACxD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,8BAA8B,EAAE,CAAC;IAC/D,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,4CAA4C,EAAE,CAAC;AAC7E,CAAC"}
|
package/dist/runner.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Readable } from "node:stream";
|
|
2
|
+
export interface ChildHandle {
|
|
3
|
+
stdout: Readable;
|
|
4
|
+
stderr: Readable;
|
|
5
|
+
/** Resolves on process close; rejects on spawn 'error'. */
|
|
6
|
+
done: Promise<{
|
|
7
|
+
code: number | null;
|
|
8
|
+
signal: NodeJS.Signals | null;
|
|
9
|
+
}>;
|
|
10
|
+
kill: (signal?: NodeJS.Signals) => void;
|
|
11
|
+
}
|
|
12
|
+
export type Runner = (cmd: string, args: string[], opts: {
|
|
13
|
+
env: NodeJS.ProcessEnv;
|
|
14
|
+
cwd?: string;
|
|
15
|
+
}) => ChildHandle;
|
|
16
|
+
export declare const realRunner: Runner;
|
|
17
|
+
/**
|
|
18
|
+
* Buffers a stream to a string. `done` resolves on end/close/error (used on the
|
|
19
|
+
* success path); `current()` returns whatever has accumulated so far (used on the
|
|
20
|
+
* timeout path, where the killed child's streams may never cleanly end).
|
|
21
|
+
*/
|
|
22
|
+
export declare function collectStream(s: Readable): {
|
|
23
|
+
done: Promise<string>;
|
|
24
|
+
current: () => string;
|
|
25
|
+
};
|
package/dist/runner.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
export const realRunner = (cmd, args, opts) => {
|
|
3
|
+
const child = spawn(cmd, args, {
|
|
4
|
+
env: opts.env,
|
|
5
|
+
cwd: opts.cwd,
|
|
6
|
+
stdio: ["ignore", "pipe", "pipe"], // argv ARRAY + no shell:true -> no command injection
|
|
7
|
+
});
|
|
8
|
+
return {
|
|
9
|
+
stdout: child.stdout,
|
|
10
|
+
stderr: child.stderr,
|
|
11
|
+
kill: (sig = "SIGTERM") => child.kill(sig),
|
|
12
|
+
done: new Promise((resolve, reject) => {
|
|
13
|
+
child.on("error", reject);
|
|
14
|
+
child.on("close", (code, signal) => resolve({ code, signal }));
|
|
15
|
+
}),
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Buffers a stream to a string. `done` resolves on end/close/error (used on the
|
|
20
|
+
* success path); `current()` returns whatever has accumulated so far (used on the
|
|
21
|
+
* timeout path, where the killed child's streams may never cleanly end).
|
|
22
|
+
*/
|
|
23
|
+
export function collectStream(s) {
|
|
24
|
+
let buf = "";
|
|
25
|
+
s.on("data", (d) => {
|
|
26
|
+
buf += d.toString();
|
|
27
|
+
});
|
|
28
|
+
const done = new Promise((resolve) => {
|
|
29
|
+
s.on("end", () => resolve(buf));
|
|
30
|
+
s.on("close", () => resolve(buf));
|
|
31
|
+
s.on("error", () => resolve(buf));
|
|
32
|
+
});
|
|
33
|
+
return { done, current: () => buf };
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAiB3C,MAAM,CAAC,MAAM,UAAU,GAAW,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE;IACpD,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE;QAC7B,GAAG,EAAE,IAAI,CAAC,GAAG;QACb,GAAG,EAAE,IAAI,CAAC,GAAG;QACb,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,qDAAqD;KACzF,CAAC,CAAC;IACH,OAAO;QACL,MAAM,EAAE,KAAK,CAAC,MAAO;QACrB,MAAM,EAAE,KAAK,CAAC,MAAO;QACrB,IAAI,EAAE,CAAC,GAAG,GAAG,SAAS,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC;QAC1C,IAAI,EAAE,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACpC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAC1B,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;QACjE,CAAC,CAAC;KACH,CAAC;AACJ,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,CAAW;IACvC,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAkB,EAAE,EAAE;QAClC,GAAG,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;IACtB,CAAC,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,EAAE;QAC3C,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;QAChC,CAAC,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;QAClC,CAAC,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IACH,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC;AACtC,CAAC"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { type CursorRunner } from "./tool.js";
|
|
3
|
+
import { type ImageGenRunner } from "./imagegen.tool.js";
|
|
4
|
+
/** Build the MCP server. `run`/`gen` are injectable so tests can stub the agent layers. */
|
|
5
|
+
export declare function createServer(run?: CursorRunner, gen?: ImageGenRunner): McpServer;
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { TOOL_NAME as CURSOR_TOOL, inputSchema as cursorInput, makeToolHandler } from "./tool.js";
|
|
3
|
+
import { TOOL_NAME as IMAGE_TOOL, inputSchema as imageInput, makeImageGenHandler, } from "./imagegen.tool.js";
|
|
4
|
+
/** Build the MCP server. `run`/`gen` are injectable so tests can stub the agent layers. */
|
|
5
|
+
export function createServer(run, gen) {
|
|
6
|
+
const server = new McpServer({ name: "cursor-bridge", version: "0.1.0" });
|
|
7
|
+
server.registerTool(CURSOR_TOOL, {
|
|
8
|
+
title: "Run Cursor Agent (Composer 2.5 Fast worker)",
|
|
9
|
+
description: "Dispatch a task to cursor-agent (Composer 2.5 Fast by default) in headless mode and return the parsed result. Use mode 'default' for read-write implementation (auto-approves), 'plan'/'ask' for read-only review.",
|
|
10
|
+
inputSchema: cursorInput,
|
|
11
|
+
}, makeToolHandler(run));
|
|
12
|
+
server.registerTool(IMAGE_TOOL, {
|
|
13
|
+
title: "Generate images with gpt-image-2 (via Codex)",
|
|
14
|
+
description: "Generate or edit images with gpt-image-2 by driving 'codex exec' (subscription auth; requires 'codex login'). Returns generated file paths (and optional inline image blocks). Supports reference images for editing/iteration.",
|
|
15
|
+
inputSchema: imageInput,
|
|
16
|
+
}, makeImageGenHandler(gen));
|
|
17
|
+
return server;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,SAAS,IAAI,WAAW,EAAE,WAAW,IAAI,WAAW,EAAE,eAAe,EAAqB,MAAM,WAAW,CAAC;AACrH,OAAO,EACL,SAAS,IAAI,UAAU,EACvB,WAAW,IAAI,UAAU,EACzB,mBAAmB,GAEpB,MAAM,oBAAoB,CAAC;AAE5B,2FAA2F;AAC3F,MAAM,UAAU,YAAY,CAAC,GAAkB,EAAE,GAAoB;IACnE,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;IAC1E,MAAM,CAAC,YAAY,CACjB,WAAW,EACX;QACE,KAAK,EAAE,6CAA6C;QACpD,WAAW,EACT,oNAAoN;QACtN,WAAW,EAAE,WAAW;KACzB,EACD,eAAe,CAAC,GAAG,CAAC,CACrB,CAAC;IACF,MAAM,CAAC,YAAY,CACjB,UAAU,EACV;QACE,KAAK,EAAE,8CAA8C;QACrD,WAAW,EACT,iOAAiO;QACnO,WAAW,EAAE,UAAU;KACxB,EACD,mBAAmB,CAAC,GAAG,CAAC,CACzB,CAAC;IACF,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
package/dist/tool.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { type RunOptions } from "./cursor.js";
|
|
3
|
+
import type { CursorResult } from "./errors.js";
|
|
4
|
+
export declare const TOOL_NAME = "run_cursor_agent";
|
|
5
|
+
export declare const inputSchema: {
|
|
6
|
+
prompt: z.ZodString;
|
|
7
|
+
cwd: z.ZodString;
|
|
8
|
+
mode: z.ZodDefault<z.ZodEnum<{
|
|
9
|
+
default: "default";
|
|
10
|
+
plan: "plan";
|
|
11
|
+
ask: "ask";
|
|
12
|
+
}>>;
|
|
13
|
+
model: z.ZodDefault<z.ZodString>;
|
|
14
|
+
timeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
15
|
+
};
|
|
16
|
+
export interface ToolArgs {
|
|
17
|
+
prompt: string;
|
|
18
|
+
cwd: string;
|
|
19
|
+
mode?: "default" | "plan" | "ask";
|
|
20
|
+
model?: string;
|
|
21
|
+
timeoutMs?: number;
|
|
22
|
+
}
|
|
23
|
+
export type CursorRunner = (prompt: string, opts: RunOptions) => Promise<CursorResult>;
|
|
24
|
+
export interface ToolResult {
|
|
25
|
+
[x: string]: unknown;
|
|
26
|
+
content: {
|
|
27
|
+
type: "text";
|
|
28
|
+
text: string;
|
|
29
|
+
}[];
|
|
30
|
+
isError?: boolean;
|
|
31
|
+
structuredContent?: {
|
|
32
|
+
status: string;
|
|
33
|
+
result?: string;
|
|
34
|
+
usage?: CursorResult["usage"];
|
|
35
|
+
sessionId?: string;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export declare function makeToolHandler(run?: CursorRunner): (args: ToolArgs) => Promise<ToolResult>;
|
package/dist/tool.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { runCursorAgent } from "./cursor.js";
|
|
3
|
+
export const TOOL_NAME = "run_cursor_agent";
|
|
4
|
+
export const inputSchema = {
|
|
5
|
+
prompt: z.string().min(1).describe("The full task/prompt to send to the Composer worker."),
|
|
6
|
+
cwd: z.string().describe("Absolute path to the worktree/working dir (validated against the allowed root)."),
|
|
7
|
+
mode: z
|
|
8
|
+
.enum(["default", "plan", "ask"])
|
|
9
|
+
.default("default")
|
|
10
|
+
.describe("default = read-write (auto-approves via --force); plan/ask = read-only."),
|
|
11
|
+
model: z.string().default("composer-2.5-fast").describe("Allowlisted Cursor model id."),
|
|
12
|
+
timeoutMs: z.number().int().positive().default(270_000).describe("Wall-clock timeout before the wrapper kills cursor-agent."),
|
|
13
|
+
};
|
|
14
|
+
export function makeToolHandler(run = runCursorAgent) {
|
|
15
|
+
return async (args) => {
|
|
16
|
+
let r;
|
|
17
|
+
try {
|
|
18
|
+
r = await run(args.prompt, {
|
|
19
|
+
cwd: args.cwd,
|
|
20
|
+
mode: args.mode,
|
|
21
|
+
model: args.model,
|
|
22
|
+
timeoutMs: args.timeoutMs,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
catch (e) {
|
|
26
|
+
// Never let an exception escape the handler, and never echo the prompt body.
|
|
27
|
+
return { content: [{ type: "text", text: `TOOL_ERROR: ${e.message}` }], isError: true };
|
|
28
|
+
}
|
|
29
|
+
if (r.status === "SUCCESS") {
|
|
30
|
+
return {
|
|
31
|
+
content: [{ type: "text", text: r.result ?? "" }],
|
|
32
|
+
structuredContent: { status: r.status, result: r.result, usage: r.usage, sessionId: r.sessionId },
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
content: [{ type: "text", text: `${r.status}: ${r.error ?? "cursor-agent did not return a successful result"}` }],
|
|
37
|
+
isError: true,
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=tool.js.map
|
package/dist/tool.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool.js","sourceRoot":"","sources":["../src/tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,cAAc,EAAmB,MAAM,aAAa,CAAC;AAG9D,MAAM,CAAC,MAAM,SAAS,GAAG,kBAAkB,CAAC;AAE5C,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,sDAAsD,CAAC;IAC1F,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,iFAAiF,CAAC;IAC3G,IAAI,EAAE,CAAC;SACJ,IAAI,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;SAChC,OAAO,CAAC,SAAS,CAAC;SAClB,QAAQ,CAAC,yEAAyE,CAAC;IACtF,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC,QAAQ,CAAC,8BAA8B,CAAC;IACvF,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,2DAA2D,CAAC;CAC9H,CAAC;AAwBF,MAAM,UAAU,eAAe,CAAC,MAAoB,cAAc;IAChE,OAAO,KAAK,EAAE,IAAc,EAAuB,EAAE;QACnD,IAAI,CAAe,CAAC;QACpB,IAAI,CAAC;YACH,CAAC,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE;gBACzB,GAAG,EAAE,IAAI,CAAC,GAAG;gBACb,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,SAAS,EAAE,IAAI,CAAC,SAAS;aAC1B,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,6EAA6E;YAC7E,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,eAAgB,CAAW,CAAC,OAAO,EAAE,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QACrG,CAAC;QAED,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC3B,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;gBACjD,iBAAiB,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE;aAClG,CAAC;QACJ,CAAC;QACD,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,KAAK,IAAI,iDAAiD,EAAE,EAAE,CAAC;YACjH,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type CursorMode = "default" | "plan" | "ask";
|
|
2
|
+
export declare function assertMode(mode: string): CursorMode;
|
|
3
|
+
export declare function assertModel(model: string): string;
|
|
4
|
+
export declare function assertSha(sha: string): string;
|
|
5
|
+
/**
|
|
6
|
+
* Returns the canonical (realpath'd) cwd if it is the allowed root or strictly
|
|
7
|
+
* within it. Rejects traversal/expansion tokens before touching the FS, then
|
|
8
|
+
* defeats symlink escapes by canonicalizing both sides and prefix-checking.
|
|
9
|
+
*/
|
|
10
|
+
export declare function assertWorkspacePath(cwd: string, allowedRoot: string): string;
|
|
11
|
+
export type ImageSize = "auto" | "1024x1024" | "1536x1024" | "1024x1536";
|
|
12
|
+
export type ImageQuality = "auto" | "low" | "medium" | "high";
|
|
13
|
+
export declare function assertCount(count: number): number;
|
|
14
|
+
export declare function assertSize(size: string): ImageSize;
|
|
15
|
+
export declare function assertQuality(quality: string): ImageQuality;
|
|
16
|
+
/**
|
|
17
|
+
* Validates an output directory that MAY NOT EXIST YET (unlike assertWorkspacePath,
|
|
18
|
+
* which requires existence). Rejects traversal/expansion, lexically checks the full
|
|
19
|
+
* resolved path against the canonical root, then realpath-checks the nearest existing
|
|
20
|
+
* ancestor to defeat symlink escapes through existing directories.
|
|
21
|
+
*/
|
|
22
|
+
export declare function assertOutputDir(dir: string, allowedRoot: string): string;
|
|
23
|
+
export declare function assertReferenceImages(paths: string[], allowedRoot: string): string[];
|