@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.
package/dist/index.js CHANGED
@@ -1,50 +1,150 @@
1
1
  #!/usr/bin/env node
2
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
- import { createServer } from "./server.js";
4
2
  /**
5
- * CLI Entry Point for Sora MCP Server
3
+ * sora-mcp CLI entry point (1.0.13).
6
4
  *
7
- * Usage: sora-mcp
5
+ * Subcommands:
6
+ * - `auth login` — open the browser to authorize WorkBuddy
7
+ * - `auth status` — print JSON status (use --json for the machine-readable form)
8
+ * - `auth logout` — revoke the WorkBuddy token and delete the local credential
9
+ * - `serve` — start the stdio MCP server (default if no subcommand)
8
10
  *
9
- * Environment Variables:
10
- * SORA_MCP_TOKEN - MCP API token
11
- * SORA_API_URL - API base URL (default: http://localhost:3000/api)
11
+ * Token resolution for `serve` (highest priority first):
12
+ * 1. --token / SORA_MCP_TOKEN env (legacy Claude Desktop path)
13
+ * 2. WorkBuddy credential file under ~/.workbuddy/connectors/infimind-video/auth/credentials.json
14
+ *
15
+ * Profile filtering:
16
+ * - `--profile=workbuddy` exposes only the 5 stable WorkBuddy tools
17
+ * - `--profile=default` exposes the full tool set (legacy behavior)
12
18
  */
13
- async function main() {
14
- // Parse command line arguments
15
- const args = process.argv.slice(2);
16
- const tokenArg = args.find(arg => arg.startsWith("--token="));
17
- const tokenEnvArg = args.find(arg => arg.startsWith("--token-env="));
18
- const explicitTokenEnvName = tokenEnvArg?.split("=")[1];
19
- const tokenEnvName = explicitTokenEnvName || "SORA_MCP_TOKEN";
20
- if (tokenEnvArg && !explicitTokenEnvName) {
21
- console.error("Error: --token-env value is empty");
22
- console.error("Usage: sora-mcp --token-env=SORA_MCP_TOKEN");
23
- process.exit(1);
24
- }
25
- const tokenFromEnv = process.env[tokenEnvName];
26
- const token = tokenFromEnv || tokenArg?.split("=")[1];
19
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
20
+ import { createServer } from "./server.js";
21
+ import { installRedactingWriters } from "./auth/redact.js";
22
+ import { runLogin } from "./auth/login.js";
23
+ import { runStatus } from "./auth/status.js";
24
+ import { runLogout } from "./auth/logout.js";
25
+ import { readCredentials } from "./auth/credentialWriter.js";
26
+ import { filterToolsForProfile } from "./auth/profile.js";
27
+ import { WORKBUDDY_TOOL_NAMES } from "./auth/profile.js";
28
+ // Install redacting writers BEFORE any process.stdout / stderr write, so
29
+ // that no token, code, or verifier can ever leave the process unwrapped.
30
+ installRedactingWriters();
31
+ function getFlag(name, args) {
32
+ const pref = `--${name}=`;
33
+ for (const a of args) {
34
+ if (a === `--${name}`)
35
+ return "true";
36
+ if (a.startsWith(pref))
37
+ return a.slice(pref.length);
38
+ }
39
+ return undefined;
40
+ }
41
+ function parseProfile(args) {
42
+ const raw = getFlag("profile", args);
43
+ if (raw === "workbuddy")
44
+ return "workbuddy";
45
+ return "default";
46
+ }
47
+ async function mainServe(args) {
48
+ const profile = parseProfile(args);
49
+ const explicitToken = getFlag("token", args);
50
+ const explicitTokenEnvName = getFlag("token-env", args) || "SORA_MCP_TOKEN";
51
+ const envToken = process.env[explicitTokenEnvName];
52
+ let token;
53
+ let tokenSource = "env";
54
+ if (explicitToken) {
55
+ token = explicitToken;
56
+ tokenSource = "flag";
57
+ }
58
+ else if (envToken) {
59
+ token = envToken;
60
+ tokenSource = "env";
61
+ }
62
+ else {
63
+ const creds = readCredentials();
64
+ if (creds) {
65
+ token = creds.accessToken;
66
+ tokenSource = "file";
67
+ }
68
+ }
27
69
  if (!token) {
28
- console.error(`Error: token is required. Set ${tokenEnvName} or pass --token=YOUR_TOKEN.`);
29
- process.exit(1);
30
- }
31
- // Get API URL from environment variable or use default
32
- // Note: Default should be just the base path without /trpc
33
- const apiUrl = process.env.SORA_API_URL || "http://localhost:3000/api";
34
- // Create and start the MCP server
35
- const server = createServer(token, apiUrl);
36
- // Set up stdio transport
70
+ process.stderr.write(`Error: no token available. Run \`sora-mcp auth login\` first or set ${explicitTokenEnvName}.\n`);
71
+ process.exit(2);
72
+ }
73
+ if (tokenSource === "file" && envToken) {
74
+ // Edge case: both env and file are set; env wins. Print a one-line
75
+ // note so the user knows the legacy token is in use.
76
+ process.stderr.write("[workbuddy-auth] note: SORA_MCP_TOKEN env is set; using it instead of the WorkBuddy credential\n");
77
+ }
78
+ const apiBaseUrl = process.env.SORA_API_URL || "https://aigc-next.iclip.cn/api";
79
+ const server = createServer(token, apiBaseUrl, {
80
+ profile,
81
+ filterTools: tools => filterToolsForProfile(tools, profile),
82
+ workbuddyToolNames: [...WORKBUDDY_TOOL_NAMES],
83
+ });
84
+ if (profile === "workbuddy") {
85
+ process.stderr.write(`[workbuddy-auth] profile=workbuddy; exposing ${WORKBUDDY_TOOL_NAMES.length} stable tools\n`);
86
+ }
37
87
  const transport = new StdioServerTransport();
38
- // Connect the server to the transport
39
88
  await server.connect(transport);
40
- // Server is now running and listening for messages via stdio
41
- // Graceful shutdown
42
- process.on("SIGINT", async () => {
43
- await server.close();
44
- process.exit(0);
45
- });
89
+ const shutdown = async (sig) => {
90
+ try {
91
+ await server.close();
92
+ }
93
+ finally {
94
+ process.exit(0);
95
+ }
96
+ };
97
+ process.on("SIGINT", shutdown);
98
+ process.on("SIGTERM", shutdown);
99
+ }
100
+ async function mainAuth(args) {
101
+ const sub = args[0];
102
+ if (sub === "login") {
103
+ const result = await runLogin();
104
+ if (!result.ok) {
105
+ process.stderr.write(`Error: auth login failed: ${result.reason}\n`);
106
+ process.exit(1);
107
+ }
108
+ process.stdout.write("[workbuddy-auth] login complete\n");
109
+ return;
110
+ }
111
+ if (sub === "status") {
112
+ const wantJson = args.includes("--json");
113
+ const result = runStatus();
114
+ // Default human-readable output is a single line that WorkBuddy host
115
+ // can grep with `statusMatch: "Authenticated"`. The --json flag still
116
+ // emits the structured form so test scripts keep working.
117
+ if (wantJson) {
118
+ process.stdout.write(`${JSON.stringify({ authenticated: result.authenticated })}\n`);
119
+ }
120
+ else {
121
+ process.stdout.write(result.authenticated
122
+ ? "Authenticated\n"
123
+ : `Not authenticated: ${result.reason ?? "no credentials"}\n`);
124
+ }
125
+ process.exit(result.authenticated ? 0 : 1);
126
+ }
127
+ if (sub === "logout") {
128
+ const result = await runLogout();
129
+ if (!result.ok) {
130
+ process.stderr.write(`Error: revoke failed (${result.reason}). Local credential kept so you can retry.\n`);
131
+ process.exit(1);
132
+ }
133
+ process.stdout.write(`Logged out. revoked=${result.revoked} localDeleted=${result.localDeleted}\n`);
134
+ return;
135
+ }
136
+ process.stderr.write(`Usage: sora-mcp auth <login|status|logout>\n`);
137
+ process.exit(2);
138
+ }
139
+ async function main() {
140
+ const args = process.argv.slice(2);
141
+ if (args[0] === "auth") {
142
+ await mainAuth(args.slice(1));
143
+ return;
144
+ }
145
+ await mainServe(args);
46
146
  }
47
- main().catch((error) => {
48
- console.error("Fatal error:", error);
147
+ main().catch(err => {
148
+ process.stderr.write(`Fatal: ${err.message ?? String(err)}\n`);
49
149
  process.exit(1);
50
150
  });