mcoda 0.1.76 → 0.1.78

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 CHANGED
@@ -4,6 +4,10 @@
4
4
  - Initial npm packaging scaffold for the mcoda CLI.
5
5
  - Added bundled mswarm consent terms plus guided `mcoda setup`/postinstall consent bootstrap for installed CLI packages.
6
6
 
7
+ ## 0.1.78
8
+ - Added owner-local GPU job commands: `mcoda gpu list`, `mcoda gpu ops`, and GPU-aware `mcoda job artifact upload|run|status|logs|events|artifacts|cancel|retry`.
9
+ - Added README and usage docs for the generic GPU job connection flags and environment fallbacks.
10
+
7
11
  ## 0.1.76
8
12
  - Added top-level help aliases: `mcoda help`, `mcoda --help`, `mcoda -h`, and `mcoda -H`.
9
13
  - Release v0.1.76.
package/README.md CHANGED
@@ -34,6 +34,7 @@ mcoda docs pdr generate --workspace-root . --project WEB --rfp-path docs/rfp/web
34
34
  - Execution: `mcoda add-tests`, `mcoda work-on-tasks`, `mcoda code-review`, `mcoda qa-tasks`
35
35
  - Backlog: `mcoda backlog`, `mcoda task`
36
36
  - Jobs/telemetry: `mcoda jobs`, `mcoda tokens`, `mcoda telemetry`
37
+ - Owner-local GPU jobs: `mcoda gpu list`, `mcoda gpu ops`, `mcoda job artifact upload|run|status|logs|events|artifacts|cancel|retry`
37
38
  - Agents: `mcoda test-agent`, `mcoda agent-run`
38
39
  - Updates: `mcoda update --check`
39
40
 
@@ -45,6 +46,7 @@ If that sufficiency pass errors, create-tasks continues (fail-open) and records
45
46
  Environment variables are optional overrides for workspace settings:
46
47
  - `MCODA_DOCDEX_URL` to point at a docdex server.
47
48
  - `MCODA_API_BASE_URL` or `MCODA_JOBS_API_URL` for job APIs.
49
+ - `MCODA_MSWARM_NODE_BASE_URL`, `MCODA_MSWARM_NODE_ID`, and `MCODA_MSWARM_NODE_SIGNING_SECRET` for owner-local generic GPU job commands.
48
50
  - `MCODA_TELEMETRY` set to `off` to disable telemetry.
49
51
  - `MCODA_STREAM_IO=1` to emit agent I/O lines to stderr.
50
52
 
@@ -1 +1 @@
1
- {"version":3,"file":"McodaEntrypoint.d.ts","sourceRoot":"","sources":["../../src/bin/McodaEntrypoint.ts"],"names":[],"mappings":";AAuDA,qBAAa,eAAe;WACb,GAAG,CAAC,IAAI,GAAE,MAAM,EAA0B,GAAG,OAAO,CAAC,IAAI,CAAC;CAoPxE"}
1
+ {"version":3,"file":"McodaEntrypoint.d.ts","sourceRoot":"","sources":["../../src/bin/McodaEntrypoint.ts"],"names":[],"mappings":";AAyDA,qBAAa,eAAe;WACb,GAAG,CAAC,IAAI,GAAE,MAAM,EAA0B,GAAG,OAAO,CAAC,IAAI,CAAC;CAwPxE"}
@@ -6,6 +6,7 @@ import { AgentsCommands } from '../commands/agents/AgentsCommands.js';
6
6
  import { CloudCommands } from '../commands/cloud/CloudCommands.js';
7
7
  import { SelfHostedCommands } from '../commands/self-hosted/SelfHostedCommands.js';
8
8
  import { WorkersCommands } from '../commands/workers/WorkersCommands.js';
9
+ import { GpuCommands } from '../commands/gpu/GpuCommands.js';
9
10
  import { ConfigCommands } from '../commands/config/ConfigCommands.js';
10
11
  import { ConsentCommands } from '../commands/consent/ConsentCommands.js';
11
12
  import { GatewayAgentCommand } from '../commands/agents/GatewayAgentCommand.js';
@@ -35,7 +36,7 @@ import { AgentRunCommand } from '../commands/agents/AgentRunCommand.js';
35
36
  import { SetWorkspaceCommand } from '../commands/workspace/SetWorkspaceCommand.js';
36
37
  import { ProjectGuidanceCommand } from '../commands/workspace/ProjectGuidanceCommand.js';
37
38
  import { MswarmConfigStore } from '@mcoda/core';
38
- const TOP_LEVEL_USAGE = 'Usage: mcoda <agent|cloud|cloud-agent|self-hosted|self-hosted-agent|workers|worker|config|consent|setup|gateway-agent|test-agent|agent-run|routing|docs|openapi|job|jobs|tokens|telemetry|create-tasks|migrate-tasks|refine-tasks|task-sufficiency-audit|sds-preflight|order-tasks|tasks|add-tests|work-on-tasks|gateway-trio|code-review|qa-tasks|backlog|task|task-detail|estimate|update|set-workspace|project-guidance|pdr|sds> [...args]\n' +
39
+ const TOP_LEVEL_USAGE = 'Usage: mcoda <agent|cloud|cloud-agent|self-hosted|self-hosted-agent|workers|worker|gpu|config|consent|setup|gateway-agent|test-agent|agent-run|routing|docs|openapi|job|jobs|tokens|telemetry|create-tasks|migrate-tasks|refine-tasks|task-sufficiency-audit|sds-preflight|order-tasks|tasks|add-tests|work-on-tasks|gateway-trio|code-review|qa-tasks|backlog|task|task-detail|estimate|update|set-workspace|project-guidance|pdr|sds> [...args]\n' +
39
40
  'Setup: use `mcoda setup` after installation (or accept the postinstall prompt) to complete the mandatory mswarm telemetry consent flow.\n' +
40
41
  'Config: use `mcoda config set mswarm-api-key <KEY>` to persist an encrypted mswarm API key in the resolved global mcoda config file.\n' +
41
42
  'Consent: use `mcoda consent accept` before other commands if you need to complete consent outside the guided setup flow.\n' +
@@ -43,10 +44,11 @@ const TOP_LEVEL_USAGE = 'Usage: mcoda <agent|cloud|cloud-agent|self-hosted|self-
43
44
  'Cloud agents: use `mcoda cloud agent list|details|sync` to discover and materialize mswarm-managed remote agents.\n' +
44
45
  'Self-hosted agents: use `mcoda self-hosted agent list|details|sync` to discover and materialize owner-hosted mswarm agents.\n' +
45
46
  'Workers: use `mcoda workers list|details|sync|run` to discover, materialize, and invoke mswarm Workers.\n' +
47
+ 'GPU jobs: use `mcoda gpu list` and `mcoda job artifact upload|run|status|logs|events|artifacts|cancel` for owner-local generic GPU jobs.\n' +
46
48
  'Expose this machine: install `@mcoda/mswarm`, then run `mswarm install <KEY>`.\n' +
47
49
  'Aliases: `tasks order-by-deps` forwards to `order-tasks` (dependency-aware ordering), `task`/`task-detail` show a single task.\n' +
48
50
  'Help: use `mcoda help`, `mcoda --help`, `mcoda -h`, or `mcoda -H` for this overview.\n' +
49
- 'Job commands (mcoda job --help for details): list|status|watch|logs|inspect|resume|cancel|tokens\n' +
51
+ 'Job commands (mcoda job --help for details): list|status|watch|logs|inspect|resume|cancel|tokens plus GPU job artifact|run|events|artifacts with node options\n' +
50
52
  'Jobs API required for job commands (set MCODA_API_BASE_URL/MCODA_JOBS_API_URL or workspace api.baseUrl). status/watch/logs exit non-zero on failed/cancelled jobs per SDS.';
51
53
  const TOP_LEVEL_HELP_COMMANDS = new Set(['--help', '-h', '-H', 'help']);
52
54
  export class McodaEntrypoint {
@@ -135,6 +137,10 @@ export class McodaEntrypoint {
135
137
  await WorkersCommands.run(rest);
136
138
  return;
137
139
  }
140
+ if (command === 'gpu') {
141
+ await GpuCommands.run(rest);
142
+ return;
143
+ }
138
144
  if (command === 'config') {
139
145
  await ConfigCommands.run(rest);
140
146
  return;
@@ -0,0 +1,8 @@
1
+ export declare class GpuCommands {
2
+ static run(argv: string[]): Promise<void>;
3
+ }
4
+ export declare class GpuJobCommands {
5
+ static shouldHandle(argv: string[]): boolean;
6
+ static run(argv: string[]): Promise<void>;
7
+ }
8
+ //# sourceMappingURL=GpuCommands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GpuCommands.d.ts","sourceRoot":"","sources":["../../../src/commands/gpu/GpuCommands.ts"],"names":[],"mappings":"AAoSA,qBAAa,WAAW;WACT,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAiChD;AAED,qBAAa,cAAc;IACzB,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO;WAQ/B,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CA8FhD"}
@@ -0,0 +1,382 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { basename } from "node:path";
3
+ import { readFile } from "node:fs/promises";
4
+ import YAML from "yaml";
5
+ import { MswarmApi } from "@mcoda/core";
6
+ const GPU_USAGE = `
7
+ Usage: mcoda gpu <list|ops> [options]
8
+
9
+ Commands:
10
+ gpu list List owner-local GPU/job capability projection
11
+ gpu ops Show owner-local generic job queue, usage, quota, and audit summary
12
+
13
+ Connection options:
14
+ --node-base-url <URL> Owner-local node URL (or MCODA_MSWARM_NODE_BASE_URL)
15
+ --node-id <ID> Node id for signing capability requests
16
+ --signing-secret <KEY> Owner-local generic job signing secret
17
+ --token <TOKEN> Pre-signed capability or ops token
18
+ --timeout-ms <N> Request timeout
19
+ --audit-limit <N> Audit rows for gpu ops (default 50, max 250)
20
+ --audit-offset <N> Audit row offset for gpu ops
21
+ --json Emit JSON
22
+ `.trim();
23
+ const JOB_USAGE = `
24
+ Usage: mcoda job <artifact|run|status|logs|events|artifacts|cancel|retry> [options]
25
+
26
+ GPU job commands:
27
+ job artifact upload <FILE>
28
+ --job-id <ID> --request-id <ID> --node-id <ID> --job-type <TYPE>
29
+ [--artifact-path <PATH>] [--artifact-name <NAME>] [--content-type <TYPE>]
30
+ job run --job-file <FILE> [--wait] [--json]
31
+ job run --type <TYPE> --payload-file <FILE> [--wait] [--json]
32
+ job status <JOB_ID> [--job-file <FILE> | --node-id ... --request-id ... --job-type ...]
33
+ job logs <JOB_ID>
34
+ job events <JOB_ID>
35
+ job artifacts <JOB_ID>
36
+ job cancel <JOB_ID>
37
+ job retry <JOB_ID>
38
+
39
+ Connection options:
40
+ --node-base-url <URL> Owner-local node URL (or MCODA_MSWARM_NODE_BASE_URL)
41
+ --node-id <ID> Node id for signing requests
42
+ --signing-secret <KEY> Owner-local generic job signing secret
43
+ --token <TOKEN> Pre-signed generic job token
44
+ --timeout-ms <N> Request timeout
45
+ --json Emit JSON
46
+ `.trim();
47
+ const GPU_JOB_SUBCOMMANDS = new Set(["artifact", "run", "events", "artifacts"]);
48
+ const SHARED_JOB_SUBCOMMANDS = new Set(["status", "logs", "cancel", "retry"]);
49
+ const GPU_JOB_FLAGS = new Set([
50
+ "--gpu",
51
+ "--node-base-url",
52
+ "--node-id",
53
+ "--signing-secret",
54
+ "--token",
55
+ "--job-file",
56
+ "--payload-file",
57
+ "--job-type",
58
+ "--type",
59
+ "--schema-version",
60
+ "--request-id"
61
+ ]);
62
+ const TERMINAL_STATES = new Set(["succeeded", "failed", "cancelled", "expired", "blocked"]);
63
+ const parseArgs = (argv) => {
64
+ const flags = {};
65
+ const positionals = [];
66
+ for (let index = 0; index < argv.length; index += 1) {
67
+ const arg = argv[index];
68
+ if (arg.startsWith("--")) {
69
+ const key = arg.replace(/^--/, "");
70
+ const next = argv[index + 1];
71
+ if (next && !next.startsWith("--")) {
72
+ const current = flags[key];
73
+ if (current === undefined)
74
+ flags[key] = next;
75
+ else if (Array.isArray(current))
76
+ flags[key] = [...current, next];
77
+ else if (typeof current === "string")
78
+ flags[key] = [current, next];
79
+ else
80
+ flags[key] = [next];
81
+ index += 1;
82
+ }
83
+ else {
84
+ flags[key] = true;
85
+ }
86
+ continue;
87
+ }
88
+ positionals.push(arg);
89
+ }
90
+ return { flags, positionals };
91
+ };
92
+ const resolveString = (value) => {
93
+ if (value === undefined || typeof value === "boolean")
94
+ return undefined;
95
+ return Array.isArray(value) ? value[value.length - 1] : value;
96
+ };
97
+ const resolvePositiveInt = (value, label) => {
98
+ const raw = resolveString(value);
99
+ if (raw === undefined)
100
+ return undefined;
101
+ const parsed = Number.parseInt(raw, 10);
102
+ if (!Number.isFinite(parsed) || parsed <= 0) {
103
+ throw new Error(`Invalid ${label}; expected a positive integer`);
104
+ }
105
+ return parsed;
106
+ };
107
+ const resolveNonNegativeInt = (value, label) => {
108
+ const raw = resolveString(value);
109
+ if (raw === undefined)
110
+ return undefined;
111
+ const parsed = Number.parseInt(raw, 10);
112
+ if (!Number.isFinite(parsed) || parsed < 0) {
113
+ throw new Error(`Invalid ${label}; expected a non-negative integer`);
114
+ }
115
+ return parsed;
116
+ };
117
+ const nodeBaseUrl = (parsed) => resolveString(parsed.flags["node-base-url"]) || process.env.MCODA_MSWARM_NODE_BASE_URL;
118
+ const nodeId = (parsed) => resolveString(parsed.flags["node-id"]) || process.env.MCODA_MSWARM_NODE_ID || process.env.MSWARM_SELF_HOSTED_NODE_ID;
119
+ const signingSecret = (parsed) => resolveString(parsed.flags["signing-secret"]) ||
120
+ process.env.MCODA_MSWARM_NODE_SIGNING_SECRET ||
121
+ process.env.MSWARM_SELF_HOSTED_INVOCATION_SIGNING_SECRET;
122
+ const baseAuth = (parsed) => ({
123
+ nodeBaseUrl: nodeBaseUrl(parsed),
124
+ token: resolveString(parsed.flags.token),
125
+ signingSecret: signingSecret(parsed),
126
+ tokenTtlSeconds: resolvePositiveInt(parsed.flags["token-ttl-seconds"], "--token-ttl-seconds")
127
+ });
128
+ const createApi = async (parsed) => MswarmApi.create({
129
+ baseUrl: nodeBaseUrl(parsed),
130
+ apiKey: resolveString(parsed.flags["api-key"]),
131
+ timeoutMs: resolvePositiveInt(parsed.flags["timeout-ms"], "--timeout-ms")
132
+ });
133
+ const readStructuredFile = async (file) => {
134
+ const raw = await readFile(file, "utf8");
135
+ if (file.endsWith(".yaml") || file.endsWith(".yml")) {
136
+ return YAML.parse(raw);
137
+ }
138
+ return JSON.parse(raw);
139
+ };
140
+ const asRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value)
141
+ ? value
142
+ : {};
143
+ const readJobEnvelope = async (parsed) => {
144
+ const file = resolveString(parsed.flags["job-file"]) || resolveString(parsed.flags["payload-file"]);
145
+ if (!file) {
146
+ throw new Error("GPU job run requires --job-file or --payload-file");
147
+ }
148
+ const payload = asRecord(await readStructuredFile(file));
149
+ if (typeof payload.job_id === "string" && typeof payload.request_id === "string" && typeof payload.node_id === "string") {
150
+ return payload;
151
+ }
152
+ const schemaVersion = resolveString(parsed.flags["schema-version"]) ||
153
+ (typeof payload.schema_version === "string" ? payload.schema_version : "2026-06-14");
154
+ const jobType = resolveString(parsed.flags.type) ||
155
+ resolveString(parsed.flags["job-type"]) ||
156
+ (typeof payload.job_type === "string" ? payload.job_type : undefined);
157
+ if (!jobType) {
158
+ throw new Error("--job-file/--payload-file must contain job_type or use --type/--job-type");
159
+ }
160
+ return {
161
+ job_id: resolveString(parsed.flags["job-id"]) || `job-${randomUUID()}`,
162
+ request_id: resolveString(parsed.flags["request-id"]) || `req-${randomUUID()}`,
163
+ node_id: requireText(nodeId(parsed), "--node-id"),
164
+ job: {
165
+ ...payload,
166
+ schema_version: schemaVersion,
167
+ job_type: jobType
168
+ }
169
+ };
170
+ };
171
+ const referenceFromArgs = async (parsed, explicitJobId) => {
172
+ const jobFile = resolveString(parsed.flags["job-file"]) || resolveString(parsed.flags["payload-file"]);
173
+ if (jobFile) {
174
+ const job = await readJobEnvelope(parsed);
175
+ return {
176
+ ...baseAuth(parsed),
177
+ jobId: explicitJobId || job.job_id,
178
+ nodeId: job.node_id,
179
+ requestId: job.request_id,
180
+ schemaVersion: job.job.schema_version,
181
+ jobType: job.job.job_type
182
+ };
183
+ }
184
+ return {
185
+ ...baseAuth(parsed),
186
+ jobId: requireText(explicitJobId || resolveString(parsed.flags["job-id"]), "JOB_ID"),
187
+ nodeId: nodeId(parsed),
188
+ requestId: resolveString(parsed.flags["request-id"]),
189
+ schemaVersion: resolveString(parsed.flags["schema-version"]) || "2026-06-14",
190
+ jobType: resolveString(parsed.flags["job-type"]) || resolveString(parsed.flags.type)
191
+ };
192
+ };
193
+ function requireText(value, label) {
194
+ if (!value || !value.trim()) {
195
+ throw new Error(`${label} is required`);
196
+ }
197
+ return value.trim();
198
+ }
199
+ const printJsonOrValue = (value, json) => {
200
+ // eslint-disable-next-line no-console
201
+ console.log(json ? JSON.stringify(value, null, 2) : formatValue(value));
202
+ };
203
+ const formatValue = (value) => {
204
+ if (typeof value === "string")
205
+ return value;
206
+ return JSON.stringify(value, null, 2);
207
+ };
208
+ const printSnapshot = (snapshot, json) => {
209
+ if (json) {
210
+ printJsonOrValue(snapshot, true);
211
+ return;
212
+ }
213
+ const job = snapshot.job;
214
+ // eslint-disable-next-line no-console
215
+ console.log(`${job.job_id} ${job.state}`);
216
+ if (job.backpressure?.message) {
217
+ // eslint-disable-next-line no-console
218
+ console.log(job.backpressure.message);
219
+ }
220
+ if (job.result?.error?.message) {
221
+ // eslint-disable-next-line no-console
222
+ console.log(job.result.error.message);
223
+ }
224
+ };
225
+ const formatOpsSummary = (summary) => {
226
+ const lines = [
227
+ `${summary.node.node_id} ${summary.node.generic_jobs_enabled ? "generic-jobs-enabled" : "generic-jobs-disabled"}`,
228
+ `queue active=${summary.queue.active_jobs} queued=${summary.queue.queued_jobs} terminal=${summary.queue.terminal_jobs}`,
229
+ `quota available=${summary.quota.available_slots}/${summary.quota.max_concurrent_jobs} production_enforced=${summary.quota.production_enforced}`,
230
+ `usage total=${summary.usage.total_jobs} succeeded=${summary.usage.succeeded_jobs} failed=${summary.usage.failed_jobs} cancelled=${summary.usage.cancelled_jobs} blocked=${summary.usage.blocked_jobs} gpu_seconds=${summary.usage.gpu_seconds}`,
231
+ ];
232
+ if (summary.queue.jobs.length) {
233
+ lines.push("jobs:");
234
+ for (const job of summary.queue.jobs.slice(0, 10)) {
235
+ lines.push(` ${job.job_id} ${job.state} ${job.job_type} tenant=${job.tenant_id}`);
236
+ }
237
+ }
238
+ if (summary.audit.events.length) {
239
+ lines.push(`audit offset=${summary.audit.offset} limit=${summary.audit.limit} total=${summary.audit.total}:`);
240
+ for (const event of summary.audit.events.slice(0, 10)) {
241
+ lines.push(` ${event.timestamp} ${event.action} ${event.job_id}`);
242
+ }
243
+ }
244
+ return lines.join("\n");
245
+ };
246
+ const waitForJob = async (api, reference, intervalMs) => {
247
+ let snapshot = await api.getGenericJob(reference);
248
+ while (!TERMINAL_STATES.has(snapshot.job.state)) {
249
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
250
+ snapshot = await api.getGenericJob(reference);
251
+ }
252
+ return snapshot;
253
+ };
254
+ export class GpuCommands {
255
+ static async run(argv) {
256
+ const [subcommand, ...rest] = argv;
257
+ if (!subcommand || argv.includes("--help") || argv.includes("-h")) {
258
+ // eslint-disable-next-line no-console
259
+ console.log(GPU_USAGE);
260
+ return;
261
+ }
262
+ if (subcommand !== "list" && subcommand !== "ops") {
263
+ throw new Error(`Unknown gpu subcommand: ${subcommand}`);
264
+ }
265
+ const parsed = parseArgs(rest);
266
+ const api = await createApi(parsed);
267
+ try {
268
+ if (subcommand === "list") {
269
+ const capabilities = await api.listGpuCapabilities({
270
+ ...baseAuth(parsed),
271
+ nodeId: nodeId(parsed)
272
+ });
273
+ printJsonOrValue(capabilities, Boolean(parsed.flags.json));
274
+ return;
275
+ }
276
+ const ops = await api.getGenericJobOps({
277
+ ...baseAuth(parsed),
278
+ nodeId: nodeId(parsed),
279
+ auditLimit: resolvePositiveInt(parsed.flags["audit-limit"], "--audit-limit"),
280
+ auditOffset: resolveNonNegativeInt(parsed.flags["audit-offset"], "--audit-offset")
281
+ });
282
+ // eslint-disable-next-line no-console
283
+ console.log(parsed.flags.json ? JSON.stringify(ops, null, 2) : formatOpsSummary(ops));
284
+ }
285
+ finally {
286
+ await api.close();
287
+ }
288
+ }
289
+ }
290
+ export class GpuJobCommands {
291
+ static shouldHandle(argv) {
292
+ const [subcommand] = argv;
293
+ if (!subcommand)
294
+ return false;
295
+ if (GPU_JOB_SUBCOMMANDS.has(subcommand))
296
+ return true;
297
+ if (!SHARED_JOB_SUBCOMMANDS.has(subcommand))
298
+ return false;
299
+ return argv.some((arg) => GPU_JOB_FLAGS.has(arg) || arg.startsWith("--node-base-url="));
300
+ }
301
+ static async run(argv) {
302
+ const [subcommand, ...rest] = argv;
303
+ if (!subcommand || argv.includes("--help") || argv.includes("-h")) {
304
+ // eslint-disable-next-line no-console
305
+ console.log(JOB_USAGE);
306
+ return;
307
+ }
308
+ const parsed = parseArgs(rest);
309
+ const api = await createApi(parsed);
310
+ try {
311
+ switch (subcommand) {
312
+ case "artifact": {
313
+ const action = parsed.positionals[0];
314
+ const file = parsed.positionals[1];
315
+ if (action !== "upload" || !file) {
316
+ throw new Error("Usage: mcoda job artifact upload <FILE> [options]");
317
+ }
318
+ const content = await readFile(file);
319
+ const artifactPath = resolveString(parsed.flags["artifact-path"]) || basename(file);
320
+ const result = await api.uploadGenericJobArtifact({
321
+ ...(await referenceFromArgs(parsed, resolveString(parsed.flags["job-id"]))),
322
+ name: resolveString(parsed.flags["artifact-name"]) || basename(file),
323
+ path: artifactPath,
324
+ contentBase64: content.toString("base64"),
325
+ contentType: resolveString(parsed.flags["content-type"]),
326
+ sha256: createHash("sha256").update(content).digest("hex"),
327
+ sizeBytes: content.length
328
+ });
329
+ printJsonOrValue(result, Boolean(parsed.flags.json));
330
+ break;
331
+ }
332
+ case "run": {
333
+ const job = await readJobEnvelope(parsed);
334
+ const snapshot = await api.runGenericJob(job, baseAuth(parsed));
335
+ const finalSnapshot = parsed.flags.wait
336
+ ? await waitForJob(api, await referenceFromArgs(parsed, job.job_id), resolvePositiveInt(parsed.flags["interval-ms"], "--interval-ms") || 1000)
337
+ : snapshot;
338
+ printSnapshot(finalSnapshot, Boolean(parsed.flags.json));
339
+ break;
340
+ }
341
+ case "status": {
342
+ const snapshot = await api.getGenericJob(await referenceFromArgs(parsed, parsed.positionals[0]));
343
+ printSnapshot(snapshot, Boolean(parsed.flags.json));
344
+ break;
345
+ }
346
+ case "logs": {
347
+ const result = await api.getGenericJobLogs(await referenceFromArgs(parsed, parsed.positionals[0]));
348
+ if (parsed.flags.json)
349
+ printJsonOrValue(result, true);
350
+ else
351
+ printJsonOrValue(result.logs.map((log) => log.message).join("\n"), false);
352
+ break;
353
+ }
354
+ case "events": {
355
+ const result = await api.getGenericJobEvents(await referenceFromArgs(parsed, parsed.positionals[0]));
356
+ printJsonOrValue(result, Boolean(parsed.flags.json));
357
+ break;
358
+ }
359
+ case "artifacts": {
360
+ const result = await api.getGenericJobArtifacts(await referenceFromArgs(parsed, parsed.positionals[0]));
361
+ printJsonOrValue(result, Boolean(parsed.flags.json));
362
+ break;
363
+ }
364
+ case "cancel": {
365
+ const result = await api.cancelGenericJob(await referenceFromArgs(parsed, parsed.positionals[0]));
366
+ printSnapshot(result, Boolean(parsed.flags.json));
367
+ break;
368
+ }
369
+ case "retry": {
370
+ const result = await api.retryGenericJob(await referenceFromArgs(parsed, parsed.positionals[0]));
371
+ printSnapshot(result, Boolean(parsed.flags.json));
372
+ break;
373
+ }
374
+ default:
375
+ throw new Error(`Unknown GPU job subcommand: ${subcommand}`);
376
+ }
377
+ }
378
+ finally {
379
+ await api.close();
380
+ }
381
+ }
382
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"JobsCommands.d.ts","sourceRoot":"","sources":["../../../src/commands/jobs/JobsCommands.ts"],"names":[],"mappings":"AACA,OAAO,EAAsE,aAAa,EAA0C,MAAM,aAAa,CAAC;AAGxJ,KAAK,aAAa,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEvG,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,aAAa,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAuCD,eAAO,MAAM,YAAY,GAAI,MAAM,MAAM,EAAE,KAAG,aAyH7C,CAAC;AA8BF,eAAO,MAAM,mBAAmB,GAAI,UAAU,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,MAAM,GAAG,SAiBhF,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,UAAU,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,MAAM,GAAG,SAsB/E,CAAC;AAaF,eAAO,MAAM,eAAe,GAAI,MAAM,aAAa,EAAE,KAAG,IA8CvD,CAAC;AAwJF,qBAAa,YAAY;WACV,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CA6OhD"}
1
+ {"version":3,"file":"JobsCommands.d.ts","sourceRoot":"","sources":["../../../src/commands/jobs/JobsCommands.ts"],"names":[],"mappings":"AACA,OAAO,EAAsE,aAAa,EAA0C,MAAM,aAAa,CAAC;AAIxJ,KAAK,aAAa,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEvG,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,aAAa,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAuCD,eAAO,MAAM,YAAY,GAAI,MAAM,MAAM,EAAE,KAAG,aAyH7C,CAAC;AA8BF,eAAO,MAAM,mBAAmB,GAAI,UAAU,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,MAAM,GAAG,SAiBhF,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,UAAU,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,MAAM,GAAG,SAsB/E,CAAC;AAaF,eAAO,MAAM,eAAe,GAAI,MAAM,aAAa,EAAE,KAAG,IA8CvD,CAAC;AAwJF,qBAAa,YAAY;WACV,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAiPhD"}
@@ -1,5 +1,6 @@
1
1
  import path from "node:path";
2
2
  import { JobInsightsService, JobResumeService, JobService, TelemetryService, WorkspaceResolver } from "@mcoda/core";
3
+ import { GpuJobCommands } from "../gpu/GpuCommands.js";
3
4
  const usage = `mcoda job <list|status|watch|logs|inspect|resume|cancel|tokens> ...
4
5
 
5
6
  Commands:
@@ -401,6 +402,10 @@ const handleTokens = async (parsed, workspace) => {
401
402
  };
402
403
  export class JobsCommands {
403
404
  static async run(argv) {
405
+ if (GpuJobCommands.shouldHandle(argv)) {
406
+ await GpuJobCommands.run(argv);
407
+ return;
408
+ }
404
409
  const parsed = parseJobArgs(argv);
405
410
  const workspace = await WorkspaceResolver.resolveWorkspace({
406
411
  cwd: process.cwd(),
package/dist/index.d.ts CHANGED
@@ -3,6 +3,7 @@ export * from './bin/McodaEntrypoint.js';
3
3
  export * from './commands/cloud/CloudCommands.js';
4
4
  export * from './commands/self-hosted/SelfHostedCommands.js';
5
5
  export * from './commands/workers/WorkersCommands.js';
6
+ export * from './commands/gpu/GpuCommands.js';
6
7
  export * from './commands/config/ConfigCommands.js';
7
8
  export * from './commands/consent/ConsentCommands.js';
8
9
  export * from './commands/docs/DocsCommands.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qCAAqC,CAAC;AACpD,cAAc,0BAA0B,CAAC;AACzC,cAAc,mCAAmC,CAAC;AAClD,cAAc,8CAA8C,CAAC;AAC7D,cAAc,uCAAuC,CAAC;AACtD,cAAc,qCAAqC,CAAC;AACpD,cAAc,uCAAuC,CAAC;AACtD,cAAc,iCAAiC,CAAC;AAChD,cAAc,iCAAiC,CAAC;AAChD,cAAc,uCAAuC,CAAC;AACtD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,uCAAuC,CAAC;AACtD,cAAc,wCAAwC,CAAC;AACvD,cAAc,yCAAyC,CAAC;AACxD,cAAc,yCAAyC,CAAC;AACxD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,2CAA2C,CAAC;AAC1D,cAAc,oDAAoD,CAAC;AACnE,cAAc,4CAA4C,CAAC;AAC3D,cAAc,uCAAuC,CAAC;AACtD,cAAc,uCAAuC,CAAC;AACtD,cAAc,wCAAwC,CAAC;AACvD,cAAc,uCAAuC,CAAC;AACtD,cAAc,qCAAqC,CAAC;AACpD,cAAc,uCAAuC,CAAC;AACtD,cAAc,kCAAkC,CAAC;AACjD,cAAc,uCAAuC,CAAC;AACtD,cAAc,gDAAgD,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qCAAqC,CAAC;AACpD,cAAc,0BAA0B,CAAC;AACzC,cAAc,mCAAmC,CAAC;AAClD,cAAc,8CAA8C,CAAC;AAC7D,cAAc,uCAAuC,CAAC;AACtD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,qCAAqC,CAAC;AACpD,cAAc,uCAAuC,CAAC;AACtD,cAAc,iCAAiC,CAAC;AAChD,cAAc,iCAAiC,CAAC;AAChD,cAAc,uCAAuC,CAAC;AACtD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,uCAAuC,CAAC;AACtD,cAAc,wCAAwC,CAAC;AACvD,cAAc,yCAAyC,CAAC;AACxD,cAAc,yCAAyC,CAAC;AACxD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,2CAA2C,CAAC;AAC1D,cAAc,oDAAoD,CAAC;AACnE,cAAc,4CAA4C,CAAC;AAC3D,cAAc,uCAAuC,CAAC;AACtD,cAAc,uCAAuC,CAAC;AACtD,cAAc,wCAAwC,CAAC;AACvD,cAAc,uCAAuC,CAAC;AACtD,cAAc,qCAAqC,CAAC;AACpD,cAAc,uCAAuC,CAAC;AACtD,cAAc,kCAAkC,CAAC;AACjD,cAAc,uCAAuC,CAAC;AACtD,cAAc,gDAAgD,CAAC"}
package/dist/index.js CHANGED
@@ -3,6 +3,7 @@ export * from './bin/McodaEntrypoint.js';
3
3
  export * from './commands/cloud/CloudCommands.js';
4
4
  export * from './commands/self-hosted/SelfHostedCommands.js';
5
5
  export * from './commands/workers/WorkersCommands.js';
6
+ export * from './commands/gpu/GpuCommands.js';
6
7
  export * from './commands/config/ConfigCommands.js';
7
8
  export * from './commands/consent/ConsentCommands.js';
8
9
  export * from './commands/docs/DocsCommands.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcoda",
3
- "version": "0.1.76",
3
+ "version": "0.1.78",
4
4
  "description": "Local-first CLI for planning, documentation, and execution workflows with agent assistance.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -47,12 +47,12 @@
47
47
  },
48
48
  "dependencies": {
49
49
  "yaml": "^2.4.2",
50
- "@mcoda/shared": "0.1.76",
51
- "@mcoda/core": "0.1.76"
50
+ "@mcoda/core": "0.1.78",
51
+ "@mcoda/shared": "0.1.78"
52
52
  },
53
53
  "devDependencies": {
54
- "@mcoda/db": "0.1.76",
55
- "@mcoda/integrations": "0.1.76"
54
+ "@mcoda/integrations": "0.1.78",
55
+ "@mcoda/db": "0.1.78"
56
56
  },
57
57
  "scripts": {
58
58
  "build": "tsc -p tsconfig.json",