@lievclouds/dsh-cortex 0.3.6 → 0.3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lievclouds/dsh-cortex",
3
- "version": "0.3.6",
3
+ "version": "0.3.9",
4
4
  "description": "Cortex workbench for DeepSeek Harness: the original Cortex UI (vulnerabilities, assets, internal operations, Memory Nebula, snapshots, retest) as a native DSH panel, plus 12 native agent tools so DSH conversations use the local Cortex SQLite without MCP.",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -2,6 +2,7 @@ import * as DialogPrimitive from '@radix-ui/react-dialog'
2
2
  import { Loader2, Trash2 } from 'lucide-react'
3
3
  import { Button } from '@renderer/components/ui/button'
4
4
  import { useI18n } from '@renderer/i18n'
5
+ import { getPortalContainer } from '../../../dsh/portal'
5
6
 
6
7
  interface DeleteAssetDialogProps {
7
8
  open: boolean
@@ -27,8 +28,8 @@ export function DeleteAssetDialog({
27
28
  const { t } = useI18n()
28
29
 
29
30
  return (
30
- <DialogPrimitive.Root open={open} onOpenChange={onOpenChange}>
31
- <DialogPrimitive.Portal>
31
+ <DialogPrimitive.Root open={open} onOpenChange={onOpenChange} modal={false}>
32
+ <DialogPrimitive.Portal container={getPortalContainer()}>
32
33
  <DialogPrimitive.Overlay className="sheet-overlay fixed inset-0 z-[70] bg-[hsl(224_18%_10%/0.3)]" />
33
34
  <DialogPrimitive.Content className="fixed left-1/2 top-1/2 z-[70] w-[min(420px,calc(100vw-2rem))] -translate-x-1/2 -translate-y-1/2 rounded-xl border border-border bg-background p-5 shadow-panel focus:outline-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring">
34
35
  <DialogPrimitive.Title className="text-[16px] font-medium tracking-tight text-foreground">
@@ -37,6 +37,7 @@ import { usePocErrorMessage } from '@renderer/components/poc/usePocErrorMessage'
37
37
  import { getApi } from '@renderer/lib/api'
38
38
  import { useI18n } from '@renderer/i18n'
39
39
  import { cn } from '@renderer/lib/utils'
40
+ import { getPortalContainer } from '../../../dsh/portal'
40
41
 
41
42
  interface PocConfigPanelProps {
42
43
  open: boolean
@@ -122,8 +123,8 @@ export function PocConfigPanel({
122
123
  : null
123
124
 
124
125
  return (
125
- <DialogPrimitive.Root open={open} onOpenChange={onOpenChange}>
126
- <DialogPrimitive.Portal>
126
+ <DialogPrimitive.Root open={open} onOpenChange={onOpenChange} modal={false}>
127
+ <DialogPrimitive.Portal container={getPortalContainer()}>
127
128
  <DialogPrimitive.Overlay className="sheet-overlay fixed inset-0 z-[85] bg-[hsl(224_18%_10%/0.3)]" />
128
129
  <DialogPrimitive.Content
129
130
  data-testid="retest-config-panel"
@@ -5,6 +5,7 @@ import type { PocConfigView } from '@shared/poc'
5
5
  import { ProxySection } from '@renderer/components/poc/PocConfigPanel'
6
6
  import { useI18n } from '@renderer/i18n'
7
7
  import { getApi } from '@renderer/lib/api'
8
+ import { getPortalContainer } from '../../../dsh/portal'
8
9
 
9
10
  interface RetestProxyDialogProps {
10
11
  open: boolean
@@ -43,8 +44,8 @@ export function RetestProxyDialog({ open, onOpenChange }: RetestProxyDialogProps
43
44
  }, [open, t])
44
45
 
45
46
  return (
46
- <DialogPrimitive.Root open={open} onOpenChange={onOpenChange}>
47
- <DialogPrimitive.Portal>
47
+ <DialogPrimitive.Root open={open} onOpenChange={onOpenChange} modal={false}>
48
+ <DialogPrimitive.Portal container={getPortalContainer()}>
48
49
  <DialogPrimitive.Overlay className="sheet-overlay fixed inset-0 z-[85] bg-[hsl(224_18%_10%/0.3)]" />
49
50
  <DialogPrimitive.Content className="sheet-content fixed left-1/2 top-1/2 z-[90] flex max-h-[88vh] w-[calc(100vw-32px)] max-w-[560px] -translate-x-1/2 -translate-y-1/2 flex-col overflow-hidden rounded-xl border border-border bg-background">
50
51
  <div className="flex shrink-0 items-start justify-between gap-3 border-b border-border px-5 py-4">
@@ -6,6 +6,7 @@ import type { SnapshotSession } from '@shared/types'
6
6
  import { Button } from '@renderer/components/ui/button'
7
7
  import { apiUnavailableMessage, getApi, toErrorMessage } from '@renderer/lib/api'
8
8
  import { useI18n } from '@renderer/i18n'
9
+ import { getPortalContainer } from '../../../dsh/portal'
9
10
 
10
11
  interface DeleteSnapshotDialogProps {
11
12
  open: boolean
@@ -56,13 +57,14 @@ export function DeleteSnapshotDialog({
56
57
  }
57
58
 
58
59
  return (
59
- <DialogPrimitive.Root
60
+ <DialogPrimitive.Root
61
+ modal={false}
60
62
  open={open}
61
63
  onOpenChange={(next) => {
62
64
  if (!pending) onOpenChange(next)
63
65
  }}
64
66
  >
65
- <DialogPrimitive.Portal>
67
+ <DialogPrimitive.Portal container={getPortalContainer()}>
66
68
  <DialogPrimitive.Overlay className="sheet-overlay fixed inset-0 z-[70] bg-[hsl(224_18%_10%/0.3)]" />
67
69
  <DialogPrimitive.Content className="fixed left-1/2 top-1/2 z-[70] w-[min(440px,calc(100vw-2rem))] -translate-x-1/2 -translate-y-1/2 rounded-xl border border-border bg-background p-5 shadow-panel focus:outline-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring">
68
70
  <DialogPrimitive.Title className="text-[16px] font-medium tracking-tight text-foreground">
@@ -2,6 +2,7 @@ import * as DialogPrimitive from '@radix-ui/react-dialog'
2
2
  import { Loader2, Trash2 } from 'lucide-react'
3
3
  import { Button } from '@renderer/components/ui/button'
4
4
  import { useI18n } from '@renderer/i18n'
5
+ import { getPortalContainer } from '../../../dsh/portal'
5
6
 
6
7
  interface BulkDeleteDialogProps {
7
8
  open: boolean
@@ -22,8 +23,8 @@ export function BulkDeleteDialog({
22
23
  const { t } = useI18n()
23
24
 
24
25
  return (
25
- <DialogPrimitive.Root open={open} onOpenChange={onOpenChange}>
26
- <DialogPrimitive.Portal>
26
+ <DialogPrimitive.Root open={open} onOpenChange={onOpenChange} modal={false}>
27
+ <DialogPrimitive.Portal container={getPortalContainer()}>
27
28
  <DialogPrimitive.Overlay className="sheet-overlay fixed inset-0 z-[70] bg-[hsl(224_18%_10%/0.3)]" />
28
29
  <DialogPrimitive.Content className="fixed left-1/2 top-1/2 z-[70] w-[min(420px,calc(100vw-2rem))] -translate-x-1/2 -translate-y-1/2 rounded-xl border border-border bg-background p-5 shadow-panel focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring">
29
30
  <DialogPrimitive.Title className="text-[16px] font-medium tracking-tight text-foreground">
@@ -2,6 +2,7 @@ import * as DialogPrimitive from '@radix-ui/react-dialog'
2
2
  import { Loader2, Trash2 } from 'lucide-react'
3
3
  import { Button } from '@renderer/components/ui/button'
4
4
  import { useI18n } from '@renderer/i18n'
5
+ import { getPortalContainer } from '../../../dsh/portal'
5
6
 
6
7
  interface ConfirmDeleteDialogProps {
7
8
  open: boolean
@@ -22,8 +23,8 @@ export function ConfirmDeleteDialog({
22
23
  const { t } = useI18n()
23
24
 
24
25
  return (
25
- <DialogPrimitive.Root open={open} onOpenChange={onOpenChange}>
26
- <DialogPrimitive.Portal>
26
+ <DialogPrimitive.Root open={open} onOpenChange={onOpenChange} modal={false}>
27
+ <DialogPrimitive.Portal container={getPortalContainer()}>
27
28
  <DialogPrimitive.Overlay className="sheet-overlay fixed inset-0 z-[70] bg-[hsl(224_18%_10%/0.3)]" />
28
29
  <DialogPrimitive.Content className="fixed left-1/2 top-1/2 z-[70] w-[min(420px,calc(100vw-2rem))] -translate-x-1/2 -translate-y-1/2 rounded-xl border border-border bg-background p-5 shadow-panel focus:outline-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring">
29
30
  <DialogPrimitive.Title className="text-[16px] font-medium tracking-tight text-foreground">
@@ -85,6 +85,9 @@ const ROUTES = [
85
85
 
86
86
  { pattern: /^\/snapshots$/, methods: { GET: snapshots.handleList } },
87
87
  { pattern: /^\/snapshots\/([^/]+)\/archive$/, methods: { POST: snapshots.handleArchive } },
88
+ // Both spellings exist: the vendored renderer's original Web API uses
89
+ // `/restore`, while the desktop contract names it `restore-previous`.
90
+ { pattern: /^\/snapshots\/([^/]+)\/restore$/, methods: { POST: snapshots.handleRestorePrevious } },
88
91
  {
89
92
  pattern: /^\/snapshots\/([^/]+)\/restore-previous$/,
90
93
  methods: { POST: snapshots.handleRestorePrevious },
@@ -132,19 +135,21 @@ function assertWriteFence(req, method) {
132
135
  }
133
136
  const headers = (req && req.headers) || {}
134
137
  const origin = headers.origin
138
+ const fetchSite = String(headers['sec-fetch-site'] || '').trim().toLowerCase()
135
139
  if (origin !== undefined && origin !== null && String(origin).trim().length > 0) {
136
140
  if (!isLoopbackOrigin(String(origin))) {
137
- throw forbidden('FORBIDDEN_ORIGIN', 'write request origin is not allowed')
141
+ // DSH renders the plugin from the custom `dsh-app://app` origin; its
142
+ // same-origin DELETE carries `Sec-Fetch-Site: same-origin`, which is the
143
+ // signal we trust (a cross-site page would report cross-site).
144
+ if (fetchSite !== 'same-origin' && fetchSite !== 'none') {
145
+ throw forbidden('FORBIDDEN_ORIGIN', 'write request origin is not allowed')
146
+ }
138
147
  }
139
148
  }
140
- const fetchSite = headers['sec-fetch-site']
141
- if (fetchSite !== undefined && fetchSite !== null && String(fetchSite).trim().length > 0) {
142
- const site = String(fetchSite).trim().toLowerCase()
143
- if (site !== 'same-origin' && site !== 'none') {
144
- throw forbidden('FORBIDDEN_ORIGIN', 'cross-site write requests are not allowed', {
145
- header: 'Sec-Fetch-Site',
146
- })
147
- }
149
+ if (fetchSite.length > 0 && fetchSite !== 'same-origin' && fetchSite !== 'none') {
150
+ throw forbidden('FORBIDDEN_ORIGIN', 'cross-site write requests are not allowed', {
151
+ header: 'Sec-Fetch-Site',
152
+ })
148
153
  }
149
154
  }
150
155
 
package/src/host/http.js CHANGED
@@ -135,7 +135,14 @@ export function assertSameOriginWrite(req) {
135
135
  const origin = headers.origin
136
136
  if (origin !== undefined && origin !== null && String(origin).trim().length > 0) {
137
137
  if (!isLoopbackOrigin(String(origin))) {
138
- throw forbidden('FORBIDDEN_ORIGIN', 'write request origin is not allowed')
138
+ // The DSH page origin is the custom `dsh-app://app` scheme, not a
139
+ // loopback http(s) origin. Its same-origin fetch still carries
140
+ // `Sec-Fetch-Site: same-origin`; trust that rather than the scheme, so
141
+ // UI writes are not rejected while cross-site pages stay blocked.
142
+ const site = String(headers['sec-fetch-site'] || '').trim().toLowerCase()
143
+ if (site !== 'same-origin' && site !== 'none') {
144
+ throw forbidden('FORBIDDEN_ORIGIN', 'write request origin is not allowed')
145
+ }
139
146
  }
140
147
  }
141
148