@neta-art/cohub-cli 2.2.9 → 2.3.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/commands/profile.js +33 -0
- package/dist/commands/referrals.d.ts +2 -0
- package/dist/commands/referrals.js +84 -0
- package/dist/commands/spaces.js +104 -0
- package/dist/index.js +3 -0
- package/package.json +3 -3
package/dist/commands/profile.js
CHANGED
|
@@ -3,6 +3,39 @@ import { createClient } from "../client.js";
|
|
|
3
3
|
import { json as outJson, jsonRequested, ok, error, handleHttp } from "../output.js";
|
|
4
4
|
export function registerProfile(program) {
|
|
5
5
|
const profileCmd = program.command("profile").description("Manage your profile");
|
|
6
|
+
profileCmd
|
|
7
|
+
.command("get <username>")
|
|
8
|
+
.description("Show a public user profile")
|
|
9
|
+
.option("--json", "Output as JSON")
|
|
10
|
+
.action(async (username, opts) => {
|
|
11
|
+
const client = createClient();
|
|
12
|
+
try {
|
|
13
|
+
const result = await client.users.getByUsername(username);
|
|
14
|
+
if (jsonRequested(opts))
|
|
15
|
+
return outJson(result);
|
|
16
|
+
const handle = result.profile.username ? `@${result.profile.username}` : result.profile.userUuid;
|
|
17
|
+
console.log(`\n ${result.profile.displayName} (${handle})`);
|
|
18
|
+
console.log(` Spaces: ${result.spaces.length}`);
|
|
19
|
+
console.log(` Works: ${result.works.length}\n`);
|
|
20
|
+
if (result.spaces.length > 0) {
|
|
21
|
+
console.log(" Spaces:");
|
|
22
|
+
for (const space of result.spaces) {
|
|
23
|
+
console.log(` - ${space.name} [${space.accessLabel}] ${space.spaceUrl}`);
|
|
24
|
+
}
|
|
25
|
+
console.log("");
|
|
26
|
+
}
|
|
27
|
+
if (result.works.length > 0) {
|
|
28
|
+
console.log(" Works:");
|
|
29
|
+
for (const work of result.works) {
|
|
30
|
+
console.log(` - ${work.title} ${work.publicUrl}`);
|
|
31
|
+
}
|
|
32
|
+
console.log("");
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
catch (e) {
|
|
36
|
+
handleHttp(e);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
6
39
|
profileCmd
|
|
7
40
|
.command("update")
|
|
8
41
|
.description("Update your profile")
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { createClient } from "../client.js";
|
|
2
|
+
import { error, handleHttp, json as outJson, jsonRequested, ok, table } from "../output.js";
|
|
3
|
+
function referralUrl(code) {
|
|
4
|
+
const origin = process.env.COHUB_WEB_URL?.replace(/\/+$/, "") ?? "https://cohub.run";
|
|
5
|
+
return `${origin}/referrals/${code}`;
|
|
6
|
+
}
|
|
7
|
+
async function confirmRotate(opts) {
|
|
8
|
+
if (opts.yes)
|
|
9
|
+
return;
|
|
10
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
11
|
+
return error("Confirmation required", "Pass --yes to replace the referral link.");
|
|
12
|
+
}
|
|
13
|
+
process.stdout.write("The current referral link will stop working. Continue? [y/N] ");
|
|
14
|
+
const chunks = [];
|
|
15
|
+
for await (const chunk of process.stdin) {
|
|
16
|
+
chunks.push(chunk);
|
|
17
|
+
break;
|
|
18
|
+
}
|
|
19
|
+
const answer = Buffer.concat(chunks).toString().trim().toLowerCase();
|
|
20
|
+
if (answer !== "y" && answer !== "yes")
|
|
21
|
+
return error("Cancelled");
|
|
22
|
+
}
|
|
23
|
+
export function registerReferrals(program) {
|
|
24
|
+
const command = program
|
|
25
|
+
.command("referrals")
|
|
26
|
+
.description("View and manage your referral link")
|
|
27
|
+
.option("--json", "Output as JSON")
|
|
28
|
+
.action(async (opts) => {
|
|
29
|
+
const client = createClient();
|
|
30
|
+
try {
|
|
31
|
+
const result = await client.referrals.getMine();
|
|
32
|
+
const output = { ...result, url: referralUrl(result.code) };
|
|
33
|
+
if (jsonRequested(opts))
|
|
34
|
+
return outJson(output);
|
|
35
|
+
console.log(`\nReferral link: ${output.url}`);
|
|
36
|
+
console.log(`Rewarded: ${result.summary.rewarded}`);
|
|
37
|
+
console.log(`Earned: $${result.summary.earnedUsd.toFixed(2)}\n`);
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
handleHttp(error);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
command
|
|
44
|
+
.command("ls")
|
|
45
|
+
.alias("list")
|
|
46
|
+
.description("List your referrals")
|
|
47
|
+
.option("--json", "Output as JSON")
|
|
48
|
+
.action(async (opts) => {
|
|
49
|
+
const client = createClient();
|
|
50
|
+
try {
|
|
51
|
+
const result = await client.referrals.getMine();
|
|
52
|
+
if (jsonRequested(opts))
|
|
53
|
+
return outJson(result);
|
|
54
|
+
table(result.items, [
|
|
55
|
+
{ key: "id", label: "ID" },
|
|
56
|
+
{ key: "status", label: "Status" },
|
|
57
|
+
{ key: "claimedAt", label: "Claimed" },
|
|
58
|
+
{ key: "rewardedAt", label: "Rewarded" },
|
|
59
|
+
]);
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
handleHttp(error);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
command
|
|
66
|
+
.command("rotate")
|
|
67
|
+
.description("Replace your referral link")
|
|
68
|
+
.option("-y, --yes", "Confirm replacement")
|
|
69
|
+
.option("--json", "Output as JSON")
|
|
70
|
+
.action(async (opts) => {
|
|
71
|
+
await confirmRotate(opts);
|
|
72
|
+
const client = createClient();
|
|
73
|
+
try {
|
|
74
|
+
const result = await client.referrals.rotateCode();
|
|
75
|
+
const output = { ...result, url: referralUrl(result.code) };
|
|
76
|
+
if (jsonRequested(opts))
|
|
77
|
+
return outJson(output);
|
|
78
|
+
ok(`Referral link rotated: ${output.url}`);
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
handleHttp(error);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
package/dist/commands/spaces.js
CHANGED
|
@@ -252,6 +252,85 @@ async function sendPrompt(command, words, opts) {
|
|
|
252
252
|
handleHttp(e);
|
|
253
253
|
}
|
|
254
254
|
}
|
|
255
|
+
async function runCompletionCommand(command, words, opts) {
|
|
256
|
+
const spaceId = resolveSpace(command);
|
|
257
|
+
const content = words.join(" ").trim();
|
|
258
|
+
if (!content && process.stdin.isTTY) {
|
|
259
|
+
return error("Message required", "Pass content args or pipe via stdin");
|
|
260
|
+
}
|
|
261
|
+
let text = content;
|
|
262
|
+
if (!text && !process.stdin.isTTY) {
|
|
263
|
+
const chunks = [];
|
|
264
|
+
for await (const chunk of process.stdin)
|
|
265
|
+
chunks.push(chunk);
|
|
266
|
+
text = Buffer.concat(chunks).toString().trim();
|
|
267
|
+
}
|
|
268
|
+
if (!text)
|
|
269
|
+
return error("Message required", "Pass content args or pipe via stdin");
|
|
270
|
+
const temperature = opts.temperature !== undefined ? Number(opts.temperature) : undefined;
|
|
271
|
+
if (temperature !== undefined && !Number.isFinite(temperature)) {
|
|
272
|
+
return error("Invalid temperature", "--temperature must be a number");
|
|
273
|
+
}
|
|
274
|
+
const maxTokens = opts.maxTokens !== undefined ? Number(opts.maxTokens) : undefined;
|
|
275
|
+
if (maxTokens !== undefined && (!Number.isFinite(maxTokens) || maxTokens <= 0)) {
|
|
276
|
+
return error("Invalid max tokens", "--max-tokens must be a positive number");
|
|
277
|
+
}
|
|
278
|
+
const thinkingLevel = opts.thinkingLevel?.trim() || undefined;
|
|
279
|
+
if (thinkingLevel
|
|
280
|
+
&& !new Set(["off", "minimal", "low", "medium", "high", "xhigh"]).has(thinkingLevel)) {
|
|
281
|
+
return error("Invalid thinking level", "Use off|minimal|low|medium|high|xhigh");
|
|
282
|
+
}
|
|
283
|
+
const client = createClient();
|
|
284
|
+
const input = {
|
|
285
|
+
provider: opts.provider,
|
|
286
|
+
model: opts.model,
|
|
287
|
+
systemPromptPath: opts.systemPrompt,
|
|
288
|
+
messages: [{ role: "user", content: [{ type: "text", text }] }],
|
|
289
|
+
temperature,
|
|
290
|
+
maxTokens,
|
|
291
|
+
thinkingLevel: thinkingLevel,
|
|
292
|
+
};
|
|
293
|
+
try {
|
|
294
|
+
if (opts.stream) {
|
|
295
|
+
const iterator = client.space(spaceId).streamCompletion(input);
|
|
296
|
+
let step = await iterator.next();
|
|
297
|
+
let finalResult = null;
|
|
298
|
+
while (!step.done) {
|
|
299
|
+
const event = step.value;
|
|
300
|
+
if (event.type === "delta") {
|
|
301
|
+
if (!jsonRequested(opts))
|
|
302
|
+
process.stdout.write(event.text);
|
|
303
|
+
}
|
|
304
|
+
else if (event.type === "thinking_delta" && !jsonRequested(opts)) {
|
|
305
|
+
process.stderr.write(event.text);
|
|
306
|
+
}
|
|
307
|
+
step = await iterator.next();
|
|
308
|
+
}
|
|
309
|
+
finalResult = step.value;
|
|
310
|
+
if (jsonRequested(opts))
|
|
311
|
+
return outJson(finalResult);
|
|
312
|
+
process.stdout.write("\n");
|
|
313
|
+
if (finalResult.usage?.totalTokens != null) {
|
|
314
|
+
process.stderr.write(` tokens: ${finalResult.usage.totalTokens}\n`);
|
|
315
|
+
}
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
const result = await client.space(spaceId).completion(input);
|
|
319
|
+
if (jsonRequested(opts))
|
|
320
|
+
return outJson(result);
|
|
321
|
+
const textOut = result.message.content
|
|
322
|
+
.filter((block) => block.type === "text")
|
|
323
|
+
.map((block) => block.text)
|
|
324
|
+
.join("");
|
|
325
|
+
console.log(textOut);
|
|
326
|
+
if (result.usage?.totalTokens != null) {
|
|
327
|
+
process.stderr.write(`\n tokens: ${result.usage.totalTokens}\n`);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
catch (e) {
|
|
331
|
+
handleHttp(e);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
255
334
|
export function registerPrompt(program) {
|
|
256
335
|
program
|
|
257
336
|
.command("prompt [content...]")
|
|
@@ -272,6 +351,18 @@ export function registerPrompt(program) {
|
|
|
272
351
|
.option("--image <path>", "Attach an image", collectOption, [])
|
|
273
352
|
.option("--json", "Output as JSON")
|
|
274
353
|
.action((words, opts) => sendPrompt(program, words, opts));
|
|
354
|
+
program
|
|
355
|
+
.command("completion [content...]")
|
|
356
|
+
.description("Raw LLM completion with full message control")
|
|
357
|
+
.option("-m, --model <model>", "Model name")
|
|
358
|
+
.option("-p, --provider <provider>", "Provider name")
|
|
359
|
+
.option("--system-prompt <path>", "Space-relative system prompt markdown path")
|
|
360
|
+
.option("--stream", "Stream tokens via SSE")
|
|
361
|
+
.option("--temperature <n>", "Sampling temperature")
|
|
362
|
+
.option("--max-tokens <n>", "Maximum output tokens")
|
|
363
|
+
.option("--thinking-level <level>", "Thinking level: off|minimal|low|medium|high|xhigh")
|
|
364
|
+
.option("--json", "Output as JSON")
|
|
365
|
+
.action((words, opts) => runCompletionCommand(program, words, opts));
|
|
275
366
|
}
|
|
276
367
|
export function registerSpaces(program) {
|
|
277
368
|
const spacesCmd = program.command("spaces").description("Space management");
|
|
@@ -476,6 +567,19 @@ export function registerSpaces(program) {
|
|
|
476
567
|
.option("--image <path>", "Attach an image", collectOption, [])
|
|
477
568
|
.option("--json", "Output as JSON")
|
|
478
569
|
.action((words, opts) => sendPrompt(spacesCmd, words, opts));
|
|
570
|
+
// ── spaces completion ──
|
|
571
|
+
spacesCmd
|
|
572
|
+
.command("completion [content...]")
|
|
573
|
+
.description("Raw LLM completion with full message control")
|
|
574
|
+
.option("-m, --model <model>", "Model name")
|
|
575
|
+
.option("-p, --provider <provider>", "Provider name")
|
|
576
|
+
.option("--system-prompt <path>", "Space-relative system prompt markdown path")
|
|
577
|
+
.option("--stream", "Stream tokens via SSE")
|
|
578
|
+
.option("--temperature <n>", "Sampling temperature")
|
|
579
|
+
.option("--max-tokens <n>", "Maximum output tokens")
|
|
580
|
+
.option("--thinking-level <level>", "Thinking level: off|minimal|low|medium|high|xhigh")
|
|
581
|
+
.option("--json", "Output as JSON")
|
|
582
|
+
.action((words, opts) => runCompletionCommand(spacesCmd, words, opts));
|
|
479
583
|
// ── spaces files ──
|
|
480
584
|
registerFiles(spacesCmd);
|
|
481
585
|
// ── spaces sessions ──
|
package/dist/index.js
CHANGED
|
@@ -10,6 +10,7 @@ import { registerModels } from "./commands/models.js";
|
|
|
10
10
|
import { registerProfile } from "./commands/profile.js";
|
|
11
11
|
import { registerSearch } from "./commands/search.js";
|
|
12
12
|
import { registerReferences } from "./commands/references.js";
|
|
13
|
+
import { registerReferrals } from "./commands/referrals.js";
|
|
13
14
|
import { registerPrompt, registerSpaces } from "./commands/spaces.js";
|
|
14
15
|
import { maybeHandleRunCommand } from "./commands/run.js";
|
|
15
16
|
import { registerSandbox } from "./commands/sandbox.js";
|
|
@@ -41,6 +42,7 @@ Common commands:
|
|
|
41
42
|
cohub profile avatar ./avatar.png
|
|
42
43
|
cohub spaces ls
|
|
43
44
|
cohub -s <space-id> prompt "Fix the failing tests"
|
|
45
|
+
cohub -s <space-id> completion "Summarize AGENTS.md" --system-prompt AGENTS.md --stream
|
|
44
46
|
cohub -s <space-id> run -- git status
|
|
45
47
|
cohub sandbox up ./my-project
|
|
46
48
|
cohub search "release notes"
|
|
@@ -67,6 +69,7 @@ registerGenerations(program);
|
|
|
67
69
|
registerModels(program);
|
|
68
70
|
registerSearch(program);
|
|
69
71
|
registerReferences(program);
|
|
72
|
+
registerReferrals(program);
|
|
70
73
|
registerTasks(program);
|
|
71
74
|
registerCronJobs(program);
|
|
72
75
|
registerWorks(program);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neta-art/cohub-cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "CLI for Cohub — spaces, sessions, and agent collaboration.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -13,10 +13,10 @@
|
|
|
13
13
|
"README.md"
|
|
14
14
|
],
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@neta-art/generation": "^0.1.
|
|
16
|
+
"@neta-art/generation": "^0.1.13",
|
|
17
17
|
"commander": "^14.0.3",
|
|
18
18
|
"sharp": "^0.34.5",
|
|
19
|
-
"@neta-art/cohub": "2.
|
|
19
|
+
"@neta-art/cohub": "2.10.0"
|
|
20
20
|
},
|
|
21
21
|
"publishConfig": {
|
|
22
22
|
"access": "public"
|