@dexto/image-local 1.7.1 → 1.8.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.
package/README.md CHANGED
@@ -7,7 +7,9 @@ This package default-exports a typed `DextoImage` (no side effects, no registrie
7
7
 
8
8
  ## What’s included
9
9
 
10
- - **Storage factories**: local filesystem blob store, SQLite database, in-memory cache (plus in-memory alternatives; Postgres/Redis factories are included but require optional deps)
10
+ - **Stores**: local `DextoStores` built with filesystem artifacts, SQLite session data, and in-memory fast state by default
11
+ - **Workspace handles**: local filesystem workspace handles for local CLI/app runs
12
+ - **Skill sources**: local and plugin skill directories loaded through `SkillManager`
11
13
  - **Tool factories**: builtin, filesystem, process, todo, plan, agent-spawner
12
14
  - **Hooks**: content-policy, response-sanitizer
13
15
  - **Compaction**: reactive-overflow, noop
@@ -44,7 +46,7 @@ tools:
44
46
 
45
47
  Notes:
46
48
  - Omit `tools:` to use `image.defaults.tools`.
47
- - Storage defaults come from `image.defaults.storage` (override with `storage:` in YAML).
49
+ - Store defaults come from `image.defaults.storage` and are resolved by `storage.createStores(...)`.
48
50
  - `filesystem-tools.allowedPaths` defines the static sandbox. In manual mode, attempts to access paths outside the sandbox trigger a directory access approval prompt; if approved, access is granted for the current session or for a single occurrence.
49
51
 
50
52
  ## App usage (direct import)
package/dist/index.cjs CHANGED
@@ -28,6 +28,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
28
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
29
  var index_exports = {};
30
30
  __export(index_exports, {
31
+ LocalWorkspaceHandleProvider: () => import_local_workspace_handle_provider.LocalWorkspaceHandleProvider,
31
32
  default: () => index_default
32
33
  });
33
34
  module.exports = __toCommonJS(index_exports);
@@ -46,6 +47,7 @@ var import_tools_plan = require("@dexto/tools-plan");
46
47
  var import_tools_scheduler = require("@dexto/tools-scheduler");
47
48
  var import_tools_lifecycle = require("@dexto/tools-lifecycle");
48
49
  var import_agent_management = require("@dexto/agent-management");
50
+ var import_local_workspace_handle_provider = require("./local-workspace-handle-provider.js");
49
51
  const import_meta = {};
50
52
  function readPackageJson(packageJsonPath) {
51
53
  if (!(0, import_node_fs.existsSync)(packageJsonPath)) {
@@ -136,6 +138,82 @@ const reactiveOverflowCompactionFactory = {
136
138
  }
137
139
  })
138
140
  };
141
+ async function createLocalStores(config, logger) {
142
+ let blobStore;
143
+ if (config.blob.type === "local") {
144
+ blobStore = new import_storage.LocalBlobStore(import_storage.LocalBlobStoreSchema.parse(config.blob), logger);
145
+ } else {
146
+ const memoryBlobConfig = import_storage.InMemoryBlobStoreSchema.parse(config.blob);
147
+ blobStore = new import_storage.MemoryBlobStore(
148
+ {
149
+ maxBlobSize: memoryBlobConfig.maxBlobSize,
150
+ maxTotalSize: memoryBlobConfig.maxTotalSize
151
+ },
152
+ logger
153
+ );
154
+ }
155
+ let database;
156
+ if (config.database.type === "sqlite") {
157
+ database = new import_storage.SQLiteStore(import_storage.SqliteDatabaseSchema.parse(config.database), logger);
158
+ } else if (config.database.type === "postgres") {
159
+ database = new import_storage.PostgresStore(import_storage.PostgresDatabaseSchema.parse(config.database), logger);
160
+ } else {
161
+ import_storage.InMemoryDatabaseSchema.parse(config.database);
162
+ database = new import_storage.MemoryDatabaseStore();
163
+ }
164
+ let cache;
165
+ if (config.cache.type === "redis") {
166
+ cache = new import_storage.RedisStore(import_storage.RedisCacheSchema.parse(config.cache), logger);
167
+ } else {
168
+ import_storage.InMemoryCacheSchema.parse(config.cache);
169
+ cache = new import_storage.MemoryCacheStore();
170
+ }
171
+ return new import_core.BackendDextoStores(
172
+ {
173
+ conversation: new import_core.DatabaseConversationStore(database, logger),
174
+ sessions: new import_core.DatabaseBackedSessionStore(database, cache),
175
+ memories: new import_core.DatabaseBackedMemoryStore(database),
176
+ workspaces: new import_core.DatabaseBackedWorkspaceStore(database),
177
+ approvals: new import_core.DatabaseBackedApprovalStore(database, cache, logger),
178
+ toolPreferences: new import_core.DatabaseBackedToolPreferenceStore(database, cache, logger),
179
+ toolState: new import_core.DatabaseBackedToolStateStore(database),
180
+ steerQueue: new import_core.DatabaseBackedSessionMessageQueueStore(
181
+ database,
182
+ cache,
183
+ logger,
184
+ import_core.SESSION_STEER_QUEUE_KEY_PREFIX
185
+ ),
186
+ followUpQueue: new import_core.DatabaseBackedSessionMessageQueueStore(
187
+ database,
188
+ cache,
189
+ logger,
190
+ import_core.SESSION_FOLLOW_UP_QUEUE_KEY_PREFIX
191
+ ),
192
+ customPrompts: new import_core.DatabaseBackedCustomPromptStore(database),
193
+ artifacts: new import_core.DatabaseBackedArtifactStore(blobStore),
194
+ runtimeEvents: new import_core.DatabaseBackedRuntimeEventStore(database),
195
+ toolExecutions: new import_core.DatabaseBackedToolExecutionStore(database)
196
+ },
197
+ {
198
+ async connect() {
199
+ await cache.connect();
200
+ await database.connect();
201
+ await blobStore.connect();
202
+ },
203
+ async disconnect() {
204
+ await Promise.all([
205
+ cache.disconnect(),
206
+ database.disconnect(),
207
+ blobStore.disconnect()
208
+ ]);
209
+ },
210
+ isConnected() {
211
+ return cache.isConnected() && database.isConnected() && blobStore.isConnected();
212
+ }
213
+ },
214
+ "backend"
215
+ );
216
+ }
139
217
  const imageLocal = {
140
218
  metadata: {
141
219
  name: imageMetadata.name,
@@ -168,7 +246,6 @@ const imageLocal = {
168
246
  title: "Plan Mode",
169
247
  description: "Internal prompt used by the CLI plan mode toggle. This is injected into the first user message when plan mode is enabled.",
170
248
  "user-invocable": false,
171
- "disable-model-invocation": true,
172
249
  prompt: [
173
250
  "You are in PLAN MODE.",
174
251
  "",
@@ -202,19 +279,8 @@ const imageLocal = {
202
279
  "agent-spawner": import_agent_management.agentSpawnerToolsFactory
203
280
  },
204
281
  storage: {
205
- blob: {
206
- local: import_storage.localBlobStoreFactory,
207
- "in-memory": import_storage.inMemoryBlobStoreFactory
208
- },
209
- database: {
210
- sqlite: import_storage.sqliteDatabaseFactory,
211
- postgres: import_storage.postgresDatabaseFactory,
212
- "in-memory": import_storage.inMemoryDatabaseFactory
213
- },
214
- cache: {
215
- "in-memory": import_storage.inMemoryCacheFactory,
216
- redis: import_storage.redisCacheFactory
217
- }
282
+ configSchema: import_storage.StorageSchema,
283
+ createStores: createLocalStores
218
284
  },
219
285
  hooks: {
220
286
  "content-policy": contentPolicyFactory,
@@ -224,6 +290,18 @@ const imageLocal = {
224
290
  "reactive-overflow": reactiveOverflowCompactionFactory,
225
291
  noop: noopCompactionFactory
226
292
  },
227
- logger: import_core.defaultLoggerFactory
293
+ logger: import_core.defaultLoggerFactory,
294
+ workspace: {
295
+ create: () => new import_local_workspace_handle_provider.LocalWorkspaceHandleProvider()
296
+ },
297
+ skills: {
298
+ create: (context) => (0, import_agent_management.createLocalSkillSources)({
299
+ workspaceRoot: context?.hostContext?.workspaceRoot
300
+ })
301
+ }
228
302
  };
229
303
  var index_default = imageLocal;
304
+ // Annotate the CommonJS export names for ESM import in node:
305
+ 0 && (module.exports = {
306
+ LocalWorkspaceHandleProvider
307
+ });
package/dist/index.d.cts CHANGED
@@ -1,5 +1,13 @@
1
1
  import { DextoImage } from '@dexto/agent-config';
2
+ import { WorkspaceHandleProvider, WorkspaceContext, OpenWorkspaceInput, WorkspaceHandle } from '@dexto/core/workspace';
3
+
4
+ declare class LocalWorkspaceHandleProvider implements WorkspaceHandleProvider {
5
+ open(input: {
6
+ context: WorkspaceContext;
7
+ input?: OpenWorkspaceInput;
8
+ }): Promise<WorkspaceHandle>;
9
+ }
2
10
 
3
11
  declare const imageLocal: DextoImage;
4
12
 
5
- export { imageLocal as default };
13
+ export { LocalWorkspaceHandleProvider, imageLocal as default };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  import { type DextoImage } from '@dexto/agent-config';
2
+ import { LocalWorkspaceHandleProvider } from './local-workspace-handle-provider.js';
2
3
  declare const imageLocal: DextoImage;
3
4
  export default imageLocal;
5
+ export { LocalWorkspaceHandleProvider };
4
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,KAAK,UAAU,EAOlB,MAAM,qBAAqB,CAAC;AAmJ7B,QAAA,MAAM,UAAU,EAAE,UA0FjB,CAAC;AAEF,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,KAAK,UAAU,EAOlB,MAAM,qBAAqB,CAAC;AA2D7B,OAAO,EAAE,4BAA4B,EAAE,MAAM,sCAAsC,CAAC;AAmMpF,QAAA,MAAM,UAAU,EAAE,UAuFjB,CAAC;AAEF,eAAe,UAAU,CAAC;AAC1B,OAAO,EAAE,4BAA4B,EAAE,CAAC"}
package/dist/index.js CHANGED
@@ -8,19 +8,42 @@ import { fileURLToPath } from "node:url";
8
8
  import { z } from "zod";
9
9
  import {
10
10
  ContentPolicyHook,
11
+ BackendDextoStores,
12
+ DatabaseBackedApprovalStore,
13
+ DatabaseBackedArtifactStore,
14
+ DatabaseBackedCustomPromptStore,
15
+ DatabaseBackedMemoryStore,
16
+ DatabaseBackedRuntimeEventStore,
17
+ DatabaseBackedSessionMessageQueueStore,
18
+ DatabaseBackedSessionStore,
19
+ DatabaseBackedToolExecutionStore,
20
+ DatabaseBackedToolPreferenceStore,
21
+ DatabaseBackedToolStateStore,
22
+ DatabaseBackedWorkspaceStore,
23
+ SESSION_FOLLOW_UP_QUEUE_KEY_PREFIX,
24
+ SESSION_STEER_QUEUE_KEY_PREFIX,
25
+ DatabaseConversationStore,
11
26
  ResponseSanitizerHook,
12
27
  defaultLoggerFactory,
13
28
  NoOpCompactionStrategy,
14
29
  ReactiveOverflowCompactionStrategy
15
30
  } from "@dexto/core";
16
31
  import {
17
- localBlobStoreFactory,
18
- inMemoryBlobStoreFactory,
19
- sqliteDatabaseFactory,
20
- postgresDatabaseFactory,
21
- inMemoryDatabaseFactory,
22
- inMemoryCacheFactory,
23
- redisCacheFactory
32
+ StorageSchema,
33
+ LocalBlobStoreSchema,
34
+ InMemoryBlobStoreSchema,
35
+ SqliteDatabaseSchema,
36
+ PostgresDatabaseSchema,
37
+ InMemoryDatabaseSchema,
38
+ RedisCacheSchema,
39
+ InMemoryCacheSchema,
40
+ LocalBlobStore,
41
+ MemoryBlobStore,
42
+ SQLiteStore,
43
+ PostgresStore,
44
+ MemoryDatabaseStore,
45
+ RedisStore,
46
+ MemoryCacheStore
24
47
  } from "@dexto/storage";
25
48
  import { builtinToolsFactory } from "@dexto/tools-builtins";
26
49
  import { fileSystemToolsFactory } from "@dexto/tools-filesystem";
@@ -31,9 +54,11 @@ import { schedulerToolsFactory } from "@dexto/tools-scheduler";
31
54
  import { lifecycleToolsFactory } from "@dexto/tools-lifecycle";
32
55
  import {
33
56
  agentSpawnerToolsFactory,
57
+ createLocalSkillSources,
34
58
  creatorToolsFactory,
35
59
  getDextoPackageRoot
36
60
  } from "@dexto/agent-management";
61
+ import { LocalWorkspaceHandleProvider } from "./local-workspace-handle-provider.js";
37
62
  function readPackageJson(packageJsonPath) {
38
63
  if (!existsSync(packageJsonPath)) {
39
64
  return null;
@@ -123,6 +148,82 @@ const reactiveOverflowCompactionFactory = {
123
148
  }
124
149
  })
125
150
  };
151
+ async function createLocalStores(config, logger) {
152
+ let blobStore;
153
+ if (config.blob.type === "local") {
154
+ blobStore = new LocalBlobStore(LocalBlobStoreSchema.parse(config.blob), logger);
155
+ } else {
156
+ const memoryBlobConfig = InMemoryBlobStoreSchema.parse(config.blob);
157
+ blobStore = new MemoryBlobStore(
158
+ {
159
+ maxBlobSize: memoryBlobConfig.maxBlobSize,
160
+ maxTotalSize: memoryBlobConfig.maxTotalSize
161
+ },
162
+ logger
163
+ );
164
+ }
165
+ let database;
166
+ if (config.database.type === "sqlite") {
167
+ database = new SQLiteStore(SqliteDatabaseSchema.parse(config.database), logger);
168
+ } else if (config.database.type === "postgres") {
169
+ database = new PostgresStore(PostgresDatabaseSchema.parse(config.database), logger);
170
+ } else {
171
+ InMemoryDatabaseSchema.parse(config.database);
172
+ database = new MemoryDatabaseStore();
173
+ }
174
+ let cache;
175
+ if (config.cache.type === "redis") {
176
+ cache = new RedisStore(RedisCacheSchema.parse(config.cache), logger);
177
+ } else {
178
+ InMemoryCacheSchema.parse(config.cache);
179
+ cache = new MemoryCacheStore();
180
+ }
181
+ return new BackendDextoStores(
182
+ {
183
+ conversation: new DatabaseConversationStore(database, logger),
184
+ sessions: new DatabaseBackedSessionStore(database, cache),
185
+ memories: new DatabaseBackedMemoryStore(database),
186
+ workspaces: new DatabaseBackedWorkspaceStore(database),
187
+ approvals: new DatabaseBackedApprovalStore(database, cache, logger),
188
+ toolPreferences: new DatabaseBackedToolPreferenceStore(database, cache, logger),
189
+ toolState: new DatabaseBackedToolStateStore(database),
190
+ steerQueue: new DatabaseBackedSessionMessageQueueStore(
191
+ database,
192
+ cache,
193
+ logger,
194
+ SESSION_STEER_QUEUE_KEY_PREFIX
195
+ ),
196
+ followUpQueue: new DatabaseBackedSessionMessageQueueStore(
197
+ database,
198
+ cache,
199
+ logger,
200
+ SESSION_FOLLOW_UP_QUEUE_KEY_PREFIX
201
+ ),
202
+ customPrompts: new DatabaseBackedCustomPromptStore(database),
203
+ artifacts: new DatabaseBackedArtifactStore(blobStore),
204
+ runtimeEvents: new DatabaseBackedRuntimeEventStore(database),
205
+ toolExecutions: new DatabaseBackedToolExecutionStore(database)
206
+ },
207
+ {
208
+ async connect() {
209
+ await cache.connect();
210
+ await database.connect();
211
+ await blobStore.connect();
212
+ },
213
+ async disconnect() {
214
+ await Promise.all([
215
+ cache.disconnect(),
216
+ database.disconnect(),
217
+ blobStore.disconnect()
218
+ ]);
219
+ },
220
+ isConnected() {
221
+ return cache.isConnected() && database.isConnected() && blobStore.isConnected();
222
+ }
223
+ },
224
+ "backend"
225
+ );
226
+ }
126
227
  const imageLocal = {
127
228
  metadata: {
128
229
  name: imageMetadata.name,
@@ -155,7 +256,6 @@ const imageLocal = {
155
256
  title: "Plan Mode",
156
257
  description: "Internal prompt used by the CLI plan mode toggle. This is injected into the first user message when plan mode is enabled.",
157
258
  "user-invocable": false,
158
- "disable-model-invocation": true,
159
259
  prompt: [
160
260
  "You are in PLAN MODE.",
161
261
  "",
@@ -189,19 +289,8 @@ const imageLocal = {
189
289
  "agent-spawner": agentSpawnerToolsFactory
190
290
  },
191
291
  storage: {
192
- blob: {
193
- local: localBlobStoreFactory,
194
- "in-memory": inMemoryBlobStoreFactory
195
- },
196
- database: {
197
- sqlite: sqliteDatabaseFactory,
198
- postgres: postgresDatabaseFactory,
199
- "in-memory": inMemoryDatabaseFactory
200
- },
201
- cache: {
202
- "in-memory": inMemoryCacheFactory,
203
- redis: redisCacheFactory
204
- }
292
+ configSchema: StorageSchema,
293
+ createStores: createLocalStores
205
294
  },
206
295
  hooks: {
207
296
  "content-policy": contentPolicyFactory,
@@ -211,9 +300,18 @@ const imageLocal = {
211
300
  "reactive-overflow": reactiveOverflowCompactionFactory,
212
301
  noop: noopCompactionFactory
213
302
  },
214
- logger: defaultLoggerFactory
303
+ logger: defaultLoggerFactory,
304
+ workspace: {
305
+ create: () => new LocalWorkspaceHandleProvider()
306
+ },
307
+ skills: {
308
+ create: (context) => createLocalSkillSources({
309
+ workspaceRoot: context?.hostContext?.workspaceRoot
310
+ })
311
+ }
215
312
  };
216
313
  var index_default = imageLocal;
217
314
  export {
315
+ LocalWorkspaceHandleProvider,
218
316
  index_default as default
219
317
  };
@@ -0,0 +1,151 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var local_workspace_handle_provider_exports = {};
30
+ __export(local_workspace_handle_provider_exports, {
31
+ LocalWorkspaceHandleProvider: () => LocalWorkspaceHandleProvider
32
+ });
33
+ module.exports = __toCommonJS(local_workspace_handle_provider_exports);
34
+ var import_node_child_process = require("node:child_process");
35
+ var import_promises = require("node:fs/promises");
36
+ var import_node_path = __toESM(require("node:path"), 1);
37
+ var import_node_util = require("node:util");
38
+ var import_glob = require("glob");
39
+ var import_workspace = require("@dexto/core/workspace");
40
+ const exec = (0, import_node_util.promisify)(import_node_child_process.exec);
41
+ class LocalWorkspaceHandleProvider {
42
+ async open(input) {
43
+ const root = import_node_path.default.resolve(input.context.path);
44
+ const files = new LocalWorkspaceFiles(root);
45
+ const capabilities = resolveCapabilities(input.input);
46
+ const handle = {
47
+ context: {
48
+ ...input.context,
49
+ path: root
50
+ },
51
+ capabilities,
52
+ files
53
+ };
54
+ if (capabilities.includes("processes")) {
55
+ handle.processes = new LocalWorkspaceProcesses(root);
56
+ }
57
+ return handle;
58
+ }
59
+ }
60
+ class LocalWorkspaceFiles {
61
+ constructor(root) {
62
+ this.root = root;
63
+ }
64
+ readText = async (filePath) => {
65
+ try {
66
+ return await (0, import_promises.readFile)(this.resolveInsideRoot(filePath), "utf-8");
67
+ } catch (error) {
68
+ if (error.code === "ENOENT") {
69
+ throw import_workspace.WorkspaceError.fileNotFound(filePath);
70
+ }
71
+ throw error;
72
+ }
73
+ };
74
+ readFile = async (filePath) => {
75
+ return this.readText(filePath);
76
+ };
77
+ glob = async (pattern) => {
78
+ this.assertRelativePattern(pattern);
79
+ const files = await (0, import_glob.glob)(pattern, {
80
+ cwd: this.root,
81
+ absolute: false,
82
+ nodir: true,
83
+ follow: false,
84
+ posix: true
85
+ });
86
+ return files.sort();
87
+ };
88
+ writeFile = async (filePath, content) => {
89
+ const resolvedPath = this.resolveInsideRoot(filePath);
90
+ await (0, import_promises.mkdir)(import_node_path.default.dirname(resolvedPath), { recursive: true });
91
+ try {
92
+ await (0, import_promises.writeFile)(resolvedPath, content, "utf-8");
93
+ } catch (error) {
94
+ if (error.code === "ENOENT") {
95
+ throw import_workspace.WorkspaceError.fileNotFound(filePath);
96
+ }
97
+ throw error;
98
+ }
99
+ };
100
+ listFiles = async (directoryPath = ".") => {
101
+ const resolvedPath = this.resolveInsideRoot(directoryPath);
102
+ const relativePath = import_node_path.default.relative(this.root, resolvedPath);
103
+ const pattern = relativePath ? `${relativePath.split(import_node_path.default.sep).join("/")}/**/*` : "**/*";
104
+ return this.glob(pattern);
105
+ };
106
+ resolveInsideRoot(filePath) {
107
+ const resolvedPath = import_node_path.default.isAbsolute(filePath) ? import_node_path.default.resolve(filePath) : import_node_path.default.resolve(this.root, filePath);
108
+ const relativePath = import_node_path.default.relative(this.root, resolvedPath);
109
+ if (relativePath === "" || !relativePath.startsWith("..") && !import_node_path.default.isAbsolute(relativePath)) {
110
+ return resolvedPath;
111
+ }
112
+ throw import_workspace.WorkspaceError.pathOutsideWorkspace(filePath);
113
+ }
114
+ assertRelativePattern(pattern) {
115
+ if (import_node_path.default.isAbsolute(pattern) || pattern.split(/[\\/]/).includes("..")) {
116
+ throw import_workspace.WorkspaceError.pathOutsideWorkspace(pattern);
117
+ }
118
+ }
119
+ }
120
+ class LocalWorkspaceProcesses {
121
+ constructor(root) {
122
+ this.root = root;
123
+ }
124
+ exec = async (input) => {
125
+ const cwd = input.cwd === void 0 ? this.root : resolveInsideRoot(this.root, input.cwd);
126
+ const result = await exec(input.command, { cwd });
127
+ return {
128
+ stdout: result.stdout,
129
+ stderr: result.stderr
130
+ };
131
+ };
132
+ }
133
+ function resolveCapabilities(input) {
134
+ const capabilities = ["files"];
135
+ if (input?.intent === "process" || input?.capabilities?.includes("processes")) {
136
+ capabilities.push("processes");
137
+ }
138
+ return capabilities;
139
+ }
140
+ function resolveInsideRoot(root, filePath) {
141
+ const resolvedPath = import_node_path.default.isAbsolute(filePath) ? import_node_path.default.resolve(filePath) : import_node_path.default.resolve(root, filePath);
142
+ const relativePath = import_node_path.default.relative(root, resolvedPath);
143
+ if (relativePath === "" || !relativePath.startsWith("..") && !import_node_path.default.isAbsolute(relativePath)) {
144
+ return resolvedPath;
145
+ }
146
+ throw import_workspace.WorkspaceError.pathOutsideWorkspace(filePath);
147
+ }
148
+ // Annotate the CommonJS export names for ESM import in node:
149
+ 0 && (module.exports = {
150
+ LocalWorkspaceHandleProvider
151
+ });
@@ -0,0 +1,8 @@
1
+ import type { OpenWorkspaceInput, WorkspaceContext, WorkspaceHandle, WorkspaceHandleProvider } from '@dexto/core/workspace';
2
+ export declare class LocalWorkspaceHandleProvider implements WorkspaceHandleProvider {
3
+ open(input: {
4
+ context: WorkspaceContext;
5
+ input?: OpenWorkspaceInput;
6
+ }): Promise<WorkspaceHandle>;
7
+ }
8
+ //# sourceMappingURL=local-workspace-handle-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-workspace-handle-provider.d.ts","sourceRoot":"","sources":["../src/local-workspace-handle-provider.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACR,kBAAkB,EAElB,gBAAgB,EAChB,eAAe,EACf,uBAAuB,EAC1B,MAAM,uBAAuB,CAAC;AAI/B,qBAAa,4BAA6B,YAAW,uBAAuB;IAClE,IAAI,CAAC,KAAK,EAAE;QACd,OAAO,EAAE,gBAAgB,CAAC;QAC1B,KAAK,CAAC,EAAE,kBAAkB,CAAC;KAC9B,GAAG,OAAO,CAAC,eAAe,CAAC;CAoB/B"}
@@ -0,0 +1,117 @@
1
+ import { exec as execCallback } from "node:child_process";
2
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { promisify } from "node:util";
5
+ import { glob } from "glob";
6
+ import { WorkspaceError } from "@dexto/core/workspace";
7
+ const exec = promisify(execCallback);
8
+ class LocalWorkspaceHandleProvider {
9
+ async open(input) {
10
+ const root = path.resolve(input.context.path);
11
+ const files = new LocalWorkspaceFiles(root);
12
+ const capabilities = resolveCapabilities(input.input);
13
+ const handle = {
14
+ context: {
15
+ ...input.context,
16
+ path: root
17
+ },
18
+ capabilities,
19
+ files
20
+ };
21
+ if (capabilities.includes("processes")) {
22
+ handle.processes = new LocalWorkspaceProcesses(root);
23
+ }
24
+ return handle;
25
+ }
26
+ }
27
+ class LocalWorkspaceFiles {
28
+ constructor(root) {
29
+ this.root = root;
30
+ }
31
+ readText = async (filePath) => {
32
+ try {
33
+ return await readFile(this.resolveInsideRoot(filePath), "utf-8");
34
+ } catch (error) {
35
+ if (error.code === "ENOENT") {
36
+ throw WorkspaceError.fileNotFound(filePath);
37
+ }
38
+ throw error;
39
+ }
40
+ };
41
+ readFile = async (filePath) => {
42
+ return this.readText(filePath);
43
+ };
44
+ glob = async (pattern) => {
45
+ this.assertRelativePattern(pattern);
46
+ const files = await glob(pattern, {
47
+ cwd: this.root,
48
+ absolute: false,
49
+ nodir: true,
50
+ follow: false,
51
+ posix: true
52
+ });
53
+ return files.sort();
54
+ };
55
+ writeFile = async (filePath, content) => {
56
+ const resolvedPath = this.resolveInsideRoot(filePath);
57
+ await mkdir(path.dirname(resolvedPath), { recursive: true });
58
+ try {
59
+ await writeFile(resolvedPath, content, "utf-8");
60
+ } catch (error) {
61
+ if (error.code === "ENOENT") {
62
+ throw WorkspaceError.fileNotFound(filePath);
63
+ }
64
+ throw error;
65
+ }
66
+ };
67
+ listFiles = async (directoryPath = ".") => {
68
+ const resolvedPath = this.resolveInsideRoot(directoryPath);
69
+ const relativePath = path.relative(this.root, resolvedPath);
70
+ const pattern = relativePath ? `${relativePath.split(path.sep).join("/")}/**/*` : "**/*";
71
+ return this.glob(pattern);
72
+ };
73
+ resolveInsideRoot(filePath) {
74
+ const resolvedPath = path.isAbsolute(filePath) ? path.resolve(filePath) : path.resolve(this.root, filePath);
75
+ const relativePath = path.relative(this.root, resolvedPath);
76
+ if (relativePath === "" || !relativePath.startsWith("..") && !path.isAbsolute(relativePath)) {
77
+ return resolvedPath;
78
+ }
79
+ throw WorkspaceError.pathOutsideWorkspace(filePath);
80
+ }
81
+ assertRelativePattern(pattern) {
82
+ if (path.isAbsolute(pattern) || pattern.split(/[\\/]/).includes("..")) {
83
+ throw WorkspaceError.pathOutsideWorkspace(pattern);
84
+ }
85
+ }
86
+ }
87
+ class LocalWorkspaceProcesses {
88
+ constructor(root) {
89
+ this.root = root;
90
+ }
91
+ exec = async (input) => {
92
+ const cwd = input.cwd === void 0 ? this.root : resolveInsideRoot(this.root, input.cwd);
93
+ const result = await exec(input.command, { cwd });
94
+ return {
95
+ stdout: result.stdout,
96
+ stderr: result.stderr
97
+ };
98
+ };
99
+ }
100
+ function resolveCapabilities(input) {
101
+ const capabilities = ["files"];
102
+ if (input?.intent === "process" || input?.capabilities?.includes("processes")) {
103
+ capabilities.push("processes");
104
+ }
105
+ return capabilities;
106
+ }
107
+ function resolveInsideRoot(root, filePath) {
108
+ const resolvedPath = path.isAbsolute(filePath) ? path.resolve(filePath) : path.resolve(root, filePath);
109
+ const relativePath = path.relative(root, resolvedPath);
110
+ if (relativePath === "" || !relativePath.startsWith("..") && !path.isAbsolute(relativePath)) {
111
+ return resolvedPath;
112
+ }
113
+ throw WorkspaceError.pathOutsideWorkspace(filePath);
114
+ }
115
+ export {
116
+ LocalWorkspaceHandleProvider
117
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dexto/image-local",
3
- "version": "1.7.1",
3
+ "version": "1.8.0",
4
4
  "description": "Local development base image for Dexto agents with filesystem and process tools",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -20,18 +20,19 @@
20
20
  "process"
21
21
  ],
22
22
  "dependencies": {
23
+ "glob": "^12.0.0",
23
24
  "zod": "^4.3.6",
24
- "@dexto/agent-config": "1.7.1",
25
- "@dexto/agent-management": "1.7.1",
26
- "@dexto/core": "1.7.1",
27
- "@dexto/storage": "1.7.1",
28
- "@dexto/tools-builtins": "1.7.1",
29
- "@dexto/tools-filesystem": "1.7.1",
30
- "@dexto/tools-lifecycle": "1.7.1",
31
- "@dexto/tools-plan": "1.7.1",
32
- "@dexto/tools-process": "1.7.1",
33
- "@dexto/tools-scheduler": "1.7.1",
34
- "@dexto/tools-todo": "1.7.1"
25
+ "@dexto/agent-config": "1.8.0",
26
+ "@dexto/agent-management": "1.8.0",
27
+ "@dexto/core": "1.8.0",
28
+ "@dexto/storage": "1.8.0",
29
+ "@dexto/tools-builtins": "1.8.0",
30
+ "@dexto/tools-filesystem": "1.8.0",
31
+ "@dexto/tools-lifecycle": "1.8.0",
32
+ "@dexto/tools-plan": "1.8.0",
33
+ "@dexto/tools-process": "1.8.0",
34
+ "@dexto/tools-scheduler": "1.8.0",
35
+ "@dexto/tools-todo": "1.8.0"
35
36
  },
36
37
  "devDependencies": {
37
38
  "tsup": "^8.0.0",