dsh-edge 0.2.0 → 0.3.0-alpha.1
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.i18n.yaml +2 -2
- package/README.md +11 -8
- package/README.zh.md +11 -8
- package/THIRD_PARTY_NOTICES.md +447 -348
- package/dist/assets/{index-BNMwCG9c.css → index-C6eRlFa6.css} +1 -1
- package/dist/assets/{index-CA9Bpko5.js → index-ClqxG24t.js} +35 -35
- package/dist/index.html +3 -3
- package/dist/plugins/@deepseek-ai/dsh-api-gateway/client.js +84 -55
- package/dist/plugins/@deepseek-ai/dsh-client-connection/client.js +10 -6
- package/dist/plugins/@deepseek-ai/dsh-client-runtime/client.js +15 -6
- package/dist/plugins/@deepseek-ai/dsh-client-ui-attachment/client.js +782 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-conversation/client.js +188 -72
- package/dist/plugins/@deepseek-ai/dsh-client-ui-permission-presets/client.js +46 -13
- package/dist/plugins/@deepseek-ai/dsh-client-ui-sidebar/client.js +1 -1
- package/dist/plugins/@deepseek-ai/dsh-client-ui-subagent/client.js +287 -128
- package/dist/plugins/@deepseek-ai/dsh-client-ui-user-questions/client.js +46 -9
- package/dist/plugins/@deepseek-ai/dsh-client-ui-workspace/client.js +26 -0
- package/package.json +25 -22
- package/scripts/cli.mjs +3 -0
- package/scripts/install.d.mts +25 -0
- package/scripts/install.mjs +185 -6
- package/scripts/legal-files.mjs +5 -1
- package/scripts/wrangler-config-core.mjs +28 -0
- package/scripts/wrangler-config.d.mts +1 -0
- package/worker/direct/index.js +1021 -991
- package/worker/isolated/index.js +677 -647
|
@@ -230,7 +230,7 @@ window.__ModuleLoader__.load({
|
|
|
230
230
|
}
|
|
231
231
|
//#endregion
|
|
232
232
|
//#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-user-questions/src/client/QuestionComposer.module.css.mjs
|
|
233
|
-
const css = ".Mbwy4a_frame{padding:6px calc(var(--dsh-composer-side-clearance) + 16px) 10px;justify-content:center;display:flex}.Mbwy4a_card{width:100%;max-width:var(--dsh-chat-content-width);border:1px solid var(--dsw-alias-border-l2-darkmode-thin);background:var(--dsw-specific-input-major);max-height:min(60vh,520px);box-shadow:var(--dsw-shadow-lv2);color:var(--dsw-alias-label-primary);--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);border-radius:20px;flex-direction:column;padding:0 0 10px;display:flex;overflow:hidden}.Mbwy4a_card,.Mbwy4a_card *{box-sizing:border-box}.Mbwy4a_cardMinimized{max-height:none}.Mbwy4a_cardMinimized .Mbwy4a_header{padding-bottom:14px}.Mbwy4a_headerActions{flex-shrink:0;align-items:center;gap:4px;display:flex}.Mbwy4a_header{flex-shrink:0;justify-content:space-between;align-items:flex-start;gap:16px;padding:20px 16px 0 24px;display:flex}.Mbwy4a_headingBlock{min-width:0}.Mbwy4a_eyebrow{color:var(--dsw-alias-label-tertiary);margin-bottom:5px;font-size:11px;line-height:16px}.Mbwy4a_title{margin:0;font-size:16px;font-weight:500;line-height:22px}.Mbwy4a_detail{margin:0 2px 8px}.Mbwy4a_footerActions{flex-shrink:0;align-items:center;gap:12px;display:flex}.Mbwy4a_pager{flex-shrink:0;align-items:center;gap:6px;display:flex}.Mbwy4a_progress{color:var(--dsw-alias-label-secondary);white-space:nowrap;word-spacing:-2px;padding:0 4px;font-size:14px;font-weight:500;line-height:24px}.Mbwy4a_iconButton{width:24px;height:24px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:none;border-radius:999px;place-items:center;padding:0;display:grid}.Mbwy4a_iconButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.Mbwy4a_iconButton:disabled{color:var(--dsw-alias-label-dimmed);cursor:default}.Mbwy4a_body{overscroll-behavior:contain;flex-direction:column;flex:auto;min-height:0;display:flex;overflow-y:auto}.Mbwy4a_options{flex-direction:column;gap:1px;margin:8px 0 0;padding:4px 12px;display:flex}.Mbwy4a_option{width:100%;min-height:40px;color:inherit;text-align:left;cursor:pointer;background:0 0;border:1px solid #0000;border-radius:12px;flex-shrink:0;align-items:flex-start;gap:8px;padding:8px 12px 8px 8px;transition:background-color .12s,border-color .12s;display:flex}.Mbwy4a_option:hover:not(:disabled),.Mbwy4a_optionSelected{background:var(--dsw-alias-interactive-bg-hover)}.Mbwy4a_optionSelected{border-color:var(--dsw-alias-border-l2)}.Mbwy4a_option:disabled{cursor:default}.Mbwy4a_number{background:var(--dsw-alias-bg-overlay);width:20px;height:20px;color:var(--dsw-alias-label-secondary);border-radius:6px;flex:0 0 20px;place-items:center;margin-top:2px;font-size:12px;font-weight:500;line-height:18px;display:grid}.Mbwy4a_checkbox{flex:0 0 20px;place-items:center;width:20px;height:20px;margin-top:2px;display:grid}.Mbwy4a_checkbox:before{content:\"\";border:1px solid var(--dsw-alias-border-l4);border-radius:4px;grid-area:1/1;width:14px;height:14px;transition:background-color .12s,border-color .12s}.Mbwy4a_checkbox>svg{grid-area:1/1}.Mbwy4a_checkboxChecked{color:var(--dsw-alias-label-primary-foreground)}.Mbwy4a_checkboxChecked:before{border-color:var(--dsw-alias-label-primary);background:var(--dsw-alias-label-primary)}.Mbwy4a_optionCopy{flex:1;min-width:0}.Mbwy4a_optionLine{flex-wrap:wrap;align-items:baseline;gap:2px 6px;display:flex}.Mbwy4a_optionLabel{font-size:14px;font-weight:500;line-height:24px}.Mbwy4a_badge{background:var(--dsw-specific-sidebar-nav-item-active-accent);color:var(--dsw-alias-button-info-fill);border-radius:6px;padding:0 4px;font-size:11px;font-weight:600;line-height:18px}.Mbwy4a_description{color:var(--dsw-alias-label-tertiary);font-size:14px;font-weight:400;line-height:24px}.Mbwy4a_customRow{border:1px solid #0000;border-radius:12px;flex-shrink:0;align-items:flex-start;gap:8px;width:100%;min-height:40px;padding:8px 12px 8px 8px;transition:background-color .12s,border-color .12s;display:flex}.Mbwy4a_customRow:hover,.Mbwy4a_customRow:focus-within,.Mbwy4a_customRowActive{background:var(--dsw-alias-interactive-bg-hover)}.Mbwy4a_customRow:focus-within,.Mbwy4a_customRowActive{border-color:var(--dsw-alias-border-l2)}.
|
|
233
|
+
const css = ".Mbwy4a_frame{padding:6px calc(var(--dsh-composer-side-clearance) + 16px) 10px;justify-content:center;display:flex}.Mbwy4a_card{width:100%;max-width:var(--dsh-chat-content-width);border:1px solid var(--dsw-alias-border-l2-darkmode-thin);background:var(--dsw-specific-input-major);max-height:min(60vh,520px);box-shadow:var(--dsw-shadow-lv2);color:var(--dsw-alias-label-primary);--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);border-radius:20px;flex-direction:column;padding:0 0 10px;display:flex;overflow:hidden}.Mbwy4a_card,.Mbwy4a_card *{box-sizing:border-box}.Mbwy4a_cardMinimized{max-height:none}.Mbwy4a_cardMinimized .Mbwy4a_header{padding-bottom:14px}.Mbwy4a_headerActions{flex-shrink:0;align-items:center;gap:4px;display:flex}.Mbwy4a_header{flex-shrink:0;justify-content:space-between;align-items:flex-start;gap:16px;padding:20px 16px 0 24px;display:flex}.Mbwy4a_headingBlock{min-width:0}.Mbwy4a_eyebrow{color:var(--dsw-alias-label-tertiary);margin-bottom:5px;font-size:11px;line-height:16px}.Mbwy4a_title{margin:0;font-size:16px;font-weight:500;line-height:22px}.Mbwy4a_detail{margin:0 2px 8px}.Mbwy4a_footerActions{flex-shrink:0;align-items:center;gap:12px;display:flex}.Mbwy4a_pager{flex-shrink:0;align-items:center;gap:6px;display:flex}.Mbwy4a_progress{color:var(--dsw-alias-label-secondary);white-space:nowrap;word-spacing:-2px;padding:0 4px;font-size:14px;font-weight:500;line-height:24px}.Mbwy4a_iconButton{width:24px;height:24px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:none;border-radius:999px;place-items:center;padding:0;display:grid}.Mbwy4a_iconButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.Mbwy4a_iconButton:disabled{color:var(--dsw-alias-label-dimmed);cursor:default}.Mbwy4a_body{overscroll-behavior:contain;flex-direction:column;flex:auto;min-height:0;display:flex;overflow-y:auto}.Mbwy4a_options{flex-direction:column;gap:1px;margin:8px 0 0;padding:4px 12px;display:flex}.Mbwy4a_option{width:100%;min-height:40px;color:inherit;text-align:left;cursor:pointer;background:0 0;border:1px solid #0000;border-radius:12px;flex-shrink:0;align-items:flex-start;gap:8px;padding:8px 12px 8px 8px;transition:background-color .12s,border-color .12s;display:flex}.Mbwy4a_option:hover:not(:disabled),.Mbwy4a_optionSelected{background:var(--dsw-alias-interactive-bg-hover)}.Mbwy4a_optionSelected{border-color:var(--dsw-alias-border-l2)}.Mbwy4a_option:disabled{cursor:default}.Mbwy4a_number{background:var(--dsw-alias-bg-overlay);width:20px;height:20px;color:var(--dsw-alias-label-secondary);border-radius:6px;flex:0 0 20px;place-items:center;margin-top:2px;font-size:12px;font-weight:500;line-height:18px;display:grid}.Mbwy4a_checkbox{flex:0 0 20px;place-items:center;width:20px;height:20px;margin-top:2px;display:grid}.Mbwy4a_checkbox:before{content:\"\";border:1px solid var(--dsw-alias-border-l4);border-radius:4px;grid-area:1/1;width:14px;height:14px;transition:background-color .12s,border-color .12s}.Mbwy4a_checkbox>svg{grid-area:1/1}.Mbwy4a_checkboxChecked{color:var(--dsw-alias-label-primary-foreground)}.Mbwy4a_checkboxChecked:before{border-color:var(--dsw-alias-label-primary);background:var(--dsw-alias-label-primary)}.Mbwy4a_optionCopy{flex:1;min-width:0}.Mbwy4a_optionLine{flex-wrap:wrap;align-items:baseline;gap:2px 6px;display:flex}.Mbwy4a_optionLabel{font-size:14px;font-weight:500;line-height:24px}.Mbwy4a_badge{background:var(--dsw-specific-sidebar-nav-item-active-accent);color:var(--dsw-alias-button-info-fill);border-radius:6px;padding:0 4px;font-size:11px;font-weight:600;line-height:18px}.Mbwy4a_description{color:var(--dsw-alias-label-tertiary);font-size:14px;font-weight:400;line-height:24px}.Mbwy4a_customRow{border:1px solid #0000;border-radius:12px;flex-shrink:0;align-items:flex-start;gap:8px;width:100%;min-height:40px;padding:8px 12px 8px 8px;transition:background-color .12s,border-color .12s;display:flex}.Mbwy4a_customRow:hover,.Mbwy4a_customRow:focus-within,.Mbwy4a_customRowActive{background:var(--dsw-alias-interactive-bg-hover)}.Mbwy4a_customRow:focus-within,.Mbwy4a_customRowActive{border-color:var(--dsw-alias-border-l2)}.Mbwy4a_field{--dsh-answer-field-padding:0;min-width:0;display:grid}.Mbwy4a_field>*{min-width:0;padding:var(--dsh-answer-field-padding);font:inherit;white-space:pre-wrap;word-break:break-word;overflow-wrap:anywhere;grid-area:1/1;font-size:14px;line-height:24px}.Mbwy4a_fieldMirror{box-sizing:content-box;visibility:hidden;max-height:144px;overflow:hidden}.Mbwy4a_fieldInput{resize:none;color:var(--dsw-alias-label-primary);caret-color:var(--dsw-alias-state-business-primary);background:0 0;border:none;outline:none;overflow-y:auto}.Mbwy4a_fieldInput::placeholder{color:var(--dsw-alias-label-caption)}.Mbwy4a_customInline{flex:1}.Mbwy4a_customBlock{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-module-platform);--dsh-answer-field-padding:8px 12px;border-radius:10px;flex-shrink:0;min-height:64px;margin:0 12px}.Mbwy4a_customBlock:focus-within{border-color:var(--dsw-alias-state-business-primary)}.Mbwy4a_footer{flex-shrink:0;justify-content:space-between;align-items:center;gap:12px;margin-top:12px;padding:0 10px 0 18px;display:flex}.Mbwy4a_feedback{min-height:16px;color:var(--dsw-alias-state-error-primary);text-align:right;flex:1;font-size:11px;line-height:16px}@media (width<=720px){.Mbwy4a_card{border-radius:16px}.Mbwy4a_header{padding:10px 12px 0 18px}.Mbwy4a_options{padding:4px 8px}.Mbwy4a_title{font-size:15px;line-height:21px}.Mbwy4a_option,.Mbwy4a_customRow{padding:8px 6px}.Mbwy4a_footer{align-items:flex-end;padding:0 10px}.Mbwy4a_footerActions{flex-shrink:0}}@media (prefers-reduced-motion:reduce){.Mbwy4a_option,.Mbwy4a_customRow{transition:none}}";
|
|
234
234
|
const tagId = "@deepseek-ai/dsh-client-ui-user-questions/QuestionComposer.module.css";
|
|
235
235
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
236
236
|
const tag = document.createElement("style");
|
|
@@ -246,14 +246,17 @@ window.__ModuleLoader__.load({
|
|
|
246
246
|
"cardMinimized": "Mbwy4a_cardMinimized",
|
|
247
247
|
"checkbox": "Mbwy4a_checkbox",
|
|
248
248
|
"checkboxChecked": "Mbwy4a_checkboxChecked",
|
|
249
|
-
"
|
|
249
|
+
"customBlock": "Mbwy4a_customBlock",
|
|
250
|
+
"customInline": "Mbwy4a_customInline",
|
|
250
251
|
"customRow": "Mbwy4a_customRow",
|
|
251
252
|
"customRowActive": "Mbwy4a_customRowActive",
|
|
252
|
-
"customTextarea": "Mbwy4a_customTextarea",
|
|
253
253
|
"description": "Mbwy4a_description",
|
|
254
254
|
"detail": "Mbwy4a_detail",
|
|
255
255
|
"eyebrow": "Mbwy4a_eyebrow",
|
|
256
256
|
"feedback": "Mbwy4a_feedback",
|
|
257
|
+
"field": "Mbwy4a_field",
|
|
258
|
+
"fieldInput": "Mbwy4a_fieldInput",
|
|
259
|
+
"fieldMirror": "Mbwy4a_fieldMirror",
|
|
257
260
|
"footer": "Mbwy4a_footer",
|
|
258
261
|
"footerActions": "Mbwy4a_footerActions",
|
|
259
262
|
"frame": "Mbwy4a_frame",
|
|
@@ -294,6 +297,42 @@ window.__ModuleLoader__.load({
|
|
|
294
297
|
return event.nativeEvent.isComposing || event.nativeEvent.keyCode === 229;
|
|
295
298
|
}
|
|
296
299
|
/**
|
|
300
|
+
* Auto-growing free-text answer: a textarea, so a long answer soft-wraps and
|
|
301
|
+
* Shift+Enter breaks a line, over a hidden mirror that owns the height.
|
|
302
|
+
*
|
|
303
|
+
* The mirror renders the draft plus a trailing newline in normal flow and so
|
|
304
|
+
* sizes the grid row (counting rows by '\n' cannot see soft wraps); the
|
|
305
|
+
* textarea shares that one cell and stretches to it, and `rows={1}` keeps the
|
|
306
|
+
* control's own intrinsic height out of the row sizing so the mirror alone
|
|
307
|
+
* decides. Past the mirror's cap the textarea scrolls itself — it is the only
|
|
308
|
+
* scrollport in the stack, there being no second glyph layer to keep aligned.
|
|
309
|
+
* Mirror and textarea MUST share font, line-height, padding and wrapping rules
|
|
310
|
+
* or the two heights diverge.
|
|
311
|
+
*
|
|
312
|
+
* @param props - field shape, draft text, and the field's event handlers.
|
|
313
|
+
* @returns The mirrored auto-growing field.
|
|
314
|
+
*/
|
|
315
|
+
function AnswerField(props) {
|
|
316
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
317
|
+
className: clsx(QuestionComposer_module_css_default.field, props.variant === "inline" ? QuestionComposer_module_css_default.customInline : QuestionComposer_module_css_default.customBlock),
|
|
318
|
+
children: [(0, react_jsx_runtime.jsx)("div", {
|
|
319
|
+
"aria-hidden": true,
|
|
320
|
+
className: QuestionComposer_module_css_default.fieldMirror,
|
|
321
|
+
children: `${props.value}\n`
|
|
322
|
+
}), (0, react_jsx_runtime.jsx)("textarea", {
|
|
323
|
+
autoFocus: props.autoFocus,
|
|
324
|
+
className: QuestionComposer_module_css_default.fieldInput,
|
|
325
|
+
value: props.value,
|
|
326
|
+
disabled: props.disabled,
|
|
327
|
+
rows: 1,
|
|
328
|
+
placeholder: props.placeholder,
|
|
329
|
+
onFocus: props.onFocus,
|
|
330
|
+
onChange: props.onChange,
|
|
331
|
+
onKeyDown: props.onKeyDown
|
|
332
|
+
})]
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
297
336
|
* Composer takeover boundary; the carrier key keys local drafts, so a
|
|
298
337
|
* same-request replay (same key, new carrier object) preserves them.
|
|
299
338
|
*
|
|
@@ -538,21 +577,19 @@ window.__ModuleLoader__.load({
|
|
|
538
577
|
className: QuestionComposer_module_css_default.number,
|
|
539
578
|
"aria-hidden": "true",
|
|
540
579
|
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEditOutline16, { size: 12 })
|
|
541
|
-
}), (0, react_jsx_runtime.jsx)(
|
|
542
|
-
|
|
543
|
-
className: QuestionComposer_module_css_default.customInput,
|
|
580
|
+
}), (0, react_jsx_runtime.jsx)(AnswerField, {
|
|
581
|
+
variant: "inline",
|
|
544
582
|
value: draft.custom,
|
|
545
583
|
disabled: busy !== null,
|
|
546
584
|
placeholder: t("custom.placeholder"),
|
|
547
585
|
onChange: draftCustom,
|
|
548
586
|
onKeyDown: continueFromCustom
|
|
549
587
|
})]
|
|
550
|
-
}) : (0, react_jsx_runtime.jsx)(
|
|
588
|
+
}) : (0, react_jsx_runtime.jsx)(AnswerField, {
|
|
551
589
|
autoFocus: !focusedQuestions.current.has(index),
|
|
552
|
-
|
|
590
|
+
variant: "block",
|
|
553
591
|
value: draft.custom,
|
|
554
592
|
disabled: busy !== null,
|
|
555
|
-
rows: 2,
|
|
556
593
|
placeholder: t("custom.placeholder"),
|
|
557
594
|
onFocus: () => {
|
|
558
595
|
focusedQuestions.current.add(index);
|
|
@@ -1657,6 +1657,32 @@ window.__ModuleLoader__.load({
|
|
|
1657
1657
|
const groupExpansion = useStore((s) => s.groupExpansion);
|
|
1658
1658
|
const sessionOrderByAccount = useStore((s) => s.sessionOrderByAccount);
|
|
1659
1659
|
const sessionUpdatedAtByAccount = useStore((s) => s.sessionUpdatedAtByAccount);
|
|
1660
|
+
const currentBlankSessionId = useSessions((state) => {
|
|
1661
|
+
const current = state.current;
|
|
1662
|
+
return current !== void 0 && state.byId[current]?.blank === true ? current : void 0;
|
|
1663
|
+
});
|
|
1664
|
+
const currentBlankAccount = currentBlankSessionId === void 0 ? void 0 : workspaces.find((workspace) => workspace.sessionIds.includes(currentBlankSessionId))?.workspaceId ?? "";
|
|
1665
|
+
const promotedBlank = (0, react.useRef)(void 0);
|
|
1666
|
+
(0, react.useEffect)(() => {
|
|
1667
|
+
if (currentBlankSessionId === void 0 || currentBlankAccount === void 0) {
|
|
1668
|
+
promotedBlank.current = void 0;
|
|
1669
|
+
return;
|
|
1670
|
+
}
|
|
1671
|
+
if (promotedBlank.current?.sessionId === currentBlankSessionId && promotedBlank.current.accountKey === currentBlankAccount) return;
|
|
1672
|
+
promotedBlank.current = {
|
|
1673
|
+
sessionId: currentBlankSessionId,
|
|
1674
|
+
accountKey: currentBlankAccount
|
|
1675
|
+
};
|
|
1676
|
+
for (const accountKey of new Set([currentBlankAccount, FLAT_SESSION_ORDER_KEY])) {
|
|
1677
|
+
const previous = sessionOrderByAccount[accountKey] ?? [];
|
|
1678
|
+
actions.setSessionOrder(accountKey, [currentBlankSessionId, ...previous.filter((id) => id !== currentBlankSessionId)]);
|
|
1679
|
+
}
|
|
1680
|
+
}, [
|
|
1681
|
+
actions.setSessionOrder,
|
|
1682
|
+
currentBlankAccount,
|
|
1683
|
+
currentBlankSessionId,
|
|
1684
|
+
sessionOrderByAccount
|
|
1685
|
+
]);
|
|
1660
1686
|
(0, react.useEffect)(() => {
|
|
1661
1687
|
if (workspacePhase !== "ready") return;
|
|
1662
1688
|
actions.retainAccountKeys([
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-edge",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0-alpha.1",
|
|
4
4
|
"description": "Run DeepSeek Harness on Cloudflare Workers as an independent community project",
|
|
5
5
|
"author": "pawaca",
|
|
6
6
|
"license": "MIT",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"access": "public"
|
|
42
42
|
},
|
|
43
43
|
"dshEdge": {
|
|
44
|
-
"upstreamVersion": "0.1.
|
|
44
|
+
"upstreamVersion": "0.1.1-rc.1"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@clack/prompts": "1.7.0",
|
|
@@ -54,27 +54,30 @@
|
|
|
54
54
|
"@cloudflare/computer": "0.2.0",
|
|
55
55
|
"@cloudflare/workers-types": "5.20260812.1",
|
|
56
56
|
"@deepseek-ai/cordis": "4.0.1",
|
|
57
|
-
"@deepseek-ai/dsh-agent": "0.1.
|
|
58
|
-
"@deepseek-ai/dsh-agent-loop": "0.1.
|
|
59
|
-
"@deepseek-ai/dsh-anonymous-user-id": "0.1.
|
|
60
|
-
"@deepseek-ai/dsh-
|
|
61
|
-
"@deepseek-ai/dsh-
|
|
62
|
-
"@deepseek-ai/dsh-
|
|
63
|
-
"@deepseek-ai/dsh-
|
|
64
|
-
"@deepseek-ai/dsh-
|
|
65
|
-
"@deepseek-ai/dsh-llm
|
|
66
|
-
"@deepseek-ai/dsh-
|
|
67
|
-
"@deepseek-ai/dsh-session
|
|
68
|
-
"@deepseek-ai/dsh-session-
|
|
69
|
-
"@deepseek-ai/dsh-session-
|
|
70
|
-
"@deepseek-ai/dsh-
|
|
71
|
-
"@deepseek-ai/dsh-
|
|
72
|
-
"@deepseek-ai/dsh-
|
|
73
|
-
"@deepseek-ai/dsh-tool-
|
|
74
|
-
"@deepseek-ai/dsh-
|
|
75
|
-
"@deepseek-ai/dsh-
|
|
76
|
-
"@deepseek-ai/dsh-web
|
|
57
|
+
"@deepseek-ai/dsh-agent": "0.1.1-rc.1",
|
|
58
|
+
"@deepseek-ai/dsh-agent-loop": "0.1.1-rc.1",
|
|
59
|
+
"@deepseek-ai/dsh-anonymous-user-id": "0.1.1-rc.1",
|
|
60
|
+
"@deepseek-ai/dsh-attachment": "0.1.1-rc.1",
|
|
61
|
+
"@deepseek-ai/dsh-brand": "0.1.1-rc.1",
|
|
62
|
+
"@deepseek-ai/dsh-credentials": "0.1.1-rc.1",
|
|
63
|
+
"@deepseek-ai/dsh-host-apiproxy": "0.1.1-rc.1",
|
|
64
|
+
"@deepseek-ai/dsh-launch-environment": "0.1.1-rc.1",
|
|
65
|
+
"@deepseek-ai/dsh-llm": "0.1.1-rc.1",
|
|
66
|
+
"@deepseek-ai/dsh-llm-deepseek": "0.1.1-rc.1",
|
|
67
|
+
"@deepseek-ai/dsh-session": "0.1.1-rc.1",
|
|
68
|
+
"@deepseek-ai/dsh-session-persistence": "0.1.1-rc.1",
|
|
69
|
+
"@deepseek-ai/dsh-session-query": "0.1.1-rc.1",
|
|
70
|
+
"@deepseek-ai/dsh-session-title": "0.1.1-rc.1",
|
|
71
|
+
"@deepseek-ai/dsh-settings": "0.1.1-rc.1",
|
|
72
|
+
"@deepseek-ai/dsh-system-prompt": "0.1.1-rc.1",
|
|
73
|
+
"@deepseek-ai/dsh-tool-call-timeout-policy": "0.1.1-rc.1",
|
|
74
|
+
"@deepseek-ai/dsh-tool-web": "0.1.1-rc.1",
|
|
75
|
+
"@deepseek-ai/dsh-tools": "0.1.1-rc.1",
|
|
76
|
+
"@deepseek-ai/dsh-web": "0.1.1-rc.1",
|
|
77
|
+
"@deepseek-ai/dsh-web-search-deepseek": "0.1.1-rc.1",
|
|
77
78
|
"just-bash": "3.3.0",
|
|
79
|
+
"fast-png": "8.0.0",
|
|
80
|
+
"jpeg-js": "0.4.4",
|
|
78
81
|
"playwright": "^1.49.0",
|
|
79
82
|
"typescript": "^6.0.3",
|
|
80
83
|
"vitest": "^4.1.8",
|
package/scripts/cli.mjs
CHANGED
|
@@ -166,6 +166,9 @@ export function createInstallerUi(
|
|
|
166
166
|
`Account: ${summary.accountLabel}`,
|
|
167
167
|
`Worker: ${summary.workerName}`,
|
|
168
168
|
`Cost: ${summary.paid ? 'Workers Paid is required' : 'Works on Workers Free'}`,
|
|
169
|
+
`Images: ${summary.attachmentStorage === 'temporary-do'
|
|
170
|
+
? 'stored in this instance (64 MiB limit)'
|
|
171
|
+
: 'stored privately in Cloudflare R2'}`,
|
|
169
172
|
...(command === 'upgrade' ? ['Existing Durable Object data is preserved.', 'The entered secrets replace the active values.'] : []),
|
|
170
173
|
].join('\n'), command === 'upgrade' ? 'Upgrade summary' : 'Installation summary')
|
|
171
174
|
return await requireAnswer(await clack.confirm(withOutput({
|
package/scripts/install.d.mts
CHANGED
|
@@ -30,6 +30,8 @@ export interface InstallerUi {
|
|
|
30
30
|
accountLabel: string
|
|
31
31
|
workerName: string
|
|
32
32
|
paid: boolean
|
|
33
|
+
temporary: boolean
|
|
34
|
+
attachmentStorage: 'private-r2' | 'temporary-do'
|
|
33
35
|
}): Promise<boolean>
|
|
34
36
|
acceptTemporaryTerms(): Promise<boolean>
|
|
35
37
|
deploymentStart?(message: string): void
|
|
@@ -58,6 +60,7 @@ export interface InstallResult {
|
|
|
58
60
|
publicUrl: string
|
|
59
61
|
versionId?: string
|
|
60
62
|
account?: CloudflareAccount
|
|
63
|
+
attachmentStorage: 'private-r2' | 'temporary-do'
|
|
61
64
|
claimUrl?: string
|
|
62
65
|
mode: RuntimeMode
|
|
63
66
|
ownerSecret: string
|
|
@@ -86,6 +89,17 @@ export function validateWorkerName(value: string): string | undefined
|
|
|
86
89
|
export function validateOwnerSecret(value: string): string | undefined
|
|
87
90
|
export function validateDeepSeekKey(value: string): string | undefined
|
|
88
91
|
export function generateOwnerSecret(): string
|
|
92
|
+
export function attachmentBucketName(workerName: string): string
|
|
93
|
+
export function ensureR2Bucket(options: {
|
|
94
|
+
bucketName: string
|
|
95
|
+
runWrangler: (args: string[], options?: {
|
|
96
|
+
environment?: NodeJS.ProcessEnv
|
|
97
|
+
signal?: AbortSignal
|
|
98
|
+
}) => Promise<CommandResult>
|
|
99
|
+
environment?: NodeJS.ProcessEnv
|
|
100
|
+
profile?: string
|
|
101
|
+
signal?: AbortSignal
|
|
102
|
+
}): Promise<{ bucketName: string; created: boolean }>
|
|
89
103
|
export function wranglerEnvironment(environment?: NodeJS.ProcessEnv): NodeJS.ProcessEnv
|
|
90
104
|
export function unauthenticatedEnvironment(environment?: NodeJS.ProcessEnv): NodeJS.ProcessEnv
|
|
91
105
|
export function wranglerDeployArgs(options: {
|
|
@@ -99,6 +113,17 @@ export function wranglerDeployArgs(options: {
|
|
|
99
113
|
export function parseDeploymentOutput(source: string): { publicUrl: string; versionId?: string }
|
|
100
114
|
export function parseClaimUrl(source: string): string | undefined
|
|
101
115
|
export function parseWorkerExistence(result: CommandResult): boolean
|
|
116
|
+
export function detectExistingAttachmentStorage(options: {
|
|
117
|
+
workerName: string
|
|
118
|
+
mode: RuntimeMode
|
|
119
|
+
runWrangler: (args: string[], options?: {
|
|
120
|
+
environment?: NodeJS.ProcessEnv
|
|
121
|
+
signal?: AbortSignal
|
|
122
|
+
}) => Promise<CommandResult>
|
|
123
|
+
environment?: NodeJS.ProcessEnv
|
|
124
|
+
profile?: string
|
|
125
|
+
signal?: AbortSignal
|
|
126
|
+
}): Promise<'private-r2' | 'temporary-do'>
|
|
102
127
|
export function truncateUtf8Tail(value: string, maxBytes: number): string
|
|
103
128
|
export function createOutputForwarder(
|
|
104
129
|
source: NodeJS.ReadableStream,
|
package/scripts/install.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { randomBytes } from 'node:crypto'
|
|
1
|
+
import { createHash, randomBytes } from 'node:crypto'
|
|
2
2
|
import { execFile } from 'node:child_process'
|
|
3
3
|
import { chmod, mkdtemp, open, rm, writeFile } from 'node:fs/promises'
|
|
4
4
|
import { createRequire } from 'node:module'
|
|
@@ -93,6 +93,7 @@ const SENSITIVE_ENV_KEY = /(KEY|PASSWORD|SECRET|TOKEN)/iu
|
|
|
93
93
|
const WORKER_NAME = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/u
|
|
94
94
|
const CLAIM_URL = /https:\/\/dash\.cloudflare\.com\/claim-preview\?[^\s\u001b]+/u
|
|
95
95
|
const MAX_CAPTURE_BYTES = 2 * 1024 * 1024
|
|
96
|
+
const ATTACHMENT_STORAGE_BINDING = 'DSH_EDGE_ATTACHMENT_STORAGE'
|
|
96
97
|
const WINDOWS_ACL_TIMEOUT_MS = 30_000
|
|
97
98
|
const WINDOWS_PRIVATE_DIRECTORY_SCRIPT = String.raw`
|
|
98
99
|
$ErrorActionPreference = 'Stop'
|
|
@@ -232,6 +233,58 @@ export function generateOwnerSecret() {
|
|
|
232
233
|
return randomBytes(32).toString('base64url')
|
|
233
234
|
}
|
|
234
235
|
|
|
236
|
+
/** Derive one stable account-local R2 bucket name from the Worker service name. */
|
|
237
|
+
export function attachmentBucketName(workerName) {
|
|
238
|
+
if (validateWorkerName(workerName) !== undefined) throw new Error('Worker name is invalid.')
|
|
239
|
+
const suffix = '-attachments'
|
|
240
|
+
if (workerName.length + suffix.length <= 63) return `${workerName}${suffix}`
|
|
241
|
+
const digest = createHash('sha256').update(workerName).digest('hex').slice(0, 8)
|
|
242
|
+
const prefix = workerName.slice(0, 42).replace(/-+$/u, '')
|
|
243
|
+
return `${prefix}-${digest}${suffix}`
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/** Create or reuse the permanent deployment's private R2 attachment bucket. */
|
|
247
|
+
export async function ensureR2Bucket({
|
|
248
|
+
bucketName,
|
|
249
|
+
runWrangler,
|
|
250
|
+
environment,
|
|
251
|
+
profile,
|
|
252
|
+
signal,
|
|
253
|
+
}) {
|
|
254
|
+
const infoArgs = ['r2', 'bucket', 'info', bucketName, '--json', ...profileArgs(profile)]
|
|
255
|
+
const initial = await runWrangler(infoArgs, { environment, signal })
|
|
256
|
+
if (initial.status === 0) {
|
|
257
|
+
requireR2BucketInfo(initial.stdout, bucketName)
|
|
258
|
+
return { bucketName, created: false }
|
|
259
|
+
}
|
|
260
|
+
const created = await runWrangler([
|
|
261
|
+
'r2', 'bucket', 'create', bucketName, ...profileArgs(profile),
|
|
262
|
+
], { environment, signal })
|
|
263
|
+
if (created.status === 0) return { bucketName, created: true }
|
|
264
|
+
// A concurrent installer may have won the create race; prove exact existence.
|
|
265
|
+
const recovered = await runWrangler(infoArgs, { environment, signal })
|
|
266
|
+
if (recovered.status === 0) {
|
|
267
|
+
requireR2BucketInfo(recovered.stdout, bucketName)
|
|
268
|
+
return { bucketName, created: false }
|
|
269
|
+
}
|
|
270
|
+
throw new Error(commandFailure(
|
|
271
|
+
`Could not create or access private R2 bucket "${bucketName}". Enable R2 for this Cloudflare account (or use a temporary preview), then retry`,
|
|
272
|
+
created,
|
|
273
|
+
))
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function requireR2BucketInfo(source, expectedName) {
|
|
277
|
+
let info
|
|
278
|
+
try {
|
|
279
|
+
info = JSON.parse(source)
|
|
280
|
+
} catch {
|
|
281
|
+
throw new Error('Wrangler returned malformed R2 bucket information.')
|
|
282
|
+
}
|
|
283
|
+
if (info === null || typeof info !== 'object' || info.name !== expectedName) {
|
|
284
|
+
throw new Error('Wrangler returned unexpected R2 bucket information.')
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
235
288
|
/** Build the least-privilege environment used by authenticated Wrangler commands. */
|
|
236
289
|
export function wranglerEnvironment(environment = process.env) {
|
|
237
290
|
return pickEnvironment(environment, [
|
|
@@ -319,6 +372,94 @@ export function parseWorkerExistence(result) {
|
|
|
319
372
|
throw new Error(commandFailure('Could not check whether the Worker already exists', result))
|
|
320
373
|
}
|
|
321
374
|
|
|
375
|
+
/** Inspect active Worker versions so upgrades preserve the existing attachment backend. */
|
|
376
|
+
export async function detectExistingAttachmentStorage({
|
|
377
|
+
workerName,
|
|
378
|
+
mode,
|
|
379
|
+
runWrangler,
|
|
380
|
+
environment,
|
|
381
|
+
profile,
|
|
382
|
+
signal,
|
|
383
|
+
}) {
|
|
384
|
+
const args = [
|
|
385
|
+
'--name', workerName, '--json',
|
|
386
|
+
...runtimeEnvironmentArgs(mode),
|
|
387
|
+
...profileArgs(profile),
|
|
388
|
+
]
|
|
389
|
+
const status = await runWrangler(['deployments', 'status', ...args], {
|
|
390
|
+
environment,
|
|
391
|
+
signal,
|
|
392
|
+
})
|
|
393
|
+
requireSuccess(status, 'Could not inspect the existing Worker deployment')
|
|
394
|
+
const versionIds = deploymentVersionIds(status.stdout)
|
|
395
|
+
const backends = new Set()
|
|
396
|
+
for (const versionId of versionIds) {
|
|
397
|
+
const version = await runWrangler([
|
|
398
|
+
'versions', 'view', versionId, ...args,
|
|
399
|
+
], { environment, signal })
|
|
400
|
+
requireSuccess(version, `Could not inspect existing Worker version ${versionId}`)
|
|
401
|
+
backends.add(versionAttachmentStorage(version.stdout))
|
|
402
|
+
}
|
|
403
|
+
if (backends.size !== 1) {
|
|
404
|
+
throw new Error('The active Worker versions use different attachment backends. Finish the existing rollout before upgrading.')
|
|
405
|
+
}
|
|
406
|
+
return backends.values().next().value
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function deploymentVersionIds(source) {
|
|
410
|
+
let deployment
|
|
411
|
+
try {
|
|
412
|
+
deployment = JSON.parse(source)
|
|
413
|
+
} catch {
|
|
414
|
+
throw new Error('Wrangler returned malformed deployment status.')
|
|
415
|
+
}
|
|
416
|
+
if (!isRecord(deployment) || !Array.isArray(deployment.versions)) {
|
|
417
|
+
throw new Error('Wrangler returned unexpected deployment status.')
|
|
418
|
+
}
|
|
419
|
+
const ids = deployment.versions
|
|
420
|
+
.filter(version => isRecord(version) && typeof version.percentage === 'number'
|
|
421
|
+
&& version.percentage > 0)
|
|
422
|
+
.map(version => version.version_id)
|
|
423
|
+
if (ids.length === 0 || ids.some(id => typeof id !== 'string' || id.length === 0)) {
|
|
424
|
+
throw new Error('Wrangler deployment status has no active Worker version.')
|
|
425
|
+
}
|
|
426
|
+
return [...new Set(ids)]
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function versionAttachmentStorage(source) {
|
|
430
|
+
let version
|
|
431
|
+
try {
|
|
432
|
+
version = JSON.parse(source)
|
|
433
|
+
} catch {
|
|
434
|
+
throw new Error('Wrangler returned malformed Worker version details.')
|
|
435
|
+
}
|
|
436
|
+
const bindings = isRecord(version) && isRecord(version.resources)
|
|
437
|
+
? version.resources.bindings
|
|
438
|
+
: undefined
|
|
439
|
+
if (!Array.isArray(bindings) || bindings.some(binding => !isRecord(binding))) {
|
|
440
|
+
throw new Error('Wrangler returned unexpected Worker version details.')
|
|
441
|
+
}
|
|
442
|
+
const attachment = bindings.filter(binding => binding.name === 'DSH_EDGE_ATTACHMENTS')
|
|
443
|
+
if (attachment.length > 1
|
|
444
|
+
|| (attachment.length === 1 && attachment[0].type !== 'r2_bucket')) {
|
|
445
|
+
throw new Error('The existing Worker has an invalid attachment binding.')
|
|
446
|
+
}
|
|
447
|
+
const markers = bindings.filter(binding => binding.name === ATTACHMENT_STORAGE_BINDING)
|
|
448
|
+
if (markers.length > 1 || (markers.length === 1
|
|
449
|
+
&& (markers[0].type !== 'plain_text'
|
|
450
|
+
|| (markers[0].text !== 'temporary-do' && markers[0].text !== 'private-r2')))) {
|
|
451
|
+
throw new Error('The existing Worker has an invalid attachment storage marker.')
|
|
452
|
+
}
|
|
453
|
+
const marker = markers[0]?.text
|
|
454
|
+
if ((marker === 'temporary-do' && attachment.length !== 0)
|
|
455
|
+
|| (marker === 'private-r2' && attachment.length !== 1)) {
|
|
456
|
+
throw new Error('The existing Worker attachment marker does not match its binding.')
|
|
457
|
+
}
|
|
458
|
+
// Releases before attachment support had neither binding nor marker. An
|
|
459
|
+
// authenticated upgrade can safely initialize those instances on private R2.
|
|
460
|
+
return marker ?? 'private-r2'
|
|
461
|
+
}
|
|
462
|
+
|
|
322
463
|
/** Run the complete guided install with UI and Wrangler supplied as replaceable boundaries. */
|
|
323
464
|
export async function installEdge({
|
|
324
465
|
command = 'install',
|
|
@@ -380,6 +521,7 @@ export async function installEdge({
|
|
|
380
521
|
: requireSelectedAccount(accountSelection, accounts)
|
|
381
522
|
|
|
382
523
|
let workerName = DEFAULT_WORKER_NAME
|
|
524
|
+
let updatingExisting = false
|
|
383
525
|
while (true) {
|
|
384
526
|
workerName = await ui.workerName(workerName, validateWorkerName)
|
|
385
527
|
if (temporary) break
|
|
@@ -394,15 +536,33 @@ export async function installEdge({
|
|
|
394
536
|
}))
|
|
395
537
|
if (command === 'upgrade') {
|
|
396
538
|
if (!exists) throw new Error(`${workerName} does not exist in this account and runtime. Run dsh-edge install first.`)
|
|
539
|
+
updatingExisting = true
|
|
397
540
|
break
|
|
398
541
|
}
|
|
399
542
|
if (!exists) break
|
|
400
543
|
const action = await ui.workerConflict(workerName)
|
|
401
|
-
if (action === 'update')
|
|
544
|
+
if (action === 'update') {
|
|
545
|
+
updatingExisting = true
|
|
546
|
+
break
|
|
547
|
+
}
|
|
402
548
|
if (action === 'cancel') throw new InstallCancelledError()
|
|
403
549
|
workerName = `${workerName}-2`
|
|
404
550
|
}
|
|
405
551
|
|
|
552
|
+
const commandEnvironment = temporary
|
|
553
|
+
? unauthenticatedEnvironment(environment)
|
|
554
|
+
: accountEnvironment(profileEnvironment ?? environment, account.id)
|
|
555
|
+
const attachmentStorage = updatingExisting
|
|
556
|
+
? await detectExistingAttachmentStorage({
|
|
557
|
+
workerName,
|
|
558
|
+
mode,
|
|
559
|
+
runWrangler,
|
|
560
|
+
environment: commandEnvironment,
|
|
561
|
+
profile,
|
|
562
|
+
signal,
|
|
563
|
+
})
|
|
564
|
+
: temporary ? 'temporary-do' : 'private-r2'
|
|
565
|
+
|
|
406
566
|
const secretMode = await ui.selectOwnerSecretMode()
|
|
407
567
|
const ownerSecret = secretMode === 'generate'
|
|
408
568
|
? generateOwnerSecret()
|
|
@@ -419,17 +579,34 @@ export async function installEdge({
|
|
|
419
579
|
accountLabel: temporary ? 'Temporary account' : account.name,
|
|
420
580
|
workerName,
|
|
421
581
|
paid: mode === 'isolated',
|
|
582
|
+
temporary,
|
|
583
|
+
attachmentStorage,
|
|
422
584
|
})
|
|
423
585
|
if (!confirmed) throw new InstallCancelledError()
|
|
424
586
|
if (temporary && !await ui.acceptTemporaryTerms()) {
|
|
425
587
|
throw new InstallCancelledError()
|
|
426
588
|
}
|
|
427
589
|
|
|
590
|
+
let bucketName
|
|
591
|
+
if (attachmentStorage === 'private-r2') {
|
|
592
|
+
bucketName = attachmentBucketName(workerName)
|
|
593
|
+
ui.step(`Preparing private image storage (${bucketName})…`)
|
|
594
|
+
await ensureR2Bucket({
|
|
595
|
+
bucketName,
|
|
596
|
+
runWrangler,
|
|
597
|
+
environment: commandEnvironment,
|
|
598
|
+
profile,
|
|
599
|
+
signal,
|
|
600
|
+
})
|
|
601
|
+
}
|
|
602
|
+
|
|
428
603
|
temporaryDirectory = await createTemporaryDirectory()
|
|
429
604
|
const secretsFile = join(temporaryDirectory, 'secrets.json')
|
|
430
605
|
const configFile = join(temporaryDirectory, 'wrangler.json')
|
|
431
606
|
const outputFile = join(temporaryDirectory, 'wrangler-output.ndjson')
|
|
432
|
-
await writePrebuiltModeWranglerConfig(mode, configFile
|
|
607
|
+
await writePrebuiltModeWranglerConfig(mode, configFile, {
|
|
608
|
+
...bucketName === undefined ? {} : { r2BucketName: bucketName },
|
|
609
|
+
})
|
|
433
610
|
await writeFile(secretsFile, JSON.stringify({
|
|
434
611
|
DEEPSEEK_API_KEY: deepSeekKey,
|
|
435
612
|
DSH_EDGE_ACCESS_KEY: ownerSecret,
|
|
@@ -439,9 +616,6 @@ export async function installEdge({
|
|
|
439
616
|
? 'Uploading the tested Worker release…'
|
|
440
617
|
: 'Installing the tested Worker release…'
|
|
441
618
|
ui.deploymentStart?.(deploymentMessage)
|
|
442
|
-
const commandEnvironment = temporary
|
|
443
|
-
? unauthenticatedEnvironment(environment)
|
|
444
|
-
: accountEnvironment(profileEnvironment ?? environment, account.id)
|
|
445
619
|
if (temporary) commandEnvironment.XDG_CONFIG_HOME = temporaryDirectory
|
|
446
620
|
commandEnvironment.WRANGLER_LOG_SANITIZE = 'true'
|
|
447
621
|
commandEnvironment.WRANGLER_OUTPUT_FILE_PATH = outputFile
|
|
@@ -527,6 +701,7 @@ export async function installEdge({
|
|
|
527
701
|
let result = {
|
|
528
702
|
...deployment,
|
|
529
703
|
account,
|
|
704
|
+
attachmentStorage,
|
|
530
705
|
claimUrl,
|
|
531
706
|
mode,
|
|
532
707
|
ownerSecret,
|
|
@@ -843,6 +1018,10 @@ function isTerminalSafe(value) {
|
|
|
843
1018
|
return !CONTROL_CHARACTER.test(value) && !BIDI_CONTROL.test(value)
|
|
844
1019
|
}
|
|
845
1020
|
|
|
1021
|
+
function isRecord(value) {
|
|
1022
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
1023
|
+
}
|
|
1024
|
+
|
|
846
1025
|
/** Create a chunk-safe terminal filter that retains plain text only. */
|
|
847
1026
|
export function createTerminalSanitizer() {
|
|
848
1027
|
let state = 'text'
|
package/scripts/legal-files.mjs
CHANGED
|
@@ -41,6 +41,10 @@ const repositoryRoot = fileURLToPath(new URL('../../../', import.meta.url))
|
|
|
41
41
|
const packageRoot = fileURLToPath(new URL('../', import.meta.url))
|
|
42
42
|
const standaloneRoot = join(packageRoot, 'standalone')
|
|
43
43
|
const edgeManifest = JSON.parse(readFileSync(join(packageRoot, 'package.json'), 'utf8'))
|
|
44
|
+
const patchAudit = JSON.parse(readFileSync(join(standaloneRoot, 'patches', 'audit.json'), 'utf8'))
|
|
45
|
+
if (!Array.isArray(patchAudit) || patchAudit.length === 0) {
|
|
46
|
+
throw new Error('The retained patch audit must be a non-empty array.')
|
|
47
|
+
}
|
|
44
48
|
|
|
45
49
|
function bundledComponents() {
|
|
46
50
|
const invocation = resolvePnpmInvocation(process.env.npm_execpath ?? 'pnpm', [
|
|
@@ -212,7 +216,7 @@ function renderNotices(components) {
|
|
|
212
216
|
|
|
213
217
|
## DeepSeek Harness
|
|
214
218
|
|
|
215
|
-
\`dsh-edge\` assembles published DeepSeek Harness packages and applies
|
|
219
|
+
\`dsh-edge\` assembles published DeepSeek Harness packages and applies ${String(patchAudit.length)} version-bound adaptations to the pinned \`${edgeManifest.dshEdge.upstreamVersion}\` release. DeepSeek Harness remains under its upstream MIT license:
|
|
216
220
|
|
|
217
221
|
\`\`\`text
|
|
218
222
|
${upstreamLicense}
|
|
@@ -11,6 +11,8 @@ const WORKER_ARTIFACTS = Object.freeze({
|
|
|
11
11
|
direct: 'worker/direct/index.js',
|
|
12
12
|
isolated: 'worker/isolated/index.js',
|
|
13
13
|
})
|
|
14
|
+
const ATTACHMENT_BINDING = 'DSH_EDGE_ATTACHMENTS'
|
|
15
|
+
const ATTACHMENT_STORAGE_BINDING = 'DSH_EDGE_ATTACHMENT_STORAGE'
|
|
14
16
|
|
|
15
17
|
/** Render one mode-specific build configuration from an already parsed source object. */
|
|
16
18
|
export function renderParsedSourceModeWranglerConfig(mode, parsed, options = {}) {
|
|
@@ -34,6 +36,7 @@ export function renderParsedSourceModeWranglerConfig(mode, parsed, options = {})
|
|
|
34
36
|
config.main = resolve(root, parsed.main)
|
|
35
37
|
config.assets.directory = options.assetsDirectory ?? resolve(root, parsed.assets.directory)
|
|
36
38
|
config.minify = true
|
|
39
|
+
applyAttachmentStorage(config, mode, options.r2BucketName)
|
|
37
40
|
if (mode === 'direct') {
|
|
38
41
|
config.alias = {
|
|
39
42
|
...config.alias,
|
|
@@ -63,9 +66,34 @@ export function renderParsedPrebuiltModeWranglerConfig(mode, parsed, options = {
|
|
|
63
66
|
config.assets.directory = resolve(root, parsed.assets.directory)
|
|
64
67
|
config.no_bundle = true
|
|
65
68
|
config.find_additional_modules = false
|
|
69
|
+
applyAttachmentStorage(config, mode, options.r2BucketName)
|
|
66
70
|
return `${JSON.stringify(config, undefined, 2)}\n`
|
|
67
71
|
}
|
|
68
72
|
|
|
73
|
+
function applyAttachmentStorage(config, mode, bucketName) {
|
|
74
|
+
if (bucketName !== undefined && (typeof bucketName !== 'string' || bucketName.length === 0)) {
|
|
75
|
+
throw new Error('R2 attachment bucket name must be a non-empty string.')
|
|
76
|
+
}
|
|
77
|
+
const target = mode === 'direct' ? config : config.env?.isolated
|
|
78
|
+
if (!isRecord(target)) {
|
|
79
|
+
throw new Error('wrangler.jsonc must declare the isolated environment.')
|
|
80
|
+
}
|
|
81
|
+
if (target.vars !== undefined && !isRecord(target.vars)) {
|
|
82
|
+
throw new Error('wrangler.jsonc vars must be an object.')
|
|
83
|
+
}
|
|
84
|
+
target.vars = {
|
|
85
|
+
...target.vars,
|
|
86
|
+
[ATTACHMENT_STORAGE_BINDING]: bucketName === undefined ? 'temporary-do' : 'private-r2',
|
|
87
|
+
}
|
|
88
|
+
if (bucketName === undefined) return
|
|
89
|
+
const binding = [{ binding: ATTACHMENT_BINDING, bucket_name: bucketName }]
|
|
90
|
+
if (mode === 'direct') {
|
|
91
|
+
config.r2_buckets = binding
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
target.r2_buckets = binding
|
|
95
|
+
}
|
|
96
|
+
|
|
69
97
|
/** Return the released entrypoint for a runtime mode. */
|
|
70
98
|
export function workerArtifactPath(mode, options = {}) {
|
|
71
99
|
requireRuntimeMode(mode)
|