brainjar 0.3.747 → 0.3.976
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/brainjar.js +1762 -376
- package/package.json +1 -1
package/brainjar.js
CHANGED
|
@@ -107,7 +107,7 @@ var CLI_VERSION, CACHE, DAY;
|
|
|
107
107
|
var init_version_check = __esm({
|
|
108
108
|
"packages/cli/src/version-check.ts"() {
|
|
109
109
|
"use strict";
|
|
110
|
-
CLI_VERSION = "0.3.
|
|
110
|
+
CLI_VERSION = "0.3.976";
|
|
111
111
|
CACHE = path.join(os.homedir(), ".brainjar", "version-check.json");
|
|
112
112
|
DAY = 864e5;
|
|
113
113
|
}
|
|
@@ -1344,6 +1344,8 @@ var require_defaults = __commonJS({
|
|
|
1344
1344
|
idleTimeoutMillis: 3e4,
|
|
1345
1345
|
client_encoding: "",
|
|
1346
1346
|
ssl: false,
|
|
1347
|
+
// SSL negotiation style: 'postgres' (traditional SSLRequest) or 'direct'
|
|
1348
|
+
sslnegotiation: void 0,
|
|
1347
1349
|
application_name: void 0,
|
|
1348
1350
|
fallback_application_name: void 0,
|
|
1349
1351
|
options: void 0,
|
|
@@ -1481,20 +1483,20 @@ var require_utils = __commonJS({
|
|
|
1481
1483
|
}
|
|
1482
1484
|
return config;
|
|
1483
1485
|
}
|
|
1484
|
-
var escapeIdentifier2 = function(
|
|
1485
|
-
return '"' +
|
|
1486
|
+
var escapeIdentifier2 = function(str2) {
|
|
1487
|
+
return '"' + str2.replace(/"/g, '""') + '"';
|
|
1486
1488
|
};
|
|
1487
|
-
var escapeLiteral2 = function(
|
|
1489
|
+
var escapeLiteral2 = function(str2) {
|
|
1488
1490
|
let hasBackslash = false;
|
|
1489
1491
|
let escaped = "'";
|
|
1490
|
-
if (
|
|
1492
|
+
if (str2 == null) {
|
|
1491
1493
|
return "''";
|
|
1492
1494
|
}
|
|
1493
|
-
if (typeof
|
|
1495
|
+
if (typeof str2 !== "string") {
|
|
1494
1496
|
return "''";
|
|
1495
1497
|
}
|
|
1496
|
-
for (let i = 0; i <
|
|
1497
|
-
const c =
|
|
1498
|
+
for (let i = 0; i < str2.length; i++) {
|
|
1499
|
+
const c = str2[i];
|
|
1498
1500
|
if (c === "'") {
|
|
1499
1501
|
escaped += c + c;
|
|
1500
1502
|
} else if (c === "\\") {
|
|
@@ -1909,22 +1911,22 @@ var require_type_overrides = __commonJS({
|
|
|
1909
1911
|
var require_pg_connection_string = __commonJS({
|
|
1910
1912
|
"node_modules/pg-connection-string/index.js"(exports, module) {
|
|
1911
1913
|
"use strict";
|
|
1912
|
-
function parse(
|
|
1913
|
-
if (
|
|
1914
|
-
const config2 =
|
|
1914
|
+
function parse(str2, options = {}) {
|
|
1915
|
+
if (str2.charAt(0) === "/") {
|
|
1916
|
+
const config2 = str2.split(" ");
|
|
1915
1917
|
return { host: config2[0], database: config2[1] };
|
|
1916
1918
|
}
|
|
1917
1919
|
const config = /* @__PURE__ */ Object.create(null);
|
|
1918
1920
|
let result;
|
|
1919
1921
|
let dummyHost = false;
|
|
1920
|
-
if (/ |%[^a-f0-9]|%[a-f0-9][^a-f0-9]/i.test(
|
|
1921
|
-
|
|
1922
|
+
if (/ |%[^a-f0-9]|%[a-f0-9][^a-f0-9]/i.test(str2)) {
|
|
1923
|
+
str2 = encodeURI(str2).replace(/%25(\d\d)/g, "%$1");
|
|
1922
1924
|
}
|
|
1923
1925
|
try {
|
|
1924
1926
|
try {
|
|
1925
|
-
result = new URL(
|
|
1927
|
+
result = new URL(str2, "postgres://base");
|
|
1926
1928
|
} catch (e) {
|
|
1927
|
-
result = new URL(
|
|
1929
|
+
result = new URL(str2.replace("@/", "@___DUMMY___/"), "postgres://base");
|
|
1928
1930
|
dummyHost = true;
|
|
1929
1931
|
}
|
|
1930
1932
|
} catch (err) {
|
|
@@ -1942,11 +1944,11 @@ var require_pg_connection_string = __commonJS({
|
|
|
1942
1944
|
config.client_encoding = result.searchParams.get("encoding");
|
|
1943
1945
|
return config;
|
|
1944
1946
|
}
|
|
1945
|
-
const
|
|
1947
|
+
const hostname2 = dummyHost ? "" : result.hostname;
|
|
1946
1948
|
if (!config.host) {
|
|
1947
|
-
config.host = decodeURIComponent(
|
|
1948
|
-
} else if (
|
|
1949
|
-
result.pathname =
|
|
1949
|
+
config.host = decodeURIComponent(hostname2);
|
|
1950
|
+
} else if (hostname2 && /^%2f/i.test(hostname2)) {
|
|
1951
|
+
result.pathname = hostname2 + result.pathname;
|
|
1950
1952
|
}
|
|
1951
1953
|
if (!config.port) {
|
|
1952
1954
|
config.port = result.port;
|
|
@@ -1962,6 +1964,9 @@ var require_pg_connection_string = __commonJS({
|
|
|
1962
1964
|
if (config.sslcert || config.sslkey || config.sslrootcert || config.sslmode) {
|
|
1963
1965
|
config.ssl = {};
|
|
1964
1966
|
}
|
|
1967
|
+
if (config.sslnegotiation === "direct" && config.ssl === void 0) {
|
|
1968
|
+
config.ssl = true;
|
|
1969
|
+
}
|
|
1965
1970
|
const fs = config.sslcert || config.sslkey || config.sslrootcert ? __require("fs") : null;
|
|
1966
1971
|
if (config.sslcert) {
|
|
1967
1972
|
config.ssl.cert = fs.readFileSync(config.sslcert).toString();
|
|
@@ -2067,8 +2072,8 @@ var require_pg_connection_string = __commonJS({
|
|
|
2067
2072
|
}, /* @__PURE__ */ Object.create(null));
|
|
2068
2073
|
return poolConfig;
|
|
2069
2074
|
}
|
|
2070
|
-
function parseIntoClientConfig(
|
|
2071
|
-
return toClientConfig(parse(
|
|
2075
|
+
function parseIntoClientConfig(str2) {
|
|
2076
|
+
return toClientConfig(parse(str2));
|
|
2072
2077
|
}
|
|
2073
2078
|
function deprecatedSslModeWarning(sslmode) {
|
|
2074
2079
|
if (!deprecatedSslModeWarning.warned && typeof process !== "undefined" && process.emitWarning) {
|
|
@@ -2167,6 +2172,15 @@ var require_connection_parameters = __commonJS({
|
|
|
2167
2172
|
enumerable: false
|
|
2168
2173
|
});
|
|
2169
2174
|
}
|
|
2175
|
+
this.sslnegotiation = val("sslnegotiation", config, "PGSSLNEGOTIATION");
|
|
2176
|
+
if (this.sslnegotiation !== void 0 && this.sslnegotiation !== "postgres" && this.sslnegotiation !== "direct") {
|
|
2177
|
+
throw new Error(
|
|
2178
|
+
`Invalid sslnegotiation value: "${this.sslnegotiation}". Valid values are "postgres" and "direct".`
|
|
2179
|
+
);
|
|
2180
|
+
}
|
|
2181
|
+
if (this.sslnegotiation === "direct" && !this.ssl) {
|
|
2182
|
+
throw new Error("sslnegotiation=direct requires SSL to be enabled");
|
|
2183
|
+
}
|
|
2170
2184
|
this.client_encoding = val("client_encoding", config);
|
|
2171
2185
|
this.replication = val("replication", config);
|
|
2172
2186
|
this.isDomainSocket = !(this.host || "").indexOf("/");
|
|
@@ -2205,6 +2219,7 @@ var require_connection_parameters = __commonJS({
|
|
|
2205
2219
|
add(params, ssl, "sslkey");
|
|
2206
2220
|
add(params, ssl, "sslcert");
|
|
2207
2221
|
add(params, ssl, "sslrootcert");
|
|
2222
|
+
add(params, this, "sslnegotiation");
|
|
2208
2223
|
if (this.database) {
|
|
2209
2224
|
params.push("dbname=" + quoteParamValue(this.database));
|
|
2210
2225
|
}
|
|
@@ -2446,7 +2461,7 @@ var require_query = __commonJS({
|
|
|
2446
2461
|
if (typeof this.text !== "string" && typeof this.name !== "string") {
|
|
2447
2462
|
return new Error("A query must have either text or a name. Supplying neither is unsupported.");
|
|
2448
2463
|
}
|
|
2449
|
-
const previous = connection.parsedStatements[this.name];
|
|
2464
|
+
const previous = connection.parsedStatements[this.name] || connection.submittedNamedStatements[this.name];
|
|
2450
2465
|
if (this.text && previous && this.text !== previous) {
|
|
2451
2466
|
return new Error(`Prepared statements must be unique - '${this.name}' was used for a different statement`);
|
|
2452
2467
|
}
|
|
@@ -2466,7 +2481,7 @@ var require_query = __commonJS({
|
|
|
2466
2481
|
return null;
|
|
2467
2482
|
}
|
|
2468
2483
|
hasBeenParsed(connection) {
|
|
2469
|
-
return this.name && connection.parsedStatements[this.name];
|
|
2484
|
+
return this.name && (connection.parsedStatements[this.name] || connection.submittedNamedStatements[this.name]);
|
|
2470
2485
|
}
|
|
2471
2486
|
handlePortalSuspended(connection) {
|
|
2472
2487
|
this._getRows(connection, this.rows);
|
|
@@ -2490,6 +2505,9 @@ var require_query = __commonJS({
|
|
|
2490
2505
|
name: this.name,
|
|
2491
2506
|
types: this.types
|
|
2492
2507
|
});
|
|
2508
|
+
if (this.name) {
|
|
2509
|
+
connection.submittedNamedStatements[this.name] = this.text;
|
|
2510
|
+
}
|
|
2493
2511
|
}
|
|
2494
2512
|
try {
|
|
2495
2513
|
connection.bind({
|
|
@@ -2500,6 +2518,8 @@ var require_query = __commonJS({
|
|
|
2500
2518
|
valueMapper: utils.prepareValue
|
|
2501
2519
|
});
|
|
2502
2520
|
} catch (err) {
|
|
2521
|
+
connection.close({ type: "S", name: this.name });
|
|
2522
|
+
connection.sync();
|
|
2503
2523
|
this.handleError(err, connection);
|
|
2504
2524
|
return;
|
|
2505
2525
|
}
|
|
@@ -2739,6 +2759,25 @@ var require_buffer_writer = __commonJS({
|
|
|
2739
2759
|
this.offset += len;
|
|
2740
2760
|
return this;
|
|
2741
2761
|
}
|
|
2762
|
+
// Write an Int32 byte-length prefix immediately followed by the string's UTF-8
|
|
2763
|
+
// bytes. Postgres' Bind wire format prefixes every parameter with its length,
|
|
2764
|
+
// and doing it in one method computes Buffer.byteLength ONCE — the previous
|
|
2765
|
+
// `addInt32(Buffer.byteLength(s)).addString(s)` pairing scanned the string
|
|
2766
|
+
// three times (byteLength for the prefix, byteLength again inside addString,
|
|
2767
|
+
// then the encode), which is costly for large text parameters.
|
|
2768
|
+
addInt32PrefixedString(string) {
|
|
2769
|
+
const len = Buffer.byteLength(string);
|
|
2770
|
+
this.ensure(4 + len);
|
|
2771
|
+
const buffer = this.buffer;
|
|
2772
|
+
let offset = this.offset;
|
|
2773
|
+
buffer[offset++] = len >>> 24 & 255;
|
|
2774
|
+
buffer[offset++] = len >>> 16 & 255;
|
|
2775
|
+
buffer[offset++] = len >>> 8 & 255;
|
|
2776
|
+
buffer[offset++] = len >>> 0 & 255;
|
|
2777
|
+
buffer.write(string, offset, "utf-8");
|
|
2778
|
+
this.offset = offset + len;
|
|
2779
|
+
return this;
|
|
2780
|
+
}
|
|
2742
2781
|
add(otherBuffer) {
|
|
2743
2782
|
this.ensure(otherBuffer.length);
|
|
2744
2783
|
otherBuffer.copy(this.buffer, this.offset);
|
|
@@ -2760,6 +2799,10 @@ var require_buffer_writer = __commonJS({
|
|
|
2760
2799
|
this.buffer = Buffer.allocUnsafe(this.size);
|
|
2761
2800
|
return result;
|
|
2762
2801
|
}
|
|
2802
|
+
clear() {
|
|
2803
|
+
this.offset = 5;
|
|
2804
|
+
this.headerPosition = 0;
|
|
2805
|
+
}
|
|
2763
2806
|
};
|
|
2764
2807
|
exports.Writer = Writer;
|
|
2765
2808
|
}
|
|
@@ -2796,7 +2839,7 @@ var require_serializer = __commonJS({
|
|
|
2796
2839
|
);
|
|
2797
2840
|
};
|
|
2798
2841
|
var sendSASLInitialResponseMessage = function(mechanism, initialResponse) {
|
|
2799
|
-
writer.addCString(mechanism).
|
|
2842
|
+
writer.addCString(mechanism).addInt32PrefixedString(initialResponse);
|
|
2800
2843
|
return writer.flush(
|
|
2801
2844
|
112
|
|
2802
2845
|
/* code.startup */
|
|
@@ -2855,8 +2898,7 @@ var require_serializer = __commonJS({
|
|
|
2855
2898
|
0
|
|
2856
2899
|
/* ParamType.STRING */
|
|
2857
2900
|
);
|
|
2858
|
-
paramWriter.
|
|
2859
|
-
paramWriter.addString(mappedVal);
|
|
2901
|
+
paramWriter.addInt32PrefixedString(mappedVal);
|
|
2860
2902
|
}
|
|
2861
2903
|
}
|
|
2862
2904
|
};
|
|
@@ -2868,7 +2910,13 @@ var require_serializer = __commonJS({
|
|
|
2868
2910
|
const len = values.length;
|
|
2869
2911
|
writer.addCString(portal).addCString(statement);
|
|
2870
2912
|
writer.addInt16(len);
|
|
2871
|
-
|
|
2913
|
+
try {
|
|
2914
|
+
writeValues(values, config.valueMapper);
|
|
2915
|
+
} catch (err) {
|
|
2916
|
+
writer.clear();
|
|
2917
|
+
paramWriter.clear();
|
|
2918
|
+
throw err;
|
|
2919
|
+
}
|
|
2872
2920
|
writer.addInt16(len);
|
|
2873
2921
|
writer.add(paramWriter.flush());
|
|
2874
2922
|
writer.addInt16(1);
|
|
@@ -3026,7 +3074,7 @@ var require_buffer_reader = __commonJS({
|
|
|
3026
3074
|
cstring() {
|
|
3027
3075
|
const start = this.offset;
|
|
3028
3076
|
let end = start;
|
|
3029
|
-
while (this.buffer[end++]
|
|
3077
|
+
while (this.buffer[end++]) {
|
|
3030
3078
|
}
|
|
3031
3079
|
this.offset = end;
|
|
3032
3080
|
return this.buffer.toString(this.encoding, start, end - 1);
|
|
@@ -3248,7 +3296,7 @@ var require_parser = __commonJS({
|
|
|
3248
3296
|
const parameterCount = reader.int16();
|
|
3249
3297
|
const message = new messages_1.ParameterDescriptionMessage(LATEINIT_LENGTH, parameterCount);
|
|
3250
3298
|
for (let i = 0; i < parameterCount; i++) {
|
|
3251
|
-
message.dataTypeIDs[i] = reader.
|
|
3299
|
+
message.dataTypeIDs[i] = reader.uint32();
|
|
3252
3300
|
}
|
|
3253
3301
|
return message;
|
|
3254
3302
|
};
|
|
@@ -3353,7 +3401,8 @@ var require_dist = __commonJS({
|
|
|
3353
3401
|
"node_modules/pg-protocol/dist/index.js"(exports) {
|
|
3354
3402
|
"use strict";
|
|
3355
3403
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3356
|
-
exports.DatabaseError = exports.serialize =
|
|
3404
|
+
exports.DatabaseError = exports.serialize = void 0;
|
|
3405
|
+
exports.parse = parse;
|
|
3357
3406
|
var messages_1 = require_messages();
|
|
3358
3407
|
Object.defineProperty(exports, "DatabaseError", { enumerable: true, get: function() {
|
|
3359
3408
|
return messages_1.DatabaseError;
|
|
@@ -3368,7 +3417,6 @@ var require_dist = __commonJS({
|
|
|
3368
3417
|
stream.on("data", (buffer) => parser.parse(buffer, callback));
|
|
3369
3418
|
return new Promise((resolve) => stream.on("end", () => resolve()));
|
|
3370
3419
|
}
|
|
3371
|
-
exports.parse = parse;
|
|
3372
3420
|
}
|
|
3373
3421
|
});
|
|
3374
3422
|
|
|
@@ -3453,7 +3501,8 @@ var require_connection = __commonJS({
|
|
|
3453
3501
|
"use strict";
|
|
3454
3502
|
var EventEmitter = __require("events").EventEmitter;
|
|
3455
3503
|
var { parse, serialize } = require_dist();
|
|
3456
|
-
var
|
|
3504
|
+
var stream = require_stream();
|
|
3505
|
+
var { getStream } = stream;
|
|
3457
3506
|
var flushBuffer = serialize.flush();
|
|
3458
3507
|
var syncBuffer = serialize.sync();
|
|
3459
3508
|
var endBuffer = serialize.end();
|
|
@@ -3468,7 +3517,9 @@ var require_connection = __commonJS({
|
|
|
3468
3517
|
this._keepAlive = config.keepAlive;
|
|
3469
3518
|
this._keepAliveInitialDelayMillis = config.keepAliveInitialDelayMillis;
|
|
3470
3519
|
this.parsedStatements = {};
|
|
3520
|
+
this.submittedNamedStatements = {};
|
|
3471
3521
|
this.ssl = config.ssl || false;
|
|
3522
|
+
this.sslNegotiation = config.sslNegotiation || "postgres";
|
|
3472
3523
|
this._ending = false;
|
|
3473
3524
|
this._emitMessage = false;
|
|
3474
3525
|
const self = this;
|
|
@@ -3502,6 +3553,11 @@ var require_connection = __commonJS({
|
|
|
3502
3553
|
if (!this.ssl) {
|
|
3503
3554
|
return this.attachListeners(this.stream);
|
|
3504
3555
|
}
|
|
3556
|
+
if (this.sslNegotiation === "direct") {
|
|
3557
|
+
return this.stream.once("connect", function() {
|
|
3558
|
+
self.upgradeToSSL(host, reportStreamError);
|
|
3559
|
+
});
|
|
3560
|
+
}
|
|
3505
3561
|
this.stream.once("data", function(buffer) {
|
|
3506
3562
|
const responseCode = buffer.toString("utf8");
|
|
3507
3563
|
switch (responseCode) {
|
|
@@ -3514,31 +3570,38 @@ var require_connection = __commonJS({
|
|
|
3514
3570
|
self.stream.end();
|
|
3515
3571
|
return self.emit("error", new Error("There was an error establishing an SSL connection"));
|
|
3516
3572
|
}
|
|
3517
|
-
|
|
3518
|
-
socket: self.stream
|
|
3519
|
-
};
|
|
3520
|
-
if (self.ssl !== true) {
|
|
3521
|
-
Object.assign(options, self.ssl);
|
|
3522
|
-
if ("key" in self.ssl) {
|
|
3523
|
-
options.key = self.ssl.key;
|
|
3524
|
-
}
|
|
3525
|
-
}
|
|
3526
|
-
const net = __require("net");
|
|
3527
|
-
if (net.isIP && net.isIP(host) === 0) {
|
|
3528
|
-
options.servername = host;
|
|
3529
|
-
}
|
|
3530
|
-
try {
|
|
3531
|
-
self.stream = getSecureStream(options);
|
|
3532
|
-
} catch (err) {
|
|
3533
|
-
return self.emit("error", err);
|
|
3534
|
-
}
|
|
3535
|
-
self.attachListeners(self.stream);
|
|
3536
|
-
self.stream.on("error", reportStreamError);
|
|
3537
|
-
self.emit("sslconnect");
|
|
3573
|
+
self.upgradeToSSL(host, reportStreamError);
|
|
3538
3574
|
});
|
|
3539
3575
|
}
|
|
3540
|
-
|
|
3541
|
-
|
|
3576
|
+
upgradeToSSL(host, reportStreamError) {
|
|
3577
|
+
const self = this;
|
|
3578
|
+
const options = {
|
|
3579
|
+
socket: self.stream
|
|
3580
|
+
};
|
|
3581
|
+
if (self.ssl !== true) {
|
|
3582
|
+
Object.assign(options, self.ssl);
|
|
3583
|
+
if ("key" in self.ssl) {
|
|
3584
|
+
options.key = self.ssl.key;
|
|
3585
|
+
}
|
|
3586
|
+
}
|
|
3587
|
+
if (self.sslNegotiation === "direct") {
|
|
3588
|
+
options.ALPNProtocols = ["postgresql"];
|
|
3589
|
+
}
|
|
3590
|
+
const net = __require("net");
|
|
3591
|
+
if (net.isIP && net.isIP(host) === 0) {
|
|
3592
|
+
options.servername = host;
|
|
3593
|
+
}
|
|
3594
|
+
try {
|
|
3595
|
+
self.stream = stream.getSecureStream(options);
|
|
3596
|
+
} catch (err) {
|
|
3597
|
+
return self.emit("error", err);
|
|
3598
|
+
}
|
|
3599
|
+
self.attachListeners(self.stream);
|
|
3600
|
+
self.stream.on("error", reportStreamError);
|
|
3601
|
+
self.emit("sslconnect");
|
|
3602
|
+
}
|
|
3603
|
+
attachListeners(stream2) {
|
|
3604
|
+
parse(stream2, (msg) => {
|
|
3542
3605
|
const eventName = msg.name === "error" ? "errorMessage" : msg.name;
|
|
3543
3606
|
if (this._emitMessage) {
|
|
3544
3607
|
this.emit("message", msg);
|
|
@@ -3966,8 +4029,8 @@ var require_client = __commonJS({
|
|
|
3966
4029
|
return Number.isFinite(value) ? value : defaultValue;
|
|
3967
4030
|
}
|
|
3968
4031
|
if (typeof value === "string" && value.trim() !== "") {
|
|
3969
|
-
const
|
|
3970
|
-
return Number.isFinite(
|
|
4032
|
+
const n2 = Number(value);
|
|
4033
|
+
return Number.isFinite(n2) ? n2 : defaultValue;
|
|
3971
4034
|
}
|
|
3972
4035
|
return defaultValue;
|
|
3973
4036
|
}
|
|
@@ -4005,15 +4068,19 @@ var require_client = __commonJS({
|
|
|
4005
4068
|
this.connection = c.connection || new Connection2({
|
|
4006
4069
|
stream: c.stream,
|
|
4007
4070
|
ssl: this.connectionParameters.ssl,
|
|
4071
|
+
sslNegotiation: this.connectionParameters.sslnegotiation,
|
|
4008
4072
|
keepAlive: c.keepAlive || false,
|
|
4009
4073
|
keepAliveInitialDelayMillis: c.keepAliveInitialDelayMillis || 0,
|
|
4010
4074
|
encoding: this.connectionParameters.client_encoding || "utf8"
|
|
4011
4075
|
});
|
|
4012
4076
|
this._queryQueue = [];
|
|
4077
|
+
this._sentQueryQueue = [];
|
|
4078
|
+
this.pipeline = Boolean(c.pipeline);
|
|
4013
4079
|
this.binary = c.binary || defaults2.binary;
|
|
4014
4080
|
this.processID = null;
|
|
4015
4081
|
this.secretKey = null;
|
|
4016
4082
|
this.ssl = this.connectionParameters.ssl || false;
|
|
4083
|
+
this.sslNegotiation = this.connectionParameters.sslnegotiation || "postgres";
|
|
4017
4084
|
if (this.ssl && this.ssl.key) {
|
|
4018
4085
|
Object.defineProperty(this.ssl, "key", {
|
|
4019
4086
|
enumerable: false
|
|
@@ -4043,6 +4110,8 @@ var require_client = __commonJS({
|
|
|
4043
4110
|
enqueueError(activeQuery);
|
|
4044
4111
|
this._activeQuery = null;
|
|
4045
4112
|
}
|
|
4113
|
+
this._sentQueryQueue.forEach(enqueueError);
|
|
4114
|
+
this._sentQueryQueue.length = 0;
|
|
4046
4115
|
this._queryQueue.forEach(enqueueError);
|
|
4047
4116
|
this._queryQueue.length = 0;
|
|
4048
4117
|
}
|
|
@@ -4074,7 +4143,9 @@ var require_client = __commonJS({
|
|
|
4074
4143
|
}
|
|
4075
4144
|
con.on("connect", function() {
|
|
4076
4145
|
if (self.ssl) {
|
|
4077
|
-
|
|
4146
|
+
if (self.sslNegotiation !== "direct") {
|
|
4147
|
+
con.requestSsl();
|
|
4148
|
+
}
|
|
4078
4149
|
} else {
|
|
4079
4150
|
con.startup(self.getStartupConf());
|
|
4080
4151
|
}
|
|
@@ -4283,6 +4354,9 @@ var require_client = __commonJS({
|
|
|
4283
4354
|
return;
|
|
4284
4355
|
}
|
|
4285
4356
|
this._activeQuery = null;
|
|
4357
|
+
if (activeQuery.name) {
|
|
4358
|
+
delete this.connection.submittedNamedStatements[activeQuery.name];
|
|
4359
|
+
}
|
|
4286
4360
|
activeQuery.handleError(msg, this.connection);
|
|
4287
4361
|
}
|
|
4288
4362
|
_handleRowDescription(msg) {
|
|
@@ -4339,6 +4413,7 @@ var require_client = __commonJS({
|
|
|
4339
4413
|
}
|
|
4340
4414
|
if (activeQuery.name) {
|
|
4341
4415
|
this.connection.parsedStatements[activeQuery.name] = activeQuery.text;
|
|
4416
|
+
delete this.connection.submittedNamedStatements[activeQuery.name];
|
|
4342
4417
|
}
|
|
4343
4418
|
}
|
|
4344
4419
|
_handleCopyInResponse(msg) {
|
|
@@ -4405,6 +4480,9 @@ var require_client = __commonJS({
|
|
|
4405
4480
|
});
|
|
4406
4481
|
} else if (client._queryQueue.indexOf(query) !== -1) {
|
|
4407
4482
|
client._queryQueue.splice(client._queryQueue.indexOf(query), 1);
|
|
4483
|
+
} else if (client._sentQueryQueue.indexOf(query) !== -1) {
|
|
4484
|
+
query.callback = () => {
|
|
4485
|
+
};
|
|
4408
4486
|
}
|
|
4409
4487
|
}
|
|
4410
4488
|
setTypeParser(oid, format, parseFn) {
|
|
@@ -4416,13 +4494,17 @@ var require_client = __commonJS({
|
|
|
4416
4494
|
// escapeIdentifier and escapeLiteral moved to utility functions & exported
|
|
4417
4495
|
// on PG
|
|
4418
4496
|
// re-exported here for backwards compatibility
|
|
4419
|
-
escapeIdentifier(
|
|
4420
|
-
return utils.escapeIdentifier(
|
|
4497
|
+
escapeIdentifier(str2) {
|
|
4498
|
+
return utils.escapeIdentifier(str2);
|
|
4421
4499
|
}
|
|
4422
|
-
escapeLiteral(
|
|
4423
|
-
return utils.escapeLiteral(
|
|
4500
|
+
escapeLiteral(str2) {
|
|
4501
|
+
return utils.escapeLiteral(str2);
|
|
4424
4502
|
}
|
|
4425
4503
|
_pulseQueryQueue() {
|
|
4504
|
+
if (this.pipeline) {
|
|
4505
|
+
this._pulsePipelinedQueryQueue();
|
|
4506
|
+
return;
|
|
4507
|
+
}
|
|
4426
4508
|
if (this.readyForQuery === true) {
|
|
4427
4509
|
this._activeQuery = this._queryQueue.shift();
|
|
4428
4510
|
const activeQuery = this._getActiveQuery();
|
|
@@ -4443,6 +4525,30 @@ var require_client = __commonJS({
|
|
|
4443
4525
|
}
|
|
4444
4526
|
}
|
|
4445
4527
|
}
|
|
4528
|
+
_pulsePipelinedQueryQueue() {
|
|
4529
|
+
if (!this._connected || !this._queryable) {
|
|
4530
|
+
return;
|
|
4531
|
+
}
|
|
4532
|
+
while (this._queryQueue.length > 0) {
|
|
4533
|
+
const query = this._queryQueue.shift();
|
|
4534
|
+
this.hasExecuted = true;
|
|
4535
|
+
const queryError = query.submit(this.connection);
|
|
4536
|
+
if (queryError) {
|
|
4537
|
+
process.nextTick(() => {
|
|
4538
|
+
query.handleError(queryError, this.connection);
|
|
4539
|
+
});
|
|
4540
|
+
continue;
|
|
4541
|
+
}
|
|
4542
|
+
this._sentQueryQueue.push(query);
|
|
4543
|
+
}
|
|
4544
|
+
if (this.readyForQuery && !this._activeQuery && this._sentQueryQueue.length > 0) {
|
|
4545
|
+
this._activeQuery = this._sentQueryQueue.shift();
|
|
4546
|
+
this.readyForQuery = false;
|
|
4547
|
+
}
|
|
4548
|
+
if (!this._activeQuery && this._sentQueryQueue.length === 0 && this._queryQueue.length === 0 && this.hasExecuted) {
|
|
4549
|
+
this.emit("drain");
|
|
4550
|
+
}
|
|
4551
|
+
}
|
|
4446
4552
|
query(config, values, callback) {
|
|
4447
4553
|
let query;
|
|
4448
4554
|
let result;
|
|
@@ -4486,6 +4592,9 @@ var require_client = __commonJS({
|
|
|
4486
4592
|
const index = this._queryQueue.indexOf(query);
|
|
4487
4593
|
if (index > -1) {
|
|
4488
4594
|
this._queryQueue.splice(index, 1);
|
|
4595
|
+
} else if (this.pipeline) {
|
|
4596
|
+
this.connection.stream.destroy();
|
|
4597
|
+
return;
|
|
4489
4598
|
}
|
|
4490
4599
|
this._pulseQueryQueue();
|
|
4491
4600
|
}, readTimeout);
|
|
@@ -4512,7 +4621,7 @@ var require_client = __commonJS({
|
|
|
4512
4621
|
});
|
|
4513
4622
|
return result;
|
|
4514
4623
|
}
|
|
4515
|
-
if (this._queryQueue.length > 0) {
|
|
4624
|
+
if (this._queryQueue.length > 0 && !this.pipeline) {
|
|
4516
4625
|
queryQueueLengthDeprecationNotice();
|
|
4517
4626
|
}
|
|
4518
4627
|
this._queryQueue.push(query);
|
|
@@ -4538,7 +4647,11 @@ var require_client = __commonJS({
|
|
|
4538
4647
|
return this._Promise.resolve();
|
|
4539
4648
|
}
|
|
4540
4649
|
}
|
|
4541
|
-
if (
|
|
4650
|
+
if (!this._queryable) {
|
|
4651
|
+
this.connection.stream.destroy();
|
|
4652
|
+
} else if (this.pipeline && (this._getActiveQuery() || this._sentQueryQueue.length > 0 || this._queryQueue.length > 0)) {
|
|
4653
|
+
this.once("drain", () => this.connection.end());
|
|
4654
|
+
} else if (this._getActiveQuery()) {
|
|
4542
4655
|
this.connection.stream.destroy();
|
|
4543
4656
|
} else {
|
|
4544
4657
|
this.connection.end();
|
|
@@ -5028,7 +5141,7 @@ var require_query2 = __commonJS({
|
|
|
5028
5141
|
sourceFunction: "routine"
|
|
5029
5142
|
};
|
|
5030
5143
|
NativeQuery.prototype.handleError = function(err) {
|
|
5031
|
-
const fields = this.native.pq.resultErrorFields();
|
|
5144
|
+
const fields = this.native && this.native.pq.resultErrorFields();
|
|
5032
5145
|
if (fields) {
|
|
5033
5146
|
for (const key in fields) {
|
|
5034
5147
|
const normalizedFieldName = errorFieldMap[key] || key;
|
|
@@ -5161,6 +5274,8 @@ var require_client2 = __commonJS({
|
|
|
5161
5274
|
this._connecting = false;
|
|
5162
5275
|
this._connected = false;
|
|
5163
5276
|
this._queryable = true;
|
|
5277
|
+
this.pipeline = Boolean(config.pipeline);
|
|
5278
|
+
this._pipelineInFlight = false;
|
|
5164
5279
|
const cp = this.connectionParameters = new ConnectionParameters(config);
|
|
5165
5280
|
if (config.nativeConnectionString) cp.nativeConnectionString = config.nativeConnectionString;
|
|
5166
5281
|
this.user = cp.user;
|
|
@@ -5304,7 +5419,7 @@ var require_client2 = __commonJS({
|
|
|
5304
5419
|
});
|
|
5305
5420
|
return result;
|
|
5306
5421
|
}
|
|
5307
|
-
if (this._queryQueue.length > 0) {
|
|
5422
|
+
if (this._queryQueue.length > 0 && !this.pipeline) {
|
|
5308
5423
|
queryQueueLengthDeprecationNotice();
|
|
5309
5424
|
}
|
|
5310
5425
|
this._queryQueue.push(query);
|
|
@@ -5326,14 +5441,21 @@ var require_client2 = __commonJS({
|
|
|
5326
5441
|
cb = (err) => err ? reject(err) : resolve();
|
|
5327
5442
|
});
|
|
5328
5443
|
}
|
|
5329
|
-
|
|
5330
|
-
self.
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
|
|
5444
|
+
const doEnd = function() {
|
|
5445
|
+
self.native.end(function() {
|
|
5446
|
+
self._connected = false;
|
|
5447
|
+
self._errorAllQueries(new Error("Connection terminated"));
|
|
5448
|
+
process.nextTick(() => {
|
|
5449
|
+
self.emit("end");
|
|
5450
|
+
if (cb) cb();
|
|
5451
|
+
});
|
|
5335
5452
|
});
|
|
5336
|
-
}
|
|
5453
|
+
};
|
|
5454
|
+
if (this.pipeline && (this._pipelineInFlight || this._queryQueue.length > 0)) {
|
|
5455
|
+
this.once("drain", doEnd);
|
|
5456
|
+
} else {
|
|
5457
|
+
doEnd();
|
|
5458
|
+
}
|
|
5337
5459
|
return result;
|
|
5338
5460
|
};
|
|
5339
5461
|
Client2.prototype._hasActiveQuery = function() {
|
|
@@ -5343,6 +5465,9 @@ var require_client2 = __commonJS({
|
|
|
5343
5465
|
if (!this._connected) {
|
|
5344
5466
|
return;
|
|
5345
5467
|
}
|
|
5468
|
+
if (this.pipeline && !initialConnection) {
|
|
5469
|
+
return this._pulsePipelinedQueryQueue();
|
|
5470
|
+
}
|
|
5346
5471
|
if (this._hasActiveQuery()) {
|
|
5347
5472
|
return;
|
|
5348
5473
|
}
|
|
@@ -5360,6 +5485,69 @@ var require_client2 = __commonJS({
|
|
|
5360
5485
|
self._pulseQueryQueue();
|
|
5361
5486
|
});
|
|
5362
5487
|
};
|
|
5488
|
+
Client2.prototype._pulsePipelinedQueryQueue = function() {
|
|
5489
|
+
if (!this._connected || this._pipelineInFlight) {
|
|
5490
|
+
return;
|
|
5491
|
+
}
|
|
5492
|
+
if (this._queryQueue.length === 0) {
|
|
5493
|
+
if (this.hasExecuted) {
|
|
5494
|
+
this.emit("drain");
|
|
5495
|
+
}
|
|
5496
|
+
return;
|
|
5497
|
+
}
|
|
5498
|
+
this._pipelineInFlight = true;
|
|
5499
|
+
const self = this;
|
|
5500
|
+
const queries = [];
|
|
5501
|
+
const nativeQueries = [];
|
|
5502
|
+
const utils = require_utils();
|
|
5503
|
+
while (this._queryQueue.length > 0) {
|
|
5504
|
+
const query = this._queryQueue.shift();
|
|
5505
|
+
this.hasExecuted = true;
|
|
5506
|
+
nativeQueries.push(query);
|
|
5507
|
+
const values = query.values ? query.values.map(utils.prepareValue) : null;
|
|
5508
|
+
const pipelineEntry = { text: query.text, name: query.name };
|
|
5509
|
+
if (values) {
|
|
5510
|
+
pipelineEntry.values = values;
|
|
5511
|
+
}
|
|
5512
|
+
if (query.name && this.namedQueries[query.name]) {
|
|
5513
|
+
pipelineEntry._alreadyPrepared = true;
|
|
5514
|
+
}
|
|
5515
|
+
queries.push(pipelineEntry);
|
|
5516
|
+
}
|
|
5517
|
+
this.native.pipeline(queries, function(err, results) {
|
|
5518
|
+
self._pipelineInFlight = false;
|
|
5519
|
+
if (err) {
|
|
5520
|
+
for (let i = 0; i < nativeQueries.length; i++) {
|
|
5521
|
+
const q = nativeQueries[i];
|
|
5522
|
+
q.native = self.native;
|
|
5523
|
+
q.handleError(err);
|
|
5524
|
+
}
|
|
5525
|
+
self._pulsePipelinedQueryQueue();
|
|
5526
|
+
return;
|
|
5527
|
+
}
|
|
5528
|
+
for (let i = 0; i < nativeQueries.length; i++) {
|
|
5529
|
+
const q = nativeQueries[i];
|
|
5530
|
+
const r = results[i];
|
|
5531
|
+
q.native = self.native;
|
|
5532
|
+
if (r.err) {
|
|
5533
|
+
q.handleError(r.err);
|
|
5534
|
+
} else {
|
|
5535
|
+
if (q.name) {
|
|
5536
|
+
self.namedQueries[q.name] = q.text;
|
|
5537
|
+
}
|
|
5538
|
+
q.state = "end";
|
|
5539
|
+
q.emit("end", r.result);
|
|
5540
|
+
if (q.callback) {
|
|
5541
|
+
q.callback(null, r.result);
|
|
5542
|
+
}
|
|
5543
|
+
}
|
|
5544
|
+
setImmediate(function() {
|
|
5545
|
+
q.emit("_done");
|
|
5546
|
+
});
|
|
5547
|
+
}
|
|
5548
|
+
self._pulsePipelinedQueryQueue();
|
|
5549
|
+
});
|
|
5550
|
+
};
|
|
5363
5551
|
Client2.prototype.cancel = function(query) {
|
|
5364
5552
|
if (this._activeQuery === query) {
|
|
5365
5553
|
this.native.cancel(function() {
|
|
@@ -5707,6 +5895,11 @@ function loadEnv() {
|
|
|
5707
5895
|
}
|
|
5708
5896
|
return { ...cachedFileVars, ...process.env };
|
|
5709
5897
|
}
|
|
5898
|
+
function estimateCostUsd(model, tokens) {
|
|
5899
|
+
const [base = model, tier] = model.split(":");
|
|
5900
|
+
const p = MODEL_PRICES[base] ?? MODEL_PRICES[MODEL_TIERS.standard];
|
|
5901
|
+
return (tier === "flex" ? 0.5 : 1) * (tokens.input * p.input + tokens.cachedInput * p.cachedInput + tokens.output * p.output) / 1e6;
|
|
5902
|
+
}
|
|
5710
5903
|
function loadConfig(env = loadEnv()) {
|
|
5711
5904
|
return new Config(env);
|
|
5712
5905
|
}
|
|
@@ -5719,10 +5912,22 @@ function redactUrlPassword(url) {
|
|
|
5719
5912
|
return url;
|
|
5720
5913
|
}
|
|
5721
5914
|
}
|
|
5722
|
-
var cachedFileVars, DEFAULT_TENANT_ID, SECRETS, CORPORATE_API_KEY_ENV, DEFAULT_DATABASE_URL, MissingSecretError, Config;
|
|
5915
|
+
var cachedFileVars, MODEL_TIERS, MODEL_PRICES, DEFAULT_TENANT_ID, SECRETS, CORPORATE_API_KEY_ENV, DEFAULT_DATABASE_URL, MissingSecretError, Config;
|
|
5723
5916
|
var init_config = __esm({
|
|
5724
5917
|
"packages/shared/src/config.ts"() {
|
|
5725
5918
|
"use strict";
|
|
5919
|
+
MODEL_TIERS = {
|
|
5920
|
+
fast: "claude-haiku-4-5",
|
|
5921
|
+
standard: "claude-sonnet-5",
|
|
5922
|
+
premium: "claude-opus-5"
|
|
5923
|
+
};
|
|
5924
|
+
MODEL_PRICES = {
|
|
5925
|
+
"claude-opus-5": { input: 5, cachedInput: 0.5, output: 25 },
|
|
5926
|
+
// Launch pricing made permanent (official pricing page, checked 2026-08-18).
|
|
5927
|
+
"claude-sonnet-5": { input: 2, cachedInput: 0.2, output: 10 },
|
|
5928
|
+
"claude-haiku-4-5": { input: 1, cachedInput: 0.1, output: 5 },
|
|
5929
|
+
"gpt-5.6-luna": { input: 0.2, cachedInput: 0.02, output: 1.2 }
|
|
5930
|
+
};
|
|
5726
5931
|
DEFAULT_TENANT_ID = "00000000-0000-0000-0000-000000000002";
|
|
5727
5932
|
SECRETS = {
|
|
5728
5933
|
anthropicApiKey: {
|
|
@@ -5828,13 +6033,6 @@ var init_config = __esm({
|
|
|
5828
6033
|
}
|
|
5829
6034
|
});
|
|
5830
6035
|
|
|
5831
|
-
// packages/shared/src/ratelimit.ts
|
|
5832
|
-
var init_ratelimit = __esm({
|
|
5833
|
-
"packages/shared/src/ratelimit.ts"() {
|
|
5834
|
-
"use strict";
|
|
5835
|
-
}
|
|
5836
|
-
});
|
|
5837
|
-
|
|
5838
6036
|
// packages/shared/src/sanitize.ts
|
|
5839
6037
|
var init_sanitize = __esm({
|
|
5840
6038
|
"packages/shared/src/sanitize.ts"() {
|
|
@@ -5851,6 +6049,8 @@ function validateFlowDiagram(d) {
|
|
|
5851
6049
|
if (!short(f.title, 120)) errs.push("title: missing or too long");
|
|
5852
6050
|
if (f.summary !== void 0 && !short(f.summary, 300))
|
|
5853
6051
|
errs.push("summary: empty or too long");
|
|
6052
|
+
if (f.explanation !== void 0 && !short(f.explanation, 4e3))
|
|
6053
|
+
errs.push("explanation: empty or too long");
|
|
5854
6054
|
const lanes = Array.isArray(f.lanes) ? f.lanes : [];
|
|
5855
6055
|
if (lanes.length < 2 || lanes.length > 6)
|
|
5856
6056
|
errs.push("lanes: need 2-6 phase bands");
|
|
@@ -5863,22 +6063,31 @@ function validateFlowDiagram(d) {
|
|
|
5863
6063
|
} else laneKeys.add(l.key);
|
|
5864
6064
|
}
|
|
5865
6065
|
const nodes = Array.isArray(f.nodes) ? f.nodes : [];
|
|
5866
|
-
if (nodes.length < 3 || nodes.length >
|
|
5867
|
-
errs.push("nodes: need 3-
|
|
6066
|
+
if (nodes.length < 3 || nodes.length > 18)
|
|
6067
|
+
errs.push("nodes: need 3-18 nodes");
|
|
5868
6068
|
const ids = /* @__PURE__ */ new Set();
|
|
5869
|
-
for (const
|
|
5870
|
-
if (!short(
|
|
6069
|
+
for (const n2 of nodes) {
|
|
6070
|
+
if (!short(n2?.id, 40) || !short(n2?.title, 60)) {
|
|
5871
6071
|
errs.push("nodes: each needs id and title");
|
|
5872
6072
|
continue;
|
|
5873
6073
|
}
|
|
5874
|
-
if (ids.has(
|
|
5875
|
-
ids.add(
|
|
5876
|
-
if (!laneKeys.has(
|
|
5877
|
-
if (!NODE_KINDS.has(
|
|
5878
|
-
if (
|
|
5879
|
-
errs.push(`nodes: ${
|
|
5880
|
-
if (
|
|
5881
|
-
errs.push(`nodes: ${
|
|
6074
|
+
if (ids.has(n2.id)) errs.push(`nodes: duplicate id ${n2.id}`);
|
|
6075
|
+
ids.add(n2.id);
|
|
6076
|
+
if (!laneKeys.has(n2.lane ?? "")) errs.push(`nodes: ${n2.id} unknown lane`);
|
|
6077
|
+
if (!NODE_KINDS.has(n2.kind ?? "")) errs.push(`nodes: ${n2.id} unknown kind`);
|
|
6078
|
+
if (n2.subtitle !== void 0 && !short(n2.subtitle, 60))
|
|
6079
|
+
errs.push(`nodes: ${n2.id} oversized subtitle`);
|
|
6080
|
+
if (n2.evidence !== void 0 && !Array.isArray(n2.evidence))
|
|
6081
|
+
errs.push(`nodes: ${n2.id} evidence must be a list`);
|
|
6082
|
+
if (Array.isArray(n2.evidence)) {
|
|
6083
|
+
if (n2.evidence.length > 12)
|
|
6084
|
+
errs.push(`nodes: ${n2.id} too many evidence entries`);
|
|
6085
|
+
for (const ev of n2.evidence)
|
|
6086
|
+
if (!short(ev, 200) || /[-]/.test(ev))
|
|
6087
|
+
errs.push(`nodes: ${n2.id} evidence entry is not a short string`);
|
|
6088
|
+
}
|
|
6089
|
+
if (n2.description !== void 0 && !short(n2.description, 600))
|
|
6090
|
+
errs.push(`nodes: ${n2.id} description empty or too long`);
|
|
5882
6091
|
}
|
|
5883
6092
|
const edges = Array.isArray(f.edges) ? f.edges : [];
|
|
5884
6093
|
if (edges.length < 2 || edges.length > 24)
|
|
@@ -5889,7 +6098,7 @@ function validateFlowDiagram(d) {
|
|
|
5889
6098
|
continue;
|
|
5890
6099
|
}
|
|
5891
6100
|
if (e.from === e.to) errs.push("edges: self-loop");
|
|
5892
|
-
if (e.label !== void 0 && !short(e.label,
|
|
6101
|
+
if (e.label !== void 0 && !short(e.label, 60))
|
|
5893
6102
|
errs.push("edges: oversized label");
|
|
5894
6103
|
if (e.kind !== void 0 && !EDGE_KINDS.has(e.kind))
|
|
5895
6104
|
errs.push("edges: unknown kind");
|
|
@@ -5957,7 +6166,55 @@ var init_capture_events = __esm({
|
|
|
5957
6166
|
tool_name: 200,
|
|
5958
6167
|
reason: 200
|
|
5959
6168
|
};
|
|
5960
|
-
cut = (s,
|
|
6169
|
+
cut = (s, n2) => s === void 0 ? void 0 : s.length > n2 ? s.slice(0, n2) : s;
|
|
6170
|
+
}
|
|
6171
|
+
});
|
|
6172
|
+
|
|
6173
|
+
// packages/shared/src/emoji.ts
|
|
6174
|
+
function isEmoji(s) {
|
|
6175
|
+
return ONE_EMOJI.test(s);
|
|
6176
|
+
}
|
|
6177
|
+
var ONE_EMOJI;
|
|
6178
|
+
var init_emoji = __esm({
|
|
6179
|
+
"packages/shared/src/emoji.ts"() {
|
|
6180
|
+
"use strict";
|
|
6181
|
+
ONE_EMOJI = new RegExp("^\\p{RGI_Emoji}$", "v");
|
|
6182
|
+
}
|
|
6183
|
+
});
|
|
6184
|
+
|
|
6185
|
+
// packages/shared/src/password.ts
|
|
6186
|
+
var init_password = __esm({
|
|
6187
|
+
"packages/shared/src/password.ts"() {
|
|
6188
|
+
"use strict";
|
|
6189
|
+
}
|
|
6190
|
+
});
|
|
6191
|
+
|
|
6192
|
+
// packages/shared/src/totp.ts
|
|
6193
|
+
var init_totp = __esm({
|
|
6194
|
+
"packages/shared/src/totp.ts"() {
|
|
6195
|
+
"use strict";
|
|
6196
|
+
}
|
|
6197
|
+
});
|
|
6198
|
+
|
|
6199
|
+
// packages/shared/src/brand.ts
|
|
6200
|
+
var BRAND_MARK_PATH, BRAND_ICON_SVG;
|
|
6201
|
+
var init_brand = __esm({
|
|
6202
|
+
"packages/shared/src/brand.ts"() {
|
|
6203
|
+
"use strict";
|
|
6204
|
+
BRAND_MARK_PATH = "M14.47 157.37 L191.23 245.75 A24.00 24.00 0 0 1 204.50 267.21 L204.50 519.96 A12.00 12.00 0 0 1 187.13 530.70 L9.95 442.11 A18.00 18.00 0 0 1 0.00 426.01 L0.00 166.31 A10.00 10.00 0 0 1 14.47 157.37 Z M17.89 102.06 L203.22 9.39 A42.00 42.00 0 0 1 240.78 9.39 L421.89 99.94 A40.00 40.00 0 0 1 444.00 135.72 L444.00 195.70 A28.00 28.00 0 0 1 428.52 220.74 L253.97 308.01 A10.00 10.00 0 0 1 239.50 299.07 L239.50 249.29 A30.00 30.00 0 0 0 222.92 222.46 L17.89 119.94 A10.00 10.00 0 0 1 17.89 102.06 Z M256.08 332.67 L382.11 269.66 A40.00 40.00 0 0 1 417.89 269.66 L467.39 294.41 A40.00 40.00 0 0 1 489.50 330.19 L489.50 425.70 A10.00 10.00 0 0 1 483.97 434.65 L256.87 548.20 A12.00 12.00 0 0 1 239.50 537.46 L239.50 359.51 A30.00 30.00 0 0 1 256.08 332.67 Z";
|
|
6205
|
+
BRAND_ICON_SVG = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><rect width="640" height="640" rx="120" fill="#ffffff"/><g transform="translate(117.48,89.6) scale(0.82747)" fill="#1c1c1a"><path d="' + BRAND_MARK_PATH + '"/></g></svg>';
|
|
6206
|
+
}
|
|
6207
|
+
});
|
|
6208
|
+
|
|
6209
|
+
// packages/shared/src/summary-report.ts
|
|
6210
|
+
var CONTROL;
|
|
6211
|
+
var init_summary_report = __esm({
|
|
6212
|
+
"packages/shared/src/summary-report.ts"() {
|
|
6213
|
+
"use strict";
|
|
6214
|
+
CONTROL = new RegExp(
|
|
6215
|
+
"[\\u0000-\\u0008\\u000b\\u000c\\u000e-\\u001f\\u007f]",
|
|
6216
|
+
"g"
|
|
6217
|
+
);
|
|
5961
6218
|
}
|
|
5962
6219
|
});
|
|
5963
6220
|
|
|
@@ -5969,10 +6226,14 @@ var init_src = __esm({
|
|
|
5969
6226
|
init_memory();
|
|
5970
6227
|
init_confidence();
|
|
5971
6228
|
init_config();
|
|
5972
|
-
init_ratelimit();
|
|
5973
6229
|
init_sanitize();
|
|
5974
6230
|
init_flow();
|
|
5975
6231
|
init_capture_events();
|
|
6232
|
+
init_emoji();
|
|
6233
|
+
init_password();
|
|
6234
|
+
init_totp();
|
|
6235
|
+
init_brand();
|
|
6236
|
+
init_summary_report();
|
|
5976
6237
|
PROSE_STYLE = "Never use em dashes (\u2014) or en dashes (\u2013) anywhere; use commas, colons, periods, or parentheses instead.";
|
|
5977
6238
|
}
|
|
5978
6239
|
});
|
|
@@ -5986,8 +6247,11 @@ function getPool() {
|
|
|
5986
6247
|
}
|
|
5987
6248
|
pool = new esm_default.Pool({
|
|
5988
6249
|
connectionString: config.databaseUrl,
|
|
6250
|
+
// Per process (BJ-137): many instances times the default would fill Postgres; each role
|
|
6251
|
+
// sets its own in the compose file. PgBouncer in front is the cloud ticket's (BJ-140).
|
|
6252
|
+
max: Number(process.env.DATABASE_POOL_MAX) || 10,
|
|
5989
6253
|
// Session GUC set in the startup packet — race-free, applies before any query.
|
|
5990
|
-
options: `-c app.tenant_id=${config.tenantId}`
|
|
6254
|
+
options: `-c app.tenant_id=${config.tenantId} -c hnsw.iterative_scan=strict_order`
|
|
5991
6255
|
});
|
|
5992
6256
|
pool.on("error", () => {
|
|
5993
6257
|
});
|
|
@@ -6538,12 +6802,12 @@ var init_repository = __esm({
|
|
|
6538
6802
|
* first (five logs cover a week completely, where a newest-N slice of raw objects sees only
|
|
6539
6803
|
* the last hours of a busy range).
|
|
6540
6804
|
*/
|
|
6541
|
-
async dailyLogsInRange(startIso, endIso, limit = 50) {
|
|
6805
|
+
async dailyLogsInRange(startIso, endIso, limit = 50, order = "desc") {
|
|
6542
6806
|
return (await this.pool.query(
|
|
6543
6807
|
`SELECT ${MO_COLUMNS}, ${EV_AGG} FROM memory_object
|
|
6544
6808
|
WHERE brain_id = $1 AND type = 'daily_log'
|
|
6545
6809
|
AND occurred_at >= $2::timestamptz AND occurred_at < $3::timestamptz
|
|
6546
|
-
ORDER BY occurred_at DESC LIMIT $4`,
|
|
6810
|
+
ORDER BY occurred_at ${order === "asc" ? "ASC" : "DESC"} LIMIT $4`,
|
|
6547
6811
|
[this.brainId, startIso, endIso, limit]
|
|
6548
6812
|
)).rows;
|
|
6549
6813
|
}
|
|
@@ -6729,6 +6993,20 @@ var init_repository = __esm({
|
|
|
6729
6993
|
return r.rows;
|
|
6730
6994
|
}
|
|
6731
6995
|
// --- Overview / summaries ---
|
|
6996
|
+
/** Compact freshness inputs for the whole Brain, without loading bodies or N+1 queries. */
|
|
6997
|
+
async memoryFreshnessRows() {
|
|
6998
|
+
return (await this.pool.query(
|
|
6999
|
+
`SELECT m.id, m.type, m.title, m.updated_at AS "updatedAt",
|
|
7000
|
+
m.last_verified_at AS "lastVerifiedAt", m.status,
|
|
7001
|
+
EXISTS (SELECT 1 FROM superseded_object s WHERE s.object_id = m.id) AS superseded,
|
|
7002
|
+
EXISTS (SELECT 1 FROM evidence_link el
|
|
7003
|
+
JOIN raw_artifact ra ON ra.id = el.raw_artifact_id
|
|
7004
|
+
WHERE el.memory_object_id = m.id AND ra.last_seen_at > m.updated_at) AS "evidenceChanged"
|
|
7005
|
+
FROM memory_object m WHERE m.brain_id = $1
|
|
7006
|
+
ORDER BY m.updated_at DESC, m.id`,
|
|
7007
|
+
[this.brainId]
|
|
7008
|
+
)).rows;
|
|
7009
|
+
}
|
|
6732
7010
|
/** Memory-object counts for this Brain, grouped by type and confidence (dashboard overview). */
|
|
6733
7011
|
async counts() {
|
|
6734
7012
|
const rows = (await this.pool.query(
|
|
@@ -6777,7 +7055,8 @@ var init_repository = __esm({
|
|
|
6777
7055
|
return (await this.pool.query(
|
|
6778
7056
|
`SELECT * FROM summary WHERE brain_id = $1
|
|
6779
7057
|
AND ($3 OR created_by IS NULL OR created_by = $2)
|
|
6780
|
-
ORDER BY
|
|
7058
|
+
ORDER BY period_start DESC NULLS LAST,
|
|
7059
|
+
COALESCE(period_end, period_start) DESC, created_at DESC`,
|
|
6781
7060
|
[this.brainId, viewerId ?? null, viewerId === void 0]
|
|
6782
7061
|
)).rows;
|
|
6783
7062
|
}
|
|
@@ -6797,8 +7076,8 @@ var init_repository = __esm({
|
|
|
6797
7076
|
if (!isConfidence(confidence))
|
|
6798
7077
|
throw new Error(`invalid confidence: ${confidence}`);
|
|
6799
7078
|
const r = await this.pool.query(
|
|
6800
|
-
`INSERT INTO summary (brain_id, timescale, title, body, period_start, period_end, confidence, created_by)
|
|
6801
|
-
VALUES ($1,$2,$3,$4,$5::date,$6::date,$7,$8) RETURNING *`,
|
|
7079
|
+
`INSERT INTO summary (brain_id, timescale, title, body, period_start, period_end, confidence, created_by, chronicle, data_through)
|
|
7080
|
+
VALUES ($1,$2,$3,$4,$5::date,$6::date,$7,$8,$9::jsonb,$10::date) RETURNING *`,
|
|
6802
7081
|
[
|
|
6803
7082
|
this.brainId,
|
|
6804
7083
|
input.timescale,
|
|
@@ -6807,7 +7086,9 @@ var init_repository = __esm({
|
|
|
6807
7086
|
input.periodStartIso ?? null,
|
|
6808
7087
|
input.periodEndIso ?? null,
|
|
6809
7088
|
confidence,
|
|
6810
|
-
input.createdBy ?? null
|
|
7089
|
+
input.createdBy ?? null,
|
|
7090
|
+
input.chronicle ? JSON.stringify(input.chronicle) : null,
|
|
7091
|
+
input.dataThroughIso ?? null
|
|
6811
7092
|
]
|
|
6812
7093
|
);
|
|
6813
7094
|
return r.rows[0];
|
|
@@ -6842,14 +7123,69 @@ var init_repository = __esm({
|
|
|
6842
7123
|
*/
|
|
6843
7124
|
async updateSummary(id, patch) {
|
|
6844
7125
|
const r = await this.pool.query(
|
|
6845
|
-
`UPDATE summary SET title = $3, body = $4, confidence = $5
|
|
7126
|
+
`UPDATE summary SET title = $3, body = $4, confidence = $5,
|
|
7127
|
+
chronicle = COALESCE($6::jsonb, chronicle),
|
|
7128
|
+
data_through = COALESCE($7::date, data_through)
|
|
6846
7129
|
WHERE id = $1 AND brain_id = $2 RETURNING *`,
|
|
6847
|
-
[
|
|
7130
|
+
[
|
|
7131
|
+
id,
|
|
7132
|
+
this.brainId,
|
|
7133
|
+
patch.title,
|
|
7134
|
+
patch.body,
|
|
7135
|
+
patch.confidence,
|
|
7136
|
+
patch.chronicle ? JSON.stringify(patch.chronicle) : null,
|
|
7137
|
+
patch.dataThroughIso ?? null
|
|
7138
|
+
]
|
|
7139
|
+
);
|
|
7140
|
+
const row = r.rows[0];
|
|
7141
|
+
if (!row) throw new Error("summary not found in this Brain");
|
|
7142
|
+
return row;
|
|
7143
|
+
}
|
|
7144
|
+
/**
|
|
7145
|
+
* Claim the report job for one floor (summaries review S01): returns true for exactly one
|
|
7146
|
+
* caller per (summary, hash), so two web processes never write the same report twice. A
|
|
7147
|
+
* claim older than ten minutes counts as abandoned (the process died mid-call) and may be
|
|
7148
|
+
* taken again; `force` retakes a failed one on the reader's request.
|
|
7149
|
+
*/
|
|
7150
|
+
async claimSummaryReport(id, hash, opts = {}) {
|
|
7151
|
+
const r = await this.pool.query(
|
|
7152
|
+
`UPDATE summary
|
|
7153
|
+
SET report_status = 'pending', report_status_hash = $3,
|
|
7154
|
+
report_status_at = now(), report_error = NULL
|
|
7155
|
+
WHERE id = $1 AND brain_id = $2
|
|
7156
|
+
AND report_hash IS DISTINCT FROM $3
|
|
7157
|
+
AND (report_status_hash IS DISTINCT FROM $3
|
|
7158
|
+
OR report_status IS NULL
|
|
7159
|
+
OR report_status_at < now() - interval '10 minutes'
|
|
7160
|
+
OR (report_status = 'failed' AND $4))`,
|
|
7161
|
+
[id, this.brainId, hash, Boolean(opts.force)]
|
|
7162
|
+
);
|
|
7163
|
+
return (r.rowCount ?? 0) > 0;
|
|
7164
|
+
}
|
|
7165
|
+
/** Store the validated report for the floor it was written from; the job is done. */
|
|
7166
|
+
async saveSummaryReport(id, report, hash) {
|
|
7167
|
+
const r = await this.pool.query(
|
|
7168
|
+
`UPDATE summary
|
|
7169
|
+
SET report = $3::jsonb, report_hash = $4, report_at = now(),
|
|
7170
|
+
report_status = NULL, report_status_hash = NULL,
|
|
7171
|
+
report_status_at = NULL, report_error = NULL
|
|
7172
|
+
WHERE id = $1 AND brain_id = $2 RETURNING *`,
|
|
7173
|
+
[id, this.brainId, JSON.stringify(report), hash]
|
|
6848
7174
|
);
|
|
6849
7175
|
const row = r.rows[0];
|
|
6850
7176
|
if (!row) throw new Error("summary not found in this Brain");
|
|
6851
7177
|
return row;
|
|
6852
7178
|
}
|
|
7179
|
+
/** Record why the report for this floor could not be written; the page shows it. */
|
|
7180
|
+
async failSummaryReport(id, hash, error) {
|
|
7181
|
+
await this.pool.query(
|
|
7182
|
+
`UPDATE summary
|
|
7183
|
+
SET report_status = 'failed', report_status_hash = $3,
|
|
7184
|
+
report_status_at = now(), report_error = $4
|
|
7185
|
+
WHERE id = $1 AND brain_id = $2`,
|
|
7186
|
+
[id, this.brainId, hash, error.slice(0, 200)]
|
|
7187
|
+
);
|
|
7188
|
+
}
|
|
6853
7189
|
/** Store the synthesized prose layer with the floor hash it was built from. */
|
|
6854
7190
|
async saveSummarySynthesis(id, text, hash) {
|
|
6855
7191
|
const r = await this.pool.query(
|
|
@@ -6862,13 +7198,23 @@ var init_repository = __esm({
|
|
|
6862
7198
|
return row;
|
|
6863
7199
|
}
|
|
6864
7200
|
// --- Keyword search (Postgres FTS) ---
|
|
6865
|
-
/**
|
|
7201
|
+
/**
|
|
7202
|
+
* Ranked full-text search over title/why/body in this Brain where any query word counts
|
|
7203
|
+
* (documents sharing more and rarer words rank first). websearch_to_tsquery required EVERY
|
|
7204
|
+
* word, so a sentence-length question or ticket intent matched nothing: measured on 314 real
|
|
7205
|
+
* chat questions, 15% got a keyword hit, with OR nearly all do. plainto_tsquery still stems
|
|
7206
|
+
* and drops stop words; its AND becomes OR. Documents holding every word come first and are
|
|
7207
|
+
* flagged, so fusion can keep trusting them as before.
|
|
7208
|
+
*/
|
|
6866
7209
|
async searchKeyword(query, limit = 10) {
|
|
6867
7210
|
const r = await this.pool.query(
|
|
6868
|
-
`SELECT ${MO_COLUMNS}, ${EV_AGG}, ts_rank(tsv,
|
|
6869
|
-
|
|
6870
|
-
|
|
6871
|
-
|
|
7211
|
+
`SELECT ${MO_COLUMNS}, ${EV_AGG}, ts_rank(tsv, q.kq) AS rank,
|
|
7212
|
+
tsv @@ q.all_kq AS all_words
|
|
7213
|
+
FROM memory_object,
|
|
7214
|
+
(SELECT plainto_tsquery('english', $1) AS all_kq,
|
|
7215
|
+
replace(plainto_tsquery('english', $1)::text, ' & ', ' | ')::tsquery AS kq) q
|
|
7216
|
+
WHERE brain_id = $2 AND tsv @@ q.kq
|
|
7217
|
+
ORDER BY all_words DESC, rank DESC
|
|
6872
7218
|
LIMIT $3`,
|
|
6873
7219
|
[query, this.brainId, limit]
|
|
6874
7220
|
);
|
|
@@ -6958,8 +7304,10 @@ var init_repository = __esm({
|
|
|
6958
7304
|
*/
|
|
6959
7305
|
async saveDocPage(input) {
|
|
6960
7306
|
const r = await this.pool.query(
|
|
6961
|
-
`INSERT INTO doc_page (brain_id, slug, version, kind, title, model, markdown, source_hash, deleted)
|
|
6962
|
-
SELECT $1, $2, COALESCE(max(version), 0) + 1, $3, $4, $5, $6, $7, $8
|
|
7307
|
+
`INSERT INTO doc_page (brain_id, slug, version, kind, title, model, markdown, source_hash, deleted, icon)
|
|
7308
|
+
SELECT $1, $2, COALESCE(max(version), 0) + 1, $3, $4, $5, $6, $7, $8,
|
|
7309
|
+
COALESCE((SELECT icon FROM doc_page WHERE brain_id = $1 AND slug = $2
|
|
7310
|
+
ORDER BY version DESC LIMIT 1), $9)
|
|
6963
7311
|
FROM doc_page WHERE brain_id = $1 AND slug = $2
|
|
6964
7312
|
RETURNING *`,
|
|
6965
7313
|
[
|
|
@@ -6970,11 +7318,21 @@ var init_repository = __esm({
|
|
|
6970
7318
|
JSON.stringify(input.model),
|
|
6971
7319
|
input.markdown,
|
|
6972
7320
|
input.sourceHash,
|
|
6973
|
-
input.deleted ?? false
|
|
7321
|
+
input.deleted ?? false,
|
|
7322
|
+
input.icon ?? null
|
|
6974
7323
|
]
|
|
6975
7324
|
);
|
|
6976
7325
|
return r.rows[0];
|
|
6977
7326
|
}
|
|
7327
|
+
/** Set the icon on a page's current version; false when the page does not exist. */
|
|
7328
|
+
async setDocIcon(slug, icon) {
|
|
7329
|
+
const r = await this.pool.query(
|
|
7330
|
+
`UPDATE doc_page SET icon = $3 WHERE brain_id = $1 AND slug = $2
|
|
7331
|
+
AND version = (SELECT max(version) FROM doc_page WHERE brain_id = $1 AND slug = $2)`,
|
|
7332
|
+
[this.brainId, slug, icon]
|
|
7333
|
+
);
|
|
7334
|
+
return (r.rowCount ?? 0) > 0;
|
|
7335
|
+
}
|
|
6978
7336
|
/** The current documentation: latest version per slug, tombstones excluded, entry first. */
|
|
6979
7337
|
async latestDocPages() {
|
|
6980
7338
|
return this.docPagesAsOf();
|
|
@@ -7037,35 +7395,48 @@ var init_repository = __esm({
|
|
|
7037
7395
|
* ponytail: tsvector computed per query; add a stored generated column + GIN index when a
|
|
7038
7396
|
* Brain's doc tree grows past a few hundred pages.
|
|
7039
7397
|
*/
|
|
7040
|
-
|
|
7398
|
+
/**
|
|
7399
|
+
* Docs search over the stored pages as they stood at `at` (default: now), so the time
|
|
7400
|
+
* lens keeps its point in time (docs review R06). A page whose title equals the query
|
|
7401
|
+
* ranks first, then title prefixes and title mentions, then full-text rank: an exact
|
|
7402
|
+
* page never hides behind pages that merely mention it (R12).
|
|
7403
|
+
*/
|
|
7404
|
+
async docSearch(query, limit = 8, at) {
|
|
7041
7405
|
const terms = query.trim().toLowerCase().split(/\s+/).map((t) => t.replace(/[^\p{L}\p{N}]/gu, "")).filter(Boolean).slice(0, 8);
|
|
7042
7406
|
if (terms.length === 0) return [];
|
|
7043
7407
|
const tsq = terms.map((t, i) => i === terms.length - 1 ? `${t}:*` : t).join(" & ");
|
|
7408
|
+
const plain = query.trim().toLowerCase().replace(/\s+/g, " ");
|
|
7044
7409
|
const r = await this.pool.query(
|
|
7045
7410
|
`WITH latest AS (
|
|
7046
7411
|
SELECT DISTINCT ON (slug) * FROM doc_page
|
|
7047
|
-
WHERE brain_id = $1
|
|
7412
|
+
WHERE brain_id = $1 AND generated_at <= COALESCE($4, now())
|
|
7048
7413
|
ORDER BY slug, version DESC
|
|
7049
7414
|
)
|
|
7050
7415
|
SELECT slug, kind, title,
|
|
7051
7416
|
ts_headline('english', c.prose, q,
|
|
7052
7417
|
'StartSel=[[, StopSel=]], MaxWords=18, MinWords=8') AS snippet
|
|
7053
7418
|
FROM latest, to_tsquery('english', $2) q,
|
|
7054
|
-
-- Snippets read as prose: fenced code, inline backticks, link
|
|
7055
|
-
--
|
|
7056
|
-
--
|
|
7419
|
+
-- Snippets read as prose: fenced code, inline backticks, link targets (web,
|
|
7420
|
+
-- repo-relative, and the docs' own page: scheme), and markdown punctuation
|
|
7421
|
+
-- stripped for display only; the search vector keeps the raw markdown so code
|
|
7422
|
+
-- and config names stay findable. (Template literal: regex backslashes are
|
|
7423
|
+
-- doubled, a single one silently vanishes and the pattern never matches.)
|
|
7057
7424
|
LATERAL (SELECT regexp_replace(regexp_replace(regexp_replace(
|
|
7058
7425
|
markdown, '\`\`\`[^\`]*\`\`\`', ' ', 'g'),
|
|
7059
|
-
']((https
|
|
7426
|
+
'\\]\\((https?:|brainjar-doku/|features/|page:)[^)]*\\)', ']', 'g'),
|
|
7060
7427
|
'[\`#*_|]', '', 'g') AS prose) c
|
|
7061
7428
|
WHERE NOT deleted
|
|
7062
7429
|
AND (setweight(to_tsvector('english', title), 'A')
|
|
7063
7430
|
|| setweight(to_tsvector('english', markdown), 'B')) @@ q
|
|
7064
|
-
ORDER BY
|
|
7065
|
-
|
|
7066
|
-
|
|
7431
|
+
ORDER BY
|
|
7432
|
+
(lower(title) = $5) DESC,
|
|
7433
|
+
(lower(title) LIKE $5 || '%') DESC,
|
|
7434
|
+
(position($5 in lower(title)) > 0) DESC,
|
|
7435
|
+
ts_rank(
|
|
7436
|
+
setweight(to_tsvector('english', title), 'A')
|
|
7437
|
+
|| setweight(to_tsvector('english', markdown), 'B'), q) DESC
|
|
7067
7438
|
LIMIT $3`,
|
|
7068
|
-
[this.brainId, tsq, limit]
|
|
7439
|
+
[this.brainId, tsq, limit, at ?? null, plain]
|
|
7069
7440
|
);
|
|
7070
7441
|
return r.rows;
|
|
7071
7442
|
}
|
|
@@ -7239,13 +7610,45 @@ var init_repository = __esm({
|
|
|
7239
7610
|
}
|
|
7240
7611
|
});
|
|
7241
7612
|
|
|
7613
|
+
// packages/db/src/ratelimit.ts
|
|
7614
|
+
var init_ratelimit = __esm({
|
|
7615
|
+
"packages/db/src/ratelimit.ts"() {
|
|
7616
|
+
"use strict";
|
|
7617
|
+
init_pool();
|
|
7618
|
+
}
|
|
7619
|
+
});
|
|
7620
|
+
|
|
7621
|
+
// packages/db/src/auth-event.ts
|
|
7622
|
+
var init_auth_event = __esm({
|
|
7623
|
+
"packages/db/src/auth-event.ts"() {
|
|
7624
|
+
"use strict";
|
|
7625
|
+
init_pool();
|
|
7626
|
+
}
|
|
7627
|
+
});
|
|
7628
|
+
|
|
7629
|
+
// packages/db/src/email-token.ts
|
|
7630
|
+
var init_email_token = __esm({
|
|
7631
|
+
"packages/db/src/email-token.ts"() {
|
|
7632
|
+
"use strict";
|
|
7633
|
+
init_pool();
|
|
7634
|
+
}
|
|
7635
|
+
});
|
|
7636
|
+
|
|
7242
7637
|
// packages/db/src/auth.ts
|
|
7243
|
-
var DEFAULT_TTL_HOURS;
|
|
7638
|
+
var DEFAULT_TTL_HOURS, LOGIN_FAILURE_WINDOW_MS, SCRYPT, REAUTH_WINDOW_MS, AVATAR_MAX_BYTES;
|
|
7244
7639
|
var init_auth = __esm({
|
|
7245
7640
|
"packages/db/src/auth.ts"() {
|
|
7246
7641
|
"use strict";
|
|
7642
|
+
init_src();
|
|
7247
7643
|
init_pool();
|
|
7644
|
+
init_ratelimit();
|
|
7645
|
+
init_auth_event();
|
|
7646
|
+
init_email_token();
|
|
7248
7647
|
DEFAULT_TTL_HOURS = 24 * 90;
|
|
7648
|
+
LOGIN_FAILURE_WINDOW_MS = 15 * 6e4;
|
|
7649
|
+
SCRYPT = { N: 16384, r: 8, p: 1, maxmem: 64 * 1024 * 1024 };
|
|
7650
|
+
REAUTH_WINDOW_MS = 5 * 6e4;
|
|
7651
|
+
AVATAR_MAX_BYTES = 256 * 1024;
|
|
7249
7652
|
}
|
|
7250
7653
|
});
|
|
7251
7654
|
|
|
@@ -7269,11 +7672,43 @@ var init_oauth = __esm({
|
|
|
7269
7672
|
}
|
|
7270
7673
|
});
|
|
7271
7674
|
|
|
7675
|
+
// packages/db/src/totp.ts
|
|
7676
|
+
var CHALLENGE_TTL_MS;
|
|
7677
|
+
var init_totp2 = __esm({
|
|
7678
|
+
"packages/db/src/totp.ts"() {
|
|
7679
|
+
"use strict";
|
|
7680
|
+
init_src();
|
|
7681
|
+
init_pool();
|
|
7682
|
+
CHALLENGE_TTL_MS = 5 * 6e4;
|
|
7683
|
+
}
|
|
7684
|
+
});
|
|
7685
|
+
|
|
7686
|
+
// packages/db/src/passkey.ts
|
|
7687
|
+
var CHALLENGE_TTL_MS2;
|
|
7688
|
+
var init_passkey = __esm({
|
|
7689
|
+
"packages/db/src/passkey.ts"() {
|
|
7690
|
+
"use strict";
|
|
7691
|
+
init_pool();
|
|
7692
|
+
CHALLENGE_TTL_MS2 = 5 * 6e4;
|
|
7693
|
+
}
|
|
7694
|
+
});
|
|
7695
|
+
|
|
7696
|
+
// packages/db/src/identity.ts
|
|
7697
|
+
var STATE_TTL_MS;
|
|
7698
|
+
var init_identity = __esm({
|
|
7699
|
+
"packages/db/src/identity.ts"() {
|
|
7700
|
+
"use strict";
|
|
7701
|
+
init_pool();
|
|
7702
|
+
STATE_TTL_MS = 5 * 6e4;
|
|
7703
|
+
}
|
|
7704
|
+
});
|
|
7705
|
+
|
|
7272
7706
|
// packages/db/src/team.ts
|
|
7273
7707
|
var init_team = __esm({
|
|
7274
7708
|
"packages/db/src/team.ts"() {
|
|
7275
7709
|
"use strict";
|
|
7276
7710
|
init_pool();
|
|
7711
|
+
init_auth();
|
|
7277
7712
|
}
|
|
7278
7713
|
});
|
|
7279
7714
|
|
|
@@ -7333,14 +7768,42 @@ var init_settings = __esm({
|
|
|
7333
7768
|
APP_SETTING_KEYS = [
|
|
7334
7769
|
"capture_mode",
|
|
7335
7770
|
// transcript privacy: full | distilled | off
|
|
7336
|
-
"author",
|
|
7337
|
-
// developer identity stamped on captured reasoning
|
|
7338
|
-
"promotion_threshold",
|
|
7339
|
-
// signal/noise gate (0..1)
|
|
7340
7771
|
"rationale_model",
|
|
7341
7772
|
// Anthropic model for rationale/consolidation
|
|
7342
|
-
"public_url"
|
|
7773
|
+
"public_url",
|
|
7343
7774
|
// this server's address as clients reach it (wizard command, MCP config)
|
|
7775
|
+
// Outgoing mail (BJ-103); the password is a secret and lives next to the provider keys.
|
|
7776
|
+
"smtp_host",
|
|
7777
|
+
"smtp_port",
|
|
7778
|
+
"smtp_user",
|
|
7779
|
+
"smtp_secure",
|
|
7780
|
+
// "1" = TLS from the start (465), else STARTTLS (587)
|
|
7781
|
+
"mail_from",
|
|
7782
|
+
// Sign in with a provider (BJ-112); the client secrets are secrets, see below.
|
|
7783
|
+
"google_client_id",
|
|
7784
|
+
"github_client_id",
|
|
7785
|
+
// The legal pages the signup checkbox links to (BJ-113).
|
|
7786
|
+
"terms_url",
|
|
7787
|
+
"privacy_url",
|
|
7788
|
+
// Voice chat (voice handoff, 2026-09-21): an explicit "true" or "false"; absent means on.
|
|
7789
|
+
"voice_enabled",
|
|
7790
|
+
// The GPT-Live voice (voice picker, 2026-09-22): an id from VOICE_OPTIONS; absent = default.
|
|
7791
|
+
"voice_name",
|
|
7792
|
+
// AI cost cap (BJ-139), set on the Operator page; env overrides, defaults in ingest/allowance.
|
|
7793
|
+
"ai_contingent",
|
|
7794
|
+
// "on" | "off": monthly contingent per workspace (default on in SaaS)
|
|
7795
|
+
"ai_free_monthly_usd",
|
|
7796
|
+
// Free workspace, USD per month
|
|
7797
|
+
"ai_team_seat_monthly_usd",
|
|
7798
|
+
// Team, USD per seat and month (applies once licenses exist)
|
|
7799
|
+
"ai_operator_exempt",
|
|
7800
|
+
// "true" | "false": operator accounts are never capped
|
|
7801
|
+
"ai_daily_budget_usd",
|
|
7802
|
+
// the instance's emergency brake on the company key; empty = off
|
|
7803
|
+
"ai_budget_released_day",
|
|
7804
|
+
// YYYY-MM-DD the operator lifted today's brake
|
|
7805
|
+
"ai_budget_alerted_day"
|
|
7806
|
+
// YYYY-MM-DD the operator was told the brake tripped
|
|
7344
7807
|
];
|
|
7345
7808
|
PROVIDER_KEY_SETTINGS = {
|
|
7346
7809
|
anthropic: "anthropic_api_key",
|
|
@@ -7373,11 +7836,112 @@ var init_tickets = __esm({
|
|
|
7373
7836
|
}
|
|
7374
7837
|
});
|
|
7375
7838
|
|
|
7839
|
+
// packages/db/src/jobs.ts
|
|
7840
|
+
var CAPTURE_JOB_KINDS, USER_JOB_KINDS;
|
|
7841
|
+
var init_jobs = __esm({
|
|
7842
|
+
"packages/db/src/jobs.ts"() {
|
|
7843
|
+
"use strict";
|
|
7844
|
+
init_pool();
|
|
7845
|
+
CAPTURE_JOB_KINDS = [
|
|
7846
|
+
"capture-extract",
|
|
7847
|
+
"capture-session",
|
|
7848
|
+
"capture-transcript",
|
|
7849
|
+
"capture-structure"
|
|
7850
|
+
];
|
|
7851
|
+
USER_JOB_KINDS = [
|
|
7852
|
+
...CAPTURE_JOB_KINDS,
|
|
7853
|
+
"deepscan",
|
|
7854
|
+
"verify",
|
|
7855
|
+
"summary-report",
|
|
7856
|
+
"resynthesize"
|
|
7857
|
+
];
|
|
7858
|
+
}
|
|
7859
|
+
});
|
|
7860
|
+
|
|
7861
|
+
// packages/db/src/spend.ts
|
|
7862
|
+
function dayKey(at = /* @__PURE__ */ new Date()) {
|
|
7863
|
+
return `day:${at.toISOString().slice(0, 10)}`;
|
|
7864
|
+
}
|
|
7865
|
+
async function addSpend(brainId, usd, at = /* @__PURE__ */ new Date(), pool2 = getPool()) {
|
|
7866
|
+
if (!(usd > 0)) return;
|
|
7867
|
+
await pool2.query(
|
|
7868
|
+
`INSERT INTO llm_spend (key, usd)
|
|
7869
|
+
SELECT k, $2 FROM (
|
|
7870
|
+
SELECT $3::text AS k
|
|
7871
|
+
UNION ALL
|
|
7872
|
+
SELECT 'ws:' || b.owner_id || ':' || $4 FROM brain b
|
|
7873
|
+
WHERE b.id = $1 AND b.owner_id IS NOT NULL
|
|
7874
|
+
) keys
|
|
7875
|
+
ON CONFLICT (key) DO UPDATE SET usd = llm_spend.usd + EXCLUDED.usd, updated_at = now()`,
|
|
7876
|
+
[brainId, usd, dayKey(at), at.toISOString().slice(0, 7)]
|
|
7877
|
+
);
|
|
7878
|
+
}
|
|
7879
|
+
var init_spend = __esm({
|
|
7880
|
+
"packages/db/src/spend.ts"() {
|
|
7881
|
+
"use strict";
|
|
7882
|
+
init_pool();
|
|
7883
|
+
}
|
|
7884
|
+
});
|
|
7885
|
+
|
|
7886
|
+
// packages/db/src/provider-rate.ts
|
|
7887
|
+
async function saveRate(keyId2, model, s, pool2 = getPool()) {
|
|
7888
|
+
await pool2.query(
|
|
7889
|
+
`INSERT INTO llm_rate (key_id, model, requests_limit, requests_remaining, requests_reset_at,
|
|
7890
|
+
tokens_limit, tokens_remaining, tokens_reset_at, observed_at)
|
|
7891
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
|
7892
|
+
ON CONFLICT (key_id, model) DO UPDATE SET
|
|
7893
|
+
requests_limit = EXCLUDED.requests_limit,
|
|
7894
|
+
requests_remaining = EXCLUDED.requests_remaining,
|
|
7895
|
+
requests_reset_at = EXCLUDED.requests_reset_at,
|
|
7896
|
+
tokens_limit = EXCLUDED.tokens_limit,
|
|
7897
|
+
tokens_remaining = EXCLUDED.tokens_remaining,
|
|
7898
|
+
tokens_reset_at = EXCLUDED.tokens_reset_at,
|
|
7899
|
+
observed_at = EXCLUDED.observed_at
|
|
7900
|
+
WHERE llm_rate.observed_at <= EXCLUDED.observed_at`,
|
|
7901
|
+
[
|
|
7902
|
+
keyId2,
|
|
7903
|
+
model,
|
|
7904
|
+
s.requestsLimit,
|
|
7905
|
+
s.requestsRemaining,
|
|
7906
|
+
s.requestsResetAt,
|
|
7907
|
+
s.tokensLimit,
|
|
7908
|
+
s.tokensRemaining,
|
|
7909
|
+
s.tokensResetAt,
|
|
7910
|
+
s.observedAt
|
|
7911
|
+
]
|
|
7912
|
+
);
|
|
7913
|
+
}
|
|
7914
|
+
async function loadRate(keyId2, model, pool2 = getPool()) {
|
|
7915
|
+
const r = await pool2.query(
|
|
7916
|
+
"SELECT * FROM llm_rate WHERE key_id = $1 AND model = $2",
|
|
7917
|
+
[keyId2, model]
|
|
7918
|
+
);
|
|
7919
|
+
const x = r.rows[0];
|
|
7920
|
+
if (!x) return null;
|
|
7921
|
+
const n2 = (v) => v === null ? null : Number(v);
|
|
7922
|
+
return {
|
|
7923
|
+
requestsLimit: n2(x.requests_limit),
|
|
7924
|
+
requestsRemaining: n2(x.requests_remaining),
|
|
7925
|
+
requestsResetAt: x.requests_reset_at,
|
|
7926
|
+
tokensLimit: n2(x.tokens_limit),
|
|
7927
|
+
tokensRemaining: n2(x.tokens_remaining),
|
|
7928
|
+
tokensResetAt: x.tokens_reset_at,
|
|
7929
|
+
observedAt: x.observed_at
|
|
7930
|
+
};
|
|
7931
|
+
}
|
|
7932
|
+
var init_provider_rate = __esm({
|
|
7933
|
+
"packages/db/src/provider-rate.ts"() {
|
|
7934
|
+
"use strict";
|
|
7935
|
+
init_pool();
|
|
7936
|
+
}
|
|
7937
|
+
});
|
|
7938
|
+
|
|
7376
7939
|
// packages/db/src/ruleset.ts
|
|
7377
7940
|
var init_ruleset = __esm({
|
|
7378
7941
|
"packages/db/src/ruleset.ts"() {
|
|
7379
7942
|
"use strict";
|
|
7380
7943
|
init_pool();
|
|
7944
|
+
init_tickets();
|
|
7381
7945
|
}
|
|
7382
7946
|
});
|
|
7383
7947
|
|
|
@@ -7396,11 +7960,21 @@ async function recordLlmUsage(u) {
|
|
|
7396
7960
|
Math.max(0, Math.round(u.outputTokens))
|
|
7397
7961
|
]
|
|
7398
7962
|
);
|
|
7963
|
+
await addSpend(
|
|
7964
|
+
u.brainId ?? null,
|
|
7965
|
+
estimateCostUsd(u.model, {
|
|
7966
|
+
input: u.inputTokens,
|
|
7967
|
+
cachedInput: u.cachedInputTokens,
|
|
7968
|
+
output: u.outputTokens
|
|
7969
|
+
})
|
|
7970
|
+
);
|
|
7399
7971
|
}
|
|
7400
7972
|
var init_usage = __esm({
|
|
7401
7973
|
"packages/db/src/usage.ts"() {
|
|
7402
7974
|
"use strict";
|
|
7975
|
+
init_src();
|
|
7403
7976
|
init_pool();
|
|
7977
|
+
init_spend();
|
|
7404
7978
|
}
|
|
7405
7979
|
});
|
|
7406
7980
|
|
|
@@ -7414,12 +7988,21 @@ var init_src2 = __esm({
|
|
|
7414
7988
|
init_auth();
|
|
7415
7989
|
init_api_token();
|
|
7416
7990
|
init_oauth();
|
|
7991
|
+
init_ratelimit();
|
|
7992
|
+
init_auth_event();
|
|
7993
|
+
init_email_token();
|
|
7994
|
+
init_totp2();
|
|
7995
|
+
init_passkey();
|
|
7996
|
+
init_identity();
|
|
7417
7997
|
init_team();
|
|
7418
7998
|
init_rbac();
|
|
7419
7999
|
init_settings();
|
|
7420
8000
|
init_notifications();
|
|
7421
8001
|
init_conversations();
|
|
7422
8002
|
init_tickets();
|
|
8003
|
+
init_jobs();
|
|
8004
|
+
init_spend();
|
|
8005
|
+
init_provider_rate();
|
|
7423
8006
|
init_ruleset();
|
|
7424
8007
|
init_usage();
|
|
7425
8008
|
init_audit_origin();
|
|
@@ -7671,14 +8254,14 @@ var init_values = __esm({
|
|
|
7671
8254
|
};
|
|
7672
8255
|
isArray = (val) => (isArray = Array.isArray, isArray(val));
|
|
7673
8256
|
isReadonlyArray = isArray;
|
|
7674
|
-
validatePositiveInteger = (name,
|
|
7675
|
-
if (typeof
|
|
8257
|
+
validatePositiveInteger = (name, n2) => {
|
|
8258
|
+
if (typeof n2 !== "number" || !Number.isInteger(n2)) {
|
|
7676
8259
|
throw new AnthropicError(`${name} must be an integer`);
|
|
7677
8260
|
}
|
|
7678
|
-
if (
|
|
8261
|
+
if (n2 < 0) {
|
|
7679
8262
|
throw new AnthropicError(`${name} must be a positive integer`);
|
|
7680
8263
|
}
|
|
7681
|
-
return
|
|
8264
|
+
return n2;
|
|
7682
8265
|
};
|
|
7683
8266
|
safeJSON = (text) => {
|
|
7684
8267
|
try {
|
|
@@ -7950,9 +8533,9 @@ function concatBytes(buffers) {
|
|
|
7950
8533
|
}
|
|
7951
8534
|
return output;
|
|
7952
8535
|
}
|
|
7953
|
-
function encodeUTF8(
|
|
8536
|
+
function encodeUTF8(str2) {
|
|
7954
8537
|
let encoder;
|
|
7955
|
-
return (encodeUTF8_ ?? (encoder = new globalThis.TextEncoder(), encodeUTF8_ = encoder.encode.bind(encoder)))(
|
|
8538
|
+
return (encodeUTF8_ ?? (encoder = new globalThis.TextEncoder(), encodeUTF8_ = encoder.encode.bind(encoder)))(str2);
|
|
7956
8539
|
}
|
|
7957
8540
|
function decodeUTF8(bytes) {
|
|
7958
8541
|
let decoder;
|
|
@@ -8171,12 +8754,12 @@ async function* iterSSEChunks(iterator) {
|
|
|
8171
8754
|
yield data;
|
|
8172
8755
|
}
|
|
8173
8756
|
}
|
|
8174
|
-
function partition(
|
|
8175
|
-
const index =
|
|
8757
|
+
function partition(str2, delimiter) {
|
|
8758
|
+
const index = str2.indexOf(delimiter);
|
|
8176
8759
|
if (index !== -1) {
|
|
8177
|
-
return [
|
|
8760
|
+
return [str2.substring(0, index), delimiter, str2.substring(index + delimiter.length)];
|
|
8178
8761
|
}
|
|
8179
|
-
return [
|
|
8762
|
+
return [str2, "", ""];
|
|
8180
8763
|
}
|
|
8181
8764
|
var _Stream_client, Stream, SSEDecoder;
|
|
8182
8765
|
var init_streaming = __esm({
|
|
@@ -8891,8 +9474,8 @@ var init_headers = __esm({
|
|
|
8891
9474
|
});
|
|
8892
9475
|
|
|
8893
9476
|
// node_modules/@anthropic-ai/sdk/internal/utils/path.mjs
|
|
8894
|
-
function encodeURIPath(
|
|
8895
|
-
return
|
|
9477
|
+
function encodeURIPath(str2) {
|
|
9478
|
+
return str2.replace(/[^A-Za-z0-9\-._~!$&'()*+,;=:@]+/g, encodeURIComponent);
|
|
8896
9479
|
}
|
|
8897
9480
|
var EMPTY, createPathTagFunction, path3;
|
|
8898
9481
|
var init_path = __esm({
|
|
@@ -12288,11 +12871,118 @@ var init_sdk = __esm({
|
|
|
12288
12871
|
}
|
|
12289
12872
|
});
|
|
12290
12873
|
|
|
12874
|
+
// packages/ingest/src/provider-rate.ts
|
|
12875
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
12876
|
+
function parseDuration(s) {
|
|
12877
|
+
if (!s) return null;
|
|
12878
|
+
let ms = 0;
|
|
12879
|
+
let hit = false;
|
|
12880
|
+
for (const [, n2, unit] of s.matchAll(/(\d+(?:\.\d+)?)(ms|h|m|s)/g)) {
|
|
12881
|
+
hit = true;
|
|
12882
|
+
ms += Number(n2) * UNIT_MS[unit];
|
|
12883
|
+
}
|
|
12884
|
+
return hit ? ms : null;
|
|
12885
|
+
}
|
|
12886
|
+
function parseRateHeaders(h, now = /* @__PURE__ */ new Date()) {
|
|
12887
|
+
const num = (a, b) => {
|
|
12888
|
+
const v = h.get(a) ?? h.get(b);
|
|
12889
|
+
const n2 = v === null ? NaN : Number(v);
|
|
12890
|
+
return Number.isFinite(n2) ? n2 : null;
|
|
12891
|
+
};
|
|
12892
|
+
const at = (openai, anthropic) => {
|
|
12893
|
+
const d = parseDuration(h.get(openai));
|
|
12894
|
+
if (d !== null) return new Date(now.getTime() + d);
|
|
12895
|
+
const t = Date.parse(h.get(anthropic) ?? "");
|
|
12896
|
+
return Number.isFinite(t) ? new Date(t) : null;
|
|
12897
|
+
};
|
|
12898
|
+
const s = {
|
|
12899
|
+
requestsLimit: num(
|
|
12900
|
+
"x-ratelimit-limit-requests",
|
|
12901
|
+
"anthropic-ratelimit-requests-limit"
|
|
12902
|
+
),
|
|
12903
|
+
requestsRemaining: num(
|
|
12904
|
+
"x-ratelimit-remaining-requests",
|
|
12905
|
+
"anthropic-ratelimit-requests-remaining"
|
|
12906
|
+
),
|
|
12907
|
+
requestsResetAt: at(
|
|
12908
|
+
"x-ratelimit-reset-requests",
|
|
12909
|
+
"anthropic-ratelimit-requests-reset"
|
|
12910
|
+
),
|
|
12911
|
+
tokensLimit: num(
|
|
12912
|
+
"x-ratelimit-limit-tokens",
|
|
12913
|
+
"anthropic-ratelimit-tokens-limit"
|
|
12914
|
+
),
|
|
12915
|
+
tokensRemaining: num(
|
|
12916
|
+
"x-ratelimit-remaining-tokens",
|
|
12917
|
+
"anthropic-ratelimit-tokens-remaining"
|
|
12918
|
+
),
|
|
12919
|
+
tokensResetAt: at(
|
|
12920
|
+
"x-ratelimit-reset-tokens",
|
|
12921
|
+
"anthropic-ratelimit-tokens-reset"
|
|
12922
|
+
),
|
|
12923
|
+
observedAt: now
|
|
12924
|
+
};
|
|
12925
|
+
return s.requestsLimit === null && s.tokensLimit === null ? null : s;
|
|
12926
|
+
}
|
|
12927
|
+
function roomWaitMs(s, now = /* @__PURE__ */ new Date(), reserve = RESERVE) {
|
|
12928
|
+
const wait = (limit, remaining, resetAt) => {
|
|
12929
|
+
if (!limit || remaining === null || !resetAt) return 0;
|
|
12930
|
+
const need = reserve * limit;
|
|
12931
|
+
if (remaining >= need || resetAt <= now) return 0;
|
|
12932
|
+
const span = resetAt.getTime() - s.observedAt.getTime();
|
|
12933
|
+
const okAt = s.observedAt.getTime() + span * (need - remaining) / (limit - remaining);
|
|
12934
|
+
return Math.max(0, Math.ceil(okAt - now.getTime()));
|
|
12935
|
+
};
|
|
12936
|
+
return Math.max(
|
|
12937
|
+
wait(s.requestsLimit, s.requestsRemaining, s.requestsResetAt),
|
|
12938
|
+
wait(s.tokensLimit, s.tokensRemaining, s.tokensResetAt)
|
|
12939
|
+
);
|
|
12940
|
+
}
|
|
12941
|
+
async function awaitRoom(id, model) {
|
|
12942
|
+
for (; ; ) {
|
|
12943
|
+
const s = await loadRate(id, model).catch(() => null);
|
|
12944
|
+
const ms = s ? roomWaitMs(s) : 0;
|
|
12945
|
+
if (ms <= 0) return;
|
|
12946
|
+
await sleep2(Math.min(ms, MAX_NAP_MS) + Math.random() * 250);
|
|
12947
|
+
}
|
|
12948
|
+
}
|
|
12949
|
+
function noteRate(id, model, headers) {
|
|
12950
|
+
const s = headers ? parseRateHeaders(headers) : null;
|
|
12951
|
+
if (s) void saveRate(id, model, s).catch(() => {
|
|
12952
|
+
});
|
|
12953
|
+
}
|
|
12954
|
+
function rateFetch(apiKey, model, automatic) {
|
|
12955
|
+
const id = keyId(apiKey);
|
|
12956
|
+
return async (input, init2) => {
|
|
12957
|
+
if (automatic) await awaitRoom(id, model);
|
|
12958
|
+
const res = await fetch(input, init2);
|
|
12959
|
+
noteRate(id, model, res.headers);
|
|
12960
|
+
return res;
|
|
12961
|
+
};
|
|
12962
|
+
}
|
|
12963
|
+
var RESERVE, MAX_NAP_MS, sleep2, isAutomatic, keyId, UNIT_MS;
|
|
12964
|
+
var init_provider_rate2 = __esm({
|
|
12965
|
+
"packages/ingest/src/provider-rate.ts"() {
|
|
12966
|
+
"use strict";
|
|
12967
|
+
init_src2();
|
|
12968
|
+
RESERVE = 0.2;
|
|
12969
|
+
MAX_NAP_MS = 6e4;
|
|
12970
|
+
sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
12971
|
+
isAutomatic = (meta) => !meta?.actor && !meta?.waiting;
|
|
12972
|
+
keyId = (apiKey) => createHash2("sha256").update(apiKey).digest("hex").slice(0, 16);
|
|
12973
|
+
UNIT_MS = { ms: 1, s: 1e3, m: 6e4, h: 36e5 };
|
|
12974
|
+
}
|
|
12975
|
+
});
|
|
12976
|
+
|
|
12291
12977
|
// packages/ingest/src/llm.ts
|
|
12292
12978
|
function rationaleModelId(env = process.env) {
|
|
12293
12979
|
return env.BRAINJAR_RATIONALE_MODEL?.trim() || "claude-opus-5";
|
|
12294
12980
|
}
|
|
12295
12981
|
function logUsage(meta, model, usage) {
|
|
12982
|
+
if (!meta?.brainId)
|
|
12983
|
+
console.warn(
|
|
12984
|
+
`brainjar: AI call without a Brain (workload ${meta?.workload ?? "other"}); it counts only toward the daily budget`
|
|
12985
|
+
);
|
|
12296
12986
|
void recordLlmUsage({
|
|
12297
12987
|
brainId: meta?.brainId ?? null,
|
|
12298
12988
|
workload: meta?.workload ?? "other",
|
|
@@ -12318,6 +13008,7 @@ var init_llm = __esm({
|
|
|
12318
13008
|
"use strict";
|
|
12319
13009
|
init_sdk();
|
|
12320
13010
|
init_src2();
|
|
13011
|
+
init_provider_rate2();
|
|
12321
13012
|
cachedSystem = (system) => [
|
|
12322
13013
|
{
|
|
12323
13014
|
type: "text",
|
|
@@ -12330,9 +13021,12 @@ var init_llm = __esm({
|
|
|
12330
13021
|
model;
|
|
12331
13022
|
meta;
|
|
12332
13023
|
constructor(opts) {
|
|
12333
|
-
this.client = new Anthropic({ apiKey: opts.apiKey });
|
|
12334
13024
|
this.model = opts.model ?? rationaleModelId();
|
|
12335
13025
|
this.meta = opts.meta;
|
|
13026
|
+
this.client = new Anthropic({
|
|
13027
|
+
apiKey: opts.apiKey,
|
|
13028
|
+
fetch: rateFetch(opts.apiKey, this.model, isAutomatic(opts.meta))
|
|
13029
|
+
});
|
|
12336
13030
|
}
|
|
12337
13031
|
async complete({
|
|
12338
13032
|
system,
|
|
@@ -12379,17 +13073,29 @@ var init_llm = __esm({
|
|
|
12379
13073
|
var init_openai = __esm({
|
|
12380
13074
|
"packages/ingest/src/openai.ts"() {
|
|
12381
13075
|
"use strict";
|
|
13076
|
+
init_src();
|
|
12382
13077
|
init_llm();
|
|
13078
|
+
init_provider_rate2();
|
|
12383
13079
|
}
|
|
12384
13080
|
});
|
|
12385
13081
|
|
|
12386
|
-
// packages/ingest/src/
|
|
12387
|
-
var
|
|
12388
|
-
"packages/ingest/src/
|
|
13082
|
+
// packages/ingest/src/allowance.ts
|
|
13083
|
+
var init_allowance = __esm({
|
|
13084
|
+
"packages/ingest/src/allowance.ts"() {
|
|
12389
13085
|
"use strict";
|
|
12390
13086
|
init_src2();
|
|
12391
13087
|
init_src();
|
|
12392
|
-
|
|
13088
|
+
}
|
|
13089
|
+
});
|
|
13090
|
+
|
|
13091
|
+
// packages/ingest/src/routing.ts
|
|
13092
|
+
var init_routing = __esm({
|
|
13093
|
+
"packages/ingest/src/routing.ts"() {
|
|
13094
|
+
"use strict";
|
|
13095
|
+
init_src2();
|
|
13096
|
+
init_src();
|
|
13097
|
+
init_allowance();
|
|
13098
|
+
init_llm();
|
|
12393
13099
|
init_openai();
|
|
12394
13100
|
}
|
|
12395
13101
|
});
|
|
@@ -13716,7 +14422,7 @@ ${line}
|
|
|
13716
14422
|
`.split("\n");
|
|
13717
14423
|
} else {
|
|
13718
14424
|
const i = lines.findIndex(
|
|
13719
|
-
(l,
|
|
14425
|
+
(l, n2) => n2 >= range[0] && n2 < range[1] && /^\s*url\s*=/.test(l)
|
|
13720
14426
|
);
|
|
13721
14427
|
next = [...lines];
|
|
13722
14428
|
if (i >= 0) next[i] = line;
|
|
@@ -13936,17 +14642,25 @@ var init_daily_log = __esm({
|
|
|
13936
14642
|
});
|
|
13937
14643
|
|
|
13938
14644
|
// packages/ingest/src/summaries.ts
|
|
13939
|
-
var
|
|
14645
|
+
var REPORT_SYSTEM;
|
|
13940
14646
|
var init_summaries = __esm({
|
|
13941
14647
|
"packages/ingest/src/summaries.ts"() {
|
|
13942
14648
|
"use strict";
|
|
13943
14649
|
init_src2();
|
|
13944
14650
|
init_src();
|
|
13945
|
-
|
|
13946
|
-
|
|
13947
|
-
|
|
13948
|
-
"
|
|
13949
|
-
"
|
|
14651
|
+
init_routing();
|
|
14652
|
+
init_allowance();
|
|
14653
|
+
REPORT_SYSTEM = [
|
|
14654
|
+
"You write the period report of a software project for the people who work on it: what changed, why it matters, what was decided, what is still open.",
|
|
14655
|
+
"Input: the captured records inside the period, numbered, oldest first, each with its date, time, type and title. Record types: feature (behavior that was built or changed), decision (a recorded direction, NOT a delivery), failure (an approach that did not work), event (a commit or another recorded event), note (a note or follow-up).",
|
|
14656
|
+
"Return ONLY a JSON object, no prose around it, no code fence, with this shape:",
|
|
14657
|
+
'{"lead": string, "sections": [{"heading": string, "body": string, "sources": [number]}], "decisions": [{"title": string, "body": string, "sources": [number]}], "open": [{"title": string, "body": string, "sources": [number]}], "timeline": [{"date": "YYYY-MM-DD", "title": string, "body": string, "sources": [number]}]}',
|
|
14658
|
+
"lead: at most three sentences, about sixty words, a reader understands in thirty seconds: the most important changes and what they mean for the people using the project.",
|
|
14659
|
+
"sections: two to six themes that emerge from THESE records (never a fixed list of topics); each body is one to three short paragraphs of plain product language; bullets only for genuinely parallel items; no headings inside a body; sources lists the record numbers the section rests on.",
|
|
14660
|
+
"Say only what the records show. Delivered behavior needs feature or event records; a decision record means something was decided, not that it shipped, so describe it as decided. Never invent numbers, dates, names or sources.",
|
|
14661
|
+
"decisions: only decisions that a decision record documents, cited by number; an empty array otherwise. open: only follow-ups the records name (failure or note records); when the records do not show whether it is resolved, say so; an empty array otherwise. timeline: only when at least three distinct milestones stand out on different days; each entry's date must be the date of a record it cites; an empty array otherwise.",
|
|
14662
|
+
"Fit the length to the data: a short period with few records gets a short report. A month is read as a month: condense to its themes instead of listing weeks. A year is read as an arc: what changed between its start and its end.",
|
|
14663
|
+
"No file paths, function names, migration numbers or shell commands. Write in English.",
|
|
13950
14664
|
PROSE_STYLE
|
|
13951
14665
|
].join(" ");
|
|
13952
14666
|
}
|
|
@@ -13972,11 +14686,11 @@ function slugify(title) {
|
|
|
13972
14686
|
}
|
|
13973
14687
|
function humans(names) {
|
|
13974
14688
|
const count = /* @__PURE__ */ new Map();
|
|
13975
|
-
for (const
|
|
13976
|
-
if (!
|
|
13977
|
-
count.set(
|
|
14689
|
+
for (const n2 of names) {
|
|
14690
|
+
if (!n2 || n2 === "system") continue;
|
|
14691
|
+
count.set(n2, (count.get(n2) ?? 0) + 1);
|
|
13978
14692
|
}
|
|
13979
|
-
return [...count.entries()].sort((a, b) => b[1] - a[1]).map(([
|
|
14693
|
+
return [...count.entries()].sort((a, b) => b[1] - a[1]).map(([n2]) => n2);
|
|
13980
14694
|
}
|
|
13981
14695
|
function moduleGroup(modulePath) {
|
|
13982
14696
|
const segs = modulePath.split("/");
|
|
@@ -14027,7 +14741,8 @@ function buildDokuModel(input) {
|
|
|
14027
14741
|
changes,
|
|
14028
14742
|
decisions,
|
|
14029
14743
|
locations,
|
|
14030
|
-
evidenceUris
|
|
14744
|
+
evidenceUris,
|
|
14745
|
+
linkedDecisionIds
|
|
14031
14746
|
}) => {
|
|
14032
14747
|
let slug = slugify(feature.title);
|
|
14033
14748
|
for (let i = 2; usedSlugs.has(slug) || RESERVED_SLUGS.has(slug); i++)
|
|
@@ -14066,13 +14781,16 @@ function buildDokuModel(input) {
|
|
|
14066
14781
|
owner: explicitOwner ?? contributors[0] ?? null,
|
|
14067
14782
|
contributors,
|
|
14068
14783
|
narrative: null,
|
|
14069
|
-
decisions: dedupeDecisions(decisions).map(
|
|
14070
|
-
|
|
14071
|
-
|
|
14072
|
-
|
|
14073
|
-
|
|
14074
|
-
|
|
14075
|
-
|
|
14784
|
+
decisions: dedupeDecisions(decisions).map(
|
|
14785
|
+
(d) => ({
|
|
14786
|
+
title: d.title,
|
|
14787
|
+
why: d.why,
|
|
14788
|
+
by: d.created_by,
|
|
14789
|
+
date: fmtDate(d.occurred_at),
|
|
14790
|
+
objectId: d.id,
|
|
14791
|
+
...linkedDecisionIds ? { via: linkedDecisionIds.has(d.id) ? "link" : "location" } : {}
|
|
14792
|
+
})
|
|
14793
|
+
).sort((a, b) => a.date < b.date ? 1 : -1),
|
|
14076
14794
|
history,
|
|
14077
14795
|
updated: history[0].date
|
|
14078
14796
|
};
|
|
@@ -14147,8 +14865,19 @@ function renderEntryPage(model, base = "brainjar-doku/", narrative = null) {
|
|
|
14147
14865
|
if (more.length) parts.push("## More", more.join("\n"));
|
|
14148
14866
|
return parts.join("\n\n") + "\n";
|
|
14149
14867
|
}
|
|
14150
|
-
function renderArchitecturePage(model) {
|
|
14868
|
+
function renderArchitecturePage(model, synthesized = {}) {
|
|
14151
14869
|
const parts = [`# How it works`, DOKU_NOTICE];
|
|
14870
|
+
if (synthesized.narrative) parts.push(synthesized.narrative);
|
|
14871
|
+
for (const f of synthesized.flows ?? []) {
|
|
14872
|
+
parts.push(
|
|
14873
|
+
[
|
|
14874
|
+
`## ${f.title}`,
|
|
14875
|
+
...f.summary ? [f.summary] : [],
|
|
14876
|
+
...f.explanation ? [f.explanation] : [],
|
|
14877
|
+
flowLanesMarkdown(f)
|
|
14878
|
+
].join("\n\n")
|
|
14879
|
+
);
|
|
14880
|
+
}
|
|
14152
14881
|
const idx = new Map(model.architecture.groups.map((g, i) => [g.name, i]));
|
|
14153
14882
|
const lines = ["```mermaid", "flowchart LR"];
|
|
14154
14883
|
for (const g of model.architecture.groups) {
|
|
@@ -14162,7 +14891,7 @@ function renderArchitecturePage(model) {
|
|
|
14162
14891
|
);
|
|
14163
14892
|
}
|
|
14164
14893
|
lines.push("```");
|
|
14165
|
-
parts.push(lines.join("\n"));
|
|
14894
|
+
parts.push("## Module groups", lines.join("\n"));
|
|
14166
14895
|
parts.push(
|
|
14167
14896
|
"## Modules by group",
|
|
14168
14897
|
model.architecture.groups.map(
|
|
@@ -14179,6 +14908,234 @@ function firstSentence(s) {
|
|
|
14179
14908
|
const cut2 = t.slice(0, 200);
|
|
14180
14909
|
return `${cut2.slice(0, cut2.lastIndexOf(" "))}\u2026`;
|
|
14181
14910
|
}
|
|
14911
|
+
function parsePresentation(raw, ctx) {
|
|
14912
|
+
const bad = (error) => ({ presentation: null, error, dropped: [] });
|
|
14913
|
+
const o = raw;
|
|
14914
|
+
if (!o || typeof o !== "object") return bad("not an object");
|
|
14915
|
+
if (!str(o.summary, LIMIT.summary))
|
|
14916
|
+
return bad("summary: missing or too long");
|
|
14917
|
+
if (!Array.isArray(o.blocks) || o.blocks.length === 0)
|
|
14918
|
+
return bad("blocks: need a non-empty list");
|
|
14919
|
+
if (o.blocks.length > MAX_BLOCKS)
|
|
14920
|
+
return bad(`blocks: more than ${MAX_BLOCKS}`);
|
|
14921
|
+
const blocks = [];
|
|
14922
|
+
const dropped = [];
|
|
14923
|
+
const flowsPlaced = /* @__PURE__ */ new Set();
|
|
14924
|
+
for (const [i, cand] of o.blocks.entries()) {
|
|
14925
|
+
const b = cand;
|
|
14926
|
+
const at = `block ${i + 1}`;
|
|
14927
|
+
if (!b || typeof b !== "object") return bad(`${at}: not an object`);
|
|
14928
|
+
const title = b.title;
|
|
14929
|
+
switch (b.kind) {
|
|
14930
|
+
case "text": {
|
|
14931
|
+
if (title !== void 0 && !str(title, LIMIT.title))
|
|
14932
|
+
return bad(`${at}: text title too long`);
|
|
14933
|
+
if (!str(b.markdown, LIMIT.markdown))
|
|
14934
|
+
return bad(`${at}: text markdown missing or too long`);
|
|
14935
|
+
blocks.push({
|
|
14936
|
+
kind: "text",
|
|
14937
|
+
...title ? { title: title.trim() } : {},
|
|
14938
|
+
markdown: b.markdown.trim()
|
|
14939
|
+
});
|
|
14940
|
+
break;
|
|
14941
|
+
}
|
|
14942
|
+
case "steps": {
|
|
14943
|
+
if (!str(title, LIMIT.title)) return bad(`${at}: steps need a title`);
|
|
14944
|
+
const items = b.items;
|
|
14945
|
+
if (!Array.isArray(items) || items.length === 0 || items.length > LIMIT.stepItems)
|
|
14946
|
+
return bad(`${at}: steps need 1-${LIMIT.stepItems} items`);
|
|
14947
|
+
const out = [];
|
|
14948
|
+
for (const it of items) {
|
|
14949
|
+
const s = it;
|
|
14950
|
+
if (!str(s?.title, LIMIT.title) || !str(s?.body, LIMIT.stepBody, 0))
|
|
14951
|
+
return bad(`${at}: step needs title and body`);
|
|
14952
|
+
out.push({ title: s.title.trim(), body: s.body.trim() });
|
|
14953
|
+
}
|
|
14954
|
+
blocks.push({ kind: "steps", title: title.trim(), items: out });
|
|
14955
|
+
break;
|
|
14956
|
+
}
|
|
14957
|
+
case "table": {
|
|
14958
|
+
if (!str(title, LIMIT.title)) return bad(`${at}: table needs a title`);
|
|
14959
|
+
const columns = b.columns;
|
|
14960
|
+
if (!Array.isArray(columns) || columns.length < 2 || columns.length > LIMIT.columns || !columns.every((c) => str(c, 80)))
|
|
14961
|
+
return bad(`${at}: table needs 2-${LIMIT.columns} column names`);
|
|
14962
|
+
const rows = b.rows;
|
|
14963
|
+
if (!Array.isArray(rows) || rows.length === 0 || rows.length > LIMIT.rows)
|
|
14964
|
+
return bad(`${at}: table needs 1-${LIMIT.rows} rows`);
|
|
14965
|
+
const outRows = [];
|
|
14966
|
+
for (const r of rows) {
|
|
14967
|
+
if (!Array.isArray(r) || r.length !== columns.length || !r.every((c) => typeof c === "string" && c.length <= LIMIT.cell))
|
|
14968
|
+
return bad(
|
|
14969
|
+
`${at}: every row needs exactly ${columns.length} cells`
|
|
14970
|
+
);
|
|
14971
|
+
outRows.push(r.map((c) => c.trim()));
|
|
14972
|
+
}
|
|
14973
|
+
blocks.push({
|
|
14974
|
+
kind: "table",
|
|
14975
|
+
title: title.trim(),
|
|
14976
|
+
columns: columns.map((c) => c.trim()),
|
|
14977
|
+
rows: outRows
|
|
14978
|
+
});
|
|
14979
|
+
break;
|
|
14980
|
+
}
|
|
14981
|
+
case "endpoint": {
|
|
14982
|
+
const method = typeof b.method === "string" ? b.method.trim().toUpperCase() : "";
|
|
14983
|
+
if (!HTTP_METHODS.has(method)) return bad(`${at}: unknown HTTP method`);
|
|
14984
|
+
if (!str(b.path, LIMIT.endpointPath) || !/^\/\S*$/.test(b.path.trim()))
|
|
14985
|
+
return bad(`${at}: endpoint path must start with /`);
|
|
14986
|
+
if (!str(b.summary, LIMIT.endpointSummary))
|
|
14987
|
+
return bad(`${at}: endpoint needs a summary`);
|
|
14988
|
+
if (b.auth !== void 0 && !str(b.auth, LIMIT.auth))
|
|
14989
|
+
return bad(`${at}: endpoint auth too long`);
|
|
14990
|
+
const path20 = b.path.trim();
|
|
14991
|
+
if (!ctx.factSheet.includes(path20)) {
|
|
14992
|
+
dropped.push(`endpoint ${method} ${path20} (not in the fact sheet)`);
|
|
14993
|
+
break;
|
|
14994
|
+
}
|
|
14995
|
+
blocks.push({
|
|
14996
|
+
kind: "endpoint",
|
|
14997
|
+
method,
|
|
14998
|
+
path: path20,
|
|
14999
|
+
summary: b.summary.trim(),
|
|
15000
|
+
...b.auth ? { auth: b.auth.trim() } : {}
|
|
15001
|
+
});
|
|
15002
|
+
break;
|
|
15003
|
+
}
|
|
15004
|
+
case "code": {
|
|
15005
|
+
if (!str(title, LIMIT.title)) return bad(`${at}: code needs a title`);
|
|
15006
|
+
if (typeof b.language !== "string" || !/^[a-z0-9+#.-]{1,20}$/i.test(b.language.trim()))
|
|
15007
|
+
return bad(`${at}: code needs a language tag`);
|
|
15008
|
+
if (!str(b.code, LIMIT.code))
|
|
15009
|
+
return bad(`${at}: code missing or too long`);
|
|
15010
|
+
blocks.push({
|
|
15011
|
+
kind: "code",
|
|
15012
|
+
title: title.trim(),
|
|
15013
|
+
language: b.language.trim().toLowerCase(),
|
|
15014
|
+
code: b.code.replace(/\s+$/, ""),
|
|
15015
|
+
...b.illustrative === true ? { illustrative: true } : {}
|
|
15016
|
+
});
|
|
15017
|
+
break;
|
|
15018
|
+
}
|
|
15019
|
+
case "callout": {
|
|
15020
|
+
if (b.tone !== "note" && b.tone !== "warning")
|
|
15021
|
+
return bad(`${at}: callout tone must be note or warning`);
|
|
15022
|
+
if (!str(b.text, LIMIT.callout)) return bad(`${at}: callout text`);
|
|
15023
|
+
blocks.push({ kind: "callout", tone: b.tone, text: b.text.trim() });
|
|
15024
|
+
break;
|
|
15025
|
+
}
|
|
15026
|
+
case "flow": {
|
|
15027
|
+
if (!str(title, LIMIT.title)) return bad(`${at}: flow needs a title`);
|
|
15028
|
+
if (b.source !== "flowDiagram" && b.source !== "flow")
|
|
15029
|
+
return bad(`${at}: flow source must be flowDiagram or flow`);
|
|
15030
|
+
const exists2 = b.source === "flowDiagram" ? ctx.hasFlowDiagram : ctx.hasFlow;
|
|
15031
|
+
if (!exists2 || flowsPlaced.has(b.source)) {
|
|
15032
|
+
dropped.push(
|
|
15033
|
+
`flow ${b.source} (${exists2 ? "placed twice" : "no such diagram"})`
|
|
15034
|
+
);
|
|
15035
|
+
break;
|
|
15036
|
+
}
|
|
15037
|
+
flowsPlaced.add(b.source);
|
|
15038
|
+
blocks.push({ kind: "flow", title: title.trim(), source: b.source });
|
|
15039
|
+
break;
|
|
15040
|
+
}
|
|
15041
|
+
default:
|
|
15042
|
+
return bad(`${at}: unknown kind ${String(b.kind)}`);
|
|
15043
|
+
}
|
|
15044
|
+
}
|
|
15045
|
+
if (blocks.length === 0) return bad("blocks: nothing left after validation");
|
|
15046
|
+
return {
|
|
15047
|
+
presentation: { version: 1, summary: o.summary.trim(), blocks },
|
|
15048
|
+
error: null,
|
|
15049
|
+
dropped
|
|
15050
|
+
};
|
|
15051
|
+
}
|
|
15052
|
+
function flowLanesMarkdown(d) {
|
|
15053
|
+
return d.lanes.map((l) => {
|
|
15054
|
+
const nodes = d.nodes.filter((n2) => n2.lane === l.key).map((n2) => n2.title).join(", ");
|
|
15055
|
+
return `- **${l.title}**: ${nodes || "(no stations)"}`;
|
|
15056
|
+
}).join("\n");
|
|
15057
|
+
}
|
|
15058
|
+
function renderBlocksMarkdown(p, diagrams = {}) {
|
|
15059
|
+
const parts = [];
|
|
15060
|
+
for (const b of p.blocks) {
|
|
15061
|
+
switch (b.kind) {
|
|
15062
|
+
case "text":
|
|
15063
|
+
parts.push([h2(b.title), b.markdown].filter(Boolean).join("\n\n"));
|
|
15064
|
+
break;
|
|
15065
|
+
case "steps":
|
|
15066
|
+
parts.push(
|
|
15067
|
+
[
|
|
15068
|
+
h2(b.title),
|
|
15069
|
+
b.items.map(
|
|
15070
|
+
(s, i) => `${i + 1}. **${s.title}**${s.body ? ` ${s.body}` : ""}`
|
|
15071
|
+
).join("\n")
|
|
15072
|
+
].join("\n\n")
|
|
15073
|
+
);
|
|
15074
|
+
break;
|
|
15075
|
+
case "table":
|
|
15076
|
+
parts.push(
|
|
15077
|
+
[
|
|
15078
|
+
h2(b.title),
|
|
15079
|
+
[
|
|
15080
|
+
`| ${b.columns.map(mdCell).join(" | ")} |`,
|
|
15081
|
+
`|${b.columns.map(() => "---").join("|")}|`,
|
|
15082
|
+
...b.rows.map((r) => `| ${r.map(mdCell).join(" | ")} |`)
|
|
15083
|
+
].join("\n")
|
|
15084
|
+
].join("\n\n")
|
|
15085
|
+
);
|
|
15086
|
+
break;
|
|
15087
|
+
case "endpoint":
|
|
15088
|
+
parts.push(
|
|
15089
|
+
[
|
|
15090
|
+
`### \`${b.method} ${b.path}\``,
|
|
15091
|
+
b.summary,
|
|
15092
|
+
...b.auth ? [`Authentication: ${b.auth}`] : []
|
|
15093
|
+
].join("\n\n")
|
|
15094
|
+
);
|
|
15095
|
+
break;
|
|
15096
|
+
case "code":
|
|
15097
|
+
parts.push(
|
|
15098
|
+
[
|
|
15099
|
+
`**${b.title}**${b.illustrative ? " (illustrative example, replace the placeholders)" : ""}`,
|
|
15100
|
+
`\`\`\`${b.language}
|
|
15101
|
+
${b.code}
|
|
15102
|
+
\`\`\``
|
|
15103
|
+
].join("\n\n")
|
|
15104
|
+
);
|
|
15105
|
+
break;
|
|
15106
|
+
case "callout":
|
|
15107
|
+
parts.push(
|
|
15108
|
+
`> **${b.tone === "warning" ? "Warning" : "Note"}:** ${b.text.replace(/\n/g, "\n> ")}`
|
|
15109
|
+
);
|
|
15110
|
+
break;
|
|
15111
|
+
case "flow": {
|
|
15112
|
+
const lines = [h2(b.title)];
|
|
15113
|
+
if (b.source === "flowDiagram" && diagrams.flowDiagram) {
|
|
15114
|
+
const d = diagrams.flowDiagram;
|
|
15115
|
+
lines.push(
|
|
15116
|
+
`_Runtime flow diagram: ${d.title}. Interactive on the web page; its lanes and stations:_`
|
|
15117
|
+
);
|
|
15118
|
+
lines.push(flowLanesMarkdown(d));
|
|
15119
|
+
} else if (b.source === "flow" && diagrams.flow) {
|
|
15120
|
+
lines.push(
|
|
15121
|
+
diagrams.flow.steps.map((s, i) => `${i + 1}. ${s}`).join("\n")
|
|
15122
|
+
);
|
|
15123
|
+
}
|
|
15124
|
+
parts.push(lines.join("\n\n"));
|
|
15125
|
+
break;
|
|
15126
|
+
}
|
|
15127
|
+
}
|
|
15128
|
+
}
|
|
15129
|
+
return parts.join("\n\n");
|
|
15130
|
+
}
|
|
15131
|
+
function pageBody(f) {
|
|
15132
|
+
if (f.presentation)
|
|
15133
|
+
return [
|
|
15134
|
+
f.presentation.summary,
|
|
15135
|
+
renderBlocksMarkdown(f.presentation, f)
|
|
15136
|
+
].join("\n\n");
|
|
15137
|
+
return f.body ?? null;
|
|
15138
|
+
}
|
|
14182
15139
|
function renderFeaturePage(f) {
|
|
14183
15140
|
const meta = [
|
|
14184
15141
|
"| | |",
|
|
@@ -14189,8 +15146,9 @@ function renderFeaturePage(f) {
|
|
|
14189
15146
|
...f.locations.length ? [`| Code | ${f.locations.map((l) => `\`${l}\``).join(", ")} |`] : []
|
|
14190
15147
|
];
|
|
14191
15148
|
const parts = [`# ${f.docTitle ?? f.title}`, meta.join("\n")];
|
|
14192
|
-
|
|
14193
|
-
|
|
15149
|
+
const body = pageBody(f);
|
|
15150
|
+
if (body) {
|
|
15151
|
+
parts.push(body);
|
|
14194
15152
|
} else {
|
|
14195
15153
|
if (f.narrative) parts.push(f.narrative);
|
|
14196
15154
|
parts.push("## What it does", f.what);
|
|
@@ -14236,7 +15194,7 @@ function renderCategoryPage(model) {
|
|
|
14236
15194
|
function renderChangelogPage(model) {
|
|
14237
15195
|
const parts = [`# Changelog`, DOKU_NOTICE];
|
|
14238
15196
|
for (const day of model.changelog) {
|
|
14239
|
-
const counts = Object.entries(day.counts).map(([t,
|
|
15197
|
+
const counts = Object.entries(day.counts).map(([t, n2]) => `${n2} ${t}${n2 === 1 ? "" : "s"}`).join(" \xB7 ");
|
|
14240
15198
|
const who = day.contributors.length ? ` \xB7 by ${day.contributors.join(", ")}` : "";
|
|
14241
15199
|
parts.push(
|
|
14242
15200
|
`## ${day.date}`,
|
|
@@ -14333,7 +15291,7 @@ function renderDokuPages(model) {
|
|
|
14333
15291
|
}
|
|
14334
15292
|
return pages;
|
|
14335
15293
|
}
|
|
14336
|
-
var normTitle, DOC_SECTIONS, fmtDate, MAX_GROUPS, MAX_HIGHLIGHTS, HIGHLIGHT_TYPES, RESERVED_SLUGS, DOKU_NOTICE, mermaidId, mermaidLabel, mdCell, asRecord;
|
|
15294
|
+
var normTitle, DOC_SECTIONS, fmtDate, MAX_GROUPS, MAX_HIGHLIGHTS, HIGHLIGHT_TYPES, RESERVED_SLUGS, DOKU_NOTICE, mermaidId, mermaidLabel, mdCell, HTTP_METHODS, MAX_BLOCKS, LIMIT, str, h2, asRecord;
|
|
14337
15295
|
var init_doku_model = __esm({
|
|
14338
15296
|
"packages/ingest/src/doku-model.ts"() {
|
|
14339
15297
|
"use strict";
|
|
@@ -14360,12 +15318,39 @@ var init_doku_model = __esm({
|
|
|
14360
15318
|
mermaidId = (name, i) => `g${i}`;
|
|
14361
15319
|
mermaidLabel = (s) => s.replace(/"/g, "'");
|
|
14362
15320
|
mdCell = (s) => (s ?? "").replace(/\|/g, "\\|").replace(/\s+/g, " ").trim();
|
|
15321
|
+
HTTP_METHODS = /* @__PURE__ */ new Set([
|
|
15322
|
+
"GET",
|
|
15323
|
+
"POST",
|
|
15324
|
+
"PUT",
|
|
15325
|
+
"PATCH",
|
|
15326
|
+
"DELETE",
|
|
15327
|
+
"HEAD",
|
|
15328
|
+
"OPTIONS"
|
|
15329
|
+
]);
|
|
15330
|
+
MAX_BLOCKS = 40;
|
|
15331
|
+
LIMIT = {
|
|
15332
|
+
title: 120,
|
|
15333
|
+
summary: 300,
|
|
15334
|
+
markdown: 2e4,
|
|
15335
|
+
stepItems: 20,
|
|
15336
|
+
stepBody: 1e3,
|
|
15337
|
+
columns: 6,
|
|
15338
|
+
rows: 40,
|
|
15339
|
+
cell: 400,
|
|
15340
|
+
endpointPath: 200,
|
|
15341
|
+
endpointSummary: 400,
|
|
15342
|
+
auth: 200,
|
|
15343
|
+
code: 6e3,
|
|
15344
|
+
callout: 600
|
|
15345
|
+
};
|
|
15346
|
+
str = (v, max, min = 1) => typeof v === "string" && v.trim().length >= min && v.length <= max;
|
|
15347
|
+
h2 = (t) => t ? `## ${t.trim()}` : null;
|
|
14363
15348
|
asRecord = (v) => v;
|
|
14364
15349
|
}
|
|
14365
15350
|
});
|
|
14366
15351
|
|
|
14367
15352
|
// packages/ingest/src/doku.ts
|
|
14368
|
-
import { createHash as
|
|
15353
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
14369
15354
|
import { mkdir as mkdir10, readFile as readFile10, rm as rm6, stat, writeFile as writeFile10 } from "node:fs/promises";
|
|
14370
15355
|
import path15 from "node:path";
|
|
14371
15356
|
function bodyLocations(o) {
|
|
@@ -14397,10 +15382,10 @@ async function loadDokuData(brainId, root) {
|
|
|
14397
15382
|
relations: ["changes", "affects", "mentions"]
|
|
14398
15383
|
});
|
|
14399
15384
|
const changes = neighbors.filter(
|
|
14400
|
-
(
|
|
15385
|
+
(n2) => n2.id !== feature.id && n2.type === "event"
|
|
14401
15386
|
);
|
|
14402
15387
|
const linkedDecisions = neighbors.filter(
|
|
14403
|
-
(
|
|
15388
|
+
(n2) => n2.id !== feature.id && n2.type === "decision"
|
|
14404
15389
|
);
|
|
14405
15390
|
const linkedModules = neighbors.filter(isModule).map(moduleName);
|
|
14406
15391
|
const locations = [
|
|
@@ -14419,7 +15404,14 @@ async function loadDokuData(brainId, root) {
|
|
|
14419
15404
|
feature.id,
|
|
14420
15405
|
...changes.map((c) => c.id)
|
|
14421
15406
|
]);
|
|
14422
|
-
return {
|
|
15407
|
+
return {
|
|
15408
|
+
feature,
|
|
15409
|
+
changes,
|
|
15410
|
+
decisions,
|
|
15411
|
+
locations,
|
|
15412
|
+
evidenceUris,
|
|
15413
|
+
linkedDecisionIds: new Set(linkedDecisions.map((d) => d.id))
|
|
15414
|
+
};
|
|
14423
15415
|
})
|
|
14424
15416
|
);
|
|
14425
15417
|
const days = (await Promise.all(
|
|
@@ -14457,20 +15449,50 @@ async function loadDokuData(brainId, root) {
|
|
|
14457
15449
|
scripts
|
|
14458
15450
|
};
|
|
14459
15451
|
}
|
|
14460
|
-
function
|
|
15452
|
+
function aiState(m) {
|
|
15453
|
+
return {
|
|
15454
|
+
factHash: typeof m?.factHash === "string" ? m.factHash : "",
|
|
15455
|
+
aiIncomplete: m?.aiIncomplete === true,
|
|
15456
|
+
aiAttempts: typeof m?.aiAttempts === "number" ? m.aiAttempts : 0
|
|
15457
|
+
};
|
|
15458
|
+
}
|
|
15459
|
+
function aiComplete(kind, m) {
|
|
15460
|
+
if (!m) return false;
|
|
15461
|
+
const text = (v) => typeof v === "string" && v.trim().length > 0;
|
|
15462
|
+
switch (kind) {
|
|
15463
|
+
case "entry":
|
|
15464
|
+
case "architecture":
|
|
15465
|
+
return text(m.narrative);
|
|
15466
|
+
case "feature":
|
|
15467
|
+
return (Boolean(m.presentation) || text(m.body)) && text(m.category);
|
|
15468
|
+
case "category":
|
|
15469
|
+
return text(m.body);
|
|
15470
|
+
default:
|
|
15471
|
+
return true;
|
|
15472
|
+
}
|
|
15473
|
+
}
|
|
15474
|
+
function parseDocPage(raw, categoryKeys, ctx) {
|
|
15475
|
+
const bad = (error2) => ({ page: null, error: error2, dropped: [] });
|
|
14461
15476
|
const match = raw.match(/\{[\s\S]*\}/);
|
|
14462
|
-
if (!match) return
|
|
15477
|
+
if (!match) return bad("no JSON");
|
|
15478
|
+
let obj;
|
|
14463
15479
|
try {
|
|
14464
|
-
|
|
14465
|
-
const title = typeof obj.title === "string" ? obj.title.trim() : "";
|
|
14466
|
-
const category = typeof obj.category === "string" ? obj.category.trim() : "";
|
|
14467
|
-
const body = typeof obj.body === "string" ? obj.body.trim() : "";
|
|
14468
|
-
if (!title || title.length > 120 || !body) return null;
|
|
14469
|
-
if (!categoryKeys.includes(category)) return null;
|
|
14470
|
-
return { title, category, body };
|
|
15480
|
+
obj = JSON.parse(match[0]);
|
|
14471
15481
|
} catch {
|
|
14472
|
-
return
|
|
14473
|
-
}
|
|
15482
|
+
return bad("invalid JSON (truncated reply?)");
|
|
15483
|
+
}
|
|
15484
|
+
const title = typeof obj.title === "string" ? obj.title.trim() : "";
|
|
15485
|
+
const category = typeof obj.category === "string" ? obj.category.trim() : "";
|
|
15486
|
+
if (!title || title.length > 120) return bad("title: missing or too long");
|
|
15487
|
+
if (!categoryKeys.includes(category)) return bad("category: unknown key");
|
|
15488
|
+
const icon = typeof obj.icon === "string" && isEmoji(obj.icon.trim()) ? obj.icon.trim() : null;
|
|
15489
|
+
const { presentation, error, dropped } = parsePresentation(obj, ctx);
|
|
15490
|
+
if (!presentation) return bad(error ?? "presentation invalid");
|
|
15491
|
+
return {
|
|
15492
|
+
page: { title, category, presentation, icon },
|
|
15493
|
+
error: null,
|
|
15494
|
+
dropped
|
|
15495
|
+
};
|
|
14474
15496
|
}
|
|
14475
15497
|
function parseTaxonomy(raw) {
|
|
14476
15498
|
const match = raw.match(/\{[\s\S]*\}/);
|
|
@@ -14485,25 +15507,53 @@ function parseTaxonomy(raw) {
|
|
|
14485
15507
|
const name = typeof r.name === "string" ? r.name.trim() : "";
|
|
14486
15508
|
const description = typeof r.description === "string" ? r.description.trim() : "";
|
|
14487
15509
|
if (!/^[a-z0-9-]{2,40}$/.test(key) || !name || !description) return null;
|
|
15510
|
+
if (name.length > 80 || description.length > 240) return null;
|
|
14488
15511
|
out.push({ key, name, description });
|
|
14489
15512
|
}
|
|
14490
|
-
if (out.length <
|
|
15513
|
+
if (out.length < 1 || out.length > 40) return null;
|
|
14491
15514
|
if (new Set(out.map((c) => c.key)).size !== out.length) return null;
|
|
14492
15515
|
return out;
|
|
14493
15516
|
} catch {
|
|
14494
15517
|
return null;
|
|
14495
15518
|
}
|
|
14496
15519
|
}
|
|
15520
|
+
function parseRefile(raw, slugs, keys) {
|
|
15521
|
+
let pages = null;
|
|
15522
|
+
const match = raw.match(/\{[\s\S]*\}/);
|
|
15523
|
+
if (match) {
|
|
15524
|
+
try {
|
|
15525
|
+
const obj = JSON.parse(match[0]);
|
|
15526
|
+
if (typeof obj.pages === "object" && obj.pages !== null && !Array.isArray(obj.pages))
|
|
15527
|
+
pages = obj.pages;
|
|
15528
|
+
} catch {
|
|
15529
|
+
}
|
|
15530
|
+
}
|
|
15531
|
+
if (!pages) {
|
|
15532
|
+
pages = {};
|
|
15533
|
+
for (const m of raw.matchAll(/"([^"\n]+)"\s*:\s*"([^"\n]*)"/g))
|
|
15534
|
+
pages[m[1]] = m[2];
|
|
15535
|
+
}
|
|
15536
|
+
const known = new Set(keys);
|
|
15537
|
+
const out = {};
|
|
15538
|
+
for (const slug of slugs) {
|
|
15539
|
+
const key = pages[slug];
|
|
15540
|
+
if (typeof key === "string" && known.has(key.trim()))
|
|
15541
|
+
out[slug] = key.trim();
|
|
15542
|
+
}
|
|
15543
|
+
return Object.keys(out).length ? out : null;
|
|
15544
|
+
}
|
|
14497
15545
|
function parseCategoryBody(raw, memberSlugs) {
|
|
14498
15546
|
let body = raw.trim();
|
|
14499
|
-
if (!body || body.length >
|
|
15547
|
+
if (!body || body.length > 2e4) return null;
|
|
14500
15548
|
const known = new Set(memberSlugs);
|
|
14501
15549
|
body = body.replace(
|
|
14502
15550
|
/\[([^\]]+)\]\(page:([^)\s]+)\)/g,
|
|
14503
15551
|
(whole, text, slug) => known.has(slug) ? whole : text
|
|
14504
15552
|
);
|
|
14505
|
-
|
|
14506
|
-
|
|
15553
|
+
body = body.replace(
|
|
15554
|
+
/\(page:([^)\s]+)\)/g,
|
|
15555
|
+
(whole, slug) => known.has(slug) ? whole : ""
|
|
15556
|
+
);
|
|
14507
15557
|
return body;
|
|
14508
15558
|
}
|
|
14509
15559
|
function archNotesInput(model) {
|
|
@@ -14597,13 +15647,19 @@ function parseArchFlows(raw, knownModules) {
|
|
|
14597
15647
|
const errors = [];
|
|
14598
15648
|
for (const cand of obj.flows.slice(0, 4)) {
|
|
14599
15649
|
const f = cand;
|
|
15650
|
+
if (f && typeof f === "object") {
|
|
15651
|
+
const x = f.explanation;
|
|
15652
|
+
if (typeof x === "string" && x.trim() && x.length <= 4e3)
|
|
15653
|
+
f.explanation = x.trim();
|
|
15654
|
+
else delete f.explanation;
|
|
15655
|
+
}
|
|
14600
15656
|
if (Array.isArray(f?.nodes)) {
|
|
14601
|
-
for (const
|
|
14602
|
-
if (Array.isArray(
|
|
14603
|
-
|
|
15657
|
+
for (const n2 of f.nodes) {
|
|
15658
|
+
if (Array.isArray(n2?.evidence)) {
|
|
15659
|
+
n2.evidence = n2.evidence.filter(
|
|
14604
15660
|
(p) => typeof p === "string" && knownModules.has(p)
|
|
14605
15661
|
);
|
|
14606
|
-
if (
|
|
15662
|
+
if (n2.evidence.length === 0) delete n2.evidence;
|
|
14607
15663
|
}
|
|
14608
15664
|
}
|
|
14609
15665
|
}
|
|
@@ -14627,12 +15683,12 @@ function parseFeatureFlow(raw, slug, knownPaths) {
|
|
|
14627
15683
|
const f = obj.flow;
|
|
14628
15684
|
f.slug = `${slug.slice(0, 59)}-flow`;
|
|
14629
15685
|
if (Array.isArray(f?.nodes)) {
|
|
14630
|
-
for (const
|
|
14631
|
-
if (Array.isArray(
|
|
14632
|
-
|
|
15686
|
+
for (const n2 of f.nodes) {
|
|
15687
|
+
if (Array.isArray(n2?.evidence)) {
|
|
15688
|
+
n2.evidence = n2.evidence.filter(
|
|
14633
15689
|
(p) => typeof p === "string" && knownPaths.has(p)
|
|
14634
15690
|
);
|
|
14635
|
-
if (
|
|
15691
|
+
if (n2.evidence.length === 0) delete n2.evidence;
|
|
14636
15692
|
}
|
|
14637
15693
|
}
|
|
14638
15694
|
}
|
|
@@ -14657,7 +15713,7 @@ function parseFlow(raw, configKeys) {
|
|
|
14657
15713
|
const edges = obj.edges;
|
|
14658
15714
|
if (!Array.isArray(edges) || edges.length === 0 || !edges.every(
|
|
14659
15715
|
(e) => Array.isArray(e) && e.length === 2 && e.every(
|
|
14660
|
-
(
|
|
15716
|
+
(n2) => Number.isInteger(n2) && n2 >= 0 && n2 < steps.length
|
|
14661
15717
|
) && e[0] !== e[1]
|
|
14662
15718
|
))
|
|
14663
15719
|
return bad("edges: need valid [from,to] index pairs");
|
|
@@ -14705,9 +15761,16 @@ async function generateDokuInner(brainId, opts = {}) {
|
|
|
14705
15761
|
const repo = brainRepo(brainId);
|
|
14706
15762
|
const meta = new Map((await repo.docPageMeta()).map((m) => [m.slug, m]));
|
|
14707
15763
|
const storedBySlug = /* @__PURE__ */ new Map();
|
|
15764
|
+
const storedRows = /* @__PURE__ */ new Map();
|
|
14708
15765
|
try {
|
|
14709
|
-
for (const p of await repo.latestDocPages())
|
|
15766
|
+
for (const p of await repo.latestDocPages()) {
|
|
14710
15767
|
storedBySlug.set(p.slug, p.model);
|
|
15768
|
+
storedRows.set(p.slug, {
|
|
15769
|
+
title: p.title,
|
|
15770
|
+
model: p.model,
|
|
15771
|
+
markdown: p.markdown
|
|
15772
|
+
});
|
|
15773
|
+
}
|
|
14711
15774
|
} catch {
|
|
14712
15775
|
}
|
|
14713
15776
|
const takenTitle = /* @__PURE__ */ new Map();
|
|
@@ -14726,10 +15789,11 @@ async function generateDokuInner(brainId, opts = {}) {
|
|
|
14726
15789
|
const stored = storedBySlug.get("index")?.categories;
|
|
14727
15790
|
if (Array.isArray(stored) && stored.length) categories = stored;
|
|
14728
15791
|
}
|
|
15792
|
+
const storedTaxonomy = JSON.stringify(categories);
|
|
14729
15793
|
const hasNewFeature = pages.some(
|
|
14730
15794
|
(p) => p.kind === "feature" && !meta.get(p.slug)
|
|
14731
15795
|
);
|
|
14732
|
-
if (opts.llm && (categories.length === 0 || hasNewFeature)) {
|
|
15796
|
+
if (opts.llm && (categories.length === 0 || hasNewFeature || opts.retaxonomy)) {
|
|
14733
15797
|
try {
|
|
14734
15798
|
const next = parseTaxonomy(
|
|
14735
15799
|
await opts.llm.complete({
|
|
@@ -14743,7 +15807,9 @@ async function generateDokuInner(brainId, opts = {}) {
|
|
|
14743
15807
|
(f) => `- ${f.title}: ${firstSentence(f.what)}`
|
|
14744
15808
|
)
|
|
14745
15809
|
].join("\n"),
|
|
14746
|
-
|
|
15810
|
+
// Room for a large product's taxonomy; an invalid or clipped reply keeps the
|
|
15811
|
+
// stored one (parseTaxonomy returns null).
|
|
15812
|
+
maxTokens: 3e3
|
|
14747
15813
|
})
|
|
14748
15814
|
);
|
|
14749
15815
|
if (next) categories = next;
|
|
@@ -14751,42 +15817,82 @@ async function generateDokuInner(brainId, opts = {}) {
|
|
|
14751
15817
|
}
|
|
14752
15818
|
}
|
|
14753
15819
|
const categoryKeys = categories.map((c) => c.key);
|
|
15820
|
+
const taxonomyChanged = JSON.stringify(categories) !== storedTaxonomy;
|
|
15821
|
+
const endpointFacts = [];
|
|
15822
|
+
for (const [slug, m] of storedBySlug) {
|
|
15823
|
+
const p = m.presentation;
|
|
15824
|
+
if (!Array.isArray(p?.blocks)) continue;
|
|
15825
|
+
for (const b of p.blocks)
|
|
15826
|
+
if (b.kind === "endpoint")
|
|
15827
|
+
endpointFacts.push({
|
|
15828
|
+
slug,
|
|
15829
|
+
title: typeof m.docTitle === "string" ? m.docTitle : slug,
|
|
15830
|
+
method: b.method,
|
|
15831
|
+
path: b.path,
|
|
15832
|
+
summary: b.summary,
|
|
15833
|
+
...b.auth ? { auth: b.auth } : {}
|
|
15834
|
+
});
|
|
15835
|
+
}
|
|
14754
15836
|
let written = 0;
|
|
14755
15837
|
let polished = false;
|
|
14756
15838
|
const seen = /* @__PURE__ */ new Set();
|
|
14757
15839
|
const featureInfo = [];
|
|
15840
|
+
const save = async (input) => {
|
|
15841
|
+
await repo.saveDocPage(input);
|
|
15842
|
+
storedRows.set(input.slug, {
|
|
15843
|
+
title: input.title,
|
|
15844
|
+
model: input.model,
|
|
15845
|
+
markdown: input.markdown
|
|
15846
|
+
});
|
|
15847
|
+
storedBySlug.set(input.slug, input.model);
|
|
15848
|
+
written++;
|
|
15849
|
+
};
|
|
14758
15850
|
for (const page of pages) {
|
|
14759
15851
|
seen.add(page.slug);
|
|
14760
15852
|
const rawStructure = page.kind === "architecture" ? (() => {
|
|
14761
15853
|
const raw = page.model.raw;
|
|
14762
15854
|
return raw ? sha1(JSON.stringify([raw.modules, raw.edges])) : "";
|
|
14763
15855
|
})() : "";
|
|
15856
|
+
const factHash = sha1(page.markdown);
|
|
14764
15857
|
const sourceHash = sha1(
|
|
14765
|
-
page.markdown + (page.kind === "entry" ? "
|
|
14766
|
-
//
|
|
14767
|
-
|
|
15858
|
+
page.markdown + (page.kind === "entry" ? "v4" : page.kind === "feature" ? (
|
|
15859
|
+
// f7: the block presentation model (docs redesign) regenerates every
|
|
15860
|
+
// feature page once; run with `only` first for a representative sample.
|
|
15861
|
+
"f7"
|
|
14768
15862
|
) : page.kind === "architecture" ? (
|
|
14769
|
-
//
|
|
14770
|
-
"
|
|
15863
|
+
// a6: flow explanations + full markdown (docs hubs) regenerate the page once.
|
|
15864
|
+
"a6" + rawStructure
|
|
14771
15865
|
) : "")
|
|
14772
15866
|
);
|
|
14773
15867
|
const prev = meta.get(page.slug);
|
|
14774
|
-
|
|
15868
|
+
const stored = storedBySlug.get(page.slug);
|
|
15869
|
+
const state = aiState(stored);
|
|
15870
|
+
const required = Boolean(opts.llm && page.polishable);
|
|
15871
|
+
const outsideSample = Boolean(opts.only && !opts.only.includes(page.slug));
|
|
15872
|
+
const unchanged = Boolean(
|
|
15873
|
+
prev && !prev.deleted && prev.source_hash === sourceHash
|
|
15874
|
+
);
|
|
15875
|
+
const retry = required && unchanged && (state.aiIncomplete || !aiComplete(page.kind, stored)) && (state.aiAttempts < MAX_AI_ATTEMPTS || Boolean(opts.forceRetry));
|
|
15876
|
+
const entryTaxonomy = page.kind === "entry" && taxonomyChanged;
|
|
15877
|
+
if (prev && !prev.deleted && (outsideSample || unchanged && !storedCollisions.has(page.slug) && !retry && !entryTaxonomy)) {
|
|
14775
15878
|
if (page.kind === "feature") {
|
|
14776
|
-
const
|
|
15879
|
+
const s = stored;
|
|
14777
15880
|
const f = page.model;
|
|
14778
15881
|
featureInfo.push({
|
|
14779
15882
|
slug: page.slug,
|
|
14780
|
-
title: typeof
|
|
15883
|
+
title: typeof s?.docTitle === "string" ? s.docTitle : page.title,
|
|
14781
15884
|
what: f.what,
|
|
14782
15885
|
updated: f.updated,
|
|
14783
|
-
category: typeof
|
|
15886
|
+
category: typeof s?.category === "string" ? s.category : null
|
|
14784
15887
|
});
|
|
14785
15888
|
}
|
|
14786
15889
|
continue;
|
|
14787
15890
|
}
|
|
15891
|
+
const sameFacts = stored !== void 0 && state.factHash === factHash;
|
|
14788
15892
|
let markdown = page.markdown;
|
|
14789
15893
|
let model = page.model;
|
|
15894
|
+
let icon = null;
|
|
15895
|
+
let ok2 = !required;
|
|
14790
15896
|
if (opts.llm && page.polishable && page.kind === "architecture") {
|
|
14791
15897
|
try {
|
|
14792
15898
|
const { notes } = parseArchNotes(
|
|
@@ -14800,6 +15906,7 @@ async function generateDokuInner(brainId, opts = {}) {
|
|
|
14800
15906
|
if (notes) {
|
|
14801
15907
|
model = { ...model, narrative: notes.narrative, roles: notes.roles };
|
|
14802
15908
|
polished = true;
|
|
15909
|
+
ok2 = true;
|
|
14803
15910
|
}
|
|
14804
15911
|
} catch {
|
|
14805
15912
|
}
|
|
@@ -14818,61 +15925,47 @@ async function generateDokuInner(brainId, opts = {}) {
|
|
|
14818
15925
|
}
|
|
14819
15926
|
} catch {
|
|
14820
15927
|
}
|
|
15928
|
+
if (!ok2 && sameFacts) {
|
|
15929
|
+
const s = stored;
|
|
15930
|
+
const kept = model;
|
|
15931
|
+
model = {
|
|
15932
|
+
...model,
|
|
15933
|
+
...typeof s.narrative === "string" ? { narrative: s.narrative, roles: s.roles } : {},
|
|
15934
|
+
...!kept.flows && Array.isArray(s.flows) ? { flows: s.flows } : {}
|
|
15935
|
+
};
|
|
15936
|
+
}
|
|
15937
|
+
const arch = model;
|
|
15938
|
+
if (arch.narrative || arch.flows?.length)
|
|
15939
|
+
markdown = renderArchitecturePage(doku, {
|
|
15940
|
+
narrative: arch.narrative ?? null,
|
|
15941
|
+
flows: arch.flows ?? []
|
|
15942
|
+
});
|
|
14821
15943
|
} else if (page.kind === "entry") {
|
|
14822
15944
|
model = { ...page.model, categories };
|
|
14823
15945
|
if (opts.llm && page.polishable) {
|
|
14824
15946
|
try {
|
|
14825
|
-
const
|
|
15947
|
+
const narrative2 = (await opts.llm.complete({
|
|
14826
15948
|
system: ENTRY_NARRATIVE_SYSTEM,
|
|
14827
15949
|
prompt: page.markdown,
|
|
14828
|
-
maxTokens:
|
|
15950
|
+
maxTokens: 2500
|
|
14829
15951
|
})).trim();
|
|
14830
|
-
if (
|
|
14831
|
-
model = { ...model, narrative };
|
|
14832
|
-
markdown = renderEntryPage(doku, "brainjar-doku/", narrative);
|
|
15952
|
+
if (narrative2) {
|
|
15953
|
+
model = { ...model, narrative: narrative2 };
|
|
14833
15954
|
polished = true;
|
|
15955
|
+
ok2 = true;
|
|
14834
15956
|
}
|
|
14835
15957
|
} catch {
|
|
14836
15958
|
}
|
|
14837
|
-
|
|
14838
|
-
|
|
14839
|
-
|
|
14840
|
-
|
|
14841
|
-
await opts.llm.complete({
|
|
14842
|
-
system: PAGE_SYSTEM,
|
|
14843
|
-
prompt: [
|
|
14844
|
-
"Categories (pick exactly one key):",
|
|
14845
|
-
...categories.length ? categories.map(
|
|
14846
|
-
(c) => `- ${c.key}: ${c.name} (${c.description})`
|
|
14847
|
-
) : ["- general: General (everything)"],
|
|
14848
|
-
"",
|
|
14849
|
-
"Fact sheet:",
|
|
14850
|
-
page.markdown
|
|
14851
|
-
].join("\n"),
|
|
14852
|
-
maxTokens: 2500
|
|
14853
|
-
}),
|
|
14854
|
-
categoryKeys.length ? categoryKeys : ["general"]
|
|
14855
|
-
);
|
|
14856
|
-
if (synth) {
|
|
14857
|
-
const title = pickDocTitle(
|
|
14858
|
-
synth.title,
|
|
14859
|
-
page.title,
|
|
14860
|
-
page.slug,
|
|
14861
|
-
takenTitle
|
|
14862
|
-
);
|
|
14863
|
-
const f = {
|
|
14864
|
-
...page.model,
|
|
14865
|
-
body: synth.body,
|
|
14866
|
-
docTitle: title,
|
|
14867
|
-
category: synth.category
|
|
14868
|
-
};
|
|
14869
|
-
model = f;
|
|
14870
|
-
markdown = renderFeaturePage(f);
|
|
14871
|
-
page.title = title;
|
|
14872
|
-
polished = true;
|
|
15959
|
+
if (!ok2 && sameFacts) {
|
|
15960
|
+
const s = stored;
|
|
15961
|
+
if (typeof s.narrative === "string" && s.narrative.trim())
|
|
15962
|
+
model = { ...model, narrative: s.narrative };
|
|
14873
15963
|
}
|
|
14874
|
-
} catch {
|
|
14875
15964
|
}
|
|
15965
|
+
const narrative = model.narrative;
|
|
15966
|
+
if (narrative)
|
|
15967
|
+
markdown = renderEntryPage(doku, "brainjar-doku/", narrative);
|
|
15968
|
+
} else if (opts.llm && page.polishable) {
|
|
14876
15969
|
try {
|
|
14877
15970
|
const { flow } = parseFlow(
|
|
14878
15971
|
await opts.llm.complete({
|
|
@@ -14912,7 +16005,123 @@ async function generateDokuInner(brainId, opts = {}) {
|
|
|
14912
16005
|
console.warn(`doku: feature flow failed (${page.slug}):`, e);
|
|
14913
16006
|
}
|
|
14914
16007
|
}
|
|
16008
|
+
try {
|
|
16009
|
+
const diagrams = model;
|
|
16010
|
+
const shared = endpointFacts.filter(
|
|
16011
|
+
(e) => e.slug !== page.slug && page.markdown.includes(e.path)
|
|
16012
|
+
);
|
|
16013
|
+
const sharedLines = shared.map(
|
|
16014
|
+
(e) => `- ${e.method} ${e.path}: ${e.summary}${e.auth ? ` Authentication: ${e.auth}` : ""} (page: ${e.slug}, "${e.title}")`
|
|
16015
|
+
);
|
|
16016
|
+
const ctx = {
|
|
16017
|
+
factSheet: [page.markdown, ...sharedLines].join("\n"),
|
|
16018
|
+
hasFlowDiagram: Boolean(diagrams.flowDiagram),
|
|
16019
|
+
hasFlow: Boolean(diagrams.flow)
|
|
16020
|
+
};
|
|
16021
|
+
const prompt = [
|
|
16022
|
+
"Categories (pick exactly one key):",
|
|
16023
|
+
...categories.length ? categories.map((c) => `- ${c.key}: ${c.name} (${c.description})`) : ["- general: General (everything)"],
|
|
16024
|
+
"",
|
|
16025
|
+
"Available diagrams (the only allowed flow block sources):",
|
|
16026
|
+
...ctx.hasFlowDiagram ? [
|
|
16027
|
+
`- flowDiagram: "${diagrams.flowDiagram.title}" (the feature's runtime flow, lanes and stations)`
|
|
16028
|
+
] : [],
|
|
16029
|
+
...ctx.hasFlow && !ctx.hasFlowDiagram ? ["- flow: the feature's runtime steps as a simple chain"] : [],
|
|
16030
|
+
...!ctx.hasFlowDiagram && !ctx.hasFlow ? ["- (none)"] : [],
|
|
16031
|
+
"",
|
|
16032
|
+
...sharedLines.length ? [
|
|
16033
|
+
"Documented elsewhere for the same endpoint (proven facts; reuse them, link the page):",
|
|
16034
|
+
...sharedLines,
|
|
16035
|
+
""
|
|
16036
|
+
] : [],
|
|
16037
|
+
"Fact sheet:",
|
|
16038
|
+
page.markdown
|
|
16039
|
+
].join("\n");
|
|
16040
|
+
const keys = categoryKeys.length ? categoryKeys : ["general"];
|
|
16041
|
+
let synth = parseDocPage(
|
|
16042
|
+
await opts.llm.complete({
|
|
16043
|
+
system: PAGE_SYSTEM,
|
|
16044
|
+
prompt,
|
|
16045
|
+
maxTokens: 7e3
|
|
16046
|
+
}),
|
|
16047
|
+
keys,
|
|
16048
|
+
ctx
|
|
16049
|
+
);
|
|
16050
|
+
if (!synth.page) {
|
|
16051
|
+
console.warn(
|
|
16052
|
+
`doku: page rejected (${page.slug}): ${synth.error}; retrying once`
|
|
16053
|
+
);
|
|
16054
|
+
synth = parseDocPage(
|
|
16055
|
+
await opts.llm.complete({
|
|
16056
|
+
system: PAGE_SYSTEM,
|
|
16057
|
+
prompt,
|
|
16058
|
+
maxTokens: 1e4
|
|
16059
|
+
}),
|
|
16060
|
+
keys,
|
|
16061
|
+
ctx
|
|
16062
|
+
);
|
|
16063
|
+
}
|
|
16064
|
+
if (synth.dropped.length)
|
|
16065
|
+
console.warn(
|
|
16066
|
+
`doku: blocks dropped (${page.slug}): ${synth.dropped.join("; ")}`
|
|
16067
|
+
);
|
|
16068
|
+
if (synth.page) {
|
|
16069
|
+
const title = pickDocTitle(
|
|
16070
|
+
synth.page.title,
|
|
16071
|
+
page.title,
|
|
16072
|
+
page.slug,
|
|
16073
|
+
takenTitle
|
|
16074
|
+
);
|
|
16075
|
+
const f = {
|
|
16076
|
+
...model,
|
|
16077
|
+
presentation: synth.page.presentation,
|
|
16078
|
+
docTitle: title,
|
|
16079
|
+
category: synth.page.category
|
|
16080
|
+
};
|
|
16081
|
+
f.body = pageBody(f);
|
|
16082
|
+
model = f;
|
|
16083
|
+
markdown = renderFeaturePage(f);
|
|
16084
|
+
page.title = title;
|
|
16085
|
+
icon = synth.page.icon;
|
|
16086
|
+
polished = true;
|
|
16087
|
+
ok2 = true;
|
|
16088
|
+
} else {
|
|
16089
|
+
console.warn(
|
|
16090
|
+
`doku: page rejected twice (${page.slug}): ${synth.error}`
|
|
16091
|
+
);
|
|
16092
|
+
}
|
|
16093
|
+
} catch (e) {
|
|
16094
|
+
console.warn(`doku: page synthesis failed (${page.slug}):`, e);
|
|
16095
|
+
}
|
|
16096
|
+
if (!ok2 && sameFacts && aiComplete("feature", stored)) {
|
|
16097
|
+
const s = stored;
|
|
16098
|
+
const fresh = model;
|
|
16099
|
+
const f = {
|
|
16100
|
+
...model,
|
|
16101
|
+
...s.presentation ? { presentation: s.presentation } : {},
|
|
16102
|
+
body: s.body ?? null,
|
|
16103
|
+
docTitle: s.docTitle ?? null,
|
|
16104
|
+
category: s.category ?? null,
|
|
16105
|
+
...!fresh.flow && s.flow ? { flow: s.flow } : {},
|
|
16106
|
+
...!fresh.flowDiagram && s.flowDiagram ? { flowDiagram: s.flowDiagram } : {}
|
|
16107
|
+
};
|
|
16108
|
+
model = f;
|
|
16109
|
+
markdown = renderFeaturePage(f);
|
|
16110
|
+
if (f.docTitle) page.title = f.docTitle;
|
|
16111
|
+
}
|
|
16112
|
+
if (!ok2) {
|
|
16113
|
+
const s = stored;
|
|
16114
|
+
const m = model;
|
|
16115
|
+
if (typeof m.category !== "string" && typeof s?.category === "string" && categoryKeys.includes(s.category))
|
|
16116
|
+
model = { ...model, category: s.category };
|
|
16117
|
+
}
|
|
14915
16118
|
}
|
|
16119
|
+
model = {
|
|
16120
|
+
...model,
|
|
16121
|
+
factHash,
|
|
16122
|
+
aiIncomplete: required ? !ok2 : !aiComplete(page.kind, model),
|
|
16123
|
+
aiAttempts: required && !ok2 ? (sameFacts ? state.aiAttempts : 0) + 1 : 0
|
|
16124
|
+
};
|
|
14916
16125
|
if (page.kind === "feature") {
|
|
14917
16126
|
const m = model;
|
|
14918
16127
|
const f = page.model;
|
|
@@ -14924,15 +16133,68 @@ async function generateDokuInner(brainId, opts = {}) {
|
|
|
14924
16133
|
category: typeof m.category === "string" ? m.category : null
|
|
14925
16134
|
});
|
|
14926
16135
|
}
|
|
14927
|
-
await
|
|
16136
|
+
await save({
|
|
14928
16137
|
slug: page.slug,
|
|
14929
16138
|
kind: page.kind,
|
|
14930
16139
|
title: page.title,
|
|
14931
16140
|
model,
|
|
14932
16141
|
markdown,
|
|
14933
|
-
sourceHash
|
|
16142
|
+
sourceHash,
|
|
16143
|
+
icon
|
|
14934
16144
|
});
|
|
14935
|
-
|
|
16145
|
+
}
|
|
16146
|
+
if (opts.llm && categoryKeys.length) {
|
|
16147
|
+
const candidates = featureInfo.filter(
|
|
16148
|
+
(f) => taxonomyChanged || opts.retaxonomy || !f.category || !categoryKeys.includes(f.category)
|
|
16149
|
+
);
|
|
16150
|
+
if (candidates.length) {
|
|
16151
|
+
try {
|
|
16152
|
+
const filed = parseRefile(
|
|
16153
|
+
await opts.llm.complete({
|
|
16154
|
+
system: REFILE_SYSTEM,
|
|
16155
|
+
prompt: [
|
|
16156
|
+
"Categories:",
|
|
16157
|
+
...categories.map(
|
|
16158
|
+
(c) => `- ${c.key}: ${c.name} (${c.description})`
|
|
16159
|
+
),
|
|
16160
|
+
"",
|
|
16161
|
+
"Pages:",
|
|
16162
|
+
...candidates.map(
|
|
16163
|
+
(f) => `- ${f.slug}: ${f.title}: ${firstSentence(f.what)}`
|
|
16164
|
+
)
|
|
16165
|
+
].join("\n"),
|
|
16166
|
+
maxTokens: Math.min(16e3, 300 + candidates.length * 60)
|
|
16167
|
+
}),
|
|
16168
|
+
candidates.map((f) => f.slug),
|
|
16169
|
+
categoryKeys
|
|
16170
|
+
);
|
|
16171
|
+
if (filed) {
|
|
16172
|
+
const unfiled = candidates.filter((f) => !filed[f.slug]).length;
|
|
16173
|
+
if (unfiled)
|
|
16174
|
+
console.warn(
|
|
16175
|
+
`doku: re-filing left ${unfiled} of ${candidates.length} pages as they were`
|
|
16176
|
+
);
|
|
16177
|
+
for (const f of candidates) {
|
|
16178
|
+
const key = filed[f.slug];
|
|
16179
|
+
if (!key || key === f.category) continue;
|
|
16180
|
+
const row = storedRows.get(f.slug);
|
|
16181
|
+
const prev = meta.get(f.slug);
|
|
16182
|
+
if (!row) continue;
|
|
16183
|
+
f.category = key;
|
|
16184
|
+
await save({
|
|
16185
|
+
slug: f.slug,
|
|
16186
|
+
kind: "feature",
|
|
16187
|
+
title: row.title,
|
|
16188
|
+
model: { ...row.model, category: key },
|
|
16189
|
+
markdown: row.markdown,
|
|
16190
|
+
sourceHash: prev?.source_hash ?? sha1(row.markdown)
|
|
16191
|
+
});
|
|
16192
|
+
}
|
|
16193
|
+
} else console.warn("doku: re-filing rejected (invalid reply)");
|
|
16194
|
+
} catch (e) {
|
|
16195
|
+
console.warn("doku: re-filing failed:", e);
|
|
16196
|
+
}
|
|
16197
|
+
}
|
|
14936
16198
|
}
|
|
14937
16199
|
for (const c of categories) {
|
|
14938
16200
|
const members = featureInfo.filter((f) => f.category === c.key).sort((a, b) => a.title.localeCompare(b.title)).map(({ slug: slug2, title, what, updated }) => ({
|
|
@@ -14952,11 +16214,18 @@ async function generateDokuInner(brainId, opts = {}) {
|
|
|
14952
16214
|
body: null,
|
|
14953
16215
|
members
|
|
14954
16216
|
};
|
|
14955
|
-
const
|
|
16217
|
+
const factHash = sha1(renderCategoryPage(catModel));
|
|
16218
|
+
const sourceHash = sha1(renderCategoryPage(catModel) + "c2");
|
|
14956
16219
|
const prev = meta.get(slug);
|
|
14957
|
-
const
|
|
14958
|
-
|
|
14959
|
-
|
|
16220
|
+
const stored = storedBySlug.get(slug);
|
|
16221
|
+
const state = aiState(stored);
|
|
16222
|
+
const unchanged = Boolean(
|
|
16223
|
+
prev && !prev.deleted && prev.source_hash === sourceHash
|
|
16224
|
+
);
|
|
16225
|
+
const retry = Boolean(opts.llm) && unchanged && !aiComplete("category", stored) && (state.aiAttempts < MAX_AI_ATTEMPTS || Boolean(opts.forceRetry));
|
|
16226
|
+
if (unchanged && !retry) continue;
|
|
16227
|
+
const sameFacts = stored !== void 0 && state.factHash === factHash;
|
|
16228
|
+
let ok2 = !opts.llm;
|
|
14960
16229
|
if (opts.llm) {
|
|
14961
16230
|
try {
|
|
14962
16231
|
const body = parseCategoryBody(
|
|
@@ -14971,28 +16240,37 @@ async function generateDokuInner(brainId, opts = {}) {
|
|
|
14971
16240
|
(m) => `- ${m.slug}: ${m.title}: ${firstSentence(m.what)}`
|
|
14972
16241
|
)
|
|
14973
16242
|
].join("\n"),
|
|
14974
|
-
//
|
|
14975
|
-
//
|
|
14976
|
-
maxTokens:
|
|
16243
|
+
// Structured markdown for a large area needs room; a clipped body would
|
|
16244
|
+
// lose its last sections.
|
|
16245
|
+
maxTokens: 4e3
|
|
14977
16246
|
}),
|
|
14978
16247
|
members.map((m) => m.slug)
|
|
14979
16248
|
);
|
|
14980
16249
|
if (body) {
|
|
14981
16250
|
catModel.body = body;
|
|
14982
16251
|
polished = true;
|
|
16252
|
+
ok2 = true;
|
|
14983
16253
|
}
|
|
14984
16254
|
} catch {
|
|
14985
16255
|
}
|
|
16256
|
+
if (!ok2 && sameFacts) {
|
|
16257
|
+
const s = stored;
|
|
16258
|
+
if (typeof s.body === "string" && s.body.trim()) catModel.body = s.body;
|
|
16259
|
+
}
|
|
14986
16260
|
}
|
|
14987
|
-
await
|
|
16261
|
+
await save({
|
|
14988
16262
|
slug,
|
|
14989
16263
|
kind: "category",
|
|
14990
16264
|
title: c.name,
|
|
14991
|
-
model:
|
|
16265
|
+
model: {
|
|
16266
|
+
...catModel,
|
|
16267
|
+
factHash,
|
|
16268
|
+
aiIncomplete: opts.llm ? !ok2 : !catModel.body,
|
|
16269
|
+
aiAttempts: opts.llm && !ok2 ? (sameFacts ? state.aiAttempts : 0) + 1 : 0
|
|
16270
|
+
},
|
|
14992
16271
|
markdown: renderCategoryPage(catModel),
|
|
14993
16272
|
sourceHash
|
|
14994
16273
|
});
|
|
14995
|
-
written++;
|
|
14996
16274
|
}
|
|
14997
16275
|
let deleted = 0;
|
|
14998
16276
|
for (const [slug, m] of meta) {
|
|
@@ -15061,7 +16339,7 @@ async function exportDoku(brainId, root) {
|
|
|
15061
16339
|
const sync = await writeDokuTree(root, files);
|
|
15062
16340
|
return { pages: files.length, ...sync };
|
|
15063
16341
|
}
|
|
15064
|
-
var DOKU_FILENAME, DOKU_DIR, MANIFEST, CHANGELOG_DAYS, sha1, exists, isModule, moduleName, isRepoPath, normPath, PAGE_SYSTEM, TAXONOMY_SYSTEM, CATEGORY_SYSTEM, ENTRY_NARRATIVE_SYSTEM, FLOW_SYSTEM, ARCH_SYSTEM, ARCH_FLOWS_SYSTEM, FEATURE_FLOW_SYSTEM, normTitle2, generateDoku;
|
|
16342
|
+
var DOKU_FILENAME, DOKU_DIR, MANIFEST, CHANGELOG_DAYS, sha1, exists, isModule, moduleName, isRepoPath, normPath, MAX_AI_ATTEMPTS, PAGE_SYSTEM, TAXONOMY_SYSTEM, REFILE_SYSTEM, CATEGORY_SYSTEM, ENTRY_NARRATIVE_SYSTEM, FLOW_SYSTEM, ARCH_SYSTEM, ARCH_FLOWS_SYSTEM, FEATURE_FLOW_SYSTEM, normTitle2, generateDoku;
|
|
15065
16343
|
var init_doku = __esm({
|
|
15066
16344
|
"packages/ingest/src/doku.ts"() {
|
|
15067
16345
|
"use strict";
|
|
@@ -15072,7 +16350,7 @@ var init_doku = __esm({
|
|
|
15072
16350
|
DOKU_DIR = "brainjar-doku";
|
|
15073
16351
|
MANIFEST = ".manifest.json";
|
|
15074
16352
|
CHANGELOG_DAYS = 60;
|
|
15075
|
-
sha1 = (s) =>
|
|
16353
|
+
sha1 = (s) => createHash3("sha1").update(s).digest("hex");
|
|
15076
16354
|
exists = (p) => stat(p).then(
|
|
15077
16355
|
() => true,
|
|
15078
16356
|
() => false
|
|
@@ -15081,41 +16359,57 @@ var init_doku = __esm({
|
|
|
15081
16359
|
moduleName = (o) => String(o.body.name ?? o.title);
|
|
15082
16360
|
isRepoPath = (name) => !/^([a-zA-Z]:[/\\]|[/\\])/.test(name) && !name.includes("node_modules") && !name.includes(".claude/");
|
|
15083
16361
|
normPath = (p) => p.replace(/\\/g, "/").replace(/^\.\//, "").toLowerCase();
|
|
16362
|
+
MAX_AI_ATTEMPTS = 3;
|
|
15084
16363
|
PAGE_SYSTEM = [
|
|
15085
16364
|
"You write the documentation page for ONE feature, from its fact sheet.",
|
|
15086
|
-
'Respond ONLY with a JSON object of exactly this shape: {"title": "...", "category": "...", "
|
|
16365
|
+
'Respond ONLY with a JSON object of exactly this shape: {"title": "...", "category": "...", "icon": "...", "summary": "...", "blocks": [...]}. No code fences, nothing outside the JSON.',
|
|
16366
|
+
'"icon" is exactly ONE emoji that a reader would associate with this feature (a lock for auth, a magnifying glass for search, a bell for notifications); never text.',
|
|
15087
16367
|
'"title" is a short human page title in product language (a name, not a sentence): never a file path, commit message, or code identifier.',
|
|
15088
16368
|
'"category" is EXACTLY one key from the category list given with the input; pick the best fit, never invent a key.',
|
|
15089
|
-
'"
|
|
15090
|
-
"
|
|
15091
|
-
"
|
|
15092
|
-
"
|
|
16369
|
+
'"summary" is 1-2 plain sentences (at most 240 characters) saying what the reader can DO or GET with this feature. It opens the page; it never replaces the description.',
|
|
16370
|
+
'"blocks" is the page content as an ordered list of content blocks; the app decides layout and colors. Each block is an object with "kind", one of:',
|
|
16371
|
+
'{"kind":"text","title":"...","markdown":"..."}: the explanation itself, in full prose: as many connected paragraphs as the subject needs, ### subheadings (never # or ##; "title" is the section heading), lists, inline code, links. The feature description LIVES in text blocks; several text blocks may alternate with other kinds.',
|
|
16372
|
+
'{"kind":"steps","title":"...","items":[{"title":"...","body":"..."}]}: only for a real sequence of actions a person performs (setup, usage, an operator workflow), never for numbering facts.',
|
|
16373
|
+
'{"kind":"table","title":"...","columns":["..."],"rows":[["..."]]}: settings, parameters, error cases, or differences where the columns enable a real comparison. Every row has exactly as many cells as there are columns. Never a table for a single sentence.',
|
|
16374
|
+
'{"kind":"endpoint","method":"GET","path":"/...","summary":"...","auth":"..."}: one concrete HTTP endpoint, ONLY when the fact sheet names that exact method and path; "auth" only when the fact sheet states the authentication. Never derive an endpoint from a file name or a feature title.',
|
|
16375
|
+
'{"kind":"code","title":"...","language":"bash","code":"...","illustrative":true}: a request, response, or command example. Illustrative examples use placeholders such as <API_TOKEN>, never real keys or private content, and never show behavior the fact sheet does not state.',
|
|
16376
|
+
'{"kind":"callout","tone":"note","text":"..."}: one short prerequisite, effect, or limit. "warning" only for a concrete consequence the reader must avoid; otherwise "note".',
|
|
16377
|
+
`{"kind":"flow","title":"...","source":"flowDiagram"}: places the feature's existing runtime diagram where it helps the explanation; allowed only when the input lists that diagram as available, at most once.`,
|
|
16378
|
+
"Composition: explanation and outcome before technical detail. A feature exposing an API documents what it is, why you need it, and each endpoint with its parameters, request and response shape, and error cases, all from the facts only. A user-facing feature explains what is shown and how to use it, with steps only for a real action sequence. A pipeline explains what goes in, what comes out, and when it runs. A configuration-heavy feature tabulates its settings and explains precedence and effects. A feature containing several of these covers each. Mix kinds freely; there is no fixed template and no fixed number of blocks.",
|
|
16379
|
+
"Length follows the subject: an extensive feature gets several text sections with subheadings, the relationships, prerequisites, special cases, and reasons explained, and a concrete example; a small feature stays short. Never break a coherent explanation into bullet points for layout reasons and never cut it short. Explain a term at its first use; state the concrete consequence for the reader.",
|
|
16380
|
+
"Keep every fact; invent nothing. Missing facts stay missing: leave optional blocks out, and when an indispensable piece of information is not available, say so in one plain sentence addressed to the reader (for example: The exact request path is not documented yet.), never mentioning a fact sheet, an input, or a model. Do not repeat the change history or the decision list (both are shown separately). No H1 heading (the title renders above).",
|
|
16381
|
+
"When the input lists facts documented elsewhere for the SAME endpoint or interface, use them: they are proven, so the endpoint may be shown here as well, and the other page may be linked with a short readable link text in the form [text](page:slug), copying the slug exactly. Never merge pages because their titles look alike.",
|
|
15093
16382
|
PROSE_STYLE
|
|
15094
16383
|
].join(" ");
|
|
15095
16384
|
TAXONOMY_SYSTEM = [
|
|
15096
16385
|
"You maintain the category structure of a software product's documentation.",
|
|
15097
16386
|
"You are given the product's feature list and the EXISTING categories (possibly empty).",
|
|
15098
16387
|
'Respond ONLY with a JSON object of exactly this shape: {"categories": [{"key": "...", "name": "...", "description": "..."}]}. No code fences, nothing outside the JSON.',
|
|
15099
|
-
"
|
|
16388
|
+
"The NUMBER of categories follows the product: one distinct area of the product is one category. A small product may need one or two; a large one may need more than eight. Never one category per feature, never a category for a single page when it belongs to an existing area, and never padding to reach a count. Product language a new product manager understands, never code module names.",
|
|
15100
16389
|
"STABILITY WINS: keep every existing category (same key, same name) that still fits; only add a category when several features fit nothing, only rename or drop on clear misfit.",
|
|
15101
16390
|
"Group low-level technical internals (schema files, build plumbing) into ONE category rather than spreading them.",
|
|
15102
|
-
'"key" is a short kebab-case identifier; "description" is
|
|
16391
|
+
'"key" is a short kebab-case identifier; "description" is ONE short plain sentence (at most 120 characters) saying what the area is for, written for a reader who does not know the product; the details belong to the category page, not here.',
|
|
15103
16392
|
PROSE_STYLE
|
|
15104
16393
|
].join(" ");
|
|
16394
|
+
REFILE_SYSTEM = [
|
|
16395
|
+
"You file documentation pages into the given categories of a software product.",
|
|
16396
|
+
'Respond ONLY with a JSON object of exactly this shape: {"pages": {"<slug>": "<category key>"}}. No code fences, nothing outside the JSON.',
|
|
16397
|
+
"Cover EVERY listed slug exactly once with exactly one key from the category list; choose by the page's subject as its title and summary describe it. Never invent keys or slugs."
|
|
16398
|
+
].join(" ");
|
|
15105
16399
|
CATEGORY_SYSTEM = [
|
|
15106
|
-
"You write the
|
|
15107
|
-
"You are given the category (name, description) and its member pages (slug, title, summary).",
|
|
15108
|
-
"Respond with
|
|
15109
|
-
"
|
|
15110
|
-
"Ground everything in the given summaries; invent nothing.",
|
|
16400
|
+
"You write the overview page for ONE documentation category: what this product area does, how its parts relate, and what a reader needs to know before opening a page.",
|
|
16401
|
+
"You are given the category (name, description) and its member pages (slug, title, summary). The complete member list is rendered separately below your text, so you never have to mention every page.",
|
|
16402
|
+
"Respond with structured markdown: an opening paragraph, then ## and ### headings with as many paragraphs as the subject needs, and lists only where they help. Explain the area's job, the relationships between its parts, prerequisites, limits, and terms at first use. Long is fine when the content needs it; never pad a small area.",
|
|
16403
|
+
"Link a page only where the link helps the reader at that point, with a short readable link text, exactly the form [text](page:slug), copying the slug character for character from the input. Never invent a slug and never turn a page title into a new page.",
|
|
16404
|
+
"No H1 heading (the category name renders above). Ground everything in the given summaries; invent nothing.",
|
|
15111
16405
|
PROSE_STYLE
|
|
15112
16406
|
].join(" ");
|
|
15113
16407
|
ENTRY_NARRATIVE_SYSTEM = [
|
|
15114
16408
|
"You write the opening description for a software project's documentation.",
|
|
15115
16409
|
"You are given the documentation entry page (the project's feature list) as markdown.",
|
|
15116
|
-
"Respond
|
|
16410
|
+
"Respond in markdown: first one short plain paragraph saying what the project IS and for whom, then ## sections a newcomer understands, covering in order: the problem it solves; what it DOES (the core loop in plain words); its SURFACES and integrations (the ways people and tools connect to it); and how the pieces fit together. Paragraphs carry the explanation; a list only where items are truly parallel.",
|
|
15117
16411
|
"Complete enough that the reader could present the product afterwards, without descending into single-feature detail.",
|
|
15118
|
-
"Ground every statement in the given features; invent nothing, no marketing superlatives, no
|
|
16412
|
+
"Ground every statement in the given features; invent nothing, no marketing superlatives, no H1 heading.",
|
|
15119
16413
|
PROSE_STYLE
|
|
15120
16414
|
].join(" ");
|
|
15121
16415
|
FLOW_SYSTEM = [
|
|
@@ -15137,7 +16431,8 @@ var init_doku = __esm({
|
|
|
15137
16431
|
].join(" ");
|
|
15138
16432
|
ARCH_FLOWS_SYSTEM = [
|
|
15139
16433
|
"You read one architecture overview of a software system (module groups with their features and dependencies, plus real file paths) and produce the system's main RUNTIME flows: how a request, session, or piece of data actually moves through the system.",
|
|
15140
|
-
'Respond ONLY with JSON of exactly this shape: {"flows": [{"slug": "...", "title": "...", "summary": "...", "lanes": [{"key": "...", "title": "..."}], "nodes": [{"id": "...", "lane": "...", "title": "...", "subtitle": "...", "kind": "...", "evidence": ["path"]}], "edges": [{"from": "...", "to": "...", "label": "...", "kind": "..."}]}]}.
|
|
16434
|
+
'Respond ONLY with JSON of exactly this shape: {"flows": [{"slug": "...", "title": "...", "summary": "...", "explanation": "...", "lanes": [{"key": "...", "title": "..."}], "nodes": [{"id": "...", "lane": "...", "title": "...", "subtitle": "...", "kind": "...", "evidence": ["path"]}], "edges": [{"from": "...", "to": "...", "label": "...", "kind": "..."}]}]}. Nothing outside the JSON.',
|
|
16435
|
+
'"explanation" walks a reader through THIS flow in markdown (2-5 short paragraphs, ### subheadings where they help, at most 3500 characters): what starts it, what each phase does, what comes out. Describe an order only where the edges show it; where paths run in parallel or depend on a gate, say so instead of inventing a sequence. Name the same stations as the nodes.',
|
|
15141
16436
|
"Produce 2 to 4 flows, one per MAIN runtime path a newcomer must understand (for example: how input becomes stored data, how a query becomes an answer, how an external agent connects, how background verification runs). Cover every major path the features support; do not stop at two when the system clearly has more.",
|
|
15142
16437
|
"lanes are 3-5 PHASES of the flow in order (intake, processing, storage, ...), titled in plain words. Never folder names.",
|
|
15143
16438
|
"nodes are the runtime components in those phases: 4-10 per flow. kind is exactly one of: actor (human or external agent), surface (UI or endpoint), logic (application work), policy (a gate: auth, quota, validation), action (an outward call such as an LLM or external API), data (a store). title at most 3 words; subtitle at most 3 words (under 24 characters).",
|
|
@@ -15272,10 +16567,12 @@ var init_verify_agent = __esm({
|
|
|
15272
16567
|
init_sdk();
|
|
15273
16568
|
init_src2();
|
|
15274
16569
|
init_src();
|
|
16570
|
+
init_allowance();
|
|
15275
16571
|
init_product_facts();
|
|
15276
16572
|
init_doku();
|
|
15277
16573
|
init_llm();
|
|
15278
16574
|
init_openai();
|
|
16575
|
+
init_provider_rate2();
|
|
15279
16576
|
init_routing();
|
|
15280
16577
|
VERIFY_SYSTEM = [
|
|
15281
16578
|
"You are BrainJar's verification agent. You read a software repository and record durable product knowledge, with one iron rule: prose is never truth, code is.",
|
|
@@ -15301,9 +16598,9 @@ var init_verify_agent = __esm({
|
|
|
15301
16598
|
}
|
|
15302
16599
|
});
|
|
15303
16600
|
|
|
15304
|
-
// packages/ingest/src/verify-
|
|
15305
|
-
var
|
|
15306
|
-
"packages/ingest/src/verify-
|
|
16601
|
+
// packages/ingest/src/verify-start.ts
|
|
16602
|
+
var init_verify_start = __esm({
|
|
16603
|
+
"packages/ingest/src/verify-start.ts"() {
|
|
15307
16604
|
"use strict";
|
|
15308
16605
|
init_src2();
|
|
15309
16606
|
init_src();
|
|
@@ -15312,14 +16609,49 @@ var init_verify_poll = __esm({
|
|
|
15312
16609
|
}
|
|
15313
16610
|
});
|
|
15314
16611
|
|
|
15315
|
-
// packages/ingest/src/verify-
|
|
15316
|
-
var
|
|
15317
|
-
"packages/ingest/src/verify-
|
|
16612
|
+
// packages/ingest/src/verify-poll.ts
|
|
16613
|
+
var init_verify_poll = __esm({
|
|
16614
|
+
"packages/ingest/src/verify-poll.ts"() {
|
|
15318
16615
|
"use strict";
|
|
15319
16616
|
init_src2();
|
|
15320
16617
|
init_src();
|
|
15321
16618
|
init_repo_reader();
|
|
15322
16619
|
init_verify_agent();
|
|
16620
|
+
init_verify_start();
|
|
16621
|
+
}
|
|
16622
|
+
});
|
|
16623
|
+
|
|
16624
|
+
// packages/ingest/src/scheduler.ts
|
|
16625
|
+
var init_scheduler = __esm({
|
|
16626
|
+
"packages/ingest/src/scheduler.ts"() {
|
|
16627
|
+
"use strict";
|
|
16628
|
+
init_src2();
|
|
16629
|
+
init_daily_log();
|
|
16630
|
+
init_summaries();
|
|
16631
|
+
}
|
|
16632
|
+
});
|
|
16633
|
+
|
|
16634
|
+
// packages/ingest/src/job-worker.ts
|
|
16635
|
+
var init_job_worker = __esm({
|
|
16636
|
+
"packages/ingest/src/job-worker.ts"() {
|
|
16637
|
+
"use strict";
|
|
16638
|
+
init_src2();
|
|
16639
|
+
init_deepscan();
|
|
16640
|
+
init_doku();
|
|
16641
|
+
init_scheduler();
|
|
16642
|
+
init_summaries();
|
|
16643
|
+
init_verify_poll();
|
|
16644
|
+
init_routing();
|
|
16645
|
+
init_allowance();
|
|
16646
|
+
init_verify_start();
|
|
16647
|
+
}
|
|
16648
|
+
});
|
|
16649
|
+
|
|
16650
|
+
// packages/ingest/src/job-clock.ts
|
|
16651
|
+
var init_job_clock = __esm({
|
|
16652
|
+
"packages/ingest/src/job-clock.ts"() {
|
|
16653
|
+
"use strict";
|
|
16654
|
+
init_src2();
|
|
15323
16655
|
}
|
|
15324
16656
|
});
|
|
15325
16657
|
|
|
@@ -15330,18 +16662,10 @@ var init_ask_agent_model = __esm({
|
|
|
15330
16662
|
init_sdk();
|
|
15331
16663
|
init_src2();
|
|
15332
16664
|
init_src();
|
|
16665
|
+
init_allowance();
|
|
15333
16666
|
init_llm();
|
|
15334
16667
|
init_openai();
|
|
15335
|
-
|
|
15336
|
-
});
|
|
15337
|
-
|
|
15338
|
-
// packages/ingest/src/scheduler.ts
|
|
15339
|
-
var init_scheduler = __esm({
|
|
15340
|
-
"packages/ingest/src/scheduler.ts"() {
|
|
15341
|
-
"use strict";
|
|
15342
|
-
init_src2();
|
|
15343
|
-
init_daily_log();
|
|
15344
|
-
init_summaries();
|
|
16668
|
+
init_provider_rate2();
|
|
15345
16669
|
}
|
|
15346
16670
|
});
|
|
15347
16671
|
|
|
@@ -15364,6 +16688,7 @@ var init_src3 = __esm({
|
|
|
15364
16688
|
init_llm();
|
|
15365
16689
|
init_openai();
|
|
15366
16690
|
init_routing();
|
|
16691
|
+
init_allowance();
|
|
15367
16692
|
init_rationale_extractor();
|
|
15368
16693
|
init_extraction();
|
|
15369
16694
|
init_entities();
|
|
@@ -15401,6 +16726,8 @@ var init_src3 = __esm({
|
|
|
15401
16726
|
init_verify_agent();
|
|
15402
16727
|
init_verify_poll();
|
|
15403
16728
|
init_verify_start();
|
|
16729
|
+
init_job_worker();
|
|
16730
|
+
init_job_clock();
|
|
15404
16731
|
init_ask_agent_model();
|
|
15405
16732
|
init_scheduler();
|
|
15406
16733
|
registerConnector(
|
|
@@ -15453,7 +16780,24 @@ async function captureApi(pathAndQuery, init2 = {}) {
|
|
|
15453
16780
|
}
|
|
15454
16781
|
return body;
|
|
15455
16782
|
}
|
|
15456
|
-
|
|
16783
|
+
async function uploadArtifacts(brain, sourceId, artifacts, cursor3) {
|
|
16784
|
+
let inserted = 0;
|
|
16785
|
+
for (let i = 0; i < artifacts.length; i += UPLOAD_PORTION) {
|
|
16786
|
+
const last = i + UPLOAD_PORTION >= artifacts.length;
|
|
16787
|
+
const r = await captureApi("/capture/artifacts", {
|
|
16788
|
+
method: "POST",
|
|
16789
|
+
body: {
|
|
16790
|
+
brain,
|
|
16791
|
+
sourceId,
|
|
16792
|
+
artifacts: artifacts.slice(i, i + UPLOAD_PORTION),
|
|
16793
|
+
cursor: last ? cursor3 : null
|
|
16794
|
+
}
|
|
16795
|
+
});
|
|
16796
|
+
inserted += r.inserted;
|
|
16797
|
+
}
|
|
16798
|
+
return inserted;
|
|
16799
|
+
}
|
|
16800
|
+
var TokenError, TOKEN_HELP, UPLOAD_PORTION;
|
|
15457
16801
|
var init_api_client = __esm({
|
|
15458
16802
|
"packages/cli/src/api-client.ts"() {
|
|
15459
16803
|
"use strict";
|
|
@@ -15461,6 +16805,7 @@ var init_api_client = __esm({
|
|
|
15461
16805
|
TokenError = class extends Error {
|
|
15462
16806
|
};
|
|
15463
16807
|
TOKEN_HELP = "BrainJar token revoked or invalid. Create a new token under Account in the web app, then run `npx brainjar init --brain <id> --token <token>` once in any project on this machine.";
|
|
16808
|
+
UPLOAD_PORTION = 2e3;
|
|
15464
16809
|
}
|
|
15465
16810
|
});
|
|
15466
16811
|
|
|
@@ -15693,7 +17038,7 @@ var init_debt = __esm({
|
|
|
15693
17038
|
});
|
|
15694
17039
|
|
|
15695
17040
|
// packages/cli/src/adapters/claude-code.ts
|
|
15696
|
-
import { createHash as
|
|
17041
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
15697
17042
|
function targetOf(input) {
|
|
15698
17043
|
const i = input ?? {};
|
|
15699
17044
|
for (const k of ["command", "file_path", "notebook_path"]) {
|
|
@@ -15738,7 +17083,7 @@ var init_claude_code = __esm({
|
|
|
15738
17083
|
"use strict";
|
|
15739
17084
|
init_src();
|
|
15740
17085
|
EDIT_TOOLS = /* @__PURE__ */ new Set(["Edit", "Write", "MultiEdit", "NotebookEdit"]);
|
|
15741
|
-
short2 = (s) =>
|
|
17086
|
+
short2 = (s) => createHash4("sha256").update(s).digest("hex").slice(0, 16);
|
|
15742
17087
|
claudeCode2 = {
|
|
15743
17088
|
id: "claude-code",
|
|
15744
17089
|
map(payload, now) {
|
|
@@ -15837,7 +17182,7 @@ var init_claude_code = __esm({
|
|
|
15837
17182
|
});
|
|
15838
17183
|
|
|
15839
17184
|
// packages/cli/src/adapters/cursor.ts
|
|
15840
|
-
import { createHash as
|
|
17185
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
15841
17186
|
function targetOf2(input) {
|
|
15842
17187
|
const i = input ?? {};
|
|
15843
17188
|
for (const k of ["command", "file_path", "path", "target_file"]) {
|
|
@@ -15858,7 +17203,7 @@ var init_cursor = __esm({
|
|
|
15858
17203
|
"packages/cli/src/adapters/cursor.ts"() {
|
|
15859
17204
|
"use strict";
|
|
15860
17205
|
init_src();
|
|
15861
|
-
short3 = (s) =>
|
|
17206
|
+
short3 = (s) => createHash5("sha256").update(s).digest("hex").slice(0, 16);
|
|
15862
17207
|
cursor2 = {
|
|
15863
17208
|
id: "cursor",
|
|
15864
17209
|
map(payload, now) {
|
|
@@ -15976,7 +17321,7 @@ var init_cursor = __esm({
|
|
|
15976
17321
|
});
|
|
15977
17322
|
|
|
15978
17323
|
// packages/cli/src/adapters/codex.ts
|
|
15979
|
-
import { createHash as
|
|
17324
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
15980
17325
|
function patchFiles(patch) {
|
|
15981
17326
|
const out = /* @__PURE__ */ new Set();
|
|
15982
17327
|
for (const m of patch.matchAll(
|
|
@@ -16012,7 +17357,7 @@ var init_codex = __esm({
|
|
|
16012
17357
|
"use strict";
|
|
16013
17358
|
init_src();
|
|
16014
17359
|
PATCH_TOOLS = /* @__PURE__ */ new Set(["apply_patch", "Edit", "Write"]);
|
|
16015
|
-
short4 = (s) =>
|
|
17360
|
+
short4 = (s) => createHash6("sha256").update(s).digest("hex").slice(0, 16);
|
|
16016
17361
|
codex2 = {
|
|
16017
17362
|
id: "codex",
|
|
16018
17363
|
map(payload, now) {
|
|
@@ -16109,7 +17454,7 @@ var init_codex = __esm({
|
|
|
16109
17454
|
});
|
|
16110
17455
|
|
|
16111
17456
|
// packages/cli/src/adapters/copilot.ts
|
|
16112
|
-
import { createHash as
|
|
17457
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
16113
17458
|
function targetOf3(args) {
|
|
16114
17459
|
if (!args || typeof args !== "object") return "";
|
|
16115
17460
|
const a = args;
|
|
@@ -16142,7 +17487,7 @@ var init_copilot = __esm({
|
|
|
16142
17487
|
"use strict";
|
|
16143
17488
|
init_src();
|
|
16144
17489
|
EDIT_TOOLS2 = /* @__PURE__ */ new Set(["edit", "create"]);
|
|
16145
|
-
short5 = (s) =>
|
|
17490
|
+
short5 = (s) => createHash7("sha256").update(s).digest("hex").slice(0, 16);
|
|
16146
17491
|
EVENT_ALIASES = {
|
|
16147
17492
|
SessionStart: "sessionStart",
|
|
16148
17493
|
SessionEnd: "sessionEnd",
|
|
@@ -16251,7 +17596,7 @@ var init_copilot = __esm({
|
|
|
16251
17596
|
});
|
|
16252
17597
|
|
|
16253
17598
|
// packages/cli/src/adapters/gemini.ts
|
|
16254
|
-
import { createHash as
|
|
17599
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
16255
17600
|
function targetOf4(input) {
|
|
16256
17601
|
const i = input ?? {};
|
|
16257
17602
|
for (const k of ["command", "file_path"]) {
|
|
@@ -16273,7 +17618,7 @@ var init_gemini = __esm({
|
|
|
16273
17618
|
"use strict";
|
|
16274
17619
|
init_src();
|
|
16275
17620
|
EDIT_TOOLS3 = /* @__PURE__ */ new Set(["write_file", "replace"]);
|
|
16276
|
-
short6 = (s) =>
|
|
17621
|
+
short6 = (s) => createHash8("sha256").update(s).digest("hex").slice(0, 16);
|
|
16277
17622
|
gemini2 = {
|
|
16278
17623
|
id: "gemini",
|
|
16279
17624
|
map(payload, now) {
|
|
@@ -16369,7 +17714,7 @@ var init_gemini = __esm({
|
|
|
16369
17714
|
});
|
|
16370
17715
|
|
|
16371
17716
|
// packages/cli/src/adapters/kiro.ts
|
|
16372
|
-
import { createHash as
|
|
17717
|
+
import { createHash as createHash9 } from "node:crypto";
|
|
16373
17718
|
function targetOf5(input) {
|
|
16374
17719
|
const i = input ?? {};
|
|
16375
17720
|
for (const k of ["command", "cmd", "path", "file_path", "filePath"]) {
|
|
@@ -16400,7 +17745,7 @@ var init_kiro = __esm({
|
|
|
16400
17745
|
"use strict";
|
|
16401
17746
|
init_src();
|
|
16402
17747
|
EDIT_TOOLS4 = /* @__PURE__ */ new Set(["fs_write", "write", "str_replace", "fs_append"]);
|
|
16403
|
-
short7 = (s) =>
|
|
17748
|
+
short7 = (s) => createHash9("sha256").update(s).digest("hex").slice(0, 16);
|
|
16404
17749
|
EVENTS5 = {
|
|
16405
17750
|
agentSpawn: "start",
|
|
16406
17751
|
sessionStart: "start",
|
|
@@ -16516,7 +17861,7 @@ var init_kiro = __esm({
|
|
|
16516
17861
|
});
|
|
16517
17862
|
|
|
16518
17863
|
// packages/cli/src/adapters/opencode.ts
|
|
16519
|
-
import { createHash as
|
|
17864
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
16520
17865
|
function commitShas7(text) {
|
|
16521
17866
|
const out = /* @__PURE__ */ new Set();
|
|
16522
17867
|
for (const m of (text ?? "").matchAll(/\[[^\]\n]* ([0-9a-f]{7,40})\]/g)) {
|
|
@@ -16530,7 +17875,7 @@ var init_opencode = __esm({
|
|
|
16530
17875
|
"use strict";
|
|
16531
17876
|
init_src();
|
|
16532
17877
|
EDIT_TOOLS5 = /* @__PURE__ */ new Set(["edit", "write"]);
|
|
16533
|
-
short8 = (s) =>
|
|
17878
|
+
short8 = (s) => createHash10("sha256").update(s).digest("hex").slice(0, 16);
|
|
16534
17879
|
opencode2 = {
|
|
16535
17880
|
id: "opencode",
|
|
16536
17881
|
map(payload, now) {
|
|
@@ -16630,7 +17975,7 @@ var init_opencode = __esm({
|
|
|
16630
17975
|
});
|
|
16631
17976
|
|
|
16632
17977
|
// packages/cli/src/adapters/cline.ts
|
|
16633
|
-
import { createHash as
|
|
17978
|
+
import { createHash as createHash11 } from "node:crypto";
|
|
16634
17979
|
function targetOf6(params) {
|
|
16635
17980
|
const i = params ?? {};
|
|
16636
17981
|
for (const k of ["command", "path"]) {
|
|
@@ -16646,9 +17991,9 @@ function commitShas8(text) {
|
|
|
16646
17991
|
return [...out];
|
|
16647
17992
|
}
|
|
16648
17993
|
function stamp2(ts, now) {
|
|
16649
|
-
const
|
|
16650
|
-
if (typeof
|
|
16651
|
-
return new Date(
|
|
17994
|
+
const n2 = typeof ts === "string" && /^\d+$/.test(ts) ? Number(ts) : ts;
|
|
17995
|
+
if (typeof n2 === "number" && Number.isFinite(n2))
|
|
17996
|
+
return new Date(n2).toISOString();
|
|
16652
17997
|
if (typeof ts === "string" && !Number.isNaN(Date.parse(ts)))
|
|
16653
17998
|
return new Date(ts).toISOString();
|
|
16654
17999
|
return now;
|
|
@@ -16662,7 +18007,7 @@ var init_cline = __esm({
|
|
|
16662
18007
|
"use strict";
|
|
16663
18008
|
init_src();
|
|
16664
18009
|
EDIT_TOOLS6 = /* @__PURE__ */ new Set(["write_to_file", "replace_in_file", "apply_patch"]);
|
|
16665
|
-
short9 = (s) =>
|
|
18010
|
+
short9 = (s) => createHash11("sha256").update(s).digest("hex").slice(0, 16);
|
|
16666
18011
|
CLINE_REPLY = JSON.stringify({ cancel: false });
|
|
16667
18012
|
cline2 = {
|
|
16668
18013
|
id: "cline",
|
|
@@ -16860,16 +18205,12 @@ async function linkForSpool(file) {
|
|
|
16860
18205
|
async function shipSpool(file, link) {
|
|
16861
18206
|
const events = readSpool(file);
|
|
16862
18207
|
if (!events.length) return;
|
|
16863
|
-
|
|
18208
|
+
await captureApi("/capture/events", {
|
|
16864
18209
|
method: "POST",
|
|
16865
|
-
body: {
|
|
16866
|
-
brain: link.brainId,
|
|
16867
|
-
events,
|
|
16868
|
-
author: loadEnv().BRAINJAR_AUTHOR?.trim() || null
|
|
16869
|
-
}
|
|
18210
|
+
body: { brain: link.brainId, events }
|
|
16870
18211
|
});
|
|
16871
18212
|
console.error(
|
|
16872
|
-
`brainjar: session
|
|
18213
|
+
`brainjar: session sent (${events.length} events), the server reads it in the background`
|
|
16873
18214
|
);
|
|
16874
18215
|
}
|
|
16875
18216
|
function parseHookArgs(argv) {
|
|
@@ -16968,23 +18309,16 @@ async function syncRepo(root) {
|
|
|
16968
18309
|
const connector = createConnector("git_local", { cwd: root }, {});
|
|
16969
18310
|
const artifacts = await connector.sync(project.cursor ?? void 0);
|
|
16970
18311
|
if (artifacts.length > 0) {
|
|
16971
|
-
const
|
|
16972
|
-
|
|
16973
|
-
|
|
16974
|
-
|
|
16975
|
-
|
|
16976
|
-
brain: link.brainId,
|
|
16977
|
-
sourceId: project.sourceId,
|
|
16978
|
-
artifacts,
|
|
16979
|
-
cursor: maxUpdatedAt(artifacts) ?? project.cursor
|
|
16980
|
-
}
|
|
16981
|
-
}
|
|
18312
|
+
const inserted = await uploadArtifacts(
|
|
18313
|
+
link.brainId,
|
|
18314
|
+
project.sourceId,
|
|
18315
|
+
artifacts,
|
|
18316
|
+
maxUpdatedAt(artifacts) ?? project.cursor
|
|
16982
18317
|
);
|
|
16983
|
-
if (
|
|
16984
|
-
console.error(
|
|
16985
|
-
|
|
16986
|
-
|
|
16987
|
-
}
|
|
18318
|
+
if (inserted > 0) {
|
|
18319
|
+
console.error(
|
|
18320
|
+
`brainjar: ${inserted} new commits sent, the server reads them in the background`
|
|
18321
|
+
);
|
|
16988
18322
|
const graph = await scanStructure(root);
|
|
16989
18323
|
await captureApi("/capture/structure", {
|
|
16990
18324
|
method: "POST",
|
|
@@ -17041,12 +18375,67 @@ var init_sync2 = __esm({
|
|
|
17041
18375
|
}
|
|
17042
18376
|
});
|
|
17043
18377
|
|
|
18378
|
+
// packages/cli/src/wait.ts
|
|
18379
|
+
function progressLine(p) {
|
|
18380
|
+
const left = p.pending > 0 ? `${n(p.pending)} commits and sessions still to read` : `${n(p.queued + p.running)} capture jobs still running`;
|
|
18381
|
+
const queue = p.ahead > 0 ? ` (${n(p.ahead)} jobs of other projects ahead)` : "";
|
|
18382
|
+
return ` \u2026 ${left}${queue}`;
|
|
18383
|
+
}
|
|
18384
|
+
async function waitForServer(brainId, since, opts = {}) {
|
|
18385
|
+
const poll = opts.poll ?? (() => captureApi(
|
|
18386
|
+
`/capture/progress?brain=${brainId}&since=${encodeURIComponent(since.toISOString())}`
|
|
18387
|
+
));
|
|
18388
|
+
const sleep3 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
18389
|
+
const write = opts.write ?? ((s) => void process.stdout.write(s));
|
|
18390
|
+
const tty = opts.tty ?? Boolean(process.stdout.isTTY);
|
|
18391
|
+
const intervalMs = opts.intervalMs ?? 2e3;
|
|
18392
|
+
let shown = "";
|
|
18393
|
+
let shownAt = 0;
|
|
18394
|
+
let misses = 0;
|
|
18395
|
+
for (; ; ) {
|
|
18396
|
+
let p;
|
|
18397
|
+
try {
|
|
18398
|
+
p = await poll();
|
|
18399
|
+
misses = 0;
|
|
18400
|
+
} catch {
|
|
18401
|
+
if (++misses * intervalMs >= 6e4) {
|
|
18402
|
+
if (tty && shown) write("\n");
|
|
18403
|
+
return null;
|
|
18404
|
+
}
|
|
18405
|
+
await sleep3(intervalMs);
|
|
18406
|
+
continue;
|
|
18407
|
+
}
|
|
18408
|
+
if (p.queued + p.running === 0) {
|
|
18409
|
+
if (tty && shown) write("\r\x1B[K");
|
|
18410
|
+
return p;
|
|
18411
|
+
}
|
|
18412
|
+
const line = progressLine(p);
|
|
18413
|
+
if (tty) write(`\r\x1B[K${line}`);
|
|
18414
|
+
else if (line !== shown && Date.now() - shownAt >= 15e3) {
|
|
18415
|
+
write(`${line}
|
|
18416
|
+
`);
|
|
18417
|
+
shownAt = Date.now();
|
|
18418
|
+
}
|
|
18419
|
+
shown = line;
|
|
18420
|
+
await sleep3(intervalMs);
|
|
18421
|
+
}
|
|
18422
|
+
}
|
|
18423
|
+
var n;
|
|
18424
|
+
var init_wait = __esm({
|
|
18425
|
+
"packages/cli/src/wait.ts"() {
|
|
18426
|
+
"use strict";
|
|
18427
|
+
init_api_client();
|
|
18428
|
+
n = (x) => x.toLocaleString("en-US");
|
|
18429
|
+
}
|
|
18430
|
+
});
|
|
18431
|
+
|
|
17044
18432
|
// packages/cli/src/init.ts
|
|
17045
18433
|
var init_exports = {};
|
|
17046
18434
|
__export(init_exports, {
|
|
17047
18435
|
init: () => init,
|
|
17048
18436
|
initMain: () => initMain
|
|
17049
18437
|
});
|
|
18438
|
+
import { hostname } from "node:os";
|
|
17050
18439
|
import { readFile as readFile12 } from "node:fs/promises";
|
|
17051
18440
|
async function init(argv) {
|
|
17052
18441
|
const skipTranscripts = argv.includes("--no-transcripts");
|
|
@@ -17056,6 +18445,7 @@ async function init(argv) {
|
|
|
17056
18445
|
const tokenFlag = flag("--token");
|
|
17057
18446
|
const root = await repoRoot();
|
|
17058
18447
|
const name = repoName(root);
|
|
18448
|
+
const startedAt = new Date(Date.now() - 6e4);
|
|
17059
18449
|
console.log(`
|
|
17060
18450
|
\u{1F9E0} BrainJar init \u2014 ${name}
|
|
17061
18451
|
repo: ${root}`);
|
|
@@ -17083,6 +18473,7 @@ async function init(argv) {
|
|
|
17083
18473
|
method: "POST",
|
|
17084
18474
|
body: {
|
|
17085
18475
|
name,
|
|
18476
|
+
machine: hostname(),
|
|
17086
18477
|
...brainFlag ? { brainId: brainFlag } : reuse ? { brainId: reuse.brainId } : {},
|
|
17087
18478
|
...reuse ? { sourceId: reuse.sourceId } : {},
|
|
17088
18479
|
...remote ? { remote } : {}
|
|
@@ -17104,61 +18495,45 @@ async function init(argv) {
|
|
|
17104
18495
|
}
|
|
17105
18496
|
step(3, "Scanning code + ingesting git history\u2026");
|
|
17106
18497
|
const graph = await scanStructure(root);
|
|
17107
|
-
|
|
17108
|
-
"
|
|
17109
|
-
|
|
17110
|
-
);
|
|
18498
|
+
await captureApi("/capture/structure", {
|
|
18499
|
+
method: "POST",
|
|
18500
|
+
body: { brain: conn.brainId, graph }
|
|
18501
|
+
});
|
|
17111
18502
|
const connector = createConnector("git_local", { cwd: root }, {});
|
|
17112
18503
|
const artifacts = await connector.sync(conn.cursor ?? void 0);
|
|
17113
18504
|
let commits = 0;
|
|
17114
|
-
let objects = 0;
|
|
17115
18505
|
if (artifacts.length > 0) {
|
|
17116
|
-
|
|
17117
|
-
|
|
17118
|
-
|
|
17119
|
-
|
|
17120
|
-
|
|
17121
|
-
brain: conn.brainId,
|
|
17122
|
-
sourceId: conn.sourceId,
|
|
17123
|
-
artifacts,
|
|
17124
|
-
cursor: maxUpdatedAt(artifacts) ?? conn.cursor
|
|
17125
|
-
}
|
|
17126
|
-
}
|
|
18506
|
+
commits = await uploadArtifacts(
|
|
18507
|
+
conn.brainId,
|
|
18508
|
+
conn.sourceId,
|
|
18509
|
+
artifacts,
|
|
18510
|
+
maxUpdatedAt(artifacts) ?? conn.cursor
|
|
17127
18511
|
);
|
|
17128
|
-
commits = r.inserted;
|
|
17129
|
-
objects = r.objects;
|
|
17130
18512
|
}
|
|
17131
18513
|
ok(
|
|
17132
|
-
`${
|
|
18514
|
+
`${graph.modules.length} modules, ${commits} new commits sent to the server`
|
|
17133
18515
|
);
|
|
17134
18516
|
step(4, "Backfilling past agent sessions\u2026");
|
|
17135
18517
|
if (skipTranscripts) {
|
|
17136
18518
|
ok("skipped (--no-transcripts)");
|
|
17137
18519
|
} else {
|
|
17138
|
-
const author = loadEnv().BRAINJAR_AUTHOR?.trim() || null;
|
|
17139
18520
|
const files = await transcriptFiles(root);
|
|
17140
|
-
let prompts = 0;
|
|
17141
|
-
let failures = 0;
|
|
17142
|
-
let consolidated = 0;
|
|
17143
18521
|
let truncatedCount = 0;
|
|
17144
18522
|
let skipped = 0;
|
|
17145
18523
|
for (const f of files) {
|
|
17146
18524
|
try {
|
|
17147
18525
|
const { text, truncated } = capTranscript(await readFile12(f, "utf8"));
|
|
17148
18526
|
if (truncated) truncatedCount++;
|
|
17149
|
-
|
|
18527
|
+
await captureApi("/capture/transcript", {
|
|
17150
18528
|
method: "POST",
|
|
17151
|
-
body: { brain: conn.brainId, text,
|
|
18529
|
+
body: { brain: conn.brainId, text, backfill: true }
|
|
17152
18530
|
});
|
|
17153
|
-
prompts += r.prompts;
|
|
17154
|
-
failures += r.failures;
|
|
17155
|
-
consolidated += r.consolidated;
|
|
17156
18531
|
} catch {
|
|
17157
18532
|
skipped++;
|
|
17158
18533
|
}
|
|
17159
18534
|
}
|
|
17160
18535
|
ok(
|
|
17161
|
-
`${files.length - skipped} sessions
|
|
18536
|
+
`${files.length - skipped} sessions sent to the server` + (truncatedCount ? ` (${truncatedCount} oversized, newest 40MB kept)` : "") + (skipped ? `, ${skipped} skipped on errors` : "")
|
|
17162
18537
|
);
|
|
17163
18538
|
}
|
|
17164
18539
|
step(5, "Semantic search\u2026");
|
|
@@ -17208,6 +18583,15 @@ async function init(argv) {
|
|
|
17208
18583
|
} catch {
|
|
17209
18584
|
ok("skipped (could not write the pointer)");
|
|
17210
18585
|
}
|
|
18586
|
+
step(8, "Filling the Brain on the server\u2026");
|
|
18587
|
+
console.log(" Stopping this (Ctrl+C) is safe: the server keeps working.");
|
|
18588
|
+
const filled = await waitForServer(conn.brainId, startedAt);
|
|
18589
|
+
if (!filled) ok("lost the connection to the server; it keeps working");
|
|
18590
|
+
else if (filled.failed > 0)
|
|
18591
|
+
ok(
|
|
18592
|
+
`done; ${filled.failed} capture jobs failed for good (see the bell in the web app)`
|
|
18593
|
+
);
|
|
18594
|
+
else ok("done");
|
|
17211
18595
|
try {
|
|
17212
18596
|
await captureApi("/capture/connect", {
|
|
17213
18597
|
method: "POST",
|
|
@@ -17215,6 +18599,7 @@ async function init(argv) {
|
|
|
17215
18599
|
brainId: conn.brainId,
|
|
17216
18600
|
sourceId: conn.sourceId,
|
|
17217
18601
|
name,
|
|
18602
|
+
machine: hostname(),
|
|
17218
18603
|
done: true,
|
|
17219
18604
|
tools: tools.map((t) => t.id)
|
|
17220
18605
|
}
|
|
@@ -17254,8 +18639,9 @@ var init_init = __esm({
|
|
|
17254
18639
|
init_src();
|
|
17255
18640
|
init_api_client();
|
|
17256
18641
|
init_sync2();
|
|
17257
|
-
|
|
17258
|
-
|
|
18642
|
+
init_wait();
|
|
18643
|
+
step = (n2, msg) => console.log(`
|
|
18644
|
+
[${n2}/8] ${msg}`);
|
|
17259
18645
|
ok = (msg) => console.log(` \u2713 ${msg}`);
|
|
17260
18646
|
}
|
|
17261
18647
|
});
|
|
@@ -17528,13 +18914,13 @@ function selectDeepScanFiles(candidates, graph, maxFiles = MAX_FILES) {
|
|
|
17528
18914
|
}
|
|
17529
18915
|
};
|
|
17530
18916
|
for (const bucket of BUCKET_CAPS) {
|
|
17531
|
-
let
|
|
18917
|
+
let n2 = 0;
|
|
17532
18918
|
const ordered = [...candidates].sort().reverse();
|
|
17533
18919
|
for (const p of ordered) {
|
|
17534
|
-
if (
|
|
18920
|
+
if (n2 >= bucket.cap) break;
|
|
17535
18921
|
if (bucket.test(p) && !has.has(p)) {
|
|
17536
18922
|
take(p);
|
|
17537
|
-
|
|
18923
|
+
n2++;
|
|
17538
18924
|
}
|
|
17539
18925
|
}
|
|
17540
18926
|
}
|