@vinihcrosa/lumem-os 0.1.0 → 0.3.0
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/README.md +19 -5
- package/bin/lumem.mjs +393 -168
- package/dist/server/main.mjs +2153 -615
- package/dist/web/assets/{codemirror-setup-Csmyc9rQ.js → codemirror-setup-CHk_6JnB.js} +1 -1
- package/dist/web/assets/index-C5PMQN0Q.css +32 -0
- package/dist/web/assets/index-Cft1ygtm.js +101 -0
- package/dist/web/index.html +2 -2
- package/drizzle/0012_modo_do_lumem.sql +55 -0
- package/drizzle/0013_consumo_por_agente.sql +1 -0
- package/drizzle/meta/0012_snapshot.json +1681 -0
- package/drizzle/meta/0013_snapshot.json +1688 -0
- package/drizzle/meta/_journal.json +14 -0
- package/package.json +1 -1
- package/dist/web/assets/index-BlWx0v0O.js +0 -101
- package/dist/web/assets/index-jS7lSqBc.css +0 -32
package/dist/server/main.mjs
CHANGED
|
@@ -7629,41 +7629,41 @@ var require_queue = __commonJS({
|
|
|
7629
7629
|
queue.drained = drained;
|
|
7630
7630
|
return queue;
|
|
7631
7631
|
function push(value) {
|
|
7632
|
-
var p = new Promise(function(
|
|
7632
|
+
var p = new Promise(function(resolve6, reject) {
|
|
7633
7633
|
pushCb(value, function(err, result) {
|
|
7634
7634
|
if (err) {
|
|
7635
7635
|
reject(err);
|
|
7636
7636
|
return;
|
|
7637
7637
|
}
|
|
7638
|
-
|
|
7638
|
+
resolve6(result);
|
|
7639
7639
|
});
|
|
7640
7640
|
});
|
|
7641
7641
|
p.catch(noop2);
|
|
7642
7642
|
return p;
|
|
7643
7643
|
}
|
|
7644
7644
|
function unshift(value) {
|
|
7645
|
-
var p = new Promise(function(
|
|
7645
|
+
var p = new Promise(function(resolve6, reject) {
|
|
7646
7646
|
unshiftCb(value, function(err, result) {
|
|
7647
7647
|
if (err) {
|
|
7648
7648
|
reject(err);
|
|
7649
7649
|
return;
|
|
7650
7650
|
}
|
|
7651
|
-
|
|
7651
|
+
resolve6(result);
|
|
7652
7652
|
});
|
|
7653
7653
|
});
|
|
7654
7654
|
p.catch(noop2);
|
|
7655
7655
|
return p;
|
|
7656
7656
|
}
|
|
7657
7657
|
function drained() {
|
|
7658
|
-
var p = new Promise(function(
|
|
7658
|
+
var p = new Promise(function(resolve6) {
|
|
7659
7659
|
process.nextTick(function() {
|
|
7660
7660
|
if (queue.idle()) {
|
|
7661
|
-
|
|
7661
|
+
resolve6();
|
|
7662
7662
|
} else {
|
|
7663
7663
|
var previousDrain = queue.drain;
|
|
7664
7664
|
queue.drain = function() {
|
|
7665
7665
|
if (typeof previousDrain === "function") previousDrain();
|
|
7666
|
-
|
|
7666
|
+
resolve6();
|
|
7667
7667
|
queue.drain = previousDrain;
|
|
7668
7668
|
};
|
|
7669
7669
|
}
|
|
@@ -8006,8 +8006,8 @@ var require_create_promise = __commonJS({
|
|
|
8006
8006
|
reject: null,
|
|
8007
8007
|
promise: null
|
|
8008
8008
|
};
|
|
8009
|
-
obj.promise = new Promise((
|
|
8010
|
-
obj.resolve =
|
|
8009
|
+
obj.promise = new Promise((resolve6, reject) => {
|
|
8010
|
+
obj.resolve = resolve6;
|
|
8011
8011
|
obj.reject = reject;
|
|
8012
8012
|
});
|
|
8013
8013
|
return obj;
|
|
@@ -8276,11 +8276,11 @@ var require_thenify = __commonJS({
|
|
|
8276
8276
|
return;
|
|
8277
8277
|
}
|
|
8278
8278
|
debug("thenify");
|
|
8279
|
-
return (
|
|
8279
|
+
return (resolve6, reject) => {
|
|
8280
8280
|
const p = this._loadRegistered();
|
|
8281
8281
|
return p.then(() => {
|
|
8282
8282
|
this[kThenifyDoNotWrap] = true;
|
|
8283
|
-
return
|
|
8283
|
+
return resolve6(this._server);
|
|
8284
8284
|
}, reject);
|
|
8285
8285
|
};
|
|
8286
8286
|
}
|
|
@@ -8417,12 +8417,12 @@ var require_avvio = __commonJS({
|
|
|
8417
8417
|
inherits(Boot, EE);
|
|
8418
8418
|
if ("asyncDispose" in Symbol) {
|
|
8419
8419
|
Boot.prototype[Symbol.asyncDispose] = function() {
|
|
8420
|
-
return new Promise((
|
|
8420
|
+
return new Promise((resolve6, reject) => {
|
|
8421
8421
|
this.close((err) => {
|
|
8422
8422
|
if (err) {
|
|
8423
8423
|
return reject(err);
|
|
8424
8424
|
}
|
|
8425
|
-
|
|
8425
|
+
resolve6();
|
|
8426
8426
|
});
|
|
8427
8427
|
});
|
|
8428
8428
|
};
|
|
@@ -8569,12 +8569,12 @@ var require_avvio = __commonJS({
|
|
|
8569
8569
|
throw new AVV_ERR_CALLBACK_NOT_FN("close", typeof func);
|
|
8570
8570
|
}
|
|
8571
8571
|
} else {
|
|
8572
|
-
promise2 = new Promise(function(
|
|
8572
|
+
promise2 = new Promise(function(resolve6, reject) {
|
|
8573
8573
|
func = function(err) {
|
|
8574
8574
|
if (err) {
|
|
8575
8575
|
return reject(err);
|
|
8576
8576
|
}
|
|
8577
|
-
|
|
8577
|
+
resolve6();
|
|
8578
8578
|
};
|
|
8579
8579
|
});
|
|
8580
8580
|
}
|
|
@@ -8594,7 +8594,7 @@ var require_avvio = __commonJS({
|
|
|
8594
8594
|
queueMicrotask(this.start.bind(this));
|
|
8595
8595
|
return;
|
|
8596
8596
|
}
|
|
8597
|
-
return new Promise((
|
|
8597
|
+
return new Promise((resolve6, reject) => {
|
|
8598
8598
|
this._readyQ.push(readyPromiseCB);
|
|
8599
8599
|
this.start();
|
|
8600
8600
|
const relativeContext = this._current[0].server;
|
|
@@ -8602,7 +8602,7 @@ var require_avvio = __commonJS({
|
|
|
8602
8602
|
if (err) {
|
|
8603
8603
|
reject(err);
|
|
8604
8604
|
} else {
|
|
8605
|
-
|
|
8605
|
+
resolve6(relativeContext);
|
|
8606
8606
|
}
|
|
8607
8607
|
process.nextTick(done);
|
|
8608
8608
|
}
|
|
@@ -9982,8 +9982,8 @@ var require_promise = __commonJS({
|
|
|
9982
9982
|
var { kTestInternals } = require_symbols2();
|
|
9983
9983
|
function withResolvers2() {
|
|
9984
9984
|
let res, rej;
|
|
9985
|
-
const promise2 = new Promise((
|
|
9986
|
-
res =
|
|
9985
|
+
const promise2 = new Promise((resolve6, reject) => {
|
|
9986
|
+
res = resolve6;
|
|
9987
9987
|
rej = reject;
|
|
9988
9988
|
});
|
|
9989
9989
|
return { promise: promise2, resolve: res, reject: rej };
|
|
@@ -10082,15 +10082,15 @@ var require_server = __commonJS({
|
|
|
10082
10082
|
if (cb === void 0) {
|
|
10083
10083
|
const listening = listenPromise.call(this, server, listenOptions);
|
|
10084
10084
|
return listening.then((address) => {
|
|
10085
|
-
const { promise: promise2, resolve:
|
|
10085
|
+
const { promise: promise2, resolve: resolve6 } = PonyPromise.withResolvers();
|
|
10086
10086
|
if (host === "localhost") {
|
|
10087
10087
|
multipleBindings.call(this, server, httpHandler, options, listenOptions, () => {
|
|
10088
10088
|
this[kState].listening = true;
|
|
10089
|
-
|
|
10089
|
+
resolve6(address);
|
|
10090
10090
|
onListenHookRunner(this);
|
|
10091
10091
|
});
|
|
10092
10092
|
} else {
|
|
10093
|
-
|
|
10093
|
+
resolve6(address);
|
|
10094
10094
|
onListenHookRunner(this);
|
|
10095
10095
|
}
|
|
10096
10096
|
return promise2;
|
|
@@ -10217,7 +10217,7 @@ var require_server = __commonJS({
|
|
|
10217
10217
|
}
|
|
10218
10218
|
return this.ready().then(() => {
|
|
10219
10219
|
if (this[kState].aborted) return;
|
|
10220
|
-
const { promise: promise2, resolve:
|
|
10220
|
+
const { promise: promise2, resolve: resolve6, reject } = PonyPromise.withResolvers();
|
|
10221
10221
|
const errEventHandler = (err) => {
|
|
10222
10222
|
cleanup();
|
|
10223
10223
|
this[kState].listening = false;
|
|
@@ -10226,7 +10226,7 @@ var require_server = __commonJS({
|
|
|
10226
10226
|
const listeningEventHandler = () => {
|
|
10227
10227
|
cleanup();
|
|
10228
10228
|
this[kState].listening = true;
|
|
10229
|
-
|
|
10229
|
+
resolve6(logServerAddress.call(
|
|
10230
10230
|
this,
|
|
10231
10231
|
server,
|
|
10232
10232
|
listenOptions.listenTextResolver || defaultResolveServerListeningText
|
|
@@ -13993,7 +13993,7 @@ var require_redact = __commonJS({
|
|
|
13993
13993
|
} = options;
|
|
13994
13994
|
validatePaths(paths);
|
|
13995
13995
|
const pathStructure = buildPathStructure(paths);
|
|
13996
|
-
return function
|
|
13996
|
+
return function redact2(obj) {
|
|
13997
13997
|
if (strict && (obj === null || typeof obj !== "object")) {
|
|
13998
13998
|
if (obj === null || obj === void 0) {
|
|
13999
13999
|
return serialize ? serialize(obj) : obj;
|
|
@@ -15181,7 +15181,7 @@ var require_thread_stream = __commonJS({
|
|
|
15181
15181
|
var { version: version3 } = require_package();
|
|
15182
15182
|
var { EventEmitter: EventEmitter2 } = __require("events");
|
|
15183
15183
|
var { Worker } = __require("worker_threads");
|
|
15184
|
-
var { join:
|
|
15184
|
+
var { join: join21 } = __require("path");
|
|
15185
15185
|
var { pathToFileURL } = __require("url");
|
|
15186
15186
|
var { wait } = require_wait();
|
|
15187
15187
|
var {
|
|
@@ -15232,7 +15232,7 @@ var require_thread_stream = __commonJS({
|
|
|
15232
15232
|
function createWorker(stream, opts) {
|
|
15233
15233
|
const { filename, workerData } = opts;
|
|
15234
15234
|
const bundlerOverrides = "__bundlerPathsOverrides" in globalThis ? globalThis.__bundlerPathsOverrides : {};
|
|
15235
|
-
const toExecute = bundlerOverrides["thread-stream-worker"] ||
|
|
15235
|
+
const toExecute = bundlerOverrides["thread-stream-worker"] || join21(__dirname, "lib", "worker.js");
|
|
15236
15236
|
const worker = new Worker(toExecute, {
|
|
15237
15237
|
...opts.workerOpts,
|
|
15238
15238
|
name: opts.workerOpts?.name || "thread-stream",
|
|
@@ -15700,7 +15700,7 @@ var require_transport = __commonJS({
|
|
|
15700
15700
|
var { createRequire } = __require("module");
|
|
15701
15701
|
var { existsSync: existsSync10 } = __require("node:fs");
|
|
15702
15702
|
var getCallers = require_caller();
|
|
15703
|
-
var { join:
|
|
15703
|
+
var { join: join21, isAbsolute: isAbsolute8, sep: sep6 } = __require("node:path");
|
|
15704
15704
|
var { fileURLToPath: fileURLToPath3 } = __require("node:url");
|
|
15705
15705
|
var sleep = require_atomic_sleep();
|
|
15706
15706
|
var onExit = require_on_exit_leak_free();
|
|
@@ -15772,7 +15772,7 @@ var require_transport = __commonJS({
|
|
|
15772
15772
|
return false;
|
|
15773
15773
|
}
|
|
15774
15774
|
}
|
|
15775
|
-
return
|
|
15775
|
+
return isAbsolute8(path) && !existsSync10(path);
|
|
15776
15776
|
}
|
|
15777
15777
|
function stripQuotes(value) {
|
|
15778
15778
|
const first = value[0];
|
|
@@ -15853,7 +15853,7 @@ var require_transport = __commonJS({
|
|
|
15853
15853
|
throw new Error("only one of target or targets can be specified");
|
|
15854
15854
|
}
|
|
15855
15855
|
if (targets) {
|
|
15856
|
-
target = bundlerOverrides["pino-worker"] ||
|
|
15856
|
+
target = bundlerOverrides["pino-worker"] || join21(__dirname, "worker.js");
|
|
15857
15857
|
options.targets = targets.filter((dest) => dest.target).map((dest) => {
|
|
15858
15858
|
return {
|
|
15859
15859
|
...dest,
|
|
@@ -15871,7 +15871,7 @@ var require_transport = __commonJS({
|
|
|
15871
15871
|
});
|
|
15872
15872
|
});
|
|
15873
15873
|
} else if (pipeline) {
|
|
15874
|
-
target = bundlerOverrides["pino-worker"] ||
|
|
15874
|
+
target = bundlerOverrides["pino-worker"] || join21(__dirname, "worker.js");
|
|
15875
15875
|
options.pipelines = [pipeline.map((dest) => {
|
|
15876
15876
|
return {
|
|
15877
15877
|
...dest,
|
|
@@ -15890,11 +15890,11 @@ var require_transport = __commonJS({
|
|
|
15890
15890
|
return buildStream(fixTarget(target), options, worker, sync, name);
|
|
15891
15891
|
function fixTarget(origin) {
|
|
15892
15892
|
origin = bundlerOverrides[origin] || origin;
|
|
15893
|
-
if (
|
|
15893
|
+
if (isAbsolute8(origin) || origin.indexOf("file://") === 0) {
|
|
15894
15894
|
return origin;
|
|
15895
15895
|
}
|
|
15896
15896
|
if (origin === "pino/file") {
|
|
15897
|
-
return
|
|
15897
|
+
return join21(__dirname, "..", "file.js");
|
|
15898
15898
|
}
|
|
15899
15899
|
let fixTarget2;
|
|
15900
15900
|
for (const filePath of callers) {
|
|
@@ -16874,7 +16874,7 @@ var require_safe_stable_stringify = __commonJS({
|
|
|
16874
16874
|
return circularValue;
|
|
16875
16875
|
}
|
|
16876
16876
|
let res = "";
|
|
16877
|
-
let
|
|
16877
|
+
let join21 = ",";
|
|
16878
16878
|
const originalIndentation = indentation;
|
|
16879
16879
|
if (Array.isArray(value)) {
|
|
16880
16880
|
if (value.length === 0) {
|
|
@@ -16888,7 +16888,7 @@ var require_safe_stable_stringify = __commonJS({
|
|
|
16888
16888
|
indentation += spacer;
|
|
16889
16889
|
res += `
|
|
16890
16890
|
${indentation}`;
|
|
16891
|
-
|
|
16891
|
+
join21 = `,
|
|
16892
16892
|
${indentation}`;
|
|
16893
16893
|
}
|
|
16894
16894
|
const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
|
|
@@ -16896,13 +16896,13 @@ ${indentation}`;
|
|
|
16896
16896
|
for (; i < maximumValuesToStringify - 1; i++) {
|
|
16897
16897
|
const tmp2 = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
|
|
16898
16898
|
res += tmp2 !== void 0 ? tmp2 : "null";
|
|
16899
|
-
res +=
|
|
16899
|
+
res += join21;
|
|
16900
16900
|
}
|
|
16901
16901
|
const tmp = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
|
|
16902
16902
|
res += tmp !== void 0 ? tmp : "null";
|
|
16903
16903
|
if (value.length - 1 > maximumBreadth) {
|
|
16904
16904
|
const removedKeys = value.length - maximumBreadth - 1;
|
|
16905
|
-
res += `${
|
|
16905
|
+
res += `${join21}"... ${getItemCount(removedKeys)} not stringified"`;
|
|
16906
16906
|
}
|
|
16907
16907
|
if (spacer !== "") {
|
|
16908
16908
|
res += `
|
|
@@ -16923,7 +16923,7 @@ ${originalIndentation}`;
|
|
|
16923
16923
|
let separator = "";
|
|
16924
16924
|
if (spacer !== "") {
|
|
16925
16925
|
indentation += spacer;
|
|
16926
|
-
|
|
16926
|
+
join21 = `,
|
|
16927
16927
|
${indentation}`;
|
|
16928
16928
|
whitespace = " ";
|
|
16929
16929
|
}
|
|
@@ -16937,13 +16937,13 @@ ${indentation}`;
|
|
|
16937
16937
|
const tmp = stringifyFnReplacer(key2, value, stack, replacer, spacer, indentation);
|
|
16938
16938
|
if (tmp !== void 0) {
|
|
16939
16939
|
res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
|
|
16940
|
-
separator =
|
|
16940
|
+
separator = join21;
|
|
16941
16941
|
}
|
|
16942
16942
|
}
|
|
16943
16943
|
if (keyLength > maximumBreadth) {
|
|
16944
16944
|
const removedKeys = keyLength - maximumBreadth;
|
|
16945
16945
|
res += `${separator}"...":${whitespace}"${getItemCount(removedKeys)} not stringified"`;
|
|
16946
|
-
separator =
|
|
16946
|
+
separator = join21;
|
|
16947
16947
|
}
|
|
16948
16948
|
if (spacer !== "" && separator.length > 1) {
|
|
16949
16949
|
res = `
|
|
@@ -16984,7 +16984,7 @@ ${originalIndentation}`;
|
|
|
16984
16984
|
}
|
|
16985
16985
|
const originalIndentation = indentation;
|
|
16986
16986
|
let res = "";
|
|
16987
|
-
let
|
|
16987
|
+
let join21 = ",";
|
|
16988
16988
|
if (Array.isArray(value)) {
|
|
16989
16989
|
if (value.length === 0) {
|
|
16990
16990
|
return "[]";
|
|
@@ -16997,7 +16997,7 @@ ${originalIndentation}`;
|
|
|
16997
16997
|
indentation += spacer;
|
|
16998
16998
|
res += `
|
|
16999
16999
|
${indentation}`;
|
|
17000
|
-
|
|
17000
|
+
join21 = `,
|
|
17001
17001
|
${indentation}`;
|
|
17002
17002
|
}
|
|
17003
17003
|
const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
|
|
@@ -17005,13 +17005,13 @@ ${indentation}`;
|
|
|
17005
17005
|
for (; i < maximumValuesToStringify - 1; i++) {
|
|
17006
17006
|
const tmp2 = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
|
|
17007
17007
|
res += tmp2 !== void 0 ? tmp2 : "null";
|
|
17008
|
-
res +=
|
|
17008
|
+
res += join21;
|
|
17009
17009
|
}
|
|
17010
17010
|
const tmp = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
|
|
17011
17011
|
res += tmp !== void 0 ? tmp : "null";
|
|
17012
17012
|
if (value.length - 1 > maximumBreadth) {
|
|
17013
17013
|
const removedKeys = value.length - maximumBreadth - 1;
|
|
17014
|
-
res += `${
|
|
17014
|
+
res += `${join21}"... ${getItemCount(removedKeys)} not stringified"`;
|
|
17015
17015
|
}
|
|
17016
17016
|
if (spacer !== "") {
|
|
17017
17017
|
res += `
|
|
@@ -17024,7 +17024,7 @@ ${originalIndentation}`;
|
|
|
17024
17024
|
let whitespace = "";
|
|
17025
17025
|
if (spacer !== "") {
|
|
17026
17026
|
indentation += spacer;
|
|
17027
|
-
|
|
17027
|
+
join21 = `,
|
|
17028
17028
|
${indentation}`;
|
|
17029
17029
|
whitespace = " ";
|
|
17030
17030
|
}
|
|
@@ -17033,7 +17033,7 @@ ${indentation}`;
|
|
|
17033
17033
|
const tmp = stringifyArrayReplacer(key2, value[key2], stack, replacer, spacer, indentation);
|
|
17034
17034
|
if (tmp !== void 0) {
|
|
17035
17035
|
res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
|
|
17036
|
-
separator =
|
|
17036
|
+
separator = join21;
|
|
17037
17037
|
}
|
|
17038
17038
|
}
|
|
17039
17039
|
if (spacer !== "" && separator.length > 1) {
|
|
@@ -17091,20 +17091,20 @@ ${originalIndentation}`;
|
|
|
17091
17091
|
indentation += spacer;
|
|
17092
17092
|
let res2 = `
|
|
17093
17093
|
${indentation}`;
|
|
17094
|
-
const
|
|
17094
|
+
const join22 = `,
|
|
17095
17095
|
${indentation}`;
|
|
17096
17096
|
const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
|
|
17097
17097
|
let i = 0;
|
|
17098
17098
|
for (; i < maximumValuesToStringify - 1; i++) {
|
|
17099
17099
|
const tmp2 = stringifyIndent(String(i), value[i], stack, spacer, indentation);
|
|
17100
17100
|
res2 += tmp2 !== void 0 ? tmp2 : "null";
|
|
17101
|
-
res2 +=
|
|
17101
|
+
res2 += join22;
|
|
17102
17102
|
}
|
|
17103
17103
|
const tmp = stringifyIndent(String(i), value[i], stack, spacer, indentation);
|
|
17104
17104
|
res2 += tmp !== void 0 ? tmp : "null";
|
|
17105
17105
|
if (value.length - 1 > maximumBreadth) {
|
|
17106
17106
|
const removedKeys = value.length - maximumBreadth - 1;
|
|
17107
|
-
res2 += `${
|
|
17107
|
+
res2 += `${join22}"... ${getItemCount(removedKeys)} not stringified"`;
|
|
17108
17108
|
}
|
|
17109
17109
|
res2 += `
|
|
17110
17110
|
${originalIndentation}`;
|
|
@@ -17120,16 +17120,16 @@ ${originalIndentation}`;
|
|
|
17120
17120
|
return '"[Object]"';
|
|
17121
17121
|
}
|
|
17122
17122
|
indentation += spacer;
|
|
17123
|
-
const
|
|
17123
|
+
const join21 = `,
|
|
17124
17124
|
${indentation}`;
|
|
17125
17125
|
let res = "";
|
|
17126
17126
|
let separator = "";
|
|
17127
17127
|
let maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth);
|
|
17128
17128
|
if (isTypedArrayWithEntries(value)) {
|
|
17129
|
-
res += stringifyTypedArray(value,
|
|
17129
|
+
res += stringifyTypedArray(value, join21, maximumBreadth);
|
|
17130
17130
|
keys = keys.slice(value.length);
|
|
17131
17131
|
maximumPropertiesToStringify -= value.length;
|
|
17132
|
-
separator =
|
|
17132
|
+
separator = join21;
|
|
17133
17133
|
}
|
|
17134
17134
|
if (deterministic) {
|
|
17135
17135
|
keys = sort(keys, comparator);
|
|
@@ -17140,13 +17140,13 @@ ${indentation}`;
|
|
|
17140
17140
|
const tmp = stringifyIndent(key2, value[key2], stack, spacer, indentation);
|
|
17141
17141
|
if (tmp !== void 0) {
|
|
17142
17142
|
res += `${separator}${strEscape(key2)}: ${tmp}`;
|
|
17143
|
-
separator =
|
|
17143
|
+
separator = join21;
|
|
17144
17144
|
}
|
|
17145
17145
|
}
|
|
17146
17146
|
if (keyLength > maximumBreadth) {
|
|
17147
17147
|
const removedKeys = keyLength - maximumBreadth;
|
|
17148
17148
|
res += `${separator}"...": "${getItemCount(removedKeys)} not stringified"`;
|
|
17149
|
-
separator =
|
|
17149
|
+
separator = join21;
|
|
17150
17150
|
}
|
|
17151
17151
|
if (separator !== "") {
|
|
17152
17152
|
res = `
|
|
@@ -17554,7 +17554,7 @@ var require_pino = __commonJS({
|
|
|
17554
17554
|
const { opts, stream } = normalize5(instance, caller(), ...args);
|
|
17555
17555
|
if (opts.level && typeof opts.level === "string" && DEFAULT_LEVELS[opts.level.toLowerCase()] !== void 0) opts.level = opts.level.toLowerCase();
|
|
17556
17556
|
const {
|
|
17557
|
-
redact,
|
|
17557
|
+
redact: redact2,
|
|
17558
17558
|
crlf,
|
|
17559
17559
|
serializers: serializers2,
|
|
17560
17560
|
timestamp,
|
|
@@ -17588,8 +17588,8 @@ var require_pino = __commonJS({
|
|
|
17588
17588
|
const stringifyFn = stringify2.bind({
|
|
17589
17589
|
[stringifySafeSym]: stringifySafe
|
|
17590
17590
|
});
|
|
17591
|
-
const stringifiers =
|
|
17592
|
-
const formatOpts =
|
|
17591
|
+
const stringifiers = redact2 ? redaction(redact2, stringifyFn) : {};
|
|
17592
|
+
const formatOpts = redact2 ? { stringify: stringifiers[redactFmtSym] } : { stringify: stringifyFn };
|
|
17593
17593
|
const end = "}" + (crlf ? "\r\n" : "\n");
|
|
17594
17594
|
const coreChindings = asChindings.bind(null, {
|
|
17595
17595
|
[chindingsSym]: "",
|
|
@@ -24203,7 +24203,7 @@ var require_compile = __commonJS({
|
|
|
24203
24203
|
const schOrFunc = root.refs[ref];
|
|
24204
24204
|
if (schOrFunc)
|
|
24205
24205
|
return schOrFunc;
|
|
24206
|
-
let _sch =
|
|
24206
|
+
let _sch = resolve6.call(this, root, ref);
|
|
24207
24207
|
if (_sch === void 0) {
|
|
24208
24208
|
const schema2 = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
|
|
24209
24209
|
const { schemaId } = this.opts;
|
|
@@ -24230,7 +24230,7 @@ var require_compile = __commonJS({
|
|
|
24230
24230
|
function sameSchemaEnv(s1, s2) {
|
|
24231
24231
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
24232
24232
|
}
|
|
24233
|
-
function
|
|
24233
|
+
function resolve6(root, ref) {
|
|
24234
24234
|
let sch;
|
|
24235
24235
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
24236
24236
|
ref = sch;
|
|
@@ -24861,7 +24861,7 @@ var require_fast_uri = __commonJS({
|
|
|
24861
24861
|
}
|
|
24862
24862
|
return uri;
|
|
24863
24863
|
}
|
|
24864
|
-
function
|
|
24864
|
+
function resolve6(baseURI, relativeURI, options) {
|
|
24865
24865
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
24866
24866
|
const { parsed: baseParsed, malformedAuthorityOrPort: baseMalformed } = parseWithStatus(baseURI, schemelessOptions);
|
|
24867
24867
|
const { parsed: relativeParsed, malformedAuthorityOrPort: relativeMalformed } = parseWithStatus(relativeURI, schemelessOptions);
|
|
@@ -24872,49 +24872,49 @@ var require_fast_uri = __commonJS({
|
|
|
24872
24872
|
schemelessOptions.skipEscape = true;
|
|
24873
24873
|
return serialize(resolved, schemelessOptions);
|
|
24874
24874
|
}
|
|
24875
|
-
function resolveComponent(base,
|
|
24875
|
+
function resolveComponent(base, relative5, options, skipNormalization) {
|
|
24876
24876
|
const target = {};
|
|
24877
24877
|
if (!skipNormalization) {
|
|
24878
24878
|
base = parse5(serialize(base, options), options);
|
|
24879
|
-
|
|
24879
|
+
relative5 = parse5(serialize(relative5, options), options);
|
|
24880
24880
|
}
|
|
24881
24881
|
options = options || {};
|
|
24882
|
-
if (!options.tolerant &&
|
|
24883
|
-
target.scheme =
|
|
24884
|
-
target.userinfo =
|
|
24885
|
-
target.host =
|
|
24886
|
-
target.port =
|
|
24887
|
-
target.path = removeDotSegments(
|
|
24888
|
-
target.query =
|
|
24882
|
+
if (!options.tolerant && relative5.scheme) {
|
|
24883
|
+
target.scheme = relative5.scheme;
|
|
24884
|
+
target.userinfo = relative5.userinfo;
|
|
24885
|
+
target.host = relative5.host;
|
|
24886
|
+
target.port = relative5.port;
|
|
24887
|
+
target.path = removeDotSegments(relative5.path || "");
|
|
24888
|
+
target.query = relative5.query;
|
|
24889
24889
|
} else {
|
|
24890
|
-
if (
|
|
24891
|
-
target.userinfo =
|
|
24892
|
-
target.host =
|
|
24893
|
-
target.port =
|
|
24894
|
-
target.path = removeDotSegments(
|
|
24895
|
-
target.query =
|
|
24890
|
+
if (relative5.userinfo !== void 0 || relative5.host !== void 0 || relative5.port !== void 0) {
|
|
24891
|
+
target.userinfo = relative5.userinfo;
|
|
24892
|
+
target.host = relative5.host;
|
|
24893
|
+
target.port = relative5.port;
|
|
24894
|
+
target.path = removeDotSegments(relative5.path || "");
|
|
24895
|
+
target.query = relative5.query;
|
|
24896
24896
|
} else {
|
|
24897
|
-
if (!
|
|
24897
|
+
if (!relative5.path) {
|
|
24898
24898
|
target.path = base.path;
|
|
24899
|
-
if (
|
|
24900
|
-
target.query =
|
|
24899
|
+
if (relative5.query !== void 0) {
|
|
24900
|
+
target.query = relative5.query;
|
|
24901
24901
|
} else {
|
|
24902
24902
|
target.query = base.query;
|
|
24903
24903
|
}
|
|
24904
24904
|
} else {
|
|
24905
|
-
if (
|
|
24906
|
-
target.path = removeDotSegments(
|
|
24905
|
+
if (relative5.path[0] === "/") {
|
|
24906
|
+
target.path = removeDotSegments(relative5.path);
|
|
24907
24907
|
} else {
|
|
24908
24908
|
if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) {
|
|
24909
|
-
target.path = "/" +
|
|
24909
|
+
target.path = "/" + relative5.path;
|
|
24910
24910
|
} else if (!base.path) {
|
|
24911
|
-
target.path =
|
|
24911
|
+
target.path = relative5.path;
|
|
24912
24912
|
} else {
|
|
24913
|
-
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) +
|
|
24913
|
+
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative5.path;
|
|
24914
24914
|
}
|
|
24915
24915
|
target.path = removeDotSegments(target.path);
|
|
24916
24916
|
}
|
|
24917
|
-
target.query =
|
|
24917
|
+
target.query = relative5.query;
|
|
24918
24918
|
}
|
|
24919
24919
|
target.userinfo = base.userinfo;
|
|
24920
24920
|
target.host = base.host;
|
|
@@ -24922,7 +24922,7 @@ var require_fast_uri = __commonJS({
|
|
|
24922
24922
|
}
|
|
24923
24923
|
target.scheme = base.scheme;
|
|
24924
24924
|
}
|
|
24925
|
-
target.fragment =
|
|
24925
|
+
target.fragment = relative5.fragment;
|
|
24926
24926
|
return target;
|
|
24927
24927
|
}
|
|
24928
24928
|
function equal(uriA, uriB, options) {
|
|
@@ -25145,7 +25145,7 @@ var require_fast_uri = __commonJS({
|
|
|
25145
25145
|
var fastUri = {
|
|
25146
25146
|
SCHEMES,
|
|
25147
25147
|
normalize: normalize5,
|
|
25148
|
-
resolve:
|
|
25148
|
+
resolve: resolve6,
|
|
25149
25149
|
resolveComponent,
|
|
25150
25150
|
equal,
|
|
25151
25151
|
serialize,
|
|
@@ -28901,37 +28901,37 @@ var require_parseJson = __commonJS({
|
|
|
28901
28901
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28902
28902
|
exports.parseJsonString = exports.parseJsonNumber = exports.parseJson = void 0;
|
|
28903
28903
|
var rxParseJson = /position\s(\d+)(?: \(line \d+ column \d+\))?$/;
|
|
28904
|
-
function
|
|
28904
|
+
function parseJson2(s, pos) {
|
|
28905
28905
|
let endPos;
|
|
28906
|
-
|
|
28906
|
+
parseJson2.message = void 0;
|
|
28907
28907
|
let matches;
|
|
28908
28908
|
if (pos)
|
|
28909
28909
|
s = s.slice(pos);
|
|
28910
28910
|
try {
|
|
28911
|
-
|
|
28911
|
+
parseJson2.position = pos + s.length;
|
|
28912
28912
|
return JSON.parse(s);
|
|
28913
28913
|
} catch (e) {
|
|
28914
28914
|
matches = rxParseJson.exec(e.message);
|
|
28915
28915
|
if (!matches) {
|
|
28916
|
-
|
|
28916
|
+
parseJson2.message = "unexpected end";
|
|
28917
28917
|
return void 0;
|
|
28918
28918
|
}
|
|
28919
28919
|
endPos = +matches[1];
|
|
28920
28920
|
const c = s[endPos];
|
|
28921
28921
|
s = s.slice(0, endPos);
|
|
28922
|
-
|
|
28922
|
+
parseJson2.position = pos + endPos;
|
|
28923
28923
|
try {
|
|
28924
28924
|
return JSON.parse(s);
|
|
28925
28925
|
} catch (e1) {
|
|
28926
|
-
|
|
28926
|
+
parseJson2.message = `unexpected token ${c}`;
|
|
28927
28927
|
return void 0;
|
|
28928
28928
|
}
|
|
28929
28929
|
}
|
|
28930
28930
|
}
|
|
28931
|
-
exports.parseJson =
|
|
28932
|
-
|
|
28933
|
-
|
|
28934
|
-
|
|
28931
|
+
exports.parseJson = parseJson2;
|
|
28932
|
+
parseJson2.message = void 0;
|
|
28933
|
+
parseJson2.position = 0;
|
|
28934
|
+
parseJson2.code = 'require("ajv/dist/runtime/parseJson").parseJson';
|
|
28935
28935
|
function parseJsonNumber(s, pos, maxDigits) {
|
|
28936
28936
|
let numStr = "";
|
|
28937
28937
|
let c;
|
|
@@ -30145,7 +30145,7 @@ var require_fast_uri2 = __commonJS({
|
|
|
30145
30145
|
}
|
|
30146
30146
|
return uri;
|
|
30147
30147
|
}
|
|
30148
|
-
function
|
|
30148
|
+
function resolve6(baseURI, relativeURI, options) {
|
|
30149
30149
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
30150
30150
|
const { parsed: baseParsed, malformedAuthorityOrPort: baseMalformed } = parseWithStatus(baseURI, schemelessOptions);
|
|
30151
30151
|
const { parsed: relativeParsed, malformedAuthorityOrPort: relativeMalformed } = parseWithStatus(relativeURI, schemelessOptions);
|
|
@@ -30156,49 +30156,49 @@ var require_fast_uri2 = __commonJS({
|
|
|
30156
30156
|
schemelessOptions.skipEscape = true;
|
|
30157
30157
|
return serialize(resolved, schemelessOptions);
|
|
30158
30158
|
}
|
|
30159
|
-
function resolveComponent(base,
|
|
30159
|
+
function resolveComponent(base, relative5, options, skipNormalization) {
|
|
30160
30160
|
const target = {};
|
|
30161
30161
|
if (!skipNormalization) {
|
|
30162
30162
|
base = parse5(serialize(base, options), options);
|
|
30163
|
-
|
|
30163
|
+
relative5 = parse5(serialize(relative5, options), options);
|
|
30164
30164
|
}
|
|
30165
30165
|
options = options || {};
|
|
30166
|
-
if (!options.tolerant &&
|
|
30167
|
-
target.scheme =
|
|
30168
|
-
target.userinfo =
|
|
30169
|
-
target.host =
|
|
30170
|
-
target.port =
|
|
30171
|
-
target.path = removeDotSegments(
|
|
30172
|
-
target.query =
|
|
30166
|
+
if (!options.tolerant && relative5.scheme) {
|
|
30167
|
+
target.scheme = relative5.scheme;
|
|
30168
|
+
target.userinfo = relative5.userinfo;
|
|
30169
|
+
target.host = relative5.host;
|
|
30170
|
+
target.port = relative5.port;
|
|
30171
|
+
target.path = removeDotSegments(relative5.path || "");
|
|
30172
|
+
target.query = relative5.query;
|
|
30173
30173
|
} else {
|
|
30174
|
-
if (
|
|
30175
|
-
target.userinfo =
|
|
30176
|
-
target.host =
|
|
30177
|
-
target.port =
|
|
30178
|
-
target.path = removeDotSegments(
|
|
30179
|
-
target.query =
|
|
30174
|
+
if (relative5.userinfo !== void 0 || relative5.host !== void 0 || relative5.port !== void 0) {
|
|
30175
|
+
target.userinfo = relative5.userinfo;
|
|
30176
|
+
target.host = relative5.host;
|
|
30177
|
+
target.port = relative5.port;
|
|
30178
|
+
target.path = removeDotSegments(relative5.path || "");
|
|
30179
|
+
target.query = relative5.query;
|
|
30180
30180
|
} else {
|
|
30181
|
-
if (!
|
|
30181
|
+
if (!relative5.path) {
|
|
30182
30182
|
target.path = base.path;
|
|
30183
|
-
if (
|
|
30184
|
-
target.query =
|
|
30183
|
+
if (relative5.query !== void 0) {
|
|
30184
|
+
target.query = relative5.query;
|
|
30185
30185
|
} else {
|
|
30186
30186
|
target.query = base.query;
|
|
30187
30187
|
}
|
|
30188
30188
|
} else {
|
|
30189
|
-
if (
|
|
30190
|
-
target.path = removeDotSegments(
|
|
30189
|
+
if (relative5.path[0] === "/") {
|
|
30190
|
+
target.path = removeDotSegments(relative5.path);
|
|
30191
30191
|
} else {
|
|
30192
30192
|
if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) {
|
|
30193
|
-
target.path = "/" +
|
|
30193
|
+
target.path = "/" + relative5.path;
|
|
30194
30194
|
} else if (!base.path) {
|
|
30195
|
-
target.path =
|
|
30195
|
+
target.path = relative5.path;
|
|
30196
30196
|
} else {
|
|
30197
|
-
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) +
|
|
30197
|
+
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative5.path;
|
|
30198
30198
|
}
|
|
30199
30199
|
target.path = removeDotSegments(target.path);
|
|
30200
30200
|
}
|
|
30201
|
-
target.query =
|
|
30201
|
+
target.query = relative5.query;
|
|
30202
30202
|
}
|
|
30203
30203
|
target.userinfo = base.userinfo;
|
|
30204
30204
|
target.host = base.host;
|
|
@@ -30206,7 +30206,7 @@ var require_fast_uri2 = __commonJS({
|
|
|
30206
30206
|
}
|
|
30207
30207
|
target.scheme = base.scheme;
|
|
30208
30208
|
}
|
|
30209
|
-
target.fragment =
|
|
30209
|
+
target.fragment = relative5.fragment;
|
|
30210
30210
|
return target;
|
|
30211
30211
|
}
|
|
30212
30212
|
function equal(uriA, uriB, options) {
|
|
@@ -30428,7 +30428,7 @@ var require_fast_uri2 = __commonJS({
|
|
|
30428
30428
|
var fastUri = {
|
|
30429
30429
|
SCHEMES,
|
|
30430
30430
|
normalize: normalize5,
|
|
30431
|
-
resolve:
|
|
30431
|
+
resolve: resolve6,
|
|
30432
30432
|
resolveComponent,
|
|
30433
30433
|
equal,
|
|
30434
30434
|
serialize,
|
|
@@ -42289,9 +42289,9 @@ var require_light_my_request = __commonJS({
|
|
|
42289
42289
|
const res = new Response2(req, callback);
|
|
42290
42290
|
return makeRequest(dispatchFunc, server, req, res);
|
|
42291
42291
|
} else {
|
|
42292
|
-
return new Promise((
|
|
42292
|
+
return new Promise((resolve6, reject) => {
|
|
42293
42293
|
const req = new RequestConstructor(options);
|
|
42294
|
-
const res = new Response2(req,
|
|
42294
|
+
const res = new Response2(req, resolve6, reject);
|
|
42295
42295
|
makeRequest(dispatchFunc, server, req, res);
|
|
42296
42296
|
});
|
|
42297
42297
|
}
|
|
@@ -49470,7 +49470,7 @@ var require_commonjs4 = __commonJS({
|
|
|
49470
49470
|
var EMITTING_END = Symbol("emittingEnd");
|
|
49471
49471
|
var EMITTED_ERROR = Symbol("emittedError");
|
|
49472
49472
|
var CLOSED = Symbol("closed");
|
|
49473
|
-
var
|
|
49473
|
+
var READ2 = Symbol("read");
|
|
49474
49474
|
var FLUSH = Symbol("flush");
|
|
49475
49475
|
var FLUSHCHUNK = Symbol("flushChunk");
|
|
49476
49476
|
var ENCODING = Symbol("encoding");
|
|
@@ -49775,11 +49775,11 @@ var require_commonjs4 = __commonJS({
|
|
|
49775
49775
|
this[ENCODING] ? this[BUFFER].join("") : Buffer.concat(this[BUFFER], this[BUFFERLENGTH])
|
|
49776
49776
|
];
|
|
49777
49777
|
}
|
|
49778
|
-
const ret = this[
|
|
49778
|
+
const ret = this[READ2](n || null, this[BUFFER][0]);
|
|
49779
49779
|
this[MAYBE_EMIT_END]();
|
|
49780
49780
|
return ret;
|
|
49781
49781
|
}
|
|
49782
|
-
[
|
|
49782
|
+
[READ2](n, chunk) {
|
|
49783
49783
|
if (this[OBJECTMODE])
|
|
49784
49784
|
this[BUFFERSHIFT]();
|
|
49785
49785
|
else {
|
|
@@ -50181,10 +50181,10 @@ var require_commonjs4 = __commonJS({
|
|
|
50181
50181
|
* Return a void Promise that resolves once the stream ends.
|
|
50182
50182
|
*/
|
|
50183
50183
|
async promise() {
|
|
50184
|
-
return new Promise((
|
|
50184
|
+
return new Promise((resolve6, reject) => {
|
|
50185
50185
|
this.on(DESTROYED, () => reject(new Error("stream destroyed")));
|
|
50186
50186
|
this.on("error", (er) => reject(er));
|
|
50187
|
-
this.on("end", () =>
|
|
50187
|
+
this.on("end", () => resolve6());
|
|
50188
50188
|
});
|
|
50189
50189
|
}
|
|
50190
50190
|
/**
|
|
@@ -50208,7 +50208,7 @@ var require_commonjs4 = __commonJS({
|
|
|
50208
50208
|
return Promise.resolve({ done: false, value: res });
|
|
50209
50209
|
if (this[EOF])
|
|
50210
50210
|
return stop();
|
|
50211
|
-
let
|
|
50211
|
+
let resolve6;
|
|
50212
50212
|
let reject;
|
|
50213
50213
|
const onerr = (er) => {
|
|
50214
50214
|
this.off("data", ondata);
|
|
@@ -50222,19 +50222,19 @@ var require_commonjs4 = __commonJS({
|
|
|
50222
50222
|
this.off("end", onend);
|
|
50223
50223
|
this.off(DESTROYED, ondestroy);
|
|
50224
50224
|
this.pause();
|
|
50225
|
-
|
|
50225
|
+
resolve6({ value, done: !!this[EOF] });
|
|
50226
50226
|
};
|
|
50227
50227
|
const onend = () => {
|
|
50228
50228
|
this.off("error", onerr);
|
|
50229
50229
|
this.off("data", ondata);
|
|
50230
50230
|
this.off(DESTROYED, ondestroy);
|
|
50231
50231
|
stop();
|
|
50232
|
-
|
|
50232
|
+
resolve6({ done: true, value: void 0 });
|
|
50233
50233
|
};
|
|
50234
50234
|
const ondestroy = () => onerr(new Error("stream destroyed"));
|
|
50235
50235
|
return new Promise((res2, rej) => {
|
|
50236
50236
|
reject = rej;
|
|
50237
|
-
|
|
50237
|
+
resolve6 = res2;
|
|
50238
50238
|
this.once(DESTROYED, ondestroy);
|
|
50239
50239
|
this.once("error", onerr);
|
|
50240
50240
|
this.once("end", onend);
|
|
@@ -51254,9 +51254,9 @@ var require_commonjs5 = __commonJS({
|
|
|
51254
51254
|
if (this.#asyncReaddirInFlight) {
|
|
51255
51255
|
await this.#asyncReaddirInFlight;
|
|
51256
51256
|
} else {
|
|
51257
|
-
let
|
|
51257
|
+
let resolve6 = () => {
|
|
51258
51258
|
};
|
|
51259
|
-
this.#asyncReaddirInFlight = new Promise((res) =>
|
|
51259
|
+
this.#asyncReaddirInFlight = new Promise((res) => resolve6 = res);
|
|
51260
51260
|
try {
|
|
51261
51261
|
for (const e of await this.#fs.promises.readdir(fullpath, {
|
|
51262
51262
|
withFileTypes: true
|
|
@@ -51269,7 +51269,7 @@ var require_commonjs5 = __commonJS({
|
|
|
51269
51269
|
children.provisional = 0;
|
|
51270
51270
|
}
|
|
51271
51271
|
this.#asyncReaddirInFlight = void 0;
|
|
51272
|
-
|
|
51272
|
+
resolve6();
|
|
51273
51273
|
}
|
|
51274
51274
|
return children.slice(0, children.provisional);
|
|
51275
51275
|
}
|
|
@@ -52356,10 +52356,10 @@ var require_ignore = __commonJS({
|
|
|
52356
52356
|
ignored(p) {
|
|
52357
52357
|
const fullpath = p.fullpath();
|
|
52358
52358
|
const fullpaths = `${fullpath}/`;
|
|
52359
|
-
const
|
|
52360
|
-
const relatives = `${
|
|
52359
|
+
const relative5 = p.relative() || ".";
|
|
52360
|
+
const relatives = `${relative5}/`;
|
|
52361
52361
|
for (const m of this.relative) {
|
|
52362
|
-
if (m.match(
|
|
52362
|
+
if (m.match(relative5) || m.match(relatives))
|
|
52363
52363
|
return true;
|
|
52364
52364
|
}
|
|
52365
52365
|
for (const m of this.absolute) {
|
|
@@ -52370,9 +52370,9 @@ var require_ignore = __commonJS({
|
|
|
52370
52370
|
}
|
|
52371
52371
|
childrenIgnored(p) {
|
|
52372
52372
|
const fullpath = p.fullpath() + "/";
|
|
52373
|
-
const
|
|
52373
|
+
const relative5 = (p.relative() || ".") + "/";
|
|
52374
52374
|
for (const m of this.relativeChildren) {
|
|
52375
|
-
if (m.match(
|
|
52375
|
+
if (m.match(relative5))
|
|
52376
52376
|
return true;
|
|
52377
52377
|
}
|
|
52378
52378
|
for (const m of this.absoluteChildren) {
|
|
@@ -53741,7 +53741,7 @@ var require_parseTokenList = __commonJS({
|
|
|
53741
53741
|
// ../../node_modules/.pnpm/depd@2.0.0/node_modules/depd/index.js
|
|
53742
53742
|
var require_depd = __commonJS({
|
|
53743
53743
|
"../../node_modules/.pnpm/depd@2.0.0/node_modules/depd/index.js"(exports, module) {
|
|
53744
|
-
var
|
|
53744
|
+
var relative5 = __require("path").relative;
|
|
53745
53745
|
module.exports = depd;
|
|
53746
53746
|
var basePath = process.cwd();
|
|
53747
53747
|
function containsNamespace(str, namespace) {
|
|
@@ -53933,7 +53933,7 @@ var require_depd = __commonJS({
|
|
|
53933
53933
|
return formatted;
|
|
53934
53934
|
}
|
|
53935
53935
|
function formatLocation(callSite) {
|
|
53936
|
-
return
|
|
53936
|
+
return relative5(basePath, callSite[0]) + ":" + callSite[1] + ":" + callSite[2];
|
|
53937
53937
|
}
|
|
53938
53938
|
function getStack() {
|
|
53939
53939
|
var limit = Error.stackTraceLimit;
|
|
@@ -54466,9 +54466,9 @@ var require_send = __commonJS({
|
|
|
54466
54466
|
var { parseTokenList } = require_parseTokenList();
|
|
54467
54467
|
var { createHttpError } = require_createHttpError();
|
|
54468
54468
|
var extname = path.extname;
|
|
54469
|
-
var
|
|
54469
|
+
var join21 = path.join;
|
|
54470
54470
|
var normalize5 = path.normalize;
|
|
54471
|
-
var
|
|
54471
|
+
var resolve6 = path.resolve;
|
|
54472
54472
|
var sep6 = path.sep;
|
|
54473
54473
|
var Readable2 = stream.Readable;
|
|
54474
54474
|
var BYTES_RANGE_REGEXP = /^ *bytes=/;
|
|
@@ -54515,7 +54515,7 @@ var require_send = __commonJS({
|
|
|
54515
54515
|
const lastModified = options.lastModified !== void 0 ? Boolean(options.lastModified) : true;
|
|
54516
54516
|
const maxage = normalizeMaxAge(options.maxAge ?? options.maxage);
|
|
54517
54517
|
const maxContentRangeChunkSize = options.maxContentRangeChunkSize !== void 0 ? Number(options.maxContentRangeChunkSize) : null;
|
|
54518
|
-
const root = options.root ?
|
|
54518
|
+
const root = options.root ? resolve6(options.root) : null;
|
|
54519
54519
|
const highWaterMark = Number.isSafeInteger(options.highWaterMark) && options.highWaterMark > 0 ? options.highWaterMark : null;
|
|
54520
54520
|
return {
|
|
54521
54521
|
acceptRanges,
|
|
@@ -54553,14 +54553,14 @@ var require_send = __commonJS({
|
|
|
54553
54553
|
return { statusCode: 403 };
|
|
54554
54554
|
}
|
|
54555
54555
|
parts = path2.split(sep6);
|
|
54556
|
-
path2 = normalize5(
|
|
54556
|
+
path2 = normalize5(join21(root, path2));
|
|
54557
54557
|
} else {
|
|
54558
54558
|
if (UP_PATH_REGEXP.test(path2)) {
|
|
54559
54559
|
debug('malicious path "%s"', path2);
|
|
54560
54560
|
return { statusCode: 403 };
|
|
54561
54561
|
}
|
|
54562
54562
|
parts = normalize5(path2).split(sep6);
|
|
54563
|
-
path2 =
|
|
54563
|
+
path2 = resolve6(path2);
|
|
54564
54564
|
}
|
|
54565
54565
|
return { path: path2, parts };
|
|
54566
54566
|
}
|
|
@@ -54656,9 +54656,9 @@ var require_send = __commonJS({
|
|
|
54656
54656
|
);
|
|
54657
54657
|
}
|
|
54658
54658
|
function tryStat(path2) {
|
|
54659
|
-
return new Promise((
|
|
54659
|
+
return new Promise((resolve7) => {
|
|
54660
54660
|
fs2.stat(path2, function onstat(error40, stat3) {
|
|
54661
|
-
|
|
54661
|
+
resolve7({ error: error40, stat: stat3 });
|
|
54662
54662
|
});
|
|
54663
54663
|
});
|
|
54664
54664
|
}
|
|
@@ -54836,7 +54836,7 @@ var require_send = __commonJS({
|
|
|
54836
54836
|
let err;
|
|
54837
54837
|
for (let i = 0; i < options.index.length; i++) {
|
|
54838
54838
|
const index2 = options.index[i];
|
|
54839
|
-
const p =
|
|
54839
|
+
const p = join21(path2, index2);
|
|
54840
54840
|
const { error: error40, stat: stat3 } = await tryStat(p);
|
|
54841
54841
|
if (error40) {
|
|
54842
54842
|
err = error40;
|
|
@@ -55132,7 +55132,7 @@ var require_content_disposition = __commonJS({
|
|
|
55132
55132
|
"use strict";
|
|
55133
55133
|
module.exports = contentDisposition;
|
|
55134
55134
|
module.exports.parse = parse5;
|
|
55135
|
-
var
|
|
55135
|
+
var basename5 = __require("path").basename;
|
|
55136
55136
|
var Buffer2 = require_safe_buffer().Buffer;
|
|
55137
55137
|
var ENCODE_URL_ATTR_CHAR_REGEXP = /[\x00-\x20"'()*,/:;<=>?@[\\\]{}\x7f]/g;
|
|
55138
55138
|
var HEX_ESCAPE_REGEXP = /%[0-9A-Fa-f]{2}/;
|
|
@@ -55168,9 +55168,9 @@ var require_content_disposition = __commonJS({
|
|
|
55168
55168
|
if (typeof fallback === "string" && NON_LATIN1_REGEXP.test(fallback)) {
|
|
55169
55169
|
throw new TypeError("fallback must be ISO-8859-1 string");
|
|
55170
55170
|
}
|
|
55171
|
-
var name =
|
|
55171
|
+
var name = basename5(filename);
|
|
55172
55172
|
var isQuotedString = TEXT_REGEXP.test(name);
|
|
55173
|
-
var fallbackName = typeof fallback !== "string" ? fallback && getlatin1(name) :
|
|
55173
|
+
var fallbackName = typeof fallback !== "string" ? fallback && getlatin1(name) : basename5(fallback);
|
|
55174
55174
|
var hasFallback = typeof fallbackName === "string" && fallbackName !== name;
|
|
55175
55175
|
if (hasFallback || !isQuotedString || HEX_ESCAPE_REGEXP.test(name)) {
|
|
55176
55176
|
params["filename*"] = name;
|
|
@@ -60013,6 +60013,9 @@ var acpPermissionOptionKindSchema = external_exports.enum([
|
|
|
60013
60013
|
"reject_once",
|
|
60014
60014
|
"reject_always"
|
|
60015
60015
|
]);
|
|
60016
|
+
var lumemModeSchema = external_exports.enum(["ask", "auto", "free"]);
|
|
60017
|
+
var lumemModeDefaultSchema = external_exports.enum(["ask", "auto"]);
|
|
60018
|
+
var acpModeOwnerSchema = external_exports.enum(["agent", "lumem"]);
|
|
60016
60019
|
var acpPermissionOptionSchema = external_exports.object({
|
|
60017
60020
|
optionId: external_exports.string(),
|
|
60018
60021
|
/** The agent's own label, shown verbatim (A13). */
|
|
@@ -60116,13 +60119,40 @@ var acpEventSchema = external_exports.discriminatedUnion("type", [
|
|
|
60116
60119
|
title: external_exports.string(),
|
|
60117
60120
|
command: external_exports.string().nullish(),
|
|
60118
60121
|
cwd: external_exports.string(),
|
|
60119
|
-
options: external_exports.array(acpPermissionOptionSchema).min(1)
|
|
60122
|
+
options: external_exports.array(acpPermissionOptionSchema).min(1),
|
|
60123
|
+
/**
|
|
60124
|
+
* Why the Lumem policy did not answer this one (`session-mode`, T7).
|
|
60125
|
+
*
|
|
60126
|
+
* Null under `ask`, and under an agent that owns its own modes: there is
|
|
60127
|
+
* nothing to explain when asking *is* the rule. Set when a policy that would
|
|
60128
|
+
* normally answer did not — `auto` facing a write, or an agent that offered
|
|
60129
|
+
* no way to allow (Q6) — because "why am I being asked in auto mode?" is
|
|
60130
|
+
* otherwise a question the screen cannot answer.
|
|
60131
|
+
*/
|
|
60132
|
+
policyReason: external_exports.string().nullable().default(null)
|
|
60120
60133
|
}),
|
|
60121
60134
|
external_exports.object({
|
|
60122
60135
|
type: external_exports.literal("permission_resolved"),
|
|
60123
60136
|
requestId: external_exports.string(),
|
|
60124
60137
|
/** `"cancelled"` is the agent giving up on the ask, not the user denying. */
|
|
60125
|
-
outcome: external_exports.union([external_exports.literal("cancelled"), external_exports.object({ optionId: external_exports.string() })])
|
|
60138
|
+
outcome: external_exports.union([external_exports.literal("cancelled"), external_exports.object({ optionId: external_exports.string() })]),
|
|
60139
|
+
/**
|
|
60140
|
+
* Who signed it (F1.6).
|
|
60141
|
+
*
|
|
60142
|
+
* `default` and not `optional`, for two reasons that point the same way: a
|
|
60143
|
+
* transcript recorded before this feature has no field and must still open,
|
|
60144
|
+
* and `"user"` is the truthful answer for every one of them — nothing but a
|
|
60145
|
+
* person could answer a permission request back then.
|
|
60146
|
+
*/
|
|
60147
|
+
by: external_exports.enum(["user", "lumem"]).default("user"),
|
|
60148
|
+
/**
|
|
60149
|
+
* Why the policy answered, in the words the card shows.
|
|
60150
|
+
*
|
|
60151
|
+
* Only ever set when `by` is `"lumem"`: a person clicking a button is its
|
|
60152
|
+
* own reason, and inventing prose for it would be the daemon narrating the
|
|
60153
|
+
* user's intent.
|
|
60154
|
+
*/
|
|
60155
|
+
reason: external_exports.string().nullable().default(null)
|
|
60126
60156
|
}),
|
|
60127
60157
|
/**
|
|
60128
60158
|
* The plan, whole (F2.5).
|
|
@@ -60167,7 +60197,24 @@ var acpEventSchema = external_exports.discriminatedUnion("type", [
|
|
|
60167
60197
|
* sending this. So the client replaces rather than merging, and there is exactly
|
|
60168
60198
|
* one place that has to get the merge right.
|
|
60169
60199
|
*/
|
|
60170
|
-
options: external_exports.array(acpConfigOptionSchema)
|
|
60200
|
+
options: external_exports.array(acpConfigOptionSchema),
|
|
60201
|
+
/**
|
|
60202
|
+
* Which of the two authorities is in charge right now (A1).
|
|
60203
|
+
*
|
|
60204
|
+
* Defaults to `"agent"` so a transcript recorded before `session-mode` reads
|
|
60205
|
+
* as what it was: a conversation where only the agent could offer a mode.
|
|
60206
|
+
*/
|
|
60207
|
+
modeOwner: acpModeOwnerSchema.default("agent"),
|
|
60208
|
+
/**
|
|
60209
|
+
* The policy value, whoever owns the selector.
|
|
60210
|
+
*
|
|
60211
|
+
* Sent even while the agent owns the mode, and that is on purpose: the value
|
|
60212
|
+
* is *kept*, not erased, so an agent that stops reporting modes hands the
|
|
60213
|
+
* conversation back to the policy it already had (T12).
|
|
60214
|
+
*/
|
|
60215
|
+
lumemMode: lumemModeSchema.default("ask"),
|
|
60216
|
+
/** What a new session in this workspace would start at — the menu's footer (Q5). */
|
|
60217
|
+
lumemModeDefault: lumemModeDefaultSchema.default("ask")
|
|
60171
60218
|
}),
|
|
60172
60219
|
/** What `/` offers (F2.8). An empty list means the agent offers none. */
|
|
60173
60220
|
external_exports.object({ type: external_exports.literal("commands"), commands: external_exports.array(acpCommandSchema) }),
|
|
@@ -60265,7 +60312,17 @@ var acpClientMessageSchema = external_exports.discriminatedUnion("type", [
|
|
|
60265
60312
|
type: external_exports.literal("set_config"),
|
|
60266
60313
|
optionId: external_exports.string().min(1),
|
|
60267
60314
|
value: external_exports.string().min(1)
|
|
60268
|
-
})
|
|
60315
|
+
}),
|
|
60316
|
+
/**
|
|
60317
|
+
* Switch the policy Lumem applies when the agent offers no modes (F1.1).
|
|
60318
|
+
*
|
|
60319
|
+
* A message of its own rather than a `set_config` with `optionId: "mode"`,
|
|
60320
|
+
* because it is not the same act: `set_config` forwards a value to the agent
|
|
60321
|
+
* and this one never leaves the daemon. Sharing the message would put two
|
|
60322
|
+
* destinations behind one name, and the first bug would be a policy change
|
|
60323
|
+
* quietly forwarded to an agent that does not have the option.
|
|
60324
|
+
*/
|
|
60325
|
+
external_exports.object({ type: external_exports.literal("set_lumem_mode"), mode: lumemModeSchema })
|
|
60269
60326
|
]);
|
|
60270
60327
|
var acpServerMessageSchema = external_exports.discriminatedUnion("type", [
|
|
60271
60328
|
/**
|
|
@@ -60288,6 +60345,23 @@ var acpServerMessageSchema = external_exports.discriminatedUnion("type", [
|
|
|
60288
60345
|
* broken for as long as nothing changed.
|
|
60289
60346
|
*/
|
|
60290
60347
|
configOptions: external_exports.array(acpConfigOptionSchema).default([]),
|
|
60348
|
+
/**
|
|
60349
|
+
* The mode state, on attach, for the same reason `configOptions` is here: a
|
|
60350
|
+
* composer whose mode pill appeared only once something changed would look
|
|
60351
|
+
* broken for as long as nothing did.
|
|
60352
|
+
*/
|
|
60353
|
+
modeOwner: acpModeOwnerSchema.default("agent"),
|
|
60354
|
+
lumemMode: lumemModeSchema.default("ask"),
|
|
60355
|
+
lumemModeDefault: lumemModeDefaultSchema.default("ask"),
|
|
60356
|
+
/**
|
|
60357
|
+
* The checkout this conversation runs in.
|
|
60358
|
+
*
|
|
60359
|
+
* Here because the `free` gate has to name what it is opening, and it names
|
|
60360
|
+
* it as a **path on disk** rather than as "the worktree": the path is what
|
|
60361
|
+
* says the size of the damage (Q4). A gate that could not say it would be
|
|
60362
|
+
* asking for a decision without stating its scope.
|
|
60363
|
+
*/
|
|
60364
|
+
cwd: external_exports.string().default(""),
|
|
60291
60365
|
transcript: external_exports.array(acpTranscriptEntrySchema)
|
|
60292
60366
|
}),
|
|
60293
60367
|
external_exports.object({
|
|
@@ -60330,16 +60404,46 @@ function encodeAcpServerMessage(message) {
|
|
|
60330
60404
|
return JSON.stringify(message);
|
|
60331
60405
|
}
|
|
60332
60406
|
|
|
60407
|
+
// ../shared/src/adapters.ts
|
|
60408
|
+
function adapterInstallCommand(spec) {
|
|
60409
|
+
return spec.package === null ? null : `npm i -g ${spec.package}@${spec.pinnedVersion}`;
|
|
60410
|
+
}
|
|
60411
|
+
var CLAUDE_ADAPTER = {
|
|
60412
|
+
id: "claude",
|
|
60413
|
+
label: "Claude Code",
|
|
60414
|
+
package: "@agentclientprotocol/claude-agent-acp",
|
|
60415
|
+
// Duas strings porque não são a mesma string, e é exatamente essa a armadilha:
|
|
60416
|
+
// o pacote é escopado e o binário não.
|
|
60417
|
+
command: "claude-agent-acp",
|
|
60418
|
+
pinnedVersion: "0.40.0",
|
|
60419
|
+
cli: { command: "claude", install: null },
|
|
60420
|
+
apiKeyEnv: ["ANTHROPIC_API_KEY"]
|
|
60421
|
+
};
|
|
60422
|
+
var CODEX_ADAPTER = {
|
|
60423
|
+
id: "codex",
|
|
60424
|
+
label: "Codex",
|
|
60425
|
+
package: "@agentclientprotocol/codex-acp",
|
|
60426
|
+
command: "codex-acp",
|
|
60427
|
+
// Medido na fase 0, em 2026-09-06: é o que ele reportou como
|
|
60428
|
+
// `agentInfo.version`.
|
|
60429
|
+
pinnedVersion: "1.10.0",
|
|
60430
|
+
// Null, e não `{ command: "codex" }` por simetria: ele traz o próprio.
|
|
60431
|
+
cli: null,
|
|
60432
|
+
apiKeyEnv: ["CODEX_API_KEY", "OPENAI_API_KEY"]
|
|
60433
|
+
};
|
|
60434
|
+
var ADAPTERS = [CLAUDE_ADAPTER, CODEX_ADAPTER];
|
|
60435
|
+
var DEFAULT_ADAPTER_ID = CLAUDE_ADAPTER.id;
|
|
60436
|
+
function adapterById(id) {
|
|
60437
|
+
return ADAPTERS.find((spec) => spec.id === id) ?? null;
|
|
60438
|
+
}
|
|
60439
|
+
function adapterByCommand(command) {
|
|
60440
|
+
return ADAPTERS.find((spec) => spec.command === command) ?? null;
|
|
60441
|
+
}
|
|
60442
|
+
|
|
60333
60443
|
// ../shared/src/constants.ts
|
|
60334
|
-
var LUMEM_VERSION = "0.
|
|
60444
|
+
var LUMEM_VERSION = "0.3.0";
|
|
60335
60445
|
var DEFAULT_SERVER_PORT = 4317;
|
|
60336
|
-
var ACP_ADAPTER_COMMAND = "claude-agent-acp";
|
|
60337
|
-
var ACP_ADAPTER_PACKAGE = "@agentclientprotocol/claude-agent-acp";
|
|
60338
|
-
var ACP_ADAPTER_INSTALL = `npm i -g ${ACP_ADAPTER_PACKAGE}`;
|
|
60339
|
-
var CLAUDE_CLI_COMMAND = "claude";
|
|
60340
|
-
var ANTHROPIC_API_KEY_ENV = "ANTHROPIC_API_KEY";
|
|
60341
60446
|
var MIN_GIT_VERSION = { major: 2, minor: 30 };
|
|
60342
|
-
var ACP_ADAPTER_PINNED_VERSION = "0.40.0";
|
|
60343
60447
|
var ADAPTERS_DIR_NAME = "adapters";
|
|
60344
60448
|
var ACP_AUTH_REQUIRED_CODE = -32e3;
|
|
60345
60449
|
|
|
@@ -60794,6 +60898,32 @@ function createGitService({ exec = execGit } = {}) {
|
|
|
60794
60898
|
const [behind, ahead] = stdout.trim().split(/\s+/).map(Number);
|
|
60795
60899
|
return { ahead: ahead ?? 0, behind: behind ?? 0 };
|
|
60796
60900
|
},
|
|
60901
|
+
async getRemoteUrl(path) {
|
|
60902
|
+
const { stdout } = await exec(["remote", "get-url", "origin"], { cwd: path }).catch(() => ({
|
|
60903
|
+
stdout: "",
|
|
60904
|
+
stderr: ""
|
|
60905
|
+
}));
|
|
60906
|
+
const url2 = stdout.trim();
|
|
60907
|
+
return url2 === "" ? null : url2;
|
|
60908
|
+
},
|
|
60909
|
+
async getSubject(path) {
|
|
60910
|
+
const { stdout } = await exec(["log", "-1", "--format=%s"], { cwd: path }).catch(() => ({
|
|
60911
|
+
stdout: "",
|
|
60912
|
+
stderr: ""
|
|
60913
|
+
}));
|
|
60914
|
+
const subject = stdout.trim();
|
|
60915
|
+
return subject === "" ? null : subject;
|
|
60916
|
+
},
|
|
60917
|
+
async hasRemoteBranch(path, branch) {
|
|
60918
|
+
const { stdout } = await exec(["for-each-ref", "--format=%(refname)", "refs/remotes/"], {
|
|
60919
|
+
cwd: path
|
|
60920
|
+
}).catch(() => ({ stdout: "", stderr: "" }));
|
|
60921
|
+
return stdout.split("\n").map((line) => line.trim()).filter((line) => line !== "").some((ref) => {
|
|
60922
|
+
const rest = ref.slice("refs/remotes/".length);
|
|
60923
|
+
const slash = rest.indexOf("/");
|
|
60924
|
+
return slash !== -1 && rest.slice(slash + 1) === branch;
|
|
60925
|
+
});
|
|
60926
|
+
},
|
|
60797
60927
|
async listChanges(path, input) {
|
|
60798
60928
|
const comparedTo = await resolveComparison(path, input);
|
|
60799
60929
|
const tracked2 = comparedTo === null ? [] : await trackedChanges(path, comparedTo);
|
|
@@ -62026,7 +62156,7 @@ function sql(strings, ...params) {
|
|
|
62026
62156
|
return new SQL([new StringChunk(str)]);
|
|
62027
62157
|
}
|
|
62028
62158
|
sql2.raw = raw;
|
|
62029
|
-
function
|
|
62159
|
+
function join21(chunks, separator) {
|
|
62030
62160
|
const result = [];
|
|
62031
62161
|
for (const [i, chunk] of chunks.entries()) {
|
|
62032
62162
|
if (i > 0 && separator !== void 0) {
|
|
@@ -62036,7 +62166,7 @@ function sql(strings, ...params) {
|
|
|
62036
62166
|
}
|
|
62037
62167
|
return new SQL(result);
|
|
62038
62168
|
}
|
|
62039
|
-
sql2.join =
|
|
62169
|
+
sql2.join = join21;
|
|
62040
62170
|
function identifier(value) {
|
|
62041
62171
|
return new Name(value);
|
|
62042
62172
|
}
|
|
@@ -64603,7 +64733,7 @@ var SQLiteSelectQueryBuilderBase = class extends TypedQueryBuilder {
|
|
|
64603
64733
|
const baseTableName = this.tableName;
|
|
64604
64734
|
const tableName = getTableLikeName(table);
|
|
64605
64735
|
for (const item of extractUsedTable(table)) this.usedTables.add(item);
|
|
64606
|
-
if (typeof tableName === "string" && this.config.joins?.some((
|
|
64736
|
+
if (typeof tableName === "string" && this.config.joins?.some((join21) => join21.alias === tableName)) {
|
|
64607
64737
|
throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
64608
64738
|
}
|
|
64609
64739
|
if (!this.isPartialSelect) {
|
|
@@ -65462,7 +65592,7 @@ var SQLiteUpdateBase = class extends QueryPromise {
|
|
|
65462
65592
|
createJoin(joinType) {
|
|
65463
65593
|
return (table, on2) => {
|
|
65464
65594
|
const tableName = getTableLikeName(table);
|
|
65465
|
-
if (typeof tableName === "string" && this.config.joins.some((
|
|
65595
|
+
if (typeof tableName === "string" && this.config.joins.some((join21) => join21.alias === tableName)) {
|
|
65466
65596
|
throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
65467
65597
|
}
|
|
65468
65598
|
if (typeof on2 === "function") {
|
|
@@ -66328,11 +66458,26 @@ var timestamps = {
|
|
|
66328
66458
|
createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull().default(NOW),
|
|
66329
66459
|
updatedAt: integer("updated_at", { mode: "timestamp_ms" }).notNull().default(NOW)
|
|
66330
66460
|
};
|
|
66331
|
-
var workspace = sqliteTable(
|
|
66332
|
-
|
|
66333
|
-
|
|
66334
|
-
|
|
66335
|
-
|
|
66461
|
+
var workspace = sqliteTable(
|
|
66462
|
+
"workspace",
|
|
66463
|
+
{
|
|
66464
|
+
id: text("id").primaryKey(),
|
|
66465
|
+
name: text("name").notNull().unique(),
|
|
66466
|
+
/**
|
|
66467
|
+
* O modo do Lumem que uma sessão nova deste workspace herda (`session-mode`, Q5).
|
|
66468
|
+
*
|
|
66469
|
+
* Sem `'free'` no CHECK, e isso é a decisão inteira em uma linha: o portão do
|
|
66470
|
+
* `liberado` é **por sessão**, e um padrão que o atravessasse sozinho o
|
|
66471
|
+
* anularia. Recusar na ESCRITA, e não silenciar na leitura — silenciar é o
|
|
66472
|
+
* modo de falha que ninguém percebe.
|
|
66473
|
+
*/
|
|
66474
|
+
defaultLumemMode: text("default_lumem_mode").notNull().default("ask"),
|
|
66475
|
+
...timestamps
|
|
66476
|
+
},
|
|
66477
|
+
(table) => [
|
|
66478
|
+
check("workspace_default_lumem_mode", sql`${table.defaultLumemMode} IN ('ask', 'auto')`)
|
|
66479
|
+
]
|
|
66480
|
+
);
|
|
66336
66481
|
var project = sqliteTable(
|
|
66337
66482
|
"project",
|
|
66338
66483
|
{
|
|
@@ -66479,6 +66624,18 @@ var session = sqliteTable(
|
|
|
66479
66624
|
/** Current permission mode and model, as the protocol reports them. */
|
|
66480
66625
|
mode: text("mode"),
|
|
66481
66626
|
model: text("model"),
|
|
66627
|
+
/**
|
|
66628
|
+
* A política do Lumem desta conversa (`session-mode`, F1.4).
|
|
66629
|
+
*
|
|
66630
|
+
* Coluna própria, e **não** reaproveitamento do `mode` acima: aquele é o modo
|
|
66631
|
+
* do protocolo, relatado pelo agente, e este é o que o daemon responde a um
|
|
66632
|
+
* pedido de permissão. Guardar os dois no mesmo lugar seria perder a única
|
|
66633
|
+
* informação que separa "o agente tentou" de "o Lumem deixou passar".
|
|
66634
|
+
*
|
|
66635
|
+
* `NOT NULL DEFAULT 'ask'` faz toda sessão gravada antes desta feature
|
|
66636
|
+
* acordar perguntando — nenhuma acorda liberada.
|
|
66637
|
+
*/
|
|
66638
|
+
lumemMode: text("lumem_mode").notNull().default("ask"),
|
|
66482
66639
|
/**
|
|
66483
66640
|
* The session this one continues (F5.2, D12).
|
|
66484
66641
|
*
|
|
@@ -66539,7 +66696,10 @@ var session = sqliteTable(
|
|
|
66539
66696
|
"session_acp_id",
|
|
66540
66697
|
sql`(${table.transport} = 'acp' AND ${table.acpSessionId} IS NOT NULL)
|
|
66541
66698
|
OR (${table.transport} = 'pty' AND ${table.acpSessionId} IS NULL)`
|
|
66542
|
-
)
|
|
66699
|
+
),
|
|
66700
|
+
// Os três valores da política, e o `free` entra aqui — o que o workspace não
|
|
66701
|
+
// pode é *herdar* liberado; uma sessão pode chegar lá pelo portão.
|
|
66702
|
+
check("session_lumem_mode", sql`${table.lumemMode} IN ('ask', 'auto', 'free')`)
|
|
66543
66703
|
]
|
|
66544
66704
|
);
|
|
66545
66705
|
var checkoutPort = sqliteTable(
|
|
@@ -66758,6 +66918,20 @@ var sessionUsage = sqliteTable(
|
|
|
66758
66918
|
projectId: text("project_id").notNull(),
|
|
66759
66919
|
/** A worktree, quando a sessão rodou numa. `''` quando ela é do projeto. */
|
|
66760
66920
|
worktreeId: text("worktree_id").notNull().default(""),
|
|
66921
|
+
/**
|
|
66922
|
+
* Qual agente gastou (`second-agent`, F5).
|
|
66923
|
+
*
|
|
66924
|
+
* Resolvido na escrita, como o projeto e a worktree, e pela mesma razão: a
|
|
66925
|
+
* pergunta "quanto cada agente custou" não pode depender de um join com a
|
|
66926
|
+
* `session`, que é uma tabela que muda e de onde a linha pode sumir.
|
|
66927
|
+
*
|
|
66928
|
+
* Anulável, e sem chave estrangeira. Anulável porque a sessão de shell e a de
|
|
66929
|
+
* script não têm agente, e porque a linha gravada **antes** desta coluna não
|
|
66930
|
+
* ganha um agente inventado — ela fica de fora do agrupamento, o que é a
|
|
66931
|
+
* verdade. Sem estrangeira porque consumo é histórico: apagar a configuração
|
|
66932
|
+
* de ontem não pode apagar o que ela gastou.
|
|
66933
|
+
*/
|
|
66934
|
+
agentConfigId: text("agent_config_id"),
|
|
66761
66935
|
/** A variação da janela de contexto neste turno. Nunca negativa. */
|
|
66762
66936
|
tokens: integer("tokens").notNull().default(0),
|
|
66763
66937
|
/**
|
|
@@ -66916,95 +67090,8 @@ async function withConstraints(run3, translations) {
|
|
|
66916
67090
|
}
|
|
66917
67091
|
}
|
|
66918
67092
|
|
|
66919
|
-
// src/repositories/agentConfig.ts
|
|
66920
|
-
var DEFAULT_AGENT_CONFIG = {
|
|
66921
|
-
name: "claude-code",
|
|
66922
|
-
command: "claude",
|
|
66923
|
-
args: [],
|
|
66924
|
-
env: {},
|
|
66925
|
-
transport: "pty"
|
|
66926
|
-
};
|
|
66927
|
-
function conflicts(name) {
|
|
66928
|
-
return {
|
|
66929
|
-
"unique:agent_config.name": {
|
|
66930
|
-
code: "DUPLICATE",
|
|
66931
|
-
message: `j\xE1 existe uma configura\xE7\xE3o chamada "${name}"`
|
|
66932
|
-
},
|
|
66933
|
-
foreignKey: {
|
|
66934
|
-
code: "IN_USE",
|
|
66935
|
-
message: "a configura\xE7\xE3o ainda est\xE1 em uso por alguma sess\xE3o"
|
|
66936
|
-
},
|
|
66937
|
-
// Without this the CHECK surfaces as a raw SQLite error, which reads as a
|
|
66938
|
-
// daemon defect rather than as the one thing the caller got wrong.
|
|
66939
|
-
"check:agent_config_adapter_version": {
|
|
66940
|
-
code: "INVALID_ARGUMENT",
|
|
66941
|
-
message: "configura\xE7\xE3o ACP precisa de uma vers\xE3o de adaptador fixa, e configura\xE7\xE3o PTY n\xE3o pode ter uma"
|
|
66942
|
-
},
|
|
66943
|
-
"check:agent_config_transport": {
|
|
66944
|
-
code: "INVALID_ARGUMENT",
|
|
66945
|
-
message: "transporte precisa ser pty ou acp"
|
|
66946
|
-
}
|
|
66947
|
-
};
|
|
66948
|
-
}
|
|
66949
|
-
function createAgentConfigRepository(db) {
|
|
66950
|
-
async function require_(id) {
|
|
66951
|
-
const found = await db.query.agentConfig.findFirst({ where: eq(agentConfig.id, id) });
|
|
66952
|
-
if (!found) throw new DomainError("NOT_FOUND", `configura\xE7\xE3o ${id} n\xE3o existe`);
|
|
66953
|
-
return found;
|
|
66954
|
-
}
|
|
66955
|
-
return {
|
|
66956
|
-
async create({ name, command, args = [], env = {}, transport = "pty", adapterVersion = null }) {
|
|
66957
|
-
const [row] = await withConstraints(
|
|
66958
|
-
() => db.insert(agentConfig).values({ id: newId(), name, command, args, env, transport, adapterVersion }).returning(),
|
|
66959
|
-
conflicts(name)
|
|
66960
|
-
);
|
|
66961
|
-
return row;
|
|
66962
|
-
},
|
|
66963
|
-
list() {
|
|
66964
|
-
return db.select().from(agentConfig).orderBy(asc(agentConfig.name));
|
|
66965
|
-
},
|
|
66966
|
-
findById(id) {
|
|
66967
|
-
return db.query.agentConfig.findFirst({ where: eq(agentConfig.id, id) });
|
|
66968
|
-
},
|
|
66969
|
-
findByName(name) {
|
|
66970
|
-
return db.query.agentConfig.findFirst({ where: eq(agentConfig.name, name) });
|
|
66971
|
-
},
|
|
66972
|
-
async update(id, input) {
|
|
66973
|
-
const current = await require_(id);
|
|
66974
|
-
const [row] = await withConstraints(
|
|
66975
|
-
() => db.update(agentConfig).set({ ...input, updatedAt: /* @__PURE__ */ new Date() }).where(eq(agentConfig.id, id)).returning(),
|
|
66976
|
-
conflicts(input.name ?? current.name)
|
|
66977
|
-
);
|
|
66978
|
-
return row;
|
|
66979
|
-
},
|
|
66980
|
-
async remove(id) {
|
|
66981
|
-
await require_(id);
|
|
66982
|
-
await withConstraints(
|
|
66983
|
-
() => db.delete(agentConfig).where(eq(agentConfig.id, id)).returning(),
|
|
66984
|
-
conflicts("")
|
|
66985
|
-
);
|
|
66986
|
-
},
|
|
66987
|
-
async seedDefaults() {
|
|
66988
|
-
const existing = await db.query.agentConfig.findFirst({
|
|
66989
|
-
where: eq(agentConfig.name, DEFAULT_AGENT_CONFIG.name)
|
|
66990
|
-
});
|
|
66991
|
-
if (existing) return;
|
|
66992
|
-
await db.insert(agentConfig).values({
|
|
66993
|
-
id: newId(),
|
|
66994
|
-
name: DEFAULT_AGENT_CONFIG.name,
|
|
66995
|
-
command: DEFAULT_AGENT_CONFIG.command,
|
|
66996
|
-
args: DEFAULT_AGENT_CONFIG.args ?? [],
|
|
66997
|
-
env: DEFAULT_AGENT_CONFIG.env ?? {},
|
|
66998
|
-
// Still PTY. The default configuration changes transport when the
|
|
66999
|
-
// conversation can render one end to end, not when the column exists.
|
|
67000
|
-
transport: DEFAULT_AGENT_CONFIG.transport ?? "pty"
|
|
67001
|
-
});
|
|
67002
|
-
}
|
|
67003
|
-
};
|
|
67004
|
-
}
|
|
67005
|
-
|
|
67006
67093
|
// src/repositories/project.ts
|
|
67007
|
-
function
|
|
67094
|
+
function conflicts(name, path) {
|
|
67008
67095
|
return {
|
|
67009
67096
|
"unique:project.workspace_id,project.name": {
|
|
67010
67097
|
code: "DUPLICATE",
|
|
@@ -67032,7 +67119,7 @@ function createProjectRepository(db) {
|
|
|
67032
67119
|
async create(input) {
|
|
67033
67120
|
const [row] = await withConstraints(
|
|
67034
67121
|
() => db.insert(project).values({ id: newId(), ...input }).returning(),
|
|
67035
|
-
|
|
67122
|
+
conflicts(input.name, input.path)
|
|
67036
67123
|
);
|
|
67037
67124
|
return row;
|
|
67038
67125
|
},
|
|
@@ -67053,7 +67140,7 @@ function createProjectRepository(db) {
|
|
|
67053
67140
|
await require_(id);
|
|
67054
67141
|
const [row] = await withConstraints(
|
|
67055
67142
|
() => db.update(project).set({ name, updatedAt: /* @__PURE__ */ new Date() }).where(eq(project.id, id)).returning(),
|
|
67056
|
-
|
|
67143
|
+
conflicts(name)
|
|
67057
67144
|
);
|
|
67058
67145
|
return row;
|
|
67059
67146
|
},
|
|
@@ -67063,11 +67150,9 @@ function createProjectRepository(db) {
|
|
|
67063
67150
|
},
|
|
67064
67151
|
async remove(id) {
|
|
67065
67152
|
await require_(id);
|
|
67066
|
-
|
|
67067
|
-
|
|
67068
|
-
|
|
67069
|
-
message: "o projeto ainda tem worktrees registradas; remova-as antes"
|
|
67070
|
-
}
|
|
67153
|
+
db.transaction((tx) => {
|
|
67154
|
+
tx.delete(worktree).where(eq(worktree.projectId, id)).run();
|
|
67155
|
+
tx.delete(project).where(eq(project.id, id)).run();
|
|
67071
67156
|
});
|
|
67072
67157
|
}
|
|
67073
67158
|
};
|
|
@@ -67131,6 +67216,10 @@ function createSessionRepository(db) {
|
|
|
67131
67216
|
await db.update(session).set({
|
|
67132
67217
|
...config2.mode === void 0 ? {} : { mode: config2.mode },
|
|
67133
67218
|
...config2.model === void 0 ? {} : { model: config2.model },
|
|
67219
|
+
// A política do Lumem entra pelo mesmo caminho do modo do protocolo,
|
|
67220
|
+
// e em coluna diferente: uma é o que o agente relatou, a outra é o que
|
|
67221
|
+
// o daemon responde. Guardar as duas juntas apagaria a diferença.
|
|
67222
|
+
...config2.lumemMode === void 0 ? {} : { lumemMode: config2.lumemMode },
|
|
67134
67223
|
updatedAt: /* @__PURE__ */ new Date()
|
|
67135
67224
|
}).where(and(eq(session.id, id), eq(session.state, "running")));
|
|
67136
67225
|
},
|
|
@@ -67201,7 +67290,7 @@ function createWorkspaceRepository(db) {
|
|
|
67201
67290
|
}
|
|
67202
67291
|
|
|
67203
67292
|
// src/repositories/worktree.ts
|
|
67204
|
-
function
|
|
67293
|
+
function conflicts2(name) {
|
|
67205
67294
|
return {
|
|
67206
67295
|
"unique:worktree.project_id,worktree.name": {
|
|
67207
67296
|
code: "DUPLICATE",
|
|
@@ -67224,7 +67313,7 @@ function createWorktreeRepository(db) {
|
|
|
67224
67313
|
async create(input) {
|
|
67225
67314
|
const [row] = await withConstraints(
|
|
67226
67315
|
() => db.insert(worktree).values({ id: newId(), ...input }).returning(),
|
|
67227
|
-
|
|
67316
|
+
conflicts2(input.name)
|
|
67228
67317
|
);
|
|
67229
67318
|
return row;
|
|
67230
67319
|
},
|
|
@@ -67242,7 +67331,7 @@ function createWorktreeRepository(db) {
|
|
|
67242
67331
|
await require_(id);
|
|
67243
67332
|
const [row] = await withConstraints(
|
|
67244
67333
|
() => db.update(worktree).set({ state, updatedAt: /* @__PURE__ */ new Date() }).where(eq(worktree.id, id)).returning(),
|
|
67245
|
-
|
|
67334
|
+
conflicts2("")
|
|
67246
67335
|
);
|
|
67247
67336
|
return row;
|
|
67248
67337
|
},
|
|
@@ -67250,7 +67339,7 @@ function createWorktreeRepository(db) {
|
|
|
67250
67339
|
await require_(id);
|
|
67251
67340
|
const [row] = await withConstraints(
|
|
67252
67341
|
() => db.update(worktree).set({ path, updatedAt: /* @__PURE__ */ new Date() }).where(eq(worktree.id, id)).returning(),
|
|
67253
|
-
|
|
67342
|
+
conflicts2("")
|
|
67254
67343
|
);
|
|
67255
67344
|
return row;
|
|
67256
67345
|
},
|
|
@@ -67468,7 +67557,6 @@ async function subdirectories(path) {
|
|
|
67468
67557
|
return entries.filter((entry) => entry.isDirectory()).map((entry) => join4(path, entry.name));
|
|
67469
67558
|
}
|
|
67470
67559
|
async function reconcileOnBoot(options) {
|
|
67471
|
-
await createAgentConfigRepository(options.db).seedDefaults();
|
|
67472
67560
|
const layout = await migrateWorktreeLayout(options);
|
|
67473
67561
|
const clones = await reconcileClones(options).catch(() => 0);
|
|
67474
67562
|
const worktrees = await reconcileWorktrees(options);
|
|
@@ -81018,11 +81106,11 @@ var Connection = class {
|
|
|
81018
81106
|
const id = this.nextRequestId++;
|
|
81019
81107
|
let cancel = () => {
|
|
81020
81108
|
};
|
|
81021
|
-
const response = new Promise((
|
|
81109
|
+
const response = new Promise((resolve6, reject) => {
|
|
81022
81110
|
const pendingResponse = {
|
|
81023
81111
|
resolve: (value) => {
|
|
81024
81112
|
try {
|
|
81025
|
-
|
|
81113
|
+
resolve6(mapResponse ? mapResponse(value) : value);
|
|
81026
81114
|
} catch (error40) {
|
|
81027
81115
|
reject(error40);
|
|
81028
81116
|
}
|
|
@@ -81079,8 +81167,8 @@ var Connection = class {
|
|
|
81079
81167
|
this.stream = stream;
|
|
81080
81168
|
this.staticHandlers = handlers2;
|
|
81081
81169
|
this.allowBatches = options?.allowBatches ?? true;
|
|
81082
|
-
this.closedPromise = new Promise((
|
|
81083
|
-
this.abortController.signal.addEventListener("abort", () =>
|
|
81170
|
+
this.closedPromise = new Promise((resolve6) => {
|
|
81171
|
+
this.abortController.signal.addEventListener("abort", () => resolve6());
|
|
81084
81172
|
});
|
|
81085
81173
|
void this.receive();
|
|
81086
81174
|
}
|
|
@@ -81961,8 +82049,8 @@ var AsyncQueue = class {
|
|
|
81961
82049
|
if (this.failed) {
|
|
81962
82050
|
return Promise.reject(this.failure);
|
|
81963
82051
|
}
|
|
81964
|
-
return new Promise((
|
|
81965
|
-
this.waiters.push({ resolve:
|
|
82052
|
+
return new Promise((resolve6, reject) => {
|
|
82053
|
+
this.waiters.push({ resolve: resolve6, reject });
|
|
81966
82054
|
});
|
|
81967
82055
|
}
|
|
81968
82056
|
};
|
|
@@ -82566,6 +82654,9 @@ var legacyClientNotificationMethods = /* @__PURE__ */ new Set([
|
|
|
82566
82654
|
CLIENT_METHODS.elicitation_complete
|
|
82567
82655
|
]);
|
|
82568
82656
|
|
|
82657
|
+
// src/acp/AcpManager.ts
|
|
82658
|
+
import { basename as basename3 } from "node:path";
|
|
82659
|
+
|
|
82569
82660
|
// src/agents/availability.ts
|
|
82570
82661
|
import { accessSync, constants } from "node:fs";
|
|
82571
82662
|
import { delimiter, isAbsolute, join as join5 } from "node:path";
|
|
@@ -82654,27 +82745,27 @@ function touchesGit(candidate) {
|
|
|
82654
82745
|
return candidate.split(sep2).includes(GIT_DIR);
|
|
82655
82746
|
}
|
|
82656
82747
|
async function resolveInsideRoot(root, requested) {
|
|
82657
|
-
const
|
|
82748
|
+
const relative5 = normalizeRelative(requested);
|
|
82658
82749
|
const realRoot = await realRootOf(root);
|
|
82659
|
-
const target = join6(realRoot,
|
|
82750
|
+
const target = join6(realRoot, relative5);
|
|
82660
82751
|
let absolute;
|
|
82661
82752
|
try {
|
|
82662
82753
|
absolute = await realpath3(target);
|
|
82663
82754
|
} catch (error40) {
|
|
82664
82755
|
const code = error40.code;
|
|
82665
82756
|
if (code === "EACCES" || code === "EPERM") {
|
|
82666
|
-
throw new DomainError("BLOCKED", `sem permiss\xE3o de leitura em ${
|
|
82757
|
+
throw new DomainError("BLOCKED", `sem permiss\xE3o de leitura em ${relative5 || "."}`);
|
|
82667
82758
|
}
|
|
82668
|
-
throw new DomainError("NOT_FOUND", `${
|
|
82759
|
+
throw new DomainError("NOT_FOUND", `${relative5 || "."} n\xE3o existe no checkout`);
|
|
82669
82760
|
}
|
|
82670
82761
|
if (!inside(realRoot, absolute)) {
|
|
82671
82762
|
throw new DomainError(
|
|
82672
82763
|
"BLOCKED",
|
|
82673
|
-
target === absolute ? `${
|
|
82764
|
+
target === absolute ? `${relative5} est\xE1 fora do checkout` : `${relative5} aponta para fora do checkout`
|
|
82674
82765
|
);
|
|
82675
82766
|
}
|
|
82676
|
-
const insideGit = touchesGit(
|
|
82677
|
-
return { absolute, relative:
|
|
82767
|
+
const insideGit = touchesGit(relative5) || touchesGit(relativeTo(realRoot, absolute));
|
|
82768
|
+
return { absolute, relative: relative5, insideGit };
|
|
82678
82769
|
}
|
|
82679
82770
|
function refuseWritingIntoGit(shown, candidate) {
|
|
82680
82771
|
if (!touchesGit(candidate)) return;
|
|
@@ -82690,13 +82781,13 @@ async function diskNameOf(parent, requested) {
|
|
|
82690
82781
|
return names.find((name) => name.toLowerCase() === folded) ?? requested;
|
|
82691
82782
|
}
|
|
82692
82783
|
async function resolveForWrite(root, requested) {
|
|
82693
|
-
const
|
|
82694
|
-
if (
|
|
82784
|
+
const relative5 = normalizeRelative(requested);
|
|
82785
|
+
if (relative5 === "") {
|
|
82695
82786
|
throw new DomainError("INVALID_ARGUMENT", "a raiz do checkout n\xE3o aceita escrita");
|
|
82696
82787
|
}
|
|
82697
|
-
refuseWritingIntoGit(
|
|
82788
|
+
refuseWritingIntoGit(relative5, relative5);
|
|
82698
82789
|
const realRoot = await realRootOf(root);
|
|
82699
|
-
const parentRelative = dirname4(
|
|
82790
|
+
const parentRelative = dirname4(relative5) === "." ? "" : dirname4(relative5);
|
|
82700
82791
|
const literalParent = join6(realRoot, parentRelative);
|
|
82701
82792
|
let parent;
|
|
82702
82793
|
try {
|
|
@@ -82717,7 +82808,7 @@ async function resolveForWrite(root, requested) {
|
|
|
82717
82808
|
literalParent === parent ? `${parentRelative} est\xE1 fora do checkout` : `${parentRelative} aponta para fora do checkout`
|
|
82718
82809
|
);
|
|
82719
82810
|
}
|
|
82720
|
-
const literalTarget = join6(parent, basename(
|
|
82811
|
+
const literalTarget = join6(parent, basename(relative5));
|
|
82721
82812
|
let entry = literalTarget;
|
|
82722
82813
|
let target = literalTarget;
|
|
82723
82814
|
let targetless = null;
|
|
@@ -82728,7 +82819,7 @@ async function resolveForWrite(root, requested) {
|
|
|
82728
82819
|
entry = await realpath3(literalTarget);
|
|
82729
82820
|
target = entry;
|
|
82730
82821
|
} else {
|
|
82731
|
-
entry = join6(parent, await diskNameOf(parent, basename(
|
|
82822
|
+
entry = join6(parent, await diskNameOf(parent, basename(relative5)));
|
|
82732
82823
|
const destination = await realpath3(literalTarget).catch(() => null);
|
|
82733
82824
|
if (destination === null) {
|
|
82734
82825
|
target = null;
|
|
@@ -82747,16 +82838,16 @@ async function resolveForWrite(root, requested) {
|
|
|
82747
82838
|
throw new DomainError("INVALID_ARGUMENT", `${parentRelative || "."} n\xE3o \xE9 um diret\xF3rio`);
|
|
82748
82839
|
}
|
|
82749
82840
|
if (code !== "ENOENT") {
|
|
82750
|
-
throw new DomainError("BLOCKED", `sem acesso a ${
|
|
82841
|
+
throw new DomainError("BLOCKED", `sem acesso a ${relative5}`);
|
|
82751
82842
|
}
|
|
82752
82843
|
exists2 = false;
|
|
82753
82844
|
}
|
|
82754
|
-
refuseWritingIntoGit(
|
|
82755
|
-
if (target !== null) refuseWritingIntoGit(
|
|
82845
|
+
refuseWritingIntoGit(relative5, relativeTo(realRoot, entry));
|
|
82846
|
+
if (target !== null) refuseWritingIntoGit(relative5, relativeTo(realRoot, target));
|
|
82756
82847
|
if (target === realRoot) {
|
|
82757
82848
|
throw new DomainError("INVALID_ARGUMENT", "a raiz do checkout n\xE3o aceita escrita");
|
|
82758
82849
|
}
|
|
82759
|
-
return { relative:
|
|
82850
|
+
return { relative: relative5, entry, target, targetless, exists: exists2 };
|
|
82760
82851
|
}
|
|
82761
82852
|
|
|
82762
82853
|
// src/files/FileService.ts
|
|
@@ -82808,11 +82899,11 @@ async function writeAtomically(target, content, mode) {
|
|
|
82808
82899
|
throw error40;
|
|
82809
82900
|
}
|
|
82810
82901
|
}
|
|
82811
|
-
function asCreationFailure(error40,
|
|
82902
|
+
function asCreationFailure(error40, relative5) {
|
|
82812
82903
|
const code = error40.code;
|
|
82813
|
-
const parent = dirname5(
|
|
82904
|
+
const parent = dirname5(relative5) === "." ? "." : dirname5(relative5);
|
|
82814
82905
|
if (code === "EEXIST") {
|
|
82815
|
-
return new DomainError("DUPLICATE", `j\xE1 existe alguma coisa em ${
|
|
82906
|
+
return new DomainError("DUPLICATE", `j\xE1 existe alguma coisa em ${relative5}`);
|
|
82816
82907
|
}
|
|
82817
82908
|
if (code === "ENOENT") {
|
|
82818
82909
|
return new DomainError("NOT_FOUND", `o diret\xF3rio ${parent} n\xE3o existe no checkout`);
|
|
@@ -82847,16 +82938,16 @@ function asRenameFailure(error40, from, to) {
|
|
|
82847
82938
|
}
|
|
82848
82939
|
return error40;
|
|
82849
82940
|
}
|
|
82850
|
-
function asRemovalFailure(error40,
|
|
82941
|
+
function asRemovalFailure(error40, relative5) {
|
|
82851
82942
|
const code = error40.code;
|
|
82852
82943
|
if (code === "ENOENT") {
|
|
82853
|
-
return new DomainError("NOT_FOUND", `${
|
|
82944
|
+
return new DomainError("NOT_FOUND", `${relative5} n\xE3o est\xE1 mais no checkout`);
|
|
82854
82945
|
}
|
|
82855
82946
|
if (code === "ENOTEMPTY") {
|
|
82856
|
-
return new DomainError("BLOCKED", `${
|
|
82947
|
+
return new DomainError("BLOCKED", `${relative5} deixou de estar vazia`);
|
|
82857
82948
|
}
|
|
82858
82949
|
if (code === "EACCES" || code === "EPERM") {
|
|
82859
|
-
return new DomainError("BLOCKED", `sem permiss\xE3o para apagar ${
|
|
82950
|
+
return new DomainError("BLOCKED", `sem permiss\xE3o para apagar ${relative5}`);
|
|
82860
82951
|
}
|
|
82861
82952
|
return error40;
|
|
82862
82953
|
}
|
|
@@ -82922,10 +83013,10 @@ function createFileService({
|
|
|
82922
83013
|
} = {}) {
|
|
82923
83014
|
return {
|
|
82924
83015
|
async listDir(root, path, options = {}) {
|
|
82925
|
-
const { absolute, relative:
|
|
83016
|
+
const { absolute, relative: relative5 } = await resolveInsideRoot(root, path);
|
|
82926
83017
|
const info = await stat2(absolute);
|
|
82927
83018
|
if (!info.isDirectory()) {
|
|
82928
|
-
throw new DomainError("INVALID_ARGUMENT", `${
|
|
83019
|
+
throw new DomainError("INVALID_ARGUMENT", `${relative5} n\xE3o \xE9 um diret\xF3rio`);
|
|
82929
83020
|
}
|
|
82930
83021
|
let dirents;
|
|
82931
83022
|
try {
|
|
@@ -82933,7 +83024,7 @@ function createFileService({
|
|
|
82933
83024
|
} catch (error40) {
|
|
82934
83025
|
const code = error40.code;
|
|
82935
83026
|
if (code === "EACCES" || code === "EPERM") {
|
|
82936
|
-
throw new DomainError("BLOCKED", `sem permiss\xE3o de leitura em ${
|
|
83027
|
+
throw new DomainError("BLOCKED", `sem permiss\xE3o de leitura em ${relative5 || "."}`);
|
|
82937
83028
|
}
|
|
82938
83029
|
throw error40;
|
|
82939
83030
|
}
|
|
@@ -82957,16 +83048,16 @@ function createFileService({
|
|
|
82957
83048
|
})
|
|
82958
83049
|
);
|
|
82959
83050
|
entries.sort(compareEntries);
|
|
82960
|
-
return { path:
|
|
83051
|
+
return { path: relative5, entries, total, truncated: total > kept.length };
|
|
82961
83052
|
},
|
|
82962
83053
|
async readFile(root, path) {
|
|
82963
|
-
const { absolute, relative:
|
|
83054
|
+
const { absolute, relative: relative5, insideGit } = await resolveInsideRoot(root, path);
|
|
82964
83055
|
const info = await stat2(absolute);
|
|
82965
83056
|
if (info.isDirectory()) {
|
|
82966
|
-
throw new DomainError("INVALID_ARGUMENT", `${
|
|
83057
|
+
throw new DomainError("INVALID_ARGUMENT", `${relative5} \xE9 um diret\xF3rio`);
|
|
82967
83058
|
}
|
|
82968
83059
|
if (info.size > maxBytes) {
|
|
82969
|
-
return { kind: "too-large", path:
|
|
83060
|
+
return { kind: "too-large", path: relative5, bytes: info.size, limit: maxBytes };
|
|
82970
83061
|
}
|
|
82971
83062
|
let buffer;
|
|
82972
83063
|
try {
|
|
@@ -82974,16 +83065,16 @@ function createFileService({
|
|
|
82974
83065
|
} catch (error40) {
|
|
82975
83066
|
const code = error40.code;
|
|
82976
83067
|
if (code === "EACCES" || code === "EPERM") {
|
|
82977
|
-
throw new DomainError("BLOCKED", `sem permiss\xE3o de leitura em ${
|
|
83068
|
+
throw new DomainError("BLOCKED", `sem permiss\xE3o de leitura em ${relative5}`);
|
|
82978
83069
|
}
|
|
82979
83070
|
throw error40;
|
|
82980
83071
|
}
|
|
82981
|
-
if (isBinary(buffer)) return { kind: "binary", path:
|
|
83072
|
+
if (isBinary(buffer)) return { kind: "binary", path: relative5, bytes: buffer.length };
|
|
82982
83073
|
const text2 = buffer.toString("utf8");
|
|
82983
83074
|
const readOnly = await readOnlyReasonFor(absolute, buffer, text2, insideGit);
|
|
82984
83075
|
return {
|
|
82985
83076
|
kind: "text",
|
|
82986
|
-
path:
|
|
83077
|
+
path: relative5,
|
|
82987
83078
|
bytes: buffer.length,
|
|
82988
83079
|
lines: countLines(text2),
|
|
82989
83080
|
text: text2,
|
|
@@ -82992,15 +83083,15 @@ function createFileService({
|
|
|
82992
83083
|
};
|
|
82993
83084
|
},
|
|
82994
83085
|
async writeFile(root, path, { text: text2, baseRevision }) {
|
|
82995
|
-
const { relative:
|
|
83086
|
+
const { relative: relative5, target, targetless, exists: exists2 } = await resolveForWrite(root, path);
|
|
82996
83087
|
if (target === null) {
|
|
82997
83088
|
throw new DomainError(
|
|
82998
83089
|
"BLOCKED",
|
|
82999
|
-
targetless === "outside" ? `escrita recusada em ${
|
|
83090
|
+
targetless === "outside" ? `escrita recusada em ${relative5}: o link aponta para fora do checkout` : `escrita recusada em ${relative5}: o link est\xE1 pendurado, e nada prova onde a grava\xE7\xE3o cairia`
|
|
83000
83091
|
);
|
|
83001
83092
|
}
|
|
83002
83093
|
if (!exists2) {
|
|
83003
|
-
throw new DomainError("NOT_FOUND", `${
|
|
83094
|
+
throw new DomainError("NOT_FOUND", `${relative5} n\xE3o existe no checkout`);
|
|
83004
83095
|
}
|
|
83005
83096
|
const buffer = Buffer.from(text2, "utf8");
|
|
83006
83097
|
if (buffer.length > maxBytes) {
|
|
@@ -83012,22 +83103,22 @@ function createFileService({
|
|
|
83012
83103
|
const info = await stat2(target).catch((error40) => {
|
|
83013
83104
|
throw new DomainError(
|
|
83014
83105
|
error40.code === "EACCES" || error40.code === "EPERM" ? "BLOCKED" : "NOT_FOUND",
|
|
83015
|
-
`${
|
|
83106
|
+
`${relative5} n\xE3o est\xE1 mais no checkout`
|
|
83016
83107
|
);
|
|
83017
83108
|
});
|
|
83018
83109
|
if (info.isDirectory()) {
|
|
83019
|
-
throw new DomainError("INVALID_ARGUMENT", `${
|
|
83110
|
+
throw new DomainError("INVALID_ARGUMENT", `${relative5} \xE9 um diret\xF3rio`);
|
|
83020
83111
|
}
|
|
83021
83112
|
if (info.size > maxBytes) {
|
|
83022
83113
|
throw new DomainError(
|
|
83023
83114
|
"BLOCKED",
|
|
83024
|
-
`${
|
|
83115
|
+
`${relative5} tem ${info.size} bytes no disco, acima do limite de ${maxBytes}`
|
|
83025
83116
|
);
|
|
83026
83117
|
}
|
|
83027
83118
|
if (!await isWritable(target)) {
|
|
83028
83119
|
throw new DomainError(
|
|
83029
83120
|
"BLOCKED",
|
|
83030
|
-
`escrita recusada em ${
|
|
83121
|
+
`escrita recusada em ${relative5}: o arquivo \xE9 somente leitura no disco`
|
|
83031
83122
|
);
|
|
83032
83123
|
}
|
|
83033
83124
|
let current;
|
|
@@ -83036,14 +83127,14 @@ function createFileService({
|
|
|
83036
83127
|
} catch (error40) {
|
|
83037
83128
|
const code = error40.code;
|
|
83038
83129
|
if (code === "EACCES" || code === "EPERM") {
|
|
83039
|
-
throw new DomainError("BLOCKED", `sem permiss\xE3o de escrita em ${
|
|
83130
|
+
throw new DomainError("BLOCKED", `sem permiss\xE3o de escrita em ${relative5}`);
|
|
83040
83131
|
}
|
|
83041
83132
|
throw error40;
|
|
83042
83133
|
}
|
|
83043
83134
|
if (!survivesUtf8(current, current.toString("utf8"))) {
|
|
83044
83135
|
throw new DomainError(
|
|
83045
83136
|
"BLOCKED",
|
|
83046
|
-
`escrita recusada em ${
|
|
83137
|
+
`escrita recusada em ${relative5}: o conte\xFAdo no disco n\xE3o sobrevive a uma ida e volta em UTF-8, e gravar destruiria bytes`
|
|
83047
83138
|
);
|
|
83048
83139
|
}
|
|
83049
83140
|
const revision = revisionOf(current);
|
|
@@ -83055,29 +83146,29 @@ function createFileService({
|
|
|
83055
83146
|
} catch (error40) {
|
|
83056
83147
|
const code = error40.code;
|
|
83057
83148
|
if (code === "EACCES" || code === "EPERM") {
|
|
83058
|
-
throw new DomainError("BLOCKED", `sem permiss\xE3o de escrita em ${
|
|
83149
|
+
throw new DomainError("BLOCKED", `sem permiss\xE3o de escrita em ${relative5}`);
|
|
83059
83150
|
}
|
|
83060
83151
|
throw error40;
|
|
83061
83152
|
}
|
|
83062
83153
|
return { ok: true, revision: revisionOf(buffer) };
|
|
83063
83154
|
},
|
|
83064
83155
|
async createFile(root, path) {
|
|
83065
|
-
const { relative:
|
|
83156
|
+
const { relative: relative5, entry } = await resolveForWrite(root, path);
|
|
83066
83157
|
try {
|
|
83067
83158
|
await writeFile(entry, "", { flag: "wx" });
|
|
83068
83159
|
} catch (error40) {
|
|
83069
|
-
throw asCreationFailure(error40,
|
|
83160
|
+
throw asCreationFailure(error40, relative5);
|
|
83070
83161
|
}
|
|
83071
|
-
return { path:
|
|
83162
|
+
return { path: relative5 };
|
|
83072
83163
|
},
|
|
83073
83164
|
async createDir(root, path) {
|
|
83074
|
-
const { relative:
|
|
83165
|
+
const { relative: relative5, entry } = await resolveForWrite(root, path);
|
|
83075
83166
|
try {
|
|
83076
83167
|
await mkdir3(entry);
|
|
83077
83168
|
} catch (error40) {
|
|
83078
|
-
throw asCreationFailure(error40,
|
|
83169
|
+
throw asCreationFailure(error40, relative5);
|
|
83079
83170
|
}
|
|
83080
|
-
return { path:
|
|
83171
|
+
return { path: relative5 };
|
|
83081
83172
|
},
|
|
83082
83173
|
async rename(root, from, to) {
|
|
83083
83174
|
const source = await resolveForWrite(root, from);
|
|
@@ -83102,50 +83193,50 @@ function createFileService({
|
|
|
83102
83193
|
return { path: destination.relative };
|
|
83103
83194
|
},
|
|
83104
83195
|
async remove(root, path, { recursive = false } = {}) {
|
|
83105
|
-
const { relative:
|
|
83196
|
+
const { relative: relative5, entry, exists: exists2 } = await resolveForWrite(root, path);
|
|
83106
83197
|
if (!exists2) {
|
|
83107
|
-
throw new DomainError("NOT_FOUND", `${
|
|
83198
|
+
throw new DomainError("NOT_FOUND", `${relative5} n\xE3o existe no checkout`);
|
|
83108
83199
|
}
|
|
83109
83200
|
const info = await lstat3(entry).catch((error40) => {
|
|
83110
|
-
throw asRemovalFailure(error40,
|
|
83201
|
+
throw asRemovalFailure(error40, relative5);
|
|
83111
83202
|
});
|
|
83112
83203
|
if (!info.isDirectory()) {
|
|
83113
83204
|
await unlink(entry).catch((error40) => {
|
|
83114
|
-
throw asRemovalFailure(error40,
|
|
83205
|
+
throw asRemovalFailure(error40, relative5);
|
|
83115
83206
|
});
|
|
83116
83207
|
return;
|
|
83117
83208
|
}
|
|
83118
83209
|
if (recursive) {
|
|
83119
83210
|
await rm2(entry, { recursive: true }).catch((error40) => {
|
|
83120
|
-
throw asRemovalFailure(error40,
|
|
83211
|
+
throw asRemovalFailure(error40, relative5);
|
|
83121
83212
|
});
|
|
83122
83213
|
return;
|
|
83123
83214
|
}
|
|
83124
|
-
const
|
|
83125
|
-
throw asRemovalFailure(error40,
|
|
83215
|
+
const inside3 = await readdir4(entry).catch((error40) => {
|
|
83216
|
+
throw asRemovalFailure(error40, relative5);
|
|
83126
83217
|
});
|
|
83127
|
-
if (
|
|
83218
|
+
if (inside3.length > 0) {
|
|
83128
83219
|
throw new DomainError(
|
|
83129
83220
|
"BLOCKED",
|
|
83130
|
-
`a pasta ${
|
|
83221
|
+
`a pasta ${relative5} tem ${inside3.length} ${inside3.length === 1 ? "entrada" : "entradas"} dentro; apagar assim mesmo exige recursive`
|
|
83131
83222
|
);
|
|
83132
83223
|
}
|
|
83133
83224
|
await rmdir(entry).catch((error40) => {
|
|
83134
|
-
throw asRemovalFailure(error40,
|
|
83225
|
+
throw asRemovalFailure(error40, relative5);
|
|
83135
83226
|
});
|
|
83136
83227
|
},
|
|
83137
83228
|
async deletePreview(root, path) {
|
|
83138
|
-
const { relative:
|
|
83229
|
+
const { relative: relative5, entry, exists: exists2 } = await resolveForWrite(root, path);
|
|
83139
83230
|
if (!exists2) {
|
|
83140
|
-
throw new DomainError("NOT_FOUND", `${
|
|
83231
|
+
throw new DomainError("NOT_FOUND", `${relative5} n\xE3o existe no checkout`);
|
|
83141
83232
|
}
|
|
83142
83233
|
const info = await lstat3(entry).catch((error40) => {
|
|
83143
|
-
throw asRemovalFailure(error40,
|
|
83234
|
+
throw asRemovalFailure(error40, relative5);
|
|
83144
83235
|
});
|
|
83145
83236
|
if (!info.isDirectory()) {
|
|
83146
|
-
return { kind: "file", path:
|
|
83237
|
+
return { kind: "file", path: relative5, tracked: await isTracked(entry) };
|
|
83147
83238
|
}
|
|
83148
|
-
return { kind: "dir", path:
|
|
83239
|
+
return { kind: "dir", path: relative5, ...await countTree(entry, maxPreviewEntries) };
|
|
83149
83240
|
}
|
|
83150
83241
|
};
|
|
83151
83242
|
}
|
|
@@ -83165,8 +83256,8 @@ function createFsBridge({ files: files2, root }) {
|
|
|
83165
83256
|
}
|
|
83166
83257
|
return candidate;
|
|
83167
83258
|
}
|
|
83168
|
-
async function ensureParents(
|
|
83169
|
-
const parts =
|
|
83259
|
+
async function ensureParents(relative5) {
|
|
83260
|
+
const parts = relative5.split(sep3).slice(0, -1);
|
|
83170
83261
|
let walked = "";
|
|
83171
83262
|
for (const part of parts) {
|
|
83172
83263
|
walked = walked === "" ? part : `${walked}${sep3}${part}`;
|
|
@@ -83196,16 +83287,16 @@ function createFsBridge({ files: files2, root }) {
|
|
|
83196
83287
|
return sliceWindow(content.text, window2);
|
|
83197
83288
|
},
|
|
83198
83289
|
async write(path, content) {
|
|
83199
|
-
const
|
|
83200
|
-
const existing = await files2.readFile(absoluteRoot,
|
|
83290
|
+
const relative5 = toRelative2(path);
|
|
83291
|
+
const existing = await files2.readFile(absoluteRoot, relative5).catch((error40) => {
|
|
83201
83292
|
if (error40 instanceof DomainError && error40.code === "NOT_FOUND") return null;
|
|
83202
83293
|
throw error40;
|
|
83203
83294
|
});
|
|
83204
83295
|
let before = existing;
|
|
83205
83296
|
if (before === null) {
|
|
83206
|
-
await ensureParents(
|
|
83207
|
-
await files2.createFile(absoluteRoot,
|
|
83208
|
-
before = await files2.readFile(absoluteRoot,
|
|
83297
|
+
await ensureParents(relative5);
|
|
83298
|
+
await files2.createFile(absoluteRoot, relative5);
|
|
83299
|
+
before = await files2.readFile(absoluteRoot, relative5);
|
|
83209
83300
|
}
|
|
83210
83301
|
if (before.kind !== "text") {
|
|
83211
83302
|
throw new DomainError(
|
|
@@ -83213,7 +83304,7 @@ function createFsBridge({ files: files2, root }) {
|
|
|
83213
83304
|
`escrita recusada em ${path}: o conte\xFAdo atual n\xE3o \xE9 texto`
|
|
83214
83305
|
);
|
|
83215
83306
|
}
|
|
83216
|
-
const result = await files2.writeFile(absoluteRoot,
|
|
83307
|
+
const result = await files2.writeFile(absoluteRoot, relative5, {
|
|
83217
83308
|
text: content,
|
|
83218
83309
|
baseRevision: before.revision
|
|
83219
83310
|
});
|
|
@@ -83271,11 +83362,11 @@ function createTerminalBridge({
|
|
|
83271
83362
|
const entry = {
|
|
83272
83363
|
ptySessionId: spawned.id,
|
|
83273
83364
|
settled: null,
|
|
83274
|
-
exited: new Promise((
|
|
83365
|
+
exited: new Promise((resolve6) => {
|
|
83275
83366
|
ptyManager.onExit(spawned.id, ({ exitCode, signal }) => {
|
|
83276
83367
|
const status = { exitCode, signal: signal === null ? null : String(signal) };
|
|
83277
83368
|
entry.settled = status;
|
|
83278
|
-
|
|
83369
|
+
resolve6(status);
|
|
83279
83370
|
});
|
|
83280
83371
|
})
|
|
83281
83372
|
};
|
|
@@ -83322,9 +83413,9 @@ function spawnAcpProcess({ command, args, cwd, env }) {
|
|
|
83322
83413
|
env: { ...process.env, ...env },
|
|
83323
83414
|
stdio: ["pipe", "pipe", "inherit"]
|
|
83324
83415
|
});
|
|
83325
|
-
const exited = new Promise((
|
|
83326
|
-
child.once("exit", (exitCode, signal) =>
|
|
83327
|
-
child.once("error", () =>
|
|
83416
|
+
const exited = new Promise((resolve6) => {
|
|
83417
|
+
child.once("exit", (exitCode, signal) => resolve6({ exitCode, signal }));
|
|
83418
|
+
child.once("error", () => resolve6({ exitCode: null, signal: null }));
|
|
83328
83419
|
});
|
|
83329
83420
|
if (!child.stdin || !child.stdout) {
|
|
83330
83421
|
throw new DomainError("SPAWN_FAILED", `${command} started without a usable stdio pipe`);
|
|
@@ -83339,6 +83430,54 @@ function spawnAcpProcess({ command, args, cwd, env }) {
|
|
|
83339
83430
|
};
|
|
83340
83431
|
}
|
|
83341
83432
|
|
|
83433
|
+
// src/acp/permission-policy.ts
|
|
83434
|
+
import { isAbsolute as isAbsolute4, relative, resolve as resolve3 } from "node:path";
|
|
83435
|
+
var READ = "read";
|
|
83436
|
+
var AUTO_ONLY_READS = true;
|
|
83437
|
+
function decidePermission(mode, cwd, call) {
|
|
83438
|
+
if (mode === "ask") {
|
|
83439
|
+
return { approve: false, reason: null };
|
|
83440
|
+
}
|
|
83441
|
+
if (mode === "auto" && !isReadInside(cwd, call)) {
|
|
83442
|
+
return { approve: false, reason: whyNotAuto(cwd, call) };
|
|
83443
|
+
}
|
|
83444
|
+
const allow = call.options.find((option) => option.kind === "allow_once");
|
|
83445
|
+
if (!allow) {
|
|
83446
|
+
return {
|
|
83447
|
+
approve: false,
|
|
83448
|
+
reason: "o modo do Lumem aprovaria isto, mas o agente n\xE3o ofereceu nenhuma op\xE7\xE3o de permitir uma vez"
|
|
83449
|
+
};
|
|
83450
|
+
}
|
|
83451
|
+
return {
|
|
83452
|
+
approve: true,
|
|
83453
|
+
optionId: allow.optionId,
|
|
83454
|
+
reason: mode === "free" ? "Modo Liberado: nada \xE9 perguntado dentro deste checkout." : "Modo Autom\xE1tico: leitura de arquivo, caminho dentro do checkout."
|
|
83455
|
+
};
|
|
83456
|
+
}
|
|
83457
|
+
function isReadInside(cwd, call) {
|
|
83458
|
+
if (AUTO_ONLY_READS && call.kind !== READ) return false;
|
|
83459
|
+
if (call.locations.length === 0) return false;
|
|
83460
|
+
return call.locations.every((location) => inside2(cwd, location.path));
|
|
83461
|
+
}
|
|
83462
|
+
function whyNotAuto(cwd, call) {
|
|
83463
|
+
if (call.kind !== READ) {
|
|
83464
|
+
return `O modo Autom\xE1tico do Lumem aprova sozinho s\xF3 leitura de arquivo. Esta chamada \xE9 "${call.kind}".`;
|
|
83465
|
+
}
|
|
83466
|
+
if (call.locations.length === 0) {
|
|
83467
|
+
return "O modo Autom\xE1tico do Lumem precisa saber qual arquivo: esta leitura n\xE3o disse nenhum caminho.";
|
|
83468
|
+
}
|
|
83469
|
+
const outside = call.locations.find((location) => !inside2(cwd, location.path));
|
|
83470
|
+
return `O modo Autom\xE1tico do Lumem aprova sozinho s\xF3 dentro do checkout, e "${outside?.path ?? ""}" est\xE1 fora.`;
|
|
83471
|
+
}
|
|
83472
|
+
function inside2(root, path) {
|
|
83473
|
+
if (root === "") return false;
|
|
83474
|
+
const from = resolve3(root);
|
|
83475
|
+
const to = resolve3(from, path);
|
|
83476
|
+
if (to === from) return true;
|
|
83477
|
+
const step = relative(from, to);
|
|
83478
|
+
return step !== "" && !step.startsWith("..") && !isAbsolute4(step);
|
|
83479
|
+
}
|
|
83480
|
+
|
|
83342
83481
|
// src/acp/translate.ts
|
|
83343
83482
|
var TOOL_STATUS = {
|
|
83344
83483
|
pending: "pending",
|
|
@@ -83653,6 +83792,10 @@ function sniffUnknownUpdates(source, { onUnknown }) {
|
|
|
83653
83792
|
var ACP_PROTOCOL_VERSION = 1;
|
|
83654
83793
|
var LUMEM_CLIENT_VERSION = "0.1.0";
|
|
83655
83794
|
var DEFAULT_HANDSHAKE_TIMEOUT_MS = 15e3;
|
|
83795
|
+
function modeOwnerOf(info) {
|
|
83796
|
+
if (info.mode !== "") return "agent";
|
|
83797
|
+
return info.configOptions.some((option) => option.id === MODE_OPTION) ? "agent" : "lumem";
|
|
83798
|
+
}
|
|
83656
83799
|
var AcpManager = class {
|
|
83657
83800
|
sessions = /* @__PURE__ */ new Map();
|
|
83658
83801
|
exitWatchers = /* @__PURE__ */ new Set();
|
|
@@ -83761,6 +83904,82 @@ var AcpManager = class {
|
|
|
83761
83904
|
* the spike measured both at zero: nothing is generated until `session/prompt`,
|
|
83762
83905
|
* which never happens here.
|
|
83763
83906
|
*/
|
|
83907
|
+
/**
|
|
83908
|
+
* Entrar no agente, pelo método que o próprio agente ofereceu (F2, T10).
|
|
83909
|
+
*
|
|
83910
|
+
* O caminho que a `agent-login` construiu roda um **comando** num PTY, e ele
|
|
83911
|
+
* continua valendo — é o que o `claude-agent-acp` oferece. Medido na fase 0
|
|
83912
|
+
* (§4.2): o `codex-acp` não oferece comando nenhum. Os métodos dele são
|
|
83913
|
+
* `api-key`, `chat-gpt` e `chat-gpt-device-code`, e os três se atravessam por
|
|
83914
|
+
* **uma chamada**.
|
|
83915
|
+
*
|
|
83916
|
+
* Um processo por tentativa, e ele morre no fim. A credencial não fica aqui: é
|
|
83917
|
+
* o adaptador que a escreve na casa dele — `~/.codex`, no caso — e é por isso
|
|
83918
|
+
* que um processo curto basta. O que o Lumem guarda de tudo isto é nada.
|
|
83919
|
+
*
|
|
83920
|
+
* **Sem `withTimeout` no `authenticate`.** Todos os outros passos do protocolo
|
|
83921
|
+
* são conversa entre dois programas e um limite de 15 s é generoso; este espera
|
|
83922
|
+
* uma pessoa autorizar em outro lugar, e um limite aqui seria o daemon
|
|
83923
|
+
* desistindo de alguém que está digitando um código. Quem desiste é quem
|
|
83924
|
+
* clicou: o `signal` mata o processo.
|
|
83925
|
+
*/
|
|
83926
|
+
async authenticate(options) {
|
|
83927
|
+
if (options.methodId.trim() === "") {
|
|
83928
|
+
throw new DomainError("INVALID_ARGUMENT", "sem o m\xE9todo n\xE3o h\xE1 como entrar");
|
|
83929
|
+
}
|
|
83930
|
+
const { session: session2, child } = this.launch(options, { probe: true });
|
|
83931
|
+
session2.elicit = options.onElicitation;
|
|
83932
|
+
session2.elicitDone = options.onElicitationDone;
|
|
83933
|
+
const abort = () => child.kill();
|
|
83934
|
+
options.signal?.addEventListener("abort", abort, { once: true });
|
|
83935
|
+
try {
|
|
83936
|
+
const initialize = await this.initialize(session2);
|
|
83937
|
+
if (initialize.protocolVersion !== ACP_PROTOCOL_VERSION) {
|
|
83938
|
+
throw new DomainError(
|
|
83939
|
+
"SPAWN_FAILED",
|
|
83940
|
+
`o adaptador fala a vers\xE3o ${initialize.protocolVersion} do protocolo, e este daemon fala a ${ACP_PROTOCOL_VERSION}`
|
|
83941
|
+
);
|
|
83942
|
+
}
|
|
83943
|
+
const offered = (initialize.authMethods ?? []).some(
|
|
83944
|
+
(method) => method.id === options.methodId
|
|
83945
|
+
);
|
|
83946
|
+
if (!offered) {
|
|
83947
|
+
throw new DomainError(
|
|
83948
|
+
"NOT_FOUND",
|
|
83949
|
+
`o adaptador n\xE3o oferece o m\xE9todo de login "${options.methodId}"`
|
|
83950
|
+
);
|
|
83951
|
+
}
|
|
83952
|
+
await session2.connection.agent.request("authenticate", {
|
|
83953
|
+
methodId: options.methodId,
|
|
83954
|
+
/*
|
|
83955
|
+
* A chave viaja no `_meta` e não fica em lugar nenhum.
|
|
83956
|
+
*
|
|
83957
|
+
* Medido no pacote: `authenticateWithApiKey` lê
|
|
83958
|
+
* `_meta["api-key"].apiKey` e, na falta dele, o ambiente
|
|
83959
|
+
* (`CODEX_API_KEY`, `OPENAI_API_KEY`). O daemon é o carteiro — ele não
|
|
83960
|
+
* grava, não loga e não devolve.
|
|
83961
|
+
*/
|
|
83962
|
+
...options.apiKey === void 0 ? {} : { _meta: { "api-key": { apiKey: options.apiKey } } }
|
|
83963
|
+
});
|
|
83964
|
+
try {
|
|
83965
|
+
await this.withTimeout(
|
|
83966
|
+
session2.connection.agent.request("session/new", { cwd: options.cwd, mcpServers: [] }),
|
|
83967
|
+
"session/new"
|
|
83968
|
+
);
|
|
83969
|
+
} catch (error40) {
|
|
83970
|
+
if (isAuthRequired(error40)) {
|
|
83971
|
+
throw new DomainError(
|
|
83972
|
+
"BLOCKED",
|
|
83973
|
+
"o adaptador aceitou o login e continua pedindo credencial \u2014 a conta n\xE3o serve para esta sess\xE3o"
|
|
83974
|
+
);
|
|
83975
|
+
}
|
|
83976
|
+
throw error40;
|
|
83977
|
+
}
|
|
83978
|
+
} finally {
|
|
83979
|
+
options.signal?.removeEventListener("abort", abort);
|
|
83980
|
+
child.kill();
|
|
83981
|
+
}
|
|
83982
|
+
}
|
|
83764
83983
|
async probe(options) {
|
|
83765
83984
|
const startedAt = this.now();
|
|
83766
83985
|
const { session: session2, child } = this.launch(options, { probe: true });
|
|
@@ -83854,10 +84073,22 @@ var AcpManager = class {
|
|
|
83854
84073
|
exitCode: null,
|
|
83855
84074
|
mode: "",
|
|
83856
84075
|
model: "",
|
|
83857
|
-
configOptions: []
|
|
84076
|
+
configOptions: [],
|
|
84077
|
+
/*
|
|
84078
|
+
* A session is born asking, always (F1.5).
|
|
84079
|
+
*
|
|
84080
|
+
* The workspace default is applied by the caller that knows which
|
|
84081
|
+
* workspace this is — the store — and even that one cannot make a
|
|
84082
|
+
* session start free: the gate for `free` is per session, and a default
|
|
84083
|
+
* that walked through it would annul it (Q5).
|
|
84084
|
+
*/
|
|
84085
|
+
lumemMode: options.lumemMode ?? "ask",
|
|
84086
|
+
lumemModeDefault: options.lumemModeDefault ?? "ask"
|
|
83858
84087
|
},
|
|
83859
84088
|
process: child,
|
|
83860
84089
|
connection: void 0,
|
|
84090
|
+
elicit: void 0,
|
|
84091
|
+
elicitDone: void 0,
|
|
83861
84092
|
listeners: /* @__PURE__ */ new Set(),
|
|
83862
84093
|
openToolCalls: /* @__PURE__ */ new Set(),
|
|
83863
84094
|
pendingPermissions: /* @__PURE__ */ new Map(),
|
|
@@ -83980,7 +84211,50 @@ var AcpManager = class {
|
|
|
83980
84211
|
}
|
|
83981
84212
|
session2.pendingPermissions.delete(requestId);
|
|
83982
84213
|
pending.resolve({ outcome: "selected", optionId });
|
|
83983
|
-
this.emit(session2, {
|
|
84214
|
+
this.emit(session2, {
|
|
84215
|
+
type: "permission_resolved",
|
|
84216
|
+
requestId,
|
|
84217
|
+
outcome: { optionId },
|
|
84218
|
+
by: "user",
|
|
84219
|
+
reason: null
|
|
84220
|
+
});
|
|
84221
|
+
}
|
|
84222
|
+
/**
|
|
84223
|
+
* Switch the policy Lumem applies to permission requests (F1.1).
|
|
84224
|
+
*
|
|
84225
|
+
* Synchronous, and that is the whole difference from `setConfig`: nothing
|
|
84226
|
+
* leaves the daemon. `setConfig` forwards a value to the agent and waits for it
|
|
84227
|
+
* to answer; this changes what *we* answer, and the agent never learns a policy
|
|
84228
|
+
* exists.
|
|
84229
|
+
*
|
|
84230
|
+
* Two refusals, and both are named rather than silent:
|
|
84231
|
+
*
|
|
84232
|
+
* - **the agent owns the selector** (A1). Accepting the switch and doing
|
|
84233
|
+
* nothing with it would leave a stored value that never applies — the tell
|
|
84234
|
+
* would be a pill that changes and a behaviour that does not.
|
|
84235
|
+
* - **a turn is running** (F1.7). Same reason `setConfig` refuses, and
|
|
84236
|
+
* deliberately the same message: from where the user stands it is the same
|
|
84237
|
+
* act, and two wordings for one rule teach that there are two rules.
|
|
84238
|
+
*/
|
|
84239
|
+
setLumemMode(id, mode) {
|
|
84240
|
+
const session2 = this.require(id);
|
|
84241
|
+
if (session2.info.state === "exited") {
|
|
84242
|
+
throw new DomainError("SESSION_EXITED", `session ${id} has exited`);
|
|
84243
|
+
}
|
|
84244
|
+
if (modeOwnerOf(session2.info) === "agent") {
|
|
84245
|
+
throw new DomainError(
|
|
84246
|
+
"BLOCKED",
|
|
84247
|
+
"este agente oferece modos pr\xF3prios: troque o modo dele, n\xE3o a pol\xEDtica do Lumem"
|
|
84248
|
+
);
|
|
84249
|
+
}
|
|
84250
|
+
if (session2.promptInFlight) {
|
|
84251
|
+
throw new DomainError(
|
|
84252
|
+
"BLOCKED",
|
|
84253
|
+
"n\xE3o d\xE1 para trocar de modo ou modelo no meio de um turno: interrompa ou espere ele acabar"
|
|
84254
|
+
);
|
|
84255
|
+
}
|
|
84256
|
+
session2.info.lumemMode = mode;
|
|
84257
|
+
this.emitConfig(session2);
|
|
83984
84258
|
}
|
|
83985
84259
|
/**
|
|
83986
84260
|
* Everything an attaching client needs to catch up, in one frame.
|
|
@@ -84057,18 +84331,18 @@ var AcpManager = class {
|
|
|
84057
84331
|
const running = [...this.sessions.values()].filter((s) => s.info.state === "running");
|
|
84058
84332
|
await Promise.all(
|
|
84059
84333
|
running.map(
|
|
84060
|
-
(session2) => new Promise((
|
|
84061
|
-
const timer = setTimeout(
|
|
84334
|
+
(session2) => new Promise((resolve6) => {
|
|
84335
|
+
const timer = setTimeout(resolve6, timeoutMs);
|
|
84062
84336
|
timer.unref?.();
|
|
84063
84337
|
void session2.process.exited.then(() => {
|
|
84064
84338
|
clearTimeout(timer);
|
|
84065
|
-
|
|
84339
|
+
resolve6();
|
|
84066
84340
|
});
|
|
84067
84341
|
try {
|
|
84068
84342
|
session2.process.kill();
|
|
84069
84343
|
} catch {
|
|
84070
84344
|
clearTimeout(timer);
|
|
84071
|
-
|
|
84345
|
+
resolve6();
|
|
84072
84346
|
}
|
|
84073
84347
|
})
|
|
84074
84348
|
)
|
|
@@ -84130,8 +84404,33 @@ var AcpManager = class {
|
|
|
84130
84404
|
}).onRequest("terminal/release", ({ params }) => {
|
|
84131
84405
|
this.requireTerminals(session2).release(params.terminalId);
|
|
84132
84406
|
return {};
|
|
84407
|
+
}).onRequest("elicitation/create", ({ params }) => {
|
|
84408
|
+
const record2 = params;
|
|
84409
|
+
const url2 = record2["url"];
|
|
84410
|
+
if (session2.elicit === void 0 || params.mode !== "url" || typeof url2 !== "string") {
|
|
84411
|
+
return { action: "decline" };
|
|
84412
|
+
}
|
|
84413
|
+
session2.elicit({
|
|
84414
|
+
elicitationId: String(record2["elicitationId"] ?? ""),
|
|
84415
|
+
url: url2,
|
|
84416
|
+
message: params.message,
|
|
84417
|
+
code: codeIn(params.message)
|
|
84418
|
+
});
|
|
84419
|
+
return { action: "accept" };
|
|
84420
|
+
}).onNotification("elicitation/complete", ({ params }) => {
|
|
84421
|
+
session2.elicitDone?.(params.elicitationId);
|
|
84133
84422
|
}).onRequest("session/request_permission", ({ params }) => {
|
|
84134
84423
|
const requestId = newId();
|
|
84424
|
+
const options = params.options.map((option) => ({
|
|
84425
|
+
optionId: option.optionId,
|
|
84426
|
+
name: option.name,
|
|
84427
|
+
kind: option.kind
|
|
84428
|
+
}));
|
|
84429
|
+
const decision = modeOwnerOf(session2.info) === "lumem" ? decidePermission(session2.info.lumemMode, session2.info.cwd, {
|
|
84430
|
+
kind: kindOf(params.toolCall),
|
|
84431
|
+
locations: locationsOf(params.toolCall),
|
|
84432
|
+
options
|
|
84433
|
+
}) : { approve: false, reason: null };
|
|
84135
84434
|
this.emit(session2, {
|
|
84136
84435
|
type: "permission_request",
|
|
84137
84436
|
requestId,
|
|
@@ -84139,15 +84438,24 @@ var AcpManager = class {
|
|
|
84139
84438
|
title: params.toolCall.title ?? params.toolCall.toolCallId,
|
|
84140
84439
|
command: commandOf(params.toolCall),
|
|
84141
84440
|
cwd: session2.info.cwd,
|
|
84142
|
-
options
|
|
84143
|
-
|
|
84144
|
-
name: option.name,
|
|
84145
|
-
kind: option.kind
|
|
84146
|
-
}))
|
|
84441
|
+
options,
|
|
84442
|
+
policyReason: decision.approve ? null : decision.reason
|
|
84147
84443
|
});
|
|
84148
|
-
|
|
84444
|
+
if (decision.approve) {
|
|
84445
|
+
this.emit(session2, {
|
|
84446
|
+
type: "permission_resolved",
|
|
84447
|
+
requestId,
|
|
84448
|
+
outcome: { optionId: decision.optionId },
|
|
84449
|
+
by: "lumem",
|
|
84450
|
+
reason: decision.reason
|
|
84451
|
+
});
|
|
84452
|
+
return Promise.resolve({
|
|
84453
|
+
outcome: { outcome: "selected", optionId: decision.optionId }
|
|
84454
|
+
});
|
|
84455
|
+
}
|
|
84456
|
+
return new Promise((resolve6) => {
|
|
84149
84457
|
session2.pendingPermissions.set(requestId, {
|
|
84150
|
-
resolve: (outcome) =>
|
|
84458
|
+
resolve: (outcome) => resolve6({ outcome })
|
|
84151
84459
|
});
|
|
84152
84460
|
});
|
|
84153
84461
|
}).onNotification(
|
|
@@ -84262,7 +84570,20 @@ var AcpManager = class {
|
|
|
84262
84570
|
* Gated on the `PtyManager` too: the methods it offers are commands, and
|
|
84263
84571
|
* a client that cannot run one has no business being offered it.
|
|
84264
84572
|
*/
|
|
84265
|
-
...this.ptyManager ? { auth: { terminal: true } } : {}
|
|
84573
|
+
...this.ptyManager ? { auth: { terminal: true } } : {},
|
|
84574
|
+
/*
|
|
84575
|
+
* "Eu sei mostrar uma URL", declarado porque os dois métodos existem.
|
|
84576
|
+
*
|
|
84577
|
+
* Medido na fase 0 da `second-agent` (§4.2): sem isto, o `codex-acp`
|
|
84578
|
+
* não oferece `chat-gpt-device-code` — o único método dele que **não**
|
|
84579
|
+
* abre um navegador na máquina do daemon. Declarar é o que faz o
|
|
84580
|
+
* método aparecer, e é por isso que ele não é opcional aqui.
|
|
84581
|
+
*
|
|
84582
|
+
* Não é gatilhado no `ptyManager` como o `auth.terminal`: mostrar uma
|
|
84583
|
+
* URL não precisa de terminal nenhum, e o que responde ao pedido é o
|
|
84584
|
+
* `elicitation/create` acima, que existe sempre.
|
|
84585
|
+
*/
|
|
84586
|
+
elicitation: { url: {} }
|
|
84266
84587
|
},
|
|
84267
84588
|
...this.ptyManager ? { _meta: { "terminal-auth": true } } : {},
|
|
84268
84589
|
clientInfo: { name: "lumem", version: LUMEM_CLIENT_VERSION }
|
|
@@ -84362,6 +84683,9 @@ var AcpManager = class {
|
|
|
84362
84683
|
modeId: value
|
|
84363
84684
|
});
|
|
84364
84685
|
session2.info.mode = value;
|
|
84686
|
+
session2.info.configOptions = session2.info.configOptions.map(
|
|
84687
|
+
(option) => option.id === MODE_OPTION ? { ...option, currentValue: value } : option
|
|
84688
|
+
);
|
|
84365
84689
|
this.emitConfig(session2);
|
|
84366
84690
|
return;
|
|
84367
84691
|
}
|
|
@@ -84387,7 +84711,10 @@ var AcpManager = class {
|
|
|
84387
84711
|
this.emit(session2, {
|
|
84388
84712
|
type: "config",
|
|
84389
84713
|
mode: session2.info.mode,
|
|
84390
|
-
options: [...session2.info.configOptions]
|
|
84714
|
+
options: [...session2.info.configOptions],
|
|
84715
|
+
modeOwner: modeOwnerOf(session2.info),
|
|
84716
|
+
lumemMode: session2.info.lumemMode,
|
|
84717
|
+
lumemModeDefault: session2.info.lumemModeDefault
|
|
84391
84718
|
});
|
|
84392
84719
|
for (const watcher of [...this.configWatchers]) {
|
|
84393
84720
|
try {
|
|
@@ -84551,6 +84878,18 @@ function flattenChoices(options) {
|
|
|
84551
84878
|
];
|
|
84552
84879
|
});
|
|
84553
84880
|
}
|
|
84881
|
+
function kindOf(toolCall) {
|
|
84882
|
+
const parsed = acpToolKindSchema.safeParse(toolCall.kind);
|
|
84883
|
+
return parsed.success ? parsed.data : "other";
|
|
84884
|
+
}
|
|
84885
|
+
function locationsOf(toolCall) {
|
|
84886
|
+
const locations = toolCall.locations;
|
|
84887
|
+
if (!Array.isArray(locations)) return [];
|
|
84888
|
+
return locations.flatMap((location) => {
|
|
84889
|
+
const path = location.path;
|
|
84890
|
+
return typeof path === "string" ? [{ path, line: null }] : [];
|
|
84891
|
+
});
|
|
84892
|
+
}
|
|
84554
84893
|
function commandOf(toolCall) {
|
|
84555
84894
|
const raw = toolCall.rawInput;
|
|
84556
84895
|
if (typeof raw === "object" && raw !== null) {
|
|
@@ -84561,7 +84900,8 @@ function commandOf(toolCall) {
|
|
|
84561
84900
|
}
|
|
84562
84901
|
function notInstalled(command, adapterVersion) {
|
|
84563
84902
|
const pinned = adapterVersion ?? "";
|
|
84564
|
-
const
|
|
84903
|
+
const spec = adapterByCommand(basename3(command));
|
|
84904
|
+
const remedy = spec !== null && spec.package !== null ? `npm i -g ${spec.package}@${pinned || spec.pinnedVersion}` : `instale o adaptador e deixe "${command}" no PATH`;
|
|
84565
84905
|
const version3 = pinned ? ` Esta sess\xE3o fixa a vers\xE3o ${pinned}.` : "";
|
|
84566
84906
|
return new DomainError(
|
|
84567
84907
|
"SPAWN_FAILED",
|
|
@@ -84577,6 +84917,10 @@ function launchFailure(command, adapterVersion, cause) {
|
|
|
84577
84917
|
{ cause }
|
|
84578
84918
|
);
|
|
84579
84919
|
}
|
|
84920
|
+
function codeIn(message) {
|
|
84921
|
+
const match = /\b([A-Z0-9]{4}-[A-Z0-9]{4}|[A-Z0-9]{6,12})\b/.exec(message);
|
|
84922
|
+
return match?.[1] ?? null;
|
|
84923
|
+
}
|
|
84580
84924
|
function isAuthRequired(error40) {
|
|
84581
84925
|
const code = error40.code;
|
|
84582
84926
|
return code === ACP_AUTH_REQUIRED_CODE;
|
|
@@ -84711,7 +85055,7 @@ import { dirname as dirname6, join as join11 } from "node:path";
|
|
|
84711
85055
|
// src/memory/catalog.ts
|
|
84712
85056
|
import { createHash as createHash2 } from "node:crypto";
|
|
84713
85057
|
import { readdir as readdir5, readFile as readFile3 } from "node:fs/promises";
|
|
84714
|
-
import { join as join9, relative, sep as sep4 } from "node:path";
|
|
85058
|
+
import { join as join9, relative as relative2, sep as sep4 } from "node:path";
|
|
84715
85059
|
|
|
84716
85060
|
// src/memory/entry.ts
|
|
84717
85061
|
var import_yaml = __toESM(require_dist(), 1);
|
|
@@ -85010,7 +85354,7 @@ async function collectFrom(stateDir, dir, location, items) {
|
|
|
85010
85354
|
if (!dirent.isFile() || !dirent.name.endsWith(".md")) continue;
|
|
85011
85355
|
if (dirent.name === "MEMORY.md") continue;
|
|
85012
85356
|
const absolute = join9(dir, dirent.name);
|
|
85013
|
-
const path =
|
|
85357
|
+
const path = relative2(stateDir, absolute).split(sep4).join("/");
|
|
85014
85358
|
const text2 = await readFile3(absolute, "utf8").catch((error40) => error40);
|
|
85015
85359
|
if (typeof text2 !== "string") {
|
|
85016
85360
|
items.push({ path, reason: `n\xE3o foi poss\xEDvel ler: ${text2.message}` });
|
|
@@ -85201,7 +85545,7 @@ function listDecisions(db, { path, limit = 50 } = {}) {
|
|
|
85201
85545
|
}
|
|
85202
85546
|
|
|
85203
85547
|
// src/memory/paths.ts
|
|
85204
|
-
import { join as join10, relative as
|
|
85548
|
+
import { join as join10, relative as relative3, sep as sep5 } from "node:path";
|
|
85205
85549
|
var ID_SEGMENT = "[A-Za-z0-9_-]+";
|
|
85206
85550
|
function memoryDirFor(stateDir, target) {
|
|
85207
85551
|
switch (target.scope) {
|
|
@@ -85281,7 +85625,7 @@ function slugOf(path, type) {
|
|
|
85281
85625
|
return file2.replace(new RegExp(`^${type}_`), "").replace(/\.md$/, "");
|
|
85282
85626
|
}
|
|
85283
85627
|
function repoRelative(stateDir, absolutePath) {
|
|
85284
|
-
const rel =
|
|
85628
|
+
const rel = relative3(stateDir, absolutePath);
|
|
85285
85629
|
if (rel === "" || rel.startsWith("..")) {
|
|
85286
85630
|
throw new DomainError("INVALID_ARGUMENT", `caminho fora do state dir: ${absolutePath}`);
|
|
85287
85631
|
}
|
|
@@ -86395,8 +86739,72 @@ function parseWrite(requested) {
|
|
|
86395
86739
|
throw new DomainError("INVALID_ARGUMENT", `${field} \u2014 ${issue2?.message ?? "inv\xE1lido"}`);
|
|
86396
86740
|
}
|
|
86397
86741
|
|
|
86742
|
+
// src/repositories/agentConfig.ts
|
|
86743
|
+
function conflicts3(name) {
|
|
86744
|
+
return {
|
|
86745
|
+
"unique:agent_config.name": {
|
|
86746
|
+
code: "DUPLICATE",
|
|
86747
|
+
message: `j\xE1 existe uma configura\xE7\xE3o chamada "${name}"`
|
|
86748
|
+
},
|
|
86749
|
+
foreignKey: {
|
|
86750
|
+
code: "IN_USE",
|
|
86751
|
+
message: "a configura\xE7\xE3o ainda est\xE1 em uso por alguma sess\xE3o"
|
|
86752
|
+
},
|
|
86753
|
+
// Without this the CHECK surfaces as a raw SQLite error, which reads as a
|
|
86754
|
+
// daemon defect rather than as the one thing the caller got wrong.
|
|
86755
|
+
"check:agent_config_adapter_version": {
|
|
86756
|
+
code: "INVALID_ARGUMENT",
|
|
86757
|
+
message: "configura\xE7\xE3o ACP precisa de uma vers\xE3o de adaptador fixa, e configura\xE7\xE3o PTY n\xE3o pode ter uma"
|
|
86758
|
+
},
|
|
86759
|
+
"check:agent_config_transport": {
|
|
86760
|
+
code: "INVALID_ARGUMENT",
|
|
86761
|
+
message: "transporte precisa ser pty ou acp"
|
|
86762
|
+
}
|
|
86763
|
+
};
|
|
86764
|
+
}
|
|
86765
|
+
function createAgentConfigRepository(db) {
|
|
86766
|
+
async function require_(id) {
|
|
86767
|
+
const found = await db.query.agentConfig.findFirst({ where: eq(agentConfig.id, id) });
|
|
86768
|
+
if (!found) throw new DomainError("NOT_FOUND", `configura\xE7\xE3o ${id} n\xE3o existe`);
|
|
86769
|
+
return found;
|
|
86770
|
+
}
|
|
86771
|
+
return {
|
|
86772
|
+
async create({ name, command, args = [], env = {}, transport = "pty", adapterVersion = null }) {
|
|
86773
|
+
const [row] = await withConstraints(
|
|
86774
|
+
() => db.insert(agentConfig).values({ id: newId(), name, command, args, env, transport, adapterVersion }).returning(),
|
|
86775
|
+
conflicts3(name)
|
|
86776
|
+
);
|
|
86777
|
+
return row;
|
|
86778
|
+
},
|
|
86779
|
+
list() {
|
|
86780
|
+
return db.select().from(agentConfig).orderBy(asc(agentConfig.name));
|
|
86781
|
+
},
|
|
86782
|
+
findById(id) {
|
|
86783
|
+
return db.query.agentConfig.findFirst({ where: eq(agentConfig.id, id) });
|
|
86784
|
+
},
|
|
86785
|
+
findByName(name) {
|
|
86786
|
+
return db.query.agentConfig.findFirst({ where: eq(agentConfig.name, name) });
|
|
86787
|
+
},
|
|
86788
|
+
async update(id, input) {
|
|
86789
|
+
const current = await require_(id);
|
|
86790
|
+
const [row] = await withConstraints(
|
|
86791
|
+
() => db.update(agentConfig).set({ ...input, updatedAt: /* @__PURE__ */ new Date() }).where(eq(agentConfig.id, id)).returning(),
|
|
86792
|
+
conflicts3(input.name ?? current.name)
|
|
86793
|
+
);
|
|
86794
|
+
return row;
|
|
86795
|
+
},
|
|
86796
|
+
async remove(id) {
|
|
86797
|
+
await require_(id);
|
|
86798
|
+
await withConstraints(
|
|
86799
|
+
() => db.delete(agentConfig).where(eq(agentConfig.id, id)).returning(),
|
|
86800
|
+
conflicts3("")
|
|
86801
|
+
);
|
|
86802
|
+
}
|
|
86803
|
+
};
|
|
86804
|
+
}
|
|
86805
|
+
|
|
86398
86806
|
// src/memory/projection.ts
|
|
86399
|
-
import { relative as
|
|
86807
|
+
import { relative as relative4, isAbsolute as isAbsolute5 } from "node:path";
|
|
86400
86808
|
var MAX_FILES = 12;
|
|
86401
86809
|
var MAX_COMMANDS = 8;
|
|
86402
86810
|
function isEmpty(projection) {
|
|
@@ -86444,8 +86852,8 @@ function projectSession(entries, { cwd }) {
|
|
|
86444
86852
|
};
|
|
86445
86853
|
}
|
|
86446
86854
|
function toRelative(path, cwd) {
|
|
86447
|
-
if (!
|
|
86448
|
-
const rel =
|
|
86855
|
+
if (!isAbsolute5(path)) return path;
|
|
86856
|
+
const rel = relative4(cwd, path);
|
|
86449
86857
|
if (rel === "" || rel.startsWith("..")) return null;
|
|
86450
86858
|
return rel;
|
|
86451
86859
|
}
|
|
@@ -86884,6 +87292,7 @@ function trackSessionUsage({ db, acpManager, log }) {
|
|
|
86884
87292
|
sessionId,
|
|
86885
87293
|
projectId: scope.projectId,
|
|
86886
87294
|
worktreeId: scope.worktreeId,
|
|
87295
|
+
agentConfigId: scope.agentConfigId,
|
|
86887
87296
|
tokens,
|
|
86888
87297
|
...cost === null ? {} : { cost: cost.amount, currency: cost.currency }
|
|
86889
87298
|
}).run();
|
|
@@ -86899,10 +87308,95 @@ function trackSessionUsage({ db, acpManager, log }) {
|
|
|
86899
87308
|
async function scopeOf(db, sessionId) {
|
|
86900
87309
|
const row = await createSessionRepository(db).findById(sessionId);
|
|
86901
87310
|
if (row === void 0) return null;
|
|
86902
|
-
|
|
87311
|
+
const agentConfigId = row.agentConfigId ?? null;
|
|
87312
|
+
if (row.scopeType === "project") {
|
|
87313
|
+
return { projectId: row.scopeId, worktreeId: "", agentConfigId };
|
|
87314
|
+
}
|
|
86903
87315
|
const worktree2 = await createWorktreeRepository(db).findById(row.scopeId);
|
|
86904
87316
|
if (worktree2 === void 0) return null;
|
|
86905
|
-
return { projectId: worktree2.projectId, worktreeId: worktree2.id };
|
|
87317
|
+
return { projectId: worktree2.projectId, worktreeId: worktree2.id, agentConfigId };
|
|
87318
|
+
}
|
|
87319
|
+
|
|
87320
|
+
// src/setup/agent-auth.ts
|
|
87321
|
+
function redact(message, secret) {
|
|
87322
|
+
if (secret === void 0 || secret === "") return message;
|
|
87323
|
+
return message.split(secret).join("\u2022\u2022\u2022");
|
|
87324
|
+
}
|
|
87325
|
+
function createAgentAuthService({
|
|
87326
|
+
acpManager
|
|
87327
|
+
}) {
|
|
87328
|
+
const attempts = /* @__PURE__ */ new Map();
|
|
87329
|
+
function require_(id) {
|
|
87330
|
+
const attempt = attempts.get(id);
|
|
87331
|
+
if (attempt === void 0) {
|
|
87332
|
+
throw new DomainError("NOT_FOUND", `n\xE3o existe login em andamento com o id ${id}`);
|
|
87333
|
+
}
|
|
87334
|
+
return attempt;
|
|
87335
|
+
}
|
|
87336
|
+
function view({ id, state, elicitation, message }) {
|
|
87337
|
+
return { id, state, elicitation, message };
|
|
87338
|
+
}
|
|
87339
|
+
return {
|
|
87340
|
+
start(options) {
|
|
87341
|
+
const attempt = {
|
|
87342
|
+
id: newId(),
|
|
87343
|
+
state: "running",
|
|
87344
|
+
elicitation: null,
|
|
87345
|
+
message: null,
|
|
87346
|
+
controller: new AbortController()
|
|
87347
|
+
};
|
|
87348
|
+
attempts.set(attempt.id, attempt);
|
|
87349
|
+
void acpManager.authenticate({
|
|
87350
|
+
command: options.command,
|
|
87351
|
+
...options.args ? { args: options.args } : {},
|
|
87352
|
+
cwd: options.cwd,
|
|
87353
|
+
methodId: options.methodId,
|
|
87354
|
+
...options.apiKey === void 0 ? {} : { apiKey: options.apiKey },
|
|
87355
|
+
...options.adapterVersion === void 0 ? {} : { adapterVersion: options.adapterVersion },
|
|
87356
|
+
onElicitation: (elicitation) => {
|
|
87357
|
+
attempt.elicitation = elicitation;
|
|
87358
|
+
},
|
|
87359
|
+
onElicitationDone: (elicitationId) => {
|
|
87360
|
+
if (attempt.elicitation?.elicitationId === elicitationId) {
|
|
87361
|
+
attempt.elicitation = null;
|
|
87362
|
+
}
|
|
87363
|
+
},
|
|
87364
|
+
signal: attempt.controller.signal
|
|
87365
|
+
}).then(() => {
|
|
87366
|
+
if (attempt.state === "running") attempt.state = "ok";
|
|
87367
|
+
attempt.elicitation = null;
|
|
87368
|
+
}).catch((error40) => {
|
|
87369
|
+
if (attempt.state === "cancelled") return;
|
|
87370
|
+
attempt.state = "failed";
|
|
87371
|
+
attempt.elicitation = null;
|
|
87372
|
+
attempt.message = redact(
|
|
87373
|
+
error40 instanceof Error ? error40.message : "o adaptador recusou o login",
|
|
87374
|
+
options.apiKey
|
|
87375
|
+
);
|
|
87376
|
+
});
|
|
87377
|
+
return view(attempt);
|
|
87378
|
+
},
|
|
87379
|
+
status(id) {
|
|
87380
|
+
return view(require_(id));
|
|
87381
|
+
},
|
|
87382
|
+
cancel(id) {
|
|
87383
|
+
const attempt = require_(id);
|
|
87384
|
+
if (attempt.state === "running") {
|
|
87385
|
+
attempt.state = "cancelled";
|
|
87386
|
+
attempt.elicitation = null;
|
|
87387
|
+
attempt.controller.abort();
|
|
87388
|
+
}
|
|
87389
|
+
return view(attempt);
|
|
87390
|
+
},
|
|
87391
|
+
cancelAll() {
|
|
87392
|
+
for (const attempt of attempts.values()) {
|
|
87393
|
+
if (attempt.state === "running") {
|
|
87394
|
+
attempt.state = "cancelled";
|
|
87395
|
+
attempt.controller.abort();
|
|
87396
|
+
}
|
|
87397
|
+
}
|
|
87398
|
+
}
|
|
87399
|
+
};
|
|
86906
87400
|
}
|
|
86907
87401
|
|
|
86908
87402
|
// src/memory/skill.ts
|
|
@@ -87212,18 +87706,18 @@ var PtyManager = class {
|
|
|
87212
87706
|
const running = [...this.sessions.values()].filter((s) => s.info.state === "running");
|
|
87213
87707
|
await Promise.all(
|
|
87214
87708
|
running.map(
|
|
87215
|
-
(session2) => new Promise((
|
|
87216
|
-
const timer = setTimeout(
|
|
87709
|
+
(session2) => new Promise((resolve6) => {
|
|
87710
|
+
const timer = setTimeout(resolve6, timeoutMs);
|
|
87217
87711
|
timer.unref?.();
|
|
87218
87712
|
session2.exitListeners.add(() => {
|
|
87219
87713
|
clearTimeout(timer);
|
|
87220
|
-
|
|
87714
|
+
resolve6();
|
|
87221
87715
|
});
|
|
87222
87716
|
try {
|
|
87223
87717
|
session2.pty.kill();
|
|
87224
87718
|
} catch {
|
|
87225
87719
|
clearTimeout(timer);
|
|
87226
|
-
|
|
87720
|
+
resolve6();
|
|
87227
87721
|
}
|
|
87228
87722
|
})
|
|
87229
87723
|
)
|
|
@@ -87333,10 +87827,10 @@ async function releasePort(db, scope) {
|
|
|
87333
87827
|
);
|
|
87334
87828
|
}
|
|
87335
87829
|
function isPortFree(port) {
|
|
87336
|
-
return new Promise((
|
|
87830
|
+
return new Promise((resolve6) => {
|
|
87337
87831
|
const probe = createServer();
|
|
87338
|
-
probe.once("error", () =>
|
|
87339
|
-
probe.once("listening", () => probe.close(() =>
|
|
87832
|
+
probe.once("error", () => resolve6(false));
|
|
87833
|
+
probe.once("listening", () => probe.close(() => resolve6(true)));
|
|
87340
87834
|
probe.listen(port, "0.0.0.0");
|
|
87341
87835
|
});
|
|
87342
87836
|
}
|
|
@@ -87999,7 +88493,7 @@ function parse3(toml, { maxDepth = 1e3, integersAsBigInt } = {}) {
|
|
|
87999
88493
|
}
|
|
88000
88494
|
|
|
88001
88495
|
// src/memory/project-identity.ts
|
|
88002
|
-
import { dirname as dirname7, join as join13, resolve as
|
|
88496
|
+
import { dirname as dirname7, join as join13, resolve as resolve4 } from "node:path";
|
|
88003
88497
|
var PROJECT_FILE = join13(".lumem", "project.toml");
|
|
88004
88498
|
|
|
88005
88499
|
// src/scripts/project-scripts.ts
|
|
@@ -88131,7 +88625,7 @@ function createScriptRunner({
|
|
|
88131
88625
|
log
|
|
88132
88626
|
}) {
|
|
88133
88627
|
const discovered = /* @__PURE__ */ new Map();
|
|
88134
|
-
async function
|
|
88628
|
+
async function checkoutOf2(scope) {
|
|
88135
88629
|
const projects = createProjectRepository(db);
|
|
88136
88630
|
if (scope.scopeType === "project") {
|
|
88137
88631
|
const project3 = await projects.findById(scope.scopeId);
|
|
@@ -88184,7 +88678,7 @@ function createScriptRunner({
|
|
|
88184
88678
|
}
|
|
88185
88679
|
return {
|
|
88186
88680
|
async status(scope) {
|
|
88187
|
-
const { project: project2, cwd } = await
|
|
88681
|
+
const { project: project2, cwd } = await checkoutOf2(scope);
|
|
88188
88682
|
const scripts = await readProjectScripts(cwd, {
|
|
88189
88683
|
warn: (message) => log?.warn({ scope, message }, "scripts ignorados")
|
|
88190
88684
|
});
|
|
@@ -88208,7 +88702,7 @@ function createScriptRunner({
|
|
|
88208
88702
|
};
|
|
88209
88703
|
},
|
|
88210
88704
|
async start(scope, phase) {
|
|
88211
|
-
const { project: project2, worktree: worktree2, cwd } = await
|
|
88705
|
+
const { project: project2, worktree: worktree2, cwd } = await checkoutOf2(scope);
|
|
88212
88706
|
const scripts = await readProjectScripts(cwd);
|
|
88213
88707
|
const command = scripts[phase];
|
|
88214
88708
|
if (command === null) {
|
|
@@ -88274,15 +88768,15 @@ function createScriptRunner({
|
|
|
88274
88768
|
},
|
|
88275
88769
|
async runToCompletion(scope, phase, { timeoutMs = TEARDOWN_TIMEOUT_MS } = {}) {
|
|
88276
88770
|
const { session: row } = await this.start(scope, phase);
|
|
88277
|
-
return new Promise((
|
|
88771
|
+
return new Promise((resolve6) => {
|
|
88278
88772
|
const timer = setTimeout(() => {
|
|
88279
88773
|
log?.warn({ scope, phase }, "script n\xE3o terminou no tempo \u2014 seguindo sem ele");
|
|
88280
88774
|
ptyManager.kill(row.id);
|
|
88281
|
-
|
|
88775
|
+
resolve6(null);
|
|
88282
88776
|
}, timeoutMs);
|
|
88283
88777
|
ptyManager.onExit(row.id, ({ exitCode }) => {
|
|
88284
88778
|
clearTimeout(timer);
|
|
88285
|
-
|
|
88779
|
+
resolve6(exitCode);
|
|
88286
88780
|
});
|
|
88287
88781
|
});
|
|
88288
88782
|
},
|
|
@@ -88450,6 +88944,19 @@ function createSessionStore({
|
|
|
88450
88944
|
log?.warn({ session: row.id, err: error40 }, "falha ao varrer reverts do checkout");
|
|
88451
88945
|
}
|
|
88452
88946
|
}
|
|
88947
|
+
async function inheritedMode(scopeType, scopeId) {
|
|
88948
|
+
try {
|
|
88949
|
+
const projects = createProjectRepository(db);
|
|
88950
|
+
const projectId = scopeType === "project" ? scopeId : (await createWorktreeRepository(db).findById(scopeId))?.projectId;
|
|
88951
|
+
if (!projectId) return "ask";
|
|
88952
|
+
const project2 = await projects.findById(projectId);
|
|
88953
|
+
if (!project2) return "ask";
|
|
88954
|
+
const workspace2 = await createWorkspaceRepository(db).findById(project2.workspaceId);
|
|
88955
|
+
return workspace2?.defaultLumemMode === "auto" ? "auto" : "ask";
|
|
88956
|
+
} catch {
|
|
88957
|
+
return "ask";
|
|
88958
|
+
}
|
|
88959
|
+
}
|
|
88453
88960
|
return {
|
|
88454
88961
|
async start(input) {
|
|
88455
88962
|
const { kind, agentConfigId = null, scopeType, scopeId, cwd, command } = input;
|
|
@@ -88462,12 +88969,17 @@ function createSessionStore({
|
|
|
88462
88969
|
"esta inst\xE2ncia n\xE3o sabe iniciar sess\xE3o ACP: nenhum AcpManager foi ligado"
|
|
88463
88970
|
);
|
|
88464
88971
|
}
|
|
88972
|
+
const inherited = await inheritedMode(scopeType, scopeId);
|
|
88465
88973
|
const agent = await acpManager.spawn({
|
|
88466
88974
|
command,
|
|
88467
88975
|
...input.args ? { args: input.args } : {},
|
|
88468
88976
|
cwd,
|
|
88469
88977
|
...input.env ? { env: input.env } : {},
|
|
88470
|
-
...input.adapterVersion ? { adapterVersion: input.adapterVersion } : {}
|
|
88978
|
+
...input.adapterVersion ? { adapterVersion: input.adapterVersion } : {},
|
|
88979
|
+
// O modo herdado, e ele é o mesmo nos dois campos: uma sessão nova
|
|
88980
|
+
// começa no padrão do workspace, e o menu mostra de onde veio.
|
|
88981
|
+
lumemMode: inherited,
|
|
88982
|
+
lumemModeDefault: inherited
|
|
88471
88983
|
});
|
|
88472
88984
|
try {
|
|
88473
88985
|
return await sessions.create({
|
|
@@ -88545,6 +89057,16 @@ function createSessionStore({
|
|
|
88545
89057
|
...config2?.env && Object.keys(config2.env).length > 0 ? { env: config2.env } : {},
|
|
88546
89058
|
...config2?.adapterVersion ? { adapterVersion: config2.adapterVersion } : {},
|
|
88547
89059
|
acpSessionId: row.acpSessionId,
|
|
89060
|
+
/*
|
|
89061
|
+
* A política volta como estava (F1.4).
|
|
89062
|
+
*
|
|
89063
|
+
* `lumemMode` da linha morta, e não o padrão do workspace: retomar é
|
|
89064
|
+
* continuar a mesma conversa, e uma que estava em `auto` voltar
|
|
89065
|
+
* perguntando tudo seria a feature desfazendo a escolha em silêncio.
|
|
89066
|
+
* Inclusive `free` — o portão já foi atravessado por esta conversa.
|
|
89067
|
+
*/
|
|
89068
|
+
lumemMode: row.lumemMode,
|
|
89069
|
+
lumemModeDefault: await inheritedMode(row.scopeType, row.scopeId),
|
|
88548
89070
|
// What makes the new session's transcript self-contained (D15): the old
|
|
88549
89071
|
// conversation is copied in front of it, and the separator recorded after.
|
|
88550
89072
|
fromSessionId: row.id
|
|
@@ -88597,6 +89119,19 @@ function createSessionStore({
|
|
|
88597
89119
|
* interface offering something it cannot do.
|
|
88598
89120
|
*/
|
|
88599
89121
|
configOptions: [],
|
|
89122
|
+
/*
|
|
89123
|
+
* O modo em que a conversa **esteve** (F1.8).
|
|
89124
|
+
*
|
|
89125
|
+
* A autoria sai de `row.mode`, e não de `configOptions`: aqui a lista é
|
|
89126
|
+
* vazia por construção, e perguntar a ela quem era o dono responderia
|
|
89127
|
+
* "o Lumem" para toda conversa morta — inclusive as que rodaram com um
|
|
89128
|
+
* agente cheio de modos. O que a linha guarda é qual modo estava valendo,
|
|
89129
|
+
* e ter um é a prova de que o agente era o dono.
|
|
89130
|
+
*/
|
|
89131
|
+
modeOwner: row.mode ? "agent" : "lumem",
|
|
89132
|
+
lumemMode: row.lumemMode,
|
|
89133
|
+
lumemModeDefault: "ask",
|
|
89134
|
+
cwd: row.cwd,
|
|
88600
89135
|
transcript: [...acpManager.storedTranscript(row.id)]
|
|
88601
89136
|
};
|
|
88602
89137
|
},
|
|
@@ -88636,7 +89171,11 @@ function createSessionStore({
|
|
|
88636
89171
|
const offPty = ptyManager.watchExits((info) => record2(info.id, info.exitCode));
|
|
88637
89172
|
const offAcp = acpManager?.watchExits((info) => record2(info.id, info.exitCode));
|
|
88638
89173
|
const offConfig = acpManager?.watchConfig((info) => {
|
|
88639
|
-
void sessions.setConfig(info.id, {
|
|
89174
|
+
void sessions.setConfig(info.id, {
|
|
89175
|
+
mode: info.mode,
|
|
89176
|
+
model: info.model,
|
|
89177
|
+
lumemMode: info.lumemMode
|
|
89178
|
+
}).catch((error40) => {
|
|
88640
89179
|
log?.warn({ session: info.id, err: error40 }, "falha ao registrar troca de configura\xE7\xE3o");
|
|
88641
89180
|
});
|
|
88642
89181
|
});
|
|
@@ -89593,8 +90132,8 @@ var Unpromise = class Unpromise2 {
|
|
|
89593
90132
|
status: "fulfilled",
|
|
89594
90133
|
value
|
|
89595
90134
|
};
|
|
89596
|
-
subscribers === null || subscribers === void 0 || subscribers.forEach(({ resolve:
|
|
89597
|
-
|
|
90135
|
+
subscribers === null || subscribers === void 0 || subscribers.forEach(({ resolve: resolve6 }) => {
|
|
90136
|
+
resolve6(value);
|
|
89598
90137
|
});
|
|
89599
90138
|
});
|
|
89600
90139
|
if ("catch" in thenReturn) thenReturn.catch((reason) => {
|
|
@@ -89735,15 +90274,15 @@ function resolveSelfTuple(promise2) {
|
|
|
89735
90274
|
return Unpromise.proxy(promise2).then(() => [promise2]);
|
|
89736
90275
|
}
|
|
89737
90276
|
function withResolvers() {
|
|
89738
|
-
let
|
|
90277
|
+
let resolve6;
|
|
89739
90278
|
let reject;
|
|
89740
90279
|
const promise2 = new Promise((_resolve, _reject) => {
|
|
89741
|
-
|
|
90280
|
+
resolve6 = _resolve;
|
|
89742
90281
|
reject = _reject;
|
|
89743
90282
|
});
|
|
89744
90283
|
return {
|
|
89745
90284
|
promise: promise2,
|
|
89746
|
-
resolve:
|
|
90285
|
+
resolve: resolve6,
|
|
89747
90286
|
reject
|
|
89748
90287
|
};
|
|
89749
90288
|
}
|
|
@@ -89787,8 +90326,8 @@ function timerResource(ms) {
|
|
|
89787
90326
|
let timer = null;
|
|
89788
90327
|
return makeResource({ start() {
|
|
89789
90328
|
if (timer) throw new Error("Timer already started");
|
|
89790
|
-
const promise2 = new Promise((
|
|
89791
|
-
timer = setTimeout(() =>
|
|
90329
|
+
const promise2 = new Promise((resolve6) => {
|
|
90330
|
+
timer = setTimeout(() => resolve6(disposablePromiseTimerResult), ms);
|
|
89792
90331
|
});
|
|
89793
90332
|
return promise2;
|
|
89794
90333
|
} }, () => {
|
|
@@ -89975,15 +90514,15 @@ function _takeWithGrace() {
|
|
|
89975
90514
|
return _takeWithGrace.apply(this, arguments);
|
|
89976
90515
|
}
|
|
89977
90516
|
function createDeferred() {
|
|
89978
|
-
let
|
|
90517
|
+
let resolve6;
|
|
89979
90518
|
let reject;
|
|
89980
90519
|
const promise2 = new Promise((res, rej) => {
|
|
89981
|
-
|
|
90520
|
+
resolve6 = res;
|
|
89982
90521
|
reject = rej;
|
|
89983
90522
|
});
|
|
89984
90523
|
return {
|
|
89985
90524
|
promise: promise2,
|
|
89986
|
-
resolve:
|
|
90525
|
+
resolve: resolve6,
|
|
89987
90526
|
reject
|
|
89988
90527
|
};
|
|
89989
90528
|
}
|
|
@@ -91558,8 +92097,8 @@ function getWSConnectionHandler(opts) {
|
|
|
91558
92097
|
try {
|
|
91559
92098
|
var _usingCtx = (0, import_usingCtx2.default)();
|
|
91560
92099
|
const iterator = _usingCtx.a(iteratorResource(iterable));
|
|
91561
|
-
const abortPromise = new Promise((
|
|
91562
|
-
abortController$1.signal.onabort = () =>
|
|
92100
|
+
const abortPromise = new Promise((resolve6) => {
|
|
92101
|
+
abortController$1.signal.onabort = () => resolve6("abort");
|
|
91563
92102
|
});
|
|
91564
92103
|
let next;
|
|
91565
92104
|
let result$1;
|
|
@@ -92039,8 +92578,8 @@ async function* snapshots(id, current, listeners, signal) {
|
|
|
92039
92578
|
yield job;
|
|
92040
92579
|
if (isTerminal(job.state)) return;
|
|
92041
92580
|
}
|
|
92042
|
-
await new Promise((
|
|
92043
|
-
wake =
|
|
92581
|
+
await new Promise((resolve6) => {
|
|
92582
|
+
wake = resolve6;
|
|
92044
92583
|
});
|
|
92045
92584
|
wake = void 0;
|
|
92046
92585
|
}
|
|
@@ -92051,6 +92590,652 @@ async function* snapshots(id, current, listeners, signal) {
|
|
|
92051
92590
|
}
|
|
92052
92591
|
}
|
|
92053
92592
|
|
|
92593
|
+
// src/pr/GhHost.ts
|
|
92594
|
+
import { mkdtemp, rm as rm4, writeFile as writeFile4 } from "node:fs/promises";
|
|
92595
|
+
import { tmpdir } from "node:os";
|
|
92596
|
+
import { join as join15 } from "node:path";
|
|
92597
|
+
|
|
92598
|
+
// src/git/git-url.ts
|
|
92599
|
+
var ALLOWED = /* @__PURE__ */ new Set(["https", "http", "ssh", "file"]);
|
|
92600
|
+
var HELPER = /^([A-Za-z][A-Za-z0-9+.-]*)::/;
|
|
92601
|
+
var SCHEME = /^([A-Za-z][A-Za-z0-9+.-]*):\/\//;
|
|
92602
|
+
var CONTROL = /[\u0000-\u001f\u007f]/;
|
|
92603
|
+
function refuse(rule, message) {
|
|
92604
|
+
return { ok: false, rule, message };
|
|
92605
|
+
}
|
|
92606
|
+
function refuseScheme(scheme) {
|
|
92607
|
+
if (scheme === "git") {
|
|
92608
|
+
return refuse(
|
|
92609
|
+
"scheme",
|
|
92610
|
+
"o transporte git:// n\xE3o \xE9 aceito: ele n\xE3o autentica nem verifica integridade \u2014 use a URL https ou ssh do mesmo reposit\xF3rio"
|
|
92611
|
+
);
|
|
92612
|
+
}
|
|
92613
|
+
return refuse(
|
|
92614
|
+
"scheme",
|
|
92615
|
+
`o transporte "${scheme}" n\xE3o est\xE1 na lista de transportes aceitos (https, http, ssh, file)`
|
|
92616
|
+
);
|
|
92617
|
+
}
|
|
92618
|
+
function parseGitUrl(input) {
|
|
92619
|
+
const raw = input.trim();
|
|
92620
|
+
if (raw === "") return refuse("empty", "informe a URL do reposit\xF3rio");
|
|
92621
|
+
if (CONTROL.test(raw)) {
|
|
92622
|
+
return refuse("control-chars", "a URL tem caracteres de controle");
|
|
92623
|
+
}
|
|
92624
|
+
if (raw.startsWith("-")) {
|
|
92625
|
+
return refuse("leading-dash", "a URL n\xE3o pode come\xE7ar com '-'");
|
|
92626
|
+
}
|
|
92627
|
+
const helper = HELPER.exec(raw);
|
|
92628
|
+
if (helper) return refuseScheme(helper[1].toLowerCase());
|
|
92629
|
+
const scheme = SCHEME.exec(raw);
|
|
92630
|
+
if (scheme) return parseWithScheme(raw, scheme[1].toLowerCase());
|
|
92631
|
+
if (isScp(raw)) return parseScp(raw);
|
|
92632
|
+
return refuse(
|
|
92633
|
+
"scheme",
|
|
92634
|
+
"n\xE3o reconheci isto como URL git: use https://, ssh://, file:// ou a forma git@host:org/repo.git"
|
|
92635
|
+
);
|
|
92636
|
+
}
|
|
92637
|
+
function scpColon(raw) {
|
|
92638
|
+
const at = raw.indexOf("@");
|
|
92639
|
+
return raw.indexOf(":", at === -1 ? 0 : at + 1);
|
|
92640
|
+
}
|
|
92641
|
+
function isScp(raw) {
|
|
92642
|
+
const colon = scpColon(raw);
|
|
92643
|
+
if (colon <= 0) return false;
|
|
92644
|
+
const slash = raw.indexOf("/");
|
|
92645
|
+
return slash === -1 || colon < slash;
|
|
92646
|
+
}
|
|
92647
|
+
function parseWithScheme(raw, scheme) {
|
|
92648
|
+
if (!ALLOWED.has(scheme)) return refuseScheme(scheme);
|
|
92649
|
+
let parsed;
|
|
92650
|
+
try {
|
|
92651
|
+
parsed = new URL(raw);
|
|
92652
|
+
} catch {
|
|
92653
|
+
return refuse("host", `n\xE3o consegui ler ${raw} como URL`);
|
|
92654
|
+
}
|
|
92655
|
+
if (scheme === "file") return parseFile(raw, parsed);
|
|
92656
|
+
if (parsed.hostname === "" || raw.slice(scheme.length + "://".length).startsWith("/")) {
|
|
92657
|
+
return refuse("host", "a URL n\xE3o tem host");
|
|
92658
|
+
}
|
|
92659
|
+
const path = parsed.pathname.replace(/^\/+/, "");
|
|
92660
|
+
if (path === "") {
|
|
92661
|
+
return refuse("path", "a URL n\xE3o diz qual reposit\xF3rio");
|
|
92662
|
+
}
|
|
92663
|
+
const sanitized = new URL(raw);
|
|
92664
|
+
sanitized.username = "";
|
|
92665
|
+
sanitized.password = "";
|
|
92666
|
+
return {
|
|
92667
|
+
ok: true,
|
|
92668
|
+
raw,
|
|
92669
|
+
url: {
|
|
92670
|
+
scheme,
|
|
92671
|
+
href: sanitized.href,
|
|
92672
|
+
host: parsed.host,
|
|
92673
|
+
path,
|
|
92674
|
+
insecure: scheme === "http",
|
|
92675
|
+
scp: false
|
|
92676
|
+
}
|
|
92677
|
+
};
|
|
92678
|
+
}
|
|
92679
|
+
function parseFile(raw, parsed) {
|
|
92680
|
+
if (parsed.hostname !== "" && parsed.hostname !== "localhost") {
|
|
92681
|
+
return refuse("host", "file:// com host n\xE3o \xE9 aceito; use file:///caminho/absoluto");
|
|
92682
|
+
}
|
|
92683
|
+
const path = decodeURIComponent(parsed.pathname);
|
|
92684
|
+
if (!path.startsWith("/")) {
|
|
92685
|
+
return refuse("path", "o caminho de um file:// precisa ser absoluto");
|
|
92686
|
+
}
|
|
92687
|
+
return {
|
|
92688
|
+
ok: true,
|
|
92689
|
+
raw,
|
|
92690
|
+
url: { scheme: "file", href: `file://${parsed.pathname}`, host: "", path, insecure: false, scp: false }
|
|
92691
|
+
};
|
|
92692
|
+
}
|
|
92693
|
+
function parseScp(raw) {
|
|
92694
|
+
const colon = scpColon(raw);
|
|
92695
|
+
const authority = raw.slice(0, colon);
|
|
92696
|
+
const path = raw.slice(colon + 1);
|
|
92697
|
+
const at = authority.lastIndexOf("@");
|
|
92698
|
+
const user = at === -1 ? "" : authority.slice(0, at);
|
|
92699
|
+
const host = authority.slice(at + 1);
|
|
92700
|
+
if (host === "") return refuse("host", "a URL n\xE3o tem host");
|
|
92701
|
+
if (path === "") return refuse("path", "a URL n\xE3o diz qual reposit\xF3rio");
|
|
92702
|
+
const cleanUser = user.includes(":") ? user.slice(0, user.indexOf(":")) : user;
|
|
92703
|
+
const prefix = cleanUser === "" ? "" : `${cleanUser}@`;
|
|
92704
|
+
return {
|
|
92705
|
+
ok: true,
|
|
92706
|
+
raw,
|
|
92707
|
+
url: {
|
|
92708
|
+
scheme: "ssh",
|
|
92709
|
+
href: `${prefix}${host}:${path}`,
|
|
92710
|
+
host,
|
|
92711
|
+
path: path.replace(/^\/+/, ""),
|
|
92712
|
+
insecure: false,
|
|
92713
|
+
scp: true
|
|
92714
|
+
}
|
|
92715
|
+
};
|
|
92716
|
+
}
|
|
92717
|
+
function repoNameOf(url2) {
|
|
92718
|
+
const segments = url2.path.split("/").filter((segment) => segment !== "");
|
|
92719
|
+
const last = segments.at(-1) ?? "";
|
|
92720
|
+
return last.endsWith(".git") ? last.slice(0, -".git".length) : last;
|
|
92721
|
+
}
|
|
92722
|
+
|
|
92723
|
+
// src/pr/exec.ts
|
|
92724
|
+
import { execFile as execFile2 } from "node:child_process";
|
|
92725
|
+
var DEFAULT_GH_TIMEOUT_MS = 2e4;
|
|
92726
|
+
var GH_MAX_BUFFER = 8 * 1024 * 1024;
|
|
92727
|
+
var execGh = (args, { cwd, timeoutMs = DEFAULT_GH_TIMEOUT_MS, bin = "gh" }) => new Promise((resolve6) => {
|
|
92728
|
+
execFile2(
|
|
92729
|
+
bin,
|
|
92730
|
+
[...args],
|
|
92731
|
+
{
|
|
92732
|
+
cwd,
|
|
92733
|
+
timeout: timeoutMs,
|
|
92734
|
+
maxBuffer: GH_MAX_BUFFER,
|
|
92735
|
+
env: {
|
|
92736
|
+
...process.env,
|
|
92737
|
+
// Cor no meio de um JSON é o tipo de coisa que só quebra na máquina de
|
|
92738
|
+
// outra pessoa, que tem `GH_FORCE_TTY` no perfil.
|
|
92739
|
+
NO_COLOR: "1",
|
|
92740
|
+
GH_NO_UPDATE_NOTIFIER: "1",
|
|
92741
|
+
// O `gh` chama o `git`, e o `git` pode pedir credencial. Sem isto, um
|
|
92742
|
+
// remote mal configurado trava o daemon até o timeout.
|
|
92743
|
+
GIT_TERMINAL_PROMPT: "0"
|
|
92744
|
+
}
|
|
92745
|
+
},
|
|
92746
|
+
(error40, stdout, stderr) => {
|
|
92747
|
+
const failure = error40;
|
|
92748
|
+
resolve6({
|
|
92749
|
+
stdout,
|
|
92750
|
+
stderr,
|
|
92751
|
+
code: failure === null ? 0 : typeof failure.code === "number" ? failure.code : null,
|
|
92752
|
+
spawnError: typeof failure?.code === "string" ? failure.code : null,
|
|
92753
|
+
timedOut: failure?.killed === true
|
|
92754
|
+
});
|
|
92755
|
+
}
|
|
92756
|
+
);
|
|
92757
|
+
});
|
|
92758
|
+
var EXIT_AUTH_REQUIRED = 4;
|
|
92759
|
+
var OFFLINE = [
|
|
92760
|
+
"dial tcp",
|
|
92761
|
+
"no such host",
|
|
92762
|
+
"connection refused",
|
|
92763
|
+
"network is unreachable",
|
|
92764
|
+
"i/o timeout",
|
|
92765
|
+
"tls: ",
|
|
92766
|
+
"proxyconnect",
|
|
92767
|
+
"eof"
|
|
92768
|
+
];
|
|
92769
|
+
function classify(result) {
|
|
92770
|
+
if (result.spawnError === "ENOENT") {
|
|
92771
|
+
return {
|
|
92772
|
+
kind: "no-binary",
|
|
92773
|
+
message: "o gh n\xE3o est\xE1 instalado ou n\xE3o est\xE1 no PATH"
|
|
92774
|
+
};
|
|
92775
|
+
}
|
|
92776
|
+
if (result.timedOut) {
|
|
92777
|
+
return { kind: "timeout", message: "o gh n\xE3o respondeu a tempo" };
|
|
92778
|
+
}
|
|
92779
|
+
if (result.spawnError !== null) {
|
|
92780
|
+
return { kind: "failed", message: "n\xE3o deu para executar o gh" };
|
|
92781
|
+
}
|
|
92782
|
+
if (result.code === 0) return null;
|
|
92783
|
+
const stderr = result.stderr.toLowerCase();
|
|
92784
|
+
if (result.code === EXIT_AUTH_REQUIRED || stderr.includes("gh auth login")) {
|
|
92785
|
+
if (stderr.includes("known github host")) {
|
|
92786
|
+
return {
|
|
92787
|
+
kind: "unsupported-host",
|
|
92788
|
+
message: "nenhum remote deste reposit\xF3rio aponta para um host GitHub conhecido"
|
|
92789
|
+
};
|
|
92790
|
+
}
|
|
92791
|
+
return { kind: "no-auth", message: "o gh n\xE3o est\xE1 autenticado" };
|
|
92792
|
+
}
|
|
92793
|
+
if (stderr.includes("rate limit")) {
|
|
92794
|
+
return {
|
|
92795
|
+
kind: "rate-limit",
|
|
92796
|
+
message: "o limite de consultas do GitHub foi atingido",
|
|
92797
|
+
retryAt: parseRetryAt(result.stderr)
|
|
92798
|
+
};
|
|
92799
|
+
}
|
|
92800
|
+
if (stderr.includes("not a git repository")) {
|
|
92801
|
+
return { kind: "not-a-repo", message: "este diret\xF3rio n\xE3o \xE9 um reposit\xF3rio git" };
|
|
92802
|
+
}
|
|
92803
|
+
if (OFFLINE.some((needle) => stderr.includes(needle))) {
|
|
92804
|
+
return { kind: "offline", message: "n\xE3o deu para falar com o GitHub" };
|
|
92805
|
+
}
|
|
92806
|
+
return { kind: "failed", message: "o gh recusou a consulta" };
|
|
92807
|
+
}
|
|
92808
|
+
function parseRetryAt(stderr) {
|
|
92809
|
+
const iso = /(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z)/.exec(stderr);
|
|
92810
|
+
if (iso) return iso[1];
|
|
92811
|
+
const seconds = /retry after (\d+) seconds?/i.exec(stderr);
|
|
92812
|
+
if (seconds) return new Date(Date.now() + Number(seconds[1]) * 1e3).toISOString();
|
|
92813
|
+
return null;
|
|
92814
|
+
}
|
|
92815
|
+
|
|
92816
|
+
// src/pr/url.ts
|
|
92817
|
+
function safeUrl(candidate, host) {
|
|
92818
|
+
if (candidate === null || candidate === void 0 || candidate.trim() === "") return null;
|
|
92819
|
+
if (host === null || host === "") return null;
|
|
92820
|
+
let parsed;
|
|
92821
|
+
try {
|
|
92822
|
+
parsed = new URL(candidate.trim());
|
|
92823
|
+
} catch {
|
|
92824
|
+
return null;
|
|
92825
|
+
}
|
|
92826
|
+
if (parsed.protocol !== "https:") return null;
|
|
92827
|
+
if (parsed.username !== "" || parsed.password !== "") return null;
|
|
92828
|
+
if (parsed.hostname.toLowerCase() !== host.toLowerCase()) return null;
|
|
92829
|
+
return parsed.href;
|
|
92830
|
+
}
|
|
92831
|
+
var NAME_WITH_OWNER = /^[A-Za-z0-9][A-Za-z0-9._-]*\/[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
92832
|
+
function compareUrl(input) {
|
|
92833
|
+
const { host, repo, base, head } = input;
|
|
92834
|
+
if (host === null || host === "" || base === "" || head === "") return null;
|
|
92835
|
+
if (!NAME_WITH_OWNER.test(repo)) return null;
|
|
92836
|
+
if (base === head) return null;
|
|
92837
|
+
const url2 = `https://${host}/${repo}/compare/${encodeURIComponent(base)}...${encodeURIComponent(head)}?expand=1`;
|
|
92838
|
+
return safeUrl(url2, host);
|
|
92839
|
+
}
|
|
92840
|
+
|
|
92841
|
+
// src/pr/GhHost.ts
|
|
92842
|
+
var DEFAULT_LIMIT = 50;
|
|
92843
|
+
var FIELDS = [
|
|
92844
|
+
"number",
|
|
92845
|
+
"url",
|
|
92846
|
+
"title",
|
|
92847
|
+
"state",
|
|
92848
|
+
"isDraft",
|
|
92849
|
+
"mergeable",
|
|
92850
|
+
"mergeStateStatus",
|
|
92851
|
+
"reviewDecision",
|
|
92852
|
+
"latestReviews",
|
|
92853
|
+
"headRefName",
|
|
92854
|
+
"baseRefName",
|
|
92855
|
+
"updatedAt",
|
|
92856
|
+
"mergedAt",
|
|
92857
|
+
"closedAt",
|
|
92858
|
+
"author",
|
|
92859
|
+
"statusCheckRollup"
|
|
92860
|
+
].join(",");
|
|
92861
|
+
var PROJECTION = [
|
|
92862
|
+
"map({number, url, title, state, isDraft, mergeable, mergeStateStatus, reviewDecision,",
|
|
92863
|
+
"headRefName, baseRefName, updatedAt, mergedAt, closedAt,",
|
|
92864
|
+
'author: (.author.login // ""),',
|
|
92865
|
+
'reviews: [.latestReviews[]? | {author: (.author.login // ""), state: .state}],',
|
|
92866
|
+
"checks: [.statusCheckRollup[]? | {",
|
|
92867
|
+
'name: (.name // .context // ""), app: (.workflowName // .context // ""),',
|
|
92868
|
+
'status: (.status // "COMPLETED"), conclusion: (.conclusion // .state // ""),',
|
|
92869
|
+
'url: (.detailsUrl // .targetUrl // ""),',
|
|
92870
|
+
"startedAt: (.startedAt // null), completedAt: (.completedAt // null)}]})"
|
|
92871
|
+
].join(" ");
|
|
92872
|
+
var REPO_FIELDS = [
|
|
92873
|
+
"nameWithOwner",
|
|
92874
|
+
"mergeCommitAllowed",
|
|
92875
|
+
"squashMergeAllowed",
|
|
92876
|
+
"rebaseMergeAllowed",
|
|
92877
|
+
"deleteBranchOnMerge"
|
|
92878
|
+
].join(",");
|
|
92879
|
+
var CONTROL2 = /[\u0000-\u001f\u007f]/;
|
|
92880
|
+
function createGhHost({ exec = execGh, limit = DEFAULT_LIMIT } = {}) {
|
|
92881
|
+
function hostOf(remoteUrl) {
|
|
92882
|
+
if (remoteUrl === null || remoteUrl.trim() === "") return null;
|
|
92883
|
+
const parsed = parseGitUrl(remoteUrl);
|
|
92884
|
+
if (!parsed.ok) return null;
|
|
92885
|
+
const host = parsed.url.host.split(":")[0] ?? "";
|
|
92886
|
+
return host === "" ? null : host.toLowerCase();
|
|
92887
|
+
}
|
|
92888
|
+
function isGitHub(host) {
|
|
92889
|
+
if (host === null) return false;
|
|
92890
|
+
return host === "github.com" || host.endsWith(".github.com") || host.startsWith("github.");
|
|
92891
|
+
}
|
|
92892
|
+
async function run3(args, cwd) {
|
|
92893
|
+
const result = await exec(args, { cwd });
|
|
92894
|
+
const failure = classify(result);
|
|
92895
|
+
return failure === null ? { ok: true, stdout: result.stdout } : { ok: false, failure };
|
|
92896
|
+
}
|
|
92897
|
+
function unsupported(host) {
|
|
92898
|
+
return {
|
|
92899
|
+
kind: "unsupported-host",
|
|
92900
|
+
message: host === null ? "este reposit\xF3rio n\xE3o tem um remote conhecido" : `o Lumem ainda n\xE3o fala com ${host}`
|
|
92901
|
+
};
|
|
92902
|
+
}
|
|
92903
|
+
function flag(name, value) {
|
|
92904
|
+
return `--${name}=${value}`;
|
|
92905
|
+
}
|
|
92906
|
+
function refuseControl(value, what) {
|
|
92907
|
+
return CONTROL2.test(value) ? { kind: "failed", message: `o ${what} tem caracteres de controle` } : null;
|
|
92908
|
+
}
|
|
92909
|
+
return {
|
|
92910
|
+
name: "GitHub",
|
|
92911
|
+
supports(remoteUrl) {
|
|
92912
|
+
return isGitHub(hostOf(remoteUrl));
|
|
92913
|
+
},
|
|
92914
|
+
async read({ repoPath, remoteUrl }) {
|
|
92915
|
+
const host = hostOf(remoteUrl);
|
|
92916
|
+
if (!isGitHub(host)) return { ok: false, failure: unsupported(host) };
|
|
92917
|
+
const [pulls, repo] = await Promise.all([
|
|
92918
|
+
run3(
|
|
92919
|
+
[
|
|
92920
|
+
"pr",
|
|
92921
|
+
"list",
|
|
92922
|
+
"--state",
|
|
92923
|
+
"all",
|
|
92924
|
+
"--limit",
|
|
92925
|
+
String(limit),
|
|
92926
|
+
"--json",
|
|
92927
|
+
FIELDS,
|
|
92928
|
+
"--jq",
|
|
92929
|
+
PROJECTION
|
|
92930
|
+
],
|
|
92931
|
+
repoPath
|
|
92932
|
+
),
|
|
92933
|
+
run3(["repo", "view", "--json", REPO_FIELDS], repoPath)
|
|
92934
|
+
]);
|
|
92935
|
+
if (!pulls.ok) return pulls;
|
|
92936
|
+
if (!repo.ok) return repo;
|
|
92937
|
+
const parsed = parseJson(pulls.stdout, []);
|
|
92938
|
+
const settings = parseJson(repo.stdout, {});
|
|
92939
|
+
if (parsed === null || settings === null) {
|
|
92940
|
+
return {
|
|
92941
|
+
ok: false,
|
|
92942
|
+
failure: { kind: "failed", message: "o gh respondeu algo que n\xE3o \xE9 JSON" }
|
|
92943
|
+
};
|
|
92944
|
+
}
|
|
92945
|
+
return {
|
|
92946
|
+
ok: true,
|
|
92947
|
+
snapshot: {
|
|
92948
|
+
host: host ?? "",
|
|
92949
|
+
repo: typeof settings.nameWithOwner === "string" ? settings.nameWithOwner : "",
|
|
92950
|
+
pulls: parsed,
|
|
92951
|
+
merge: mergeOptionsOf(settings),
|
|
92952
|
+
readAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
92953
|
+
}
|
|
92954
|
+
};
|
|
92955
|
+
},
|
|
92956
|
+
async create(input) {
|
|
92957
|
+
const host = hostOf(input.remoteUrl);
|
|
92958
|
+
if (!isGitHub(host)) return { ok: false, failure: unsupported(host) };
|
|
92959
|
+
const bad = refuseControl(input.title, "t\xEDtulo") ?? refuseControl(input.base, "nome da base") ?? refuseControl(input.head, "nome da branch");
|
|
92960
|
+
if (bad !== null) return { ok: false, failure: bad };
|
|
92961
|
+
if (input.title.trim() === "") {
|
|
92962
|
+
return { ok: false, failure: { kind: "failed", message: "a PR precisa de um t\xEDtulo" } };
|
|
92963
|
+
}
|
|
92964
|
+
const dir = await mkdtemp(join15(tmpdir(), "lumem-pr-"));
|
|
92965
|
+
const bodyFile = join15(dir, "body.md");
|
|
92966
|
+
try {
|
|
92967
|
+
await writeFile4(bodyFile, input.body, "utf8");
|
|
92968
|
+
const args = [
|
|
92969
|
+
"pr",
|
|
92970
|
+
"create",
|
|
92971
|
+
flag("base", input.base),
|
|
92972
|
+
flag("head", input.head),
|
|
92973
|
+
flag("title", input.title),
|
|
92974
|
+
flag("body-file", bodyFile)
|
|
92975
|
+
];
|
|
92976
|
+
if (input.draft) args.push("--draft");
|
|
92977
|
+
const result = await run3(args, input.repoPath);
|
|
92978
|
+
if (!result.ok) return { ok: false, failure: createFailure(result.failure) };
|
|
92979
|
+
const printed = result.stdout.trim().split("\n").at(-1)?.trim() ?? "";
|
|
92980
|
+
return { ok: true, url: safeUrl(printed, host) ?? "" };
|
|
92981
|
+
} finally {
|
|
92982
|
+
await rm4(dir, { recursive: true, force: true });
|
|
92983
|
+
}
|
|
92984
|
+
},
|
|
92985
|
+
async merge(input) {
|
|
92986
|
+
const host = hostOf(input.remoteUrl);
|
|
92987
|
+
if (!isGitHub(host)) return { ok: false, failure: unsupported(host) };
|
|
92988
|
+
if (!Number.isInteger(input.number) || input.number <= 0) {
|
|
92989
|
+
return { ok: false, failure: { kind: "failed", message: "n\xFAmero de PR inv\xE1lido" } };
|
|
92990
|
+
}
|
|
92991
|
+
const args = ["pr", "merge", String(input.number), `--${input.strategy}`];
|
|
92992
|
+
if (input.deleteBranch) args.push("--delete-branch");
|
|
92993
|
+
const result = await run3(args, input.repoPath);
|
|
92994
|
+
if (!result.ok) return { ok: false, failure: mergeFailure(result.failure) };
|
|
92995
|
+
return { ok: true, url: "" };
|
|
92996
|
+
}
|
|
92997
|
+
};
|
|
92998
|
+
}
|
|
92999
|
+
function createFailure(failure) {
|
|
93000
|
+
if (failure.kind !== "failed") return failure;
|
|
93001
|
+
return { kind: "failed", message: "o GitHub recusou criar a pull request" };
|
|
93002
|
+
}
|
|
93003
|
+
function mergeFailure(failure) {
|
|
93004
|
+
if (failure.kind !== "failed") return failure;
|
|
93005
|
+
return { kind: "failed", message: "o GitHub recusou o merge" };
|
|
93006
|
+
}
|
|
93007
|
+
function mergeOptionsOf(settings) {
|
|
93008
|
+
const flagOf = (key) => settings[key] === true;
|
|
93009
|
+
const options = {
|
|
93010
|
+
merge: flagOf("mergeCommitAllowed"),
|
|
93011
|
+
squash: flagOf("squashMergeAllowed"),
|
|
93012
|
+
rebase: flagOf("rebaseMergeAllowed"),
|
|
93013
|
+
deleteBranchOnMerge: flagOf("deleteBranchOnMerge")
|
|
93014
|
+
};
|
|
93015
|
+
if (!options.merge && !options.squash && !options.rebase) {
|
|
93016
|
+
return { ...options, merge: true };
|
|
93017
|
+
}
|
|
93018
|
+
return options;
|
|
93019
|
+
}
|
|
93020
|
+
function parseJson(stdout, whenEmpty) {
|
|
93021
|
+
const text2 = stdout.trim();
|
|
93022
|
+
if (text2 === "") return whenEmpty;
|
|
93023
|
+
try {
|
|
93024
|
+
return JSON.parse(text2);
|
|
93025
|
+
} catch {
|
|
93026
|
+
return null;
|
|
93027
|
+
}
|
|
93028
|
+
}
|
|
93029
|
+
|
|
93030
|
+
// src/pr/verdict.ts
|
|
93031
|
+
var PASSED = /* @__PURE__ */ new Set(["SUCCESS", "NEUTRAL"]);
|
|
93032
|
+
var SKIPPED = /* @__PURE__ */ new Set(["SKIPPED", "STALE"]);
|
|
93033
|
+
var FAILED = /* @__PURE__ */ new Set([
|
|
93034
|
+
"FAILURE",
|
|
93035
|
+
"TIMED_OUT",
|
|
93036
|
+
"CANCELLED",
|
|
93037
|
+
"ACTION_REQUIRED",
|
|
93038
|
+
"STARTUP_FAILURE",
|
|
93039
|
+
"ERROR"
|
|
93040
|
+
]);
|
|
93041
|
+
function groupOf(check3) {
|
|
93042
|
+
const status = check3.status.toUpperCase();
|
|
93043
|
+
if (status !== "COMPLETED" && status !== "SUCCESS" && status !== "FAILURE") {
|
|
93044
|
+
return "running";
|
|
93045
|
+
}
|
|
93046
|
+
const conclusion = check3.conclusion.toUpperCase();
|
|
93047
|
+
if (PASSED.has(conclusion)) return "passed";
|
|
93048
|
+
if (SKIPPED.has(conclusion)) return "skipped";
|
|
93049
|
+
if (FAILED.has(conclusion)) return "failed";
|
|
93050
|
+
return "running";
|
|
93051
|
+
}
|
|
93052
|
+
function countChecks(checks) {
|
|
93053
|
+
const counts = { failed: 0, running: 0, passed: 0, skipped: 0 };
|
|
93054
|
+
for (const check3 of checks) counts[groupOf(check3)] += 1;
|
|
93055
|
+
return counts;
|
|
93056
|
+
}
|
|
93057
|
+
function queuedCount(checks) {
|
|
93058
|
+
return checks.filter((check3) => {
|
|
93059
|
+
const status = check3.status.toUpperCase();
|
|
93060
|
+
return status === "QUEUED" || status === "WAITING" || status === "REQUESTED";
|
|
93061
|
+
}).length;
|
|
93062
|
+
}
|
|
93063
|
+
function namesOf(checks, group) {
|
|
93064
|
+
return checks.filter((check3) => groupOf(check3) === group).map((check3) => check3.name);
|
|
93065
|
+
}
|
|
93066
|
+
function changesRequestedBy(reviews) {
|
|
93067
|
+
return reviews.filter((review) => review.state.toUpperCase() === "CHANGES_REQUESTED").map((review) => review.author);
|
|
93068
|
+
}
|
|
93069
|
+
function approvedBy(reviews) {
|
|
93070
|
+
return reviews.filter((review) => review.state.toUpperCase() === "APPROVED").map((review) => review.author);
|
|
93071
|
+
}
|
|
93072
|
+
function decide2(pr) {
|
|
93073
|
+
const state = pr.state.toUpperCase();
|
|
93074
|
+
if (state === "MERGED") {
|
|
93075
|
+
return { verdict: "merged", reason: { kind: "merged", base: pr.baseRefName } };
|
|
93076
|
+
}
|
|
93077
|
+
if (state === "CLOSED") {
|
|
93078
|
+
return { verdict: "closed", reason: { kind: "closed" } };
|
|
93079
|
+
}
|
|
93080
|
+
const counts = countChecks(pr.checks);
|
|
93081
|
+
if (pr.isDraft) {
|
|
93082
|
+
return { verdict: "draft", reason: { kind: "draft", passed: counts.passed } };
|
|
93083
|
+
}
|
|
93084
|
+
const mergeable = pr.mergeable.toUpperCase();
|
|
93085
|
+
const mergeState = pr.mergeStateStatus.toUpperCase();
|
|
93086
|
+
if (mergeable === "CONFLICTING" || mergeState === "DIRTY") {
|
|
93087
|
+
return { verdict: "blocked", reason: { kind: "conflict", base: pr.baseRefName } };
|
|
93088
|
+
}
|
|
93089
|
+
if (counts.failed > 0) {
|
|
93090
|
+
return {
|
|
93091
|
+
verdict: "blocked",
|
|
93092
|
+
reason: { kind: "checks-failed", names: namesOf(pr.checks, "failed") }
|
|
93093
|
+
};
|
|
93094
|
+
}
|
|
93095
|
+
const requested = changesRequestedBy(pr.reviews);
|
|
93096
|
+
if (pr.reviewDecision.toUpperCase() === "CHANGES_REQUESTED" || requested.length > 0) {
|
|
93097
|
+
return { verdict: "blocked", reason: { kind: "changes-requested", by: requested } };
|
|
93098
|
+
}
|
|
93099
|
+
if (pr.reviewDecision.toUpperCase() === "REVIEW_REQUIRED") {
|
|
93100
|
+
return { verdict: "blocked", reason: { kind: "review-required" } };
|
|
93101
|
+
}
|
|
93102
|
+
if (mergeState === "BEHIND") {
|
|
93103
|
+
return { verdict: "blocked", reason: { kind: "behind", base: pr.baseRefName } };
|
|
93104
|
+
}
|
|
93105
|
+
if (counts.running > 0) {
|
|
93106
|
+
return {
|
|
93107
|
+
verdict: "pending",
|
|
93108
|
+
reason: {
|
|
93109
|
+
kind: "checks-running",
|
|
93110
|
+
names: namesOf(pr.checks, "running"),
|
|
93111
|
+
running: counts.running - queuedCount(pr.checks),
|
|
93112
|
+
queued: queuedCount(pr.checks)
|
|
93113
|
+
}
|
|
93114
|
+
};
|
|
93115
|
+
}
|
|
93116
|
+
if (mergeState === "BLOCKED") {
|
|
93117
|
+
return { verdict: "blocked", reason: { kind: "blocked-by-host" } };
|
|
93118
|
+
}
|
|
93119
|
+
if (mergeable !== "MERGEABLE") {
|
|
93120
|
+
return { verdict: "pending", reason: { kind: "mergeability-unknown" } };
|
|
93121
|
+
}
|
|
93122
|
+
if (mergeState !== "CLEAN" && mergeState !== "UNSTABLE" && mergeState !== "HAS_HOOKS") {
|
|
93123
|
+
return { verdict: "pending", reason: { kind: "mergeability-unknown" } };
|
|
93124
|
+
}
|
|
93125
|
+
return {
|
|
93126
|
+
verdict: "ready",
|
|
93127
|
+
reason: { kind: "ready", passed: counts.passed, approvedBy: approvedBy(pr.reviews) }
|
|
93128
|
+
};
|
|
93129
|
+
}
|
|
93130
|
+
|
|
93131
|
+
// src/pr/PrCache.ts
|
|
93132
|
+
var TTL_BUSY_MS = 15e3;
|
|
93133
|
+
var TTL_IDLE_MS = 6e4;
|
|
93134
|
+
var BACKOFF_START_MS = TTL_IDLE_MS;
|
|
93135
|
+
var BACKOFF_MAX_MS = 6e5;
|
|
93136
|
+
function createPrCache({
|
|
93137
|
+
host,
|
|
93138
|
+
now = () => Date.now(),
|
|
93139
|
+
onChange
|
|
93140
|
+
}) {
|
|
93141
|
+
const slots = /* @__PURE__ */ new Map();
|
|
93142
|
+
let reads = 0;
|
|
93143
|
+
function slotOf(projectId) {
|
|
93144
|
+
const existing = slots.get(projectId);
|
|
93145
|
+
if (existing) return existing;
|
|
93146
|
+
const fresh = {
|
|
93147
|
+
snapshot: null,
|
|
93148
|
+
failure: null,
|
|
93149
|
+
readAt: null,
|
|
93150
|
+
freshUntil: 0,
|
|
93151
|
+
failures: 0,
|
|
93152
|
+
inFlight: null,
|
|
93153
|
+
want: 0,
|
|
93154
|
+
served: 0
|
|
93155
|
+
};
|
|
93156
|
+
slots.set(projectId, fresh);
|
|
93157
|
+
return fresh;
|
|
93158
|
+
}
|
|
93159
|
+
function viewOf2(slot) {
|
|
93160
|
+
return { snapshot: slot.snapshot, failure: slot.failure, readAt: slot.readAt };
|
|
93161
|
+
}
|
|
93162
|
+
function ttlOf(slot) {
|
|
93163
|
+
if (slot.failures > 0) {
|
|
93164
|
+
return Math.min(BACKOFF_START_MS * 2 ** (slot.failures - 1), BACKOFF_MAX_MS);
|
|
93165
|
+
}
|
|
93166
|
+
const pulls = slot.snapshot?.pulls ?? [];
|
|
93167
|
+
const busy = pulls.some(
|
|
93168
|
+
(pull) => pull.state.toUpperCase() === "OPEN" && countChecks(pull.checks).running > 0
|
|
93169
|
+
);
|
|
93170
|
+
return busy ? TTL_BUSY_MS : TTL_IDLE_MS;
|
|
93171
|
+
}
|
|
93172
|
+
async function fetch(project2, slot, want) {
|
|
93173
|
+
reads += 1;
|
|
93174
|
+
try {
|
|
93175
|
+
const read = await host.read({ repoPath: project2.path, remoteUrl: project2.remoteUrl });
|
|
93176
|
+
if (read.ok) {
|
|
93177
|
+
const changed = digestOf(slot.snapshot) !== digestOf(read.snapshot);
|
|
93178
|
+
slot.snapshot = read.snapshot;
|
|
93179
|
+
slot.failure = null;
|
|
93180
|
+
slot.readAt = read.snapshot.readAt;
|
|
93181
|
+
slot.failures = 0;
|
|
93182
|
+
if (changed) onChange?.(project2.id);
|
|
93183
|
+
} else {
|
|
93184
|
+
slot.failure = read.failure;
|
|
93185
|
+
slot.failures += 1;
|
|
93186
|
+
}
|
|
93187
|
+
} catch {
|
|
93188
|
+
slot.failure = { kind: "failed", message: "n\xE3o deu para consultar o host" };
|
|
93189
|
+
slot.failures += 1;
|
|
93190
|
+
} finally {
|
|
93191
|
+
slot.freshUntil = now() + ttlOf(slot);
|
|
93192
|
+
slot.served = Math.max(slot.served, want);
|
|
93193
|
+
}
|
|
93194
|
+
return viewOf2(slot);
|
|
93195
|
+
}
|
|
93196
|
+
function start(project2, slot) {
|
|
93197
|
+
const current = slot.inFlight;
|
|
93198
|
+
if (current !== null) {
|
|
93199
|
+
if (current.want >= slot.want) return current.promise;
|
|
93200
|
+
return current.promise.then(() => start(project2, slot));
|
|
93201
|
+
}
|
|
93202
|
+
const holder = { want: slot.want, promise: Promise.resolve(viewOf2(slot)) };
|
|
93203
|
+
holder.promise = fetch(project2, slot, holder.want).finally(() => {
|
|
93204
|
+
if (slot.inFlight === holder) slot.inFlight = null;
|
|
93205
|
+
});
|
|
93206
|
+
slot.inFlight = holder;
|
|
93207
|
+
return holder.promise;
|
|
93208
|
+
}
|
|
93209
|
+
return {
|
|
93210
|
+
get(project2) {
|
|
93211
|
+
const slot = slotOf(project2.id);
|
|
93212
|
+
const known = slot.snapshot !== null || slot.failure !== null;
|
|
93213
|
+
if (!known || slot.want > slot.served) return start(project2, slot);
|
|
93214
|
+
if (now() < slot.freshUntil) return Promise.resolve(viewOf2(slot));
|
|
93215
|
+
void start(project2, slot).catch(() => void 0);
|
|
93216
|
+
return Promise.resolve(viewOf2(slot));
|
|
93217
|
+
},
|
|
93218
|
+
invalidate(projectId) {
|
|
93219
|
+
const slot = slots.get(projectId);
|
|
93220
|
+
if (!slot) return;
|
|
93221
|
+
slot.freshUntil = 0;
|
|
93222
|
+
slot.want += 1;
|
|
93223
|
+
slot.failures = 0;
|
|
93224
|
+
},
|
|
93225
|
+
forget(projectId) {
|
|
93226
|
+
slots.delete(projectId);
|
|
93227
|
+
},
|
|
93228
|
+
get reads() {
|
|
93229
|
+
return reads;
|
|
93230
|
+
}
|
|
93231
|
+
};
|
|
93232
|
+
}
|
|
93233
|
+
function digestOf(snapshot) {
|
|
93234
|
+
if (snapshot === null) return "";
|
|
93235
|
+
const { readAt: _ignored, ...rest } = snapshot;
|
|
93236
|
+
return JSON.stringify(rest);
|
|
93237
|
+
}
|
|
93238
|
+
|
|
92054
93239
|
// ../../node_modules/.pnpm/ws@8.21.3/node_modules/ws/wrapper.mjs
|
|
92055
93240
|
var import_stream2 = __toESM(require_stream(), 1);
|
|
92056
93241
|
var import_extension = __toESM(require_extension(), 1);
|
|
@@ -92143,6 +93328,13 @@ function registerAcpWebSocket({
|
|
|
92143
93328
|
// with empty dropdowns until the agent happened to mention something would
|
|
92144
93329
|
// look broken for as long as nothing did.
|
|
92145
93330
|
configOptions: [...info.configOptions],
|
|
93331
|
+
// Who owns the mode selector, and what the policy is set to — on attach for
|
|
93332
|
+
// the same reason `configOptions` is: a composer whose mode pill appeared
|
|
93333
|
+
// only once something changed would look broken for as long as nothing did.
|
|
93334
|
+
modeOwner: modeOwnerOf(info),
|
|
93335
|
+
lumemMode: info.lumemMode,
|
|
93336
|
+
lumemModeDefault: info.lumemModeDefault,
|
|
93337
|
+
cwd: info.cwd,
|
|
92146
93338
|
transcript: [...acpManager.transcript(sessionId)]
|
|
92147
93339
|
});
|
|
92148
93340
|
const offEvent = acpManager.onEvent(
|
|
@@ -92177,6 +93369,10 @@ function registerAcpWebSocket({
|
|
|
92177
93369
|
});
|
|
92178
93370
|
return;
|
|
92179
93371
|
}
|
|
93372
|
+
if (message.type === "set_lumem_mode") {
|
|
93373
|
+
acpManager.setLumemMode(sessionId, message.mode);
|
|
93374
|
+
return;
|
|
93375
|
+
}
|
|
92180
93376
|
acpManager.respondToPermission(sessionId, message.requestId, message.optionId);
|
|
92181
93377
|
} catch (error40) {
|
|
92182
93378
|
reportFailure(error40, message.type);
|
|
@@ -92205,8 +93401,8 @@ function registerAcpWebSocket({
|
|
|
92205
93401
|
});
|
|
92206
93402
|
app.addHook("onClose", async () => {
|
|
92207
93403
|
for (const client2 of wss.clients) client2.close(CLOSE_GOING_AWAY, "daemon shutting down");
|
|
92208
|
-
await new Promise((
|
|
92209
|
-
wss.close(() =>
|
|
93404
|
+
await new Promise((resolve6) => {
|
|
93405
|
+
wss.close(() => resolve6());
|
|
92210
93406
|
});
|
|
92211
93407
|
});
|
|
92212
93408
|
}
|
|
@@ -92599,8 +93795,8 @@ function registerPtyWebSocket({
|
|
|
92599
93795
|
});
|
|
92600
93796
|
app.addHook("onClose", async () => {
|
|
92601
93797
|
for (const client2 of wss.clients) client2.close(CLOSE_GOING_AWAY2, "daemon shutting down");
|
|
92602
|
-
await new Promise((
|
|
92603
|
-
wss.close(() =>
|
|
93798
|
+
await new Promise((resolve6) => {
|
|
93799
|
+
wss.close(() => resolve6());
|
|
92604
93800
|
});
|
|
92605
93801
|
});
|
|
92606
93802
|
}
|
|
@@ -92608,12 +93804,12 @@ function registerPtyWebSocket({
|
|
|
92608
93804
|
// src/web/static.ts
|
|
92609
93805
|
var import_static = __toESM(require_static(), 1);
|
|
92610
93806
|
import { existsSync as existsSync4 } from "node:fs";
|
|
92611
|
-
import { join as
|
|
93807
|
+
import { join as join16 } from "node:path";
|
|
92612
93808
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
92613
93809
|
var DAEMON_PREFIXES = ["/trpc", "/pty", "/acp", "/memory"];
|
|
92614
93810
|
function resolveWebRoot(override = null) {
|
|
92615
93811
|
const candidate = override ?? fileURLToPath2(new URL("../web/", import.meta.url));
|
|
92616
|
-
return existsSync4(
|
|
93812
|
+
return existsSync4(join16(candidate, "index.html")) ? candidate : null;
|
|
92617
93813
|
}
|
|
92618
93814
|
var IMMUTABLE = "public, max-age=31536000, immutable";
|
|
92619
93815
|
var REVALIDATE = "no-cache";
|
|
@@ -93276,14 +94472,305 @@ var memoryRouter = router({
|
|
|
93276
94472
|
)
|
|
93277
94473
|
});
|
|
93278
94474
|
|
|
94475
|
+
// src/pr/view.ts
|
|
94476
|
+
var GROUP_ORDER = { failed: 0, running: 1, passed: 2, skipped: 3 };
|
|
94477
|
+
function durationOf(check3) {
|
|
94478
|
+
if (check3.startedAt === null) return null;
|
|
94479
|
+
const start = Date.parse(check3.startedAt);
|
|
94480
|
+
const end = check3.completedAt === null ? Date.now() : Date.parse(check3.completedAt);
|
|
94481
|
+
if (Number.isNaN(start) || Number.isNaN(end)) return null;
|
|
94482
|
+
return Math.max(0, end - start);
|
|
94483
|
+
}
|
|
94484
|
+
function checkViewOf(check3, host) {
|
|
94485
|
+
return {
|
|
94486
|
+
name: check3.name,
|
|
94487
|
+
app: check3.app,
|
|
94488
|
+
group: groupOf(check3),
|
|
94489
|
+
url: safeUrl(check3.url, host),
|
|
94490
|
+
durationMs: durationOf(check3)
|
|
94491
|
+
};
|
|
94492
|
+
}
|
|
94493
|
+
function viewOf({ pull, host, alsoOpen }) {
|
|
94494
|
+
const { verdict, reason } = decide2(pull);
|
|
94495
|
+
const checks = pull.checks.map((check3) => checkViewOf(check3, host)).sort((a, b) => GROUP_ORDER[a.group] - GROUP_ORDER[b.group]);
|
|
94496
|
+
return {
|
|
94497
|
+
number: pull.number,
|
|
94498
|
+
url: safeUrl(pull.url, host),
|
|
94499
|
+
title: pull.title,
|
|
94500
|
+
verdict,
|
|
94501
|
+
reason,
|
|
94502
|
+
counts: countChecks(pull.checks),
|
|
94503
|
+
checks,
|
|
94504
|
+
base: pull.baseRefName,
|
|
94505
|
+
head: pull.headRefName,
|
|
94506
|
+
updatedAt: pull.updatedAt,
|
|
94507
|
+
author: pull.author,
|
|
94508
|
+
alsoOpen
|
|
94509
|
+
};
|
|
94510
|
+
}
|
|
94511
|
+
var CLOSED_PR_MAX_AGE_DAYS = 30;
|
|
94512
|
+
function pickForBranch(pulls, branch, now = Date.now()) {
|
|
94513
|
+
const mine = pulls.filter((pull) => pull.headRefName === branch);
|
|
94514
|
+
if (mine.length === 0) return null;
|
|
94515
|
+
const open = mine.filter((pull) => pull.state.toUpperCase() === "OPEN");
|
|
94516
|
+
const recent = (pull) => {
|
|
94517
|
+
const at = Date.parse(pull.updatedAt);
|
|
94518
|
+
if (Number.isNaN(at)) return false;
|
|
94519
|
+
return now - at <= CLOSED_PR_MAX_AGE_DAYS * 24 * 60 * 60 * 1e3;
|
|
94520
|
+
};
|
|
94521
|
+
const byUpdated = (a, b) => Date.parse(b.updatedAt) - Date.parse(a.updatedAt);
|
|
94522
|
+
if (open.length > 0) {
|
|
94523
|
+
const [first2] = [...open].sort(byUpdated);
|
|
94524
|
+
return { pull: first2, alsoOpen: open.length - 1 };
|
|
94525
|
+
}
|
|
94526
|
+
const closed = mine.filter(recent).sort(byUpdated);
|
|
94527
|
+
const [first] = closed;
|
|
94528
|
+
return first === void 0 ? null : { pull: first, alsoOpen: 0 };
|
|
94529
|
+
}
|
|
94530
|
+
|
|
94531
|
+
// src/routers/pr.ts
|
|
94532
|
+
var worktreeInput = external_exports.object({ worktreeId: external_exports.string().min(1) });
|
|
94533
|
+
var projectInput = external_exports.object({ projectId: external_exports.string().min(1) });
|
|
94534
|
+
var strategySchema = external_exports.enum(["merge", "squash", "rebase"]);
|
|
94535
|
+
async function remoteOf(ctx, project2) {
|
|
94536
|
+
return project2.remoteUrl ?? await ctx.git.getRemoteUrl(project2.path);
|
|
94537
|
+
}
|
|
94538
|
+
async function checkoutOf(ctx, worktreeId) {
|
|
94539
|
+
const worktree2 = await createWorktreeRepository(ctx.db).findById(worktreeId);
|
|
94540
|
+
if (!worktree2) throw new DomainError("NOT_FOUND", `worktree ${worktreeId} n\xE3o existe`);
|
|
94541
|
+
if (worktree2.state === "missing") {
|
|
94542
|
+
throw new DomainError("BLOCKED", `a worktree "${worktree2.name}" n\xE3o est\xE1 no disco`);
|
|
94543
|
+
}
|
|
94544
|
+
const project2 = await createProjectRepository(ctx.db).findById(worktree2.projectId);
|
|
94545
|
+
if (!project2) {
|
|
94546
|
+
throw new DomainError("NOT_FOUND", `projeto ${worktree2.projectId} n\xE3o existe`);
|
|
94547
|
+
}
|
|
94548
|
+
return {
|
|
94549
|
+
project: { id: project2.id, path: project2.path, remoteUrl: await remoteOf(ctx, project2) },
|
|
94550
|
+
branch: worktree2.branch,
|
|
94551
|
+
base: project2.defaultBranch,
|
|
94552
|
+
cwd: worktree2.path
|
|
94553
|
+
};
|
|
94554
|
+
}
|
|
94555
|
+
function failureViewOf(entry) {
|
|
94556
|
+
if (entry.failure === null) return null;
|
|
94557
|
+
return {
|
|
94558
|
+
kind: entry.failure.kind,
|
|
94559
|
+
message: entry.failure.message,
|
|
94560
|
+
retryAt: entry.failure.retryAt ?? null
|
|
94561
|
+
};
|
|
94562
|
+
}
|
|
94563
|
+
var NO_MERGE_OPTIONS = {
|
|
94564
|
+
merge: false,
|
|
94565
|
+
squash: false,
|
|
94566
|
+
rebase: false,
|
|
94567
|
+
deleteBranchOnMerge: false
|
|
94568
|
+
};
|
|
94569
|
+
var prRouter = router({
|
|
94570
|
+
/**
|
|
94571
|
+
* A barra, inteira, numa leitura só.
|
|
94572
|
+
*
|
|
94573
|
+
* Ela precisa de sete coisas que vêm de três lugares — o host, o git local e
|
|
94574
|
+
* o banco —, e pedi-las em três chamadas daria três respostas de instantes
|
|
94575
|
+
* diferentes na mesma barra.
|
|
94576
|
+
*/
|
|
94577
|
+
getByWorktree: publicProcedure.input(worktreeInput).query(
|
|
94578
|
+
({ ctx, input }) => domainSafeAsync(async () => {
|
|
94579
|
+
const checkout = await checkoutOf(ctx, input.worktreeId);
|
|
94580
|
+
const [entry, published] = await Promise.all([
|
|
94581
|
+
ctx.pr.get(checkout.project),
|
|
94582
|
+
// Barato e local: lê a referência de rastreamento do disco. É o que
|
|
94583
|
+
// separa "sem pull request" de "branch não publicada" — as duas neutras,
|
|
94584
|
+
// e dizer a errada manda a pessoa procurar uma PR que não podia existir.
|
|
94585
|
+
ctx.git.hasRemoteBranch(checkout.cwd, checkout.branch)
|
|
94586
|
+
]);
|
|
94587
|
+
const snapshot = entry.snapshot;
|
|
94588
|
+
const host = snapshot?.host ?? null;
|
|
94589
|
+
const picked = snapshot === null ? null : pickForBranch(snapshot.pulls, checkout.branch);
|
|
94590
|
+
return {
|
|
94591
|
+
pull: picked === null ? null : viewOf({ pull: picked.pull, host, alsoOpen: picked.alsoOpen }),
|
|
94592
|
+
failure: failureViewOf(entry),
|
|
94593
|
+
readAt: entry.readAt,
|
|
94594
|
+
host,
|
|
94595
|
+
branch: checkout.branch,
|
|
94596
|
+
base: checkout.base,
|
|
94597
|
+
published,
|
|
94598
|
+
compareUrl: snapshot === null ? null : compareUrl({
|
|
94599
|
+
host,
|
|
94600
|
+
repo: snapshot.repo,
|
|
94601
|
+
base: checkout.base,
|
|
94602
|
+
head: checkout.branch
|
|
94603
|
+
}),
|
|
94604
|
+
merge: snapshot?.merge ?? NO_MERGE_OPTIONS
|
|
94605
|
+
};
|
|
94606
|
+
})
|
|
94607
|
+
),
|
|
94608
|
+
/**
|
|
94609
|
+
* Os marcadores da sidebar, num pedido só.
|
|
94610
|
+
*
|
|
94611
|
+
* F3.1 e F3.3: N worktrees não fazem N consultas, e a cor sai do **mesmo**
|
|
94612
|
+
* veredito da barra. Worktree sem PR não entra na lista — marcador cinza em
|
|
94613
|
+
* cinco linhas ensina o olho a ignorar a coluna inteira (Q9).
|
|
94614
|
+
*/
|
|
94615
|
+
listByProject: publicProcedure.input(projectInput).query(
|
|
94616
|
+
({ ctx, input }) => domainSafeAsync(async () => {
|
|
94617
|
+
const project2 = await createProjectRepository(ctx.db).findById(input.projectId);
|
|
94618
|
+
if (!project2) throw new DomainError("NOT_FOUND", `projeto ${input.projectId} n\xE3o existe`);
|
|
94619
|
+
const entry = await ctx.pr.get({
|
|
94620
|
+
id: project2.id,
|
|
94621
|
+
path: project2.path,
|
|
94622
|
+
remoteUrl: await remoteOf(ctx, project2)
|
|
94623
|
+
});
|
|
94624
|
+
const snapshot = entry.snapshot;
|
|
94625
|
+
if (snapshot === null) return [];
|
|
94626
|
+
const worktrees = await createWorktreeRepository(ctx.db).listByProject(project2.id);
|
|
94627
|
+
return worktrees.flatMap((worktree2) => {
|
|
94628
|
+
const picked = pickForBranch(snapshot.pulls, worktree2.branch);
|
|
94629
|
+
if (picked === null) return [];
|
|
94630
|
+
const view = viewOf({ pull: picked.pull, host: snapshot.host, alsoOpen: picked.alsoOpen });
|
|
94631
|
+
return [{ worktreeId: worktree2.id, number: view.number, verdict: view.verdict }];
|
|
94632
|
+
});
|
|
94633
|
+
})
|
|
94634
|
+
),
|
|
94635
|
+
/**
|
|
94636
|
+
* F7 — mesclar.
|
|
94637
|
+
*
|
|
94638
|
+
* O portão é o veredito, e ele é **relido aqui** (F7.2). Recusar só na tela
|
|
94639
|
+
* seria conforto: quem garante é a procedure, e o teste que a prova chama
|
|
94640
|
+
* daqui, sem passar pelo botão.
|
|
94641
|
+
*/
|
|
94642
|
+
merge: publicProcedure.input(worktreeInput.extend({ strategy: strategySchema, deleteBranch: external_exports.boolean() })).mutation(
|
|
94643
|
+
({ ctx, input }) => domainSafeAsync(async () => {
|
|
94644
|
+
const checkout = await checkoutOf(ctx, input.worktreeId);
|
|
94645
|
+
ctx.pr.invalidate(checkout.project.id);
|
|
94646
|
+
const entry = await ctx.pr.get(checkout.project);
|
|
94647
|
+
const snapshot = entry.snapshot;
|
|
94648
|
+
if (entry.failure !== null) {
|
|
94649
|
+
throw new DomainError(
|
|
94650
|
+
"BLOCKED",
|
|
94651
|
+
`n\xE3o deu para confirmar o estado da pull request: ${entry.failure.message}`
|
|
94652
|
+
);
|
|
94653
|
+
}
|
|
94654
|
+
if (snapshot === null) {
|
|
94655
|
+
throw new DomainError("BLOCKED", "n\xE3o deu para ler o estado da pull request");
|
|
94656
|
+
}
|
|
94657
|
+
const picked = pickForBranch(snapshot.pulls, checkout.branch);
|
|
94658
|
+
if (picked === null) {
|
|
94659
|
+
throw new DomainError("NOT_FOUND", "esta worktree n\xE3o tem pull request");
|
|
94660
|
+
}
|
|
94661
|
+
const view = viewOf({ pull: picked.pull, host: snapshot.host, alsoOpen: picked.alsoOpen });
|
|
94662
|
+
if (view.verdict !== "ready") {
|
|
94663
|
+
throw new DomainError(
|
|
94664
|
+
"BLOCKED",
|
|
94665
|
+
`a pull request #${String(view.number)} n\xE3o est\xE1 pronta para merge`
|
|
94666
|
+
);
|
|
94667
|
+
}
|
|
94668
|
+
if (!allows(snapshot.merge, input.strategy)) {
|
|
94669
|
+
throw new DomainError(
|
|
94670
|
+
"BLOCKED",
|
|
94671
|
+
`o reposit\xF3rio n\xE3o permite merge por ${input.strategy}`
|
|
94672
|
+
);
|
|
94673
|
+
}
|
|
94674
|
+
const write = await ctx.prHost.merge({
|
|
94675
|
+
repoPath: checkout.cwd,
|
|
94676
|
+
remoteUrl: checkout.project.remoteUrl,
|
|
94677
|
+
// Do cache do daemon, **nunca** do cliente (§4.2.12 do PRD).
|
|
94678
|
+
number: view.number,
|
|
94679
|
+
strategy: input.strategy,
|
|
94680
|
+
deleteBranch: input.deleteBranch
|
|
94681
|
+
});
|
|
94682
|
+
if (!write.ok) throw new DomainError("GIT_FAILED", write.failure.message);
|
|
94683
|
+
ctx.pr.invalidate(checkout.project.id);
|
|
94684
|
+
ctx.events.emit({ type: "worktree.changed", projectId: checkout.project.id });
|
|
94685
|
+
return { number: view.number };
|
|
94686
|
+
})
|
|
94687
|
+
),
|
|
94688
|
+
/**
|
|
94689
|
+
* O que o formulário propõe, e por que ele não é um campo do `PrStatus`.
|
|
94690
|
+
*
|
|
94691
|
+
* A [Q4](../../../../docs/prd/pull-request-status/open-questions.md) e a F7.6
|
|
94692
|
+
* pedem título vindo do assunto do último commit. Isso custa um `git log`, e
|
|
94693
|
+
* só interessa quando o formulário abre — dentro do `PrStatus` seria um
|
|
94694
|
+
* processo git a cada ciclo de poll, por worktree aberta.
|
|
94695
|
+
*/
|
|
94696
|
+
draft: publicProcedure.input(worktreeInput).query(
|
|
94697
|
+
({ ctx, input }) => domainSafeAsync(async () => {
|
|
94698
|
+
const checkout = await checkoutOf(ctx, input.worktreeId);
|
|
94699
|
+
return {
|
|
94700
|
+
title: await ctx.git.getSubject(checkout.cwd) ?? "",
|
|
94701
|
+
base: checkout.base,
|
|
94702
|
+
head: checkout.branch
|
|
94703
|
+
};
|
|
94704
|
+
})
|
|
94705
|
+
),
|
|
94706
|
+
/**
|
|
94707
|
+
* F7 — criar.
|
|
94708
|
+
*
|
|
94709
|
+
* Título e corpo vêm da tela, que é o que o §4.2 do PRD existe para aguentar.
|
|
94710
|
+
* A base e a head **não** vêm: elas saem do banco e do git local.
|
|
94711
|
+
*/
|
|
94712
|
+
create: publicProcedure.input(
|
|
94713
|
+
worktreeInput.extend({
|
|
94714
|
+
title: external_exports.string().min(1).max(256),
|
|
94715
|
+
body: external_exports.string().max(6e4),
|
|
94716
|
+
draft: external_exports.boolean()
|
|
94717
|
+
})
|
|
94718
|
+
).mutation(
|
|
94719
|
+
({ ctx, input }) => domainSafeAsync(async () => {
|
|
94720
|
+
const checkout = await checkoutOf(ctx, input.worktreeId);
|
|
94721
|
+
const published = await ctx.git.hasRemoteBranch(checkout.cwd, checkout.branch);
|
|
94722
|
+
if (!published) {
|
|
94723
|
+
throw new DomainError(
|
|
94724
|
+
"BLOCKED",
|
|
94725
|
+
`a branch "${checkout.branch}" ainda n\xE3o foi publicada`
|
|
94726
|
+
);
|
|
94727
|
+
}
|
|
94728
|
+
const write = await ctx.prHost.create({
|
|
94729
|
+
repoPath: checkout.cwd,
|
|
94730
|
+
remoteUrl: checkout.project.remoteUrl,
|
|
94731
|
+
base: checkout.base,
|
|
94732
|
+
head: checkout.branch,
|
|
94733
|
+
title: input.title,
|
|
94734
|
+
body: input.body,
|
|
94735
|
+
draft: input.draft
|
|
94736
|
+
});
|
|
94737
|
+
if (!write.ok) throw new DomainError("GIT_FAILED", write.failure.message);
|
|
94738
|
+
ctx.pr.invalidate(checkout.project.id);
|
|
94739
|
+
ctx.events.emit({ type: "worktree.changed", projectId: checkout.project.id });
|
|
94740
|
+
return { url: write.url };
|
|
94741
|
+
})
|
|
94742
|
+
),
|
|
94743
|
+
/**
|
|
94744
|
+
* "Tentar de novo", e o `⟳` da coluna. Não escreve nada; só invalida.
|
|
94745
|
+
*
|
|
94746
|
+
* A entrada é o **escopo**, e não o id do projeto, porque o escopo é o que o
|
|
94747
|
+
* cliente sabe **na hora do clique** — o projeto de uma worktree chega uma
|
|
94748
|
+
* consulta depois. Pedir o projeto fazia o botão não fazer nada quando o
|
|
94749
|
+
* clique vinha cedo, que é exatamente quando alguém clica em recarregar.
|
|
94750
|
+
*/
|
|
94751
|
+
refresh: publicProcedure.input(external_exports.object({ scopeType: external_exports.enum(["project", "worktree"]), scopeId: external_exports.string().min(1) })).mutation(
|
|
94752
|
+
({ ctx, input }) => domainSafeAsync(async () => {
|
|
94753
|
+
const projectId = input.scopeType === "project" ? input.scopeId : (await createWorktreeRepository(ctx.db).findById(input.scopeId))?.projectId ?? null;
|
|
94754
|
+
if (projectId === null) {
|
|
94755
|
+
throw new DomainError("NOT_FOUND", `worktree ${input.scopeId} n\xE3o existe`);
|
|
94756
|
+
}
|
|
94757
|
+
ctx.pr.invalidate(projectId);
|
|
94758
|
+
return { ok: true };
|
|
94759
|
+
})
|
|
94760
|
+
)
|
|
94761
|
+
});
|
|
94762
|
+
function allows(options, strategy) {
|
|
94763
|
+
return options[strategy];
|
|
94764
|
+
}
|
|
94765
|
+
|
|
93279
94766
|
// src/routers/project.ts
|
|
93280
94767
|
import { existsSync as existsSync5 } from "node:fs";
|
|
93281
|
-
import { rename as rename4, rm as
|
|
93282
|
-
import { basename as
|
|
94768
|
+
import { rename as rename4, rm as rm7 } from "node:fs/promises";
|
|
94769
|
+
import { basename as basename4, dirname as dirname11, isAbsolute as isAbsolute6, normalize as normalize4 } from "node:path";
|
|
93283
94770
|
|
|
93284
94771
|
// src/git/clone.ts
|
|
93285
94772
|
import { spawn as spawn2 } from "node:child_process";
|
|
93286
|
-
import { rename as rename3, rm as
|
|
94773
|
+
import { rename as rename3, rm as rm5 } from "node:fs/promises";
|
|
93287
94774
|
var CloneError = class extends DomainError {
|
|
93288
94775
|
constructor(failure, message, options) {
|
|
93289
94776
|
super(failure === "internal" ? "BLOCKED" : "GIT_FAILED", message, options);
|
|
@@ -93304,7 +94791,7 @@ async function cloneRepository(options) {
|
|
|
93304
94791
|
await rename3(tempPath, finalTarget);
|
|
93305
94792
|
return finalTarget;
|
|
93306
94793
|
} catch (error40) {
|
|
93307
|
-
await
|
|
94794
|
+
await rm5(tempPath, { recursive: true, force: true }).catch(() => {
|
|
93308
94795
|
});
|
|
93309
94796
|
throw error40;
|
|
93310
94797
|
}
|
|
@@ -93348,7 +94835,7 @@ function runClone({
|
|
|
93348
94835
|
silenceMs = DEFAULT_SILENCE_MS,
|
|
93349
94836
|
env
|
|
93350
94837
|
}) {
|
|
93351
|
-
return new Promise((
|
|
94838
|
+
return new Promise((resolve6, reject) => {
|
|
93352
94839
|
const child = spawn2("git", cloneArgs(rawUrl, tempPath), {
|
|
93353
94840
|
env: cloneEnv(env),
|
|
93354
94841
|
stdio: ["ignore", "ignore", "pipe"]
|
|
@@ -93371,7 +94858,7 @@ function runClone({
|
|
|
93371
94858
|
silence.stop();
|
|
93372
94859
|
signal?.removeEventListener("abort", onAbort);
|
|
93373
94860
|
if (error40) reject(error40);
|
|
93374
|
-
else
|
|
94861
|
+
else resolve6();
|
|
93375
94862
|
}
|
|
93376
94863
|
function onAbort() {
|
|
93377
94864
|
finish(new CloneError("internal", "clone cancelado"));
|
|
@@ -93402,7 +94889,7 @@ function runClone({
|
|
|
93402
94889
|
if (killTimer) clearTimeout(killTimer);
|
|
93403
94890
|
if (code === 0) return finish();
|
|
93404
94891
|
const stderr = errors.text();
|
|
93405
|
-
finish(new CloneError(
|
|
94892
|
+
finish(new CloneError(classify2(stderr), lastMeaningfulLine(stderr) || `git saiu com ${code}`));
|
|
93406
94893
|
});
|
|
93407
94894
|
});
|
|
93408
94895
|
}
|
|
@@ -93410,7 +94897,7 @@ async function rewriteOrigin({ rawUrl, url: url2, tempPath }) {
|
|
|
93410
94897
|
if (rawUrl === url2) return;
|
|
93411
94898
|
await execGit(["remote", "set-url", "origin", url2], { cwd: tempPath });
|
|
93412
94899
|
}
|
|
93413
|
-
function
|
|
94900
|
+
function classify2(stderr) {
|
|
93414
94901
|
if (/authentication failed|could not read username|could not read password|invalid username or password|permission denied \(publickey|terminal prompts disabled|host key verification failed/i.test(
|
|
93415
94902
|
stderr
|
|
93416
94903
|
)) {
|
|
@@ -93509,134 +94996,7 @@ var SilenceTimer = class {
|
|
|
93509
94996
|
|
|
93510
94997
|
// src/git/clone-plan.ts
|
|
93511
94998
|
import { homedir } from "node:os";
|
|
93512
|
-
import { dirname as dirname9, join as
|
|
93513
|
-
|
|
93514
|
-
// src/git/git-url.ts
|
|
93515
|
-
var ALLOWED = /* @__PURE__ */ new Set(["https", "http", "ssh", "file"]);
|
|
93516
|
-
var HELPER = /^([A-Za-z][A-Za-z0-9+.-]*)::/;
|
|
93517
|
-
var SCHEME = /^([A-Za-z][A-Za-z0-9+.-]*):\/\//;
|
|
93518
|
-
var CONTROL = /[\u0000-\u001f\u007f]/;
|
|
93519
|
-
function refuse(rule, message) {
|
|
93520
|
-
return { ok: false, rule, message };
|
|
93521
|
-
}
|
|
93522
|
-
function refuseScheme(scheme) {
|
|
93523
|
-
if (scheme === "git") {
|
|
93524
|
-
return refuse(
|
|
93525
|
-
"scheme",
|
|
93526
|
-
"o transporte git:// n\xE3o \xE9 aceito: ele n\xE3o autentica nem verifica integridade \u2014 use a URL https ou ssh do mesmo reposit\xF3rio"
|
|
93527
|
-
);
|
|
93528
|
-
}
|
|
93529
|
-
return refuse(
|
|
93530
|
-
"scheme",
|
|
93531
|
-
`o transporte "${scheme}" n\xE3o est\xE1 na lista de transportes aceitos (https, http, ssh, file)`
|
|
93532
|
-
);
|
|
93533
|
-
}
|
|
93534
|
-
function parseGitUrl(input) {
|
|
93535
|
-
const raw = input.trim();
|
|
93536
|
-
if (raw === "") return refuse("empty", "informe a URL do reposit\xF3rio");
|
|
93537
|
-
if (CONTROL.test(raw)) {
|
|
93538
|
-
return refuse("control-chars", "a URL tem caracteres de controle");
|
|
93539
|
-
}
|
|
93540
|
-
if (raw.startsWith("-")) {
|
|
93541
|
-
return refuse("leading-dash", "a URL n\xE3o pode come\xE7ar com '-'");
|
|
93542
|
-
}
|
|
93543
|
-
const helper = HELPER.exec(raw);
|
|
93544
|
-
if (helper) return refuseScheme(helper[1].toLowerCase());
|
|
93545
|
-
const scheme = SCHEME.exec(raw);
|
|
93546
|
-
if (scheme) return parseWithScheme(raw, scheme[1].toLowerCase());
|
|
93547
|
-
if (isScp(raw)) return parseScp(raw);
|
|
93548
|
-
return refuse(
|
|
93549
|
-
"scheme",
|
|
93550
|
-
"n\xE3o reconheci isto como URL git: use https://, ssh://, file:// ou a forma git@host:org/repo.git"
|
|
93551
|
-
);
|
|
93552
|
-
}
|
|
93553
|
-
function scpColon(raw) {
|
|
93554
|
-
const at = raw.indexOf("@");
|
|
93555
|
-
return raw.indexOf(":", at === -1 ? 0 : at + 1);
|
|
93556
|
-
}
|
|
93557
|
-
function isScp(raw) {
|
|
93558
|
-
const colon = scpColon(raw);
|
|
93559
|
-
if (colon <= 0) return false;
|
|
93560
|
-
const slash = raw.indexOf("/");
|
|
93561
|
-
return slash === -1 || colon < slash;
|
|
93562
|
-
}
|
|
93563
|
-
function parseWithScheme(raw, scheme) {
|
|
93564
|
-
if (!ALLOWED.has(scheme)) return refuseScheme(scheme);
|
|
93565
|
-
let parsed;
|
|
93566
|
-
try {
|
|
93567
|
-
parsed = new URL(raw);
|
|
93568
|
-
} catch {
|
|
93569
|
-
return refuse("host", `n\xE3o consegui ler ${raw} como URL`);
|
|
93570
|
-
}
|
|
93571
|
-
if (scheme === "file") return parseFile(raw, parsed);
|
|
93572
|
-
if (parsed.hostname === "" || raw.slice(scheme.length + "://".length).startsWith("/")) {
|
|
93573
|
-
return refuse("host", "a URL n\xE3o tem host");
|
|
93574
|
-
}
|
|
93575
|
-
const path = parsed.pathname.replace(/^\/+/, "");
|
|
93576
|
-
if (path === "") {
|
|
93577
|
-
return refuse("path", "a URL n\xE3o diz qual reposit\xF3rio");
|
|
93578
|
-
}
|
|
93579
|
-
const sanitized = new URL(raw);
|
|
93580
|
-
sanitized.username = "";
|
|
93581
|
-
sanitized.password = "";
|
|
93582
|
-
return {
|
|
93583
|
-
ok: true,
|
|
93584
|
-
raw,
|
|
93585
|
-
url: {
|
|
93586
|
-
scheme,
|
|
93587
|
-
href: sanitized.href,
|
|
93588
|
-
host: parsed.host,
|
|
93589
|
-
path,
|
|
93590
|
-
insecure: scheme === "http",
|
|
93591
|
-
scp: false
|
|
93592
|
-
}
|
|
93593
|
-
};
|
|
93594
|
-
}
|
|
93595
|
-
function parseFile(raw, parsed) {
|
|
93596
|
-
if (parsed.hostname !== "" && parsed.hostname !== "localhost") {
|
|
93597
|
-
return refuse("host", "file:// com host n\xE3o \xE9 aceito; use file:///caminho/absoluto");
|
|
93598
|
-
}
|
|
93599
|
-
const path = decodeURIComponent(parsed.pathname);
|
|
93600
|
-
if (!path.startsWith("/")) {
|
|
93601
|
-
return refuse("path", "o caminho de um file:// precisa ser absoluto");
|
|
93602
|
-
}
|
|
93603
|
-
return {
|
|
93604
|
-
ok: true,
|
|
93605
|
-
raw,
|
|
93606
|
-
url: { scheme: "file", href: `file://${parsed.pathname}`, host: "", path, insecure: false, scp: false }
|
|
93607
|
-
};
|
|
93608
|
-
}
|
|
93609
|
-
function parseScp(raw) {
|
|
93610
|
-
const colon = scpColon(raw);
|
|
93611
|
-
const authority = raw.slice(0, colon);
|
|
93612
|
-
const path = raw.slice(colon + 1);
|
|
93613
|
-
const at = authority.lastIndexOf("@");
|
|
93614
|
-
const user = at === -1 ? "" : authority.slice(0, at);
|
|
93615
|
-
const host = authority.slice(at + 1);
|
|
93616
|
-
if (host === "") return refuse("host", "a URL n\xE3o tem host");
|
|
93617
|
-
if (path === "") return refuse("path", "a URL n\xE3o diz qual reposit\xF3rio");
|
|
93618
|
-
const cleanUser = user.includes(":") ? user.slice(0, user.indexOf(":")) : user;
|
|
93619
|
-
const prefix = cleanUser === "" ? "" : `${cleanUser}@`;
|
|
93620
|
-
return {
|
|
93621
|
-
ok: true,
|
|
93622
|
-
raw,
|
|
93623
|
-
url: {
|
|
93624
|
-
scheme: "ssh",
|
|
93625
|
-
href: `${prefix}${host}:${path}`,
|
|
93626
|
-
host,
|
|
93627
|
-
path: path.replace(/^\/+/, ""),
|
|
93628
|
-
insecure: false,
|
|
93629
|
-
scp: true
|
|
93630
|
-
}
|
|
93631
|
-
};
|
|
93632
|
-
}
|
|
93633
|
-
function repoNameOf(url2) {
|
|
93634
|
-
const segments = url2.path.split("/").filter((segment) => segment !== "");
|
|
93635
|
-
const last = segments.at(-1) ?? "";
|
|
93636
|
-
return last.endsWith(".git") ? last.slice(0, -".git".length) : last;
|
|
93637
|
-
}
|
|
93638
|
-
|
|
93639
|
-
// src/git/clone-plan.ts
|
|
94999
|
+
import { dirname as dirname9, join as join17, normalize as normalize3 } from "node:path";
|
|
93640
95000
|
function planClone({
|
|
93641
95001
|
source,
|
|
93642
95002
|
workspacesDir,
|
|
@@ -93667,11 +95027,11 @@ function rawUrlOf(source) {
|
|
|
93667
95027
|
return parsed.ok ? parsed.raw : null;
|
|
93668
95028
|
}
|
|
93669
95029
|
function tempCloneDir(target, jobId) {
|
|
93670
|
-
return
|
|
95030
|
+
return join17(dirname9(target), `.lumem-clone-${jobId}`);
|
|
93671
95031
|
}
|
|
93672
95032
|
|
|
93673
95033
|
// src/git/managed-dir.ts
|
|
93674
|
-
import { lstat as lstat4, readdir as readdir6, realpath as realpath5, rm as
|
|
95034
|
+
import { lstat as lstat4, readdir as readdir6, realpath as realpath5, rm as rm6, rmdir as rmdir2 } from "node:fs/promises";
|
|
93675
95035
|
import { dirname as dirname10 } from "node:path";
|
|
93676
95036
|
async function deleteManagedRepo({ path, workspacesDir }) {
|
|
93677
95037
|
let info;
|
|
@@ -93688,7 +95048,7 @@ async function deleteManagedRepo({ path, workspacesDir }) {
|
|
|
93688
95048
|
);
|
|
93689
95049
|
}
|
|
93690
95050
|
await requireInsideTree(path, workspacesDir);
|
|
93691
|
-
await
|
|
95051
|
+
await rm6(path, { recursive: true, force: true });
|
|
93692
95052
|
}
|
|
93693
95053
|
async function collectEmptyProjectHome(home, workspacesDir) {
|
|
93694
95054
|
await requireInsideTree(home, workspacesDir).catch(() => {
|
|
@@ -93752,7 +95112,7 @@ async function resolveAvailableName(ctx, workspaceId, desired) {
|
|
|
93752
95112
|
if (!taken.has(candidate)) return candidate;
|
|
93753
95113
|
}
|
|
93754
95114
|
}
|
|
93755
|
-
var pathSchema = external_exports.string().trim().min(1, "informe o caminho do reposit\xF3rio").refine((value) =>
|
|
95115
|
+
var pathSchema = external_exports.string().trim().min(1, "informe o caminho do reposit\xF3rio").refine((value) => isAbsolute6(value), "o caminho precisa ser absoluto").transform((value) => normalize4(value));
|
|
93756
95116
|
var projectRouter = router({
|
|
93757
95117
|
listByWorkspace: publicProcedure.input(external_exports.object({ workspaceId: external_exports.string().min(1) })).query(async ({ ctx, input }) => {
|
|
93758
95118
|
const rows = await createProjectRepository(ctx.db).listByWorkspace(input.workspaceId);
|
|
@@ -93813,7 +95173,7 @@ var projectRouter = router({
|
|
|
93813
95173
|
registerProject(ctx, {
|
|
93814
95174
|
workspaceId: input.workspaceId,
|
|
93815
95175
|
path: input.path,
|
|
93816
|
-
name: input.name ??
|
|
95176
|
+
name: input.name ?? basename4(input.path)
|
|
93817
95177
|
})
|
|
93818
95178
|
)
|
|
93819
95179
|
)
|
|
@@ -93827,23 +95187,27 @@ var projectRouter = router({
|
|
|
93827
95187
|
),
|
|
93828
95188
|
remove: publicProcedure.input(external_exports.object({ id: external_exports.string().min(1) })).mutation(
|
|
93829
95189
|
({ ctx, input }) => domainSafeAsync(async () => {
|
|
93830
|
-
const
|
|
93831
|
-
|
|
95190
|
+
const projects = createProjectRepository(ctx.db);
|
|
95191
|
+
const worktrees = createWorktreeRepository(ctx.db);
|
|
95192
|
+
const row = await projects.findById(input.id);
|
|
95193
|
+
const owned = await worktrees.listByProject(input.id);
|
|
95194
|
+
const vanishing = row?.managed === true ? [] : owned;
|
|
95195
|
+
const runningLists = await Promise.all([
|
|
95196
|
+
ctx.sessionStore.listRunningInScope("project", input.id),
|
|
95197
|
+
...vanishing.map((wt) => ctx.sessionStore.listRunningInScope("worktree", wt.id))
|
|
95198
|
+
]);
|
|
95199
|
+
const running = runningLists.reduce((total, list) => total + list.length, 0);
|
|
95200
|
+
if (running > 0) {
|
|
93832
95201
|
throw new DomainError(
|
|
93833
95202
|
"BLOCKED",
|
|
93834
|
-
`o projeto tem ${running
|
|
95203
|
+
`o projeto tem ${running} sess\xE3o(\xF5es) rodando; encerre-as antes de remover`
|
|
93835
95204
|
);
|
|
93836
95205
|
}
|
|
93837
|
-
|
|
93838
|
-
|
|
93839
|
-
|
|
93840
|
-
|
|
93841
|
-
|
|
93842
|
-
throw new DomainError(
|
|
93843
|
-
"IN_USE",
|
|
93844
|
-
`o projeto ainda tem worktrees registradas (${worktrees.length}); remova-as antes`
|
|
93845
|
-
);
|
|
93846
|
-
}
|
|
95206
|
+
if (row?.managed && owned.length > 0) {
|
|
95207
|
+
throw new DomainError(
|
|
95208
|
+
"IN_USE",
|
|
95209
|
+
`o projeto ainda tem worktrees registradas (${owned.length}); remova-as antes`
|
|
95210
|
+
);
|
|
93847
95211
|
}
|
|
93848
95212
|
if (row) {
|
|
93849
95213
|
if (row.managed) {
|
|
@@ -93855,7 +95219,11 @@ var projectRouter = router({
|
|
|
93855
95219
|
).catch(() => false);
|
|
93856
95220
|
}
|
|
93857
95221
|
await projects.remove(input.id);
|
|
93858
|
-
if (row)
|
|
95222
|
+
if (row) {
|
|
95223
|
+
ctx.events.emit({ type: "project.changed", workspaceId: row.workspaceId });
|
|
95224
|
+
if (vanishing.length > 0)
|
|
95225
|
+
ctx.events.emit({ type: "worktree.changed", projectId: input.id });
|
|
95226
|
+
}
|
|
93859
95227
|
return { ok: true };
|
|
93860
95228
|
})
|
|
93861
95229
|
),
|
|
@@ -93983,7 +95351,7 @@ async function runCloneJob(ctx, job, { rawUrl, url: url2, name }) {
|
|
|
93983
95351
|
finalName === name ? void 0 : `o nome ${name} j\xE1 existia; registrado como ${finalName}`
|
|
93984
95352
|
);
|
|
93985
95353
|
} catch (error40) {
|
|
93986
|
-
if (landed !== null) await
|
|
95354
|
+
if (landed !== null) await rm7(landed, { recursive: true, force: true }).catch(() => {
|
|
93987
95355
|
});
|
|
93988
95356
|
await sweep();
|
|
93989
95357
|
if (ctx.clones.get(job.id)?.state === "cancelled") return;
|
|
@@ -94248,24 +95616,24 @@ var sessionRouter = router({
|
|
|
94248
95616
|
|
|
94249
95617
|
// src/routers/setup.ts
|
|
94250
95618
|
import { mkdirSync as mkdirSync4 } from "node:fs";
|
|
94251
|
-
import { join as
|
|
95619
|
+
import { join as join19 } from "node:path";
|
|
94252
95620
|
|
|
94253
95621
|
// src/setup/run-command.ts
|
|
94254
|
-
import { execFile as
|
|
95622
|
+
import { execFile as execFile3 } from "node:child_process";
|
|
94255
95623
|
var DEFAULT_VERSION_TIMEOUT_MS = 3e3;
|
|
94256
|
-
var runCommand = (command, args, { timeoutMs = DEFAULT_VERSION_TIMEOUT_MS } = {}) => new Promise((
|
|
94257
|
-
|
|
95624
|
+
var runCommand = (command, args, { timeoutMs = DEFAULT_VERSION_TIMEOUT_MS } = {}) => new Promise((resolve6) => {
|
|
95625
|
+
execFile3(
|
|
94258
95626
|
command,
|
|
94259
95627
|
[...args],
|
|
94260
95628
|
{ timeout: timeoutMs, maxBuffer: 1024 * 1024 },
|
|
94261
95629
|
(error40, stdout, stderr) => {
|
|
94262
95630
|
const output = (stdout.trim() !== "" ? stdout : stderr).trim();
|
|
94263
95631
|
if (error40 === null) {
|
|
94264
|
-
|
|
95632
|
+
resolve6({ ok: true, output, failure: null });
|
|
94265
95633
|
return;
|
|
94266
95634
|
}
|
|
94267
95635
|
const killed = error40.killed === true;
|
|
94268
|
-
|
|
95636
|
+
resolve6({
|
|
94269
95637
|
ok: false,
|
|
94270
95638
|
output,
|
|
94271
95639
|
failure: killed ? `n\xE3o respondeu em ${timeoutMs} ms` : error40.message
|
|
@@ -94303,42 +95671,86 @@ async function detectAgents({
|
|
|
94303
95671
|
path = process.env["PATH"],
|
|
94304
95672
|
env = process.env,
|
|
94305
95673
|
run: run3 = runCommand,
|
|
95674
|
+
specs = ADAPTERS,
|
|
94306
95675
|
installedAt
|
|
94307
95676
|
} = {}) {
|
|
94308
|
-
const
|
|
94309
|
-
|
|
94310
|
-
|
|
95677
|
+
const adapters = await Promise.all(
|
|
95678
|
+
specs.map(async (spec) => reportFor(spec, { path, env, run: run3, installedAt }))
|
|
95679
|
+
);
|
|
95680
|
+
return { adapters };
|
|
95681
|
+
}
|
|
95682
|
+
async function reportFor(spec, {
|
|
95683
|
+
path,
|
|
95684
|
+
env,
|
|
95685
|
+
run: run3,
|
|
95686
|
+
installedAt
|
|
95687
|
+
}) {
|
|
95688
|
+
const [adapter, cli] = await Promise.all([
|
|
95689
|
+
inspect(spec.command, adapterInstallCommand(spec), {
|
|
95690
|
+
path,
|
|
95691
|
+
run: run3,
|
|
95692
|
+
...installedAt === void 0 ? {} : { preferred: installedAt(spec) }
|
|
95693
|
+
}),
|
|
95694
|
+
spec.cli === null ? Promise.resolve(null) : inspect(spec.cli.command, spec.cli.install, { path, run: run3 })
|
|
94311
95695
|
]);
|
|
94312
|
-
|
|
94313
|
-
|
|
95696
|
+
return {
|
|
95697
|
+
id: spec.id,
|
|
95698
|
+
label: spec.label,
|
|
95699
|
+
adapter,
|
|
95700
|
+
cli,
|
|
95701
|
+
apiKeyEnv: spec.apiKeyEnv.find((name) => (env[name] ?? "").trim() !== "") ?? null
|
|
95702
|
+
};
|
|
94314
95703
|
}
|
|
94315
95704
|
|
|
94316
95705
|
// src/setup/install-adapter.ts
|
|
94317
95706
|
import { existsSync as existsSync6 } from "node:fs";
|
|
94318
95707
|
import { mkdir as mkdir8 } from "node:fs/promises";
|
|
94319
|
-
import { join as
|
|
94320
|
-
function
|
|
94321
|
-
return
|
|
95708
|
+
import { join as join18 } from "node:path";
|
|
95709
|
+
function adapterDir(dir, spec = CLAUDE_ADAPTER) {
|
|
95710
|
+
return join18(dir, spec.id);
|
|
95711
|
+
}
|
|
95712
|
+
function adapterBinaryPath(dir, spec = CLAUDE_ADAPTER) {
|
|
95713
|
+
return join18(adapterDir(dir, spec), "node_modules", ".bin", spec.command);
|
|
95714
|
+
}
|
|
95715
|
+
function legacyAdapterBinaryPath(dir, spec) {
|
|
95716
|
+
return join18(dir, "node_modules", ".bin", spec.command);
|
|
94322
95717
|
}
|
|
94323
95718
|
async function installAdapter({
|
|
95719
|
+
spec = CLAUDE_ADAPTER,
|
|
94324
95720
|
dir,
|
|
94325
95721
|
run: run3 = runCommand,
|
|
94326
|
-
timeoutMs = 12e4
|
|
95722
|
+
timeoutMs = 12e4,
|
|
95723
|
+
resolve: resolve6 = resolveCommandPath
|
|
94327
95724
|
}) {
|
|
94328
|
-
|
|
95725
|
+
if (spec.package === null) {
|
|
95726
|
+
const found = resolve6(spec.command);
|
|
95727
|
+
if (found === null) {
|
|
95728
|
+
throw new DomainError(
|
|
95729
|
+
"NOT_FOUND",
|
|
95730
|
+
`${spec.label} n\xE3o tem adaptador para instalar: o bin\xE1rio ${spec.command} tem que estar no PATH, e n\xE3o est\xE1`
|
|
95731
|
+
);
|
|
95732
|
+
}
|
|
95733
|
+
return { path: found, version: spec.pinnedVersion, alreadyInstalled: true };
|
|
95734
|
+
}
|
|
95735
|
+
const target = adapterDir(dir, spec);
|
|
95736
|
+
const binary = adapterBinaryPath(dir, spec);
|
|
94329
95737
|
if (existsSync6(binary)) {
|
|
94330
|
-
return { path: binary, version:
|
|
95738
|
+
return { path: binary, version: spec.pinnedVersion, alreadyInstalled: true };
|
|
95739
|
+
}
|
|
95740
|
+
const legacy = legacyAdapterBinaryPath(dir, spec);
|
|
95741
|
+
if (existsSync6(legacy)) {
|
|
95742
|
+
return { path: legacy, version: spec.pinnedVersion, alreadyInstalled: true };
|
|
94331
95743
|
}
|
|
94332
|
-
await mkdir8(
|
|
95744
|
+
await mkdir8(target, { recursive: true });
|
|
94333
95745
|
const outcome = await run3(
|
|
94334
95746
|
"npm",
|
|
94335
95747
|
[
|
|
94336
95748
|
"install",
|
|
94337
95749
|
"--prefix",
|
|
94338
|
-
|
|
95750
|
+
target,
|
|
94339
95751
|
"--no-fund",
|
|
94340
95752
|
"--no-audit",
|
|
94341
|
-
`${
|
|
95753
|
+
`${spec.package}@${spec.pinnedVersion}`
|
|
94342
95754
|
],
|
|
94343
95755
|
{ timeoutMs }
|
|
94344
95756
|
);
|
|
@@ -94355,10 +95767,10 @@ ${outcome.output}`.trim()
|
|
|
94355
95767
|
if (!existsSync6(binary)) {
|
|
94356
95768
|
throw new DomainError(
|
|
94357
95769
|
"SPAWN_FAILED",
|
|
94358
|
-
`o npm terminou sem erro mas ${binary} n\xE3o existe \u2014 o pacote ${
|
|
95770
|
+
`o npm terminou sem erro mas ${binary} n\xE3o existe \u2014 o pacote ${spec.package} pode ter mudado de layout`
|
|
94359
95771
|
);
|
|
94360
95772
|
}
|
|
94361
|
-
return { path: binary, version:
|
|
95773
|
+
return { path: binary, version: spec.pinnedVersion, alreadyInstalled: false };
|
|
94362
95774
|
}
|
|
94363
95775
|
|
|
94364
95776
|
// src/setup/login.ts
|
|
@@ -94553,6 +95965,17 @@ function diskCheck(config2, freeBytes) {
|
|
|
94553
95965
|
}
|
|
94554
95966
|
|
|
94555
95967
|
// src/routers/setup.ts
|
|
95968
|
+
function adaptersDir(stateDir) {
|
|
95969
|
+
return join19(stateDir, ADAPTERS_DIR_NAME);
|
|
95970
|
+
}
|
|
95971
|
+
function specOf(id) {
|
|
95972
|
+
const spec = adapterById(id ?? DEFAULT_ADAPTER_ID);
|
|
95973
|
+
if (spec === null) {
|
|
95974
|
+
throw new DomainError("INVALID_ARGUMENT", `n\xE3o existe adaptador "${id}" no cat\xE1logo`);
|
|
95975
|
+
}
|
|
95976
|
+
return spec;
|
|
95977
|
+
}
|
|
95978
|
+
var adapterInput = external_exports.object({ adapterId: external_exports.string().trim().min(1).optional() }).optional();
|
|
94556
95979
|
var setupRouter = router({
|
|
94557
95980
|
/** The five checks, each one able to fail without the others. */
|
|
94558
95981
|
preflight: publicProcedure.query(({ ctx }) => preflight({ config: ctx.config })),
|
|
@@ -94564,7 +95987,9 @@ var setupRouter = router({
|
|
|
94564
95987
|
* have it globally, and the flow must not ask twice for the same thing.
|
|
94565
95988
|
*/
|
|
94566
95989
|
agents: publicProcedure.query(
|
|
94567
|
-
({ ctx }) => detectAgents({
|
|
95990
|
+
({ ctx }) => detectAgents({
|
|
95991
|
+
installedAt: (spec) => adapterBinaryPath(adaptersDir(ctx.config.stateDir), spec)
|
|
95992
|
+
})
|
|
94568
95993
|
),
|
|
94569
95994
|
/**
|
|
94570
95995
|
* Installs the adapter into the daemon's own directory, at the pinned version.
|
|
@@ -94573,9 +95998,9 @@ var setupRouter = router({
|
|
|
94573
95998
|
* costs is named in `install-adapter.ts` — the daemon runs a package manager and
|
|
94574
95999
|
* then executes what it downloaded.
|
|
94575
96000
|
*/
|
|
94576
|
-
installAdapter: publicProcedure.mutation(
|
|
94577
|
-
({ ctx }) => domainSafeAsync(
|
|
94578
|
-
() => installAdapter({ dir:
|
|
96001
|
+
installAdapter: publicProcedure.input(adapterInput).mutation(
|
|
96002
|
+
({ ctx, input }) => domainSafeAsync(
|
|
96003
|
+
() => installAdapter({ spec: specOf(input?.adapterId), dir: adaptersDir(ctx.config.stateDir) })
|
|
94579
96004
|
)
|
|
94580
96005
|
),
|
|
94581
96006
|
/**
|
|
@@ -94591,6 +96016,8 @@ var setupRouter = router({
|
|
|
94591
96016
|
methodId: external_exports.string().trim().min(1),
|
|
94592
96017
|
/** Which adapter to ask. Defaults to what the flow installed or found. */
|
|
94593
96018
|
command: external_exports.string().trim().min(1).optional(),
|
|
96019
|
+
/** Which catalogued adapter, when no explicit command is given. */
|
|
96020
|
+
adapterId: external_exports.string().trim().min(1).optional(),
|
|
94594
96021
|
/**
|
|
94595
96022
|
* And its arguments, because a command without them is a different program.
|
|
94596
96023
|
*
|
|
@@ -94603,8 +96030,8 @@ var setupRouter = router({
|
|
|
94603
96030
|
})
|
|
94604
96031
|
).mutation(
|
|
94605
96032
|
({ ctx, input }) => domainSafeAsync(async () => {
|
|
94606
|
-
const command = input.command ??
|
|
94607
|
-
const cwd =
|
|
96033
|
+
const command = input.command ?? specOf(input.adapterId).command;
|
|
96034
|
+
const cwd = join19(ctx.config.stateDir, "probe");
|
|
94608
96035
|
const report = await ctx.acpManager.probe({
|
|
94609
96036
|
command,
|
|
94610
96037
|
...input.args === void 0 ? {} : { args: input.args },
|
|
@@ -94633,6 +96060,60 @@ var setupRouter = router({
|
|
|
94633
96060
|
});
|
|
94634
96061
|
})
|
|
94635
96062
|
),
|
|
96063
|
+
/**
|
|
96064
|
+
* Entrar no agente, pela chamada que o próprio agente ofereceu (T10, T11).
|
|
96065
|
+
*
|
|
96066
|
+
* Três procedimentos e nenhum bloqueante, porque um deles espera **uma
|
|
96067
|
+
* pessoa**: o `authenticate` de um método de navegador fica pendurado até
|
|
96068
|
+
* alguém autorizar noutro lugar, e o pedido de mostrar uma URL chega no meio
|
|
96069
|
+
* dessa espera. Começar e perguntar é o mesmo desenho do login por comando, que
|
|
96070
|
+
* devolvia um `ptySessionId` para o cliente acompanhar.
|
|
96071
|
+
*
|
|
96072
|
+
* O `command` continua sendo conferido contra o handshake — o cliente manda um
|
|
96073
|
+
* `methodId`, e o que roda é o que o adaptador declarou para aquele id.
|
|
96074
|
+
*/
|
|
96075
|
+
authenticate: publicProcedure.input(
|
|
96076
|
+
external_exports.object({
|
|
96077
|
+
methodId: external_exports.string().trim().min(1),
|
|
96078
|
+
/** Qual adaptador, quando não vem um comando explícito. */
|
|
96079
|
+
adapterId: external_exports.string().trim().min(1).optional(),
|
|
96080
|
+
command: external_exports.string().trim().min(1).optional(),
|
|
96081
|
+
args: external_exports.array(external_exports.string()).optional(),
|
|
96082
|
+
/**
|
|
96083
|
+
* A chave, quando o método pede uma.
|
|
96084
|
+
*
|
|
96085
|
+
* Ela entra por aqui, atravessa o daemon e vai para o adaptador. Não é
|
|
96086
|
+
* gravada, não é logada e **não volta** em nenhuma resposta — o que volta
|
|
96087
|
+
* é o estado da tentativa.
|
|
96088
|
+
*/
|
|
96089
|
+
apiKey: external_exports.string().min(1).optional()
|
|
96090
|
+
})
|
|
96091
|
+
).mutation(
|
|
96092
|
+
({ ctx, input }) => domainSafeAsync(() => {
|
|
96093
|
+
const spec = specOf(input.adapterId);
|
|
96094
|
+
const cwd = join19(ctx.config.stateDir, "probe");
|
|
96095
|
+
mkdirSync4(cwd, { recursive: true });
|
|
96096
|
+
return Promise.resolve(
|
|
96097
|
+
ctx.agentAuth.start({
|
|
96098
|
+
command: input.command ?? spec.command,
|
|
96099
|
+
...input.args === void 0 ? {} : { args: input.args },
|
|
96100
|
+
cwd,
|
|
96101
|
+
methodId: input.methodId,
|
|
96102
|
+
...input.apiKey === void 0 ? {} : { apiKey: input.apiKey },
|
|
96103
|
+
adapterVersion: spec.pinnedVersion
|
|
96104
|
+
})
|
|
96105
|
+
);
|
|
96106
|
+
})
|
|
96107
|
+
),
|
|
96108
|
+
/** O estado de uma tentativa de login. É o que a tela pergunta enquanto espera. */
|
|
96109
|
+
authState: publicProcedure.input(external_exports.object({ loginId: external_exports.string().trim().min(1) })).query(({ ctx, input }) => domainSafe(() => ctx.agentAuth.status(input.loginId))),
|
|
96110
|
+
/**
|
|
96111
|
+
* Desistir: mata o adaptador.
|
|
96112
|
+
*
|
|
96113
|
+
* Não há "cancelar" no protocolo — o `authenticate` está esperando uma pessoa,
|
|
96114
|
+
* e a única forma de parar de esperar é o processo acabar. Ele é do daemon.
|
|
96115
|
+
*/
|
|
96116
|
+
cancelAuth: publicProcedure.input(external_exports.object({ loginId: external_exports.string().trim().min(1) })).mutation(({ ctx, input }) => domainSafe(() => ctx.agentAuth.cancel(input.loginId))),
|
|
94636
96117
|
/**
|
|
94637
96118
|
* One handshake, then the process dies.
|
|
94638
96119
|
*
|
|
@@ -94644,12 +96125,14 @@ var setupRouter = router({
|
|
|
94644
96125
|
external_exports.object({
|
|
94645
96126
|
/** Defaults to the adapter the flow installs. */
|
|
94646
96127
|
command: external_exports.string().trim().min(1).optional(),
|
|
96128
|
+
/** Which catalogued adapter, when no explicit command is given. */
|
|
96129
|
+
adapterId: external_exports.string().trim().min(1).optional(),
|
|
94647
96130
|
args: external_exports.array(external_exports.string()).optional()
|
|
94648
96131
|
}).optional()
|
|
94649
96132
|
).query(
|
|
94650
96133
|
({ ctx, input }) => domainSafeAsync(async () => {
|
|
94651
|
-
const command = input?.command ??
|
|
94652
|
-
const cwd =
|
|
96134
|
+
const command = input?.command ?? specOf(input?.adapterId).command;
|
|
96135
|
+
const cwd = join19(ctx.config.stateDir, "probe");
|
|
94653
96136
|
try {
|
|
94654
96137
|
mkdirSync4(cwd, { recursive: true });
|
|
94655
96138
|
} catch (error40) {
|
|
@@ -94726,6 +96209,32 @@ function usageByWorktree(db, { projectId, period: period2, now }) {
|
|
|
94726
96209
|
and(eq(sessionUsage.worktreeId, worktree.id), gte(sessionUsage.createdAt, since))
|
|
94727
96210
|
).where(eq(worktree.projectId, projectId)).groupBy(worktree.id).orderBy(sql`${SUM.tokens} desc`, worktree.name).all();
|
|
94728
96211
|
}
|
|
96212
|
+
function usageByProjectAndAgent(db, { workspaceId, period: period2, now }) {
|
|
96213
|
+
const since = windowStart(period2, now);
|
|
96214
|
+
return db.select({
|
|
96215
|
+
projectId: sessionUsage.projectId,
|
|
96216
|
+
agentConfigId: sessionUsage.agentConfigId,
|
|
96217
|
+
// `max` e não `min`: dá no mesmo dentro de um grupo — o join é por id — e
|
|
96218
|
+
// é o mesmo truque que a moeda já usa para atravessar um `GROUP BY`.
|
|
96219
|
+
name: sql`max(${agentConfig.name})`,
|
|
96220
|
+
tokens: SUM.tokens,
|
|
96221
|
+
cost: SUM.cost,
|
|
96222
|
+
currency: SUM.currency,
|
|
96223
|
+
turns: SUM.turns
|
|
96224
|
+
}).from(sessionUsage).innerJoin(project, eq(project.id, sessionUsage.projectId)).leftJoin(agentConfig, eq(agentConfig.id, sessionUsage.agentConfigId)).where(and(eq(project.workspaceId, workspaceId), gte(sessionUsage.createdAt, since))).groupBy(sessionUsage.projectId, sessionUsage.agentConfigId).orderBy(sql`${SUM.tokens} desc`).all();
|
|
96225
|
+
}
|
|
96226
|
+
function usageByWorktreeAndAgent(db, { projectId, period: period2, now }) {
|
|
96227
|
+
const since = windowStart(period2, now);
|
|
96228
|
+
return db.select({
|
|
96229
|
+
worktreeId: sessionUsage.worktreeId,
|
|
96230
|
+
agentConfigId: sessionUsage.agentConfigId,
|
|
96231
|
+
name: sql`max(${agentConfig.name})`,
|
|
96232
|
+
tokens: SUM.tokens,
|
|
96233
|
+
cost: SUM.cost,
|
|
96234
|
+
currency: SUM.currency,
|
|
96235
|
+
turns: SUM.turns
|
|
96236
|
+
}).from(sessionUsage).leftJoin(agentConfig, eq(agentConfig.id, sessionUsage.agentConfigId)).where(and(eq(sessionUsage.projectId, projectId), gte(sessionUsage.createdAt, since))).groupBy(sessionUsage.worktreeId, sessionUsage.agentConfigId).orderBy(sql`${SUM.tokens} desc`).all();
|
|
96237
|
+
}
|
|
94729
96238
|
function usageOutsideWorktrees(db, { projectId, period: period2, now }) {
|
|
94730
96239
|
const since = windowStart(period2, now);
|
|
94731
96240
|
const [row] = db.select({ tokens: SUM.tokens, cost: SUM.cost, currency: SUM.currency, turns: SUM.turns }).from(sessionUsage).where(
|
|
@@ -94762,7 +96271,21 @@ var usageRouter = router({
|
|
|
94762
96271
|
projectId: input.projectId,
|
|
94763
96272
|
period: input.period
|
|
94764
96273
|
})
|
|
94765
|
-
}))
|
|
96274
|
+
})),
|
|
96275
|
+
/*
|
|
96276
|
+
* O mesmo consumo por agente (`second-agent`, F5).
|
|
96277
|
+
*
|
|
96278
|
+
* Procedimentos separados, e não um `groupBy` nos dois de cima: a resposta
|
|
96279
|
+
* agrupada tem uma linha por par, e enfiá-la na mesma chamada mudaria a forma
|
|
96280
|
+
* do que a tela do workspace já lê. A tela só pede isto quando há **mais de um
|
|
96281
|
+
* agente** — com um, a coluna não existe e a chamada não acontece (C5).
|
|
96282
|
+
*/
|
|
96283
|
+
byProjectAndAgent: publicProcedure.input(external_exports.object({ workspaceId: external_exports.string().min(1), period })).query(
|
|
96284
|
+
({ ctx, input }) => usageByProjectAndAgent(ctx.db, { workspaceId: input.workspaceId, period: input.period })
|
|
96285
|
+
),
|
|
96286
|
+
byWorktreeAndAgent: publicProcedure.input(external_exports.object({ projectId: external_exports.string().min(1), period })).query(
|
|
96287
|
+
({ ctx, input }) => usageByWorktreeAndAgent(ctx.db, { projectId: input.projectId, period: input.period })
|
|
96288
|
+
)
|
|
94766
96289
|
});
|
|
94767
96290
|
|
|
94768
96291
|
// src/routers/workspace.ts
|
|
@@ -94959,6 +96482,7 @@ var appRouter = router({
|
|
|
94959
96482
|
events: eventsRouter,
|
|
94960
96483
|
files: filesRouter,
|
|
94961
96484
|
memory: memoryRouter,
|
|
96485
|
+
pr: prRouter,
|
|
94962
96486
|
project: projectRouter,
|
|
94963
96487
|
scripts: scriptsRouter,
|
|
94964
96488
|
session: sessionRouter,
|
|
@@ -94989,6 +96513,14 @@ async function createServer2({
|
|
|
94989
96513
|
}),
|
|
94990
96514
|
git = createGitService(),
|
|
94991
96515
|
clones = createCloneJobStore(),
|
|
96516
|
+
prHost = createGhHost(),
|
|
96517
|
+
pr = createPrCache({
|
|
96518
|
+
host: prHost,
|
|
96519
|
+
// F6.3: a barra não depende só do relógio dela. É o que faz um merge feito
|
|
96520
|
+
// noutra aba aparecer nesta sem esperar o próximo ciclo.
|
|
96521
|
+
onChange: (projectId) => events.emit({ type: "pr.changed", projectId })
|
|
96522
|
+
}),
|
|
96523
|
+
agentAuth = createAgentAuthService({ acpManager }),
|
|
94992
96524
|
logger = false
|
|
94993
96525
|
}) {
|
|
94994
96526
|
const app = (0, import_fastify2.default)({
|
|
@@ -95007,6 +96539,9 @@ async function createServer2({
|
|
|
95007
96539
|
scripts,
|
|
95008
96540
|
git,
|
|
95009
96541
|
clones,
|
|
96542
|
+
pr,
|
|
96543
|
+
prHost,
|
|
96544
|
+
agentAuth,
|
|
95010
96545
|
events
|
|
95011
96546
|
});
|
|
95012
96547
|
await app.register(fastifyTRPCPlugin, {
|
|
@@ -95142,6 +96677,7 @@ async function bootstrap({
|
|
|
95142
96677
|
askUrl: `http://${config2.host}:${String(config2.port)}/memory/ask`
|
|
95143
96678
|
})
|
|
95144
96679
|
});
|
|
96680
|
+
const agentAuth = createAgentAuthService({ acpManager: acp });
|
|
95145
96681
|
const sessionStore = createSessionStore({
|
|
95146
96682
|
db: openedDatabase.db,
|
|
95147
96683
|
ptyManager,
|
|
@@ -95204,6 +96740,7 @@ async function bootstrap({
|
|
|
95204
96740
|
}
|
|
95205
96741
|
}),
|
|
95206
96742
|
events,
|
|
96743
|
+
agentAuth,
|
|
95207
96744
|
logger
|
|
95208
96745
|
});
|
|
95209
96746
|
bootedApp = app;
|
|
@@ -95215,6 +96752,7 @@ async function bootstrap({
|
|
|
95215
96752
|
stopUsageTracking();
|
|
95216
96753
|
await ptyManager.killAll();
|
|
95217
96754
|
await acp.killAll();
|
|
96755
|
+
agentAuth.cancelAll();
|
|
95218
96756
|
if (beforeClose) await beforeClose();
|
|
95219
96757
|
await app.close();
|
|
95220
96758
|
if (owned) openedDatabase.close();
|
|
@@ -95253,7 +96791,7 @@ async function bootstrap({
|
|
|
95253
96791
|
|
|
95254
96792
|
// src/config.ts
|
|
95255
96793
|
import { homedir as homedir2 } from "node:os";
|
|
95256
|
-
import { isAbsolute as
|
|
96794
|
+
import { isAbsolute as isAbsolute7, join as join20, resolve as resolve5 } from "node:path";
|
|
95257
96795
|
function readPort(raw) {
|
|
95258
96796
|
if (raw === void 0 || raw === "") return DEFAULT_SERVER_PORT;
|
|
95259
96797
|
const trimmed2 = raw.trim();
|
|
@@ -95267,22 +96805,22 @@ function readPort(raw) {
|
|
|
95267
96805
|
return parsed;
|
|
95268
96806
|
}
|
|
95269
96807
|
function absoluteDir(raw) {
|
|
95270
|
-
const expanded = raw === "~" || raw.startsWith("~/") ?
|
|
95271
|
-
return
|
|
96808
|
+
const expanded = raw === "~" || raw.startsWith("~/") ? join20(homedir2(), raw.slice(1)) : raw;
|
|
96809
|
+
return isAbsolute7(expanded) ? join20(expanded) : resolve5(expanded);
|
|
95272
96810
|
}
|
|
95273
96811
|
function readBudget(raw) {
|
|
95274
96812
|
if (raw === void 0 || !/^\d+$/.test(raw.trim())) return 3;
|
|
95275
96813
|
return Number.parseInt(raw.trim(), 10);
|
|
95276
96814
|
}
|
|
95277
96815
|
function loadConfig(env = process.env) {
|
|
95278
|
-
const stateDir = absoluteDir(env.LUMEM_STATE_DIR ??
|
|
96816
|
+
const stateDir = absoluteDir(env.LUMEM_STATE_DIR ?? join20(homedir2(), ".lumem"));
|
|
95279
96817
|
return {
|
|
95280
96818
|
port: readPort(env.LUMEM_PORT),
|
|
95281
96819
|
host: env.LUMEM_HOST ?? "127.0.0.1",
|
|
95282
96820
|
stateDir,
|
|
95283
|
-
databasePath: env.LUMEM_DB_PATH ??
|
|
95284
|
-
workspacesDir:
|
|
95285
|
-
transcriptsDir:
|
|
96821
|
+
databasePath: env.LUMEM_DB_PATH ?? join20(stateDir, "lumem.db"),
|
|
96822
|
+
workspacesDir: join20(stateDir, "workspaces"),
|
|
96823
|
+
transcriptsDir: join20(stateDir, "transcripts"),
|
|
95286
96824
|
// /bin/sh exists on every POSIX system this daemon can run on; SHELL is
|
|
95287
96825
|
// unset under launchd and in some containers.
|
|
95288
96826
|
shell: env.SHELL === void 0 || env.SHELL === "" ? "/bin/sh" : env.SHELL,
|