opencode-agora 0.4.0 → 0.4.2

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.
Files changed (248) hide show
  1. package/README.md +89 -415
  2. package/dist/atomic-write.d.ts +10 -0
  3. package/dist/atomic-write.d.ts.map +1 -0
  4. package/dist/atomic-write.js +23 -0
  5. package/dist/atomic-write.js.map +1 -0
  6. package/dist/auth/refresh.d.ts +17 -0
  7. package/dist/auth/refresh.d.ts.map +1 -0
  8. package/dist/auth/refresh.js +50 -0
  9. package/dist/auth/refresh.js.map +1 -0
  10. package/dist/cli/app.d.ts +11 -19
  11. package/dist/cli/app.d.ts.map +1 -1
  12. package/dist/cli/app.js +159 -2028
  13. package/dist/cli/app.js.map +1 -1
  14. package/dist/cli/commands/browse.d.ts +4 -0
  15. package/dist/cli/commands/browse.d.ts.map +1 -0
  16. package/dist/cli/commands/browse.js +80 -0
  17. package/dist/cli/commands/browse.js.map +1 -0
  18. package/dist/cli/commands/chat.d.ts +4 -0
  19. package/dist/cli/commands/chat.d.ts.map +1 -0
  20. package/dist/cli/commands/chat.js +125 -0
  21. package/dist/cli/commands/chat.js.map +1 -0
  22. package/dist/cli/commands/community.d.ts +12 -0
  23. package/dist/cli/commands/community.d.ts.map +1 -0
  24. package/dist/cli/commands/community.js +453 -0
  25. package/dist/cli/commands/community.js.map +1 -0
  26. package/dist/cli/commands/export.d.ts +3 -0
  27. package/dist/cli/commands/export.d.ts.map +1 -0
  28. package/dist/cli/commands/export.js +108 -0
  29. package/dist/cli/commands/export.js.map +1 -0
  30. package/dist/cli/commands/init.d.ts +4 -0
  31. package/dist/cli/commands/init.d.ts.map +1 -0
  32. package/dist/cli/commands/init.js +299 -0
  33. package/dist/cli/commands/init.js.map +1 -0
  34. package/dist/cli/commands/learn.d.ts +4 -0
  35. package/dist/cli/commands/learn.d.ts.map +1 -0
  36. package/dist/cli/commands/learn.js +62 -0
  37. package/dist/cli/commands/learn.js.map +1 -0
  38. package/dist/cli/commands/marketplace.d.ts +9 -0
  39. package/dist/cli/commands/marketplace.d.ts.map +1 -0
  40. package/dist/cli/commands/marketplace.js +321 -0
  41. package/dist/cli/commands/marketplace.js.map +1 -0
  42. package/dist/cli/commands/notify.d.ts +3 -0
  43. package/dist/cli/commands/notify.d.ts.map +1 -0
  44. package/dist/cli/commands/notify.js +59 -0
  45. package/dist/cli/commands/notify.js.map +1 -0
  46. package/dist/cli/commands/operations.d.ts +16 -0
  47. package/dist/cli/commands/operations.d.ts.map +1 -0
  48. package/dist/cli/commands/operations.js +1041 -0
  49. package/dist/cli/commands/operations.js.map +1 -0
  50. package/dist/cli/commands/outdated.d.ts +3 -0
  51. package/dist/cli/commands/outdated.d.ts.map +1 -0
  52. package/dist/cli/commands/outdated.js +48 -0
  53. package/dist/cli/commands/outdated.js.map +1 -0
  54. package/dist/cli/commands/ping.d.ts +3 -0
  55. package/dist/cli/commands/ping.d.ts.map +1 -0
  56. package/dist/cli/commands/ping.js +56 -0
  57. package/dist/cli/commands/ping.js.map +1 -0
  58. package/dist/cli/commands/scan.d.ts +3 -0
  59. package/dist/cli/commands/scan.d.ts.map +1 -0
  60. package/dist/cli/commands/scan.js +35 -0
  61. package/dist/cli/commands/scan.js.map +1 -0
  62. package/dist/cli/commands/today.d.ts +3 -0
  63. package/dist/cli/commands/today.d.ts.map +1 -0
  64. package/dist/cli/commands/today.js +142 -0
  65. package/dist/cli/commands/today.js.map +1 -0
  66. package/dist/cli/commands/types.d.ts +5 -0
  67. package/dist/cli/commands/types.d.ts.map +1 -0
  68. package/dist/cli/commands/types.js +2 -0
  69. package/dist/cli/commands/types.js.map +1 -0
  70. package/dist/cli/commands/watch.d.ts +3 -0
  71. package/dist/cli/commands/watch.d.ts.map +1 -0
  72. package/dist/cli/commands/watch.js +41 -0
  73. package/dist/cli/commands/watch.js.map +1 -0
  74. package/dist/cli/commands/welcome.d.ts +3 -0
  75. package/dist/cli/commands/welcome.d.ts.map +1 -0
  76. package/dist/cli/commands/welcome.js +97 -0
  77. package/dist/cli/commands/welcome.js.map +1 -0
  78. package/dist/cli/commands-meta.d.ts.map +1 -1
  79. package/dist/cli/commands-meta.js +286 -29
  80. package/dist/cli/commands-meta.js.map +1 -1
  81. package/dist/cli/completions-gen.d.ts +2 -0
  82. package/dist/cli/completions-gen.d.ts.map +1 -0
  83. package/dist/cli/completions-gen.js +195 -0
  84. package/dist/cli/completions-gen.js.map +1 -0
  85. package/dist/cli/completions.d.ts.map +1 -1
  86. package/dist/cli/completions.js +42 -5
  87. package/dist/cli/completions.js.map +1 -1
  88. package/dist/cli/flags.d.ts +19 -0
  89. package/dist/cli/flags.d.ts.map +1 -0
  90. package/dist/cli/flags.js +91 -0
  91. package/dist/cli/flags.js.map +1 -0
  92. package/dist/cli/format.d.ts +19 -0
  93. package/dist/cli/format.d.ts.map +1 -0
  94. package/dist/cli/format.js +249 -0
  95. package/dist/cli/format.js.map +1 -0
  96. package/dist/cli/helpers.d.ts +95 -0
  97. package/dist/cli/helpers.d.ts.map +1 -0
  98. package/dist/cli/helpers.js +301 -0
  99. package/dist/cli/helpers.js.map +1 -0
  100. package/dist/cli/mcp-server.d.ts +7 -1
  101. package/dist/cli/mcp-server.d.ts.map +1 -1
  102. package/dist/cli/mcp-server.js +70 -2
  103. package/dist/cli/mcp-server.js.map +1 -1
  104. package/dist/cli/menu.d.ts.map +1 -1
  105. package/dist/cli/menu.js +11 -3
  106. package/dist/cli/menu.js.map +1 -1
  107. package/dist/cli/pages/community.d.ts +6 -0
  108. package/dist/cli/pages/community.d.ts.map +1 -1
  109. package/dist/cli/pages/community.js +882 -64
  110. package/dist/cli/pages/community.js.map +1 -1
  111. package/dist/cli/pages/helpers.d.ts +14 -9
  112. package/dist/cli/pages/helpers.d.ts.map +1 -1
  113. package/dist/cli/pages/helpers.js +37 -6
  114. package/dist/cli/pages/helpers.js.map +1 -1
  115. package/dist/cli/pages/home.d.ts +1 -0
  116. package/dist/cli/pages/home.d.ts.map +1 -1
  117. package/dist/cli/pages/home.js +203 -120
  118. package/dist/cli/pages/home.js.map +1 -1
  119. package/dist/cli/pages/marketplace.d.ts +2 -0
  120. package/dist/cli/pages/marketplace.d.ts.map +1 -1
  121. package/dist/cli/pages/marketplace.js +524 -62
  122. package/dist/cli/pages/marketplace.js.map +1 -1
  123. package/dist/cli/pages/news.d.ts +28 -0
  124. package/dist/cli/pages/news.d.ts.map +1 -1
  125. package/dist/cli/pages/news.js +209 -82
  126. package/dist/cli/pages/news.js.map +1 -1
  127. package/dist/cli/pages/settings.d.ts.map +1 -1
  128. package/dist/cli/pages/settings.js +163 -33
  129. package/dist/cli/pages/settings.js.map +1 -1
  130. package/dist/cli/pages/types.d.ts +1 -1
  131. package/dist/cli/pages/types.d.ts.map +1 -1
  132. package/dist/cli/prompter.d.ts.map +1 -1
  133. package/dist/cli/prompter.js +43 -8
  134. package/dist/cli/prompter.js.map +1 -1
  135. package/dist/cli/shell.d.ts +2 -2
  136. package/dist/cli/shell.d.ts.map +1 -1
  137. package/dist/cli/shell.js +321 -18
  138. package/dist/cli/shell.js.map +1 -1
  139. package/dist/cli/tui.d.ts +1 -1
  140. package/dist/cli/tui.d.ts.map +1 -1
  141. package/dist/cli/tui.js +69 -23
  142. package/dist/cli/tui.js.map +1 -1
  143. package/dist/community/client.d.ts +45 -8
  144. package/dist/community/client.d.ts.map +1 -1
  145. package/dist/community/client.js +118 -23
  146. package/dist/community/client.js.map +1 -1
  147. package/dist/community/search.d.ts +25 -0
  148. package/dist/community/search.d.ts.map +1 -0
  149. package/dist/community/search.js +62 -0
  150. package/dist/community/search.js.map +1 -0
  151. package/dist/community/types.d.ts +21 -0
  152. package/dist/community/types.d.ts.map +1 -1
  153. package/dist/community/types.js +1 -1
  154. package/dist/config.d.ts +0 -4
  155. package/dist/config.d.ts.map +1 -1
  156. package/dist/config.js +0 -15
  157. package/dist/config.js.map +1 -1
  158. package/dist/data.d.ts.map +1 -1
  159. package/dist/data.js +142 -68
  160. package/dist/data.js.map +1 -1
  161. package/dist/format.d.ts +0 -2
  162. package/dist/format.d.ts.map +1 -1
  163. package/dist/format.js +0 -2
  164. package/dist/format.js.map +1 -1
  165. package/dist/hubs/cache.d.ts +6 -0
  166. package/dist/hubs/cache.d.ts.map +1 -0
  167. package/dist/hubs/cache.js +46 -0
  168. package/dist/hubs/cache.js.map +1 -0
  169. package/dist/hubs/enrichment.d.ts +43 -0
  170. package/dist/hubs/enrichment.d.ts.map +1 -0
  171. package/dist/hubs/enrichment.js +239 -0
  172. package/dist/hubs/enrichment.js.map +1 -0
  173. package/dist/hubs/github.d.ts +12 -0
  174. package/dist/hubs/github.d.ts.map +1 -0
  175. package/dist/hubs/github.js +54 -0
  176. package/dist/hubs/github.js.map +1 -0
  177. package/dist/hubs/huggingface.d.ts +27 -0
  178. package/dist/hubs/huggingface.d.ts.map +1 -0
  179. package/dist/hubs/huggingface.js +88 -0
  180. package/dist/hubs/huggingface.js.map +1 -0
  181. package/dist/hubs/quality.d.ts +26 -0
  182. package/dist/hubs/quality.d.ts.map +1 -0
  183. package/dist/hubs/quality.js +57 -0
  184. package/dist/hubs/quality.js.map +1 -0
  185. package/dist/hubs/types.d.ts +30 -0
  186. package/dist/hubs/types.d.ts.map +1 -0
  187. package/dist/hubs/types.js +2 -0
  188. package/dist/hubs/types.js.map +1 -0
  189. package/dist/index.d.ts.map +1 -1
  190. package/dist/index.js +84 -0
  191. package/dist/index.js.map +1 -1
  192. package/dist/init.js +2 -2
  193. package/dist/init.js.map +1 -1
  194. package/dist/live.d.ts +10 -0
  195. package/dist/live.d.ts.map +1 -1
  196. package/dist/live.js +31 -1
  197. package/dist/live.js.map +1 -1
  198. package/dist/marketplace.d.ts +16 -3
  199. package/dist/marketplace.d.ts.map +1 -1
  200. package/dist/marketplace.js +174 -7
  201. package/dist/marketplace.js.map +1 -1
  202. package/dist/news/cache.d.ts.map +1 -1
  203. package/dist/news/cache.js +4 -3
  204. package/dist/news/cache.js.map +1 -1
  205. package/dist/news/score.js +1 -1
  206. package/dist/news/sources/arxiv.d.ts.map +1 -1
  207. package/dist/news/sources/arxiv.js +10 -6
  208. package/dist/news/sources/arxiv.js.map +1 -1
  209. package/dist/news/sources/github-trending.d.ts.map +1 -1
  210. package/dist/news/sources/github-trending.js +9 -5
  211. package/dist/news/sources/github-trending.js.map +1 -1
  212. package/dist/news/sources/hn.d.ts.map +1 -1
  213. package/dist/news/sources/hn.js +8 -4
  214. package/dist/news/sources/hn.js.map +1 -1
  215. package/dist/news/sources/reddit.d.ts.map +1 -1
  216. package/dist/news/sources/reddit.js +5 -4
  217. package/dist/news/sources/reddit.js.map +1 -1
  218. package/dist/news/sources/rss.d.ts +10 -11
  219. package/dist/news/sources/rss.d.ts.map +1 -1
  220. package/dist/news/sources/rss.js +11 -99
  221. package/dist/news/sources/rss.js.map +1 -1
  222. package/dist/news/types.d.ts +3 -0
  223. package/dist/news/types.d.ts.map +1 -1
  224. package/dist/news/types.js +15 -6
  225. package/dist/news/types.js.map +1 -1
  226. package/dist/outdated.d.ts +24 -0
  227. package/dist/outdated.d.ts.map +1 -0
  228. package/dist/outdated.js +53 -0
  229. package/dist/outdated.js.map +1 -0
  230. package/dist/preferences.d.ts.map +1 -1
  231. package/dist/preferences.js +4 -4
  232. package/dist/preferences.js.map +1 -1
  233. package/dist/scan.d.ts +26 -0
  234. package/dist/scan.d.ts.map +1 -0
  235. package/dist/scan.js +179 -0
  236. package/dist/scan.js.map +1 -0
  237. package/dist/settings.d.ts.map +1 -1
  238. package/dist/settings.js +14 -20
  239. package/dist/settings.js.map +1 -1
  240. package/dist/state.d.ts +9 -2
  241. package/dist/state.d.ts.map +1 -1
  242. package/dist/state.js +41 -19
  243. package/dist/state.js.map +1 -1
  244. package/dist/types.d.ts +13 -0
  245. package/dist/types.d.ts.map +1 -1
  246. package/dist/ui.d.ts +1 -1
  247. package/dist/ui.js +1 -1
  248. package/package.json +4 -2
@@ -1,11 +1,63 @@
1
- import { getMarketplaceItems, searchMarketplaceItems, similarItems, } from '../../marketplace.js';
1
+ import { execSync } from 'node:child_process';
2
+ import { getMarketplaceItems, searchMarketplaceItems, similarItems, createInstallPlan, renderPermissionLines, hasPermissions, describePermissionGlob } from '../../marketplace.js';
2
3
  import { vlen, rail, noRail, sep, fmtCount, frame, scrollbar } from './helpers.js';
4
+ import { enrichItem, enrichHfItem } from '../../hubs/enrichment.js';
5
+ import { scanItem } from '../../scan.js';
6
+ import { detectAgoraDataDir, loadAgoraState, saveItemToState, writeAgoraState } from '../../state.js';
3
7
  const state = {
4
- cursor: 0, detail: false, query: '', filtering: false,
5
- category: 'all', sort: 'installs',
8
+ cursor: 0,
9
+ detail: false,
10
+ query: '',
11
+ filtering: false,
12
+ category: 'all',
13
+ sort: 'installs',
14
+ sourceFilter: 'all',
15
+ pricingFilter: 'all',
16
+ view: 'list',
17
+ installPlan: null,
18
+ installStatus: null,
19
+ enrichment: null,
20
+ enrichmentLoading: false,
21
+ scanResult: null,
22
+ scanLoading: false
6
23
  };
7
- function filtered() {
8
- const all = state.query ? searchMarketplaceItems({ query: state.query }) : getMarketplaceItems();
24
+ function itemSource(item) {
25
+ return item.kind === 'package' ? item.source : undefined;
26
+ }
27
+ function itemPricing(item) {
28
+ if (item.kind !== 'package')
29
+ return 'free';
30
+ return item.pricing?.kind ?? 'free';
31
+ }
32
+ function itemRepository(item) {
33
+ return item.kind === 'package' ? item.repository : undefined;
34
+ }
35
+ function itemPushedAt(item) {
36
+ return item.kind === 'package' ? item.pushedAt : undefined;
37
+ }
38
+ export function sourceBadge(item) {
39
+ const src = itemSource(item);
40
+ if (src === 'github')
41
+ return '[gh] ';
42
+ if (src === 'hf')
43
+ return '[hf] ';
44
+ return '[c] ';
45
+ }
46
+ function matchesSourceFilter(item, filter) {
47
+ if (filter === 'all')
48
+ return true;
49
+ const src = itemSource(item);
50
+ if (filter === 'curated')
51
+ return !src;
52
+ return src === filter;
53
+ }
54
+ function matchesPricingFilter(item, filter) {
55
+ if (filter === 'all')
56
+ return true;
57
+ return itemPricing(item) === filter;
58
+ }
59
+ function filtered(source) {
60
+ const all = state.query ? searchMarketplaceItems({ query: state.query }) : source;
9
61
  const sorted = all.slice().sort((a, b) => {
10
62
  if (state.sort === 'name')
11
63
  return a.name.localeCompare(b.name);
@@ -13,9 +65,10 @@ function filtered() {
13
65
  return (b.stars ?? 0) - (a.stars ?? 0);
14
66
  return (b.installs ?? 0) - (a.installs ?? 0);
15
67
  });
16
- return state.category === 'all'
17
- ? sorted
18
- : sorted.filter((i) => (i.tags ?? []).includes(state.category));
68
+ return sorted
69
+ .filter((i) => state.category === 'all' || (i.tags ?? []).includes(state.category))
70
+ .filter((i) => matchesSourceFilter(i, state.sourceFilter))
71
+ .filter((i) => matchesPricingFilter(i, state.pricingFilter));
19
72
  }
20
73
  export const marketplacePage = {
21
74
  id: 'marketplace',
@@ -26,67 +79,301 @@ export const marketplacePage = {
26
79
  { key: 'j/k', label: 'nav' },
27
80
  { key: 'Enter', label: 'details' },
28
81
  { key: 'i', label: 'install' },
82
+ { key: 'S', label: 'scan' },
29
83
  { key: 's', label: 'save' },
84
+ { key: 'o', label: 'sort/open' },
30
85
  { key: '/', label: 'filter' },
31
86
  { key: 'c', label: 'category' },
32
- { key: 'o', label: 'sort' },
87
+ { key: 't', label: 'source' },
88
+ { key: 'p', label: 'price' }
33
89
  ],
34
90
  render(ctx) {
35
91
  const { style, width, height } = ctx;
36
- const items = filtered();
92
+ if (state.view === 'scan') {
93
+ const lines = [];
94
+ lines.push(' ' + style.bold(style.accent('SCAN')));
95
+ lines.push(' ' + sep('', width - 2, style));
96
+ lines.push('');
97
+ const it = filtered(getMarketplaceItems())[state.cursor];
98
+ if (it)
99
+ lines.push(' ' + style.bold(it.name) + style.dim(' ' + it.id));
100
+ lines.push('');
101
+ if (state.scanLoading) {
102
+ lines.push(' ' + style.dim('Scanning…'));
103
+ }
104
+ else if (state.scanResult) {
105
+ for (const c of state.scanResult.checks) {
106
+ const icon = c.status === 'pass' ? style.accent('✓') : c.status === 'warn' ? style.orange('⚠') : style.bold('✗');
107
+ lines.push(' ' + icon + ' ' + style.dim(c.label) + ' ' + c.message);
108
+ }
109
+ lines.push('');
110
+ const { pass, warn, fail } = state.scanResult.summary;
111
+ lines.push(' ' + style.dim(`${pass} pass · ${warn} warning(s) · ${fail} failure(s)`));
112
+ }
113
+ else {
114
+ lines.push(' ' + style.dim('No scan result.'));
115
+ }
116
+ lines.push('');
117
+ lines.push(' ' + style.accent('esc') + style.dim(' back'));
118
+ return frame(lines, width, height);
119
+ }
120
+ if (state.view === 'install-preview' && state.installPlan) {
121
+ const plan = state.installPlan;
122
+ const lines = [];
123
+ lines.push(' ' + style.bold(style.accent('INSTALL PREVIEW')));
124
+ lines.push(' ' + sep('', width - 2, style));
125
+ lines.push('');
126
+ lines.push(' ' + style.bold(plan.item.name) + style.dim(' ' + plan.kind));
127
+ lines.push('');
128
+ if (plan.kind === 'git-clone') {
129
+ if (plan.cloneTarget)
130
+ lines.push(' ' + style.dim('Target ') + plan.cloneTarget);
131
+ if (plan.commands.length) {
132
+ lines.push('');
133
+ lines.push(' ' + style.dim('Command'));
134
+ for (const cmd of plan.commands)
135
+ lines.push(' ' + cmd);
136
+ }
137
+ if (plan.postInstallHint) {
138
+ lines.push('');
139
+ lines.push(' ' + style.dim('Next steps ') + plan.postInstallHint);
140
+ }
141
+ }
142
+ else if (plan.kind === 'package-install') {
143
+ if (plan.commands.length) {
144
+ lines.push(' ' + style.dim('Commands'));
145
+ for (const cmd of plan.commands)
146
+ lines.push(' ' + cmd);
147
+ }
148
+ }
149
+ else {
150
+ if (plan.commands.length) {
151
+ lines.push(' ' + style.dim('Commands'));
152
+ for (const cmd of plan.commands)
153
+ lines.push(' ' + cmd);
154
+ }
155
+ if (plan.notes.length) {
156
+ lines.push('');
157
+ for (const note of plan.notes)
158
+ lines.push(' ' + style.dim(note));
159
+ }
160
+ }
161
+ lines.push('');
162
+ const permLines = renderPermissionLines(plan.permissions);
163
+ if (permLines.length === 1) {
164
+ lines.push(' ' + style.dim(permLines[0]));
165
+ }
166
+ else {
167
+ lines.push(' ' + style.dim(permLines[0]));
168
+ for (const row of permLines.slice(1)) {
169
+ const spaceIdx = row.indexOf(' ', 2);
170
+ const label = row.slice(2, spaceIdx);
171
+ const value = row.slice(spaceIdx + 1);
172
+ lines.push(' ' + style.dim(label) + ' ' + value);
173
+ }
174
+ }
175
+ if (state.installStatus) {
176
+ lines.push('');
177
+ lines.push(' ' + style.accent(state.installStatus));
178
+ }
179
+ lines.push('');
180
+ if (hasPermissions(plan.permissions)) {
181
+ lines.push(' ' +
182
+ style.accent('g') +
183
+ style.dim(' grant + install ') +
184
+ style.accent('d') +
185
+ style.dim(' details ') +
186
+ style.accent('n') +
187
+ style.dim('/') +
188
+ style.accent('Esc') +
189
+ style.dim(' cancel'));
190
+ }
191
+ else {
192
+ lines.push(' ' +
193
+ style.accent('y') +
194
+ style.dim(' confirm ') +
195
+ style.accent('n') +
196
+ style.dim('/') +
197
+ style.accent('Esc') +
198
+ style.dim(' cancel'));
199
+ }
200
+ return frame(lines, width, height);
201
+ }
202
+ if (state.view === 'install-perm-details' && state.installPlan) {
203
+ const perms = state.installPlan.permissions;
204
+ const lines = [];
205
+ lines.push(' ' + style.bold(style.accent('PERMISSIONS DETAIL')));
206
+ lines.push(' ' + sep('', width - 2, style));
207
+ lines.push('');
208
+ const renderGroup = (label, legend, values) => {
209
+ if (!values?.length)
210
+ return;
211
+ lines.push(' ' + style.dim(label) + ' ' + style.dim(legend));
212
+ for (const v of values) {
213
+ const note = describePermissionGlob(v);
214
+ lines.push(' ' + v + (note ? ' ' + style.dim('— ' + note) : ''));
215
+ }
216
+ lines.push('');
217
+ };
218
+ renderGroup('fs ', 'What the package can read or write on disk.', perms?.fs);
219
+ renderGroup('net ', 'Hosts the package will reach over the network.', perms?.net);
220
+ renderGroup('exec', 'Binaries the package will invoke.', perms?.exec);
221
+ lines.push(' ' + style.accent('Esc') + style.dim(' back'));
222
+ return frame(lines, width, height);
223
+ }
224
+ const allItems = getMarketplaceItems();
225
+ const items = filtered(allItems);
37
226
  state.cursor = Math.min(state.cursor, Math.max(0, items.length - 1));
38
227
  const lines = [];
39
- const top = ' ' + style.bold(style.accent('MARKETPLACE'))
40
- + ' ' + style.dim('category: ') + style.accent(state.category)
41
- + style.dim(' \u00b7 sort: ') + style.accent(state.sort)
42
- + ' ' + style.dim(items.length + (items.length === 1 ? ' item' : ' items'));
228
+ // Source breakdown counts
229
+ let sourceBreakdown = '';
230
+ if (state.sourceFilter === 'all') {
231
+ const nCurated = allItems.filter((i) => !itemSource(i)).length;
232
+ const nGh = allItems.filter((i) => itemSource(i) === 'github').length;
233
+ const nHf = allItems.filter((i) => itemSource(i) === 'hf').length;
234
+ const parts = [];
235
+ if (nCurated)
236
+ parts.push(nCurated + ' curated');
237
+ if (nGh)
238
+ parts.push(nGh + ' gh');
239
+ if (nHf)
240
+ parts.push(nHf + ' hf');
241
+ if (parts.length)
242
+ sourceBreakdown = ' ' + style.dim(parts.join(' · '));
243
+ }
244
+ const top = ' ' +
245
+ style.bold(style.accent('MARKETPLACE')) +
246
+ ' ' +
247
+ style.dim('category: ') +
248
+ style.accent(state.category) +
249
+ style.dim(' · sort: ') +
250
+ style.accent(state.sort) +
251
+ style.dim(' · src: ') +
252
+ style.accent(state.sourceFilter) +
253
+ style.dim(' · price: ') +
254
+ style.accent(state.pricingFilter) +
255
+ ' ' +
256
+ style.dim(items.length + (items.length === 1 ? ' item' : ' items')) +
257
+ sourceBreakdown;
43
258
  lines.push(top);
44
259
  lines.push(' ' + sep('', width - 2, style));
45
260
  if (state.filtering) {
46
- lines.push(' ' + style.accent('/') + ' ' + state.query + style.dim('\u258f'));
261
+ lines.push(' ' + style.accent('/') + ' ' + state.query + style.dim(''));
47
262
  lines.push('');
48
263
  }
49
264
  if (items.length === 0) {
50
265
  lines.push('');
51
- lines.push(' ' + style.dim('No items match ')
52
- + style.accent(state.query || state.category) + style.dim('.'));
53
- lines.push(' ' + style.dim('Press ') + style.accent('/')
54
- + style.dim(' to change filter, ') + style.accent('c')
55
- + style.dim(' to reset category.'));
266
+ lines.push(' ' +
267
+ style.dim('No items match ') +
268
+ style.accent(state.query || state.category) +
269
+ style.dim('.'));
270
+ lines.push(' ' +
271
+ style.dim('Press ') +
272
+ style.accent('/') +
273
+ style.dim(' to change filter, ') +
274
+ style.accent('c') +
275
+ style.dim(' to reset category.'));
276
+ if (process.env.AGORA_LIVE_HUBS !== '1' &&
277
+ state.sourceFilter !== 'curated') {
278
+ lines.push('');
279
+ lines.push(' ' +
280
+ style.dim('Tip: set AGORA_LIVE_HUBS=1 to pull live GitHub + HuggingFace items.'));
281
+ }
56
282
  return frame(lines, width, height);
57
283
  }
58
284
  if (state.detail) {
59
285
  const it = items[state.cursor];
60
- if (it) {
61
- lines.push('');
62
- lines.push(' ' + style.bold(it.name)
63
- + (it.author ? style.dim(' by ' + it.author) : ''));
64
- lines.push(' ' + style.dim((it.tags ?? []).map((t) => '[' + t + ']').join(' ')));
65
- lines.push('');
66
- lines.push(' ' + (it.description ?? ''));
67
- lines.push('');
68
- lines.push(' '
69
- + style.accent(fmtCount(it.installs ?? 0)) + style.dim(' installs ')
70
- + (it.stars !== undefined
71
- ? style.accent(fmtCount(it.stars)) + style.dim(' \u2605') : ''));
72
- lines.push('');
73
- lines.push(' '
74
- + style.accent('i') + style.dim(' install ')
75
- + style.accent('s') + style.dim(' save ')
76
- + style.accent('Esc') + style.dim(' back'));
77
- const kind = it.kind === 'workflow' ? 'workflow' : 'package';
78
- const related = similarItems(it.id, { limit: 3, type: kind });
79
- if (related.length > 0) {
80
- lines.push('');
81
- lines.push(' ' + sep('Related', width - 2, style));
82
- for (const rel of related) {
83
- if (rel.id === it.id)
84
- continue;
85
- lines.push(' \u00b7 ' + style.bold(rel.name.padEnd(20))
86
- + style.dim(fmtCount(rel.installs ?? 0) + ' installs'));
286
+ if (!it)
287
+ return frame(lines, width, height);
288
+ const detail = [];
289
+ // Header line: badge + name + pricing + author
290
+ const badge = style.dim(sourceBadge(it));
291
+ const pricing = itemPricing(it) === 'paid' ? ' ' + style.accent('PAID') : '';
292
+ detail.push(' ' + badge + style.bold(style.accent(it.name)) + pricing);
293
+ const metaLine = (it.author ? style.dim('by ' + it.author) : '') +
294
+ (it.version ? style.dim(' v' + it.version) : '');
295
+ if (metaLine.trim())
296
+ detail.push(' ' + metaLine);
297
+ detail.push(' ' + sep('', width - 2, style));
298
+ // Description block (AI-enriched when available)
299
+ const displayDesc = state.enrichment?.description
300
+ ? state.enrichment.description + style.dim(' (ai)')
301
+ : (it.description ?? '');
302
+ if (displayDesc)
303
+ detail.push(' ' + displayDesc);
304
+ if (state.enrichmentLoading)
305
+ detail.push(' ' + style.dim('(enriching…)'));
306
+ // Stats line
307
+ detail.push('');
308
+ const statsParts = [];
309
+ if (it.installs !== undefined) {
310
+ statsParts.push(style.accent(fmtCount(it.installs)) + style.dim(' installs'));
311
+ }
312
+ if (it.stars !== undefined) {
313
+ statsParts.push(style.accent(fmtCount(it.stars)) + style.dim(' ★'));
314
+ }
315
+ const pushedAt = itemPushedAt(it);
316
+ if (pushedAt) {
317
+ const ageH = (Date.now() - new Date(pushedAt).getTime()) / 3600000;
318
+ const age = ageH < 24 ? Math.round(ageH) + 'h' : Math.round(ageH / 24) + 'd';
319
+ statsParts.push(style.dim('updated ' + age + ' ago'));
320
+ }
321
+ if (statsParts.length)
322
+ detail.push(' ' + statsParts.join(' '));
323
+ // Tags
324
+ if (it.tags?.length) {
325
+ detail.push(' ' + style.dim((it.tags ?? []).map((t) => '[' + t + ']').join(' ')));
326
+ }
327
+ // Repository link
328
+ const repoUrl = itemRepository(it);
329
+ if (repoUrl) {
330
+ detail.push(' ' + style.dim('repo ') + repoUrl);
331
+ }
332
+ // Permissions block (always shown — none-declared is informative)
333
+ const itPerms = it.kind === 'package' ? it.permissions : undefined;
334
+ if (hasPermissions(itPerms)) {
335
+ detail.push('');
336
+ detail.push(' ' + sep('Permissions', width - 2, style));
337
+ for (const row of renderPermissionLines(itPerms).slice(1)) {
338
+ const spaceIdx = row.indexOf(' ', 2);
339
+ const label = row.slice(2, spaceIdx);
340
+ const value = row.slice(spaceIdx + 1);
341
+ detail.push(' ' + style.dim(label) + ' ' + value);
342
+ }
343
+ }
344
+ // Related items
345
+ const kind = it.kind === 'workflow' ? 'workflow' : 'package';
346
+ const related = similarItems(it.id, { limit: 4, type: kind });
347
+ const relatedFiltered = related.filter((r) => r.id !== it.id).slice(0, 3);
348
+ if (relatedFiltered.length > 0) {
349
+ detail.push('');
350
+ detail.push(' ' + sep('Related', width - 2, style));
351
+ for (const rel of relatedFiltered) {
352
+ const relStats = style.dim(fmtCount(rel.installs ?? 0) + ' installs');
353
+ detail.push(' ' + style.bold(rel.name.padEnd(28)) + relStats);
354
+ if (rel.description) {
355
+ detail.push(' ' + style.dim((rel.description ?? '').slice(0, Math.max(0, width - 6))));
87
356
  }
88
357
  }
89
358
  }
359
+ // Footer — pinned via padding
360
+ const footer = [
361
+ ' ' + sep('', width - 2, style),
362
+ ' ' +
363
+ style.accent('i') +
364
+ style.dim(' install ') +
365
+ style.accent('s') +
366
+ style.dim(' save ') +
367
+ style.accent('o') +
368
+ style.dim(' open repo ') +
369
+ style.accent('Esc') +
370
+ style.dim(' back')
371
+ ];
372
+ const padCount = Math.max(0, height - lines.length - detail.length - footer.length);
373
+ lines.push(...detail);
374
+ for (let i = 0; i < padCount; i++)
375
+ lines.push('');
376
+ lines.push(...footer);
90
377
  return frame(lines, width, height);
91
378
  }
92
379
  const limit = Math.max(0, height - lines.length - 1);
@@ -98,24 +385,89 @@ export const marketplacePage = {
98
385
  continue;
99
386
  const selected = start + i === state.cursor;
100
387
  const lead = selected ? rail(style) : noRail();
101
- const stats = style.accent(fmtCount(it.installs ?? 0).padStart(7))
102
- + style.dim(' installs')
103
- + (it.stars !== undefined
104
- ? ' ' + style.accent(fmtCount(it.stars).padStart(5)) + style.dim(' \u2605')
388
+ const badge = style.dim(sourceBadge(it));
389
+ const pricingBadge = itemPricing(it) === 'paid' ? style.accent('PAID') + ' ' : '';
390
+ const stats = pricingBadge +
391
+ style.accent(fmtCount(it.installs ?? 0).padStart(7)) +
392
+ style.dim(' installs') +
393
+ (it.stars !== undefined
394
+ ? ' ' + style.accent(fmtCount(it.stars).padStart(5)) + style.dim(' ★')
105
395
  : '');
106
396
  const nameCell = selected ? style.bold(it.name) : it.name;
107
- const left = ' ' + lead + nameCell;
397
+ const perms = it.kind === 'package' ? it.permissions : undefined;
398
+ const permCats = perms
399
+ ? ['fs', 'net', 'exec'].filter((k) => perms[k]?.length).join(' ')
400
+ : '';
401
+ const permSuffix = permCats ? ' ' + style.dim('[' + permCats + ']') : '';
402
+ const left = ' ' + lead + badge + nameCell + permSuffix;
108
403
  const room = width - vlen(left) - vlen(stats) - 2;
109
404
  const desc = style.dim((it.description ?? '').slice(0, Math.max(0, room - 2)));
110
405
  const pad = ' '.repeat(Math.max(1, room - vlen(desc) - 1));
111
406
  lines.push(left + ' ' + desc + pad + stats + ' ' + sbar[i]);
112
407
  }
113
- lines.push(' ' + (items.length > limit
114
- ? style.dim('items ' + (start + 1) + '\u2013' + Math.min(start + limit, items.length) + ' of ' + items.length)
115
- : style.dim(items.length + (items.length === 1 ? ' item' : ' items'))));
408
+ lines.push(' ' +
409
+ (items.length > limit
410
+ ? style.dim('items ' +
411
+ (start + 1) +
412
+ '–' +
413
+ Math.min(start + limit, items.length) +
414
+ ' of ' +
415
+ items.length)
416
+ : style.dim(items.length + (items.length === 1 ? ' item' : ' items'))));
116
417
  return frame(lines, width, height);
117
418
  },
118
419
  handleKey(event, _ctx) {
420
+ if (state.view === 'scan') {
421
+ if (event.key === 'esc' || event.key === 'n') {
422
+ state.view = 'list';
423
+ state.scanResult = null;
424
+ state.scanLoading = false;
425
+ }
426
+ return { kind: 'none' };
427
+ }
428
+ if (state.view === 'install-perm-details') {
429
+ if (event.key === 'esc')
430
+ state.view = 'install-preview';
431
+ return { kind: 'none' };
432
+ }
433
+ if (state.view === 'install-preview') {
434
+ const confirmKeys = state.installPlan && hasPermissions(state.installPlan.permissions)
435
+ ? new Set(['g', 'y'])
436
+ : new Set(['y']);
437
+ if (confirmKeys.has(event.key) && state.installPlan) {
438
+ const plan = state.installPlan;
439
+ if (!plan.installable) {
440
+ state.installStatus = plan.reason || 'Not installable.';
441
+ return { kind: 'none' };
442
+ }
443
+ let success = true;
444
+ for (const cmd of plan.commands) {
445
+ try {
446
+ execSync(cmd, { stdio: 'pipe', timeout: 60000 });
447
+ }
448
+ catch {
449
+ state.installStatus = `Failed: ${cmd}`;
450
+ success = false;
451
+ break;
452
+ }
453
+ }
454
+ if (success) {
455
+ state.installStatus = `Installed ${plan.item.name}`;
456
+ }
457
+ return { kind: 'none' };
458
+ }
459
+ if (event.key === 'd' && state.installPlan && hasPermissions(state.installPlan.permissions)) {
460
+ state.view = 'install-perm-details';
461
+ return { kind: 'none' };
462
+ }
463
+ if (event.key === 'n' || event.key === 'esc') {
464
+ state.view = 'list';
465
+ state.installPlan = null;
466
+ state.installStatus = null;
467
+ return { kind: 'none' };
468
+ }
469
+ return { kind: 'none' };
470
+ }
119
471
  if (state.filtering) {
120
472
  if (event.key === 'esc') {
121
473
  state.filtering = false;
@@ -136,7 +488,7 @@ export const marketplacePage = {
136
488
  }
137
489
  return { kind: 'none' };
138
490
  }
139
- const items = filtered();
491
+ const items = filtered(getMarketplaceItems());
140
492
  switch (event.key) {
141
493
  case 'j':
142
494
  case 'down':
@@ -146,18 +498,93 @@ export const marketplacePage = {
146
498
  case 'up':
147
499
  state.cursor = Math.max(0, state.cursor - 1);
148
500
  return { kind: 'none' };
149
- case 'enter':
501
+ case 'enter': {
150
502
  state.detail = !state.detail;
503
+ if (state.detail) {
504
+ const selected = items[state.cursor];
505
+ const selectedSource = selected ? itemSource(selected) : undefined;
506
+ if (selected && selectedSource === 'github') {
507
+ state.enrichment = null;
508
+ state.enrichmentLoading = true;
509
+ const repoId = selected.id.startsWith('gh:') ? selected.id.slice(3) : null;
510
+ if (repoId) {
511
+ const dataDir = detectAgoraDataDir({ env: process.env });
512
+ const ctx = _ctx;
513
+ enrichItem(repoId, dataDir).then((entry) => {
514
+ state.enrichment = entry;
515
+ state.enrichmentLoading = false;
516
+ ctx.repaint();
517
+ });
518
+ }
519
+ else {
520
+ state.enrichmentLoading = false;
521
+ }
522
+ }
523
+ else if (selected && selectedSource === 'hf') {
524
+ state.enrichment = null;
525
+ state.enrichmentLoading = true;
526
+ const repoId = selected.id.startsWith('hf:') ? selected.id.slice(3) : null;
527
+ if (repoId) {
528
+ const dataDir = detectAgoraDataDir({ env: process.env });
529
+ const ctx = _ctx;
530
+ enrichHfItem(repoId, dataDir).then((entry) => {
531
+ state.enrichment = entry;
532
+ state.enrichmentLoading = false;
533
+ ctx.repaint();
534
+ });
535
+ }
536
+ else {
537
+ state.enrichmentLoading = false;
538
+ }
539
+ }
540
+ else {
541
+ state.enrichment = null;
542
+ state.enrichmentLoading = false;
543
+ }
544
+ }
545
+ else {
546
+ state.enrichment = null;
547
+ state.enrichmentLoading = false;
548
+ }
151
549
  return { kind: 'none' };
550
+ }
152
551
  case 'esc':
153
- if (state.detail)
552
+ if (state.detail) {
154
553
  state.detail = false;
554
+ state.enrichment = null;
555
+ state.enrichmentLoading = false;
556
+ }
155
557
  return { kind: 'none' };
156
558
  case 'i': {
157
559
  const it = items[state.cursor];
158
- return { kind: 'status', message: it ? 'install ' + it.id + ' queued' : 'nothing selected' };
560
+ if (!it)
561
+ return { kind: 'status', message: 'nothing selected' };
562
+ const plan = createInstallPlan(it, {}, {
563
+ aiInstallHint: state.enrichment?.installHint
564
+ });
565
+ state.installPlan = plan;
566
+ state.installStatus = null;
567
+ state.view = 'install-preview';
568
+ return { kind: 'none' };
569
+ }
570
+ case 'S': {
571
+ const it = items[state.cursor];
572
+ if (!it)
573
+ return { kind: 'status', message: 'nothing selected' };
574
+ state.view = 'scan';
575
+ state.scanResult = null;
576
+ state.scanLoading = true;
577
+ const ctx = _ctx;
578
+ scanItem(it, { githubToken: process.env.AGORA_GITHUB_TOKEN }).then((r) => {
579
+ state.scanResult = r;
580
+ state.scanLoading = false;
581
+ ctx.repaint();
582
+ }).catch(() => {
583
+ state.scanLoading = false;
584
+ ctx.repaint();
585
+ });
586
+ return { kind: 'none' };
159
587
  }
160
- case 's': return { kind: 'status', message: 'saved' };
161
588
  case '/':
162
589
  state.filtering = true;
163
590
  return { kind: 'none' };
@@ -168,12 +595,47 @@ export const marketplacePage = {
168
595
  return { kind: 'none' };
169
596
  }
170
597
  case 'o': {
598
+ if (state.detail) {
599
+ const it = items[state.cursor];
600
+ const url = it ? itemRepository(it) : undefined;
601
+ if (url)
602
+ return { kind: 'open-url', url };
603
+ return { kind: 'status', message: 'no repo url' };
604
+ }
171
605
  const order = ['installs', 'stars', 'name'];
172
606
  state.sort = order[(order.indexOf(state.sort) + 1) % order.length] ?? 'installs';
173
607
  return { kind: 'none' };
174
608
  }
175
- default: return { kind: 'none' };
609
+ case 's': {
610
+ if (!state.detail)
611
+ return { kind: 'none' };
612
+ const it = items[state.cursor];
613
+ if (!it)
614
+ return { kind: 'status', message: 'nothing selected' };
615
+ const dir = detectAgoraDataDir({ env: process.env });
616
+ const next = saveItemToState(loadAgoraState(dir), it);
617
+ if (!next.added)
618
+ return { kind: 'status', message: 'already saved' };
619
+ writeAgoraState(dir, next.state);
620
+ return { kind: 'status', message: 'saved ' + it.name };
621
+ }
622
+ case 't': {
623
+ const order = ['all', 'curated', 'github', 'hf'];
624
+ state.sourceFilter =
625
+ order[(order.indexOf(state.sourceFilter) + 1) % order.length] ?? 'all';
626
+ state.cursor = 0;
627
+ return { kind: 'none' };
628
+ }
629
+ case 'p': {
630
+ const order = ['all', 'free', 'paid'];
631
+ state.pricingFilter =
632
+ order[(order.indexOf(state.pricingFilter) + 1) % order.length] ?? 'all';
633
+ state.cursor = 0;
634
+ return { kind: 'none' };
635
+ }
636
+ default:
637
+ return { kind: 'none' };
176
638
  }
177
- },
639
+ }
178
640
  };
179
641
  //# sourceMappingURL=marketplace.js.map