dsh-image-gen 0.1.6 → 0.2.0

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.
@@ -15,6 +15,8 @@ interface ImageSettings {
15
15
  seedreamModel?: string;
16
16
  dashscopeEndpoint?: string;
17
17
  dashscopeModel?: string;
18
+ saveToWorkspace?: boolean;
19
+ workspaceFolder?: string;
18
20
  }
19
21
  interface SettingsFace {
20
22
  scope: SettingsScope<ImageSettings>;
@@ -2,6 +2,8 @@
2
2
  import z from '@deepseek-ai/schemastery';
3
3
  import { DEFAULT_DASHSCOPE_ENDPOINT, DEFAULT_DASHSCOPE_MODEL, DEFAULT_GOOGLE_ENDPOINT, DEFAULT_GOOGLE_MODEL, DEFAULT_OPENAI_BASE_URL, DEFAULT_OPENAI_MODEL, DEFAULT_SEEDREAM_BASE_URL, DEFAULT_SEEDREAM_MODEL, IMAGE_PROVIDERS, type ImageProvider } from './shared.js';
4
4
  export { DEFAULT_DASHSCOPE_ENDPOINT, DEFAULT_DASHSCOPE_MODEL, DEFAULT_GOOGLE_ENDPOINT, DEFAULT_GOOGLE_MODEL, DEFAULT_OPENAI_BASE_URL, DEFAULT_OPENAI_MODEL, DEFAULT_SEEDREAM_BASE_URL, DEFAULT_SEEDREAM_MODEL, IMAGE_PROVIDERS, type ImageProvider, };
5
+ /** Default workspace subfolder that receives generated image files. */
6
+ export declare const DEFAULT_WORKSPACE_FOLDER = "dsh-image-gen";
5
7
  /** Google API credential reference. */
6
8
  export declare const GOOGLE_API_KEY_ENV = "GEMINI_API_KEY";
7
9
  /** OpenAI Platform or compatible relay credential reference. */
@@ -26,6 +28,10 @@ export interface Config {
26
28
  seedreamModel?: string;
27
29
  dashscopeEndpoint?: string;
28
30
  dashscopeModel?: string;
31
+ /** Also write every generated image as a file under the session workspace. */
32
+ saveToWorkspace?: boolean;
33
+ /** Workspace subfolder for generated images; empty means the workspace root. */
34
+ workspaceFolder?: string;
29
35
  }
30
36
  /** Cordis configuration schema. */
31
37
  export declare const Config: z<Config>;
@@ -1,5 +1,5 @@
1
- /** DashScope (Aliyun Wanx & Qwen-Image) image-generation protocol adapter. */
2
- import type { ImageAttachmentRef } from '@deepseek-ai/dsh-attachment';
1
+ /** DashScope Qwen Image generation and editing adapter. */
2
+ import type { ImageAttachmentRef, ImageMediaType } from '@deepseek-ai/dsh-attachment';
3
3
  export interface DashScopeImageOptions {
4
4
  apiKey: string;
5
5
  endpoint: string;
@@ -9,8 +9,17 @@ export interface DashScopeImageOptions {
9
9
  maxBytes: number;
10
10
  signal?: AbortSignal;
11
11
  }
12
- /** Call DashScope image synthesis/generation API and handle both synchronous output & async task polling. */
12
+ export interface DashScopeEditOptions extends DashScopeImageOptions {
13
+ sourceImage: {
14
+ data: Uint8Array;
15
+ mediaType: ImageMediaType;
16
+ };
17
+ }
13
18
  export declare function generateDashScopeImage(options: DashScopeImageOptions): Promise<{
14
19
  data: Uint8Array;
15
20
  mediaType: ImageAttachmentRef['mediaType'];
16
21
  }>;
22
+ export declare function editDashScopeImage(options: DashScopeEditOptions): Promise<{
23
+ data: Uint8Array;
24
+ mediaType: ImageAttachmentRef['mediaType'];
25
+ }>;
@@ -6,14 +6,25 @@ export interface GeneratedImage {
6
6
  data: Uint8Array;
7
7
  mediaType: ImageMediaType;
8
8
  }
9
- /** Send one native Google image request and validate its inline image response. */
10
- export declare function generateGoogleImage(input: {
9
+ interface GoogleRequestBase {
11
10
  apiKey: string;
12
11
  endpoint: string;
13
12
  model: string;
14
- prompt: string;
15
13
  aspectRatio: AspectRatio;
16
14
  imageSize: ImageSize;
17
15
  maxBytes: number;
18
16
  signal: AbortSignal;
17
+ }
18
+ /** Send one native Google text-to-image request. */
19
+ export declare function generateGoogleImage(input: GoogleRequestBase & {
20
+ prompt: string;
21
+ }): Promise<GeneratedImage>;
22
+ /** Send one native Google image-editing request using already-resolved bytes. */
23
+ export declare function editGoogleImage(input: GoogleRequestBase & {
24
+ prompt: string;
25
+ sourceImage: {
26
+ data: Uint8Array;
27
+ mediaType: ImageMediaType;
28
+ };
19
29
  }): Promise<GeneratedImage>;
30
+ export {};
@@ -3,9 +3,6 @@ import type { Context } from '@deepseek-ai/cordis';
3
3
  import { Config } from './config.js';
4
4
  export { Config } from './config.js';
5
5
  export { IMAGE_ROUTE, imageAttachmentFromMeta } from './image-route.js';
6
- /** Cordis plugin name. */
7
6
  export declare const name = "dsh-image-gen";
8
- /** Host services required by the Bundle. */
9
7
  export declare const inject: string[];
10
- /** Register settings, the image route, and the model-callable tool. */
11
8
  export declare function apply(ctx: Context, config?: Config): void;
@@ -1,11 +1,13 @@
1
- /** OpenAI Images API and Volcengine Ark/Seedream response adapter. */
1
+ /** OpenAI Images API and compatible response adapter. */
2
2
  import type { ImageMediaType } from '@deepseek-ai/dsh-attachment';
3
- /** One generated raster normalized before Attachment persistence. */
4
3
  export interface GeneratedCompatibleImage {
5
4
  data: Uint8Array;
6
5
  mediaType: ImageMediaType;
7
6
  }
8
- /** Generate an image from an OpenAI-compatible endpoint. */
7
+ export interface CompatibleReferenceImage {
8
+ data: Uint8Array;
9
+ mediaType: ImageMediaType;
10
+ }
9
11
  export declare function generateOpenAICompatibleImage(input: {
10
12
  provider: 'openai' | 'seedream';
11
13
  apiKey: string;
@@ -16,3 +18,13 @@ export declare function generateOpenAICompatibleImage(input: {
16
18
  maxBytes: number;
17
19
  signal: AbortSignal;
18
20
  }): Promise<GeneratedCompatibleImage>;
21
+ export declare function editOpenAICompatibleImage(input: {
22
+ apiKey: string;
23
+ baseURL: string;
24
+ model: string;
25
+ prompt: string;
26
+ sourceImage: CompatibleReferenceImage;
27
+ size?: string;
28
+ maxBytes: number;
29
+ signal: AbortSignal;
30
+ }): Promise<GeneratedCompatibleImage>;
@@ -0,0 +1,38 @@
1
+ import type { ImageAttachmentRef, ImageMediaType, StoredImageAttachment } from '@deepseek-ai/dsh-attachment';
2
+ import type { Message } from '@deepseek-ai/dsh-llm';
3
+ /** Provider-neutral image bytes passed across the DSH/provider boundary. */
4
+ export interface ResolvedReferenceImage {
5
+ data: Uint8Array;
6
+ mediaType: ImageMediaType;
7
+ }
8
+ /** The small latest-DSH session surface this Bundle depends on. */
9
+ export interface ReferenceImageAgent {
10
+ session: {
11
+ deriveMessages(): readonly Message[];
12
+ header?: {
13
+ cwd?: string;
14
+ };
15
+ };
16
+ }
17
+ /** The small latest-DSH attachment surface this Bundle depends on. */
18
+ export interface ReferenceImageStore {
19
+ readImage(ref: ImageAttachmentRef, signal?: AbortSignal): Promise<StoredImageAttachment>;
20
+ }
21
+ /**
22
+ * Resolve and read the image to edit from the current effective conversation.
23
+ *
24
+ * This is the only module allowed to know how DSH sessions expose effective
25
+ * messages or how nested model-facing content stores durable image refs.
26
+ */
27
+ export declare function resolveReferenceImage(input: {
28
+ agent?: ReferenceImageAgent;
29
+ attachments: ReferenceImageStore;
30
+ sourceAttachmentId?: string;
31
+ sourcePath?: string;
32
+ maxBytes?: number;
33
+ signal: AbortSignal;
34
+ }): Promise<ResolvedReferenceImage>;
35
+ /** Find the newest matching image in the effective, replacement-aware history. */
36
+ export declare function findReferenceImage(messages: readonly Message[], sourceAttachmentId?: string): ImageAttachmentRef | undefined;
37
+ /** Validate an untrusted serialized image reference at an HTTP/UI boundary. */
38
+ export declare function parseImageAttachmentRef(value: unknown): ImageAttachmentRef | undefined;
@@ -0,0 +1,17 @@
1
+ /** Volcengine Ark Seedream image-editing adapter. */
2
+ import type { ImageMediaType } from '@deepseek-ai/dsh-attachment';
3
+ import type { GeneratedCompatibleImage } from './openai-compatible.js';
4
+ /** Edit one image through Ark ImageGenerations using a data-URL reference. */
5
+ export declare function editSeedreamImage(input: {
6
+ apiKey: string;
7
+ baseURL: string;
8
+ model: string;
9
+ prompt: string;
10
+ sourceImage: {
11
+ data: Uint8Array;
12
+ mediaType: ImageMediaType;
13
+ };
14
+ size?: string;
15
+ maxBytes: number;
16
+ signal: AbortSignal;
17
+ }): Promise<GeneratedCompatibleImage>;
@@ -15,6 +15,6 @@ export declare const DEFAULT_DASHSCOPE_ENDPOINT = "https://dashscope.aliyuncs.co
15
15
  export declare const DEFAULT_GOOGLE_MODEL = "gemini-3.1-flash-image";
16
16
  export declare const DEFAULT_OPENAI_MODEL = "gpt-image-2";
17
17
  export declare const DEFAULT_SEEDREAM_MODEL = "doubao-seedream-5-0-260128";
18
- export declare const DEFAULT_DASHSCOPE_MODEL = "wanx2.1-t2i-turbo";
18
+ export declare const DEFAULT_DASHSCOPE_MODEL = "qwen-image-3.0";
19
19
  export declare const DEFAULT_MODELS: Record<ImageProvider, string>;
20
20
  export declare const DEFAULT_BASE_URLS: Record<ImageProvider, string>;
@@ -0,0 +1,52 @@
1
+ import type { ImageAttachmentRef } from '@deepseek-ai/dsh-attachment';
2
+ /**
3
+ * Build the deterministic file name for a generated image:
4
+ * `image-<digest-prefix>.<ext>`. The digest prefix comes from the
5
+ * content-addressed attachment id, so the same image bytes always map to the
6
+ * same file name regardless of when they were generated, and re-saving simply
7
+ * overwrites the previous copy in place.
8
+ * @param attachmentId - durable attachment id (`sha256:<hex>`).
9
+ * @param mediaType - verified image media type.
10
+ * @returns the file name (no directory).
11
+ */
12
+ export declare function workspaceImageName(attachmentId: string, mediaType: ImageAttachmentRef['mediaType']): string;
13
+ /**
14
+ * Resolve the configured image folder inside the session workspace. The
15
+ * folder may nest, but must stay inside the workspace: absolute paths and
16
+ * parent-traversal segments are rejected for both separator styles.
17
+ *
18
+ * This lexical pass is necessary but not sufficient: `saveImageToWorkspace`
19
+ * additionally verifies the on-disk resolution so symlinked folders cannot
20
+ * escape the workspace.
21
+ * @param workspaceRoot - the session workspace directory.
22
+ * @param folder - configured subfolder; empty/blank means the workspace root.
23
+ * @returns the absolute image directory.
24
+ * @throws when the folder would escape the workspace root.
25
+ */
26
+ export declare function workspaceImageDir(workspaceRoot: string, folder: string | undefined): string;
27
+ /**
28
+ * Write one generated image durably under the session workspace.
29
+ *
30
+ * Containment is enforced twice: lexically by `workspaceImageDir`, then
31
+ * against real paths, so a configured folder (or any intermediate segment)
32
+ * that is a symlink pointing outside the workspace is rejected before and
33
+ * after anything is created.
34
+ *
35
+ * The bytes are written to a same-directory staging file and renamed onto the
36
+ * target, so a crash never leaves a half-written image under its final name.
37
+ * Re-saving identical bytes rewrites the same file (the name is content-
38
+ * addressed), which keeps repeated generations idempotent. A cancellation is
39
+ * honoured up to and including the final rename: an aborted save never
40
+ * resolves successfully and never leaves the image behind under its final
41
+ * name.
42
+ * @param options - workspace root, configured folder, attachment identity, and image bytes.
43
+ * @returns the absolute path of the written file.
44
+ */
45
+ export declare function saveImageToWorkspace(options: {
46
+ workspaceRoot: string;
47
+ folder?: string | undefined;
48
+ attachmentId: string;
49
+ mediaType: ImageAttachmentRef['mediaType'];
50
+ data: Uint8Array;
51
+ signal?: AbortSignal;
52
+ }): Promise<string>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-image-gen",
3
- "version": "0.1.6",
3
+ "version": "0.2.0",
4
4
  "description": "Bring ChatGPT-like image generation to DeepSeek Harness — Gemini, OpenAI, Seedream, DashScope & more.",
5
5
  "type": "module",
6
6
  "author": "shanliuling",
@@ -54,6 +54,13 @@
54
54
  "platform": "web"
55
55
  }
56
56
  },
57
+ "scripts": {
58
+ "typecheck": "tsc --noEmit",
59
+ "build": "tsc && tsdown",
60
+ "prepare": "pnpm run build",
61
+ "test": "vitest run",
62
+ "pack:check": "pnpm pack --dry-run"
63
+ },
57
64
  "engines": {
58
65
  "node": "^22.19.0 || >=24.0.0"
59
66
  },
@@ -80,37 +87,38 @@
80
87
  ],
81
88
  "peerDependencies": {
82
89
  "@deepseek-ai/cordis": ">=4.0.0 <5",
83
- "@deepseek-ai/dsh-attachment": ">=0.1.0-rc.5 <0.2.0",
84
- "@deepseek-ai/dsh-api-remotes": ">=0.1.0-rc.5 <0.2.0",
85
- "@deepseek-ai/dsh-client-connection": ">=0.1.0-rc.5 <0.2.0",
86
- "@deepseek-ai/dsh-client-runtime": ">=0.1.0-rc.5 <0.2.0",
87
- "@deepseek-ai/dsh-client-ui-conversation": ">=0.1.0-rc.5 <0.2.0",
88
- "@deepseek-ai/dsh-client-ui-settings": ">=0.1.0-rc.5 <0.2.0",
89
- "@deepseek-ai/dsh-client-ui-settings-plugins": ">=0.1.0-rc.5 <0.2.0",
90
- "@deepseek-ai/dsh-client-ui-slots": ">=0.1.0-rc.5 <0.2.0",
91
- "@deepseek-ai/dsh-client-ui-tool": ">=0.1.0-rc.5 <0.2.0",
92
- "@deepseek-ai/dsh-credentials": ">=0.1.0-rc.5 <0.2.0",
93
- "@deepseek-ai/dsh-host-webserver": ">=0.1.0-rc.5 <0.2.0",
94
- "@deepseek-ai/dsh-settings": ">=0.1.0-rc.5 <0.2.0",
95
- "@deepseek-ai/dsh-tools": ">=0.1.0-rc.5 <0.2.0",
90
+ "@deepseek-ai/dsh-api-remotes": ">=0.1.1-rc.2 <0.2.0",
91
+ "@deepseek-ai/dsh-attachment": ">=0.1.1-rc.2 <0.2.0",
92
+ "@deepseek-ai/dsh-client-connection": ">=0.1.1-rc.2 <0.2.0",
93
+ "@deepseek-ai/dsh-client-runtime": ">=0.1.1-rc.2 <0.2.0",
94
+ "@deepseek-ai/dsh-client-ui-conversation": ">=0.1.1-rc.2 <0.2.0",
95
+ "@deepseek-ai/dsh-client-ui-settings": ">=0.1.1-rc.2 <0.2.0",
96
+ "@deepseek-ai/dsh-client-ui-settings-plugins": ">=0.1.1-rc.2 <0.2.0",
97
+ "@deepseek-ai/dsh-client-ui-slots": ">=0.1.1-rc.2 <0.2.0",
98
+ "@deepseek-ai/dsh-client-ui-tool": ">=0.1.1-rc.2 <0.2.0",
99
+ "@deepseek-ai/dsh-credentials": ">=0.1.1-rc.2 <0.2.0",
100
+ "@deepseek-ai/dsh-host-webserver": ">=0.1.1-rc.2 <0.2.0",
101
+ "@deepseek-ai/dsh-settings": ">=0.1.1-rc.2 <0.2.0",
102
+ "@deepseek-ai/dsh-tools": ">=0.1.1-rc.2 <0.2.0",
96
103
  "@deepseek-ai/schemastery": ">=3.18.0 <4",
97
104
  "react": ">=18.2.0 <19"
98
105
  },
99
106
  "devDependencies": {
100
107
  "@deepseek-ai/cordis": "4.0.1",
101
- "@deepseek-ai/dsh-attachment": "0.1.0-rc.6",
102
- "@deepseek-ai/dsh-api-remotes": "0.1.0-rc.6",
103
- "@deepseek-ai/dsh-client-connection": "0.1.0-rc.6",
104
- "@deepseek-ai/dsh-client-runtime": "0.1.0-rc.6",
105
- "@deepseek-ai/dsh-client-ui-conversation": "0.1.0-rc.6",
106
- "@deepseek-ai/dsh-client-ui-settings": "0.1.0-rc.6",
107
- "@deepseek-ai/dsh-client-ui-settings-plugins": "0.1.0-rc.6",
108
- "@deepseek-ai/dsh-client-ui-slots": "0.1.0-rc.6",
109
- "@deepseek-ai/dsh-client-ui-tool": "0.1.0-rc.6",
110
- "@deepseek-ai/dsh-credentials": "0.1.0-rc.6",
111
- "@deepseek-ai/dsh-host-webserver": "0.1.0-rc.6",
112
- "@deepseek-ai/dsh-settings": "0.1.0-rc.6",
113
- "@deepseek-ai/dsh-tools": "0.1.0-rc.6",
108
+ "@deepseek-ai/dsh-api-remotes": "0.1.1-rc.2",
109
+ "@deepseek-ai/dsh-attachment": "0.1.1-rc.2",
110
+ "@deepseek-ai/dsh-client-connection": "0.1.1-rc.2",
111
+ "@deepseek-ai/dsh-client-runtime": "0.1.1-rc.2",
112
+ "@deepseek-ai/dsh-client-ui-conversation": "0.1.1-rc.2",
113
+ "@deepseek-ai/dsh-client-ui-settings": "0.1.1-rc.2",
114
+ "@deepseek-ai/dsh-client-ui-settings-plugins": "0.1.1-rc.2",
115
+ "@deepseek-ai/dsh-client-ui-slots": "0.1.1-rc.2",
116
+ "@deepseek-ai/dsh-client-ui-tool": "0.1.1-rc.2",
117
+ "@deepseek-ai/dsh-credentials": "0.1.1-rc.2",
118
+ "@deepseek-ai/dsh-host-webserver": "0.1.1-rc.2",
119
+ "@deepseek-ai/dsh-llm": "0.1.1-rc.2",
120
+ "@deepseek-ai/dsh-settings": "0.1.1-rc.2",
121
+ "@deepseek-ai/dsh-tools": "0.1.1-rc.2",
114
122
  "@deepseek-ai/schemastery": "3.18.1",
115
123
  "@types/node": "^22.20.0",
116
124
  "@types/react": "~18.3.1",
@@ -119,10 +127,5 @@
119
127
  "typescript": "^6.0.3",
120
128
  "vitest": "^4.1.8"
121
129
  },
122
- "scripts": {
123
- "typecheck": "tsc --noEmit",
124
- "build": "tsc && tsdown",
125
- "test": "vitest run",
126
- "pack:check": "pnpm pack --dry-run"
127
- }
128
- }
130
+ "packageManager": "pnpm@11.7.0"
131
+ }