@jackwener/opencli 1.7.7 → 1.7.9

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 (280) hide show
  1. package/README.md +49 -14
  2. package/README.zh-CN.md +30 -10
  3. package/cli-manifest.json +782 -55
  4. package/clis/36kr/news.js +1 -1
  5. package/clis/amazon/discussion.js +37 -6
  6. package/clis/amazon/discussion.test.js +147 -32
  7. package/clis/apple-podcasts/commands.test.js +4 -4
  8. package/clis/apple-podcasts/episodes.js +1 -1
  9. package/clis/apple-podcasts/search.js +1 -1
  10. package/clis/apple-podcasts/top.js +1 -1
  11. package/clis/arxiv/paper.js +1 -1
  12. package/clis/arxiv/search.js +1 -1
  13. package/clis/band/mentions.js +3 -3
  14. package/clis/bbc/news.js +1 -1
  15. package/clis/bilibili/subtitle.js +2 -2
  16. package/clis/bloomberg/businessweek.js +1 -1
  17. package/clis/bloomberg/economics.js +1 -1
  18. package/clis/bloomberg/industries.js +1 -1
  19. package/clis/bloomberg/main.js +1 -1
  20. package/clis/bloomberg/markets.js +1 -1
  21. package/clis/bloomberg/opinions.js +1 -1
  22. package/clis/bloomberg/politics.js +1 -1
  23. package/clis/bloomberg/tech.js +1 -1
  24. package/clis/boss/search.js +49 -8
  25. package/clis/boss/search.test.js +78 -0
  26. package/clis/boss/send.js +3 -3
  27. package/clis/chatgpt/image.js +37 -8
  28. package/clis/chatgpt/image.test.js +92 -0
  29. package/clis/chatgpt/utils.js +39 -6
  30. package/clis/chatgpt/utils.test.js +63 -0
  31. package/clis/chatgpt-app/ask.js +4 -20
  32. package/clis/chatgpt-app/ax.js +135 -2
  33. package/clis/chatgpt-app/ax.test.js +35 -0
  34. package/clis/chatgpt-app/model.js +1 -1
  35. package/clis/chatgpt-app/new.js +1 -1
  36. package/clis/chatgpt-app/read.js +1 -1
  37. package/clis/chatgpt-app/send.js +3 -22
  38. package/clis/chatgpt-app/status.js +1 -1
  39. package/clis/chatwise/ask.js +2 -2
  40. package/clis/chatwise/model.js +2 -2
  41. package/clis/chatwise/send.js +2 -2
  42. package/clis/claude/ask.js +128 -0
  43. package/clis/claude/ask.test.js +338 -0
  44. package/clis/claude/commands.test.js +118 -0
  45. package/clis/claude/detail.js +29 -0
  46. package/clis/claude/history.js +31 -0
  47. package/clis/claude/new.js +21 -0
  48. package/clis/claude/read.js +24 -0
  49. package/clis/claude/send.js +41 -0
  50. package/clis/claude/status.js +24 -0
  51. package/clis/claude/utils.js +440 -0
  52. package/clis/claude/utils.test.js +148 -0
  53. package/clis/codex/ask.js +2 -2
  54. package/clis/codex/send.js +2 -2
  55. package/clis/ctrip/search.js +1 -1
  56. package/clis/ctrip/search.test.js +4 -4
  57. package/clis/cursor/ask.js +2 -2
  58. package/clis/cursor/composer.js +2 -2
  59. package/clis/cursor/send.js +2 -2
  60. package/clis/deepseek/ask.js +49 -10
  61. package/clis/deepseek/ask.test.js +150 -3
  62. package/clis/deepseek/utils.js +60 -22
  63. package/clis/deepseek/utils.test.js +124 -5
  64. package/clis/doubao/utils.js +53 -11
  65. package/clis/doubao/utils.test.js +22 -2
  66. package/clis/eastmoney/announcement.js +1 -1
  67. package/clis/eastmoney/convertible.js +1 -1
  68. package/clis/eastmoney/etf.js +1 -1
  69. package/clis/eastmoney/holders.js +1 -1
  70. package/clis/eastmoney/index-board.js +1 -1
  71. package/clis/eastmoney/kline.js +1 -1
  72. package/clis/eastmoney/kuaixun.js +1 -1
  73. package/clis/eastmoney/longhu.js +1 -1
  74. package/clis/eastmoney/money-flow.js +1 -1
  75. package/clis/eastmoney/northbound.js +1 -1
  76. package/clis/eastmoney/quote.js +1 -1
  77. package/clis/eastmoney/rank.js +1 -1
  78. package/clis/eastmoney/sectors.js +1 -1
  79. package/clis/facebook/marketplace-inbox.js +83 -0
  80. package/clis/facebook/marketplace-listings.js +83 -0
  81. package/clis/facebook/marketplace.test.js +91 -0
  82. package/clis/google/news.js +1 -1
  83. package/clis/google/suggest.js +1 -1
  84. package/clis/google/trends.js +1 -1
  85. package/clis/google-scholar/cite.js +74 -0
  86. package/clis/google-scholar/cite.test.js +47 -0
  87. package/clis/google-scholar/profile.js +92 -0
  88. package/clis/google-scholar/profile.test.js +49 -0
  89. package/clis/google-scholar/search.js +1 -1
  90. package/clis/google-scholar/search.test.js +15 -0
  91. package/clis/hf/top.js +1 -1
  92. package/clis/jd/item.js +679 -47
  93. package/clis/jd/item.test.js +318 -7
  94. package/clis/jd/item.test.ts +517 -0
  95. package/clis/lesswrong/comments.js +1 -1
  96. package/clis/lesswrong/curated.js +1 -1
  97. package/clis/lesswrong/frontpage.js +1 -1
  98. package/clis/lesswrong/new.js +1 -1
  99. package/clis/lesswrong/read.js +1 -1
  100. package/clis/lesswrong/sequences.js +1 -1
  101. package/clis/lesswrong/shortform.js +1 -1
  102. package/clis/lesswrong/tag.js +1 -1
  103. package/clis/lesswrong/tags.js +1 -1
  104. package/clis/lesswrong/top-month.js +1 -1
  105. package/clis/lesswrong/top-week.js +1 -1
  106. package/clis/lesswrong/top-year.js +1 -1
  107. package/clis/lesswrong/top.js +1 -1
  108. package/clis/lesswrong/user-posts.js +1 -1
  109. package/clis/lesswrong/user.js +1 -1
  110. package/clis/paperreview/commands.test.js +6 -6
  111. package/clis/paperreview/feedback.js +1 -1
  112. package/clis/paperreview/review.js +1 -1
  113. package/clis/paperreview/submit.js +1 -1
  114. package/clis/powerchina/search.js +250 -0
  115. package/clis/powerchina/search.test.js +67 -0
  116. package/clis/producthunt/posts.js +1 -1
  117. package/clis/producthunt/today.js +1 -1
  118. package/clis/sinablog/search.js +1 -1
  119. package/clis/sinafinance/news.js +1 -1
  120. package/clis/sinafinance/stock.js +6 -3
  121. package/clis/sinafinance/stock.test.js +59 -0
  122. package/clis/spotify/spotify.js +6 -6
  123. package/clis/substack/search.js +1 -1
  124. package/clis/toutiao/articles.js +80 -0
  125. package/clis/toutiao/articles.test.js +30 -0
  126. package/clis/twitter/followers.js +2 -2
  127. package/clis/twitter/following.js +224 -73
  128. package/clis/twitter/following.test.js +277 -0
  129. package/clis/twitter/post.js +184 -47
  130. package/clis/twitter/post.test.js +114 -34
  131. package/clis/uiverse/_shared.js +63 -4
  132. package/clis/uiverse/_shared.test.js +7 -0
  133. package/clis/uiverse/code.js +1 -0
  134. package/clis/uiverse/navigation.test.js +12 -0
  135. package/clis/uiverse/preview.js +1 -0
  136. package/clis/web/read.js +319 -81
  137. package/clis/web/read.test.js +221 -5
  138. package/clis/weibo/favorites.js +169 -0
  139. package/clis/weibo/favorites.test.js +114 -0
  140. package/clis/weibo/publish.js +282 -0
  141. package/clis/weibo/publish.test.js +183 -0
  142. package/clis/weixin/create-draft.js +225 -0
  143. package/clis/weixin/drafts.js +65 -0
  144. package/clis/weixin/drafts.test.js +65 -0
  145. package/clis/weread/ranking.js +1 -1
  146. package/clis/weread/search-regression.test.js +8 -8
  147. package/clis/weread/search.js +1 -1
  148. package/clis/wikipedia/random.js +1 -1
  149. package/clis/wikipedia/search.js +1 -1
  150. package/clis/wikipedia/summary.js +1 -1
  151. package/clis/wikipedia/trending.js +1 -1
  152. package/clis/xianyu/chat.js +3 -3
  153. package/clis/xianyu/item.js +2 -2
  154. package/clis/xianyu/item.test.js +3 -3
  155. package/clis/xiaohongshu/search.js +17 -2
  156. package/clis/xiaohongshu/search.test.js +37 -1
  157. package/clis/xiaoyuzhou/download.js +1 -1
  158. package/clis/xiaoyuzhou/download.test.js +3 -3
  159. package/clis/xiaoyuzhou/episode.js +1 -1
  160. package/clis/xiaoyuzhou/podcast-episodes.js +1 -1
  161. package/clis/xiaoyuzhou/podcast-episodes.test.js +2 -2
  162. package/clis/xiaoyuzhou/podcast.js +1 -1
  163. package/clis/xiaoyuzhou/transcript.js +1 -1
  164. package/clis/xiaoyuzhou/transcript.test.js +5 -5
  165. package/clis/yollomi/models.js +1 -1
  166. package/clis/youtube/channel.js +24 -1
  167. package/clis/youtube/channel.test.js +59 -0
  168. package/clis/zhihu/answer.js +21 -162
  169. package/clis/zhihu/answer.test.js +26 -53
  170. package/clis/zhihu/collection.js +197 -0
  171. package/clis/zhihu/collection.test.js +290 -0
  172. package/clis/zhihu/collections.js +127 -0
  173. package/clis/zhihu/collections.test.js +182 -0
  174. package/clis/zhihu/comment.js +24 -305
  175. package/clis/zhihu/comment.test.js +31 -35
  176. package/clis/zhihu/favorite.js +44 -182
  177. package/clis/zhihu/favorite.test.js +30 -167
  178. package/clis/zhihu/follow.js +25 -56
  179. package/clis/zhihu/follow.test.js +20 -23
  180. package/clis/zhihu/like.js +22 -67
  181. package/clis/zhihu/like.test.js +19 -42
  182. package/clis/zhihu/search.js +3 -2
  183. package/clis/zhihu/write-shared.js +8 -1
  184. package/clis/zhihu/write-shared.test.js +1 -0
  185. package/clis/zlibrary/commands.test.js +75 -0
  186. package/clis/zlibrary/info.js +47 -0
  187. package/clis/zlibrary/search.js +46 -0
  188. package/clis/zlibrary/utils.js +136 -0
  189. package/dist/src/adapter-source.d.ts +11 -0
  190. package/dist/src/adapter-source.js +24 -0
  191. package/dist/src/adapter-source.test.js +29 -0
  192. package/dist/src/browser/base-page.d.ts +3 -1
  193. package/dist/src/browser/base-page.js +76 -1
  194. package/dist/src/browser/base-page.test.d.ts +1 -0
  195. package/dist/src/browser/base-page.test.js +74 -0
  196. package/dist/src/browser/bridge.d.ts +1 -0
  197. package/dist/src/browser/bridge.js +36 -9
  198. package/dist/src/browser/cdp.d.ts +1 -0
  199. package/dist/src/browser/cdp.js +3 -3
  200. package/dist/src/browser/daemon-client.d.ts +38 -4
  201. package/dist/src/browser/daemon-client.js +24 -7
  202. package/dist/src/browser/daemon-client.test.js +49 -0
  203. package/dist/src/browser/errors.js +3 -0
  204. package/dist/src/browser/errors.test.js +3 -0
  205. package/dist/src/browser/network-cache.d.ts +1 -0
  206. package/dist/src/browser/page.d.ts +3 -1
  207. package/dist/src/browser/page.js +10 -2
  208. package/dist/src/browser/profile.d.ts +14 -0
  209. package/dist/src/browser/profile.js +85 -0
  210. package/dist/src/build-manifest.d.ts +2 -0
  211. package/dist/src/build-manifest.js +13 -3
  212. package/dist/src/build-manifest.test.js +20 -2
  213. package/dist/src/cli.d.ts +6 -0
  214. package/dist/src/cli.js +462 -32
  215. package/dist/src/cli.test.js +209 -2
  216. package/dist/src/commanderAdapter.js +29 -9
  217. package/dist/src/commanderAdapter.test.js +78 -2
  218. package/dist/src/commands/daemon.js +6 -0
  219. package/dist/src/completion-shared.js +1 -2
  220. package/dist/src/completion.test.js +3 -2
  221. package/dist/src/daemon.js +125 -41
  222. package/dist/src/doctor.d.ts +4 -6
  223. package/dist/src/doctor.js +80 -22
  224. package/dist/src/doctor.test.js +82 -0
  225. package/dist/src/engine.test.js +6 -5
  226. package/dist/src/errors.d.ts +14 -8
  227. package/dist/src/errors.js +36 -30
  228. package/dist/src/errors.test.js +5 -5
  229. package/dist/src/execution.d.ts +4 -0
  230. package/dist/src/execution.js +173 -25
  231. package/dist/src/execution.test.js +171 -1
  232. package/dist/src/main.js +10 -0
  233. package/dist/src/observation/artifact.d.ts +16 -0
  234. package/dist/src/observation/artifact.js +260 -0
  235. package/dist/src/observation/artifact.test.d.ts +1 -0
  236. package/dist/src/observation/artifact.test.js +121 -0
  237. package/dist/src/observation/events.d.ts +89 -0
  238. package/dist/src/observation/events.js +1 -0
  239. package/dist/src/observation/index.d.ts +7 -0
  240. package/dist/src/observation/index.js +7 -0
  241. package/dist/src/observation/manager.d.ts +9 -0
  242. package/dist/src/observation/manager.js +27 -0
  243. package/dist/src/observation/manager.test.d.ts +1 -0
  244. package/dist/src/observation/manager.test.js +13 -0
  245. package/dist/src/observation/redaction.d.ts +11 -0
  246. package/dist/src/observation/redaction.js +81 -0
  247. package/dist/src/observation/redaction.test.d.ts +1 -0
  248. package/dist/src/observation/redaction.test.js +32 -0
  249. package/dist/src/observation/retention.d.ts +32 -0
  250. package/dist/src/observation/retention.js +160 -0
  251. package/dist/src/observation/retention.test.d.ts +1 -0
  252. package/dist/src/observation/retention.test.js +118 -0
  253. package/dist/src/observation/ring-buffer.d.ts +22 -0
  254. package/dist/src/observation/ring-buffer.js +45 -0
  255. package/dist/src/observation/ring-buffer.test.d.ts +1 -0
  256. package/dist/src/observation/ring-buffer.test.js +22 -0
  257. package/dist/src/observation/session.d.ts +25 -0
  258. package/dist/src/observation/session.js +50 -0
  259. package/dist/src/pipeline/executor.test.js +1 -0
  260. package/dist/src/pipeline/steps/download.test.js +1 -0
  261. package/dist/src/pipeline/steps/fetch.js +1 -21
  262. package/dist/src/pipeline/steps/fetch.test.js +6 -12
  263. package/dist/src/plugin-scaffold.js +1 -1
  264. package/dist/src/plugin-scaffold.test.js +1 -1
  265. package/dist/src/registry.d.ts +40 -9
  266. package/dist/src/registry.js +3 -1
  267. package/dist/src/runtime-detect.d.ts +10 -0
  268. package/dist/src/runtime-detect.js +19 -0
  269. package/dist/src/runtime-detect.test.js +12 -1
  270. package/dist/src/runtime.d.ts +2 -0
  271. package/dist/src/runtime.js +1 -0
  272. package/dist/src/types.d.ts +22 -0
  273. package/dist/src/update-check.d.ts +31 -1
  274. package/dist/src/update-check.js +62 -16
  275. package/dist/src/update-check.test.js +86 -1
  276. package/package.json +1 -1
  277. package/dist/src/diagnostic.d.ts +0 -63
  278. package/dist/src/diagnostic.js +0 -292
  279. package/dist/src/diagnostic.test.js +0 -302
  280. /package/dist/src/{diagnostic.test.d.ts → adapter-source.test.d.ts} +0 -0
@@ -15,8 +15,9 @@ import { BasePage } from './base-page.js';
15
15
  */
16
16
  export declare class Page extends BasePage {
17
17
  private readonly workspace;
18
+ readonly contextId?: string | undefined;
18
19
  private readonly _idleTimeout;
19
- constructor(workspace?: string, idleTimeout?: number);
20
+ constructor(workspace?: string, idleTimeout?: number, contextId?: string | undefined);
20
21
  /** Active page identity (targetId), set after navigate and used in all subsequent commands */
21
22
  private _page;
22
23
  private _networkCaptureUnsupported;
@@ -28,6 +29,7 @@ export declare class Page extends BasePage {
28
29
  goto(url: string, options?: {
29
30
  waitUntil?: 'load' | 'none';
30
31
  settleMs?: number;
32
+ allowBoundNavigation?: boolean;
31
33
  }): Promise<void>;
32
34
  /** Get the active page identity (targetId) */
33
35
  getActivePage(): string | undefined;
@@ -27,10 +27,12 @@ function isUnsupportedNetworkCaptureError(err) {
27
27
  */
28
28
  export class Page extends BasePage {
29
29
  workspace;
30
+ contextId;
30
31
  _idleTimeout;
31
- constructor(workspace = 'default', idleTimeout) {
32
+ constructor(workspace = 'default', idleTimeout, contextId) {
32
33
  super();
33
34
  this.workspace = workspace;
35
+ this.contextId = contextId;
34
36
  this._idleTimeout = idleTimeout;
35
37
  }
36
38
  /** Active page identity (targetId), set after navigate and used in all subsequent commands */
@@ -39,12 +41,17 @@ export class Page extends BasePage {
39
41
  _networkCaptureWarned = false;
40
42
  /** Helper: spread workspace into command params */
41
43
  _wsOpt() {
42
- return { workspace: this.workspace, ...(this._idleTimeout != null && { idleTimeout: this._idleTimeout }) };
44
+ return {
45
+ workspace: this.workspace,
46
+ ...(this.contextId && { contextId: this.contextId }),
47
+ ...(this._idleTimeout != null && { idleTimeout: this._idleTimeout }),
48
+ };
43
49
  }
44
50
  /** Helper: spread workspace + page identity into command params */
45
51
  _cmdOpts() {
46
52
  return {
47
53
  workspace: this.workspace,
54
+ ...(this.contextId && { contextId: this.contextId }),
48
55
  ...(this._page !== undefined && { page: this._page }),
49
56
  ...(this._idleTimeout != null && { idleTimeout: this._idleTimeout }),
50
57
  };
@@ -53,6 +60,7 @@ export class Page extends BasePage {
53
60
  const result = await sendCommandFull('navigate', {
54
61
  url,
55
62
  ...this._cmdOpts(),
63
+ ...(options?.allowBoundNavigation === true && { allowBoundNavigation: true }),
56
64
  });
57
65
  // Remember the page identity (targetId) for subsequent calls
58
66
  if (result.page) {
@@ -0,0 +1,14 @@
1
+ export declare const DEFAULT_CONTEXT_ID = "default";
2
+ export type ProfileConfig = {
3
+ version: 1;
4
+ defaultContextId?: string;
5
+ aliases: Record<string, string>;
6
+ };
7
+ export declare function normalizeContextId(value: string | undefined | null): string | undefined;
8
+ export declare function emptyProfileConfig(): ProfileConfig;
9
+ export declare function loadProfileConfig(): ProfileConfig;
10
+ export declare function saveProfileConfig(config: ProfileConfig): void;
11
+ export declare function resolveProfileContextId(profile?: string): string | undefined;
12
+ export declare function aliasForContextId(config: ProfileConfig, contextId: string): string | undefined;
13
+ export declare function renameProfile(contextId: string, alias: string): ProfileConfig;
14
+ export declare function setDefaultProfile(profile: string): ProfileConfig;
@@ -0,0 +1,85 @@
1
+ import * as fs from 'node:fs';
2
+ import * as os from 'node:os';
3
+ import * as path from 'node:path';
4
+ export const DEFAULT_CONTEXT_ID = 'default';
5
+ function profileConfigPath() {
6
+ const baseDir = process.env.OPENCLI_CONFIG_DIR || path.join(os.homedir(), '.opencli');
7
+ return path.join(baseDir, 'browser-profiles.json');
8
+ }
9
+ export function normalizeContextId(value) {
10
+ const trimmed = value?.trim();
11
+ return trimmed || undefined;
12
+ }
13
+ export function emptyProfileConfig() {
14
+ return { version: 1, aliases: {} };
15
+ }
16
+ export function loadProfileConfig() {
17
+ try {
18
+ const raw = fs.readFileSync(profileConfigPath(), 'utf-8');
19
+ const parsed = JSON.parse(raw);
20
+ const aliases = parsed.aliases && typeof parsed.aliases === 'object'
21
+ ? Object.fromEntries(Object.entries(parsed.aliases).filter((entry) => {
22
+ const [key, value] = entry;
23
+ return typeof key === 'string' && key.trim().length > 0
24
+ && typeof value === 'string' && value.trim().length > 0;
25
+ }))
26
+ : {};
27
+ return {
28
+ version: 1,
29
+ aliases,
30
+ ...(typeof parsed.defaultContextId === 'string' && parsed.defaultContextId.trim()
31
+ ? { defaultContextId: parsed.defaultContextId.trim() }
32
+ : {}),
33
+ };
34
+ }
35
+ catch {
36
+ return emptyProfileConfig();
37
+ }
38
+ }
39
+ export function saveProfileConfig(config) {
40
+ const target = profileConfigPath();
41
+ fs.mkdirSync(path.dirname(target), { recursive: true });
42
+ fs.writeFileSync(target, JSON.stringify(config, null, 2) + '\n', 'utf-8');
43
+ }
44
+ export function resolveProfileContextId(profile) {
45
+ const config = loadProfileConfig();
46
+ const requested = normalizeContextId(profile)
47
+ ?? normalizeContextId(process.env.OPENCLI_PROFILE)
48
+ ?? normalizeContextId(config.defaultContextId);
49
+ if (!requested)
50
+ return undefined;
51
+ return config.aliases[requested] ?? requested;
52
+ }
53
+ export function aliasForContextId(config, contextId) {
54
+ for (const [alias, id] of Object.entries(config.aliases)) {
55
+ if (id === contextId)
56
+ return alias;
57
+ }
58
+ return undefined;
59
+ }
60
+ export function renameProfile(contextId, alias) {
61
+ const normalizedContextId = normalizeContextId(contextId);
62
+ const normalizedAlias = normalizeContextId(alias);
63
+ if (!normalizedContextId)
64
+ throw new Error('profile contextId is required');
65
+ if (!normalizedAlias)
66
+ throw new Error('profile alias is required');
67
+ const config = loadProfileConfig();
68
+ for (const [existingAlias, existingContextId] of Object.entries(config.aliases)) {
69
+ if (existingAlias !== normalizedAlias && existingContextId === normalizedContextId) {
70
+ delete config.aliases[existingAlias];
71
+ }
72
+ }
73
+ config.aliases[normalizedAlias] = normalizedContextId;
74
+ saveProfileConfig(config);
75
+ return config;
76
+ }
77
+ export function setDefaultProfile(profile) {
78
+ const contextId = resolveProfileContextId(profile) ?? normalizeContextId(profile);
79
+ if (!contextId)
80
+ throw new Error('profile is required');
81
+ const config = loadProfileConfig();
82
+ config.defaultContextId = contextId;
83
+ saveProfileConfig(config);
84
+ return config;
85
+ }
@@ -39,5 +39,7 @@ export interface ManifestEntry {
39
39
  /** Pre-navigation control — see CliCommand.navigateBefore */
40
40
  navigateBefore?: boolean | string;
41
41
  }
42
+ export declare function normalizeManifestPath(relativePath: string): string;
42
43
  export declare function loadManifestEntries(filePath: string, site: string, importer?: (moduleHref: string) => Promise<unknown>): Promise<ManifestEntry[]>;
43
44
  export declare function buildManifest(): Promise<ManifestEntry[]>;
45
+ export declare function serializeManifest(manifest: ManifestEntry[]): string;
@@ -36,6 +36,12 @@ function toModulePath(filePath, site) {
36
36
  const baseName = path.basename(filePath, path.extname(filePath));
37
37
  return `${site}/${baseName}.js`;
38
38
  }
39
+ export function normalizeManifestPath(relativePath) {
40
+ return relativePath.replace(/\\/g, '/');
41
+ }
42
+ function toManifestRelativePath(filePath) {
43
+ return normalizeManifestPath(path.relative(CLIS_DIR, filePath));
44
+ }
39
45
  function isCliCommandValue(value, site) {
40
46
  return isRecord(value)
41
47
  && typeof value.site === 'string'
@@ -85,8 +91,9 @@ export async function loadManifestEntries(filePath, site, importer = moduleHref
85
91
  return !previous || previous !== cmd;
86
92
  })
87
93
  .map(([, cmd]) => cmd);
88
- // Resolve sourceFile relative to clis/.
89
- const sourceRelative = path.relative(CLIS_DIR, filePath);
94
+ // Manifest paths are cross-platform artifacts; keep them POSIX-style even
95
+ // when build-manifest runs on Windows.
96
+ const sourceRelative = toManifestRelativePath(filePath);
90
97
  const seen = new Set();
91
98
  return runtimeCommands
92
99
  .filter((cmd) => {
@@ -128,10 +135,13 @@ export async function buildManifest() {
128
135
  }
129
136
  return [...manifest.values()].sort((a, b) => a.site.localeCompare(b.site) || a.name.localeCompare(b.name));
130
137
  }
138
+ export function serializeManifest(manifest) {
139
+ return `${JSON.stringify(manifest, null, 2)}\n`;
140
+ }
131
141
  async function main() {
132
142
  const manifest = await buildManifest();
133
143
  fs.mkdirSync(path.dirname(OUTPUT), { recursive: true });
134
- fs.writeFileSync(OUTPUT, JSON.stringify(manifest, null, 2));
144
+ fs.writeFileSync(OUTPUT, serializeManifest(manifest));
135
145
  console.log(`✅ Manifest compiled: ${manifest.length} entries → ${OUTPUT}`);
136
146
  // Restore executable permissions on bin entries.
137
147
  // tsc does not preserve the +x bit, so after a clean rebuild the CLI
@@ -3,7 +3,7 @@ import * as fs from 'node:fs';
3
3
  import * as os from 'node:os';
4
4
  import * as path from 'node:path';
5
5
  import { cli, getRegistry, Strategy } from './registry.js';
6
- import { loadManifestEntries } from './build-manifest.js';
6
+ import { loadManifestEntries, normalizeManifestPath, serializeManifest } from './build-manifest.js';
7
7
  describe('manifest helper rules', () => {
8
8
  const tempDirs = [];
9
9
  afterEach(() => {
@@ -76,8 +76,9 @@ describe('manifest helper rules', () => {
76
76
  replacedBy: 'opencli demo new',
77
77
  },
78
78
  ]);
79
- // Verify sourceFile is included
79
+ // Verify sourceFile is included and stable for manifest consumers.
80
80
  expect(entries[0].sourceFile).toBeDefined();
81
+ expect(entries[0].sourceFile).not.toContain('\\');
81
82
  getRegistry().delete(key);
82
83
  });
83
84
  it('falls back to registry delta for side-effect-only cli modules', async () => {
@@ -139,4 +140,21 @@ describe('manifest helper rules', () => {
139
140
  getRegistry().delete(screenKey);
140
141
  getRegistry().delete(statusKey);
141
142
  });
143
+ it('normalizes manifest paths to POSIX separators', () => {
144
+ expect(normalizeManifestPath('demo\\status.js')).toBe('demo/status.js');
145
+ expect(normalizeManifestPath('demo/status.js')).toBe('demo/status.js');
146
+ });
147
+ it('serializes manifest json with a trailing newline', () => {
148
+ const serialized = serializeManifest([{
149
+ site: 'demo',
150
+ name: 'status',
151
+ description: '',
152
+ strategy: 'public',
153
+ browser: false,
154
+ args: [],
155
+ type: 'js',
156
+ }]);
157
+ expect(serialized.endsWith('\n')).toBe(true);
158
+ expect(serialized).toContain('\n]');
159
+ });
142
160
  });
package/dist/src/cli.d.ts CHANGED
@@ -6,6 +6,12 @@
6
6
  */
7
7
  import { Command } from 'commander';
8
8
  import { findPackageRoot } from './package-paths.js';
9
+ export declare function selectFreshByTimestamp<T extends {
10
+ timestamp?: unknown;
11
+ }>(items: T[], lastSeenTs: number): {
12
+ fresh: T[];
13
+ lastSeenTs: number;
14
+ };
9
15
  /**
10
16
  * Check whether the site-memory scaffolding exists under
11
17
  * ~/.opencli/sites/<site>/. Agents have a strong tendency to forget to write