@clawos-dev/clawd 0.2.142-beta.297.e4b56a8 → 0.2.142
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/dist/cli.cjs +485 -248
- package/dist/dispatch/mcp-server.cjs +15 -19
- package/package.json +2 -1
package/dist/cli.cjs
CHANGED
|
@@ -5952,17 +5952,41 @@ var init_dispatch = __esm({
|
|
|
5952
5952
|
]);
|
|
5953
5953
|
DispatchRunArgsSchema = external_exports.object({
|
|
5954
5954
|
targetPersona: external_exports.string().min(1),
|
|
5955
|
-
prompt: external_exports.string()
|
|
5956
|
-
// mode='continue' 时 daemon 反查 (sourceSessionId, targetPersona) 最近 B session 复用;
|
|
5957
|
-
// 'new' 或 undefined(默认)= 当前行为,新建 B。
|
|
5958
|
-
mode: external_exports.enum(["new", "continue"]).optional()
|
|
5955
|
+
prompt: external_exports.string()
|
|
5959
5956
|
});
|
|
5960
5957
|
DispatchCompleteArgsSchema = external_exports.object({
|
|
5958
|
+
dispatchId: external_exports.string().min(1),
|
|
5961
5959
|
outcome: DispatchOutcomeSchema
|
|
5962
5960
|
});
|
|
5963
5961
|
}
|
|
5964
5962
|
});
|
|
5965
5963
|
|
|
5964
|
+
// ../protocol/src/log.ts
|
|
5965
|
+
var LOG_SOURCES, LogSourceSchema, LOG_LEVELS, LogLevelSchema, LogRecordSchema;
|
|
5966
|
+
var init_log = __esm({
|
|
5967
|
+
"../protocol/src/log.ts"() {
|
|
5968
|
+
"use strict";
|
|
5969
|
+
init_zod();
|
|
5970
|
+
LOG_SOURCES = ["daemon", "main", "renderer"];
|
|
5971
|
+
LogSourceSchema = external_exports.enum(LOG_SOURCES);
|
|
5972
|
+
LOG_LEVELS = ["debug", "info", "warn", "error"];
|
|
5973
|
+
LogLevelSchema = external_exports.enum(LOG_LEVELS);
|
|
5974
|
+
LogRecordSchema = external_exports.object({
|
|
5975
|
+
ts: external_exports.number().int(),
|
|
5976
|
+
source: LogSourceSchema,
|
|
5977
|
+
level: LogLevelSchema,
|
|
5978
|
+
msg: external_exports.string(),
|
|
5979
|
+
ownerPrincipalId: external_exports.string(),
|
|
5980
|
+
deviceId: external_exports.string(),
|
|
5981
|
+
appVersion: external_exports.string(),
|
|
5982
|
+
daemonVersion: external_exports.string().optional(),
|
|
5983
|
+
os: external_exports.string(),
|
|
5984
|
+
meta_json: external_exports.string().optional(),
|
|
5985
|
+
sessionId: external_exports.string().optional()
|
|
5986
|
+
});
|
|
5987
|
+
}
|
|
5988
|
+
});
|
|
5989
|
+
|
|
5966
5990
|
// ../protocol/src/runtime.ts
|
|
5967
5991
|
var init_runtime = __esm({
|
|
5968
5992
|
"../protocol/src/runtime.ts"() {
|
|
@@ -5982,6 +6006,7 @@ var init_runtime = __esm({
|
|
|
5982
6006
|
init_extension();
|
|
5983
6007
|
init_feishu_auth();
|
|
5984
6008
|
init_dispatch();
|
|
6009
|
+
init_log();
|
|
5985
6010
|
}
|
|
5986
6011
|
});
|
|
5987
6012
|
|
|
@@ -7134,7 +7159,7 @@ var require_atomic_sleep = __commonJS({
|
|
|
7134
7159
|
"../node_modules/.pnpm/atomic-sleep@1.0.0/node_modules/atomic-sleep/index.js"(exports2, module2) {
|
|
7135
7160
|
"use strict";
|
|
7136
7161
|
if (typeof SharedArrayBuffer !== "undefined" && typeof Atomics !== "undefined") {
|
|
7137
|
-
let
|
|
7162
|
+
let sleep2 = function(ms) {
|
|
7138
7163
|
const valid = ms > 0 && ms < Infinity;
|
|
7139
7164
|
if (valid === false) {
|
|
7140
7165
|
if (typeof ms !== "number" && typeof ms !== "bigint") {
|
|
@@ -7145,9 +7170,9 @@ var require_atomic_sleep = __commonJS({
|
|
|
7145
7170
|
Atomics.wait(nil, 0, 0, Number(ms));
|
|
7146
7171
|
};
|
|
7147
7172
|
const nil = new Int32Array(new SharedArrayBuffer(4));
|
|
7148
|
-
module2.exports =
|
|
7173
|
+
module2.exports = sleep2;
|
|
7149
7174
|
} else {
|
|
7150
|
-
let
|
|
7175
|
+
let sleep2 = function(ms) {
|
|
7151
7176
|
const valid = ms > 0 && ms < Infinity;
|
|
7152
7177
|
if (valid === false) {
|
|
7153
7178
|
if (typeof ms !== "number" && typeof ms !== "bigint") {
|
|
@@ -7159,7 +7184,7 @@ var require_atomic_sleep = __commonJS({
|
|
|
7159
7184
|
while (target > Date.now()) {
|
|
7160
7185
|
}
|
|
7161
7186
|
};
|
|
7162
|
-
module2.exports =
|
|
7187
|
+
module2.exports = sleep2;
|
|
7163
7188
|
}
|
|
7164
7189
|
}
|
|
7165
7190
|
});
|
|
@@ -7172,7 +7197,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7172
7197
|
var EventEmitter3 = require("events");
|
|
7173
7198
|
var inherits = require("util").inherits;
|
|
7174
7199
|
var path59 = require("path");
|
|
7175
|
-
var
|
|
7200
|
+
var sleep2 = require_atomic_sleep();
|
|
7176
7201
|
var assert = require("assert");
|
|
7177
7202
|
var BUSY_WRITE_TIMEOUT = 100;
|
|
7178
7203
|
var kEmptyBuffer = Buffer.allocUnsafe(0);
|
|
@@ -7318,7 +7343,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7318
7343
|
if ((err.code === "EAGAIN" || err.code === "EBUSY") && this.retryEAGAIN(err, this._writingBuf.length, this._len - this._writingBuf.length)) {
|
|
7319
7344
|
if (this.sync) {
|
|
7320
7345
|
try {
|
|
7321
|
-
|
|
7346
|
+
sleep2(BUSY_WRITE_TIMEOUT);
|
|
7322
7347
|
this.release(void 0, 0);
|
|
7323
7348
|
} catch (err2) {
|
|
7324
7349
|
this.release(err2);
|
|
@@ -7631,7 +7656,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7631
7656
|
if (shouldRetry && !this.retryEAGAIN(err, buf.length, this._len - buf.length)) {
|
|
7632
7657
|
throw err;
|
|
7633
7658
|
}
|
|
7634
|
-
|
|
7659
|
+
sleep2(BUSY_WRITE_TIMEOUT);
|
|
7635
7660
|
}
|
|
7636
7661
|
}
|
|
7637
7662
|
try {
|
|
@@ -7668,7 +7693,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7668
7693
|
if (shouldRetry && !this.retryEAGAIN(err, buf.length, this._len - buf.length)) {
|
|
7669
7694
|
throw err;
|
|
7670
7695
|
}
|
|
7671
|
-
|
|
7696
|
+
sleep2(BUSY_WRITE_TIMEOUT);
|
|
7672
7697
|
}
|
|
7673
7698
|
}
|
|
7674
7699
|
}
|
|
@@ -8409,7 +8434,7 @@ var require_transport = __commonJS({
|
|
|
8409
8434
|
var { createRequire } = require("module");
|
|
8410
8435
|
var getCallers = require_caller();
|
|
8411
8436
|
var { join: join15, isAbsolute: isAbsolute2, sep: sep2 } = require("path");
|
|
8412
|
-
var
|
|
8437
|
+
var sleep2 = require_atomic_sleep();
|
|
8413
8438
|
var onExit = require_on_exit_leak_free();
|
|
8414
8439
|
var ThreadStream = require_thread_stream();
|
|
8415
8440
|
function setupOnExit(stream) {
|
|
@@ -8443,7 +8468,7 @@ var require_transport = __commonJS({
|
|
|
8443
8468
|
return;
|
|
8444
8469
|
}
|
|
8445
8470
|
stream.flushSync();
|
|
8446
|
-
|
|
8471
|
+
sleep2(100);
|
|
8447
8472
|
stream.end();
|
|
8448
8473
|
}
|
|
8449
8474
|
return stream;
|
|
@@ -18162,7 +18187,7 @@ var require_validation = __commonJS({
|
|
|
18162
18187
|
var require_receiver = __commonJS({
|
|
18163
18188
|
"../node_modules/.pnpm/ws@8.20.0/node_modules/ws/lib/receiver.js"(exports2, module2) {
|
|
18164
18189
|
"use strict";
|
|
18165
|
-
var { Writable:
|
|
18190
|
+
var { Writable: Writable3 } = require("stream");
|
|
18166
18191
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
18167
18192
|
var {
|
|
18168
18193
|
BINARY_TYPES,
|
|
@@ -18180,7 +18205,7 @@ var require_receiver = __commonJS({
|
|
|
18180
18205
|
var GET_DATA = 4;
|
|
18181
18206
|
var INFLATING = 5;
|
|
18182
18207
|
var DEFER_EVENT = 6;
|
|
18183
|
-
var Receiver2 = class extends
|
|
18208
|
+
var Receiver2 = class extends Writable3 {
|
|
18184
18209
|
/**
|
|
18185
18210
|
* Creates a Receiver instance.
|
|
18186
18211
|
*
|
|
@@ -21443,7 +21468,7 @@ var require_stream_writable = __commonJS({
|
|
|
21443
21468
|
"../node_modules/.pnpm/readable-stream@2.3.8/node_modules/readable-stream/lib/_stream_writable.js"(exports2, module2) {
|
|
21444
21469
|
"use strict";
|
|
21445
21470
|
var pna = require_process_nextick_args();
|
|
21446
|
-
module2.exports =
|
|
21471
|
+
module2.exports = Writable3;
|
|
21447
21472
|
function CorkedRequest(state) {
|
|
21448
21473
|
var _this = this;
|
|
21449
21474
|
this.next = null;
|
|
@@ -21454,7 +21479,7 @@ var require_stream_writable = __commonJS({
|
|
|
21454
21479
|
}
|
|
21455
21480
|
var asyncWrite = !process.browser && ["v0.10", "v0.9."].indexOf(process.version.slice(0, 5)) > -1 ? setImmediate : pna.nextTick;
|
|
21456
21481
|
var Duplex;
|
|
21457
|
-
|
|
21482
|
+
Writable3.WritableState = WritableState;
|
|
21458
21483
|
var util2 = Object.create(require_util());
|
|
21459
21484
|
util2.inherits = require_inherits();
|
|
21460
21485
|
var internalUtil = {
|
|
@@ -21471,7 +21496,7 @@ var require_stream_writable = __commonJS({
|
|
|
21471
21496
|
return Buffer2.isBuffer(obj) || obj instanceof OurUint8Array;
|
|
21472
21497
|
}
|
|
21473
21498
|
var destroyImpl = require_destroy();
|
|
21474
|
-
util2.inherits(
|
|
21499
|
+
util2.inherits(Writable3, Stream);
|
|
21475
21500
|
function nop() {
|
|
21476
21501
|
}
|
|
21477
21502
|
function WritableState(options, stream) {
|
|
@@ -21536,10 +21561,10 @@ var require_stream_writable = __commonJS({
|
|
|
21536
21561
|
var realHasInstance;
|
|
21537
21562
|
if (typeof Symbol === "function" && Symbol.hasInstance && typeof Function.prototype[Symbol.hasInstance] === "function") {
|
|
21538
21563
|
realHasInstance = Function.prototype[Symbol.hasInstance];
|
|
21539
|
-
Object.defineProperty(
|
|
21564
|
+
Object.defineProperty(Writable3, Symbol.hasInstance, {
|
|
21540
21565
|
value: function(object) {
|
|
21541
21566
|
if (realHasInstance.call(this, object)) return true;
|
|
21542
|
-
if (this !==
|
|
21567
|
+
if (this !== Writable3) return false;
|
|
21543
21568
|
return object && object._writableState instanceof WritableState;
|
|
21544
21569
|
}
|
|
21545
21570
|
});
|
|
@@ -21548,10 +21573,10 @@ var require_stream_writable = __commonJS({
|
|
|
21548
21573
|
return object instanceof this;
|
|
21549
21574
|
};
|
|
21550
21575
|
}
|
|
21551
|
-
function
|
|
21576
|
+
function Writable3(options) {
|
|
21552
21577
|
Duplex = Duplex || require_stream_duplex();
|
|
21553
|
-
if (!realHasInstance.call(
|
|
21554
|
-
return new
|
|
21578
|
+
if (!realHasInstance.call(Writable3, this) && !(this instanceof Duplex)) {
|
|
21579
|
+
return new Writable3(options);
|
|
21555
21580
|
}
|
|
21556
21581
|
this._writableState = new WritableState(options, this);
|
|
21557
21582
|
this.writable = true;
|
|
@@ -21563,7 +21588,7 @@ var require_stream_writable = __commonJS({
|
|
|
21563
21588
|
}
|
|
21564
21589
|
Stream.call(this);
|
|
21565
21590
|
}
|
|
21566
|
-
|
|
21591
|
+
Writable3.prototype.pipe = function() {
|
|
21567
21592
|
this.emit("error", new Error("Cannot pipe, not readable"));
|
|
21568
21593
|
};
|
|
21569
21594
|
function writeAfterEnd(stream, cb) {
|
|
@@ -21586,7 +21611,7 @@ var require_stream_writable = __commonJS({
|
|
|
21586
21611
|
}
|
|
21587
21612
|
return valid;
|
|
21588
21613
|
}
|
|
21589
|
-
|
|
21614
|
+
Writable3.prototype.write = function(chunk, encoding, cb) {
|
|
21590
21615
|
var state = this._writableState;
|
|
21591
21616
|
var ret = false;
|
|
21592
21617
|
var isBuf = !state.objectMode && _isUint8Array(chunk);
|
|
@@ -21607,18 +21632,18 @@ var require_stream_writable = __commonJS({
|
|
|
21607
21632
|
}
|
|
21608
21633
|
return ret;
|
|
21609
21634
|
};
|
|
21610
|
-
|
|
21635
|
+
Writable3.prototype.cork = function() {
|
|
21611
21636
|
var state = this._writableState;
|
|
21612
21637
|
state.corked++;
|
|
21613
21638
|
};
|
|
21614
|
-
|
|
21639
|
+
Writable3.prototype.uncork = function() {
|
|
21615
21640
|
var state = this._writableState;
|
|
21616
21641
|
if (state.corked) {
|
|
21617
21642
|
state.corked--;
|
|
21618
21643
|
if (!state.writing && !state.corked && !state.bufferProcessing && state.bufferedRequest) clearBuffer(this, state);
|
|
21619
21644
|
}
|
|
21620
21645
|
};
|
|
21621
|
-
|
|
21646
|
+
Writable3.prototype.setDefaultEncoding = function setDefaultEncoding(encoding) {
|
|
21622
21647
|
if (typeof encoding === "string") encoding = encoding.toLowerCase();
|
|
21623
21648
|
if (!(["hex", "utf8", "utf-8", "ascii", "binary", "base64", "ucs2", "ucs-2", "utf16le", "utf-16le", "raw"].indexOf((encoding + "").toLowerCase()) > -1)) throw new TypeError("Unknown encoding: " + encoding);
|
|
21624
21649
|
this._writableState.defaultEncoding = encoding;
|
|
@@ -21630,7 +21655,7 @@ var require_stream_writable = __commonJS({
|
|
|
21630
21655
|
}
|
|
21631
21656
|
return chunk;
|
|
21632
21657
|
}
|
|
21633
|
-
Object.defineProperty(
|
|
21658
|
+
Object.defineProperty(Writable3.prototype, "writableHighWaterMark", {
|
|
21634
21659
|
// making it explicit this property is not enumerable
|
|
21635
21660
|
// because otherwise some prototype manipulation in
|
|
21636
21661
|
// userland will fail
|
|
@@ -21776,11 +21801,11 @@ var require_stream_writable = __commonJS({
|
|
|
21776
21801
|
state.bufferedRequest = entry;
|
|
21777
21802
|
state.bufferProcessing = false;
|
|
21778
21803
|
}
|
|
21779
|
-
|
|
21804
|
+
Writable3.prototype._write = function(chunk, encoding, cb) {
|
|
21780
21805
|
cb(new Error("_write() is not implemented"));
|
|
21781
21806
|
};
|
|
21782
|
-
|
|
21783
|
-
|
|
21807
|
+
Writable3.prototype._writev = null;
|
|
21808
|
+
Writable3.prototype.end = function(chunk, encoding, cb) {
|
|
21784
21809
|
var state = this._writableState;
|
|
21785
21810
|
if (typeof chunk === "function") {
|
|
21786
21811
|
cb = chunk;
|
|
@@ -21855,7 +21880,7 @@ var require_stream_writable = __commonJS({
|
|
|
21855
21880
|
}
|
|
21856
21881
|
state.corkedRequestsFree.next = corkReq;
|
|
21857
21882
|
}
|
|
21858
|
-
Object.defineProperty(
|
|
21883
|
+
Object.defineProperty(Writable3.prototype, "destroyed", {
|
|
21859
21884
|
get: function() {
|
|
21860
21885
|
if (this._writableState === void 0) {
|
|
21861
21886
|
return false;
|
|
@@ -21869,9 +21894,9 @@ var require_stream_writable = __commonJS({
|
|
|
21869
21894
|
this._writableState.destroyed = value;
|
|
21870
21895
|
}
|
|
21871
21896
|
});
|
|
21872
|
-
|
|
21873
|
-
|
|
21874
|
-
|
|
21897
|
+
Writable3.prototype.destroy = destroyImpl.destroy;
|
|
21898
|
+
Writable3.prototype._undestroy = destroyImpl.undestroy;
|
|
21899
|
+
Writable3.prototype._destroy = function(err, cb) {
|
|
21875
21900
|
this.end();
|
|
21876
21901
|
cb(err);
|
|
21877
21902
|
};
|
|
@@ -21894,13 +21919,13 @@ var require_stream_duplex = __commonJS({
|
|
|
21894
21919
|
var util2 = Object.create(require_util());
|
|
21895
21920
|
util2.inherits = require_inherits();
|
|
21896
21921
|
var Readable3 = require_stream_readable();
|
|
21897
|
-
var
|
|
21922
|
+
var Writable3 = require_stream_writable();
|
|
21898
21923
|
util2.inherits(Duplex, Readable3);
|
|
21899
21924
|
{
|
|
21900
|
-
keys = objectKeys(
|
|
21925
|
+
keys = objectKeys(Writable3.prototype);
|
|
21901
21926
|
for (v2 = 0; v2 < keys.length; v2++) {
|
|
21902
21927
|
method = keys[v2];
|
|
21903
|
-
if (!Duplex.prototype[method]) Duplex.prototype[method] =
|
|
21928
|
+
if (!Duplex.prototype[method]) Duplex.prototype[method] = Writable3.prototype[method];
|
|
21904
21929
|
}
|
|
21905
21930
|
}
|
|
21906
21931
|
var keys;
|
|
@@ -21909,7 +21934,7 @@ var require_stream_duplex = __commonJS({
|
|
|
21909
21934
|
function Duplex(options) {
|
|
21910
21935
|
if (!(this instanceof Duplex)) return new Duplex(options);
|
|
21911
21936
|
Readable3.call(this, options);
|
|
21912
|
-
|
|
21937
|
+
Writable3.call(this, options);
|
|
21913
21938
|
if (options && options.readable === false) this.readable = false;
|
|
21914
21939
|
if (options && options.writable === false) this.writable = false;
|
|
21915
21940
|
this.allowHalfOpen = true;
|
|
@@ -31151,6 +31176,24 @@ init_protocol();
|
|
|
31151
31176
|
var DEFAULT_PORT = 18790;
|
|
31152
31177
|
var DEFAULT_HOST = "127.0.0.1";
|
|
31153
31178
|
var DEFAULT_CLAWOS_API = "https://api.clawos.chat";
|
|
31179
|
+
var DEFAULT_LOG_ENDPOINT = "https://clawd-prod.cn-hangzhou.log.aliyuncs.com/logstores/app-logs/track";
|
|
31180
|
+
function resolveLogShipping(raw, cliNoShipping) {
|
|
31181
|
+
if (cliNoShipping) {
|
|
31182
|
+
return { mode: "off", endpoint: null, sampling: {} };
|
|
31183
|
+
}
|
|
31184
|
+
const rawMode = raw?.mode;
|
|
31185
|
+
const mode = rawMode === "off" || rawMode === "errors-only" ? rawMode : "on";
|
|
31186
|
+
if (mode === "off") {
|
|
31187
|
+
return { mode, endpoint: null, sampling: {} };
|
|
31188
|
+
}
|
|
31189
|
+
const endpoint = typeof raw?.endpoint === "string" && raw.endpoint ? raw.endpoint : raw?.endpoint === null ? null : DEFAULT_LOG_ENDPOINT;
|
|
31190
|
+
const sampling = mode === "errors-only" ? { debug: 0, info: 0, warn: 1, error: 1 } : isPlainSamplingMap(raw?.sampling) ? raw.sampling : {};
|
|
31191
|
+
return { mode, endpoint, sampling };
|
|
31192
|
+
}
|
|
31193
|
+
function isPlainSamplingMap(v2) {
|
|
31194
|
+
if (!v2 || typeof v2 !== "object") return false;
|
|
31195
|
+
return Object.values(v2).every((n) => typeof n === "number");
|
|
31196
|
+
}
|
|
31154
31197
|
function parseArgs(argv) {
|
|
31155
31198
|
const out = {};
|
|
31156
31199
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -31196,6 +31239,9 @@ function parseArgs(argv) {
|
|
|
31196
31239
|
case "-v":
|
|
31197
31240
|
out.version = true;
|
|
31198
31241
|
break;
|
|
31242
|
+
case "--no-log-shipping":
|
|
31243
|
+
out.noLogShipping = true;
|
|
31244
|
+
break;
|
|
31199
31245
|
default:
|
|
31200
31246
|
if (a.startsWith("--")) throw new Error(`unknown flag: ${a}`);
|
|
31201
31247
|
break;
|
|
@@ -31241,6 +31287,10 @@ function resolveConfig(opts) {
|
|
|
31241
31287
|
const mode = DAEMON_MODE_VALUES.includes(rawMode ?? "") ? rawMode : "sdk";
|
|
31242
31288
|
const filePreviewPorts = Array.isArray(fileCfg.previewPorts) ? fileCfg.previewPorts.map((n) => Number(n)).filter((n) => Number.isInteger(n) && n > 0) : null;
|
|
31243
31289
|
const previewPorts = env.CLAWD_PREVIEW_PORTS ? env.CLAWD_PREVIEW_PORTS.split(",").map((s) => Number(s.trim())).filter((n) => Number.isInteger(n) && n > 0) : filePreviewPorts && filePreviewPorts.length > 0 ? filePreviewPorts : null;
|
|
31290
|
+
const logShipping = resolveLogShipping(
|
|
31291
|
+
fileCfg.logShipping,
|
|
31292
|
+
args.noLogShipping ?? false
|
|
31293
|
+
);
|
|
31244
31294
|
return {
|
|
31245
31295
|
port,
|
|
31246
31296
|
host,
|
|
@@ -31253,7 +31303,8 @@ function resolveConfig(opts) {
|
|
|
31253
31303
|
noTunnelPersist,
|
|
31254
31304
|
frpcBinary,
|
|
31255
31305
|
mode,
|
|
31256
|
-
previewPorts
|
|
31306
|
+
previewPorts,
|
|
31307
|
+
logShipping
|
|
31257
31308
|
};
|
|
31258
31309
|
}
|
|
31259
31310
|
var HELP_TEXT = `clawd [options]
|
|
@@ -31264,6 +31315,7 @@ var HELP_TEXT = `clawd [options]
|
|
|
31264
31315
|
--tunnel \u542F\u7528 frp tunnel \u628A\u672C\u5730\u7AEF\u53E3\u66B4\u9732\u5230\u516C\u7F51
|
|
31265
31316
|
--clawos-api <url> tunnel register \u63A5\u53E3\u7684 base url\uFF08\u9ED8\u8BA4 https://api.clawos.chat\uFF09
|
|
31266
31317
|
--auth-token <s> \u6307\u5B9A daemon auth token\uFF1B\u7F3A\u7701\u65F6 tunnel \u6A21\u5F0F\u4ECE ~/.clawd/auth.json \u590D\u7528\uFF0C\u6CA1\u6709\u5C31\u751F\u6210
|
|
31318
|
+
--no-log-shipping \u7981\u7528 SLS \u65E5\u5FD7\u4E0A\u884C\uFF08\u8986\u76D6 config.json logShipping.mode=off\uFF09
|
|
31267
31319
|
--help / -h \u663E\u793A\u5E2E\u52A9
|
|
31268
31320
|
--version / -v \u663E\u793A\u7248\u672C
|
|
31269
31321
|
|
|
@@ -31339,6 +31391,7 @@ var import_node_url2 = require("url");
|
|
|
31339
31391
|
// src/logger.ts
|
|
31340
31392
|
var import_node_fs2 = __toESM(require("fs"), 1);
|
|
31341
31393
|
var import_node_path2 = __toESM(require("path"), 1);
|
|
31394
|
+
var import_node_stream = require("stream");
|
|
31342
31395
|
var import_pino = __toESM(require_pino(), 1);
|
|
31343
31396
|
function wrap(inner) {
|
|
31344
31397
|
return {
|
|
@@ -31364,12 +31417,329 @@ function createLogger(opts = {}) {
|
|
|
31364
31417
|
} catch {
|
|
31365
31418
|
}
|
|
31366
31419
|
}
|
|
31420
|
+
if (opts.logClient) {
|
|
31421
|
+
const client = opts.logClient;
|
|
31422
|
+
const sink = new import_node_stream.Writable({
|
|
31423
|
+
write(chunk, _enc, cb) {
|
|
31424
|
+
try {
|
|
31425
|
+
const line = String(chunk).trim();
|
|
31426
|
+
if (line) {
|
|
31427
|
+
const obj = JSON.parse(line);
|
|
31428
|
+
const lvl = pinoLevelToString(obj.level);
|
|
31429
|
+
if (lvl) {
|
|
31430
|
+
const meta = {};
|
|
31431
|
+
for (const [k2, v2] of Object.entries(obj)) {
|
|
31432
|
+
if (k2 === "level" || k2 === "msg" || k2 === "time" || k2 === "pid" || k2 === "hostname") continue;
|
|
31433
|
+
if (k2 === "sessionId") continue;
|
|
31434
|
+
meta[k2] = v2;
|
|
31435
|
+
}
|
|
31436
|
+
client.push({
|
|
31437
|
+
ts: typeof obj.time === "number" ? obj.time : void 0,
|
|
31438
|
+
level: lvl,
|
|
31439
|
+
msg: typeof obj.msg === "string" ? obj.msg : "",
|
|
31440
|
+
meta: Object.keys(meta).length > 0 ? meta : void 0,
|
|
31441
|
+
sessionId: typeof obj.sessionId === "string" ? obj.sessionId : void 0
|
|
31442
|
+
});
|
|
31443
|
+
}
|
|
31444
|
+
}
|
|
31445
|
+
} catch {
|
|
31446
|
+
}
|
|
31447
|
+
cb();
|
|
31448
|
+
}
|
|
31449
|
+
});
|
|
31450
|
+
streams.push({ stream: sink, level });
|
|
31451
|
+
}
|
|
31367
31452
|
const base = (0, import_pino.default)(
|
|
31368
31453
|
{ level, base: { pid: process.pid } },
|
|
31369
31454
|
import_pino.default.multistream(streams)
|
|
31370
31455
|
);
|
|
31371
31456
|
return wrap(base);
|
|
31372
31457
|
}
|
|
31458
|
+
function pinoLevelToString(n) {
|
|
31459
|
+
if (typeof n !== "number") return null;
|
|
31460
|
+
if (n >= 50) return "error";
|
|
31461
|
+
if (n >= 40) return "warn";
|
|
31462
|
+
if (n >= 30) return "info";
|
|
31463
|
+
if (n >= 20) return "debug";
|
|
31464
|
+
return null;
|
|
31465
|
+
}
|
|
31466
|
+
|
|
31467
|
+
// ../log-client/src/types.ts
|
|
31468
|
+
var DEFAULT_BATCH = {
|
|
31469
|
+
maxRecords: 100,
|
|
31470
|
+
maxBytes: 512 * 1024,
|
|
31471
|
+
maxWaitMs: 5e3
|
|
31472
|
+
};
|
|
31473
|
+
var DEFAULT_BUFFER_CAP = 5e3;
|
|
31474
|
+
var DEFAULT_SAMPLING = {
|
|
31475
|
+
debug: 0,
|
|
31476
|
+
info: 1,
|
|
31477
|
+
warn: 1,
|
|
31478
|
+
error: 1
|
|
31479
|
+
};
|
|
31480
|
+
var MAX_MSG_BYTES = 1024;
|
|
31481
|
+
var MAX_META_JSON_BYTES = 2048;
|
|
31482
|
+
var REDACT_KEY_REGEX = /token|secret|password|auth(?!or)|apikey|signsecret/i;
|
|
31483
|
+
var REDACTED_PLACEHOLDER = "[REDACTED]";
|
|
31484
|
+
|
|
31485
|
+
// ../log-client/src/redactor.ts
|
|
31486
|
+
var TRUNCATE_SUFFIX = "\u2026[truncated]";
|
|
31487
|
+
function redactMeta(meta, homeDir) {
|
|
31488
|
+
const cleaned = cleanValue(meta, homeDir, 0);
|
|
31489
|
+
const json = safeStringify(cleaned);
|
|
31490
|
+
if (json.length <= MAX_META_JSON_BYTES) return cleaned;
|
|
31491
|
+
const truncated = json.slice(0, MAX_META_JSON_BYTES) + TRUNCATE_SUFFIX;
|
|
31492
|
+
return { __truncated__: truncated };
|
|
31493
|
+
}
|
|
31494
|
+
function cleanValue(v2, homeDir, depth) {
|
|
31495
|
+
if (depth > 10) return "[depth-limit]";
|
|
31496
|
+
if (v2 == null) return v2;
|
|
31497
|
+
if (typeof v2 === "string") {
|
|
31498
|
+
return homeDir ? v2.split(homeDir).join("~") : v2;
|
|
31499
|
+
}
|
|
31500
|
+
if (typeof v2 !== "object") return v2;
|
|
31501
|
+
if (Array.isArray(v2)) return v2.map((x) => cleanValue(x, homeDir, depth + 1));
|
|
31502
|
+
const out = {};
|
|
31503
|
+
for (const [k2, val] of Object.entries(v2)) {
|
|
31504
|
+
if (REDACT_KEY_REGEX.test(k2)) {
|
|
31505
|
+
out[k2] = REDACTED_PLACEHOLDER;
|
|
31506
|
+
} else {
|
|
31507
|
+
out[k2] = cleanValue(val, homeDir, depth + 1);
|
|
31508
|
+
}
|
|
31509
|
+
}
|
|
31510
|
+
return out;
|
|
31511
|
+
}
|
|
31512
|
+
function safeStringify(v2) {
|
|
31513
|
+
try {
|
|
31514
|
+
return JSON.stringify(v2) ?? "";
|
|
31515
|
+
} catch {
|
|
31516
|
+
return '"[serialize-failed]"';
|
|
31517
|
+
}
|
|
31518
|
+
}
|
|
31519
|
+
function truncateMsg(msg) {
|
|
31520
|
+
if (msg.length <= MAX_MSG_BYTES) return msg;
|
|
31521
|
+
return msg.slice(0, MAX_MSG_BYTES) + TRUNCATE_SUFFIX;
|
|
31522
|
+
}
|
|
31523
|
+
|
|
31524
|
+
// ../log-client/src/sampling.ts
|
|
31525
|
+
function shouldSample(level, config, rng2) {
|
|
31526
|
+
const rate = config[level] ?? DEFAULT_SAMPLING[level];
|
|
31527
|
+
if (rate <= 0) return false;
|
|
31528
|
+
if (rate >= 1) return true;
|
|
31529
|
+
return rng2() < rate;
|
|
31530
|
+
}
|
|
31531
|
+
|
|
31532
|
+
// ../log-client/src/batcher.ts
|
|
31533
|
+
var Batcher = class {
|
|
31534
|
+
constructor(deps) {
|
|
31535
|
+
this.deps = deps;
|
|
31536
|
+
}
|
|
31537
|
+
deps;
|
|
31538
|
+
queue = [];
|
|
31539
|
+
bytes = 0;
|
|
31540
|
+
droppedSinceFlush = 0;
|
|
31541
|
+
identityReady = false;
|
|
31542
|
+
timer = null;
|
|
31543
|
+
disposed = false;
|
|
31544
|
+
pendingSends = [];
|
|
31545
|
+
enqueue(record) {
|
|
31546
|
+
if (this.disposed) return;
|
|
31547
|
+
const sz = approxSize(record);
|
|
31548
|
+
while (this.queue.length >= this.deps.bufferCap) {
|
|
31549
|
+
this.queue.shift();
|
|
31550
|
+
this.droppedSinceFlush++;
|
|
31551
|
+
}
|
|
31552
|
+
this.queue.push(record);
|
|
31553
|
+
this.bytes += sz;
|
|
31554
|
+
if (!this.identityReady) return;
|
|
31555
|
+
this.maybeTrigger();
|
|
31556
|
+
}
|
|
31557
|
+
setIdentityReady(ready) {
|
|
31558
|
+
this.identityReady = ready;
|
|
31559
|
+
if (ready && this.queue.length > 0) this.maybeTrigger();
|
|
31560
|
+
}
|
|
31561
|
+
async flush() {
|
|
31562
|
+
if (this.queue.length > 0) {
|
|
31563
|
+
this.fire("flush");
|
|
31564
|
+
}
|
|
31565
|
+
await Promise.allSettled(this.pendingSends);
|
|
31566
|
+
}
|
|
31567
|
+
stats() {
|
|
31568
|
+
return { queued: this.queue.length, dropped: this.droppedSinceFlush };
|
|
31569
|
+
}
|
|
31570
|
+
async dispose() {
|
|
31571
|
+
if (this.disposed) return;
|
|
31572
|
+
this.disposed = true;
|
|
31573
|
+
if (this.timer) {
|
|
31574
|
+
clearTimeout(this.timer);
|
|
31575
|
+
this.timer = null;
|
|
31576
|
+
}
|
|
31577
|
+
await this.flush();
|
|
31578
|
+
}
|
|
31579
|
+
maybeTrigger() {
|
|
31580
|
+
const { maxRecords, maxBytes, maxWaitMs } = this.deps.batch;
|
|
31581
|
+
if (this.queue.length >= maxRecords) {
|
|
31582
|
+
this.fire("records");
|
|
31583
|
+
return;
|
|
31584
|
+
}
|
|
31585
|
+
if (this.bytes >= maxBytes) {
|
|
31586
|
+
this.fire("bytes");
|
|
31587
|
+
return;
|
|
31588
|
+
}
|
|
31589
|
+
if (this.timer == null) {
|
|
31590
|
+
this.timer = setTimeout(() => {
|
|
31591
|
+
this.timer = null;
|
|
31592
|
+
if (this.queue.length > 0 && this.identityReady) this.fire("time");
|
|
31593
|
+
}, maxWaitMs);
|
|
31594
|
+
}
|
|
31595
|
+
}
|
|
31596
|
+
fire(reason) {
|
|
31597
|
+
if (this.timer) {
|
|
31598
|
+
clearTimeout(this.timer);
|
|
31599
|
+
this.timer = null;
|
|
31600
|
+
}
|
|
31601
|
+
const records = this.queue;
|
|
31602
|
+
const droppedCarryover = this.droppedSinceFlush;
|
|
31603
|
+
this.queue = [];
|
|
31604
|
+
this.bytes = 0;
|
|
31605
|
+
this.droppedSinceFlush = 0;
|
|
31606
|
+
const p2 = this.deps.onBatch({ reason, records, droppedCarryover }).catch(() => {
|
|
31607
|
+
});
|
|
31608
|
+
this.pendingSends.push(p2);
|
|
31609
|
+
p2.finally(() => {
|
|
31610
|
+
this.pendingSends = this.pendingSends.filter((x) => x !== p2);
|
|
31611
|
+
});
|
|
31612
|
+
}
|
|
31613
|
+
};
|
|
31614
|
+
function approxSize(record) {
|
|
31615
|
+
return (record.msg?.length ?? 0) + (record.meta_json?.length ?? 0) + 200;
|
|
31616
|
+
}
|
|
31617
|
+
|
|
31618
|
+
// ../log-client/src/sender.ts
|
|
31619
|
+
var BACKOFF_MS = [1e3, 2e3, 4e3];
|
|
31620
|
+
async function sendBatch(deps, batch) {
|
|
31621
|
+
const body = JSON.stringify({
|
|
31622
|
+
__topic__: deps.topic ?? "clawd",
|
|
31623
|
+
__source__: deps.source,
|
|
31624
|
+
__logs__: batch.records,
|
|
31625
|
+
__dropped__: batch.droppedCarryover
|
|
31626
|
+
});
|
|
31627
|
+
for (let attempt = 0; attempt < BACKOFF_MS.length; attempt++) {
|
|
31628
|
+
if (attempt > 0) await sleep(BACKOFF_MS[attempt - 1]);
|
|
31629
|
+
try {
|
|
31630
|
+
const res = await deps.fetchImpl(deps.endpoint, {
|
|
31631
|
+
method: "POST",
|
|
31632
|
+
headers: { "Content-Type": "application/json" },
|
|
31633
|
+
body
|
|
31634
|
+
});
|
|
31635
|
+
if (res.status >= 200 && res.status < 300) {
|
|
31636
|
+
return { sent: batch.records.length, failed: 0 };
|
|
31637
|
+
}
|
|
31638
|
+
if (res.status >= 400 && res.status < 500) {
|
|
31639
|
+
return { sent: 0, failed: batch.records.length };
|
|
31640
|
+
}
|
|
31641
|
+
} catch {
|
|
31642
|
+
}
|
|
31643
|
+
}
|
|
31644
|
+
return { sent: 0, failed: batch.records.length };
|
|
31645
|
+
}
|
|
31646
|
+
function sleep(ms) {
|
|
31647
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
31648
|
+
}
|
|
31649
|
+
|
|
31650
|
+
// ../log-client/src/client.ts
|
|
31651
|
+
function createLogClient(opts) {
|
|
31652
|
+
const stats = { sent: 0, dropped: 0, queued: 0, failed: 0 };
|
|
31653
|
+
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
31654
|
+
const now = opts.now ?? Date.now;
|
|
31655
|
+
const sampling = { ...DEFAULT_SAMPLING, ...opts.sampling ?? {} };
|
|
31656
|
+
const homeDir = opts.homeDir === void 0 ? null : opts.homeDir;
|
|
31657
|
+
const batchConfig = { ...DEFAULT_BATCH, ...opts.batch ?? {} };
|
|
31658
|
+
const bufferCap = opts.bufferCap ?? DEFAULT_BUFFER_CAP;
|
|
31659
|
+
let identity = opts.identity;
|
|
31660
|
+
let disposed = false;
|
|
31661
|
+
if (opts.endpoint == null) {
|
|
31662
|
+
return {
|
|
31663
|
+
push: () => {
|
|
31664
|
+
if (disposed) return;
|
|
31665
|
+
stats.dropped++;
|
|
31666
|
+
},
|
|
31667
|
+
flush: async () => {
|
|
31668
|
+
},
|
|
31669
|
+
setIdentity: (next) => {
|
|
31670
|
+
identity = next;
|
|
31671
|
+
},
|
|
31672
|
+
stats: () => ({ ...stats }),
|
|
31673
|
+
dispose: async () => {
|
|
31674
|
+
disposed = true;
|
|
31675
|
+
}
|
|
31676
|
+
};
|
|
31677
|
+
}
|
|
31678
|
+
const endpoint = opts.endpoint;
|
|
31679
|
+
const batcher = new Batcher({
|
|
31680
|
+
batch: batchConfig,
|
|
31681
|
+
bufferCap,
|
|
31682
|
+
now,
|
|
31683
|
+
onBatch: async (b2) => {
|
|
31684
|
+
const res = await sendBatch({ endpoint, source: opts.source, fetchImpl }, b2);
|
|
31685
|
+
stats.sent += res.sent;
|
|
31686
|
+
stats.failed += res.failed;
|
|
31687
|
+
}
|
|
31688
|
+
});
|
|
31689
|
+
batcher.setIdentityReady(identity != null);
|
|
31690
|
+
return {
|
|
31691
|
+
push(input) {
|
|
31692
|
+
if (disposed) return;
|
|
31693
|
+
try {
|
|
31694
|
+
if (!shouldSample(input.level, sampling, Math.random)) {
|
|
31695
|
+
stats.dropped++;
|
|
31696
|
+
return;
|
|
31697
|
+
}
|
|
31698
|
+
const record = buildRecord(input, identity, opts.source, homeDir, now);
|
|
31699
|
+
batcher.enqueue(record);
|
|
31700
|
+
} catch {
|
|
31701
|
+
}
|
|
31702
|
+
},
|
|
31703
|
+
async flush() {
|
|
31704
|
+
try {
|
|
31705
|
+
await batcher.flush();
|
|
31706
|
+
} catch {
|
|
31707
|
+
}
|
|
31708
|
+
},
|
|
31709
|
+
setIdentity(next) {
|
|
31710
|
+
identity = next;
|
|
31711
|
+
batcher.setIdentityReady(next != null);
|
|
31712
|
+
},
|
|
31713
|
+
stats() {
|
|
31714
|
+
const bs = batcher.stats();
|
|
31715
|
+
return { ...stats, queued: bs.queued, dropped: stats.dropped + bs.dropped };
|
|
31716
|
+
},
|
|
31717
|
+
async dispose() {
|
|
31718
|
+
disposed = true;
|
|
31719
|
+
try {
|
|
31720
|
+
await batcher.dispose();
|
|
31721
|
+
} catch {
|
|
31722
|
+
}
|
|
31723
|
+
}
|
|
31724
|
+
};
|
|
31725
|
+
}
|
|
31726
|
+
function buildRecord(input, identity, source, homeDir, now) {
|
|
31727
|
+
const cleanedMeta = input.meta ? redactMeta(input.meta, homeDir) : void 0;
|
|
31728
|
+
const meta_json = cleanedMeta ? JSON.stringify(cleanedMeta) : void 0;
|
|
31729
|
+
return {
|
|
31730
|
+
ts: input.ts ?? now(),
|
|
31731
|
+
source,
|
|
31732
|
+
level: input.level,
|
|
31733
|
+
msg: truncateMsg(input.msg),
|
|
31734
|
+
ownerPrincipalId: identity?.ownerPrincipalId ?? "__pending__",
|
|
31735
|
+
deviceId: identity?.deviceId ?? "__pending__",
|
|
31736
|
+
appVersion: identity?.appVersion ?? "__pending__",
|
|
31737
|
+
daemonVersion: identity?.daemonVersion,
|
|
31738
|
+
os: identity?.os ?? "unknown",
|
|
31739
|
+
meta_json,
|
|
31740
|
+
sessionId: input.sessionId
|
|
31741
|
+
};
|
|
31742
|
+
}
|
|
31373
31743
|
|
|
31374
31744
|
// src/session/store-factory.ts
|
|
31375
31745
|
var path5 = __toESM(require("path"), 1);
|
|
@@ -31804,6 +32174,8 @@ function buildSpawnContext(state, deps) {
|
|
|
31804
32174
|
env.CLAWD_SESSION_ID = file.sessionId;
|
|
31805
32175
|
const daemonUrl = deps.getDaemonUrl?.() ?? null;
|
|
31806
32176
|
if (daemonUrl) env.CLAWD_DAEMON_URL = daemonUrl;
|
|
32177
|
+
const dispatchId = deps.lookupDispatchByBSessionId?.(file.sessionId);
|
|
32178
|
+
if (dispatchId) env.CLAWD_DISPATCH_ID = dispatchId;
|
|
31807
32179
|
const dispatchMcpConfigPath2 = deps.getDispatchMcpConfigPath?.() ?? null;
|
|
31808
32180
|
const ctx = {
|
|
31809
32181
|
cwd: file.cwd,
|
|
@@ -32566,9 +32938,10 @@ var SessionRunner = class {
|
|
|
32566
32938
|
// 单栏 refactor (spec 2026-06-02 §5.1): cc 子进程 env 注入 CLAWD_DAEMON_URL,让 assistant
|
|
32567
32939
|
// curl daemon HTTP RPC adapter (/api/rpc/<method>) 触发管理操作。null = HTTP adapter 未启。
|
|
32568
32940
|
getDaemonUrl: this.hooks.getDaemonUrl,
|
|
32569
|
-
// Persona dispatch:透传 dispatch.mcp.json
|
|
32570
|
-
//
|
|
32941
|
+
// Persona dispatch:透传 dispatch.mcp.json 路径闭包 + B→dispatchId 反查闭包。
|
|
32942
|
+
// 见 reducer.buildSpawnContext 注 CLAWD_DISPATCH_ID env / 派生 SpawnContext.dispatchMcpConfigPath
|
|
32571
32943
|
getDispatchMcpConfigPath: this.hooks.getDispatchMcpConfigPath,
|
|
32944
|
+
lookupDispatchByBSessionId: this.hooks.lookupDispatchByBSessionId,
|
|
32572
32945
|
// ReadyGate v2:透传 mode 让 reducer send / ready-detected 分支决定走暂存队列还是直写
|
|
32573
32946
|
mode: this.hooks.mode,
|
|
32574
32947
|
// [RG-DBG] 注入 logger 让 reducer rgDbg 走 pino 进 clawd.log;定位完跟 rgDbg 一起删
|
|
@@ -33040,22 +33413,9 @@ You may read this file with your Read tool to understand context. You do not hav
|
|
|
33040
33413
|
|
|
33041
33414
|
When done, call the MCP tool \`mcp__clawd-dispatch__personaDispatchComplete\` with your result:
|
|
33042
33415
|
- Success: { text: "...", filePaths?: ["abs/path", ...] }
|
|
33043
|
-
- Failure: { isFailure: true, reason: "..." }
|
|
33044
|
-
}
|
|
33045
|
-
function buildDispatchContinuationPack(args) {
|
|
33046
|
-
return `[Dispatched from owner \u2014 follow-up query]
|
|
33047
|
-
|
|
33048
|
-
Although you reported back on the previous task, the owner has a follow-up question. Continue from where you left off.
|
|
33416
|
+
- Failure: { isFailure: true, reason: "..." }
|
|
33049
33417
|
|
|
33050
|
-
|
|
33051
|
-
${args.prompt}
|
|
33052
|
-
|
|
33053
|
-
Source conversation (jsonl, same path as before, may contain new messages):
|
|
33054
|
-
${args.sourceJsonlPath}
|
|
33055
|
-
|
|
33056
|
-
When done, call \`mcp__clawd-dispatch__personaDispatchComplete\` again with your result:
|
|
33057
|
-
- Success: { text: "...", filePaths?: ["abs/path", ...] }
|
|
33058
|
-
- Failure: { isFailure: true, reason: "..." }`;
|
|
33418
|
+
dispatchId for this task: ${args.dispatchId}`;
|
|
33059
33419
|
}
|
|
33060
33420
|
function derivePersonaSpawnCwd(file, personaRoot) {
|
|
33061
33421
|
const personaId = file.ownerPersonaId;
|
|
@@ -33257,23 +33617,6 @@ var SessionManager = class {
|
|
|
33257
33617
|
const file = this.findOwnedSession(sessionId);
|
|
33258
33618
|
return file ? this.scopeForFile(file) : null;
|
|
33259
33619
|
}
|
|
33260
|
-
/**
|
|
33261
|
-
* 反查 (sourceSessionId, targetPersona) 最近一次被 dispatched 出的 B session。
|
|
33262
|
-
* mode='continue' 路径用:命中 → 复用该 B;不命中 → fallback 走新建路径。
|
|
33263
|
-
*
|
|
33264
|
-
* 反查源是 SessionStore 持久化字段(dispatchedFromSessionId + ownerPersonaId),不引入新表,
|
|
33265
|
-
* daemon 重启不丢。同对历史多条按 updatedAt 取最近(reducer 写入时持续更新)。
|
|
33266
|
-
*/
|
|
33267
|
-
findLatestDispatchedSession(args) {
|
|
33268
|
-
const candidates = this.listAllOwned().filter(
|
|
33269
|
-
(f) => f.dispatchedFromSessionId === args.sourceSessionId && f.ownerPersonaId === args.targetPersona
|
|
33270
|
-
);
|
|
33271
|
-
if (candidates.length === 0) return null;
|
|
33272
|
-
candidates.sort(
|
|
33273
|
-
(a, b2) => a.updatedAt > b2.updatedAt ? -1 : a.updatedAt < b2.updatedAt ? 1 : 0
|
|
33274
|
-
);
|
|
33275
|
-
return candidates[0];
|
|
33276
|
-
}
|
|
33277
33620
|
// 合并 default + 每个 persona 的 owner/ + guests/<capId>/ —— 桌面 App 主 session 列表入口.
|
|
33278
33621
|
// guest sessions 由 handlers/session.ts list 处按 ctx.principal.kind === 'guest' 做权限过滤
|
|
33279
33622
|
// (creatorPrincipalId === capId), 这里仅负责数据源汇总.
|
|
@@ -33380,9 +33723,10 @@ var SessionManager = class {
|
|
|
33380
33723
|
// 单栏 refactor (spec 2026-06-02 §5.1): 透传 daemon HTTP RPC base URL 闭包,
|
|
33381
33724
|
// reducer 把它注入 cc 子进程 env CLAWD_DAEMON_URL.
|
|
33382
33725
|
getDaemonUrl: this.deps.getDaemonUrl,
|
|
33383
|
-
// Persona dispatch: dispatch.mcp.json
|
|
33384
|
-
//
|
|
33726
|
+
// Persona dispatch (Task 7): dispatch.mcp.json 路径 + B sessionId → dispatchId 反查
|
|
33727
|
+
// 闭包透传给 reducer,让 cc spawn 加 --mcp-config flag + B session 注 CLAWD_DISPATCH_ID env.
|
|
33385
33728
|
getDispatchMcpConfigPath: this.deps.dispatchMcpConfigPath ? () => this.deps.dispatchMcpConfigPath ?? null : void 0,
|
|
33729
|
+
lookupDispatchByBSessionId: this.deps.personaDispatchManager ? (bSid) => this.deps.personaDispatchManager?.lookupDispatchByBSessionId(bSid) : void 0,
|
|
33386
33730
|
// file-sharing (spec §6 PR 3):闭包 scope + sessionId,runner 只暴露 tool/relPath/cwd
|
|
33387
33731
|
onFileEdit: attachmentGroup ? (input) => attachmentGroup.onFileEdit({
|
|
33388
33732
|
scope,
|
|
@@ -34200,11 +34544,10 @@ var SessionManager = class {
|
|
|
34200
34544
|
* Persona dispatch: 启动 B session 并投递任务包当第一条 user message。
|
|
34201
34545
|
*
|
|
34202
34546
|
* - 复用 persona-owner 创建路径(cwd=personaDir、scope=persona-owner、不沙箱)
|
|
34203
|
-
* - SessionFile 上写 dispatchedFromSessionId 关联回 A(UI 折叠用;mirror 一侧 strip
|
|
34204
|
-
*
|
|
34205
|
-
*
|
|
34206
|
-
*
|
|
34207
|
-
* - 投任务包;包尾教 B 用 personaDispatchComplete tool 回传
|
|
34547
|
+
* - SessionFile 上写 dispatchedFromSessionId 关联回 A(UI 折叠用;mirror 一侧 strip)
|
|
34548
|
+
* - 跟 PersonaDispatchManager 双向登记 dispatchId ↔ B sessionId,让后续 reducer
|
|
34549
|
+
* buildSpawnContext 能反查到 dispatchId 注 CLAWD_DISPATCH_ID env
|
|
34550
|
+
* - 投 inject-owner-text 任务包;包尾教 B 用 personaDispatchComplete tool 回传
|
|
34208
34551
|
*
|
|
34209
34552
|
* cwd 派生与 SessionManager.create 的 owner persona 路径一致(derivePersonaSpawnCwd)。
|
|
34210
34553
|
*/
|
|
@@ -34247,7 +34590,8 @@ var SessionManager = class {
|
|
|
34247
34590
|
});
|
|
34248
34591
|
const taskPack = buildDispatchTaskPack({
|
|
34249
34592
|
prompt: args.prompt,
|
|
34250
|
-
sourceJsonlPath: args.sourceJsonlPath
|
|
34593
|
+
sourceJsonlPath: args.sourceJsonlPath,
|
|
34594
|
+
dispatchId: args.dispatchId
|
|
34251
34595
|
});
|
|
34252
34596
|
runner.input({ kind: "command", command: { kind: "send", text: taskPack } });
|
|
34253
34597
|
this.deps.logger?.info("dispatch.createDispatchedSession.task-pack-sent", {
|
|
@@ -34256,41 +34600,6 @@ var SessionManager = class {
|
|
|
34256
34600
|
});
|
|
34257
34601
|
return { sessionId };
|
|
34258
34602
|
}
|
|
34259
|
-
/**
|
|
34260
|
-
* mode='continue' 复用已有 B session,投 continuation pack 给 runner。
|
|
34261
|
-
*
|
|
34262
|
-
* 关键差异 vs createDispatchedSession:
|
|
34263
|
-
* - 不 newSessionId() / 不 storeFor(scope).write()——SessionFile 复用
|
|
34264
|
-
* - 跟 PersonaDispatchManager 重新登记 dispatchId → bSessionId(新 dispatchId、同 bSessionId)
|
|
34265
|
-
* - ensureRunnerForScope 在 runner 不存在时建;存在则复用
|
|
34266
|
-
* - runner.input send 投 continuationPack:runner / reducer 内部决定 cc 死活;
|
|
34267
|
-
* cc 活 → 自然续;cc 死 → spawn 时 jsonl 已存在自动 --resume <toolSessionId>
|
|
34268
|
-
*/
|
|
34269
|
-
resumeDispatchedSession(args) {
|
|
34270
|
-
if (!this.deps.personaDispatchManager) {
|
|
34271
|
-
throw new Error("resumeDispatchedSession: personaDispatchManager missing in ManagerDeps");
|
|
34272
|
-
}
|
|
34273
|
-
const file = this.findOwnedSession(args.bSessionId);
|
|
34274
|
-
if (!file) {
|
|
34275
|
-
throw new Error(`resumeDispatchedSession: B session ${args.bSessionId} not found`);
|
|
34276
|
-
}
|
|
34277
|
-
const scope = this.scopeForFile(file);
|
|
34278
|
-
this.deps.personaDispatchManager.registerBSession(args.dispatchId, args.bSessionId);
|
|
34279
|
-
this.deps.logger?.info("dispatch.resumeDispatchedSession.registered", {
|
|
34280
|
-
dispatchId: args.dispatchId,
|
|
34281
|
-
bSessionId: args.bSessionId
|
|
34282
|
-
});
|
|
34283
|
-
const runner = this.ensureRunnerForScope(file, scope);
|
|
34284
|
-
const pack = buildDispatchContinuationPack({
|
|
34285
|
-
prompt: args.prompt,
|
|
34286
|
-
sourceJsonlPath: args.sourceJsonlPath
|
|
34287
|
-
});
|
|
34288
|
-
runner.input({ kind: "command", command: { kind: "send", text: pack } });
|
|
34289
|
-
this.deps.logger?.info("dispatch.resumeDispatchedSession.continuation-sent", {
|
|
34290
|
-
dispatchId: args.dispatchId,
|
|
34291
|
-
bSessionId: args.bSessionId
|
|
34292
|
-
});
|
|
34293
|
-
}
|
|
34294
34603
|
/**
|
|
34295
34604
|
* 老板插话:把 'inject-owner-text' input 喂给对应 runner,
|
|
34296
34605
|
* runner 不存在时按 scope 懒创建。
|
|
@@ -36376,7 +36685,7 @@ init_claude_history();
|
|
|
36376
36685
|
|
|
36377
36686
|
// src/pty/pty-child-process.ts
|
|
36378
36687
|
var import_node_events = require("events");
|
|
36379
|
-
var
|
|
36688
|
+
var import_node_stream2 = require("stream");
|
|
36380
36689
|
var PtyChildProcess = class extends import_node_events.EventEmitter {
|
|
36381
36690
|
constructor(pty, opts = {}) {
|
|
36382
36691
|
super();
|
|
@@ -36405,7 +36714,7 @@ var PtyChildProcess = class extends import_node_events.EventEmitter {
|
|
|
36405
36714
|
pty.write(data);
|
|
36406
36715
|
}
|
|
36407
36716
|
};
|
|
36408
|
-
this.stdin = new
|
|
36717
|
+
this.stdin = new import_node_stream2.Writable({
|
|
36409
36718
|
decodeStrings: false,
|
|
36410
36719
|
write: (chunk, _enc, cb) => {
|
|
36411
36720
|
const s = typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
@@ -36424,7 +36733,7 @@ var PtyChildProcess = class extends import_node_events.EventEmitter {
|
|
|
36424
36733
|
}
|
|
36425
36734
|
}
|
|
36426
36735
|
});
|
|
36427
|
-
this.stdout = new
|
|
36736
|
+
this.stdout = new import_node_stream2.Readable({ read() {
|
|
36428
36737
|
} });
|
|
36429
36738
|
pty.onData((data) => {
|
|
36430
36739
|
if (!this.firstDataLogged) {
|
|
@@ -36437,7 +36746,7 @@ var PtyChildProcess = class extends import_node_events.EventEmitter {
|
|
|
36437
36746
|
}
|
|
36438
36747
|
this.stdout.push(Buffer.from(data, "utf8"));
|
|
36439
36748
|
});
|
|
36440
|
-
this.stderr = new
|
|
36749
|
+
this.stderr = new import_node_stream2.Readable({ read() {
|
|
36441
36750
|
} });
|
|
36442
36751
|
pty.onExit(({ exitCode, signal }) => {
|
|
36443
36752
|
this.exitCode = exitCode;
|
|
@@ -36992,7 +37301,6 @@ var PersonaDispatchManager = class {
|
|
|
36992
37301
|
dispatchId,
|
|
36993
37302
|
sourceSessionId: args.sourceSessionId,
|
|
36994
37303
|
targetPersona: args.targetPersona,
|
|
36995
|
-
bSessionId: args.bSessionId,
|
|
36996
37304
|
waiters: [],
|
|
36997
37305
|
outcome: null
|
|
36998
37306
|
});
|
|
@@ -37023,22 +37331,10 @@ var PersonaDispatchManager = class {
|
|
|
37023
37331
|
if (!state) throw new Error(`unknown dispatchId: ${dispatchId}`);
|
|
37024
37332
|
state.bSessionId = bSessionId;
|
|
37025
37333
|
}
|
|
37026
|
-
/**
|
|
37027
|
-
|
|
37028
|
-
* complete handler 用这个把 B 的 outcome 配对回 A 的 waiter——dispatchId 不再走 cc env,
|
|
37029
|
-
* 全靠 (bSessionId → in-flight dispatchId) 反查。
|
|
37030
|
-
*
|
|
37031
|
-
* 同一 bSessionId 不会同时有多个 in-flight(A 拿不到 outcome 不会发起新 dispatch;
|
|
37032
|
-
* mode='continue' 入口已防御重入)。
|
|
37033
|
-
*
|
|
37034
|
-
* 历史 entry(已 resolved)暂不清——单 daemon lifetime 累积可接受;未来用量大了加 TTL GC
|
|
37035
|
-
* (complete 后 5min 删 entry)。
|
|
37036
|
-
*/
|
|
37037
|
-
findInflightDispatchByBSessionId(bSessionId) {
|
|
37334
|
+
/** reducer buildSpawnContext 调,按 B 的 sessionId 反查 dispatchId(用于注 CLAWD_DISPATCH_ID env) */
|
|
37335
|
+
lookupDispatchByBSessionId(bSessionId) {
|
|
37038
37336
|
for (const state of this.map.values()) {
|
|
37039
|
-
if (state.bSessionId === bSessionId
|
|
37040
|
-
return state.dispatchId;
|
|
37041
|
-
}
|
|
37337
|
+
if (state.bSessionId === bSessionId) return state.dispatchId;
|
|
37042
37338
|
}
|
|
37043
37339
|
return void 0;
|
|
37044
37340
|
}
|
|
@@ -37070,15 +37366,6 @@ function writeDispatchMcpConfig(args) {
|
|
|
37070
37366
|
|
|
37071
37367
|
// src/handlers/persona-dispatch.ts
|
|
37072
37368
|
init_protocol();
|
|
37073
|
-
var NO_PREVIOUS_PREFIX = "(no previous session found, started fresh)";
|
|
37074
|
-
function prefixNoPreviousOutcome(outcome) {
|
|
37075
|
-
if (outcome.kind === "success") {
|
|
37076
|
-
return { ...outcome, text: `${NO_PREVIOUS_PREFIX}
|
|
37077
|
-
|
|
37078
|
-
${outcome.text}` };
|
|
37079
|
-
}
|
|
37080
|
-
return { ...outcome, reason: `${NO_PREVIOUS_PREFIX} ${outcome.reason}` };
|
|
37081
|
-
}
|
|
37082
37369
|
function buildPersonaDispatchHandlers(deps) {
|
|
37083
37370
|
const { personaDispatchManager: mgr, spawnB, logger } = deps;
|
|
37084
37371
|
const run = async (frame) => {
|
|
@@ -37090,66 +37377,26 @@ function buildPersonaDispatchHandlers(deps) {
|
|
|
37090
37377
|
"personaDispatch:run requires sessionId (caller must pass x-clawd-session-id header)"
|
|
37091
37378
|
);
|
|
37092
37379
|
}
|
|
37093
|
-
const requestedMode = args.mode ?? "new";
|
|
37094
|
-
let route = "new";
|
|
37095
|
-
let bSessionId;
|
|
37096
|
-
let noPrevious = false;
|
|
37097
|
-
if (requestedMode === "continue") {
|
|
37098
|
-
const bFile = deps.findLatestDispatchedSession?.({
|
|
37099
|
-
sourceSessionId,
|
|
37100
|
-
targetPersona: args.targetPersona
|
|
37101
|
-
});
|
|
37102
|
-
if (bFile) {
|
|
37103
|
-
const inflight = deps.findInflightDispatchByBSessionId?.(bFile.sessionId);
|
|
37104
|
-
if (inflight) {
|
|
37105
|
-
logger?.warn("dispatch.run.continue.busy", {
|
|
37106
|
-
sourceSessionId,
|
|
37107
|
-
targetPersona: args.targetPersona,
|
|
37108
|
-
bSessionId: bFile.sessionId,
|
|
37109
|
-
inflightDispatchId: inflight
|
|
37110
|
-
});
|
|
37111
|
-
return {
|
|
37112
|
-
response: {
|
|
37113
|
-
type: "personaDispatch:run:ok",
|
|
37114
|
-
outcome: {
|
|
37115
|
-
kind: "failure",
|
|
37116
|
-
reason: "previous dispatch to this persona session not yet complete"
|
|
37117
|
-
}
|
|
37118
|
-
}
|
|
37119
|
-
};
|
|
37120
|
-
}
|
|
37121
|
-
route = "continue";
|
|
37122
|
-
bSessionId = bFile.sessionId;
|
|
37123
|
-
} else {
|
|
37124
|
-
noPrevious = true;
|
|
37125
|
-
}
|
|
37126
|
-
}
|
|
37127
37380
|
const { dispatchId } = mgr.start({
|
|
37128
37381
|
sourceSessionId,
|
|
37129
|
-
targetPersona: args.targetPersona
|
|
37130
|
-
bSessionId
|
|
37382
|
+
targetPersona: args.targetPersona
|
|
37131
37383
|
});
|
|
37132
37384
|
logger?.info("dispatch.run.received", {
|
|
37133
37385
|
dispatchId,
|
|
37134
37386
|
sourceSessionId,
|
|
37135
37387
|
targetPersona: args.targetPersona,
|
|
37136
|
-
promptLen: args.prompt.length
|
|
37137
|
-
requestedMode,
|
|
37138
|
-
route,
|
|
37139
|
-
noPrevious
|
|
37388
|
+
promptLen: args.prompt.length
|
|
37140
37389
|
});
|
|
37141
37390
|
void spawnB({
|
|
37142
37391
|
dispatchId,
|
|
37143
37392
|
sourceSessionId,
|
|
37144
37393
|
targetPersona: args.targetPersona,
|
|
37145
|
-
prompt: args.prompt
|
|
37146
|
-
route,
|
|
37147
|
-
bSessionId
|
|
37394
|
+
prompt: args.prompt
|
|
37148
37395
|
}).then(() => {
|
|
37149
|
-
logger?.info("dispatch.spawnB.ok", { dispatchId
|
|
37396
|
+
logger?.info("dispatch.spawnB.ok", { dispatchId });
|
|
37150
37397
|
}).catch((err) => {
|
|
37151
37398
|
const reason = err instanceof Error ? err.message : String(err);
|
|
37152
|
-
logger?.warn("dispatch.spawnB.failed", { dispatchId,
|
|
37399
|
+
logger?.warn("dispatch.spawnB.failed", { dispatchId, reason });
|
|
37153
37400
|
mgr.complete(dispatchId, {
|
|
37154
37401
|
kind: "failure",
|
|
37155
37402
|
reason: `failed to spawn B: ${reason}`
|
|
@@ -37161,32 +37408,18 @@ function buildPersonaDispatchHandlers(deps) {
|
|
|
37161
37408
|
dispatchId,
|
|
37162
37409
|
outcomeKind: outcome.kind
|
|
37163
37410
|
});
|
|
37164
|
-
const finalOutcome = noPrevious ? prefixNoPreviousOutcome(outcome) : outcome;
|
|
37165
37411
|
return {
|
|
37166
|
-
response: { type: "personaDispatch:run:ok", outcome
|
|
37412
|
+
response: { type: "personaDispatch:run:ok", outcome }
|
|
37167
37413
|
};
|
|
37168
37414
|
};
|
|
37169
37415
|
const complete = async (frame) => {
|
|
37170
37416
|
const { type: _t, requestId: _r, ...rest } = frame;
|
|
37171
|
-
const sessionId = typeof rest.sessionId === "string" ? rest.sessionId : void 0;
|
|
37172
37417
|
const args = DispatchCompleteArgsSchema.parse(rest);
|
|
37173
|
-
if (!sessionId) {
|
|
37174
|
-
throw new Error(
|
|
37175
|
-
"personaDispatch:complete requires sessionId (caller must pass x-clawd-session-id header)"
|
|
37176
|
-
);
|
|
37177
|
-
}
|
|
37178
|
-
const dispatchId = mgr.findInflightDispatchByBSessionId(sessionId);
|
|
37179
|
-
if (!dispatchId) {
|
|
37180
|
-
throw new Error(
|
|
37181
|
-
`no in-flight dispatch for session ${sessionId} (already completed or B was never dispatched)`
|
|
37182
|
-
);
|
|
37183
|
-
}
|
|
37184
37418
|
logger?.info("dispatch.complete.received", {
|
|
37185
|
-
dispatchId,
|
|
37186
|
-
sessionId,
|
|
37419
|
+
dispatchId: args.dispatchId,
|
|
37187
37420
|
outcomeKind: args.outcome.kind
|
|
37188
37421
|
});
|
|
37189
|
-
mgr.complete(dispatchId, args.outcome);
|
|
37422
|
+
mgr.complete(args.dispatchId, args.outcome);
|
|
37190
37423
|
return {
|
|
37191
37424
|
response: { type: "personaDispatch:complete:ok" }
|
|
37192
37425
|
};
|
|
@@ -38043,7 +38276,7 @@ var import_stream = __toESM(require_stream(), 1);
|
|
|
38043
38276
|
var import_extension = __toESM(require_extension(), 1);
|
|
38044
38277
|
var import_permessage_deflate = __toESM(require_permessage_deflate(), 1);
|
|
38045
38278
|
var import_receiver = __toESM(require_receiver(), 1);
|
|
38046
|
-
var
|
|
38279
|
+
var import_sender2 = __toESM(require_sender(), 1);
|
|
38047
38280
|
var import_subprotocol = __toESM(require_subprotocol(), 1);
|
|
38048
38281
|
var import_websocket = __toESM(require_websocket(), 1);
|
|
38049
38282
|
var import_websocket_server = __toESM(require_websocket_server(), 1);
|
|
@@ -40593,7 +40826,7 @@ var import_node_fs23 = __toESM(require("fs"), 1);
|
|
|
40593
40826
|
var import_node_os11 = __toESM(require("os"), 1);
|
|
40594
40827
|
var import_node_path23 = __toESM(require("path"), 1);
|
|
40595
40828
|
var import_node_child_process7 = require("child_process");
|
|
40596
|
-
var
|
|
40829
|
+
var import_node_stream3 = require("stream");
|
|
40597
40830
|
var import_promises3 = require("stream/promises");
|
|
40598
40831
|
var FRPC_VERSION = "0.68.0";
|
|
40599
40832
|
var UnsupportedPlatformError = class extends Error {
|
|
@@ -40684,7 +40917,7 @@ async function downloadToFile(url, dest, fetchImpl) {
|
|
|
40684
40917
|
throw new Error(`download failed: ${res.status} ${res.statusText}`);
|
|
40685
40918
|
}
|
|
40686
40919
|
const out = import_node_fs23.default.createWriteStream(dest);
|
|
40687
|
-
const nodeStream =
|
|
40920
|
+
const nodeStream = import_node_stream3.Readable.fromWeb(res.body);
|
|
40688
40921
|
await (0, import_promises3.pipeline)(nodeStream, out);
|
|
40689
40922
|
}
|
|
40690
40923
|
async function extractFrpcFromTarball(tarball, binDir, version2, platform, destBin) {
|
|
@@ -40759,7 +40992,7 @@ async function killStaleFrpc(deps) {
|
|
|
40759
40992
|
const isAlive = deps.isPidAliveImpl ?? defaultIsPidAlive;
|
|
40760
40993
|
const killPid = deps.killPidImpl ?? defaultKillPid;
|
|
40761
40994
|
const scanPids = deps.scanFrpcPidsImpl ?? ((tp) => defaultScanFrpcPidsByCmdline(tp, deps.logger));
|
|
40762
|
-
const
|
|
40995
|
+
const sleep2 = deps.sleepImpl ?? defaultSleep;
|
|
40763
40996
|
const victims = /* @__PURE__ */ new Set();
|
|
40764
40997
|
const raw = readPidFile(pidFile);
|
|
40765
40998
|
if (raw) {
|
|
@@ -40787,7 +41020,7 @@ async function killStaleFrpc(deps) {
|
|
|
40787
41020
|
deps.logger?.warn("tunnel: killing stale frpc before respawn", { pid });
|
|
40788
41021
|
killPid(pid, "SIGKILL");
|
|
40789
41022
|
}
|
|
40790
|
-
await
|
|
41023
|
+
await sleep2(deps.reapWaitMs ?? 300);
|
|
40791
41024
|
try {
|
|
40792
41025
|
import_node_fs24.default.unlinkSync(pidFile);
|
|
40793
41026
|
} catch {
|
|
@@ -42704,6 +42937,7 @@ function buildReadyFrame(deps, client) {
|
|
|
42704
42937
|
if (deps.httpToken) fileSharing.httpToken = deps.httpToken;
|
|
42705
42938
|
}
|
|
42706
42939
|
const daemonSource = process.env.CLAWD_DAEMON_SOURCE === "ota" ? "ota" : "packaged";
|
|
42940
|
+
const logShipping = deps.getLogShipping ? deps.getLogShipping() : null;
|
|
42707
42941
|
return {
|
|
42708
42942
|
version,
|
|
42709
42943
|
protocolVersion: PROTOCOL_VERSION,
|
|
@@ -42714,7 +42948,8 @@ function buildReadyFrame(deps, client) {
|
|
|
42714
42948
|
tunnelUrl,
|
|
42715
42949
|
mode: deps.mode,
|
|
42716
42950
|
daemonSource,
|
|
42717
|
-
...fileSharing
|
|
42951
|
+
...fileSharing,
|
|
42952
|
+
...logShipping ? { logShipping } : {}
|
|
42718
42953
|
};
|
|
42719
42954
|
}
|
|
42720
42955
|
function buildMetaHandlers(deps) {
|
|
@@ -45665,9 +45900,25 @@ var BundleCache = class {
|
|
|
45665
45900
|
// src/index.ts
|
|
45666
45901
|
var import_meta4 = {};
|
|
45667
45902
|
async function startDaemon(config) {
|
|
45903
|
+
const authFile = loadOrCreateAuthFile({ dataDir: config.dataDir });
|
|
45904
|
+
const logShippingCfg = config.logShipping ?? { endpoint: null, sampling: {}, mode: "off" };
|
|
45905
|
+
const logClient = createLogClient({
|
|
45906
|
+
endpoint: logShippingCfg.endpoint,
|
|
45907
|
+
identity: {
|
|
45908
|
+
ownerPrincipalId: authFile.ownerPrincipalId,
|
|
45909
|
+
deviceId: authFile.deviceId,
|
|
45910
|
+
appVersion: version,
|
|
45911
|
+
daemonVersion: version,
|
|
45912
|
+
os: `${process.platform}-${process.arch}`
|
|
45913
|
+
},
|
|
45914
|
+
source: "daemon",
|
|
45915
|
+
sampling: logShippingCfg.sampling,
|
|
45916
|
+
homeDir: import_node_os19.default.homedir()
|
|
45917
|
+
});
|
|
45668
45918
|
const logger = createLogger({
|
|
45669
45919
|
level: config.logLevel,
|
|
45670
|
-
file: import_node_path46.default.join(config.dataDir, "clawd.log")
|
|
45920
|
+
file: import_node_path46.default.join(config.dataDir, "clawd.log"),
|
|
45921
|
+
logClient
|
|
45671
45922
|
});
|
|
45672
45923
|
logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
|
|
45673
45924
|
const stateMgr = new StateFileManager({ dataDir: config.dataDir });
|
|
@@ -45678,7 +45929,6 @@ async function startDaemon(config) {
|
|
|
45678
45929
|
if (pre.status === "stale") {
|
|
45679
45930
|
logger.warn("stale state file detected, overwriting", { pid: pre.existing.pid });
|
|
45680
45931
|
}
|
|
45681
|
-
const authFile = loadOrCreateAuthFile({ dataDir: config.dataDir });
|
|
45682
45932
|
let resolvedAuthToken = null;
|
|
45683
45933
|
if (config.authToken && config.authToken.trim()) {
|
|
45684
45934
|
resolvedAuthToken = config.authToken.trim();
|
|
@@ -45862,9 +46112,9 @@ async function startDaemon(config) {
|
|
|
45862
46112
|
// 127.0.0.1(不是 config.host)—— cc 跑在本机,loopback 最稳;外部访问限制 + http-router
|
|
45863
46113
|
// 的 isLoopback 兜底已确保安全。
|
|
45864
46114
|
getDaemonUrl: () => `http://127.0.0.1:${config.port}`,
|
|
45865
|
-
// Persona dispatch: manager 通过这两个 deps 跟 PersonaDispatchManager 协作。
|
|
45866
|
-
// - personaDispatchManager: createDispatchedSession
|
|
45867
|
-
//
|
|
46115
|
+
// Persona dispatch (Task 8): manager 通过这两个 deps 跟 PersonaDispatchManager 协作。
|
|
46116
|
+
// - personaDispatchManager: manager.createDispatchedSession 用它 registerBSession;
|
|
46117
|
+
// reducer 通过闭包反查 dispatchId 注 CLAWD_DISPATCH_ID env。
|
|
45868
46118
|
// - dispatchMcpConfigPath: reducer 透传到 SpawnContext,cc spawn 加 --mcp-config flag。
|
|
45869
46119
|
personaDispatchManager,
|
|
45870
46120
|
dispatchMcpConfigPath: dispatchMcpConfigPath2,
|
|
@@ -46193,16 +46443,11 @@ async function startDaemon(config) {
|
|
|
46193
46443
|
const dispatchHandlers = buildPersonaDispatchHandlers({
|
|
46194
46444
|
personaDispatchManager,
|
|
46195
46445
|
logger,
|
|
46196
|
-
// continue 路径反查(命中 → 走 resumeDispatchedSession;不命中 → fallback 新建 + outcome 前缀)
|
|
46197
|
-
findLatestDispatchedSession: (q) => manager.findLatestDispatchedSession(q),
|
|
46198
|
-
// continue 路径防御:B 仍有 in-flight 时直接 fail(老板规则不会触发,但兜底)
|
|
46199
|
-
findInflightDispatchByBSessionId: (sid) => personaDispatchManager.findInflightDispatchByBSessionId(sid),
|
|
46200
46446
|
spawnB: async (args) => {
|
|
46201
46447
|
logger.info("dispatch.spawnB.start", {
|
|
46202
46448
|
dispatchId: args.dispatchId,
|
|
46203
46449
|
targetPersona: args.targetPersona,
|
|
46204
|
-
sourceSessionId: args.sourceSessionId
|
|
46205
|
-
route: args.route
|
|
46450
|
+
sourceSessionId: args.sourceSessionId
|
|
46206
46451
|
});
|
|
46207
46452
|
const sourceFile = manager.findOwnedSession(args.sourceSessionId);
|
|
46208
46453
|
if (!sourceFile) {
|
|
@@ -46218,28 +46463,15 @@ async function startDaemon(config) {
|
|
|
46218
46463
|
logger.info("dispatch.spawnB.source-resolved", {
|
|
46219
46464
|
dispatchId: args.dispatchId,
|
|
46220
46465
|
sourceJsonlPath,
|
|
46221
|
-
hasToolSessionId: Boolean(sourceFile.toolSessionId)
|
|
46222
|
-
|
|
46466
|
+
hasToolSessionId: Boolean(sourceFile.toolSessionId)
|
|
46467
|
+
});
|
|
46468
|
+
manager.createDispatchedSession({
|
|
46469
|
+
dispatchId: args.dispatchId,
|
|
46470
|
+
sourceSessionId: args.sourceSessionId,
|
|
46471
|
+
targetPersona: args.targetPersona,
|
|
46472
|
+
prompt: args.prompt,
|
|
46473
|
+
sourceJsonlPath
|
|
46223
46474
|
});
|
|
46224
|
-
if (args.route === "continue") {
|
|
46225
|
-
if (!args.bSessionId) {
|
|
46226
|
-
throw new Error("continue route requires bSessionId");
|
|
46227
|
-
}
|
|
46228
|
-
manager.resumeDispatchedSession({
|
|
46229
|
-
dispatchId: args.dispatchId,
|
|
46230
|
-
bSessionId: args.bSessionId,
|
|
46231
|
-
prompt: args.prompt,
|
|
46232
|
-
sourceJsonlPath
|
|
46233
|
-
});
|
|
46234
|
-
} else {
|
|
46235
|
-
manager.createDispatchedSession({
|
|
46236
|
-
dispatchId: args.dispatchId,
|
|
46237
|
-
sourceSessionId: args.sourceSessionId,
|
|
46238
|
-
targetPersona: args.targetPersona,
|
|
46239
|
-
prompt: args.prompt,
|
|
46240
|
-
sourceJsonlPath
|
|
46241
|
-
});
|
|
46242
|
-
}
|
|
46243
46475
|
}
|
|
46244
46476
|
});
|
|
46245
46477
|
handlers = { ...handlers, ...dispatchHandlers };
|
|
@@ -46317,6 +46549,10 @@ async function startDaemon(config) {
|
|
|
46317
46549
|
manager,
|
|
46318
46550
|
getAdapter,
|
|
46319
46551
|
getTunnelUrl: () => currentTunnelUrl,
|
|
46552
|
+
getLogShipping: () => config.logShipping ? {
|
|
46553
|
+
endpoint: config.logShipping.endpoint,
|
|
46554
|
+
sampling: config.logShipping.sampling
|
|
46555
|
+
} : null,
|
|
46320
46556
|
// ready 帧 mode = daemon CC spawn 模式('sdk' | 'tui');UI 用它挂 XtermPanel
|
|
46321
46557
|
mode: config.mode,
|
|
46322
46558
|
// file-sharing 字段:httpBaseUrl 跟 tunnel 状态走;httpToken 复用 owner WS token
|
|
@@ -46547,6 +46783,7 @@ ${bar}
|
|
|
46547
46783
|
}
|
|
46548
46784
|
await wss.stop();
|
|
46549
46785
|
stateMgr.delete();
|
|
46786
|
+
if (logClient) await logClient.dispose();
|
|
46550
46787
|
};
|
|
46551
46788
|
return {
|
|
46552
46789
|
stop: shutdown,
|
|
@@ -39574,11 +39574,9 @@ async function handlePersonaDispatchToolCall(input, ctx) {
|
|
|
39574
39574
|
"content-type": "application/json",
|
|
39575
39575
|
"x-clawd-session-id": ctx.sessionId
|
|
39576
39576
|
},
|
|
39577
|
-
// input.mode === undefined → JSON.stringify omit;daemon parse 用 .optional() 接住缺省。
|
|
39578
39577
|
body: JSON.stringify({
|
|
39579
39578
|
targetPersona: input.targetPersona,
|
|
39580
|
-
prompt: input.prompt
|
|
39581
|
-
mode: input.mode
|
|
39579
|
+
prompt: input.prompt
|
|
39582
39580
|
})
|
|
39583
39581
|
});
|
|
39584
39582
|
} catch (err) {
|
|
@@ -39628,16 +39626,16 @@ async function handlePersonaDispatchCompleteToolCall(input, ctx) {
|
|
|
39628
39626
|
isFailure: Boolean(input.isFailure),
|
|
39629
39627
|
textLen: input.text?.length ?? 0,
|
|
39630
39628
|
filePathCount: input.filePaths?.length ?? 0,
|
|
39631
|
-
|
|
39629
|
+
dispatchId: ctx.dispatchId,
|
|
39632
39630
|
daemonUrl: ctx.daemonUrl
|
|
39633
39631
|
});
|
|
39634
|
-
if (!ctx.
|
|
39635
|
-
log("personaDispatchComplete
|
|
39632
|
+
if (!ctx.dispatchId) {
|
|
39633
|
+
log("personaDispatchComplete CLAWD_DISPATCH_ID missing");
|
|
39636
39634
|
return {
|
|
39637
39635
|
content: [
|
|
39638
39636
|
{
|
|
39639
39637
|
type: "text",
|
|
39640
|
-
text: "Error:
|
|
39638
|
+
text: "Error: CLAWD_DISPATCH_ID env not set; this session was not dispatched."
|
|
39641
39639
|
}
|
|
39642
39640
|
],
|
|
39643
39641
|
isError: true
|
|
@@ -39652,15 +39650,14 @@ async function handlePersonaDispatchCompleteToolCall(input, ctx) {
|
|
|
39652
39650
|
filePaths: input.filePaths
|
|
39653
39651
|
};
|
|
39654
39652
|
const url = `${ctx.daemonUrl}/api/rpc/personaDispatch.complete`;
|
|
39655
|
-
log("personaDispatchComplete pre-fetch", { url,
|
|
39653
|
+
log("personaDispatchComplete pre-fetch", { url, hasSessionId: Boolean(ctx.sessionId) });
|
|
39654
|
+
const headers = { "content-type": "application/json" };
|
|
39655
|
+
if (ctx.sessionId) headers["x-clawd-session-id"] = ctx.sessionId;
|
|
39656
39656
|
try {
|
|
39657
39657
|
const res = await f(url, {
|
|
39658
39658
|
method: "POST",
|
|
39659
|
-
headers
|
|
39660
|
-
|
|
39661
|
-
"x-clawd-session-id": ctx.sessionId
|
|
39662
|
-
},
|
|
39663
|
-
body: JSON.stringify({ outcome })
|
|
39659
|
+
headers,
|
|
39660
|
+
body: JSON.stringify({ dispatchId: ctx.dispatchId, outcome })
|
|
39664
39661
|
});
|
|
39665
39662
|
log("personaDispatchComplete post-fetch", { url, status: res.status });
|
|
39666
39663
|
} catch (err) {
|
|
@@ -39684,10 +39681,12 @@ async function handlePersonaDispatchCompleteToolCall(input, ctx) {
|
|
|
39684
39681
|
async function main() {
|
|
39685
39682
|
const daemonUrl = process.env.CLAWD_DAEMON_URL;
|
|
39686
39683
|
const sessionId = process.env.CLAWD_SESSION_ID;
|
|
39684
|
+
const dispatchId = process.env.CLAWD_DISPATCH_ID;
|
|
39687
39685
|
log("boot", {
|
|
39688
39686
|
pid: process.pid,
|
|
39689
39687
|
hasDaemonUrl: Boolean(daemonUrl),
|
|
39690
39688
|
hasSessionId: Boolean(sessionId),
|
|
39689
|
+
hasDispatchId: Boolean(dispatchId),
|
|
39691
39690
|
logPath: DISPATCH_LOG ?? "(stderr)"
|
|
39692
39691
|
});
|
|
39693
39692
|
if (!daemonUrl) {
|
|
@@ -39704,14 +39703,11 @@ async function main() {
|
|
|
39704
39703
|
"personaDispatch",
|
|
39705
39704
|
{
|
|
39706
39705
|
title: "Dispatch task to another persona",
|
|
39707
|
-
description: "Delegate a task to another clawd persona. Use this when the user message contains `@persona/<id>` to hand the task off to that persona. The tool blocks until the other persona finishes and returns its result.
|
|
39706
|
+
description: "Delegate a task to another clawd persona. Use this when the user message contains `@persona/<id>` to hand the task off to that persona. The tool blocks until the other persona finishes and returns its result.",
|
|
39708
39707
|
inputSchema: {
|
|
39709
39708
|
targetPersona: external_exports.string().describe("persona id (e.g. persona-bug-fixer)"),
|
|
39710
39709
|
prompt: external_exports.string().describe(
|
|
39711
39710
|
"the task to perform, with the @persona/<id> token stripped out"
|
|
39712
|
-
),
|
|
39713
|
-
mode: external_exports.enum(["new", "continue"]).optional().describe(
|
|
39714
|
-
"continue = resume the previous B session for this (sourceSession, targetPersona) pair; new (default) = fresh B session"
|
|
39715
39711
|
)
|
|
39716
39712
|
}
|
|
39717
39713
|
},
|
|
@@ -39730,8 +39726,8 @@ async function main() {
|
|
|
39730
39726
|
}
|
|
39731
39727
|
},
|
|
39732
39728
|
// sessionId 透传:complete RPC 必须带 x-clawd-session-id header(rpc-adapter 强制)。
|
|
39733
|
-
//
|
|
39734
|
-
async (input) => handlePersonaDispatchCompleteToolCall(input, { daemonUrl, sessionId })
|
|
39729
|
+
// B session 的 cc env 有 CLAWD_SESSION_ID = B 的 sessionId。
|
|
39730
|
+
async (input) => handlePersonaDispatchCompleteToolCall(input, { daemonUrl, sessionId, dispatchId })
|
|
39735
39731
|
);
|
|
39736
39732
|
await server.connect(new StdioServerTransport());
|
|
39737
39733
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clawos-dev/clawd",
|
|
3
|
-
"version": "0.2.142
|
|
3
|
+
"version": "0.2.142",
|
|
4
4
|
"description": "Standalone clawd daemon — Claude Code (and future Codex) session server over WebSocket",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@clawos/clawd-protocol": "workspace:*",
|
|
30
|
+
"@clawos/log-client": "workspace:*",
|
|
30
31
|
"@types/diff": "^7.0.2",
|
|
31
32
|
"@types/node": "^22.0.0",
|
|
32
33
|
"@types/uuid": "^10.0.0",
|