@linzumi/cli 0.0.2-beta → 0.0.4-beta

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/README.md CHANGED
@@ -1,19 +1,19 @@
1
1
  # @linzumi/cli
2
2
 
3
3
  ```text
4
- . * . . *
5
- * _.-"""-._ .
6
- .-' _ _ '-. *
7
- / (o)_(o) \
8
- | .-. | mouse in the grove
9
- | .-( )-. | listening for Kandan
10
- \ / '-' \ /
11
- '-.\__ __/.-'
12
- /| |\
13
- _/ | | | \_
14
- .' |__|__| '.
15
- / . /___\ . \
16
- '._.' /_____\ '._.'
4
+ ___ ___
5
+ .-' '-. .-' '-.
6
+ .' ' '.
7
+ / _ _ \
8
+ | .' '. .' '. |
9
+ | / \ .-. / \ |
10
+ \ \ (o o) / /
11
+ '._'._ \_/ _.'_.'
12
+ | | |
13
+ | /|\ |
14
+ |___/ | \___|
15
+ \_|_/
16
+ / \
17
17
  ```
18
18
 
19
19
  Linzumi's CLI package. It installs the `linzumi` executable.
@@ -27,7 +27,7 @@ runner. It wraps the same local runner that was previously started with
27
27
  Install the exact beta version:
28
28
 
29
29
  ```bash
30
- npm install -g @linzumi/cli@0.0.2-beta
30
+ npm install -g @linzumi/cli@0.0.4-beta
31
31
  ```
32
32
 
33
33
  Or install the current beta tag:
@@ -47,50 +47,43 @@ linzumi --version
47
47
  Expected CLI output:
48
48
 
49
49
  ```bash
50
- linzumi 0.0.2-beta
50
+ linzumi 0.0.4-beta
51
51
  ```
52
52
 
53
- ## Equivalent Command
53
+ ## Prod Quick Start
54
54
 
55
- The old local runner command:
55
+ For the Linzumi workspace in prod, this is the first command to try:
56
56
 
57
57
  ```bash
58
- bun run start -- \
58
+ npm install -g @linzumi/cli@0.0.4-beta
59
+
60
+ linzumi connect \
59
61
  --kandan-url wss://serve.kandanai.com \
60
62
  --workspace linzumi \
61
63
  --channel seans-playground \
62
- --kandan-thread-id 6d454179-8c6c-45c6-a447-06f01cb6fa71 \
63
- --listen-user sean \
64
- --cwd /Users/seans/code/linzumi-main \
65
64
  --codex-bin codex \
66
- --model gpt-5.5 \
67
- --reasoning-effort low \
68
- --fast \
69
- --launch-tui \
70
- --oauth-callback-host 100.71.192.98
65
+ --launch-tui
71
66
  ```
72
67
 
73
- is now:
68
+ The runner handles OAuth itself. If the cached Kandan token is missing or
69
+ rejected, it opens the OAuth flow and saves the refreshed auth cache.
70
+ By default it listens for replies from the authenticated Kandan user.
71
+
72
+ For a more explicit launch that pins the Codex model, reasoning effort, and
73
+ fast service tier:
74
74
 
75
75
  ```bash
76
76
  linzumi connect \
77
77
  --kandan-url wss://serve.kandanai.com \
78
78
  --workspace linzumi \
79
79
  --channel seans-playground \
80
- --kandan-thread-id 6d454179-8c6c-45c6-a447-06f01cb6fa71 \
81
- --listen-user sean \
82
- --cwd /Users/seans/code/linzumi-main \
83
80
  --codex-bin codex \
84
81
  --model gpt-5.5 \
85
82
  --reasoning-effort low \
86
83
  --fast \
87
- --launch-tui \
88
- --oauth-callback-host 100.71.192.98
84
+ --launch-tui
89
85
  ```
90
86
 
91
- The runner handles OAuth itself. If the cached Kandan token is missing or
92
- rejected, it opens the OAuth flow and saves the refreshed auth cache.
93
-
94
87
  ## Basic Use
95
88
 
96
89
  Running `linzumi` without arguments prints the local runner guide:
@@ -106,8 +99,6 @@ linzumi connect \
106
99
  --kandan-url wss://serve.kandanai.com \
107
100
  --workspace linzumi \
108
101
  --channel seans-playground \
109
- --listen-user sean \
110
- --cwd /Users/seans/code/linzumi-main \
111
102
  --codex-bin codex \
112
103
  --launch-tui
113
104
  ```
@@ -137,7 +128,7 @@ linzumi auth [auth options]
137
128
  --workspace <slug> Workspace slug, for example linzumi
138
129
  --channel <slug|w/c> Channel slug, or workspace/channel
139
130
  --kandan-thread-id <uuid> Resume an existing Kandan thread
140
- --listen-user <user|all> User whose replies are accepted, or all
131
+ --listen-user <user|all> User whose replies are accepted, default authenticated user
141
132
  --cwd <path> Working directory for Codex
142
133
  --codex-bin <path> Codex executable, default codex
143
134
  --model <name> Codex model
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linzumi/cli",
3
- "version": "0.0.2-beta",
3
+ "version": "0.0.4-beta",
4
4
  "description": "Linzumi local Codex runner CLI.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -139,18 +139,18 @@ export function senderAllowed(
139
139
  event: Pick<KandanChatEvent, "actorSlug" | "actorUserId">,
140
140
  runnerIdentity: RunnerIdentity,
141
141
  ): boolean {
142
- const normalized = listenUser.trim();
142
+ const normalized = listenUser.trim().toLocaleLowerCase();
143
143
 
144
144
  if (normalized === "all") {
145
145
  return true;
146
146
  }
147
147
 
148
- if (event.actorSlug !== undefined && event.actorSlug === normalized) {
148
+ if (event.actorSlug !== undefined && event.actorSlug.toLocaleLowerCase() === normalized) {
149
149
  return true;
150
150
  }
151
151
 
152
152
  return (
153
- runnerIdentity.actorUsername === normalized &&
153
+ runnerIdentity.actorUsername?.toLocaleLowerCase() === normalized &&
154
154
  runnerIdentity.actorUserId !== undefined &&
155
155
  event.actorUserId === runnerIdentity.actorUserId
156
156
  );
package/src/index.ts CHANGED
@@ -18,6 +18,7 @@ import { randomUUID } from "node:crypto";
18
18
  import { runLocalCodexRunner, type RunnerOptions } from "./runner";
19
19
  import { writeCachedLocalRunnerToken } from "./authCache";
20
20
  import { resolveLocalRunnerToken } from "./authResolution";
21
+ import { identityFromAccessToken } from "./channelSessionSupport";
21
22
  import { acquireLocalRunnerTokenDetails } from "./oauth";
22
23
 
23
24
  type FlagDefinition = {
@@ -48,11 +49,13 @@ const flagDefinitions = new Map<string, FlagDefinition>([
48
49
  ["help", { kind: "boolean" }],
49
50
  ]);
50
51
 
51
- try {
52
- await main(process.argv.slice(2));
53
- } catch (error) {
54
- process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
55
- process.exit(1);
52
+ if (import.meta.main) {
53
+ try {
54
+ await main(process.argv.slice(2));
55
+ } catch (error) {
56
+ process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
57
+ process.exit(1);
58
+ }
56
59
  }
57
60
 
58
61
  async function main(args: readonly string[]): Promise<void> {
@@ -63,7 +66,7 @@ async function main(args: readonly string[]): Promise<void> {
63
66
  process.stdout.write(connectGuideText());
64
67
  return;
65
68
  case "version":
66
- process.stdout.write("linzumi 0.0.2-beta\n");
69
+ process.stdout.write("linzumi 0.0.4-beta\n");
67
70
  return;
68
71
  case "auth":
69
72
  await runAuthCommand(parsed.args);
@@ -132,7 +135,7 @@ async function runAuthCommand(args: readonly string[]): Promise<void> {
132
135
  process.stdout.write(`Saved Kandan local runner auth for ${cached.kandanBaseUrl}\n`);
133
136
  }
134
137
 
135
- async function parseRunnerArgs(args: readonly string[]): Promise<RunnerOptions> {
138
+ export async function parseRunnerArgs(args: readonly string[]): Promise<RunnerOptions> {
136
139
  const values = strictFlagValues(args);
137
140
 
138
141
  if (values.get("help") === true) {
@@ -141,24 +144,25 @@ async function parseRunnerArgs(args: readonly string[]): Promise<RunnerOptions>
141
144
  }
142
145
 
143
146
  if (values.get("version") === true) {
144
- process.stdout.write("linzumi 0.0.2-beta\n");
147
+ process.stdout.write("linzumi 0.0.4-beta\n");
145
148
  process.exit(0);
146
149
  }
147
150
 
148
- const channelSession = parseChannelSession(values);
151
+ const channelTarget = parseChannelSessionTarget(values);
149
152
  const kandanUrl = required(values, "kandan-url");
150
153
  const explicitToken = stringValue(values, "token");
151
154
  const token = await resolveLocalRunnerToken({
152
155
  kandanUrl,
153
156
  explicitToken,
154
- workspaceSlug: channelSession?.workspaceSlug,
155
- channelSlug: channelSession?.channelSlug,
157
+ workspaceSlug: channelTarget?.workspaceSlug,
158
+ channelSlug: channelTarget?.channelSlug,
156
159
  authFilePath: stringValue(values, "auth-file"),
157
160
  callbackHost: stringValue(values, "oauth-callback-host"),
158
161
  reportRejectedCachedToken: () => {
159
162
  process.stderr.write("Cached Kandan local runner auth was rejected; starting OAuth.\n");
160
163
  },
161
164
  });
165
+ const channelSession = parseChannelSession(values, token, channelTarget);
162
166
 
163
167
  return {
164
168
  kandanUrl,
@@ -210,15 +214,16 @@ function strictFlagValues(args: readonly string[]): Map<string, string | true> {
210
214
  }
211
215
 
212
216
  function parseChannelSession(
213
- values: Map<string, string | true>
217
+ values: Map<string, string | true>,
218
+ token: string,
219
+ target: { readonly workspaceSlug: string; readonly channelSlug: string } | undefined,
214
220
  ): RunnerOptions["channelSession"] {
215
- const target = parseOptionalChannelTarget(values);
216
-
217
221
  if (target === undefined) {
218
222
  return undefined;
219
223
  }
220
224
 
221
- const listenUser = required(values, "listen-user");
225
+ const listenUser =
226
+ stringValue(values, "listen-user") ?? defaultListenUserFromToken(token);
222
227
 
223
228
  return {
224
229
  workspaceSlug: target.workspaceSlug,
@@ -232,6 +237,22 @@ function parseChannelSession(
232
237
  };
233
238
  }
234
239
 
240
+ function parseChannelSessionTarget(
241
+ values: Map<string, string | true>
242
+ ): { readonly workspaceSlug: string; readonly channelSlug: string } | undefined {
243
+ return parseOptionalChannelTarget(values);
244
+ }
245
+
246
+ function defaultListenUserFromToken(token: string): string {
247
+ const username = identityFromAccessToken(token).actorUsername;
248
+
249
+ if (username !== undefined) {
250
+ return username;
251
+ }
252
+
253
+ throw new Error("missing --listen-user and authenticated user is unavailable");
254
+ }
255
+
235
256
  function parseOptionalChannelTarget(
236
257
  values: Map<string, string | true>
237
258
  ): { readonly workspaceSlug: string; readonly channelSlug: string } | undefined {
@@ -297,7 +318,7 @@ function helpText(): string {
297
318
 
298
319
  Usage:
299
320
  linzumi
300
- linzumi connect --kandan-url <ws-url> --workspace <slug> --channel <slug> --listen-user <username|all> [options]
321
+ linzumi connect --kandan-url <ws-url> --workspace <slug> --channel <slug> [options]
301
322
  linzumi auth --kandan-url <ws-url> [--workspace <slug> --channel <slug>]
302
323
 
303
324
  Required:
@@ -310,7 +331,7 @@ Channel binding:
310
331
  --workspace <slug> Workspace slug
311
332
  --channel <slug|w/c> Channel slug, or workspace/channel
312
333
  --kandan-thread-id <uuid> Resume an existing Kandan thread instead of announcing a new root
313
- --listen-user <user|all> User whose replies are accepted, or all
334
+ --listen-user <user|all> User whose replies are accepted, default authenticated user
314
335
 
315
336
  Codex:
316
337
  --cwd <path> Working directory for Codex, default current directory
@@ -326,15 +347,17 @@ Codex:
326
347
 
327
348
  Examples:
328
349
  Good:
350
+ linzumi connect --kandan-url wss://serve.kandanai.com --workspace linzumi --channel seans-playground --codex-bin codex --launch-tui
351
+ linzumi connect --kandan-url wss://serve.kandanai.com --workspace linzumi --channel seans-playground --codex-bin codex --model gpt-5.5 --reasoning-effort low --fast --launch-tui
329
352
  linzumi auth --kandan-url ws://127.0.0.1:4160 --workspace default --channel seans-playground
330
353
  linzumi auth --kandan-url ws://100.71.192.98:4160 --oauth-callback-host 100.71.192.98 --workspace default --channel seans-playground
331
- linzumi connect --kandan-url ws://127.0.0.1:4160 --token "$TOKEN" --workspace default --channel seans-playground --listen-user sean --cwd /tmp/kandan-runner-a
332
- linzumi connect --kandan-url ws://127.0.0.1:4160 --workspace default --channel seans-playground --listen-user sean
354
+ linzumi connect --kandan-url ws://127.0.0.1:4160 --token "$TOKEN" --workspace default --channel seans-playground --cwd /tmp/kandan-runner-a
355
+ linzumi connect --kandan-url ws://127.0.0.1:4160 --workspace default --channel seans-playground
333
356
  linzumi connect --kandan-url ws://127.0.0.1:4160 --token "$TOKEN" --channel default/seans-playground --listen-user all --launch-tui
334
357
 
335
358
  Bad:
336
- linzumi connect --kandan-url ws://127.0.0.1:4160 --workspace default --channel seans-playground
337
- Missing --listen-user.
359
+ linzumi connect --kandan-url ws://127.0.0.1:4160 --token not-a-jwt --workspace default --channel seans-playground
360
+ Missing --listen-user and authenticated user is unavailable.
338
361
  linzumi connect --kandan-url ws://127.0.0.1:4160 --token "$TOKEN" --listen-users sean
339
362
  Invalid flag: use --listen-user.
340
363
  `;
@@ -346,7 +369,7 @@ function connectGuideText(): string {
346
369
  Connect this computer to Kandan as a local Codex runner.
347
370
 
348
371
  Use:
349
- linzumi connect --kandan-url <ws-url> --workspace <slug> --channel <slug> --listen-user <username|all> [options]
372
+ linzumi connect --kandan-url <ws-url> --workspace <slug> --channel <slug> [options]
350
373
 
351
374
  For help:
352
375
  linzumi connect --help