@linzumi/cli 0.0.77-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 +3196 -563
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -9772,7 +9772,8 @@ function linzumiMcpServerConfig(options) {
|
|
|
9772
9772
|
...options.cwd === void 0 ? [] : ["--cwd", options.cwd],
|
|
9773
9773
|
...options.threadId === void 0 ? [] : ["--thread-id", options.threadId],
|
|
9774
9774
|
"--mode",
|
|
9775
|
-
options.operatingMode ?? "text"
|
|
9775
|
+
options.operatingMode ?? "text",
|
|
9776
|
+
...options.toolScope === void 0 || options.toolScope === "all" ? [] : ["--tool-scope", options.toolScope]
|
|
9776
9777
|
],
|
|
9777
9778
|
env: {
|
|
9778
9779
|
...options.accessToken === void 0 ? {} : { LINZUMI_MCP_ACCESS_TOKEN: options.accessToken }
|
|
@@ -10725,6 +10726,9 @@ async function validateLocalRunnerToken(args) {
|
|
|
10725
10726
|
if (args.channelSlug !== void 0) {
|
|
10726
10727
|
url.searchParams.set("channel", args.channelSlug);
|
|
10727
10728
|
}
|
|
10729
|
+
for (const scope of args.requiredScopes ?? []) {
|
|
10730
|
+
url.searchParams.append("required_scope", scope);
|
|
10731
|
+
}
|
|
10728
10732
|
const response = await fetch(url, {
|
|
10729
10733
|
method: "GET",
|
|
10730
10734
|
headers: { authorization: `Bearer ${args.accessToken}` }
|
|
@@ -13577,7 +13581,7 @@ async function resolveEditorRuntime(options) {
|
|
|
13577
13581
|
fetchImpl: options.fetchImpl ?? fetch
|
|
13578
13582
|
});
|
|
13579
13583
|
if (!manifest.ok) {
|
|
13580
|
-
return unavailable(
|
|
13584
|
+
return unavailable(manifest.reason);
|
|
13581
13585
|
}
|
|
13582
13586
|
const cacheRoot = options.cacheRoot ?? defaultEditorRuntimeCacheRoot();
|
|
13583
13587
|
const installed = installedRuntime(cacheRoot, manifest.manifest);
|
|
@@ -13669,17 +13673,24 @@ async function fetchApprovedManifest(args) {
|
|
|
13669
13673
|
headers: { authorization: `Bearer ${args.token}` }
|
|
13670
13674
|
}).catch(() => void 0);
|
|
13671
13675
|
if (response === void 0) {
|
|
13672
|
-
return { ok: false };
|
|
13676
|
+
return { ok: false, reason: "manifest_unavailable" };
|
|
13673
13677
|
}
|
|
13674
13678
|
if (response.status !== 200) {
|
|
13675
|
-
return {
|
|
13679
|
+
return {
|
|
13680
|
+
ok: false,
|
|
13681
|
+
reason: response.status === 401 ? await unauthorizedManifestReason(response) : "manifest_unavailable"
|
|
13682
|
+
};
|
|
13676
13683
|
}
|
|
13677
13684
|
const body = await response.json();
|
|
13678
13685
|
if (!isJsonObject(body) || body.ok !== true || !isJsonObject(body.runtime)) {
|
|
13679
|
-
return { ok: false };
|
|
13686
|
+
return { ok: false, reason: "manifest_unavailable" };
|
|
13680
13687
|
}
|
|
13681
13688
|
return normalizeManifest(body.runtime);
|
|
13682
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
|
+
}
|
|
13683
13694
|
function normalizeManifest(value) {
|
|
13684
13695
|
const version = nonEmptyString(value.version);
|
|
13685
13696
|
const platform = nonEmptyString(value.platform);
|
|
@@ -13690,7 +13701,7 @@ function normalizeManifest(value) {
|
|
|
13690
13701
|
const manifestPath = nonEmptyString(value.manifestPath) ?? "linzumi-editor-runtime.json";
|
|
13691
13702
|
const assets = normalizeRuntimeAssets(value.assets);
|
|
13692
13703
|
if (version === void 0 || platform === void 0 || archiveUrl === void 0 || archiveSha256 === void 0 || codeServerVersion === void 0 || assets === void 0) {
|
|
13693
|
-
return { ok: false };
|
|
13704
|
+
return { ok: false, reason: "manifest_unavailable" };
|
|
13694
13705
|
}
|
|
13695
13706
|
return {
|
|
13696
13707
|
ok: true,
|
|
@@ -14264,6 +14275,20 @@ function assertRunnerConnectionDependencies(status) {
|
|
|
14264
14275
|
);
|
|
14265
14276
|
}
|
|
14266
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
|
+
}
|
|
14267
14292
|
throw new Error(
|
|
14268
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."
|
|
14269
14294
|
);
|
|
@@ -14632,7 +14657,7 @@ var linzumiCliVersion, linzumiCliVersionText;
|
|
|
14632
14657
|
var init_version = __esm({
|
|
14633
14658
|
"src/version.ts"() {
|
|
14634
14659
|
"use strict";
|
|
14635
|
-
linzumiCliVersion = "0.0.
|
|
14660
|
+
linzumiCliVersion = "0.0.78-beta";
|
|
14636
14661
|
linzumiCliVersionText = `linzumi ${linzumiCliVersion}`;
|
|
14637
14662
|
}
|
|
14638
14663
|
});
|
|
@@ -14938,6 +14963,39 @@ function updateRunnerConsoleDashboard(state, event, payload, nowMs) {
|
|
|
14938
14963
|
state.lastUpdateAtMs = nowMs;
|
|
14939
14964
|
const previousJobKey = latestDashboardJob(state)?.key;
|
|
14940
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
|
+
}
|
|
14941
14999
|
case "kandan.message_queued":
|
|
14942
15000
|
case "codex.turn_starting": {
|
|
14943
15001
|
const job = dashboardJobForPayload(state, payload, nowMs);
|
|
@@ -15006,12 +15064,15 @@ function updateRunnerConsoleDashboard(state, event, payload, nowMs) {
|
|
|
15006
15064
|
rememberRawLine(state, event, payload, previousJobKey);
|
|
15007
15065
|
}
|
|
15008
15066
|
function renderRunnerConsoleDashboard(state, nowMs) {
|
|
15009
|
-
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) {
|
|
15010
15068
|
return void 0;
|
|
15011
15069
|
}
|
|
15012
15070
|
const jobs = dashboardJobs(state);
|
|
15013
15071
|
switch (state.mode.type) {
|
|
15014
15072
|
case "table":
|
|
15073
|
+
if (state.jobs.size === 0 && state.discovery.size > 0) {
|
|
15074
|
+
return renderSignupDiscoveryWelcome(state, nowMs);
|
|
15075
|
+
}
|
|
15015
15076
|
return renderDashboardTable(state, jobs, nowMs);
|
|
15016
15077
|
case "raw_all":
|
|
15017
15078
|
return renderRawDashboard(
|
|
@@ -15037,9 +15098,195 @@ function renderRunnerConsoleDashboard(state, nowMs) {
|
|
|
15037
15098
|
}
|
|
15038
15099
|
}
|
|
15039
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
|
+
}
|
|
15040
15285
|
function renderDashboardTable(state, jobs, nowMs) {
|
|
15041
15286
|
const model = dashboardTableModel(state, jobs, nowMs);
|
|
15042
15287
|
return [
|
|
15288
|
+
runnerWelcomeHeader(),
|
|
15289
|
+
"",
|
|
15043
15290
|
"Linzumi Commander",
|
|
15044
15291
|
"",
|
|
15045
15292
|
`Jobs: ${jobs.length} Last update: ${timeAgo(state.lastUpdateAtMs, nowMs)}`,
|
|
@@ -15052,6 +15299,9 @@ function renderDashboardTable(state, jobs, nowMs) {
|
|
|
15052
15299
|
""
|
|
15053
15300
|
].join("\n");
|
|
15054
15301
|
}
|
|
15302
|
+
function runnerWelcomeHeader() {
|
|
15303
|
+
return runnerWelcomeHeaderLines.join("\n");
|
|
15304
|
+
}
|
|
15055
15305
|
function creditExhaustionBanner(summary) {
|
|
15056
15306
|
if (summary === void 0) {
|
|
15057
15307
|
return [];
|
|
@@ -15060,7 +15310,12 @@ function creditExhaustionBanner(summary) {
|
|
|
15060
15310
|
}
|
|
15061
15311
|
function dashboardTableModel(state, jobs, nowMs) {
|
|
15062
15312
|
const selectedJobKey = selectedDashboardJobKey(state, jobs);
|
|
15063
|
-
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()] : [
|
|
15064
15319
|
dashboardTableHeader(),
|
|
15065
15320
|
...jobs.map((job) => jobRow(job, selectedJobKey, nowMs))
|
|
15066
15321
|
];
|
|
@@ -15070,6 +15325,91 @@ function dashboardTableModel(state, jobs, nowMs) {
|
|
|
15070
15325
|
rows
|
|
15071
15326
|
};
|
|
15072
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
|
+
}
|
|
15073
15413
|
function dashboardTableHeader() {
|
|
15074
15414
|
return [
|
|
15075
15415
|
"",
|
|
@@ -15114,6 +15454,10 @@ function formatRunnerConsoleEvent(event, payload) {
|
|
|
15114
15454
|
return ignoredMessage(payload);
|
|
15115
15455
|
case "kandan.message_queued":
|
|
15116
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);
|
|
15117
15461
|
case "kandan.chat_event_failed":
|
|
15118
15462
|
return `Incoming message handling failed: seq=${text(payload.seq)} reason=${text(payload.message)}`;
|
|
15119
15463
|
case "kandan.reconnected":
|
|
@@ -15153,6 +15497,20 @@ function formatRunnerConsoleEvent(event, payload) {
|
|
|
15153
15497
|
return void 0;
|
|
15154
15498
|
}
|
|
15155
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
|
+
}
|
|
15156
15514
|
function connectedRunnerMessage(payload) {
|
|
15157
15515
|
return [
|
|
15158
15516
|
"Connected to Linzumi",
|
|
@@ -15440,11 +15798,11 @@ function createRunnerConsoleDashboardTui(state, exitProcess = () => process.kill
|
|
|
15440
15798
|
top: 0,
|
|
15441
15799
|
left: 0,
|
|
15442
15800
|
width: "100%",
|
|
15443
|
-
height:
|
|
15801
|
+
height: 1,
|
|
15444
15802
|
tags: false
|
|
15445
15803
|
});
|
|
15446
15804
|
const tableElement = blessed.listtable({
|
|
15447
|
-
top:
|
|
15805
|
+
top: 1,
|
|
15448
15806
|
left: 0,
|
|
15449
15807
|
width: "100%",
|
|
15450
15808
|
bottom: 1,
|
|
@@ -15566,7 +15924,15 @@ function createRunnerConsoleDashboardTui(state, exitProcess = () => process.kill
|
|
|
15566
15924
|
switch (state.mode.type) {
|
|
15567
15925
|
case "table": {
|
|
15568
15926
|
const model = dashboardTableModel(state, dashboardJobs(state), nowMs);
|
|
15569
|
-
|
|
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);
|
|
15570
15936
|
header.show();
|
|
15571
15937
|
tableElement.setData(model.rows);
|
|
15572
15938
|
tableElement.show();
|
|
@@ -15617,6 +15983,9 @@ function createRunnerConsoleDashboardTui(state, exitProcess = () => process.kill
|
|
|
15617
15983
|
}
|
|
15618
15984
|
return { render, destroy };
|
|
15619
15985
|
}
|
|
15986
|
+
function tuiScreenHeight(screen) {
|
|
15987
|
+
return typeof screen.height === "number" ? screen.height : 24;
|
|
15988
|
+
}
|
|
15620
15989
|
function syncTuiTableSelection(tableElement, model) {
|
|
15621
15990
|
const selectedIndex = model.jobs.findIndex(
|
|
15622
15991
|
(job) => job.key === model.selectedJobKey
|
|
@@ -15624,8 +15993,46 @@ function syncTuiTableSelection(tableElement, model) {
|
|
|
15624
15993
|
const tableIndex = selectedIndex < 0 ? 1 : selectedIndex + 1;
|
|
15625
15994
|
tableElement.select(tableIndex);
|
|
15626
15995
|
}
|
|
15627
|
-
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
|
+
}
|
|
15628
16033
|
return [
|
|
16034
|
+
welcomeHeader,
|
|
16035
|
+
"",
|
|
15629
16036
|
"Linzumi Commander",
|
|
15630
16037
|
`Jobs: ${jobCount} Last update: ${timeAgo(state.lastUpdateAtMs, nowMs)}`,
|
|
15631
16038
|
...creditExhaustionBanner(state.creditExhaustion),
|
|
@@ -15634,6 +16041,20 @@ function tuiHeaderContent(state, jobCount, nowMs) {
|
|
|
15634
16041
|
"Controls: click row/enter raw job | r raw stream | esc/back table | mouse wheel scroll"
|
|
15635
16042
|
].join("\n");
|
|
15636
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
|
+
}
|
|
15637
16058
|
function updateRunnerConsoleDashboardMode(state, key) {
|
|
15638
16059
|
switch (key) {
|
|
15639
16060
|
case "r":
|
|
@@ -15887,12 +16308,13 @@ function stringValue4(value) {
|
|
|
15887
16308
|
function numberValue(value) {
|
|
15888
16309
|
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
15889
16310
|
}
|
|
15890
|
-
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;
|
|
15891
16312
|
var init_runnerConsoleReporter = __esm({
|
|
15892
16313
|
"src/runnerConsoleReporter.ts"() {
|
|
15893
16314
|
"use strict";
|
|
15894
16315
|
dashboardState = {
|
|
15895
16316
|
jobs: /* @__PURE__ */ new Map(),
|
|
16317
|
+
discovery: /* @__PURE__ */ new Map(),
|
|
15896
16318
|
rawLines: [],
|
|
15897
16319
|
nextJobOrdinal: 0,
|
|
15898
16320
|
mode: { type: "table" },
|
|
@@ -15900,7 +16322,10 @@ var init_runnerConsoleReporter = __esm({
|
|
|
15900
16322
|
tokenUsage: void 0,
|
|
15901
16323
|
rateLimit: void 0,
|
|
15902
16324
|
creditExhaustion: void 0,
|
|
15903
|
-
lastUpdateAtMs: void 0
|
|
16325
|
+
lastUpdateAtMs: void 0,
|
|
16326
|
+
browserUrl: void 0,
|
|
16327
|
+
workspace: void 0,
|
|
16328
|
+
runnerId: void 0
|
|
15904
16329
|
};
|
|
15905
16330
|
maxRawLines = 500;
|
|
15906
16331
|
escapeKey = "\x1B";
|
|
@@ -15919,6 +16344,37 @@ var init_runnerConsoleReporter = __esm({
|
|
|
15919
16344
|
{ width: 12 },
|
|
15920
16345
|
{ width: 24 }
|
|
15921
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;
|
|
15922
16378
|
redrawScreen = (screen) => {
|
|
15923
16379
|
process.stdout.write(`\x1B[2J\x1B[H${screen}`);
|
|
15924
16380
|
};
|
|
@@ -15931,18 +16387,1052 @@ var init_runnerConsoleReporter = __esm({
|
|
|
15931
16387
|
}
|
|
15932
16388
|
});
|
|
15933
16389
|
|
|
15934
|
-
// src/
|
|
15935
|
-
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";
|
|
15936
16642
|
import { homedir as homedir9 } from "node:os";
|
|
15937
|
-
|
|
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";
|
|
15938
17428
|
function defaultAuthFilePath() {
|
|
15939
|
-
return
|
|
17429
|
+
return join15(homedir11(), ".linzumi", "auth.json");
|
|
15940
17430
|
}
|
|
15941
17431
|
function readCachedLocalRunnerToken(kandanUrl, authFilePath = defaultAuthFilePath()) {
|
|
15942
|
-
if (!
|
|
17432
|
+
if (!existsSync10(authFilePath)) {
|
|
15943
17433
|
return void 0;
|
|
15944
17434
|
}
|
|
15945
|
-
const authFile = parseAuthFile(
|
|
17435
|
+
const authFile = parseAuthFile(readFileSync12(authFilePath, "utf8"));
|
|
15946
17436
|
const kandanBaseUrl = kandanHttpBaseUrl(kandanUrl);
|
|
15947
17437
|
const entry = authFile.local_codex_runner?.[kandanBaseUrl];
|
|
15948
17438
|
if (entry === void 0 || entry.access_token.trim() === "") {
|
|
@@ -15959,12 +17449,12 @@ function readCachedLocalRunnerToken(kandanUrl, authFilePath = defaultAuthFilePat
|
|
|
15959
17449
|
};
|
|
15960
17450
|
}
|
|
15961
17451
|
function readPersonalAgentDelegationToken(authFilePath) {
|
|
15962
|
-
if (!
|
|
17452
|
+
if (!existsSync10(authFilePath)) {
|
|
15963
17453
|
throw new Error(
|
|
15964
17454
|
`missing personal-agent delegation auth file: ${authFilePath}`
|
|
15965
17455
|
);
|
|
15966
17456
|
}
|
|
15967
|
-
const authFile = parseAuthFile(
|
|
17457
|
+
const authFile = parseAuthFile(readFileSync12(authFilePath, "utf8"));
|
|
15968
17458
|
const entry = authFile.personal_agent_delegation;
|
|
15969
17459
|
if (entry === void 0 || entry.access_token.trim() === "") {
|
|
15970
17460
|
throw new Error(
|
|
@@ -15982,7 +17472,7 @@ function readPersonalAgentDelegationToken(authFilePath) {
|
|
|
15982
17472
|
}
|
|
15983
17473
|
function writeCachedLocalRunnerToken(args) {
|
|
15984
17474
|
const authFilePath = args.authFilePath ?? defaultAuthFilePath();
|
|
15985
|
-
const existing =
|
|
17475
|
+
const existing = existsSync10(authFilePath) ? parseAuthFile(readFileSync12(authFilePath, "utf8")) : { version: 1 };
|
|
15986
17476
|
const kandanBaseUrl = kandanHttpBaseUrl(args.kandanUrl);
|
|
15987
17477
|
const issuedAt = /* @__PURE__ */ new Date();
|
|
15988
17478
|
const expiresAt = args.expiresInSeconds === void 0 ? void 0 : new Date(
|
|
@@ -16000,8 +17490,8 @@ function writeCachedLocalRunnerToken(args) {
|
|
|
16000
17490
|
}
|
|
16001
17491
|
}
|
|
16002
17492
|
};
|
|
16003
|
-
|
|
16004
|
-
|
|
17493
|
+
mkdirSync9(dirname10(authFilePath), { recursive: true });
|
|
17494
|
+
writeFileSync7(authFilePath, `${JSON.stringify(next, null, 2)}
|
|
16005
17495
|
`, "utf8");
|
|
16006
17496
|
return {
|
|
16007
17497
|
accessToken: args.accessToken,
|
|
@@ -16419,9 +17909,9 @@ var init_threadCodexWorkerIpc = __esm({
|
|
|
16419
17909
|
|
|
16420
17910
|
// src/signupTaskSuggestions.ts
|
|
16421
17911
|
import { spawn as spawn6 } from "node:child_process";
|
|
16422
|
-
import { mkdtempSync as mkdtempSync3, readFileSync as
|
|
17912
|
+
import { mkdtempSync as mkdtempSync3, readFileSync as readFileSync13, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "node:fs";
|
|
16423
17913
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
16424
|
-
import { join as
|
|
17914
|
+
import { join as join16 } from "node:path";
|
|
16425
17915
|
async function suggestSignupTasksWithCodex(args) {
|
|
16426
17916
|
const attempts = 2;
|
|
16427
17917
|
let previousResponse;
|
|
@@ -16443,11 +17933,11 @@ async function suggestSignupTasksWithCodex(args) {
|
|
|
16443
17933
|
);
|
|
16444
17934
|
}
|
|
16445
17935
|
async function runCodexTaskSuggestion(args) {
|
|
16446
|
-
const tempRoot = mkdtempSync3(
|
|
16447
|
-
const schemaPath =
|
|
16448
|
-
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");
|
|
16449
17939
|
const prompt = taskSuggestionPrompt(args.previousResponse);
|
|
16450
|
-
|
|
17940
|
+
writeFileSync8(
|
|
16451
17941
|
schemaPath,
|
|
16452
17942
|
`${JSON.stringify(taskSuggestionJsonSchema(), null, 2)}
|
|
16453
17943
|
`,
|
|
@@ -16464,7 +17954,7 @@ async function runCodexTaskSuggestion(args) {
|
|
|
16464
17954
|
prompt
|
|
16465
17955
|
})
|
|
16466
17956
|
);
|
|
16467
|
-
return
|
|
17957
|
+
return readFileSync13(outputPath, "utf8");
|
|
16468
17958
|
} finally {
|
|
16469
17959
|
rmSync3(tempRoot, { recursive: true, force: true });
|
|
16470
17960
|
}
|
|
@@ -16513,6 +18003,14 @@ function taskSuggestionPrompt(previousResponse) {
|
|
|
16513
18003
|
"Task:",
|
|
16514
18004
|
"Inspect this repository read-only and suggest exactly 5 useful quick starter tasks for Codex agents.",
|
|
16515
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
|
+
"",
|
|
16516
18014
|
"Constraints:",
|
|
16517
18015
|
"- Prefer tasks that are small, concrete, and likely to produce useful first results.",
|
|
16518
18016
|
"- Do not modify files.",
|
|
@@ -16563,7 +18061,9 @@ function parseTaskSuggestionResponse(response) {
|
|
|
16563
18061
|
const titles = tasks.map(
|
|
16564
18062
|
(task) => typeof task === "object" && task !== null && !Array.isArray(task) ? task.title : void 0
|
|
16565
18063
|
);
|
|
16566
|
-
if (!titles.every(
|
|
18064
|
+
if (!titles.every(
|
|
18065
|
+
(title) => typeof title === "string" && title.trim() !== "" && title.trim().length <= 120
|
|
18066
|
+
)) {
|
|
16567
18067
|
return void 0;
|
|
16568
18068
|
}
|
|
16569
18069
|
return titles.map((title, index) => ({
|
|
@@ -16626,8 +18126,8 @@ var init_signupTaskSuggestions = __esm({
|
|
|
16626
18126
|
|
|
16627
18127
|
// src/remoteCodexSandboxRunner.ts
|
|
16628
18128
|
import { spawn as spawn7 } from "node:child_process";
|
|
16629
|
-
import { existsSync as
|
|
16630
|
-
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";
|
|
16631
18131
|
function createConfiguredRemoteCodexSandboxRunner(args) {
|
|
16632
18132
|
const kind = normalizedSandboxKind(args.env.LINZUMI_REMOTE_CODEX_SANDBOX);
|
|
16633
18133
|
if (kind === void 0) {
|
|
@@ -16646,7 +18146,7 @@ function createConfiguredRemoteCodexSandboxRunner(args) {
|
|
|
16646
18146
|
}
|
|
16647
18147
|
break;
|
|
16648
18148
|
}
|
|
16649
|
-
if (!
|
|
18149
|
+
if (!existsSync11(sandboxBin)) {
|
|
16650
18150
|
throw new Error(`remote Codex sandbox binary not found: ${sandboxBin}`);
|
|
16651
18151
|
}
|
|
16652
18152
|
return createRemoteCodexSandboxRunner({
|
|
@@ -16657,7 +18157,7 @@ function createConfiguredRemoteCodexSandboxRunner(args) {
|
|
|
16657
18157
|
function createRemoteCodexSandboxRunner(config, deps = {}) {
|
|
16658
18158
|
const resolvedDeps = {
|
|
16659
18159
|
platform: deps.platform ?? process.platform,
|
|
16660
|
-
exists: deps.exists ??
|
|
18160
|
+
exists: deps.exists ?? existsSync11,
|
|
16661
18161
|
spawnProcess: deps.spawnProcess ?? spawn7
|
|
16662
18162
|
};
|
|
16663
18163
|
return (request) => runSandboxInvocation(
|
|
@@ -16740,10 +18240,10 @@ function bubblewrapArgs(sandboxBin, request, exists) {
|
|
|
16740
18240
|
key,
|
|
16741
18241
|
value
|
|
16742
18242
|
]);
|
|
16743
|
-
const readOnlyBindArgs =
|
|
18243
|
+
const readOnlyBindArgs = uniqueStrings3([
|
|
16744
18244
|
...linuxReadOnlyRoots,
|
|
16745
|
-
...isAbsolute3(request.command) ? [
|
|
16746
|
-
|
|
18245
|
+
...isAbsolute3(request.command) ? [dirname11(request.command)] : [],
|
|
18246
|
+
dirname11(sandboxBin)
|
|
16747
18247
|
]).flatMap((path2) => exists(path2) ? ["--ro-bind", path2, path2] : []);
|
|
16748
18248
|
const cwdParentDirs = parentDirs(request.cwd).flatMap((path2) => [
|
|
16749
18249
|
"--dir",
|
|
@@ -16774,9 +18274,9 @@ function bubblewrapArgs(sandboxBin, request, exists) {
|
|
|
16774
18274
|
];
|
|
16775
18275
|
}
|
|
16776
18276
|
function macosSeatbeltProfile(request, exists) {
|
|
16777
|
-
const readableRoots =
|
|
18277
|
+
const readableRoots = uniqueStrings3([
|
|
16778
18278
|
...macosReadOnlyRoots,
|
|
16779
|
-
...isAbsolute3(request.command) ? [
|
|
18279
|
+
...isAbsolute3(request.command) ? [dirname11(request.command)] : []
|
|
16780
18280
|
]).filter(exists);
|
|
16781
18281
|
const readableRules = readableRoots.map((path2) => `(allow file-read* (subpath ${sandboxString(path2)}))`).join("\n");
|
|
16782
18282
|
const writableTempRules = macosWritableTempRoots.filter(exists).flatMap((path2) => [
|
|
@@ -16858,11 +18358,11 @@ function parentDirs(path2) {
|
|
|
16858
18358
|
(_part, index) => `/${parents.slice(0, index + 1).join("/")}`
|
|
16859
18359
|
);
|
|
16860
18360
|
}
|
|
16861
|
-
function
|
|
18361
|
+
function uniqueStrings3(values) {
|
|
16862
18362
|
return Array.from(new Set(values));
|
|
16863
18363
|
}
|
|
16864
18364
|
function existingPathAliases(path2) {
|
|
16865
|
-
return
|
|
18365
|
+
return uniqueStrings3([path2, realpathSync5(path2)]);
|
|
16866
18366
|
}
|
|
16867
18367
|
function sandboxString(value) {
|
|
16868
18368
|
return JSON.stringify(value);
|
|
@@ -16898,21 +18398,29 @@ var init_remoteCodexSandboxRunner = __esm({
|
|
|
16898
18398
|
});
|
|
16899
18399
|
|
|
16900
18400
|
// src/runner.ts
|
|
16901
|
-
import { spawn as spawn8, spawnSync as
|
|
16902
|
-
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";
|
|
16903
18403
|
import {
|
|
16904
18404
|
chmodSync as chmodSync2,
|
|
16905
18405
|
lstatSync,
|
|
16906
|
-
mkdirSync as
|
|
18406
|
+
mkdirSync as mkdirSync10,
|
|
16907
18407
|
mkdtempSync as mkdtempSync4,
|
|
16908
|
-
readdirSync as
|
|
18408
|
+
readdirSync as readdirSync4,
|
|
16909
18409
|
realpathSync as realpathSync6,
|
|
16910
18410
|
rmSync as rmSync4,
|
|
16911
|
-
statSync
|
|
18411
|
+
statSync as statSync3
|
|
16912
18412
|
} from "node:fs";
|
|
18413
|
+
import { readFile as readFile2 } from "node:fs/promises";
|
|
16913
18414
|
import { createServer as createServer3 } from "node:http";
|
|
16914
18415
|
import { hostname as hostname2, tmpdir as tmpdir3 } from "node:os";
|
|
16915
|
-
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";
|
|
16916
18424
|
async function runLocalCodexRunner(options) {
|
|
16917
18425
|
const log = makeRunnerLogger(options);
|
|
16918
18426
|
const cleanup = {
|
|
@@ -17172,7 +18680,7 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
|
|
|
17172
18680
|
});
|
|
17173
18681
|
}
|
|
17174
18682
|
}
|
|
17175
|
-
const instanceId = `codex-${
|
|
18683
|
+
const instanceId = `codex-${randomUUID4()}`;
|
|
17176
18684
|
const publishLocalEditorStatus = (payload) => {
|
|
17177
18685
|
void kandan.push(topic, "local_editor_status", payload).catch((error) => {
|
|
17178
18686
|
log("kandan.local_editor_status_push_failed", {
|
|
@@ -17247,7 +18755,7 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
|
|
|
17247
18755
|
};
|
|
17248
18756
|
const approveEditorPortForwardCandidate = (candidate) => {
|
|
17249
18757
|
const request = pendingRequestFromCandidate({
|
|
17250
|
-
requestId: `editor-port-forward-auto-${
|
|
18758
|
+
requestId: `editor-port-forward-auto-${randomUUID4()}`,
|
|
17251
18759
|
sourceSeq: 0,
|
|
17252
18760
|
candidate
|
|
17253
18761
|
});
|
|
@@ -17475,7 +18983,7 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
|
|
|
17475
18983
|
return;
|
|
17476
18984
|
}
|
|
17477
18985
|
const request = pendingRequestFromCandidate({
|
|
17478
|
-
requestId: `claude-port-forward-auto-${
|
|
18986
|
+
requestId: `claude-port-forward-auto-${randomUUID4()}`,
|
|
17479
18987
|
sourceSeq,
|
|
17480
18988
|
candidate
|
|
17481
18989
|
});
|
|
@@ -17888,6 +19396,21 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
|
|
|
17888
19396
|
codexUrl: codexUrl ?? null,
|
|
17889
19397
|
replacedRunners
|
|
17890
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
|
+
);
|
|
17891
19414
|
const channelSession = options.channelSession === void 0 ? void 0 : await attachChannelSession({
|
|
17892
19415
|
kandan,
|
|
17893
19416
|
codex,
|
|
@@ -18517,6 +20040,17 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
|
|
|
18517
20040
|
void pushHeartbeat();
|
|
18518
20041
|
return;
|
|
18519
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
|
+
}
|
|
18520
20054
|
void resolveSessionControl(channelSession, dynamicChannelSessions, control).then((handled) => {
|
|
18521
20055
|
if (handled !== void 0) {
|
|
18522
20056
|
return handled;
|
|
@@ -18553,9 +20087,19 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
|
|
|
18553
20087
|
});
|
|
18554
20088
|
});
|
|
18555
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) {
|
|
18556
20100
|
controlDispatcher.value = handleControl;
|
|
18557
20101
|
pendingControls.splice(0).forEach(handleControl);
|
|
18558
|
-
|
|
20102
|
+
startOnboardingDiscoveryAgents(options, log);
|
|
18559
20103
|
}
|
|
18560
20104
|
function controlTargetsInstance(control, instanceId) {
|
|
18561
20105
|
switch (control.type) {
|
|
@@ -19697,22 +21241,6 @@ ${args.developerPrompt}
|
|
|
19697
21241
|
`;
|
|
19698
21242
|
const linzumiContext = args.linzumiContext === void 0 ? "" : `
|
|
19699
21243
|
${formatLinzumiConversationContextForPrompt(args.linzumiContext)}
|
|
19700
|
-
`;
|
|
19701
|
-
const reviewGuidance = `
|
|
19702
|
-
<linzumi_pr_review_guidance>
|
|
19703
|
-
For frontend or other user-visible changes, ask whether the user wants
|
|
19704
|
-
screenshot or screen-recording proof, ideally before/after when that helps
|
|
19705
|
-
review the change. Screen recordings should be WebM or MP4. When the user asks
|
|
19706
|
-
you to open or update a PR, attach or link that proof in the PR when feasible,
|
|
19707
|
-
and state the exact blocker when it is not feasible.
|
|
19708
|
-
</linzumi_pr_review_guidance>
|
|
19709
|
-
|
|
19710
|
-
<linzumi_user_visible_writing_guide>
|
|
19711
|
-
When you write user-visible UI, status, error, or PR-review copy, keep it short,
|
|
19712
|
-
direct, and focused on the user's outcome. Avoid implementation-heavy sentences,
|
|
19713
|
-
stacked clauses, and internal mechanics unless the user explicitly needs that
|
|
19714
|
-
detail.
|
|
19715
|
-
</linzumi_user_visible_writing_guide>
|
|
19716
21244
|
`;
|
|
19717
21245
|
return `<context>
|
|
19718
21246
|
You are a Linzumi ${agentLabel} session launched by the Linzumi Commander.
|
|
@@ -19731,6 +21259,15 @@ Linzumi.
|
|
|
19731
21259
|
Linzumi ${agentLabel} session: You, the inner ${agentLabel} process that performs the actual
|
|
19732
21260
|
work in the approved project folder.
|
|
19733
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.
|
|
19734
21271
|
</term_definitions>
|
|
19735
21272
|
|
|
19736
21273
|
<linzumi_mcp>
|
|
@@ -19740,24 +21277,53 @@ read a message by ID/URL, read a thread by ID, read the scoped channel, or send
|
|
|
19740
21277
|
a concise DM to the Commander owner when the task genuinely requires it.
|
|
19741
21278
|
</linzumi_mcp>
|
|
19742
21279
|
|
|
19743
|
-
<coding_job_metadata>
|
|
19744
|
-
When you are working inside a coding-job thread, keep the job metadata current through the Linzumi MCP tools.
|
|
19745
|
-
At the beginning of a job, call linzumi_upsert_coding_job_plan with your current understanding of the goal, then call linzumi_replace_coding_job_plan_steps with an ordered list of steps to accomplish that goal.
|
|
19746
|
-
The plan is allowed to change. Rework it when the old plan is no longer a useful description of the work, but do not churn it for minor tactical adjustments.
|
|
19747
|
-
As you make progress, call linzumi_update_coding_job_plan_step to mark the active step and completed steps. Read the metadata first when you need current step ids or lock versions.
|
|
19748
|
-
When you open, discover, or choose the primary GitHub pull request for the job, call linzumi_link_coding_job_pull_request with the PR URL, repo owner/name, PR number, title, head/base branches, head/base SHAs, local worktree path, remote URL, and current dirty status. This link is what lets GitHub PR comments, files, commits, checks, deployments, and reviews appear automatically in the metadata overview.
|
|
19749
|
-
The plan and goal are user-visible, so keep titles short, descriptions concrete, and completion notes focused on what was verified.
|
|
19750
|
-
</coding_job_metadata>
|
|
19751
|
-
|
|
19752
21280
|
<task_instructions>
|
|
19753
21281
|
Work only in the approved project folder unless the human explicitly asks for
|
|
19754
21282
|
something else in the Linzumi thread. Start, inspect, and modify the local app
|
|
19755
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.
|
|
19756
21303
|
</task_instructions>
|
|
19757
21304
|
|
|
19758
21305
|
<rules>
|
|
19759
21306
|
You MUST treat the Linzumi thread as the source of truth for user-facing
|
|
19760
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.
|
|
19761
21327
|
You MUST keep user-visible preview servers bound to 0.0.0.0, not 127.0.0.1 or
|
|
19762
21328
|
localhost, so the Linzumi secure tunnel can reach them.
|
|
19763
21329
|
You MUST keep any preview or dev server as your descendant process so the
|
|
@@ -19774,16 +21340,51 @@ revert another session's work.
|
|
|
19774
21340
|
|
|
19775
21341
|
<examples>
|
|
19776
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
|
+
|
|
19777
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.
|
|
19778
21372
|
</examples>
|
|
19779
21373
|
${linzumiContext}
|
|
19780
|
-
${reviewGuidance}
|
|
19781
21374
|
${customPrompt}
|
|
19782
21375
|
<task_reminder>
|
|
19783
21376
|
You are the Commander-launched Linzumi ${agentLabel} session. Do the implementation
|
|
19784
21377
|
work in the approved project folder, keep preview servers reachable through the
|
|
19785
|
-
secure tunnel, and keep the Linzumi thread truthful.
|
|
19786
|
-
</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>`;
|
|
19787
21388
|
}
|
|
19788
21389
|
function availableRunnerAgentProviders(options) {
|
|
19789
21390
|
switch (options.claudeCodeRunner !== void 0 || options.claudeCodeAvailable === true) {
|
|
@@ -21580,6 +23181,869 @@ function redactedThreadRunnerCliArgs(args) {
|
|
|
21580
23181
|
function optionalCliValue(flag, value) {
|
|
21581
23182
|
return value === void 0 || value === "" ? [] : [flag, value];
|
|
21582
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
|
+
}
|
|
21583
24047
|
async function startOwnedCodexAppServer(options, args = { linzumiMcp: true }) {
|
|
21584
24048
|
ensureCodexProjectTrusted(options.cwd);
|
|
21585
24049
|
const defaults = runnerRuntimeDefaults(options);
|
|
@@ -21636,9 +24100,9 @@ function mcpOwnerUsername(options) {
|
|
|
21636
24100
|
return options.channelSession?.listenUser ?? identityFromAccessToken(options.token).actorUsername;
|
|
21637
24101
|
}
|
|
21638
24102
|
function writeEphemeralMcpAuthFile(options) {
|
|
21639
|
-
const directory = mkdtempSync4(
|
|
24103
|
+
const directory = mkdtempSync4(join17(tmpdir3(), "linzumi-mcp-auth-"));
|
|
21640
24104
|
chmodSync2(directory, 448);
|
|
21641
|
-
const path2 =
|
|
24105
|
+
const path2 = join17(directory, "auth.json");
|
|
21642
24106
|
writeCachedLocalRunnerToken({
|
|
21643
24107
|
kandanUrl: options.kandanUrl,
|
|
21644
24108
|
accessToken: options.token,
|
|
@@ -21714,7 +24178,7 @@ function configuredAllowedCwds(values, options = {}) {
|
|
|
21714
24178
|
const absolutePath = resolve7(expandUserPath(value));
|
|
21715
24179
|
try {
|
|
21716
24180
|
if (options.createMissing === true) {
|
|
21717
|
-
|
|
24181
|
+
mkdirSync10(absolutePath, { recursive: true });
|
|
21718
24182
|
}
|
|
21719
24183
|
const realPath = realpathSync6(absolutePath);
|
|
21720
24184
|
allowedCwds.push(
|
|
@@ -21751,9 +24215,9 @@ function allowedCwdProjects(allowedCwds) {
|
|
|
21751
24215
|
});
|
|
21752
24216
|
}
|
|
21753
24217
|
function isGitProjectDirectory(cwd) {
|
|
21754
|
-
const gitPath =
|
|
24218
|
+
const gitPath = join17(cwd, ".git");
|
|
21755
24219
|
try {
|
|
21756
|
-
const gitPathStats =
|
|
24220
|
+
const gitPathStats = statSync3(gitPath);
|
|
21757
24221
|
return gitPathStats.isDirectory() || gitPathStats.isFile();
|
|
21758
24222
|
} catch {
|
|
21759
24223
|
return false;
|
|
@@ -21764,7 +24228,7 @@ function browseRunnerDirectory(control, options) {
|
|
|
21764
24228
|
const requestedPath = stringValue(control.path) ?? currentHomeDirectory();
|
|
21765
24229
|
try {
|
|
21766
24230
|
const currentPath = realpathSync6(resolve7(expandUserPath(requestedPath)));
|
|
21767
|
-
const stats =
|
|
24231
|
+
const stats = statSync3(currentPath);
|
|
21768
24232
|
if (!stats.isDirectory()) {
|
|
21769
24233
|
return {
|
|
21770
24234
|
instanceId: options.runnerId,
|
|
@@ -21774,9 +24238,9 @@ function browseRunnerDirectory(control, options) {
|
|
|
21774
24238
|
error: "not_directory"
|
|
21775
24239
|
};
|
|
21776
24240
|
}
|
|
21777
|
-
const parent =
|
|
21778
|
-
const entries =
|
|
21779
|
-
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);
|
|
21780
24244
|
return {
|
|
21781
24245
|
name: entry.name,
|
|
21782
24246
|
path: path2,
|
|
@@ -21817,7 +24281,7 @@ function projectDirectoryName(name) {
|
|
|
21817
24281
|
function availableProjectDirectoryName(projectsRoot, baseName, suffix = 0) {
|
|
21818
24282
|
for (let nextSuffix = suffix; ; nextSuffix += 1) {
|
|
21819
24283
|
const candidate = nextSuffix === 0 ? baseName : `${baseName}-${nextSuffix}`;
|
|
21820
|
-
if (!projectPathExists(
|
|
24284
|
+
if (!projectPathExists(join17(projectsRoot, candidate))) {
|
|
21821
24285
|
return candidate;
|
|
21822
24286
|
}
|
|
21823
24287
|
}
|
|
@@ -21845,9 +24309,9 @@ function createRunnerProject(control, options, allowedCwds) {
|
|
|
21845
24309
|
error: "invalid_project_template"
|
|
21846
24310
|
};
|
|
21847
24311
|
}
|
|
21848
|
-
const projectsRoot =
|
|
24312
|
+
const projectsRoot = join17(currentHomeDirectory(), "linzumi");
|
|
21849
24313
|
const resolvedProjectDirName = template === "hello_linzumi_demo" ? availableProjectDirectoryName(projectsRoot, projectDirName) : projectDirName;
|
|
21850
|
-
const projectPath =
|
|
24314
|
+
const projectPath = join17(projectsRoot, resolvedProjectDirName);
|
|
21851
24315
|
let createdProjectPath = false;
|
|
21852
24316
|
try {
|
|
21853
24317
|
if (template !== "hello_linzumi_demo" && projectPathExists(projectPath)) {
|
|
@@ -21859,7 +24323,7 @@ function createRunnerProject(control, options, allowedCwds) {
|
|
|
21859
24323
|
error: "project_directory_exists"
|
|
21860
24324
|
};
|
|
21861
24325
|
}
|
|
21862
|
-
|
|
24326
|
+
mkdirSync10(projectsRoot, { recursive: true });
|
|
21863
24327
|
if (template === "hello_linzumi_demo") {
|
|
21864
24328
|
createdProjectPath = true;
|
|
21865
24329
|
createHelloLinzumiProject({
|
|
@@ -21867,10 +24331,10 @@ function createRunnerProject(control, options, allowedCwds) {
|
|
|
21867
24331
|
name: resolvedProjectDirName
|
|
21868
24332
|
});
|
|
21869
24333
|
} else {
|
|
21870
|
-
|
|
24334
|
+
mkdirSync10(projectPath, { recursive: false });
|
|
21871
24335
|
createdProjectPath = true;
|
|
21872
24336
|
}
|
|
21873
|
-
const git =
|
|
24337
|
+
const git = spawnSync5("git", ["init"], {
|
|
21874
24338
|
cwd: projectPath,
|
|
21875
24339
|
encoding: "utf8",
|
|
21876
24340
|
env: process.env
|
|
@@ -21978,7 +24442,7 @@ async function suggestRunnerTasks(control, options, allowedCwds) {
|
|
|
21978
24442
|
};
|
|
21979
24443
|
}
|
|
21980
24444
|
}
|
|
21981
|
-
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;
|
|
21982
24446
|
var init_runner = __esm({
|
|
21983
24447
|
"src/runner.ts"() {
|
|
21984
24448
|
"use strict";
|
|
@@ -22009,13 +24473,20 @@ var init_runner = __esm({
|
|
|
22009
24473
|
init_runnerConsoleReporter();
|
|
22010
24474
|
init_streamDeltaQueue();
|
|
22011
24475
|
init_version();
|
|
24476
|
+
init_telemetry();
|
|
22012
24477
|
init_mcpConfig();
|
|
24478
|
+
init_linzumiApiClient();
|
|
24479
|
+
init_onboardingConversationDiscovery();
|
|
24480
|
+
init_onboardingProjectDiscovery();
|
|
22013
24481
|
init_authCache();
|
|
22014
24482
|
init_threadCodexWorkerIpc();
|
|
22015
24483
|
init_signupTaskSuggestions();
|
|
22016
24484
|
init_userFacingErrors();
|
|
22017
24485
|
init_remoteCodexSandboxRunner();
|
|
22018
24486
|
THREAD_RUNNER_READY_TIMEOUT_MS = 3e4;
|
|
24487
|
+
onboardingDiscoveryAgentStartKeys = /* @__PURE__ */ new Set();
|
|
24488
|
+
onboardingConversationImportKeys = /* @__PURE__ */ new Set();
|
|
24489
|
+
onboardingConversationDiscoveryReportLimit = 100;
|
|
22019
24490
|
claudeSessionStoreSequenceHighWater = /* @__PURE__ */ new Map();
|
|
22020
24491
|
ClaudeCodeOutputPostError = class extends Error {
|
|
22021
24492
|
constructor(cause) {
|
|
@@ -22024,11 +24495,17 @@ var init_runner = __esm({
|
|
|
22024
24495
|
this.name = "ClaudeCodeOutputPostError";
|
|
22025
24496
|
}
|
|
22026
24497
|
};
|
|
24498
|
+
onboardingConversationTitleFirstMessages = 4;
|
|
24499
|
+
onboardingConversationTitleLastMessages = 4;
|
|
24500
|
+
onboardingConversationTitleBodyMaxLength = 1200;
|
|
24501
|
+
onboardingConversationImportMessageMaxLength = 15500;
|
|
24502
|
+
onboardingConversationImportCandidateLimit = 500;
|
|
24503
|
+
onboardingConversationImportProgressInterval = 25;
|
|
22027
24504
|
}
|
|
22028
24505
|
});
|
|
22029
24506
|
|
|
22030
24507
|
// src/kandanTls.ts
|
|
22031
|
-
import { existsSync as
|
|
24508
|
+
import { existsSync as existsSync12, readFileSync as readFileSync14 } from "node:fs";
|
|
22032
24509
|
import { Agent } from "undici";
|
|
22033
24510
|
import WsWebSocket from "ws";
|
|
22034
24511
|
function kandanTlsTrustFromEnv() {
|
|
@@ -22039,10 +24516,10 @@ function kandanTlsTrustFromCaFile(caFile) {
|
|
|
22039
24516
|
return void 0;
|
|
22040
24517
|
}
|
|
22041
24518
|
const trimmed = caFile.trim();
|
|
22042
|
-
if (!
|
|
24519
|
+
if (!existsSync12(trimmed)) {
|
|
22043
24520
|
throw new Error(`KANDAN_TLS_CA_FILE does not exist: ${trimmed}`);
|
|
22044
24521
|
}
|
|
22045
|
-
const ca =
|
|
24522
|
+
const ca = readFileSync14(trimmed, "utf8");
|
|
22046
24523
|
return {
|
|
22047
24524
|
caFile: trimmed,
|
|
22048
24525
|
ca,
|
|
@@ -40825,11 +43302,11 @@ var init_RemoveFileError = __esm({
|
|
|
40825
43302
|
});
|
|
40826
43303
|
|
|
40827
43304
|
// ../../node_modules/@inquirer/external-editor/dist/esm/index.js
|
|
40828
|
-
import { spawn as spawn10, spawnSync as
|
|
40829
|
-
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";
|
|
40830
43307
|
import path from "node:path";
|
|
40831
43308
|
import os from "node:os";
|
|
40832
|
-
import { randomUUID as
|
|
43309
|
+
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
40833
43310
|
function editAsync(text2 = "", callback, fileOptions) {
|
|
40834
43311
|
const editor = new ExternalEditor(text2, fileOptions);
|
|
40835
43312
|
editor.runAsync((err, result) => {
|
|
@@ -40929,7 +43406,7 @@ var init_esm5 = __esm({
|
|
|
40929
43406
|
createTemporaryFile() {
|
|
40930
43407
|
try {
|
|
40931
43408
|
const baseDir = this.fileOptions.dir ?? os.tmpdir();
|
|
40932
|
-
const id =
|
|
43409
|
+
const id = randomUUID5();
|
|
40933
43410
|
const prefix = sanitizeAffix(this.fileOptions.prefix);
|
|
40934
43411
|
const postfix = sanitizeAffix(this.fileOptions.postfix);
|
|
40935
43412
|
const filename = `${prefix}${id}${postfix}`;
|
|
@@ -40943,14 +43420,14 @@ var init_esm5 = __esm({
|
|
|
40943
43420
|
if (Object.prototype.hasOwnProperty.call(this.fileOptions, "mode")) {
|
|
40944
43421
|
opt.mode = this.fileOptions.mode;
|
|
40945
43422
|
}
|
|
40946
|
-
|
|
43423
|
+
writeFileSync11(this.tempFile, this.text, opt);
|
|
40947
43424
|
} catch (createFileError) {
|
|
40948
43425
|
throw new CreateFileError(createFileError);
|
|
40949
43426
|
}
|
|
40950
43427
|
}
|
|
40951
43428
|
readTemporaryFile() {
|
|
40952
43429
|
try {
|
|
40953
|
-
const tempFileBuffer =
|
|
43430
|
+
const tempFileBuffer = readFileSync17(this.tempFile);
|
|
40954
43431
|
if (tempFileBuffer.length === 0) {
|
|
40955
43432
|
this.text = "";
|
|
40956
43433
|
} else {
|
|
@@ -40973,7 +43450,7 @@ var init_esm5 = __esm({
|
|
|
40973
43450
|
}
|
|
40974
43451
|
launchEditor() {
|
|
40975
43452
|
try {
|
|
40976
|
-
const editorProcess =
|
|
43453
|
+
const editorProcess = spawnSync6(this.editor.bin, this.editor.args.concat([this.tempFile]), { stdio: "inherit" });
|
|
40977
43454
|
this.lastExitStatus = editorProcess.status ?? 0;
|
|
40978
43455
|
} catch (launchError) {
|
|
40979
43456
|
throw new LaunchEditorError(launchError);
|
|
@@ -42315,21 +44792,21 @@ __export(signupFlow_exports, {
|
|
|
42315
44792
|
signupTaskSuggestionWaitMessageForTest: () => signupTaskSuggestionWaitMessageForTest,
|
|
42316
44793
|
toggleProjectPickerSelectionForTest: () => toggleProjectPickerSelectionForTest
|
|
42317
44794
|
});
|
|
42318
|
-
import { spawn as spawn11, spawnSync as
|
|
44795
|
+
import { spawn as spawn11, spawnSync as spawnSync7 } from "node:child_process";
|
|
42319
44796
|
import {
|
|
42320
|
-
existsSync as
|
|
44797
|
+
existsSync as existsSync15,
|
|
42321
44798
|
constants as fsConstants,
|
|
42322
|
-
mkdirSync as
|
|
44799
|
+
mkdirSync as mkdirSync13,
|
|
42323
44800
|
mkdtempSync as mkdtempSync5,
|
|
42324
|
-
readFileSync as
|
|
42325
|
-
readdirSync as
|
|
44801
|
+
readFileSync as readFileSync18,
|
|
44802
|
+
readdirSync as readdirSync5,
|
|
42326
44803
|
rmSync as rmSync5,
|
|
42327
|
-
statSync as
|
|
42328
|
-
writeFileSync as
|
|
44804
|
+
statSync as statSync4,
|
|
44805
|
+
writeFileSync as writeFileSync12
|
|
42329
44806
|
} from "node:fs";
|
|
42330
44807
|
import { access } from "node:fs/promises";
|
|
42331
|
-
import { homedir as
|
|
42332
|
-
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";
|
|
42333
44810
|
import { stdin as defaultStdin, stdout as defaultStdout } from "node:process";
|
|
42334
44811
|
import { emitKeypressEvents } from "node:readline";
|
|
42335
44812
|
function signupHelpText() {
|
|
@@ -42445,12 +44922,12 @@ function defaultSignupDraftStore(serviceUrl) {
|
|
|
42445
44922
|
const path2 = defaultSignupDraftPath(serviceUrl);
|
|
42446
44923
|
return {
|
|
42447
44924
|
read: () => {
|
|
42448
|
-
if (!
|
|
44925
|
+
if (!existsSync15(path2)) {
|
|
42449
44926
|
return void 0;
|
|
42450
44927
|
}
|
|
42451
44928
|
let parsed;
|
|
42452
44929
|
try {
|
|
42453
|
-
parsed = JSON.parse(
|
|
44930
|
+
parsed = JSON.parse(readFileSync18(path2, "utf8"));
|
|
42454
44931
|
} catch (_error) {
|
|
42455
44932
|
return void 0;
|
|
42456
44933
|
}
|
|
@@ -42460,8 +44937,8 @@ function defaultSignupDraftStore(serviceUrl) {
|
|
|
42460
44937
|
return comparableServiceUrl(parsed.serviceUrl) === comparableServiceUrl(serviceUrl) ? parsed : void 0;
|
|
42461
44938
|
},
|
|
42462
44939
|
write: (draft) => {
|
|
42463
|
-
|
|
42464
|
-
|
|
44940
|
+
mkdirSync13(dirname15(path2), { recursive: true });
|
|
44941
|
+
writeFileSync12(path2, `${JSON.stringify(draft, null, 2)}
|
|
42465
44942
|
`, {
|
|
42466
44943
|
encoding: "utf8",
|
|
42467
44944
|
mode: 384
|
|
@@ -42473,8 +44950,8 @@ function defaultSignupDraftStore(serviceUrl) {
|
|
|
42473
44950
|
};
|
|
42474
44951
|
}
|
|
42475
44952
|
function defaultSignupDraftPath(serviceUrl) {
|
|
42476
|
-
return
|
|
42477
|
-
|
|
44953
|
+
return join20(
|
|
44954
|
+
dirname15(localConfigPath()),
|
|
42478
44955
|
`signup-draft.${localConfigScopeFileStem(serviceUrl)}.json`
|
|
42479
44956
|
);
|
|
42480
44957
|
}
|
|
@@ -42508,8 +44985,8 @@ function defaultSignupTaskCacheStore(serviceUrl) {
|
|
|
42508
44985
|
}
|
|
42509
44986
|
}
|
|
42510
44987
|
};
|
|
42511
|
-
|
|
42512
|
-
|
|
44988
|
+
mkdirSync13(dirname15(path2), { recursive: true });
|
|
44989
|
+
writeFileSync12(path2, `${JSON.stringify(next, null, 2)}
|
|
42513
44990
|
`, {
|
|
42514
44991
|
encoding: "utf8",
|
|
42515
44992
|
mode: 384
|
|
@@ -42518,18 +44995,18 @@ function defaultSignupTaskCacheStore(serviceUrl) {
|
|
|
42518
44995
|
};
|
|
42519
44996
|
}
|
|
42520
44997
|
function defaultSignupTaskCachePath(serviceUrl) {
|
|
42521
|
-
return
|
|
42522
|
-
|
|
44998
|
+
return join20(
|
|
44999
|
+
dirname15(localConfigPath()),
|
|
42523
45000
|
`signup-task-cache.${localConfigScopeFileStem(serviceUrl)}.json`
|
|
42524
45001
|
);
|
|
42525
45002
|
}
|
|
42526
45003
|
function readSignupTaskCache(path2) {
|
|
42527
|
-
if (!
|
|
45004
|
+
if (!existsSync15(path2)) {
|
|
42528
45005
|
return { version: 1, entries: {} };
|
|
42529
45006
|
}
|
|
42530
45007
|
let parsed;
|
|
42531
45008
|
try {
|
|
42532
|
-
parsed = JSON.parse(
|
|
45009
|
+
parsed = JSON.parse(readFileSync18(path2, "utf8"));
|
|
42533
45010
|
} catch (_error) {
|
|
42534
45011
|
return { version: 1, entries: {} };
|
|
42535
45012
|
}
|
|
@@ -43715,6 +46192,7 @@ async function signupCommanderRunnerOptions(args, runnerId) {
|
|
|
43715
46192
|
editorRuntime: editorRuntime.runtime,
|
|
43716
46193
|
socketFactory: trustedWebSocketFactory(trust),
|
|
43717
46194
|
dependencyStatus,
|
|
46195
|
+
onboardingDiscovery: "start",
|
|
43718
46196
|
runtimeDefaults: {
|
|
43719
46197
|
model: void 0,
|
|
43720
46198
|
reasoningEffort: void 0,
|
|
@@ -44413,7 +46891,7 @@ function autocompletePathSuggestions(pathInput) {
|
|
|
44413
46891
|
try {
|
|
44414
46892
|
const showHidden = prefix.startsWith(".");
|
|
44415
46893
|
const currentPath = directoryExists(normalizedInput) ? [normalizedInput.replace(/\/$/, "") || "/"] : [];
|
|
44416
|
-
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) => ({
|
|
44417
46895
|
path: path2,
|
|
44418
46896
|
score: fuzzyPathSegmentScore(path2.split("/").at(-1) ?? path2, prefix)
|
|
44419
46897
|
})).filter((candidate) => candidate.score !== void 0).sort((left, right) => {
|
|
@@ -44659,7 +47137,7 @@ async function runSignupPreflights(runtime) {
|
|
|
44659
47137
|
function defaultPreflightRuntime() {
|
|
44660
47138
|
return {
|
|
44661
47139
|
cwd: process.cwd(),
|
|
44662
|
-
homeDir:
|
|
47140
|
+
homeDir: homedir14(),
|
|
44663
47141
|
probeTool,
|
|
44664
47142
|
readGitEmail,
|
|
44665
47143
|
discoverCodeRoots,
|
|
@@ -44804,13 +47282,13 @@ async function suggestTasksWithCodex(projectPath, retryPolicy) {
|
|
|
44804
47282
|
);
|
|
44805
47283
|
}
|
|
44806
47284
|
async function runCodexTaskSuggestion2(projectPath, previousResponse, retryPolicy) {
|
|
44807
|
-
const tempRoot = mkdtempSync5(
|
|
44808
|
-
const schemaPath =
|
|
44809
|
-
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");
|
|
44810
47288
|
const model = process.env.LINZUMI_SIGNUP_TASK_MODEL ?? "gpt-5.4-mini";
|
|
44811
47289
|
const prompt = taskSuggestionPrompt2(previousResponse);
|
|
44812
47290
|
const codexCommand = await resolveSignupCodexCommand();
|
|
44813
|
-
|
|
47291
|
+
writeFileSync12(
|
|
44814
47292
|
schemaPath,
|
|
44815
47293
|
`${JSON.stringify(taskSuggestionJsonSchema2(), null, 2)}
|
|
44816
47294
|
`,
|
|
@@ -44831,7 +47309,7 @@ async function runCodexTaskSuggestion2(projectPath, previousResponse, retryPolic
|
|
|
44831
47309
|
),
|
|
44832
47310
|
retryPolicy
|
|
44833
47311
|
);
|
|
44834
|
-
return
|
|
47312
|
+
return readFileSync18(outputPath, "utf8");
|
|
44835
47313
|
} finally {
|
|
44836
47314
|
rmSync5(tempRoot, { recursive: true, force: true });
|
|
44837
47315
|
}
|
|
@@ -44868,7 +47346,7 @@ function codexTaskSuggestionProcess2(args) {
|
|
|
44868
47346
|
function signupCodexTaskSuggestionProcessForTest(args) {
|
|
44869
47347
|
return codexTaskSuggestionProcess2(args);
|
|
44870
47348
|
}
|
|
44871
|
-
async function resolveSignupCodexCommand(env = process.env, homeDir =
|
|
47349
|
+
async function resolveSignupCodexCommand(env = process.env, homeDir = homedir14(), executableExists = fileIsExecutable) {
|
|
44872
47350
|
const override = firstConfiguredValue([
|
|
44873
47351
|
env.LINZUMI_SIGNUP_CODEX_BIN,
|
|
44874
47352
|
env.LINZUMI_CODEX_BIN
|
|
@@ -44923,7 +47401,7 @@ function resolveHomePath(path2, homeDir) {
|
|
|
44923
47401
|
return homeDir;
|
|
44924
47402
|
}
|
|
44925
47403
|
if (path2.startsWith("~/")) {
|
|
44926
|
-
return
|
|
47404
|
+
return join20(homeDir, path2.slice(2));
|
|
44927
47405
|
}
|
|
44928
47406
|
return resolve9(path2);
|
|
44929
47407
|
}
|
|
@@ -44936,9 +47414,9 @@ function commandLooksPathLike(command) {
|
|
|
44936
47414
|
}
|
|
44937
47415
|
function homeManagedCodexCandidates(homeDir) {
|
|
44938
47416
|
return [
|
|
44939
|
-
|
|
44940
|
-
|
|
44941
|
-
|
|
47417
|
+
join20(homeDir, ".volta", "bin", "codex"),
|
|
47418
|
+
join20(homeDir, ".local", "bin", "codex"),
|
|
47419
|
+
join20(homeDir, "bin", "codex")
|
|
44942
47420
|
];
|
|
44943
47421
|
}
|
|
44944
47422
|
async function firstExecutablePath(paths, executableExists) {
|
|
@@ -44953,7 +47431,7 @@ function commandPathCandidates(path2) {
|
|
|
44953
47431
|
if (path2 === void 0 || path2.trim() === "") {
|
|
44954
47432
|
return [];
|
|
44955
47433
|
}
|
|
44956
|
-
return path2.split(delimiter3).filter((entry) => entry.trim() !== "").map((entry) =>
|
|
47434
|
+
return path2.split(delimiter3).filter((entry) => entry.trim() !== "").map((entry) => join20(entry, "codex"));
|
|
44957
47435
|
}
|
|
44958
47436
|
async function fileIsExecutable(path2) {
|
|
44959
47437
|
try {
|
|
@@ -44978,6 +47456,14 @@ function taskSuggestionPrompt2(previousResponse) {
|
|
|
44978
47456
|
"Task:",
|
|
44979
47457
|
"Inspect this repository read-only and suggest exactly 5 useful quick starter tasks for Codex agents.",
|
|
44980
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
|
+
"",
|
|
44981
47467
|
"Constraints:",
|
|
44982
47468
|
"- Prefer tasks that are small, concrete, and likely to produce useful first results.",
|
|
44983
47469
|
"- Do not modify files.",
|
|
@@ -45178,11 +47664,11 @@ function codexPreflightLocation(command, homeDir) {
|
|
|
45178
47664
|
switch (command) {
|
|
45179
47665
|
case "codex":
|
|
45180
47666
|
return "on PATH";
|
|
45181
|
-
case
|
|
47667
|
+
case join20(homeDir, ".volta", "bin", "codex"):
|
|
45182
47668
|
return "via Volta";
|
|
45183
|
-
case
|
|
47669
|
+
case join20(homeDir, ".local", "bin", "codex"):
|
|
45184
47670
|
return "via ~/.local/bin";
|
|
45185
|
-
case
|
|
47671
|
+
case join20(homeDir, "bin", "codex"):
|
|
45186
47672
|
return "via ~/bin";
|
|
45187
47673
|
default:
|
|
45188
47674
|
return "from configured path";
|
|
@@ -45287,7 +47773,7 @@ function spawnSyncGit(args, cwd) {
|
|
|
45287
47773
|
}
|
|
45288
47774
|
function spawnSyncGitOutput(args, cwd) {
|
|
45289
47775
|
try {
|
|
45290
|
-
const result =
|
|
47776
|
+
const result = spawnSync7("git", [...args], {
|
|
45291
47777
|
cwd,
|
|
45292
47778
|
stdio: ["ignore", "pipe", "ignore"]
|
|
45293
47779
|
});
|
|
@@ -45337,13 +47823,13 @@ function probeToolWithArgs(command, args, cwd) {
|
|
|
45337
47823
|
}
|
|
45338
47824
|
async function discoverCodeRoots(homeDir) {
|
|
45339
47825
|
const candidates = ["src", "code", "projects"].map(
|
|
45340
|
-
(name) =>
|
|
47826
|
+
(name) => join20(homeDir, name)
|
|
45341
47827
|
);
|
|
45342
|
-
return candidates.filter((path2) =>
|
|
47828
|
+
return candidates.filter((path2) => existsSync15(path2)).flatMap((path2) => discoveredProjectNames(path2));
|
|
45343
47829
|
}
|
|
45344
47830
|
function discoveredProjectNames(root) {
|
|
45345
47831
|
try {
|
|
45346
|
-
return
|
|
47832
|
+
return readdirSync5(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).slice(0, 3).map((entry) => `~/${root.split("/").at(-1)}/${entry.name}`);
|
|
45347
47833
|
} catch {
|
|
45348
47834
|
return [];
|
|
45349
47835
|
}
|
|
@@ -45391,7 +47877,7 @@ function discoverProjectsFromCurrentDirectory(cwd) {
|
|
|
45391
47877
|
}
|
|
45392
47878
|
function discoverProjectsFromGuessedRoots(homeDir) {
|
|
45393
47879
|
const guessedRoots = ["src", "code", "projects"].map(
|
|
45394
|
-
(name) =>
|
|
47880
|
+
(name) => join20(homeDir, name)
|
|
45395
47881
|
);
|
|
45396
47882
|
const projects = guessedRoots.flatMap(
|
|
45397
47883
|
(root) => discoverProjectsUnderRoot(root)
|
|
@@ -45443,25 +47929,25 @@ function looksLikeProject(path2) {
|
|
|
45443
47929
|
"pnpm-lock.yaml",
|
|
45444
47930
|
"yarn.lock",
|
|
45445
47931
|
"package-lock.json"
|
|
45446
|
-
].some((name) =>
|
|
47932
|
+
].some((name) => existsSync15(join20(path2, name)));
|
|
45447
47933
|
}
|
|
45448
47934
|
function detectProjectLanguage(path2) {
|
|
45449
|
-
if (
|
|
47935
|
+
if (existsSync15(join20(path2, "pyproject.toml")) || existsSync15(join20(path2, "requirements.txt"))) {
|
|
45450
47936
|
return "Python";
|
|
45451
47937
|
}
|
|
45452
|
-
if (
|
|
47938
|
+
if (existsSync15(join20(path2, "Cargo.toml"))) {
|
|
45453
47939
|
return "Rust";
|
|
45454
47940
|
}
|
|
45455
|
-
if (
|
|
47941
|
+
if (existsSync15(join20(path2, "go.mod"))) {
|
|
45456
47942
|
return "Go";
|
|
45457
47943
|
}
|
|
45458
|
-
if (
|
|
47944
|
+
if (existsSync15(join20(path2, "mix.exs"))) {
|
|
45459
47945
|
return "Elixir";
|
|
45460
47946
|
}
|
|
45461
|
-
if (
|
|
47947
|
+
if (existsSync15(join20(path2, "tsconfig.json")) || packageJsonMentionsTypeScript(path2)) {
|
|
45462
47948
|
return "TypeScript";
|
|
45463
47949
|
}
|
|
45464
|
-
if (
|
|
47950
|
+
if (existsSync15(join20(path2, "package.json"))) {
|
|
45465
47951
|
return "JavaScript";
|
|
45466
47952
|
}
|
|
45467
47953
|
return "Project";
|
|
@@ -45469,7 +47955,7 @@ function detectProjectLanguage(path2) {
|
|
|
45469
47955
|
function packageJsonMentionsTypeScript(path2) {
|
|
45470
47956
|
try {
|
|
45471
47957
|
const packageJson2 = JSON.parse(
|
|
45472
|
-
|
|
47958
|
+
readFileSync18(join20(path2, "package.json"), "utf8")
|
|
45473
47959
|
);
|
|
45474
47960
|
return packageJson2.dependencies?.typescript !== void 0 || packageJson2.devDependencies?.typescript !== void 0;
|
|
45475
47961
|
} catch {
|
|
@@ -45477,11 +47963,11 @@ function packageJsonMentionsTypeScript(path2) {
|
|
|
45477
47963
|
}
|
|
45478
47964
|
}
|
|
45479
47965
|
function hasGitMetadata(path2) {
|
|
45480
|
-
return
|
|
47966
|
+
return existsSync15(join20(path2, ".git"));
|
|
45481
47967
|
}
|
|
45482
47968
|
function childDirectories(root) {
|
|
45483
47969
|
try {
|
|
45484
|
-
return
|
|
47970
|
+
return readdirSync5(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join20(root, entry.name));
|
|
45485
47971
|
} catch {
|
|
45486
47972
|
return [];
|
|
45487
47973
|
}
|
|
@@ -45500,17 +47986,17 @@ function ignoredProjectDirectory(path2) {
|
|
|
45500
47986
|
}
|
|
45501
47987
|
function directoryExists(path2) {
|
|
45502
47988
|
try {
|
|
45503
|
-
return
|
|
47989
|
+
return statSync4(path2).isDirectory();
|
|
45504
47990
|
} catch {
|
|
45505
47991
|
return false;
|
|
45506
47992
|
}
|
|
45507
47993
|
}
|
|
45508
47994
|
function expandHomePath(path2) {
|
|
45509
47995
|
if (path2 === "~") {
|
|
45510
|
-
return
|
|
47996
|
+
return homedir14();
|
|
45511
47997
|
}
|
|
45512
47998
|
if (path2.startsWith("~/")) {
|
|
45513
|
-
return
|
|
47999
|
+
return join20(homedir14(), path2.slice(2));
|
|
45514
48000
|
}
|
|
45515
48001
|
return resolve9(path2);
|
|
45516
48002
|
}
|
|
@@ -45599,8 +48085,8 @@ secure mission control for all your agents on your computers
|
|
|
45599
48085
|
init_runner();
|
|
45600
48086
|
init_claudeCodeSession();
|
|
45601
48087
|
init_authCache();
|
|
45602
|
-
import { existsSync as
|
|
45603
|
-
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";
|
|
45604
48090
|
import { resolve as resolve10 } from "node:path";
|
|
45605
48091
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
45606
48092
|
|
|
@@ -45621,7 +48107,8 @@ async function resolveLocalRunnerToken(args, deps = {
|
|
|
45621
48107
|
kandanUrl: args.kandanUrl,
|
|
45622
48108
|
accessToken: cached.accessToken,
|
|
45623
48109
|
workspaceSlug: args.workspaceSlug,
|
|
45624
|
-
channelSlug: args.channelSlug
|
|
48110
|
+
channelSlug: args.channelSlug,
|
|
48111
|
+
requiredScopes: args.requiredScopes
|
|
45625
48112
|
});
|
|
45626
48113
|
if (cachedTokenIsUsable) {
|
|
45627
48114
|
return cached.accessToken;
|
|
@@ -45661,9 +48148,9 @@ init_kandanTls();
|
|
|
45661
48148
|
init_protocol();
|
|
45662
48149
|
init_json();
|
|
45663
48150
|
init_defaultUrls();
|
|
45664
|
-
import { existsSync as
|
|
45665
|
-
import { dirname as
|
|
45666
|
-
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";
|
|
45667
48154
|
async function runAgentCliCommand(args, deps = {
|
|
45668
48155
|
fetchImpl: fetch,
|
|
45669
48156
|
stdout: process.stdout,
|
|
@@ -46371,7 +48858,7 @@ function agentTokenFile(flags) {
|
|
|
46371
48858
|
return flags.get("agent-token-file") ?? defaultAgentTokenFilePath();
|
|
46372
48859
|
}
|
|
46373
48860
|
function defaultAgentTokenFilePath() {
|
|
46374
|
-
return
|
|
48861
|
+
return join18(homedir12(), ".linzumi", "agent-token.json");
|
|
46375
48862
|
}
|
|
46376
48863
|
function normalizedApiUrl(apiUrl) {
|
|
46377
48864
|
return apiUrl.endsWith("/") ? apiUrl : `${apiUrl}/`;
|
|
@@ -46380,11 +48867,11 @@ function authorizationHeaders(token) {
|
|
|
46380
48867
|
return { authorization: `Bearer ${token}` };
|
|
46381
48868
|
}
|
|
46382
48869
|
function readOptionalTextFile(path2) {
|
|
46383
|
-
return
|
|
48870
|
+
return existsSync13(path2) ? readFileSync15(path2, "utf8") : void 0;
|
|
46384
48871
|
}
|
|
46385
48872
|
function writeTextFile(path2, content) {
|
|
46386
|
-
|
|
46387
|
-
|
|
48873
|
+
mkdirSync11(dirname13(path2), { recursive: true });
|
|
48874
|
+
writeFileSync9(path2, content);
|
|
46388
48875
|
}
|
|
46389
48876
|
function readStoredAgentTokenFile(path2, readTextFile = readOptionalTextFile) {
|
|
46390
48877
|
const content = readTextFile(path2);
|
|
@@ -46471,27 +48958,27 @@ init_helloLinzumiProject();
|
|
|
46471
48958
|
// src/commanderDaemon.ts
|
|
46472
48959
|
init_runnerLogger();
|
|
46473
48960
|
import {
|
|
46474
|
-
existsSync as
|
|
46475
|
-
closeSync as
|
|
46476
|
-
mkdirSync as
|
|
46477
|
-
openSync as
|
|
46478
|
-
readFileSync as
|
|
48961
|
+
existsSync as existsSync14,
|
|
48962
|
+
closeSync as closeSync3,
|
|
48963
|
+
mkdirSync as mkdirSync12,
|
|
48964
|
+
openSync as openSync4,
|
|
48965
|
+
readFileSync as readFileSync16,
|
|
46479
48966
|
watch,
|
|
46480
|
-
writeFileSync as
|
|
48967
|
+
writeFileSync as writeFileSync10
|
|
46481
48968
|
} from "node:fs";
|
|
46482
|
-
import { homedir as
|
|
46483
|
-
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";
|
|
46484
48971
|
import { execFileSync, spawn as spawn9 } from "node:child_process";
|
|
46485
48972
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
46486
48973
|
var connectedMarkers = ["Connected to Linzumi", "Runner connected:"];
|
|
46487
48974
|
function commanderStatusDir() {
|
|
46488
|
-
return
|
|
48975
|
+
return join19(homedir13(), ".linzumi", "commanders");
|
|
46489
48976
|
}
|
|
46490
48977
|
function commanderStatusFile(runnerId, statusDir = commanderStatusDir()) {
|
|
46491
|
-
return
|
|
48978
|
+
return join19(statusDir, `${safeRunnerId(runnerId)}.json`);
|
|
46492
48979
|
}
|
|
46493
48980
|
function defaultCommanderLogFile(runnerId) {
|
|
46494
|
-
return
|
|
48981
|
+
return join19(homedir13(), ".linzumi", "logs", `${safeRunnerId(runnerId)}.log`);
|
|
46495
48982
|
}
|
|
46496
48983
|
function commanderLogIsConnected(log) {
|
|
46497
48984
|
return connectedMarkers.some((marker) => log.includes(marker));
|
|
@@ -46512,10 +48999,10 @@ function startCommanderDaemon(options) {
|
|
|
46512
48999
|
"--log-file",
|
|
46513
49000
|
logFile
|
|
46514
49001
|
];
|
|
46515
|
-
|
|
46516
|
-
|
|
46517
|
-
const out =
|
|
46518
|
-
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");
|
|
46519
49006
|
writeCliAuditEvent(
|
|
46520
49007
|
"process.spawn",
|
|
46521
49008
|
{
|
|
@@ -46542,8 +49029,8 @@ function startCommanderDaemon(options) {
|
|
|
46542
49029
|
},
|
|
46543
49030
|
{ sessionId: options.runnerId }
|
|
46544
49031
|
);
|
|
46545
|
-
|
|
46546
|
-
|
|
49032
|
+
closeSync3(out);
|
|
49033
|
+
closeSync3(err);
|
|
46547
49034
|
child.unref();
|
|
46548
49035
|
if (child.pid === void 0) {
|
|
46549
49036
|
throw new Error("commander daemon did not report a pid");
|
|
@@ -46559,21 +49046,21 @@ function startCommanderDaemon(options) {
|
|
|
46559
49046
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
46560
49047
|
command: [nodeBin, ...command]
|
|
46561
49048
|
};
|
|
46562
|
-
|
|
49049
|
+
writeFileSync10(statusFile, `${JSON.stringify(record, null, 2)}
|
|
46563
49050
|
`);
|
|
46564
49051
|
return record;
|
|
46565
49052
|
}
|
|
46566
49053
|
function commanderDaemonStatus(runnerId, statusDir = commanderStatusDir(), processIdentityReader = readProcessIdentity) {
|
|
46567
49054
|
const statusFile = commanderStatusFile(runnerId, statusDir);
|
|
46568
|
-
if (!
|
|
49055
|
+
if (!existsSync14(statusFile)) {
|
|
46569
49056
|
return { status: "missing", runnerId, statusFile };
|
|
46570
49057
|
}
|
|
46571
|
-
const record = parseRecord(
|
|
49058
|
+
const record = parseRecord(readFileSync16(statusFile, "utf8"));
|
|
46572
49059
|
return processIsRunning(record.pid) && processMatchesRecord(record, processIdentityReader) ? { status: "running", record } : { status: "stopped", record };
|
|
46573
49060
|
}
|
|
46574
49061
|
async function waitForCommanderDaemon(options) {
|
|
46575
49062
|
const now = options.now ?? (() => Date.now());
|
|
46576
|
-
const readTextFile = options.readTextFile ?? ((path2) =>
|
|
49063
|
+
const readTextFile = options.readTextFile ?? ((path2) => existsSync14(path2) ? readFileSync16(path2, "utf8") : void 0);
|
|
46577
49064
|
const statusImpl = options.statusImpl ?? commanderDaemonStatus;
|
|
46578
49065
|
const deadline = now() + options.timeoutMs;
|
|
46579
49066
|
while (now() <= deadline) {
|
|
@@ -46772,6 +49259,7 @@ async function waitForFileChangeOrTimeout(path2, deadline, now, ready2 = () => f
|
|
|
46772
49259
|
|
|
46773
49260
|
// src/index.ts
|
|
46774
49261
|
init_version();
|
|
49262
|
+
init_telemetry();
|
|
46775
49263
|
|
|
46776
49264
|
// ../../node_modules/zod/v3/external.js
|
|
46777
49265
|
var external_exports = {};
|
|
@@ -54437,7 +56925,7 @@ var StdioServerTransport = class {
|
|
|
54437
56925
|
};
|
|
54438
56926
|
|
|
54439
56927
|
// src/mcpServer.ts
|
|
54440
|
-
import { readFile as
|
|
56928
|
+
import { readFile as readFile3 } from "node:fs/promises";
|
|
54441
56929
|
|
|
54442
56930
|
// node_modules/zod/v3/external.js
|
|
54443
56931
|
var external_exports2 = {};
|
|
@@ -58484,78 +60972,19 @@ var NEVER2 = INVALID2;
|
|
|
58484
60972
|
init_authCache();
|
|
58485
60973
|
init_commanderAttachments();
|
|
58486
60974
|
init_json();
|
|
58487
|
-
|
|
58488
|
-
// src/linzumiApiClient.ts
|
|
58489
|
-
init_oauth();
|
|
58490
|
-
init_protocol();
|
|
58491
|
-
function createLinzumiMcpApiClient(options) {
|
|
58492
|
-
const fetchImpl = options.fetchImpl ?? fetch;
|
|
58493
|
-
const baseUrl = kandanHttpBaseUrl(options.kandanUrl);
|
|
58494
|
-
const apiPrefix = options.authMode === "personal-agent-delegation" ? "/api/v2/personal-agent-mcp" : "/api/v2/local-runner-mcp";
|
|
58495
|
-
const operatingMode = options.operatingMode ?? "text";
|
|
58496
|
-
const request = async (method, path2, params) => {
|
|
58497
|
-
const url = new URL(path2, baseUrl);
|
|
58498
|
-
const paramsWithMode = {
|
|
58499
|
-
...params,
|
|
58500
|
-
operating_mode: operatingMode
|
|
58501
|
-
};
|
|
58502
|
-
const requestInit = {
|
|
58503
|
-
method,
|
|
58504
|
-
headers: { authorization: `Bearer ${options.accessToken}` }
|
|
58505
|
-
};
|
|
58506
|
-
if (method === "GET") {
|
|
58507
|
-
for (const [key, value] of Object.entries(paramsWithMode)) {
|
|
58508
|
-
if (value !== void 0 && value !== null) {
|
|
58509
|
-
url.searchParams.set(key, String(value));
|
|
58510
|
-
}
|
|
58511
|
-
}
|
|
58512
|
-
} else {
|
|
58513
|
-
requestInit.headers = {
|
|
58514
|
-
...requestInit.headers,
|
|
58515
|
-
"content-type": "application/json"
|
|
58516
|
-
};
|
|
58517
|
-
requestInit.body = JSON.stringify(paramsWithMode);
|
|
58518
|
-
}
|
|
58519
|
-
const response = await fetchImpl(url, requestInit);
|
|
58520
|
-
const parsed = await response.json();
|
|
58521
|
-
const body = isJsonObject(parsed) ? parsed : void 0;
|
|
58522
|
-
if (body === void 0) {
|
|
58523
|
-
throw new Error(`Linzumi MCP API returned non-object JSON from ${path2}`);
|
|
58524
|
-
}
|
|
58525
|
-
if (response.ok && body.ok === true) {
|
|
58526
|
-
return body;
|
|
58527
|
-
}
|
|
58528
|
-
const error = typeof body.error === "string" ? body.error : `HTTP ${response.status}`;
|
|
58529
|
-
throw new Error(`Linzumi MCP API ${path2} failed: ${error}`);
|
|
58530
|
-
};
|
|
58531
|
-
return {
|
|
58532
|
-
validateAuth: () => request("GET", `${apiPrefix}/validate`, {}),
|
|
58533
|
-
getMessage: (params) => request("GET", `${apiPrefix}/message`, params),
|
|
58534
|
-
getThread: (params) => request("GET", `${apiPrefix}/thread`, params),
|
|
58535
|
-
getChannel: (params) => request("GET", `${apiPrefix}/channel`, params),
|
|
58536
|
-
getCodingJobMetadata: (params) => request("GET", `${apiPrefix}/coding-job-metadata`, params),
|
|
58537
|
-
upsertCodingJobPlan: (params) => request("POST", `${apiPrefix}/coding-job-plan`, params),
|
|
58538
|
-
replaceCodingJobPlanSteps: (params) => request("POST", `${apiPrefix}/coding-job-plan/steps`, params),
|
|
58539
|
-
updateCodingJobPlanStep: (params) => request("POST", `${apiPrefix}/coding-job-plan/step`, params),
|
|
58540
|
-
linkCodingJobPullRequest: (params) => request("POST", `${apiPrefix}/coding-job-primary-pr`, params),
|
|
58541
|
-
listVaultSecrets: (params) => request("GET", `${apiPrefix}/vault-secrets`, params),
|
|
58542
|
-
sendChannelMessage: (params) => request("POST", `${apiPrefix}/channel-message`, params),
|
|
58543
|
-
prepareMessageUploads: (params) => request("POST", `${apiPrefix}/message-uploads/prepare`, params),
|
|
58544
|
-
attachMessageFiles: (params) => request("POST", `${apiPrefix}/message-files/attach`, params),
|
|
58545
|
-
prepareCustomEmoji: (params) => request("POST", `${apiPrefix}/custom-emoji/prepare`, params),
|
|
58546
|
-
renameCustomEmoji: (params) => request("POST", `${apiPrefix}/custom-emoji/rename`, params),
|
|
58547
|
-
sendThreadReply: (params) => request("POST", `${apiPrefix}/thread-reply`, params),
|
|
58548
|
-
sendDm: (params) => request("POST", `${apiPrefix}/dm`, params),
|
|
58549
|
-
dmOwner: (params) => request("POST", `${apiPrefix}/dm-owner`, params),
|
|
58550
|
-
getVaultValues: (params) => request("POST", `${apiPrefix}/vault-values`, params)
|
|
58551
|
-
};
|
|
58552
|
-
}
|
|
58553
|
-
|
|
58554
|
-
// src/mcpServer.ts
|
|
60975
|
+
init_linzumiApiClient();
|
|
58555
60976
|
init_mcpConfig();
|
|
58556
60977
|
init_oauth();
|
|
58557
60978
|
init_protocol();
|
|
58558
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
|
+
}
|
|
58559
60988
|
function normalizeCustomEmojiNameForSchema(value) {
|
|
58560
60989
|
return value.trim().replace(/^:+/, "").replace(/:+$/, "").toLowerCase();
|
|
58561
60990
|
}
|
|
@@ -58578,6 +61007,7 @@ var mcpFlagDefinitions = /* @__PURE__ */ new Map([
|
|
|
58578
61007
|
["thread-id", { kind: "value" }],
|
|
58579
61008
|
["format", { kind: "value" }],
|
|
58580
61009
|
["command", { kind: "value" }],
|
|
61010
|
+
["tool-scope", { kind: "value" }],
|
|
58581
61011
|
["include-token", { kind: "boolean" }],
|
|
58582
61012
|
["help", { kind: "boolean" }]
|
|
58583
61013
|
]);
|
|
@@ -58617,6 +61047,8 @@ Tools:
|
|
|
58617
61047
|
linzumi_get_channel Read bounded recent messages and channel metadata.
|
|
58618
61048
|
linzumi_get_coding_job_metadata
|
|
58619
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.
|
|
58620
61052
|
linzumi_upsert_coding_job_plan
|
|
58621
61053
|
Set or update the coding job goal.
|
|
58622
61054
|
linzumi_replace_coding_job_plan_steps
|
|
@@ -58627,6 +61059,12 @@ Tools:
|
|
|
58627
61059
|
Link the coding job to its primary GitHub pull request.
|
|
58628
61060
|
linzumi_list_vault_secrets
|
|
58629
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.
|
|
58630
61068
|
linzumi_send_channel_message
|
|
58631
61069
|
Send a plain-text message to the scoped channel.
|
|
58632
61070
|
linzumi_send_thread_reply
|
|
@@ -58662,6 +61100,7 @@ async function runMcpServer(args) {
|
|
|
58662
61100
|
});
|
|
58663
61101
|
const ownerUsername = stringValue5(values, "owner-username") ?? process.env.LINZUMI_MCP_OWNER_USERNAME;
|
|
58664
61102
|
const operatingMode = mcpOperatingMode(stringValue5(values, "mode"));
|
|
61103
|
+
const toolScope = mcpToolScope(stringValue5(values, "tool-scope"));
|
|
58665
61104
|
const cwd = stringValue5(values, "cwd") ?? process.cwd();
|
|
58666
61105
|
const defaultThreadId = stringValue5(values, "thread-id") ?? process.env.LINZUMI_THREAD_RUNNER_KANDAN_THREAD_ID;
|
|
58667
61106
|
const client = createLinzumiMcpApiClient({
|
|
@@ -58675,318 +61114,447 @@ async function runMcpServer(args) {
|
|
|
58675
61114
|
version: "0.1.0"
|
|
58676
61115
|
});
|
|
58677
61116
|
registerEmptyMcpResources(server);
|
|
58678
|
-
|
|
58679
|
-
|
|
58680
|
-
|
|
58681
|
-
|
|
58682
|
-
|
|
58683
|
-
|
|
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
|
-
|
|
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."
|
|
61124
|
+
),
|
|
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."
|
|
61171
|
+
)
|
|
61172
|
+
},
|
|
61173
|
+
async (params) => mcpJsonResult(
|
|
61174
|
+
await client.getCodingJobMetadata(
|
|
61175
|
+
paramsWithDefaultThread(params, defaultThreadId)
|
|
61176
|
+
)
|
|
58726
61177
|
)
|
|
58727
|
-
)
|
|
58728
|
-
|
|
58729
|
-
|
|
58730
|
-
|
|
58731
|
-
|
|
58732
|
-
|
|
58733
|
-
|
|
58734
|
-
|
|
58735
|
-
|
|
58736
|
-
|
|
58737
|
-
|
|
58738
|
-
|
|
58739
|
-
|
|
58740
|
-
|
|
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
|
+
)
|
|
58741
61196
|
)
|
|
58742
|
-
)
|
|
58743
|
-
|
|
58744
|
-
|
|
58745
|
-
|
|
58746
|
-
|
|
58747
|
-
|
|
58748
|
-
|
|
58749
|
-
|
|
58750
|
-
|
|
58751
|
-
|
|
58752
|
-
|
|
58753
|
-
|
|
58754
|
-
|
|
58755
|
-
|
|
58756
|
-
|
|
58757
|
-
|
|
58758
|
-
|
|
58759
|
-
|
|
58760
|
-
"active",
|
|
58761
|
-
"completed",
|
|
58762
|
-
"blocked",
|
|
58763
|
-
"canceled"
|
|
58764
|
-
]),
|
|
58765
|
-
status_note: external_exports2.string().min(1).max(4e3).optional(),
|
|
58766
|
-
completed_debrief: external_exports2.string().min(1).max(2e4).optional()
|
|
58767
|
-
})
|
|
58768
|
-
).min(1).max(50).describe(
|
|
58769
|
-
"Ordered user-visible plan steps. Completed steps must include completed_debrief."
|
|
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
|
+
)
|
|
58770
61215
|
)
|
|
58771
|
-
|
|
58772
|
-
|
|
58773
|
-
|
|
58774
|
-
|
|
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
|
+
)
|
|
58775
61253
|
)
|
|
58776
|
-
)
|
|
58777
|
-
|
|
58778
|
-
|
|
58779
|
-
|
|
58780
|
-
|
|
58781
|
-
|
|
58782
|
-
|
|
58783
|
-
|
|
58784
|
-
|
|
58785
|
-
|
|
58786
|
-
|
|
58787
|
-
|
|
58788
|
-
|
|
58789
|
-
|
|
58790
|
-
|
|
58791
|
-
|
|
58792
|
-
|
|
58793
|
-
|
|
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
|
+
)
|
|
58794
61282
|
)
|
|
58795
|
-
)
|
|
58796
|
-
|
|
58797
|
-
|
|
58798
|
-
|
|
58799
|
-
|
|
58800
|
-
|
|
58801
|
-
|
|
58802
|
-
|
|
58803
|
-
|
|
58804
|
-
|
|
58805
|
-
|
|
58806
|
-
|
|
58807
|
-
|
|
58808
|
-
|
|
58809
|
-
|
|
58810
|
-
|
|
58811
|
-
|
|
58812
|
-
|
|
58813
|
-
|
|
58814
|
-
|
|
58815
|
-
|
|
58816
|
-
|
|
58817
|
-
|
|
58818
|
-
|
|
58819
|
-
|
|
58820
|
-
|
|
58821
|
-
|
|
58822
|
-
|
|
58823
|
-
|
|
58824
|
-
|
|
58825
|
-
|
|
58826
|
-
|
|
58827
|
-
|
|
58828
|
-
|
|
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
|
+
)
|
|
58829
61321
|
)
|
|
58830
|
-
)
|
|
58831
|
-
|
|
58832
|
-
|
|
58833
|
-
|
|
58834
|
-
|
|
58835
|
-
|
|
58836
|
-
|
|
58837
|
-
|
|
58838
|
-
|
|
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."
|
|
58839
61384
|
),
|
|
58840
|
-
|
|
58841
|
-
|
|
58842
|
-
"Thread UUID. Defaults to the active Linzumi thread when the MCP server was launched with one."
|
|
61385
|
+
workspace: external_exports2.string().optional().describe(
|
|
61386
|
+
"Workspace slug. Omit to use the authenticated token scope."
|
|
58843
61387
|
)
|
|
58844
|
-
}
|
|
58845
|
-
|
|
58846
|
-
|
|
58847
|
-
|
|
58848
|
-
|
|
58849
|
-
|
|
58850
|
-
|
|
58851
|
-
|
|
58852
|
-
|
|
58853
|
-
workspace: external_exports2.string().optional().describe("Workspace slug. Omit to use the authenticated token scope."),
|
|
58854
|
-
channel: external_exports2.string().optional().describe("Channel slug. Omit to use the authenticated token scope.")
|
|
58855
|
-
},
|
|
58856
|
-
async (params) => {
|
|
58857
|
-
const target = targetWithDefaultThread(
|
|
58858
|
-
params.target,
|
|
58859
|
-
defaultThreadId
|
|
58860
|
-
);
|
|
58861
|
-
const result = await uploadFilesWithClient({
|
|
58862
|
-
client,
|
|
58863
|
-
cwd,
|
|
58864
|
-
kandanUrl,
|
|
58865
|
-
target,
|
|
58866
|
-
files: params.files,
|
|
58867
|
-
workspace: params.workspace,
|
|
58868
|
-
channel: params.channel,
|
|
58869
|
-
body: params.body
|
|
58870
|
-
});
|
|
58871
|
-
return mcpJsonResult(result);
|
|
58872
|
-
}
|
|
58873
|
-
);
|
|
58874
|
-
server.tool(
|
|
58875
|
-
"linzumi_list_vault_secrets",
|
|
58876
|
-
"List vault secret names, descriptions, and whether each secret is personal or workspace-owned. Does not return secret values.",
|
|
58877
|
-
{},
|
|
58878
|
-
async (params) => mcpJsonResult(await client.listVaultSecrets(params))
|
|
58879
|
-
);
|
|
58880
|
-
server.tool(
|
|
58881
|
-
"linzumi_upload_custom_emoji",
|
|
58882
|
-
"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:.",
|
|
58883
|
-
{
|
|
58884
|
-
name: customEmojiNameSchema.describe(
|
|
58885
|
-
"Custom emoji shortcode name, with or without surrounding colons."
|
|
58886
|
-
),
|
|
58887
|
-
file: external_exports2.string().min(1).describe(
|
|
58888
|
-
"Local PNG/WebP/GIF/JPEG file path to upload. Prefer transparent PNG for generated emoji."
|
|
58889
|
-
),
|
|
58890
|
-
workspace: external_exports2.string().optional().describe("Workspace slug. Omit to use the authenticated token scope.")
|
|
58891
|
-
},
|
|
58892
|
-
async (params) => mcpJsonResult(
|
|
58893
|
-
await uploadCustomEmojiWithClient({
|
|
58894
|
-
client,
|
|
58895
|
-
cwd,
|
|
58896
|
-
kandanUrl,
|
|
58897
|
-
name: params.name,
|
|
58898
|
-
file: params.file,
|
|
58899
|
-
workspace: params.workspace
|
|
58900
|
-
})
|
|
58901
|
-
)
|
|
58902
|
-
);
|
|
58903
|
-
server.tool(
|
|
58904
|
-
"linzumi_rename_custom_emoji",
|
|
58905
|
-
"Rename an existing workspace custom emoji. The new name may include surrounding colons; Linzumi stores the normalized shortcode without colons.",
|
|
58906
|
-
{
|
|
58907
|
-
emoji_id: external_exports2.union([external_exports2.string(), external_exports2.number().int().positive()]).describe(
|
|
58908
|
-
"Workspace custom emoji id returned by linzumi_upload_custom_emoji or workspace emoji listings."
|
|
58909
|
-
),
|
|
58910
|
-
name: customEmojiNameSchema.describe(
|
|
58911
|
-
"New shortcode name, with or without surrounding colons."
|
|
58912
|
-
),
|
|
58913
|
-
workspace: external_exports2.string().optional().describe("Workspace slug. Omit to use the authenticated token scope.")
|
|
58914
|
-
},
|
|
58915
|
-
async (params) => mcpJsonResult(await client.renameCustomEmoji(params))
|
|
58916
|
-
);
|
|
58917
|
-
server.tool(
|
|
58918
|
-
"linzumi_dm_owner",
|
|
58919
|
-
"Send a plain-text DM to the configured Commander owner. Requires dm.write and a visible owner username.",
|
|
58920
|
-
{
|
|
58921
|
-
owner_username: external_exports2.string().optional().describe(
|
|
58922
|
-
"Owner username. Defaults to LINZUMI_MCP_OWNER_USERNAME or --owner-username."
|
|
58923
|
-
),
|
|
58924
|
-
workspace: external_exports2.string().optional().describe("Workspace slug. Omit to use the authenticated token scope."),
|
|
58925
|
-
channel: external_exports2.string().optional().describe("Source channel slug used to verify owner visibility."),
|
|
58926
|
-
body: external_exports2.string().min(1).max(2e4).describe("Plain-text message body to DM.")
|
|
58927
|
-
},
|
|
58928
|
-
async (params) => {
|
|
58929
|
-
const owner = params.owner_username ?? ownerUsername;
|
|
58930
|
-
if (owner === void 0) {
|
|
58931
|
-
throw new Error("owner_username is required for linzumi_dm_owner");
|
|
58932
|
-
}
|
|
58933
|
-
return mcpJsonResult(
|
|
58934
|
-
await client.dmOwner({
|
|
58935
|
-
...params,
|
|
58936
|
-
owner_username: owner
|
|
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
|
|
58937
61397
|
})
|
|
58938
|
-
)
|
|
58939
|
-
|
|
58940
|
-
|
|
58941
|
-
|
|
58942
|
-
|
|
58943
|
-
|
|
58944
|
-
|
|
58945
|
-
|
|
58946
|
-
|
|
58947
|
-
|
|
58948
|
-
|
|
58949
|
-
|
|
58950
|
-
|
|
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
|
+
}
|
|
58951
61417
|
server.tool(
|
|
58952
|
-
"
|
|
58953
|
-
"
|
|
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.",
|
|
58954
61420
|
{
|
|
58955
61421
|
workspace: external_exports2.string().optional().describe("Workspace slug. Omit to use the authenticated token scope."),
|
|
58956
|
-
|
|
58957
|
-
|
|
58958
|
-
|
|
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.")
|
|
58959
61428
|
},
|
|
58960
|
-
async (params) => mcpJsonResult(await client.
|
|
61429
|
+
async (params) => mcpJsonResult(await client.noteProjectDirectory(params))
|
|
58961
61430
|
);
|
|
58962
61431
|
server.tool(
|
|
58963
|
-
"
|
|
58964
|
-
"
|
|
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.",
|
|
58965
61434
|
{
|
|
58966
61435
|
workspace: external_exports2.string().optional().describe("Workspace slug. Omit to use the authenticated token scope."),
|
|
58967
|
-
|
|
58968
|
-
|
|
58969
|
-
|
|
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
|
+
)
|
|
58970
61445
|
},
|
|
58971
|
-
async (params) => mcpJsonResult(await client.
|
|
61446
|
+
async (params) => mcpJsonResult(await client.noteAgentConversation(params))
|
|
58972
61447
|
);
|
|
58973
61448
|
server.tool(
|
|
58974
|
-
"
|
|
58975
|
-
"
|
|
61449
|
+
"linzumi_note_onboarding_discovery_status",
|
|
61450
|
+
"Record onboarding discovery progress. Call when a search phase starts, checks another place, completes, or fails.",
|
|
58976
61451
|
{
|
|
58977
61452
|
workspace: external_exports2.string().optional().describe("Workspace slug. Omit to use the authenticated token scope."),
|
|
58978
|
-
|
|
58979
|
-
|
|
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."
|
|
61458
|
+
),
|
|
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."
|
|
58980
61462
|
),
|
|
58981
|
-
|
|
58982
|
-
|
|
58983
|
-
"Explicit vault env var names to request. This tool cannot list all vault keys."
|
|
61463
|
+
current_source: external_exports2.string().optional().describe(
|
|
61464
|
+
"Short machine-readable source label such as git, codex, or claude_code."
|
|
58984
61465
|
),
|
|
58985
|
-
|
|
58986
|
-
|
|
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.")
|
|
58987
61468
|
},
|
|
58988
|
-
async (params) => mcpJsonResult(
|
|
61469
|
+
async (params) => mcpJsonResult(
|
|
61470
|
+
await client.noteOnboardingDiscoveryStatus(params)
|
|
61471
|
+
)
|
|
58989
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
|
+
}
|
|
58990
61558
|
await server.connect(new StdioServerTransport());
|
|
58991
61559
|
}
|
|
58992
61560
|
async function uploadFilesWithClient(args) {
|
|
@@ -59014,13 +61582,13 @@ async function uploadFilesWithClient(args) {
|
|
|
59014
61582
|
if (uploadUrl === void 0) {
|
|
59015
61583
|
throw new Error("Linzumi upload prepare response missing upload_url");
|
|
59016
61584
|
}
|
|
59017
|
-
const bytes = await
|
|
61585
|
+
const bytes = await readFile3(file.path);
|
|
59018
61586
|
const uploadBody = bytes.buffer.slice(
|
|
59019
61587
|
bytes.byteOffset,
|
|
59020
61588
|
bytes.byteOffset + bytes.byteLength
|
|
59021
61589
|
);
|
|
59022
61590
|
const response = await fetch(
|
|
59023
|
-
|
|
61591
|
+
resolveLinzumiUploadUrl2(args.kandanUrl, uploadUrl),
|
|
59024
61592
|
{
|
|
59025
61593
|
method: uploadMethod,
|
|
59026
61594
|
headers: { "content-type": file.contentType },
|
|
@@ -59064,13 +61632,13 @@ async function uploadCustomEmojiWithClient(args) {
|
|
|
59064
61632
|
if (uploadUrl === void 0) {
|
|
59065
61633
|
throw new Error("Linzumi custom emoji prepare response missing upload_url");
|
|
59066
61634
|
}
|
|
59067
|
-
const bytes = await
|
|
61635
|
+
const bytes = await readFile3(file.path);
|
|
59068
61636
|
const uploadBody = bytes.buffer.slice(
|
|
59069
61637
|
bytes.byteOffset,
|
|
59070
61638
|
bytes.byteOffset + bytes.byteLength
|
|
59071
61639
|
);
|
|
59072
61640
|
const response = await fetch(
|
|
59073
|
-
|
|
61641
|
+
resolveLinzumiUploadUrl2(args.kandanUrl, uploadUrl),
|
|
59074
61642
|
{
|
|
59075
61643
|
method: uploadMethod,
|
|
59076
61644
|
headers: { "content-type": file.contentType },
|
|
@@ -59133,7 +61701,7 @@ function paramsWithDefaultThread(params, defaultThreadId) {
|
|
|
59133
61701
|
}
|
|
59134
61702
|
return { ...params, thread_id: defaultThreadId };
|
|
59135
61703
|
}
|
|
59136
|
-
function
|
|
61704
|
+
function resolveLinzumiUploadUrl2(kandanUrl, uploadUrl) {
|
|
59137
61705
|
try {
|
|
59138
61706
|
return new URL(uploadUrl).toString();
|
|
59139
61707
|
} catch (_error) {
|
|
@@ -59171,7 +61739,8 @@ async function runMcpConfig(args) {
|
|
|
59171
61739
|
accessToken: token,
|
|
59172
61740
|
delegationAuthFilePath: stringValue5(values, "delegation-auth-file"),
|
|
59173
61741
|
ownerUsername: stringValue5(values, "owner-username") ?? process.env.LINZUMI_MCP_OWNER_USERNAME,
|
|
59174
|
-
operatingMode
|
|
61742
|
+
operatingMode,
|
|
61743
|
+
toolScope: mcpToolScope(stringValue5(values, "tool-scope"))
|
|
59175
61744
|
});
|
|
59176
61745
|
switch (format) {
|
|
59177
61746
|
case "codex":
|
|
@@ -59299,6 +61868,17 @@ function mcpOperatingMode(value) {
|
|
|
59299
61868
|
throw new Error("--mode must be voice or text");
|
|
59300
61869
|
}
|
|
59301
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
|
+
}
|
|
59302
61882
|
function required(values, key) {
|
|
59303
61883
|
const value = stringValue5(values, key);
|
|
59304
61884
|
if (value === void 0) {
|
|
@@ -59641,6 +62221,7 @@ function optionalObjectField(input, key) {
|
|
|
59641
62221
|
|
|
59642
62222
|
// src/index.ts
|
|
59643
62223
|
init_userFacingErrors();
|
|
62224
|
+
var onboardingDiscoveryRequiredScopes = ["local_runner.discovery.write"];
|
|
59644
62225
|
var flagDefinitions = /* @__PURE__ */ new Map([
|
|
59645
62226
|
["version", { kind: "boolean" }],
|
|
59646
62227
|
["api-url", { kind: "value" }],
|
|
@@ -59664,6 +62245,7 @@ var flagDefinitions = /* @__PURE__ */ new Map([
|
|
|
59664
62245
|
["forward-port", { kind: "value" }],
|
|
59665
62246
|
["code-server-bin", { kind: "value" }],
|
|
59666
62247
|
["fast", { kind: "boolean" }],
|
|
62248
|
+
["no-onboarding-discovery", { kind: "boolean" }],
|
|
59667
62249
|
["log-file", { kind: "value" }],
|
|
59668
62250
|
["status-dir", { kind: "value" }],
|
|
59669
62251
|
["timeout-ms", { kind: "value" }],
|
|
@@ -59786,8 +62368,41 @@ async function main(args) {
|
|
|
59786
62368
|
await runThreadCodexWorker(parseThreadCodexWorkerArgs(parsed.args));
|
|
59787
62369
|
return;
|
|
59788
62370
|
}
|
|
59789
|
-
const
|
|
59790
|
-
|
|
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
|
+
}
|
|
59791
62406
|
return;
|
|
59792
62407
|
}
|
|
59793
62408
|
}
|
|
@@ -60123,6 +62738,7 @@ async function parseStartRunnerArgs(args, deps = {
|
|
|
60123
62738
|
const allowedCwds = Array.from(/* @__PURE__ */ new Set([cwd, ...explicitAllowedCwds]));
|
|
60124
62739
|
const requestedCodexBin = stringValue7(values, "codex-bin") ?? "codex";
|
|
60125
62740
|
const customCodeServerBin = stringValue7(values, "code-server-bin");
|
|
62741
|
+
const onboardingDiscovery = values.get("no-onboarding-discovery") === true ? void 0 : "start";
|
|
60126
62742
|
const initialDependencyStatus = await deps.buildDependencyStatus({
|
|
60127
62743
|
cwd,
|
|
60128
62744
|
codexBin: requestedCodexBin,
|
|
@@ -60141,9 +62757,10 @@ async function parseStartRunnerArgs(args, deps = {
|
|
|
60141
62757
|
const token = await deps.resolveToken({
|
|
60142
62758
|
kandanUrl,
|
|
60143
62759
|
explicitToken,
|
|
60144
|
-
onboarding: "start",
|
|
62760
|
+
onboarding: onboardingDiscovery === "start" ? "start" : void 0,
|
|
60145
62761
|
authFilePath,
|
|
60146
62762
|
callbackHost,
|
|
62763
|
+
requiredScopes: onboardingDiscovery === "start" ? onboardingDiscoveryRequiredScopes : void 0,
|
|
60147
62764
|
reportRejectedCachedToken
|
|
60148
62765
|
});
|
|
60149
62766
|
const target = await deps.fetchStartTarget({ kandanUrl, accessToken: token });
|
|
@@ -60151,7 +62768,8 @@ async function parseStartRunnerArgs(args, deps = {
|
|
|
60151
62768
|
kandanUrl,
|
|
60152
62769
|
accessToken: token,
|
|
60153
62770
|
workspaceSlug: target.workspaceSlug,
|
|
60154
|
-
channelSlug: target.channelSlug
|
|
62771
|
+
channelSlug: target.channelSlug,
|
|
62772
|
+
requiredScopes: onboardingDiscovery === "start" ? onboardingDiscoveryRequiredScopes : void 0
|
|
60155
62773
|
});
|
|
60156
62774
|
const targetToken = tokenMatchesTarget ? token : await resolveStartTargetToken({
|
|
60157
62775
|
kandanUrl,
|
|
@@ -60159,6 +62777,7 @@ async function parseStartRunnerArgs(args, deps = {
|
|
|
60159
62777
|
target,
|
|
60160
62778
|
authFilePath,
|
|
60161
62779
|
callbackHost,
|
|
62780
|
+
requiredScopes: onboardingDiscovery === "start" ? onboardingDiscoveryRequiredScopes : void 0,
|
|
60162
62781
|
resolveToken: deps.resolveToken,
|
|
60163
62782
|
reportRejectedCachedToken
|
|
60164
62783
|
});
|
|
@@ -60200,6 +62819,7 @@ async function parseStartRunnerArgs(args, deps = {
|
|
|
60200
62819
|
dependencyStatus,
|
|
60201
62820
|
claudeCodeAvailable,
|
|
60202
62821
|
runtimeDefaults: runnerRuntimeDefaultsFromValues(values),
|
|
62822
|
+
onboardingDiscovery,
|
|
60203
62823
|
channelSession: {
|
|
60204
62824
|
workspaceSlug: target.workspaceSlug,
|
|
60205
62825
|
channelSlug: target.channelSlug,
|
|
@@ -60309,7 +62929,7 @@ async function parseAgentRunnerArgs(args, deps = {
|
|
|
60309
62929
|
};
|
|
60310
62930
|
}
|
|
60311
62931
|
function readAgentTokenTextFile(path2) {
|
|
60312
|
-
return
|
|
62932
|
+
return existsSync16(path2) ? readFileSync19(path2, "utf8") : void 0;
|
|
60313
62933
|
}
|
|
60314
62934
|
function rejectAgentRunnerTargetingFlags(values) {
|
|
60315
62935
|
const unsupportedFlags = [
|
|
@@ -60381,6 +63001,7 @@ async function resolveStartTargetToken(args) {
|
|
|
60381
63001
|
channelSlug: args.target.channelSlug,
|
|
60382
63002
|
authFilePath: args.authFilePath,
|
|
60383
63003
|
callbackHost: args.callbackHost,
|
|
63004
|
+
requiredScopes: args.requiredScopes,
|
|
60384
63005
|
reportRejectedCachedToken: args.reportRejectedCachedToken
|
|
60385
63006
|
});
|
|
60386
63007
|
}
|
|
@@ -60420,6 +63041,7 @@ async function parseRunnerArgs(args, deps = {
|
|
|
60420
63041
|
) : [...localConfiguredAllowedCwds.allowedCwds];
|
|
60421
63042
|
const requestedCodexBin = stringValue7(values, "codex-bin") ?? "codex";
|
|
60422
63043
|
const customCodeServerBin = stringValue7(values, "code-server-bin");
|
|
63044
|
+
const onboardingDiscovery = values.get("no-onboarding-discovery") === true ? void 0 : "start";
|
|
60423
63045
|
const initialDependencyStatus = await deps.buildDependencyStatus({
|
|
60424
63046
|
cwd,
|
|
60425
63047
|
codexBin: requestedCodexBin,
|
|
@@ -60434,6 +63056,7 @@ async function parseRunnerArgs(args, deps = {
|
|
|
60434
63056
|
workspaceSlug,
|
|
60435
63057
|
authFilePath: stringValue7(values, "auth-file"),
|
|
60436
63058
|
callbackHost: stringValue7(values, "oauth-callback-host"),
|
|
63059
|
+
requiredScopes: onboardingDiscovery === "start" ? onboardingDiscoveryRequiredScopes : void 0,
|
|
60437
63060
|
reportRejectedCachedToken: () => {
|
|
60438
63061
|
process.stderr.write(
|
|
60439
63062
|
"Cached Linzumi local runner auth was rejected; starting OAuth.\n"
|
|
@@ -60481,6 +63104,7 @@ async function parseRunnerArgs(args, deps = {
|
|
|
60481
63104
|
claudeCodeAvailable,
|
|
60482
63105
|
workspaceSlug: workspaceSlug ?? singleWorkspaceScopeFromAccessToken(token),
|
|
60483
63106
|
runtimeDefaults: runnerRuntimeDefaultsFromValues(values),
|
|
63107
|
+
onboardingDiscovery,
|
|
60484
63108
|
channelSession: void 0
|
|
60485
63109
|
};
|
|
60486
63110
|
}
|
|
@@ -60496,6 +63120,15 @@ function runnerRuntimeDefaultsFromValues(values) {
|
|
|
60496
63120
|
function electronAutoConnectLaunchSource() {
|
|
60497
63121
|
return process.env.LINZUMI_ELECTRON_AUTO_CONNECT_RUNNER === "1" ? "electron_auto_connect" : void 0;
|
|
60498
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
|
+
}
|
|
60499
63132
|
function kandanUrlValue(values) {
|
|
60500
63133
|
const apiUrl = stringValue7(values, "api-url");
|
|
60501
63134
|
const linzumiUrl = stringValue7(values, "linzumi-url");
|
|
@@ -60607,10 +63240,10 @@ function rejectStartTargetingFlags(values) {
|
|
|
60607
63240
|
}
|
|
60608
63241
|
function resolveUserPath(pathValue) {
|
|
60609
63242
|
if (pathValue === "~") {
|
|
60610
|
-
return
|
|
63243
|
+
return homedir15();
|
|
60611
63244
|
}
|
|
60612
63245
|
if (pathValue.startsWith("~/")) {
|
|
60613
|
-
return resolve10(
|
|
63246
|
+
return resolve10(homedir15(), pathValue.slice(2));
|
|
60614
63247
|
}
|
|
60615
63248
|
return resolve10(pathValue);
|
|
60616
63249
|
}
|