@pingagent/sdk 0.1.10 → 0.1.11
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/pingagent.js +878 -6
- package/dist/chunk-6OA4F66H.js +3193 -0
- package/dist/chunk-BLHMTUID.js +3610 -0
- package/dist/chunk-HCQ7CEDE.js +3556 -0
- package/dist/chunk-R3D7LOGB.js +3553 -0
- package/dist/chunk-SMDQYV7Z.js +3173 -0
- package/dist/chunk-YBNFPOKO.js +3553 -0
- package/dist/index.d.ts +229 -8
- package/dist/index.js +21 -1
- package/dist/web-server.js +732 -13
- package/package.json +3 -3
package/bin/pingagent.js
CHANGED
|
@@ -4,6 +4,7 @@ import * as fs from 'node:fs';
|
|
|
4
4
|
import * as path from 'node:path';
|
|
5
5
|
import * as os from 'node:os';
|
|
6
6
|
import * as readline from 'node:readline';
|
|
7
|
+
import { spawnSync } from 'node:child_process';
|
|
7
8
|
import {
|
|
8
9
|
PingAgentClient,
|
|
9
10
|
generateIdentity,
|
|
@@ -17,10 +18,16 @@ import {
|
|
|
17
18
|
HistoryManager,
|
|
18
19
|
A2AAdapter,
|
|
19
20
|
SessionManager,
|
|
21
|
+
SessionSummaryManager,
|
|
20
22
|
TaskThreadManager,
|
|
23
|
+
TaskHandoffManager,
|
|
21
24
|
TrustPolicyAuditManager,
|
|
25
|
+
TrustRecommendationManager,
|
|
26
|
+
getTrustRecommendationActionLabel,
|
|
27
|
+
formatCapabilityCardSummary,
|
|
22
28
|
defaultTrustPolicyDoc,
|
|
23
29
|
normalizeTrustPolicyDoc,
|
|
30
|
+
upsertTrustPolicyRecommendation,
|
|
24
31
|
getActiveSessionFilePath,
|
|
25
32
|
getSessionMapFilePath,
|
|
26
33
|
getSessionBindingAlertsFilePath,
|
|
@@ -29,12 +36,16 @@ import {
|
|
|
29
36
|
readSessionBindingAlerts,
|
|
30
37
|
setSessionBinding,
|
|
31
38
|
removeSessionBinding,
|
|
39
|
+
readIngressRuntimeStatus,
|
|
32
40
|
} from '../dist/index.js';
|
|
33
41
|
import { ERROR_HINTS, SCHEMA_TEXT } from '@pingagent/schemas';
|
|
34
42
|
|
|
35
43
|
const DEFAULT_SERVER = 'https://pingagent.chat';
|
|
36
44
|
const UPGRADE_URL = 'https://pingagent.chat';
|
|
37
45
|
const DEFAULT_IDENTITY_PATH = path.join(os.homedir(), '.pingagent', 'identity.json');
|
|
46
|
+
const OFFICIAL_HOSTED_ORIGIN = new URL(DEFAULT_SERVER).origin;
|
|
47
|
+
const hostedPublicLinkAttempts = new Set();
|
|
48
|
+
const SESSION_SUMMARY_FIELDS = ['objective', 'context', 'constraints', 'decisions', 'open_questions', 'next_action', 'handoff_ready_text'];
|
|
38
49
|
|
|
39
50
|
function resolvePath(p) {
|
|
40
51
|
if (!p) return p;
|
|
@@ -42,6 +53,18 @@ function resolvePath(p) {
|
|
|
42
53
|
return p;
|
|
43
54
|
}
|
|
44
55
|
|
|
56
|
+
function normalizeOrigin(input) {
|
|
57
|
+
try {
|
|
58
|
+
return new URL(String(input ?? '')).origin;
|
|
59
|
+
} catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function isOfficialHostedServer(serverUrl) {
|
|
65
|
+
return normalizeOrigin(serverUrl) === OFFICIAL_HOSTED_ORIGIN;
|
|
66
|
+
}
|
|
67
|
+
|
|
45
68
|
function getEffectiveIdentityPath() {
|
|
46
69
|
const dir = program.opts().identityDir;
|
|
47
70
|
if (dir) return path.join(resolvePath(dir), 'identity.json');
|
|
@@ -108,6 +131,38 @@ function readTrustPolicyDoc(identityPath) {
|
|
|
108
131
|
}
|
|
109
132
|
}
|
|
110
133
|
|
|
134
|
+
function writeTrustPolicyDoc(identityPath, doc) {
|
|
135
|
+
const policyPath = getTrustPolicyPath(identityPath);
|
|
136
|
+
fs.mkdirSync(path.dirname(policyPath), { recursive: true, mode: 0o700 });
|
|
137
|
+
fs.writeFileSync(policyPath, JSON.stringify(normalizeTrustPolicyDoc(doc), null, 2), 'utf-8');
|
|
138
|
+
return policyPath;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function findOpenClawInstallScript() {
|
|
142
|
+
const explicit = process.env.PINGAGENT_OPENCLAW_INSTALL_BIN;
|
|
143
|
+
if (explicit) return { cmd: process.execPath, args: [resolvePath(explicit)] };
|
|
144
|
+
const repoScript = path.resolve(process.cwd(), 'packages', 'openclaw-install', 'install.mjs');
|
|
145
|
+
if (fs.existsSync(repoScript)) return { cmd: process.execPath, args: [repoScript] };
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function runOpenClawInstall(args) {
|
|
150
|
+
const resolved = findOpenClawInstallScript();
|
|
151
|
+
if (!resolved) {
|
|
152
|
+
return { ok: false, stdout: '', stderr: 'OpenClaw installer script not found locally. Set PINGAGENT_OPENCLAW_INSTALL_BIN.' };
|
|
153
|
+
}
|
|
154
|
+
const result = spawnSync(resolved.cmd, [...resolved.args, ...args], {
|
|
155
|
+
encoding: 'utf-8',
|
|
156
|
+
env: process.env,
|
|
157
|
+
});
|
|
158
|
+
return {
|
|
159
|
+
ok: result.status === 0,
|
|
160
|
+
stdout: String(result.stdout ?? ''),
|
|
161
|
+
stderr: String(result.stderr ?? ''),
|
|
162
|
+
status: result.status ?? 1,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
111
166
|
function clearScreen() {
|
|
112
167
|
process.stdout.write('\x1Bc');
|
|
113
168
|
}
|
|
@@ -173,6 +228,7 @@ function buildTaskExportBody(task, format = 'plain') {
|
|
|
173
228
|
message: task.error_message,
|
|
174
229
|
}
|
|
175
230
|
: null,
|
|
231
|
+
handoff: task.handoff || null,
|
|
176
232
|
};
|
|
177
233
|
if (format === 'json') return JSON.stringify(payload, null, 2);
|
|
178
234
|
return [
|
|
@@ -232,7 +288,9 @@ function buildHostState(identityPath, selectedSessionKey = null, historyPageInde
|
|
|
232
288
|
const store = openStore(identityPath);
|
|
233
289
|
try {
|
|
234
290
|
const sessionManager = new SessionManager(store);
|
|
291
|
+
const sessionSummaryManager = new SessionSummaryManager(store);
|
|
235
292
|
const taskManager = new TaskThreadManager(store);
|
|
293
|
+
const taskHandoffManager = new TaskHandoffManager(store);
|
|
236
294
|
const historyManager = new HistoryManager(store);
|
|
237
295
|
const auditManager = new TrustPolicyAuditManager(store);
|
|
238
296
|
const sessions = sessionManager.listRecentSessions(50);
|
|
@@ -241,6 +299,7 @@ function buildHostState(identityPath, selectedSessionKey = null, historyPageInde
|
|
|
241
299
|
const bindingByConversation = new Map(bindings.map((row) => [row.conversation_id, row]));
|
|
242
300
|
const alertByConversation = new Map(alerts.map((row) => [row.conversation_id, row]));
|
|
243
301
|
const activeChatSession = readCurrentActiveSessionKey();
|
|
302
|
+
const ingressRuntime = readIngressRuntimeStatus();
|
|
244
303
|
const desiredSelectedSessionKey =
|
|
245
304
|
(selectedSessionKey && sessions.some((session) => session.session_key === selectedSessionKey) ? selectedSessionKey : null) ??
|
|
246
305
|
sessionManager.getActiveSession()?.session_key ??
|
|
@@ -255,8 +314,14 @@ function buildHostState(identityPath, selectedSessionKey = null, historyPageInde
|
|
|
255
314
|
is_active_chat_session: session.session_key === activeChatSession,
|
|
256
315
|
}));
|
|
257
316
|
const selectedSession = sessionsWithMeta.find((session) => session.session_key === desiredSelectedSessionKey) ?? null;
|
|
317
|
+
const selectedSessionSummary = selectedSession
|
|
318
|
+
? sessionSummaryManager.get(selectedSession.session_key)
|
|
319
|
+
: null;
|
|
258
320
|
const selectedTasks = selectedSession
|
|
259
|
-
? taskManager.listBySession(selectedSession.session_key, 12)
|
|
321
|
+
? taskManager.listBySession(selectedSession.session_key, 12).map((task) => ({
|
|
322
|
+
...task,
|
|
323
|
+
handoff: taskHandoffManager.get(task.task_id),
|
|
324
|
+
}))
|
|
260
325
|
: [];
|
|
261
326
|
const selectedAuditEvents = selectedSession
|
|
262
327
|
? auditManager.listBySession(selectedSession.session_key, 12)
|
|
@@ -270,27 +335,45 @@ function buildHostState(identityPath, selectedSessionKey = null, historyPageInde
|
|
|
270
335
|
const selectedHistorySearchResults = selectedSession?.conversation_id && historySearchQuery.trim()
|
|
271
336
|
? historyManager.search(historySearchQuery.trim(), { conversationId: selectedSession.conversation_id, limit: 50 })
|
|
272
337
|
: [];
|
|
338
|
+
const recommendationManager = new TrustRecommendationManager(store);
|
|
339
|
+
recommendationManager.sync({
|
|
340
|
+
policyDoc: policy.doc,
|
|
341
|
+
sessions,
|
|
342
|
+
tasks: taskManager.listRecent(100),
|
|
343
|
+
auditEvents: auditManager.listRecent(200),
|
|
344
|
+
runtimeMode,
|
|
345
|
+
limit: 50,
|
|
346
|
+
});
|
|
347
|
+
const selectedRecommendations = selectedSession?.remote_did
|
|
348
|
+
? recommendationManager.list({ remoteDid: selectedSession.remote_did, limit: 10 })
|
|
349
|
+
: [];
|
|
273
350
|
const unreadTotal = sessionsWithMeta.reduce((sum, session) => sum + (session.unread_count ?? 0), 0);
|
|
274
351
|
const alertSessions = sessionsWithMeta.filter((session) => !!session.binding_alert).length;
|
|
275
352
|
return {
|
|
276
353
|
identity,
|
|
277
354
|
runtimeMode,
|
|
278
355
|
activeChatSession,
|
|
356
|
+
ingressRuntime,
|
|
279
357
|
activeChatSessionFile: getActiveSessionFilePath(),
|
|
280
358
|
sessionMapPath: getSessionMapFilePath(),
|
|
281
359
|
sessionBindingAlertsPath: getSessionBindingAlertsFilePath(),
|
|
282
360
|
policyPath: policy.path,
|
|
283
361
|
policyDoc: policy.doc,
|
|
284
362
|
sessions: sessionsWithMeta,
|
|
285
|
-
tasks: taskManager.listRecent(30)
|
|
363
|
+
tasks: taskManager.listRecent(30).map((task) => ({
|
|
364
|
+
...task,
|
|
365
|
+
handoff: taskHandoffManager.get(task.task_id),
|
|
366
|
+
})),
|
|
286
367
|
auditEvents: auditManager.listRecent(40),
|
|
287
368
|
selectedSession,
|
|
369
|
+
selectedSessionSummary,
|
|
288
370
|
selectedTasks,
|
|
289
371
|
selectedAuditEvents,
|
|
290
372
|
selectedMessages,
|
|
291
373
|
selectedHistoryPage,
|
|
292
374
|
selectedHistorySearchQuery: historySearchQuery.trim(),
|
|
293
375
|
selectedHistorySearchResults,
|
|
376
|
+
selectedRecommendations,
|
|
294
377
|
unreadTotal,
|
|
295
378
|
alertSessions,
|
|
296
379
|
};
|
|
@@ -302,9 +385,13 @@ function buildHostState(identityPath, selectedSessionKey = null, historyPageInde
|
|
|
302
385
|
function renderHostTuiScreen(hostState, uiState) {
|
|
303
386
|
const sessions = hostState.sessions || [];
|
|
304
387
|
const selected = hostState.selectedSession || sessions[0] || null;
|
|
388
|
+
const selectedSummary = hostState.selectedSessionSummary || null;
|
|
305
389
|
const tasks = hostState.selectedTasks || [];
|
|
306
390
|
const auditEvents = hostState.selectedAuditEvents || [];
|
|
307
391
|
const messages = hostState.selectedMessages || [];
|
|
392
|
+
const recommendations = hostState.selectedRecommendations || [];
|
|
393
|
+
const openRecommendation = recommendations.find((item) => item.status === 'open') || null;
|
|
394
|
+
const reopenRecommendation = recommendations.find((item) => item.status === 'dismissed' || item.status === 'superseded') || null;
|
|
308
395
|
const historyPage = hostState.selectedHistoryPage || { messages: [], pageIndex: 0, hasOlder: false, hasNewer: false };
|
|
309
396
|
const historySearchQuery = hostState.selectedHistorySearchQuery || '';
|
|
310
397
|
const historySearchResults = hostState.selectedHistorySearchResults || [];
|
|
@@ -315,17 +402,24 @@ function renderHostTuiScreen(hostState, uiState) {
|
|
|
315
402
|
? ` (${Math.max(0, Math.ceil((uiState.statusExpiresAt - Date.now()) / 1000))}s)`
|
|
316
403
|
: '';
|
|
317
404
|
const statusTs = uiState?.statusAt ? ` @ ${formatStatusTimestamp(uiState.statusAt)}` : '';
|
|
405
|
+
const degraded = !hostState.ingressRuntime
|
|
406
|
+
|| hostState.ingressRuntime.receive_mode === 'polling_degraded'
|
|
407
|
+
|| !!hostState.ingressRuntime.hooks_last_error;
|
|
408
|
+
const ingressLabel = degraded ? 'Degraded' : 'Ready';
|
|
318
409
|
const lines = [
|
|
319
410
|
'PingAgent Host TUI',
|
|
320
411
|
`DID: ${hostState.identity.did}`,
|
|
321
412
|
`status=${formatStatusLine(uiState?.statusLevel || 'info', uiState?.statusMessage || '(ready)')}${statusTs}${statusCountdown}`,
|
|
322
|
-
`runtime_mode=${hostState.runtimeMode} active_chat_session=${hostState.activeChatSession || '(none)'}`,
|
|
413
|
+
`runtime_mode=${hostState.runtimeMode} receive_mode=${hostState.ingressRuntime?.receive_mode || 'webhook'} active_chat_session=${hostState.activeChatSession || '(none)'}`,
|
|
414
|
+
`ingress=${ingressLabel}${degraded ? ' action=[f] fix-now' : ''}`,
|
|
415
|
+
uiState?.publicLinkUrl ? `public_link=${uiState.publicLinkUrl}` : null,
|
|
323
416
|
`sessions=${sessions.length} unread_total=${hostState.unreadTotal ?? 0} alert_sessions=${hostState.alertSessions ?? 0} view=${view}`,
|
|
324
417
|
`policy=${hostState.policyPath}`,
|
|
325
418
|
`session_map=${hostState.sessionMapPath}`,
|
|
326
419
|
`binding_alerts=${hostState.sessionBindingAlertsPath}`,
|
|
420
|
+
hostState.ingressRuntime?.hooks_last_error ? `hooks_error=${truncateLine(hostState.ingressRuntime.hooks_last_error, 120)}` : null,
|
|
327
421
|
'',
|
|
328
|
-
];
|
|
422
|
+
].filter(Boolean);
|
|
329
423
|
|
|
330
424
|
if (view === 'help') {
|
|
331
425
|
lines.push('Help');
|
|
@@ -339,10 +433,16 @@ function renderHostTuiScreen(hostState, uiState) {
|
|
|
339
433
|
lines.push('- t: open task list view for selected session');
|
|
340
434
|
lines.push('- x: cancel selected task (in task views)');
|
|
341
435
|
lines.push('- p: multiline reply prompt (detail view)');
|
|
436
|
+
lines.push('- S: edit carry-forward summary (detail view)');
|
|
437
|
+
lines.push('- d: try demo agent preset');
|
|
342
438
|
lines.push('- o: open local history paging (detail view)');
|
|
343
439
|
lines.push('- n / p: older / newer history page (history view)');
|
|
344
440
|
lines.push('- s or /: search local history (history view)');
|
|
345
441
|
lines.push('- y: dump task detail to stdout (task-detail view, choose json/plain)');
|
|
442
|
+
lines.push('- f: repair OpenClaw hooks config');
|
|
443
|
+
lines.push('- A: apply first open trust recommendation for selected session');
|
|
444
|
+
lines.push('- D: dismiss current open recommendation');
|
|
445
|
+
lines.push('- R: reopen dismissed/superseded recommendation');
|
|
346
446
|
lines.push('- b: bind selected conversation to current chat session');
|
|
347
447
|
lines.push('- c: clear selected binding');
|
|
348
448
|
lines.push('- q: quit');
|
|
@@ -382,8 +482,17 @@ function renderHostTuiScreen(hostState, uiState) {
|
|
|
382
482
|
lines.push(`status=${selectedTask.status}`);
|
|
383
483
|
lines.push(`updated_at=${formatTs(selectedTask.updated_at, false)}`);
|
|
384
484
|
if (selectedTask.started_at) lines.push(`started_at=${formatTs(selectedTask.started_at, false)}`);
|
|
485
|
+
if (selectedTask.handoff?.objective) lines.push(`handoff_objective=${selectedTask.handoff.objective}`);
|
|
486
|
+
if (selectedTask.handoff?.priority) lines.push(`handoff_priority=${selectedTask.handoff.priority}`);
|
|
487
|
+
if (selectedTask.handoff?.success_criteria) lines.push(`handoff_success=${selectedTask.handoff.success_criteria}`);
|
|
488
|
+
if (selectedTask.handoff?.callback_session_key) lines.push(`handoff_callback=${selectedTask.handoff.callback_session_key}`);
|
|
385
489
|
lines.push('actions=[x] cancel-task [y] dump-stdout [j/k] switch-task [h/Esc] back-to-tasks');
|
|
386
490
|
lines.push('');
|
|
491
|
+
if (selectedTask.handoff?.carry_forward_summary) {
|
|
492
|
+
lines.push('Handoff Summary');
|
|
493
|
+
lines.push(selectedTask.handoff.carry_forward_summary);
|
|
494
|
+
lines.push('');
|
|
495
|
+
}
|
|
387
496
|
lines.push('Result');
|
|
388
497
|
lines.push(selectedTask.result_summary || '(none)');
|
|
389
498
|
lines.push('');
|
|
@@ -409,6 +518,8 @@ function renderHostTuiScreen(hostState, uiState) {
|
|
|
409
518
|
lines.push(`status=${selectedTask.status}`);
|
|
410
519
|
lines.push(`updated_at=${formatTs(selectedTask.updated_at, false)}`);
|
|
411
520
|
if (selectedTask.started_at) lines.push(`started_at=${formatTs(selectedTask.started_at, false)}`);
|
|
521
|
+
if (selectedTask.handoff?.objective) lines.push(`handoff_objective=${selectedTask.handoff.objective}`);
|
|
522
|
+
if (selectedTask.handoff?.priority) lines.push(`handoff_priority=${selectedTask.handoff.priority}`);
|
|
412
523
|
if (selectedTask.result_summary) lines.push(`result=${selectedTask.result_summary}`);
|
|
413
524
|
if (selectedTask.error_code || selectedTask.error_message) {
|
|
414
525
|
lines.push(`error=${selectedTask.error_code || 'E_TASK'} ${selectedTask.error_message || ''}`.trim());
|
|
@@ -440,10 +551,26 @@ function renderHostTuiScreen(hostState, uiState) {
|
|
|
440
551
|
} else {
|
|
441
552
|
lines.push('needs_rebind=false');
|
|
442
553
|
}
|
|
554
|
+
if (openRecommendation) {
|
|
555
|
+
lines.push(`trust_action=${getTrustRecommendationActionLabel(openRecommendation)}`);
|
|
556
|
+
} else if (reopenRecommendation) {
|
|
557
|
+
lines.push(`trust_action=${getTrustRecommendationActionLabel(reopenRecommendation)}`);
|
|
558
|
+
}
|
|
559
|
+
if (selectedSummary) {
|
|
560
|
+
lines.push(`summary_objective=${selectedSummary.objective || '(none)'}`);
|
|
561
|
+
lines.push(`summary_next_action=${selectedSummary.next_action || '(none)'}`);
|
|
562
|
+
} else {
|
|
563
|
+
lines.push('summary_objective=(none)');
|
|
564
|
+
}
|
|
443
565
|
const actionBar = [
|
|
444
566
|
selected.trust_state === 'pending' ? '[a] approve' : null,
|
|
567
|
+
'[A] apply-rec',
|
|
568
|
+
'[D] dismiss-rec',
|
|
569
|
+
'[R] reopen-rec',
|
|
445
570
|
'[m] mark-read',
|
|
571
|
+
'[d] demo',
|
|
446
572
|
'[p] reply',
|
|
573
|
+
'[S] summary',
|
|
447
574
|
'[o] history',
|
|
448
575
|
'[t] tasks',
|
|
449
576
|
'[b] bind-current',
|
|
@@ -451,9 +578,22 @@ function renderHostTuiScreen(hostState, uiState) {
|
|
|
451
578
|
].filter(Boolean).join(' ');
|
|
452
579
|
lines.push(`actions=${actionBar}`);
|
|
453
580
|
lines.push('');
|
|
581
|
+
lines.push('Carry-Forward Summary');
|
|
582
|
+
if (selectedSummary) {
|
|
583
|
+
lines.push(`- objective: ${selectedSummary.objective || '(none)'}`);
|
|
584
|
+
lines.push(`- context: ${truncateLine(selectedSummary.context || '(none)', 100)}`);
|
|
585
|
+
lines.push(`- constraints: ${truncateLine(selectedSummary.constraints || '(none)', 100)}`);
|
|
586
|
+
lines.push(`- decisions: ${truncateLine(selectedSummary.decisions || '(none)', 100)}`);
|
|
587
|
+
lines.push(`- open_questions: ${truncateLine(selectedSummary.open_questions || '(none)', 100)}`);
|
|
588
|
+
lines.push(`- next_action: ${truncateLine(selectedSummary.next_action || '(none)', 100)}`);
|
|
589
|
+
lines.push(`- handoff_ready: ${truncateLine(selectedSummary.handoff_ready_text || '(none)', 100)}`);
|
|
590
|
+
} else {
|
|
591
|
+
lines.push('- none');
|
|
592
|
+
}
|
|
593
|
+
lines.push('');
|
|
454
594
|
lines.push('Tasks');
|
|
455
595
|
lines.push(...(tasks.length
|
|
456
|
-
? tasks.map((task) => `- ${task.title || task.task_id} [${task.status}] ${truncateLine(task.result_summary || task.error_message || '', 80)}`)
|
|
596
|
+
? tasks.map((task) => `- ${task.title || task.task_id} [${task.status}]${task.handoff?.objective ? ` handoff=${truncateLine(task.handoff.objective, 30)}` : ''} ${truncateLine(task.result_summary || task.error_message || '', 80)}`)
|
|
457
597
|
: ['- none']));
|
|
458
598
|
if (view === 'detail') {
|
|
459
599
|
lines.push('');
|
|
@@ -471,7 +611,7 @@ function renderHostTuiScreen(hostState, uiState) {
|
|
|
471
611
|
}
|
|
472
612
|
|
|
473
613
|
lines.push('');
|
|
474
|
-
lines.push('Keys: ↑/↓ or j/k select Enter/l open Esc/h back g/G jump r refresh a approve m read p reply o history s search t tasks x cancel-task y dump b bind c clear ? help q quit');
|
|
614
|
+
lines.push('Keys: ↑/↓ or j/k select Enter/l open Esc/h back g/G jump r refresh a approve A apply-rec D dismiss-rec R reopen-rec d demo m read p reply o history s search t tasks x cancel-task y dump f fix-hooks b bind c clear ? help q quit');
|
|
475
615
|
return lines.join('\n');
|
|
476
616
|
}
|
|
477
617
|
|
|
@@ -489,6 +629,7 @@ async function runHostTui(identityPath, opts) {
|
|
|
489
629
|
statusAt: 0,
|
|
490
630
|
selectedHistoryPageIndex: 0,
|
|
491
631
|
historySearchQuery: '',
|
|
632
|
+
publicLinkUrl: '',
|
|
492
633
|
};
|
|
493
634
|
|
|
494
635
|
const render = () => {
|
|
@@ -511,6 +652,9 @@ async function runHostTui(identityPath, opts) {
|
|
|
511
652
|
};
|
|
512
653
|
|
|
513
654
|
if (once) {
|
|
655
|
+
void maybeEnsureHostedPublicLink(identityPath).then((res) => {
|
|
656
|
+
if (res?.data?.public_url) uiState.publicLinkUrl = res.data.public_url;
|
|
657
|
+
});
|
|
514
658
|
const { screen } = render();
|
|
515
659
|
console.log(screen);
|
|
516
660
|
return;
|
|
@@ -546,6 +690,132 @@ async function runHostTui(identityPath, opts) {
|
|
|
546
690
|
uiState.statusAt = Date.now();
|
|
547
691
|
};
|
|
548
692
|
|
|
693
|
+
const applySessionRecommendation = (selected) => {
|
|
694
|
+
if (!selected?.remote_did) return { ok: false, message: 'No remote DID for selected session.' };
|
|
695
|
+
const store = openStore(identityPath);
|
|
696
|
+
try {
|
|
697
|
+
const { path: policyPath, doc } = readTrustPolicyDoc(identityPath);
|
|
698
|
+
const auditManager = new TrustPolicyAuditManager(store);
|
|
699
|
+
const recommendationManager = new TrustRecommendationManager(store);
|
|
700
|
+
recommendationManager.sync({
|
|
701
|
+
policyDoc: doc,
|
|
702
|
+
sessions: new SessionManager(store).listRecentSessions(100),
|
|
703
|
+
tasks: new TaskThreadManager(store).listRecent(100),
|
|
704
|
+
auditEvents: auditManager.listRecent(200),
|
|
705
|
+
runtimeMode: process.env.PINGAGENT_RUNTIME_MODE || 'bridge',
|
|
706
|
+
limit: 50,
|
|
707
|
+
});
|
|
708
|
+
const recommendation = recommendationManager.list({
|
|
709
|
+
remoteDid: selected.remote_did,
|
|
710
|
+
status: 'open',
|
|
711
|
+
limit: 1,
|
|
712
|
+
})[0];
|
|
713
|
+
if (!recommendation) return { ok: false, message: 'No open recommendation for this session.' };
|
|
714
|
+
const nextDoc = upsertTrustPolicyRecommendation(doc, recommendation);
|
|
715
|
+
writeTrustPolicyDoc(identityPath, nextDoc);
|
|
716
|
+
recommendationManager.apply(recommendation.id);
|
|
717
|
+
auditManager.record({
|
|
718
|
+
event_type: 'recommendation_applied',
|
|
719
|
+
policy_scope: recommendation.policy,
|
|
720
|
+
remote_did: recommendation.remote_did,
|
|
721
|
+
action: String(recommendation.action),
|
|
722
|
+
outcome: 'recommendation_applied',
|
|
723
|
+
explanation: recommendation.reason,
|
|
724
|
+
matched_rule: recommendation.match,
|
|
725
|
+
detail: { recommendation_id: recommendation.id, session_key: selected.session_key },
|
|
726
|
+
});
|
|
727
|
+
return { ok: true, message: `${getTrustRecommendationActionLabel(recommendation)} (${recommendation.policy})`, path: policyPath };
|
|
728
|
+
} finally {
|
|
729
|
+
store.close();
|
|
730
|
+
}
|
|
731
|
+
};
|
|
732
|
+
|
|
733
|
+
const dismissSessionRecommendation = (selected) => {
|
|
734
|
+
if (!selected?.remote_did) return { ok: false, message: 'No remote DID for selected session.' };
|
|
735
|
+
const store = openStore(identityPath);
|
|
736
|
+
try {
|
|
737
|
+
const recommendationManager = new TrustRecommendationManager(store);
|
|
738
|
+
const recommendation = recommendationManager.list({
|
|
739
|
+
remoteDid: selected.remote_did,
|
|
740
|
+
status: 'open',
|
|
741
|
+
limit: 1,
|
|
742
|
+
})[0];
|
|
743
|
+
if (!recommendation) return { ok: false, message: 'No open recommendation for this session.' };
|
|
744
|
+
recommendationManager.dismiss(recommendation.id);
|
|
745
|
+
new TrustPolicyAuditManager(store).record({
|
|
746
|
+
event_type: 'recommendation_dismissed',
|
|
747
|
+
policy_scope: recommendation.policy,
|
|
748
|
+
remote_did: recommendation.remote_did,
|
|
749
|
+
action: String(recommendation.action),
|
|
750
|
+
outcome: 'recommendation_dismissed',
|
|
751
|
+
explanation: recommendation.reason,
|
|
752
|
+
matched_rule: recommendation.match,
|
|
753
|
+
detail: { recommendation_id: recommendation.id, session_key: selected.session_key },
|
|
754
|
+
});
|
|
755
|
+
return { ok: true, message: `Dismissed ${getTrustRecommendationActionLabel(recommendation)}` };
|
|
756
|
+
} finally {
|
|
757
|
+
store.close();
|
|
758
|
+
}
|
|
759
|
+
};
|
|
760
|
+
|
|
761
|
+
const reopenSessionRecommendation = (selected) => {
|
|
762
|
+
if (!selected?.remote_did) return { ok: false, message: 'No remote DID for selected session.' };
|
|
763
|
+
const store = openStore(identityPath);
|
|
764
|
+
try {
|
|
765
|
+
const recommendationManager = new TrustRecommendationManager(store);
|
|
766
|
+
const recommendation = recommendationManager.list({
|
|
767
|
+
remoteDid: selected.remote_did,
|
|
768
|
+
status: ['dismissed', 'superseded'],
|
|
769
|
+
limit: 1,
|
|
770
|
+
})[0];
|
|
771
|
+
if (!recommendation) return { ok: false, message: 'No dismissed or superseded recommendation for this session.' };
|
|
772
|
+
recommendationManager.reopen(recommendation.id);
|
|
773
|
+
new TrustPolicyAuditManager(store).record({
|
|
774
|
+
event_type: 'recommendation_reopened',
|
|
775
|
+
policy_scope: recommendation.policy,
|
|
776
|
+
remote_did: recommendation.remote_did,
|
|
777
|
+
action: String(recommendation.action),
|
|
778
|
+
outcome: 'recommendation_reopened',
|
|
779
|
+
explanation: recommendation.reason,
|
|
780
|
+
matched_rule: recommendation.match,
|
|
781
|
+
detail: { recommendation_id: recommendation.id, session_key: selected.session_key },
|
|
782
|
+
});
|
|
783
|
+
return { ok: true, message: 'Reopened recommendation' };
|
|
784
|
+
} finally {
|
|
785
|
+
store.close();
|
|
786
|
+
}
|
|
787
|
+
};
|
|
788
|
+
|
|
789
|
+
const sendDemoPreset = async () => {
|
|
790
|
+
const answer = await promptLine('Demo preset [hello/delegate/trust] (default hello): ');
|
|
791
|
+
const preset = (answer.trim().toLowerCase() || 'hello');
|
|
792
|
+
const presetMessages = {
|
|
793
|
+
hello: 'Hello',
|
|
794
|
+
delegate: 'Please show me how task delegation works in PingAgent.',
|
|
795
|
+
trust: 'Show me how trust decisions and recommendations work.',
|
|
796
|
+
};
|
|
797
|
+
const message = presetMessages[preset] || presetMessages.hello;
|
|
798
|
+
const { client, store } = await getClientWithStore(identityPath);
|
|
799
|
+
try {
|
|
800
|
+
const resolved = await client.resolveAlias('pingagent/demo');
|
|
801
|
+
if (!resolved.ok || !resolved.data?.did) {
|
|
802
|
+
setStatus(`Demo resolve failed: ${resolved.error?.message || 'unknown error'}`, 'err');
|
|
803
|
+
return;
|
|
804
|
+
}
|
|
805
|
+
const convo = await client.openConversation(resolved.data.did);
|
|
806
|
+
if (!convo.ok || !convo.data?.conversation_id) {
|
|
807
|
+
setStatus(`Demo open failed: ${convo.error?.message || 'unknown error'}`, 'err');
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
const sendRes = await client.sendMessage(convo.data.conversation_id, SCHEMA_TEXT, { text: message });
|
|
811
|
+
setStatus(sendRes.ok
|
|
812
|
+
? `Demo preset sent (${preset}) conversation=${convo.data.conversation_id}`
|
|
813
|
+
: `Demo send failed: ${sendRes.error?.message || 'unknown error'}`, sendRes.ok ? 'ok' : 'err', sendRes.ok ? 7000 : 9000);
|
|
814
|
+
} finally {
|
|
815
|
+
store.close();
|
|
816
|
+
}
|
|
817
|
+
};
|
|
818
|
+
|
|
549
819
|
const promptLine = async (question) => {
|
|
550
820
|
stopInterval();
|
|
551
821
|
if (process.stdin.setRawMode) process.stdin.setRawMode(false);
|
|
@@ -655,6 +925,13 @@ async function runHostTui(identityPath, opts) {
|
|
|
655
925
|
};
|
|
656
926
|
|
|
657
927
|
startInterval();
|
|
928
|
+
void maybeEnsureHostedPublicLink(identityPath).then((result) => {
|
|
929
|
+
if (result?.data?.public_url) {
|
|
930
|
+
uiState.publicLinkUrl = result.data.public_url;
|
|
931
|
+
setStatus(result.created ? `Public link ready: ${result.data.public_url}` : `Public link available: ${result.data.public_url}`, 'ok', 7000);
|
|
932
|
+
latestState = redraw();
|
|
933
|
+
}
|
|
934
|
+
});
|
|
658
935
|
|
|
659
936
|
process.stdin.on('keypress', async (_str, key) => {
|
|
660
937
|
if (key?.name === 'q' || (key?.ctrl && key?.name === 'c')) {
|
|
@@ -762,6 +1039,35 @@ async function runHostTui(identityPath, opts) {
|
|
|
762
1039
|
latestState = redraw();
|
|
763
1040
|
return;
|
|
764
1041
|
}
|
|
1042
|
+
if (_str === 'A') {
|
|
1043
|
+
const selected = latestState.selectedSession;
|
|
1044
|
+
if (!selected) return;
|
|
1045
|
+
const result = applySessionRecommendation(selected);
|
|
1046
|
+
setStatus(result.message, result.ok ? 'ok' : 'warn');
|
|
1047
|
+
latestState = redraw();
|
|
1048
|
+
return;
|
|
1049
|
+
}
|
|
1050
|
+
if (_str === 'D') {
|
|
1051
|
+
const selected = latestState.selectedSession;
|
|
1052
|
+
if (!selected) return;
|
|
1053
|
+
const result = dismissSessionRecommendation(selected);
|
|
1054
|
+
setStatus(result.message, result.ok ? 'ok' : 'warn');
|
|
1055
|
+
latestState = redraw();
|
|
1056
|
+
return;
|
|
1057
|
+
}
|
|
1058
|
+
if (_str === 'R') {
|
|
1059
|
+
const selected = latestState.selectedSession;
|
|
1060
|
+
if (!selected) return;
|
|
1061
|
+
const result = reopenSessionRecommendation(selected);
|
|
1062
|
+
setStatus(result.message, result.ok ? 'ok' : 'warn');
|
|
1063
|
+
latestState = redraw();
|
|
1064
|
+
return;
|
|
1065
|
+
}
|
|
1066
|
+
if (key?.name === 'd') {
|
|
1067
|
+
await sendDemoPreset();
|
|
1068
|
+
latestState = redraw();
|
|
1069
|
+
return;
|
|
1070
|
+
}
|
|
765
1071
|
if (key?.name === 'p' && uiState.view === 'detail') {
|
|
766
1072
|
const selected = latestState.selectedSession;
|
|
767
1073
|
if (!selected?.conversation_id) return;
|
|
@@ -784,6 +1090,51 @@ async function runHostTui(identityPath, opts) {
|
|
|
784
1090
|
latestState = redraw();
|
|
785
1091
|
return;
|
|
786
1092
|
}
|
|
1093
|
+
if (_str === 'S' && uiState.view === 'detail') {
|
|
1094
|
+
const selected = latestState.selectedSession;
|
|
1095
|
+
if (!selected) return;
|
|
1096
|
+
const existing = latestState.selectedSessionSummary || {};
|
|
1097
|
+
const input = await promptMultiline(
|
|
1098
|
+
`Edit carry-forward summary as JSON for ${selected.remote_did || selected.session_key}\nCurrent:\n${JSON.stringify({
|
|
1099
|
+
objective: existing.objective || '',
|
|
1100
|
+
context: existing.context || '',
|
|
1101
|
+
constraints: existing.constraints || '',
|
|
1102
|
+
decisions: existing.decisions || '',
|
|
1103
|
+
open_questions: existing.open_questions || '',
|
|
1104
|
+
next_action: existing.next_action || '',
|
|
1105
|
+
handoff_ready_text: existing.handoff_ready_text || '',
|
|
1106
|
+
}, null, 2)}`
|
|
1107
|
+
);
|
|
1108
|
+
if (!input.trim()) {
|
|
1109
|
+
setStatus('Summary update cancelled.', 'warn');
|
|
1110
|
+
latestState = redraw();
|
|
1111
|
+
return;
|
|
1112
|
+
}
|
|
1113
|
+
try {
|
|
1114
|
+
const parsed = JSON.parse(input);
|
|
1115
|
+
const store = openStore(identityPath);
|
|
1116
|
+
try {
|
|
1117
|
+
const manager = new SessionSummaryManager(store);
|
|
1118
|
+
manager.upsert({
|
|
1119
|
+
session_key: selected.session_key,
|
|
1120
|
+
objective: parsed.objective,
|
|
1121
|
+
context: parsed.context,
|
|
1122
|
+
constraints: parsed.constraints,
|
|
1123
|
+
decisions: parsed.decisions,
|
|
1124
|
+
open_questions: parsed.open_questions,
|
|
1125
|
+
next_action: parsed.next_action,
|
|
1126
|
+
handoff_ready_text: parsed.handoff_ready_text,
|
|
1127
|
+
});
|
|
1128
|
+
} finally {
|
|
1129
|
+
store.close();
|
|
1130
|
+
}
|
|
1131
|
+
setStatus(`Saved carry-forward summary for ${selected.session_key}`, 'ok');
|
|
1132
|
+
} catch (error) {
|
|
1133
|
+
setStatus(`Summary update failed: ${error?.message || 'invalid JSON'}`, 'err', 9000);
|
|
1134
|
+
}
|
|
1135
|
+
latestState = redraw();
|
|
1136
|
+
return;
|
|
1137
|
+
}
|
|
787
1138
|
if (key?.name === 'o' && uiState.view === 'detail') {
|
|
788
1139
|
uiState.view = 'history';
|
|
789
1140
|
uiState.selectedHistoryPageIndex = 0;
|
|
@@ -851,6 +1202,21 @@ async function runHostTui(identityPath, opts) {
|
|
|
851
1202
|
latestState = redraw();
|
|
852
1203
|
return;
|
|
853
1204
|
}
|
|
1205
|
+
if (key?.name === 'f') {
|
|
1206
|
+
const confirmed = await confirmAction('Repair OpenClaw hooks config now? A timestamped backup will be written first.');
|
|
1207
|
+
if (!confirmed) {
|
|
1208
|
+
setStatus('Hooks repair cancelled.', 'warn');
|
|
1209
|
+
latestState = redraw();
|
|
1210
|
+
return;
|
|
1211
|
+
}
|
|
1212
|
+
const result = runOpenClawInstall(['fix-hooks']);
|
|
1213
|
+
setStatus(result.ok
|
|
1214
|
+
? `Hooks repaired.${result.stdout ? ` ${truncateLine(result.stdout.replace(/\s+/g, ' '), 100)}` : ''}`
|
|
1215
|
+
: `Hooks repair failed: ${truncateLine(result.stderr || result.stdout || 'unknown error', 120)}`,
|
|
1216
|
+
result.ok ? 'ok' : 'err', result.ok ? 7000 : 9000);
|
|
1217
|
+
latestState = redraw();
|
|
1218
|
+
return;
|
|
1219
|
+
}
|
|
854
1220
|
if (key?.name === 'y' && uiState.view === 'task-detail') {
|
|
855
1221
|
const tasks = latestState.selectedTasks || [];
|
|
856
1222
|
const task = tasks[Math.max(0, Math.min(uiState.selectedTaskIndex, Math.max(0, tasks.length - 1)))] || null;
|
|
@@ -916,6 +1282,51 @@ async function getClientWithStore(identityPath) {
|
|
|
916
1282
|
return { client, store };
|
|
917
1283
|
}
|
|
918
1284
|
|
|
1285
|
+
function resolveSessionFromStore(store, args = {}) {
|
|
1286
|
+
const sessionManager = new SessionManager(store);
|
|
1287
|
+
let session = args.sessionKey ? sessionManager.get(args.sessionKey) : null;
|
|
1288
|
+
if (!session && args.conversationId) session = sessionManager.getByConversationId(args.conversationId);
|
|
1289
|
+
if (!session && args.remoteDid) {
|
|
1290
|
+
session = sessionManager.listRecentSessions(200).find((item) => item.remote_did === args.remoteDid) ?? null;
|
|
1291
|
+
}
|
|
1292
|
+
return session ?? sessionManager.getActiveSession() ?? sessionManager.listRecentSessions(1)[0] ?? null;
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
async function resolveTarget(client, target) {
|
|
1296
|
+
const value = String(target ?? '').trim();
|
|
1297
|
+
if (!value) throw new Error('Missing target');
|
|
1298
|
+
if (value.startsWith('did:agent:')) return value;
|
|
1299
|
+
if (value.startsWith('@')) {
|
|
1300
|
+
const resolved = await client.resolveAlias(value.slice(1));
|
|
1301
|
+
if (!resolved.ok || !resolved.data?.did) {
|
|
1302
|
+
throw new Error(resolved.error?.message || `Cannot resolve alias ${value}`);
|
|
1303
|
+
}
|
|
1304
|
+
return resolved.data.did;
|
|
1305
|
+
}
|
|
1306
|
+
const publicAgent = await client.getPublicAgent(value).catch(() => ({ ok: false }));
|
|
1307
|
+
if (publicAgent.ok && publicAgent.data?.did) return publicAgent.data.did;
|
|
1308
|
+
return value;
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
async function maybeEnsureHostedPublicLink(identityPath) {
|
|
1312
|
+
const p = identityPath ?? getEffectiveIdentityPath();
|
|
1313
|
+
const id = loadIdentity(p);
|
|
1314
|
+
if (!isOfficialHostedServer(id.serverUrl ?? DEFAULT_SERVER)) return null;
|
|
1315
|
+
const key = `${p}:${normalizeOrigin(id.serverUrl ?? DEFAULT_SERVER)}`;
|
|
1316
|
+
if (hostedPublicLinkAttempts.has(key)) return null;
|
|
1317
|
+
hostedPublicLinkAttempts.add(key);
|
|
1318
|
+
try {
|
|
1319
|
+
const client = await getClient(p);
|
|
1320
|
+
const current = await client.getPublicSelf().catch(() => ({ ok: false }));
|
|
1321
|
+
if (current.ok && current.data?.public_url) return { created: false, data: current.data };
|
|
1322
|
+
const created = await client.createPublicLink({ enabled: true }).catch(() => ({ ok: false }));
|
|
1323
|
+
if (created.ok && created.data) return { created: true, data: created.data };
|
|
1324
|
+
return null;
|
|
1325
|
+
} catch {
|
|
1326
|
+
return null;
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
|
|
919
1330
|
const program = new Command();
|
|
920
1331
|
program
|
|
921
1332
|
.name('pingagent')
|
|
@@ -2249,6 +2660,467 @@ billing
|
|
|
2249
2660
|
}
|
|
2250
2661
|
});
|
|
2251
2662
|
|
|
2663
|
+
const publicCmd = program.command('public').description('Hosted public growth surface: shareable profile links, contact cards, and task shares');
|
|
2664
|
+
|
|
2665
|
+
publicCmd
|
|
2666
|
+
.command('link')
|
|
2667
|
+
.description('Create or update your hosted public share link')
|
|
2668
|
+
.option('--slug <slug>', 'Preferred public slug')
|
|
2669
|
+
.option('--json', 'Output as JSON')
|
|
2670
|
+
.action(async (opts) => {
|
|
2671
|
+
const client = await getClient();
|
|
2672
|
+
const res = await client.createPublicLink({ slug: opts.slug });
|
|
2673
|
+
if (!res.ok) {
|
|
2674
|
+
if (opts.json) console.log(JSON.stringify(res, null, 2));
|
|
2675
|
+
else printError(res.error);
|
|
2676
|
+
process.exit(1);
|
|
2677
|
+
}
|
|
2678
|
+
if (opts.json) console.log(JSON.stringify(res.data, null, 2));
|
|
2679
|
+
else {
|
|
2680
|
+
console.log(`Public slug: ${res.data.public_slug || '(none)'}`);
|
|
2681
|
+
console.log(`Canonical: ${res.data.canonical_slug || '(none)'}`);
|
|
2682
|
+
console.log(`URL: ${res.data.public_url || '(none)'}`);
|
|
2683
|
+
}
|
|
2684
|
+
});
|
|
2685
|
+
|
|
2686
|
+
publicCmd
|
|
2687
|
+
.command('profile')
|
|
2688
|
+
.description('Show your hosted public share state')
|
|
2689
|
+
.option('--json', 'Output as JSON')
|
|
2690
|
+
.action(async (opts) => {
|
|
2691
|
+
const client = await getClient();
|
|
2692
|
+
await maybeEnsureHostedPublicLink();
|
|
2693
|
+
const res = await client.getPublicSelf();
|
|
2694
|
+
if (!res.ok) {
|
|
2695
|
+
if (opts.json) console.log(JSON.stringify(res, null, 2));
|
|
2696
|
+
else printError(res.error);
|
|
2697
|
+
process.exit(1);
|
|
2698
|
+
}
|
|
2699
|
+
if (opts.json) console.log(JSON.stringify(res.data, null, 2));
|
|
2700
|
+
else {
|
|
2701
|
+
console.log(`DID: ${res.data.did}`);
|
|
2702
|
+
console.log(`Alias: ${res.data.alias || '(none)'}`);
|
|
2703
|
+
console.log(`Public slug: ${res.data.public_slug || '(none)'}`);
|
|
2704
|
+
console.log(`Enabled: ${res.data.public_share_enabled ? 'yes' : 'no'}`);
|
|
2705
|
+
console.log(`Discoverable:${res.data.discoverable ? ' yes' : ' no'}`);
|
|
2706
|
+
console.log(`URL: ${res.data.public_url || '(none)'}`);
|
|
2707
|
+
}
|
|
2708
|
+
});
|
|
2709
|
+
|
|
2710
|
+
publicCmd
|
|
2711
|
+
.command('contact-card')
|
|
2712
|
+
.description('Create a shareable contact card for this agent or another target DID')
|
|
2713
|
+
.option('--target-did <did>', 'Target DID to place in the contact card')
|
|
2714
|
+
.option('--intro-note <text>', 'Intro note shown on the card')
|
|
2715
|
+
.option('--message-template <text>', 'Suggested first-message template')
|
|
2716
|
+
.option('--json', 'Output as JSON')
|
|
2717
|
+
.action(async (opts) => {
|
|
2718
|
+
const client = await getClient();
|
|
2719
|
+
const res = await client.createContactCard({
|
|
2720
|
+
target_did: opts.targetDid,
|
|
2721
|
+
intro_note: opts.introNote,
|
|
2722
|
+
message_template: opts.messageTemplate,
|
|
2723
|
+
});
|
|
2724
|
+
if (!res.ok) {
|
|
2725
|
+
if (opts.json) console.log(JSON.stringify(res, null, 2));
|
|
2726
|
+
else printError(res.error);
|
|
2727
|
+
process.exit(1);
|
|
2728
|
+
}
|
|
2729
|
+
if (opts.json) console.log(JSON.stringify(res.data, null, 2));
|
|
2730
|
+
else {
|
|
2731
|
+
console.log(`Contact card: ${res.data.id}`);
|
|
2732
|
+
console.log(`Target DID: ${res.data.target_did}`);
|
|
2733
|
+
console.log(`URL: ${res.data.share_url || '(none)'}`);
|
|
2734
|
+
}
|
|
2735
|
+
});
|
|
2736
|
+
|
|
2737
|
+
publicCmd
|
|
2738
|
+
.command('task-share')
|
|
2739
|
+
.description('Publish a shareable task result summary (explicit publish only)')
|
|
2740
|
+
.requiredOption('--summary <text>', 'Generated summary to publish')
|
|
2741
|
+
.option('--task-id <id>', 'Task ID')
|
|
2742
|
+
.option('--title <title>', 'Task title')
|
|
2743
|
+
.option('--status <status>', 'Task status', 'processed')
|
|
2744
|
+
.option('--conversation <id>', 'Conversation ID')
|
|
2745
|
+
.option('--json', 'Output as JSON')
|
|
2746
|
+
.action(async (opts) => {
|
|
2747
|
+
const client = await getClient();
|
|
2748
|
+
const res = await client.createTaskShare({
|
|
2749
|
+
task_id: opts.taskId,
|
|
2750
|
+
title: opts.title,
|
|
2751
|
+
status: opts.status,
|
|
2752
|
+
summary: opts.summary,
|
|
2753
|
+
conversation_id: opts.conversation,
|
|
2754
|
+
});
|
|
2755
|
+
if (!res.ok) {
|
|
2756
|
+
if (opts.json) console.log(JSON.stringify(res, null, 2));
|
|
2757
|
+
else printError(res.error);
|
|
2758
|
+
process.exit(1);
|
|
2759
|
+
}
|
|
2760
|
+
if (opts.json) console.log(JSON.stringify(res.data, null, 2));
|
|
2761
|
+
else {
|
|
2762
|
+
console.log(`Task share: ${res.data.id}`);
|
|
2763
|
+
console.log(`URL: ${res.data.share_url || '(none)'}`);
|
|
2764
|
+
}
|
|
2765
|
+
});
|
|
2766
|
+
|
|
2767
|
+
program
|
|
2768
|
+
.command('capability-card')
|
|
2769
|
+
.description('Show or update the structured machine-readable capability card for this agent')
|
|
2770
|
+
.option('--summary <text>', 'Capability card summary')
|
|
2771
|
+
.option('--accepts-new-work <value>', 'true or false')
|
|
2772
|
+
.option('--preferred-contact-mode <mode>', 'dm, task, or either')
|
|
2773
|
+
.option('--capability-item <json>', 'Capability item JSON; repeat to add/replace entries by id', (value, acc) => {
|
|
2774
|
+
acc.push(value);
|
|
2775
|
+
return acc;
|
|
2776
|
+
}, [])
|
|
2777
|
+
.option('--replace-items', 'Replace capability items with the provided --capability-item rows instead of merging by id')
|
|
2778
|
+
.option('--json', 'Output as JSON')
|
|
2779
|
+
.action(async (opts) => {
|
|
2780
|
+
const client = await getClient();
|
|
2781
|
+
const profileRes = await client.getProfile();
|
|
2782
|
+
if (!profileRes.ok || !profileRes.data) {
|
|
2783
|
+
if (opts.json) console.log(JSON.stringify(profileRes, null, 2));
|
|
2784
|
+
else printError(profileRes.error);
|
|
2785
|
+
process.exit(1);
|
|
2786
|
+
}
|
|
2787
|
+
const current = profileRes.data.capability_card || { version: '1', capabilities: [] };
|
|
2788
|
+
const shouldUpdate =
|
|
2789
|
+
opts.summary !== undefined
|
|
2790
|
+
|| opts.acceptsNewWork !== undefined
|
|
2791
|
+
|| opts.preferredContactMode !== undefined
|
|
2792
|
+
|| (opts.capabilityItem && opts.capabilityItem.length > 0)
|
|
2793
|
+
|| opts.replaceItems;
|
|
2794
|
+
if (!shouldUpdate) {
|
|
2795
|
+
if (opts.json) console.log(JSON.stringify(current, null, 2));
|
|
2796
|
+
else {
|
|
2797
|
+
console.log(`summary=${formatCapabilityCardSummary(current)}`);
|
|
2798
|
+
console.log(JSON.stringify(current, null, 2));
|
|
2799
|
+
}
|
|
2800
|
+
return;
|
|
2801
|
+
}
|
|
2802
|
+
let capabilityItems = Array.isArray(current.capabilities) ? [...current.capabilities] : [];
|
|
2803
|
+
if (opts.capabilityItem && opts.capabilityItem.length > 0) {
|
|
2804
|
+
const parsedItems = opts.capabilityItem.map((item) => JSON.parse(item));
|
|
2805
|
+
if (opts.replaceItems) {
|
|
2806
|
+
capabilityItems = parsedItems;
|
|
2807
|
+
} else {
|
|
2808
|
+
const byId = new Map(capabilityItems.map((item) => [item.id, item]));
|
|
2809
|
+
for (const item of parsedItems) {
|
|
2810
|
+
byId.set(item.id, { ...(byId.get(item.id) || {}), ...item });
|
|
2811
|
+
}
|
|
2812
|
+
capabilityItems = Array.from(byId.values());
|
|
2813
|
+
}
|
|
2814
|
+
} else if (opts.replaceItems) {
|
|
2815
|
+
capabilityItems = [];
|
|
2816
|
+
}
|
|
2817
|
+
const nextCard = {
|
|
2818
|
+
version: current.version || '1',
|
|
2819
|
+
summary: opts.summary !== undefined ? opts.summary : current.summary,
|
|
2820
|
+
accepts_new_work: opts.acceptsNewWork !== undefined
|
|
2821
|
+
? String(opts.acceptsNewWork).trim().toLowerCase() === 'true'
|
|
2822
|
+
: current.accepts_new_work,
|
|
2823
|
+
preferred_contact_mode: opts.preferredContactMode !== undefined
|
|
2824
|
+
? opts.preferredContactMode
|
|
2825
|
+
: current.preferred_contact_mode,
|
|
2826
|
+
capabilities: capabilityItems,
|
|
2827
|
+
};
|
|
2828
|
+
const updateRes = await client.updateProfile({ capability_card: nextCard });
|
|
2829
|
+
if (!updateRes.ok || !updateRes.data) {
|
|
2830
|
+
if (opts.json) console.log(JSON.stringify(updateRes, null, 2));
|
|
2831
|
+
else printError(updateRes.error);
|
|
2832
|
+
process.exit(1);
|
|
2833
|
+
}
|
|
2834
|
+
if (opts.json) console.log(JSON.stringify(updateRes.data.capability_card || nextCard, null, 2));
|
|
2835
|
+
else {
|
|
2836
|
+
console.log(`summary=${formatCapabilityCardSummary(updateRes.data.capability_card || nextCard)}`);
|
|
2837
|
+
console.log(JSON.stringify(updateRes.data.capability_card || nextCard, null, 2));
|
|
2838
|
+
}
|
|
2839
|
+
});
|
|
2840
|
+
|
|
2841
|
+
program
|
|
2842
|
+
.command('session-summary')
|
|
2843
|
+
.description('Show or update the local carry-forward summary for a session')
|
|
2844
|
+
.option('--session-key <key>', 'Exact session key')
|
|
2845
|
+
.option('--conversation-id <id>', 'Conversation ID')
|
|
2846
|
+
.option('--remote-did <did>', 'Remote DID')
|
|
2847
|
+
.option('--objective <text>', 'Current objective')
|
|
2848
|
+
.option('--context <text>', 'Current context')
|
|
2849
|
+
.option('--constraints <text>', 'Constraints')
|
|
2850
|
+
.option('--decisions <text>', 'Decisions already made')
|
|
2851
|
+
.option('--open-questions <text>', 'Open questions')
|
|
2852
|
+
.option('--next-action <text>', 'Next action')
|
|
2853
|
+
.option('--handoff-ready-text <text>', 'Handoff-ready summary text')
|
|
2854
|
+
.option('--clear-field <field>', 'Clear one summary field; repeatable', (value, acc) => {
|
|
2855
|
+
acc.push(value);
|
|
2856
|
+
return acc;
|
|
2857
|
+
}, [])
|
|
2858
|
+
.option('--json', 'Output as JSON')
|
|
2859
|
+
.action(async (opts) => {
|
|
2860
|
+
const identityPath = getIdentityPathForCommand(opts);
|
|
2861
|
+
const store = openStore(identityPath);
|
|
2862
|
+
try {
|
|
2863
|
+
const session = resolveSessionFromStore(store, {
|
|
2864
|
+
sessionKey: opts.sessionKey,
|
|
2865
|
+
conversationId: opts.conversationId,
|
|
2866
|
+
remoteDid: opts.remoteDid,
|
|
2867
|
+
});
|
|
2868
|
+
if (!session) {
|
|
2869
|
+
console.error('No session found. Use pingagent host tui or pingagent recent sessions first.');
|
|
2870
|
+
process.exit(1);
|
|
2871
|
+
}
|
|
2872
|
+
const manager = new SessionSummaryManager(store);
|
|
2873
|
+
const clearFields = Array.isArray(opts.clearField)
|
|
2874
|
+
? opts.clearField
|
|
2875
|
+
.map((value) => String(value || '').trim())
|
|
2876
|
+
.filter((value) => SESSION_SUMMARY_FIELDS.includes(value))
|
|
2877
|
+
: [];
|
|
2878
|
+
if (Array.isArray(opts.clearField) && opts.clearField.length !== clearFields.length) {
|
|
2879
|
+
console.error(`clear-field must be one of: ${SESSION_SUMMARY_FIELDS.join(', ')}`);
|
|
2880
|
+
process.exit(1);
|
|
2881
|
+
}
|
|
2882
|
+
const shouldUpdate = [
|
|
2883
|
+
opts.objective,
|
|
2884
|
+
opts.context,
|
|
2885
|
+
opts.constraints,
|
|
2886
|
+
opts.decisions,
|
|
2887
|
+
opts.openQuestions,
|
|
2888
|
+
opts.nextAction,
|
|
2889
|
+
opts.handoffReadyText,
|
|
2890
|
+
].some((value) => value !== undefined) || clearFields.length > 0;
|
|
2891
|
+
let summary = shouldUpdate
|
|
2892
|
+
? manager.upsert({
|
|
2893
|
+
session_key: session.session_key,
|
|
2894
|
+
objective: opts.objective,
|
|
2895
|
+
context: opts.context,
|
|
2896
|
+
constraints: opts.constraints,
|
|
2897
|
+
decisions: opts.decisions,
|
|
2898
|
+
open_questions: opts.openQuestions,
|
|
2899
|
+
next_action: opts.nextAction,
|
|
2900
|
+
handoff_ready_text: opts.handoffReadyText,
|
|
2901
|
+
})
|
|
2902
|
+
: manager.get(session.session_key);
|
|
2903
|
+
if (clearFields.length > 0) {
|
|
2904
|
+
summary = manager.clearFields(session.session_key, clearFields);
|
|
2905
|
+
}
|
|
2906
|
+
if (opts.json) {
|
|
2907
|
+
console.log(JSON.stringify({ session, summary }, null, 2));
|
|
2908
|
+
} else {
|
|
2909
|
+
console.log(`session=${session.session_key}`);
|
|
2910
|
+
console.log(`conversation=${session.conversation_id || '(none)'}`);
|
|
2911
|
+
console.log(`remote=${session.remote_did || '(unknown)'}`);
|
|
2912
|
+
console.log(JSON.stringify(summary, null, 2));
|
|
2913
|
+
}
|
|
2914
|
+
} finally {
|
|
2915
|
+
store.close();
|
|
2916
|
+
}
|
|
2917
|
+
});
|
|
2918
|
+
|
|
2919
|
+
program
|
|
2920
|
+
.command('handoff')
|
|
2921
|
+
.description('Send a first-class delegation / handoff task using the current task-thread transport')
|
|
2922
|
+
.requiredOption('--to <target>', 'Target DID, alias, public slug, or connectable identity')
|
|
2923
|
+
.requiredOption('--title <title>', 'Task title')
|
|
2924
|
+
.option('--description <text>', 'Task description')
|
|
2925
|
+
.option('--objective <text>', 'Delegation objective')
|
|
2926
|
+
.option('--success-criteria <text>', 'Success criteria')
|
|
2927
|
+
.option('--priority <text>', 'Priority label')
|
|
2928
|
+
.option('--carry-forward-summary <text>', 'Explicit carry-forward summary; otherwise use the session summary')
|
|
2929
|
+
.option('--callback-session-key <key>', 'Callback session key to reference in the handoff')
|
|
2930
|
+
.option('--session-key <key>', 'Source session key whose summary should be used')
|
|
2931
|
+
.option('--conversation-id <id>', 'Source conversation ID whose summary should be used')
|
|
2932
|
+
.option('--remote-did <did>', 'Source remote DID whose summary should be used')
|
|
2933
|
+
.option('--json', 'Output as JSON')
|
|
2934
|
+
.action(async (opts) => {
|
|
2935
|
+
const identityPath = getIdentityPathForCommand(opts);
|
|
2936
|
+
const { client, store } = await getClientWithStore(identityPath);
|
|
2937
|
+
try {
|
|
2938
|
+
const targetDid = await resolveTarget(client, opts.to);
|
|
2939
|
+
const sourceSession = resolveSessionFromStore(store, {
|
|
2940
|
+
sessionKey: opts.sessionKey,
|
|
2941
|
+
conversationId: opts.conversationId,
|
|
2942
|
+
remoteDid: opts.remoteDid,
|
|
2943
|
+
});
|
|
2944
|
+
const result = await client.sendHandoff(targetDid, {
|
|
2945
|
+
title: opts.title,
|
|
2946
|
+
description: opts.description,
|
|
2947
|
+
objective: opts.objective,
|
|
2948
|
+
carry_forward_summary: opts.carryForwardSummary,
|
|
2949
|
+
success_criteria: opts.successCriteria,
|
|
2950
|
+
callback_session_key: opts.callbackSessionKey || sourceSession?.session_key,
|
|
2951
|
+
priority: opts.priority,
|
|
2952
|
+
}, {
|
|
2953
|
+
sessionKey: sourceSession?.session_key,
|
|
2954
|
+
conversationId: sourceSession?.conversation_id,
|
|
2955
|
+
});
|
|
2956
|
+
if (!result.ok || !result.data) {
|
|
2957
|
+
if (opts.json) console.log(JSON.stringify(result, null, 2));
|
|
2958
|
+
else printError(result.error);
|
|
2959
|
+
process.exit(1);
|
|
2960
|
+
}
|
|
2961
|
+
if (opts.json) {
|
|
2962
|
+
console.log(JSON.stringify({
|
|
2963
|
+
target_did: targetDid,
|
|
2964
|
+
source_session_key: sourceSession?.session_key ?? null,
|
|
2965
|
+
...result.data,
|
|
2966
|
+
}, null, 2));
|
|
2967
|
+
} else {
|
|
2968
|
+
console.log(`task_id=${result.data.task_id}`);
|
|
2969
|
+
console.log(`conversation_id=${result.data.conversation_id}`);
|
|
2970
|
+
console.log(`target_did=${targetDid}`);
|
|
2971
|
+
console.log(`source_session_key=${sourceSession?.session_key || '(none)'}`);
|
|
2972
|
+
console.log(JSON.stringify(result.data.handoff, null, 2));
|
|
2973
|
+
}
|
|
2974
|
+
} finally {
|
|
2975
|
+
store.close();
|
|
2976
|
+
}
|
|
2977
|
+
});
|
|
2978
|
+
|
|
2979
|
+
program
|
|
2980
|
+
.command('demo')
|
|
2981
|
+
.description('Open or message the official PingAgent demo agent')
|
|
2982
|
+
.option('--preset <name>', 'Preset first message: hello, delegate, or trust')
|
|
2983
|
+
.option('--message <text>', 'Optional first message to send immediately')
|
|
2984
|
+
.option('--json', 'Output as JSON')
|
|
2985
|
+
.action(async (opts) => {
|
|
2986
|
+
const client = await getClient();
|
|
2987
|
+
const resolved = await client.resolveAlias('pingagent/demo');
|
|
2988
|
+
if (!resolved.ok || !resolved.data?.did) {
|
|
2989
|
+
if (opts.json) console.log(JSON.stringify(resolved, null, 2));
|
|
2990
|
+
else printError(resolved.error);
|
|
2991
|
+
process.exit(1);
|
|
2992
|
+
}
|
|
2993
|
+
const convo = await client.openConversation(resolved.data.did);
|
|
2994
|
+
if (!convo.ok || !convo.data) {
|
|
2995
|
+
if (opts.json) console.log(JSON.stringify(convo, null, 2));
|
|
2996
|
+
else printError(convo.error);
|
|
2997
|
+
process.exit(1);
|
|
2998
|
+
}
|
|
2999
|
+
const presetMessages = {
|
|
3000
|
+
hello: 'Hello',
|
|
3001
|
+
delegate: 'Please show me how task delegation works in PingAgent.',
|
|
3002
|
+
trust: 'Show me how trust decisions and recommendations work.',
|
|
3003
|
+
};
|
|
3004
|
+
const effectiveMessage = typeof opts.message === 'string' && opts.message.trim()
|
|
3005
|
+
? opts.message
|
|
3006
|
+
: (typeof opts.preset === 'string' && presetMessages[opts.preset.trim().toLowerCase()] ? presetMessages[opts.preset.trim().toLowerCase()] : '');
|
|
3007
|
+
if (effectiveMessage) {
|
|
3008
|
+
const sendRes = await client.sendMessage(convo.data.conversation_id, SCHEMA_TEXT, { text: effectiveMessage });
|
|
3009
|
+
if (!sendRes.ok) {
|
|
3010
|
+
if (opts.json) console.log(JSON.stringify(sendRes, null, 2));
|
|
3011
|
+
else printError(sendRes.error);
|
|
3012
|
+
process.exit(1);
|
|
3013
|
+
}
|
|
3014
|
+
if (opts.json) console.log(JSON.stringify({ did: resolved.data.did, conversation_id: convo.data.conversation_id, message_id: sendRes.data?.message_id, preset: opts.preset ?? null }, null, 2));
|
|
3015
|
+
else console.log(`Demo agent messaged. conversation=${convo.data.conversation_id} message=${sendRes.data?.message_id}`);
|
|
3016
|
+
return;
|
|
3017
|
+
}
|
|
3018
|
+
if (opts.json) console.log(JSON.stringify({ did: resolved.data.did, conversation_id: convo.data.conversation_id }, null, 2));
|
|
3019
|
+
else console.log(`Demo agent ready. did=${resolved.data.did} conversation=${convo.data.conversation_id}`);
|
|
3020
|
+
});
|
|
3021
|
+
|
|
3022
|
+
program
|
|
3023
|
+
.command('connect')
|
|
3024
|
+
.description('Consume a PingAgent public link or contact card and open a conversation')
|
|
3025
|
+
.argument('<target>', 'Share URL, contact card URL, public slug, alias, or DID')
|
|
3026
|
+
.option('--message <text>', 'Optional first message to send immediately')
|
|
3027
|
+
.option('--json', 'Output as JSON')
|
|
3028
|
+
.action(async (target, opts) => {
|
|
3029
|
+
const client = await getClient();
|
|
3030
|
+
let targetDid = '';
|
|
3031
|
+
let prefMessage = typeof opts.message === 'string' ? opts.message : '';
|
|
3032
|
+
try {
|
|
3033
|
+
if (/^https?:\/\//.test(target)) {
|
|
3034
|
+
const url = new URL(target);
|
|
3035
|
+
const parts = url.pathname.split('/').filter(Boolean);
|
|
3036
|
+
if (parts[0] === 'c' && parts[1]) {
|
|
3037
|
+
const card = await client.getContactCard(parts[1]);
|
|
3038
|
+
if (!card.ok || !card.data) {
|
|
3039
|
+
if (opts.json) console.log(JSON.stringify(card, null, 2));
|
|
3040
|
+
else printError(card.error);
|
|
3041
|
+
process.exit(1);
|
|
3042
|
+
}
|
|
3043
|
+
targetDid = card.data.target_did;
|
|
3044
|
+
if (!prefMessage && card.data.message_template) prefMessage = card.data.message_template;
|
|
3045
|
+
} else if (parts[0] === 'a' && parts[1]) {
|
|
3046
|
+
const agent = await client.getPublicAgent(parts[1]);
|
|
3047
|
+
if (!agent.ok || !agent.data?.did) {
|
|
3048
|
+
if (opts.json) console.log(JSON.stringify(agent, null, 2));
|
|
3049
|
+
else printError(agent.error);
|
|
3050
|
+
process.exit(1);
|
|
3051
|
+
}
|
|
3052
|
+
targetDid = agent.data.did;
|
|
3053
|
+
} else if (parts[0] === 'connect' && parts[1]) {
|
|
3054
|
+
const requestedTarget = decodeURIComponent(parts[1]);
|
|
3055
|
+
if (!prefMessage && url.searchParams.get('message')) {
|
|
3056
|
+
prefMessage = url.searchParams.get('message') || '';
|
|
3057
|
+
}
|
|
3058
|
+
if (requestedTarget.startsWith('did:agent:')) {
|
|
3059
|
+
targetDid = requestedTarget;
|
|
3060
|
+
} else if (requestedTarget.startsWith('@')) {
|
|
3061
|
+
const resolved = await client.resolveAlias(requestedTarget.slice(1));
|
|
3062
|
+
if (!resolved.ok || !resolved.data?.did) {
|
|
3063
|
+
if (opts.json) console.log(JSON.stringify(resolved, null, 2));
|
|
3064
|
+
else printError(resolved.error);
|
|
3065
|
+
process.exit(1);
|
|
3066
|
+
}
|
|
3067
|
+
targetDid = resolved.data.did;
|
|
3068
|
+
} else {
|
|
3069
|
+
const agent = await client.getPublicAgent(requestedTarget);
|
|
3070
|
+
if (!agent.ok || !agent.data?.did) {
|
|
3071
|
+
if (opts.json) console.log(JSON.stringify(agent, null, 2));
|
|
3072
|
+
else printError(agent.error);
|
|
3073
|
+
process.exit(1);
|
|
3074
|
+
}
|
|
3075
|
+
targetDid = agent.data.did;
|
|
3076
|
+
}
|
|
3077
|
+
}
|
|
3078
|
+
}
|
|
3079
|
+
} catch {
|
|
3080
|
+
// fall through to text target handling
|
|
3081
|
+
}
|
|
3082
|
+
if (!targetDid) {
|
|
3083
|
+
if (String(target).startsWith('did:agent:')) {
|
|
3084
|
+
targetDid = String(target);
|
|
3085
|
+
} else if (String(target).startsWith('@')) {
|
|
3086
|
+
const resolved = await client.resolveAlias(String(target).slice(1));
|
|
3087
|
+
if (!resolved.ok || !resolved.data?.did) {
|
|
3088
|
+
if (opts.json) console.log(JSON.stringify(resolved, null, 2));
|
|
3089
|
+
else printError(resolved.error);
|
|
3090
|
+
process.exit(1);
|
|
3091
|
+
}
|
|
3092
|
+
targetDid = resolved.data.did;
|
|
3093
|
+
} else {
|
|
3094
|
+
const agent = await client.getPublicAgent(String(target));
|
|
3095
|
+
if (!agent.ok || !agent.data?.did) {
|
|
3096
|
+
if (opts.json) console.log(JSON.stringify(agent, null, 2));
|
|
3097
|
+
else printError(agent.error);
|
|
3098
|
+
process.exit(1);
|
|
3099
|
+
}
|
|
3100
|
+
targetDid = agent.data.did;
|
|
3101
|
+
}
|
|
3102
|
+
}
|
|
3103
|
+
const convo = await client.openConversation(targetDid);
|
|
3104
|
+
if (!convo.ok || !convo.data) {
|
|
3105
|
+
if (opts.json) console.log(JSON.stringify(convo, null, 2));
|
|
3106
|
+
else printError(convo.error);
|
|
3107
|
+
process.exit(1);
|
|
3108
|
+
}
|
|
3109
|
+
if (prefMessage) {
|
|
3110
|
+
const sendRes = await client.sendMessage(convo.data.conversation_id, SCHEMA_TEXT, { text: prefMessage });
|
|
3111
|
+
if (!sendRes.ok) {
|
|
3112
|
+
if (opts.json) console.log(JSON.stringify(sendRes, null, 2));
|
|
3113
|
+
else printError(sendRes.error);
|
|
3114
|
+
process.exit(1);
|
|
3115
|
+
}
|
|
3116
|
+
if (opts.json) console.log(JSON.stringify({ did: targetDid, conversation_id: convo.data.conversation_id, message_id: sendRes.data?.message_id }, null, 2));
|
|
3117
|
+
else console.log(`Connected and sent first message. conversation=${convo.data.conversation_id} did=${targetDid}`);
|
|
3118
|
+
return;
|
|
3119
|
+
}
|
|
3120
|
+
if (opts.json) console.log(JSON.stringify({ did: targetDid, conversation_id: convo.data.conversation_id }, null, 2));
|
|
3121
|
+
else console.log(`Connected. conversation=${convo.data.conversation_id} did=${targetDid}`);
|
|
3122
|
+
});
|
|
3123
|
+
|
|
2252
3124
|
const host = program
|
|
2253
3125
|
.command('host')
|
|
2254
3126
|
.description('Headless runtime inspection and control for PingAgent host state');
|