@1a35e1/sonar-cli 0.2.1 → 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +151 -265
- package/dist/commands/{inbox/archive.js → archive.js} +2 -2
- package/dist/commands/config/data/download.js +2 -2
- package/dist/commands/config/data/sync.js +2 -2
- package/dist/commands/config/nuke.js +20 -2
- package/dist/commands/feed.js +105 -155
- package/dist/commands/index.js +172 -4
- package/dist/commands/{inbox/later.js → later.js} +2 -2
- package/dist/commands/refresh.js +41 -0
- package/dist/commands/{inbox/skip.js → skip.js} +2 -2
- package/dist/commands/status.js +128 -0
- package/dist/commands/sync/bookmarks.js +35 -0
- package/dist/commands/topics/add.js +71 -0
- package/dist/commands/topics/delete.js +42 -0
- package/dist/commands/topics/edit.js +97 -0
- package/dist/commands/topics/index.js +54 -0
- package/dist/commands/topics/suggest.js +125 -0
- package/dist/commands/topics/view.js +48 -0
- package/dist/components/AccountCard.js +1 -1
- package/dist/components/Banner.js +11 -0
- package/dist/components/InteractiveSession.js +95 -210
- package/dist/components/Spinner.js +5 -4
- package/dist/components/TopicCard.js +15 -0
- package/dist/components/TweetCard.js +76 -0
- package/dist/lib/ai.js +85 -0
- package/dist/lib/client.js +66 -40
- package/dist/lib/config.js +3 -2
- package/dist/lib/data-queries.js +1 -3
- package/dist/lib/skill.js +66 -226
- package/package.json +13 -3
- package/dist/commands/account.js +0 -75
- package/dist/commands/inbox/index.js +0 -103
- package/dist/commands/inbox/read.js +0 -41
- package/dist/commands/ingest/bookmarks.js +0 -55
- package/dist/commands/ingest/index.js +0 -5
- package/dist/commands/ingest/tweets.js +0 -55
- package/dist/commands/interests/create.js +0 -107
- package/dist/commands/interests/index.js +0 -56
- package/dist/commands/interests/match.js +0 -33
- package/dist/commands/interests/update.js +0 -153
- package/dist/commands/monitor.js +0 -93
- package/dist/commands/quickstart.js +0 -231
- package/dist/components/InterestCard.js +0 -10
package/dist/commands/monitor.js
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useState } from 'react';
|
|
3
|
-
import zod from 'zod';
|
|
4
|
-
import { Box, Text, useApp } from 'ink';
|
|
5
|
-
import { getToken, getApiUrl } from '../lib/config.js';
|
|
6
|
-
import { gql } from '../lib/client.js';
|
|
7
|
-
import { Spinner } from '../components/Spinner.js';
|
|
8
|
-
import { AccountCard } from '../components/AccountCard.js';
|
|
9
|
-
export const options = zod.object({
|
|
10
|
-
watch: zod.boolean().default(false).describe('Poll and refresh every 2 seconds'),
|
|
11
|
-
});
|
|
12
|
-
const POLL_INTERVAL = 2000;
|
|
13
|
-
const QUEUE_LABELS = {
|
|
14
|
-
tweets: 'Tweets',
|
|
15
|
-
bookmarks: 'Bookmarks',
|
|
16
|
-
social_graph: 'Social graph',
|
|
17
|
-
suggestions: 'Suggestions',
|
|
18
|
-
};
|
|
19
|
-
export default function Monitor({ options: flags }) {
|
|
20
|
-
const { exit } = useApp();
|
|
21
|
-
const [data, setData] = useState(null);
|
|
22
|
-
const [error, setError] = useState(null);
|
|
23
|
-
useEffect(() => {
|
|
24
|
-
const token = getToken();
|
|
25
|
-
const baseUrl = getApiUrl().replace(/\/graphql$/, '');
|
|
26
|
-
async function fetchStatus() {
|
|
27
|
-
const controller = new AbortController();
|
|
28
|
-
const timer = setTimeout(() => controller.abort(), 10_000);
|
|
29
|
-
try {
|
|
30
|
-
const [statusRes, meRes] = await Promise.all([
|
|
31
|
-
fetch(`${baseUrl}/indexing/status`, {
|
|
32
|
-
signal: controller.signal,
|
|
33
|
-
headers: { Authorization: `Bearer ${token}` },
|
|
34
|
-
}),
|
|
35
|
-
gql(`
|
|
36
|
-
query MonitorStatus {
|
|
37
|
-
me {
|
|
38
|
-
accountId
|
|
39
|
-
email
|
|
40
|
-
xHandle
|
|
41
|
-
xid
|
|
42
|
-
isPayingCustomer
|
|
43
|
-
indexingAccounts
|
|
44
|
-
indexedTweets
|
|
45
|
-
pendingEmbeddings
|
|
46
|
-
twitterIndexedAt
|
|
47
|
-
refreshedSuggestionsAt
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
`),
|
|
51
|
-
]);
|
|
52
|
-
clearTimeout(timer);
|
|
53
|
-
if (!statusRes.ok)
|
|
54
|
-
throw new Error(`HTTP ${statusRes.status} from ${baseUrl}`);
|
|
55
|
-
const status = await statusRes.json();
|
|
56
|
-
setData({ me: meRes.me, queues: status.queues });
|
|
57
|
-
setError(null);
|
|
58
|
-
}
|
|
59
|
-
catch (err) {
|
|
60
|
-
clearTimeout(timer);
|
|
61
|
-
if (err instanceof DOMException && err.name === 'AbortError') {
|
|
62
|
-
setError('Monitor request timed out (10s). ' +
|
|
63
|
-
'The server may be overloaded. ' +
|
|
64
|
-
'Check SONAR_API_URL or retry without --watch.');
|
|
65
|
-
}
|
|
66
|
-
else {
|
|
67
|
-
setError(err instanceof Error ? err.message : String(err));
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
fetchStatus();
|
|
72
|
-
if (!flags.watch)
|
|
73
|
-
return;
|
|
74
|
-
const timer = setInterval(fetchStatus, POLL_INTERVAL);
|
|
75
|
-
return () => clearInterval(timer);
|
|
76
|
-
}, []);
|
|
77
|
-
useEffect(() => {
|
|
78
|
-
if (!flags.watch && data !== null)
|
|
79
|
-
exit();
|
|
80
|
-
}, [data]);
|
|
81
|
-
useEffect(() => {
|
|
82
|
-
if (!flags.watch && error !== null)
|
|
83
|
-
exit(new Error(error));
|
|
84
|
-
}, [error]);
|
|
85
|
-
if (error)
|
|
86
|
-
return _jsxs(Text, { color: "red", children: ["Error: ", error] });
|
|
87
|
-
if (!data)
|
|
88
|
-
return _jsx(Spinner, { label: "Loading ingest status..." });
|
|
89
|
-
const { me, queues } = data;
|
|
90
|
-
const entries = Object.entries(queues);
|
|
91
|
-
const hasActivity = entries.length > 0 || me.pendingEmbeddings > 0;
|
|
92
|
-
return (_jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsx(AccountCard, { me: me }), _jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, color: "cyan", children: "Job Queues" }), !hasActivity ? (_jsxs(_Fragment, { children: [_jsx(Text, { color: "green", children: "No active ingest jobs." }), _jsxs(Text, { color: "green", children: ["Run ", _jsx(Text, { color: "cyan", children: "sonar interests match" }), " to start surface relevant tweets."] })] })) : (_jsxs(Box, { flexDirection: "column", gap: 0, children: [_jsxs(Box, { gap: 2, marginBottom: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: ('Queue').padEnd(16) }), _jsx(Text, { bold: true, color: "cyan", children: 'Running'.padEnd(10) }), _jsx(Text, { bold: true, color: "cyan", children: "Queued" })] }), entries.map(([name, counts]) => (_jsxs(Box, { gap: 2, children: [_jsx(Text, { children: (QUEUE_LABELS[name] ?? name).padEnd(16) }), _jsx(Text, { color: counts.running > 0 ? 'green' : 'white', children: String(counts.running).padEnd(10) }), _jsx(Text, { color: counts.queued > 0 ? 'yellow' : 'white', children: counts.queued })] }, name)))] }))] })] }));
|
|
93
|
-
}
|
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useRef, useState } from 'react';
|
|
3
|
-
import { Box, Text, useApp, useInput } from 'ink';
|
|
4
|
-
import { gql } from '../lib/client.js';
|
|
5
|
-
import { readConfig } from '../lib/config.js';
|
|
6
|
-
import { Spinner } from '../components/Spinner.js';
|
|
7
|
-
// ─── Queries / Mutations ──────────────────────────────────────────────────────
|
|
8
|
-
const BOOTSTRAP_QUERY = `
|
|
9
|
-
query QuickstartBootstrap {
|
|
10
|
-
me {
|
|
11
|
-
xHandle
|
|
12
|
-
}
|
|
13
|
-
projects {
|
|
14
|
-
id: nanoId
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
`;
|
|
18
|
-
const CREATE_MUTATION = `
|
|
19
|
-
mutation CreateOrUpdateInterest(
|
|
20
|
-
$nanoId: String
|
|
21
|
-
$name: String!
|
|
22
|
-
$description: String
|
|
23
|
-
$keywords: [String!]
|
|
24
|
-
$relatedTopics: [String!]
|
|
25
|
-
) {
|
|
26
|
-
createOrUpdateProject(input: {
|
|
27
|
-
nanoId: $nanoId
|
|
28
|
-
name: $name
|
|
29
|
-
description: $description
|
|
30
|
-
keywords: $keywords
|
|
31
|
-
relatedTopics: $relatedTopics
|
|
32
|
-
}) {
|
|
33
|
-
nanoId
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
`;
|
|
37
|
-
const INGEST_MUTATION = `
|
|
38
|
-
mutation IndexTweets {
|
|
39
|
-
indexTweets
|
|
40
|
-
}
|
|
41
|
-
`;
|
|
42
|
-
const INBOX_QUERY = `
|
|
43
|
-
query QuickstartInbox($status: SuggestionStatus, $limit: Int) {
|
|
44
|
-
suggestions(status: $status, limit: $limit) {
|
|
45
|
-
suggestionId
|
|
46
|
-
score
|
|
47
|
-
projectsMatched
|
|
48
|
-
status
|
|
49
|
-
tweet {
|
|
50
|
-
xid
|
|
51
|
-
text
|
|
52
|
-
createdAt
|
|
53
|
-
user {
|
|
54
|
-
displayName
|
|
55
|
-
username
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
`;
|
|
61
|
-
// ─── Starter interest suggestions ────────────────────────────────────────────
|
|
62
|
-
/**
|
|
63
|
-
* Returns 3 sensible starter interest drafts. In the future this could use
|
|
64
|
-
* the user's X bio / pinned tweet, but for now these are broadly useful
|
|
65
|
-
* defaults for the typical Sonar user (tech-forward Twitter crowd).
|
|
66
|
-
*/
|
|
67
|
-
function buildStarterSuggestions(_xHandle) {
|
|
68
|
-
return [
|
|
69
|
-
{
|
|
70
|
-
name: 'AI and machine learning',
|
|
71
|
-
description: 'Breakthroughs, papers, tools, and discussion around AI, LLMs, and machine learning.',
|
|
72
|
-
keywords: ['LLM', 'AI agents', 'machine learning', 'GPT', 'fine-tuning', 'inference'],
|
|
73
|
-
relatedTopics: ['artificial intelligence', 'deep learning', 'foundation models'],
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
name: 'Software engineering and developer tools',
|
|
77
|
-
description: 'New frameworks, libraries, OSS releases, and engineering practices worth tracking.',
|
|
78
|
-
keywords: ['open source', 'TypeScript', 'Rust', 'developer tools', 'CLI', 'API design'],
|
|
79
|
-
relatedTopics: ['software development', 'devex', 'programming'],
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
name: 'Tech startups and product launches',
|
|
83
|
-
description: 'Funding rounds, product launches, founder insights, and market moves in tech.',
|
|
84
|
-
keywords: ['startup', 'YC', 'product launch', 'founder', 'seed round', 'SaaS'],
|
|
85
|
-
relatedTopics: ['venture capital', 'entrepreneurship', 'B2B software'],
|
|
86
|
-
},
|
|
87
|
-
];
|
|
88
|
-
}
|
|
89
|
-
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
90
|
-
function relativeTime(dateStr) {
|
|
91
|
-
const ts = new Date(dateStr).getTime();
|
|
92
|
-
if (isNaN(ts))
|
|
93
|
-
return '?';
|
|
94
|
-
const diff = Math.max(0, Date.now() - ts);
|
|
95
|
-
const mins = Math.floor(diff / 60000);
|
|
96
|
-
if (mins < 60)
|
|
97
|
-
return `${mins}m`;
|
|
98
|
-
const hours = Math.floor(mins / 60);
|
|
99
|
-
if (hours < 24)
|
|
100
|
-
return `${hours}h`;
|
|
101
|
-
return `${Math.floor(hours / 24)}d`;
|
|
102
|
-
}
|
|
103
|
-
function hasToken() {
|
|
104
|
-
if (process.env.SONAR_API_KEY?.trim())
|
|
105
|
-
return true;
|
|
106
|
-
const config = readConfig();
|
|
107
|
-
return Boolean(config.token?.trim());
|
|
108
|
-
}
|
|
109
|
-
// ─── Sub-renders ──────────────────────────────────────────────────────────────
|
|
110
|
-
function UnauthenticatedView() {
|
|
111
|
-
return (_jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsx(Text, { bold: true, color: "yellow", children: "\u26A0 Not authenticated" }), _jsxs(Text, { children: ["Sonar needs an API key to get started. Get one at", ' ', _jsx(Text, { color: "cyan", children: "https://sonar.8640p.info" })] }), _jsxs(Box, { flexDirection: "column", gap: 0, children: [_jsx(Text, { dimColor: true, children: "Then run one of:" }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "SONAR_API_KEY=<key> sonar quickstart" }), " (one-off)"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "sonar config setup --key <key>" }), " (persist to ~/.sonar/config.json)"] })] })] }));
|
|
112
|
-
}
|
|
113
|
-
function ConfirmView({ me, suggestions, onConfirm, onAbort, }) {
|
|
114
|
-
useInput((input, key) => {
|
|
115
|
-
if (key.return || input === 'y' || input === 'Y') {
|
|
116
|
-
onConfirm();
|
|
117
|
-
}
|
|
118
|
-
else if (input === 'n' || input === 'N' || key.escape) {
|
|
119
|
-
onAbort();
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
|
-
return (_jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "Welcome to Sonar," }), _jsxs(Text, { bold: true, children: ["@", me.xHandle, "!"] })] }), _jsx(Text, { children: "You have no interests set up yet. Here are 3 starter suggestions to get your inbox going:" }), suggestions.map((s, i) => (_jsxs(Box, { flexDirection: "column", gap: 0, paddingLeft: 2, children: [_jsxs(Box, { gap: 1, children: [_jsxs(Text, { color: "cyan", children: [i + 1, "."] }), _jsx(Text, { bold: true, children: s.name })] }), _jsx(Box, { paddingLeft: 4, children: _jsx(Text, { dimColor: true, children: s.description }) }), _jsxs(Box, { gap: 1, paddingLeft: 4, children: [_jsx(Text, { dimColor: true, children: "keywords:" }), _jsx(Text, { dimColor: true, children: s.keywords.slice(0, 4).join(', ') })] })] }, s.name))), _jsxs(Box, { marginTop: 1, gap: 1, children: [_jsx(Text, { dimColor: true, children: "Create these interests and kick off indexing?" }), _jsx(Text, { bold: true, color: "cyan", children: "[Y/n]" })] }), _jsxs(Text, { dimColor: true, children: ["tip: customise later with", ' ', _jsx(Text, { color: "cyan", children: "sonar interests create --from-prompt \"...\"" })] })] }));
|
|
123
|
-
}
|
|
124
|
-
function CreatingView({ suggestions, progress }) {
|
|
125
|
-
return (_jsxs(Box, { flexDirection: "column", gap: 0, children: [_jsxs(Box, { gap: 1, marginBottom: 1, children: [_jsx(Text, { bold: true, children: "Setting up interests" }), _jsxs(Text, { dimColor: true, children: ["(", progress, "/", suggestions.length, ")"] })] }), suggestions.map((s, i) => (_jsxs(Box, { gap: 1, children: [i < progress ? (_jsx(Text, { color: "green", children: "\u2713" })) : i === progress ? (_jsx(Spinner, { label: "" })) : (_jsx(Text, { dimColor: true, children: "\u00B7" })), _jsx(Text, { dimColor: i > progress, color: i < progress ? 'green' : undefined, children: s.name })] }, s.name)))] }));
|
|
126
|
-
}
|
|
127
|
-
function InboxView({ items, created }) {
|
|
128
|
-
if (items.length === 0) {
|
|
129
|
-
return (_jsxs(Box, { flexDirection: "column", gap: 1, children: [created ? (_jsx(Text, { color: "green", children: "\u2713 Interests created and indexing triggered!" })) : (_jsx(Text, { color: "green", children: "\u2713 Your interests are set up \u2014 indexing is in progress." })), _jsxs(Box, { flexDirection: "column", gap: 0, children: [_jsx(Text, { children: "Your inbox is empty right now \u2014 indexing takes a few minutes." }), _jsxs(Text, { dimColor: true, children: ["Check back shortly with: ", _jsx(Text, { color: "cyan", children: "sonar inbox" })] })] }), _jsxs(Box, { flexDirection: "column", gap: 0, children: [_jsxs(Text, { dimColor: true, children: ["Monitor indexing progress: ", _jsx(Text, { color: "cyan", children: "sonar monitor" })] }), _jsxs(Text, { dimColor: true, children: ["Browse your full inbox: ", _jsx(Text, { color: "cyan", children: "sonar inbox" })] }), _jsxs(Text, { dimColor: true, children: ["Edit interests: ", _jsx(Text, { color: "cyan", children: "sonar interests" })] })] })] }));
|
|
130
|
-
}
|
|
131
|
-
return (_jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsx(Text, { color: "green", children: "\u2713 You're all set! Here's your inbox:" }), items.slice(0, 10).map((s) => {
|
|
132
|
-
const handle = s.tweet.user.username ?? s.tweet.user.displayName;
|
|
133
|
-
return (_jsxs(Box, { flexDirection: "column", gap: 0, children: [_jsxs(Box, { gap: 2, children: [_jsx(Text, { color: "cyan", children: relativeTime(s.tweet.createdAt) }), _jsx(Text, { color: "green", children: s.score.toFixed(2) }), _jsxs(Text, { dimColor: true, children: ["@", handle] })] }), _jsx(Box, { paddingLeft: 2, width: 80, children: _jsx(Text, { wrap: "wrap", dimColor: true, children: s.tweet.text.replace(/\n/g, ' ').slice(0, 120) }) })] }, s.suggestionId));
|
|
134
|
-
}), items.length > 10 && (_jsxs(Text, { dimColor: true, children: ["\u2026 and ", items.length - 10, " more. Run ", _jsx(Text, { color: "cyan", children: "sonar inbox" }), " to see all."] })), _jsxs(Text, { dimColor: true, children: ["Interactive mode: ", _jsx(Text, { color: "cyan", children: "sonar inbox --interactive" }), ' · ', "Full inbox: ", _jsx(Text, { color: "cyan", children: "sonar inbox" })] })] }));
|
|
135
|
-
}
|
|
136
|
-
// ─── Main component ───────────────────────────────────────────────────────────
|
|
137
|
-
export default function Quickstart() {
|
|
138
|
-
const { exit } = useApp();
|
|
139
|
-
const [phase, setPhase] = useState({ type: 'loading' });
|
|
140
|
-
const abortedRef = useRef(false);
|
|
141
|
-
const confirmedRef = useRef(false);
|
|
142
|
-
// ── Bootstrap: check auth + fetch me + projects ──────────────────────────
|
|
143
|
-
useEffect(() => {
|
|
144
|
-
if (!hasToken()) {
|
|
145
|
-
setPhase({ type: 'unauthenticated' });
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
async function bootstrap() {
|
|
149
|
-
try {
|
|
150
|
-
const result = await gql(BOOTSTRAP_QUERY);
|
|
151
|
-
if (!result.me) {
|
|
152
|
-
setPhase({ type: 'unauthenticated' });
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
// If interests already exist, jump straight to inbox
|
|
156
|
-
if (result.projects.length > 0) {
|
|
157
|
-
const inbox = await gql(INBOX_QUERY, {
|
|
158
|
-
status: 'INBOX',
|
|
159
|
-
limit: 20,
|
|
160
|
-
});
|
|
161
|
-
setPhase({ type: 'inbox', items: inbox.suggestions, created: false });
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
// No interests — propose starters
|
|
165
|
-
const suggestions = buildStarterSuggestions(result.me.xHandle);
|
|
166
|
-
setPhase({ type: 'confirm', me: result.me, suggestions });
|
|
167
|
-
}
|
|
168
|
-
catch (err) {
|
|
169
|
-
setPhase({ type: 'error', message: err instanceof Error ? err.message : String(err) });
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
bootstrap();
|
|
173
|
-
}, []);
|
|
174
|
-
// ── Create interests + ingest (triggered from confirm handler) ────────────
|
|
175
|
-
const handleConfirm = async (suggestions) => {
|
|
176
|
-
if (confirmedRef.current)
|
|
177
|
-
return;
|
|
178
|
-
confirmedRef.current = true;
|
|
179
|
-
setPhase({ type: 'creating', suggestions, progress: 0 });
|
|
180
|
-
try {
|
|
181
|
-
// Create each interest sequentially so progress counter is accurate
|
|
182
|
-
for (let i = 0; i < suggestions.length; i++) {
|
|
183
|
-
if (abortedRef.current)
|
|
184
|
-
return;
|
|
185
|
-
const s = suggestions[i];
|
|
186
|
-
await gql(CREATE_MUTATION, {
|
|
187
|
-
nanoId: null,
|
|
188
|
-
name: s.name,
|
|
189
|
-
description: s.description,
|
|
190
|
-
keywords: s.keywords,
|
|
191
|
-
relatedTopics: s.relatedTopics,
|
|
192
|
-
});
|
|
193
|
-
setPhase({ type: 'creating', suggestions, progress: i + 1 });
|
|
194
|
-
}
|
|
195
|
-
// Trigger ingest
|
|
196
|
-
setPhase({ type: 'ingesting' });
|
|
197
|
-
await gql(INGEST_MUTATION);
|
|
198
|
-
// Fetch initial inbox (may be empty — that's fine)
|
|
199
|
-
const inbox = await gql(INBOX_QUERY, {
|
|
200
|
-
status: 'INBOX',
|
|
201
|
-
limit: 20,
|
|
202
|
-
});
|
|
203
|
-
setPhase({ type: 'inbox', items: inbox.suggestions, created: true });
|
|
204
|
-
}
|
|
205
|
-
catch (err) {
|
|
206
|
-
setPhase({ type: 'error', message: err instanceof Error ? err.message : String(err) });
|
|
207
|
-
}
|
|
208
|
-
};
|
|
209
|
-
const handleAbort = () => {
|
|
210
|
-
abortedRef.current = true;
|
|
211
|
-
process.stdout.write('\nAborted. Run sonar quickstart again whenever you\'re ready.\n');
|
|
212
|
-
exit();
|
|
213
|
-
};
|
|
214
|
-
// ── Render ─────────────────────────────────────────────────────────────────
|
|
215
|
-
switch (phase.type) {
|
|
216
|
-
case 'loading':
|
|
217
|
-
return _jsx(Spinner, { label: "Loading your Sonar profile..." });
|
|
218
|
-
case 'unauthenticated':
|
|
219
|
-
return _jsx(UnauthenticatedView, {});
|
|
220
|
-
case 'error':
|
|
221
|
-
return (_jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsxs(Text, { color: "red", children: ["Error: ", phase.message] }), _jsxs(Text, { dimColor: true, children: ["Check your connection and API key, then retry: ", _jsx(Text, { color: "cyan", children: "sonar quickstart" })] })] }));
|
|
222
|
-
case 'confirm':
|
|
223
|
-
return (_jsx(ConfirmView, { me: phase.me, suggestions: phase.suggestions, onConfirm: () => handleConfirm(phase.suggestions), onAbort: handleAbort }));
|
|
224
|
-
case 'creating':
|
|
225
|
-
return _jsx(CreatingView, { suggestions: phase.suggestions, progress: phase.progress });
|
|
226
|
-
case 'ingesting':
|
|
227
|
-
return _jsx(Spinner, { label: "Triggering tweet indexing..." });
|
|
228
|
-
case 'inbox':
|
|
229
|
-
return _jsx(InboxView, { items: phase.items, created: phase.created });
|
|
230
|
-
}
|
|
231
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Box, Text } from 'ink';
|
|
3
|
-
export function InterestCard({ interest, termWidth, isLast }) {
|
|
4
|
-
const updatedAt = new Date(interest.updatedAt).toLocaleDateString('en-US', {
|
|
5
|
-
month: 'short',
|
|
6
|
-
day: 'numeric',
|
|
7
|
-
year: 'numeric',
|
|
8
|
-
});
|
|
9
|
-
return (_jsxs(Box, { flexDirection: "column", marginBottom: isLast ? 0 : 1, width: termWidth, children: [_jsxs(Box, { children: [_jsx(Text, { color: "cyan", bold: true, children: interest.name }), _jsxs(Text, { dimColor: true, children: [" v", interest.version, " \u00B7 ", interest.id, " \u00B7 ", updatedAt] })] }), interest.description && (_jsxs(Box, { children: [_jsxs(Text, { color: "gray", children: ['└', " "] }), _jsx(Text, { wrap: "wrap", children: interest.description })] })), interest.keywords && interest.keywords.length > 0 && (_jsxs(Box, { marginLeft: 2, children: [_jsx(Text, { dimColor: true, children: "keywords " }), _jsx(Text, { color: "yellow", children: interest.keywords.join(' ') })] })), interest.relatedTopics && interest.relatedTopics.length > 0 && (_jsxs(Box, { marginLeft: 2, children: [_jsx(Text, { dimColor: true, children: "topics " }), _jsx(Text, { children: interest.relatedTopics.join(' ') })] })), !isLast && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: '─'.repeat(Math.min(termWidth - 2, 72)) }) }))] }));
|
|
10
|
-
}
|