ctxloom-pro 1.1.0 → 1.1.2
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.
|
@@ -169,12 +169,12 @@ var init_VectorStore = __esm({
|
|
|
169
169
|
// server/index.ts
|
|
170
170
|
import express from "express";
|
|
171
171
|
import cors from "cors";
|
|
172
|
-
import
|
|
172
|
+
import path42 from "path";
|
|
173
173
|
import fs32 from "fs";
|
|
174
174
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
175
175
|
|
|
176
176
|
// server/loader.ts
|
|
177
|
-
import
|
|
177
|
+
import path36 from "path";
|
|
178
178
|
|
|
179
179
|
// ../../packages/core/src/graph/DependencyGraph.ts
|
|
180
180
|
import fs7 from "fs";
|
|
@@ -3133,8 +3133,8 @@ var CoChangeIndex = class _CoChangeIndex {
|
|
|
3133
3133
|
if (event.isBulk || event.isMerge) return;
|
|
3134
3134
|
const paths = event.files.map((f) => f.path);
|
|
3135
3135
|
if (paths.length === 0) return;
|
|
3136
|
-
for (const
|
|
3137
|
-
this.nodeCounts.set(
|
|
3136
|
+
for (const path43 of paths) {
|
|
3137
|
+
this.nodeCounts.set(path43, (this.nodeCounts.get(path43) ?? 0) + 1);
|
|
3138
3138
|
}
|
|
3139
3139
|
for (let i = 0; i < paths.length; i++) {
|
|
3140
3140
|
for (let j = i + 1; j < paths.length; j++) {
|
|
@@ -3281,8 +3281,8 @@ var ChurnIndex = class _ChurnIndex {
|
|
|
3281
3281
|
*/
|
|
3282
3282
|
snapshot() {
|
|
3283
3283
|
const nodes = {};
|
|
3284
|
-
for (const [
|
|
3285
|
-
nodes[
|
|
3284
|
+
for (const [path43, raw] of this.nodes) {
|
|
3285
|
+
nodes[path43] = {
|
|
3286
3286
|
commits: raw.commits,
|
|
3287
3287
|
churnLines: raw.churnLines,
|
|
3288
3288
|
bugCommits: raw.bugCommits,
|
|
@@ -3297,8 +3297,8 @@ var ChurnIndex = class _ChurnIndex {
|
|
|
3297
3297
|
*/
|
|
3298
3298
|
static load(s) {
|
|
3299
3299
|
const idx = new _ChurnIndex();
|
|
3300
|
-
for (const [
|
|
3301
|
-
idx.nodes.set(
|
|
3300
|
+
for (const [path43, raw] of Object.entries(s.nodes)) {
|
|
3301
|
+
idx.nodes.set(path43, {
|
|
3302
3302
|
commits: raw.commits,
|
|
3303
3303
|
churnLines: raw.churnLines,
|
|
3304
3304
|
bugCommits: raw.bugCommits,
|
|
@@ -3311,8 +3311,8 @@ var ChurnIndex = class _ChurnIndex {
|
|
|
3311
3311
|
// -------------------------------------------------------------------------
|
|
3312
3312
|
// Private helpers
|
|
3313
3313
|
// -------------------------------------------------------------------------
|
|
3314
|
-
getOrCreate(
|
|
3315
|
-
const existing = this.nodes.get(
|
|
3314
|
+
getOrCreate(path43) {
|
|
3315
|
+
const existing = this.nodes.get(path43);
|
|
3316
3316
|
if (existing !== void 0) return existing;
|
|
3317
3317
|
const fresh = {
|
|
3318
3318
|
commits: 0,
|
|
@@ -3321,7 +3321,7 @@ var ChurnIndex = class _ChurnIndex {
|
|
|
3321
3321
|
authorCounts: {},
|
|
3322
3322
|
lastTouch: 0
|
|
3323
3323
|
};
|
|
3324
|
-
this.nodes.set(
|
|
3324
|
+
this.nodes.set(path43, fresh);
|
|
3325
3325
|
return fresh;
|
|
3326
3326
|
}
|
|
3327
3327
|
};
|
|
@@ -3402,12 +3402,12 @@ var OwnershipIndex = class _OwnershipIndex {
|
|
|
3402
3402
|
*/
|
|
3403
3403
|
snapshot() {
|
|
3404
3404
|
const nodes = {};
|
|
3405
|
-
for (const [
|
|
3405
|
+
for (const [path43, raw] of this.nodes) {
|
|
3406
3406
|
const authorWeights = {};
|
|
3407
3407
|
for (const [email, entry] of Object.entries(raw.authorWeights)) {
|
|
3408
3408
|
authorWeights[email] = { ...entry };
|
|
3409
3409
|
}
|
|
3410
|
-
nodes[
|
|
3410
|
+
nodes[path43] = { authorWeights, lastTouch: raw.lastTouch };
|
|
3411
3411
|
}
|
|
3412
3412
|
return { version: 1, nodes };
|
|
3413
3413
|
}
|
|
@@ -3416,23 +3416,23 @@ var OwnershipIndex = class _OwnershipIndex {
|
|
|
3416
3416
|
*/
|
|
3417
3417
|
static load(s) {
|
|
3418
3418
|
const idx = new _OwnershipIndex();
|
|
3419
|
-
for (const [
|
|
3419
|
+
for (const [path43, raw] of Object.entries(s.nodes)) {
|
|
3420
3420
|
const authorWeights = {};
|
|
3421
3421
|
for (const [email, entry] of Object.entries(raw.authorWeights)) {
|
|
3422
3422
|
authorWeights[email] = { ...entry };
|
|
3423
3423
|
}
|
|
3424
|
-
idx.nodes.set(
|
|
3424
|
+
idx.nodes.set(path43, { authorWeights, lastTouch: raw.lastTouch });
|
|
3425
3425
|
}
|
|
3426
3426
|
return idx;
|
|
3427
3427
|
}
|
|
3428
3428
|
// -------------------------------------------------------------------------
|
|
3429
3429
|
// Private helpers
|
|
3430
3430
|
// -------------------------------------------------------------------------
|
|
3431
|
-
getOrCreate(
|
|
3432
|
-
const existing = this.nodes.get(
|
|
3431
|
+
getOrCreate(path43) {
|
|
3432
|
+
const existing = this.nodes.get(path43);
|
|
3433
3433
|
if (existing !== void 0) return existing;
|
|
3434
3434
|
const fresh = { authorWeights: {}, lastTouch: 0 };
|
|
3435
|
-
this.nodes.set(
|
|
3435
|
+
this.nodes.set(path43, fresh);
|
|
3436
3436
|
return fresh;
|
|
3437
3437
|
}
|
|
3438
3438
|
};
|
|
@@ -4670,8 +4670,8 @@ function getErrorMap() {
|
|
|
4670
4670
|
|
|
4671
4671
|
// ../../node_modules/zod/v3/helpers/parseUtil.js
|
|
4672
4672
|
var makeIssue = (params) => {
|
|
4673
|
-
const { data, path:
|
|
4674
|
-
const fullPath = [...
|
|
4673
|
+
const { data, path: path43, errorMaps, issueData } = params;
|
|
4674
|
+
const fullPath = [...path43, ...issueData.path || []];
|
|
4675
4675
|
const fullIssue = {
|
|
4676
4676
|
...issueData,
|
|
4677
4677
|
path: fullPath
|
|
@@ -4787,11 +4787,11 @@ var errorUtil;
|
|
|
4787
4787
|
|
|
4788
4788
|
// ../../node_modules/zod/v3/types.js
|
|
4789
4789
|
var ParseInputLazyPath = class {
|
|
4790
|
-
constructor(parent, value,
|
|
4790
|
+
constructor(parent, value, path43, key) {
|
|
4791
4791
|
this._cachedPath = [];
|
|
4792
4792
|
this.parent = parent;
|
|
4793
4793
|
this.data = value;
|
|
4794
|
-
this._path =
|
|
4794
|
+
this._path = path43;
|
|
4795
4795
|
this._key = key;
|
|
4796
4796
|
}
|
|
4797
4797
|
get path() {
|
|
@@ -11268,24 +11268,34 @@ import { readFileSync as readFileSync2 } from "fs";
|
|
|
11268
11268
|
// ../../packages/core/src/license/index.ts
|
|
11269
11269
|
import os3 from "os";
|
|
11270
11270
|
|
|
11271
|
+
// ../../packages/core/src/license/DistinctIdStore.ts
|
|
11272
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync2 } from "fs";
|
|
11273
|
+
import path32 from "path";
|
|
11274
|
+
import os4 from "os";
|
|
11275
|
+
|
|
11271
11276
|
// ../../packages/core/src/license/telemetry.ts
|
|
11272
11277
|
var TELEMETRY_DISABLED = process.env["CTXLOOM_NO_TELEMETRY"] === "1" || process.env["DO_NOT_TRACK"] === "1";
|
|
11278
|
+
var CTXLOOM_VERSION = typeof __CTXLOOM_VERSION__ === "string" && __CTXLOOM_VERSION__.length > 0 ? __CTXLOOM_VERSION__ : "dev";
|
|
11273
11279
|
var POSTHOG_KEY = process.env["POSTHOG_API_KEY"] ?? (typeof __TELEMETRY_POSTHOG_KEY__ === "string" ? __TELEMETRY_POSTHOG_KEY__ : "");
|
|
11274
11280
|
var SENTRY_DSN = process.env["SENTRY_DSN"] ?? (typeof __TELEMETRY_SENTRY_DSN__ === "string" ? __TELEMETRY_SENTRY_DSN__ : "");
|
|
11275
11281
|
|
|
11276
11282
|
// ../../packages/core/src/server/ProjectState.ts
|
|
11277
|
-
import
|
|
11283
|
+
import path34 from "path";
|
|
11284
|
+
|
|
11285
|
+
// ../../packages/core/src/server/projectId.ts
|
|
11286
|
+
import crypto5 from "crypto";
|
|
11287
|
+
import path33 from "path";
|
|
11278
11288
|
|
|
11279
11289
|
// ../../packages/core/src/server/ProjectStateManager.ts
|
|
11280
11290
|
init_logger();
|
|
11281
11291
|
|
|
11282
11292
|
// ../../packages/core/src/server/resolveProjectRoot.ts
|
|
11283
11293
|
import fs29 from "fs";
|
|
11284
|
-
import
|
|
11294
|
+
import path35 from "path";
|
|
11285
11295
|
|
|
11286
11296
|
// server/loader.ts
|
|
11287
11297
|
async function loadContext(root) {
|
|
11288
|
-
const absRoot =
|
|
11298
|
+
const absRoot = path36.resolve(root);
|
|
11289
11299
|
const overlay = new GitOverlayStore(absRoot);
|
|
11290
11300
|
const gitEnabled = await overlay.loadSnapshot();
|
|
11291
11301
|
const graph = new DependencyGraph();
|
|
@@ -11539,20 +11549,20 @@ function buildOwnershipRouter(ctx) {
|
|
|
11539
11549
|
// server/routes/file.ts
|
|
11540
11550
|
import { Router as Router7 } from "express";
|
|
11541
11551
|
import fs30 from "fs/promises";
|
|
11542
|
-
import
|
|
11552
|
+
import path37 from "path";
|
|
11543
11553
|
function buildFileRouter(ctx) {
|
|
11544
11554
|
const router = Router7();
|
|
11545
11555
|
router.get("/", async (req, res) => {
|
|
11546
11556
|
const rel = req.query.path;
|
|
11547
11557
|
if (!rel) return res.status(400).json({ error: "missing path" });
|
|
11548
|
-
const abs =
|
|
11549
|
-
const rootBoundary = ctx.root.endsWith(
|
|
11558
|
+
const abs = path37.resolve(ctx.root, rel);
|
|
11559
|
+
const rootBoundary = ctx.root.endsWith(path37.sep) ? ctx.root : ctx.root + path37.sep;
|
|
11550
11560
|
if (abs !== ctx.root && !abs.startsWith(rootBoundary)) {
|
|
11551
11561
|
return res.status(403).json({ error: "forbidden" });
|
|
11552
11562
|
}
|
|
11553
11563
|
try {
|
|
11554
11564
|
const content = await fs30.readFile(abs, "utf-8");
|
|
11555
|
-
const ext =
|
|
11565
|
+
const ext = path37.extname(abs).slice(1);
|
|
11556
11566
|
res.json({ content, lines: content.split("\n").length, ext });
|
|
11557
11567
|
} catch {
|
|
11558
11568
|
res.status(404).json({ error: "not found" });
|
|
@@ -11564,7 +11574,7 @@ function buildFileRouter(ctx) {
|
|
|
11564
11574
|
// server/routes/open.ts
|
|
11565
11575
|
import { Router as Router8 } from "express";
|
|
11566
11576
|
import { execFile as execFile2 } from "child_process";
|
|
11567
|
-
import
|
|
11577
|
+
import path38 from "path";
|
|
11568
11578
|
function tryOpen(bin, abs) {
|
|
11569
11579
|
return new Promise((resolve) => {
|
|
11570
11580
|
execFile2(bin, [abs], { timeout: 5e3 }, (err) => resolve(!err));
|
|
@@ -11575,8 +11585,8 @@ function buildOpenRouter(ctx) {
|
|
|
11575
11585
|
router.post("/", async (req, res) => {
|
|
11576
11586
|
const rel = req.body?.path;
|
|
11577
11587
|
if (!rel || typeof rel !== "string") return res.status(400).json({ error: "missing path" });
|
|
11578
|
-
const abs =
|
|
11579
|
-
const rootBoundary = ctx.root.endsWith(
|
|
11588
|
+
const abs = path38.resolve(ctx.root, rel);
|
|
11589
|
+
const rootBoundary = ctx.root.endsWith(path38.sep) ? ctx.root : ctx.root + path38.sep;
|
|
11580
11590
|
if (abs !== ctx.root && !abs.startsWith(rootBoundary)) {
|
|
11581
11591
|
return res.status(403).json({ error: "forbidden" });
|
|
11582
11592
|
}
|
|
@@ -11588,7 +11598,7 @@ function buildOpenRouter(ctx) {
|
|
|
11588
11598
|
|
|
11589
11599
|
// server/routes/tokens.ts
|
|
11590
11600
|
import { Router as Router9 } from "express";
|
|
11591
|
-
import
|
|
11601
|
+
import path39 from "path";
|
|
11592
11602
|
import fs31 from "fs";
|
|
11593
11603
|
var CHARS_PER_TOKEN = 4;
|
|
11594
11604
|
var cache = null;
|
|
@@ -11604,7 +11614,7 @@ function buildTokensRouter(ctx) {
|
|
|
11604
11614
|
let fullChars = 0;
|
|
11605
11615
|
let skeletonChars = 0;
|
|
11606
11616
|
for (const file of files) {
|
|
11607
|
-
const absPath =
|
|
11617
|
+
const absPath = path39.join(ctx.root, file);
|
|
11608
11618
|
try {
|
|
11609
11619
|
const content = fs31.readFileSync(absPath, "utf-8");
|
|
11610
11620
|
fullChars += content.length;
|
|
@@ -11707,23 +11717,23 @@ function buildFileTrendsRouter(ctx) {
|
|
|
11707
11717
|
|
|
11708
11718
|
// server/routes/projects.ts
|
|
11709
11719
|
import { Router as Router12 } from "express";
|
|
11710
|
-
import
|
|
11720
|
+
import path41 from "path";
|
|
11711
11721
|
|
|
11712
11722
|
// server/projects.ts
|
|
11713
|
-
import { existsSync as
|
|
11714
|
-
import
|
|
11715
|
-
import
|
|
11716
|
-
import
|
|
11717
|
-
var HOME =
|
|
11718
|
-
var REGISTRY_PATH =
|
|
11723
|
+
import { existsSync as existsSync3, readFileSync as readFileSync4 } from "fs";
|
|
11724
|
+
import os5 from "os";
|
|
11725
|
+
import path40 from "path";
|
|
11726
|
+
import crypto6 from "crypto";
|
|
11727
|
+
var HOME = os5.homedir();
|
|
11728
|
+
var REGISTRY_PATH = path40.join(HOME, ".ctxloom", "repos.json");
|
|
11719
11729
|
function slugFor(root) {
|
|
11720
|
-
const abs =
|
|
11721
|
-
return
|
|
11730
|
+
const abs = path40.resolve(root);
|
|
11731
|
+
return crypto6.createHash("sha1").update(abs).digest("hex").slice(0, 12);
|
|
11722
11732
|
}
|
|
11723
11733
|
function readRegistry() {
|
|
11724
|
-
if (!
|
|
11734
|
+
if (!existsSync3(REGISTRY_PATH)) return [];
|
|
11725
11735
|
try {
|
|
11726
|
-
const raw =
|
|
11736
|
+
const raw = readFileSync4(REGISTRY_PATH, "utf-8");
|
|
11727
11737
|
const parsed = JSON.parse(raw);
|
|
11728
11738
|
if (!Array.isArray(parsed)) return [];
|
|
11729
11739
|
return parsed.filter((r) => typeof r?.root === "string");
|
|
@@ -11732,27 +11742,27 @@ function readRegistry() {
|
|
|
11732
11742
|
}
|
|
11733
11743
|
}
|
|
11734
11744
|
function listProjects(defaultRoot) {
|
|
11735
|
-
const absDefault =
|
|
11745
|
+
const absDefault = path40.resolve(defaultRoot);
|
|
11736
11746
|
const out = [
|
|
11737
11747
|
{
|
|
11738
11748
|
slug: slugFor(absDefault),
|
|
11739
|
-
name:
|
|
11749
|
+
name: path40.basename(absDefault) || absDefault,
|
|
11740
11750
|
root: absDefault,
|
|
11741
11751
|
isDefault: true,
|
|
11742
|
-
hasSnapshot:
|
|
11752
|
+
hasSnapshot: existsSync3(path40.join(absDefault, ".ctxloom"))
|
|
11743
11753
|
}
|
|
11744
11754
|
];
|
|
11745
11755
|
const seen = /* @__PURE__ */ new Set([absDefault]);
|
|
11746
11756
|
for (const entry of readRegistry()) {
|
|
11747
|
-
const abs =
|
|
11757
|
+
const abs = path40.resolve(entry.root);
|
|
11748
11758
|
if (seen.has(abs)) continue;
|
|
11749
11759
|
seen.add(abs);
|
|
11750
11760
|
const item = {
|
|
11751
11761
|
slug: slugFor(abs),
|
|
11752
|
-
name: entry.name ?? (
|
|
11762
|
+
name: entry.name ?? (path40.basename(abs) || abs),
|
|
11753
11763
|
root: abs,
|
|
11754
11764
|
isDefault: false,
|
|
11755
|
-
hasSnapshot:
|
|
11765
|
+
hasSnapshot: existsSync3(path40.join(abs, ".ctxloom"))
|
|
11756
11766
|
};
|
|
11757
11767
|
if (entry.alias !== void 0) item.alias = entry.alias;
|
|
11758
11768
|
out.push(item);
|
|
@@ -11805,7 +11815,7 @@ function buildProjectsRouter(deps) {
|
|
|
11805
11815
|
} catch (err) {
|
|
11806
11816
|
const detail = err instanceof Error ? err.message : String(err);
|
|
11807
11817
|
res.status(500).json({
|
|
11808
|
-
error: `failed to switch to ${
|
|
11818
|
+
error: `failed to switch to ${path41.basename(target.root)}: ${detail}`
|
|
11809
11819
|
});
|
|
11810
11820
|
}
|
|
11811
11821
|
});
|
|
@@ -11813,7 +11823,7 @@ function buildProjectsRouter(deps) {
|
|
|
11813
11823
|
}
|
|
11814
11824
|
|
|
11815
11825
|
// server/index.ts
|
|
11816
|
-
var __dirname2 =
|
|
11826
|
+
var __dirname2 = path42.dirname(fileURLToPath2(import.meta.url));
|
|
11817
11827
|
async function startDashboard(options) {
|
|
11818
11828
|
const { root, port, open } = options;
|
|
11819
11829
|
console.log(`ctxloom dashboard \u2014 loading context from ${root}...`);
|
|
@@ -11872,7 +11882,7 @@ async function startDashboard(options) {
|
|
|
11872
11882
|
}
|
|
11873
11883
|
activeWatcher = null;
|
|
11874
11884
|
}
|
|
11875
|
-
const snapshotDir =
|
|
11885
|
+
const snapshotDir = path42.join(targetRoot, ".ctxloom");
|
|
11876
11886
|
try {
|
|
11877
11887
|
activeWatcher = fs32.watch(snapshotDir, (_event, filename) => {
|
|
11878
11888
|
if (!filename || !filename.includes("snapshot")) return;
|
|
@@ -11902,12 +11912,12 @@ async function startDashboard(options) {
|
|
|
11902
11912
|
attachSnapshotWatcher(newRoot);
|
|
11903
11913
|
}
|
|
11904
11914
|
}));
|
|
11905
|
-
const clientDist =
|
|
11906
|
-
const clientDistExists = fs32.existsSync(
|
|
11915
|
+
const clientDist = path42.join(__dirname2, "../dashboard/client");
|
|
11916
|
+
const clientDistExists = fs32.existsSync(path42.join(clientDist, "index.html"));
|
|
11907
11917
|
if (clientDistExists) {
|
|
11908
11918
|
app.use(express.static(clientDist, { dotfiles: "allow" }));
|
|
11909
11919
|
app.get(/.*/, (_req, res) => {
|
|
11910
|
-
res.sendFile(
|
|
11920
|
+
res.sendFile(path42.join(clientDist, "index.html"), { dotfiles: "allow" });
|
|
11911
11921
|
});
|
|
11912
11922
|
} else {
|
|
11913
11923
|
app.get(/^\/(?!api\/).*/, (_req, res) => {
|
|
@@ -3406,8 +3406,8 @@ var CoChangeIndex = class _CoChangeIndex {
|
|
|
3406
3406
|
if (event.isBulk || event.isMerge) return;
|
|
3407
3407
|
const paths = event.files.map((f) => f.path);
|
|
3408
3408
|
if (paths.length === 0) return;
|
|
3409
|
-
for (const
|
|
3410
|
-
this.nodeCounts.set(
|
|
3409
|
+
for (const path34 of paths) {
|
|
3410
|
+
this.nodeCounts.set(path34, (this.nodeCounts.get(path34) ?? 0) + 1);
|
|
3411
3411
|
}
|
|
3412
3412
|
for (let i = 0; i < paths.length; i++) {
|
|
3413
3413
|
for (let j = i + 1; j < paths.length; j++) {
|
|
@@ -3554,8 +3554,8 @@ var ChurnIndex = class _ChurnIndex {
|
|
|
3554
3554
|
*/
|
|
3555
3555
|
snapshot() {
|
|
3556
3556
|
const nodes = {};
|
|
3557
|
-
for (const [
|
|
3558
|
-
nodes[
|
|
3557
|
+
for (const [path34, raw] of this.nodes) {
|
|
3558
|
+
nodes[path34] = {
|
|
3559
3559
|
commits: raw.commits,
|
|
3560
3560
|
churnLines: raw.churnLines,
|
|
3561
3561
|
bugCommits: raw.bugCommits,
|
|
@@ -3570,8 +3570,8 @@ var ChurnIndex = class _ChurnIndex {
|
|
|
3570
3570
|
*/
|
|
3571
3571
|
static load(s) {
|
|
3572
3572
|
const idx = new _ChurnIndex();
|
|
3573
|
-
for (const [
|
|
3574
|
-
idx.nodes.set(
|
|
3573
|
+
for (const [path34, raw] of Object.entries(s.nodes)) {
|
|
3574
|
+
idx.nodes.set(path34, {
|
|
3575
3575
|
commits: raw.commits,
|
|
3576
3576
|
churnLines: raw.churnLines,
|
|
3577
3577
|
bugCommits: raw.bugCommits,
|
|
@@ -3584,8 +3584,8 @@ var ChurnIndex = class _ChurnIndex {
|
|
|
3584
3584
|
// -------------------------------------------------------------------------
|
|
3585
3585
|
// Private helpers
|
|
3586
3586
|
// -------------------------------------------------------------------------
|
|
3587
|
-
getOrCreate(
|
|
3588
|
-
const existing = this.nodes.get(
|
|
3587
|
+
getOrCreate(path34) {
|
|
3588
|
+
const existing = this.nodes.get(path34);
|
|
3589
3589
|
if (existing !== void 0) return existing;
|
|
3590
3590
|
const fresh = {
|
|
3591
3591
|
commits: 0,
|
|
@@ -3594,7 +3594,7 @@ var ChurnIndex = class _ChurnIndex {
|
|
|
3594
3594
|
authorCounts: {},
|
|
3595
3595
|
lastTouch: 0
|
|
3596
3596
|
};
|
|
3597
|
-
this.nodes.set(
|
|
3597
|
+
this.nodes.set(path34, fresh);
|
|
3598
3598
|
return fresh;
|
|
3599
3599
|
}
|
|
3600
3600
|
};
|
|
@@ -3675,12 +3675,12 @@ var OwnershipIndex = class _OwnershipIndex {
|
|
|
3675
3675
|
*/
|
|
3676
3676
|
snapshot() {
|
|
3677
3677
|
const nodes = {};
|
|
3678
|
-
for (const [
|
|
3678
|
+
for (const [path34, raw] of this.nodes) {
|
|
3679
3679
|
const authorWeights = {};
|
|
3680
3680
|
for (const [email, entry] of Object.entries(raw.authorWeights)) {
|
|
3681
3681
|
authorWeights[email] = { ...entry };
|
|
3682
3682
|
}
|
|
3683
|
-
nodes[
|
|
3683
|
+
nodes[path34] = { authorWeights, lastTouch: raw.lastTouch };
|
|
3684
3684
|
}
|
|
3685
3685
|
return { version: 1, nodes };
|
|
3686
3686
|
}
|
|
@@ -3689,23 +3689,23 @@ var OwnershipIndex = class _OwnershipIndex {
|
|
|
3689
3689
|
*/
|
|
3690
3690
|
static load(s) {
|
|
3691
3691
|
const idx = new _OwnershipIndex();
|
|
3692
|
-
for (const [
|
|
3692
|
+
for (const [path34, raw] of Object.entries(s.nodes)) {
|
|
3693
3693
|
const authorWeights = {};
|
|
3694
3694
|
for (const [email, entry] of Object.entries(raw.authorWeights)) {
|
|
3695
3695
|
authorWeights[email] = { ...entry };
|
|
3696
3696
|
}
|
|
3697
|
-
idx.nodes.set(
|
|
3697
|
+
idx.nodes.set(path34, { authorWeights, lastTouch: raw.lastTouch });
|
|
3698
3698
|
}
|
|
3699
3699
|
return idx;
|
|
3700
3700
|
}
|
|
3701
3701
|
// -------------------------------------------------------------------------
|
|
3702
3702
|
// Private helpers
|
|
3703
3703
|
// -------------------------------------------------------------------------
|
|
3704
|
-
getOrCreate(
|
|
3705
|
-
const existing = this.nodes.get(
|
|
3704
|
+
getOrCreate(path34) {
|
|
3705
|
+
const existing = this.nodes.get(path34);
|
|
3706
3706
|
if (existing !== void 0) return existing;
|
|
3707
3707
|
const fresh = { authorWeights: {}, lastTouch: 0 };
|
|
3708
|
-
this.nodes.set(
|
|
3708
|
+
this.nodes.set(path34, fresh);
|
|
3709
3709
|
return fresh;
|
|
3710
3710
|
}
|
|
3711
3711
|
};
|
|
@@ -8794,18 +8794,79 @@ async function startTrial(email, opts = {}) {
|
|
|
8794
8794
|
return result;
|
|
8795
8795
|
}
|
|
8796
8796
|
|
|
8797
|
+
// packages/core/src/license/DistinctIdStore.ts
|
|
8798
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync2 } from "fs";
|
|
8799
|
+
import path30 from "path";
|
|
8800
|
+
import os4 from "os";
|
|
8801
|
+
var UUID_V4_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
8802
|
+
function distinctIdPath(home) {
|
|
8803
|
+
return path30.join(home ?? os4.homedir(), ".ctxloom", "distinct_id");
|
|
8804
|
+
}
|
|
8805
|
+
function isValidV4(id) {
|
|
8806
|
+
return typeof id === "string" && UUID_V4_REGEX.test(id);
|
|
8807
|
+
}
|
|
8808
|
+
function getOrCreateDistinctId(home) {
|
|
8809
|
+
const filePath = distinctIdPath(home);
|
|
8810
|
+
if (existsSync2(filePath)) {
|
|
8811
|
+
try {
|
|
8812
|
+
const raw = readFileSync3(filePath, "utf8");
|
|
8813
|
+
const parsed = JSON.parse(raw);
|
|
8814
|
+
if (parsed !== null && typeof parsed === "object" && isValidV4(parsed["id"])) {
|
|
8815
|
+
return parsed;
|
|
8816
|
+
}
|
|
8817
|
+
} catch {
|
|
8818
|
+
}
|
|
8819
|
+
}
|
|
8820
|
+
const record = {
|
|
8821
|
+
id: crypto.randomUUID(),
|
|
8822
|
+
alias_pending: os4.hostname()
|
|
8823
|
+
};
|
|
8824
|
+
mkdirSync2(path30.dirname(filePath), { recursive: true });
|
|
8825
|
+
writeFileSync2(filePath, JSON.stringify(record), { mode: 384 });
|
|
8826
|
+
return record;
|
|
8827
|
+
}
|
|
8828
|
+
function markAliasSent(home) {
|
|
8829
|
+
const filePath = distinctIdPath(home);
|
|
8830
|
+
try {
|
|
8831
|
+
const raw = readFileSync3(filePath, "utf8");
|
|
8832
|
+
const parsed = JSON.parse(raw);
|
|
8833
|
+
const { alias_pending: _dropped, ...rest } = parsed;
|
|
8834
|
+
writeFileSync2(filePath, JSON.stringify(rest), { mode: 384 });
|
|
8835
|
+
} catch {
|
|
8836
|
+
}
|
|
8837
|
+
}
|
|
8838
|
+
|
|
8797
8839
|
// packages/core/src/license/telemetry.ts
|
|
8798
8840
|
var TELEMETRY_DISABLED = process.env["CTXLOOM_NO_TELEMETRY"] === "1" || process.env["DO_NOT_TRACK"] === "1";
|
|
8841
|
+
var CTXLOOM_VERSION = "1.1.2".length > 0 ? "1.1.2" : "dev";
|
|
8799
8842
|
var POSTHOG_HOST = "https://eu.i.posthog.com";
|
|
8800
8843
|
var POSTHOG_KEY = process.env["POSTHOG_API_KEY"] ?? (true ? "phc_CiDkmFLcZ2K6uCpcoSUQLmFrnnUvsyXGhSxopX5TVKE6" : "");
|
|
8801
8844
|
var SENTRY_DSN = process.env["SENTRY_DSN"] ?? (true ? "https://81c94a0f04a8e242dee493ac1e17f733@o4508531702497280.ingest.de.sentry.io/4511256875368528" : "");
|
|
8802
|
-
|
|
8845
|
+
var cachedDistinctId = null;
|
|
8846
|
+
function resolveDistinctId() {
|
|
8847
|
+
if (cachedDistinctId) return cachedDistinctId;
|
|
8848
|
+
try {
|
|
8849
|
+
cachedDistinctId = getOrCreateDistinctId();
|
|
8850
|
+
return cachedDistinctId;
|
|
8851
|
+
} catch {
|
|
8852
|
+
return null;
|
|
8853
|
+
}
|
|
8854
|
+
}
|
|
8855
|
+
function track(event, props = {}) {
|
|
8803
8856
|
if (TELEMETRY_DISABLED || !POSTHOG_KEY) return;
|
|
8804
|
-
|
|
8857
|
+
const record = resolveDistinctId();
|
|
8858
|
+
if (!record) return;
|
|
8859
|
+
void sendPostHog(event, record.id, props);
|
|
8860
|
+
if (record.alias_pending) {
|
|
8861
|
+
const oldAlias = record.alias_pending;
|
|
8862
|
+
void sendAlias(record.id, oldAlias);
|
|
8863
|
+
}
|
|
8805
8864
|
}
|
|
8806
8865
|
function captureError(err, context = {}) {
|
|
8807
8866
|
if (TELEMETRY_DISABLED || !SENTRY_DSN) return;
|
|
8808
|
-
|
|
8867
|
+
const record = resolveDistinctId();
|
|
8868
|
+
const augmented = record ? { ...context, distinct_id: record.id } : context;
|
|
8869
|
+
void sendSentry(err, augmented);
|
|
8809
8870
|
}
|
|
8810
8871
|
async function sendPostHog(event, distinctId, props) {
|
|
8811
8872
|
try {
|
|
@@ -8818,6 +8879,7 @@ async function sendPostHog(event, distinctId, props) {
|
|
|
8818
8879
|
event,
|
|
8819
8880
|
properties: {
|
|
8820
8881
|
$lib: "ctxloom-cli",
|
|
8882
|
+
release: CTXLOOM_VERSION,
|
|
8821
8883
|
...props
|
|
8822
8884
|
}
|
|
8823
8885
|
}),
|
|
@@ -8826,6 +8888,33 @@ async function sendPostHog(event, distinctId, props) {
|
|
|
8826
8888
|
} catch {
|
|
8827
8889
|
}
|
|
8828
8890
|
}
|
|
8891
|
+
async function sendAlias(newId, oldAlias) {
|
|
8892
|
+
try {
|
|
8893
|
+
const res = await fetch(`${POSTHOG_HOST}/capture/`, {
|
|
8894
|
+
method: "POST",
|
|
8895
|
+
headers: { "Content-Type": "application/json" },
|
|
8896
|
+
body: JSON.stringify({
|
|
8897
|
+
api_key: POSTHOG_KEY,
|
|
8898
|
+
distinct_id: newId,
|
|
8899
|
+
event: "$create_alias",
|
|
8900
|
+
properties: {
|
|
8901
|
+
$lib: "ctxloom-cli",
|
|
8902
|
+
release: CTXLOOM_VERSION,
|
|
8903
|
+
alias: oldAlias
|
|
8904
|
+
}
|
|
8905
|
+
}),
|
|
8906
|
+
signal: AbortSignal.timeout(4e3)
|
|
8907
|
+
});
|
|
8908
|
+
if (res.ok) {
|
|
8909
|
+
try {
|
|
8910
|
+
markAliasSent();
|
|
8911
|
+
} catch {
|
|
8912
|
+
}
|
|
8913
|
+
if (cachedDistinctId) cachedDistinctId = { id: cachedDistinctId.id };
|
|
8914
|
+
}
|
|
8915
|
+
} catch {
|
|
8916
|
+
}
|
|
8917
|
+
}
|
|
8829
8918
|
async function sendSentry(err, context) {
|
|
8830
8919
|
try {
|
|
8831
8920
|
const dsn = parseDsn(SENTRY_DSN);
|
|
@@ -8853,7 +8942,7 @@ async function sendSentry(err, context) {
|
|
|
8853
8942
|
]
|
|
8854
8943
|
},
|
|
8855
8944
|
extra: context,
|
|
8856
|
-
tags: { runtime: "node", component: "cli-license" }
|
|
8945
|
+
tags: { runtime: "node", component: "cli-license", release: CTXLOOM_VERSION }
|
|
8857
8946
|
}),
|
|
8858
8947
|
signal: AbortSignal.timeout(4e3)
|
|
8859
8948
|
});
|
|
@@ -8869,20 +8958,33 @@ function parseDsn(dsn) {
|
|
|
8869
8958
|
return null;
|
|
8870
8959
|
}
|
|
8871
8960
|
}
|
|
8961
|
+
function scrubPath(filename) {
|
|
8962
|
+
return filename.replace(/^\/Users\/[^/]+\//, "/Users/~/").replace(/^\/home\/[^/]+\//, "/home/~/").replace(/^([A-Z]:\\\\Users\\\\)[^\\]+\\\\/, "$1~\\\\").replace(/^([A-Z]:\\Users\\)[^\\]+\\/, "$1~\\");
|
|
8963
|
+
}
|
|
8872
8964
|
function parseStack(stack) {
|
|
8873
8965
|
return stack.split("\n").slice(1).map((line) => {
|
|
8874
8966
|
const m = line.trim().match(/at (.+?) \((.+?):(\d+):\d+\)/);
|
|
8875
8967
|
if (!m) return null;
|
|
8876
|
-
return { function: m[1] ?? "", filename: m[2] ?? "", lineno: Number(m[3]) };
|
|
8968
|
+
return { function: m[1] ?? "", filename: scrubPath(m[2] ?? ""), lineno: Number(m[3]) };
|
|
8877
8969
|
}).filter((f) => f !== null).slice(0, 20);
|
|
8878
8970
|
}
|
|
8879
8971
|
|
|
8880
8972
|
// packages/core/src/server/ProjectState.ts
|
|
8881
|
-
import
|
|
8973
|
+
import path32 from "path";
|
|
8974
|
+
|
|
8975
|
+
// packages/core/src/server/projectId.ts
|
|
8976
|
+
import crypto5 from "crypto";
|
|
8977
|
+
import path31 from "path";
|
|
8978
|
+
function hashProjectRoot(absPath) {
|
|
8979
|
+
const canonical = path31.resolve(absPath);
|
|
8980
|
+
return crypto5.createHash("sha256").update(canonical).digest("hex").slice(0, 16);
|
|
8981
|
+
}
|
|
8982
|
+
|
|
8983
|
+
// packages/core/src/server/ProjectState.ts
|
|
8882
8984
|
function createProjectState(projectRoot, opts = {}) {
|
|
8883
8985
|
return {
|
|
8884
8986
|
projectRoot,
|
|
8885
|
-
dbPath:
|
|
8987
|
+
dbPath: path32.join(projectRoot, ".ctxloom", "vectors.lancedb"),
|
|
8886
8988
|
pinned: opts.pinned ?? false,
|
|
8887
8989
|
lastTouchedAt: Date.now(),
|
|
8888
8990
|
vectorsInitialized: false,
|
|
@@ -8900,8 +9002,16 @@ function createProjectState(projectRoot, opts = {}) {
|
|
|
8900
9002
|
async function ensureVectorsInitialized(state) {
|
|
8901
9003
|
if (state.vectorsInitialized) return;
|
|
8902
9004
|
if (!state.storePromise) return;
|
|
8903
|
-
|
|
8904
|
-
|
|
9005
|
+
try {
|
|
9006
|
+
await state.storePromise;
|
|
9007
|
+
state.vectorsInitialized = true;
|
|
9008
|
+
} catch (err) {
|
|
9009
|
+
captureError(err, {
|
|
9010
|
+
project_id: hashProjectRoot(state.projectRoot),
|
|
9011
|
+
phase: "vector_init"
|
|
9012
|
+
});
|
|
9013
|
+
throw err;
|
|
9014
|
+
}
|
|
8905
9015
|
}
|
|
8906
9016
|
async function disposeProjectState(state) {
|
|
8907
9017
|
try {
|
|
@@ -8995,12 +9105,23 @@ var ProjectStateManager = class {
|
|
|
8995
9105
|
);
|
|
8996
9106
|
}
|
|
8997
9107
|
this.map.delete(victim.projectRoot);
|
|
9108
|
+
const pinnedCount = Array.from(this.map.values()).filter((s) => s.pinned).length;
|
|
9109
|
+
track("project_evicted", {
|
|
9110
|
+
project_id: hashProjectRoot(victim.projectRoot),
|
|
9111
|
+
pinned_count: pinnedCount,
|
|
9112
|
+
cap: this.maxProjects
|
|
9113
|
+
});
|
|
8998
9114
|
void this.onDispose(victim).then(() => {
|
|
8999
9115
|
logger.info("project.evicted", {
|
|
9000
9116
|
root: victim.projectRoot,
|
|
9001
9117
|
reason: "lru_cap_reached",
|
|
9002
9118
|
ttl_seconds: Math.round((Date.now() - victim.lastTouchedAt) / 1e3)
|
|
9003
9119
|
});
|
|
9120
|
+
}).catch((err) => {
|
|
9121
|
+
captureError(err, {
|
|
9122
|
+
project_id: hashProjectRoot(victim.projectRoot),
|
|
9123
|
+
phase: "dispose"
|
|
9124
|
+
});
|
|
9004
9125
|
});
|
|
9005
9126
|
}
|
|
9006
9127
|
/**
|
|
@@ -9016,7 +9137,7 @@ var ProjectStateManager = class {
|
|
|
9016
9137
|
|
|
9017
9138
|
// packages/core/src/server/resolveProjectRoot.ts
|
|
9018
9139
|
import fs27 from "fs";
|
|
9019
|
-
import
|
|
9140
|
+
import path33 from "path";
|
|
9020
9141
|
var PATH_SEPARATOR_PATTERN = /[/\\~]|^[A-Za-z]:/;
|
|
9021
9142
|
function looksLikePath(value) {
|
|
9022
9143
|
return PATH_SEPARATOR_PATTERN.test(value);
|
|
@@ -9041,9 +9162,9 @@ function resolvePathSafely(p, cwd) {
|
|
|
9041
9162
|
let expanded = p;
|
|
9042
9163
|
if (p === "~" || p.startsWith("~/")) {
|
|
9043
9164
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
9044
|
-
expanded = p === "~" ? home :
|
|
9165
|
+
expanded = p === "~" ? home : path33.join(home, p.slice(2));
|
|
9045
9166
|
}
|
|
9046
|
-
return
|
|
9167
|
+
return path33.isAbsolute(expanded) ? path33.resolve(expanded) : path33.resolve(cwd, expanded);
|
|
9047
9168
|
}
|
|
9048
9169
|
function realpathOrSame(p) {
|
|
9049
9170
|
try {
|
|
@@ -9115,7 +9236,7 @@ function validateDefaultRoot(candidate) {
|
|
|
9115
9236
|
} catch {
|
|
9116
9237
|
return false;
|
|
9117
9238
|
}
|
|
9118
|
-
return PROJECT_MARKERS.some((m) => fs27.existsSync(
|
|
9239
|
+
return PROJECT_MARKERS.some((m) => fs27.existsSync(path33.join(candidate, m)));
|
|
9119
9240
|
}
|
|
9120
9241
|
|
|
9121
9242
|
// packages/core/src/server/structuredErrors.ts
|
|
@@ -9171,6 +9292,21 @@ var FirstTouchTracker = class {
|
|
|
9171
9292
|
}
|
|
9172
9293
|
};
|
|
9173
9294
|
|
|
9295
|
+
// packages/core/src/server/EmittedOnceTracker.ts
|
|
9296
|
+
var EmittedOnceTracker = class {
|
|
9297
|
+
seen = /* @__PURE__ */ new Set();
|
|
9298
|
+
/** Returns true the first time `key` is seen, false thereafter. */
|
|
9299
|
+
markAndCheck(key) {
|
|
9300
|
+
if (this.seen.has(key)) return false;
|
|
9301
|
+
this.seen.add(key);
|
|
9302
|
+
return true;
|
|
9303
|
+
}
|
|
9304
|
+
/** Clear all keys. Used by tests. */
|
|
9305
|
+
reset() {
|
|
9306
|
+
this.seen.clear();
|
|
9307
|
+
}
|
|
9308
|
+
};
|
|
9309
|
+
|
|
9174
9310
|
export {
|
|
9175
9311
|
GRAMMAR_MANIFEST,
|
|
9176
9312
|
findGrammar,
|
|
@@ -9253,8 +9389,11 @@ export {
|
|
|
9253
9389
|
activateLicense,
|
|
9254
9390
|
deactivateLicense,
|
|
9255
9391
|
startTrial,
|
|
9392
|
+
getOrCreateDistinctId,
|
|
9393
|
+
markAliasSent,
|
|
9256
9394
|
track,
|
|
9257
9395
|
captureError,
|
|
9396
|
+
hashProjectRoot,
|
|
9258
9397
|
createProjectState,
|
|
9259
9398
|
ensureVectorsInitialized,
|
|
9260
9399
|
disposeProjectState,
|
|
@@ -9267,6 +9406,7 @@ export {
|
|
|
9267
9406
|
aliasNotFoundError,
|
|
9268
9407
|
noParseableSourcesWarning,
|
|
9269
9408
|
wrapWithIndexingEnvelope,
|
|
9270
|
-
FirstTouchTracker
|
|
9409
|
+
FirstTouchTracker,
|
|
9410
|
+
EmittedOnceTracker
|
|
9271
9411
|
};
|
|
9272
|
-
//# sourceMappingURL=chunk-
|
|
9412
|
+
//# sourceMappingURL=chunk-RGO3M3IR.js.map
|
package/dist/index.js
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
AuthorResolver,
|
|
9
9
|
DependencyGraph,
|
|
10
10
|
EmailAlreadyUsedError,
|
|
11
|
+
EmittedOnceTracker,
|
|
11
12
|
FileWatcher,
|
|
12
13
|
FingerprintAlreadyUsedError,
|
|
13
14
|
FirstTouchTracker,
|
|
@@ -30,6 +31,7 @@ import {
|
|
|
30
31
|
ensureVectorsInitialized,
|
|
31
32
|
generateCODEOWNERS,
|
|
32
33
|
getLicenseInfo,
|
|
34
|
+
hashProjectRoot,
|
|
33
35
|
isActive,
|
|
34
36
|
loadReviewConfig,
|
|
35
37
|
noDefaultProjectError,
|
|
@@ -43,7 +45,7 @@ import {
|
|
|
43
45
|
validateDefaultRoot,
|
|
44
46
|
wrapWithIndexingEnvelope,
|
|
45
47
|
writeCODEOWNERS
|
|
46
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-RGO3M3IR.js";
|
|
47
49
|
import {
|
|
48
50
|
VectorStore
|
|
49
51
|
} from "./chunk-NEHYSE2Y.js";
|
|
@@ -87,6 +89,7 @@ var stateManager = new ProjectStateManager({
|
|
|
87
89
|
maxProjects: DISABLE_MULTIPROJECT ? 1 : MAX_PROJECTS
|
|
88
90
|
});
|
|
89
91
|
var firstTouchTracker = new FirstTouchTracker();
|
|
92
|
+
var emittedOnceTracker = new EmittedOnceTracker();
|
|
90
93
|
async function initStore(state) {
|
|
91
94
|
if (!state.storePromise) {
|
|
92
95
|
state.storePromise = (async () => {
|
|
@@ -110,12 +113,20 @@ async function initParser(state) {
|
|
|
110
113
|
async function initGraph(state) {
|
|
111
114
|
if (!state.graphPromise) {
|
|
112
115
|
state.graphPromise = (async () => {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
116
|
+
try {
|
|
117
|
+
const parser = await initParser(state);
|
|
118
|
+
const graph = new DependencyGraph();
|
|
119
|
+
graph.setParser(parser);
|
|
120
|
+
await graph.buildFromDirectory(state.projectRoot);
|
|
121
|
+
state.graphInitialized = true;
|
|
122
|
+
return graph;
|
|
123
|
+
} catch (err) {
|
|
124
|
+
captureError(err, {
|
|
125
|
+
project_id: hashProjectRoot(state.projectRoot),
|
|
126
|
+
phase: "graph_init"
|
|
127
|
+
});
|
|
128
|
+
throw err;
|
|
129
|
+
}
|
|
119
130
|
})();
|
|
120
131
|
}
|
|
121
132
|
return state.graphPromise;
|
|
@@ -130,31 +141,60 @@ async function initSkeletonizer(state) {
|
|
|
130
141
|
}
|
|
131
142
|
return state.skeletonizerPromise;
|
|
132
143
|
}
|
|
144
|
+
function classifyResolutionSource(arg, env) {
|
|
145
|
+
if (arg !== void 0) {
|
|
146
|
+
return /[/\\~]|^[A-Za-z]:/.test(arg) ? "arg-path" : "alias";
|
|
147
|
+
}
|
|
148
|
+
return env ? "env" : "cwd";
|
|
149
|
+
}
|
|
133
150
|
function buildContext(defaultRoot, noDefaultMode) {
|
|
134
151
|
const repoRegistry = new RepoRegistry(repoRegistryPath);
|
|
135
152
|
function resolveOrDefault(arg) {
|
|
153
|
+
let state;
|
|
154
|
+
let source;
|
|
136
155
|
if (DISABLE_MULTIPROJECT) {
|
|
137
156
|
if (!defaultRoot) {
|
|
138
157
|
throw new Error("CTXLOOM_DISABLE_MULTIPROJECT=1 but server has no default root.");
|
|
139
158
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
if (arg === void 0) {
|
|
159
|
+
state = stateManager.get(defaultRoot);
|
|
160
|
+
source = "env";
|
|
161
|
+
} else if (arg === void 0) {
|
|
143
162
|
if (!defaultRoot) {
|
|
144
163
|
throw new Error("no_default_project");
|
|
145
164
|
}
|
|
146
|
-
|
|
165
|
+
state = stateManager.get(defaultRoot);
|
|
166
|
+
source = classifyResolutionSource(void 0, process.env.CTXLOOM_ROOT);
|
|
167
|
+
} else {
|
|
168
|
+
const outcome = resolveProjectRoot({
|
|
169
|
+
arg,
|
|
170
|
+
env: process.env.CTXLOOM_ROOT,
|
|
171
|
+
cwd: process.cwd(),
|
|
172
|
+
registry: repoRegistry
|
|
173
|
+
});
|
|
174
|
+
if (outcome.kind !== "ok") {
|
|
175
|
+
throw new Error(JSON.stringify(outcome));
|
|
176
|
+
}
|
|
177
|
+
state = stateManager.get(outcome.root);
|
|
178
|
+
source = classifyResolutionSource(arg, process.env.CTXLOOM_ROOT);
|
|
147
179
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
180
|
+
try {
|
|
181
|
+
const projectId = hashProjectRoot(state.projectRoot);
|
|
182
|
+
if (emittedOnceTracker.markAndCheck(`project_resolved:${projectId}`)) {
|
|
183
|
+
track("project_resolved", {
|
|
184
|
+
project_id: projectId,
|
|
185
|
+
source,
|
|
186
|
+
via_alias: source === "alias"
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
if (stateManager.size() >= 2 && emittedOnceTracker.markAndCheck("multi_project_active")) {
|
|
190
|
+
track("multi_project_active", {
|
|
191
|
+
active_count: stateManager.size(),
|
|
192
|
+
cap: stateManager.max
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
} catch {
|
|
156
196
|
}
|
|
157
|
-
return
|
|
197
|
+
return state;
|
|
158
198
|
}
|
|
159
199
|
const ctx = {
|
|
160
200
|
projectRoot: defaultRoot ?? "",
|
|
@@ -231,6 +271,17 @@ function createServer() {
|
|
|
231
271
|
const root = state.projectRoot;
|
|
232
272
|
const graphFirstTouch = firstTouchTracker.markAndCheck(root, "graph");
|
|
233
273
|
if (graphFirstTouch) {
|
|
274
|
+
try {
|
|
275
|
+
const graphInst = state.graphPromise ? await state.graphPromise : null;
|
|
276
|
+
track("project_first_touch", {
|
|
277
|
+
project_id: hashProjectRoot(root),
|
|
278
|
+
tier: "graph",
|
|
279
|
+
duration_ms: durationMs,
|
|
280
|
+
nodes: graphInst?.allFiles().length ?? null,
|
|
281
|
+
edges: graphInst?.edgeCount() ?? null
|
|
282
|
+
});
|
|
283
|
+
} catch {
|
|
284
|
+
}
|
|
234
285
|
const wrapped = wrapWithIndexingEnvelope(
|
|
235
286
|
{ firstTouch: true, projectRoot: root, tier: "graph", durationMs },
|
|
236
287
|
text
|
|
@@ -240,6 +291,14 @@ function createServer() {
|
|
|
240
291
|
if (state.vectorsInitialized) {
|
|
241
292
|
const vectorsFirstTouch = firstTouchTracker.markAndCheck(root, "vectors");
|
|
242
293
|
if (vectorsFirstTouch) {
|
|
294
|
+
try {
|
|
295
|
+
track("project_first_touch", {
|
|
296
|
+
project_id: hashProjectRoot(root),
|
|
297
|
+
tier: "vectors",
|
|
298
|
+
duration_ms: durationMs
|
|
299
|
+
});
|
|
300
|
+
} catch {
|
|
301
|
+
}
|
|
243
302
|
const wrapped = wrapWithIndexingEnvelope(
|
|
244
303
|
{ firstTouch: true, projectRoot: root, tier: "vectors", durationMs },
|
|
245
304
|
text
|
|
@@ -250,9 +309,31 @@ function createServer() {
|
|
|
250
309
|
} catch {
|
|
251
310
|
}
|
|
252
311
|
}
|
|
312
|
+
if (Math.random() < 0.25) {
|
|
313
|
+
try {
|
|
314
|
+
const projectRootArg2 = args2?.project_root;
|
|
315
|
+
if (!ctx.noDefaultMode || projectRootArg2 !== void 0) {
|
|
316
|
+
const sampleState = resolveOrDefault(projectRootArg2);
|
|
317
|
+
track("tool_dispatched", {
|
|
318
|
+
project_id: hashProjectRoot(sampleState.projectRoot),
|
|
319
|
+
tool: name,
|
|
320
|
+
duration_ms: durationMs
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
} catch {
|
|
324
|
+
}
|
|
325
|
+
}
|
|
253
326
|
return { content: [{ type: "text", text }] };
|
|
254
327
|
} catch (err) {
|
|
255
328
|
if (err instanceof Error && err.message === "no_default_project") {
|
|
329
|
+
const hadArg10 = args2?.project_root !== void 0;
|
|
330
|
+
try {
|
|
331
|
+
track("project_resolution_failed", {
|
|
332
|
+
error_code: "no_default_project",
|
|
333
|
+
had_arg: hadArg10
|
|
334
|
+
});
|
|
335
|
+
} catch {
|
|
336
|
+
}
|
|
256
337
|
const xml = noDefaultProjectError({
|
|
257
338
|
attemptedRoot: PROJECT_ROOT,
|
|
258
339
|
resolutionChain: "CTXLOOM_ROOT env var\u2192unset, fallback_cwd\u2192" + PROJECT_ROOT,
|
|
@@ -264,6 +345,13 @@ function createServer() {
|
|
|
264
345
|
try {
|
|
265
346
|
const parsed2 = JSON.parse(err.message);
|
|
266
347
|
if (parsed2.kind === "alias_not_found") {
|
|
348
|
+
try {
|
|
349
|
+
track("project_resolution_failed", {
|
|
350
|
+
error_code: "alias_not_found",
|
|
351
|
+
had_arg: true
|
|
352
|
+
});
|
|
353
|
+
} catch {
|
|
354
|
+
}
|
|
267
355
|
const xml = aliasNotFoundError({
|
|
268
356
|
alias: String(parsed2.alias ?? ""),
|
|
269
357
|
didYouMean: Array.isArray(parsed2.didYouMean) ? parsed2.didYouMean : []
|
|
@@ -271,6 +359,13 @@ function createServer() {
|
|
|
271
359
|
return { content: [{ type: "text", text: xml }], isError: true };
|
|
272
360
|
}
|
|
273
361
|
if (parsed2.kind === "project_root_not_found") {
|
|
362
|
+
try {
|
|
363
|
+
track("project_resolution_failed", {
|
|
364
|
+
error_code: "project_root_not_found",
|
|
365
|
+
had_arg: true
|
|
366
|
+
});
|
|
367
|
+
} catch {
|
|
368
|
+
}
|
|
274
369
|
const xml = projectRootNotFoundError({
|
|
275
370
|
path: String(parsed2.attemptedPath ?? ""),
|
|
276
371
|
resolutionChain: String(parsed2.resolutionChain ?? "")
|
|
@@ -280,6 +375,20 @@ function createServer() {
|
|
|
280
375
|
} catch {
|
|
281
376
|
}
|
|
282
377
|
}
|
|
378
|
+
try {
|
|
379
|
+
const projectRootArg13 = args2?.project_root;
|
|
380
|
+
let projectIdForCtx;
|
|
381
|
+
try {
|
|
382
|
+
const fallbackState = resolveOrDefault(projectRootArg13);
|
|
383
|
+
projectIdForCtx = hashProjectRoot(fallbackState.projectRoot);
|
|
384
|
+
} catch {
|
|
385
|
+
}
|
|
386
|
+
captureError(err, {
|
|
387
|
+
tool: name,
|
|
388
|
+
...projectIdForCtx ? { project_id: projectIdForCtx } : {}
|
|
389
|
+
});
|
|
390
|
+
} catch {
|
|
391
|
+
}
|
|
283
392
|
return {
|
|
284
393
|
content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
|
|
285
394
|
isError: true
|
|
@@ -300,6 +409,7 @@ async function startServer(opts = {}) {
|
|
|
300
409
|
logger.warn(
|
|
301
410
|
"[DEPRECATED] CTXLOOM_DISABLE_MULTIPROJECT=1 is set \u2014 multi-project support is disabled. maxProjects is capped at 1 and project_root arguments are ignored. This kill switch will be removed in a future release."
|
|
302
411
|
);
|
|
412
|
+
track("kill_switch_active", { cap: 1 });
|
|
303
413
|
}
|
|
304
414
|
try {
|
|
305
415
|
const { execSync: execSync2 } = await import("child_process");
|
|
@@ -782,7 +892,7 @@ try {
|
|
|
782
892
|
} catch {
|
|
783
893
|
}
|
|
784
894
|
var args = process.argv.slice(2);
|
|
785
|
-
var ctxloomVersion = "1.1.
|
|
895
|
+
var ctxloomVersion = "1.1.2".length > 0 ? "1.1.2" : "dev";
|
|
786
896
|
if (args.includes("--version") || args.includes("-v")) {
|
|
787
897
|
process.stdout.write(`ctxloom ${ctxloomVersion}
|
|
788
898
|
`);
|
|
@@ -855,7 +965,7 @@ async function checkLicense() {
|
|
|
855
965
|
if (command !== void 0 && LICENSE_GATE_BYPASS_COMMANDS.has(command)) return;
|
|
856
966
|
const ciKey = process.env["CTXLOOM_LICENSE_KEY"];
|
|
857
967
|
if (ciKey) {
|
|
858
|
-
const { ApiClient } = await import("./src-
|
|
968
|
+
const { ApiClient } = await import("./src-5MVUPLMZ.js");
|
|
859
969
|
const client = new ApiClient(process.env["CTXLOOM_API_BASE"]);
|
|
860
970
|
try {
|
|
861
971
|
const result = await client.validate(ciKey, "ci-ephemeral");
|
|
@@ -876,7 +986,7 @@ ctxloom license is ${result.status}.
|
|
|
876
986
|
}
|
|
877
987
|
const active = await isActive();
|
|
878
988
|
if (!active) {
|
|
879
|
-
track("license_gate_hit"
|
|
989
|
+
track("license_gate_hit");
|
|
880
990
|
process.stderr.write(errorBlock("ctxloom requires an active license.", [
|
|
881
991
|
`${style.bold("ctxloom trial")} ${style.dim("\u2014 start a 7-day free trial")}`,
|
|
882
992
|
`${style.bold("ctxloom activate <KEY>")} ${style.dim("\u2014 activate a purchased key")}`,
|
|
@@ -925,7 +1035,7 @@ async function runTrial() {
|
|
|
925
1035
|
process.stdout.write(` ${style.dim(`Your license key will arrive at ${email} after checkout.`)}
|
|
926
1036
|
`);
|
|
927
1037
|
process.stdout.write(nextStep("Activate on this machine", "ctxloom activate <KEY>"));
|
|
928
|
-
track("trial_started",
|
|
1038
|
+
track("trial_started", { email });
|
|
929
1039
|
} catch (err) {
|
|
930
1040
|
if (err instanceof FingerprintAlreadyUsedError) {
|
|
931
1041
|
process.stdout.write(errorBlock("A trial has already been used on this machine.", [
|
|
@@ -970,7 +1080,7 @@ async function runActivate(key) {
|
|
|
970
1080
|
["Machine", `${os.hostname()} (${os.platform()}-${os.arch()})`]
|
|
971
1081
|
]));
|
|
972
1082
|
process.stdout.write(nextStep("Configure your AI tools", "ctxloom setup"));
|
|
973
|
-
track("license_activated",
|
|
1083
|
+
track("license_activated", { tier: license.tier });
|
|
974
1084
|
} catch (err) {
|
|
975
1085
|
if (err instanceof SeatLimitError) {
|
|
976
1086
|
process.stdout.write(errorBlock("Seat limit reached.", [
|
|
@@ -1007,7 +1117,7 @@ async function runDeactivate() {
|
|
|
1007
1117
|
process.stdout.write(` ${success("Deactivated")}
|
|
1008
1118
|
`);
|
|
1009
1119
|
process.stdout.write(nextStep("Activate on another machine", "ctxloom activate <KEY>"));
|
|
1010
|
-
track("license_deactivated"
|
|
1120
|
+
track("license_deactivated");
|
|
1011
1121
|
} catch (err) {
|
|
1012
1122
|
if (err instanceof NetworkError) {
|
|
1013
1123
|
process.stderr.write(errorBlock("Deactivation failed \u2014 network error.", [
|
|
@@ -1208,7 +1318,7 @@ async function main() {
|
|
|
1208
1318
|
process.exit(1);
|
|
1209
1319
|
}
|
|
1210
1320
|
if (alias !== void 0) {
|
|
1211
|
-
const { validateAlias } = await import("./src-
|
|
1321
|
+
const { validateAlias } = await import("./src-5MVUPLMZ.js");
|
|
1212
1322
|
const v = validateAlias(alias);
|
|
1213
1323
|
if (!v.ok) {
|
|
1214
1324
|
console.error(`[ctxloom] Invalid alias: ${v.reason}`);
|
|
@@ -1224,6 +1334,12 @@ async function main() {
|
|
|
1224
1334
|
console.error(`[ctxloom] ${err instanceof Error ? err.message : String(err)}`);
|
|
1225
1335
|
process.exit(1);
|
|
1226
1336
|
}
|
|
1337
|
+
if (alias !== void 0) {
|
|
1338
|
+
track("alias_registered", {
|
|
1339
|
+
alias_length: alias.length,
|
|
1340
|
+
was_collision: false
|
|
1341
|
+
});
|
|
1342
|
+
}
|
|
1227
1343
|
console.log(`[ctxloom] Registered repo: ${absPath}${alias ? ` (alias: ${alias})` : ""}`);
|
|
1228
1344
|
console.log(`[ctxloom] LanceDB path: ${dbPath}`);
|
|
1229
1345
|
console.log(`[ctxloom] Registry: ${registryPath}`);
|
|
@@ -1446,7 +1562,7 @@ Suggested reviewers for ${files.length} file(s):`);
|
|
|
1446
1562
|
process.stderr.write("[ctxloom] --limit must be a non-negative integer (0 for unlimited)\n");
|
|
1447
1563
|
process.exit(2);
|
|
1448
1564
|
}
|
|
1449
|
-
const { loadRulesConfig, RulesChecker, formatText, formatJson, RulesConfigError } = await import("./src-
|
|
1565
|
+
const { loadRulesConfig, RulesChecker, formatText, formatJson, RulesConfigError } = await import("./src-5MVUPLMZ.js");
|
|
1450
1566
|
let config;
|
|
1451
1567
|
try {
|
|
1452
1568
|
config = await loadRulesConfig(root);
|
|
@@ -1470,7 +1586,7 @@ Suggested reviewers for ${files.length} file(s):`);
|
|
|
1470
1586
|
}
|
|
1471
1587
|
let graph;
|
|
1472
1588
|
if (useSnapshot) {
|
|
1473
|
-
const { DependencyGraph: DG } = await import("./src-
|
|
1589
|
+
const { DependencyGraph: DG } = await import("./src-5MVUPLMZ.js");
|
|
1474
1590
|
graph = new DG();
|
|
1475
1591
|
const loaded = await graph.loadSnapshotOnly(root);
|
|
1476
1592
|
if (!loaded) {
|
|
@@ -1479,7 +1595,7 @@ Suggested reviewers for ${files.length} file(s):`);
|
|
|
1479
1595
|
}
|
|
1480
1596
|
} else {
|
|
1481
1597
|
process.stderr.write("[ctxloom] Building dependency graph...\n");
|
|
1482
|
-
const { ASTParser: ASTParser2, DependencyGraph: DependencyGraph2 } = await import("./src-
|
|
1598
|
+
const { ASTParser: ASTParser2, DependencyGraph: DependencyGraph2 } = await import("./src-5MVUPLMZ.js");
|
|
1483
1599
|
let parser;
|
|
1484
1600
|
try {
|
|
1485
1601
|
parser = new ASTParser2();
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
DEFAULT_REVIEW_CONFIG,
|
|
11
11
|
DependencyGraph,
|
|
12
12
|
EmailAlreadyUsedError,
|
|
13
|
+
EmittedOnceTracker,
|
|
13
14
|
FileWatcher,
|
|
14
15
|
Fingerprint,
|
|
15
16
|
FingerprintAlreadyUsedError,
|
|
@@ -66,6 +67,8 @@ import {
|
|
|
66
67
|
generateCODEOWNERS,
|
|
67
68
|
getImpactRadius,
|
|
68
69
|
getLicenseInfo,
|
|
70
|
+
getOrCreateDistinctId,
|
|
71
|
+
hashProjectRoot,
|
|
69
72
|
isActive,
|
|
70
73
|
isSiloed,
|
|
71
74
|
listNamedSnapshots,
|
|
@@ -73,6 +76,7 @@ import {
|
|
|
73
76
|
loadReviewConfig,
|
|
74
77
|
loadRulesConfig,
|
|
75
78
|
loadTrendSeries,
|
|
79
|
+
markAliasSent,
|
|
76
80
|
maybePrintExpiryWarning,
|
|
77
81
|
mergeIntoFile,
|
|
78
82
|
noDefaultProjectError,
|
|
@@ -95,7 +99,7 @@ import {
|
|
|
95
99
|
validateDefaultRoot,
|
|
96
100
|
wrapWithIndexingEnvelope,
|
|
97
101
|
writeCODEOWNERS
|
|
98
|
-
} from "./chunk-
|
|
102
|
+
} from "./chunk-RGO3M3IR.js";
|
|
99
103
|
import {
|
|
100
104
|
VectorStore
|
|
101
105
|
} from "./chunk-NEHYSE2Y.js";
|
|
@@ -121,6 +125,7 @@ export {
|
|
|
121
125
|
DependencyGraph,
|
|
122
126
|
EMBEDDING_DIMENSION,
|
|
123
127
|
EmailAlreadyUsedError,
|
|
128
|
+
EmittedOnceTracker,
|
|
124
129
|
FileWatcher,
|
|
125
130
|
Fingerprint,
|
|
126
131
|
FingerprintAlreadyUsedError,
|
|
@@ -180,6 +185,8 @@ export {
|
|
|
180
185
|
generateEmbedding,
|
|
181
186
|
getImpactRadius,
|
|
182
187
|
getLicenseInfo,
|
|
188
|
+
getOrCreateDistinctId,
|
|
189
|
+
hashProjectRoot,
|
|
183
190
|
indexDirectory,
|
|
184
191
|
isActive,
|
|
185
192
|
isSiloed,
|
|
@@ -189,6 +196,7 @@ export {
|
|
|
189
196
|
loadRulesConfig,
|
|
190
197
|
loadTrendSeries,
|
|
191
198
|
logger,
|
|
199
|
+
markAliasSent,
|
|
192
200
|
maybePrintExpiryWarning,
|
|
193
201
|
mergeIntoFile,
|
|
194
202
|
noDefaultProjectError,
|
|
@@ -212,4 +220,4 @@ export {
|
|
|
212
220
|
wrapWithIndexingEnvelope,
|
|
213
221
|
writeCODEOWNERS
|
|
214
222
|
};
|
|
215
|
-
//# sourceMappingURL=src-
|
|
223
|
+
//# sourceMappingURL=src-5MVUPLMZ.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ctxloom-pro",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "ctxloom — The Universal Code Context Engine. A local-first MCP server providing intelligent code context via hybrid Vector + AST + Graph search with Skeletonization (92% token reduction).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|