@mediaruntime/cli 0.1.0 → 0.2.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 +7 -0
- package/README.md +18 -6
- package/dist/cli.d.ts +38 -1
- package/dist/cli.js +344 -10
- package/dist/cli.js.map +1 -1
- package/docs/AUTH.md +85 -0
- package/docs/CLI_SDD.md +40 -13
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.0 — 2026-08-16
|
|
4
|
+
|
|
5
|
+
- Add browser-assisted `login`, `auth status`, and revoking `logout` commands.
|
|
6
|
+
- Store dedicated CLI credentials only in the operating-system credential vault.
|
|
7
|
+
- Keep `MEDIARUNTIME_API_KEY` as the higher-priority permanent automation interface.
|
|
8
|
+
- Bind the one-time exchange with PKCE and redact credentials from all CLI output.
|
|
9
|
+
|
|
3
10
|
## 0.1.0 — 2026-08-16
|
|
4
11
|
|
|
5
12
|
- Add `run` with local uploads, frozen output aliases, safe waiting, and atomic canonical
|
package/README.md
CHANGED
|
@@ -11,14 +11,25 @@ npm install --global @mediaruntime/cli
|
|
|
11
11
|
mediaruntime --version
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
Node.js 20 or newer is required.
|
|
15
|
-
|
|
14
|
+
Node.js 20 or newer is required. For interactive use, authorize through the browser once:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
mediaruntime login
|
|
18
|
+
mediaruntime auth status
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The CLI stores its dedicated revocable key in the operating-system credential vault; it
|
|
22
|
+
never writes the key to a plaintext configuration file. Use `mediaruntime logout` to
|
|
23
|
+
revoke and remove it.
|
|
24
|
+
|
|
25
|
+
For CI, servers, containers, and deliberate account overrides, continue setting:
|
|
16
26
|
|
|
17
27
|
```bash
|
|
18
28
|
export MEDIARUNTIME_API_KEY="sk_..."
|
|
19
29
|
```
|
|
20
30
|
|
|
21
|
-
|
|
31
|
+
An explicit `MEDIARUNTIME_API_KEY` permanently remains supported and takes precedence
|
|
32
|
+
over the stored login. Do not pass credentials as command-line arguments. `--base-url` and
|
|
22
33
|
`MEDIARUNTIME_API_URL` are development/staging overrides, not normal client setup.
|
|
23
34
|
|
|
24
35
|
## Run a job
|
|
@@ -104,6 +115,7 @@ The full implementation contract is in [docs/CLI_SDD.md](docs/CLI_SDD.md).
|
|
|
104
115
|
|
|
105
116
|
## Scope
|
|
106
117
|
|
|
107
|
-
The
|
|
108
|
-
|
|
109
|
-
|
|
118
|
+
The CLI does not implement `listen` or a webhook relay. Production completion continues
|
|
119
|
+
to use the account destination under Account → Webhooks. The ZIP remains the canonical
|
|
120
|
+
completed-job result. See [browser authentication](docs/AUTH.md) for the complete login,
|
|
121
|
+
credential precedence, and security contract.
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
import { ListJobsParams, JobPage, JobDetails, CreateJobParams, JobReceiptData, WaitForJobOptions } from '@mediaruntime/node';
|
|
2
2
|
|
|
3
|
+
interface StoredCredential {
|
|
4
|
+
apiKey: string;
|
|
5
|
+
accountId: string;
|
|
6
|
+
keyId: string;
|
|
7
|
+
baseUrl: string;
|
|
8
|
+
createdAt: string;
|
|
9
|
+
}
|
|
10
|
+
interface CredentialStore {
|
|
11
|
+
get(baseUrl: string): Promise<StoredCredential | null>;
|
|
12
|
+
set(credential: StoredCredential): Promise<void>;
|
|
13
|
+
delete(baseUrl: string): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
|
|
3
16
|
interface JobsReadClient {
|
|
4
17
|
list(params?: ListJobsParams): Promise<JobPage>;
|
|
5
18
|
get(jobId: string): Promise<JobDetails>;
|
|
@@ -53,6 +66,24 @@ declare function signSyntheticWebhook(payload: Readonly<Record<string, unknown>>
|
|
|
53
66
|
secret: string;
|
|
54
67
|
}): SignedWebhook;
|
|
55
68
|
|
|
69
|
+
interface AuthCommandDependencies {
|
|
70
|
+
credentialStore?: CredentialStore;
|
|
71
|
+
openBrowser?: (url: string) => Promise<void>;
|
|
72
|
+
sleep?: (milliseconds: number) => Promise<void>;
|
|
73
|
+
now?: () => number;
|
|
74
|
+
randomVerifier?: () => string;
|
|
75
|
+
writeStdout?: (text: string) => void;
|
|
76
|
+
}
|
|
77
|
+
declare function resolveCredential(baseUrl: string, dependencies?: AuthCommandDependencies): Promise<{
|
|
78
|
+
apiKey: string;
|
|
79
|
+
source: "environment" | "keychain";
|
|
80
|
+
stored?: StoredCredential;
|
|
81
|
+
} | null>;
|
|
82
|
+
declare function runLoginCommand(args: string[], baseUrl: string, dependencies?: AuthCommandDependencies): Promise<number>;
|
|
83
|
+
declare function runAuthStatusCommand(args: string[], baseUrl: string, dependencies?: AuthCommandDependencies): Promise<number>;
|
|
84
|
+
declare function runLogoutCommand(args: string[], baseUrl: string, dependencies?: AuthCommandDependencies): Promise<number>;
|
|
85
|
+
declare function runAuthCommand(args: string[], baseUrl: string, dependencies?: AuthCommandDependencies): Promise<number>;
|
|
86
|
+
|
|
56
87
|
type CliJobsClient = RunJobsClient & JobsReadClient;
|
|
57
88
|
interface CliClient {
|
|
58
89
|
jobs: CliJobsClient;
|
|
@@ -60,6 +91,7 @@ interface CliClient {
|
|
|
60
91
|
interface CliDependencies {
|
|
61
92
|
createClient?(options: {
|
|
62
93
|
baseUrl?: string;
|
|
94
|
+
apiKey?: string;
|
|
63
95
|
}): CliClient;
|
|
64
96
|
downloadBundle?(url: string, destination: string, options: {
|
|
65
97
|
force: boolean;
|
|
@@ -68,6 +100,11 @@ interface CliDependencies {
|
|
|
68
100
|
}): Promise<void>;
|
|
69
101
|
writeStdout?(text: string): void;
|
|
70
102
|
writeStderr?(text: string): void;
|
|
103
|
+
credentialStore?: CredentialStore;
|
|
104
|
+
openBrowser?(url: string): Promise<void>;
|
|
105
|
+
sleep?(milliseconds: number): Promise<void>;
|
|
106
|
+
now?(): number;
|
|
107
|
+
randomVerifier?(): string;
|
|
71
108
|
}
|
|
72
109
|
declare function downloadBundleAtomically(url: string, destination: string, options: {
|
|
73
110
|
force: boolean;
|
|
@@ -76,4 +113,4 @@ declare function downloadBundleAtomically(url: string, destination: string, opti
|
|
|
76
113
|
}): Promise<void>;
|
|
77
114
|
declare function executeCli(argv: string[], dependencies?: CliDependencies): Promise<number>;
|
|
78
115
|
|
|
79
|
-
export { type CliClient, type CliDependencies, createSyntheticTerminalEvent, downloadBundleAtomically, executeCli, parseTriggerArguments, runTriggerCommand, signSyntheticWebhook };
|
|
116
|
+
export { type CliClient, type CliDependencies, createSyntheticTerminalEvent, downloadBundleAtomically, executeCli, parseTriggerArguments, resolveCredential, runAuthCommand, runAuthStatusCommand, runLoginCommand, runLogoutCommand, runTriggerCommand, signSyntheticWebhook };
|
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
-
import { createHash, randomUUID as randomUUID2 } from "crypto";
|
|
4
|
+
import { createHash as createHash2, randomUUID as randomUUID2 } from "crypto";
|
|
5
5
|
import { constants, realpathSync } from "fs";
|
|
6
6
|
import {
|
|
7
7
|
access,
|
|
@@ -52,6 +52,312 @@ var BundleDownloadError = class extends CliError {
|
|
|
52
52
|
}
|
|
53
53
|
};
|
|
54
54
|
|
|
55
|
+
// src/auth/api.ts
|
|
56
|
+
var PRODUCTION_ORIGIN = "https://mediaruntime.com";
|
|
57
|
+
var AuthApiError = class extends CliError {
|
|
58
|
+
status;
|
|
59
|
+
constructor(code, message, status) {
|
|
60
|
+
super(code, message, status >= 500 ? 5 : 3);
|
|
61
|
+
this.name = "AuthApiError";
|
|
62
|
+
this.status = status;
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
function endpoint(baseUrl, path) {
|
|
66
|
+
return `${baseUrl.replace(/\/+$/, "")}/api/cli/auth/${path}`;
|
|
67
|
+
}
|
|
68
|
+
async function responsePayload(response) {
|
|
69
|
+
try {
|
|
70
|
+
const value = await response.json();
|
|
71
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
72
|
+
} catch {
|
|
73
|
+
return {};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async function post(baseUrl, path, body, apiKey) {
|
|
77
|
+
let response;
|
|
78
|
+
try {
|
|
79
|
+
response = await fetch(endpoint(baseUrl, path), {
|
|
80
|
+
method: "POST",
|
|
81
|
+
redirect: "error",
|
|
82
|
+
headers: {
|
|
83
|
+
"Content-Type": "application/json",
|
|
84
|
+
...apiKey ? { "X-API-Key": apiKey } : {}
|
|
85
|
+
},
|
|
86
|
+
body: JSON.stringify(body)
|
|
87
|
+
});
|
|
88
|
+
} catch (error) {
|
|
89
|
+
throw new CliError("auth_connection_error", "Could not reach MediaRuntime authentication", 5, { cause: error });
|
|
90
|
+
}
|
|
91
|
+
const payload = await responsePayload(response);
|
|
92
|
+
if (!response.ok) {
|
|
93
|
+
const errorPayload = payload;
|
|
94
|
+
throw new AuthApiError(
|
|
95
|
+
typeof errorPayload.error === "string" ? errorPayload.error : "authorization_failed",
|
|
96
|
+
typeof errorPayload.detail === "string" ? errorPayload.detail : `Authentication failed with HTTP ${response.status}`,
|
|
97
|
+
response.status
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
return payload;
|
|
101
|
+
}
|
|
102
|
+
function requireString(payload, key) {
|
|
103
|
+
const value = payload[key];
|
|
104
|
+
if (typeof value !== "string" || !value) throw new CliError("invalid_auth_response", "MediaRuntime returned an invalid authentication response", 5);
|
|
105
|
+
return value;
|
|
106
|
+
}
|
|
107
|
+
async function startAuthorization(baseUrl, codeChallenge) {
|
|
108
|
+
const payload = await post(baseUrl, "start", { codeChallenge, clientName: "MediaRuntime CLI" });
|
|
109
|
+
return {
|
|
110
|
+
deviceCode: requireString(payload, "deviceCode"),
|
|
111
|
+
userCode: requireString(payload, "userCode"),
|
|
112
|
+
verificationUri: requireString(payload, "verificationUri"),
|
|
113
|
+
verificationUriComplete: requireString(payload, "verificationUriComplete"),
|
|
114
|
+
expiresIn: Number(payload.expiresIn),
|
|
115
|
+
interval: Number(payload.interval)
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
async function exchangeAuthorization(baseUrl, deviceCode, codeVerifier) {
|
|
119
|
+
const payload = await post(baseUrl, "token", { deviceCode, codeVerifier });
|
|
120
|
+
const credentialType = requireString(payload, "credentialType");
|
|
121
|
+
if (credentialType !== "cli") throw new CliError("invalid_auth_response", "MediaRuntime returned the wrong credential type", 5);
|
|
122
|
+
return {
|
|
123
|
+
apiKey: requireString(payload, "apiKey"),
|
|
124
|
+
accountId: requireString(payload, "accountId"),
|
|
125
|
+
keyId: requireString(payload, "keyId"),
|
|
126
|
+
credentialType
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
async function inspectCredential(baseUrl, apiKey) {
|
|
130
|
+
const payload = await post(baseUrl, "status", {}, apiKey);
|
|
131
|
+
const credentialType = requireString(payload, "credentialType");
|
|
132
|
+
if (credentialType !== "cli" && credentialType !== "api_key") {
|
|
133
|
+
throw new CliError("invalid_auth_response", "MediaRuntime returned the wrong credential type", 5);
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
status: "authenticated",
|
|
137
|
+
accountId: requireString(payload, "accountId"),
|
|
138
|
+
keyId: requireString(payload, "keyId"),
|
|
139
|
+
credentialType
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
async function revokeCredential(baseUrl, apiKey) {
|
|
143
|
+
await post(baseUrl, "revoke", {}, apiKey);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// src/commands/auth.ts
|
|
147
|
+
import { createHash, randomBytes } from "crypto";
|
|
148
|
+
|
|
149
|
+
// src/auth/browser.ts
|
|
150
|
+
import { spawn } from "child_process";
|
|
151
|
+
async function openBrowser(url) {
|
|
152
|
+
const command = process.platform === "darwin" ? { executable: "open", args: [url] } : process.platform === "win32" ? { executable: "rundll32", args: ["url.dll,FileProtocolHandler", url] } : { executable: "xdg-open", args: [url] };
|
|
153
|
+
await new Promise((resolve2, reject) => {
|
|
154
|
+
const child = spawn(command.executable, command.args, {
|
|
155
|
+
detached: true,
|
|
156
|
+
stdio: "ignore"
|
|
157
|
+
});
|
|
158
|
+
child.once("error", (error) => reject(new CliError(
|
|
159
|
+
"browser_open_failed",
|
|
160
|
+
"Could not open a browser; rerun with --no-browser and open the printed URL",
|
|
161
|
+
2,
|
|
162
|
+
{ cause: error }
|
|
163
|
+
)));
|
|
164
|
+
child.once("spawn", () => {
|
|
165
|
+
child.unref();
|
|
166
|
+
resolve2();
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// src/auth/credential-store.ts
|
|
172
|
+
var SERVICE = "mediaruntime-cli";
|
|
173
|
+
function vaultAccount(baseUrl) {
|
|
174
|
+
return new URL(baseUrl).host.toLowerCase();
|
|
175
|
+
}
|
|
176
|
+
function validCredential(value) {
|
|
177
|
+
if (!value || typeof value !== "object") return false;
|
|
178
|
+
const item = value;
|
|
179
|
+
return typeof item.apiKey === "string" && item.apiKey.startsWith("sk_") && typeof item.accountId === "string" && item.accountId.length > 0 && typeof item.keyId === "string" && item.keyId.length > 0 && typeof item.baseUrl === "string" && item.baseUrl.length > 0 && typeof item.createdAt === "string" && item.createdAt.length > 0;
|
|
180
|
+
}
|
|
181
|
+
async function entry(baseUrl) {
|
|
182
|
+
try {
|
|
183
|
+
const { Entry } = await import("@napi-rs/keyring");
|
|
184
|
+
return new Entry(SERVICE, vaultAccount(baseUrl));
|
|
185
|
+
} catch (error) {
|
|
186
|
+
throw new CliError(
|
|
187
|
+
"credential_store_unavailable",
|
|
188
|
+
"The operating-system credential vault is unavailable; use MEDIARUNTIME_API_KEY on this machine",
|
|
189
|
+
2,
|
|
190
|
+
{ cause: error }
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
var OsCredentialStore = class {
|
|
195
|
+
async get(baseUrl) {
|
|
196
|
+
try {
|
|
197
|
+
const raw = (await entry(baseUrl)).getPassword();
|
|
198
|
+
if (!raw) return null;
|
|
199
|
+
const parsed = JSON.parse(raw);
|
|
200
|
+
if (!validCredential(parsed) || parsed.baseUrl !== baseUrl) {
|
|
201
|
+
throw new Error("Stored credential has an invalid shape");
|
|
202
|
+
}
|
|
203
|
+
return parsed;
|
|
204
|
+
} catch (error) {
|
|
205
|
+
if (error instanceof SyntaxError || error instanceof Error && error.message.includes("invalid shape")) {
|
|
206
|
+
throw new CliError("credential_store_invalid", "The stored MediaRuntime credential is invalid; run mediaruntime logout --local-only", 3);
|
|
207
|
+
}
|
|
208
|
+
if (error instanceof CliError) throw error;
|
|
209
|
+
if (error instanceof Error && /not found|no entry|no matching|item.*exist/i.test(error.message)) return null;
|
|
210
|
+
throw new CliError("credential_store_error", "Could not read the operating-system credential vault", 2, { cause: error });
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
async set(credential) {
|
|
214
|
+
try {
|
|
215
|
+
(await entry(credential.baseUrl)).setPassword(JSON.stringify(credential));
|
|
216
|
+
} catch (error) {
|
|
217
|
+
if (error instanceof CliError) throw error;
|
|
218
|
+
throw new CliError("credential_store_error", "Could not save the login in the operating-system credential vault", 2, { cause: error });
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
async delete(baseUrl) {
|
|
222
|
+
try {
|
|
223
|
+
(await entry(baseUrl)).deletePassword();
|
|
224
|
+
} catch (error) {
|
|
225
|
+
if (error instanceof CliError) throw error;
|
|
226
|
+
if (error instanceof Error && /not found|no entry|no matching|item.*exist/i.test(error.message)) return;
|
|
227
|
+
throw new CliError("credential_store_error", "Could not remove the login from the operating-system credential vault", 2, { cause: error });
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
// src/commands/auth.ts
|
|
233
|
+
var defaultSleep = (milliseconds) => new Promise((resolve2) => setTimeout(resolve2, milliseconds));
|
|
234
|
+
function verifierChallenge(verifier) {
|
|
235
|
+
return createHash("sha256").update(verifier).digest("base64url");
|
|
236
|
+
}
|
|
237
|
+
function defaultVerifier() {
|
|
238
|
+
return randomBytes(32).toString("base64url");
|
|
239
|
+
}
|
|
240
|
+
function store(dependencies) {
|
|
241
|
+
return dependencies.credentialStore ?? new OsCredentialStore();
|
|
242
|
+
}
|
|
243
|
+
function assertOnly(args, allowed) {
|
|
244
|
+
const unexpected = args.find((arg) => !allowed.includes(arg));
|
|
245
|
+
if (unexpected) throw new UsageError(`Unknown authentication option: ${unexpected}`);
|
|
246
|
+
}
|
|
247
|
+
async function resolveCredential(baseUrl, dependencies = {}) {
|
|
248
|
+
const environment = process.env.MEDIARUNTIME_API_KEY?.trim();
|
|
249
|
+
if (environment) return { apiKey: environment, source: "environment" };
|
|
250
|
+
const stored = await store(dependencies).get(baseUrl);
|
|
251
|
+
return stored ? { apiKey: stored.apiKey, source: "keychain", stored } : null;
|
|
252
|
+
}
|
|
253
|
+
async function runLoginCommand(args, baseUrl, dependencies = {}) {
|
|
254
|
+
assertOnly(args, ["--no-browser"]);
|
|
255
|
+
const noBrowser = args.includes("--no-browser");
|
|
256
|
+
const write = dependencies.writeStdout ?? ((text) => process.stdout.write(text));
|
|
257
|
+
const existing = await store(dependencies).get(baseUrl);
|
|
258
|
+
if (existing) {
|
|
259
|
+
try {
|
|
260
|
+
await inspectCredential(baseUrl, existing.apiKey);
|
|
261
|
+
write(`Already logged in to ${baseUrl} (account ${existing.accountId}).
|
|
262
|
+
`);
|
|
263
|
+
return 0;
|
|
264
|
+
} catch (error) {
|
|
265
|
+
if (!(error instanceof AuthApiError) || error.status >= 500) throw error;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
const verifier = dependencies.randomVerifier?.() ?? defaultVerifier();
|
|
269
|
+
if (verifier.length < 43 || verifier.length > 128) {
|
|
270
|
+
throw new CliError("invalid_verifier", "Generated PKCE verifier is invalid", 1);
|
|
271
|
+
}
|
|
272
|
+
const authorization = await startAuthorization(baseUrl, verifierChallenge(verifier));
|
|
273
|
+
if (!Number.isFinite(authorization.expiresIn) || authorization.expiresIn <= 0 || !Number.isFinite(authorization.interval) || authorization.interval < 1) {
|
|
274
|
+
throw new CliError("invalid_auth_response", "MediaRuntime returned invalid authorization timing", 5);
|
|
275
|
+
}
|
|
276
|
+
write(`Open this URL to authorize MediaRuntime CLI:
|
|
277
|
+
${authorization.verificationUriComplete}
|
|
278
|
+
|
|
279
|
+
`);
|
|
280
|
+
write(`Confirm code: ${authorization.userCode}
|
|
281
|
+
`);
|
|
282
|
+
if (!noBrowser) await (dependencies.openBrowser ?? openBrowser)(authorization.verificationUriComplete);
|
|
283
|
+
write("Waiting for browser approval\u2026\n");
|
|
284
|
+
const now = dependencies.now ?? Date.now;
|
|
285
|
+
const sleep = dependencies.sleep ?? defaultSleep;
|
|
286
|
+
const deadline = now() + authorization.expiresIn * 1e3;
|
|
287
|
+
let credential;
|
|
288
|
+
while (now() < deadline) {
|
|
289
|
+
try {
|
|
290
|
+
credential = await exchangeAuthorization(baseUrl, authorization.deviceCode, verifier);
|
|
291
|
+
break;
|
|
292
|
+
} catch (error) {
|
|
293
|
+
if (error instanceof AuthApiError && error.code === "authorization_pending") {
|
|
294
|
+
await sleep(authorization.interval * 1e3);
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
throw error;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
if (!credential) throw new CliError("authorization_timeout", "Browser authorization expired; run mediaruntime login again", 7);
|
|
301
|
+
try {
|
|
302
|
+
await inspectCredential(baseUrl, credential.apiKey);
|
|
303
|
+
await store(dependencies).set({
|
|
304
|
+
apiKey: credential.apiKey,
|
|
305
|
+
accountId: credential.accountId,
|
|
306
|
+
keyId: credential.keyId,
|
|
307
|
+
baseUrl,
|
|
308
|
+
createdAt: new Date(now()).toISOString()
|
|
309
|
+
});
|
|
310
|
+
} catch (error) {
|
|
311
|
+
await revokeCredential(baseUrl, credential.apiKey).catch(() => void 0);
|
|
312
|
+
throw error;
|
|
313
|
+
}
|
|
314
|
+
write(`Logged in to ${baseUrl} (account ${credential.accountId}).
|
|
315
|
+
`);
|
|
316
|
+
if (process.env.MEDIARUNTIME_API_KEY?.trim()) {
|
|
317
|
+
write("MEDIARUNTIME_API_KEY is set and will take precedence over this login.\n");
|
|
318
|
+
}
|
|
319
|
+
return 0;
|
|
320
|
+
}
|
|
321
|
+
async function runAuthStatusCommand(args, baseUrl, dependencies = {}) {
|
|
322
|
+
assertOnly(args, ["--json"]);
|
|
323
|
+
const json = args.includes("--json");
|
|
324
|
+
const write = dependencies.writeStdout ?? ((text) => process.stdout.write(text));
|
|
325
|
+
const resolved = await resolveCredential(baseUrl, dependencies);
|
|
326
|
+
if (!resolved) throw new CliError("authentication_required", "Run mediaruntime login or set MEDIARUNTIME_API_KEY", 3);
|
|
327
|
+
const status = await inspectCredential(baseUrl, resolved.apiKey);
|
|
328
|
+
if (json) {
|
|
329
|
+
write(`${JSON.stringify({ authenticated: true, source: resolved.source, ...status })}
|
|
330
|
+
`);
|
|
331
|
+
} else {
|
|
332
|
+
write(`Authenticated via ${resolved.source} (account ${status.accountId}, key ${status.keyId}).
|
|
333
|
+
`);
|
|
334
|
+
}
|
|
335
|
+
return 0;
|
|
336
|
+
}
|
|
337
|
+
async function runLogoutCommand(args, baseUrl, dependencies = {}) {
|
|
338
|
+
assertOnly(args, ["--local-only"]);
|
|
339
|
+
const localOnly = args.includes("--local-only");
|
|
340
|
+
const write = dependencies.writeStdout ?? ((text) => process.stdout.write(text));
|
|
341
|
+
const credentialStore = store(dependencies);
|
|
342
|
+
const credential = await credentialStore.get(baseUrl);
|
|
343
|
+
if (!credential) {
|
|
344
|
+
write(`No stored login for ${baseUrl}.
|
|
345
|
+
`);
|
|
346
|
+
if (process.env.MEDIARUNTIME_API_KEY?.trim()) write("MEDIARUNTIME_API_KEY remains active in this shell.\n");
|
|
347
|
+
return 0;
|
|
348
|
+
}
|
|
349
|
+
if (!localOnly) await revokeCredential(baseUrl, credential.apiKey);
|
|
350
|
+
await credentialStore.delete(baseUrl);
|
|
351
|
+
write(localOnly ? "Removed the local CLI login without server revocation.\n" : "Logged out and revoked the CLI credential.\n");
|
|
352
|
+
if (process.env.MEDIARUNTIME_API_KEY?.trim()) write("MEDIARUNTIME_API_KEY remains active in this shell.\n");
|
|
353
|
+
return 0;
|
|
354
|
+
}
|
|
355
|
+
async function runAuthCommand(args, baseUrl, dependencies = {}) {
|
|
356
|
+
const subcommand = args[0];
|
|
357
|
+
if (subcommand !== "status") throw new UsageError("Usage: mediaruntime auth status [--json]");
|
|
358
|
+
return runAuthStatusCommand(args.slice(1), baseUrl, dependencies);
|
|
359
|
+
}
|
|
360
|
+
|
|
55
361
|
// src/commands/jobs.ts
|
|
56
362
|
var UNSUCCESSFUL_TERMINAL_STATUSES = /* @__PURE__ */ new Set(["FAILED", "REJECTED", "PARTIAL"]);
|
|
57
363
|
function optionValue(args, index, option) {
|
|
@@ -400,7 +706,7 @@ async function runCommand(args, dependencies) {
|
|
|
400
706
|
}
|
|
401
707
|
|
|
402
708
|
// src/commands/trigger.ts
|
|
403
|
-
import { randomBytes, randomUUID } from "crypto";
|
|
709
|
+
import { randomBytes as randomBytes2, randomUUID } from "crypto";
|
|
404
710
|
import { readFile } from "fs/promises";
|
|
405
711
|
|
|
406
712
|
// src/trigger/destination.ts
|
|
@@ -628,7 +934,7 @@ async function runTriggerCommand(args, dependencies = {}) {
|
|
|
628
934
|
fetch: dependencies.fetch ?? globalThis.fetch,
|
|
629
935
|
now: dependencies.now ?? Date.now,
|
|
630
936
|
randomUUID: dependencies.randomUUID ?? randomUUID,
|
|
631
|
-
randomBytes: dependencies.randomBytes ??
|
|
937
|
+
randomBytes: dependencies.randomBytes ?? randomBytes2,
|
|
632
938
|
readFile: dependencies.readFile ?? readFile,
|
|
633
939
|
writeStdout: dependencies.writeStdout ?? ((text) => process.stdout.write(text))
|
|
634
940
|
};
|
|
@@ -677,7 +983,7 @@ async function runTriggerCommand(args, dependencies = {}) {
|
|
|
677
983
|
}
|
|
678
984
|
|
|
679
985
|
// src/cli.ts
|
|
680
|
-
var VERSION = "0.
|
|
986
|
+
var VERSION = "0.2.0";
|
|
681
987
|
var HELP = `MediaRuntime CLI
|
|
682
988
|
|
|
683
989
|
Usage:
|
|
@@ -685,6 +991,9 @@ Usage:
|
|
|
685
991
|
mediaruntime jobs list [--status <status>] [--limit <n>] [--cursor <cursor>]
|
|
686
992
|
mediaruntime jobs get <job_id> [--download <bundle.zip>] [--force]
|
|
687
993
|
mediaruntime trigger <job.completed|job.failed|job.rejected> --to <local-url>
|
|
994
|
+
mediaruntime login [--no-browser]
|
|
995
|
+
mediaruntime auth status
|
|
996
|
+
mediaruntime logout [--local-only]
|
|
688
997
|
|
|
689
998
|
Global options:
|
|
690
999
|
--base-url <url> Developer override; normally leave unset
|
|
@@ -694,7 +1003,7 @@ Global options:
|
|
|
694
1003
|
Run/jobs options:
|
|
695
1004
|
--json Machine-readable, URL-redacted output
|
|
696
1005
|
|
|
697
|
-
Authentication uses MEDIARUNTIME_API_KEY.
|
|
1006
|
+
Authentication uses a secure login or MEDIARUNTIME_API_KEY.
|
|
698
1007
|
`;
|
|
699
1008
|
function isFileExistsError(error) {
|
|
700
1009
|
return error instanceof Error && "code" in error && error.code === "EEXIST";
|
|
@@ -728,7 +1037,7 @@ async function downloadBundleAtomically(url, destination, options) {
|
|
|
728
1037
|
throw new BundleDownloadError(`Bundle download failed with HTTP ${response.status}`);
|
|
729
1038
|
}
|
|
730
1039
|
if (!response.body) throw new BundleDownloadError("Bundle download returned an empty response body");
|
|
731
|
-
const hash =
|
|
1040
|
+
const hash = createHash2("sha256");
|
|
732
1041
|
let sizeBytes = 0;
|
|
733
1042
|
const integrity = new Transform({
|
|
734
1043
|
transform(chunk, _encoding, callback) {
|
|
@@ -849,12 +1158,32 @@ async function executeCli(argv, dependencies = {}) {
|
|
|
849
1158
|
}
|
|
850
1159
|
const configuredBaseUrl = global.baseUrl ?? process.env.MEDIARUNTIME_API_URL;
|
|
851
1160
|
const baseUrl = configuredBaseUrl ? normalizeBaseUrl(configuredBaseUrl) : void 0;
|
|
852
|
-
|
|
853
|
-
|
|
1161
|
+
const authBaseUrl = baseUrl ?? PRODUCTION_ORIGIN;
|
|
1162
|
+
const authDependencies = {
|
|
1163
|
+
...dependencies.credentialStore ? { credentialStore: dependencies.credentialStore } : {},
|
|
1164
|
+
...dependencies.openBrowser ? { openBrowser: dependencies.openBrowser } : {},
|
|
1165
|
+
...dependencies.sleep ? { sleep: dependencies.sleep } : {},
|
|
1166
|
+
...dependencies.now ? { now: dependencies.now } : {},
|
|
1167
|
+
...dependencies.randomVerifier ? { randomVerifier: dependencies.randomVerifier } : {},
|
|
1168
|
+
writeStdout
|
|
1169
|
+
};
|
|
1170
|
+
if (command === "login") return await runLoginCommand(global.args.slice(1), authBaseUrl, authDependencies);
|
|
1171
|
+
if (command === "auth") return await runAuthCommand(global.args.slice(1), authBaseUrl, authDependencies);
|
|
1172
|
+
if (command === "logout") return await runLogoutCommand(global.args.slice(1), authBaseUrl, authDependencies);
|
|
1173
|
+
let apiKey = process.env.MEDIARUNTIME_API_KEY?.trim();
|
|
1174
|
+
if (!apiKey && (!dependencies.createClient || dependencies.credentialStore)) {
|
|
1175
|
+
apiKey = (await resolveCredential(authBaseUrl, authDependencies))?.apiKey;
|
|
1176
|
+
}
|
|
1177
|
+
if (!dependencies.createClient && !apiKey) {
|
|
1178
|
+
throw new UsageError("Run mediaruntime login or set MEDIARUNTIME_API_KEY before using run or jobs");
|
|
854
1179
|
}
|
|
855
1180
|
const client = dependencies.createClient?.({
|
|
856
|
-
...baseUrl === void 0 ? {} : { baseUrl }
|
|
857
|
-
|
|
1181
|
+
...baseUrl === void 0 ? {} : { baseUrl },
|
|
1182
|
+
...apiKey === void 0 ? {} : { apiKey }
|
|
1183
|
+
}) ?? new MediaRuntime({
|
|
1184
|
+
...baseUrl === void 0 ? {} : { baseUrl },
|
|
1185
|
+
...apiKey === void 0 ? {} : { apiKey }
|
|
1186
|
+
});
|
|
858
1187
|
const downloadBundle = dependencies.downloadBundle ?? downloadBundleAtomically;
|
|
859
1188
|
const commandDependencies = { jobs: client.jobs, writeStdout, downloadBundle };
|
|
860
1189
|
if (command === "run") return await runCommand(global.args.slice(1), commandDependencies);
|
|
@@ -889,6 +1218,11 @@ export {
|
|
|
889
1218
|
downloadBundleAtomically,
|
|
890
1219
|
executeCli,
|
|
891
1220
|
parseTriggerArguments,
|
|
1221
|
+
resolveCredential,
|
|
1222
|
+
runAuthCommand,
|
|
1223
|
+
runAuthStatusCommand,
|
|
1224
|
+
runLoginCommand,
|
|
1225
|
+
runLogoutCommand,
|
|
892
1226
|
runTriggerCommand,
|
|
893
1227
|
signSyntheticWebhook
|
|
894
1228
|
};
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts","../src/errors.ts","../src/commands/jobs.ts","../src/commands/run.ts","../src/commands/trigger.ts","../src/trigger/destination.ts","../src/trigger/event.ts","../src/trigger/signature.ts"],"sourcesContent":["import { createHash, randomUUID } from \"node:crypto\";\nimport { constants, realpathSync } from \"node:fs\";\nimport {\n access,\n link,\n rename,\n rm,\n} from \"node:fs/promises\";\nimport { createWriteStream } from \"node:fs\";\nimport { basename, dirname, resolve } from \"node:path\";\nimport { Readable, Transform } from \"node:stream\";\nimport { pipeline } from \"node:stream/promises\";\nimport type { ReadableStream as NodeReadableStream } from \"node:stream/web\";\nimport { fileURLToPath } from \"node:url\";\nimport {\n JobWaitTimeoutError,\n MediaRuntime,\n MediaRuntimeApiError,\n MediaRuntimeConnectionError,\n} from \"@mediaruntime/node\";\nimport {\n runJobsCommand,\n type JobsReadClient,\n} from \"./commands/jobs.js\";\nimport {\n runCommand,\n type RunJobsClient,\n} from \"./commands/run.js\";\nimport { runTriggerCommand } from \"./commands/trigger.js\";\nimport { BundleDownloadError, CliError, UsageError } from \"./errors.js\";\nimport { isLoopbackDestination } from \"./trigger/destination.js\";\n\nexport { parseTriggerArguments, runTriggerCommand } from \"./commands/trigger.js\";\nexport { createSyntheticTerminalEvent } from \"./trigger/event.js\";\nexport { signSyntheticWebhook } from \"./trigger/signature.js\";\n\nconst VERSION = \"0.1.0\";\nconst HELP = `MediaRuntime CLI\n\nUsage:\n mediaruntime run <source> --output <alias> [--output <alias>] [--wait]\n mediaruntime jobs list [--status <status>] [--limit <n>] [--cursor <cursor>]\n mediaruntime jobs get <job_id> [--download <bundle.zip>] [--force]\n mediaruntime trigger <job.completed|job.failed|job.rejected> --to <local-url>\n\nGlobal options:\n --base-url <url> Developer override; normally leave unset\n --help Show help\n --version Show version\n\nRun/jobs options:\n --json Machine-readable, URL-redacted output\n\nAuthentication uses MEDIARUNTIME_API_KEY.\n`;\n\ntype CliJobsClient = RunJobsClient & JobsReadClient;\n\nexport interface CliClient {\n jobs: CliJobsClient;\n}\n\nexport interface CliDependencies {\n createClient?(options: { baseUrl?: string }): CliClient;\n downloadBundle?(\n url: string,\n destination: string,\n options: {\n force: boolean;\n expectedSizeBytes?: number | null;\n expectedSha256?: string | null;\n },\n ): Promise<void>;\n writeStdout?(text: string): void;\n writeStderr?(text: string): void;\n}\n\nfunction isFileExistsError(error: unknown): boolean {\n return error instanceof Error && \"code\" in error && error.code === \"EEXIST\";\n}\n\nasync function exists(path: string): Promise<boolean> {\n try {\n await access(path, constants.F_OK);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function downloadBundleAtomically(\n url: string,\n destination: string,\n options: {\n force: boolean;\n expectedSizeBytes?: number | null;\n expectedSha256?: string | null;\n },\n): Promise<void> {\n const target = resolve(destination);\n if (!options.force && await exists(target)) {\n throw new BundleDownloadError(`Refusing to overwrite existing file: ${destination}`);\n }\n const temporary = resolve(\n dirname(target),\n `.${basename(target)}.${randomUUID()}.part`,\n );\n\n try {\n let response: Response;\n try {\n response = await fetch(url);\n } catch (error) {\n throw new BundleDownloadError(\"Bundle download failed before receiving a response\", { cause: error });\n }\n if (!response.ok) {\n await response.body?.cancel();\n throw new BundleDownloadError(`Bundle download failed with HTTP ${response.status}`);\n }\n if (!response.body) throw new BundleDownloadError(\"Bundle download returned an empty response body\");\n const hash = createHash(\"sha256\");\n let sizeBytes = 0;\n const integrity = new Transform({\n transform(chunk: Buffer, _encoding, callback) {\n sizeBytes += chunk.length;\n hash.update(chunk);\n callback(null, chunk);\n },\n });\n await pipeline(\n Readable.fromWeb(response.body as unknown as NodeReadableStream<Uint8Array>),\n integrity,\n createWriteStream(temporary, { flags: \"wx\", mode: 0o600 }),\n );\n const sha256 = hash.digest(\"hex\");\n if (options.expectedSizeBytes !== undefined && options.expectedSizeBytes !== null &&\n sizeBytes !== options.expectedSizeBytes) {\n throw new BundleDownloadError(\n `Bundle size mismatch: expected ${options.expectedSizeBytes} bytes, received ${sizeBytes}`,\n );\n }\n if (options.expectedSha256 && sha256 !== options.expectedSha256.toLowerCase()) {\n throw new BundleDownloadError(\"Bundle SHA-256 mismatch\");\n }\n\n if (options.force) {\n await rename(temporary, target);\n } else {\n try {\n // A hard link publishes the completed same-filesystem temporary file atomically\n // and fails rather than replacing a destination created during the download.\n await link(temporary, target);\n } catch (error) {\n if (isFileExistsError(error)) {\n throw new BundleDownloadError(`Refusing to overwrite existing file: ${destination}`);\n }\n throw error;\n }\n await rm(temporary);\n }\n } catch (error) {\n await rm(temporary, { force: true });\n if (error instanceof BundleDownloadError) throw error;\n throw new BundleDownloadError(`Could not write bundle to ${destination}`, { cause: error });\n }\n}\n\nfunction normalizeBaseUrl(raw: string): string {\n let value: URL;\n try {\n value = new URL(raw);\n } catch {\n throw new UsageError(\"--base-url must be an absolute HTTP(S) origin\");\n }\n if (value.username || value.password || value.search || value.hash || value.pathname !== \"/\") {\n throw new UsageError(\"--base-url must be an origin without credentials, path, query, or fragment\");\n }\n if (value.protocol !== \"https:\" && !(value.protocol === \"http:\" && isLoopbackDestination(value))) {\n throw new UsageError(\"--base-url must use HTTPS unless it points to loopback\");\n }\n return value.origin;\n}\n\nfunction extractGlobalOptions(argv: string[]): { baseUrl?: string; args: string[] } {\n const args = [...argv];\n let baseUrl: string | undefined;\n while (args[0] === \"--base-url\") {\n const value = args[1];\n if (!value || value.startsWith(\"--\")) throw new UsageError(\"--base-url requires a value\");\n baseUrl = normalizeBaseUrl(value);\n args.splice(0, 2);\n }\n return { ...(baseUrl === undefined ? {} : { baseUrl }), args };\n}\n\nfunction safeApiError(error: MediaRuntimeApiError): Record<string, unknown> {\n return {\n name: error.name,\n message: error.message,\n code: error.code,\n status: error.status,\n retryable: error.retryable,\n requestId: error.requestId ?? null,\n field: error.field ?? null,\n };\n}\n\nfunction exitCodeFor(error: unknown): number {\n if (error instanceof CliError) return error.exitCode;\n if (error instanceof JobWaitTimeoutError) return 7;\n if (error instanceof MediaRuntimeConnectionError) return 5;\n if (error instanceof MediaRuntimeApiError) {\n if ([401, 402, 403].includes(error.status)) return 3;\n if (error.status >= 500) return 5;\n return 4;\n }\n if (error instanceof DOMException && error.name === \"AbortError\") return 130;\n if (error instanceof TypeError && /fetch|network|socket|connect/i.test(error.message)) return 5;\n return 1;\n}\n\nfunction safeError(error: unknown, exitCode: number): Record<string, unknown> {\n if (error instanceof MediaRuntimeApiError) return { ...safeApiError(error), exitCode };\n return {\n code: error instanceof CliError ? error.code : \"cli_error\",\n message: error instanceof Error ? error.message : \"Unknown error\",\n exitCode,\n };\n}\n\nexport async function executeCli(\n argv: string[],\n dependencies: CliDependencies = {},\n): Promise<number> {\n const writeStdout = dependencies.writeStdout ?? ((text: string) => process.stdout.write(text));\n const writeStderr = dependencies.writeStderr ?? ((text: string) => process.stderr.write(text));\n const json = argv.includes(\"--json\");\n try {\n const global = extractGlobalOptions(argv);\n const command = global.args[0];\n if (!command || command === \"--help\" || command === \"-h\") {\n writeStdout(HELP);\n return 0;\n }\n if (command === \"--version\" || command === \"-v\") {\n writeStdout(`${VERSION}\\n`);\n return 0;\n }\n if (command === \"trigger\") {\n return await runTriggerCommand(global.args.slice(1), { writeStdout });\n }\n\n const configuredBaseUrl = global.baseUrl ?? process.env.MEDIARUNTIME_API_URL;\n const baseUrl = configuredBaseUrl ? normalizeBaseUrl(configuredBaseUrl) : undefined;\n if (!dependencies.createClient && !process.env.MEDIARUNTIME_API_KEY?.trim()) {\n throw new UsageError(\"Set MEDIARUNTIME_API_KEY before using run or jobs\");\n }\n const client = dependencies.createClient?.({\n ...(baseUrl === undefined ? {} : { baseUrl }),\n }) ?? new MediaRuntime({ ...(baseUrl === undefined ? {} : { baseUrl }) });\n const downloadBundle = dependencies.downloadBundle ?? downloadBundleAtomically;\n const commandDependencies = { jobs: client.jobs, writeStdout, downloadBundle };\n if (command === \"run\") return await runCommand(global.args.slice(1), commandDependencies);\n if (command === \"jobs\") {\n return await runJobsCommand(global.args.slice(1), commandDependencies);\n }\n throw new UsageError(`Unknown command: ${command}`);\n } catch (error) {\n const exitCode = exitCodeFor(error);\n if (json) {\n writeStderr(`${JSON.stringify({ error: safeError(error, exitCode) })}\\n`);\n } else {\n const requestId = error instanceof MediaRuntimeApiError && error.requestId\n ? ` (request ${error.requestId})`\n : \"\";\n writeStderr(`Error: ${error instanceof Error ? error.message : \"Unknown error\"}${requestId}\\n`);\n }\n return exitCode;\n }\n}\n\nlet isEntrypoint = false;\nif (process.argv[1] !== undefined) {\n try {\n // npm exposes binaries through a symlink. Resolve both sides so the installed command\n // executes while importing dist/cli.js in tests remains side-effect free.\n isEntrypoint = realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url));\n } catch {\n isEntrypoint = false;\n }\n}\nif (isEntrypoint) process.exitCode = await executeCli(process.argv.slice(2));\n","export class CliError extends Error {\n readonly code: string;\n readonly exitCode: number;\n\n constructor(code: string, message: string, exitCode: number, options?: { cause?: unknown }) {\n super(message, options);\n this.name = \"CliError\";\n this.code = code;\n this.exitCode = exitCode;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class UsageError extends CliError {\n constructor(message: string, options?: { cause?: unknown }) {\n super(\"usage_error\", message, 2, options);\n this.name = \"UsageError\";\n }\n}\n\nexport class TriggerDeliveryError extends CliError {\n constructor(message: string, options?: { cause?: unknown }) {\n super(\"trigger_delivery_error\", message, 8, options);\n this.name = \"TriggerDeliveryError\";\n }\n}\n\nexport class BundleDownloadError extends CliError {\n constructor(message: string, options?: { cause?: unknown }) {\n super(\"bundle_download_error\", message, 9, options);\n this.name = \"BundleDownloadError\";\n }\n}\n","import type { JobDetails, JobPage, JobStatus, ListJobsParams } from \"@mediaruntime/node\";\nimport { BundleDownloadError, UsageError } from \"../errors.js\";\n\nconst UNSUCCESSFUL_TERMINAL_STATUSES = new Set([\"FAILED\", \"REJECTED\", \"PARTIAL\"]);\n\nexport interface JobsReadClient {\n list(params?: ListJobsParams): Promise<JobPage>;\n get(jobId: string): Promise<JobDetails>;\n}\n\nexport interface JobsCommandDependencies {\n jobs: JobsReadClient;\n writeStdout(text: string): void;\n downloadBundle(\n url: string,\n destination: string,\n options: { force: boolean; expectedSizeBytes?: number | null; expectedSha256?: string | null },\n ): Promise<void>;\n}\n\nfunction optionValue(args: string[], index: number, option: string): string {\n const value = args[index + 1];\n if (!value || value.startsWith(\"--\")) throw new UsageError(`${option} requires a value`);\n return value;\n}\n\nfunction detailsProjection(job: JobDetails): Record<string, unknown> {\n return {\n id: job.id,\n status: job.status,\n tier: job.tier,\n usage: job.usage,\n billing: job.billing,\n bundle: {\n available: job.bundle.available,\n expiresAt: job.bundle.expiresAt,\n sizeBytes: job.bundle.sizeBytes,\n sha256: job.bundle.sha256,\n retentionDays: job.bundle.retentionDays,\n },\n media: job.media,\n metadata: job.metadata,\n error: job.error,\n createdAt: job.createdAt,\n updatedAt: job.updatedAt,\n startedAt: job.startedAt,\n completedAt: job.completedAt,\n };\n}\n\nfunction humanDetails(job: JobDetails): string {\n const lines = [\n `Job: ${job.id}`,\n `Status: ${job.status}`,\n `Tier: ${job.tier.billed ?? job.tier.effective ?? job.tier.requested ?? \"-\"}`,\n `Bundle: ${job.bundle.available ? \"available\" : \"unavailable\"}`,\n ];\n if (job.bundle.sizeBytes !== null) lines.push(`Bundle size: ${job.bundle.sizeBytes} bytes`);\n if (job.error) lines.push(`Error: ${job.error}`);\n return `${lines.join(\"\\n\")}\\n`;\n}\n\nfunction cell(value: unknown): string {\n if (value === null || value === undefined || value === \"\") return \"-\";\n return String(value);\n}\n\nfunction humanList(page: JobPage): string {\n const rows = page.jobs.map((job) => [\n job.id,\n job.status,\n cell(job.tierBilled),\n cell(job.unitsTotal),\n job.bundleAvailable ? \"yes\" : \"no\",\n cell(job.updatedAt),\n ]);\n const headings = [\"ID\", \"STATUS\", \"TIER\", \"UNITS\", \"BUNDLE\", \"UPDATED\"];\n const widths = headings.map((heading, index) =>\n Math.max(heading.length, ...rows.map((row) => row[index]?.length ?? 0)),\n );\n const render = (row: string[]): string =>\n row.map((value, index) => value.padEnd(widths[index] ?? value.length)).join(\" \").trimEnd();\n const lines = [render(headings), render(widths.map((width) => \"-\".repeat(width))), ...rows.map(render)];\n if (page.nextCursor) lines.push(`Next cursor: ${page.nextCursor}`);\n return `${lines.join(\"\\n\")}\\n`;\n}\n\nasync function listCommand(args: string[], dependencies: JobsCommandDependencies): Promise<number> {\n let status: string | undefined;\n let limit: number | undefined;\n let cursor: string | undefined;\n let json = false;\n for (let index = 0; index < args.length; index += 1) {\n const argument = args[index];\n if (argument === \"--status\") {\n status = optionValue(args, index, argument).toUpperCase();\n index += 1;\n } else if (argument === \"--limit\") {\n const value = Number(optionValue(args, index, argument));\n if (!Number.isSafeInteger(value) || value < 1 || value > 100) {\n throw new UsageError(\"--limit must be an integer between 1 and 100\");\n }\n limit = value;\n index += 1;\n } else if (argument === \"--cursor\") {\n cursor = optionValue(args, index, argument);\n index += 1;\n } else if (argument === \"--json\") {\n json = true;\n } else {\n throw new UsageError(`Unknown jobs list option: ${argument}`);\n }\n }\n const params: ListJobsParams = {\n ...(status === undefined ? {} : { status: status as JobStatus }),\n ...(limit === undefined ? {} : { limit }),\n ...(cursor === undefined ? {} : { cursor }),\n };\n const page = await dependencies.jobs.list(params);\n dependencies.writeStdout(json ? `${JSON.stringify(page)}\\n` : humanList(page));\n return 0;\n}\n\nasync function getCommand(args: string[], dependencies: JobsCommandDependencies): Promise<number> {\n let jobId: string | undefined;\n let download: string | undefined;\n let force = false;\n let json = false;\n for (let index = 0; index < args.length; index += 1) {\n const argument = args[index];\n if (argument === \"--download\") {\n download = optionValue(args, index, argument);\n index += 1;\n } else if (argument === \"--force\") {\n force = true;\n } else if (argument === \"--json\") {\n json = true;\n } else if (argument?.startsWith(\"-\")) {\n throw new UsageError(`Unknown jobs get option: ${argument}`);\n } else if (!jobId) {\n jobId = argument;\n } else {\n throw new UsageError(\"jobs get accepts exactly one job ID\");\n }\n }\n if (!jobId) throw new UsageError(\"Usage: mediaruntime jobs get <job_id>\");\n if (force && !download) throw new UsageError(\"--force requires --download\");\n const job = await dependencies.jobs.get(jobId);\n if (!json) dependencies.writeStdout(humanDetails(job));\n\n if (UNSUCCESSFUL_TERMINAL_STATUSES.has(String(job.status).toUpperCase())) {\n if (json) dependencies.writeStdout(`${JSON.stringify(detailsProjection(job))}\\n`);\n return 6;\n }\n if (download) {\n if (String(job.status).toUpperCase() !== \"COMPLETED\" ||\n !job.bundle.available || !job.bundle.downloadUrl) {\n throw new BundleDownloadError(\"Bundle download requires a COMPLETED job with an available canonical bundle\");\n }\n try {\n await dependencies.downloadBundle(job.bundle.downloadUrl, download, {\n force,\n expectedSizeBytes: job.bundle.sizeBytes,\n expectedSha256: job.bundle.sha256,\n });\n } catch (error) {\n if (error instanceof BundleDownloadError) throw error;\n throw new BundleDownloadError(`Could not download bundle to ${download}`, { cause: error });\n }\n if (!json) dependencies.writeStdout(`Downloaded bundle to ${download}\\n`);\n }\n if (json) dependencies.writeStdout(`${JSON.stringify(detailsProjection(job))}\\n`);\n return 0;\n}\n\nexport async function runJobsCommand(\n args: string[],\n dependencies: JobsCommandDependencies,\n): Promise<number> {\n const subcommand = args[0];\n if (subcommand === \"list\") return listCommand(args.slice(1), dependencies);\n if (subcommand === \"get\") return getCommand(args.slice(1), dependencies);\n throw new UsageError(\"Usage: mediaruntime jobs <list|get>\");\n}\n","import type {\n CreateJobParams,\n JobDetails,\n JobOutput,\n JobReceiptData,\n Metadata,\n OutputAlias,\n WaitForJobOptions,\n} from \"@mediaruntime/node\";\nimport { BundleDownloadError, UsageError } from \"../errors.js\";\n\nconst UNSUCCESSFUL_TERMINAL_STATUSES = new Set([\"FAILED\", \"REJECTED\", \"PARTIAL\"]);\nconst OUTPUT_ALIASES = new Set([\n \"video.web\",\n \"video.streaming\",\n \"video.social\",\n \"audio.web\",\n \"audio.transcription\",\n \"image.web\",\n]);\n\ninterface SubmittedJob extends JobReceiptData {\n wait(options?: WaitForJobOptions): Promise<JobDetails>;\n}\n\nexport interface RunJobsClient {\n create(params: CreateJobParams): Promise<SubmittedJob>;\n}\n\nexport interface RunCommandDependencies {\n jobs: RunJobsClient;\n writeStdout(text: string): void;\n downloadBundle(\n url: string,\n destination: string,\n options: { force: boolean; expectedSizeBytes?: number | null; expectedSha256?: string | null },\n ): Promise<void>;\n}\n\ninterface RunOptions {\n source: string;\n outputs: string[];\n metadata?: Metadata;\n idempotencyKey?: string;\n wait: boolean;\n timeoutMs?: number;\n download?: string;\n force: boolean;\n json: boolean;\n}\n\nfunction optionValue(args: string[], index: number, option: string): string {\n const value = args[index + 1];\n if (!value || value.startsWith(\"--\")) throw new UsageError(`${option} requires a value`);\n return value;\n}\n\nfunction parseMetadata(value: string): Metadata {\n let parsed: unknown;\n try {\n parsed = JSON.parse(value) as unknown;\n } catch {\n throw new UsageError(\"--metadata must be a valid JSON object\");\n }\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n throw new UsageError(\"--metadata must be a JSON object\");\n }\n return parsed as Metadata;\n}\n\nfunction parseRunOptions(args: string[]): RunOptions {\n let source: string | undefined;\n const outputs: string[] = [];\n let metadata: Metadata | undefined;\n let idempotencyKey: string | undefined;\n let wait = false;\n let timeoutMs: number | undefined;\n let download: string | undefined;\n let force = false;\n let json = false;\n\n for (let index = 0; index < args.length; index += 1) {\n const argument = args[index];\n if (argument === \"--output\" || argument === \"-o\") {\n outputs.push(optionValue(args, index, argument));\n index += 1;\n } else if (argument === \"--metadata\") {\n metadata = parseMetadata(optionValue(args, index, argument));\n index += 1;\n } else if (argument === \"--idempotency-key\") {\n idempotencyKey = optionValue(args, index, argument);\n index += 1;\n } else if (argument === \"--wait\") {\n wait = true;\n } else if (argument === \"--timeout-ms\") {\n const value = Number(optionValue(args, index, argument));\n if (!Number.isSafeInteger(value) || value <= 0) {\n throw new UsageError(\"--timeout-ms must be a positive integer\");\n }\n timeoutMs = value;\n index += 1;\n } else if (argument === \"--download\") {\n download = optionValue(args, index, argument);\n wait = true;\n index += 1;\n } else if (argument === \"--force\") {\n force = true;\n } else if (argument === \"--json\") {\n json = true;\n } else if (argument?.startsWith(\"-\")) {\n throw new UsageError(`Unknown run option: ${argument}`);\n } else if (source === undefined) {\n source = argument;\n } else {\n throw new UsageError(\"run accepts exactly one source\");\n }\n }\n\n if (!source) throw new UsageError(\"Usage: mediaruntime run <source> --output <alias> [--wait]\");\n if (outputs.length === 0) throw new UsageError(\"run requires at least one --output\");\n const unsupported = outputs.find((output) => !OUTPUT_ALIASES.has(output));\n if (unsupported) throw new UsageError(`Unsupported output alias: ${unsupported}`);\n if (force && !download) throw new UsageError(\"--force requires --download\");\n if (timeoutMs !== undefined && !wait) throw new UsageError(\"--timeout-ms requires --wait or --download\");\n return {\n source,\n outputs,\n ...(metadata === undefined ? {} : { metadata }),\n ...(idempotencyKey === undefined ? {} : { idempotencyKey }),\n wait,\n ...(timeoutMs === undefined ? {} : { timeoutMs }),\n ...(download === undefined ? {} : { download }),\n force,\n json,\n };\n}\n\nfunction receiptProjection(job: SubmittedJob): JobReceiptData {\n return {\n id: job.id,\n status: job.status,\n tier: job.tier,\n requiredTier: job.requiredTier,\n outputs: job.outputs,\n message: job.message,\n };\n}\n\nfunction detailsProjection(job: JobDetails): Record<string, unknown> {\n return {\n id: job.id,\n status: job.status,\n tier: job.tier,\n usage: job.usage,\n billing: job.billing,\n bundle: {\n available: job.bundle.available,\n expiresAt: job.bundle.expiresAt,\n sizeBytes: job.bundle.sizeBytes,\n sha256: job.bundle.sha256,\n retentionDays: job.bundle.retentionDays,\n },\n media: job.media,\n metadata: job.metadata,\n error: job.error,\n createdAt: job.createdAt,\n updatedAt: job.updatedAt,\n startedAt: job.startedAt,\n completedAt: job.completedAt,\n };\n}\n\nfunction writeHumanReceipt(job: SubmittedJob, write: (text: string) => void): void {\n write(`Job: ${job.id}\\nStatus: ${job.status}\\nTier: ${job.tier || \"-\"}\\n`);\n}\n\nfunction writeHumanDetails(job: JobDetails, write: (text: string) => void): void {\n const error = job.error ? `\\nError: ${job.error}` : \"\";\n write(\n `Job: ${job.id}\\nStatus: ${job.status}\\n` +\n `Bundle: ${job.bundle.available ? \"available\" : \"unavailable\"}${error}\\n`,\n );\n}\n\nexport async function runCommand(\n args: string[],\n dependencies: RunCommandDependencies,\n): Promise<number> {\n const options = parseRunOptions(args);\n const params: CreateJobParams = {\n source: options.source,\n outputs: options.outputs as Array<OutputAlias | JobOutput>,\n ...(options.metadata === undefined ? {} : { metadata: options.metadata }),\n ...(options.idempotencyKey === undefined ? {} : { idempotencyKey: options.idempotencyKey }),\n };\n const submitted = await dependencies.jobs.create(params);\n\n if (!options.wait) {\n if (options.json) dependencies.writeStdout(`${JSON.stringify(receiptProjection(submitted))}\\n`);\n else writeHumanReceipt(submitted, dependencies.writeStdout);\n return UNSUCCESSFUL_TERMINAL_STATUSES.has(String(submitted.status).toUpperCase()) ? 6 : 0;\n }\n\n const details = await submitted.wait(\n options.timeoutMs === undefined ? {} : { timeoutMs: options.timeoutMs },\n );\n if (!options.json) writeHumanDetails(details, dependencies.writeStdout);\n if (String(details.status).toUpperCase() !== \"COMPLETED\") {\n if (options.json) dependencies.writeStdout(`${JSON.stringify(detailsProjection(details))}\\n`);\n return 6;\n }\n if (options.download) {\n const url = details.bundle.downloadUrl;\n if (!details.bundle.available || !url) {\n throw new BundleDownloadError(\"Completed job does not have an available canonical bundle\");\n }\n try {\n await dependencies.downloadBundle(url, options.download, {\n force: options.force,\n expectedSizeBytes: details.bundle.sizeBytes,\n expectedSha256: details.bundle.sha256,\n });\n } catch (error) {\n if (error instanceof BundleDownloadError) throw error;\n throw new BundleDownloadError(`Could not download bundle to ${options.download}`, { cause: error });\n }\n if (!options.json) dependencies.writeStdout(`Downloaded bundle to ${options.download}\\n`);\n }\n if (options.json) dependencies.writeStdout(`${JSON.stringify(detailsProjection(details))}\\n`);\n return 0;\n}\n","import { randomBytes, randomUUID } from \"node:crypto\";\nimport { readFile } from \"node:fs/promises\";\n\nimport { parseLocalDestination } from \"../trigger/destination.js\";\nimport {\n SUPPORTED_TRIGGER_EVENTS,\n createSyntheticTerminalEvent,\n isTriggerEventType,\n type TriggerEventType,\n} from \"../trigger/event.js\";\nimport { signSyntheticWebhook } from \"../trigger/signature.js\";\nimport { TriggerDeliveryError, UsageError } from \"../errors.js\";\n\nexport interface TriggerCommandDependencies {\n env?: Readonly<Record<string, string | undefined>>;\n fetch?: typeof globalThis.fetch;\n now?: () => number;\n randomUUID?: () => string;\n randomBytes?: (size: number) => Uint8Array;\n readFile?: (path: string, encoding: \"utf8\") => Promise<string>;\n writeStdout?: (text: string) => void;\n}\n\ninterface TriggerArguments {\n type: TriggerEventType;\n destination: URL;\n secretFile?: string;\n generateSecret: boolean;\n jobId?: string;\n accountId?: string;\n json: boolean;\n}\n\nfunction requireOptionValue(args: string[], index: number, option: string): string {\n const value = args[index + 1];\n if (!value || value.startsWith(\"--\")) throw new UsageError(`${option} requires a value`);\n return value;\n}\n\nexport function parseTriggerArguments(args: string[]): TriggerArguments {\n const typeValue = args[0];\n if (!typeValue || typeValue.startsWith(\"--\")) {\n throw new UsageError(\n `trigger requires an event type: ${SUPPORTED_TRIGGER_EVENTS.join(\", \")}`,\n );\n }\n if (!isTriggerEventType(typeValue)) {\n throw new UsageError(\n `Unsupported trigger event ${JSON.stringify(typeValue)}; expected ${SUPPORTED_TRIGGER_EVENTS.join(\", \")}`,\n );\n }\n\n let to: string | undefined;\n let secretFile: string | undefined;\n let generateSecret = false;\n let jobId: string | undefined;\n let accountId: string | undefined;\n let json = false;\n for (let index = 1; index < args.length; index += 1) {\n const option = args[index];\n if (!option) continue;\n if (option === \"--generate-secret\") {\n generateSecret = true;\n continue;\n }\n if (option === \"--json\") {\n json = true;\n continue;\n }\n if ([\"--to\", \"--secret-file\", \"--job-id\", \"--account-id\"].includes(option)) {\n const value = requireOptionValue(args, index, option);\n index += 1;\n if (option === \"--to\") to = value;\n else if (option === \"--secret-file\") secretFile = value;\n else if (option === \"--job-id\") jobId = value;\n else accountId = value;\n continue;\n }\n throw new UsageError(`Unknown trigger option: ${option}`);\n }\n\n if (!to) throw new UsageError(\"trigger requires an explicit --to local URL\");\n if (generateSecret && secretFile) {\n throw new UsageError(\"--generate-secret and --secret-file are mutually exclusive\");\n }\n if (generateSecret && json) {\n throw new UsageError(\"--generate-secret and --json are mutually exclusive\");\n }\n if (jobId !== undefined && !jobId.trim()) throw new UsageError(\"--job-id must not be empty\");\n if (accountId !== undefined && !accountId.trim()) {\n throw new UsageError(\"--account-id must not be empty\");\n }\n return {\n type: typeValue,\n destination: parseLocalDestination(to),\n ...(secretFile === undefined ? {} : { secretFile }),\n generateSecret,\n ...(jobId === undefined ? {} : { jobId: jobId.trim() }),\n ...(accountId === undefined ? {} : { accountId: accountId.trim() }),\n json,\n };\n}\n\nasync function resolveSecret(\n args: TriggerArguments,\n dependencies: Required<\n Pick<TriggerCommandDependencies, \"env\" | \"randomBytes\" | \"readFile\" | \"writeStdout\">\n >,\n): Promise<string> {\n if (args.generateSecret) {\n const generated = Buffer.from(dependencies.randomBytes(32)).toString(\"base64url\");\n dependencies.writeStdout(\n `Generated webhook secret (configure the local receiver with this value): ${generated}\\n`,\n );\n return generated;\n }\n let secret: string | undefined;\n if (args.secretFile) {\n try {\n secret = await dependencies.readFile(args.secretFile, \"utf8\");\n } catch (error) {\n throw new UsageError(`Could not read webhook secret file: ${args.secretFile}`, { cause: error });\n }\n } else {\n secret = dependencies.env.MEDIARUNTIME_WEBHOOK_SECRET;\n }\n const normalized = secret?.trim();\n if (!normalized) {\n throw new UsageError(\n \"Set MEDIARUNTIME_WEBHOOK_SECRET, pass --secret-file, or explicitly use --generate-secret\",\n );\n }\n return normalized;\n}\n\nexport async function runTriggerCommand(\n args: string[],\n dependencies: TriggerCommandDependencies = {},\n): Promise<number> {\n const parsed = parseTriggerArguments(args);\n const deps = {\n env: dependencies.env ?? process.env,\n fetch: dependencies.fetch ?? globalThis.fetch,\n now: dependencies.now ?? Date.now,\n randomUUID: dependencies.randomUUID ?? randomUUID,\n randomBytes: dependencies.randomBytes ?? randomBytes,\n readFile: dependencies.readFile ?? readFile,\n writeStdout: dependencies.writeStdout ?? ((text: string) => process.stdout.write(text)),\n };\n const secret = await resolveSecret(parsed, deps);\n const timestamp = Math.floor(deps.now() / 1000);\n const unique = deps.randomUUID();\n const eventId = `evt_cli_${unique}`;\n const jobId = parsed.jobId ?? `job_cli_${unique}`;\n const accountId = parsed.accountId ?? \"acc_cli_local\";\n const payload = createSyntheticTerminalEvent(parsed.type, {\n eventId,\n jobId,\n accountId,\n timestamp,\n });\n const signed = signSyntheticWebhook(payload, { eventId, timestamp, secret });\n\n const response = await deps.fetch(parsed.destination, {\n method: \"POST\",\n headers: signed.headers,\n body: signed.body,\n redirect: \"manual\",\n signal: AbortSignal.timeout(10_000),\n });\n if (!response.ok) {\n throw new TriggerDeliveryError(\n `Local webhook endpoint returned HTTP ${response.status}${response.statusText ? ` ${response.statusText}` : \"\"}`,\n );\n }\n if (parsed.json) {\n deps.writeStdout(`${JSON.stringify({\n eventId,\n type: parsed.type,\n jobId,\n status: payload.status,\n destination: parsed.destination.href,\n httpStatus: response.status,\n })}\\n`);\n } else {\n deps.writeStdout(\n `Sent synthetic ${parsed.type} event ${eventId} to ${parsed.destination.href} (HTTP ${response.status})\\n`,\n );\n }\n return 0;\n}\n","import { UsageError } from \"../errors.js\";\n\nexport function isLoopbackDestination(value: URL): boolean {\n let hostname = value.hostname.toLowerCase();\n if (hostname.endsWith(\".\")) hostname = hostname.slice(0, -1);\n if (hostname.startsWith(\"[\") && hostname.endsWith(\"]\")) {\n hostname = hostname.slice(1, -1);\n }\n if (hostname === \"localhost\" || hostname === \"::1\" || hostname === \"0:0:0:0:0:0:0:1\") {\n return true;\n }\n\n const octets = hostname.split(\".\");\n if (octets.length !== 4 || octets.some((part) => !/^\\d{1,3}$/.test(part))) return false;\n const numbers = octets.map(Number);\n return numbers[0] === 127 && numbers.every((part) => part >= 0 && part <= 255);\n}\n\nexport function parseLocalDestination(raw: string): URL {\n let destination: URL;\n try {\n destination = new URL(raw);\n } catch {\n throw new UsageError(\"--to must be an absolute local HTTP(S) URL\");\n }\n if (destination.protocol !== \"http:\" && destination.protocol !== \"https:\") {\n throw new UsageError(\"--to must use http:// or https://\");\n }\n if (destination.username || destination.password) {\n throw new UsageError(\"--to must not contain URL credentials\");\n }\n if (!isLoopbackDestination(destination)) {\n throw new UsageError(\n \"trigger only sends to loopback destinations (localhost, 127.0.0.0/8, or ::1)\",\n );\n }\n return destination;\n}\n","export const SUPPORTED_TRIGGER_EVENTS = [\n \"job.completed\",\n \"job.failed\",\n \"job.rejected\",\n] as const;\n\nexport type TriggerEventType = (typeof SUPPORTED_TRIGGER_EVENTS)[number];\n\nexport function isTriggerEventType(value: string): value is TriggerEventType {\n return (SUPPORTED_TRIGGER_EVENTS as readonly string[]).includes(value);\n}\n\nexport interface SyntheticEventOptions {\n eventId: string;\n jobId: string;\n accountId: string;\n timestamp: number;\n}\n\nexport function createSyntheticTerminalEvent(\n type: TriggerEventType,\n options: SyntheticEventOptions,\n): Record<string, unknown> {\n const status = type.slice(\"job.\".length).toUpperCase();\n const occurredAt = new Date(options.timestamp * 1000).toISOString();\n const base = {\n event_id: options.eventId,\n job_id: options.jobId,\n account_id: options.accountId,\n status,\n };\n\n if (type === \"job.completed\") {\n const expiresAt = new Date((options.timestamp + 7 * 24 * 60 * 60) * 1000).toISOString();\n return {\n ...base,\n completedAt: occurredAt,\n delivery: {\n mode: \"PULL\",\n retentionDays: 7,\n expiresAt,\n bundle: {\n type: \"zip\",\n filename: `${options.jobId}_outputs.zip`,\n size_bytes: 0,\n sha256: \"0\".repeat(64),\n download: {\n url: `http://127.0.0.1/__mediaruntime_synthetic__/${encodeURIComponent(options.jobId)}.zip`,\n expiresAt,\n },\n },\n layout: \"bundle_relative_v1\",\n deliverables: [],\n manifests: {},\n },\n billing: { status: \"PAID\" },\n usage: { units_total: 0 },\n meta: { synthetic: true, request_metadata: {} },\n };\n }\n\n const failed = type === \"job.failed\";\n return {\n ...base,\n [failed ? \"failedAt\" : \"rejectedAt\"]: occurredAt,\n delivery: { mode: \"NONE\" },\n billing: { status: \"RELEASED\" },\n error: {\n code: failed ? \"SYNTHETIC_ENGINE_FAILED\" : \"SYNTHETIC_REJECTED\",\n message: `Synthetic ${type} event from the MediaRuntime CLI`,\n },\n meta: { synthetic: true, request_metadata: {} },\n };\n}\n","import { createHmac } from \"node:crypto\";\n\nexport const WEBHOOK_HEADERS = {\n id: \"X-Transcoder-Id\",\n timestamp: \"X-Transcoder-Timestamp\",\n signature: \"X-Transcoder-Signature\",\n} as const;\n\nexport interface SignedWebhook {\n body: string;\n headers: Record<string, string>;\n}\n\nexport function signSyntheticWebhook(\n payload: Readonly<Record<string, unknown>>,\n options: { eventId: string; timestamp: number; secret: string },\n): SignedWebhook {\n if (!options.eventId.trim()) throw new Error(\"Webhook event ID must not be empty\");\n if (!Number.isInteger(options.timestamp) || options.timestamp < 0) {\n throw new Error(\"Webhook timestamp must be a non-negative integer\");\n }\n if (!options.secret) throw new Error(\"Webhook secret must not be empty\");\n\n // JSON is serialized once. These exact bytes are both signed and sent.\n const body = JSON.stringify(payload);\n const bodyBytes = Buffer.from(body, \"utf8\");\n const prefix = Buffer.from(`${options.timestamp}.${options.eventId}.`, \"utf8\");\n const digest = createHmac(\"sha256\", options.secret)\n .update(prefix)\n .update(bodyBytes)\n .digest(\"hex\");\n\n return {\n body,\n headers: {\n \"Content-Type\": \"application/json\",\n [WEBHOOK_HEADERS.id]: options.eventId,\n [WEBHOOK_HEADERS.timestamp]: String(options.timestamp),\n [WEBHOOK_HEADERS.signature]: `t=${options.timestamp},v1=${digest}`,\n },\n };\n}\n"],"mappings":"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","names":["randomUUID","UNSUCCESSFUL_TERMINAL_STATUSES","optionValue","detailsProjection","randomUUID"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/errors.ts","../src/auth/api.ts","../src/commands/auth.ts","../src/auth/browser.ts","../src/auth/credential-store.ts","../src/commands/jobs.ts","../src/commands/run.ts","../src/commands/trigger.ts","../src/trigger/destination.ts","../src/trigger/event.ts","../src/trigger/signature.ts"],"sourcesContent":["import { createHash, randomUUID } from \"node:crypto\";\nimport { constants, realpathSync } from \"node:fs\";\nimport {\n access,\n link,\n rename,\n rm,\n} from \"node:fs/promises\";\nimport { createWriteStream } from \"node:fs\";\nimport { basename, dirname, resolve } from \"node:path\";\nimport { Readable, Transform } from \"node:stream\";\nimport { pipeline } from \"node:stream/promises\";\nimport type { ReadableStream as NodeReadableStream } from \"node:stream/web\";\nimport { fileURLToPath } from \"node:url\";\nimport {\n JobWaitTimeoutError,\n MediaRuntime,\n MediaRuntimeApiError,\n MediaRuntimeConnectionError,\n} from \"@mediaruntime/node\";\nimport { PRODUCTION_ORIGIN } from \"./auth/api.js\";\nimport type { CredentialStore } from \"./auth/credential-store.js\";\nimport {\n resolveCredential,\n runAuthCommand,\n runLoginCommand,\n runLogoutCommand,\n type AuthCommandDependencies,\n} from \"./commands/auth.js\";\nimport {\n runJobsCommand,\n type JobsReadClient,\n} from \"./commands/jobs.js\";\nimport {\n runCommand,\n type RunJobsClient,\n} from \"./commands/run.js\";\nimport { runTriggerCommand } from \"./commands/trigger.js\";\nimport { BundleDownloadError, CliError, UsageError } from \"./errors.js\";\nimport { isLoopbackDestination } from \"./trigger/destination.js\";\n\nexport { parseTriggerArguments, runTriggerCommand } from \"./commands/trigger.js\";\nexport { createSyntheticTerminalEvent } from \"./trigger/event.js\";\nexport { signSyntheticWebhook } from \"./trigger/signature.js\";\nexport {\n resolveCredential,\n runAuthCommand,\n runAuthStatusCommand,\n runLoginCommand,\n runLogoutCommand,\n} from \"./commands/auth.js\";\n\nconst VERSION = \"0.2.0\";\nconst HELP = `MediaRuntime CLI\n\nUsage:\n mediaruntime run <source> --output <alias> [--output <alias>] [--wait]\n mediaruntime jobs list [--status <status>] [--limit <n>] [--cursor <cursor>]\n mediaruntime jobs get <job_id> [--download <bundle.zip>] [--force]\n mediaruntime trigger <job.completed|job.failed|job.rejected> --to <local-url>\n mediaruntime login [--no-browser]\n mediaruntime auth status\n mediaruntime logout [--local-only]\n\nGlobal options:\n --base-url <url> Developer override; normally leave unset\n --help Show help\n --version Show version\n\nRun/jobs options:\n --json Machine-readable, URL-redacted output\n\nAuthentication uses a secure login or MEDIARUNTIME_API_KEY.\n`;\n\ntype CliJobsClient = RunJobsClient & JobsReadClient;\n\nexport interface CliClient {\n jobs: CliJobsClient;\n}\n\nexport interface CliDependencies {\n createClient?(options: { baseUrl?: string; apiKey?: string }): CliClient;\n downloadBundle?(\n url: string,\n destination: string,\n options: {\n force: boolean;\n expectedSizeBytes?: number | null;\n expectedSha256?: string | null;\n },\n ): Promise<void>;\n writeStdout?(text: string): void;\n writeStderr?(text: string): void;\n credentialStore?: CredentialStore;\n openBrowser?(url: string): Promise<void>;\n sleep?(milliseconds: number): Promise<void>;\n now?(): number;\n randomVerifier?(): string;\n}\n\nfunction isFileExistsError(error: unknown): boolean {\n return error instanceof Error && \"code\" in error && error.code === \"EEXIST\";\n}\n\nasync function exists(path: string): Promise<boolean> {\n try {\n await access(path, constants.F_OK);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function downloadBundleAtomically(\n url: string,\n destination: string,\n options: {\n force: boolean;\n expectedSizeBytes?: number | null;\n expectedSha256?: string | null;\n },\n): Promise<void> {\n const target = resolve(destination);\n if (!options.force && await exists(target)) {\n throw new BundleDownloadError(`Refusing to overwrite existing file: ${destination}`);\n }\n const temporary = resolve(\n dirname(target),\n `.${basename(target)}.${randomUUID()}.part`,\n );\n\n try {\n let response: Response;\n try {\n response = await fetch(url);\n } catch (error) {\n throw new BundleDownloadError(\"Bundle download failed before receiving a response\", { cause: error });\n }\n if (!response.ok) {\n await response.body?.cancel();\n throw new BundleDownloadError(`Bundle download failed with HTTP ${response.status}`);\n }\n if (!response.body) throw new BundleDownloadError(\"Bundle download returned an empty response body\");\n const hash = createHash(\"sha256\");\n let sizeBytes = 0;\n const integrity = new Transform({\n transform(chunk: Buffer, _encoding, callback) {\n sizeBytes += chunk.length;\n hash.update(chunk);\n callback(null, chunk);\n },\n });\n await pipeline(\n Readable.fromWeb(response.body as unknown as NodeReadableStream<Uint8Array>),\n integrity,\n createWriteStream(temporary, { flags: \"wx\", mode: 0o600 }),\n );\n const sha256 = hash.digest(\"hex\");\n if (options.expectedSizeBytes !== undefined && options.expectedSizeBytes !== null &&\n sizeBytes !== options.expectedSizeBytes) {\n throw new BundleDownloadError(\n `Bundle size mismatch: expected ${options.expectedSizeBytes} bytes, received ${sizeBytes}`,\n );\n }\n if (options.expectedSha256 && sha256 !== options.expectedSha256.toLowerCase()) {\n throw new BundleDownloadError(\"Bundle SHA-256 mismatch\");\n }\n\n if (options.force) {\n await rename(temporary, target);\n } else {\n try {\n // A hard link publishes the completed same-filesystem temporary file atomically\n // and fails rather than replacing a destination created during the download.\n await link(temporary, target);\n } catch (error) {\n if (isFileExistsError(error)) {\n throw new BundleDownloadError(`Refusing to overwrite existing file: ${destination}`);\n }\n throw error;\n }\n await rm(temporary);\n }\n } catch (error) {\n await rm(temporary, { force: true });\n if (error instanceof BundleDownloadError) throw error;\n throw new BundleDownloadError(`Could not write bundle to ${destination}`, { cause: error });\n }\n}\n\nfunction normalizeBaseUrl(raw: string): string {\n let value: URL;\n try {\n value = new URL(raw);\n } catch {\n throw new UsageError(\"--base-url must be an absolute HTTP(S) origin\");\n }\n if (value.username || value.password || value.search || value.hash || value.pathname !== \"/\") {\n throw new UsageError(\"--base-url must be an origin without credentials, path, query, or fragment\");\n }\n if (value.protocol !== \"https:\" && !(value.protocol === \"http:\" && isLoopbackDestination(value))) {\n throw new UsageError(\"--base-url must use HTTPS unless it points to loopback\");\n }\n return value.origin;\n}\n\nfunction extractGlobalOptions(argv: string[]): { baseUrl?: string; args: string[] } {\n const args = [...argv];\n let baseUrl: string | undefined;\n while (args[0] === \"--base-url\") {\n const value = args[1];\n if (!value || value.startsWith(\"--\")) throw new UsageError(\"--base-url requires a value\");\n baseUrl = normalizeBaseUrl(value);\n args.splice(0, 2);\n }\n return { ...(baseUrl === undefined ? {} : { baseUrl }), args };\n}\n\nfunction safeApiError(error: MediaRuntimeApiError): Record<string, unknown> {\n return {\n name: error.name,\n message: error.message,\n code: error.code,\n status: error.status,\n retryable: error.retryable,\n requestId: error.requestId ?? null,\n field: error.field ?? null,\n };\n}\n\nfunction exitCodeFor(error: unknown): number {\n if (error instanceof CliError) return error.exitCode;\n if (error instanceof JobWaitTimeoutError) return 7;\n if (error instanceof MediaRuntimeConnectionError) return 5;\n if (error instanceof MediaRuntimeApiError) {\n if ([401, 402, 403].includes(error.status)) return 3;\n if (error.status >= 500) return 5;\n return 4;\n }\n if (error instanceof DOMException && error.name === \"AbortError\") return 130;\n if (error instanceof TypeError && /fetch|network|socket|connect/i.test(error.message)) return 5;\n return 1;\n}\n\nfunction safeError(error: unknown, exitCode: number): Record<string, unknown> {\n if (error instanceof MediaRuntimeApiError) return { ...safeApiError(error), exitCode };\n return {\n code: error instanceof CliError ? error.code : \"cli_error\",\n message: error instanceof Error ? error.message : \"Unknown error\",\n exitCode,\n };\n}\n\nexport async function executeCli(\n argv: string[],\n dependencies: CliDependencies = {},\n): Promise<number> {\n const writeStdout = dependencies.writeStdout ?? ((text: string) => process.stdout.write(text));\n const writeStderr = dependencies.writeStderr ?? ((text: string) => process.stderr.write(text));\n const json = argv.includes(\"--json\");\n try {\n const global = extractGlobalOptions(argv);\n const command = global.args[0];\n if (!command || command === \"--help\" || command === \"-h\") {\n writeStdout(HELP);\n return 0;\n }\n if (command === \"--version\" || command === \"-v\") {\n writeStdout(`${VERSION}\\n`);\n return 0;\n }\n if (command === \"trigger\") {\n return await runTriggerCommand(global.args.slice(1), { writeStdout });\n }\n\n const configuredBaseUrl = global.baseUrl ?? process.env.MEDIARUNTIME_API_URL;\n const baseUrl = configuredBaseUrl ? normalizeBaseUrl(configuredBaseUrl) : undefined;\n const authBaseUrl = baseUrl ?? PRODUCTION_ORIGIN;\n const authDependencies: AuthCommandDependencies = {\n ...(dependencies.credentialStore ? { credentialStore: dependencies.credentialStore } : {}),\n ...(dependencies.openBrowser ? { openBrowser: dependencies.openBrowser } : {}),\n ...(dependencies.sleep ? { sleep: dependencies.sleep } : {}),\n ...(dependencies.now ? { now: dependencies.now } : {}),\n ...(dependencies.randomVerifier ? { randomVerifier: dependencies.randomVerifier } : {}),\n writeStdout,\n };\n if (command === \"login\") return await runLoginCommand(global.args.slice(1), authBaseUrl, authDependencies);\n if (command === \"auth\") return await runAuthCommand(global.args.slice(1), authBaseUrl, authDependencies);\n if (command === \"logout\") return await runLogoutCommand(global.args.slice(1), authBaseUrl, authDependencies);\n\n let apiKey = process.env.MEDIARUNTIME_API_KEY?.trim();\n if (!apiKey && (!dependencies.createClient || dependencies.credentialStore)) {\n apiKey = (await resolveCredential(authBaseUrl, authDependencies))?.apiKey;\n }\n if (!dependencies.createClient && !apiKey) {\n throw new UsageError(\"Run mediaruntime login or set MEDIARUNTIME_API_KEY before using run or jobs\");\n }\n const client = dependencies.createClient?.({\n ...(baseUrl === undefined ? {} : { baseUrl }),\n ...(apiKey === undefined ? {} : { apiKey }),\n }) ?? new MediaRuntime({\n ...(baseUrl === undefined ? {} : { baseUrl }),\n ...(apiKey === undefined ? {} : { apiKey }),\n });\n const downloadBundle = dependencies.downloadBundle ?? downloadBundleAtomically;\n const commandDependencies = { jobs: client.jobs, writeStdout, downloadBundle };\n if (command === \"run\") return await runCommand(global.args.slice(1), commandDependencies);\n if (command === \"jobs\") {\n return await runJobsCommand(global.args.slice(1), commandDependencies);\n }\n throw new UsageError(`Unknown command: ${command}`);\n } catch (error) {\n const exitCode = exitCodeFor(error);\n if (json) {\n writeStderr(`${JSON.stringify({ error: safeError(error, exitCode) })}\\n`);\n } else {\n const requestId = error instanceof MediaRuntimeApiError && error.requestId\n ? ` (request ${error.requestId})`\n : \"\";\n writeStderr(`Error: ${error instanceof Error ? error.message : \"Unknown error\"}${requestId}\\n`);\n }\n return exitCode;\n }\n}\n\nlet isEntrypoint = false;\nif (process.argv[1] !== undefined) {\n try {\n // npm exposes binaries through a symlink. Resolve both sides so the installed command\n // executes while importing dist/cli.js in tests remains side-effect free.\n isEntrypoint = realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url));\n } catch {\n isEntrypoint = false;\n }\n}\nif (isEntrypoint) process.exitCode = await executeCli(process.argv.slice(2));\n","export class CliError extends Error {\n readonly code: string;\n readonly exitCode: number;\n\n constructor(code: string, message: string, exitCode: number, options?: { cause?: unknown }) {\n super(message, options);\n this.name = \"CliError\";\n this.code = code;\n this.exitCode = exitCode;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class UsageError extends CliError {\n constructor(message: string, options?: { cause?: unknown }) {\n super(\"usage_error\", message, 2, options);\n this.name = \"UsageError\";\n }\n}\n\nexport class TriggerDeliveryError extends CliError {\n constructor(message: string, options?: { cause?: unknown }) {\n super(\"trigger_delivery_error\", message, 8, options);\n this.name = \"TriggerDeliveryError\";\n }\n}\n\nexport class BundleDownloadError extends CliError {\n constructor(message: string, options?: { cause?: unknown }) {\n super(\"bundle_download_error\", message, 9, options);\n this.name = \"BundleDownloadError\";\n }\n}\n","import { CliError } from \"../errors.js\";\n\nexport const PRODUCTION_ORIGIN = \"https://mediaruntime.com\";\n\ninterface ErrorPayload {\n error?: unknown;\n detail?: unknown;\n}\n\nexport interface DeviceAuthorization {\n deviceCode: string;\n userCode: string;\n verificationUri: string;\n verificationUriComplete: string;\n expiresIn: number;\n interval: number;\n}\n\nexport interface IssuedCredential {\n apiKey: string;\n accountId: string;\n keyId: string;\n credentialType: \"cli\";\n}\n\nexport interface CredentialStatus {\n status: \"authenticated\";\n accountId: string;\n keyId: string;\n credentialType: \"cli\" | \"api_key\";\n}\n\nexport class AuthApiError extends CliError {\n readonly status: number;\n\n constructor(code: string, message: string, status: number) {\n super(code, message, status >= 500 ? 5 : 3);\n this.name = \"AuthApiError\";\n this.status = status;\n }\n}\n\nfunction endpoint(baseUrl: string, path: string): string {\n return `${baseUrl.replace(/\\/+$/, \"\")}/api/cli/auth/${path}`;\n}\n\nasync function responsePayload(response: Response): Promise<Record<string, unknown>> {\n try {\n const value: unknown = await response.json();\n return value && typeof value === \"object\" && !Array.isArray(value)\n ? value as Record<string, unknown>\n : {};\n } catch {\n return {};\n }\n}\n\nasync function post(\n baseUrl: string,\n path: string,\n body: Record<string, unknown>,\n apiKey?: string,\n): Promise<Record<string, unknown>> {\n let response: Response;\n try {\n response = await fetch(endpoint(baseUrl, path), {\n method: \"POST\",\n redirect: \"error\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...(apiKey ? { \"X-API-Key\": apiKey } : {}),\n },\n body: JSON.stringify(body),\n });\n } catch (error) {\n throw new CliError(\"auth_connection_error\", \"Could not reach MediaRuntime authentication\", 5, { cause: error });\n }\n const payload = await responsePayload(response);\n if (!response.ok) {\n const errorPayload = payload as ErrorPayload;\n throw new AuthApiError(\n typeof errorPayload.error === \"string\" ? errorPayload.error : \"authorization_failed\",\n typeof errorPayload.detail === \"string\" ? errorPayload.detail : `Authentication failed with HTTP ${response.status}`,\n response.status,\n );\n }\n return payload;\n}\n\nfunction requireString(payload: Record<string, unknown>, key: string): string {\n const value = payload[key];\n if (typeof value !== \"string\" || !value) throw new CliError(\"invalid_auth_response\", \"MediaRuntime returned an invalid authentication response\", 5);\n return value;\n}\n\nexport async function startAuthorization(baseUrl: string, codeChallenge: string): Promise<DeviceAuthorization> {\n const payload = await post(baseUrl, \"start\", { codeChallenge, clientName: \"MediaRuntime CLI\" });\n return {\n deviceCode: requireString(payload, \"deviceCode\"),\n userCode: requireString(payload, \"userCode\"),\n verificationUri: requireString(payload, \"verificationUri\"),\n verificationUriComplete: requireString(payload, \"verificationUriComplete\"),\n expiresIn: Number(payload.expiresIn),\n interval: Number(payload.interval),\n };\n}\n\nexport async function exchangeAuthorization(baseUrl: string, deviceCode: string, codeVerifier: string): Promise<IssuedCredential> {\n const payload = await post(baseUrl, \"token\", { deviceCode, codeVerifier });\n const credentialType = requireString(payload, \"credentialType\");\n if (credentialType !== \"cli\") throw new CliError(\"invalid_auth_response\", \"MediaRuntime returned the wrong credential type\", 5);\n return {\n apiKey: requireString(payload, \"apiKey\"),\n accountId: requireString(payload, \"accountId\"),\n keyId: requireString(payload, \"keyId\"),\n credentialType,\n };\n}\n\nexport async function inspectCredential(baseUrl: string, apiKey: string): Promise<CredentialStatus> {\n const payload = await post(baseUrl, \"status\", {}, apiKey);\n const credentialType = requireString(payload, \"credentialType\");\n if (credentialType !== \"cli\" && credentialType !== \"api_key\") {\n throw new CliError(\"invalid_auth_response\", \"MediaRuntime returned the wrong credential type\", 5);\n }\n return {\n status: \"authenticated\",\n accountId: requireString(payload, \"accountId\"),\n keyId: requireString(payload, \"keyId\"),\n credentialType,\n };\n}\n\nexport async function revokeCredential(baseUrl: string, apiKey: string): Promise<void> {\n await post(baseUrl, \"revoke\", {}, apiKey);\n}\n","import { createHash, randomBytes } from \"node:crypto\";\nimport {\n AuthApiError,\n exchangeAuthorization,\n inspectCredential,\n revokeCredential,\n startAuthorization,\n} from \"../auth/api.js\";\nimport { openBrowser } from \"../auth/browser.js\";\nimport {\n OsCredentialStore,\n type CredentialStore,\n type StoredCredential,\n} from \"../auth/credential-store.js\";\nimport { CliError, UsageError } from \"../errors.js\";\n\nexport interface AuthCommandDependencies {\n credentialStore?: CredentialStore;\n openBrowser?: (url: string) => Promise<void>;\n sleep?: (milliseconds: number) => Promise<void>;\n now?: () => number;\n randomVerifier?: () => string;\n writeStdout?: (text: string) => void;\n}\n\nconst defaultSleep = (milliseconds: number) => new Promise<void>((resolve) => setTimeout(resolve, milliseconds));\n\nfunction verifierChallenge(verifier: string): string {\n return createHash(\"sha256\").update(verifier).digest(\"base64url\");\n}\n\nfunction defaultVerifier(): string {\n return randomBytes(32).toString(\"base64url\");\n}\n\nfunction store(dependencies: AuthCommandDependencies): CredentialStore {\n return dependencies.credentialStore ?? new OsCredentialStore();\n}\n\nfunction assertOnly(args: string[], allowed: string[]): void {\n const unexpected = args.find((arg) => !allowed.includes(arg));\n if (unexpected) throw new UsageError(`Unknown authentication option: ${unexpected}`);\n}\n\nexport async function resolveCredential(\n baseUrl: string,\n dependencies: AuthCommandDependencies = {},\n): Promise<{ apiKey: string; source: \"environment\" | \"keychain\"; stored?: StoredCredential } | null> {\n const environment = process.env.MEDIARUNTIME_API_KEY?.trim();\n if (environment) return { apiKey: environment, source: \"environment\" };\n const stored = await store(dependencies).get(baseUrl);\n return stored ? { apiKey: stored.apiKey, source: \"keychain\", stored } : null;\n}\n\nexport async function runLoginCommand(\n args: string[],\n baseUrl: string,\n dependencies: AuthCommandDependencies = {},\n): Promise<number> {\n assertOnly(args, [\"--no-browser\"]);\n const noBrowser = args.includes(\"--no-browser\");\n const write = dependencies.writeStdout ?? ((text: string) => process.stdout.write(text));\n const existing = await store(dependencies).get(baseUrl);\n if (existing) {\n try {\n await inspectCredential(baseUrl, existing.apiKey);\n write(`Already logged in to ${baseUrl} (account ${existing.accountId}).\\n`);\n return 0;\n } catch (error) {\n if (!(error instanceof AuthApiError) || error.status >= 500) throw error;\n }\n }\n\n const verifier = dependencies.randomVerifier?.() ?? defaultVerifier();\n if (verifier.length < 43 || verifier.length > 128) {\n throw new CliError(\"invalid_verifier\", \"Generated PKCE verifier is invalid\", 1);\n }\n const authorization = await startAuthorization(baseUrl, verifierChallenge(verifier));\n if (!Number.isFinite(authorization.expiresIn) || authorization.expiresIn <= 0 ||\n !Number.isFinite(authorization.interval) || authorization.interval < 1) {\n throw new CliError(\"invalid_auth_response\", \"MediaRuntime returned invalid authorization timing\", 5);\n }\n\n write(`Open this URL to authorize MediaRuntime CLI:\\n${authorization.verificationUriComplete}\\n\\n`);\n write(`Confirm code: ${authorization.userCode}\\n`);\n if (!noBrowser) await (dependencies.openBrowser ?? openBrowser)(authorization.verificationUriComplete);\n write(\"Waiting for browser approval…\\n\");\n\n const now = dependencies.now ?? Date.now;\n const sleep = dependencies.sleep ?? defaultSleep;\n const deadline = now() + authorization.expiresIn * 1000;\n let credential;\n while (now() < deadline) {\n try {\n credential = await exchangeAuthorization(baseUrl, authorization.deviceCode, verifier);\n break;\n } catch (error) {\n if (error instanceof AuthApiError && error.code === \"authorization_pending\") {\n await sleep(authorization.interval * 1000);\n continue;\n }\n throw error;\n }\n }\n if (!credential) throw new CliError(\"authorization_timeout\", \"Browser authorization expired; run mediaruntime login again\", 7);\n\n try {\n await inspectCredential(baseUrl, credential.apiKey);\n await store(dependencies).set({\n apiKey: credential.apiKey,\n accountId: credential.accountId,\n keyId: credential.keyId,\n baseUrl,\n createdAt: new Date(now()).toISOString(),\n });\n } catch (error) {\n await revokeCredential(baseUrl, credential.apiKey).catch(() => undefined);\n throw error;\n }\n\n write(`Logged in to ${baseUrl} (account ${credential.accountId}).\\n`);\n if (process.env.MEDIARUNTIME_API_KEY?.trim()) {\n write(\"MEDIARUNTIME_API_KEY is set and will take precedence over this login.\\n\");\n }\n return 0;\n}\n\nexport async function runAuthStatusCommand(\n args: string[],\n baseUrl: string,\n dependencies: AuthCommandDependencies = {},\n): Promise<number> {\n assertOnly(args, [\"--json\"]);\n const json = args.includes(\"--json\");\n const write = dependencies.writeStdout ?? ((text: string) => process.stdout.write(text));\n const resolved = await resolveCredential(baseUrl, dependencies);\n if (!resolved) throw new CliError(\"authentication_required\", \"Run mediaruntime login or set MEDIARUNTIME_API_KEY\", 3);\n const status = await inspectCredential(baseUrl, resolved.apiKey);\n if (json) {\n write(`${JSON.stringify({ authenticated: true, source: resolved.source, ...status })}\\n`);\n } else {\n write(`Authenticated via ${resolved.source} (account ${status.accountId}, key ${status.keyId}).\\n`);\n }\n return 0;\n}\n\nexport async function runLogoutCommand(\n args: string[],\n baseUrl: string,\n dependencies: AuthCommandDependencies = {},\n): Promise<number> {\n assertOnly(args, [\"--local-only\"]);\n const localOnly = args.includes(\"--local-only\");\n const write = dependencies.writeStdout ?? ((text: string) => process.stdout.write(text));\n const credentialStore = store(dependencies);\n const credential = await credentialStore.get(baseUrl);\n if (!credential) {\n write(`No stored login for ${baseUrl}.\\n`);\n if (process.env.MEDIARUNTIME_API_KEY?.trim()) write(\"MEDIARUNTIME_API_KEY remains active in this shell.\\n\");\n return 0;\n }\n if (!localOnly) await revokeCredential(baseUrl, credential.apiKey);\n await credentialStore.delete(baseUrl);\n write(localOnly ? \"Removed the local CLI login without server revocation.\\n\" : \"Logged out and revoked the CLI credential.\\n\");\n if (process.env.MEDIARUNTIME_API_KEY?.trim()) write(\"MEDIARUNTIME_API_KEY remains active in this shell.\\n\");\n return 0;\n}\n\nexport async function runAuthCommand(\n args: string[],\n baseUrl: string,\n dependencies: AuthCommandDependencies = {},\n): Promise<number> {\n const subcommand = args[0];\n if (subcommand !== \"status\") throw new UsageError(\"Usage: mediaruntime auth status [--json]\");\n return runAuthStatusCommand(args.slice(1), baseUrl, dependencies);\n}\n","import { spawn } from \"node:child_process\";\nimport { CliError } from \"../errors.js\";\n\nexport async function openBrowser(url: string): Promise<void> {\n const command = process.platform === \"darwin\"\n ? { executable: \"open\", args: [url] }\n : process.platform === \"win32\"\n ? { executable: \"rundll32\", args: [\"url.dll,FileProtocolHandler\", url] }\n : { executable: \"xdg-open\", args: [url] };\n\n await new Promise<void>((resolve, reject) => {\n const child = spawn(command.executable, command.args, {\n detached: true,\n stdio: \"ignore\",\n });\n child.once(\"error\", (error) => reject(new CliError(\n \"browser_open_failed\",\n \"Could not open a browser; rerun with --no-browser and open the printed URL\",\n 2,\n { cause: error },\n )));\n child.once(\"spawn\", () => {\n child.unref();\n resolve();\n });\n });\n}\n","import { CliError } from \"../errors.js\";\n\nconst SERVICE = \"mediaruntime-cli\";\n\nexport interface StoredCredential {\n apiKey: string;\n accountId: string;\n keyId: string;\n baseUrl: string;\n createdAt: string;\n}\n\nexport interface CredentialStore {\n get(baseUrl: string): Promise<StoredCredential | null>;\n set(credential: StoredCredential): Promise<void>;\n delete(baseUrl: string): Promise<void>;\n}\n\nfunction vaultAccount(baseUrl: string): string {\n return new URL(baseUrl).host.toLowerCase();\n}\n\nfunction validCredential(value: unknown): value is StoredCredential {\n if (!value || typeof value !== \"object\") return false;\n const item = value as Record<string, unknown>;\n return typeof item.apiKey === \"string\" && item.apiKey.startsWith(\"sk_\") &&\n typeof item.accountId === \"string\" && item.accountId.length > 0 &&\n typeof item.keyId === \"string\" && item.keyId.length > 0 &&\n typeof item.baseUrl === \"string\" && item.baseUrl.length > 0 &&\n typeof item.createdAt === \"string\" && item.createdAt.length > 0;\n}\n\nasync function entry(baseUrl: string) {\n try {\n const { Entry } = await import(\"@napi-rs/keyring\");\n return new Entry(SERVICE, vaultAccount(baseUrl));\n } catch (error) {\n throw new CliError(\n \"credential_store_unavailable\",\n \"The operating-system credential vault is unavailable; use MEDIARUNTIME_API_KEY on this machine\",\n 2,\n { cause: error },\n );\n }\n}\n\nexport class OsCredentialStore implements CredentialStore {\n async get(baseUrl: string): Promise<StoredCredential | null> {\n try {\n const raw = (await entry(baseUrl)).getPassword();\n if (!raw) return null;\n const parsed: unknown = JSON.parse(raw);\n if (!validCredential(parsed) || parsed.baseUrl !== baseUrl) {\n throw new Error(\"Stored credential has an invalid shape\");\n }\n return parsed;\n } catch (error) {\n if (error instanceof SyntaxError || (error instanceof Error && error.message.includes(\"invalid shape\"))) {\n throw new CliError(\"credential_store_invalid\", \"The stored MediaRuntime credential is invalid; run mediaruntime logout --local-only\", 3);\n }\n if (error instanceof CliError) throw error;\n // Keyring implementations return a not-found error rather than null on some platforms.\n if (error instanceof Error && /not found|no entry|no matching|item.*exist/i.test(error.message)) return null;\n throw new CliError(\"credential_store_error\", \"Could not read the operating-system credential vault\", 2, { cause: error });\n }\n }\n\n async set(credential: StoredCredential): Promise<void> {\n try {\n (await entry(credential.baseUrl)).setPassword(JSON.stringify(credential));\n } catch (error) {\n if (error instanceof CliError) throw error;\n throw new CliError(\"credential_store_error\", \"Could not save the login in the operating-system credential vault\", 2, { cause: error });\n }\n }\n\n async delete(baseUrl: string): Promise<void> {\n try {\n (await entry(baseUrl)).deletePassword();\n } catch (error) {\n if (error instanceof CliError) throw error;\n if (error instanceof Error && /not found|no entry|no matching|item.*exist/i.test(error.message)) return;\n throw new CliError(\"credential_store_error\", \"Could not remove the login from the operating-system credential vault\", 2, { cause: error });\n }\n }\n}\n","import type { JobDetails, JobPage, JobStatus, ListJobsParams } from \"@mediaruntime/node\";\nimport { BundleDownloadError, UsageError } from \"../errors.js\";\n\nconst UNSUCCESSFUL_TERMINAL_STATUSES = new Set([\"FAILED\", \"REJECTED\", \"PARTIAL\"]);\n\nexport interface JobsReadClient {\n list(params?: ListJobsParams): Promise<JobPage>;\n get(jobId: string): Promise<JobDetails>;\n}\n\nexport interface JobsCommandDependencies {\n jobs: JobsReadClient;\n writeStdout(text: string): void;\n downloadBundle(\n url: string,\n destination: string,\n options: { force: boolean; expectedSizeBytes?: number | null; expectedSha256?: string | null },\n ): Promise<void>;\n}\n\nfunction optionValue(args: string[], index: number, option: string): string {\n const value = args[index + 1];\n if (!value || value.startsWith(\"--\")) throw new UsageError(`${option} requires a value`);\n return value;\n}\n\nfunction detailsProjection(job: JobDetails): Record<string, unknown> {\n return {\n id: job.id,\n status: job.status,\n tier: job.tier,\n usage: job.usage,\n billing: job.billing,\n bundle: {\n available: job.bundle.available,\n expiresAt: job.bundle.expiresAt,\n sizeBytes: job.bundle.sizeBytes,\n sha256: job.bundle.sha256,\n retentionDays: job.bundle.retentionDays,\n },\n media: job.media,\n metadata: job.metadata,\n error: job.error,\n createdAt: job.createdAt,\n updatedAt: job.updatedAt,\n startedAt: job.startedAt,\n completedAt: job.completedAt,\n };\n}\n\nfunction humanDetails(job: JobDetails): string {\n const lines = [\n `Job: ${job.id}`,\n `Status: ${job.status}`,\n `Tier: ${job.tier.billed ?? job.tier.effective ?? job.tier.requested ?? \"-\"}`,\n `Bundle: ${job.bundle.available ? \"available\" : \"unavailable\"}`,\n ];\n if (job.bundle.sizeBytes !== null) lines.push(`Bundle size: ${job.bundle.sizeBytes} bytes`);\n if (job.error) lines.push(`Error: ${job.error}`);\n return `${lines.join(\"\\n\")}\\n`;\n}\n\nfunction cell(value: unknown): string {\n if (value === null || value === undefined || value === \"\") return \"-\";\n return String(value);\n}\n\nfunction humanList(page: JobPage): string {\n const rows = page.jobs.map((job) => [\n job.id,\n job.status,\n cell(job.tierBilled),\n cell(job.unitsTotal),\n job.bundleAvailable ? \"yes\" : \"no\",\n cell(job.updatedAt),\n ]);\n const headings = [\"ID\", \"STATUS\", \"TIER\", \"UNITS\", \"BUNDLE\", \"UPDATED\"];\n const widths = headings.map((heading, index) =>\n Math.max(heading.length, ...rows.map((row) => row[index]?.length ?? 0)),\n );\n const render = (row: string[]): string =>\n row.map((value, index) => value.padEnd(widths[index] ?? value.length)).join(\" \").trimEnd();\n const lines = [render(headings), render(widths.map((width) => \"-\".repeat(width))), ...rows.map(render)];\n if (page.nextCursor) lines.push(`Next cursor: ${page.nextCursor}`);\n return `${lines.join(\"\\n\")}\\n`;\n}\n\nasync function listCommand(args: string[], dependencies: JobsCommandDependencies): Promise<number> {\n let status: string | undefined;\n let limit: number | undefined;\n let cursor: string | undefined;\n let json = false;\n for (let index = 0; index < args.length; index += 1) {\n const argument = args[index];\n if (argument === \"--status\") {\n status = optionValue(args, index, argument).toUpperCase();\n index += 1;\n } else if (argument === \"--limit\") {\n const value = Number(optionValue(args, index, argument));\n if (!Number.isSafeInteger(value) || value < 1 || value > 100) {\n throw new UsageError(\"--limit must be an integer between 1 and 100\");\n }\n limit = value;\n index += 1;\n } else if (argument === \"--cursor\") {\n cursor = optionValue(args, index, argument);\n index += 1;\n } else if (argument === \"--json\") {\n json = true;\n } else {\n throw new UsageError(`Unknown jobs list option: ${argument}`);\n }\n }\n const params: ListJobsParams = {\n ...(status === undefined ? {} : { status: status as JobStatus }),\n ...(limit === undefined ? {} : { limit }),\n ...(cursor === undefined ? {} : { cursor }),\n };\n const page = await dependencies.jobs.list(params);\n dependencies.writeStdout(json ? `${JSON.stringify(page)}\\n` : humanList(page));\n return 0;\n}\n\nasync function getCommand(args: string[], dependencies: JobsCommandDependencies): Promise<number> {\n let jobId: string | undefined;\n let download: string | undefined;\n let force = false;\n let json = false;\n for (let index = 0; index < args.length; index += 1) {\n const argument = args[index];\n if (argument === \"--download\") {\n download = optionValue(args, index, argument);\n index += 1;\n } else if (argument === \"--force\") {\n force = true;\n } else if (argument === \"--json\") {\n json = true;\n } else if (argument?.startsWith(\"-\")) {\n throw new UsageError(`Unknown jobs get option: ${argument}`);\n } else if (!jobId) {\n jobId = argument;\n } else {\n throw new UsageError(\"jobs get accepts exactly one job ID\");\n }\n }\n if (!jobId) throw new UsageError(\"Usage: mediaruntime jobs get <job_id>\");\n if (force && !download) throw new UsageError(\"--force requires --download\");\n const job = await dependencies.jobs.get(jobId);\n if (!json) dependencies.writeStdout(humanDetails(job));\n\n if (UNSUCCESSFUL_TERMINAL_STATUSES.has(String(job.status).toUpperCase())) {\n if (json) dependencies.writeStdout(`${JSON.stringify(detailsProjection(job))}\\n`);\n return 6;\n }\n if (download) {\n if (String(job.status).toUpperCase() !== \"COMPLETED\" ||\n !job.bundle.available || !job.bundle.downloadUrl) {\n throw new BundleDownloadError(\"Bundle download requires a COMPLETED job with an available canonical bundle\");\n }\n try {\n await dependencies.downloadBundle(job.bundle.downloadUrl, download, {\n force,\n expectedSizeBytes: job.bundle.sizeBytes,\n expectedSha256: job.bundle.sha256,\n });\n } catch (error) {\n if (error instanceof BundleDownloadError) throw error;\n throw new BundleDownloadError(`Could not download bundle to ${download}`, { cause: error });\n }\n if (!json) dependencies.writeStdout(`Downloaded bundle to ${download}\\n`);\n }\n if (json) dependencies.writeStdout(`${JSON.stringify(detailsProjection(job))}\\n`);\n return 0;\n}\n\nexport async function runJobsCommand(\n args: string[],\n dependencies: JobsCommandDependencies,\n): Promise<number> {\n const subcommand = args[0];\n if (subcommand === \"list\") return listCommand(args.slice(1), dependencies);\n if (subcommand === \"get\") return getCommand(args.slice(1), dependencies);\n throw new UsageError(\"Usage: mediaruntime jobs <list|get>\");\n}\n","import type {\n CreateJobParams,\n JobDetails,\n JobOutput,\n JobReceiptData,\n Metadata,\n OutputAlias,\n WaitForJobOptions,\n} from \"@mediaruntime/node\";\nimport { BundleDownloadError, UsageError } from \"../errors.js\";\n\nconst UNSUCCESSFUL_TERMINAL_STATUSES = new Set([\"FAILED\", \"REJECTED\", \"PARTIAL\"]);\nconst OUTPUT_ALIASES = new Set([\n \"video.web\",\n \"video.streaming\",\n \"video.social\",\n \"audio.web\",\n \"audio.transcription\",\n \"image.web\",\n]);\n\ninterface SubmittedJob extends JobReceiptData {\n wait(options?: WaitForJobOptions): Promise<JobDetails>;\n}\n\nexport interface RunJobsClient {\n create(params: CreateJobParams): Promise<SubmittedJob>;\n}\n\nexport interface RunCommandDependencies {\n jobs: RunJobsClient;\n writeStdout(text: string): void;\n downloadBundle(\n url: string,\n destination: string,\n options: { force: boolean; expectedSizeBytes?: number | null; expectedSha256?: string | null },\n ): Promise<void>;\n}\n\ninterface RunOptions {\n source: string;\n outputs: string[];\n metadata?: Metadata;\n idempotencyKey?: string;\n wait: boolean;\n timeoutMs?: number;\n download?: string;\n force: boolean;\n json: boolean;\n}\n\nfunction optionValue(args: string[], index: number, option: string): string {\n const value = args[index + 1];\n if (!value || value.startsWith(\"--\")) throw new UsageError(`${option} requires a value`);\n return value;\n}\n\nfunction parseMetadata(value: string): Metadata {\n let parsed: unknown;\n try {\n parsed = JSON.parse(value) as unknown;\n } catch {\n throw new UsageError(\"--metadata must be a valid JSON object\");\n }\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n throw new UsageError(\"--metadata must be a JSON object\");\n }\n return parsed as Metadata;\n}\n\nfunction parseRunOptions(args: string[]): RunOptions {\n let source: string | undefined;\n const outputs: string[] = [];\n let metadata: Metadata | undefined;\n let idempotencyKey: string | undefined;\n let wait = false;\n let timeoutMs: number | undefined;\n let download: string | undefined;\n let force = false;\n let json = false;\n\n for (let index = 0; index < args.length; index += 1) {\n const argument = args[index];\n if (argument === \"--output\" || argument === \"-o\") {\n outputs.push(optionValue(args, index, argument));\n index += 1;\n } else if (argument === \"--metadata\") {\n metadata = parseMetadata(optionValue(args, index, argument));\n index += 1;\n } else if (argument === \"--idempotency-key\") {\n idempotencyKey = optionValue(args, index, argument);\n index += 1;\n } else if (argument === \"--wait\") {\n wait = true;\n } else if (argument === \"--timeout-ms\") {\n const value = Number(optionValue(args, index, argument));\n if (!Number.isSafeInteger(value) || value <= 0) {\n throw new UsageError(\"--timeout-ms must be a positive integer\");\n }\n timeoutMs = value;\n index += 1;\n } else if (argument === \"--download\") {\n download = optionValue(args, index, argument);\n wait = true;\n index += 1;\n } else if (argument === \"--force\") {\n force = true;\n } else if (argument === \"--json\") {\n json = true;\n } else if (argument?.startsWith(\"-\")) {\n throw new UsageError(`Unknown run option: ${argument}`);\n } else if (source === undefined) {\n source = argument;\n } else {\n throw new UsageError(\"run accepts exactly one source\");\n }\n }\n\n if (!source) throw new UsageError(\"Usage: mediaruntime run <source> --output <alias> [--wait]\");\n if (outputs.length === 0) throw new UsageError(\"run requires at least one --output\");\n const unsupported = outputs.find((output) => !OUTPUT_ALIASES.has(output));\n if (unsupported) throw new UsageError(`Unsupported output alias: ${unsupported}`);\n if (force && !download) throw new UsageError(\"--force requires --download\");\n if (timeoutMs !== undefined && !wait) throw new UsageError(\"--timeout-ms requires --wait or --download\");\n return {\n source,\n outputs,\n ...(metadata === undefined ? {} : { metadata }),\n ...(idempotencyKey === undefined ? {} : { idempotencyKey }),\n wait,\n ...(timeoutMs === undefined ? {} : { timeoutMs }),\n ...(download === undefined ? {} : { download }),\n force,\n json,\n };\n}\n\nfunction receiptProjection(job: SubmittedJob): JobReceiptData {\n return {\n id: job.id,\n status: job.status,\n tier: job.tier,\n requiredTier: job.requiredTier,\n outputs: job.outputs,\n message: job.message,\n };\n}\n\nfunction detailsProjection(job: JobDetails): Record<string, unknown> {\n return {\n id: job.id,\n status: job.status,\n tier: job.tier,\n usage: job.usage,\n billing: job.billing,\n bundle: {\n available: job.bundle.available,\n expiresAt: job.bundle.expiresAt,\n sizeBytes: job.bundle.sizeBytes,\n sha256: job.bundle.sha256,\n retentionDays: job.bundle.retentionDays,\n },\n media: job.media,\n metadata: job.metadata,\n error: job.error,\n createdAt: job.createdAt,\n updatedAt: job.updatedAt,\n startedAt: job.startedAt,\n completedAt: job.completedAt,\n };\n}\n\nfunction writeHumanReceipt(job: SubmittedJob, write: (text: string) => void): void {\n write(`Job: ${job.id}\\nStatus: ${job.status}\\nTier: ${job.tier || \"-\"}\\n`);\n}\n\nfunction writeHumanDetails(job: JobDetails, write: (text: string) => void): void {\n const error = job.error ? `\\nError: ${job.error}` : \"\";\n write(\n `Job: ${job.id}\\nStatus: ${job.status}\\n` +\n `Bundle: ${job.bundle.available ? \"available\" : \"unavailable\"}${error}\\n`,\n );\n}\n\nexport async function runCommand(\n args: string[],\n dependencies: RunCommandDependencies,\n): Promise<number> {\n const options = parseRunOptions(args);\n const params: CreateJobParams = {\n source: options.source,\n outputs: options.outputs as Array<OutputAlias | JobOutput>,\n ...(options.metadata === undefined ? {} : { metadata: options.metadata }),\n ...(options.idempotencyKey === undefined ? {} : { idempotencyKey: options.idempotencyKey }),\n };\n const submitted = await dependencies.jobs.create(params);\n\n if (!options.wait) {\n if (options.json) dependencies.writeStdout(`${JSON.stringify(receiptProjection(submitted))}\\n`);\n else writeHumanReceipt(submitted, dependencies.writeStdout);\n return UNSUCCESSFUL_TERMINAL_STATUSES.has(String(submitted.status).toUpperCase()) ? 6 : 0;\n }\n\n const details = await submitted.wait(\n options.timeoutMs === undefined ? {} : { timeoutMs: options.timeoutMs },\n );\n if (!options.json) writeHumanDetails(details, dependencies.writeStdout);\n if (String(details.status).toUpperCase() !== \"COMPLETED\") {\n if (options.json) dependencies.writeStdout(`${JSON.stringify(detailsProjection(details))}\\n`);\n return 6;\n }\n if (options.download) {\n const url = details.bundle.downloadUrl;\n if (!details.bundle.available || !url) {\n throw new BundleDownloadError(\"Completed job does not have an available canonical bundle\");\n }\n try {\n await dependencies.downloadBundle(url, options.download, {\n force: options.force,\n expectedSizeBytes: details.bundle.sizeBytes,\n expectedSha256: details.bundle.sha256,\n });\n } catch (error) {\n if (error instanceof BundleDownloadError) throw error;\n throw new BundleDownloadError(`Could not download bundle to ${options.download}`, { cause: error });\n }\n if (!options.json) dependencies.writeStdout(`Downloaded bundle to ${options.download}\\n`);\n }\n if (options.json) dependencies.writeStdout(`${JSON.stringify(detailsProjection(details))}\\n`);\n return 0;\n}\n","import { randomBytes, randomUUID } from \"node:crypto\";\nimport { readFile } from \"node:fs/promises\";\n\nimport { parseLocalDestination } from \"../trigger/destination.js\";\nimport {\n SUPPORTED_TRIGGER_EVENTS,\n createSyntheticTerminalEvent,\n isTriggerEventType,\n type TriggerEventType,\n} from \"../trigger/event.js\";\nimport { signSyntheticWebhook } from \"../trigger/signature.js\";\nimport { TriggerDeliveryError, UsageError } from \"../errors.js\";\n\nexport interface TriggerCommandDependencies {\n env?: Readonly<Record<string, string | undefined>>;\n fetch?: typeof globalThis.fetch;\n now?: () => number;\n randomUUID?: () => string;\n randomBytes?: (size: number) => Uint8Array;\n readFile?: (path: string, encoding: \"utf8\") => Promise<string>;\n writeStdout?: (text: string) => void;\n}\n\ninterface TriggerArguments {\n type: TriggerEventType;\n destination: URL;\n secretFile?: string;\n generateSecret: boolean;\n jobId?: string;\n accountId?: string;\n json: boolean;\n}\n\nfunction requireOptionValue(args: string[], index: number, option: string): string {\n const value = args[index + 1];\n if (!value || value.startsWith(\"--\")) throw new UsageError(`${option} requires a value`);\n return value;\n}\n\nexport function parseTriggerArguments(args: string[]): TriggerArguments {\n const typeValue = args[0];\n if (!typeValue || typeValue.startsWith(\"--\")) {\n throw new UsageError(\n `trigger requires an event type: ${SUPPORTED_TRIGGER_EVENTS.join(\", \")}`,\n );\n }\n if (!isTriggerEventType(typeValue)) {\n throw new UsageError(\n `Unsupported trigger event ${JSON.stringify(typeValue)}; expected ${SUPPORTED_TRIGGER_EVENTS.join(\", \")}`,\n );\n }\n\n let to: string | undefined;\n let secretFile: string | undefined;\n let generateSecret = false;\n let jobId: string | undefined;\n let accountId: string | undefined;\n let json = false;\n for (let index = 1; index < args.length; index += 1) {\n const option = args[index];\n if (!option) continue;\n if (option === \"--generate-secret\") {\n generateSecret = true;\n continue;\n }\n if (option === \"--json\") {\n json = true;\n continue;\n }\n if ([\"--to\", \"--secret-file\", \"--job-id\", \"--account-id\"].includes(option)) {\n const value = requireOptionValue(args, index, option);\n index += 1;\n if (option === \"--to\") to = value;\n else if (option === \"--secret-file\") secretFile = value;\n else if (option === \"--job-id\") jobId = value;\n else accountId = value;\n continue;\n }\n throw new UsageError(`Unknown trigger option: ${option}`);\n }\n\n if (!to) throw new UsageError(\"trigger requires an explicit --to local URL\");\n if (generateSecret && secretFile) {\n throw new UsageError(\"--generate-secret and --secret-file are mutually exclusive\");\n }\n if (generateSecret && json) {\n throw new UsageError(\"--generate-secret and --json are mutually exclusive\");\n }\n if (jobId !== undefined && !jobId.trim()) throw new UsageError(\"--job-id must not be empty\");\n if (accountId !== undefined && !accountId.trim()) {\n throw new UsageError(\"--account-id must not be empty\");\n }\n return {\n type: typeValue,\n destination: parseLocalDestination(to),\n ...(secretFile === undefined ? {} : { secretFile }),\n generateSecret,\n ...(jobId === undefined ? {} : { jobId: jobId.trim() }),\n ...(accountId === undefined ? {} : { accountId: accountId.trim() }),\n json,\n };\n}\n\nasync function resolveSecret(\n args: TriggerArguments,\n dependencies: Required<\n Pick<TriggerCommandDependencies, \"env\" | \"randomBytes\" | \"readFile\" | \"writeStdout\">\n >,\n): Promise<string> {\n if (args.generateSecret) {\n const generated = Buffer.from(dependencies.randomBytes(32)).toString(\"base64url\");\n dependencies.writeStdout(\n `Generated webhook secret (configure the local receiver with this value): ${generated}\\n`,\n );\n return generated;\n }\n let secret: string | undefined;\n if (args.secretFile) {\n try {\n secret = await dependencies.readFile(args.secretFile, \"utf8\");\n } catch (error) {\n throw new UsageError(`Could not read webhook secret file: ${args.secretFile}`, { cause: error });\n }\n } else {\n secret = dependencies.env.MEDIARUNTIME_WEBHOOK_SECRET;\n }\n const normalized = secret?.trim();\n if (!normalized) {\n throw new UsageError(\n \"Set MEDIARUNTIME_WEBHOOK_SECRET, pass --secret-file, or explicitly use --generate-secret\",\n );\n }\n return normalized;\n}\n\nexport async function runTriggerCommand(\n args: string[],\n dependencies: TriggerCommandDependencies = {},\n): Promise<number> {\n const parsed = parseTriggerArguments(args);\n const deps = {\n env: dependencies.env ?? process.env,\n fetch: dependencies.fetch ?? globalThis.fetch,\n now: dependencies.now ?? Date.now,\n randomUUID: dependencies.randomUUID ?? randomUUID,\n randomBytes: dependencies.randomBytes ?? randomBytes,\n readFile: dependencies.readFile ?? readFile,\n writeStdout: dependencies.writeStdout ?? ((text: string) => process.stdout.write(text)),\n };\n const secret = await resolveSecret(parsed, deps);\n const timestamp = Math.floor(deps.now() / 1000);\n const unique = deps.randomUUID();\n const eventId = `evt_cli_${unique}`;\n const jobId = parsed.jobId ?? `job_cli_${unique}`;\n const accountId = parsed.accountId ?? \"acc_cli_local\";\n const payload = createSyntheticTerminalEvent(parsed.type, {\n eventId,\n jobId,\n accountId,\n timestamp,\n });\n const signed = signSyntheticWebhook(payload, { eventId, timestamp, secret });\n\n const response = await deps.fetch(parsed.destination, {\n method: \"POST\",\n headers: signed.headers,\n body: signed.body,\n redirect: \"manual\",\n signal: AbortSignal.timeout(10_000),\n });\n if (!response.ok) {\n throw new TriggerDeliveryError(\n `Local webhook endpoint returned HTTP ${response.status}${response.statusText ? ` ${response.statusText}` : \"\"}`,\n );\n }\n if (parsed.json) {\n deps.writeStdout(`${JSON.stringify({\n eventId,\n type: parsed.type,\n jobId,\n status: payload.status,\n destination: parsed.destination.href,\n httpStatus: response.status,\n })}\\n`);\n } else {\n deps.writeStdout(\n `Sent synthetic ${parsed.type} event ${eventId} to ${parsed.destination.href} (HTTP ${response.status})\\n`,\n );\n }\n return 0;\n}\n","import { UsageError } from \"../errors.js\";\n\nexport function isLoopbackDestination(value: URL): boolean {\n let hostname = value.hostname.toLowerCase();\n if (hostname.endsWith(\".\")) hostname = hostname.slice(0, -1);\n if (hostname.startsWith(\"[\") && hostname.endsWith(\"]\")) {\n hostname = hostname.slice(1, -1);\n }\n if (hostname === \"localhost\" || hostname === \"::1\" || hostname === \"0:0:0:0:0:0:0:1\") {\n return true;\n }\n\n const octets = hostname.split(\".\");\n if (octets.length !== 4 || octets.some((part) => !/^\\d{1,3}$/.test(part))) return false;\n const numbers = octets.map(Number);\n return numbers[0] === 127 && numbers.every((part) => part >= 0 && part <= 255);\n}\n\nexport function parseLocalDestination(raw: string): URL {\n let destination: URL;\n try {\n destination = new URL(raw);\n } catch {\n throw new UsageError(\"--to must be an absolute local HTTP(S) URL\");\n }\n if (destination.protocol !== \"http:\" && destination.protocol !== \"https:\") {\n throw new UsageError(\"--to must use http:// or https://\");\n }\n if (destination.username || destination.password) {\n throw new UsageError(\"--to must not contain URL credentials\");\n }\n if (!isLoopbackDestination(destination)) {\n throw new UsageError(\n \"trigger only sends to loopback destinations (localhost, 127.0.0.0/8, or ::1)\",\n );\n }\n return destination;\n}\n","export const SUPPORTED_TRIGGER_EVENTS = [\n \"job.completed\",\n \"job.failed\",\n \"job.rejected\",\n] as const;\n\nexport type TriggerEventType = (typeof SUPPORTED_TRIGGER_EVENTS)[number];\n\nexport function isTriggerEventType(value: string): value is TriggerEventType {\n return (SUPPORTED_TRIGGER_EVENTS as readonly string[]).includes(value);\n}\n\nexport interface SyntheticEventOptions {\n eventId: string;\n jobId: string;\n accountId: string;\n timestamp: number;\n}\n\nexport function createSyntheticTerminalEvent(\n type: TriggerEventType,\n options: SyntheticEventOptions,\n): Record<string, unknown> {\n const status = type.slice(\"job.\".length).toUpperCase();\n const occurredAt = new Date(options.timestamp * 1000).toISOString();\n const base = {\n event_id: options.eventId,\n job_id: options.jobId,\n account_id: options.accountId,\n status,\n };\n\n if (type === \"job.completed\") {\n const expiresAt = new Date((options.timestamp + 7 * 24 * 60 * 60) * 1000).toISOString();\n return {\n ...base,\n completedAt: occurredAt,\n delivery: {\n mode: \"PULL\",\n retentionDays: 7,\n expiresAt,\n bundle: {\n type: \"zip\",\n filename: `${options.jobId}_outputs.zip`,\n size_bytes: 0,\n sha256: \"0\".repeat(64),\n download: {\n url: `http://127.0.0.1/__mediaruntime_synthetic__/${encodeURIComponent(options.jobId)}.zip`,\n expiresAt,\n },\n },\n layout: \"bundle_relative_v1\",\n deliverables: [],\n manifests: {},\n },\n billing: { status: \"PAID\" },\n usage: { units_total: 0 },\n meta: { synthetic: true, request_metadata: {} },\n };\n }\n\n const failed = type === \"job.failed\";\n return {\n ...base,\n [failed ? \"failedAt\" : \"rejectedAt\"]: occurredAt,\n delivery: { mode: \"NONE\" },\n billing: { status: \"RELEASED\" },\n error: {\n code: failed ? \"SYNTHETIC_ENGINE_FAILED\" : \"SYNTHETIC_REJECTED\",\n message: `Synthetic ${type} event from the MediaRuntime CLI`,\n },\n meta: { synthetic: true, request_metadata: {} },\n };\n}\n","import { createHmac } from \"node:crypto\";\n\nexport const WEBHOOK_HEADERS = {\n id: \"X-Transcoder-Id\",\n timestamp: \"X-Transcoder-Timestamp\",\n signature: \"X-Transcoder-Signature\",\n} as const;\n\nexport interface SignedWebhook {\n body: string;\n headers: Record<string, string>;\n}\n\nexport function signSyntheticWebhook(\n payload: Readonly<Record<string, unknown>>,\n options: { eventId: string; timestamp: number; secret: string },\n): SignedWebhook {\n if (!options.eventId.trim()) throw new Error(\"Webhook event ID must not be empty\");\n if (!Number.isInteger(options.timestamp) || options.timestamp < 0) {\n throw new Error(\"Webhook timestamp must be a non-negative integer\");\n }\n if (!options.secret) throw new Error(\"Webhook secret must not be empty\");\n\n // JSON is serialized once. These exact bytes are both signed and sent.\n const body = JSON.stringify(payload);\n const bodyBytes = Buffer.from(body, \"utf8\");\n const prefix = Buffer.from(`${options.timestamp}.${options.eventId}.`, \"utf8\");\n const digest = createHmac(\"sha256\", options.secret)\n .update(prefix)\n .update(bodyBytes)\n .digest(\"hex\");\n\n return {\n body,\n headers: {\n \"Content-Type\": \"application/json\",\n [WEBHOOK_HEADERS.id]: options.eventId,\n [WEBHOOK_HEADERS.timestamp]: String(options.timestamp),\n [WEBHOOK_HEADERS.signature]: `t=${options.timestamp},v1=${digest}`,\n },\n };\n}\n"],"mappings":"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","names":["createHash","randomUUID","resolve","resolve","UNSUCCESSFUL_TERMINAL_STATUSES","optionValue","detailsProjection","randomBytes","randomBytes","randomUUID","createHash"]}
|
package/docs/AUTH.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# CLI browser authentication
|
|
2
|
+
|
|
3
|
+
## Outcome
|
|
4
|
+
|
|
5
|
+
`mediaruntime login` authenticates an interactive developer through the existing
|
|
6
|
+
MediaRuntime website and stores a dedicated, revocable CLI API key in the operating
|
|
7
|
+
system credential vault. `MEDIARUNTIME_API_KEY` remains permanently supported and takes
|
|
8
|
+
precedence over a stored login for CI, servers, containers, and intentional per-command
|
|
9
|
+
account selection.
|
|
10
|
+
|
|
11
|
+
The browser flow does not change gateway authentication. Once authorized, the CLI uses
|
|
12
|
+
the same `X-API-Key` data-plane contract as every other API-key client.
|
|
13
|
+
|
|
14
|
+
## Protocol
|
|
15
|
+
|
|
16
|
+
1. The CLI generates a PKCE verifier and sends only its SHA-256 challenge to
|
|
17
|
+
`POST /api/cli/auth/start`.
|
|
18
|
+
2. Functions returns a signed, ten-minute stateless device request, a short display
|
|
19
|
+
code, a polling interval, and
|
|
20
|
+
`https://mediaruntime.com/cli/authorize?...`.
|
|
21
|
+
3. The CLI opens that URL and also prints the URL and display code for headless use.
|
|
22
|
+
4. The website uses the existing Firebase login. It shows the code and selected account,
|
|
23
|
+
and requires an explicit Approve action.
|
|
24
|
+
5. The browser sends its Firebase ID token to `POST /api/cli/auth/approve`. Functions
|
|
25
|
+
verifies the signed device request and re-checks identity, active owner/admin
|
|
26
|
+
membership, account state, billing, and wallet access before persisting an approved
|
|
27
|
+
authorization session.
|
|
28
|
+
6. The CLI polls `POST /api/cli/auth/token` with the device code and PKCE verifier.
|
|
29
|
+
Functions verifies both, creates one dedicated API key, and returns that key only to
|
|
30
|
+
the CLI. The browser never receives it and Firestore never stores it in plaintext.
|
|
31
|
+
7. The CLI validates the credential and stores it in the platform credential vault.
|
|
32
|
+
|
|
33
|
+
Pending, denied, expired, consumed, and slow polling states use stable machine-readable
|
|
34
|
+
error codes. Authorization sessions expire after ten minutes. Successful exchange is
|
|
35
|
+
single-use; a short deterministic replay window makes a lost token response recoverable
|
|
36
|
+
without creating another key.
|
|
37
|
+
|
|
38
|
+
## Commands
|
|
39
|
+
|
|
40
|
+
```text
|
|
41
|
+
mediaruntime login [--no-browser]
|
|
42
|
+
mediaruntime auth status
|
|
43
|
+
mediaruntime logout [--local-only]
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`login` refuses `--json` because it is interactive. It never prints the API key.
|
|
47
|
+
`auth status` identifies the active credential source (`environment` or `keychain`) and
|
|
48
|
+
validates it without displaying secret material. `logout` revokes the stored CLI key
|
|
49
|
+
before deleting it; `--local-only` is an explicit recovery path when the service cannot
|
|
50
|
+
be reached.
|
|
51
|
+
|
|
52
|
+
## Credential resolution
|
|
53
|
+
|
|
54
|
+
Authenticated commands resolve credentials in this order:
|
|
55
|
+
|
|
56
|
+
1. non-empty `MEDIARUNTIME_API_KEY`;
|
|
57
|
+
2. an OS-vault credential stored by `mediaruntime login`;
|
|
58
|
+
3. an authentication-required error that recommends `mediaruntime login` or the
|
|
59
|
+
environment variable.
|
|
60
|
+
|
|
61
|
+
No command-line API-key flag exists. No plaintext credential-file fallback exists. A
|
|
62
|
+
machine without a supported credential vault must use `MEDIARUNTIME_API_KEY`.
|
|
63
|
+
|
|
64
|
+
## Security invariants
|
|
65
|
+
|
|
66
|
+
- Device codes and PKCE verifiers carry at least 256 bits of randomness.
|
|
67
|
+
- Starting login is stateless and performs no database write. After browser approval,
|
|
68
|
+
Firestore stores the device-code hash, PKCE challenge, approval metadata, and key
|
|
69
|
+
identifiers, never the raw device code, API key, or PKCE verifier.
|
|
70
|
+
- Display codes are bound to the device session and expire after ten minutes.
|
|
71
|
+
- Approval requires a fresh verified Firebase identity and active owner/admin account
|
|
72
|
+
membership.
|
|
73
|
+
- A session cannot change accounts after approval.
|
|
74
|
+
- A token exchange cannot mint more than one API key.
|
|
75
|
+
- CLI keys are separately labeled and revocable; login never revokes unrelated keys.
|
|
76
|
+
- Logout revokes only the key created for that stored CLI login.
|
|
77
|
+
- Secrets are excluded from logs, JSON output, URLs, errors, analytics, and shell history.
|
|
78
|
+
- Production uses HTTPS. Development overrides may use loopback HTTP only.
|
|
79
|
+
|
|
80
|
+
## Deployment and compatibility
|
|
81
|
+
|
|
82
|
+
The hosted approval route and Functions endpoints must deploy before publishing the CLI
|
|
83
|
+
version that exposes `login`. Older CLI versions and all SDKs continue to use
|
|
84
|
+
`MEDIARUNTIME_API_KEY` unchanged. If browser authorization is unavailable, automation and
|
|
85
|
+
interactive users can still use the environment variable.
|
package/docs/CLI_SDD.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# MediaRuntime CLI software design
|
|
2
2
|
|
|
3
|
-
Status: implementation contract
|
|
3
|
+
Status: implementation contract through browser-auth release 0.2
|
|
4
4
|
|
|
5
5
|
Package: `@mediaruntime/cli`
|
|
6
6
|
|
|
@@ -10,18 +10,22 @@ Runtime: Node.js 20 or newer
|
|
|
10
10
|
|
|
11
11
|
## 1. Decision summary
|
|
12
12
|
|
|
13
|
-
The
|
|
14
|
-
|
|
13
|
+
The CLI is a thin, script-safe layer over the published `@mediaruntime/node` SDK. It
|
|
14
|
+
ships the media commands plus an interactive authentication surface:
|
|
15
15
|
|
|
16
16
|
```text
|
|
17
17
|
mediaruntime run <source> --output <alias>
|
|
18
18
|
mediaruntime jobs list
|
|
19
19
|
mediaruntime jobs get <job_id>
|
|
20
20
|
mediaruntime trigger job.completed --to http://127.0.0.1:3000/webhooks/mediaruntime
|
|
21
|
+
mediaruntime login
|
|
22
|
+
mediaruntime auth status
|
|
23
|
+
mediaruntime logout
|
|
21
24
|
```
|
|
22
25
|
|
|
23
26
|
`run` and `jobs` use the same production API, authentication, local-file upload,
|
|
24
|
-
idempotency, retry, error, and polling behavior as the Node SDK. `
|
|
27
|
+
idempotency, retry, error, and polling behavior as the Node SDK. `login` authorizes a
|
|
28
|
+
dedicated API key through the hosted browser flow defined in `docs/AUTH.md`. `trigger` is entirely
|
|
25
29
|
local: it creates and signs a synthetic terminal webhook without contacting the
|
|
26
30
|
MediaRuntime API.
|
|
27
31
|
|
|
@@ -38,6 +42,8 @@ engine directory layout.
|
|
|
38
42
|
- Download the canonical ZIP safely without leaking credentials to the storage host.
|
|
39
43
|
- Let a developer exercise their local webhook receiver with the production signing
|
|
40
44
|
protocol and realistic terminal payloads.
|
|
45
|
+
- Let interactive developers authorize in the browser and keep the resulting dedicated
|
|
46
|
+
credential in the operating-system vault without weakening environment-key automation.
|
|
41
47
|
- Preserve the SDK and gateway as the authorities for request serialization, uploads,
|
|
42
48
|
retries, aliases, status parsing, and API errors.
|
|
43
49
|
|
|
@@ -45,8 +51,8 @@ engine directory layout.
|
|
|
45
51
|
|
|
46
52
|
- `mediaruntime listen` is not a v1 command. It requires a multi-tenant relay,
|
|
47
53
|
authenticated connections, event ownership, expiry, rate limits, and an abuse model.
|
|
48
|
-
-
|
|
49
|
-
|
|
54
|
+
- Named multi-account profiles are deferred. Browser login stores one credential per API
|
|
55
|
+
origin; an explicit environment key remains the account override.
|
|
50
56
|
- Batch submission, explicit output recipe editing, moderation, watermark management,
|
|
51
57
|
webhook registration, media-report retrieval, and moderation-result retrieval remain
|
|
52
58
|
available through the SDK/API but are not CLI v1 commands.
|
|
@@ -79,13 +85,14 @@ Authenticated commands resolve configuration once before doing file or network w
|
|
|
79
85
|
|
|
80
86
|
| Setting | Highest precedence | Environment | Default |
|
|
81
87
|
|---|---|---|---|
|
|
82
|
-
| API key |
|
|
88
|
+
| API key | `MEDIARUNTIME_API_KEY` | OS-vault browser login | required for `run` and `jobs` |
|
|
83
89
|
| API base URL | `--base-url <url>` | `MEDIARUNTIME_API_URL` | `https://mediaruntime.com` |
|
|
84
90
|
| Webhook signing secret | `--secret-file <path>` | `MEDIARUNTIME_WEBHOOK_SECRET` | required by `trigger`, unless explicitly generated |
|
|
85
91
|
|
|
86
92
|
There is deliberately no `--api-key` or `--secret` option: command-line arguments are
|
|
87
93
|
visible in shell history and process listings. The CLI does not load `.env` files, write
|
|
88
|
-
credentials,
|
|
94
|
+
plaintext credentials, or search parent directories for configuration. Browser login is
|
|
95
|
+
stored only in the platform credential vault; machines without one use the environment.
|
|
89
96
|
|
|
90
97
|
`--base-url` exists for a local gateway, staging, and deterministic tests; ordinary users
|
|
91
98
|
must not need it. It must be an absolute HTTP(S) origin with no credentials, query, or
|
|
@@ -341,7 +348,7 @@ results and must not invent conflicting mappings.
|
|
|
341
348
|
| `4` | Non-retryable API outcome such as `400`, `404`, `409`, `410`, `413`, or `422`, or exhausted `429` |
|
|
342
349
|
| `5` | Connection/HTTP timeout or exhausted retryable `5xx` response |
|
|
343
350
|
| `6` | A submitted or inspected job is terminal `FAILED`, `REJECTED`, or `PARTIAL` |
|
|
344
|
-
| `7` |
|
|
351
|
+
| `7` | Job waiting or browser authorization exceeded its bounded timeout |
|
|
345
352
|
| `8` | Local trigger endpoint returned a redirect or non-`2xx` response |
|
|
346
353
|
| `9` | Bundle availability, redemption, integrity, or filesystem failure |
|
|
347
354
|
| `130` | Interrupted by `SIGINT`; in-flight fetch/poll/download is aborted and temporary files are removed |
|
|
@@ -356,7 +363,8 @@ endpoint that refuses the event is `8`.
|
|
|
356
363
|
## 10. Security rules
|
|
357
364
|
|
|
358
365
|
- Never accept API keys or webhook secrets as ordinary command arguments.
|
|
359
|
-
-
|
|
366
|
+
- Persist browser-login credentials only in the operating-system credential vault; never
|
|
367
|
+
fall back to a plaintext file.
|
|
360
368
|
- Redact credentials and signed query strings from errors and debug output.
|
|
361
369
|
- Treat JSON output containing metadata as caller-sensitive even though signed bundle URLs
|
|
362
370
|
are deliberately omitted.
|
|
@@ -382,6 +390,10 @@ src/cli.ts argv dispatch, configuration, signals, exit/error ren
|
|
|
382
390
|
src/commands/run.ts run parsing and SDK orchestration
|
|
383
391
|
src/commands/jobs.ts list/get parsing and projections
|
|
384
392
|
src/commands/trigger.ts local event orchestration
|
|
393
|
+
src/commands/auth.ts login/status/logout orchestration and precedence
|
|
394
|
+
src/auth/api.ts one-time browser authorization protocol
|
|
395
|
+
src/auth/credential-store.ts OS credential-vault boundary
|
|
396
|
+
src/auth/browser.ts shell-free browser launcher
|
|
385
397
|
src/download.ts streamed atomic bundle redemption
|
|
386
398
|
src/trigger/event.ts synthetic public payloads
|
|
387
399
|
src/trigger/signature.ts byte signing and headers
|
|
@@ -398,10 +410,24 @@ production credentials, billable jobs, real storage, or listening ports.
|
|
|
398
410
|
|
|
399
411
|
- Every documented command/flag combination parses; unknown flags and extra positionals
|
|
400
412
|
exit `2` before network access.
|
|
401
|
-
- Missing/blank
|
|
413
|
+
- Missing/blank environment key falls back to the OS-vault login; when both are absent,
|
|
414
|
+
`run` and `jobs` fail while `trigger` remains independent.
|
|
402
415
|
- `--base-url` overrides the environment, which overrides production; invalid or insecure
|
|
403
416
|
non-loopback HTTP URLs fail closed.
|
|
404
|
-
- No command accepts `--api-key`, `--secret`, a per-job webhook URL,
|
|
417
|
+
- No command accepts `--api-key`, `--secret`, a per-job webhook URL, or `listen`.
|
|
418
|
+
|
|
419
|
+
### Browser authentication
|
|
420
|
+
|
|
421
|
+
- `login` sends only a PKCE challenge, opens the returned MediaRuntime authorization URL,
|
|
422
|
+
polls at the server-provided interval, and never prints the issued API key.
|
|
423
|
+
- Approval requires the existing verified Firebase identity, active owner/admin account
|
|
424
|
+
membership, active billing, available funds, and an explicit matching-code consent.
|
|
425
|
+
- The browser and Firestore never receive the raw CLI API key or PKCE verifier.
|
|
426
|
+
- A successful exchange creates exactly one dedicated CLI key across concurrent polls and
|
|
427
|
+
lost responses, then stores it only in the OS credential vault.
|
|
428
|
+
- `auth status` reports source/account/key metadata without secret material.
|
|
429
|
+
- `logout` revokes the dedicated key before deleting it; `--local-only` is explicit.
|
|
430
|
+
- `MEDIARUNTIME_API_KEY` takes precedence and remains supported for automation.
|
|
405
431
|
|
|
406
432
|
### Run
|
|
407
433
|
|
|
@@ -463,4 +489,5 @@ The v1 package is ready only when:
|
|
|
463
489
|
account and produces a valid ZIP.
|
|
464
490
|
5. A local receiver using `@mediaruntime/node` verification accepts every trigger type and
|
|
465
491
|
rejects a changed body, stale timestamp, and wrong secret.
|
|
466
|
-
6.
|
|
492
|
+
6. Browser login passes a production create/approve/exchange/status/logout smoke test;
|
|
493
|
+
documentation labels only `listen` as deferred.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mediaruntime/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Official command-line client for MediaRuntime",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"prepack": "npm run check && npm test"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
+
"@napi-rs/keyring": "^1.3.0",
|
|
34
35
|
"@mediaruntime/node": "^0.2.5"
|
|
35
36
|
},
|
|
36
37
|
"devDependencies": {
|