dingding-local-api-core 1.3.8

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 (3) hide show
  1. package/README.md +17 -0
  2. package/package.json +18 -0
  3. package/src/index.cjs +173 -0
package/README.md ADDED
@@ -0,0 +1,17 @@
1
+ # dingding-local-api-core
2
+
3
+ Shared Node.js client for DingDing Browser Local API.
4
+
5
+ ```js
6
+ const { createClient } = require('dingding-local-api-core')
7
+
8
+ const client = createClient({ port: 19876, apiKey: process.env.DDB_API_KEY })
9
+ const profiles = await client.listProfiles()
10
+ ```
11
+
12
+ Environment variables:
13
+
14
+ - `DDB_BASE_URL` / `DINGDING_BASE_URL`
15
+ - `DDB_PORT` / `DINGDING_PORT`
16
+ - `DDB_API_KEY` / `DINGDING_API_KEY`
17
+ - `DDB_AUTH_HEADER` / `DINGDING_AUTH_HEADER`
package/package.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "dingding-local-api-core",
3
+ "version": "1.3.8",
4
+ "description": "DingDing Browser Local API client for CLI and MCP integrations.",
5
+ "license": "MIT",
6
+ "main": "src/index.cjs",
7
+ "files": [
8
+ "src",
9
+ "README.md"
10
+ ],
11
+ "engines": {
12
+ "node": ">=18"
13
+ },
14
+ "scripts": {
15
+ "test": "node --test test/*.test.cjs",
16
+ "check": "node --check src/index.cjs"
17
+ }
18
+ }
package/src/index.cjs ADDED
@@ -0,0 +1,173 @@
1
+ 'use strict'
2
+
3
+ const DEFAULT_PORT = 19876
4
+ const DEFAULT_AUTH_HEADER = 'X-Ant-Api-Key'
5
+
6
+ function normalizeBaseUrl(options = {}, env = process.env) {
7
+ const explicit = trim(options.baseUrl || env.DDB_BASE_URL || env.DINGDING_BASE_URL)
8
+ if (explicit) return explicit.replace(/\/+$/, '')
9
+
10
+ const rawPort = trim(options.port || env.DDB_PORT || env.DINGDING_PORT || env.PORT)
11
+ const port = rawPort || String(DEFAULT_PORT)
12
+ return `http://127.0.0.1:${port}`
13
+ }
14
+
15
+ function createClient(options = {}) {
16
+ const env = options.env || process.env
17
+ const baseUrl = normalizeBaseUrl(options, env)
18
+ const apiKey = trim(options.apiKey || env.DDB_API_KEY || env.DINGDING_API_KEY || env.API_KEY)
19
+ const authHeader = trim(options.authHeader || env.DDB_AUTH_HEADER || env.DINGDING_AUTH_HEADER || env.AUTH_HEADER) || DEFAULT_AUTH_HEADER
20
+ const fetchImpl = options.fetch || globalThis.fetch
21
+ if (typeof fetchImpl !== 'function') {
22
+ throw new Error('global fetch is unavailable; DingDing CLI requires Node.js 18 or newer')
23
+ }
24
+
25
+ async function request(method, path, body, query) {
26
+ const url = new URL(path, baseUrl)
27
+ for (const [key, value] of Object.entries(query || {})) {
28
+ if (value === undefined || value === null || value === '') continue
29
+ url.searchParams.set(key, String(value))
30
+ }
31
+
32
+ const headers = { Accept: 'application/json' }
33
+ const init = { method, headers }
34
+ if (apiKey) headers[authHeader] = apiKey
35
+ if (body !== undefined) {
36
+ headers['Content-Type'] = 'application/json'
37
+ init.body = JSON.stringify(body)
38
+ }
39
+
40
+ const response = await fetchImpl(url, init)
41
+ const text = await response.text()
42
+ const payload = parseJSONText(text)
43
+ if (!response.ok) {
44
+ const message = payload && typeof payload === 'object' && payload.error ? payload.error : `HTTP ${response.status}`
45
+ const err = new Error(message)
46
+ err.status = response.status
47
+ err.payload = payload || text
48
+ throw err
49
+ }
50
+ return payload === undefined ? text : payload
51
+ }
52
+
53
+ return {
54
+ baseUrl,
55
+ authHeader,
56
+ request,
57
+ health: () => request('GET', '/api/health'),
58
+ listProfiles: () => request('GET', '/api/profiles'),
59
+ getProfile: (profileId) => request('GET', `/api/profiles/${encodeURIComponent(required(profileId, 'profileId'))}`),
60
+ createProfile: (input) => request('POST', '/api/profiles', input),
61
+ updateProfile: (profileId, input) => request('PUT', `/api/profiles/${encodeURIComponent(required(profileId, 'profileId'))}`, input),
62
+ deleteProfile: (profileId) => request('DELETE', `/api/profiles/${encodeURIComponent(required(profileId, 'profileId'))}`),
63
+ launch: (input) => request('POST', '/api/launch', input),
64
+ launchByCode: (code) => request('GET', `/api/launch/${encodeURIComponent(required(code, 'code'))}`),
65
+ launchLogs: (limit) => request('GET', '/api/launch/logs', undefined, { limit }),
66
+ runtimeActive: () => request('GET', '/api/runtime/active'),
67
+ runtimeSession: (input) => request('POST', '/api/runtime/session', input),
68
+ runtimeStatus: (input) => request('POST', '/api/runtime/status', input),
69
+ runtimeStop: (input) => request('POST', '/api/runtime/stop', input),
70
+ listAutomationScripts: () => request('GET', '/api/automation/scripts'),
71
+ getAutomationScript: (scriptId) => request('GET', `/api/automation/scripts/${encodeURIComponent(required(scriptId, 'scriptId'))}`),
72
+ runAutomationScript: (input) => request('POST', '/api/automation/scripts/run', input),
73
+ listAutomationRuns: (limit) => request('GET', '/api/automation/scripts/runs', undefined, { limit }),
74
+ listGroups: () => request('GET', '/api/groups'),
75
+ createGroup: (input) => request('POST', '/api/groups', input),
76
+ updateGroup: (groupId, input) => request('PUT', `/api/groups/${encodeURIComponent(required(groupId, 'groupId'))}`, input),
77
+ deleteGroup: (groupId) => request('DELETE', `/api/groups/${encodeURIComponent(required(groupId, 'groupId'))}`),
78
+ moveProfilesToGroup: (groupId, profileIds) => request('POST', `/api/groups/${encodeURIComponent(required(groupId, 'groupId'))}/profiles`, { profileIds }),
79
+ listProxies: (groupName) => request('GET', '/api/proxies', undefined, { groupName }),
80
+ listProxyGroups: () => request('GET', '/api/proxies/groups'),
81
+ getProxy: (proxyId) => request('GET', `/api/proxies/${encodeURIComponent(required(proxyId, 'proxyId'))}`),
82
+ createProxy: (input) => request('POST', '/api/proxies', input),
83
+ saveProxies: (items) => request('PUT', '/api/proxies', items),
84
+ updateProxy: (proxyId, input) => request('PUT', `/api/proxies/${encodeURIComponent(required(proxyId, 'proxyId'))}`, input),
85
+ deleteProxy: (proxyId) => request('DELETE', `/api/proxies/${encodeURIComponent(required(proxyId, 'proxyId'))}`),
86
+ listTags: () => request('GET', '/api/tags'),
87
+ renameTag: (oldName, newName) => request('POST', '/api/tags/rename', { oldName, newName }),
88
+ setTags: (profileIds, tags, replace = false) => request('POST', '/api/tags/set', { profileIds, tags, replace }),
89
+ removeTags: (profileIds, tags) => request('POST', '/api/tags/remove', { profileIds, tags }),
90
+ listCores: () => request('GET', '/api/cores'),
91
+ }
92
+ }
93
+
94
+ function selectorBodyFromValue(value, options = {}) {
95
+ if (value && typeof value === 'object' && !Array.isArray(value)) return value
96
+ const field = selectorFieldName(options.by || options.selector || 'code')
97
+ return { [field]: required(value, field) }
98
+ }
99
+
100
+ function selectorFieldName(input) {
101
+ const normalized = trim(input).toLowerCase().replace(/[_\s]+/g, '-')
102
+ switch (normalized) {
103
+ case 'profile-id':
104
+ case 'profileid':
105
+ case 'id':
106
+ return 'profileId'
107
+ case 'profile-name':
108
+ case 'profilename':
109
+ case 'name':
110
+ return 'profileName'
111
+ case 'group-id':
112
+ case 'groupid':
113
+ return 'groupId'
114
+ case 'keyword':
115
+ case 'tag':
116
+ case 'key':
117
+ case 'code':
118
+ return normalized
119
+ default:
120
+ throw new Error(`unsupported selector field: ${input}`)
121
+ }
122
+ }
123
+
124
+ function parseJSONArg(raw, label = 'argument') {
125
+ const text = trim(raw)
126
+ if (!text) return {}
127
+ try {
128
+ return JSON.parse(text)
129
+ } catch (err) {
130
+ throw new Error(`${label} must be valid JSON: ${err.message}`)
131
+ }
132
+ }
133
+
134
+ function parseValueOrJSON(raw) {
135
+ const text = trim(raw)
136
+ if (!text) return {}
137
+ if (text.startsWith('{') || text.startsWith('[')) return parseJSONArg(text)
138
+ return text
139
+ }
140
+
141
+ function parseJSONText(text) {
142
+ const body = trim(text)
143
+ if (!body) return undefined
144
+ try {
145
+ return JSON.parse(body)
146
+ } catch {
147
+ return undefined
148
+ }
149
+ }
150
+
151
+ function required(value, name) {
152
+ const out = trim(value)
153
+ if (!out) throw new Error(`${name} is required`)
154
+ return out
155
+ }
156
+
157
+ function trim(value) {
158
+ if (value === undefined || value === null) return ''
159
+ return String(value).trim()
160
+ }
161
+
162
+ module.exports = {
163
+ DEFAULT_AUTH_HEADER,
164
+ DEFAULT_PORT,
165
+ createClient,
166
+ normalizeBaseUrl,
167
+ parseJSONArg,
168
+ parseValueOrJSON,
169
+ required,
170
+ selectorBodyFromValue,
171
+ selectorFieldName,
172
+ trim,
173
+ }