Initial commit with translated description
This commit is contained in:
7
.clawhub/origin.json
Normal file
7
.clawhub/origin.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"registry": "https://clawhub.ai",
|
||||||
|
"slug": "antigravity-image-gen",
|
||||||
|
"installedVersion": "1.0.0",
|
||||||
|
"installedAt": 1771354397276
|
||||||
|
}
|
||||||
46
SKILL.md
Normal file
46
SKILL.md
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
---
|
||||||
|
name: antigravity-image-gen
|
||||||
|
description: "使用Google Antigravity API生成图像。"
|
||||||
|
read_when:
|
||||||
|
- User asks to generate an image
|
||||||
|
- User wants to create visual content
|
||||||
|
metadata: {"clawdbot":{"emoji":"🎨","requires":{"bins":["node"],"config":["auth.profiles"]},"description":"Generates images via internal Google API using local OAuth credentials."}}
|
||||||
|
---
|
||||||
|
|
||||||
|
# Antigravity Image Generation
|
||||||
|
|
||||||
|
Generate high-quality images using the internal Google Antigravity API (Gemini 3 Pro Image). This skill bypasses the need for browser automation by using the `daily-cloudcode-pa.sandbox` endpoint directly with your OAuth credentials.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- **Google Antigravity OAuth Profile**: Must be present in your OpenClaw auth-profiles.json.
|
||||||
|
- **Node.js**: Available in the environment.
|
||||||
|
- **Security Note**: This skill reads local OAuth tokens from your profile to authenticate with Google's API. This is expected behavior for internal tool use.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Direct Script Execution
|
||||||
|
|
||||||
|
```bash
|
||||||
|
/home/ubuntu/clawd/skills/antigravity-image-gen/scripts/generate.js \
|
||||||
|
--prompt "A futuristic city on Mars" \
|
||||||
|
--output "/tmp/mars.png" \
|
||||||
|
--aspect-ratio "16:9"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Arguments
|
||||||
|
|
||||||
|
- `--prompt` (Required): The description of the image.
|
||||||
|
- `--output` (Optional): Path to save the image (default: `/tmp/antigravity_<ts>.png`).
|
||||||
|
- `--aspect-ratio` (Optional): `1:1` (default), `16:9`, `9:16`, `4:3`, `3:4`.
|
||||||
|
|
||||||
|
## Output
|
||||||
|
|
||||||
|
- The script writes the image to the specified path.
|
||||||
|
- It prints `MEDIA: <path>` to stdout, which allows Clawdbot to automatically detect and display the image.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
- **429 Resource Exhausted**: Quota limit reached. Wait or check your project limits.
|
||||||
|
- **No image data found**: The model might have refused the prompt (safety) or the API structure changed. Check the "Model message" output.
|
||||||
|
- **Auth Error**: Ensure you have logged in via `google-antigravity` provider.
|
||||||
6
_meta.json
Normal file
6
_meta.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"ownerId": "kn79jhh393p6ryrcqfgx3rvw5n802qpe",
|
||||||
|
"slug": "antigravity-image-gen",
|
||||||
|
"version": "2.0.0",
|
||||||
|
"publishedAt": 1771355111026
|
||||||
|
}
|
||||||
173
scripts/generate.js
Normal file
173
scripts/generate.js
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Antigravity Image Generator (Native)
|
||||||
|
* Uses the google-antigravity OAuth token to generate images via the Cloud Code sandbox API.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* node generate.js --prompt "..." --output "..." [--aspect-ratio "16:9"]
|
||||||
|
*/
|
||||||
|
|
||||||
|
const fs = require('node:fs');
|
||||||
|
const https = require('node:https');
|
||||||
|
const { Buffer } = require('node:buffer');
|
||||||
|
const path = require('node:path');
|
||||||
|
|
||||||
|
// --- Config ---
|
||||||
|
const ENDPOINT = "https://daily-cloudcode-pa.sandbox.googleapis.com/v1internal:streamGenerateContent?alt=sse";
|
||||||
|
const PROFILE_PATH = process.env.OPENCLAW_AUTH_PATH || path.join(process.env.HOME || process.env.USERPROFILE, '.openclaw/agents/main/agent/auth-profiles.json');
|
||||||
|
// Project ID found in auth profile or fallback
|
||||||
|
const FALLBACK_PROJECT_ID = "junoai-465910";
|
||||||
|
|
||||||
|
// --- Args Parsing ---
|
||||||
|
const args = process.argv.slice(2);
|
||||||
|
let prompt = "";
|
||||||
|
let outputFile = "";
|
||||||
|
let aspectRatio = "1:1";
|
||||||
|
|
||||||
|
for (let i = 0; i < args.length; i++) {
|
||||||
|
if (args[i] === '--prompt' && args[i+1]) {
|
||||||
|
prompt = args[i+1];
|
||||||
|
i++;
|
||||||
|
} else if (args[i] === '--output' && args[i+1]) {
|
||||||
|
outputFile = args[i+1];
|
||||||
|
i++;
|
||||||
|
} else if (args[i] === '--aspect-ratio' && args[i+1]) {
|
||||||
|
aspectRatio = args[i+1];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!prompt) {
|
||||||
|
console.error("Error: --prompt is required");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!outputFile) {
|
||||||
|
const dir = path.join(process.env.USERPROFILE || process.env.HOME || '/home/ubuntu', 'Pictures', 'Clawd');
|
||||||
|
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
||||||
|
outputFile = path.join(dir, `antigravity_${Date.now()}.png`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Auth Loading ---
|
||||||
|
console.log("🔐 Loading Antigravity credentials...");
|
||||||
|
if (!fs.existsSync(PROFILE_PATH)) {
|
||||||
|
console.error(`Error: Auth profile not found at ${PROFILE_PATH}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let token = "";
|
||||||
|
let projectId = FALLBACK_PROJECT_ID;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const profiles = JSON.parse(fs.readFileSync(PROFILE_PATH, 'utf8'));
|
||||||
|
// Look for google-antigravity profile
|
||||||
|
const profileKey = Object.keys(profiles.profiles).find(k => k.startsWith("google-antigravity"));
|
||||||
|
const auth = profiles.profiles[profileKey];
|
||||||
|
|
||||||
|
if (!auth || !auth.access) {
|
||||||
|
console.error("Error: No google-antigravity profile or access token found.");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
token = auth.access;
|
||||||
|
if (auth.projectId) projectId = auth.projectId;
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`Error parsing auth profile: ${e.message}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Request ---
|
||||||
|
const payload = {
|
||||||
|
project: projectId,
|
||||||
|
model: "gemini-3-pro-image",
|
||||||
|
request: {
|
||||||
|
contents: [{
|
||||||
|
role: "user",
|
||||||
|
parts: [{ text: prompt }]
|
||||||
|
}],
|
||||||
|
systemInstruction: {
|
||||||
|
parts: [{ text: "You are an AI image generator. Generate images based on user descriptions." }]
|
||||||
|
},
|
||||||
|
generationConfig: {
|
||||||
|
imageConfig: { aspectRatio: aspectRatio },
|
||||||
|
candidateCount: 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
requestType: "agent",
|
||||||
|
requestId: `agent-${Date.now()}`,
|
||||||
|
userAgent: "antigravity"
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(`🎨 Generating image...`);
|
||||||
|
console.log(` Prompt: "${prompt.substring(0, 50)}..."`);
|
||||||
|
console.log(` Ratio: ${aspectRatio}`);
|
||||||
|
|
||||||
|
const req = https.request(ENDPOINT, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${token}`,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'text/event-stream',
|
||||||
|
// IMPORTANT: Version bump to bypass deprecation checks
|
||||||
|
'User-Agent': 'antigravity/2.0.0 darwin/arm64',
|
||||||
|
'X-Goog-Api-Client': 'google-cloud-sdk vscode_cloudshelleditor/0.1',
|
||||||
|
'Client-Metadata': JSON.stringify({
|
||||||
|
ideType: "IDE_UNSPECIFIED",
|
||||||
|
platform: "PLATFORM_UNSPECIFIED",
|
||||||
|
pluginType: "GEMINI",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, (res) => {
|
||||||
|
if (res.statusCode !== 200) {
|
||||||
|
console.error(`API Error: ${res.statusCode} ${res.statusMessage}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = '';
|
||||||
|
|
||||||
|
res.on('data', (chunk) => {
|
||||||
|
data += chunk.toString();
|
||||||
|
});
|
||||||
|
|
||||||
|
res.on('end', () => {
|
||||||
|
const lines = data.split('\n');
|
||||||
|
for (const line of lines) {
|
||||||
|
if (line.startsWith('data:')) {
|
||||||
|
try {
|
||||||
|
const json = JSON.parse(line.substring(5));
|
||||||
|
|
||||||
|
// Check for error in response content
|
||||||
|
const parts = json.response?.candidates?.[0]?.content?.parts;
|
||||||
|
if (parts) {
|
||||||
|
for (const part of parts) {
|
||||||
|
if (part.inlineData && part.inlineData.data) {
|
||||||
|
// Success!
|
||||||
|
fs.writeFileSync(outputFile, Buffer.from(part.inlineData.data, 'base64'));
|
||||||
|
console.log(`✅ Image saved to: ${outputFile}`);
|
||||||
|
// Clawdbot magic tag
|
||||||
|
console.log(`MEDIA: ${outputFile}`);
|
||||||
|
process.exit(0);
|
||||||
|
} else if (part.text) {
|
||||||
|
console.log(`Model message: ${part.text}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Ignore parse errors for keep-alives
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.error("❌ No image data found in response.");
|
||||||
|
console.error("Raw start:", data.substring(0, 200));
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
req.on('error', (e) => {
|
||||||
|
console.error(`Request error: ${e.message}`);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
req.write(JSON.stringify(payload));
|
||||||
|
req.end();
|
||||||
Reference in New Issue
Block a user