@infimind/video-mcp-cli 1.0.11 → 1.0.13

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.
@@ -0,0 +1,15 @@
1
+ export const WORKBUDDY_TOOL_NAMES = [
2
+ "get_user_info",
3
+ "create_video_task",
4
+ "get_video_status",
5
+ "create_video_analysis_task",
6
+ "get_video_analysis_status",
7
+ ];
8
+ export function isWorkbuddyTool(name) {
9
+ return WORKBUDDY_TOOL_NAMES.includes(name);
10
+ }
11
+ export function filterToolsForProfile(tools, profile) {
12
+ if (profile !== "workbuddy")
13
+ return tools;
14
+ return tools.filter(t => isWorkbuddyTool(t.name));
15
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Redact WorkBuddy OAuth / token material from any log line.
3
+ *
4
+ * Mirrors the server-side redaction (server/oauthWorkbuddy/redact.ts) so the
5
+ * CLI never leaks the same secret types the server protects.
6
+ *
7
+ * Replaced patterns:
8
+ * - `wb_[A-Za-z0-9_-]{20,}` (raw WorkBuddy token)
9
+ * - `Bearer [A-Za-z0-9._-]{8,}` (Authorization header value)
10
+ * - `code_verifier=[A-Za-z0-9._-]{20,}` (PKCE verifier)
11
+ * - `code=[A-Za-z0-9._-]{20,}` (authorization code, possibly in URL fragment)
12
+ *
13
+ * We deliberately do NOT match bare base64url sequences of 43+ characters:
14
+ * MCP stdio JSON-RPC messages (which flow through `process.stdout.write`)
15
+ * frequently contain taskId / video URL hashes that are 30-60 chars long.
16
+ * A blanket 43-char redact would corrupt the protocol. Instead we redact
17
+ * only the explicit secret-shaped substrings above; if a CLI command logs
18
+ * a raw PKCE verifier or WorkBuddy token outside of one of these patterns,
19
+ * that's a bug to fix at the caller (don't print secrets).
20
+ */
21
+ const WORKBUDDY_TOKEN_RE = /wb_[A-Za-z0-9_-]{20,}/g;
22
+ const BEARER_RE = /Bearer\s+[A-Za-z0-9._-]{8,}/g;
23
+ const CODE_VERIFIER_RE = /code_verifier=[A-Za-z0-9._-]{20,}/g;
24
+ const CODE_PARAM_RE = /code=[A-Za-z0-9._-]{20,}/g;
25
+ const PLACEHOLDER = "${REDACTED}";
26
+ export function redactSecrets(input) {
27
+ if (typeof input !== "string" || input.length === 0)
28
+ return input;
29
+ let out = input;
30
+ out = out.replace(WORKBUDDY_TOKEN_RE, PLACEHOLDER);
31
+ out = out.replace(BEARER_RE, `Bearer ${PLACEHOLDER}`);
32
+ out = out.replace(CODE_VERIFIER_RE, `code_verifier=${PLACEHOLDER}`);
33
+ out = out.replace(CODE_PARAM_RE, `code=${PLACEHOLDER}`);
34
+ return out;
35
+ }
36
+ /**
37
+ * Wrap `process.stderr.write` so log lines are redacted before they leave
38
+ * the process. `process.stdout.write` is left untouched because the MCP
39
+ * stdio transport sends JSON-RPC messages there; any blanket redact would
40
+ * corrupt the protocol.
41
+ *
42
+ * Call once at startup of any CLI subcommand that emits log lines
43
+ * (auth login, auth logout, profile notes, error paths). The `serve`
44
+ * subcommand is excluded — see mcp-server/src/index.ts.
45
+ */
46
+ export function installRedactingWriters() {
47
+ const origStderr = process.stderr.write.bind(process.stderr);
48
+ const wrap = (orig) => {
49
+ return ((chunk, ...args) => {
50
+ if (typeof chunk === "string") {
51
+ return orig(redactSecrets(chunk), ...args);
52
+ }
53
+ try {
54
+ const decoded = Buffer.from(chunk).toString("utf8");
55
+ return orig(redactSecrets(decoded), ...args);
56
+ }
57
+ catch {
58
+ return orig(chunk, ...args);
59
+ }
60
+ });
61
+ };
62
+ process.stderr.write = wrap(origStderr);
63
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * `sora-mcp auth status --json` implementation.
3
+ *
4
+ * Reports `{"authenticated": true|false}`. We deliberately do not contact the
5
+ * server to avoid leaking the bearer token in headers; instead, the presence
6
+ * of a valid credential file (mode 0600) is enough for a "true" verdict.
7
+ *
8
+ * If the network is reachable, an offline status will still report
9
+ * `{"authenticated": false}` (fail-closed) per plan §3.
10
+ */
11
+ import { existsSync, statSync } from "node:fs";
12
+ import { credentialsFilePath } from "./paths.js";
13
+ export function runStatus() {
14
+ const path = credentialsFilePath();
15
+ if (!existsSync(path)) {
16
+ return { authenticated: false, reason: "no_credentials", filePath: path };
17
+ }
18
+ try {
19
+ const st = statSync(path);
20
+ if (process.platform !== "win32") {
21
+ const mode = st.mode & 0o777;
22
+ if (mode & 0o077) {
23
+ return {
24
+ authenticated: false,
25
+ reason: "insecure_permissions",
26
+ filePath: path,
27
+ };
28
+ }
29
+ }
30
+ return { authenticated: true, filePath: path };
31
+ }
32
+ catch (err) {
33
+ return { authenticated: false, reason: "stat_failed", filePath: path };
34
+ }
35
+ }