cueloop 0.1.0-alpha.58 → 0.1.0-alpha.59
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/package.json +5 -5
- package/src/main.ts +85 -95
- package/src/session-commands.ts +127 -93
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cueloop",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.59",
|
|
4
4
|
"description": "Terminal review surface for coding agents: annotate plans and diffs, return a verdict, send structured feedback back",
|
|
5
5
|
"homepage": "https://github.com/mmurakaru/cueloop#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -30,10 +30,10 @@
|
|
|
30
30
|
"build:binary": "bun build --compile --external cpu-features ./src/main.ts --outfile dist/cueloop"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@cueloop/adapters": "0.1.0-alpha.
|
|
34
|
-
"@cueloop/client": "0.1.0-alpha.
|
|
35
|
-
"@cueloop/daemon": "0.1.0-alpha.
|
|
36
|
-
"@cueloop/schema": "0.1.0-alpha.
|
|
33
|
+
"@cueloop/adapters": "0.1.0-alpha.59",
|
|
34
|
+
"@cueloop/client": "0.1.0-alpha.59",
|
|
35
|
+
"@cueloop/daemon": "0.1.0-alpha.59",
|
|
36
|
+
"@cueloop/schema": "0.1.0-alpha.59"
|
|
37
37
|
},
|
|
38
38
|
"engines": {
|
|
39
39
|
"bun": ">=1.3.0"
|
package/src/main.ts
CHANGED
|
@@ -29,103 +29,93 @@ import { openReview, resolveWorkspace } from "@cueloop/daemon/review";
|
|
|
29
29
|
const argv = process.argv.slice(2);
|
|
30
30
|
const cmd = argv[0];
|
|
31
31
|
|
|
32
|
+
async function daemonCommand(): Promise<number> {
|
|
33
|
+
const { DaemonServer } = await import("@cueloop/daemon");
|
|
34
|
+
const server = new DaemonServer({ idleExitMs: 0 });
|
|
35
|
+
const path = server.start();
|
|
36
|
+
if (path === null) {
|
|
37
|
+
console.error("a cueloop daemon already owns this home - nothing to do");
|
|
38
|
+
return 1;
|
|
39
|
+
}
|
|
40
|
+
console.log(`cueloop daemon (foreground) on ${path}`);
|
|
41
|
+
await new Promise(() => {}); // run until signalled
|
|
42
|
+
return 0;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function serveEntry(rest: string[]): Promise<number> {
|
|
46
|
+
const { positional, flags } = parseArgs(rest);
|
|
47
|
+
const port = stringFlag(flags, "port");
|
|
48
|
+
const { serveClient } = await import("@cueloop/client");
|
|
49
|
+
const handle = await serveClient({
|
|
50
|
+
port: port !== undefined ? Number(port) : undefined,
|
|
51
|
+
host: stringFlag(flags, "host"),
|
|
52
|
+
sessionId: positional[0],
|
|
53
|
+
});
|
|
54
|
+
console.log(
|
|
55
|
+
[
|
|
56
|
+
"",
|
|
57
|
+
`observers join with: ssh -p ${handle.port} ${handle.host === "0.0.0.0" || handle.host === "::" ? "<this-host>" : handle.host}`,
|
|
58
|
+
"",
|
|
59
|
+
"no passwords, no keys: anyone who can reach this address can watch.",
|
|
60
|
+
"share the address deliberately (SSH tunnel, tailnet). observers are",
|
|
61
|
+
"read-only; you stay the one writable controller via `cueloop` locally.",
|
|
62
|
+
"ctrl-c stops serving.",
|
|
63
|
+
].join("\n"),
|
|
64
|
+
);
|
|
65
|
+
const stop = () => void handle.close().finally(() => process.exit(0));
|
|
66
|
+
process.on("SIGINT", stop);
|
|
67
|
+
process.on("SIGTERM", stop);
|
|
68
|
+
await new Promise(() => {}); // serve until signalled
|
|
69
|
+
return 0;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function shareEntry(rest: string[]): Promise<number> {
|
|
73
|
+
const { positional, flags } = parseArgs(rest);
|
|
74
|
+
const { shareCommand, sharePullCommand } = await import("./share-command");
|
|
75
|
+
const port = stringFlag(flags, "port");
|
|
76
|
+
const target = {
|
|
77
|
+
host: stringFlag(flags, "host"),
|
|
78
|
+
port: port !== undefined ? Number(port) : undefined,
|
|
79
|
+
};
|
|
80
|
+
if (positional[0] === "pull") return sharePullCommand({ ...target, sessionId: positional[1] });
|
|
81
|
+
return shareCommand({ ...target, sessionId: positional[0] });
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
type CommandHandler = (rest: string[]) => number | Promise<number>;
|
|
85
|
+
|
|
86
|
+
const commandHandlers: Record<string, CommandHandler> = {
|
|
87
|
+
session: (rest) => sessionCommand(rest),
|
|
88
|
+
daemon: () => daemonCommand(),
|
|
89
|
+
plan: (rest) => planCommand(rest),
|
|
90
|
+
diff: (rest) => diffCommand(rest),
|
|
91
|
+
prototype: (rest) => prototypeCommand(rest),
|
|
92
|
+
serve: (rest) => serveEntry(rest),
|
|
93
|
+
share: (rest) => shareEntry(rest),
|
|
94
|
+
wake: async (rest) => (await import("./wake-command")).wakeCommand(rest),
|
|
95
|
+
actions: async (rest) => (await import("./actions-command")).actionsCommand(rest),
|
|
96
|
+
refine: async (rest) => (await import("./refine-command")).refineCommand(rest),
|
|
97
|
+
review: (rest) => reviewEntry(rest),
|
|
98
|
+
"review-post": async (rest) => (await import("./pr")).reviewPostCommand(rest),
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const versionAliases = new Set(["-v", "--version", "version"]);
|
|
102
|
+
const helpAliases = new Set(["-h", "--help", "help"]);
|
|
103
|
+
|
|
32
104
|
async function main(): Promise<number> {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
console.log(`cueloop daemon (foreground) on ${path}`);
|
|
45
|
-
await new Promise(() => {}); // run until signalled
|
|
46
|
-
return 0;
|
|
47
|
-
}
|
|
48
|
-
case "plan":
|
|
49
|
-
return planCommand(argv.slice(1));
|
|
50
|
-
case "diff":
|
|
51
|
-
return diffCommand(argv.slice(1));
|
|
52
|
-
case "prototype":
|
|
53
|
-
return prototypeCommand(argv.slice(1));
|
|
54
|
-
case "serve": {
|
|
55
|
-
const { positional, flags } = parseArgs(argv.slice(1));
|
|
56
|
-
const port = stringFlag(flags, "port");
|
|
57
|
-
const { serveClient } = await import("@cueloop/client");
|
|
58
|
-
const handle = await serveClient({
|
|
59
|
-
port: port !== undefined ? Number(port) : undefined,
|
|
60
|
-
host: stringFlag(flags, "host"),
|
|
61
|
-
sessionId: positional[0],
|
|
62
|
-
});
|
|
63
|
-
console.log(
|
|
64
|
-
[
|
|
65
|
-
"",
|
|
66
|
-
`observers join with: ssh -p ${handle.port} ${handle.host === "0.0.0.0" || handle.host === "::" ? "<this-host>" : handle.host}`,
|
|
67
|
-
"",
|
|
68
|
-
"no passwords, no keys: anyone who can reach this address can watch.",
|
|
69
|
-
"share the address deliberately (SSH tunnel, tailnet). observers are",
|
|
70
|
-
"read-only; you stay the one writable controller via `cueloop` locally.",
|
|
71
|
-
"ctrl-c stops serving.",
|
|
72
|
-
].join("\n"),
|
|
73
|
-
);
|
|
74
|
-
const stop = () => void handle.close().finally(() => process.exit(0));
|
|
75
|
-
process.on("SIGINT", stop);
|
|
76
|
-
process.on("SIGTERM", stop);
|
|
77
|
-
await new Promise(() => {}); // serve until signalled
|
|
78
|
-
return 0;
|
|
79
|
-
}
|
|
80
|
-
case "share": {
|
|
81
|
-
const { positional, flags } = parseArgs(argv.slice(1));
|
|
82
|
-
const { shareCommand, sharePullCommand } = await import("./share-command");
|
|
83
|
-
const port = stringFlag(flags, "port");
|
|
84
|
-
const target = {
|
|
85
|
-
host: stringFlag(flags, "host"),
|
|
86
|
-
port: port !== undefined ? Number(port) : undefined,
|
|
87
|
-
};
|
|
88
|
-
if (positional[0] === "pull")
|
|
89
|
-
return sharePullCommand({ ...target, sessionId: positional[1] });
|
|
90
|
-
return shareCommand({ ...target, sessionId: positional[0] });
|
|
91
|
-
}
|
|
92
|
-
case "wake": {
|
|
93
|
-
const { wakeCommand } = await import("./wake-command");
|
|
94
|
-
return wakeCommand(argv.slice(1));
|
|
95
|
-
}
|
|
96
|
-
case "actions": {
|
|
97
|
-
const { actionsCommand } = await import("./actions-command");
|
|
98
|
-
return actionsCommand(argv.slice(1));
|
|
99
|
-
}
|
|
100
|
-
case "refine": {
|
|
101
|
-
const { refineCommand } = await import("./refine-command");
|
|
102
|
-
return refineCommand(argv.slice(1));
|
|
103
|
-
}
|
|
104
|
-
case "review":
|
|
105
|
-
return reviewEntry(argv.slice(1));
|
|
106
|
-
case "review-post": {
|
|
107
|
-
const { reviewPostCommand } = await import("./pr");
|
|
108
|
-
return reviewPostCommand(argv.slice(1));
|
|
109
|
-
}
|
|
110
|
-
case "-v":
|
|
111
|
-
case "--version":
|
|
112
|
-
case "version":
|
|
113
|
-
console.log(CLI_VERSION);
|
|
114
|
-
return 0;
|
|
115
|
-
case "-h":
|
|
116
|
-
case "--help":
|
|
117
|
-
case "help":
|
|
118
|
-
printHelp();
|
|
119
|
-
return 0;
|
|
120
|
-
case undefined:
|
|
121
|
-
return runTui();
|
|
122
|
-
default: {
|
|
123
|
-
// `cueloop <session-id>` opens the TUI on that session
|
|
124
|
-
if (cmd.startsWith("ses_")) return runTui(cmd);
|
|
125
|
-
printHelp();
|
|
126
|
-
return 2;
|
|
127
|
-
}
|
|
105
|
+
if (cmd === undefined) return runTui();
|
|
106
|
+
const handler = commandHandlers[cmd];
|
|
107
|
+
if (handler !== undefined) return handler(argv.slice(1));
|
|
108
|
+
if (versionAliases.has(cmd)) {
|
|
109
|
+
console.log(CLI_VERSION);
|
|
110
|
+
return 0;
|
|
111
|
+
}
|
|
112
|
+
if (helpAliases.has(cmd)) {
|
|
113
|
+
printHelp();
|
|
114
|
+
return 0;
|
|
128
115
|
}
|
|
116
|
+
if (cmd.startsWith("ses_")) return runTui(cmd);
|
|
117
|
+
printHelp();
|
|
118
|
+
return 2;
|
|
129
119
|
}
|
|
130
120
|
|
|
131
121
|
/**
|
package/src/session-commands.ts
CHANGED
|
@@ -19,6 +19,126 @@ function out(value: unknown): void {
|
|
|
19
19
|
console.log(JSON.stringify(value, null, 2));
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
type SessionFlags = Record<string, string | boolean>;
|
|
23
|
+
type SessionContext = { client: DaemonClient; positional: string[]; flags: SessionFlags };
|
|
24
|
+
type SessionVerbHandler = (context: SessionContext) => Promise<number>;
|
|
25
|
+
|
|
26
|
+
async function sessionCreate({ client, flags }: SessionContext): Promise<number> {
|
|
27
|
+
const contentFile = stringFlag(flags, "content-file");
|
|
28
|
+
const content = contentFile ? await Bun.file(contentFile).text() : await readStdin();
|
|
29
|
+
// per-file agent notes for diff sessions: a JSON array of { path, body }
|
|
30
|
+
const notesFile = stringFlag(flags, "notes-file");
|
|
31
|
+
const notes = notesFile
|
|
32
|
+
? (JSON.parse(await Bun.file(notesFile).text()) as ReviewNote[])
|
|
33
|
+
: undefined;
|
|
34
|
+
const review = await openReview(client, {
|
|
35
|
+
type: (stringFlag(flags, "type") ?? "plan") as ArtifactType,
|
|
36
|
+
content,
|
|
37
|
+
cwd: stringFlag(flags, "cwd"),
|
|
38
|
+
agent: stringFlag(flags, "agent"),
|
|
39
|
+
agentSessionId: stringFlag(flags, "agent-session-id"),
|
|
40
|
+
planPath: stringFlag(flags, "plan-path"),
|
|
41
|
+
prototypePath: stringFlag(flags, "prototype-path"),
|
|
42
|
+
title: stringFlag(flags, "title"),
|
|
43
|
+
notes,
|
|
44
|
+
});
|
|
45
|
+
// herdr auto-open: render the review in a tab (no-op outside herdr).
|
|
46
|
+
await openHerdrPaneForReview(review.session, client);
|
|
47
|
+
out(review.session);
|
|
48
|
+
return 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function sessionGetCommand({ client, positional }: SessionContext): Promise<number> {
|
|
52
|
+
out(await client.sessionGet(required(positional[1], "session id")));
|
|
53
|
+
return 0;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function sessionListCommand({ client, flags }: SessionContext): Promise<number> {
|
|
57
|
+
const status = stringFlag(flags, "status") as "pending" | "resolved" | undefined;
|
|
58
|
+
out(await client.sessionList(status ? { status } : undefined));
|
|
59
|
+
return 0;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function sessionWaitCommand({ client, positional, flags }: SessionContext): Promise<number> {
|
|
63
|
+
const id = required(positional[1], "session id");
|
|
64
|
+
const timeoutMs = Number(stringFlag(flags, "timeout-ms") ?? "60000");
|
|
65
|
+
const session = await client.sessionWait(id, timeoutMs);
|
|
66
|
+
if (session === null) {
|
|
67
|
+
out({ status: "pending" });
|
|
68
|
+
return 0;
|
|
69
|
+
}
|
|
70
|
+
const { allow, feedback } = verdictResponse(session);
|
|
71
|
+
out({ status: "resolved", allow, verdict: session.verdict!.kind, feedback });
|
|
72
|
+
return 0;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function sessionAnnotateCommand({
|
|
76
|
+
client,
|
|
77
|
+
positional,
|
|
78
|
+
flags,
|
|
79
|
+
}: SessionContext): Promise<number> {
|
|
80
|
+
const id = required(positional[1], "session id");
|
|
81
|
+
const quote = required(stringFlag(flags, "quote"), "--quote");
|
|
82
|
+
const author = stringFlag(flags, "author");
|
|
83
|
+
const body = await annotateBody(flags, client, id);
|
|
84
|
+
out(
|
|
85
|
+
await client.sessionAnnotate(
|
|
86
|
+
id,
|
|
87
|
+
{
|
|
88
|
+
id: stringFlag(flags, "annotation-id") ?? newAnnotationId(),
|
|
89
|
+
kind: stringFlag(flags, "kind") ?? "comment",
|
|
90
|
+
anchor: {
|
|
91
|
+
quote,
|
|
92
|
+
prefix: stringFlag(flags, "prefix") ?? "",
|
|
93
|
+
suffix: stringFlag(flags, "suffix") ?? "",
|
|
94
|
+
},
|
|
95
|
+
body,
|
|
96
|
+
...(author ? { author } : {}),
|
|
97
|
+
},
|
|
98
|
+
stringFlag(flags, "author-name"),
|
|
99
|
+
),
|
|
100
|
+
);
|
|
101
|
+
return 0;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function sessionResolveCommand({
|
|
105
|
+
client,
|
|
106
|
+
positional,
|
|
107
|
+
flags,
|
|
108
|
+
}: SessionContext): Promise<number> {
|
|
109
|
+
const id = required(positional[1], "session id");
|
|
110
|
+
const kind = required(stringFlag(flags, "verdict"), "--verdict") as VerdictKind;
|
|
111
|
+
out(await client.sessionResolve(id, kind, stringFlag(flags, "summary") ?? ""));
|
|
112
|
+
return 0;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async function sessionSubmitRevisionCommand({
|
|
116
|
+
client,
|
|
117
|
+
positional,
|
|
118
|
+
flags,
|
|
119
|
+
}: SessionContext): Promise<number> {
|
|
120
|
+
const id = required(positional[1], "session id");
|
|
121
|
+
const contentFile = stringFlag(flags, "content-file");
|
|
122
|
+
const content = contentFile ? await Bun.file(contentFile).text() : await readStdin();
|
|
123
|
+
// --addressed a_1,a_2 - the annotation ids this revision acted on
|
|
124
|
+
const addressed = (stringFlag(flags, "addressed") ?? "")
|
|
125
|
+
.split(",")
|
|
126
|
+
.map((annotationId) => annotationId.trim())
|
|
127
|
+
.filter(Boolean);
|
|
128
|
+
out(await client.sessionSubmitRevision(id, content, addressed));
|
|
129
|
+
return 0;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const sessionVerbHandlers: Record<string, SessionVerbHandler> = {
|
|
133
|
+
create: sessionCreate,
|
|
134
|
+
get: sessionGetCommand,
|
|
135
|
+
list: sessionListCommand,
|
|
136
|
+
wait: sessionWaitCommand,
|
|
137
|
+
annotate: sessionAnnotateCommand,
|
|
138
|
+
resolve: sessionResolveCommand,
|
|
139
|
+
"submit-revision": sessionSubmitRevisionCommand,
|
|
140
|
+
};
|
|
141
|
+
|
|
22
142
|
export async function sessionCommand(argv: string[]): Promise<number> {
|
|
23
143
|
const { positional, flags } = parseArgs(argv);
|
|
24
144
|
const verb = positional[0];
|
|
@@ -27,100 +147,14 @@ export async function sessionCommand(argv: string[]): Promise<number> {
|
|
|
27
147
|
const role = stringFlag(flags, "role") === "agent" ? "agent" : undefined;
|
|
28
148
|
const client = await DaemonClient.connect({ autostart: true, role });
|
|
29
149
|
try {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const notes = notesFile
|
|
37
|
-
? (JSON.parse(await Bun.file(notesFile).text()) as ReviewNote[])
|
|
38
|
-
: undefined;
|
|
39
|
-
const review = await openReview(client, {
|
|
40
|
-
type: (stringFlag(flags, "type") ?? "plan") as ArtifactType,
|
|
41
|
-
content,
|
|
42
|
-
cwd: stringFlag(flags, "cwd"),
|
|
43
|
-
agent: stringFlag(flags, "agent"),
|
|
44
|
-
agentSessionId: stringFlag(flags, "agent-session-id"),
|
|
45
|
-
planPath: stringFlag(flags, "plan-path"),
|
|
46
|
-
prototypePath: stringFlag(flags, "prototype-path"),
|
|
47
|
-
title: stringFlag(flags, "title"),
|
|
48
|
-
notes,
|
|
49
|
-
});
|
|
50
|
-
// herdr auto-open: render the review in a tab (no-op outside herdr).
|
|
51
|
-
await openHerdrPaneForReview(review.session, client);
|
|
52
|
-
out(review.session);
|
|
53
|
-
return 0;
|
|
54
|
-
}
|
|
55
|
-
case "get": {
|
|
56
|
-
out(await client.sessionGet(required(positional[1], "session id")));
|
|
57
|
-
return 0;
|
|
58
|
-
}
|
|
59
|
-
case "list": {
|
|
60
|
-
const status = stringFlag(flags, "status") as "pending" | "resolved" | undefined;
|
|
61
|
-
out(await client.sessionList(status ? { status } : undefined));
|
|
62
|
-
return 0;
|
|
63
|
-
}
|
|
64
|
-
case "wait": {
|
|
65
|
-
const id = required(positional[1], "session id");
|
|
66
|
-
const timeoutMs = Number(stringFlag(flags, "timeout-ms") ?? "60000");
|
|
67
|
-
const session = await client.sessionWait(id, timeoutMs);
|
|
68
|
-
if (session === null) {
|
|
69
|
-
out({ status: "pending" });
|
|
70
|
-
return 0;
|
|
71
|
-
}
|
|
72
|
-
const { allow, feedback } = verdictResponse(session);
|
|
73
|
-
out({ status: "resolved", allow, verdict: session.verdict!.kind, feedback });
|
|
74
|
-
return 0;
|
|
75
|
-
}
|
|
76
|
-
case "annotate": {
|
|
77
|
-
const id = required(positional[1], "session id");
|
|
78
|
-
const quote = required(stringFlag(flags, "quote"), "--quote");
|
|
79
|
-
const author = stringFlag(flags, "author");
|
|
80
|
-
const body = await annotateBody(flags, client, id);
|
|
81
|
-
out(
|
|
82
|
-
await client.sessionAnnotate(
|
|
83
|
-
id,
|
|
84
|
-
{
|
|
85
|
-
id: stringFlag(flags, "annotation-id") ?? newAnnotationId(),
|
|
86
|
-
kind: stringFlag(flags, "kind") ?? "comment",
|
|
87
|
-
anchor: {
|
|
88
|
-
quote,
|
|
89
|
-
prefix: stringFlag(flags, "prefix") ?? "",
|
|
90
|
-
suffix: stringFlag(flags, "suffix") ?? "",
|
|
91
|
-
},
|
|
92
|
-
body,
|
|
93
|
-
...(author ? { author } : {}),
|
|
94
|
-
},
|
|
95
|
-
stringFlag(flags, "author-name"),
|
|
96
|
-
),
|
|
97
|
-
);
|
|
98
|
-
return 0;
|
|
99
|
-
}
|
|
100
|
-
case "resolve": {
|
|
101
|
-
const id = required(positional[1], "session id");
|
|
102
|
-
const kind = required(stringFlag(flags, "verdict"), "--verdict") as VerdictKind;
|
|
103
|
-
out(await client.sessionResolve(id, kind, stringFlag(flags, "summary") ?? ""));
|
|
104
|
-
return 0;
|
|
105
|
-
}
|
|
106
|
-
case "submit-revision": {
|
|
107
|
-
const id = required(positional[1], "session id");
|
|
108
|
-
const contentFile = stringFlag(flags, "content-file");
|
|
109
|
-
const content = contentFile ? await Bun.file(contentFile).text() : await readStdin();
|
|
110
|
-
// --addressed a_1,a_2 - the annotation ids this revision acted on
|
|
111
|
-
const addressed = (stringFlag(flags, "addressed") ?? "")
|
|
112
|
-
.split(",")
|
|
113
|
-
.map((annotationId) => annotationId.trim())
|
|
114
|
-
.filter(Boolean);
|
|
115
|
-
out(await client.sessionSubmitRevision(id, content, addressed));
|
|
116
|
-
return 0;
|
|
117
|
-
}
|
|
118
|
-
default:
|
|
119
|
-
console.error(
|
|
120
|
-
"usage: cueloop session <create|get|list|wait|annotate|resolve|submit-revision> [flags]",
|
|
121
|
-
);
|
|
122
|
-
return 2;
|
|
150
|
+
const handler = verb !== undefined ? sessionVerbHandlers[verb] : undefined;
|
|
151
|
+
if (handler === undefined) {
|
|
152
|
+
console.error(
|
|
153
|
+
"usage: cueloop session <create|get|list|wait|annotate|resolve|submit-revision> [flags]",
|
|
154
|
+
);
|
|
155
|
+
return 2;
|
|
123
156
|
}
|
|
157
|
+
return await handler({ client, positional, flags });
|
|
124
158
|
} finally {
|
|
125
159
|
client.close();
|
|
126
160
|
}
|