@huaweidevtools/aishell-dsh-e2b 0.0.0-stage → 0.0.1-beta.4

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,114 @@
1
+ /**
2
+ * The sandbox a conversation works in.
3
+ *
4
+ * One conversation, one sandbox. That is the whole model: there is no
5
+ * "which sandbox did you mean" question for the tools to ask, because there is
6
+ * only ever one answer, and no `sandboxId` parameter for the model to get
7
+ * wrong. The tools act on *the* sandbox, and creating one is the explicit act
8
+ * that binds it to the conversation.
9
+ *
10
+ * ## Why the binding is created explicitly, never lazily
11
+ *
12
+ * A tool that quietly created a sandbox on first use would have to choose the
13
+ * template and the lifetime itself, and those are exactly the two facts the
14
+ * operator should be deciding — a preview page wants a different lifetime from
15
+ * a debugging session, and that choice is the reason the create tool takes
16
+ * parameters at all. A lazy create would move that decision into the plugin
17
+ * and out of the conversation, where nobody can see it or correct it.
18
+ *
19
+ * So a tool with no sandbox bound fails, and names `aishell__sandbox_create` as the way
20
+ * forward. The failure is cheap and legible; a silently invented sandbox is
21
+ * neither.
22
+ *
23
+ * ## Why the binding survives a restart
24
+ *
25
+ * The mapping cannot live in memory alone. A plugin restart — a reload, a
26
+ * crash, a profile edit — would otherwise orphan a running sandbox: the model
27
+ * would find no binding, create a second sandbox, and the first would keep
28
+ * billing until its lifetime expired, with nothing in the conversation
29
+ * accounting for it.
30
+ *
31
+ * So the sandbox carries its conversation in its own `metadata` at creation,
32
+ * and the registry rebuilds the binding by querying for it. The gateway
33
+ * forwards `metadata` filters to the upstream unchanged, and
34
+ * `Sandbox.list({ query: { metadata } })` is ordinary SDK surface, so the
35
+ * recovery needs nothing from either service.
36
+ *
37
+ * A recovered binding is refused if the sandbox is not running. A paused
38
+ * sandbox is not adopted: connecting to it would resume it and silently
39
+ * restart its clock, which is a billing decision nobody asked for.
40
+ *
41
+ * @module @huaweidevtools/aishell-dsh-e2b/sandbox-registry
42
+ */
43
+ /** The metadata key a sandbox carries its owning conversation under. */
44
+ export declare const SESSION_METADATA_KEY = "aishell-session";
45
+ /** One sandbox the registry knows about. */
46
+ export interface BoundSandbox {
47
+ /** The sandbox id. */
48
+ readonly sandboxId: string;
49
+ /** Whether the gateway reports it as running. */
50
+ readonly state: string;
51
+ }
52
+ /** Lists the sandboxes carrying one metadata pair. */
53
+ export type SandboxFinder = (metadata: Record<string, string>, signal: AbortSignal | undefined) => Promise<ReadonlyArray<BoundSandbox>>;
54
+ /** In-memory bindings plus the lookup used to rebuild them. */
55
+ export declare class SandboxRegistry {
56
+ /** Session id → the sandbox bound to it. */
57
+ private readonly bySession;
58
+ /** The metadata lookup, injected so tests need no gateway. */
59
+ private readonly find;
60
+ constructor(find: SandboxFinder);
61
+ /**
62
+ * Bind a sandbox to its conversation.
63
+ *
64
+ * @param sessionId - the owning conversation, or `undefined` for a call that
65
+ * ran outside one. Such a call is recorded nowhere: without a conversation
66
+ * to key on, there would be nothing to bind later calls to.
67
+ * @param sandboxId - the sandbox to bind.
68
+ */
69
+ bind(sessionId: string | undefined, sandboxId: string): void;
70
+ /**
71
+ * The sandbox this conversation is bound to, recovering the binding if this
72
+ * process has not seen one.
73
+ *
74
+ * @param sessionId - the calling conversation.
75
+ * @param signal - caller cancellation, forwarded to the lookup.
76
+ * @returns the sandbox id, or `undefined` when the conversation has none.
77
+ */
78
+ resolve(sessionId: string | undefined, signal?: AbortSignal): Promise<string | undefined>;
79
+ /**
80
+ * The sandbox bound right now, without a lookup.
81
+ *
82
+ * Used where a call has already resolved the binding and needs it again
83
+ * without a second round trip.
84
+ *
85
+ * @param sessionId - the calling conversation.
86
+ * @returns the bound sandbox id, or `undefined`.
87
+ */
88
+ bound(sessionId: string | undefined): string | undefined;
89
+ /**
90
+ * Drop a binding, because the sandbox it names no longer exists.
91
+ *
92
+ * @param sandboxId - the sandbox that is gone.
93
+ */
94
+ forget(sandboxId: string): void;
95
+ }
96
+ /** The metadata a new sandbox carries so its conversation can be recovered. */
97
+ export declare function sessionMetadata(sessionId: string | undefined, extra: Record<string, string> | undefined): Record<string, string> | undefined;
98
+ /**
99
+ * The message a tool gets when the conversation has no sandbox.
100
+ *
101
+ * Names the remedy, and deliberately does not offer to create one: the model
102
+ * chooses the template and the lifetime, and it cannot do that from inside a
103
+ * tool that was asked to write a file.
104
+ */
105
+ export declare const NO_SANDBOX_MESSAGE: string;
106
+ /**
107
+ * The message a tool gets when a sandbox existed but is now gone.
108
+ *
109
+ * Distinct from {@link NO_SANDBOX_MESSAGE} because the remedy differs: the
110
+ * binding is stale rather than absent, and the model should know the previous
111
+ * sandbox's contents are not recoverable — creating a new one starts empty.
112
+ */
113
+ export declare const SANDBOX_GONE_MESSAGE: string;
114
+ //# sourceMappingURL=sandbox-registry.d.ts.map
@@ -0,0 +1,46 @@
1
+ /**
2
+ * The model-facing tools.
3
+ *
4
+ * Each one is a thin projection of an E2B SDK call: the tool's parameters are
5
+ * the SDK's own options, and its result is the SDK's own return value in a
6
+ * shape that survives JSON. That correspondence is the point — an operator who
7
+ * reads the E2B documentation should find nothing surprising here, and the
8
+ * next plugin in this family can keep the same contract.
9
+ *
10
+ * Three conventions are load-bearing and are repeated in the descriptions the
11
+ * model reads:
12
+ *
13
+ * - **One conversation, one sandbox.** No tool but `aishell__sandbox_create` takes a
14
+ * `sandboxId`. There is nothing to choose between, so there is no parameter
15
+ * to get wrong, and no way for a call to land in a different sandbox than
16
+ * the model meant.
17
+ * - **`timeoutMs` is milliseconds.** The TypeScript SDK counts milliseconds
18
+ * where the Python one counts seconds; a model that learned the Python
19
+ * spelling would ask for a five-second sandbox and get five thousand.
20
+ * - **A non-zero exit is a result, not a failure.** `aishell__sandbox_commands_run`
21
+ * reports the code and both streams, so a failing command reads as an
22
+ * outcome to reason about rather than a broken tool.
23
+ *
24
+ * @module @huaweidevtools/aishell-dsh-e2b/tools
25
+ */
26
+ import type { Context } from '@deepseek-ai/cordis';
27
+ import type { SandboxClient } from './client.ts';
28
+ import { type SandboxRegistry } from './sandbox-registry.ts';
29
+ /** What every tool needs to act. */
30
+ export interface ToolDeps {
31
+ /** The sandbox gateway connection. */
32
+ readonly client: SandboxClient;
33
+ /** The sandbox this conversation is bound to. */
34
+ readonly registry: SandboxRegistry;
35
+ /** The filesystem the local workspace is reached through, when one is mounted. */
36
+ readonly resolveFs: () => Context['fs'] | undefined;
37
+ }
38
+ /**
39
+ * Register every sandbox tool.
40
+ *
41
+ * @param ctx - the plugin context, providing the tool registry.
42
+ * @param deps - the shared dependencies.
43
+ * @returns disposers for the registrations.
44
+ */
45
+ export declare function registerSandboxTools(ctx: Context, deps: ToolDeps): Array<() => void>;
46
+ //# sourceMappingURL=tools.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,56 @@
1
1
  {
2
2
  "name": "@huaweidevtools/aishell-dsh-e2b",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "description": "Runs throwaway and preview deployments in E2B sandboxes on DSH — sandbox lifecycle, commands, and files as tools",
4
+ "version": "0.0.1-beta.4",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/yusheng-g/aishell-dsh-plugins.git",
11
+ "directory": "packages/aishell-e2b"
12
+ },
13
+ "type": "module",
14
+ "main": "lib/index.js",
15
+ "types": "lib/types/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./lib/types/index.d.ts",
19
+ "default": "./lib/index.js"
20
+ }
21
+ },
22
+ "files": [
23
+ "lib/index.js",
24
+ "lib/types/**/*.d.ts",
25
+ "cordis.patch.yml"
26
+ ],
27
+ "license": "Apache-2.0",
28
+ "dependencies": {
29
+ "e2b": "2.59.0",
30
+ "@huaweidevtools/aishell-dsh-credentials-store": "0.0.1-beta.4"
31
+ },
32
+ "peerDependencies": {
33
+ "@deepseek-ai/cordis": "^4.0.4",
34
+ "@deepseek-ai/dsh-fs": "0.2.0-rc.1 || 0.2.1-rc.1 || 0.2.1-rc.2 || 0.2.1",
35
+ "@deepseek-ai/dsh-system-prompt": "0.2.0-rc.1 || 0.2.1-rc.1 || 0.2.1-rc.2 || 0.2.1",
36
+ "@deepseek-ai/dsh-tools": "0.2.0-rc.1 || 0.2.1-rc.1 || 0.2.1-rc.2 || 0.2.1"
37
+ },
38
+ "devDependencies": {
39
+ "@deepseek-ai/cordis": "^4.0.4",
40
+ "@deepseek-ai/cordis-plugin-loader": "^1.0.5",
41
+ "@deepseek-ai/dsh-fs": "0.2.0-rc.1 || 0.2.1-rc.1 || 0.2.1-rc.2 || 0.2.1",
42
+ "@deepseek-ai/dsh-system-prompt": "0.2.0-rc.1 || 0.2.1-rc.1 || 0.2.1-rc.2 || 0.2.1",
43
+ "@deepseek-ai/dsh-tools": "0.2.0-rc.1 || 0.2.1-rc.1 || 0.2.1-rc.2 || 0.2.1",
44
+ "@deepseek-ai/schemastery": "3.18.4",
45
+ "tsdown": "^0.10.0",
46
+ "typescript": "^5.7.0"
47
+ },
48
+ "dsh": {
49
+ "bundle": {
50
+ "patch": "./cordis.patch.yml"
51
+ }
52
+ },
53
+ "scripts": {
54
+ "build:bundle": "tsdown"
55
+ }
6
56
  }