@linxin666/dsh-ssh 0.4.4 → 0.4.6

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/src/tools.ts CHANGED
@@ -91,8 +91,7 @@ function renderTunnel(tunnel: TunnelInfo): string {
91
91
  export function sshListTool(engine: SshEngine) {
92
92
  return defineTool({
93
93
  name: 'ssh_list',
94
- description: 'List configured SSH hosts (alias, host, user, auth, environment, tags, description). Use ssh_exec etc. with the alias. ' +
95
- 'Triggers: SSH, remote server, server IP/hostname, connect/login, check server/status, deploy, upload/download, jump host, tunnel, port forward.',
94
+ description: 'List configured SSH hosts (alias, host, user, auth, environment, tags, description). Use ssh_exec etc. with the alias.',
96
95
  parameters: {
97
96
  query: { type: 'string', description: 'Optional fuzzy match against alias, description, host, and tags.' },
98
97
  },
@@ -129,6 +128,8 @@ export function sshListTool(engine: SshEngine) {
129
128
  },
130
129
  render: (_args, value: { hosts?: AgentHostRow[] }) => text(renderHosts(value.hosts ?? [])),
131
130
  },
131
+ // Reads the local host registry only: safe to overlap with sibling calls.
132
+ isConcurrencySafe: () => true,
132
133
  async execute(args) {
133
134
  return { hosts: engine.list(args.query).map(toAgentHostRow) }
134
135
  },
@@ -139,8 +140,7 @@ export function sshListTool(engine: SshEngine) {
139
140
  export function sshExecTool(engine: SshEngine) {
140
141
  return defineTool({
141
142
  name: 'ssh_exec',
142
- description: 'Execute a shell command on a REMOTE SSH host by alias; the command runs on the remote host, never on this machine. For commands on this machine, use the local bash tool. Prefer combining independent read-only queries into one command. ' +
143
- 'Triggers: run command on server, deploy, check server/status, service control, view logs, any remote operation.',
143
+ description: 'Execute a shell command on a REMOTE SSH host by alias; the command runs on the remote host, never on this machine. For commands on this machine, use the local bash tool. Prefer combining independent read-only queries into one command.',
144
144
  parameters: {
145
145
  alias: { type: 'string', required: true, description: 'Host alias from ssh_list.' },
146
146
  command: { type: 'string', required: true, description: 'The shell command to run remotely.' },
@@ -162,9 +162,11 @@ export function sshExecTool(engine: SshEngine) {
162
162
  },
163
163
  render: (_args, value: ExecResult) => text(renderExec(value)),
164
164
  },
165
- async execute(args) {
165
+ async execute(args, exec) {
166
166
  try {
167
- return await engine.exec(args.alias, args.command, args.timeoutMs)
167
+ // Forward the caller's cancellation: aborting closes the remote channel
168
+ // instead of leaving the command running on the host.
169
+ return await engine.exec(args.alias, args.command, args.timeoutMs, exec.signal)
168
170
  } catch (error) {
169
171
  return {
170
172
  success: false,
@@ -184,8 +186,7 @@ export function sshExecTool(engine: SshEngine) {
184
186
  export function sshUploadTool(engine: SshEngine) {
185
187
  return defineTool({
186
188
  name: 'ssh_upload',
187
- description: 'Transfer a file FROM this machine (the dsh host) TO a remote SSH host. Use this only when the file must be copied to the remote host. Files that stay on this machine are handled with the local file tools (read / write / edit), not ssh_upload. ' +
188
- 'Triggers: upload file to server, deploy artifact, copy config to server.',
189
+ description: 'Transfer a file FROM this machine (the dsh host) TO a remote SSH host. Use this only when the file must be copied to the remote host. Files that stay on this machine are handled with the local file tools (read / write / edit), not ssh_upload.',
189
190
  parameters: {
190
191
  alias: { type: 'string', required: true, description: 'Host alias from ssh_list.' },
191
192
  localPath: { type: 'string', required: true, description: 'Absolute path of the source file on THIS machine (the dsh host) — not a path on the remote host.' },
@@ -221,8 +222,7 @@ export function sshUploadTool(engine: SshEngine) {
221
222
  export function sshDownloadTool(engine: SshEngine) {
222
223
  return defineTool({
223
224
  name: 'ssh_download',
224
- description: 'Copy a remote FILE from a configured SSH host to this machine (the dsh host). Use this only when the source is on the remote host; files already on this machine are read with the local file tools (read / write / edit), not ssh_download. Directory download is not supported — download files individually. ' +
225
- 'Triggers: download file from server, fetch remote log/artifact.',
225
+ description: 'Copy a remote FILE from a configured SSH host to this machine (the dsh host). Use this only when the source is on the remote host; files already on this machine are read with the local file tools (read / write / edit), not ssh_download. Directory download is not supported — download files individually.',
226
226
  parameters: {
227
227
  alias: { type: 'string', required: true, description: 'Host alias from ssh_list.' },
228
228
  remotePath: { type: 'string', required: true, description: 'Absolute path of the source file on the remote SSH host.' },
@@ -257,8 +257,7 @@ export function sshDownloadTool(engine: SshEngine) {
257
257
  export function sshTunnelTool(engine: SshEngine) {
258
258
  return defineTool({
259
259
  name: 'ssh_tunnel',
260
- description: 'Manage local port-forward tunnels to a configured SSH host. Start a tunnel to reach a remote internal service (database, web UI, API) through 127.0.0.1 on this machine. ' +
261
- 'Triggers: tunnel, port forward, connect database, access internal service.',
260
+ description: 'Manage local port-forward tunnels to a configured SSH host. Start a tunnel to reach a remote internal service (database, web UI, API) through 127.0.0.1 on this machine.',
262
261
  parameters: {
263
262
  action: { type: 'string', required: true, enum: ['start', 'list', 'stop', 'stop-all'], description: 'start / list / stop / stop-all.' },
264
263
  alias: { type: 'string', description: 'Host alias (required for start, optional for stop-all).' },
@@ -357,8 +356,7 @@ export function sshTunnelTool(engine: SshEngine) {
357
356
  export function sshClusterTool(engine: SshEngine) {
358
357
  return defineTool({
359
358
  name: 'ssh_cluster',
360
- description: 'Run one command concurrently across selected SSH hosts; at least one aliases, environment, or tags filter is required. ' +
361
- 'Triggers: run on selected servers, batch operation, production servers, cluster command.',
359
+ description: 'Run one command concurrently across selected SSH hosts; at least one aliases, environment, or tags filter is required.',
362
360
  parameters: {
363
361
  command: { type: 'string', required: true, description: 'The shell command to run on every matched host.' },
364
362
  aliases: { type: 'array', items: { type: 'string' }, description: 'Optional alias filter; at least one of aliases, environment, or tags is required.' },
@@ -394,12 +392,13 @@ export function sshClusterTool(engine: SshEngine) {
394
392
  },
395
393
  render: (_args, value: { results?: ClusterResult[] }) => text(renderCluster(value.results ?? [])),
396
394
  },
397
- async execute(args) {
395
+ async execute(args, exec) {
398
396
  const hasSelector = (Array.isArray(args.aliases) && args.aliases.some((alias) => typeof alias === 'string' && alias.trim() !== '')) ||
399
397
  (typeof args.environment === 'string' && args.environment.trim() !== '') ||
400
398
  (Array.isArray(args.tags) && args.tags.some((tag) => typeof tag === 'string' && tag.trim() !== ''))
401
399
  if (!hasSelector) throw new Error('ssh_cluster requires aliases, environment, or tags to limit the target set')
402
- return { results: await engine.cluster(args) }
400
+ // Cancellation reaches every host's in-flight command.
401
+ return { results: await engine.cluster({ ...args, signal: exec.signal }) }
403
402
  },
404
403
  })
405
404
  }
package/lib/invariant.js DELETED
@@ -1,6 +0,0 @@
1
- //#region src/invariant.ts
2
- /** Invariant companion plugin (no assertions — nothing to check at runtime). */
3
- /** Provides no assertions: the ssh plugin owns no cross-package runtime invariants. */
4
- function apply() {}
5
- //#endregion
6
- export { apply };
@@ -1,3 +0,0 @@
1
- /** Invariant companion plugin (no assertions — nothing to check at runtime). */
2
- /** Provides no assertions: the ssh plugin owns no cross-package runtime invariants. */
3
- export declare function apply(): void;
package/src/invariant.ts DELETED
@@ -1,4 +0,0 @@
1
- /** Invariant companion plugin (no assertions — nothing to check at runtime). */
2
-
3
- /** Provides no assertions: the ssh plugin owns no cross-package runtime invariants. */
4
- export function apply(): void {}