@linzumi/cli 0.0.76-beta → 0.0.78-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 +1 -1
- package/dist/index.js +3255 -453
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -376,12 +376,7 @@ function appendDownloadedAttachmentContext(input, attachments, options = {}) {
|
|
|
376
376
|
"Do not mention that you are using the upload tool. Do not print an upload manifest or upload footer in the message.",
|
|
377
377
|
"The MCP upload tool is the only supported path for attaching generated files to Linzumi messages."
|
|
378
378
|
];
|
|
379
|
-
return [
|
|
380
|
-
input,
|
|
381
|
-
...attachmentContext,
|
|
382
|
-
"",
|
|
383
|
-
...uploadInstructions
|
|
384
|
-
].join("\n");
|
|
379
|
+
return [input, ...attachmentContext, "", ...uploadInstructions].join("\n");
|
|
385
380
|
}
|
|
386
381
|
function codexImageInputItemsForDownloadedAttachments(attachments) {
|
|
387
382
|
return attachments.flatMap(
|
|
@@ -659,7 +654,11 @@ async function commanderAttachmentUploadRoots(cwdRealPath) {
|
|
|
659
654
|
const roots = [
|
|
660
655
|
{ path: cwdRealPath, label: "runner cwd" }
|
|
661
656
|
];
|
|
662
|
-
const
|
|
657
|
+
const configuredHome = process.env.HOME?.trim();
|
|
658
|
+
const downloadsPath = resolve(
|
|
659
|
+
configuredHome === void 0 || configuredHome === "" ? homedir() : configuredHome,
|
|
660
|
+
"Downloads"
|
|
661
|
+
);
|
|
663
662
|
try {
|
|
664
663
|
const downloadsRealPath = await realpath(downloadsPath);
|
|
665
664
|
if (!roots.some((root) => root.path === downloadsRealPath)) {
|
|
@@ -9603,7 +9602,7 @@ function defaultCliAuditLogFile() {
|
|
|
9603
9602
|
return override === void 0 || override === "" ? join4(homedir4(), ".linzumi", "logs", "command-events.jsonl") : override;
|
|
9604
9603
|
}
|
|
9605
9604
|
function defaultRunnerLogFile() {
|
|
9606
|
-
return join4(homedir4(), ".linzumi", "logs", "runner
|
|
9605
|
+
return join4(homedir4(), ".linzumi", "logs", "linzumi-runner.log");
|
|
9607
9606
|
}
|
|
9608
9607
|
function redactForCliLog(value) {
|
|
9609
9608
|
return redactObject(value);
|
|
@@ -9773,7 +9772,8 @@ function linzumiMcpServerConfig(options) {
|
|
|
9773
9772
|
...options.cwd === void 0 ? [] : ["--cwd", options.cwd],
|
|
9774
9773
|
...options.threadId === void 0 ? [] : ["--thread-id", options.threadId],
|
|
9775
9774
|
"--mode",
|
|
9776
|
-
options.operatingMode ?? "text"
|
|
9775
|
+
options.operatingMode ?? "text",
|
|
9776
|
+
...options.toolScope === void 0 || options.toolScope === "all" ? [] : ["--tool-scope", options.toolScope]
|
|
9777
9777
|
],
|
|
9778
9778
|
env: {
|
|
9779
9779
|
...options.accessToken === void 0 ? {} : { LINZUMI_MCP_ACCESS_TOKEN: options.accessToken }
|
|
@@ -10726,6 +10726,9 @@ async function validateLocalRunnerToken(args) {
|
|
|
10726
10726
|
if (args.channelSlug !== void 0) {
|
|
10727
10727
|
url.searchParams.set("channel", args.channelSlug);
|
|
10728
10728
|
}
|
|
10729
|
+
for (const scope of args.requiredScopes ?? []) {
|
|
10730
|
+
url.searchParams.append("required_scope", scope);
|
|
10731
|
+
}
|
|
10729
10732
|
const response = await fetch(url, {
|
|
10730
10733
|
method: "GET",
|
|
10731
10734
|
headers: { authorization: `Bearer ${args.accessToken}` }
|
|
@@ -13578,7 +13581,7 @@ async function resolveEditorRuntime(options) {
|
|
|
13578
13581
|
fetchImpl: options.fetchImpl ?? fetch
|
|
13579
13582
|
});
|
|
13580
13583
|
if (!manifest.ok) {
|
|
13581
|
-
return unavailable(
|
|
13584
|
+
return unavailable(manifest.reason);
|
|
13582
13585
|
}
|
|
13583
13586
|
const cacheRoot = options.cacheRoot ?? defaultEditorRuntimeCacheRoot();
|
|
13584
13587
|
const installed = installedRuntime(cacheRoot, manifest.manifest);
|
|
@@ -13670,17 +13673,24 @@ async function fetchApprovedManifest(args) {
|
|
|
13670
13673
|
headers: { authorization: `Bearer ${args.token}` }
|
|
13671
13674
|
}).catch(() => void 0);
|
|
13672
13675
|
if (response === void 0) {
|
|
13673
|
-
return { ok: false };
|
|
13676
|
+
return { ok: false, reason: "manifest_unavailable" };
|
|
13674
13677
|
}
|
|
13675
13678
|
if (response.status !== 200) {
|
|
13676
|
-
return {
|
|
13679
|
+
return {
|
|
13680
|
+
ok: false,
|
|
13681
|
+
reason: response.status === 401 ? await unauthorizedManifestReason(response) : "manifest_unavailable"
|
|
13682
|
+
};
|
|
13677
13683
|
}
|
|
13678
13684
|
const body = await response.json();
|
|
13679
13685
|
if (!isJsonObject(body) || body.ok !== true || !isJsonObject(body.runtime)) {
|
|
13680
|
-
return { ok: false };
|
|
13686
|
+
return { ok: false, reason: "manifest_unavailable" };
|
|
13681
13687
|
}
|
|
13682
13688
|
return normalizeManifest(body.runtime);
|
|
13683
13689
|
}
|
|
13690
|
+
async function unauthorizedManifestReason(response) {
|
|
13691
|
+
const body = await response.json().catch(() => void 0);
|
|
13692
|
+
return isJsonObject(body) && body.error === "token_expired" ? "auth_expired" : "auth_unauthorized";
|
|
13693
|
+
}
|
|
13684
13694
|
function normalizeManifest(value) {
|
|
13685
13695
|
const version = nonEmptyString(value.version);
|
|
13686
13696
|
const platform = nonEmptyString(value.platform);
|
|
@@ -13691,7 +13701,7 @@ function normalizeManifest(value) {
|
|
|
13691
13701
|
const manifestPath = nonEmptyString(value.manifestPath) ?? "linzumi-editor-runtime.json";
|
|
13692
13702
|
const assets = normalizeRuntimeAssets(value.assets);
|
|
13693
13703
|
if (version === void 0 || platform === void 0 || archiveUrl === void 0 || archiveSha256 === void 0 || codeServerVersion === void 0 || assets === void 0) {
|
|
13694
|
-
return { ok: false };
|
|
13704
|
+
return { ok: false, reason: "manifest_unavailable" };
|
|
13695
13705
|
}
|
|
13696
13706
|
return {
|
|
13697
13707
|
ok: true,
|
|
@@ -14265,6 +14275,20 @@ function assertRunnerConnectionDependencies(status) {
|
|
|
14265
14275
|
);
|
|
14266
14276
|
}
|
|
14267
14277
|
if (status.editorRuntime?.status === "unavailable" || status.codeServer?.available === false && status.codeServer.reason !== "not_configured") {
|
|
14278
|
+
if (status.editorRuntime?.status === "unavailable") {
|
|
14279
|
+
switch (status.editorRuntime.reason) {
|
|
14280
|
+
case "auth_expired":
|
|
14281
|
+
throw new Error(
|
|
14282
|
+
"Linzumi authentication expired. Sign in again, then reconnect the local runner."
|
|
14283
|
+
);
|
|
14284
|
+
case "auth_unauthorized":
|
|
14285
|
+
throw new Error(
|
|
14286
|
+
"Linzumi authentication was rejected. Sign in again, then reconnect the local runner."
|
|
14287
|
+
);
|
|
14288
|
+
default:
|
|
14289
|
+
break;
|
|
14290
|
+
}
|
|
14291
|
+
}
|
|
14268
14292
|
throw new Error(
|
|
14269
14293
|
"The Linzumi editor runtime is not available. Reconnect when the runtime update finishes or pass --code-server-bin <path> for a custom development runtime."
|
|
14270
14294
|
);
|
|
@@ -14633,7 +14657,7 @@ var linzumiCliVersion, linzumiCliVersionText;
|
|
|
14633
14657
|
var init_version = __esm({
|
|
14634
14658
|
"src/version.ts"() {
|
|
14635
14659
|
"use strict";
|
|
14636
|
-
linzumiCliVersion = "0.0.
|
|
14660
|
+
linzumiCliVersion = "0.0.78-beta";
|
|
14637
14661
|
linzumiCliVersionText = `linzumi ${linzumiCliVersion}`;
|
|
14638
14662
|
}
|
|
14639
14663
|
});
|
|
@@ -14939,6 +14963,39 @@ function updateRunnerConsoleDashboard(state, event, payload, nowMs) {
|
|
|
14939
14963
|
state.lastUpdateAtMs = nowMs;
|
|
14940
14964
|
const previousJobKey = latestDashboardJob(state)?.key;
|
|
14941
14965
|
switch (event) {
|
|
14966
|
+
case "onboarding.discovery_agents_started":
|
|
14967
|
+
state.browserUrl = stringValue4(payload.kandanUrl);
|
|
14968
|
+
state.workspace = stringValue4(payload.workspace);
|
|
14969
|
+
state.runnerId = stringValue4(payload.runnerId);
|
|
14970
|
+
break;
|
|
14971
|
+
case "onboarding.discovery_status": {
|
|
14972
|
+
const phase = stringValue4(payload.phase);
|
|
14973
|
+
const status = stringValue4(payload.status);
|
|
14974
|
+
const metadata = objectValue5(payload.metadata);
|
|
14975
|
+
const workerId = stringValue4(metadata?.worker_id);
|
|
14976
|
+
if (phase !== void 0 && status !== void 0) {
|
|
14977
|
+
const isConversationPhase = phase === "conversations";
|
|
14978
|
+
const key = isConversationPhase ? phase : workerId ?? phase;
|
|
14979
|
+
const previous = state.discovery.get(key);
|
|
14980
|
+
const mergedMetadata = isConversationPhase ? mergeConversationImportMetadata(previous?.metadata, metadata) : metadata;
|
|
14981
|
+
state.discovery.set(key, {
|
|
14982
|
+
key,
|
|
14983
|
+
phase,
|
|
14984
|
+
status: isConversationPhase ? conversationDiscoveryMergedStatus(previous, status, metadata) : status,
|
|
14985
|
+
placesSearched: Math.max(
|
|
14986
|
+
previous?.placesSearched ?? 0,
|
|
14987
|
+
numberValue(payload.places_searched) ?? 0
|
|
14988
|
+
),
|
|
14989
|
+
message: stringValue4(payload.message) ?? (isConversationPhase ? previous?.message : void 0),
|
|
14990
|
+
currentPlace: stringValue4(payload.current_place) ?? (isConversationPhase ? previous?.currentPlace : void 0),
|
|
14991
|
+
currentSource: stringValue4(payload.current_source) ?? (isConversationPhase ? previous?.currentSource : void 0),
|
|
14992
|
+
lastError: stringValue4(payload.last_error),
|
|
14993
|
+
metadata: mergedMetadata,
|
|
14994
|
+
updatedAtMs: nowMs
|
|
14995
|
+
});
|
|
14996
|
+
}
|
|
14997
|
+
break;
|
|
14998
|
+
}
|
|
14942
14999
|
case "kandan.message_queued":
|
|
14943
15000
|
case "codex.turn_starting": {
|
|
14944
15001
|
const job = dashboardJobForPayload(state, payload, nowMs);
|
|
@@ -15007,12 +15064,15 @@ function updateRunnerConsoleDashboard(state, event, payload, nowMs) {
|
|
|
15007
15064
|
rememberRawLine(state, event, payload, previousJobKey);
|
|
15008
15065
|
}
|
|
15009
15066
|
function renderRunnerConsoleDashboard(state, nowMs) {
|
|
15010
|
-
if (state.jobs.size === 0 && state.tokenUsage === void 0 && state.rateLimit === void 0 && state.creditExhaustion === void 0) {
|
|
15067
|
+
if (state.jobs.size === 0 && state.discovery.size === 0 && state.tokenUsage === void 0 && state.rateLimit === void 0 && state.creditExhaustion === void 0) {
|
|
15011
15068
|
return void 0;
|
|
15012
15069
|
}
|
|
15013
15070
|
const jobs = dashboardJobs(state);
|
|
15014
15071
|
switch (state.mode.type) {
|
|
15015
15072
|
case "table":
|
|
15073
|
+
if (state.jobs.size === 0 && state.discovery.size > 0) {
|
|
15074
|
+
return renderSignupDiscoveryWelcome(state, nowMs);
|
|
15075
|
+
}
|
|
15016
15076
|
return renderDashboardTable(state, jobs, nowMs);
|
|
15017
15077
|
case "raw_all":
|
|
15018
15078
|
return renderRawDashboard(
|
|
@@ -15038,9 +15098,195 @@ function renderRunnerConsoleDashboard(state, nowMs) {
|
|
|
15038
15098
|
}
|
|
15039
15099
|
}
|
|
15040
15100
|
}
|
|
15101
|
+
function renderSignupDiscoveryWelcome(state, nowMs) {
|
|
15102
|
+
const statuses = Array.from(state.discovery.values()).sort(
|
|
15103
|
+
(left, right) => discoveryPhaseSortKey(left.phase).localeCompare(
|
|
15104
|
+
discoveryPhaseSortKey(right.phase)
|
|
15105
|
+
)
|
|
15106
|
+
);
|
|
15107
|
+
const placesSearched = statuses.reduce(
|
|
15108
|
+
(total, status) => total + status.placesSearched,
|
|
15109
|
+
0
|
|
15110
|
+
);
|
|
15111
|
+
const stillSearching = statuses.some(
|
|
15112
|
+
(status) => ["starting", "running"].includes(status.status)
|
|
15113
|
+
);
|
|
15114
|
+
const statusRows = statuses.length === 0 ? ["Discovery starting 0 places Waiting for discovery agents"] : statuses.map(
|
|
15115
|
+
(status) => [
|
|
15116
|
+
discoveryPhaseLabel(status.phase).padEnd(14),
|
|
15117
|
+
discoveryStatusLabel(status.status).padEnd(10),
|
|
15118
|
+
`${String(status.placesSearched)} places`.padEnd(10),
|
|
15119
|
+
discoveryStatusDetail(status)
|
|
15120
|
+
].join(" ")
|
|
15121
|
+
);
|
|
15122
|
+
return [
|
|
15123
|
+
runnerWelcomeHeader(),
|
|
15124
|
+
"",
|
|
15125
|
+
"Linzumi setup",
|
|
15126
|
+
"New computer connected",
|
|
15127
|
+
"",
|
|
15128
|
+
`Continue in the browser: ${state.browserUrl ?? "open the Linzumi setup page"}`,
|
|
15129
|
+
"",
|
|
15130
|
+
discoverySummaryText(statuses),
|
|
15131
|
+
`Workspace: ${state.workspace ?? "?"}`,
|
|
15132
|
+
`Runner: ${state.runnerId ?? "?"}`,
|
|
15133
|
+
`Status: ${stillSearching ? "searching" : "finished"} Places searched: ${placesSearched}`,
|
|
15134
|
+
`Last update: ${timeAgo(state.lastUpdateAtMs, nowMs)}`,
|
|
15135
|
+
"",
|
|
15136
|
+
"Phase Status Places Detail",
|
|
15137
|
+
"------------- ---------- --------- ------------------------------",
|
|
15138
|
+
...statusRows,
|
|
15139
|
+
""
|
|
15140
|
+
].join("\n");
|
|
15141
|
+
}
|
|
15142
|
+
function discoveryPhaseSortKey(phase) {
|
|
15143
|
+
switch (phase) {
|
|
15144
|
+
case "projects":
|
|
15145
|
+
return "1-projects";
|
|
15146
|
+
case "conversations":
|
|
15147
|
+
return "2-conversations";
|
|
15148
|
+
default:
|
|
15149
|
+
return `3-${phase}`;
|
|
15150
|
+
}
|
|
15151
|
+
}
|
|
15152
|
+
function discoveryPhaseLabel(phase) {
|
|
15153
|
+
switch (phase) {
|
|
15154
|
+
case "projects":
|
|
15155
|
+
return "Projects";
|
|
15156
|
+
case "conversations":
|
|
15157
|
+
return "Conversations";
|
|
15158
|
+
default:
|
|
15159
|
+
return "Discovery";
|
|
15160
|
+
}
|
|
15161
|
+
}
|
|
15162
|
+
function discoveryStatusLabel(status) {
|
|
15163
|
+
switch (status) {
|
|
15164
|
+
case "starting":
|
|
15165
|
+
return "starting";
|
|
15166
|
+
case "running":
|
|
15167
|
+
return "searching";
|
|
15168
|
+
case "completed":
|
|
15169
|
+
return "done";
|
|
15170
|
+
case "failed":
|
|
15171
|
+
return "failed";
|
|
15172
|
+
default:
|
|
15173
|
+
return status;
|
|
15174
|
+
}
|
|
15175
|
+
}
|
|
15176
|
+
function discoverySummaryText(statuses) {
|
|
15177
|
+
if (statuses.length === 0) {
|
|
15178
|
+
return "I'm waiting for the discovery agents to start.";
|
|
15179
|
+
}
|
|
15180
|
+
const stillSearching = statuses.some(
|
|
15181
|
+
(status) => ["starting", "running"].includes(status.status)
|
|
15182
|
+
);
|
|
15183
|
+
return stillSearching ? "I'm scouting existing projects and coding-agent conversations." : "Discovery is finished and idle. Select a project in the browser or move on.";
|
|
15184
|
+
}
|
|
15185
|
+
function discoveryStatusDetail(status) {
|
|
15186
|
+
if (status.lastError !== void 0) {
|
|
15187
|
+
return status.lastError;
|
|
15188
|
+
}
|
|
15189
|
+
const importProgress = conversationImportProgressText(status);
|
|
15190
|
+
if (importProgress !== void 0) {
|
|
15191
|
+
return importProgress;
|
|
15192
|
+
}
|
|
15193
|
+
const source = discoverySourceLabel(status.currentSource);
|
|
15194
|
+
if (status.currentPlace !== void 0) {
|
|
15195
|
+
return source === void 0 ? status.currentPlace : `${source}: ${status.currentPlace}`;
|
|
15196
|
+
}
|
|
15197
|
+
return status.message ?? discoveryDefaultMessage(status.phase);
|
|
15198
|
+
}
|
|
15199
|
+
function conversationImportProgressText(status) {
|
|
15200
|
+
if (status.phase !== "conversations") {
|
|
15201
|
+
return void 0;
|
|
15202
|
+
}
|
|
15203
|
+
if (!conversationImportMetadata(status.metadata)) {
|
|
15204
|
+
return void 0;
|
|
15205
|
+
}
|
|
15206
|
+
const totalCount = numberValue(status.metadata?.total_count) ?? (numberValue(status.metadata?.codex_count) ?? 0) + (numberValue(status.metadata?.claude_code_count) ?? 0);
|
|
15207
|
+
const reportedCount = numberValue(status.metadata?.imported_count) ?? numberValue(status.metadata?.reported_count);
|
|
15208
|
+
if (reportedCount === void 0 || totalCount <= 0) {
|
|
15209
|
+
return void 0;
|
|
15210
|
+
}
|
|
15211
|
+
const label = status.status === "completed" ? "Imported" : "Importing";
|
|
15212
|
+
return `${label} [${conversationImportProgressBar(
|
|
15213
|
+
reportedCount,
|
|
15214
|
+
totalCount
|
|
15215
|
+
)}] ${reportedCount} / ${totalCount} Codex/Claude Code conversations`;
|
|
15216
|
+
}
|
|
15217
|
+
function conversationImportMetadata(metadata) {
|
|
15218
|
+
return stringValue4(metadata?.worker_id) === "conversations-local-import" || numberValue(metadata?.imported_count) !== void 0 || numberValue(metadata?.failed_import_count) !== void 0 || numberValue(metadata?.total_count) !== void 0;
|
|
15219
|
+
}
|
|
15220
|
+
function conversationImportProgressBar(reportedCount, totalCount) {
|
|
15221
|
+
const width = 10;
|
|
15222
|
+
const filled = Math.min(
|
|
15223
|
+
width,
|
|
15224
|
+
Math.max(0, Math.floor(reportedCount / totalCount * width))
|
|
15225
|
+
);
|
|
15226
|
+
return `${"#".repeat(filled)}${"-".repeat(width - filled)}`;
|
|
15227
|
+
}
|
|
15228
|
+
function mergeConversationImportMetadata(previous, next) {
|
|
15229
|
+
if (previous === void 0) {
|
|
15230
|
+
return next;
|
|
15231
|
+
}
|
|
15232
|
+
if (next === void 0) {
|
|
15233
|
+
return previous;
|
|
15234
|
+
}
|
|
15235
|
+
return {
|
|
15236
|
+
...previous,
|
|
15237
|
+
...next,
|
|
15238
|
+
...maxOptionalMetadataNumber(previous, next, "codex_count"),
|
|
15239
|
+
...maxOptionalMetadataNumber(previous, next, "claude_code_count"),
|
|
15240
|
+
...maxOptionalMetadataNumber(previous, next, "reported_count"),
|
|
15241
|
+
...maxOptionalMetadataNumber(previous, next, "imported_count"),
|
|
15242
|
+
...maxOptionalMetadataNumber(previous, next, "total_count")
|
|
15243
|
+
};
|
|
15244
|
+
}
|
|
15245
|
+
function maxOptionalMetadataNumber(previous, next, key) {
|
|
15246
|
+
const previousValue = numberValue(previous[key]);
|
|
15247
|
+
const nextValue = numberValue(next[key]);
|
|
15248
|
+
if (previousValue === void 0 && nextValue === void 0) {
|
|
15249
|
+
return {};
|
|
15250
|
+
}
|
|
15251
|
+
return {
|
|
15252
|
+
[key]: Math.max(previousValue ?? 0, nextValue ?? 0)
|
|
15253
|
+
};
|
|
15254
|
+
}
|
|
15255
|
+
function conversationDiscoveryMergedStatus(previous, nextStatus, nextMetadata) {
|
|
15256
|
+
if (previous?.status === "running" && conversationImportMetadata(previous.metadata) && !conversationImportMetadata(nextMetadata)) {
|
|
15257
|
+
return previous.status;
|
|
15258
|
+
}
|
|
15259
|
+
return nextStatus;
|
|
15260
|
+
}
|
|
15261
|
+
function discoverySourceLabel(source) {
|
|
15262
|
+
switch (source) {
|
|
15263
|
+
case "git":
|
|
15264
|
+
return "Git";
|
|
15265
|
+
case "codex":
|
|
15266
|
+
return "Codex";
|
|
15267
|
+
case "claude_code":
|
|
15268
|
+
return "Claude Code";
|
|
15269
|
+
case void 0:
|
|
15270
|
+
return void 0;
|
|
15271
|
+
default:
|
|
15272
|
+
return source;
|
|
15273
|
+
}
|
|
15274
|
+
}
|
|
15275
|
+
function discoveryDefaultMessage(phase) {
|
|
15276
|
+
switch (phase) {
|
|
15277
|
+
case "projects":
|
|
15278
|
+
return "Checking common project folders and Git worktrees.";
|
|
15279
|
+
case "conversations":
|
|
15280
|
+
return "Checking Codex and Claude Code sessions.";
|
|
15281
|
+
default:
|
|
15282
|
+
return "Checking local context.";
|
|
15283
|
+
}
|
|
15284
|
+
}
|
|
15041
15285
|
function renderDashboardTable(state, jobs, nowMs) {
|
|
15042
15286
|
const model = dashboardTableModel(state, jobs, nowMs);
|
|
15043
15287
|
return [
|
|
15288
|
+
runnerWelcomeHeader(),
|
|
15289
|
+
"",
|
|
15044
15290
|
"Linzumi Commander",
|
|
15045
15291
|
"",
|
|
15046
15292
|
`Jobs: ${jobs.length} Last update: ${timeAgo(state.lastUpdateAtMs, nowMs)}`,
|
|
@@ -15053,6 +15299,9 @@ function renderDashboardTable(state, jobs, nowMs) {
|
|
|
15053
15299
|
""
|
|
15054
15300
|
].join("\n");
|
|
15055
15301
|
}
|
|
15302
|
+
function runnerWelcomeHeader() {
|
|
15303
|
+
return runnerWelcomeHeaderLines.join("\n");
|
|
15304
|
+
}
|
|
15056
15305
|
function creditExhaustionBanner(summary) {
|
|
15057
15306
|
if (summary === void 0) {
|
|
15058
15307
|
return [];
|
|
@@ -15061,7 +15310,12 @@ function creditExhaustionBanner(summary) {
|
|
|
15061
15310
|
}
|
|
15062
15311
|
function dashboardTableModel(state, jobs, nowMs) {
|
|
15063
15312
|
const selectedJobKey = selectedDashboardJobKey(state, jobs);
|
|
15064
|
-
const
|
|
15313
|
+
const activeImportRows = activeConversationImportRows(state, nowMs);
|
|
15314
|
+
const rows = jobs.length > 0 && activeImportRows.length > 0 ? [
|
|
15315
|
+
dashboardTableHeader(),
|
|
15316
|
+
...activeImportRows,
|
|
15317
|
+
...jobs.map((job) => jobRow(job, selectedJobKey, nowMs))
|
|
15318
|
+
] : jobs.length === 0 && state.discovery.size > 0 ? discoveryTableRows(state, nowMs) : jobs.length === 0 ? [dashboardTableHeader(), emptyJobRow()] : [
|
|
15065
15319
|
dashboardTableHeader(),
|
|
15066
15320
|
...jobs.map((job) => jobRow(job, selectedJobKey, nowMs))
|
|
15067
15321
|
];
|
|
@@ -15071,6 +15325,91 @@ function dashboardTableModel(state, jobs, nowMs) {
|
|
|
15071
15325
|
rows
|
|
15072
15326
|
};
|
|
15073
15327
|
}
|
|
15328
|
+
function activeConversationImportRows(state, nowMs) {
|
|
15329
|
+
return Array.from(state.discovery.values()).filter(activeConversationImportStatus).sort((left, right) => right.updatedAtMs - left.updatedAtMs).map((status) => [
|
|
15330
|
+
"",
|
|
15331
|
+
discoveryPhaseLabel(status.phase),
|
|
15332
|
+
discoveryStatusLabel(status.status),
|
|
15333
|
+
discoveryStatusCurrentSearch(status),
|
|
15334
|
+
String(status.placesSearched),
|
|
15335
|
+
discoveryStatusDetail(status),
|
|
15336
|
+
timeAgo(status.updatedAtMs, nowMs),
|
|
15337
|
+
discoveryStatusEventLabel(status),
|
|
15338
|
+
"onboarding.discovery"
|
|
15339
|
+
]);
|
|
15340
|
+
}
|
|
15341
|
+
function activeConversationImportStatus(status) {
|
|
15342
|
+
return ["starting", "running"].includes(status.status) && conversationImportProgressText(status) !== void 0;
|
|
15343
|
+
}
|
|
15344
|
+
function discoveryTableRows(state, nowMs) {
|
|
15345
|
+
const statuses = Array.from(state.discovery.values()).sort(
|
|
15346
|
+
(left, right) => discoveryPhaseSortKey(left.phase).localeCompare(
|
|
15347
|
+
discoveryPhaseSortKey(right.phase)
|
|
15348
|
+
)
|
|
15349
|
+
);
|
|
15350
|
+
return [
|
|
15351
|
+
[
|
|
15352
|
+
"",
|
|
15353
|
+
"Onboarding scout",
|
|
15354
|
+
"Status",
|
|
15355
|
+
"Current search",
|
|
15356
|
+
"Checked",
|
|
15357
|
+
"Detail",
|
|
15358
|
+
"Ago",
|
|
15359
|
+
"Latest event",
|
|
15360
|
+
"Event type"
|
|
15361
|
+
],
|
|
15362
|
+
...statuses.length === 0 ? [
|
|
15363
|
+
[
|
|
15364
|
+
"",
|
|
15365
|
+
"Discovery",
|
|
15366
|
+
"starting",
|
|
15367
|
+
"Waiting for agents",
|
|
15368
|
+
"0",
|
|
15369
|
+
"Waiting for Commander discovery agents",
|
|
15370
|
+
"?",
|
|
15371
|
+
"onboarding",
|
|
15372
|
+
"discovery"
|
|
15373
|
+
]
|
|
15374
|
+
] : statuses.map((status) => [
|
|
15375
|
+
"",
|
|
15376
|
+
discoveryPhaseLabel(status.phase),
|
|
15377
|
+
discoveryStatusLabel(status.status),
|
|
15378
|
+
discoveryStatusCurrentSearch(status),
|
|
15379
|
+
String(status.placesSearched),
|
|
15380
|
+
discoveryStatusDetail(status),
|
|
15381
|
+
timeAgo(status.updatedAtMs, nowMs),
|
|
15382
|
+
discoveryStatusEventLabel(status),
|
|
15383
|
+
"onboarding.discovery"
|
|
15384
|
+
])
|
|
15385
|
+
];
|
|
15386
|
+
}
|
|
15387
|
+
function discoveryStatusEventLabel(status) {
|
|
15388
|
+
if (["starting", "running"].includes(status.status) && conversationImportProgressText(status) !== void 0) {
|
|
15389
|
+
return "importing";
|
|
15390
|
+
}
|
|
15391
|
+
switch (status.status) {
|
|
15392
|
+
case "completed":
|
|
15393
|
+
return "idle";
|
|
15394
|
+
case "failed":
|
|
15395
|
+
return "stopped";
|
|
15396
|
+
default:
|
|
15397
|
+
return "scouting";
|
|
15398
|
+
}
|
|
15399
|
+
}
|
|
15400
|
+
function discoveryStatusCurrentSearch(status) {
|
|
15401
|
+
if (status.lastError !== void 0) {
|
|
15402
|
+
return "stopped";
|
|
15403
|
+
}
|
|
15404
|
+
switch (status.status) {
|
|
15405
|
+
case "completed":
|
|
15406
|
+
return "finished";
|
|
15407
|
+
case "failed":
|
|
15408
|
+
return "stopped";
|
|
15409
|
+
default:
|
|
15410
|
+
return status.currentPlace ?? status.message ?? discoveryDefaultMessage(status.phase);
|
|
15411
|
+
}
|
|
15412
|
+
}
|
|
15074
15413
|
function dashboardTableHeader() {
|
|
15075
15414
|
return [
|
|
15076
15415
|
"",
|
|
@@ -15115,6 +15454,10 @@ function formatRunnerConsoleEvent(event, payload) {
|
|
|
15115
15454
|
return ignoredMessage(payload);
|
|
15116
15455
|
case "kandan.message_queued":
|
|
15117
15456
|
return `Incoming message from ${sender(payload)}: queued seq=${text(payload.seq)} depth=${text(payload.queue_depth)}`;
|
|
15457
|
+
case "onboarding.discovery_status":
|
|
15458
|
+
return onboardingDiscoveryStatusLine(payload);
|
|
15459
|
+
case "onboarding.discovery_initial_search_completed":
|
|
15460
|
+
return onboardingDiscoveryInitialSearchCompletedLine(payload);
|
|
15118
15461
|
case "kandan.chat_event_failed":
|
|
15119
15462
|
return `Incoming message handling failed: seq=${text(payload.seq)} reason=${text(payload.message)}`;
|
|
15120
15463
|
case "kandan.reconnected":
|
|
@@ -15154,6 +15497,20 @@ function formatRunnerConsoleEvent(event, payload) {
|
|
|
15154
15497
|
return void 0;
|
|
15155
15498
|
}
|
|
15156
15499
|
}
|
|
15500
|
+
function onboardingDiscoveryStatusLine(payload) {
|
|
15501
|
+
const phase = discoveryPhaseLabel(stringValue4(payload.phase) ?? "discovery");
|
|
15502
|
+
const status = discoveryStatusLabel(stringValue4(payload.status) ?? "unknown");
|
|
15503
|
+
const placesSearched = numberValue(payload.places_searched) ?? 0;
|
|
15504
|
+
const currentPlace = stringValue4(payload.current_place);
|
|
15505
|
+
const message = stringValue4(payload.message);
|
|
15506
|
+
const detail = stringValue4(payload.last_error) ?? currentPlace ?? message ?? "Checking local context";
|
|
15507
|
+
return `Onboarding discovery: ${phase} ${status} checked=${placesSearched} current=${detail}`;
|
|
15508
|
+
}
|
|
15509
|
+
function onboardingDiscoveryInitialSearchCompletedLine(payload) {
|
|
15510
|
+
const durationMs = numberValue(payload.duration_ms) ?? 0;
|
|
15511
|
+
const seconds = (durationMs / 1e3).toFixed(1);
|
|
15512
|
+
return `Onboarding discovery initial search completed in ${durationMs}ms (${seconds}s) workspace=${text(payload.workspace)} runner=${text(payload.runnerId)}`;
|
|
15513
|
+
}
|
|
15157
15514
|
function connectedRunnerMessage(payload) {
|
|
15158
15515
|
return [
|
|
15159
15516
|
"Connected to Linzumi",
|
|
@@ -15441,11 +15798,11 @@ function createRunnerConsoleDashboardTui(state, exitProcess = () => process.kill
|
|
|
15441
15798
|
top: 0,
|
|
15442
15799
|
left: 0,
|
|
15443
15800
|
width: "100%",
|
|
15444
|
-
height:
|
|
15801
|
+
height: 1,
|
|
15445
15802
|
tags: false
|
|
15446
15803
|
});
|
|
15447
15804
|
const tableElement = blessed.listtable({
|
|
15448
|
-
top:
|
|
15805
|
+
top: 1,
|
|
15449
15806
|
left: 0,
|
|
15450
15807
|
width: "100%",
|
|
15451
15808
|
bottom: 1,
|
|
@@ -15567,7 +15924,15 @@ function createRunnerConsoleDashboardTui(state, exitProcess = () => process.kill
|
|
|
15567
15924
|
switch (state.mode.type) {
|
|
15568
15925
|
case "table": {
|
|
15569
15926
|
const model = dashboardTableModel(state, dashboardJobs(state), nowMs);
|
|
15570
|
-
|
|
15927
|
+
const headerLayout = runnerConsoleTuiHeaderLayout(
|
|
15928
|
+
state,
|
|
15929
|
+
model.jobs.length,
|
|
15930
|
+
nowMs,
|
|
15931
|
+
tuiScreenHeight(screen)
|
|
15932
|
+
);
|
|
15933
|
+
header.height = headerLayout.height;
|
|
15934
|
+
tableElement.top = headerLayout.height;
|
|
15935
|
+
header.setContent(headerLayout.content);
|
|
15571
15936
|
header.show();
|
|
15572
15937
|
tableElement.setData(model.rows);
|
|
15573
15938
|
tableElement.show();
|
|
@@ -15618,6 +15983,9 @@ function createRunnerConsoleDashboardTui(state, exitProcess = () => process.kill
|
|
|
15618
15983
|
}
|
|
15619
15984
|
return { render, destroy };
|
|
15620
15985
|
}
|
|
15986
|
+
function tuiScreenHeight(screen) {
|
|
15987
|
+
return typeof screen.height === "number" ? screen.height : 24;
|
|
15988
|
+
}
|
|
15621
15989
|
function syncTuiTableSelection(tableElement, model) {
|
|
15622
15990
|
const selectedIndex = model.jobs.findIndex(
|
|
15623
15991
|
(job) => job.key === model.selectedJobKey
|
|
@@ -15625,8 +15993,46 @@ function syncTuiTableSelection(tableElement, model) {
|
|
|
15625
15993
|
const tableIndex = selectedIndex < 0 ? 1 : selectedIndex + 1;
|
|
15626
15994
|
tableElement.select(tableIndex);
|
|
15627
15995
|
}
|
|
15628
|
-
function
|
|
15996
|
+
function runnerConsoleTuiHeaderLayout(state, jobCount, nowMs, terminalRows) {
|
|
15997
|
+
const fullContent = tuiHeaderContent(
|
|
15998
|
+
state,
|
|
15999
|
+
jobCount,
|
|
16000
|
+
nowMs,
|
|
16001
|
+
runnerWelcomeHeader()
|
|
16002
|
+
);
|
|
16003
|
+
const fullHeight = tuiHeaderHeight(fullContent);
|
|
16004
|
+
if (terminalRows - fullHeight >= minimumVisibleTuiTableRows) {
|
|
16005
|
+
return { content: fullContent, height: fullHeight };
|
|
16006
|
+
}
|
|
16007
|
+
const compactContent = tuiHeaderContent(
|
|
16008
|
+
state,
|
|
16009
|
+
jobCount,
|
|
16010
|
+
nowMs,
|
|
16011
|
+
runnerWelcomeCompactHeader()
|
|
16012
|
+
);
|
|
16013
|
+
return {
|
|
16014
|
+
content: compactContent,
|
|
16015
|
+
height: tuiHeaderHeight(compactContent)
|
|
16016
|
+
};
|
|
16017
|
+
}
|
|
16018
|
+
function tuiHeaderHeight(content) {
|
|
16019
|
+
return content.split("\n").length + 1;
|
|
16020
|
+
}
|
|
16021
|
+
function tuiHeaderContent(state, jobCount, nowMs, welcomeHeader) {
|
|
16022
|
+
if (jobCount === 0 && state.discovery.size > 0) {
|
|
16023
|
+
return [
|
|
16024
|
+
welcomeHeader,
|
|
16025
|
+
"",
|
|
16026
|
+
"Linzumi Commander",
|
|
16027
|
+
discoveryHeaderStatus(state),
|
|
16028
|
+
`Workspace: ${state.workspace ?? "?"} Runner: ${state.runnerId ?? "?"}`,
|
|
16029
|
+
`Last update: ${timeAgo(state.lastUpdateAtMs, nowMs)}`,
|
|
16030
|
+
"Controls: r raw stream | esc/back table | mouse wheel scroll"
|
|
16031
|
+
].join("\n");
|
|
16032
|
+
}
|
|
15629
16033
|
return [
|
|
16034
|
+
welcomeHeader,
|
|
16035
|
+
"",
|
|
15630
16036
|
"Linzumi Commander",
|
|
15631
16037
|
`Jobs: ${jobCount} Last update: ${timeAgo(state.lastUpdateAtMs, nowMs)}`,
|
|
15632
16038
|
...creditExhaustionBanner(state.creditExhaustion),
|
|
@@ -15635,6 +16041,20 @@ function tuiHeaderContent(state, jobCount, nowMs) {
|
|
|
15635
16041
|
"Controls: click row/enter raw job | r raw stream | esc/back table | mouse wheel scroll"
|
|
15636
16042
|
].join("\n");
|
|
15637
16043
|
}
|
|
16044
|
+
function runnerWelcomeCompactHeader() {
|
|
16045
|
+
return runnerWelcomeCompactHeaderLines.join("\n");
|
|
16046
|
+
}
|
|
16047
|
+
function discoveryHeaderStatus(state) {
|
|
16048
|
+
const statuses = Array.from(state.discovery.values());
|
|
16049
|
+
const placesSearched = statuses.reduce(
|
|
16050
|
+
(total, status) => total + status.placesSearched,
|
|
16051
|
+
0
|
|
16052
|
+
);
|
|
16053
|
+
const stillSearching = statuses.some(
|
|
16054
|
+
(status) => ["starting", "running"].includes(status.status)
|
|
16055
|
+
);
|
|
16056
|
+
return `Onboarding discovery: ${stillSearching ? "searching" : "finished/idle"} Places searched: ${placesSearched}`;
|
|
16057
|
+
}
|
|
15638
16058
|
function updateRunnerConsoleDashboardMode(state, key) {
|
|
15639
16059
|
switch (key) {
|
|
15640
16060
|
case "r":
|
|
@@ -15888,12 +16308,13 @@ function stringValue4(value) {
|
|
|
15888
16308
|
function numberValue(value) {
|
|
15889
16309
|
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
15890
16310
|
}
|
|
15891
|
-
var dashboardState, maxRawLines, escapeKey, ctrlCKey, enterKey, upKey, downKey, dashboardTableColumns, redrawScreen, keyboardState, tickerState, tuiState;
|
|
16311
|
+
var dashboardState, maxRawLines, escapeKey, ctrlCKey, enterKey, upKey, downKey, dashboardTableColumns, runnerWelcomeHeaderLines, runnerWelcomeCompactHeaderLines, minimumVisibleTuiTableRows, redrawScreen, keyboardState, tickerState, tuiState;
|
|
15892
16312
|
var init_runnerConsoleReporter = __esm({
|
|
15893
16313
|
"src/runnerConsoleReporter.ts"() {
|
|
15894
16314
|
"use strict";
|
|
15895
16315
|
dashboardState = {
|
|
15896
16316
|
jobs: /* @__PURE__ */ new Map(),
|
|
16317
|
+
discovery: /* @__PURE__ */ new Map(),
|
|
15897
16318
|
rawLines: [],
|
|
15898
16319
|
nextJobOrdinal: 0,
|
|
15899
16320
|
mode: { type: "table" },
|
|
@@ -15901,7 +16322,10 @@ var init_runnerConsoleReporter = __esm({
|
|
|
15901
16322
|
tokenUsage: void 0,
|
|
15902
16323
|
rateLimit: void 0,
|
|
15903
16324
|
creditExhaustion: void 0,
|
|
15904
|
-
lastUpdateAtMs: void 0
|
|
16325
|
+
lastUpdateAtMs: void 0,
|
|
16326
|
+
browserUrl: void 0,
|
|
16327
|
+
workspace: void 0,
|
|
16328
|
+
runnerId: void 0
|
|
15905
16329
|
};
|
|
15906
16330
|
maxRawLines = 500;
|
|
15907
16331
|
escapeKey = "\x1B";
|
|
@@ -15920,6 +16344,37 @@ var init_runnerConsoleReporter = __esm({
|
|
|
15920
16344
|
{ width: 12 },
|
|
15921
16345
|
{ width: 24 }
|
|
15922
16346
|
];
|
|
16347
|
+
runnerWelcomeHeaderLines = [
|
|
16348
|
+
"\u2593\u2593\u2557 \u2593\u2593\u2557\u2593\u2593\u2593\u2557 \u2593\u2593\u2557\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2557\u2593\u2593\u2557 \u2593\u2593\u2557\u2593\u2593\u2593\u2557 \u2593\u2593\u2593\u2557\u2593\u2593\u2557",
|
|
16349
|
+
"\u2593\u2593\u2551 \u2593\u2593\u2551\u2593\u2593\u2593\u2593\u2557 \u2593\u2593\u2551\u255A\u2550\u2550\u2593\u2593\u2593\u2554\u255D\u2593\u2593\u2551 \u2593\u2593\u2551\u2593\u2593\u2593\u2593\u2557 \u2593\u2593\u2593\u2593\u2551\u2593\u2593\u2551",
|
|
16350
|
+
"\u2593\u2593\u2551 \u2593\u2593\u2551\u2593\u2593\u2554\u2593\u2593\u2557 \u2593\u2593\u2551 \u2593\u2593\u2593\u2554\u255D \u2593\u2593\u2551 \u2593\u2593\u2551\u2593\u2593\u2554\u2593\u2593\u2593\u2593\u2554\u2593\u2593\u2551\u2593\u2593\u2551",
|
|
16351
|
+
"\u2593\u2593\u2551 \u2593\u2593\u2551\u2593\u2593\u2551\u255A\u2593\u2593\u2557\u2593\u2593\u2551 \u2593\u2593\u2593\u2554\u255D \u2593\u2593\u2551 \u2593\u2593\u2551\u2593\u2593\u2551\u255A\u2593\u2593\u2554\u255D\u2593\u2593\u2551\u2593\u2593\u2551",
|
|
16352
|
+
"\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u255A\u2593\u2593\u2593\u2593\u2551\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2557\u255A\u2593\u2593\u2593\u2593\u2593\u2593\u2554\u255D\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2593\u2593\u2551\u2593\u2593\u2551",
|
|
16353
|
+
"\u255A\u2550\u2550\u2550\u2592\u2592\u2592@@@@@@@@\u2592\u2592\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2592\u2592\u2592@@@@@@@@\u2592\u2592\u255D\u255A\u2550\u255D",
|
|
16354
|
+
" \u2592@@@@@@@@@@@@@@@@\u2592 \u2592@@@@@@@@@@@@@@@@\u2592",
|
|
16355
|
+
"@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@",
|
|
16356
|
+
" @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@",
|
|
16357
|
+
" \u2551\u2551 \u2551\u2551",
|
|
16358
|
+
" \u2551\u2551 \u2551\u2551",
|
|
16359
|
+
" \u2551\u2551 ()-().----. \u2551\u2551",
|
|
16360
|
+
" \u2551\u2551 \"/` ___ ;_________\u2551\u2551_.'",
|
|
16361
|
+
" \u2551\u2551 ` ^^ ^^ \u2551\u2551",
|
|
16362
|
+
"\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2568\u2568\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\xB7\xB7\u2500\u2500\u2500\u2500\xB7\xB7\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2568\u2568\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
16363
|
+
"Welcome to the Linzumi runner.",
|
|
16364
|
+
"Codex is running on this computer and connected to Linzumi.",
|
|
16365
|
+
"Active Codex jobs appear below as they run.",
|
|
16366
|
+
"You can return to the Linzumi app now."
|
|
16367
|
+
];
|
|
16368
|
+
runnerWelcomeCompactHeaderLines = [
|
|
16369
|
+
"\u2593\u2593\u2557 LINZUMI RUNNER \u2593\u2593\u2557",
|
|
16370
|
+
"\u2551\u2551 ()-().----. \u2551\u2551",
|
|
16371
|
+
"\u2568\u2568\u2500\u2500 connected to Codex and Linzumi \u2500\u2500\u2568\u2568",
|
|
16372
|
+
"Welcome to the Linzumi runner.",
|
|
16373
|
+
"Codex is running on this computer and connected to Linzumi.",
|
|
16374
|
+
"Active Codex jobs appear below as they run.",
|
|
16375
|
+
"You can return to the Linzumi app now."
|
|
16376
|
+
];
|
|
16377
|
+
minimumVisibleTuiTableRows = 6;
|
|
15923
16378
|
redrawScreen = (screen) => {
|
|
15924
16379
|
process.stdout.write(`\x1B[2J\x1B[H${screen}`);
|
|
15925
16380
|
};
|
|
@@ -15932,19 +16387,1052 @@ var init_runnerConsoleReporter = __esm({
|
|
|
15932
16387
|
}
|
|
15933
16388
|
});
|
|
15934
16389
|
|
|
15935
|
-
// src/
|
|
15936
|
-
import {
|
|
16390
|
+
// src/telemetry.ts
|
|
16391
|
+
import { mkdirSync as mkdirSync8, readFileSync as readFileSync9, writeFileSync as writeFileSync6 } from "node:fs";
|
|
16392
|
+
import { dirname as dirname8, basename as basename5, join as join12 } from "node:path";
|
|
16393
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
16394
|
+
function peekFlagValue(args, flags) {
|
|
16395
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
16396
|
+
const arg = args[index] ?? "";
|
|
16397
|
+
for (const flag of flags) {
|
|
16398
|
+
if (arg.startsWith(`${flag}=`)) {
|
|
16399
|
+
return arg.slice(flag.length + 1);
|
|
16400
|
+
}
|
|
16401
|
+
}
|
|
16402
|
+
const value = args[index + 1];
|
|
16403
|
+
if (flags.includes(arg) && value !== void 0 && !value.startsWith("--")) {
|
|
16404
|
+
return value;
|
|
16405
|
+
}
|
|
16406
|
+
}
|
|
16407
|
+
return void 0;
|
|
16408
|
+
}
|
|
16409
|
+
function telemetryDisabled(env = process.env) {
|
|
16410
|
+
const optOut = env.LINZUMI_TELEMETRY?.trim().toLowerCase();
|
|
16411
|
+
if (optOut === "0" || optOut === "false") {
|
|
16412
|
+
return true;
|
|
16413
|
+
}
|
|
16414
|
+
return env.NODE_ENV === "test" || env.VITEST !== void 0;
|
|
16415
|
+
}
|
|
16416
|
+
function telemetryBaseUrl(apiUrl, env = process.env) {
|
|
16417
|
+
const override = env.LINZUMI_TELEMETRY_BASE_URL?.trim();
|
|
16418
|
+
if (override !== void 0 && override !== "") {
|
|
16419
|
+
return override.replace(/\/+$/, "");
|
|
16420
|
+
}
|
|
16421
|
+
if (apiUrl === void 0 || apiUrl.trim() === "") {
|
|
16422
|
+
return defaultLinzumiHttpUrl;
|
|
16423
|
+
}
|
|
16424
|
+
try {
|
|
16425
|
+
return kandanHttpBaseUrl(apiUrl);
|
|
16426
|
+
} catch {
|
|
16427
|
+
return defaultLinzumiHttpUrl;
|
|
16428
|
+
}
|
|
16429
|
+
}
|
|
16430
|
+
function telemetryInstallIdPath(env = process.env) {
|
|
16431
|
+
return join12(dirname8(localConfigPath(env)), "install-id");
|
|
16432
|
+
}
|
|
16433
|
+
function ensureTelemetryInstallId(env = process.env) {
|
|
16434
|
+
const path2 = telemetryInstallIdPath(env);
|
|
16435
|
+
try {
|
|
16436
|
+
const existing = readFileSync9(path2, "utf8").trim();
|
|
16437
|
+
if (existing !== "" && existing.length <= 128) {
|
|
16438
|
+
return existing;
|
|
16439
|
+
}
|
|
16440
|
+
} catch {
|
|
16441
|
+
}
|
|
16442
|
+
const installId = randomUUID3();
|
|
16443
|
+
try {
|
|
16444
|
+
mkdirSync8(dirname8(path2), { recursive: true });
|
|
16445
|
+
writeFileSync6(path2, `${installId}
|
|
16446
|
+
`, { mode: 384 });
|
|
16447
|
+
} catch {
|
|
16448
|
+
}
|
|
16449
|
+
return installId;
|
|
16450
|
+
}
|
|
16451
|
+
function buildLifecyclePayload(event, context) {
|
|
16452
|
+
const severity = event.severity ?? "INFO";
|
|
16453
|
+
return {
|
|
16454
|
+
resourceLogs: [
|
|
16455
|
+
{
|
|
16456
|
+
resource: {
|
|
16457
|
+
attributes: otlpAttributes({
|
|
16458
|
+
"service.name": "linzumi-cli",
|
|
16459
|
+
"service.version": linzumiCliVersion,
|
|
16460
|
+
"linzumi.install_id": context.installId,
|
|
16461
|
+
"os.platform": context.platform ?? process.platform,
|
|
16462
|
+
"host.arch": context.arch ?? process.arch
|
|
16463
|
+
})
|
|
16464
|
+
},
|
|
16465
|
+
scopeLogs: [
|
|
16466
|
+
{
|
|
16467
|
+
scope: { name: "linzumi-cli" },
|
|
16468
|
+
logRecords: [
|
|
16469
|
+
{
|
|
16470
|
+
timeUnixNano: context.timeUnixNano ?? `${BigInt(Date.now()) * 1000000n}`,
|
|
16471
|
+
severityNumber: severity === "ERROR" ? 17 : 9,
|
|
16472
|
+
severityText: severity,
|
|
16473
|
+
body: { stringValue: event.name },
|
|
16474
|
+
attributes: otlpAttributes(event.attributes ?? {})
|
|
16475
|
+
}
|
|
16476
|
+
]
|
|
16477
|
+
}
|
|
16478
|
+
]
|
|
16479
|
+
}
|
|
16480
|
+
]
|
|
16481
|
+
};
|
|
16482
|
+
}
|
|
16483
|
+
function otlpAttributes(attributes) {
|
|
16484
|
+
return Object.entries(attributes).map(([key, value]) => ({
|
|
16485
|
+
key,
|
|
16486
|
+
value: otlpAttributeValue(value)
|
|
16487
|
+
}));
|
|
16488
|
+
}
|
|
16489
|
+
function otlpAttributeValue(value) {
|
|
16490
|
+
switch (typeof value) {
|
|
16491
|
+
case "string":
|
|
16492
|
+
return { stringValue: value };
|
|
16493
|
+
case "boolean":
|
|
16494
|
+
return { boolValue: value };
|
|
16495
|
+
default:
|
|
16496
|
+
return Number.isSafeInteger(value) ? { intValue: `${value}` } : { doubleValue: value };
|
|
16497
|
+
}
|
|
16498
|
+
}
|
|
16499
|
+
function lifecycleErrorAttributes(error) {
|
|
16500
|
+
const errorClass = error instanceof Error ? error.constructor.name : typeof error;
|
|
16501
|
+
const rawMessage = error instanceof Error ? error.message : String(error);
|
|
16502
|
+
const withoutPaths = rawMessage.replace(
|
|
16503
|
+
/(?:[A-Za-z]:)?(?:[\\/][^\s\\/:"']+){2,}/g,
|
|
16504
|
+
(path2) => basename5(path2)
|
|
16505
|
+
);
|
|
16506
|
+
const message = withoutPaths.length > maxErrorMessageLength ? `${withoutPaths.slice(0, maxErrorMessageLength)}\u2026` : withoutPaths;
|
|
16507
|
+
return { error_class: errorClass, error_message: message };
|
|
16508
|
+
}
|
|
16509
|
+
function postLifecycleEvent(event, opts = {}) {
|
|
16510
|
+
try {
|
|
16511
|
+
const env = opts.env ?? process.env;
|
|
16512
|
+
if (telemetryDisabled(env)) {
|
|
16513
|
+
return Promise.resolve();
|
|
16514
|
+
}
|
|
16515
|
+
const payload = buildLifecyclePayload(event, {
|
|
16516
|
+
installId: ensureTelemetryInstallId(env)
|
|
16517
|
+
});
|
|
16518
|
+
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
16519
|
+
return Promise.resolve().then(
|
|
16520
|
+
() => fetchImpl(
|
|
16521
|
+
`${telemetryBaseUrl(opts.apiUrl, env)}${telemetryEndpointPath}`,
|
|
16522
|
+
{
|
|
16523
|
+
method: "POST",
|
|
16524
|
+
headers: { "content-type": "application/json" },
|
|
16525
|
+
body: JSON.stringify(payload),
|
|
16526
|
+
// AbortSignal.timeout timers are unref'd by Node, so a pending
|
|
16527
|
+
// telemetry request never keeps a short-lived process alive past
|
|
16528
|
+
// the bounded window and never delays process exit.
|
|
16529
|
+
signal: AbortSignal.timeout(
|
|
16530
|
+
opts.timeoutMs ?? defaultTelemetryTimeoutMs
|
|
16531
|
+
)
|
|
16532
|
+
}
|
|
16533
|
+
)
|
|
16534
|
+
).then((response) => {
|
|
16535
|
+
void response.arrayBuffer().catch(() => void 0);
|
|
16536
|
+
}).catch(() => void 0);
|
|
16537
|
+
} catch {
|
|
16538
|
+
return Promise.resolve();
|
|
16539
|
+
}
|
|
16540
|
+
}
|
|
16541
|
+
var telemetryEndpointPath, defaultTelemetryTimeoutMs, maxErrorMessageLength;
|
|
16542
|
+
var init_telemetry = __esm({
|
|
16543
|
+
"src/telemetry.ts"() {
|
|
16544
|
+
"use strict";
|
|
16545
|
+
init_localConfig();
|
|
16546
|
+
init_oauth();
|
|
16547
|
+
init_defaultUrls();
|
|
16548
|
+
init_version();
|
|
16549
|
+
telemetryEndpointPath = "/api/v1/observability/logs";
|
|
16550
|
+
defaultTelemetryTimeoutMs = 2e3;
|
|
16551
|
+
maxErrorMessageLength = 200;
|
|
16552
|
+
}
|
|
16553
|
+
});
|
|
16554
|
+
|
|
16555
|
+
// src/linzumiApiClient.ts
|
|
16556
|
+
function createLinzumiMcpApiClient(options) {
|
|
16557
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
16558
|
+
const baseUrl = kandanHttpBaseUrl(options.kandanUrl);
|
|
16559
|
+
const apiPrefix = options.authMode === "personal-agent-delegation" ? "/api/v2/personal-agent-mcp" : "/api/v2/local-runner-mcp";
|
|
16560
|
+
const operatingMode = options.operatingMode ?? "text";
|
|
16561
|
+
const request = async (method, path2, params) => {
|
|
16562
|
+
const url = new URL(path2, baseUrl);
|
|
16563
|
+
const paramsWithMode = {
|
|
16564
|
+
...params,
|
|
16565
|
+
operating_mode: operatingMode
|
|
16566
|
+
};
|
|
16567
|
+
const requestInit = {
|
|
16568
|
+
method,
|
|
16569
|
+
headers: { authorization: `Bearer ${options.accessToken}` }
|
|
16570
|
+
};
|
|
16571
|
+
if (method === "GET") {
|
|
16572
|
+
for (const [key, value] of Object.entries(paramsWithMode)) {
|
|
16573
|
+
if (value !== void 0 && value !== null) {
|
|
16574
|
+
url.searchParams.set(key, String(value));
|
|
16575
|
+
}
|
|
16576
|
+
}
|
|
16577
|
+
} else {
|
|
16578
|
+
requestInit.headers = {
|
|
16579
|
+
...requestInit.headers,
|
|
16580
|
+
"content-type": "application/json"
|
|
16581
|
+
};
|
|
16582
|
+
requestInit.body = JSON.stringify(paramsWithMode);
|
|
16583
|
+
}
|
|
16584
|
+
const response = await fetchImpl(url, requestInit);
|
|
16585
|
+
const parsed = await response.json();
|
|
16586
|
+
const body = isJsonObject(parsed) ? parsed : void 0;
|
|
16587
|
+
if (body === void 0) {
|
|
16588
|
+
throw new Error(`Linzumi MCP API returned non-object JSON from ${path2}`);
|
|
16589
|
+
}
|
|
16590
|
+
if (response.ok && body.ok === true) {
|
|
16591
|
+
return body;
|
|
16592
|
+
}
|
|
16593
|
+
const error = typeof body.error === "string" ? body.error : `HTTP ${response.status}`;
|
|
16594
|
+
throw new Error(`Linzumi MCP API ${path2} failed: ${error}`);
|
|
16595
|
+
};
|
|
16596
|
+
return {
|
|
16597
|
+
validateAuth: () => request("GET", `${apiPrefix}/validate`, {}),
|
|
16598
|
+
issueOnboardingDiscoveryToken: (params) => request("POST", `${apiPrefix}/onboarding-discovery-token`, params),
|
|
16599
|
+
getMessage: (params) => request("GET", `${apiPrefix}/message`, params),
|
|
16600
|
+
getThread: (params) => request("GET", `${apiPrefix}/thread`, params),
|
|
16601
|
+
getChannel: (params) => request("GET", `${apiPrefix}/channel`, params),
|
|
16602
|
+
getCodingJobMetadata: (params) => request("GET", `${apiPrefix}/coding-job-metadata`, params),
|
|
16603
|
+
renameCodingJob: (params) => request("POST", `${apiPrefix}/coding-job-title`, params),
|
|
16604
|
+
upsertCodingJobPlan: (params) => request("POST", `${apiPrefix}/coding-job-plan`, params),
|
|
16605
|
+
replaceCodingJobPlanSteps: (params) => request("POST", `${apiPrefix}/coding-job-plan/steps`, params),
|
|
16606
|
+
updateCodingJobPlanStep: (params) => request("POST", `${apiPrefix}/coding-job-plan/step`, params),
|
|
16607
|
+
linkCodingJobPullRequest: (params) => request("POST", `${apiPrefix}/coding-job-primary-pr`, params),
|
|
16608
|
+
listVaultSecrets: (params) => request("GET", `${apiPrefix}/vault-secrets`, params),
|
|
16609
|
+
noteProjectDirectory: (params) => request("POST", `${apiPrefix}/discovered-project`, params),
|
|
16610
|
+
noteAgentConversation: (params) => request("POST", `${apiPrefix}/discovered-conversation`, params),
|
|
16611
|
+
noteOnboardingDiscoveryStatus: (params) => request("POST", `${apiPrefix}/discovery-status`, params),
|
|
16612
|
+
sendChannelMessage: (params) => request("POST", `${apiPrefix}/channel-message`, params),
|
|
16613
|
+
prepareMessageUploads: (params) => request("POST", `${apiPrefix}/message-uploads/prepare`, params),
|
|
16614
|
+
attachMessageFiles: (params) => request("POST", `${apiPrefix}/message-files/attach`, params),
|
|
16615
|
+
prepareCustomEmoji: (params) => request("POST", `${apiPrefix}/custom-emoji/prepare`, params),
|
|
16616
|
+
renameCustomEmoji: (params) => request("POST", `${apiPrefix}/custom-emoji/rename`, params),
|
|
16617
|
+
sendThreadReply: (params) => request("POST", `${apiPrefix}/thread-reply`, params),
|
|
16618
|
+
sendDm: (params) => request("POST", `${apiPrefix}/dm`, params),
|
|
16619
|
+
dmOwner: (params) => request("POST", `${apiPrefix}/dm-owner`, params),
|
|
16620
|
+
getVaultValues: (params) => request("POST", `${apiPrefix}/vault-values`, params)
|
|
16621
|
+
};
|
|
16622
|
+
}
|
|
16623
|
+
var init_linzumiApiClient = __esm({
|
|
16624
|
+
"src/linzumiApiClient.ts"() {
|
|
16625
|
+
"use strict";
|
|
16626
|
+
init_oauth();
|
|
16627
|
+
init_protocol();
|
|
16628
|
+
}
|
|
16629
|
+
});
|
|
16630
|
+
|
|
16631
|
+
// src/onboardingConversationDiscovery.ts
|
|
16632
|
+
import {
|
|
16633
|
+
closeSync as closeSync2,
|
|
16634
|
+
existsSync as existsSync8,
|
|
16635
|
+
openSync as openSync3,
|
|
16636
|
+
readdirSync as readdirSync2,
|
|
16637
|
+
readFileSync as readFileSync10,
|
|
16638
|
+
readSync,
|
|
16639
|
+
statSync
|
|
16640
|
+
} from "node:fs";
|
|
16641
|
+
import { basename as basename6, join as join13 } from "node:path";
|
|
15937
16642
|
import { homedir as homedir9 } from "node:os";
|
|
15938
|
-
|
|
16643
|
+
function discoverLocalConversations(options = {}) {
|
|
16644
|
+
const startedAtMs = options.nowMs ?? Date.now();
|
|
16645
|
+
const homeDir = options.homeDir ?? homedir9();
|
|
16646
|
+
const env = options.env ?? process.env;
|
|
16647
|
+
const codexRoot = join13(env.CODEX_HOME ?? join13(homeDir, ".codex"), "sessions");
|
|
16648
|
+
const claudeRoot = join13(
|
|
16649
|
+
env.CLAUDE_CONFIG_DIR ?? join13(homeDir, ".claude"),
|
|
16650
|
+
"projects"
|
|
16651
|
+
);
|
|
16652
|
+
const codexStartedAtMs = Date.now();
|
|
16653
|
+
const codexFiles = listJsonlFiles(codexRoot);
|
|
16654
|
+
const codexDurationMs = Date.now() - codexStartedAtMs;
|
|
16655
|
+
const claudeStartedAtMs = Date.now();
|
|
16656
|
+
const claudeFiles = listJsonlFiles(claudeRoot);
|
|
16657
|
+
const claudeDurationMs = Date.now() - claudeStartedAtMs;
|
|
16658
|
+
const claudeTopLevelFiles = claudeFiles.filter(
|
|
16659
|
+
(filePath) => !filePath.replaceAll("\\", "/").includes("/subagents/")
|
|
16660
|
+
);
|
|
16661
|
+
const candidateLimit = normalizeCandidateLimit(options.candidateLimit);
|
|
16662
|
+
const candidateRecords = [
|
|
16663
|
+
...conversationFileRecords("codex", codexFiles),
|
|
16664
|
+
...conversationFileRecords("claude_code", claudeTopLevelFiles)
|
|
16665
|
+
].sort(compareConversationFileRecords).slice(0, candidateLimit);
|
|
16666
|
+
const candidates = candidateRecords.flatMap((record) => {
|
|
16667
|
+
const candidate = candidateFromRecord(record);
|
|
16668
|
+
return candidate === void 0 ? [] : [candidate];
|
|
16669
|
+
});
|
|
16670
|
+
return {
|
|
16671
|
+
candidates,
|
|
16672
|
+
codexCount: codexFiles.length,
|
|
16673
|
+
claudeCodeCount: claudeTopLevelFiles.length,
|
|
16674
|
+
claudeCodeSubagentCount: claudeFiles.length - claudeTopLevelFiles.length,
|
|
16675
|
+
placesSearched: existingPathCount([codexRoot, claudeRoot]),
|
|
16676
|
+
durationMs: Math.max(0, Date.now() - startedAtMs),
|
|
16677
|
+
searchStats: [
|
|
16678
|
+
{
|
|
16679
|
+
place: codexRoot,
|
|
16680
|
+
source: "codex",
|
|
16681
|
+
duration_ms: codexDurationMs,
|
|
16682
|
+
result_count: codexFiles.length
|
|
16683
|
+
},
|
|
16684
|
+
{
|
|
16685
|
+
place: claudeRoot,
|
|
16686
|
+
source: "claude_code",
|
|
16687
|
+
duration_ms: claudeDurationMs,
|
|
16688
|
+
result_count: claudeTopLevelFiles.length,
|
|
16689
|
+
subagent_result_count: claudeFiles.length - claudeTopLevelFiles.length
|
|
16690
|
+
}
|
|
16691
|
+
]
|
|
16692
|
+
};
|
|
16693
|
+
}
|
|
16694
|
+
function readLocalConversationImportMessages(candidate) {
|
|
16695
|
+
const lines = jsonLines(candidate.path);
|
|
16696
|
+
switch (candidate.source) {
|
|
16697
|
+
case "codex":
|
|
16698
|
+
return codexImportMessages(lines);
|
|
16699
|
+
case "claude_code":
|
|
16700
|
+
return claudeImportMessages(lines);
|
|
16701
|
+
}
|
|
16702
|
+
}
|
|
16703
|
+
function normalizeCandidateLimit(value) {
|
|
16704
|
+
if (value === void 0) {
|
|
16705
|
+
return defaultCandidateLimit;
|
|
16706
|
+
}
|
|
16707
|
+
if (Number.isFinite(value) && value > 0) {
|
|
16708
|
+
return Math.floor(value);
|
|
16709
|
+
}
|
|
16710
|
+
return defaultCandidateLimit;
|
|
16711
|
+
}
|
|
16712
|
+
function conversationFileRecords(source, paths) {
|
|
16713
|
+
return paths.flatMap((path2) => {
|
|
16714
|
+
try {
|
|
16715
|
+
return [
|
|
16716
|
+
{
|
|
16717
|
+
source,
|
|
16718
|
+
path: path2,
|
|
16719
|
+
mtimeMs: statSync(path2).mtimeMs
|
|
16720
|
+
}
|
|
16721
|
+
];
|
|
16722
|
+
} catch (_error) {
|
|
16723
|
+
return [];
|
|
16724
|
+
}
|
|
16725
|
+
});
|
|
16726
|
+
}
|
|
16727
|
+
function compareConversationFileRecords(left, right) {
|
|
16728
|
+
return right.mtimeMs - left.mtimeMs || left.path.localeCompare(right.path);
|
|
16729
|
+
}
|
|
16730
|
+
function candidateFromRecord(record) {
|
|
16731
|
+
switch (record.source) {
|
|
16732
|
+
case "codex":
|
|
16733
|
+
return codexCandidateFromPath(
|
|
16734
|
+
record.path,
|
|
16735
|
+
new Date(record.mtimeMs).toISOString()
|
|
16736
|
+
);
|
|
16737
|
+
case "claude_code":
|
|
16738
|
+
return claudeCandidateFromPath(
|
|
16739
|
+
record.path,
|
|
16740
|
+
new Date(record.mtimeMs).toISOString()
|
|
16741
|
+
);
|
|
16742
|
+
}
|
|
16743
|
+
}
|
|
16744
|
+
function listJsonlFiles(root) {
|
|
16745
|
+
if (!existsSync8(root)) {
|
|
16746
|
+
return [];
|
|
16747
|
+
}
|
|
16748
|
+
const walk = (directory) => {
|
|
16749
|
+
let entries;
|
|
16750
|
+
try {
|
|
16751
|
+
entries = readdirSync2(directory, { withFileTypes: true });
|
|
16752
|
+
} catch (_error) {
|
|
16753
|
+
return [];
|
|
16754
|
+
}
|
|
16755
|
+
return entries.flatMap((entry) => {
|
|
16756
|
+
const filePath = join13(directory, entry.name);
|
|
16757
|
+
if (entry.isDirectory()) {
|
|
16758
|
+
return walk(filePath);
|
|
16759
|
+
}
|
|
16760
|
+
return entry.isFile() && entry.name.endsWith(".jsonl") ? [filePath] : [];
|
|
16761
|
+
});
|
|
16762
|
+
};
|
|
16763
|
+
return walk(root);
|
|
16764
|
+
}
|
|
16765
|
+
function existingPathCount(paths) {
|
|
16766
|
+
return paths.filter((path2) => existsSync8(path2)).length;
|
|
16767
|
+
}
|
|
16768
|
+
function codexCandidateFromPath(path2, activityAt) {
|
|
16769
|
+
const lines = boundedJsonLines(path2);
|
|
16770
|
+
const sessionMeta = objectValue(
|
|
16771
|
+
lines.find((line) => line.type === "session_meta")?.payload
|
|
16772
|
+
);
|
|
16773
|
+
const sessionId = stringValue(sessionMeta?.id) ?? sessionIdFromFilename(path2);
|
|
16774
|
+
const cwd = stringValue(sessionMeta?.cwd);
|
|
16775
|
+
const firstUserMessage = firstCodexUserMessage(lines);
|
|
16776
|
+
if (firstUserMessage === void 0 && hasInternalCodexDiscoveryPrompt(lines)) {
|
|
16777
|
+
return void 0;
|
|
16778
|
+
}
|
|
16779
|
+
const displayName = cleanConversationTitle(firstUserMessage);
|
|
16780
|
+
if (displayName === void 0) {
|
|
16781
|
+
return void 0;
|
|
16782
|
+
}
|
|
16783
|
+
return {
|
|
16784
|
+
source: "codex",
|
|
16785
|
+
importKey: sessionId ?? path2,
|
|
16786
|
+
path: path2,
|
|
16787
|
+
displayName,
|
|
16788
|
+
activityAt,
|
|
16789
|
+
metadata: compactJsonObject({
|
|
16790
|
+
session_id: sessionId,
|
|
16791
|
+
project_path: cwd,
|
|
16792
|
+
title: displayName,
|
|
16793
|
+
first_user_message: cleanConversationTitle(firstUserMessage),
|
|
16794
|
+
discovery_method: "local_filesystem"
|
|
16795
|
+
})
|
|
16796
|
+
};
|
|
16797
|
+
}
|
|
16798
|
+
function claudeCandidateFromPath(path2, activityAt) {
|
|
16799
|
+
const lines = boundedJsonLines(path2);
|
|
16800
|
+
const sessionId = lines.map((line) => stringValue(line.sessionId)).find(Boolean) ?? basename6(path2, ".jsonl");
|
|
16801
|
+
const cwd = lines.map((line) => stringValue(line.cwd)).find(Boolean);
|
|
16802
|
+
const aiTitle = lines.map((line) => stringValue(line.aiTitle)).find(Boolean);
|
|
16803
|
+
const firstUserMessage = firstClaudeUserMessage(lines);
|
|
16804
|
+
const displayName = cleanConversationTitle(aiTitle) ?? cleanConversationTitle(firstUserMessage);
|
|
16805
|
+
if (displayName === void 0) {
|
|
16806
|
+
return void 0;
|
|
16807
|
+
}
|
|
16808
|
+
return {
|
|
16809
|
+
source: "claude_code",
|
|
16810
|
+
importKey: sessionId,
|
|
16811
|
+
path: path2,
|
|
16812
|
+
displayName,
|
|
16813
|
+
activityAt,
|
|
16814
|
+
metadata: compactJsonObject({
|
|
16815
|
+
session_id: sessionId,
|
|
16816
|
+
project_path: cwd,
|
|
16817
|
+
title: displayName,
|
|
16818
|
+
first_user_message: cleanConversationTitle(firstUserMessage),
|
|
16819
|
+
discovery_method: "local_filesystem"
|
|
16820
|
+
})
|
|
16821
|
+
};
|
|
16822
|
+
}
|
|
16823
|
+
function boundedJsonLines(path2) {
|
|
16824
|
+
return boundedTranscriptText(path2).flatMap((line) => {
|
|
16825
|
+
try {
|
|
16826
|
+
const parsed = JSON.parse(line);
|
|
16827
|
+
return isJsonObject(parsed) ? [parsed] : [];
|
|
16828
|
+
} catch (_error) {
|
|
16829
|
+
return [];
|
|
16830
|
+
}
|
|
16831
|
+
});
|
|
16832
|
+
}
|
|
16833
|
+
function jsonLines(path2) {
|
|
16834
|
+
try {
|
|
16835
|
+
return readFileSync10(path2, "utf8").split("\n").filter((line) => line.trim() !== "").flatMap((line) => {
|
|
16836
|
+
try {
|
|
16837
|
+
const parsed = JSON.parse(line);
|
|
16838
|
+
return isJsonObject(parsed) ? [parsed] : [];
|
|
16839
|
+
} catch (_error) {
|
|
16840
|
+
return [];
|
|
16841
|
+
}
|
|
16842
|
+
});
|
|
16843
|
+
} catch (_error) {
|
|
16844
|
+
return [];
|
|
16845
|
+
}
|
|
16846
|
+
}
|
|
16847
|
+
function codexImportMessages(lines) {
|
|
16848
|
+
return lines.flatMap((line, index) => {
|
|
16849
|
+
if (line.type !== "response_item") {
|
|
16850
|
+
return [];
|
|
16851
|
+
}
|
|
16852
|
+
const payload = objectValue(line.payload);
|
|
16853
|
+
const role = importRole(stringValue(payload?.role));
|
|
16854
|
+
const content = payload?.content;
|
|
16855
|
+
const body = importMessageBody(textFromContent(content), role);
|
|
16856
|
+
const attachments = attachmentsFromContent(content);
|
|
16857
|
+
const activityAt = importMessageActivityAt(line, payload);
|
|
16858
|
+
if (body === void 0 && attachments.length === 0) {
|
|
16859
|
+
return [];
|
|
16860
|
+
}
|
|
16861
|
+
return [
|
|
16862
|
+
{
|
|
16863
|
+
itemKey: `codex:${index}`,
|
|
16864
|
+
role,
|
|
16865
|
+
body: body ?? attachmentOnlyBody(role, attachments.length),
|
|
16866
|
+
attachments,
|
|
16867
|
+
activityAt
|
|
16868
|
+
}
|
|
16869
|
+
];
|
|
16870
|
+
});
|
|
16871
|
+
}
|
|
16872
|
+
function claudeImportMessages(lines) {
|
|
16873
|
+
return lines.flatMap((line, index) => {
|
|
16874
|
+
const message = objectValue(line.message);
|
|
16875
|
+
const role = importRole(
|
|
16876
|
+
stringValue(message?.role) ?? stringValue(line.type)
|
|
16877
|
+
);
|
|
16878
|
+
const content = message?.content ?? line.content;
|
|
16879
|
+
const body = importMessageBody(textFromContent(content), role);
|
|
16880
|
+
const attachments = attachmentsFromContent(content);
|
|
16881
|
+
const activityAt = importMessageActivityAt(line, message);
|
|
16882
|
+
if (body === void 0 && attachments.length === 0) {
|
|
16883
|
+
return [];
|
|
16884
|
+
}
|
|
16885
|
+
return [
|
|
16886
|
+
{
|
|
16887
|
+
itemKey: `claude:${index}`,
|
|
16888
|
+
role,
|
|
16889
|
+
body: body ?? attachmentOnlyBody(role, attachments.length),
|
|
16890
|
+
attachments,
|
|
16891
|
+
activityAt
|
|
16892
|
+
}
|
|
16893
|
+
];
|
|
16894
|
+
});
|
|
16895
|
+
}
|
|
16896
|
+
function importMessageActivityAt(line, payload) {
|
|
16897
|
+
return [
|
|
16898
|
+
stringValue(line.timestamp),
|
|
16899
|
+
stringValue(payload?.timestamp),
|
|
16900
|
+
stringValue(line.created_at),
|
|
16901
|
+
stringValue(payload?.created_at),
|
|
16902
|
+
stringValue(line.createdAt),
|
|
16903
|
+
stringValue(payload?.createdAt),
|
|
16904
|
+
stringValue(line.updated_at),
|
|
16905
|
+
stringValue(payload?.updated_at)
|
|
16906
|
+
].find(
|
|
16907
|
+
(candidate) => candidate !== void 0 && !Number.isNaN(Date.parse(candidate))
|
|
16908
|
+
);
|
|
16909
|
+
}
|
|
16910
|
+
function importRole(value) {
|
|
16911
|
+
switch (value) {
|
|
16912
|
+
case "assistant":
|
|
16913
|
+
return "assistant";
|
|
16914
|
+
case "system":
|
|
16915
|
+
return "system";
|
|
16916
|
+
case "tool":
|
|
16917
|
+
case "tool_result":
|
|
16918
|
+
case "function_call_output":
|
|
16919
|
+
return "tool";
|
|
16920
|
+
default:
|
|
16921
|
+
return "user";
|
|
16922
|
+
}
|
|
16923
|
+
}
|
|
16924
|
+
function importMessageBody(textParts, _role) {
|
|
16925
|
+
const text2 = textParts.map((part) => part.trim()).filter((part) => !isLowSignalTranscriptMarker(part)).filter((part) => part !== "").join("\n\n").trim();
|
|
16926
|
+
if (text2 === "") {
|
|
16927
|
+
return void 0;
|
|
16928
|
+
}
|
|
16929
|
+
return text2;
|
|
16930
|
+
}
|
|
16931
|
+
function isLowSignalTranscriptMarker(value) {
|
|
16932
|
+
const collapsed = value.replace(/\s+/gu, " ").trim();
|
|
16933
|
+
return collapsed.startsWith("<turn_aborted>") || collapsed.startsWith("<subagent_notification>") || collapsed.startsWith("<environment_context>") || collapsed.startsWith("# AGENTS.md instructions") || collapsed.startsWith("# Context from my IDE setup") || collapsed.startsWith("# Context from my editor");
|
|
16934
|
+
}
|
|
16935
|
+
function attachmentOnlyBody(role, attachmentCount) {
|
|
16936
|
+
return `${importRoleLabel(role)} sent ${attachmentCount} attachment${attachmentCount === 1 ? "" : "s"}`;
|
|
16937
|
+
}
|
|
16938
|
+
function importRoleLabel(role) {
|
|
16939
|
+
switch (role) {
|
|
16940
|
+
case "assistant":
|
|
16941
|
+
return "Assistant";
|
|
16942
|
+
case "system":
|
|
16943
|
+
return "System";
|
|
16944
|
+
case "tool":
|
|
16945
|
+
return "Tool";
|
|
16946
|
+
case "user":
|
|
16947
|
+
return "User";
|
|
16948
|
+
}
|
|
16949
|
+
}
|
|
16950
|
+
function attachmentsFromContent(value) {
|
|
16951
|
+
return arrayValue(value)?.flatMap((entry, index) => {
|
|
16952
|
+
const item = objectValue(entry);
|
|
16953
|
+
if (item === void 0) {
|
|
16954
|
+
return [];
|
|
16955
|
+
}
|
|
16956
|
+
return attachmentFromObject(item, index);
|
|
16957
|
+
}) ?? [];
|
|
16958
|
+
}
|
|
16959
|
+
function attachmentFromObject(item, index) {
|
|
16960
|
+
const localPath = stringValue(item.path) ?? stringValue(item.file_path) ?? stringValue(item.filePath);
|
|
16961
|
+
const fileName = stringValue(item.name) ?? stringValue(item.file_name) ?? stringValue(item.filename);
|
|
16962
|
+
const contentType = stringValue(item.mime_type) ?? stringValue(item.media_type) ?? stringValue(item.content_type);
|
|
16963
|
+
if (localPath !== void 0) {
|
|
16964
|
+
return [
|
|
16965
|
+
{
|
|
16966
|
+
kind: "path",
|
|
16967
|
+
path: localPath,
|
|
16968
|
+
fileName,
|
|
16969
|
+
contentType
|
|
16970
|
+
}
|
|
16971
|
+
];
|
|
16972
|
+
}
|
|
16973
|
+
const source = objectValue(item.source);
|
|
16974
|
+
const dataBase64 = stringValue(item.data) ?? stringValue(source?.data) ?? dataUrlBase64(item);
|
|
16975
|
+
const mediaType = stringValue(source?.media_type) ?? stringValue(item.media_type) ?? stringValue(item.mime_type) ?? stringValue(item.content_type);
|
|
16976
|
+
if (dataBase64 !== void 0 && mediaType !== void 0) {
|
|
16977
|
+
return [
|
|
16978
|
+
{
|
|
16979
|
+
kind: "bytes",
|
|
16980
|
+
dataBase64,
|
|
16981
|
+
fileName: fileName ?? `conversation-attachment-${index + 1}${extensionForContentType(mediaType)}`,
|
|
16982
|
+
contentType: mediaType
|
|
16983
|
+
}
|
|
16984
|
+
];
|
|
16985
|
+
}
|
|
16986
|
+
return [];
|
|
16987
|
+
}
|
|
16988
|
+
function dataUrlBase64(item) {
|
|
16989
|
+
const imageUrl = stringValue(item.image_url) ?? stringValue(item.url);
|
|
16990
|
+
if (imageUrl === void 0) {
|
|
16991
|
+
return void 0;
|
|
16992
|
+
}
|
|
16993
|
+
const match = imageUrl.match(/^data:[^;,]+;base64,(.+)$/u);
|
|
16994
|
+
return match?.[1];
|
|
16995
|
+
}
|
|
16996
|
+
function extensionForContentType(contentType) {
|
|
16997
|
+
switch (contentType) {
|
|
16998
|
+
case "image/jpeg":
|
|
16999
|
+
return ".jpg";
|
|
17000
|
+
case "image/png":
|
|
17001
|
+
return ".png";
|
|
17002
|
+
case "image/gif":
|
|
17003
|
+
return ".gif";
|
|
17004
|
+
case "image/webp":
|
|
17005
|
+
return ".webp";
|
|
17006
|
+
default:
|
|
17007
|
+
return "";
|
|
17008
|
+
}
|
|
17009
|
+
}
|
|
17010
|
+
function boundedTranscriptText(path2) {
|
|
17011
|
+
let fd;
|
|
17012
|
+
try {
|
|
17013
|
+
const stat2 = statSync(path2);
|
|
17014
|
+
fd = openSync3(path2, "r");
|
|
17015
|
+
const firstBuffer = Buffer.alloc(
|
|
17016
|
+
Math.min(boundedTranscriptBytes, stat2.size)
|
|
17017
|
+
);
|
|
17018
|
+
const firstBytes = readSync(fd, firstBuffer, 0, firstBuffer.length, 0);
|
|
17019
|
+
const firstText = firstBuffer.subarray(0, firstBytes).toString("utf8");
|
|
17020
|
+
if (stat2.size <= boundedTranscriptBytes) {
|
|
17021
|
+
return firstText.split("\n").filter((line) => line.trim() !== "");
|
|
17022
|
+
}
|
|
17023
|
+
const lastBuffer = Buffer.alloc(boundedTranscriptBytes);
|
|
17024
|
+
const lastOffset = Math.max(0, stat2.size - boundedTranscriptBytes);
|
|
17025
|
+
const lastBytes = readSync(
|
|
17026
|
+
fd,
|
|
17027
|
+
lastBuffer,
|
|
17028
|
+
0,
|
|
17029
|
+
lastBuffer.length,
|
|
17030
|
+
lastOffset
|
|
17031
|
+
);
|
|
17032
|
+
const lastText = lastBuffer.subarray(0, lastBytes).toString("utf8");
|
|
17033
|
+
return `${firstText}
|
|
17034
|
+
${lastText}`.split("\n").filter((line) => line.trim() !== "");
|
|
17035
|
+
} catch (_error) {
|
|
17036
|
+
return [];
|
|
17037
|
+
} finally {
|
|
17038
|
+
if (fd !== void 0) {
|
|
17039
|
+
closeSync2(fd);
|
|
17040
|
+
}
|
|
17041
|
+
}
|
|
17042
|
+
}
|
|
17043
|
+
function firstCodexUserMessage(lines) {
|
|
17044
|
+
return lines.filter((line) => line.type === "response_item").map((line) => objectValue(line.payload)).filter((payload) => payload !== void 0).filter((payload) => payload.role === "user").flatMap((payload) => textFromContent(payload.content)).filter((text2) => !isInternalOnboardingDiscoveryPrompt(text2)).map(cleanConversationTitle).find((title) => title !== void 0);
|
|
17045
|
+
}
|
|
17046
|
+
function hasInternalCodexDiscoveryPrompt(lines) {
|
|
17047
|
+
return lines.filter((line) => line.type === "response_item").map((line) => objectValue(line.payload)).filter((payload) => payload !== void 0).filter((payload) => payload.role === "user").flatMap((payload) => textFromContent(payload.content)).some(isInternalOnboardingDiscoveryPrompt);
|
|
17048
|
+
}
|
|
17049
|
+
function firstClaudeUserMessage(lines) {
|
|
17050
|
+
return lines.filter((line) => line.type === "user").map((line) => objectValue(line.message)).flatMap((message) => textFromContent(message?.content)).map(cleanConversationTitle).find((title) => title !== void 0);
|
|
17051
|
+
}
|
|
17052
|
+
function textFromContent(value) {
|
|
17053
|
+
const direct = stringValue(value);
|
|
17054
|
+
if (direct !== void 0) {
|
|
17055
|
+
return [direct];
|
|
17056
|
+
}
|
|
17057
|
+
return arrayValue(value)?.flatMap((entry) => {
|
|
17058
|
+
const item = objectValue(entry);
|
|
17059
|
+
const itemText = stringValue(item?.text);
|
|
17060
|
+
return itemText === void 0 ? [] : [itemText];
|
|
17061
|
+
}) ?? [];
|
|
17062
|
+
}
|
|
17063
|
+
function cleanConversationTitle(value) {
|
|
17064
|
+
if (value === void 0) {
|
|
17065
|
+
return void 0;
|
|
17066
|
+
}
|
|
17067
|
+
const withoutKandanHeader = value.replace(
|
|
17068
|
+
/^Kandan message seq=\d+ from [^\n]+:\n\n/u,
|
|
17069
|
+
""
|
|
17070
|
+
);
|
|
17071
|
+
const withoutUploadBoilerplate = withoutKandanHeader.replace(
|
|
17072
|
+
/\n\nWhen you create files that should be attached back to Linzumi,[\s\S]*$/u,
|
|
17073
|
+
""
|
|
17074
|
+
);
|
|
17075
|
+
const structuredTaskTitle = structuredPromptTaskTitle(
|
|
17076
|
+
withoutUploadBoilerplate
|
|
17077
|
+
);
|
|
17078
|
+
const collapsed = withoutUploadBoilerplate.replace(/\s+/gu, " ").trim();
|
|
17079
|
+
if (collapsed === "") {
|
|
17080
|
+
return void 0;
|
|
17081
|
+
}
|
|
17082
|
+
if (setupPreambleTitle(collapsed)) {
|
|
17083
|
+
return structuredTaskTitle;
|
|
17084
|
+
}
|
|
17085
|
+
return truncateConversationTitle(collapsed);
|
|
17086
|
+
}
|
|
17087
|
+
function isInternalOnboardingDiscoveryPrompt(value) {
|
|
17088
|
+
const collapsed = value.replace(/\s+/gu, " ").trim();
|
|
17089
|
+
return collapsed.startsWith(
|
|
17090
|
+
"You are Linzumi onboarding discovery running on the user computer."
|
|
17091
|
+
) || collapsed.startsWith(
|
|
17092
|
+
"You are Linzumi onboarding conversation discovery running on the user computer."
|
|
17093
|
+
);
|
|
17094
|
+
}
|
|
17095
|
+
function setupPreambleTitle(value) {
|
|
17096
|
+
return value.startsWith("<environment_context>") || value.startsWith("# Context from my IDE setup") || value.startsWith("# Context from my editor") || value.startsWith("# AGENTS.md instructions") || value.includes("<INSTRUCTIONS>") || value.startsWith(
|
|
17097
|
+
"You are Linzumi onboarding discovery running on the user computer."
|
|
17098
|
+
) || value.startsWith(
|
|
17099
|
+
"You are Linzumi onboarding conversation discovery running on the user computer."
|
|
17100
|
+
) || value.startsWith("We need answer user?");
|
|
17101
|
+
}
|
|
17102
|
+
function structuredPromptTaskTitle(value) {
|
|
17103
|
+
const match = value.match(/(?:^|\n)Task:\s*\n+([^\n]+)/u);
|
|
17104
|
+
const taskLine = match?.[1]?.replace(/\s+/gu, " ").trim();
|
|
17105
|
+
return taskLine === void 0 || taskLine === "" ? void 0 : truncateConversationTitle(taskLine);
|
|
17106
|
+
}
|
|
17107
|
+
function truncateConversationTitle(value) {
|
|
17108
|
+
return value.length > titleMaxLength ? `${value.slice(0, titleMaxLength - 3)}...` : value;
|
|
17109
|
+
}
|
|
17110
|
+
function sessionIdFromFilename(path2) {
|
|
17111
|
+
const fileName = basename6(path2, ".jsonl");
|
|
17112
|
+
return fileName === "" ? void 0 : fileName;
|
|
17113
|
+
}
|
|
17114
|
+
function compactJsonObject(value) {
|
|
17115
|
+
return Object.fromEntries(
|
|
17116
|
+
Object.entries(value).filter((entry) => {
|
|
17117
|
+
const entryValue = entry[1];
|
|
17118
|
+
return entryValue !== void 0 && entryValue !== "";
|
|
17119
|
+
})
|
|
17120
|
+
);
|
|
17121
|
+
}
|
|
17122
|
+
var boundedTranscriptBytes, titleMaxLength, defaultCandidateLimit;
|
|
17123
|
+
var init_onboardingConversationDiscovery = __esm({
|
|
17124
|
+
"src/onboardingConversationDiscovery.ts"() {
|
|
17125
|
+
"use strict";
|
|
17126
|
+
init_protocol();
|
|
17127
|
+
init_json();
|
|
17128
|
+
boundedTranscriptBytes = 64 * 1024;
|
|
17129
|
+
titleMaxLength = 140;
|
|
17130
|
+
defaultCandidateLimit = 100;
|
|
17131
|
+
}
|
|
17132
|
+
});
|
|
17133
|
+
|
|
17134
|
+
// src/onboardingProjectDiscovery.ts
|
|
17135
|
+
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
17136
|
+
import { homedir as homedir10 } from "node:os";
|
|
17137
|
+
import { existsSync as existsSync9, readdirSync as readdirSync3, readFileSync as readFileSync11, statSync as statSync2 } from "node:fs";
|
|
17138
|
+
import { basename as basename7, dirname as dirname9, join as join14 } from "node:path";
|
|
17139
|
+
function discoverCurrentGitProject(args) {
|
|
17140
|
+
const startedAtMs = args.nowMs ?? Date.now();
|
|
17141
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
17142
|
+
const searchStats = [];
|
|
17143
|
+
const gitStartedAtMs = Date.now();
|
|
17144
|
+
const topLevel = gitOutput(args.cwd, ["rev-parse", "--show-toplevel"]);
|
|
17145
|
+
const gitDurationMs = Date.now() - gitStartedAtMs;
|
|
17146
|
+
if (topLevel !== void 0) {
|
|
17147
|
+
mergeCandidate(
|
|
17148
|
+
candidates,
|
|
17149
|
+
projectCandidateFromWorktree(topLevel, "local_current_git", topLevel)
|
|
17150
|
+
);
|
|
17151
|
+
}
|
|
17152
|
+
searchStats.push({
|
|
17153
|
+
place: args.cwd,
|
|
17154
|
+
source: "git",
|
|
17155
|
+
duration_ms: gitDurationMs,
|
|
17156
|
+
result_count: topLevel === void 0 ? 0 : 1
|
|
17157
|
+
});
|
|
17158
|
+
const sourceRoots = args.sourceRoots ?? defaultLocalProjectSourceRoots(args.cwd);
|
|
17159
|
+
let placesSearched = 1;
|
|
17160
|
+
for (const sourceRoot of sourceRoots) {
|
|
17161
|
+
const sourceRootStartedAtMs = Date.now();
|
|
17162
|
+
const foundInRoot = discoverGitWorktreesInSourceRoot(sourceRoot);
|
|
17163
|
+
for (const worktreePath of foundInRoot) {
|
|
17164
|
+
mergeCandidate(
|
|
17165
|
+
candidates,
|
|
17166
|
+
projectCandidateFromWorktree(
|
|
17167
|
+
worktreePath,
|
|
17168
|
+
"local_source_root",
|
|
17169
|
+
topLevel
|
|
17170
|
+
)
|
|
17171
|
+
);
|
|
17172
|
+
}
|
|
17173
|
+
searchStats.push({
|
|
17174
|
+
place: sourceRoot,
|
|
17175
|
+
source: "git",
|
|
17176
|
+
duration_ms: Date.now() - sourceRootStartedAtMs,
|
|
17177
|
+
result_count: foundInRoot.length
|
|
17178
|
+
});
|
|
17179
|
+
placesSearched += 1;
|
|
17180
|
+
}
|
|
17181
|
+
const discoveredCandidates = Array.from(candidates.values()).sort(
|
|
17182
|
+
compareProjectCandidates
|
|
17183
|
+
);
|
|
17184
|
+
const worktreeCount = discoveredCandidates.reduce((total, candidate) => {
|
|
17185
|
+
const count = candidate.metadata.worktree_count;
|
|
17186
|
+
return typeof count === "number" ? total + count : total;
|
|
17187
|
+
}, 0);
|
|
17188
|
+
if (worktreeCount > 0) {
|
|
17189
|
+
searchStats.push({
|
|
17190
|
+
place: "git worktree list --porcelain",
|
|
17191
|
+
source: "git",
|
|
17192
|
+
duration_ms: 0,
|
|
17193
|
+
result_count: worktreeCount
|
|
17194
|
+
});
|
|
17195
|
+
placesSearched += 1;
|
|
17196
|
+
}
|
|
17197
|
+
return {
|
|
17198
|
+
candidates: discoveredCandidates,
|
|
17199
|
+
placesSearched,
|
|
17200
|
+
durationMs: Math.max(0, Date.now() - startedAtMs),
|
|
17201
|
+
searchStats
|
|
17202
|
+
};
|
|
17203
|
+
}
|
|
17204
|
+
function projectCandidateFromWorktree(worktreePath, discoveryMethod, currentTopLevel) {
|
|
17205
|
+
const topLevel = gitOutput(worktreePath, ["rev-parse", "--show-toplevel"]);
|
|
17206
|
+
if (topLevel === void 0) {
|
|
17207
|
+
return void 0;
|
|
17208
|
+
}
|
|
17209
|
+
const worktrees = gitWorktrees(topLevel);
|
|
17210
|
+
const canonicalPath = worktrees[0] ?? topLevel;
|
|
17211
|
+
const remoteOrigin = gitOutput(topLevel, [
|
|
17212
|
+
"config",
|
|
17213
|
+
"--get",
|
|
17214
|
+
"remote.origin.url"
|
|
17215
|
+
]);
|
|
17216
|
+
const branch = gitOutput(topLevel, ["branch", "--show-current"]);
|
|
17217
|
+
const headSha = gitOutput(topLevel, ["rev-parse", "HEAD"]);
|
|
17218
|
+
const lastCommitAt = gitOutput(topLevel, ["log", "-1", "--format=%cI"]);
|
|
17219
|
+
const readmeName = readmeProjectName(canonicalPath);
|
|
17220
|
+
const displayName = readmeName ?? basename7(canonicalPath);
|
|
17221
|
+
const importKey = remoteOrigin ?? canonicalPath;
|
|
17222
|
+
const currentWorktreePath = currentTopLevel === void 0 || currentTopLevel === canonicalPath || !worktrees.includes(currentTopLevel) ? void 0 : currentTopLevel;
|
|
17223
|
+
return {
|
|
17224
|
+
importKey,
|
|
17225
|
+
path: canonicalPath,
|
|
17226
|
+
displayName,
|
|
17227
|
+
activityAt: lastCommitAt,
|
|
17228
|
+
metadata: compactJsonObject2({
|
|
17229
|
+
branch,
|
|
17230
|
+
remote_origin: remoteOrigin,
|
|
17231
|
+
head_sha: headSha,
|
|
17232
|
+
discovery_method: discoveryMethod,
|
|
17233
|
+
canonical_path: canonicalPath,
|
|
17234
|
+
current_worktree_path: currentWorktreePath,
|
|
17235
|
+
worktree_count: worktrees.length === 0 ? void 0 : worktrees.length,
|
|
17236
|
+
worktree_paths: worktrees.length === 0 ? void 0 : worktrees.slice(0, worktreePathSampleLimit),
|
|
17237
|
+
worktree_paths_truncated: worktrees.length > worktreePathSampleLimit ? true : void 0,
|
|
17238
|
+
last_commit_at: lastCommitAt,
|
|
17239
|
+
readme_name: readmeName
|
|
17240
|
+
})
|
|
17241
|
+
};
|
|
17242
|
+
}
|
|
17243
|
+
function mergeCandidate(candidates, candidate) {
|
|
17244
|
+
if (candidate === void 0) {
|
|
17245
|
+
return;
|
|
17246
|
+
}
|
|
17247
|
+
const existing = candidates.get(candidate.importKey);
|
|
17248
|
+
if (existing === void 0 || shouldReplaceCandidate(existing, candidate)) {
|
|
17249
|
+
candidates.set(candidate.importKey, candidate);
|
|
17250
|
+
}
|
|
17251
|
+
}
|
|
17252
|
+
function shouldReplaceCandidate(existing, candidate) {
|
|
17253
|
+
const existingMethod = existing.metadata.discovery_method;
|
|
17254
|
+
const candidateMethod = candidate.metadata.discovery_method;
|
|
17255
|
+
if (existingMethod === "local_current_git") {
|
|
17256
|
+
return false;
|
|
17257
|
+
}
|
|
17258
|
+
if (candidateMethod === "local_current_git") {
|
|
17259
|
+
return true;
|
|
17260
|
+
}
|
|
17261
|
+
const existingActivity = Date.parse(existing.activityAt ?? "");
|
|
17262
|
+
const candidateActivity = Date.parse(candidate.activityAt ?? "");
|
|
17263
|
+
if (Number.isNaN(existingActivity)) {
|
|
17264
|
+
return !Number.isNaN(candidateActivity);
|
|
17265
|
+
}
|
|
17266
|
+
if (Number.isNaN(candidateActivity)) {
|
|
17267
|
+
return false;
|
|
17268
|
+
}
|
|
17269
|
+
return candidateActivity > existingActivity;
|
|
17270
|
+
}
|
|
17271
|
+
function compareProjectCandidates(left, right) {
|
|
17272
|
+
const leftActivity = Date.parse(left.activityAt ?? "");
|
|
17273
|
+
const rightActivity = Date.parse(right.activityAt ?? "");
|
|
17274
|
+
if (!Number.isNaN(leftActivity) && !Number.isNaN(rightActivity)) {
|
|
17275
|
+
return rightActivity - leftActivity;
|
|
17276
|
+
}
|
|
17277
|
+
if (!Number.isNaN(leftActivity)) {
|
|
17278
|
+
return -1;
|
|
17279
|
+
}
|
|
17280
|
+
if (!Number.isNaN(rightActivity)) {
|
|
17281
|
+
return 1;
|
|
17282
|
+
}
|
|
17283
|
+
return left.displayName.localeCompare(right.displayName);
|
|
17284
|
+
}
|
|
17285
|
+
function discoverGitWorktreesInSourceRoot(sourceRoot) {
|
|
17286
|
+
if (!isDirectory(sourceRoot)) {
|
|
17287
|
+
return [];
|
|
17288
|
+
}
|
|
17289
|
+
try {
|
|
17290
|
+
return readdirSync3(sourceRoot, { withFileTypes: true }).flatMap((entry) => {
|
|
17291
|
+
if (!entry.isDirectory()) {
|
|
17292
|
+
return [];
|
|
17293
|
+
}
|
|
17294
|
+
const candidatePath = join14(sourceRoot, entry.name);
|
|
17295
|
+
return isGitWorktreeRoot(candidatePath) ? [candidatePath] : [];
|
|
17296
|
+
}).sort();
|
|
17297
|
+
} catch (_error) {
|
|
17298
|
+
return [];
|
|
17299
|
+
}
|
|
17300
|
+
}
|
|
17301
|
+
function isGitWorktreeRoot(path2) {
|
|
17302
|
+
return existsSync9(join14(path2, ".git"));
|
|
17303
|
+
}
|
|
17304
|
+
function defaultLocalProjectSourceRoots(cwd) {
|
|
17305
|
+
return uniqueStrings2([
|
|
17306
|
+
...ancestorSourceRoots(cwd),
|
|
17307
|
+
...homeSourceRoots(),
|
|
17308
|
+
...externalVolumeSourceRoots()
|
|
17309
|
+
]).filter(isDirectory);
|
|
17310
|
+
}
|
|
17311
|
+
function ancestorSourceRoots(cwd) {
|
|
17312
|
+
const sourceRootNames = /* @__PURE__ */ new Set([
|
|
17313
|
+
"code",
|
|
17314
|
+
"src",
|
|
17315
|
+
"projects",
|
|
17316
|
+
"Developer",
|
|
17317
|
+
"workspace",
|
|
17318
|
+
"work"
|
|
17319
|
+
]);
|
|
17320
|
+
const roots = [];
|
|
17321
|
+
let current = cwd;
|
|
17322
|
+
while (current !== dirname9(current)) {
|
|
17323
|
+
if (sourceRootNames.has(basename7(current))) {
|
|
17324
|
+
roots.push(current);
|
|
17325
|
+
}
|
|
17326
|
+
current = dirname9(current);
|
|
17327
|
+
}
|
|
17328
|
+
return roots;
|
|
17329
|
+
}
|
|
17330
|
+
function homeSourceRoots() {
|
|
17331
|
+
const home = homedir10();
|
|
17332
|
+
return [
|
|
17333
|
+
"code",
|
|
17334
|
+
"src",
|
|
17335
|
+
"projects",
|
|
17336
|
+
"Developer",
|
|
17337
|
+
"workspace",
|
|
17338
|
+
"work",
|
|
17339
|
+
"Documents",
|
|
17340
|
+
"Desktop"
|
|
17341
|
+
].map((directoryName) => join14(home, directoryName));
|
|
17342
|
+
}
|
|
17343
|
+
function externalVolumeSourceRoots() {
|
|
17344
|
+
const volumesRoot = "/Volumes";
|
|
17345
|
+
if (!isDirectory(volumesRoot)) {
|
|
17346
|
+
return [];
|
|
17347
|
+
}
|
|
17348
|
+
try {
|
|
17349
|
+
return readdirSync3(volumesRoot, { withFileTypes: true }).flatMap(
|
|
17350
|
+
(entry) => {
|
|
17351
|
+
if (!entry.isDirectory()) {
|
|
17352
|
+
return [];
|
|
17353
|
+
}
|
|
17354
|
+
const volumePath = join14(volumesRoot, entry.name);
|
|
17355
|
+
return ["code", "src", "projects"].map(
|
|
17356
|
+
(directoryName) => join14(volumePath, directoryName)
|
|
17357
|
+
);
|
|
17358
|
+
}
|
|
17359
|
+
);
|
|
17360
|
+
} catch (_error) {
|
|
17361
|
+
return [];
|
|
17362
|
+
}
|
|
17363
|
+
}
|
|
17364
|
+
function isDirectory(path2) {
|
|
17365
|
+
try {
|
|
17366
|
+
return statSync2(path2).isDirectory();
|
|
17367
|
+
} catch (_error) {
|
|
17368
|
+
return false;
|
|
17369
|
+
}
|
|
17370
|
+
}
|
|
17371
|
+
function uniqueStrings2(values) {
|
|
17372
|
+
return Array.from(new Set(values));
|
|
17373
|
+
}
|
|
17374
|
+
function gitOutput(cwd, args) {
|
|
17375
|
+
const result = spawnSync4("git", args, {
|
|
17376
|
+
cwd,
|
|
17377
|
+
encoding: "utf8",
|
|
17378
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
17379
|
+
});
|
|
17380
|
+
if (result.status !== 0) {
|
|
17381
|
+
return void 0;
|
|
17382
|
+
}
|
|
17383
|
+
const output = result.stdout.trim();
|
|
17384
|
+
return output === "" ? void 0 : output;
|
|
17385
|
+
}
|
|
17386
|
+
function gitWorktrees(cwd) {
|
|
17387
|
+
const output = gitOutput(cwd, ["worktree", "list", "--porcelain"]);
|
|
17388
|
+
if (output === void 0) {
|
|
17389
|
+
return [];
|
|
17390
|
+
}
|
|
17391
|
+
return output.split("\n").flatMap(
|
|
17392
|
+
(line) => line.startsWith("worktree ") ? [line.slice("worktree ".length)] : []
|
|
17393
|
+
);
|
|
17394
|
+
}
|
|
17395
|
+
function readmeProjectName(projectPath) {
|
|
17396
|
+
const readmePath = ["README.md", "README.markdown", "readme.md"].map((fileName) => join14(projectPath, fileName)).find((path2) => existsSync9(path2));
|
|
17397
|
+
if (readmePath === void 0) {
|
|
17398
|
+
return void 0;
|
|
17399
|
+
}
|
|
17400
|
+
try {
|
|
17401
|
+
const heading = readFileSync11(readmePath, "utf8").split("\n").map((line) => line.trim()).find((line) => line.startsWith("# "));
|
|
17402
|
+
const title = heading?.replace(/^#+\s+/u, "").trim();
|
|
17403
|
+
return title === void 0 || title === "" ? void 0 : title;
|
|
17404
|
+
} catch (_error) {
|
|
17405
|
+
return void 0;
|
|
17406
|
+
}
|
|
17407
|
+
}
|
|
17408
|
+
function compactJsonObject2(value) {
|
|
17409
|
+
return Object.fromEntries(
|
|
17410
|
+
Object.entries(value).filter((entry) => {
|
|
17411
|
+
const entryValue = entry[1];
|
|
17412
|
+
return entryValue !== void 0 && entryValue !== "";
|
|
17413
|
+
})
|
|
17414
|
+
);
|
|
17415
|
+
}
|
|
17416
|
+
var worktreePathSampleLimit;
|
|
17417
|
+
var init_onboardingProjectDiscovery = __esm({
|
|
17418
|
+
"src/onboardingProjectDiscovery.ts"() {
|
|
17419
|
+
"use strict";
|
|
17420
|
+
worktreePathSampleLimit = 25;
|
|
17421
|
+
}
|
|
17422
|
+
});
|
|
17423
|
+
|
|
17424
|
+
// src/authCache.ts
|
|
17425
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync9, readFileSync as readFileSync12, writeFileSync as writeFileSync7 } from "node:fs";
|
|
17426
|
+
import { homedir as homedir11 } from "node:os";
|
|
17427
|
+
import { dirname as dirname10, join as join15 } from "node:path";
|
|
15939
17428
|
function defaultAuthFilePath() {
|
|
15940
|
-
|
|
15941
|
-
return join12(base, "auth.json");
|
|
17429
|
+
return join15(homedir11(), ".linzumi", "auth.json");
|
|
15942
17430
|
}
|
|
15943
17431
|
function readCachedLocalRunnerToken(kandanUrl, authFilePath = defaultAuthFilePath()) {
|
|
15944
|
-
if (!
|
|
17432
|
+
if (!existsSync10(authFilePath)) {
|
|
15945
17433
|
return void 0;
|
|
15946
17434
|
}
|
|
15947
|
-
const authFile = parseAuthFile(
|
|
17435
|
+
const authFile = parseAuthFile(readFileSync12(authFilePath, "utf8"));
|
|
15948
17436
|
const kandanBaseUrl = kandanHttpBaseUrl(kandanUrl);
|
|
15949
17437
|
const entry = authFile.local_codex_runner?.[kandanBaseUrl];
|
|
15950
17438
|
if (entry === void 0 || entry.access_token.trim() === "") {
|
|
@@ -15961,12 +17449,12 @@ function readCachedLocalRunnerToken(kandanUrl, authFilePath = defaultAuthFilePat
|
|
|
15961
17449
|
};
|
|
15962
17450
|
}
|
|
15963
17451
|
function readPersonalAgentDelegationToken(authFilePath) {
|
|
15964
|
-
if (!
|
|
17452
|
+
if (!existsSync10(authFilePath)) {
|
|
15965
17453
|
throw new Error(
|
|
15966
17454
|
`missing personal-agent delegation auth file: ${authFilePath}`
|
|
15967
17455
|
);
|
|
15968
17456
|
}
|
|
15969
|
-
const authFile = parseAuthFile(
|
|
17457
|
+
const authFile = parseAuthFile(readFileSync12(authFilePath, "utf8"));
|
|
15970
17458
|
const entry = authFile.personal_agent_delegation;
|
|
15971
17459
|
if (entry === void 0 || entry.access_token.trim() === "") {
|
|
15972
17460
|
throw new Error(
|
|
@@ -15984,7 +17472,7 @@ function readPersonalAgentDelegationToken(authFilePath) {
|
|
|
15984
17472
|
}
|
|
15985
17473
|
function writeCachedLocalRunnerToken(args) {
|
|
15986
17474
|
const authFilePath = args.authFilePath ?? defaultAuthFilePath();
|
|
15987
|
-
const existing =
|
|
17475
|
+
const existing = existsSync10(authFilePath) ? parseAuthFile(readFileSync12(authFilePath, "utf8")) : { version: 1 };
|
|
15988
17476
|
const kandanBaseUrl = kandanHttpBaseUrl(args.kandanUrl);
|
|
15989
17477
|
const issuedAt = /* @__PURE__ */ new Date();
|
|
15990
17478
|
const expiresAt = args.expiresInSeconds === void 0 ? void 0 : new Date(
|
|
@@ -16002,8 +17490,8 @@ function writeCachedLocalRunnerToken(args) {
|
|
|
16002
17490
|
}
|
|
16003
17491
|
}
|
|
16004
17492
|
};
|
|
16005
|
-
|
|
16006
|
-
|
|
17493
|
+
mkdirSync9(dirname10(authFilePath), { recursive: true });
|
|
17494
|
+
writeFileSync7(authFilePath, `${JSON.stringify(next, null, 2)}
|
|
16007
17495
|
`, "utf8");
|
|
16008
17496
|
return {
|
|
16009
17497
|
accessToken: args.accessToken,
|
|
@@ -16421,9 +17909,9 @@ var init_threadCodexWorkerIpc = __esm({
|
|
|
16421
17909
|
|
|
16422
17910
|
// src/signupTaskSuggestions.ts
|
|
16423
17911
|
import { spawn as spawn6 } from "node:child_process";
|
|
16424
|
-
import { mkdtempSync as mkdtempSync3, readFileSync as
|
|
17912
|
+
import { mkdtempSync as mkdtempSync3, readFileSync as readFileSync13, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "node:fs";
|
|
16425
17913
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
16426
|
-
import { join as
|
|
17914
|
+
import { join as join16 } from "node:path";
|
|
16427
17915
|
async function suggestSignupTasksWithCodex(args) {
|
|
16428
17916
|
const attempts = 2;
|
|
16429
17917
|
let previousResponse;
|
|
@@ -16445,11 +17933,11 @@ async function suggestSignupTasksWithCodex(args) {
|
|
|
16445
17933
|
);
|
|
16446
17934
|
}
|
|
16447
17935
|
async function runCodexTaskSuggestion(args) {
|
|
16448
|
-
const tempRoot = mkdtempSync3(
|
|
16449
|
-
const schemaPath =
|
|
16450
|
-
const outputPath =
|
|
17936
|
+
const tempRoot = mkdtempSync3(join16(tmpdir2(), "linzumi-signup-codex-tasks-"));
|
|
17937
|
+
const schemaPath = join16(tempRoot, "task-suggestions.schema.json");
|
|
17938
|
+
const outputPath = join16(tempRoot, "task-suggestions.json");
|
|
16451
17939
|
const prompt = taskSuggestionPrompt(args.previousResponse);
|
|
16452
|
-
|
|
17940
|
+
writeFileSync8(
|
|
16453
17941
|
schemaPath,
|
|
16454
17942
|
`${JSON.stringify(taskSuggestionJsonSchema(), null, 2)}
|
|
16455
17943
|
`,
|
|
@@ -16466,7 +17954,7 @@ async function runCodexTaskSuggestion(args) {
|
|
|
16466
17954
|
prompt
|
|
16467
17955
|
})
|
|
16468
17956
|
);
|
|
16469
|
-
return
|
|
17957
|
+
return readFileSync13(outputPath, "utf8");
|
|
16470
17958
|
} finally {
|
|
16471
17959
|
rmSync3(tempRoot, { recursive: true, force: true });
|
|
16472
17960
|
}
|
|
@@ -16515,6 +18003,14 @@ function taskSuggestionPrompt(previousResponse) {
|
|
|
16515
18003
|
"Task:",
|
|
16516
18004
|
"Inspect this repository read-only and suggest exactly 5 useful quick starter tasks for Codex agents.",
|
|
16517
18005
|
"",
|
|
18006
|
+
"GitHub context:",
|
|
18007
|
+
"- First check whether the GitHub CLI is available and authenticated with a cheap command such as `gh auth status`.",
|
|
18008
|
+
"- If `gh` is missing, unauthenticated, or this repository has no GitHub remote, skip GitHub context and rely on local files.",
|
|
18009
|
+
"- When `gh` is authenticated, use bounded reads such as `gh pr list --limit 30 --state all` and `gh issue list --limit 30 --state all` to inspect recent open and closed pull requests and issues.",
|
|
18010
|
+
"- Be mindful of GitHub rate limits, CPU usage, and network usage; prefer one or two small calls and do not paginate or crawl exhaustively.",
|
|
18011
|
+
"- If a GitHub call returns unauthorized, forbidden, or not found for this repository, stop GitHub lookups for this repository.",
|
|
18012
|
+
"- Use pull request and issue titles, states, labels, and recent activity to infer what the repo is about, what the user has been working on, and likely next tasks.",
|
|
18013
|
+
"",
|
|
16518
18014
|
"Constraints:",
|
|
16519
18015
|
"- Prefer tasks that are small, concrete, and likely to produce useful first results.",
|
|
16520
18016
|
"- Do not modify files.",
|
|
@@ -16565,7 +18061,9 @@ function parseTaskSuggestionResponse(response) {
|
|
|
16565
18061
|
const titles = tasks.map(
|
|
16566
18062
|
(task) => typeof task === "object" && task !== null && !Array.isArray(task) ? task.title : void 0
|
|
16567
18063
|
);
|
|
16568
|
-
if (!titles.every(
|
|
18064
|
+
if (!titles.every(
|
|
18065
|
+
(title) => typeof title === "string" && title.trim() !== "" && title.trim().length <= 120
|
|
18066
|
+
)) {
|
|
16569
18067
|
return void 0;
|
|
16570
18068
|
}
|
|
16571
18069
|
return titles.map((title, index) => ({
|
|
@@ -16628,8 +18126,8 @@ var init_signupTaskSuggestions = __esm({
|
|
|
16628
18126
|
|
|
16629
18127
|
// src/remoteCodexSandboxRunner.ts
|
|
16630
18128
|
import { spawn as spawn7 } from "node:child_process";
|
|
16631
|
-
import { existsSync as
|
|
16632
|
-
import { dirname as
|
|
18129
|
+
import { existsSync as existsSync11, realpathSync as realpathSync5 } from "node:fs";
|
|
18130
|
+
import { dirname as dirname11, isAbsolute as isAbsolute3 } from "node:path";
|
|
16633
18131
|
function createConfiguredRemoteCodexSandboxRunner(args) {
|
|
16634
18132
|
const kind = normalizedSandboxKind(args.env.LINZUMI_REMOTE_CODEX_SANDBOX);
|
|
16635
18133
|
if (kind === void 0) {
|
|
@@ -16648,7 +18146,7 @@ function createConfiguredRemoteCodexSandboxRunner(args) {
|
|
|
16648
18146
|
}
|
|
16649
18147
|
break;
|
|
16650
18148
|
}
|
|
16651
|
-
if (!
|
|
18149
|
+
if (!existsSync11(sandboxBin)) {
|
|
16652
18150
|
throw new Error(`remote Codex sandbox binary not found: ${sandboxBin}`);
|
|
16653
18151
|
}
|
|
16654
18152
|
return createRemoteCodexSandboxRunner({
|
|
@@ -16659,7 +18157,7 @@ function createConfiguredRemoteCodexSandboxRunner(args) {
|
|
|
16659
18157
|
function createRemoteCodexSandboxRunner(config, deps = {}) {
|
|
16660
18158
|
const resolvedDeps = {
|
|
16661
18159
|
platform: deps.platform ?? process.platform,
|
|
16662
|
-
exists: deps.exists ??
|
|
18160
|
+
exists: deps.exists ?? existsSync11,
|
|
16663
18161
|
spawnProcess: deps.spawnProcess ?? spawn7
|
|
16664
18162
|
};
|
|
16665
18163
|
return (request) => runSandboxInvocation(
|
|
@@ -16742,10 +18240,10 @@ function bubblewrapArgs(sandboxBin, request, exists) {
|
|
|
16742
18240
|
key,
|
|
16743
18241
|
value
|
|
16744
18242
|
]);
|
|
16745
|
-
const readOnlyBindArgs =
|
|
18243
|
+
const readOnlyBindArgs = uniqueStrings3([
|
|
16746
18244
|
...linuxReadOnlyRoots,
|
|
16747
|
-
...isAbsolute3(request.command) ? [
|
|
16748
|
-
|
|
18245
|
+
...isAbsolute3(request.command) ? [dirname11(request.command)] : [],
|
|
18246
|
+
dirname11(sandboxBin)
|
|
16749
18247
|
]).flatMap((path2) => exists(path2) ? ["--ro-bind", path2, path2] : []);
|
|
16750
18248
|
const cwdParentDirs = parentDirs(request.cwd).flatMap((path2) => [
|
|
16751
18249
|
"--dir",
|
|
@@ -16776,9 +18274,9 @@ function bubblewrapArgs(sandboxBin, request, exists) {
|
|
|
16776
18274
|
];
|
|
16777
18275
|
}
|
|
16778
18276
|
function macosSeatbeltProfile(request, exists) {
|
|
16779
|
-
const readableRoots =
|
|
18277
|
+
const readableRoots = uniqueStrings3([
|
|
16780
18278
|
...macosReadOnlyRoots,
|
|
16781
|
-
...isAbsolute3(request.command) ? [
|
|
18279
|
+
...isAbsolute3(request.command) ? [dirname11(request.command)] : []
|
|
16782
18280
|
]).filter(exists);
|
|
16783
18281
|
const readableRules = readableRoots.map((path2) => `(allow file-read* (subpath ${sandboxString(path2)}))`).join("\n");
|
|
16784
18282
|
const writableTempRules = macosWritableTempRoots.filter(exists).flatMap((path2) => [
|
|
@@ -16860,11 +18358,11 @@ function parentDirs(path2) {
|
|
|
16860
18358
|
(_part, index) => `/${parents.slice(0, index + 1).join("/")}`
|
|
16861
18359
|
);
|
|
16862
18360
|
}
|
|
16863
|
-
function
|
|
18361
|
+
function uniqueStrings3(values) {
|
|
16864
18362
|
return Array.from(new Set(values));
|
|
16865
18363
|
}
|
|
16866
18364
|
function existingPathAliases(path2) {
|
|
16867
|
-
return
|
|
18365
|
+
return uniqueStrings3([path2, realpathSync5(path2)]);
|
|
16868
18366
|
}
|
|
16869
18367
|
function sandboxString(value) {
|
|
16870
18368
|
return JSON.stringify(value);
|
|
@@ -16900,21 +18398,29 @@ var init_remoteCodexSandboxRunner = __esm({
|
|
|
16900
18398
|
});
|
|
16901
18399
|
|
|
16902
18400
|
// src/runner.ts
|
|
16903
|
-
import { spawn as spawn8, spawnSync as
|
|
16904
|
-
import { createHash as createHash3, randomUUID as
|
|
18401
|
+
import { spawn as spawn8, spawnSync as spawnSync5 } from "node:child_process";
|
|
18402
|
+
import { createHash as createHash3, randomUUID as randomUUID4 } from "node:crypto";
|
|
16905
18403
|
import {
|
|
16906
18404
|
chmodSync as chmodSync2,
|
|
16907
18405
|
lstatSync,
|
|
16908
|
-
mkdirSync as
|
|
18406
|
+
mkdirSync as mkdirSync10,
|
|
16909
18407
|
mkdtempSync as mkdtempSync4,
|
|
16910
|
-
readdirSync as
|
|
18408
|
+
readdirSync as readdirSync4,
|
|
16911
18409
|
realpathSync as realpathSync6,
|
|
16912
18410
|
rmSync as rmSync4,
|
|
16913
|
-
statSync
|
|
18411
|
+
statSync as statSync3
|
|
16914
18412
|
} from "node:fs";
|
|
18413
|
+
import { readFile as readFile2 } from "node:fs/promises";
|
|
16915
18414
|
import { createServer as createServer3 } from "node:http";
|
|
16916
18415
|
import { hostname as hostname2, tmpdir as tmpdir3 } from "node:os";
|
|
16917
|
-
import {
|
|
18416
|
+
import {
|
|
18417
|
+
basename as basename8,
|
|
18418
|
+
dirname as dirname12,
|
|
18419
|
+
extname as extname2,
|
|
18420
|
+
isAbsolute as isAbsolute4,
|
|
18421
|
+
join as join17,
|
|
18422
|
+
resolve as resolve7
|
|
18423
|
+
} from "node:path";
|
|
16918
18424
|
async function runLocalCodexRunner(options) {
|
|
16919
18425
|
const log = makeRunnerLogger(options);
|
|
16920
18426
|
const cleanup = {
|
|
@@ -17174,7 +18680,7 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
|
|
|
17174
18680
|
});
|
|
17175
18681
|
}
|
|
17176
18682
|
}
|
|
17177
|
-
const instanceId = `codex-${
|
|
18683
|
+
const instanceId = `codex-${randomUUID4()}`;
|
|
17178
18684
|
const publishLocalEditorStatus = (payload) => {
|
|
17179
18685
|
void kandan.push(topic, "local_editor_status", payload).catch((error) => {
|
|
17180
18686
|
log("kandan.local_editor_status_push_failed", {
|
|
@@ -17249,7 +18755,7 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
|
|
|
17249
18755
|
};
|
|
17250
18756
|
const approveEditorPortForwardCandidate = (candidate) => {
|
|
17251
18757
|
const request = pendingRequestFromCandidate({
|
|
17252
|
-
requestId: `editor-port-forward-auto-${
|
|
18758
|
+
requestId: `editor-port-forward-auto-${randomUUID4()}`,
|
|
17253
18759
|
sourceSeq: 0,
|
|
17254
18760
|
candidate
|
|
17255
18761
|
});
|
|
@@ -17477,7 +18983,7 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
|
|
|
17477
18983
|
return;
|
|
17478
18984
|
}
|
|
17479
18985
|
const request = pendingRequestFromCandidate({
|
|
17480
|
-
requestId: `claude-port-forward-auto-${
|
|
18986
|
+
requestId: `claude-port-forward-auto-${randomUUID4()}`,
|
|
17481
18987
|
sourceSeq,
|
|
17482
18988
|
candidate
|
|
17483
18989
|
});
|
|
@@ -17890,6 +19396,21 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
|
|
|
17890
19396
|
codexUrl: codexUrl ?? null,
|
|
17891
19397
|
replacedRunners
|
|
17892
19398
|
});
|
|
19399
|
+
const lifecycleWorkspaceSlug = runnerWorkspaceSlug(options);
|
|
19400
|
+
const lifecycleTelemetryAttributes = {
|
|
19401
|
+
...lifecycleWorkspaceSlug === void 0 ? {} : { workspace: lifecycleWorkspaceSlug },
|
|
19402
|
+
launch_source: options.launchSource ?? "cli"
|
|
19403
|
+
};
|
|
19404
|
+
void postLifecycleEvent(
|
|
19405
|
+
{ name: "commander.connected", attributes: lifecycleTelemetryAttributes },
|
|
19406
|
+
{ apiUrl: options.kandanUrl }
|
|
19407
|
+
);
|
|
19408
|
+
cleanup.actions.push(
|
|
19409
|
+
() => postLifecycleEvent(
|
|
19410
|
+
{ name: "commander.exited", attributes: lifecycleTelemetryAttributes },
|
|
19411
|
+
{ apiUrl: options.kandanUrl }
|
|
19412
|
+
)
|
|
19413
|
+
);
|
|
17893
19414
|
const channelSession = options.channelSession === void 0 ? void 0 : await attachChannelSession({
|
|
17894
19415
|
kandan,
|
|
17895
19416
|
codex,
|
|
@@ -18519,6 +20040,17 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
|
|
|
18519
20040
|
void pushHeartbeat();
|
|
18520
20041
|
return;
|
|
18521
20042
|
}
|
|
20043
|
+
if (control.type === "import_onboarding_conversations") {
|
|
20044
|
+
void runLocalOnboardingConversationImport(options, log, control).catch(
|
|
20045
|
+
(error) => {
|
|
20046
|
+
log("onboarding.conversation_import_failed", {
|
|
20047
|
+
runnerId: options.runnerId,
|
|
20048
|
+
message: error instanceof Error ? error.message : String(error)
|
|
20049
|
+
});
|
|
20050
|
+
}
|
|
20051
|
+
);
|
|
20052
|
+
return;
|
|
20053
|
+
}
|
|
18522
20054
|
void resolveSessionControl(channelSession, dynamicChannelSessions, control).then((handled) => {
|
|
18523
20055
|
if (handled !== void 0) {
|
|
18524
20056
|
return handled;
|
|
@@ -18555,9 +20087,19 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
|
|
|
18555
20087
|
});
|
|
18556
20088
|
});
|
|
18557
20089
|
};
|
|
20090
|
+
finishRunnerStartup(
|
|
20091
|
+
options,
|
|
20092
|
+
log,
|
|
20093
|
+
pendingControls,
|
|
20094
|
+
controlDispatcher,
|
|
20095
|
+
handleControl
|
|
20096
|
+
);
|
|
20097
|
+
return { instanceId, codexUrl, close };
|
|
20098
|
+
}
|
|
20099
|
+
function finishRunnerStartup(options, log, pendingControls, controlDispatcher, handleControl) {
|
|
18558
20100
|
controlDispatcher.value = handleControl;
|
|
18559
20101
|
pendingControls.splice(0).forEach(handleControl);
|
|
18560
|
-
|
|
20102
|
+
startOnboardingDiscoveryAgents(options, log);
|
|
18561
20103
|
}
|
|
18562
20104
|
function controlTargetsInstance(control, instanceId) {
|
|
18563
20105
|
switch (control.type) {
|
|
@@ -19699,22 +21241,6 @@ ${args.developerPrompt}
|
|
|
19699
21241
|
`;
|
|
19700
21242
|
const linzumiContext = args.linzumiContext === void 0 ? "" : `
|
|
19701
21243
|
${formatLinzumiConversationContextForPrompt(args.linzumiContext)}
|
|
19702
|
-
`;
|
|
19703
|
-
const reviewGuidance = `
|
|
19704
|
-
<linzumi_pr_review_guidance>
|
|
19705
|
-
For frontend or other user-visible changes, ask whether the user wants
|
|
19706
|
-
screenshot or screen-recording proof, ideally before/after when that helps
|
|
19707
|
-
review the change. Screen recordings should be WebM or MP4. When the user asks
|
|
19708
|
-
you to open or update a PR, attach or link that proof in the PR when feasible,
|
|
19709
|
-
and state the exact blocker when it is not feasible.
|
|
19710
|
-
</linzumi_pr_review_guidance>
|
|
19711
|
-
|
|
19712
|
-
<linzumi_user_visible_writing_guide>
|
|
19713
|
-
When you write user-visible UI, status, error, or PR-review copy, keep it short,
|
|
19714
|
-
direct, and focused on the user's outcome. Avoid implementation-heavy sentences,
|
|
19715
|
-
stacked clauses, and internal mechanics unless the user explicitly needs that
|
|
19716
|
-
detail.
|
|
19717
|
-
</linzumi_user_visible_writing_guide>
|
|
19718
21244
|
`;
|
|
19719
21245
|
return `<context>
|
|
19720
21246
|
You are a Linzumi ${agentLabel} session launched by the Linzumi Commander.
|
|
@@ -19733,6 +21259,15 @@ Linzumi.
|
|
|
19733
21259
|
Linzumi ${agentLabel} session: You, the inner ${agentLabel} process that performs the actual
|
|
19734
21260
|
work in the approved project folder.
|
|
19735
21261
|
Approved project folder: ${args.cwd}
|
|
21262
|
+
Coding-job metadata: User-visible goal, plan steps, step statuses, completion
|
|
21263
|
+
notes, primary pull request link, and worktree state shown in Linzumi's coding
|
|
21264
|
+
job overview.
|
|
21265
|
+
Plan step: A discrete chunk of work with a short title, one-line description,
|
|
21266
|
+
status, and completion note. It should describe an actual goal, not just a
|
|
21267
|
+
process verb.
|
|
21268
|
+
Primary pull request: The GitHub PR the job is currently driving or reviewing;
|
|
21269
|
+
Linzumi uses this link to hydrate PR comments, files, commits, checks, and
|
|
21270
|
+
deployments in the metadata overview.
|
|
19736
21271
|
</term_definitions>
|
|
19737
21272
|
|
|
19738
21273
|
<linzumi_mcp>
|
|
@@ -19746,11 +21281,49 @@ a concise DM to the Commander owner when the task genuinely requires it.
|
|
|
19746
21281
|
Work only in the approved project folder unless the human explicitly asks for
|
|
19747
21282
|
something else in the Linzumi thread. Start, inspect, and modify the local app
|
|
19748
21283
|
from that folder. Report concise progress and exact blockers in the thread.
|
|
21284
|
+
Use the Linzumi MCP server named "linzumi" when you need authenticated
|
|
21285
|
+
workspace context or coding-job metadata tools.
|
|
21286
|
+
For coding-job threads, start by setting the current goal with
|
|
21287
|
+
linzumi_upsert_coding_job_plan, then replace the plan steps with
|
|
21288
|
+
linzumi_replace_coding_job_plan_steps.
|
|
21289
|
+
When the coding-job scope changes materially, call linzumi_rename_coding_job
|
|
21290
|
+
with a concise title so the Linzumi thread title and workflow-facing title
|
|
21291
|
+
match the work.
|
|
21292
|
+
As work proceeds, update each step with linzumi_update_coding_job_plan_step.
|
|
21293
|
+
When you open, discover, or choose the primary pull request, link it with
|
|
21294
|
+
linzumi_link_coding_job_pull_request.
|
|
21295
|
+
Before stopping, confirm the metadata overview tells the truth for a user who
|
|
21296
|
+
only sees that overview: completed work is completed, active work is really
|
|
21297
|
+
active, blocked work says why, and no stale in-progress step is left behind.
|
|
21298
|
+
For frontend or other user-visible changes, ask whether the user wants
|
|
21299
|
+
screenshot or screen-recording proof, ideally before/after when that helps
|
|
21300
|
+
review the change. Screen recordings should be WebM or MP4.
|
|
21301
|
+
When the user asks you to open or update a PR, attach or link feasible proof in
|
|
21302
|
+
the PR and state the exact blocker when proof is not feasible.
|
|
19749
21303
|
</task_instructions>
|
|
19750
21304
|
|
|
19751
21305
|
<rules>
|
|
19752
21306
|
You MUST treat the Linzumi thread as the source of truth for user-facing
|
|
19753
21307
|
progress.
|
|
21308
|
+
For coding-job threads, you MUST keep coding-job metadata current throughout
|
|
21309
|
+
the job.
|
|
21310
|
+
Plan steps MUST be fairly granular, discrete chunks of real work that a human
|
|
21311
|
+
can track.
|
|
21312
|
+
Plan step descriptions MUST stay short, usually one line and less than one full
|
|
21313
|
+
sentence.
|
|
21314
|
+
Plan steps MUST NOT be vague process-only labels such as "sync PR",
|
|
21315
|
+
"update PR", "verify PR", or "commit and merge" when those labels hide the
|
|
21316
|
+
concrete goal.
|
|
21317
|
+
You MUST update steps in sequence as work is completed. Do not mark a later
|
|
21318
|
+
step active while earlier applicable steps are still pending.
|
|
21319
|
+
Before you stop work, you MUST reconcile the plan so the job is not left with a
|
|
21320
|
+
stale active step. You may delegate metadata upkeep to a sub-agent when useful,
|
|
21321
|
+
but the visible job state must remain truthful.
|
|
21322
|
+
If the coding-job thread title is missing, undefined, or "Untitled", you MUST
|
|
21323
|
+
choose a concise, specific title for the job.
|
|
21324
|
+
User-visible UI, status, error, and PR-review copy MUST keep it short, direct,
|
|
21325
|
+
and focused on the user's outcome unless the user explicitly asks for
|
|
21326
|
+
implementation detail.
|
|
19754
21327
|
You MUST keep user-visible preview servers bound to 0.0.0.0, not 127.0.0.1 or
|
|
19755
21328
|
localhost, so the Linzumi secure tunnel can reach them.
|
|
19756
21329
|
You MUST keep any preview or dev server as your descendant process so the
|
|
@@ -19767,16 +21340,51 @@ revert another session's work.
|
|
|
19767
21340
|
|
|
19768
21341
|
<examples>
|
|
19769
21342
|
GOOD preview command: npm run dev -- --host 0.0.0.0 --port 8787
|
|
21343
|
+
Why good: The preview is reachable through the Linzumi secure tunnel.
|
|
21344
|
+
|
|
19770
21345
|
BAD preview command: npm run dev -- --host 127.0.0.1
|
|
21346
|
+
Why bad: Binding to localhost prevents the Linzumi secure tunnel from reaching
|
|
21347
|
+
the preview.
|
|
21348
|
+
|
|
21349
|
+
GOOD coding-job plan:
|
|
21350
|
+
- Inspect prompt sources: Locate the prompt builder, prompt guide, and tests.
|
|
21351
|
+
- Add metadata guidance: Patch the prompt with plan, title, and stop-state rules.
|
|
21352
|
+
- Verify compiled prompt: Run focused tests and inspect the generated text.
|
|
21353
|
+
- Push PR update: Commit owned files, push the branch, and refresh PR metadata.
|
|
21354
|
+
Why good: Each step is a concrete chunk of work, the list is short enough to
|
|
21355
|
+
scan, and the titles explain what is actually happening.
|
|
21356
|
+
|
|
21357
|
+
BAD coding-job plan:
|
|
21358
|
+
- Sync PR
|
|
21359
|
+
- Update PR
|
|
21360
|
+
- Verify PR
|
|
21361
|
+
- Commit and merge
|
|
21362
|
+
Why bad: These labels describe process mechanics, not the concrete work or
|
|
21363
|
+
product goal. A user cannot tell what the agent has learned, changed, or still
|
|
21364
|
+
needs to do.
|
|
21365
|
+
|
|
21366
|
+
BAD coding-job metadata state:
|
|
21367
|
+
- Step 1 complete, Step 2 complete, Step 3 active, Step 4 pending.
|
|
21368
|
+
- The agent has stopped working and sent its final response.
|
|
21369
|
+
Why bad: The overview still says work is active even though the agent is idle.
|
|
21370
|
+
Before stopping, the agent should mark Step 3 completed, blocked, or canceled
|
|
21371
|
+
with a truthful note.
|
|
19771
21372
|
</examples>
|
|
19772
21373
|
${linzumiContext}
|
|
19773
|
-
${reviewGuidance}
|
|
19774
21374
|
${customPrompt}
|
|
19775
21375
|
<task_reminder>
|
|
19776
21376
|
You are the Commander-launched Linzumi ${agentLabel} session. Do the implementation
|
|
19777
21377
|
work in the approved project folder, keep preview servers reachable through the
|
|
19778
|
-
secure tunnel, and keep the Linzumi thread truthful.
|
|
19779
|
-
</task_reminder
|
|
21378
|
+
secure tunnel, and keep the Linzumi thread and coding-job metadata truthful.
|
|
21379
|
+
</task_reminder>
|
|
21380
|
+
|
|
21381
|
+
<output_format>
|
|
21382
|
+
Use normal concise Linzumi thread messages for user-facing progress and final
|
|
21383
|
+
answers. When the task requires files, code, commits, pull requests, previews,
|
|
21384
|
+
or verification output, report the concrete artifact, command, result, and any
|
|
21385
|
+
exact blocker. Do not emit machine-only JSON unless the user or tool explicitly
|
|
21386
|
+
requests it.
|
|
21387
|
+
</output_format>`;
|
|
19780
21388
|
}
|
|
19781
21389
|
function availableRunnerAgentProviders(options) {
|
|
19782
21390
|
switch (options.claudeCodeRunner !== void 0 || options.claudeCodeAvailable === true) {
|
|
@@ -21573,6 +23181,869 @@ function redactedThreadRunnerCliArgs(args) {
|
|
|
21573
23181
|
function optionalCliValue(flag, value) {
|
|
21574
23182
|
return value === void 0 || value === "" ? [] : [flag, value];
|
|
21575
23183
|
}
|
|
23184
|
+
function startOnboardingDiscoveryAgents(options, log) {
|
|
23185
|
+
const workspaceSlug = runnerWorkspaceSlug(options);
|
|
23186
|
+
if (!shouldStartOnboardingDiscoveryAgents(options, workspaceSlug)) {
|
|
23187
|
+
return;
|
|
23188
|
+
}
|
|
23189
|
+
const startKey = onboardingDiscoveryAgentStartKey(options, workspaceSlug);
|
|
23190
|
+
if (onboardingDiscoveryAgentStartKeys.has(startKey)) {
|
|
23191
|
+
log("onboarding.discovery_agents_already_started", {
|
|
23192
|
+
runnerId: options.runnerId,
|
|
23193
|
+
workspace: workspaceSlug
|
|
23194
|
+
});
|
|
23195
|
+
return;
|
|
23196
|
+
}
|
|
23197
|
+
onboardingDiscoveryAgentStartKeys.add(startKey);
|
|
23198
|
+
log("onboarding.discovery_agents_started", {
|
|
23199
|
+
runnerId: options.runnerId,
|
|
23200
|
+
workspace: workspaceSlug,
|
|
23201
|
+
kandanUrl: options.kandanUrl
|
|
23202
|
+
});
|
|
23203
|
+
const discoveryStartedAtMs = Date.now();
|
|
23204
|
+
void Promise.allSettled([
|
|
23205
|
+
runLocalOnboardingProjectDiscovery(options, log),
|
|
23206
|
+
runLocalOnboardingConversationDiscovery(options, log)
|
|
23207
|
+
]).then(async (results) => {
|
|
23208
|
+
const initialSearchDurationMs = Date.now() - discoveryStartedAtMs;
|
|
23209
|
+
let successfulWriteCount = 0;
|
|
23210
|
+
for (const result of results) {
|
|
23211
|
+
if (result.status !== "fulfilled" || result.value === void 0) {
|
|
23212
|
+
continue;
|
|
23213
|
+
}
|
|
23214
|
+
successfulWriteCount += result.value.successfulPostStartWriteCount ?? 0;
|
|
23215
|
+
const initialSearchStatusWritten = await reportOnboardingDiscoveryStatus(
|
|
23216
|
+
options,
|
|
23217
|
+
log,
|
|
23218
|
+
{
|
|
23219
|
+
...result.value,
|
|
23220
|
+
metadata: {
|
|
23221
|
+
...result.value.metadata,
|
|
23222
|
+
initial_search_duration_ms: initialSearchDurationMs
|
|
23223
|
+
}
|
|
23224
|
+
}
|
|
23225
|
+
);
|
|
23226
|
+
if (initialSearchStatusWritten) {
|
|
23227
|
+
successfulWriteCount += 1;
|
|
23228
|
+
}
|
|
23229
|
+
}
|
|
23230
|
+
if (successfulWriteCount === 0) {
|
|
23231
|
+
onboardingDiscoveryAgentStartKeys.delete(startKey);
|
|
23232
|
+
log("onboarding.discovery_agents_start_key_cleared", {
|
|
23233
|
+
runnerId: options.runnerId,
|
|
23234
|
+
workspace: workspaceSlug
|
|
23235
|
+
});
|
|
23236
|
+
}
|
|
23237
|
+
log("onboarding.discovery_initial_search_completed", {
|
|
23238
|
+
runnerId: options.runnerId,
|
|
23239
|
+
workspace: workspaceSlug,
|
|
23240
|
+
duration_ms: initialSearchDurationMs,
|
|
23241
|
+
workers_completed: 2,
|
|
23242
|
+
successful_write_count: successfulWriteCount
|
|
23243
|
+
});
|
|
23244
|
+
});
|
|
23245
|
+
}
|
|
23246
|
+
function withSuccessfulWriteCount(report, successfulPostStartWriteCount) {
|
|
23247
|
+
return {
|
|
23248
|
+
...report,
|
|
23249
|
+
successfulPostStartWriteCount
|
|
23250
|
+
};
|
|
23251
|
+
}
|
|
23252
|
+
async function addStatusWriteResult(successfulWriteCount, statusWrite) {
|
|
23253
|
+
const written = await statusWrite;
|
|
23254
|
+
return written ? successfulWriteCount + 1 : successfulWriteCount;
|
|
23255
|
+
}
|
|
23256
|
+
function successfulSettledWriteCount(results) {
|
|
23257
|
+
return results.filter((result) => result.status === "fulfilled").length;
|
|
23258
|
+
}
|
|
23259
|
+
async function runLocalOnboardingConversationImport(options, log, control) {
|
|
23260
|
+
const workspaceSlug = stringValue(control.workspace) ?? runnerWorkspaceSlug(options);
|
|
23261
|
+
if (workspaceSlug === void 0) {
|
|
23262
|
+
throw new Error("workspace_required");
|
|
23263
|
+
}
|
|
23264
|
+
const sources = onboardingConversationImportSources(control.sources);
|
|
23265
|
+
const importKey = onboardingConversationImportRunKey(
|
|
23266
|
+
options.runnerId,
|
|
23267
|
+
workspaceSlug,
|
|
23268
|
+
sources
|
|
23269
|
+
);
|
|
23270
|
+
if (onboardingConversationImportKeys.has(importKey)) {
|
|
23271
|
+
log("onboarding.conversation_import_skipped", {
|
|
23272
|
+
runnerId: options.runnerId,
|
|
23273
|
+
workspace: workspaceSlug,
|
|
23274
|
+
sources,
|
|
23275
|
+
reason: "already_running"
|
|
23276
|
+
});
|
|
23277
|
+
return;
|
|
23278
|
+
}
|
|
23279
|
+
onboardingConversationImportKeys.add(importKey);
|
|
23280
|
+
try {
|
|
23281
|
+
await runLocalOnboardingConversationImportOnce(
|
|
23282
|
+
options,
|
|
23283
|
+
log,
|
|
23284
|
+
control,
|
|
23285
|
+
workspaceSlug,
|
|
23286
|
+
sources
|
|
23287
|
+
);
|
|
23288
|
+
} finally {
|
|
23289
|
+
onboardingConversationImportKeys.delete(importKey);
|
|
23290
|
+
}
|
|
23291
|
+
}
|
|
23292
|
+
async function runLocalOnboardingConversationImportOnce(options, log, _control, workspaceSlug, sources) {
|
|
23293
|
+
if (sources.length === 0) {
|
|
23294
|
+
await reportOnboardingDiscoveryStatus(options, log, {
|
|
23295
|
+
phase: "conversations",
|
|
23296
|
+
status: "completed",
|
|
23297
|
+
placesSearched: 0,
|
|
23298
|
+
message: "No local conversation imports selected",
|
|
23299
|
+
currentPlace: "Codex and Claude Code stores",
|
|
23300
|
+
metadata: {
|
|
23301
|
+
worker_id: "conversations-local-import",
|
|
23302
|
+
worker_label: "Local conversation import",
|
|
23303
|
+
imported_count: 0,
|
|
23304
|
+
reported_count: 0
|
|
23305
|
+
}
|
|
23306
|
+
});
|
|
23307
|
+
return;
|
|
23308
|
+
}
|
|
23309
|
+
await reportOnboardingDiscoveryStatus(options, log, {
|
|
23310
|
+
phase: "conversations",
|
|
23311
|
+
status: "running",
|
|
23312
|
+
placesSearched: 0,
|
|
23313
|
+
message: "Importing local conversations",
|
|
23314
|
+
currentPlace: "Codex and Claude Code stores",
|
|
23315
|
+
metadata: {
|
|
23316
|
+
worker_id: "conversations-local-import",
|
|
23317
|
+
worker_label: "Local conversation import",
|
|
23318
|
+
imported_count: 0,
|
|
23319
|
+
reported_count: 0
|
|
23320
|
+
}
|
|
23321
|
+
});
|
|
23322
|
+
const summary = options.localConversationDiscovery?.() ?? discoverLocalConversations({
|
|
23323
|
+
candidateLimit: onboardingConversationImportCandidateLimit
|
|
23324
|
+
});
|
|
23325
|
+
const sourceSet = new Set(sources);
|
|
23326
|
+
const candidates = summary.candidates.filter((candidate) => sourceSet.has(candidate.source)).slice(0, onboardingConversationImportCandidateLimit);
|
|
23327
|
+
const totalCount = candidates.length;
|
|
23328
|
+
const client = createLinzumiMcpApiClient({
|
|
23329
|
+
kandanUrl: options.kandanUrl,
|
|
23330
|
+
accessToken: options.token,
|
|
23331
|
+
fetchImpl: options.fetch,
|
|
23332
|
+
operatingMode: "text"
|
|
23333
|
+
});
|
|
23334
|
+
let importedCount = 0;
|
|
23335
|
+
let failedImportCount = 0;
|
|
23336
|
+
let skippedImportCount = 0;
|
|
23337
|
+
for (const [index, candidate] of candidates.entries()) {
|
|
23338
|
+
try {
|
|
23339
|
+
const messages = readLocalConversationImportMessages(candidate);
|
|
23340
|
+
const response = await client.noteAgentConversation({
|
|
23341
|
+
workspace: workspaceSlug,
|
|
23342
|
+
runner_id: options.runnerId,
|
|
23343
|
+
source: candidate.source,
|
|
23344
|
+
import_key: candidate.importKey,
|
|
23345
|
+
import_to_office_channel: true,
|
|
23346
|
+
path: candidate.path,
|
|
23347
|
+
display_name: candidate.displayName,
|
|
23348
|
+
...candidate.activityAt === void 0 ? {} : { activity_at: candidate.activityAt },
|
|
23349
|
+
metadata: candidate.metadata,
|
|
23350
|
+
title_messages: onboardingConversationTitleMessages(messages)
|
|
23351
|
+
});
|
|
23352
|
+
const officeImport = onboardingConversationOfficeImport(response);
|
|
23353
|
+
const threadId = stringValue(officeImport?.thread_id);
|
|
23354
|
+
const importStatus = stringValue(officeImport?.status);
|
|
23355
|
+
switch (importStatus) {
|
|
23356
|
+
case "failed":
|
|
23357
|
+
skippedImportCount += 1;
|
|
23358
|
+
log("onboarding.conversation_import_candidate_skipped", {
|
|
23359
|
+
runnerId: options.runnerId,
|
|
23360
|
+
workspace: workspaceSlug,
|
|
23361
|
+
source: candidate.source,
|
|
23362
|
+
import_key: candidate.importKey,
|
|
23363
|
+
message: stringValue(officeImport?.error) ?? "conversation_import_failed"
|
|
23364
|
+
});
|
|
23365
|
+
continue;
|
|
23366
|
+
default:
|
|
23367
|
+
break;
|
|
23368
|
+
}
|
|
23369
|
+
switch (threadId) {
|
|
23370
|
+
case void 0:
|
|
23371
|
+
break;
|
|
23372
|
+
default: {
|
|
23373
|
+
const replayedChunkCount = await replayLocalOnboardingConversation({
|
|
23374
|
+
client,
|
|
23375
|
+
options,
|
|
23376
|
+
workspaceSlug,
|
|
23377
|
+
threadId,
|
|
23378
|
+
candidate,
|
|
23379
|
+
messages
|
|
23380
|
+
});
|
|
23381
|
+
await markLocalOnboardingConversationReplayCompleted({
|
|
23382
|
+
client,
|
|
23383
|
+
workspaceSlug,
|
|
23384
|
+
runnerId: options.runnerId,
|
|
23385
|
+
candidate,
|
|
23386
|
+
threadId,
|
|
23387
|
+
officeImport,
|
|
23388
|
+
replayedMessageCount: messages.length,
|
|
23389
|
+
replayedChunkCount
|
|
23390
|
+
}).catch((error) => {
|
|
23391
|
+
log("onboarding.conversation_import_replay_completion_failed", {
|
|
23392
|
+
runnerId: options.runnerId,
|
|
23393
|
+
workspace: workspaceSlug,
|
|
23394
|
+
source: candidate.source,
|
|
23395
|
+
import_key: candidate.importKey,
|
|
23396
|
+
message: error instanceof Error ? error.message : String(error)
|
|
23397
|
+
});
|
|
23398
|
+
});
|
|
23399
|
+
break;
|
|
23400
|
+
}
|
|
23401
|
+
}
|
|
23402
|
+
importedCount += 1;
|
|
23403
|
+
} catch (error) {
|
|
23404
|
+
failedImportCount += 1;
|
|
23405
|
+
log("onboarding.conversation_import_candidate_failed", {
|
|
23406
|
+
runnerId: options.runnerId,
|
|
23407
|
+
workspace: workspaceSlug,
|
|
23408
|
+
source: candidate.source,
|
|
23409
|
+
import_key: candidate.importKey,
|
|
23410
|
+
message: error instanceof Error ? error.message : String(error)
|
|
23411
|
+
});
|
|
23412
|
+
}
|
|
23413
|
+
const processedCount = index + 1;
|
|
23414
|
+
if (processedCount === candidates.length || processedCount % onboardingConversationImportProgressInterval === 0) {
|
|
23415
|
+
await reportOnboardingDiscoveryStatus(options, log, {
|
|
23416
|
+
phase: "conversations",
|
|
23417
|
+
status: "running",
|
|
23418
|
+
placesSearched: summary.placesSearched,
|
|
23419
|
+
message: "Importing local conversations",
|
|
23420
|
+
currentPlace: "Codex and Claude Code stores",
|
|
23421
|
+
metadata: {
|
|
23422
|
+
worker_id: "conversations-local-import",
|
|
23423
|
+
worker_label: "Local conversation import",
|
|
23424
|
+
duration_ms: summary.durationMs,
|
|
23425
|
+
codex_count: sources.includes("codex") ? summary.codexCount : 0,
|
|
23426
|
+
claude_code_count: sources.includes("claude_code") ? summary.claudeCodeCount : 0,
|
|
23427
|
+
candidate_limit: onboardingConversationImportCandidateLimit,
|
|
23428
|
+
imported_count: importedCount,
|
|
23429
|
+
failed_import_count: failedImportCount,
|
|
23430
|
+
skipped_import_count: skippedImportCount,
|
|
23431
|
+
reported_count: importedCount,
|
|
23432
|
+
total_count: totalCount
|
|
23433
|
+
}
|
|
23434
|
+
});
|
|
23435
|
+
}
|
|
23436
|
+
}
|
|
23437
|
+
await reportOnboardingDiscoveryStatus(options, log, {
|
|
23438
|
+
phase: "conversations",
|
|
23439
|
+
status: "completed",
|
|
23440
|
+
placesSearched: summary.placesSearched,
|
|
23441
|
+
message: "Local conversation import finished",
|
|
23442
|
+
currentPlace: "Codex and Claude Code stores",
|
|
23443
|
+
metadata: {
|
|
23444
|
+
worker_id: "conversations-local-import",
|
|
23445
|
+
worker_label: "Local conversation import",
|
|
23446
|
+
duration_ms: summary.durationMs,
|
|
23447
|
+
codex_count: sources.includes("codex") ? summary.codexCount : 0,
|
|
23448
|
+
claude_code_count: sources.includes("claude_code") ? summary.claudeCodeCount : 0,
|
|
23449
|
+
candidate_limit: onboardingConversationImportCandidateLimit,
|
|
23450
|
+
imported_count: importedCount,
|
|
23451
|
+
failed_import_count: failedImportCount,
|
|
23452
|
+
skipped_import_count: skippedImportCount,
|
|
23453
|
+
reported_count: importedCount,
|
|
23454
|
+
total_count: totalCount
|
|
23455
|
+
}
|
|
23456
|
+
});
|
|
23457
|
+
}
|
|
23458
|
+
function onboardingConversationImportRunKey(runnerId, workspaceSlug, sources) {
|
|
23459
|
+
return `${runnerId}:${workspaceSlug}:${[...sources].sort().join(",")}`;
|
|
23460
|
+
}
|
|
23461
|
+
function onboardingConversationImportSources(value) {
|
|
23462
|
+
return Array.from(
|
|
23463
|
+
new Set(
|
|
23464
|
+
(arrayValue(value) ?? []).flatMap((source) => {
|
|
23465
|
+
switch (stringValue(source)) {
|
|
23466
|
+
case "codex":
|
|
23467
|
+
return ["codex"];
|
|
23468
|
+
case "claude_code":
|
|
23469
|
+
return ["claude_code"];
|
|
23470
|
+
default:
|
|
23471
|
+
return [];
|
|
23472
|
+
}
|
|
23473
|
+
})
|
|
23474
|
+
)
|
|
23475
|
+
).sort();
|
|
23476
|
+
}
|
|
23477
|
+
async function replayLocalOnboardingConversation(args) {
|
|
23478
|
+
let replayedChunkCount = 0;
|
|
23479
|
+
for (const message of args.messages) {
|
|
23480
|
+
const chunks = onboardingConversationImportMessageChunks(message);
|
|
23481
|
+
for (const chunk of chunks) {
|
|
23482
|
+
const clientMessageId = onboardingConversationImportClientMessageId(
|
|
23483
|
+
args.candidate.importKey,
|
|
23484
|
+
chunk.itemKey
|
|
23485
|
+
);
|
|
23486
|
+
const writeMetadata = {
|
|
23487
|
+
import_role: message.role,
|
|
23488
|
+
import_source: args.candidate.source,
|
|
23489
|
+
...chunk.activityAt === void 0 ? {} : { source_activity_at: chunk.activityAt }
|
|
23490
|
+
};
|
|
23491
|
+
const chunkHasAttachments = chunk.attachments.length > 0 && chunk.index === chunks.length - 1;
|
|
23492
|
+
if (!chunkHasAttachments) {
|
|
23493
|
+
await args.client.sendThreadReply({
|
|
23494
|
+
workspace: args.workspaceSlug,
|
|
23495
|
+
thread_id: args.threadId,
|
|
23496
|
+
body: chunk.body,
|
|
23497
|
+
client_message_id: clientMessageId,
|
|
23498
|
+
...writeMetadata
|
|
23499
|
+
});
|
|
23500
|
+
replayedChunkCount += 1;
|
|
23501
|
+
continue;
|
|
23502
|
+
}
|
|
23503
|
+
const uploadResult = await uploadLocalConversationAttachments({
|
|
23504
|
+
client: args.client,
|
|
23505
|
+
options: args.options,
|
|
23506
|
+
workspaceSlug: args.workspaceSlug,
|
|
23507
|
+
candidatePath: args.candidate.path,
|
|
23508
|
+
body: chunk.body,
|
|
23509
|
+
attachments: chunk.attachments
|
|
23510
|
+
});
|
|
23511
|
+
if (uploadResult.uploadedFileIds.length > 0) {
|
|
23512
|
+
await args.client.attachMessageFiles({
|
|
23513
|
+
workspace: args.workspaceSlug,
|
|
23514
|
+
target: { kind: "new_message", thread_id: args.threadId },
|
|
23515
|
+
body: uploadResult.body,
|
|
23516
|
+
uploaded_file_ids: uploadResult.uploadedFileIds,
|
|
23517
|
+
client_message_id: clientMessageId,
|
|
23518
|
+
...writeMetadata
|
|
23519
|
+
});
|
|
23520
|
+
replayedChunkCount += 1;
|
|
23521
|
+
continue;
|
|
23522
|
+
}
|
|
23523
|
+
await args.client.sendThreadReply({
|
|
23524
|
+
workspace: args.workspaceSlug,
|
|
23525
|
+
thread_id: args.threadId,
|
|
23526
|
+
body: uploadResult.body,
|
|
23527
|
+
client_message_id: clientMessageId,
|
|
23528
|
+
...writeMetadata
|
|
23529
|
+
});
|
|
23530
|
+
replayedChunkCount += 1;
|
|
23531
|
+
}
|
|
23532
|
+
}
|
|
23533
|
+
return replayedChunkCount;
|
|
23534
|
+
}
|
|
23535
|
+
function onboardingConversationImportMessageChunks(message) {
|
|
23536
|
+
const parts = splitOnboardingConversationImportBody(message.body);
|
|
23537
|
+
return parts.map((body, index) => ({
|
|
23538
|
+
itemKey: parts.length === 1 ? message.itemKey : `${message.itemKey}:part-${index + 1}`,
|
|
23539
|
+
body,
|
|
23540
|
+
attachments: index === parts.length - 1 ? message.attachments : [],
|
|
23541
|
+
activityAt: message.activityAt,
|
|
23542
|
+
index
|
|
23543
|
+
}));
|
|
23544
|
+
}
|
|
23545
|
+
function splitOnboardingConversationImportBody(body) {
|
|
23546
|
+
const trimmed = body.trim();
|
|
23547
|
+
if (trimmed.length <= onboardingConversationImportMessageMaxLength) {
|
|
23548
|
+
return [trimmed];
|
|
23549
|
+
}
|
|
23550
|
+
const parts = [];
|
|
23551
|
+
let remaining = trimmed;
|
|
23552
|
+
while (remaining.length > onboardingConversationImportMessageMaxLength) {
|
|
23553
|
+
const slice = remaining.slice(
|
|
23554
|
+
0,
|
|
23555
|
+
onboardingConversationImportMessageMaxLength
|
|
23556
|
+
);
|
|
23557
|
+
const paragraphBreak = slice.lastIndexOf("\n\n");
|
|
23558
|
+
const lineBreak = slice.lastIndexOf("\n");
|
|
23559
|
+
const splitAt = paragraphBreak > onboardingConversationImportMessageMaxLength / 2 ? paragraphBreak : lineBreak > onboardingConversationImportMessageMaxLength / 2 ? lineBreak : onboardingConversationImportMessageMaxLength;
|
|
23560
|
+
const part = remaining.slice(0, splitAt).trim();
|
|
23561
|
+
if (part !== "") {
|
|
23562
|
+
parts.push(part);
|
|
23563
|
+
}
|
|
23564
|
+
remaining = remaining.slice(splitAt).trim();
|
|
23565
|
+
}
|
|
23566
|
+
if (remaining !== "") {
|
|
23567
|
+
parts.push(remaining);
|
|
23568
|
+
}
|
|
23569
|
+
return parts;
|
|
23570
|
+
}
|
|
23571
|
+
function onboardingConversationTitleMessages(messages) {
|
|
23572
|
+
const indexedMessages = messages.map((message, index) => ({
|
|
23573
|
+
message,
|
|
23574
|
+
index
|
|
23575
|
+
}));
|
|
23576
|
+
const firstMessages = indexedMessages.slice(0, onboardingConversationTitleFirstMessages).map(
|
|
23577
|
+
({ message, index }) => onboardingConversationTitleMessage("first", index, message)
|
|
23578
|
+
);
|
|
23579
|
+
const lastStartIndex = Math.max(
|
|
23580
|
+
onboardingConversationTitleFirstMessages,
|
|
23581
|
+
indexedMessages.length - onboardingConversationTitleLastMessages
|
|
23582
|
+
);
|
|
23583
|
+
const lastMessages = indexedMessages.slice(lastStartIndex).map(
|
|
23584
|
+
({ message, index }) => onboardingConversationTitleMessage("last", index, message)
|
|
23585
|
+
);
|
|
23586
|
+
return [...firstMessages, ...lastMessages].filter(
|
|
23587
|
+
(message) => message !== void 0
|
|
23588
|
+
);
|
|
23589
|
+
}
|
|
23590
|
+
function onboardingConversationTitleMessage(position, index, message) {
|
|
23591
|
+
const body = message.body.trim();
|
|
23592
|
+
if (body === "") {
|
|
23593
|
+
return void 0;
|
|
23594
|
+
}
|
|
23595
|
+
return {
|
|
23596
|
+
position,
|
|
23597
|
+
index,
|
|
23598
|
+
role: message.role,
|
|
23599
|
+
body: body.length > onboardingConversationTitleBodyMaxLength ? `${body.slice(0, onboardingConversationTitleBodyMaxLength).trim()}...` : body
|
|
23600
|
+
};
|
|
23601
|
+
}
|
|
23602
|
+
async function markLocalOnboardingConversationReplayCompleted(args) {
|
|
23603
|
+
await args.client.noteAgentConversation({
|
|
23604
|
+
workspace: args.workspaceSlug,
|
|
23605
|
+
runner_id: args.runnerId,
|
|
23606
|
+
source: args.candidate.source,
|
|
23607
|
+
import_key: args.candidate.importKey,
|
|
23608
|
+
import_to_office_channel: true,
|
|
23609
|
+
path: args.candidate.path,
|
|
23610
|
+
display_name: args.candidate.displayName,
|
|
23611
|
+
...args.candidate.activityAt === void 0 ? {} : { activity_at: args.candidate.activityAt },
|
|
23612
|
+
metadata: {
|
|
23613
|
+
...args.candidate.metadata,
|
|
23614
|
+
office_channel_import: {
|
|
23615
|
+
...args.officeImport ?? {},
|
|
23616
|
+
thread_id: args.threadId,
|
|
23617
|
+
replayed_message_count: args.replayedMessageCount,
|
|
23618
|
+
replayed_chunk_count: args.replayedChunkCount,
|
|
23619
|
+
replay_completed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
23620
|
+
}
|
|
23621
|
+
}
|
|
23622
|
+
});
|
|
23623
|
+
}
|
|
23624
|
+
function onboardingConversationOfficeImport(response) {
|
|
23625
|
+
const discovery = objectValue(response.discovery);
|
|
23626
|
+
const metadata = objectValue(discovery?.metadata);
|
|
23627
|
+
return objectValue(metadata?.office_channel_import);
|
|
23628
|
+
}
|
|
23629
|
+
function onboardingConversationImportClientMessageId(importKey, itemKey) {
|
|
23630
|
+
const importHash = createHash3("sha256").update(importKey).digest("hex");
|
|
23631
|
+
const itemHash = createHash3("sha256").update(itemKey).digest("hex");
|
|
23632
|
+
return `onboarding-import:${importHash.slice(0, 32)}:${itemHash.slice(0, 24)}`;
|
|
23633
|
+
}
|
|
23634
|
+
async function uploadLocalConversationAttachments(args) {
|
|
23635
|
+
const files = (await Promise.all(
|
|
23636
|
+
args.attachments.map(
|
|
23637
|
+
(attachment) => localConversationAttachmentFile(args.candidatePath, attachment)
|
|
23638
|
+
)
|
|
23639
|
+
)).filter(
|
|
23640
|
+
(file) => file !== void 0
|
|
23641
|
+
);
|
|
23642
|
+
if (files.length === 0) {
|
|
23643
|
+
return {
|
|
23644
|
+
body: `${args.body}
|
|
23645
|
+
|
|
23646
|
+
${args.attachments.length} referenced attachment${args.attachments.length === 1 ? "" : "s"} were not available on disk.`,
|
|
23647
|
+
uploadedFileIds: []
|
|
23648
|
+
};
|
|
23649
|
+
}
|
|
23650
|
+
const prepare = await args.client.prepareMessageUploads({
|
|
23651
|
+
workspace: args.workspaceSlug,
|
|
23652
|
+
files: files.map((file) => ({
|
|
23653
|
+
file_name: file.fileName,
|
|
23654
|
+
content_type: file.contentType,
|
|
23655
|
+
size_bytes: file.bytes.byteLength
|
|
23656
|
+
}))
|
|
23657
|
+
});
|
|
23658
|
+
const uploads = arrayValue(prepare.uploads) ?? [];
|
|
23659
|
+
if (uploads.length !== files.length) {
|
|
23660
|
+
throw new Error("Linzumi upload prepare response count mismatch");
|
|
23661
|
+
}
|
|
23662
|
+
const preparedUploads = files.map((file, index) => {
|
|
23663
|
+
const upload = objectValue(uploads[index]);
|
|
23664
|
+
const uploadUrl = stringValue(upload?.upload_url);
|
|
23665
|
+
const uploadMethod = stringValue(upload?.upload_method) ?? "PUT";
|
|
23666
|
+
const fileId = stringValue(upload?.file_id);
|
|
23667
|
+
if (uploadUrl === void 0) {
|
|
23668
|
+
throw new Error("Linzumi upload prepare response missing upload_url");
|
|
23669
|
+
}
|
|
23670
|
+
if (fileId === void 0) {
|
|
23671
|
+
throw new Error("Linzumi upload prepare response missing file_id");
|
|
23672
|
+
}
|
|
23673
|
+
return {
|
|
23674
|
+
file,
|
|
23675
|
+
fileId,
|
|
23676
|
+
uploadMethod,
|
|
23677
|
+
uploadUrl
|
|
23678
|
+
};
|
|
23679
|
+
});
|
|
23680
|
+
const uploadResults = await Promise.allSettled(
|
|
23681
|
+
preparedUploads.map(async ({ file, uploadMethod, uploadUrl }) => {
|
|
23682
|
+
const uploadBody = file.bytes.buffer.slice(
|
|
23683
|
+
file.bytes.byteOffset,
|
|
23684
|
+
file.bytes.byteOffset + file.bytes.byteLength
|
|
23685
|
+
);
|
|
23686
|
+
const response = await (args.options.fetch ?? globalThis.fetch)(
|
|
23687
|
+
resolveLinzumiUploadUrl(args.options.kandanUrl, uploadUrl),
|
|
23688
|
+
{
|
|
23689
|
+
method: uploadMethod,
|
|
23690
|
+
headers: { "content-type": file.contentType },
|
|
23691
|
+
body: uploadBody
|
|
23692
|
+
}
|
|
23693
|
+
);
|
|
23694
|
+
if (!response.ok) {
|
|
23695
|
+
throw new Error(
|
|
23696
|
+
`Linzumi upload failed for ${file.fileName}: ${response.status} ${response.statusText}`
|
|
23697
|
+
);
|
|
23698
|
+
}
|
|
23699
|
+
})
|
|
23700
|
+
);
|
|
23701
|
+
const uploadedFileIds = preparedUploads.flatMap((upload, index) => {
|
|
23702
|
+
switch (uploadResults[index]?.status) {
|
|
23703
|
+
case "fulfilled":
|
|
23704
|
+
return [upload.fileId];
|
|
23705
|
+
default:
|
|
23706
|
+
return [];
|
|
23707
|
+
}
|
|
23708
|
+
});
|
|
23709
|
+
const failedUploadCount = uploadResults.filter(
|
|
23710
|
+
(result) => result.status === "rejected"
|
|
23711
|
+
).length;
|
|
23712
|
+
return {
|
|
23713
|
+
body: failedUploadCount === 0 ? args.body : `${args.body}
|
|
23714
|
+
|
|
23715
|
+
${failedUploadCount} referenced attachment${failedUploadCount === 1 ? "" : "s"} failed to upload.`,
|
|
23716
|
+
uploadedFileIds
|
|
23717
|
+
};
|
|
23718
|
+
}
|
|
23719
|
+
async function localConversationAttachmentFile(candidatePath, attachment) {
|
|
23720
|
+
switch (attachment.kind) {
|
|
23721
|
+
case "bytes": {
|
|
23722
|
+
return {
|
|
23723
|
+
fileName: attachment.fileName,
|
|
23724
|
+
contentType: attachment.contentType,
|
|
23725
|
+
bytes: Buffer.from(attachment.dataBase64, "base64")
|
|
23726
|
+
};
|
|
23727
|
+
}
|
|
23728
|
+
case "path": {
|
|
23729
|
+
const path2 = isAbsolute4(attachment.path) ? attachment.path : resolve7(dirname12(candidatePath), attachment.path);
|
|
23730
|
+
try {
|
|
23731
|
+
const bytes = await readFile2(path2);
|
|
23732
|
+
return {
|
|
23733
|
+
fileName: attachment.fileName ?? basename8(path2),
|
|
23734
|
+
contentType: attachment.contentType ?? contentTypeForPath(path2),
|
|
23735
|
+
bytes
|
|
23736
|
+
};
|
|
23737
|
+
} catch (_error) {
|
|
23738
|
+
return void 0;
|
|
23739
|
+
}
|
|
23740
|
+
}
|
|
23741
|
+
}
|
|
23742
|
+
}
|
|
23743
|
+
function contentTypeForPath(path2) {
|
|
23744
|
+
switch (extname2(path2).toLowerCase()) {
|
|
23745
|
+
case ".gif":
|
|
23746
|
+
return "image/gif";
|
|
23747
|
+
case ".jpeg":
|
|
23748
|
+
case ".jpg":
|
|
23749
|
+
return "image/jpeg";
|
|
23750
|
+
case ".png":
|
|
23751
|
+
return "image/png";
|
|
23752
|
+
case ".webp":
|
|
23753
|
+
return "image/webp";
|
|
23754
|
+
case ".json":
|
|
23755
|
+
case ".jsonl":
|
|
23756
|
+
return "application/json";
|
|
23757
|
+
case ".md":
|
|
23758
|
+
return "text/markdown";
|
|
23759
|
+
case ".txt":
|
|
23760
|
+
return "text/plain";
|
|
23761
|
+
default:
|
|
23762
|
+
return "application/octet-stream";
|
|
23763
|
+
}
|
|
23764
|
+
}
|
|
23765
|
+
function resolveLinzumiUploadUrl(kandanUrl, uploadUrl) {
|
|
23766
|
+
try {
|
|
23767
|
+
return new URL(uploadUrl).toString();
|
|
23768
|
+
} catch (_error) {
|
|
23769
|
+
const httpBase = kandanUrl.replace(/^wss:\/\//, "https://").replace(/^ws:\/\//, "http://");
|
|
23770
|
+
return new URL(uploadUrl, httpBase).toString();
|
|
23771
|
+
}
|
|
23772
|
+
}
|
|
23773
|
+
async function runLocalOnboardingConversationDiscovery(options, log) {
|
|
23774
|
+
const workspaceSlug = runnerWorkspaceSlug(options);
|
|
23775
|
+
if (workspaceSlug === void 0) {
|
|
23776
|
+
return;
|
|
23777
|
+
}
|
|
23778
|
+
await reportOnboardingDiscoveryStatus(options, log, {
|
|
23779
|
+
phase: "conversations",
|
|
23780
|
+
status: "starting",
|
|
23781
|
+
placesSearched: 0,
|
|
23782
|
+
message: "Counting local conversations",
|
|
23783
|
+
currentPlace: "Codex and Claude Code stores",
|
|
23784
|
+
metadata: {
|
|
23785
|
+
worker_id: "conversations-local-filesystem",
|
|
23786
|
+
worker_label: "Local conversation files"
|
|
23787
|
+
}
|
|
23788
|
+
});
|
|
23789
|
+
let successfulPostStartWriteCount = 0;
|
|
23790
|
+
try {
|
|
23791
|
+
const summary = options.localConversationDiscovery?.() ?? discoverLocalConversations();
|
|
23792
|
+
const reportedCandidates = summary.candidates.slice(
|
|
23793
|
+
0,
|
|
23794
|
+
onboardingConversationDiscoveryReportLimit
|
|
23795
|
+
);
|
|
23796
|
+
const baseMetadata = {
|
|
23797
|
+
worker_id: "conversations-local-filesystem",
|
|
23798
|
+
worker_label: "Local conversation files",
|
|
23799
|
+
duration_ms: summary.durationMs,
|
|
23800
|
+
codex_count: summary.codexCount,
|
|
23801
|
+
claude_code_count: summary.claudeCodeCount,
|
|
23802
|
+
claude_code_subagent_count: summary.claudeCodeSubagentCount,
|
|
23803
|
+
reported_count: reportedCandidates.length,
|
|
23804
|
+
report_limit: onboardingConversationDiscoveryReportLimit,
|
|
23805
|
+
search_stats: summary.searchStats
|
|
23806
|
+
};
|
|
23807
|
+
successfulPostStartWriteCount = await addStatusWriteResult(
|
|
23808
|
+
successfulPostStartWriteCount,
|
|
23809
|
+
reportOnboardingDiscoveryStatus(options, log, {
|
|
23810
|
+
phase: "conversations",
|
|
23811
|
+
status: "running",
|
|
23812
|
+
placesSearched: summary.placesSearched,
|
|
23813
|
+
message: "Reporting local conversations",
|
|
23814
|
+
currentPlace: "Codex and Claude Code stores",
|
|
23815
|
+
metadata: baseMetadata
|
|
23816
|
+
})
|
|
23817
|
+
);
|
|
23818
|
+
const client = createLinzumiMcpApiClient({
|
|
23819
|
+
kandanUrl: options.kandanUrl,
|
|
23820
|
+
accessToken: options.token,
|
|
23821
|
+
fetchImpl: options.fetch,
|
|
23822
|
+
operatingMode: "text"
|
|
23823
|
+
});
|
|
23824
|
+
const reportResults = await Promise.allSettled(
|
|
23825
|
+
reportedCandidates.map(
|
|
23826
|
+
(candidate) => client.noteAgentConversation({
|
|
23827
|
+
workspace: workspaceSlug,
|
|
23828
|
+
runner_id: options.runnerId,
|
|
23829
|
+
source: candidate.source,
|
|
23830
|
+
import_key: candidate.importKey,
|
|
23831
|
+
path: candidate.path,
|
|
23832
|
+
display_name: candidate.displayName,
|
|
23833
|
+
...candidate.activityAt === void 0 ? {} : { activity_at: candidate.activityAt },
|
|
23834
|
+
metadata: candidate.metadata
|
|
23835
|
+
})
|
|
23836
|
+
)
|
|
23837
|
+
);
|
|
23838
|
+
const failedReportCount = reportResults.filter(
|
|
23839
|
+
(result) => result.status === "rejected"
|
|
23840
|
+
).length;
|
|
23841
|
+
successfulPostStartWriteCount += successfulSettledWriteCount(reportResults);
|
|
23842
|
+
const completedMetadata = {
|
|
23843
|
+
...baseMetadata,
|
|
23844
|
+
failed_report_count: failedReportCount
|
|
23845
|
+
};
|
|
23846
|
+
const completedReport = {
|
|
23847
|
+
phase: "conversations",
|
|
23848
|
+
status: "completed",
|
|
23849
|
+
placesSearched: summary.placesSearched,
|
|
23850
|
+
message: "Local conversation scan finished",
|
|
23851
|
+
currentPlace: "Codex and Claude Code stores",
|
|
23852
|
+
metadata: completedMetadata
|
|
23853
|
+
};
|
|
23854
|
+
successfulPostStartWriteCount = await addStatusWriteResult(
|
|
23855
|
+
successfulPostStartWriteCount,
|
|
23856
|
+
reportOnboardingDiscoveryStatus(options, log, completedReport)
|
|
23857
|
+
);
|
|
23858
|
+
log("onboarding.local_conversation_discovery_completed", {
|
|
23859
|
+
runnerId: options.runnerId,
|
|
23860
|
+
workspace: workspaceSlug,
|
|
23861
|
+
duration_ms: summary.durationMs,
|
|
23862
|
+
codex_count: summary.codexCount,
|
|
23863
|
+
claude_code_count: summary.claudeCodeCount,
|
|
23864
|
+
claude_code_subagent_count: summary.claudeCodeSubagentCount,
|
|
23865
|
+
reported_count: reportedCandidates.length,
|
|
23866
|
+
failed_report_count: failedReportCount
|
|
23867
|
+
});
|
|
23868
|
+
return withSuccessfulWriteCount(
|
|
23869
|
+
completedReport,
|
|
23870
|
+
successfulPostStartWriteCount
|
|
23871
|
+
);
|
|
23872
|
+
} catch (error) {
|
|
23873
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
23874
|
+
const failedReport = {
|
|
23875
|
+
phase: "conversations",
|
|
23876
|
+
status: "failed",
|
|
23877
|
+
placesSearched: 0,
|
|
23878
|
+
message: "Local conversation scan failed",
|
|
23879
|
+
currentPlace: "Codex and Claude Code stores",
|
|
23880
|
+
lastError: message,
|
|
23881
|
+
metadata: {
|
|
23882
|
+
worker_id: "conversations-local-filesystem",
|
|
23883
|
+
worker_label: "Local conversation files"
|
|
23884
|
+
}
|
|
23885
|
+
};
|
|
23886
|
+
successfulPostStartWriteCount = await addStatusWriteResult(
|
|
23887
|
+
successfulPostStartWriteCount,
|
|
23888
|
+
reportOnboardingDiscoveryStatus(options, log, failedReport)
|
|
23889
|
+
);
|
|
23890
|
+
log("onboarding.local_conversation_discovery_failed", {
|
|
23891
|
+
runnerId: options.runnerId,
|
|
23892
|
+
workspace: workspaceSlug,
|
|
23893
|
+
message
|
|
23894
|
+
});
|
|
23895
|
+
return withSuccessfulWriteCount(
|
|
23896
|
+
failedReport,
|
|
23897
|
+
successfulPostStartWriteCount
|
|
23898
|
+
);
|
|
23899
|
+
}
|
|
23900
|
+
}
|
|
23901
|
+
async function runLocalOnboardingProjectDiscovery(options, log) {
|
|
23902
|
+
const workspaceSlug = runnerWorkspaceSlug(options);
|
|
23903
|
+
if (workspaceSlug === void 0) {
|
|
23904
|
+
return;
|
|
23905
|
+
}
|
|
23906
|
+
await reportOnboardingDiscoveryStatus(options, log, {
|
|
23907
|
+
phase: "projects",
|
|
23908
|
+
status: "starting",
|
|
23909
|
+
placesSearched: 0,
|
|
23910
|
+
message: "Checking current Git project",
|
|
23911
|
+
currentPlace: options.cwd,
|
|
23912
|
+
currentSource: "git",
|
|
23913
|
+
metadata: {
|
|
23914
|
+
worker_id: "projects-local-current",
|
|
23915
|
+
worker_label: "Current Git project"
|
|
23916
|
+
}
|
|
23917
|
+
});
|
|
23918
|
+
let successfulPostStartWriteCount = 0;
|
|
23919
|
+
try {
|
|
23920
|
+
const summary = options.localProjectDiscovery?.() ?? discoverCurrentGitProject({ cwd: options.cwd });
|
|
23921
|
+
const client = createLinzumiMcpApiClient({
|
|
23922
|
+
kandanUrl: options.kandanUrl,
|
|
23923
|
+
accessToken: options.token,
|
|
23924
|
+
fetchImpl: options.fetch,
|
|
23925
|
+
operatingMode: "text"
|
|
23926
|
+
});
|
|
23927
|
+
const reportResults = await Promise.allSettled(
|
|
23928
|
+
summary.candidates.map(
|
|
23929
|
+
(candidate) => client.noteProjectDirectory({
|
|
23930
|
+
workspace: workspaceSlug,
|
|
23931
|
+
runner_id: options.runnerId,
|
|
23932
|
+
import_key: candidate.importKey,
|
|
23933
|
+
path: candidate.path,
|
|
23934
|
+
display_name: candidate.displayName,
|
|
23935
|
+
...candidate.activityAt === void 0 ? {} : { activity_at: candidate.activityAt },
|
|
23936
|
+
metadata: candidate.metadata
|
|
23937
|
+
})
|
|
23938
|
+
)
|
|
23939
|
+
);
|
|
23940
|
+
const failedReportCount = reportResults.filter(
|
|
23941
|
+
(result) => result.status === "rejected"
|
|
23942
|
+
).length;
|
|
23943
|
+
successfulPostStartWriteCount += successfulSettledWriteCount(reportResults);
|
|
23944
|
+
const completedReport = {
|
|
23945
|
+
phase: "projects",
|
|
23946
|
+
status: "completed",
|
|
23947
|
+
placesSearched: summary.placesSearched,
|
|
23948
|
+
message: summary.candidates.length === 0 ? "Current folder is not a Git project" : "Current Git project found",
|
|
23949
|
+
currentPlace: options.cwd,
|
|
23950
|
+
currentSource: "git",
|
|
23951
|
+
metadata: {
|
|
23952
|
+
worker_id: "projects-local-current",
|
|
23953
|
+
worker_label: "Current Git project",
|
|
23954
|
+
duration_ms: summary.durationMs,
|
|
23955
|
+
reported_count: summary.candidates.length,
|
|
23956
|
+
failed_report_count: failedReportCount,
|
|
23957
|
+
search_stats: summary.searchStats
|
|
23958
|
+
}
|
|
23959
|
+
};
|
|
23960
|
+
successfulPostStartWriteCount = await addStatusWriteResult(
|
|
23961
|
+
successfulPostStartWriteCount,
|
|
23962
|
+
reportOnboardingDiscoveryStatus(options, log, completedReport)
|
|
23963
|
+
);
|
|
23964
|
+
log("onboarding.local_project_discovery_completed", {
|
|
23965
|
+
runnerId: options.runnerId,
|
|
23966
|
+
workspace: workspaceSlug,
|
|
23967
|
+
duration_ms: summary.durationMs,
|
|
23968
|
+
reported_count: summary.candidates.length,
|
|
23969
|
+
failed_report_count: failedReportCount
|
|
23970
|
+
});
|
|
23971
|
+
return withSuccessfulWriteCount(
|
|
23972
|
+
completedReport,
|
|
23973
|
+
successfulPostStartWriteCount
|
|
23974
|
+
);
|
|
23975
|
+
} catch (error) {
|
|
23976
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
23977
|
+
const failedReport = {
|
|
23978
|
+
phase: "projects",
|
|
23979
|
+
status: "failed",
|
|
23980
|
+
placesSearched: 0,
|
|
23981
|
+
message: "Current Git project scan failed",
|
|
23982
|
+
currentPlace: options.cwd,
|
|
23983
|
+
currentSource: "git",
|
|
23984
|
+
lastError: message,
|
|
23985
|
+
metadata: {
|
|
23986
|
+
worker_id: "projects-local-current",
|
|
23987
|
+
worker_label: "Current Git project"
|
|
23988
|
+
}
|
|
23989
|
+
};
|
|
23990
|
+
successfulPostStartWriteCount = await addStatusWriteResult(
|
|
23991
|
+
successfulPostStartWriteCount,
|
|
23992
|
+
reportOnboardingDiscoveryStatus(options, log, failedReport)
|
|
23993
|
+
);
|
|
23994
|
+
log("onboarding.local_project_discovery_failed", {
|
|
23995
|
+
runnerId: options.runnerId,
|
|
23996
|
+
workspace: workspaceSlug,
|
|
23997
|
+
message
|
|
23998
|
+
});
|
|
23999
|
+
return withSuccessfulWriteCount(
|
|
24000
|
+
failedReport,
|
|
24001
|
+
successfulPostStartWriteCount
|
|
24002
|
+
);
|
|
24003
|
+
}
|
|
24004
|
+
}
|
|
24005
|
+
function shouldStartOnboardingDiscoveryAgents(options, workspaceSlug = runnerWorkspaceSlug(options)) {
|
|
24006
|
+
return workspaceSlug !== void 0 && options.threadProcess === void 0 && options.onboardingDiscovery === "start";
|
|
24007
|
+
}
|
|
24008
|
+
async function reportOnboardingDiscoveryStatus(options, log, args) {
|
|
24009
|
+
const workspaceSlug = runnerWorkspaceSlug(options);
|
|
24010
|
+
if (workspaceSlug === void 0) {
|
|
24011
|
+
return false;
|
|
24012
|
+
}
|
|
24013
|
+
const client = createLinzumiMcpApiClient({
|
|
24014
|
+
kandanUrl: options.kandanUrl,
|
|
24015
|
+
accessToken: options.token,
|
|
24016
|
+
fetchImpl: options.fetch,
|
|
24017
|
+
operatingMode: "text"
|
|
24018
|
+
});
|
|
24019
|
+
const payload = {
|
|
24020
|
+
workspace: workspaceSlug,
|
|
24021
|
+
runner_id: options.runnerId,
|
|
24022
|
+
phase: args.phase,
|
|
24023
|
+
status: args.status,
|
|
24024
|
+
places_searched: args.placesSearched,
|
|
24025
|
+
message: args.message,
|
|
24026
|
+
...args.currentPlace === void 0 || args.currentPlace === "" ? {} : { current_place: args.currentPlace },
|
|
24027
|
+
...args.currentSource === void 0 || args.currentSource === "" ? {} : { current_source: args.currentSource },
|
|
24028
|
+
...args.lastError === void 0 || args.lastError === "" ? {} : { last_error: args.lastError },
|
|
24029
|
+
...args.metadata === void 0 ? {} : { metadata: args.metadata }
|
|
24030
|
+
};
|
|
24031
|
+
log("onboarding.discovery_status", payload);
|
|
24032
|
+
try {
|
|
24033
|
+
await client.noteOnboardingDiscoveryStatus(payload);
|
|
24034
|
+
return true;
|
|
24035
|
+
} catch (error) {
|
|
24036
|
+
log("onboarding.discovery_status_failed", {
|
|
24037
|
+
phase: args.phase,
|
|
24038
|
+
status: args.status,
|
|
24039
|
+
message: error instanceof Error ? error.message : String(error)
|
|
24040
|
+
});
|
|
24041
|
+
return false;
|
|
24042
|
+
}
|
|
24043
|
+
}
|
|
24044
|
+
function onboardingDiscoveryAgentStartKey(options, workspaceSlug) {
|
|
24045
|
+
return `${options.kandanUrl}\0${workspaceSlug}\0${options.runnerId}`;
|
|
24046
|
+
}
|
|
21576
24047
|
async function startOwnedCodexAppServer(options, args = { linzumiMcp: true }) {
|
|
21577
24048
|
ensureCodexProjectTrusted(options.cwd);
|
|
21578
24049
|
const defaults = runnerRuntimeDefaults(options);
|
|
@@ -21629,9 +24100,9 @@ function mcpOwnerUsername(options) {
|
|
|
21629
24100
|
return options.channelSession?.listenUser ?? identityFromAccessToken(options.token).actorUsername;
|
|
21630
24101
|
}
|
|
21631
24102
|
function writeEphemeralMcpAuthFile(options) {
|
|
21632
|
-
const directory = mkdtempSync4(
|
|
24103
|
+
const directory = mkdtempSync4(join17(tmpdir3(), "linzumi-mcp-auth-"));
|
|
21633
24104
|
chmodSync2(directory, 448);
|
|
21634
|
-
const path2 =
|
|
24105
|
+
const path2 = join17(directory, "auth.json");
|
|
21635
24106
|
writeCachedLocalRunnerToken({
|
|
21636
24107
|
kandanUrl: options.kandanUrl,
|
|
21637
24108
|
accessToken: options.token,
|
|
@@ -21707,7 +24178,7 @@ function configuredAllowedCwds(values, options = {}) {
|
|
|
21707
24178
|
const absolutePath = resolve7(expandUserPath(value));
|
|
21708
24179
|
try {
|
|
21709
24180
|
if (options.createMissing === true) {
|
|
21710
|
-
|
|
24181
|
+
mkdirSync10(absolutePath, { recursive: true });
|
|
21711
24182
|
}
|
|
21712
24183
|
const realPath = realpathSync6(absolutePath);
|
|
21713
24184
|
allowedCwds.push(
|
|
@@ -21744,9 +24215,9 @@ function allowedCwdProjects(allowedCwds) {
|
|
|
21744
24215
|
});
|
|
21745
24216
|
}
|
|
21746
24217
|
function isGitProjectDirectory(cwd) {
|
|
21747
|
-
const gitPath =
|
|
24218
|
+
const gitPath = join17(cwd, ".git");
|
|
21748
24219
|
try {
|
|
21749
|
-
const gitPathStats =
|
|
24220
|
+
const gitPathStats = statSync3(gitPath);
|
|
21750
24221
|
return gitPathStats.isDirectory() || gitPathStats.isFile();
|
|
21751
24222
|
} catch {
|
|
21752
24223
|
return false;
|
|
@@ -21757,7 +24228,7 @@ function browseRunnerDirectory(control, options) {
|
|
|
21757
24228
|
const requestedPath = stringValue(control.path) ?? currentHomeDirectory();
|
|
21758
24229
|
try {
|
|
21759
24230
|
const currentPath = realpathSync6(resolve7(expandUserPath(requestedPath)));
|
|
21760
|
-
const stats =
|
|
24231
|
+
const stats = statSync3(currentPath);
|
|
21761
24232
|
if (!stats.isDirectory()) {
|
|
21762
24233
|
return {
|
|
21763
24234
|
instanceId: options.runnerId,
|
|
@@ -21767,9 +24238,9 @@ function browseRunnerDirectory(control, options) {
|
|
|
21767
24238
|
error: "not_directory"
|
|
21768
24239
|
};
|
|
21769
24240
|
}
|
|
21770
|
-
const parent =
|
|
21771
|
-
const entries =
|
|
21772
|
-
const path2 =
|
|
24241
|
+
const parent = dirname12(currentPath);
|
|
24242
|
+
const entries = readdirSync4(currentPath, { withFileTypes: true }).filter((entry) => entry.isDirectory()).filter((entry) => visibleRunnerDirectoryEntryName(entry.name)).map((entry) => {
|
|
24243
|
+
const path2 = join17(currentPath, entry.name);
|
|
21773
24244
|
return {
|
|
21774
24245
|
name: entry.name,
|
|
21775
24246
|
path: path2,
|
|
@@ -21810,7 +24281,7 @@ function projectDirectoryName(name) {
|
|
|
21810
24281
|
function availableProjectDirectoryName(projectsRoot, baseName, suffix = 0) {
|
|
21811
24282
|
for (let nextSuffix = suffix; ; nextSuffix += 1) {
|
|
21812
24283
|
const candidate = nextSuffix === 0 ? baseName : `${baseName}-${nextSuffix}`;
|
|
21813
|
-
if (!projectPathExists(
|
|
24284
|
+
if (!projectPathExists(join17(projectsRoot, candidate))) {
|
|
21814
24285
|
return candidate;
|
|
21815
24286
|
}
|
|
21816
24287
|
}
|
|
@@ -21838,9 +24309,9 @@ function createRunnerProject(control, options, allowedCwds) {
|
|
|
21838
24309
|
error: "invalid_project_template"
|
|
21839
24310
|
};
|
|
21840
24311
|
}
|
|
21841
|
-
const projectsRoot =
|
|
24312
|
+
const projectsRoot = join17(currentHomeDirectory(), "linzumi");
|
|
21842
24313
|
const resolvedProjectDirName = template === "hello_linzumi_demo" ? availableProjectDirectoryName(projectsRoot, projectDirName) : projectDirName;
|
|
21843
|
-
const projectPath =
|
|
24314
|
+
const projectPath = join17(projectsRoot, resolvedProjectDirName);
|
|
21844
24315
|
let createdProjectPath = false;
|
|
21845
24316
|
try {
|
|
21846
24317
|
if (template !== "hello_linzumi_demo" && projectPathExists(projectPath)) {
|
|
@@ -21852,7 +24323,7 @@ function createRunnerProject(control, options, allowedCwds) {
|
|
|
21852
24323
|
error: "project_directory_exists"
|
|
21853
24324
|
};
|
|
21854
24325
|
}
|
|
21855
|
-
|
|
24326
|
+
mkdirSync10(projectsRoot, { recursive: true });
|
|
21856
24327
|
if (template === "hello_linzumi_demo") {
|
|
21857
24328
|
createdProjectPath = true;
|
|
21858
24329
|
createHelloLinzumiProject({
|
|
@@ -21860,10 +24331,10 @@ function createRunnerProject(control, options, allowedCwds) {
|
|
|
21860
24331
|
name: resolvedProjectDirName
|
|
21861
24332
|
});
|
|
21862
24333
|
} else {
|
|
21863
|
-
|
|
24334
|
+
mkdirSync10(projectPath, { recursive: false });
|
|
21864
24335
|
createdProjectPath = true;
|
|
21865
24336
|
}
|
|
21866
|
-
const git =
|
|
24337
|
+
const git = spawnSync5("git", ["init"], {
|
|
21867
24338
|
cwd: projectPath,
|
|
21868
24339
|
encoding: "utf8",
|
|
21869
24340
|
env: process.env
|
|
@@ -21971,7 +24442,7 @@ async function suggestRunnerTasks(control, options, allowedCwds) {
|
|
|
21971
24442
|
};
|
|
21972
24443
|
}
|
|
21973
24444
|
}
|
|
21974
|
-
var THREAD_RUNNER_READY_TIMEOUT_MS, claudeSessionStoreSequenceHighWater, ClaudeCodeOutputPostError;
|
|
24445
|
+
var THREAD_RUNNER_READY_TIMEOUT_MS, onboardingDiscoveryAgentStartKeys, onboardingConversationImportKeys, onboardingConversationDiscoveryReportLimit, claudeSessionStoreSequenceHighWater, ClaudeCodeOutputPostError, onboardingConversationTitleFirstMessages, onboardingConversationTitleLastMessages, onboardingConversationTitleBodyMaxLength, onboardingConversationImportMessageMaxLength, onboardingConversationImportCandidateLimit, onboardingConversationImportProgressInterval;
|
|
21975
24446
|
var init_runner = __esm({
|
|
21976
24447
|
"src/runner.ts"() {
|
|
21977
24448
|
"use strict";
|
|
@@ -22002,13 +24473,20 @@ var init_runner = __esm({
|
|
|
22002
24473
|
init_runnerConsoleReporter();
|
|
22003
24474
|
init_streamDeltaQueue();
|
|
22004
24475
|
init_version();
|
|
24476
|
+
init_telemetry();
|
|
22005
24477
|
init_mcpConfig();
|
|
24478
|
+
init_linzumiApiClient();
|
|
24479
|
+
init_onboardingConversationDiscovery();
|
|
24480
|
+
init_onboardingProjectDiscovery();
|
|
22006
24481
|
init_authCache();
|
|
22007
24482
|
init_threadCodexWorkerIpc();
|
|
22008
24483
|
init_signupTaskSuggestions();
|
|
22009
24484
|
init_userFacingErrors();
|
|
22010
24485
|
init_remoteCodexSandboxRunner();
|
|
22011
24486
|
THREAD_RUNNER_READY_TIMEOUT_MS = 3e4;
|
|
24487
|
+
onboardingDiscoveryAgentStartKeys = /* @__PURE__ */ new Set();
|
|
24488
|
+
onboardingConversationImportKeys = /* @__PURE__ */ new Set();
|
|
24489
|
+
onboardingConversationDiscoveryReportLimit = 100;
|
|
22012
24490
|
claudeSessionStoreSequenceHighWater = /* @__PURE__ */ new Map();
|
|
22013
24491
|
ClaudeCodeOutputPostError = class extends Error {
|
|
22014
24492
|
constructor(cause) {
|
|
@@ -22017,11 +24495,17 @@ var init_runner = __esm({
|
|
|
22017
24495
|
this.name = "ClaudeCodeOutputPostError";
|
|
22018
24496
|
}
|
|
22019
24497
|
};
|
|
24498
|
+
onboardingConversationTitleFirstMessages = 4;
|
|
24499
|
+
onboardingConversationTitleLastMessages = 4;
|
|
24500
|
+
onboardingConversationTitleBodyMaxLength = 1200;
|
|
24501
|
+
onboardingConversationImportMessageMaxLength = 15500;
|
|
24502
|
+
onboardingConversationImportCandidateLimit = 500;
|
|
24503
|
+
onboardingConversationImportProgressInterval = 25;
|
|
22020
24504
|
}
|
|
22021
24505
|
});
|
|
22022
24506
|
|
|
22023
24507
|
// src/kandanTls.ts
|
|
22024
|
-
import { existsSync as
|
|
24508
|
+
import { existsSync as existsSync12, readFileSync as readFileSync14 } from "node:fs";
|
|
22025
24509
|
import { Agent } from "undici";
|
|
22026
24510
|
import WsWebSocket from "ws";
|
|
22027
24511
|
function kandanTlsTrustFromEnv() {
|
|
@@ -22032,10 +24516,10 @@ function kandanTlsTrustFromCaFile(caFile) {
|
|
|
22032
24516
|
return void 0;
|
|
22033
24517
|
}
|
|
22034
24518
|
const trimmed = caFile.trim();
|
|
22035
|
-
if (!
|
|
24519
|
+
if (!existsSync12(trimmed)) {
|
|
22036
24520
|
throw new Error(`KANDAN_TLS_CA_FILE does not exist: ${trimmed}`);
|
|
22037
24521
|
}
|
|
22038
|
-
const ca =
|
|
24522
|
+
const ca = readFileSync14(trimmed, "utf8");
|
|
22039
24523
|
return {
|
|
22040
24524
|
caFile: trimmed,
|
|
22041
24525
|
ca,
|
|
@@ -40818,11 +43302,11 @@ var init_RemoveFileError = __esm({
|
|
|
40818
43302
|
});
|
|
40819
43303
|
|
|
40820
43304
|
// ../../node_modules/@inquirer/external-editor/dist/esm/index.js
|
|
40821
|
-
import { spawn as spawn10, spawnSync as
|
|
40822
|
-
import { readFileSync as
|
|
43305
|
+
import { spawn as spawn10, spawnSync as spawnSync6 } from "child_process";
|
|
43306
|
+
import { readFileSync as readFileSync17, unlinkSync as unlinkSync3, writeFileSync as writeFileSync11 } from "fs";
|
|
40823
43307
|
import path from "node:path";
|
|
40824
43308
|
import os from "node:os";
|
|
40825
|
-
import { randomUUID as
|
|
43309
|
+
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
40826
43310
|
function editAsync(text2 = "", callback, fileOptions) {
|
|
40827
43311
|
const editor = new ExternalEditor(text2, fileOptions);
|
|
40828
43312
|
editor.runAsync((err, result) => {
|
|
@@ -40922,7 +43406,7 @@ var init_esm5 = __esm({
|
|
|
40922
43406
|
createTemporaryFile() {
|
|
40923
43407
|
try {
|
|
40924
43408
|
const baseDir = this.fileOptions.dir ?? os.tmpdir();
|
|
40925
|
-
const id =
|
|
43409
|
+
const id = randomUUID5();
|
|
40926
43410
|
const prefix = sanitizeAffix(this.fileOptions.prefix);
|
|
40927
43411
|
const postfix = sanitizeAffix(this.fileOptions.postfix);
|
|
40928
43412
|
const filename = `${prefix}${id}${postfix}`;
|
|
@@ -40936,14 +43420,14 @@ var init_esm5 = __esm({
|
|
|
40936
43420
|
if (Object.prototype.hasOwnProperty.call(this.fileOptions, "mode")) {
|
|
40937
43421
|
opt.mode = this.fileOptions.mode;
|
|
40938
43422
|
}
|
|
40939
|
-
|
|
43423
|
+
writeFileSync11(this.tempFile, this.text, opt);
|
|
40940
43424
|
} catch (createFileError) {
|
|
40941
43425
|
throw new CreateFileError(createFileError);
|
|
40942
43426
|
}
|
|
40943
43427
|
}
|
|
40944
43428
|
readTemporaryFile() {
|
|
40945
43429
|
try {
|
|
40946
|
-
const tempFileBuffer =
|
|
43430
|
+
const tempFileBuffer = readFileSync17(this.tempFile);
|
|
40947
43431
|
if (tempFileBuffer.length === 0) {
|
|
40948
43432
|
this.text = "";
|
|
40949
43433
|
} else {
|
|
@@ -40966,7 +43450,7 @@ var init_esm5 = __esm({
|
|
|
40966
43450
|
}
|
|
40967
43451
|
launchEditor() {
|
|
40968
43452
|
try {
|
|
40969
|
-
const editorProcess =
|
|
43453
|
+
const editorProcess = spawnSync6(this.editor.bin, this.editor.args.concat([this.tempFile]), { stdio: "inherit" });
|
|
40970
43454
|
this.lastExitStatus = editorProcess.status ?? 0;
|
|
40971
43455
|
} catch (launchError) {
|
|
40972
43456
|
throw new LaunchEditorError(launchError);
|
|
@@ -42308,21 +44792,21 @@ __export(signupFlow_exports, {
|
|
|
42308
44792
|
signupTaskSuggestionWaitMessageForTest: () => signupTaskSuggestionWaitMessageForTest,
|
|
42309
44793
|
toggleProjectPickerSelectionForTest: () => toggleProjectPickerSelectionForTest
|
|
42310
44794
|
});
|
|
42311
|
-
import { spawn as spawn11, spawnSync as
|
|
44795
|
+
import { spawn as spawn11, spawnSync as spawnSync7 } from "node:child_process";
|
|
42312
44796
|
import {
|
|
42313
|
-
existsSync as
|
|
44797
|
+
existsSync as existsSync15,
|
|
42314
44798
|
constants as fsConstants,
|
|
42315
|
-
mkdirSync as
|
|
44799
|
+
mkdirSync as mkdirSync13,
|
|
42316
44800
|
mkdtempSync as mkdtempSync5,
|
|
42317
|
-
readFileSync as
|
|
42318
|
-
readdirSync as
|
|
44801
|
+
readFileSync as readFileSync18,
|
|
44802
|
+
readdirSync as readdirSync5,
|
|
42319
44803
|
rmSync as rmSync5,
|
|
42320
|
-
statSync as
|
|
42321
|
-
writeFileSync as
|
|
44804
|
+
statSync as statSync4,
|
|
44805
|
+
writeFileSync as writeFileSync12
|
|
42322
44806
|
} from "node:fs";
|
|
42323
44807
|
import { access } from "node:fs/promises";
|
|
42324
|
-
import { homedir as
|
|
42325
|
-
import { delimiter as delimiter3, dirname as
|
|
44808
|
+
import { homedir as homedir14, tmpdir as tmpdir4 } from "node:os";
|
|
44809
|
+
import { delimiter as delimiter3, dirname as dirname15, join as join20, resolve as resolve9 } from "node:path";
|
|
42326
44810
|
import { stdin as defaultStdin, stdout as defaultStdout } from "node:process";
|
|
42327
44811
|
import { emitKeypressEvents } from "node:readline";
|
|
42328
44812
|
function signupHelpText() {
|
|
@@ -42334,6 +44818,7 @@ function signupHelpText() {
|
|
|
42334
44818
|
"",
|
|
42335
44819
|
"Options:",
|
|
42336
44820
|
" --api-url <url> Linzumi HTTP origin, default https://serve.linzumi.com",
|
|
44821
|
+
" --code-server-bin <path> Custom development code-server executable",
|
|
42337
44822
|
" --force-signup Ignore existing local signup auth and create a fresh login",
|
|
42338
44823
|
" --no-open Print the launch URL without opening the browser",
|
|
42339
44824
|
" --debug-signup Print the collected launch payload JSON after signup",
|
|
@@ -42352,6 +44837,7 @@ function signupPromptCancelMessage(error) {
|
|
|
42352
44837
|
function parseSignupTuiOptions(args) {
|
|
42353
44838
|
let serviceUrl = defaultLinzumiHttpUrl;
|
|
42354
44839
|
let forceSignup = false;
|
|
44840
|
+
let codeServerBin;
|
|
42355
44841
|
let openBrowser2 = true;
|
|
42356
44842
|
let debugLaunchPayload = false;
|
|
42357
44843
|
for (let index = 0; index < args.length; index += 1) {
|
|
@@ -42377,11 +44863,26 @@ function parseSignupTuiOptions(args) {
|
|
|
42377
44863
|
index += 1;
|
|
42378
44864
|
break;
|
|
42379
44865
|
}
|
|
44866
|
+
case "--code-server-bin": {
|
|
44867
|
+
const value = args[index + 1];
|
|
44868
|
+
if (value === void 0 || value.startsWith("--")) {
|
|
44869
|
+
throw new Error("missing value for --code-server-bin");
|
|
44870
|
+
}
|
|
44871
|
+
codeServerBin = value;
|
|
44872
|
+
index += 1;
|
|
44873
|
+
break;
|
|
44874
|
+
}
|
|
42380
44875
|
default:
|
|
42381
44876
|
throw new Error(`invalid signup flag: ${arg ?? ""}`);
|
|
42382
44877
|
}
|
|
42383
44878
|
}
|
|
42384
|
-
return {
|
|
44879
|
+
return {
|
|
44880
|
+
serviceUrl,
|
|
44881
|
+
forceSignup,
|
|
44882
|
+
...codeServerBin === void 0 ? {} : { codeServerBin },
|
|
44883
|
+
openBrowser: openBrowser2,
|
|
44884
|
+
debugLaunchPayload
|
|
44885
|
+
};
|
|
42385
44886
|
}
|
|
42386
44887
|
function signupAuthMatchesServiceUrl(auth, serviceUrl) {
|
|
42387
44888
|
return comparableServiceUrl(auth.serviceUrl) === comparableServiceUrl(serviceUrl);
|
|
@@ -42421,12 +44922,12 @@ function defaultSignupDraftStore(serviceUrl) {
|
|
|
42421
44922
|
const path2 = defaultSignupDraftPath(serviceUrl);
|
|
42422
44923
|
return {
|
|
42423
44924
|
read: () => {
|
|
42424
|
-
if (!
|
|
44925
|
+
if (!existsSync15(path2)) {
|
|
42425
44926
|
return void 0;
|
|
42426
44927
|
}
|
|
42427
44928
|
let parsed;
|
|
42428
44929
|
try {
|
|
42429
|
-
parsed = JSON.parse(
|
|
44930
|
+
parsed = JSON.parse(readFileSync18(path2, "utf8"));
|
|
42430
44931
|
} catch (_error) {
|
|
42431
44932
|
return void 0;
|
|
42432
44933
|
}
|
|
@@ -42436,8 +44937,8 @@ function defaultSignupDraftStore(serviceUrl) {
|
|
|
42436
44937
|
return comparableServiceUrl(parsed.serviceUrl) === comparableServiceUrl(serviceUrl) ? parsed : void 0;
|
|
42437
44938
|
},
|
|
42438
44939
|
write: (draft) => {
|
|
42439
|
-
|
|
42440
|
-
|
|
44940
|
+
mkdirSync13(dirname15(path2), { recursive: true });
|
|
44941
|
+
writeFileSync12(path2, `${JSON.stringify(draft, null, 2)}
|
|
42441
44942
|
`, {
|
|
42442
44943
|
encoding: "utf8",
|
|
42443
44944
|
mode: 384
|
|
@@ -42449,8 +44950,8 @@ function defaultSignupDraftStore(serviceUrl) {
|
|
|
42449
44950
|
};
|
|
42450
44951
|
}
|
|
42451
44952
|
function defaultSignupDraftPath(serviceUrl) {
|
|
42452
|
-
return
|
|
42453
|
-
|
|
44953
|
+
return join20(
|
|
44954
|
+
dirname15(localConfigPath()),
|
|
42454
44955
|
`signup-draft.${localConfigScopeFileStem(serviceUrl)}.json`
|
|
42455
44956
|
);
|
|
42456
44957
|
}
|
|
@@ -42484,8 +44985,8 @@ function defaultSignupTaskCacheStore(serviceUrl) {
|
|
|
42484
44985
|
}
|
|
42485
44986
|
}
|
|
42486
44987
|
};
|
|
42487
|
-
|
|
42488
|
-
|
|
44988
|
+
mkdirSync13(dirname15(path2), { recursive: true });
|
|
44989
|
+
writeFileSync12(path2, `${JSON.stringify(next, null, 2)}
|
|
42489
44990
|
`, {
|
|
42490
44991
|
encoding: "utf8",
|
|
42491
44992
|
mode: 384
|
|
@@ -42494,18 +44995,18 @@ function defaultSignupTaskCacheStore(serviceUrl) {
|
|
|
42494
44995
|
};
|
|
42495
44996
|
}
|
|
42496
44997
|
function defaultSignupTaskCachePath(serviceUrl) {
|
|
42497
|
-
return
|
|
42498
|
-
|
|
44998
|
+
return join20(
|
|
44999
|
+
dirname15(localConfigPath()),
|
|
42499
45000
|
`signup-task-cache.${localConfigScopeFileStem(serviceUrl)}.json`
|
|
42500
45001
|
);
|
|
42501
45002
|
}
|
|
42502
45003
|
function readSignupTaskCache(path2) {
|
|
42503
|
-
if (!
|
|
45004
|
+
if (!existsSync15(path2)) {
|
|
42504
45005
|
return { version: 1, entries: {} };
|
|
42505
45006
|
}
|
|
42506
45007
|
let parsed;
|
|
42507
45008
|
try {
|
|
42508
|
-
parsed = JSON.parse(
|
|
45009
|
+
parsed = JSON.parse(readFileSync18(path2, "utf8"));
|
|
42509
45010
|
} catch (_error) {
|
|
42510
45011
|
return { version: 1, entries: {} };
|
|
42511
45012
|
}
|
|
@@ -42637,6 +45138,7 @@ async function runSignupFlow(deps = {}) {
|
|
|
42637
45138
|
const preflight = deps.preflight ?? defaultPreflightRuntime();
|
|
42638
45139
|
const forceSignup = deps.forceSignup === true;
|
|
42639
45140
|
const serviceUrl = deps.serviceUrl ?? defaultLinzumiHttpUrl;
|
|
45141
|
+
const codeServerBin = deps.codeServerBin;
|
|
42640
45142
|
const openBrowser2 = deps.openBrowser !== false;
|
|
42641
45143
|
const debugLaunchPayload = deps.debugLaunchPayload === true;
|
|
42642
45144
|
const openUrl = deps.openUrl ?? openUrlInBrowser;
|
|
@@ -42925,7 +45427,8 @@ async function runSignupFlow(deps = {}) {
|
|
|
42925
45427
|
workspaceSlug: missionControl.workspace.slug,
|
|
42926
45428
|
projectPath: starterProjectPath,
|
|
42927
45429
|
selectedProjectPaths: missionControl.config.allowedCwds,
|
|
42928
|
-
codexBin: verifiedCodexBin
|
|
45430
|
+
codexBin: verifiedCodexBin,
|
|
45431
|
+
...codeServerBin === void 0 ? {} : { codeServerBin }
|
|
42929
45432
|
});
|
|
42930
45433
|
const starterTaskLaunches = await startSignupTasksForCommander(
|
|
42931
45434
|
signupServerClient,
|
|
@@ -43659,6 +46162,7 @@ async function signupCommanderRunnerOptions(args, runnerId) {
|
|
|
43659
46162
|
const editorRuntime = await resolveEditorRuntime({
|
|
43660
46163
|
kandanUrl: args.serviceUrl,
|
|
43661
46164
|
token: args.localRunnerAccessToken,
|
|
46165
|
+
customCodeServerBin: args.codeServerBin,
|
|
43662
46166
|
fetchImpl: trustedFetch(trust)
|
|
43663
46167
|
});
|
|
43664
46168
|
const dependencyStatus = await buildRunnerDependencyStatus({
|
|
@@ -43688,6 +46192,7 @@ async function signupCommanderRunnerOptions(args, runnerId) {
|
|
|
43688
46192
|
editorRuntime: editorRuntime.runtime,
|
|
43689
46193
|
socketFactory: trustedWebSocketFactory(trust),
|
|
43690
46194
|
dependencyStatus,
|
|
46195
|
+
onboardingDiscovery: "start",
|
|
43691
46196
|
runtimeDefaults: {
|
|
43692
46197
|
model: void 0,
|
|
43693
46198
|
reasoningEffort: void 0,
|
|
@@ -43713,6 +46218,7 @@ function signupConnectRestartCommand(args) {
|
|
|
43713
46218
|
"--workspace",
|
|
43714
46219
|
args.workspaceSlug,
|
|
43715
46220
|
...args.codexBin === void 0 ? [] : ["--codex-bin", args.codexBin],
|
|
46221
|
+
...args.codeServerBin === void 0 ? [] : ["--code-server-bin", args.codeServerBin],
|
|
43716
46222
|
"--cwd",
|
|
43717
46223
|
args.projectPath,
|
|
43718
46224
|
...args.serviceUrl === defaultLinzumiHttpUrl ? [] : ["--api-url", args.serviceUrl]
|
|
@@ -44385,7 +46891,7 @@ function autocompletePathSuggestions(pathInput) {
|
|
|
44385
46891
|
try {
|
|
44386
46892
|
const showHidden = prefix.startsWith(".");
|
|
44387
46893
|
const currentPath = directoryExists(normalizedInput) ? [normalizedInput.replace(/\/$/, "") || "/"] : [];
|
|
44388
|
-
const childPaths =
|
|
46894
|
+
const childPaths = readdirSync5(directoryPath, { withFileTypes: true }).filter((entry) => entry.isDirectory()).filter((entry) => showHidden || !entry.name.startsWith(".")).map((entry) => join20(directoryPath, entry.name)).map((path2) => ({
|
|
44389
46895
|
path: path2,
|
|
44390
46896
|
score: fuzzyPathSegmentScore(path2.split("/").at(-1) ?? path2, prefix)
|
|
44391
46897
|
})).filter((candidate) => candidate.score !== void 0).sort((left, right) => {
|
|
@@ -44631,7 +47137,7 @@ async function runSignupPreflights(runtime) {
|
|
|
44631
47137
|
function defaultPreflightRuntime() {
|
|
44632
47138
|
return {
|
|
44633
47139
|
cwd: process.cwd(),
|
|
44634
|
-
homeDir:
|
|
47140
|
+
homeDir: homedir14(),
|
|
44635
47141
|
probeTool,
|
|
44636
47142
|
readGitEmail,
|
|
44637
47143
|
discoverCodeRoots,
|
|
@@ -44776,13 +47282,13 @@ async function suggestTasksWithCodex(projectPath, retryPolicy) {
|
|
|
44776
47282
|
);
|
|
44777
47283
|
}
|
|
44778
47284
|
async function runCodexTaskSuggestion2(projectPath, previousResponse, retryPolicy) {
|
|
44779
|
-
const tempRoot = mkdtempSync5(
|
|
44780
|
-
const schemaPath =
|
|
44781
|
-
const outputPath =
|
|
47285
|
+
const tempRoot = mkdtempSync5(join20(tmpdir4(), "linzumi-signup-codex-tasks-"));
|
|
47286
|
+
const schemaPath = join20(tempRoot, "task-suggestions.schema.json");
|
|
47287
|
+
const outputPath = join20(tempRoot, "task-suggestions.json");
|
|
44782
47288
|
const model = process.env.LINZUMI_SIGNUP_TASK_MODEL ?? "gpt-5.4-mini";
|
|
44783
47289
|
const prompt = taskSuggestionPrompt2(previousResponse);
|
|
44784
47290
|
const codexCommand = await resolveSignupCodexCommand();
|
|
44785
|
-
|
|
47291
|
+
writeFileSync12(
|
|
44786
47292
|
schemaPath,
|
|
44787
47293
|
`${JSON.stringify(taskSuggestionJsonSchema2(), null, 2)}
|
|
44788
47294
|
`,
|
|
@@ -44803,7 +47309,7 @@ async function runCodexTaskSuggestion2(projectPath, previousResponse, retryPolic
|
|
|
44803
47309
|
),
|
|
44804
47310
|
retryPolicy
|
|
44805
47311
|
);
|
|
44806
|
-
return
|
|
47312
|
+
return readFileSync18(outputPath, "utf8");
|
|
44807
47313
|
} finally {
|
|
44808
47314
|
rmSync5(tempRoot, { recursive: true, force: true });
|
|
44809
47315
|
}
|
|
@@ -44840,7 +47346,7 @@ function codexTaskSuggestionProcess2(args) {
|
|
|
44840
47346
|
function signupCodexTaskSuggestionProcessForTest(args) {
|
|
44841
47347
|
return codexTaskSuggestionProcess2(args);
|
|
44842
47348
|
}
|
|
44843
|
-
async function resolveSignupCodexCommand(env = process.env, homeDir =
|
|
47349
|
+
async function resolveSignupCodexCommand(env = process.env, homeDir = homedir14(), executableExists = fileIsExecutable) {
|
|
44844
47350
|
const override = firstConfiguredValue([
|
|
44845
47351
|
env.LINZUMI_SIGNUP_CODEX_BIN,
|
|
44846
47352
|
env.LINZUMI_CODEX_BIN
|
|
@@ -44895,7 +47401,7 @@ function resolveHomePath(path2, homeDir) {
|
|
|
44895
47401
|
return homeDir;
|
|
44896
47402
|
}
|
|
44897
47403
|
if (path2.startsWith("~/")) {
|
|
44898
|
-
return
|
|
47404
|
+
return join20(homeDir, path2.slice(2));
|
|
44899
47405
|
}
|
|
44900
47406
|
return resolve9(path2);
|
|
44901
47407
|
}
|
|
@@ -44908,9 +47414,9 @@ function commandLooksPathLike(command) {
|
|
|
44908
47414
|
}
|
|
44909
47415
|
function homeManagedCodexCandidates(homeDir) {
|
|
44910
47416
|
return [
|
|
44911
|
-
|
|
44912
|
-
|
|
44913
|
-
|
|
47417
|
+
join20(homeDir, ".volta", "bin", "codex"),
|
|
47418
|
+
join20(homeDir, ".local", "bin", "codex"),
|
|
47419
|
+
join20(homeDir, "bin", "codex")
|
|
44914
47420
|
];
|
|
44915
47421
|
}
|
|
44916
47422
|
async function firstExecutablePath(paths, executableExists) {
|
|
@@ -44925,7 +47431,7 @@ function commandPathCandidates(path2) {
|
|
|
44925
47431
|
if (path2 === void 0 || path2.trim() === "") {
|
|
44926
47432
|
return [];
|
|
44927
47433
|
}
|
|
44928
|
-
return path2.split(delimiter3).filter((entry) => entry.trim() !== "").map((entry) =>
|
|
47434
|
+
return path2.split(delimiter3).filter((entry) => entry.trim() !== "").map((entry) => join20(entry, "codex"));
|
|
44929
47435
|
}
|
|
44930
47436
|
async function fileIsExecutable(path2) {
|
|
44931
47437
|
try {
|
|
@@ -44950,6 +47456,14 @@ function taskSuggestionPrompt2(previousResponse) {
|
|
|
44950
47456
|
"Task:",
|
|
44951
47457
|
"Inspect this repository read-only and suggest exactly 5 useful quick starter tasks for Codex agents.",
|
|
44952
47458
|
"",
|
|
47459
|
+
"GitHub context:",
|
|
47460
|
+
"- First check whether the GitHub CLI is available and authenticated with a cheap command such as `gh auth status`.",
|
|
47461
|
+
"- If `gh` is missing, unauthenticated, or this repository has no GitHub remote, skip GitHub context and rely on local files.",
|
|
47462
|
+
"- When `gh` is authenticated, use bounded reads such as `gh pr list --limit 30 --state all` and `gh issue list --limit 30 --state all` to inspect recent open and closed pull requests and issues.",
|
|
47463
|
+
"- Be mindful of GitHub rate limits, CPU usage, and network usage; prefer one or two small calls and do not paginate or crawl exhaustively.",
|
|
47464
|
+
"- If a GitHub call returns unauthorized, forbidden, or not found for this repository, stop GitHub lookups for this repository.",
|
|
47465
|
+
"- Use pull request and issue titles, states, labels, and recent activity to infer what the repo is about, what the user has been working on, and likely next tasks.",
|
|
47466
|
+
"",
|
|
44953
47467
|
"Constraints:",
|
|
44954
47468
|
"- Prefer tasks that are small, concrete, and likely to produce useful first results.",
|
|
44955
47469
|
"- Do not modify files.",
|
|
@@ -45150,11 +47664,11 @@ function codexPreflightLocation(command, homeDir) {
|
|
|
45150
47664
|
switch (command) {
|
|
45151
47665
|
case "codex":
|
|
45152
47666
|
return "on PATH";
|
|
45153
|
-
case
|
|
47667
|
+
case join20(homeDir, ".volta", "bin", "codex"):
|
|
45154
47668
|
return "via Volta";
|
|
45155
|
-
case
|
|
47669
|
+
case join20(homeDir, ".local", "bin", "codex"):
|
|
45156
47670
|
return "via ~/.local/bin";
|
|
45157
|
-
case
|
|
47671
|
+
case join20(homeDir, "bin", "codex"):
|
|
45158
47672
|
return "via ~/bin";
|
|
45159
47673
|
default:
|
|
45160
47674
|
return "from configured path";
|
|
@@ -45259,7 +47773,7 @@ function spawnSyncGit(args, cwd) {
|
|
|
45259
47773
|
}
|
|
45260
47774
|
function spawnSyncGitOutput(args, cwd) {
|
|
45261
47775
|
try {
|
|
45262
|
-
const result =
|
|
47776
|
+
const result = spawnSync7("git", [...args], {
|
|
45263
47777
|
cwd,
|
|
45264
47778
|
stdio: ["ignore", "pipe", "ignore"]
|
|
45265
47779
|
});
|
|
@@ -45309,13 +47823,13 @@ function probeToolWithArgs(command, args, cwd) {
|
|
|
45309
47823
|
}
|
|
45310
47824
|
async function discoverCodeRoots(homeDir) {
|
|
45311
47825
|
const candidates = ["src", "code", "projects"].map(
|
|
45312
|
-
(name) =>
|
|
47826
|
+
(name) => join20(homeDir, name)
|
|
45313
47827
|
);
|
|
45314
|
-
return candidates.filter((path2) =>
|
|
47828
|
+
return candidates.filter((path2) => existsSync15(path2)).flatMap((path2) => discoveredProjectNames(path2));
|
|
45315
47829
|
}
|
|
45316
47830
|
function discoveredProjectNames(root) {
|
|
45317
47831
|
try {
|
|
45318
|
-
return
|
|
47832
|
+
return readdirSync5(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).slice(0, 3).map((entry) => `~/${root.split("/").at(-1)}/${entry.name}`);
|
|
45319
47833
|
} catch {
|
|
45320
47834
|
return [];
|
|
45321
47835
|
}
|
|
@@ -45363,7 +47877,7 @@ function discoverProjectsFromCurrentDirectory(cwd) {
|
|
|
45363
47877
|
}
|
|
45364
47878
|
function discoverProjectsFromGuessedRoots(homeDir) {
|
|
45365
47879
|
const guessedRoots = ["src", "code", "projects"].map(
|
|
45366
|
-
(name) =>
|
|
47880
|
+
(name) => join20(homeDir, name)
|
|
45367
47881
|
);
|
|
45368
47882
|
const projects = guessedRoots.flatMap(
|
|
45369
47883
|
(root) => discoverProjectsUnderRoot(root)
|
|
@@ -45415,25 +47929,25 @@ function looksLikeProject(path2) {
|
|
|
45415
47929
|
"pnpm-lock.yaml",
|
|
45416
47930
|
"yarn.lock",
|
|
45417
47931
|
"package-lock.json"
|
|
45418
|
-
].some((name) =>
|
|
47932
|
+
].some((name) => existsSync15(join20(path2, name)));
|
|
45419
47933
|
}
|
|
45420
47934
|
function detectProjectLanguage(path2) {
|
|
45421
|
-
if (
|
|
47935
|
+
if (existsSync15(join20(path2, "pyproject.toml")) || existsSync15(join20(path2, "requirements.txt"))) {
|
|
45422
47936
|
return "Python";
|
|
45423
47937
|
}
|
|
45424
|
-
if (
|
|
47938
|
+
if (existsSync15(join20(path2, "Cargo.toml"))) {
|
|
45425
47939
|
return "Rust";
|
|
45426
47940
|
}
|
|
45427
|
-
if (
|
|
47941
|
+
if (existsSync15(join20(path2, "go.mod"))) {
|
|
45428
47942
|
return "Go";
|
|
45429
47943
|
}
|
|
45430
|
-
if (
|
|
47944
|
+
if (existsSync15(join20(path2, "mix.exs"))) {
|
|
45431
47945
|
return "Elixir";
|
|
45432
47946
|
}
|
|
45433
|
-
if (
|
|
47947
|
+
if (existsSync15(join20(path2, "tsconfig.json")) || packageJsonMentionsTypeScript(path2)) {
|
|
45434
47948
|
return "TypeScript";
|
|
45435
47949
|
}
|
|
45436
|
-
if (
|
|
47950
|
+
if (existsSync15(join20(path2, "package.json"))) {
|
|
45437
47951
|
return "JavaScript";
|
|
45438
47952
|
}
|
|
45439
47953
|
return "Project";
|
|
@@ -45441,7 +47955,7 @@ function detectProjectLanguage(path2) {
|
|
|
45441
47955
|
function packageJsonMentionsTypeScript(path2) {
|
|
45442
47956
|
try {
|
|
45443
47957
|
const packageJson2 = JSON.parse(
|
|
45444
|
-
|
|
47958
|
+
readFileSync18(join20(path2, "package.json"), "utf8")
|
|
45445
47959
|
);
|
|
45446
47960
|
return packageJson2.dependencies?.typescript !== void 0 || packageJson2.devDependencies?.typescript !== void 0;
|
|
45447
47961
|
} catch {
|
|
@@ -45449,11 +47963,11 @@ function packageJsonMentionsTypeScript(path2) {
|
|
|
45449
47963
|
}
|
|
45450
47964
|
}
|
|
45451
47965
|
function hasGitMetadata(path2) {
|
|
45452
|
-
return
|
|
47966
|
+
return existsSync15(join20(path2, ".git"));
|
|
45453
47967
|
}
|
|
45454
47968
|
function childDirectories(root) {
|
|
45455
47969
|
try {
|
|
45456
|
-
return
|
|
47970
|
+
return readdirSync5(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join20(root, entry.name));
|
|
45457
47971
|
} catch {
|
|
45458
47972
|
return [];
|
|
45459
47973
|
}
|
|
@@ -45472,17 +47986,17 @@ function ignoredProjectDirectory(path2) {
|
|
|
45472
47986
|
}
|
|
45473
47987
|
function directoryExists(path2) {
|
|
45474
47988
|
try {
|
|
45475
|
-
return
|
|
47989
|
+
return statSync4(path2).isDirectory();
|
|
45476
47990
|
} catch {
|
|
45477
47991
|
return false;
|
|
45478
47992
|
}
|
|
45479
47993
|
}
|
|
45480
47994
|
function expandHomePath(path2) {
|
|
45481
47995
|
if (path2 === "~") {
|
|
45482
|
-
return
|
|
47996
|
+
return homedir14();
|
|
45483
47997
|
}
|
|
45484
47998
|
if (path2.startsWith("~/")) {
|
|
45485
|
-
return
|
|
47999
|
+
return join20(homedir14(), path2.slice(2));
|
|
45486
48000
|
}
|
|
45487
48001
|
return resolve9(path2);
|
|
45488
48002
|
}
|
|
@@ -45571,8 +48085,8 @@ secure mission control for all your agents on your computers
|
|
|
45571
48085
|
init_runner();
|
|
45572
48086
|
init_claudeCodeSession();
|
|
45573
48087
|
init_authCache();
|
|
45574
|
-
import { existsSync as
|
|
45575
|
-
import { homedir as
|
|
48088
|
+
import { existsSync as existsSync16, readFileSync as readFileSync19, realpathSync as realpathSync7 } from "node:fs";
|
|
48089
|
+
import { homedir as homedir15 } from "node:os";
|
|
45576
48090
|
import { resolve as resolve10 } from "node:path";
|
|
45577
48091
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
45578
48092
|
|
|
@@ -45593,7 +48107,8 @@ async function resolveLocalRunnerToken(args, deps = {
|
|
|
45593
48107
|
kandanUrl: args.kandanUrl,
|
|
45594
48108
|
accessToken: cached.accessToken,
|
|
45595
48109
|
workspaceSlug: args.workspaceSlug,
|
|
45596
|
-
channelSlug: args.channelSlug
|
|
48110
|
+
channelSlug: args.channelSlug,
|
|
48111
|
+
requiredScopes: args.requiredScopes
|
|
45597
48112
|
});
|
|
45598
48113
|
if (cachedTokenIsUsable) {
|
|
45599
48114
|
return cached.accessToken;
|
|
@@ -45633,9 +48148,9 @@ init_kandanTls();
|
|
|
45633
48148
|
init_protocol();
|
|
45634
48149
|
init_json();
|
|
45635
48150
|
init_defaultUrls();
|
|
45636
|
-
import { existsSync as
|
|
45637
|
-
import { dirname as
|
|
45638
|
-
import { homedir as
|
|
48151
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync11, readFileSync as readFileSync15, writeFileSync as writeFileSync9 } from "node:fs";
|
|
48152
|
+
import { dirname as dirname13, join as join18 } from "node:path";
|
|
48153
|
+
import { homedir as homedir12 } from "node:os";
|
|
45639
48154
|
async function runAgentCliCommand(args, deps = {
|
|
45640
48155
|
fetchImpl: fetch,
|
|
45641
48156
|
stdout: process.stdout,
|
|
@@ -46343,7 +48858,7 @@ function agentTokenFile(flags) {
|
|
|
46343
48858
|
return flags.get("agent-token-file") ?? defaultAgentTokenFilePath();
|
|
46344
48859
|
}
|
|
46345
48860
|
function defaultAgentTokenFilePath() {
|
|
46346
|
-
return
|
|
48861
|
+
return join18(homedir12(), ".linzumi", "agent-token.json");
|
|
46347
48862
|
}
|
|
46348
48863
|
function normalizedApiUrl(apiUrl) {
|
|
46349
48864
|
return apiUrl.endsWith("/") ? apiUrl : `${apiUrl}/`;
|
|
@@ -46352,11 +48867,11 @@ function authorizationHeaders(token) {
|
|
|
46352
48867
|
return { authorization: `Bearer ${token}` };
|
|
46353
48868
|
}
|
|
46354
48869
|
function readOptionalTextFile(path2) {
|
|
46355
|
-
return
|
|
48870
|
+
return existsSync13(path2) ? readFileSync15(path2, "utf8") : void 0;
|
|
46356
48871
|
}
|
|
46357
48872
|
function writeTextFile(path2, content) {
|
|
46358
|
-
|
|
46359
|
-
|
|
48873
|
+
mkdirSync11(dirname13(path2), { recursive: true });
|
|
48874
|
+
writeFileSync9(path2, content);
|
|
46360
48875
|
}
|
|
46361
48876
|
function readStoredAgentTokenFile(path2, readTextFile = readOptionalTextFile) {
|
|
46362
48877
|
const content = readTextFile(path2);
|
|
@@ -46443,27 +48958,27 @@ init_helloLinzumiProject();
|
|
|
46443
48958
|
// src/commanderDaemon.ts
|
|
46444
48959
|
init_runnerLogger();
|
|
46445
48960
|
import {
|
|
46446
|
-
existsSync as
|
|
46447
|
-
closeSync as
|
|
46448
|
-
mkdirSync as
|
|
46449
|
-
openSync as
|
|
46450
|
-
readFileSync as
|
|
48961
|
+
existsSync as existsSync14,
|
|
48962
|
+
closeSync as closeSync3,
|
|
48963
|
+
mkdirSync as mkdirSync12,
|
|
48964
|
+
openSync as openSync4,
|
|
48965
|
+
readFileSync as readFileSync16,
|
|
46451
48966
|
watch,
|
|
46452
|
-
writeFileSync as
|
|
48967
|
+
writeFileSync as writeFileSync10
|
|
46453
48968
|
} from "node:fs";
|
|
46454
|
-
import { homedir as
|
|
46455
|
-
import { dirname as
|
|
48969
|
+
import { homedir as homedir13 } from "node:os";
|
|
48970
|
+
import { dirname as dirname14, join as join19, resolve as resolve8 } from "node:path";
|
|
46456
48971
|
import { execFileSync, spawn as spawn9 } from "node:child_process";
|
|
46457
48972
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
46458
48973
|
var connectedMarkers = ["Connected to Linzumi", "Runner connected:"];
|
|
46459
48974
|
function commanderStatusDir() {
|
|
46460
|
-
return
|
|
48975
|
+
return join19(homedir13(), ".linzumi", "commanders");
|
|
46461
48976
|
}
|
|
46462
48977
|
function commanderStatusFile(runnerId, statusDir = commanderStatusDir()) {
|
|
46463
|
-
return
|
|
48978
|
+
return join19(statusDir, `${safeRunnerId(runnerId)}.json`);
|
|
46464
48979
|
}
|
|
46465
|
-
function defaultCommanderLogFile(runnerId
|
|
46466
|
-
return
|
|
48980
|
+
function defaultCommanderLogFile(runnerId) {
|
|
48981
|
+
return join19(homedir13(), ".linzumi", "logs", `${safeRunnerId(runnerId)}.log`);
|
|
46467
48982
|
}
|
|
46468
48983
|
function commanderLogIsConnected(log) {
|
|
46469
48984
|
return connectedMarkers.some((marker) => log.includes(marker));
|
|
@@ -46472,7 +48987,7 @@ function startCommanderDaemon(options) {
|
|
|
46472
48987
|
const statusDir = options.statusDir ?? commanderStatusDir();
|
|
46473
48988
|
const statusFile = commanderStatusFile(options.runnerId, statusDir);
|
|
46474
48989
|
const logFile = resolve8(
|
|
46475
|
-
options.logFile ?? defaultCommanderLogFile(options.runnerId
|
|
48990
|
+
options.logFile ?? defaultCommanderLogFile(options.runnerId)
|
|
46476
48991
|
);
|
|
46477
48992
|
const entrypoint = options.entrypoint ?? currentEntrypoint();
|
|
46478
48993
|
const nodeBin = options.nodeBin ?? process.execPath;
|
|
@@ -46484,10 +48999,10 @@ function startCommanderDaemon(options) {
|
|
|
46484
48999
|
"--log-file",
|
|
46485
49000
|
logFile
|
|
46486
49001
|
];
|
|
46487
|
-
|
|
46488
|
-
|
|
46489
|
-
const out =
|
|
46490
|
-
const err =
|
|
49002
|
+
mkdirSync12(statusDir, { recursive: true });
|
|
49003
|
+
mkdirSync12(dirname14(logFile), { recursive: true });
|
|
49004
|
+
const out = openSync4(logFile, "a");
|
|
49005
|
+
const err = openSync4(logFile, "a");
|
|
46491
49006
|
writeCliAuditEvent(
|
|
46492
49007
|
"process.spawn",
|
|
46493
49008
|
{
|
|
@@ -46514,8 +49029,8 @@ function startCommanderDaemon(options) {
|
|
|
46514
49029
|
},
|
|
46515
49030
|
{ sessionId: options.runnerId }
|
|
46516
49031
|
);
|
|
46517
|
-
|
|
46518
|
-
|
|
49032
|
+
closeSync3(out);
|
|
49033
|
+
closeSync3(err);
|
|
46519
49034
|
child.unref();
|
|
46520
49035
|
if (child.pid === void 0) {
|
|
46521
49036
|
throw new Error("commander daemon did not report a pid");
|
|
@@ -46531,21 +49046,21 @@ function startCommanderDaemon(options) {
|
|
|
46531
49046
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
46532
49047
|
command: [nodeBin, ...command]
|
|
46533
49048
|
};
|
|
46534
|
-
|
|
49049
|
+
writeFileSync10(statusFile, `${JSON.stringify(record, null, 2)}
|
|
46535
49050
|
`);
|
|
46536
49051
|
return record;
|
|
46537
49052
|
}
|
|
46538
49053
|
function commanderDaemonStatus(runnerId, statusDir = commanderStatusDir(), processIdentityReader = readProcessIdentity) {
|
|
46539
49054
|
const statusFile = commanderStatusFile(runnerId, statusDir);
|
|
46540
|
-
if (!
|
|
49055
|
+
if (!existsSync14(statusFile)) {
|
|
46541
49056
|
return { status: "missing", runnerId, statusFile };
|
|
46542
49057
|
}
|
|
46543
|
-
const record = parseRecord(
|
|
49058
|
+
const record = parseRecord(readFileSync16(statusFile, "utf8"));
|
|
46544
49059
|
return processIsRunning(record.pid) && processMatchesRecord(record, processIdentityReader) ? { status: "running", record } : { status: "stopped", record };
|
|
46545
49060
|
}
|
|
46546
49061
|
async function waitForCommanderDaemon(options) {
|
|
46547
49062
|
const now = options.now ?? (() => Date.now());
|
|
46548
|
-
const readTextFile = options.readTextFile ?? ((path2) =>
|
|
49063
|
+
const readTextFile = options.readTextFile ?? ((path2) => existsSync14(path2) ? readFileSync16(path2, "utf8") : void 0);
|
|
46549
49064
|
const statusImpl = options.statusImpl ?? commanderDaemonStatus;
|
|
46550
49065
|
const deadline = now() + options.timeoutMs;
|
|
46551
49066
|
while (now() <= deadline) {
|
|
@@ -46744,6 +49259,7 @@ async function waitForFileChangeOrTimeout(path2, deadline, now, ready2 = () => f
|
|
|
46744
49259
|
|
|
46745
49260
|
// src/index.ts
|
|
46746
49261
|
init_version();
|
|
49262
|
+
init_telemetry();
|
|
46747
49263
|
|
|
46748
49264
|
// ../../node_modules/zod/v3/external.js
|
|
46749
49265
|
var external_exports = {};
|
|
@@ -54409,7 +56925,7 @@ var StdioServerTransport = class {
|
|
|
54409
56925
|
};
|
|
54410
56926
|
|
|
54411
56927
|
// src/mcpServer.ts
|
|
54412
|
-
import { readFile as
|
|
56928
|
+
import { readFile as readFile3 } from "node:fs/promises";
|
|
54413
56929
|
|
|
54414
56930
|
// node_modules/zod/v3/external.js
|
|
54415
56931
|
var external_exports2 = {};
|
|
@@ -58456,73 +60972,19 @@ var NEVER2 = INVALID2;
|
|
|
58456
60972
|
init_authCache();
|
|
58457
60973
|
init_commanderAttachments();
|
|
58458
60974
|
init_json();
|
|
58459
|
-
|
|
58460
|
-
// src/linzumiApiClient.ts
|
|
58461
|
-
init_oauth();
|
|
58462
|
-
init_protocol();
|
|
58463
|
-
function createLinzumiMcpApiClient(options) {
|
|
58464
|
-
const fetchImpl = options.fetchImpl ?? fetch;
|
|
58465
|
-
const baseUrl = kandanHttpBaseUrl(options.kandanUrl);
|
|
58466
|
-
const apiPrefix = options.authMode === "personal-agent-delegation" ? "/api/v2/personal-agent-mcp" : "/api/v2/local-runner-mcp";
|
|
58467
|
-
const operatingMode = options.operatingMode ?? "text";
|
|
58468
|
-
const request = async (method, path2, params) => {
|
|
58469
|
-
const url = new URL(path2, baseUrl);
|
|
58470
|
-
const paramsWithMode = {
|
|
58471
|
-
...params,
|
|
58472
|
-
operating_mode: operatingMode
|
|
58473
|
-
};
|
|
58474
|
-
const requestInit = {
|
|
58475
|
-
method,
|
|
58476
|
-
headers: { authorization: `Bearer ${options.accessToken}` }
|
|
58477
|
-
};
|
|
58478
|
-
if (method === "GET") {
|
|
58479
|
-
for (const [key, value] of Object.entries(paramsWithMode)) {
|
|
58480
|
-
if (value !== void 0 && value !== null) {
|
|
58481
|
-
url.searchParams.set(key, String(value));
|
|
58482
|
-
}
|
|
58483
|
-
}
|
|
58484
|
-
} else {
|
|
58485
|
-
requestInit.headers = {
|
|
58486
|
-
...requestInit.headers,
|
|
58487
|
-
"content-type": "application/json"
|
|
58488
|
-
};
|
|
58489
|
-
requestInit.body = JSON.stringify(paramsWithMode);
|
|
58490
|
-
}
|
|
58491
|
-
const response = await fetchImpl(url, requestInit);
|
|
58492
|
-
const parsed = await response.json();
|
|
58493
|
-
const body = isJsonObject(parsed) ? parsed : void 0;
|
|
58494
|
-
if (body === void 0) {
|
|
58495
|
-
throw new Error(`Linzumi MCP API returned non-object JSON from ${path2}`);
|
|
58496
|
-
}
|
|
58497
|
-
if (response.ok && body.ok === true) {
|
|
58498
|
-
return body;
|
|
58499
|
-
}
|
|
58500
|
-
const error = typeof body.error === "string" ? body.error : `HTTP ${response.status}`;
|
|
58501
|
-
throw new Error(`Linzumi MCP API ${path2} failed: ${error}`);
|
|
58502
|
-
};
|
|
58503
|
-
return {
|
|
58504
|
-
validateAuth: () => request("GET", `${apiPrefix}/validate`, {}),
|
|
58505
|
-
getMessage: (params) => request("GET", `${apiPrefix}/message`, params),
|
|
58506
|
-
getThread: (params) => request("GET", `${apiPrefix}/thread`, params),
|
|
58507
|
-
getChannel: (params) => request("GET", `${apiPrefix}/channel`, params),
|
|
58508
|
-
listVaultSecrets: (params) => request("GET", `${apiPrefix}/vault-secrets`, params),
|
|
58509
|
-
sendChannelMessage: (params) => request("POST", `${apiPrefix}/channel-message`, params),
|
|
58510
|
-
prepareMessageUploads: (params) => request("POST", `${apiPrefix}/message-uploads/prepare`, params),
|
|
58511
|
-
attachMessageFiles: (params) => request("POST", `${apiPrefix}/message-files/attach`, params),
|
|
58512
|
-
prepareCustomEmoji: (params) => request("POST", `${apiPrefix}/custom-emoji/prepare`, params),
|
|
58513
|
-
renameCustomEmoji: (params) => request("POST", `${apiPrefix}/custom-emoji/rename`, params),
|
|
58514
|
-
sendThreadReply: (params) => request("POST", `${apiPrefix}/thread-reply`, params),
|
|
58515
|
-
sendDm: (params) => request("POST", `${apiPrefix}/dm`, params),
|
|
58516
|
-
dmOwner: (params) => request("POST", `${apiPrefix}/dm-owner`, params),
|
|
58517
|
-
getVaultValues: (params) => request("POST", `${apiPrefix}/vault-values`, params)
|
|
58518
|
-
};
|
|
58519
|
-
}
|
|
58520
|
-
|
|
58521
|
-
// src/mcpServer.ts
|
|
60975
|
+
init_linzumiApiClient();
|
|
58522
60976
|
init_mcpConfig();
|
|
58523
60977
|
init_oauth();
|
|
58524
60978
|
init_protocol();
|
|
58525
60979
|
var maxCustomEmojiNameLength = 64;
|
|
60980
|
+
function customEmojiMcpToolsEnabledForScope(toolScope) {
|
|
60981
|
+
switch (toolScope) {
|
|
60982
|
+
case "all":
|
|
60983
|
+
return true;
|
|
60984
|
+
case "onboarding-discovery":
|
|
60985
|
+
return false;
|
|
60986
|
+
}
|
|
60987
|
+
}
|
|
58526
60988
|
function normalizeCustomEmojiNameForSchema(value) {
|
|
58527
60989
|
return value.trim().replace(/^:+/, "").replace(/:+$/, "").toLowerCase();
|
|
58528
60990
|
}
|
|
@@ -58545,6 +61007,7 @@ var mcpFlagDefinitions = /* @__PURE__ */ new Map([
|
|
|
58545
61007
|
["thread-id", { kind: "value" }],
|
|
58546
61008
|
["format", { kind: "value" }],
|
|
58547
61009
|
["command", { kind: "value" }],
|
|
61010
|
+
["tool-scope", { kind: "value" }],
|
|
58548
61011
|
["include-token", { kind: "boolean" }],
|
|
58549
61012
|
["help", { kind: "boolean" }]
|
|
58550
61013
|
]);
|
|
@@ -58582,8 +61045,26 @@ Tools:
|
|
|
58582
61045
|
linzumi_get_message Read one message by scoped seq or Linzumi message URL.
|
|
58583
61046
|
linzumi_get_thread Read one bounded thread by ID.
|
|
58584
61047
|
linzumi_get_channel Read bounded recent messages and channel metadata.
|
|
61048
|
+
linzumi_get_coding_job_metadata
|
|
61049
|
+
Read the current coding job goal, plan, and workflow metadata.
|
|
61050
|
+
linzumi_rename_coding_job
|
|
61051
|
+
Update the coding job thread and workflow-facing title.
|
|
61052
|
+
linzumi_upsert_coding_job_plan
|
|
61053
|
+
Set or update the coding job goal.
|
|
61054
|
+
linzumi_replace_coding_job_plan_steps
|
|
61055
|
+
Replace the ordered coding job plan steps.
|
|
61056
|
+
linzumi_update_coding_job_plan_step
|
|
61057
|
+
Update one coding job plan step status or note.
|
|
61058
|
+
linzumi_link_coding_job_pull_request
|
|
61059
|
+
Link the coding job to its primary GitHub pull request.
|
|
58585
61060
|
linzumi_list_vault_secrets
|
|
58586
61061
|
List vault secret names, descriptions, and owner scope.
|
|
61062
|
+
linzumi_note_project_directory
|
|
61063
|
+
Record one discovered local Git project directory.
|
|
61064
|
+
linzumi_note_agent_conversation
|
|
61065
|
+
Record one discovered Codex or Claude Code conversation.
|
|
61066
|
+
linzumi_note_onboarding_discovery_status
|
|
61067
|
+
Record onboarding discovery progress or failure.
|
|
58587
61068
|
linzumi_send_channel_message
|
|
58588
61069
|
Send a plain-text message to the scoped channel.
|
|
58589
61070
|
linzumi_send_thread_reply
|
|
@@ -58619,6 +61100,7 @@ async function runMcpServer(args) {
|
|
|
58619
61100
|
});
|
|
58620
61101
|
const ownerUsername = stringValue5(values, "owner-username") ?? process.env.LINZUMI_MCP_OWNER_USERNAME;
|
|
58621
61102
|
const operatingMode = mcpOperatingMode(stringValue5(values, "mode"));
|
|
61103
|
+
const toolScope = mcpToolScope(stringValue5(values, "tool-scope"));
|
|
58622
61104
|
const cwd = stringValue5(values, "cwd") ?? process.cwd();
|
|
58623
61105
|
const defaultThreadId = stringValue5(values, "thread-id") ?? process.env.LINZUMI_THREAD_RUNNER_KANDAN_THREAD_ID;
|
|
58624
61106
|
const client = createLinzumiMcpApiClient({
|
|
@@ -58632,201 +61114,447 @@ async function runMcpServer(args) {
|
|
|
58632
61114
|
version: "0.1.0"
|
|
58633
61115
|
});
|
|
58634
61116
|
registerEmptyMcpResources(server);
|
|
58635
|
-
|
|
58636
|
-
|
|
58637
|
-
|
|
58638
|
-
|
|
58639
|
-
|
|
58640
|
-
|
|
58641
|
-
|
|
58642
|
-
message_seq: external_exports2.union([external_exports2.string(), external_exports2.number()]).optional().describe("Alias for message_id."),
|
|
58643
|
-
message_url: external_exports2.string().optional().describe("Full Linzumi message URL with #message-<seq>."),
|
|
58644
|
-
before: external_exports2.number().int().min(0).max(100).optional().describe("Number of visible messages before the target."),
|
|
58645
|
-
after: external_exports2.number().int().min(0).max(100).optional().describe("Number of visible messages after the target.")
|
|
58646
|
-
},
|
|
58647
|
-
async (params) => mcpJsonResult(await client.getMessage(params))
|
|
58648
|
-
);
|
|
58649
|
-
server.tool(
|
|
58650
|
-
"linzumi_get_thread",
|
|
58651
|
-
"Read one Linzumi thread by thread_id in the scoped channel, including the parent message and bounded replies.",
|
|
58652
|
-
{
|
|
58653
|
-
workspace: external_exports2.string().optional().describe("Workspace slug. Omit to use the authenticated token scope."),
|
|
58654
|
-
channel: external_exports2.string().optional().describe("Channel slug. Omit to use the authenticated token scope."),
|
|
58655
|
-
thread_id: external_exports2.string().uuid().describe("Linzumi thread UUID."),
|
|
58656
|
-
limit: external_exports2.number().int().min(1).max(100).optional().describe("Maximum replies to return."),
|
|
58657
|
-
before_seq: external_exports2.number().int().positive().optional().describe("Return replies before this channel seq.")
|
|
58658
|
-
},
|
|
58659
|
-
async (params) => mcpJsonResult(await client.getThread(params))
|
|
58660
|
-
);
|
|
58661
|
-
server.tool(
|
|
58662
|
-
"linzumi_get_channel",
|
|
58663
|
-
"Read scoped Linzumi channel metadata and bounded recent channel messages.",
|
|
58664
|
-
{
|
|
58665
|
-
workspace: external_exports2.string().optional().describe("Workspace slug. Omit to use the authenticated token scope."),
|
|
58666
|
-
channel: external_exports2.string().optional().describe("Channel slug. Omit to use the authenticated token scope."),
|
|
58667
|
-
limit: external_exports2.number().int().min(1).max(100).optional().describe("Maximum messages to return."),
|
|
58668
|
-
before_seq: external_exports2.number().int().positive().optional().describe("Return messages before this channel seq.")
|
|
58669
|
-
},
|
|
58670
|
-
async (params) => mcpJsonResult(await client.getChannel(params))
|
|
58671
|
-
);
|
|
58672
|
-
server.tool(
|
|
58673
|
-
"linzumi_upload_files",
|
|
58674
|
-
"Upload local files to Linzumi and attach them to a new, exact, or latest assistant message. Use silently; do not mention this tool in the visible reply.",
|
|
58675
|
-
{
|
|
58676
|
-
target: external_exports2.object({
|
|
58677
|
-
kind: external_exports2.enum(["new_message", "message_seq", "latest_assistant_message"]).describe(
|
|
58678
|
-
"new_message creates a message; message_seq edits an exact message; latest_assistant_message edits the latest assistant message in the scoped thread/channel."
|
|
61117
|
+
if (toolScope === "all") {
|
|
61118
|
+
server.tool(
|
|
61119
|
+
"linzumi_get_message",
|
|
61120
|
+
"Read one Linzumi message by scoped message_seq/message_id or a Linzumi message URL, with optional bounded before/after context.",
|
|
61121
|
+
{
|
|
61122
|
+
workspace: external_exports2.string().optional().describe(
|
|
61123
|
+
"Workspace slug. Omit to use the authenticated token scope."
|
|
58679
61124
|
),
|
|
58680
|
-
|
|
58681
|
-
|
|
58682
|
-
|
|
61125
|
+
channel: external_exports2.string().optional().describe("Channel slug. Omit to use the authenticated token scope."),
|
|
61126
|
+
message_id: external_exports2.union([external_exports2.string(), external_exports2.number()]).optional().describe("Message seq inside the scoped channel."),
|
|
61127
|
+
message_seq: external_exports2.union([external_exports2.string(), external_exports2.number()]).optional().describe("Alias for message_id."),
|
|
61128
|
+
message_url: external_exports2.string().optional().describe("Full Linzumi message URL with #message-<seq>."),
|
|
61129
|
+
before: external_exports2.number().int().min(0).max(100).optional().describe("Number of visible messages before the target."),
|
|
61130
|
+
after: external_exports2.number().int().min(0).max(100).optional().describe("Number of visible messages after the target.")
|
|
61131
|
+
},
|
|
61132
|
+
async (params) => mcpJsonResult(await client.getMessage(params))
|
|
61133
|
+
);
|
|
61134
|
+
server.tool(
|
|
61135
|
+
"linzumi_get_thread",
|
|
61136
|
+
"Read one Linzumi thread by thread_id in the scoped channel, including the parent message and bounded replies.",
|
|
61137
|
+
{
|
|
61138
|
+
workspace: external_exports2.string().optional().describe(
|
|
61139
|
+
"Workspace slug. Omit to use the authenticated token scope."
|
|
61140
|
+
),
|
|
61141
|
+
channel: external_exports2.string().optional().describe("Channel slug. Omit to use the authenticated token scope."),
|
|
61142
|
+
thread_id: external_exports2.string().uuid().describe("Linzumi thread UUID."),
|
|
61143
|
+
limit: external_exports2.number().int().min(1).max(100).optional().describe("Maximum replies to return."),
|
|
61144
|
+
before_seq: external_exports2.number().int().positive().optional().describe("Return replies before this channel seq.")
|
|
61145
|
+
},
|
|
61146
|
+
async (params) => mcpJsonResult(await client.getThread(params))
|
|
61147
|
+
);
|
|
61148
|
+
server.tool(
|
|
61149
|
+
"linzumi_get_channel",
|
|
61150
|
+
"Read scoped Linzumi channel metadata and bounded recent channel messages.",
|
|
61151
|
+
{
|
|
61152
|
+
workspace: external_exports2.string().optional().describe(
|
|
61153
|
+
"Workspace slug. Omit to use the authenticated token scope."
|
|
61154
|
+
),
|
|
61155
|
+
channel: external_exports2.string().optional().describe("Channel slug. Omit to use the authenticated token scope."),
|
|
61156
|
+
limit: external_exports2.number().int().min(1).max(100).optional().describe("Maximum messages to return."),
|
|
61157
|
+
before_seq: external_exports2.number().int().positive().optional().describe("Return messages before this channel seq.")
|
|
61158
|
+
},
|
|
61159
|
+
async (params) => mcpJsonResult(await client.getChannel(params))
|
|
61160
|
+
);
|
|
61161
|
+
server.tool(
|
|
61162
|
+
"linzumi_get_coding_job_metadata",
|
|
61163
|
+
"Read the active coding job workflow metadata for a thread, including the agent-owned goal, ordered plan steps, step lock versions, and workflow status.",
|
|
61164
|
+
{
|
|
61165
|
+
workspace: external_exports2.string().optional().describe(
|
|
61166
|
+
"Workspace slug. Omit to use the authenticated token scope."
|
|
61167
|
+
),
|
|
61168
|
+
channel: external_exports2.string().optional().describe("Channel slug. Omit to use the authenticated token scope."),
|
|
61169
|
+
thread_id: external_exports2.string().uuid().optional().describe(
|
|
61170
|
+
"Linzumi thread UUID. Defaults to the active coding job thread."
|
|
58683
61171
|
)
|
|
58684
|
-
}
|
|
58685
|
-
|
|
58686
|
-
|
|
58687
|
-
|
|
58688
|
-
|
|
58689
|
-
)
|
|
58690
|
-
|
|
58691
|
-
|
|
58692
|
-
|
|
58693
|
-
|
|
58694
|
-
|
|
58695
|
-
|
|
58696
|
-
|
|
58697
|
-
|
|
58698
|
-
|
|
58699
|
-
|
|
58700
|
-
|
|
58701
|
-
|
|
58702
|
-
|
|
58703
|
-
|
|
58704
|
-
|
|
58705
|
-
|
|
58706
|
-
|
|
58707
|
-
|
|
58708
|
-
|
|
58709
|
-
|
|
58710
|
-
|
|
58711
|
-
|
|
58712
|
-
|
|
58713
|
-
|
|
58714
|
-
|
|
58715
|
-
|
|
58716
|
-
|
|
58717
|
-
|
|
58718
|
-
|
|
58719
|
-
|
|
58720
|
-
|
|
58721
|
-
|
|
58722
|
-
|
|
58723
|
-
|
|
58724
|
-
|
|
58725
|
-
|
|
58726
|
-
|
|
58727
|
-
|
|
58728
|
-
|
|
58729
|
-
|
|
58730
|
-
|
|
58731
|
-
|
|
58732
|
-
|
|
58733
|
-
|
|
58734
|
-
|
|
58735
|
-
|
|
58736
|
-
|
|
58737
|
-
|
|
58738
|
-
|
|
58739
|
-
|
|
58740
|
-
|
|
58741
|
-
|
|
58742
|
-
|
|
58743
|
-
|
|
58744
|
-
|
|
58745
|
-
|
|
58746
|
-
|
|
58747
|
-
|
|
58748
|
-
|
|
58749
|
-
|
|
58750
|
-
|
|
58751
|
-
|
|
58752
|
-
|
|
58753
|
-
|
|
58754
|
-
|
|
58755
|
-
|
|
58756
|
-
|
|
58757
|
-
|
|
58758
|
-
|
|
58759
|
-
|
|
58760
|
-
|
|
58761
|
-
|
|
58762
|
-
|
|
58763
|
-
|
|
58764
|
-
|
|
58765
|
-
|
|
58766
|
-
|
|
58767
|
-
|
|
58768
|
-
|
|
58769
|
-
|
|
58770
|
-
|
|
58771
|
-
|
|
58772
|
-
|
|
58773
|
-
|
|
58774
|
-
|
|
58775
|
-
|
|
58776
|
-
|
|
61172
|
+
},
|
|
61173
|
+
async (params) => mcpJsonResult(
|
|
61174
|
+
await client.getCodingJobMetadata(
|
|
61175
|
+
paramsWithDefaultThread(params, defaultThreadId)
|
|
61176
|
+
)
|
|
61177
|
+
)
|
|
61178
|
+
);
|
|
61179
|
+
server.tool(
|
|
61180
|
+
"linzumi_rename_coding_job",
|
|
61181
|
+
"Update the current coding job thread title and workflow-facing title. Use this when the job scope changes materially so the Linzumi thread list and metadata overview stay accurate.",
|
|
61182
|
+
{
|
|
61183
|
+
workspace: external_exports2.string().optional().describe(
|
|
61184
|
+
"Workspace slug. Omit to use the authenticated token scope."
|
|
61185
|
+
),
|
|
61186
|
+
channel: external_exports2.string().optional().describe("Channel slug. Omit to use the authenticated token scope."),
|
|
61187
|
+
thread_id: external_exports2.string().uuid().optional().describe(
|
|
61188
|
+
"Linzumi thread UUID. Defaults to the active coding job thread."
|
|
61189
|
+
),
|
|
61190
|
+
title: external_exports2.string().min(1).max(100).describe("Concise user-visible title for this coding job.")
|
|
61191
|
+
},
|
|
61192
|
+
async (params) => mcpJsonResult(
|
|
61193
|
+
await client.renameCodingJob(
|
|
61194
|
+
paramsWithDefaultThread(params, defaultThreadId)
|
|
61195
|
+
)
|
|
61196
|
+
)
|
|
61197
|
+
);
|
|
61198
|
+
server.tool(
|
|
61199
|
+
"linzumi_upsert_coding_job_plan",
|
|
61200
|
+
"Set or update the current coding job goal. Use this at job start and whenever the goal changes materially.",
|
|
61201
|
+
{
|
|
61202
|
+
workspace: external_exports2.string().optional().describe(
|
|
61203
|
+
"Workspace slug. Omit to use the authenticated token scope."
|
|
61204
|
+
),
|
|
61205
|
+
channel: external_exports2.string().optional().describe("Channel slug. Omit to use the authenticated token scope."),
|
|
61206
|
+
thread_id: external_exports2.string().uuid().optional().describe(
|
|
61207
|
+
"Linzumi thread UUID. Defaults to the active coding job thread."
|
|
61208
|
+
),
|
|
61209
|
+
goal: external_exports2.string().min(1).max(1e4).describe("Concrete user-visible goal for this coding job.")
|
|
61210
|
+
},
|
|
61211
|
+
async (params) => mcpJsonResult(
|
|
61212
|
+
await client.upsertCodingJobPlan(
|
|
61213
|
+
paramsWithDefaultThread(params, defaultThreadId)
|
|
61214
|
+
)
|
|
61215
|
+
)
|
|
61216
|
+
);
|
|
61217
|
+
server.tool(
|
|
61218
|
+
"linzumi_replace_coding_job_plan_steps",
|
|
61219
|
+
"Replace the ordered coding job plan steps. Use this for the initial plan and for substantial rewrites when the old plan no longer fits the work.",
|
|
61220
|
+
{
|
|
61221
|
+
workspace: external_exports2.string().optional().describe(
|
|
61222
|
+
"Workspace slug. Omit to use the authenticated token scope."
|
|
61223
|
+
),
|
|
61224
|
+
channel: external_exports2.string().optional().describe("Channel slug. Omit to use the authenticated token scope."),
|
|
61225
|
+
thread_id: external_exports2.string().uuid().optional().describe(
|
|
61226
|
+
"Linzumi thread UUID. Defaults to the active coding job thread."
|
|
61227
|
+
),
|
|
61228
|
+
goal: external_exports2.string().min(1).max(1e4).optional().describe(
|
|
61229
|
+
"Required when no plan exists yet; updates the current coding job goal."
|
|
61230
|
+
),
|
|
61231
|
+
steps: external_exports2.array(
|
|
61232
|
+
external_exports2.object({
|
|
61233
|
+
title: external_exports2.string().min(1).max(255),
|
|
61234
|
+
description: external_exports2.string().min(1).max(1e4),
|
|
61235
|
+
status: external_exports2.enum([
|
|
61236
|
+
"pending",
|
|
61237
|
+
"active",
|
|
61238
|
+
"completed",
|
|
61239
|
+
"blocked",
|
|
61240
|
+
"canceled"
|
|
61241
|
+
]),
|
|
61242
|
+
status_note: external_exports2.string().min(1).max(4e3).optional(),
|
|
61243
|
+
completed_debrief: external_exports2.string().min(1).max(2e4).optional()
|
|
61244
|
+
})
|
|
61245
|
+
).min(1).max(50).describe(
|
|
61246
|
+
"Ordered user-visible plan steps. Completed steps must include completed_debrief."
|
|
61247
|
+
)
|
|
61248
|
+
},
|
|
61249
|
+
async (params) => mcpJsonResult(
|
|
61250
|
+
await client.replaceCodingJobPlanSteps(
|
|
61251
|
+
paramsWithDefaultThread(params, defaultThreadId)
|
|
61252
|
+
)
|
|
61253
|
+
)
|
|
61254
|
+
);
|
|
61255
|
+
server.tool(
|
|
61256
|
+
"linzumi_update_coding_job_plan_step",
|
|
61257
|
+
"Update one coding job plan step status or note. Read metadata first and pass the current step lock_version.",
|
|
61258
|
+
{
|
|
61259
|
+
workspace: external_exports2.string().optional().describe(
|
|
61260
|
+
"Workspace slug. Omit to use the authenticated token scope."
|
|
61261
|
+
),
|
|
61262
|
+
channel: external_exports2.string().optional().describe("Channel slug. Omit to use the authenticated token scope."),
|
|
61263
|
+
thread_id: external_exports2.string().uuid().optional().describe(
|
|
61264
|
+
"Linzumi thread UUID. Defaults to the active coding job thread."
|
|
61265
|
+
),
|
|
61266
|
+
step_id: external_exports2.string().uuid().describe("Step id returned by linzumi_get_coding_job_metadata."),
|
|
61267
|
+
lock_version: external_exports2.number().int().positive().describe("Current step lock_version from metadata."),
|
|
61268
|
+
status: external_exports2.enum([
|
|
61269
|
+
"pending",
|
|
61270
|
+
"active",
|
|
61271
|
+
"completed",
|
|
61272
|
+
"blocked",
|
|
61273
|
+
"canceled"
|
|
61274
|
+
]),
|
|
61275
|
+
status_note: external_exports2.string().min(1).max(4e3).optional(),
|
|
61276
|
+
completed_debrief: external_exports2.string().min(1).max(2e4).optional().describe("Required when marking a step completed.")
|
|
61277
|
+
},
|
|
61278
|
+
async (params) => mcpJsonResult(
|
|
61279
|
+
await client.updateCodingJobPlanStep(
|
|
61280
|
+
paramsWithDefaultThread(params, defaultThreadId)
|
|
61281
|
+
)
|
|
61282
|
+
)
|
|
61283
|
+
);
|
|
61284
|
+
server.tool(
|
|
61285
|
+
"linzumi_link_coding_job_pull_request",
|
|
61286
|
+
"Link the active coding job to its primary GitHub pull request so PR comments, files, commits, checks, deployments, and reviews can flow into the metadata view.",
|
|
61287
|
+
{
|
|
61288
|
+
workspace: external_exports2.string().optional().describe(
|
|
61289
|
+
"Workspace slug. Omit to use the authenticated token scope."
|
|
61290
|
+
),
|
|
61291
|
+
channel: external_exports2.string().optional().describe("Channel slug. Omit to use the authenticated token scope."),
|
|
61292
|
+
thread_id: external_exports2.string().uuid().optional().describe(
|
|
61293
|
+
"Linzumi thread UUID. Defaults to the active coding job thread."
|
|
61294
|
+
),
|
|
61295
|
+
github_repo_owner: external_exports2.string().min(1).max(255),
|
|
61296
|
+
github_repo_name: external_exports2.string().min(1).max(255),
|
|
61297
|
+
github_pr_number: external_exports2.number().int().positive(),
|
|
61298
|
+
github_pr_url: external_exports2.string().url(),
|
|
61299
|
+
title: external_exports2.string().min(1).max(255),
|
|
61300
|
+
body: external_exports2.string().min(1).max(1e5).optional(),
|
|
61301
|
+
head_branch: external_exports2.string().min(1).max(255),
|
|
61302
|
+
head_sha: external_exports2.string().min(7).max(64),
|
|
61303
|
+
base_branch: external_exports2.string().min(1).max(255),
|
|
61304
|
+
base_sha: external_exports2.string().min(7).max(64),
|
|
61305
|
+
state: external_exports2.enum(["draft", "open", "closed", "merged"]).optional(),
|
|
61306
|
+
is_draft: external_exports2.boolean().optional(),
|
|
61307
|
+
review_status: external_exports2.enum(["unknown", "review_required", "approved", "changes_requested"]).optional(),
|
|
61308
|
+
source_remote_url: external_exports2.string().min(1).max(1e3).describe(
|
|
61309
|
+
"Git remote URL for the local job worktree; required when this job has no repo workspace yet."
|
|
61310
|
+
),
|
|
61311
|
+
runtime_worktree_root: external_exports2.string().min(1).max(1e3).describe(
|
|
61312
|
+
"Absolute local worktree path for the coding job; required when this job has no repo workspace yet."
|
|
61313
|
+
),
|
|
61314
|
+
dirty_status: external_exports2.enum(["unknown", "clean", "dirty"]).optional(),
|
|
61315
|
+
dirty_summary: external_exports2.string().min(1).max(2e3).optional()
|
|
61316
|
+
},
|
|
61317
|
+
async (params) => mcpJsonResult(
|
|
61318
|
+
await client.linkCodingJobPullRequest(
|
|
61319
|
+
paramsWithDefaultThread(params, defaultThreadId)
|
|
61320
|
+
)
|
|
61321
|
+
)
|
|
61322
|
+
);
|
|
61323
|
+
server.tool(
|
|
61324
|
+
"linzumi_upload_files",
|
|
61325
|
+
"Upload local files to Linzumi and attach them to a new, exact, or latest assistant message. Use silently; do not mention this tool in the visible reply.",
|
|
61326
|
+
{
|
|
61327
|
+
target: external_exports2.object({
|
|
61328
|
+
kind: external_exports2.enum(["new_message", "message_seq", "latest_assistant_message"]).describe(
|
|
61329
|
+
"new_message creates a message; message_seq edits an exact message; latest_assistant_message edits the latest assistant message in the scoped thread/channel."
|
|
61330
|
+
),
|
|
61331
|
+
message_seq: external_exports2.union([external_exports2.string(), external_exports2.number()]).optional().describe("Required when kind is message_seq."),
|
|
61332
|
+
thread_id: external_exports2.string().optional().describe(
|
|
61333
|
+
"Thread UUID. Defaults to the active Linzumi thread when the MCP server was launched with one."
|
|
61334
|
+
)
|
|
61335
|
+
}).describe(
|
|
61336
|
+
"Attachment target. Do not guess; use new_message when there is no previous assistant message."
|
|
61337
|
+
),
|
|
61338
|
+
files: external_exports2.array(external_exports2.string().min(1)).min(1).max(16).describe(
|
|
61339
|
+
"Local file paths to upload. Paths must be non-hidden supported file types inside the approved workspace or Downloads."
|
|
61340
|
+
),
|
|
61341
|
+
body: external_exports2.string().min(1).max(2e4).optional().describe(
|
|
61342
|
+
"Required for new_message. Optional replacement body for message_seq/latest_assistant_message; when omitted, the existing message body is preserved."
|
|
61343
|
+
),
|
|
61344
|
+
workspace: external_exports2.string().optional().describe(
|
|
61345
|
+
"Workspace slug. Omit to use the authenticated token scope."
|
|
61346
|
+
),
|
|
61347
|
+
channel: external_exports2.string().optional().describe("Channel slug. Omit to use the authenticated token scope.")
|
|
61348
|
+
},
|
|
61349
|
+
async (params) => {
|
|
61350
|
+
const target = targetWithDefaultThread(
|
|
61351
|
+
params.target,
|
|
61352
|
+
defaultThreadId
|
|
61353
|
+
);
|
|
61354
|
+
const result = await uploadFilesWithClient({
|
|
61355
|
+
client,
|
|
61356
|
+
cwd,
|
|
61357
|
+
kandanUrl,
|
|
61358
|
+
target,
|
|
61359
|
+
files: params.files,
|
|
61360
|
+
workspace: params.workspace,
|
|
61361
|
+
channel: params.channel,
|
|
61362
|
+
body: params.body
|
|
61363
|
+
});
|
|
61364
|
+
return mcpJsonResult(result);
|
|
61365
|
+
}
|
|
61366
|
+
);
|
|
61367
|
+
server.tool(
|
|
61368
|
+
"linzumi_list_vault_secrets",
|
|
61369
|
+
"List vault secret names, descriptions, and whether each secret is personal or workspace-owned. Does not return secret values.",
|
|
61370
|
+
{},
|
|
61371
|
+
async (params) => mcpJsonResult(await client.listVaultSecrets(params))
|
|
61372
|
+
);
|
|
61373
|
+
}
|
|
61374
|
+
if (customEmojiMcpToolsEnabledForScope(toolScope)) {
|
|
61375
|
+
server.tool(
|
|
61376
|
+
"linzumi_upload_custom_emoji",
|
|
61377
|
+
"Upload a local image as a workspace custom emoji. For generated emoji art, create a transparent PNG with a simple centered subject, crisp edges, strong contrast, minimal tiny details, and enough outline/highlight separation to read at 16-24px on both dark and light backgrounds. Use lowercase shortcode names without colons unless preserving a user-provided :name:.",
|
|
61378
|
+
{
|
|
61379
|
+
name: customEmojiNameSchema.describe(
|
|
61380
|
+
"Custom emoji shortcode name, with or without surrounding colons."
|
|
61381
|
+
),
|
|
61382
|
+
file: external_exports2.string().min(1).describe(
|
|
61383
|
+
"Local PNG/WebP/GIF/JPEG file path to upload. Prefer transparent PNG for generated emoji."
|
|
61384
|
+
),
|
|
61385
|
+
workspace: external_exports2.string().optional().describe(
|
|
61386
|
+
"Workspace slug. Omit to use the authenticated token scope."
|
|
61387
|
+
)
|
|
61388
|
+
},
|
|
61389
|
+
async (params) => mcpJsonResult(
|
|
61390
|
+
await uploadCustomEmojiWithClient({
|
|
61391
|
+
client,
|
|
61392
|
+
cwd,
|
|
61393
|
+
kandanUrl,
|
|
61394
|
+
name: params.name,
|
|
61395
|
+
file: params.file,
|
|
61396
|
+
workspace: params.workspace
|
|
58777
61397
|
})
|
|
58778
|
-
)
|
|
58779
|
-
|
|
58780
|
-
|
|
58781
|
-
|
|
58782
|
-
|
|
58783
|
-
|
|
58784
|
-
|
|
58785
|
-
|
|
58786
|
-
|
|
58787
|
-
|
|
58788
|
-
|
|
58789
|
-
|
|
58790
|
-
|
|
61398
|
+
)
|
|
61399
|
+
);
|
|
61400
|
+
server.tool(
|
|
61401
|
+
"linzumi_rename_custom_emoji",
|
|
61402
|
+
"Rename an existing workspace custom emoji. The new name may include surrounding colons; Linzumi stores the normalized shortcode without colons.",
|
|
61403
|
+
{
|
|
61404
|
+
emoji_id: external_exports2.union([external_exports2.string(), external_exports2.number().int().positive()]).describe(
|
|
61405
|
+
"Workspace custom emoji id returned by linzumi_upload_custom_emoji or workspace emoji listings."
|
|
61406
|
+
),
|
|
61407
|
+
name: customEmojiNameSchema.describe(
|
|
61408
|
+
"New shortcode name, with or without surrounding colons."
|
|
61409
|
+
),
|
|
61410
|
+
workspace: external_exports2.string().optional().describe(
|
|
61411
|
+
"Workspace slug. Omit to use the authenticated token scope."
|
|
61412
|
+
)
|
|
61413
|
+
},
|
|
61414
|
+
async (params) => mcpJsonResult(await client.renameCustomEmoji(params))
|
|
61415
|
+
);
|
|
61416
|
+
}
|
|
58791
61417
|
server.tool(
|
|
58792
|
-
"
|
|
58793
|
-
"
|
|
61418
|
+
"linzumi_note_project_directory",
|
|
61419
|
+
"Record one discovered local Git repository or worktree for onboarding import. Call once per project as soon as it is found.",
|
|
58794
61420
|
{
|
|
58795
61421
|
workspace: external_exports2.string().optional().describe("Workspace slug. Omit to use the authenticated token scope."),
|
|
58796
|
-
|
|
58797
|
-
|
|
58798
|
-
|
|
61422
|
+
runner_id: external_exports2.string().min(1).describe("Local runner/computer id provided in the discovery prompt."),
|
|
61423
|
+
path: external_exports2.string().min(1).describe("Absolute local path to the repository worktree root."),
|
|
61424
|
+
display_name: external_exports2.string().optional().describe("Short project label. Defaults to the path basename."),
|
|
61425
|
+
activity_at: external_exports2.string().optional().describe("Best-known recent activity time as an ISO-8601 timestamp."),
|
|
61426
|
+
import_key: external_exports2.string().optional().describe("Stable dedupe key. Defaults to path."),
|
|
61427
|
+
metadata: external_exports2.record(external_exports2.unknown()).optional().describe("Small structured metadata such as branch or remote origin.")
|
|
58799
61428
|
},
|
|
58800
|
-
async (params) => mcpJsonResult(await client.
|
|
61429
|
+
async (params) => mcpJsonResult(await client.noteProjectDirectory(params))
|
|
58801
61430
|
);
|
|
58802
61431
|
server.tool(
|
|
58803
|
-
"
|
|
58804
|
-
"
|
|
61432
|
+
"linzumi_note_agent_conversation",
|
|
61433
|
+
"Record one discovered Codex or Claude Code local conversation for onboarding import. Call once per conversation as soon as it is found.",
|
|
58805
61434
|
{
|
|
58806
61435
|
workspace: external_exports2.string().optional().describe("Workspace slug. Omit to use the authenticated token scope."),
|
|
58807
|
-
|
|
58808
|
-
|
|
58809
|
-
|
|
61436
|
+
runner_id: external_exports2.string().min(1).describe("Local runner/computer id provided in the discovery prompt."),
|
|
61437
|
+
source: external_exports2.enum(["codex", "claude_code"]).describe("Conversation source."),
|
|
61438
|
+
import_key: external_exports2.string().min(1).describe("Stable dedupe key, such as a session id or file path."),
|
|
61439
|
+
path: external_exports2.string().optional().describe("Absolute local path to the conversation file or directory."),
|
|
61440
|
+
display_name: external_exports2.string().optional().describe("Short label for the discovered conversation."),
|
|
61441
|
+
activity_at: external_exports2.string().optional().describe("Best-known recent activity time as an ISO-8601 timestamp."),
|
|
61442
|
+
metadata: external_exports2.record(external_exports2.unknown()).optional().describe(
|
|
61443
|
+
"Small structured metadata such as session id or project path."
|
|
61444
|
+
)
|
|
58810
61445
|
},
|
|
58811
|
-
async (params) => mcpJsonResult(await client.
|
|
61446
|
+
async (params) => mcpJsonResult(await client.noteAgentConversation(params))
|
|
58812
61447
|
);
|
|
58813
61448
|
server.tool(
|
|
58814
|
-
"
|
|
58815
|
-
"
|
|
61449
|
+
"linzumi_note_onboarding_discovery_status",
|
|
61450
|
+
"Record onboarding discovery progress. Call when a search phase starts, checks another place, completes, or fails.",
|
|
58816
61451
|
{
|
|
58817
61452
|
workspace: external_exports2.string().optional().describe("Workspace slug. Omit to use the authenticated token scope."),
|
|
58818
|
-
|
|
58819
|
-
|
|
61453
|
+
runner_id: external_exports2.string().min(1).describe("Local runner/computer id provided in the discovery prompt."),
|
|
61454
|
+
phase: external_exports2.enum(["projects", "conversations"]).describe("Discovery phase being reported."),
|
|
61455
|
+
status: external_exports2.enum(["starting", "running", "completed", "failed"]).describe("Current phase status."),
|
|
61456
|
+
places_searched: external_exports2.number().int().nonnegative().optional().describe(
|
|
61457
|
+
"Count of concrete folders/files/known locations checked so far."
|
|
58820
61458
|
),
|
|
58821
|
-
|
|
58822
|
-
|
|
58823
|
-
"
|
|
61459
|
+
message: external_exports2.string().optional().describe("Short user-facing status note."),
|
|
61460
|
+
current_place: external_exports2.string().optional().describe(
|
|
61461
|
+
"Current folder, file, Git command target, or known location being checked."
|
|
58824
61462
|
),
|
|
58825
|
-
|
|
58826
|
-
|
|
61463
|
+
current_source: external_exports2.string().optional().describe(
|
|
61464
|
+
"Short machine-readable source label such as git, codex, or claude_code."
|
|
61465
|
+
),
|
|
61466
|
+
last_error: external_exports2.string().optional().describe("Short failure detail when status is failed."),
|
|
61467
|
+
metadata: external_exports2.record(external_exports2.unknown()).optional().describe("Small structured status metadata.")
|
|
58827
61468
|
},
|
|
58828
|
-
async (params) => mcpJsonResult(
|
|
61469
|
+
async (params) => mcpJsonResult(
|
|
61470
|
+
await client.noteOnboardingDiscoveryStatus(params)
|
|
61471
|
+
)
|
|
58829
61472
|
);
|
|
61473
|
+
if (toolScope === "all") {
|
|
61474
|
+
server.tool(
|
|
61475
|
+
"linzumi_dm_owner",
|
|
61476
|
+
"Send a plain-text DM to the configured Commander owner. Requires dm.write and a visible owner username.",
|
|
61477
|
+
{
|
|
61478
|
+
owner_username: external_exports2.string().optional().describe(
|
|
61479
|
+
"Owner username. Defaults to LINZUMI_MCP_OWNER_USERNAME or --owner-username."
|
|
61480
|
+
),
|
|
61481
|
+
workspace: external_exports2.string().optional().describe(
|
|
61482
|
+
"Workspace slug. Omit to use the authenticated token scope."
|
|
61483
|
+
),
|
|
61484
|
+
channel: external_exports2.string().optional().describe("Source channel slug used to verify owner visibility."),
|
|
61485
|
+
body: external_exports2.string().min(1).max(2e4).describe("Plain-text message body to DM.")
|
|
61486
|
+
},
|
|
61487
|
+
async (params) => {
|
|
61488
|
+
const owner = params.owner_username ?? ownerUsername;
|
|
61489
|
+
if (owner === void 0) {
|
|
61490
|
+
throw new Error("owner_username is required for linzumi_dm_owner");
|
|
61491
|
+
}
|
|
61492
|
+
return mcpJsonResult(
|
|
61493
|
+
await client.dmOwner({
|
|
61494
|
+
...params,
|
|
61495
|
+
owner_username: owner
|
|
61496
|
+
})
|
|
61497
|
+
);
|
|
61498
|
+
}
|
|
61499
|
+
);
|
|
61500
|
+
server.tool(
|
|
61501
|
+
"linzumi_send_channel_message",
|
|
61502
|
+
"Send a plain-text message as the authenticated local-runner user to the scoped Linzumi channel. Requires channel.write.",
|
|
61503
|
+
{
|
|
61504
|
+
workspace: external_exports2.string().optional().describe(
|
|
61505
|
+
"Workspace slug. Omit to use the authenticated token scope."
|
|
61506
|
+
),
|
|
61507
|
+
channel: external_exports2.string().optional().describe("Channel slug. Omit to use the authenticated token scope."),
|
|
61508
|
+
body: external_exports2.string().min(1).max(2e4).describe("Plain-text message body to post.")
|
|
61509
|
+
},
|
|
61510
|
+
async (params) => mcpJsonResult(await client.sendChannelMessage(params))
|
|
61511
|
+
);
|
|
61512
|
+
server.tool(
|
|
61513
|
+
"linzumi_send_thread_reply",
|
|
61514
|
+
"Send a plain-text reply as the authenticated local-runner user to an existing thread in the scoped Linzumi channel. Requires thread.write.",
|
|
61515
|
+
{
|
|
61516
|
+
workspace: external_exports2.string().optional().describe(
|
|
61517
|
+
"Workspace slug. Omit to use the authenticated token scope."
|
|
61518
|
+
),
|
|
61519
|
+
channel: external_exports2.string().optional().describe("Channel slug. Omit to use the authenticated token scope."),
|
|
61520
|
+
thread_id: external_exports2.string().uuid().describe("Existing Linzumi thread UUID."),
|
|
61521
|
+
body: external_exports2.string().min(1).max(2e4).describe("Plain-text reply body to post.")
|
|
61522
|
+
},
|
|
61523
|
+
async (params) => mcpJsonResult(await client.sendThreadReply(params))
|
|
61524
|
+
);
|
|
61525
|
+
server.tool(
|
|
61526
|
+
"linzumi_send_dm",
|
|
61527
|
+
"Send a plain-text DM as the authenticated local-runner user to a visible user in the scoped workspace/channel. Requires dm.write.",
|
|
61528
|
+
{
|
|
61529
|
+
workspace: external_exports2.string().optional().describe(
|
|
61530
|
+
"Workspace slug. Omit to use the authenticated token scope."
|
|
61531
|
+
),
|
|
61532
|
+
channel: external_exports2.string().optional().describe("Source channel slug used to verify target visibility."),
|
|
61533
|
+
username: external_exports2.string().describe("Visible Linzumi username to DM."),
|
|
61534
|
+
body: external_exports2.string().min(1).max(2e4).describe("Plain-text message body to DM.")
|
|
61535
|
+
},
|
|
61536
|
+
async (params) => mcpJsonResult(await client.sendDm(params))
|
|
61537
|
+
);
|
|
61538
|
+
server.tool(
|
|
61539
|
+
"linzumi_get_vault_values",
|
|
61540
|
+
"Request values for explicit vault env var names after a thread-scoped human approval. Requires vault.read; thread_id resolves the approval thread.",
|
|
61541
|
+
{
|
|
61542
|
+
workspace: external_exports2.string().optional().describe(
|
|
61543
|
+
"Workspace slug. Omit to use the authenticated token scope."
|
|
61544
|
+
),
|
|
61545
|
+
channel: external_exports2.string().optional().describe(
|
|
61546
|
+
"Channel slug used to disambiguate the approval thread when needed."
|
|
61547
|
+
),
|
|
61548
|
+
thread_id: external_exports2.string().uuid().describe("Existing Linzumi thread UUID for the inline approval."),
|
|
61549
|
+
env_var_names: external_exports2.array(external_exports2.string().regex(/^[A-Z][A-Z0-9_]{0,127}$/)).min(1).max(20).describe(
|
|
61550
|
+
"Explicit vault env var names to request. This tool cannot list all vault keys."
|
|
61551
|
+
),
|
|
61552
|
+
description: external_exports2.string().min(1).max(500).describe("Human-readable reason shown in the approval request."),
|
|
61553
|
+
justification: external_exports2.string().min(10).max(1e3).describe("Agent justification shown to the approver.")
|
|
61554
|
+
},
|
|
61555
|
+
async (params) => mcpJsonResult(await client.getVaultValues(params))
|
|
61556
|
+
);
|
|
61557
|
+
}
|
|
58830
61558
|
await server.connect(new StdioServerTransport());
|
|
58831
61559
|
}
|
|
58832
61560
|
async function uploadFilesWithClient(args) {
|
|
@@ -58854,13 +61582,13 @@ async function uploadFilesWithClient(args) {
|
|
|
58854
61582
|
if (uploadUrl === void 0) {
|
|
58855
61583
|
throw new Error("Linzumi upload prepare response missing upload_url");
|
|
58856
61584
|
}
|
|
58857
|
-
const bytes = await
|
|
61585
|
+
const bytes = await readFile3(file.path);
|
|
58858
61586
|
const uploadBody = bytes.buffer.slice(
|
|
58859
61587
|
bytes.byteOffset,
|
|
58860
61588
|
bytes.byteOffset + bytes.byteLength
|
|
58861
61589
|
);
|
|
58862
61590
|
const response = await fetch(
|
|
58863
|
-
|
|
61591
|
+
resolveLinzumiUploadUrl2(args.kandanUrl, uploadUrl),
|
|
58864
61592
|
{
|
|
58865
61593
|
method: uploadMethod,
|
|
58866
61594
|
headers: { "content-type": file.contentType },
|
|
@@ -58904,13 +61632,13 @@ async function uploadCustomEmojiWithClient(args) {
|
|
|
58904
61632
|
if (uploadUrl === void 0) {
|
|
58905
61633
|
throw new Error("Linzumi custom emoji prepare response missing upload_url");
|
|
58906
61634
|
}
|
|
58907
|
-
const bytes = await
|
|
61635
|
+
const bytes = await readFile3(file.path);
|
|
58908
61636
|
const uploadBody = bytes.buffer.slice(
|
|
58909
61637
|
bytes.byteOffset,
|
|
58910
61638
|
bytes.byteOffset + bytes.byteLength
|
|
58911
61639
|
);
|
|
58912
61640
|
const response = await fetch(
|
|
58913
|
-
|
|
61641
|
+
resolveLinzumiUploadUrl2(args.kandanUrl, uploadUrl),
|
|
58914
61642
|
{
|
|
58915
61643
|
method: uploadMethod,
|
|
58916
61644
|
headers: { "content-type": file.contentType },
|
|
@@ -58966,7 +61694,14 @@ function targetWithDefaultThread(target, defaultThreadId) {
|
|
|
58966
61694
|
return target;
|
|
58967
61695
|
}
|
|
58968
61696
|
}
|
|
58969
|
-
function
|
|
61697
|
+
function paramsWithDefaultThread(params, defaultThreadId) {
|
|
61698
|
+
const existingThreadId = stringValue(params.thread_id);
|
|
61699
|
+
if (existingThreadId !== void 0 || defaultThreadId === void 0) {
|
|
61700
|
+
return params;
|
|
61701
|
+
}
|
|
61702
|
+
return { ...params, thread_id: defaultThreadId };
|
|
61703
|
+
}
|
|
61704
|
+
function resolveLinzumiUploadUrl2(kandanUrl, uploadUrl) {
|
|
58970
61705
|
try {
|
|
58971
61706
|
return new URL(uploadUrl).toString();
|
|
58972
61707
|
} catch (_error) {
|
|
@@ -59004,7 +61739,8 @@ async function runMcpConfig(args) {
|
|
|
59004
61739
|
accessToken: token,
|
|
59005
61740
|
delegationAuthFilePath: stringValue5(values, "delegation-auth-file"),
|
|
59006
61741
|
ownerUsername: stringValue5(values, "owner-username") ?? process.env.LINZUMI_MCP_OWNER_USERNAME,
|
|
59007
|
-
operatingMode
|
|
61742
|
+
operatingMode,
|
|
61743
|
+
toolScope: mcpToolScope(stringValue5(values, "tool-scope"))
|
|
59008
61744
|
});
|
|
59009
61745
|
switch (format) {
|
|
59010
61746
|
case "codex":
|
|
@@ -59132,6 +61868,17 @@ function mcpOperatingMode(value) {
|
|
|
59132
61868
|
throw new Error("--mode must be voice or text");
|
|
59133
61869
|
}
|
|
59134
61870
|
}
|
|
61871
|
+
function mcpToolScope(value) {
|
|
61872
|
+
switch (value) {
|
|
61873
|
+
case void 0:
|
|
61874
|
+
return "all";
|
|
61875
|
+
case "all":
|
|
61876
|
+
case "onboarding-discovery":
|
|
61877
|
+
return value;
|
|
61878
|
+
default:
|
|
61879
|
+
throw new Error("--tool-scope must be all or onboarding-discovery");
|
|
61880
|
+
}
|
|
61881
|
+
}
|
|
59135
61882
|
function required(values, key) {
|
|
59136
61883
|
const value = stringValue5(values, key);
|
|
59137
61884
|
if (value === void 0) {
|
|
@@ -59474,6 +62221,7 @@ function optionalObjectField(input, key) {
|
|
|
59474
62221
|
|
|
59475
62222
|
// src/index.ts
|
|
59476
62223
|
init_userFacingErrors();
|
|
62224
|
+
var onboardingDiscoveryRequiredScopes = ["local_runner.discovery.write"];
|
|
59477
62225
|
var flagDefinitions = /* @__PURE__ */ new Map([
|
|
59478
62226
|
["version", { kind: "boolean" }],
|
|
59479
62227
|
["api-url", { kind: "value" }],
|
|
@@ -59497,6 +62245,7 @@ var flagDefinitions = /* @__PURE__ */ new Map([
|
|
|
59497
62245
|
["forward-port", { kind: "value" }],
|
|
59498
62246
|
["code-server-bin", { kind: "value" }],
|
|
59499
62247
|
["fast", { kind: "boolean" }],
|
|
62248
|
+
["no-onboarding-discovery", { kind: "boolean" }],
|
|
59500
62249
|
["log-file", { kind: "value" }],
|
|
59501
62250
|
["status-dir", { kind: "value" }],
|
|
59502
62251
|
["timeout-ms", { kind: "value" }],
|
|
@@ -59566,6 +62315,7 @@ async function main(args) {
|
|
|
59566
62315
|
await runSignupFlow2({
|
|
59567
62316
|
forceSignup: options.forceSignup,
|
|
59568
62317
|
serviceUrl: options.serviceUrl,
|
|
62318
|
+
codeServerBin: options.codeServerBin,
|
|
59569
62319
|
openBrowser: false,
|
|
59570
62320
|
debugLaunchPayload: options.debugLaunchPayload,
|
|
59571
62321
|
signupServerClient: false
|
|
@@ -59584,6 +62334,7 @@ async function main(args) {
|
|
|
59584
62334
|
await runSignupFlow2({
|
|
59585
62335
|
forceSignup: options.forceSignup,
|
|
59586
62336
|
serviceUrl: options.serviceUrl,
|
|
62337
|
+
codeServerBin: options.codeServerBin,
|
|
59587
62338
|
openBrowser: options.openBrowser,
|
|
59588
62339
|
debugLaunchPayload: options.debugLaunchPayload
|
|
59589
62340
|
});
|
|
@@ -59617,8 +62368,41 @@ async function main(args) {
|
|
|
59617
62368
|
await runThreadCodexWorker(parseThreadCodexWorkerArgs(parsed.args));
|
|
59618
62369
|
return;
|
|
59619
62370
|
}
|
|
59620
|
-
const
|
|
59621
|
-
|
|
62371
|
+
const telemetryApiUrl = peekFlagValue(parsed.args, [
|
|
62372
|
+
"--api-url",
|
|
62373
|
+
"--linzumi-url"
|
|
62374
|
+
]);
|
|
62375
|
+
if (!isHelpOrVersionInvocation(parsed.args)) {
|
|
62376
|
+
void postLifecycleEvent(
|
|
62377
|
+
{
|
|
62378
|
+
name: "commander.connect.started",
|
|
62379
|
+
attributes: {
|
|
62380
|
+
...workspaceTelemetryAttribute(
|
|
62381
|
+
peekFlagValue(parsed.args, ["--workspace"])
|
|
62382
|
+
),
|
|
62383
|
+
launch_source: connectLaunchSourceTelemetryValue()
|
|
62384
|
+
}
|
|
62385
|
+
},
|
|
62386
|
+
{ apiUrl: telemetryApiUrl }
|
|
62387
|
+
);
|
|
62388
|
+
}
|
|
62389
|
+
try {
|
|
62390
|
+
const options = await parseRunnerArgs(parsed.args);
|
|
62391
|
+
await runLocalCodexRunner(withLocalMachineId(options));
|
|
62392
|
+
} catch (error) {
|
|
62393
|
+
await postLifecycleEvent(
|
|
62394
|
+
{
|
|
62395
|
+
name: "commander.error",
|
|
62396
|
+
severity: "ERROR",
|
|
62397
|
+
attributes: {
|
|
62398
|
+
...lifecycleErrorAttributes(error),
|
|
62399
|
+
launch_source: connectLaunchSourceTelemetryValue()
|
|
62400
|
+
}
|
|
62401
|
+
},
|
|
62402
|
+
{ apiUrl: telemetryApiUrl }
|
|
62403
|
+
);
|
|
62404
|
+
throw error;
|
|
62405
|
+
}
|
|
59622
62406
|
return;
|
|
59623
62407
|
}
|
|
59624
62408
|
}
|
|
@@ -59954,6 +62738,7 @@ async function parseStartRunnerArgs(args, deps = {
|
|
|
59954
62738
|
const allowedCwds = Array.from(/* @__PURE__ */ new Set([cwd, ...explicitAllowedCwds]));
|
|
59955
62739
|
const requestedCodexBin = stringValue7(values, "codex-bin") ?? "codex";
|
|
59956
62740
|
const customCodeServerBin = stringValue7(values, "code-server-bin");
|
|
62741
|
+
const onboardingDiscovery = values.get("no-onboarding-discovery") === true ? void 0 : "start";
|
|
59957
62742
|
const initialDependencyStatus = await deps.buildDependencyStatus({
|
|
59958
62743
|
cwd,
|
|
59959
62744
|
codexBin: requestedCodexBin,
|
|
@@ -59972,9 +62757,10 @@ async function parseStartRunnerArgs(args, deps = {
|
|
|
59972
62757
|
const token = await deps.resolveToken({
|
|
59973
62758
|
kandanUrl,
|
|
59974
62759
|
explicitToken,
|
|
59975
|
-
onboarding: "start",
|
|
62760
|
+
onboarding: onboardingDiscovery === "start" ? "start" : void 0,
|
|
59976
62761
|
authFilePath,
|
|
59977
62762
|
callbackHost,
|
|
62763
|
+
requiredScopes: onboardingDiscovery === "start" ? onboardingDiscoveryRequiredScopes : void 0,
|
|
59978
62764
|
reportRejectedCachedToken
|
|
59979
62765
|
});
|
|
59980
62766
|
const target = await deps.fetchStartTarget({ kandanUrl, accessToken: token });
|
|
@@ -59982,7 +62768,8 @@ async function parseStartRunnerArgs(args, deps = {
|
|
|
59982
62768
|
kandanUrl,
|
|
59983
62769
|
accessToken: token,
|
|
59984
62770
|
workspaceSlug: target.workspaceSlug,
|
|
59985
|
-
channelSlug: target.channelSlug
|
|
62771
|
+
channelSlug: target.channelSlug,
|
|
62772
|
+
requiredScopes: onboardingDiscovery === "start" ? onboardingDiscoveryRequiredScopes : void 0
|
|
59986
62773
|
});
|
|
59987
62774
|
const targetToken = tokenMatchesTarget ? token : await resolveStartTargetToken({
|
|
59988
62775
|
kandanUrl,
|
|
@@ -59990,6 +62777,7 @@ async function parseStartRunnerArgs(args, deps = {
|
|
|
59990
62777
|
target,
|
|
59991
62778
|
authFilePath,
|
|
59992
62779
|
callbackHost,
|
|
62780
|
+
requiredScopes: onboardingDiscovery === "start" ? onboardingDiscoveryRequiredScopes : void 0,
|
|
59993
62781
|
resolveToken: deps.resolveToken,
|
|
59994
62782
|
reportRejectedCachedToken
|
|
59995
62783
|
});
|
|
@@ -60031,6 +62819,7 @@ async function parseStartRunnerArgs(args, deps = {
|
|
|
60031
62819
|
dependencyStatus,
|
|
60032
62820
|
claudeCodeAvailable,
|
|
60033
62821
|
runtimeDefaults: runnerRuntimeDefaultsFromValues(values),
|
|
62822
|
+
onboardingDiscovery,
|
|
60034
62823
|
channelSession: {
|
|
60035
62824
|
workspaceSlug: target.workspaceSlug,
|
|
60036
62825
|
channelSlug: target.channelSlug,
|
|
@@ -60140,7 +62929,7 @@ async function parseAgentRunnerArgs(args, deps = {
|
|
|
60140
62929
|
};
|
|
60141
62930
|
}
|
|
60142
62931
|
function readAgentTokenTextFile(path2) {
|
|
60143
|
-
return
|
|
62932
|
+
return existsSync16(path2) ? readFileSync19(path2, "utf8") : void 0;
|
|
60144
62933
|
}
|
|
60145
62934
|
function rejectAgentRunnerTargetingFlags(values) {
|
|
60146
62935
|
const unsupportedFlags = [
|
|
@@ -60212,6 +63001,7 @@ async function resolveStartTargetToken(args) {
|
|
|
60212
63001
|
channelSlug: args.target.channelSlug,
|
|
60213
63002
|
authFilePath: args.authFilePath,
|
|
60214
63003
|
callbackHost: args.callbackHost,
|
|
63004
|
+
requiredScopes: args.requiredScopes,
|
|
60215
63005
|
reportRejectedCachedToken: args.reportRejectedCachedToken
|
|
60216
63006
|
});
|
|
60217
63007
|
}
|
|
@@ -60251,6 +63041,7 @@ async function parseRunnerArgs(args, deps = {
|
|
|
60251
63041
|
) : [...localConfiguredAllowedCwds.allowedCwds];
|
|
60252
63042
|
const requestedCodexBin = stringValue7(values, "codex-bin") ?? "codex";
|
|
60253
63043
|
const customCodeServerBin = stringValue7(values, "code-server-bin");
|
|
63044
|
+
const onboardingDiscovery = values.get("no-onboarding-discovery") === true ? void 0 : "start";
|
|
60254
63045
|
const initialDependencyStatus = await deps.buildDependencyStatus({
|
|
60255
63046
|
cwd,
|
|
60256
63047
|
codexBin: requestedCodexBin,
|
|
@@ -60265,6 +63056,7 @@ async function parseRunnerArgs(args, deps = {
|
|
|
60265
63056
|
workspaceSlug,
|
|
60266
63057
|
authFilePath: stringValue7(values, "auth-file"),
|
|
60267
63058
|
callbackHost: stringValue7(values, "oauth-callback-host"),
|
|
63059
|
+
requiredScopes: onboardingDiscovery === "start" ? onboardingDiscoveryRequiredScopes : void 0,
|
|
60268
63060
|
reportRejectedCachedToken: () => {
|
|
60269
63061
|
process.stderr.write(
|
|
60270
63062
|
"Cached Linzumi local runner auth was rejected; starting OAuth.\n"
|
|
@@ -60312,6 +63104,7 @@ async function parseRunnerArgs(args, deps = {
|
|
|
60312
63104
|
claudeCodeAvailable,
|
|
60313
63105
|
workspaceSlug: workspaceSlug ?? singleWorkspaceScopeFromAccessToken(token),
|
|
60314
63106
|
runtimeDefaults: runnerRuntimeDefaultsFromValues(values),
|
|
63107
|
+
onboardingDiscovery,
|
|
60315
63108
|
channelSession: void 0
|
|
60316
63109
|
};
|
|
60317
63110
|
}
|
|
@@ -60327,6 +63120,15 @@ function runnerRuntimeDefaultsFromValues(values) {
|
|
|
60327
63120
|
function electronAutoConnectLaunchSource() {
|
|
60328
63121
|
return process.env.LINZUMI_ELECTRON_AUTO_CONNECT_RUNNER === "1" ? "electron_auto_connect" : void 0;
|
|
60329
63122
|
}
|
|
63123
|
+
function connectLaunchSourceTelemetryValue() {
|
|
63124
|
+
return electronAutoConnectLaunchSource() ?? "cli";
|
|
63125
|
+
}
|
|
63126
|
+
function workspaceTelemetryAttribute(workspace) {
|
|
63127
|
+
return workspace === void 0 ? {} : { workspace };
|
|
63128
|
+
}
|
|
63129
|
+
function isHelpOrVersionInvocation(args) {
|
|
63130
|
+
return args.some((arg) => ["--help", "-h", "--version"].includes(arg));
|
|
63131
|
+
}
|
|
60330
63132
|
function kandanUrlValue(values) {
|
|
60331
63133
|
const apiUrl = stringValue7(values, "api-url");
|
|
60332
63134
|
const linzumiUrl = stringValue7(values, "linzumi-url");
|
|
@@ -60438,10 +63240,10 @@ function rejectStartTargetingFlags(values) {
|
|
|
60438
63240
|
}
|
|
60439
63241
|
function resolveUserPath(pathValue) {
|
|
60440
63242
|
if (pathValue === "~") {
|
|
60441
|
-
return
|
|
63243
|
+
return homedir15();
|
|
60442
63244
|
}
|
|
60443
63245
|
if (pathValue.startsWith("~/")) {
|
|
60444
|
-
return resolve10(
|
|
63246
|
+
return resolve10(homedir15(), pathValue.slice(2));
|
|
60445
63247
|
}
|
|
60446
63248
|
return resolve10(pathValue);
|
|
60447
63249
|
}
|
|
@@ -60637,7 +63439,7 @@ Codex:
|
|
|
60637
63439
|
--approval-policy <value> Approval-policy metadata shown in Linzumi
|
|
60638
63440
|
--stream-flush-ms <ms> Batch live Codex deltas before Linzumi persistence, default 150
|
|
60639
63441
|
--fast Mark this runner as low-latency/fast in the availability message
|
|
60640
|
-
--log-file <path> JSONL event log path, default ~/.linzumi/logs/runner
|
|
63442
|
+
--log-file <path> JSONL event log path, default ~/.linzumi/logs/linzumi-runner.log
|
|
60641
63443
|
--allowed-cwd <paths> Extra comma-separated roots where Linzumi may start local Codex sessions
|
|
60642
63444
|
--forward-port <ports> Comma-separated local TCP ports Linzumi may expose as authenticated previews
|
|
60643
63445
|
--code-server-bin <path> Custom development code-server executable. The default editor runtime is downloaded from Linzumi.
|
|
@@ -60719,7 +63521,7 @@ What it does:
|
|
|
60719
63521
|
Options:
|
|
60720
63522
|
--api-url <url> Linzumi API URL used to select the stored scoped runner id
|
|
60721
63523
|
--status-dir <path> Status directory, default ~/.linzumi/commanders
|
|
60722
|
-
--log-file <path> Commander log path, default
|
|
63524
|
+
--log-file <path> Commander log path, default ~/.linzumi/logs/<runner-id>.log
|
|
60723
63525
|
--timeout-ms <ms> Wait timeout, default 30000
|
|
60724
63526
|
|
|
60725
63527
|
All normal Commander options such as --agent-token-file, --allowed-cwd,
|