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