@huanlin/dsh-plugin-aigc-canvas 0.1.10 → 0.2.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.
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Pure provider-id validation shared by the host half (Config / ProviderStore)
3
+ * and the client settings page. Kept free of schemastery imports so the
4
+ * client bundle (tsdown purity gate) can inline it.
5
+ *
6
+ * @module @huanlin/dsh-plugin-aigc-canvas/provider-shape
7
+ */
8
+ /** Provider id pattern: lowercase letters, digits, hyphens; must start with a letter. */
9
+ export declare const PROVIDER_ID_PATTERN: RegExp;
10
+ /**
11
+ * Validate a provider id.
12
+ * @param id - candidate id (as typed into the settings page or passed to the store).
13
+ * @returns an error message, or undefined when valid.
14
+ */
15
+ export declare function validateProviderId(id: string): string | undefined;
@@ -1,3 +1,17 @@
1
+ /**
2
+ * Provider store with CRUD, read-through the entry's profile-owned Cordis
3
+ * Config when a persistence face is attached (the list persists in the
4
+ * active profile's `cordis.patch.yml` under the `dsh-aigc-canvas` entry id,
5
+ * dsh 0.1.7-rc.1 DSH-0.1.7-J1-04). Reads derive from the persistence
6
+ * source on every call, so edits committed by ANY writer (the settings
7
+ * page through the client `configForms` transport, the model's
8
+ * aigc_provider_set_instructions tool, a hand-edited profile patch) are
9
+ * visible immediately. Without an attached face (headless assemblies with
10
+ * no settings provider) the store is in-memory only: composition seed,
11
+ * lost on unload.
12
+ *
13
+ * @module @huanlin/dsh-plugin-aigc-canvas/provider-store
14
+ */
1
15
  import type { AigcProvider, ResolvedAigcProvider } from './config.js';
2
16
  /** CRUD result: the success branch carries the latest list. */
3
17
  export type ProviderMutationResult = {
@@ -8,43 +22,64 @@ export type ProviderMutationResult = {
8
22
  readonly error: string;
9
23
  };
10
24
  /**
11
- * Mutable provider store. Owns the canonical provider list; the backend
12
- * client map and RPC handlers share one instance per plugin fiber.
25
+ * Persistence face attached to a {@link ProviderStore}.
13
26
  *
14
- * Persistence: on construction the store loads `~/.dsh/aigc-canvas/
15
- * providers.json` (if present) and merges it over the cordis.yml seed —
16
- * persisted providers win, so user edits and deletions survive restarts.
17
- * Every mutation writes the list back to disk (fire-and-forget).
27
+ * `source` reads the currently committed raw list (the entry's live volatile
28
+ * config); `persist` commits a replacement. Both are provided by
29
+ * `installAigcSettings` (see `settings.ts`).
30
+ */
31
+ export interface ProviderPersistence {
32
+ /** The currently committed raw provider list. */
33
+ source(): readonly AigcProvider[];
34
+ /** Commit a replacement list. */
35
+ persist(providers: readonly AigcProvider[]): Promise<void>;
36
+ }
37
+ /**
38
+ * Mutable provider store. Owns the canonical provider list; tool
39
+ * registration shares one instance per plugin fiber.
40
+ *
41
+ * While no persistence face is attached the in-memory map is canonical
42
+ * (headless mode). Once a face is attached, every read resolves through the
43
+ * face's source and every mutation commits through `persist` before its
44
+ * result is reported — the committed profile config is the single source of
45
+ * truth.
18
46
  */
19
47
  export declare class ProviderStore {
20
48
  private readonly providers;
21
- private readonly dataPath;
22
- /** Serializes disk writes so rapid mutations can't interleave. */
23
- private persistChain;
24
- constructor(seed: readonly AigcProvider[], dataPath?: string);
25
- /** Snapshot of all providers, in insertion order. */
49
+ /** Optional persistence face; absent in headless mode. */
50
+ private persistence;
51
+ /** @param seed - resolved seed providers (the composition layer). */
52
+ constructor(seed: readonly ResolvedAigcProvider[]);
53
+ /**
54
+ * Attach a persistence face. Subsequent reads derive from the face's
55
+ * source and mutations commit through it.
56
+ * @param persistence - the read/write face over the entry's config.
57
+ */
58
+ attachPersistence(persistence: ProviderPersistence): void;
59
+ /** The committed provider list (insertion order; duplicates keep the first). */
60
+ private committed;
61
+ /** Snapshot of all providers, in order. */
26
62
  list(): readonly ResolvedAigcProvider[];
27
63
  /** Look up one provider by id. */
28
64
  get(id: string): ResolvedAigcProvider | undefined;
29
- /** The default provider (first in insertion order); undefined if empty. */
65
+ /** The default provider (first in order); undefined if empty. */
30
66
  defaultProvider(): ResolvedAigcProvider | undefined;
31
67
  /** Add a new provider. Returns failure for duplicate id or invalid shape. */
32
- add(provider: AigcProvider): ProviderMutationResult;
68
+ add(provider: AigcProvider): Promise<ProviderMutationResult>;
33
69
  /** Update an existing provider. Returns failure if the id is unknown. */
34
- update(provider: AigcProvider): ProviderMutationResult;
70
+ update(provider: AigcProvider): Promise<ProviderMutationResult>;
35
71
  /**
36
72
  * Replace a provider's usage instructions (called by the model's
37
73
  * aigc_provider_set_instructions tool after it probes the API).
38
74
  */
39
- setInstructions(id: string, instructions: string): ProviderMutationResult;
75
+ setInstructions(id: string, instructions: string): Promise<ProviderMutationResult>;
40
76
  /** Remove a provider. Returns failure for unknown id. */
41
- remove(id: string): ProviderMutationResult;
77
+ remove(id: string): Promise<ProviderMutationResult>;
42
78
  /**
43
- * Persist the current provider list to disk (fire-and-forget, serialized).
44
- * Only the user-editable fields are written; `builtin` is re-derived from
45
- * the seed on load. Failures are swallowed — the in-memory state stays
46
- * canonical. Each call snapshots the CURRENT list, so a burst of mutations
47
- * ends with the latest state on disk.
79
+ * Commit a replacement list: through the persistence face when attached
80
+ * (the post-commit committed value is reported), otherwise into the
81
+ * in-memory map. A refused persistence write leaves the committed state
82
+ * untouched and reports `{ ok: false }`.
48
83
  */
49
- private persist;
84
+ private commit;
50
85
  }
@@ -0,0 +1,78 @@
1
+ /**
2
+ * settings.ts — host-side bridge between the entry's profile-owned Config
3
+ * and the plugin's provider store, plus the one-time legacy import.
4
+ *
5
+ * dsh 0.1.7-rc.1 (`DSH-0.1.7-J1-04`) removed the namespace-registration API:
6
+ * a plugin declares a Cordis `Config` (see `config.ts`, `providers` marked
7
+ * `.volatile()`) and only declares the presentation policy for its own page.
8
+ * The bridge exposes:
9
+ *
10
+ * - `source()`: the raw committed provider list, read from the live
11
+ * volatile reference on every call, so the tools and RPC surface always
12
+ * serve the latest accepted value (including edits written by other
13
+ * surfaces through the settings service).
14
+ * - `persist(providers)`: commits a replacement list through the settings
15
+ * service, which writes it into the active profile's `cordis.patch.yml`
16
+ * under the entry id `dsh-aigc-canvas` and updates the live reference
17
+ * in place.
18
+ * - `writable`: whether a settings provider is mounted.
19
+ *
20
+ * On first mount the legacy persistence file of pre-0.1.11 versions
21
+ * (`~/.dsh/aigc-canvas/providers.json`) is imported once (the file is
22
+ * renamed before the import, so a partial import never repeats) and left
23
+ * behind as `providers.json.imported`.
24
+ *
25
+ * @module @huanlin/dsh-plugin-aigc-canvas/settings
26
+ */
27
+ import type { AigcEntryConfig, AigcProvider } from './config.js';
28
+ import type { AigcSettingsForms, Context } from './context-types.js';
29
+ /** Profile entry id under which the provider list persists (`cordis.patch.yml`). */
30
+ export declare const SETTINGS_NAMESPACE = "dsh-aigc-canvas";
31
+ /** Default legacy persistence path (pre-0.1.11 custom file). */
32
+ export declare const LEGACY_PROVIDERS_JSON: string;
33
+ /** Minimal logger face (cordis `ctx.logger`). */
34
+ interface AigcLogger {
35
+ info(...args: unknown[]): void;
36
+ warn(...args: unknown[]): void;
37
+ }
38
+ /** Read/write face the provider store consumes. */
39
+ export interface AigcSettingsBridge {
40
+ /** The raw committed provider list, read from the entry's live volatile config. */
41
+ source(): readonly AigcProvider[];
42
+ /**
43
+ * Commit a replacement provider list to the profile entry config.
44
+ * No-op without a mounted settings provider (headless assemblies).
45
+ */
46
+ persist(providers: readonly AigcProvider[]): Promise<void>;
47
+ /** Whether a settings provider is mounted (false in headless assemblies). */
48
+ readonly writable: boolean;
49
+ }
50
+ /**
51
+ * Declare the plugin's settings presentation policy and return the bridge.
52
+ *
53
+ * `auto: false` suppresses the schema-generated page: this plugin ships its
54
+ * own editor as the bundle row's `plugins.row.config` entry on the Plugins
55
+ * page.
56
+ *
57
+ * @param ctx - host context.
58
+ * @param entry - the entry's volatile Cordis config.
59
+ * @param options - optional overrides (legacy import path, attach callback).
60
+ * @param options.legacyPath - path of the legacy providers file to import once.
61
+ * @param options.onReady - invoked when the settings provider is mounted (before the legacy import settles).
62
+ * @returns the bridge the provider store consumes.
63
+ */
64
+ export declare function installAigcSettings(ctx: Context, entry: AigcEntryConfig | undefined, options?: {
65
+ legacyPath?: string;
66
+ onReady?: () => void;
67
+ }): AigcSettingsBridge;
68
+ /**
69
+ * Import the legacy `providers.json` once: rename the file first (so a
70
+ * partial import never repeats), then commit the parsed list into the
71
+ * profile entry config. Failures are logged, never thrown.
72
+ *
73
+ * @param path - the legacy file path.
74
+ * @param settings - the mounted settings service.
75
+ * @param logger - the host logger.
76
+ */
77
+ export declare function importLegacyProviders(path: string, settings: AigcSettingsForms, logger: AigcLogger): Promise<void>;
78
+ export {};
package/lib/tools.d.ts CHANGED
@@ -31,7 +31,7 @@ export interface ProviderInfo {
31
31
  *
32
32
  * @param ctx - host plugin context (carries the tools service).
33
33
  * @param getProvider - live provider getter (takes optional provider id).
34
- * @param setInstructions - persists usage instructions for one provider (the host's ProviderStore).
34
+ * @param setInstructions - persists usage instructions for one provider (the host's ProviderStore; resolves after the profile-config commit).
35
35
  * @param listProviders - returns info for all providers (for aigc_get_provider_info).
36
36
  * @param canvas - the canvas registry service (host-owned state).
37
37
  * @param resolveCwd - live cwd resolver for one session id.
@@ -39,9 +39,9 @@ export interface ProviderInfo {
39
39
  * @param getMediaLimit - live cap on bytes the http tool may write to disk.
40
40
  * @returns a disposer that unregisters all tools.
41
41
  */
42
- export declare function registerTools(ctx: Context, getProvider: (providerId?: string) => ResolvedAigcProvider, setInstructions: (id: string, instructions: string) => {
42
+ export declare function registerTools(ctx: Context, getProvider: (providerId?: string) => ResolvedAigcProvider, setInstructions: (id: string, instructions: string) => Promise<{
43
43
  ok: boolean;
44
44
  error?: string;
45
- }, listProviders: () => readonly ProviderInfo[], canvas: AigcCanvasService, resolveCwd: (sessionId: string) => string, getTimeoutMs: () => number, getMediaLimit?: () => number): () => void;
45
+ }>, listProviders: () => readonly ProviderInfo[], canvas: AigcCanvasService, resolveCwd: (sessionId: string) => string, getTimeoutMs: () => number, getMediaLimit?: () => number): () => void;
46
46
  /** Re-export the projection helpers for the unit tests. */
47
47
  export { elementProjection, edgeProjection, titleOf };
package/locale/en.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "AIGC Canvas",
4
+ "description": "An infinite free canvas for better-sidebar plus provider-agnostic generation: the agent calls any configured provider via aigc_http_request (endpoint + apiKey attached automatically) and places generated images, videos, and audio at arbitrary canvas positions; double-click an element to inspect its prompt and params."
5
+ }
6
+ }
package/locale/zh.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "AIGC 画布",
4
+ "description": "面向 better-sidebar 的无限自由画布,搭配供应商无关的生成:模型通过 aigc_http_request 调用任意已配置供应商(endpoint + apiKey 自动附加),生成的图片/视频/音频可放置在画布任意位置,双击元素即可查看提示词与生成参数。"
5
+ }
6
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@huanlin/dsh-plugin-aigc-canvas",
3
- "version": "0.1.10",
3
+ "version": "0.2.1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -22,6 +22,7 @@
22
22
  "types": "./lib/types/client/service.d.ts",
23
23
  "default": "./lib/client.js"
24
24
  },
25
+ "./locale/*.json": "./locale/*.json",
25
26
  "./package.json": "./package.json"
26
27
  },
27
28
  "engines": {
@@ -30,6 +31,7 @@
30
31
  "files": [
31
32
  "lib/",
32
33
  "assets/",
34
+ "locale/",
33
35
  "cordis.patch.yml"
34
36
  ],
35
37
  "dsh": {
@@ -43,6 +45,7 @@
43
45
  "@deepseek-ai/dsh-client-ui-renderer",
44
46
  "@deepseek-ai/dsh-client-ui-slots",
45
47
  "@deepseek-ai/dsh-client-ui-settings",
48
+ "@deepseek-ai/dsh-client-ui-plugin-manager",
46
49
  "@deepseek-ai/dsh-client-ui-primitives"
47
50
  ],
48
51
  "platform": "web"
@@ -54,15 +57,16 @@
54
57
  },
55
58
  "peerDependencies": {
56
59
  "@deepseek-ai/cordis": "^4.0.1",
57
- "@deepseek-ai/dsh-client-locale": "^0.1.7-rc.1",
58
- "@deepseek-ai/dsh-client-ui-primitives": "^0.1.7-rc.1",
59
- "@deepseek-ai/dsh-client-ui-settings": "^0.1.7-rc.1",
60
- "@deepseek-ai/dsh-client-ui-conversation": "^0.1.7-rc.1",
61
- "@deepseek-ai/dsh-client-ui-renderer": "^0.1.7-rc.1",
62
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.7-rc.1",
63
- "@deepseek-ai/dsh-host-webserver": "^0.1.7-rc.1",
64
- "@deepseek-ai/dsh-session": "^0.1.7-rc.1",
65
- "@deepseek-ai/dsh-tools": "^0.1.7-rc.1",
60
+ "@deepseek-ai/dsh-client-locale": "^0.2.0-rc.1",
61
+ "@deepseek-ai/dsh-client-ui-primitives": "^0.2.0-rc.1",
62
+ "@deepseek-ai/dsh-client-ui-settings": "^0.2.0-rc.1",
63
+ "@deepseek-ai/dsh-client-ui-plugin-manager": "^0.2.0-rc.1",
64
+ "@deepseek-ai/dsh-client-ui-conversation": "^0.2.0-rc.1",
65
+ "@deepseek-ai/dsh-client-ui-renderer": "^0.2.0-rc.1",
66
+ "@deepseek-ai/dsh-client-ui-slots": "^0.2.0-rc.1",
67
+ "@deepseek-ai/dsh-host-webserver": "^0.2.0-rc.1",
68
+ "@deepseek-ai/dsh-session": "^0.2.0-rc.1",
69
+ "@deepseek-ai/dsh-tools": "^0.2.0-rc.1",
66
70
  "cordis": "^4.0.0-rc.7",
67
71
  "dsh-better-sidebar": "^0.4.0",
68
72
  "react": "^18.2.0",
@@ -81,6 +85,9 @@
81
85
  "@deepseek-ai/dsh-client-ui-settings": {
82
86
  "optional": true
83
87
  },
88
+ "@deepseek-ai/dsh-client-ui-plugin-manager": {
89
+ "optional": true
90
+ },
84
91
  "@deepseek-ai/dsh-client-ui-conversation": {
85
92
  "optional": true
86
93
  },
@@ -117,6 +124,7 @@
117
124
  "@deepseek-ai/dsh-client-locale": "link:D:/Projects/deepseek-harness/dsh/packages/client/locale",
118
125
  "@deepseek-ai/dsh-client-ui-primitives": "link:D:/Projects/deepseek-harness/dsh/packages/client/ui-primitives",
119
126
  "@deepseek-ai/dsh-client-ui-settings": "link:D:/Projects/deepseek-harness/dsh/packages/client/ui-settings",
127
+ "@deepseek-ai/dsh-client-ui-plugin-manager": "link:D:/Projects/deepseek-harness/dsh/packages/client/ui-plugin-manager",
120
128
  "@deepseek-ai/dsh-client-ui-conversation": "link:D:/Projects/deepseek-harness/dsh/packages/client/ui-conversation",
121
129
  "@deepseek-ai/dsh-client-ui-renderer": "link:D:/Projects/deepseek-harness/dsh/packages/client/ui-renderer",
122
130
  "@deepseek-ai/dsh-client-ui-slots": "link:D:/Projects/deepseek-harness/dsh/packages/client/ui-slots",