openxiangda 1.0.157 → 1.0.159

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/lib/cli.js CHANGED
@@ -5924,6 +5924,8 @@ async function runDirectRequest(config, target, flags, request, options = {}) {
5924
5924
  method: request.method || 'GET',
5925
5925
  body: request.body,
5926
5926
  strictEnvelope: request.strictEnvelope,
5927
+ timeoutMs:
5928
+ request.timeoutMs || flags['timeout-ms'] || flags.timeout || undefined,
5927
5929
  };
5928
5930
  let data;
5929
5931
  if (request.auth === false) {
package/lib/http.js CHANGED
@@ -1,8 +1,15 @@
1
1
  const { formatFetchError, maskText } = require('./utils');
2
2
 
3
- const DEFAULT_REQUEST_TIMEOUT_MS = 60000;
3
+ const DEFAULT_REQUEST_TIMEOUT_MS = normalizeTimeoutMs(
4
+ process.env.OPENXIANGDA_REQUEST_TIMEOUT_MS,
5
+ 120000
6
+ );
7
+ const DEFAULT_CONNECT_TIMEOUT_MS = normalizeTimeoutMs(
8
+ process.env.OPENXIANGDA_CONNECT_TIMEOUT_MS,
9
+ 30000
10
+ );
4
11
 
5
- configureFetchDispatcher(DEFAULT_REQUEST_TIMEOUT_MS);
12
+ configureFetchDispatcher(DEFAULT_CONNECT_TIMEOUT_MS);
6
13
 
7
14
  async function requestJson(baseUrl, apiPath, options = {}) {
8
15
  if (typeof fetch !== 'function') {
@@ -82,17 +89,106 @@ module.exports = {
82
89
 
83
90
  function configureFetchDispatcher(timeoutMs) {
84
91
  try {
85
- // Node fetch uses undici internally. Setting the global dispatcher prevents
86
- // large resource operations from failing on undici's 10s connect default.
87
- const { Agent, setGlobalDispatcher } = require('undici');
88
- setGlobalDispatcher(
89
- new Agent({
90
- connect: {
91
- timeout: timeoutMs,
92
- },
93
- })
92
+ // Node fetch uses undici internally but does not honor proxy environment
93
+ // variables by itself. Keep NO_PROXY-aware routing while extending the
94
+ // connect timeout for private and high-latency deployments.
95
+ const { Agent, ProxyAgent, setGlobalDispatcher } = require('undici');
96
+ const connect = { timeout: timeoutMs };
97
+ const directAgent = new Agent({ connect });
98
+ const httpProxyUrl = readProxyUrl('HTTP_PROXY');
99
+ const httpsProxyUrl = readProxyUrl('HTTPS_PROXY') || httpProxyUrl;
100
+ const allProxyUrl = readProxyUrl('ALL_PROXY');
101
+ const httpProxyAgent = createProxyAgent(
102
+ ProxyAgent,
103
+ httpProxyUrl || allProxyUrl,
104
+ connect
94
105
  );
106
+ const httpsProxyAgent = createProxyAgent(
107
+ ProxyAgent,
108
+ httpsProxyUrl || allProxyUrl,
109
+ connect
110
+ );
111
+
112
+ setGlobalDispatcher({
113
+ dispatch(options, handler) {
114
+ const origin = new URL(String(options.origin));
115
+ if (shouldBypassProxy(origin)) {
116
+ return directAgent.dispatch(options, handler);
117
+ }
118
+ const proxyAgent =
119
+ origin.protocol === 'https:' ? httpsProxyAgent : httpProxyAgent;
120
+ return (proxyAgent || directAgent).dispatch(options, handler);
121
+ },
122
+ close() {
123
+ return closeDispatchers([
124
+ directAgent,
125
+ httpProxyAgent,
126
+ httpsProxyAgent,
127
+ ]);
128
+ },
129
+ destroy(error) {
130
+ return destroyDispatchers(
131
+ [directAgent, httpProxyAgent, httpsProxyAgent],
132
+ error
133
+ );
134
+ },
135
+ });
95
136
  } catch {
96
137
  // Keep working on runtimes where undici is not directly importable.
97
138
  }
98
139
  }
140
+
141
+ function readProxyUrl(name) {
142
+ const value = process.env[name] || process.env[name.toLowerCase()];
143
+ return String(value || '').trim();
144
+ }
145
+
146
+ function createProxyAgent(ProxyAgent, uri, connect) {
147
+ if (!uri) return null;
148
+ return new ProxyAgent({ uri, connect });
149
+ }
150
+
151
+ function shouldBypassProxy(origin) {
152
+ const noProxy = readProxyUrl('NO_PROXY');
153
+ if (!noProxy) return false;
154
+ const hostname = origin.hostname.replace(/^\[|\]$/g, '').toLowerCase();
155
+ const port = origin.port || (origin.protocol === 'https:' ? '443' : '80');
156
+
157
+ return noProxy.split(',').some(rawEntry => {
158
+ const entry = rawEntry.trim().toLowerCase();
159
+ if (!entry) return false;
160
+ if (entry === '*') return true;
161
+
162
+ const normalized = entry.replace(/^https?:\/\//, '');
163
+ const separator = normalized.lastIndexOf(':');
164
+ const hasSinglePortSeparator =
165
+ separator > -1 && normalized.indexOf(':') === separator;
166
+ const entryHost = (hasSinglePortSeparator
167
+ ? normalized.slice(0, separator)
168
+ : normalized
169
+ ).replace(/^\[|\]$/g, '');
170
+ const entryPort = hasSinglePortSeparator
171
+ ? normalized.slice(separator + 1)
172
+ : '';
173
+ if (entryPort && entryPort !== port) return false;
174
+
175
+ const suffix = entryHost.replace(/^\*?\./, '');
176
+ return hostname === suffix || hostname.endsWith(`.${suffix}`);
177
+ });
178
+ }
179
+
180
+ async function closeDispatchers(dispatchers) {
181
+ await Promise.all(
182
+ uniqueDispatchers(dispatchers).map(dispatcher => dispatcher.close())
183
+ );
184
+ }
185
+
186
+ async function destroyDispatchers(dispatchers, error) {
187
+ await Promise.all(
188
+ uniqueDispatchers(dispatchers).map(dispatcher => dispatcher.destroy(error))
189
+ );
190
+ }
191
+
192
+ function uniqueDispatchers(dispatchers) {
193
+ return [...new Set(dispatchers.filter(Boolean))];
194
+ }
@@ -99,6 +99,38 @@ Custom cards use the variables defined in the DingTalk developer console:
99
99
  }
100
100
  ```
101
101
 
102
+ For DingTalk card components that expect structured JSON, map the card variable
103
+ directly to one exact payload placeholder. Arrays and objects are preserved and
104
+ serialized as JSON instead of being formatted as display text:
105
+
106
+ ```json
107
+ {
108
+ "card": {
109
+ "mode": "custom",
110
+ "cardTemplateId": "${DINGTALK_CARD_TEMPLATE_ID}",
111
+ "paramMap": {
112
+ "title": "{{title}}",
113
+ "contentList": "{{contentList}}"
114
+ }
115
+ }
116
+ }
117
+ ```
118
+
119
+ ```json
120
+ {
121
+ "payload": {
122
+ "title": "预约通知",
123
+ "contentList": [
124
+ { "text": "**预约单号:** Y202607130001" },
125
+ { "text": "**仪器名称:** 场发射扫描电镜" }
126
+ ]
127
+ }
128
+ }
129
+ ```
130
+
131
+ Keep the placeholder as the complete value (`"{{contentList}}"`). Mixing it
132
+ with surrounding text intentionally converts the value to display text.
133
+
102
134
  Standard card payload variables include `title`, `lastMessage`, `content`, `contentList`, `jumpUrl`, `createTime`, `status`, `operator`, `currentUser`, `formData.current`, `workflowData`, `variables`, and `app`. General notifications do not show approval buttons by default.
103
135
 
104
136
  ## Runtime Calls
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openxiangda",
3
- "version": "1.0.157",
3
+ "version": "1.0.159",
4
4
  "description": "OpenXiangda CLI, workspace build tools, runtime SDK, and form components.",
5
5
  "private": false,
6
6
  "bin": {
@@ -62,7 +62,7 @@
62
62
  "scripts": {
63
63
  "build:sdk": "tsup --config packages/sdk/tsup.config.ts",
64
64
  "check": "node --check bin/openxiangda.js && node --check lib/*.js && node --check packages/sdk/src/build-source/src/cli.mjs && node --check packages/sdk/src/build-source/scripts/*.mjs && node --check packages/sdk/src/build-source/scripts/utils/*.mjs",
65
- "test": "npm run check && node scripts/form-export-cli-smoke.mjs && node scripts/open-api-cli-smoke.mjs",
65
+ "test": "npm run check && node scripts/http-proxy-smoke.mjs && node scripts/form-export-cli-smoke.mjs && node scripts/open-api-cli-smoke.mjs",
66
66
  "prepack": "npm run build:sdk",
67
67
  "test:profile-isolation": "bash scripts/profile-isolation-smoke.sh",
68
68
  "test:resource-plan": "node scripts/resource-plan-smoke.mjs",