@llmindset/hf-mcp 0.2.33 → 0.2.34

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.
Files changed (76) hide show
  1. package/dist/hf-api-call.d.ts.map +1 -1
  2. package/dist/hf-api-call.js +4 -0
  3. package/dist/hf-api-call.js.map +1 -1
  4. package/dist/index.d.ts +1 -0
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +1 -0
  7. package/dist/index.js.map +1 -1
  8. package/dist/jobs/api-client.d.ts +19 -0
  9. package/dist/jobs/api-client.d.ts.map +1 -0
  10. package/dist/jobs/api-client.js +104 -0
  11. package/dist/jobs/api-client.js.map +1 -0
  12. package/dist/jobs/commands/inspect.d.ts +5 -0
  13. package/dist/jobs/commands/inspect.d.ts.map +1 -0
  14. package/dist/jobs/commands/inspect.js +21 -0
  15. package/dist/jobs/commands/inspect.js.map +1 -0
  16. package/dist/jobs/commands/logs.d.ts +4 -0
  17. package/dist/jobs/commands/logs.d.ts.map +1 -0
  18. package/dist/jobs/commands/logs.js +24 -0
  19. package/dist/jobs/commands/logs.js.map +1 -0
  20. package/dist/jobs/commands/ps.d.ts +4 -0
  21. package/dist/jobs/commands/ps.d.ts.map +1 -0
  22. package/dist/jobs/commands/ps.js +23 -0
  23. package/dist/jobs/commands/ps.js.map +1 -0
  24. package/dist/jobs/commands/run.d.ts +5 -0
  25. package/dist/jobs/commands/run.d.ts.map +1 -0
  26. package/dist/jobs/commands/run.js +89 -0
  27. package/dist/jobs/commands/run.js.map +1 -0
  28. package/dist/jobs/commands/scheduled.d.ts +10 -0
  29. package/dist/jobs/commands/scheduled.d.ts.map +1 -0
  30. package/dist/jobs/commands/scheduled.js +111 -0
  31. package/dist/jobs/commands/scheduled.js.map +1 -0
  32. package/dist/jobs/commands/utils.d.ts +19 -0
  33. package/dist/jobs/commands/utils.d.ts.map +1 -0
  34. package/dist/jobs/commands/utils.js +99 -0
  35. package/dist/jobs/commands/utils.js.map +1 -0
  36. package/dist/jobs/formatters.d.ts +6 -0
  37. package/dist/jobs/formatters.d.ts.map +1 -0
  38. package/dist/jobs/formatters.js +98 -0
  39. package/dist/jobs/formatters.js.map +1 -0
  40. package/dist/jobs/sse-handler.d.ts +12 -0
  41. package/dist/jobs/sse-handler.d.ts.map +1 -0
  42. package/dist/jobs/sse-handler.js +80 -0
  43. package/dist/jobs/sse-handler.js.map +1 -0
  44. package/dist/jobs/tool.d.ts +35 -0
  45. package/dist/jobs/tool.d.ts.map +1 -0
  46. package/dist/jobs/tool.js +333 -0
  47. package/dist/jobs/tool.js.map +1 -0
  48. package/dist/jobs/types.d.ts +295 -0
  49. package/dist/jobs/types.d.ts.map +1 -0
  50. package/dist/jobs/types.js +95 -0
  51. package/dist/jobs/types.js.map +1 -0
  52. package/dist/tool-ids.d.ts +3 -2
  53. package/dist/tool-ids.d.ts.map +1 -1
  54. package/dist/tool-ids.js +10 -2
  55. package/dist/tool-ids.js.map +1 -1
  56. package/dist/types/tool-result.d.ts +1 -0
  57. package/dist/types/tool-result.d.ts.map +1 -1
  58. package/package.json +3 -1
  59. package/src/hf-api-call.ts +6 -0
  60. package/src/index.ts +1 -0
  61. package/src/jobs/api-client.ts +195 -0
  62. package/src/jobs/commands/inspect.ts +38 -0
  63. package/src/jobs/commands/logs.ts +36 -0
  64. package/src/jobs/commands/ps.ts +40 -0
  65. package/src/jobs/commands/run.ts +134 -0
  66. package/src/jobs/commands/scheduled.ts +189 -0
  67. package/src/jobs/commands/utils.ts +158 -0
  68. package/src/jobs/formatters.ts +149 -0
  69. package/src/jobs/sse-handler.ts +144 -0
  70. package/src/jobs/tool.ts +435 -0
  71. package/src/jobs/types.ts +237 -0
  72. package/src/tool-ids.ts +11 -1
  73. package/src/types/tool-result.ts +6 -0
  74. package/test/jobs/command-translation.spec.ts +277 -0
  75. package/test/jobs/formatters.spec.ts +267 -0
  76. package/test/jobs/uv-command.spec.ts +81 -0
@@ -0,0 +1,267 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { formatJobsTable, formatScheduledJobsTable, formatJobDetails } from '../../src/jobs/formatters.js';
3
+ import type { JobInfo, ScheduledJobInfo, JobSpec } from '../../src/jobs/types.js';
4
+
5
+ describe('Jobs Formatters', () => {
6
+ describe('formatJobsTable', () => {
7
+ it('should return message for empty job list', () => {
8
+ const result = formatJobsTable([]);
9
+ expect(result).toBe('No jobs found.');
10
+ });
11
+
12
+ it('should format a single job as markdown table', () => {
13
+ const jobs: JobInfo[] = [
14
+ {
15
+ id: 'job123',
16
+ createdAt: '2025-01-20T10:00:00Z',
17
+ dockerImage: 'python:3.12',
18
+ command: ['python', 'script.py'],
19
+ flavor: 'cpu-basic',
20
+ status: { stage: 'RUNNING' },
21
+ owner: { id: 'u123', name: 'testuser', type: 'user' },
22
+ environment: {},
23
+ },
24
+ ];
25
+
26
+ const result = formatJobsTable(jobs);
27
+
28
+ // Should be a markdown table
29
+ expect(result).toContain('| JOB ID');
30
+ expect(result).toContain('| IMAGE/SPACE');
31
+ expect(result).toContain('| COMMAND');
32
+ expect(result).toContain('| CREATED');
33
+ expect(result).toContain('| STATUS');
34
+
35
+ // Should contain separator line
36
+ expect(result).toContain('|---');
37
+
38
+ // Should contain job data
39
+ expect(result).toContain('job123');
40
+ expect(result).toContain('python:3.12');
41
+ expect(result).toContain('python script.py');
42
+ expect(result).toContain('RUNNING');
43
+ });
44
+
45
+ it('should format multiple jobs', () => {
46
+ const jobs: JobInfo[] = [
47
+ {
48
+ id: 'job123',
49
+ createdAt: '2025-01-20T10:00:00Z',
50
+ dockerImage: 'python:3.12',
51
+ command: ['python', 'script.py'],
52
+ flavor: 'cpu-basic',
53
+ status: { stage: 'RUNNING' },
54
+ owner: { id: 'u123', name: 'testuser', type: 'user' },
55
+ environment: {},
56
+ },
57
+ {
58
+ id: 'job456',
59
+ createdAt: '2025-01-20T11:00:00Z',
60
+ dockerImage: 'ubuntu',
61
+ command: ['bash', 'test.sh'],
62
+ flavor: 'a10g-small',
63
+ status: { stage: 'COMPLETED' },
64
+ owner: { id: 'u123', name: 'testuser', type: 'user' },
65
+ environment: {},
66
+ },
67
+ ];
68
+
69
+ const result = formatJobsTable(jobs);
70
+
71
+ // Should contain both jobs
72
+ expect(result).toContain('job123');
73
+ expect(result).toContain('job456');
74
+ expect(result).toContain('python:3.12');
75
+ expect(result).toContain('ubuntu');
76
+ expect(result).toContain('RUNNING');
77
+ expect(result).toContain('COMPLETED');
78
+ });
79
+
80
+ it('should handle Space IDs instead of Docker images', () => {
81
+ const jobs: JobInfo[] = [
82
+ {
83
+ id: 'job789',
84
+ createdAt: '2025-01-20T12:00:00Z',
85
+ spaceId: 'user/myspace',
86
+ command: ['python', 'app.py'],
87
+ flavor: 'cpu-basic',
88
+ status: { stage: 'RUNNING' },
89
+ owner: { id: 'u123', name: 'testuser', type: 'user' },
90
+ environment: {},
91
+ },
92
+ ];
93
+
94
+ const result = formatJobsTable(jobs);
95
+ expect(result).toContain('user/myspace');
96
+ });
97
+
98
+ it('should truncate long values with ellipsis', () => {
99
+ const jobs: JobInfo[] = [
100
+ {
101
+ id: 'a'.repeat(50),
102
+ createdAt: '2025-01-20T10:00:00Z',
103
+ dockerImage: 'very-long-image-name-that-exceeds-column-width',
104
+ command: ['python', '-c', 'print(' + 'x'.repeat(100) + ')'],
105
+ flavor: 'cpu-basic',
106
+ status: { stage: 'RUNNING' },
107
+ owner: { id: 'u123', name: 'testuser', type: 'user' },
108
+ environment: {},
109
+ },
110
+ ];
111
+
112
+ const result = formatJobsTable(jobs);
113
+ expect(result).toContain('...');
114
+ });
115
+ });
116
+
117
+ describe('formatScheduledJobsTable', () => {
118
+ it('should return message for empty scheduled job list', () => {
119
+ const result = formatScheduledJobsTable([]);
120
+ expect(result).toBe('No scheduled jobs found.');
121
+ });
122
+
123
+ it('should format a scheduled job as markdown table', () => {
124
+ const jobSpec: JobSpec = {
125
+ dockerImage: 'python:3.12',
126
+ command: ['python', 'backup.py'],
127
+ flavor: 'cpu-basic',
128
+ };
129
+
130
+ const jobs: ScheduledJobInfo[] = [
131
+ {
132
+ id: 'sched123',
133
+ schedule: '@hourly',
134
+ suspend: false,
135
+ jobSpec,
136
+ lastRun: '2025-01-20T10:00:00Z',
137
+ nextRun: '2025-01-20T11:00:00Z',
138
+ owner: { id: 'u123', name: 'testuser', type: 'user' },
139
+ createdAt: '2025-01-20T09:00:00Z',
140
+ },
141
+ ];
142
+
143
+ const result = formatScheduledJobsTable(jobs);
144
+
145
+ // Should be a markdown table
146
+ expect(result).toContain('| ID');
147
+ expect(result).toContain('| SCHEDULE');
148
+ expect(result).toContain('| IMAGE/SPACE');
149
+ expect(result).toContain('| COMMAND');
150
+ expect(result).toContain('| LAST RUN');
151
+ expect(result).toContain('| NEXT RUN');
152
+ expect(result).toContain('| SUSPENDED');
153
+
154
+ // Should contain job data
155
+ expect(result).toContain('sched123');
156
+ expect(result).toContain('@hourly');
157
+ expect(result).toContain('python:3.12');
158
+ expect(result).toContain('No'); // Not suspended
159
+ });
160
+
161
+ it('should show Yes for suspended jobs', () => {
162
+ const jobSpec: JobSpec = {
163
+ dockerImage: 'ubuntu',
164
+ command: ['bash', 'cleanup.sh'],
165
+ flavor: 'cpu-basic',
166
+ };
167
+
168
+ const jobs: ScheduledJobInfo[] = [
169
+ {
170
+ id: 'sched456',
171
+ schedule: '@daily',
172
+ suspend: true,
173
+ jobSpec,
174
+ owner: { id: 'u123', name: 'testuser', type: 'user' },
175
+ createdAt: '2025-01-20T09:00:00Z',
176
+ },
177
+ ];
178
+
179
+ const result = formatScheduledJobsTable(jobs);
180
+ expect(result).toContain('Yes'); // Suspended
181
+ });
182
+ });
183
+
184
+ describe('formatJobDetails', () => {
185
+ it('should format a single job as JSON in code block', () => {
186
+ const job: JobInfo = {
187
+ id: 'job123',
188
+ createdAt: '2025-01-20T10:00:00Z',
189
+ dockerImage: 'python:3.12',
190
+ command: ['python', 'script.py'],
191
+ flavor: 'cpu-basic',
192
+ status: { stage: 'RUNNING', message: null },
193
+ owner: { id: 'u123', name: 'testuser', type: 'user' },
194
+ environment: {},
195
+ };
196
+
197
+ const result = formatJobDetails(job);
198
+
199
+ // Should be wrapped in code block
200
+ expect(result).toMatch(/^```json\n/);
201
+ expect(result).toMatch(/\n```$/);
202
+
203
+ // Should contain job data as JSON
204
+ expect(result).toContain('"id": "job123"');
205
+ expect(result).toContain('"dockerImage": "python:3.12"');
206
+ expect(result).toContain('"stage": "RUNNING"');
207
+ });
208
+
209
+ it('should format multiple jobs as JSON array', () => {
210
+ const jobs: JobInfo[] = [
211
+ {
212
+ id: 'job123',
213
+ createdAt: '2025-01-20T10:00:00Z',
214
+ dockerImage: 'python:3.12',
215
+ command: ['python', 'script.py'],
216
+ flavor: 'cpu-basic',
217
+ status: { stage: 'RUNNING' },
218
+ owner: { id: 'u123', name: 'testuser', type: 'user' },
219
+ environment: {},
220
+ },
221
+ {
222
+ id: 'job456',
223
+ createdAt: '2025-01-20T11:00:00Z',
224
+ dockerImage: 'ubuntu',
225
+ command: ['bash', 'test.sh'],
226
+ flavor: 'cpu-basic',
227
+ status: { stage: 'COMPLETED' },
228
+ owner: { id: 'u123', name: 'testuser', type: 'user' },
229
+ environment: {},
230
+ },
231
+ ];
232
+
233
+ const result = formatJobDetails(jobs);
234
+
235
+ // Should be wrapped in code block
236
+ expect(result).toMatch(/^```json\n/);
237
+ expect(result).toMatch(/\n```$/);
238
+
239
+ // Should be an array
240
+ expect(result).toContain('[');
241
+ expect(result).toContain(']');
242
+
243
+ // Should contain both jobs
244
+ expect(result).toContain('"id": "job123"');
245
+ expect(result).toContain('"id": "job456"');
246
+ });
247
+
248
+ it('should format JSON with proper indentation', () => {
249
+ const job: JobInfo = {
250
+ id: 'job123',
251
+ createdAt: '2025-01-20T10:00:00Z',
252
+ dockerImage: 'python:3.12',
253
+ command: ['python', 'script.py'],
254
+ flavor: 'cpu-basic',
255
+ status: { stage: 'RUNNING' },
256
+ owner: { id: 'u123', name: 'testuser', type: 'user' },
257
+ environment: {},
258
+ };
259
+
260
+ const result = formatJobDetails(job);
261
+
262
+ // Should have proper indentation (2 spaces)
263
+ expect(result).toContain(' "id"');
264
+ expect(result).toContain(' "createdAt"');
265
+ });
266
+ });
267
+ });
@@ -0,0 +1,81 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import { uvCommand } from '../../src/jobs/commands/run.js';
3
+ import type { JobsApiClient } from '../../src/jobs/api-client.js';
4
+ import type { JobInfo, JobSpec } from '../../src/jobs/types.js';
5
+
6
+ function setupMockClient() {
7
+ let capturedSpec: JobSpec | undefined;
8
+
9
+ const runJob = vi.fn(async (spec: JobSpec) => {
10
+ capturedSpec = spec;
11
+ const job: JobInfo = {
12
+ id: 'job-123',
13
+ createdAt: new Date().toISOString(),
14
+ command: spec.command,
15
+ arguments: spec.arguments,
16
+ environment: spec.environment ?? {},
17
+ secrets: {},
18
+ flavor: spec.flavor,
19
+ status: { stage: 'RUNNING' },
20
+ owner: { id: 'owner-1', name: 'tester', type: 'user' },
21
+ };
22
+ return job;
23
+ });
24
+
25
+ const getLogsUrl = vi.fn(() => 'https://example.test/logs');
26
+
27
+ const client = {
28
+ runJob,
29
+ getLogsUrl,
30
+ } as unknown as JobsApiClient;
31
+
32
+ return {
33
+ client,
34
+ runJob,
35
+ getLogsUrl,
36
+ get lastSpec() {
37
+ return capturedSpec;
38
+ },
39
+ };
40
+ }
41
+
42
+ describe('uvCommand', () => {
43
+ it('wraps inline scripts in a shell pipeline executed via /bin/sh', async () => {
44
+ const harness = setupMockClient();
45
+ const script = 'print("hello")\nprint("world")';
46
+
47
+ await uvCommand({ script, detach: true }, harness.client);
48
+
49
+ expect(harness.runJob).toHaveBeenCalledTimes(1);
50
+ expect(harness.lastSpec).toBeDefined();
51
+ expect(harness.lastSpec?.command).toEqual([
52
+ '/bin/sh',
53
+ '-lc',
54
+ expect.stringContaining('uv run'),
55
+ ]);
56
+
57
+ const encoded = Buffer.from(script).toString('base64');
58
+ expect(harness.lastSpec?.command?.[2]).toContain(`echo "${encoded}" | base64 -d | uv run`);
59
+ expect(harness.lastSpec?.command?.[2]).toContain(' -');
60
+ });
61
+
62
+ it('includes dependency and python flags when provided', async () => {
63
+ const harness = setupMockClient();
64
+ const script = 'print("deps")\nprint("python")';
65
+
66
+ await uvCommand(
67
+ {
68
+ script,
69
+ with_deps: ['numpy', 'pandas'],
70
+ python: '3.12',
71
+ detach: true,
72
+ },
73
+ harness.client
74
+ );
75
+
76
+ const shellCommand = harness.lastSpec?.command?.[2];
77
+ expect(shellCommand).toContain('--with numpy');
78
+ expect(shellCommand).toContain('--with pandas');
79
+ expect(shellCommand).toContain('-p 3.12');
80
+ });
81
+ });