dsh-edge 0.2.0 → 0.3.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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)}.Mbwy4a_customInput{min-width:0;color:var(--dsw-alias-label-primary);caret-color:var(--dsw-alias-state-business-primary);font:inherit;background:0 0;border:none;outline:none;flex:1;padding:0;font-size:14px;line-height:24px}.Mbwy4a_customInput::placeholder{color:var(--dsw-alias-label-caption)}.Mbwy4a_customTextarea{resize:none;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-module-platform);min-height:64px;max-height:140px;color:var(--dsw-alias-label-primary);caret-color:var(--dsw-alias-state-business-primary);font:inherit;border-radius:10px;outline:none;flex-shrink:0;margin:0 12px;padding:8px 12px;font-size:14px;line-height:24px;display:block}.Mbwy4a_customTextarea:focus{border-color:var(--dsw-alias-state-business-primary)}.Mbwy4a_customTextarea::placeholder{color:var(--dsw-alias-label-caption)}.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}}";
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
- "customInput": "Mbwy4a_customInput",
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)("input", {
542
- type: "text",
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)("textarea", {
588
+ }) : (0, react_jsx_runtime.jsx)(AnswerField, {
551
589
  autoFocus: !focusedQuestions.current.has(index),
552
- className: QuestionComposer_module_css_default.customTextarea,
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.2.0",
3
+ "version": "0.3.0-alpha.2",
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.0-rc.8"
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.0-rc.8",
58
- "@deepseek-ai/dsh-agent-loop": "0.1.0-rc.8",
59
- "@deepseek-ai/dsh-anonymous-user-id": "0.1.0-rc.8",
60
- "@deepseek-ai/dsh-brand": "0.1.0-rc.8",
61
- "@deepseek-ai/dsh-credentials": "0.1.0-rc.8",
62
- "@deepseek-ai/dsh-host-apiproxy": "0.1.0-rc.8",
63
- "@deepseek-ai/dsh-launch-environment": "0.1.0-rc.8",
64
- "@deepseek-ai/dsh-llm": "0.1.0-rc.8",
65
- "@deepseek-ai/dsh-llm-deepseek": "0.1.0-rc.8",
66
- "@deepseek-ai/dsh-session": "0.1.0-rc.8",
67
- "@deepseek-ai/dsh-session-persistence": "0.1.0-rc.8",
68
- "@deepseek-ai/dsh-session-query": "0.1.0-rc.8",
69
- "@deepseek-ai/dsh-session-title": "0.1.0-rc.8",
70
- "@deepseek-ai/dsh-settings": "0.1.0-rc.8",
71
- "@deepseek-ai/dsh-system-prompt": "0.1.0-rc.8",
72
- "@deepseek-ai/dsh-tool-call-timeout-policy": "0.1.0-rc.8",
73
- "@deepseek-ai/dsh-tool-web": "0.1.0-rc.8",
74
- "@deepseek-ai/dsh-tools": "0.1.0-rc.8",
75
- "@deepseek-ai/dsh-web": "0.1.0-rc.8",
76
- "@deepseek-ai/dsh-web-search-deepseek": "0.1.0-rc.8",
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
@@ -133,6 +133,58 @@ export function createInstallerUi(
133
133
  ],
134
134
  })))
135
135
  },
136
+ async selectInitialAttachmentStorage() {
137
+ note([
138
+ 'This Worker predates image attachments, so no existing image references need migration.',
139
+ 'The selected backend is pinned for future upgrades and is not changed automatically.',
140
+ ].join('\n'), 'Choose image storage once')
141
+ return await requireAnswer(await clack.select(withOutput({
142
+ message: 'Where should this Worker store new images?',
143
+ initialValue: 'temporary-do',
144
+ signal,
145
+ options: [
146
+ {
147
+ value: 'temporary-do',
148
+ label: 'Durable Object — no R2 setup',
149
+ hint: 'recommended for Workers Free; 64 MiB per instance',
150
+ },
151
+ {
152
+ value: 'private-r2',
153
+ label: 'Private R2 bucket',
154
+ hint: 'requires an enabled R2 subscription; includes a free tier',
155
+ },
156
+ ],
157
+ })))
158
+ },
159
+ async r2SubscriptionUnavailable({ activationUrl, canSwitchToDurableObject }) {
160
+ note([
161
+ 'Cloudflare requires R2 to be enabled before dsh-edge can create a private bucket.',
162
+ `Enable R2: ${activationUrl}`,
163
+ 'R2 Standard includes monthly free usage, but activation requires Dashboard checkout.',
164
+ 'After checkout completes, return here and retry.',
165
+ ].join('\n'), 'R2 is not enabled for this account')
166
+ const options = [
167
+ ...(canSwitchToDurableObject
168
+ ? [{
169
+ value: 'temporary-do',
170
+ label: 'Use Durable Object storage',
171
+ hint: 'continue now without R2; 64 MiB per instance',
172
+ }]
173
+ : []),
174
+ {
175
+ value: 'retry',
176
+ label: 'Retry R2',
177
+ hint: 'choose this after enabling R2 in the Dashboard',
178
+ },
179
+ { value: 'cancel', label: 'Cancel installation' },
180
+ ]
181
+ return await requireAnswer(await clack.select(withOutput({
182
+ message: 'How should dsh-edge continue?',
183
+ initialValue: canSwitchToDurableObject ? 'temporary-do' : 'retry',
184
+ signal,
185
+ options,
186
+ })))
187
+ },
136
188
  async selectOwnerSecretMode() {
137
189
  return await requireAnswer(await clack.select(withOutput({
138
190
  message: 'Set the owner access key',
@@ -166,7 +218,10 @@ export function createInstallerUi(
166
218
  `Account: ${summary.accountLabel}`,
167
219
  `Worker: ${summary.workerName}`,
168
220
  `Cost: ${summary.paid ? 'Workers Paid is required' : 'Works on Workers Free'}`,
169
- ...(command === 'upgrade' ? ['Existing Durable Object data is preserved.', 'The entered secrets replace the active values.'] : []),
221
+ `Images: ${summary.attachmentStorage === 'temporary-do'
222
+ ? 'stored in this instance (64 MiB limit)'
223
+ : 'stored privately in Cloudflare R2'}`,
224
+ ...(command === 'upgrade' ? ['Existing Durable Object data is preserved.', 'You will re-enter the two Worker secrets after confirming.'] : []),
170
225
  ].join('\n'), command === 'upgrade' ? 'Upgrade summary' : 'Installation summary')
171
226
  return await requireAnswer(await clack.confirm(withOutput({
172
227
  message: command === 'upgrade' ? 'Upgrade this instance?' : 'Install this instance?',
@@ -1,5 +1,6 @@
1
1
  export type RuntimeMode = 'direct' | 'isolated'
2
2
  export type InstallerCommand = 'install' | 'upgrade'
3
+ export type AttachmentStorage = 'private-r2' | 'temporary-do'
3
4
 
4
5
  export interface CloudflareAccount {
5
6
  id: string
@@ -21,6 +22,11 @@ export interface InstallerUi {
21
22
  selectAccount(choices: Array<{ value: string; label: string; hint?: string }>): Promise<string>
22
23
  workerName(initialValue: string, validate: (value: string) => string | undefined): Promise<string>
23
24
  workerConflict(workerName: string): Promise<'rename' | 'update' | 'cancel'>
25
+ selectInitialAttachmentStorage(): Promise<AttachmentStorage>
26
+ r2SubscriptionUnavailable(options: {
27
+ activationUrl: string
28
+ canSwitchToDurableObject: boolean
29
+ }): Promise<'retry' | 'temporary-do' | 'cancel'>
24
30
  selectOwnerSecretMode(): Promise<'generate' | 'custom'>
25
31
  ownerSecret(validate: (value: string) => string | undefined): Promise<string>
26
32
  deepSeekKey(validate: (value: string) => string | undefined): Promise<string>
@@ -30,6 +36,8 @@ export interface InstallerUi {
30
36
  accountLabel: string
31
37
  workerName: string
32
38
  paid: boolean
39
+ temporary: boolean
40
+ attachmentStorage: AttachmentStorage
33
41
  }): Promise<boolean>
34
42
  acceptTemporaryTerms(): Promise<boolean>
35
43
  deploymentStart?(message: string): void
@@ -58,6 +66,7 @@ export interface InstallResult {
58
66
  publicUrl: string
59
67
  versionId?: string
60
68
  account?: CloudflareAccount
69
+ attachmentStorage: AttachmentStorage
61
70
  claimUrl?: string
62
71
  mode: RuntimeMode
63
72
  ownerSecret: string
@@ -86,6 +95,17 @@ export function validateWorkerName(value: string): string | undefined
86
95
  export function validateOwnerSecret(value: string): string | undefined
87
96
  export function validateDeepSeekKey(value: string): string | undefined
88
97
  export function generateOwnerSecret(): string
98
+ export function attachmentBucketName(workerName: string): string
99
+ export function ensureR2Bucket(options: {
100
+ bucketName: string
101
+ runWrangler: (args: string[], options?: {
102
+ environment?: NodeJS.ProcessEnv
103
+ signal?: AbortSignal
104
+ }) => Promise<CommandResult>
105
+ environment?: NodeJS.ProcessEnv
106
+ profile?: string
107
+ signal?: AbortSignal
108
+ }): Promise<{ bucketName: string; created: boolean }>
89
109
  export function wranglerEnvironment(environment?: NodeJS.ProcessEnv): NodeJS.ProcessEnv
90
110
  export function unauthenticatedEnvironment(environment?: NodeJS.ProcessEnv): NodeJS.ProcessEnv
91
111
  export function wranglerDeployArgs(options: {
@@ -99,6 +119,17 @@ export function wranglerDeployArgs(options: {
99
119
  export function parseDeploymentOutput(source: string): { publicUrl: string; versionId?: string }
100
120
  export function parseClaimUrl(source: string): string | undefined
101
121
  export function parseWorkerExistence(result: CommandResult): boolean
122
+ export function detectExistingAttachmentStorage(options: {
123
+ workerName: string
124
+ mode: RuntimeMode
125
+ runWrangler: (args: string[], options?: {
126
+ environment?: NodeJS.ProcessEnv
127
+ signal?: AbortSignal
128
+ }) => Promise<CommandResult>
129
+ environment?: NodeJS.ProcessEnv
130
+ profile?: string
131
+ signal?: AbortSignal
132
+ }): Promise<AttachmentStorage | undefined>
102
133
  export function truncateUtf8Tail(value: string, maxBytes: number): string
103
134
  export function createOutputForwarder(
104
135
  source: NodeJS.ReadableStream,