@jay-framework/aiditor 0.19.7 → 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.
- package/dist/actions/cancel-agent-task.jay-action +1 -0
- package/dist/actions/cancel-agent-task.jay-action.d.ts +1 -0
- package/dist/actions/get-contract-inspector-tags.jay-action +9 -0
- package/dist/actions/get-contract-inspector-tags.jay-action.d.ts +6 -0
- package/dist/actions/get-page-assets.jay-action +10 -0
- package/dist/actions/get-page-assets.jay-action.d.ts +7 -0
- package/dist/actions/get-page-meta.jay-action +9 -0
- package/dist/actions/get-page-meta.jay-action.d.ts +6 -0
- package/dist/actions/save-page-assets.jay-action +8 -0
- package/dist/actions/save-page-assets.jay-action.d.ts +5 -0
- package/dist/actions/save-page-meta.jay-action +8 -0
- package/dist/actions/save-page-meta.jay-action.d.ts +5 -0
- package/dist/actions/sync-detected-page-assets.jay-action +12 -0
- package/dist/actions/sync-detected-page-assets.jay-action.d.ts +10 -0
- package/dist/agent-kit-template/plugin/aiditor-add-menu.md +24 -0
- package/dist/index.client.d.ts +331 -43
- package/dist/index.client.js +9987 -2675
- package/dist/index.d.ts +137 -8
- package/dist/index.js +799 -80
- package/dist/pages/aiditor/page.css +1139 -71
- package/dist/pages/aiditor/page.jay-html +2794 -651
- package/dist/pages/aiditor/page.jay-html.d.ts +1039 -0
- package/package.json +18 -9
- package/plugin.yaml +12 -0
package/dist/index.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
1
4
|
import { makeJayQuery, makeJayStream, makeJayStackComponent, phaseOutput, RenderPipeline } from "@jay-framework/fullstack-component";
|
|
2
5
|
import { DEV_SERVER_SERVICE } from "@jay-framework/dev-server";
|
|
3
6
|
import fs, { readFile, readdir } from "fs/promises";
|
|
@@ -5,11 +8,13 @@ import path, { extname } from "path";
|
|
|
5
8
|
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
6
9
|
import fs$1 from "fs";
|
|
7
10
|
import crypto from "crypto";
|
|
8
|
-
import { load } from "js-yaml";
|
|
11
|
+
import yaml, { load } from "js-yaml";
|
|
9
12
|
import { getLogger } from "@jay-framework/logger";
|
|
10
13
|
import { fileURLToPath } from "url";
|
|
11
14
|
import { spawn } from "child_process";
|
|
12
15
|
import { scanRoutes } from "@jay-framework/stack-route-scanner";
|
|
16
|
+
import { parseContract, ContractTagType } from "@jay-framework/compiler-jay-html";
|
|
17
|
+
import { checkValidationErrors } from "@jay-framework/compiler-shared";
|
|
13
18
|
import { setActionCallerOptions } from "@jay-framework/stack-client-runtime";
|
|
14
19
|
const getAiditorBootstrap = makeJayQuery(
|
|
15
20
|
"aiditor.getAiditorBootstrap"
|
|
@@ -20,7 +25,7 @@ const getAiditorBootstrap = makeJayQuery(
|
|
|
20
25
|
};
|
|
21
26
|
});
|
|
22
27
|
const getProjectInfoAction = makeJayQuery("aditor.getProjectInfo").withServices(DEV_SERVER_SERVICE).withHandler(async (input, devServer) => {
|
|
23
|
-
const routes = devServer.
|
|
28
|
+
const routes = await devServer.refreshRoutes();
|
|
24
29
|
return { routes, httpBaseUrl: input.jayDevUrl };
|
|
25
30
|
});
|
|
26
31
|
const getPageParamsAction = makeJayStream("aditor.getPageParams").withServices(DEV_SERVER_SERVICE).withHandler(async function* (input, devServer) {
|
|
@@ -367,6 +372,30 @@ function optionalString(obj, field) {
|
|
|
367
372
|
const trimmed = value.trim();
|
|
368
373
|
return trimmed.length > 0 ? trimmed : void 0;
|
|
369
374
|
}
|
|
375
|
+
function optionalBoolean(obj, field) {
|
|
376
|
+
const value = obj[field];
|
|
377
|
+
return typeof value === "boolean" ? value : void 0;
|
|
378
|
+
}
|
|
379
|
+
function validateInteraction(raw, path2, errors) {
|
|
380
|
+
if (raw === void 0) return void 0;
|
|
381
|
+
if (!isRecord(raw)) {
|
|
382
|
+
errors.push({ path: path2, message: "interaction must be an object" });
|
|
383
|
+
return void 0;
|
|
384
|
+
}
|
|
385
|
+
const mode = raw.mode;
|
|
386
|
+
if (mode !== "reference" && mode !== "stage-place") {
|
|
387
|
+
errors.push({
|
|
388
|
+
path: `${path2}.mode`,
|
|
389
|
+
message: 'mode must be "reference" or "stage-place"'
|
|
390
|
+
});
|
|
391
|
+
return void 0;
|
|
392
|
+
}
|
|
393
|
+
return {
|
|
394
|
+
mode,
|
|
395
|
+
persistOnPage: optionalBoolean(raw, "persistOnPage"),
|
|
396
|
+
stagePromptTemplate: optionalString(raw, "stagePromptTemplate")
|
|
397
|
+
};
|
|
398
|
+
}
|
|
370
399
|
function validateAddMenuItem(raw, path2) {
|
|
371
400
|
const errors = [];
|
|
372
401
|
if (!isRecord(raw)) {
|
|
@@ -390,6 +419,11 @@ function validateAddMenuItem(raw, path2) {
|
|
|
390
419
|
if (errors.length > 0 || !id || !title || !category || !prompt) {
|
|
391
420
|
return { item: null, errors };
|
|
392
421
|
}
|
|
422
|
+
const interaction = validateInteraction(
|
|
423
|
+
raw.interaction,
|
|
424
|
+
`${path2}.interaction`,
|
|
425
|
+
errors
|
|
426
|
+
);
|
|
393
427
|
return {
|
|
394
428
|
item: {
|
|
395
429
|
id,
|
|
@@ -399,7 +433,8 @@ function validateAddMenuItem(raw, path2) {
|
|
|
399
433
|
pluginName: optionalString(raw, "pluginName"),
|
|
400
434
|
packageName: optionalString(raw, "packageName"),
|
|
401
435
|
subCategory: optionalString(raw, "subCategory"),
|
|
402
|
-
thumbnail: optionalString(raw, "thumbnail")
|
|
436
|
+
thumbnail: optionalString(raw, "thumbnail"),
|
|
437
|
+
...interaction ? { interaction } : {}
|
|
403
438
|
},
|
|
404
439
|
errors
|
|
405
440
|
};
|
|
@@ -431,11 +466,11 @@ function validateAddMenuCatalogFile(raw, sourcePath) {
|
|
|
431
466
|
}
|
|
432
467
|
return { file: { items }, errors };
|
|
433
468
|
}
|
|
434
|
-
function parsePluginsIndexYaml(
|
|
469
|
+
function parsePluginsIndexYaml(yaml2) {
|
|
435
470
|
const entries = [];
|
|
436
471
|
let currentPlugin = null;
|
|
437
472
|
let inContracts = false;
|
|
438
|
-
for (const line of
|
|
473
|
+
for (const line of yaml2.split("\n")) {
|
|
439
474
|
const pluginMatch = line.match(/^ - name:\s*(.+)$/);
|
|
440
475
|
if (pluginMatch && !line.startsWith(" ")) {
|
|
441
476
|
currentPlugin = pluginMatch[1].trim();
|
|
@@ -512,8 +547,8 @@ async function isPluginInstalled(projectRoot, pluginName) {
|
|
|
512
547
|
async function parsePluginsIndexContracts(projectRoot) {
|
|
513
548
|
const indexPath = path.join(projectRoot, "agent-kit", "plugins-index.yaml");
|
|
514
549
|
try {
|
|
515
|
-
const
|
|
516
|
-
return parsePluginsIndexYaml(
|
|
550
|
+
const yaml2 = await fs.readFile(indexPath, "utf-8");
|
|
551
|
+
return parsePluginsIndexYaml(yaml2);
|
|
517
552
|
} catch (e) {
|
|
518
553
|
const code = e && typeof e === "object" && "code" in e ? e.code : void 0;
|
|
519
554
|
if (code === "ENOENT") return [];
|
|
@@ -529,10 +564,10 @@ async function parsePluginYamlContracts(projectRoot, pluginName) {
|
|
|
529
564
|
"plugin.yaml"
|
|
530
565
|
);
|
|
531
566
|
try {
|
|
532
|
-
const
|
|
567
|
+
const yaml2 = await fs.readFile(pluginYamlPath, "utf-8");
|
|
533
568
|
const entries = [];
|
|
534
569
|
let inContracts = false;
|
|
535
|
-
for (const line of
|
|
570
|
+
for (const line of yaml2.split("\n")) {
|
|
536
571
|
if (line.trim() === "contracts:") {
|
|
537
572
|
inContracts = true;
|
|
538
573
|
continue;
|
|
@@ -638,6 +673,65 @@ async function listAddMenuItems(projectRoot) {
|
|
|
638
673
|
warnings
|
|
639
674
|
};
|
|
640
675
|
}
|
|
676
|
+
const DEFAULT_PUBLIC_FOLDER = "./public";
|
|
677
|
+
function resolveProjectPublicDir(projectDir) {
|
|
678
|
+
const configPath = path.join(projectDir, ".jay");
|
|
679
|
+
let publicFolder = DEFAULT_PUBLIC_FOLDER;
|
|
680
|
+
if (fs$1.existsSync(configPath)) {
|
|
681
|
+
try {
|
|
682
|
+
const parsed = yaml.load(fs$1.readFileSync(configPath, "utf-8"));
|
|
683
|
+
if (parsed?.devServer?.publicFolder) {
|
|
684
|
+
publicFolder = parsed.devServer.publicFolder;
|
|
685
|
+
}
|
|
686
|
+
} catch {
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
return path.resolve(projectDir, publicFolder);
|
|
690
|
+
}
|
|
691
|
+
function sanitizeAttachmentFileName(name) {
|
|
692
|
+
const base = path.basename(name).replace(/[^\w.\-]+/g, "_");
|
|
693
|
+
return base.length > 0 ? base : "attachment";
|
|
694
|
+
}
|
|
695
|
+
function projectHasWixMediaPlugin(projectDir) {
|
|
696
|
+
const indexPath = path.join(projectDir, "agent-kit", "plugins-index.yaml");
|
|
697
|
+
if (!fs$1.existsSync(indexPath)) return false;
|
|
698
|
+
try {
|
|
699
|
+
const parsed = yaml.load(fs$1.readFileSync(indexPath, "utf-8"));
|
|
700
|
+
return parsed.plugins?.some((plugin) => plugin.name === "wix-media") ?? false;
|
|
701
|
+
} catch {
|
|
702
|
+
return false;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
function buildVisualAttachmentRules(projectDir) {
|
|
706
|
+
const publicDir = resolveProjectPublicDir(projectDir);
|
|
707
|
+
const publicFolderLabel = path.relative(projectDir, publicDir) || "public";
|
|
708
|
+
const lines = [
|
|
709
|
+
"User-uploaded file attachments (per marker):",
|
|
710
|
+
"- Each attachment is on disk at the listed path — Read it for context (layout, colors, dimensions, inspiration).",
|
|
711
|
+
"- Use the marker instruction to decide whether to place the file in the project:",
|
|
712
|
+
" - Instruction asks to display, embed, show, use, set as image/background, or otherwise wire the attachment into the page → copy the file from the attachment path into the project before adding src/href in jay-html. Do not reference build/aditor paths in templates.",
|
|
713
|
+
' - Attachment is reference or inspiration only (mood, "like this", layout reference) and the instruction does not ask to embed it → read for context only; do not copy into the project.',
|
|
714
|
+
`- Local static hosting: copy into \`${publicFolderLabel}/\` (keep the original filename when safe). Reference in jay-html as \`/{filename}\` (e.g. \`src="/photo.png"\`).`,
|
|
715
|
+
"- Never invent placeholder image URLs."
|
|
716
|
+
];
|
|
717
|
+
if (projectHasWixMediaPlugin(projectDir)) {
|
|
718
|
+
lines.push(
|
|
719
|
+
"- Wix Media Manager (`@jay-framework/wix-media` is installed): when the user wants this image as a live site asset, run `jay-stack run wix-media/upload-public` on the attachment file (or copy to public first if the command expects a project path), then use the returned Wix CDN URL in jay-html. Do not duplicate Wix CDN assets into public/. Read agent-kit/designer/wix-media.md for URL patterns."
|
|
720
|
+
);
|
|
721
|
+
}
|
|
722
|
+
return lines.join("\n");
|
|
723
|
+
}
|
|
724
|
+
function persistFile$1(file, destDir, fileName) {
|
|
725
|
+
fs$1.mkdirSync(destDir, { recursive: true });
|
|
726
|
+
const dest = path.join(destDir, fileName);
|
|
727
|
+
fs$1.copyFileSync(file.path, dest);
|
|
728
|
+
return dest;
|
|
729
|
+
}
|
|
730
|
+
function persistVisualTaskAttachment(file, taskAttDir) {
|
|
731
|
+
const originalFileName = sanitizeAttachmentFileName(file.name);
|
|
732
|
+
const taskCopyPath = persistFile$1(file, taskAttDir, originalFileName);
|
|
733
|
+
return { taskCopyPath, originalFileName };
|
|
734
|
+
}
|
|
641
735
|
const CATALOG = [
|
|
642
736
|
{
|
|
643
737
|
pluginName: "wix-server-client",
|
|
@@ -814,6 +908,26 @@ function buildLegacyPluginManifestPromptSection(selectedPlugins) {
|
|
|
814
908
|
}
|
|
815
909
|
function buildRouteMigrationPromptSection(routeMigration) {
|
|
816
910
|
if (!routeMigration) return "";
|
|
911
|
+
if (routeMigration.reason === "page-info-route-edit") {
|
|
912
|
+
return [
|
|
913
|
+
"",
|
|
914
|
+
"## Page route change (required — apply now)",
|
|
915
|
+
"",
|
|
916
|
+
`- Current route: \`${routeMigration.currentRoute}\``,
|
|
917
|
+
`- New route: \`${routeMigration.plannedRoute}\``,
|
|
918
|
+
"",
|
|
919
|
+
"The user approved changing this page's URL. Implement the migration now:",
|
|
920
|
+
"- Move or rename `src/pages/` files and directories so the page is served at the new route.",
|
|
921
|
+
"- Jay Stack directory mapping (URL → folder under `src/pages/`):",
|
|
922
|
+
" - `/segment/:param` → `segment/[param]/`",
|
|
923
|
+
" - optional `/{/:param}` (Express) → `segment/[[param]]/` (double brackets)",
|
|
924
|
+
" - catch-all `/*param` → `segment/[...param]/`",
|
|
925
|
+
"- After moving, remove the old `page.jay-html` (and empty parent dirs if safe).",
|
|
926
|
+
"- Update all internal links, navigation, breadcrumbs, and dynamic links across the site that reference the old route.",
|
|
927
|
+
"- Keep headless component bindings and jay-params consistent with the new route shape.",
|
|
928
|
+
""
|
|
929
|
+
].join("\n");
|
|
930
|
+
}
|
|
817
931
|
return [
|
|
818
932
|
"",
|
|
819
933
|
"## Planned route migration (apply only if user request requires it)",
|
|
@@ -826,6 +940,35 @@ function buildRouteMigrationPromptSection(routeMigration) {
|
|
|
826
940
|
""
|
|
827
941
|
].join("\n");
|
|
828
942
|
}
|
|
943
|
+
function buildPageAssetsPromptSection(assets) {
|
|
944
|
+
const active = assets.filter((a) => a.persistOnPage && !a.userRemoved);
|
|
945
|
+
if (active.length === 0) return "";
|
|
946
|
+
const lines = [
|
|
947
|
+
"Page assets (in scope for this page — use when referenced in marker instructions or @mentions):"
|
|
948
|
+
];
|
|
949
|
+
for (const asset of active) {
|
|
950
|
+
lines.push(`- ${asset.title} (id: ${asset.id})`);
|
|
951
|
+
for (const line of asset.prompt.trim().split("\n")) {
|
|
952
|
+
lines.push(` ${line}`);
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
return lines.join("\n");
|
|
956
|
+
}
|
|
957
|
+
function buildPageComponentsPromptSection(components) {
|
|
958
|
+
if (components.length === 0) return "";
|
|
959
|
+
const lines = [
|
|
960
|
+
"Page components (from page.jay-html — use when referenced in marker instructions or @mentions):"
|
|
961
|
+
];
|
|
962
|
+
for (const component of components) {
|
|
963
|
+
lines.push(
|
|
964
|
+
`- ${component.title}${component.componentKey ? ` (key: ${component.componentKey})` : ""}`
|
|
965
|
+
);
|
|
966
|
+
for (const line of component.prompt.trim().split("\n")) {
|
|
967
|
+
lines.push(` ${line}`);
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
return lines.join("\n");
|
|
971
|
+
}
|
|
829
972
|
function buildNonVisualPrompt(config, notes, imagePath) {
|
|
830
973
|
const lines = [
|
|
831
974
|
`Project directory: ${config.projectDir}`,
|
|
@@ -914,6 +1057,8 @@ function parseNotesField(notes) {
|
|
|
914
1057
|
annotations: j.annotations,
|
|
915
1058
|
previewNavLog,
|
|
916
1059
|
addMenuAttachments: j.addMenuAttachments,
|
|
1060
|
+
pageAssets: j.pageAssets,
|
|
1061
|
+
pageComponents: j.pageComponents,
|
|
917
1062
|
routeMigration: j.routeMigration
|
|
918
1063
|
},
|
|
919
1064
|
plain: ""
|
|
@@ -926,6 +1071,8 @@ function parseNotesField(notes) {
|
|
|
926
1071
|
structured: {
|
|
927
1072
|
annotations: j.annotations,
|
|
928
1073
|
addMenuAttachments: j.addMenuAttachments,
|
|
1074
|
+
pageAssets: j.pageAssets,
|
|
1075
|
+
pageComponents: j.pageComponents,
|
|
929
1076
|
routeMigration: j.routeMigration
|
|
930
1077
|
},
|
|
931
1078
|
structuredVideo: null,
|
|
@@ -936,6 +1083,27 @@ function parseNotesField(notes) {
|
|
|
936
1083
|
}
|
|
937
1084
|
return empty();
|
|
938
1085
|
}
|
|
1086
|
+
function appendVisualAttachmentLines(body, attachments, indent = "") {
|
|
1087
|
+
if (attachments.length === 0) {
|
|
1088
|
+
body.push(`${indent}Attachments: (none)`);
|
|
1089
|
+
return;
|
|
1090
|
+
}
|
|
1091
|
+
body.push(
|
|
1092
|
+
`${indent}Attachments (read from disk; belong to this annotation only; match "image #N" in the instruction):`
|
|
1093
|
+
);
|
|
1094
|
+
for (let i = 0; i < attachments.length; i++) {
|
|
1095
|
+
const att = attachments[i];
|
|
1096
|
+
body.push(
|
|
1097
|
+
`${indent}- Image #${i + 1}: ${att.taskCopyPath} (filename: ${att.originalFileName})`
|
|
1098
|
+
);
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
function visualPromptHasAttachments(attachmentMap) {
|
|
1102
|
+
for (const list of attachmentMap.values()) {
|
|
1103
|
+
if (list.length > 0) return true;
|
|
1104
|
+
}
|
|
1105
|
+
return false;
|
|
1106
|
+
}
|
|
939
1107
|
function buildVisualPromptDual(config, pageRoute, renderedUrl, dual, parsed, attachmentPathsByAnnotationId, addMenu = { itemById: /* @__PURE__ */ new Map() }) {
|
|
940
1108
|
const preamble = [
|
|
941
1109
|
`Project directory: ${config.projectDir}`,
|
|
@@ -952,9 +1120,20 @@ function buildVisualPromptDual(config, pageRoute, renderedUrl, dual, parsed, att
|
|
|
952
1120
|
"- The annotation instructions and attachments are listed below in text — do NOT rely on screenshot text.",
|
|
953
1121
|
"",
|
|
954
1122
|
ANNOTATION_TARGETING_RULES,
|
|
955
|
-
""
|
|
1123
|
+
"",
|
|
1124
|
+
...visualPromptHasAttachments(attachmentPathsByAnnotationId) ? [buildVisualAttachmentRules(config.projectDir), ""] : []
|
|
956
1125
|
];
|
|
957
1126
|
const body = [];
|
|
1127
|
+
const pageComponents = parsed.structured?.pageComponents ?? addMenu.pageComponents ?? [];
|
|
1128
|
+
if (pageComponents.length > 0) {
|
|
1129
|
+
const section = buildPageComponentsPromptSection(pageComponents);
|
|
1130
|
+
if (section.trim()) body.push(section.trim(), "");
|
|
1131
|
+
}
|
|
1132
|
+
const pageAssets = parsed.structured?.pageAssets ?? addMenu.pageAssets ?? [];
|
|
1133
|
+
if (pageAssets.length > 0) {
|
|
1134
|
+
const section = buildPageAssetsPromptSection(pageAssets);
|
|
1135
|
+
if (section.trim()) body.push(section.trim(), "");
|
|
1136
|
+
}
|
|
958
1137
|
const routeMigration = parsed.structured?.routeMigration ?? void 0;
|
|
959
1138
|
const requestAddMenu = parsed.structured?.addMenuAttachments ?? [];
|
|
960
1139
|
if (requestAddMenu.length > 0) {
|
|
@@ -981,16 +1160,7 @@ function buildVisualPromptDual(config, pageRoute, renderedUrl, dual, parsed, att
|
|
|
981
1160
|
appendLegacyBindingLines(body, ann, ann.id);
|
|
982
1161
|
}
|
|
983
1162
|
body.push(`Instruction: ${ann.instruction.trim()}`);
|
|
984
|
-
|
|
985
|
-
body.push(
|
|
986
|
-
"Attachments (read these files; they belong to this annotation only):"
|
|
987
|
-
);
|
|
988
|
-
for (const p of paths) {
|
|
989
|
-
body.push(`- ${p}`);
|
|
990
|
-
}
|
|
991
|
-
} else {
|
|
992
|
-
body.push("Attachments: (none)");
|
|
993
|
-
}
|
|
1163
|
+
appendVisualAttachmentLines(body, paths);
|
|
994
1164
|
body.push("");
|
|
995
1165
|
}
|
|
996
1166
|
} else {
|
|
@@ -1025,7 +1195,8 @@ function buildVisualPromptVideo(config, pageRoute, renderedUrl, videoPath, frame
|
|
|
1025
1195
|
"",
|
|
1026
1196
|
...navLines,
|
|
1027
1197
|
ANNOTATION_TARGETING_RULES,
|
|
1028
|
-
""
|
|
1198
|
+
"",
|
|
1199
|
+
...visualPromptHasAttachments(attachmentPathsByPin) ? [buildVisualAttachmentRules(config.projectDir), ""] : []
|
|
1029
1200
|
];
|
|
1030
1201
|
if (!sv || sv.annotations.length === 0) {
|
|
1031
1202
|
return [
|
|
@@ -1039,6 +1210,16 @@ function buildVisualPromptVideo(config, pageRoute, renderedUrl, videoPath, frame
|
|
|
1039
1210
|
pinById.set(a.id, String(i + 1));
|
|
1040
1211
|
}
|
|
1041
1212
|
const body = [];
|
|
1213
|
+
const pageComponents = sv.pageComponents ?? addMenu.pageComponents ?? [];
|
|
1214
|
+
if (pageComponents.length > 0) {
|
|
1215
|
+
const section = buildPageComponentsPromptSection(pageComponents);
|
|
1216
|
+
if (section.trim()) body.push(section.trim(), "");
|
|
1217
|
+
}
|
|
1218
|
+
const pageAssets = sv.pageAssets ?? addMenu.pageAssets ?? [];
|
|
1219
|
+
if (pageAssets.length > 0) {
|
|
1220
|
+
const section = buildPageAssetsPromptSection(pageAssets);
|
|
1221
|
+
if (section.trim()) body.push(section.trim(), "");
|
|
1222
|
+
}
|
|
1042
1223
|
const routeMigration = sv.routeMigration;
|
|
1043
1224
|
const requestAddMenu = sv.addMenuAttachments ?? [];
|
|
1044
1225
|
if (requestAddMenu.length > 0) {
|
|
@@ -1079,10 +1260,7 @@ function buildVisualPromptVideo(config, pageRoute, renderedUrl, videoPath, frame
|
|
|
1079
1260
|
body.push(` Page path at this pin: ${ann.pagePathAtTime}`);
|
|
1080
1261
|
}
|
|
1081
1262
|
if (paths.length > 0) {
|
|
1082
|
-
body
|
|
1083
|
-
" Attachments (read these files; they belong to this pin only):"
|
|
1084
|
-
);
|
|
1085
|
-
for (const p of paths) body.push(` - ${p}`);
|
|
1263
|
+
appendVisualAttachmentLines(body, paths, " ");
|
|
1086
1264
|
} else {
|
|
1087
1265
|
body.push(" Attachments: (none)");
|
|
1088
1266
|
}
|
|
@@ -1091,12 +1269,32 @@ function buildVisualPromptVideo(config, pageRoute, renderedUrl, videoPath, frame
|
|
|
1091
1269
|
}
|
|
1092
1270
|
return [...preamble, ...body].join("\n");
|
|
1093
1271
|
}
|
|
1272
|
+
function distinctTimeSecsAscending(annotations) {
|
|
1273
|
+
const s = /* @__PURE__ */ new Set();
|
|
1274
|
+
for (const a of annotations) {
|
|
1275
|
+
s.add(a.timeSec);
|
|
1276
|
+
}
|
|
1277
|
+
return [...s].sort((a, b) => a - b);
|
|
1278
|
+
}
|
|
1094
1279
|
function persistFile(file, destDir, fileName) {
|
|
1095
1280
|
fs$1.mkdirSync(destDir, { recursive: true });
|
|
1096
1281
|
const dest = path.join(destDir, fileName ?? file.name);
|
|
1097
1282
|
fs$1.copyFileSync(file.path, dest);
|
|
1098
1283
|
return dest;
|
|
1099
1284
|
}
|
|
1285
|
+
function persistExtraFileAttachments(taskDir, extraFiles) {
|
|
1286
|
+
const attachmentPaths = /* @__PURE__ */ new Map();
|
|
1287
|
+
for (const [key, file] of Object.entries(extraFiles)) {
|
|
1288
|
+
if (!key.startsWith("attachment_")) continue;
|
|
1289
|
+
const pinId = key.split("_")[1];
|
|
1290
|
+
const attDir = path.join(taskDir, `annotation-${pinId}`);
|
|
1291
|
+
const persisted = persistVisualTaskAttachment(file, attDir);
|
|
1292
|
+
const existing = attachmentPaths.get(pinId) ?? [];
|
|
1293
|
+
existing.push(persisted);
|
|
1294
|
+
attachmentPaths.set(pinId, existing);
|
|
1295
|
+
}
|
|
1296
|
+
return attachmentPaths;
|
|
1297
|
+
}
|
|
1100
1298
|
const submitTaskAction = makeJayStream("aiditor.submitTask").withFiles().withHandler(async function* (input) {
|
|
1101
1299
|
const projectDir = process.cwd();
|
|
1102
1300
|
const buildFolder = path.join(projectDir, "build");
|
|
@@ -1110,7 +1308,8 @@ const submitTaskAction = makeJayStream("aiditor.submitTask").withFiles().withHan
|
|
|
1110
1308
|
const isVisual = !!input.visualTask;
|
|
1111
1309
|
const catalog = await listAddMenuItems(projectDir);
|
|
1112
1310
|
const addMenu = {
|
|
1113
|
-
itemById: new Map(catalog.items.map((item) => [item.id, item]))
|
|
1311
|
+
itemById: new Map(catalog.items.map((item) => [item.id, item])),
|
|
1312
|
+
pageAssets: parsed.structured?.pageAssets ?? parsed.structuredVideo?.pageAssets ?? void 0
|
|
1114
1313
|
};
|
|
1115
1314
|
let promptContent;
|
|
1116
1315
|
if (isVideo) {
|
|
@@ -1121,14 +1320,13 @@ const submitTaskAction = makeJayStream("aiditor.submitTask").withFiles().withHan
|
|
|
1121
1320
|
}
|
|
1122
1321
|
const extra = input.extraFiles ?? {};
|
|
1123
1322
|
const frames = [];
|
|
1323
|
+
const momentTimes = parsed.structuredVideo ? distinctTimeSecsAscending(parsed.structuredVideo.annotations) : [];
|
|
1124
1324
|
for (let i = 0; ; i++) {
|
|
1125
1325
|
const markers = extra[`frame_${i}_markers_only`];
|
|
1126
1326
|
const clean = extra[`frame_${i}_clean`];
|
|
1127
1327
|
if (!markers && !clean) break;
|
|
1128
1328
|
const frameDir = path.join(taskDir, "frames", String(i));
|
|
1129
|
-
const timeSec =
|
|
1130
|
-
(a, idx) => idx === i || a.timeSec !== void 0
|
|
1131
|
-
)?.timeSec ?? i;
|
|
1329
|
+
const timeSec = momentTimes[i] ?? i;
|
|
1132
1330
|
frames.push({
|
|
1133
1331
|
timeSec,
|
|
1134
1332
|
frameIndex: i,
|
|
@@ -1136,16 +1334,7 @@ const submitTaskAction = makeJayStream("aiditor.submitTask").withFiles().withHan
|
|
|
1136
1334
|
markersOnly: markers ? persistFile(markers, frameDir, "markers-only.png") : ""
|
|
1137
1335
|
});
|
|
1138
1336
|
}
|
|
1139
|
-
const attachmentPathsByPin =
|
|
1140
|
-
for (const [key, file] of Object.entries(extra)) {
|
|
1141
|
-
if (!key.startsWith("attachment_")) continue;
|
|
1142
|
-
const pinId = key.split("_")[1];
|
|
1143
|
-
const attDir = path.join(taskDir, `annotation-${pinId}`);
|
|
1144
|
-
const filePath = persistFile(file, attDir);
|
|
1145
|
-
const existing = attachmentPathsByPin.get(pinId) ?? [];
|
|
1146
|
-
existing.push(filePath);
|
|
1147
|
-
attachmentPathsByPin.set(pinId, existing);
|
|
1148
|
-
}
|
|
1337
|
+
const attachmentPathsByPin = persistExtraFileAttachments(taskDir, extra);
|
|
1149
1338
|
promptContent = buildVisualPromptVideo(
|
|
1150
1339
|
config,
|
|
1151
1340
|
input.pageRoute ?? "/",
|
|
@@ -1167,16 +1356,10 @@ const submitTaskAction = makeJayStream("aiditor.submitTask").withFiles().withHan
|
|
|
1167
1356
|
)
|
|
1168
1357
|
};
|
|
1169
1358
|
const extraFiles = input.extraFiles ?? {};
|
|
1170
|
-
const attachmentPathsByAnnotationId =
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
const attDir = path.join(taskDir, `annotation-${pinId}`);
|
|
1175
|
-
const filePath = persistFile(file, attDir);
|
|
1176
|
-
const existing = attachmentPathsByAnnotationId.get(pinId) ?? [];
|
|
1177
|
-
existing.push(filePath);
|
|
1178
|
-
attachmentPathsByAnnotationId.set(pinId, existing);
|
|
1179
|
-
}
|
|
1359
|
+
const attachmentPathsByAnnotationId = persistExtraFileAttachments(
|
|
1360
|
+
taskDir,
|
|
1361
|
+
extraFiles
|
|
1362
|
+
);
|
|
1180
1363
|
promptContent = buildVisualPromptDual(
|
|
1181
1364
|
config,
|
|
1182
1365
|
input.pageRoute ?? "/",
|
|
@@ -1283,17 +1466,19 @@ const submitTaskAction = makeJayStream("aiditor.submitTask").withFiles().withHan
|
|
|
1283
1466
|
});
|
|
1284
1467
|
const cancelAgentTaskAction = makeJayQuery(
|
|
1285
1468
|
"aiditor.cancelAgentTask"
|
|
1286
|
-
).withHandler(
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1469
|
+
).withHandler(
|
|
1470
|
+
async (input) => {
|
|
1471
|
+
const routeKey = input.cancelKey?.trim() || resolveAgentContextKey({
|
|
1472
|
+
pageRoute: input.pageRoute ?? "/",
|
|
1473
|
+
renderedUrl: input.renderedUrl
|
|
1474
|
+
});
|
|
1475
|
+
const cancelled = cancelAgentTask(routeKey);
|
|
1476
|
+
if (!cancelled) {
|
|
1477
|
+
forceReleaseAgentRoute(routeKey);
|
|
1478
|
+
}
|
|
1479
|
+
return { cancelled: true };
|
|
1294
1480
|
}
|
|
1295
|
-
|
|
1296
|
-
});
|
|
1481
|
+
);
|
|
1297
1482
|
class AddPagePromptLoadError extends Error {
|
|
1298
1483
|
constructor(message) {
|
|
1299
1484
|
super(message);
|
|
@@ -2119,6 +2304,12 @@ When the image is a formal Figma **design definitions** board, apply these rules
|
|
|
2119
2304
|
${designDefinitionsBody}`;
|
|
2120
2305
|
});
|
|
2121
2306
|
}
|
|
2307
|
+
class BriefFillCancelledError extends Error {
|
|
2308
|
+
constructor() {
|
|
2309
|
+
super("Stopped by user.");
|
|
2310
|
+
__publicField(this, "name", "BriefFillCancelledError");
|
|
2311
|
+
}
|
|
2312
|
+
}
|
|
2122
2313
|
const briefFillModelOverride = process.env.AIDITOR_BRIEF_FILL_MODEL?.trim();
|
|
2123
2314
|
const ALLOWED_IMAGE_MIMES = /* @__PURE__ */ new Set([
|
|
2124
2315
|
"image/png",
|
|
@@ -2286,6 +2477,9 @@ async function runBriefFillAgentQuery(input) {
|
|
|
2286
2477
|
thinking: { type: "disabled" }
|
|
2287
2478
|
}
|
|
2288
2479
|
})) {
|
|
2480
|
+
if (input.cancelKey && isAgentTaskCancelled(input.cancelKey)) {
|
|
2481
|
+
throw new BriefFillCancelledError();
|
|
2482
|
+
}
|
|
2289
2483
|
collected.push(message);
|
|
2290
2484
|
}
|
|
2291
2485
|
const rawText = extractBriefFillRawText(collected);
|
|
@@ -2314,6 +2508,19 @@ async function generateBriefFromImages(input, options) {
|
|
|
2314
2508
|
}
|
|
2315
2509
|
return { markdown };
|
|
2316
2510
|
}
|
|
2511
|
+
const BRIEF_FILL_CANCEL_PREFIX = "brief-fill:";
|
|
2512
|
+
function resolveBriefFillCancelKey(input) {
|
|
2513
|
+
const targetSuffix = input.target ? `:${input.target}` : "";
|
|
2514
|
+
const requestId = input.requestId?.trim();
|
|
2515
|
+
if (requestId) {
|
|
2516
|
+
return `${BRIEF_FILL_CANCEL_PREFIX}request:${requestId}${targetSuffix}`;
|
|
2517
|
+
}
|
|
2518
|
+
const route = input.pageRoute?.trim();
|
|
2519
|
+
if (route) {
|
|
2520
|
+
return `${BRIEF_FILL_CANCEL_PREFIX}route:${normalizePageRoute(route)}${targetSuffix}`;
|
|
2521
|
+
}
|
|
2522
|
+
return `${BRIEF_FILL_CANCEL_PREFIX}draft${targetSuffix}`;
|
|
2523
|
+
}
|
|
2317
2524
|
function persistUpload(file, destPath) {
|
|
2318
2525
|
fs$1.mkdirSync(path.dirname(destPath), { recursive: true });
|
|
2319
2526
|
fs$1.copyFileSync(file.path, destPath);
|
|
@@ -2653,15 +2860,27 @@ const generateAddPageBriefFromImageAction = makeJayQuery(
|
|
|
2653
2860
|
}
|
|
2654
2861
|
try {
|
|
2655
2862
|
const images = imageFiles.map(readImageAsBase64);
|
|
2656
|
-
const
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2863
|
+
const cancelKey = resolveBriefFillCancelKey({
|
|
2864
|
+
requestId: input.requestId,
|
|
2865
|
+
pageRoute: input.pageRoute,
|
|
2866
|
+
target: input.target
|
|
2867
|
+
});
|
|
2868
|
+
const cancelToken = beginCancellableAgentTask(cancelKey);
|
|
2869
|
+
let result;
|
|
2870
|
+
try {
|
|
2871
|
+
result = await generateBriefFromImages(
|
|
2872
|
+
{
|
|
2873
|
+
target: input.target,
|
|
2874
|
+
images,
|
|
2875
|
+
contextNotes: input.contextNotes,
|
|
2876
|
+
pageRoute: input.pageRoute,
|
|
2877
|
+
cancelKey
|
|
2878
|
+
},
|
|
2879
|
+
{ projectDir }
|
|
2880
|
+
);
|
|
2881
|
+
} finally {
|
|
2882
|
+
endCancellableAgentTask(cancelKey, cancelToken);
|
|
2883
|
+
}
|
|
2665
2884
|
const referenceAttachments = [];
|
|
2666
2885
|
if (input.requestId) {
|
|
2667
2886
|
for (const file of imageFiles) {
|
|
@@ -2690,6 +2909,9 @@ const generateAddPageBriefFromImageAction = makeJayQuery(
|
|
|
2690
2909
|
referenceAttachments
|
|
2691
2910
|
};
|
|
2692
2911
|
} catch (err) {
|
|
2912
|
+
if (err instanceof BriefFillCancelledError) {
|
|
2913
|
+
return { ok: false, error: err.message };
|
|
2914
|
+
}
|
|
2693
2915
|
return {
|
|
2694
2916
|
ok: false,
|
|
2695
2917
|
error: err instanceof Error ? err.message : String(err)
|
|
@@ -2712,10 +2934,10 @@ function isValidInstallSpec(spec) {
|
|
|
2712
2934
|
function getPluginSourcesPath(projectRoot) {
|
|
2713
2935
|
return path.join(projectRoot, ".aiditor", "plugin-sources.yaml");
|
|
2714
2936
|
}
|
|
2715
|
-
function parsePluginSourcesYaml(
|
|
2937
|
+
function parsePluginSourcesYaml(yaml2) {
|
|
2716
2938
|
const sources = [];
|
|
2717
2939
|
let current = null;
|
|
2718
|
-
for (const line of
|
|
2940
|
+
for (const line of yaml2.split("\n")) {
|
|
2719
2941
|
const itemMatch = line.match(/^ - pluginName:\s*(.+)$/);
|
|
2720
2942
|
if (itemMatch) {
|
|
2721
2943
|
if (current?.pluginName && current.packageName && current.installSpec) {
|
|
@@ -3443,24 +3665,61 @@ function pluginNameFromItemId(itemId) {
|
|
|
3443
3665
|
if (colon <= 0) return null;
|
|
3444
3666
|
return itemId.slice(0, colon);
|
|
3445
3667
|
}
|
|
3446
|
-
async function
|
|
3447
|
-
const
|
|
3448
|
-
const contractName = contractNameFromItemId(item.id);
|
|
3449
|
-
if (!pluginName || !contractName) return null;
|
|
3450
|
-
const contractPath = path.join(
|
|
3668
|
+
async function resolveContractFilePath(projectRoot, input) {
|
|
3669
|
+
const materializedPath = path.join(
|
|
3451
3670
|
projectRoot,
|
|
3452
3671
|
"agent-kit",
|
|
3453
3672
|
"materialized-contracts",
|
|
3454
|
-
pluginName,
|
|
3455
|
-
`${contractName}.jay-contract`
|
|
3673
|
+
input.pluginName,
|
|
3674
|
+
`${input.contractName}.jay-contract`
|
|
3456
3675
|
);
|
|
3457
|
-
|
|
3676
|
+
const indexPath = path.join(projectRoot, "agent-kit", "plugins-index.yaml");
|
|
3677
|
+
try {
|
|
3678
|
+
const indexYaml = await fs.readFile(indexPath, "utf-8");
|
|
3679
|
+
const index = load(indexYaml);
|
|
3680
|
+
const plugin = index.plugins?.find((p) => p.name === input.pluginName);
|
|
3681
|
+
const contract = plugin?.contracts?.find(
|
|
3682
|
+
(c) => c.name === input.contractName
|
|
3683
|
+
);
|
|
3684
|
+
if (contract?.path) {
|
|
3685
|
+
const resolved = path.resolve(
|
|
3686
|
+
projectRoot,
|
|
3687
|
+
contract.path.replace(/^\.\//, "")
|
|
3688
|
+
);
|
|
3689
|
+
try {
|
|
3690
|
+
await fs.access(resolved);
|
|
3691
|
+
return resolved;
|
|
3692
|
+
} catch {
|
|
3693
|
+
}
|
|
3694
|
+
}
|
|
3695
|
+
} catch (e) {
|
|
3696
|
+
const code = e && typeof e === "object" && "code" in e ? e.code : void 0;
|
|
3697
|
+
if (code !== "ENOENT") throw e;
|
|
3698
|
+
}
|
|
3458
3699
|
try {
|
|
3459
|
-
|
|
3700
|
+
await fs.access(materializedPath);
|
|
3701
|
+
return materializedPath;
|
|
3702
|
+
} catch {
|
|
3703
|
+
throw new Error(
|
|
3704
|
+
`Contract "${input.contractName}" not found for plugin "${input.pluginName}". Check agent-kit/plugins-index.yaml or run \`yarn agent-kit\`.`
|
|
3705
|
+
);
|
|
3706
|
+
}
|
|
3707
|
+
}
|
|
3708
|
+
async function resolveContractParamsForItem(projectRoot, item) {
|
|
3709
|
+
const pluginName = item.pluginName ?? pluginNameFromItemId(item.id);
|
|
3710
|
+
const contractName = contractNameFromItemId(item.id);
|
|
3711
|
+
if (!pluginName || !contractName) return null;
|
|
3712
|
+
let yaml2;
|
|
3713
|
+
try {
|
|
3714
|
+
const contractPath = await resolveContractFilePath(projectRoot, {
|
|
3715
|
+
pluginName,
|
|
3716
|
+
contractName
|
|
3717
|
+
});
|
|
3718
|
+
yaml2 = await fs.readFile(contractPath, "utf-8");
|
|
3460
3719
|
} catch {
|
|
3461
3720
|
return null;
|
|
3462
3721
|
}
|
|
3463
|
-
return parseContractParamsFromYaml(
|
|
3722
|
+
return parseContractParamsFromYaml(yaml2, contractName, pluginName);
|
|
3464
3723
|
}
|
|
3465
3724
|
const listAddMenuItemsAction = makeJayQuery("aiditor.listAddMenuItems").withServices(DEV_SERVER_SERVICE).withHandler(async () => {
|
|
3466
3725
|
const projectRoot = process.cwd();
|
|
@@ -3792,6 +4051,460 @@ const writePluginSourceAction = makeJayQuery("aiditor.writePluginSource").withSe
|
|
|
3792
4051
|
await writePluginSource(projectRoot, input);
|
|
3793
4052
|
return { ok: true };
|
|
3794
4053
|
});
|
|
4054
|
+
const PAGE_ASSETS_VERSION = 1;
|
|
4055
|
+
function pageAssetsDirSegment(contextKey) {
|
|
4056
|
+
const trimmed = contextKey.replace(/^\//, "").replace(/\/$/, "");
|
|
4057
|
+
return trimmed === "" ? "_root" : trimmed.replace(/\//g, "--");
|
|
4058
|
+
}
|
|
4059
|
+
function emptyPageAssetsFile(input) {
|
|
4060
|
+
return {
|
|
4061
|
+
version: PAGE_ASSETS_VERSION,
|
|
4062
|
+
contextKey: input.contextKey,
|
|
4063
|
+
pageRoute: input.pageRoute,
|
|
4064
|
+
renderedUrl: input.renderedUrl,
|
|
4065
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4066
|
+
assets: [],
|
|
4067
|
+
suppressedDetectedKeys: []
|
|
4068
|
+
};
|
|
4069
|
+
}
|
|
4070
|
+
function getPageAssetsFilePath(projectDir, contextKey) {
|
|
4071
|
+
const segment = pageAssetsDirSegment(contextKey);
|
|
4072
|
+
return path.join(
|
|
4073
|
+
projectDir,
|
|
4074
|
+
".aiditor",
|
|
4075
|
+
"page-assets",
|
|
4076
|
+
segment,
|
|
4077
|
+
"page-assets.json"
|
|
4078
|
+
);
|
|
4079
|
+
}
|
|
4080
|
+
const fileMutexChains$1 = /* @__PURE__ */ new Map();
|
|
4081
|
+
async function withPageAssetsFileLock(filePath, fn) {
|
|
4082
|
+
const previous = fileMutexChains$1.get(filePath) ?? Promise.resolve();
|
|
4083
|
+
let release;
|
|
4084
|
+
const next = new Promise((resolve) => {
|
|
4085
|
+
release = resolve;
|
|
4086
|
+
});
|
|
4087
|
+
fileMutexChains$1.set(filePath, next);
|
|
4088
|
+
await previous;
|
|
4089
|
+
try {
|
|
4090
|
+
return await fn();
|
|
4091
|
+
} finally {
|
|
4092
|
+
release();
|
|
4093
|
+
if (fileMutexChains$1.get(filePath) === next) {
|
|
4094
|
+
fileMutexChains$1.delete(filePath);
|
|
4095
|
+
}
|
|
4096
|
+
}
|
|
4097
|
+
}
|
|
4098
|
+
async function readPageAssetsFile(filePath, fallback) {
|
|
4099
|
+
try {
|
|
4100
|
+
const raw = await fs.readFile(filePath, "utf-8");
|
|
4101
|
+
const parsed = JSON.parse(raw);
|
|
4102
|
+
if (parsed.version !== PAGE_ASSETS_VERSION || typeof parsed.contextKey !== "string" || typeof parsed.pageRoute !== "string" || !Array.isArray(parsed.assets)) {
|
|
4103
|
+
return fallback;
|
|
4104
|
+
}
|
|
4105
|
+
return {
|
|
4106
|
+
version: PAGE_ASSETS_VERSION,
|
|
4107
|
+
contextKey: parsed.contextKey,
|
|
4108
|
+
pageRoute: parsed.pageRoute,
|
|
4109
|
+
renderedUrl: typeof parsed.renderedUrl === "string" ? parsed.renderedUrl : void 0,
|
|
4110
|
+
updatedAt: typeof parsed.updatedAt === "string" ? parsed.updatedAt : (/* @__PURE__ */ new Date()).toISOString(),
|
|
4111
|
+
assets: parsed.assets.filter(
|
|
4112
|
+
(a) => !!a && typeof a === "object" && typeof a.id === "string" && typeof a.title === "string" && typeof a.prompt === "string"
|
|
4113
|
+
),
|
|
4114
|
+
suppressedDetectedKeys: Array.isArray(parsed.suppressedDetectedKeys) ? parsed.suppressedDetectedKeys.filter(
|
|
4115
|
+
(k) => typeof k === "string"
|
|
4116
|
+
) : [],
|
|
4117
|
+
routeMigration: parsed.routeMigration
|
|
4118
|
+
};
|
|
4119
|
+
} catch (e) {
|
|
4120
|
+
const code = e && typeof e === "object" && "code" in e ? e.code : void 0;
|
|
4121
|
+
if (code === "ENOENT") {
|
|
4122
|
+
return fallback;
|
|
4123
|
+
}
|
|
4124
|
+
throw e;
|
|
4125
|
+
}
|
|
4126
|
+
}
|
|
4127
|
+
async function writePageAssetsFileAtomic(filePath, file) {
|
|
4128
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
4129
|
+
const tmp = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
4130
|
+
const body = `${JSON.stringify(file, null, 2)}
|
|
4131
|
+
`;
|
|
4132
|
+
await fs.writeFile(tmp, body, "utf-8");
|
|
4133
|
+
await fs.rename(tmp, filePath);
|
|
4134
|
+
}
|
|
4135
|
+
async function getPageAssets(projectDir, input) {
|
|
4136
|
+
const filePath = getPageAssetsFilePath(projectDir, input.contextKey);
|
|
4137
|
+
const fallback = emptyPageAssetsFile(input);
|
|
4138
|
+
return withPageAssetsFileLock(
|
|
4139
|
+
filePath,
|
|
4140
|
+
async () => readPageAssetsFile(filePath, fallback)
|
|
4141
|
+
);
|
|
4142
|
+
}
|
|
4143
|
+
async function savePageAssets(projectDir, file) {
|
|
4144
|
+
const filePath = getPageAssetsFilePath(projectDir, file.contextKey);
|
|
4145
|
+
const next = {
|
|
4146
|
+
...file,
|
|
4147
|
+
version: PAGE_ASSETS_VERSION,
|
|
4148
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4149
|
+
};
|
|
4150
|
+
await withPageAssetsFileLock(filePath, async () => {
|
|
4151
|
+
await writePageAssetsFileAtomic(filePath, next);
|
|
4152
|
+
});
|
|
4153
|
+
}
|
|
4154
|
+
const PAGE_META_VERSION = 1;
|
|
4155
|
+
function pageMetaDirSegment(contextKey) {
|
|
4156
|
+
const trimmed = contextKey.replace(/^\//, "").replace(/\/$/, "");
|
|
4157
|
+
return trimmed === "" ? "_root" : trimmed.replace(/\//g, "--");
|
|
4158
|
+
}
|
|
4159
|
+
function emptyPageMetaFile(input) {
|
|
4160
|
+
return {
|
|
4161
|
+
version: PAGE_META_VERSION,
|
|
4162
|
+
contextKey: input.contextKey,
|
|
4163
|
+
pageRoute: input.pageRoute,
|
|
4164
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4165
|
+
};
|
|
4166
|
+
}
|
|
4167
|
+
function getPageMetaFilePath(projectDir, contextKey) {
|
|
4168
|
+
const segment = pageMetaDirSegment(contextKey);
|
|
4169
|
+
return path.join(
|
|
4170
|
+
projectDir,
|
|
4171
|
+
".aiditor",
|
|
4172
|
+
"page-meta",
|
|
4173
|
+
segment,
|
|
4174
|
+
"page-meta.json"
|
|
4175
|
+
);
|
|
4176
|
+
}
|
|
4177
|
+
const fileMutexChains = /* @__PURE__ */ new Map();
|
|
4178
|
+
async function withPageMetaFileLock(filePath, fn) {
|
|
4179
|
+
const previous = fileMutexChains.get(filePath) ?? Promise.resolve();
|
|
4180
|
+
let release;
|
|
4181
|
+
const next = new Promise((resolve) => {
|
|
4182
|
+
release = resolve;
|
|
4183
|
+
});
|
|
4184
|
+
fileMutexChains.set(filePath, next);
|
|
4185
|
+
await previous;
|
|
4186
|
+
try {
|
|
4187
|
+
return await fn();
|
|
4188
|
+
} finally {
|
|
4189
|
+
release();
|
|
4190
|
+
if (fileMutexChains.get(filePath) === next) {
|
|
4191
|
+
fileMutexChains.delete(filePath);
|
|
4192
|
+
}
|
|
4193
|
+
}
|
|
4194
|
+
}
|
|
4195
|
+
async function readPageMetaFile(filePath, fallback) {
|
|
4196
|
+
try {
|
|
4197
|
+
const raw = await fs.readFile(filePath, "utf-8");
|
|
4198
|
+
const parsed = JSON.parse(raw);
|
|
4199
|
+
if (parsed.version !== PAGE_META_VERSION || typeof parsed.contextKey !== "string" || typeof parsed.pageRoute !== "string") {
|
|
4200
|
+
return fallback;
|
|
4201
|
+
}
|
|
4202
|
+
return {
|
|
4203
|
+
version: PAGE_META_VERSION,
|
|
4204
|
+
contextKey: parsed.contextKey,
|
|
4205
|
+
pageRoute: parsed.pageRoute,
|
|
4206
|
+
updatedAt: typeof parsed.updatedAt === "string" ? parsed.updatedAt : (/* @__PURE__ */ new Date()).toISOString(),
|
|
4207
|
+
plannedRoute: typeof parsed.plannedRoute === "string" ? parsed.plannedRoute : void 0,
|
|
4208
|
+
routeMigration: parsed.routeMigration
|
|
4209
|
+
};
|
|
4210
|
+
} catch (e) {
|
|
4211
|
+
const code = e && typeof e === "object" && "code" in e ? e.code : void 0;
|
|
4212
|
+
if (code === "ENOENT") {
|
|
4213
|
+
return fallback;
|
|
4214
|
+
}
|
|
4215
|
+
throw e;
|
|
4216
|
+
}
|
|
4217
|
+
}
|
|
4218
|
+
async function readLegacyPageAssets(projectDir, contextKey) {
|
|
4219
|
+
const filePath = getPageAssetsFilePath(projectDir, contextKey);
|
|
4220
|
+
try {
|
|
4221
|
+
const raw = await fs.readFile(filePath, "utf-8");
|
|
4222
|
+
const parsed = JSON.parse(raw);
|
|
4223
|
+
if (typeof parsed.contextKey !== "string") return null;
|
|
4224
|
+
return parsed;
|
|
4225
|
+
} catch {
|
|
4226
|
+
return null;
|
|
4227
|
+
}
|
|
4228
|
+
}
|
|
4229
|
+
async function writePageMetaFileAtomic(filePath, file) {
|
|
4230
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
4231
|
+
const tmp = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
4232
|
+
const body = `${JSON.stringify(file, null, 2)}
|
|
4233
|
+
`;
|
|
4234
|
+
await fs.writeFile(tmp, body, "utf-8");
|
|
4235
|
+
await fs.rename(tmp, filePath);
|
|
4236
|
+
}
|
|
4237
|
+
async function fileExists(filePath) {
|
|
4238
|
+
try {
|
|
4239
|
+
await fs.access(filePath);
|
|
4240
|
+
return true;
|
|
4241
|
+
} catch {
|
|
4242
|
+
return false;
|
|
4243
|
+
}
|
|
4244
|
+
}
|
|
4245
|
+
async function getPageMeta(projectDir, input) {
|
|
4246
|
+
const filePath = getPageMetaFilePath(projectDir, input.contextKey);
|
|
4247
|
+
const fallback = emptyPageMetaFile(input);
|
|
4248
|
+
return withPageMetaFileLock(filePath, async () => {
|
|
4249
|
+
if (await fileExists(filePath)) {
|
|
4250
|
+
return readPageMetaFile(filePath, fallback);
|
|
4251
|
+
}
|
|
4252
|
+
const legacy = await readLegacyPageAssets(projectDir, input.contextKey);
|
|
4253
|
+
if (!legacy) return fallback;
|
|
4254
|
+
const migrated = {
|
|
4255
|
+
...fallback,
|
|
4256
|
+
contextKey: input.contextKey,
|
|
4257
|
+
pageRoute: input.pageRoute,
|
|
4258
|
+
plannedRoute: legacy.routeMigration?.plannedRoute,
|
|
4259
|
+
routeMigration: legacy.routeMigration,
|
|
4260
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4261
|
+
};
|
|
4262
|
+
await writePageMetaFileAtomic(filePath, migrated);
|
|
4263
|
+
return migrated;
|
|
4264
|
+
});
|
|
4265
|
+
}
|
|
4266
|
+
async function savePageMeta(projectDir, file) {
|
|
4267
|
+
const filePath = getPageMetaFilePath(projectDir, file.contextKey);
|
|
4268
|
+
const next = {
|
|
4269
|
+
...file,
|
|
4270
|
+
version: PAGE_META_VERSION,
|
|
4271
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4272
|
+
};
|
|
4273
|
+
await withPageMetaFileLock(filePath, async () => {
|
|
4274
|
+
await writePageMetaFileAtomic(filePath, next);
|
|
4275
|
+
});
|
|
4276
|
+
}
|
|
4277
|
+
function formatTagDescription(description) {
|
|
4278
|
+
if (!description?.length) return void 0;
|
|
4279
|
+
return description.join(" ");
|
|
4280
|
+
}
|
|
4281
|
+
function tagKindFromTypes(types) {
|
|
4282
|
+
if (types.includes(ContractTagType.subContract)) return "sub-contract";
|
|
4283
|
+
if (types.includes(ContractTagType.interactive)) return "interactive";
|
|
4284
|
+
if (types.includes(ContractTagType.variant)) return "variant";
|
|
4285
|
+
return "data";
|
|
4286
|
+
}
|
|
4287
|
+
function flattenTags(tags, parentPath = "") {
|
|
4288
|
+
const results = [];
|
|
4289
|
+
for (const tag of tags) {
|
|
4290
|
+
const tagPath = parentPath ? `${parentPath}.${tag.tag}` : tag.tag;
|
|
4291
|
+
results.push({
|
|
4292
|
+
tagPath,
|
|
4293
|
+
tagKind: tagKindFromTypes(tag.type),
|
|
4294
|
+
linkedContract: tag.link,
|
|
4295
|
+
description: formatTagDescription(tag.description)
|
|
4296
|
+
});
|
|
4297
|
+
if (tag.tags?.length) {
|
|
4298
|
+
for (const child of tag.tags) {
|
|
4299
|
+
const childPath = `${tagPath}.${child.tag}`;
|
|
4300
|
+
results.push({
|
|
4301
|
+
tagPath: childPath,
|
|
4302
|
+
tagKind: tagKindFromTypes(child.type),
|
|
4303
|
+
linkedContract: child.link,
|
|
4304
|
+
description: formatTagDescription(child.description)
|
|
4305
|
+
});
|
|
4306
|
+
}
|
|
4307
|
+
}
|
|
4308
|
+
}
|
|
4309
|
+
return results;
|
|
4310
|
+
}
|
|
4311
|
+
async function resolveContractTagsForComponent(projectRoot, input) {
|
|
4312
|
+
const contractPath = await resolveContractFilePath(projectRoot, input);
|
|
4313
|
+
const yaml2 = await fs.readFile(contractPath, "utf-8");
|
|
4314
|
+
const fileName = path.basename(contractPath);
|
|
4315
|
+
const contract = checkValidationErrors(
|
|
4316
|
+
parseContract(yaml2, fileName)
|
|
4317
|
+
);
|
|
4318
|
+
return flattenTags(contract.tags);
|
|
4319
|
+
}
|
|
4320
|
+
const getPageMetaAction = makeJayQuery("aiditor.getPageMeta").withServices(DEV_SERVER_SERVICE).withHandler(async (input) => {
|
|
4321
|
+
const projectRoot = process.cwd();
|
|
4322
|
+
const file = await getPageMeta(projectRoot, input);
|
|
4323
|
+
return { file };
|
|
4324
|
+
});
|
|
4325
|
+
const savePageMetaAction = makeJayQuery("aiditor.savePageMeta").withServices(DEV_SERVER_SERVICE).withHandler(async (input) => {
|
|
4326
|
+
const projectRoot = process.cwd();
|
|
4327
|
+
await savePageMeta(projectRoot, input.file);
|
|
4328
|
+
return { ok: true };
|
|
4329
|
+
});
|
|
4330
|
+
const getContractInspectorTagsAction = makeJayQuery(
|
|
4331
|
+
"aiditor.getContractInspectorTags"
|
|
4332
|
+
).withServices(DEV_SERVER_SERVICE).withHandler(async (input) => {
|
|
4333
|
+
const projectRoot = process.cwd();
|
|
4334
|
+
const tags = await resolveContractTagsForComponent(projectRoot, input);
|
|
4335
|
+
return { tags };
|
|
4336
|
+
});
|
|
4337
|
+
function pluginNameFromPackageAttr(pluginAttr) {
|
|
4338
|
+
const prefix = "@jay-framework/";
|
|
4339
|
+
if (pluginAttr.startsWith(prefix)) {
|
|
4340
|
+
return pluginAttr.slice(prefix.length);
|
|
4341
|
+
}
|
|
4342
|
+
return pluginAttr;
|
|
4343
|
+
}
|
|
4344
|
+
const HEADLESS_SCRIPT_RE = /<script[^>]*type\s*=\s*["']application\/jay-headless["'][^>]*>/gi;
|
|
4345
|
+
const JAY_TAG_RE = /<jay:([a-zA-Z][\w-]*)\b/g;
|
|
4346
|
+
function readScriptAttr(tag, attr) {
|
|
4347
|
+
const re = new RegExp(`${attr}\\s*=\\s*["']([^"']+)["']`, "i");
|
|
4348
|
+
const m = tag.match(re);
|
|
4349
|
+
return m?.[1]?.trim();
|
|
4350
|
+
}
|
|
4351
|
+
function humanizeContractName(contract) {
|
|
4352
|
+
return contract.split(/[-_]/u).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
4353
|
+
}
|
|
4354
|
+
function catalogItemForDetection(pluginName, contract, catalogByPluginContract) {
|
|
4355
|
+
return catalogByPluginContract.get(`${pluginName}:${contract}`);
|
|
4356
|
+
}
|
|
4357
|
+
function buildCatalogLookup(items) {
|
|
4358
|
+
const map = /* @__PURE__ */ new Map();
|
|
4359
|
+
for (const item of items) {
|
|
4360
|
+
if (item.pluginName && item.id.includes(":")) {
|
|
4361
|
+
const contract = item.id.split(":").slice(1).join(":");
|
|
4362
|
+
map.set(`${item.pluginName}:${contract}`, item);
|
|
4363
|
+
}
|
|
4364
|
+
}
|
|
4365
|
+
return map;
|
|
4366
|
+
}
|
|
4367
|
+
function detectPageComponentsFromHtml(pageHtml, catalogItems = []) {
|
|
4368
|
+
const catalogByPluginContract = buildCatalogLookup(catalogItems);
|
|
4369
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4370
|
+
const results = [];
|
|
4371
|
+
const headlessTags = pageHtml.match(HEADLESS_SCRIPT_RE) ?? [];
|
|
4372
|
+
for (const tag of headlessTags) {
|
|
4373
|
+
const pluginAttr = readScriptAttr(tag, "plugin");
|
|
4374
|
+
const contract = readScriptAttr(tag, "contract");
|
|
4375
|
+
const key = readScriptAttr(tag, "key");
|
|
4376
|
+
if (!pluginAttr || !contract) continue;
|
|
4377
|
+
const pluginName = pluginNameFromPackageAttr(pluginAttr);
|
|
4378
|
+
const detectedKey = key ? `headless:${pluginName}:${contract}:${key}` : `headless:${pluginName}:${contract}`;
|
|
4379
|
+
if (seen.has(detectedKey)) continue;
|
|
4380
|
+
seen.add(detectedKey);
|
|
4381
|
+
const catalogItem = catalogItemForDetection(
|
|
4382
|
+
pluginName,
|
|
4383
|
+
contract,
|
|
4384
|
+
catalogByPluginContract
|
|
4385
|
+
);
|
|
4386
|
+
results.push({
|
|
4387
|
+
detectedKey,
|
|
4388
|
+
title: catalogItem?.title ?? humanizeContractName(contract),
|
|
4389
|
+
prompt: catalogItem?.prompt ?? `Use headless component @jay-framework/${pluginName} / contract ${contract}.${key ? ` Script key: ${key}.` : ""}`,
|
|
4390
|
+
pluginName,
|
|
4391
|
+
contractName: contract,
|
|
4392
|
+
componentKey: key
|
|
4393
|
+
});
|
|
4394
|
+
}
|
|
4395
|
+
let jayMatch;
|
|
4396
|
+
const jayRe = new RegExp(JAY_TAG_RE.source, "g");
|
|
4397
|
+
while ((jayMatch = jayRe.exec(pageHtml)) !== null) {
|
|
4398
|
+
const rawTag = jayMatch[1];
|
|
4399
|
+
if (!rawTag) continue;
|
|
4400
|
+
const tagName = rawTag.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
|
4401
|
+
const detectedKey = `jay:${tagName}`;
|
|
4402
|
+
if (seen.has(detectedKey)) continue;
|
|
4403
|
+
seen.add(detectedKey);
|
|
4404
|
+
const contractGuess = tagName;
|
|
4405
|
+
const catalogItem = [...catalogItems].find((item) => {
|
|
4406
|
+
const idSuffix = item.id.split(":").slice(1).join(":");
|
|
4407
|
+
return idSuffix === contractGuess || idSuffix === tagName;
|
|
4408
|
+
});
|
|
4409
|
+
results.push({
|
|
4410
|
+
detectedKey,
|
|
4411
|
+
title: catalogItem?.title ?? humanizeContractName(tagName),
|
|
4412
|
+
prompt: catalogItem?.prompt ?? `Use <jay:${rawTag}> component on this page per agent-kit designer docs.`,
|
|
4413
|
+
pluginName: catalogItem?.pluginName,
|
|
4414
|
+
contractName: contractGuess
|
|
4415
|
+
});
|
|
4416
|
+
}
|
|
4417
|
+
return results;
|
|
4418
|
+
}
|
|
4419
|
+
function detectedToPageAssetEntry(detected) {
|
|
4420
|
+
return {
|
|
4421
|
+
id: crypto.randomUUID(),
|
|
4422
|
+
title: detected.title,
|
|
4423
|
+
prompt: detected.prompt,
|
|
4424
|
+
source: "detected",
|
|
4425
|
+
itemId: detected.pluginName ? `${detected.pluginName}:${detected.contractName ?? ""}` : void 0,
|
|
4426
|
+
persistOnPage: true,
|
|
4427
|
+
addedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4428
|
+
detectedKey: detected.detectedKey,
|
|
4429
|
+
detectedMeta: {
|
|
4430
|
+
contractName: detected.contractName,
|
|
4431
|
+
pluginName: detected.pluginName,
|
|
4432
|
+
componentKey: detected.componentKey
|
|
4433
|
+
}
|
|
4434
|
+
};
|
|
4435
|
+
}
|
|
4436
|
+
function mergeDetectedPageAssets(existingAssets, detected, suppressedDetectedKeys = []) {
|
|
4437
|
+
const suppressed = new Set(suppressedDetectedKeys);
|
|
4438
|
+
const activeDetectedKeys = new Set(
|
|
4439
|
+
existingAssets.filter((a) => a.source === "detected" && !a.userRemoved && a.detectedKey).map((a) => a.detectedKey)
|
|
4440
|
+
);
|
|
4441
|
+
const detectedKeySet = new Set(detected.map((d) => d.detectedKey));
|
|
4442
|
+
const nextAssets = existingAssets.filter((asset) => {
|
|
4443
|
+
if (asset.source !== "detected" || asset.userRemoved) return true;
|
|
4444
|
+
if (!asset.detectedKey) return true;
|
|
4445
|
+
return detectedKeySet.has(asset.detectedKey);
|
|
4446
|
+
});
|
|
4447
|
+
for (const component of detected) {
|
|
4448
|
+
if (suppressed.has(component.detectedKey)) continue;
|
|
4449
|
+
const tombstoned = nextAssets.some(
|
|
4450
|
+
(a) => a.source === "detected" && a.detectedKey === component.detectedKey && a.userRemoved
|
|
4451
|
+
);
|
|
4452
|
+
if (tombstoned) continue;
|
|
4453
|
+
if (activeDetectedKeys.has(component.detectedKey)) continue;
|
|
4454
|
+
nextAssets.push(detectedToPageAssetEntry(component));
|
|
4455
|
+
activeDetectedKeys.add(component.detectedKey);
|
|
4456
|
+
}
|
|
4457
|
+
return {
|
|
4458
|
+
assets: nextAssets,
|
|
4459
|
+
suppressedDetectedKeys: [...suppressed]
|
|
4460
|
+
};
|
|
4461
|
+
}
|
|
4462
|
+
const getPageAssetsAction = makeJayQuery("aiditor.getPageAssets").withServices(DEV_SERVER_SERVICE).withHandler(
|
|
4463
|
+
async (input) => {
|
|
4464
|
+
const projectRoot = process.cwd();
|
|
4465
|
+
const file = await getPageAssets(projectRoot, input);
|
|
4466
|
+
return { file };
|
|
4467
|
+
}
|
|
4468
|
+
);
|
|
4469
|
+
const savePageAssetsAction = makeJayQuery("aiditor.savePageAssets").withServices(DEV_SERVER_SERVICE).withHandler(async (input) => {
|
|
4470
|
+
const projectRoot = process.cwd();
|
|
4471
|
+
await savePageAssets(projectRoot, input.file);
|
|
4472
|
+
return { ok: true };
|
|
4473
|
+
});
|
|
4474
|
+
const syncDetectedPageAssetsAction = makeJayQuery(
|
|
4475
|
+
"aiditor.syncDetectedPageAssets"
|
|
4476
|
+
).withServices(DEV_SERVER_SERVICE).withHandler(
|
|
4477
|
+
async (input) => {
|
|
4478
|
+
const projectRoot = process.cwd();
|
|
4479
|
+
const file = await getPageAssets(projectRoot, input);
|
|
4480
|
+
let pageHtml = "";
|
|
4481
|
+
const jayHtmlPath = input.jayHtmlPath ?? await resolveJayHtmlPathForPageRoute(projectRoot, input.pageRoute);
|
|
4482
|
+
if (jayHtmlPath) {
|
|
4483
|
+
try {
|
|
4484
|
+
pageHtml = await fs.readFile(jayHtmlPath, "utf-8");
|
|
4485
|
+
} catch {
|
|
4486
|
+
pageHtml = "";
|
|
4487
|
+
}
|
|
4488
|
+
}
|
|
4489
|
+
const catalog = await listAddMenuItems(projectRoot);
|
|
4490
|
+
const detected = detectPageComponentsFromHtml(pageHtml, catalog.items);
|
|
4491
|
+
const merged = mergeDetectedPageAssets(
|
|
4492
|
+
file.assets,
|
|
4493
|
+
detected,
|
|
4494
|
+
file.suppressedDetectedKeys ?? []
|
|
4495
|
+
);
|
|
4496
|
+
const next = {
|
|
4497
|
+
...file,
|
|
4498
|
+
contextKey: input.contextKey,
|
|
4499
|
+
pageRoute: input.pageRoute,
|
|
4500
|
+
renderedUrl: input.renderedUrl ?? file.renderedUrl,
|
|
4501
|
+
assets: merged.assets,
|
|
4502
|
+
suppressedDetectedKeys: merged.suppressedDetectedKeys
|
|
4503
|
+
};
|
|
4504
|
+
await savePageAssets(projectRoot, next);
|
|
4505
|
+
return { file: next, detectedCount: detected.length };
|
|
4506
|
+
}
|
|
4507
|
+
);
|
|
3795
4508
|
const AIDITOR_PUBLISH_SCRIPT = "aiditor:publish";
|
|
3796
4509
|
async function projectHasAiditorPublishScript(projectRoot) {
|
|
3797
4510
|
try {
|
|
@@ -3956,6 +4669,9 @@ export {
|
|
|
3956
4669
|
ensureProjectPluginAction,
|
|
3957
4670
|
generateAddPageBriefFromImageAction,
|
|
3958
4671
|
getAiditorBootstrap,
|
|
4672
|
+
getContractInspectorTagsAction,
|
|
4673
|
+
getPageAssetsAction,
|
|
4674
|
+
getPageMetaAction,
|
|
3959
4675
|
getPageParamsAction,
|
|
3960
4676
|
getPluginSetupStatusAction,
|
|
3961
4677
|
getProjectInfoAction,
|
|
@@ -3970,12 +4686,15 @@ export {
|
|
|
3970
4686
|
resolveAddMenuContractParamsAction,
|
|
3971
4687
|
runAiditorPublishAction,
|
|
3972
4688
|
saveAddPageDraftAction,
|
|
4689
|
+
savePageAssetsAction,
|
|
4690
|
+
savePageMetaAction,
|
|
3973
4691
|
setupAiditor,
|
|
3974
4692
|
startAddPageRequestAction,
|
|
3975
4693
|
submitAddPageAction,
|
|
3976
4694
|
submitTaskAction,
|
|
3977
4695
|
syncAddMenuAttachmentsAction,
|
|
3978
4696
|
syncAddPagePluginManifestAction,
|
|
4697
|
+
syncDetectedPageAssetsAction,
|
|
3979
4698
|
uploadAddPageAssetAction,
|
|
3980
4699
|
writePluginSourceAction
|
|
3981
4700
|
};
|