commanderclaw 1.1.15 → 1.1.20

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.
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/plugin/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/plugin/client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,OAAO,EAGP,QAAQ,EACR,eAAe,EACf,IAAI,EACJ,iBAAiB,EACjB,eAAe,EACf,WAAW,EACZ,MAAM,SAAS,CAAC;AAwCjB,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,CAAC,EAAE;QACV,OAAO,EAAE,OAAO,CAAC;QACjB,WAAW,EAAE,MAAM,CAAC;QACpB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAED,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,EAAE,CAA8B;IACxC,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,KAAK,CAAkB;IAC/B,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,eAAe,CAAkD;IACzE,OAAO,CAAC,aAAa,CAAqE;IAC1F,OAAO,CAAC,iBAAiB,CAA+C;IACxE,OAAO,CAAC,iBAAiB,CAGT;gBAEJ,OAAO,EAAE,aAAa;IAI5B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAwC9B,OAAO,CAAC,aAAa;IAwFrB,OAAO,CAAC,WAAW;IAoBnB,OAAO,CAAC,gBAAgB;IAWxB,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,gBAAgB;IAkBlB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAajC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI;IAiC3C,EAAE,CAAC,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,GAAG,IAAI;IAMvE,GAAG,CAAC,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,GAAG,IAAI;IAQxE,OAAO,CAAC,IAAI;IAWZ,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI;IAI9D,WAAW,IAAI,OAAO;IAItB,SAAS,IAAI,MAAM,GAAG,IAAI;IAI1B,QAAQ,IAAI,QAAQ,EAAE;IAItB,cAAc,IAAI,MAAM,GAAG,IAAI;IAI/B,OAAO,CAAC,UAAU;YAIJ,QAAQ;IAwBhB,SAAS,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;IAKhC,SAAS,IAAI,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAIvD,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAOjD,SAAS,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;IAK5B,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAItC,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAOvE,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAMtD,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAMvD,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IAOxF,cAAc,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI;IAIhD,cAAc,CAAC,OAAO,EAAE,iBAAiB,GAAG;QAAE,MAAM,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;CAG1G"}
@@ -140,6 +140,12 @@ class CommanderClawClient {
140
140
  this.emit("chat", { type: "chat", data: msg });
141
141
  break;
142
142
  }
143
+ case "SESSION_SWITCH": {
144
+ const payload = msg.payload;
145
+ console.log(`[CommanderClawClient] SESSION_SWITCH: sessionId=${payload.sessionId}, action=${payload.action || "legacy"}`);
146
+ this.emit("session_switch", { type: "session_switch", data: payload });
147
+ break;
148
+ }
143
149
  default: {
144
150
  console.log(`[CommanderClawClient] Unknown message type: ${msg.type}`);
145
151
  const handler = this.messageHandlers.get(msg.type);
@@ -4272,6 +4278,314 @@ Note: Some commands may require King-level permissions.`,
4272
4278
  };
4273
4279
  }
4274
4280
 
4281
+ /**
4282
+ * OpenClaw Session Strategy
4283
+ *
4284
+ * Uses ACP Runtime to create and manage sessions for OpenClaw-based agents (QClaw, OpenCode, etc.).
4285
+ */
4286
+ class OpenClawSessionStrategy {
4287
+ name;
4288
+ runtime = null;
4289
+ sessions = new Map();
4290
+ activeSessionId = null;
4291
+ initialized = false;
4292
+ initPromise = null;
4293
+ constructor(backendType) {
4294
+ this.name = backendType;
4295
+ this.initPromise = this.initRuntime();
4296
+ }
4297
+ async initRuntime() {
4298
+ try {
4299
+ // Dynamic import to handle cases where openclaw is not available
4300
+ const openclaw = await import('openclaw/plugin-sdk');
4301
+ const backend = openclaw.requireAcpRuntimeBackend();
4302
+ this.runtime = backend?.runtime;
4303
+ this.initialized = true;
4304
+ console.log(`[CommanderClaw] ACP Runtime initialized for ${this.name}`);
4305
+ }
4306
+ catch (e) {
4307
+ console.warn(`[CommanderClaw] ACP Runtime not available for ${this.name}:`, e);
4308
+ this.initialized = true; // Mark as initialized even on failure
4309
+ }
4310
+ }
4311
+ async ensureInitialized() {
4312
+ if (!this.initialized && this.initPromise) {
4313
+ await this.initPromise;
4314
+ }
4315
+ }
4316
+ isAvailable() {
4317
+ return !!this.runtime;
4318
+ }
4319
+ async createSession(sessionId, options) {
4320
+ await this.ensureInitialized();
4321
+ if (!this.runtime) {
4322
+ throw new Error('ACP Runtime not available');
4323
+ }
4324
+ const sessionKey = options?.sessionKey || `commanderclaw:${sessionId}`;
4325
+ try {
4326
+ const handle = await this.runtime.ensureSession({
4327
+ sessionKey,
4328
+ agent: 'default',
4329
+ mode: options?.mode || 'persistent',
4330
+ cwd: options?.cwd,
4331
+ env: options?.env,
4332
+ });
4333
+ const sessionHandle = {
4334
+ sessionId,
4335
+ localSessionKey: handle.sessionKey,
4336
+ createdAt: new Date(),
4337
+ metadata: {
4338
+ backendSessionId: handle.backendSessionId,
4339
+ agentSessionId: handle.agentSessionId,
4340
+ cwd: handle.cwd,
4341
+ },
4342
+ };
4343
+ this.sessions.set(sessionId, sessionHandle);
4344
+ this.activeSessionId = sessionId;
4345
+ console.log(`[CommanderClaw] Session created via ACP Runtime: ${sessionId} -> ${handle.sessionKey}`);
4346
+ return sessionHandle;
4347
+ }
4348
+ catch (e) {
4349
+ console.error(`[CommanderClaw] Failed to create session ${sessionId}:`, e);
4350
+ throw e;
4351
+ }
4352
+ }
4353
+ getSession(sessionId) {
4354
+ return this.sessions.get(sessionId) || null;
4355
+ }
4356
+ switchSession(sessionId) {
4357
+ if (!this.sessions.has(sessionId)) {
4358
+ throw new Error(`Session ${sessionId} not found`);
4359
+ }
4360
+ this.activeSessionId = sessionId;
4361
+ console.log(`[CommanderClaw] Switched to session: ${sessionId}`);
4362
+ }
4363
+ getActiveSession() {
4364
+ if (!this.activeSessionId)
4365
+ return null;
4366
+ return this.sessions.get(this.activeSessionId) || null;
4367
+ }
4368
+ async closeSession(sessionId) {
4369
+ const session = this.sessions.get(sessionId);
4370
+ if (!session)
4371
+ return;
4372
+ if (this.runtime?.close) {
4373
+ try {
4374
+ await this.runtime.close({
4375
+ handle: { sessionKey: session.localSessionKey },
4376
+ reason: 'Session closed by CommanderClaw',
4377
+ });
4378
+ }
4379
+ catch (e) {
4380
+ console.warn(`[CommanderClaw] Failed to close session ${sessionId} via ACP Runtime:`, e);
4381
+ }
4382
+ }
4383
+ this.sessions.delete(sessionId);
4384
+ if (this.activeSessionId === sessionId) {
4385
+ this.activeSessionId = null;
4386
+ }
4387
+ console.log(`[CommanderClaw] Session closed: ${sessionId}`);
4388
+ }
4389
+ }
4390
+
4391
+ /**
4392
+ * Simple Session Strategy
4393
+ *
4394
+ * Fallback strategy that uses in-memory session mapping.
4395
+ * Used when ACP Runtime is not available.
4396
+ */
4397
+ class SimpleSessionStrategy {
4398
+ name = 'simple';
4399
+ sessions = new Map();
4400
+ activeSessionId = null;
4401
+ isAvailable() {
4402
+ return true; // Always available as fallback
4403
+ }
4404
+ async createSession(sessionId, options) {
4405
+ const existing = this.sessions.get(sessionId);
4406
+ if (existing) {
4407
+ this.activeSessionId = sessionId;
4408
+ return existing;
4409
+ }
4410
+ const sessionKey = options?.sessionKey || `simple:${sessionId}:${Date.now()}`;
4411
+ const sessionHandle = {
4412
+ sessionId,
4413
+ localSessionKey: sessionKey,
4414
+ createdAt: new Date(),
4415
+ };
4416
+ this.sessions.set(sessionId, sessionHandle);
4417
+ this.activeSessionId = sessionId;
4418
+ console.log(`[CommanderClaw] Simple session created: ${sessionId} -> ${sessionKey}`);
4419
+ return sessionHandle;
4420
+ }
4421
+ getSession(sessionId) {
4422
+ return this.sessions.get(sessionId) || null;
4423
+ }
4424
+ switchSession(sessionId) {
4425
+ if (!this.sessions.has(sessionId)) {
4426
+ throw new Error(`Session ${sessionId} not found`);
4427
+ }
4428
+ this.activeSessionId = sessionId;
4429
+ console.log(`[CommanderClaw] Switched to simple session: ${sessionId}`);
4430
+ }
4431
+ getActiveSession() {
4432
+ if (!this.activeSessionId)
4433
+ return null;
4434
+ return this.sessions.get(this.activeSessionId) || null;
4435
+ }
4436
+ async closeSession(sessionId) {
4437
+ this.sessions.delete(sessionId);
4438
+ if (this.activeSessionId === sessionId) {
4439
+ this.activeSessionId = null;
4440
+ }
4441
+ console.log(`[CommanderClaw] Simple session closed: ${sessionId}`);
4442
+ }
4443
+ }
4444
+
4445
+ /**
4446
+ * Backend Detector
4447
+ *
4448
+ * Detects the ACP backend type for the current environment.
4449
+ */
4450
+ function detectBackend() {
4451
+ // 1. Check environment variables
4452
+ if (process.env.QCLAW_BACKEND)
4453
+ return 'qclaw';
4454
+ if (process.env.OPENCODE_BACKEND)
4455
+ return 'opencode';
4456
+ // 2. Try to load OpenClaw SDK and detect backend
4457
+ try {
4458
+ // Use require for synchronous detection
4459
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
4460
+ const openclaw = require('openclaw/plugin-sdk');
4461
+ const backend = openclaw.requireAcpRuntimeBackend();
4462
+ if (!backend)
4463
+ return 'simple';
4464
+ const id = (backend.id || '').toLowerCase();
4465
+ if (id.includes('qclaw'))
4466
+ return 'qclaw';
4467
+ if (id.includes('opencode'))
4468
+ return 'opencode';
4469
+ if (id.includes('claude'))
4470
+ return 'claude';
4471
+ if (id.includes('cursor'))
4472
+ return 'cursor';
4473
+ // Has backend but unknown type
4474
+ return 'custom';
4475
+ }
4476
+ catch {
4477
+ // OpenClaw SDK not available or backend not registered
4478
+ }
4479
+ return 'simple';
4480
+ }
4481
+
4482
+ /**
4483
+ * Session Manager
4484
+ *
4485
+ * Manages session strategies and provides a unified interface for session operations.
4486
+ */
4487
+ class SessionManager {
4488
+ activeStrategy = null;
4489
+ backendType = 'unknown';
4490
+ constructor() {
4491
+ this.initStrategy();
4492
+ }
4493
+ initStrategy() {
4494
+ const backend = detectBackend();
4495
+ this.backendType = backend;
4496
+ console.log(`[CommanderClaw] Detected backend: ${backend}`);
4497
+ if (backend === 'simple') {
4498
+ // No ACP Runtime available, use simple strategy
4499
+ this.activeStrategy = new SimpleSessionStrategy();
4500
+ console.log(`[CommanderClaw] Using strategy: simple (no ACP Runtime)`);
4501
+ }
4502
+ else {
4503
+ // Try OpenClaw strategy first
4504
+ const openclawStrategy = new OpenClawSessionStrategy(backend);
4505
+ // Check availability asynchronously
4506
+ openclawStrategy['initPromise']?.then(() => {
4507
+ if (openclawStrategy.isAvailable()) {
4508
+ console.log(`[CommanderClaw] Using strategy: ${backend} (ACP Runtime)`);
4509
+ }
4510
+ else {
4511
+ console.warn(`[CommanderClaw] OpenClaw strategy not available, using simple fallback`);
4512
+ this.activeStrategy = new SimpleSessionStrategy();
4513
+ }
4514
+ });
4515
+ this.activeStrategy = openclawStrategy;
4516
+ }
4517
+ }
4518
+ /**
4519
+ * Get the backend type that was detected
4520
+ */
4521
+ getBackendType() {
4522
+ return this.backendType;
4523
+ }
4524
+ /**
4525
+ * Get the active strategy name
4526
+ */
4527
+ getStrategyName() {
4528
+ return this.activeStrategy?.name || 'none';
4529
+ }
4530
+ /**
4531
+ * Get the active strategy
4532
+ */
4533
+ getActiveStrategy() {
4534
+ return this.activeStrategy;
4535
+ }
4536
+ /**
4537
+ * Create a new session
4538
+ */
4539
+ async createSession(sessionId, options) {
4540
+ if (!this.activeStrategy) {
4541
+ console.warn('[CommanderClaw] No session strategy available');
4542
+ return null;
4543
+ }
4544
+ try {
4545
+ return await this.activeStrategy.createSession(sessionId, options);
4546
+ }
4547
+ catch (e) {
4548
+ console.error(`[CommanderClaw] Failed to create session ${sessionId}:`, e);
4549
+ return null;
4550
+ }
4551
+ }
4552
+ /**
4553
+ * Get an existing session
4554
+ */
4555
+ getSession(sessionId) {
4556
+ return this.activeStrategy?.getSession(sessionId) || null;
4557
+ }
4558
+ /**
4559
+ * Switch to an existing session
4560
+ */
4561
+ switchSession(sessionId) {
4562
+ this.activeStrategy?.switchSession(sessionId);
4563
+ }
4564
+ /**
4565
+ * Get the currently active session
4566
+ */
4567
+ getActiveSession() {
4568
+ return this.activeStrategy?.getActiveSession() || null;
4569
+ }
4570
+ /**
4571
+ * Close a session
4572
+ */
4573
+ async closeSession(sessionId) {
4574
+ await this.activeStrategy?.closeSession(sessionId);
4575
+ }
4576
+ }
4577
+ // Singleton instance
4578
+ let sessionManager$1 = null;
4579
+ /**
4580
+ * Get the singleton SessionManager instance
4581
+ */
4582
+ function getSessionManager() {
4583
+ if (!sessionManager$1) {
4584
+ sessionManager$1 = new SessionManager();
4585
+ }
4586
+ return sessionManager$1;
4587
+ }
4588
+
4275
4589
  /**
4276
4590
  * CommanderClaw Channel Plugin for OpenClaw
4277
4591
  *
@@ -4283,6 +4597,8 @@ const { setRuntime, getRuntime } = pluginSdk.createPluginRuntimeStore("Commander
4283
4597
  let client = null;
4284
4598
  let pluginConfig = null;
4285
4599
  let currentLog = null;
4600
+ let currentSessionKey = null;
4601
+ const sessionManager = getSessionManager();
4286
4602
  const DEFAULT_CONFIG = {
4287
4603
  deviceName: "OpenClaw Agent",
4288
4604
  autoConnect: true,
@@ -4340,6 +4656,8 @@ async function handleChatMessage(msg) {
4340
4656
  id: senderId,
4341
4657
  },
4342
4658
  });
4659
+ const sessionHandle = sessionManager.getActiveSession();
4660
+ const sessionKey = sessionHandle?.localSessionKey || currentSessionKey || route.sessionKey;
4343
4661
  const ctxPayload = rt.channel.reply.finalizeInboundContext({
4344
4662
  Body: content,
4345
4663
  RawBody: content,
@@ -4348,7 +4666,7 @@ async function handleChatMessage(msg) {
4348
4666
  From: `${CHANNEL_ID}:${senderId}`,
4349
4667
  To: `${CHANNEL_ID}:${nodeId}`,
4350
4668
  SenderId: senderId,
4351
- SessionKey: route.sessionKey,
4669
+ SessionKey: sessionKey,
4352
4670
  AccountId: "default",
4353
4671
  ChatType: "direct",
4354
4672
  ConversationLabel: `user:${senderId}`,
@@ -4423,6 +4741,61 @@ function initializeClient(config, log) {
4423
4741
  client.on("chat", (event) => {
4424
4742
  handleChatMessage(event.data);
4425
4743
  });
4744
+ client.on("session_switch", async (event) => {
4745
+ const payload = event.data;
4746
+ log.info("SESSION_SWITCH received", {
4747
+ sessionId: payload.sessionId,
4748
+ sessionKey: payload.sessionKey,
4749
+ action: payload.action || "legacy",
4750
+ });
4751
+ const action = payload.action || "switch";
4752
+ switch (action) {
4753
+ case "create": {
4754
+ try {
4755
+ const handle = await sessionManager.createSession(payload.sessionId, { ...payload.options, sessionKey: payload.sessionKey });
4756
+ currentSessionKey = handle.localSessionKey;
4757
+ log.info("Session created", {
4758
+ sessionId: payload.sessionId,
4759
+ localSessionKey: handle.localSessionKey,
4760
+ });
4761
+ }
4762
+ catch (e) {
4763
+ log.error("Failed to create session", {
4764
+ sessionId: payload.sessionId,
4765
+ error: String(e),
4766
+ });
4767
+ }
4768
+ break;
4769
+ }
4770
+ case "switch": {
4771
+ const existing = sessionManager.getSession(payload.sessionId);
4772
+ if (existing) {
4773
+ sessionManager.switchSession(payload.sessionId);
4774
+ currentSessionKey = existing.localSessionKey;
4775
+ log.info("Session switched", { sessionId: payload.sessionId });
4776
+ }
4777
+ else {
4778
+ log.info("Session not found, creating", { sessionId: payload.sessionId });
4779
+ try {
4780
+ const handle = await sessionManager.createSession(payload.sessionId, { sessionKey: payload.sessionKey });
4781
+ currentSessionKey = handle.localSessionKey;
4782
+ }
4783
+ catch (e) {
4784
+ log.error("Failed to create session on switch", {
4785
+ sessionId: payload.sessionId,
4786
+ error: String(e),
4787
+ });
4788
+ }
4789
+ }
4790
+ break;
4791
+ }
4792
+ case "close":
4793
+ await sessionManager.closeSession(payload.sessionId);
4794
+ currentSessionKey = null;
4795
+ log.info("Session closed", { sessionId: payload.sessionId });
4796
+ break;
4797
+ }
4798
+ });
4426
4799
  client.connect().catch((error) => {
4427
4800
  log.error("Failed to connect to CommanderClaw server", {
4428
4801
  error: error.message,