ctxloom-pro 1.1.4 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -630,6 +630,19 @@ Token counts use the standard 4 chars/token approximation. Per-repo range (57–
|
|
|
630
630
|
| `CTXLOOM_GRAMMAR_CDN` | CDN base URL for grammar downloads (air-gapped environments) | Built-in |
|
|
631
631
|
| `CTXLOOM_MAX_PROJECTS` | LRU cache cap for multi-project state (v1.1.0+) | `5` |
|
|
632
632
|
| `CTXLOOM_DISABLE_MULTIPROJECT` | Set to `1` to revert to v1.0.31 single-project mode (v1.1.0+) | (unset) |
|
|
633
|
+
| `CTXLOOM_NO_TELEMETRY` | Set to `1` to disable anonymous telemetry entirely (v1.2.0+) | (unset) |
|
|
634
|
+
| `CTXLOOM_TELEMETRY_LEVEL` | `all` / `error` / `off` — granular telemetry scope (v1.2.0+) | `all` |
|
|
635
|
+
| `DO_NOT_TRACK` | Universal cross-tool opt-out — equivalent to `CTXLOOM_NO_TELEMETRY=1` | (unset) |
|
|
636
|
+
|
|
637
|
+
---
|
|
638
|
+
|
|
639
|
+
## Telemetry
|
|
640
|
+
|
|
641
|
+
ctxloom collects **anonymous, opt-out telemetry** to understand which features are used and to catch crashes. **No file contents, paths, project names, or aliases are ever transmitted.** Project identifiers are SHA-256 hashes of the absolute path. The `distinct_id` is a random UUID at `~/.ctxloom/distinct_id`.
|
|
642
|
+
|
|
643
|
+
Disable with `CTXLOOM_NO_TELEMETRY=1` or the cross-tool `DO_NOT_TRACK=1`. For a granular middle ground (crash reports yes, usage analytics no) use `CTXLOOM_TELEMETRY_LEVEL=error`.
|
|
644
|
+
|
|
645
|
+
The complete list of events, properties, what is *never* collected, and how project paths are anonymized is documented in **[docs/TELEMETRY.md](docs/TELEMETRY.md)**.
|
|
633
646
|
|
|
634
647
|
---
|
|
635
648
|
|
|
@@ -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 path43 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 path37 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 path44 of paths) {
|
|
3137
|
+
this.nodeCounts.set(path44, (this.nodeCounts.get(path44) ?? 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 [path44, raw] of this.nodes) {
|
|
3285
|
+
nodes[path44] = {
|
|
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 [path44, raw] of Object.entries(s.nodes)) {
|
|
3301
|
+
idx.nodes.set(path44, {
|
|
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(path44) {
|
|
3315
|
+
const existing = this.nodes.get(path44);
|
|
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(path44, 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 [path44, 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[path44] = { 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 [path44, 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(path44, { 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(path44) {
|
|
3432
|
+
const existing = this.nodes.get(path44);
|
|
3433
3433
|
if (existing !== void 0) return existing;
|
|
3434
3434
|
const fresh = { authorWeights: {}, lastTouch: 0 };
|
|
3435
|
-
this.nodes.set(
|
|
3435
|
+
this.nodes.set(path44, 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: path44, errorMaps, issueData } = params;
|
|
4674
|
+
const fullPath = [...path44, ...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, path44, key) {
|
|
4791
4791
|
this._cachedPath = [];
|
|
4792
4792
|
this.parent = parent;
|
|
4793
4793
|
this.data = value;
|
|
4794
|
-
this._path =
|
|
4794
|
+
this._path = path44;
|
|
4795
4795
|
this._key = key;
|
|
4796
4796
|
}
|
|
4797
4797
|
get path() {
|
|
@@ -11311,11 +11311,20 @@ function markAliasSent(home) {
|
|
|
11311
11311
|
}
|
|
11312
11312
|
|
|
11313
11313
|
// ../../packages/core/src/license/telemetry.ts
|
|
11314
|
-
|
|
11315
|
-
|
|
11314
|
+
function resolveTelemetryLevel() {
|
|
11315
|
+
if (process.env["CTXLOOM_NO_TELEMETRY"] === "1" || process.env["DO_NOT_TRACK"] === "1") {
|
|
11316
|
+
return "off";
|
|
11317
|
+
}
|
|
11318
|
+
const raw = process.env["CTXLOOM_TELEMETRY_LEVEL"]?.toLowerCase();
|
|
11319
|
+
if (raw === "off" || raw === "error" || raw === "all") return raw;
|
|
11320
|
+
return "all";
|
|
11321
|
+
}
|
|
11322
|
+
var TELEMETRY_LEVEL = resolveTelemetryLevel();
|
|
11323
|
+
var TELEMETRY_DISABLED = TELEMETRY_LEVEL === "off";
|
|
11324
|
+
var CTXLOOM_VERSION = "1.2.0".length > 0 ? "1.2.0" : "dev";
|
|
11316
11325
|
var POSTHOG_HOST = "https://eu.i.posthog.com";
|
|
11317
|
-
var POSTHOG_KEY = process.env["POSTHOG_API_KEY"] ?? (
|
|
11318
|
-
var SENTRY_DSN = process.env["SENTRY_DSN"] ?? (
|
|
11326
|
+
var POSTHOG_KEY = process.env["POSTHOG_API_KEY"] ?? (true ? "phc_CiDkmFLcZ2K6uCpcoSUQLmFrnnUvsyXGhSxopX5TVKE6" : "");
|
|
11327
|
+
var SENTRY_DSN = process.env["SENTRY_DSN"] ?? (true ? "https://81c94a0f04a8e242dee493ac1e17f733@o4508531702497280.ingest.de.sentry.io/4511256875368528" : "");
|
|
11319
11328
|
var cachedDistinctId = null;
|
|
11320
11329
|
function resolveDistinctId() {
|
|
11321
11330
|
if (cachedDistinctId) return cachedDistinctId;
|
|
@@ -11327,7 +11336,7 @@ function resolveDistinctId() {
|
|
|
11327
11336
|
}
|
|
11328
11337
|
}
|
|
11329
11338
|
function track(event, props = {}) {
|
|
11330
|
-
if (
|
|
11339
|
+
if (TELEMETRY_LEVEL !== "all" || !POSTHOG_KEY) return;
|
|
11331
11340
|
const record = resolveDistinctId();
|
|
11332
11341
|
if (!record) return;
|
|
11333
11342
|
void sendPostHog(event, record.id, props);
|
|
@@ -11443,23 +11452,28 @@ function parseStack(stack) {
|
|
|
11443
11452
|
}).filter((f) => f !== null).slice(0, 20);
|
|
11444
11453
|
}
|
|
11445
11454
|
|
|
11455
|
+
// ../../packages/core/src/license/TelemetryNotice.ts
|
|
11456
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
11457
|
+
import path33 from "path";
|
|
11458
|
+
import os5 from "os";
|
|
11459
|
+
|
|
11446
11460
|
// ../../packages/core/src/server/ProjectState.ts
|
|
11447
|
-
import
|
|
11461
|
+
import path35 from "path";
|
|
11448
11462
|
|
|
11449
11463
|
// ../../packages/core/src/server/projectId.ts
|
|
11450
11464
|
import crypto5 from "crypto";
|
|
11451
|
-
import
|
|
11465
|
+
import path34 from "path";
|
|
11452
11466
|
|
|
11453
11467
|
// ../../packages/core/src/server/ProjectStateManager.ts
|
|
11454
11468
|
init_logger();
|
|
11455
11469
|
|
|
11456
11470
|
// ../../packages/core/src/server/resolveProjectRoot.ts
|
|
11457
11471
|
import fs29 from "fs";
|
|
11458
|
-
import
|
|
11472
|
+
import path36 from "path";
|
|
11459
11473
|
|
|
11460
11474
|
// server/loader.ts
|
|
11461
11475
|
async function loadContext(root) {
|
|
11462
|
-
const absRoot =
|
|
11476
|
+
const absRoot = path37.resolve(root);
|
|
11463
11477
|
const overlay = new GitOverlayStore(absRoot);
|
|
11464
11478
|
const gitEnabled = await overlay.loadSnapshot();
|
|
11465
11479
|
const graph = new DependencyGraph();
|
|
@@ -11713,20 +11727,20 @@ function buildOwnershipRouter(ctx) {
|
|
|
11713
11727
|
// server/routes/file.ts
|
|
11714
11728
|
import { Router as Router7 } from "express";
|
|
11715
11729
|
import fs30 from "fs/promises";
|
|
11716
|
-
import
|
|
11730
|
+
import path38 from "path";
|
|
11717
11731
|
function buildFileRouter(ctx) {
|
|
11718
11732
|
const router = Router7();
|
|
11719
11733
|
router.get("/", async (req, res) => {
|
|
11720
11734
|
const rel = req.query.path;
|
|
11721
11735
|
if (!rel) return res.status(400).json({ error: "missing path" });
|
|
11722
|
-
const abs =
|
|
11723
|
-
const rootBoundary = ctx.root.endsWith(
|
|
11736
|
+
const abs = path38.resolve(ctx.root, rel);
|
|
11737
|
+
const rootBoundary = ctx.root.endsWith(path38.sep) ? ctx.root : ctx.root + path38.sep;
|
|
11724
11738
|
if (abs !== ctx.root && !abs.startsWith(rootBoundary)) {
|
|
11725
11739
|
return res.status(403).json({ error: "forbidden" });
|
|
11726
11740
|
}
|
|
11727
11741
|
try {
|
|
11728
11742
|
const content = await fs30.readFile(abs, "utf-8");
|
|
11729
|
-
const ext =
|
|
11743
|
+
const ext = path38.extname(abs).slice(1);
|
|
11730
11744
|
res.json({ content, lines: content.split("\n").length, ext });
|
|
11731
11745
|
} catch {
|
|
11732
11746
|
res.status(404).json({ error: "not found" });
|
|
@@ -11738,7 +11752,7 @@ function buildFileRouter(ctx) {
|
|
|
11738
11752
|
// server/routes/open.ts
|
|
11739
11753
|
import { Router as Router8 } from "express";
|
|
11740
11754
|
import { execFile as execFile2 } from "child_process";
|
|
11741
|
-
import
|
|
11755
|
+
import path39 from "path";
|
|
11742
11756
|
function tryOpen(bin, abs) {
|
|
11743
11757
|
return new Promise((resolve) => {
|
|
11744
11758
|
execFile2(bin, [abs], { timeout: 5e3 }, (err) => resolve(!err));
|
|
@@ -11749,8 +11763,8 @@ function buildOpenRouter(ctx) {
|
|
|
11749
11763
|
router.post("/", async (req, res) => {
|
|
11750
11764
|
const rel = req.body?.path;
|
|
11751
11765
|
if (!rel || typeof rel !== "string") return res.status(400).json({ error: "missing path" });
|
|
11752
|
-
const abs =
|
|
11753
|
-
const rootBoundary = ctx.root.endsWith(
|
|
11766
|
+
const abs = path39.resolve(ctx.root, rel);
|
|
11767
|
+
const rootBoundary = ctx.root.endsWith(path39.sep) ? ctx.root : ctx.root + path39.sep;
|
|
11754
11768
|
if (abs !== ctx.root && !abs.startsWith(rootBoundary)) {
|
|
11755
11769
|
return res.status(403).json({ error: "forbidden" });
|
|
11756
11770
|
}
|
|
@@ -11762,7 +11776,7 @@ function buildOpenRouter(ctx) {
|
|
|
11762
11776
|
|
|
11763
11777
|
// server/routes/tokens.ts
|
|
11764
11778
|
import { Router as Router9 } from "express";
|
|
11765
|
-
import
|
|
11779
|
+
import path40 from "path";
|
|
11766
11780
|
import fs31 from "fs";
|
|
11767
11781
|
var CHARS_PER_TOKEN = 4;
|
|
11768
11782
|
var cache = null;
|
|
@@ -11778,7 +11792,7 @@ function buildTokensRouter(ctx) {
|
|
|
11778
11792
|
let fullChars = 0;
|
|
11779
11793
|
let skeletonChars = 0;
|
|
11780
11794
|
for (const file of files) {
|
|
11781
|
-
const absPath =
|
|
11795
|
+
const absPath = path40.join(ctx.root, file);
|
|
11782
11796
|
try {
|
|
11783
11797
|
const content = fs31.readFileSync(absPath, "utf-8");
|
|
11784
11798
|
fullChars += content.length;
|
|
@@ -11881,21 +11895,21 @@ function buildFileTrendsRouter(ctx) {
|
|
|
11881
11895
|
|
|
11882
11896
|
// server/routes/projects.ts
|
|
11883
11897
|
import { Router as Router12 } from "express";
|
|
11884
|
-
import
|
|
11898
|
+
import path42 from "path";
|
|
11885
11899
|
|
|
11886
11900
|
// server/projects.ts
|
|
11887
|
-
import { existsSync as
|
|
11888
|
-
import
|
|
11889
|
-
import
|
|
11901
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
|
|
11902
|
+
import os6 from "os";
|
|
11903
|
+
import path41 from "path";
|
|
11890
11904
|
import crypto6 from "crypto";
|
|
11891
|
-
var HOME =
|
|
11892
|
-
var REGISTRY_PATH =
|
|
11905
|
+
var HOME = os6.homedir();
|
|
11906
|
+
var REGISTRY_PATH = path41.join(HOME, ".ctxloom", "repos.json");
|
|
11893
11907
|
function slugFor(root) {
|
|
11894
|
-
const abs =
|
|
11908
|
+
const abs = path41.resolve(root);
|
|
11895
11909
|
return crypto6.createHash("sha1").update(abs).digest("hex").slice(0, 12);
|
|
11896
11910
|
}
|
|
11897
11911
|
function readRegistry() {
|
|
11898
|
-
if (!
|
|
11912
|
+
if (!existsSync4(REGISTRY_PATH)) return [];
|
|
11899
11913
|
try {
|
|
11900
11914
|
const raw = readFileSync4(REGISTRY_PATH, "utf-8");
|
|
11901
11915
|
const parsed = JSON.parse(raw);
|
|
@@ -11906,27 +11920,27 @@ function readRegistry() {
|
|
|
11906
11920
|
}
|
|
11907
11921
|
}
|
|
11908
11922
|
function listProjects(defaultRoot) {
|
|
11909
|
-
const absDefault =
|
|
11923
|
+
const absDefault = path41.resolve(defaultRoot);
|
|
11910
11924
|
const out = [
|
|
11911
11925
|
{
|
|
11912
11926
|
slug: slugFor(absDefault),
|
|
11913
|
-
name:
|
|
11927
|
+
name: path41.basename(absDefault) || absDefault,
|
|
11914
11928
|
root: absDefault,
|
|
11915
11929
|
isDefault: true,
|
|
11916
|
-
hasSnapshot:
|
|
11930
|
+
hasSnapshot: existsSync4(path41.join(absDefault, ".ctxloom"))
|
|
11917
11931
|
}
|
|
11918
11932
|
];
|
|
11919
11933
|
const seen = /* @__PURE__ */ new Set([absDefault]);
|
|
11920
11934
|
for (const entry of readRegistry()) {
|
|
11921
|
-
const abs =
|
|
11935
|
+
const abs = path41.resolve(entry.root);
|
|
11922
11936
|
if (seen.has(abs)) continue;
|
|
11923
11937
|
seen.add(abs);
|
|
11924
11938
|
const item = {
|
|
11925
11939
|
slug: slugFor(abs),
|
|
11926
|
-
name: entry.name ?? (
|
|
11940
|
+
name: entry.name ?? (path41.basename(abs) || abs),
|
|
11927
11941
|
root: abs,
|
|
11928
11942
|
isDefault: false,
|
|
11929
|
-
hasSnapshot:
|
|
11943
|
+
hasSnapshot: existsSync4(path41.join(abs, ".ctxloom"))
|
|
11930
11944
|
};
|
|
11931
11945
|
if (entry.alias !== void 0) item.alias = entry.alias;
|
|
11932
11946
|
out.push(item);
|
|
@@ -11979,7 +11993,7 @@ function buildProjectsRouter(deps) {
|
|
|
11979
11993
|
} catch (err) {
|
|
11980
11994
|
const detail = err instanceof Error ? err.message : String(err);
|
|
11981
11995
|
res.status(500).json({
|
|
11982
|
-
error: `failed to switch to ${
|
|
11996
|
+
error: `failed to switch to ${path42.basename(target.root)}: ${detail}`
|
|
11983
11997
|
});
|
|
11984
11998
|
}
|
|
11985
11999
|
});
|
|
@@ -12036,7 +12050,7 @@ function buildTelemetryRouter() {
|
|
|
12036
12050
|
}
|
|
12037
12051
|
|
|
12038
12052
|
// server/index.ts
|
|
12039
|
-
var __dirname2 =
|
|
12053
|
+
var __dirname2 = path43.dirname(fileURLToPath2(import.meta.url));
|
|
12040
12054
|
async function startDashboard(options) {
|
|
12041
12055
|
const { root, port, open } = options;
|
|
12042
12056
|
console.log(`ctxloom dashboard \u2014 loading context from ${root}...`);
|
|
@@ -12095,7 +12109,7 @@ async function startDashboard(options) {
|
|
|
12095
12109
|
}
|
|
12096
12110
|
activeWatcher = null;
|
|
12097
12111
|
}
|
|
12098
|
-
const snapshotDir =
|
|
12112
|
+
const snapshotDir = path43.join(targetRoot, ".ctxloom");
|
|
12099
12113
|
try {
|
|
12100
12114
|
activeWatcher = fs32.watch(snapshotDir, (_event, filename) => {
|
|
12101
12115
|
if (!filename || !filename.includes("snapshot")) return;
|
|
@@ -12126,12 +12140,12 @@ async function startDashboard(options) {
|
|
|
12126
12140
|
attachSnapshotWatcher(newRoot);
|
|
12127
12141
|
}
|
|
12128
12142
|
}));
|
|
12129
|
-
const clientDist =
|
|
12130
|
-
const clientDistExists = fs32.existsSync(
|
|
12143
|
+
const clientDist = path43.join(__dirname2, "../dashboard/client");
|
|
12144
|
+
const clientDistExists = fs32.existsSync(path43.join(clientDist, "index.html"));
|
|
12131
12145
|
if (clientDistExists) {
|
|
12132
12146
|
app.use(express.static(clientDist, { dotfiles: "allow" }));
|
|
12133
12147
|
app.get(/.*/, (_req, res) => {
|
|
12134
|
-
res.sendFile(
|
|
12148
|
+
res.sendFile(path43.join(clientDist, "index.html"), { dotfiles: "allow" });
|
|
12135
12149
|
});
|
|
12136
12150
|
} else {
|
|
12137
12151
|
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 path35 of paths) {
|
|
3410
|
+
this.nodeCounts.set(path35, (this.nodeCounts.get(path35) ?? 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 [path35, raw] of this.nodes) {
|
|
3558
|
+
nodes[path35] = {
|
|
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 [path35, raw] of Object.entries(s.nodes)) {
|
|
3574
|
+
idx.nodes.set(path35, {
|
|
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(path35) {
|
|
3588
|
+
const existing = this.nodes.get(path35);
|
|
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(path35, 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 [path35, 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[path35] = { 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 [path35, 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(path35, { 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(path35) {
|
|
3705
|
+
const existing = this.nodes.get(path35);
|
|
3706
3706
|
if (existing !== void 0) return existing;
|
|
3707
3707
|
const fresh = { authorWeights: {}, lastTouch: 0 };
|
|
3708
|
-
this.nodes.set(
|
|
3708
|
+
this.nodes.set(path35, fresh);
|
|
3709
3709
|
return fresh;
|
|
3710
3710
|
}
|
|
3711
3711
|
};
|
|
@@ -8837,8 +8837,20 @@ function markAliasSent(home) {
|
|
|
8837
8837
|
}
|
|
8838
8838
|
|
|
8839
8839
|
// packages/core/src/license/telemetry.ts
|
|
8840
|
-
|
|
8841
|
-
|
|
8840
|
+
function resolveTelemetryLevel() {
|
|
8841
|
+
if (process.env["CTXLOOM_NO_TELEMETRY"] === "1" || process.env["DO_NOT_TRACK"] === "1") {
|
|
8842
|
+
return "off";
|
|
8843
|
+
}
|
|
8844
|
+
const raw = process.env["CTXLOOM_TELEMETRY_LEVEL"]?.toLowerCase();
|
|
8845
|
+
if (raw === "off" || raw === "error" || raw === "all") return raw;
|
|
8846
|
+
return "all";
|
|
8847
|
+
}
|
|
8848
|
+
var TELEMETRY_LEVEL = resolveTelemetryLevel();
|
|
8849
|
+
var TELEMETRY_DISABLED = TELEMETRY_LEVEL === "off";
|
|
8850
|
+
function getTelemetryLevel() {
|
|
8851
|
+
return TELEMETRY_LEVEL;
|
|
8852
|
+
}
|
|
8853
|
+
var CTXLOOM_VERSION = "1.2.0".length > 0 ? "1.2.0" : "dev";
|
|
8842
8854
|
var POSTHOG_HOST = "https://eu.i.posthog.com";
|
|
8843
8855
|
var POSTHOG_KEY = process.env["POSTHOG_API_KEY"] ?? (true ? "phc_CiDkmFLcZ2K6uCpcoSUQLmFrnnUvsyXGhSxopX5TVKE6" : "");
|
|
8844
8856
|
var SENTRY_DSN = process.env["SENTRY_DSN"] ?? (true ? "https://81c94a0f04a8e242dee493ac1e17f733@o4508531702497280.ingest.de.sentry.io/4511256875368528" : "");
|
|
@@ -8853,7 +8865,7 @@ function resolveDistinctId() {
|
|
|
8853
8865
|
}
|
|
8854
8866
|
}
|
|
8855
8867
|
function track(event, props = {}) {
|
|
8856
|
-
if (
|
|
8868
|
+
if (TELEMETRY_LEVEL !== "all" || !POSTHOG_KEY) return;
|
|
8857
8869
|
const record = resolveDistinctId();
|
|
8858
8870
|
if (!record) return;
|
|
8859
8871
|
void sendPostHog(event, record.id, props);
|
|
@@ -8969,14 +8981,32 @@ function parseStack(stack) {
|
|
|
8969
8981
|
}).filter((f) => f !== null).slice(0, 20);
|
|
8970
8982
|
}
|
|
8971
8983
|
|
|
8984
|
+
// packages/core/src/license/TelemetryNotice.ts
|
|
8985
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
8986
|
+
import path31 from "path";
|
|
8987
|
+
import os5 from "os";
|
|
8988
|
+
function noticePath(home) {
|
|
8989
|
+
return path31.join(home ?? os5.homedir(), ".ctxloom", "telemetry_notice_shown");
|
|
8990
|
+
}
|
|
8991
|
+
function shouldShowTelemetryNotice(home) {
|
|
8992
|
+
const filePath = noticePath(home);
|
|
8993
|
+
if (existsSync3(filePath)) return false;
|
|
8994
|
+
try {
|
|
8995
|
+
mkdirSync3(path31.dirname(filePath), { recursive: true });
|
|
8996
|
+
writeFileSync3(filePath, (/* @__PURE__ */ new Date()).toISOString(), { mode: 384 });
|
|
8997
|
+
} catch {
|
|
8998
|
+
}
|
|
8999
|
+
return true;
|
|
9000
|
+
}
|
|
9001
|
+
|
|
8972
9002
|
// packages/core/src/server/ProjectState.ts
|
|
8973
|
-
import
|
|
9003
|
+
import path33 from "path";
|
|
8974
9004
|
|
|
8975
9005
|
// packages/core/src/server/projectId.ts
|
|
8976
9006
|
import crypto5 from "crypto";
|
|
8977
|
-
import
|
|
9007
|
+
import path32 from "path";
|
|
8978
9008
|
function hashProjectRoot(absPath) {
|
|
8979
|
-
const canonical =
|
|
9009
|
+
const canonical = path32.resolve(absPath);
|
|
8980
9010
|
return crypto5.createHash("sha256").update(canonical).digest("hex").slice(0, 16);
|
|
8981
9011
|
}
|
|
8982
9012
|
|
|
@@ -8984,7 +9014,7 @@ function hashProjectRoot(absPath) {
|
|
|
8984
9014
|
function createProjectState(projectRoot, opts = {}) {
|
|
8985
9015
|
return {
|
|
8986
9016
|
projectRoot,
|
|
8987
|
-
dbPath:
|
|
9017
|
+
dbPath: path33.join(projectRoot, ".ctxloom", "vectors.lancedb"),
|
|
8988
9018
|
pinned: opts.pinned ?? false,
|
|
8989
9019
|
lastTouchedAt: Date.now(),
|
|
8990
9020
|
vectorsInitialized: false,
|
|
@@ -9137,7 +9167,7 @@ var ProjectStateManager = class {
|
|
|
9137
9167
|
|
|
9138
9168
|
// packages/core/src/server/resolveProjectRoot.ts
|
|
9139
9169
|
import fs27 from "fs";
|
|
9140
|
-
import
|
|
9170
|
+
import path34 from "path";
|
|
9141
9171
|
var PATH_SEPARATOR_PATTERN = /[/\\~]|^[A-Za-z]:/;
|
|
9142
9172
|
function looksLikePath(value) {
|
|
9143
9173
|
return PATH_SEPARATOR_PATTERN.test(value);
|
|
@@ -9162,9 +9192,9 @@ function resolvePathSafely(p, cwd) {
|
|
|
9162
9192
|
let expanded = p;
|
|
9163
9193
|
if (p === "~" || p.startsWith("~/")) {
|
|
9164
9194
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
9165
|
-
expanded = p === "~" ? home :
|
|
9195
|
+
expanded = p === "~" ? home : path34.join(home, p.slice(2));
|
|
9166
9196
|
}
|
|
9167
|
-
return
|
|
9197
|
+
return path34.isAbsolute(expanded) ? path34.resolve(expanded) : path34.resolve(cwd, expanded);
|
|
9168
9198
|
}
|
|
9169
9199
|
function realpathOrSame(p) {
|
|
9170
9200
|
try {
|
|
@@ -9236,7 +9266,7 @@ function validateDefaultRoot(candidate) {
|
|
|
9236
9266
|
} catch {
|
|
9237
9267
|
return false;
|
|
9238
9268
|
}
|
|
9239
|
-
return PROJECT_MARKERS.some((m) => fs27.existsSync(
|
|
9269
|
+
return PROJECT_MARKERS.some((m) => fs27.existsSync(path34.join(candidate, m)));
|
|
9240
9270
|
}
|
|
9241
9271
|
|
|
9242
9272
|
// packages/core/src/server/structuredErrors.ts
|
|
@@ -9391,8 +9421,10 @@ export {
|
|
|
9391
9421
|
startTrial,
|
|
9392
9422
|
getOrCreateDistinctId,
|
|
9393
9423
|
markAliasSent,
|
|
9424
|
+
getTelemetryLevel,
|
|
9394
9425
|
track,
|
|
9395
9426
|
captureError,
|
|
9427
|
+
shouldShowTelemetryNotice,
|
|
9396
9428
|
hashProjectRoot,
|
|
9397
9429
|
createProjectState,
|
|
9398
9430
|
ensureVectorsInitialized,
|
|
@@ -9409,4 +9441,4 @@ export {
|
|
|
9409
9441
|
FirstTouchTracker,
|
|
9410
9442
|
EmittedOnceTracker
|
|
9411
9443
|
};
|
|
9412
|
-
//# sourceMappingURL=chunk-
|
|
9444
|
+
//# sourceMappingURL=chunk-4B2S3WJ5.js.map
|
package/dist/index.js
CHANGED
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
ensureVectorsInitialized,
|
|
32
32
|
generateCODEOWNERS,
|
|
33
33
|
getLicenseInfo,
|
|
34
|
+
getTelemetryLevel,
|
|
34
35
|
hashProjectRoot,
|
|
35
36
|
isActive,
|
|
36
37
|
loadReviewConfig,
|
|
@@ -40,12 +41,13 @@ import {
|
|
|
40
41
|
resolveProjectRoot,
|
|
41
42
|
resolveViaGitHubApi,
|
|
42
43
|
scoreReviewers,
|
|
44
|
+
shouldShowTelemetryNotice,
|
|
43
45
|
startTrial,
|
|
44
46
|
track,
|
|
45
47
|
validateDefaultRoot,
|
|
46
48
|
wrapWithIndexingEnvelope,
|
|
47
49
|
writeCODEOWNERS
|
|
48
|
-
} from "./chunk-
|
|
50
|
+
} from "./chunk-4B2S3WJ5.js";
|
|
49
51
|
import {
|
|
50
52
|
VectorStore
|
|
51
53
|
} from "./chunk-NEHYSE2Y.js";
|
|
@@ -892,7 +894,7 @@ try {
|
|
|
892
894
|
} catch {
|
|
893
895
|
}
|
|
894
896
|
var args = process.argv.slice(2);
|
|
895
|
-
var ctxloomVersion = "1.
|
|
897
|
+
var ctxloomVersion = "1.2.0".length > 0 ? "1.2.0" : "dev";
|
|
896
898
|
if (args.includes("--version") || args.includes("-v")) {
|
|
897
899
|
process.stdout.write(`ctxloom ${ctxloomVersion}
|
|
898
900
|
`);
|
|
@@ -965,7 +967,7 @@ async function checkLicense() {
|
|
|
965
967
|
if (command !== void 0 && LICENSE_GATE_BYPASS_COMMANDS.has(command)) return;
|
|
966
968
|
const ciKey = process.env["CTXLOOM_LICENSE_KEY"];
|
|
967
969
|
if (ciKey) {
|
|
968
|
-
const { ApiClient } = await import("./src-
|
|
970
|
+
const { ApiClient } = await import("./src-CNHVHDBT.js");
|
|
969
971
|
const client = new ApiClient(process.env["CTXLOOM_API_BASE"]);
|
|
970
972
|
try {
|
|
971
973
|
const result = await client.validate(ciKey, "ci-ephemeral");
|
|
@@ -1169,7 +1171,26 @@ async function runStatus() {
|
|
|
1169
1171
|
]));
|
|
1170
1172
|
process.stdout.write("\n");
|
|
1171
1173
|
}
|
|
1174
|
+
function maybePrintTelemetryNotice() {
|
|
1175
|
+
if (command === void 0) return;
|
|
1176
|
+
if (getTelemetryLevel() === "off") return;
|
|
1177
|
+
if (!shouldShowTelemetryNotice()) return;
|
|
1178
|
+
process.stderr.write(
|
|
1179
|
+
`
|
|
1180
|
+
${style.dim("\u2500".repeat(60))}
|
|
1181
|
+
${style.bold("ctxloom collects anonymous usage telemetry")} to improve the tool.
|
|
1182
|
+
No file contents, paths, or aliases are ever transmitted.
|
|
1183
|
+
|
|
1184
|
+
Disable with: ${style.highlight("CTXLOOM_NO_TELEMETRY=1")}
|
|
1185
|
+
Errors only: ${style.highlight("CTXLOOM_TELEMETRY_LEVEL=error")}
|
|
1186
|
+
Details: ${style.highlight("https://github.com/kodiii/ctxloom/blob/main/docs/TELEMETRY.md")}
|
|
1187
|
+
${style.dim("\u2500".repeat(60))}
|
|
1188
|
+
|
|
1189
|
+
`
|
|
1190
|
+
);
|
|
1191
|
+
}
|
|
1172
1192
|
async function main() {
|
|
1193
|
+
maybePrintTelemetryNotice();
|
|
1173
1194
|
await checkLicense();
|
|
1174
1195
|
switch (command) {
|
|
1175
1196
|
case "trial": {
|
|
@@ -1318,7 +1339,7 @@ async function main() {
|
|
|
1318
1339
|
process.exit(1);
|
|
1319
1340
|
}
|
|
1320
1341
|
if (alias !== void 0) {
|
|
1321
|
-
const { validateAlias } = await import("./src-
|
|
1342
|
+
const { validateAlias } = await import("./src-CNHVHDBT.js");
|
|
1322
1343
|
const v = validateAlias(alias);
|
|
1323
1344
|
if (!v.ok) {
|
|
1324
1345
|
console.error(`[ctxloom] Invalid alias: ${v.reason}`);
|
|
@@ -1562,7 +1583,7 @@ Suggested reviewers for ${files.length} file(s):`);
|
|
|
1562
1583
|
process.stderr.write("[ctxloom] --limit must be a non-negative integer (0 for unlimited)\n");
|
|
1563
1584
|
process.exit(2);
|
|
1564
1585
|
}
|
|
1565
|
-
const { loadRulesConfig, RulesChecker, formatText, formatJson, RulesConfigError } = await import("./src-
|
|
1586
|
+
const { loadRulesConfig, RulesChecker, formatText, formatJson, RulesConfigError } = await import("./src-CNHVHDBT.js");
|
|
1566
1587
|
let config;
|
|
1567
1588
|
try {
|
|
1568
1589
|
config = await loadRulesConfig(root);
|
|
@@ -1586,7 +1607,7 @@ Suggested reviewers for ${files.length} file(s):`);
|
|
|
1586
1607
|
}
|
|
1587
1608
|
let graph;
|
|
1588
1609
|
if (useSnapshot) {
|
|
1589
|
-
const { DependencyGraph: DG } = await import("./src-
|
|
1610
|
+
const { DependencyGraph: DG } = await import("./src-CNHVHDBT.js");
|
|
1590
1611
|
graph = new DG();
|
|
1591
1612
|
const loaded = await graph.loadSnapshotOnly(root);
|
|
1592
1613
|
if (!loaded) {
|
|
@@ -1595,7 +1616,7 @@ Suggested reviewers for ${files.length} file(s):`);
|
|
|
1595
1616
|
}
|
|
1596
1617
|
} else {
|
|
1597
1618
|
process.stderr.write("[ctxloom] Building dependency graph...\n");
|
|
1598
|
-
const { ASTParser: ASTParser2, DependencyGraph: DependencyGraph2 } = await import("./src-
|
|
1619
|
+
const { ASTParser: ASTParser2, DependencyGraph: DependencyGraph2 } = await import("./src-CNHVHDBT.js");
|
|
1599
1620
|
let parser;
|
|
1600
1621
|
try {
|
|
1601
1622
|
parser = new ASTParser2();
|
|
@@ -68,6 +68,7 @@ import {
|
|
|
68
68
|
getImpactRadius,
|
|
69
69
|
getLicenseInfo,
|
|
70
70
|
getOrCreateDistinctId,
|
|
71
|
+
getTelemetryLevel,
|
|
71
72
|
hashProjectRoot,
|
|
72
73
|
isActive,
|
|
73
74
|
isSiloed,
|
|
@@ -93,13 +94,14 @@ import {
|
|
|
93
94
|
scoreAll,
|
|
94
95
|
scoreFromBreakdown,
|
|
95
96
|
scoreReviewers,
|
|
97
|
+
shouldShowTelemetryNotice,
|
|
96
98
|
startTrial,
|
|
97
99
|
track,
|
|
98
100
|
validateAlias,
|
|
99
101
|
validateDefaultRoot,
|
|
100
102
|
wrapWithIndexingEnvelope,
|
|
101
103
|
writeCODEOWNERS
|
|
102
|
-
} from "./chunk-
|
|
104
|
+
} from "./chunk-4B2S3WJ5.js";
|
|
103
105
|
import {
|
|
104
106
|
VectorStore
|
|
105
107
|
} from "./chunk-NEHYSE2Y.js";
|
|
@@ -186,6 +188,7 @@ export {
|
|
|
186
188
|
getImpactRadius,
|
|
187
189
|
getLicenseInfo,
|
|
188
190
|
getOrCreateDistinctId,
|
|
191
|
+
getTelemetryLevel,
|
|
189
192
|
hashProjectRoot,
|
|
190
193
|
indexDirectory,
|
|
191
194
|
isActive,
|
|
@@ -213,6 +216,7 @@ export {
|
|
|
213
216
|
scoreAll,
|
|
214
217
|
scoreFromBreakdown,
|
|
215
218
|
scoreReviewers,
|
|
219
|
+
shouldShowTelemetryNotice,
|
|
216
220
|
startTrial,
|
|
217
221
|
track,
|
|
218
222
|
validateAlias,
|
|
@@ -220,4 +224,4 @@ export {
|
|
|
220
224
|
wrapWithIndexingEnvelope,
|
|
221
225
|
writeCODEOWNERS
|
|
222
226
|
};
|
|
223
|
-
//# sourceMappingURL=src-
|
|
227
|
+
//# sourceMappingURL=src-CNHVHDBT.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ctxloom-pro",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
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",
|