@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,611 @@
1
+ /* eslint-disable no-await-in-loop */
2
+ const uniq = require('lodash/uniq');
3
+ const logger = require('@abtnode/logger')('@abtnode/core:blocklet:manager:start');
4
+ const pAll = require('p-all');
5
+ const { BlockletEvents, BlockletInternalEvents, BlockletStatus, BlockletGroup } = require('@blocklet/constant');
6
+ const {
7
+ forEachBlocklet,
8
+ hasStartEngine,
9
+ hasRunnableComponent,
10
+ getDisplayName,
11
+ isExternalBlocklet,
12
+ getComponentMissingConfigs,
13
+ } = require('@blocklet/meta/lib/util');
14
+ const { getRequiredComponentsLayers } = require('@blocklet/meta/lib/get-required-components-layers');
15
+ const { getBlockletEngine } = require('@blocklet/meta/lib/engine');
16
+ const { getComponentsInternalInfo } = require('@blocklet/meta/lib/blocklet');
17
+
18
+ const states = require('../../../states');
19
+ const launcher = require('../../../util/launcher');
20
+ const hooks = require('../../hooks');
21
+ const { startLock } = require('../lock');
22
+ const {
23
+ startBlockletProcess,
24
+ getRuntimeEnvironments,
25
+ validateBlocklet,
26
+ validateBlockletChainInfo,
27
+ ensureAppPortsNotOccupied,
28
+ getHealthyCheckTimeout,
29
+ shouldSkipComponent,
30
+ getComponentNamesWithVersion,
31
+ getHookArgs,
32
+ } = require('../../../util/blocklet');
33
+ const checkNeedRunDocker = require('../../../util/docker/check-need-run-docker');
34
+ const { dockerExec } = require('../../../util/docker/docker-exec');
35
+ const { dockerExecChown } = require('../../../util/docker/docker-exec-chown');
36
+ const checkDockerRunHistory = require('../../../util/docker/check-docker-run-history');
37
+
38
+ /**
39
+ * Start required components by dependency order
40
+ */
41
+ async function startRequiredComponents(
42
+ manager,
43
+ {
44
+ componentDids,
45
+ inputComponentDids,
46
+ blocklet,
47
+ throwOnError,
48
+ checkHealthImmediately,
49
+ e2eMode,
50
+ context,
51
+ atomic,
52
+ onStarted,
53
+ onError,
54
+ }
55
+ ) {
56
+ if (!blocklet.children) {
57
+ return componentDids;
58
+ }
59
+ const targetDid = !inputComponentDids?.length ? componentDids?.[0] : inputComponentDids[0];
60
+
61
+ const canStartStatus = {
62
+ [BlockletStatus.installed]: true,
63
+ [BlockletStatus.error]: true,
64
+ [BlockletStatus.stopped]: true,
65
+ };
66
+
67
+ const requiredDidsLayers = getRequiredComponentsLayers({
68
+ targetDid,
69
+ children: blocklet.children,
70
+ filter: (child) => atomic || canStartStatus[child.status],
71
+ });
72
+
73
+ logger.info('start required components', {
74
+ did: blocklet.meta.did,
75
+ targetDid,
76
+ componentDids,
77
+ inputComponentDids,
78
+ checkHealthImmediately,
79
+ requiredDidsLayers,
80
+ });
81
+
82
+ if (requiredDidsLayers.length) {
83
+ // start by dependency
84
+ for (const dids of requiredDidsLayers) {
85
+ if (atomic) {
86
+ await _start(
87
+ manager,
88
+ { blocklet, throwOnError, checkHealthImmediately, e2eMode, componentDids: dids, onStarted, onError },
89
+ context
90
+ );
91
+ } else {
92
+ const tasks = dids.map(
93
+ (x) => () =>
94
+ _start(
95
+ manager,
96
+ { blocklet, throwOnError, checkHealthImmediately, e2eMode, componentDids: [x], onStarted, onError },
97
+ context
98
+ )
99
+ );
100
+ await pAll(tasks, { concurrency: 4 }).catch((err) => {
101
+ throw new Error(err.errors ? err.errors.join(', ') : err);
102
+ });
103
+ }
104
+ }
105
+ }
106
+
107
+ // remove the components that have just been started
108
+ const startedDids = new Set(requiredDidsLayers.flat());
109
+ return componentDids.filter((x) => !startedDids.has(x));
110
+ }
111
+
112
+ /**
113
+ * Start blocklet
114
+ */
115
+ async function start(
116
+ manager,
117
+ {
118
+ did,
119
+ throwOnError,
120
+ checkHealthImmediately = false,
121
+ e2eMode = false,
122
+ componentDids: inputComponentDids,
123
+ atomic,
124
+ operator,
125
+ },
126
+ context
127
+ ) {
128
+ const lockName = `${did}-start`;
129
+ const shouldLock = inputComponentDids?.length === 0;
130
+
131
+ if (shouldLock) {
132
+ await startLock.acquire(lockName);
133
+ }
134
+
135
+ try {
136
+ const blocklet = await manager.ensureBlocklet(did, { e2eMode });
137
+ const baseComponentDids = uniq(
138
+ inputComponentDids?.length ? inputComponentDids : blocklet.children.map((x) => x.meta.did)
139
+ );
140
+
141
+ try {
142
+ await manager.deleteProcess({ did, componentDids: baseComponentDids, isGreen: true });
143
+ } catch (error) {
144
+ logger.warn('Failed to delete process; this warning can be safely ignored.', { error });
145
+ }
146
+
147
+ await manager.checkControllerStatus(blocklet, 'start');
148
+
149
+ // 优先启动资源型组件
150
+ const componentDids = [];
151
+ const nonEntryComponentIds = [];
152
+ for (const b of blocklet.children) {
153
+ if (!b?.meta?.did) {
154
+ continue;
155
+ }
156
+ if (!baseComponentDids.includes(b.meta.did)) {
157
+ continue;
158
+ }
159
+ if (!hasStartEngine(b.meta)) {
160
+ nonEntryComponentIds.push(b.meta.did);
161
+ continue;
162
+ }
163
+ const engine = getBlockletEngine(b.meta);
164
+ if (engine.interpreter === 'blocklet') {
165
+ nonEntryComponentIds.push(b.meta.did);
166
+ continue;
167
+ }
168
+ componentDids.push(b.meta.did);
169
+ }
170
+
171
+ await manager.checkControllerStatus(blocklet, 'start');
172
+
173
+ if (nonEntryComponentIds.length) {
174
+ await states.blocklet.setBlockletStatus(did, BlockletStatus.running, {
175
+ componentDids: nonEntryComponentIds,
176
+ operator,
177
+ });
178
+ }
179
+ // sync component config before at first to ensure resource component config is ready
180
+ const serverSk = (await states.node.read()).sk;
181
+ await Promise.all(
182
+ componentDids.map((componentDid) =>
183
+ manager.configSynchronizer.syncComponentConfig(componentDid, blocklet.meta.did, { serverSk })
184
+ )
185
+ );
186
+
187
+ // 如果只有 nonEntryComponent,没有 entry component,直接触发 started 事件并返回
188
+ if (componentDids.length === 0) {
189
+ const finalBlocklet = await manager.getBlocklet(did);
190
+ await manager.configSynchronizer.throttledSyncAppConfig(finalBlocklet);
191
+ const componentsInfo = getComponentsInternalInfo(finalBlocklet);
192
+ manager.emit(BlockletInternalEvents.componentUpdated, {
193
+ appDid: blocklet.appDid,
194
+ components: componentsInfo,
195
+ });
196
+ manager.emit(BlockletInternalEvents.componentStarted, {
197
+ appDid: blocklet.appDid,
198
+ components: nonEntryComponentIds.map((x) => ({ did: x })),
199
+ });
200
+ manager.emit(BlockletEvents.statusChange, finalBlocklet);
201
+ manager.emit(BlockletEvents.started, { ...finalBlocklet, componentDids: nonEntryComponentIds });
202
+ launcher.notifyBlockletStarted(finalBlocklet);
203
+
204
+ // 根据情况更新 route table
205
+ if (!['true', '1'].includes(process.env.ABT_NODE_DISABLE_BLUE_GREEN)) {
206
+ manager.emit(BlockletEvents.blueOrGreenStarted, {
207
+ blocklet: finalBlocklet,
208
+ componentDids: inputComponentDids,
209
+ context,
210
+ });
211
+ }
212
+
213
+ return finalBlocklet;
214
+ }
215
+
216
+ const doc1 = await states.blocklet.setBlockletStatus(did, BlockletStatus.starting, {
217
+ componentDids,
218
+ operator,
219
+ });
220
+
221
+ manager.emit(BlockletEvents.statusChange, doc1);
222
+ const startedBlockletDids = [];
223
+ const errorBlockletDids = [];
224
+ const parentBlockletId = blocklet.id;
225
+
226
+ // Helper function to update child status immediately and emit events
227
+ const updateChildStatusImmediately = async (componentDid, status) => {
228
+ if (!states.blockletChild || !parentBlockletId) {
229
+ return;
230
+ }
231
+
232
+ try {
233
+ await states.blockletChild.updateChildStatus(parentBlockletId, componentDid, {
234
+ status,
235
+ operator,
236
+ });
237
+
238
+ // Get updated blocklet to emit events
239
+ const updatedBlocklet = await manager.getBlocklet(did);
240
+ const componentsInfo = getComponentsInternalInfo(updatedBlocklet);
241
+
242
+ manager.emit(BlockletInternalEvents.componentUpdated, {
243
+ appDid: blocklet.appDid,
244
+ components: componentsInfo.filter((c) => c.did === componentDid),
245
+ });
246
+
247
+ if (status === BlockletStatus.running) {
248
+ manager.emit(BlockletInternalEvents.componentStarted, {
249
+ appDid: blocklet.appDid,
250
+ components: [{ did: componentDid }],
251
+ });
252
+ }
253
+
254
+ manager.emit(BlockletEvents.statusChange, updatedBlocklet);
255
+ } catch (err) {
256
+ logger.error('Failed to update child status immediately', {
257
+ componentDid,
258
+ status,
259
+ error: err.message,
260
+ });
261
+ }
262
+ };
263
+
264
+ const notStartedComponentDids = await startRequiredComponents(manager, {
265
+ componentDids,
266
+ inputComponentDids,
267
+ blocklet,
268
+ throwOnError,
269
+ checkHealthImmediately,
270
+ e2eMode,
271
+ context,
272
+ atomic,
273
+ onStarted: async (subDid) => {
274
+ startedBlockletDids.push({ did: subDid });
275
+ await updateChildStatusImmediately(subDid, BlockletStatus.running);
276
+ },
277
+ onError: async (subDid, error) => {
278
+ errorBlockletDids.push({ did: subDid, error });
279
+ await updateChildStatusImmediately(subDid, BlockletStatus.error);
280
+ },
281
+ });
282
+
283
+ const tasks = notStartedComponentDids.map(
284
+ (componentDid) => () =>
285
+ _start(
286
+ manager,
287
+ {
288
+ blocklet,
289
+ throwOnError,
290
+ checkHealthImmediately,
291
+ e2eMode,
292
+ componentDids: [componentDid],
293
+ operator,
294
+ onStarted: async (subDid) => {
295
+ startedBlockletDids.push({ did: subDid });
296
+ await updateChildStatusImmediately(subDid, BlockletStatus.running);
297
+ },
298
+ onError: async (subDid, error) => {
299
+ errorBlockletDids.push({ did: subDid, error });
300
+ await updateChildStatusImmediately(subDid, BlockletStatus.error);
301
+ },
302
+ },
303
+ context
304
+ )
305
+ );
306
+
307
+ await pAll(tasks, { concurrency: 6 });
308
+
309
+ // Sync parent blocklet uptime status once after all components are processed
310
+ await states.blocklet.syncUptimeStatus(parentBlockletId);
311
+
312
+ const nextBlocklet = await manager.ensureBlocklet(did, { e2eMode });
313
+ let errorDescription = '';
314
+ let resultBlocklet = nextBlocklet;
315
+
316
+ // Status updates are now done immediately in callbacks, so we only need to handle final events and cleanup
317
+ if (startedBlockletDids.length) {
318
+ const finalBlocklet = await manager.getBlocklet(did);
319
+ resultBlocklet = finalBlocklet;
320
+
321
+ // Sync app config after all components started
322
+ await manager.configSynchronizer.throttledSyncAppConfig(finalBlocklet, { wait: 200 });
323
+ const componentsInfo = getComponentsInternalInfo(finalBlocklet);
324
+ manager.emit(BlockletInternalEvents.componentUpdated, {
325
+ appDid: blocklet.appDid,
326
+ components: componentsInfo,
327
+ });
328
+
329
+ const allStartedComponentDids = uniq([
330
+ ...(componentDids || []),
331
+ ...nonEntryComponentIds,
332
+ ...startedBlockletDids.map((x) => x.did),
333
+ ]);
334
+ manager.emit(BlockletInternalEvents.componentStarted, {
335
+ appDid: blocklet.appDid,
336
+ components: allStartedComponentDids.map((x) => ({ did: x })),
337
+ });
338
+
339
+ manager.emit(BlockletEvents.statusChange, finalBlocklet);
340
+ manager.emit(BlockletEvents.started, { ...finalBlocklet, componentDids: allStartedComponentDids });
341
+
342
+ launcher.notifyBlockletStarted(finalBlocklet);
343
+ }
344
+
345
+ if (errorBlockletDids.length) {
346
+ const error = errorBlockletDids[0];
347
+ for (const item of errorBlockletDids) {
348
+ logger.error('Failed to start blocklet', {
349
+ error: item.error,
350
+ title: nextBlocklet.meta.title,
351
+ name: getComponentNamesWithVersion(nextBlocklet, [item.did]),
352
+ });
353
+ }
354
+
355
+ errorDescription = `${getComponentNamesWithVersion(
356
+ nextBlocklet,
357
+ errorBlockletDids.map((x) => x.did)
358
+ )} start failed for ${getDisplayName(nextBlocklet)}: ${errorBlockletDids.map((x) => x.error.message).join('. ')}`;
359
+ manager._createNotification(did, {
360
+ title: `${errorBlockletDids.length > 1 ? `${errorBlockletDids.length} components` : 'Component'} failed to start for ${getDisplayName(nextBlocklet)}`,
361
+ description: errorDescription,
362
+ entityType: 'blocklet',
363
+ entityId: did,
364
+ severity: 'error',
365
+ });
366
+
367
+ await manager.deleteProcess({
368
+ did,
369
+ componentDids: errorBlockletDids.map((x) => x.did),
370
+ shouldUpdateBlockletStatus: false,
371
+ });
372
+
373
+ const finalBlocklet = await manager.getBlocklet(did);
374
+ resultBlocklet = finalBlocklet;
375
+ manager.emit(BlockletEvents.startFailed, {
376
+ ...finalBlocklet,
377
+ componentDids: errorBlockletDids.map((x) => x.did),
378
+ error: { message: error.message },
379
+ });
380
+ manager.emit(BlockletEvents.statusChange, { ...finalBlocklet, error: { message: error.message } });
381
+ }
382
+
383
+ // 根据情况更新 route table, 会判断只有包含多 interfaces 的 DID 才会更新 route table
384
+ if (!['true', '1'].includes(process.env.ABT_NODE_DISABLE_BLUE_GREEN)) {
385
+ manager.emit(BlockletEvents.blueOrGreenStarted, {
386
+ blocklet: resultBlocklet,
387
+ componentDids: inputComponentDids,
388
+ context,
389
+ });
390
+ }
391
+
392
+ if (throwOnError && errorDescription) {
393
+ throw new Error(errorDescription);
394
+ }
395
+
396
+ return resultBlocklet;
397
+ } finally {
398
+ if (shouldLock) {
399
+ await startLock.releaseLock(lockName);
400
+ }
401
+ }
402
+ }
403
+
404
+ /**
405
+ * Internal start implementation
406
+ */
407
+ async function _start(
408
+ manager,
409
+ {
410
+ blocklet: inputBlocklet,
411
+ did,
412
+ throwOnError,
413
+ checkHealthImmediately = false,
414
+ e2eMode = false,
415
+ componentDids,
416
+ operator: _operator,
417
+ onStarted,
418
+ onError,
419
+ },
420
+ context
421
+ ) {
422
+ const operator = _operator || context?.user?.did;
423
+
424
+ logger.info('start blocklet', {
425
+ did: did || inputBlocklet.meta.did,
426
+ componentDids,
427
+ throwOnError,
428
+ checkHealthImmediately,
429
+ e2eMode,
430
+ operator,
431
+ });
432
+ // should check blocklet integrity
433
+ const blocklet1 = inputBlocklet || (await manager.ensureBlocklet(did, { e2eMode }));
434
+
435
+ did = blocklet1.meta.did; // eslint-disable-line no-param-reassign
436
+
437
+ // validate requirement and engine
438
+ await validateBlocklet(blocklet1);
439
+ await validateBlockletChainInfo(blocklet1);
440
+
441
+ if (!hasRunnableComponent(blocklet1)) {
442
+ throw new Error('No runnable component found');
443
+ }
444
+
445
+ const entryComponentIds = [];
446
+ const nonEntryComponentIds = [];
447
+ const componentDidsSet = new Set(componentDids);
448
+ try {
449
+ await forEachBlocklet(
450
+ blocklet1,
451
+ (b) => {
452
+ if (!componentDidsSet.has(b.meta.did)) {
453
+ return;
454
+ }
455
+
456
+ if (b.meta.group === BlockletGroup.gateway) {
457
+ nonEntryComponentIds.push(b.meta.did);
458
+ return;
459
+ }
460
+
461
+ const engine = getBlockletEngine(b.meta);
462
+ if (engine.interpreter === 'blocklet') {
463
+ nonEntryComponentIds.push(b.meta.did);
464
+ return;
465
+ }
466
+
467
+ if (!hasStartEngine(b.meta)) {
468
+ nonEntryComponentIds.push(b.meta.did);
469
+ return;
470
+ }
471
+ entryComponentIds.push(b.meta.did);
472
+ },
473
+ { parallel: true, concurrencyLimit: 4 }
474
+ );
475
+ if (nonEntryComponentIds.length) {
476
+ for (const subDid of nonEntryComponentIds) {
477
+ onStarted?.(subDid);
478
+ }
479
+ }
480
+ } catch (err) {
481
+ logger.error('Failed to categorize components into entry and non-entry types', { error: err.message });
482
+ throw err;
483
+ }
484
+
485
+ if (!entryComponentIds.length) {
486
+ return;
487
+ }
488
+
489
+ try {
490
+ // check required config
491
+ for (const component of blocklet1.children) {
492
+ if (!entryComponentIds.includes(component.meta.did)) {
493
+ continue;
494
+ }
495
+ if (!shouldSkipComponent(component.meta.did, entryComponentIds)) {
496
+ const missingProps = getComponentMissingConfigs(component, blocklet1);
497
+ if (missingProps.length) {
498
+ throw new Error(
499
+ `Missing required configuration to start ${component.meta.title}: ${missingProps
500
+ .map((x) => x.key)
501
+ .join(',')}`
502
+ );
503
+ }
504
+ }
505
+ }
506
+
507
+ blocklet1.status = BlockletStatus.starting;
508
+ const blocklet = await ensureAppPortsNotOccupied({
509
+ blocklet: blocklet1,
510
+ componentDids: entryComponentIds,
511
+ states,
512
+ manager,
513
+ });
514
+
515
+ const nodeInfo = await states.node.read();
516
+
517
+ const nodeEnvironments = await states.node.getEnvironments(nodeInfo);
518
+
519
+ if (checkDockerRunHistory(nodeInfo)) {
520
+ const needChownDirs = [];
521
+ await forEachBlocklet(blocklet, (b, { ancestors }) => {
522
+ if (!b.meta?.docker?.image || b.meta?.docker?.runBaseScript) {
523
+ return;
524
+ }
525
+ const env = getRuntimeEnvironments(b, nodeEnvironments, ancestors);
526
+ needChownDirs.push(env.BLOCKLET_APP_DIR);
527
+ needChownDirs.push(env.BLOCKLET_DATA_DIR);
528
+ needChownDirs.push(env.BLOCKLET_LOG_DIR);
529
+ });
530
+ await dockerExecChown({ name: `${blocklet?.meta?.did}-before-start`, dirs: needChownDirs });
531
+ }
532
+
533
+ const getHookFn =
534
+ (hookName) =>
535
+ async (b, { env }) => {
536
+ const hookArgs = getHookArgs(b);
537
+ const needRunDocker = await checkNeedRunDocker(b.meta, env, nodeInfo, isExternalBlocklet(blocklet));
538
+ if (!b.meta.scripts?.[hookName]) {
539
+ return null;
540
+ }
541
+ if (needRunDocker) {
542
+ return dockerExec({
543
+ blocklet,
544
+ meta: b.meta,
545
+ script: b.meta.scripts?.[hookName],
546
+ hookName,
547
+ nodeInfo,
548
+ env,
549
+ ...hookArgs,
550
+ });
551
+ }
552
+ return hooks[hookName](b, {
553
+ appDir: b.env.appDir,
554
+ hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
555
+ env,
556
+ did, // root blocklet did,
557
+ teamManager: manager.teamManager,
558
+ ...hookArgs,
559
+ });
560
+ };
561
+
562
+ await startBlockletProcess(blocklet, {
563
+ ...context,
564
+ preFlight: getHookFn('preFlight'),
565
+ preStart: getHookFn('preStart'),
566
+ postStart: getHookFn('postStart'),
567
+ nodeEnvironments,
568
+ nodeInfo: await states.node.read(),
569
+ e2eMode,
570
+ componentDids: entryComponentIds,
571
+ configSynchronizer: manager.configSynchronizer,
572
+ });
573
+
574
+ // check blocklet healthy
575
+ const { startTimeout, minConsecutiveTime } = getHealthyCheckTimeout(blocklet, {
576
+ checkHealthImmediately,
577
+ componentDids,
578
+ });
579
+
580
+ const params = {
581
+ did,
582
+ context,
583
+ minConsecutiveTime,
584
+ timeout: startTimeout,
585
+ componentDids: entryComponentIds,
586
+ };
587
+
588
+ await manager._onCheckIfStarted(params, { throwOnError });
589
+
590
+ for (const subDid of entryComponentIds) {
591
+ onStarted?.(subDid);
592
+ }
593
+ } catch (err) {
594
+ const status = await states.blocklet.getBlockletStatus(did);
595
+ if ([BlockletStatus.stopping, BlockletStatus.stopped].includes(status)) {
596
+ logger.info('Failed to start blocklet maybe due to manually stopped');
597
+ }
598
+ for (const subDid of entryComponentIds) {
599
+ onError?.(subDid, err);
600
+ }
601
+ if (throwOnError) {
602
+ throw err;
603
+ }
604
+ }
605
+ }
606
+
607
+ module.exports = {
608
+ startRequiredComponents,
609
+ start,
610
+ _start,
611
+ };