@abtnode/core 1.16.32 → 1.16.33-beta-20240929-110332-9d5dfdc4
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/api/team.js +3 -3
- package/lib/blocklet/manager/disk.js +9 -4
- package/lib/states/audit-log.js +1 -12
- package/lib/util/blocklet.js +12 -4
- package/lib/util/docker/alpine-dockerfile.js +31 -0
- package/lib/util/docker/blocklet-custom-dockerfile.js +92 -0
- package/lib/util/docker/create-docker-image.js +17 -54
- package/lib/util/docker/parse-docker-options-from-pm2.js +141 -90
- package/package.json +22 -22
package/lib/api/team.js
CHANGED
|
@@ -1740,7 +1740,7 @@ class TeamAPI extends EventEmitter {
|
|
|
1740
1740
|
userSessions: [userSession],
|
|
1741
1741
|
},
|
|
1742
1742
|
});
|
|
1743
|
-
logger.
|
|
1743
|
+
logger.debug('Sync userSession to federated site successfully', {
|
|
1744
1744
|
userDid,
|
|
1745
1745
|
visitorId,
|
|
1746
1746
|
ua,
|
|
@@ -1751,8 +1751,8 @@ class TeamAPI extends EventEmitter {
|
|
|
1751
1751
|
extra,
|
|
1752
1752
|
});
|
|
1753
1753
|
} catch (err) {
|
|
1754
|
-
logger.
|
|
1755
|
-
'
|
|
1754
|
+
logger.error(
|
|
1755
|
+
'Sync userSession to federated site failed',
|
|
1756
1756
|
{
|
|
1757
1757
|
userDid,
|
|
1758
1758
|
visitorId,
|
|
@@ -102,7 +102,7 @@ const isFunction = require('lodash/isFunction');
|
|
|
102
102
|
const { encode } = require('@abtnode/util/lib/base32');
|
|
103
103
|
const formatContext = require('@abtnode/util/lib/format-context');
|
|
104
104
|
const md5 = require('@abtnode/util/lib/md5');
|
|
105
|
-
const { callFederated } = require('@abtnode/auth/lib/util/federated');
|
|
105
|
+
const { callFederated, isMaster } = require('@abtnode/auth/lib/util/federated');
|
|
106
106
|
const pAll = require('p-all');
|
|
107
107
|
const toBlockletDid = require('@blocklet/meta/lib/did');
|
|
108
108
|
const launcher = require('../../util/launcher');
|
|
@@ -4622,7 +4622,7 @@ class FederatedBlockletManager extends DiskBlockletManager {
|
|
|
4622
4622
|
sites: [],
|
|
4623
4623
|
});
|
|
4624
4624
|
const masterSite = federated.sites[0];
|
|
4625
|
-
if (masterSite && masterSite
|
|
4625
|
+
if (masterSite && isMaster(masterSite)) {
|
|
4626
4626
|
const nodeInfo = await states.node.read();
|
|
4627
4627
|
const blockletInfo = getBlockletInfo(blocklet, nodeInfo.sk);
|
|
4628
4628
|
const { permanentWallet } = blockletInfo;
|
|
@@ -4673,7 +4673,7 @@ class FederatedBlockletManager extends DiskBlockletManager {
|
|
|
4673
4673
|
});
|
|
4674
4674
|
const masterSite = federated.sites[0];
|
|
4675
4675
|
// 只有 Master 可以调用这个逻辑
|
|
4676
|
-
if (masterSite && masterSite
|
|
4676
|
+
if (masterSite && isMaster(masterSite) && masterSite.appPid === did) {
|
|
4677
4677
|
const nodeInfo = await states.node.read();
|
|
4678
4678
|
const blockletInfo = getBlockletInfo(blocklet, nodeInfo.sk);
|
|
4679
4679
|
const { permanentWallet } = blockletInfo;
|
|
@@ -4938,9 +4938,14 @@ class FederatedBlockletManager extends DiskBlockletManager {
|
|
|
4938
4938
|
site: item,
|
|
4939
4939
|
data: safeData,
|
|
4940
4940
|
});
|
|
4941
|
+
logger.debug('Sync federated sites successfully', {
|
|
4942
|
+
action: 'sync',
|
|
4943
|
+
site: item,
|
|
4944
|
+
data: safeData,
|
|
4945
|
+
});
|
|
4941
4946
|
return result;
|
|
4942
4947
|
} catch (error) {
|
|
4943
|
-
logger.
|
|
4948
|
+
logger.error('Failed to sync federated sites', {
|
|
4944
4949
|
error,
|
|
4945
4950
|
action: 'sync',
|
|
4946
4951
|
site: item,
|
package/lib/states/audit-log.js
CHANGED
|
@@ -194,7 +194,7 @@ const getLogContent = async (action, args, context, result, info, node) => {
|
|
|
194
194
|
// blocklet federated config
|
|
195
195
|
case 'requestJoinFederated':
|
|
196
196
|
return `request join in federated: member site ${getSiteInfo(
|
|
197
|
-
args.
|
|
197
|
+
args.memberSite
|
|
198
198
|
)} applied to join master site ${getBlockletInfo(result, info)}`;
|
|
199
199
|
case 'quitFederated':
|
|
200
200
|
return `quit federated: member site ${getSiteInfo(args.memberSite)} quit ${getBlockletInfo(result, info)}`;
|
|
@@ -204,16 +204,6 @@ const getLogContent = async (action, args, context, result, info, node) => {
|
|
|
204
204
|
return `audit federated: master site ${getSiteInfo(
|
|
205
205
|
args.masterSite
|
|
206
206
|
)} audit federated login member site ${getBlockletInfo(result, info)} with status: ${args.status}`;
|
|
207
|
-
case 'syncFederated':
|
|
208
|
-
const syncData = {
|
|
209
|
-
users: args.users,
|
|
210
|
-
sites: args.sites,
|
|
211
|
-
userSessions: args.userSessions,
|
|
212
|
-
};
|
|
213
|
-
return `sync federated: site ${getSiteInfo(args.callerSite)} sync data:
|
|
214
|
-
\`\`\`json
|
|
215
|
-
${JSON.stringify(syncData, null, 2)}
|
|
216
|
-
\`\`\``;
|
|
217
207
|
|
|
218
208
|
// federated user relate
|
|
219
209
|
case 'loginFederated':
|
|
@@ -415,7 +405,6 @@ const getLogCategory = (action) => {
|
|
|
415
405
|
case 'requestQuitFederated':
|
|
416
406
|
case 'disbandFederated':
|
|
417
407
|
case 'auditFederated':
|
|
418
|
-
case 'syncFederated':
|
|
419
408
|
return 'blocklet';
|
|
420
409
|
|
|
421
410
|
// store,此处应该返回 server
|
package/lib/util/blocklet.js
CHANGED
|
@@ -69,6 +69,7 @@ const {
|
|
|
69
69
|
BLOCKLET_RESOURCE_DIR,
|
|
70
70
|
BLOCKLET_TENANT_MODES,
|
|
71
71
|
PROJECT,
|
|
72
|
+
BLOCKLET_INTERFACE_TYPE_DOCKER,
|
|
72
73
|
} = require('@blocklet/constant');
|
|
73
74
|
const validateBlockletEntry = require('@blocklet/meta/lib/entry');
|
|
74
75
|
const { getBlockletEngine } = require('@blocklet/meta/lib/engine');
|
|
@@ -98,6 +99,7 @@ const getComponentProcessId = require('@blocklet/meta/lib/get-component-process-
|
|
|
98
99
|
const { isInServerlessMode } = require('@abtnode/util/lib/serverless');
|
|
99
100
|
const md5 = require('@abtnode/util/lib/md5');
|
|
100
101
|
|
|
102
|
+
const ensureDockerEndpointHealthy = require('@abtnode/util/lib/ensure-docker-endpoint-healthy');
|
|
101
103
|
const { validate: validateEngine, get: getEngine } = require('../blocklet/manager/engine');
|
|
102
104
|
|
|
103
105
|
const isRequirementsSatisfied = require('./requirement');
|
|
@@ -689,7 +691,7 @@ const startBlockletProcess = async (
|
|
|
689
691
|
const nextOptions =
|
|
690
692
|
b.meta.group === 'static' || b.mode === BLOCKLET_MODES.DEVELOPMENT
|
|
691
693
|
? options
|
|
692
|
-
: await parseDockerOptionsFromPm2(options, nodeInfo);
|
|
694
|
+
: await parseDockerOptionsFromPm2({ options, nodeInfo, meta: b.meta, ports: b.ports });
|
|
693
695
|
|
|
694
696
|
await pm2.startAsync(nextOptions);
|
|
695
697
|
|
|
@@ -887,7 +889,9 @@ const _checkProcessHealthy = async (blocklet, { minConsecutiveTime, timeout, log
|
|
|
887
889
|
const { processId } = env;
|
|
888
890
|
|
|
889
891
|
const webInterface = (meta.interfaces || []).find((x) => x.type === BLOCKLET_INTERFACE_TYPE_WEB);
|
|
890
|
-
|
|
892
|
+
const dockerInterface = (meta.interfaces || []).find((x) => x.type === BLOCKLET_INTERFACE_TYPE_DOCKER);
|
|
893
|
+
|
|
894
|
+
if (!webInterface && !dockerInterface) {
|
|
891
895
|
// TODO: how do we check healthy for service interfaces
|
|
892
896
|
throw new Error(`Blocklet ${name} does not have any web interface`);
|
|
893
897
|
}
|
|
@@ -915,7 +919,7 @@ const _checkProcessHealthy = async (blocklet, { minConsecutiveTime, timeout, log
|
|
|
915
919
|
throw new Error('process not start within 10s');
|
|
916
920
|
}
|
|
917
921
|
|
|
918
|
-
const port = findInterfacePortByName({ meta, ports }, webInterface.name);
|
|
922
|
+
const port = findInterfacePortByName({ meta, ports }, (webInterface || dockerInterface).name);
|
|
919
923
|
if (logToTerminal) {
|
|
920
924
|
// eslint-disable-next-line no-console
|
|
921
925
|
console.log(
|
|
@@ -925,7 +929,11 @@ const _checkProcessHealthy = async (blocklet, { minConsecutiveTime, timeout, log
|
|
|
925
929
|
);
|
|
926
930
|
}
|
|
927
931
|
try {
|
|
928
|
-
|
|
932
|
+
if (webInterface) {
|
|
933
|
+
await ensureEndpointHealthy({ port, minConsecutiveTime, timeout });
|
|
934
|
+
} else {
|
|
935
|
+
await ensureDockerEndpointHealthy({ hostIP: dockerInterface.hostIP, port, minConsecutiveTime, timeout });
|
|
936
|
+
}
|
|
929
937
|
} catch (error) {
|
|
930
938
|
logger.error('ensure endpoint healthy failed', { port, minConsecutiveTime, timeout });
|
|
931
939
|
throw error;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const dockerfile = `
|
|
2
|
+
# Base image
|
|
3
|
+
FROM node:22-alpine
|
|
4
|
+
|
|
5
|
+
RUN apk add --no-cache python3 make gcc g++ build-base socat curl gnupg wget su-exec shadow git ca-certificates cairo-dev pango-dev jpeg-dev giflib-dev curl build-essential
|
|
6
|
+
|
|
7
|
+
RUN ln -sf /usr/bin/python3 /usr/bin/python
|
|
8
|
+
|
|
9
|
+
ENV DOCKER_DATA /var/lib/blocklet
|
|
10
|
+
VOLUME /var/lib/blocklet
|
|
11
|
+
|
|
12
|
+
RUN mkdir -p /var/lib/blocklet
|
|
13
|
+
|
|
14
|
+
ENV NPM_CONFIG_CACHE=/var/lib/blocklet/.npm
|
|
15
|
+
|
|
16
|
+
RUN npm install -g pnpm nr
|
|
17
|
+
|
|
18
|
+
ENV PATH=/sbin:$PATH
|
|
19
|
+
|
|
20
|
+
WORKDIR /var/lib/blocklet
|
|
21
|
+
`.trim();
|
|
22
|
+
|
|
23
|
+
module.exports = {
|
|
24
|
+
dockerfile,
|
|
25
|
+
image: 'node_alpine_v3',
|
|
26
|
+
shell: 'su-exec',
|
|
27
|
+
network: '--network host',
|
|
28
|
+
baseDir: '/var/lib/blocklet',
|
|
29
|
+
installNodeModules: true,
|
|
30
|
+
runBaseScript: true,
|
|
31
|
+
};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const md5 = require('@abtnode/util/lib/md5');
|
|
4
|
+
const alpineDockerfile = require('./alpine-dockerfile');
|
|
5
|
+
|
|
6
|
+
function getBlockletCustomDockerfile({ appDir, dataDir, meta, ports }) {
|
|
7
|
+
const dockerMeta = meta.docker || {};
|
|
8
|
+
if (!dockerMeta.image && !dockerMeta.dockerfile) {
|
|
9
|
+
return alpineDockerfile;
|
|
10
|
+
}
|
|
11
|
+
let file = '';
|
|
12
|
+
if (dockerMeta.dockerfile) {
|
|
13
|
+
// 为了安全性, 默认不允许 build custom dockerfile
|
|
14
|
+
if (!process.env.CAN_BUILD_DOCKER_FILE) {
|
|
15
|
+
throw new Error('Cannot build dockerfile');
|
|
16
|
+
}
|
|
17
|
+
const dockerfilePath = path.join(appDir, dockerMeta.dockerfile);
|
|
18
|
+
if (!fs.existsSync(dockerfilePath)) {
|
|
19
|
+
throw new Error('Dockerfile not found');
|
|
20
|
+
}
|
|
21
|
+
file = fs.readFileSync(dockerfilePath, 'utf8').trim();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const hash = md5(file);
|
|
25
|
+
let shell;
|
|
26
|
+
if (dockerMeta.shell) {
|
|
27
|
+
shell = dockerMeta.shell;
|
|
28
|
+
} else if (/su-exec/.test(file)) {
|
|
29
|
+
shell = 'su-exec';
|
|
30
|
+
} else if (/gosu/.test(file)) {
|
|
31
|
+
shell = 'gosu';
|
|
32
|
+
} else {
|
|
33
|
+
shell = 'sh';
|
|
34
|
+
}
|
|
35
|
+
const interfaceMeta = (meta.interfaces || [])
|
|
36
|
+
.map((item) => (item.containerPort ? item : null))
|
|
37
|
+
.filter((item) => item !== null);
|
|
38
|
+
|
|
39
|
+
let baseDir = '/var/lib/blocklet';
|
|
40
|
+
if (dockerMeta.workdir) {
|
|
41
|
+
baseDir = dockerMeta.workdir;
|
|
42
|
+
} else {
|
|
43
|
+
const workDir = file.match(/WORKDIR\s+(.+)/)?.[1];
|
|
44
|
+
if (workDir) {
|
|
45
|
+
baseDir = workDir;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const volumes = (dockerMeta.volumes || [])
|
|
50
|
+
.map((volume) => {
|
|
51
|
+
const [a, b] = volume.split(':');
|
|
52
|
+
const hostDir = a.replace('$BLOCKLET_DATA_DIR', dataDir).replace('$BLOCKLET_APP_DIR', appDir);
|
|
53
|
+
return ` -v ${hostDir}:${b} `;
|
|
54
|
+
})
|
|
55
|
+
.join('');
|
|
56
|
+
|
|
57
|
+
let network = '';
|
|
58
|
+
if (interfaceMeta.length > 0) {
|
|
59
|
+
network = interfaceMeta
|
|
60
|
+
.map((item) => {
|
|
61
|
+
const port = ports[item.port];
|
|
62
|
+
return port ? ` -p ${item.hostIP || '127.0.0.1'}:${port}:${item.containerPort}` : '';
|
|
63
|
+
})
|
|
64
|
+
.join('')
|
|
65
|
+
.trim();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!network.length) {
|
|
69
|
+
network = `--network ${dockerMeta.network || 'host'}`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// 防止 dockerImage 和 docker args, dockerScript 会注入 -v 等命令
|
|
73
|
+
const dockerArgs = [dockerMeta.command, dockerMeta.script, dockerMeta.image].filter(Boolean).join(' ');
|
|
74
|
+
if (/(\s|^)-v\s/.test(dockerArgs)) {
|
|
75
|
+
throw new Error('Detected forbidden volume mount (-v) in docker args');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
dockerfile: file,
|
|
80
|
+
image: dockerMeta.image || `blocklet_custom_${hash.slice(0, 16)}`,
|
|
81
|
+
shell,
|
|
82
|
+
baseDir,
|
|
83
|
+
volumes,
|
|
84
|
+
network,
|
|
85
|
+
runBaseScript: !!dockerMeta.runBaseScript,
|
|
86
|
+
args: dockerMeta.command || '',
|
|
87
|
+
script: dockerMeta.script || dockerMeta.installNodeModules ? '' : 'tail -f /dev/null',
|
|
88
|
+
installNodeModules: !!dockerMeta.installNodeModules,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
module.exports = getBlockletCustomDockerfile;
|
|
@@ -3,57 +3,19 @@ const path = require('path');
|
|
|
3
3
|
const { execSync } = require('child_process');
|
|
4
4
|
const logger = require('@abtnode/logger')('@abtnode/core:util:blocklet');
|
|
5
5
|
const { checkDockerHasImage } = require('./check-docker-has-image');
|
|
6
|
+
const getBlockletCustomDockerfile = require('./blocklet-custom-dockerfile');
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
async function buildImage() {
|
|
8
|
+
async function buildImage({ image, dockerfile }) {
|
|
10
9
|
const dir = process.env.ABT_NODE_DATA_DIR;
|
|
11
10
|
if (!dir) {
|
|
12
11
|
logger.error('ABT_NODE_DATA_DIR is not set');
|
|
13
12
|
return;
|
|
14
13
|
}
|
|
15
|
-
|
|
14
|
+
|
|
15
|
+
if (await checkDockerHasImage(image)) {
|
|
16
16
|
return;
|
|
17
17
|
}
|
|
18
18
|
const dockerfilePath = path.join(dir, 'Dockerfile');
|
|
19
|
-
const dockerfileContent = `
|
|
20
|
-
# Base image
|
|
21
|
-
FROM node:22-alpine
|
|
22
|
-
|
|
23
|
-
# Install necessary packages
|
|
24
|
-
RUN apk add --no-cache python3 make gcc g++ build-base socat curl gnupg wget su-exec shadow git ca-certificates cairo-dev pango-dev jpeg-dev giflib-dev curl
|
|
25
|
-
|
|
26
|
-
# Create a symlink for python if necessary
|
|
27
|
-
RUN ln -sf /usr/bin/python3 /usr/bin/python
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
# RUN install --verbose --directory --owner blocklet --group blocklet --mode 3777 /var/run/blocklet
|
|
31
|
-
|
|
32
|
-
ENV DOCKER_DATA /var/lib/blocklet
|
|
33
|
-
VOLUME /var/lib/blocklet
|
|
34
|
-
|
|
35
|
-
RUN mkdir -p /var/lib/blocklet
|
|
36
|
-
|
|
37
|
-
ENV NPM_CONFIG_CACHE=/var/lib/blocklet/.npm
|
|
38
|
-
|
|
39
|
-
# Set appropriate ownership and permission
|
|
40
|
-
RUN mkdir -p /var/lib/blocklet/.npm
|
|
41
|
-
RUN mkdir -p /var/lib/blocklet/logs
|
|
42
|
-
RUN mkdir -p /var/lib/blocklet/cache
|
|
43
|
-
RUN mkdir -p /var/lib/blocklet/data
|
|
44
|
-
RUN mkdir -p /var/lib/blocklet/blocklets
|
|
45
|
-
RUN chmod -R 777 /var/lib/blocklet
|
|
46
|
-
|
|
47
|
-
# Install global npm packages
|
|
48
|
-
RUN npm install -g pnpm nr
|
|
49
|
-
|
|
50
|
-
# Ensure correct PATH
|
|
51
|
-
ENV PATH=/sbin:$PATH
|
|
52
|
-
|
|
53
|
-
# Set working directory
|
|
54
|
-
WORKDIR /var/lib/blocklet
|
|
55
|
-
|
|
56
|
-
`.trim();
|
|
57
19
|
|
|
58
20
|
try {
|
|
59
21
|
await fs.access(dockerfilePath);
|
|
@@ -62,36 +24,37 @@ WORKDIR /var/lib/blocklet
|
|
|
62
24
|
// File doesn't exist, no need to remove
|
|
63
25
|
}
|
|
64
26
|
|
|
65
|
-
await fs.writeFile(dockerfilePath,
|
|
27
|
+
await fs.writeFile(dockerfilePath, dockerfile);
|
|
66
28
|
try {
|
|
67
29
|
execSync(
|
|
68
|
-
`docker build -f ${path.join(dir, 'Dockerfile')} --no-cache --build-arg uid=$(id -u) --build-arg gid=$(id -g) -t ${
|
|
30
|
+
`docker build -f ${path.join(dir, 'Dockerfile')} --no-cache --build-arg uid=$(id -u) --build-arg gid=$(id -g) -t ${image} .`,
|
|
69
31
|
{
|
|
70
32
|
stdio: 'inherit',
|
|
71
33
|
encoding: 'utf-8',
|
|
72
34
|
}
|
|
73
35
|
);
|
|
74
36
|
} catch (error) {
|
|
75
|
-
// 如果 docker 会报错: ERROR: failed to solve: failed to read dockerfile: open /var/snap/docker/common/var-lib-docker/tmp/buildkit-mount875872699/Dockerfile: no such file or directory
|
|
76
|
-
// 表示 docker 没有权限访问 ABT_NODE_DATA_DIR 目录, 后续的一系列操作也会失败
|
|
77
37
|
logger.error('Failed to build Docker image. Error:', error.message);
|
|
78
38
|
throw error;
|
|
79
39
|
}
|
|
80
40
|
logger.info('Success build docker image');
|
|
81
41
|
}
|
|
82
42
|
|
|
83
|
-
|
|
43
|
+
const building = {};
|
|
84
44
|
|
|
85
|
-
async function createDockerImage() {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
45
|
+
async function createDockerImage(data) {
|
|
46
|
+
const dockerfileInfo = getBlockletCustomDockerfile(data);
|
|
47
|
+
if (building[dockerfileInfo.image]) {
|
|
48
|
+
await building[dockerfileInfo.image];
|
|
49
|
+
return dockerfileInfo;
|
|
50
|
+
}
|
|
51
|
+
if (dockerfileInfo.dockerfile) {
|
|
52
|
+
building[dockerfileInfo.image] = buildImage(dockerfileInfo);
|
|
53
|
+
await building[dockerfileInfo.image];
|
|
89
54
|
}
|
|
90
|
-
|
|
91
|
-
await building;
|
|
55
|
+
return dockerfileInfo;
|
|
92
56
|
}
|
|
93
57
|
|
|
94
58
|
module.exports = {
|
|
95
59
|
createDockerImage,
|
|
96
|
-
DOCKER_IMAGE_NAME,
|
|
97
60
|
};
|
|
@@ -4,10 +4,10 @@ const { stringify: stringifyEnvFile } = require('envfile');
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const fs = require('fs/promises');
|
|
6
6
|
const os = require('os');
|
|
7
|
-
const { existsSync } = require('fs-extra');
|
|
7
|
+
const { existsSync, writeFileSync } = require('fs-extra');
|
|
8
8
|
const { NODE_MODES } = require('@abtnode/constant');
|
|
9
9
|
const parseDockerName = require('./parse-docker-name');
|
|
10
|
-
const {
|
|
10
|
+
const { createDockerImage } = require('./create-docker-image');
|
|
11
11
|
const { checkDockerInstalled, checkDockerInstalledCache } = require('./check-docker-installed');
|
|
12
12
|
|
|
13
13
|
async function mkdirpWithPermissions(dir, mode = 0o755) {
|
|
@@ -20,87 +20,122 @@ async function mkdirpWithPermissions(dir, mode = 0o755) {
|
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
23
|
+
// 函数用于生成 root_script.sh 和 user_script.sh
|
|
24
|
+
function createScripts({ rootCommands, userCommands, scriptDir, baseDir, uid, gid, shell }) {
|
|
25
|
+
const userScriptPath = path.join(scriptDir, 'user_script.sh');
|
|
26
|
+
const rootScriptPath = path.join(scriptDir, 'root_script.sh');
|
|
27
|
+
const execShell = shell === 'sh' || shell === 'bash' ? '' : `exec ${shell} ${uid}:${gid}`;
|
|
28
|
+
// 生成 root_script.sh 内容
|
|
29
|
+
const rootScriptContent = `
|
|
30
|
+
#!/bin/sh
|
|
31
|
+
${rootCommands.filter(Boolean).join('\n')}
|
|
32
|
+
${execShell} sh -c '${path.join(baseDir, 'dockerTemp', 'user_script.sh')}'
|
|
33
|
+
`.trim();
|
|
34
|
+
|
|
35
|
+
const userScriptContent = `
|
|
36
|
+
#!/bin/sh
|
|
37
|
+
|
|
38
|
+
${userCommands.filter(Boolean).join('\n')}
|
|
39
|
+
`.trim();
|
|
40
|
+
|
|
41
|
+
writeFileSync(rootScriptPath, rootScriptContent, { mode: 0o755 });
|
|
42
|
+
writeFileSync(userScriptPath, userScriptContent, { mode: 0o755 });
|
|
43
|
+
}
|
|
28
44
|
|
|
29
|
-
|
|
30
|
-
|
|
45
|
+
async function parseDockerOptionsFromPm2({ options, nodeInfo, meta, ports }) {
|
|
46
|
+
// Serverless mode does not support skipping Docker
|
|
47
|
+
if (options.env.SKIP_DOCKER && nodeInfo.mode !== NODE_MODES.SERVERLESS) {
|
|
48
|
+
return options;
|
|
49
|
+
}
|
|
31
50
|
|
|
51
|
+
// Use Docker if enabled via environment variable or config
|
|
52
|
+
const useDocker = options.env.USE_DOCKER || nodeInfo.enableDocker;
|
|
32
53
|
if (!useDocker) {
|
|
33
|
-
return
|
|
54
|
+
return options;
|
|
34
55
|
}
|
|
35
56
|
|
|
36
|
-
//
|
|
57
|
+
// Ensure Docker is installed
|
|
37
58
|
if (!nodeInfo.isDockerInstalled) {
|
|
38
59
|
checkDockerInstalledCache.checked = false;
|
|
39
60
|
if (!(await checkDockerInstalled())) {
|
|
40
61
|
throw new Error('Docker is not installed');
|
|
41
62
|
}
|
|
42
63
|
}
|
|
43
|
-
|
|
44
|
-
const
|
|
64
|
+
|
|
65
|
+
const dockerInfo = await createDockerImage({
|
|
66
|
+
appDir: options.env.BLOCKLET_APP_DIR,
|
|
67
|
+
dataDir: options.env.BLOCKLET_DATA_DIR,
|
|
68
|
+
meta,
|
|
69
|
+
ports,
|
|
70
|
+
});
|
|
71
|
+
const nextOptions = { ...options };
|
|
72
|
+
nextOptions.env = { ...nextOptions.env };
|
|
45
73
|
const name = parseDockerName(options.name);
|
|
46
74
|
|
|
47
|
-
//
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
75
|
+
// Ensure environment variables are properly set within the Docker container
|
|
76
|
+
const envDefaults = {
|
|
77
|
+
BLOCKLET_DOCKER_CPUS: '2',
|
|
78
|
+
BLOCKLET_DOCKER_MEMORY: '2g',
|
|
79
|
+
BLOCKLET_DOCKER_DISK_SIZE: '0g',
|
|
80
|
+
BLOCKLET_DOCKER_NAME: name,
|
|
81
|
+
INTERNAL_HOST: 'host.docker.internal',
|
|
82
|
+
};
|
|
83
|
+
nextOptions.env = { ...nextOptions.env, ...envDefaults };
|
|
53
84
|
|
|
54
|
-
|
|
55
|
-
const baseDir =
|
|
85
|
+
const dockerEnv = { ...nextOptions.env };
|
|
86
|
+
const { baseDir } = dockerInfo;
|
|
56
87
|
const serverDir = process.env.ABT_NODE_DATA_DIR;
|
|
88
|
+
const replaceDir = (dir) => dir.replace(serverDir, baseDir);
|
|
89
|
+
|
|
57
90
|
dockerEnv.DOCKER_CONTAINER_SERVER_DIR = baseDir;
|
|
58
|
-
dockerEnv.BLOCKLET_APP_DATA_DIR =
|
|
59
|
-
dockerEnv.BLOCKLET_APP_DIR =
|
|
60
|
-
dockerEnv.BLOCKLET_DATA_DIR =
|
|
91
|
+
dockerEnv.BLOCKLET_APP_DATA_DIR = replaceDir(nextOptions.env.BLOCKLET_APP_DATA_DIR);
|
|
92
|
+
dockerEnv.BLOCKLET_APP_DIR = replaceDir(nextOptions.env.BLOCKLET_APP_DIR);
|
|
93
|
+
dockerEnv.BLOCKLET_DATA_DIR = replaceDir(nextOptions.env.BLOCKLET_DATA_DIR);
|
|
61
94
|
dockerEnv.BLOCKLET_LOG_DIR = path.join(baseDir, 'logs');
|
|
62
95
|
dockerEnv.BLOCKLET_CACHE_DIR = path.join(baseDir, 'cache');
|
|
63
|
-
dockerEnv.BLOCKLET_APP_SHARE_DIR =
|
|
64
|
-
dockerEnv.BLOCKLET_SHARE_DIR =
|
|
65
|
-
dockerEnv.BLOCKLET_DOCKER_NAME = name;
|
|
96
|
+
dockerEnv.BLOCKLET_APP_SHARE_DIR = replaceDir(nextOptions.env.BLOCKLET_APP_SHARE_DIR);
|
|
97
|
+
dockerEnv.BLOCKLET_SHARE_DIR = replaceDir(nextOptions.env.BLOCKLET_SHARE_DIR);
|
|
66
98
|
dockerEnv.BLOCKLET_HOST = 'host.docker.internal';
|
|
67
99
|
dockerEnv.USING_DOCKER = 'true';
|
|
68
100
|
dockerEnv.LOCAL_USER_ID = process.getuid();
|
|
69
101
|
|
|
70
102
|
let nodeModulesName = 'node_modules';
|
|
71
103
|
let installScript = '';
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
104
|
+
const appDir = nextOptions.env.BLOCKLET_APP_DIR;
|
|
105
|
+
const packageJsonPath = path.join(appDir, 'package.json');
|
|
106
|
+
const compactJsPath = path.join(appDir, 'blocklet-compact.js');
|
|
107
|
+
|
|
108
|
+
if (dockerInfo.installNodeModules && existsSync(packageJsonPath) && existsSync(compactJsPath)) {
|
|
109
|
+
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf-8'));
|
|
77
110
|
if (packageJson.dependencies || packageJson.devDependencies) {
|
|
78
|
-
dockerEnv.BLOCKLET_APP_PACKAGE_JSON = JSON.stringify(packageJson);
|
|
79
111
|
nodeModulesName = 'docker_node_modules';
|
|
80
|
-
installScript = 'pnpm install --unsafe-perm --fetch-timeout=60000 --fetch-retries=5
|
|
112
|
+
installScript = 'pnpm install --unsafe-perm --fetch-timeout=60000 --fetch-retries=5';
|
|
81
113
|
}
|
|
82
114
|
}
|
|
83
115
|
|
|
84
|
-
|
|
85
|
-
dockerEnv.
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
COMPONENT_DOCKER_ENV_FILE_NAME
|
|
93
|
-
);
|
|
116
|
+
// Replace all occurrences of serverDir with baseDir in dockerEnv
|
|
117
|
+
const dockerEnvString = JSON.stringify(dockerEnv).replace(new RegExp(serverDir, 'g'), baseDir);
|
|
118
|
+
const updatedDockerEnv = JSON.parse(dockerEnvString);
|
|
119
|
+
|
|
120
|
+
updatedDockerEnv.DOCKER_HOST_SERVER_DIR = serverDir;
|
|
121
|
+
const envVars = stringifyEnvFile(updatedDockerEnv);
|
|
122
|
+
const dockerTempDir = path.join(serverDir, 'tmp', 'docker', nextOptions.env.BLOCKLET_REAL_NAME);
|
|
123
|
+
const dockerEnvFile = path.join(dockerTempDir, COMPONENT_DOCKER_ENV_FILE_NAME);
|
|
94
124
|
await mkdirpWithPermissions(path.dirname(dockerEnvFile));
|
|
95
125
|
await fs.writeFile(dockerEnvFile, envVars);
|
|
96
126
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
127
|
+
let runScript = dockerInfo.script || '';
|
|
128
|
+
if (!runScript && dockerInfo.installNodeModules) {
|
|
129
|
+
runScript = /(npm|yarn|pnpm|bun)/g.test(nextOptions.script) ? nextOptions.script : `node ${nextOptions.script}`;
|
|
130
|
+
}
|
|
101
131
|
|
|
102
|
-
|
|
103
|
-
|
|
132
|
+
const diskSizeOption =
|
|
133
|
+
nextOptions.env.BLOCKLET_DOCKER_DISK_SIZE !== '0g'
|
|
134
|
+
? `--storage-opt size=${nextOptions.env.BLOCKLET_DOCKER_DISK_SIZE}`
|
|
135
|
+
: '';
|
|
136
|
+
|
|
137
|
+
nextOptions.args = [];
|
|
138
|
+
nextOptions.namespace = 'blocklets_docker';
|
|
104
139
|
const uid = process.getuid();
|
|
105
140
|
const gid = process.getgid();
|
|
106
141
|
|
|
@@ -112,43 +147,43 @@ async function parseDockerOptionsFromPm2(input, nodeInfo) {
|
|
|
112
147
|
target: path.join(baseDir, 'blocklets'),
|
|
113
148
|
canEdit: false,
|
|
114
149
|
},
|
|
115
|
-
{ source:
|
|
116
|
-
{ source:
|
|
117
|
-
{ source:
|
|
118
|
-
{ source:
|
|
150
|
+
{ source: nextOptions.env.BLOCKLET_APP_DIR, target: dockerEnv.BLOCKLET_APP_DIR, canEdit: true },
|
|
151
|
+
{ source: nextOptions.env.BLOCKLET_DATA_DIR, target: dockerEnv.BLOCKLET_DATA_DIR, canEdit: true },
|
|
152
|
+
{ source: nextOptions.env.BLOCKLET_LOG_DIR, target: dockerEnv.BLOCKLET_LOG_DIR, canEdit: true },
|
|
153
|
+
{ source: nextOptions.env.BLOCKLET_CACHE_DIR, target: dockerEnv.BLOCKLET_CACHE_DIR, canEdit: true },
|
|
119
154
|
{
|
|
120
|
-
source: path.join(
|
|
155
|
+
source: path.join(nextOptions.env.BLOCKLET_APP_DIR, nodeModulesName),
|
|
121
156
|
target: path.join(dockerEnv.BLOCKLET_APP_DIR, 'node_modules'),
|
|
122
157
|
canEdit: true,
|
|
123
158
|
},
|
|
124
159
|
{
|
|
125
|
-
source:
|
|
126
|
-
target:
|
|
160
|
+
source: nextOptions.env.BLOCKLET_SHARE_DIR,
|
|
161
|
+
target: dockerEnv.BLOCKLET_SHARE_DIR,
|
|
127
162
|
canEdit: false,
|
|
128
163
|
},
|
|
129
164
|
{
|
|
130
|
-
source:
|
|
131
|
-
target:
|
|
165
|
+
source: nextOptions.env.BLOCKLET_APP_SHARE_DIR,
|
|
166
|
+
target: dockerEnv.BLOCKLET_APP_SHARE_DIR,
|
|
132
167
|
canEdit: true,
|
|
133
168
|
},
|
|
134
169
|
{
|
|
135
|
-
source: path.join(
|
|
170
|
+
source: path.join(nextOptions.env.BLOCKLET_APP_DATA_DIR, '.config'),
|
|
136
171
|
target: path.join(dockerEnv.BLOCKLET_APP_DATA_DIR, '.config'),
|
|
137
172
|
canEdit: false,
|
|
138
173
|
},
|
|
139
174
|
{
|
|
140
|
-
source: path.join(
|
|
175
|
+
source: path.join(nextOptions.env.BLOCKLET_APP_DATA_DIR, '.assets'),
|
|
141
176
|
target: path.join(dockerEnv.BLOCKLET_APP_DATA_DIR, '.assets'),
|
|
142
177
|
canEdit: false,
|
|
143
178
|
},
|
|
144
179
|
{
|
|
145
|
-
source: path.join(
|
|
180
|
+
source: path.join(nextOptions.env.BLOCKLET_APP_DATA_DIR, '.projects'),
|
|
146
181
|
target: path.join(dockerEnv.BLOCKLET_APP_DATA_DIR, '.projects'),
|
|
147
182
|
canEdit: true,
|
|
148
183
|
},
|
|
149
184
|
{
|
|
150
|
-
source:
|
|
151
|
-
target:
|
|
185
|
+
source: nextOptions.env.BLOCKLET_APP_DATA_DIR,
|
|
186
|
+
target: dockerEnv.BLOCKLET_APP_DATA_DIR,
|
|
152
187
|
canEdit: false,
|
|
153
188
|
},
|
|
154
189
|
].filter((v) => {
|
|
@@ -158,49 +193,65 @@ async function parseDockerOptionsFromPm2(input, nodeInfo) {
|
|
|
158
193
|
sourceDirs.add(v.source);
|
|
159
194
|
return true;
|
|
160
195
|
});
|
|
161
|
-
|
|
196
|
+
|
|
197
|
+
// Ensure necessary directories exist
|
|
162
198
|
for (const bindMount of bindMounts) {
|
|
163
199
|
if (!existsSync(bindMount.source)) {
|
|
164
200
|
// eslint-disable-next-line no-await-in-loop
|
|
165
201
|
await mkdirpWithPermissions(bindMount.source);
|
|
166
202
|
}
|
|
167
|
-
mount += ` -v ${bindMount.source}:${bindMount.target}:${bindMount.canEdit ? 'rw' : 'ro'} `;
|
|
168
203
|
}
|
|
169
|
-
await mkdirpWithPermissions(path.join(
|
|
204
|
+
await mkdirpWithPermissions(path.join(nextOptions.env.BLOCKLET_APP_DIR, 'node_modules'));
|
|
170
205
|
|
|
171
|
-
//
|
|
206
|
+
// Construct volume arguments
|
|
207
|
+
const volumes = bindMounts
|
|
208
|
+
.map(({ source, target, canEdit }) => `-v ${source}:${target}:${canEdit ? 'rw' : 'ro'}`)
|
|
209
|
+
.join(' ');
|
|
210
|
+
|
|
211
|
+
// On macOS, wait 2 seconds after stopping to prevent Orbstack from crashing
|
|
172
212
|
const awaitRestart = os.platform() === 'linux' ? '' : 'sleep 2 &&';
|
|
173
213
|
|
|
174
|
-
|
|
214
|
+
createScripts({
|
|
215
|
+
rootCommands: [
|
|
216
|
+
`echo "start ${meta.name} in docker: ${dockerInfo.network}"`,
|
|
217
|
+
`mkdir -p ${baseDir} || :`,
|
|
218
|
+
`mkdir -p ${path.join(baseDir, 'blocklets')} || :`,
|
|
219
|
+
`mkdir -p ${dockerEnv.BLOCKLET_APP_DIR} || :`,
|
|
220
|
+
`mkdir -p ${dockerEnv.BLOCKLET_APP_DATA_DIR} || :`,
|
|
221
|
+
`mkdir -p ${dockerEnv.BLOCKLET_LOG_DIR} || :`,
|
|
222
|
+
`mkdir -p ${dockerEnv.BLOCKLET_CACHE_DIR} || :`,
|
|
223
|
+
'mkdir -p /.local || :',
|
|
224
|
+
`chown ${uid}:${gid} /.local`,
|
|
225
|
+
`chown ${uid}:${gid} ${baseDir}`,
|
|
226
|
+
`chown ${uid}:${gid} /`,
|
|
227
|
+
],
|
|
228
|
+
userCommands: [`cd ${dockerEnv.BLOCKLET_APP_DIR}`, installScript, runScript],
|
|
229
|
+
uid,
|
|
230
|
+
gid,
|
|
231
|
+
shell: dockerInfo.shell,
|
|
232
|
+
baseDir,
|
|
233
|
+
scriptDir: dockerTempDir,
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
nextOptions.script = `
|
|
175
237
|
docker rm -fv ${name} > /dev/null 2>&1 || true && \
|
|
176
238
|
${awaitRestart} \
|
|
177
239
|
docker run --name ${name} \
|
|
178
|
-
${
|
|
179
|
-
${
|
|
240
|
+
${volumes} \
|
|
241
|
+
${dockerInfo.volumes || ''} \
|
|
242
|
+
-v ${dockerTempDir}:${path.join(baseDir, 'dockerTemp')}:rw \
|
|
243
|
+
${diskSizeOption} \
|
|
180
244
|
--cpus="${dockerEnv.BLOCKLET_DOCKER_CPUS}" \
|
|
181
245
|
--memory="${dockerEnv.BLOCKLET_DOCKER_MEMORY}" \
|
|
182
246
|
--env-file ${dockerEnvFile} \
|
|
183
|
-
${
|
|
184
|
-
${
|
|
185
|
-
sh
|
|
186
|
-
|
|
187
|
-
chown ${uid}:${gid} /.local && \
|
|
188
|
-
chown ${uid}:${gid} ${baseDir} && \
|
|
189
|
-
chown ${uid}:${gid} / && \
|
|
190
|
-
exec su-exec ${uid}:${gid} sh -c "
|
|
191
|
-
cd ${dockerEnv.BLOCKLET_APP_DIR} && \
|
|
192
|
-
${installScript} \
|
|
193
|
-
${runScript}
|
|
194
|
-
"
|
|
195
|
-
'`;
|
|
196
|
-
|
|
197
|
-
options.env = {
|
|
198
|
-
USING_DOCKER: 'true',
|
|
199
|
-
};
|
|
247
|
+
${dockerInfo.network} \
|
|
248
|
+
${dockerInfo.image} ${dockerInfo.command || ''} \
|
|
249
|
+
${dockerInfo.runBaseScript ? `sh ${path.join(baseDir, 'dockerTemp', 'root_script.sh')}` : ''}
|
|
250
|
+
`;
|
|
200
251
|
|
|
201
|
-
|
|
252
|
+
nextOptions.exec_mode = 'fork_mode';
|
|
202
253
|
|
|
203
|
-
return
|
|
254
|
+
return nextOptions;
|
|
204
255
|
}
|
|
205
256
|
|
|
206
257
|
module.exports = parseDockerOptionsFromPm2;
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "1.16.
|
|
6
|
+
"version": "1.16.33-beta-20240929-110332-9d5dfdc4",
|
|
7
7
|
"description": "",
|
|
8
8
|
"main": "lib/index.js",
|
|
9
9
|
"files": [
|
|
@@ -19,19 +19,19 @@
|
|
|
19
19
|
"author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
|
|
20
20
|
"license": "Apache-2.0",
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@abtnode/analytics": "1.16.
|
|
23
|
-
"@abtnode/auth": "1.16.
|
|
24
|
-
"@abtnode/certificate-manager": "1.16.
|
|
25
|
-
"@abtnode/constant": "1.16.
|
|
26
|
-
"@abtnode/cron": "1.16.
|
|
27
|
-
"@abtnode/logger": "1.16.
|
|
28
|
-
"@abtnode/models": "1.16.
|
|
29
|
-
"@abtnode/queue": "1.16.
|
|
30
|
-
"@abtnode/rbac": "1.16.
|
|
31
|
-
"@abtnode/router-provider": "1.16.
|
|
32
|
-
"@abtnode/static-server": "1.16.
|
|
33
|
-
"@abtnode/timemachine": "1.16.
|
|
34
|
-
"@abtnode/util": "1.16.
|
|
22
|
+
"@abtnode/analytics": "1.16.33-beta-20240929-110332-9d5dfdc4",
|
|
23
|
+
"@abtnode/auth": "1.16.33-beta-20240929-110332-9d5dfdc4",
|
|
24
|
+
"@abtnode/certificate-manager": "1.16.33-beta-20240929-110332-9d5dfdc4",
|
|
25
|
+
"@abtnode/constant": "1.16.33-beta-20240929-110332-9d5dfdc4",
|
|
26
|
+
"@abtnode/cron": "1.16.33-beta-20240929-110332-9d5dfdc4",
|
|
27
|
+
"@abtnode/logger": "1.16.33-beta-20240929-110332-9d5dfdc4",
|
|
28
|
+
"@abtnode/models": "1.16.33-beta-20240929-110332-9d5dfdc4",
|
|
29
|
+
"@abtnode/queue": "1.16.33-beta-20240929-110332-9d5dfdc4",
|
|
30
|
+
"@abtnode/rbac": "1.16.33-beta-20240929-110332-9d5dfdc4",
|
|
31
|
+
"@abtnode/router-provider": "1.16.33-beta-20240929-110332-9d5dfdc4",
|
|
32
|
+
"@abtnode/static-server": "1.16.33-beta-20240929-110332-9d5dfdc4",
|
|
33
|
+
"@abtnode/timemachine": "1.16.33-beta-20240929-110332-9d5dfdc4",
|
|
34
|
+
"@abtnode/util": "1.16.33-beta-20240929-110332-9d5dfdc4",
|
|
35
35
|
"@arcblock/did": "1.18.135",
|
|
36
36
|
"@arcblock/did-auth": "1.18.135",
|
|
37
37
|
"@arcblock/did-ext": "^1.18.135",
|
|
@@ -42,13 +42,13 @@
|
|
|
42
42
|
"@arcblock/pm2-events": "^0.0.5",
|
|
43
43
|
"@arcblock/validator": "^1.18.135",
|
|
44
44
|
"@arcblock/vc": "1.18.135",
|
|
45
|
-
"@blocklet/constant": "1.16.
|
|
46
|
-
"@blocklet/env": "1.16.
|
|
47
|
-
"@blocklet/meta": "1.16.
|
|
48
|
-
"@blocklet/resolver": "1.16.
|
|
49
|
-
"@blocklet/sdk": "1.16.
|
|
50
|
-
"@blocklet/store": "1.16.
|
|
51
|
-
"@did-space/client": "^0.5.
|
|
45
|
+
"@blocklet/constant": "1.16.33-beta-20240929-110332-9d5dfdc4",
|
|
46
|
+
"@blocklet/env": "1.16.33-beta-20240929-110332-9d5dfdc4",
|
|
47
|
+
"@blocklet/meta": "1.16.33-beta-20240929-110332-9d5dfdc4",
|
|
48
|
+
"@blocklet/resolver": "1.16.33-beta-20240929-110332-9d5dfdc4",
|
|
49
|
+
"@blocklet/sdk": "1.16.33-beta-20240929-110332-9d5dfdc4",
|
|
50
|
+
"@blocklet/store": "1.16.33-beta-20240929-110332-9d5dfdc4",
|
|
51
|
+
"@did-space/client": "^0.5.47",
|
|
52
52
|
"@fidm/x509": "^1.2.1",
|
|
53
53
|
"@ocap/mcrypto": "1.18.135",
|
|
54
54
|
"@ocap/util": "1.18.135",
|
|
@@ -105,5 +105,5 @@
|
|
|
105
105
|
"jest": "^29.7.0",
|
|
106
106
|
"unzipper": "^0.10.11"
|
|
107
107
|
},
|
|
108
|
-
"gitHead": "
|
|
108
|
+
"gitHead": "315517d60ab74ae086b6ff07e4871a3723c4dde4"
|
|
109
109
|
}
|