@thinremote/thinr-cli 1.2.0-alpha.1 → 1.3.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/CHANGELOG.md +63 -1
- package/README.md +235 -30
- package/bin/thinr.js +43 -25
- package/commands/_shared.js +154 -0
- package/commands/dashboard.js +17 -0
- package/commands/device/_shared.js +17 -34
- package/commands/device/console.js +1 -0
- package/commands/device/delete.js +53 -0
- package/commands/device/exec.js +7 -6
- package/commands/device/fs.js +193 -0
- package/commands/device/index.js +9 -0
- package/commands/device/list.js +35 -23
- package/commands/device/logs.js +178 -0
- package/commands/device/property.js +30 -28
- package/commands/device/proxy.js +3 -0
- package/commands/device/resource.js +30 -29
- package/commands/device/status.js +35 -14
- package/commands/device/transfer.js +236 -0
- package/commands/device/update.js +15 -10
- package/commands/fleet/index.js +17 -0
- package/commands/fleet/upgrade.js +275 -0
- package/commands/logout.js +10 -11
- package/commands/playbook/_shared.js +12 -0
- package/commands/playbook/index.js +18 -0
- package/commands/playbook/run.js +302 -0
- package/commands/playbook/validate.js +38 -0
- package/commands/product/_shared.js +8 -0
- package/commands/product/exec.js +243 -0
- package/commands/product/fs.js +218 -0
- package/commands/product/index.js +35 -0
- package/commands/product/list.js +86 -0
- package/commands/product/logs.js +289 -0
- package/commands/product/metric.js +124 -0
- package/commands/product/playbook.js +911 -0
- package/commands/product/property.js +76 -0
- package/commands/product/props.js +125 -0
- package/commands/product/push.js +184 -0
- package/commands/product/resource.js +76 -0
- package/commands/product/status.js +213 -0
- package/commands/profile.js +76 -8
- package/dist/dashboard.js +5644 -0
- package/lib/agent-releases.js +59 -0
- package/lib/agent-versions.js +73 -0
- package/lib/alarms.js +252 -0
- package/lib/api.js +8 -2
- package/lib/auth.js +68 -20
- package/lib/bucket.js +87 -0
- package/lib/command-stream.js +213 -0
- package/lib/config.js +5 -3
- package/lib/console.js +19 -5
- package/lib/constants.js +83 -0
- package/lib/dashboard/event-stream.js +239 -0
- package/lib/dashboard/metric-aggregate.js +64 -0
- package/lib/dashboard/metric-filter.js +39 -0
- package/lib/debug-log.js +93 -0
- package/lib/device-api.js +131 -148
- package/lib/devices.js +18 -21
- package/lib/errors.js +12 -0
- package/lib/fleet-upgrade.js +106 -0
- package/lib/format.js +114 -0
- package/lib/mcp/registry.js +14 -0
- package/lib/mcp/tools-alarms.js +510 -0
- package/lib/mcp/tools-buckets.js +103 -0
- package/lib/mcp/tools-devices.js +33 -3
- package/lib/mcp/tools-exec.js +30 -17
- package/lib/mcp/tools-filesystem.js +102 -6
- package/lib/mcp/tools-install.js +153 -0
- package/lib/mcp/tools-logs.js +225 -0
- package/lib/mcp/tools-playbook.js +211 -0
- package/lib/mcp/tools-products.js +1226 -105
- package/lib/mcp/tools-profile.js +634 -0
- package/lib/mcp/tools-tokens.js +371 -0
- package/lib/monitoring.js +26 -1
- package/lib/playbook/actions.js +447 -0
- package/lib/playbook/expression.js +302 -0
- package/lib/playbook/fleet.js +175 -0
- package/lib/playbook/loader.js +247 -0
- package/lib/playbook/runner.js +300 -0
- package/lib/playbook/schema.js +209 -0
- package/lib/playbook/vars.js +209 -0
- package/lib/product/log-presets.js +196 -0
- package/lib/product/logs.js +485 -0
- package/lib/product-orchestrator.js +124 -0
- package/lib/product.js +523 -20
- package/lib/profile.js +665 -0
- package/lib/property.js +7 -40
- package/lib/proxy.js +9 -8
- package/lib/resource.js +10 -14
- package/lib/status.js +78 -37
- package/lib/tokens.js +374 -0
- package/package.json +14 -3
- package/commands/product.js +0 -407
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import { parsePlaybook } from '../playbook/loader.js';
|
|
3
|
+
import { buildDryRunPlan, resolveTargets, runPlaybook } from '../playbook/runner.js';
|
|
4
|
+
import { playbookSchema } from '../playbook/schema.js';
|
|
5
|
+
import { inputError } from '../errors.js';
|
|
6
|
+
|
|
7
|
+
async function toolPlaybookSchema() {
|
|
8
|
+
const schema = playbookSchema();
|
|
9
|
+
return {
|
|
10
|
+
content: [{ type: 'text', text: JSON.stringify(schema, null, 2) }],
|
|
11
|
+
isError: false,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async function toolPlaybookValidate(args) {
|
|
16
|
+
if (typeof args.content !== 'string' || !args.content.trim()) {
|
|
17
|
+
throw inputError('content (YAML playbook source) is required');
|
|
18
|
+
}
|
|
19
|
+
try {
|
|
20
|
+
const pb = parsePlaybook(args.content);
|
|
21
|
+
const summary = {
|
|
22
|
+
valid: true,
|
|
23
|
+
name: pb.name,
|
|
24
|
+
description: pb.description,
|
|
25
|
+
target: pb.target,
|
|
26
|
+
steps: pb.steps.map((s) => ({ name: s.name, action: s.action })),
|
|
27
|
+
};
|
|
28
|
+
return {
|
|
29
|
+
content: [{ type: 'text', text: JSON.stringify(summary, null, 2) }],
|
|
30
|
+
isError: false,
|
|
31
|
+
};
|
|
32
|
+
} catch (err) {
|
|
33
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
34
|
+
return {
|
|
35
|
+
content: [{ type: 'text', text: msg }],
|
|
36
|
+
isError: true,
|
|
37
|
+
_meta: { code: 'input_error', message: msg },
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function toolPlaybookRun(args) {
|
|
43
|
+
if (typeof args.content !== 'string' || !args.content.trim()) {
|
|
44
|
+
throw inputError('content (YAML playbook source) is required');
|
|
45
|
+
}
|
|
46
|
+
const pb = parsePlaybook(args.content);
|
|
47
|
+
if (args.target_product) pb.target.product = args.target_product;
|
|
48
|
+
if (args.target_group) pb.target.group = args.target_group;
|
|
49
|
+
if (Array.isArray(args.target_devices) && args.target_devices.length) {
|
|
50
|
+
pb.target.devices = args.target_devices;
|
|
51
|
+
}
|
|
52
|
+
if (Number.isInteger(args.concurrency) && args.concurrency > 0) {
|
|
53
|
+
pb.target.concurrency = args.concurrency;
|
|
54
|
+
}
|
|
55
|
+
if (typeof args.fail_fast === 'boolean') pb.target.fail_fast = args.fail_fast;
|
|
56
|
+
if (args.vars && typeof args.vars === 'object') {
|
|
57
|
+
pb.vars = { ...pb.vars, ...args.vars };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (args.dry_run) {
|
|
61
|
+
const plan = buildDryRunPlan(pb);
|
|
62
|
+
const out = {
|
|
63
|
+
dry_run: true,
|
|
64
|
+
name: pb.name,
|
|
65
|
+
target: pb.target,
|
|
66
|
+
vars: pb.vars,
|
|
67
|
+
steps: plan,
|
|
68
|
+
};
|
|
69
|
+
return {
|
|
70
|
+
content: [{ type: 'text', text: JSON.stringify(out, null, 2) }],
|
|
71
|
+
isError: false,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const devices = await resolveTargets(pb, { user: args.user });
|
|
76
|
+
if (devices.length === 0) {
|
|
77
|
+
return {
|
|
78
|
+
content: [
|
|
79
|
+
{
|
|
80
|
+
type: 'text',
|
|
81
|
+
text: `No devices match the playbook target (product="${pb.target.product}"${pb.target.group ? `, group="${pb.target.group}"` : ''}).`,
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
isError: false,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const checkMode = !!args.check;
|
|
89
|
+
const results = await runPlaybook(pb, devices, {
|
|
90
|
+
user: args.user,
|
|
91
|
+
concurrency: pb.target.concurrency,
|
|
92
|
+
failFast: pb.target.fail_fast,
|
|
93
|
+
checkMode,
|
|
94
|
+
continueOnError: !!args.continue_on_error,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const okCount = results.filter((r) => r.ok).length;
|
|
98
|
+
let verdicts;
|
|
99
|
+
if (checkMode) {
|
|
100
|
+
verdicts = { changed: 0, unchanged: 0, unknown: 0 };
|
|
101
|
+
for (const r of results) {
|
|
102
|
+
for (const s of r.steps) {
|
|
103
|
+
if (s.verdict && s.verdict in verdicts) verdicts[s.verdict] += 1;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
const summary = {
|
|
108
|
+
name: pb.name,
|
|
109
|
+
target: pb.target,
|
|
110
|
+
mode: checkMode ? 'check' : 'apply',
|
|
111
|
+
total: results.length,
|
|
112
|
+
ok: okCount,
|
|
113
|
+
failed: results.length - okCount,
|
|
114
|
+
...(verdicts ? { verdicts } : {}),
|
|
115
|
+
results,
|
|
116
|
+
};
|
|
117
|
+
return {
|
|
118
|
+
content: [{ type: 'text', text: JSON.stringify(summary, null, 2) }],
|
|
119
|
+
isError: okCount < results.length,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export const tools = [
|
|
124
|
+
{
|
|
125
|
+
name: 'thinr_playbook_schema',
|
|
126
|
+
description: `Return the JSON schema a ThinR playbook document must follow: accepted top-level fields, the target block, the \`vars\` block (plain and extended forms with \`default\` / \`description\` / \`type\` / \`overridable\` / \`required\`), the variable substitution syntax, and the full catalogue of step actions with their parameters. Call this ONCE at the start of any authoring session — whether the user is asking for a one-shot local playbook (\`thinr_playbook_run\`) or a persistent product playbook (\`thinr_product_playbook_write\`) — so the generated YAML validates on the first try.`,
|
|
127
|
+
inputSchema: {
|
|
128
|
+
type: 'object',
|
|
129
|
+
properties: {},
|
|
130
|
+
required: [],
|
|
131
|
+
},
|
|
132
|
+
handler: toolPlaybookSchema,
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
name: 'thinr_playbook_validate',
|
|
136
|
+
description: `Parse and validate a playbook document without running it or uploading it. Returns a structured summary (name, description, target, step list) on success, or a consolidated error message listing every issue. Use this while drafting YAML from a natural-language request: write, call validate, fix the reported errors, repeat — then hand the final content to thinr_product_playbook_write (persistent) or thinr_playbook_run (one-shot). Never upload YAML that hasn't validated cleanly; thinr_product_playbook_write will re-validate and reject invalid documents, but running validate during iteration keeps the loop tight without touching product storage.`,
|
|
137
|
+
inputSchema: {
|
|
138
|
+
type: 'object',
|
|
139
|
+
properties: {
|
|
140
|
+
content: {
|
|
141
|
+
type: 'string',
|
|
142
|
+
description: 'Full YAML source of the playbook.',
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
required: ['content'],
|
|
146
|
+
},
|
|
147
|
+
handler: toolPlaybookValidate,
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
name: 'thinr_playbook_run',
|
|
151
|
+
description: `Run a playbook (YAML source passed inline) against the product / devices declared in its target block. Optional overrides let you retarget a saved playbook without editing it. Set \`dry_run: true\` to get the execution plan without touching any device.
|
|
152
|
+
|
|
153
|
+
Response carries a consolidated report: devices visited, ok / failed counts, and per-device step outcomes (summary, duration, error message if any). \`isError\` is true whenever any device didn't complete every step cleanly, so clients can branch on a single flag.`,
|
|
154
|
+
inputSchema: {
|
|
155
|
+
type: 'object',
|
|
156
|
+
properties: {
|
|
157
|
+
content: {
|
|
158
|
+
type: 'string',
|
|
159
|
+
description: 'Full YAML source of the playbook.',
|
|
160
|
+
},
|
|
161
|
+
dry_run: {
|
|
162
|
+
type: 'boolean',
|
|
163
|
+
description:
|
|
164
|
+
'Return the static plan without contacting devices (default: false).',
|
|
165
|
+
},
|
|
166
|
+
check: {
|
|
167
|
+
type: 'boolean',
|
|
168
|
+
description:
|
|
169
|
+
'Contact devices read-only and report what each step would change, without writing. Each step verdict is one of "changed" / "unchanged" / "unknown". Actions that can\'t be predicted safely (exec, resource) always return "unknown".',
|
|
170
|
+
},
|
|
171
|
+
target_product: {
|
|
172
|
+
type: 'string',
|
|
173
|
+
description: 'Override the playbook\'s target.product.',
|
|
174
|
+
},
|
|
175
|
+
target_group: {
|
|
176
|
+
type: 'string',
|
|
177
|
+
description: 'Override the playbook\'s target.group.',
|
|
178
|
+
},
|
|
179
|
+
target_devices: {
|
|
180
|
+
type: 'array',
|
|
181
|
+
description: 'Override the playbook\'s target.devices with an explicit list.',
|
|
182
|
+
},
|
|
183
|
+
concurrency: {
|
|
184
|
+
type: 'number',
|
|
185
|
+
description: 'Override max parallel devices.',
|
|
186
|
+
},
|
|
187
|
+
fail_fast: {
|
|
188
|
+
type: 'boolean',
|
|
189
|
+
description:
|
|
190
|
+
'Stop dequeueing new devices on the first failure (default: playbook\'s own value).',
|
|
191
|
+
},
|
|
192
|
+
continue_on_error: {
|
|
193
|
+
type: 'boolean',
|
|
194
|
+
description:
|
|
195
|
+
'Keep running subsequent steps on a device even if one step fails (default: false — a failing step stops that device).',
|
|
196
|
+
},
|
|
197
|
+
vars: {
|
|
198
|
+
type: 'object',
|
|
199
|
+
description:
|
|
200
|
+
'Merge into the playbook\'s `vars` block before substitution.',
|
|
201
|
+
},
|
|
202
|
+
user: {
|
|
203
|
+
type: 'string',
|
|
204
|
+
description: 'API user (admin impersonation).',
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
required: ['content'],
|
|
208
|
+
},
|
|
209
|
+
handler: toolPlaybookRun,
|
|
210
|
+
},
|
|
211
|
+
];
|