pushnetgo-cli 1.0.7 → 1.1.1

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.
@@ -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 — sync music, validate tokens\n HTTP API: ${API_URL}\n Auth: x-api-key: pushnetgo-xxx`)
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('sync-music')
15
- .alias('sync')
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,31 @@ 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
- npm install -g pushnetgo-cli
50
- export MYAPP_API_KEY="pushnetgo-xxx"
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
- Commands:
53
- pushnetgo-cli token check — validate API key
54
- pushnetgo-cli sync-music --url URL upload music from URL
55
- pushnetgo-cli api-url show HTTP API endpoint
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
+ Public guide (no auth): GET https://flutter-ai-playground-f8e3b.web.app/api
115
+ HTTP endpoint: ${API_URL}
116
+ Auth: x-api-key: pushnetgo-xxx
60
117
 
61
- No Firebase config needed. Works on any machine.`);
118
+ No Firebase config needed. Any machine.`);
62
119
  process.exit(0);
63
120
  }
64
121
 
@@ -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 };
@@ -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
- module.exports = { hasServiceAccount, checkTokenHttp, syncMusicHttp, API_URL };
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pushnetgo-cli",
3
- "version": "1.0.7",
3
+ "version": "1.1.1",
4
4
  "description": "PushNetGo App CLI — HTTP API: https://us-central1-flutter-ai-playground-f8e3b.cloudfunctions.net/unifiedGatewayApi",
5
5
  "bin": {
6
6
  "pushnetgo-cli": "./bin/pushnetgo-cli.js"