publishport-opencli 1.0.1 → 1.0.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.
- package/README.md +61 -61
- package/README.zh-CN.md +68 -68
- package/cli-manifest.json +502 -59
- package/clis/1688/shared.js +2 -2
- package/clis/36kr/news.js +1 -1
- package/clis/_atlassian/shared.js +1 -1
- package/clis/_shared/article/auth.js +1 -1
- package/clis/_shared/article/publish.js +1 -1
- package/clis/_shared/content-guard.js +1 -1
- package/clis/_shared/self-hosted-sites.js +2 -0
- package/clis/_shared/site-auth.js +1 -1
- package/clis/_shared/token-auth.js +2 -2
- package/clis/_shared/video-publish.js +1 -1
- package/clis/aibase/news.js +1 -1
- package/clis/amazon/shared.js +2 -2
- package/clis/antigravity/SKILL.md +12 -12
- package/clis/antigravity/storage.js +1 -1
- package/clis/apple-podcasts/episodes.js +1 -1
- package/clis/archive/item.js +1 -1
- package/clis/archive/search.js +1 -1
- package/clis/archive/snapshots.js +1 -1
- package/clis/archive/wayback.js +1 -1
- package/clis/arxiv/author.js +1 -1
- package/clis/baijiahao/publish.js +1 -1
- package/clis/bbc/utils.js +1 -1
- package/clis/bloomberg/businessweek.js +1 -1
- package/clis/bloomberg/utils.js +1 -1
- package/clis/booking/search.js +1 -1
- package/clis/boss/utils.js +1 -1
- package/clis/chatgpt/ask.js +1 -1
- package/clis/chatgpt/detail.js +1 -1
- package/clis/chatgpt/history.js +1 -1
- package/clis/chatgpt/project-file-add.js +1 -1
- package/clis/chatgpt/project-list.js +1 -1
- package/clis/chatgpt/utils.js +2 -2
- package/clis/chess/utils.js +1 -1
- package/clis/claude/ask.js +1 -1
- package/clis/claude/history.js +1 -1
- package/clis/claude/utils.js +1 -1
- package/clis/codex/extract-diff.js +1 -1
- package/clis/coingecko/coin.js +1 -1
- package/clis/crates/utils.js +1 -1
- package/clis/csdn/stats.js +1 -1
- package/clis/dblp/utils.js +1 -1
- package/clis/defillama/utils.js +1 -1
- package/clis/devto/publish.js +1 -1
- package/clis/discord-app/utils.js +1 -1
- package/clis/dockerhub/utils.js +1 -1
- package/clis/douban/utils.js +1 -1
- package/clis/douyin/_shared/tos-upload.js +1 -1
- package/clis/douyin/hashtag.js +1 -1
- package/clis/douyin/publish-image.js +1 -1
- package/clis/douyin/publish.js +1 -1
- package/clis/eastmoney/announcement.js +1 -1
- package/clis/eastmoney/rank.js +1 -1
- package/clis/eastmoney/sectors.js +1 -1
- package/clis/endoflife/utils.js +1 -1
- package/clis/flathub/utils.js +1 -1
- package/clis/gemini/detail.js +1 -1
- package/clis/gemini/read.js +1 -1
- package/clis/geogebra/add-circle.js +1 -1
- package/clis/geogebra/add-line.js +1 -1
- package/clis/geogebra/add-point.js +1 -1
- package/clis/geogebra/add-polygon.js +1 -1
- package/clis/geogebra/eval.js +1 -1
- package/clis/geogebra/hexagon.js +1 -1
- package/clis/geogebra/info.js +1 -1
- package/clis/geogebra/triangle.js +1 -1
- package/clis/ghost/login.js +1 -0
- package/clis/ghost/publish.js +1 -1
- package/clis/ghost/shared.js +24 -1
- package/clis/ghost/sites.js +1 -0
- package/clis/ghost/whoami.js +1 -1
- package/clis/gitee/user.js +2 -2
- package/clis/github-trending/repos.js +1 -1
- package/clis/goproxy/utils.js +1 -1
- package/clis/grok/export-all.js +1 -1
- package/clis/grok/export.js +1 -1
- package/clis/hashnode/publish.js +1 -1
- package/clis/hf/datasets.js +1 -1
- package/clis/hf/models.js +1 -1
- package/clis/hf/paper.js +1 -1
- package/clis/hf/spaces.js +1 -1
- package/clis/homebrew/utils.js +1 -1
- package/clis/instagram/note.js +1 -1
- package/clis/jimeng/generate.js +1 -1
- package/clis/juejin/utils.js +1 -1
- package/clis/kuaishou/publish.js +1 -1
- package/clis/lesswrong/tag.js +1 -1
- package/clis/lichess/utils.js +1 -1
- package/clis/linkedin/search.js +2 -2
- package/clis/linux-do/feed.js +2 -2
- package/clis/lobsters/domain.js +1 -1
- package/clis/mastodon/login.js +1 -0
- package/clis/mastodon/post.js +1 -1
- package/clis/mastodon/shared.js +10 -10
- package/clis/mastodon/sites.js +1 -0
- package/clis/mastodon/whoami.js +1 -1
- package/clis/maven/utils.js +1 -1
- package/clis/mdn/search.js +1 -1
- package/clis/medium/tag.js +1 -1
- package/clis/notebooklm/current.js +1 -1
- package/clis/notebooklm/get.js +1 -1
- package/clis/notebooklm/history.js +1 -1
- package/clis/notebooklm/note-list.js +1 -1
- package/clis/notebooklm/notes-get.js +1 -1
- package/clis/notebooklm/open.js +1 -1
- package/clis/notebooklm/source-fulltext.js +1 -1
- package/clis/notebooklm/source-get.js +1 -1
- package/clis/notebooklm/source-guide.js +1 -1
- package/clis/notebooklm/source-list.js +1 -1
- package/clis/notebooklm/summary.js +1 -1
- package/clis/notebooklm/utils.js +1 -1
- package/clis/npm/utils.js +1 -1
- package/clis/nuget/utils.js +1 -1
- package/clis/nvd/cve.js +1 -1
- package/clis/oeis/utils.js +1 -1
- package/clis/ones/common.js +2 -2
- package/clis/ones/me.js +1 -1
- package/clis/ones/tasks.js +1 -1
- package/clis/ones/token-info.js +1 -1
- package/clis/ones/worklog.js +1 -1
- package/clis/openalex/utils.js +1 -1
- package/clis/oschina/stats.js +1 -1
- package/clis/osv/utils.js +1 -1
- package/clis/packagist/utils.js +1 -1
- package/clis/pixiv/detail.js +1 -1
- package/clis/pixiv/user.js +1 -1
- package/clis/producthunt/utils.js +1 -1
- package/clis/pypi/utils.js +1 -1
- package/clis/qiita/gql.js +2 -2
- package/clis/rest-countries/utils.js +1 -1
- package/clis/rfc/utils.js +1 -1
- package/clis/rubygems/utils.js +1 -1
- package/clis/semanticscholar/utils.js +1 -1
- package/clis/slock/errors.js +1 -1
- package/clis/spotify/spotify.js +1 -1
- package/clis/spotify/utils.js +1 -1
- package/clis/stackoverflow/utils.js +1 -1
- package/clis/steam/utils.js +1 -1
- package/clis/substack/auth.js +2 -2
- package/clis/suno/generate.js +2 -2
- package/clis/suno/list.js +3 -3
- package/clis/telegram/send.js +1 -1
- package/clis/tiktok/creator-videos.js +1 -1
- package/clis/tiktok/publish.js +1 -1
- package/clis/tiktok/utils.js +1 -1
- package/clis/trae-cn/activity.js +1 -1
- package/clis/trae-cn/approve.js +1 -1
- package/clis/trae-cn/ask.js +1 -1
- package/clis/trae-cn/dump.js +1 -1
- package/clis/trae-cn/export.js +1 -1
- package/clis/trae-cn/model.js +1 -1
- package/clis/trae-cn/new.js +1 -1
- package/clis/trae-cn/read.js +1 -1
- package/clis/trae-cn/screenshot.js +1 -1
- package/clis/trae-cn/select-model.js +1 -1
- package/clis/trae-cn/send.js +1 -1
- package/clis/trae-cn/setup.js +1 -1
- package/clis/trae-cn/status.js +1 -1
- package/clis/trae-cn/targets.js +1 -1
- package/clis/trae-cn/watch.js +1 -1
- package/clis/trae-solo/state-fs.js +1 -1
- package/clis/tumblr/publish.js +1 -1
- package/clis/tvmaze/utils.js +1 -1
- package/clis/twitter/bookmark-folder.js +1 -1
- package/clis/twitter/download.js +1 -1
- package/clis/twitter/followers.js +2 -2
- package/clis/twitter/following.js +2 -2
- package/clis/twitter/likes.js +1 -1
- package/clis/twitter/list-add-batch.js +1 -1
- package/clis/twitter/list-add-core.js +1 -1
- package/clis/twitter/list-add.js +1 -1
- package/clis/twitter/list-create.js +1 -1
- package/clis/twitter/list-delete.js +3 -3
- package/clis/twitter/list-remove-batch.js +1 -1
- package/clis/twitter/list-remove.js +1 -1
- package/clis/twitter/list-tweets.js +1 -1
- package/clis/twitter/profile.js +1 -1
- package/clis/twitter/search.js +1 -1
- package/clis/twitter/shared.js +1 -1
- package/clis/twitter/tweets.js +1 -1
- package/clis/uisdc/news.js +1 -1
- package/clis/v2ex/daily.js +2 -2
- package/clis/v2ex/me.js +2 -2
- package/clis/v2ex/notifications.js +1 -1
- package/clis/wikidata/utils.js +1 -1
- package/clis/wikipedia/page.js +2 -2
- package/clis/wikipedia/summary.js +1 -1
- package/clis/wikipedia/utils.js +1 -1
- package/clis/wordpress/login.js +1 -1
- package/clis/wordpress/publish.js +1 -1
- package/clis/wordpress/shared.js +3 -3
- package/clis/wordpress/sites.js +1 -0
- package/clis/wordpress/whoami.js +1 -1
- package/clis/woshipm/stats.js +1 -1
- package/clis/xiaoe/content.js +1 -1
- package/clis/xiaohongshu/draft-delete.js +1 -1
- package/clis/xiaohongshu/draft-open.js +1 -1
- package/clis/xiaohongshu/draft-utils.js +2 -2
- package/clis/xiaohongshu/publish-video.js +1 -1
- package/clis/xiaohongshu/publish.js +1 -1
- package/clis/xiaoyuzhou/auth.js +1 -1
- package/clis/xiaoyuzhou/transcript.js +1 -1
- package/clis/yollomi/background.js +1 -1
- package/clis/yollomi/edit.js +1 -1
- package/clis/yollomi/generate.js +1 -1
- package/clis/yollomi/try-on.js +1 -1
- package/clis/youtube/publish.js +1 -1
- package/clis/zhihu/answer-comments.js +1 -1
- package/clis/zhihu/answer-detail.js +1 -1
- package/clis/zhihu/collection.js +2 -2
- package/clis/zhihu/collections.js +1 -1
- package/clis/zhihu/question.js +1 -1
- package/clis/zhihu/search.js +1 -1
- package/clis/zhihu/target.js +1 -1
- package/clis/zhihu/user-arg.js +1 -1
- package/dist/src/adapter-shadow.js +1 -1
- package/dist/src/browser/analyze.js +1 -1
- package/dist/src/browser/article-extract.d.ts +1 -1
- package/dist/src/browser/base-page.js +3 -3
- package/dist/src/browser/cdp.js +1 -1
- package/dist/src/browser/daemon-client.d.ts +1 -1
- package/dist/src/browser/daemon-lifecycle.js +6 -6
- package/dist/src/browser/daemon-version.js +1 -1
- package/dist/src/browser/dom-snapshot.d.ts +1 -1
- package/dist/src/browser/dom-snapshot.js +2 -2
- package/dist/src/browser/errors.js +2 -2
- package/dist/src/browser/network-cache.js +1 -1
- package/dist/src/browser/profile.js +1 -1
- package/dist/src/browser/target-resolver.js +4 -4
- package/dist/src/browser/verify-fixture.js +1 -1
- package/dist/src/browser-session-lock.js +1 -1
- package/dist/src/build-manifest.d.ts +1 -1
- package/dist/src/build-manifest.js +1 -1
- package/dist/src/cli-argv-preprocess.d.ts +4 -4
- package/dist/src/cli-argv-preprocess.js +1 -1
- package/dist/src/cli.d.ts +1 -1
- package/dist/src/cli.js +19 -19
- package/dist/src/commanderAdapter.js +1 -1
- package/dist/src/commands/auth.js +2 -2
- package/dist/src/commands/daemon.d.ts +4 -4
- package/dist/src/commands/daemon.js +1 -1
- package/dist/src/completion-shared.js +12 -12
- package/dist/src/completion.d.ts +2 -2
- package/dist/src/constants.d.ts +1 -1
- package/dist/src/constants.js +1 -1
- package/dist/src/daemon-utils.d.ts +1 -1
- package/dist/src/daemon-utils.js +1 -1
- package/dist/src/daemon.d.ts +2 -2
- package/dist/src/daemon.js +1 -1
- package/dist/src/discovery.d.ts +7 -7
- package/dist/src/discovery.js +2 -2
- package/dist/src/doctor.d.ts +1 -1
- package/dist/src/doctor.js +6 -6
- package/dist/src/electron-apps.d.ts +1 -1
- package/dist/src/electron-apps.js +1 -1
- package/dist/src/errors.d.ts +2 -2
- package/dist/src/errors.js +1 -1
- package/dist/src/execution.js +1 -1
- package/dist/src/external.d.ts +1 -1
- package/dist/src/external.js +1 -1
- package/dist/src/help.d.ts +1 -1
- package/dist/src/help.js +3 -3
- package/dist/src/hooks.d.ts +1 -1
- package/dist/src/launcher.js +2 -2
- package/dist/src/logger.d.ts +1 -1
- package/dist/src/main.d.ts +1 -1
- package/dist/src/main.js +1 -1
- package/dist/src/observation/artifact.js +2 -2
- package/dist/src/package-paths.js +1 -1
- package/dist/src/package-paths.test.d.ts +1 -0
- package/dist/src/plugin-manifest.d.ts +12 -3
- package/dist/src/plugin-manifest.js +1 -1
- package/dist/src/plugin-scaffold.d.ts +2 -2
- package/dist/src/plugin-scaffold.js +9 -9
- package/dist/src/plugin.d.ts +4 -4
- package/dist/src/plugin.js +10 -10
- package/dist/src/rate-limit.js +1 -1
- package/dist/src/registry-api.d.ts +1 -1
- package/dist/src/runtime-detect.d.ts +1 -1
- package/dist/src/serialization.js +1 -1
- package/dist/src/skills.js +6 -6
- package/dist/src/user-runtime.d.ts +8 -0
- package/dist/src/user-runtime.js +1 -0
- package/dist/src/user-runtime.test.d.ts +1 -0
- package/dist/src/validate.js +1 -1
- package/dist/src/validate.test.d.ts +1 -1
- package/docs/guide/browser-bridge.md +23 -23
- package/docs/guide/electron-app-cli.md +3 -3
- package/docs/guide/exit-codes.md +3 -3
- package/docs/guide/extending-opencli.md +37 -37
- package/docs/guide/getting-started.md +19 -19
- package/docs/guide/installation.md +6 -6
- package/docs/guide/plugins.md +32 -32
- package/docs/guide/remote-orchestration.md +5 -5
- package/docs/guide/troubleshooting.md +9 -9
- package/package.json +3 -2
- package/scripts/fetch-adapters.js +6 -6
- package/scripts/postinstall.js +14 -14
- package/skills/opencli-adapter-author/SKILL.md +24 -24
- package/skills/opencli-adapter-author/references/adapter-template.md +13 -13
- package/skills/opencli-adapter-author/references/api-discovery.md +25 -25
- package/skills/opencli-adapter-author/references/coverage-matrix.md +3 -3
- package/skills/opencli-adapter-author/references/field-decode-playbook.md +10 -10
- package/skills/opencli-adapter-author/references/jsdom-fixture-pattern.md +2 -2
- package/skills/opencli-adapter-author/references/site-memory.md +11 -11
- package/skills/opencli-adapter-author/references/site-recon.md +7 -7
- package/skills/opencli-adapter-author/references/strategy-selection.md +3 -3
- package/skills/opencli-adapter-author/references/success-rate-pitfalls.md +4 -4
- package/skills/opencli-adapter-author/references/typed-errors.md +2 -2
- package/skills/opencli-autofix/SKILL.md +29 -29
- package/skills/opencli-browser/SKILL.md +55 -55
- package/skills/opencli-browser-sitemap/SKILL.md +7 -7
- package/skills/opencli-sitemap-author/SKILL.md +11 -11
- package/skills/opencli-sitemap-author/references/sitemap-schema.md +19 -19
- package/skills/opencli-usage/SKILL.md +43 -43
package/clis/mastodon/post.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import*as
|
|
1
|
+
import*as v from"node:fs";import*as D from"node:path";import{randomUUID as U}from"node:crypto";import{cli as X,Strategy as B}from"@jackwener/opencli/registry";import{ArgumentError as $,CommandExecutionError as F}from"@jackwener/opencli/errors";import{assertLiteralContent as P}from"../_shared/content-guard.js";import{apiErrorText as Y,mastoFormRequest as y,readAccessToken as T,resolveInstance as _,uploadMedia as b}from"./shared.js";const C=4,V=["public","unlisted","private","direct"],L={".jpg":"image/jpeg",".jpeg":"image/jpeg",".png":"image/png",".gif":"image/gif",".webp":"image/webp",".mp4":"video/mp4",".mov":"video/quicktime",".m4v":"video/mp4"};function A(J){const z=String(J).split(",").map((K)=>K.trim()).filter(Boolean);if(z.length>C)throw new $(`媒体过多: ${z.length}(上限 ${C})`);return z.map((K)=>{const G=D.resolve(K),Q=D.extname(G).toLowerCase(),N=L[Q];if(!N)throw new $(`不支持的媒体格式 "${Q}"。支持: ${Object.keys(L).join(", ")}`);const W=v.statSync(G,{throwIfNoEntry:!1});if(!W||!W.isFile())throw new $(`不是有效文件: ${G}`);return{name:D.basename(G),mime:N,base64:v.readFileSync(G).toString("base64")}})}X({site:"mastodon",name:"post",access:"write",description:"发一条嘟文(status)到你登录的 Mastodon 实例。零配置:凭证从浏览器登录态自动获取,无需 API key。"+" 用 --site 选择已保存连接(也可直接传实例网址)。支持图片/视频、可见范围、内容警告、回复。",domain:"mastodon.social",strategy:B.UI,browser:!0,navigateBefore:!1,args:[{name:"status",type:"string",positional:!0,help:"嘟文正文(纯文本发帖必填;仅发媒体时可省)"},{name:"site",type:"string",required:!1,help:"连接名称或实例网址;省略时使用默认站点"},{name:"instance",type:"string",required:!1,help:"兼容旧版:实例网址(推荐改用 --site)"},{name:"media",type:"string",required:!1,help:"媒体路径,逗号分隔,最多 4 个(jpg/png/gif/webp/mp4/mov)"},{name:"visibility",type:"string",default:"public",choices:V,help:"可见范围(默认 public)"},{name:"spoiler",type:"string",required:!1,help:"内容警告文本(CW);设了会自动标记为敏感"},{name:"sensitive",type:"boolean",required:!1,help:"把媒体标记为敏感内容"},{name:"language",type:"string",required:!1,help:"ISO 639 语言码,如 zh、en"},{name:"reply-to",type:"string",required:!1,help:"回复的目标 status id(in_reply_to_id)"}],columns:["status","id","url","visibility"],func:async(J,z)=>{if(!J)throw new F("mastodon post 需要浏览器会话");const{url:K}=_(z),G=String(z.status??"").trim();P(G,{label:"嘟文正文"});const Q=z.media?A(z.media):[];if(!G&&Q.length===0)throw new $("嘟文为空:请传正文,或用 --media 附带媒体。");const N=String(z.visibility??"public");if(!V.includes(N))throw new $(`--visibility 只能是 ${V.join("/")}`);const{token:W}=await T(J,K),j=[];for(const O of Q){const{id:S}=await b(J,{token:W,file:O});j.push(S)}const H=[];if(G)H.push(["status",G]);H.push(["visibility",N]);for(const O of j)H.push(["media_ids[]",O]);if(z.spoiler)H.push(["spoiler_text",String(z.spoiler)]);if(z.sensitive||z.spoiler)H.push(["sensitive","true"]);if(z.language)H.push(["language",String(z.language)]);if(z["reply-to"])H.push(["in_reply_to_id",String(z["reply-to"])]);const Z=await y(J,{path:"/api/v1/statuses",method:"POST",token:W,form:H,headers:{"Idempotency-Key":U()}});if(!Z.ok){const O=Y(Z.json)||Z.text?.slice(0,200)||"";throw new F(`发嘟失败(HTTP ${Z.httpStatus})${O?": "+O:""}`)}const q=Z.json,R=String(q?.id??"").trim();if(!R)throw new F("发嘟响应缺少 status id;请回查后再决定是否重试。");return[{status:"success",id:R,url:q?.url??q?.uri??`${K}/@me/${R}`,visibility:q?.visibility??N}]}});
|
package/clis/mastodon/shared.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{AuthRequiredError as X,CommandExecutionError as W}from"@jackwener/opencli/errors";import{loadSiteConnections as $,normalizeSiteUrl as v,resolveSiteConnection as D}from"../_shared/self-hosted-sites.js";export function normalizeInstanceUrl(B){return v(B,{originOnly:!0,label:"Mastodon 实例网址"})}export function resolveInstance(B={}){return D("mastodon",B,{selector:"site",legacySelectors:["instance"],originOnly:!0})}export function configuredInstances(){return $("mastodon").sites}export async function readCurrentAccessToken(B){return B.evaluate(`(() => {
|
|
2
2
|
const el = document.getElementById('initial-state');
|
|
3
3
|
if (!el) return { ok: false, error: 'no-initial-state' };
|
|
4
4
|
let state;
|
|
@@ -8,13 +8,13 @@ import{ArgumentError as W,AuthRequiredError as Y,CommandExecutionError as X}from
|
|
|
8
8
|
const token = meta && meta.access_token;
|
|
9
9
|
if (!token) return { ok: false, error: 'no-access-token' };
|
|
10
10
|
return { ok: true, token: token, version: (meta && meta.version) || null };
|
|
11
|
-
})()`);if(!
|
|
11
|
+
})()`)}export async function readAccessToken(B,H){await B.goto(`${H}/home`,{waitUntil:"load",settleMs:800});const J=await readCurrentAccessToken(B);if(!J?.ok||!J.token)throw new X(H,`未在 ${H} 检测到登录态(${J?.error??"unknown"});请先在浏览器登录该实例。`);return{token:J.token,version:J.version??null}}export async function mastoFormRequest(B,{path:H,method:J="GET",token:L,form:G=[],headers:K={}}){const Q=G.length>0?new URLSearchParams(G).toString():"",V={Authorization:`Bearer ${L}`,Accept:"application/json",...Q?{"Content-Type":"application/x-www-form-urlencoded"}:{},...K};return O(B,{path:H,method:J,headers:V,body:Q||null})}export async function uploadMedia(B,{token:H,file:J,description:L}){const G=await I(B,{path:"/api/v2/media",token:H,file:J,description:L??null});if(G.httpStatus!==200&&G.httpStatus!==202)throw new W(`媒体上传失败(HTTP ${G.httpStatus}): ${apiErrorText(G.json)||G.text?.slice(0,200)||""}`);const K=String(G.json?.id??"").trim();if(!K)throw new W("媒体上传响应缺少 attachment id");if(G.httpStatus===200&&G.json?.url)return{id:K,url:G.json.url};const Q=1000,V=60000,Z=Date.now()+V;while(Date.now()<Z){await B.wait(1);const N=await mastoFormRequest(B,{path:`/api/v1/media/${K}`,method:"GET",token:H});if(N.httpStatus===200&&N.json?.url)return{id:K,url:N.json.url};if(N.httpStatus!==200&&N.httpStatus!==206)throw new W(`媒体处理查询失败(HTTP ${N.httpStatus})`)}return{id:K,url:null}}export function apiErrorText(B){if(!B||typeof B!=="object")return"";if(typeof B.error==="string")return B.error;if(B.error_description)return String(B.error_description);return""}async function O(B,{path:H,method:J,headers:L,body:G}){const K=await B.evaluate(`(async () => {
|
|
12
12
|
try {
|
|
13
|
-
const r = await fetch(${JSON.stringify(
|
|
14
|
-
method: ${JSON.stringify(
|
|
13
|
+
const r = await fetch(${JSON.stringify(H)}, {
|
|
14
|
+
method: ${JSON.stringify(J)},
|
|
15
15
|
headers: ${JSON.stringify(L)},
|
|
16
16
|
credentials: 'include',
|
|
17
|
-
body: ${
|
|
17
|
+
body: ${G===null?"undefined":JSON.stringify(G)},
|
|
18
18
|
});
|
|
19
19
|
const text = await r.text();
|
|
20
20
|
let json = null;
|
|
@@ -23,7 +23,7 @@ import{ArgumentError as W,AuthRequiredError as Y,CommandExecutionError as X}from
|
|
|
23
23
|
} catch (e) {
|
|
24
24
|
return { httpStatus: 0, ok: false, json: null, text: String(e && e.message || e) };
|
|
25
25
|
}
|
|
26
|
-
})()`);return
|
|
26
|
+
})()`);return Y(K,H)}async function I(B,{path:H,token:J,file:L,description:G}){const K=await B.evaluate(`(async () => {
|
|
27
27
|
try {
|
|
28
28
|
const f = ${JSON.stringify(L)};
|
|
29
29
|
const bin = atob(f.base64);
|
|
@@ -31,10 +31,10 @@ import{ArgumentError as W,AuthRequiredError as Y,CommandExecutionError as X}from
|
|
|
31
31
|
for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
|
|
32
32
|
const fd = new FormData();
|
|
33
33
|
fd.append('file', new File([bytes], f.name, { type: f.mime }), f.name);
|
|
34
|
-
${
|
|
35
|
-
const r = await fetch(${JSON.stringify(
|
|
34
|
+
${G?`fd.append('description', ${JSON.stringify(String(G))});`:""}
|
|
35
|
+
const r = await fetch(${JSON.stringify(H)}, {
|
|
36
36
|
method: 'POST',
|
|
37
|
-
headers: { 'Authorization': 'Bearer ' + ${JSON.stringify(
|
|
37
|
+
headers: { 'Authorization': 'Bearer ' + ${JSON.stringify(J)}, 'Accept': 'application/json' },
|
|
38
38
|
credentials: 'include',
|
|
39
39
|
body: fd,
|
|
40
40
|
});
|
|
@@ -45,4 +45,4 @@ import{ArgumentError as W,AuthRequiredError as Y,CommandExecutionError as X}from
|
|
|
45
45
|
} catch (e) {
|
|
46
46
|
return { httpStatus: 0, ok: false, json: null, text: String(e && e.message || e) };
|
|
47
47
|
}
|
|
48
|
-
})()`);return
|
|
48
|
+
})()`);return Y(K,H)}function Y(B,H){if(B?.httpStatus===401||B?.httpStatus===403)throw new X("mastodon",`Mastodon API ${H} 返回 HTTP ${B.httpStatus}(登录态失效或缺 write scope)`);return{httpStatus:B?.httpStatus??0,ok:!!B?.ok,json:B?.json??null,text:B?.text??""}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{cli as n}from"@jackwener/opencli/registry";import{connectionCommandDefinitions as i}from"../_shared/self-hosted-sites.js";for(const o of i("mastodon",{originOnly:!0}))n(o);
|
package/clis/mastodon/whoami.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{cli as r,Strategy as c}from"@jackwener/opencli/registry";import{AuthRequiredError as
|
|
1
|
+
import{cli as r,Strategy as c}from"@jackwener/opencli/registry";import{AuthRequiredError as l}from"@jackwener/opencli/errors";import{configuredInstances as u,mastoFormRequest as m,readAccessToken as d,resolveInstance as f}from"./shared.js";async function g(t){const s=u();for(const e of s){const a=await t.getCookies({url:e.url});if(Array.isArray(a)&&a.some((n)=>n?.value&&n.name==="_mastodon_session"))return{logged_in:!0}}return{logged_in:!1}}r({site:"mastodon",name:"whoami",access:"read",description:"检测 Mastodon 实例的登录状态。用 --site 选择已保存连接(也可直接传实例网址)。",domain:"mastodon.social",strategy:c.UI,browser:!0,navigateBefore:!1,args:[{name:"site",type:"string",required:!1,help:"连接名称或实例网址;省略时使用默认站点"},{name:"instance",type:"string",required:!1,help:"兼容旧版:实例网址(推荐改用 --site)"}],columns:["logged_in","account","display_name","instance"],authStatus:{quickCheck:g},func:async(t,s)=>{const{url:e}=f(s);let a;try{({token:a}=await d(t,e))}catch(i){if(i instanceof l)return[{logged_in:!1,account:"",display_name:"",instance:e}];throw i}const n=await m(t,{path:"/api/v1/accounts/verify_credentials",method:"GET",token:a});if(!n.ok||!n.json?.username)return[{logged_in:!1,account:"",display_name:"",instance:e}];const o=n.json;return[{logged_in:!0,account:o.acct||o.username||"",display_name:o.display_name||"",instance:e}]}});
|
package/clis/maven/utils.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{ArgumentError as J,CommandExecutionError as L,EmptyResultError as Q}from"@jackwener/opencli/errors";export const MAVEN_BASE="https://search.maven.org/solrsearch/select",MAVEN_REPO_BASE="https://repo1.maven.org/maven2";const W="opencli-maven-adapter (+https://github.com/jackwener/
|
|
1
|
+
import{ArgumentError as J,CommandExecutionError as L,EmptyResultError as Q}from"@jackwener/opencli/errors";export const MAVEN_BASE="https://search.maven.org/solrsearch/select",MAVEN_REPO_BASE="https://repo1.maven.org/maven2";const W="opencli-maven-adapter (+https://github.com/jackwener/OpenCLI)",P=/^[A-Za-z0-9][A-Za-z0-9._-]*$/;export function requireString(B,q){const z=String(B??"").trim();if(!z)throw new J(`maven ${q} cannot be empty`);return z}export function requireBoundedInt(B,q,z,G="limit"){const F=B??q,H=typeof F==="number"?F:Number(F);if(!Number.isInteger(H)||H<=0)throw new J(`maven ${G} must be a positive integer`);if(H>z)throw new J(`maven ${G} must be <= ${z}`);return H}export function requireCoord(B){const q=String(B??"").trim();if(!q)throw new J('maven coordinate is required (e.g. "com.fasterxml.jackson.core:jackson-databind")');const z=q.split(":");if(z.length<2||z.length>3)throw new J(`maven coordinate "${B}" must be "groupId:artifactId" or "groupId:artifactId:version"`);const[G,F,H]=z;if(!G||!F)throw new J(`maven coordinate "${B}" is missing groupId or artifactId`);if(G.length>200||!P.test(G))throw new J(`maven groupId "${G}" is not a valid token`,'Use letters / digits / "_-." (max 200 chars), starting with a letter or digit.');if(F.length>200||!P.test(F))throw new J(`maven artifactId "${F}" is not a valid token`,'Use letters / digits / "_-." (max 200 chars), starting with a letter or digit.');if(H!=null&&H.length>200)throw new J(`maven version "${H}" is too long (max 200 chars).`);return{groupId:G,artifactId:F,version:H??null}}export async function mavenFetch(B,q){let z;try{z=await fetch(B,{headers:{"user-agent":W,accept:"application/json"}})}catch(F){throw new L(`${q} request failed: ${F?.message??F}`,"Check that search.maven.org is reachable from this network.")}if(z.status===404)throw new Q(q,`Maven Central returned 404 for ${B}.`);if(z.status===429)throw new L(`${q} returned HTTP 429 (rate limited)`,"Maven Central throttles bursts; wait a few seconds and retry.");if(!z.ok)throw new L(`${q} returned HTTP ${z.status}`);let G;try{G=await z.json()}catch(F){throw new L(`${q} returned malformed JSON: ${F?.message??F}`)}return G}export function epochMsToIso(B){if(B==null)return null;const q=typeof B==="number"?B:Number(B);if(!Number.isFinite(q)||q<=0)return null;return new Date(q).toISOString().replace(/\.\d+Z$/,"Z")}
|
package/clis/mdn/search.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{cli as p,Strategy as w}from"@jackwener/opencli/registry";import{ArgumentError as i,CommandExecutionError as l,EmptyResultError as f}from"@jackwener/opencli/errors";const u="https://developer.mozilla.org",y="opencli-mdn-adapter (+https://github.com/jackwener/
|
|
1
|
+
import{cli as p,Strategy as w}from"@jackwener/opencli/registry";import{ArgumentError as i,CommandExecutionError as l,EmptyResultError as f}from"@jackwener/opencli/errors";const u="https://developer.mozilla.org",y="opencli-mdn-adapter (+https://github.com/jackwener/OpenCLI)",d=new Set(["en-US","de","es","fr","ja","ko","pt-BR","ru","zh-CN","zh-TW"]);function g(r,t){const n=String(r??"").trim();if(!n)throw new i(`mdn ${t} cannot be empty`);return n}function S(r,t,n){const o=r??t,a=typeof o==="number"?o:Number(o);if(!Number.isInteger(a)||a<=0)throw new i("mdn limit must be a positive integer");if(a>n)throw new i(`mdn limit must be <= ${n}`);return a}function $(r){const t=String(r??"en-US").trim();if(!d.has(t))throw new i(`mdn locale "${r}" is not supported`,`Allowed locales: ${[...d].join(" / ")}`);return t}p({site:"mdn",name:"search",access:"read",description:"Search MDN Web Docs by keyword",domain:"developer.mozilla.org",strategy:w.PUBLIC,browser:!1,args:[{name:"query",positional:!0,required:!0,help:'Search keyword (e.g. "fetch", "flexbox", "Array.prototype.map")'},{name:"limit",type:"int",default:10,help:"Max results (1-50)"},{name:"locale",default:"en-US",help:"Doc locale (en-US default; de / es / fr / ja / ko / pt-BR / ru / zh-CN / zh-TW)"}],columns:["rank","title","slug","locale","summary","url"],func:async(r)=>{const t=g(r.query,"query"),n=S(r.limit,10,50),o=$(r.locale),a=`${u}/api/v1/search?q=${encodeURIComponent(t)}&locale=${encodeURIComponent(o)}&size=${n}`;let s;try{s=await fetch(a,{headers:{"user-agent":y,accept:"application/json"}})}catch(e){throw new l(`mdn search request failed: ${e?.message??e}`)}if(s.status===429)throw new l("mdn search returned HTTP 429 (rate limited)","MDN throttles bursty traffic; wait a few seconds and retry.");if(!s.ok)throw new l(`mdn search returned HTTP ${s.status}`);let c;try{c=await s.json()}catch(e){throw new l(`mdn search returned malformed JSON: ${e?.message??e}`)}const m=Array.isArray(c?.documents)?c.documents:[];if(!m.length)throw new f("mdn search",`No MDN results matched "${t}" (locale ${o}).`);return m.slice(0,n).map((e,h)=>({rank:h+1,title:String(e.title??""),slug:String(e.slug??""),locale:String(e.locale??o),summary:String(e.summary??"").replace(/\s+/g," ").trim(),url:e.mdn_url?`${u}${e.mdn_url}`:""}))}});
|
package/clis/medium/tag.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{cli as f,Strategy as w}from"@jackwener/opencli/registry";import{ArgumentError as o,CommandExecutionError as g,EmptyResultError as p}from"@jackwener/opencli/errors";const S=/^[a-z0-9][a-z0-9-]*$/i,x={"&":"&","<":"<",">":">",""":'"',"'":"'","'":"'"," ":" "};function s(t){return String(t??"").replace(/&#x([0-9a-fA-F]+);/g,(e,i)=>String.fromCodePoint(parseInt(i,16))).replace(/&#(\d+);/g,(e,i)=>String.fromCodePoint(parseInt(i,10))).replace(/&(amp|lt|gt|quot|apos|#39|nbsp);/g,(e)=>x[e]||e)}function m(t,e){const i=t.match(new RegExp(`<${e}[^>]*>\\s*<!\\[CDATA\\[([\\s\\S]*?)\\]\\]>\\s*<\\/${e}>`));if(i)return i[1];const n=t.match(new RegExp(`<${e}[^>]*>([\\s\\S]*?)<\\/${e}>`));return n?n[1]:""}function T(t){const e=[],i=/<category(?:[^>]*)>(?:<!\[CDATA\[([\s\S]*?)\]\]>|([\s\S]*?))<\/category>/g;let n;while((n=i.exec(t))!==null){const r=s((n[1]??n[2]??"").trim());if(r)e.push(r)}return e}function y(t){if(!t)return"";const e=new Date(t);if(Number.isNaN(e.getTime()))return"";return e.toISOString().slice(0,10)}function $(t){return s(String(t??"").replace(/<[^>]+>/g," ").replace(/\s+/g," ").trim())}function b(t){const e=String(t??"").trim().toLowerCase();if(!e)throw new o('medium tag is required (e.g. "programming", "javascript")');if(!S.test(e))throw new o(`medium tag "${t}" is not valid`,'Tags are lowercase alphanumeric, optionally hyphenated (e.g. "machine-learning").');return e}function C(t,e,i){const n=t??e,r=typeof n==="number"?n:Number(n);if(!Number.isInteger(r)||r<=0)throw new o("medium limit must be a positive integer");if(r>i)throw new o(`medium limit must be <= ${i}`);return r}f({site:"medium",name:"tag",access:"read",description:"Latest Medium articles tagged with a given keyword (RSS feed)",domain:"medium.com",strategy:w.PUBLIC,browser:!1,args:[{name:"tag",positional:!0,required:!0,help:'Lowercase tag slug (e.g. "programming", "machine-learning")'},{name:"limit",type:"int",default:20,help:"Max articles (1-25 — single RSS page)"}],columns:["rank","title","author","description","categories","published","url"],func:async(t)=>{const e=b(t.tag),i=C(t.limit,20,25),n=`https://medium.com/feed/tag/${e}`;let r;try{r=await fetch(n,{headers:{"user-agent":"opencli-medium-adapter (+https://github.com/jackwener/
|
|
1
|
+
import{cli as f,Strategy as w}from"@jackwener/opencli/registry";import{ArgumentError as o,CommandExecutionError as g,EmptyResultError as p}from"@jackwener/opencli/errors";const S=/^[a-z0-9][a-z0-9-]*$/i,x={"&":"&","<":"<",">":">",""":'"',"'":"'","'":"'"," ":" "};function s(t){return String(t??"").replace(/&#x([0-9a-fA-F]+);/g,(e,i)=>String.fromCodePoint(parseInt(i,16))).replace(/&#(\d+);/g,(e,i)=>String.fromCodePoint(parseInt(i,10))).replace(/&(amp|lt|gt|quot|apos|#39|nbsp);/g,(e)=>x[e]||e)}function m(t,e){const i=t.match(new RegExp(`<${e}[^>]*>\\s*<!\\[CDATA\\[([\\s\\S]*?)\\]\\]>\\s*<\\/${e}>`));if(i)return i[1];const n=t.match(new RegExp(`<${e}[^>]*>([\\s\\S]*?)<\\/${e}>`));return n?n[1]:""}function T(t){const e=[],i=/<category(?:[^>]*)>(?:<!\[CDATA\[([\s\S]*?)\]\]>|([\s\S]*?))<\/category>/g;let n;while((n=i.exec(t))!==null){const r=s((n[1]??n[2]??"").trim());if(r)e.push(r)}return e}function y(t){if(!t)return"";const e=new Date(t);if(Number.isNaN(e.getTime()))return"";return e.toISOString().slice(0,10)}function $(t){return s(String(t??"").replace(/<[^>]+>/g," ").replace(/\s+/g," ").trim())}function b(t){const e=String(t??"").trim().toLowerCase();if(!e)throw new o('medium tag is required (e.g. "programming", "javascript")');if(!S.test(e))throw new o(`medium tag "${t}" is not valid`,'Tags are lowercase alphanumeric, optionally hyphenated (e.g. "machine-learning").');return e}function C(t,e,i){const n=t??e,r=typeof n==="number"?n:Number(n);if(!Number.isInteger(r)||r<=0)throw new o("medium limit must be a positive integer");if(r>i)throw new o(`medium limit must be <= ${i}`);return r}f({site:"medium",name:"tag",access:"read",description:"Latest Medium articles tagged with a given keyword (RSS feed)",domain:"medium.com",strategy:w.PUBLIC,browser:!1,args:[{name:"tag",positional:!0,required:!0,help:'Lowercase tag slug (e.g. "programming", "machine-learning")'},{name:"limit",type:"int",default:20,help:"Max articles (1-25 — single RSS page)"}],columns:["rank","title","author","description","categories","published","url"],func:async(t)=>{const e=b(t.tag),i=C(t.limit,20,25),n=`https://medium.com/feed/tag/${e}`;let r;try{r=await fetch(n,{headers:{"user-agent":"opencli-medium-adapter (+https://github.com/jackwener/OpenCLI)",accept:"application/rss+xml, application/xml"}})}catch(a){throw new g(`medium tag request failed: ${a?.message??a}`,"Check that medium.com is reachable from this network.")}if(r.status===404)throw new p("medium tag",`Medium tag "${e}" does not exist.`);if(!r.ok)throw new g(`medium tag returned HTTP ${r.status}`);const d=await r.text(),u=[],l=/<item[^>]*>([\s\S]*?)<\/item>/g;let c;while((c=l.exec(d))!==null)u.push(c[1]);if(!u.length)throw new p("medium tag",`Medium tag "${e}" RSS feed has no items.`);return u.slice(0,i).map((a,h)=>({rank:h+1,title:s(m(a,"title")).trim(),author:s(m(a,"dc:creator")).trim(),description:$(m(a,"description")),categories:T(a).join(", "),published:y(s(m(a,"pubDate")).trim()),url:s(m(a,"link")).trim()}))}});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{cli as r,Strategy as n}from"@jackwener/opencli/registry";import{EmptyResultError as e}from"@jackwener/opencli/errors";import{NOTEBOOKLM_DOMAIN as a,NOTEBOOKLM_SITE as i}from"./shared.js";import{getNotebooklmPageState as s,readCurrentNotebooklm as m,requireNotebooklmSession as c}from"./utils.js";r({site:i,name:"current",access:"read",description:"Show metadata for the currently opened NotebookLM notebook tab",domain:a,strategy:n.COOKIE,browser:!0,navigateBefore:!1,args:[],columns:["id","title","url","source"],func:async(o)=>{await c(o);if((await s(o)).kind!=="notebook")throw new e("
|
|
1
|
+
import{cli as r,Strategy as n}from"@jackwener/opencli/registry";import{EmptyResultError as e}from"@jackwener/opencli/errors";import{NOTEBOOKLM_DOMAIN as a,NOTEBOOKLM_SITE as i}from"./shared.js";import{getNotebooklmPageState as s,readCurrentNotebooklm as m,requireNotebooklmSession as c}from"./utils.js";r({site:i,name:"current",access:"read",description:"Show metadata for the currently opened NotebookLM notebook tab",domain:a,strategy:n.COOKIE,browser:!0,navigateBefore:!1,args:[],columns:["id","title","url","source"],func:async(o)=>{await c(o);if((await s(o)).kind!=="notebook")throw new e("ppcli notebooklm current","No NotebookLM notebook is open in the adapter session. Run `ppcli notebooklm open <notebook>` first.");const t=await m(o);if(!t)throw new e("ppcli notebooklm current","NotebookLM notebook metadata was not found on the current page.");return[t]}});
|
package/clis/notebooklm/get.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{cli as
|
|
1
|
+
import{cli as a,Strategy as n}from"@jackwener/opencli/registry";import{EmptyResultError as r}from"@jackwener/opencli/errors";import{NOTEBOOKLM_DOMAIN as i,NOTEBOOKLM_SITE as s}from"./shared.js";import{getNotebooklmDetailViaRpc as c,getNotebooklmPageState as l,readCurrentNotebooklm as u,requireNotebooklmSession as m}from"./utils.js";a({site:s,name:"get",access:"read",aliases:["metadata"],description:"Get rich metadata for the currently opened NotebookLM notebook",domain:i,strategy:n.COOKIE,browser:!0,navigateBefore:!1,args:[],columns:["id","title","emoji","source_count","created_at","updated_at","url","source"],func:async(o)=>{await m(o);if((await l(o)).kind!=="notebook")throw new r("ppcli notebooklm get","No NotebookLM notebook is open in the adapter session. Run `ppcli notebooklm open <notebook>` first.");const t=await c(o).catch(()=>null);if(t)return[t];const e=await u(o);if(!e)throw new r("ppcli notebooklm get","NotebookLM notebook metadata was not found on the current page.");return[{...e,emoji:null,source_count:null,updated_at:null}]}});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{cli as t,Strategy as e}from"@jackwener/opencli/registry";import{EmptyResultError as r}from"@jackwener/opencli/errors";import{NOTEBOOKLM_DOMAIN as i,NOTEBOOKLM_SITE as s}from"./shared.js";import{getNotebooklmPageState as n,listNotebooklmHistoryViaRpc as a,requireNotebooklmSession as m}from"./utils.js";t({site:s,name:"history",access:"read",description:"List NotebookLM conversation history threads in the current notebook",domain:i,strategy:e.COOKIE,browser:!0,navigateBefore:!1,args:[],columns:["thread_id","item_count","preview","source","notebook_id","url"],func:async(o)=>{await m(o);if((await n(o)).kind!=="notebook")throw new r("
|
|
1
|
+
import{cli as t,Strategy as e}from"@jackwener/opencli/registry";import{EmptyResultError as r}from"@jackwener/opencli/errors";import{NOTEBOOKLM_DOMAIN as i,NOTEBOOKLM_SITE as s}from"./shared.js";import{getNotebooklmPageState as n,listNotebooklmHistoryViaRpc as a,requireNotebooklmSession as m}from"./utils.js";t({site:s,name:"history",access:"read",description:"List NotebookLM conversation history threads in the current notebook",domain:i,strategy:e.COOKIE,browser:!0,navigateBefore:!1,args:[],columns:["thread_id","item_count","preview","source","notebook_id","url"],func:async(o)=>{await m(o);if((await n(o)).kind!=="notebook")throw new r("ppcli notebooklm history","No NotebookLM notebook is open in the adapter session. Run `ppcli notebooklm open <notebook>` first.");return await a(o)}});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{cli as
|
|
1
|
+
import{cli as r,Strategy as n}from"@jackwener/opencli/registry";import{EmptyResultError as e}from"@jackwener/opencli/errors";import{NOTEBOOKLM_DOMAIN as i,NOTEBOOKLM_SITE as s}from"./shared.js";import{getNotebooklmPageState as a,listNotebooklmNotesFromPage as l,requireNotebooklmSession as m}from"./utils.js";r({site:s,name:"note-list",access:"read",aliases:["notes-list"],description:"List saved notes from the Studio panel of the current NotebookLM notebook",domain:i,strategy:n.COOKIE,browser:!0,navigateBefore:!1,args:[],columns:["title","created_at","source","url"],func:async(o)=>{await m(o);if((await a(o)).kind!=="notebook")throw new e("ppcli notebooklm note-list","No NotebookLM notebook is open in the adapter session. Run `ppcli notebooklm open <notebook>` first.");const t=await l(o);if(t.length>0)return t;throw new e("ppcli notebooklm note-list","No NotebookLM notes are visible in the Studio panel. Reload the notebook page or close the note editor and retry.")}});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{cli as U,Strategy as W}from"@jackwener/opencli/registry";import{EmptyResultError as J}from"@jackwener/opencli/errors";import{NOTEBOOKLM_DOMAIN as X,NOTEBOOKLM_SITE as Y}from"./shared.js";import{findNotebooklmNoteRow as Z,getNotebooklmPageState as $,listNotebooklmNotesFromPage as c,readNotebooklmVisibleNoteFromPage as f,requireNotebooklmSession as h}from"./utils.js";function A(x,C){const G=C.trim().toLowerCase();if(!G)return!1;const j=x.trim().toLowerCase();return j===G||j.includes(G)}U({site:Y,name:"notes-get",access:"read",description:"Get one note from the current NotebookLM notebook by title from the visible note editor",domain:X,strategy:W.COOKIE,browser:!0,navigateBefore:!1,args:[{name:"note",positional:!0,required:!0,help:"Note title or id from the current notebook"}],columns:["title","content","source","url"],func:async(x,C)=>{await h(x);if((await $(x)).kind!=="notebook")throw new J("
|
|
1
|
+
import{cli as U,Strategy as W}from"@jackwener/opencli/registry";import{EmptyResultError as J}from"@jackwener/opencli/errors";import{NOTEBOOKLM_DOMAIN as X,NOTEBOOKLM_SITE as Y}from"./shared.js";import{findNotebooklmNoteRow as Z,getNotebooklmPageState as $,listNotebooklmNotesFromPage as c,readNotebooklmVisibleNoteFromPage as f,requireNotebooklmSession as h}from"./utils.js";function A(x,C){const G=C.trim().toLowerCase();if(!G)return!1;const j=x.trim().toLowerCase();return j===G||j.includes(G)}U({site:Y,name:"notes-get",access:"read",description:"Get one note from the current NotebookLM notebook by title from the visible note editor",domain:X,strategy:W.COOKIE,browser:!0,navigateBefore:!1,args:[{name:"note",positional:!0,required:!0,help:"Note title or id from the current notebook"}],columns:["title","content","source","url"],func:async(x,C)=>{await h(x);if((await $(x)).kind!=="notebook")throw new J("ppcli notebooklm notes-get","No NotebookLM notebook is open in the adapter session. Run `ppcli notebooklm open <notebook>` first.");const j=typeof C.note==="string"?C.note:String(C.note??""),H=await f(x);if(H&&A(H.title,j))return[H];const Q=await c(x);if(Z(Q,j))throw new J("ppcli notebooklm notes-get",`Note "${j}" is listed in Studio, but ppcli currently reads note content only from the visible note editor. Open that note in NotebookLM, then retry.`);throw new J("ppcli notebooklm notes-get",`Note "${j}" was not found in the current notebook.`)}});
|
package/clis/notebooklm/open.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{cli as i,Strategy as a}from"@jackwener/opencli/registry";import{CliError as s,EmptyResultError as b}from"@jackwener/opencli/errors";import{NOTEBOOKLM_DOMAIN as l,NOTEBOOKLM_SITE as k}from"./shared.js";import{buildNotebooklmNotebookUrl as d,getNotebooklmPageState as m,parseNotebooklmNotebookTarget as
|
|
1
|
+
import{cli as i,Strategy as a}from"@jackwener/opencli/registry";import{CliError as s,EmptyResultError as b}from"@jackwener/opencli/errors";import{NOTEBOOKLM_DOMAIN as l,NOTEBOOKLM_SITE as k}from"./shared.js";import{buildNotebooklmNotebookUrl as d,getNotebooklmPageState as m,parseNotebooklmNotebookTarget as p,readCurrentNotebooklm as u,requireNotebooklmSession as c}from"./utils.js";i({site:k,name:"open",access:"read",aliases:["select"],description:"Open one NotebookLM notebook in the adapter session by id or URL",domain:l,strategy:a.COOKIE,browser:!0,navigateBefore:!1,args:[{name:"notebook",positional:!0,required:!0,help:"Notebook id from list output, or a full NotebookLM notebook URL"}],columns:["id","title","url","source"],func:async(o,r)=>{const t=p(String(r.notebook??""));await o.goto(d(t));await o.wait(2);await c(o);const e=await m(o);if(e.kind!=="notebook")throw new s("NOTEBOOKLM_OPEN_FAILED",`NotebookLM notebook "${t}" did not open in the adapter session`,"Run `ppcli notebooklm list -f json` first and pass a valid notebook id.");if(e.notebookId!==t)console.warn(`[notebooklm open] expected notebook "${t}" but page reports "${e.notebookId}"; continuing`);const n=await u(o);if(!n)throw new b("ppcli notebooklm open","NotebookLM notebook metadata was not found after navigation.");return[n]}});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{cli as G,Strategy as H}from"@jackwener/opencli/registry";import{EmptyResultError as n}from"@jackwener/opencli/errors";import{NOTEBOOKLM_DOMAIN as I,NOTEBOOKLM_SITE as J}from"./shared.js";import{findNotebooklmSourceRow as K,getNotebooklmPageState as L,getNotebooklmSourceFulltextViaRpc as M,listNotebooklmSourcesFromPage as O,listNotebooklmSourcesViaRpc as P,requireNotebooklmSession as Q}from"./utils.js";G({site:J,name:"source-fulltext",access:"read",description:"Get the extracted fulltext for one source in the currently opened NotebookLM notebook",domain:I,strategy:H.COOKIE,browser:!0,navigateBefore:!1,args:[{name:"source",positional:!0,required:!0,help:"Source id or title from the current notebook"}],columns:["title","kind","char_count","url","source"],func:async(j,v)=>{await Q(j);if((await L(j)).kind!=="notebook")throw new n("
|
|
1
|
+
import{cli as G,Strategy as H}from"@jackwener/opencli/registry";import{EmptyResultError as n}from"@jackwener/opencli/errors";import{NOTEBOOKLM_DOMAIN as I,NOTEBOOKLM_SITE as J}from"./shared.js";import{findNotebooklmSourceRow as K,getNotebooklmPageState as L,getNotebooklmSourceFulltextViaRpc as M,listNotebooklmSourcesFromPage as O,listNotebooklmSourcesViaRpc as P,requireNotebooklmSession as Q}from"./utils.js";G({site:J,name:"source-fulltext",access:"read",description:"Get the extracted fulltext for one source in the currently opened NotebookLM notebook",domain:I,strategy:H.COOKIE,browser:!0,navigateBefore:!1,args:[{name:"source",positional:!0,required:!0,help:"Source id or title from the current notebook"}],columns:["title","kind","char_count","url","source"],func:async(j,v)=>{await Q(j);if((await L(j)).kind!=="notebook")throw new n("ppcli notebooklm source-fulltext","No NotebookLM notebook is open in the adapter session. Run `ppcli notebooklm open <notebook>` first.");const A=await P(j).catch(()=>[]),B=A.length>0?A:await O(j);if(B.length===0)throw new n("ppcli notebooklm source-fulltext","No NotebookLM sources were found on the current page.");const C=typeof v.source==="string"?v.source:String(v.source??""),z=K(B,C);if(!z)throw new n("ppcli notebooklm source-fulltext",`Source "${C}" was not found in the current notebook.`);const D=await M(j,z.id).catch(()=>null);if(D)return[D];throw new n("ppcli notebooklm source-fulltext",`NotebookLM fulltext was not available for source "${z.title}".`)}});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{cli as c,Strategy as u}from"@jackwener/opencli/registry";import{EmptyResultError as t}from"@jackwener/opencli/errors";import{NOTEBOOKLM_DOMAIN as a,NOTEBOOKLM_SITE as l}from"./shared.js";import{findNotebooklmSourceRow as m,getNotebooklmPageState as
|
|
1
|
+
import{cli as c,Strategy as u}from"@jackwener/opencli/registry";import{EmptyResultError as t}from"@jackwener/opencli/errors";import{NOTEBOOKLM_DOMAIN as a,NOTEBOOKLM_SITE as l}from"./shared.js";import{findNotebooklmSourceRow as m,getNotebooklmPageState as p,listNotebooklmSourcesFromPage as b,listNotebooklmSourcesViaRpc as d,requireNotebooklmSession as f}from"./utils.js";c({site:l,name:"source-get",access:"read",description:"Get one source from the currently opened NotebookLM notebook by id or title",domain:a,strategy:u.COOKIE,browser:!0,navigateBefore:!1,args:[{name:"source",positional:!0,required:!0,help:"Source id or title from the current notebook"}],columns:["title","id","type","size","created_at","updated_at","url","source"],func:async(o,e)=>{await f(o);if((await p(o)).kind!=="notebook")throw new t("ppcli notebooklm source-get","No NotebookLM notebook is open in the adapter session. Run `ppcli notebooklm open <notebook>` first.");const r=await d(o).catch(()=>[]),n=r.length>0?r:await b(o);if(n.length===0)throw new t("ppcli notebooklm source-get","No NotebookLM sources were found on the current page.");const i=typeof e.source==="string"?e.source:String(e.source??""),s=m(n,i);if(s)return[s];throw new t("ppcli notebooklm source-get",`Source "${i}" was not found in the current notebook.`)}});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{cli as u,Strategy as a}from"@jackwener/opencli/registry";import{EmptyResultError as e}from"@jackwener/opencli/errors";import{NOTEBOOKLM_DOMAIN as l,NOTEBOOKLM_SITE as m}from"./shared.js";import{findNotebooklmSourceRow as d,getNotebooklmPageState as b,getNotebooklmSourceGuideViaRpc as
|
|
1
|
+
import{cli as u,Strategy as a}from"@jackwener/opencli/registry";import{EmptyResultError as e}from"@jackwener/opencli/errors";import{NOTEBOOKLM_DOMAIN as l,NOTEBOOKLM_SITE as m}from"./shared.js";import{findNotebooklmSourceRow as d,getNotebooklmPageState as b,getNotebooklmSourceGuideViaRpc as p,listNotebooklmSourcesFromPage as k,listNotebooklmSourcesViaRpc as w,requireNotebooklmSession as f}from"./utils.js";u({site:m,name:"source-guide",access:"read",description:"Get the guide summary and keywords for one source in the currently opened NotebookLM notebook",domain:l,strategy:a.COOKIE,browser:!0,navigateBefore:!1,args:[{name:"source",positional:!0,required:!0,help:"Source id or title from the current notebook"}],columns:["source_id","notebook_id","title","type","summary","keywords","source"],func:async(o,t)=>{await f(o);if((await b(o)).kind!=="notebook")throw new e("ppcli notebooklm source-guide","No NotebookLM notebook is open in the adapter session. Run `ppcli notebooklm open <notebook>` first.");const i=await w(o).catch(()=>[]),n=i.length>0?i:await k(o);if(n.length===0)throw new e("ppcli notebooklm source-guide","No NotebookLM sources were found on the current page.");const s=typeof t.source==="string"?t.source:String(t.source??""),r=d(n,s);if(!r)throw new e("ppcli notebooklm source-guide",`Source "${s}" was not found in the current notebook.`);const c=await p(o,r).catch(()=>null);if(c)return[c];throw new e("ppcli notebooklm source-guide",`NotebookLM guide was not available for source "${r.title}".`)}});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{cli as s,Strategy as
|
|
1
|
+
import{cli as s,Strategy as i}from"@jackwener/opencli/registry";import{EmptyResultError as r}from"@jackwener/opencli/errors";import{NOTEBOOKLM_DOMAIN as n,NOTEBOOKLM_SITE as a}from"./shared.js";import{getNotebooklmPageState as c,listNotebooklmSourcesFromPage as l,listNotebooklmSourcesViaRpc as m,requireNotebooklmSession as u}from"./utils.js";s({site:a,name:"source-list",access:"read",description:"List sources for the currently opened NotebookLM notebook",domain:n,strategy:i.COOKIE,browser:!0,navigateBefore:!1,args:[],columns:["title","id","type","size","created_at","updated_at","url","source"],func:async(o)=>{await u(o);if((await c(o)).kind!=="notebook")throw new r("ppcli notebooklm source-list","No NotebookLM notebook is open in the adapter session. Run `ppcli notebooklm open <notebook>` first.");const t=await m(o).catch(()=>[]);if(t.length>0)return t;const e=await l(o);if(e.length>0)return e;throw new r("ppcli notebooklm source-list","No NotebookLM sources were found on the current page.")}});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{cli as m,Strategy as a}from"@jackwener/opencli/registry";import{EmptyResultError as r}from"@jackwener/opencli/errors";import{NOTEBOOKLM_DOMAIN as n,NOTEBOOKLM_SITE as s}from"./shared.js";import{getNotebooklmPageState as i,getNotebooklmSummaryViaRpc as u,readNotebooklmSummaryFromPage as c,requireNotebooklmSession as l}from"./utils.js";m({site:s,name:"summary",access:"read",description:"Get the summary block from the currently opened NotebookLM notebook",domain:n,strategy:a.COOKIE,browser:!0,navigateBefore:!1,args:[],columns:["title","summary","source","url"],func:async(o)=>{await l(o);if((await i(o)).kind!=="notebook")throw new r("
|
|
1
|
+
import{cli as m,Strategy as a}from"@jackwener/opencli/registry";import{EmptyResultError as r}from"@jackwener/opencli/errors";import{NOTEBOOKLM_DOMAIN as n,NOTEBOOKLM_SITE as s}from"./shared.js";import{getNotebooklmPageState as i,getNotebooklmSummaryViaRpc as u,readNotebooklmSummaryFromPage as c,requireNotebooklmSession as l}from"./utils.js";m({site:s,name:"summary",access:"read",description:"Get the summary block from the currently opened NotebookLM notebook",domain:n,strategy:a.COOKIE,browser:!0,navigateBefore:!1,args:[],columns:["title","summary","source","url"],func:async(o)=>{await l(o);if((await i(o)).kind!=="notebook")throw new r("ppcli notebooklm summary","No NotebookLM notebook is open in the adapter session. Run `ppcli notebooklm open <notebook>` first.");const t=await c(o);if(t)return[t];const e=await u(o).catch(()=>null);if(e)return[e];throw new r("ppcli notebooklm summary","NotebookLM summary was not found on the current page.")}});
|
package/clis/notebooklm/utils.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{ArgumentError as x,AuthRequiredError as E,CliError as C,CommandExecutionError as A}from"@jackwener/opencli/errors";import{NOTEBOOKLM_DOMAIN as $,NOTEBOOKLM_HOME_URL as P}from"./shared.js";import{callNotebooklmRpc as j,getNotebooklmPageAuth as z,unwrapNotebooklmEvaluateResult as B}from"./rpc.js";export{buildNotebooklmRpcBody,extractNotebooklmRpcResult,fetchNotebooklmInPage,getNotebooklmPageAuth,parseNotebooklmChunkedResponse,stripNotebooklmAntiXssi}from"./rpc.js";const S="wXbhsf",Y="rLM1Ne",y="hPTbtc",b="khqZz";function H(G){let F=G;while(Array.isArray(F)&&F.length===1&&Array.isArray(F[0]))F=F[0];return F}export function isPlainObject(G){return G!==null&&typeof G==="object"&&!Array.isArray(G)}export function parseNotebooklmIdFromUrl(G){return G.match(/\/notebook\/([^/?#]+)/)?.[1]??""}const h=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function N(G){if(!h.test(G))throw new C("NOTEBOOKLM_INVALID_NOTEBOOK",`NotebookLM notebook id "${G}" is not a valid UUID`,"Pass a notebook id from `
|
|
1
|
+
import{ArgumentError as x,AuthRequiredError as E,CliError as C,CommandExecutionError as A}from"@jackwener/opencli/errors";import{NOTEBOOKLM_DOMAIN as $,NOTEBOOKLM_HOME_URL as P}from"./shared.js";import{callNotebooklmRpc as j,getNotebooklmPageAuth as z,unwrapNotebooklmEvaluateResult as B}from"./rpc.js";export{buildNotebooklmRpcBody,extractNotebooklmRpcResult,fetchNotebooklmInPage,getNotebooklmPageAuth,parseNotebooklmChunkedResponse,stripNotebooklmAntiXssi}from"./rpc.js";const S="wXbhsf",Y="rLM1Ne",y="hPTbtc",b="khqZz";function H(G){let F=G;while(Array.isArray(F)&&F.length===1&&Array.isArray(F[0]))F=F[0];return F}export function isPlainObject(G){return G!==null&&typeof G==="object"&&!Array.isArray(G)}export function parseNotebooklmIdFromUrl(G){return G.match(/\/notebook\/([^/?#]+)/)?.[1]??""}const h=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function N(G){if(!h.test(G))throw new C("NOTEBOOKLM_INVALID_NOTEBOOK",`NotebookLM notebook id "${G}" is not a valid UUID`,"Pass a notebook id from `ppcli notebooklm list` or a full notebook URL like https://notebooklm.google.com/notebook/<uuid>.");return G}export function parseNotebooklmNotebookTarget(G){const F=G.trim();if(!F)throw new C("NOTEBOOKLM_INVALID_NOTEBOOK","NotebookLM notebook id is required","Pass a notebook id from `ppcli notebooklm list` or a full notebook URL.");if(/^https?:\/\//i.test(F)){let Q;try{Q=new URL(F)}catch{throw new C("NOTEBOOKLM_INVALID_NOTEBOOK","NotebookLM notebook URL is invalid","Pass a full NotebookLM notebook URL like https://notebooklm.google.com/notebook/<uuid>.")}if(Q.protocol!=="https:"||Q.hostname!==$||Q.username||Q.password||Q.port)throw new C("NOTEBOOKLM_INVALID_NOTEBOOK","NotebookLM notebook URL must be a canonical https://notebooklm.google.com URL","Pass a notebook id from `ppcli notebooklm list` or a full NotebookLM notebook URL.");const V=parseNotebooklmIdFromUrl(F);if(!V)throw new C("NOTEBOOKLM_INVALID_NOTEBOOK","NotebookLM notebook URL is invalid","Pass a full NotebookLM notebook URL like https://notebooklm.google.com/notebook/<uuid>.");return N(V)}const J=F.match(/(?:^|\/)notebook\/([^/?#]+)/);if(J?.[1])return N(J[1]);return N(F)}export function getNotebooklmAuthuser(){const G=process.env.OPENCLI_NOTEBOOKLM_AUTHUSER;return typeof G==="string"&&/^\d+$/.test(G)?G:""}export function requireNotebooklmExecute(G,F){if(G!==!0)throw new x(`Refusing to ${F}: pass --execute to perform this NotebookLM write`)}export function buildNotebooklmNotebookUrl(G){const F=new URL(`/notebook/${encodeURIComponent(G)}`,P),J=getNotebooklmAuthuser();if(J)F.searchParams.set("authuser",J);return F.toString()}export function classifyNotebooklmPage(G){try{const F=new URL(G);if(F.hostname!==$)return"unknown";if(/\/notebook\/[^/?#]+/.test(F.pathname))return"notebook";return"home"}catch{return"unknown"}}export function normalizeNotebooklmTitle(G,F=""){if(typeof G!=="string")return F;let J=G.replace(/\s+/g," ").trim();if(/^Untitled\b/i.test(J)&&/otebook$/i.test(J)&&J!=="Untitled notebook")J="Untitled notebook";return J||F}function k(G){if(typeof G!=="string")return null;const F=G.trim();if(!F)return null;const J=Date.parse(F);if(Number.isNaN(J))return F;return new Date(J).toISOString()}function K(G){if(typeof G==="number"&&Number.isFinite(G))try{return new Date(G*1000).toISOString()}catch{return null}if(Array.isArray(G)&&typeof G[0]==="number"&&Number.isFinite(G[0])){const F=G[0],J=typeof G[1]==="number"&&Number.isFinite(G[1])?G[1]:0;try{return new Date(F*1000+Math.floor(J/1e6)).toISOString()}catch{return null}}return null}function O(G){if(typeof G==="number"&&Number.isFinite(G))return G;if(!Array.isArray(G)||typeof G[1]!=="number"||!Number.isFinite(G[1]))return null;return G[1]}function U(G){const F=O(G);if(F===8)return"pasted-text";if(F===9)return"youtube";if(F===2)return"generated-text";if(F===3)return"pdf";if(F===4)return"audio";if(F===5)return"web";if(F===6)return"video";return F==null?null:`type-${F}`}function q(G){if(typeof G==="string"&&G.trim())return G.trim();if(!Array.isArray(G))return null;for(const F of G){const J=q(F);if(J)return J}return null}function _(G){return/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(G)}function M(G,F){if(typeof G==="string"){const J=normalizeNotebooklmTitle(G);if(!J)return F;if(_(J))return F;if(/^[\d\s]+$/.test(J))return F;if(/^(null|undefined)$/i.test(J))return F;F.push(J);return F}if(!Array.isArray(G))return F;for(const J of G)M(J,F);return F}function T(G,F){if(typeof G==="string"){const J=G.trim();if(J)F.push(J);return F}if(!Array.isArray(G))return F;for(const J of G)T(J,F);return F}function R(G,F,J){if(typeof G==="string"){const Q=G.trim();if(_(Q)&&!J.has(Q)){J.add(Q);F.push(Q)}return F}if(!Array.isArray(G))return F;for(const Q of G)R(Q,F,J);return F}export function parseNotebooklmHistoryThreadIdsResult(G){return R(G,[],new Set)}export function extractNotebooklmHistoryPreview(G){const F=M(G,[]);return F.length>0?F[0]:null}export function parseNotebooklmNoteListRawRows(G,F,J){return G.map((V)=>{const W=normalizeNotebooklmTitle(V.title,""),X=String(V.text??"").replace(/\bsticky_note_2\b/g," ").replace(/\bmore_vert\b/g," ").replace(/\s+/g," ").trim();if(!W)return null;const Z=X.startsWith(W)?X.slice(W.length).trim():X.replace(W,"").trim();return{notebook_id:F,title:W,created_at:Z||null,url:J,source:"studio-list"}}).filter((V)=>V!==null)}function I(G,F,J){const Q=normalizeNotebooklmTitle(G?.title,"Untitled Notebook"),V=String(G?.summary??"").trim();if(!V)return null;return{notebook_id:F,title:Q,summary:V,url:J,source:"summary-dom"}}function g(G,F,J){const Q=normalizeNotebooklmTitle(G?.title,""),V=String(G?.content??"").replace(/\r\n/g,`
|
|
2
2
|
`).trim();if(!Q)return null;return{notebook_id:F,id:null,title:Q,content:V,url:J,source:"studio-editor"}}export function parseNotebooklmListResult(G){if(!Array.isArray(G)||G.length===0)return[];const F=Array.isArray(G[0])?G[0]:G;if(!Array.isArray(F))return[];return F.filter((J)=>Array.isArray(J)).map((J)=>{const Q=Array.isArray(J[5])?J[5]:[],V=Array.isArray(Q[5])?Q[5]:[],W=typeof J[2]==="string"?J[2]:"",X=typeof J[0]==="string"?J[0].replace(/^thought\s*\n/,""):"";return{id:W,title:normalizeNotebooklmTitle(X,"Untitled Notebook"),url:`https://${$}/notebook/${W}`,source:"rpc",is_owner:Q.length>1?Q[1]===!1:!0,created_at:V.length>0?K(V[0]):null}}).filter((J)=>J.id)}export function parseNotebooklmNotebookDetailResult(G){const F=H(G);if(!Array.isArray(F)||F.length<3)return null;const J=typeof F[2]==="string"?F[2]:"";if(!J)return null;const Q=normalizeNotebooklmTitle(F[0],"Untitled Notebook"),V=typeof F[3]==="string"?F[3]:null,W=Array.isArray(F[5])?F[5]:[],X=Array.isArray(F[1])?F[1]:[];return{id:J,title:Q,url:`https://${$}/notebook/${J}`,source:"rpc",is_owner:W.length>1?W[1]===!1:!0,created_at:K(W[8]),updated_at:K(W[5]),emoji:V,source_count:X.length}}export function parseNotebooklmSourceListResult(G){const F=H(G),J=parseNotebooklmNotebookDetailResult(F);if(!J||!Array.isArray(F))return[];return(Array.isArray(F[1])?F[1]:[]).filter((V)=>Array.isArray(V)).map((V)=>{const W=q(V[0])??"",X=normalizeNotebooklmTitle(V[1],"Untitled source"),Z=Array.isArray(V[2])?V[2]:[],L=typeof Z[4]==="number"?Z[4]:V[3];return{id:W,notebook_id:J.id,title:X,url:J.url,source:"rpc",type:U(L),type_code:O(L),size:typeof Z[1]==="number"&&Number.isFinite(Z[1])?Z[1]:null,created_at:K(Z[2]),updated_at:K(Z[14])}}).filter((V)=>V.id)}export function parseNotebooklmSourceGuideResult(G,F){if(!Array.isArray(G)||G.length===0||!Array.isArray(G[0]))return null;const J=G[0],Q=Array.isArray(J)&&J.length>0&&Array.isArray(J[0])?J[0]:J;if(!Array.isArray(Q))return null;const V=Array.isArray(Q[1])&&typeof Q[1][0]==="string"?Q[1][0].trim():"",W=Array.isArray(Q[2])&&Array.isArray(Q[2][0])?Q[2][0].filter((X)=>typeof X==="string"&&X.trim().length>0):[];if(!V)return null;return{source_id:F.id,notebook_id:F.notebook_id,title:F.title,type:F.type??null,summary:V,keywords:W,source:"rpc"}}export function parseNotebooklmSourceFulltextResult(G,F,J){if(!Array.isArray(G)||G.length===0||!Array.isArray(G[0]))return null;const Q=G[0],V=q(Q[0])??"",W=normalizeNotebooklmTitle(Q[1],"Untitled source"),X=Array.isArray(Q[2])?Q[2]:[],Z=Array.isArray(X[7])&&typeof X[7][0]==="string"?X[7][0]:J,L=U([null,X[4]]),f=Array.isArray(G[3])&&G[3].length>0?G[3][0]:[],D=T(f,[]).join(`
|
|
3
3
|
`).trim();if(!V||!D)return null;return{source_id:V,notebook_id:F,title:W,kind:L,content:D,char_count:D.length,url:Z,source:"rpc"}}export function findNotebooklmSourceRow(G,F){const J=F.trim().toLowerCase();if(!J)return null;const Q=G.find((X)=>X.id.trim().toLowerCase()===J);if(Q)return Q;const V=G.find((X)=>X.title.trim().toLowerCase()===J);if(V)return V;const W=G.filter((X)=>X.title.trim().toLowerCase().includes(J));if(W.length===1)return W[0];return null}export function findNotebooklmNoteRow(G,F){const J=F.trim().toLowerCase();if(!J)return null;const Q=G.find((W)=>W.title.trim().toLowerCase()===J);if(Q)return Q;const V=G.filter((W)=>W.title.trim().toLowerCase().includes(J));if(V.length===1)return V[0];return null}export async function listNotebooklmViaRpc(G){const F=await j(G,S,[null,1,null,[2]]);return parseNotebooklmListResult(F.result)}export async function getNotebooklmDetailViaRpc(G){const F=await getNotebooklmPageState(G);if(F.kind!=="notebook"||!F.notebookId)return null;const J=await j(G,Y,[F.notebookId,null,[2],null,0]);return parseNotebooklmNotebookDetailResult(J.result)}export async function getNotebooklmNotebookDetailById(G,F){const J=await j(G,Y,[F,null,[2],null,0]);return{detail:parseNotebooklmNotebookDetailResult(J.result),sources:parseNotebooklmSourceListResult(J.result)}}export async function verifyNotebooklmNotebookExists(G,F,J){try{const{detail:Q}=await getNotebooklmNotebookDetailById(G,F);if(!Q||Q.id!==F)throw new A(`NotebookLM ${J} succeeded but the notebook ${F} was not found in the post-write verification`);return Q}catch(Q){if(Q instanceof E||Q instanceof A)throw Q;throw new A(`NotebookLM ${J} post-write verification failed: ${Q?.message||Q}`)}}export async function verifyNotebooklmSourceAdded(G,F,J,Q){try{const{detail:V,sources:W}=await getNotebooklmNotebookDetailById(G,F);if(!V||V.id!==F)throw new A(`NotebookLM ${Q} succeeded but the notebook ${F} was not found in the post-write verification`);const X=W.find((Z)=>Z.id===J);if(!X)throw new A(`NotebookLM ${Q} succeeded but source ${J} did not appear in the notebook's source list`);return X}catch(V){if(V instanceof E||V instanceof A)throw V;throw new A(`NotebookLM ${Q} post-write verification failed: ${V?.message||V}`)}}export async function listNotebooklmSourcesViaRpc(G){const F=await getNotebooklmPageState(G);if(F.kind!=="notebook"||!F.notebookId)return[];const J=await j(G,Y,[F.notebookId,null,[2],null,0]);return parseNotebooklmSourceListResult(J.result)}export async function listNotebooklmHistoryViaRpc(G){const F=await getNotebooklmPageState(G);if(F.kind!=="notebook"||!F.notebookId)return[];const J=await j(G,y,[[],null,F.notebookId,20]),Q=parseNotebooklmHistoryThreadIdsResult(J.result);if(Q.length===0)return[];const V=[];for(const W of Q){const X=await j(G,b,[[],null,null,W,20]);V.push({notebook_id:F.notebookId,thread_id:W,item_count:Array.isArray(X.result)?X.result.length:0,preview:extractNotebooklmHistoryPreview(X.result),url:F.url||`https://${$}/notebook/${F.notebookId}`,source:"rpc"})}return V}export async function listNotebooklmNotesFromPage(G){const F=await getNotebooklmPageState(G);if(F.kind!=="notebook"||!F.notebookId)return[];const J=B(await G.evaluate(`(() => {
|
|
4
4
|
return Array.from(document.querySelectorAll('artifact-library-note')).map((node) => {
|
package/clis/npm/utils.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{ArgumentError as F,CommandExecutionError as J,EmptyResultError as L}from"@jackwener/opencli/errors";export const NPM_REGISTRY="https://registry.npmjs.org",NPM_API="https://api.npmjs.org";const O="opencli-npm-adapter (+https://github.com/jackwener/
|
|
1
|
+
import{ArgumentError as F,CommandExecutionError as J,EmptyResultError as L}from"@jackwener/opencli/errors";export const NPM_REGISTRY="https://registry.npmjs.org",NPM_API="https://api.npmjs.org";const O="opencli-npm-adapter (+https://github.com/jackwener/OpenCLI)",Q=/^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/i;export function requireString(B,q){const z=String(B??"").trim();if(!z)throw new F(`npm ${q} cannot be empty`);return z}export function requirePackageName(B){const q=String(B??"").trim();if(!q)throw new F('npm package name is required (e.g. "react", "@vercel/og")');if(q.length>214)throw new F(`npm package name "${B}" is too long (max 214 chars)`);if(!Q.test(q))throw new F(`npm package name "${B}" is not a valid registry name`,'Names are 1–214 chars of lowercase a-z / 0-9 / "-._" (scoped form: "@scope/name").');return q}export function requireBoundedInt(B,q,z,H="limit"){const D=B??q,I=typeof D==="number"?D:Number(D);if(!Number.isInteger(I)||I<=0)throw new F(`npm ${H} must be a positive integer`);if(I>z)throw new F(`npm ${H} must be <= ${z}`);return I}export async function npmFetch(B,q){let z;try{z=await fetch(B,{headers:{"user-agent":O,accept:"application/json"}})}catch(D){throw new J(`${q} request failed: ${D?.message??D}`,"Check that registry.npmjs.org / api.npmjs.org are reachable from this network.")}if(z.status===404)throw new L(q,`npm registry returned 404 for ${B}.`);if(z.status===429)throw new J(`${q} returned HTTP 429 (rate limited)`,"npm throttles unauthenticated bursts; wait a few seconds and retry.");if(!z.ok)throw new J(`${q} returned HTTP ${z.status}`);let H;try{H=await z.json()}catch(D){throw new J(`${q} returned malformed JSON: ${D?.message??D}`)}return H}
|
package/clis/nuget/utils.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{ArgumentError as J,CommandExecutionError as M,EmptyResultError as O}from"@jackwener/opencli/errors";export const NUGET_SEARCH_BASE="https://azuresearch-usnc.nuget.org",NUGET_REGISTRATION_BASE="https://api.nuget.org/v3/registration5-semver1";const Q="opencli-nuget-adapter/1.0 (+https://github.com/jackwener/
|
|
1
|
+
import{ArgumentError as J,CommandExecutionError as M,EmptyResultError as O}from"@jackwener/opencli/errors";export const NUGET_SEARCH_BASE="https://azuresearch-usnc.nuget.org",NUGET_REGISTRATION_BASE="https://api.nuget.org/v3/registration5-semver1";const Q="opencli-nuget-adapter/1.0 (+https://github.com/jackwener/OpenCLI; mailto:ppcli@example.com)",S=/^[A-Za-z0-9](?:[A-Za-z0-9._-]{0,99})$/;export function requireString(z,q){const B=String(z??"").trim();if(!B)throw new J(`nuget ${q} cannot be empty`);return B}export function requireBoundedInt(z,q,B,H="limit"){const F=z??q,L=typeof F==="number"?F:Number(F);if(!Number.isInteger(L)||L<=0)throw new J(`nuget ${H} must be a positive integer`);if(L>B)throw new J(`nuget ${H} must be <= ${B}`);return L}export function requirePackageId(z){const q=String(z??"").trim();if(!q)throw new J('nuget package id is required (e.g. "Newtonsoft.Json")');if(!S.test(q))throw new J(`nuget package id "${z}" is not a valid NuGet identifier`,"NuGet IDs are 1-100 chars: letters/digits/`.`/`_`/`-`, starting with letter or digit.");return q}export async function nugetFetch(z,q){let B;try{B=await fetch(z,{headers:{"user-agent":Q,accept:"application/json"}})}catch(F){throw new M(`${q} request failed: ${F?.message??F}`,"Check that api.nuget.org is reachable from this network.")}if(B.status===404)throw new O(q,`NuGet returned 404 for ${z}.`);if(B.status===429)throw new M(`${q} returned HTTP 429 (rate limited)`);if(!B.ok)throw new M(`${q} returned HTTP ${B.status}`);let H;try{H=await B.json()}catch(F){throw new M(`${q} returned malformed JSON: ${F?.message??F}`)}return H}export function joinTags(z){if(!Array.isArray(z))return"";return z.filter((q)=>typeof q==="string"&&q.trim()).join(", ")}export function joinAuthors(z){if(Array.isArray(z))return z.filter((q)=>typeof q==="string"&&q.trim()).join(", ");if(typeof z==="string")return z.trim();return""}
|
package/clis/nvd/cve.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{cli as
|
|
1
|
+
import{cli as l,Strategy as u}from"@jackwener/opencli/registry";import{ArgumentError as v,CommandExecutionError as a,EmptyResultError as f}from"@jackwener/opencli/errors";const p="https://services.nvd.nist.gov/rest/json/cves/2.0",g="opencli-nvd-adapter (+https://github.com/jackwener/OpenCLI)",h=/^CVE-\d{4}-\d{4,}$/i;function y(e){const t=String(e??"").trim().toUpperCase();if(!t)throw new v('nvd CVE id is required (e.g. "CVE-2021-44228")');if(!h.test(t))throw new v(`nvd CVE id "${e}" is not a valid CVE identifier`,'Expected the form "CVE-YYYY-N..." with at least 4 sequence digits.');return t}function V(e){if(!Array.isArray(e))return"";const t=e.find((s)=>s?.lang==="en");return String(t?.value??e[0]?.value??"").trim()}function S(e){if(!e||typeof e!=="object")return null;const t=[...Array.isArray(e.cvssMetricV31)?e.cvssMetricV31:[],...Array.isArray(e.cvssMetricV30)?e.cvssMetricV30:[],...Array.isArray(e.cvssMetricV2)?e.cvssMetricV2:[]];return t.find((s)=>s?.type==="Primary")||t[0]||null}function w(e){if(!Array.isArray(e))return"";const t=new Set;for(const s of e)for(const i of s?.description??[])if(i?.value)t.add(String(i.value));return[...t].join(", ")}l({site:"nvd",name:"cve",access:"read",description:"NIST NVD CVE detail (description, CVSS, CWE, KEV flag)",domain:"services.nvd.nist.gov",strategy:u.PUBLIC,browser:!1,args:[{name:"id",positional:!0,required:!0,help:'CVE identifier (e.g. "CVE-2021-44228")'}],columns:["id","published","lastModified","vulnStatus","baseScore","severity","attackVector","cwe","kevAdded","description","url"],func:async(e)=>{const t=y(e.id),s=`${p}?cveId=${encodeURIComponent(t)}`;let i;try{i=await fetch(s,{headers:{"user-agent":g,accept:"application/json"}})}catch(n){throw new a(`nvd cve request failed: ${n?.message??n}`)}if(i.status===403)throw new a("nvd cve returned HTTP 403","NVD enforces aggressive rate limits without an API key. Wait, then retry or set NVD_API_KEY (not yet wired).");if(i.status===429)throw new a("nvd cve returned HTTP 429 (rate limited)","NVD throttles unauthenticated traffic; wait several seconds before retry.");if(!i.ok)throw new a(`nvd cve returned HTTP ${i.status}`);let c;try{c=await i.json()}catch(n){throw new a(`nvd cve returned malformed JSON: ${n?.message??n}`)}const r=(Array.isArray(c?.vulnerabilities)?c.vulnerabilities:[])[0]?.cve;if(!r||!r.id)throw new f("nvd cve",`NVD has no record for "${t}".`);const d=S(r.metrics),o=d?.cvssData??{};return[{id:String(r.id),published:String(r.published??"").slice(0,10),lastModified:String(r.lastModified??"").slice(0,10),vulnStatus:String(r.vulnStatus??""),baseScore:o.baseScore!=null?Number(o.baseScore):null,severity:String(o.baseSeverity??d?.baseSeverity??""),attackVector:String(o.attackVector??""),cwe:w(r.weaknesses),kevAdded:r.cisaExploitAdd?String(r.cisaExploitAdd).slice(0,10):"",description:V(r.descriptions),url:`https://nvd.nist.gov/vuln/detail/${r.id}`}]}});
|
package/clis/oeis/utils.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{ArgumentError as H,CommandExecutionError as K,EmptyResultError as L}from"@jackwener/opencli/errors";export const OEIS_BASE="https://oeis.org";const M="opencli-oeis-adapter/1.0 (+https://github.com/jackwener/
|
|
1
|
+
import{ArgumentError as H,CommandExecutionError as K,EmptyResultError as L}from"@jackwener/opencli/errors";export const OEIS_BASE="https://oeis.org";const M="opencli-oeis-adapter/1.0 (+https://github.com/jackwener/OpenCLI; mailto:ppcli@example.com)",W=/^A\d{1,7}$/;export function requireString(z,B){const q=String(z??"").trim();if(!q)throw new H(`oeis ${B} cannot be empty`);return q}export function requireBoundedInt(z,B,q,G="limit"){const F=z??B,J=typeof F==="number"?F:Number(F);if(!Number.isInteger(J)||J<=0)throw new H(`oeis ${G} must be a positive integer`);if(J>q)throw new H(`oeis ${G} must be <= ${q}`);return J}export function requireSequenceId(z){const B=String(z??"").trim().toUpperCase();if(!B)throw new H('oeis sequence id is required (e.g. "A000045" for Fibonacci)');const q=B.replace(/^HTTPS?:\/\/(?:WWW\.)?OEIS\.ORG\//,"").replace(/\/.*$/,"");if(!W.test(q))throw new H(`oeis sequence id "${z}" is not a valid A-number`,'Expected format: "A" + digits (e.g. "A000045").');return q}export async function oeisFetch(z,B){let q;try{q=await fetch(z,{headers:{"user-agent":M,accept:"application/json"}})}catch(F){throw new K(`${B} request failed: ${F?.message??F}`,"Check that oeis.org is reachable from this network.")}if(q.status===404)throw new L(B,`OEIS returned 404 for ${z}.`);if(q.status===429)throw new K(`${B} returned HTTP 429 (rate limited)`);if(!q.ok)throw new K(`${B} returned HTTP ${q.status}`);let G;try{G=await q.json()}catch(F){throw new K(`${B} returned malformed JSON: ${F?.message??F}`)}return G}export function formatId(z){if(typeof z!=="number"||!Number.isInteger(z)||z<0)return null;return`A${String(z).padStart(6,"0")}`}export function previewTerms(z,B=12){if(typeof z!=="string")return"";const q=z.split(",").map((G)=>G.trim()).filter(Boolean);if(q.length<=B)return q.join(", ");return[...q.slice(0,B),`(+${q.length-B})`].join(", ")}
|
package/clis/ones/common.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{CliError as Q}from"@jackwener/opencli/errors";export const API_PREFIX="/project/api/project";export function getOnesBaseUrl(){const D=process.env.ONES_BASE_URL?.trim().replace(/\/+$/,"");if(!D)throw new Q("CONFIG","Missing ONES_BASE_URL","Set ONES_BASE_URL to your deployment origin, e.g. https://your-team.ones.cn (no trailing slash).");return D}export function onesApiUrl(D){const K=getOnesBaseUrl(),q=D.replace(/^\/+/,"");return`${K}${API_PREFIX}/${q}`}export async function gotoOnesHome(D){await D.goto(getOnesBaseUrl(),{waitUntil:"load"});await D.wait(2)}function G(D,K){const x={Referer:getOnesBaseUrl()};if(D){const L=process.env.ONES_USER_ID?.trim()||process.env.ONES_USER_UUID?.trim()||process.env.Ones_User_Id?.trim(),N=process.env.ONES_AUTH_TOKEN?.trim()||process.env.Ones_Auth_Token?.trim();if(L&&N){x["Ones-User-Id"]=L;x["Ones-Auth-Token"]=N}}if(K)x["Content-Type"]="application/json";return x}export function summarizeOnesError(D,K){if(K&&typeof K==="object"){const q=K,x=[];if(typeof q.type==="string")x.push(q.type);if(typeof q.reason==="string")x.push(q.reason);if(typeof q.errcode==="string")x.push(q.errcode);if(typeof q.message==="string")x.push(q.message);if(q.code!==void 0&&q.code!==null)x.push(`code=${String(q.code)}`);if(x.length)return x.filter(Boolean).join(" · ")}return D===401?"Unauthorized":`HTTP ${D}`}function M(D,K){if(K===null||typeof K!=="object")return;const q=K;if(Array.isArray(q.groups))return;if(!(typeof q.reason==="string"&&q.reason.length>0||typeof q.errcode==="string"&&q.errcode.length>0||typeof q.type==="string"&&q.type.length>0))return;const L=summarizeOnesError(200,K);throw new Q("FETCH_ERROR",`ONES ${D}: ${L}`,"若 query 不合法会返回 ServerError;可试
|
|
1
|
+
import{CliError as Q}from"@jackwener/opencli/errors";export const API_PREFIX="/project/api/project";export function getOnesBaseUrl(){const D=process.env.ONES_BASE_URL?.trim().replace(/\/+$/,"");if(!D)throw new Q("CONFIG","Missing ONES_BASE_URL","Set ONES_BASE_URL to your deployment origin, e.g. https://your-team.ones.cn (no trailing slash).");return D}export function onesApiUrl(D){const K=getOnesBaseUrl(),q=D.replace(/^\/+/,"");return`${K}${API_PREFIX}/${q}`}export async function gotoOnesHome(D){await D.goto(getOnesBaseUrl(),{waitUntil:"load"});await D.wait(2)}function G(D,K){const x={Referer:getOnesBaseUrl()};if(D){const L=process.env.ONES_USER_ID?.trim()||process.env.ONES_USER_UUID?.trim()||process.env.Ones_User_Id?.trim(),N=process.env.ONES_AUTH_TOKEN?.trim()||process.env.Ones_Auth_Token?.trim();if(L&&N){x["Ones-User-Id"]=L;x["Ones-Auth-Token"]=N}}if(K)x["Content-Type"]="application/json";return x}export function summarizeOnesError(D,K){if(K&&typeof K==="object"){const q=K,x=[];if(typeof q.type==="string")x.push(q.type);if(typeof q.reason==="string")x.push(q.reason);if(typeof q.errcode==="string")x.push(q.errcode);if(typeof q.message==="string")x.push(q.message);if(q.code!==void 0&&q.code!==null)x.push(`code=${String(q.code)}`);if(x.length)return x.filter(Boolean).join(" · ")}return D===401?"Unauthorized":`HTTP ${D}`}function M(D,K){if(K===null||typeof K!=="object")return;const q=K;if(Array.isArray(q.groups))return;if(!(typeof q.reason==="string"&&q.reason.length>0||typeof q.errcode==="string"&&q.errcode.length>0||typeof q.type==="string"&&q.type.length>0))return;const L=summarizeOnesError(200,K);throw new Q("FETCH_ERROR",`ONES ${D}: ${L}`,"若 query 不合法会返回 ServerError;可试 ppcli ones tasks(空 must)或检查筛选器文档。响应全文可用 -v 或临时打日志。")}export async function onesFetchInPageWithMeta(D,K,q={}){if(!q.skipGoto)await gotoOnesHome(D);const x=onesApiUrl(K),L=(q.method??"GET").toUpperCase(),N=q.auth!==!1,S=q.body??null,V=G(N,S!==null||L==="POST"||L==="PUT"||L==="PATCH"),Y=JSON.stringify(x),Z=JSON.stringify(L),$=JSON.stringify(V),j=S===null?"null":JSON.stringify(S);return await D.evaluate(`
|
|
2
2
|
(async () => {
|
|
3
3
|
const url = ${Y};
|
|
4
4
|
const method = ${Z};
|
|
@@ -20,4 +20,4 @@ import{CliError as Q}from"@jackwener/opencli/errors";export const API_PREFIX="/p
|
|
|
20
20
|
}
|
|
21
21
|
return { ok: res.ok, status: res.status, parsed };
|
|
22
22
|
})()
|
|
23
|
-
`)}export async function resolveOnesUserUuid(D,K){const q=process.env.ONES_USER_ID?.trim()||process.env.ONES_USER_UUID?.trim()||process.env.Ones_User_Id?.trim();if(q)return q;const x=await onesFetchInPage(D,"users/me",{skipGoto:K?.skipGoto}),L=x.user&&typeof x.user==="object"?x.user:x;if(!L||typeof L.uuid!=="string")throw new Q("FETCH_ERROR","Could not read current user uuid from users/me","Set ONES_USER_ID or ensure Chrome is logged in; try:
|
|
23
|
+
`)}export async function resolveOnesUserUuid(D,K){const q=process.env.ONES_USER_ID?.trim()||process.env.ONES_USER_UUID?.trim()||process.env.Ones_User_Id?.trim();if(q)return q;const x=await onesFetchInPage(D,"users/me",{skipGoto:K?.skipGoto}),L=x.user&&typeof x.user==="object"?x.user:x;if(!L||typeof L.uuid!=="string")throw new Q("FETCH_ERROR","Could not read current user uuid from users/me","Set ONES_USER_ID or ensure Chrome is logged in; try: ppcli ones me -f json");return String(L.uuid)}export async function onesFetchInPage(D,K,q={}){const x=await onesFetchInPageWithMeta(D,K,q);if(!x.ok){const L=summarizeOnesError(x.status,x.parsed),N=x.status===401?"在 Chrome 中打开 ONES 并登录;或先执行 ppcli ones login 后按提示 export ONES_USER_ID / ONES_AUTH_TOKEN;并确认 ONES_BASE_URL 与浏览器地址一致。":"检查 ONES_BASE_URL、VPN/内网,以及实例是否仍为 Project API 路径。";throw new Q("FETCH_ERROR",`ONES ${K}: ${L}`,N)}if(K.includes("/filters/peek"))M(K,x.parsed);return x.parsed}
|
package/clis/ones/me.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{cli as s,Strategy as n}from"@jackwener/opencli/registry";import{CliError as
|
|
1
|
+
import{cli as s,Strategy as n}from"@jackwener/opencli/registry";import{CliError as i}from"@jackwener/opencli/errors";import{onesFetchInPage as o}from"./common.js";s({site:"ones",name:"me",access:"read",description:"ONES Project API — current user (GET users/me) via Chrome Bridge",domain:"ones.cn",strategy:n.COOKIE,browser:!0,navigateBefore:!1,args:[],columns:["uuid","name","email","phone","status"],func:async(t)=>{const r=await o(t,"users/me"),e=r.user&&typeof r.user==="object"?r.user:r;if(!e||typeof e.uuid!=="string")throw new i("FETCH_ERROR","Unexpected users/me response","See raw JSON with: ppcli ones me -f json");return[{uuid:String(e.uuid),name:String(e.name??""),email:String(e.email??""),phone:String(e.phone??""),status:e.status!=null?String(e.status):""}]}});
|
package/clis/ones/tasks.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{cli as c,Strategy as d}from"@jackwener/opencli/registry";import{CliError as h}from"@jackwener/opencli/errors";import{gotoOnesHome as y,onesFetchInPage as U}from"./common.js";import{enrichPeekEntriesWithDetails as E}from"./enrich-tasks.js";import{resolveTaskListLabels as I}from"./resolve-labels.js";import{defaultPeekBody as O,flattenPeekGroups as g,mapTaskEntry as k,parsePeekLimit as P}from"./task-helpers.js";function D(t,s){const e=[];if(t?.trim())e.push({in:{"field_values.field006":[t.trim()]}});if(s?.trim())e.push({equal:{assign:s.trim()}});if(e.length===0)return{must:[]};return{must:e}}c({site:"ones",name:"tasks",access:"read",description:"ONES Project API — list work items (POST team/:team/filters/peek); use token-info -f json for team uuid",domain:"ones.cn",strategy:d.COOKIE,browser:!0,navigateBefore:!1,args:[{name:"team",type:"str",required:!1,positional:!0,help:"Team UUID (8 chars), or set ONES_TEAM_UUID"},{name:"project",type:"str",required:!1,help:"Filter by project UUID (field006 / 所属项目)"},{name:"assign",type:"str",required:!1,help:"Filter by assignee user UUID (负责人 assign)"},{name:"limit",type:"int",default:30,help:"Max rows after flattening groups (default 30)"}],columns:["title","status","project","uuid","updated","工时"],func:async(t,s)=>{const e=s.team?.trim()||process.env.ONES_TEAM_UUID?.trim()||process.env.ONES_TEAM_ID?.trim();if(!e)throw new h("CONFIG","team UUID required","Pass team as first argument or set ONES_TEAM_UUID (see `
|
|
1
|
+
import{cli as c,Strategy as d}from"@jackwener/opencli/registry";import{CliError as h}from"@jackwener/opencli/errors";import{gotoOnesHome as y,onesFetchInPage as U}from"./common.js";import{enrichPeekEntriesWithDetails as E}from"./enrich-tasks.js";import{resolveTaskListLabels as I}from"./resolve-labels.js";import{defaultPeekBody as O,flattenPeekGroups as g,mapTaskEntry as k,parsePeekLimit as P}from"./task-helpers.js";function D(t,s){const e=[];if(t?.trim())e.push({in:{"field_values.field006":[t.trim()]}});if(s?.trim())e.push({equal:{assign:s.trim()}});if(e.length===0)return{must:[]};return{must:e}}c({site:"ones",name:"tasks",access:"read",description:"ONES Project API — list work items (POST team/:team/filters/peek); use token-info -f json for team uuid",domain:"ones.cn",strategy:d.COOKIE,browser:!0,navigateBefore:!1,args:[{name:"team",type:"str",required:!1,positional:!0,help:"Team UUID (8 chars), or set ONES_TEAM_UUID"},{name:"project",type:"str",required:!1,help:"Filter by project UUID (field006 / 所属项目)"},{name:"assign",type:"str",required:!1,help:"Filter by assignee user UUID (负责人 assign)"},{name:"limit",type:"int",default:30,help:"Max rows after flattening groups (default 30)"}],columns:["title","status","project","uuid","updated","工时"],func:async(t,s)=>{const e=s.team?.trim()||process.env.ONES_TEAM_UUID?.trim()||process.env.ONES_TEAM_ID?.trim();if(!e)throw new h("CONFIG","team UUID required","Pass team as first argument or set ONES_TEAM_UUID (see `ppcli ones token-info -f json` → teams[].uuid).");const i=s.project?.trim(),o=s.assign?.trim(),a=P(s.limit,30);await y(t);const n=O(D(i,o)),m=`team/${e}/filters/peek`,l=await U(t,m,{method:"POST",body:JSON.stringify(n),skipGoto:!0}),u=g(l,a),r=await E(t,e,u,!0),f=await I(t,e,r,!0);return r.map((p)=>k(p,f))}});
|
package/clis/ones/token-info.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{cli as m,Strategy as u}from"@jackwener/opencli/registry";import{CliError as c}from"@jackwener/opencli/errors";import{onesFetchInPage as g}from"./common.js";m({site:"ones",name:"token-info",access:"read",description:"ONES Project API — session detail (GET auth/token_info) via Chrome Bridge: user, teams, org",domain:"ones.cn",strategy:u.COOKIE,browser:!0,navigateBefore:!1,args:[],columns:["uuid","name","email","teams","org_name"],func:async(i)=>{const e=await g(i,"auth/token_info"),r=e.user&&typeof e.user==="object"?e.user:null;if(!r?.uuid)throw new c("FETCH_ERROR","Unexpected auth/token_info response","Try `
|
|
1
|
+
import{cli as m,Strategy as u}from"@jackwener/opencli/registry";import{CliError as c}from"@jackwener/opencli/errors";import{onesFetchInPage as g}from"./common.js";m({site:"ones",name:"token-info",access:"read",description:"ONES Project API — session detail (GET auth/token_info) via Chrome Bridge: user, teams, org",domain:"ones.cn",strategy:u.COOKIE,browser:!0,navigateBefore:!1,args:[],columns:["uuid","name","email","teams","org_name"],func:async(i)=>{const e=await g(i,"auth/token_info"),r=e.user&&typeof e.user==="object"?e.user:null;if(!r?.uuid)throw new c("FETCH_ERROR","Unexpected auth/token_info response","Try `ppcli ones me -f json` or check ONES_* env vars.");const s=(Array.isArray(e.teams)?e.teams:[]).map((a)=>{const n=String(a.name??"").trim(),o=String(a.uuid??"").trim();if(n&&o)return`${n} (${o})`;return o||n}).filter(Boolean).join(", "),t=e.org&&typeof e.org==="object"?e.org:null;return[{uuid:String(r.uuid),name:String(r.name??""),email:String(r.email??""),teams:s,org_name:t?String(t.name??""):""}]}});
|
package/clis/ones/worklog.js
CHANGED
|
@@ -11,4 +11,4 @@ import{cli as H,Strategy as b}from"@jackwener/opencli/registry";import{CliError
|
|
|
11
11
|
) {
|
|
12
12
|
key
|
|
13
13
|
}
|
|
14
|
-
}`})}H({site:"ones",name:"worklog",access:"write",description:"ONES — log work hours on a task (defaults to today; use --date to backfill; endpoint falls back by deployment).",domain:"ones.cn",strategy:b.COOKIE,browser:!0,navigateBefore:!1,args:[{name:"task",type:"str",required:!0,positional:!0,help:"Work item UUID (usually 16 chars), from my-tasks or browser URL …/task/<id>"},{name:"hours",type:"str",required:!0,positional:!0,help:"Hours to log for this entry (e.g. 2 or 1.5), converted with ONES_MANHOUR_SCALE"},{name:"team",type:"str",required:!1,help:"Team UUID from URL …/team/<uuid>/…, or set ONES_TEAM_UUID"},{name:"date",type:"str",required:!1,help:"Entry date YYYY-MM-DD, defaults to today (local timezone); use for backfill"},{name:"note",type:"str",required:!1,help:"Optional note (written to description/desc)"},{name:"owner",type:"str",required:!1,help:"Owner user UUID (defaults to current logged-in user)"}],columns:["task","date","hours","owner","endpoint"],func:async(K,V)=>{const J=String(V.task??"").trim();if(!J)throw new $("CONFIG","task uuid required","Pass the work item uuid from
|
|
14
|
+
}`})}H({site:"ones",name:"worklog",access:"write",description:"ONES — log work hours on a task (defaults to today; use --date to backfill; endpoint falls back by deployment).",domain:"ones.cn",strategy:b.COOKIE,browser:!0,navigateBefore:!1,args:[{name:"task",type:"str",required:!0,positional:!0,help:"Work item UUID (usually 16 chars), from my-tasks or browser URL …/task/<id>"},{name:"hours",type:"str",required:!0,positional:!0,help:"Hours to log for this entry (e.g. 2 or 1.5), converted with ONES_MANHOUR_SCALE"},{name:"team",type:"str",required:!1,help:"Team UUID from URL …/team/<uuid>/…, or set ONES_TEAM_UUID"},{name:"date",type:"str",required:!1,help:"Entry date YYYY-MM-DD, defaults to today (local timezone); use for backfill"},{name:"note",type:"str",required:!1,help:"Optional note (written to description/desc)"},{name:"owner",type:"str",required:!1,help:"Owner user UUID (defaults to current logged-in user)"}],columns:["task","date","hours","owner","endpoint"],func:async(K,V)=>{const J=String(V.task??"").trim();if(!J)throw new $("CONFIG","task uuid required","Pass the work item uuid from ppcli ones my-tasks or the URL.");const N=V.team?.trim()||process.env.ONES_TEAM_UUID?.trim()||process.env.ONES_TEAM_ID?.trim();if(!N)throw new $("CONFIG","team UUID required","Pass --team or set ONES_TEAM_UUID (from …/team/<team>/…).");const Z=Number(String(V.hours??"").replace(/,/g,""));if(!Number.isFinite(Z)||Z<=0||Z>1000)throw new $("CONFIG","hours must be a positive number (hours)","Example: ppcli ones worklog <taskUUID> 2 --team <teamUUID>");const X=V.date?.trim()||h();if(!k(X))throw new $("CONFIG","invalid --date","Use YYYY-MM-DD, e.g. 2026-03-24.");const x=String(V.note??"").trim(),G=y(Z),U=w(X);if(!U)throw new $("CONFIG","invalid date for start_time",`Could not parse date ${X}.`);await S(K);const q=V.owner?.trim()||await M(K,{skipGoto:!0}),z={owner:q,manhour:G,start_date:X,end_date:X,desc:x},Y={owner:q,allManhour:G,startDate:X,endDate:X,desc:x},_=encodeURIComponent(J),A=buildAddManhourGraphqlBody({ownerId:q,taskId:J,startTime:U,rawManhour:G,note:x}),O=[{path:`team/${N}/items/graphql`,body:A},{path:`team/${N}/task/${_}/manhours/add`,body:JSON.stringify(z)},{path:`team/${N}/task/${_}/manhours/add`,body:JSON.stringify(Y)},{path:`team/${N}/task/${_}/manhours/add`,body:JSON.stringify({manhours:[z]})},{path:`team/${N}/task/${_}/manhours/add`,body:JSON.stringify({manhours:[Y]})},{path:`team/${N}/task/${_}/manhour/add`,body:JSON.stringify(z)},{path:`team/${N}/task/${_}/manhour/add`,body:JSON.stringify(Y)},{path:`team/${N}/tasks/update3`,body:JSON.stringify({tasks:[{uuid:J,manhours:[z]}]})}],W=await R(K,`team/${N}/task/${_}/info`,{method:"GET",skipGoto:!0}),B=W.ok?v(W.parsed):null;let D="";for(const{path:P,body:C}of O){const E=await R(K,P,{method:"POST",body:C,skipGoto:!0}),j=I(E);if(!j){const F=await R(K,`team/${N}/task/${_}/info`,{method:"GET",skipGoto:!0});if(F.ok){const L=v(F.parsed);if(B===null?L!==null:L!==null&&Math.abs(L-B)>=1)return[{task:J,date:X,hours:String(Z),owner:q,endpoint:P}];D=`no effect (total_manhour ${String(B)} -> ${String(L)})`;continue}return[{task:J,date:X,hours:String(Z),owner:q,endpoint:P}]}D=j}throw new $("FETCH_ERROR",`ONES worklog: all endpoints failed (last: ${D})`)}});
|
package/clis/openalex/utils.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{ArgumentError as L,CommandExecutionError as N,EmptyResultError as S}from"@jackwener/opencli/errors";export const OPENALEX_BASE="https://api.openalex.org";const T="opencli-openalex-adapter (+https://github.com/jackwener/
|
|
1
|
+
import{ArgumentError as L,CommandExecutionError as N,EmptyResultError as S}from"@jackwener/opencli/errors";export const OPENALEX_BASE="https://api.openalex.org";const T="opencli-openalex-adapter (+https://github.com/jackwener/OpenCLI)",X=/^W\d{4,}$/,P=/^10\.\S+$/;export function requireString(G,z){const H=String(G??"").trim();if(!H)throw new L(`openalex ${z} cannot be empty`);return H}export function requireBoundedInt(G,z,H,J="limit"){const F=G??z,M=typeof F==="number"?F:Number(F);if(!Number.isInteger(M)||M<=0)throw new L(`openalex ${J} must be a positive integer`);if(M>H)throw new L(`openalex ${J} must be <= ${H}`);return M}export function requireWorkRef(G){const z=String(G??"").trim();if(!z)throw new L('openalex work id is required (e.g. "W2741809807", "10.7717/peerj.4375")');const H=z.match(/^https?:\/\/(?:api\.)?openalex\.org\/(?:works\/)?([WAaSCFwIPwT]\d+)/i);if(H){const F=H[1].toUpperCase();if(F[0]!=="W")throw new L(`openalex work id "${G}" must be a Work (W…) ID, got "${F[0]}…"`);return F}if(X.test(z.toUpperCase()))return z.toUpperCase();if(/^doi:/i.test(z)){const F=z.replace(/^doi:/i,"").trim();if(P.test(F))return`doi:${F}`}const J=z.match(/^https?:\/\/(?:dx\.)?doi\.org\/(.+)$/i);if(J&&P.test(J[1]))return`doi:${J[1]}`;if(P.test(z))return`doi:${z}`;throw new L(`openalex work id "${G}" is not recognised`,'Use a Work id ("W2741809807"), a DOI ("10.7717/peerj.4375"), or a full openalex.org / doi.org URL.')}export async function openalexFetch(G,z){let H;try{H=await fetch(G,{headers:{"user-agent":T,accept:"application/json"}})}catch(F){throw new N(`${z} request failed: ${F?.message??F}`,"Check that api.openalex.org is reachable from this network.")}if(H.status===404)throw new S(z,`OpenAlex returned 404 for ${G}.`);if(H.status===429)throw new N(`${z} returned HTTP 429 (rate limited)`,"OpenAlex throttles unauthenticated traffic; wait a few seconds and retry, or set OPENALEX_MAILTO.");if(!H.ok){let F="";try{const Q=(await H.text()).match(/"message"\s*:\s*"([^"]+)"/);if(Q)F=` (${Q[1]})`}catch{}throw new N(`${z} returned HTTP ${H.status}${F}`)}let J;try{J=await H.json()}catch(F){throw new N(`${z} returned malformed JSON: ${F?.message??F}`)}return J}export function bareId(G){const z=String(G??"").trim();if(!z)return"";return z.replace(/^https?:\/\/(?:api\.)?openalex\.org\//i,"").replace(/^works\//i,"")}export function bareDoi(G){const z=String(G??"").trim();if(!z)return"";return z.replace(/^https?:\/\/(?:dx\.)?doi\.org\//i,"")}export function reconstructAbstract(G){if(!G||typeof G!=="object")return"";const z=[];for(const[H,J]of Object.entries(G)){if(!Array.isArray(J))continue;for(const F of J)if(Number.isInteger(F)&&F>=0&&F<1e5)z[F]=H}return z.filter(Boolean).join(" ").replace(/\s+/g," ").trim()}export function appendMailto(G){const z=process.env.OPENALEX_MAILTO?.trim();if(!z)return G;const H=G.includes("?")?"&":"?";return`${G}${H}mailto=${encodeURIComponent(z)}`}
|
package/clis/oschina/stats.js
CHANGED
|
@@ -51,4 +51,4 @@ import{cli as Y,Strategy as $}from"@jackwener/opencli/registry";import{CliError
|
|
|
51
51
|
}
|
|
52
52
|
return out;
|
|
53
53
|
}
|
|
54
|
-
`}function f(h){if(h&&typeof h==="object"&&!Array.isArray(h)&&"session"in h&&"data"in h)return h.data;return h}function U(h){return typeof h==="number"&&Number.isFinite(h)?h:""}function W(h,H,Q,T,q){const F=h.id!=null?String(h.id):"",N=T&&F?T.get(F):null,B=(z)=>h[z]!=null?h[z]:N?N[z]:void 0,K=Number(B("type")),O={};for(const[z,D]of Object.entries({reply_count:U(B("replyCount")),state:B("state"),as_recomm:h.asRecomm,detritus:B("detritus"),scheduled_time:h.scheduledTime,scheduled_state:h.scheduledState,summary:typeof B("detail")==="string"?String(B("detail")).slice(0,80):void 0}))if(D!==void 0&&D!==null&&D!=="")O[z]=D;return{id:F,type:"博客",title:h.title!=null?String(h.title):"",url:F&&H?`https://my.oschina.net/u/${H}/blog/${F}`:"",published_at:B("createTime")||"",views:U(B("viewCount")),likes:U(B("voteCount")),comments:U(B("commentCount")),collects:"",shares:"",origin:K===2?"转载":K===1?"原创":"",is_private:Q!=="已发布"?"":q||N?"否":"是",recommended:Number(B("recomm"))===1?"推荐":"",top:Number(B("asTop"))===1?"置顶":"",status:Q,extra:Object.keys(O).length?JSON.stringify(O):""}}Y({site:"oschina",name:"stats",access:"read",description:"开源中国博客数据(每篇阅读/点赞/评论等全量运营指标)",domain:"my.oschina.net",strategy:$.COOKIE,browser:!0,args:[{name:"limit",type:"number",default:20,help:"返回条数"},{name:"user",help:"查看指定用户 ID 的公开博客数据(默认当前登录账号,含私密与定时博客)"}],columns:["id","type","title","url","published_at","views","likes","comments","collects","shares","origin","is_private","recommended","top","status","extra"],func:async(h,H)=>{const Q=Number(H.limit)||20,T=H.user!=null&&String(H.user).trim()!==""?String(H.user).trim():"";await h.goto("https://my.oschina.net/");await h.wait(1);const q=f(await h.evaluate(Z(Q,T)));if(!q||typeof q!=="object")throw Error("开源中国页面取数失败:未拿到任何响应,请确认 Chrome 已打开且能访问 my.oschina.net。");if(q.notLoggedIn)throw Error("未登录开源中国:user/myDetails 未返回用户信息。请先在 Chrome 中登录 my.oschina.net(可用
|
|
54
|
+
`}function f(h){if(h&&typeof h==="object"&&!Array.isArray(h)&&"session"in h&&"data"in h)return h.data;return h}function U(h){return typeof h==="number"&&Number.isFinite(h)?h:""}function W(h,H,Q,T,q){const F=h.id!=null?String(h.id):"",N=T&&F?T.get(F):null,B=(z)=>h[z]!=null?h[z]:N?N[z]:void 0,K=Number(B("type")),O={};for(const[z,D]of Object.entries({reply_count:U(B("replyCount")),state:B("state"),as_recomm:h.asRecomm,detritus:B("detritus"),scheduled_time:h.scheduledTime,scheduled_state:h.scheduledState,summary:typeof B("detail")==="string"?String(B("detail")).slice(0,80):void 0}))if(D!==void 0&&D!==null&&D!=="")O[z]=D;return{id:F,type:"博客",title:h.title!=null?String(h.title):"",url:F&&H?`https://my.oschina.net/u/${H}/blog/${F}`:"",published_at:B("createTime")||"",views:U(B("viewCount")),likes:U(B("voteCount")),comments:U(B("commentCount")),collects:"",shares:"",origin:K===2?"转载":K===1?"原创":"",is_private:Q!=="已发布"?"":q||N?"否":"是",recommended:Number(B("recomm"))===1?"推荐":"",top:Number(B("asTop"))===1?"置顶":"",status:Q,extra:Object.keys(O).length?JSON.stringify(O):""}}Y({site:"oschina",name:"stats",access:"read",description:"开源中国博客数据(每篇阅读/点赞/评论等全量运营指标)",domain:"my.oschina.net",strategy:$.COOKIE,browser:!0,args:[{name:"limit",type:"number",default:20,help:"返回条数"},{name:"user",help:"查看指定用户 ID 的公开博客数据(默认当前登录账号,含私密与定时博客)"}],columns:["id","type","title","url","published_at","views","likes","comments","collects","shares","origin","is_private","recommended","top","status","extra"],func:async(h,H)=>{const Q=Number(H.limit)||20,T=H.user!=null&&String(H.user).trim()!==""?String(H.user).trim():"";await h.goto("https://my.oschina.net/");await h.wait(1);const q=f(await h.evaluate(Z(Q,T)));if(!q||typeof q!=="object")throw Error("开源中国页面取数失败:未拿到任何响应,请确认 Chrome 已打开且能访问 my.oschina.net。");if(q.notLoggedIn)throw Error("未登录开源中国:user/myDetails 未返回用户信息。请先在 Chrome 中登录 my.oschina.net(可用 ppcli oschina login)。");const F=String(q.targetUser||q.me||""),N=q.pub&&q.pub.rows||[];if(q.pub&&q.pub.error)throw Error("获取开源中国博客公开列表失败:"+q.pub.error);const B=new Map(N.filter((z)=>z&&z.id!=null).map((z)=>[String(z.id),z])),K=[],O=new Set;if(q.isSelf){if(q.mine&&q.mine.error)throw Error("获取开源中国「我的创作」博客列表失败:"+q.mine.error);for(const z of q.mine&&q.mine.rows||[]){const D=W(z,F,"已发布",B);if(D.id)O.add(D.id);K.push(D)}for(const z of N){const D=z&&z.id!=null?String(z.id):"";if(!D||O.has(D))continue;O.add(D);K.push(W(z,F,"已发布",null,!0))}for(const z of q.scheduled&&q.scheduled.rows||[])K.push(W(z,F,"已定时未发布",null,!1))}else for(const z of N)K.push(W(z,F,"已发布",null,!0));if(K.length===0)throw new I("EMPTY_RESULT",q.isSelf?"当前开源中国账号没有任何博客(已发布/定时均为空)。":`用户 ${F} 没有任何公开博客。`,q.isSelf?"发布过内容后再来查看数据(ppcli oschina article)。":"请确认用户 ID 是否正确。");return K.slice(0,Q)}});
|
package/clis/osv/utils.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{ArgumentError as K,CommandExecutionError as P,EmptyResultError as W}from"@jackwener/opencli/errors";export const OSV_BASE="https://api.osv.dev";const X="opencli-osv-adapter (+https://github.com/jackwener/
|
|
1
|
+
import{ArgumentError as K,CommandExecutionError as P,EmptyResultError as W}from"@jackwener/opencli/errors";export const OSV_BASE="https://api.osv.dev";const X="opencli-osv-adapter (+https://github.com/jackwener/OpenCLI)",$=/^[A-Za-z0-9][A-Za-z0-9._-]{0,79}$/;export const OSV_ECOSYSTEMS=new Set(["npm","PyPI","Go","Maven","NuGet","RubyGems","crates.io","Packagist","Pub","Hex","Hackage","CRAN","Bitnami","GitHub Actions","SwiftURL"]);export function requireString(B,q){const z=String(B??"").trim();if(!z)throw new K(`osv ${q} cannot be empty`);return z}export function requireVulnId(B){const q=String(B??"").trim();if(!q)throw new K('osv vulnerability id is required (e.g. "GHSA-29mw-wpgm-hmr9", "CVE-2020-28500")',"IDs are listed at https://osv.dev — paste the canonical id from the vulnerability page.");if(!$.test(q))throw new K(`osv vulnerability id "${B}" is not a valid OSV id`,'IDs are short ASCII tokens like "GHSA-...", "CVE-...", "PYSEC-...".');return q}export function requireEcosystem(B){const q=String(B??"").trim();if(!q)throw new K("osv --ecosystem is required when querying by package",`Pick one of: ${[...OSV_ECOSYSTEMS].join(", ")}.`);if(!OSV_ECOSYSTEMS.has(q))throw new K(`osv --ecosystem "${B}" is not a recognised OSV ecosystem`,`Pick one of: ${[...OSV_ECOSYSTEMS].join(", ")}.`);return q}export function requireBoundedInt(B,q,z,G="limit"){const H=B??q,Q=typeof H==="number"?H:Number(H);if(!Number.isInteger(Q)||Q<=0)throw new K(`osv ${G} must be a positive integer`);if(Q>z)throw new K(`osv ${G} must be <= ${z}`);return Q}async function Z(B,q){let z;try{z=await B.json()}catch(G){throw new P(`${q} returned malformed JSON: ${G?.message??G}`)}return z}export async function osvGet(B,q){let z;try{z=await fetch(B,{headers:{"user-agent":X,accept:"application/json"}})}catch(G){throw new P(`${q} request failed: ${G?.message??G}`,"Check that api.osv.dev is reachable from this network.")}if(z.status===404)throw new W(q,`OSV.dev returned 404 for ${B}.`);if(z.status===429)throw new P(`${q} returned HTTP 429 (rate limited)`);if(!z.ok)throw new P(`${q} returned HTTP ${z.status}`);return Z(z,q)}export async function osvPost(B,q,z){let G;try{G=await fetch(B,{method:"POST",headers:{"user-agent":X,accept:"application/json","content-type":"application/json"},body:JSON.stringify(q)})}catch(H){throw new P(`${z} request failed: ${H?.message??H}`,"Check that api.osv.dev is reachable from this network.")}if(G.status===404)throw new W(z,`OSV.dev returned 404 for ${B}.`);if(G.status===429)throw new P(`${z} returned HTTP 429 (rate limited)`);if(!G.ok){const H=await G.text().catch(()=>"");throw new P(`${z} returned HTTP ${G.status}${H?`: ${H.slice(0,200)}`:""}`)}return Z(G,z)}export function severityLabel(B){const q=B?.database_specific;if(q&&typeof q.severity==="string"&&q.severity.trim())return q.severity.trim();const z=Array.isArray(B?.severity)?B.severity:[];for(const G of z)if(G&&typeof G.score==="string"&&G.score.trim())return G.score.trim();return null}export function trimDate(B){const q=String(B??"").trim();if(!q)return null;const z=q.replace(/\.\d+/,"");return z.endsWith("Z")?z:q.length>=10?q.slice(0,10):null}
|
package/clis/packagist/utils.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{ArgumentError as K,CommandExecutionError as O,EmptyResultError as W}from"@jackwener/opencli/errors";export const PACKAGIST_BASE="https://packagist.org";const Y="opencli-packagist-adapter (+https://github.com/jackwener/
|
|
1
|
+
import{ArgumentError as K,CommandExecutionError as O,EmptyResultError as W}from"@jackwener/opencli/errors";export const PACKAGIST_BASE="https://packagist.org";const Y="opencli-packagist-adapter (+https://github.com/jackwener/OpenCLI)",Q=/^[a-z0-9]([_.-]?[a-z0-9]+)*$/;export function requireString(H,q){const z=String(H??"").trim();if(!z)throw new K(`packagist ${q} cannot be empty`);return z}export function requireBoundedInt(H,q,z,J="limit"){const B=H??q,L=typeof B==="number"?B:Number(B);if(!Number.isInteger(L)||L<=0)throw new K(`packagist ${J} must be a positive integer`);if(L>z)throw new K(`packagist ${J} must be <= ${z}`);return L}export function requirePackageName(H){const q=String(H??"").trim().toLowerCase();if(!q)throw new K('packagist package name is required (e.g. "symfony/console", "monolog/monolog")');const z=q.indexOf("/");if(z<=0||z===q.length-1)throw new K(`packagist package "${H}" must be "<vendor>/<package>"`,"Both segments are required (Composer convention).");const J=q.slice(0,z),B=q.slice(z+1);if(J.length>100||B.length>100||!Q.test(J)||!Q.test(B))throw new K(`packagist package "${H}" is not a valid Composer name`,'Use lowercase letters / digits / "_-.", segments separated by single "_-." chars (max 100 chars each).');return{vendor:J,package:B,full:`${J}/${B}`}}export async function packagistFetch(H,q){let z;try{z=await fetch(H,{headers:{"user-agent":Y,accept:"application/json"}})}catch(B){throw new O(`${q} request failed: ${B?.message??B}`,"Check that packagist.org is reachable from this network.")}if(z.status===404)throw new W(q,`Packagist returned 404 for ${H}.`);if(z.status===429)throw new O(`${q} returned HTTP 429 (rate limited)`,"Packagist throttles bursts; wait a few seconds and retry.");if(!z.ok)throw new O(`${q} returned HTTP ${z.status}`);let J;try{J=await z.json()}catch(B){throw new O(`${q} returned malformed JSON: ${B?.message??B}`)}return J}export function trimDate(H){const q=String(H??"").trim();if(!q)return null;return q.replace(/\.\d+/,"").replace(/(?:[+-]\d{2}:?\d{2}|Z)?$/,"Z")}export function pickStableVersion(H){if(!H||typeof H!=="object")return null;const q=Object.keys(H);if(!q.length)return null;const z=/(?:^|[._\-+])(?:dev|alpha|beta|rc|pre|nightly)(?:[._\-+\d]|$)/i,J=/\.x-dev$|-dev$/i;for(const B of q){if(J.test(B))continue;if(z.test(B))continue;return B}return q[0]}
|