runline 0.5.3 → 0.7.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/dist/plugins/_shared/parseSize.js +26 -0
- package/dist/plugins/googleImage/src/index.js +79 -0
- package/dist/plugins/linear/src/index.js +986 -128
- package/dist/plugins/openai/src/index.js +112 -0
- package/dist/plugins/recraft/src/index.js +100 -0
- package/dist/plugins/replicate/src/index.js +168 -0
- package/dist/plugins/together/src/index.js +93 -0
- package/dist/plugins/xai/src/index.js +77 -0
- package/package.json +2 -2
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse a `WxH` string into a { width, height } pair.
|
|
3
|
+
*
|
|
4
|
+
* Used by image-gen plugins (replicate, together, …) that take a
|
|
5
|
+
* single `size` input but talk to APIs which want explicit
|
|
6
|
+
* dimensions. Throws with the caller's plugin name in the message
|
|
7
|
+
* so error output points back at the right tool.
|
|
8
|
+
*
|
|
9
|
+
* parseSize("1024x1024", "replicate") // { width: 1024, height: 1024 }
|
|
10
|
+
* parseSize(undefined, "replicate") // defaults to 1024x1024
|
|
11
|
+
* parseSize("1024", "replicate") // throws
|
|
12
|
+
*/
|
|
13
|
+
export function parseSize(size, pluginName, defaults = { width: 1024, height: 1024 }) {
|
|
14
|
+
if (!size)
|
|
15
|
+
return { ...defaults };
|
|
16
|
+
const parts = size.split("x").map((s) => Number(s.trim()));
|
|
17
|
+
if (parts.length !== 2 ||
|
|
18
|
+
!Number.isFinite(parts[0]) ||
|
|
19
|
+
!Number.isFinite(parts[1])) {
|
|
20
|
+
throw new Error(`${pluginName}: invalid size "${size}", expected WxH`);
|
|
21
|
+
}
|
|
22
|
+
if (parts[0] <= 0 || parts[1] <= 0) {
|
|
23
|
+
throw new Error(`${pluginName}: size dimensions must be positive`);
|
|
24
|
+
}
|
|
25
|
+
return { width: parts[0], height: parts[1] };
|
|
26
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google Gemini image generation (Nano Banana / Imagen) for runline.
|
|
3
|
+
*
|
|
4
|
+
* Distinct from the rest of the googleX family — those wrap Workspace
|
|
5
|
+
* APIs over OAuth2, this one wraps Generative Language over a single
|
|
6
|
+
* API key. Kept under the `googleImage` namespace so it doesn't
|
|
7
|
+
* collide with `googleDrive`, `googleDocs`, etc.
|
|
8
|
+
*
|
|
9
|
+
* await googleImage.image.create({ prompt: "a watercolor fox" })
|
|
10
|
+
* await googleImage.image.create({
|
|
11
|
+
* prompt: "edit: make the sky stormier",
|
|
12
|
+
* model: "gemini-3-pro-image-preview",
|
|
13
|
+
* })
|
|
14
|
+
*
|
|
15
|
+
* Nano Banana supports conversational editing — chain prompts in
|
|
16
|
+
* follow-up calls and it'll keep iterating on the last image.
|
|
17
|
+
*/
|
|
18
|
+
const BASE = "https://generativelanguage.googleapis.com/v1beta/models";
|
|
19
|
+
export default function googleImage(rl) {
|
|
20
|
+
rl.setName("googleImage");
|
|
21
|
+
rl.setVersion("0.1.0");
|
|
22
|
+
rl.setConnectionSchema({
|
|
23
|
+
apiKey: {
|
|
24
|
+
type: "string",
|
|
25
|
+
required: true,
|
|
26
|
+
description: "Google AI API key (Gemini)",
|
|
27
|
+
env: "GOOGLE_API_KEY",
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
rl.registerAction("image.create", {
|
|
31
|
+
description: "Generate an image with Google's Gemini image models (Nano Banana / Imagen). Returns base64 bytes per candidate.",
|
|
32
|
+
inputSchema: {
|
|
33
|
+
prompt: {
|
|
34
|
+
type: "string",
|
|
35
|
+
required: true,
|
|
36
|
+
description: "Detailed description of the image",
|
|
37
|
+
},
|
|
38
|
+
model: {
|
|
39
|
+
type: "string",
|
|
40
|
+
required: false,
|
|
41
|
+
description: "gemini-2.5-flash-image (Nano Banana, default) | gemini-3-pro-image-preview | gemini-3.1-flash-image-preview",
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
async execute(input, ctx) {
|
|
45
|
+
const p = (input ?? {});
|
|
46
|
+
if (typeof p.prompt !== "string" || p.prompt.length === 0) {
|
|
47
|
+
throw new Error("googleImage: prompt is required");
|
|
48
|
+
}
|
|
49
|
+
const apiKey = ctx.connection.config.apiKey;
|
|
50
|
+
const model = p.model ?? "gemini-2.5-flash-image";
|
|
51
|
+
const url = `${BASE}/${model}:generateContent?key=${encodeURIComponent(apiKey)}`;
|
|
52
|
+
const body = {
|
|
53
|
+
contents: [{ parts: [{ text: p.prompt }] }],
|
|
54
|
+
generationConfig: { responseModalities: ["IMAGE", "TEXT"] },
|
|
55
|
+
};
|
|
56
|
+
const res = await fetch(url, {
|
|
57
|
+
method: "POST",
|
|
58
|
+
headers: { "Content-Type": "application/json" },
|
|
59
|
+
body: JSON.stringify(body),
|
|
60
|
+
});
|
|
61
|
+
if (!res.ok) {
|
|
62
|
+
throw new Error(`Google API error ${res.status}: ${await res.text()}`);
|
|
63
|
+
}
|
|
64
|
+
const data = (await res.json());
|
|
65
|
+
const images = [];
|
|
66
|
+
for (const candidate of data.candidates ?? []) {
|
|
67
|
+
for (const part of candidate.content?.parts ?? []) {
|
|
68
|
+
if (part.inlineData?.data) {
|
|
69
|
+
images.push({
|
|
70
|
+
base64: part.inlineData.data,
|
|
71
|
+
mimeType: part.inlineData.mimeType ?? "image/png",
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return { provider: "googleImage", model, images };
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
}
|