@commonlyai/cli 0.1.229 → 0.1.230
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/package.json +1 -1
- package/src/commands/agent.js +70 -5
package/package.json
CHANGED
package/src/commands/agent.js
CHANGED
|
@@ -560,7 +560,12 @@ const listAgents = async (opts, deps) => {
|
|
|
560
560
|
return 0;
|
|
561
561
|
}
|
|
562
562
|
|
|
563
|
-
|
|
563
|
+
// The pod roster also names the handle a person types to reach each agent
|
|
564
|
+
// (F44). It is printed whole: a cut handle reaches nobody.
|
|
565
|
+
const handleOf = (inst) => (inst.mention ? `@${inst.mention}` : '');
|
|
566
|
+
const handleWidth = opts.pod ? Math.max(6, ...rows.map((inst) => handleOf(inst).length)) : 0;
|
|
567
|
+
const handleCol = (s) => (opts.pod ? `${col(s, handleWidth)} ` : '');
|
|
568
|
+
log(`${col('NAME', 16)} ${col('INSTANCE', 10)} ${handleCol('HANDLE')}${col('RUNTIME', 14)} ${col('HOST', 6)} ${col('STATUS', 10)} LAST SEEN`);
|
|
564
569
|
log('─'.repeat(80));
|
|
565
570
|
rows.forEach((inst) => {
|
|
566
571
|
// The pod roster names the agent `name`; the admin route uses `agentName`.
|
|
@@ -569,7 +574,7 @@ const listAgents = async (opts, deps) => {
|
|
|
569
574
|
const host = inst.config?.runtime?.host || inst.runtime?.host || '';
|
|
570
575
|
const seen = inst.lastActiveAt || inst.usage?.lastUsedAt || inst.connection?.lastSeenAt;
|
|
571
576
|
const lastSeen = seen ? new Date(seen).toLocaleString() : 'never';
|
|
572
|
-
log(`${col(displayName, 16)} ${col(inst.instanceId, 10)} ${col(runtimeType, 14)} ${col(host, 6)} ${col(inst.status, 10)} ${lastSeen}`);
|
|
577
|
+
log(`${col(displayName, 16)} ${col(inst.instanceId, 10)} ${handleCol(handleOf(inst))}${col(runtimeType, 14)} ${col(host, 6)} ${col(inst.status, 10)} ${lastSeen}`);
|
|
573
578
|
});
|
|
574
579
|
return 0;
|
|
575
580
|
} catch (err) {
|
|
@@ -579,6 +584,64 @@ const listAgents = async (opts, deps) => {
|
|
|
579
584
|
}
|
|
580
585
|
};
|
|
581
586
|
|
|
587
|
+
/**
|
|
588
|
+
* The seat `agent logs <name> --pod` reads (F44). An agent has three names: its
|
|
589
|
+
* agentName, its instance, and the handle its @mentions route to. Any of them
|
|
590
|
+
* finds it in the pod roster, so a seat whose instance isn't "default" no longer
|
|
591
|
+
* reads as idle. An explicit --instance-id is used as given.
|
|
592
|
+
*/
|
|
593
|
+
export const resolveLogsSeat = async (client, podId, typed, instanceId) => {
|
|
594
|
+
// Activity is stored under the bare agentName, never an @ spelling.
|
|
595
|
+
const bare = String(typed).trim().replace(/^@/, '');
|
|
596
|
+
const isHandle = String(typed).trim().startsWith('@');
|
|
597
|
+
// A plain name with an explicit instance is used as given. An @handle still
|
|
598
|
+
// needs the roster: activity is stored under the agentName, not the handle.
|
|
599
|
+
if (instanceId && !isHandle) return { agentName: bare, instanceId };
|
|
600
|
+
const want = bare.toLowerCase();
|
|
601
|
+
let agents;
|
|
602
|
+
try {
|
|
603
|
+
({ agents = [] } = await client.get(`/api/registry/pods/${encodeURIComponent(podId)}/agents`));
|
|
604
|
+
} catch (err) {
|
|
605
|
+
// The default instance is still worth reading when the roster isn't.
|
|
606
|
+
return {
|
|
607
|
+
agentName: bare,
|
|
608
|
+
instanceId: instanceId || 'default',
|
|
609
|
+
note: `The pod roster couldn't be read (${err.message}), so instance ${instanceId || 'default'} was read for '${bare}'.`,
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
const seatOf = (inst) => ({ agentName: inst.name || inst.agentName, instanceId: inst.instanceId || 'default' });
|
|
613
|
+
// An @handle routes to one seat, so it wins over another seat's instance of the same name.
|
|
614
|
+
if (isHandle) {
|
|
615
|
+
const byHandle = agents.filter((inst) => typeof inst.mention === 'string' && inst.mention.toLowerCase() === want);
|
|
616
|
+
if (byHandle.length === 1) return instanceId ? { ...seatOf(byHandle[0]), instanceId } : seatOf(byHandle[0]);
|
|
617
|
+
if (instanceId) {
|
|
618
|
+
return {
|
|
619
|
+
agentName: bare,
|
|
620
|
+
instanceId,
|
|
621
|
+
note: `'${typed}' is not a handle in this pod's roster, so agent name '${bare}' was read at instance ${instanceId}.`,
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
const named = (inst) => [inst.mention, inst.name || inst.agentName, inst.instanceId !== 'default' && inst.instanceId]
|
|
626
|
+
.some((n) => typeof n === 'string' && n.toLowerCase() === want);
|
|
627
|
+
const hits = agents.filter(named);
|
|
628
|
+
if (hits.length > 1) {
|
|
629
|
+
const pairs = hits.map((h) => `${h.name || h.agentName}/${h.instanceId}`).join(', ');
|
|
630
|
+
throw new Error(`${hits.length} seats are named '${typed}' here: ${pairs}. `
|
|
631
|
+
+ `Run: commonly agent logs <agentName> --pod ${podId} --instance-id <instance>`);
|
|
632
|
+
}
|
|
633
|
+
if (hits.length === 0) {
|
|
634
|
+
// A typo must not read like an idle seat, so say what was read instead.
|
|
635
|
+
const handles = agents.map((inst) => inst.mention).filter(Boolean).map((m) => `@${m}`).join(', ');
|
|
636
|
+
return {
|
|
637
|
+
agentName: bare,
|
|
638
|
+
instanceId: 'default',
|
|
639
|
+
note: `'${typed}' is not in this pod's roster, so instance default was read.${handles ? ` Seats here: ${handles}` : ''}`,
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
return seatOf(hits[0]);
|
|
643
|
+
};
|
|
644
|
+
|
|
582
645
|
/**
|
|
583
646
|
* Enumerate every agent attached on this laptop — i.e. every file in
|
|
584
647
|
* ~/.commonly/tokens/*.json. Cross-references the session store for a
|
|
@@ -4466,7 +4529,7 @@ Use --local to find the name you'd pass to 'agent run' or 'agent detach'.
|
|
|
4466
4529
|
.command('logs <name>')
|
|
4467
4530
|
.description("Show a seat's recent log: local daemon log, or server activity with --pod")
|
|
4468
4531
|
.option('--pod <podId>', 'Read server-side events and runs for a pod you belong to')
|
|
4469
|
-
.option('--instance-id <id>', 'Seat instance ID
|
|
4532
|
+
.option('--instance-id <id>', 'Seat instance ID (with --pod, found from the name when omitted)')
|
|
4470
4533
|
.option('--follow', 'Continue printing new output', false)
|
|
4471
4534
|
.option('--lines <n>', 'Number of trailing local lines to show', '50')
|
|
4472
4535
|
.option('--instance <url>', 'Target Commonly instance (with --pod)')
|
|
@@ -4520,10 +4583,12 @@ Use --local to find the name you'd pass to 'agent run' or 'agent detach'.
|
|
|
4520
4583
|
if (!token) { console.error('Not logged in. Run: commonly login'); process.exit(1); }
|
|
4521
4584
|
const client = createClient({ instance: resolveInstanceUrl(opts.instance), token });
|
|
4522
4585
|
const printActivity = async () => {
|
|
4586
|
+
const seat = await resolveLogsSeat(client, opts.pod, name, opts.instanceId);
|
|
4587
|
+
if (seat.note) console.error(seat.note);
|
|
4523
4588
|
const { events = [], runs = [] } = await client.get('/api/agents/seat-activity', {
|
|
4524
4589
|
podId: opts.pod,
|
|
4525
|
-
agentName:
|
|
4526
|
-
instanceId:
|
|
4590
|
+
agentName: seat.agentName,
|
|
4591
|
+
instanceId: seat.instanceId,
|
|
4527
4592
|
});
|
|
4528
4593
|
events.forEach((e) => {
|
|
4529
4594
|
const ts = new Date(e.createdAt).toTimeString().slice(0, 8);
|