@openclaw/novita-provider 2026.11.3 → 2026.11.5-beta.2

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/README.md CHANGED
@@ -2,6 +2,9 @@
2
2
 
3
3
  Official OpenClaw provider plugin for NovitaAI's OpenAI-compatible API.
4
4
 
5
+ Video task polling honors the provider's private-network policy even without a
6
+ proxy override. Generated video downloads keep their separate asset boundary.
7
+
5
8
  ## Install
6
9
 
7
10
  ```sh
@@ -0,0 +1 @@
1
+ {"version":3,"entries":[{"output":"index.js","source":"extensions/novita/index.ts"},{"output":"video-generation-provider.js","source":"extensions/novita/video-generation-provider.ts"}],"chunks":[{"output":"index.js","sha256":"e96c2caf023d52b0d2aae8f343a3528c2d1bc2239450d733187376127bd63f1b","facadeSource":"extensions/novita/index.ts","sources":["extensions/novita/index.ts","extensions/novita/openclaw.plugin.json"]},{"output":"video-generation-provider.js","sha256":"28831dd0b6a0a2ae3b4572c3bb30de2ee81fe91255eaa0871844a3041d3793f9","facadeSource":"extensions/novita/video-generation-provider.ts","sources":["extensions/novita/video-generation-provider.ts"]}],"unrenderedSources":[]}
@@ -1,8 +1,7 @@
1
- import { toImageDataUrl } from "openclaw/plugin-sdk/image-generation";
1
+ import { resolveImageSourceUrl } from "openclaw/plugin-sdk/image-generation";
2
2
  import { downloadGeneratedVideoAsset, resolveGeneratedMediaMaxBytes } from "openclaw/plugin-sdk/media-generation-runtime";
3
3
  import { isProviderApiKeyConfiguredAsync } from "openclaw/plugin-sdk/provider-auth";
4
- import { resolveApiKeyForProvider } from "openclaw/plugin-sdk/provider-auth-runtime";
5
- import { assertOkOrThrowHttpError, createProviderOperationDeadline, createProviderOperationTimeoutResolver, pollProviderOperationJson, postJsonRequest, readProviderJsonObjectResponse, resolveProviderHttpRequestConfig, sanitizeConfiguredModelProviderRequest } from "openclaw/plugin-sdk/provider-http";
4
+ import { createProviderOperationDeadline, createProviderOperationTimeoutResolver, pollProviderOperationJson, postJsonResponse, resolveProviderApiKeyHttpRequestConfig, sanitizeConfiguredModelProviderRequest } from "openclaw/plugin-sdk/provider-http";
6
5
  import { isRecord, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
7
6
  //#region extensions/novita/video-generation-provider.ts
8
7
  const DEFAULT_BASE_URL = "https://api.novita.ai";
@@ -121,10 +120,6 @@ const MODEL_CATALOG = {
121
120
  }
122
121
  }
123
122
  };
124
- function nearestDuration(value, allowed) {
125
- const requested = typeof value === "number" && Number.isFinite(value) ? value : allowed[0];
126
- return allowed.reduce((best, current) => Math.abs(current - requested) < Math.abs(best - requested) ? current : best);
127
- }
128
123
  function resolveModel(req) {
129
124
  const model = normalizeOptionalString(req.model) ?? DEFAULT_MODEL;
130
125
  if (!Object.hasOwn(MODEL_CATALOG, model)) throw new Error(`Novita video generation does not support model ${model}.`);
@@ -135,18 +130,6 @@ function resolveModel(req) {
135
130
  if (images[0]?.role && images[0].role !== "first_frame") throw new Error("Novita image-to-video supports only an ordinary or first_frame image.");
136
131
  return images.length > 0 ? model.replace(/-t2v$/, "-i2v") : model;
137
132
  }
138
- function resolveImage(req) {
139
- const image = req.inputImages?.[0];
140
- if (!image) return;
141
- const url = normalizeOptionalString(image.url);
142
- if (url) return url;
143
- if (!image.buffer) throw new Error("Novita image-to-video input is missing image data.");
144
- return toImageDataUrl({
145
- ...image,
146
- buffer: image.buffer,
147
- defaultMimeType: "image/png"
148
- });
149
- }
150
133
  function resolveResolution(req, wan) {
151
134
  return normalizeOptionalString(req.resolution)?.toUpperCase() === "1080P" ? "1080P" : wan ? "720P" : "768P";
152
135
  }
@@ -160,18 +143,14 @@ function resolveWanSize(req) {
160
143
  }
161
144
  function buildBody(req, model) {
162
145
  const wan = model.startsWith("wan");
163
- const image = resolveImage(req);
146
+ const image = resolveImageSourceUrl(req.inputImages?.[0], "Novita image-to-video input is missing image data.");
164
147
  const options = req.providerOptions ?? {};
165
148
  const mode = image ? "imageToVideo" : "generate";
166
149
  const schema = MODEL_CATALOG[model]?.capabilities?.[mode]?.providerOptions ?? {};
167
150
  for (const [key, value] of Object.entries(options)) if (typeof value !== schema[key]) throw new Error(`Novita ${model} does not support provider option ${key} with this value.`);
168
151
  const audioInputs = req.inputAudios ?? [];
169
152
  if (audioInputs.length > (wan ? 1 : 0)) throw new Error(`Novita ${model} supports ${wan ? "at most one" : "no"} audio reference input.`);
170
- const duration = nearestDuration(req.durationSeconds, wan ? [
171
- 5,
172
- 10,
173
- 15
174
- ] : [6, 10]);
153
+ const duration = req.durationSeconds ?? (wan ? 5 : 6);
175
154
  const resolution = resolveResolution(req, wan);
176
155
  if (!wan) {
177
156
  if (duration === 10 && resolution === "1080P") throw new Error("Novita Hailuo 1080P requires a 6-second duration; use 768P for 10 seconds.");
@@ -240,13 +219,17 @@ function buildNovitaVideoGenerationProvider() {
240
219
  async generateVideo(req) {
241
220
  const model = resolveModel(req);
242
221
  const body = buildBody(req, model);
243
- const auth = await resolveApiKeyForProvider({
222
+ const providerConfig = req.cfg.models?.providers?.novita;
223
+ const configuredBase = normalizeOptionalString(providerConfig?.baseUrl)?.replace(/\/+$/, "").replace(/\/openai\/v1$/, "");
224
+ const { baseUrl, headers, allowPrivateNetwork, dispatcherPolicy } = await resolveProviderApiKeyHttpRequestConfig({
225
+ req,
226
+ missingApiKeyMessage: "Novita API key missing",
227
+ baseUrl: configuredBase ?? DEFAULT_BASE_URL,
228
+ defaultBaseUrl: DEFAULT_BASE_URL,
244
229
  provider: "novita",
245
- cfg: req.cfg,
246
- agentDir: req.agentDir,
247
- store: req.authStore
230
+ capability: "video",
231
+ request: sanitizeConfiguredModelProviderRequest(providerConfig?.request)
248
232
  });
249
- if (!auth.apiKey) throw new Error("Novita API key missing");
250
233
  const deadline = createProviderOperationDeadline({
251
234
  timeoutMs: req.timeoutMs ?? DEFAULT_OPERATION_TIMEOUT_MS,
252
235
  label: "Novita video generation"
@@ -255,43 +238,25 @@ function buildNovitaVideoGenerationProvider() {
255
238
  deadline,
256
239
  defaultTimeoutMs: DEFAULT_TIMEOUT_MS
257
240
  });
258
- const providerConfig = req.cfg.models?.providers?.novita;
259
- const configuredBase = normalizeOptionalString(providerConfig?.baseUrl)?.replace(/\/+$/, "").replace(/\/openai\/v1$/, "");
260
- const { baseUrl, headers, allowPrivateNetwork, dispatcherPolicy } = resolveProviderHttpRequestConfig({
261
- baseUrl: configuredBase ?? DEFAULT_BASE_URL,
262
- defaultBaseUrl: DEFAULT_BASE_URL,
263
- defaultHeaders: {
264
- Authorization: `Bearer ${auth.apiKey}`,
265
- "Content-Type": "application/json"
266
- },
267
- provider: "novita",
268
- capability: "video",
269
- transport: "http",
270
- request: sanitizeConfiguredModelProviderRequest(providerConfig?.request)
271
- });
272
241
  const endpoint = `/v3/async/${model}`;
273
- const { response, release } = await postJsonRequest({
242
+ const submitted = await postJsonResponse({
274
243
  url: `${baseUrl}${endpoint}`,
275
244
  headers,
276
245
  body,
277
246
  timeoutMs: timeoutMs(),
278
247
  fetchFn: fetch,
279
248
  allowPrivateNetwork,
280
- dispatcherPolicy
281
- });
282
- let taskId;
283
- try {
284
- await assertOkOrThrowHttpError(response, "Novita video generation failed", {
249
+ dispatcherPolicy,
250
+ errorLabel: "Novita video generation failed",
251
+ errorOptions: {
285
252
  bodyTimeoutMs: timeoutMs,
286
253
  requestHeaders: headers
287
- });
288
- const submitted = await readProviderJsonObjectResponse(response, "Novita video generation failed", { timeoutMs });
289
- const id = normalizeOptionalString(submitted.task_id);
290
- if (!id) throw new Error("Novita video generation response missing task_id");
291
- taskId = id;
292
- } finally {
293
- await release();
294
- }
254
+ },
255
+ readOptions: { timeoutMs },
256
+ objectResponse: true
257
+ });
258
+ const taskId = normalizeOptionalString(submitted.task_id);
259
+ if (!taskId) throw new Error("Novita video generation response missing task_id");
295
260
  const pollUrl = new URL(`${baseUrl}/v3/async/task-result`);
296
261
  pollUrl.searchParams.set("task_id", taskId);
297
262
  const completed = await pollProviderOperationJson({
@@ -300,7 +265,7 @@ function buildNovitaVideoGenerationProvider() {
300
265
  deadline,
301
266
  defaultTimeoutMs: DEFAULT_TIMEOUT_MS,
302
267
  fetchFn: fetch,
303
- allowPrivateNetwork,
268
+ ssrfPolicy: { allowPrivateNetwork },
304
269
  dispatcherPolicy,
305
270
  maxAttempts: 120,
306
271
  pollIntervalMs: 5e3,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/novita-provider",
3
- "version": "2026.11.3",
3
+ "version": "2026.11.5-beta.2",
4
4
  "description": "OpenClaw NovitaAI provider plugin",
5
5
  "repository": {
6
6
  "type": "git",
@@ -18,10 +18,10 @@
18
18
  "minHostVersion": ">=2026.7.2"
19
19
  },
20
20
  "compat": {
21
- "pluginApi": ">=2026.11.3"
21
+ "pluginApi": ">=2026.11.5-beta.2"
22
22
  },
23
23
  "build": {
24
- "openclawVersion": "2026.11.3",
24
+ "openclawVersion": "2026.11.5-beta.2",
25
25
  "bundledDist": false
26
26
  },
27
27
  "release": {
@@ -40,7 +40,7 @@
40
40
  "assets/activity.svg"
41
41
  ],
42
42
  "peerDependencies": {
43
- "openclaw": ">=2026.11.3"
43
+ "openclaw": ">=2026.11.5-beta.2"
44
44
  },
45
45
  "peerDependenciesMeta": {
46
46
  "openclaw": {