mirror of
https://github.com/hansjone/netxops.git
synced 2026-10-15 13:23:22 +08:00
Stop requiring dsh-client-store in the Plugins card bundle.
Shipped dsh web seed has no client-store entry; use a private snapshot store and only externalize platform seed modules. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
e45657b1ee
commit
69a4820369
6 changed files with 78 additions and 25 deletions
|
|
@ -333,8 +333,27 @@ function NetxopsCard(props) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// src/client/snapshot-store.ts
|
||||||
|
function createSnapshotStore(init) {
|
||||||
|
let state = init;
|
||||||
|
const listeners = new Set;
|
||||||
|
return {
|
||||||
|
getSnapshot: () => state,
|
||||||
|
subscribe: (listener) => {
|
||||||
|
listeners.add(listener);
|
||||||
|
return () => {
|
||||||
|
listeners.delete(listener);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
set: (next) => {
|
||||||
|
state = next;
|
||||||
|
for (const listener of [...listeners])
|
||||||
|
listener();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// src/client/card-form.ts
|
// src/client/card-form.ts
|
||||||
var import_dsh_client_store = require("@deepseek-ai/dsh-client-store");
|
|
||||||
function textField(field) {
|
function textField(field) {
|
||||||
return {
|
return {
|
||||||
field,
|
field,
|
||||||
|
|
@ -363,7 +382,7 @@ class CardForm {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
bind(project) {
|
bind(project) {
|
||||||
const store = import_dsh_client_store.createSnapshotStore(project());
|
const store = createSnapshotStore(project());
|
||||||
this.listeners.add(() => {
|
this.listeners.add(() => {
|
||||||
store.set(project());
|
store.set(project());
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "dsh-netxops",
|
"name": "dsh-netxops",
|
||||||
"version": "0.1.6",
|
"version": "0.1.7",
|
||||||
"description": "DeepSeek Harness Netx Ops: host bridge + Plugins settings card + agent preset",
|
"description": "DeepSeek Harness Netx Ops: host bridge + Plugins settings card + agent preset",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|
@ -70,7 +70,6 @@
|
||||||
"@deepseek-ai/dsh-tool-fs": "*",
|
"@deepseek-ai/dsh-tool-fs": "*",
|
||||||
"@deepseek-ai/dsh-tool-jobs": "*",
|
"@deepseek-ai/dsh-tool-jobs": "*",
|
||||||
"@deepseek-ai/dsh-agent-instructions": "*",
|
"@deepseek-ai/dsh-agent-instructions": "*",
|
||||||
"@deepseek-ai/dsh-client-store": "*",
|
|
||||||
"@deepseek-ai/dsh-client-locale": "*",
|
"@deepseek-ai/dsh-client-locale": "*",
|
||||||
"@deepseek-ai/dsh-client-ui-settings": "*",
|
"@deepseek-ai/dsh-client-ui-settings": "*",
|
||||||
"@deepseek-ai/dsh-client-ui-settings-plugins": "*",
|
"@deepseek-ai/dsh-client-ui-settings-plugins": "*",
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,12 @@
|
||||||
* Build lib/client.js as a DSH ModuleLoader factory artifact using Bun's bundler.
|
* Build lib/client.js as a DSH ModuleLoader factory artifact using Bun's bundler.
|
||||||
* Usage: bun run scripts/build-client.mjs
|
* Usage: bun run scripts/build-client.mjs
|
||||||
*
|
*
|
||||||
* Must emit production JSX (`react/jsx-runtime`). The web shell seeds only that
|
* Externals must match the *installed* web shell seed table. Shipped
|
||||||
* specifier — `react/jsx-dev-runtime` is not in PLATFORM_MODULES and fails load.
|
* `@deepseek-ai/dsh` 0.1.1-rc.2 seeds only:
|
||||||
|
* react, react/jsx-runtime, react-dom, react-dom/client,
|
||||||
|
* @deepseek-ai/cordis, dsh-client-ui-slots, dsh-client-ui-primitives
|
||||||
|
* Do not leave other @deepseek-ai packages as require() targets unless they
|
||||||
|
* are also listed in dsh.client.inject and arrive as graph factories.
|
||||||
*/
|
*/
|
||||||
import { mkdirSync, writeFileSync } from 'node:fs'
|
import { mkdirSync, writeFileSync } from 'node:fs'
|
||||||
import { dirname, join } from 'node:path'
|
import { dirname, join } from 'node:path'
|
||||||
|
|
@ -14,28 +18,15 @@ const entry = join(root, 'src/client/index.ts')
|
||||||
const outFile = join(root, 'lib', 'client.js')
|
const outFile = join(root, 'lib', 'client.js')
|
||||||
const id = 'dsh-netxops'
|
const id = 'dsh-netxops'
|
||||||
|
|
||||||
/** Align with @deepseek-ai/dsh-client-web PLATFORM_MODULES + dsh.client.inject. */
|
/** Exact PLATFORM_MODULES keys from shipped dsh-web-frontend seed. */
|
||||||
const external = [
|
const external = [
|
||||||
'react',
|
'react',
|
||||||
'react/jsx-runtime',
|
'react/jsx-runtime',
|
||||||
'react-dom',
|
'react-dom',
|
||||||
'react-dom/client',
|
'react-dom/client',
|
||||||
'@deepseek-ai/cordis',
|
'@deepseek-ai/cordis',
|
||||||
'@deepseek-ai/dsh-client-store',
|
|
||||||
'@deepseek-ai/dsh-client-locale',
|
|
||||||
'@deepseek-ai/dsh-client-locale/client',
|
|
||||||
'@deepseek-ai/dsh-client-ui-settings',
|
|
||||||
'@deepseek-ai/dsh-client-ui-settings/client',
|
|
||||||
'@deepseek-ai/dsh-client-ui-settings-plugins',
|
|
||||||
'@deepseek-ai/dsh-client-ui-settings-plugins/client',
|
|
||||||
'@deepseek-ai/dsh-client-ui-slots',
|
'@deepseek-ai/dsh-client-ui-slots',
|
||||||
'@deepseek-ai/dsh-client-ui-renderer',
|
|
||||||
'@deepseek-ai/dsh-client-ui-renderer/client',
|
|
||||||
'@deepseek-ai/dsh-client-ui-primitives',
|
'@deepseek-ai/dsh-client-ui-primitives',
|
||||||
'@deepseek-ai/dsh-api-remotes',
|
|
||||||
'@deepseek-ai/dsh-api-remotes/client',
|
|
||||||
'@deepseek-ai/dsh-client-connection',
|
|
||||||
'@deepseek-ai/dsh-client-connection/client',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
const result = await Bun.build({
|
const result = await Bun.build({
|
||||||
|
|
@ -44,7 +35,6 @@ const result = await Bun.build({
|
||||||
format: 'cjs',
|
format: 'cjs',
|
||||||
sourcemap: 'none',
|
sourcemap: 'none',
|
||||||
minify: false,
|
minify: false,
|
||||||
// Force production JSX transform (jsx-runtime, not jsx-dev-runtime).
|
|
||||||
define: {
|
define: {
|
||||||
'process.env.NODE_ENV': JSON.stringify('production'),
|
'process.env.NODE_ENV': JSON.stringify('production'),
|
||||||
},
|
},
|
||||||
|
|
@ -56,10 +46,21 @@ if (!result.success) {
|
||||||
throw new Error('Bun.build failed')
|
throw new Error('Bun.build failed')
|
||||||
}
|
}
|
||||||
|
|
||||||
let raw = await result.outputs[0].text()
|
const raw = await result.outputs[0].text()
|
||||||
if (raw.includes('react/jsx-dev-runtime')) {
|
if (raw.includes('react/jsx-dev-runtime')) {
|
||||||
throw new Error('client bundle still references react/jsx-dev-runtime; refuse to ship')
|
throw new Error('client bundle still references react/jsx-dev-runtime; refuse to ship')
|
||||||
}
|
}
|
||||||
|
if (raw.includes('@deepseek-ai/dsh-client-store')) {
|
||||||
|
throw new Error('client bundle still requires dsh-client-store; refuse to ship')
|
||||||
|
}
|
||||||
|
|
||||||
|
const requires = [...raw.matchAll(/require\("([^"]+)"\)/g)].map(m => m[1])
|
||||||
|
const allowed = new Set(external)
|
||||||
|
for (const spec of requires) {
|
||||||
|
if (!allowed.has(spec)) {
|
||||||
|
throw new Error(`client bundle requires undeclared external "${spec}"; refuse to ship`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const body = raw
|
const body = raw
|
||||||
.replace(/^"use strict";\s*/m, '')
|
.replace(/^"use strict";\s*/m, '')
|
||||||
|
|
@ -79,4 +80,4 @@ ${body}
|
||||||
|
|
||||||
mkdirSync(dirname(outFile), { recursive: true })
|
mkdirSync(dirname(outFile), { recursive: true })
|
||||||
writeFileSync(outFile, artifact, 'utf8')
|
writeFileSync(outFile, artifact, 'utf8')
|
||||||
console.log(`wrote ${outFile} (${artifact.length} bytes)`)
|
console.log(`wrote ${outFile} (${artifact.length} bytes); requires: ${[...new Set(requires)].join(', ') || '(none)'}`)
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@
|
||||||
* Self-contained (cannot value-import ui-settings-plugins — client bundle purity).
|
* Self-contained (cannot value-import ui-settings-plugins — client bundle purity).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { createSnapshotStore, type SnapshotStore } from '@deepseek-ai/dsh-client-store'
|
|
||||||
import type { SettingsScope, SettingsScopeSnapshot } from '@deepseek-ai/dsh-client-ui-settings/client'
|
import type { SettingsScope, SettingsScopeSnapshot } from '@deepseek-ai/dsh-client-ui-settings/client'
|
||||||
|
import { createSnapshotStore, type SnapshotStore } from './snapshot-store.ts'
|
||||||
|
|
||||||
export type FieldWrite = { kind: 'set'; value: unknown } | { kind: 'clear' }
|
export type FieldWrite = { kind: 'set'; value: unknown } | { kind: 'clear' }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@
|
||||||
|
|
||||||
import type { Context as ClientContext } from '@deepseek-ai/cordis'
|
import type { Context as ClientContext } from '@deepseek-ai/cordis'
|
||||||
import type {} from '@deepseek-ai/dsh-api-remotes/client'
|
import type {} from '@deepseek-ai/dsh-api-remotes/client'
|
||||||
import type { SnapshotStore } from '@deepseek-ai/dsh-client-store'
|
|
||||||
import type { SettingsScope, SettingsScopeSnapshot } from '@deepseek-ai/dsh-client-ui-settings/client'
|
import type { SettingsScope, SettingsScopeSnapshot } from '@deepseek-ai/dsh-client-ui-settings/client'
|
||||||
|
import type { SnapshotStore } from './snapshot-store.ts'
|
||||||
import {
|
import {
|
||||||
CardForm, textField,
|
CardForm, textField,
|
||||||
type CardActions, type CardFieldState, type CardShell,
|
type CardActions, type CardFieldState, type CardShell,
|
||||||
|
|
|
||||||
34
src/client/snapshot-store.ts
Normal file
34
src/client/snapshot-store.ts
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
/**
|
||||||
|
* Minimal snapshot store for the Plugins card.
|
||||||
|
*
|
||||||
|
* Shipped DSH web (`@deepseek-ai/dsh` ≤0.1.1-rc.2) seeds only react / cordis /
|
||||||
|
* ui-slots / ui-primitives — not `@deepseek-ai/dsh-client-store`. Keep this
|
||||||
|
* private so the client bundle does not require that package.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface SnapshotStore<T> {
|
||||||
|
getSnapshot: () => T
|
||||||
|
subscribe: (listener: () => void) => () => void
|
||||||
|
set: (next: T) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a synchronous snapshot store (same-tick notify for controlled inputs).
|
||||||
|
* @param init - initial snapshot.
|
||||||
|
* @returns the store.
|
||||||
|
*/
|
||||||
|
export function createSnapshotStore<T>(init: T): SnapshotStore<T> {
|
||||||
|
let state = init
|
||||||
|
const listeners = new Set<() => void>()
|
||||||
|
return {
|
||||||
|
getSnapshot: () => state,
|
||||||
|
subscribe: (listener) => {
|
||||||
|
listeners.add(listener)
|
||||||
|
return () => { listeners.delete(listener) }
|
||||||
|
},
|
||||||
|
set: (next) => {
|
||||||
|
state = next
|
||||||
|
for (const listener of [...listeners]) listener()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue