@rubytech/create-realagent 1.0.818 → 1.0.819
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/payload/platform/lib/graph-write/dist/__tests__/action-provenance-gate.test.d.ts +2 -0
- package/payload/platform/lib/graph-write/dist/__tests__/action-provenance-gate.test.d.ts.map +1 -0
- package/payload/platform/lib/graph-write/dist/__tests__/action-provenance-gate.test.js +168 -0
- package/payload/platform/lib/graph-write/dist/__tests__/action-provenance-gate.test.js.map +1 -0
- package/payload/platform/lib/graph-write/dist/index.d.ts +28 -5
- package/payload/platform/lib/graph-write/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/graph-write/dist/index.js +97 -3
- package/payload/platform/lib/graph-write/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-write/src/__tests__/action-provenance-gate.test.ts +191 -0
- package/payload/platform/lib/graph-write/src/index.ts +90 -9
- package/payload/platform/neo4j/schema.cypher +24 -0
- package/payload/platform/plugins/admin/skills/onboarding/SKILL.md +24 -6
- package/payload/platform/plugins/docs/references/internals.md +16 -0
- package/payload/platform/plugins/memory/PLUGIN.md +6 -0
- package/payload/platform/plugins/memory/mcp/dist/index.js +7 -2
- package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts +8 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js +26 -2
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js.map +1 -1
- package/payload/platform/plugins/memory/references/schema-base.md +8 -0
- package/payload/platform/plugins/tasks/PLUGIN.md +2 -2
- package/payload/platform/plugins/tasks/mcp/dist/index.js +10 -5
- package/payload/platform/plugins/tasks/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-create.d.ts +27 -1
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-create.d.ts.map +1 -1
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-create.js +45 -2
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-create.js.map +1 -1
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-update.d.ts +20 -1
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-update.d.ts.map +1 -1
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-update.js +46 -6
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-update.js.map +1 -1
- package/payload/server/server.js +523 -44
package/payload/server/server.js
CHANGED
|
@@ -121,8 +121,225 @@ import {
|
|
|
121
121
|
writeAdminUserAndPerson
|
|
122
122
|
} from "./chunk-PXQA2MA3.js";
|
|
123
123
|
|
|
124
|
-
// ../lib/graph-
|
|
124
|
+
// ../lib/graph-write/dist/audit.js
|
|
125
|
+
var require_audit = __commonJS({
|
|
126
|
+
"../lib/graph-write/dist/audit.js"(exports) {
|
|
127
|
+
"use strict";
|
|
128
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
129
|
+
exports.auditCypherWrite = auditCypherWrite;
|
|
130
|
+
exports.formatAuditLine = formatAuditLine;
|
|
131
|
+
var EDGE_PATTERN = /\[[^\]]*?:([A-Z_][A-Za-z0-9_]*(?:\|[A-Z_][A-Za-z0-9_]*)*)[^\]]*?\]/g;
|
|
132
|
+
var CREATE_OR_MERGE_NODE = /\b(?:CREATE|MERGE)\s*\(\s*[A-Za-z_][A-Za-z0-9_]*\s*:\s*[A-Z]/g;
|
|
133
|
+
var PROVENANCE_TOKEN = /\bcreatedBy(?:Agent|Tool|Session|Source)\b/g;
|
|
134
|
+
function stripStringLiterals(cypher) {
|
|
135
|
+
return cypher.replace(/'[^']*'|"[^"]*"/g, '""');
|
|
136
|
+
}
|
|
137
|
+
function extractEdgeTypes(cleaned) {
|
|
138
|
+
const out = /* @__PURE__ */ new Set();
|
|
139
|
+
for (const m of cleaned.matchAll(EDGE_PATTERN)) {
|
|
140
|
+
for (const t of m[1].split("|")) {
|
|
141
|
+
const clean = t.trim();
|
|
142
|
+
if (clean)
|
|
143
|
+
out.add(clean);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return out;
|
|
147
|
+
}
|
|
148
|
+
function countCreateOrMergeNodes(cleaned) {
|
|
149
|
+
const matches = cleaned.match(CREATE_OR_MERGE_NODE);
|
|
150
|
+
return matches ? matches.length : 0;
|
|
151
|
+
}
|
|
152
|
+
function countProvenanceStamps(cleaned) {
|
|
153
|
+
const matches = cleaned.match(PROVENANCE_TOKEN);
|
|
154
|
+
return matches ? matches.length : 0;
|
|
155
|
+
}
|
|
156
|
+
function auditCypherWrite(input) {
|
|
157
|
+
const warnings = [];
|
|
158
|
+
const cleaned = stripStringLiterals(input.cypher);
|
|
159
|
+
const referencedTypes = extractEdgeTypes(cleaned);
|
|
160
|
+
for (const t of referencedTypes) {
|
|
161
|
+
if (!input.schema.relationshipTypes.has(t)) {
|
|
162
|
+
warnings.push({ kind: "unknown-type-warning", type: t });
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (input.nodesCreated > 0) {
|
|
166
|
+
const createOrMergeNodes = countCreateOrMergeNodes(cleaned);
|
|
167
|
+
if (createOrMergeNodes > 0) {
|
|
168
|
+
const stamps = countProvenanceStamps(cleaned);
|
|
169
|
+
if (stamps < createOrMergeNodes) {
|
|
170
|
+
warnings.push({
|
|
171
|
+
kind: "missing-provenance-warning",
|
|
172
|
+
created: createOrMergeNodes,
|
|
173
|
+
stamped: stamps
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (input.orphanIds.length > 0) {
|
|
179
|
+
warnings.push({ kind: "orphan-warning", orphanIds: input.orphanIds });
|
|
180
|
+
}
|
|
181
|
+
return warnings;
|
|
182
|
+
}
|
|
183
|
+
function formatAuditLine(line) {
|
|
184
|
+
const prefixField = `query="${line.cypherPrefix.replace(/"/g, "'")}"`;
|
|
185
|
+
switch (line.kind) {
|
|
186
|
+
case "accepted":
|
|
187
|
+
return `[graph-cypher-write] accepted ${prefixField} nodesCreated=${line.nodesCreated} relsCreated=${line.relsCreated} agentName=${line.agentName} sessionId=${line.sessionId}`;
|
|
188
|
+
case "orphan-warning":
|
|
189
|
+
return `[graph-cypher-write] orphan-warning ${prefixField} orphanIds=${line.orphanIds.join(",")}`;
|
|
190
|
+
case "unknown-type-warning":
|
|
191
|
+
return `[graph-cypher-write] unknown-type-warning ${prefixField} type=${line.type}`;
|
|
192
|
+
case "missing-provenance-warning":
|
|
193
|
+
return `[graph-cypher-write] missing-provenance-warning ${prefixField} created=${line.created} stamped=${line.stamped}`;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
// ../lib/graph-write/dist/index.js
|
|
125
200
|
var require_dist = __commonJS({
|
|
201
|
+
"../lib/graph-write/dist/index.js"(exports) {
|
|
202
|
+
"use strict";
|
|
203
|
+
var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
204
|
+
if (k2 === void 0) k2 = k;
|
|
205
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
206
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
207
|
+
desc = { enumerable: true, get: function() {
|
|
208
|
+
return m[k];
|
|
209
|
+
} };
|
|
210
|
+
}
|
|
211
|
+
Object.defineProperty(o, k2, desc);
|
|
212
|
+
}) : (function(o, m, k, k2) {
|
|
213
|
+
if (k2 === void 0) k2 = k;
|
|
214
|
+
o[k2] = m[k];
|
|
215
|
+
}));
|
|
216
|
+
var __exportStar = exports && exports.__exportStar || function(m, exports2) {
|
|
217
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports2, p)) __createBinding(exports2, m, p);
|
|
218
|
+
};
|
|
219
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
220
|
+
exports.ACTION_PROVENANCE_LABELS = void 0;
|
|
221
|
+
exports.stampCreatedBy = stampCreatedBy;
|
|
222
|
+
exports.writeNodeWithEdges = writeNodeWithEdges2;
|
|
223
|
+
__exportStar(require_audit(), exports);
|
|
224
|
+
exports.ACTION_PROVENANCE_LABELS = /* @__PURE__ */ new Set([
|
|
225
|
+
"Person",
|
|
226
|
+
"UserProfile",
|
|
227
|
+
"AdminUser",
|
|
228
|
+
"Organization",
|
|
229
|
+
"LocalBusiness",
|
|
230
|
+
"CloudflareTunnel",
|
|
231
|
+
"CloudflareHostname"
|
|
232
|
+
]);
|
|
233
|
+
function requiresActionProvenance(labels) {
|
|
234
|
+
for (const label of labels) {
|
|
235
|
+
if (exports.ACTION_PROVENANCE_LABELS.has(label))
|
|
236
|
+
return true;
|
|
237
|
+
}
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
240
|
+
function findProducedFromTaskCandidates(relationships) {
|
|
241
|
+
return relationships.filter((r) => r.type === "PRODUCED" && r.direction === "incoming");
|
|
242
|
+
}
|
|
243
|
+
function stampCreatedBy(props, createdBy) {
|
|
244
|
+
return {
|
|
245
|
+
...props,
|
|
246
|
+
createdByAgent: createdBy.agent ?? "unknown",
|
|
247
|
+
createdBySession: createdBy.session ?? "unknown",
|
|
248
|
+
createdByTool: createdBy.tool ?? null,
|
|
249
|
+
createdBySource: createdBy.source ?? null
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
async function writeNodeWithEdges2(params) {
|
|
253
|
+
const { session, labels, props, relationships, createdBy } = params;
|
|
254
|
+
const agentLabel = createdBy.agent ?? createdBy.source ?? "unknown";
|
|
255
|
+
const labelCsv = labels.join(",");
|
|
256
|
+
const reviewDigestActionTool = typeof props.actionTool === "string" && props.actionTool === "review-digest-compose";
|
|
257
|
+
if (labels.includes("ReviewAlert") || reviewDigestActionTool) {
|
|
258
|
+
const actionToolField = reviewDigestActionTool ? "review-digest-compose" : "n/a";
|
|
259
|
+
process.stderr.write(`[graph-write] reject reason=removed-feature labels=${labelCsv} actionTool=${actionToolField} agent=${agentLabel}
|
|
260
|
+
`);
|
|
261
|
+
throw new Error("Write doctrine violated: review-detector feature removed (Task 884) \u2014 `:ReviewAlert` and `:Event {actionTool:'review-digest-compose'}` writes are not allowed.");
|
|
262
|
+
}
|
|
263
|
+
if (!relationships || relationships.length < 1) {
|
|
264
|
+
process.stderr.write(`[graph-write] reject reason=zero-relationships labels=${labelCsv} agent=${agentLabel}
|
|
265
|
+
`);
|
|
266
|
+
throw new Error("Write doctrine violated: a node must be created with at least one relationship. See .docs/neo4j.md (Write doctrine).");
|
|
267
|
+
}
|
|
268
|
+
const labelStr = labels.map((l) => `\`${l.replace(/`/g, "")}\``).join(":");
|
|
269
|
+
const nodeProps = stampCreatedBy(props, createdBy);
|
|
270
|
+
return await session.executeWrite(async (tx) => {
|
|
271
|
+
const targetIds = relationships.map((r) => r.targetNodeId);
|
|
272
|
+
const check = await tx.run(`UNWIND $ids AS id MATCH (t) WHERE elementId(t) = id RETURN elementId(t) AS id, labels(t) AS labels`, { ids: targetIds });
|
|
273
|
+
const labelsByTarget = /* @__PURE__ */ new Map();
|
|
274
|
+
for (const rec of check.records) {
|
|
275
|
+
labelsByTarget.set(rec.get("id"), rec.get("labels"));
|
|
276
|
+
}
|
|
277
|
+
const found = labelsByTarget.size;
|
|
278
|
+
const uniqueRequested = new Set(targetIds).size;
|
|
279
|
+
if (found !== uniqueRequested) {
|
|
280
|
+
process.stderr.write(`[graph-write] reject reason=unresolved-target labels=${labelCsv} agent=${agentLabel} requested=${uniqueRequested} found=${found}
|
|
281
|
+
`);
|
|
282
|
+
throw new Error(`Write doctrine violated: ${uniqueRequested - found} of ${uniqueRequested} relationship target(s) did not resolve (elementId mismatch). No node created.`);
|
|
283
|
+
}
|
|
284
|
+
let producedByTaskId = null;
|
|
285
|
+
if (requiresActionProvenance(labels) && (createdBy.agent ?? "") !== "system") {
|
|
286
|
+
const candidates = findProducedFromTaskCandidates(relationships);
|
|
287
|
+
const taskCandidates = candidates.filter((r) => {
|
|
288
|
+
const lbls = labelsByTarget.get(r.targetNodeId);
|
|
289
|
+
return Array.isArray(lbls) && lbls.includes("Task");
|
|
290
|
+
});
|
|
291
|
+
if (taskCandidates.length === 0) {
|
|
292
|
+
process.stderr.write(`[graph-write] reject reason=missing-action-provenance labels=${labelCsv} agent=${agentLabel}
|
|
293
|
+
`);
|
|
294
|
+
throw new Error(`Process provenance doctrine violated: write to ${labelCsv} requires an inbound :PRODUCED edge from a :Task (createdBy.agent='${agentLabel}'). See .docs/neo4j.md (Process provenance doctrine).`);
|
|
295
|
+
}
|
|
296
|
+
producedByTaskId = taskCandidates[0].targetNodeId;
|
|
297
|
+
}
|
|
298
|
+
let nodeRes;
|
|
299
|
+
try {
|
|
300
|
+
nodeRes = await tx.run(`CREATE (n:${labelStr} $props) RETURN elementId(n) AS nodeId, labels(n) AS nodeLabels`, { props: nodeProps });
|
|
301
|
+
} catch (err) {
|
|
302
|
+
const code = err?.code ?? "";
|
|
303
|
+
if (code === "Neo.ClientError.Schema.ConstraintValidationFailed" && labels.includes("UserProfile")) {
|
|
304
|
+
const accountIdProp = nodeProps.accountId;
|
|
305
|
+
const userIdProp = nodeProps.userId;
|
|
306
|
+
const acctSlice = typeof accountIdProp === "string" ? accountIdProp.slice(0, 8) : "unknown";
|
|
307
|
+
const userSlice = typeof userIdProp === "string" ? userIdProp.slice(0, 8) : "unknown";
|
|
308
|
+
process.stderr.write(`[graph-write] reject reason=user-profile-uniqueness-violation accountId=${acctSlice} userId=${userSlice} writer=${agentLabel}
|
|
309
|
+
`);
|
|
310
|
+
}
|
|
311
|
+
throw err;
|
|
312
|
+
}
|
|
313
|
+
const nodeId = nodeRes.records[0].get("nodeId");
|
|
314
|
+
const nodeLabels = nodeRes.records[0].get("nodeLabels");
|
|
315
|
+
let edgesCreated = 0;
|
|
316
|
+
for (const rel of relationships) {
|
|
317
|
+
const type = rel.type.replace(/`/g, "");
|
|
318
|
+
const q = rel.direction === "outgoing" ? `MATCH (a), (b) WHERE elementId(a) = $from AND elementId(b) = $to CREATE (a)-[:\`${type}\`]->(b)` : `MATCH (a), (b) WHERE elementId(a) = $from AND elementId(b) = $to CREATE (b)-[:\`${type}\`]->(a)`;
|
|
319
|
+
const r = await tx.run(q, { from: nodeId, to: rel.targetNodeId });
|
|
320
|
+
const created = r.summary.counters.updates().relationshipsCreated;
|
|
321
|
+
if (created === 0) {
|
|
322
|
+
process.stderr.write(`[graph-write] reject reason=unresolved-target-on-create labels=${labelCsv} agent=${agentLabel} relType=${rel.type} targetId=${rel.targetNodeId}
|
|
323
|
+
`);
|
|
324
|
+
throw new Error(`Write doctrine violated: relationship CREATE to target ${rel.targetNodeId} produced 0 edges (target likely deleted concurrently after pre-check). Transaction rolled back.`);
|
|
325
|
+
}
|
|
326
|
+
edgesCreated += created;
|
|
327
|
+
}
|
|
328
|
+
if (edgesCreated !== relationships.length) {
|
|
329
|
+
process.stderr.write(`[graph-write] reject reason=edge-count-mismatch labels=${labelCsv} agent=${agentLabel} requested=${relationships.length} created=${edgesCreated}
|
|
330
|
+
`);
|
|
331
|
+
throw new Error(`Write doctrine violated: expected ${relationships.length} edges, created ${edgesCreated}. Transaction rolled back.`);
|
|
332
|
+
}
|
|
333
|
+
process.stderr.write(`[graph-write] accepted labels=${labelCsv} edges=${edgesCreated} createdByAgent=${createdBy.agent ?? "unknown"} createdByTool=${createdBy.tool ?? createdBy.source ?? "unknown"} producedByTask=${producedByTaskId ?? "none"}
|
|
334
|
+
`);
|
|
335
|
+
return { nodeId, labels: nodeLabels, edgesCreated };
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
// ../lib/graph-trash/dist/index.js
|
|
342
|
+
var require_dist2 = __commonJS({
|
|
126
343
|
"../lib/graph-trash/dist/index.js"(exports) {
|
|
127
344
|
"use strict";
|
|
128
345
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -618,7 +835,7 @@ var serveStatic = (options = { root: "" }) => {
|
|
|
618
835
|
};
|
|
619
836
|
|
|
620
837
|
// server/index.ts
|
|
621
|
-
import { readFileSync as
|
|
838
|
+
import { readFileSync as readFileSync16, existsSync as existsSync22, watchFile } from "fs";
|
|
622
839
|
import { resolve as resolve21, join as join9, basename as basename5 } from "path";
|
|
623
840
|
import { homedir as homedir2 } from "os";
|
|
624
841
|
|
|
@@ -8580,7 +8797,7 @@ var events_default = app20;
|
|
|
8580
8797
|
// server/routes/admin/cloudflare.ts
|
|
8581
8798
|
import { homedir } from "os";
|
|
8582
8799
|
import { resolve as resolve14 } from "path";
|
|
8583
|
-
import { readFileSync as
|
|
8800
|
+
import { readFileSync as readFileSync14 } from "fs";
|
|
8584
8801
|
|
|
8585
8802
|
// app/lib/dns-label.ts
|
|
8586
8803
|
var VALID_LABEL = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
@@ -8618,6 +8835,214 @@ function addAliasDomain(hostname2) {
|
|
|
8618
8835
|
writeFileSync6(ALIAS_DOMAINS_PATH, JSON.stringify([...existing], null, 2) + "\n", "utf-8");
|
|
8619
8836
|
}
|
|
8620
8837
|
|
|
8838
|
+
// app/lib/cloudflare-task-tracker.ts
|
|
8839
|
+
import { readFileSync as readFileSync13, existsSync as existsSync17 } from "fs";
|
|
8840
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
8841
|
+
var import_dist = __toESM(require_dist(), 1);
|
|
8842
|
+
var CREATED_BY_AGENT = "cloudflare-setup-endpoint";
|
|
8843
|
+
var TASK_KIND = "cloudflare-tunnel-login";
|
|
8844
|
+
async function openCloudflareTask(params) {
|
|
8845
|
+
const { accountId, conversationKey, inputsProvided } = params;
|
|
8846
|
+
const taskId = randomUUID7();
|
|
8847
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
8848
|
+
const session = getSession();
|
|
8849
|
+
try {
|
|
8850
|
+
const conv = await session.run(
|
|
8851
|
+
`MATCH (c:Conversation {sessionKey: $conversationKey, accountId: $accountId}) RETURN elementId(c) AS id LIMIT 1`,
|
|
8852
|
+
{ conversationKey, accountId }
|
|
8853
|
+
);
|
|
8854
|
+
if (conv.records.length === 0) {
|
|
8855
|
+
throw new Error(
|
|
8856
|
+
`cloudflare-task-tracker: no Conversation with sessionKey=${conversationKey.slice(-8)} for accountId \u2014 refusing to create an orphan Task`
|
|
8857
|
+
);
|
|
8858
|
+
}
|
|
8859
|
+
const conversationElementId = conv.records[0].get("id");
|
|
8860
|
+
const props = {
|
|
8861
|
+
taskId,
|
|
8862
|
+
accountId,
|
|
8863
|
+
name: "Cloudflare tunnel login + setup",
|
|
8864
|
+
description: `Deterministic cloudflare setup invoked by /api/admin/cloudflare/setup. inputsProvided=[${inputsProvided.join(", ")}]`,
|
|
8865
|
+
status: "running",
|
|
8866
|
+
priority: "normal",
|
|
8867
|
+
kind: TASK_KIND,
|
|
8868
|
+
inputsProvided,
|
|
8869
|
+
startedAt: now,
|
|
8870
|
+
createdAt: now,
|
|
8871
|
+
updatedAt: now
|
|
8872
|
+
};
|
|
8873
|
+
const relationships = [
|
|
8874
|
+
{
|
|
8875
|
+
type: "RAISED_DURING",
|
|
8876
|
+
direction: "outgoing",
|
|
8877
|
+
targetNodeId: conversationElementId
|
|
8878
|
+
}
|
|
8879
|
+
];
|
|
8880
|
+
const result = await (0, import_dist.writeNodeWithEdges)({
|
|
8881
|
+
session,
|
|
8882
|
+
labels: ["Task"],
|
|
8883
|
+
props,
|
|
8884
|
+
relationships,
|
|
8885
|
+
createdBy: {
|
|
8886
|
+
agent: CREATED_BY_AGENT,
|
|
8887
|
+
session: conversationKey,
|
|
8888
|
+
tool: "cloudflare-setup-endpoint"
|
|
8889
|
+
}
|
|
8890
|
+
});
|
|
8891
|
+
process.stderr.write(
|
|
8892
|
+
`[task] action-start kind=${TASK_KIND} taskId=${taskId} raisedDuring=${conversationKey.slice(-8)}
|
|
8893
|
+
`
|
|
8894
|
+
);
|
|
8895
|
+
return { taskId, taskElementId: result.nodeId };
|
|
8896
|
+
} finally {
|
|
8897
|
+
await session.close();
|
|
8898
|
+
}
|
|
8899
|
+
}
|
|
8900
|
+
async function appendCloudflareSteps(taskId, accountId, streamLogPath) {
|
|
8901
|
+
if (!existsSync17(streamLogPath)) return [];
|
|
8902
|
+
let content;
|
|
8903
|
+
try {
|
|
8904
|
+
content = readFileSync13(streamLogPath, "utf-8");
|
|
8905
|
+
} catch {
|
|
8906
|
+
return [];
|
|
8907
|
+
}
|
|
8908
|
+
const steps = [];
|
|
8909
|
+
for (const line of content.split(/\r?\n/)) {
|
|
8910
|
+
const m = line.match(/\bphase_line\s+setup-tunnel\s+step=(\S+)/);
|
|
8911
|
+
if (m) steps.push(m[1]);
|
|
8912
|
+
}
|
|
8913
|
+
if (steps.length === 0) return [];
|
|
8914
|
+
const session = getSession();
|
|
8915
|
+
try {
|
|
8916
|
+
await session.run(
|
|
8917
|
+
`MATCH (t:Task {taskId: $taskId, accountId: $accountId})
|
|
8918
|
+
SET t.steps = coalesce(t.steps, []) + $steps,
|
|
8919
|
+
t.updatedAt = $updatedAt`,
|
|
8920
|
+
{ taskId, accountId, steps, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }
|
|
8921
|
+
);
|
|
8922
|
+
for (const step of steps) {
|
|
8923
|
+
process.stderr.write(
|
|
8924
|
+
`[task] action-step kind=${TASK_KIND} taskId=${taskId} step=${step}
|
|
8925
|
+
`
|
|
8926
|
+
);
|
|
8927
|
+
}
|
|
8928
|
+
return steps;
|
|
8929
|
+
} finally {
|
|
8930
|
+
await session.close();
|
|
8931
|
+
}
|
|
8932
|
+
}
|
|
8933
|
+
async function completeCloudflareTask(params) {
|
|
8934
|
+
const { taskId, taskElementId, accountId, conversationKey, tunnelId, tunnelName, hostnames, status, errorMessage } = params;
|
|
8935
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
8936
|
+
if (status === "failed" && (!errorMessage || errorMessage.trim().length === 0)) {
|
|
8937
|
+
throw new Error(
|
|
8938
|
+
"cloudflare-task-tracker: errorMessage is required when status='failed' (Task 885 process-provenance contract)."
|
|
8939
|
+
);
|
|
8940
|
+
}
|
|
8941
|
+
const session = getSession();
|
|
8942
|
+
try {
|
|
8943
|
+
if (status === "completed" && tunnelId && tunnelName) {
|
|
8944
|
+
const conv = await session.run(
|
|
8945
|
+
`MATCH (c:Conversation {sessionKey: $conversationKey, accountId: $accountId}) RETURN elementId(c) AS id LIMIT 1`,
|
|
8946
|
+
{ conversationKey, accountId }
|
|
8947
|
+
);
|
|
8948
|
+
const conversationElementId = conv.records.length > 0 ? conv.records[0].get("id") : null;
|
|
8949
|
+
const tunnelProps = {
|
|
8950
|
+
accountId,
|
|
8951
|
+
tunnelId,
|
|
8952
|
+
tunnelName,
|
|
8953
|
+
createdAt: now,
|
|
8954
|
+
updatedAt: now
|
|
8955
|
+
};
|
|
8956
|
+
const tunnelRels = [
|
|
8957
|
+
{ type: "PRODUCED", direction: "incoming", targetNodeId: taskElementId }
|
|
8958
|
+
];
|
|
8959
|
+
if (conversationElementId) {
|
|
8960
|
+
tunnelRels.push({
|
|
8961
|
+
type: "RAISED_DURING",
|
|
8962
|
+
direction: "outgoing",
|
|
8963
|
+
targetNodeId: conversationElementId
|
|
8964
|
+
});
|
|
8965
|
+
}
|
|
8966
|
+
const tunnelWrite = await (0, import_dist.writeNodeWithEdges)({
|
|
8967
|
+
session,
|
|
8968
|
+
labels: ["CloudflareTunnel"],
|
|
8969
|
+
props: tunnelProps,
|
|
8970
|
+
relationships: tunnelRels,
|
|
8971
|
+
createdBy: {
|
|
8972
|
+
agent: CREATED_BY_AGENT,
|
|
8973
|
+
session: conversationKey,
|
|
8974
|
+
tool: "cloudflare-setup-endpoint"
|
|
8975
|
+
}
|
|
8976
|
+
});
|
|
8977
|
+
for (const h of hostnames ?? []) {
|
|
8978
|
+
const hostRels = [
|
|
8979
|
+
{ type: "PRODUCED", direction: "incoming", targetNodeId: taskElementId },
|
|
8980
|
+
{
|
|
8981
|
+
type: "ROUTES_TO",
|
|
8982
|
+
direction: "outgoing",
|
|
8983
|
+
targetNodeId: tunnelWrite.nodeId
|
|
8984
|
+
}
|
|
8985
|
+
];
|
|
8986
|
+
await (0, import_dist.writeNodeWithEdges)({
|
|
8987
|
+
session,
|
|
8988
|
+
labels: ["CloudflareHostname"],
|
|
8989
|
+
props: {
|
|
8990
|
+
accountId,
|
|
8991
|
+
hostnameValue: h.hostnameValue,
|
|
8992
|
+
tunnelId,
|
|
8993
|
+
isApex: h.isApex,
|
|
8994
|
+
createdAt: now,
|
|
8995
|
+
updatedAt: now
|
|
8996
|
+
},
|
|
8997
|
+
relationships: hostRels,
|
|
8998
|
+
createdBy: {
|
|
8999
|
+
agent: CREATED_BY_AGENT,
|
|
9000
|
+
session: conversationKey,
|
|
9001
|
+
tool: "cloudflare-setup-endpoint"
|
|
9002
|
+
}
|
|
9003
|
+
});
|
|
9004
|
+
}
|
|
9005
|
+
}
|
|
9006
|
+
const setClauses = [
|
|
9007
|
+
"t.status = $status",
|
|
9008
|
+
"t.completedAt = $now",
|
|
9009
|
+
"t.updatedAt = $now"
|
|
9010
|
+
];
|
|
9011
|
+
const queryParams = { taskId, accountId, status, now };
|
|
9012
|
+
if (status === "failed" && errorMessage) {
|
|
9013
|
+
setClauses.push("t.errorMessage = $errorMessage");
|
|
9014
|
+
queryParams.errorMessage = errorMessage;
|
|
9015
|
+
}
|
|
9016
|
+
const updateRes = await session.run(
|
|
9017
|
+
`MATCH (t:Task {taskId: $taskId, accountId: $accountId})
|
|
9018
|
+
SET ${setClauses.join(", ")}
|
|
9019
|
+
RETURN size(coalesce(t.steps, [])) AS stepsCount`,
|
|
9020
|
+
queryParams
|
|
9021
|
+
);
|
|
9022
|
+
const stepsCount = updateRes.records[0]?.get("stepsCount")?.toNumber?.() ?? 0;
|
|
9023
|
+
process.stderr.write(
|
|
9024
|
+
`[task] action-done kind=${TASK_KIND} taskId=${taskId} status=${status} stepsCount=${stepsCount}
|
|
9025
|
+
`
|
|
9026
|
+
);
|
|
9027
|
+
} finally {
|
|
9028
|
+
await session.close();
|
|
9029
|
+
}
|
|
9030
|
+
}
|
|
9031
|
+
function readTunnelState(brandConfigDir) {
|
|
9032
|
+
const statePath = `${process.env.HOME ?? ""}/${brandConfigDir}/cloudflared/tunnel.state`;
|
|
9033
|
+
if (!existsSync17(statePath)) return null;
|
|
9034
|
+
try {
|
|
9035
|
+
const parsed = JSON.parse(readFileSync13(statePath, "utf-8"));
|
|
9036
|
+
const tunnelId = typeof parsed.tunnelId === "string" ? parsed.tunnelId : null;
|
|
9037
|
+
const tunnelName = typeof parsed.tunnelName === "string" ? parsed.tunnelName : null;
|
|
9038
|
+
const domain = typeof parsed.domain === "string" ? parsed.domain : null;
|
|
9039
|
+
if (!tunnelId || !tunnelName || !domain) return null;
|
|
9040
|
+
return { tunnelId, tunnelName, domain };
|
|
9041
|
+
} catch {
|
|
9042
|
+
return null;
|
|
9043
|
+
}
|
|
9044
|
+
}
|
|
9045
|
+
|
|
8621
9046
|
// server/routes/admin/cloudflare.ts
|
|
8622
9047
|
var SETUP_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
8623
9048
|
var DOMAINS_TIMEOUT_MS = 40 * 1e3;
|
|
@@ -8625,7 +9050,7 @@ function loadBrandInfo() {
|
|
|
8625
9050
|
const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ?? resolve14(process.cwd(), "..");
|
|
8626
9051
|
const brandPath = resolve14(platformRoot2, "config", "brand.json");
|
|
8627
9052
|
try {
|
|
8628
|
-
const parsed = JSON.parse(
|
|
9053
|
+
const parsed = JSON.parse(readFileSync14(brandPath, "utf-8"));
|
|
8629
9054
|
const hostname2 = typeof parsed.hostname === "string" && parsed.hostname ? parsed.hostname : "maxy";
|
|
8630
9055
|
const configDir2 = typeof parsed.configDir === "string" && parsed.configDir ? parsed.configDir : ".maxy";
|
|
8631
9056
|
return { hostname: hostname2, configDir: configDir2 };
|
|
@@ -8867,6 +9292,17 @@ app21.post("/setup", requireAdminSession, async (c) => {
|
|
|
8867
9292
|
if (await isActionActive("cloudflare-setup")) {
|
|
8868
9293
|
return err("request", "Another Cloudflare setup is already running. Wait for it to finish before starting a new one.");
|
|
8869
9294
|
}
|
|
9295
|
+
let cloudflareTask;
|
|
9296
|
+
try {
|
|
9297
|
+
cloudflareTask = await openCloudflareTask({
|
|
9298
|
+
accountId,
|
|
9299
|
+
conversationKey: sessionKey,
|
|
9300
|
+
inputsProvided: ["adminLabel", "adminDomain", publicFqdn ? "publicLabel" : null, apex ? "apex" : null, "password"].filter((s) => typeof s === "string")
|
|
9301
|
+
});
|
|
9302
|
+
log(`phase=task-opened taskId=${cloudflareTask.taskId}`);
|
|
9303
|
+
} catch (e) {
|
|
9304
|
+
return err("script", `Failed to open process-provenance Task record: ${e instanceof Error ? e.message : String(e)}`);
|
|
9305
|
+
}
|
|
8870
9306
|
let actionId;
|
|
8871
9307
|
let unit;
|
|
8872
9308
|
try {
|
|
@@ -8879,8 +9315,27 @@ app21.post("/setup", requireAdminSession, async (c) => {
|
|
|
8879
9315
|
log(`phase=action-launched id=${actionId} unit=${unit}`);
|
|
8880
9316
|
void (async () => {
|
|
8881
9317
|
const status = await waitForExit(unit, SETUP_TIMEOUT_MS);
|
|
9318
|
+
try {
|
|
9319
|
+
const appendedSteps = await appendCloudflareSteps(cloudflareTask.taskId, accountId, streamLogPath);
|
|
9320
|
+
log(`phase=task-steps-appended count=${appendedSteps.length}`);
|
|
9321
|
+
} catch (e) {
|
|
9322
|
+
logErr(`phase=task-steps-append-failed reason="${e instanceof Error ? e.message : String(e)}"`);
|
|
9323
|
+
}
|
|
8882
9324
|
if (!status || status.execMainStatus !== 0) {
|
|
8883
9325
|
logErr(`phase=post-exit-skipped reason=${status ? `exit=${status.execMainStatus}` : "unit-gone"} id=${actionId}`);
|
|
9326
|
+
try {
|
|
9327
|
+
const exitReason = status ? `script exited with status ${status.execMainStatus}` : "systemd unit vanished before exit recorded";
|
|
9328
|
+
await completeCloudflareTask({
|
|
9329
|
+
taskId: cloudflareTask.taskId,
|
|
9330
|
+
taskElementId: cloudflareTask.taskElementId,
|
|
9331
|
+
accountId,
|
|
9332
|
+
conversationKey: sessionKey,
|
|
9333
|
+
status: "failed",
|
|
9334
|
+
errorMessage: exitReason
|
|
9335
|
+
});
|
|
9336
|
+
} catch (e) {
|
|
9337
|
+
logErr(`phase=task-close-failed status=failed reason="${e instanceof Error ? e.message : String(e)}"`);
|
|
9338
|
+
}
|
|
8884
9339
|
return;
|
|
8885
9340
|
}
|
|
8886
9341
|
const candidates = [publicFqdn, apex].filter((h) => typeof h === "string" && h.length > 0);
|
|
@@ -8900,6 +9355,30 @@ app21.post("/setup", requireAdminSession, async (c) => {
|
|
|
8900
9355
|
logErr(`phase=alias-domain-write host=${host} result=error reason="${e instanceof Error ? e.message : String(e)}"`);
|
|
8901
9356
|
}
|
|
8902
9357
|
}
|
|
9358
|
+
try {
|
|
9359
|
+
const tunnelState = readTunnelState(brand.configDir);
|
|
9360
|
+
const allHostnames = [adminFqdn, publicFqdn, apex].filter(
|
|
9361
|
+
(h) => typeof h === "string" && h.length > 0
|
|
9362
|
+
);
|
|
9363
|
+
const hostnameRecords = allHostnames.map((value) => ({
|
|
9364
|
+
hostnameValue: value,
|
|
9365
|
+
// Apex heuristic: exactly one dot in the FQDN. Mirrors setup-tunnel.sh:332.
|
|
9366
|
+
isApex: (value.match(/\./g)?.length ?? 0) === 1
|
|
9367
|
+
}));
|
|
9368
|
+
await completeCloudflareTask({
|
|
9369
|
+
taskId: cloudflareTask.taskId,
|
|
9370
|
+
taskElementId: cloudflareTask.taskElementId,
|
|
9371
|
+
accountId,
|
|
9372
|
+
conversationKey: sessionKey,
|
|
9373
|
+
tunnelId: tunnelState?.tunnelId,
|
|
9374
|
+
tunnelName: tunnelState?.tunnelName,
|
|
9375
|
+
hostnames: tunnelState ? hostnameRecords : void 0,
|
|
9376
|
+
status: "completed"
|
|
9377
|
+
});
|
|
9378
|
+
log(`phase=task-closed status=completed tunnelId=${tunnelState?.tunnelId ?? "absent"} hostnames=${allHostnames.length}`);
|
|
9379
|
+
} catch (e) {
|
|
9380
|
+
logErr(`phase=task-close-failed status=completed reason="${e instanceof Error ? e.message : String(e)}"`);
|
|
9381
|
+
}
|
|
8903
9382
|
log(`phase=done action=${actionId}`);
|
|
8904
9383
|
})().catch((e) => logErr(`post-exit handler threw: ${e}`));
|
|
8905
9384
|
const total = Date.now() - started;
|
|
@@ -9592,7 +10071,7 @@ app22.delete("/", requireAdminSession, async (c) => {
|
|
|
9592
10071
|
var files_default = app22;
|
|
9593
10072
|
|
|
9594
10073
|
// ../lib/graph-search/src/index.ts
|
|
9595
|
-
var
|
|
10074
|
+
var import_dist2 = __toESM(require_dist2());
|
|
9596
10075
|
import { int } from "neo4j-driver";
|
|
9597
10076
|
var VECTOR_WEIGHT = 0.7;
|
|
9598
10077
|
var BM25_WEIGHT = 0.3;
|
|
@@ -9647,7 +10126,7 @@ async function bm25Only(session, params) {
|
|
|
9647
10126
|
${scopeClause}
|
|
9648
10127
|
${agentClause}
|
|
9649
10128
|
${labelClause}
|
|
9650
|
-
AND ${(0,
|
|
10129
|
+
AND ${(0, import_dist2.notTrashed)("node")}
|
|
9651
10130
|
${kwClause}
|
|
9652
10131
|
RETURN node, score, labels(node) AS nodeLabels, elementId(node) AS nodeId
|
|
9653
10132
|
ORDER BY score DESC
|
|
@@ -9737,7 +10216,7 @@ async function hybrid(session, embed2, params) {
|
|
|
9737
10216
|
WHERE node.accountId = $accountId
|
|
9738
10217
|
${scopeClause}
|
|
9739
10218
|
${agentClause}
|
|
9740
|
-
AND ${(0,
|
|
10219
|
+
AND ${(0, import_dist2.notTrashed)("node")}
|
|
9741
10220
|
${keywordClause}
|
|
9742
10221
|
RETURN node, score, labels(node) AS nodeLabels, elementId(node) AS nodeId
|
|
9743
10222
|
ORDER BY score DESC
|
|
@@ -9798,7 +10277,7 @@ async function hybrid(session, embed2, params) {
|
|
|
9798
10277
|
const propResult = await session.run(
|
|
9799
10278
|
`MATCH (node)
|
|
9800
10279
|
WHERE node.accountId = $accountId
|
|
9801
|
-
AND ${(0,
|
|
10280
|
+
AND ${(0, import_dist2.notTrashed)("node")}
|
|
9802
10281
|
AND node.keywords IS NOT NULL
|
|
9803
10282
|
AND ANY(kw IN $kwSubs WHERE ANY(nk IN node.keywords WHERE toLower(nk) = kw))
|
|
9804
10283
|
${propScopeClause}
|
|
@@ -9855,7 +10334,7 @@ async function hybrid(session, embed2, params) {
|
|
|
9855
10334
|
`UNWIND $nodeIds AS nid
|
|
9856
10335
|
MATCH (n)-[r]-(related)
|
|
9857
10336
|
WHERE elementId(n) = nid
|
|
9858
|
-
AND ${(0,
|
|
10337
|
+
AND ${(0, import_dist2.notTrashed)("related")}
|
|
9859
10338
|
${expandScopeClause}
|
|
9860
10339
|
${expandAgentClause}
|
|
9861
10340
|
WITH nid, n, r, related
|
|
@@ -11031,7 +11510,7 @@ var adherence_default = app30;
|
|
|
11031
11510
|
import neo4j3 from "neo4j-driver";
|
|
11032
11511
|
import { readFile as readFile5, readdir as readdir3, stat as stat5 } from "fs/promises";
|
|
11033
11512
|
import { resolve as resolve17, relative as relative2, isAbsolute } from "path";
|
|
11034
|
-
import { existsSync as
|
|
11513
|
+
import { existsSync as existsSync18 } from "fs";
|
|
11035
11514
|
var LIMIT = 50;
|
|
11036
11515
|
var TEXT_MIME_PREFIXES = ["text/", "application/json", "application/markdown"];
|
|
11037
11516
|
var ADMIN_AGENT_FILES = ["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"];
|
|
@@ -11179,7 +11658,7 @@ async function fetchAgentTemplateRows(accountDir) {
|
|
|
11179
11658
|
async function unionSpecialistFilenames(overrideDir, bundledDir) {
|
|
11180
11659
|
const names = /* @__PURE__ */ new Set();
|
|
11181
11660
|
for (const dir of [overrideDir, bundledDir]) {
|
|
11182
|
-
if (!
|
|
11661
|
+
if (!existsSync18(dir)) continue;
|
|
11183
11662
|
try {
|
|
11184
11663
|
const entries = await readdir3(dir);
|
|
11185
11664
|
for (const entry of entries) {
|
|
@@ -11194,7 +11673,7 @@ async function unionSpecialistFilenames(overrideDir, bundledDir) {
|
|
|
11194
11673
|
}
|
|
11195
11674
|
async function readAgentTemplateRow(inp) {
|
|
11196
11675
|
let chosenPath = null;
|
|
11197
|
-
if (
|
|
11676
|
+
if (existsSync18(inp.overridePath)) {
|
|
11198
11677
|
try {
|
|
11199
11678
|
validateFilePathInAccount(inp.overridePath, inp.overrideRoot);
|
|
11200
11679
|
chosenPath = inp.overridePath;
|
|
@@ -11205,7 +11684,7 @@ async function readAgentTemplateRow(inp) {
|
|
|
11205
11684
|
);
|
|
11206
11685
|
return null;
|
|
11207
11686
|
}
|
|
11208
|
-
} else if (
|
|
11687
|
+
} else if (existsSync18(inp.bundledPath)) {
|
|
11209
11688
|
if (!isWithin(inp.bundledPath, inp.bundledRoot)) {
|
|
11210
11689
|
console.error(
|
|
11211
11690
|
`[admin/sidebar-artefacts] agent-template-read-failed agent=${inp.displayName} kind=${inp.logName} error="bundled path outside PLATFORM_ROOT"`
|
|
@@ -11247,7 +11726,7 @@ var sidebar_artefacts_default = app31;
|
|
|
11247
11726
|
// server/routes/admin/sidebar-artefact-save.ts
|
|
11248
11727
|
import { mkdir as mkdir4, readdir as readdir4, stat as stat6, writeFile as writeFile5 } from "fs/promises";
|
|
11249
11728
|
import { resolve as resolve18 } from "path";
|
|
11250
|
-
import { existsSync as
|
|
11729
|
+
import { existsSync as existsSync19 } from "fs";
|
|
11251
11730
|
var ADMIN_AGENT_FILES2 = /* @__PURE__ */ new Set(["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"]);
|
|
11252
11731
|
var UUID_RE5 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
11253
11732
|
var app32 = new Hono();
|
|
@@ -11311,7 +11790,7 @@ async function resolveSavePath(id, accountId, accountDir) {
|
|
|
11311
11790
|
}
|
|
11312
11791
|
if (UUID_RE5.test(id)) {
|
|
11313
11792
|
const dir = resolve18(ATTACHMENTS_ROOT, accountId, id);
|
|
11314
|
-
if (!
|
|
11793
|
+
if (!existsSync19(dir)) {
|
|
11315
11794
|
return { kind: "reject", status: 400, reason: "not-found" };
|
|
11316
11795
|
}
|
|
11317
11796
|
try {
|
|
@@ -11335,7 +11814,7 @@ var sidebar_artefact_save_default = app32;
|
|
|
11335
11814
|
|
|
11336
11815
|
// server/routes/admin/sidebar-artefact-content.ts
|
|
11337
11816
|
import { readFile as readFile6, readdir as readdir5 } from "fs/promises";
|
|
11338
|
-
import { existsSync as
|
|
11817
|
+
import { existsSync as existsSync20 } from "fs";
|
|
11339
11818
|
import { resolve as resolve19 } from "path";
|
|
11340
11819
|
var UUID_RE6 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
11341
11820
|
var app33 = new Hono();
|
|
@@ -11349,7 +11828,7 @@ app33.get("/", requireAdminSession, async (c) => {
|
|
|
11349
11828
|
return new Response("Not found", { status: 404 });
|
|
11350
11829
|
}
|
|
11351
11830
|
const dir = resolve19(ATTACHMENTS_ROOT, accountId, id);
|
|
11352
|
-
if (!
|
|
11831
|
+
if (!existsSync20(dir)) {
|
|
11353
11832
|
console.error(`[admin/sidebar-artefact-content] not-found id=${id.slice(0, 8)}`);
|
|
11354
11833
|
return new Response("Not found", { status: 404 });
|
|
11355
11834
|
}
|
|
@@ -11411,7 +11890,7 @@ app34.route("/sidebar-artefact-content", sidebar_artefact_content_default);
|
|
|
11411
11890
|
var admin_default = app34;
|
|
11412
11891
|
|
|
11413
11892
|
// server/routes/sites.ts
|
|
11414
|
-
import { existsSync as
|
|
11893
|
+
import { existsSync as existsSync21, readFileSync as readFileSync15, realpathSync as realpathSync5, statSync as statSync5 } from "fs";
|
|
11415
11894
|
import { resolve as resolve20 } from "path";
|
|
11416
11895
|
var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
|
|
11417
11896
|
var MIME = {
|
|
@@ -11478,7 +11957,7 @@ app35.get("/:rel{.*}", (c) => {
|
|
|
11478
11957
|
}
|
|
11479
11958
|
let stat7;
|
|
11480
11959
|
try {
|
|
11481
|
-
stat7 =
|
|
11960
|
+
stat7 = existsSync21(filePath) ? statSync5(filePath) : null;
|
|
11482
11961
|
} catch {
|
|
11483
11962
|
stat7 = null;
|
|
11484
11963
|
}
|
|
@@ -11491,7 +11970,7 @@ app35.get("/:rel{.*}", (c) => {
|
|
|
11491
11970
|
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
|
|
11492
11971
|
return c.text("Forbidden", 403);
|
|
11493
11972
|
}
|
|
11494
|
-
if (!
|
|
11973
|
+
if (!existsSync21(filePath)) {
|
|
11495
11974
|
console.error(`[sites] not-found path=${reqPath} status=404`);
|
|
11496
11975
|
return c.text("Not found", 404);
|
|
11497
11976
|
}
|
|
@@ -11510,7 +11989,7 @@ app35.get("/:rel{.*}", (c) => {
|
|
|
11510
11989
|
}
|
|
11511
11990
|
let body;
|
|
11512
11991
|
try {
|
|
11513
|
-
body =
|
|
11992
|
+
body = readFileSync15(realPath);
|
|
11514
11993
|
} catch (err) {
|
|
11515
11994
|
const code = err?.code;
|
|
11516
11995
|
if (code === "EISDIR") {
|
|
@@ -11644,12 +12123,12 @@ function clientFrom(c) {
|
|
|
11644
12123
|
var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
11645
12124
|
var BRAND_JSON_PATH = PLATFORM_ROOT7 ? join9(PLATFORM_ROOT7, "config", "brand.json") : "";
|
|
11646
12125
|
var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", domain: "getmaxy.com" };
|
|
11647
|
-
if (BRAND_JSON_PATH && !
|
|
12126
|
+
if (BRAND_JSON_PATH && !existsSync22(BRAND_JSON_PATH)) {
|
|
11648
12127
|
console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
|
|
11649
12128
|
}
|
|
11650
|
-
if (BRAND_JSON_PATH &&
|
|
12129
|
+
if (BRAND_JSON_PATH && existsSync22(BRAND_JSON_PATH)) {
|
|
11651
12130
|
try {
|
|
11652
|
-
const parsed = JSON.parse(
|
|
12131
|
+
const parsed = JSON.parse(readFileSync16(BRAND_JSON_PATH, "utf-8"));
|
|
11653
12132
|
BRAND = { ...BRAND, ...parsed };
|
|
11654
12133
|
} catch (err) {
|
|
11655
12134
|
console.error(`[brand] Failed to parse brand.json: ${err.message}`);
|
|
@@ -11671,8 +12150,8 @@ var brandLoginOpts = {
|
|
|
11671
12150
|
var ALIAS_DOMAINS_PATH2 = join9(homedir2(), BRAND.configDir, "alias-domains.json");
|
|
11672
12151
|
function loadAliasDomains() {
|
|
11673
12152
|
try {
|
|
11674
|
-
if (!
|
|
11675
|
-
const parsed = JSON.parse(
|
|
12153
|
+
if (!existsSync22(ALIAS_DOMAINS_PATH2)) return null;
|
|
12154
|
+
const parsed = JSON.parse(readFileSync16(ALIAS_DOMAINS_PATH2, "utf-8"));
|
|
11676
12155
|
if (!Array.isArray(parsed)) {
|
|
11677
12156
|
console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
|
|
11678
12157
|
return null;
|
|
@@ -12018,14 +12497,14 @@ app36.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
12018
12497
|
console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
|
|
12019
12498
|
return c.text("Forbidden", 403);
|
|
12020
12499
|
}
|
|
12021
|
-
if (!
|
|
12500
|
+
if (!existsSync22(filePath)) {
|
|
12022
12501
|
console.error(`[agent-assets] serve slug=${slug} file=${filename} status=404`);
|
|
12023
12502
|
return c.text("Not found", 404);
|
|
12024
12503
|
}
|
|
12025
12504
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
12026
12505
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
12027
12506
|
console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
|
|
12028
|
-
const body =
|
|
12507
|
+
const body = readFileSync16(filePath);
|
|
12029
12508
|
return c.body(body, 200, {
|
|
12030
12509
|
"Content-Type": contentType,
|
|
12031
12510
|
"Cache-Control": "public, max-age=3600"
|
|
@@ -12048,14 +12527,14 @@ app36.get("/generated/:filename", (c) => {
|
|
|
12048
12527
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
12049
12528
|
return c.text("Forbidden", 403);
|
|
12050
12529
|
}
|
|
12051
|
-
if (!
|
|
12530
|
+
if (!existsSync22(filePath)) {
|
|
12052
12531
|
console.error(`[generated] serve file=${filename} status=404`);
|
|
12053
12532
|
return c.text("Not found", 404);
|
|
12054
12533
|
}
|
|
12055
12534
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
12056
12535
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
12057
12536
|
console.log(`[generated] serve file=${filename} status=200`);
|
|
12058
|
-
const body =
|
|
12537
|
+
const body = readFileSync16(filePath);
|
|
12059
12538
|
return c.body(body, 200, {
|
|
12060
12539
|
"Content-Type": contentType,
|
|
12061
12540
|
"Cache-Control": "public, max-age=86400"
|
|
@@ -12065,9 +12544,9 @@ app36.route("/sites", sites_default);
|
|
|
12065
12544
|
var htmlCache = /* @__PURE__ */ new Map();
|
|
12066
12545
|
var brandLogoPath = "/brand/maxy-monochrome.png";
|
|
12067
12546
|
var brandIconPath = "/brand/maxy-monochrome.png";
|
|
12068
|
-
if (BRAND_JSON_PATH &&
|
|
12547
|
+
if (BRAND_JSON_PATH && existsSync22(BRAND_JSON_PATH)) {
|
|
12069
12548
|
try {
|
|
12070
|
-
const fullBrand = JSON.parse(
|
|
12549
|
+
const fullBrand = JSON.parse(readFileSync16(BRAND_JSON_PATH, "utf-8"));
|
|
12071
12550
|
if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
|
|
12072
12551
|
brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
|
|
12073
12552
|
} catch {
|
|
@@ -12085,8 +12564,8 @@ function readInstalledVersion() {
|
|
|
12085
12564
|
try {
|
|
12086
12565
|
if (!PLATFORM_ROOT7) return "unknown";
|
|
12087
12566
|
const versionFile = join9(PLATFORM_ROOT7, "config", `.${BRAND.hostname}-version`);
|
|
12088
|
-
if (!
|
|
12089
|
-
const content =
|
|
12567
|
+
if (!existsSync22(versionFile)) return "unknown";
|
|
12568
|
+
const content = readFileSync16(versionFile, "utf-8").trim();
|
|
12090
12569
|
return content || "unknown";
|
|
12091
12570
|
} catch {
|
|
12092
12571
|
return "unknown";
|
|
@@ -12127,7 +12606,7 @@ var clientErrorReporterScript = `<script>
|
|
|
12127
12606
|
function cachedHtml(file) {
|
|
12128
12607
|
let html = htmlCache.get(file);
|
|
12129
12608
|
if (!html) {
|
|
12130
|
-
html =
|
|
12609
|
+
html = readFileSync16(resolve21(process.cwd(), "public", file), "utf-8");
|
|
12131
12610
|
const productNameEsc = escapeHtml(BRAND.productName);
|
|
12132
12611
|
html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
|
|
12133
12612
|
html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
|
|
@@ -12146,13 +12625,13 @@ function loadBrandingCache(agentSlug) {
|
|
|
12146
12625
|
const configDir2 = join9(homedir2(), BRAND.configDir);
|
|
12147
12626
|
try {
|
|
12148
12627
|
const accountJsonPath = join9(configDir2, "account.json");
|
|
12149
|
-
if (!
|
|
12150
|
-
const account = JSON.parse(
|
|
12628
|
+
if (!existsSync22(accountJsonPath)) return null;
|
|
12629
|
+
const account = JSON.parse(readFileSync16(accountJsonPath, "utf-8"));
|
|
12151
12630
|
const accountId = account.accountId;
|
|
12152
12631
|
if (!accountId) return null;
|
|
12153
12632
|
const cachePath = join9(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
|
|
12154
|
-
if (!
|
|
12155
|
-
return JSON.parse(
|
|
12633
|
+
if (!existsSync22(cachePath)) return null;
|
|
12634
|
+
return JSON.parse(readFileSync16(cachePath, "utf-8"));
|
|
12156
12635
|
} catch {
|
|
12157
12636
|
return null;
|
|
12158
12637
|
}
|
|
@@ -12161,8 +12640,8 @@ function resolveDefaultSlug() {
|
|
|
12161
12640
|
try {
|
|
12162
12641
|
const configDir2 = join9(homedir2(), BRAND.configDir);
|
|
12163
12642
|
const accountJsonPath = join9(configDir2, "account.json");
|
|
12164
|
-
if (!
|
|
12165
|
-
const account = JSON.parse(
|
|
12643
|
+
if (!existsSync22(accountJsonPath)) return null;
|
|
12644
|
+
const account = JSON.parse(readFileSync16(accountJsonPath, "utf-8"));
|
|
12166
12645
|
return account.defaultAgent || null;
|
|
12167
12646
|
} catch {
|
|
12168
12647
|
return null;
|
|
@@ -12235,7 +12714,7 @@ app36.use("/vnc-popout.html", logViewerFetch);
|
|
|
12235
12714
|
app36.get("/vnc-popout.html", (c) => {
|
|
12236
12715
|
let html = htmlCache.get("vnc-popout.html");
|
|
12237
12716
|
if (!html) {
|
|
12238
|
-
html =
|
|
12717
|
+
html = readFileSync16(resolve21(process.cwd(), "public", "vnc-popout.html"), "utf-8");
|
|
12239
12718
|
const name = escapeHtml(BRAND.productName);
|
|
12240
12719
|
html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
|
|
12241
12720
|
html = html.replace("</head>", ` ${brandScript}
|
|
@@ -12325,8 +12804,8 @@ try {
|
|
|
12325
12804
|
(async () => {
|
|
12326
12805
|
try {
|
|
12327
12806
|
let userId = "";
|
|
12328
|
-
if (
|
|
12329
|
-
const users = JSON.parse(
|
|
12807
|
+
if (existsSync22(USERS_FILE)) {
|
|
12808
|
+
const users = JSON.parse(readFileSync16(USERS_FILE, "utf-8").trim() || "[]");
|
|
12330
12809
|
userId = users[0]?.userId ?? "";
|
|
12331
12810
|
}
|
|
12332
12811
|
await backfillNullUserIdConversations(userId);
|