@ours.network/install 1.2.1-nightly.1 → 1.2.1-nightly.10
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/README.md +148 -128
- package/assets/Dockerfile +4 -1
- package/assets/docker-compose.yaml +21 -1
- package/assets/release-lock.json +82 -73
- package/assets/release.json +33 -19
- package/assets/scripts/build/build-common.mjs +1 -1
- package/assets/scripts/build/build-sdk.mjs +6 -1
- package/assets/scripts/maintenance/build-context.mjs +1 -1
- package/assets/scripts/maintenance/daemon-owner.mjs +15 -0
- package/assets/scripts/maintenance/docker-layout-conversion.mjs +3 -1
- package/assets/scripts/maintenance/release-graph.mjs +18 -1
- package/assets/scripts/maintenance/state-operation.mjs +4 -2
- package/assets/scripts/runtime/client-setup.mjs +3 -3
- package/assets/scripts/runtime/entrypoint.sh +1 -1
- package/assets/scripts/runtime/legacy-import.mjs +103 -0
- package/assets/sources.json +46 -28
- package/install.mjs +2 -2
- package/lib/build-transition.mjs +13 -7
- package/lib/client-cli.mjs +117 -0
- package/lib/docker-runtime-repair.mjs +125 -0
- package/lib/effects.mjs +139 -85
- package/lib/fleet-settings.mjs +43 -0
- package/lib/legacy-migration.mjs +202 -0
- package/lib/legacy-state.mjs +205 -0
- package/lib/managed-cli.mjs +164 -0
- package/lib/orchestrate.mjs +162 -58
- package/lib/plan.mjs +10 -6
- package/lib/prompt.mjs +113 -119
- package/lib/server-onboarding.mjs +114 -0
- package/lib/setup-options.mjs +253 -0
- package/lib/setup.mjs +154 -0
- package/lib/target.mjs +4 -4
- package/lib/uninstall.mjs +2 -2
- package/lib/usage.mjs +49 -44
- package/package.json +1 -1
package/assets/release-lock.json
CHANGED
|
@@ -8,15 +8,16 @@
|
|
|
8
8
|
"name": "ours-release-set-verification",
|
|
9
9
|
"version": "0.0.0",
|
|
10
10
|
"dependencies": {
|
|
11
|
-
"@ours.network/claude-code": "1.2.0-nightly.
|
|
12
|
-
"@ours.network/cli": "2.8.1-nightly.
|
|
13
|
-
"@ours.network/codex": "1.2.0-nightly.
|
|
14
|
-
"@ours.network/cowork": "1.3.2-nightly.
|
|
15
|
-
"@ours.network/
|
|
16
|
-
"@ours.network/
|
|
17
|
-
"@ours.network/
|
|
18
|
-
"@ours.network/
|
|
19
|
-
"@ours.network/
|
|
11
|
+
"@ours.network/claude-code": "1.2.0-nightly.5",
|
|
12
|
+
"@ours.network/cli": "2.8.1-nightly.7",
|
|
13
|
+
"@ours.network/codex": "1.2.0-nightly.5",
|
|
14
|
+
"@ours.network/cowork": "1.3.2-nightly.20260921.0d6bb50",
|
|
15
|
+
"@ours.network/daemon": "3.8.1-nightly.1",
|
|
16
|
+
"@ours.network/fleet": "1.2.0-nightly.3",
|
|
17
|
+
"@ours.network/mcp": "1.2.0-nightly.5",
|
|
18
|
+
"@ours.network/messenger-server": "1.0.31-nightly.3",
|
|
19
|
+
"@ours.network/sdk": "3.8.1-nightly.9",
|
|
20
|
+
"@ours.network/tg-connector": "1.0.1-nightly.3"
|
|
20
21
|
}
|
|
21
22
|
},
|
|
22
23
|
"node_modules/@adapt-toolkit/sdk": {
|
|
@@ -439,9 +440,9 @@
|
|
|
439
440
|
}
|
|
440
441
|
},
|
|
441
442
|
"node_modules/@agentclientprotocol/sdk": {
|
|
442
|
-
"version": "1.
|
|
443
|
-
"resolved": "https://registry.npmjs.org/@agentclientprotocol/sdk/-/sdk-1.
|
|
444
|
-
"integrity": "sha512
|
|
443
|
+
"version": "1.5.0",
|
|
444
|
+
"resolved": "https://registry.npmjs.org/@agentclientprotocol/sdk/-/sdk-1.5.0.tgz",
|
|
445
|
+
"integrity": "sha512-524jwbB2iYWA+kWWyv9fhKbhU89dH/lu9u5EXwVNmfYzopV8BujCDxByBDZhxRUkB7RWIJzISCnwivdDk+bdVg==",
|
|
445
446
|
"license": "Apache-2.0",
|
|
446
447
|
"peerDependencies": {
|
|
447
448
|
"zod": "^3.25.0 || ^4.0.0"
|
|
@@ -565,9 +566,9 @@
|
|
|
565
566
|
]
|
|
566
567
|
},
|
|
567
568
|
"node_modules/@anthropic-ai/sdk": {
|
|
568
|
-
"version": "0.
|
|
569
|
-
"resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.
|
|
570
|
-
"integrity": "sha512-
|
|
569
|
+
"version": "0.127.0",
|
|
570
|
+
"resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.127.0.tgz",
|
|
571
|
+
"integrity": "sha512-OKiVbG9LHCxjn4fBJZJFy/Bz3hqE1RYpwaeSEDNheQnvz0XNKmGT/Lhkz2Sf1a27Ldt3vVv+BkU81I0U/8EpoQ==",
|
|
571
572
|
"license": "MIT",
|
|
572
573
|
"optional": true,
|
|
573
574
|
"peer": true,
|
|
@@ -811,9 +812,9 @@
|
|
|
811
812
|
}
|
|
812
813
|
},
|
|
813
814
|
"node_modules/@fastify/websocket": {
|
|
814
|
-
"version": "11.3.
|
|
815
|
-
"resolved": "https://registry.npmjs.org/@fastify/websocket/-/websocket-11.3.
|
|
816
|
-
"integrity": "sha512-
|
|
815
|
+
"version": "11.3.1",
|
|
816
|
+
"resolved": "https://registry.npmjs.org/@fastify/websocket/-/websocket-11.3.1.tgz",
|
|
817
|
+
"integrity": "sha512-3117Fteg0MC9zRl0ilNAQelRb+PNDvNuWe0yJ+jnLthaPPZnlml+GraGpesNUEQvLl8+3C+W4aKZz10XmQ10Mg==",
|
|
817
818
|
"funding": [
|
|
818
819
|
{
|
|
819
820
|
"type": "github",
|
|
@@ -1051,13 +1052,14 @@
|
|
|
1051
1052
|
}
|
|
1052
1053
|
},
|
|
1053
1054
|
"node_modules/@ours.network/claude-code": {
|
|
1054
|
-
"version": "1.2.0-nightly.
|
|
1055
|
-
"resolved": "https://registry.npmjs.org/@ours.network/claude-code/-/claude-code-1.2.0-nightly.
|
|
1056
|
-
"integrity": "sha512-
|
|
1055
|
+
"version": "1.2.0-nightly.5",
|
|
1056
|
+
"resolved": "https://registry.npmjs.org/@ours.network/claude-code/-/claude-code-1.2.0-nightly.5.tgz",
|
|
1057
|
+
"integrity": "sha512-PxPegFvXx5bkvTprB8dcPjIYvE5bvjQoAtw31onOAZa0YdX2K7yYpesYcZ0sOnFdSQkI4+aaVyTb1Lj9EiMABQ==",
|
|
1057
1058
|
"license": "FSL-1.1-Apache-2.0",
|
|
1058
1059
|
"dependencies": {
|
|
1059
1060
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
1060
|
-
"@ours.network/
|
|
1061
|
+
"@ours.network/mcp": "1.2.0-nightly.5",
|
|
1062
|
+
"@ours.network/sdk": "3.8.1-nightly.9",
|
|
1061
1063
|
"ws": "^8.21.0",
|
|
1062
1064
|
"zod": "^3.25.76"
|
|
1063
1065
|
},
|
|
@@ -1066,34 +1068,29 @@
|
|
|
1066
1068
|
}
|
|
1067
1069
|
},
|
|
1068
1070
|
"node_modules/@ours.network/cli": {
|
|
1069
|
-
"version": "2.8.1-nightly.
|
|
1070
|
-
"resolved": "https://registry.npmjs.org/@ours.network/cli/-/cli-2.8.1-nightly.
|
|
1071
|
-
"integrity": "sha512-
|
|
1071
|
+
"version": "2.8.1-nightly.7",
|
|
1072
|
+
"resolved": "https://registry.npmjs.org/@ours.network/cli/-/cli-2.8.1-nightly.7.tgz",
|
|
1073
|
+
"integrity": "sha512-Kq3iySBsOgazTxawxY8aFvaMTQ6fnkPZ2KmO/k6NhPaVpZbmBcL/9OWefKYEUlDMvuSOEVzjQLZa0Wac4oopuA==",
|
|
1072
1074
|
"license": "FSL-1.1-Apache-2.0",
|
|
1073
1075
|
"dependencies": {
|
|
1074
|
-
"@
|
|
1075
|
-
"@adapt-toolkit/sdk-native": "0.10.13-nightly.1",
|
|
1076
|
-
"@ours.network/sdk": "3.8.1-nightly.3",
|
|
1077
|
-
"pg": "^8.16.3"
|
|
1076
|
+
"@ours.network/sdk": "3.8.1-nightly.9"
|
|
1078
1077
|
},
|
|
1079
1078
|
"bin": {
|
|
1080
1079
|
"ours": "dist/cli.js"
|
|
1081
1080
|
},
|
|
1082
1081
|
"engines": {
|
|
1083
1082
|
"node": ">=22"
|
|
1084
|
-
},
|
|
1085
|
-
"optionalDependencies": {
|
|
1086
|
-
"better-sqlite3": "^13.0.3"
|
|
1087
1083
|
}
|
|
1088
1084
|
},
|
|
1089
1085
|
"node_modules/@ours.network/codex": {
|
|
1090
|
-
"version": "1.2.0-nightly.
|
|
1091
|
-
"resolved": "https://registry.npmjs.org/@ours.network/codex/-/codex-1.2.0-nightly.
|
|
1092
|
-
"integrity": "sha512-
|
|
1086
|
+
"version": "1.2.0-nightly.5",
|
|
1087
|
+
"resolved": "https://registry.npmjs.org/@ours.network/codex/-/codex-1.2.0-nightly.5.tgz",
|
|
1088
|
+
"integrity": "sha512-6E3tE0valPE0FSOsONh3BrrBo9Gxt1IgWm5Hwj0xm00mGQ+qsjGRrzCB8snrOh9OdnGuvPOF20aunmp4g4KoMg==",
|
|
1093
1089
|
"license": "FSL-1.1-Apache-2.0",
|
|
1094
1090
|
"dependencies": {
|
|
1095
1091
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
1096
|
-
"@ours.network/
|
|
1092
|
+
"@ours.network/mcp": "1.2.0-nightly.5",
|
|
1093
|
+
"@ours.network/sdk": "3.8.1-nightly.9",
|
|
1097
1094
|
"ws": "^8.21.0",
|
|
1098
1095
|
"zod": "^3.25.76"
|
|
1099
1096
|
},
|
|
@@ -1106,12 +1103,12 @@
|
|
|
1106
1103
|
}
|
|
1107
1104
|
},
|
|
1108
1105
|
"node_modules/@ours.network/cowork": {
|
|
1109
|
-
"version": "1.3.2-nightly.
|
|
1110
|
-
"resolved": "https://registry.npmjs.org/@ours.network/cowork/-/cowork-1.3.2-nightly.
|
|
1111
|
-
"integrity": "sha512-
|
|
1106
|
+
"version": "1.3.2-nightly.20260921.0d6bb50",
|
|
1107
|
+
"resolved": "https://registry.npmjs.org/@ours.network/cowork/-/cowork-1.3.2-nightly.20260921.0d6bb50.tgz",
|
|
1108
|
+
"integrity": "sha512-gyUgY7Fw6RQVhONqexLNcS8m0WuBUQiHYIrWqd2FeAyyJOONjYGfbRtgJ67Xin1YTYf8Cxlomuf6j8cEc4ZwZg==",
|
|
1112
1109
|
"license": "FSL-1.1-Apache-2.0",
|
|
1113
1110
|
"dependencies": {
|
|
1114
|
-
"@ours.network/sdk": "3.8.1-nightly.
|
|
1111
|
+
"@ours.network/sdk": "3.8.1-nightly.9",
|
|
1115
1112
|
"ajv": "8.20.0",
|
|
1116
1113
|
"better-sqlite3": "13.0.3",
|
|
1117
1114
|
"react": "18.3.1",
|
|
@@ -1125,17 +1122,39 @@
|
|
|
1125
1122
|
"node": ">=22"
|
|
1126
1123
|
}
|
|
1127
1124
|
},
|
|
1125
|
+
"node_modules/@ours.network/daemon": {
|
|
1126
|
+
"version": "3.8.1-nightly.1",
|
|
1127
|
+
"resolved": "https://registry.npmjs.org/@ours.network/daemon/-/daemon-3.8.1-nightly.1.tgz",
|
|
1128
|
+
"integrity": "sha512-1+RA0UH/wJUSmKnmZuHS3v6UyH5PsrZWoqwa5rFKl6n2Cd8sWDs++DrwwV0IOFdgFcDOtb/k8mes4sI2J7J8mg==",
|
|
1129
|
+
"license": "FSL-1.1-Apache-2.0",
|
|
1130
|
+
"dependencies": {
|
|
1131
|
+
"@adapt-toolkit/sdk": "0.10.13-nightly.1",
|
|
1132
|
+
"@adapt-toolkit/sdk-native": "0.10.13-nightly.1",
|
|
1133
|
+
"@ours.network/sdk": "3.8.1-nightly.9",
|
|
1134
|
+
"ajv": "^8.20.0",
|
|
1135
|
+
"pg": "^8.16.3"
|
|
1136
|
+
},
|
|
1137
|
+
"bin": {
|
|
1138
|
+
"ours-daemon": "dist/cli.js"
|
|
1139
|
+
},
|
|
1140
|
+
"engines": {
|
|
1141
|
+
"node": ">=22"
|
|
1142
|
+
},
|
|
1143
|
+
"optionalDependencies": {
|
|
1144
|
+
"better-sqlite3": "^13.0.3"
|
|
1145
|
+
}
|
|
1146
|
+
},
|
|
1128
1147
|
"node_modules/@ours.network/fleet": {
|
|
1129
|
-
"version": "1.2.0-nightly.
|
|
1130
|
-
"resolved": "https://registry.npmjs.org/@ours.network/fleet/-/fleet-1.2.0-nightly.
|
|
1131
|
-
"integrity": "sha512-
|
|
1148
|
+
"version": "1.2.0-nightly.3",
|
|
1149
|
+
"resolved": "https://registry.npmjs.org/@ours.network/fleet/-/fleet-1.2.0-nightly.3.tgz",
|
|
1150
|
+
"integrity": "sha512-FzizLkh1fQ9RkzmID1v42QlZs9E0QHWJ44IWIWzYd0DRaetcXg/5FUVTNc82U5yWZyveBZTJq6mvg9H73NSlkw==",
|
|
1132
1151
|
"license": "FSL-1.1-Apache-2.0",
|
|
1133
1152
|
"dependencies": {
|
|
1134
1153
|
"@agentclientprotocol/sdk": "^1.3.0",
|
|
1135
1154
|
"@fastify/static": "^10.1.2",
|
|
1136
1155
|
"@fastify/websocket": "^11.2.0",
|
|
1137
|
-
"@ours.network/cli": "2.8.1-nightly.
|
|
1138
|
-
"@ours.network/sdk": "3.8.1-nightly.
|
|
1156
|
+
"@ours.network/cli": "2.8.1-nightly.7",
|
|
1157
|
+
"@ours.network/sdk": "3.8.1-nightly.9",
|
|
1139
1158
|
"commander": "^12.1.0",
|
|
1140
1159
|
"fastify": "^5.4.0",
|
|
1141
1160
|
"react": "^19.1.1",
|
|
@@ -1183,13 +1202,12 @@
|
|
|
1183
1202
|
"license": "MIT"
|
|
1184
1203
|
},
|
|
1185
1204
|
"node_modules/@ours.network/mcp": {
|
|
1186
|
-
"version": "1.2.0-nightly.
|
|
1187
|
-
"resolved": "https://registry.npmjs.org/@ours.network/mcp/-/mcp-1.2.0-nightly.
|
|
1188
|
-
"integrity": "sha512-
|
|
1205
|
+
"version": "1.2.0-nightly.5",
|
|
1206
|
+
"resolved": "https://registry.npmjs.org/@ours.network/mcp/-/mcp-1.2.0-nightly.5.tgz",
|
|
1207
|
+
"integrity": "sha512-CsRsHlJwU5WkUeF8mMLysq9GsYLBA2wnl8v+qZhaJZhSJWoNb5TqfXd2v5/Su7VUpOaRwvubv/9Jf86eQgqhGw==",
|
|
1189
1208
|
"license": "FSL-1.1-Apache-2.0",
|
|
1190
1209
|
"dependencies": {
|
|
1191
|
-
"@ours.network/
|
|
1192
|
-
"@ours.network/sdk": "3.8.1-nightly.3"
|
|
1210
|
+
"@ours.network/sdk": "3.8.1-nightly.9"
|
|
1193
1211
|
},
|
|
1194
1212
|
"bin": {
|
|
1195
1213
|
"ours-mcp": "dist/cli.js"
|
|
@@ -1199,14 +1217,14 @@
|
|
|
1199
1217
|
}
|
|
1200
1218
|
},
|
|
1201
1219
|
"node_modules/@ours.network/messenger-server": {
|
|
1202
|
-
"version": "1.0.31-nightly.
|
|
1203
|
-
"resolved": "https://registry.npmjs.org/@ours.network/messenger-server/-/messenger-server-1.0.31-nightly.
|
|
1204
|
-
"integrity": "sha512-
|
|
1220
|
+
"version": "1.0.31-nightly.3",
|
|
1221
|
+
"resolved": "https://registry.npmjs.org/@ours.network/messenger-server/-/messenger-server-1.0.31-nightly.3.tgz",
|
|
1222
|
+
"integrity": "sha512-j86POD2U2yh7jiIqbIixDu8TpEpaVf+dHLs3adtTRfTs6V7ucl//digdkyc1uaZrUliRduxXKBOrb/1Nu648xQ==",
|
|
1205
1223
|
"license": "FSL-1.1-Apache-2.0",
|
|
1206
1224
|
"dependencies": {
|
|
1207
1225
|
"@eslint-community/regexpp": "4.12.2",
|
|
1208
1226
|
"@fontsource-variable/jetbrains-mono": "^5.3.0",
|
|
1209
|
-
"@ours.network/sdk": "3.8.1-nightly.
|
|
1227
|
+
"@ours.network/sdk": "3.8.1-nightly.9",
|
|
1210
1228
|
"@radix-ui/react-dialog": "^1.1.23",
|
|
1211
1229
|
"framer-motion": "^12.43.0",
|
|
1212
1230
|
"jsqr": "^1.4.0",
|
|
@@ -1232,34 +1250,25 @@
|
|
|
1232
1250
|
}
|
|
1233
1251
|
},
|
|
1234
1252
|
"node_modules/@ours.network/sdk": {
|
|
1235
|
-
"version": "3.8.1-nightly.
|
|
1236
|
-
"resolved": "https://registry.npmjs.org/@ours.network/sdk/-/sdk-3.8.1-nightly.
|
|
1237
|
-
"integrity": "sha512-
|
|
1253
|
+
"version": "3.8.1-nightly.9",
|
|
1254
|
+
"resolved": "https://registry.npmjs.org/@ours.network/sdk/-/sdk-3.8.1-nightly.9.tgz",
|
|
1255
|
+
"integrity": "sha512-9xh/7hWZ8zRV+8Vimu7OTf2qUAp4o4MJFxlCVtyi1YO0WyFFgk+dZcT8nWlXY/JWIPFdb8wnYW/OSoqGUAw4Hw==",
|
|
1238
1256
|
"license": "FSL-1.1-Apache-2.0",
|
|
1239
1257
|
"workspaces": [
|
|
1240
1258
|
".",
|
|
1241
1259
|
"packages/*"
|
|
1242
1260
|
],
|
|
1243
|
-
"dependencies": {
|
|
1244
|
-
"@adapt-toolkit/sdk": "0.10.13-nightly.1",
|
|
1245
|
-
"@adapt-toolkit/sdk-native": "0.10.13-nightly.1",
|
|
1246
|
-
"ajv": "^8.20.0",
|
|
1247
|
-
"pg": "^8.16.3"
|
|
1248
|
-
},
|
|
1249
1261
|
"engines": {
|
|
1250
1262
|
"node": ">=22"
|
|
1251
|
-
},
|
|
1252
|
-
"optionalDependencies": {
|
|
1253
|
-
"better-sqlite3": "^13.0.3"
|
|
1254
1263
|
}
|
|
1255
1264
|
},
|
|
1256
1265
|
"node_modules/@ours.network/tg-connector": {
|
|
1257
|
-
"version": "1.0.1-nightly.
|
|
1258
|
-
"resolved": "https://registry.npmjs.org/@ours.network/tg-connector/-/tg-connector-1.0.1-nightly.
|
|
1259
|
-
"integrity": "sha512
|
|
1266
|
+
"version": "1.0.1-nightly.3",
|
|
1267
|
+
"resolved": "https://registry.npmjs.org/@ours.network/tg-connector/-/tg-connector-1.0.1-nightly.3.tgz",
|
|
1268
|
+
"integrity": "sha512-UDHEgM5C3mnnyfV3KTM2XFDGZ7vYs7oBE/3r8bKDtxJdlQZ4bxxI44LKrPI3gO2sdGROsDdM8/tvzwbJ8MiBYw==",
|
|
1260
1269
|
"license": "FSL-1.1-Apache-2.0",
|
|
1261
1270
|
"dependencies": {
|
|
1262
|
-
"@ours.network/sdk": "3.8.1-nightly.
|
|
1271
|
+
"@ours.network/sdk": "3.8.1-nightly.9",
|
|
1263
1272
|
"undici": "^7.28.0"
|
|
1264
1273
|
},
|
|
1265
1274
|
"bin": {
|
|
@@ -4285,9 +4294,9 @@
|
|
|
4285
4294
|
}
|
|
4286
4295
|
},
|
|
4287
4296
|
"node_modules/lru-cache": {
|
|
4288
|
-
"version": "11.5.
|
|
4289
|
-
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.
|
|
4290
|
-
"integrity": "sha512-
|
|
4297
|
+
"version": "11.5.3",
|
|
4298
|
+
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.3.tgz",
|
|
4299
|
+
"integrity": "sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==",
|
|
4291
4300
|
"license": "BlueOak-1.0.0",
|
|
4292
4301
|
"engines": {
|
|
4293
4302
|
"node": "20 || >=22"
|
package/assets/release.json
CHANGED
|
@@ -1,43 +1,57 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema": 1,
|
|
3
3
|
"channel": "nightly",
|
|
4
|
-
"installerVersion": "1.2.1-nightly.
|
|
4
|
+
"installerVersion": "1.2.1-nightly.10",
|
|
5
5
|
"packages": {
|
|
6
6
|
"@ours.network/sdk": {
|
|
7
|
-
"version": "3.8.1-nightly.
|
|
8
|
-
"integrity": "sha512-
|
|
7
|
+
"version": "3.8.1-nightly.9",
|
|
8
|
+
"integrity": "sha512-9xh/7hWZ8zRV+8Vimu7OTf2qUAp4o4MJFxlCVtyi1YO0WyFFgk+dZcT8nWlXY/JWIPFdb8wnYW/OSoqGUAw4Hw=="
|
|
9
9
|
},
|
|
10
10
|
"@ours.network/cli": {
|
|
11
|
-
"version": "2.8.1-nightly.
|
|
12
|
-
"integrity": "sha512-
|
|
11
|
+
"version": "2.8.1-nightly.7",
|
|
12
|
+
"integrity": "sha512-Kq3iySBsOgazTxawxY8aFvaMTQ6fnkPZ2KmO/k6NhPaVpZbmBcL/9OWefKYEUlDMvuSOEVzjQLZa0Wac4oopuA=="
|
|
13
13
|
},
|
|
14
14
|
"@ours.network/tg-connector": {
|
|
15
|
-
"version": "1.0.1-nightly.
|
|
16
|
-
"integrity": "sha512
|
|
15
|
+
"version": "1.0.1-nightly.3",
|
|
16
|
+
"integrity": "sha512-UDHEgM5C3mnnyfV3KTM2XFDGZ7vYs7oBE/3r8bKDtxJdlQZ4bxxI44LKrPI3gO2sdGROsDdM8/tvzwbJ8MiBYw=="
|
|
17
17
|
},
|
|
18
18
|
"@ours.network/cowork": {
|
|
19
|
-
"version": "1.3.2-nightly.
|
|
20
|
-
"integrity": "sha512-
|
|
19
|
+
"version": "1.3.2-nightly.20260921.0d6bb50",
|
|
20
|
+
"integrity": "sha512-gyUgY7Fw6RQVhONqexLNcS8m0WuBUQiHYIrWqd2FeAyyJOONjYGfbRtgJ67Xin1YTYf8Cxlomuf6j8cEc4ZwZg=="
|
|
21
21
|
},
|
|
22
22
|
"@ours.network/messenger-server": {
|
|
23
|
-
"version": "1.0.31-nightly.
|
|
24
|
-
"integrity": "sha512-
|
|
23
|
+
"version": "1.0.31-nightly.3",
|
|
24
|
+
"integrity": "sha512-j86POD2U2yh7jiIqbIixDu8TpEpaVf+dHLs3adtTRfTs6V7ucl//digdkyc1uaZrUliRduxXKBOrb/1Nu648xQ=="
|
|
25
25
|
},
|
|
26
26
|
"@ours.network/fleet": {
|
|
27
|
-
"version": "1.2.0-nightly.
|
|
28
|
-
"integrity": "sha512-
|
|
27
|
+
"version": "1.2.0-nightly.3",
|
|
28
|
+
"integrity": "sha512-FzizLkh1fQ9RkzmID1v42QlZs9E0QHWJ44IWIWzYd0DRaetcXg/5FUVTNc82U5yWZyveBZTJq6mvg9H73NSlkw=="
|
|
29
29
|
},
|
|
30
30
|
"@ours.network/mcp": {
|
|
31
|
-
"version": "1.2.0-nightly.
|
|
32
|
-
"integrity": "sha512-
|
|
31
|
+
"version": "1.2.0-nightly.5",
|
|
32
|
+
"integrity": "sha512-CsRsHlJwU5WkUeF8mMLysq9GsYLBA2wnl8v+qZhaJZhSJWoNb5TqfXd2v5/Su7VUpOaRwvubv/9Jf86eQgqhGw=="
|
|
33
33
|
},
|
|
34
34
|
"@ours.network/codex": {
|
|
35
|
-
"version": "1.2.0-nightly.
|
|
36
|
-
"integrity": "sha512-
|
|
35
|
+
"version": "1.2.0-nightly.5",
|
|
36
|
+
"integrity": "sha512-6E3tE0valPE0FSOsONh3BrrBo9Gxt1IgWm5Hwj0xm00mGQ+qsjGRrzCB8snrOh9OdnGuvPOF20aunmp4g4KoMg=="
|
|
37
37
|
},
|
|
38
38
|
"@ours.network/claude-code": {
|
|
39
|
-
"version": "1.2.0-nightly.
|
|
40
|
-
"integrity": "sha512-
|
|
39
|
+
"version": "1.2.0-nightly.5",
|
|
40
|
+
"integrity": "sha512-PxPegFvXx5bkvTprB8dcPjIYvE5bvjQoAtw31onOAZa0YdX2K7yYpesYcZ0sOnFdSQkI4+aaVyTb1Lj9EiMABQ=="
|
|
41
|
+
},
|
|
42
|
+
"@ours.network/daemon": {
|
|
43
|
+
"version": "3.8.1-nightly.1",
|
|
44
|
+
"integrity": "sha512-1+RA0UH/wJUSmKnmZuHS3v6UyH5PsrZWoqwa5rFKl6n2Cd8sWDs++DrwwV0IOFdgFcDOtb/k8mes4sI2J7J8mg=="
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"hostCli": {
|
|
48
|
+
"@ours.network/sdk": {
|
|
49
|
+
"version": "3.8.1-nightly.9",
|
|
50
|
+
"integrity": "sha512-9xh/7hWZ8zRV+8Vimu7OTf2qUAp4o4MJFxlCVtyi1YO0WyFFgk+dZcT8nWlXY/JWIPFdb8wnYW/OSoqGUAw4Hw=="
|
|
51
|
+
},
|
|
52
|
+
"@ours.network/cli": {
|
|
53
|
+
"version": "2.8.1-nightly.7",
|
|
54
|
+
"integrity": "sha512-Kq3iySBsOgazTxawxY8aFvaMTQ6fnkPZ2KmO/k6NhPaVpZbmBcL/9OWefKYEUlDMvuSOEVzjQLZa0Wac4oopuA=="
|
|
41
55
|
}
|
|
42
56
|
}
|
|
43
57
|
}
|
|
@@ -7,7 +7,7 @@ export const ROOT = resolve(process.env.OURS_BUILD_ROOT || '/opt/ours');
|
|
|
7
7
|
export const SOURCE_ROOT = resolve(process.env.OURS_SOURCE_ROOT || '/src');
|
|
8
8
|
export const OUT = join(ROOT, 'docker/vendor');
|
|
9
9
|
export const CONFIG = JSON.parse(readFileSync(join(ROOT, 'sources.json'), 'utf8'));
|
|
10
|
-
export const SELECTED = new Set((process.env.OURS_BUILD_PACKAGES || 'sdk,cli,
|
|
10
|
+
export const SELECTED = new Set((process.env.OURS_BUILD_PACKAGES || 'sdk,cli,daemon,tg-connector,cowork,messenger-server').split(',').map(name => '@ours.network/' + name));
|
|
11
11
|
export const inheritedLock = process.env.OURS_INSTALLER_LOCK_FD === '3' ? [3] : [];
|
|
12
12
|
export const run = (args, cwd) => execFileSync(args[0], args.slice(1), { cwd, stdio: ['inherit', 'inherit', 'inherit', ...inheritedLock] });
|
|
13
13
|
export const capture = (args, cwd, env = process.env) => execFileSync(args[0], args.slice(1), { cwd, env, encoding: 'utf8', stdio: ['ignore', 'pipe', 'inherit', ...inheritedLock] });
|
|
@@ -2,8 +2,13 @@ import { join } from 'node:path';
|
|
|
2
2
|
import { SOURCE_ROOT, SELECTED, pack, run } from './build-common.mjs';
|
|
3
3
|
const source = join(SOURCE_ROOT, 'sdk');
|
|
4
4
|
run(['npm', 'ci', '--no-audit', '--no-fund'], source);
|
|
5
|
-
run(['bash', 'scripts/compile-mufl.sh'], source);
|
|
5
|
+
if (SELECTED.has('@ours.network/daemon')) run(['bash', 'scripts/compile-mufl.sh'], source);
|
|
6
6
|
run(['npm', 'run', 'build'], source);
|
|
7
7
|
if (SELECTED.has('@ours.network/cli')) run(['npm', 'run', 'build:cli'], source);
|
|
8
8
|
pack(source, '@ours.network/sdk', 'sdk');
|
|
9
9
|
pack(join(source, 'packages/cli'), '@ours.network/cli', 'sdk');
|
|
10
|
+
|
|
11
|
+
if (SELECTED.has('@ours.network/daemon')) {
|
|
12
|
+
run(['npm', 'run', 'build:daemon'], source);
|
|
13
|
+
pack(join(source, 'packages/daemon'), '@ours.network/daemon', 'sdk');
|
|
14
|
+
}
|
|
@@ -8,7 +8,7 @@ import { semanticRecordEqual } from './provenance-compare.mjs';
|
|
|
8
8
|
export const BASE_RECORDS = ['package-lock.json', 'dependency-tree.json'];
|
|
9
9
|
export const CONTEXT = 'build-context.json';
|
|
10
10
|
const admitted = new WeakMap();
|
|
11
|
-
const packages = new Set(['sdk', 'cli', 'mcp', 'tg-connector', 'cowork', 'messenger-server', 'fleet', 'codex', 'claude-code', 'install'].map(n => '@ours.network/' + n));
|
|
11
|
+
const packages = new Set(['sdk', 'cli', 'daemon', 'mcp', 'tg-connector', 'cowork', 'messenger-server', 'fleet', 'codex', 'claude-code', 'install'].map(n => '@ours.network/' + n));
|
|
12
12
|
const digest = (bytes, algorithm = 'sha256', encoding = 'hex') => createHash(algorithm).update(bytes).digest(encoding);
|
|
13
13
|
const fail = text => { throw new Error(`Build context: ${text}`); };
|
|
14
14
|
const keys = (v, expected) => v && !Array.isArray(v) && typeof v === 'object' && Object.keys(v).sort().join('\0') === [...expected].sort().join('\0');
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
|
|
4
|
+
// Native callers provide an exact selected executable. Compose mounts the
|
|
5
|
+
// selected runtime and supplies its bin directory, including retained releases.
|
|
6
|
+
export function daemonOwner(env = process.env) {
|
|
7
|
+
if (env.OURS_CLI_PATH) return env.OURS_CLI_PATH;
|
|
8
|
+
const bin = env.OURS_DAEMON_BIN_DIR;
|
|
9
|
+
if (!bin) throw new Error('daemon maintenance requires its selected executable or bin directory');
|
|
10
|
+
const daemon = join(bin, 'ours-daemon');
|
|
11
|
+
if (existsSync(daemon)) return daemon;
|
|
12
|
+
const legacy = join(bin, 'ours');
|
|
13
|
+
if (existsSync(legacy)) return legacy;
|
|
14
|
+
throw new Error('selected runtime contains no daemon administrator');
|
|
15
|
+
}
|
|
@@ -6,6 +6,8 @@ import { pathToFileURL } from 'node:url';
|
|
|
6
6
|
import { copyPrivateTree, scanSource, createArchive, validateArchive } from './state-archive.mjs';
|
|
7
7
|
import { publishNoReplace } from './state-native.mjs';
|
|
8
8
|
|
|
9
|
+
import { daemonOwner } from './daemon-owner.mjs';
|
|
10
|
+
|
|
9
11
|
const COMPONENTS = ['daemon', 'telegram', 'cowork', 'messenger'];
|
|
10
12
|
import { recordNames, readBuildRecords, validateBuildRecordSet } from './build-context.mjs';
|
|
11
13
|
const DAEMON_STATE = '/var/lib/ours';
|
|
@@ -207,7 +209,7 @@ export async function runDockerLayoutCommand(argv, env = process.env) {
|
|
|
207
209
|
const build = env.OURS_BUILD_ROOT || '/opt/ours';
|
|
208
210
|
const options = {
|
|
209
211
|
uid: process.getuid(), gid: process.getgid(), instanceId: env.OURS_DAEMON_ID,
|
|
210
|
-
cli:
|
|
212
|
+
cli: daemonOwner({ OURS_DAEMON_BIN_DIR: '/opt/ours/node_modules/.bin', ...env }),
|
|
211
213
|
configPath: env.OURS_DAEMON_CONFIG || '/var/lib/ours/config.json',
|
|
212
214
|
provenance: readBuildRecords(build),
|
|
213
215
|
};
|
|
@@ -20,10 +20,19 @@ export function releaseBinding(policy) {
|
|
|
20
20
|
if (release?.schema !== 1 || !['stable', 'nightly'].includes(release.channel)) fail('invalid release binding');
|
|
21
21
|
const pattern = release.channel === 'nightly' ? nightly : stable;
|
|
22
22
|
if (typeof release.installerVersion !== 'string' || !pattern.test(release.installerVersion)) fail('invalid installer version/channel');
|
|
23
|
-
if (
|
|
23
|
+
if (release.scope !== undefined && release.scope !== 'host-cli') fail('invalid release scope');
|
|
24
|
+
const required = release.scope === 'host-cli' ? ['@ours.network/cli', '@ours.network/sdk'] : [...names, ...(release.packages?.['@ours.network/daemon'] ? ['@ours.network/daemon'] : [])];
|
|
25
|
+
if (!release.packages || JSON.stringify(Object.keys(release.packages).sort()) !== JSON.stringify([...required].sort())) fail('release must select exactly the required ours packages');
|
|
26
|
+
if (release.scope === 'host-cli' && Object.hasOwn(release, 'hostCli')) fail('nested host CLI selection');
|
|
24
27
|
for (const [name, entry] of Object.entries(release.packages)) {
|
|
25
28
|
if (typeof entry?.version !== 'string' || !pattern.test(entry.version) || !/^sha512-[A-Za-z0-9+/]{86}==$/.test(entry.integrity ?? '')) fail(`invalid release artifact: ${name}`);
|
|
26
29
|
}
|
|
30
|
+
if (Object.hasOwn(release, 'hostCli')) {
|
|
31
|
+
if (!release.hostCli || Object.keys(release.hostCli).sort().join(',') !== '@ours.network/cli,@ours.network/sdk') fail('host CLI must select exactly CLI and SDK');
|
|
32
|
+
for (const [name, entry] of Object.entries(release.hostCli)) {
|
|
33
|
+
if (typeof entry?.version !== 'string' || !pattern.test(entry.version) || !/^sha512-[A-Za-z0-9+/]{86}==$/.test(entry.integrity ?? '')) fail(`invalid host CLI artifact: ${name}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
27
36
|
if (!policy.packages || !Object.keys(policy.packages).length) fail('missing release source selection');
|
|
28
37
|
for (const [name, entry] of Object.entries(policy.packages)) {
|
|
29
38
|
if (entry?.type !== 'npm' || entry.version !== release.packages[name]?.version || Object.keys(entry).sort().join(',') !== 'type,version') fail(`source selection differs from release: ${name}`);
|
|
@@ -92,3 +101,11 @@ export function verifyRuntimeRelease(root) {
|
|
|
92
101
|
if (!existsSync(path)) return { verified: false, reason: 'legacy runtime without sources' };
|
|
93
102
|
return verifyReleaseGraph(root, json(path));
|
|
94
103
|
}
|
|
104
|
+
|
|
105
|
+
/** The client command has its own exact dependency selection, never a graph bypass. */
|
|
106
|
+
export function hostCliPolicy(policy) {
|
|
107
|
+
const release = releaseBinding(policy);
|
|
108
|
+
if (!release?.hostCli) return null;
|
|
109
|
+
const { hostCli, ...base } = release;
|
|
110
|
+
return { release: { ...base, scope: 'host-cli', packages: hostCli }, packages: Object.fromEntries(Object.entries(hostCli).map(([name, p]) => [name, { type: 'npm', version: p.version }])) };
|
|
111
|
+
}
|
|
@@ -9,6 +9,8 @@ import { createArchive, extractArchive, validateArchive, scanSource, copyPrivate
|
|
|
9
9
|
import { exchange, tryLock, setMtimeNs } from './state-native.mjs';
|
|
10
10
|
import { recordNames, readBuildRecords, equalBuildRecords, initializeBuildMarker } from './build-context.mjs';
|
|
11
11
|
|
|
12
|
+
import { daemonOwner } from './daemon-owner.mjs';
|
|
13
|
+
|
|
12
14
|
const PROVENANCE = '.ours-provenance';
|
|
13
15
|
const APPLICATIONS = ['daemon', 'telegram', 'cowork', 'messenger'];
|
|
14
16
|
const exists = path => { try { fs.lstatSync(path); return true; } catch (e) { if (e.code === 'ENOENT') return false; throw e; } };
|
|
@@ -145,9 +147,9 @@ export async function runStateOperation(argv, env = process.env, checkpoints = {
|
|
|
145
147
|
}
|
|
146
148
|
function retainAuthority(staging) {
|
|
147
149
|
if (!['daemon', 'server'].includes(domain)) return;
|
|
148
|
-
if (!env.
|
|
150
|
+
if (!env.OURS_DAEMON_CONFIG) fail('daemon maintenance requires its selected executable and configuration');
|
|
149
151
|
const daemon = commonTree ? join(staging, 'daemon') : staging;
|
|
150
|
-
owner([env
|
|
152
|
+
owner([daemonOwner(env), 'config', 'access-retain', '--config', env.OURS_DAEMON_CONFIG, '--target-state-dir', daemon, '--json']);
|
|
151
153
|
if (!commonTree) return;
|
|
152
154
|
const credentials = join(live, 'credentials'), destination = join(staging, 'credentials');
|
|
153
155
|
for (const name of ['telegram', 'cowork', 'messenger']) if (!privateStat(join(credentials, name, 'daemon-token'), false, 0o600).size) fail('current managed credential is empty');
|
|
@@ -2,7 +2,7 @@ import { readBuildRecords, initializeBuildMarker } from '../maintenance/build-co
|
|
|
2
2
|
import { spawnSync } from 'node:child_process';
|
|
3
3
|
import { randomBytes } from 'node:crypto';
|
|
4
4
|
import {
|
|
5
|
-
closeSync, constants, fstatSync, lstatSync, mkdirSync, openSync, readFileSync,
|
|
5
|
+
existsSync, closeSync, constants, fstatSync, lstatSync, mkdirSync, openSync, readFileSync,
|
|
6
6
|
realpathSync, renameSync, unlinkSync, writeFileSync, readdirSync, chmodSync, chownSync,
|
|
7
7
|
} from 'node:fs';
|
|
8
8
|
|
|
@@ -67,7 +67,7 @@ function validateCommon(idName = 'OURS_DAEMON_ID') {
|
|
|
67
67
|
|
|
68
68
|
function composeConfig(domain) {
|
|
69
69
|
if (domain === 'daemon') {
|
|
70
|
-
const value = { stateDir: '/var/lib/ours', port: 3050, apiVisibility: 'owner'
|
|
70
|
+
const value = { stateDir: '/var/lib/ours', port: 3050, apiVisibility: 'owner' };
|
|
71
71
|
if (process.env.OURS_BROKER_URL) value.brokerUrl = process.env.OURS_BROKER_URL;
|
|
72
72
|
return value;
|
|
73
73
|
}
|
|
@@ -155,7 +155,7 @@ async function telegramInput() {
|
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
function cliJson(args) {
|
|
158
|
-
const result = spawnSync(process.execPath, ['/opt/ours/node_modules/@ours.network/cli/dist/cli.js', ...args], {
|
|
158
|
+
const result = spawnSync(process.execPath, [existsSync('/opt/ours/node_modules/@ours.network/daemon/dist/cli.js') ? '/opt/ours/node_modules/@ours.network/daemon/dist/cli.js' : '/opt/ours/node_modules/@ours.network/cli/dist/cli.js', ...args], {
|
|
159
159
|
encoding: 'utf8', timeout: 60_000, maxBuffer: 1024 * 1024,
|
|
160
160
|
});
|
|
161
161
|
if (result.status !== 0) fail('Official OURS CLI operation failed');
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/** Import a stopped, read-only host migration stage into the managed Docker volume. */
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import { dirname, join, resolve, isAbsolute } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { randomUUID } from 'node:crypto';
|
|
6
|
+
// Repository layout and the maintenance image intentionally place this binding differently.
|
|
7
|
+
const nativeModule = new URL('../maintenance/state-native.mjs', import.meta.url);
|
|
8
|
+
const { publishNoReplace } = await import(fs.existsSync(nativeModule) ? nativeModule.href : new URL('./state-native.mjs', import.meta.url).href);
|
|
9
|
+
|
|
10
|
+
const MARKER = '.ours-legacy-import.json';
|
|
11
|
+
const COMPONENTS = ['mcp', 'daemon'];
|
|
12
|
+
const fail = message => { throw Error(`Legacy volume import: ${message}`); };
|
|
13
|
+
function canonical(path) {
|
|
14
|
+
if (typeof path !== 'string' || !isAbsolute(path) || resolve(path) !== path) fail('paths must be normalized absolute paths');
|
|
15
|
+
if (fs.realpathSync(path) !== path) fail('symlink paths are not supported');
|
|
16
|
+
}
|
|
17
|
+
function privateEntry(path, uid, gid, directory) {
|
|
18
|
+
const stat = fs.lstatSync(path);
|
|
19
|
+
if (!(directory ? stat.isDirectory() : stat.isFile()) || stat.isSymbolicLink()
|
|
20
|
+
|| stat.uid !== uid || stat.gid !== gid
|
|
21
|
+
|| (directory ? (stat.mode & 0o7777) !== 0o700 : ((stat.mode & 0o7077) !== 0 || !(stat.mode & 0o400)))
|
|
22
|
+
|| (!directory && stat.nlink !== 1)) fail('unsafe ownership, permissions, or linked state entry');
|
|
23
|
+
return stat;
|
|
24
|
+
}
|
|
25
|
+
function scan(root, uid, gid) {
|
|
26
|
+
const entries = [];
|
|
27
|
+
function visit(path, relative) {
|
|
28
|
+
const stat = fs.lstatSync(path), directory = stat.isDirectory();
|
|
29
|
+
privateEntry(path, uid, gid, directory); entries.push({ relative, directory, mode: stat.mode & 0o700 });
|
|
30
|
+
if (directory) for (const name of fs.readdirSync(path).sort()) visit(join(path, name), relative ? join(relative, name) : name);
|
|
31
|
+
}
|
|
32
|
+
canonical(root); visit(root, ''); return entries;
|
|
33
|
+
}
|
|
34
|
+
function readReceipt(root, uid, gid, targetRoot) {
|
|
35
|
+
privateEntry(join(root, MARKER), uid, gid, false);
|
|
36
|
+
const value = JSON.parse(fs.readFileSync(join(root, MARKER), 'utf8'));
|
|
37
|
+
const keys = ['sourceStateDir', 'sourceConfigPath', 'targetRoot', 'rootName'];
|
|
38
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)
|
|
39
|
+
|| Object.keys(value).sort().join('\0') !== [...keys].sort().join('\0')
|
|
40
|
+
|| keys.some(key => typeof value[key] !== 'string' || !value[key])
|
|
41
|
+
|| value.targetRoot !== targetRoot
|
|
42
|
+
|| ['sourceStateDir', 'sourceConfigPath', 'targetRoot'].some(key => !isAbsolute(value[key]) || resolve(value[key]) !== value[key])) fail('invalid or mismatched migration receipt');
|
|
43
|
+
return Object.fromEntries(keys.map(key => [key, value[key]]));
|
|
44
|
+
}
|
|
45
|
+
const same = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
|
46
|
+
function existingDestination(path, expected, uid, gid, targetRoot) {
|
|
47
|
+
if (!fs.existsSync(path)) return false;
|
|
48
|
+
canonical(path); privateEntry(path, uid, gid, true);
|
|
49
|
+
if (!fs.readdirSync(path).length) return false;
|
|
50
|
+
if (!fs.existsSync(join(path, MARKER)) || !same(readReceipt(path, uid, gid, targetRoot), expected)) fail('nonmatching existing destination');
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
function makeDirectory(path, uid, gid) {
|
|
54
|
+
if (!fs.existsSync(path)) { fs.mkdirSync(path, { mode: 0o700 }); fs.chownSync(path, uid, gid); }
|
|
55
|
+
canonical(path); privateEntry(path, uid, gid, true);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Caller holds installation exclusion and has stopped all destination writers. */
|
|
59
|
+
export function importLegacyVolume({ sourceRoot = '/legacy-import', storageRoot = '/storage', targetRoot, uid, gid }) {
|
|
60
|
+
if (![uid, gid].every(value => Number.isSafeInteger(value) && value >= 0)) fail('numeric target UID/GID required');
|
|
61
|
+
if (typeof targetRoot !== 'string' || !isAbsolute(targetRoot) || resolve(targetRoot) !== targetRoot) fail('expected target root required');
|
|
62
|
+
canonical(sourceRoot); canonical(storageRoot);
|
|
63
|
+
const sourceOwner = fs.lstatSync(sourceRoot);
|
|
64
|
+
privateEntry(sourceRoot, sourceOwner.uid, sourceOwner.gid, true);
|
|
65
|
+
const sources = COMPONENTS.map(component => {
|
|
66
|
+
const path = join(sourceRoot, component);
|
|
67
|
+
const entries = scan(path, sourceOwner.uid, sourceOwner.gid);
|
|
68
|
+
return { component, path, entries, receipt: readReceipt(path, sourceOwner.uid, sourceOwner.gid, targetRoot) };
|
|
69
|
+
});
|
|
70
|
+
if (!same(sources[0].receipt, sources[1].receipt)) fail('source daemon and MCP receipts differ');
|
|
71
|
+
const rootStat = fs.lstatSync(storageRoot);
|
|
72
|
+
const freshRoot = rootStat.isDirectory() && rootStat.uid === 0 && rootStat.gid === 0 && fs.readdirSync(storageRoot).length === 0;
|
|
73
|
+
if (!freshRoot) privateEntry(storageRoot, uid, gid, true);
|
|
74
|
+
const state = join(storageRoot, 'state');
|
|
75
|
+
if (fs.existsSync(state)) { canonical(state); privateEntry(state, uid, gid, true); }
|
|
76
|
+
// Validate every existing destination before changing even an empty volume.
|
|
77
|
+
for (const source of sources) source.reused = existingDestination(join(state, source.component), source.receipt, uid, gid, targetRoot);
|
|
78
|
+
if (freshRoot) { fs.chmodSync(storageRoot, 0o700); fs.chownSync(storageRoot, uid, gid); }
|
|
79
|
+
makeDirectory(state, uid, gid);
|
|
80
|
+
for (const source of sources) {
|
|
81
|
+
if (source.reused) continue;
|
|
82
|
+
const destination = join(state, source.component), staging = join(state, `.legacy-import-${randomUUID()}`);
|
|
83
|
+
try {
|
|
84
|
+
fs.cpSync(source.path, staging, { recursive: true, errorOnExist: true, force: false });
|
|
85
|
+
for (const entry of [...source.entries].reverse()) {
|
|
86
|
+
const path = entry.relative ? join(staging, entry.relative) : staging;
|
|
87
|
+
fs.chmodSync(path, entry.mode); fs.chownSync(path, uid, gid);
|
|
88
|
+
}
|
|
89
|
+
scan(staging, uid, gid);
|
|
90
|
+
if (!same(readReceipt(staging, uid, gid, targetRoot), source.receipt)) fail('copied receipt changed');
|
|
91
|
+
if (fs.existsSync(destination)) fs.rmdirSync(destination); // empty skeleton only
|
|
92
|
+
publishNoReplace(staging, destination);
|
|
93
|
+
} finally { fs.rmSync(staging, { recursive: true, force: true }); }
|
|
94
|
+
}
|
|
95
|
+
return { imported: sources.filter(source => !source.reused).map(source => source.component), retained: sources.filter(source => source.reused).map(source => source.component) };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
99
|
+
try {
|
|
100
|
+
importLegacyVolume({ targetRoot: process.env.OURS_LEGACY_TARGET_ROOT, uid: Number(process.env.OURS_UID), gid: Number(process.env.OURS_GID) });
|
|
101
|
+
console.log('Legacy daemon and MCP state imported into the selected volume.');
|
|
102
|
+
} catch (error) { console.error(error.message); process.exitCode = 1; }
|
|
103
|
+
}
|