@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 +131 -0
- package/dist/args.d.ts +4 -0
- package/dist/args.js +21 -0
- package/dist/args.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +68 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands.d.ts +7 -0
- package/dist/commands.js +95 -0
- package/dist/commands.js.map +1 -0
- package/dist/config.d.ts +9 -0
- package/dist/config.js +26 -0
- package/dist/config.js.map +1 -0
- package/dist/http.d.ts +5 -0
- package/dist/http.js +37 -0
- package/dist/http.js.map +1 -0
- package/dist/login.d.ts +2 -0
- package/dist/login.js +78 -0
- package/dist/login.js.map +1 -0
- package/dist/platform.d.ts +1 -0
- package/dist/platform.js +18 -0
- package/dist/platform.js.map +1 -0
- package/package.json +28 -0
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
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
|
package/dist/args.js.map
ADDED
|
@@ -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
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
|
package/dist/cli.js.map
ADDED
|
@@ -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>;
|
package/dist/commands.js
ADDED
|
@@ -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":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -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
|
package/dist/http.js.map
ADDED
|
@@ -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"}
|
package/dist/login.d.ts
ADDED
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>;
|
package/dist/platform.js
ADDED
|
@@ -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
|
+
}
|