pushnetgo-cli 1.0.7 → 1.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/bin/pushnetgo-cli.js +76 -20
- package/commands/chat.js +40 -0
- package/commands/music.js +93 -0
- package/lib/http-client.js +46 -1
- package/package.json +1 -1
package/bin/pushnetgo-cli.js
CHANGED
|
@@ -7,22 +7,74 @@ const VERSION = require('../package.json').version;
|
|
|
7
7
|
|
|
8
8
|
program
|
|
9
9
|
.name('pushnetgo-cli')
|
|
10
|
-
.description(`PushNetGo App CLI —
|
|
10
|
+
.description(`PushNetGo App CLI — chat, music, tokens\n HTTP API: ${API_URL}\n Auth: x-api-key: pushnetgo-xxx`)
|
|
11
11
|
.version(VERSION);
|
|
12
12
|
|
|
13
|
+
// Chat
|
|
13
14
|
program
|
|
14
|
-
.command('
|
|
15
|
-
.
|
|
16
|
-
.description('Sync music to App open project')
|
|
17
|
-
.option('-p, --project <id>', 'target project ID', 'default')
|
|
18
|
-
.option('-d, --dir <path>', 'local MP3 directory')
|
|
19
|
-
.option('-u, --url <url>', 'Mureka AI generated music URL')
|
|
15
|
+
.command('chat <message>')
|
|
16
|
+
.description('AI chat')
|
|
20
17
|
.option('-k, --key <token>', 'API Key (or set MYAPP_API_KEY env var)')
|
|
18
|
+
.action(async (message, options) => {
|
|
19
|
+
const { chat } = require('../commands/chat');
|
|
20
|
+
await chat(message, options);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// Music group
|
|
24
|
+
const musicCmd = program
|
|
25
|
+
.command('music')
|
|
26
|
+
.description('Music management');
|
|
27
|
+
|
|
28
|
+
musicCmd
|
|
29
|
+
.command('list')
|
|
30
|
+
.description('List tracks')
|
|
31
|
+
.option('-k, --key <token>', 'API Key')
|
|
21
32
|
.action(async (options) => {
|
|
33
|
+
const { musicList } = require('../commands/music');
|
|
34
|
+
await musicList(options);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
musicCmd
|
|
38
|
+
.command('get <trackId>')
|
|
39
|
+
.description('Track info')
|
|
40
|
+
.option('-k, --key <token>', 'API Key')
|
|
41
|
+
.action(async (trackId, options) => {
|
|
42
|
+
const { musicGet } = require('../commands/music');
|
|
43
|
+
await musicGet(trackId, options);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
musicCmd
|
|
47
|
+
.command('download <trackId>')
|
|
48
|
+
.description('Download track to local file')
|
|
49
|
+
.option('-k, --key <token>', 'API Key')
|
|
50
|
+
.option('-o, --output <path>', 'Output file path')
|
|
51
|
+
.action(async (trackId, options) => {
|
|
52
|
+
const { musicDownload } = require('../commands/music');
|
|
53
|
+
await musicDownload(trackId, options);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
musicCmd
|
|
57
|
+
.command('upload')
|
|
58
|
+
.description('Upload music from URL')
|
|
59
|
+
.option('-p, --project <id>', 'target project ID', 'default')
|
|
60
|
+
.option('-u, --url <url>', 'Music URL')
|
|
61
|
+
.option('-k, --key <token>', 'API Key')
|
|
62
|
+
.action(async (options) => {
|
|
63
|
+
options.dir = null; // force HTTP mode
|
|
22
64
|
const { syncMusic } = require('../commands/sync-music');
|
|
23
65
|
await syncMusic(options);
|
|
24
66
|
});
|
|
25
67
|
|
|
68
|
+
musicCmd
|
|
69
|
+
.command('delete <trackId>')
|
|
70
|
+
.description('Delete track')
|
|
71
|
+
.option('-k, --key <token>', 'API Key')
|
|
72
|
+
.action(async (trackId, options) => {
|
|
73
|
+
const { musicDelete } = require('../commands/music');
|
|
74
|
+
await musicDelete(trackId, options);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Token
|
|
26
78
|
program
|
|
27
79
|
.command('token <action>')
|
|
28
80
|
.description('Token management: check')
|
|
@@ -39,26 +91,30 @@ program
|
|
|
39
91
|
program
|
|
40
92
|
.command('api-url')
|
|
41
93
|
.description('Print the HTTP API endpoint URL')
|
|
42
|
-
.action(() =>
|
|
43
|
-
console.log(API_URL);
|
|
44
|
-
});
|
|
94
|
+
.action(() => console.log(API_URL));
|
|
45
95
|
|
|
46
96
|
if (process.argv.length <= 2) {
|
|
47
97
|
console.log(`pushnetgo-cli v${VERSION}
|
|
48
98
|
|
|
49
|
-
|
|
50
|
-
|
|
99
|
+
Chat:
|
|
100
|
+
pushnetgo-cli chat "hello" — AI chat
|
|
101
|
+
|
|
102
|
+
Music:
|
|
103
|
+
pushnetgo-cli music list — list tracks
|
|
104
|
+
pushnetgo-cli music get <id> — track info
|
|
105
|
+
pushnetgo-cli music download <id> — download to local
|
|
106
|
+
pushnetgo-cli music upload --url URL — upload from URL
|
|
107
|
+
pushnetgo-cli music delete <id> — delete track
|
|
51
108
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
pushnetgo-cli --help — all commands and options
|
|
109
|
+
Token:
|
|
110
|
+
pushnetgo-cli token check — validate key
|
|
111
|
+
pushnetgo-cli api-url — HTTP endpoint
|
|
112
|
+
pushnetgo-cli --help — all options
|
|
57
113
|
|
|
58
|
-
HTTP endpoint: ${API_URL}
|
|
59
|
-
Auth: x-api-key: pushnetgo-xxx
|
|
114
|
+
HTTP endpoint: ${API_URL}
|
|
115
|
+
Auth: x-api-key: pushnetgo-xxx
|
|
60
116
|
|
|
61
|
-
No Firebase config needed.
|
|
117
|
+
No Firebase config needed. Any machine.`);
|
|
62
118
|
process.exit(0);
|
|
63
119
|
}
|
|
64
120
|
|
package/commands/chat.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const { resolveKeyEnv } = require('../lib/auth');
|
|
2
|
+
const { hasServiceAccount, chatHttp } = require('../lib/http-client');
|
|
3
|
+
|
|
4
|
+
async function chat(message, options) {
|
|
5
|
+
if (!message || message.trim() === '') {
|
|
6
|
+
console.error('Usage: pushnetgo-cli chat "your message here"');
|
|
7
|
+
process.exit(1);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const key = resolveKeyEnv(options.key);
|
|
11
|
+
|
|
12
|
+
if (hasServiceAccount()) {
|
|
13
|
+
const { getFirestore, admin } = require('../lib/firestore');
|
|
14
|
+
const db = getFirestore();
|
|
15
|
+
const snap = await db.collection('tokens')
|
|
16
|
+
.where('tokenValue', '==', key)
|
|
17
|
+
.where('isActive', '==', true)
|
|
18
|
+
.limit(1)
|
|
19
|
+
.get();
|
|
20
|
+
if (snap.empty) { console.error('Token not found or inactive'); process.exit(1); }
|
|
21
|
+
console.log('Auth OK');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
console.log('Sending...');
|
|
26
|
+
const result = await chatHttp(key, message);
|
|
27
|
+
if (result.choices && result.choices[0]) {
|
|
28
|
+
console.log('\n' + result.choices[0].message.content);
|
|
29
|
+
} else if (result.response) {
|
|
30
|
+
console.log('\n' + result.response);
|
|
31
|
+
} else {
|
|
32
|
+
console.log('\n' + JSON.stringify(result, null, 2));
|
|
33
|
+
}
|
|
34
|
+
} catch (e) {
|
|
35
|
+
console.error('Chat error:', e.error || e.message || e);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = { chat };
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const { resolveKeyEnv } = require('../lib/auth');
|
|
5
|
+
const { hasServiceAccount, musicListHttp, musicGetHttp, musicDeleteHttp, downloadFile } = require('../lib/http-client');
|
|
6
|
+
|
|
7
|
+
async function musicList(options) {
|
|
8
|
+
const key = resolveKeyEnv(options.key);
|
|
9
|
+
try {
|
|
10
|
+
const result = await musicListHttp(key);
|
|
11
|
+
const tracks = result.tracks || [];
|
|
12
|
+
if (tracks.length === 0) {
|
|
13
|
+
console.log('No tracks found.');
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
tracks.forEach((t, i) => {
|
|
17
|
+
console.log(`[${i + 1}] ${t.id} — ${t.artist || '?'} — ${t.title || '?'}`);
|
|
18
|
+
});
|
|
19
|
+
console.log(`\n${tracks.length} track(s)`);
|
|
20
|
+
} catch (e) {
|
|
21
|
+
console.error('Error:', e.error || e.message || e);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async function musicGet(trackId, options) {
|
|
27
|
+
if (!trackId) {
|
|
28
|
+
console.error('Usage: pushnetgo-cli music get <trackId>');
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
const key = resolveKeyEnv(options.key);
|
|
32
|
+
try {
|
|
33
|
+
const result = await musicGetHttp(key, trackId);
|
|
34
|
+
if (result.track) {
|
|
35
|
+
const t = result.track;
|
|
36
|
+
console.log(`ID: ${t.id || trackId}`);
|
|
37
|
+
console.log(`Title: ${t.title || '?'}`);
|
|
38
|
+
console.log(`Artist: ${t.artist || '?'}`);
|
|
39
|
+
if (t.musicUrl) console.log(`URL: ${t.musicUrl}`);
|
|
40
|
+
if (t.plays !== undefined) console.log(`Plays: ${t.plays}`);
|
|
41
|
+
} else {
|
|
42
|
+
console.log(JSON.stringify(result, null, 2));
|
|
43
|
+
}
|
|
44
|
+
} catch (e) {
|
|
45
|
+
console.error('Error:', e.error || e.message || e);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function musicDownload(trackId, options) {
|
|
51
|
+
if (!trackId) {
|
|
52
|
+
console.error('Usage: pushnetgo-cli music download <trackId>');
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
const key = resolveKeyEnv(options.key);
|
|
56
|
+
try {
|
|
57
|
+
console.log('Fetching track info...');
|
|
58
|
+
const result = await musicGetHttp(key, trackId);
|
|
59
|
+
const t = result.track || result;
|
|
60
|
+
const url = t.musicUrl;
|
|
61
|
+
if (!url) {
|
|
62
|
+
console.error('No music URL found for this track.');
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
const ext = path.extname(new URL(url).pathname) || '.mp3';
|
|
66
|
+
const destName = options.output || `${trackId}${ext}`;
|
|
67
|
+
const destPath = options.output ? destName : path.join(os.homedir(), 'Downloads', destName);
|
|
68
|
+
console.log(`Downloading: ${url}`);
|
|
69
|
+
console.log(`Saving to: ${destPath}`);
|
|
70
|
+
await downloadFile(url, destPath);
|
|
71
|
+
console.log('Done.');
|
|
72
|
+
} catch (e) {
|
|
73
|
+
console.error('Error:', e.error || e.message || e);
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function musicDelete(trackId, options) {
|
|
79
|
+
if (!trackId) {
|
|
80
|
+
console.error('Usage: pushnetgo-cli music delete <trackId>');
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
const key = resolveKeyEnv(options.key);
|
|
84
|
+
try {
|
|
85
|
+
await musicDeleteHttp(key, trackId);
|
|
86
|
+
console.log(`Deleted: ${trackId}`);
|
|
87
|
+
} catch (e) {
|
|
88
|
+
console.error('Error:', e.error || e.message || e);
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
module.exports = { musicList, musicGet, musicDownload, musicDelete };
|
package/lib/http-client.js
CHANGED
|
@@ -54,4 +54,49 @@ async function syncMusicHttp(key, url, projectId) {
|
|
|
54
54
|
return _request({ action: 'sync-music', url, projectId }, key);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
async function chatHttp(key, message) {
|
|
58
|
+
return _request({ action: 'ai_chat', messages: [{ role: 'user', content: message }] }, key);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function musicListHttp(key) {
|
|
62
|
+
return _request({ action: 'music_list' }, key);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function musicGetHttp(key, trackId) {
|
|
66
|
+
return _request({ action: 'music_get', trackId }, key);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function musicDeleteHttp(key, trackId) {
|
|
70
|
+
return _request({ action: 'music_delete', trackId }, key);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function downloadFile(url, destPath) {
|
|
74
|
+
return new Promise((resolve, reject) => {
|
|
75
|
+
const client = url.startsWith('https') ? https : http;
|
|
76
|
+
const file = fs.createWriteStream(destPath);
|
|
77
|
+
client.get(url, (response) => {
|
|
78
|
+
if (response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
|
|
79
|
+
file.close();
|
|
80
|
+
try { fs.unlinkSync(destPath); } catch (_) {}
|
|
81
|
+
return downloadFile(response.headers.location, destPath).then(resolve).catch(reject);
|
|
82
|
+
}
|
|
83
|
+
if (response.statusCode >= 400) {
|
|
84
|
+
file.close();
|
|
85
|
+
try { fs.unlinkSync(destPath); } catch (_) {}
|
|
86
|
+
return reject(new Error(`HTTP ${response.statusCode}`));
|
|
87
|
+
}
|
|
88
|
+
response.pipe(file);
|
|
89
|
+
file.on('finish', () => { file.close(); resolve(destPath); });
|
|
90
|
+
}).on('error', (err) => {
|
|
91
|
+
file.close();
|
|
92
|
+
try { fs.unlinkSync(destPath); } catch (_) {}
|
|
93
|
+
reject(err);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
module.exports = {
|
|
99
|
+
hasServiceAccount, checkTokenHttp, syncMusicHttp,
|
|
100
|
+
chatHttp, musicListHttp, musicGetHttp, musicDeleteHttp,
|
|
101
|
+
downloadFile, API_URL
|
|
102
|
+
};
|
package/package.json
CHANGED