@context-engine-bridge/context-engine-mcp-bridge 0.0.79 → 0.0.82

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/package.json +1 -1
  2. package/src/uploader.js +136 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@context-engine-bridge/context-engine-mcp-bridge",
3
- "version": "0.0.79",
3
+ "version": "0.0.82",
4
4
  "description": "Context Engine MCP bridge (http/stdio proxy combining indexer + memory servers)",
5
5
  "bin": {
6
6
  "ctxce": "bin/ctxce.js",
package/src/uploader.js CHANGED
@@ -37,6 +37,7 @@ const DEFAULT_IGNORES = [
37
37
  ];
38
38
 
39
39
  const MAX_FILE_SIZE = 10 * 1024 * 1024;
40
+ const ADMIN_SESSION_COOKIE_NAME = "ctxce_session";
40
41
 
41
42
  function sanitizeRepoName(repoName) {
42
43
  return String(repoName || "")
@@ -61,6 +62,116 @@ function getCollectionName(repoName) {
61
62
  return `${sanitized}-${hash}`;
62
63
  }
63
64
 
65
+ function normalizeBackendUrl(raw) {
66
+ const value = String(raw || "").trim();
67
+ if (!value) return "";
68
+ try {
69
+ const parsed = new URL(value);
70
+ return `${parsed.protocol}//${parsed.host}`;
71
+ } catch {
72
+ return value.replace(/\/+$/, "");
73
+ }
74
+ }
75
+
76
+ function buildBridgeStateHeaders(sessionId) {
77
+ const headers = {
78
+ Accept: "application/json",
79
+ };
80
+ if (sessionId) {
81
+ headers.Authorization = `Bearer ${sessionId}`;
82
+ headers["X-Session-Id"] = sessionId;
83
+ headers.Cookie = `${ADMIN_SESSION_COOKIE_NAME}=${sessionId}`;
84
+ }
85
+ return headers;
86
+ }
87
+
88
+ async function fetchBridgeCollectionState({
89
+ workspacePath,
90
+ uploadEndpoint,
91
+ sessionId,
92
+ repoName,
93
+ logicalRepoId,
94
+ log = console.error,
95
+ }) {
96
+ const backendUrl = normalizeBackendUrl(uploadEndpoint);
97
+ if (!backendUrl) {
98
+ return { kind: "skipped" };
99
+ }
100
+
101
+ try {
102
+ const url = new URL("/bridge/state", backendUrl);
103
+ if (workspacePath) {
104
+ url.searchParams.set("workspace", workspacePath);
105
+ }
106
+ if (repoName) {
107
+ url.searchParams.set("repo_name", repoName);
108
+ }
109
+ if (logicalRepoId) {
110
+ url.searchParams.set("logical_repo_id", logicalRepoId);
111
+ }
112
+
113
+ const resp = await fetch(url, {
114
+ method: "GET",
115
+ headers: buildBridgeStateHeaders(sessionId),
116
+ });
117
+ if (resp.status === 404) {
118
+ return { kind: "missing" };
119
+ }
120
+ if (resp.status === 401 || resp.status === 403) {
121
+ return { kind: "unavailable" };
122
+ }
123
+ if (!resp.ok) {
124
+ log(`[uploader] bridge/state lookup failed: HTTP ${resp.status}`);
125
+ return { kind: "unavailable" };
126
+ }
127
+
128
+ const data = await resp.json();
129
+ return {
130
+ kind: "ok",
131
+ state: data && typeof data === "object" ? data : null,
132
+ };
133
+ } catch (err) {
134
+ const message = err instanceof Error ? err.message : String(err);
135
+ log(`[uploader] bridge/state lookup failed: ${message}`);
136
+ return { kind: "unavailable" };
137
+ }
138
+ }
139
+
140
+ async function resolveRepoScopedCollection(workspacePath, uploadEndpoint, sessionId, options = {}) {
141
+ const { log = console.error } = options;
142
+ const repoName = extractRepoNameFromPath(workspacePath);
143
+ const logicalRepoIdentity = computeLogicalRepoIdentity(workspacePath);
144
+
145
+ const stateResult = await fetchBridgeCollectionState({
146
+ workspacePath,
147
+ uploadEndpoint,
148
+ sessionId,
149
+ repoName,
150
+ logicalRepoId: logicalRepoIdentity.id,
151
+ log,
152
+ });
153
+
154
+ if (stateResult.kind !== "ok") {
155
+ return null;
156
+ }
157
+
158
+ const state = stateResult.state || {};
159
+ const servingCollection = typeof state.serving_collection === "string"
160
+ ? state.serving_collection.trim()
161
+ : "";
162
+ const activeCollection = typeof state.active_collection === "string"
163
+ ? state.active_collection.trim()
164
+ : "";
165
+ const resolvedCollection = servingCollection || activeCollection;
166
+
167
+ if (resolvedCollection) {
168
+ log(`[uploader] Resuming repo-scoped collection ${resolvedCollection} for ${logicalRepoIdentity.id}`);
169
+ return resolvedCollection;
170
+ }
171
+
172
+ return null;
173
+ }
174
+
64
175
  export function findGitRoot(startPath) {
65
176
  let current = path.resolve(startPath);
66
177
  while (current !== path.dirname(current)) {
@@ -451,7 +562,13 @@ function scanWorkspace(workspacePath, ig) {
451
562
  }
452
563
 
453
564
  export async function createBundle(workspacePath, options = {}) {
454
- const { log = console.error, gitHistory = null, gitState = null, allowEmpty = false } = options;
565
+ const {
566
+ log = console.error,
567
+ gitHistory = null,
568
+ gitState = null,
569
+ allowEmpty = false,
570
+ collectionName = null,
571
+ } = options;
455
572
 
456
573
  const ig = loadGitignore(workspacePath);
457
574
  const files = scanWorkspace(workspacePath, ig);
@@ -508,7 +625,7 @@ export async function createBundle(workspacePath, options = {}) {
508
625
  version: "1.0",
509
626
  bundle_id: bundleId,
510
627
  workspace_path: workspacePath,
511
- collection_name: getCollectionName(repoName),
628
+ collection_name: collectionName || getCollectionName(repoName),
512
629
  created_at: createdAt,
513
630
  sequence_number: null,
514
631
  parent_sequence: null,
@@ -583,6 +700,12 @@ export async function uploadGitHistoryOnly(workspacePath, uploadEndpoint, sessio
583
700
  const { log = console.error, orgId, orgSlug, quietNoop = false } = options;
584
701
 
585
702
  try {
703
+ const resolvedCollection = await resolveRepoScopedCollection(
704
+ workspacePath,
705
+ uploadEndpoint,
706
+ sessionId,
707
+ { log, orgId, orgSlug }
708
+ );
586
709
  const gitHistory = collectGitHistory(workspacePath);
587
710
  if (!gitHistory) {
588
711
  if (!quietNoop) {
@@ -597,6 +720,7 @@ export async function uploadGitHistoryOnly(workspacePath, uploadEndpoint, sessio
597
720
  gitHistory,
598
721
  gitState,
599
722
  allowEmpty: true,
723
+ collectionName: resolvedCollection,
600
724
  });
601
725
  if (!bundle) {
602
726
  return { success: false, error: "failed_to_create_history_bundle" };
@@ -740,7 +864,16 @@ async function _indexWorkspaceInner(workspacePath, uploadEndpoint, sessionId, op
740
864
  });
741
865
  }
742
866
 
743
- const bundle = await createBundle(workspacePath, { log });
867
+ const resolvedCollection = await resolveRepoScopedCollection(
868
+ workspacePath,
869
+ uploadEndpoint,
870
+ sessionId,
871
+ { log, orgId, orgSlug }
872
+ );
873
+ const bundle = await createBundle(workspacePath, {
874
+ log,
875
+ collectionName: resolvedCollection,
876
+ });
744
877
  if (!bundle) {
745
878
  return await uploadGitHistoryOnly(workspacePath, uploadEndpoint, sessionId, {
746
879
  log,