@xiashe/cli 0.1.0 → 0.1.2

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.
Files changed (2) hide show
  1. package/bin/xiashe.mjs +138 -2
  2. package/package.json +2 -2
package/bin/xiashe.mjs CHANGED
@@ -67,7 +67,10 @@ Usage:
67
67
  ${c} agent connect --token <token> [--name <name>] [--kind <kind>]
68
68
  ${c} agent heartbeat
69
69
  ${c} agent list
70
+ ${c} agent revoke --agent <id-or-name>
71
+ ${c} agent run [--once] [--ack] [--idle-ms <ms>] [--busy-ms <ms>] [--max-idle-ms <ms>]
70
72
  ${c} agent tasks list|watch|ack <taskId>|complete <taskId> [--content <text>]
73
+ ${c} agent transfers list|accept <transferId>|complete <transferId>|fail <transferId>
71
74
  ${c} agent task create --agent <id-or-name> --chat <id-or-title> --content <text>
72
75
 
73
76
  ${c} chat list
@@ -187,6 +190,26 @@ function parseFlags(argv) {
187
190
  return out;
188
191
  }
189
192
 
193
+ function sleep(ms) {
194
+ return new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
195
+ }
196
+
197
+ function unwrapResult(payload) {
198
+ return payload && typeof payload === 'object' && payload.result && typeof payload.result === 'object'
199
+ ? payload.result
200
+ : payload;
201
+ }
202
+
203
+ function numericFlag(flags, key, fallback) {
204
+ const value = Number(flags[key] ?? fallback);
205
+ return Number.isFinite(value) && value > 0 ? value : fallback;
206
+ }
207
+
208
+ function jitterMs(ms, ratio = 0.2) {
209
+ const spread = Math.max(0, ms * ratio);
210
+ return Math.round(ms - spread + Math.random() * spread * 2);
211
+ }
212
+
190
213
  function requireValue(key, value) {
191
214
  if (!value || String(value).startsWith('--')) {
192
215
  fail(`Missing value for ${key}.`, EXIT_USAGE);
@@ -636,6 +659,10 @@ async function cmdAgent(sub, args, global) {
636
659
  print(result, global);
637
660
  return;
638
661
  }
662
+ if (sub === 'run' || sub === 'receiver' || sub === 'loop') {
663
+ await cmdAgentReceiver(flags, client, global);
664
+ return;
665
+ }
639
666
  if (sub === 'list' || sub === 'agents' || !sub) {
640
667
  const rows = await client.listAgents(Number(flags.limit || 50));
641
668
  if (global.json) {
@@ -652,10 +679,22 @@ async function cmdAgent(sub, args, global) {
652
679
  ]);
653
680
  return;
654
681
  }
682
+ if (sub === 'revoke' || sub === 'disconnect' || sub === 'delete') {
683
+ const agent = await resolveAgent(client, requireArg(flags.agent || flags._[0], '--agent'));
684
+ const result = await client.revokeAgent(agent.id, {
685
+ idempotencyKey: flags.idempotencyKey || createIdempotencyKey('cli-agent-revoke', [agent.id])
686
+ });
687
+ print(result, global);
688
+ return;
689
+ }
655
690
  if (sub === 'tasks') {
656
691
  await cmdAgentTasks(flags, client, global);
657
692
  return;
658
693
  }
694
+ if (sub === 'transfers') {
695
+ await cmdAgentTransfers(flags, client, global);
696
+ return;
697
+ }
659
698
  if (sub === 'task') {
660
699
  const action = requireArg(flags._[0], 'agent task action');
661
700
  if (action !== 'create') fail('Usage: xiashe agent task create --agent <id-or-name> --chat <id-or-title> --content <text>', EXIT_USAGE);
@@ -673,6 +712,103 @@ async function cmdAgent(sub, args, global) {
673
712
  fail(`Unknown agent command: ${sub}`, EXIT_USAGE);
674
713
  }
675
714
 
715
+ async function cmdAgentReceiver(flags, client, global) {
716
+ const idleMs = numericFlag(flags, 'idle-ms', 60_000);
717
+ const busyMs = numericFlag(flags, 'busy-ms', 15_000);
718
+ const maxIdleMs = numericFlag(flags, 'max-idle-ms', 300_000);
719
+ const deadline = Date.now() + numericFlag(flags, 'timeout-ms', 24 * 60 * 60_000);
720
+ const capabilities = parseCsv(flags.capabilities || 'chat,files,tasks,skills');
721
+ const seenTasks = new Set();
722
+ const seenTransfers = new Set();
723
+ let emptyCycles = 0;
724
+ let failures = 0;
725
+
726
+ while (Date.now() < deadline) {
727
+ let nextDelayMs = idleMs;
728
+ try {
729
+ const heartbeat = unwrapResult(await client.agentHeartbeat({ capabilities }));
730
+ const pendingTaskCount = Number(heartbeat.pendingTaskCount || 0);
731
+ const pendingTransferCount = Number(heartbeat.pendingTransferCount || 0);
732
+ const pendingCount = pendingTaskCount + pendingTransferCount;
733
+ failures = 0;
734
+
735
+ if (pendingTaskCount > 0) {
736
+ const tasksResult = unwrapResult(await client.agentListTasks({ limit: Number(flags.limit || 20) }));
737
+ const tasks = tasksResult.tasks || tasksResult.items || [];
738
+ for (const task of tasks) {
739
+ const id = task.taskId || task.id;
740
+ if (!id || seenTasks.has(id)) continue;
741
+ seenTasks.add(id);
742
+ console.log(JSON.stringify({ type: 'task', ...task }, null, 2));
743
+ if (flags.ack) await client.agentAckTask(id, 'running');
744
+ }
745
+ }
746
+
747
+ if (pendingTransferCount > 0) {
748
+ const transfersResult = unwrapResult(await client.agentListTransfers({ limit: Number(flags.limit || 20) }));
749
+ const transfers = transfersResult.transfers || transfersResult.items || [];
750
+ for (const transfer of transfers) {
751
+ const id = transfer.transferId || transfer.id;
752
+ if (!id || seenTransfers.has(id)) continue;
753
+ seenTransfers.add(id);
754
+ console.log(JSON.stringify({ type: 'transfer', ...transfer }, null, 2));
755
+ }
756
+ }
757
+
758
+ if (pendingCount > 0) {
759
+ emptyCycles = 0;
760
+ nextDelayMs = Number(heartbeat.recommendedNextHeartbeatMs || busyMs);
761
+ } else {
762
+ emptyCycles += 1;
763
+ const backoffStep = Math.min(3, Math.floor(emptyCycles / 5));
764
+ nextDelayMs = Math.min(maxIdleMs, Number(heartbeat.recommendedNextHeartbeatMs || idleMs) * 2 ** backoffStep);
765
+ }
766
+
767
+ if (global.json) {
768
+ console.log(JSON.stringify({
769
+ ok: true,
770
+ pendingTaskCount,
771
+ pendingTransferCount,
772
+ nextHeartbeatMs: nextDelayMs,
773
+ emptyCycles
774
+ }));
775
+ }
776
+ if (flags.once) return;
777
+ } catch (error) {
778
+ failures += 1;
779
+ nextDelayMs = Math.min(maxIdleMs, idleMs * 2 ** Math.min(5, failures));
780
+ const message = error instanceof Error ? error.message : String(error);
781
+ if (flags.once) throw error;
782
+ console.error(`${PRODUCT_NAME} agent receiver error: ${message}. Retrying in ${Math.round(nextDelayMs / 1000)}s.`);
783
+ }
784
+ await sleep(jitterMs(nextDelayMs, 0.2));
785
+ }
786
+ }
787
+
788
+ async function cmdAgentTransfers(flags, client, global) {
789
+ const action = flags._[0] || 'list';
790
+ if (action === 'list') {
791
+ print(await client.agentListTransfers({ limit: Number(flags.limit || 20) }), global);
792
+ return;
793
+ }
794
+ if (action === 'accept') {
795
+ const transferId = requireArg(flags._[1], 'transfer id');
796
+ print(await client.agentAcceptTransfer(transferId, { idempotencyKey: flags.idempotencyKey || undefined }), global);
797
+ return;
798
+ }
799
+ if (action === 'complete' || action === 'fail' || action === 'cancel') {
800
+ const transferId = requireArg(flags._[1], 'transfer id');
801
+ const status = action === 'fail' ? 'failed' : action === 'cancel' ? 'cancelled' : 'completed';
802
+ print(await client.agentCompleteTransfer(transferId, {
803
+ status,
804
+ error: flags.error || undefined,
805
+ idempotencyKey: flags.idempotencyKey || undefined
806
+ }), global);
807
+ return;
808
+ }
809
+ fail(`Unknown agent transfers command: ${action}`, EXIT_USAGE);
810
+ }
811
+
676
812
  async function cmdAgentTasks(flags, client, global) {
677
813
  const action = flags._[0] || 'list';
678
814
  if (action === 'list') {
@@ -685,7 +821,7 @@ async function cmdAgentTasks(flags, client, global) {
685
821
  const seen = new Set();
686
822
  const deadline = Date.now() + Number(flags['timeout-ms'] || 300_000);
687
823
  while (Date.now() < deadline) {
688
- const result = await client.agentListTasks({ limit: Number(flags.limit || 20) });
824
+ const result = unwrapResult(await client.agentListTasks({ limit: Number(flags.limit || 20) }));
689
825
  const tasks = result.tasks || result.items || [];
690
826
  for (const task of tasks) {
691
827
  const id = task.taskId || task.id;
@@ -694,7 +830,7 @@ async function cmdAgentTasks(flags, client, global) {
694
830
  console.log(JSON.stringify(task, null, 2));
695
831
  if (flags.ack) await client.agentAckTask(id, 'running');
696
832
  }
697
- await new Promise((resolve) => setTimeout(resolve, 2500));
833
+ await sleep(2500);
698
834
  }
699
835
  return;
700
836
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xiashe/cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "xiashe": "bin/xiashe.mjs"
@@ -10,7 +10,7 @@
10
10
  "README.md"
11
11
  ],
12
12
  "dependencies": {
13
- "@xiashe/sdk": "0.1.0"
13
+ "@xiashe/sdk": "0.1.1"
14
14
  },
15
15
  "engines": {
16
16
  "node": ">=20"