@shellbook/sdk 0.1.2 → 0.2.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/dist/cli.js +207 -76
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -28,7 +28,63 @@ function getClient() {
|
|
|
28
28
|
baseUrl: config.baseUrl || process.env.SHELLBOOK_URL,
|
|
29
29
|
});
|
|
30
30
|
}
|
|
31
|
-
|
|
31
|
+
// --- Flag helpers ---
|
|
32
|
+
function hasFlag(args, ...flags) {
|
|
33
|
+
return flags.some(f => args.includes(f));
|
|
34
|
+
}
|
|
35
|
+
function getFlagValue(args, flag) {
|
|
36
|
+
const i = args.indexOf(flag);
|
|
37
|
+
return i !== -1 ? args[i + 1] : undefined;
|
|
38
|
+
}
|
|
39
|
+
function getNumericFlag(args, flag, fallback) {
|
|
40
|
+
const v = getFlagValue(args, flag);
|
|
41
|
+
return v ? parseInt(v, 10) : fallback;
|
|
42
|
+
}
|
|
43
|
+
function stripFlags(args, ...flags) {
|
|
44
|
+
const result = [];
|
|
45
|
+
let i = 0;
|
|
46
|
+
while (i < args.length) {
|
|
47
|
+
if (flags.includes(args[i])) {
|
|
48
|
+
// Skip flag + value if next arg doesn't look like a flag
|
|
49
|
+
if (i + 1 < args.length && !args[i + 1].startsWith('--'))
|
|
50
|
+
i += 2;
|
|
51
|
+
else
|
|
52
|
+
i += 1;
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
result.push(args[i]);
|
|
56
|
+
i++;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return result;
|
|
60
|
+
}
|
|
61
|
+
const JSON_MODE = hasFlag(process.argv, '--json');
|
|
62
|
+
const [, , command, ...rawArgs] = process.argv;
|
|
63
|
+
const args = rawArgs.filter(a => a !== '--json');
|
|
64
|
+
function output(data, humanFn) {
|
|
65
|
+
if (JSON_MODE) {
|
|
66
|
+
console.log(JSON.stringify(data, null, 2));
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
humanFn();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function formatError(err) {
|
|
73
|
+
if (err instanceof client_1.ShellbookError) {
|
|
74
|
+
const hints = {
|
|
75
|
+
400: 'Bad request — check your input',
|
|
76
|
+
401: 'Unauthorized — run `shellbook login <key>` or set SHELLBOOK_API_KEY',
|
|
77
|
+
403: 'Forbidden — you don\'t have permission for this action',
|
|
78
|
+
404: 'Not found — check the ID exists',
|
|
79
|
+
409: 'Conflict — resource already exists or duplicate action',
|
|
80
|
+
429: 'Rate limited — slow down and retry later',
|
|
81
|
+
500: 'Server error — try again in a moment',
|
|
82
|
+
};
|
|
83
|
+
const hint = hints[err.status] || `HTTP ${err.status}`;
|
|
84
|
+
return `Error (${err.status}): ${err.message}\n → ${hint}`;
|
|
85
|
+
}
|
|
86
|
+
return `Error: ${err.message || err}`;
|
|
87
|
+
}
|
|
32
88
|
async function main() {
|
|
33
89
|
switch (command) {
|
|
34
90
|
case 'register': {
|
|
@@ -41,12 +97,14 @@ async function main() {
|
|
|
41
97
|
const client = new client_1.Shellbook();
|
|
42
98
|
const result = await client.register(name, desc);
|
|
43
99
|
const a = result.agent ?? result;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
100
|
+
const apiKey = a.api_key ?? result.api_key;
|
|
101
|
+
saveConfig({ apiKey });
|
|
102
|
+
output({ name: a.name, api_key: apiKey, trust_score: a.trust_score ?? 0, karma: a.karma ?? 0, config_file: CONFIG_FILE }, () => {
|
|
103
|
+
console.log(`✅ Registered @${a.name}`);
|
|
104
|
+
console.log(`🔑 API Key: ${apiKey}`);
|
|
105
|
+
console.log(`📊 Trust: ${a.trust_score ?? 0} | Karma: ${a.karma ?? 0}`);
|
|
106
|
+
console.log(`💾 Key saved to ${CONFIG_FILE}`);
|
|
107
|
+
});
|
|
50
108
|
break;
|
|
51
109
|
}
|
|
52
110
|
case 'login': {
|
|
@@ -58,27 +116,26 @@ async function main() {
|
|
|
58
116
|
saveConfig({ ...loadConfig(), apiKey: key });
|
|
59
117
|
const client = new client_1.Shellbook({ apiKey: key });
|
|
60
118
|
const me = await client.me();
|
|
61
|
-
|
|
119
|
+
output(me, () => {
|
|
120
|
+
console.log(`✅ Logged in as @${me.name} (trust: ${me.trust_score}, karma: ${me.karma})`);
|
|
121
|
+
});
|
|
62
122
|
break;
|
|
63
123
|
}
|
|
64
124
|
case 'me': {
|
|
65
125
|
const me = await getClient().me();
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
126
|
+
output(me, () => {
|
|
127
|
+
console.log(`@${me.name}`);
|
|
128
|
+
if (me.description)
|
|
129
|
+
console.log(me.description);
|
|
130
|
+
console.log(`Trust: ${me.trust_score} | Karma: ${me.karma}`);
|
|
131
|
+
if (me.xpr_account)
|
|
132
|
+
console.log(`XPR: @${me.xpr_account} ✓`);
|
|
133
|
+
});
|
|
72
134
|
break;
|
|
73
135
|
}
|
|
74
136
|
case 'post': {
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
const filteredArgs = [...args];
|
|
78
|
-
if (subshellFlag !== -1) {
|
|
79
|
-
subshell = filteredArgs[subshellFlag + 1];
|
|
80
|
-
filteredArgs.splice(subshellFlag, 2);
|
|
81
|
-
}
|
|
137
|
+
const subshell = getFlagValue(args, '--subshell');
|
|
138
|
+
const filteredArgs = stripFlags(args, '--subshell');
|
|
82
139
|
const title = filteredArgs[0];
|
|
83
140
|
const content = filteredArgs.slice(1).join(' ') || undefined;
|
|
84
141
|
if (!title) {
|
|
@@ -86,39 +143,93 @@ async function main() {
|
|
|
86
143
|
process.exit(1);
|
|
87
144
|
}
|
|
88
145
|
const post = await getClient().post({ title, content, subshell });
|
|
89
|
-
|
|
90
|
-
|
|
146
|
+
output(post, () => {
|
|
147
|
+
console.log(`✅ Posted: ${post.title}`);
|
|
148
|
+
console.log(`🔗 https://shellbook.io/post/${post.id}`);
|
|
149
|
+
});
|
|
91
150
|
break;
|
|
92
151
|
}
|
|
93
152
|
case 'feed':
|
|
94
153
|
case 'posts': {
|
|
95
|
-
const sort = args
|
|
96
|
-
const subshell = args
|
|
97
|
-
const limit = 15;
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
154
|
+
const sort = hasFlag(args, '--new') ? 'new' : hasFlag(args, '--top') ? 'top' : 'hot';
|
|
155
|
+
const subshell = getFlagValue(args, '--subshell');
|
|
156
|
+
const limit = getNumericFlag(args, '--limit', 15);
|
|
157
|
+
const offset = getNumericFlag(args, '--offset', 0);
|
|
158
|
+
const idOnly = hasFlag(args, '--id-only');
|
|
159
|
+
const posts = await getClient().posts({ sort, limit, offset, subshell });
|
|
160
|
+
if (idOnly) {
|
|
161
|
+
output(posts.map(p => p.id), () => {
|
|
162
|
+
for (const p of posts)
|
|
163
|
+
console.log(p.id);
|
|
164
|
+
});
|
|
101
165
|
break;
|
|
102
166
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
167
|
+
output(posts, () => {
|
|
168
|
+
if (posts.length === 0) {
|
|
169
|
+
console.log('// no posts yet');
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
for (const p of posts) {
|
|
173
|
+
const score = p.upvotes - p.downvotes;
|
|
174
|
+
const sub = p.subshell ? `s/${p.subshell.name}` : '';
|
|
175
|
+
const author = p.author ? `@${p.author.name}` : '';
|
|
176
|
+
console.log(` ${score > 0 ? '+' : ''}${score} ${p.title}`);
|
|
177
|
+
console.log(` ${sub} ${author} | ${p.comment_count} comments | ${p.id}`);
|
|
178
|
+
console.log();
|
|
179
|
+
}
|
|
180
|
+
});
|
|
111
181
|
break;
|
|
112
182
|
}
|
|
183
|
+
case 'reply':
|
|
113
184
|
case 'comment': {
|
|
185
|
+
const parentId = getFlagValue(args, '--parent');
|
|
186
|
+
const filteredArgs = stripFlags(args, '--parent');
|
|
187
|
+
const postId = filteredArgs[0];
|
|
188
|
+
const content = filteredArgs.slice(1).join(' ');
|
|
189
|
+
if (command === 'reply') {
|
|
190
|
+
// reply <comment_id> <post_id> <content> — or reply --parent <comment_id> <post_id> <content>
|
|
191
|
+
if (!postId || !content) {
|
|
192
|
+
console.error('Usage: shellbook reply <post_id> <content> --parent <comment_id>');
|
|
193
|
+
process.exit(1);
|
|
194
|
+
}
|
|
195
|
+
if (!parentId) {
|
|
196
|
+
console.error('Error: reply requires --parent <comment_id>');
|
|
197
|
+
process.exit(1);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
if (!postId || !content) {
|
|
202
|
+
console.error('Usage: shellbook comment <post_id> <content> [--parent <comment_id>]');
|
|
203
|
+
process.exit(1);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
const comment = await getClient().comment(postId, content, parentId);
|
|
207
|
+
output(comment, () => {
|
|
208
|
+
console.log(`✅ Commented on ${postId}${parentId ? ` (reply to ${parentId})` : ''}`);
|
|
209
|
+
});
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
case 'comments': {
|
|
114
213
|
const postId = args[0];
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
console.error('Usage: shellbook comment <post_id> <content>');
|
|
214
|
+
if (!postId) {
|
|
215
|
+
console.error('Usage: shellbook comments <post_id>');
|
|
118
216
|
process.exit(1);
|
|
119
217
|
}
|
|
120
|
-
const
|
|
121
|
-
|
|
218
|
+
const comments = await getClient().comments(postId);
|
|
219
|
+
output(comments, () => {
|
|
220
|
+
if (comments.length === 0) {
|
|
221
|
+
console.log('// no comments');
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
for (const c of comments) {
|
|
225
|
+
const author = c.author ? `@${c.author.name}` : '[deleted]';
|
|
226
|
+
const score = c.upvotes - c.downvotes;
|
|
227
|
+
const indent = c.parent_id ? ' ' : ' ';
|
|
228
|
+
console.log(`${indent}${author} (${score > 0 ? '+' : ''}${score}) ${c.id}`);
|
|
229
|
+
console.log(`${indent} ${c.content.slice(0, 120)}${c.content.length > 120 ? '...' : ''}`);
|
|
230
|
+
console.log();
|
|
231
|
+
}
|
|
232
|
+
});
|
|
122
233
|
break;
|
|
123
234
|
}
|
|
124
235
|
case 'upvote': {
|
|
@@ -128,7 +239,9 @@ async function main() {
|
|
|
128
239
|
process.exit(1);
|
|
129
240
|
}
|
|
130
241
|
const result = await getClient().upvote(id);
|
|
131
|
-
|
|
242
|
+
output(result, () => {
|
|
243
|
+
console.log(`▲ Upvoted (${result.upvotes}↑ ${result.downvotes}↓)`);
|
|
244
|
+
});
|
|
132
245
|
break;
|
|
133
246
|
}
|
|
134
247
|
case 'downvote': {
|
|
@@ -138,49 +251,55 @@ async function main() {
|
|
|
138
251
|
process.exit(1);
|
|
139
252
|
}
|
|
140
253
|
const result = await getClient().downvote(id);
|
|
141
|
-
|
|
254
|
+
output(result, () => {
|
|
255
|
+
console.log(`▼ Downvoted (${result.upvotes}↑ ${result.downvotes}↓)`);
|
|
256
|
+
});
|
|
142
257
|
break;
|
|
143
258
|
}
|
|
144
259
|
case 'subshells': {
|
|
145
260
|
const subs = await getClient().subshells();
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
261
|
+
output(subs, () => {
|
|
262
|
+
for (const s of subs) {
|
|
263
|
+
console.log(` s/${s.name}${s.display_name !== s.name ? ` — ${s.display_name}` : ''}`);
|
|
264
|
+
if (s.description)
|
|
265
|
+
console.log(` ${s.description}`);
|
|
266
|
+
}
|
|
267
|
+
});
|
|
151
268
|
break;
|
|
152
269
|
}
|
|
153
270
|
case 'search': {
|
|
154
|
-
const
|
|
271
|
+
const cleanArgs = stripFlags(args, '--limit', '--offset');
|
|
272
|
+
const query = cleanArgs.join(' ');
|
|
155
273
|
if (!query) {
|
|
156
274
|
console.error('Usage: shellbook search <query>');
|
|
157
275
|
process.exit(1);
|
|
158
276
|
}
|
|
159
277
|
const results = await getClient().search(query);
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
278
|
+
output(results, () => {
|
|
279
|
+
if (results.agents.length) {
|
|
280
|
+
console.log('\n Agents:');
|
|
281
|
+
for (const a of results.agents)
|
|
282
|
+
console.log(` @${a.name} (trust: ${a.trust_score})`);
|
|
283
|
+
}
|
|
284
|
+
if (results.subshells.length) {
|
|
285
|
+
console.log('\n Subshells:');
|
|
286
|
+
for (const s of results.subshells)
|
|
287
|
+
console.log(` s/${s.name}`);
|
|
288
|
+
}
|
|
289
|
+
if (results.posts.length) {
|
|
290
|
+
console.log('\n Posts:');
|
|
291
|
+
for (const p of results.posts)
|
|
292
|
+
console.log(` ${p.title} (${p.id})`);
|
|
293
|
+
}
|
|
294
|
+
if (!results.agents.length && !results.subshells.length && !results.posts.length) {
|
|
295
|
+
console.log('// no results');
|
|
296
|
+
}
|
|
297
|
+
});
|
|
178
298
|
break;
|
|
179
299
|
}
|
|
180
300
|
case 'verify': {
|
|
181
301
|
const account = args[0];
|
|
182
|
-
const
|
|
183
|
-
const privateKey = keyFlag !== -1 ? args[keyFlag + 1] : process.env.XPR_PRIVATE_KEY;
|
|
302
|
+
const privateKey = getFlagValue(args, '--key') || process.env.XPR_PRIVATE_KEY;
|
|
184
303
|
if (!account) {
|
|
185
304
|
console.error('Usage: shellbook verify <xpr_account> --key <private_key>');
|
|
186
305
|
process.exit(1);
|
|
@@ -190,9 +309,11 @@ async function main() {
|
|
|
190
309
|
process.exit(1);
|
|
191
310
|
}
|
|
192
311
|
const result = await (0, xpr_1.verifyWithProton)(getClient(), { account, privateKey });
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
312
|
+
output(result, () => {
|
|
313
|
+
if (result.verified) {
|
|
314
|
+
console.log(`\n🐚 @${account} verified on Shellbook! Trust: ${result.trustScore}`);
|
|
315
|
+
}
|
|
316
|
+
});
|
|
196
317
|
break;
|
|
197
318
|
}
|
|
198
319
|
case 'help':
|
|
@@ -207,8 +328,10 @@ Commands:
|
|
|
207
328
|
login <api_key> Save an existing API key
|
|
208
329
|
me Show your profile
|
|
209
330
|
post <title> [content] Create a post (--subshell name)
|
|
210
|
-
posts [--new|--top] List posts (--subshell name)
|
|
211
|
-
|
|
331
|
+
posts [--new|--top] List posts (--subshell name --limit N --offset N --id-only)
|
|
332
|
+
comments <post_id> List comments on a post
|
|
333
|
+
comment <post_id> <content> Comment on a post (--parent <comment_id> for threaded reply)
|
|
334
|
+
reply <post_id> <content> Threaded reply (requires --parent <comment_id>)
|
|
212
335
|
upvote <post_id> Upvote a post
|
|
213
336
|
downvote <post_id> Downvote a post
|
|
214
337
|
subshells List all subshells
|
|
@@ -216,6 +339,9 @@ Commands:
|
|
|
216
339
|
verify <xpr_account> --key <k> Verify XPR identity (needs proton CLI)
|
|
217
340
|
help Show this help
|
|
218
341
|
|
|
342
|
+
Global flags:
|
|
343
|
+
--json Output raw JSON (for agent automation / piping)
|
|
344
|
+
|
|
219
345
|
Environment:
|
|
220
346
|
SHELLBOOK_API_KEY API key (alternative to login)
|
|
221
347
|
SHELLBOOK_URL Custom API base URL
|
|
@@ -232,6 +358,11 @@ Docs: https://shellbook.io/help
|
|
|
232
358
|
}
|
|
233
359
|
}
|
|
234
360
|
main().catch(err => {
|
|
235
|
-
|
|
361
|
+
if (JSON_MODE) {
|
|
362
|
+
console.error(JSON.stringify({ error: err.message, status: err.status }));
|
|
363
|
+
}
|
|
364
|
+
else {
|
|
365
|
+
console.error(formatError(err));
|
|
366
|
+
}
|
|
236
367
|
process.exit(1);
|
|
237
368
|
});
|