@dai_ming/plugin-deliverables 1.1.7 → 1.1.9

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/index.js CHANGED
@@ -1802,6 +1802,30 @@ function extractDeliverableURL(line) {
1802
1802
  return "";
1803
1803
  }
1804
1804
 
1805
+ function extractAllDeliverableURLs(line) {
1806
+ const text = String(line || "").trim();
1807
+ if (!text) {
1808
+ return [];
1809
+ }
1810
+ const urls = [];
1811
+ const seen = new Set();
1812
+ const markdownLinks = text.matchAll(/\((https?:\/\/[^)\s]+)\)/giu);
1813
+ for (const m of markdownLinks) {
1814
+ const url = m[1].trim();
1815
+ if (url && !seen.has(url)) {
1816
+ seen.add(url);
1817
+ urls.push(url);
1818
+ }
1819
+ }
1820
+ if (urls.length === 0) {
1821
+ const rawLink = text.match(/https?:\/\/\S+/iu);
1822
+ if (rawLink && rawLink[0]) {
1823
+ urls.push(rawLink[0].trim());
1824
+ }
1825
+ }
1826
+ return urls;
1827
+ }
1828
+
1805
1829
  function configuredKBHosts() {
1806
1830
  const cfg = loadDeliverableConfig();
1807
1831
  const values = [
@@ -1943,10 +1967,12 @@ function splitDeliverableMessage(text) {
1943
1967
  const normalizedLine = stripBulletPrefix(line).trim();
1944
1968
  if (normalizedLine) {
1945
1969
  linkLines.push(normalizedLine);
1946
- const url = extractDeliverableURL(normalizedLine);
1947
- if (url) {
1948
- linkUrls.push(url);
1949
- linkItems.push({ line: normalizedLine, url });
1970
+ const urls = extractAllDeliverableURLs(normalizedLine);
1971
+ for (const url of urls) {
1972
+ if (isTrustedDeliverableURL(url)) {
1973
+ linkUrls.push(url);
1974
+ linkItems.push({ line: normalizedLine, url });
1975
+ }
1950
1976
  }
1951
1977
  }
1952
1978
  }
@@ -1965,6 +1991,7 @@ function splitDeliverableMessage(text) {
1965
1991
  linkItems,
1966
1992
  primaryLinkUrl: linkUrls.length > 0 ? linkUrls[0] : "",
1967
1993
  fileLinkUrl: selectPalzFileURL(linkItems),
1994
+ deduplicatedItems: deduplicateLinkItems(linkItems),
1968
1995
  };
1969
1996
  }
1970
1997
 
@@ -2007,6 +2034,31 @@ function selectPalzFileURL(linkItems) {
2007
2034
  return (preview && preview.url) || linkItems[0].url || "";
2008
2035
  }
2009
2036
 
2037
+ function deduplicateLinkItems(linkItems) {
2038
+ if (!Array.isArray(linkItems) || linkItems.length === 0) {
2039
+ return { fileItems: [], textItems: [] };
2040
+ }
2041
+ const byIdentity = new Map();
2042
+ const textItems = [];
2043
+ for (const item of linkItems) {
2044
+ const identity = extractDeliverableIdentity(item && item.url);
2045
+ if (!identity) {
2046
+ textItems.push(item);
2047
+ continue;
2048
+ }
2049
+ const existing = byIdentity.get(identity);
2050
+ if (!existing) {
2051
+ byIdentity.set(identity, item);
2052
+ } else if (/\/documents\/preview\//u.test(String(item.url || ""))) {
2053
+ byIdentity.set(identity, item);
2054
+ }
2055
+ }
2056
+ return {
2057
+ fileItems: Array.from(byIdentity.values()),
2058
+ textItems,
2059
+ };
2060
+ }
2061
+
2010
2062
  function cloneBody(body, content, suffix) {
2011
2063
  const next = {};
2012
2064
  Object.keys(body).forEach((key) => {
@@ -2063,6 +2115,7 @@ function shouldSplitPalzPayload(body) {
2063
2115
  links: split.links,
2064
2116
  primaryLinkUrl: split.primaryLinkUrl,
2065
2117
  fileLinkUrl: split.fileLinkUrl,
2118
+ deduplicatedItems: split.deduplicatedItems,
2066
2119
  };
2067
2120
  }
2068
2121
  return split;
@@ -2157,16 +2210,15 @@ function installPalzFetchPatch(api) {
2157
2210
  return originalFetch(input, init);
2158
2211
  }
2159
2212
 
2213
+ const dedup = split.deduplicatedItems || { fileItems: [], textItems: [] };
2214
+ const hasFileItems = dedup.fileItems.length > 0;
2215
+ const hasTextItems = dedup.textItems.length > 0;
2216
+
2160
2217
  const summaryBody = cloneBody(parsed, split.summary, "__summary");
2161
- const fileLinkUrl = split.fileLinkUrl || "";
2162
- const linksBody = fileLinkUrl
2163
- ? cloneBodyAsFileLink(parsed, fileLinkUrl, "__links")
2164
- : cloneBody(parsed, split.links, "__links");
2165
2218
  const summaryBodyStr = JSON.stringify(summaryBody);
2166
- const linksBodyStr = JSON.stringify(linksBody);
2167
2219
 
2168
2220
  api.logger.info?.(
2169
- `[plugin-deliverables] split deliverable reply injected by plugin-deliverables target=${String(parsed.conversation_id || "")} summaryMsgId=${String(summaryBody.msg_id || "")} linksMsgId=${String(linksBody.msg_id || "")} linksMode=${fileLinkUrl ? "file_url" : "text"}`,
2221
+ `[plugin-deliverables] split deliverable reply injected by plugin-deliverables target=${String(parsed.conversation_id || "")} summaryMsgId=${String(summaryBody.msg_id || "")} fileLinks=${dedup.fileItems.length} textLinks=${dedup.textItems.length}`,
2170
2222
  );
2171
2223
  api.logger.info?.(
2172
2224
  `[plugin-deliverables] palz summary request body_length=${summaryBodyStr.length}\n request_body=${summaryBodyStr}`,
@@ -2176,28 +2228,54 @@ function installPalzFetchPatch(api) {
2176
2228
  const summaryInit = Object.assign({}, baseInit, {
2177
2229
  body: summaryBodyStr,
2178
2230
  });
2179
- const linksInit = Object.assign({}, baseInit, {
2180
- body: linksBodyStr,
2181
- });
2182
2231
 
2183
- const firstResponse = await originalFetch(input, summaryInit);
2184
- const firstResponseText = await readResponseText(firstResponse);
2232
+ const summaryResponse = await originalFetch(input, summaryInit);
2233
+ const summaryResponseText = await readResponseText(summaryResponse);
2185
2234
  api.logger.info?.(
2186
- `[plugin-deliverables] palz summary response status=${firstResponse ? firstResponse.status : "unknown"} ok=${Boolean(firstResponse && firstResponse.ok)}\n response_body=${previewText(firstResponseText)}`,
2235
+ `[plugin-deliverables] palz summary response status=${summaryResponse ? summaryResponse.status : "unknown"} ok=${Boolean(summaryResponse && summaryResponse.ok)}\n response_body=${previewText(summaryResponseText)}`,
2187
2236
  );
2188
- if (!firstResponse || !firstResponse.ok) {
2189
- return firstResponse;
2237
+ if (!summaryResponse || !summaryResponse.ok) {
2238
+ return summaryResponse;
2190
2239
  }
2191
2240
 
2192
- api.logger.info?.(
2193
- `[plugin-deliverables] palz links request body_length=${linksBodyStr.length}\n request_body=${linksBodyStr}`,
2194
- );
2195
- const secondResponse = await originalFetch(input, linksInit);
2196
- const secondResponseText = await readResponseText(secondResponse);
2197
- api.logger.info?.(
2198
- `[plugin-deliverables] palz links response status=${secondResponse ? secondResponse.status : "unknown"} ok=${Boolean(secondResponse && secondResponse.ok)}\n response_body=${previewText(secondResponseText)}`,
2199
- );
2200
- return secondResponse;
2241
+ let lastResponse = summaryResponse;
2242
+
2243
+ for (let idx = 0; idx < dedup.fileItems.length; idx += 1) {
2244
+ const item = dedup.fileItems[idx];
2245
+ const fileBody = cloneBodyAsFileLink(parsed, item.url, `__links_${idx}`);
2246
+ const fileBodyStr = JSON.stringify(fileBody);
2247
+ api.logger.info?.(
2248
+ `[plugin-deliverables] palz file_url request [${idx}] body_length=${fileBodyStr.length} url=${item.url}\n request_body=${fileBodyStr}`,
2249
+ );
2250
+ const fileInit = Object.assign({}, baseInit, { body: fileBodyStr });
2251
+ const fileResponse = await originalFetch(input, fileInit);
2252
+ const fileResponseText = await readResponseText(fileResponse);
2253
+ api.logger.info?.(
2254
+ `[plugin-deliverables] palz file_url response [${idx}] status=${fileResponse ? fileResponse.status : "unknown"} ok=${Boolean(fileResponse && fileResponse.ok)}\n response_body=${previewText(fileResponseText)}`,
2255
+ );
2256
+ if (!fileResponse || !fileResponse.ok) {
2257
+ return fileResponse;
2258
+ }
2259
+ lastResponse = fileResponse;
2260
+ }
2261
+
2262
+ if (hasTextItems) {
2263
+ const textContent = dedup.textItems.map((item) => item.line).join("\n");
2264
+ const textBody = cloneBody(parsed, textContent, "__links_text");
2265
+ const textBodyStr = JSON.stringify(textBody);
2266
+ api.logger.info?.(
2267
+ `[plugin-deliverables] palz text links request body_length=${textBodyStr.length}\n request_body=${textBodyStr}`,
2268
+ );
2269
+ const textInit = Object.assign({}, baseInit, { body: textBodyStr });
2270
+ const textResponse = await originalFetch(input, textInit);
2271
+ const textResponseText = await readResponseText(textResponse);
2272
+ api.logger.info?.(
2273
+ `[plugin-deliverables] palz text links response status=${textResponse ? textResponse.status : "unknown"} ok=${Boolean(textResponse && textResponse.ok)}\n response_body=${previewText(textResponseText)}`,
2274
+ );
2275
+ lastResponse = textResponse;
2276
+ }
2277
+
2278
+ return lastResponse;
2201
2279
  };
2202
2280
 
2203
2281
  globalThis.fetch = patchedFetch;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plugin-deliverables",
3
- "version": "1.1.7",
3
+ "version": "1.1.9",
4
4
  "npm_package": "@dai_ming/plugin-deliverables",
5
5
  "description": "Deliverables plugin: native upload tool + skill + AGENTS rules for AI-generated file uploads",
6
6
  "skills": {
@@ -2,7 +2,7 @@
2
2
  "id": "plugin-deliverables",
3
3
  "name": "Deliverables",
4
4
  "description": "Deliverables runtime guard for upload-first file delivery with Palz split-send diagnostics.",
5
- "version": "1.1.7",
5
+ "version": "1.1.9",
6
6
  "skills": ["./skills"],
7
7
  "configSchema": {
8
8
  "type": "object",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dai_ming/plugin-deliverables",
3
- "version": "1.1.7",
3
+ "version": "1.1.9",
4
4
  "description": "OpenClaw deliverables native plugin — upload AI-generated files to OSS and return shareable preview/download links",
5
5
  "keywords": [
6
6
  "openclaw",