@cortexkit/magic-context 0.21.2 → 0.21.5

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.
Files changed (2) hide show
  1. package/dist/index.js +121 -17
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -27303,7 +27303,6 @@ var init_magic_context = __esm(() => {
27303
27303
  enabled: exports_external.boolean().default(true),
27304
27304
  min_clusters: exports_external.number().min(1).default(3)
27305
27305
  }).default({ enabled: true, min_clusters: 3 }),
27306
- compaction_markers: exports_external.boolean().default(true),
27307
27306
  system_prompt_injection: exports_external.object({
27308
27307
  enabled: exports_external.boolean().default(true),
27309
27308
  skip_signatures: exports_external.array(exports_external.string()).default(["<!-- magic-context: skip -->"])
@@ -27379,6 +27378,21 @@ import { dirname as dirname7, isAbsolute, resolve as resolve2 } from "node:path"
27379
27378
  function substituteConfigVariables(input) {
27380
27379
  const warnings = [];
27381
27380
  let text2 = input.text;
27381
+ if (input.isProjectConfig) {
27382
+ const hasEnvTokens = ENV_PATTERN.test(text2);
27383
+ const hasFileTokens = FILE_PATTERN.test(text2);
27384
+ ENV_PATTERN.lastIndex = 0;
27385
+ FILE_PATTERN.lastIndex = 0;
27386
+ if (hasEnvTokens || hasFileTokens) {
27387
+ const tokenTypes = [
27388
+ hasEnvTokens ? "{env:}" : undefined,
27389
+ hasFileTokens ? "{file:}" : undefined
27390
+ ].filter(Boolean).join(" and ");
27391
+ warnings.push(`Project-level config no longer supports ${tokenTypes} tokens for security reasons; leaving tokens literal. Move secret expansion to user-level config.`);
27392
+ }
27393
+ return { text: text2, warnings };
27394
+ }
27395
+ text2 = stripJsonComments(text2);
27382
27396
  text2 = text2.replace(ENV_PATTERN, (_, rawName) => {
27383
27397
  const varName = rawName.trim();
27384
27398
  const value = varName ? process.env[varName] : undefined;
@@ -27386,7 +27400,7 @@ function substituteConfigVariables(input) {
27386
27400
  warnings.push(`Environment variable ${varName} is not set (referenced via {env:${varName}}); using empty string`);
27387
27401
  return "";
27388
27402
  }
27389
- return value;
27403
+ return JSON.stringify(value).slice(1, -1);
27390
27404
  });
27391
27405
  const fileMatches = Array.from(text2.matchAll(FILE_PATTERN));
27392
27406
  if (fileMatches.length === 0) {
@@ -27433,6 +27447,7 @@ function substituteConfigVariables(input) {
27433
27447
  }
27434
27448
  var ENV_PATTERN, FILE_PATTERN;
27435
27449
  var init_variable = __esm(() => {
27450
+ init_jsonc_parser();
27436
27451
  ENV_PATTERN = /\{env:([^}]+)\}/g;
27437
27452
  FILE_PATTERN = /\{file:([^}]+)\}/g;
27438
27453
  });
@@ -27448,13 +27463,17 @@ function getUserConfigBasePath() {
27448
27463
  function getProjectConfigBasePath(directory) {
27449
27464
  return join11(directory, ".opencode", CONFIG_FILE_BASENAME);
27450
27465
  }
27451
- function loadConfigFile(configPath) {
27466
+ function loadConfigFile(configPath, isProjectConfig = false) {
27452
27467
  try {
27453
27468
  if (!existsSync13(configPath)) {
27454
27469
  return null;
27455
27470
  }
27456
27471
  const rawText = readFileSync8(configPath, "utf-8");
27457
- const substituted = substituteConfigVariables({ text: rawText, configPath });
27472
+ const substituted = substituteConfigVariables({
27473
+ text: rawText,
27474
+ configPath,
27475
+ isProjectConfig
27476
+ });
27458
27477
  return {
27459
27478
  config: parseJsonc3(substituted.text),
27460
27479
  warnings: substituted.warnings.map((w3) => `${configPath}: ${w3}`)
@@ -27618,7 +27637,7 @@ function loadPluginConfig(directory) {
27618
27637
  const dotOpenCodeDetected = detectConfigFile(getProjectConfigBasePath(directory));
27619
27638
  const projectDetected = rootDetected.format !== "none" ? rootDetected : dotOpenCodeDetected;
27620
27639
  const userLoaded = userDetected.format === "none" ? null : loadConfigFile(userDetected.path);
27621
- const projectLoaded = projectDetected.format === "none" ? null : loadConfigFile(projectDetected.path);
27640
+ const projectLoaded = projectDetected.format === "none" ? null : loadConfigFile(projectDetected.path, true);
27622
27641
  const allWarnings = [];
27623
27642
  let mergedRaw = {};
27624
27643
  if (userLoaded) {
@@ -27964,7 +27983,38 @@ function redactionTypeForKey(key) {
27964
27983
  return suffix || "secret";
27965
27984
  }
27966
27985
  function isSecretKey(key) {
27967
- return SECRET_KEY_PATTERN.test(key);
27986
+ const segments = key.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase().split(/[._-]+/).filter(Boolean);
27987
+ if (segments.length === 0)
27988
+ return false;
27989
+ if (segments.length === 1) {
27990
+ const first = segments[0];
27991
+ return Boolean(first && SECRET_SEGMENT_PATTERN.test(first));
27992
+ }
27993
+ for (let i = 0;i < segments.length; i++) {
27994
+ const seg = segments[i];
27995
+ if (!seg || !SECRET_SEGMENT_PATTERN.test(seg))
27996
+ continue;
27997
+ let trailingOk = true;
27998
+ for (let j3 = i + 1;j3 < segments.length; j3++) {
27999
+ const tail = segments[j3];
28000
+ if (!tail)
28001
+ continue;
28002
+ if (TRAILING_DESCRIPTORS.has(tail))
28003
+ continue;
28004
+ if (SECRET_SEGMENT_PATTERN.test(tail))
28005
+ continue;
28006
+ trailingOk = false;
28007
+ break;
28008
+ }
28009
+ if (!trailingOk)
28010
+ continue;
28011
+ for (let k2 = i - 1;k2 >= 0; k2--) {
28012
+ const lead = segments[k2];
28013
+ if (lead && SECRET_QUALIFIERS.has(lead))
28014
+ return true;
28015
+ }
28016
+ }
28017
+ return false;
27968
28018
  }
27969
28019
  function sanitizePathString(value) {
27970
28020
  const home = homedir10();
@@ -28013,9 +28063,41 @@ function sanitizeConfigValue(value, keyPath = []) {
28013
28063
  }
28014
28064
  return value;
28015
28065
  }
28016
- var SECRET_KEY_PATTERN, SECRET_TEXT_PATTERNS;
28066
+ var SECRET_WORDS, SECRET_SEGMENT_PATTERN, TRAILING_DESCRIPTORS, SECRET_QUALIFIERS, SECRET_TEXT_PATTERNS;
28017
28067
  var init_redaction = __esm(() => {
28018
- SECRET_KEY_PATTERN = /(?:key|token|secret|password|auth|bearer|credential)/i;
28068
+ SECRET_WORDS = [
28069
+ "key",
28070
+ "token",
28071
+ "secret",
28072
+ "password",
28073
+ "auth",
28074
+ "authorization",
28075
+ "bearer",
28076
+ "credential"
28077
+ ];
28078
+ SECRET_SEGMENT_PATTERN = new RegExp(`^(?:${SECRET_WORDS.map((w3) => `${w3}s?`).join("|")})$`, "i");
28079
+ TRAILING_DESCRIPTORS = new Set(["id", "ids", "value", "values", "header", "headers"]);
28080
+ SECRET_QUALIFIERS = new Set([
28081
+ "api",
28082
+ "access",
28083
+ "private",
28084
+ "client",
28085
+ "auth",
28086
+ "authorization",
28087
+ "secret",
28088
+ "bearer",
28089
+ "session",
28090
+ "refresh",
28091
+ "service",
28092
+ "x",
28093
+ "openai",
28094
+ "anthropic",
28095
+ "google",
28096
+ "github",
28097
+ "huggingface",
28098
+ "aws",
28099
+ "azure"
28100
+ ]);
28019
28101
  SECRET_TEXT_PATTERNS = [
28020
28102
  {
28021
28103
  pattern: /\bsk-ant-(?:api03-)?[A-Za-z0-9_-]{32,}/g,
@@ -28970,12 +29052,15 @@ async function runDoctor(options = {}) {
28970
29052
  let mcChanged = false;
28971
29053
  const experimental = mcConfig.experimental;
28972
29054
  if (experimental && "compaction_markers" in experimental) {
28973
- if (!("compaction_markers" in mcConfig)) {
28974
- mcConfig.compaction_markers = experimental.compaction_markers;
28975
- }
28976
29055
  delete experimental.compaction_markers;
28977
29056
  mcChanged = true;
28978
- log.success("Migrated experimental.compaction_markers → compaction_markers (now default: true)");
29057
+ log.success("Removed deprecated experimental.compaction_markers (always-on since v0.21.4)");
29058
+ fixed++;
29059
+ }
29060
+ if ("compaction_markers" in mcConfig) {
29061
+ delete mcConfig.compaction_markers;
29062
+ mcChanged = true;
29063
+ log.success("Removed deprecated compaction_markers (always-on since v0.21.4)");
28979
29064
  fixed++;
28980
29065
  }
28981
29066
  if (experimental && "user_memories" in experimental) {
@@ -161950,8 +162035,10 @@ function deleteOrphanProjectKeyFiles(db, now = Date.now()) {
161950
162035
  if (existsSync18(row.projectPath))
161951
162036
  continue;
161952
162037
  try {
161953
- db.prepare("DELETE FROM project_key_files WHERE project_path = ?").run(row.projectPath);
161954
- db.prepare("DELETE FROM project_key_files_version WHERE project_path = ?").run(row.projectPath);
162038
+ db.transaction(() => {
162039
+ db.prepare("DELETE FROM project_key_files WHERE project_path = ?").run(row.projectPath);
162040
+ db.prepare("DELETE FROM project_key_files_version WHERE project_path = ?").run(row.projectPath);
162041
+ })();
161955
162042
  deletedProjects++;
161956
162043
  } catch (error51) {
161957
162044
  log2(`[key-files] orphan GC failed for ${row.projectPath}:`, error51);
@@ -161979,7 +162066,7 @@ function getCurrentVersion(db) {
161979
162066
  const row = db.prepare("SELECT MAX(version) as version FROM schema_migrations").get();
161980
162067
  return row?.version ?? 0;
161981
162068
  }
161982
- function isSiblingMigrationConflict(error51, version2) {
162069
+ function isSiblingMigrationConflict(db, error51, version2) {
161983
162070
  if (!(error51 instanceof Error))
161984
162071
  return false;
161985
162072
  const code = error51.code;
@@ -161991,7 +162078,8 @@ function isSiblingMigrationConflict(error51, version2) {
161991
162078
  return false;
161992
162079
  if (!msg.toLowerCase().includes("version"))
161993
162080
  return false;
161994
- return true;
162081
+ const confirmed = db.prepare("SELECT 1 FROM schema_migrations WHERE version = ?").get(version2);
162082
+ return confirmed != null;
161995
162083
  }
161996
162084
  function runMigrations(db) {
161997
162085
  ensureMigrationsTable(db);
@@ -162012,7 +162100,7 @@ function runMigrations(db) {
162012
162100
  log2(`[migrations] applied v${migration.version}: ${migration.description}`);
162013
162101
  migrationIndex += 1;
162014
162102
  } catch (error51) {
162015
- if (isSiblingMigrationConflict(error51, migration.version)) {
162103
+ if (isSiblingMigrationConflict(db, error51, migration.version)) {
162016
162104
  log2(`[migrations] v${migration.version} already applied by sibling instance — resuming with re-read version`);
162017
162105
  const reReadVersion = getCurrentVersion(db);
162018
162106
  if (reReadVersion <= currentVersion) {
@@ -162418,6 +162506,16 @@ var init_migrations = __esm(async () => {
162418
162506
  WHERE auto_search_hint_decisions IS NULL
162419
162507
  `);
162420
162508
  }
162509
+ },
162510
+ {
162511
+ version: 18,
162512
+ description: "Add pending_pi_compaction_marker_state column for Pi deferred marker drain",
162513
+ up: (db) => {
162514
+ const cols = db.prepare("PRAGMA table_info(session_meta)").all();
162515
+ if (!cols.some((c2) => c2.name === "pending_pi_compaction_marker_state")) {
162516
+ db.exec("ALTER TABLE session_meta ADD COLUMN pending_pi_compaction_marker_state TEXT");
162517
+ }
162518
+ }
162421
162519
  }
162422
162520
  ];
162423
162521
  });
@@ -162981,6 +163079,11 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
162981
163079
  -- Excluded from healNullTextColumns. Readers filter IS NOT NULL AND
162982
163080
  -- != empty-string defensively. Plan v6 section 3.
162983
163081
  pending_compaction_marker_state TEXT,
163082
+ -- pending_pi_compaction_marker_state: intentionally NULLABLE without a
163083
+ -- default. Absence of a deferred Pi-native marker is SQL NULL; presence
163084
+ -- is a valid JSON blob written via setPendingPiCompactionMarkerState.
163085
+ -- Excluded from healNullTextColumns.
163086
+ pending_pi_compaction_marker_state TEXT,
162984
163087
  -- deferred_execute_state: intentionally NULLABLE without a default.
162985
163088
  -- Absence is SQL NULL; presence is a JSON blob written via
162986
163089
  -- setDeferredExecutePendingIfAbsent. Excluded from healNullTextColumns.
@@ -163077,6 +163180,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
163077
163180
  ensureColumn(db, "session_meta", "detected_context_limit", "INTEGER DEFAULT 0");
163078
163181
  ensureColumn(db, "session_meta", "needs_emergency_recovery", "INTEGER DEFAULT 0");
163079
163182
  ensureColumn(db, "session_meta", "pending_compaction_marker_state", "TEXT");
163183
+ ensureColumn(db, "session_meta", "pending_pi_compaction_marker_state", "TEXT");
163080
163184
  ensureColumn(db, "session_meta", "deferred_execute_state", "TEXT");
163081
163185
  ensureColumn(db, "tags", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
163082
163186
  ensureColumn(db, "pending_ops", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cortexkit/magic-context",
3
- "version": "0.21.2",
3
+ "version": "0.21.5",
4
4
  "description": "Unified CLI for Magic Context — setup, doctor, and migration across OpenCode and Pi",
5
5
  "keywords": [
6
6
  "opencode",