@xiashe/cli 0.1.0 → 0.1.4
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/README.md +3 -1
- package/bin/xiashe.mjs +147 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -18,12 +18,14 @@ xiashe skills list
|
|
|
18
18
|
xiashe skills install copywriting --connection MaxClaw-1 --watch
|
|
19
19
|
xiashe agent issue --name "Local WorkBuddy"
|
|
20
20
|
xiashe agent connect --token "<token>" --name "Local WorkBuddy"
|
|
21
|
-
xiashe agent
|
|
21
|
+
xiashe agent run --ack
|
|
22
22
|
xiashe chat send --chat "New Chat" --content "帮我处理这个文件" --file ./report.pdf --agent "Local WorkBuddy"
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
Local state is stored in `~/.xiashe/config.json`.
|
|
26
26
|
|
|
27
|
+
`xiashe agent run --ack` is the recommended lightweight receiver for shell-capable Agents. It sends cheap heartbeats and only lists/executes work when the backend reports pending tasks or transfers. Default cadence is about 30s while idle, 10s while work is active, and up to 2 minutes after long idle periods.
|
|
28
|
+
|
|
27
29
|
Production defaults:
|
|
28
30
|
|
|
29
31
|
- Convex: `https://api.xiashe.chat`
|
package/bin/xiashe.mjs
CHANGED
|
@@ -64,10 +64,13 @@ Usage:
|
|
|
64
64
|
${c} skills claim <one-time-token>
|
|
65
65
|
|
|
66
66
|
${c} agent issue [--name <name>] [--kind <kind>] [--json]
|
|
67
|
-
${c} agent connect --token <token> [--name <name>] [--kind <kind>]
|
|
68
|
-
${c} agent heartbeat
|
|
67
|
+
${c} agent connect --token <token> [--name <name>] [--kind <kind>] [--webhook-url <https-url>] [--webhook-token <secret>]
|
|
68
|
+
${c} agent heartbeat [--webhook-url <https-url>] [--webhook-token <secret>|--disable-webhook]
|
|
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);
|
|
@@ -622,6 +645,8 @@ async function cmdAgent(sub, args, global) {
|
|
|
622
645
|
agentName: flags.name || flags['agent-name'] || 'XiaShe CLI Agent',
|
|
623
646
|
agentKind: flags.kind || 'cli',
|
|
624
647
|
agentRef: flags.ref || `${osName()} ${process.cwd()}`,
|
|
648
|
+
webhookUrl: flags['webhook-url'] || undefined,
|
|
649
|
+
webhookAuthToken: flags['webhook-token'] || flags['webhook-auth-token'] || undefined,
|
|
625
650
|
transport: flags.transport || 'npx'
|
|
626
651
|
});
|
|
627
652
|
await saveAgentSession(profileName, claim);
|
|
@@ -631,11 +656,20 @@ async function cmdAgent(sub, args, global) {
|
|
|
631
656
|
if (sub === 'heartbeat') {
|
|
632
657
|
const result = await client.agentHeartbeat({
|
|
633
658
|
status: flags.status || 'online',
|
|
659
|
+
agentName: flags.name || flags['agent-name'] || undefined,
|
|
660
|
+
agentRef: flags.ref || undefined,
|
|
661
|
+
webhookUrl: flags['webhook-url'] || undefined,
|
|
662
|
+
webhookAuthToken: flags['webhook-token'] || flags['webhook-auth-token'] || undefined,
|
|
663
|
+
webhookMode: flags['disable-webhook'] ? 'disabled' : flags['webhook-url'] ? 'notify' : undefined,
|
|
634
664
|
capabilities: parseCsv(flags.capabilities || 'chat,files,tasks,skills')
|
|
635
665
|
});
|
|
636
666
|
print(result, global);
|
|
637
667
|
return;
|
|
638
668
|
}
|
|
669
|
+
if (sub === 'run' || sub === 'receiver' || sub === 'loop') {
|
|
670
|
+
await cmdAgentReceiver(flags, client, global);
|
|
671
|
+
return;
|
|
672
|
+
}
|
|
639
673
|
if (sub === 'list' || sub === 'agents' || !sub) {
|
|
640
674
|
const rows = await client.listAgents(Number(flags.limit || 50));
|
|
641
675
|
if (global.json) {
|
|
@@ -652,10 +686,22 @@ async function cmdAgent(sub, args, global) {
|
|
|
652
686
|
]);
|
|
653
687
|
return;
|
|
654
688
|
}
|
|
689
|
+
if (sub === 'revoke' || sub === 'disconnect' || sub === 'delete') {
|
|
690
|
+
const agent = await resolveAgent(client, requireArg(flags.agent || flags._[0], '--agent'));
|
|
691
|
+
const result = await client.revokeAgent(agent.id, {
|
|
692
|
+
idempotencyKey: flags.idempotencyKey || createIdempotencyKey('cli-agent-revoke', [agent.id])
|
|
693
|
+
});
|
|
694
|
+
print(result, global);
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
655
697
|
if (sub === 'tasks') {
|
|
656
698
|
await cmdAgentTasks(flags, client, global);
|
|
657
699
|
return;
|
|
658
700
|
}
|
|
701
|
+
if (sub === 'transfers') {
|
|
702
|
+
await cmdAgentTransfers(flags, client, global);
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
659
705
|
if (sub === 'task') {
|
|
660
706
|
const action = requireArg(flags._[0], 'agent task action');
|
|
661
707
|
if (action !== 'create') fail('Usage: xiashe agent task create --agent <id-or-name> --chat <id-or-title> --content <text>', EXIT_USAGE);
|
|
@@ -673,6 +719,103 @@ async function cmdAgent(sub, args, global) {
|
|
|
673
719
|
fail(`Unknown agent command: ${sub}`, EXIT_USAGE);
|
|
674
720
|
}
|
|
675
721
|
|
|
722
|
+
async function cmdAgentReceiver(flags, client, global) {
|
|
723
|
+
const idleMs = numericFlag(flags, 'idle-ms', 30_000);
|
|
724
|
+
const busyMs = numericFlag(flags, 'busy-ms', 10_000);
|
|
725
|
+
const maxIdleMs = numericFlag(flags, 'max-idle-ms', 120_000);
|
|
726
|
+
const deadline = Date.now() + numericFlag(flags, 'timeout-ms', 24 * 60 * 60_000);
|
|
727
|
+
const capabilities = parseCsv(flags.capabilities || 'chat,files,tasks,skills');
|
|
728
|
+
const seenTasks = new Set();
|
|
729
|
+
const seenTransfers = new Set();
|
|
730
|
+
let emptyCycles = 0;
|
|
731
|
+
let failures = 0;
|
|
732
|
+
|
|
733
|
+
while (Date.now() < deadline) {
|
|
734
|
+
let nextDelayMs = idleMs;
|
|
735
|
+
try {
|
|
736
|
+
const heartbeat = unwrapResult(await client.agentHeartbeat({ capabilities }));
|
|
737
|
+
const pendingTaskCount = Number(heartbeat.pendingTaskCount || 0);
|
|
738
|
+
const pendingTransferCount = Number(heartbeat.pendingTransferCount || 0);
|
|
739
|
+
const pendingCount = pendingTaskCount + pendingTransferCount;
|
|
740
|
+
failures = 0;
|
|
741
|
+
|
|
742
|
+
if (pendingTaskCount > 0) {
|
|
743
|
+
const tasksResult = unwrapResult(await client.agentListTasks({ limit: Number(flags.limit || 20) }));
|
|
744
|
+
const tasks = tasksResult.tasks || tasksResult.items || [];
|
|
745
|
+
for (const task of tasks) {
|
|
746
|
+
const id = task.taskId || task.id;
|
|
747
|
+
if (!id || seenTasks.has(id)) continue;
|
|
748
|
+
seenTasks.add(id);
|
|
749
|
+
console.log(JSON.stringify({ type: 'task', ...task }, null, 2));
|
|
750
|
+
if (flags.ack) await client.agentAckTask(id, 'running');
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
if (pendingTransferCount > 0) {
|
|
755
|
+
const transfersResult = unwrapResult(await client.agentListTransfers({ limit: Number(flags.limit || 20) }));
|
|
756
|
+
const transfers = transfersResult.transfers || transfersResult.items || [];
|
|
757
|
+
for (const transfer of transfers) {
|
|
758
|
+
const id = transfer.transferId || transfer.id;
|
|
759
|
+
if (!id || seenTransfers.has(id)) continue;
|
|
760
|
+
seenTransfers.add(id);
|
|
761
|
+
console.log(JSON.stringify({ type: 'transfer', ...transfer }, null, 2));
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
if (pendingCount > 0) {
|
|
766
|
+
emptyCycles = 0;
|
|
767
|
+
nextDelayMs = Number(heartbeat.recommendedNextHeartbeatMs || busyMs);
|
|
768
|
+
} else {
|
|
769
|
+
emptyCycles += 1;
|
|
770
|
+
const backoffStep = Math.min(3, Math.floor(emptyCycles / 5));
|
|
771
|
+
nextDelayMs = Math.min(maxIdleMs, Number(heartbeat.recommendedNextHeartbeatMs || idleMs) * 2 ** backoffStep);
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
if (global.json) {
|
|
775
|
+
console.log(JSON.stringify({
|
|
776
|
+
ok: true,
|
|
777
|
+
pendingTaskCount,
|
|
778
|
+
pendingTransferCount,
|
|
779
|
+
nextHeartbeatMs: nextDelayMs,
|
|
780
|
+
emptyCycles
|
|
781
|
+
}));
|
|
782
|
+
}
|
|
783
|
+
if (flags.once) return;
|
|
784
|
+
} catch (error) {
|
|
785
|
+
failures += 1;
|
|
786
|
+
nextDelayMs = Math.min(maxIdleMs, idleMs * 2 ** Math.min(5, failures));
|
|
787
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
788
|
+
if (flags.once) throw error;
|
|
789
|
+
console.error(`${PRODUCT_NAME} agent receiver error: ${message}. Retrying in ${Math.round(nextDelayMs / 1000)}s.`);
|
|
790
|
+
}
|
|
791
|
+
await sleep(jitterMs(nextDelayMs, 0.2));
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
async function cmdAgentTransfers(flags, client, global) {
|
|
796
|
+
const action = flags._[0] || 'list';
|
|
797
|
+
if (action === 'list') {
|
|
798
|
+
print(await client.agentListTransfers({ limit: Number(flags.limit || 20) }), global);
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
if (action === 'accept') {
|
|
802
|
+
const transferId = requireArg(flags._[1], 'transfer id');
|
|
803
|
+
print(await client.agentAcceptTransfer(transferId, { idempotencyKey: flags.idempotencyKey || undefined }), global);
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
if (action === 'complete' || action === 'fail' || action === 'cancel') {
|
|
807
|
+
const transferId = requireArg(flags._[1], 'transfer id');
|
|
808
|
+
const status = action === 'fail' ? 'failed' : action === 'cancel' ? 'cancelled' : 'completed';
|
|
809
|
+
print(await client.agentCompleteTransfer(transferId, {
|
|
810
|
+
status,
|
|
811
|
+
error: flags.error || undefined,
|
|
812
|
+
idempotencyKey: flags.idempotencyKey || undefined
|
|
813
|
+
}), global);
|
|
814
|
+
return;
|
|
815
|
+
}
|
|
816
|
+
fail(`Unknown agent transfers command: ${action}`, EXIT_USAGE);
|
|
817
|
+
}
|
|
818
|
+
|
|
676
819
|
async function cmdAgentTasks(flags, client, global) {
|
|
677
820
|
const action = flags._[0] || 'list';
|
|
678
821
|
if (action === 'list') {
|
|
@@ -685,7 +828,7 @@ async function cmdAgentTasks(flags, client, global) {
|
|
|
685
828
|
const seen = new Set();
|
|
686
829
|
const deadline = Date.now() + Number(flags['timeout-ms'] || 300_000);
|
|
687
830
|
while (Date.now() < deadline) {
|
|
688
|
-
const result = await client.agentListTasks({ limit: Number(flags.limit || 20) });
|
|
831
|
+
const result = unwrapResult(await client.agentListTasks({ limit: Number(flags.limit || 20) }));
|
|
689
832
|
const tasks = result.tasks || result.items || [];
|
|
690
833
|
for (const task of tasks) {
|
|
691
834
|
const id = task.taskId || task.id;
|
|
@@ -694,7 +837,7 @@ async function cmdAgentTasks(flags, client, global) {
|
|
|
694
837
|
console.log(JSON.stringify(task, null, 2));
|
|
695
838
|
if (flags.ack) await client.agentAckTask(id, 'running');
|
|
696
839
|
}
|
|
697
|
-
await
|
|
840
|
+
await sleep(2500);
|
|
698
841
|
}
|
|
699
842
|
return;
|
|
700
843
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xiashe/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
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.3"
|
|
14
14
|
},
|
|
15
15
|
"engines": {
|
|
16
16
|
"node": ">=20"
|