opencode-agora 0.4.0 → 0.4.1

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