brainjar 0.3.745 → 0.3.975
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 +1432 -332
- 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.975";
|
|
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() {
|
|
@@ -5851,6 +6039,8 @@ function validateFlowDiagram(d) {
|
|
|
5851
6039
|
if (!short(f.title, 120)) errs.push("title: missing or too long");
|
|
5852
6040
|
if (f.summary !== void 0 && !short(f.summary, 300))
|
|
5853
6041
|
errs.push("summary: empty or too long");
|
|
6042
|
+
if (f.explanation !== void 0 && !short(f.explanation, 4e3))
|
|
6043
|
+
errs.push("explanation: empty or too long");
|
|
5854
6044
|
const lanes = Array.isArray(f.lanes) ? f.lanes : [];
|
|
5855
6045
|
if (lanes.length < 2 || lanes.length > 6)
|
|
5856
6046
|
errs.push("lanes: need 2-6 phase bands");
|
|
@@ -5863,22 +6053,31 @@ function validateFlowDiagram(d) {
|
|
|
5863
6053
|
} else laneKeys.add(l.key);
|
|
5864
6054
|
}
|
|
5865
6055
|
const nodes = Array.isArray(f.nodes) ? f.nodes : [];
|
|
5866
|
-
if (nodes.length < 3 || nodes.length >
|
|
5867
|
-
errs.push("nodes: need 3-
|
|
6056
|
+
if (nodes.length < 3 || nodes.length > 18)
|
|
6057
|
+
errs.push("nodes: need 3-18 nodes");
|
|
5868
6058
|
const ids = /* @__PURE__ */ new Set();
|
|
5869
|
-
for (const
|
|
5870
|
-
if (!short(
|
|
6059
|
+
for (const n2 of nodes) {
|
|
6060
|
+
if (!short(n2?.id, 40) || !short(n2?.title, 60)) {
|
|
5871
6061
|
errs.push("nodes: each needs id and title");
|
|
5872
6062
|
continue;
|
|
5873
6063
|
}
|
|
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: ${
|
|
6064
|
+
if (ids.has(n2.id)) errs.push(`nodes: duplicate id ${n2.id}`);
|
|
6065
|
+
ids.add(n2.id);
|
|
6066
|
+
if (!laneKeys.has(n2.lane ?? "")) errs.push(`nodes: ${n2.id} unknown lane`);
|
|
6067
|
+
if (!NODE_KINDS.has(n2.kind ?? "")) errs.push(`nodes: ${n2.id} unknown kind`);
|
|
6068
|
+
if (n2.subtitle !== void 0 && !short(n2.subtitle, 60))
|
|
6069
|
+
errs.push(`nodes: ${n2.id} oversized subtitle`);
|
|
6070
|
+
if (n2.evidence !== void 0 && !Array.isArray(n2.evidence))
|
|
6071
|
+
errs.push(`nodes: ${n2.id} evidence must be a list`);
|
|
6072
|
+
if (Array.isArray(n2.evidence)) {
|
|
6073
|
+
if (n2.evidence.length > 12)
|
|
6074
|
+
errs.push(`nodes: ${n2.id} too many evidence entries`);
|
|
6075
|
+
for (const ev of n2.evidence)
|
|
6076
|
+
if (!short(ev, 200) || /[-]/.test(ev))
|
|
6077
|
+
errs.push(`nodes: ${n2.id} evidence entry is not a short string`);
|
|
6078
|
+
}
|
|
6079
|
+
if (n2.description !== void 0 && !short(n2.description, 600))
|
|
6080
|
+
errs.push(`nodes: ${n2.id} description empty or too long`);
|
|
5882
6081
|
}
|
|
5883
6082
|
const edges = Array.isArray(f.edges) ? f.edges : [];
|
|
5884
6083
|
if (edges.length < 2 || edges.length > 24)
|
|
@@ -5889,7 +6088,7 @@ function validateFlowDiagram(d) {
|
|
|
5889
6088
|
continue;
|
|
5890
6089
|
}
|
|
5891
6090
|
if (e.from === e.to) errs.push("edges: self-loop");
|
|
5892
|
-
if (e.label !== void 0 && !short(e.label,
|
|
6091
|
+
if (e.label !== void 0 && !short(e.label, 60))
|
|
5893
6092
|
errs.push("edges: oversized label");
|
|
5894
6093
|
if (e.kind !== void 0 && !EDGE_KINDS.has(e.kind))
|
|
5895
6094
|
errs.push("edges: unknown kind");
|
|
@@ -5957,7 +6156,55 @@ var init_capture_events = __esm({
|
|
|
5957
6156
|
tool_name: 200,
|
|
5958
6157
|
reason: 200
|
|
5959
6158
|
};
|
|
5960
|
-
cut = (s,
|
|
6159
|
+
cut = (s, n2) => s === void 0 ? void 0 : s.length > n2 ? s.slice(0, n2) : s;
|
|
6160
|
+
}
|
|
6161
|
+
});
|
|
6162
|
+
|
|
6163
|
+
// packages/shared/src/emoji.ts
|
|
6164
|
+
function isEmoji(s) {
|
|
6165
|
+
return ONE_EMOJI.test(s);
|
|
6166
|
+
}
|
|
6167
|
+
var ONE_EMOJI;
|
|
6168
|
+
var init_emoji = __esm({
|
|
6169
|
+
"packages/shared/src/emoji.ts"() {
|
|
6170
|
+
"use strict";
|
|
6171
|
+
ONE_EMOJI = new RegExp("^\\p{RGI_Emoji}$", "v");
|
|
6172
|
+
}
|
|
6173
|
+
});
|
|
6174
|
+
|
|
6175
|
+
// packages/shared/src/password.ts
|
|
6176
|
+
var init_password = __esm({
|
|
6177
|
+
"packages/shared/src/password.ts"() {
|
|
6178
|
+
"use strict";
|
|
6179
|
+
}
|
|
6180
|
+
});
|
|
6181
|
+
|
|
6182
|
+
// packages/shared/src/totp.ts
|
|
6183
|
+
var init_totp = __esm({
|
|
6184
|
+
"packages/shared/src/totp.ts"() {
|
|
6185
|
+
"use strict";
|
|
6186
|
+
}
|
|
6187
|
+
});
|
|
6188
|
+
|
|
6189
|
+
// packages/shared/src/brand.ts
|
|
6190
|
+
var BRAND_MARK_PATH, BRAND_ICON_SVG;
|
|
6191
|
+
var init_brand = __esm({
|
|
6192
|
+
"packages/shared/src/brand.ts"() {
|
|
6193
|
+
"use strict";
|
|
6194
|
+
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";
|
|
6195
|
+
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>';
|
|
6196
|
+
}
|
|
6197
|
+
});
|
|
6198
|
+
|
|
6199
|
+
// packages/shared/src/summary-report.ts
|
|
6200
|
+
var CONTROL;
|
|
6201
|
+
var init_summary_report = __esm({
|
|
6202
|
+
"packages/shared/src/summary-report.ts"() {
|
|
6203
|
+
"use strict";
|
|
6204
|
+
CONTROL = new RegExp(
|
|
6205
|
+
"[\\u0000-\\u0008\\u000b\\u000c\\u000e-\\u001f\\u007f]",
|
|
6206
|
+
"g"
|
|
6207
|
+
);
|
|
5961
6208
|
}
|
|
5962
6209
|
});
|
|
5963
6210
|
|
|
@@ -5973,6 +6220,11 @@ var init_src = __esm({
|
|
|
5973
6220
|
init_sanitize();
|
|
5974
6221
|
init_flow();
|
|
5975
6222
|
init_capture_events();
|
|
6223
|
+
init_emoji();
|
|
6224
|
+
init_password();
|
|
6225
|
+
init_totp();
|
|
6226
|
+
init_brand();
|
|
6227
|
+
init_summary_report();
|
|
5976
6228
|
PROSE_STYLE = "Never use em dashes (\u2014) or en dashes (\u2013) anywhere; use commas, colons, periods, or parentheses instead.";
|
|
5977
6229
|
}
|
|
5978
6230
|
});
|
|
@@ -6492,8 +6744,8 @@ var init_repository = __esm({
|
|
|
6492
6744
|
const key = authorKey.trim().toLowerCase();
|
|
6493
6745
|
if (!key) return [];
|
|
6494
6746
|
return (await this.pool.query(
|
|
6495
|
-
`SELECT ra.id, ra.kind, ra.external_id, ra.title, ra.uri,
|
|
6496
|
-
left(ra.body, 160) AS excerpt, ra.src_created_at AS at,
|
|
6747
|
+
`SELECT ra.id, ra.kind, ra.external_id, coalesce(ra.title, '') AS title, ra.uri,
|
|
6748
|
+
coalesce(left(ra.body, 160), '') AS excerpt, ra.src_created_at AS at,
|
|
6497
6749
|
(SELECT el.memory_object_id FROM evidence_link el
|
|
6498
6750
|
JOIN memory_object m ON m.id = el.memory_object_id
|
|
6499
6751
|
WHERE el.raw_artifact_id = ra.id AND m.brain_id = $1
|
|
@@ -6538,12 +6790,12 @@ var init_repository = __esm({
|
|
|
6538
6790
|
* first (five logs cover a week completely, where a newest-N slice of raw objects sees only
|
|
6539
6791
|
* the last hours of a busy range).
|
|
6540
6792
|
*/
|
|
6541
|
-
async dailyLogsInRange(startIso, endIso, limit = 50) {
|
|
6793
|
+
async dailyLogsInRange(startIso, endIso, limit = 50, order = "desc") {
|
|
6542
6794
|
return (await this.pool.query(
|
|
6543
6795
|
`SELECT ${MO_COLUMNS}, ${EV_AGG} FROM memory_object
|
|
6544
6796
|
WHERE brain_id = $1 AND type = 'daily_log'
|
|
6545
6797
|
AND occurred_at >= $2::timestamptz AND occurred_at < $3::timestamptz
|
|
6546
|
-
ORDER BY occurred_at DESC LIMIT $4`,
|
|
6798
|
+
ORDER BY occurred_at ${order === "asc" ? "ASC" : "DESC"} LIMIT $4`,
|
|
6547
6799
|
[this.brainId, startIso, endIso, limit]
|
|
6548
6800
|
)).rows;
|
|
6549
6801
|
}
|
|
@@ -6729,6 +6981,20 @@ var init_repository = __esm({
|
|
|
6729
6981
|
return r.rows;
|
|
6730
6982
|
}
|
|
6731
6983
|
// --- Overview / summaries ---
|
|
6984
|
+
/** Compact freshness inputs for the whole Brain, without loading bodies or N+1 queries. */
|
|
6985
|
+
async memoryFreshnessRows() {
|
|
6986
|
+
return (await this.pool.query(
|
|
6987
|
+
`SELECT m.id, m.type, m.title, m.updated_at AS "updatedAt",
|
|
6988
|
+
m.last_verified_at AS "lastVerifiedAt", m.status,
|
|
6989
|
+
EXISTS (SELECT 1 FROM superseded_object s WHERE s.object_id = m.id) AS superseded,
|
|
6990
|
+
EXISTS (SELECT 1 FROM evidence_link el
|
|
6991
|
+
JOIN raw_artifact ra ON ra.id = el.raw_artifact_id
|
|
6992
|
+
WHERE el.memory_object_id = m.id AND ra.last_seen_at > m.updated_at) AS "evidenceChanged"
|
|
6993
|
+
FROM memory_object m WHERE m.brain_id = $1
|
|
6994
|
+
ORDER BY m.updated_at DESC, m.id`,
|
|
6995
|
+
[this.brainId]
|
|
6996
|
+
)).rows;
|
|
6997
|
+
}
|
|
6732
6998
|
/** Memory-object counts for this Brain, grouped by type and confidence (dashboard overview). */
|
|
6733
6999
|
async counts() {
|
|
6734
7000
|
const rows = (await this.pool.query(
|
|
@@ -6777,7 +7043,8 @@ var init_repository = __esm({
|
|
|
6777
7043
|
return (await this.pool.query(
|
|
6778
7044
|
`SELECT * FROM summary WHERE brain_id = $1
|
|
6779
7045
|
AND ($3 OR created_by IS NULL OR created_by = $2)
|
|
6780
|
-
ORDER BY
|
|
7046
|
+
ORDER BY period_start DESC NULLS LAST,
|
|
7047
|
+
COALESCE(period_end, period_start) DESC, created_at DESC`,
|
|
6781
7048
|
[this.brainId, viewerId ?? null, viewerId === void 0]
|
|
6782
7049
|
)).rows;
|
|
6783
7050
|
}
|
|
@@ -6797,8 +7064,8 @@ var init_repository = __esm({
|
|
|
6797
7064
|
if (!isConfidence(confidence))
|
|
6798
7065
|
throw new Error(`invalid confidence: ${confidence}`);
|
|
6799
7066
|
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 *`,
|
|
7067
|
+
`INSERT INTO summary (brain_id, timescale, title, body, period_start, period_end, confidence, created_by, chronicle, data_through)
|
|
7068
|
+
VALUES ($1,$2,$3,$4,$5::date,$6::date,$7,$8,$9::jsonb,$10::date) RETURNING *`,
|
|
6802
7069
|
[
|
|
6803
7070
|
this.brainId,
|
|
6804
7071
|
input.timescale,
|
|
@@ -6807,7 +7074,9 @@ var init_repository = __esm({
|
|
|
6807
7074
|
input.periodStartIso ?? null,
|
|
6808
7075
|
input.periodEndIso ?? null,
|
|
6809
7076
|
confidence,
|
|
6810
|
-
input.createdBy ?? null
|
|
7077
|
+
input.createdBy ?? null,
|
|
7078
|
+
input.chronicle ? JSON.stringify(input.chronicle) : null,
|
|
7079
|
+
input.dataThroughIso ?? null
|
|
6811
7080
|
]
|
|
6812
7081
|
);
|
|
6813
7082
|
return r.rows[0];
|
|
@@ -6842,14 +7111,69 @@ var init_repository = __esm({
|
|
|
6842
7111
|
*/
|
|
6843
7112
|
async updateSummary(id, patch) {
|
|
6844
7113
|
const r = await this.pool.query(
|
|
6845
|
-
`UPDATE summary SET title = $3, body = $4, confidence = $5
|
|
7114
|
+
`UPDATE summary SET title = $3, body = $4, confidence = $5,
|
|
7115
|
+
chronicle = COALESCE($6::jsonb, chronicle),
|
|
7116
|
+
data_through = COALESCE($7::date, data_through)
|
|
6846
7117
|
WHERE id = $1 AND brain_id = $2 RETURNING *`,
|
|
6847
|
-
[
|
|
7118
|
+
[
|
|
7119
|
+
id,
|
|
7120
|
+
this.brainId,
|
|
7121
|
+
patch.title,
|
|
7122
|
+
patch.body,
|
|
7123
|
+
patch.confidence,
|
|
7124
|
+
patch.chronicle ? JSON.stringify(patch.chronicle) : null,
|
|
7125
|
+
patch.dataThroughIso ?? null
|
|
7126
|
+
]
|
|
6848
7127
|
);
|
|
6849
7128
|
const row = r.rows[0];
|
|
6850
7129
|
if (!row) throw new Error("summary not found in this Brain");
|
|
6851
7130
|
return row;
|
|
6852
7131
|
}
|
|
7132
|
+
/**
|
|
7133
|
+
* Claim the report job for one floor (summaries review S01): returns true for exactly one
|
|
7134
|
+
* caller per (summary, hash), so two web processes never write the same report twice. A
|
|
7135
|
+
* claim older than ten minutes counts as abandoned (the process died mid-call) and may be
|
|
7136
|
+
* taken again; `force` retakes a failed one on the reader's request.
|
|
7137
|
+
*/
|
|
7138
|
+
async claimSummaryReport(id, hash, opts = {}) {
|
|
7139
|
+
const r = await this.pool.query(
|
|
7140
|
+
`UPDATE summary
|
|
7141
|
+
SET report_status = 'pending', report_status_hash = $3,
|
|
7142
|
+
report_status_at = now(), report_error = NULL
|
|
7143
|
+
WHERE id = $1 AND brain_id = $2
|
|
7144
|
+
AND report_hash IS DISTINCT FROM $3
|
|
7145
|
+
AND (report_status_hash IS DISTINCT FROM $3
|
|
7146
|
+
OR report_status IS NULL
|
|
7147
|
+
OR report_status_at < now() - interval '10 minutes'
|
|
7148
|
+
OR (report_status = 'failed' AND $4))`,
|
|
7149
|
+
[id, this.brainId, hash, Boolean(opts.force)]
|
|
7150
|
+
);
|
|
7151
|
+
return (r.rowCount ?? 0) > 0;
|
|
7152
|
+
}
|
|
7153
|
+
/** Store the validated report for the floor it was written from; the job is done. */
|
|
7154
|
+
async saveSummaryReport(id, report, hash) {
|
|
7155
|
+
const r = await this.pool.query(
|
|
7156
|
+
`UPDATE summary
|
|
7157
|
+
SET report = $3::jsonb, report_hash = $4, report_at = now(),
|
|
7158
|
+
report_status = NULL, report_status_hash = NULL,
|
|
7159
|
+
report_status_at = NULL, report_error = NULL
|
|
7160
|
+
WHERE id = $1 AND brain_id = $2 RETURNING *`,
|
|
7161
|
+
[id, this.brainId, JSON.stringify(report), hash]
|
|
7162
|
+
);
|
|
7163
|
+
const row = r.rows[0];
|
|
7164
|
+
if (!row) throw new Error("summary not found in this Brain");
|
|
7165
|
+
return row;
|
|
7166
|
+
}
|
|
7167
|
+
/** Record why the report for this floor could not be written; the page shows it. */
|
|
7168
|
+
async failSummaryReport(id, hash, error) {
|
|
7169
|
+
await this.pool.query(
|
|
7170
|
+
`UPDATE summary
|
|
7171
|
+
SET report_status = 'failed', report_status_hash = $3,
|
|
7172
|
+
report_status_at = now(), report_error = $4
|
|
7173
|
+
WHERE id = $1 AND brain_id = $2`,
|
|
7174
|
+
[id, this.brainId, hash, error.slice(0, 200)]
|
|
7175
|
+
);
|
|
7176
|
+
}
|
|
6853
7177
|
/** Store the synthesized prose layer with the floor hash it was built from. */
|
|
6854
7178
|
async saveSummarySynthesis(id, text, hash) {
|
|
6855
7179
|
const r = await this.pool.query(
|
|
@@ -6862,13 +7186,23 @@ var init_repository = __esm({
|
|
|
6862
7186
|
return row;
|
|
6863
7187
|
}
|
|
6864
7188
|
// --- Keyword search (Postgres FTS) ---
|
|
6865
|
-
/**
|
|
7189
|
+
/**
|
|
7190
|
+
* Ranked full-text search over title/why/body in this Brain where any query word counts
|
|
7191
|
+
* (documents sharing more and rarer words rank first). websearch_to_tsquery required EVERY
|
|
7192
|
+
* word, so a sentence-length question or ticket intent matched nothing: measured on 314 real
|
|
7193
|
+
* chat questions, 15% got a keyword hit, with OR nearly all do. plainto_tsquery still stems
|
|
7194
|
+
* and drops stop words; its AND becomes OR. Documents holding every word come first and are
|
|
7195
|
+
* flagged, so fusion can keep trusting them as before.
|
|
7196
|
+
*/
|
|
6866
7197
|
async searchKeyword(query, limit = 10) {
|
|
6867
7198
|
const r = await this.pool.query(
|
|
6868
|
-
`SELECT ${MO_COLUMNS}, ${EV_AGG}, ts_rank(tsv,
|
|
6869
|
-
|
|
6870
|
-
|
|
6871
|
-
|
|
7199
|
+
`SELECT ${MO_COLUMNS}, ${EV_AGG}, ts_rank(tsv, q.kq) AS rank,
|
|
7200
|
+
tsv @@ q.all_kq AS all_words
|
|
7201
|
+
FROM memory_object,
|
|
7202
|
+
(SELECT plainto_tsquery('english', $1) AS all_kq,
|
|
7203
|
+
replace(plainto_tsquery('english', $1)::text, ' & ', ' | ')::tsquery AS kq) q
|
|
7204
|
+
WHERE brain_id = $2 AND tsv @@ q.kq
|
|
7205
|
+
ORDER BY all_words DESC, rank DESC
|
|
6872
7206
|
LIMIT $3`,
|
|
6873
7207
|
[query, this.brainId, limit]
|
|
6874
7208
|
);
|
|
@@ -6958,8 +7292,10 @@ var init_repository = __esm({
|
|
|
6958
7292
|
*/
|
|
6959
7293
|
async saveDocPage(input) {
|
|
6960
7294
|
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
|
|
7295
|
+
`INSERT INTO doc_page (brain_id, slug, version, kind, title, model, markdown, source_hash, deleted, icon)
|
|
7296
|
+
SELECT $1, $2, COALESCE(max(version), 0) + 1, $3, $4, $5, $6, $7, $8,
|
|
7297
|
+
COALESCE((SELECT icon FROM doc_page WHERE brain_id = $1 AND slug = $2
|
|
7298
|
+
ORDER BY version DESC LIMIT 1), $9)
|
|
6963
7299
|
FROM doc_page WHERE brain_id = $1 AND slug = $2
|
|
6964
7300
|
RETURNING *`,
|
|
6965
7301
|
[
|
|
@@ -6970,11 +7306,21 @@ var init_repository = __esm({
|
|
|
6970
7306
|
JSON.stringify(input.model),
|
|
6971
7307
|
input.markdown,
|
|
6972
7308
|
input.sourceHash,
|
|
6973
|
-
input.deleted ?? false
|
|
7309
|
+
input.deleted ?? false,
|
|
7310
|
+
input.icon ?? null
|
|
6974
7311
|
]
|
|
6975
7312
|
);
|
|
6976
7313
|
return r.rows[0];
|
|
6977
7314
|
}
|
|
7315
|
+
/** Set the icon on a page's current version; false when the page does not exist. */
|
|
7316
|
+
async setDocIcon(slug, icon) {
|
|
7317
|
+
const r = await this.pool.query(
|
|
7318
|
+
`UPDATE doc_page SET icon = $3 WHERE brain_id = $1 AND slug = $2
|
|
7319
|
+
AND version = (SELECT max(version) FROM doc_page WHERE brain_id = $1 AND slug = $2)`,
|
|
7320
|
+
[this.brainId, slug, icon]
|
|
7321
|
+
);
|
|
7322
|
+
return (r.rowCount ?? 0) > 0;
|
|
7323
|
+
}
|
|
6978
7324
|
/** The current documentation: latest version per slug, tombstones excluded, entry first. */
|
|
6979
7325
|
async latestDocPages() {
|
|
6980
7326
|
return this.docPagesAsOf();
|
|
@@ -7037,35 +7383,48 @@ var init_repository = __esm({
|
|
|
7037
7383
|
* ponytail: tsvector computed per query; add a stored generated column + GIN index when a
|
|
7038
7384
|
* Brain's doc tree grows past a few hundred pages.
|
|
7039
7385
|
*/
|
|
7040
|
-
|
|
7386
|
+
/**
|
|
7387
|
+
* Docs search over the stored pages as they stood at `at` (default: now), so the time
|
|
7388
|
+
* lens keeps its point in time (docs review R06). A page whose title equals the query
|
|
7389
|
+
* ranks first, then title prefixes and title mentions, then full-text rank: an exact
|
|
7390
|
+
* page never hides behind pages that merely mention it (R12).
|
|
7391
|
+
*/
|
|
7392
|
+
async docSearch(query, limit = 8, at) {
|
|
7041
7393
|
const terms = query.trim().toLowerCase().split(/\s+/).map((t) => t.replace(/[^\p{L}\p{N}]/gu, "")).filter(Boolean).slice(0, 8);
|
|
7042
7394
|
if (terms.length === 0) return [];
|
|
7043
7395
|
const tsq = terms.map((t, i) => i === terms.length - 1 ? `${t}:*` : t).join(" & ");
|
|
7396
|
+
const plain = query.trim().toLowerCase().replace(/\s+/g, " ");
|
|
7044
7397
|
const r = await this.pool.query(
|
|
7045
7398
|
`WITH latest AS (
|
|
7046
7399
|
SELECT DISTINCT ON (slug) * FROM doc_page
|
|
7047
|
-
WHERE brain_id = $1
|
|
7400
|
+
WHERE brain_id = $1 AND generated_at <= COALESCE($4, now())
|
|
7048
7401
|
ORDER BY slug, version DESC
|
|
7049
7402
|
)
|
|
7050
7403
|
SELECT slug, kind, title,
|
|
7051
7404
|
ts_headline('english', c.prose, q,
|
|
7052
7405
|
'StartSel=[[, StopSel=]], MaxWords=18, MinWords=8') AS snippet
|
|
7053
7406
|
FROM latest, to_tsquery('english', $2) q,
|
|
7054
|
-
-- Snippets read as prose: fenced code, inline backticks, link
|
|
7055
|
-
--
|
|
7056
|
-
--
|
|
7407
|
+
-- Snippets read as prose: fenced code, inline backticks, link targets (web,
|
|
7408
|
+
-- repo-relative, and the docs' own page: scheme), and markdown punctuation
|
|
7409
|
+
-- stripped for display only; the search vector keeps the raw markdown so code
|
|
7410
|
+
-- and config names stay findable. (Template literal: regex backslashes are
|
|
7411
|
+
-- doubled, a single one silently vanishes and the pattern never matches.)
|
|
7057
7412
|
LATERAL (SELECT regexp_replace(regexp_replace(regexp_replace(
|
|
7058
7413
|
markdown, '\`\`\`[^\`]*\`\`\`', ' ', 'g'),
|
|
7059
|
-
']((https
|
|
7414
|
+
'\\]\\((https?:|brainjar-doku/|features/|page:)[^)]*\\)', ']', 'g'),
|
|
7060
7415
|
'[\`#*_|]', '', 'g') AS prose) c
|
|
7061
7416
|
WHERE NOT deleted
|
|
7062
7417
|
AND (setweight(to_tsvector('english', title), 'A')
|
|
7063
7418
|
|| setweight(to_tsvector('english', markdown), 'B')) @@ q
|
|
7064
|
-
ORDER BY
|
|
7065
|
-
|
|
7066
|
-
|
|
7419
|
+
ORDER BY
|
|
7420
|
+
(lower(title) = $5) DESC,
|
|
7421
|
+
(lower(title) LIKE $5 || '%') DESC,
|
|
7422
|
+
(position($5 in lower(title)) > 0) DESC,
|
|
7423
|
+
ts_rank(
|
|
7424
|
+
setweight(to_tsvector('english', title), 'A')
|
|
7425
|
+
|| setweight(to_tsvector('english', markdown), 'B'), q) DESC
|
|
7067
7426
|
LIMIT $3`,
|
|
7068
|
-
[this.brainId, tsq, limit]
|
|
7427
|
+
[this.brainId, tsq, limit, at ?? null, plain]
|
|
7069
7428
|
);
|
|
7070
7429
|
return r.rows;
|
|
7071
7430
|
}
|
|
@@ -7239,13 +7598,45 @@ var init_repository = __esm({
|
|
|
7239
7598
|
}
|
|
7240
7599
|
});
|
|
7241
7600
|
|
|
7601
|
+
// packages/db/src/ratelimit.ts
|
|
7602
|
+
var init_ratelimit2 = __esm({
|
|
7603
|
+
"packages/db/src/ratelimit.ts"() {
|
|
7604
|
+
"use strict";
|
|
7605
|
+
init_pool();
|
|
7606
|
+
}
|
|
7607
|
+
});
|
|
7608
|
+
|
|
7609
|
+
// packages/db/src/auth-event.ts
|
|
7610
|
+
var init_auth_event = __esm({
|
|
7611
|
+
"packages/db/src/auth-event.ts"() {
|
|
7612
|
+
"use strict";
|
|
7613
|
+
init_pool();
|
|
7614
|
+
}
|
|
7615
|
+
});
|
|
7616
|
+
|
|
7617
|
+
// packages/db/src/email-token.ts
|
|
7618
|
+
var init_email_token = __esm({
|
|
7619
|
+
"packages/db/src/email-token.ts"() {
|
|
7620
|
+
"use strict";
|
|
7621
|
+
init_pool();
|
|
7622
|
+
}
|
|
7623
|
+
});
|
|
7624
|
+
|
|
7242
7625
|
// packages/db/src/auth.ts
|
|
7243
|
-
var DEFAULT_TTL_HOURS;
|
|
7626
|
+
var DEFAULT_TTL_HOURS, LOGIN_FAILURE_WINDOW_MS, SCRYPT, REAUTH_WINDOW_MS, AVATAR_MAX_BYTES;
|
|
7244
7627
|
var init_auth = __esm({
|
|
7245
7628
|
"packages/db/src/auth.ts"() {
|
|
7246
7629
|
"use strict";
|
|
7630
|
+
init_src();
|
|
7247
7631
|
init_pool();
|
|
7632
|
+
init_ratelimit2();
|
|
7633
|
+
init_auth_event();
|
|
7634
|
+
init_email_token();
|
|
7248
7635
|
DEFAULT_TTL_HOURS = 24 * 90;
|
|
7636
|
+
LOGIN_FAILURE_WINDOW_MS = 15 * 6e4;
|
|
7637
|
+
SCRYPT = { N: 16384, r: 8, p: 1, maxmem: 64 * 1024 * 1024 };
|
|
7638
|
+
REAUTH_WINDOW_MS = 5 * 6e4;
|
|
7639
|
+
AVATAR_MAX_BYTES = 256 * 1024;
|
|
7249
7640
|
}
|
|
7250
7641
|
});
|
|
7251
7642
|
|
|
@@ -7269,11 +7660,43 @@ var init_oauth = __esm({
|
|
|
7269
7660
|
}
|
|
7270
7661
|
});
|
|
7271
7662
|
|
|
7663
|
+
// packages/db/src/totp.ts
|
|
7664
|
+
var CHALLENGE_TTL_MS;
|
|
7665
|
+
var init_totp2 = __esm({
|
|
7666
|
+
"packages/db/src/totp.ts"() {
|
|
7667
|
+
"use strict";
|
|
7668
|
+
init_src();
|
|
7669
|
+
init_pool();
|
|
7670
|
+
CHALLENGE_TTL_MS = 5 * 6e4;
|
|
7671
|
+
}
|
|
7672
|
+
});
|
|
7673
|
+
|
|
7674
|
+
// packages/db/src/passkey.ts
|
|
7675
|
+
var CHALLENGE_TTL_MS2;
|
|
7676
|
+
var init_passkey = __esm({
|
|
7677
|
+
"packages/db/src/passkey.ts"() {
|
|
7678
|
+
"use strict";
|
|
7679
|
+
init_pool();
|
|
7680
|
+
CHALLENGE_TTL_MS2 = 5 * 6e4;
|
|
7681
|
+
}
|
|
7682
|
+
});
|
|
7683
|
+
|
|
7684
|
+
// packages/db/src/identity.ts
|
|
7685
|
+
var STATE_TTL_MS;
|
|
7686
|
+
var init_identity = __esm({
|
|
7687
|
+
"packages/db/src/identity.ts"() {
|
|
7688
|
+
"use strict";
|
|
7689
|
+
init_pool();
|
|
7690
|
+
STATE_TTL_MS = 5 * 6e4;
|
|
7691
|
+
}
|
|
7692
|
+
});
|
|
7693
|
+
|
|
7272
7694
|
// packages/db/src/team.ts
|
|
7273
7695
|
var init_team = __esm({
|
|
7274
7696
|
"packages/db/src/team.ts"() {
|
|
7275
7697
|
"use strict";
|
|
7276
7698
|
init_pool();
|
|
7699
|
+
init_auth();
|
|
7277
7700
|
}
|
|
7278
7701
|
});
|
|
7279
7702
|
|
|
@@ -7333,14 +7756,27 @@ var init_settings = __esm({
|
|
|
7333
7756
|
APP_SETTING_KEYS = [
|
|
7334
7757
|
"capture_mode",
|
|
7335
7758
|
// transcript privacy: full | distilled | off
|
|
7336
|
-
"author",
|
|
7337
|
-
// developer identity stamped on captured reasoning
|
|
7338
|
-
"promotion_threshold",
|
|
7339
|
-
// signal/noise gate (0..1)
|
|
7340
7759
|
"rationale_model",
|
|
7341
7760
|
// Anthropic model for rationale/consolidation
|
|
7342
|
-
"public_url"
|
|
7761
|
+
"public_url",
|
|
7343
7762
|
// this server's address as clients reach it (wizard command, MCP config)
|
|
7763
|
+
// Outgoing mail (BJ-103); the password is a secret and lives next to the provider keys.
|
|
7764
|
+
"smtp_host",
|
|
7765
|
+
"smtp_port",
|
|
7766
|
+
"smtp_user",
|
|
7767
|
+
"smtp_secure",
|
|
7768
|
+
// "1" = TLS from the start (465), else STARTTLS (587)
|
|
7769
|
+
"mail_from",
|
|
7770
|
+
// Sign in with a provider (BJ-112); the client secrets are secrets, see below.
|
|
7771
|
+
"google_client_id",
|
|
7772
|
+
"github_client_id",
|
|
7773
|
+
// The legal pages the signup checkbox links to (BJ-113).
|
|
7774
|
+
"terms_url",
|
|
7775
|
+
"privacy_url",
|
|
7776
|
+
// Voice chat (voice handoff, 2026-09-21): an explicit "true" or "false"; absent means on.
|
|
7777
|
+
"voice_enabled",
|
|
7778
|
+
// The GPT-Live voice (voice picker, 2026-09-22): an id from VOICE_OPTIONS; absent = default.
|
|
7779
|
+
"voice_name"
|
|
7344
7780
|
];
|
|
7345
7781
|
PROVIDER_KEY_SETTINGS = {
|
|
7346
7782
|
anthropic: "anthropic_api_key",
|
|
@@ -7373,11 +7809,20 @@ var init_tickets = __esm({
|
|
|
7373
7809
|
}
|
|
7374
7810
|
});
|
|
7375
7811
|
|
|
7812
|
+
// packages/db/src/jobs.ts
|
|
7813
|
+
var init_jobs = __esm({
|
|
7814
|
+
"packages/db/src/jobs.ts"() {
|
|
7815
|
+
"use strict";
|
|
7816
|
+
init_pool();
|
|
7817
|
+
}
|
|
7818
|
+
});
|
|
7819
|
+
|
|
7376
7820
|
// packages/db/src/ruleset.ts
|
|
7377
7821
|
var init_ruleset = __esm({
|
|
7378
7822
|
"packages/db/src/ruleset.ts"() {
|
|
7379
7823
|
"use strict";
|
|
7380
7824
|
init_pool();
|
|
7825
|
+
init_tickets();
|
|
7381
7826
|
}
|
|
7382
7827
|
});
|
|
7383
7828
|
|
|
@@ -7414,12 +7859,19 @@ var init_src2 = __esm({
|
|
|
7414
7859
|
init_auth();
|
|
7415
7860
|
init_api_token();
|
|
7416
7861
|
init_oauth();
|
|
7862
|
+
init_ratelimit2();
|
|
7863
|
+
init_auth_event();
|
|
7864
|
+
init_email_token();
|
|
7865
|
+
init_totp2();
|
|
7866
|
+
init_passkey();
|
|
7867
|
+
init_identity();
|
|
7417
7868
|
init_team();
|
|
7418
7869
|
init_rbac();
|
|
7419
7870
|
init_settings();
|
|
7420
7871
|
init_notifications();
|
|
7421
7872
|
init_conversations();
|
|
7422
7873
|
init_tickets();
|
|
7874
|
+
init_jobs();
|
|
7423
7875
|
init_ruleset();
|
|
7424
7876
|
init_usage();
|
|
7425
7877
|
init_audit_origin();
|
|
@@ -7671,14 +8123,14 @@ var init_values = __esm({
|
|
|
7671
8123
|
};
|
|
7672
8124
|
isArray = (val) => (isArray = Array.isArray, isArray(val));
|
|
7673
8125
|
isReadonlyArray = isArray;
|
|
7674
|
-
validatePositiveInteger = (name,
|
|
7675
|
-
if (typeof
|
|
8126
|
+
validatePositiveInteger = (name, n2) => {
|
|
8127
|
+
if (typeof n2 !== "number" || !Number.isInteger(n2)) {
|
|
7676
8128
|
throw new AnthropicError(`${name} must be an integer`);
|
|
7677
8129
|
}
|
|
7678
|
-
if (
|
|
8130
|
+
if (n2 < 0) {
|
|
7679
8131
|
throw new AnthropicError(`${name} must be a positive integer`);
|
|
7680
8132
|
}
|
|
7681
|
-
return
|
|
8133
|
+
return n2;
|
|
7682
8134
|
};
|
|
7683
8135
|
safeJSON = (text) => {
|
|
7684
8136
|
try {
|
|
@@ -7950,9 +8402,9 @@ function concatBytes(buffers) {
|
|
|
7950
8402
|
}
|
|
7951
8403
|
return output;
|
|
7952
8404
|
}
|
|
7953
|
-
function encodeUTF8(
|
|
8405
|
+
function encodeUTF8(str2) {
|
|
7954
8406
|
let encoder;
|
|
7955
|
-
return (encodeUTF8_ ?? (encoder = new globalThis.TextEncoder(), encodeUTF8_ = encoder.encode.bind(encoder)))(
|
|
8407
|
+
return (encodeUTF8_ ?? (encoder = new globalThis.TextEncoder(), encodeUTF8_ = encoder.encode.bind(encoder)))(str2);
|
|
7956
8408
|
}
|
|
7957
8409
|
function decodeUTF8(bytes) {
|
|
7958
8410
|
let decoder;
|
|
@@ -8171,12 +8623,12 @@ async function* iterSSEChunks(iterator) {
|
|
|
8171
8623
|
yield data;
|
|
8172
8624
|
}
|
|
8173
8625
|
}
|
|
8174
|
-
function partition(
|
|
8175
|
-
const index =
|
|
8626
|
+
function partition(str2, delimiter) {
|
|
8627
|
+
const index = str2.indexOf(delimiter);
|
|
8176
8628
|
if (index !== -1) {
|
|
8177
|
-
return [
|
|
8629
|
+
return [str2.substring(0, index), delimiter, str2.substring(index + delimiter.length)];
|
|
8178
8630
|
}
|
|
8179
|
-
return [
|
|
8631
|
+
return [str2, "", ""];
|
|
8180
8632
|
}
|
|
8181
8633
|
var _Stream_client, Stream, SSEDecoder;
|
|
8182
8634
|
var init_streaming = __esm({
|
|
@@ -8891,8 +9343,8 @@ var init_headers = __esm({
|
|
|
8891
9343
|
});
|
|
8892
9344
|
|
|
8893
9345
|
// node_modules/@anthropic-ai/sdk/internal/utils/path.mjs
|
|
8894
|
-
function encodeURIPath(
|
|
8895
|
-
return
|
|
9346
|
+
function encodeURIPath(str2) {
|
|
9347
|
+
return str2.replace(/[^A-Za-z0-9\-._~!$&'()*+,;=:@]+/g, encodeURIComponent);
|
|
8896
9348
|
}
|
|
8897
9349
|
var EMPTY, createPathTagFunction, path3;
|
|
8898
9350
|
var init_path = __esm({
|
|
@@ -13716,7 +14168,7 @@ ${line}
|
|
|
13716
14168
|
`.split("\n");
|
|
13717
14169
|
} else {
|
|
13718
14170
|
const i = lines.findIndex(
|
|
13719
|
-
(l,
|
|
14171
|
+
(l, n2) => n2 >= range[0] && n2 < range[1] && /^\s*url\s*=/.test(l)
|
|
13720
14172
|
);
|
|
13721
14173
|
next = [...lines];
|
|
13722
14174
|
if (i >= 0) next[i] = line;
|
|
@@ -13936,17 +14388,23 @@ var init_daily_log = __esm({
|
|
|
13936
14388
|
});
|
|
13937
14389
|
|
|
13938
14390
|
// packages/ingest/src/summaries.ts
|
|
13939
|
-
var
|
|
14391
|
+
var REPORT_SYSTEM;
|
|
13940
14392
|
var init_summaries = __esm({
|
|
13941
14393
|
"packages/ingest/src/summaries.ts"() {
|
|
13942
14394
|
"use strict";
|
|
13943
14395
|
init_src2();
|
|
13944
14396
|
init_src();
|
|
13945
|
-
|
|
13946
|
-
"You
|
|
13947
|
-
"
|
|
13948
|
-
"Return
|
|
13949
|
-
"
|
|
14397
|
+
REPORT_SYSTEM = [
|
|
14398
|
+
"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.",
|
|
14399
|
+
"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).",
|
|
14400
|
+
"Return ONLY a JSON object, no prose around it, no code fence, with this shape:",
|
|
14401
|
+
'{"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]}]}',
|
|
14402
|
+
"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.",
|
|
14403
|
+
"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.",
|
|
14404
|
+
"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.",
|
|
14405
|
+
"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.",
|
|
14406
|
+
"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.",
|
|
14407
|
+
"No file paths, function names, migration numbers or shell commands. Write in English.",
|
|
13950
14408
|
PROSE_STYLE
|
|
13951
14409
|
].join(" ");
|
|
13952
14410
|
}
|
|
@@ -13972,11 +14430,11 @@ function slugify(title) {
|
|
|
13972
14430
|
}
|
|
13973
14431
|
function humans(names) {
|
|
13974
14432
|
const count = /* @__PURE__ */ new Map();
|
|
13975
|
-
for (const
|
|
13976
|
-
if (!
|
|
13977
|
-
count.set(
|
|
14433
|
+
for (const n2 of names) {
|
|
14434
|
+
if (!n2 || n2 === "system") continue;
|
|
14435
|
+
count.set(n2, (count.get(n2) ?? 0) + 1);
|
|
13978
14436
|
}
|
|
13979
|
-
return [...count.entries()].sort((a, b) => b[1] - a[1]).map(([
|
|
14437
|
+
return [...count.entries()].sort((a, b) => b[1] - a[1]).map(([n2]) => n2);
|
|
13980
14438
|
}
|
|
13981
14439
|
function moduleGroup(modulePath) {
|
|
13982
14440
|
const segs = modulePath.split("/");
|
|
@@ -14027,7 +14485,8 @@ function buildDokuModel(input) {
|
|
|
14027
14485
|
changes,
|
|
14028
14486
|
decisions,
|
|
14029
14487
|
locations,
|
|
14030
|
-
evidenceUris
|
|
14488
|
+
evidenceUris,
|
|
14489
|
+
linkedDecisionIds
|
|
14031
14490
|
}) => {
|
|
14032
14491
|
let slug = slugify(feature.title);
|
|
14033
14492
|
for (let i = 2; usedSlugs.has(slug) || RESERVED_SLUGS.has(slug); i++)
|
|
@@ -14066,13 +14525,16 @@ function buildDokuModel(input) {
|
|
|
14066
14525
|
owner: explicitOwner ?? contributors[0] ?? null,
|
|
14067
14526
|
contributors,
|
|
14068
14527
|
narrative: null,
|
|
14069
|
-
decisions: dedupeDecisions(decisions).map(
|
|
14070
|
-
|
|
14071
|
-
|
|
14072
|
-
|
|
14073
|
-
|
|
14074
|
-
|
|
14075
|
-
|
|
14528
|
+
decisions: dedupeDecisions(decisions).map(
|
|
14529
|
+
(d) => ({
|
|
14530
|
+
title: d.title,
|
|
14531
|
+
why: d.why,
|
|
14532
|
+
by: d.created_by,
|
|
14533
|
+
date: fmtDate(d.occurred_at),
|
|
14534
|
+
objectId: d.id,
|
|
14535
|
+
...linkedDecisionIds ? { via: linkedDecisionIds.has(d.id) ? "link" : "location" } : {}
|
|
14536
|
+
})
|
|
14537
|
+
).sort((a, b) => a.date < b.date ? 1 : -1),
|
|
14076
14538
|
history,
|
|
14077
14539
|
updated: history[0].date
|
|
14078
14540
|
};
|
|
@@ -14147,8 +14609,19 @@ function renderEntryPage(model, base = "brainjar-doku/", narrative = null) {
|
|
|
14147
14609
|
if (more.length) parts.push("## More", more.join("\n"));
|
|
14148
14610
|
return parts.join("\n\n") + "\n";
|
|
14149
14611
|
}
|
|
14150
|
-
function renderArchitecturePage(model) {
|
|
14612
|
+
function renderArchitecturePage(model, synthesized = {}) {
|
|
14151
14613
|
const parts = [`# How it works`, DOKU_NOTICE];
|
|
14614
|
+
if (synthesized.narrative) parts.push(synthesized.narrative);
|
|
14615
|
+
for (const f of synthesized.flows ?? []) {
|
|
14616
|
+
parts.push(
|
|
14617
|
+
[
|
|
14618
|
+
`## ${f.title}`,
|
|
14619
|
+
...f.summary ? [f.summary] : [],
|
|
14620
|
+
...f.explanation ? [f.explanation] : [],
|
|
14621
|
+
flowLanesMarkdown(f)
|
|
14622
|
+
].join("\n\n")
|
|
14623
|
+
);
|
|
14624
|
+
}
|
|
14152
14625
|
const idx = new Map(model.architecture.groups.map((g, i) => [g.name, i]));
|
|
14153
14626
|
const lines = ["```mermaid", "flowchart LR"];
|
|
14154
14627
|
for (const g of model.architecture.groups) {
|
|
@@ -14162,7 +14635,7 @@ function renderArchitecturePage(model) {
|
|
|
14162
14635
|
);
|
|
14163
14636
|
}
|
|
14164
14637
|
lines.push("```");
|
|
14165
|
-
parts.push(lines.join("\n"));
|
|
14638
|
+
parts.push("## Module groups", lines.join("\n"));
|
|
14166
14639
|
parts.push(
|
|
14167
14640
|
"## Modules by group",
|
|
14168
14641
|
model.architecture.groups.map(
|
|
@@ -14179,6 +14652,234 @@ function firstSentence(s) {
|
|
|
14179
14652
|
const cut2 = t.slice(0, 200);
|
|
14180
14653
|
return `${cut2.slice(0, cut2.lastIndexOf(" "))}\u2026`;
|
|
14181
14654
|
}
|
|
14655
|
+
function parsePresentation(raw, ctx) {
|
|
14656
|
+
const bad = (error) => ({ presentation: null, error, dropped: [] });
|
|
14657
|
+
const o = raw;
|
|
14658
|
+
if (!o || typeof o !== "object") return bad("not an object");
|
|
14659
|
+
if (!str(o.summary, LIMIT.summary))
|
|
14660
|
+
return bad("summary: missing or too long");
|
|
14661
|
+
if (!Array.isArray(o.blocks) || o.blocks.length === 0)
|
|
14662
|
+
return bad("blocks: need a non-empty list");
|
|
14663
|
+
if (o.blocks.length > MAX_BLOCKS)
|
|
14664
|
+
return bad(`blocks: more than ${MAX_BLOCKS}`);
|
|
14665
|
+
const blocks = [];
|
|
14666
|
+
const dropped = [];
|
|
14667
|
+
const flowsPlaced = /* @__PURE__ */ new Set();
|
|
14668
|
+
for (const [i, cand] of o.blocks.entries()) {
|
|
14669
|
+
const b = cand;
|
|
14670
|
+
const at = `block ${i + 1}`;
|
|
14671
|
+
if (!b || typeof b !== "object") return bad(`${at}: not an object`);
|
|
14672
|
+
const title = b.title;
|
|
14673
|
+
switch (b.kind) {
|
|
14674
|
+
case "text": {
|
|
14675
|
+
if (title !== void 0 && !str(title, LIMIT.title))
|
|
14676
|
+
return bad(`${at}: text title too long`);
|
|
14677
|
+
if (!str(b.markdown, LIMIT.markdown))
|
|
14678
|
+
return bad(`${at}: text markdown missing or too long`);
|
|
14679
|
+
blocks.push({
|
|
14680
|
+
kind: "text",
|
|
14681
|
+
...title ? { title: title.trim() } : {},
|
|
14682
|
+
markdown: b.markdown.trim()
|
|
14683
|
+
});
|
|
14684
|
+
break;
|
|
14685
|
+
}
|
|
14686
|
+
case "steps": {
|
|
14687
|
+
if (!str(title, LIMIT.title)) return bad(`${at}: steps need a title`);
|
|
14688
|
+
const items = b.items;
|
|
14689
|
+
if (!Array.isArray(items) || items.length === 0 || items.length > LIMIT.stepItems)
|
|
14690
|
+
return bad(`${at}: steps need 1-${LIMIT.stepItems} items`);
|
|
14691
|
+
const out = [];
|
|
14692
|
+
for (const it of items) {
|
|
14693
|
+
const s = it;
|
|
14694
|
+
if (!str(s?.title, LIMIT.title) || !str(s?.body, LIMIT.stepBody, 0))
|
|
14695
|
+
return bad(`${at}: step needs title and body`);
|
|
14696
|
+
out.push({ title: s.title.trim(), body: s.body.trim() });
|
|
14697
|
+
}
|
|
14698
|
+
blocks.push({ kind: "steps", title: title.trim(), items: out });
|
|
14699
|
+
break;
|
|
14700
|
+
}
|
|
14701
|
+
case "table": {
|
|
14702
|
+
if (!str(title, LIMIT.title)) return bad(`${at}: table needs a title`);
|
|
14703
|
+
const columns = b.columns;
|
|
14704
|
+
if (!Array.isArray(columns) || columns.length < 2 || columns.length > LIMIT.columns || !columns.every((c) => str(c, 80)))
|
|
14705
|
+
return bad(`${at}: table needs 2-${LIMIT.columns} column names`);
|
|
14706
|
+
const rows = b.rows;
|
|
14707
|
+
if (!Array.isArray(rows) || rows.length === 0 || rows.length > LIMIT.rows)
|
|
14708
|
+
return bad(`${at}: table needs 1-${LIMIT.rows} rows`);
|
|
14709
|
+
const outRows = [];
|
|
14710
|
+
for (const r of rows) {
|
|
14711
|
+
if (!Array.isArray(r) || r.length !== columns.length || !r.every((c) => typeof c === "string" && c.length <= LIMIT.cell))
|
|
14712
|
+
return bad(
|
|
14713
|
+
`${at}: every row needs exactly ${columns.length} cells`
|
|
14714
|
+
);
|
|
14715
|
+
outRows.push(r.map((c) => c.trim()));
|
|
14716
|
+
}
|
|
14717
|
+
blocks.push({
|
|
14718
|
+
kind: "table",
|
|
14719
|
+
title: title.trim(),
|
|
14720
|
+
columns: columns.map((c) => c.trim()),
|
|
14721
|
+
rows: outRows
|
|
14722
|
+
});
|
|
14723
|
+
break;
|
|
14724
|
+
}
|
|
14725
|
+
case "endpoint": {
|
|
14726
|
+
const method = typeof b.method === "string" ? b.method.trim().toUpperCase() : "";
|
|
14727
|
+
if (!HTTP_METHODS.has(method)) return bad(`${at}: unknown HTTP method`);
|
|
14728
|
+
if (!str(b.path, LIMIT.endpointPath) || !/^\/\S*$/.test(b.path.trim()))
|
|
14729
|
+
return bad(`${at}: endpoint path must start with /`);
|
|
14730
|
+
if (!str(b.summary, LIMIT.endpointSummary))
|
|
14731
|
+
return bad(`${at}: endpoint needs a summary`);
|
|
14732
|
+
if (b.auth !== void 0 && !str(b.auth, LIMIT.auth))
|
|
14733
|
+
return bad(`${at}: endpoint auth too long`);
|
|
14734
|
+
const path20 = b.path.trim();
|
|
14735
|
+
if (!ctx.factSheet.includes(path20)) {
|
|
14736
|
+
dropped.push(`endpoint ${method} ${path20} (not in the fact sheet)`);
|
|
14737
|
+
break;
|
|
14738
|
+
}
|
|
14739
|
+
blocks.push({
|
|
14740
|
+
kind: "endpoint",
|
|
14741
|
+
method,
|
|
14742
|
+
path: path20,
|
|
14743
|
+
summary: b.summary.trim(),
|
|
14744
|
+
...b.auth ? { auth: b.auth.trim() } : {}
|
|
14745
|
+
});
|
|
14746
|
+
break;
|
|
14747
|
+
}
|
|
14748
|
+
case "code": {
|
|
14749
|
+
if (!str(title, LIMIT.title)) return bad(`${at}: code needs a title`);
|
|
14750
|
+
if (typeof b.language !== "string" || !/^[a-z0-9+#.-]{1,20}$/i.test(b.language.trim()))
|
|
14751
|
+
return bad(`${at}: code needs a language tag`);
|
|
14752
|
+
if (!str(b.code, LIMIT.code))
|
|
14753
|
+
return bad(`${at}: code missing or too long`);
|
|
14754
|
+
blocks.push({
|
|
14755
|
+
kind: "code",
|
|
14756
|
+
title: title.trim(),
|
|
14757
|
+
language: b.language.trim().toLowerCase(),
|
|
14758
|
+
code: b.code.replace(/\s+$/, ""),
|
|
14759
|
+
...b.illustrative === true ? { illustrative: true } : {}
|
|
14760
|
+
});
|
|
14761
|
+
break;
|
|
14762
|
+
}
|
|
14763
|
+
case "callout": {
|
|
14764
|
+
if (b.tone !== "note" && b.tone !== "warning")
|
|
14765
|
+
return bad(`${at}: callout tone must be note or warning`);
|
|
14766
|
+
if (!str(b.text, LIMIT.callout)) return bad(`${at}: callout text`);
|
|
14767
|
+
blocks.push({ kind: "callout", tone: b.tone, text: b.text.trim() });
|
|
14768
|
+
break;
|
|
14769
|
+
}
|
|
14770
|
+
case "flow": {
|
|
14771
|
+
if (!str(title, LIMIT.title)) return bad(`${at}: flow needs a title`);
|
|
14772
|
+
if (b.source !== "flowDiagram" && b.source !== "flow")
|
|
14773
|
+
return bad(`${at}: flow source must be flowDiagram or flow`);
|
|
14774
|
+
const exists2 = b.source === "flowDiagram" ? ctx.hasFlowDiagram : ctx.hasFlow;
|
|
14775
|
+
if (!exists2 || flowsPlaced.has(b.source)) {
|
|
14776
|
+
dropped.push(
|
|
14777
|
+
`flow ${b.source} (${exists2 ? "placed twice" : "no such diagram"})`
|
|
14778
|
+
);
|
|
14779
|
+
break;
|
|
14780
|
+
}
|
|
14781
|
+
flowsPlaced.add(b.source);
|
|
14782
|
+
blocks.push({ kind: "flow", title: title.trim(), source: b.source });
|
|
14783
|
+
break;
|
|
14784
|
+
}
|
|
14785
|
+
default:
|
|
14786
|
+
return bad(`${at}: unknown kind ${String(b.kind)}`);
|
|
14787
|
+
}
|
|
14788
|
+
}
|
|
14789
|
+
if (blocks.length === 0) return bad("blocks: nothing left after validation");
|
|
14790
|
+
return {
|
|
14791
|
+
presentation: { version: 1, summary: o.summary.trim(), blocks },
|
|
14792
|
+
error: null,
|
|
14793
|
+
dropped
|
|
14794
|
+
};
|
|
14795
|
+
}
|
|
14796
|
+
function flowLanesMarkdown(d) {
|
|
14797
|
+
return d.lanes.map((l) => {
|
|
14798
|
+
const nodes = d.nodes.filter((n2) => n2.lane === l.key).map((n2) => n2.title).join(", ");
|
|
14799
|
+
return `- **${l.title}**: ${nodes || "(no stations)"}`;
|
|
14800
|
+
}).join("\n");
|
|
14801
|
+
}
|
|
14802
|
+
function renderBlocksMarkdown(p, diagrams = {}) {
|
|
14803
|
+
const parts = [];
|
|
14804
|
+
for (const b of p.blocks) {
|
|
14805
|
+
switch (b.kind) {
|
|
14806
|
+
case "text":
|
|
14807
|
+
parts.push([h2(b.title), b.markdown].filter(Boolean).join("\n\n"));
|
|
14808
|
+
break;
|
|
14809
|
+
case "steps":
|
|
14810
|
+
parts.push(
|
|
14811
|
+
[
|
|
14812
|
+
h2(b.title),
|
|
14813
|
+
b.items.map(
|
|
14814
|
+
(s, i) => `${i + 1}. **${s.title}**${s.body ? ` ${s.body}` : ""}`
|
|
14815
|
+
).join("\n")
|
|
14816
|
+
].join("\n\n")
|
|
14817
|
+
);
|
|
14818
|
+
break;
|
|
14819
|
+
case "table":
|
|
14820
|
+
parts.push(
|
|
14821
|
+
[
|
|
14822
|
+
h2(b.title),
|
|
14823
|
+
[
|
|
14824
|
+
`| ${b.columns.map(mdCell).join(" | ")} |`,
|
|
14825
|
+
`|${b.columns.map(() => "---").join("|")}|`,
|
|
14826
|
+
...b.rows.map((r) => `| ${r.map(mdCell).join(" | ")} |`)
|
|
14827
|
+
].join("\n")
|
|
14828
|
+
].join("\n\n")
|
|
14829
|
+
);
|
|
14830
|
+
break;
|
|
14831
|
+
case "endpoint":
|
|
14832
|
+
parts.push(
|
|
14833
|
+
[
|
|
14834
|
+
`### \`${b.method} ${b.path}\``,
|
|
14835
|
+
b.summary,
|
|
14836
|
+
...b.auth ? [`Authentication: ${b.auth}`] : []
|
|
14837
|
+
].join("\n\n")
|
|
14838
|
+
);
|
|
14839
|
+
break;
|
|
14840
|
+
case "code":
|
|
14841
|
+
parts.push(
|
|
14842
|
+
[
|
|
14843
|
+
`**${b.title}**${b.illustrative ? " (illustrative example, replace the placeholders)" : ""}`,
|
|
14844
|
+
`\`\`\`${b.language}
|
|
14845
|
+
${b.code}
|
|
14846
|
+
\`\`\``
|
|
14847
|
+
].join("\n\n")
|
|
14848
|
+
);
|
|
14849
|
+
break;
|
|
14850
|
+
case "callout":
|
|
14851
|
+
parts.push(
|
|
14852
|
+
`> **${b.tone === "warning" ? "Warning" : "Note"}:** ${b.text.replace(/\n/g, "\n> ")}`
|
|
14853
|
+
);
|
|
14854
|
+
break;
|
|
14855
|
+
case "flow": {
|
|
14856
|
+
const lines = [h2(b.title)];
|
|
14857
|
+
if (b.source === "flowDiagram" && diagrams.flowDiagram) {
|
|
14858
|
+
const d = diagrams.flowDiagram;
|
|
14859
|
+
lines.push(
|
|
14860
|
+
`_Runtime flow diagram: ${d.title}. Interactive on the web page; its lanes and stations:_`
|
|
14861
|
+
);
|
|
14862
|
+
lines.push(flowLanesMarkdown(d));
|
|
14863
|
+
} else if (b.source === "flow" && diagrams.flow) {
|
|
14864
|
+
lines.push(
|
|
14865
|
+
diagrams.flow.steps.map((s, i) => `${i + 1}. ${s}`).join("\n")
|
|
14866
|
+
);
|
|
14867
|
+
}
|
|
14868
|
+
parts.push(lines.join("\n\n"));
|
|
14869
|
+
break;
|
|
14870
|
+
}
|
|
14871
|
+
}
|
|
14872
|
+
}
|
|
14873
|
+
return parts.join("\n\n");
|
|
14874
|
+
}
|
|
14875
|
+
function pageBody(f) {
|
|
14876
|
+
if (f.presentation)
|
|
14877
|
+
return [
|
|
14878
|
+
f.presentation.summary,
|
|
14879
|
+
renderBlocksMarkdown(f.presentation, f)
|
|
14880
|
+
].join("\n\n");
|
|
14881
|
+
return f.body ?? null;
|
|
14882
|
+
}
|
|
14182
14883
|
function renderFeaturePage(f) {
|
|
14183
14884
|
const meta = [
|
|
14184
14885
|
"| | |",
|
|
@@ -14189,8 +14890,9 @@ function renderFeaturePage(f) {
|
|
|
14189
14890
|
...f.locations.length ? [`| Code | ${f.locations.map((l) => `\`${l}\``).join(", ")} |`] : []
|
|
14190
14891
|
];
|
|
14191
14892
|
const parts = [`# ${f.docTitle ?? f.title}`, meta.join("\n")];
|
|
14192
|
-
|
|
14193
|
-
|
|
14893
|
+
const body = pageBody(f);
|
|
14894
|
+
if (body) {
|
|
14895
|
+
parts.push(body);
|
|
14194
14896
|
} else {
|
|
14195
14897
|
if (f.narrative) parts.push(f.narrative);
|
|
14196
14898
|
parts.push("## What it does", f.what);
|
|
@@ -14236,7 +14938,7 @@ function renderCategoryPage(model) {
|
|
|
14236
14938
|
function renderChangelogPage(model) {
|
|
14237
14939
|
const parts = [`# Changelog`, DOKU_NOTICE];
|
|
14238
14940
|
for (const day of model.changelog) {
|
|
14239
|
-
const counts = Object.entries(day.counts).map(([t,
|
|
14941
|
+
const counts = Object.entries(day.counts).map(([t, n2]) => `${n2} ${t}${n2 === 1 ? "" : "s"}`).join(" \xB7 ");
|
|
14240
14942
|
const who = day.contributors.length ? ` \xB7 by ${day.contributors.join(", ")}` : "";
|
|
14241
14943
|
parts.push(
|
|
14242
14944
|
`## ${day.date}`,
|
|
@@ -14333,7 +15035,7 @@ function renderDokuPages(model) {
|
|
|
14333
15035
|
}
|
|
14334
15036
|
return pages;
|
|
14335
15037
|
}
|
|
14336
|
-
var normTitle, DOC_SECTIONS, fmtDate, MAX_GROUPS, MAX_HIGHLIGHTS, HIGHLIGHT_TYPES, RESERVED_SLUGS, DOKU_NOTICE, mermaidId, mermaidLabel, mdCell, asRecord;
|
|
15038
|
+
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
15039
|
var init_doku_model = __esm({
|
|
14338
15040
|
"packages/ingest/src/doku-model.ts"() {
|
|
14339
15041
|
"use strict";
|
|
@@ -14360,6 +15062,33 @@ var init_doku_model = __esm({
|
|
|
14360
15062
|
mermaidId = (name, i) => `g${i}`;
|
|
14361
15063
|
mermaidLabel = (s) => s.replace(/"/g, "'");
|
|
14362
15064
|
mdCell = (s) => (s ?? "").replace(/\|/g, "\\|").replace(/\s+/g, " ").trim();
|
|
15065
|
+
HTTP_METHODS = /* @__PURE__ */ new Set([
|
|
15066
|
+
"GET",
|
|
15067
|
+
"POST",
|
|
15068
|
+
"PUT",
|
|
15069
|
+
"PATCH",
|
|
15070
|
+
"DELETE",
|
|
15071
|
+
"HEAD",
|
|
15072
|
+
"OPTIONS"
|
|
15073
|
+
]);
|
|
15074
|
+
MAX_BLOCKS = 40;
|
|
15075
|
+
LIMIT = {
|
|
15076
|
+
title: 120,
|
|
15077
|
+
summary: 300,
|
|
15078
|
+
markdown: 2e4,
|
|
15079
|
+
stepItems: 20,
|
|
15080
|
+
stepBody: 1e3,
|
|
15081
|
+
columns: 6,
|
|
15082
|
+
rows: 40,
|
|
15083
|
+
cell: 400,
|
|
15084
|
+
endpointPath: 200,
|
|
15085
|
+
endpointSummary: 400,
|
|
15086
|
+
auth: 200,
|
|
15087
|
+
code: 6e3,
|
|
15088
|
+
callout: 600
|
|
15089
|
+
};
|
|
15090
|
+
str = (v, max, min = 1) => typeof v === "string" && v.trim().length >= min && v.length <= max;
|
|
15091
|
+
h2 = (t) => t ? `## ${t.trim()}` : null;
|
|
14363
15092
|
asRecord = (v) => v;
|
|
14364
15093
|
}
|
|
14365
15094
|
});
|
|
@@ -14397,10 +15126,10 @@ async function loadDokuData(brainId, root) {
|
|
|
14397
15126
|
relations: ["changes", "affects", "mentions"]
|
|
14398
15127
|
});
|
|
14399
15128
|
const changes = neighbors.filter(
|
|
14400
|
-
(
|
|
15129
|
+
(n2) => n2.id !== feature.id && n2.type === "event"
|
|
14401
15130
|
);
|
|
14402
15131
|
const linkedDecisions = neighbors.filter(
|
|
14403
|
-
(
|
|
15132
|
+
(n2) => n2.id !== feature.id && n2.type === "decision"
|
|
14404
15133
|
);
|
|
14405
15134
|
const linkedModules = neighbors.filter(isModule).map(moduleName);
|
|
14406
15135
|
const locations = [
|
|
@@ -14419,7 +15148,14 @@ async function loadDokuData(brainId, root) {
|
|
|
14419
15148
|
feature.id,
|
|
14420
15149
|
...changes.map((c) => c.id)
|
|
14421
15150
|
]);
|
|
14422
|
-
return {
|
|
15151
|
+
return {
|
|
15152
|
+
feature,
|
|
15153
|
+
changes,
|
|
15154
|
+
decisions,
|
|
15155
|
+
locations,
|
|
15156
|
+
evidenceUris,
|
|
15157
|
+
linkedDecisionIds: new Set(linkedDecisions.map((d) => d.id))
|
|
15158
|
+
};
|
|
14423
15159
|
})
|
|
14424
15160
|
);
|
|
14425
15161
|
const days = (await Promise.all(
|
|
@@ -14457,20 +15193,50 @@ async function loadDokuData(brainId, root) {
|
|
|
14457
15193
|
scripts
|
|
14458
15194
|
};
|
|
14459
15195
|
}
|
|
14460
|
-
function
|
|
15196
|
+
function aiState(m) {
|
|
15197
|
+
return {
|
|
15198
|
+
factHash: typeof m?.factHash === "string" ? m.factHash : "",
|
|
15199
|
+
aiIncomplete: m?.aiIncomplete === true,
|
|
15200
|
+
aiAttempts: typeof m?.aiAttempts === "number" ? m.aiAttempts : 0
|
|
15201
|
+
};
|
|
15202
|
+
}
|
|
15203
|
+
function aiComplete(kind, m) {
|
|
15204
|
+
if (!m) return false;
|
|
15205
|
+
const text = (v) => typeof v === "string" && v.trim().length > 0;
|
|
15206
|
+
switch (kind) {
|
|
15207
|
+
case "entry":
|
|
15208
|
+
case "architecture":
|
|
15209
|
+
return text(m.narrative);
|
|
15210
|
+
case "feature":
|
|
15211
|
+
return (Boolean(m.presentation) || text(m.body)) && text(m.category);
|
|
15212
|
+
case "category":
|
|
15213
|
+
return text(m.body);
|
|
15214
|
+
default:
|
|
15215
|
+
return true;
|
|
15216
|
+
}
|
|
15217
|
+
}
|
|
15218
|
+
function parseDocPage(raw, categoryKeys, ctx) {
|
|
15219
|
+
const bad = (error2) => ({ page: null, error: error2, dropped: [] });
|
|
14461
15220
|
const match = raw.match(/\{[\s\S]*\}/);
|
|
14462
|
-
if (!match) return
|
|
15221
|
+
if (!match) return bad("no JSON");
|
|
15222
|
+
let obj;
|
|
14463
15223
|
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 };
|
|
15224
|
+
obj = JSON.parse(match[0]);
|
|
14471
15225
|
} catch {
|
|
14472
|
-
return
|
|
14473
|
-
}
|
|
15226
|
+
return bad("invalid JSON (truncated reply?)");
|
|
15227
|
+
}
|
|
15228
|
+
const title = typeof obj.title === "string" ? obj.title.trim() : "";
|
|
15229
|
+
const category = typeof obj.category === "string" ? obj.category.trim() : "";
|
|
15230
|
+
if (!title || title.length > 120) return bad("title: missing or too long");
|
|
15231
|
+
if (!categoryKeys.includes(category)) return bad("category: unknown key");
|
|
15232
|
+
const icon = typeof obj.icon === "string" && isEmoji(obj.icon.trim()) ? obj.icon.trim() : null;
|
|
15233
|
+
const { presentation, error, dropped } = parsePresentation(obj, ctx);
|
|
15234
|
+
if (!presentation) return bad(error ?? "presentation invalid");
|
|
15235
|
+
return {
|
|
15236
|
+
page: { title, category, presentation, icon },
|
|
15237
|
+
error: null,
|
|
15238
|
+
dropped
|
|
15239
|
+
};
|
|
14474
15240
|
}
|
|
14475
15241
|
function parseTaxonomy(raw) {
|
|
14476
15242
|
const match = raw.match(/\{[\s\S]*\}/);
|
|
@@ -14485,25 +15251,53 @@ function parseTaxonomy(raw) {
|
|
|
14485
15251
|
const name = typeof r.name === "string" ? r.name.trim() : "";
|
|
14486
15252
|
const description = typeof r.description === "string" ? r.description.trim() : "";
|
|
14487
15253
|
if (!/^[a-z0-9-]{2,40}$/.test(key) || !name || !description) return null;
|
|
15254
|
+
if (name.length > 80 || description.length > 240) return null;
|
|
14488
15255
|
out.push({ key, name, description });
|
|
14489
15256
|
}
|
|
14490
|
-
if (out.length <
|
|
15257
|
+
if (out.length < 1 || out.length > 40) return null;
|
|
14491
15258
|
if (new Set(out.map((c) => c.key)).size !== out.length) return null;
|
|
14492
15259
|
return out;
|
|
14493
15260
|
} catch {
|
|
14494
15261
|
return null;
|
|
14495
15262
|
}
|
|
14496
15263
|
}
|
|
15264
|
+
function parseRefile(raw, slugs, keys) {
|
|
15265
|
+
let pages = null;
|
|
15266
|
+
const match = raw.match(/\{[\s\S]*\}/);
|
|
15267
|
+
if (match) {
|
|
15268
|
+
try {
|
|
15269
|
+
const obj = JSON.parse(match[0]);
|
|
15270
|
+
if (typeof obj.pages === "object" && obj.pages !== null && !Array.isArray(obj.pages))
|
|
15271
|
+
pages = obj.pages;
|
|
15272
|
+
} catch {
|
|
15273
|
+
}
|
|
15274
|
+
}
|
|
15275
|
+
if (!pages) {
|
|
15276
|
+
pages = {};
|
|
15277
|
+
for (const m of raw.matchAll(/"([^"\n]+)"\s*:\s*"([^"\n]*)"/g))
|
|
15278
|
+
pages[m[1]] = m[2];
|
|
15279
|
+
}
|
|
15280
|
+
const known = new Set(keys);
|
|
15281
|
+
const out = {};
|
|
15282
|
+
for (const slug of slugs) {
|
|
15283
|
+
const key = pages[slug];
|
|
15284
|
+
if (typeof key === "string" && known.has(key.trim()))
|
|
15285
|
+
out[slug] = key.trim();
|
|
15286
|
+
}
|
|
15287
|
+
return Object.keys(out).length ? out : null;
|
|
15288
|
+
}
|
|
14497
15289
|
function parseCategoryBody(raw, memberSlugs) {
|
|
14498
15290
|
let body = raw.trim();
|
|
14499
|
-
if (!body || body.length >
|
|
15291
|
+
if (!body || body.length > 2e4) return null;
|
|
14500
15292
|
const known = new Set(memberSlugs);
|
|
14501
15293
|
body = body.replace(
|
|
14502
15294
|
/\[([^\]]+)\]\(page:([^)\s]+)\)/g,
|
|
14503
15295
|
(whole, text, slug) => known.has(slug) ? whole : text
|
|
14504
15296
|
);
|
|
14505
|
-
|
|
14506
|
-
|
|
15297
|
+
body = body.replace(
|
|
15298
|
+
/\(page:([^)\s]+)\)/g,
|
|
15299
|
+
(whole, slug) => known.has(slug) ? whole : ""
|
|
15300
|
+
);
|
|
14507
15301
|
return body;
|
|
14508
15302
|
}
|
|
14509
15303
|
function archNotesInput(model) {
|
|
@@ -14597,13 +15391,19 @@ function parseArchFlows(raw, knownModules) {
|
|
|
14597
15391
|
const errors = [];
|
|
14598
15392
|
for (const cand of obj.flows.slice(0, 4)) {
|
|
14599
15393
|
const f = cand;
|
|
15394
|
+
if (f && typeof f === "object") {
|
|
15395
|
+
const x = f.explanation;
|
|
15396
|
+
if (typeof x === "string" && x.trim() && x.length <= 4e3)
|
|
15397
|
+
f.explanation = x.trim();
|
|
15398
|
+
else delete f.explanation;
|
|
15399
|
+
}
|
|
14600
15400
|
if (Array.isArray(f?.nodes)) {
|
|
14601
|
-
for (const
|
|
14602
|
-
if (Array.isArray(
|
|
14603
|
-
|
|
15401
|
+
for (const n2 of f.nodes) {
|
|
15402
|
+
if (Array.isArray(n2?.evidence)) {
|
|
15403
|
+
n2.evidence = n2.evidence.filter(
|
|
14604
15404
|
(p) => typeof p === "string" && knownModules.has(p)
|
|
14605
15405
|
);
|
|
14606
|
-
if (
|
|
15406
|
+
if (n2.evidence.length === 0) delete n2.evidence;
|
|
14607
15407
|
}
|
|
14608
15408
|
}
|
|
14609
15409
|
}
|
|
@@ -14627,12 +15427,12 @@ function parseFeatureFlow(raw, slug, knownPaths) {
|
|
|
14627
15427
|
const f = obj.flow;
|
|
14628
15428
|
f.slug = `${slug.slice(0, 59)}-flow`;
|
|
14629
15429
|
if (Array.isArray(f?.nodes)) {
|
|
14630
|
-
for (const
|
|
14631
|
-
if (Array.isArray(
|
|
14632
|
-
|
|
15430
|
+
for (const n2 of f.nodes) {
|
|
15431
|
+
if (Array.isArray(n2?.evidence)) {
|
|
15432
|
+
n2.evidence = n2.evidence.filter(
|
|
14633
15433
|
(p) => typeof p === "string" && knownPaths.has(p)
|
|
14634
15434
|
);
|
|
14635
|
-
if (
|
|
15435
|
+
if (n2.evidence.length === 0) delete n2.evidence;
|
|
14636
15436
|
}
|
|
14637
15437
|
}
|
|
14638
15438
|
}
|
|
@@ -14657,7 +15457,7 @@ function parseFlow(raw, configKeys) {
|
|
|
14657
15457
|
const edges = obj.edges;
|
|
14658
15458
|
if (!Array.isArray(edges) || edges.length === 0 || !edges.every(
|
|
14659
15459
|
(e) => Array.isArray(e) && e.length === 2 && e.every(
|
|
14660
|
-
(
|
|
15460
|
+
(n2) => Number.isInteger(n2) && n2 >= 0 && n2 < steps.length
|
|
14661
15461
|
) && e[0] !== e[1]
|
|
14662
15462
|
))
|
|
14663
15463
|
return bad("edges: need valid [from,to] index pairs");
|
|
@@ -14705,9 +15505,16 @@ async function generateDokuInner(brainId, opts = {}) {
|
|
|
14705
15505
|
const repo = brainRepo(brainId);
|
|
14706
15506
|
const meta = new Map((await repo.docPageMeta()).map((m) => [m.slug, m]));
|
|
14707
15507
|
const storedBySlug = /* @__PURE__ */ new Map();
|
|
15508
|
+
const storedRows = /* @__PURE__ */ new Map();
|
|
14708
15509
|
try {
|
|
14709
|
-
for (const p of await repo.latestDocPages())
|
|
15510
|
+
for (const p of await repo.latestDocPages()) {
|
|
14710
15511
|
storedBySlug.set(p.slug, p.model);
|
|
15512
|
+
storedRows.set(p.slug, {
|
|
15513
|
+
title: p.title,
|
|
15514
|
+
model: p.model,
|
|
15515
|
+
markdown: p.markdown
|
|
15516
|
+
});
|
|
15517
|
+
}
|
|
14711
15518
|
} catch {
|
|
14712
15519
|
}
|
|
14713
15520
|
const takenTitle = /* @__PURE__ */ new Map();
|
|
@@ -14726,10 +15533,11 @@ async function generateDokuInner(brainId, opts = {}) {
|
|
|
14726
15533
|
const stored = storedBySlug.get("index")?.categories;
|
|
14727
15534
|
if (Array.isArray(stored) && stored.length) categories = stored;
|
|
14728
15535
|
}
|
|
15536
|
+
const storedTaxonomy = JSON.stringify(categories);
|
|
14729
15537
|
const hasNewFeature = pages.some(
|
|
14730
15538
|
(p) => p.kind === "feature" && !meta.get(p.slug)
|
|
14731
15539
|
);
|
|
14732
|
-
if (opts.llm && (categories.length === 0 || hasNewFeature)) {
|
|
15540
|
+
if (opts.llm && (categories.length === 0 || hasNewFeature || opts.retaxonomy)) {
|
|
14733
15541
|
try {
|
|
14734
15542
|
const next = parseTaxonomy(
|
|
14735
15543
|
await opts.llm.complete({
|
|
@@ -14743,7 +15551,9 @@ async function generateDokuInner(brainId, opts = {}) {
|
|
|
14743
15551
|
(f) => `- ${f.title}: ${firstSentence(f.what)}`
|
|
14744
15552
|
)
|
|
14745
15553
|
].join("\n"),
|
|
14746
|
-
|
|
15554
|
+
// Room for a large product's taxonomy; an invalid or clipped reply keeps the
|
|
15555
|
+
// stored one (parseTaxonomy returns null).
|
|
15556
|
+
maxTokens: 3e3
|
|
14747
15557
|
})
|
|
14748
15558
|
);
|
|
14749
15559
|
if (next) categories = next;
|
|
@@ -14751,42 +15561,82 @@ async function generateDokuInner(brainId, opts = {}) {
|
|
|
14751
15561
|
}
|
|
14752
15562
|
}
|
|
14753
15563
|
const categoryKeys = categories.map((c) => c.key);
|
|
15564
|
+
const taxonomyChanged = JSON.stringify(categories) !== storedTaxonomy;
|
|
15565
|
+
const endpointFacts = [];
|
|
15566
|
+
for (const [slug, m] of storedBySlug) {
|
|
15567
|
+
const p = m.presentation;
|
|
15568
|
+
if (!Array.isArray(p?.blocks)) continue;
|
|
15569
|
+
for (const b of p.blocks)
|
|
15570
|
+
if (b.kind === "endpoint")
|
|
15571
|
+
endpointFacts.push({
|
|
15572
|
+
slug,
|
|
15573
|
+
title: typeof m.docTitle === "string" ? m.docTitle : slug,
|
|
15574
|
+
method: b.method,
|
|
15575
|
+
path: b.path,
|
|
15576
|
+
summary: b.summary,
|
|
15577
|
+
...b.auth ? { auth: b.auth } : {}
|
|
15578
|
+
});
|
|
15579
|
+
}
|
|
14754
15580
|
let written = 0;
|
|
14755
15581
|
let polished = false;
|
|
14756
15582
|
const seen = /* @__PURE__ */ new Set();
|
|
14757
15583
|
const featureInfo = [];
|
|
15584
|
+
const save = async (input) => {
|
|
15585
|
+
await repo.saveDocPage(input);
|
|
15586
|
+
storedRows.set(input.slug, {
|
|
15587
|
+
title: input.title,
|
|
15588
|
+
model: input.model,
|
|
15589
|
+
markdown: input.markdown
|
|
15590
|
+
});
|
|
15591
|
+
storedBySlug.set(input.slug, input.model);
|
|
15592
|
+
written++;
|
|
15593
|
+
};
|
|
14758
15594
|
for (const page of pages) {
|
|
14759
15595
|
seen.add(page.slug);
|
|
14760
15596
|
const rawStructure = page.kind === "architecture" ? (() => {
|
|
14761
15597
|
const raw = page.model.raw;
|
|
14762
15598
|
return raw ? sha1(JSON.stringify([raw.modules, raw.edges])) : "";
|
|
14763
15599
|
})() : "";
|
|
15600
|
+
const factHash = sha1(page.markdown);
|
|
14764
15601
|
const sourceHash = sha1(
|
|
14765
|
-
page.markdown + (page.kind === "entry" ? "
|
|
14766
|
-
//
|
|
14767
|
-
|
|
15602
|
+
page.markdown + (page.kind === "entry" ? "v4" : page.kind === "feature" ? (
|
|
15603
|
+
// f7: the block presentation model (docs redesign) regenerates every
|
|
15604
|
+
// feature page once; run with `only` first for a representative sample.
|
|
15605
|
+
"f7"
|
|
14768
15606
|
) : page.kind === "architecture" ? (
|
|
14769
|
-
//
|
|
14770
|
-
"
|
|
15607
|
+
// a6: flow explanations + full markdown (docs hubs) regenerate the page once.
|
|
15608
|
+
"a6" + rawStructure
|
|
14771
15609
|
) : "")
|
|
14772
15610
|
);
|
|
14773
15611
|
const prev = meta.get(page.slug);
|
|
14774
|
-
|
|
15612
|
+
const stored = storedBySlug.get(page.slug);
|
|
15613
|
+
const state = aiState(stored);
|
|
15614
|
+
const required = Boolean(opts.llm && page.polishable);
|
|
15615
|
+
const outsideSample = Boolean(opts.only && !opts.only.includes(page.slug));
|
|
15616
|
+
const unchanged = Boolean(
|
|
15617
|
+
prev && !prev.deleted && prev.source_hash === sourceHash
|
|
15618
|
+
);
|
|
15619
|
+
const retry = required && unchanged && (state.aiIncomplete || !aiComplete(page.kind, stored)) && (state.aiAttempts < MAX_AI_ATTEMPTS || Boolean(opts.forceRetry));
|
|
15620
|
+
const entryTaxonomy = page.kind === "entry" && taxonomyChanged;
|
|
15621
|
+
if (prev && !prev.deleted && (outsideSample || unchanged && !storedCollisions.has(page.slug) && !retry && !entryTaxonomy)) {
|
|
14775
15622
|
if (page.kind === "feature") {
|
|
14776
|
-
const
|
|
15623
|
+
const s = stored;
|
|
14777
15624
|
const f = page.model;
|
|
14778
15625
|
featureInfo.push({
|
|
14779
15626
|
slug: page.slug,
|
|
14780
|
-
title: typeof
|
|
15627
|
+
title: typeof s?.docTitle === "string" ? s.docTitle : page.title,
|
|
14781
15628
|
what: f.what,
|
|
14782
15629
|
updated: f.updated,
|
|
14783
|
-
category: typeof
|
|
15630
|
+
category: typeof s?.category === "string" ? s.category : null
|
|
14784
15631
|
});
|
|
14785
15632
|
}
|
|
14786
15633
|
continue;
|
|
14787
15634
|
}
|
|
15635
|
+
const sameFacts = stored !== void 0 && state.factHash === factHash;
|
|
14788
15636
|
let markdown = page.markdown;
|
|
14789
15637
|
let model = page.model;
|
|
15638
|
+
let icon = null;
|
|
15639
|
+
let ok2 = !required;
|
|
14790
15640
|
if (opts.llm && page.polishable && page.kind === "architecture") {
|
|
14791
15641
|
try {
|
|
14792
15642
|
const { notes } = parseArchNotes(
|
|
@@ -14800,6 +15650,7 @@ async function generateDokuInner(brainId, opts = {}) {
|
|
|
14800
15650
|
if (notes) {
|
|
14801
15651
|
model = { ...model, narrative: notes.narrative, roles: notes.roles };
|
|
14802
15652
|
polished = true;
|
|
15653
|
+
ok2 = true;
|
|
14803
15654
|
}
|
|
14804
15655
|
} catch {
|
|
14805
15656
|
}
|
|
@@ -14818,61 +15669,47 @@ async function generateDokuInner(brainId, opts = {}) {
|
|
|
14818
15669
|
}
|
|
14819
15670
|
} catch {
|
|
14820
15671
|
}
|
|
15672
|
+
if (!ok2 && sameFacts) {
|
|
15673
|
+
const s = stored;
|
|
15674
|
+
const kept = model;
|
|
15675
|
+
model = {
|
|
15676
|
+
...model,
|
|
15677
|
+
...typeof s.narrative === "string" ? { narrative: s.narrative, roles: s.roles } : {},
|
|
15678
|
+
...!kept.flows && Array.isArray(s.flows) ? { flows: s.flows } : {}
|
|
15679
|
+
};
|
|
15680
|
+
}
|
|
15681
|
+
const arch = model;
|
|
15682
|
+
if (arch.narrative || arch.flows?.length)
|
|
15683
|
+
markdown = renderArchitecturePage(doku, {
|
|
15684
|
+
narrative: arch.narrative ?? null,
|
|
15685
|
+
flows: arch.flows ?? []
|
|
15686
|
+
});
|
|
14821
15687
|
} else if (page.kind === "entry") {
|
|
14822
15688
|
model = { ...page.model, categories };
|
|
14823
15689
|
if (opts.llm && page.polishable) {
|
|
14824
15690
|
try {
|
|
14825
|
-
const
|
|
15691
|
+
const narrative2 = (await opts.llm.complete({
|
|
14826
15692
|
system: ENTRY_NARRATIVE_SYSTEM,
|
|
14827
15693
|
prompt: page.markdown,
|
|
14828
|
-
maxTokens:
|
|
15694
|
+
maxTokens: 2500
|
|
14829
15695
|
})).trim();
|
|
14830
|
-
if (
|
|
14831
|
-
model = { ...model, narrative };
|
|
14832
|
-
markdown = renderEntryPage(doku, "brainjar-doku/", narrative);
|
|
15696
|
+
if (narrative2) {
|
|
15697
|
+
model = { ...model, narrative: narrative2 };
|
|
14833
15698
|
polished = true;
|
|
15699
|
+
ok2 = true;
|
|
14834
15700
|
}
|
|
14835
15701
|
} catch {
|
|
14836
15702
|
}
|
|
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;
|
|
15703
|
+
if (!ok2 && sameFacts) {
|
|
15704
|
+
const s = stored;
|
|
15705
|
+
if (typeof s.narrative === "string" && s.narrative.trim())
|
|
15706
|
+
model = { ...model, narrative: s.narrative };
|
|
14873
15707
|
}
|
|
14874
|
-
} catch {
|
|
14875
15708
|
}
|
|
15709
|
+
const narrative = model.narrative;
|
|
15710
|
+
if (narrative)
|
|
15711
|
+
markdown = renderEntryPage(doku, "brainjar-doku/", narrative);
|
|
15712
|
+
} else if (opts.llm && page.polishable) {
|
|
14876
15713
|
try {
|
|
14877
15714
|
const { flow } = parseFlow(
|
|
14878
15715
|
await opts.llm.complete({
|
|
@@ -14912,7 +15749,123 @@ async function generateDokuInner(brainId, opts = {}) {
|
|
|
14912
15749
|
console.warn(`doku: feature flow failed (${page.slug}):`, e);
|
|
14913
15750
|
}
|
|
14914
15751
|
}
|
|
15752
|
+
try {
|
|
15753
|
+
const diagrams = model;
|
|
15754
|
+
const shared = endpointFacts.filter(
|
|
15755
|
+
(e) => e.slug !== page.slug && page.markdown.includes(e.path)
|
|
15756
|
+
);
|
|
15757
|
+
const sharedLines = shared.map(
|
|
15758
|
+
(e) => `- ${e.method} ${e.path}: ${e.summary}${e.auth ? ` Authentication: ${e.auth}` : ""} (page: ${e.slug}, "${e.title}")`
|
|
15759
|
+
);
|
|
15760
|
+
const ctx = {
|
|
15761
|
+
factSheet: [page.markdown, ...sharedLines].join("\n"),
|
|
15762
|
+
hasFlowDiagram: Boolean(diagrams.flowDiagram),
|
|
15763
|
+
hasFlow: Boolean(diagrams.flow)
|
|
15764
|
+
};
|
|
15765
|
+
const prompt = [
|
|
15766
|
+
"Categories (pick exactly one key):",
|
|
15767
|
+
...categories.length ? categories.map((c) => `- ${c.key}: ${c.name} (${c.description})`) : ["- general: General (everything)"],
|
|
15768
|
+
"",
|
|
15769
|
+
"Available diagrams (the only allowed flow block sources):",
|
|
15770
|
+
...ctx.hasFlowDiagram ? [
|
|
15771
|
+
`- flowDiagram: "${diagrams.flowDiagram.title}" (the feature's runtime flow, lanes and stations)`
|
|
15772
|
+
] : [],
|
|
15773
|
+
...ctx.hasFlow && !ctx.hasFlowDiagram ? ["- flow: the feature's runtime steps as a simple chain"] : [],
|
|
15774
|
+
...!ctx.hasFlowDiagram && !ctx.hasFlow ? ["- (none)"] : [],
|
|
15775
|
+
"",
|
|
15776
|
+
...sharedLines.length ? [
|
|
15777
|
+
"Documented elsewhere for the same endpoint (proven facts; reuse them, link the page):",
|
|
15778
|
+
...sharedLines,
|
|
15779
|
+
""
|
|
15780
|
+
] : [],
|
|
15781
|
+
"Fact sheet:",
|
|
15782
|
+
page.markdown
|
|
15783
|
+
].join("\n");
|
|
15784
|
+
const keys = categoryKeys.length ? categoryKeys : ["general"];
|
|
15785
|
+
let synth = parseDocPage(
|
|
15786
|
+
await opts.llm.complete({
|
|
15787
|
+
system: PAGE_SYSTEM,
|
|
15788
|
+
prompt,
|
|
15789
|
+
maxTokens: 7e3
|
|
15790
|
+
}),
|
|
15791
|
+
keys,
|
|
15792
|
+
ctx
|
|
15793
|
+
);
|
|
15794
|
+
if (!synth.page) {
|
|
15795
|
+
console.warn(
|
|
15796
|
+
`doku: page rejected (${page.slug}): ${synth.error}; retrying once`
|
|
15797
|
+
);
|
|
15798
|
+
synth = parseDocPage(
|
|
15799
|
+
await opts.llm.complete({
|
|
15800
|
+
system: PAGE_SYSTEM,
|
|
15801
|
+
prompt,
|
|
15802
|
+
maxTokens: 1e4
|
|
15803
|
+
}),
|
|
15804
|
+
keys,
|
|
15805
|
+
ctx
|
|
15806
|
+
);
|
|
15807
|
+
}
|
|
15808
|
+
if (synth.dropped.length)
|
|
15809
|
+
console.warn(
|
|
15810
|
+
`doku: blocks dropped (${page.slug}): ${synth.dropped.join("; ")}`
|
|
15811
|
+
);
|
|
15812
|
+
if (synth.page) {
|
|
15813
|
+
const title = pickDocTitle(
|
|
15814
|
+
synth.page.title,
|
|
15815
|
+
page.title,
|
|
15816
|
+
page.slug,
|
|
15817
|
+
takenTitle
|
|
15818
|
+
);
|
|
15819
|
+
const f = {
|
|
15820
|
+
...model,
|
|
15821
|
+
presentation: synth.page.presentation,
|
|
15822
|
+
docTitle: title,
|
|
15823
|
+
category: synth.page.category
|
|
15824
|
+
};
|
|
15825
|
+
f.body = pageBody(f);
|
|
15826
|
+
model = f;
|
|
15827
|
+
markdown = renderFeaturePage(f);
|
|
15828
|
+
page.title = title;
|
|
15829
|
+
icon = synth.page.icon;
|
|
15830
|
+
polished = true;
|
|
15831
|
+
ok2 = true;
|
|
15832
|
+
} else {
|
|
15833
|
+
console.warn(
|
|
15834
|
+
`doku: page rejected twice (${page.slug}): ${synth.error}`
|
|
15835
|
+
);
|
|
15836
|
+
}
|
|
15837
|
+
} catch (e) {
|
|
15838
|
+
console.warn(`doku: page synthesis failed (${page.slug}):`, e);
|
|
15839
|
+
}
|
|
15840
|
+
if (!ok2 && sameFacts && aiComplete("feature", stored)) {
|
|
15841
|
+
const s = stored;
|
|
15842
|
+
const fresh = model;
|
|
15843
|
+
const f = {
|
|
15844
|
+
...model,
|
|
15845
|
+
...s.presentation ? { presentation: s.presentation } : {},
|
|
15846
|
+
body: s.body ?? null,
|
|
15847
|
+
docTitle: s.docTitle ?? null,
|
|
15848
|
+
category: s.category ?? null,
|
|
15849
|
+
...!fresh.flow && s.flow ? { flow: s.flow } : {},
|
|
15850
|
+
...!fresh.flowDiagram && s.flowDiagram ? { flowDiagram: s.flowDiagram } : {}
|
|
15851
|
+
};
|
|
15852
|
+
model = f;
|
|
15853
|
+
markdown = renderFeaturePage(f);
|
|
15854
|
+
if (f.docTitle) page.title = f.docTitle;
|
|
15855
|
+
}
|
|
15856
|
+
if (!ok2) {
|
|
15857
|
+
const s = stored;
|
|
15858
|
+
const m = model;
|
|
15859
|
+
if (typeof m.category !== "string" && typeof s?.category === "string" && categoryKeys.includes(s.category))
|
|
15860
|
+
model = { ...model, category: s.category };
|
|
15861
|
+
}
|
|
14915
15862
|
}
|
|
15863
|
+
model = {
|
|
15864
|
+
...model,
|
|
15865
|
+
factHash,
|
|
15866
|
+
aiIncomplete: required ? !ok2 : !aiComplete(page.kind, model),
|
|
15867
|
+
aiAttempts: required && !ok2 ? (sameFacts ? state.aiAttempts : 0) + 1 : 0
|
|
15868
|
+
};
|
|
14916
15869
|
if (page.kind === "feature") {
|
|
14917
15870
|
const m = model;
|
|
14918
15871
|
const f = page.model;
|
|
@@ -14924,15 +15877,68 @@ async function generateDokuInner(brainId, opts = {}) {
|
|
|
14924
15877
|
category: typeof m.category === "string" ? m.category : null
|
|
14925
15878
|
});
|
|
14926
15879
|
}
|
|
14927
|
-
await
|
|
15880
|
+
await save({
|
|
14928
15881
|
slug: page.slug,
|
|
14929
15882
|
kind: page.kind,
|
|
14930
15883
|
title: page.title,
|
|
14931
15884
|
model,
|
|
14932
15885
|
markdown,
|
|
14933
|
-
sourceHash
|
|
15886
|
+
sourceHash,
|
|
15887
|
+
icon
|
|
14934
15888
|
});
|
|
14935
|
-
|
|
15889
|
+
}
|
|
15890
|
+
if (opts.llm && categoryKeys.length) {
|
|
15891
|
+
const candidates = featureInfo.filter(
|
|
15892
|
+
(f) => taxonomyChanged || opts.retaxonomy || !f.category || !categoryKeys.includes(f.category)
|
|
15893
|
+
);
|
|
15894
|
+
if (candidates.length) {
|
|
15895
|
+
try {
|
|
15896
|
+
const filed = parseRefile(
|
|
15897
|
+
await opts.llm.complete({
|
|
15898
|
+
system: REFILE_SYSTEM,
|
|
15899
|
+
prompt: [
|
|
15900
|
+
"Categories:",
|
|
15901
|
+
...categories.map(
|
|
15902
|
+
(c) => `- ${c.key}: ${c.name} (${c.description})`
|
|
15903
|
+
),
|
|
15904
|
+
"",
|
|
15905
|
+
"Pages:",
|
|
15906
|
+
...candidates.map(
|
|
15907
|
+
(f) => `- ${f.slug}: ${f.title}: ${firstSentence(f.what)}`
|
|
15908
|
+
)
|
|
15909
|
+
].join("\n"),
|
|
15910
|
+
maxTokens: Math.min(16e3, 300 + candidates.length * 60)
|
|
15911
|
+
}),
|
|
15912
|
+
candidates.map((f) => f.slug),
|
|
15913
|
+
categoryKeys
|
|
15914
|
+
);
|
|
15915
|
+
if (filed) {
|
|
15916
|
+
const unfiled = candidates.filter((f) => !filed[f.slug]).length;
|
|
15917
|
+
if (unfiled)
|
|
15918
|
+
console.warn(
|
|
15919
|
+
`doku: re-filing left ${unfiled} of ${candidates.length} pages as they were`
|
|
15920
|
+
);
|
|
15921
|
+
for (const f of candidates) {
|
|
15922
|
+
const key = filed[f.slug];
|
|
15923
|
+
if (!key || key === f.category) continue;
|
|
15924
|
+
const row = storedRows.get(f.slug);
|
|
15925
|
+
const prev = meta.get(f.slug);
|
|
15926
|
+
if (!row) continue;
|
|
15927
|
+
f.category = key;
|
|
15928
|
+
await save({
|
|
15929
|
+
slug: f.slug,
|
|
15930
|
+
kind: "feature",
|
|
15931
|
+
title: row.title,
|
|
15932
|
+
model: { ...row.model, category: key },
|
|
15933
|
+
markdown: row.markdown,
|
|
15934
|
+
sourceHash: prev?.source_hash ?? sha1(row.markdown)
|
|
15935
|
+
});
|
|
15936
|
+
}
|
|
15937
|
+
} else console.warn("doku: re-filing rejected (invalid reply)");
|
|
15938
|
+
} catch (e) {
|
|
15939
|
+
console.warn("doku: re-filing failed:", e);
|
|
15940
|
+
}
|
|
15941
|
+
}
|
|
14936
15942
|
}
|
|
14937
15943
|
for (const c of categories) {
|
|
14938
15944
|
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 +15958,18 @@ async function generateDokuInner(brainId, opts = {}) {
|
|
|
14952
15958
|
body: null,
|
|
14953
15959
|
members
|
|
14954
15960
|
};
|
|
14955
|
-
const
|
|
15961
|
+
const factHash = sha1(renderCategoryPage(catModel));
|
|
15962
|
+
const sourceHash = sha1(renderCategoryPage(catModel) + "c2");
|
|
14956
15963
|
const prev = meta.get(slug);
|
|
14957
|
-
const
|
|
14958
|
-
|
|
14959
|
-
|
|
15964
|
+
const stored = storedBySlug.get(slug);
|
|
15965
|
+
const state = aiState(stored);
|
|
15966
|
+
const unchanged = Boolean(
|
|
15967
|
+
prev && !prev.deleted && prev.source_hash === sourceHash
|
|
15968
|
+
);
|
|
15969
|
+
const retry = Boolean(opts.llm) && unchanged && !aiComplete("category", stored) && (state.aiAttempts < MAX_AI_ATTEMPTS || Boolean(opts.forceRetry));
|
|
15970
|
+
if (unchanged && !retry) continue;
|
|
15971
|
+
const sameFacts = stored !== void 0 && state.factHash === factHash;
|
|
15972
|
+
let ok2 = !opts.llm;
|
|
14960
15973
|
if (opts.llm) {
|
|
14961
15974
|
try {
|
|
14962
15975
|
const body = parseCategoryBody(
|
|
@@ -14971,28 +15984,37 @@ async function generateDokuInner(brainId, opts = {}) {
|
|
|
14971
15984
|
(m) => `- ${m.slug}: ${m.title}: ${firstSentence(m.what)}`
|
|
14972
15985
|
)
|
|
14973
15986
|
].join("\n"),
|
|
14974
|
-
//
|
|
14975
|
-
//
|
|
14976
|
-
maxTokens:
|
|
15987
|
+
// Structured markdown for a large area needs room; a clipped body would
|
|
15988
|
+
// lose its last sections.
|
|
15989
|
+
maxTokens: 4e3
|
|
14977
15990
|
}),
|
|
14978
15991
|
members.map((m) => m.slug)
|
|
14979
15992
|
);
|
|
14980
15993
|
if (body) {
|
|
14981
15994
|
catModel.body = body;
|
|
14982
15995
|
polished = true;
|
|
15996
|
+
ok2 = true;
|
|
14983
15997
|
}
|
|
14984
15998
|
} catch {
|
|
14985
15999
|
}
|
|
16000
|
+
if (!ok2 && sameFacts) {
|
|
16001
|
+
const s = stored;
|
|
16002
|
+
if (typeof s.body === "string" && s.body.trim()) catModel.body = s.body;
|
|
16003
|
+
}
|
|
14986
16004
|
}
|
|
14987
|
-
await
|
|
16005
|
+
await save({
|
|
14988
16006
|
slug,
|
|
14989
16007
|
kind: "category",
|
|
14990
16008
|
title: c.name,
|
|
14991
|
-
model:
|
|
16009
|
+
model: {
|
|
16010
|
+
...catModel,
|
|
16011
|
+
factHash,
|
|
16012
|
+
aiIncomplete: opts.llm ? !ok2 : !catModel.body,
|
|
16013
|
+
aiAttempts: opts.llm && !ok2 ? (sameFacts ? state.aiAttempts : 0) + 1 : 0
|
|
16014
|
+
},
|
|
14992
16015
|
markdown: renderCategoryPage(catModel),
|
|
14993
16016
|
sourceHash
|
|
14994
16017
|
});
|
|
14995
|
-
written++;
|
|
14996
16018
|
}
|
|
14997
16019
|
let deleted = 0;
|
|
14998
16020
|
for (const [slug, m] of meta) {
|
|
@@ -15061,7 +16083,7 @@ async function exportDoku(brainId, root) {
|
|
|
15061
16083
|
const sync = await writeDokuTree(root, files);
|
|
15062
16084
|
return { pages: files.length, ...sync };
|
|
15063
16085
|
}
|
|
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;
|
|
16086
|
+
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
16087
|
var init_doku = __esm({
|
|
15066
16088
|
"packages/ingest/src/doku.ts"() {
|
|
15067
16089
|
"use strict";
|
|
@@ -15081,41 +16103,57 @@ var init_doku = __esm({
|
|
|
15081
16103
|
moduleName = (o) => String(o.body.name ?? o.title);
|
|
15082
16104
|
isRepoPath = (name) => !/^([a-zA-Z]:[/\\]|[/\\])/.test(name) && !name.includes("node_modules") && !name.includes(".claude/");
|
|
15083
16105
|
normPath = (p) => p.replace(/\\/g, "/").replace(/^\.\//, "").toLowerCase();
|
|
16106
|
+
MAX_AI_ATTEMPTS = 3;
|
|
15084
16107
|
PAGE_SYSTEM = [
|
|
15085
16108
|
"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": "...", "
|
|
16109
|
+
'Respond ONLY with a JSON object of exactly this shape: {"title": "...", "category": "...", "icon": "...", "summary": "...", "blocks": [...]}. No code fences, nothing outside the JSON.',
|
|
16110
|
+
'"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
16111
|
'"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
16112
|
'"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
|
-
"
|
|
16113
|
+
'"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.',
|
|
16114
|
+
'"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:',
|
|
16115
|
+
'{"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.',
|
|
16116
|
+
'{"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.',
|
|
16117
|
+
'{"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.',
|
|
16118
|
+
'{"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.',
|
|
16119
|
+
'{"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.',
|
|
16120
|
+
'{"kind":"callout","tone":"note","text":"..."}: one short prerequisite, effect, or limit. "warning" only for a concrete consequence the reader must avoid; otherwise "note".',
|
|
16121
|
+
`{"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.`,
|
|
16122
|
+
"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.",
|
|
16123
|
+
"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.",
|
|
16124
|
+
"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).",
|
|
16125
|
+
"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
16126
|
PROSE_STYLE
|
|
15094
16127
|
].join(" ");
|
|
15095
16128
|
TAXONOMY_SYSTEM = [
|
|
15096
16129
|
"You maintain the category structure of a software product's documentation.",
|
|
15097
16130
|
"You are given the product's feature list and the EXISTING categories (possibly empty).",
|
|
15098
16131
|
'Respond ONLY with a JSON object of exactly this shape: {"categories": [{"key": "...", "name": "...", "description": "..."}]}. No code fences, nothing outside the JSON.',
|
|
15099
|
-
"
|
|
16132
|
+
"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
16133
|
"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
16134
|
"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
|
|
16135
|
+
'"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
16136
|
PROSE_STYLE
|
|
15104
16137
|
].join(" ");
|
|
16138
|
+
REFILE_SYSTEM = [
|
|
16139
|
+
"You file documentation pages into the given categories of a software product.",
|
|
16140
|
+
'Respond ONLY with a JSON object of exactly this shape: {"pages": {"<slug>": "<category key>"}}. No code fences, nothing outside the JSON.',
|
|
16141
|
+
"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."
|
|
16142
|
+
].join(" ");
|
|
15105
16143
|
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.",
|
|
16144
|
+
"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.",
|
|
16145
|
+
"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.",
|
|
16146
|
+
"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.",
|
|
16147
|
+
"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.",
|
|
16148
|
+
"No H1 heading (the category name renders above). Ground everything in the given summaries; invent nothing.",
|
|
15111
16149
|
PROSE_STYLE
|
|
15112
16150
|
].join(" ");
|
|
15113
16151
|
ENTRY_NARRATIVE_SYSTEM = [
|
|
15114
16152
|
"You write the opening description for a software project's documentation.",
|
|
15115
16153
|
"You are given the documentation entry page (the project's feature list) as markdown.",
|
|
15116
|
-
"Respond
|
|
16154
|
+
"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
16155
|
"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
|
|
16156
|
+
"Ground every statement in the given features; invent nothing, no marketing superlatives, no H1 heading.",
|
|
15119
16157
|
PROSE_STYLE
|
|
15120
16158
|
].join(" ");
|
|
15121
16159
|
FLOW_SYSTEM = [
|
|
@@ -15137,7 +16175,8 @@ var init_doku = __esm({
|
|
|
15137
16175
|
].join(" ");
|
|
15138
16176
|
ARCH_FLOWS_SYSTEM = [
|
|
15139
16177
|
"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": "..."}]}]}.
|
|
16178
|
+
'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.',
|
|
16179
|
+
'"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
16180
|
"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
16181
|
"lanes are 3-5 PHASES of the flow in order (intake, processing, storage, ...), titled in plain words. Never folder names.",
|
|
15143
16182
|
"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).",
|
|
@@ -15301,9 +16340,9 @@ var init_verify_agent = __esm({
|
|
|
15301
16340
|
}
|
|
15302
16341
|
});
|
|
15303
16342
|
|
|
15304
|
-
// packages/ingest/src/verify-
|
|
15305
|
-
var
|
|
15306
|
-
"packages/ingest/src/verify-
|
|
16343
|
+
// packages/ingest/src/verify-start.ts
|
|
16344
|
+
var init_verify_start = __esm({
|
|
16345
|
+
"packages/ingest/src/verify-start.ts"() {
|
|
15307
16346
|
"use strict";
|
|
15308
16347
|
init_src2();
|
|
15309
16348
|
init_src();
|
|
@@ -15312,14 +16351,26 @@ var init_verify_poll = __esm({
|
|
|
15312
16351
|
}
|
|
15313
16352
|
});
|
|
15314
16353
|
|
|
15315
|
-
// packages/ingest/src/verify-
|
|
15316
|
-
var
|
|
15317
|
-
"packages/ingest/src/verify-
|
|
16354
|
+
// packages/ingest/src/verify-poll.ts
|
|
16355
|
+
var init_verify_poll = __esm({
|
|
16356
|
+
"packages/ingest/src/verify-poll.ts"() {
|
|
15318
16357
|
"use strict";
|
|
15319
16358
|
init_src2();
|
|
15320
16359
|
init_src();
|
|
15321
16360
|
init_repo_reader();
|
|
15322
16361
|
init_verify_agent();
|
|
16362
|
+
init_verify_start();
|
|
16363
|
+
}
|
|
16364
|
+
});
|
|
16365
|
+
|
|
16366
|
+
// packages/ingest/src/job-worker.ts
|
|
16367
|
+
var init_job_worker = __esm({
|
|
16368
|
+
"packages/ingest/src/job-worker.ts"() {
|
|
16369
|
+
"use strict";
|
|
16370
|
+
init_src2();
|
|
16371
|
+
init_deepscan();
|
|
16372
|
+
init_routing();
|
|
16373
|
+
init_verify_start();
|
|
15323
16374
|
}
|
|
15324
16375
|
});
|
|
15325
16376
|
|
|
@@ -15401,6 +16452,7 @@ var init_src3 = __esm({
|
|
|
15401
16452
|
init_verify_agent();
|
|
15402
16453
|
init_verify_poll();
|
|
15403
16454
|
init_verify_start();
|
|
16455
|
+
init_job_worker();
|
|
15404
16456
|
init_ask_agent_model();
|
|
15405
16457
|
init_scheduler();
|
|
15406
16458
|
registerConnector(
|
|
@@ -16646,9 +17698,9 @@ function commitShas8(text) {
|
|
|
16646
17698
|
return [...out];
|
|
16647
17699
|
}
|
|
16648
17700
|
function stamp2(ts, now) {
|
|
16649
|
-
const
|
|
16650
|
-
if (typeof
|
|
16651
|
-
return new Date(
|
|
17701
|
+
const n2 = typeof ts === "string" && /^\d+$/.test(ts) ? Number(ts) : ts;
|
|
17702
|
+
if (typeof n2 === "number" && Number.isFinite(n2))
|
|
17703
|
+
return new Date(n2).toISOString();
|
|
16652
17704
|
if (typeof ts === "string" && !Number.isNaN(Date.parse(ts)))
|
|
16653
17705
|
return new Date(ts).toISOString();
|
|
16654
17706
|
return now;
|
|
@@ -16860,16 +17912,12 @@ async function linkForSpool(file) {
|
|
|
16860
17912
|
async function shipSpool(file, link) {
|
|
16861
17913
|
const events = readSpool(file);
|
|
16862
17914
|
if (!events.length) return;
|
|
16863
|
-
|
|
17915
|
+
await captureApi("/capture/events", {
|
|
16864
17916
|
method: "POST",
|
|
16865
|
-
body: {
|
|
16866
|
-
brain: link.brainId,
|
|
16867
|
-
events,
|
|
16868
|
-
author: loadEnv().BRAINJAR_AUTHOR?.trim() || null
|
|
16869
|
-
}
|
|
17917
|
+
body: { brain: link.brainId, events }
|
|
16870
17918
|
});
|
|
16871
17919
|
console.error(
|
|
16872
|
-
`brainjar: session
|
|
17920
|
+
`brainjar: session sent (${events.length} events), the server reads it in the background`
|
|
16873
17921
|
);
|
|
16874
17922
|
}
|
|
16875
17923
|
function parseHookArgs(argv) {
|
|
@@ -16968,23 +18016,19 @@ async function syncRepo(root) {
|
|
|
16968
18016
|
const connector = createConnector("git_local", { cwd: root }, {});
|
|
16969
18017
|
const artifacts = await connector.sync(project.cursor ?? void 0);
|
|
16970
18018
|
if (artifacts.length > 0) {
|
|
16971
|
-
const r = await captureApi(
|
|
16972
|
-
"
|
|
16973
|
-
{
|
|
16974
|
-
|
|
16975
|
-
|
|
16976
|
-
|
|
16977
|
-
|
|
16978
|
-
artifacts,
|
|
16979
|
-
cursor: maxUpdatedAt(artifacts) ?? project.cursor
|
|
16980
|
-
}
|
|
18019
|
+
const r = await captureApi("/capture/artifacts", {
|
|
18020
|
+
method: "POST",
|
|
18021
|
+
body: {
|
|
18022
|
+
brain: link.brainId,
|
|
18023
|
+
sourceId: project.sourceId,
|
|
18024
|
+
artifacts,
|
|
18025
|
+
cursor: maxUpdatedAt(artifacts) ?? project.cursor
|
|
16981
18026
|
}
|
|
16982
|
-
);
|
|
18027
|
+
});
|
|
16983
18028
|
if (r.inserted > 0) {
|
|
16984
|
-
console.error(
|
|
16985
|
-
|
|
16986
|
-
|
|
16987
|
-
}
|
|
18029
|
+
console.error(
|
|
18030
|
+
`brainjar: ${r.inserted} new commits sent, the server reads them in the background`
|
|
18031
|
+
);
|
|
16988
18032
|
const graph = await scanStructure(root);
|
|
16989
18033
|
await captureApi("/capture/structure", {
|
|
16990
18034
|
method: "POST",
|
|
@@ -17041,12 +18085,67 @@ var init_sync2 = __esm({
|
|
|
17041
18085
|
}
|
|
17042
18086
|
});
|
|
17043
18087
|
|
|
18088
|
+
// packages/cli/src/wait.ts
|
|
18089
|
+
function progressLine(p) {
|
|
18090
|
+
const left = p.pending > 0 ? `${n(p.pending)} commits and sessions still to read` : `${n(p.queued + p.running)} capture jobs still running`;
|
|
18091
|
+
const queue = p.ahead > 0 ? ` (${n(p.ahead)} jobs of other projects ahead)` : "";
|
|
18092
|
+
return ` \u2026 ${left}${queue}`;
|
|
18093
|
+
}
|
|
18094
|
+
async function waitForServer(brainId, since, opts = {}) {
|
|
18095
|
+
const poll = opts.poll ?? (() => captureApi(
|
|
18096
|
+
`/capture/progress?brain=${brainId}&since=${encodeURIComponent(since.toISOString())}`
|
|
18097
|
+
));
|
|
18098
|
+
const sleep2 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
18099
|
+
const write = opts.write ?? ((s) => void process.stdout.write(s));
|
|
18100
|
+
const tty = opts.tty ?? Boolean(process.stdout.isTTY);
|
|
18101
|
+
const intervalMs = opts.intervalMs ?? 2e3;
|
|
18102
|
+
let shown = "";
|
|
18103
|
+
let shownAt = 0;
|
|
18104
|
+
let misses = 0;
|
|
18105
|
+
for (; ; ) {
|
|
18106
|
+
let p;
|
|
18107
|
+
try {
|
|
18108
|
+
p = await poll();
|
|
18109
|
+
misses = 0;
|
|
18110
|
+
} catch {
|
|
18111
|
+
if (++misses * intervalMs >= 6e4) {
|
|
18112
|
+
if (tty && shown) write("\n");
|
|
18113
|
+
return null;
|
|
18114
|
+
}
|
|
18115
|
+
await sleep2(intervalMs);
|
|
18116
|
+
continue;
|
|
18117
|
+
}
|
|
18118
|
+
if (p.queued + p.running === 0) {
|
|
18119
|
+
if (tty && shown) write("\r\x1B[K");
|
|
18120
|
+
return p;
|
|
18121
|
+
}
|
|
18122
|
+
const line = progressLine(p);
|
|
18123
|
+
if (tty) write(`\r\x1B[K${line}`);
|
|
18124
|
+
else if (line !== shown && Date.now() - shownAt >= 15e3) {
|
|
18125
|
+
write(`${line}
|
|
18126
|
+
`);
|
|
18127
|
+
shownAt = Date.now();
|
|
18128
|
+
}
|
|
18129
|
+
shown = line;
|
|
18130
|
+
await sleep2(intervalMs);
|
|
18131
|
+
}
|
|
18132
|
+
}
|
|
18133
|
+
var n;
|
|
18134
|
+
var init_wait = __esm({
|
|
18135
|
+
"packages/cli/src/wait.ts"() {
|
|
18136
|
+
"use strict";
|
|
18137
|
+
init_api_client();
|
|
18138
|
+
n = (x) => x.toLocaleString("en-US");
|
|
18139
|
+
}
|
|
18140
|
+
});
|
|
18141
|
+
|
|
17044
18142
|
// packages/cli/src/init.ts
|
|
17045
18143
|
var init_exports = {};
|
|
17046
18144
|
__export(init_exports, {
|
|
17047
18145
|
init: () => init,
|
|
17048
18146
|
initMain: () => initMain
|
|
17049
18147
|
});
|
|
18148
|
+
import { hostname } from "node:os";
|
|
17050
18149
|
import { readFile as readFile12 } from "node:fs/promises";
|
|
17051
18150
|
async function init(argv) {
|
|
17052
18151
|
const skipTranscripts = argv.includes("--no-transcripts");
|
|
@@ -17056,6 +18155,7 @@ async function init(argv) {
|
|
|
17056
18155
|
const tokenFlag = flag("--token");
|
|
17057
18156
|
const root = await repoRoot();
|
|
17058
18157
|
const name = repoName(root);
|
|
18158
|
+
const startedAt = new Date(Date.now() - 6e4);
|
|
17059
18159
|
console.log(`
|
|
17060
18160
|
\u{1F9E0} BrainJar init \u2014 ${name}
|
|
17061
18161
|
repo: ${root}`);
|
|
@@ -17083,6 +18183,7 @@ async function init(argv) {
|
|
|
17083
18183
|
method: "POST",
|
|
17084
18184
|
body: {
|
|
17085
18185
|
name,
|
|
18186
|
+
machine: hostname(),
|
|
17086
18187
|
...brainFlag ? { brainId: brainFlag } : reuse ? { brainId: reuse.brainId } : {},
|
|
17087
18188
|
...reuse ? { sourceId: reuse.sourceId } : {},
|
|
17088
18189
|
...remote ? { remote } : {}
|
|
@@ -17104,61 +18205,49 @@ async function init(argv) {
|
|
|
17104
18205
|
}
|
|
17105
18206
|
step(3, "Scanning code + ingesting git history\u2026");
|
|
17106
18207
|
const graph = await scanStructure(root);
|
|
17107
|
-
|
|
17108
|
-
"
|
|
17109
|
-
|
|
17110
|
-
);
|
|
18208
|
+
await captureApi("/capture/structure", {
|
|
18209
|
+
method: "POST",
|
|
18210
|
+
body: { brain: conn.brainId, graph }
|
|
18211
|
+
});
|
|
17111
18212
|
const connector = createConnector("git_local", { cwd: root }, {});
|
|
17112
18213
|
const artifacts = await connector.sync(conn.cursor ?? void 0);
|
|
17113
18214
|
let commits = 0;
|
|
17114
|
-
let objects = 0;
|
|
17115
18215
|
if (artifacts.length > 0) {
|
|
17116
|
-
const r = await captureApi(
|
|
17117
|
-
"
|
|
17118
|
-
{
|
|
17119
|
-
|
|
17120
|
-
|
|
17121
|
-
|
|
17122
|
-
|
|
17123
|
-
artifacts,
|
|
17124
|
-
cursor: maxUpdatedAt(artifacts) ?? conn.cursor
|
|
17125
|
-
}
|
|
18216
|
+
const r = await captureApi("/capture/artifacts", {
|
|
18217
|
+
method: "POST",
|
|
18218
|
+
body: {
|
|
18219
|
+
brain: conn.brainId,
|
|
18220
|
+
sourceId: conn.sourceId,
|
|
18221
|
+
artifacts,
|
|
18222
|
+
cursor: maxUpdatedAt(artifacts) ?? conn.cursor
|
|
17126
18223
|
}
|
|
17127
|
-
);
|
|
18224
|
+
});
|
|
17128
18225
|
commits = r.inserted;
|
|
17129
|
-
objects = r.objects;
|
|
17130
18226
|
}
|
|
17131
18227
|
ok(
|
|
17132
|
-
`${
|
|
18228
|
+
`${graph.modules.length} modules, ${commits} new commits sent to the server`
|
|
17133
18229
|
);
|
|
17134
18230
|
step(4, "Backfilling past agent sessions\u2026");
|
|
17135
18231
|
if (skipTranscripts) {
|
|
17136
18232
|
ok("skipped (--no-transcripts)");
|
|
17137
18233
|
} else {
|
|
17138
|
-
const author = loadEnv().BRAINJAR_AUTHOR?.trim() || null;
|
|
17139
18234
|
const files = await transcriptFiles(root);
|
|
17140
|
-
let prompts = 0;
|
|
17141
|
-
let failures = 0;
|
|
17142
|
-
let consolidated = 0;
|
|
17143
18235
|
let truncatedCount = 0;
|
|
17144
18236
|
let skipped = 0;
|
|
17145
18237
|
for (const f of files) {
|
|
17146
18238
|
try {
|
|
17147
18239
|
const { text, truncated } = capTranscript(await readFile12(f, "utf8"));
|
|
17148
18240
|
if (truncated) truncatedCount++;
|
|
17149
|
-
|
|
18241
|
+
await captureApi("/capture/transcript", {
|
|
17150
18242
|
method: "POST",
|
|
17151
|
-
body: { brain: conn.brainId, text,
|
|
18243
|
+
body: { brain: conn.brainId, text, backfill: true }
|
|
17152
18244
|
});
|
|
17153
|
-
prompts += r.prompts;
|
|
17154
|
-
failures += r.failures;
|
|
17155
|
-
consolidated += r.consolidated;
|
|
17156
18245
|
} catch {
|
|
17157
18246
|
skipped++;
|
|
17158
18247
|
}
|
|
17159
18248
|
}
|
|
17160
18249
|
ok(
|
|
17161
|
-
`${files.length - skipped} sessions
|
|
18250
|
+
`${files.length - skipped} sessions sent to the server` + (truncatedCount ? ` (${truncatedCount} oversized, newest 40MB kept)` : "") + (skipped ? `, ${skipped} skipped on errors` : "")
|
|
17162
18251
|
);
|
|
17163
18252
|
}
|
|
17164
18253
|
step(5, "Semantic search\u2026");
|
|
@@ -17208,6 +18297,15 @@ async function init(argv) {
|
|
|
17208
18297
|
} catch {
|
|
17209
18298
|
ok("skipped (could not write the pointer)");
|
|
17210
18299
|
}
|
|
18300
|
+
step(8, "Filling the Brain on the server\u2026");
|
|
18301
|
+
console.log(" Stopping this (Ctrl+C) is safe: the server keeps working.");
|
|
18302
|
+
const filled = await waitForServer(conn.brainId, startedAt);
|
|
18303
|
+
if (!filled) ok("lost the connection to the server; it keeps working");
|
|
18304
|
+
else if (filled.failed > 0)
|
|
18305
|
+
ok(
|
|
18306
|
+
`done; ${filled.failed} capture jobs failed for good (see the bell in the web app)`
|
|
18307
|
+
);
|
|
18308
|
+
else ok("done");
|
|
17211
18309
|
try {
|
|
17212
18310
|
await captureApi("/capture/connect", {
|
|
17213
18311
|
method: "POST",
|
|
@@ -17215,6 +18313,7 @@ async function init(argv) {
|
|
|
17215
18313
|
brainId: conn.brainId,
|
|
17216
18314
|
sourceId: conn.sourceId,
|
|
17217
18315
|
name,
|
|
18316
|
+
machine: hostname(),
|
|
17218
18317
|
done: true,
|
|
17219
18318
|
tools: tools.map((t) => t.id)
|
|
17220
18319
|
}
|
|
@@ -17254,8 +18353,9 @@ var init_init = __esm({
|
|
|
17254
18353
|
init_src();
|
|
17255
18354
|
init_api_client();
|
|
17256
18355
|
init_sync2();
|
|
17257
|
-
|
|
17258
|
-
|
|
18356
|
+
init_wait();
|
|
18357
|
+
step = (n2, msg) => console.log(`
|
|
18358
|
+
[${n2}/8] ${msg}`);
|
|
17259
18359
|
ok = (msg) => console.log(` \u2713 ${msg}`);
|
|
17260
18360
|
}
|
|
17261
18361
|
});
|
|
@@ -17528,13 +18628,13 @@ function selectDeepScanFiles(candidates, graph, maxFiles = MAX_FILES) {
|
|
|
17528
18628
|
}
|
|
17529
18629
|
};
|
|
17530
18630
|
for (const bucket of BUCKET_CAPS) {
|
|
17531
|
-
let
|
|
18631
|
+
let n2 = 0;
|
|
17532
18632
|
const ordered = [...candidates].sort().reverse();
|
|
17533
18633
|
for (const p of ordered) {
|
|
17534
|
-
if (
|
|
18634
|
+
if (n2 >= bucket.cap) break;
|
|
17535
18635
|
if (bucket.test(p) && !has.has(p)) {
|
|
17536
18636
|
take(p);
|
|
17537
|
-
|
|
18637
|
+
n2++;
|
|
17538
18638
|
}
|
|
17539
18639
|
}
|
|
17540
18640
|
}
|