@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.
- package/bin/xiashe.mjs +138 -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
|
|
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.
|
|
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.
|
|
13
|
+
"@xiashe/sdk": "0.1.1"
|
|
14
14
|
},
|
|
15
15
|
"engines": {
|
|
16
16
|
"node": ">=20"
|