codemem 0.44.1 → 0.45.0-rc.1
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/dist/index.js +652 -95
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { DEDUP_KEY_BACKFILL_JOB, DEFAULT_COORDINATOR_DB_PATH, DedupKeyBackfillRunner, DeviceIdentityError, EXTRACTION_BENCHMARK_QUALITY_WEIGHTS, MUTATING_TOOL_NAMES, MemoryStore, ObserverClient, ObserverOutputError, ObserverOutputTransportError, PROMPT_TRANSPORT_PROTOCOL_RANGE, REF_BACKFILL_JOB, RawEventIngestValidationError, RawEventSweeper, RefBackfillRunner, SCOPE_BACKFILL_JOB, SESSION_CONTEXT_BACKFILL_JOB, SUMMARY_DEDUP_BACKFILL_JOB, ScopeBackfillRunner, SessionContextBackfillRunner, SummaryDedupBackfillRunner, SyncRetentionRunner, TRUSTED_HOOK_MAPPER_OPTIONS, VERSION, VectorModelMigrationRunner, aiBackfillStructuredContent, applyBootstrapSnapshot, applyDistillRule, arePromptTransportProtocolRangesCompatible, backfillMemoryDedupKeys, backfillNarrativeFromBody, backfillTagsText, backfillVectors, buildBaseUrl, buildDirectPeerAuthHeaders, buildDistillReport, buildRawEventEnvelopeFromCodexHook, buildRawEventEnvelopeFromHook, buildViewerIdentityTarget, classifyPromptTransportFailure, clonePromptPackAttempt, collectOperationalStatus, compareMemoryRoleReports, connect, connectReadOnly, coordinatorCreateGroupAction, coordinatorCreateInviteAction, coordinatorCreateScopeAction, coordinatorDisableDeviceAction, coordinatorEnrollDeviceAction, coordinatorGrantScopeMembershipAction, coordinatorImportInviteAction, coordinatorListBootstrapGrantsAction, coordinatorListDevicesAction, coordinatorListGroupsAction, coordinatorListJoinRequestsAction, coordinatorListScopeMembershipsAction, coordinatorListScopesAction, coordinatorRemoveDeviceAction, coordinatorRenameDeviceAction, coordinatorReviewJoinRequestAction, coordinatorRevokeBootstrapGrantAction, coordinatorRevokeScopeMembershipAction, coordinatorUpdateScopeAction, createBetterSqliteCoordinatorApp, deactivateLowSignalMemories, deactivateLowSignalObservations, dedupNearDuplicateMemories, detectInstallKind, draftDistillRule, ensureDeviceIdentity, ensureSchemaBootstrapped, estimateExtractionModelCost, exportMemories, extractApplyPatchPaths, extractionReplayObserverIdentity, fetchAllSnapshotPages, fingerprintPublicKey, flushRawEvents, formatHostPort, getAttributionDiagnostics, getExtractionBenchmarkProfile, getExtractionModelPricing, getInjectionEvalScenarioPack, getInjectionEvalScenarioPrompts, getMaintenanceJob, getMemoryArtifactReport, getMemoryRoleReport, getRawEventRelinkPlan, getRawEventRelinkReport, getRawEventStatus, getSemanticIndexDiagnostics, getSessionExtractionEval, getSessionExtractionEvalScenario, getUpdateStatus, getWorkspaceCodememConfigPath, hasPendingDedupKeyBackfill, hasPendingRefBackfill, hasPendingScopeBackfill, hasPendingSessionContextBackfill, hasPendingSummaryDedupBackfill, hasUnsyncedSharedMemoryChanges, importMemories, ingestRawEvents, initDatabase, isAutomaticRecallMeasurement, isEmbeddingDisabled, isReleaseVersionForChannel, judgeDistillReport, listMaintenanceJobs, listPerPeerScopeSyncState, listRetentionScopeIds, loadObserverConfig, loadPublicKey, loadSqliteVec, mdnsEnabled, normalizePromptTransportProtocolRange, planReplicationOpsAgePrune, probeCodememViewerLiveness, probeCodememViewerLiveness as probeCodememViewerLiveness$1, probeRequiredNativeRuntime, projectMatchesFilter, promptPackArtifactFingerprint, pruneReplicationOpsUntilCaughtUp, rawEventsGate, readCodememConfigFile, readCodememConfigFileAtPath, readCoordinatorSyncConfig, readImportPayload, recordAutomaticRecall, recordPromptPackArtifacts, recordPromptPackTerminal, recordRetrievalSurface, renderUnifiedDiff, replayBatchExtraction, replayBatchExtractionWithTierRouting, requestJson, resolveCodememConfigPath, resolveDbPath, resolveHookProject, resolveProject, resolveProjectRoot, resolveRetrievalSession, retryRawEventFailures, runSyncDaemon, runSyncPass, scanSecretsRetroactive, schema, scoreExtractionBenchmarkOutput, setPeerProjectFilter, stripJsonComments, stripTrailingCommas, syncPassPreflight, tryUpdateRetrievalDelivery, updatePeerAddresses, vacuumDatabase, viewerUrl, writeCodememConfigFile } from "@codemem/core";
|
|
2
|
+
import { ContextOnlyReplayError, DEDUP_KEY_BACKFILL_JOB, DEFAULT_COORDINATOR_DB_PATH, DedupKeyBackfillRunner, DeviceIdentityError, EXTRACTION_BENCHMARK_QUALITY_WEIGHTS, MUTATING_TOOL_NAMES, MemoryStore, ObserverClient, ObserverOutputError, ObserverOutputTransportError, PROMPT_TRANSPORT_PROTOCOL_RANGE, REF_BACKFILL_JOB, RawEventIngestValidationError, RawEventSweeper, RefBackfillRunner, SCOPE_BACKFILL_JOB, SESSION_CONTEXT_BACKFILL_JOB, SUMMARY_DEDUP_BACKFILL_JOB, ScopeBackfillRunner, SessionContextBackfillRunner, SummaryDedupBackfillRunner, SyncRetentionRunner, TRUSTED_HOOK_MAPPER_OPTIONS, VERSION, VectorModelMigrationRunner, aiBackfillStructuredContent, applyBootstrapSnapshot, applyDistillRule, arePromptTransportProtocolRangesCompatible, backfillMemoryDedupKeys, backfillNarrativeFromBody, backfillTagsText, backfillVectors, buildBaseUrl, buildDirectPeerAuthHeaders, buildDistillReport, buildRawEventEnvelopeFromCodexHook, buildRawEventEnvelopeFromHook, buildViewerIdentityTarget, classifyPromptTransportFailure, clonePromptPackAttempt, collectOperationalStatus, compareMemoryRoleReports, connect, connectReadOnly, coordinatorCreateGroupAction, coordinatorCreateInviteAction, coordinatorCreateScopeAction, coordinatorDisableDeviceAction, coordinatorEnrollDeviceAction, coordinatorGrantScopeMembershipAction, coordinatorImportInviteAction, coordinatorListBootstrapGrantsAction, coordinatorListDevicesAction, coordinatorListGroupsAction, coordinatorListJoinRequestsAction, coordinatorListScopeMembershipsAction, coordinatorListScopesAction, coordinatorRemoveDeviceAction, coordinatorRenameDeviceAction, coordinatorReviewJoinRequestAction, coordinatorRevokeBootstrapGrantAction, coordinatorRevokeScopeMembershipAction, coordinatorUpdateScopeAction, createBetterSqliteCoordinatorApp, deactivateLowSignalMemories, deactivateLowSignalObservations, dedupNearDuplicateMemories, detectInstallKind, draftDistillRule, ensureDeviceIdentity, ensureSchemaBootstrapped, estimateExtractionModelCost, exportMemories, extractApplyPatchPaths, extractionReplayObserverIdentity, fetchAllSnapshotPages, fingerprintPublicKey, flushRawEvents, formatHostPort, getAttributionDiagnostics, getExtractionBenchmarkProfile, getExtractionModelPricing, getInjectionEvalScenarioPack, getInjectionEvalScenarioPrompts, getMaintenanceJob, getMemoryArtifactReport, getMemoryRoleReport, getRawEventRelinkPlan, getRawEventRelinkReport, getRawEventStatus, getSemanticIndexDiagnostics, getSessionExtractionEval, getSessionExtractionEvalScenario, getUpdateStatus, getWorkspaceCodememConfigPath, hasPendingDedupKeyBackfill, hasPendingRefBackfill, hasPendingScopeBackfill, hasPendingSessionContextBackfill, hasPendingSummaryDedupBackfill, hasUnsyncedSharedMemoryChanges, importMemories, ingestRawEvents, initDatabase, isAutomaticRecallMeasurement, isEmbeddingDisabled, isExplicitUpdateInstallEligible, isReleaseVersionForChannel, judgeDistillReport, listMaintenanceJobs, listPerPeerScopeSyncState, listRetentionScopeIds, loadObserverConfig, loadPublicKey, loadSqliteVec, mdnsEnabled, normalizePromptTransportProtocolRange, planReplicationOpsAgePrune, probeCodememViewerLiveness, probeCodememViewerLiveness as probeCodememViewerLiveness$1, probeRequiredNativeRuntime, projectMatchesFilter, promptPackArtifactFingerprint, pruneReplicationOpsUntilCaughtUp, rawEventsGate, readCodememConfigFile, readCodememConfigFileAtPath, readCoordinatorSyncConfig, readImportPayload, recordAutomaticRecall, recordPromptPackArtifacts, recordPromptPackTerminal, recordRetrievalSurface, renderUnifiedDiff, replayBatchExtraction, replayBatchExtractionWithTierRouting, requestJson, resolveCodememConfigPath, resolveComparablePath, resolveDbPath, resolveHookProject, resolveProject, resolveProjectRoot, resolveRetrievalSession, retryRawEventFailures, runSyncDaemon, runSyncPass, scanSecretsRetroactive, schema, scoreExtractionBenchmarkOutput, setPeerProjectFilter, stripJsonComments, stripTrailingCommas, syncPassPreflight, tryUpdateRetrievalDelivery, updatePeerAddresses, vacuumDatabase, viewerUrl, writeCodememConfigFile } from "@codemem/core";
|
|
3
3
|
import { Command, Option } from "commander";
|
|
4
4
|
import omelette from "omelette";
|
|
5
5
|
import { randomInt, randomUUID } from "node:crypto";
|
|
6
|
-
import { appendFileSync, copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, rmdirSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
6
|
+
import { appendFileSync, copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, rmdirSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
7
7
|
import { homedir, networkInterfaces } from "node:os";
|
|
8
8
|
import { dirname, isAbsolute, join, posix, relative, resolve, sep, win32 } from "node:path";
|
|
9
9
|
import { styleText } from "node:util";
|
|
@@ -4701,6 +4701,20 @@ function parseStrictPositiveId(value) {
|
|
|
4701
4701
|
const n = Number(value.trim());
|
|
4702
4702
|
return Number.isFinite(n) && n >= 1 && Number.isInteger(n) ? n : null;
|
|
4703
4703
|
}
|
|
4704
|
+
function contextOnlyReplayOutcome(error, target) {
|
|
4705
|
+
return {
|
|
4706
|
+
...target,
|
|
4707
|
+
status: "context_only",
|
|
4708
|
+
code: error.code,
|
|
4709
|
+
evaluated: false,
|
|
4710
|
+
message: error.message
|
|
4711
|
+
};
|
|
4712
|
+
}
|
|
4713
|
+
function emitContextOnlyReplayOutcome(outcome, opts) {
|
|
4714
|
+
if (opts.json) console.log(JSON.stringify(outcome, null, 2));
|
|
4715
|
+
else p.log.info(outcome.message);
|
|
4716
|
+
process.exitCode = 0;
|
|
4717
|
+
}
|
|
4704
4718
|
function resolveOpenAIResponsesOverride(cliEnabled, configured) {
|
|
4705
4719
|
return cliEnabled === true ? true : configured;
|
|
4706
4720
|
}
|
|
@@ -5177,6 +5191,13 @@ function createMemoryExtractionReplayCommand() {
|
|
|
5177
5191
|
].join("\n"));
|
|
5178
5192
|
p.outro("done");
|
|
5179
5193
|
} catch (error) {
|
|
5194
|
+
if (error instanceof ContextOnlyReplayError) {
|
|
5195
|
+
emitContextOnlyReplayOutcome(contextOnlyReplayOutcome(error, {
|
|
5196
|
+
batchId: Number(opts.batchId),
|
|
5197
|
+
scenarioId: opts.scenario.trim()
|
|
5198
|
+
}), opts);
|
|
5199
|
+
return;
|
|
5200
|
+
}
|
|
5180
5201
|
const message = error instanceof Error ? error.message : "Extraction replay failed";
|
|
5181
5202
|
if (opts.json) emitJsonError("extraction_replay_failed", message);
|
|
5182
5203
|
else {
|
|
@@ -5414,6 +5435,7 @@ function createMemoryExtractionBenchmarkCommand() {
|
|
|
5414
5435
|
const observer = new ObserverClient(observerConfigWithOverrides);
|
|
5415
5436
|
const outputFailures = [];
|
|
5416
5437
|
const runs = [];
|
|
5438
|
+
const contextOnlySkips = [];
|
|
5417
5439
|
for (let iteration = 1; iteration <= repetitions; iteration += 1) for (const batch of benchmark.batches) {
|
|
5418
5440
|
const scenarioId = batch.scenarioId ?? benchmark.scenarioId;
|
|
5419
5441
|
let selectedObserver = extractionReplayObserverIdentity(observer, null);
|
|
@@ -5432,6 +5454,17 @@ function createMemoryExtractionBenchmarkCommand() {
|
|
|
5432
5454
|
transcriptBudget: transcriptBudget ?? void 0
|
|
5433
5455
|
});
|
|
5434
5456
|
} catch (error) {
|
|
5457
|
+
if (error instanceof ContextOnlyReplayError) {
|
|
5458
|
+
contextOnlySkips.push({
|
|
5459
|
+
...contextOnlyReplayOutcome(error, {
|
|
5460
|
+
batchId: batch.batchId,
|
|
5461
|
+
scenarioId
|
|
5462
|
+
}),
|
|
5463
|
+
iteration,
|
|
5464
|
+
purpose: batch.purpose
|
|
5465
|
+
});
|
|
5466
|
+
continue;
|
|
5467
|
+
}
|
|
5435
5468
|
if (!(error instanceof ObserverOutputError) && !(error instanceof ObserverOutputTransportError)) throw error;
|
|
5436
5469
|
if (opts.observerTierRouting !== true) selectedObserver = extractionReplayObserverIdentity(observer, null);
|
|
5437
5470
|
const failureCost = estimateBenchmarkAttemptCost(selectedObserver.model, error.telemetry.totalUsage);
|
|
@@ -5600,6 +5633,9 @@ function createMemoryExtractionBenchmarkCommand() {
|
|
|
5600
5633
|
const knownElapsedRuns = attempts.filter((run) => run.telemetry.totalElapsedMs != null);
|
|
5601
5634
|
const summary = {
|
|
5602
5635
|
repetitions,
|
|
5636
|
+
scheduledTotal: benchmark.batches.length * repetitions,
|
|
5637
|
+
contextOnlySkipped: contextOnlySkips.length,
|
|
5638
|
+
contextOnlySkips,
|
|
5603
5639
|
...attemptSummary,
|
|
5604
5640
|
summaryDispositionTotal: runs.filter((run) => run.quality != null).length,
|
|
5605
5641
|
summaryDispositionMatches: runs.filter((run) => run.quality?.summaryDisposition.score === 1).length,
|
|
@@ -5674,6 +5710,7 @@ function createMemoryExtractionBenchmarkCommand() {
|
|
|
5674
5710
|
`Temperature: ${observerSummary.transport === "mixed" ? "mixed" : observerSummary.temperature ?? "not transmitted"}`,
|
|
5675
5711
|
`Transcript budget override: ${transcriptBudget ?? "default"}`,
|
|
5676
5712
|
`Repetitions: ${summary.repetitions}`,
|
|
5713
|
+
`Context-only skips: ${summary.contextOnlySkipped} (${summary.scheduledTotal} scheduled; ${summary.total} observer attempts)`,
|
|
5677
5714
|
`Shape-quality passes: ${summary.shapeQualityPasses}/${summary.shapeQualityTotal}`,
|
|
5678
5715
|
`Shape-quality fails: ${summary.shapeQualityFails}`,
|
|
5679
5716
|
`Expected-tier matches: ${summary.expectedTierMatches}/${summary.expectedTierTotal}`,
|
|
@@ -5687,6 +5724,7 @@ function createMemoryExtractionBenchmarkCommand() {
|
|
|
5687
5724
|
`Tokens input/output: ${summary.output.overall.inputTokens}/${summary.output.overall.outputTokens} (${summary.output.overall.knownTokenRuns} measured run(s)); retained observations: ${summary.output.overall.retainedObservations}`,
|
|
5688
5725
|
`Summary disposition matches: ${summary.output.overall.summaryDispositionMatches}/${summary.output.overall.summaryDispositionEvaluated}; contract integrity/semantic quality: ${summary.output.overall.contractIntegrityRate?.toFixed(3) ?? "n/a"}/${summary.output.overall.meanSemanticQuality?.toFixed(3) ?? "n/a"}`
|
|
5689
5726
|
].join("\n"));
|
|
5727
|
+
for (const skipped of contextOnlySkips) p.log.message(` [${skipped.batchId}#${skipped.iteration}] context_only — ${skipped.code}; not evaluated`);
|
|
5690
5728
|
for (const run of runs) {
|
|
5691
5729
|
const qualityLabel = run.quality?.weightedQualityScore == null ? "n/a" : run.quality.weightedQualityScore.toFixed(3);
|
|
5692
5730
|
const costLabel = run.cost.total == null ? "n/a" : `$${run.cost.total.totalCostUsd.toFixed(6)}`;
|
|
@@ -5934,6 +5972,8 @@ function describeCandidate(candidate) {
|
|
|
5934
5972
|
if (candidate.section) lines.push(` - section: ${candidate.section}`);
|
|
5935
5973
|
if (candidate.reasons.length > 0) lines.push(` - reasons: ${candidate.reasons.join(", ")}`);
|
|
5936
5974
|
if (scoreParts) lines.push(` - scores: ${scoreParts}`);
|
|
5975
|
+
const fusion = candidate.scores.fusion;
|
|
5976
|
+
if (fusion) lines.push(` - hybrid: fused_score=${fusion.fused_score.toPrecision(8)} fusion_rank=${fusion.fusion_rank}; combined_score is a legacy diagnostic, not the hybrid selection key`);
|
|
5937
5977
|
if (candidate.preview) lines.push(` - preview: ${candidate.preview}`);
|
|
5938
5978
|
return lines;
|
|
5939
5979
|
}
|
|
@@ -7065,6 +7105,32 @@ var serveCommand = serveCmd.action(async (action, opts) => {
|
|
|
7065
7105
|
});
|
|
7066
7106
|
//#endregion
|
|
7067
7107
|
//#region src/commands/setup-config.ts
|
|
7108
|
+
var OPENCODE_PLUGIN_SPEC = "@codemem/opencode-plugin";
|
|
7109
|
+
var LEGACY_OPENCODE_PLUGIN_SPECS = ["codemem", "@kunickiaj/codemem"];
|
|
7110
|
+
function matchesPluginSpec(entry, spec) {
|
|
7111
|
+
return typeof entry === "string" && (entry === spec || entry.startsWith(`${spec}@`));
|
|
7112
|
+
}
|
|
7113
|
+
function reconcileOpencodePluginConfig(config, { force }) {
|
|
7114
|
+
const plugins = Array.isArray(config.plugin) ? config.plugin : [];
|
|
7115
|
+
const isCanonicalSpec = (entry) => matchesPluginSpec(entry, OPENCODE_PLUGIN_SPEC);
|
|
7116
|
+
const isLegacySpec = (entry) => LEGACY_OPENCODE_PLUGIN_SPECS.some((spec) => matchesPluginSpec(entry, spec));
|
|
7117
|
+
const hasCanonicalSpec = plugins.some(isCanonicalSpec);
|
|
7118
|
+
const hasLegacySpec = plugins.some(isLegacySpec);
|
|
7119
|
+
if (hasCanonicalSpec && !hasLegacySpec && !force) return {
|
|
7120
|
+
config,
|
|
7121
|
+
changed: false,
|
|
7122
|
+
removedLegacy: false
|
|
7123
|
+
};
|
|
7124
|
+
const preserved = plugins.filter((entry) => !isCanonicalSpec(entry) && !isLegacySpec(entry));
|
|
7125
|
+
return {
|
|
7126
|
+
config: {
|
|
7127
|
+
...config,
|
|
7128
|
+
plugin: [...preserved, OPENCODE_PLUGIN_SPEC]
|
|
7129
|
+
},
|
|
7130
|
+
changed: true,
|
|
7131
|
+
removedLegacy: hasLegacySpec
|
|
7132
|
+
};
|
|
7133
|
+
}
|
|
7068
7134
|
function resolveOpencodeConfigPath(configDir) {
|
|
7069
7135
|
const jsonPath = join(configDir, "opencode.json");
|
|
7070
7136
|
if (existsSync(jsonPath)) return jsonPath;
|
|
@@ -7110,9 +7176,6 @@ function claudeConfigDir() {
|
|
|
7110
7176
|
function codexConfigDir() {
|
|
7111
7177
|
return process.env.CODEX_HOME?.trim() || join(homedir(), ".codex");
|
|
7112
7178
|
}
|
|
7113
|
-
/** The npm package name used in the OpenCode plugin array. */
|
|
7114
|
-
var OPENCODE_PLUGIN_SPEC = "@codemem/opencode-plugin";
|
|
7115
|
-
var LEGACY_OPENCODE_PLUGIN_SPECS = ["codemem", "@kunickiaj/codemem"];
|
|
7116
7179
|
/** Remove legacy copied plugin JS file from ~/.config/opencode/plugins/codemem.js */
|
|
7117
7180
|
function migrateLegacyOpencodePlugin() {
|
|
7118
7181
|
const legacyPlugin = join(opencodeConfigDir(), "plugins", "codemem.js");
|
|
@@ -7196,21 +7259,14 @@ function installPlugin(force) {
|
|
|
7196
7259
|
p.log.error(`Failed to parse ${configPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
7197
7260
|
return false;
|
|
7198
7261
|
}
|
|
7199
|
-
|
|
7200
|
-
if (!
|
|
7201
|
-
const isManagedPluginSpec = (entry) => typeof entry === "string" && [OPENCODE_PLUGIN_SPEC, ...LEGACY_OPENCODE_PLUGIN_SPECS].some((spec) => entry === spec || entry.startsWith(`${spec}@`));
|
|
7202
|
-
const hasCanonicalSpec = plugins.some((entry) => typeof entry === "string" && (entry === OPENCODE_PLUGIN_SPEC || entry.startsWith(`${OPENCODE_PLUGIN_SPEC}@`)));
|
|
7203
|
-
const hasLegacySpec = plugins.some((entry) => typeof entry === "string" && LEGACY_OPENCODE_PLUGIN_SPECS.some((spec) => entry === spec || entry.startsWith(`${spec}@`)));
|
|
7204
|
-
if (hasCanonicalSpec && !hasLegacySpec && !force) {
|
|
7262
|
+
const result = reconcileOpencodePluginConfig(config, { force });
|
|
7263
|
+
if (!result.changed) {
|
|
7205
7264
|
p.log.info(`Plugin "${OPENCODE_PLUGIN_SPEC}" already in plugin array`);
|
|
7206
7265
|
return true;
|
|
7207
7266
|
}
|
|
7208
|
-
|
|
7209
|
-
if (hasLegacySpec) p.log.step("Removed legacy OpenCode plugin spec(s): codemem / @kunickiaj/codemem");
|
|
7210
|
-
plugins.push(OPENCODE_PLUGIN_SPEC);
|
|
7211
|
-
config.plugin = plugins;
|
|
7267
|
+
if (result.removedLegacy) p.log.step("Removed legacy OpenCode plugin spec(s): codemem / @kunickiaj/codemem");
|
|
7212
7268
|
try {
|
|
7213
|
-
writeJsonConfig(configPath, config);
|
|
7269
|
+
writeJsonConfig(configPath, result.config);
|
|
7214
7270
|
p.log.success(`Plugin "${OPENCODE_PLUGIN_SPEC}" added to ${configPath}`);
|
|
7215
7271
|
} catch (err) {
|
|
7216
7272
|
p.log.error(`Failed to write ${configPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -9083,7 +9139,64 @@ var INSTALL_TIMEOUT_MS = 42e4;
|
|
|
9083
9139
|
var VERIFY_TIMEOUT_MS = 3e4;
|
|
9084
9140
|
var PUBLIC_NPM_REGISTRY = "https://registry.npmjs.org/";
|
|
9085
9141
|
var UPDATE_LOCK_FILE = "update-install.lock";
|
|
9142
|
+
var UPDATE_COMMAND_MAX_OUTPUT_BYTES = 65536;
|
|
9086
9143
|
var UpdateInstallLockedError = class extends Error {};
|
|
9144
|
+
var PnpmOwnershipError = class extends Error {};
|
|
9145
|
+
function commandStderr(outputExceeded, timedOut, stderr) {
|
|
9146
|
+
if (outputExceeded) return "command output too large";
|
|
9147
|
+
if (timedOut) return "command timed out";
|
|
9148
|
+
return stderr;
|
|
9149
|
+
}
|
|
9150
|
+
function createOutputCapture(maxOutputBytes) {
|
|
9151
|
+
let exceeded = false;
|
|
9152
|
+
let outputBytes = 0;
|
|
9153
|
+
let stderr = "";
|
|
9154
|
+
let stdout = "";
|
|
9155
|
+
return {
|
|
9156
|
+
append(stream, chunk) {
|
|
9157
|
+
if (exceeded) return;
|
|
9158
|
+
outputBytes += Buffer.byteLength(chunk);
|
|
9159
|
+
if (maxOutputBytes !== void 0 && outputBytes > maxOutputBytes) {
|
|
9160
|
+
exceeded = true;
|
|
9161
|
+
return;
|
|
9162
|
+
}
|
|
9163
|
+
if (stream === "stderr") stderr += chunk;
|
|
9164
|
+
else stdout += chunk;
|
|
9165
|
+
},
|
|
9166
|
+
exceeded: () => exceeded,
|
|
9167
|
+
stderr: () => stderr,
|
|
9168
|
+
stdout: () => stdout
|
|
9169
|
+
};
|
|
9170
|
+
}
|
|
9171
|
+
function terminateWindowsProcessTree(pid) {
|
|
9172
|
+
if (process.platform !== "win32" || !pid) return false;
|
|
9173
|
+
const systemRoot = process.env.SystemRoot?.trim();
|
|
9174
|
+
if (!systemRoot || !isAbsolute(systemRoot)) return false;
|
|
9175
|
+
spawnSync(join(systemRoot, "System32", "taskkill.exe"), [
|
|
9176
|
+
"/PID",
|
|
9177
|
+
String(pid),
|
|
9178
|
+
"/T",
|
|
9179
|
+
"/F"
|
|
9180
|
+
], {
|
|
9181
|
+
stdio: "ignore",
|
|
9182
|
+
windowsHide: true
|
|
9183
|
+
});
|
|
9184
|
+
return true;
|
|
9185
|
+
}
|
|
9186
|
+
function terminatePosixProcessGroup(pid, signal) {
|
|
9187
|
+
if (process.platform === "win32" || !pid) return false;
|
|
9188
|
+
try {
|
|
9189
|
+
process.kill(-pid, signal);
|
|
9190
|
+
return true;
|
|
9191
|
+
} catch {
|
|
9192
|
+
return false;
|
|
9193
|
+
}
|
|
9194
|
+
}
|
|
9195
|
+
function terminateCommand(child, signal) {
|
|
9196
|
+
if (terminateWindowsProcessTree(child.pid)) return;
|
|
9197
|
+
if (terminatePosixProcessGroup(child.pid, signal)) return;
|
|
9198
|
+
child.kill(signal);
|
|
9199
|
+
}
|
|
9087
9200
|
function updateInstallLockPath() {
|
|
9088
9201
|
return join(process.env.HOME?.trim() || homedir(), ".codemem", UPDATE_LOCK_FILE);
|
|
9089
9202
|
}
|
|
@@ -9143,8 +9256,7 @@ function runCommand(command, args, timeoutMs, options = {}) {
|
|
|
9143
9256
|
});
|
|
9144
9257
|
let settled = false;
|
|
9145
9258
|
let timedOut = false;
|
|
9146
|
-
|
|
9147
|
-
let stderr = "";
|
|
9259
|
+
const output = createOutputCapture(options.maxOutputBytes);
|
|
9148
9260
|
let timer;
|
|
9149
9261
|
const finish = (result) => {
|
|
9150
9262
|
if (settled) return;
|
|
@@ -9152,28 +9264,7 @@ function runCommand(command, args, timeoutMs, options = {}) {
|
|
|
9152
9264
|
clearTimeout(timer);
|
|
9153
9265
|
resolve(result);
|
|
9154
9266
|
};
|
|
9155
|
-
const terminate = (signal) =>
|
|
9156
|
-
if (process.platform === "win32" && child.pid) {
|
|
9157
|
-
const systemRoot = process.env.SystemRoot?.trim();
|
|
9158
|
-
if (systemRoot && isAbsolute(systemRoot)) {
|
|
9159
|
-
spawnSync(join(systemRoot, "System32", "taskkill.exe"), [
|
|
9160
|
-
"/PID",
|
|
9161
|
-
String(child.pid),
|
|
9162
|
-
"/T",
|
|
9163
|
-
"/F"
|
|
9164
|
-
], {
|
|
9165
|
-
stdio: "ignore",
|
|
9166
|
-
windowsHide: true
|
|
9167
|
-
});
|
|
9168
|
-
return;
|
|
9169
|
-
}
|
|
9170
|
-
}
|
|
9171
|
-
if (process.platform !== "win32" && child.pid) try {
|
|
9172
|
-
process.kill(-child.pid, signal);
|
|
9173
|
-
return;
|
|
9174
|
-
} catch {}
|
|
9175
|
-
child.kill(signal);
|
|
9176
|
-
};
|
|
9267
|
+
const terminate = (signal) => terminateCommand(child, signal);
|
|
9177
9268
|
timer = setTimeout(() => {
|
|
9178
9269
|
timedOut = true;
|
|
9179
9270
|
terminate("SIGTERM");
|
|
@@ -9182,34 +9273,43 @@ function runCommand(command, args, timeoutMs, options = {}) {
|
|
|
9182
9273
|
child.stdout.setEncoding("utf8");
|
|
9183
9274
|
child.stderr.setEncoding("utf8");
|
|
9184
9275
|
child.stdout.on("data", (chunk) => {
|
|
9185
|
-
stdout
|
|
9276
|
+
output.append("stdout", chunk);
|
|
9186
9277
|
});
|
|
9187
9278
|
child.stderr.on("data", (chunk) => {
|
|
9188
|
-
stderr
|
|
9279
|
+
output.append("stderr", chunk);
|
|
9189
9280
|
});
|
|
9190
9281
|
child.once("error", (error) => {
|
|
9191
9282
|
clearTimeout(timer);
|
|
9192
9283
|
if (!settled) reject(error);
|
|
9193
9284
|
});
|
|
9194
|
-
child.once("close", (code) =>
|
|
9195
|
-
|
|
9196
|
-
|
|
9197
|
-
|
|
9198
|
-
|
|
9285
|
+
child.once("close", (code) => {
|
|
9286
|
+
const stderr = commandStderr(output.exceeded(), timedOut, output.stderr());
|
|
9287
|
+
finish({
|
|
9288
|
+
exitCode: code ?? 1,
|
|
9289
|
+
outputExceeded: output.exceeded(),
|
|
9290
|
+
stdout: output.stdout(),
|
|
9291
|
+
stderr
|
|
9292
|
+
});
|
|
9293
|
+
});
|
|
9199
9294
|
});
|
|
9200
9295
|
}
|
|
9201
9296
|
async function resolveWindowsShim(name) {
|
|
9202
9297
|
const systemRoot = process.env.SystemRoot?.trim();
|
|
9203
9298
|
if (!systemRoot || !isAbsolute(systemRoot)) throw new Error("Windows SystemRoot is unavailable");
|
|
9204
|
-
const result = await runCommand(join(systemRoot, "System32", "where.exe"), [name], 1e4, {
|
|
9299
|
+
const result = await runCommand(join(systemRoot, "System32", "where.exe"), [name], 1e4, {
|
|
9300
|
+
cwd: join(systemRoot, "System32"),
|
|
9301
|
+
maxOutputBytes: UPDATE_COMMAND_MAX_OUTPUT_BYTES
|
|
9302
|
+
});
|
|
9303
|
+
if (result.outputExceeded) throw new Error(`unable to resolve ${name}: where.exe output too large`);
|
|
9205
9304
|
const shim = result.stdout.split(/\r?\n/).map((value) => value.trim()).find((value) => isAbsolute(value));
|
|
9206
|
-
if (
|
|
9305
|
+
if (!shim) throw Object.assign(/* @__PURE__ */ new Error(`unable to resolve ${name} from PATH`), { code: "ENOENT" });
|
|
9306
|
+
if (result.exitCode !== 0) throw new Error(`where.exe failed while resolving ${name}`);
|
|
9207
9307
|
return shim;
|
|
9208
9308
|
}
|
|
9209
9309
|
function windowsCommandLine(shim, args) {
|
|
9210
9310
|
return `""${shim}" ${args.join(" ")}"`;
|
|
9211
9311
|
}
|
|
9212
|
-
async function
|
|
9312
|
+
async function resolveNpmInstallCommand() {
|
|
9213
9313
|
if (process.platform !== "win32") return {
|
|
9214
9314
|
command: "npm",
|
|
9215
9315
|
args: []
|
|
@@ -9227,6 +9327,31 @@ async function resolveInstallCommand() {
|
|
|
9227
9327
|
cwd: join(systemRoot, "System32")
|
|
9228
9328
|
};
|
|
9229
9329
|
}
|
|
9330
|
+
async function resolvePnpmCommand() {
|
|
9331
|
+
if (process.platform !== "win32") return {
|
|
9332
|
+
command: "pnpm",
|
|
9333
|
+
args: []
|
|
9334
|
+
};
|
|
9335
|
+
const systemRoot = process.env.SystemRoot?.trim();
|
|
9336
|
+
if (!systemRoot || !isAbsolute(systemRoot)) throw new Error("Windows SystemRoot is unavailable");
|
|
9337
|
+
return {
|
|
9338
|
+
command: join(systemRoot, "System32", "cmd.exe"),
|
|
9339
|
+
args: [
|
|
9340
|
+
"/d",
|
|
9341
|
+
"/s",
|
|
9342
|
+
"/c",
|
|
9343
|
+
await resolveWindowsShim("pnpm.cmd")
|
|
9344
|
+
],
|
|
9345
|
+
cwd: join(systemRoot, "System32"),
|
|
9346
|
+
windowsVerbatimArguments: true
|
|
9347
|
+
};
|
|
9348
|
+
}
|
|
9349
|
+
function resolvedCommandArgs(command, args) {
|
|
9350
|
+
if (process.platform !== "win32") return args;
|
|
9351
|
+
const shim = command.args[3];
|
|
9352
|
+
if (!shim) throw new Error("Windows command shim is unavailable");
|
|
9353
|
+
return [...command.args.slice(0, 3), windowsCommandLine(shim, args)];
|
|
9354
|
+
}
|
|
9230
9355
|
async function resolveVerificationCommand() {
|
|
9231
9356
|
if (process.platform !== "win32") return {
|
|
9232
9357
|
command: "codemem",
|
|
@@ -9260,6 +9385,7 @@ function renderStatusWarning(error) {
|
|
|
9260
9385
|
}
|
|
9261
9386
|
function renderHumanStatus(status) {
|
|
9262
9387
|
const warning = renderStatusWarning(status.error);
|
|
9388
|
+
if (status.install_kind === "repo-dev") return `Running from repository source (package metadata: ${status.current_version}). ${status.recommended_action}${warning}`;
|
|
9263
9389
|
if (status.update_available) return `Update available${cacheQualifier(status.stale)}: ${status.current_version} → ${status.latest_version}. ${status.recommended_action}${warning}`;
|
|
9264
9390
|
if (!status.channel || !isReleaseVersionForChannel(status.current_version, status.channel)) return `Unable to compare current version ${status.current_version} with ${status.latest_version}. ${status.recommended_action}${warning}`;
|
|
9265
9391
|
return `${status.current_version} is up to date${cacheQualifier(status.stale)}.${warning}`;
|
|
@@ -9311,56 +9437,487 @@ function updateInstallArgs(npmArgs, targetVersion) {
|
|
|
9311
9437
|
if (process.platform !== "win32") return installArgs;
|
|
9312
9438
|
return [...npmArgs.slice(0, 3), windowsCommandLine(npmArgs[3] ?? "", installArgs)];
|
|
9313
9439
|
}
|
|
9314
|
-
|
|
9315
|
-
|
|
9440
|
+
function packageManagerEnvironment() {
|
|
9441
|
+
const env = { ...process.env };
|
|
9442
|
+
const protectedKeys = /* @__PURE__ */ new Set(["npm_config_registry", "npm_config_@codemem:registry"]);
|
|
9443
|
+
if (process.platform === "linux") protectedKeys.add("onnxruntime_node_install");
|
|
9444
|
+
for (const key of Object.keys(env)) if (protectedKeys.has(key.toLowerCase())) delete env[key];
|
|
9445
|
+
env.npm_config_registry = PUBLIC_NPM_REGISTRY;
|
|
9446
|
+
env["npm_config_@codemem:registry"] = PUBLIC_NPM_REGISTRY;
|
|
9447
|
+
if (process.platform === "linux") env.ONNXRUNTIME_NODE_INSTALL = "skip";
|
|
9448
|
+
return env;
|
|
9449
|
+
}
|
|
9450
|
+
function miseGlobalCwd() {
|
|
9451
|
+
if (process.platform !== "win32") return "/";
|
|
9452
|
+
const systemRoot = process.env.SystemRoot?.trim();
|
|
9453
|
+
if (systemRoot) {
|
|
9454
|
+
const root = win32.parse(systemRoot).root;
|
|
9455
|
+
if (root) return root;
|
|
9456
|
+
}
|
|
9457
|
+
return process.env.HOME?.trim() || homedir();
|
|
9458
|
+
}
|
|
9459
|
+
function updateInstallCwd() {
|
|
9460
|
+
return dirname(updateInstallLockPath());
|
|
9461
|
+
}
|
|
9462
|
+
async function resolveUpdateInstallCommand(installKind, targetVersion, pnpmCommand) {
|
|
9463
|
+
if (installKind === "mise") return {
|
|
9464
|
+
command: "mise",
|
|
9465
|
+
args: [
|
|
9466
|
+
"use",
|
|
9467
|
+
"-g",
|
|
9468
|
+
`npm:codemem@${targetVersion}`
|
|
9469
|
+
],
|
|
9470
|
+
cwd: miseGlobalCwd(),
|
|
9471
|
+
env: packageManagerEnvironment()
|
|
9472
|
+
};
|
|
9473
|
+
if (installKind === "pnpm-global") {
|
|
9474
|
+
const pnpm = pnpmCommand ?? await resolvePnpmCommand();
|
|
9475
|
+
return {
|
|
9476
|
+
...pnpm,
|
|
9477
|
+
args: resolvedCommandArgs(pnpm, [
|
|
9478
|
+
"add",
|
|
9479
|
+
"-g",
|
|
9480
|
+
"--registry",
|
|
9481
|
+
PUBLIC_NPM_REGISTRY,
|
|
9482
|
+
`--config.@codemem:registry=${PUBLIC_NPM_REGISTRY}`,
|
|
9483
|
+
`codemem@${targetVersion}`,
|
|
9484
|
+
`@codemem/embeddings@${targetVersion}`
|
|
9485
|
+
]),
|
|
9486
|
+
cwd: updateInstallCwd(),
|
|
9487
|
+
env: packageManagerEnvironment()
|
|
9488
|
+
};
|
|
9489
|
+
}
|
|
9490
|
+
const npm = await resolveNpmInstallCommand();
|
|
9491
|
+
return {
|
|
9492
|
+
command: npm.command,
|
|
9493
|
+
args: updateInstallArgs(npm.args, targetVersion),
|
|
9494
|
+
cwd: npm.cwd,
|
|
9495
|
+
env: process.platform === "linux" ? {
|
|
9496
|
+
...process.env,
|
|
9497
|
+
ONNXRUNTIME_NODE_INSTALL: "skip"
|
|
9498
|
+
} : void 0,
|
|
9499
|
+
windowsVerbatimArguments: process.platform === "win32"
|
|
9500
|
+
};
|
|
9501
|
+
}
|
|
9502
|
+
function installLaunchErrorMessage(installKind, targetVersion, error) {
|
|
9503
|
+
if ((installKind === "mise" || installKind === "pnpm-global") && error instanceof Error && error.code === "ENOENT") {
|
|
9504
|
+
if (installKind === "pnpm-global") return "pnpm was not found on PATH; install pnpm, then retry codemem update install";
|
|
9505
|
+
return `mise was not found on PATH; install mise, then run mise use -g npm:codemem@${targetVersion}`;
|
|
9506
|
+
}
|
|
9507
|
+
return error instanceof Error ? error.message : "update installation failed";
|
|
9508
|
+
}
|
|
9509
|
+
function failedInstallMessage(installKind) {
|
|
9510
|
+
if (installKind === "mise") return "mise installation failed";
|
|
9511
|
+
if (installKind === "pnpm-global") return "pnpm installation failed";
|
|
9512
|
+
return "npm installation failed";
|
|
9513
|
+
}
|
|
9514
|
+
async function runUpdateInstallCommand(installKind, targetVersion, pnpmCommand) {
|
|
9515
|
+
const install = await resolveUpdateInstallCommand(installKind, targetVersion, pnpmCommand);
|
|
9316
9516
|
try {
|
|
9317
|
-
|
|
9318
|
-
|
|
9319
|
-
env:
|
|
9517
|
+
return await runCommand(install.command, install.args, INSTALL_TIMEOUT_MS, {
|
|
9518
|
+
cwd: install.cwd,
|
|
9519
|
+
env: install.env,
|
|
9520
|
+
maxOutputBytes: UPDATE_COMMAND_MAX_OUTPUT_BYTES,
|
|
9521
|
+
windowsVerbatimArguments: install.windowsVerbatimArguments
|
|
9320
9522
|
});
|
|
9321
|
-
|
|
9322
|
-
|
|
9323
|
-
|
|
9324
|
-
|
|
9523
|
+
} catch (error) {
|
|
9524
|
+
throw new Error(installLaunchErrorMessage(installKind, targetVersion, error));
|
|
9525
|
+
}
|
|
9526
|
+
}
|
|
9527
|
+
function parseMiseSourcePath(source) {
|
|
9528
|
+
if (!source || typeof source !== "object" || Array.isArray(source)) return null;
|
|
9529
|
+
const sourcePath = source.path;
|
|
9530
|
+
return typeof sourcePath === "string" && sourcePath ? sourcePath : null;
|
|
9531
|
+
}
|
|
9532
|
+
function parseMiseStateRecord(record) {
|
|
9533
|
+
if (!record || typeof record !== "object" || Array.isArray(record)) return null;
|
|
9534
|
+
const candidate = record;
|
|
9535
|
+
const sourcePath = parseMiseSourcePath(candidate.source);
|
|
9536
|
+
const version = candidate.version;
|
|
9537
|
+
const requestedVersion = candidate.requested_version;
|
|
9538
|
+
if (typeof candidate.active !== "boolean" || typeof candidate.install_path !== "string" || !candidate.install_path || version !== void 0 && (typeof version !== "string" || !version) || requestedVersion !== void 0 && (typeof requestedVersion !== "string" || !requestedVersion) || typeof version !== "string" && typeof requestedVersion !== "string" || !sourcePath) return null;
|
|
9539
|
+
return {
|
|
9540
|
+
active: candidate.active,
|
|
9541
|
+
installPath: candidate.install_path,
|
|
9542
|
+
requestedVersion: typeof requestedVersion === "string" ? requestedVersion : null,
|
|
9543
|
+
sourceIdentity: resolveComparablePath(sourcePath),
|
|
9544
|
+
sourcePath,
|
|
9545
|
+
version: typeof version === "string" ? version : null
|
|
9546
|
+
};
|
|
9547
|
+
}
|
|
9548
|
+
function parseMiseState(raw) {
|
|
9549
|
+
if (Buffer.byteLength(raw) > UPDATE_COMMAND_MAX_OUTPUT_BYTES) return null;
|
|
9550
|
+
let payload;
|
|
9551
|
+
try {
|
|
9552
|
+
payload = JSON.parse(raw);
|
|
9553
|
+
} catch {
|
|
9554
|
+
return null;
|
|
9555
|
+
}
|
|
9556
|
+
if (!Array.isArray(payload)) return null;
|
|
9557
|
+
if (payload.length === 0) return {
|
|
9558
|
+
empty: true,
|
|
9559
|
+
state: null
|
|
9560
|
+
};
|
|
9561
|
+
if (payload.length !== 1) return null;
|
|
9562
|
+
const state = parseMiseStateRecord(payload[0]);
|
|
9563
|
+
return state ? {
|
|
9564
|
+
empty: false,
|
|
9565
|
+
state
|
|
9566
|
+
} : null;
|
|
9567
|
+
}
|
|
9568
|
+
async function readMiseState(args, targetVersion, options = {}) {
|
|
9569
|
+
try {
|
|
9570
|
+
const result = await runCommand("mise", args, VERIFY_TIMEOUT_MS, {
|
|
9571
|
+
cwd: options.cwd,
|
|
9572
|
+
env: packageManagerEnvironment(),
|
|
9573
|
+
maxOutputBytes: UPDATE_COMMAND_MAX_OUTPUT_BYTES
|
|
9325
9574
|
});
|
|
9326
|
-
if (
|
|
9327
|
-
|
|
9328
|
-
|
|
9329
|
-
|
|
9330
|
-
|
|
9331
|
-
|
|
9332
|
-
|
|
9333
|
-
|
|
9334
|
-
|
|
9335
|
-
|
|
9336
|
-
|
|
9337
|
-
|
|
9338
|
-
|
|
9339
|
-
|
|
9340
|
-
|
|
9341
|
-
|
|
9342
|
-
|
|
9343
|
-
|
|
9575
|
+
if (result.exitCode !== 0 || result.outputExceeded) return null;
|
|
9576
|
+
return parseMiseState(result.stdout);
|
|
9577
|
+
} catch (error) {
|
|
9578
|
+
throw new Error(installLaunchErrorMessage("mise", targetVersion, error));
|
|
9579
|
+
}
|
|
9580
|
+
}
|
|
9581
|
+
function isPathWithin(path, root) {
|
|
9582
|
+
return path === root || path.startsWith(`${root}/`);
|
|
9583
|
+
}
|
|
9584
|
+
function isPathBelow(path, root) {
|
|
9585
|
+
return path.startsWith(`${root}/`);
|
|
9586
|
+
}
|
|
9587
|
+
function isPortableAbsolutePath(value) {
|
|
9588
|
+
return value.startsWith("/") || /^[A-Za-z]:[\\/]/.test(value);
|
|
9589
|
+
}
|
|
9590
|
+
function isSafeAbsoluteDirectory(value) {
|
|
9591
|
+
if (!isPortableAbsolutePath(value)) return false;
|
|
9592
|
+
const normalized = resolveComparablePath(value);
|
|
9593
|
+
return normalized !== "/" && !/^[a-z]:$/i.test(normalized);
|
|
9594
|
+
}
|
|
9595
|
+
function resolveExistingComparablePath(value) {
|
|
9596
|
+
try {
|
|
9597
|
+
return resolveComparablePath(realpathSync(value));
|
|
9598
|
+
} catch {
|
|
9599
|
+
return null;
|
|
9600
|
+
}
|
|
9601
|
+
}
|
|
9602
|
+
function parsePnpmPackageState(value) {
|
|
9603
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
9604
|
+
const record = value;
|
|
9605
|
+
if (typeof record.path !== "string" || !isPortableAbsolutePath(record.path) || typeof record.version !== "string" || !record.version) return null;
|
|
9606
|
+
return {
|
|
9607
|
+
path: record.path,
|
|
9608
|
+
version: record.version
|
|
9609
|
+
};
|
|
9610
|
+
}
|
|
9611
|
+
function parsePnpmGlobalState(raw) {
|
|
9612
|
+
if (Buffer.byteLength(raw) > UPDATE_COMMAND_MAX_OUTPUT_BYTES) return null;
|
|
9613
|
+
let payload;
|
|
9614
|
+
try {
|
|
9615
|
+
payload = JSON.parse(raw);
|
|
9616
|
+
} catch {
|
|
9617
|
+
return null;
|
|
9618
|
+
}
|
|
9619
|
+
if (!Array.isArray(payload) || payload.length !== 1) return null;
|
|
9620
|
+
const topLevel = payload[0];
|
|
9621
|
+
if (!topLevel || typeof topLevel !== "object" || Array.isArray(topLevel)) return null;
|
|
9622
|
+
const topLevelRecord = topLevel;
|
|
9623
|
+
if (typeof topLevelRecord.private !== "boolean" || typeof topLevelRecord.path !== "string" || !isPortableAbsolutePath(topLevelRecord.path)) return null;
|
|
9624
|
+
const dependencies = topLevelRecord.dependencies;
|
|
9625
|
+
if (!dependencies || typeof dependencies !== "object" || Array.isArray(dependencies)) return null;
|
|
9626
|
+
const records = dependencies;
|
|
9627
|
+
if (!Object.hasOwn(records, "codemem")) return null;
|
|
9628
|
+
const codemem = parsePnpmPackageState(records.codemem);
|
|
9629
|
+
if (!codemem) return null;
|
|
9630
|
+
if (!Object.hasOwn(records, "@codemem/embeddings")) return {
|
|
9631
|
+
codemem,
|
|
9632
|
+
embeddings: null,
|
|
9633
|
+
listRootPath: topLevelRecord.path
|
|
9634
|
+
};
|
|
9635
|
+
const embeddings = parsePnpmPackageState(records["@codemem/embeddings"]);
|
|
9636
|
+
return embeddings ? {
|
|
9637
|
+
codemem,
|
|
9638
|
+
embeddings,
|
|
9639
|
+
listRootPath: topLevelRecord.path
|
|
9640
|
+
} : null;
|
|
9641
|
+
}
|
|
9642
|
+
function parsePnpmDirectory(raw) {
|
|
9643
|
+
if (Buffer.byteLength(raw) > UPDATE_COMMAND_MAX_OUTPUT_BYTES) return null;
|
|
9644
|
+
const directories = raw.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).filter(isSafeAbsoluteDirectory).map(resolveExistingComparablePath).filter((directory) => directory !== null);
|
|
9645
|
+
return directories.length === 1 ? directories[0] ?? null : null;
|
|
9646
|
+
}
|
|
9647
|
+
var PNPM_SETUP_GUIDANCE = "Run pnpm setup, then retry codemem update install";
|
|
9648
|
+
function isMissingPnpmGlobalBinDirectory(result) {
|
|
9649
|
+
const output = `${result.stdout}\n${result.stderr}`.toLowerCase();
|
|
9650
|
+
return output.includes("err_pnpm_global_bin_dir_not_in_path") || output.includes("configured global bin directory") && output.includes("is not in path");
|
|
9651
|
+
}
|
|
9652
|
+
async function runPnpmQuery(command, args) {
|
|
9653
|
+
try {
|
|
9654
|
+
return await runCommand(command.command, resolvedCommandArgs(command, args), VERIFY_TIMEOUT_MS, {
|
|
9655
|
+
cwd: updateInstallCwd(),
|
|
9656
|
+
maxOutputBytes: UPDATE_COMMAND_MAX_OUTPUT_BYTES,
|
|
9657
|
+
windowsVerbatimArguments: command.windowsVerbatimArguments
|
|
9344
9658
|
});
|
|
9345
|
-
|
|
9346
|
-
|
|
9347
|
-
|
|
9348
|
-
|
|
9349
|
-
|
|
9350
|
-
|
|
9351
|
-
|
|
9352
|
-
|
|
9659
|
+
} catch (error) {
|
|
9660
|
+
if (error instanceof Error && error.code === "ENOENT") throw new PnpmOwnershipError("pnpm was not found on PATH; install pnpm, then retry codemem update install");
|
|
9661
|
+
throw error;
|
|
9662
|
+
}
|
|
9663
|
+
}
|
|
9664
|
+
async function requirePnpmQuery(command, args) {
|
|
9665
|
+
const result = await runPnpmQuery(command, args);
|
|
9666
|
+
const label = `pnpm ${args.join(" ")}`;
|
|
9667
|
+
if (isMissingPnpmGlobalBinDirectory(result)) throw new PnpmOwnershipError(PNPM_SETUP_GUIDANCE);
|
|
9668
|
+
if (result.outputExceeded) throw new PnpmOwnershipError(`${label} returned too much output; inspect pnpm global state`);
|
|
9669
|
+
if (result.exitCode !== 0) throw new PnpmOwnershipError(`${label} failed${result.stderr.trim() ? `: ${result.stderr.trim()}` : "; inspect pnpm global configuration"}`);
|
|
9670
|
+
return result.stdout;
|
|
9671
|
+
}
|
|
9672
|
+
async function provePnpmGlobalOwnership(plan) {
|
|
9673
|
+
let command;
|
|
9674
|
+
try {
|
|
9675
|
+
command = await resolvePnpmCommand();
|
|
9676
|
+
} catch (error) {
|
|
9677
|
+
throw new PnpmOwnershipError(installLaunchErrorMessage("pnpm-global", plan.targetVersion, error));
|
|
9678
|
+
}
|
|
9679
|
+
const rootRaw = await requirePnpmQuery(command, ["root", "-g"]);
|
|
9680
|
+
const binRaw = await requirePnpmQuery(command, ["bin", "-g"]);
|
|
9681
|
+
const listRaw = await requirePnpmQuery(command, [
|
|
9682
|
+
"list",
|
|
9683
|
+
"-g",
|
|
9684
|
+
"--depth",
|
|
9685
|
+
"0",
|
|
9686
|
+
"--json"
|
|
9687
|
+
]);
|
|
9688
|
+
const rootPath = parsePnpmDirectory(rootRaw);
|
|
9689
|
+
if (!rootPath) throw new PnpmOwnershipError("pnpm root -g did not return one absolute non-root path");
|
|
9690
|
+
const binPath = parsePnpmDirectory(binRaw);
|
|
9691
|
+
if (!binPath) throw new PnpmOwnershipError("pnpm bin -g did not return one absolute non-root path");
|
|
9692
|
+
const state = parsePnpmGlobalState(listRaw);
|
|
9693
|
+
if (!state) throw new PnpmOwnershipError("pnpm list -g returned malformed or ambiguous global package state");
|
|
9694
|
+
if (state.codemem.version !== VERSION) throw new PnpmOwnershipError(`pnpm global codemem is ${state.codemem.version}, but the running CLI is ${VERSION}`);
|
|
9695
|
+
const listRootPath = resolveExistingComparablePath(state.listRootPath);
|
|
9696
|
+
const legacyModulesPath = listRootPath ? resolveExistingComparablePath(join(listRootPath, "node_modules")) : null;
|
|
9697
|
+
if (!listRootPath || rootPath !== listRootPath && rootPath !== legacyModulesPath) throw new PnpmOwnershipError("pnpm root -g is neither the pnpm list -g root nor its node_modules directory");
|
|
9698
|
+
const packagePath = resolveExistingComparablePath(state.codemem.path);
|
|
9699
|
+
if (!packagePath || !isPathBelow(packagePath, listRootPath)) throw new PnpmOwnershipError("pnpm list -g root does not own the listed codemem package path");
|
|
9700
|
+
const entryPath = plan.runningEntryPath;
|
|
9701
|
+
const canonicalEntryPath = entryPath ? resolveExistingComparablePath(entryPath) : null;
|
|
9702
|
+
if (!canonicalEntryPath || !isPathWithin(canonicalEntryPath, packagePath)) throw new PnpmOwnershipError("the pnpm global codemem package does not own the running JavaScript entry");
|
|
9703
|
+
return {
|
|
9704
|
+
binPath,
|
|
9705
|
+
command,
|
|
9706
|
+
listRootPath
|
|
9707
|
+
};
|
|
9708
|
+
}
|
|
9709
|
+
function isUserOwnedMiseSource(state) {
|
|
9710
|
+
if (!isPortableAbsolutePath(state.sourcePath)) return false;
|
|
9711
|
+
const sourcePath = resolveComparablePath(state.sourcePath);
|
|
9712
|
+
const homePath = resolveComparablePath(process.env.HOME?.trim() || homedir());
|
|
9713
|
+
if (homePath === "/" || /^[a-z]:$/i.test(homePath)) return false;
|
|
9714
|
+
return isPathWithin(sourcePath, homePath);
|
|
9715
|
+
}
|
|
9716
|
+
function isLegacyUserMiseSource(state) {
|
|
9717
|
+
if (!isUserOwnedMiseSource(state)) return false;
|
|
9718
|
+
const home = process.env.HOME?.trim() || homedir();
|
|
9719
|
+
const configHome = process.env.XDG_CONFIG_HOME?.trim() || join(home, ".config");
|
|
9720
|
+
return state.sourceIdentity === resolveComparablePath(join(configHome, "mise.toml"));
|
|
9721
|
+
}
|
|
9722
|
+
function miseStateOwnsEntry(state, runningEntryPath) {
|
|
9723
|
+
if (!runningEntryPath) return false;
|
|
9724
|
+
if (!isPortableAbsolutePath(state.installPath)) return false;
|
|
9725
|
+
const installPath = resolveComparablePath(state.installPath);
|
|
9726
|
+
const entryPath = resolveComparablePath(runningEntryPath);
|
|
9727
|
+
if (installPath === "/" || /^[a-z]:$/i.test(installPath)) return false;
|
|
9728
|
+
return entryPath === installPath || entryPath.startsWith(`${installPath}/`);
|
|
9729
|
+
}
|
|
9730
|
+
async function proveGlobalMiseOwnership(plan) {
|
|
9731
|
+
const activeState = (await readMiseState([
|
|
9732
|
+
"ls",
|
|
9733
|
+
"npm:codemem",
|
|
9734
|
+
"--current",
|
|
9735
|
+
"--json"
|
|
9736
|
+
], plan.targetVersion))?.state;
|
|
9737
|
+
if (!activeState?.active || !miseStateOwnsEntry(activeState, plan.runningEntryPath)) return null;
|
|
9738
|
+
const globalQuery = await readMiseState([
|
|
9739
|
+
"ls",
|
|
9740
|
+
"npm:codemem",
|
|
9741
|
+
"--global",
|
|
9742
|
+
"--json"
|
|
9743
|
+
], plan.targetVersion, { cwd: miseGlobalCwd() });
|
|
9744
|
+
if (globalQuery?.empty && isLegacyUserMiseSource(activeState)) return activeState;
|
|
9745
|
+
const globalState = globalQuery?.state;
|
|
9746
|
+
if (!globalState?.active || activeState.sourceIdentity !== globalState.sourceIdentity || !isUserOwnedMiseSource(globalState)) return null;
|
|
9747
|
+
return globalState;
|
|
9748
|
+
}
|
|
9749
|
+
async function resolveExplicitUpdatePlan(options) {
|
|
9750
|
+
const detectedInstallKind = detectInstallKind({
|
|
9751
|
+
entryPath: process.argv[1] ?? "",
|
|
9752
|
+
env: process.env
|
|
9753
|
+
});
|
|
9754
|
+
const status = await getUpdateStatus({
|
|
9755
|
+
currentVersion: VERSION,
|
|
9756
|
+
installKind: detectedInstallKind,
|
|
9757
|
+
refresh: true
|
|
9758
|
+
});
|
|
9759
|
+
if (!isExplicitUpdateInstallEligible(status) || !status.latest_version) {
|
|
9760
|
+
failInstall(options, "update_install_refused", status.recommended_action);
|
|
9761
|
+
return null;
|
|
9762
|
+
}
|
|
9763
|
+
if (!status.channel || !isReleaseVersionForChannel(status.latest_version, status.channel)) {
|
|
9764
|
+
failInstall(options, "update_install_refused", "release version does not match the installed channel");
|
|
9765
|
+
return null;
|
|
9766
|
+
}
|
|
9767
|
+
return {
|
|
9768
|
+
installKind: status.install_kind,
|
|
9769
|
+
manualGuidance: status.recommended_action,
|
|
9770
|
+
runningEntryPath: detectedInstallKind === "mise" || detectedInstallKind === "pnpm-global" ? resolveRunningEntryPath(process.argv[1] ?? "") : null,
|
|
9771
|
+
targetVersion: status.latest_version
|
|
9772
|
+
};
|
|
9773
|
+
}
|
|
9774
|
+
function resolveRunningEntryPath(entryPath) {
|
|
9775
|
+
return resolveComparablePath(entryPath);
|
|
9776
|
+
}
|
|
9777
|
+
async function verifyMiseInstalledVersion(plan) {
|
|
9778
|
+
const globalState = (await readMiseState([
|
|
9779
|
+
"ls",
|
|
9780
|
+
"npm:codemem",
|
|
9781
|
+
"--global",
|
|
9782
|
+
"--json"
|
|
9783
|
+
], plan.targetVersion, { cwd: miseGlobalCwd() }))?.state;
|
|
9784
|
+
if (!globalState?.active || globalState.version !== plan.targetVersion && globalState.requestedVersion !== plan.targetVersion) return false;
|
|
9785
|
+
const verification = await runCommand("mise", [
|
|
9786
|
+
"exec",
|
|
9787
|
+
"--",
|
|
9788
|
+
"codemem",
|
|
9789
|
+
"version"
|
|
9790
|
+
], VERIFY_TIMEOUT_MS, {
|
|
9791
|
+
cwd: miseGlobalCwd(),
|
|
9792
|
+
env: packageManagerEnvironment(),
|
|
9793
|
+
maxOutputBytes: UPDATE_COMMAND_MAX_OUTPUT_BYTES
|
|
9794
|
+
});
|
|
9795
|
+
return !verification.outputExceeded && verification.exitCode === 0 && verification.stdout.trim() === plan.targetVersion;
|
|
9796
|
+
}
|
|
9797
|
+
function pnpmPackageStateError(state, ownership, targetVersion) {
|
|
9798
|
+
if (!state.embeddings) return "pnpm update completed, but @codemem/embeddings is absent from global package state";
|
|
9799
|
+
if (state.codemem.version !== targetVersion || state.embeddings.version !== targetVersion) return `pnpm update completed, but global package versions do not both equal ${targetVersion}`;
|
|
9800
|
+
if (resolveExistingComparablePath(state.listRootPath) !== ownership.listRootPath) return "pnpm update completed, but the pnpm list -g root changed after ownership was proven";
|
|
9801
|
+
if ([state.codemem.path, state.embeddings.path].map(resolveExistingComparablePath).some((packagePath) => !packagePath || !isPathBelow(packagePath, ownership.listRootPath))) return "pnpm update completed, but the pnpm list -g root does not own both package paths";
|
|
9802
|
+
return null;
|
|
9803
|
+
}
|
|
9804
|
+
async function verifyPnpmOwnedLauncher(plan, ownership) {
|
|
9805
|
+
const shimName = process.platform === "win32" ? "codemem.cmd" : "codemem";
|
|
9806
|
+
const shimPath = join(ownership.binPath, shimName);
|
|
9807
|
+
let verification;
|
|
9808
|
+
try {
|
|
9809
|
+
if (process.platform === "win32") verification = await runCommand(ownership.command.command, [
|
|
9810
|
+
"/d",
|
|
9811
|
+
"/s",
|
|
9812
|
+
"/c",
|
|
9813
|
+
windowsCommandLine(shimPath, ["version"])
|
|
9814
|
+
], VERIFY_TIMEOUT_MS, {
|
|
9815
|
+
cwd: updateInstallCwd(),
|
|
9816
|
+
maxOutputBytes: UPDATE_COMMAND_MAX_OUTPUT_BYTES,
|
|
9817
|
+
windowsVerbatimArguments: true
|
|
9353
9818
|
});
|
|
9354
|
-
|
|
9355
|
-
|
|
9356
|
-
|
|
9357
|
-
}
|
|
9358
|
-
|
|
9359
|
-
|
|
9360
|
-
|
|
9361
|
-
|
|
9362
|
-
|
|
9363
|
-
|
|
9819
|
+
else verification = await runCommand(shimPath, ["version"], VERIFY_TIMEOUT_MS, {
|
|
9820
|
+
cwd: updateInstallCwd(),
|
|
9821
|
+
maxOutputBytes: UPDATE_COMMAND_MAX_OUTPUT_BYTES
|
|
9822
|
+
});
|
|
9823
|
+
} catch (error) {
|
|
9824
|
+
return `pnpm-owned codemem launcher could not be executed${error instanceof Error ? `: ${error.message}` : ""}`;
|
|
9825
|
+
}
|
|
9826
|
+
if (verification.outputExceeded) return "pnpm-owned codemem launcher returned too much output during version verification";
|
|
9827
|
+
if (verification.exitCode !== 0) return "pnpm-owned codemem launcher failed during version verification";
|
|
9828
|
+
const versionLines = verification.stdout.split(/\r?\n/).map((line) => line.trim()).filter((line) => /^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(line));
|
|
9829
|
+
if (versionLines.length !== 1 || versionLines[0] !== plan.targetVersion) return `pnpm-owned codemem launcher did not report ${plan.targetVersion}`;
|
|
9830
|
+
return null;
|
|
9831
|
+
}
|
|
9832
|
+
async function verifyPnpmInstalledVersion(plan, ownership) {
|
|
9833
|
+
let listRaw;
|
|
9834
|
+
try {
|
|
9835
|
+
listRaw = await requirePnpmQuery(ownership.command, [
|
|
9836
|
+
"list",
|
|
9837
|
+
"-g",
|
|
9838
|
+
"--depth",
|
|
9839
|
+
"0",
|
|
9840
|
+
"--json"
|
|
9841
|
+
]);
|
|
9842
|
+
} catch (error) {
|
|
9843
|
+
return error instanceof Error ? `pnpm update completed, but package-state verification failed: ${error.message}` : "pnpm update completed, but package-state verification failed";
|
|
9844
|
+
}
|
|
9845
|
+
const state = parsePnpmGlobalState(listRaw);
|
|
9846
|
+
if (!state) return "pnpm update completed, but global package state is malformed or ambiguous";
|
|
9847
|
+
const packageStateError = pnpmPackageStateError(state, ownership, plan.targetVersion);
|
|
9848
|
+
if (packageStateError) return packageStateError;
|
|
9849
|
+
return verifyPnpmOwnedLauncher(plan, ownership);
|
|
9850
|
+
}
|
|
9851
|
+
async function verifyInstalledVersion(plan) {
|
|
9852
|
+
if (plan.installKind === "mise") return verifyMiseInstalledVersion(plan);
|
|
9853
|
+
const codemem = await resolveVerificationCommand();
|
|
9854
|
+
const verificationArgs = process.platform === "win32" ? [...codemem.args.slice(0, 3), windowsCommandLine(codemem.args[3] ?? "", ["version"])] : ["version"];
|
|
9855
|
+
const verification = await runCommand(codemem.command, verificationArgs, VERIFY_TIMEOUT_MS, {
|
|
9856
|
+
cwd: codemem.cwd,
|
|
9857
|
+
maxOutputBytes: UPDATE_COMMAND_MAX_OUTPUT_BYTES,
|
|
9858
|
+
windowsVerbatimArguments: process.platform === "win32"
|
|
9859
|
+
});
|
|
9860
|
+
return !verification.outputExceeded && verification.exitCode === 0 && verification.stdout.trim() === plan.targetVersion;
|
|
9861
|
+
}
|
|
9862
|
+
function emitInstallSuccess(options, targetVersion) {
|
|
9863
|
+
const result = {
|
|
9864
|
+
previous_version: VERSION,
|
|
9865
|
+
installed_version: targetVersion
|
|
9866
|
+
};
|
|
9867
|
+
if (options.json) console.log(JSON.stringify(result));
|
|
9868
|
+
else console.log(`Updated codemem from ${VERSION} to ${targetVersion}.`);
|
|
9869
|
+
}
|
|
9870
|
+
async function resolvePnpmOwnershipForExecution(options, plan) {
|
|
9871
|
+
try {
|
|
9872
|
+
return await provePnpmGlobalOwnership(plan);
|
|
9873
|
+
} catch (error) {
|
|
9874
|
+
failInstall(options, "update_install_refused", error instanceof Error ? error.message : "could not prove pnpm global ownership");
|
|
9875
|
+
return null;
|
|
9876
|
+
}
|
|
9877
|
+
}
|
|
9878
|
+
async function executeExplicitUpdate(options, plan) {
|
|
9879
|
+
const globalMiseState = plan.installKind === "mise" ? await proveGlobalMiseOwnership(plan) : null;
|
|
9880
|
+
if (plan.installKind === "mise" && !globalMiseState) {
|
|
9881
|
+
failInstall(options, "update_install_refused", `Could not prove the active mise codemem installation is globally configured. ${plan.manualGuidance}`);
|
|
9882
|
+
return;
|
|
9883
|
+
}
|
|
9884
|
+
const pnpmOwnership = plan.installKind === "pnpm-global" ? await resolvePnpmOwnershipForExecution(options, plan) : null;
|
|
9885
|
+
if (plan.installKind === "pnpm-global" && !pnpmOwnership) return;
|
|
9886
|
+
if (pnpmOwnership) {
|
|
9887
|
+
await executePnpmUpdate(options, plan, pnpmOwnership);
|
|
9888
|
+
return;
|
|
9889
|
+
}
|
|
9890
|
+
const installation = await runUpdateInstallCommand(plan.installKind, plan.targetVersion);
|
|
9891
|
+
if (installation.exitCode !== 0 || installation.outputExceeded) {
|
|
9892
|
+
failInstall(options, "update_install_failed", installation.stderr.trim() || failedInstallMessage(plan.installKind));
|
|
9893
|
+
return;
|
|
9894
|
+
}
|
|
9895
|
+
if (!await verifyInstalledVersion(plan)) {
|
|
9896
|
+
failInstall(options, "update_verification_failed", plan.installKind === "mise" ? "mise updated global configuration, but installed version verification failed; inspect the global mise codemem state before retrying" : "installed version verification failed");
|
|
9897
|
+
return;
|
|
9898
|
+
}
|
|
9899
|
+
emitInstallSuccess(options, plan.targetVersion);
|
|
9900
|
+
}
|
|
9901
|
+
async function executePnpmUpdate(options, plan, ownership) {
|
|
9902
|
+
const installation = await runUpdateInstallCommand(plan.installKind, plan.targetVersion, ownership.command);
|
|
9903
|
+
if (installation.exitCode !== 0) {
|
|
9904
|
+
failInstall(options, "update_install_failed", isMissingPnpmGlobalBinDirectory(installation) ? PNPM_SETUP_GUIDANCE : installation.stderr.trim() || failedInstallMessage(plan.installKind));
|
|
9905
|
+
return;
|
|
9906
|
+
}
|
|
9907
|
+
const verificationError = await verifyPnpmInstalledVersion(plan, ownership);
|
|
9908
|
+
if (verificationError) {
|
|
9909
|
+
failInstall(options, "update_verification_failed", verificationError);
|
|
9910
|
+
return;
|
|
9911
|
+
}
|
|
9912
|
+
emitInstallSuccess(options, plan.targetVersion);
|
|
9913
|
+
}
|
|
9914
|
+
async function installUpdate(options) {
|
|
9915
|
+
let releaseInstallLock = null;
|
|
9916
|
+
try {
|
|
9917
|
+
const plan = await resolveExplicitUpdatePlan(options);
|
|
9918
|
+
if (!plan) return;
|
|
9919
|
+
releaseInstallLock = await acquireInstallLock();
|
|
9920
|
+
await executeExplicitUpdate(options, plan);
|
|
9364
9921
|
} catch (error) {
|
|
9365
9922
|
failInstall(options, error instanceof UpdateInstallLockedError ? "update_install_locked" : "update_install_failed", error instanceof Error ? error.message : "update installation failed");
|
|
9366
9923
|
} finally {
|