@agent-native/core 0.101.10 → 0.101.12
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/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +12 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/a2a/client.ts +23 -1
- package/corpus/core/src/a2a/handlers.ts +65 -1
- package/corpus/core/src/a2a/server.ts +124 -0
- package/corpus/core/src/a2a/task-store.ts +253 -0
- package/corpus/core/src/a2a/types.ts +26 -0
- package/corpus/core/src/agent/production-agent.ts +5 -2
- package/corpus/core/src/coding-tools/run-code.ts +65 -12
- package/corpus/core/src/extensions/actions.ts +141 -7
- package/corpus/core/src/mcp/builtin-tools.ts +25 -1
- package/corpus/core/src/scripts/call-agent.ts +22 -3
- package/corpus/core/src/server/agent-chat-plugin.ts +86 -1
- package/corpus/core/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +25 -0
- package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +59 -0
- package/corpus/templates/analytics/app/components/layout/NewDashboardDialog.tsx +5 -2
- package/corpus/templates/analytics/server/lib/agent-chat-plan-mode.ts +13 -4
- package/corpus/templates/analytics/server/lib/real-data-actions.ts +63 -1
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +30 -0
- package/corpus/templates/content/.agents/skills/document-editing/SKILL.md +23 -0
- package/corpus/templates/content/app/components/editor/MathRenderer.tsx +43 -0
- package/corpus/templates/content/app/components/editor/SlashCommandMenu.tsx +260 -4
- package/corpus/templates/content/app/components/editor/extensions/NotionExtensions.tsx +78 -0
- package/corpus/templates/content/app/global.css +53 -0
- package/corpus/templates/content/app/i18n/zh-TW.ts +13 -0
- package/corpus/templates/content/app/i18n-data.ts +163 -0
- package/corpus/templates/content/app/root.tsx +2 -0
- package/corpus/templates/content/changelog/2026-07-14-latex-equations-render-in-documents-public-pages-and-exports.md +6 -0
- package/corpus/templates/content/package.json +1 -0
- package/corpus/templates/content/shared/document-export.ts +296 -25
- package/corpus/templates/content/shared/inline-math.ts +128 -0
- package/corpus/templates/content/shared/math-rendering.ts +50 -0
- package/corpus/templates/content/shared/nfm.ts +17 -14
- package/corpus/templates/content/vite.config.ts +0 -1
- package/corpus/templates/mail/.agents/skills/email-drafts/SKILL.md +17 -0
- package/corpus/templates/mail/AGENTS.md +21 -19
- package/corpus/templates/mail/actions/create-attachment-upload.ts +49 -0
- package/corpus/templates/mail/changelog/2026-07-14-connected-agents-can-now-securely-upload-local-files-for-mai.md +6 -0
- package/corpus/templates/mail/server/handlers/media.ts +84 -61
- package/corpus/templates/mail/server/lib/attachment-upload-ticket.ts +234 -0
- package/corpus/templates/mail/server/lib/mail-connector-catalog.ts +8 -4
- package/corpus/templates/mail/server/lib/media-upload.ts +114 -0
- package/corpus/templates/mail/server/plugins/auth.ts +9 -1
- package/corpus/templates/mail/server/routes/api/media/attachment-upload/[uploadId].put.ts +1 -0
- package/dist/a2a/client.d.ts +5 -1
- package/dist/a2a/client.d.ts.map +1 -1
- package/dist/a2a/client.js +18 -1
- package/dist/a2a/client.js.map +1 -1
- package/dist/a2a/handlers.d.ts.map +1 -1
- package/dist/a2a/handlers.js +57 -8
- package/dist/a2a/handlers.js.map +1 -1
- package/dist/a2a/server.d.ts.map +1 -1
- package/dist/a2a/server.js +87 -1
- package/dist/a2a/server.js.map +1 -1
- package/dist/a2a/task-store.d.ts +27 -0
- package/dist/a2a/task-store.d.ts.map +1 -1
- package/dist/a2a/task-store.js +216 -0
- package/dist/a2a/task-store.js.map +1 -1
- package/dist/a2a/types.d.ts +24 -0
- package/dist/a2a/types.d.ts.map +1 -1
- package/dist/a2a/types.js.map +1 -1
- package/dist/agent/production-agent.d.ts +1 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +5 -2
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/coding-tools/run-code.js +64 -11
- package/dist/coding-tools/run-code.js.map +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/extensions/actions.d.ts.map +1 -1
- package/dist/extensions/actions.js +129 -7
- package/dist/extensions/actions.js.map +1 -1
- package/dist/mcp/builtin-tools.d.ts.map +1 -1
- package/dist/mcp/builtin-tools.js +22 -3
- package/dist/mcp/builtin-tools.js.map +1 -1
- package/dist/notifications/routes.d.ts +2 -2
- package/dist/observability/routes.d.ts +5 -5
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/scripts/call-agent.d.ts +1 -1
- package/dist/scripts/call-agent.d.ts.map +1 -1
- package/dist/scripts/call-agent.js +19 -1
- package/dist/scripts/call-agent.js.map +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +71 -2
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +25 -0
- package/package.json +1 -1
- package/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +25 -0
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { ACTION_CHAT_UI_INLINE_EXTENSION_RENDERER } from "../action-ui.js";
|
|
2
2
|
import { writeAppState } from "../application-state/script-helpers.js";
|
|
3
|
-
import {
|
|
3
|
+
import { readResource } from "../resources/script-helpers.js";
|
|
4
|
+
import { getRequestOrgId, getRequestRunContext, getRequestUserEmail, } from "../server/request-context.js";
|
|
4
5
|
import { resolveAccess } from "../sharing/access.js";
|
|
6
|
+
import { readWorkspaceFile, } from "../workspace-files/store.js";
|
|
5
7
|
import { getLocalExtension, isLocalExtensionRow, listLocalExtensions, } from "./local.js";
|
|
6
8
|
import { extensionPath } from "./path.js";
|
|
7
9
|
import { addExtensionSlotTarget, installExtensionSlot, uninstallExtensionSlot, listExtensionsForSlot, listSlotsForExtension, } from "./slots/store.js";
|
|
@@ -392,7 +394,7 @@ export function createExtensionActionEntries() {
|
|
|
392
394
|
},
|
|
393
395
|
"create-extension": {
|
|
394
396
|
tool: {
|
|
395
|
-
description: 'Create a persisted sandboxed Alpine.js mini-app extension and render it inline in the chat. Use this when the user wants generated UI that should be saved, reusable, or visible in the Extensions view: extensions, widgets, dashboards, calculators, mini-apps, and reusable interactive utilities. For one-time chat-only UI, use render-inline-extension instead. The content must be a self-contained Alpine.js HTML body snippet that can use appAction(), appFetch(), dbQuery(), extensionFetch(), extensionData, agentNative.ui.output(value, opts?), and agentNative.chat.send()/sendToAgentChat(). Use appAction() for app data writes and extensionData for extension-owned persisted UI state; dbQuery() is for read-only inspection of known app SQL tables. Use agentNative.ui.output for passive current values from knobs, sliders, and selections; it writes application state at inline-ui:<extension id>:output, which the agent can read later with readAppState when the user says to use that value. Use agentNative.chat.send for visible submit/apply actions. Persist reusable user-edited state with extensionData: if the extension has checkboxes, todos, notes, filters, preferences, or any control whose value should survive reload/reopen, load that state on init and save changes with extensionData, usually at user scope, instead of keeping it only in Alpine state. IMPORTANT — hosting a pasted file: if the user pasted a large HTML/Alpine file (it appears in your context as an <attachment name="pasted-text-…"> block) and asked you to host it as-is, do NOT copy that file into `content`. Instead leave `content` empty and pass `contentFromAttachment` set to that attachment\'s name (or the literal "latest" for the most recent pasted block) — the server reads the file verbatim. Re-emitting a large pasted file as `content` regularly gets cut off mid-stream and stalls the turn. Prefer appAction(name, params) for app data and actions, including read actions mounted as GET; do not call template /api/* routes from appFetch because the extension bridge only allows framework /_agent-native/* paths. Parse JSON string action results before aggregating; use dbQuery() only for known existing SQL tables and never for writes. Keep the initial create-extension payload compact and working; for complex extensions, create a useful v1 first, then use focused update-extension edits for refinements rather than assembling one enormous initial tool input. For any non-trivial component (more than a couple of state fields, any methods, any string formatting, any branching) put the component in a <script> block via Alpine.data(\'name\', () => ({...})) and reference it with x-data="name" — do NOT cram methods, template literals, or branching logic into an inline x-data="{...}" attribute (HTML parser pitfalls cause ReferenceError failures). Define every variable referenced from x-text/x-show/x-if/x-for on the data object\'s initial state. If the extension\'s value depends on an LLM call, require a real key via \\${keys.OPENAI_API_KEY}/\\${keys.ANTHROPIC_API_KEY} (and tell the user to add it in the Dispatch Vault, or in app Settings → API Keys & Connections for standalone apps, if missing) or route the AI work to the agent chat — never ship a stubbed analysis step that renders a placeholder/boolean as the result.',
|
|
397
|
+
description: 'Create a persisted sandboxed Alpine.js mini-app extension and render it inline in the chat. Use this when the user wants generated UI that should be saved, reusable, or visible in the Extensions view: extensions, widgets, dashboards, calculators, mini-apps, and reusable interactive utilities. For one-time chat-only UI, use render-inline-extension instead. The content must be a self-contained Alpine.js HTML body snippet that can use appAction(), appFetch(), dbQuery(), extensionFetch(), extensionData, agentNative.ui.output(value, opts?), and agentNative.chat.send()/sendToAgentChat(). Use appAction() for app data writes and extensionData for extension-owned persisted UI state; dbQuery() is for read-only inspection of known app SQL tables. Use agentNative.ui.output for passive current values from knobs, sliders, and selections; it writes application state at inline-ui:<extension id>:output, which the agent can read later with readAppState when the user says to use that value. Use agentNative.chat.send for visible submit/apply actions. Persist reusable user-edited state with extensionData: if the extension has checkboxes, todos, notes, filters, preferences, or any control whose value should survive reload/reopen, load that state on init and save changes with extensionData, usually at user scope, instead of keeping it only in Alpine state. IMPORTANT — hosting a pasted file: if the user pasted a large HTML/Alpine file (it appears in your context as an <attachment name="pasted-text-…"> block) and asked you to host it as-is, do NOT copy that file into `content`. Instead leave `content` empty and pass `contentFromAttachment` set to that attachment\'s name (or the literal "latest" for the most recent pasted block) — the server reads the file verbatim. Re-emitting a large pasted file as `content` regularly gets cut off mid-stream and stalls the turn. IMPORTANT — cloning a large extension that lives as a workspace resource (not a chat attachment): leave `content` empty and pass `contentFromWorkspaceFile` set to the resource path (e.g. "intuit-analytics-extension.html"); the server reads the full file. Do NOT try to reconstruct the body with run-code or route create-extension through run-code (mutating actions are not callable there). Prefer appAction(name, params) for app data and actions, including read actions mounted as GET; do not call template /api/* routes from appFetch because the extension bridge only allows framework /_agent-native/* paths. Parse JSON string action results before aggregating; use dbQuery() only for known existing SQL tables and never for writes. Keep the initial create-extension payload compact and working; for complex extensions, create a useful v1 first, then use focused update-extension edits for refinements rather than assembling one enormous initial tool input. For any non-trivial component (more than a couple of state fields, any methods, any string formatting, any branching) put the component in a <script> block via Alpine.data(\'name\', () => ({...})) and reference it with x-data="name" — do NOT cram methods, template literals, or branching logic into an inline x-data="{...}" attribute (HTML parser pitfalls cause ReferenceError failures). Define every variable referenced from x-text/x-show/x-if/x-for on the data object\'s initial state. If the extension\'s value depends on an LLM call, require a real key via \\${keys.OPENAI_API_KEY}/\\${keys.ANTHROPIC_API_KEY} (and tell the user to add it in the Dispatch Vault, or in app Settings → API Keys & Connections for standalone apps, if missing) or route the AI work to the agent chat — never ship a stubbed analysis step that renders a placeholder/boolean as the result.',
|
|
396
398
|
parameters: {
|
|
397
399
|
type: "object",
|
|
398
400
|
properties: {
|
|
@@ -412,6 +414,10 @@ export function createExtensionActionEntries() {
|
|
|
412
414
|
type: "string",
|
|
413
415
|
description: 'Host a pasted/attached file verbatim WITHOUT re-typing it. Set this to the name of an attachment on the current turn (e.g. "pasted-text-1718000000000-ab12cd.txt") or the literal "latest" for the most recent pasted block; the server resolves it into the extension content. Use this instead of `content` whenever the user pasted a large file to host — it avoids re-emitting thousands of tokens. When set, leave `content` empty.',
|
|
414
416
|
},
|
|
417
|
+
contentFromWorkspaceFile: {
|
|
418
|
+
type: "string",
|
|
419
|
+
description: 'Host a workspace/shared resource file verbatim WITHOUT re-typing it. Set this to the resource path (e.g. "intuit-analytics-extension.html"); the server reads the full file and uses it as the extension content. Use this — NOT run-code or contentFromAttachment — when cloning a large extension body that already exists as a workspace resource. When set, leave `content` empty.',
|
|
420
|
+
},
|
|
415
421
|
icon: {
|
|
416
422
|
type: "string",
|
|
417
423
|
description: "Optional icon name or short label.",
|
|
@@ -428,7 +434,7 @@ export function createExtensionActionEntries() {
|
|
|
428
434
|
const name = String(args?.name ?? "").trim();
|
|
429
435
|
if (!name)
|
|
430
436
|
return "Error: name is required.";
|
|
431
|
-
const resolved =
|
|
437
|
+
const resolved = await resolveExtensionContentAsync(args, ctx);
|
|
432
438
|
if ("error" in resolved)
|
|
433
439
|
return resolved.error;
|
|
434
440
|
const content = resolved.content.trim();
|
|
@@ -462,9 +468,12 @@ export function createExtensionActionEntries() {
|
|
|
462
468
|
catch {
|
|
463
469
|
// Non-fatal — agent can still mention the path in its reply.
|
|
464
470
|
}
|
|
471
|
+
const hiddenIds = await getHiddenExtensionIdsForCurrentUser();
|
|
465
472
|
return {
|
|
466
473
|
ok: true,
|
|
467
|
-
|
|
474
|
+
// Compact summary (contentLength + contentHash, no full body). Echoing
|
|
475
|
+
// the whole HTML back is pure token waste — the agent just supplied it.
|
|
476
|
+
extension: await summarizeExtension(existing, hiddenIds, false),
|
|
468
477
|
path: existingPath,
|
|
469
478
|
next: `Extension was already created in this session (recovered from a connection retry). The user is being navigated to it — no further navigation tool calls needed.`,
|
|
470
479
|
};
|
|
@@ -492,9 +501,12 @@ export function createExtensionActionEntries() {
|
|
|
492
501
|
catch {
|
|
493
502
|
// Non-fatal — agent can still mention the path in its reply.
|
|
494
503
|
}
|
|
504
|
+
const hiddenIds = await getHiddenExtensionIdsForCurrentUser();
|
|
495
505
|
return {
|
|
496
506
|
ok: true,
|
|
497
|
-
|
|
507
|
+
// Compact summary (contentLength + contentHash, no full body). Echoing
|
|
508
|
+
// the whole HTML back is pure token waste — the agent just supplied it.
|
|
509
|
+
extension: await summarizeExtension(extension, hiddenIds, false),
|
|
498
510
|
path,
|
|
499
511
|
next: `Created. The user is being navigated to the new extension automatically — no further navigation tool calls needed.`,
|
|
500
512
|
};
|
|
@@ -526,6 +538,10 @@ export function createExtensionActionEntries() {
|
|
|
526
538
|
type: "string",
|
|
527
539
|
description: 'Optional full replacement sourced from a pasted/attached file on the current turn, by attachment name (or the literal "latest" for the most recent pasted block). Use instead of `content` when replacing the whole body with a large pasted file so you do not have to re-type it. Ignored when `content` is provided.',
|
|
528
540
|
},
|
|
541
|
+
contentFromWorkspaceFile: {
|
|
542
|
+
type: "string",
|
|
543
|
+
description: 'Optional full replacement sourced from a workspace/shared resource file, by resource path (e.g. "intuit-analytics-extension.html"). The server reads the full file and uses it as the replacement body. Use instead of `content` when replacing the whole body with a large file that exists as a workspace resource. Ignored when `content` is provided.',
|
|
544
|
+
},
|
|
529
545
|
patches: {
|
|
530
546
|
type: "string",
|
|
531
547
|
description: 'Legacy optional JSON array of { "find": "...", "replace": "...", "all"?: true, "expectedMatches"?: 1, "required"?: true } patches. Missing required targets fail instead of silently no-oping.',
|
|
@@ -568,8 +584,9 @@ export function createExtensionActionEntries() {
|
|
|
568
584
|
? args.content
|
|
569
585
|
: undefined;
|
|
570
586
|
if (replacementContent === undefined &&
|
|
571
|
-
args?.contentFromAttachment !== undefined
|
|
572
|
-
|
|
587
|
+
(args?.contentFromAttachment !== undefined ||
|
|
588
|
+
args?.contentFromWorkspaceFile !== undefined)) {
|
|
589
|
+
const resolved = await resolveExtensionContentAsync(args, ctx);
|
|
573
590
|
if ("error" in resolved)
|
|
574
591
|
return resolved.error;
|
|
575
592
|
replacementContent = resolved.content;
|
|
@@ -1154,6 +1171,111 @@ function resolveExtensionContent(args, ctx) {
|
|
|
1154
1171
|
}
|
|
1155
1172
|
return { content: resolved };
|
|
1156
1173
|
}
|
|
1174
|
+
/**
|
|
1175
|
+
* Resolve the workspace-files bridge scope exactly the way run-code's
|
|
1176
|
+
* workspaceRead/workspaceWrite do: org-preferred (org → shared owner) with the
|
|
1177
|
+
* requesting user's email as the solo fallback. Kept in lockstep with
|
|
1178
|
+
* `resolveScope` in `workspace-files/tool.ts`.
|
|
1179
|
+
*/
|
|
1180
|
+
function workspaceFilesBridgeScope() {
|
|
1181
|
+
const orgId = getRequestOrgId();
|
|
1182
|
+
if (orgId)
|
|
1183
|
+
return { scope: "org", scopeId: orgId };
|
|
1184
|
+
const email = getRequestUserEmail();
|
|
1185
|
+
if (email)
|
|
1186
|
+
return { scope: "user", scopeId: email };
|
|
1187
|
+
return null;
|
|
1188
|
+
}
|
|
1189
|
+
/**
|
|
1190
|
+
* Read a workspace/shared/personal resource file's FULL content by path.
|
|
1191
|
+
*
|
|
1192
|
+
* Precedence (single, documented rule so this never silently resolves a
|
|
1193
|
+
* different file than the agent inspected):
|
|
1194
|
+
* 1. The run-code `workspace-files` bridge scope (org → shared owner, else the
|
|
1195
|
+
* user's email). This is the SAME owner/scope `workspaceRead` /
|
|
1196
|
+
* `workspaceWrite` use, so a body the agent staged via `workspaceWrite` is
|
|
1197
|
+
* resolved here verbatim — the two paths cannot diverge.
|
|
1198
|
+
* 2. User-managed Resources (personal override → org/shared → workspace
|
|
1199
|
+
* default) as a fallback, for pre-built resources that were created in the
|
|
1200
|
+
* Resources panel rather than staged through the bridge.
|
|
1201
|
+
*
|
|
1202
|
+
* Unlike attachments, resource content is not capped/truncated on the way in, so
|
|
1203
|
+
* this is the correct path for cloning a large extension body that already
|
|
1204
|
+
* exists as a workspace resource (e.g. a per-customer dashboard).
|
|
1205
|
+
*/
|
|
1206
|
+
async function readWorkspaceFileContent(path) {
|
|
1207
|
+
const trimmed = path.trim();
|
|
1208
|
+
if (!trimmed)
|
|
1209
|
+
return null;
|
|
1210
|
+
// 1) Bridge parity — resolve exactly the file workspaceRead/workspaceWrite see.
|
|
1211
|
+
const bridgeScope = workspaceFilesBridgeScope();
|
|
1212
|
+
if (bridgeScope) {
|
|
1213
|
+
let bridgeFile;
|
|
1214
|
+
try {
|
|
1215
|
+
bridgeFile = await readWorkspaceFile(bridgeScope, trimmed);
|
|
1216
|
+
}
|
|
1217
|
+
catch {
|
|
1218
|
+
// A THROW here is a transient store error or invalid path — NOT a
|
|
1219
|
+
// definitive "not found". Fail closed rather than silently hosting a
|
|
1220
|
+
// possibly-different same-path Resources body than workspaceRead
|
|
1221
|
+
// inspected. A retry re-runs this read cleanly.
|
|
1222
|
+
return null;
|
|
1223
|
+
}
|
|
1224
|
+
// A null result means the file genuinely does not exist in the bridge scope;
|
|
1225
|
+
// fall through to user-managed Resources for pre-built resource-panel files.
|
|
1226
|
+
if (bridgeFile && typeof bridgeFile.content === "string") {
|
|
1227
|
+
return bridgeFile.content;
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
// 2) Fallback — user-managed Resources by scope precedence.
|
|
1231
|
+
for (const scope of ["personal", "shared", "workspace"]) {
|
|
1232
|
+
try {
|
|
1233
|
+
const content = await readResource(trimmed, { scope });
|
|
1234
|
+
if (typeof content === "string")
|
|
1235
|
+
return content;
|
|
1236
|
+
}
|
|
1237
|
+
catch {
|
|
1238
|
+
// A given scope can throw (e.g. `personal` when no user identity is
|
|
1239
|
+
// resolvable in this context). Don't let one scope abort the lookup —
|
|
1240
|
+
// fall through and try the next one.
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
return null;
|
|
1244
|
+
}
|
|
1245
|
+
/**
|
|
1246
|
+
* Resolve the extension HTML body from (in priority order) inline `content`, a
|
|
1247
|
+
* `contentFromWorkspaceFile` resource path, or a `contentFromAttachment` handle.
|
|
1248
|
+
*
|
|
1249
|
+
* The workspace-file path exists because a large extension body frequently lives
|
|
1250
|
+
* as a workspace resource (not a chat attachment). Without it the model has no
|
|
1251
|
+
* viable route — inline is too large to shuttle reliably, contentFromAttachment
|
|
1252
|
+
* only sees chat attachments, and mutating actions cannot run from run-code — so
|
|
1253
|
+
* it loops and the run aborts with no_progress.
|
|
1254
|
+
*/
|
|
1255
|
+
async function resolveExtensionContentAsync(args, ctx) {
|
|
1256
|
+
const inline = args?.content !== undefined ? String(args.content) : undefined;
|
|
1257
|
+
if (inline !== undefined && inline.trim().length > 0) {
|
|
1258
|
+
return { content: inline };
|
|
1259
|
+
}
|
|
1260
|
+
const wsRef = args?.contentFromWorkspaceFile !== undefined
|
|
1261
|
+
? String(args.contentFromWorkspaceFile).trim()
|
|
1262
|
+
: "";
|
|
1263
|
+
if (wsRef) {
|
|
1264
|
+
const content = await readWorkspaceFileContent(wsRef);
|
|
1265
|
+
if (content === null) {
|
|
1266
|
+
return {
|
|
1267
|
+
error: `Error: contentFromWorkspaceFile="${wsRef}" did not match any readable workspace/shared/personal resource file. Check the exact path (e.g. "intuit-analytics-extension.html"), or pass the HTML inline via content.`,
|
|
1268
|
+
};
|
|
1269
|
+
}
|
|
1270
|
+
if (content.trim().length === 0) {
|
|
1271
|
+
return {
|
|
1272
|
+
error: `Error: workspace file "${wsRef}" is empty. Pass non-empty HTML inline via content, or point contentFromWorkspaceFile at a file with content.`,
|
|
1273
|
+
};
|
|
1274
|
+
}
|
|
1275
|
+
return { content };
|
|
1276
|
+
}
|
|
1277
|
+
return resolveExtensionContent(args, ctx);
|
|
1278
|
+
}
|
|
1157
1279
|
function coerceBoolean(value) {
|
|
1158
1280
|
return value === true || value === "true";
|
|
1159
1281
|
}
|