@abtnode/core 1.17.8-beta-20260109-075740-5f484e08 → 1.17.8-beta-20260111-112953-aed5ff39

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 (65) hide show
  1. package/lib/api/team/access-key-manager.js +104 -0
  2. package/lib/api/team/invitation-manager.js +461 -0
  3. package/lib/api/team/notification-manager.js +189 -0
  4. package/lib/api/team/oauth-manager.js +60 -0
  5. package/lib/api/team/org-crud-manager.js +202 -0
  6. package/lib/api/team/org-manager.js +56 -0
  7. package/lib/api/team/org-member-manager.js +403 -0
  8. package/lib/api/team/org-query-manager.js +126 -0
  9. package/lib/api/team/org-resource-manager.js +186 -0
  10. package/lib/api/team/passport-manager.js +670 -0
  11. package/lib/api/team/rbac-manager.js +335 -0
  12. package/lib/api/team/session-manager.js +540 -0
  13. package/lib/api/team/store-manager.js +198 -0
  14. package/lib/api/team/tag-manager.js +230 -0
  15. package/lib/api/team/user-auth-manager.js +132 -0
  16. package/lib/api/team/user-manager.js +78 -0
  17. package/lib/api/team/user-query-manager.js +299 -0
  18. package/lib/api/team/user-social-manager.js +354 -0
  19. package/lib/api/team/user-update-manager.js +224 -0
  20. package/lib/api/team/verify-code-manager.js +161 -0
  21. package/lib/api/team.js +439 -3287
  22. package/lib/blocklet/manager/disk/auth-manager.js +68 -0
  23. package/lib/blocklet/manager/disk/backup-manager.js +288 -0
  24. package/lib/blocklet/manager/disk/cleanup-manager.js +157 -0
  25. package/lib/blocklet/manager/disk/component-manager.js +83 -0
  26. package/lib/blocklet/manager/disk/config-manager.js +191 -0
  27. package/lib/blocklet/manager/disk/controller-manager.js +64 -0
  28. package/lib/blocklet/manager/disk/delete-reset-manager.js +328 -0
  29. package/lib/blocklet/manager/disk/download-manager.js +96 -0
  30. package/lib/blocklet/manager/disk/env-config-manager.js +311 -0
  31. package/lib/blocklet/manager/disk/federated-manager.js +651 -0
  32. package/lib/blocklet/manager/disk/hook-manager.js +124 -0
  33. package/lib/blocklet/manager/disk/install-component-manager.js +95 -0
  34. package/lib/blocklet/manager/disk/install-core-manager.js +448 -0
  35. package/lib/blocklet/manager/disk/install-download-manager.js +313 -0
  36. package/lib/blocklet/manager/disk/install-manager.js +36 -0
  37. package/lib/blocklet/manager/disk/install-upgrade-manager.js +340 -0
  38. package/lib/blocklet/manager/disk/job-manager.js +467 -0
  39. package/lib/blocklet/manager/disk/lifecycle-manager.js +26 -0
  40. package/lib/blocklet/manager/disk/notification-manager.js +343 -0
  41. package/lib/blocklet/manager/disk/query-manager.js +562 -0
  42. package/lib/blocklet/manager/disk/settings-manager.js +507 -0
  43. package/lib/blocklet/manager/disk/start-manager.js +611 -0
  44. package/lib/blocklet/manager/disk/stop-restart-manager.js +292 -0
  45. package/lib/blocklet/manager/disk/update-manager.js +153 -0
  46. package/lib/blocklet/manager/disk.js +669 -5796
  47. package/lib/blocklet/manager/helper/blue-green-start-blocklet.js +5 -0
  48. package/lib/blocklet/manager/lock.js +18 -0
  49. package/lib/event/index.js +28 -24
  50. package/lib/util/blocklet/app-utils.js +192 -0
  51. package/lib/util/blocklet/blocklet-loader.js +258 -0
  52. package/lib/util/blocklet/config-manager.js +232 -0
  53. package/lib/util/blocklet/did-document.js +240 -0
  54. package/lib/util/blocklet/environment.js +555 -0
  55. package/lib/util/blocklet/health-check.js +449 -0
  56. package/lib/util/blocklet/install-utils.js +365 -0
  57. package/lib/util/blocklet/logo.js +57 -0
  58. package/lib/util/blocklet/meta-utils.js +269 -0
  59. package/lib/util/blocklet/port-manager.js +141 -0
  60. package/lib/util/blocklet/process-manager.js +504 -0
  61. package/lib/util/blocklet/runtime-info.js +105 -0
  62. package/lib/util/blocklet/validation.js +418 -0
  63. package/lib/util/blocklet.js +98 -3066
  64. package/lib/util/wallet-app-notification.js +40 -0
  65. package/package.json +22 -22
@@ -0,0 +1,555 @@
1
+ /**
2
+ * Environment Module
3
+ *
4
+ * Functions for managing blocklet environment variables and configuration
5
+ * Extracted from blocklet.js for better modularity
6
+ */
7
+
8
+ const fs = require('fs-extra');
9
+ const path = require('node:path');
10
+ const get = require('lodash/get');
11
+ const isNil = require('lodash/isNil');
12
+ const toLower = require('lodash/toLower');
13
+ const { slugify } = require('transliteration');
14
+
15
+ const { toHex } = require('@ocap/util');
16
+ const { fromSecretKey } = require('@ocap/wallet');
17
+ const { urlPathFriendly } = require('@blocklet/meta/lib/url-path-friendly');
18
+ const logger = require('@abtnode/logger')('@abtnode/core:util:blocklet:environment');
19
+ const { formatEnv } = require('@abtnode/util/lib/security');
20
+ const { AIGNE_CONFIG_ENCRYPT_SALT } = require('@abtnode/constant');
21
+ const { decrypt } = require('@abtnode/util/lib/security');
22
+ const {
23
+ BlockletSource,
24
+ BLOCKLET_MODES,
25
+ BLOCKLET_ENTRY_FILE,
26
+ BLOCKLET_DEFAULT_PORT_NAME,
27
+ BLOCKLET_CONFIGURABLE_KEY,
28
+ BLOCKLET_TENANT_MODES,
29
+ PROJECT,
30
+ } = require('@blocklet/constant');
31
+ const { getBlockletInfo } = require('@blocklet/meta/lib/info');
32
+ const { getApplicationWallet: getBlockletWallet } = require('@blocklet/meta/lib/wallet');
33
+ const {
34
+ forEachBlockletSync,
35
+ findWebInterface,
36
+ getSharedConfigObj,
37
+ getComponentName,
38
+ getBlockletAppIdList,
39
+ hasStartEngine,
40
+ } = require('@blocklet/meta/lib/util');
41
+ const { getComponentsInternalInfo } = require('@blocklet/meta/lib/blocklet');
42
+ const { getBlockletEngine } = require('@blocklet/meta/lib/engine');
43
+ const { getDidDomainForBlocklet } = require('@abtnode/util/lib/get-domain-for-blocklet');
44
+ const { getComponentApiKey } = require('@abtnode/util/lib/blocklet');
45
+ const { get: getEngine } = require('../../blocklet/manager/engine');
46
+
47
+ const { templateReplace, prettyURL } = require('../index');
48
+ const { getBundleDir } = require('./install-utils');
49
+
50
+ /**
51
+ * Private node environment variables that should not be passed to blocklets
52
+ */
53
+ const PRIVATE_NODE_ENVS = [
54
+ 'ABT_NODE_UPDATER_PORT',
55
+ 'ABT_NODE_SESSION_TTL',
56
+ 'ABT_NODE_ROUTER_PROVIDER',
57
+ 'ABT_NODE_DATA_DIR',
58
+ 'ABT_NODE_TOKEN_SECRET',
59
+ 'ABT_NODE_SK',
60
+ 'ABT_NODE_SESSION_SECRET',
61
+ 'ABT_NODE_BASE_URL',
62
+ 'ABT_NODE_LOG_LEVEL',
63
+ 'ABT_NODE_LOG_DIR',
64
+ // in /core/cli/bin/blocklet.js
65
+ 'CLI_MODE',
66
+ 'ABT_NODE_HOME',
67
+ 'PM2_HOME',
68
+ 'ABT_NODE_CONFIG_FILE',
69
+ ];
70
+
71
+ /**
72
+ * @returns { dataDir, logsDir, cacheDir, appDir }
73
+ * dataDir: dataDirs.data/name (root component) or dataDirs.data/name/childName (child component)
74
+ * logsDir: dataDirs.log/name
75
+ * cacheDir: dataDirs.cache/name (root component) or dataDirs.cache/name/childName (child component)
76
+ * appDir: component bundle dir
77
+ */
78
+ const getComponentDirs = (component, { dataDirs, ensure = false, ancestors = [] } = {}) => {
79
+ const componentName = getComponentName(component, ancestors);
80
+
81
+ const logsDir = path.join(dataDirs.logs, componentName);
82
+ const dataDir = path.join(dataDirs.data, componentName);
83
+ const cacheDir = path.join(dataDirs.cache, componentName);
84
+
85
+ let appDir = null;
86
+ if (component.source === BlockletSource.local) {
87
+ appDir = component.deployedFrom;
88
+ } else {
89
+ appDir = getBundleDir(dataDirs.blocklets, component.meta);
90
+ }
91
+
92
+ if (!appDir) {
93
+ throw new Error('Can not determine blocklet directory, maybe invalid deployment from local blocklets');
94
+ }
95
+
96
+ if (ensure) {
97
+ try {
98
+ fs.mkdirSync(dataDir, { recursive: true });
99
+ fs.mkdirSync(path.join(dataDir, PROJECT.DIR), { recursive: true });
100
+ fs.mkdirSync(logsDir, { recursive: true });
101
+ fs.mkdirSync(cacheDir, { recursive: true });
102
+ fs.mkdirSync(appDir, { recursive: true }); // prevent getDiskInfo failed from custom blocklet
103
+ } catch (err) {
104
+ logger.error('make blocklet dir failed', { error: err });
105
+ }
106
+ }
107
+
108
+ return { dataDir, logsDir, cacheDir, appDir };
109
+ };
110
+
111
+ /**
112
+ * @param component {import('@blocklet/server-js').ComponentState & { environmentObj: {[key: string]: string } } }
113
+ * @returns {{cwd, script, args, environmentObj, interpreter, interpreterArgs}: { args: []}}
114
+ * @return {*}
115
+ */
116
+ const getComponentStartEngine = (component, { e2eMode = false } = {}) => {
117
+ if (!hasStartEngine(component.meta)) {
118
+ return {};
119
+ }
120
+
121
+ const { appDir } = component.env;
122
+
123
+ const cwd = appDir;
124
+
125
+ // get app dirs
126
+ const { group } = component.meta;
127
+
128
+ let startFromDevEntry = '';
129
+ if (component.mode === BLOCKLET_MODES.DEVELOPMENT && component.meta.scripts) {
130
+ startFromDevEntry = component.meta.scripts.dev;
131
+ if (e2eMode && component.meta.scripts.e2eDev) {
132
+ startFromDevEntry = component.meta.scripts.e2eDev;
133
+ }
134
+ }
135
+
136
+ const blockletEngineInfo = getBlockletEngine(component.meta);
137
+ if (blockletEngineInfo.interpreter === 'blocklet') {
138
+ return {};
139
+ }
140
+
141
+ let script = null;
142
+ let interpreter;
143
+ let interpreterArgs = [];
144
+ const environmentObj = {};
145
+ let args = [];
146
+
147
+ if (startFromDevEntry) {
148
+ script = startFromDevEntry;
149
+ } else if (group === 'dapp') {
150
+ script = blockletEngineInfo.source || BLOCKLET_ENTRY_FILE;
151
+ args = blockletEngineInfo.args || [];
152
+ }
153
+
154
+ if (component.mode !== BLOCKLET_MODES.DEVELOPMENT) {
155
+ const engine = getEngine(blockletEngineInfo.interpreter);
156
+ interpreter = engine.interpreter === 'node' ? undefined : engine.interpreter;
157
+ interpreterArgs = interpreterArgs.concat(engine.args ? [engine.args] : []);
158
+ }
159
+
160
+ return {
161
+ cwd,
162
+ script,
163
+ args,
164
+ environmentObj,
165
+ interpreter,
166
+ interpreterArgs: interpreterArgs.join(' ').trim(),
167
+ };
168
+ };
169
+
170
+ /**
171
+ * Get blocklet config object from configs array
172
+ * @param {object} blocklet - Blocklet object with configs
173
+ * @param {object} options - Options
174
+ * @param {boolean} options.excludeSecure - Exclude secure configs
175
+ * @returns {object} Config object
176
+ */
177
+ const getBlockletConfigObj = (blocklet, { excludeSecure } = {}) => {
178
+ const obj = (blocklet?.configs || [])
179
+ .filter((x) => {
180
+ if (excludeSecure) {
181
+ return !x.secure;
182
+ }
183
+ return true;
184
+ })
185
+ .reduce((acc, x) => {
186
+ acc[x.key] = templateReplace(x.value, blocklet);
187
+ return acc;
188
+ }, {});
189
+
190
+ return obj;
191
+ };
192
+
193
+ /**
194
+ * Get app-level system environment variables
195
+ * @param {object} blocklet - Blocklet object
196
+ * @param {object} nodeInfo - Node info with sk
197
+ * @param {object} dataDirs - Data directories
198
+ * @returns {object} Environment variables
199
+ */
200
+ const getAppSystemEnvironments = (blocklet, nodeInfo, dataDirs) => {
201
+ const { did, name, title, description } = blocklet.meta;
202
+ const keys = Object.keys(BLOCKLET_CONFIGURABLE_KEY);
203
+ const result = getBlockletInfo(
204
+ {
205
+ meta: blocklet.meta,
206
+ environments: keys.map((key) => ({ key, value: blocklet.configObj[key] })).filter((x) => x.value),
207
+ },
208
+ nodeInfo.sk
209
+ );
210
+
211
+ const { wallet } = result;
212
+ const appSk = toHex(wallet.secretKey);
213
+ const appPk = toHex(wallet.publicKey);
214
+
215
+ const appId = wallet.address;
216
+ const appName = title || name || result.name;
217
+ const appDescription = description || result.description;
218
+
219
+ const isMigrated = Array.isArray(blocklet.migratedFrom) && blocklet.migratedFrom.length > 0;
220
+ const appPid = blocklet.appPid || appId;
221
+ const appPsk = toHex(isMigrated ? blocklet.migratedFrom[0].appSk : appSk);
222
+
223
+ /* 获取 did domain 方式:
224
+ * 1. 先从 site 里读
225
+ * 2. 如果没有,再拼接
226
+ */
227
+
228
+ const pidDomain = getDidDomainForBlocklet({ did: appPid, didDomain: nodeInfo.didDomain });
229
+ const domainAliases = get(blocklet, 'site.domainAliases') || [];
230
+
231
+ let didDomain = domainAliases.find((item) => toLower(item.value) === toLower(pidDomain));
232
+
233
+ if (!didDomain) {
234
+ didDomain = domainAliases.find(
235
+ (item) => item.value.endsWith(nodeInfo.didDomain) || item.value.endsWith('did.staging.arcblock.io') // did.staging.arcblock.io 是旧 did domain, 但主要存在于比较旧的节点中, 需要做兼容
236
+ );
237
+ }
238
+
239
+ const appUrl = didDomain ? prettyURL(didDomain.value, true) : `https://${pidDomain}`;
240
+
241
+ return {
242
+ BLOCKLET_DID: did, // BLOCKLET_DID is always same as BLOCKLET_APP_PID in structV2 application
243
+ BLOCKLET_APP_PK: appPk,
244
+ BLOCKLET_APP_SK: appSk,
245
+ BLOCKLET_APP_ID: appId,
246
+ BLOCKLET_APP_PSK: appPsk, // permanent sk even the blocklet has been migrated
247
+ BLOCKLET_APP_PID: appPid, // permanent did even the blocklet has been migrated
248
+ BLOCKLET_APP_NAME: appName,
249
+ BLOCKLET_APP_NAME_SLUG: urlPathFriendly(slugify(appName)),
250
+ BLOCKLET_APP_DESCRIPTION: appDescription,
251
+ BLOCKLET_APP_URL: appUrl,
252
+ BLOCKLET_APP_DATA_DIR: path.join(dataDirs.data, blocklet.meta.name),
253
+ BLOCKLET_APP_TENANT_MODE: result.tenantMode || BLOCKLET_TENANT_MODES.SINGLE,
254
+ BLOCKLET_APP_SALT: blocklet.settings?.session?.salt || '',
255
+ };
256
+ };
257
+
258
+ /**
259
+ * Get app-level overwritten environment variables from config
260
+ * @param {object} blocklet - Blocklet object with configObj
261
+ * @param {object} nodeInfo - Node info with sk
262
+ * @returns {object} Environment variables
263
+ */
264
+ const getAppOverwrittenEnvironments = (blocklet, nodeInfo) => {
265
+ const result = {};
266
+ if (!blocklet || !blocklet.configObj) {
267
+ return result;
268
+ }
269
+
270
+ Object.keys(BLOCKLET_CONFIGURABLE_KEY).forEach((x) => {
271
+ if (!blocklet.configObj[x]) {
272
+ return;
273
+ }
274
+
275
+ result[x] = blocklet.configObj[x];
276
+ });
277
+
278
+ const keys = ['BLOCKLET_APP_SK', 'BLOCKLET_APP_CHAIN_TYPE', 'BLOCKLET_WALLET_TYPE'];
279
+ const isAppDidRewritten = keys.some((key) => blocklet.configObj[key]);
280
+ if (!isAppDidRewritten) {
281
+ return result;
282
+ }
283
+
284
+ // We use user configuration here without any validation since the validation is done during update phase
285
+ const { wallet } = getBlockletInfo(
286
+ {
287
+ meta: blocklet.meta,
288
+ environments: keys.map((key) => ({ key, value: blocklet.configObj[key] })).filter((x) => x.value),
289
+ },
290
+ nodeInfo.sk
291
+ );
292
+ result.BLOCKLET_APP_ID = wallet.address;
293
+
294
+ return result;
295
+ };
296
+
297
+ /**
298
+ * Get component-level system environment variables
299
+ * @param {object} blocklet - Blocklet object with env and ports
300
+ * @returns {object} Environment variables
301
+ */
302
+ const getComponentSystemEnvironments = (blocklet) => {
303
+ const { port, ports } = blocklet;
304
+ const portEnvironments = {};
305
+ if (port) {
306
+ portEnvironments[BLOCKLET_DEFAULT_PORT_NAME] = port;
307
+ }
308
+
309
+ if (ports) {
310
+ Object.assign(portEnvironments, ports);
311
+ }
312
+
313
+ return {
314
+ BLOCKLET_REAL_DID: blocklet.env.id, // <appDid>/componentDid> e.g. xxxxx/xxxxx
315
+ BLOCKLET_REAL_NAME: blocklet.env.name,
316
+ BLOCKLET_COMPONENT_DID: blocklet.meta.did, // component meta did e.g. xxxxxx
317
+ BLOCKLET_COMPONENT_VERSION: blocklet.meta.version,
318
+ BLOCKLET_DATA_DIR: blocklet.env.dataDir,
319
+ BLOCKLET_LOG_DIR: blocklet.env.logsDir,
320
+ BLOCKLET_CACHE_DIR: blocklet.env.cacheDir,
321
+ BLOCKLET_APP_DIR: blocklet.env.appDir,
322
+ ...portEnvironments,
323
+ };
324
+ };
325
+
326
+ /**
327
+ * Set 'configs', 'configObj', 'environmentObj' to blocklet
328
+ * @param {object} blocklet - Blocklet to fill configs for
329
+ * @param {Array} configs - Configs array
330
+ * @param {object} options - Optional: { rootBlocklet, nodeInfo, dataDirs }
331
+ */
332
+ const fillBlockletConfigs = (blocklet, configs, options = {}) => {
333
+ blocklet.configs = configs || [];
334
+ blocklet.configObj = getBlockletConfigObj(blocklet);
335
+ blocklet.environments = blocklet.environments || [];
336
+ blocklet.environmentObj = blocklet.environments.reduce((acc, x) => {
337
+ acc[x.key] = templateReplace(x.value, blocklet);
338
+ return acc;
339
+ }, {});
340
+
341
+ // After migration: ensure all component system environments are set from blocklet.env if available
342
+ // This ensures children loaded from blocklet_children table have all required env vars in environmentObj
343
+ if (blocklet.env) {
344
+ try {
345
+ const componentSystemEnvs = getComponentSystemEnvironments(blocklet);
346
+ // Only set env vars that are not already set
347
+ Object.entries(componentSystemEnvs).forEach(([key, value]) => {
348
+ if (value !== undefined && value !== null && !blocklet.environmentObj[key]) {
349
+ blocklet.environmentObj[key] = value;
350
+ }
351
+ });
352
+ } catch (error) {
353
+ // If getting component system environments fails, log warning but continue
354
+ logger.warn('fillBlockletConfigs: failed to get component system environments', {
355
+ blockletDid: blocklet.meta?.did,
356
+ error: error.message,
357
+ });
358
+ }
359
+ }
360
+
361
+ // For children: also set app-level environment variables from root blocklet
362
+ // This ensures children have app-level env vars like BLOCKLET_APP_ID
363
+ const { rootBlocklet, nodeInfo, dataDirs } = options;
364
+ if (rootBlocklet && nodeInfo && dataDirs && blocklet !== rootBlocklet) {
365
+ try {
366
+ const appSystemEnvs = getAppSystemEnvironments(rootBlocklet, nodeInfo, dataDirs);
367
+ const appOverwrittenEnvs = getAppOverwrittenEnvironments(rootBlocklet, nodeInfo);
368
+ // Only set env vars that are not already set
369
+ Object.entries({ ...appSystemEnvs, ...appOverwrittenEnvs }).forEach(([key, value]) => {
370
+ if (value !== undefined && value !== null && !blocklet.environmentObj[key]) {
371
+ blocklet.environmentObj[key] = value;
372
+ }
373
+ });
374
+ } catch (error) {
375
+ // If getting app system environments fails, log warning but continue
376
+ logger.warn('fillBlockletConfigs: failed to get app system environments', {
377
+ blockletDid: blocklet.meta?.did,
378
+ rootDid: rootBlocklet.meta?.did,
379
+ error: error.message,
380
+ });
381
+ }
382
+ }
383
+ };
384
+
385
+ /**
386
+ * Get runtime environment variables for a blocklet process
387
+ * @param {object} blocklet - Blocklet object
388
+ * @param {object} nodeEnvironments - Node environment variables
389
+ * @param {Array} ancestors - Ancestor blocklets
390
+ * @param {boolean} isGreen - Whether this is a green deployment
391
+ * @returns {object} Environment variables
392
+ */
393
+ const getRuntimeEnvironments = (blocklet, nodeEnvironments, ancestors, isGreen = false) => {
394
+ const root = (ancestors || [])[0] || blocklet;
395
+
396
+ const initialized = root?.settings?.initialized;
397
+
398
+ const environmentObj = { ...(blocklet.environmentObj || {}) };
399
+ if (isGreen && blocklet.greenPorts) {
400
+ Object.entries(blocklet.greenPorts).forEach(([key, value]) => {
401
+ if (!value) {
402
+ return;
403
+ }
404
+ environmentObj[key] = value;
405
+ if (key === BLOCKLET_DEFAULT_PORT_NAME || key === 'BLOCKLET_PORT') {
406
+ environmentObj[BLOCKLET_DEFAULT_PORT_NAME] = value;
407
+ }
408
+ });
409
+ }
410
+
411
+ // pm2 will force inject env variables of daemon process to blocklet process
412
+ // we can only rewrite these private env variables to empty
413
+ const safeNodeEnvironments = PRIVATE_NODE_ENVS.reduce((o, x) => {
414
+ o[x] = '';
415
+ return o;
416
+ }, {});
417
+
418
+ // get devEnvironments, when blocklet is in dev mode
419
+ const devEnvironments =
420
+ blocklet.mode === BLOCKLET_MODES.DEVELOPMENT
421
+ ? {
422
+ BLOCKLET_DEV_MOUNT_POINT: blocklet?.mountPoint || '',
423
+ }
424
+ : {};
425
+
426
+ // BLOCKLET_DEV_PORT should NOT in components of production mode
427
+ if (process.env.BLOCKLET_DEV_PORT) {
428
+ devEnvironments.BLOCKLET_DEV_PORT =
429
+ blocklet.mode === BLOCKLET_MODES.DEVELOPMENT ? process.env.BLOCKLET_DEV_PORT : '';
430
+ }
431
+
432
+ const ports = {};
433
+ forEachBlockletSync(root, (x) => {
434
+ const webInterface = findWebInterface(x);
435
+ const envObj = x.meta?.did === blocklet.meta?.did ? environmentObj : x.environmentObj;
436
+ if (webInterface && envObj?.[webInterface.port]) {
437
+ ports[envObj.BLOCKLET_REAL_NAME] = envObj[webInterface.port];
438
+ }
439
+ });
440
+
441
+ const componentsInternalInfo = getComponentsInternalInfo(root);
442
+
443
+ // use index 1 as the path to derive deterministic encryption key for blocklet
444
+ const tmp = get(nodeEnvironments, 'ABT_NODE_SK')
445
+ ? getBlockletWallet(blocklet.meta.did, nodeEnvironments.ABT_NODE_SK, undefined, 1)
446
+ : null;
447
+
448
+ // For Access Key authentication, components should use root app's wallet
449
+ // This ensures consistent accessKeyId across parent and child components
450
+ const accessWallet = get(nodeEnvironments, 'ABT_NODE_SK')
451
+ ? getBlockletWallet(root.appDid || root.meta.did, nodeEnvironments.ABT_NODE_SK, undefined, 2)
452
+ : null;
453
+
454
+ const BLOCKLET_APP_IDS = getBlockletAppIdList(root).join(',');
455
+
456
+ const componentApiKey = getComponentApiKey({
457
+ serverSk: nodeEnvironments.ABT_NODE_SK,
458
+ app: root,
459
+ component: blocklet,
460
+ });
461
+
462
+ const blockletInfo = getBlockletInfo(blocklet, nodeEnvironments.ABT_NODE_SK, { returnWallet: true });
463
+
464
+ const rootBlockletInfo =
465
+ blocklet === root ? blockletInfo : getBlockletInfo(root, nodeEnvironments.ABT_NODE_SK, { returnWallet: true });
466
+
467
+ const { wallet } = rootBlockletInfo;
468
+ const appSk = toHex(wallet.secretKey);
469
+ const appPk = toHex(wallet.publicKey);
470
+
471
+ const ethWallet = fromSecretKey(appSk.slice(0, 66), 'ethereum');
472
+ const ethPk = toHex(ethWallet.publicKey);
473
+
474
+ const isMigrated = Array.isArray(root.migratedFrom) && root.migratedFrom.length > 0;
475
+ const appPsk = toHex(isMigrated ? root.migratedFrom[0].appSk : appSk);
476
+
477
+ // Calculate permanent public key (PPK)
478
+ const appPpk = isMigrated ? toHex(fromSecretKey(appPsk, wallet.type).publicKey) : appPk;
479
+ const ethPermanentWallet = fromSecretKey(appPsk.slice(0, 66), 'ethereum');
480
+ const appPpkEth = toHex(ethPermanentWallet.publicKey);
481
+
482
+ const env = {
483
+ ...blocklet.configObj,
484
+ ...getSharedConfigObj((ancestors || [])[0], blocklet, true),
485
+ ...environmentObj,
486
+ ...devEnvironments,
487
+ BLOCKLET_MOUNT_POINTS: JSON.stringify(componentsInternalInfo),
488
+ BLOCKLET_MODE: blocklet.mode || BLOCKLET_MODES.PRODUCTION,
489
+ BLOCKLET_APP_EK: tmp?.secretKey,
490
+ // for login token authentication
491
+ BLOCKLET_SESSION_SECRET: rootBlockletInfo.secret,
492
+ BLOCKLET_APP_VERSION: root.meta.version,
493
+ BLOCKLET_APP_IDS,
494
+ BLOCKLET_COMPONENT_API_KEY: componentApiKey,
495
+ BLOCKLET_APP_ASK: accessWallet?.secretKey,
496
+ ...nodeEnvironments,
497
+ ...safeNodeEnvironments,
498
+ BLOCKLET_APP_PPK: appPpk, // permanent pk corresponding to PSK
499
+ BLOCKLET_APP_PPK_ETH: appPpkEth, // permanent pk corresponding to PSK for ethereum
500
+ BLOCKLET_APP_PK: appPk,
501
+ BLOCKLET_APP_PK_ETH: ethPk,
502
+ };
503
+
504
+ const aigne = get(root, 'settings.aigne', {});
505
+ const salt = root.meta.did || AIGNE_CONFIG_ENCRYPT_SALT;
506
+ if (!isNil(aigne) && aigne.provider) {
507
+ const { key, accessKeyId, secretAccessKey, provider } = aigne;
508
+ const selectedModel = !aigne.model || aigne.model === 'auto' ? undefined : aigne.model;
509
+ env.BLOCKLET_AIGNE_API_MODEL = selectedModel;
510
+ env.BLOCKLET_AIGNE_API_PROVIDER = aigne.provider;
511
+ const credential = {
512
+ apiKey: key ? decrypt(key, salt, '') : key || '',
513
+ accessKeyId: accessKeyId && provider === 'bedrock' ? decrypt(accessKeyId, salt, '') : accessKeyId || '',
514
+ secretAccessKey:
515
+ secretAccessKey && provider === 'bedrock' ? decrypt(secretAccessKey, salt, '') : secretAccessKey || '',
516
+ };
517
+ env.BLOCKLET_AIGNE_API_CREDENTIAL = JSON.stringify(credential);
518
+ env.BLOCKLET_AIGNE_API_URL = aigne.url || '';
519
+ }
520
+
521
+ if (root?.environmentObj?.BLOCKLET_APP_DATA_DIR) {
522
+ env.BLOCKLET_APP_SHARE_DIR = path.join(root.environmentObj.BLOCKLET_APP_DATA_DIR, '.share');
523
+ env.BLOCKLET_SHARE_DIR = path.join(root.environmentObj.BLOCKLET_APP_DATA_DIR, '.share', blocklet.meta.did);
524
+ if (!fs.existsSync(env.BLOCKLET_APP_SHARE_DIR) && process.env.ABT_NODE_DATA_DIR) {
525
+ fs.mkdirSync(env.BLOCKLET_APP_SHARE_DIR, { recursive: true });
526
+ }
527
+ }
528
+
529
+ if (initialized) {
530
+ env.initialized = initialized;
531
+ }
532
+
533
+ if (isGreen && blocklet.greenPorts?.[BLOCKLET_DEFAULT_PORT_NAME]) {
534
+ env[BLOCKLET_DEFAULT_PORT_NAME] = blocklet.greenPorts[BLOCKLET_DEFAULT_PORT_NAME];
535
+ }
536
+
537
+ // ensure all envs are literals and do not contain line breaks
538
+ Object.keys(env).forEach((key) => {
539
+ env[key] = formatEnv(env[key]);
540
+ });
541
+
542
+ return env;
543
+ };
544
+
545
+ module.exports = {
546
+ PRIVATE_NODE_ENVS,
547
+ getComponentDirs,
548
+ getComponentStartEngine,
549
+ getBlockletConfigObj,
550
+ getAppSystemEnvironments,
551
+ getAppOverwrittenEnvironments,
552
+ getComponentSystemEnvironments,
553
+ fillBlockletConfigs,
554
+ getRuntimeEnvironments,
555
+ };