@linzumi/cli 0.0.1-beta → 0.0.2-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.
@@ -0,0 +1,211 @@
1
+ /*
2
+ - Date: 2026-04-24
3
+ Spec: plans/2026-04-24-local-codex-runner-plan.md
4
+ Relationship: Defines the spec's first local runner protocol types for
5
+ Phoenix frames, Codex JSON-RPC envelopes, runner events, and controls.
6
+
7
+ - Date: 2026-04-24
8
+ Spec: plans/2026-04-24-local-codex-channel-thread-binding-spec.md
9
+ Relationship: Defines the channel-bound runner options used to bind one
10
+ local Codex process to a Kandan channel/thread and listener selector.
11
+
12
+ - Date: 2026-04-25
13
+ Spec: plans/2026-04-24-local-codex-runner-deep-quality-spec.md
14
+ Relationship: Defines the typed runner control that carries a Kandan
15
+ Approve/Deny decision back to the local Codex app-server approval request
16
+ without exposing a browser-to-runner socket.
17
+ */
18
+ export type JsonValue =
19
+ | null
20
+ | boolean
21
+ | number
22
+ | string
23
+ | JsonValue[]
24
+ | { readonly [key: string]: JsonValue };
25
+
26
+ export type JsonObject = { readonly [key: string]: JsonValue };
27
+
28
+ export type JsonRpcId = string | number;
29
+
30
+ export type JsonRpcRequest = {
31
+ readonly jsonrpc: "2.0";
32
+ readonly id: JsonRpcId;
33
+ readonly method: string;
34
+ readonly params?: JsonObject;
35
+ };
36
+
37
+ export type JsonRpcNotification = {
38
+ readonly jsonrpc: "2.0";
39
+ readonly method: string;
40
+ readonly params?: JsonObject;
41
+ };
42
+
43
+ export type JsonRpcResponse =
44
+ | {
45
+ readonly jsonrpc: "2.0";
46
+ readonly id: JsonRpcId;
47
+ readonly result: JsonValue;
48
+ }
49
+ | {
50
+ readonly jsonrpc: "2.0";
51
+ readonly id: JsonRpcId;
52
+ readonly error: {
53
+ readonly code: number;
54
+ readonly message: string;
55
+ readonly data?: JsonValue;
56
+ };
57
+ };
58
+
59
+ export type JsonRpcMessage = JsonRpcRequest | JsonRpcNotification | JsonRpcResponse;
60
+
61
+ export type PhoenixFrame = readonly [
62
+ joinRef: string | null,
63
+ ref: string | null,
64
+ topic: string,
65
+ event: string,
66
+ payload: JsonValue
67
+ ];
68
+
69
+ export type RunnerToKandanEvent =
70
+ | {
71
+ readonly kind: "instance_started";
72
+ readonly payload: JsonObject;
73
+ }
74
+ | {
75
+ readonly kind: "instance_stopped";
76
+ readonly payload: JsonObject;
77
+ }
78
+ | {
79
+ readonly kind: "codex_notification";
80
+ readonly payload: {
81
+ readonly instanceId: string;
82
+ readonly seq: number;
83
+ readonly method: string;
84
+ readonly params: JsonObject;
85
+ readonly receivedAt: string;
86
+ };
87
+ }
88
+ | {
89
+ readonly kind: "codex_request";
90
+ readonly payload: JsonObject;
91
+ }
92
+ | {
93
+ readonly kind: "codex_response";
94
+ readonly payload: JsonObject;
95
+ }
96
+ | {
97
+ readonly kind: "codex_error";
98
+ readonly payload: JsonObject;
99
+ }
100
+ | {
101
+ readonly kind: "heartbeat";
102
+ readonly payload: JsonObject;
103
+ };
104
+
105
+ export type KandanControl =
106
+ | {
107
+ readonly type: "start_instance";
108
+ readonly instanceId?: string;
109
+ readonly cwd?: string;
110
+ readonly launchTui?: boolean;
111
+ }
112
+ | {
113
+ readonly type: "stop_instance";
114
+ readonly instanceId: string;
115
+ }
116
+ | {
117
+ readonly type: "kill_instance";
118
+ readonly instanceId: string;
119
+ }
120
+ | {
121
+ readonly type: "start_turn";
122
+ readonly instanceId: string;
123
+ readonly threadId: string;
124
+ readonly input: JsonValue[];
125
+ }
126
+ | {
127
+ readonly type: "steer_turn";
128
+ readonly instanceId: string;
129
+ readonly threadId: string;
130
+ readonly turnId: string;
131
+ readonly input: JsonValue[];
132
+ }
133
+ | {
134
+ readonly type: "interrupt_turn";
135
+ readonly instanceId: string;
136
+ readonly threadId: string;
137
+ readonly turnId?: string;
138
+ }
139
+ | {
140
+ readonly type: "interrupt_queued_messages";
141
+ readonly instanceId: string;
142
+ readonly threadId: string;
143
+ readonly throughSeq?: number;
144
+ }
145
+ | {
146
+ readonly type: "resolve_codex_approval_request";
147
+ readonly instanceId: string;
148
+ readonly threadId: string;
149
+ readonly sourceSeq: number;
150
+ readonly requestId: string;
151
+ readonly decision: "approve" | "deny";
152
+ }
153
+ | {
154
+ readonly type: "read_thread";
155
+ readonly instanceId: string;
156
+ readonly threadId: string;
157
+ readonly includeTurns?: boolean;
158
+ };
159
+
160
+ export type KandanChannelSessionOptions = {
161
+ readonly workspaceSlug: string;
162
+ readonly channelSlug: string;
163
+ readonly kandanThreadId: string | undefined;
164
+ readonly listenUser: string;
165
+ readonly model?: string | undefined;
166
+ readonly reasoningEffort?: string | undefined;
167
+ readonly sandbox?: string | undefined;
168
+ readonly approvalPolicy?: string | undefined;
169
+ };
170
+
171
+ export function parseJsonObject(text: string): JsonObject {
172
+ const parsed = JSON.parse(text);
173
+
174
+ if (isJsonObject(parsed)) {
175
+ return parsed;
176
+ }
177
+
178
+ throw new Error("expected JSON object");
179
+ }
180
+
181
+ export function isJsonObject(value: unknown): value is JsonObject {
182
+ return typeof value === "object" && value !== null && !Array.isArray(value);
183
+ }
184
+
185
+ export function requireString(value: unknown, label: string): string {
186
+ if (typeof value === "string" && value.trim() !== "") {
187
+ return value.trim();
188
+ }
189
+
190
+ throw new Error(`${label} must be a non-empty string`);
191
+ }
192
+
193
+ export function requirePositiveInteger(value: unknown, label: string): number {
194
+ if (Number.isInteger(value) && typeof value === "number" && value > 0) {
195
+ return value;
196
+ }
197
+
198
+ throw new Error(`${label} must be a positive integer`);
199
+ }
200
+
201
+ export function extractCodexIds(params: JsonObject): JsonObject {
202
+ const entries = [
203
+ ["threadId", params.threadId],
204
+ ["turnId", params.turnId],
205
+ ["itemId", params.itemId],
206
+ ["processId", params.processId],
207
+ ["requestId", params.requestId]
208
+ ].filter((entry): entry is [string, JsonValue] => entry[1] !== undefined);
209
+
210
+ return Object.fromEntries(entries) as JsonObject;
211
+ }