forkit-connect 0.1.21 → 0.1.22
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/dist/cli.js +400 -57
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1399,42 +1399,65 @@ function printCollectedChanges(service, limit) {
|
|
|
1399
1399
|
for (const event of state.evidence_events) {
|
|
1400
1400
|
evidenceTypeCounts.set(event.type, (evidenceTypeCounts.get(event.type) || 0) + 1);
|
|
1401
1401
|
}
|
|
1402
|
-
|
|
1403
|
-
console.log(
|
|
1404
|
-
console.log(
|
|
1405
|
-
console.log(
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
console.log(
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1402
|
+
printCliHeader('Changes', 'Local evidence, queue, and runtime activity collected on this device.');
|
|
1403
|
+
console.log(cliKeyLine('state dir', paths.stateDir));
|
|
1404
|
+
console.log(cliKeyLine('state file', paths.stateFile));
|
|
1405
|
+
console.log(cliKeyLine('summary', joinCliSummary([
|
|
1406
|
+
formatCliCompactCount(state.evidence_events.length, 'evidence event'),
|
|
1407
|
+
formatCliCompactCount(state.pulse_events.length, 'pulse event'),
|
|
1408
|
+
formatCliCompactCount(state.sync_queue.length, 'queued sync'),
|
|
1409
|
+
formatCliCompactCount(state.pending_reviews.length, 'pending review'),
|
|
1410
|
+
])));
|
|
1411
|
+
console.log(cliKeyLine('local', joinCliSummary([
|
|
1412
|
+
formatCliCompactCount(state.detected_models.length, 'model'),
|
|
1413
|
+
formatCliCompactCount(state.detected_runtimes.length, 'runtime'),
|
|
1414
|
+
formatCliCompactCount(state.detected_agents.length, 'agent'),
|
|
1415
|
+
formatCliCompactCount(state.c2_events.length, 'c2 event'),
|
|
1416
|
+
])));
|
|
1417
|
+
console.log(cliKeyLine('note', 'Only synced queue items become backend-authoritative.'));
|
|
1415
1418
|
const sortedEvidenceTypes = [...evidenceTypeCounts.entries()].sort((left, right) => right[1] - left[1] || left[0].localeCompare(right[0]));
|
|
1416
1419
|
if (sortedEvidenceTypes.length > 0) {
|
|
1417
|
-
|
|
1420
|
+
printCliSection('Evidence Types');
|
|
1418
1421
|
for (const [type, count] of sortedEvidenceTypes) {
|
|
1419
|
-
|
|
1422
|
+
printCliEntry(type, {
|
|
1423
|
+
summary: formatCliCompactCount(count, 'event'),
|
|
1424
|
+
});
|
|
1420
1425
|
}
|
|
1421
1426
|
}
|
|
1422
1427
|
if (recentEvidence.length > 0) {
|
|
1423
|
-
|
|
1428
|
+
printCliSection(`Recent Evidence ${cliTag(String(recentEvidence.length), 'accent')}`);
|
|
1424
1429
|
for (const event of recentEvidence) {
|
|
1425
|
-
|
|
1430
|
+
printCliEntry(event.type, {
|
|
1431
|
+
summary: formatTimestamp(event.createdAt),
|
|
1432
|
+
meta: truncateCliText(formatEvidenceDetails(event.details).replace(/^\s*\|\s*/, ''), 88),
|
|
1433
|
+
});
|
|
1426
1434
|
}
|
|
1427
1435
|
}
|
|
1428
1436
|
if (recentPulse.length > 0) {
|
|
1429
|
-
|
|
1437
|
+
printCliSection(`Runtime Signals ${cliTag(String(recentPulse.length), 'good')}`);
|
|
1430
1438
|
for (const event of recentPulse) {
|
|
1431
|
-
|
|
1439
|
+
printCliEntry(event.runtime_name, {
|
|
1440
|
+
summary: event.pulse_status,
|
|
1441
|
+
meta: joinCliSummary([
|
|
1442
|
+
formatTimestamp(event.measured_at),
|
|
1443
|
+
event.model_name ? `model ${event.model_name}` : null,
|
|
1444
|
+
formatPulseRegistrationLabel(event),
|
|
1445
|
+
]),
|
|
1446
|
+
});
|
|
1432
1447
|
}
|
|
1433
1448
|
}
|
|
1434
1449
|
if (state.sync_queue.length > 0) {
|
|
1435
|
-
|
|
1450
|
+
printCliSection(`Pending Sync ${cliTag(String(Math.min(state.sync_queue.length, limit)), 'warm')}`);
|
|
1436
1451
|
for (const item of state.sync_queue.slice(0, limit)) {
|
|
1437
|
-
|
|
1452
|
+
printCliEntry(item.type, {
|
|
1453
|
+
summary: item.endpoint,
|
|
1454
|
+
meta: joinCliSummary([
|
|
1455
|
+
`attempts ${item.attempts}`,
|
|
1456
|
+
`created ${formatTimestamp(item.createdAt)}`,
|
|
1457
|
+
item.nextRetryAt ? `retry ${formatTimestamp(item.nextRetryAt)}` : null,
|
|
1458
|
+
item.lastError ? `error ${truncateCliText(item.lastError, 48)}` : null,
|
|
1459
|
+
]),
|
|
1460
|
+
});
|
|
1438
1461
|
}
|
|
1439
1462
|
}
|
|
1440
1463
|
}
|
|
@@ -1468,14 +1491,25 @@ function printReviewSnapshot(snapshot) {
|
|
|
1468
1491
|
'shadow_candidate',
|
|
1469
1492
|
'provider_unavailable',
|
|
1470
1493
|
];
|
|
1471
|
-
|
|
1494
|
+
printCliHeader('Review', `${formatCliCompactCount(snapshot.total, 'local item')} across models and runtimes.`);
|
|
1472
1495
|
for (const status of order) {
|
|
1473
1496
|
const entries = snapshot.groups[status];
|
|
1474
1497
|
if (entries.length === 0)
|
|
1475
1498
|
continue;
|
|
1476
|
-
|
|
1499
|
+
printCliSection(`${formatReviewStatusLabel(status)} ${cliTag(String(entries.length), status === 'bound_passport' ? 'good' : status === 'provider_unavailable' ? 'danger' : 'warm')}`);
|
|
1477
1500
|
for (const entry of entries) {
|
|
1478
|
-
|
|
1501
|
+
printCliEntry(formatCliReviewSubject(entry), {
|
|
1502
|
+
tag: entry.kind,
|
|
1503
|
+
tagTone: entry.kind === 'provider' ? 'danger' : entry.kind === 'process' ? 'warm' : 'accent',
|
|
1504
|
+
summary: formatReviewSuggestedActionCompact(entry.suggested_action),
|
|
1505
|
+
meta: joinCliSummary([
|
|
1506
|
+
formatCliReviewRuntime(entry),
|
|
1507
|
+
entry.discovery_hash ? `discovery ${shortId(entry.discovery_hash)}` : null,
|
|
1508
|
+
entry.registration_key ? `registration ${shortId(entry.registration_key)}` : null,
|
|
1509
|
+
entry.passport_gaid ? `passport ${shortId(entry.passport_gaid)}` : null,
|
|
1510
|
+
formatReviewEvidence(entry).replace(/^\s*\|\s*/, ''),
|
|
1511
|
+
]),
|
|
1512
|
+
});
|
|
1479
1513
|
}
|
|
1480
1514
|
}
|
|
1481
1515
|
}
|
|
@@ -1504,17 +1538,93 @@ function printTrainStatus(status) {
|
|
|
1504
1538
|
}
|
|
1505
1539
|
function printAgentReview(snapshot) {
|
|
1506
1540
|
const order = ['new_agent', 'known_agent', 'linked_agent', 'inactive_agent', 'unknown_ai_tool'];
|
|
1507
|
-
|
|
1541
|
+
printCliHeader('Agent Review', `${formatCliCompactCount(snapshot.total, 'agent item')} detected on this device.`);
|
|
1508
1542
|
for (const status of order) {
|
|
1509
1543
|
const entries = snapshot.groups[status];
|
|
1510
1544
|
if (entries.length === 0)
|
|
1511
1545
|
continue;
|
|
1512
|
-
|
|
1546
|
+
const tone = status === 'linked_agent' ? 'good' : status === 'inactive_agent' ? 'danger' : status === 'unknown_ai_tool' ? 'muted' : 'warm';
|
|
1547
|
+
const label = status === 'new_agent'
|
|
1548
|
+
? 'New agents'
|
|
1549
|
+
: status === 'known_agent'
|
|
1550
|
+
? 'Needs link'
|
|
1551
|
+
: status === 'linked_agent'
|
|
1552
|
+
? 'Linked agents'
|
|
1553
|
+
: status === 'inactive_agent'
|
|
1554
|
+
? 'Inactive agents'
|
|
1555
|
+
: 'Unknown AI tools';
|
|
1556
|
+
printCliSection(`${label} ${cliTag(String(entries.length), tone)}`);
|
|
1513
1557
|
for (const entry of entries) {
|
|
1514
|
-
|
|
1558
|
+
printCliEntry(entry.agent_name, {
|
|
1559
|
+
tag: entry.agent_type.replaceAll('_', ' '),
|
|
1560
|
+
tagTone: entry.agent_type === 'unknown_ai_tool' ? 'muted' : 'accent',
|
|
1561
|
+
summary: formatAgentSuggestedActionCompact(entry.suggested_action),
|
|
1562
|
+
meta: joinCliSummary([
|
|
1563
|
+
entry.status.replaceAll('_', ' '),
|
|
1564
|
+
entry.linked_model_name ? `model ${entry.linked_model_name}` : null,
|
|
1565
|
+
entry.linked_passport_gaid ? `passport ${shortId(entry.linked_passport_gaid)}` : null,
|
|
1566
|
+
formatReviewEvidence(entry).replace(/^\s*\|\s*/, ''),
|
|
1567
|
+
]),
|
|
1568
|
+
});
|
|
1515
1569
|
}
|
|
1516
1570
|
}
|
|
1517
1571
|
}
|
|
1572
|
+
function printWorkspaceListSurface(workspaces, options) {
|
|
1573
|
+
printCliHeader('Workspaces', `${formatCliCompactCount(workspaces.length, 'accessible workspace')} on this account.`);
|
|
1574
|
+
const identityBits = [
|
|
1575
|
+
options?.displayName || null,
|
|
1576
|
+
options?.accountEmail || null,
|
|
1577
|
+
options?.platformRole ? `role ${options.platformRole}` : null,
|
|
1578
|
+
];
|
|
1579
|
+
if (identityBits.some(Boolean)) {
|
|
1580
|
+
console.log(cliKeyLine('account', joinCliSummary(identityBits)));
|
|
1581
|
+
}
|
|
1582
|
+
if (options?.accountLimits) {
|
|
1583
|
+
console.log(cliKeyLine('plan', options.accountLimits.planName));
|
|
1584
|
+
console.log(cliKeyLine('capacity', joinCliSummary([
|
|
1585
|
+
formatRemainingLimit(options.accountLimits.workspaceLimit, options.accountLimits.workspacesUsed, 'workspace'),
|
|
1586
|
+
formatRemainingLimit(options.accountLimits.projectLimit, options.accountLimits.projectsUsed, 'project'),
|
|
1587
|
+
])));
|
|
1588
|
+
}
|
|
1589
|
+
if (workspaces.length === 0) {
|
|
1590
|
+
console.log(cliKeyLine('next', 'Create a workspace from forkit-connect workspace create.'));
|
|
1591
|
+
return;
|
|
1592
|
+
}
|
|
1593
|
+
printCliSection('Accessible');
|
|
1594
|
+
for (const workspace of workspaces) {
|
|
1595
|
+
printCliEntry(String(workspace.name || 'Unnamed workspace').trim() || 'Unnamed workspace', {
|
|
1596
|
+
tag: String(workspace.role || 'unknown').trim() || 'unknown',
|
|
1597
|
+
tagTone: 'accent',
|
|
1598
|
+
summary: formatWorkspaceStateCell(workspace),
|
|
1599
|
+
meta: joinCliSummary([
|
|
1600
|
+
String(workspace.id || workspace.gaid || workspace.passportGaid || 'unknown'),
|
|
1601
|
+
workspace.verificationStatus ? `verification ${workspace.verificationStatus}` : null,
|
|
1602
|
+
workspace.description ? truncateCliText(workspace.description, 52) : null,
|
|
1603
|
+
]),
|
|
1604
|
+
});
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
function printProjectListSurface(projects, workspaceId) {
|
|
1608
|
+
printCliHeader('Projects', `${formatCliCompactCount(projects.length, 'project')} inside workspace ${shortId(workspaceId)}.`);
|
|
1609
|
+
if (projects.length === 0) {
|
|
1610
|
+
console.log(cliKeyLine('next', 'Create the first project with forkit-connect workspace select --workspace <id> --project-name "<name>".'));
|
|
1611
|
+
return;
|
|
1612
|
+
}
|
|
1613
|
+
printCliSection('Available');
|
|
1614
|
+
for (const project of projects) {
|
|
1615
|
+
const passportCount = Number(project.passportCount);
|
|
1616
|
+
printCliEntry(String(project.name || 'Unnamed project').trim() || 'Unnamed project', {
|
|
1617
|
+
tag: String(project.status || 'active').trim() || 'active',
|
|
1618
|
+
tagTone: 'good',
|
|
1619
|
+
summary: Number.isFinite(passportCount) && passportCount >= 0 ? `${passportCount} passports` : 'passport count unavailable',
|
|
1620
|
+
meta: joinCliSummary([
|
|
1621
|
+
String(project.id || 'unknown'),
|
|
1622
|
+
project.updatedAt ? `updated ${formatTimestamp(project.updatedAt)}` : null,
|
|
1623
|
+
project.description ? truncateCliText(project.description, 52) : null,
|
|
1624
|
+
]),
|
|
1625
|
+
});
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1518
1628
|
function printAgentStatus(status) {
|
|
1519
1629
|
console.log(JSON.stringify({
|
|
1520
1630
|
detected_agents: status.detectedAgents,
|
|
@@ -1697,6 +1807,242 @@ function printSmartInbox(inbox) {
|
|
|
1697
1807
|
printInboxGroup('Connected', inbox.groups.connected);
|
|
1698
1808
|
printInboxGroup('Denied on this device', inbox.groups.ignored);
|
|
1699
1809
|
}
|
|
1810
|
+
function formatCliEvidenceSummary(confidence, sourceLabel) {
|
|
1811
|
+
const normalizedConfidence = String(confidence || '').trim().toLowerCase();
|
|
1812
|
+
if (normalizedConfidence === 'high' || normalizedConfidence === 'medium' || normalizedConfidence === 'low') {
|
|
1813
|
+
return formatCliEvidenceCell(normalizedConfidence);
|
|
1814
|
+
}
|
|
1815
|
+
const source = String(sourceLabel || '').trim();
|
|
1816
|
+
return source ? truncateCliText(source, 14) : 'local review';
|
|
1817
|
+
}
|
|
1818
|
+
function formatCliReviewSubject(entry) {
|
|
1819
|
+
if (entry.kind === 'process') {
|
|
1820
|
+
return String(entry.process_name || 'Unknown process').trim() || 'Unknown process';
|
|
1821
|
+
}
|
|
1822
|
+
return String(entry.model_name || entry.runtime_name || 'Unknown item').trim() || 'Unknown item';
|
|
1823
|
+
}
|
|
1824
|
+
function formatCliReviewRuntime(entry) {
|
|
1825
|
+
if (entry.kind === 'provider') {
|
|
1826
|
+
return String(entry.runtime_name || 'runtime').trim() || 'runtime';
|
|
1827
|
+
}
|
|
1828
|
+
return String(entry.runtime_name || 'local').trim() || 'local';
|
|
1829
|
+
}
|
|
1830
|
+
function formatReviewSuggestedActionCompact(action) {
|
|
1831
|
+
switch (action) {
|
|
1832
|
+
case 'connect_model':
|
|
1833
|
+
return 'Connect model';
|
|
1834
|
+
case 'review_drafts':
|
|
1835
|
+
return 'Open draft';
|
|
1836
|
+
case 'already_connected':
|
|
1837
|
+
return 'Already linked';
|
|
1838
|
+
case 'review/runtime':
|
|
1839
|
+
return 'Check runtime';
|
|
1840
|
+
case 'review/connect':
|
|
1841
|
+
case 'review_model':
|
|
1842
|
+
return 'Review item';
|
|
1843
|
+
default:
|
|
1844
|
+
return truncateCliText(action.replaceAll('_', ' '), 24);
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1847
|
+
function buildDiscoveryInteractiveSections(snapshot) {
|
|
1848
|
+
const mapRows = (groupLabel, items) => items.map((entry) => ({ groupLabel, entry }));
|
|
1849
|
+
const sections = [
|
|
1850
|
+
{ id: 'new', label: 'New', tone: 'accent', rows: mapRows('New models', snapshot.groups.new_unregistered) },
|
|
1851
|
+
{ id: 'known', label: 'Known', tone: 'warm', rows: mapRows('Known models', snapshot.groups.known_unregistered) },
|
|
1852
|
+
{ id: 'drafts', label: 'Drafts', tone: 'good', rows: mapRows('Pending drafts', snapshot.groups.pending_draft) },
|
|
1853
|
+
{ id: 'linked', label: 'Linked', tone: 'muted', rows: mapRows('Connected', snapshot.groups.bound_passport) },
|
|
1854
|
+
{ id: 'runtime', label: 'Runtime', tone: 'warm', rows: mapRows('Unconnected runtimes', snapshot.groups.shadow_candidate) },
|
|
1855
|
+
{ id: 'unavailable', label: 'Offline', tone: 'danger', rows: mapRows('Unavailable runtimes', snapshot.groups.provider_unavailable) },
|
|
1856
|
+
];
|
|
1857
|
+
return sections.filter((section) => section.rows.length > 0);
|
|
1858
|
+
}
|
|
1859
|
+
function buildDiscoveryInteractiveDetailLines(row) {
|
|
1860
|
+
const entry = row.entry;
|
|
1861
|
+
const lines = [
|
|
1862
|
+
formatCliDetailLine('Review', row.groupLabel),
|
|
1863
|
+
formatCliDetailLine('Item', formatCliReviewSubject(entry)),
|
|
1864
|
+
formatCliDetailLine('Runtime', formatCliReviewRuntime(entry)),
|
|
1865
|
+
formatCliDetailLine('Next', formatReviewSuggestedActionCompact(entry.suggested_action)),
|
|
1866
|
+
formatCliDetailLine('Evidence', formatCliEvidenceSummary(entry.confidence, entry.source_label)),
|
|
1867
|
+
];
|
|
1868
|
+
if (entry.source_label) {
|
|
1869
|
+
lines.push(formatCliDetailLine('Source', entry.source_label));
|
|
1870
|
+
}
|
|
1871
|
+
if (entry.discovery_hash) {
|
|
1872
|
+
lines.push(formatCliDetailLine('Discovery', shortId(entry.discovery_hash)));
|
|
1873
|
+
}
|
|
1874
|
+
if (entry.registration_key) {
|
|
1875
|
+
lines.push(formatCliDetailLine('Registration', shortId(entry.registration_key)));
|
|
1876
|
+
}
|
|
1877
|
+
if (entry.draft_id) {
|
|
1878
|
+
lines.push(formatCliDetailLine('Draft', shortId(entry.draft_id)));
|
|
1879
|
+
}
|
|
1880
|
+
if (entry.passport_gaid) {
|
|
1881
|
+
lines.push(formatCliDetailLine('Passport', shortId(entry.passport_gaid)));
|
|
1882
|
+
}
|
|
1883
|
+
if (entry.reason) {
|
|
1884
|
+
lines.push(formatCliDetailLine('Reason', truncateCliText(entry.reason, 64)));
|
|
1885
|
+
}
|
|
1886
|
+
return lines;
|
|
1887
|
+
}
|
|
1888
|
+
async function maybeShowInteractiveDiscoveryReviewTable(snapshot) {
|
|
1889
|
+
return await viewInteractiveTable({
|
|
1890
|
+
title: '[forkit-connect] Review',
|
|
1891
|
+
subtitle: `${formatCliCompactCount(snapshot.total, 'local item')} across models and runtimes`,
|
|
1892
|
+
sections: buildDiscoveryInteractiveSections(snapshot),
|
|
1893
|
+
columns: [
|
|
1894
|
+
{ header: 'Item', width: 30, render: (row) => formatCliReviewSubject(row.entry) },
|
|
1895
|
+
{ header: 'Runtime', width: 18, render: (row) => formatCliReviewRuntime(row.entry) },
|
|
1896
|
+
{ header: 'Next', width: 24, render: (row) => formatReviewSuggestedActionCompact(row.entry.suggested_action) },
|
|
1897
|
+
{ header: 'Evidence', width: 14, render: (row) => formatCliEvidenceSummary(row.entry.confidence, row.entry.source_label) },
|
|
1898
|
+
],
|
|
1899
|
+
detailLines: buildDiscoveryInteractiveDetailLines,
|
|
1900
|
+
detailTitle: (row) => `${row.entry.kind.toUpperCase()} SNAPSHOT`,
|
|
1901
|
+
emptyState: 'No local review items are available right now.',
|
|
1902
|
+
});
|
|
1903
|
+
}
|
|
1904
|
+
function formatAgentSuggestedActionCompact(action) {
|
|
1905
|
+
switch (action) {
|
|
1906
|
+
case 'none':
|
|
1907
|
+
return 'Already linked';
|
|
1908
|
+
case 'rescan_agent':
|
|
1909
|
+
return 'Rescan agent';
|
|
1910
|
+
case 'review_agent':
|
|
1911
|
+
return 'Review agent';
|
|
1912
|
+
case 'link_model':
|
|
1913
|
+
return 'Link model';
|
|
1914
|
+
case 'connect_agent':
|
|
1915
|
+
return 'Connect agent';
|
|
1916
|
+
default:
|
|
1917
|
+
return truncateCliText(action.replaceAll('_', ' '), 24);
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
function buildAgentInteractiveSections(snapshot) {
|
|
1921
|
+
const rows = (groupLabel, items) => items.map((entry) => ({ groupLabel, entry }));
|
|
1922
|
+
const sections = [
|
|
1923
|
+
{ id: 'new', label: 'New', tone: 'accent', rows: rows('New agents', snapshot.groups.new_agent) },
|
|
1924
|
+
{ id: 'known', label: 'Needs link', tone: 'warm', rows: rows('Needs link', snapshot.groups.known_agent) },
|
|
1925
|
+
{ id: 'linked', label: 'Linked', tone: 'good', rows: rows('Linked agents', snapshot.groups.linked_agent) },
|
|
1926
|
+
{ id: 'inactive', label: 'Inactive', tone: 'danger', rows: rows('Inactive agents', snapshot.groups.inactive_agent) },
|
|
1927
|
+
{ id: 'tooling', label: 'Tooling', tone: 'muted', rows: rows('Unknown AI tools', snapshot.groups.unknown_ai_tool) },
|
|
1928
|
+
];
|
|
1929
|
+
return sections.filter((section) => section.rows.length > 0);
|
|
1930
|
+
}
|
|
1931
|
+
function buildAgentInteractiveDetailLines(row) {
|
|
1932
|
+
const entry = row.entry;
|
|
1933
|
+
const lines = [
|
|
1934
|
+
formatCliDetailLine('Review', row.groupLabel),
|
|
1935
|
+
formatCliDetailLine('Status', entry.status.replaceAll('_', ' ')),
|
|
1936
|
+
formatCliDetailLine('Next', formatAgentSuggestedActionCompact(entry.suggested_action)),
|
|
1937
|
+
formatCliDetailLine('Evidence', formatCliEvidenceSummary(entry.confidence, entry.source_label)),
|
|
1938
|
+
formatCliDetailLine('Agent ID', shortId(entry.agent_id)),
|
|
1939
|
+
];
|
|
1940
|
+
if (entry.source_label) {
|
|
1941
|
+
lines.push(formatCliDetailLine('Source', entry.source_label));
|
|
1942
|
+
}
|
|
1943
|
+
if (entry.linked_model_name) {
|
|
1944
|
+
lines.push(formatCliDetailLine('Model', entry.linked_model_name));
|
|
1945
|
+
}
|
|
1946
|
+
if (entry.linked_passport_gaid) {
|
|
1947
|
+
lines.push(formatCliDetailLine('Passport', shortId(entry.linked_passport_gaid)));
|
|
1948
|
+
}
|
|
1949
|
+
if (entry.matched_terms?.length) {
|
|
1950
|
+
lines.push(formatCliDetailLine('Matched', truncateCliText(entry.matched_terms.join(', '), 64)));
|
|
1951
|
+
}
|
|
1952
|
+
return lines;
|
|
1953
|
+
}
|
|
1954
|
+
async function maybeShowInteractiveAgentReviewTable(snapshot) {
|
|
1955
|
+
return await viewInteractiveTable({
|
|
1956
|
+
title: '[forkit-connect] Agent Review',
|
|
1957
|
+
subtitle: `${formatCliCompactCount(snapshot.total, 'agent item')} discovered on this device`,
|
|
1958
|
+
sections: buildAgentInteractiveSections(snapshot),
|
|
1959
|
+
columns: [
|
|
1960
|
+
{ header: 'Agent', width: 28, render: (row) => row.entry.agent_name },
|
|
1961
|
+
{ header: 'Type', width: 18, render: (row) => row.entry.agent_type.replaceAll('_', ' ') },
|
|
1962
|
+
{ header: 'State', width: 16, render: (row) => row.entry.status.replaceAll('_', ' ') },
|
|
1963
|
+
{ header: 'Next', width: 22, render: (row) => formatAgentSuggestedActionCompact(row.entry.suggested_action) },
|
|
1964
|
+
],
|
|
1965
|
+
detailLines: buildAgentInteractiveDetailLines,
|
|
1966
|
+
detailTitle: () => 'AGENT SNAPSHOT',
|
|
1967
|
+
emptyState: 'No agent review items are available right now.',
|
|
1968
|
+
});
|
|
1969
|
+
}
|
|
1970
|
+
function formatWorkspaceStateCell(workspace) {
|
|
1971
|
+
return [String(workspace.visibility || '').trim(), String(workspace.lifecycleStatus || '').trim()]
|
|
1972
|
+
.filter(Boolean)
|
|
1973
|
+
.join(' · ') || 'active';
|
|
1974
|
+
}
|
|
1975
|
+
function buildWorkspaceInteractiveDetailLines(row) {
|
|
1976
|
+
const workspace = row.workspace;
|
|
1977
|
+
const lines = [
|
|
1978
|
+
formatCliDetailLine('Workspace', String(workspace.name || 'Unnamed workspace').trim() || 'Unnamed workspace'),
|
|
1979
|
+
formatCliDetailLine('Role', String(workspace.role || 'unknown').trim() || 'unknown'),
|
|
1980
|
+
formatCliDetailLine('Visibility', String(workspace.visibility || 'not set').trim() || 'not set'),
|
|
1981
|
+
formatCliDetailLine('Lifecycle', String(workspace.lifecycleStatus || 'active').trim() || 'active'),
|
|
1982
|
+
formatCliDetailLine('Verification', String(workspace.verificationStatus || 'unverified').trim() || 'unverified'),
|
|
1983
|
+
formatCliDetailLine('ID', String(workspace.id || workspace.gaid || workspace.passportGaid || 'unknown').trim() || 'unknown'),
|
|
1984
|
+
];
|
|
1985
|
+
if (workspace.ownerName) {
|
|
1986
|
+
lines.push(formatCliDetailLine('Owner', String(workspace.ownerName).trim()));
|
|
1987
|
+
}
|
|
1988
|
+
if (workspace.description) {
|
|
1989
|
+
lines.push(formatCliDetailLine('Notes', truncateCliText(workspace.description, 64)));
|
|
1990
|
+
}
|
|
1991
|
+
return lines;
|
|
1992
|
+
}
|
|
1993
|
+
async function maybeShowInteractiveWorkspaceTable(workspaces) {
|
|
1994
|
+
return await viewInteractiveTable({
|
|
1995
|
+
title: '[forkit-connect] Workspaces',
|
|
1996
|
+
subtitle: `${formatCliCompactCount(workspaces.length, 'accessible workspace')} on this account`,
|
|
1997
|
+
sections: [{ id: 'all', label: 'Accessible', tone: 'accent', rows: workspaces.map((workspace) => ({ workspace })) }],
|
|
1998
|
+
columns: [
|
|
1999
|
+
{ header: 'Workspace', width: 28, render: (row) => String(row.workspace.name || 'Unnamed workspace').trim() || 'Unnamed workspace' },
|
|
2000
|
+
{ header: 'Role', width: 14, render: (row) => String(row.workspace.role || 'unknown').trim() || 'unknown' },
|
|
2001
|
+
{ header: 'State', width: 26, render: (row) => formatWorkspaceStateCell(row.workspace) },
|
|
2002
|
+
{ header: 'ID', width: 14, render: (row) => shortId(String(row.workspace.id || row.workspace.gaid || row.workspace.passportGaid || 'unknown')) },
|
|
2003
|
+
],
|
|
2004
|
+
detailLines: buildWorkspaceInteractiveDetailLines,
|
|
2005
|
+
detailTitle: () => 'WORKSPACE SNAPSHOT',
|
|
2006
|
+
emptyState: 'No workspaces are available right now.',
|
|
2007
|
+
});
|
|
2008
|
+
}
|
|
2009
|
+
function buildProjectInteractiveDetailLines(row) {
|
|
2010
|
+
const project = row.project;
|
|
2011
|
+
const passportCount = Number(project.passportCount);
|
|
2012
|
+
const lines = [
|
|
2013
|
+
formatCliDetailLine('Project', String(project.name || 'Unnamed project').trim() || 'Unnamed project'),
|
|
2014
|
+
formatCliDetailLine('Status', String(project.status || 'active').trim() || 'active'),
|
|
2015
|
+
formatCliDetailLine('Passports', Number.isFinite(passportCount) && passportCount >= 0 ? String(passportCount) : 'n/a'),
|
|
2016
|
+
formatCliDetailLine('Project ID', String(project.id || 'unknown').trim() || 'unknown'),
|
|
2017
|
+
formatCliDetailLine('Workspace', shortId(row.workspaceId || String(project.workspaceId || ''))),
|
|
2018
|
+
];
|
|
2019
|
+
if (project.updatedAt) {
|
|
2020
|
+
lines.push(formatCliDetailLine('Updated', formatTimestamp(project.updatedAt)));
|
|
2021
|
+
}
|
|
2022
|
+
if (project.description) {
|
|
2023
|
+
lines.push(formatCliDetailLine('Notes', truncateCliText(project.description, 64)));
|
|
2024
|
+
}
|
|
2025
|
+
return lines;
|
|
2026
|
+
}
|
|
2027
|
+
async function maybeShowInteractiveProjectTable(projects, workspaceId) {
|
|
2028
|
+
return await viewInteractiveTable({
|
|
2029
|
+
title: '[forkit-connect] Projects',
|
|
2030
|
+
subtitle: `${formatCliCompactCount(projects.length, 'project')} inside workspace ${shortId(workspaceId)}`,
|
|
2031
|
+
sections: [{ id: 'all', label: 'Available', tone: 'accent', rows: projects.map((project) => ({ workspaceId, project })) }],
|
|
2032
|
+
columns: [
|
|
2033
|
+
{ header: 'Project', width: 28, render: (row) => String(row.project.name || 'Unnamed project').trim() || 'Unnamed project' },
|
|
2034
|
+
{ header: 'Status', width: 16, render: (row) => String(row.project.status || 'active').trim() || 'active' },
|
|
2035
|
+
{ header: 'Passports', width: 12, render: (row) => {
|
|
2036
|
+
const passportCount = Number(row.project.passportCount);
|
|
2037
|
+
return Number.isFinite(passportCount) && passportCount >= 0 ? String(passportCount) : '-';
|
|
2038
|
+
}, align: 'right' },
|
|
2039
|
+
{ header: 'Project ID', width: 14, render: (row) => shortId(String(row.project.id || 'unknown')) },
|
|
2040
|
+
],
|
|
2041
|
+
detailLines: buildProjectInteractiveDetailLines,
|
|
2042
|
+
detailTitle: () => 'PROJECT SNAPSHOT',
|
|
2043
|
+
emptyState: 'No projects are available right now.',
|
|
2044
|
+
});
|
|
2045
|
+
}
|
|
1700
2046
|
function buildInboxInteractiveSections(inbox) {
|
|
1701
2047
|
const { privateReview, ready } = splitReadyInboxItems(inbox);
|
|
1702
2048
|
const rows = (groupLabel, items) => (items.map((item) => ({ groupLabel, item })));
|
|
@@ -4622,16 +4968,13 @@ async function run() {
|
|
|
4622
4968
|
return;
|
|
4623
4969
|
}
|
|
4624
4970
|
const accountLimits = await loadCliAccountLimits().catch(() => null);
|
|
4625
|
-
|
|
4626
|
-
|
|
4627
|
-
console.log(`- welcome=${accountLimits.displayName || 'there'}`);
|
|
4628
|
-
console.log(`- plan=${accountLimits.planName}`);
|
|
4629
|
-
console.log(`- workspaces_left=${formatRemainingLimit(accountLimits.workspaceLimit, accountLimits.workspacesUsed, 'workspace')}`);
|
|
4630
|
-
console.log(`- projects_left=${formatRemainingLimit(accountLimits.projectLimit, accountLimits.projectsUsed, 'project')}`);
|
|
4631
|
-
}
|
|
4632
|
-
for (const workspace of workspaces) {
|
|
4633
|
-
console.log(formatWorkspaceAccessLine(workspace));
|
|
4971
|
+
if (await maybeShowInteractiveWorkspaceTable(workspaces)) {
|
|
4972
|
+
return;
|
|
4634
4973
|
}
|
|
4974
|
+
printWorkspaceListSurface(workspaces, {
|
|
4975
|
+
displayName: accountLimits?.displayName || null,
|
|
4976
|
+
accountLimits,
|
|
4977
|
+
});
|
|
4635
4978
|
return;
|
|
4636
4979
|
}
|
|
4637
4980
|
if (subcommand === 'select') {
|
|
@@ -4938,10 +5281,17 @@ async function run() {
|
|
|
4938
5281
|
if (command === 'review') {
|
|
4939
5282
|
const reviewScan = await service.scanRuntime();
|
|
4940
5283
|
const snapshot = service.buildReviewSnapshot(reviewScan.summary);
|
|
5284
|
+
if (hasFlag('--json')) {
|
|
5285
|
+
printJson(snapshot);
|
|
5286
|
+
return;
|
|
5287
|
+
}
|
|
4941
5288
|
if (snapshot.total === 0) {
|
|
4942
5289
|
console.log('No review items found. Run `forkit-connect scan` first or ensure local runtimes are available.');
|
|
4943
5290
|
return;
|
|
4944
5291
|
}
|
|
5292
|
+
if (await maybeShowInteractiveDiscoveryReviewTable(snapshot)) {
|
|
5293
|
+
return;
|
|
5294
|
+
}
|
|
4945
5295
|
printReviewSnapshot(snapshot);
|
|
4946
5296
|
return;
|
|
4947
5297
|
}
|
|
@@ -5435,10 +5785,17 @@ async function run() {
|
|
|
5435
5785
|
}
|
|
5436
5786
|
if (subcommand === 'review') {
|
|
5437
5787
|
const snapshot = service.buildAgentReviewSnapshot();
|
|
5788
|
+
if (hasFlag('--json')) {
|
|
5789
|
+
printJson(snapshot);
|
|
5790
|
+
return;
|
|
5791
|
+
}
|
|
5438
5792
|
if (snapshot.total === 0) {
|
|
5439
5793
|
console.log('No agent items found. Run `forkit-connect agent scan` first.');
|
|
5440
5794
|
return;
|
|
5441
5795
|
}
|
|
5796
|
+
if (await maybeShowInteractiveAgentReviewTable(snapshot)) {
|
|
5797
|
+
return;
|
|
5798
|
+
}
|
|
5442
5799
|
printAgentReview(snapshot);
|
|
5443
5800
|
return;
|
|
5444
5801
|
}
|
|
@@ -5834,22 +6191,14 @@ async function run() {
|
|
|
5834
6191
|
: typeof tokenPayload?.role === 'string'
|
|
5835
6192
|
? tokenPayload.role
|
|
5836
6193
|
: null;
|
|
5837
|
-
if (email || platformRole) {
|
|
5838
|
-
const identityParts = [
|
|
5839
|
-
email ? `account=${email}` : null,
|
|
5840
|
-
platformRole ? `platform_role=${platformRole}` : null,
|
|
5841
|
-
].filter(Boolean);
|
|
5842
|
-
console.log(`[forkit-connect] ${identityParts.join(' | ')}`);
|
|
5843
|
-
}
|
|
5844
6194
|
const workspaces = Array.isArray(payload.workspaces) ? payload.workspaces : [];
|
|
5845
|
-
|
|
5846
|
-
if (workspaces.length === 0) {
|
|
5847
|
-
console.log('[forkit-connect] No accessible workspaces returned by /api/profiles/access.');
|
|
6195
|
+
if (await maybeShowInteractiveWorkspaceTable(workspaces)) {
|
|
5848
6196
|
return;
|
|
5849
6197
|
}
|
|
5850
|
-
|
|
5851
|
-
|
|
5852
|
-
|
|
6198
|
+
printWorkspaceListSurface(workspaces, {
|
|
6199
|
+
accountEmail: email,
|
|
6200
|
+
platformRole,
|
|
6201
|
+
});
|
|
5853
6202
|
return;
|
|
5854
6203
|
}
|
|
5855
6204
|
if (command === 'projects') {
|
|
@@ -5888,17 +6237,11 @@ async function run() {
|
|
|
5888
6237
|
process.exitCode = 2;
|
|
5889
6238
|
return;
|
|
5890
6239
|
}
|
|
5891
|
-
console.log('Authenticated successfully.');
|
|
5892
6240
|
const projects = Array.isArray(result.body.projects) ? result.body.projects : [];
|
|
5893
|
-
|
|
5894
|
-
if (projects.length === 0) {
|
|
5895
|
-
console.log('No projects found in this workspace.');
|
|
5896
|
-
console.log('Run `forkit-connect workspace select --workspace <id> --project-name "<name>"` to create the first project here.');
|
|
6241
|
+
if (await maybeShowInteractiveProjectTable(projects, workspaceId)) {
|
|
5897
6242
|
return;
|
|
5898
6243
|
}
|
|
5899
|
-
|
|
5900
|
-
console.log(formatWorkspaceProjectLine(project));
|
|
5901
|
-
}
|
|
6244
|
+
printProjectListSurface(projects, workspaceId);
|
|
5902
6245
|
return;
|
|
5903
6246
|
}
|
|
5904
6247
|
if (command === 'bind') {
|