@nextblock-cms/cortex 0.20.1 → 0.21.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.
Files changed (71) hide show
  1. package/client.cjs.js +1 -1
  2. package/client.es.js +3 -7
  3. package/index.cjs.js +1 -1
  4. package/index.es.js +24 -277
  5. package/lib/ai-block-generation.cjs.js +3 -4
  6. package/lib/ai-block-generation.es.js +143 -182
  7. package/lib/ai-client.cjs.js +1 -1
  8. package/lib/ai-client.d.ts +1 -1
  9. package/lib/ai-client.es.js +101 -135
  10. package/lib/ai-config.cjs.js +1 -1
  11. package/lib/ai-config.es.js +89 -108
  12. package/lib/ai-cortex-widget-builder.cjs.js +1 -1
  13. package/lib/ai-cortex-widget-builder.es.js +56 -70
  14. package/lib/ai-global-agent-content-ops-tools.cjs.js +1 -1
  15. package/lib/ai-global-agent-content-ops-tools.d.ts +1943 -68
  16. package/lib/ai-global-agent-content-ops-tools.es.js +185 -199
  17. package/lib/ai-global-agent-custom-block-tools.cjs.js +1 -1
  18. package/lib/ai-global-agent-custom-block-tools.d.ts +2160 -95
  19. package/lib/ai-global-agent-custom-block-tools.es.js +226 -223
  20. package/lib/ai-global-agent-db-tools.cjs.js +1 -1
  21. package/lib/ai-global-agent-db-tools.d.ts +2708 -98
  22. package/lib/ai-global-agent-db-tools.es.js +1013 -986
  23. package/lib/ai-global-agent-ecommerce.cjs.js +1 -1
  24. package/lib/ai-global-agent-ecommerce.es.js +3 -7
  25. package/lib/ai-global-agent-site-tools.cjs.js +2 -3
  26. package/lib/ai-global-agent-site-tools.d.ts +10695 -500
  27. package/lib/ai-global-agent-site-tools.es.js +750 -757
  28. package/lib/ai-global-agent-theming-tools.cjs.js +1 -3
  29. package/lib/ai-global-agent-theming-tools.d.ts +2905 -105
  30. package/lib/ai-global-agent-theming-tools.es.js +326 -396
  31. package/lib/ai-global-agent-tools.cjs.js +2 -2
  32. package/lib/ai-global-agent-tools.d.ts +4 -2230
  33. package/lib/ai-global-agent-tools.es.js +3899 -4328
  34. package/lib/ai-key-crypto.cjs.js +1 -1
  35. package/lib/ai-key-crypto.es.js +63 -82
  36. package/lib/ai-key-verification.cjs.js +1 -1
  37. package/lib/ai-key-verification.es.js +74 -63
  38. package/lib/ai-model-catalog.cjs.js +1 -1
  39. package/lib/ai-model-catalog.es.js +18 -22
  40. package/lib/ai-model-registry.cjs.js +2 -2
  41. package/lib/ai-model-registry.es.js +246 -309
  42. package/lib/ai-seo-metadata.cjs.js +2 -3
  43. package/lib/ai-seo-metadata.es.js +143 -185
  44. package/lib/ai-vision.cjs.js +2 -2
  45. package/lib/ai-vision.d.ts +6 -6
  46. package/lib/ai-vision.es.js +161 -215
  47. package/lib/block-content-schemas.cjs.js +1 -1
  48. package/lib/block-content-schemas.es.js +324 -254
  49. package/lib/cortex-widget-registry.cjs.js +1 -1
  50. package/lib/cortex-widget-registry.es.js +35 -43
  51. package/lib/cortex-widget-schema.cjs.js +2 -3
  52. package/lib/cortex-widget-schema.es.js +280 -309
  53. package/lib/editor-document-from-html.cjs.js +1 -1
  54. package/lib/editor-document-from-html.es.js +334 -278
  55. package/lib/fetch-host-guard.cjs.js +1 -0
  56. package/lib/fetch-host-guard.d.ts +2 -0
  57. package/lib/fetch-host-guard.es.js +30 -0
  58. package/lib/mcp-server.cjs.js +1 -1
  59. package/lib/mcp-server.es.js +155 -173
  60. package/lib/mcp-tokens.cjs.js +1 -1
  61. package/lib/mcp-tokens.es.js +95 -107
  62. package/lib/mcp-tool-registry.cjs.js +5 -6
  63. package/lib/mcp-tool-registry.es.js +533 -411
  64. package/lib/site-brief.cjs.js +2 -2
  65. package/lib/site-brief.es.js +102 -108
  66. package/lib/vision-image-download.cjs.js +1 -0
  67. package/lib/vision-image-download.d.ts +43 -0
  68. package/lib/vision-image-download.es.js +120 -0
  69. package/lib/zod-config.cjs.js +1 -1
  70. package/lib/zod-config.es.js +5 -6
  71. package/package.json +8 -8
@@ -1,193 +1,151 @@
1
- import { generateText as p } from "ai";
2
- import { buildCortexAiRoutingPolicy as T, runWithCortexAiModelFallback as h, omitUnsupportedCortexAiModelOptions as S, getHttpStatusCode as y, isOpenRouterRecoverableRoutingError as E } from "./ai-model-registry.es.js";
3
- import "./zod-config.es.js";
4
- import { z as l } from "zod";
5
- const b = "Cortex AI SEO metadata generation can only be imported from server-side code.";
6
- function O() {
7
- if (!(typeof window > "u"))
8
- throw new Error(b);
1
+ import { buildCortexAiRoutingPolicy as e, getHttpStatusCode as t, isOpenRouterRecoverableRoutingError as n, omitUnsupportedCortexAiModelOptions as r, runWithCortexAiModelFallback as i } from "./ai-model-registry.es.js";
2
+ import { z as a } from "./zod-config.es.js";
3
+ import { generateText as o } from "ai";
4
+ //#region libs/cortex/src/lib/ai-seo-metadata.ts
5
+ var s = "Cortex AI SEO metadata generation can only be imported from server-side code.";
6
+ function c() {
7
+ if (typeof window < "u") throw Error(s);
9
8
  }
10
- const w = l.strictObject({
11
- metaDescription: l.string().trim().min(1),
12
- metaTitle: l.string().trim().min(1),
13
- ogDescription: l.string().trim().min(1),
14
- ogTitle: l.string().trim().min(1)
15
- }), c = {
16
- metaDescription: 160,
17
- metaTitle: 60,
18
- ogDescription: 200,
19
- ogTitle: 88
20
- }, A = 6e3, D = 6e4, v = /[\s.,;:!?‐-―\-/\\|&+*_"'([{·•]+$/;
21
- function d(e, t) {
22
- const n = e.trim();
23
- if (t <= 0)
24
- return "";
25
- if (n.length <= t)
26
- return n;
27
- const o = n.slice(0, t), i = o.search(/\s\S*$/);
28
- return (i > 0 ? o.slice(0, i) : o).replace(v, "").trim();
9
+ var l = a.strictObject({
10
+ metaDescription: a.string().trim().min(1),
11
+ metaTitle: a.string().trim().min(1),
12
+ ogDescription: a.string().trim().min(1),
13
+ ogTitle: a.string().trim().min(1)
14
+ }), u = {
15
+ metaDescription: 160,
16
+ metaTitle: 60,
17
+ ogDescription: 200,
18
+ ogTitle: 88
19
+ }, d = 6e3, f = 6e4, p = /[\s.,;:!?‐-―\-/\\|&+*_"'([{·•]+$/;
20
+ function m(e, t) {
21
+ let n = e.trim();
22
+ if (t <= 0) return "";
23
+ if (n.length <= t) return n;
24
+ let r = n.slice(0, t), i = r.search(/\s\S*$/);
25
+ return (i > 0 ? r.slice(0, i) : r).replace(p, "").trim();
29
26
  }
30
- function x(e) {
31
- const t = e.replace(/```[a-zA-Z0-9]*/g, ""), n = t.indexOf("{");
32
- if (n === -1)
33
- return null;
34
- let o = 0, i = !1, r = !1;
35
- for (let s = n; s < t.length; s++) {
36
- const a = t[s];
37
- if (i) {
38
- r ? r = !1 : a === "\\" ? r = !0 : a === '"' && (i = !1);
39
- continue;
40
- }
41
- if (a === '"') {
42
- i = !0;
43
- continue;
44
- }
45
- if (a === "{") {
46
- o++;
47
- continue;
48
- }
49
- if (a === "}" && (o--, o === 0))
50
- return t.slice(n, s + 1);
51
- }
52
- return null;
27
+ function h(e) {
28
+ let t = e.replace(/```[a-zA-Z0-9]*/g, ""), n = t.indexOf("{");
29
+ if (n === -1) return null;
30
+ let r = 0, i = !1, a = !1;
31
+ for (let e = n; e < t.length; e++) {
32
+ let o = t[e];
33
+ if (i) {
34
+ a ? a = !1 : o === "\\" ? a = !0 : o === "\"" && (i = !1);
35
+ continue;
36
+ }
37
+ if (o === "\"") {
38
+ i = !0;
39
+ continue;
40
+ }
41
+ if (o === "{") {
42
+ r++;
43
+ continue;
44
+ }
45
+ if (o === "}" && (r--, r === 0)) return t.slice(n, e + 1);
46
+ }
47
+ return null;
53
48
  }
54
- function _() {
55
- const e = c;
56
- return [
57
- "You are NextBlock Cortex AI, an SEO metadata writer for a content management system.",
58
- "Return ONLY a single JSON object. No markdown fences, no commentary, no explanation, no trailing prose.",
59
- 'The object must have exactly these four keys, all strings, all non-empty: "metaDescription", "metaTitle", "ogDescription", "ogTitle".',
60
- "Do not add any other key.",
61
- `metaTitle must be at most ${e.metaTitle} characters and read as a search-result headline for this specific page.`,
62
- `metaDescription must be at most ${e.metaDescription} characters, summarise what the page actually delivers, and end as a complete thought.`,
63
- `ogTitle must be at most ${e.ogTitle} characters and may be slightly more conversational than metaTitle, because it is read on a social card rather than in a results list.`,
64
- `ogDescription must be at most ${e.ogDescription} characters and should invite a click without over-promising.`,
65
- "Write plain text. No emoji, no surrounding quotation marks, no HTML, no markdown.",
66
- "Describe only what the supplied content actually contains. Never invent statistics, prices, dates, awards, or claims that are not in the content.",
67
- "Never keyword-stuff, never repeat the focus keyword more than twice across all four fields, and never pad a field to reach its character budget."
68
- ].join(" ");
49
+ function g() {
50
+ let e = u;
51
+ return [
52
+ "You are NextBlock Cortex AI, an SEO metadata writer for a content management system.",
53
+ "Return ONLY a single JSON object. No markdown fences, no commentary, no explanation, no trailing prose.",
54
+ "The object must have exactly these four keys, all strings, all non-empty: \"metaDescription\", \"metaTitle\", \"ogDescription\", \"ogTitle\".",
55
+ "Do not add any other key.",
56
+ `metaTitle must be at most ${e.metaTitle} characters and read as a search-result headline for this specific page.`,
57
+ `metaDescription must be at most ${e.metaDescription} characters, summarise what the page actually delivers, and end as a complete thought.`,
58
+ `ogTitle must be at most ${e.ogTitle} characters and may be slightly more conversational than metaTitle, because it is read on a social card rather than in a results list.`,
59
+ `ogDescription must be at most ${e.ogDescription} characters and should invite a click without over-promising.`,
60
+ "Write plain text. No emoji, no surrounding quotation marks, no HTML, no markdown.",
61
+ "Describe only what the supplied content actually contains. Never invent statistics, prices, dates, awards, or claims that are not in the content.",
62
+ "Never keyword-stuff, never repeat the focus keyword more than twice across all four fields, and never pad a field to reach its character budget."
63
+ ].join(" ");
69
64
  }
70
- function C(e) {
71
- return [
72
- "Write SEO metadata for the following page.",
73
- e.title ? `Page title: ${e.title}` : null,
74
- e.siteTitle ? `Site name: ${e.siteTitle}. Do not append the site name to metaTitle; the site appends it separately.` : null,
75
- e.focusKeyword ? `Focus keyword: ${e.focusKeyword}. Use it naturally in metaTitle and metaDescription, at most once each.` : null,
76
- e.locale ? `Write every field in this locale: ${e.locale}. Match its conventions for capitalisation and punctuation.` : null,
77
- `Page content:
78
- ${e.content}`
79
- ].filter(Boolean).join(`
80
-
81
- `);
65
+ function _(e) {
66
+ return [
67
+ "Write SEO metadata for the following page.",
68
+ e.title ? `Page title: ${e.title}` : null,
69
+ e.siteTitle ? `Site name: ${e.siteTitle}. Do not append the site name to metaTitle; the site appends it separately.` : null,
70
+ e.focusKeyword ? `Focus keyword: ${e.focusKeyword}. Use it naturally in metaTitle and metaDescription, at most once each.` : null,
71
+ e.locale ? `Write every field in this locale: ${e.locale}. Match its conventions for capitalisation and punctuation.` : null,
72
+ `Page content:\n${e.content}`
73
+ ].filter(Boolean).join("\n\n");
82
74
  }
83
- function I(e) {
84
- const t = x(e);
85
- if (!t)
86
- throw new Error("Cortex AI returned no JSON object for the SEO metadata request.");
87
- let n;
88
- try {
89
- n = JSON.parse(t);
90
- } catch (i) {
91
- throw new Error(
92
- `Cortex AI returned invalid JSON for the SEO metadata request: ${i instanceof Error ? i.message : String(i)}`
93
- );
94
- }
95
- const o = w.parse(n);
96
- return {
97
- metaDescription: d(
98
- o.metaDescription,
99
- c.metaDescription
100
- ),
101
- metaTitle: d(
102
- o.metaTitle,
103
- c.metaTitle
104
- ),
105
- ogDescription: d(
106
- o.ogDescription,
107
- c.ogDescription
108
- ),
109
- ogTitle: d(
110
- o.ogTitle,
111
- c.ogTitle
112
- )
113
- };
75
+ function v(e) {
76
+ let t = h(e);
77
+ if (!t) throw Error("Cortex AI returned no JSON object for the SEO metadata request.");
78
+ let n;
79
+ try {
80
+ n = JSON.parse(t);
81
+ } catch (e) {
82
+ throw Error(`Cortex AI returned invalid JSON for the SEO metadata request: ${e instanceof Error ? e.message : String(e)}`);
83
+ }
84
+ let r = l.parse(n);
85
+ return {
86
+ metaDescription: m(r.metaDescription, u.metaDescription),
87
+ metaTitle: m(r.metaTitle, u.metaTitle),
88
+ ogDescription: m(r.ogDescription, u.ogDescription),
89
+ ogTitle: m(r.ogTitle, u.ogTitle)
90
+ };
114
91
  }
115
- function R(e) {
116
- const t = y(e);
117
- if (t === 401 || t === 402 || t === 403)
118
- return !1;
119
- if (E(e) || t && t >= 500)
120
- return !0;
121
- const n = e instanceof Error ? e.message : String(e);
122
- return /NoContentGenerated|No content generated|Provider returned error|no JSON object|invalid JSON|invalid_type|too_small|unrecognized_keys|Invalid input|aborted|abort|timeout|timed out/i.test(
123
- n
124
- );
92
+ function y(e) {
93
+ let r = t(e);
94
+ if (r === 401 || r === 402 || r === 403) return !1;
95
+ if (n(e) || r && r >= 500) return !0;
96
+ let i = e instanceof Error ? e.message : String(e);
97
+ return /NoContentGenerated|No content generated|Provider returned error|no JSON object|invalid JSON|invalid_type|too_small|unrecognized_keys|Invalid input|aborted|abort|timeout|timed out/i.test(i);
125
98
  }
126
- async function j(e) {
127
- O();
128
- const t = d(e.content, A);
129
- if (!t)
130
- throw new Error("Cortex AI SEO metadata generation requires non-empty page content.");
131
- const { createCortexAiOpenRouterClient: n } = await import("./ai-client.es.js"), o = await n({
132
- apiKey: e.apiKey || void 0
133
- }), i = T({
134
- credentialSource: o.credentialSource,
135
- requestedModelId: e.modelId,
136
- selectedModel: o.modelSelection
137
- }), r = await h({
138
- modelIds: i.modelIds,
139
- shouldRetry: R,
140
- execute: async (s) => {
141
- const a = new AbortController(), m = setTimeout(
142
- () => a.abort(),
143
- D
144
- ), u = () => a.abort();
145
- e.abortSignal?.addEventListener("abort", u, { once: !0 }), e.abortSignal?.aborted && a.abort();
146
- try {
147
- const g = S(
148
- {
149
- abortSignal: a.signal,
150
- maxOutputTokens: 700,
151
- maxRetries: 0,
152
- prompt: C({
153
- content: t,
154
- focusKeyword: e.focusKeyword?.trim() || null,
155
- locale: e.locale?.trim() || null,
156
- siteTitle: e.siteTitle?.trim() || null,
157
- title: e.title?.trim() || null
158
- }),
159
- system: _(),
160
- temperature: 0.3
161
- },
162
- {
163
- modelId: s,
164
- modelSelection: i.modelSelection
165
- }
166
- ), f = await p({
167
- ...g,
168
- model: o.model(s)
169
- });
170
- return I(f.text);
171
- } finally {
172
- clearTimeout(m), e.abortSignal?.removeEventListener("abort", u);
173
- }
174
- }
175
- });
176
- return {
177
- attempts: r.attempts.length,
178
- credentialSource: o.credentialSource,
179
- metaDescription: r.result.metaDescription,
180
- metaTitle: r.result.metaTitle,
181
- modelId: r.modelId,
182
- ogDescription: r.result.ogDescription,
183
- ogTitle: r.result.ogTitle
184
- };
99
+ async function b(t) {
100
+ c();
101
+ let n = m(t.content, d);
102
+ if (!n) throw Error("Cortex AI SEO metadata generation requires non-empty page content.");
103
+ let { createCortexAiOpenRouterClient: a } = await import("./ai-client.es.js"), s = await a({ apiKey: t.apiKey || void 0 }), l = e({
104
+ credentialSource: s.credentialSource,
105
+ requestedModelId: t.modelId,
106
+ selectedModel: s.modelSelection
107
+ }), u = await i({
108
+ modelIds: l.modelIds,
109
+ shouldRetry: y,
110
+ execute: async (e) => {
111
+ let i = new AbortController(), a = setTimeout(() => i.abort(), f), c = () => i.abort();
112
+ t.abortSignal?.addEventListener("abort", c, { once: !0 }), t.abortSignal?.aborted && i.abort();
113
+ try {
114
+ let a = r({
115
+ abortSignal: i.signal,
116
+ maxOutputTokens: 700,
117
+ maxRetries: 0,
118
+ prompt: _({
119
+ content: n,
120
+ focusKeyword: t.focusKeyword?.trim() || null,
121
+ locale: t.locale?.trim() || null,
122
+ siteTitle: t.siteTitle?.trim() || null,
123
+ title: t.title?.trim() || null
124
+ }),
125
+ instructions: g(),
126
+ temperature: .3
127
+ }, {
128
+ modelId: e,
129
+ modelSelection: l.modelSelection
130
+ });
131
+ return v((await o({
132
+ ...a,
133
+ model: s.model(e)
134
+ })).text);
135
+ } finally {
136
+ clearTimeout(a), t.abortSignal?.removeEventListener("abort", c);
137
+ }
138
+ }
139
+ });
140
+ return {
141
+ attempts: u.attempts.length,
142
+ credentialSource: s.credentialSource,
143
+ metaDescription: u.result.metaDescription,
144
+ metaTitle: u.result.metaTitle,
145
+ modelId: u.modelId,
146
+ ogDescription: u.result.ogDescription,
147
+ ogTitle: u.result.ogTitle
148
+ };
185
149
  }
186
- export {
187
- A as CORTEX_AI_SEO_METADATA_CONTENT_BUDGET,
188
- c as CORTEX_AI_SEO_METADATA_LENGTH_BUDGETS,
189
- w as cortexAiSeoMetadataOutputSchema,
190
- x as extractJsonObject,
191
- j as generateCortexAiSeoMetadata,
192
- d as truncateOnWordBoundary
193
- };
150
+ //#endregion
151
+ export { d as CORTEX_AI_SEO_METADATA_CONTENT_BUDGET, u as CORTEX_AI_SEO_METADATA_LENGTH_BUDGETS, l as cortexAiSeoMetadataOutputSchema, h as extractJsonObject, b as generateCortexAiSeoMetadata, m as truncateOnWordBoundary };
@@ -1,3 +1,3 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const C=require("ai"),l=require("./ai-model-registry.cjs.js"),R=require("./ai-seo-metadata.cjs.js"),S="Cortex AI vision generation can only be imported from server-side code.";function L(){if(!(typeof window>"u"))throw new Error(S)}const d=["google/gemma-4-31b-it:free","google/gemma-4-26b-a4b-it:free","google/gemini-2.5-flash-lite","qwen/qwen3-vl-32b-instruct","openai/gpt-4o-mini","google/gemini-2.5-flash"],O=[/(?:^|\/)[^/]*-vl(?:[-:]|$)/i,/vision/i,/(?:^|\/)gemini-/i,/(?:^|\/)gemma-[3-9]/i,/(?:^|\/)gpt-(?:4o|4\.1|5)/i,/(?:^|\/)claude-/i,/(?:^|\/)llama-4/i,/(?:^|\/)pixtral/i,/(?:^|\/)mistral-(?:small|medium|large)-3/i,/(?:^|\/)nova-(?:lite|pro|premier)/i],u=125,p={max:1e3,min:20},M=6e4,T=new Set(['"',"'","«","»","‘","’","‚","“","”","„","`"]),v=/^(?:here(?:'s|’s| is)\s+)?(?:the\s+)?alt(?:[-\s]?text)?\s*[:–—-]\s*/i,N=/^(?:this\s+is\s+)?(?:an?|the)?\s*(?:image|photo|photograph|picture|pic|graphic|illustration|rendering|render)\s+(?:of|showing|depicting|that\s+shows)\s+/i;function X(e){const o=new Set,t=[];for(const n of e){const r=n?.trim();!r||o.has(r)||(o.add(r),t.push(r))}return t}function A(e){const o=e?.trim();return o?d.includes(o)?!0:O.some(t=>t.test(o)):!1}function _(e){const o=e.requestedModelId?.trim()||null,t=e.selectedModel?.modelId?.trim()||null,n=e.credentialSource!=="env"&&t&&A(t)?t:null;return{modelIds:X([o,n,...d])}}function f(e){let o=e.trim();for(;o.length>=2&&T.has(o[0])&&T.has(o[o.length-1]);)o=o.slice(1,-1).trim();return o}function U(e){return!e.endsWith(".")||e.endsWith("...")||e.endsWith("…")?e:(e.match(/[.!?](?=\s|$)/g)||[]).length===1?e.slice(0,-1).trimEnd():e}function b(e,o=u){let t=e.replace(/\s+/g," ").trim(),n=!1;for(let r=0;r<2;r++){t=f(t);const a=t.replace(v,"");a!==t&&(t=a.trim(),n=!0),t=f(t);const i=t.replace(N,"");i!==t&&(t=i.trim(),n=!0)}return n&&t&&(t=t.charAt(0).toUpperCase()+t.slice(1)),R.truncateOnWordBoundary(U(t),o)}function q(e){const o=e.trim();if(!o)throw new Error("Cortex AI alt text generation requires an image URL.");let t;try{t=new URL(o)}catch{throw new Error(`Cortex AI alt text generation requires an absolute http(s) image URL, received "${o}". A storage key or site-relative path must be resolved to a publicly fetchable URL first, because the model provider downloads the image server-side.`)}if(t.protocol!=="http:"&&t.protocol!=="https:")throw new Error(`Cortex AI alt text generation requires an http(s) image URL, received the "${t.protocol}" scheme.`);return t}function P(e){return["You are NextBlock Cortex AI, writing the alt attribute for an image on a website.","You are writing for a person using a screen reader who cannot see the image at all.","Describe only what is visibly present in the image: subjects, their appearance, what they are doing, the setting, and any text that appears in the image.","Never state or guess anything you cannot see — no names, no locations, no dates, no brands, no emotions, and no backstory unless they are legible in the image itself.",`Write one short factual description of at most ${e} characters.`,'Do not begin with "image of", "photo of", "picture of", "graphic of", or any similar phrase; the screen reader already announces that this is an image.','Do not add a label such as "Alt text:" and do not wrap the description in quotation marks.',"Do not keyword-stuff, do not list search terms, and do not repeat a word for emphasis.","Return plain text only: one line, no markdown, no HTML, no JSON, no commentary, no alternatives to choose between.","If surrounding page context is supplied, use it ONLY to disambiguate what you can already see — for example to choose the right word for an object or to know which product is pictured. Never use it to add detail that is not visible in the image."].join(" ")}function D(e){return[`Write the alt attribute for this image, at most ${e.maxLength} characters.`,e.context?`Surrounding page context, for disambiguation only: ${e.context}`:null].filter(Boolean).join(`
1
+ const e=require("./ai-model-registry.cjs.js"),t=require("./ai-seo-metadata.cjs.js");let n=require("ai");var r=`Cortex AI vision generation can only be imported from server-side code.`;function i(){if(typeof window<`u`)throw Error(r)}var a=[`google/gemma-4-31b-it:free`,`google/gemma-4-26b-a4b-it:free`,`google/gemini-2.5-flash-lite`,`qwen/qwen3-vl-32b-instruct`,`openai/gpt-4o-mini`,`google/gemini-2.5-flash`],o=[/(?:^|\/)[^/]*-vl(?:[-:]|$)/i,/vision/i,/(?:^|\/)gemini-/i,/(?:^|\/)gemma-[3-9]/i,/(?:^|\/)gpt-(?:4o|4\.1|5)/i,/(?:^|\/)claude-/i,/(?:^|\/)llama-4/i,/(?:^|\/)pixtral/i,/(?:^|\/)mistral-(?:small|medium|large)-3/i,/(?:^|\/)nova-(?:lite|pro|premier)/i],s=125,c={max:1e3,min:20},l=6e4,u=new Set([`"`,`'`,`«`,`»`,`‘`,`’`,`‚`,`“`,`”`,`„`,"`"]),d=/^(?:here(?:'s|’s| is)\s+)?(?:the\s+)?alt(?:[-\s]?text)?\s*[:–—-]\s*/i,f=/^(?:this\s+is\s+)?(?:an?|the)?\s*(?:image|photo|photograph|picture|pic|graphic|illustration|rendering|render)\s+(?:of|showing|depicting|that\s+shows)\s+/i;function p(e){let t=new Set,n=[];for(let r of e){let e=r?.trim();e&&!t.has(e)&&(t.add(e),n.push(e))}return n}function m(e){let t=e?.trim();return t?a.includes(t)?!0:o.some(e=>e.test(t)):!1}function h(e){let t=e.requestedModelId?.trim()||null,n=e.selectedModel?.modelId?.trim()||null;return{modelIds:p([t,e.credentialSource!==`env`&&n&&m(n)?n:null,...a])}}function g(e){let t=e.trim();for(;t.length>=2&&u.has(t[0])&&u.has(t[t.length-1]);)t=t.slice(1,-1).trim();return t}function _(e){return!e.endsWith(`.`)||e.endsWith(`...`)||e.endsWith(`…`)?e:(e.match(/[.!?](?=\s|$)/g)||[]).length===1?e.slice(0,-1).trimEnd():e}function v(e,n=125){let r=e.replace(/\s+/g,` `).trim(),i=!1;for(let e=0;e<2;e++){r=g(r);let e=r.replace(d,``);e!==r&&(r=e.trim(),i=!0),r=g(r);let t=r.replace(f,``);t!==r&&(r=t.trim(),i=!0)}return i&&r&&(r=r.charAt(0).toUpperCase()+r.slice(1)),t.truncateOnWordBoundary(_(r),n)}function y(e){let t=e.trim();if(!t)throw Error(`Cortex AI alt text generation requires an image URL.`);let n;try{n=new URL(t)}catch{throw Error(`Cortex AI alt text generation requires an absolute http(s) image URL, received "${t}". A storage key or site-relative path must be resolved to a publicly fetchable URL first, because the model provider downloads the image server-side.`)}if(n.protocol!==`http:`&&n.protocol!==`https:`)throw Error(`Cortex AI alt text generation requires an http(s) image URL, received the "${n.protocol}" scheme.`);return n}function b(e){return[`You are NextBlock Cortex AI, writing the alt attribute for an image on a website.`,`You are writing for a person using a screen reader who cannot see the image at all.`,`Describe only what is visibly present in the image: subjects, their appearance, what they are doing, the setting, and any text that appears in the image.`,`Never state or guess anything you cannot see — no names, no locations, no dates, no brands, no emotions, and no backstory unless they are legible in the image itself.`,`Write one short factual description of at most ${e} characters.`,`Do not begin with "image of", "photo of", "picture of", "graphic of", or any similar phrase; the screen reader already announces that this is an image.`,`Do not add a label such as "Alt text:" and do not wrap the description in quotation marks.`,`Do not keyword-stuff, do not list search terms, and do not repeat a word for emphasis.`,`Return plain text only: one line, no markdown, no HTML, no JSON, no commentary, no alternatives to choose between.`,`If surrounding page context is supplied, use it ONLY to disambiguate what you can already see — for example to choose the right word for an object or to know which product is pictured. Never use it to add detail that is not visible in the image.`].join(` `)}function x(e){return[`Write the alt attribute for this image, at most ${e.maxLength} characters.`,e.context?`Surrounding page context, for disambiguation only: ${e.context}`:null].filter(Boolean).join(`
2
2
 
3
- `)}function V(e){const o=l.getHttpStatusCode(e);if(o===401||o===402||o===403)return!1;if(l.isOpenRouterRecoverableRoutingError(e)||o&&o>=500)return!0;const t=e instanceof Error?e.message:String(e);return/NoContentGenerated|No content generated|Provider returned error|empty alt text|image|modality|multimodal|not support|unsupported|aborted|abort|timeout|timed out/i.test(t)}async function k(e){L();const o=q(e.imageUrl),t=Number.isFinite(e.maxLength)?Math.round(e.maxLength):u,n=Math.min(p.max,Math.max(p.min,t)),r=e.context?.replace(/\s+/g," ").trim()||null,{createCortexAiOpenRouterClient:a}=await Promise.resolve().then(()=>require("./ai-client.cjs.js")),i=await a({apiKey:e.apiKey||void 0}),x=_({credentialSource:i.credentialSource,requestedModelId:e.modelId,selectedModel:i.modelSelection}),E=[{content:[{text:D({context:r,maxLength:n}),type:"text"},{image:o,type:"image"}],role:"user"}],c=await l.runWithCortexAiModelFallback({modelIds:x.modelIds,shouldRetry:V,execute:async m=>{const s=new AbortController,I=setTimeout(()=>s.abort(),M),g=()=>s.abort();e.abortSignal?.addEventListener("abort",g,{once:!0}),e.abortSignal?.aborted&&s.abort();try{const w=l.omitUnsupportedCortexAiModelOptions({abortSignal:s.signal,maxOutputTokens:400,maxRetries:0,messages:E,system:P(n),temperature:.2},{modelId:m,modelSelection:i.modelSelection}),y=await C.generateText({...w,model:i.model(m)}),h=b(y.text,n);if(!h)throw new Error("Cortex AI returned empty alt text.");return h}finally{clearTimeout(I),e.abortSignal?.removeEventListener("abort",g)}}});return{altText:c.result,attempts:c.attempts.length,credentialSource:i.credentialSource,modelId:c.modelId}}exports.CORTEX_AI_DEFAULT_ALT_TEXT_MAX_LENGTH=u;exports.CORTEX_AI_VISION_MODEL_FALLBACK_REGISTRY=d;exports.buildCortexAiVisionRoutingPolicy=_;exports.generateCortexAiAltText=k;exports.isKnownVisionCapableCortexAiModelId=A;exports.normalizeGeneratedAltText=b;
3
+ `)}function S(t){let n=e.getHttpStatusCode(t);if(n===401||n===402||n===403)return!1;if(e.isOpenRouterRecoverableRoutingError(t)||n&&n>=500)return!0;let r=t instanceof Error?t.message:String(t);return/NoContentGenerated|No content generated|Provider returned error|empty alt text|image|modality|multimodal|not support|unsupported|aborted|abort|timeout|timed out/i.test(r)}async function C(t){i();let r=y(t.imageUrl),a=Number.isFinite(t.maxLength)?Math.round(t.maxLength):125,o=Math.min(c.max,Math.max(c.min,a)),s=t.context?.replace(/\s+/g,` `).trim()||null,{createCortexAiOpenRouterClient:u}=await Promise.resolve().then(()=>require("./ai-client.cjs.js")),d=await u({apiKey:t.apiKey||void 0}),f=h({credentialSource:d.credentialSource,requestedModelId:t.modelId,selectedModel:d.modelSelection}),{downloadCortexVisionImage:p}=await Promise.resolve().then(()=>require("./vision-image-download.cjs.js")),m=await p(r,{abortSignal:t.abortSignal}),g=[{content:[{text:x({context:s,maxLength:o}),type:`text`},{data:m.data,mediaType:m.mediaType,type:`file`}],role:`user`}],_=await e.runWithCortexAiModelFallback({modelIds:f.modelIds,shouldRetry:S,execute:async r=>{let i=new AbortController,a=setTimeout(()=>i.abort(),l),s=()=>i.abort();t.abortSignal?.addEventListener(`abort`,s,{once:!0}),t.abortSignal?.aborted&&i.abort();try{let t=e.omitUnsupportedCortexAiModelOptions({abortSignal:i.signal,maxOutputTokens:400,maxRetries:0,messages:g,instructions:b(o),temperature:.2},{modelId:r,modelSelection:d.modelSelection}),a=v((await(0,n.generateText)({...t,model:d.model(r)})).text,o);if(!a)throw Error(`Cortex AI returned empty alt text.`);return a}finally{clearTimeout(a),t.abortSignal?.removeEventListener(`abort`,s)}}});return{altText:_.result,attempts:_.attempts.length,credentialSource:d.credentialSource,modelId:_.modelId}}exports.CORTEX_AI_DEFAULT_ALT_TEXT_MAX_LENGTH=s,exports.CORTEX_AI_VISION_MODEL_FALLBACK_REGISTRY=a,exports.buildCortexAiVisionRoutingPolicy=h,exports.generateCortexAiAltText=C,exports.isKnownVisionCapableCortexAiModelId=m,exports.normalizeGeneratedAltText=v;
@@ -122,12 +122,12 @@ export interface GenerateAltTextResult {
122
122
  /**
123
123
  * Generate an `alt` attribute for an image by actually looking at it.
124
124
  *
125
- * The image is attached as an AI SDK v6 image content part inside a user message.
126
- * Two consequences of that are worth knowing at the call site: the URL must be
127
- * publicly fetchable from the server (the SDK downloads it and inlines base64
128
- * rather than forwarding the link, because the provider is created without
129
- * `supportedUrls`), and a very large source image inflates the request body on
130
- * every attempt in the fallback chain. Passing a resized or CDN-transformed URL is
125
+ * The image is downloaded once by `downloadCortexVisionImage` (DNS-pinned, private
126
+ * addresses refused, capped at CORTEX_AI_VISION_MAX_IMAGE_BYTES) and attached as an AI SDK
127
+ * file part with its bytes inside a user message. Two consequences are worth knowing at the
128
+ * call site: the URL must be publicly fetchable from the server, and a very large source
129
+ * image inflates the request body on every attempt in the fallback chain, because the
130
+ * provider receives it inline as base64. Passing a resized or CDN-transformed URL is
131
131
  * therefore materially cheaper than passing the original upload.
132
132
  */
133
133
  export declare function generateCortexAiAltText(params: GenerateAltTextParams): Promise<GenerateAltTextResult>;