@linzumi/cli 0.0.20-beta → 0.0.22-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 +65 -62
- package/bin/linzumi.js +10 -18
- package/dist/assets/linzumi-logo.svg +1 -0
- package/dist/index.js +9135 -0
- package/package.json +9 -4
- package/src/agentBootstrap.ts +0 -872
- package/src/authCache.ts +0 -157
- package/src/authResolution.ts +0 -77
- package/src/boundedCache.ts +0 -57
- package/src/channelSession.ts +0 -4301
- package/src/channelSessionSupport.ts +0 -308
- package/src/codexAppServer.ts +0 -380
- package/src/codexOutput.ts +0 -846
- package/src/codexRuntimeOptions.ts +0 -80
- package/src/dependencyStatus.ts +0 -198
- package/src/forwardTunnel.ts +0 -859
- package/src/forwardTunnelProtocol.ts +0 -324
- package/src/index.ts +0 -1080
- package/src/json.ts +0 -49
- package/src/kandanQueue.ts +0 -113
- package/src/kandanTls.ts +0 -86
- package/src/localCapabilities.ts +0 -143
- package/src/localCodexMessageState.ts +0 -135
- package/src/localCodexTurnState.ts +0 -108
- package/src/localConfig.ts +0 -99
- package/src/localEditor.ts +0 -1061
- package/src/localEditorRuntime.ts +0 -717
- package/src/localForwarding.ts +0 -523
- package/src/oauth.ts +0 -425
- package/src/pendingKandanMessageQueue.ts +0 -109
- package/src/phoenix.ts +0 -359
- package/src/portForwardApproval.ts +0 -181
- package/src/portForwardWatcher.ts +0 -404
- package/src/protocol.ts +0 -321
- package/src/runner.ts +0 -943
- package/src/runnerConsoleReporter.ts +0 -142
- package/src/runnerLogger.ts +0 -50
- package/src/streamDeltaCoalescing.ts +0 -129
- package/src/streamDeltaQueue.ts +0 -102
package/src/authCache.ts
DELETED
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
- Date: 2026-04-24
|
|
3
|
-
Spec: plans/2026-04-24-local-codex-channel-thread-binding-spec.md
|
|
4
|
-
Relationship: Stores scoped local-runner OAuth tokens so users can log in
|
|
5
|
-
once and then start local Codex runners without pasting tokens repeatedly.
|
|
6
|
-
*/
|
|
7
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
8
|
-
import { homedir } from "node:os";
|
|
9
|
-
import { dirname, join } from "node:path";
|
|
10
|
-
import { kandanHttpBaseUrl } from "./oauth";
|
|
11
|
-
|
|
12
|
-
export type CachedLocalRunnerToken = {
|
|
13
|
-
readonly accessToken: string;
|
|
14
|
-
readonly kandanBaseUrl: string;
|
|
15
|
-
readonly issuedAt: string;
|
|
16
|
-
readonly expiresAt?: string | undefined;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
type AuthFile = {
|
|
20
|
-
readonly version: 1;
|
|
21
|
-
readonly local_codex_runner?: Record<string, CachedTokenJson> | undefined;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
type CachedTokenJson = {
|
|
25
|
-
readonly access_token: string;
|
|
26
|
-
readonly issued_at: string;
|
|
27
|
-
readonly expires_at?: string | undefined;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
export function defaultAuthFilePath(): string {
|
|
31
|
-
const base = process.env.KANDAN_HOME ?? join(homedir(), ".kandan");
|
|
32
|
-
return join(base, "auth.json");
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function readCachedLocalRunnerToken(
|
|
36
|
-
kandanUrl: string,
|
|
37
|
-
authFilePath: string = defaultAuthFilePath(),
|
|
38
|
-
): CachedLocalRunnerToken | undefined {
|
|
39
|
-
if (!existsSync(authFilePath)) {
|
|
40
|
-
return undefined;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const authFile = parseAuthFile(readFileSync(authFilePath, "utf8"));
|
|
44
|
-
const kandanBaseUrl = kandanHttpBaseUrl(kandanUrl);
|
|
45
|
-
const entry = authFile.local_codex_runner?.[kandanBaseUrl];
|
|
46
|
-
|
|
47
|
-
if (entry === undefined || entry.access_token.trim() === "") {
|
|
48
|
-
return undefined;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (entry.expires_at !== undefined && Date.parse(entry.expires_at) <= Date.now()) {
|
|
52
|
-
return undefined;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return {
|
|
56
|
-
accessToken: entry.access_token,
|
|
57
|
-
kandanBaseUrl,
|
|
58
|
-
issuedAt: entry.issued_at,
|
|
59
|
-
expiresAt: entry.expires_at,
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export function writeCachedLocalRunnerToken(args: {
|
|
64
|
-
readonly kandanUrl: string;
|
|
65
|
-
readonly accessToken: string;
|
|
66
|
-
readonly expiresInSeconds?: number | undefined;
|
|
67
|
-
readonly authFilePath?: string | undefined;
|
|
68
|
-
}): CachedLocalRunnerToken {
|
|
69
|
-
const authFilePath = args.authFilePath ?? defaultAuthFilePath();
|
|
70
|
-
const existing = existsSync(authFilePath)
|
|
71
|
-
? parseAuthFile(readFileSync(authFilePath, "utf8"))
|
|
72
|
-
: { version: 1 as const };
|
|
73
|
-
const kandanBaseUrl = kandanHttpBaseUrl(args.kandanUrl);
|
|
74
|
-
const issuedAt = new Date();
|
|
75
|
-
const expiresAt =
|
|
76
|
-
args.expiresInSeconds === undefined
|
|
77
|
-
? undefined
|
|
78
|
-
: new Date(issuedAt.getTime() + args.expiresInSeconds * 1000).toISOString();
|
|
79
|
-
const next: AuthFile = {
|
|
80
|
-
...existing,
|
|
81
|
-
version: 1,
|
|
82
|
-
local_codex_runner: {
|
|
83
|
-
...(existing.local_codex_runner ?? {}),
|
|
84
|
-
[kandanBaseUrl]: {
|
|
85
|
-
access_token: args.accessToken,
|
|
86
|
-
issued_at: issuedAt.toISOString(),
|
|
87
|
-
expires_at: expiresAt,
|
|
88
|
-
},
|
|
89
|
-
},
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
mkdirSync(dirname(authFilePath), { recursive: true });
|
|
93
|
-
writeFileSync(authFilePath, `${JSON.stringify(next, null, 2)}\n`, "utf8");
|
|
94
|
-
|
|
95
|
-
return {
|
|
96
|
-
accessToken: args.accessToken,
|
|
97
|
-
kandanBaseUrl,
|
|
98
|
-
issuedAt: issuedAt.toISOString(),
|
|
99
|
-
expiresAt,
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function parseAuthFile(text: string): AuthFile {
|
|
104
|
-
const parsed: unknown = JSON.parse(text);
|
|
105
|
-
|
|
106
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
107
|
-
throw new Error("Kandan auth cache must contain a JSON object");
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const value = parsed as Record<string, unknown>;
|
|
111
|
-
const localRunner = value.local_codex_runner;
|
|
112
|
-
|
|
113
|
-
if (
|
|
114
|
-
localRunner !== undefined &&
|
|
115
|
-
(typeof localRunner !== "object" || localRunner === null || Array.isArray(localRunner))
|
|
116
|
-
) {
|
|
117
|
-
throw new Error("Kandan auth cache local_codex_runner must be an object");
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
return {
|
|
121
|
-
version: 1,
|
|
122
|
-
local_codex_runner: normalizeLocalRunnerCache(localRunner),
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
function normalizeLocalRunnerCache(value: unknown): Record<string, CachedTokenJson> | undefined {
|
|
127
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
128
|
-
return undefined;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
return Object.fromEntries(
|
|
132
|
-
Object.entries(value).flatMap(([key, entry]) => {
|
|
133
|
-
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
|
|
134
|
-
return [];
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const token = (entry as Record<string, unknown>).access_token;
|
|
138
|
-
const issuedAt = (entry as Record<string, unknown>).issued_at;
|
|
139
|
-
const expiresAt = (entry as Record<string, unknown>).expires_at;
|
|
140
|
-
|
|
141
|
-
if (typeof token !== "string" || typeof issuedAt !== "string") {
|
|
142
|
-
return [];
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
return [
|
|
146
|
-
[
|
|
147
|
-
key,
|
|
148
|
-
{
|
|
149
|
-
access_token: token,
|
|
150
|
-
issued_at: issuedAt,
|
|
151
|
-
expires_at: typeof expiresAt === "string" ? expiresAt : undefined,
|
|
152
|
-
},
|
|
153
|
-
],
|
|
154
|
-
];
|
|
155
|
-
}),
|
|
156
|
-
);
|
|
157
|
-
}
|
package/src/authResolution.ts
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
- Date: 2026-04-24
|
|
3
|
-
Spec: plans/2026-04-24-local-codex-channel-thread-binding-spec.md
|
|
4
|
-
Relationship: Implements the one-command runner auth behavior: use an
|
|
5
|
-
explicit token when supplied, validate cached scoped runner auth, and fall
|
|
6
|
-
back to the OAuth flow when cached auth is missing or rejected.
|
|
7
|
-
*/
|
|
8
|
-
import { readCachedLocalRunnerToken, writeCachedLocalRunnerToken } from "./authCache";
|
|
9
|
-
import { acquireLocalRunnerTokenDetails, validateLocalRunnerToken } from "./oauth";
|
|
10
|
-
|
|
11
|
-
export type LocalRunnerTokenResolutionArgs = {
|
|
12
|
-
readonly kandanUrl: string;
|
|
13
|
-
readonly explicitToken?: string | undefined;
|
|
14
|
-
readonly workspaceSlug?: string | undefined;
|
|
15
|
-
readonly channelSlug?: string | undefined;
|
|
16
|
-
readonly onboarding?: "start" | undefined;
|
|
17
|
-
readonly authFilePath?: string | undefined;
|
|
18
|
-
readonly callbackHost?: string | undefined;
|
|
19
|
-
readonly reportRejectedCachedToken?: (() => void) | undefined;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
type LocalRunnerTokenResolutionDeps = {
|
|
23
|
-
readonly readCachedToken: typeof readCachedLocalRunnerToken;
|
|
24
|
-
readonly validateToken: typeof validateLocalRunnerToken;
|
|
25
|
-
readonly acquireAndCacheToken: (args: LocalRunnerTokenResolutionArgs) => Promise<string>;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
export async function resolveLocalRunnerToken(
|
|
29
|
-
args: LocalRunnerTokenResolutionArgs,
|
|
30
|
-
deps: LocalRunnerTokenResolutionDeps = {
|
|
31
|
-
readCachedToken: readCachedLocalRunnerToken,
|
|
32
|
-
validateToken: validateLocalRunnerToken,
|
|
33
|
-
acquireAndCacheToken,
|
|
34
|
-
},
|
|
35
|
-
): Promise<string> {
|
|
36
|
-
if (args.explicitToken !== undefined) {
|
|
37
|
-
return args.explicitToken;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const cached = deps.readCachedToken(args.kandanUrl, args.authFilePath);
|
|
41
|
-
|
|
42
|
-
if (cached !== undefined) {
|
|
43
|
-
const cachedTokenIsUsable = await deps.validateToken({
|
|
44
|
-
kandanUrl: args.kandanUrl,
|
|
45
|
-
accessToken: cached.accessToken,
|
|
46
|
-
workspaceSlug: args.workspaceSlug,
|
|
47
|
-
channelSlug: args.channelSlug,
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
if (cachedTokenIsUsable) {
|
|
51
|
-
return cached.accessToken;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
args.reportRejectedCachedToken?.();
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return await deps.acquireAndCacheToken(args);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
async function acquireAndCacheToken(args: LocalRunnerTokenResolutionArgs): Promise<string> {
|
|
61
|
-
const token = await acquireLocalRunnerTokenDetails({
|
|
62
|
-
kandanUrl: args.kandanUrl,
|
|
63
|
-
workspaceSlug: args.workspaceSlug,
|
|
64
|
-
channelSlug: args.channelSlug,
|
|
65
|
-
onboarding: args.onboarding,
|
|
66
|
-
callbackHost: args.callbackHost,
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
writeCachedLocalRunnerToken({
|
|
70
|
-
kandanUrl: args.kandanUrl,
|
|
71
|
-
accessToken: token.accessToken,
|
|
72
|
-
expiresInSeconds: token.expiresInSeconds,
|
|
73
|
-
authFilePath: args.authFilePath,
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
return token.accessToken;
|
|
77
|
-
}
|
package/src/boundedCache.ts
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
- Date: 2026-04-26
|
|
3
|
-
Spec: kandan/server_v2/plans/2026-04-26-local-codex-driver-worldclass-spec.md
|
|
4
|
-
Relationship: Provides bounded in-memory registries for local Codex runner
|
|
5
|
-
state so long turns keep O(1) keyed lookup while retaining deterministic
|
|
6
|
-
eviction order for transcript reconciliation.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
export type BoundedCache<TValue> = {
|
|
10
|
-
readonly limit: number;
|
|
11
|
-
readonly values: Map<string, TValue>;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
export function createBoundedCache<TValue>(limit: number): BoundedCache<TValue> {
|
|
15
|
-
if (!Number.isInteger(limit) || limit <= 0) {
|
|
16
|
-
throw new Error(`invalid bounded cache limit: ${limit}`);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
return {
|
|
20
|
-
limit,
|
|
21
|
-
values: new Map(),
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function getBoundedCacheValue<TValue>(
|
|
26
|
-
cache: BoundedCache<TValue>,
|
|
27
|
-
key: string,
|
|
28
|
-
): TValue | undefined {
|
|
29
|
-
return cache.values.get(key);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function rememberBoundedCacheValue<TValue>(
|
|
33
|
-
cache: BoundedCache<TValue>,
|
|
34
|
-
key: string,
|
|
35
|
-
value: TValue,
|
|
36
|
-
): void {
|
|
37
|
-
cache.values.set(key, value);
|
|
38
|
-
|
|
39
|
-
while (cache.values.size > cache.limit) {
|
|
40
|
-
const evicted = cache.values.keys().next().value;
|
|
41
|
-
|
|
42
|
-
if (evicted !== undefined) {
|
|
43
|
-
cache.values.delete(evicted);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export function forgetBoundedCacheValue<TValue>(
|
|
49
|
-
cache: BoundedCache<TValue>,
|
|
50
|
-
key: string,
|
|
51
|
-
): void {
|
|
52
|
-
cache.values.delete(key);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export function boundedCacheValues<TValue>(cache: BoundedCache<TValue>): TValue[] {
|
|
56
|
-
return [...cache.values.values()];
|
|
57
|
-
}
|