@openclaw/signal 2026.7.2-beta.7 → 2026.8.1-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/dist/api.js +7 -7
- package/dist/{approval-handler.runtime-C-XYgcxE.js → approval-handler.runtime-Bi5yNhnj.js} +2 -2
- package/dist/{approval-reactions-DtL47gSK.js → approval-reactions-BulCzhdB.js} +15 -45
- package/dist/{channel-CmbWaYlm.js → channel-CzDJMDtX.js} +50 -91
- package/dist/channel-config-api.js +1 -1
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel.runtime-BRDaqr5-.js → channel.runtime-D12iYzJB.js} +2 -2
- package/dist/{client-BazXTH7O.js → client-CvnR8_XA.js} +31 -34
- package/dist/{client-adapter-mXGeEHQJ.js → client-adapter-D9SNPaNx.js} +13 -6
- package/dist/{config-schema-Y3dFGwdL.js → config-schema-CkMIK7zp.js} +4 -5
- package/dist/contract-api.js +1 -1
- package/dist/{doctor-contract-api-Dcte-QYB.js → doctor-contract-api-Dnp5eQQJ.js} +19 -22
- package/dist/doctor-contract-api.js +1 -1
- package/dist/{install-signal-cli-qJF7iC4h.js → install-signal-cli-Cpiyk0k1.js} +8 -5
- package/dist/{monitor-BvIzkrYW.js → monitor-DiN2hKnG.js} +128 -67
- package/dist/{probe-Br9abaGp.js → probe-C_THuQzn.js} +23 -7
- package/dist/question-reactions-ByjH5qST.js +80 -0
- package/dist/{reaction-runtime-api-BjeDyKcz.js → reaction-runtime-api-Cf6QHcE5.js} +8 -19
- package/dist/reaction-runtime-api.js +1 -1
- package/dist/runtime-api.js +7 -7
- package/dist/{send-B_Ks4MZr.js → send-BvGgByMd.js} +12 -3
- package/dist/{send.runtime-CbuXycZT.js → send.runtime-CtTBBrjL.js} +1 -1
- package/dist/{transport-detection-Da06-LOL.js → transport-detection-BItHawMq.js} +50 -145
- package/dist/{transport-detection.runtime-D_190UUL.js → transport-detection.runtime-DoiwPcrG.js} +1 -1
- package/dist/{transport-probes.runtime-CV6Qs0Og.js → transport-probes.runtime-BsJdp0oI.js} +1 -1
- package/openclaw.plugin.json +4 -1
- package/package.json +5 -5
- package/dist/approval-resolver-B3m0D2R0.js +0 -16
- package/dist/question-reactions-Ds48FtEu.js +0 -109
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
import { a as containerRpcRequest, i as containerCheck, n as signalRpcRequest$1, o as streamContainerEvents, r as streamSignalEvents$1, t as signalCheck$1 } from "./client-
|
|
1
|
+
import { a as containerRpcRequest, i as containerCheck, n as signalRpcRequest$1, o as streamContainerEvents, r as streamSignalEvents$1, t as signalCheck$1 } from "./client-CvnR8_XA.js";
|
|
2
|
+
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
2
3
|
//#region extensions/signal/src/client-adapter.ts
|
|
4
|
+
/**
|
|
5
|
+
* Signal client adapter - unified interface for both native signal-cli and bbernhard container.
|
|
6
|
+
*
|
|
7
|
+
* This adapter provides a single API that routes to the concrete account transport.
|
|
8
|
+
* Exports mirror client.ts names so consumers
|
|
9
|
+
* only need to change their import path.
|
|
10
|
+
*/
|
|
3
11
|
const DEFAULT_TIMEOUT_MS = 1e4;
|
|
4
|
-
function formatErrorMessage(error) {
|
|
5
|
-
return error instanceof Error ? error.message : String(error);
|
|
6
|
-
}
|
|
7
12
|
function usesContainer(kind) {
|
|
8
13
|
return kind === "container";
|
|
9
14
|
}
|
|
@@ -19,7 +24,7 @@ async function signalRpcRequest(method, params, opts) {
|
|
|
19
24
|
*/
|
|
20
25
|
async function signalCheck(baseUrl, timeoutMs = DEFAULT_TIMEOUT_MS, options = {}) {
|
|
21
26
|
try {
|
|
22
|
-
return usesContainer(options.transportKind) ? await containerCheck(baseUrl, timeoutMs) : await signalCheck$1(baseUrl, timeoutMs);
|
|
27
|
+
return usesContainer(options.transportKind) ? await containerCheck(baseUrl, timeoutMs, options.account) : await signalCheck$1(baseUrl, timeoutMs);
|
|
23
28
|
} catch (error) {
|
|
24
29
|
return {
|
|
25
30
|
ok: false,
|
|
@@ -42,6 +47,7 @@ async function streamSignalEvents(params) {
|
|
|
42
47
|
event: "receive",
|
|
43
48
|
data: JSON.stringify(event)
|
|
44
49
|
}),
|
|
50
|
+
onStreamOpen: params.onStreamOpen,
|
|
45
51
|
logger: params.logger
|
|
46
52
|
});
|
|
47
53
|
return streamSignalEvents$1({
|
|
@@ -49,7 +55,8 @@ async function streamSignalEvents(params) {
|
|
|
49
55
|
account: params.account,
|
|
50
56
|
abortSignal: params.abortSignal,
|
|
51
57
|
timeoutMs: params.timeoutMs,
|
|
52
|
-
onEvent: (event) => params.onEvent(event)
|
|
58
|
+
onEvent: (event) => params.onEvent(event),
|
|
59
|
+
onStreamOpen: params.onStreamOpen
|
|
53
60
|
});
|
|
54
61
|
}
|
|
55
62
|
//#endregion
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { createChannelConfigUiHints } from "openclaw/plugin-sdk/channel-core";
|
|
2
|
-
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
2
|
+
import { isRecord, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
3
3
|
import { DEFAULT_ACCOUNT_ID, normalizeAccountId, resolveAccountEntry } from "openclaw/plugin-sdk/account-resolution";
|
|
4
|
-
import { isRecord as isRecord$1 } from "openclaw/plugin-sdk/channel-secret-basic-runtime";
|
|
5
4
|
import { ChannelDeliveryStreamingConfigSchema, ChannelSendReadReceiptsSchema, ExecutableTokenSchema, ReplyToModeSchema, buildChannelConfigSchema, buildChannelReactionShape, buildCommonChannelAccountShape, buildGroupEntrySchema, requireAllowlistAllowFrom, requireOpenAllowFrom } from "openclaw/plugin-sdk/channel-config-schema";
|
|
6
5
|
import { z } from "zod";
|
|
7
6
|
//#region extensions/signal/src/config-ui-hints.ts
|
|
@@ -62,11 +61,11 @@ const SIGNAL_RETIRED_TRANSPORT_KEYS = [
|
|
|
62
61
|
];
|
|
63
62
|
const SignalTransportUrlSchema = z.string().url().regex(/^[Hh][Tt][Tt][Pp][Ss]?:\/\/(?![^/?#]*@)/, "Expected http:// or https:// URL without embedded credentials");
|
|
64
63
|
function projectSignalConfigForUpdateValidation(value) {
|
|
65
|
-
if (process.env.OPENCLAW_UPDATE_IN_PROGRESS !== "1" || !isRecord
|
|
64
|
+
if (process.env.OPENCLAW_UPDATE_IN_PROGRESS !== "1" || !isRecord(value)) return value;
|
|
66
65
|
const next = { ...value };
|
|
67
66
|
for (const key of SIGNAL_RETIRED_TRANSPORT_KEYS) delete next[key];
|
|
68
|
-
if (isRecord
|
|
69
|
-
if (!isRecord
|
|
67
|
+
if (isRecord(value.accounts)) next.accounts = Object.fromEntries(Object.entries(value.accounts).map(([accountId, account]) => {
|
|
68
|
+
if (!isRecord(account)) return [accountId, account];
|
|
70
69
|
const nextAccount = { ...account };
|
|
71
70
|
for (const key of SIGNAL_RETIRED_TRANSPORT_KEYS) delete nextAccount[key];
|
|
72
71
|
return [accountId, nextAccount];
|
package/dist/contract-api.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { d as normalizeSignalMessagingTarget, i as isSignalSenderAllowed, u as looksLikeSignalTargetId } from "./identity-BQ4OzXXk.js";
|
|
2
|
-
import { a as installSignalCliFromRelease, i as installSignalCli, n as downloadToFile, o as looksLikeArchive, r as extractSignalCliArchive, s as pickAsset, t as MAX_SIGNAL_CLI_EXTRACTED_BYTES } from "./install-signal-cli-
|
|
2
|
+
import { a as installSignalCliFromRelease, i as installSignalCli, n as downloadToFile, o as looksLikeArchive, r as extractSignalCliArchive, s as pickAsset, t as MAX_SIGNAL_CLI_EXTRACTED_BYTES } from "./install-signal-cli-Cpiyk0k1.js";
|
|
3
3
|
export { MAX_SIGNAL_CLI_EXTRACTED_BYTES, downloadToFile, extractSignalCliArchive, installSignalCli, installSignalCliFromRelease, isSignalSenderAllowed, looksLikeArchive, looksLikeSignalTargetId, normalizeSignalMessagingTarget, pickAsset };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { a as isSignalManagedNativeConnectionUrlForBind, c as buildSignalTransportHttpUrl, i as assignSignalManagedNativePort, o as isValidSignalManagedNativePort, r as allocateSignalManagedNativePort, s as resolveLocalSignalTransportPort, u as normalizeSignalTransportUrl } from "./transport-policy-DxvSMHp9.js";
|
|
2
|
+
import { isRecord, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
2
3
|
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/account-resolution";
|
|
3
|
-
import {
|
|
4
|
-
import { defineChannelAliasMigration } from "openclaw/plugin-sdk/runtime-doctor";
|
|
4
|
+
import { defineChannelAliasMigration } from "openclaw/plugin-sdk/runtime-doctor-migrations";
|
|
5
5
|
//#region extensions/signal/src/config-compat.ts
|
|
6
6
|
const LEGACY_TRANSPORT_FIELDS = [
|
|
7
7
|
"configPath",
|
|
@@ -40,16 +40,13 @@ function isSignalTransportConfig(value) {
|
|
|
40
40
|
return false;
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
|
-
function optionalString(value) {
|
|
44
|
-
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
45
|
-
}
|
|
46
43
|
function inherited(entry, parent, key) {
|
|
47
44
|
return Object.hasOwn(entry, key) ? entry[key] : parent[key];
|
|
48
45
|
}
|
|
49
46
|
function legacyBaseUrl(entry, parent) {
|
|
50
|
-
const url =
|
|
47
|
+
const url = normalizeOptionalString(inherited(entry, parent, "httpUrl"));
|
|
51
48
|
if (url) return normalizeSignalTransportUrl(url);
|
|
52
|
-
const host =
|
|
49
|
+
const host = normalizeOptionalString(inherited(entry, parent, "httpHost")) ?? "127.0.0.1";
|
|
53
50
|
const rawPort = inherited(entry, parent, "httpPort");
|
|
54
51
|
return buildSignalTransportHttpUrl(host, typeof rawPort === "number" ? rawPort : 8080);
|
|
55
52
|
}
|
|
@@ -57,11 +54,11 @@ function hasLegacyFields(entry) {
|
|
|
57
54
|
return LEGACY_TRANSPORT_FIELDS.some((field) => Object.hasOwn(entry, field));
|
|
58
55
|
}
|
|
59
56
|
function wasLegacySignalAccountConfigured(entry, parent) {
|
|
60
|
-
return Boolean(
|
|
57
|
+
return Boolean(normalizeOptionalString(inherited(entry, parent, "account")) || normalizeOptionalString(inherited(entry, parent, "configPath")) || normalizeOptionalString(inherited(entry, parent, "httpUrl")) || normalizeOptionalString(inherited(entry, parent, "httpHost")) || normalizeOptionalString(inherited(entry, parent, "cliPath")) || typeof inherited(entry, parent, "httpPort") === "number" || typeof inherited(entry, parent, "autoStart") === "boolean");
|
|
61
58
|
}
|
|
62
59
|
function hasInvalidLegacyHttpUrl(entries, parent) {
|
|
63
60
|
return entries.some((entry) => {
|
|
64
|
-
const httpUrl =
|
|
61
|
+
const httpUrl = normalizeOptionalString(inherited(entry, parent, "httpUrl"));
|
|
65
62
|
if (!httpUrl) return false;
|
|
66
63
|
try {
|
|
67
64
|
normalizeSignalTransportUrl(httpUrl);
|
|
@@ -73,10 +70,10 @@ function hasInvalidLegacyHttpUrl(entries, parent) {
|
|
|
73
70
|
}
|
|
74
71
|
function findInvalidLegacyDerivedEndpoint(entries, parent) {
|
|
75
72
|
for (const entry of entries) {
|
|
76
|
-
if (
|
|
73
|
+
if (normalizeOptionalString(inherited(entry, parent, "httpUrl"))) continue;
|
|
77
74
|
const rawPort = inherited(entry, parent, "httpPort");
|
|
78
75
|
if (rawPort !== void 0 && !isValidSignalManagedNativePort(rawPort)) return "port";
|
|
79
|
-
const host =
|
|
76
|
+
const host = normalizeOptionalString(inherited(entry, parent, "httpHost")) ?? "127.0.0.1";
|
|
80
77
|
try {
|
|
81
78
|
buildSignalTransportHttpUrl(host, typeof rawPort === "number" ? rawPort : 8080);
|
|
82
79
|
} catch {
|
|
@@ -89,19 +86,19 @@ function hasInvalidManagedTransportPort(transports) {
|
|
|
89
86
|
}
|
|
90
87
|
function requiresDetection(entry, parent, apiMode) {
|
|
91
88
|
if (apiMode !== void 0 && apiMode !== "auto") return false;
|
|
92
|
-
return Boolean(
|
|
89
|
+
return Boolean(normalizeOptionalString(inherited(entry, parent, "httpUrl"))) || !resolveLegacyAutoStart(entry, parent);
|
|
93
90
|
}
|
|
94
91
|
function resolveLegacyAutoStart(entry, parent) {
|
|
95
92
|
const autoStart = inherited(entry, parent, "autoStart");
|
|
96
93
|
if (typeof autoStart === "boolean") return autoStart;
|
|
97
|
-
return !
|
|
94
|
+
return !normalizeOptionalString(inherited(entry, parent, "httpUrl"));
|
|
98
95
|
}
|
|
99
96
|
function resolveManagedConnectionUrl(entry, parent) {
|
|
100
|
-
const httpUrl =
|
|
97
|
+
const httpUrl = normalizeOptionalString(inherited(entry, parent, "httpUrl"));
|
|
101
98
|
if (!httpUrl) return;
|
|
102
99
|
const normalizedUrl = normalizeSignalTransportUrl(httpUrl);
|
|
103
100
|
const endpoint = new URL(normalizedUrl);
|
|
104
|
-
const bindHost = (
|
|
101
|
+
const bindHost = (normalizeOptionalString(inherited(entry, parent, "httpHost")) ?? "127.0.0.1").replace(/^\[|\]$/g, "").toLowerCase();
|
|
105
102
|
const rawBindPort = inherited(entry, parent, "httpPort");
|
|
106
103
|
const bindPort = typeof rawBindPort === "number" ? rawBindPort : 8080;
|
|
107
104
|
const endpointHost = endpoint.hostname.replace(/^\[|\]$/g, "").toLowerCase();
|
|
@@ -110,10 +107,10 @@ function resolveManagedConnectionUrl(entry, parent) {
|
|
|
110
107
|
}
|
|
111
108
|
function buildManagedNativeTransport(entry, parent) {
|
|
112
109
|
const value = (key) => inherited(entry, parent, key);
|
|
113
|
-
const configPath =
|
|
114
|
-
const cliPath =
|
|
110
|
+
const configPath = normalizeOptionalString(value("configPath"));
|
|
111
|
+
const cliPath = normalizeOptionalString(value("cliPath"));
|
|
115
112
|
const url = resolveManagedConnectionUrl(entry, parent);
|
|
116
|
-
const httpHost =
|
|
113
|
+
const httpHost = normalizeOptionalString(value("httpHost"));
|
|
117
114
|
const httpPort = value("httpPort");
|
|
118
115
|
const startupTimeoutMs = value("startupTimeoutMs");
|
|
119
116
|
const receiveMode = value("receiveMode");
|
|
@@ -152,7 +149,7 @@ function resolveLegacyTransportWithoutDetection(params) {
|
|
|
152
149
|
async function resolveLegacyTransport(params) {
|
|
153
150
|
const resolved = resolveLegacyTransportWithoutDetection(params);
|
|
154
151
|
if (resolved) return resolved;
|
|
155
|
-
const account =
|
|
152
|
+
const account = normalizeOptionalString(inherited(params.entry, params.parent, "account"));
|
|
156
153
|
try {
|
|
157
154
|
const detected = await params.detect?.({
|
|
158
155
|
url: legacyBaseUrl(params.entry, params.parent),
|
|
@@ -170,7 +167,7 @@ function clearLegacyTransportFields(entry) {
|
|
|
170
167
|
}
|
|
171
168
|
function hasRootSignalAccount(entries) {
|
|
172
169
|
const root = entries[0];
|
|
173
|
-
return entries.length === 1 || Boolean(
|
|
170
|
+
return entries.length === 1 || Boolean(normalizeOptionalString(root?.account)) || isSignalTransportConfig(root?.transport);
|
|
174
171
|
}
|
|
175
172
|
function signalAccountIds(entries) {
|
|
176
173
|
const accounts = isRecord(entries[0]?.accounts) ? entries[0].accounts : {};
|
|
@@ -283,7 +280,7 @@ function hasContainerTransportWithoutEffectiveAccount(cfg) {
|
|
|
283
280
|
const rootTransport = isSignalTransportConfig(signal.transport) ? signal.transport : void 0;
|
|
284
281
|
const defaultKey = resolveSignalAccountKey(accounts, DEFAULT_ACCOUNT_ID);
|
|
285
282
|
const defaultEntry = defaultKey ? accounts[defaultKey] : void 0;
|
|
286
|
-
const defaultAccount = isRecord(defaultEntry) && defaultEntry.account !== void 0 ?
|
|
283
|
+
const defaultAccount = isRecord(defaultEntry) && defaultEntry.account !== void 0 ? normalizeOptionalString(defaultEntry.account) : normalizeOptionalString(signal.account);
|
|
287
284
|
const channelEnabled = signal.enabled !== false;
|
|
288
285
|
const defaultEnabled = !isRecord(defaultEntry) || defaultEntry.enabled !== false;
|
|
289
286
|
if (rootTransport?.kind === "container" && channelEnabled && defaultEnabled && !defaultAccount) return true;
|
|
@@ -292,7 +289,7 @@ function hasContainerTransportWithoutEffectiveAccount(cfg) {
|
|
|
292
289
|
if (!channelEnabled || entry.enabled === false) continue;
|
|
293
290
|
const isDefaultAccount = isDefaultSignalAccountId(accountId);
|
|
294
291
|
if ((isDefaultAccount && rootTransport ? rootTransport : isSignalTransportConfig(entry.transport) ? entry.transport : void 0)?.kind !== "container" || isDefaultAccount && rootTransport) continue;
|
|
295
|
-
if (!(entry.account === void 0 ?
|
|
292
|
+
if (!(entry.account === void 0 ? normalizeOptionalString(signal.account) : normalizeOptionalString(entry.account))) return true;
|
|
296
293
|
}
|
|
297
294
|
return false;
|
|
298
295
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as normalizeCompatibilityConfig, t as legacyConfigRules } from "./doctor-contract-api-
|
|
1
|
+
import { n as normalizeCompatibilityConfig, t as legacyConfigRules } from "./doctor-contract-api-Dnp5eQQJ.js";
|
|
2
2
|
export { legacyConfigRules, normalizeCompatibilityConfig };
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { isRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
2
2
|
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
3
|
-
import nodePath from "node:path";
|
|
4
3
|
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
5
4
|
import { CONFIG_DIR, extractArchive, resolveBrewExecutable } from "openclaw/plugin-sdk/setup-tools";
|
|
6
5
|
import { createWriteStream } from "node:fs";
|
|
7
6
|
import fs from "node:fs/promises";
|
|
7
|
+
import path from "node:path";
|
|
8
8
|
import { Readable, Transform } from "node:stream";
|
|
9
9
|
import { pipeline } from "node:stream/promises";
|
|
10
10
|
import { readProviderJsonObjectResponse } from "openclaw/plugin-sdk/provider-http";
|
|
@@ -107,7 +107,10 @@ async function downloadToFile(url, dest, maxRedirects = 5, maxBytes = MAX_SIGNAL
|
|
|
107
107
|
if (rawLength !== null) {
|
|
108
108
|
const trimmedLength = rawLength.trim();
|
|
109
109
|
const declaredLength = CONTENT_LENGTH_RE.test(trimmedLength) ? Number(trimmedLength) : NaN;
|
|
110
|
-
if (Number.isFinite(declaredLength) && declaredLength > maxBytes)
|
|
110
|
+
if (Number.isFinite(declaredLength) && declaredLength > maxBytes) {
|
|
111
|
+
await cancelUnusedResponseBody(response);
|
|
112
|
+
throw new Error(`signal-cli archive exceeds the ${maxBytes}-byte download cap (declared ${declaredLength}).`);
|
|
113
|
+
}
|
|
111
114
|
}
|
|
112
115
|
let totalBytes = 0;
|
|
113
116
|
const body = response.body;
|
|
@@ -131,7 +134,7 @@ async function findSignalCliBinary(root) {
|
|
|
131
134
|
if (depth > 3) return;
|
|
132
135
|
const entries = await fs.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
133
136
|
for (const entry of entries) {
|
|
134
|
-
const full =
|
|
137
|
+
const full = path.join(dir, entry.name);
|
|
135
138
|
if (entry.isDirectory()) await enqueue(full, depth + 1);
|
|
136
139
|
else if (entry.isFile() && entry.name === "signal-cli") candidates.push(full);
|
|
137
140
|
}
|
|
@@ -151,7 +154,7 @@ async function resolveBrewSignalCliPath(brewExe) {
|
|
|
151
154
|
});
|
|
152
155
|
if (result.code === 0 && result.stdout.trim()) {
|
|
153
156
|
const prefix = result.stdout.trim();
|
|
154
|
-
const candidate =
|
|
157
|
+
const candidate = path.join(prefix, "bin", "signal-cli");
|
|
155
158
|
try {
|
|
156
159
|
await fs.access(candidate);
|
|
157
160
|
return candidate;
|
|
@@ -246,7 +249,7 @@ async function installSignalCliFromRelease(runtime) {
|
|
|
246
249
|
}, async (archivePath) => {
|
|
247
250
|
runtime.log(`Downloading signal-cli ${releaseInfo.version} (${asset.name})…`);
|
|
248
251
|
await downloadToFile(asset.browser_download_url, archivePath);
|
|
249
|
-
const installRoot =
|
|
252
|
+
const installRoot = path.join(CONFIG_DIR, "tools", "signal-cli", releaseInfo.version);
|
|
250
253
|
await fs.mkdir(installRoot, { recursive: true });
|
|
251
254
|
if (!looksLikeArchive(normalizeLowercaseStringOrEmpty(asset.name))) return {
|
|
252
255
|
ok: false,
|