@context-engine-bridge/context-engine-mcp-bridge 0.0.79 → 0.0.83
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/package.json +1 -1
- package/src/uploader.js +136 -3
package/package.json
CHANGED
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 {
|
|
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
|
|
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,
|