@theia/ai-terminal 1.77.0-next.57 → 1.77.0-next.63

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.
@@ -2,15 +2,23 @@ import { ToolRequest } from '@theia/ai-core';
2
2
  import { ShellCommandPermissionService } from './shell-command-permission-service';
3
3
  import { AbstractShellExecutionTool } from './abstract-shell-execution-tool';
4
4
  import { ShellExecutionToolResult, ShellExecutionCanceledResult } from '../common/shell-execution-server';
5
+ import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
5
6
  export declare class ShellExecutionTool extends AbstractShellExecutionTool {
6
7
  protected readonly shellCommandPermissionService: ShellCommandPermissionService;
8
+ protected readonly envVariablesServer: EnvVariablesServer;
7
9
  getTool(): ToolRequest;
8
10
  protected executeCommand(argString: string, ctx?: unknown): Promise<ShellExecutionToolResult | ShellExecutionCanceledResult>;
9
11
  /**
10
12
  * Resolves a cwd value to an absolute filesystem path.
11
- * Handles: undefined (falls back to workspace root), absolute paths (pass-through),
12
- * root-relative paths (<rootName>/...), and bare root names.
13
+ * Handles: undefined (falls back to the workspace root in a single-root workspace), absolute paths
14
+ * and `file://` URIs, `~` for the home directory, root-prefixed paths (`<rootName>/...`) and bare
15
+ * root names, with the root names of {@link WorkspaceService.getRootNames}.
13
16
  */
14
- private resolveCwd;
17
+ protected resolveCwd(cwd: string | undefined): Promise<string>;
18
+ protected exampleRootName(): string;
19
+ /**
20
+ * Lists the workspace root names with their locations, for error messages.
21
+ */
22
+ protected describeRoots(): string;
15
23
  }
16
24
  //# sourceMappingURL=shell-execution-tool.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"shell-execution-tool.d.ts","sourceRoot":"","sources":["../../src/browser/shell-execution-tool.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,WAAW,EAAoB,MAAM,gBAAgB,CAAC;AAC/D,OAAO,EAAE,6BAA6B,EAAE,MAAM,oCAAoC,CAAC;AACnF,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC;AAC7E,OAAO,EAEH,wBAAwB,EACxB,4BAA4B,EAC/B,MAAM,kCAAkC,CAAC;AAG1C,qBACa,kBAAmB,SAAQ,0BAA0B;IAG9D,SAAS,CAAC,QAAQ,CAAC,6BAA6B,EAAE,6BAA6B,CAAC;IAEhF,OAAO,IAAI,WAAW;cA2GN,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,wBAAwB,GAAG,4BAA4B,CAAC;IAelI;;;;OAIG;IACH,OAAO,CAAC,UAAU;CAyCrB"}
1
+ {"version":3,"file":"shell-execution-tool.d.ts","sourceRoot":"","sources":["../../src/browser/shell-execution-tool.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,WAAW,EAAoB,MAAM,gBAAgB,CAAC;AAC/D,OAAO,EAAE,6BAA6B,EAAE,MAAM,oCAAoC,CAAC;AACnF,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC;AAC7E,OAAO,EAEH,wBAAwB,EACxB,4BAA4B,EAC/B,MAAM,kCAAkC,CAAC;AAE1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAE1E,qBACa,kBAAmB,SAAQ,0BAA0B;IAG9D,SAAS,CAAC,QAAQ,CAAC,6BAA6B,EAAE,6BAA6B,CAAC;IAGhF,SAAS,CAAC,QAAQ,CAAC,kBAAkB,EAAE,kBAAkB,CAAC;IAE1D,OAAO,IAAI,WAAW;cAgHN,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,wBAAwB,GAAG,4BAA4B,CAAC;IAelI;;;;;OAKG;cACa,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC;IA6CpE,SAAS,CAAC,eAAe,IAAI,MAAM;IAInC;;OAEG;IACH,SAAS,CAAC,aAAa,IAAI,MAAM;CAIpC"}
@@ -22,6 +22,7 @@ const shell_command_permission_service_1 = require("./shell-command-permission-s
22
22
  const abstract_shell_execution_tool_1 = require("./abstract-shell-execution-tool");
23
23
  const shell_execution_server_1 = require("../common/shell-execution-server");
24
24
  const core_1 = require("@theia/core");
25
+ const env_variables_1 = require("@theia/core/lib/common/env-variables");
25
26
  let ShellExecutionTool = class ShellExecutionTool extends abstract_shell_execution_tool_1.AbstractShellExecutionTool {
26
27
  getTool() {
27
28
  return {
@@ -62,6 +63,10 @@ To avoid losing important information, limit output size in your commands:
62
63
  - Use wc -l to count lines before fetching full output
63
64
  - Redirect verbose output to /dev/null if not needed
64
65
 
66
+ WORKING DIRECTORY: The command runs inside the directory given as cwd, so relative paths in the command
67
+ are relative to it. In a multi-root workspace, cwd is required: a workspace root name such as "backend" is the
68
+ root folder itself, not a folder to change into.
69
+
65
70
  TIMEOUT: Default 2 minutes, max 10 minutes. Specify higher timeout for longer commands.`,
66
71
  parameters: {
67
72
  type: 'object',
@@ -95,10 +100,11 @@ TIMEOUT: Default 2 minutes, max 10 minutes. Specify higher timeout for longer co
95
100
  },
96
101
  cwd: {
97
102
  type: 'string',
98
- description: 'Working directory for command execution. ' +
99
- 'Use a workspace-relative path (e.g., "backend", "frontend/src") ' +
100
- 'or an absolute filesystem path. ' +
101
- 'If omitted, defaults to the workspace root (single-root workspaces only).'
103
+ description: 'Working directory in which the command runs. ' +
104
+ 'Use a workspace root name (e.g., "backend"), a path starting with a root name (e.g., "backend/src"), ' +
105
+ 'as in the paths returned by the workspace tools, or an absolute filesystem path. ' +
106
+ 'The command starts inside this directory: with cwd "backend", do not `cd backend` in the command. ' +
107
+ 'Required in a multi-root workspace. If omitted in a single-root workspace, defaults to the workspace root.'
102
108
  },
103
109
  timeout: {
104
110
  type: 'number',
@@ -133,45 +139,57 @@ TIMEOUT: Default 2 minutes, max 10 minutes. Specify higher timeout for longer co
133
139
  const args = JSON.parse(argString);
134
140
  return this.runShellCommand({
135
141
  command: args.command,
136
- cwd: this.resolveCwd(args.cwd),
142
+ cwd: await this.resolveCwd(args.cwd),
137
143
  timeout: args.timeout,
138
144
  ctx
139
145
  });
140
146
  }
141
147
  /**
142
148
  * Resolves a cwd value to an absolute filesystem path.
143
- * Handles: undefined (falls back to workspace root), absolute paths (pass-through),
144
- * root-relative paths (<rootName>/...), and bare root names.
149
+ * Handles: undefined (falls back to the workspace root in a single-root workspace), absolute paths
150
+ * and `file://` URIs, `~` for the home directory, root-prefixed paths (`<rootName>/...`) and bare
151
+ * root names, with the root names of {@link WorkspaceService.getRootNames}.
145
152
  */
146
- resolveCwd(cwd) {
153
+ async resolveCwd(cwd) {
147
154
  const roots = this.workspaceService.tryGetRoots();
148
- const rootNames = roots.map(r => r.resource.path.base);
149
155
  if (!cwd) {
150
156
  if (roots.length === 1) {
151
157
  return roots[0].resource.path.fsPath();
152
158
  }
153
159
  throw new Error('A working directory (cwd) is required in a multi-root workspace. ' +
154
- `Available workspace roots: ${rootNames.join(', ')}. ` +
155
- 'Provide one as cwd (e.g., "backend") or use a sub-path (e.g., "backend/src").');
160
+ `Provide a workspace root name as cwd (e.g., "${this.exampleRootName()}") or a path in it ` +
161
+ `(e.g., "${this.exampleRootName()}/src"). Available workspace roots: ${this.describeRoots()}.`);
162
+ }
163
+ if (cwd.startsWith('file://')) {
164
+ return new core_1.URI(cwd).path.fsPath();
156
165
  }
157
166
  const normalized = core_1.Path.normalizePathSeparator(cwd);
158
- if (new core_1.Path(normalized).isAbsolute) {
167
+ if (new core_1.Path(normalized).isAbsolute || core_1.Path.isDrive(normalized.split('/')[0])) {
159
168
  return normalized;
160
169
  }
161
- const segments = normalized.split('/');
162
- for (const root of roots) {
163
- if (root.resource.path.base === segments[0]) {
164
- const rest = segments.slice(1).join('/');
165
- const resolved = rest ? root.resource.resolve(rest) : root.resource;
166
- return resolved.path.fsPath();
167
- }
170
+ if (normalized === '~' || normalized.startsWith('~/')) {
171
+ const home = new core_1.URI(await this.envVariablesServer.getHomeDirUri());
172
+ return (normalized === '~' ? home : home.resolve(normalized.substring(2))).path.fsPath();
173
+ }
174
+ const rootPrefixed = this.workspaceService.resolveRootPrefixedPath(normalized);
175
+ if (rootPrefixed) {
176
+ return rootPrefixed.path.fsPath();
168
177
  }
169
178
  if (roots.length === 1) {
170
179
  return roots[0].resource.resolve(normalized).path.fsPath();
171
180
  }
172
181
  throw new Error(`Could not resolve working directory '${cwd}' to a workspace root. ` +
173
182
  'In a multi-root workspace, prefix paths with the workspace root name ' +
174
- `(e.g., '${rootNames[0]}/path'). Available roots: ${rootNames.join(', ')}.`);
183
+ `(e.g., '${this.exampleRootName()}/path'). Available workspace roots: ${this.describeRoots()}.`);
184
+ }
185
+ exampleRootName() {
186
+ return this.workspaceService.getRootNames().keys().next().value ?? 'rootName';
187
+ }
188
+ /**
189
+ * Lists the workspace root names with their locations, for error messages.
190
+ */
191
+ describeRoots() {
192
+ return Array.from(this.workspaceService.getRootNames(), ([name, uri]) => `${name} (${uri.path.fsPath()})`).join(', ');
175
193
  }
176
194
  };
177
195
  exports.ShellExecutionTool = ShellExecutionTool;
@@ -179,6 +197,10 @@ tslib_1.__decorate([
179
197
  (0, inversify_1.inject)(shell_command_permission_service_1.ShellCommandPermissionService),
180
198
  tslib_1.__metadata("design:type", shell_command_permission_service_1.ShellCommandPermissionService)
181
199
  ], ShellExecutionTool.prototype, "shellCommandPermissionService", void 0);
200
+ tslib_1.__decorate([
201
+ (0, inversify_1.inject)(env_variables_1.EnvVariablesServer),
202
+ tslib_1.__metadata("design:type", Object)
203
+ ], ShellExecutionTool.prototype, "envVariablesServer", void 0);
182
204
  exports.ShellExecutionTool = ShellExecutionTool = tslib_1.__decorate([
183
205
  (0, inversify_1.injectable)()
184
206
  ], ShellExecutionTool);
@@ -1 +1 @@
1
- {"version":3,"file":"shell-execution-tool.js","sourceRoot":"","sources":["../../src/browser/shell-execution-tool.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"shell-execution-tool.js","sourceRoot":"","sources":["../../src/browser/shell-execution-tool.ts"],"names":[],"mappings":";AAAA,gFAAgF;AAChF,yCAAyC;AACzC,EAAE;AACF,2EAA2E;AAC3E,mEAAmE;AACnE,wCAAwC;AACxC,EAAE;AACF,4EAA4E;AAC5E,8EAA8E;AAC9E,6EAA6E;AAC7E,yDAAyD;AACzD,uDAAuD;AACvD,EAAE;AACF,gFAAgF;AAChF,gFAAgF;;;;AAEhF,4DAAkE;AAElE,yFAAmF;AACnF,mFAA6E;AAC7E,6EAI0C;AAC1C,sCAAwC;AACxC,wEAA0E;AAGnE,IAAM,kBAAkB,GAAxB,MAAM,kBAAmB,SAAQ,0DAA0B;IAQ9D,OAAO;QACH,OAAO;YACH,EAAE,EAAE,oDAA2B;YAC/B,IAAI,EAAE,oDAA2B;YACjC,YAAY,EAAE,aAAa;YAC3B,kBAAkB,EAAE,gEAAgE;gBAChF,mEAAmE;YACvE,WAAW,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wFAoC+D;YAC5E,UAAU,EAAE;gBACR,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACR,OAAO,EAAE;wBACL,IAAI,EAAE,QAAQ;wBACd,WAAW,EAAE,iFAAiF;qBACjG;oBACD,WAAW,EAAE;wBACT,IAAI,EAAE,QAAQ;wBACd,WAAW,EAAE,4DAA4D;4BACrE,kEAAkE;4BAClE,4CAA4C;4BAC5C,+CAA+C;4BAC/C,kEAAkE;4BAClE,iEAAiE;4BACjE,kEAAkE;4BAClE,qEAAqE;4BACrE,mFAAmF;4BACnF,mDAAmD;4BACnD,wDAAwD;4BACxD,4CAA4C;4BAC5C,oFAAoF;4BACpF,iGAAiG;4BACjG,sFAAsF;4BACtF,qGAAqG;4BACrG,sIAAsI;4BACtI,uEAAuE;4BACvE,2EAA2E;4BAC3E,gFAAgF;qBACvF;oBACD,GAAG,EAAE;wBACD,IAAI,EAAE,QAAQ;wBACd,WAAW,EAAE,+CAA+C;4BACxD,uGAAuG;4BACvG,mFAAmF;4BACnF,oGAAoG;4BACpG,4GAA4G;qBACnH;oBACD,OAAO,EAAE;wBACL,IAAI,EAAE,QAAQ;wBACd,WAAW,EAAE,iFAAiF;qBACjG;iBACJ;gBACD,QAAQ,EAAE,CAAC,SAAS,EAAE,aAAa,CAAC;aACvC;YACD,OAAO,EAAE,CAAC,SAAiB,EAAE,GAAa,EAAE,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,GAAG,CAAC;YAClF,eAAe,EAAE,CAAC,SAAiB,EAAgC,EAAE;gBACjE,IAAI,CAAC;oBACD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;oBACnC,MAAM,MAAM,GAAG,IAAI,CAAC,6BAA6B,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;oBAE7E,IAAI,MAAM,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;wBAC7B,OAAO;4BACH,MAAM,EAAE,MAAM;4BACd,MAAM,EAAE,mDAAmD,MAAM,CAAC,cAAc,EAAE;yBACrF,CAAC;oBACN,CAAC;oBACD,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;wBACjB,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;oBAC/B,CAAC;oBACD,OAAO,SAAS,CAAC,CAAC,uBAAuB;gBAC7C,CAAC;gBAAC,MAAM,CAAC;oBACL,OAAO,SAAS,CAAC,CAAC,uBAAuB;gBAC7C,CAAC;YACL,CAAC;SACJ,CAAC;IACN,CAAC;IAES,KAAK,CAAC,cAAc,CAAC,SAAiB,EAAE,GAAa;QAC3D,MAAM,IAAI,GAIN,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAE1B,OAAO,IAAI,CAAC,eAAe,CAAC;YACxB,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,GAAG,EAAE,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC;YACpC,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,GAAG;SACN,CAAC,CAAC;IACP,CAAC;IAED;;;;;OAKG;IACO,KAAK,CAAC,UAAU,CAAC,GAAuB;QAC9C,MAAM,KAAK,GAAG,IAAI,CAAC,gBAAgB,CAAC,WAAW,EAAE,CAAC;QAElD,IAAI,CAAC,GAAG,EAAE,CAAC;YACP,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACrB,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YAC3C,CAAC;YACD,MAAM,IAAI,KAAK,CACX,mEAAmE;gBACnE,gDAAgD,IAAI,CAAC,eAAe,EAAE,qBAAqB;gBAC3F,WAAW,IAAI,CAAC,eAAe,EAAE,sCAAsC,IAAI,CAAC,aAAa,EAAE,GAAG,CACjG,CAAC;QACN,CAAC;QAED,IAAI,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC5B,OAAO,IAAI,UAAG,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;QACtC,CAAC;QAED,MAAM,UAAU,GAAG,WAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,CAAC;QAEpD,IAAI,IAAI,WAAI,CAAC,UAAU,CAAC,CAAC,UAAU,IAAI,WAAI,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC5E,OAAO,UAAU,CAAC;QACtB,CAAC;QAED,IAAI,UAAU,KAAK,GAAG,IAAI,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACpD,MAAM,IAAI,GAAG,IAAI,UAAG,CAAC,MAAM,IAAI,CAAC,kBAAkB,CAAC,aAAa,EAAE,CAAC,CAAC;YACpE,OAAO,CAAC,UAAU,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;QAC7F,CAAC;QAED,MAAM,YAAY,GAAG,IAAI,CAAC,gBAAgB,CAAC,uBAAuB,CAAC,UAAU,CAAC,CAAC;QAC/E,IAAI,YAAY,EAAE,CAAC;YACf,OAAO,YAAY,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;QACtC,CAAC;QAED,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACrB,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;QAC/D,CAAC;QAED,MAAM,IAAI,KAAK,CACX,wCAAwC,GAAG,yBAAyB;YACpE,uEAAuE;YACvE,WAAW,IAAI,CAAC,eAAe,EAAE,uCAAuC,IAAI,CAAC,aAAa,EAAE,GAAG,CAClG,CAAC;IACN,CAAC;IAES,eAAe;QACrB,OAAO,IAAI,CAAC,gBAAgB,CAAC,YAAY,EAAE,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,UAAU,CAAC;IAClF,CAAC;IAED;;OAEG;IACO,aAAa;QACnB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,KAAK,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1H,CAAC;CAEJ,CAAA;AArMY,gDAAkB;AAGR;IADlB,IAAA,kBAAM,EAAC,gEAA6B,CAAC;sCACY,gEAA6B;yEAAC;AAG7D;IADlB,IAAA,kBAAM,EAAC,kCAAkB,CAAC;;8DAC+B;6BANjD,kBAAkB;IAD9B,IAAA,sBAAU,GAAE;GACA,kBAAkB,CAqM9B"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=shell-execution-tool.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell-execution-tool.spec.d.ts","sourceRoot":"","sources":["../../src/browser/shell-execution-tool.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,122 @@
1
+ "use strict";
2
+ // *****************************************************************************
3
+ // Copyright (C) 2026 STMicroelectronics and others.
4
+ //
5
+ // This program and the accompanying materials are made available under the
6
+ // terms of the Eclipse Public License v. 2.0 which is available at
7
+ // http://www.eclipse.org/legal/epl-2.0.
8
+ //
9
+ // This Source Code may also be made available under the following Secondary
10
+ // Licenses when the conditions for such availability set forth in the Eclipse
11
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
12
+ // with the GNU Classpath Exception which is available at
13
+ // https://www.gnu.org/software/classpath/license.html.
14
+ //
15
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
16
+ // *****************************************************************************
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ const tslib_1 = require("tslib");
19
+ const jsdom_1 = require("@theia/core/lib/browser/test/jsdom");
20
+ let disableJSDOM = (0, jsdom_1.enableJSDOM)();
21
+ const frontend_application_config_provider_1 = require("@theia/core/lib/browser/frontend-application-config-provider");
22
+ frontend_application_config_provider_1.FrontendApplicationConfigProvider.set({});
23
+ const chai_1 = require("chai");
24
+ const sinon = tslib_1.__importStar(require("sinon"));
25
+ const core_1 = require("@theia/core");
26
+ const uri_1 = tslib_1.__importDefault(require("@theia/core/lib/common/uri"));
27
+ const inversify_1 = require("@theia/core/shared/inversify");
28
+ const env_variables_1 = require("@theia/core/lib/common/env-variables");
29
+ const workspace_service_1 = require("@theia/workspace/lib/browser/workspace-service");
30
+ const with_workspace_service_defaults_1 = require("@theia/workspace/lib/browser/test/with-workspace-service-defaults");
31
+ const shell_execution_server_1 = require("../common/shell-execution-server");
32
+ const shell_command_permission_service_1 = require("./shell-command-permission-service");
33
+ const shell_execution_tool_1 = require("./shell-execution-tool");
34
+ disableJSDOM();
35
+ describe('ShellExecutionTool working directory', () => {
36
+ let shellServer;
37
+ let tool;
38
+ let originalIsWindows;
39
+ before(() => {
40
+ disableJSDOM = (0, jsdom_1.enableJSDOM)();
41
+ });
42
+ after(() => {
43
+ disableJSDOM();
44
+ });
45
+ beforeEach(() => {
46
+ originalIsWindows = core_1.OS.backend.isWindows;
47
+ core_1.OS.backend.isWindows = false;
48
+ });
49
+ afterEach(() => {
50
+ core_1.OS.backend.isWindows = originalIsWindows;
51
+ });
52
+ const setUp = (roots) => {
53
+ const container = new inversify_1.Container();
54
+ shellServer = {
55
+ execute: sinon.stub().callsFake(async (request) => ({
56
+ success: true, exitCode: 0, stdout: '', stderr: '', duration: 0, resolvedCwd: request.cwd
57
+ })),
58
+ cancel: sinon.stub().resolves(true)
59
+ };
60
+ const workspaceService = (0, with_workspace_service_defaults_1.withWorkspaceServiceDefaults)({
61
+ tryGetRoots: () => roots.map(root => ({ resource: new uri_1.default(root), isDirectory: true }))
62
+ });
63
+ container.bind(shell_execution_server_1.ShellExecutionServer).toConstantValue(shellServer);
64
+ container.bind(workspace_service_1.WorkspaceService).toConstantValue(workspaceService);
65
+ container.bind(shell_command_permission_service_1.ShellCommandPermissionService).toConstantValue({});
66
+ container.bind(env_variables_1.EnvVariablesServer).toConstantValue({ getHomeDirUri: async () => 'file:///home/test' });
67
+ container.bind(shell_execution_tool_1.ShellExecutionTool).toSelf();
68
+ tool = container.get(shell_execution_tool_1.ShellExecutionTool);
69
+ };
70
+ const run = (args) => Promise.resolve(tool.getTool().handler(JSON.stringify({ command: 'pwd', description: 'Print the working directory', ...args }), undefined));
71
+ const executedCwd = () => shellServer.execute.lastCall.args[0].cwd;
72
+ it('defaults to the workspace root in a single-root workspace', async () => {
73
+ setUp(['file:///ws/theia']);
74
+ await run({});
75
+ (0, chai_1.expect)(executedCwd()).to.equal('/ws/theia');
76
+ });
77
+ it('runs in the root directory named by cwd, so the command must not change into it again', async () => {
78
+ setUp(['file:///ws/theia', 'file:///ws/other']);
79
+ await run({ cwd: 'theia' });
80
+ (0, chai_1.expect)(executedCwd()).to.equal('/ws/theia');
81
+ });
82
+ it('resolves a root name shared with another root by its parent-qualified name', async () => {
83
+ setUp(['file:///x/app', 'file:///y/app']);
84
+ await run({ cwd: 'y/app/src' });
85
+ (0, chai_1.expect)(executedCwd()).to.equal('/y/app/src');
86
+ });
87
+ it('resolves absolute paths, file URIs and the home directory', async () => {
88
+ setUp(['file:///ws/theia', 'file:///ws/other']);
89
+ await run({ cwd: '/tmp/build' });
90
+ (0, chai_1.expect)(executedCwd()).to.equal('/tmp/build');
91
+ await run({ cwd: 'file:///ws/other/src' });
92
+ (0, chai_1.expect)(executedCwd()).to.equal('/ws/other/src');
93
+ await run({ cwd: '~/projects' });
94
+ (0, chai_1.expect)(executedCwd()).to.equal('/home/test/projects');
95
+ });
96
+ it('rejects a basename shared by several roots, listing the root names', async () => {
97
+ setUp(['file:///x/app', 'file:///y/app']);
98
+ let error;
99
+ try {
100
+ await run({ cwd: 'app' });
101
+ }
102
+ catch (e) {
103
+ error = e;
104
+ }
105
+ (0, chai_1.expect)(error?.message).to.contain('x/app (/x/app), y/app (/y/app)');
106
+ (0, chai_1.expect)(shellServer.execute.called).to.be.false;
107
+ });
108
+ // Agents often omit cwd on their first call and get this error (D1). It is required by design.
109
+ it('requires cwd in a multi-root workspace (D1)', async () => {
110
+ setUp(['file:///ws/theia', 'file:///ws/other']);
111
+ let error;
112
+ try {
113
+ await run({});
114
+ }
115
+ catch (e) {
116
+ error = e;
117
+ }
118
+ (0, chai_1.expect)(error?.message).to.contain('A working directory (cwd) is required in a multi-root workspace');
119
+ (0, chai_1.expect)(shellServer.execute.called).to.be.false;
120
+ });
121
+ });
122
+ //# sourceMappingURL=shell-execution-tool.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell-execution-tool.spec.js","sourceRoot":"","sources":["../../src/browser/shell-execution-tool.spec.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@theia/ai-terminal",
3
- "version": "1.77.0-next.57+81ea5f939",
3
+ "version": "1.77.0-next.63+046af0812",
4
4
  "description": "Theia - AI Terminal Extension",
5
5
  "dependencies": {
6
- "@theia/ai-chat": "1.77.0-next.57+81ea5f939",
7
- "@theia/ai-chat-ui": "1.77.0-next.57+81ea5f939",
8
- "@theia/ai-core": "1.77.0-next.57+81ea5f939",
9
- "@theia/core": "1.77.0-next.57+81ea5f939",
10
- "@theia/terminal": "1.77.0-next.57+81ea5f939",
11
- "@theia/workspace": "1.77.0-next.57+81ea5f939",
6
+ "@theia/ai-chat": "1.77.0-next.63+046af0812",
7
+ "@theia/ai-chat-ui": "1.77.0-next.63+046af0812",
8
+ "@theia/ai-core": "1.77.0-next.63+046af0812",
9
+ "@theia/core": "1.77.0-next.63+046af0812",
10
+ "@theia/terminal": "1.77.0-next.63+046af0812",
11
+ "@theia/workspace": "1.77.0-next.63+046af0812",
12
12
  "tslib": "^2.8.1",
13
13
  "zod": "^4.4.3"
14
14
  },
@@ -51,5 +51,5 @@
51
51
  "nyc": {
52
52
  "extends": "../../configs/nyc.json"
53
53
  },
54
- "gitHead": "81ea5f939c40c905de91083d07e284bfd7437fac"
54
+ "gitHead": "046af0812516f46f2bf7ce3357cca0b672159b20"
55
55
  }
@@ -0,0 +1,132 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2026 STMicroelectronics and others.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { enableJSDOM } from '@theia/core/lib/browser/test/jsdom';
18
+ let disableJSDOM = enableJSDOM();
19
+ import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
20
+ FrontendApplicationConfigProvider.set({});
21
+
22
+ import { expect } from 'chai';
23
+ import * as sinon from 'sinon';
24
+ import { OS } from '@theia/core';
25
+ import URI from '@theia/core/lib/common/uri';
26
+ import { Container } from '@theia/core/shared/inversify';
27
+ import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
28
+ import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
29
+ import { withWorkspaceServiceDefaults } from '@theia/workspace/lib/browser/test/with-workspace-service-defaults';
30
+ import { ShellExecutionRequest, ShellExecutionServer } from '../common/shell-execution-server';
31
+ import { ShellCommandPermissionService } from './shell-command-permission-service';
32
+ import { ShellExecutionTool } from './shell-execution-tool';
33
+
34
+ disableJSDOM();
35
+
36
+ describe('ShellExecutionTool working directory', () => {
37
+
38
+ let shellServer: { execute: sinon.SinonStub; cancel: sinon.SinonStub };
39
+ let tool: ShellExecutionTool;
40
+
41
+ let originalIsWindows: boolean;
42
+ before(() => {
43
+ disableJSDOM = enableJSDOM();
44
+ });
45
+ after(() => {
46
+ disableJSDOM();
47
+ });
48
+ beforeEach(() => {
49
+ originalIsWindows = OS.backend.isWindows;
50
+ OS.backend.isWindows = false;
51
+ });
52
+ afterEach(() => {
53
+ OS.backend.isWindows = originalIsWindows;
54
+ });
55
+
56
+ const setUp = (roots: string[]): void => {
57
+ const container = new Container();
58
+ shellServer = {
59
+ execute: sinon.stub().callsFake(async (request: ShellExecutionRequest) => ({
60
+ success: true, exitCode: 0, stdout: '', stderr: '', duration: 0, resolvedCwd: request.cwd
61
+ })),
62
+ cancel: sinon.stub().resolves(true)
63
+ };
64
+ const workspaceService = withWorkspaceServiceDefaults({
65
+ tryGetRoots: () => roots.map(root => ({ resource: new URI(root), isDirectory: true }))
66
+ });
67
+ container.bind(ShellExecutionServer).toConstantValue(shellServer as unknown as ShellExecutionServer);
68
+ container.bind(WorkspaceService).toConstantValue(workspaceService);
69
+ container.bind(ShellCommandPermissionService).toConstantValue({} as ShellCommandPermissionService);
70
+ container.bind(EnvVariablesServer).toConstantValue({ getHomeDirUri: async () => 'file:///home/test' } as unknown as EnvVariablesServer);
71
+ container.bind(ShellExecutionTool).toSelf();
72
+ tool = container.get(ShellExecutionTool);
73
+ };
74
+
75
+ const run = (args: { cwd?: string }): Promise<unknown> =>
76
+ Promise.resolve(tool.getTool().handler(JSON.stringify({ command: 'pwd', description: 'Print the working directory', ...args }), undefined));
77
+
78
+ const executedCwd = (): string | undefined => (shellServer.execute.lastCall.args[0] as ShellExecutionRequest).cwd;
79
+
80
+ it('defaults to the workspace root in a single-root workspace', async () => {
81
+ setUp(['file:///ws/theia']);
82
+ await run({});
83
+ expect(executedCwd()).to.equal('/ws/theia');
84
+ });
85
+
86
+ it('runs in the root directory named by cwd, so the command must not change into it again', async () => {
87
+ setUp(['file:///ws/theia', 'file:///ws/other']);
88
+ await run({ cwd: 'theia' });
89
+ expect(executedCwd()).to.equal('/ws/theia');
90
+ });
91
+
92
+ it('resolves a root name shared with another root by its parent-qualified name', async () => {
93
+ setUp(['file:///x/app', 'file:///y/app']);
94
+ await run({ cwd: 'y/app/src' });
95
+ expect(executedCwd()).to.equal('/y/app/src');
96
+ });
97
+
98
+ it('resolves absolute paths, file URIs and the home directory', async () => {
99
+ setUp(['file:///ws/theia', 'file:///ws/other']);
100
+ await run({ cwd: '/tmp/build' });
101
+ expect(executedCwd()).to.equal('/tmp/build');
102
+ await run({ cwd: 'file:///ws/other/src' });
103
+ expect(executedCwd()).to.equal('/ws/other/src');
104
+ await run({ cwd: '~/projects' });
105
+ expect(executedCwd()).to.equal('/home/test/projects');
106
+ });
107
+
108
+ it('rejects a basename shared by several roots, listing the root names', async () => {
109
+ setUp(['file:///x/app', 'file:///y/app']);
110
+ let error: Error | undefined;
111
+ try {
112
+ await run({ cwd: 'app' });
113
+ } catch (e) {
114
+ error = e;
115
+ }
116
+ expect(error?.message).to.contain('x/app (/x/app), y/app (/y/app)');
117
+ expect(shellServer.execute.called).to.be.false;
118
+ });
119
+
120
+ // Agents often omit cwd on their first call and get this error (D1). It is required by design.
121
+ it('requires cwd in a multi-root workspace (D1)', async () => {
122
+ setUp(['file:///ws/theia', 'file:///ws/other']);
123
+ let error: Error | undefined;
124
+ try {
125
+ await run({});
126
+ } catch (e) {
127
+ error = e;
128
+ }
129
+ expect(error?.message).to.contain('A working directory (cwd) is required in a multi-root workspace');
130
+ expect(shellServer.execute.called).to.be.false;
131
+ });
132
+ });
@@ -23,7 +23,8 @@ import {
23
23
  ShellExecutionToolResult,
24
24
  ShellExecutionCanceledResult
25
25
  } from '../common/shell-execution-server';
26
- import { Path } from '@theia/core';
26
+ import { Path, URI } from '@theia/core';
27
+ import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
27
28
 
28
29
  @injectable()
29
30
  export class ShellExecutionTool extends AbstractShellExecutionTool {
@@ -31,6 +32,9 @@ export class ShellExecutionTool extends AbstractShellExecutionTool {
31
32
  @inject(ShellCommandPermissionService)
32
33
  protected readonly shellCommandPermissionService: ShellCommandPermissionService;
33
34
 
35
+ @inject(EnvVariablesServer)
36
+ protected readonly envVariablesServer: EnvVariablesServer;
37
+
34
38
  getTool(): ToolRequest {
35
39
  return {
36
40
  id: SHELL_EXECUTION_FUNCTION_ID,
@@ -70,6 +74,10 @@ To avoid losing important information, limit output size in your commands:
70
74
  - Use wc -l to count lines before fetching full output
71
75
  - Redirect verbose output to /dev/null if not needed
72
76
 
77
+ WORKING DIRECTORY: The command runs inside the directory given as cwd, so relative paths in the command
78
+ are relative to it. In a multi-root workspace, cwd is required: a workspace root name such as "backend" is the
79
+ root folder itself, not a folder to change into.
80
+
73
81
  TIMEOUT: Default 2 minutes, max 10 minutes. Specify higher timeout for longer commands.`,
74
82
  parameters: {
75
83
  type: 'object',
@@ -103,10 +111,11 @@ TIMEOUT: Default 2 minutes, max 10 minutes. Specify higher timeout for longer co
103
111
  },
104
112
  cwd: {
105
113
  type: 'string',
106
- description: 'Working directory for command execution. ' +
107
- 'Use a workspace-relative path (e.g., "backend", "frontend/src") ' +
108
- 'or an absolute filesystem path. ' +
109
- 'If omitted, defaults to the workspace root (single-root workspaces only).'
114
+ description: 'Working directory in which the command runs. ' +
115
+ 'Use a workspace root name (e.g., "backend"), a path starting with a root name (e.g., "backend/src"), ' +
116
+ 'as in the paths returned by the workspace tools, or an absolute filesystem path. ' +
117
+ 'The command starts inside this directory: with cwd "backend", do not `cd backend` in the command. ' +
118
+ 'Required in a multi-root workspace. If omitted in a single-root workspace, defaults to the workspace root.'
110
119
  },
111
120
  timeout: {
112
121
  type: 'number',
@@ -147,7 +156,7 @@ TIMEOUT: Default 2 minutes, max 10 minutes. Specify higher timeout for longer co
147
156
 
148
157
  return this.runShellCommand({
149
158
  command: args.command,
150
- cwd: this.resolveCwd(args.cwd),
159
+ cwd: await this.resolveCwd(args.cwd),
151
160
  timeout: args.timeout,
152
161
  ctx
153
162
  });
@@ -155,37 +164,42 @@ TIMEOUT: Default 2 minutes, max 10 minutes. Specify higher timeout for longer co
155
164
 
156
165
  /**
157
166
  * Resolves a cwd value to an absolute filesystem path.
158
- * Handles: undefined (falls back to workspace root), absolute paths (pass-through),
159
- * root-relative paths (<rootName>/...), and bare root names.
167
+ * Handles: undefined (falls back to the workspace root in a single-root workspace), absolute paths
168
+ * and `file://` URIs, `~` for the home directory, root-prefixed paths (`<rootName>/...`) and bare
169
+ * root names, with the root names of {@link WorkspaceService.getRootNames}.
160
170
  */
161
- private resolveCwd(cwd: string | undefined): string {
171
+ protected async resolveCwd(cwd: string | undefined): Promise<string> {
162
172
  const roots = this.workspaceService.tryGetRoots();
163
173
 
164
- const rootNames = roots.map(r => r.resource.path.base);
165
174
  if (!cwd) {
166
175
  if (roots.length === 1) {
167
176
  return roots[0].resource.path.fsPath();
168
177
  }
169
178
  throw new Error(
170
179
  'A working directory (cwd) is required in a multi-root workspace. ' +
171
- `Available workspace roots: ${rootNames.join(', ')}. ` +
172
- 'Provide one as cwd (e.g., "backend") or use a sub-path (e.g., "backend/src").'
180
+ `Provide a workspace root name as cwd (e.g., "${this.exampleRootName()}") or a path in it ` +
181
+ `(e.g., "${this.exampleRootName()}/src"). Available workspace roots: ${this.describeRoots()}.`
173
182
  );
174
183
  }
175
184
 
185
+ if (cwd.startsWith('file://')) {
186
+ return new URI(cwd).path.fsPath();
187
+ }
188
+
176
189
  const normalized = Path.normalizePathSeparator(cwd);
177
190
 
178
- if (new Path(normalized).isAbsolute) {
191
+ if (new Path(normalized).isAbsolute || Path.isDrive(normalized.split('/')[0])) {
179
192
  return normalized;
180
193
  }
181
194
 
182
- const segments = normalized.split('/');
183
- for (const root of roots) {
184
- if (root.resource.path.base === segments[0]) {
185
- const rest = segments.slice(1).join('/');
186
- const resolved = rest ? root.resource.resolve(rest) : root.resource;
187
- return resolved.path.fsPath();
188
- }
195
+ if (normalized === '~' || normalized.startsWith('~/')) {
196
+ const home = new URI(await this.envVariablesServer.getHomeDirUri());
197
+ return (normalized === '~' ? home : home.resolve(normalized.substring(2))).path.fsPath();
198
+ }
199
+
200
+ const rootPrefixed = this.workspaceService.resolveRootPrefixedPath(normalized);
201
+ if (rootPrefixed) {
202
+ return rootPrefixed.path.fsPath();
189
203
  }
190
204
 
191
205
  if (roots.length === 1) {
@@ -195,8 +209,19 @@ TIMEOUT: Default 2 minutes, max 10 minutes. Specify higher timeout for longer co
195
209
  throw new Error(
196
210
  `Could not resolve working directory '${cwd}' to a workspace root. ` +
197
211
  'In a multi-root workspace, prefix paths with the workspace root name ' +
198
- `(e.g., '${rootNames[0]}/path'). Available roots: ${rootNames.join(', ')}.`
212
+ `(e.g., '${this.exampleRootName()}/path'). Available workspace roots: ${this.describeRoots()}.`
199
213
  );
200
214
  }
201
215
 
216
+ protected exampleRootName(): string {
217
+ return this.workspaceService.getRootNames().keys().next().value ?? 'rootName';
218
+ }
219
+
220
+ /**
221
+ * Lists the workspace root names with their locations, for error messages.
222
+ */
223
+ protected describeRoots(): string {
224
+ return Array.from(this.workspaceService.getRootNames(), ([name, uri]) => `${name} (${uri.path.fsPath()})`).join(', ');
225
+ }
226
+
202
227
  }