@openclaw/pixverse-provider 2026.5.27-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.
@@ -0,0 +1,11 @@
1
+ //#region extensions/pixverse/constants.ts
2
+ const PIXVERSE_PROVIDER_ID = "pixverse";
3
+ const PIXVERSE_BASE_URL_BY_REGION = {
4
+ international: "https://app-api.pixverse.ai/openapi/v2",
5
+ cn: "https://app-api.pixverseai.cn/openapi/v2"
6
+ };
7
+ const DEFAULT_PIXVERSE_REGION = "international";
8
+ const DEFAULT_PIXVERSE_MODEL_ID = "v6";
9
+ const PIXVERSE_DEFAULT_VIDEO_MODEL_REF = `${PIXVERSE_PROVIDER_ID}/v6`;
10
+ //#endregion
11
+ export { DEFAULT_PIXVERSE_MODEL_ID, DEFAULT_PIXVERSE_REGION, PIXVERSE_BASE_URL_BY_REGION, PIXVERSE_DEFAULT_VIDEO_MODEL_REF, PIXVERSE_PROVIDER_ID };
package/dist/index.js ADDED
@@ -0,0 +1,22 @@
1
+ import { PIXVERSE_PROVIDER_ID } from "./constants.js";
2
+ import { buildPixVerseApiKeyAuthMethod } from "./onboard.js";
3
+ import { buildPixVerseVideoGenerationProvider } from "./video-generation-provider.js";
4
+ import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
5
+ //#region extensions/pixverse/index.ts
6
+ var pixverse_default = definePluginEntry({
7
+ id: PIXVERSE_PROVIDER_ID,
8
+ name: "PixVerse Provider",
9
+ description: "Official external PixVerse video provider plugin",
10
+ register(api) {
11
+ api.registerProvider({
12
+ id: PIXVERSE_PROVIDER_ID,
13
+ label: "PixVerse",
14
+ docsPath: "/providers/pixverse",
15
+ envVars: ["PIXVERSE_API_KEY"],
16
+ auth: [buildPixVerseApiKeyAuthMethod()]
17
+ });
18
+ api.registerVideoGenerationProvider(buildPixVerseVideoGenerationProvider());
19
+ }
20
+ });
21
+ //#endregion
22
+ export { pixverse_default as default };
@@ -0,0 +1,159 @@
1
+ import { DEFAULT_PIXVERSE_REGION, PIXVERSE_BASE_URL_BY_REGION, PIXVERSE_DEFAULT_VIDEO_MODEL_REF, PIXVERSE_PROVIDER_ID } from "./constants.js";
2
+ import "openclaw/plugin-sdk/plugin-entry";
3
+ import { applyAuthProfileConfig, buildApiKeyCredential, ensureApiKeyFromOptionEnvOrPrompt, normalizeApiKeyInput, normalizeOptionalSecretInput, upsertAuthProfileWithLock, validateApiKeyInput } from "openclaw/plugin-sdk/provider-auth-api-key";
4
+ import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
5
+ //#region extensions/pixverse/onboard.ts
6
+ const PROFILE_ID = `${PIXVERSE_PROVIDER_ID}:default`;
7
+ async function upsertAuthProfileWithLockOrThrow(params) {
8
+ if (!await upsertAuthProfileWithLock(params)) throw new Error("Failed to update auth profile store; the auth store lock may be busy. Wait a moment and retry.");
9
+ }
10
+ function normalizePixVerseRegion(value) {
11
+ switch (normalizeOptionalString(value)?.toLowerCase()) {
12
+ case "cn":
13
+ case "china":
14
+ case "mainland":
15
+ case "pai": return "cn";
16
+ case "global":
17
+ case "intl":
18
+ case "international": return "international";
19
+ default: return;
20
+ }
21
+ }
22
+ function pixVerseRegionNote(region) {
23
+ return `PixVerse endpoint: ${region === "cn" ? "CN" : "International"} (${PIXVERSE_BASE_URL_BY_REGION[region]})`;
24
+ }
25
+ function applyPixVerseProviderConfig(cfg, region, options) {
26
+ const existingProvider = cfg.models?.providers?.["pixverse"] ?? {};
27
+ const selectedBaseUrl = PIXVERSE_BASE_URL_BY_REGION[region];
28
+ const baseUrl = options?.resetBaseUrl ? selectedBaseUrl : normalizeOptionalString(existingProvider.baseUrl) ?? selectedBaseUrl;
29
+ return {
30
+ ...cfg,
31
+ models: {
32
+ ...cfg.models,
33
+ providers: {
34
+ ...cfg.models?.providers,
35
+ [PIXVERSE_PROVIDER_ID]: {
36
+ ...existingProvider,
37
+ baseUrl,
38
+ models: existingProvider.models ?? [],
39
+ region
40
+ }
41
+ }
42
+ }
43
+ };
44
+ }
45
+ function applyPixVerseConfig(cfg, region, options) {
46
+ const next = applyPixVerseProviderConfig(cfg, region, options);
47
+ if (next.agents?.defaults?.videoGenerationModel) return next;
48
+ return {
49
+ ...next,
50
+ agents: {
51
+ ...next.agents,
52
+ defaults: {
53
+ ...next.agents?.defaults,
54
+ videoGenerationModel: { primary: PIXVERSE_DEFAULT_VIDEO_MODEL_REF }
55
+ }
56
+ }
57
+ };
58
+ }
59
+ async function promptForPixVerseRegion(ctx) {
60
+ return await ctx.prompter.select({
61
+ message: "Select PixVerse API region",
62
+ initialValue: DEFAULT_PIXVERSE_REGION,
63
+ options: [{
64
+ value: "international",
65
+ label: "International",
66
+ hint: PIXVERSE_BASE_URL_BY_REGION.international
67
+ }, {
68
+ value: "cn",
69
+ label: "CN",
70
+ hint: PIXVERSE_BASE_URL_BY_REGION.cn
71
+ }]
72
+ });
73
+ }
74
+ async function runPixVerseApiKeyAuth(ctx) {
75
+ let capturedSecretInput;
76
+ let capturedCredential = false;
77
+ let capturedMode;
78
+ await ensureApiKeyFromOptionEnvOrPrompt({
79
+ token: normalizeOptionalSecretInput(ctx.opts?.pixverseApiKey) ?? normalizeOptionalSecretInput(ctx.opts?.token),
80
+ tokenProvider: normalizeOptionalSecretInput(ctx.opts?.pixverseApiKey) ? PIXVERSE_PROVIDER_ID : normalizeOptionalSecretInput(ctx.opts?.tokenProvider),
81
+ secretInputMode: ctx.allowSecretRefPrompt === false ? ctx.secretInputMode ?? "plaintext" : ctx.secretInputMode,
82
+ config: ctx.config,
83
+ env: ctx.env,
84
+ expectedProviders: [PIXVERSE_PROVIDER_ID],
85
+ provider: PIXVERSE_PROVIDER_ID,
86
+ envLabel: "PIXVERSE_API_KEY",
87
+ promptMessage: "Enter PixVerse API key",
88
+ normalize: normalizeApiKeyInput,
89
+ validate: validateApiKeyInput,
90
+ prompter: ctx.prompter,
91
+ setCredential: async (apiKey, mode) => {
92
+ capturedSecretInput = apiKey;
93
+ capturedCredential = true;
94
+ capturedMode = mode;
95
+ }
96
+ });
97
+ if (!capturedCredential) throw new Error("Missing PixVerse API key.");
98
+ const region = await promptForPixVerseRegion(ctx);
99
+ return {
100
+ profiles: [{
101
+ profileId: PROFILE_ID,
102
+ credential: buildApiKeyCredential(PIXVERSE_PROVIDER_ID, capturedSecretInput ?? "", void 0, capturedMode ? {
103
+ secretInputMode: capturedMode,
104
+ config: ctx.config
105
+ } : void 0)
106
+ }],
107
+ configPatch: applyPixVerseConfig(ctx.config, region, { resetBaseUrl: true }),
108
+ notes: [pixVerseRegionNote(region)]
109
+ };
110
+ }
111
+ async function runPixVerseApiKeyAuthNonInteractive(ctx) {
112
+ const resolved = await ctx.resolveApiKey({
113
+ provider: PIXVERSE_PROVIDER_ID,
114
+ flagValue: normalizeOptionalSecretInput(ctx.opts.pixverseApiKey),
115
+ flagName: "--pixverse-api-key",
116
+ envVar: "PIXVERSE_API_KEY"
117
+ });
118
+ if (!resolved) return null;
119
+ if (resolved.source !== "profile") {
120
+ const credential = ctx.toApiKeyCredential({
121
+ provider: PIXVERSE_PROVIDER_ID,
122
+ resolved
123
+ });
124
+ if (!credential) return null;
125
+ await upsertAuthProfileWithLockOrThrow({
126
+ profileId: PROFILE_ID,
127
+ credential,
128
+ agentDir: ctx.agentDir
129
+ });
130
+ }
131
+ const next = applyAuthProfileConfig(ctx.config, {
132
+ profileId: PROFILE_ID,
133
+ provider: PIXVERSE_PROVIDER_ID,
134
+ mode: "api_key"
135
+ });
136
+ const explicitRegion = normalizePixVerseRegion(ctx.opts.pixverseRegion);
137
+ return applyPixVerseConfig(next, explicitRegion ?? "international", { resetBaseUrl: explicitRegion !== void 0 });
138
+ }
139
+ function buildPixVerseApiKeyAuthMethod() {
140
+ return {
141
+ id: "api-key",
142
+ label: "PixVerse API key",
143
+ hint: "Video generation API key",
144
+ kind: "api_key",
145
+ wizard: {
146
+ choiceId: "pixverse-api-key",
147
+ choiceLabel: "PixVerse API key",
148
+ choiceHint: "Prompts for International or CN endpoint",
149
+ groupId: "pixverse",
150
+ groupLabel: "PixVerse",
151
+ groupHint: "Video generation",
152
+ onboardingScopes: ["image-generation"]
153
+ },
154
+ run: runPixVerseApiKeyAuth,
155
+ runNonInteractive: runPixVerseApiKeyAuthNonInteractive
156
+ };
157
+ }
158
+ //#endregion
159
+ export { applyPixVerseConfig, applyPixVerseProviderConfig, buildPixVerseApiKeyAuthMethod };
@@ -0,0 +1,345 @@
1
+ import { DEFAULT_PIXVERSE_REGION, PIXVERSE_BASE_URL_BY_REGION, PIXVERSE_PROVIDER_ID } from "./constants.js";
2
+ import { asFiniteNumber, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
3
+ import { randomUUID } from "node:crypto";
4
+ import { extensionForMime } from "openclaw/plugin-sdk/media-mime";
5
+ import { isProviderApiKeyConfigured } from "openclaw/plugin-sdk/provider-auth";
6
+ import { resolveApiKeyForProvider } from "openclaw/plugin-sdk/provider-auth-runtime";
7
+ import { assertOkOrThrowHttpError, createProviderOperationDeadline, pollProviderOperationJson, postJsonRequest, postMultipartRequest, resolveProviderHttpRequestConfig, resolveProviderOperationTimeoutMs, sanitizeConfiguredModelProviderRequest } from "openclaw/plugin-sdk/provider-http";
8
+ //#region extensions/pixverse/video-generation-provider.ts
9
+ const DEFAULT_PIXVERSE_BASE_URL = PIXVERSE_BASE_URL_BY_REGION[DEFAULT_PIXVERSE_REGION];
10
+ const DEFAULT_PIXVERSE_QUALITY = "540p";
11
+ const DEFAULT_TIMEOUT_MS = 3e5;
12
+ const POLL_INTERVAL_MS = 5e3;
13
+ const MAX_POLL_ATTEMPTS = 180;
14
+ const MAX_DURATION_SECONDS = 15;
15
+ const PIXVERSE_VIDEO_MODELS = ["v6", "c1"];
16
+ const PIXVERSE_TEXT_ASPECT_RATIOS = [
17
+ "16:9",
18
+ "4:3",
19
+ "1:1",
20
+ "3:4",
21
+ "9:16",
22
+ "2:3",
23
+ "3:2",
24
+ "21:9"
25
+ ];
26
+ const PIXVERSE_QUALITIES = [
27
+ "360p",
28
+ "540p",
29
+ "720p",
30
+ "1080p"
31
+ ];
32
+ function resolvePixVerseBaseUrl(req) {
33
+ const provider = req.cfg?.models?.providers?.[PIXVERSE_PROVIDER_ID];
34
+ const configuredBaseUrl = normalizeOptionalString(provider?.baseUrl);
35
+ if (configuredBaseUrl) return configuredBaseUrl;
36
+ return PIXVERSE_BASE_URL_BY_REGION[resolvePixVerseApiRegion(provider?.region)];
37
+ }
38
+ function resolvePixVerseApiRegion(value) {
39
+ const region = normalizeOptionalString(value)?.toLowerCase();
40
+ switch (region) {
41
+ case "cn":
42
+ case "china":
43
+ case "mainland":
44
+ case "pai": return "cn";
45
+ case "global":
46
+ case "intl":
47
+ case "international":
48
+ case void 0: return DEFAULT_PIXVERSE_REGION;
49
+ default: throw new Error(`Unsupported PixVerse API region "${region}". Use "international" or "cn".`);
50
+ }
51
+ }
52
+ function normalizePixVerseModel(model) {
53
+ return (normalizeOptionalString(model)?.replace(/^pixverse\//iu, ""))?.toLowerCase() || "v6";
54
+ }
55
+ function resolvePixVerseQuality(req) {
56
+ const requested = normalizeOptionalString(req.providerOptions?.quality) ?? normalizeOptionalString(req.resolution) ?? normalizeOptionalString(req.size);
57
+ if (!requested) return DEFAULT_PIXVERSE_QUALITY;
58
+ const normalized = requested.toLowerCase() === "480p" ? "540p" : requested.toLowerCase();
59
+ return PIXVERSE_QUALITIES.includes(normalized) ? normalized : DEFAULT_PIXVERSE_QUALITY;
60
+ }
61
+ function resolvePixVerseDurationSeconds(value) {
62
+ if (typeof value !== "number" || !Number.isFinite(value)) return 5;
63
+ return Math.max(1, Math.min(MAX_DURATION_SECONDS, Math.round(value)));
64
+ }
65
+ function appendOptionalNumber(body, key, value) {
66
+ const numberValue = asFiniteNumber(value);
67
+ if (numberValue != null) body[key] = numberValue;
68
+ }
69
+ function appendOptionalString(body, key, value) {
70
+ const stringValue = normalizeOptionalString(value);
71
+ if (stringValue) body[key] = stringValue;
72
+ }
73
+ function buildPixVerseHeaders(headers, contentType) {
74
+ const next = new Headers(headers);
75
+ next.set("Ai-trace-id", randomUUID());
76
+ if (contentType) next.set("Content-Type", contentType);
77
+ else next.delete("Content-Type");
78
+ return next;
79
+ }
80
+ function readPixVerseSuccess(payload, label) {
81
+ if (!payload || typeof payload !== "object") throw new Error(`${label}: malformed JSON response`);
82
+ if (asFiniteNumber(payload.ErrCode) !== 0) {
83
+ const message = normalizeOptionalString(payload.ErrMsg) ?? `ErrCode ${String(payload.ErrCode)}`;
84
+ throw new Error(`${label}: ${message}`);
85
+ }
86
+ if (payload.Resp === void 0 || payload.Resp === null) throw new Error(`${label}: response missing Resp`);
87
+ return payload.Resp;
88
+ }
89
+ async function readPixVerseJson(response, label) {
90
+ let payload;
91
+ try {
92
+ payload = await response.json();
93
+ } catch (cause) {
94
+ throw new Error(`${label}: malformed JSON response`, { cause });
95
+ }
96
+ return readPixVerseSuccess(payload, label);
97
+ }
98
+ function readPixVerseVideoId(payload) {
99
+ const videoId = asFiniteNumber(payload.video_id);
100
+ if (videoId == null) throw new Error("PixVerse video generation response missing video_id");
101
+ return videoId;
102
+ }
103
+ function readPixVerseImageId(payload) {
104
+ const imageId = asFiniteNumber(payload.img_id);
105
+ if (imageId == null) throw new Error("PixVerse image upload response missing img_id");
106
+ return imageId;
107
+ }
108
+ function readPixVerseStatus(payload) {
109
+ const status = asFiniteNumber(payload.status);
110
+ if (status == null) throw new Error("PixVerse video status response missing status");
111
+ return status;
112
+ }
113
+ function buildUploadImageForm(asset) {
114
+ const form = new FormData();
115
+ const url = normalizeOptionalString(asset.url);
116
+ if (url) {
117
+ form.set("image_url", url);
118
+ return form;
119
+ }
120
+ if (!asset.buffer) throw new Error("PixVerse image-to-video input is missing image data.");
121
+ const mimeType = normalizeOptionalString(asset.mimeType) ?? "image/png";
122
+ const extension = extensionForMime(mimeType)?.slice(1) ?? "png";
123
+ const fileName = normalizeOptionalString(asset.fileName) ?? `image.${extension}`;
124
+ const bytes = new Uint8Array(asset.buffer.byteLength);
125
+ bytes.set(asset.buffer);
126
+ form.set("image", new File([bytes], fileName, { type: mimeType }));
127
+ return form;
128
+ }
129
+ function buildVideoBody(req, model, imageId) {
130
+ const options = req.providerOptions ?? {};
131
+ const body = {
132
+ duration: resolvePixVerseDurationSeconds(req.durationSeconds),
133
+ model,
134
+ prompt: req.prompt,
135
+ quality: resolvePixVerseQuality(req)
136
+ };
137
+ if (imageId !== void 0) {
138
+ body.img_id = imageId;
139
+ body.motion_mode = normalizeOptionalString(options.motion_mode) ?? normalizeOptionalString(options.motionMode) ?? "normal";
140
+ } else body.aspect_ratio = normalizeOptionalString(req.aspectRatio) ?? "16:9";
141
+ appendOptionalString(body, "negative_prompt", normalizeOptionalString(options.negative_prompt) ?? normalizeOptionalString(options.negativePrompt));
142
+ appendOptionalString(body, "camera_movement", normalizeOptionalString(options.camera_movement) ?? normalizeOptionalString(options.cameraMovement));
143
+ appendOptionalNumber(body, "template_id", asFiniteNumber(options.template_id) ?? asFiniteNumber(options.templateId));
144
+ appendOptionalNumber(body, "seed", options.seed);
145
+ if (req.audio !== void 0) body.generate_audio_switch = req.audio;
146
+ return body;
147
+ }
148
+ function readPixVerseFailureMessage(payload) {
149
+ switch (readPixVerseStatus(payload)) {
150
+ case 7: return "PixVerse video generation failed content moderation";
151
+ case 8: return "PixVerse video generation failed";
152
+ case 6: return "PixVerse video generation was deleted before completion";
153
+ default: return;
154
+ }
155
+ }
156
+ async function pollPixVerseVideo(params) {
157
+ const readResult = (payload) => readPixVerseSuccess(payload, "PixVerse video status request failed");
158
+ return readResult(await pollProviderOperationJson({
159
+ url: `${params.baseUrl}/video/result/${params.videoId}`,
160
+ headers: () => buildPixVerseHeaders(params.headers),
161
+ deadline: params.deadline,
162
+ defaultTimeoutMs: DEFAULT_TIMEOUT_MS,
163
+ fetchFn: params.fetchFn,
164
+ maxAttempts: MAX_POLL_ATTEMPTS,
165
+ pollIntervalMs: POLL_INTERVAL_MS,
166
+ requestFailedMessage: "PixVerse video status request failed",
167
+ timeoutMessage: `PixVerse video generation task ${params.videoId} did not finish in time`,
168
+ isComplete: (candidate) => readPixVerseStatus(readResult(candidate)) === 1,
169
+ getFailureMessage: (candidate) => readPixVerseFailureMessage(readResult(candidate)),
170
+ allowPrivateNetwork: params.allowPrivateNetwork,
171
+ dispatcherPolicy: params.dispatcherPolicy
172
+ }));
173
+ }
174
+ function extractPixVerseVideo(payload) {
175
+ const url = normalizeOptionalString(payload.url);
176
+ if (!url) throw new Error("PixVerse video generation completed without output URL");
177
+ return {
178
+ url,
179
+ mimeType: "video/mp4",
180
+ fileName: "video-1.mp4",
181
+ metadata: {
182
+ sourceUrl: url,
183
+ outputWidth: asFiniteNumber(payload.outputWidth),
184
+ outputHeight: asFiniteNumber(payload.outputHeight)
185
+ }
186
+ };
187
+ }
188
+ function buildPixVerseVideoGenerationProvider() {
189
+ return {
190
+ id: PIXVERSE_PROVIDER_ID,
191
+ label: "PixVerse",
192
+ defaultModel: "v6",
193
+ defaultTimeoutMs: DEFAULT_TIMEOUT_MS,
194
+ models: [...PIXVERSE_VIDEO_MODELS],
195
+ isConfigured: ({ agentDir }) => isProviderApiKeyConfigured({
196
+ provider: PIXVERSE_PROVIDER_ID,
197
+ agentDir
198
+ }),
199
+ capabilities: {
200
+ generate: {
201
+ maxVideos: 1,
202
+ maxDurationSeconds: MAX_DURATION_SECONDS,
203
+ supportedDurationSeconds: Array.from({ length: MAX_DURATION_SECONDS }, (_, index) => index + 1),
204
+ aspectRatios: [...PIXVERSE_TEXT_ASPECT_RATIOS],
205
+ resolutions: [
206
+ "360P",
207
+ "540P",
208
+ "720P",
209
+ "1080P"
210
+ ],
211
+ supportsAspectRatio: true,
212
+ supportsResolution: true,
213
+ supportsAudio: true,
214
+ providerOptions: {
215
+ seed: "number",
216
+ negative_prompt: "string",
217
+ negativePrompt: "string",
218
+ quality: "string",
219
+ camera_movement: "string",
220
+ cameraMovement: "string",
221
+ template_id: "number",
222
+ templateId: "number"
223
+ }
224
+ },
225
+ imageToVideo: {
226
+ enabled: true,
227
+ maxVideos: 1,
228
+ maxInputImages: 1,
229
+ maxDurationSeconds: MAX_DURATION_SECONDS,
230
+ supportedDurationSeconds: Array.from({ length: MAX_DURATION_SECONDS }, (_, index) => index + 1),
231
+ resolutions: [
232
+ "360P",
233
+ "540P",
234
+ "720P",
235
+ "1080P"
236
+ ],
237
+ supportsResolution: true,
238
+ supportsAudio: true,
239
+ providerOptions: {
240
+ seed: "number",
241
+ negative_prompt: "string",
242
+ negativePrompt: "string",
243
+ quality: "string",
244
+ motion_mode: "string",
245
+ motionMode: "string",
246
+ camera_movement: "string",
247
+ cameraMovement: "string",
248
+ template_id: "number",
249
+ templateId: "number"
250
+ }
251
+ },
252
+ videoToVideo: { enabled: false }
253
+ },
254
+ async generateVideo(req) {
255
+ if ((req.inputVideos?.length ?? 0) > 0) throw new Error("PixVerse video generation does not support video reference inputs.");
256
+ if ((req.inputImages?.length ?? 0) > 1) throw new Error("PixVerse image-to-video supports at most one input image.");
257
+ const auth = await resolveApiKeyForProvider({
258
+ provider: PIXVERSE_PROVIDER_ID,
259
+ cfg: req.cfg,
260
+ agentDir: req.agentDir,
261
+ store: req.authStore
262
+ });
263
+ if (!auth.apiKey) throw new Error("PixVerse API key missing");
264
+ const model = normalizePixVerseModel(req.model);
265
+ const fetchFn = fetch;
266
+ const providerConfig = req.cfg?.models?.providers?.[PIXVERSE_PROVIDER_ID];
267
+ const deadline = createProviderOperationDeadline({
268
+ timeoutMs: req.timeoutMs,
269
+ label: "PixVerse video generation"
270
+ });
271
+ const { baseUrl, allowPrivateNetwork, headers, dispatcherPolicy } = resolveProviderHttpRequestConfig({
272
+ baseUrl: resolvePixVerseBaseUrl(req),
273
+ defaultBaseUrl: DEFAULT_PIXVERSE_BASE_URL,
274
+ request: sanitizeConfiguredModelProviderRequest(providerConfig?.request),
275
+ defaultHeaders: { "API-KEY": auth.apiKey },
276
+ provider: PIXVERSE_PROVIDER_ID,
277
+ capability: "video",
278
+ transport: "http"
279
+ });
280
+ const image = req.inputImages?.[0];
281
+ let imageId;
282
+ if (image) {
283
+ const upload = await postMultipartRequest({
284
+ url: `${baseUrl}/image/upload`,
285
+ headers: buildPixVerseHeaders(headers),
286
+ body: buildUploadImageForm(image),
287
+ timeoutMs: resolveProviderOperationTimeoutMs({
288
+ deadline,
289
+ defaultTimeoutMs: DEFAULT_TIMEOUT_MS
290
+ }),
291
+ fetchFn,
292
+ allowPrivateNetwork,
293
+ dispatcherPolicy
294
+ });
295
+ try {
296
+ await assertOkOrThrowHttpError(upload.response, "PixVerse image upload failed");
297
+ imageId = readPixVerseImageId(await readPixVerseJson(upload.response, "PixVerse image upload failed"));
298
+ } finally {
299
+ await upload.release();
300
+ }
301
+ }
302
+ const endpoint = imageId === void 0 ? "/video/text/generate" : "/video/img/generate";
303
+ const create = await postJsonRequest({
304
+ url: `${baseUrl}${endpoint}`,
305
+ headers: buildPixVerseHeaders(headers, "application/json"),
306
+ body: buildVideoBody(req, model, imageId),
307
+ timeoutMs: resolveProviderOperationTimeoutMs({
308
+ deadline,
309
+ defaultTimeoutMs: DEFAULT_TIMEOUT_MS
310
+ }),
311
+ fetchFn,
312
+ allowPrivateNetwork,
313
+ dispatcherPolicy
314
+ });
315
+ try {
316
+ await assertOkOrThrowHttpError(create.response, "PixVerse video generation failed");
317
+ const videoId = readPixVerseVideoId(await readPixVerseJson(create.response, "PixVerse video generation failed"));
318
+ const completed = await pollPixVerseVideo({
319
+ videoId,
320
+ baseUrl,
321
+ deadline,
322
+ fetchFn,
323
+ allowPrivateNetwork,
324
+ dispatcherPolicy,
325
+ headers
326
+ });
327
+ return {
328
+ videos: [extractPixVerseVideo(completed)],
329
+ model,
330
+ metadata: {
331
+ endpoint,
332
+ videoId,
333
+ status: readPixVerseStatus(completed),
334
+ seed: asFiniteNumber(completed.seed),
335
+ size: asFiniteNumber(completed.size)
336
+ }
337
+ };
338
+ } finally {
339
+ await create.release();
340
+ }
341
+ }
342
+ };
343
+ }
344
+ //#endregion
345
+ export { buildPixVerseVideoGenerationProvider };
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "@openclaw/pixverse-provider",
3
+ "version": "2026.5.27-beta.1",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "@openclaw/pixverse-provider",
9
+ "version": "2026.5.27-beta.1",
10
+ "peerDependencies": {
11
+ "openclaw": ">=2026.5.27-beta.1"
12
+ },
13
+ "peerDependenciesMeta": {
14
+ "openclaw": {
15
+ "optional": true
16
+ }
17
+ }
18
+ }
19
+ }
20
+ }
@@ -0,0 +1,37 @@
1
+ {
2
+ "id": "pixverse",
3
+ "name": "PixVerse",
4
+ "description": "OpenClaw PixVerse video generation provider plugin.",
5
+ "activation": {
6
+ "onStartup": false
7
+ },
8
+ "enabledByDefault": true,
9
+ "providerAuthEnvVars": {
10
+ "pixverse": ["PIXVERSE_API_KEY"]
11
+ },
12
+ "providerAuthChoices": [
13
+ {
14
+ "provider": "pixverse",
15
+ "method": "api-key",
16
+ "choiceId": "pixverse-api-key",
17
+ "choiceLabel": "PixVerse API key",
18
+ "choiceHint": "Wizard prompts for International or CN endpoint.",
19
+ "groupId": "pixverse",
20
+ "groupLabel": "PixVerse",
21
+ "groupHint": "Video generation",
22
+ "onboardingScopes": ["image-generation"],
23
+ "optionKey": "pixverseApiKey",
24
+ "cliFlag": "--pixverse-api-key",
25
+ "cliOption": "--pixverse-api-key <key>",
26
+ "cliDescription": "PixVerse API key"
27
+ }
28
+ ],
29
+ "contracts": {
30
+ "videoGenerationProviders": ["pixverse"]
31
+ },
32
+ "configSchema": {
33
+ "type": "object",
34
+ "additionalProperties": false,
35
+ "properties": {}
36
+ }
37
+ }
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@openclaw/pixverse-provider",
3
+ "version": "2026.5.27-beta.1",
4
+ "description": "OpenClaw PixVerse video generation provider plugin.",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/openclaw/openclaw"
8
+ },
9
+ "type": "module",
10
+ "peerDependencies": {
11
+ "openclaw": ">=2026.5.27-beta.1"
12
+ },
13
+ "peerDependenciesMeta": {
14
+ "openclaw": {
15
+ "optional": true
16
+ }
17
+ },
18
+ "openclaw": {
19
+ "extensions": [
20
+ "./index.ts"
21
+ ],
22
+ "install": {
23
+ "npmSpec": "@openclaw/pixverse-provider",
24
+ "defaultChoice": "npm",
25
+ "minHostVersion": ">=2026.5.27"
26
+ },
27
+ "compat": {
28
+ "pluginApi": ">=2026.5.27-beta.1"
29
+ },
30
+ "build": {
31
+ "openclawVersion": "2026.5.27-beta.1"
32
+ },
33
+ "release": {
34
+ "publishToClawHub": true,
35
+ "publishToNpm": true
36
+ },
37
+ "runtimeExtensions": [
38
+ "./dist/index.js"
39
+ ]
40
+ },
41
+ "files": [
42
+ "dist/**",
43
+ "openclaw.plugin.json",
44
+ "npm-shrinkwrap.json"
45
+ ],
46
+ "bundledDependencies": []
47
+ }