@anweat/dsh-browser 0.1.8 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -6
- package/lib/approval-policy.js +19 -0
- package/lib/approval-policy.js.map +1 -1
- package/lib/auth-profiles.js +10 -1
- package/lib/auth-profiles.js.map +1 -1
- package/lib/automation-assets-rpc.d.ts +5 -0
- package/lib/automation-assets-rpc.js +63 -0
- package/lib/automation-assets-rpc.js.map +1 -0
- package/lib/automation-assets.d.ts +139 -0
- package/lib/automation-assets.js +372 -0
- package/lib/automation-assets.js.map +1 -0
- package/lib/automation-development.d.ts +13 -0
- package/lib/automation-development.js +37 -0
- package/lib/automation-development.js.map +1 -0
- package/lib/automation-execution.d.ts +14 -0
- package/lib/automation-execution.js +55 -0
- package/lib/automation-execution.js.map +1 -0
- package/lib/browser-service.d.ts +5 -0
- package/lib/browser-service.js +66 -14
- package/lib/browser-service.js.map +1 -1
- package/lib/client/SettingsCard.js +50 -2
- package/lib/client/SettingsCard.js.map +1 -1
- package/lib/client/automation-assets-client.d.ts +38 -0
- package/lib/client/automation-assets-client.js +83 -0
- package/lib/client/automation-assets-client.js.map +1 -0
- package/lib/client/form.d.ts +1 -1
- package/lib/client/form.js +26 -0
- package/lib/client/form.js.map +1 -1
- package/lib/client/index.d.ts +11 -0
- package/lib/client/index.js +11 -1
- package/lib/client/index.js.map +1 -1
- package/lib/client/locales.d.ts +28 -0
- package/lib/client/locales.js +10 -0
- package/lib/client/locales.js.map +1 -1
- package/lib/client/styles.d.ts +11 -0
- package/lib/client/styles.js +4 -1
- package/lib/client/styles.js.map +1 -1
- package/lib/client.js +533 -4
- package/lib/client.js.map +1 -1
- package/lib/config.d.ts +4 -0
- package/lib/config.js +21 -0
- package/lib/config.js.map +1 -1
- package/lib/freedom.d.ts +4 -1
- package/lib/freedom.js +10 -0
- package/lib/freedom.js.map +1 -1
- package/lib/index.d.ts +3 -1
- package/lib/index.js +7 -2
- package/lib/index.js.map +1 -1
- package/lib/scripts.d.ts +1 -1
- package/lib/scripts.js +3 -2
- package/lib/scripts.js.map +1 -1
- package/lib/tools.d.ts +2 -1
- package/lib/tools.js +149 -12
- package/lib/tools.js.map +1 -1
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -61,6 +61,55 @@ window.__ModuleLoader__.load({
|
|
|
61
61
|
backoffBaseMs: [1, 6e4],
|
|
62
62
|
cooldownMs: [100, 3e5]
|
|
63
63
|
};
|
|
64
|
+
const ASSET_POLICY_KEYS = /* @__PURE__ */ new Set([
|
|
65
|
+
"enabled",
|
|
66
|
+
"directory",
|
|
67
|
+
"persistenceMode",
|
|
68
|
+
"activationMode",
|
|
69
|
+
"minSuccessfulRuns",
|
|
70
|
+
"minDistinctSessions",
|
|
71
|
+
"successWindowDays",
|
|
72
|
+
"minSuccessRate",
|
|
73
|
+
"maxCandidates",
|
|
74
|
+
"candidateTtlDays",
|
|
75
|
+
"maxSuggestionsPerDay",
|
|
76
|
+
"maxDrafts",
|
|
77
|
+
"maxActiveAssets",
|
|
78
|
+
"retrievalTopK",
|
|
79
|
+
"catalogTokenBudget",
|
|
80
|
+
"modelDevelopmentEnabled",
|
|
81
|
+
"maxModelDraftWritesPerSession"
|
|
82
|
+
]);
|
|
83
|
+
function validAssetPolicy(value) {
|
|
84
|
+
if (Object.keys(value).some((key) => !ASSET_POLICY_KEYS.has(key))) return false;
|
|
85
|
+
if (value.enabled !== void 0 && typeof value.enabled !== "boolean") return false;
|
|
86
|
+
if (value.directory !== void 0 && typeof value.directory !== "string") return false;
|
|
87
|
+
if (value.modelDevelopmentEnabled !== void 0 && typeof value.modelDevelopmentEnabled !== "boolean") return false;
|
|
88
|
+
if (value.persistenceMode !== void 0 && ![
|
|
89
|
+
"off",
|
|
90
|
+
"manual",
|
|
91
|
+
"suggest",
|
|
92
|
+
"auto-draft"
|
|
93
|
+
].includes(String(value.persistenceMode))) return false;
|
|
94
|
+
if (value.activationMode !== void 0 && !["manual", "auto-tested"].includes(String(value.activationMode))) return false;
|
|
95
|
+
return Object.entries(value).every(([key, entry]) => {
|
|
96
|
+
if (![
|
|
97
|
+
"minSuccessfulRuns",
|
|
98
|
+
"minDistinctSessions",
|
|
99
|
+
"successWindowDays",
|
|
100
|
+
"minSuccessRate",
|
|
101
|
+
"maxCandidates",
|
|
102
|
+
"candidateTtlDays",
|
|
103
|
+
"maxSuggestionsPerDay",
|
|
104
|
+
"maxDrafts",
|
|
105
|
+
"maxActiveAssets",
|
|
106
|
+
"retrievalTopK",
|
|
107
|
+
"catalogTokenBudget",
|
|
108
|
+
"maxModelDraftWritesPerSession"
|
|
109
|
+
].includes(key)) return true;
|
|
110
|
+
return typeof entry === "number" && Number.isFinite(entry) && entry >= 0;
|
|
111
|
+
});
|
|
112
|
+
}
|
|
64
113
|
function validUsagePolicy(value) {
|
|
65
114
|
if (Object.keys(value).some((key) => !(key in POLICY_BOUNDS))) return false;
|
|
66
115
|
return Object.entries(POLICY_BOUNDS).every(([key, [min, max]]) => {
|
|
@@ -81,6 +130,7 @@ window.__ModuleLoader__.load({
|
|
|
81
130
|
booleanField("headless"),
|
|
82
131
|
booleanField("opencliEnabled"),
|
|
83
132
|
jsonField("usagePolicy", validUsagePolicy),
|
|
133
|
+
jsonField("automationAssets", validAssetPolicy),
|
|
84
134
|
booleanField("autoInstall"),
|
|
85
135
|
textField("storageStatePath"),
|
|
86
136
|
jsonField("authProfiles"),
|
|
@@ -316,7 +366,18 @@ window.__ModuleLoader__.load({
|
|
|
316
366
|
failed: "dsb-failed",
|
|
317
367
|
actions: "dsb-actions",
|
|
318
368
|
primary: "dsb-primary",
|
|
319
|
-
secondary: "dsb-secondary"
|
|
369
|
+
secondary: "dsb-secondary",
|
|
370
|
+
assetGroup: "dsb-asset-group",
|
|
371
|
+
assetRow: "dsb-asset-row",
|
|
372
|
+
assetToolbar: "dsb-asset-toolbar",
|
|
373
|
+
assetLayout: "dsb-asset-layout",
|
|
374
|
+
assetList: "dsb-asset-list",
|
|
375
|
+
assetItem: "dsb-asset-item",
|
|
376
|
+
assetSelected: "dsb-asset-selected",
|
|
377
|
+
assetTest: "dsb-asset-test",
|
|
378
|
+
assetTestForm: "dsb-asset-test-form",
|
|
379
|
+
assetEditor: "dsb-asset-editor",
|
|
380
|
+
invalidInput: "dsb-invalid-input"
|
|
320
381
|
};
|
|
321
382
|
function ensureStyles() {
|
|
322
383
|
if (document.getElementById("dsh-browser-settings-styles")) return;
|
|
@@ -331,7 +392,9 @@ window.__ModuleLoader__.load({
|
|
|
331
392
|
.dsb-input{box-sizing:border-box;width:100%;min-width:0;height:34px;padding:0 10px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-layer-3);font:inherit;font-size:13px;color:var(--dsw-alias-label-primary)}.dsb-input:focus-visible{outline:none;border-color:var(--dsw-alias-brand-primary)}.dsb-input:disabled{opacity:.55}.dsb-invalid .dsb-input{border-color:var(--dsw-alias-label-error)}.dsb-textarea{height:auto;padding:9px 10px;resize:vertical;line-height:1.45}.dsb-code{font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:12px}.dsb-reset{appearance:none;border:0;background:none;padding:0;color:var(--dsw-alias-brand-primary);font:inherit;font-size:11px;cursor:pointer}
|
|
332
393
|
.dsb-toggle{display:flex;align-items:flex-start;justify-content:space-between;gap:10px;padding-top:2px}.dsb-toggle-label{display:flex;align-items:flex-start;gap:9px;cursor:pointer}.dsb-check{width:16px;height:16px;flex:none;margin:2px 0 0;accent-color:var(--dsw-alias-brand-primary)}.dsb-advanced{padding:16px 0;border-top:1px solid var(--dsw-alias-border-l2)}.dsb-advanced>summary{cursor:pointer;font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary)}
|
|
333
394
|
.dsb-footer{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:14px 0 4px;border-top:1px solid var(--dsw-alias-border-l2)}.dsb-status,.dsb-failed{margin:0;font-size:12px}.dsb-status{color:var(--dsw-alias-label-tertiary)}.dsb-failed{color:var(--dsw-alias-label-error)}.dsb-actions{display:flex;gap:8px}.dsb-primary,.dsb-secondary{appearance:none;border-radius:8px;padding:6px 14px;font:inherit;font-size:13px;cursor:pointer}.dsb-primary{border:1px solid transparent;background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}.dsb-secondary{border:1px solid var(--dsw-alias-border-l2);background:transparent;color:var(--dsw-alias-label-primary)}.dsb-primary:disabled,.dsb-secondary:disabled,.dsb-reset:disabled{opacity:.4;cursor:default}
|
|
334
|
-
|
|
395
|
+
.dsb-asset-group{display:flex;flex-direction:column;gap:8px;margin-bottom:16px}.dsb-asset-group h4{margin:0;font-size:13px}.dsb-asset-row,.dsb-asset-toolbar{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px;border:1px solid var(--dsw-alias-border-l2);border-radius:9px}.dsb-asset-row p,.dsb-asset-toolbar p{margin:3px 0 0;font-size:11px;color:var(--dsw-alias-label-tertiary)}.dsb-asset-toolbar{margin-bottom:10px;border:0;padding:0}.dsb-asset-layout{display:grid;grid-template-columns:minmax(180px,.7fr) minmax(0,1.3fr);gap:12px}.dsb-asset-list{display:flex;flex-direction:column;gap:6px;max-height:500px;overflow:auto}.dsb-asset-item{display:flex;align-items:center;justify-content:space-between;gap:8px;width:100%;padding:9px 10px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);text-align:left;cursor:pointer}.dsb-asset-item span:first-child{display:flex;min-width:0;flex-direction:column;gap:3px}.dsb-asset-item small,.dsb-asset-test{font-size:10px;color:var(--dsw-alias-label-tertiary)}.dsb-asset-selected{border-color:var(--dsw-alias-brand-primary);background:color-mix(in srgb,var(--dsw-alias-brand-primary) 8%,var(--dsw-alias-bg-layer-3))}.dsb-asset-editor{display:flex;min-width:0;flex-direction:column;gap:8px}.dsb-asset-editor .dsb-actions{justify-content:flex-end;flex-wrap:wrap}.dsb-invalid-input{border-color:var(--dsw-alias-label-error)}
|
|
396
|
+
.dsb-asset-test-form{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:8px}.dsb-asset-test-form .dsb-textarea{min-height:64px}
|
|
397
|
+
@media(max-width:720px){.dsb-grid,.dsb-asset-layout{grid-template-columns:minmax(0,1fr)}.dsb-footer,.dsb-asset-row,.dsb-asset-toolbar{align-items:stretch;flex-direction:column}.dsb-actions{justify-content:flex-end}}@media(max-width:420px){.dsb-body{margin:0 12px}.dsb-actions{display:grid;grid-template-columns:1fr 1fr}.dsb-primary,.dsb-secondary{width:100%}}
|
|
335
398
|
`;
|
|
336
399
|
document.head.append(style);
|
|
337
400
|
}
|
|
@@ -538,7 +601,10 @@ window.__ModuleLoader__.load({
|
|
|
538
601
|
className: styles.sectionHead,
|
|
539
602
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t("usage") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("usageHint") })]
|
|
540
603
|
}),
|
|
541
|
-
|
|
604
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
605
|
+
className: styles.grid,
|
|
606
|
+
children: [json("usagePolicy", "usagePolicy", "usagePolicyHint", 10), json("automationAssets", "automationAssets", "automationAssetsHint", 14)]
|
|
607
|
+
}),
|
|
542
608
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
543
609
|
className: styles.notice,
|
|
544
610
|
role: "note",
|
|
@@ -546,6 +612,7 @@ window.__ModuleLoader__.load({
|
|
|
546
612
|
})
|
|
547
613
|
]
|
|
548
614
|
}),
|
|
615
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(AutomationAssetsPanel, { ...props }),
|
|
549
616
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
|
|
550
617
|
className: styles.advanced,
|
|
551
618
|
children: [
|
|
@@ -595,6 +662,254 @@ window.__ModuleLoader__.load({
|
|
|
595
662
|
}) : null]
|
|
596
663
|
});
|
|
597
664
|
}
|
|
665
|
+
function AutomationAssetsPanel(props) {
|
|
666
|
+
const { t } = props;
|
|
667
|
+
const state = props.useAutomationAssets((snapshot) => snapshot);
|
|
668
|
+
const [draft, setDraft] = (0, react.useState)("");
|
|
669
|
+
const [draftError, setDraftError] = (0, react.useState)(false);
|
|
670
|
+
const [testUrl, setTestUrl] = (0, react.useState)("");
|
|
671
|
+
const [testInputs, setTestInputs] = (0, react.useState)("{}");
|
|
672
|
+
const selected = state.selected;
|
|
673
|
+
(0, react.useEffect)(() => {
|
|
674
|
+
if (selected) setDraft(JSON.stringify(selected, null, 2));
|
|
675
|
+
}, [selected?.id, selected?.revision]);
|
|
676
|
+
const newAsset = (kind) => {
|
|
677
|
+
props.selectAutomationAsset(void 0);
|
|
678
|
+
setDraft(JSON.stringify(kind === "recipe" ? {
|
|
679
|
+
kind,
|
|
680
|
+
name: "New recipe",
|
|
681
|
+
description: "",
|
|
682
|
+
domains: [],
|
|
683
|
+
tags: [],
|
|
684
|
+
inputNames: [],
|
|
685
|
+
recipe: [{
|
|
686
|
+
type: "extract",
|
|
687
|
+
selector: "main",
|
|
688
|
+
mode: "text",
|
|
689
|
+
limit: 20
|
|
690
|
+
}]
|
|
691
|
+
} : {
|
|
692
|
+
kind,
|
|
693
|
+
name: "New userscript",
|
|
694
|
+
description: "",
|
|
695
|
+
domains: [],
|
|
696
|
+
tags: [],
|
|
697
|
+
inputNames: [],
|
|
698
|
+
source: "// ==UserScript==\n// @name New userscript\n// @match https://example.com/*\n// @grant none\n// ==/UserScript==\nreturn { title: document.title }"
|
|
699
|
+
}, null, 2));
|
|
700
|
+
setDraftError(false);
|
|
701
|
+
};
|
|
702
|
+
const save = async () => {
|
|
703
|
+
try {
|
|
704
|
+
const value = JSON.parse(draft);
|
|
705
|
+
if (selected?.id) value.id = selected.id;
|
|
706
|
+
await props.saveAutomationAsset(value);
|
|
707
|
+
setDraftError(false);
|
|
708
|
+
} catch {
|
|
709
|
+
setDraftError(true);
|
|
710
|
+
}
|
|
711
|
+
};
|
|
712
|
+
const runTest = async () => {
|
|
713
|
+
if (!selected || !testUrl.trim()) {
|
|
714
|
+
setDraftError(true);
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
try {
|
|
718
|
+
const inputs = JSON.parse(testInputs);
|
|
719
|
+
await props.testAutomationAsset(selected.id, testUrl.trim(), inputs);
|
|
720
|
+
setDraftError(false);
|
|
721
|
+
} catch {
|
|
722
|
+
setDraftError(true);
|
|
723
|
+
}
|
|
724
|
+
};
|
|
725
|
+
const candidates = state.snapshot?.candidates.filter((candidate) => candidate.suggestedAt && !candidate.dismissedAt) ?? [];
|
|
726
|
+
const assets = state.snapshot?.assets ?? [];
|
|
727
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
728
|
+
className: styles.section,
|
|
729
|
+
"data-dsh-browser-assets": true,
|
|
730
|
+
children: [
|
|
731
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
732
|
+
className: styles.sectionHead,
|
|
733
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t("assetLibrary") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("assetLibraryHint") })]
|
|
734
|
+
}),
|
|
735
|
+
state.loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
736
|
+
className: styles.notice,
|
|
737
|
+
children: t("assetLoading")
|
|
738
|
+
}) : null,
|
|
739
|
+
state.failed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
740
|
+
className: styles.failed,
|
|
741
|
+
role: "alert",
|
|
742
|
+
children: state.error || t("assetFailed")
|
|
743
|
+
}) : null,
|
|
744
|
+
candidates.length ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
745
|
+
className: styles.assetGroup,
|
|
746
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", { children: t("assetSuggestions") }), candidates.map((candidate) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("article", {
|
|
747
|
+
className: styles.assetRow,
|
|
748
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: candidate.title }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", { children: [
|
|
749
|
+
candidate.domain,
|
|
750
|
+
" · ",
|
|
751
|
+
candidate.successfulRuns,
|
|
752
|
+
" ",
|
|
753
|
+
t("assetRuns"),
|
|
754
|
+
" · ",
|
|
755
|
+
candidate.distinctSessions,
|
|
756
|
+
" ",
|
|
757
|
+
t("assetSessions")
|
|
758
|
+
] })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
759
|
+
className: styles.actions,
|
|
760
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
761
|
+
type: "button",
|
|
762
|
+
className: styles.secondary,
|
|
763
|
+
disabled: state.busy,
|
|
764
|
+
onClick: () => props.dismissAutomationCandidate(candidate.id),
|
|
765
|
+
children: t("assetDismiss")
|
|
766
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
767
|
+
type: "button",
|
|
768
|
+
className: styles.primary,
|
|
769
|
+
disabled: state.busy,
|
|
770
|
+
onClick: () => props.summarizeAutomationCandidate(candidate.id),
|
|
771
|
+
children: t("assetSummarize")
|
|
772
|
+
})]
|
|
773
|
+
})]
|
|
774
|
+
}, candidate.id))]
|
|
775
|
+
}) : null,
|
|
776
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
777
|
+
className: styles.assetToolbar,
|
|
778
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("assetScripts") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
779
|
+
className: styles.hint,
|
|
780
|
+
children: t("assetScriptsHint")
|
|
781
|
+
})] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
782
|
+
className: styles.actions,
|
|
783
|
+
children: [
|
|
784
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
785
|
+
type: "button",
|
|
786
|
+
className: styles.secondary,
|
|
787
|
+
onClick: () => newAsset("recipe"),
|
|
788
|
+
children: t("assetNewRecipe")
|
|
789
|
+
}),
|
|
790
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
791
|
+
type: "button",
|
|
792
|
+
className: styles.secondary,
|
|
793
|
+
onClick: () => newAsset("userscript"),
|
|
794
|
+
children: t("assetNewScript")
|
|
795
|
+
}),
|
|
796
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
797
|
+
type: "button",
|
|
798
|
+
className: styles.secondary,
|
|
799
|
+
onClick: props.refreshAutomationAssets,
|
|
800
|
+
children: t("assetRefresh")
|
|
801
|
+
})
|
|
802
|
+
]
|
|
803
|
+
})]
|
|
804
|
+
}),
|
|
805
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
806
|
+
className: styles.assetLayout,
|
|
807
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
808
|
+
className: styles.assetList,
|
|
809
|
+
children: assets.length ? assets.map((asset) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
810
|
+
type: "button",
|
|
811
|
+
className: `${styles.assetItem} ${selected?.id === asset.id ? styles.assetSelected : ""}`,
|
|
812
|
+
onClick: () => props.selectAutomationAsset(asset.id),
|
|
813
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: asset.name }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("small", { children: [
|
|
814
|
+
asset.kind,
|
|
815
|
+
" · ",
|
|
816
|
+
asset.status,
|
|
817
|
+
" · r",
|
|
818
|
+
asset.revision
|
|
819
|
+
] })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
820
|
+
className: styles.assetTest,
|
|
821
|
+
children: asset.testStatus
|
|
822
|
+
})]
|
|
823
|
+
}, asset.id)) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
824
|
+
className: styles.hint,
|
|
825
|
+
children: t("assetEmpty")
|
|
826
|
+
})
|
|
827
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
828
|
+
className: styles.assetEditor,
|
|
829
|
+
children: [
|
|
830
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
831
|
+
className: styles.label,
|
|
832
|
+
htmlFor: "dsh-browser-asset-editor",
|
|
833
|
+
children: t("assetEditor")
|
|
834
|
+
}),
|
|
835
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
836
|
+
id: "dsh-browser-asset-editor",
|
|
837
|
+
className: `${styles.input} ${styles.textarea} ${styles.code} ${draftError ? styles.invalidInput : ""}`,
|
|
838
|
+
rows: 18,
|
|
839
|
+
value: draft,
|
|
840
|
+
spellCheck: false,
|
|
841
|
+
placeholder: t("assetEditorHint"),
|
|
842
|
+
onChange: (event) => {
|
|
843
|
+
setDraft(event.currentTarget.value);
|
|
844
|
+
setDraftError(false);
|
|
845
|
+
}
|
|
846
|
+
}),
|
|
847
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
848
|
+
className: styles.hint,
|
|
849
|
+
children: draftError ? t("assetInvalid") : t("assetSourceBoundary")
|
|
850
|
+
}),
|
|
851
|
+
selected?.status === "draft" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
852
|
+
className: styles.assetTestForm,
|
|
853
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
854
|
+
className: styles.input,
|
|
855
|
+
value: testUrl,
|
|
856
|
+
placeholder: t("assetTestUrl"),
|
|
857
|
+
onChange: (event) => setTestUrl(event.currentTarget.value)
|
|
858
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
859
|
+
className: `${styles.input} ${styles.textarea} ${styles.code}`,
|
|
860
|
+
rows: 3,
|
|
861
|
+
value: testInputs,
|
|
862
|
+
spellCheck: false,
|
|
863
|
+
"aria-label": t("assetTestInputs"),
|
|
864
|
+
onChange: (event) => setTestInputs(event.currentTarget.value)
|
|
865
|
+
})]
|
|
866
|
+
}) : null,
|
|
867
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
868
|
+
className: styles.actions,
|
|
869
|
+
children: [
|
|
870
|
+
selected ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
871
|
+
type: "button",
|
|
872
|
+
className: styles.secondary,
|
|
873
|
+
disabled: state.busy,
|
|
874
|
+
onClick: () => props.validateAutomationAsset(selected.id),
|
|
875
|
+
children: t("assetValidate")
|
|
876
|
+
}) : null,
|
|
877
|
+
selected?.status === "draft" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
878
|
+
type: "button",
|
|
879
|
+
className: styles.secondary,
|
|
880
|
+
disabled: state.busy || !testUrl.trim(),
|
|
881
|
+
onClick: () => void runTest(),
|
|
882
|
+
children: t("assetTest")
|
|
883
|
+
}) : null,
|
|
884
|
+
selected?.status === "draft" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
885
|
+
type: "button",
|
|
886
|
+
className: styles.secondary,
|
|
887
|
+
disabled: state.busy || selected.testStatus !== "passed",
|
|
888
|
+
onClick: () => props.setAutomationAssetStatus(selected.id, "active"),
|
|
889
|
+
children: t("assetActivate")
|
|
890
|
+
}) : null,
|
|
891
|
+
selected && selected.status !== "archived" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
892
|
+
type: "button",
|
|
893
|
+
className: styles.secondary,
|
|
894
|
+
disabled: state.busy,
|
|
895
|
+
onClick: () => props.setAutomationAssetStatus(selected.id, "archived"),
|
|
896
|
+
children: t("assetArchive")
|
|
897
|
+
}) : null,
|
|
898
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
899
|
+
type: "button",
|
|
900
|
+
className: styles.primary,
|
|
901
|
+
disabled: state.busy || !draft || selected?.status === "active",
|
|
902
|
+
onClick: () => void save(),
|
|
903
|
+
children: t("assetSaveDraft")
|
|
904
|
+
})
|
|
905
|
+
]
|
|
906
|
+
})
|
|
907
|
+
]
|
|
908
|
+
})]
|
|
909
|
+
})
|
|
910
|
+
]
|
|
911
|
+
});
|
|
912
|
+
}
|
|
598
913
|
//#endregion
|
|
599
914
|
//#region src/client/locales.ts
|
|
600
915
|
const zh = {
|
|
@@ -626,6 +941,34 @@ window.__ModuleLoader__.load({
|
|
|
626
941
|
opencliEnabledHint: "站点 adapter 和 Chrome Browser Bridge 总开关。",
|
|
627
942
|
usagePolicy: "调用缓冲 JSON",
|
|
628
943
|
usagePolicyHint: "minDelayMs、maxConcurrency、burst、maxPagesPerRun、maxDepth、retryLimit、backoffBaseMs、cooldownMs。",
|
|
944
|
+
automationAssets: "自动化资产策略 JSON",
|
|
945
|
+
automationAssetsHint: "候选阈值、持久化模式、激活模式、数量与上下文预算。",
|
|
946
|
+
assetLibrary: "可复用自动化资产(实验性)",
|
|
947
|
+
assetLibraryHint: "实验功能:候选先提示是否总结;草稿真实回放后再手动激活。源码仅在点选编辑时读取。",
|
|
948
|
+
assetLoading: "正在读取本地自动化资产…",
|
|
949
|
+
assetFailed: "自动化资产读取失败。",
|
|
950
|
+
assetSuggestions: "建议总结",
|
|
951
|
+
assetRuns: "次成功",
|
|
952
|
+
assetSessions: "个会话",
|
|
953
|
+
assetDismiss: "暂不总结",
|
|
954
|
+
assetSummarize: "总结为草稿",
|
|
955
|
+
assetScripts: "脚本与 recipe",
|
|
956
|
+
assetScriptsHint: "模型只能检索已激活资产的有界摘要。",
|
|
957
|
+
assetNewRecipe: "新建 recipe",
|
|
958
|
+
assetNewScript: "新建油猴脚本",
|
|
959
|
+
assetRefresh: "刷新",
|
|
960
|
+
assetEmpty: "还没有资产。",
|
|
961
|
+
assetEditor: "资产编辑器(JSON)",
|
|
962
|
+
assetEditorHint: "选择资产或新建草稿。",
|
|
963
|
+
assetInvalid: "JSON、测试 URL 或输入无效。",
|
|
964
|
+
assetSourceBoundary: "不要保存 cookie、token、密码、完整页面内容或聊天记录。",
|
|
965
|
+
assetValidate: "静态校验",
|
|
966
|
+
assetTest: "真实回放",
|
|
967
|
+
assetTestUrl: "测试 URL(必须命中允许域名)",
|
|
968
|
+
assetTestInputs: "测试输入 JSON",
|
|
969
|
+
assetActivate: "激活",
|
|
970
|
+
assetArchive: "归档",
|
|
971
|
+
assetSaveDraft: "保存草稿",
|
|
629
972
|
autoInstall: "缺失时自动安装 Chromium",
|
|
630
973
|
autoInstallHint: "可能触发较大下载,日常建议关闭并显式调用 browser_install。",
|
|
631
974
|
storageStatePath: "全局 storageState 路径",
|
|
@@ -683,6 +1026,34 @@ window.__ModuleLoader__.load({
|
|
|
683
1026
|
opencliEnabledHint: "Master switch for site adapters and the Chrome Browser Bridge.",
|
|
684
1027
|
usagePolicy: "Usage buffer JSON",
|
|
685
1028
|
usagePolicyHint: "minDelayMs, maxConcurrency, burst, maxPagesPerRun, maxDepth, retryLimit, backoffBaseMs, cooldownMs.",
|
|
1029
|
+
automationAssets: "Automation asset policy JSON",
|
|
1030
|
+
automationAssetsHint: "Candidate thresholds, persistence, activation, count, and context budgets.",
|
|
1031
|
+
assetLibrary: "Reusable automation assets (Experimental)",
|
|
1032
|
+
assetLibraryHint: "Experimental: candidates ask before summarization; drafts require runtime replay before manual activation. Source loads only when selected.",
|
|
1033
|
+
assetLoading: "Loading local automation assets…",
|
|
1034
|
+
assetFailed: "Failed to load automation assets.",
|
|
1035
|
+
assetSuggestions: "Summarization suggestions",
|
|
1036
|
+
assetRuns: "successful runs",
|
|
1037
|
+
assetSessions: "sessions",
|
|
1038
|
+
assetDismiss: "Not now",
|
|
1039
|
+
assetSummarize: "Summarize to draft",
|
|
1040
|
+
assetScripts: "Scripts and recipes",
|
|
1041
|
+
assetScriptsHint: "The model can retrieve only bounded summaries of active assets.",
|
|
1042
|
+
assetNewRecipe: "New recipe",
|
|
1043
|
+
assetNewScript: "New userscript",
|
|
1044
|
+
assetRefresh: "Refresh",
|
|
1045
|
+
assetEmpty: "No assets yet.",
|
|
1046
|
+
assetEditor: "Asset editor (JSON)",
|
|
1047
|
+
assetEditorHint: "Select an asset or create a draft.",
|
|
1048
|
+
assetInvalid: "Invalid JSON, test URL, or inputs.",
|
|
1049
|
+
assetSourceBoundary: "Do not store cookies, tokens, passwords, full page content, or conversation transcripts.",
|
|
1050
|
+
assetValidate: "Static validate",
|
|
1051
|
+
assetTest: "Runtime replay",
|
|
1052
|
+
assetTestUrl: "Test URL (must match an allowed domain)",
|
|
1053
|
+
assetTestInputs: "Test inputs JSON",
|
|
1054
|
+
assetActivate: "Activate",
|
|
1055
|
+
assetArchive: "Archive",
|
|
1056
|
+
assetSaveDraft: "Save draft",
|
|
686
1057
|
autoInstall: "Auto-install Chromium when missing",
|
|
687
1058
|
autoInstallHint: "May download a large binary; explicit browser_install is safer for daily use.",
|
|
688
1059
|
storageStatePath: "Global storageState path",
|
|
@@ -716,6 +1087,151 @@ window.__ModuleLoader__.load({
|
|
|
716
1087
|
/** Settings namespace used by both the Host registry and the card slot key. */
|
|
717
1088
|
const SETTINGS_NAMESPACE = "browser";
|
|
718
1089
|
//#endregion
|
|
1090
|
+
//#region src/client/automation-assets-client.ts
|
|
1091
|
+
function localStore(initial) {
|
|
1092
|
+
let snapshot = initial;
|
|
1093
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
1094
|
+
return {
|
|
1095
|
+
getSnapshot: () => snapshot,
|
|
1096
|
+
subscribe(listener) {
|
|
1097
|
+
listeners.add(listener);
|
|
1098
|
+
return () => {
|
|
1099
|
+
listeners.delete(listener);
|
|
1100
|
+
};
|
|
1101
|
+
},
|
|
1102
|
+
set(next) {
|
|
1103
|
+
snapshot = next;
|
|
1104
|
+
for (const listener of listeners) listener();
|
|
1105
|
+
},
|
|
1106
|
+
update(updater) {
|
|
1107
|
+
const draft = structuredClone(snapshot);
|
|
1108
|
+
updater(draft);
|
|
1109
|
+
snapshot = draft;
|
|
1110
|
+
for (const listener of listeners) listener();
|
|
1111
|
+
}
|
|
1112
|
+
};
|
|
1113
|
+
}
|
|
1114
|
+
var AutomationAssetsController = class {
|
|
1115
|
+
rpc;
|
|
1116
|
+
store = localStore({
|
|
1117
|
+
loading: true,
|
|
1118
|
+
busy: false,
|
|
1119
|
+
failed: false
|
|
1120
|
+
});
|
|
1121
|
+
disposed = false;
|
|
1122
|
+
constructor(rpc) {
|
|
1123
|
+
this.rpc = rpc;
|
|
1124
|
+
this.refresh();
|
|
1125
|
+
}
|
|
1126
|
+
inject() {
|
|
1127
|
+
return {
|
|
1128
|
+
hooks: { automationAssets: this.store },
|
|
1129
|
+
refreshAutomationAssets: () => {
|
|
1130
|
+
this.refresh();
|
|
1131
|
+
},
|
|
1132
|
+
selectAutomationAsset: (id) => {
|
|
1133
|
+
this.select(id);
|
|
1134
|
+
},
|
|
1135
|
+
saveAutomationAsset: (asset) => this.mutate("save", { asset }),
|
|
1136
|
+
summarizeAutomationCandidate: (id) => this.mutate("summarize", { id }),
|
|
1137
|
+
dismissAutomationCandidate: (id) => this.mutate("dismiss", { id }),
|
|
1138
|
+
validateAutomationAsset: (id) => this.mutate("validate", { id }),
|
|
1139
|
+
testAutomationAsset: (id, url, inputs) => this.mutate("test", {
|
|
1140
|
+
id,
|
|
1141
|
+
url,
|
|
1142
|
+
inputs
|
|
1143
|
+
}),
|
|
1144
|
+
setAutomationAssetStatus: (id, status) => this.mutate("status", {
|
|
1145
|
+
id,
|
|
1146
|
+
status
|
|
1147
|
+
})
|
|
1148
|
+
};
|
|
1149
|
+
}
|
|
1150
|
+
snapshot() {
|
|
1151
|
+
return this.store.getSnapshot();
|
|
1152
|
+
}
|
|
1153
|
+
dispose() {
|
|
1154
|
+
this.disposed = true;
|
|
1155
|
+
}
|
|
1156
|
+
async refresh() {
|
|
1157
|
+
this.publish({
|
|
1158
|
+
...this.store.getSnapshot(),
|
|
1159
|
+
loading: true,
|
|
1160
|
+
failed: false,
|
|
1161
|
+
error: void 0
|
|
1162
|
+
});
|
|
1163
|
+
try {
|
|
1164
|
+
const snapshot = await this.call("snapshot", {});
|
|
1165
|
+
this.publish({
|
|
1166
|
+
...this.store.getSnapshot(),
|
|
1167
|
+
loading: false,
|
|
1168
|
+
snapshot
|
|
1169
|
+
});
|
|
1170
|
+
} catch (error) {
|
|
1171
|
+
this.fail(error);
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
async select(id) {
|
|
1175
|
+
if (!id) {
|
|
1176
|
+
this.publish({
|
|
1177
|
+
...this.store.getSnapshot(),
|
|
1178
|
+
selected: void 0
|
|
1179
|
+
});
|
|
1180
|
+
return;
|
|
1181
|
+
}
|
|
1182
|
+
try {
|
|
1183
|
+
const selected = await this.call("get", { id });
|
|
1184
|
+
this.publish({
|
|
1185
|
+
...this.store.getSnapshot(),
|
|
1186
|
+
selected: selected ?? void 0,
|
|
1187
|
+
failed: false,
|
|
1188
|
+
error: void 0
|
|
1189
|
+
});
|
|
1190
|
+
} catch (error) {
|
|
1191
|
+
this.fail(error);
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
async mutate(endpoint, payload) {
|
|
1195
|
+
this.publish({
|
|
1196
|
+
...this.store.getSnapshot(),
|
|
1197
|
+
busy: true,
|
|
1198
|
+
failed: false,
|
|
1199
|
+
error: void 0
|
|
1200
|
+
});
|
|
1201
|
+
try {
|
|
1202
|
+
const value = await this.call(endpoint, payload);
|
|
1203
|
+
const selected = value && typeof value === "object" && "id" in value ? value : this.store.getSnapshot().selected;
|
|
1204
|
+
const snapshot = await this.call("snapshot", {});
|
|
1205
|
+
this.publish({
|
|
1206
|
+
loading: false,
|
|
1207
|
+
busy: false,
|
|
1208
|
+
failed: false,
|
|
1209
|
+
snapshot,
|
|
1210
|
+
...selected ? { selected } : {}
|
|
1211
|
+
});
|
|
1212
|
+
} catch (error) {
|
|
1213
|
+
this.fail(error);
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
async call(endpoint, payload) {
|
|
1217
|
+
const result = await this.rpc.call("/dsh-browser-assets", endpoint, payload);
|
|
1218
|
+
if (!result.ok) throw new Error(result.error.message);
|
|
1219
|
+
return result.value;
|
|
1220
|
+
}
|
|
1221
|
+
fail(error) {
|
|
1222
|
+
this.publish({
|
|
1223
|
+
...this.store.getSnapshot(),
|
|
1224
|
+
loading: false,
|
|
1225
|
+
busy: false,
|
|
1226
|
+
failed: true,
|
|
1227
|
+
error: String(error instanceof Error ? error.message : error).slice(0, 300)
|
|
1228
|
+
});
|
|
1229
|
+
}
|
|
1230
|
+
publish(state) {
|
|
1231
|
+
if (!this.disposed) this.store.set(state);
|
|
1232
|
+
}
|
|
1233
|
+
};
|
|
1234
|
+
//#endregion
|
|
719
1235
|
//#region src/client/index.ts
|
|
720
1236
|
const name = "dsh-browser-client";
|
|
721
1237
|
const inject = [
|
|
@@ -732,12 +1248,25 @@ window.__ModuleLoader__.load({
|
|
|
732
1248
|
en
|
|
733
1249
|
}), "dsh-browser: settings dictionaries");
|
|
734
1250
|
const controller = new BrowserSettingsController(ctx.settingsScope.bind({ namespace: SETTINGS_NAMESPACE }));
|
|
1251
|
+
const assets = new AutomationAssetsController(ctx.connection.rpc);
|
|
735
1252
|
ctx.effect(() => () => controller.dispose(), "dsh-browser: settings controller");
|
|
1253
|
+
ctx.effect(() => () => assets.dispose(), "dsh-browser: automation assets controller");
|
|
736
1254
|
ctx.slots.inject("settings.plugin.item", () => ctx.slots.register({
|
|
737
1255
|
name: "settings.plugin.item",
|
|
738
1256
|
key: SETTINGS_NAMESPACE,
|
|
739
1257
|
locale: NS,
|
|
740
|
-
inject: () =>
|
|
1258
|
+
inject: () => {
|
|
1259
|
+
const settingsProps = controller.inject();
|
|
1260
|
+
const assetProps = assets.inject();
|
|
1261
|
+
return {
|
|
1262
|
+
...settingsProps,
|
|
1263
|
+
...assetProps,
|
|
1264
|
+
hooks: {
|
|
1265
|
+
...settingsProps.hooks,
|
|
1266
|
+
...assetProps.hooks
|
|
1267
|
+
}
|
|
1268
|
+
};
|
|
1269
|
+
}
|
|
741
1270
|
}, SettingsCard));
|
|
742
1271
|
}
|
|
743
1272
|
//#endregion
|