@thinremote/thinr-cli 1.3.1 → 1.3.3

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.
@@ -5,7 +5,7 @@ import { BYTES, TIMEOUTS } from '../constants.js';
5
5
  import { getAPI } from './helpers.js';
6
6
 
7
7
  async function toolExec(args) {
8
- const { api } = getAPI(args.device, args.user);
8
+ const { api } = getAPI(args.device_id, args.user);
9
9
  // Default guards against hung commands locking the session; callers
10
10
  // override for known-long operations. Knob lives in lib/constants.js.
11
11
  const timeout =
@@ -33,7 +33,7 @@ async function toolExec(args) {
33
33
  }
34
34
 
35
35
  async function toolUpdate(args) {
36
- const { api: deviceApi } = getAPI(args.device, args.user);
36
+ const { api: deviceApi } = getAPI(args.device_id, args.user);
37
37
  const payload = {
38
38
  action: args.action,
39
39
  channel: args.channel || 'latest',
@@ -46,7 +46,7 @@ async function toolUpdate(args) {
46
46
  }
47
47
 
48
48
  async function toolMonitoring(args) {
49
- const device = args.device;
49
+ const device = args.device_id;
50
50
  const user = args.user;
51
51
 
52
52
  // Real-time: read the device's `monitoring` resource directly.
@@ -143,11 +143,11 @@ export const tools = [
143
143
  type: 'object',
144
144
  properties: {
145
145
  command: { type: 'string', description: 'Shell command to execute' },
146
- device: { type: 'string', description: 'Device ID (from thinr_devices)' },
146
+ device_id: { type: 'string', description: 'Device ID (from thinr_devices)' },
147
147
  timeout: { type: 'number', description: 'Timeout in seconds (default: 30)' },
148
148
  user: { type: 'string', description: 'API user (for admin impersonation)' },
149
149
  },
150
- required: ['command', 'device'],
150
+ required: ['command', 'device_id'],
151
151
  },
152
152
  handler: toolExec,
153
153
  },
@@ -157,7 +157,7 @@ export const tools = [
157
157
  inputSchema: {
158
158
  type: 'object',
159
159
  properties: {
160
- device: { type: 'string', description: 'Device ID' },
160
+ device_id: { type: 'string', description: 'Device ID' },
161
161
  action: {
162
162
  type: 'string',
163
163
  description: '"check" to check for updates, "apply" to install the update',
@@ -165,7 +165,7 @@ export const tools = [
165
165
  channel: { type: 'string', description: 'Update channel (default: "latest")' },
166
166
  user: { type: 'string', description: 'API user' },
167
167
  },
168
- required: ['action', 'device'],
168
+ required: ['action', 'device_id'],
169
169
  },
170
170
  handler: toolUpdate,
171
171
  },
@@ -185,14 +185,15 @@ Modes:
185
185
  description:
186
186
  'If true, get live data directly from the device. If false/omitted, query historical data from the monitoring bucket.',
187
187
  },
188
- device: {
188
+ device_id: {
189
189
  type: 'string',
190
190
  description:
191
191
  'Device ID. Required for realtime. Omit for a one-row-per-device fleet view.',
192
192
  },
193
193
  items: {
194
194
  type: 'number',
195
- description: 'Number of data points to return (default: 10). In multi-device mode set ≥ number of devices (e.g. 200) since each device contributes one row.',
195
+ description:
196
+ 'Number of data points to return (default: 10). In multi-device mode set ≥ number of devices (e.g. 200) since each device contributes one row.',
196
197
  },
197
198
  minutes: {
198
199
  type: 'number',
@@ -205,12 +206,18 @@ Modes:
205
206
  },
206
207
  sort: {
207
208
  type: 'string',
208
- description: 'Sort order by timestamp: "asc" or "desc" (default: "desc"). Does NOT sort by metric — do that client-side on the result.',
209
+ description:
210
+ 'Sort order by timestamp: "asc" or "desc" (default: "desc"). Does NOT sort by metric — do that client-side on the result.',
211
+ },
212
+ agg: {
213
+ type: 'string',
214
+ description:
215
+ 'Time-window size for windowed aggregation: "5m", "10m", "1h", "6h". Only meaningful with a specific device. For a per-device single value across the whole range, omit `agg` and pass `agg_type` alone.',
209
216
  },
210
- agg: { type: 'string', description: 'Time-window size for windowed aggregation: "5m", "10m", "1h", "6h". Only meaningful with a specific device. For a per-device single value across the whole range, omit `agg` and pass `agg_type` alone.' },
211
217
  agg_type: {
212
218
  type: 'string',
213
- description: 'Reducer applied to each group/window. Supported: "mean", "max", "min", "sum", "count", "first", "last", "median", "spread" (max-min), "stddev". Most common for monitoring: max, mean, min.',
219
+ description:
220
+ 'Reducer applied to each group/window. Supported: "mean", "max", "min", "sum", "count", "first", "last", "median", "spread" (max-min), "stddev". Most common for monitoring: max, mean, min.',
214
221
  },
215
222
  fields: {
216
223
  type: 'string',
@@ -5,7 +5,7 @@ import { getAPI } from './helpers.js';
5
5
  import { inputError } from '../errors.js';
6
6
 
7
7
  async function toolRead(args) {
8
- const { api } = getAPI(args.device, args.user);
8
+ const { api } = getAPI(args.device_id, args.user);
9
9
  const content = await api.readFile(args.path);
10
10
  return {
11
11
  content: [{ type: 'text', text: content.toString('utf8') }],
@@ -14,7 +14,7 @@ async function toolRead(args) {
14
14
  }
15
15
 
16
16
  async function toolWrite(args) {
17
- const { api } = getAPI(args.device, args.user);
17
+ const { api } = getAPI(args.device_id, args.user);
18
18
  await api.writeFile(args.path, Buffer.from(args.content, 'utf8'));
19
19
  return {
20
20
  content: [{ type: 'text', text: `Written ${args.content.length} bytes to ${args.path}` }],
@@ -23,7 +23,7 @@ async function toolWrite(args) {
23
23
  }
24
24
 
25
25
  async function toolLs(args) {
26
- const { api } = getAPI(args.device, args.user);
26
+ const { api } = getAPI(args.device_id, args.user);
27
27
  const entries = await api.listDir(args.path || '/', args.include_hidden || false);
28
28
  const lines = entries.map((e) => {
29
29
  const type = e.type === 'directory' ? 'd' : '-';
@@ -38,7 +38,7 @@ async function toolLs(args) {
38
38
  }
39
39
 
40
40
  async function toolMkdir(args) {
41
- const { api } = getAPI(args.device, args.user);
41
+ const { api } = getAPI(args.device_id, args.user);
42
42
  await api.mkdir(args.path);
43
43
  return {
44
44
  content: [{ type: 'text', text: `Created directory: ${args.path}` }],
@@ -47,7 +47,7 @@ async function toolMkdir(args) {
47
47
  }
48
48
 
49
49
  async function toolRm(args) {
50
- const { api } = getAPI(args.device, args.user);
50
+ const { api } = getAPI(args.device_id, args.user);
51
51
  await api.delete(args.path, args.recursive !== false);
52
52
  return {
53
53
  content: [{ type: 'text', text: `Deleted: ${args.path}` }],
@@ -56,7 +56,7 @@ async function toolRm(args) {
56
56
  }
57
57
 
58
58
  async function toolMv(args) {
59
- const { api } = getAPI(args.device, args.user);
59
+ const { api } = getAPI(args.device_id, args.user);
60
60
  await api.move(args.source, args.destination, args.overwrite || false);
61
61
  return {
62
62
  content: [{ type: 'text', text: `Moved: ${args.source} → ${args.destination}` }],
@@ -75,7 +75,7 @@ async function toolPush(args) {
75
75
  const remotePath = args.destination.endsWith('/')
76
76
  ? args.destination + basename(localPath)
77
77
  : args.destination;
78
- const { api } = getAPI(args.device, args.user);
78
+ const { api } = getAPI(args.device_id, args.user);
79
79
  const content = await readLocalFile(localPath);
80
80
  await api.writeFile(remotePath, content);
81
81
  return {
@@ -99,7 +99,7 @@ async function toolPull(args) {
99
99
  const localPath = args.destination.endsWith('/')
100
100
  ? resolvePath(args.destination, basename(args.source))
101
101
  : resolvePath(args.destination);
102
- const { api } = getAPI(args.device, args.user);
102
+ const { api } = getAPI(args.device_id, args.user);
103
103
  const buf = await api.readFile(args.source);
104
104
  await writeLocalFile(localPath, buf);
105
105
  return {
@@ -121,10 +121,10 @@ export const tools = [
121
121
  type: 'object',
122
122
  properties: {
123
123
  path: { type: 'string', description: 'Absolute file path on the device' },
124
- device: { type: 'string', description: 'Device ID' },
124
+ device_id: { type: 'string', description: 'Device ID' },
125
125
  user: { type: 'string', description: 'API user' },
126
126
  },
127
- required: ['path', 'device'],
127
+ required: ['path', 'device_id'],
128
128
  },
129
129
  handler: toolRead,
130
130
  },
@@ -136,10 +136,10 @@ export const tools = [
136
136
  properties: {
137
137
  path: { type: 'string', description: 'Absolute file path on the device' },
138
138
  content: { type: 'string', description: 'File content to write' },
139
- device: { type: 'string', description: 'Device ID' },
139
+ device_id: { type: 'string', description: 'Device ID' },
140
140
  user: { type: 'string', description: 'API user' },
141
141
  },
142
- required: ['path', 'content', 'device'],
142
+ required: ['path', 'content', 'device_id'],
143
143
  },
144
144
  handler: toolWrite,
145
145
  },
@@ -154,10 +154,10 @@ export const tools = [
154
154
  type: 'boolean',
155
155
  description: 'Include hidden files (default: false)',
156
156
  },
157
- device: { type: 'string', description: 'Device ID' },
157
+ device_id: { type: 'string', description: 'Device ID' },
158
158
  user: { type: 'string', description: 'API user' },
159
159
  },
160
- required: ['device'],
160
+ required: ['device_id'],
161
161
  },
162
162
  handler: toolLs,
163
163
  },
@@ -168,10 +168,10 @@ export const tools = [
168
168
  type: 'object',
169
169
  properties: {
170
170
  path: { type: 'string', description: 'Directory path to create' },
171
- device: { type: 'string', description: 'Device ID' },
171
+ device_id: { type: 'string', description: 'Device ID' },
172
172
  user: { type: 'string', description: 'API user' },
173
173
  },
174
- required: ['path', 'device'],
174
+ required: ['path', 'device_id'],
175
175
  },
176
176
  handler: toolMkdir,
177
177
  },
@@ -186,10 +186,10 @@ export const tools = [
186
186
  type: 'boolean',
187
187
  description: 'Delete directory contents recursively (default: true)',
188
188
  },
189
- device: { type: 'string', description: 'Device ID' },
189
+ device_id: { type: 'string', description: 'Device ID' },
190
190
  user: { type: 'string', description: 'API user' },
191
191
  },
192
- required: ['path', 'device'],
192
+ required: ['path', 'device_id'],
193
193
  },
194
194
  handler: toolRm,
195
195
  },
@@ -205,10 +205,10 @@ export const tools = [
205
205
  type: 'boolean',
206
206
  description: 'Overwrite destination if exists (default: false)',
207
207
  },
208
- device: { type: 'string', description: 'Device ID' },
208
+ device_id: { type: 'string', description: 'Device ID' },
209
209
  user: { type: 'string', description: 'API user' },
210
210
  },
211
- required: ['source', 'destination', 'device'],
211
+ required: ['source', 'destination', 'device_id'],
212
212
  },
213
213
  handler: toolMv,
214
214
  },
@@ -227,10 +227,10 @@ export const tools = [
227
227
  description:
228
228
  'Absolute path on the device. Trailing "/" keeps the source basename.',
229
229
  },
230
- device: { type: 'string', description: 'Device ID' },
230
+ device_id: { type: 'string', description: 'Device ID' },
231
231
  user: { type: 'string', description: 'API user' },
232
232
  },
233
- required: ['source', 'destination', 'device'],
233
+ required: ['source', 'destination', 'device_id'],
234
234
  },
235
235
  handler: toolPush,
236
236
  },
@@ -249,10 +249,10 @@ export const tools = [
249
249
  description:
250
250
  'Local file path (absolute or relative to the MCP server CWD). Trailing "/" keeps the source basename.',
251
251
  },
252
- device: { type: 'string', description: 'Device ID' },
252
+ device_id: { type: 'string', description: 'Device ID' },
253
253
  user: { type: 'string', description: 'API user' },
254
254
  },
255
- required: ['source', 'destination', 'device'],
255
+ required: ['source', 'destination', 'device_id'],
256
256
  },
257
257
  handler: toolPull,
258
258
  },
@@ -41,7 +41,7 @@ async function toolAgentInstallCommand(args) {
41
41
  const url = `${scheme}://get.thinremote.io/${script}`;
42
42
 
43
43
  const agentArgs = ['install', '--token', shQuote(config.token)];
44
- if (args.device) agentArgs.push('--device', shQuote(args.device));
44
+ if (args.device_id) agentArgs.push('--device', shQuote(args.device_id));
45
45
  if (args.product) agentArgs.push('--product', shQuote(args.product));
46
46
  if (args.overwrite) agentArgs.push('--overwrite');
47
47
  if (args.no_start) agentArgs.push('--no-start');
@@ -83,7 +83,7 @@ async function toolAgentInstallCommand(args) {
83
83
  command,
84
84
  '',
85
85
  'Notes:',
86
- '- The command embeds the active profile\'s API token — treat it as a credential.',
86
+ "- The command embeds the active profile's API token — treat it as a credential.",
87
87
  '- Installing the system service needs root. Pass `sudo: true` if the target host is not already root.',
88
88
  '- The agent reads the target server from the JWT, so `--host` is not needed.',
89
89
  ];
@@ -106,10 +106,10 @@ export const tools = [
106
106
  inputSchema: {
107
107
  type: 'object',
108
108
  properties: {
109
- device: {
109
+ device_id: {
110
110
  type: 'string',
111
111
  description:
112
- 'Custom device identifier. Omit to let the agent default to the target host\'s hostname.',
112
+ "Custom device identifier. Omit to let the agent default to the target host's hostname.",
113
113
  },
114
114
  product: {
115
115
  type: 'string',
@@ -127,8 +127,7 @@ export const tools = [
127
127
  },
128
128
  no_verify_ssl: {
129
129
  type: 'boolean',
130
- description:
131
- 'Disable TLS certificate verification (for self-signed servers).',
130
+ description: 'Disable TLS certificate verification (for self-signed servers).',
132
131
  },
133
132
  channel: {
134
133
  type: 'string',
@@ -170,15 +170,15 @@ Response carries a consolidated report: devices visited, ok / failed counts, and
170
170
  },
171
171
  target_product: {
172
172
  type: 'string',
173
- description: 'Override the playbook\'s target.product.',
173
+ description: "Override the playbook's target.product.",
174
174
  },
175
175
  target_group: {
176
176
  type: 'string',
177
- description: 'Override the playbook\'s target.group.',
177
+ description: "Override the playbook's target.group.",
178
178
  },
179
179
  target_devices: {
180
180
  type: 'array',
181
- description: 'Override the playbook\'s target.devices with an explicit list.',
181
+ description: "Override the playbook's target.devices with an explicit list.",
182
182
  },
183
183
  concurrency: {
184
184
  type: 'number',
@@ -187,7 +187,7 @@ Response carries a consolidated report: devices visited, ok / failed counts, and
187
187
  fail_fast: {
188
188
  type: 'boolean',
189
189
  description:
190
- 'Stop dequeueing new devices on the first failure (default: playbook\'s own value).',
190
+ "Stop dequeueing new devices on the first failure (default: playbook's own value).",
191
191
  },
192
192
  continue_on_error: {
193
193
  type: 'boolean',
@@ -196,8 +196,7 @@ Response carries a consolidated report: devices visited, ok / failed counts, and
196
196
  },
197
197
  vars: {
198
198
  type: 'object',
199
- description:
200
- 'Merge into the playbook\'s `vars` block before substitution.',
199
+ description: "Merge into the playbook's `vars` block before substitution.",
201
200
  },
202
201
  user: {
203
202
  type: 'string',
@@ -141,20 +141,14 @@ async function toolProductUpdate(args) {
141
141
 
142
142
  const fields = Object.keys(patch);
143
143
  if (fields.length === 0) {
144
- throw inputError(
145
- 'at least one of name, description, enabled, icon must be provided',
146
- );
144
+ throw inputError('at least one of name, description, enabled, icon must be provided');
147
145
  }
148
146
 
149
147
  await updateProduct(args.product, args.user, patch);
150
148
 
151
- const summary = fields
152
- .map((f) => (f === 'config' ? 'icon' : f))
153
- .join(', ');
149
+ const summary = fields.map((f) => (f === 'config' ? 'icon' : f)).join(', ');
154
150
  return {
155
- content: [
156
- { type: 'text', text: `Updated product "${args.product}" (${summary}).` },
157
- ],
151
+ content: [{ type: 'text', text: `Updated product "${args.product}" (${summary}).` }],
158
152
  isError: false,
159
153
  };
160
154
  }
@@ -430,7 +424,7 @@ async function toolProductPlaybookWrite(args) {
430
424
  async function toolProductPlaybookRun(args) {
431
425
  if (!args.product) throw inputError('product is required');
432
426
  if (!args.name) throw inputError('name is required');
433
- if (!args.device) throw inputError('device is required (single-device only)');
427
+ if (!args.device_id) throw inputError('device is required (single-device only)');
434
428
 
435
429
  const entry = await findProductPlaybook(args.product, args.name, args.user);
436
430
  if (!entry) {
@@ -453,7 +447,7 @@ async function toolProductPlaybookRun(args) {
453
447
  throw inputError(err.message);
454
448
  }
455
449
 
456
- pb.target.devices = [args.device];
450
+ pb.target.devices = [args.device_id];
457
451
  pb.target.product = args.product;
458
452
  pb.target.group = null;
459
453
 
@@ -463,7 +457,7 @@ async function toolProductPlaybookRun(args) {
463
457
  dry_run: true,
464
458
  product: args.product,
465
459
  name: args.name,
466
- device: args.device,
460
+ device: args.device_id,
467
461
  variables: listVariables(pb),
468
462
  vars: resolvedScope,
469
463
  steps: plan,
@@ -475,11 +469,10 @@ async function toolProductPlaybookRun(args) {
475
469
  }
476
470
 
477
471
  const all = await getDevices({ product: args.product }, args.user);
478
- const device =
479
- all.find((d) => d.device === args.device) || {
480
- device: args.device,
481
- connection: { active: false },
482
- };
472
+ const device = all.find((d) => d.device === args.device_id) || {
473
+ device: args.device_id,
474
+ connection: { active: false },
475
+ };
483
476
 
484
477
  const results = await runPlaybook(pb, [device], {
485
478
  user: args.user,
@@ -492,7 +485,7 @@ async function toolProductPlaybookRun(args) {
492
485
  const payload = {
493
486
  product: args.product,
494
487
  name: args.name,
495
- device: args.device,
488
+ device: args.device_id,
496
489
  mode: args.check ? 'check' : 'apply',
497
490
  ok: !!result?.ok,
498
491
  result,
@@ -508,9 +501,7 @@ async function toolProductPlaybookRollout(args) {
508
501
  if (!args.name) throw inputError('name is required');
509
502
 
510
503
  const batchSize =
511
- Number.isFinite(args.batch_size) && args.batch_size > 0
512
- ? Math.floor(args.batch_size)
513
- : 5;
504
+ Number.isFinite(args.batch_size) && args.batch_size > 0 ? Math.floor(args.batch_size) : 5;
514
505
  const failureThreshold =
515
506
  args.failure_threshold === undefined || args.failure_threshold === null
516
507
  ? DEFAULT_FAILURE_THRESHOLD
@@ -1455,9 +1446,10 @@ Pass \`dry_run: true\` to get the resolved plan without contacting the device. P
1455
1446
  type: 'string',
1456
1447
  description: 'Playbook name as registered in the product index.',
1457
1448
  },
1458
- device: {
1449
+ device_id: {
1459
1450
  type: 'string',
1460
- description: 'Target device ID (single-device only). Pick any device of the product.',
1451
+ description:
1452
+ 'Target device ID (single-device only). Pick any device of the product.',
1461
1453
  },
1462
1454
  vars: {
1463
1455
  type: 'object',
@@ -1477,7 +1469,7 @@ Pass \`dry_run: true\` to get the resolved plan without contacting the device. P
1477
1469
  },
1478
1470
  user: { type: 'string', description: 'API user (admin impersonation).' },
1479
1471
  },
1480
- required: ['product', 'name', 'device'],
1472
+ required: ['product', 'name', 'device_id'],
1481
1473
  },
1482
1474
  handler: toolProductPlaybookRun,
1483
1475
  },
@@ -401,7 +401,8 @@ Gotchas:
401
401
  },
402
402
  enabled: {
403
403
  type: 'boolean',
404
- description: 'Default: true. Set to false to stage a definition without activating it.',
404
+ description:
405
+ 'Default: true. Set to false to stage a definition without activating it.',
405
406
  },
406
407
  user: { type: 'string', description: 'API user.' },
407
408
  },
@@ -3,17 +3,17 @@ import { getDeviceProperties, getDeviceProperty, setDeviceProperty } from '../pr
3
3
  import { inputError } from '../errors.js';
4
4
 
5
5
  async function toolPropertyGet(args) {
6
- const device = args.device;
6
+ const device = args.device_id;
7
7
  if (!device)
8
8
  throw inputError('No device specified. Use thinr_devices to list available devices.');
9
9
  if (args.property) {
10
- const value = await getDeviceProperty(device, args.property);
10
+ const value = await getDeviceProperty(device, args.property, args.user);
11
11
  return {
12
12
  content: [{ type: 'text', text: JSON.stringify(value, null, 2) }],
13
13
  isError: false,
14
14
  };
15
15
  }
16
- const list = await getDeviceProperties(device);
16
+ const list = await getDeviceProperties(device, args.user);
17
17
  const names = Array.isArray(list) ? list.map((p) => p.property).filter(Boolean) : [];
18
18
  return {
19
19
  content: [{ type: 'text', text: names.length ? names.join('\n') : '(no properties)' }],
@@ -22,12 +22,12 @@ async function toolPropertyGet(args) {
22
22
  }
23
23
 
24
24
  async function toolPropertySet(args) {
25
- const device = args.device;
25
+ const device = args.device_id;
26
26
  if (!device)
27
27
  throw inputError('No device specified. Use thinr_devices to list available devices.');
28
28
  if (!args.property) throw inputError('property is required');
29
29
  if (args.value === undefined) throw inputError('value is required');
30
- const saved = await setDeviceProperty(device, args.property, args.value);
30
+ const saved = await setDeviceProperty(device, args.property, args.value, args.user);
31
31
  return {
32
32
  content: [
33
33
  {
@@ -51,10 +51,10 @@ export const tools = [
51
51
  description:
52
52
  'Property name. Omit to list the names of all properties on the device.',
53
53
  },
54
- device: { type: 'string', description: 'Device ID' },
54
+ device_id: { type: 'string', description: 'Device ID' },
55
55
  user: { type: 'string', description: 'API user' },
56
56
  },
57
- required: ['device'],
57
+ required: ['device_id'],
58
58
  },
59
59
  handler: toolPropertyGet,
60
60
  },
@@ -69,10 +69,10 @@ export const tools = [
69
69
  description:
70
70
  'Any JSON-serialisable value (string, number, boolean, object, array).',
71
71
  },
72
- device: { type: 'string', description: 'Device ID' },
72
+ device_id: { type: 'string', description: 'Device ID' },
73
73
  user: { type: 'string', description: 'API user' },
74
74
  },
75
- required: ['property', 'value', 'device'],
75
+ required: ['property', 'value', 'device_id'],
76
76
  },
77
77
  handler: toolPropertySet,
78
78
  },
@@ -3,7 +3,7 @@ import { callDeviceResource, listDeviceResourcesWithSchemas } from '../resource.
3
3
  import { inputError } from '../errors.js';
4
4
 
5
5
  async function toolResourceList(args) {
6
- const device = args.device;
6
+ const device = args.device_id;
7
7
  if (!device)
8
8
  throw inputError('No device specified. Use thinr_devices to list available devices.');
9
9
  const entries = await listDeviceResourcesWithSchemas(device, { user: args.user });
@@ -22,7 +22,7 @@ async function toolResourceList(args) {
22
22
  }
23
23
 
24
24
  async function toolResourceCall(args) {
25
- const device = args.device;
25
+ const device = args.device_id;
26
26
  if (!device)
27
27
  throw inputError('No device specified. Use thinr_devices to list available devices.');
28
28
  if (!args.resource) throw inputError('resource is required');
@@ -47,10 +47,10 @@ export const tools = [
47
47
  inputSchema: {
48
48
  type: 'object',
49
49
  properties: {
50
- device: { type: 'string', description: 'Device ID' },
50
+ device_id: { type: 'string', description: 'Device ID' },
51
51
  user: { type: 'string', description: 'API user' },
52
52
  },
53
- required: ['device'],
53
+ required: ['device_id'],
54
54
  },
55
55
  handler: toolResourceList,
56
56
  },
@@ -68,10 +68,10 @@ export const tools = [
68
68
  description:
69
69
  'JSON object with the input parameters declared by the resource. Omit or pass {} for input-less resources.',
70
70
  },
71
- device: { type: 'string', description: 'Device ID' },
71
+ device_id: { type: 'string', description: 'Device ID' },
72
72
  user: { type: 'string', description: 'API user' },
73
73
  },
74
- required: ['resource', 'device'],
74
+ required: ['resource', 'device_id'],
75
75
  },
76
76
  handler: toolResourceCall,
77
77
  },
@@ -4,7 +4,7 @@ import { inputError } from '../errors.js';
4
4
  import { getAPI } from './helpers.js';
5
5
 
6
6
  async function toolScriptList(args) {
7
- const device = args.device;
7
+ const device = args.device_id;
8
8
  if (!device)
9
9
  throw inputError('No device specified. Use thinr_devices to list available devices.');
10
10
  const info = await readDeviceResource(device, '$scripts/info', { user: args.user });
@@ -25,7 +25,7 @@ async function toolScriptList(args) {
25
25
  }
26
26
 
27
27
  async function toolScriptWrite(args) {
28
- const device = args.device;
28
+ const device = args.device_id;
29
29
  if (!device)
30
30
  throw inputError('No device specified. Use thinr_devices to list available devices.');
31
31
  if (!args.name) throw inputError('name is required');
@@ -76,7 +76,7 @@ async function toolScriptWrite(args) {
76
76
  }
77
77
 
78
78
  async function toolScriptDelete(args) {
79
- const device = args.device;
79
+ const device = args.device_id;
80
80
  if (!device)
81
81
  throw inputError('No device specified. Use thinr_devices to list available devices.');
82
82
  if (!args.name) throw inputError('name is required');
@@ -113,10 +113,10 @@ export const tools = [
113
113
  inputSchema: {
114
114
  type: 'object',
115
115
  properties: {
116
- device: { type: 'string', description: 'Device ID' },
116
+ device_id: { type: 'string', description: 'Device ID' },
117
117
  user: { type: 'string', description: 'API user' },
118
118
  },
119
- required: ['device'],
119
+ required: ['device_id'],
120
120
  },
121
121
  handler: toolScriptList,
122
122
  },
@@ -144,10 +144,10 @@ This tool writes the script, makes it executable, and calls the agent's \`$scrip
144
144
  type: 'string',
145
145
  description: 'Full script source code, including shebang.',
146
146
  },
147
- device: { type: 'string', description: 'Device ID' },
147
+ device_id: { type: 'string', description: 'Device ID' },
148
148
  user: { type: 'string', description: 'API user' },
149
149
  },
150
- required: ['name', 'content', 'device'],
150
+ required: ['name', 'content', 'device_id'],
151
151
  },
152
152
  handler: toolScriptWrite,
153
153
  },
@@ -162,10 +162,10 @@ This tool writes the script, makes it executable, and calls the agent's \`$scrip
162
162
  description:
163
163
  'Name of the script to remove (must match the file basename, without extension).',
164
164
  },
165
- device: { type: 'string', description: 'Device ID' },
165
+ device_id: { type: 'string', description: 'Device ID' },
166
166
  user: { type: 'string', description: 'API user' },
167
167
  },
168
- required: ['name', 'device'],
168
+ required: ['name', 'device_id'],
169
169
  },
170
170
  handler: toolScriptDelete,
171
171
  },