pikiloop 0.4.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.
Files changed (154) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +353 -0
  3. package/README.v2.md +287 -0
  4. package/README.zh-CN.md +352 -0
  5. package/dashboard/dist/assets/AgentTab-UZPIhlkr.js +1 -0
  6. package/dashboard/dist/assets/DirBrowser-Ckcmi-Pi.js +1 -0
  7. package/dashboard/dist/assets/ExtensionsTab-KZhEDrdu.js +1 -0
  8. package/dashboard/dist/assets/IMAccessTab-Bd_IY1GQ.js +1 -0
  9. package/dashboard/dist/assets/Modal-CTeL0y7P.js +1 -0
  10. package/dashboard/dist/assets/Modals-axftHasy.js +1 -0
  11. package/dashboard/dist/assets/Select-C8tOdPhe.js +1 -0
  12. package/dashboard/dist/assets/SessionPanel-C1geSRxw.js +1 -0
  13. package/dashboard/dist/assets/SystemTab-DBDkaPiO.js +1 -0
  14. package/dashboard/dist/assets/anthropic-BAdojD7P.ico +0 -0
  15. package/dashboard/dist/assets/codex-DYadqqp0.png +0 -0
  16. package/dashboard/dist/assets/deepseek-BeYNZEk0.ico +0 -0
  17. package/dashboard/dist/assets/doubao-DloFDuFR.png +0 -0
  18. package/dashboard/dist/assets/feishu-C4OMrjCW.ico +0 -0
  19. package/dashboard/dist/assets/gemini-BYkEpiWr.svg +1 -0
  20. package/dashboard/dist/assets/hermes-BAarh-tH.png +0 -0
  21. package/dashboard/dist/assets/index-CpM4CqZJ.js +23 -0
  22. package/dashboard/dist/assets/index-DXSohzrE.js +3 -0
  23. package/dashboard/dist/assets/index-reSbuley.css +1 -0
  24. package/dashboard/dist/assets/markdown-DxQYQFeH.js +29 -0
  25. package/dashboard/dist/assets/minimax-PuEGTfrF.ico +0 -0
  26. package/dashboard/dist/assets/mlx-DhWwjtMw.png +0 -0
  27. package/dashboard/dist/assets/ollama-Bt9O-2K_.png +0 -0
  28. package/dashboard/dist/assets/openrouter-CsJ_bD5Q.ico +0 -0
  29. package/dashboard/dist/assets/playwright-BldPFZgC.ico +0 -0
  30. package/dashboard/dist/assets/qwen-xykkX0_y.png +0 -0
  31. package/dashboard/dist/assets/react-vendor-C7Sl8SE7.js +9 -0
  32. package/dashboard/dist/assets/router-DHISdpPk.js +3 -0
  33. package/dashboard/dist/assets/shared-BIP_4k4I.js +1 -0
  34. package/dashboard/dist/favicon.svg +28 -0
  35. package/dashboard/dist/index.html +17 -0
  36. package/dist/agent/acp-client.js +261 -0
  37. package/dist/agent/auto-update.js +432 -0
  38. package/dist/agent/await-resume.js +50 -0
  39. package/dist/agent/cli/auth.js +325 -0
  40. package/dist/agent/cli/catalog.js +40 -0
  41. package/dist/agent/cli/detector.js +136 -0
  42. package/dist/agent/cli/index.js +7 -0
  43. package/dist/agent/cli/registry.js +33 -0
  44. package/dist/agent/driver.js +39 -0
  45. package/dist/agent/drivers/claude-tui.js +2297 -0
  46. package/dist/agent/drivers/claude.js +2689 -0
  47. package/dist/agent/drivers/codex.js +2210 -0
  48. package/dist/agent/drivers/gemini.js +1059 -0
  49. package/dist/agent/drivers/hermes.js +795 -0
  50. package/dist/agent/goal.js +274 -0
  51. package/dist/agent/handover.js +130 -0
  52. package/dist/agent/images.js +355 -0
  53. package/dist/agent/index.js +50 -0
  54. package/dist/agent/mcp/bridge.js +791 -0
  55. package/dist/agent/mcp/extensions.js +637 -0
  56. package/dist/agent/mcp/oauth.js +353 -0
  57. package/dist/agent/mcp/registry.js +119 -0
  58. package/dist/agent/mcp/session-server.js +229 -0
  59. package/dist/agent/mcp/tools/ask-user.js +113 -0
  60. package/dist/agent/mcp/tools/await-resume.js +77 -0
  61. package/dist/agent/mcp/tools/goal.js +144 -0
  62. package/dist/agent/mcp/tools/types.js +12 -0
  63. package/dist/agent/mcp/tools/workspace.js +212 -0
  64. package/dist/agent/npm.js +31 -0
  65. package/dist/agent/session.js +1206 -0
  66. package/dist/agent/skill-installer.js +160 -0
  67. package/dist/agent/skills.js +257 -0
  68. package/dist/agent/stream.js +743 -0
  69. package/dist/agent/types.js +13 -0
  70. package/dist/agent/utils.js +687 -0
  71. package/dist/bot/bot.js +2499 -0
  72. package/dist/bot/command-ui.js +633 -0
  73. package/dist/bot/commands.js +513 -0
  74. package/dist/bot/headless-bot.js +36 -0
  75. package/dist/bot/host.js +192 -0
  76. package/dist/bot/human-loop.js +168 -0
  77. package/dist/bot/menu.js +48 -0
  78. package/dist/bot/orchestration.js +79 -0
  79. package/dist/bot/render-shared.js +309 -0
  80. package/dist/bot/session-hub.js +361 -0
  81. package/dist/bot/session-status.js +55 -0
  82. package/dist/bot/streaming.js +309 -0
  83. package/dist/browser-profile.js +579 -0
  84. package/dist/browser-supervisor.js +249 -0
  85. package/dist/catalog/cli-tools.js +421 -0
  86. package/dist/catalog/index.js +21 -0
  87. package/dist/catalog/local-models.js +94 -0
  88. package/dist/catalog/mcp-servers.js +315 -0
  89. package/dist/catalog/skill-repos.js +173 -0
  90. package/dist/channels/base.js +55 -0
  91. package/dist/channels/dingtalk/bot.js +549 -0
  92. package/dist/channels/dingtalk/channel.js +268 -0
  93. package/dist/channels/discord/bot.js +552 -0
  94. package/dist/channels/discord/channel.js +245 -0
  95. package/dist/channels/feishu/bot.js +1275 -0
  96. package/dist/channels/feishu/channel.js +911 -0
  97. package/dist/channels/feishu/markdown.js +91 -0
  98. package/dist/channels/feishu/render.js +619 -0
  99. package/dist/channels/health.js +109 -0
  100. package/dist/channels/slack/bot.js +554 -0
  101. package/dist/channels/slack/channel.js +283 -0
  102. package/dist/channels/states.js +6 -0
  103. package/dist/channels/telegram/bot.js +1310 -0
  104. package/dist/channels/telegram/channel.js +820 -0
  105. package/dist/channels/telegram/directory.js +111 -0
  106. package/dist/channels/telegram/live-preview.js +220 -0
  107. package/dist/channels/telegram/render.js +384 -0
  108. package/dist/channels/wecom/bot.js +558 -0
  109. package/dist/channels/wecom/channel.js +479 -0
  110. package/dist/channels/weixin/api.js +520 -0
  111. package/dist/channels/weixin/bot.js +1000 -0
  112. package/dist/channels/weixin/channel.js +222 -0
  113. package/dist/cli/autostart.js +262 -0
  114. package/dist/cli/channel-supervisor.js +313 -0
  115. package/dist/cli/channels.js +54 -0
  116. package/dist/cli/main.js +726 -0
  117. package/dist/cli/onboarding.js +227 -0
  118. package/dist/cli/run.js +308 -0
  119. package/dist/cli/setup-wizard.js +235 -0
  120. package/dist/core/config/runtime-config.js +201 -0
  121. package/dist/core/config/user-config.js +510 -0
  122. package/dist/core/config/validation.js +521 -0
  123. package/dist/core/constants.js +400 -0
  124. package/dist/core/git.js +145 -0
  125. package/dist/core/legacy-compat.js +60 -0
  126. package/dist/core/logging.js +101 -0
  127. package/dist/core/platform.js +59 -0
  128. package/dist/core/process-control.js +315 -0
  129. package/dist/core/secrets/index.js +42 -0
  130. package/dist/core/secrets/inline-seal.js +60 -0
  131. package/dist/core/secrets/ref.js +33 -0
  132. package/dist/core/secrets/resolver.js +65 -0
  133. package/dist/core/secrets/store.js +63 -0
  134. package/dist/core/utils.js +233 -0
  135. package/dist/core/version.js +15 -0
  136. package/dist/dashboard/platform.js +219 -0
  137. package/dist/dashboard/routes/agents.js +450 -0
  138. package/dist/dashboard/routes/cli.js +174 -0
  139. package/dist/dashboard/routes/config.js +523 -0
  140. package/dist/dashboard/routes/extensions.js +745 -0
  141. package/dist/dashboard/routes/local-models.js +290 -0
  142. package/dist/dashboard/routes/models.js +324 -0
  143. package/dist/dashboard/routes/sessions.js +838 -0
  144. package/dist/dashboard/runtime.js +410 -0
  145. package/dist/dashboard/server.js +237 -0
  146. package/dist/dashboard/session-control.js +347 -0
  147. package/dist/model/catalog.js +104 -0
  148. package/dist/model/index.js +20 -0
  149. package/dist/model/injector.js +272 -0
  150. package/dist/model/provider-models.js +112 -0
  151. package/dist/model/store.js +212 -0
  152. package/dist/model/types.js +13 -0
  153. package/dist/model/validation.js +203 -0
  154. package/package.json +82 -0
@@ -0,0 +1,203 @@
1
+ /**
2
+ * Provider credential validation — Feishu-style explicit check.
3
+ *
4
+ * Validation strategy: GET ${baseURL}/models with the resolved API key.
5
+ * Status semantics mirror ChannelSetupState in core/config/validation.ts:
6
+ * missing | invalid | error | ready
7
+ */
8
+ import { request } from 'undici';
9
+ import { resolveCredential } from '../core/secrets/index.js';
10
+ const VALIDATION_TIMEOUT_MS = 10_000;
11
+ function buildHeaders(provider, apiKey) {
12
+ const base = { 'Accept': 'application/json' };
13
+ switch (provider.kind) {
14
+ case 'anthropic':
15
+ // Anthropic models endpoint is /v1/models with x-api-key + anthropic-version
16
+ base['x-api-key'] = apiKey;
17
+ base['anthropic-version'] = '2023-06-01';
18
+ break;
19
+ case 'google':
20
+ // Google AI Studio uses ?key= query param, not Authorization header.
21
+ // Caller appends ?key= in URL builder below.
22
+ break;
23
+ case 'openai':
24
+ case 'openai-compatible':
25
+ default:
26
+ base['Authorization'] = `Bearer ${apiKey}`;
27
+ break;
28
+ }
29
+ if (provider.extraHeaders)
30
+ Object.assign(base, provider.extraHeaders);
31
+ return base;
32
+ }
33
+ function modelsUrl(provider, apiKey) {
34
+ const base = provider.baseURL.replace(/\/+$/, '');
35
+ if (provider.kind === 'google') {
36
+ // Google AI Studio: GET https://generativelanguage.googleapis.com/v1beta/models?key=API_KEY
37
+ const sep = base.includes('?') ? '&' : '?';
38
+ return `${base}/models${sep}key=${encodeURIComponent(apiKey)}`;
39
+ }
40
+ if (provider.kind === 'anthropic') {
41
+ return `${base}/v1/models`;
42
+ }
43
+ // OpenAI-compatible
44
+ return `${base}/models`;
45
+ }
46
+ function toNumber(value) {
47
+ if (typeof value === 'number' && Number.isFinite(value))
48
+ return value;
49
+ if (typeof value === 'string' && value.trim()) {
50
+ const n = Number(value);
51
+ if (Number.isFinite(n))
52
+ return n;
53
+ }
54
+ return undefined;
55
+ }
56
+ /**
57
+ * Convert a per-token USD price (the format OpenRouter & most OpenAI-compatible
58
+ * APIs use) to USD per 1M tokens. We preserve up to 4 decimals so micro-prices
59
+ * don't round to zero.
60
+ */
61
+ function perTokenToPerMillion(value) {
62
+ const n = toNumber(value);
63
+ if (n === undefined)
64
+ return undefined;
65
+ const perM = n * 1_000_000;
66
+ return Math.round(perM * 10000) / 10000;
67
+ }
68
+ function extractModelInfos(payload) {
69
+ if (!payload || typeof payload !== 'object')
70
+ return [];
71
+ const p = payload;
72
+ const arr = p.data || p.models;
73
+ if (!Array.isArray(arr))
74
+ return [];
75
+ const out = [];
76
+ for (const m of arr) {
77
+ const id = (typeof m?.id === 'string' && m.id) || (typeof m?.name === 'string' && m.name) || '';
78
+ if (!id)
79
+ continue;
80
+ const info = { id };
81
+ const name = typeof m?.name === 'string' ? m.name : (typeof m?.display_name === 'string' ? m.display_name : '');
82
+ if (name && name !== id)
83
+ info.name = name;
84
+ const created = toNumber(m?.created);
85
+ if (created !== undefined)
86
+ info.created = created;
87
+ const ctx = toNumber(m?.context_length) ?? toNumber(m?.max_tokens);
88
+ if (ctx !== undefined)
89
+ info.contextLength = ctx;
90
+ if (m?.pricing) {
91
+ const prompt = perTokenToPerMillion(m.pricing.prompt ?? m.pricing.input);
92
+ const completion = perTokenToPerMillion(m.pricing.completion ?? m.pricing.output);
93
+ if (prompt !== undefined)
94
+ info.pricePromptUsd = prompt;
95
+ if (completion !== undefined)
96
+ info.priceCompletionUsd = completion;
97
+ }
98
+ out.push(info);
99
+ }
100
+ return out;
101
+ }
102
+ export async function validateProvider(provider) {
103
+ let apiKey;
104
+ try {
105
+ apiKey = await resolveCredential(provider.credential);
106
+ }
107
+ catch (e) {
108
+ return {
109
+ status: {
110
+ state: 'invalid',
111
+ detail: `Cannot read credential: ${e?.message || e}`,
112
+ checkedAt: new Date().toISOString(),
113
+ },
114
+ models: [],
115
+ modelInfos: [],
116
+ };
117
+ }
118
+ if (!apiKey || apiKey.length < 4) {
119
+ return {
120
+ status: {
121
+ state: 'invalid',
122
+ detail: 'Empty or too-short API key.',
123
+ checkedAt: new Date().toISOString(),
124
+ },
125
+ models: [],
126
+ modelInfos: [],
127
+ };
128
+ }
129
+ const url = modelsUrl(provider, apiKey);
130
+ const headers = buildHeaders(provider, apiKey);
131
+ let statusCode = 0;
132
+ let bodyText = '';
133
+ try {
134
+ const result = await request(url, {
135
+ method: 'GET',
136
+ headers,
137
+ headersTimeout: VALIDATION_TIMEOUT_MS,
138
+ bodyTimeout: VALIDATION_TIMEOUT_MS,
139
+ });
140
+ statusCode = result.statusCode;
141
+ bodyText = await result.body.text();
142
+ }
143
+ catch (e) {
144
+ return {
145
+ status: {
146
+ state: 'error',
147
+ detail: `Network error reaching ${provider.baseURL}: ${e?.code || e?.message || e}`,
148
+ checkedAt: new Date().toISOString(),
149
+ },
150
+ models: [],
151
+ modelInfos: [],
152
+ };
153
+ }
154
+ if (statusCode === 401 || statusCode === 403) {
155
+ return {
156
+ status: {
157
+ state: 'invalid',
158
+ detail: `Provider rejected credential (HTTP ${statusCode}). ${shortBody(bodyText)}`,
159
+ checkedAt: new Date().toISOString(),
160
+ },
161
+ models: [],
162
+ modelInfos: [],
163
+ };
164
+ }
165
+ if (statusCode < 200 || statusCode >= 300) {
166
+ return {
167
+ status: {
168
+ state: 'error',
169
+ detail: `HTTP ${statusCode} from ${provider.baseURL}. ${shortBody(bodyText)}`,
170
+ checkedAt: new Date().toISOString(),
171
+ },
172
+ models: [],
173
+ modelInfos: [],
174
+ };
175
+ }
176
+ let parsed = null;
177
+ try {
178
+ parsed = JSON.parse(bodyText);
179
+ }
180
+ catch { /* unparsed body — still treat 2xx as ready */ }
181
+ const modelInfos = extractModelInfos(parsed);
182
+ const models = modelInfos.map(info => info.id);
183
+ return {
184
+ status: {
185
+ state: 'ready',
186
+ detail: models.length
187
+ ? `${models.length} models available.`
188
+ : `Endpoint reachable; model list not enumerable.`,
189
+ checkedAt: new Date().toISOString(),
190
+ modelCount: models.length,
191
+ },
192
+ models,
193
+ modelInfos,
194
+ };
195
+ }
196
+ function shortBody(text, max = 200) {
197
+ const trimmed = text.trim();
198
+ if (!trimmed)
199
+ return '';
200
+ if (trimmed.length <= max)
201
+ return trimmed;
202
+ return `${trimmed.slice(0, max)}…`;
203
+ }
package/package.json ADDED
@@ -0,0 +1,82 @@
1
+ {
2
+ "name": "pikiloop",
3
+ "version": "0.4.0",
4
+ "description": "Put the world's smartest AI agents in your pocket. Command local Claude & Gemini via IM. | 让最好用的 IM 变成你电脑上的顶级 Agent 控制台",
5
+ "type": "module",
6
+ "bin": {
7
+ "pikiloop": "dist/cli/main.js"
8
+ },
9
+ "files": [
10
+ "dist/",
11
+ "dashboard/dist/",
12
+ "LICENSE",
13
+ "README.md"
14
+ ],
15
+ "keywords": [
16
+ "telegram",
17
+ "ai",
18
+ "coding",
19
+ "claude",
20
+ "codex",
21
+ "bridge",
22
+ "cli"
23
+ ],
24
+ "author": "xiaotonng",
25
+ "license": "MIT",
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/xiaotonng/pikiloop.git"
29
+ },
30
+ "scripts": {
31
+ "dev": "bash scripts/dev.sh",
32
+ "command": "set -ae && . ./.env && set +a && tsx src/cli/run.ts",
33
+ "build:dashboard": "vite build --config dashboard/vite.config.ts",
34
+ "build": "npm run build:dashboard && tsc",
35
+ "prepack": "npm run build",
36
+ "test": "vitest run",
37
+ "test:watch": "vitest"
38
+ },
39
+ "engines": {
40
+ "node": ">=20.0.0"
41
+ },
42
+ "devDependencies": {
43
+ "@tailwindcss/vite": "^4.2.1",
44
+ "@types/node": "^25.3.5",
45
+ "@types/qrcode": "^1.5.6",
46
+ "@types/react": "^19.2.14",
47
+ "@types/react-dom": "^19.2.3",
48
+ "@types/which": "^3.0.4",
49
+ "@types/ws": "^8.18.1",
50
+ "@vitejs/plugin-react": "^5.1.4",
51
+ "react": "^19.2.4",
52
+ "react-dom": "^19.2.4",
53
+ "react-markdown": "^10.1.0",
54
+ "react-router-dom": "^7.13.2",
55
+ "remark-gfm": "^4.0.1",
56
+ "tailwindcss": "^4.2.1",
57
+ "tsx": "^4.21.0",
58
+ "typescript": "^5.9.3",
59
+ "vitest": "^4.0.18",
60
+ "zustand": "^5.0.12"
61
+ },
62
+ "dependencies": {
63
+ "@hono/node-server": "^1.19.11",
64
+ "@larksuiteoapi/node-sdk": "^1.59.0",
65
+ "@playwright/mcp": "^0.0.75",
66
+ "@slack/bolt": "^4.7.2",
67
+ "@slack/socket-mode": "^2.0.7",
68
+ "@slack/web-api": "^7.15.2",
69
+ "dingtalk-stream": "^2.1.6-beta.1",
70
+ "discord.js": "^14.26.4",
71
+ "hono": "^4.12.9",
72
+ "qrcode": "^1.5.4",
73
+ "remark-breaks": "^4.0.0",
74
+ "undici": "^7.22.0",
75
+ "which": "^6.0.1",
76
+ "ws": "^8.18.0"
77
+ },
78
+ "optionalDependencies": {
79
+ "@napi-rs/keyring": "^1.3.0",
80
+ "node-pty": "^1.1.0"
81
+ }
82
+ }