@cjhyy/code-shell-link 0.8.0

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 ADDED
@@ -0,0 +1,38 @@
1
+ # @cjhyy/code-shell-link
2
+
3
+ CodeShell Link 的独立 Provider 清单包。它不依赖 CodeShell Core、Electron、React 或服务端,保存:
4
+
5
+ - Provider 分类与展示元数据
6
+ - 本地、服务端连接方式
7
+ - 官方 CLI 会话快速授权(只声明能力,不在清单中拼接命令)
8
+ - 官方凭证创建入口、最小权限和双语步骤
9
+ - 稳定的 Action ID 契约
10
+
11
+ Core 负责执行本地 Actions;Desktop 通过 IPC 读取这份清单;未来 Link Server 也复用同一份定义。
12
+ 因此这个目录可以单独迁移成仓库并独立发版,而不需要把桌面 UI 一起搬走。
13
+
14
+ ## 仓库边界
15
+
16
+ 这个包是 Link 的可迁移边界:没有 CodeShell Core、Electron、React 或 Node 运行时依赖。以后可以把
17
+ `packages/link` 原样迁移到独立仓库并独立发版,桌面端和 Link Server 通过包版本升级清单。
18
+
19
+ 安全相关的执行逻辑不放进可编辑清单:CLI 命令及参数 allowlist、HTTP host/path、凭据保存、审批和
20
+ 断连取消仍由执行宿主实现。这样第三方清单更新不能把任意命令或 URL 注入用户设备。
21
+
22
+ Core 也不反向依赖这个包。Desktop / Link Server 作为宿主,在加载时把版本化清单与可信执行器组合,
23
+ 并逐项校验 Provider ID 和 Action ID;版本不匹配会直接拒绝加载,而不是向用户展示半可用连接。
24
+
25
+ ## 当前连接方式
26
+
27
+ | Provider | 本地快速授权 | 本地备用方式 | 服务器方式 |
28
+ | ------------------------------------------ | ------------- | -------------------------- | ------------------ |
29
+ | GitHub | `gh` 会话 | Fine-grained PAT | GitHub App(预留) |
30
+ | GitLab | `glab` 会话 | Personal access token | OAuth(预留) |
31
+ | Notion | `ntn` 会话 | Internal integration token | OAuth(预留) |
32
+ | Todoist | `td` 只读会话 | Developer token | OAuth(预留) |
33
+ | Vercel | `vercel` 会话 | Access token | OAuth(预留) |
34
+ | Figma / Linear / Slack / Sentry / Airtable | — | 官方 Token / App 流程 | OAuth(预留) |
35
+
36
+ 新增 Provider 时,先在 `src/catalog.ts` 增加 manifest,再在可信执行宿主实现相同的 `actionIds`。
37
+ `bun run test` 会校验每个 Provider 都有可用本地方法、独立服务端方法、完整授权引导,并锁定支持
38
+ 零复制会话的官方 CLI 清单。
@@ -0,0 +1,4 @@
1
+ import type { LinkLocalizedText, LinkProviderManifest } from "./types.js";
2
+ export declare const LINK_PROVIDER_MANIFESTS: LinkProviderManifest[];
3
+ export declare function getLinkProviderManifest(providerId: string): LinkProviderManifest | undefined;
4
+ export declare function localizeLinkText(value: LinkLocalizedText, locale: "zh" | "en"): string;
@@ -0,0 +1,397 @@
1
+ const text = (zh, en) => ({ zh, en });
2
+ function localToken(id, displayName, tokenLabel, tokenPlaceholder, authGuide) {
3
+ return {
4
+ id,
5
+ displayName,
6
+ executionRuntime: "local",
7
+ secretLocation: "device",
8
+ authKind: "token",
9
+ availability: "available",
10
+ tokenLabel,
11
+ tokenPlaceholder,
12
+ authGuide,
13
+ };
14
+ }
15
+ function managedOAuth(displayName) {
16
+ return {
17
+ id: "managed-oauth",
18
+ displayName: text(`${displayName} 官方授权`, `${displayName} OAuth`),
19
+ executionRuntime: "server",
20
+ secretLocation: "server",
21
+ authKind: "oauth",
22
+ availability: "coming-soon",
23
+ };
24
+ }
25
+ export const LINK_PROVIDER_MANIFESTS = [
26
+ {
27
+ id: "github",
28
+ displayName: "GitHub",
29
+ category: "developer",
30
+ description: text("读取仓库、文件、Issue 和 Pull Request。", "Read repositories, files, issues, and pull requests."),
31
+ brandText: "GH",
32
+ icon: "github",
33
+ accent: "neutral",
34
+ featured: true,
35
+ connectionMethods: [
36
+ {
37
+ ...localToken("fine-grained-pat", text("GitHub 登录 / Fine-grained PAT", "GitHub sign-in / fine-grained PAT"), "Fine-grained PAT", "github_pat_…", {
38
+ title: text("创建 Fine-grained PAT", "Create a fine-grained PAT"),
39
+ summary: text("适合只授权指定仓库;创建页面会预填当前 Link Actions 所需权限。", "Best for selected repositories; the creation page pre-fills permissions required by the current Link Actions."),
40
+ createCredentialUrl: "https://github.com/settings/personal-access-tokens/new?name=CodeShell+Link&description=Local+GitHub+connection+for+CodeShell&expires_in=90&contents=read&issues=write&pull_requests=read&metadata=read",
41
+ docsUrl: "https://docs.github.com/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens",
42
+ permissions: [
43
+ { id: "contents", label: "Contents: read", level: "required" },
44
+ { id: "issues", label: "Issues: write", level: "required" },
45
+ { id: "pull_requests", label: "Pull requests: read", level: "required" },
46
+ { id: "metadata", label: "Metadata: read", level: "required" },
47
+ ],
48
+ steps: [
49
+ text("选择资源所有者和允许 CodeShell 访问的仓库。", "Choose the resource owner and repositories CodeShell may access."),
50
+ text("确认预填权限,设置有效期并生成 Token。", "Review the pre-filled permissions, set an expiry, and generate the token."),
51
+ text("复制 Token,回到这里粘贴并验证。", "Copy the token, return here, and verify it."),
52
+ ],
53
+ note: text("Token 只显示一次;建议设置有效期并仅选择必要仓库。", "The token is shown once; set an expiry and select only necessary repositories."),
54
+ }),
55
+ browserAuth: {
56
+ kind: "browser-oauth",
57
+ flow: "device-code",
58
+ displayName: text("在浏览器登录 GitHub", "Sign in to GitHub in your browser"),
59
+ summary: text("无需安装 gh。CodeShell 显示一次性验证码,你在 GitHub 完成授权后即可连接。", "No gh installation required. CodeShell shows a one-time code and connects after you approve access on GitHub."),
60
+ docsUrl: "https://docs.github.com/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-user-access-token-for-a-github-app#using-the-device-flow-to-generate-a-user-access-token",
61
+ privacyNote: text("授权页由 GitHub 托管;密码不经过 CodeShell。授权结果仅加密保存在当前设备。", "GitHub hosts the authorization page, so your password never passes through CodeShell. The resulting credential is encrypted only on this device."),
62
+ },
63
+ quickAuth: {
64
+ kind: "cli-session",
65
+ command: "gh",
66
+ displayName: text("使用 GitHub CLI 登录", "Sign in with GitHub CLI"),
67
+ summary: text("直接复用本机 GitHub CLI 会话,不复制或保存 GitHub Token。", "Reuse the GitHub CLI session on this device without copying or storing its GitHub token."),
68
+ installUrl: "https://cli.github.com/",
69
+ privacyNote: text("Link 仅保存一条本地绑定记录;每次 Action 都由 gh 执行。断开 Link 不会退出 gh。", "Link stores only a local binding; every Action runs through gh. Disconnecting Link does not sign gh out."),
70
+ },
71
+ },
72
+ { ...managedOAuth("GitHub App"), id: "github-app" },
73
+ ],
74
+ actionIds: [
75
+ "list_repositories",
76
+ "get_readme",
77
+ "get_file",
78
+ "list_issues",
79
+ "get_issue",
80
+ "list_pull_requests",
81
+ "get_pull_request",
82
+ "create_issue",
83
+ ],
84
+ },
85
+ {
86
+ id: "gitlab",
87
+ displayName: "GitLab",
88
+ category: "developer",
89
+ description: text("查看项目和分配给你的 Issue。", "View projects and issues assigned to you."),
90
+ brandText: "GL",
91
+ icon: "github",
92
+ accent: "orange",
93
+ connectionMethods: [
94
+ {
95
+ ...localToken("personal-access-token", text("GitLab 登录 / Personal access token", "GitLab sign-in / personal access token"), "Personal access token", "glpat-…", {
96
+ title: text("创建 GitLab Access Token", "Create a GitLab access token"),
97
+ summary: text("只读项目和 Issue,使用 read_api 即可。", "The read_api scope is enough to read projects and issues."),
98
+ createCredentialUrl: "https://gitlab.com/-/user_settings/personal_access_tokens?name=CodeShell%20Link&description=Local%20GitLab%20connection%20for%20CodeShell&scopes=read_api",
99
+ docsUrl: "https://docs.gitlab.com/user/profile/personal_access_tokens/",
100
+ permissions: [{ id: "read_api", label: "read_api", level: "required" }],
101
+ steps: [
102
+ text("打开创建页并确认名称、有效期和 read_api 权限。", "Open the creation page and confirm the name, expiry, and read_api scope."),
103
+ text("生成后立即复制 Token。", "Generate and immediately copy the token."),
104
+ text("回到 CodeShell 粘贴并验证账号。", "Return to CodeShell, paste it, and verify the account."),
105
+ ],
106
+ }),
107
+ browserAuth: {
108
+ kind: "browser-oauth",
109
+ flow: "device-code",
110
+ displayName: text("在浏览器登录 GitLab", "Sign in to GitLab in your browser"),
111
+ summary: text("无需安装 glab;通过 GitLab 设备授权页完成登录。", "No glab installation required; complete sign-in on GitLab's device authorization page."),
112
+ docsUrl: "https://docs.gitlab.com/api/oauth2/#device-authorization-grant-flow",
113
+ privacyNote: text("登录和授权都在 GitLab 完成,访问凭证只留在当前设备。", "Sign-in and approval happen on GitLab; the resulting credential stays only on this device."),
114
+ },
115
+ quickAuth: {
116
+ kind: "cli-session",
117
+ command: "glab",
118
+ displayName: text("使用 GitLab CLI 登录", "Sign in with GitLab CLI"),
119
+ summary: text("通过浏览器授权 glab,并直接复用本机 CLI 会话,不导出 GitLab Token。", "Authorize glab in the browser and reuse its local session without exporting the GitLab token."),
120
+ installUrl: "https://docs.gitlab.com/cli/",
121
+ privacyNote: text("Link 仅保存一条本地绑定记录;每次 Action 都由 glab 执行。断开 Link 不会退出 glab。", "Link stores only a local binding; every Action runs through glab. Disconnecting Link does not sign glab out."),
122
+ },
123
+ },
124
+ managedOAuth("GitLab"),
125
+ ],
126
+ actionIds: ["list_projects", "list_issues"],
127
+ },
128
+ {
129
+ id: "sentry",
130
+ displayName: "Sentry",
131
+ category: "developer",
132
+ description: text("查看组织和项目。", "View organizations and projects."),
133
+ brandText: "Se",
134
+ icon: "conversation",
135
+ accent: "rose",
136
+ connectionMethods: [
137
+ localToken("auth-token", text("Organization auth token", "Organization auth token"), "Organization auth token", "sntrys_…", {
138
+ title: text("创建 Sentry Auth Token", "Create a Sentry auth token"),
139
+ summary: text("优先通过组织的 Internal Integration 创建最小权限 Token。", "Prefer an organization internal integration for a least-privilege token."),
140
+ createCredentialUrl: "https://sentry.io/settings/account/api/auth-tokens/",
141
+ docsUrl: "https://docs.sentry.io/api/guides/create-auth-token/",
142
+ permissions: [
143
+ { id: "org:read", label: "org:read", level: "required" },
144
+ { id: "project:read", label: "project:read", level: "required" },
145
+ ],
146
+ steps: [
147
+ text("有组织管理员权限时,优先创建 Internal Integration。", "If you administer the organization, create an internal integration first."),
148
+ text("仅授予 org:read 和 project:read。", "Grant only org:read and project:read."),
149
+ text("复制生成的 Token,回到这里验证。", "Copy the generated token and verify it here."),
150
+ ],
151
+ note: text("个人 Auth Token 也可使用,但组织 Token 更便于撤销和审计。", "A user auth token also works, but an organization token is easier to revoke and audit."),
152
+ }),
153
+ managedOAuth("Sentry"),
154
+ ],
155
+ actionIds: ["list_organizations", "list_projects"],
156
+ },
157
+ {
158
+ id: "vercel",
159
+ displayName: "Vercel",
160
+ category: "developer",
161
+ description: text("查看项目和部署状态。", "View projects and deployment status."),
162
+ brandText: "V",
163
+ icon: "github",
164
+ accent: "neutral",
165
+ connectionMethods: [
166
+ {
167
+ ...localToken("access-token", text("Vercel 登录 / Access token", "Vercel sign-in / access token"), "Access token", "Vercel access token", {
168
+ title: text("创建 Vercel Access Token", "Create a Vercel access token"),
169
+ summary: text("创建只用于 CodeShell 的 Token,并选择正确的个人账号或 Team。", "Create a token only for CodeShell and select the correct personal account or team."),
170
+ createCredentialUrl: "https://vercel.com/account/settings/tokens",
171
+ docsUrl: "https://vercel.com/kb/guide/how-do-i-use-a-vercel-api-access-token",
172
+ permissions: [
173
+ { id: "account-scope", label: "Account / Team scope", level: "required" },
174
+ ],
175
+ steps: [
176
+ text("打开 Token 设置并创建一个 CodeShell 专用 Token。", "Open token settings and create a CodeShell-only token."),
177
+ text("选择需要访问的个人账号或 Team,并设置有效期。", "Choose the personal account or team to access and set an expiry."),
178
+ text("复制 Token,回到这里验证。", "Copy the token and verify it here."),
179
+ ],
180
+ }),
181
+ quickAuth: {
182
+ kind: "cli-session",
183
+ command: "vercel",
184
+ displayName: text("使用 Vercel CLI 登录", "Sign in with Vercel CLI"),
185
+ summary: text("通过官方设备授权复用本机 Vercel CLI 会话,不复制 Access Token。", "Use Vercel's official device authorization and reuse the local CLI session without copying an access token."),
186
+ installUrl: "https://vercel.com/docs/cli",
187
+ privacyNote: text("Link 仅保存本地绑定;项目和部署读取都由 vercel api 执行。", "Link stores only a local binding; project and deployment reads run through vercel api."),
188
+ },
189
+ },
190
+ managedOAuth("Vercel"),
191
+ ],
192
+ actionIds: ["list_projects", "list_deployments"],
193
+ },
194
+ {
195
+ id: "slack",
196
+ displayName: "Slack",
197
+ category: "communication",
198
+ description: text("列出频道并读取最近消息。", "List channels and read recent messages."),
199
+ brandText: "S",
200
+ icon: "conversation",
201
+ accent: "fuchsia",
202
+ connectionMethods: [
203
+ localToken("app-token", text("Slack App token", "Slack app token"), "Bot or user token", "xoxb-… / xoxp-…", {
204
+ title: text("创建并安装 Slack App", "Create and install a Slack app"),
205
+ summary: text("Slack 不直接生成个人 Key;先创建 App、配置权限,再安装到工作区。", "Slack does not issue a simple personal key; create an app, configure scopes, then install it to a workspace."),
206
+ createCredentialUrl: "https://api.slack.com/apps",
207
+ docsUrl: "https://api.slack.com/tutorials/tracks/getting-a-token",
208
+ permissions: [
209
+ { id: "channels:read", label: "channels:read", level: "required" },
210
+ { id: "channels:history", label: "channels:history", level: "required" },
211
+ {
212
+ id: "groups:read",
213
+ label: "groups:read",
214
+ level: "optional",
215
+ description: text("需要列出私有频道时添加。", "Add to list private channels."),
216
+ },
217
+ {
218
+ id: "groups:history",
219
+ label: "groups:history",
220
+ level: "optional",
221
+ description: text("需要读取私有频道消息时添加。", "Add to read private-channel history."),
222
+ },
223
+ ],
224
+ steps: [
225
+ text("在 Slack API 创建 App,并选择目标工作区。", "Create an app in Slack API and choose the target workspace."),
226
+ text("在 OAuth & Permissions 添加 Bot Token Scopes。", "Add the bot token scopes under OAuth & Permissions."),
227
+ text("安装 App 到工作区;需要读消息的频道还要邀请该 App。", "Install the app, then invite it to channels whose history it should read."),
228
+ text("复制 Bot User OAuth Token(xoxb-)并回到这里验证。", "Copy the Bot User OAuth Token (xoxb-) and verify it here."),
229
+ ],
230
+ }),
231
+ managedOAuth("Slack"),
232
+ ],
233
+ actionIds: ["list_channels", "get_channel_history"],
234
+ },
235
+ {
236
+ id: "notion",
237
+ displayName: "Notion",
238
+ category: "work",
239
+ description: text("搜索并读取已共享的页面和数据库。", "Search and read shared pages and databases."),
240
+ brandText: "N",
241
+ icon: "notes",
242
+ accent: "neutral",
243
+ connectionMethods: [
244
+ {
245
+ ...localToken("internal-integration", text("Notion 登录 / Internal integration", "Notion sign-in / internal integration"), "Internal integration token", "ntn_… / secret_…", {
246
+ title: text("创建 Notion Internal Integration", "Create a Notion internal integration"),
247
+ summary: text("创建 Integration 后,还必须把具体页面或数据库共享给它。", "After creating the integration, explicitly share pages or databases with it."),
248
+ createCredentialUrl: "https://www.notion.so/profile/integrations",
249
+ docsUrl: "https://developers.notion.com/guides/get-started/internal-connections",
250
+ permissions: [{ id: "read-content", label: "Read content", level: "required" }],
251
+ steps: [
252
+ text("创建 Internal Integration,并选择工作区。", "Create an internal integration and select the workspace."),
253
+ text("Capabilities 中只保留读取内容所需权限。", "Keep only the capability required to read content."),
254
+ text("在每个需要访问的页面中,通过 Connections 添加该 Integration。", "Add the integration under Connections on every page it should access."),
255
+ text("复制 Internal Integration Secret 并回到这里验证。", "Copy the internal integration secret and verify it here."),
256
+ ],
257
+ note: text("没有共享给 Integration 的页面不会出现在搜索结果中。", "Pages not shared with the integration will not appear in search results."),
258
+ }),
259
+ quickAuth: {
260
+ kind: "cli-session",
261
+ command: "ntn",
262
+ displayName: text("使用 Notion CLI 登录", "Sign in with Notion CLI"),
263
+ summary: text("在浏览器选择 Notion 工作区,凭证由系统钥匙串保存。", "Choose a Notion workspace in the browser; the credential stays in the system keychain."),
264
+ installUrl: "https://developers.notion.com/cli/get-started/overview",
265
+ privacyNote: text("Link 不读取 Notion Token;搜索和页面读取都由 ntn api 执行。", "Link never reads the Notion token; search and page reads run through ntn api."),
266
+ },
267
+ },
268
+ managedOAuth("Notion"),
269
+ ],
270
+ actionIds: ["search", "get_page"],
271
+ },
272
+ {
273
+ id: "linear",
274
+ displayName: "Linear",
275
+ category: "work",
276
+ description: text("查看分配给你的 Issue 和团队。", "View assigned issues and teams."),
277
+ brandText: "L",
278
+ icon: "notes",
279
+ accent: "indigo",
280
+ connectionMethods: [
281
+ localToken("personal-api-key", text("Personal API key", "Personal API key"), "Personal API key", "lin_api_…", {
282
+ title: text("创建 Linear Personal API Key", "Create a Linear personal API key"),
283
+ summary: text("个人本地连接使用 Personal API Key;面向其他用户的产品应升级 OAuth。", "Use a personal API key for a local personal connection; products for other users should use OAuth."),
284
+ createCredentialUrl: "https://linear.app/settings/api",
285
+ docsUrl: "https://linear.app/developers/graphql",
286
+ permissions: [
287
+ { id: "workspace-access", label: "Your workspace access", level: "required" },
288
+ ],
289
+ steps: [
290
+ text("打开 Linear 的 Security & access / API 设置。", "Open Linear Security & access / API settings."),
291
+ text("创建一个名为 CodeShell Link 的 Personal API Key。", "Create a personal API key named CodeShell Link."),
292
+ text("复制 Key 并回到这里验证。", "Copy the key and verify it here."),
293
+ ],
294
+ }),
295
+ managedOAuth("Linear"),
296
+ ],
297
+ actionIds: ["list_issues", "list_teams"],
298
+ },
299
+ {
300
+ id: "todoist",
301
+ displayName: "Todoist",
302
+ category: "work",
303
+ description: text("查看项目和未完成任务。", "View projects and incomplete tasks."),
304
+ brandText: "T",
305
+ icon: "notes",
306
+ accent: "red",
307
+ connectionMethods: [
308
+ {
309
+ ...localToken("developer-token", text("Todoist 登录 / Developer token", "Todoist sign-in / developer token"), "Developer API token", "Todoist API token", {
310
+ title: text("获取 Todoist API Token", "Get a Todoist API token"),
311
+ summary: text("个人 API Token 位于 Todoist 的开发者集成设置中。", "Your personal API token is in Todoist's developer integrations settings."),
312
+ createCredentialUrl: "https://app.todoist.com/app/settings/integrations/developer",
313
+ docsUrl: "https://developer.todoist.com/",
314
+ permissions: [{ id: "account-data", label: "Your Todoist account", level: "required" }],
315
+ steps: [
316
+ text("打开 Settings → Integrations → Developer。", "Open Settings → Integrations → Developer."),
317
+ text("找到 API token 并复制。", "Find and copy the API token."),
318
+ text("回到这里粘贴并验证。", "Return here, paste it, and verify it."),
319
+ ],
320
+ note: text("重置 Todoist API Token 后,旧连接会立即失效。", "Resetting the Todoist API token immediately invalidates the old connection."),
321
+ }),
322
+ quickAuth: {
323
+ kind: "cli-session",
324
+ command: "td",
325
+ displayName: text("使用 Todoist CLI 登录", "Sign in with Todoist CLI"),
326
+ summary: text("通过浏览器授权官方 td CLI;Link 默认申请只读 data:read。", "Authorize the official td CLI in the browser; Link requests the read-only data:read scope."),
327
+ installUrl: "https://www.todoist.com/cli",
328
+ privacyNote: text("Token 保存在系统凭证管理器;Link 不导出它,项目和任务由 td 读取。", "The token stays in the system credential manager; Link never exports it and reads through td."),
329
+ },
330
+ },
331
+ managedOAuth("Todoist"),
332
+ ],
333
+ actionIds: ["list_projects", "list_tasks"],
334
+ },
335
+ {
336
+ id: "airtable",
337
+ displayName: "Airtable",
338
+ category: "work",
339
+ description: text("查看 Base、数据表与字段结构。", "View bases, tables, and field schemas."),
340
+ brandText: "A",
341
+ icon: "notes",
342
+ accent: "amber",
343
+ connectionMethods: [
344
+ localToken("personal-access-token", text("Personal access token", "Personal access token"), "Personal access token", "pat…", {
345
+ title: text("创建 Airtable Personal Access Token", "Create an Airtable personal access token"),
346
+ summary: text("权限和资源范围要同时配置;只选 CodeShell 需要读取的 Base。", "Configure both scopes and resources; select only bases CodeShell should read."),
347
+ createCredentialUrl: "https://airtable.com/create/tokens",
348
+ docsUrl: "https://airtable.com/developers/web/api/scopes",
349
+ permissions: [{ id: "schema.bases:read", label: "schema.bases:read", level: "required" }],
350
+ steps: [
351
+ text("创建一个名为 CodeShell Link 的 PAT。", "Create a PAT named CodeShell Link."),
352
+ text("添加 schema.bases:read scope。", "Add the schema.bases:read scope."),
353
+ text("在 Access 中选择允许读取的 Base 或 Workspace。", "Under Access, select allowed bases or workspaces."),
354
+ text("生成并复制 Token,回到这里验证。", "Generate and copy the token, then verify it here."),
355
+ ],
356
+ }),
357
+ managedOAuth("Airtable"),
358
+ ],
359
+ actionIds: ["list_bases", "list_tables"],
360
+ },
361
+ {
362
+ id: "figma",
363
+ displayName: "Figma",
364
+ category: "design",
365
+ description: text("读取文件结构摘要和评论。", "Read file structure summaries and comments."),
366
+ brandText: "Fi",
367
+ icon: "figma",
368
+ accent: "violet",
369
+ connectionMethods: [
370
+ localToken("personal-access-token", text("Personal access token", "Personal access token"), "Personal access token", "figd_…", {
371
+ title: text("创建 Figma Personal Access Token", "Create a Figma personal access token"),
372
+ summary: text("为文件内容、评论和当前用户分别授予最小读取权限。", "Grant the minimum read scopes for file content, comments, and the current user."),
373
+ createCredentialUrl: "https://www.figma.com/settings",
374
+ docsUrl: "https://developers.figma.com/docs/rest-api/personal-access-tokens/",
375
+ permissions: [
376
+ { id: "current_user:read", label: "current_user:read", level: "required" },
377
+ { id: "file_content:read", label: "file_content:read", level: "required" },
378
+ { id: "file_comments:read", label: "file_comments:read", level: "required" },
379
+ ],
380
+ steps: [
381
+ text("打开 Figma Settings → Security → Personal access tokens。", "Open Figma Settings → Security → Personal access tokens."),
382
+ text("创建 Token,选择上方三个读取 scope,并设置有效期。", "Create a token, select the three read scopes above, and set an expiry."),
383
+ text("复制只显示一次的 Token,回到这里验证。", "Copy the one-time token and verify it here."),
384
+ ],
385
+ note: text("Token 只能访问你的 Figma 账号本身有权限的文件。", "The token can only access files already available to your Figma account."),
386
+ }),
387
+ { ...managedOAuth("Figma"), id: "figma-oauth" },
388
+ ],
389
+ actionIds: ["get_file", "get_comments"],
390
+ },
391
+ ];
392
+ export function getLinkProviderManifest(providerId) {
393
+ return LINK_PROVIDER_MANIFESTS.find((provider) => provider.id === providerId);
394
+ }
395
+ export function localizeLinkText(value, locale) {
396
+ return value[locale];
397
+ }
@@ -0,0 +1,2 @@
1
+ export { LINK_PROVIDER_MANIFESTS, getLinkProviderManifest, localizeLinkText } from "./catalog.js";
2
+ export type { LinkAuthGuide, LinkAuthKind, LinkConnectionMethodManifest, LinkCliSessionAuth, LinkExecutionRuntime, LinkLocalizedText, LinkMethodAvailability, LinkPermissionGuide, LinkPermissionLevel, LinkProviderAccent, LinkProviderCategory, LinkProviderIcon, LinkProviderManifest, LinkSecretLocation, } from "./types.js";
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export { LINK_PROVIDER_MANIFESTS, getLinkProviderManifest, localizeLinkText } from "./catalog.js";
@@ -0,0 +1,85 @@
1
+ export interface LinkLocalizedText {
2
+ zh: string;
3
+ en: string;
4
+ }
5
+ export type LinkProviderCategory = "developer" | "communication" | "work" | "design";
6
+ export type LinkProviderIcon = "github" | "figma" | "notes" | "conversation";
7
+ export type LinkProviderAccent = "neutral" | "orange" | "rose" | "violet" | "fuchsia" | "indigo" | "red" | "amber";
8
+ export type LinkExecutionRuntime = "local" | "server";
9
+ export type LinkSecretLocation = "device" | "server";
10
+ export type LinkAuthKind = "token" | "oauth";
11
+ export type LinkMethodAvailability = "available" | "coming-soon";
12
+ export type LinkPermissionLevel = "required" | "optional";
13
+ /**
14
+ * A provider-owned CLI login that Link can bind to without exporting or
15
+ * copying the provider token. The host is responsible for allowlisting the
16
+ * command and arguments; manifests are presentation metadata only.
17
+ */
18
+ export interface LinkCliSessionAuth {
19
+ kind: "cli-session";
20
+ command: string;
21
+ displayName: LinkLocalizedText;
22
+ summary: LinkLocalizedText;
23
+ installUrl: string;
24
+ privacyNote: LinkLocalizedText;
25
+ }
26
+ /**
27
+ * A provider-hosted login that a desktop host can complete without a local
28
+ * CLI or Link Server. Client IDs are host configuration and are deliberately
29
+ * not embedded in the portable manifest.
30
+ */
31
+ export interface LinkBrowserAuth {
32
+ kind: "browser-oauth";
33
+ flow: "device-code" | "authorization-code-pkce";
34
+ displayName: LinkLocalizedText;
35
+ summary: LinkLocalizedText;
36
+ docsUrl: string;
37
+ privacyNote: LinkLocalizedText;
38
+ }
39
+ export interface LinkPermissionGuide {
40
+ id: string;
41
+ label: string;
42
+ level: LinkPermissionLevel;
43
+ description?: LinkLocalizedText;
44
+ }
45
+ export interface LinkAuthGuide {
46
+ title: LinkLocalizedText;
47
+ summary: LinkLocalizedText;
48
+ createCredentialUrl: string;
49
+ docsUrl: string;
50
+ permissions: LinkPermissionGuide[];
51
+ steps: LinkLocalizedText[];
52
+ note?: LinkLocalizedText;
53
+ }
54
+ export interface LinkConnectionMethodManifest {
55
+ id: string;
56
+ displayName: LinkLocalizedText;
57
+ executionRuntime: LinkExecutionRuntime;
58
+ secretLocation: LinkSecretLocation;
59
+ authKind: LinkAuthKind;
60
+ availability: LinkMethodAvailability;
61
+ oauthProfileId?: string;
62
+ tokenLabel?: string;
63
+ tokenPlaceholder?: string;
64
+ authGuide?: LinkAuthGuide;
65
+ /** Preferred provider-hosted login when the desktop host has a public client id. */
66
+ browserAuth?: LinkBrowserAuth;
67
+ /** Optional zero-copy local login shown before the manual-token fallback. */
68
+ quickAuth?: LinkCliSessionAuth;
69
+ }
70
+ /**
71
+ * Serializable provider metadata shared by local executors, a future Link server,
72
+ * and host UIs. It deliberately contains no CodeShell or framework dependency.
73
+ */
74
+ export interface LinkProviderManifest {
75
+ id: string;
76
+ displayName: string;
77
+ category: LinkProviderCategory;
78
+ description: LinkLocalizedText;
79
+ brandText: string;
80
+ icon: LinkProviderIcon;
81
+ accent: LinkProviderAccent;
82
+ featured?: boolean;
83
+ connectionMethods: LinkConnectionMethodManifest[];
84
+ actionIds: string[];
85
+ }
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@cjhyy/code-shell-link",
3
+ "version": "0.8.0",
4
+ "description": "Framework-independent provider manifests and authorization guides for CodeShell Link.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "README.md"
17
+ ],
18
+ "scripts": {
19
+ "build": "bun run clean && tsc -p tsconfig.json",
20
+ "dev": "tsc -p tsconfig.json --watch --preserveWatchOutput",
21
+ "test": "bun test src",
22
+ "typecheck": "tsc -p tsconfig.json --noEmit",
23
+ "clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\""
24
+ },
25
+ "engines": {
26
+ "node": ">=20.10"
27
+ },
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "git+https://github.com/cjhyy/codeshell.git",
31
+ "directory": "packages/link"
32
+ },
33
+ "publishConfig": {
34
+ "access": "public"
35
+ },
36
+ "license": "MIT"
37
+ }