@musicflowai/cli 0.1.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/README.md ADDED
@@ -0,0 +1,131 @@
1
+ # MusicFlow CLI
2
+
3
+ `@musicflowai/cli` is the command-line client for the MusicFlow agent API. It is designed to be small, scriptable, and safe to automate against. The CLI talks only to the public v1 API and stores local auth state in `~/.config/musicflow/config.json`.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @musicflowai/cli
9
+ ```
10
+
11
+ Global install is also supported:
12
+
13
+ ```bash
14
+ npm install -g @musicflowai/cli
15
+ ```
16
+
17
+ ## Authentication
18
+
19
+ There are two ways to authenticate.
20
+
21
+ ### Interactive login
22
+
23
+ Use `musicflow login` to open a browser-based login flow. After you approve the request, the CLI stores a local app-managed API key and reuses it for future requests.
24
+
25
+ ```bash
26
+ musicflow login
27
+ ```
28
+
29
+ ### API key
30
+
31
+ Set `MUSICFLOW_API_KEY` for non-interactive use:
32
+
33
+ ```bash
34
+ export MUSICFLOW_API_KEY=mf_xxxxxxxxxxxxxxxxxxxx
35
+ ```
36
+
37
+ You can also override the API base URL:
38
+
39
+ ```bash
40
+ export MUSICFLOW_APP_URL=https://api.musicflowai.com
41
+ ```
42
+
43
+ ## Commands
44
+
45
+ - `musicflow login`
46
+ - `musicflow logout`
47
+ - `musicflow whoami`
48
+ - `musicflow account`
49
+ - `musicflow auth status`
50
+ - `musicflow capabilities`
51
+ - `musicflow song create`
52
+ - `musicflow video from-song`
53
+ - `musicflow publish create`
54
+ - `musicflow plan create`
55
+ - `musicflow job get`
56
+
57
+ ## Examples
58
+
59
+ Create a song draft:
60
+
61
+ ```bash
62
+ musicflow song create \
63
+ --prompt "Write a reflective indie song about starting over" \
64
+ --producer-id 7d6b7b2a-7f1d-4f6d-bf4e-66ff5d9f0f4a \
65
+ --generate-audio
66
+ ```
67
+
68
+ Create a song and wait for audio completion:
69
+
70
+ ```bash
71
+ musicflow song create \
72
+ --prompt "Write a reflective indie song about starting over" \
73
+ --generate-audio \
74
+ --sync
75
+ ```
76
+
77
+ Create a single video from a song:
78
+
79
+ ```bash
80
+ musicflow video from-song \
81
+ --song-id 6c4ab1b7-b8b4-4c1f-9c8f-4c33b0ed7a83 \
82
+ --caption-mode auto \
83
+ --caption-style alexHormozi \
84
+ --aspect-ratio 9:16
85
+ ```
86
+
87
+ Publish an existing video:
88
+
89
+ ```bash
90
+ musicflow publish create \
91
+ --content-id 0d7df30d-4f42-4db5-b00d-43d48f8dc2f7 \
92
+ --youtube-channel-id 0b5b8ad0-4c6f-4b9f-8f08-dfe4e5f0f2d2 \
93
+ --privacy-status unlisted
94
+ ```
95
+
96
+ Inspect a queued job:
97
+
98
+ ```bash
99
+ musicflow job get --id render:4e6c6fd2-1ad4-4b89-8145-21c64f2c3e19
100
+ ```
101
+
102
+ ## Development
103
+
104
+ This package is authored in `src/` and built to `dist/`.
105
+
106
+ ```bash
107
+ cd packages/musicflow-cli
108
+ npm install
109
+ npm run build
110
+ ```
111
+
112
+ For iterative development:
113
+
114
+ ```bash
115
+ npm run dev -- song create --prompt "..."
116
+ ```
117
+
118
+ If you want to lint or type-check the package only:
119
+
120
+ ```bash
121
+ npm run check
122
+ ```
123
+
124
+ ## Release Notes
125
+
126
+ ### 0.1.0
127
+
128
+ - Initial standalone package extraction.
129
+ - Added browser-based login.
130
+ - Added support for `whoami`, `capabilities`, song creation, video creation, publishing, plan creation, and job polling.
131
+ - Switched published entrypoint to `dist/cli.js`.
package/dist/args.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ export declare function parseArgs(argv: string[]): {
2
+ positional: string[];
3
+ flags: Record<string, unknown>;
4
+ };
package/dist/args.js ADDED
@@ -0,0 +1,21 @@
1
+ export function parseArgs(argv) {
2
+ const positional = [];
3
+ const flags = {};
4
+ for (let index = 0; index < argv.length; index += 1) {
5
+ const value = argv[index];
6
+ if (!value.startsWith("--")) {
7
+ positional.push(value);
8
+ continue;
9
+ }
10
+ const key = value.slice(2).replace(/-/g, "_");
11
+ const next = argv[index + 1];
12
+ if (!next || next.startsWith("--")) {
13
+ flags[key] = true;
14
+ continue;
15
+ }
16
+ flags[key] = next;
17
+ index += 1;
18
+ }
19
+ return { positional, flags };
20
+ }
21
+ //# sourceMappingURL=args.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,SAAS,CAAC,IAAc;IACtC,MAAM,UAAU,GAAa,EAAE,CAAA;IAC/B,MAAM,KAAK,GAA4B,EAAE,CAAA;IAEzC,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACpD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAA;QACzB,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YAC5B,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;YACtB,SAAQ;QACV,CAAC;QAED,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;QAC7C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAA;QAC5B,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACnC,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,CAAA;YACjB,SAAQ;QACV,CAAC;QAED,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,CAAA;QACjB,KAAK,IAAI,CAAC,CAAA;IACZ,CAAC;IAED,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,CAAA;AAC9B,CAAC"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env node
2
+ import process from "node:process";
3
+ import { parseArgs } from "./args.js";
4
+ import { login, logout } from "./login.js";
5
+ import { capabilities, jobGet, planCreate, publishCreate, songCreate, videoFromSong, whoami, } from "./commands.js";
6
+ async function main() {
7
+ const { positional, flags } = parseArgs(process.argv.slice(2));
8
+ const [group, command] = positional;
9
+ try {
10
+ if (group === "login") {
11
+ await login(flags);
12
+ return;
13
+ }
14
+ if (group === "logout") {
15
+ logout();
16
+ return;
17
+ }
18
+ if (group === "whoami" || (group === "auth" && command === "status")) {
19
+ await whoami(flags);
20
+ return;
21
+ }
22
+ if (group === "account") {
23
+ await whoami({ ...flags, json: flags.json ?? true });
24
+ return;
25
+ }
26
+ if (group === "capabilities") {
27
+ await capabilities(flags);
28
+ return;
29
+ }
30
+ if (group === "song" && command === "create") {
31
+ await songCreate(flags);
32
+ return;
33
+ }
34
+ if (group === "video" && command === "from-song") {
35
+ await videoFromSong(flags);
36
+ return;
37
+ }
38
+ if (group === "publish" && command === "create") {
39
+ await publishCreate(flags);
40
+ return;
41
+ }
42
+ if (group === "plan" && command === "create") {
43
+ await planCreate(flags);
44
+ return;
45
+ }
46
+ if (group === "job" && command === "get") {
47
+ await jobGet(flags);
48
+ return;
49
+ }
50
+ process.stdout.write(`Usage:
51
+ musicflow login
52
+ musicflow logout
53
+ musicflow whoami
54
+ musicflow capabilities
55
+ musicflow song create --prompt "..." [--generate-audio] [--sync]
56
+ musicflow video from-song --song-id <uuid>
57
+ musicflow publish create --content-id <uuid> --youtube-channel-id <uuid>
58
+ musicflow plan create --prompt "..."
59
+ musicflow job get --id <job-id>\n`);
60
+ process.exitCode = 1;
61
+ }
62
+ catch (error) {
63
+ process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
64
+ process.exitCode = 1;
65
+ }
66
+ }
67
+ await main();
68
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,OAAO,MAAM,cAAc,CAAA;AAClC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AACrC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAC1C,OAAO,EACL,YAAY,EACZ,MAAM,EACN,UAAU,EACV,aAAa,EACb,UAAU,EACV,aAAa,EACb,MAAM,GACP,MAAM,eAAe,CAAA;AAEtB,KAAK,UAAU,IAAI;IACjB,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;IAC9D,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,UAAU,CAAA;IAEnC,IAAI,CAAC;QACH,IAAI,KAAK,KAAK,OAAO,EAAE,CAAC;YACtB,MAAM,KAAK,CAAC,KAAK,CAAC,CAAA;YAClB,OAAM;QACR,CAAC;QAED,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;YACvB,MAAM,EAAE,CAAA;YACR,OAAM;QACR,CAAC;QAED,IAAI,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,KAAK,MAAM,IAAI,OAAO,KAAK,QAAQ,CAAC,EAAE,CAAC;YACrE,MAAM,MAAM,CAAC,KAAK,CAAC,CAAA;YACnB,OAAM;QACR,CAAC;QAED,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,MAAM,MAAM,CAAC,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,IAAI,EAAE,CAAC,CAAA;YACpD,OAAM;QACR,CAAC;QAED,IAAI,KAAK,KAAK,cAAc,EAAE,CAAC;YAC7B,MAAM,YAAY,CAAC,KAAK,CAAC,CAAA;YACzB,OAAM;QACR,CAAC;QAED,IAAI,KAAK,KAAK,MAAM,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;YAC7C,MAAM,UAAU,CAAC,KAAK,CAAC,CAAA;YACvB,OAAM;QACR,CAAC;QAED,IAAI,KAAK,KAAK,OAAO,IAAI,OAAO,KAAK,WAAW,EAAE,CAAC;YACjD,MAAM,aAAa,CAAC,KAAK,CAAC,CAAA;YAC1B,OAAM;QACR,CAAC;QAED,IAAI,KAAK,KAAK,SAAS,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;YAChD,MAAM,aAAa,CAAC,KAAK,CAAC,CAAA;YAC1B,OAAM;QACR,CAAC;QAED,IAAI,KAAK,KAAK,MAAM,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;YAC7C,MAAM,UAAU,CAAC,KAAK,CAAC,CAAA;YACvB,OAAM;QACR,CAAC;QAED,IAAI,KAAK,KAAK,KAAK,IAAI,OAAO,KAAK,KAAK,EAAE,CAAC;YACzC,MAAM,MAAM,CAAC,KAAK,CAAC,CAAA;YACnB,OAAM;QACR,CAAC;QAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC;;;;;;;;;oCASW,CAAC,CAAA;QACjC,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAA;IACtB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QACnF,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAA;IACtB,CAAC;AACH,CAAC;AAED,MAAM,IAAI,EAAE,CAAA"}
@@ -0,0 +1,7 @@
1
+ export declare function whoami(flags: Record<string, unknown>): Promise<void>;
2
+ export declare function capabilities(flags: Record<string, unknown>): Promise<void>;
3
+ export declare function songCreate(flags: Record<string, unknown>): Promise<void>;
4
+ export declare function videoFromSong(flags: Record<string, unknown>): Promise<void>;
5
+ export declare function publishCreate(flags: Record<string, unknown>): Promise<void>;
6
+ export declare function planCreate(flags: Record<string, unknown>): Promise<void>;
7
+ export declare function jobGet(flags: Record<string, unknown>): Promise<void>;
@@ -0,0 +1,95 @@
1
+ import { requestApi } from "./http.js";
2
+ function maybePrintJson(data, flags) {
3
+ if (flags.json) {
4
+ process.stdout.write(`${JSON.stringify(data, null, 2)}\n`);
5
+ return true;
6
+ }
7
+ return false;
8
+ }
9
+ export async function whoami(flags) {
10
+ const data = await requestApi("GET", "/api/v1/account", undefined, flags);
11
+ if (maybePrintJson(data, flags)) {
12
+ return;
13
+ }
14
+ const account = data.account;
15
+ const subscription = data.subscription;
16
+ process.stdout.write(`${account.email || account.id} (${subscription.tier})\n`);
17
+ }
18
+ export async function capabilities(flags) {
19
+ const data = await requestApi("GET", "/api/v1/capabilities", undefined, flags);
20
+ if (maybePrintJson(data, flags)) {
21
+ return;
22
+ }
23
+ for (const capability of data.capabilities) {
24
+ process.stdout.write(`${capability.id}: ${capability.allowed ? "allowed" : "blocked"} (${capability.currentTier} -> ${capability.minimumTier})\n`);
25
+ }
26
+ }
27
+ export async function songCreate(flags) {
28
+ const data = await requestApi("POST", "/api/v1/songs", {
29
+ prompt: flags.prompt,
30
+ producer_id: flags.producer_id,
31
+ channel_id: flags.channel_id,
32
+ generate_audio: Boolean(flags.generate_audio),
33
+ sync: Boolean(flags.sync),
34
+ style_prompt: flags.style_prompt,
35
+ }, flags);
36
+ if (maybePrintJson(data, flags)) {
37
+ return;
38
+ }
39
+ process.stdout.write(`Song ${data.song.id} created: ${data.song.title}\n`);
40
+ if (data.audio) {
41
+ process.stdout.write(`Audio ready: ${data.audio.url}\n`);
42
+ }
43
+ if (data.job) {
44
+ process.stdout.write(`Audio job queued: ${data.job.id}\n`);
45
+ }
46
+ }
47
+ export async function videoFromSong(flags) {
48
+ const data = await requestApi("POST", "/api/v1/videos/from-song", {
49
+ song_id: flags.song_id,
50
+ content_id: flags.content_id,
51
+ caption_mode: flags.caption_mode,
52
+ caption_style: flags.caption_style,
53
+ aspect_ratio: flags.aspect_ratio,
54
+ resolution: flags.resolution,
55
+ background_color: flags.background_color,
56
+ background_image_url: flags.background_image_url,
57
+ thumbnail_url: flags.thumbnail_url,
58
+ show_channel_logo: typeof flags.show_channel_logo === "boolean" ? flags.show_channel_logo : undefined,
59
+ }, flags);
60
+ if (maybePrintJson(data, flags)) {
61
+ return;
62
+ }
63
+ process.stdout.write(`Video render queued: ${data.job.id}\n`);
64
+ }
65
+ export async function publishCreate(flags) {
66
+ const data = await requestApi("POST", "/api/v1/publishes", {
67
+ content_id: flags.content_id,
68
+ youtube_channel_id: flags.youtube_channel_id,
69
+ privacy_status: flags.privacy_status,
70
+ scheduled_publish_at: flags.scheduled_publish_at,
71
+ playlist_id: flags.playlist_id,
72
+ }, flags);
73
+ if (maybePrintJson(data, flags)) {
74
+ return;
75
+ }
76
+ process.stdout.write(`Published: ${data.publish.youtube_url}\n`);
77
+ }
78
+ export async function planCreate(flags) {
79
+ const data = await requestApi("POST", "/api/v1/plans", {
80
+ prompt: flags.prompt,
81
+ }, flags);
82
+ if (maybePrintJson(data, flags)) {
83
+ return;
84
+ }
85
+ process.stdout.write(`${data.plan.name}\n`);
86
+ process.stdout.write(`${data.plan.description}\n`);
87
+ }
88
+ export async function jobGet(flags) {
89
+ const data = await requestApi("GET", `/api/v1/jobs/${flags.id}`, undefined, flags);
90
+ if (maybePrintJson(data, flags)) {
91
+ return;
92
+ }
93
+ process.stdout.write(`${data.job.id}: ${data.job.status}\n`);
94
+ }
95
+ //# sourceMappingURL=commands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.js","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAEtC,SAAS,cAAc,CAAC,IAAa,EAAE,KAA8B;IACnE,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;QACf,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAA;QAC1D,OAAO,IAAI,CAAA;IACb,CAAC;IAED,OAAO,KAAK,CAAA;AACd,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,KAA8B;IACzD,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,KAAK,EAAE,iBAAiB,EAAE,SAAS,EAAE,KAAK,CAAC,CAAA;IACzE,IAAI,cAAc,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC;QAChC,OAAM;IACR,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,OAAgD,CAAA;IACrE,MAAM,YAAY,GAAG,IAAI,CAAC,YAAgC,CAAA;IAC1D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,EAAE,KAAK,YAAY,CAAC,IAAI,KAAK,CAAC,CAAA;AACjF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,KAA8B;IAC/D,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,KAAK,EAAE,sBAAsB,EAAE,SAAS,EAAE,KAAK,CAAC,CAAA;IAC9E,IAAI,cAAc,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC;QAChC,OAAM;IACR,CAAC;IAED,KAAK,MAAM,UAAU,IAAI,IAAI,CAAC,YAAiG,EAAE,CAAC;QAChI,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,GAAG,UAAU,CAAC,EAAE,KAAK,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,KAAK,UAAU,CAAC,WAAW,OAAO,UAAU,CAAC,WAAW,KAAK,CAC7H,CAAA;IACH,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,KAA8B;IAC7D,MAAM,IAAI,GAAG,MAAM,UAAU,CAC3B,MAAM,EACN,eAAe,EACf;QACE,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,cAAc,EAAE,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC;QAC7C,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC;QACzB,YAAY,EAAE,KAAK,CAAC,YAAY;KACjC,EACD,KAAK,CACN,CAAA;IAED,IAAI,cAAc,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC;QAChC,OAAM;IACR,CAAC;IAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,QAAS,IAAI,CAAC,IAAuB,CAAC,EAAE,aAAc,IAAI,CAAC,IAA0B,CAAC,KAAK,IAAI,CAAC,CAAA;IACrH,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAiB,IAAI,CAAC,KAAyB,CAAC,GAAG,IAAI,CAAC,CAAA;IAC/E,CAAC;IACD,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,qBAAsB,IAAI,CAAC,GAAsB,CAAC,EAAE,IAAI,CAAC,CAAA;IAChF,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAA8B;IAChE,MAAM,IAAI,GAAG,MAAM,UAAU,CAC3B,MAAM,EACN,0BAA0B,EAC1B;QACE,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,aAAa,EAAE,KAAK,CAAC,aAAa;QAClC,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,gBAAgB,EAAE,KAAK,CAAC,gBAAgB;QACxC,oBAAoB,EAAE,KAAK,CAAC,oBAAoB;QAChD,aAAa,EAAE,KAAK,CAAC,aAAa;QAClC,iBAAiB,EACf,OAAO,KAAK,CAAC,iBAAiB,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC,SAAS;KACrF,EACD,KAAK,CACN,CAAA;IAED,IAAI,cAAc,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC;QAChC,OAAM;IACR,CAAC;IAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,wBAAyB,IAAI,CAAC,GAAsB,CAAC,EAAE,IAAI,CAAC,CAAA;AACnF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAA8B;IAChE,MAAM,IAAI,GAAG,MAAM,UAAU,CAC3B,MAAM,EACN,mBAAmB,EACnB;QACE,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,kBAAkB,EAAE,KAAK,CAAC,kBAAkB;QAC5C,cAAc,EAAE,KAAK,CAAC,cAAc;QACpC,oBAAoB,EAAE,KAAK,CAAC,oBAAoB;QAChD,WAAW,EAAE,KAAK,CAAC,WAAW;KAC/B,EACD,KAAK,CACN,CAAA;IAED,IAAI,cAAc,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC;QAChC,OAAM;IACR,CAAC;IAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,cAAe,IAAI,CAAC,OAAmC,CAAC,WAAW,IAAI,CAAC,CAAA;AAC/F,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,KAA8B;IAC7D,MAAM,IAAI,GAAG,MAAM,UAAU,CAC3B,MAAM,EACN,eAAe,EACf;QACE,MAAM,EAAE,KAAK,CAAC,MAAM;KACrB,EACD,KAAK,CACN,CAAA;IAED,IAAI,cAAc,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC;QAChC,OAAM;IACR,CAAC;IAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAI,IAAI,CAAC,IAAyB,CAAC,IAAI,IAAI,CAAC,CAAA;IACjE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAI,IAAI,CAAC,IAAgC,CAAC,WAAW,IAAI,CAAC,CAAA;AACjF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,KAA8B;IACzD,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,KAAK,EAAE,gBAAgB,KAAK,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,KAAK,CAAC,CAAA;IAClF,IAAI,cAAc,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC;QAChC,OAAM;IACR,CAAC;IAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAI,IAAI,CAAC,GAAsC,CAAC,EAAE,KAAM,IAAI,CAAC,GAAsC,CAAC,MAAM,IAAI,CAAC,CAAA;AACtI,CAAC"}
@@ -0,0 +1,9 @@
1
+ export interface MusicflowConfig {
2
+ appUrl?: string;
3
+ apiKey?: string;
4
+ loggedInAt?: string;
5
+ }
6
+ export declare function getDefaultAppUrl(): string;
7
+ export declare function loadConfig(): MusicflowConfig;
8
+ export declare function saveConfig(config: MusicflowConfig): void;
9
+ export declare function removeConfig(): void;
package/dist/config.js ADDED
@@ -0,0 +1,26 @@
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ const CONFIG_DIR = path.join(os.homedir(), ".config", "musicflow");
5
+ const CONFIG_PATH = path.join(CONFIG_DIR, "config.json");
6
+ export function getDefaultAppUrl() {
7
+ return (process.env.MUSICFLOW_APP_URL ||
8
+ process.env.NEXT_PUBLIC_APP_URL ||
9
+ "http://localhost:3000").replace(/\/+$/, "");
10
+ }
11
+ export function loadConfig() {
12
+ if (!fs.existsSync(CONFIG_PATH)) {
13
+ return {};
14
+ }
15
+ return JSON.parse(fs.readFileSync(CONFIG_PATH, "utf8"));
16
+ }
17
+ export function saveConfig(config) {
18
+ fs.mkdirSync(CONFIG_DIR, { recursive: true });
19
+ fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
20
+ }
21
+ export function removeConfig() {
22
+ if (fs.existsSync(CONFIG_PATH)) {
23
+ fs.unlinkSync(CONFIG_PATH);
24
+ }
25
+ }
26
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,IAAI,MAAM,WAAW,CAAA;AAQ5B,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,WAAW,CAAC,CAAA;AAClE,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAA;AAExD,MAAM,UAAU,gBAAgB;IAC9B,OAAO,CACL,OAAO,CAAC,GAAG,CAAC,iBAAiB;QAC7B,OAAO,CAAC,GAAG,CAAC,mBAAmB;QAC/B,uBAAuB,CACxB,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;AACvB,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAChC,OAAO,EAAE,CAAA;IACX,CAAC;IAED,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,MAAM,CAAC,CAAoB,CAAA;AAC5E,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAuB;IAChD,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAC7C,EAAE,CAAC,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAA;AAChE,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,IAAI,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC/B,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,CAAA;IAC5B,CAAC;AACH,CAAC"}
package/dist/http.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ import { type MusicflowConfig } from "./config.js";
2
+ export declare function getAuthHeaders(config: MusicflowConfig): {
3
+ Authorization: string;
4
+ };
5
+ export declare function requestApi(method: string, route: string, body?: unknown, flags?: Record<string, unknown>): Promise<any>;
package/dist/http.js ADDED
@@ -0,0 +1,37 @@
1
+ import { loadConfig, getDefaultAppUrl } from "./config.js";
2
+ export function getAuthHeaders(config) {
3
+ const apiKey = process.env.MUSICFLOW_API_KEY || config.apiKey;
4
+ if (!apiKey) {
5
+ throw new Error("Not authenticated. Run `musicflow login` or set MUSICFLOW_API_KEY.");
6
+ }
7
+ return {
8
+ Authorization: `Bearer ${apiKey}`,
9
+ };
10
+ }
11
+ export async function requestApi(method, route, body, flags = {}) {
12
+ const config = loadConfig();
13
+ const appUrl = String(flags.app_url || config.appUrl || getDefaultAppUrl());
14
+ const response = await fetch(`${appUrl}${route}`, {
15
+ method,
16
+ headers: {
17
+ "Content-Type": "application/json",
18
+ ...getAuthHeaders(config),
19
+ },
20
+ body: body ? JSON.stringify(body) : undefined,
21
+ });
22
+ const text = await response.text();
23
+ let data = null;
24
+ if (text) {
25
+ try {
26
+ data = JSON.parse(text);
27
+ }
28
+ catch {
29
+ data = { raw: text };
30
+ }
31
+ }
32
+ if (!response.ok) {
33
+ throw new Error(data?.error?.message || `${response.status} ${response.statusText}`);
34
+ }
35
+ return data;
36
+ }
37
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAwB,MAAM,aAAa,CAAA;AAEhF,MAAM,UAAU,cAAc,CAAC,MAAuB;IACpD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,MAAM,CAAC,MAAM,CAAA;IAC7D,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,oEAAoE,CAAC,CAAA;IACvF,CAAC;IAED,OAAO;QACL,aAAa,EAAE,UAAU,MAAM,EAAE;KAClC,CAAA;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,MAAc,EACd,KAAa,EACb,IAAc,EACd,QAAiC,EAAE;IAEnC,MAAM,MAAM,GAAG,UAAU,EAAE,CAAA;IAC3B,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,IAAI,MAAM,CAAC,MAAM,IAAI,gBAAgB,EAAE,CAAC,CAAA;IAC3E,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,MAAM,GAAG,KAAK,EAAE,EAAE;QAChD,MAAM;QACN,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,GAAG,cAAc,CAAC,MAAM,CAAC;SAC1B;QACD,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;KAC9C,CAAC,CAAA;IAEF,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;IAClC,IAAI,IAAI,GAAQ,IAAI,CAAA;IACpB,IAAI,IAAI,EAAE,CAAC;QACT,IAAI,CAAC;YACH,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QACzB,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,CAAA;QACtB,CAAC;IACH,CAAC;IAED,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO,IAAI,GAAG,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAA;IACtF,CAAC;IAED,OAAO,IAAI,CAAA;AACb,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function login(flags: Record<string, unknown>): Promise<void>;
2
+ export declare function logout(): void;
package/dist/login.js ADDED
@@ -0,0 +1,78 @@
1
+ import http from "node:http";
2
+ import crypto from "node:crypto";
3
+ import { getDefaultAppUrl, loadConfig, removeConfig, saveConfig } from "./config.js";
4
+ import { openBrowser } from "./platform.js";
5
+ export async function login(flags) {
6
+ const appUrl = String(flags.app_url || getDefaultAppUrl()).replace(/\/+$/, "");
7
+ const state = crypto.randomUUID();
8
+ const apiKey = await new Promise((resolve, reject) => {
9
+ let timeout = null;
10
+ const sockets = new Set();
11
+ const server = http.createServer((request, response) => {
12
+ const requestUrl = new URL(request.url || "/", "http://127.0.0.1");
13
+ const returnedState = requestUrl.searchParams.get("state");
14
+ const returnedKey = requestUrl.searchParams.get("api_key");
15
+ const returnedError = requestUrl.searchParams.get("error");
16
+ response.writeHead(200, {
17
+ "Content-Type": "text/html; charset=utf-8",
18
+ Connection: "close",
19
+ });
20
+ response.end(returnedKey
21
+ ? "<html><body><p>MusicFlow CLI login complete. You can close this window.</p></body></html>"
22
+ : `<html><body><p>MusicFlow CLI login failed: ${returnedError || "unknown error"}</p></body></html>`, () => {
23
+ for (const socket of sockets) {
24
+ socket.destroy();
25
+ }
26
+ });
27
+ server.close();
28
+ if (timeout) {
29
+ clearTimeout(timeout);
30
+ }
31
+ if (returnedState !== state) {
32
+ reject(new Error("CLI login state mismatch"));
33
+ return;
34
+ }
35
+ if (returnedError) {
36
+ reject(new Error(returnedError));
37
+ return;
38
+ }
39
+ if (!returnedKey) {
40
+ reject(new Error("CLI login did not return an API key"));
41
+ return;
42
+ }
43
+ resolve(returnedKey);
44
+ });
45
+ server.on("connection", (socket) => {
46
+ sockets.add(socket);
47
+ socket.on("close", () => {
48
+ sockets.delete(socket);
49
+ });
50
+ });
51
+ server.listen(0, "127.0.0.1", async () => {
52
+ const address = server.address();
53
+ if (!address || typeof address === "string") {
54
+ reject(new Error("Failed to start local callback server"));
55
+ return;
56
+ }
57
+ const callbackUrl = `http://127.0.0.1:${address.port}/callback`;
58
+ const loginUrl = `${appUrl}/api/v1/cli/login/start?callback_url=${encodeURIComponent(callbackUrl)}&state=${encodeURIComponent(state)}`;
59
+ process.stderr.write(`Opening browser for login: ${loginUrl}\n`);
60
+ await openBrowser(loginUrl);
61
+ });
62
+ timeout = setTimeout(() => {
63
+ server.close();
64
+ reject(new Error("CLI login timed out"));
65
+ }, 10 * 60 * 1000);
66
+ });
67
+ saveConfig({
68
+ ...loadConfig(),
69
+ appUrl,
70
+ apiKey,
71
+ loggedInAt: new Date().toISOString(),
72
+ });
73
+ process.stdout.write("Login complete.\n");
74
+ }
75
+ export function logout() {
76
+ removeConfig();
77
+ }
78
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../src/login.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function openBrowser(url: string): Promise<void>;
@@ -0,0 +1,18 @@
1
+ import { exec } from "node:child_process";
2
+ import { promisify } from "node:util";
3
+ const execAsync = promisify(exec);
4
+ export async function openBrowser(url) {
5
+ const platform = process.platform;
6
+ const command = platform === "darwin"
7
+ ? `open "${url}"`
8
+ : platform === "win32"
9
+ ? `start "" "${url}"`
10
+ : `xdg-open "${url}"`;
11
+ try {
12
+ await execAsync(command);
13
+ }
14
+ catch {
15
+ process.stderr.write(`Open this URL in your browser:\n${url}\n`);
16
+ }
17
+ }
18
+ //# sourceMappingURL=platform.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform.js","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAA;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAErC,MAAM,SAAS,GAAG,SAAS,CAAC,IAAI,CAAC,CAAA;AAEjC,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,GAAW;IAC3C,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAA;IACjC,MAAM,OAAO,GACX,QAAQ,KAAK,QAAQ;QACnB,CAAC,CAAC,SAAS,GAAG,GAAG;QACjB,CAAC,CAAC,QAAQ,KAAK,OAAO;YACpB,CAAC,CAAC,aAAa,GAAG,GAAG;YACrB,CAAC,CAAC,aAAa,GAAG,GAAG,CAAA;IAE3B,IAAI,CAAC;QACH,MAAM,SAAS,CAAC,OAAO,CAAC,CAAA;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,mCAAmC,GAAG,IAAI,CAAC,CAAA;IAClE,CAAC;AACH,CAAC"}
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "@musicflowai/cli",
3
+ "version": "0.1.0",
4
+ "description": "Command-line client for the MusicFlow agent API",
5
+ "type": "module",
6
+ "bin": {
7
+ "musicflow": "./dist/cli.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "README.md"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsc -p tsconfig.json",
15
+ "check": "tsc -p tsconfig.json --noEmit",
16
+ "clean": "rm -rf dist",
17
+ "dev": "tsx src/cli.ts",
18
+ "prepack": "npm run build"
19
+ },
20
+ "devDependencies": {
21
+ "@types/node": "^22.0.0",
22
+ "tsx": "^4.20.5",
23
+ "typescript": "^5.0.0"
24
+ },
25
+ "engines": {
26
+ "node": ">=20"
27
+ }
28
+ }