prior-cli 1.0.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/lib/api.js ADDED
@@ -0,0 +1,244 @@
1
+ 'use strict';
2
+
3
+ const fetch = require('node-fetch');
4
+ const { getToken } = require('./config');
5
+
6
+ const BASE = 'https://prior.ngrok.app';
7
+ const CLI_BASE = 'https://prior.ngrok.app/cli-backend';
8
+
9
+ function authHeaders(extra = {}) {
10
+ const token = getToken();
11
+ const h = { 'Content-Type': 'application/json', ...extra };
12
+ if (token) h['Authorization'] = `Bearer ${token}`;
13
+ return h;
14
+ }
15
+
16
+ // ── Auth ──────────────────────────────────────────────────────
17
+
18
+ async function login(username, password) {
19
+ const res = await fetch(`${BASE}/network/api/login`, {
20
+ method: 'POST',
21
+ headers: { 'Content-Type': 'application/json' },
22
+ body: JSON.stringify({ username, password }),
23
+ });
24
+ const data = await res.json();
25
+ if (!res.ok) throw new Error(data.error || data.message || 'Login failed');
26
+ return data;
27
+ }
28
+
29
+ // ── CLI Backend ───────────────────────────────────────────────
30
+
31
+ async function checkBackend() {
32
+ try {
33
+ const res = await fetch(`${CLI_BASE}/health`, { timeout: 3000 });
34
+ if (!res.ok) return null;
35
+ return await res.json();
36
+ } catch {
37
+ return null;
38
+ }
39
+ }
40
+
41
+ // Agent chat — SSE stream. Calls onEvent(event) for each SSE event.
42
+ // Events: { type: 'thinking' | 'tool_start' | 'tool_done' | 'tool_error' |
43
+ // 'response_start' | 'response_pause' | 'text' | 'error' | 'done' }
44
+ async function agentChat(messages, opts = {}, onEvent) {
45
+ const token = getToken();
46
+ const res = await fetch(`${CLI_BASE}/api/chat`, {
47
+ method: 'POST',
48
+ headers: { 'Content-Type': 'application/json' },
49
+ body: JSON.stringify({
50
+ messages,
51
+ model: opts.model || undefined,
52
+ uncensored: opts.uncensored || false,
53
+ cwd: opts.cwd || process.cwd(),
54
+ projectContext: opts.projectContext || undefined,
55
+ token,
56
+ }),
57
+ timeout: 300000,
58
+ });
59
+
60
+ if (!res.ok) {
61
+ const err = await res.json().catch(() => ({}));
62
+ throw new Error(err.error || `HTTP ${res.status}`);
63
+ }
64
+
65
+ return new Promise((resolve, reject) => {
66
+ let buf = '';
67
+
68
+ res.body.on('data', chunk => {
69
+ buf += chunk.toString();
70
+ const lines = buf.split('\n');
71
+ buf = lines.pop();
72
+
73
+ for (const line of lines) {
74
+ if (!line.startsWith('data: ')) continue;
75
+ const raw = line.slice(6).trim();
76
+ if (!raw) continue;
77
+ try {
78
+ const event = JSON.parse(raw);
79
+ onEvent(event);
80
+ if (event.type === 'done' || event.type === 'error') resolve();
81
+ } catch { /* skip malformed SSE */ }
82
+ }
83
+ });
84
+
85
+ res.body.on('end', resolve);
86
+ res.body.on('error', reject);
87
+ });
88
+ }
89
+
90
+ // ── Direct AI (fallback, no agent) ───────────────────────────
91
+
92
+ async function generate(prompt, opts = {}, onChunk) {
93
+ const res = await fetch(`${BASE}/prior/api/generate`, {
94
+ method: 'POST',
95
+ headers: authHeaders(),
96
+ body: JSON.stringify({
97
+ prompt,
98
+ stream: true,
99
+ model: opts.model || undefined,
100
+ uncensored: opts.uncensored || false,
101
+ }),
102
+ });
103
+
104
+ if (!res.ok) {
105
+ const err = await res.json().catch(() => ({}));
106
+ throw new Error(err.error || err.message || `HTTP ${res.status}`);
107
+ }
108
+
109
+ return new Promise((resolve, reject) => {
110
+ let full = '';
111
+ let buf = '';
112
+
113
+ function processLines(raw) {
114
+ const lines = raw.split('\n');
115
+ for (const line of lines) {
116
+ const trimmed = line.replace(/^data:\s*/, '').trim();
117
+ if (!trimmed || trimmed === '[DONE]') continue;
118
+ try {
119
+ const json = JSON.parse(trimmed);
120
+ const text = json.response ?? json.content ?? json.token ?? '';
121
+ if (text) { full += text; onChunk(text); }
122
+ } catch { /* skip */ }
123
+ }
124
+ }
125
+
126
+ res.body.on('data', chunk => {
127
+ let raw = chunk.toString();
128
+ try {
129
+ const unwrapped = JSON.parse(raw);
130
+ if (typeof unwrapped === 'string') raw = unwrapped;
131
+ } catch { /* not outer-encoded */ }
132
+ buf += raw;
133
+ const lines = buf.split('\n');
134
+ buf = lines.pop();
135
+ processLines(lines.join('\n'));
136
+ });
137
+
138
+ res.body.on('end', () => {
139
+ if (buf.trim()) {
140
+ let raw = buf.trim();
141
+ try { const u = JSON.parse(raw); if (typeof u === 'string') raw = u; } catch {}
142
+ processLines(raw);
143
+ }
144
+ resolve(full);
145
+ });
146
+
147
+ res.body.on('error', reject);
148
+ });
149
+ }
150
+
151
+ // ── Image generation ──────────────────────────────────────────
152
+
153
+ async function generateImage(prompt, opts = {}) {
154
+ const res = await fetch(`${BASE}/prior/api/tools/generate-image`, {
155
+ method: 'POST',
156
+ headers: authHeaders(),
157
+ body: JSON.stringify({
158
+ prompt,
159
+ width: opts.width || 896,
160
+ height: opts.height || 896,
161
+ steps: opts.steps || 20,
162
+ }),
163
+ });
164
+ const data = await res.json();
165
+ if (!res.ok) throw new Error(data.error || data.message || `HTTP ${res.status}`);
166
+ return data;
167
+ }
168
+
169
+ async function pollImageProgress(promptId) {
170
+ const res = await fetch(`${BASE}/prior/api/tools/image-gen-progress/${promptId}`, {
171
+ headers: authHeaders(),
172
+ });
173
+ const data = await res.json();
174
+ if (!res.ok) throw new Error(data.error || data.message || `HTTP ${res.status}`);
175
+ return data;
176
+ }
177
+
178
+ async function downloadImage(filename) {
179
+ const res = await fetch(
180
+ `${BASE}/prior/api/media/images/${encodeURIComponent(filename)}`,
181
+ { headers: authHeaders() }
182
+ );
183
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
184
+ return res;
185
+ }
186
+
187
+ // ── Other endpoints ───────────────────────────────────────────
188
+
189
+ async function getModels() {
190
+ const res = await fetch(`${BASE}/prior/api/models`, { headers: authHeaders() });
191
+ const data = await res.json();
192
+ if (!res.ok) throw new Error(data.error || data.message || `HTTP ${res.status}`);
193
+ return data;
194
+ }
195
+
196
+ async function getChats() {
197
+ const res = await fetch(`${BASE}/prior/api/prior_ai_chats`, { headers: authHeaders() });
198
+ const data = await res.json();
199
+ if (!res.ok) throw new Error(data.error || data.message || `HTTP ${res.status}`);
200
+ return data;
201
+ }
202
+
203
+ async function getUsage() {
204
+ // Try the local backend first — avoids the guest-redirect issue on the public API
205
+ try {
206
+ const r = await fetch(`${CLI_BASE}/api/usage`, { headers: authHeaders(), timeout: 5000 });
207
+ if (r.ok) return await r.json();
208
+ } catch { /* backend offline, fall through */ }
209
+
210
+ // Fallback: public API (may hit redirect issues for some tokens)
211
+ const res = await fetch(`${BASE}/prior/api/usage`, { headers: authHeaders() });
212
+ const data = await res.json();
213
+ if (!res.ok) throw new Error(data.error || data.message || `HTTP ${res.status}`);
214
+ return data;
215
+ }
216
+
217
+ async function getWeather(location) {
218
+ const res = await fetch(
219
+ `${BASE}/prior/api/tools/weather?location=${encodeURIComponent(location)}`,
220
+ { headers: authHeaders() }
221
+ );
222
+ const data = await res.json();
223
+ if (!res.ok) throw new Error(data.error || data.message || `HTTP ${res.status}`);
224
+ return data;
225
+ }
226
+
227
+ async function search(query) {
228
+ const res = await fetch(`${BASE}/prior/api/tools/url-reader`, {
229
+ method: 'POST',
230
+ headers: authHeaders(),
231
+ body: JSON.stringify({ url: query }),
232
+ });
233
+ const data = await res.json();
234
+ if (!res.ok) throw new Error(data.error || data.message || `HTTP ${res.status}`);
235
+ return data;
236
+ }
237
+
238
+ module.exports = {
239
+ login,
240
+ checkBackend, agentChat,
241
+ generate,
242
+ generateImage, pollImageProgress, downloadImage,
243
+ getModels, getChats, getUsage, getWeather, search,
244
+ };
package/lib/config.js ADDED
@@ -0,0 +1,38 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const os = require('os');
6
+
7
+ const CONFIG_DIR = path.join(os.homedir(), '.prior');
8
+ const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
9
+
10
+ function getConfig() {
11
+ try {
12
+ if (!fs.existsSync(CONFIG_FILE)) return {};
13
+ return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
14
+ } catch {
15
+ return {};
16
+ }
17
+ }
18
+
19
+ function saveConfig(data) {
20
+ if (!fs.existsSync(CONFIG_DIR)) fs.mkdirSync(CONFIG_DIR, { recursive: true });
21
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(data, null, 2));
22
+ }
23
+
24
+ function getToken() { return getConfig().token || null; }
25
+ function getUsername() { return getConfig().username || null; }
26
+
27
+ function saveAuth(token, username) {
28
+ saveConfig({ ...getConfig(), token, username });
29
+ }
30
+
31
+ function clearAuth() {
32
+ const cfg = getConfig();
33
+ delete cfg.token;
34
+ delete cfg.username;
35
+ saveConfig(cfg);
36
+ }
37
+
38
+ module.exports = { getConfig, saveConfig, getToken, getUsername, saveAuth, clearAuth, CONFIG_DIR };
package/lib/render.js ADDED
@@ -0,0 +1,96 @@
1
+ 'use strict';
2
+
3
+ const chalk = require('chalk');
4
+ const THEME = '#9CE2D4';
5
+
6
+ // ── Inline markup ─────────────────────────────────────────────
7
+
8
+ function renderInline(text) {
9
+ return text
10
+ .replace(/\*\*\*(.+?)\*\*\*/g, (_,t) => chalk.bold.italic(t))
11
+ .replace(/\*\*(.+?)\*\*/g, (_,t) => chalk.bold(t))
12
+ .replace(/`([^`]+)`/g, (_,t) => chalk.hex(THEME)(t))
13
+ .replace(/\*([^*\n]+)\*/g, (_,t) => chalk.italic(t));
14
+ }
15
+
16
+ // ── Block renderer ────────────────────────────────────────────
17
+
18
+ function renderMarkdown(text, indent = ' ') {
19
+ const lines = text.split('\n');
20
+ const out = [];
21
+ let inCode = false;
22
+ let codeLang = '';
23
+
24
+ for (let i = 0; i < lines.length; i++) {
25
+ const line = lines[i];
26
+
27
+ // Code fence open/close
28
+ const fenceMatch = line.match(/^```(\w*)/);
29
+ if (fenceMatch) {
30
+ if (!inCode) {
31
+ inCode = true;
32
+ codeLang = fenceMatch[1] || 'code';
33
+ out.push(chalk.dim(`${indent}┌─ ${codeLang} ${'─'.repeat(Math.max(0, 36 - codeLang.length))}`));
34
+ } else {
35
+ inCode = false;
36
+ out.push(chalk.dim(`${indent}└${'─'.repeat(40)}`));
37
+ }
38
+ continue;
39
+ }
40
+
41
+ if (inCode) {
42
+ out.push(chalk.hex(THEME)(`${indent}│ `) + chalk.white(line));
43
+ continue;
44
+ }
45
+
46
+ // Horizontal rule
47
+ if (/^[-*_]{3,}$/.test(line.trim())) {
48
+ out.push(chalk.dim(`${indent}${'─'.repeat(44)}`));
49
+ continue;
50
+ }
51
+
52
+ // Headings
53
+ let m;
54
+ if ((m = line.match(/^(#{1,3}) (.+)/))) {
55
+ const level = m[1].length;
56
+ const txt = m[2];
57
+ if (level === 1) out.push(`\n${indent}${chalk.bold.underline(txt)}`);
58
+ else if (level === 2) out.push(`\n${indent}${chalk.bold(txt)}`);
59
+ else out.push(`${indent}${chalk.hex(THEME).bold(txt)}`);
60
+ continue;
61
+ }
62
+
63
+ // Unordered list
64
+ if ((m = line.match(/^(\s*)[-*+] (.+)/))) {
65
+ const pad = ' '.repeat(m[1].length);
66
+ out.push(`${indent}${pad}${chalk.hex(THEME)('▸')} ${renderInline(m[2])}`);
67
+ continue;
68
+ }
69
+
70
+ // Ordered list
71
+ if ((m = line.match(/^(\s*)(\d+)\. (.+)/))) {
72
+ const pad = ' '.repeat(m[1].length);
73
+ out.push(`${indent}${pad}${chalk.dim(m[2] + '.')} ${renderInline(m[3])}`);
74
+ continue;
75
+ }
76
+
77
+ // Blockquote
78
+ if ((m = line.match(/^> ?(.*)/))) {
79
+ out.push(chalk.hex(THEME)(`${indent}│ `) + chalk.dim(renderInline(m[1])));
80
+ continue;
81
+ }
82
+
83
+ // Empty line
84
+ if (!line.trim()) {
85
+ out.push('');
86
+ continue;
87
+ }
88
+
89
+ // Normal paragraph
90
+ out.push(`${indent}${renderInline(line)}`);
91
+ }
92
+
93
+ return out.join('\n');
94
+ }
95
+
96
+ module.exports = { renderMarkdown, renderInline };
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "prior-cli",
3
+ "version": "1.0.0",
4
+ "description": "Prior Network AI — command-line interface",
5
+ "bin": {
6
+ "prior": "bin/prior.js"
7
+ },
8
+ "scripts": {
9
+ "start": "node bin/prior.js"
10
+ },
11
+ "dependencies": {
12
+ "chalk": "^4.1.2",
13
+ "commander": "^11.1.0",
14
+ "node-fetch": "^2.7.0",
15
+ "open": "^8.4.2"
16
+ },
17
+ "engines": {
18
+ "node": ">=16.0.0"
19
+ },
20
+ "keywords": [
21
+ "prior",
22
+ "ai",
23
+ "cli"
24
+ ],
25
+ "license": "MIT",
26
+ "files": [
27
+ "bin/",
28
+ "lib/"
29
+ ]
30
+ }