@nextclaw/kernel 0.8.0 → 0.8.1
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/dist/index.d.ts +19 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +149 -43
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -2876,17 +2876,16 @@ var AppPackageManager = class {
|
|
|
2876
2876
|
installRuntimeHooks = (hooks) => {
|
|
2877
2877
|
this.runtimeHooks = hooks;
|
|
2878
2878
|
};
|
|
2879
|
-
|
|
2880
|
-
|
|
2879
|
+
start = async () => await this.ensureBuiltInPackages();
|
|
2880
|
+
listPackages = async (options = {}) => {
|
|
2881
2881
|
const records = await this.registryService.listApps();
|
|
2882
|
-
return { entries: await Promise.all(records.map(async (record) => await this.toPackageView(await this.installationService.info(record.appId)))) };
|
|
2882
|
+
return { entries: await Promise.all(records.map(async (record) => await this.toPackageView(await this.installationService.info(record.appId, { measureStorageUsage: options.includeStorageUsage !== false })))) };
|
|
2883
2883
|
};
|
|
2884
2884
|
listInstalledDataOwners = async () => (await this.registryService.listApps()).map((record) => ({
|
|
2885
2885
|
id: record.appId,
|
|
2886
2886
|
name: record.name
|
|
2887
2887
|
}));
|
|
2888
2888
|
getPackage = async (appId) => {
|
|
2889
|
-
await this.ensureBuiltInPackages();
|
|
2890
2889
|
try {
|
|
2891
2890
|
return await this.toPackageView(await this.installationService.info(appId));
|
|
2892
2891
|
} catch (error) {
|
|
@@ -2895,7 +2894,6 @@ var AppPackageManager = class {
|
|
|
2895
2894
|
}
|
|
2896
2895
|
};
|
|
2897
2896
|
listActiveComponentSources = async () => {
|
|
2898
|
-
await this.ensureBuiltInPackages();
|
|
2899
2897
|
const records = await this.registryService.listApps();
|
|
2900
2898
|
return (await Promise.all(records.filter((record) => record.enabled).map(async (record) => {
|
|
2901
2899
|
await this.installationService.assertVersionIntegrity(record.appId, record.activeVersion);
|
|
@@ -3212,10 +3210,22 @@ var AppDataManager = class {
|
|
|
3212
3210
|
appHomeService;
|
|
3213
3211
|
fileLockService = new FileLockService();
|
|
3214
3212
|
inventoryService = new AppInstanceInventoryService();
|
|
3213
|
+
reconciliationDiagnostics = [];
|
|
3215
3214
|
constructor(params) {
|
|
3216
3215
|
this.params = params;
|
|
3217
3216
|
this.appHomeService = new AppHomeService(params.appHomeDirectory);
|
|
3218
3217
|
}
|
|
3218
|
+
start = async () => {
|
|
3219
|
+
const workspacePath = path.resolve(this.params.getWorkspacePath());
|
|
3220
|
+
const [packageDiagnostics, workspaceDiagnostics] = await Promise.all([this.inventoryService.reconcileDeletions(this.appHomeService.getInstancesDirectory()), this.inventoryService.reconcileDeletions(this.getWorkspaceInstancesRoot(workspacePath))]);
|
|
3221
|
+
this.reconciliationDiagnostics = [...packageDiagnostics.map((entry) => ({
|
|
3222
|
+
...entry,
|
|
3223
|
+
source: "package"
|
|
3224
|
+
})), ...workspaceDiagnostics.map((entry) => ({
|
|
3225
|
+
...entry,
|
|
3226
|
+
source: "workspace-service"
|
|
3227
|
+
}))];
|
|
3228
|
+
};
|
|
3219
3229
|
list = async () => {
|
|
3220
3230
|
const workspacePath = path.resolve(this.params.getWorkspacePath());
|
|
3221
3231
|
const workspaceInstancesRoot = this.getWorkspaceInstancesRoot(workspacePath);
|
|
@@ -3228,26 +3238,31 @@ var AppDataManager = class {
|
|
|
3228
3238
|
const packageOwners = new Map(packageList.map((entry) => [entry.id, entry]));
|
|
3229
3239
|
const workspaceOwnerMap = new Map(workspaceOwners.map((entry) => [entry.id, entry]));
|
|
3230
3240
|
const workspaceScope = this.workspaceScope(workspacePath);
|
|
3241
|
+
const entries = [...packageInventory.entries.map((entry) => this.toEntry({
|
|
3242
|
+
entry,
|
|
3243
|
+
source: "package",
|
|
3244
|
+
displayName: packageOwners.get(entry.appId)?.name ?? entry.appId,
|
|
3245
|
+
active: packageOwners.has(entry.appId)
|
|
3246
|
+
})), ...workspaceInventory.entries.map((entry) => this.toEntry({
|
|
3247
|
+
entry,
|
|
3248
|
+
source: "workspace-service",
|
|
3249
|
+
displayName: workspaceOwnerMap.get(entry.appId)?.title ?? entry.appId,
|
|
3250
|
+
active: workspaceOwnerMap.has(entry.appId),
|
|
3251
|
+
scope: workspaceScope
|
|
3252
|
+
}))].sort((left, right) => left.displayName.localeCompare(right.displayName) || left.id.localeCompare(right.id));
|
|
3253
|
+
const currentDiagnostics = [...packageInventory.diagnostics.map((diagnostic) => ({
|
|
3254
|
+
...diagnostic,
|
|
3255
|
+
source: "package"
|
|
3256
|
+
})), ...workspaceInventory.diagnostics.map((diagnostic) => ({
|
|
3257
|
+
...diagnostic,
|
|
3258
|
+
source: "workspace-service"
|
|
3259
|
+
}))];
|
|
3260
|
+
const currentDiagnosticKeys = new Set(currentDiagnostics.map((entry) => `${entry.source}:${entry.instanceDirectory}`));
|
|
3261
|
+
const diagnostics = /* @__PURE__ */ new Map();
|
|
3262
|
+
for (const entry of [...currentDiagnostics, ...this.reconciliationDiagnostics.filter((diagnostic) => currentDiagnosticKeys.has(`${diagnostic.source}:${diagnostic.instanceDirectory}`))]) diagnostics.set(`${entry.source}:${entry.instanceDirectory}`, entry);
|
|
3231
3263
|
return {
|
|
3232
|
-
entries
|
|
3233
|
-
|
|
3234
|
-
source: "package",
|
|
3235
|
-
displayName: packageOwners.get(entry.appId)?.name ?? entry.appId,
|
|
3236
|
-
active: packageOwners.has(entry.appId)
|
|
3237
|
-
})), ...workspaceInventory.entries.map((entry) => this.toEntry({
|
|
3238
|
-
entry,
|
|
3239
|
-
source: "workspace-service",
|
|
3240
|
-
displayName: workspaceOwnerMap.get(entry.appId)?.title ?? entry.appId,
|
|
3241
|
-
active: workspaceOwnerMap.has(entry.appId),
|
|
3242
|
-
scope: workspaceScope
|
|
3243
|
-
}))].sort((left, right) => left.displayName.localeCompare(right.displayName) || left.id.localeCompare(right.id)),
|
|
3244
|
-
diagnostics: [...packageInventory.diagnostics.map((entry) => ({
|
|
3245
|
-
...entry,
|
|
3246
|
-
source: "package"
|
|
3247
|
-
})), ...workspaceInventory.diagnostics.map((entry) => ({
|
|
3248
|
-
...entry,
|
|
3249
|
-
source: "workspace-service"
|
|
3250
|
-
}))]
|
|
3264
|
+
entries,
|
|
3265
|
+
diagnostics: Array.from(diagnostics.values())
|
|
3251
3266
|
};
|
|
3252
3267
|
};
|
|
3253
3268
|
deleteRetained = async (dataId, confirmAppId) => {
|
|
@@ -10115,7 +10130,8 @@ var ServiceAppRecordService = class {
|
|
|
10115
10130
|
const dirPath = join(serviceAppsPath, dirName);
|
|
10116
10131
|
try {
|
|
10117
10132
|
const manifest = await readServiceAppManifest(dirPath);
|
|
10118
|
-
|
|
10133
|
+
if (manifest.id !== dirName) throw new Error(`service app manifest id must match directory name: ${dirName}`);
|
|
10134
|
+
return this.fromManifest(dirPath, manifest, void 0, await this.inspectWorkspaceStorage(manifest.id));
|
|
10119
10135
|
} catch (error) {
|
|
10120
10136
|
if (this.isMissingFileError(error)) return null;
|
|
10121
10137
|
return this.failedWorkspaceRecord(dirName, dirPath, error);
|
|
@@ -10144,13 +10160,22 @@ var ServiceAppRecordService = class {
|
|
|
10144
10160
|
}))).filter((entry) => Boolean(entry));
|
|
10145
10161
|
};
|
|
10146
10162
|
materializeWorkspaceStorage = async (serviceId) => {
|
|
10147
|
-
const instanceDirectory =
|
|
10163
|
+
const instanceDirectory = this.getWorkspaceInstanceDirectory(serviceId);
|
|
10148
10164
|
return (await this.instanceStorageService.materialize({
|
|
10149
10165
|
appId: serviceId,
|
|
10150
10166
|
instanceId: "default",
|
|
10151
10167
|
instanceDirectory
|
|
10152
10168
|
})).storage;
|
|
10153
10169
|
};
|
|
10170
|
+
inspectWorkspaceStorage = async (serviceId) => {
|
|
10171
|
+
const instanceDirectory = this.getWorkspaceInstanceDirectory(serviceId);
|
|
10172
|
+
if (!await this.pathExists(instanceDirectory)) return;
|
|
10173
|
+
return (await this.instanceStorageService.inspect({
|
|
10174
|
+
appId: serviceId,
|
|
10175
|
+
instanceId: "default",
|
|
10176
|
+
instanceDirectory
|
|
10177
|
+
})).storage;
|
|
10178
|
+
};
|
|
10154
10179
|
fromManifest = (dirPath, manifest, packageSource, storage) => {
|
|
10155
10180
|
const runtimeStatus = this.params.runtimeService.getStatus(manifest.id);
|
|
10156
10181
|
return {
|
|
@@ -10210,10 +10235,26 @@ var ServiceAppRecordService = class {
|
|
|
10210
10235
|
isolation: source.isolation
|
|
10211
10236
|
});
|
|
10212
10237
|
toTitle = (value) => basename(value).replace(/[-_]+/g, " ").trim() || value;
|
|
10238
|
+
getWorkspaceInstanceDirectory = (serviceId) => join(this.params.getWorkspacePath(), ".nextclaw", "app-instances", serviceId, "default");
|
|
10239
|
+
pathExists = async (targetPath) => {
|
|
10240
|
+
try {
|
|
10241
|
+
await access(targetPath);
|
|
10242
|
+
return true;
|
|
10243
|
+
} catch (error) {
|
|
10244
|
+
if (this.isMissingFileError(error)) return false;
|
|
10245
|
+
throw error;
|
|
10246
|
+
}
|
|
10247
|
+
};
|
|
10213
10248
|
isMissingFileError = (error) => typeof error === "object" && error !== null && error.code === "ENOENT";
|
|
10214
10249
|
};
|
|
10215
10250
|
//#endregion
|
|
10216
10251
|
//#region src/services/service-app-removal.service.ts
|
|
10252
|
+
const TRANSACTION_ID = "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}";
|
|
10253
|
+
const CURRENT_DELETION_PATTERN = new RegExp(`^\\.deleting-(?<appId>[a-z0-9]+(?:-[a-z0-9]+)*)-(?<transactionId>${TRANSACTION_ID})$`);
|
|
10254
|
+
const LEGACY_DELETION_PATTERN = new RegExp(`^(?<appId>[a-z0-9]+(?:-[a-z0-9]+)*)\\.deleting-(?<transactionId>${TRANSACTION_ID})$`);
|
|
10255
|
+
function isServiceAppDeletionTombstone(directoryName) {
|
|
10256
|
+
return Boolean(parseServiceAppDeletionTombstone(directoryName));
|
|
10257
|
+
}
|
|
10217
10258
|
var ServiceAppRemovalCleanupError = class extends Error {
|
|
10218
10259
|
constructor(cause) {
|
|
10219
10260
|
super(`临时目录清理失败:${cause instanceof Error ? cause.message : String(cause)}`);
|
|
@@ -10223,6 +10264,7 @@ var ServiceAppRemovalCleanupError = class extends Error {
|
|
|
10223
10264
|
};
|
|
10224
10265
|
var ServiceAppRemovalService = class {
|
|
10225
10266
|
fileLockService = new FileLockService();
|
|
10267
|
+
listCanonicalDirectoryNames = async (serviceAppsPath) => (await this.listDirectories(serviceAppsPath)).filter((directoryName) => !directoryName.startsWith(".") && !isServiceAppDeletionTombstone(directoryName));
|
|
10226
10268
|
remove = async (params) => await this.fileLockService.withLock(params.lockPath, async () => {
|
|
10227
10269
|
const record = await params.loadRecord();
|
|
10228
10270
|
await this.removeLocked({
|
|
@@ -10231,14 +10273,39 @@ var ServiceAppRemovalService = class {
|
|
|
10231
10273
|
});
|
|
10232
10274
|
return record;
|
|
10233
10275
|
});
|
|
10276
|
+
reconcile = async (params) => {
|
|
10277
|
+
const { grantStore, lockPathForAppId, serviceAppsPath } = params;
|
|
10278
|
+
const diagnostics = [];
|
|
10279
|
+
for (const directoryName of await this.listDirectories(serviceAppsPath)) {
|
|
10280
|
+
const appId = parseServiceAppDeletionTombstone(directoryName);
|
|
10281
|
+
if (!appId) continue;
|
|
10282
|
+
const stagedPath = join(serviceAppsPath, directoryName);
|
|
10283
|
+
const canonicalPath = join(serviceAppsPath, appId);
|
|
10284
|
+
try {
|
|
10285
|
+
await this.fileLockService.withLock(lockPathForAppId(appId), async () => {
|
|
10286
|
+
const manifest = await readServiceAppManifest(stagedPath);
|
|
10287
|
+
if (manifest.id !== appId) throw new Error(`Service App 删除墓碑 identity 不匹配:${manifest.id}`);
|
|
10288
|
+
if (!await this.pathExists(canonicalPath)) await grantStore.revokeActionsByPrefix(`${appId}.`);
|
|
10289
|
+
await rm(stagedPath, { recursive: true });
|
|
10290
|
+
});
|
|
10291
|
+
} catch (error) {
|
|
10292
|
+
diagnostics.push({
|
|
10293
|
+
appId,
|
|
10294
|
+
stagedPath,
|
|
10295
|
+
message: error instanceof Error ? error.message : String(error)
|
|
10296
|
+
});
|
|
10297
|
+
}
|
|
10298
|
+
}
|
|
10299
|
+
return diagnostics.sort((left, right) => left.stagedPath.localeCompare(right.stagedPath));
|
|
10300
|
+
};
|
|
10234
10301
|
removeLocked = async (params) => {
|
|
10235
10302
|
const { grantStore, purgeData, record, stopRuntime } = params;
|
|
10236
10303
|
const grants = (await grantStore.list()).filter((grant) => grant.actionId.startsWith(`${record.id}.`));
|
|
10237
10304
|
const stagedPaths = [];
|
|
10238
10305
|
try {
|
|
10239
10306
|
await stopRuntime(record);
|
|
10240
|
-
await this.
|
|
10241
|
-
if (purgeData && record.storage) await this.
|
|
10307
|
+
await this.stageSourcePath(record.dirPath, stagedPaths);
|
|
10308
|
+
if (purgeData && record.storage) await this.stageInstancePath(record.storage.instanceDirectory, stagedPaths);
|
|
10242
10309
|
await grantStore.revokeActionsByPrefix(`${record.id}.`);
|
|
10243
10310
|
} catch (error) {
|
|
10244
10311
|
const recoveryErrors = [...await this.restoreStagedPaths(stagedPaths), ...await this.restoreGrants(grantStore, grants)];
|
|
@@ -10251,7 +10318,15 @@ var ServiceAppRemovalService = class {
|
|
|
10251
10318
|
throw new ServiceAppRemovalCleanupError(error);
|
|
10252
10319
|
}
|
|
10253
10320
|
};
|
|
10254
|
-
|
|
10321
|
+
stageSourcePath = async (originalPath, stagedPaths) => {
|
|
10322
|
+
const stagedPath = join(dirname(originalPath), `.deleting-${basename(originalPath)}-${randomUUID()}`);
|
|
10323
|
+
await rename(originalPath, stagedPath);
|
|
10324
|
+
stagedPaths.push({
|
|
10325
|
+
originalPath,
|
|
10326
|
+
stagedPath
|
|
10327
|
+
});
|
|
10328
|
+
};
|
|
10329
|
+
stageInstancePath = async (originalPath, stagedPaths) => {
|
|
10255
10330
|
const stagedPath = `${originalPath}.deleting-${randomUUID()}`;
|
|
10256
10331
|
await rename(originalPath, stagedPath);
|
|
10257
10332
|
stagedPaths.push({
|
|
@@ -10259,6 +10334,24 @@ var ServiceAppRemovalService = class {
|
|
|
10259
10334
|
stagedPath
|
|
10260
10335
|
});
|
|
10261
10336
|
};
|
|
10337
|
+
listDirectories = async (directory) => {
|
|
10338
|
+
try {
|
|
10339
|
+
return (await readdir(directory, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
10340
|
+
} catch (error) {
|
|
10341
|
+
if (this.isMissingFileError(error)) return [];
|
|
10342
|
+
throw error;
|
|
10343
|
+
}
|
|
10344
|
+
};
|
|
10345
|
+
pathExists = async (targetPath) => {
|
|
10346
|
+
try {
|
|
10347
|
+
await access(targetPath);
|
|
10348
|
+
return true;
|
|
10349
|
+
} catch (error) {
|
|
10350
|
+
if (this.isMissingFileError(error)) return false;
|
|
10351
|
+
throw error;
|
|
10352
|
+
}
|
|
10353
|
+
};
|
|
10354
|
+
isMissingFileError = (error) => typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
10262
10355
|
restoreStagedPaths = async (stagedPaths) => {
|
|
10263
10356
|
const errors = [];
|
|
10264
10357
|
for (const entry of [...stagedPaths].reverse()) try {
|
|
@@ -10278,6 +10371,9 @@ var ServiceAppRemovalService = class {
|
|
|
10278
10371
|
return errors;
|
|
10279
10372
|
};
|
|
10280
10373
|
};
|
|
10374
|
+
function parseServiceAppDeletionTombstone(directoryName) {
|
|
10375
|
+
return CURRENT_DELETION_PATTERN.exec(directoryName)?.groups?.appId ?? LEGACY_DELETION_PATTERN.exec(directoryName)?.groups?.appId;
|
|
10376
|
+
}
|
|
10281
10377
|
//#endregion
|
|
10282
10378
|
//#region src/stores/service-action-grant.store.ts
|
|
10283
10379
|
const EMPTY_GRANTS = {
|
|
@@ -10425,13 +10521,12 @@ var ServiceAppError = class extends Error {
|
|
|
10425
10521
|
this.name = "ServiceAppError";
|
|
10426
10522
|
}
|
|
10427
10523
|
};
|
|
10428
|
-
|
|
10429
|
-
return error instanceof ServiceAppError;
|
|
10430
|
-
}
|
|
10524
|
+
const isServiceAppError = (error) => error instanceof ServiceAppError;
|
|
10431
10525
|
var ServiceAppManager = class {
|
|
10432
10526
|
removalService = new ServiceAppRemovalService();
|
|
10433
10527
|
runtimeService;
|
|
10434
10528
|
recordService;
|
|
10529
|
+
reconciliationDiagnostics = [];
|
|
10435
10530
|
constructor(params) {
|
|
10436
10531
|
this.params = params;
|
|
10437
10532
|
this.runtimeService = params.runtimeService ?? new McpServiceAppRuntimeService({ getConfig: () => params.configManager.config });
|
|
@@ -10440,6 +10535,13 @@ var ServiceAppManager = class {
|
|
|
10440
10535
|
runtimeService: this.runtimeService
|
|
10441
10536
|
});
|
|
10442
10537
|
}
|
|
10538
|
+
start = async () => {
|
|
10539
|
+
this.reconciliationDiagnostics = await this.removalService.reconcile({
|
|
10540
|
+
grantStore: this.createGrantStore(),
|
|
10541
|
+
lockPathForAppId: (appId) => this.getServiceAppLockPath(this.getWorkspacePath(), appId),
|
|
10542
|
+
serviceAppsPath: this.getServiceAppsPath(this.getWorkspacePath())
|
|
10543
|
+
});
|
|
10544
|
+
};
|
|
10443
10545
|
listServiceApps = async () => {
|
|
10444
10546
|
const workspacePath = this.getWorkspacePath();
|
|
10445
10547
|
const serviceAppsPath = this.getServiceAppsPath(workspacePath);
|
|
@@ -10450,6 +10552,7 @@ var ServiceAppManager = class {
|
|
|
10450
10552
|
return {
|
|
10451
10553
|
workspacePath,
|
|
10452
10554
|
serviceAppsPath,
|
|
10555
|
+
diagnostics: this.reconciliationDiagnostics,
|
|
10453
10556
|
entries: [...workspaceEntries, ...packageEntries].filter((entry) => Boolean(entry)).sort((left, right) => left.title.localeCompare(right.title))
|
|
10454
10557
|
};
|
|
10455
10558
|
};
|
|
@@ -10462,7 +10565,7 @@ var ServiceAppManager = class {
|
|
|
10462
10565
|
return await Promise.all(actions.map(async (action) => await this.withGrantState(action, params)));
|
|
10463
10566
|
};
|
|
10464
10567
|
discoverServiceAppActions = async (appId) => {
|
|
10465
|
-
const { manifest, record } = await this.requireServiceApp(appId);
|
|
10568
|
+
const { manifest, record } = await this.requireServiceApp(appId, true);
|
|
10466
10569
|
return mergeServiceAppRuntimeActions({
|
|
10467
10570
|
record,
|
|
10468
10571
|
manifest,
|
|
@@ -10475,7 +10578,7 @@ var ServiceAppManager = class {
|
|
|
10475
10578
|
invokeServiceAction = async (actionId, request) => {
|
|
10476
10579
|
this.assertCaller(request.caller);
|
|
10477
10580
|
this.assertDeclaredAction(actionId, request.declaredActions);
|
|
10478
|
-
const { manifest, record } = await this.requireServiceAppForAction(actionId);
|
|
10581
|
+
const { manifest, record } = await this.requireServiceAppForAction(actionId, true);
|
|
10479
10582
|
const actionName = getServiceActionName(actionId, record.id);
|
|
10480
10583
|
if (!Object.hasOwn(manifest.actions, actionName)) throw new ServiceAppError("SERVICE_APP_ACTION_NOT_FOUND", "service action not found");
|
|
10481
10584
|
const declaredRisk = manifest.actions[actionName]?.risk ?? "dangerous";
|
|
@@ -10535,12 +10638,11 @@ var ServiceAppManager = class {
|
|
|
10535
10638
|
try {
|
|
10536
10639
|
record = await this.removalService.remove({
|
|
10537
10640
|
grantStore: this.createGrantStore(),
|
|
10538
|
-
lockPath:
|
|
10641
|
+
lockPath: this.getServiceAppLockPath(workspacePath, appId),
|
|
10539
10642
|
purgeData,
|
|
10540
10643
|
loadRecord: async () => {
|
|
10541
10644
|
const { record } = await this.requireServiceApp(appId);
|
|
10542
10645
|
if (record.sourceKind === "package") throw new ServiceAppError("SERVICE_APP_MANAGED_SOURCE", `package service must be managed through Apps: ${record.packageId}`);
|
|
10543
|
-
if (purgeData && !record.storage) throw new ServiceAppError("SERVICE_APP_READ_FAILED", `Service App ${appId} 缺少受管数据目录,已停止删除。`);
|
|
10544
10646
|
return record;
|
|
10545
10647
|
},
|
|
10546
10648
|
stopRuntime: async (loaded) => await this.runtimeService.restart(loaded.id)
|
|
@@ -10624,12 +10726,12 @@ var ServiceAppManager = class {
|
|
|
10624
10726
|
if (!action) throw new ServiceAppError("SERVICE_APP_ACTION_NOT_FOUND", "service action not found");
|
|
10625
10727
|
return action;
|
|
10626
10728
|
};
|
|
10627
|
-
requireServiceAppForAction = async (actionId) => {
|
|
10729
|
+
requireServiceAppForAction = async (actionId, materializeStorage = false) => {
|
|
10628
10730
|
const appId = actionId.split(".")[0]?.trim();
|
|
10629
10731
|
if (!appId) throw new ServiceAppError("SERVICE_APP_INVALID_ACTION", "service action id is invalid");
|
|
10630
|
-
return await this.requireServiceApp(appId);
|
|
10732
|
+
return await this.requireServiceApp(appId, materializeStorage);
|
|
10631
10733
|
};
|
|
10632
|
-
requireServiceApp = async (appId) => {
|
|
10734
|
+
requireServiceApp = async (appId, materializeStorage = false) => {
|
|
10633
10735
|
let dirPath = join(this.getServiceAppsPath(this.getWorkspacePath()), appId);
|
|
10634
10736
|
let packageSource;
|
|
10635
10737
|
try {
|
|
@@ -10644,9 +10746,10 @@ var ServiceAppManager = class {
|
|
|
10644
10746
|
if (!(await stat(dirPath)).isDirectory()) throw new ServiceAppError("SERVICE_APP_NOT_FOUND", "service app not found");
|
|
10645
10747
|
const manifest = await readServiceAppManifest(dirPath);
|
|
10646
10748
|
if (manifest.id !== appId) throw new ServiceAppError("SERVICE_APP_INVALID_MANIFEST", "service app manifest id must match directory name");
|
|
10749
|
+
const storage = packageSource?.storage ?? (materializeStorage ? await this.recordService.materializeWorkspaceStorage(manifest.id) : await this.recordService.inspectWorkspaceStorage(manifest.id));
|
|
10647
10750
|
return {
|
|
10648
10751
|
manifest,
|
|
10649
|
-
record: this.recordService.fromManifest(dirPath, manifest, packageSource,
|
|
10752
|
+
record: this.recordService.fromManifest(dirPath, manifest, packageSource, storage)
|
|
10650
10753
|
};
|
|
10651
10754
|
} catch (error) {
|
|
10652
10755
|
if (isServiceAppError(error)) throw error;
|
|
@@ -10664,7 +10767,7 @@ var ServiceAppManager = class {
|
|
|
10664
10767
|
const manifest = await readServiceAppManifest(dirPath);
|
|
10665
10768
|
return {
|
|
10666
10769
|
manifest,
|
|
10667
|
-
record: this.recordService.fromManifest(dirPath, manifest, void 0, await this.recordService.
|
|
10770
|
+
record: this.recordService.fromManifest(dirPath, manifest, void 0, await this.recordService.inspectWorkspaceStorage(manifest.id))
|
|
10668
10771
|
};
|
|
10669
10772
|
} catch {
|
|
10670
10773
|
return null;
|
|
@@ -10692,13 +10795,13 @@ var ServiceAppManager = class {
|
|
|
10692
10795
|
normalizeActionIds = (actionIds) => Array.from(new Set(actionIds.map((actionId) => actionId.trim()).filter((actionId) => actionId.length > 0)));
|
|
10693
10796
|
getWorkspacePath = () => getWorkspacePathFromConfig(this.params.configManager.config);
|
|
10694
10797
|
getServiceAppsPath = (workspacePath) => join(workspacePath, DEFAULT_SERVICE_APPS_DIR);
|
|
10798
|
+
getServiceAppLockPath = (workspacePath, appId) => join(workspacePath, ".nextclaw", "locks", "service-apps", `${appId}.lock`);
|
|
10695
10799
|
createGrantStore = () => new ServiceActionGrantStore(join(this.getServiceAppsPath(this.getWorkspacePath()), SERVICE_ACTION_GRANTS_FILE_NAME));
|
|
10696
10800
|
listPackageComponentSources = async () => await this.params.listPackageComponentSources?.() ?? [];
|
|
10697
10801
|
listServiceAppDirNames = async (serviceAppsPath) => {
|
|
10698
10802
|
try {
|
|
10699
|
-
return
|
|
10803
|
+
return await this.removalService.listCanonicalDirectoryNames(serviceAppsPath);
|
|
10700
10804
|
} catch (error) {
|
|
10701
|
-
if (this.isMissingFileError(error)) return [];
|
|
10702
10805
|
throw new ServiceAppError("SERVICE_APP_READ_FAILED", error instanceof Error ? error.message : String(error));
|
|
10703
10806
|
}
|
|
10704
10807
|
};
|
|
@@ -15970,6 +16073,9 @@ var NextclawKernel = class {
|
|
|
15970
16073
|
};
|
|
15971
16074
|
getGatewayController = () => this.gatewayController;
|
|
15972
16075
|
start = async () => {
|
|
16076
|
+
await this.appPackageManager.start();
|
|
16077
|
+
await this.appDataManager.start();
|
|
16078
|
+
await this.serviceAppManager.start();
|
|
15973
16079
|
this.sessionSearch.start();
|
|
15974
16080
|
this.mcpManager.start();
|
|
15975
16081
|
this.providerModelCatalog.start();
|