@nextclaw/kernel 0.15.1 → 0.15.2
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 +39 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +435 -82
- package/dist/index.js.map +1 -1
- package/package.json +9 -9
package/dist/index.js
CHANGED
|
@@ -4404,7 +4404,7 @@ var AppPackageDependencyCoordinator = class {
|
|
|
4404
4404
|
});
|
|
4405
4405
|
resolveStoredProviderIds = async (target) => await this.dependencyService.resolveStoredProviderIds(target);
|
|
4406
4406
|
listBindings = async (target) => await this.dependencyService.listBindings(target);
|
|
4407
|
-
|
|
4407
|
+
loadActiveComponentSourcesWithDiagnostics = async () => {
|
|
4408
4408
|
const records = await this.params.registryService.listApps();
|
|
4409
4409
|
const results = await Promise.all(records.filter((record) => record.enabled).map(async (record) => {
|
|
4410
4410
|
try {
|
|
@@ -4542,6 +4542,54 @@ var AppPackageDependencyCoordinator = class {
|
|
|
4542
4542
|
};
|
|
4543
4543
|
};
|
|
4544
4544
|
//#endregion
|
|
4545
|
+
//#region src/services/app-package-component-catalog.service.ts
|
|
4546
|
+
const EMPTY_COMPONENT_CATALOG = {
|
|
4547
|
+
sources: [],
|
|
4548
|
+
unavailablePackages: []
|
|
4549
|
+
};
|
|
4550
|
+
/**
|
|
4551
|
+
* Owns the verified projection of active package components.
|
|
4552
|
+
*
|
|
4553
|
+
* Expensive package integrity checks happen while refreshing this catalog at
|
|
4554
|
+
* lifecycle boundaries. Product reads consume the last complete snapshot and
|
|
4555
|
+
* never trigger a package-tree scan after initialization.
|
|
4556
|
+
*/
|
|
4557
|
+
var AppPackageComponentCatalogService = class {
|
|
4558
|
+
snapshot = EMPTY_COMPONENT_CATALOG;
|
|
4559
|
+
requestedRevision = 0;
|
|
4560
|
+
appliedRevision = 0;
|
|
4561
|
+
refreshPromise;
|
|
4562
|
+
constructor(load) {
|
|
4563
|
+
this.load = load;
|
|
4564
|
+
}
|
|
4565
|
+
read = async () => {
|
|
4566
|
+
if (this.appliedRevision === 0) await this.refresh();
|
|
4567
|
+
return cloneCatalog(this.snapshot);
|
|
4568
|
+
};
|
|
4569
|
+
refresh = async () => {
|
|
4570
|
+
this.requestedRevision += 1;
|
|
4571
|
+
this.refreshPromise ??= this.runRefreshLoop();
|
|
4572
|
+
await this.refreshPromise;
|
|
4573
|
+
};
|
|
4574
|
+
runRefreshLoop = async () => {
|
|
4575
|
+
try {
|
|
4576
|
+
while (this.appliedRevision < this.requestedRevision) {
|
|
4577
|
+
const targetRevision = this.requestedRevision;
|
|
4578
|
+
this.snapshot = cloneCatalog(await this.load());
|
|
4579
|
+
this.appliedRevision = targetRevision;
|
|
4580
|
+
}
|
|
4581
|
+
} finally {
|
|
4582
|
+
this.refreshPromise = void 0;
|
|
4583
|
+
}
|
|
4584
|
+
};
|
|
4585
|
+
};
|
|
4586
|
+
function cloneCatalog(catalog) {
|
|
4587
|
+
return {
|
|
4588
|
+
sources: catalog.sources.map((source) => ({ ...source })),
|
|
4589
|
+
unavailablePackages: catalog.unavailablePackages.map((diagnostic) => ({ ...diagnostic }))
|
|
4590
|
+
};
|
|
4591
|
+
}
|
|
4592
|
+
//#endregion
|
|
4545
4593
|
//#region src/services/app-package-document-access.service.ts
|
|
4546
4594
|
var AppPackageDocumentAccessService = class {
|
|
4547
4595
|
constructor(params) {
|
|
@@ -4709,6 +4757,7 @@ var AppPackageManager = class {
|
|
|
4709
4757
|
hostTargetService = new AppPackageHostTargetService();
|
|
4710
4758
|
presentationService = new AppPackagePresentationService();
|
|
4711
4759
|
dependencyCoordinator;
|
|
4760
|
+
componentCatalog;
|
|
4712
4761
|
runtimeActivationService = new AppPackageRuntimeActivationService();
|
|
4713
4762
|
registryService;
|
|
4714
4763
|
grantService;
|
|
@@ -4736,6 +4785,7 @@ var AppPackageManager = class {
|
|
|
4736
4785
|
listCapabilityProviders: async () => await this.runtimeHooks.listCapabilityProviders(),
|
|
4737
4786
|
resolveSecurity: this.resolveSecurity
|
|
4738
4787
|
});
|
|
4788
|
+
this.componentCatalog = new AppPackageComponentCatalogService(this.dependencyCoordinator.loadActiveComponentSourcesWithDiagnostics);
|
|
4739
4789
|
this.readinessManager = new AppPackageReadinessManager({
|
|
4740
4790
|
manifestService: this.manifestService,
|
|
4741
4791
|
installationService: this.installationService,
|
|
@@ -4759,7 +4809,10 @@ var AppPackageManager = class {
|
|
|
4759
4809
|
...hooks
|
|
4760
4810
|
};
|
|
4761
4811
|
};
|
|
4762
|
-
start = async () =>
|
|
4812
|
+
start = async () => {
|
|
4813
|
+
await this.ensureBuiltInPackages();
|
|
4814
|
+
await this.componentCatalog.refresh();
|
|
4815
|
+
};
|
|
4763
4816
|
listPackages = async (options = {}) => {
|
|
4764
4817
|
const [records, providers] = await Promise.all([this.registryService.listApps(), this.runtimeHooks.listCapabilityProviders()]);
|
|
4765
4818
|
const entries = (await Promise.all(records.map(async (record) => await this.installationService.withAppOperation(record.appId, async () => await this.readListedPackageView(record.appId, options.includeStorageUsage !== false, providers))))).filter((entry) => entry !== void 0);
|
|
@@ -4784,9 +4837,21 @@ var AppPackageManager = class {
|
|
|
4784
4837
|
};
|
|
4785
4838
|
inspectDependencies = async (appId) => await this.installationService.withAppOperation(appId, async () => await this.dependencyCoordinator.inspect(appId));
|
|
4786
4839
|
verifyDependencies = async (appId) => await this.installationService.withAppOperation(appId, async () => await this.dependencyCoordinator.verify(appId));
|
|
4787
|
-
setupDependencies = async (appId) =>
|
|
4788
|
-
|
|
4789
|
-
|
|
4840
|
+
setupDependencies = async (appId) => {
|
|
4841
|
+
const result = await this.dependencyCoordinator.setup(appId);
|
|
4842
|
+
await this.componentCatalog.refresh();
|
|
4843
|
+
return result;
|
|
4844
|
+
};
|
|
4845
|
+
bindDependency = async (appId, input) => {
|
|
4846
|
+
const result = await this.dependencyCoordinator.bind(appId, input);
|
|
4847
|
+
await this.componentCatalog.refresh();
|
|
4848
|
+
return result;
|
|
4849
|
+
};
|
|
4850
|
+
unbindDependency = async (appId, input) => {
|
|
4851
|
+
const result = await this.dependencyCoordinator.unbind(appId, input);
|
|
4852
|
+
await this.componentCatalog.refresh();
|
|
4853
|
+
return result;
|
|
4854
|
+
};
|
|
4790
4855
|
inspectSecrets = async (appId) => await this.installationService.withAppOperation(appId, async () => await this.readinessManager.inspectSecrets(await this.installationService.info(appId), false));
|
|
4791
4856
|
verifySecrets = async (appId) => await this.installationService.withAppOperation(appId, async () => await this.readinessManager.inspectSecrets(await this.installationService.info(appId), true));
|
|
4792
4857
|
bindSecret = async (appId, input) => await this.installationService.withAppOperation(appId, async () => {
|
|
@@ -4810,7 +4875,7 @@ var AppPackageManager = class {
|
|
|
4810
4875
|
grantDocumentAccess = async (appId, input) => await this.documentAccessService.grant(appId, input);
|
|
4811
4876
|
revokeDocumentAccess = async (appId, scopeId) => await this.documentAccessService.revoke(appId, scopeId);
|
|
4812
4877
|
listActiveComponentSources = async () => (await this.listActiveComponentSourcesWithDiagnostics()).sources;
|
|
4813
|
-
listActiveComponentSourcesWithDiagnostics = async () => await this.
|
|
4878
|
+
listActiveComponentSourcesWithDiagnostics = async () => await this.componentCatalog.read();
|
|
4814
4879
|
listOperations = async () => await this.operationManager.list();
|
|
4815
4880
|
startOperation = async (input) => {
|
|
4816
4881
|
await this.ensureBuiltInPackages();
|
|
@@ -4822,6 +4887,7 @@ var AppPackageManager = class {
|
|
|
4822
4887
|
onProgress
|
|
4823
4888
|
});
|
|
4824
4889
|
if (await this.isBuiltInAppId(result.appId)) await this.registryService.setBuiltInSuppressed(result.appId, false);
|
|
4890
|
+
await this.componentCatalog.refresh();
|
|
4825
4891
|
return await this.getPackage(result.appId);
|
|
4826
4892
|
};
|
|
4827
4893
|
enable = async (appId) => {
|
|
@@ -4849,6 +4915,7 @@ var AppPackageManager = class {
|
|
|
4849
4915
|
});
|
|
4850
4916
|
throw error;
|
|
4851
4917
|
}
|
|
4918
|
+
await this.componentCatalog.refresh();
|
|
4852
4919
|
return await this.getPackage(appId);
|
|
4853
4920
|
});
|
|
4854
4921
|
};
|
|
@@ -4859,6 +4926,7 @@ var AppPackageManager = class {
|
|
|
4859
4926
|
await this.dependencyCoordinator.assertNoEnabledDependents(app);
|
|
4860
4927
|
await this.runtimeHooks.beforeDeactivate(this.toComponentSources(app));
|
|
4861
4928
|
await this.installationService.setEnabled(appId, false);
|
|
4929
|
+
await this.componentCatalog.refresh();
|
|
4862
4930
|
return await this.getPackage(appId);
|
|
4863
4931
|
});
|
|
4864
4932
|
};
|
|
@@ -4888,6 +4956,7 @@ var AppPackageManager = class {
|
|
|
4888
4956
|
}
|
|
4889
4957
|
activated = (await this.installationService.rollback(appId, result.version)).rolledBack;
|
|
4890
4958
|
if (current.enabled) await this.runtimeHooks.afterActivate(this.toComponentSources(candidate));
|
|
4959
|
+
await this.componentCatalog.refresh();
|
|
4891
4960
|
return {
|
|
4892
4961
|
package: await this.getPackage(appId),
|
|
4893
4962
|
result
|
|
@@ -4932,6 +5001,7 @@ var AppPackageManager = class {
|
|
|
4932
5001
|
}
|
|
4933
5002
|
result = await this.installationService.rollback(appId, version);
|
|
4934
5003
|
if (current.enabled) await this.runtimeHooks.afterActivate(this.toComponentSources(candidate));
|
|
5004
|
+
await this.componentCatalog.refresh();
|
|
4935
5005
|
return {
|
|
4936
5006
|
package: await this.getPackage(appId),
|
|
4937
5007
|
result
|
|
@@ -4957,7 +5027,9 @@ var AppPackageManager = class {
|
|
|
4957
5027
|
try {
|
|
4958
5028
|
const sources = this.toComponentSources(current);
|
|
4959
5029
|
rollbackRuntimeState = await this.runtimeHooks.beforeUninstall(sources) || void 0;
|
|
4960
|
-
|
|
5030
|
+
const result = await this.installationService.uninstall(appId, purgeData);
|
|
5031
|
+
await this.componentCatalog.refresh();
|
|
5032
|
+
return result;
|
|
4961
5033
|
} catch (error) {
|
|
4962
5034
|
const recoveryErrors = [];
|
|
4963
5035
|
if (rollbackRuntimeState) try {
|
|
@@ -10696,7 +10768,7 @@ var PanelAppPackageStateManager = class {
|
|
|
10696
10768
|
try {
|
|
10697
10769
|
return await this.params.sourceService.resolveSource(this.params.getPanelsPath(), id);
|
|
10698
10770
|
} catch (error) {
|
|
10699
|
-
if (!isPanelAppError(error) || error.code !== "PANEL_APP_NOT_FOUND") throw error;
|
|
10771
|
+
if (!isPanelAppError(error) || error.code !== "PANEL_APP_NOT_FOUND" && error.code !== "PANEL_APP_INVALID_ID") throw error;
|
|
10700
10772
|
}
|
|
10701
10773
|
const match = (await this.listSources()).find(({ source, packageSource }) => packageSource && (encodePanelAppId(source.sourceName) === id || packageSource.id === id));
|
|
10702
10774
|
if (!match) throw new PanelAppError("PANEL_APP_NOT_FOUND", "panel app not found");
|
|
@@ -10707,16 +10779,34 @@ var PanelAppPackageStateManager = class {
|
|
|
10707
10779
|
};
|
|
10708
10780
|
readContentSourceByIdOrAppId = async (id) => {
|
|
10709
10781
|
const panelsPath = this.params.getPanelsPath();
|
|
10710
|
-
|
|
10711
|
-
|
|
10712
|
-
|
|
10713
|
-
|
|
10714
|
-
|
|
10715
|
-
|
|
10716
|
-
|
|
10717
|
-
|
|
10718
|
-
|
|
10719
|
-
|
|
10782
|
+
const { source } = await this.resolveSourceByIdOrAppId(id);
|
|
10783
|
+
return await readPanelAppContentSourceByIdOrPath({
|
|
10784
|
+
createAssetBaseHref: this.params.createAssetBaseHref,
|
|
10785
|
+
id,
|
|
10786
|
+
panelsPath,
|
|
10787
|
+
sourcePath: source.sourcePath,
|
|
10788
|
+
sourceService: this.params.sourceService
|
|
10789
|
+
});
|
|
10790
|
+
};
|
|
10791
|
+
resolveSourceByIdOrAppId = async (id) => {
|
|
10792
|
+
const panelsPath = this.params.getPanelsPath();
|
|
10793
|
+
try {
|
|
10794
|
+
return await this.toResolvedTarget(await this.params.sourceService.resolveSource(panelsPath, id));
|
|
10795
|
+
} catch (error) {
|
|
10796
|
+
if (!isPanelAppError(error) || error.code !== "PANEL_APP_NOT_FOUND" && error.code !== "PANEL_APP_INVALID_ID") throw error;
|
|
10797
|
+
}
|
|
10798
|
+
const packageSources = (await this.listPackageComponentSources()).filter((component) => component.kind === "panel");
|
|
10799
|
+
const exactPackageSource = packageSources.find((component) => component.id === id);
|
|
10800
|
+
if (exactPackageSource) return await this.toResolvedTarget(await this.params.sourceService.resolveSourcePath(exactPackageSource.sourcePath), exactPackageSource);
|
|
10801
|
+
const workspaceSources = await this.params.sourceService.listSources(panelsPath);
|
|
10802
|
+
for (const source of workspaceSources) {
|
|
10803
|
+
const target = await this.toResolvedTarget(source);
|
|
10804
|
+
if (resolvePanelAppAppId(source, target.manifest) === id) return target;
|
|
10805
|
+
}
|
|
10806
|
+
for (const packageSource of packageSources) {
|
|
10807
|
+
const source = await this.params.sourceService.resolveSourcePath(packageSource.sourcePath);
|
|
10808
|
+
const target = await this.toResolvedTarget(source, packageSource);
|
|
10809
|
+
if (encodePanelAppId(source.sourceName) === id || resolvePanelAppAppId(source, target.manifest) === id) return target;
|
|
10720
10810
|
}
|
|
10721
10811
|
throw new PanelAppError("PANEL_APP_NOT_FOUND", "panel app not found");
|
|
10722
10812
|
};
|
|
@@ -10795,6 +10885,11 @@ var PanelAppPackageStateManager = class {
|
|
|
10795
10885
|
return errors;
|
|
10796
10886
|
};
|
|
10797
10887
|
listPackageComponentSources = async () => await this.params.listPackageComponentSources?.() ?? [];
|
|
10888
|
+
toResolvedTarget = async (source, packageSource) => ({
|
|
10889
|
+
source,
|
|
10890
|
+
packageSource,
|
|
10891
|
+
manifest: source.manifest ?? parsePanelAppManifest(await readFile(source.entryPath, "utf8"))
|
|
10892
|
+
});
|
|
10798
10893
|
};
|
|
10799
10894
|
//#endregion
|
|
10800
10895
|
//#region src/utils/panel-app-time.utils.ts
|
|
@@ -11964,6 +12059,12 @@ var PanelAppManager = class {
|
|
|
11964
12059
|
unavailablePackages: await this.params.listPackageComponentDiagnostics?.() ?? []
|
|
11965
12060
|
};
|
|
11966
12061
|
};
|
|
12062
|
+
getPanelApp = async (id) => {
|
|
12063
|
+
const panelsPath = this.getPanelsPath(this.getWorkspacePath());
|
|
12064
|
+
const resolved = await this.packageStateManager.resolveSourceByIdOrAppId(id);
|
|
12065
|
+
const appState = await this.createStateStore(panelsPath).load();
|
|
12066
|
+
return await this.entryPresenter.build(resolved.source, appState.apps[encodePanelAppId(resolved.source.sourceName)] ?? {}, resolved.packageSource, appState.mainSidebarAppIds);
|
|
12067
|
+
};
|
|
11967
12068
|
getPanelAppContent = async (id, sourcePath) => {
|
|
11968
12069
|
try {
|
|
11969
12070
|
const resolved = sourcePath ? await readPanelAppContentSourceByIdOrPath({
|
|
@@ -12101,19 +12202,19 @@ var PanelAppManager = class {
|
|
|
12101
12202
|
return await this.agentBridgeService.grantAgentCapability(bridgeSession, capability);
|
|
12102
12203
|
};
|
|
12103
12204
|
updatePanelAppPreferences = async (id, preferences) => {
|
|
12104
|
-
const fileName = await this.resolvePanelAppFileName(id);
|
|
12105
12205
|
const panelsPath = this.getPanelsPath(this.getWorkspacePath());
|
|
12106
|
-
const
|
|
12107
|
-
const
|
|
12108
|
-
const appId = resolvePanelAppAppId(source,
|
|
12206
|
+
const resolved = await this.packageStateManager.resolveSourceByIdOrAppId(id);
|
|
12207
|
+
const fileName = resolved.source.sourceName;
|
|
12208
|
+
const appId = resolvePanelAppAppId(resolved.source, resolved.manifest);
|
|
12109
12209
|
const result = await this.createStateStore(panelsPath).updatePreferences(encodePanelAppId(fileName), appId, preferences);
|
|
12110
|
-
return await this.entryPresenter.build(source, result.entry, packageSource, result.mainSidebarAppIds);
|
|
12210
|
+
return await this.entryPresenter.build(resolved.source, result.entry, resolved.packageSource, result.mainSidebarAppIds);
|
|
12111
12211
|
};
|
|
12112
12212
|
recordPanelAppOpened = async (id) => {
|
|
12113
|
-
const fileName = await this.resolvePanelAppFileName(id);
|
|
12114
12213
|
const panelsPath = this.getPanelsPath(this.getWorkspacePath());
|
|
12214
|
+
const resolved = await this.packageStateManager.resolveSourceByIdOrAppId(id);
|
|
12215
|
+
const fileName = resolved.source.sourceName;
|
|
12115
12216
|
const result = await this.createStateStore(panelsPath).recordOpened(encodePanelAppId(fileName));
|
|
12116
|
-
return await this.entryPresenter.build(
|
|
12217
|
+
return await this.entryPresenter.build(resolved.source, result.entry, resolved.packageSource, result.mainSidebarAppIds);
|
|
12117
12218
|
};
|
|
12118
12219
|
deletePanelApp = async (id) => {
|
|
12119
12220
|
const panelsPath = this.getPanelsPath(this.getWorkspacePath());
|
|
@@ -12146,9 +12247,6 @@ var PanelAppManager = class {
|
|
|
12146
12247
|
return `${PANEL_APP_TOKENIZED_ASSET_BASE_PATH}/${encodeURIComponent(token)}/`;
|
|
12147
12248
|
};
|
|
12148
12249
|
createStateStore = (panelsPath) => new PanelAppStateStore(panelsPath);
|
|
12149
|
-
resolvePanelAppFileName = async (id) => {
|
|
12150
|
-
return (await this.packageStateManager.resolveSource(id)).sourceName;
|
|
12151
|
-
};
|
|
12152
12250
|
assertCanActivatePackageComponents = async (components) => await this.packageStateManager.assertCanActivate(components);
|
|
12153
12251
|
deactivatePackageComponents = (components) => this.packageStateManager.deactivate(components);
|
|
12154
12252
|
preparePackageComponentDeactivation = (components) => {
|
|
@@ -20329,7 +20427,7 @@ var ObservationManager = class {
|
|
|
20329
20427
|
};
|
|
20330
20428
|
//#endregion
|
|
20331
20429
|
//#region src/features/projects/stores/project.store.ts
|
|
20332
|
-
const PROJECT_STORE_VERSION =
|
|
20430
|
+
const PROJECT_STORE_VERSION = 3;
|
|
20333
20431
|
function createProjectId() {
|
|
20334
20432
|
return randomBytes(9).toString("base64url");
|
|
20335
20433
|
}
|
|
@@ -20368,17 +20466,18 @@ var ProjectStore = class {
|
|
|
20368
20466
|
throw error;
|
|
20369
20467
|
}
|
|
20370
20468
|
if (storeFile.version === PROJECT_STORE_VERSION) return false;
|
|
20371
|
-
await this.save(storeFile.projects.map((project) => ({
|
|
20469
|
+
await this.save(storeFile.version === 1 ? storeFile.projects.map((project) => ({
|
|
20372
20470
|
id: createProjectId(),
|
|
20373
20471
|
...project
|
|
20374
|
-
})));
|
|
20472
|
+
})) : storeFile.projects);
|
|
20375
20473
|
return true;
|
|
20376
20474
|
};
|
|
20377
|
-
save = async (projects) => {
|
|
20475
|
+
save = async (projects, removedProjects = []) => {
|
|
20378
20476
|
const tempPath = `${this.storePath}.${randomUUID()}.tmp`;
|
|
20379
20477
|
const storeFile = {
|
|
20380
20478
|
version: PROJECT_STORE_VERSION,
|
|
20381
|
-
projects
|
|
20479
|
+
projects,
|
|
20480
|
+
removedProjects
|
|
20382
20481
|
};
|
|
20383
20482
|
await mkdir(dirname(this.storePath), { recursive: true });
|
|
20384
20483
|
try {
|
|
@@ -20389,19 +20488,57 @@ var ProjectStore = class {
|
|
|
20389
20488
|
throw error;
|
|
20390
20489
|
}
|
|
20391
20490
|
};
|
|
20491
|
+
remove = async (projectId) => {
|
|
20492
|
+
const storeFile = await this.readStoreFile();
|
|
20493
|
+
const project = storeFile.projects.find((entry) => entry.id === projectId);
|
|
20494
|
+
if (!project) return null;
|
|
20495
|
+
await this.save(storeFile.projects.filter((entry) => entry.id !== projectId), [...storeFile.removedProjects.filter((entry) => entry.id !== projectId), project]);
|
|
20496
|
+
return structuredClone(project);
|
|
20497
|
+
};
|
|
20498
|
+
restoreByRootPath = async (rootPath, updatedAt) => {
|
|
20499
|
+
const storeFile = await this.readStoreFile();
|
|
20500
|
+
const project = storeFile.removedProjects.find((entry) => entry.rootPath === rootPath);
|
|
20501
|
+
if (!project) return null;
|
|
20502
|
+
const restored = {
|
|
20503
|
+
...project,
|
|
20504
|
+
updatedAt
|
|
20505
|
+
};
|
|
20506
|
+
await this.save([...storeFile.projects, restored], storeFile.removedProjects.filter((entry) => entry.id !== project.id));
|
|
20507
|
+
return structuredClone(restored);
|
|
20508
|
+
};
|
|
20509
|
+
isRemovedRootPath = async (rootPath) => (await this.readStoreFile()).removedProjects.some((entry) => entry.rootPath === rootPath);
|
|
20510
|
+
readStoreFile = async () => {
|
|
20511
|
+
try {
|
|
20512
|
+
return this.parseStoreFile(await readFile(this.storePath, "utf8"));
|
|
20513
|
+
} catch (error) {
|
|
20514
|
+
if (this.isMissingFileError(error)) return {
|
|
20515
|
+
version: PROJECT_STORE_VERSION,
|
|
20516
|
+
projects: [],
|
|
20517
|
+
removedProjects: []
|
|
20518
|
+
};
|
|
20519
|
+
if (error instanceof SyntaxError) throw new ProjectStoreError("project registry contains invalid JSON");
|
|
20520
|
+
throw error;
|
|
20521
|
+
}
|
|
20522
|
+
};
|
|
20392
20523
|
parseStoreFile = (source) => {
|
|
20393
20524
|
const value = this.parseStoredFile(source);
|
|
20394
|
-
if (value.version !== PROJECT_STORE_VERSION || !value.projects.every(this.isProjectRecord)) throw new ProjectStoreError("project registry has an unsupported structure");
|
|
20525
|
+
if (value.version !== PROJECT_STORE_VERSION || !value.projects.every(this.isProjectRecord) || !value.removedProjects.every(this.isProjectRecord)) throw new ProjectStoreError("project registry has an unsupported structure");
|
|
20395
20526
|
return {
|
|
20396
20527
|
version: PROJECT_STORE_VERSION,
|
|
20397
|
-
projects: value.projects.map((project) => structuredClone(project))
|
|
20528
|
+
projects: value.projects.map((project) => structuredClone(project)),
|
|
20529
|
+
removedProjects: value.removedProjects.map((project) => structuredClone(project))
|
|
20398
20530
|
};
|
|
20399
20531
|
};
|
|
20400
20532
|
parseStoredFile = (source) => {
|
|
20401
20533
|
const value = JSON.parse(source);
|
|
20402
20534
|
if (!this.isRecord(value) || !Array.isArray(value.projects)) throw new ProjectStoreError("project registry has an unsupported structure");
|
|
20403
|
-
if (value.version === PROJECT_STORE_VERSION && value.projects.every(this.isProjectRecord)) return {
|
|
20535
|
+
if (value.version === PROJECT_STORE_VERSION && Array.isArray(value.removedProjects) && value.projects.every(this.isProjectRecord) && value.removedProjects.every(this.isProjectRecord)) return {
|
|
20404
20536
|
version: PROJECT_STORE_VERSION,
|
|
20537
|
+
projects: value.projects.map((project) => structuredClone(project)),
|
|
20538
|
+
removedProjects: value.removedProjects.map((project) => structuredClone(project))
|
|
20539
|
+
};
|
|
20540
|
+
if (value.version === 2 && value.projects.every(this.isProjectRecord)) return {
|
|
20541
|
+
version: 2,
|
|
20405
20542
|
projects: value.projects.map((project) => structuredClone(project))
|
|
20406
20543
|
};
|
|
20407
20544
|
if (value.version === 1 && value.projects.every(this.isLegacyProjectRecord)) return {
|
|
@@ -20487,7 +20624,15 @@ var ProjectManager = class {
|
|
|
20487
20624
|
name,
|
|
20488
20625
|
rootPath,
|
|
20489
20626
|
template
|
|
20490
|
-
});
|
|
20627
|
+
}, { restoreRemoved: true });
|
|
20628
|
+
};
|
|
20629
|
+
addExistingProject = async (rootPath, name) => {
|
|
20630
|
+
const canonicalPath = await this.resolveExistingProjectRoot(rootPath);
|
|
20631
|
+
if (!canonicalPath) return null;
|
|
20632
|
+
return await this.upsertProject({
|
|
20633
|
+
name: name === void 0 ? basename(canonicalPath) : this.normalizeName(name),
|
|
20634
|
+
rootPath: canonicalPath
|
|
20635
|
+
}, { restoreRemoved: true });
|
|
20491
20636
|
};
|
|
20492
20637
|
registerExistingProject = async (rootPath, name) => {
|
|
20493
20638
|
const canonicalPath = await this.resolveExistingProjectRoot(rootPath);
|
|
@@ -20495,7 +20640,13 @@ var ProjectManager = class {
|
|
|
20495
20640
|
return await this.upsertProject({
|
|
20496
20641
|
name: name === void 0 ? basename(canonicalPath) : this.normalizeName(name),
|
|
20497
20642
|
rootPath: canonicalPath
|
|
20498
|
-
});
|
|
20643
|
+
}, { restoreRemoved: true });
|
|
20644
|
+
};
|
|
20645
|
+
removeProject = async (projectId, confirmProjectId) => {
|
|
20646
|
+
if (confirmProjectId !== projectId) throw new ProjectError("PROJECT_REMOVE_CONFIRMATION_MISMATCH", "project removal confirmation must exactly match the project id");
|
|
20647
|
+
const removed = await this.store.remove(projectId);
|
|
20648
|
+
if (!removed) throw new ProjectError("PROJECT_NOT_FOUND", "project was not found");
|
|
20649
|
+
return removed;
|
|
20499
20650
|
};
|
|
20500
20651
|
normalizeSessionProjectRoot = async (value) => {
|
|
20501
20652
|
return (await this.normalizeSessionProjectContext(value))?.rootPath ?? null;
|
|
@@ -20529,18 +20680,30 @@ var ProjectManager = class {
|
|
|
20529
20680
|
for (const projectRoot of projectRoots) {
|
|
20530
20681
|
if (projectRoot == null || typeof projectRoot === "string" && !projectRoot.trim()) continue;
|
|
20531
20682
|
try {
|
|
20532
|
-
await this.
|
|
20683
|
+
const canonicalPath = await this.resolveExistingProjectRoot(projectRoot);
|
|
20684
|
+
if (!canonicalPath || await this.store.isRemovedRootPath(canonicalPath)) continue;
|
|
20685
|
+
await this.upsertProject({
|
|
20686
|
+
name: basename(canonicalPath),
|
|
20687
|
+
rootPath: canonicalPath
|
|
20688
|
+
});
|
|
20533
20689
|
} catch (error) {
|
|
20534
20690
|
const message = error instanceof Error ? error.message : String(error);
|
|
20535
20691
|
console.warn(`[project-manager] skipped historical project root: ${message}`);
|
|
20536
20692
|
}
|
|
20537
20693
|
}
|
|
20538
20694
|
};
|
|
20539
|
-
upsertProject = async (input) => {
|
|
20695
|
+
upsertProject = async (input, options = {}) => {
|
|
20540
20696
|
const projects = await this.store.list();
|
|
20541
20697
|
const existing = projects.find((project) => project.rootPath === input.rootPath);
|
|
20542
20698
|
if (existing) return existing;
|
|
20543
20699
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
20700
|
+
if (options.restoreRemoved) {
|
|
20701
|
+
const restored = await this.store.restoreByRootPath(input.rootPath, now);
|
|
20702
|
+
if (restored) {
|
|
20703
|
+
await this.options.onProjectRegistered?.(structuredClone(restored));
|
|
20704
|
+
return restored;
|
|
20705
|
+
}
|
|
20706
|
+
}
|
|
20544
20707
|
const project = {
|
|
20545
20708
|
id: createProjectId(),
|
|
20546
20709
|
name: input.name,
|
|
@@ -20854,6 +21017,95 @@ function toArtifactLink(row) {
|
|
|
20854
21017
|
};
|
|
20855
21018
|
}
|
|
20856
21019
|
//#endregion
|
|
21020
|
+
//#region src/features/projects/stores/project-work-query.store.ts
|
|
21021
|
+
var ProjectWorkQueryStore = class {
|
|
21022
|
+
constructor(db, toItem) {
|
|
21023
|
+
this.db = db;
|
|
21024
|
+
this.toItem = toItem;
|
|
21025
|
+
}
|
|
21026
|
+
listItems = (params) => {
|
|
21027
|
+
const { cursor, includeDeleted, limit, projectId, stateId } = params;
|
|
21028
|
+
const conditions = ["items.project_id = ?"];
|
|
21029
|
+
const values = [projectId];
|
|
21030
|
+
if (!includeDeleted) conditions.push("items.deleted_at IS NULL");
|
|
21031
|
+
if (stateId) {
|
|
21032
|
+
conditions.push("items.state_id = ?");
|
|
21033
|
+
values.push(stateId);
|
|
21034
|
+
}
|
|
21035
|
+
const total = Number(this.db().prepare(`SELECT COUNT(*) AS count FROM project_work_items AS items
|
|
21036
|
+
WHERE ${conditions.join(" AND ")}`).get(...values).count);
|
|
21037
|
+
if (cursor) {
|
|
21038
|
+
conditions.push("(items.updated_at < ? OR (items.updated_at = ? AND items.id < ?))");
|
|
21039
|
+
values.push(cursor.updatedAt, cursor.updatedAt, cursor.id);
|
|
21040
|
+
}
|
|
21041
|
+
const rows = this.db().prepare(`SELECT items.*,
|
|
21042
|
+
(SELECT COUNT(*) FROM project_work_artifact_links AS links
|
|
21043
|
+
WHERE links.project_id = items.project_id AND links.work_item_id = items.id) AS artifact_count
|
|
21044
|
+
FROM project_work_items AS items
|
|
21045
|
+
WHERE ${conditions.join(" AND ")}
|
|
21046
|
+
ORDER BY items.updated_at DESC, items.id DESC
|
|
21047
|
+
LIMIT ?`).all(...values, limit + 1);
|
|
21048
|
+
return {
|
|
21049
|
+
items: rows.slice(0, limit).map((row) => ({
|
|
21050
|
+
...this.toItem(row),
|
|
21051
|
+
artifactCount: Number(row.artifact_count)
|
|
21052
|
+
})),
|
|
21053
|
+
hasMore: rows.length > limit,
|
|
21054
|
+
total
|
|
21055
|
+
};
|
|
21056
|
+
};
|
|
21057
|
+
summarizeItems = (projectId) => {
|
|
21058
|
+
const row = this.db().prepare(`SELECT
|
|
21059
|
+
COUNT(*) AS total,
|
|
21060
|
+
SUM(CASE WHEN states.category NOT IN ('completed', 'canceled') THEN 1 ELSE 0 END) AS active,
|
|
21061
|
+
SUM(CASE WHEN states.category = 'completed' THEN 1 ELSE 0 END) AS completed,
|
|
21062
|
+
SUM(CASE WHEN items.attention != 'none' THEN 1 ELSE 0 END) AS attention,
|
|
21063
|
+
MAX(items.updated_at) AS updated_at
|
|
21064
|
+
FROM project_work_items AS items
|
|
21065
|
+
JOIN project_work_states AS states
|
|
21066
|
+
ON states.project_id = items.project_id AND states.id = items.state_id
|
|
21067
|
+
WHERE items.project_id = ? AND items.deleted_at IS NULL`).get(projectId);
|
|
21068
|
+
return {
|
|
21069
|
+
total: Number(row.total),
|
|
21070
|
+
active: Number(row.active ?? 0),
|
|
21071
|
+
completed: Number(row.completed ?? 0),
|
|
21072
|
+
attention: Number(row.attention ?? 0),
|
|
21073
|
+
updatedAt: row.updated_at
|
|
21074
|
+
};
|
|
21075
|
+
};
|
|
21076
|
+
listRecentArtifacts = (params) => {
|
|
21077
|
+
const { cursor, limit, projectId } = params;
|
|
21078
|
+
const total = Number(this.db().prepare("SELECT COUNT(DISTINCT path) AS count FROM project_work_artifact_links WHERE project_id = ?").get(projectId).count);
|
|
21079
|
+
const cursorCondition = cursor ? "AND (created_at < ? OR (created_at = ? AND id < ?))" : "";
|
|
21080
|
+
const rows = this.db().prepare(`WITH ranked AS (
|
|
21081
|
+
SELECT links.id, links.path, links.label, links.work_item_id,
|
|
21082
|
+
items.title AS work_item_title, links.created_at,
|
|
21083
|
+
ROW_NUMBER() OVER (
|
|
21084
|
+
PARTITION BY links.path
|
|
21085
|
+
ORDER BY links.created_at DESC, links.id DESC
|
|
21086
|
+
) AS path_rank
|
|
21087
|
+
FROM project_work_artifact_links AS links
|
|
21088
|
+
JOIN project_work_items AS items
|
|
21089
|
+
ON items.project_id = links.project_id AND items.id = links.work_item_id
|
|
21090
|
+
WHERE links.project_id = ?
|
|
21091
|
+
)
|
|
21092
|
+
SELECT id, path, label, work_item_id, work_item_title, created_at
|
|
21093
|
+
FROM ranked
|
|
21094
|
+
WHERE path_rank = 1 ${cursorCondition}
|
|
21095
|
+
ORDER BY created_at DESC, id DESC
|
|
21096
|
+
LIMIT ?`).all(projectId, ...cursor ? [
|
|
21097
|
+
cursor.createdAt,
|
|
21098
|
+
cursor.createdAt,
|
|
21099
|
+
cursor.id
|
|
21100
|
+
] : [], limit + 1);
|
|
21101
|
+
return {
|
|
21102
|
+
artifacts: rows.slice(0, limit),
|
|
21103
|
+
hasMore: rows.length > limit,
|
|
21104
|
+
total
|
|
21105
|
+
};
|
|
21106
|
+
};
|
|
21107
|
+
};
|
|
21108
|
+
//#endregion
|
|
20857
21109
|
//#region src/features/projects/stores/project-work-state.store.ts
|
|
20858
21110
|
const DEFAULT_PROJECT_WORK_STATES = [
|
|
20859
21111
|
{
|
|
@@ -21007,6 +21259,7 @@ var ProjectWorkStore = class {
|
|
|
21007
21259
|
database = null;
|
|
21008
21260
|
readyPromise = null;
|
|
21009
21261
|
activities = new ProjectWorkActivityStore(() => this.db());
|
|
21262
|
+
queries = new ProjectWorkQueryStore(() => this.db(), toItem);
|
|
21010
21263
|
states = new ProjectWorkStateStore(() => this.db(), this.activities.insertActivity);
|
|
21011
21264
|
constructor(databasePath) {
|
|
21012
21265
|
this.databasePath = databasePath;
|
|
@@ -21041,12 +21294,6 @@ var ProjectWorkStore = class {
|
|
|
21041
21294
|
await this.ensureReady();
|
|
21042
21295
|
return await this.states.deleteState(projectId, stateId, migrateToStateId, actor);
|
|
21043
21296
|
};
|
|
21044
|
-
listItems = async (projectId, includeDeleted = false) => {
|
|
21045
|
-
await this.ensureReady();
|
|
21046
|
-
return this.db().prepare(`SELECT * FROM project_work_items
|
|
21047
|
-
WHERE project_id = ? ${includeDeleted ? "" : "AND deleted_at IS NULL"}
|
|
21048
|
-
ORDER BY updated_at DESC, created_at DESC`).all(projectId).map(toItem);
|
|
21049
|
-
};
|
|
21050
21297
|
getItem = async (projectId, workItemId) => {
|
|
21051
21298
|
await this.ensureReady();
|
|
21052
21299
|
const row = this.db().prepare("SELECT * FROM project_work_items WHERE project_id = ? AND id = ? LIMIT 1").get(projectId, workItemId);
|
|
@@ -21193,8 +21440,12 @@ var ProjectWorkStore = class {
|
|
|
21193
21440
|
);
|
|
21194
21441
|
CREATE INDEX IF NOT EXISTS project_work_items_list_idx
|
|
21195
21442
|
ON project_work_items(project_id, deleted_at, updated_at);
|
|
21443
|
+
CREATE INDEX IF NOT EXISTS project_work_items_state_list_idx
|
|
21444
|
+
ON project_work_items(project_id, state_id, deleted_at, updated_at, id);
|
|
21196
21445
|
CREATE INDEX IF NOT EXISTS project_work_activity_timeline_idx
|
|
21197
21446
|
ON project_work_activities(project_id, work_item_id, created_at);
|
|
21447
|
+
CREATE INDEX IF NOT EXISTS project_work_artifacts_recent_idx
|
|
21448
|
+
ON project_work_artifact_links(project_id, created_at, id);
|
|
21198
21449
|
`);
|
|
21199
21450
|
};
|
|
21200
21451
|
db = () => {
|
|
@@ -21242,6 +21493,112 @@ function isProjectWorkError(error) {
|
|
|
21242
21493
|
return error instanceof ProjectWorkError;
|
|
21243
21494
|
}
|
|
21244
21495
|
//#endregion
|
|
21496
|
+
//#region src/features/projects/services/project-work-query.service.ts
|
|
21497
|
+
var ProjectWorkQueryService = class {
|
|
21498
|
+
constructor(store, projectManager) {
|
|
21499
|
+
this.store = store;
|
|
21500
|
+
this.projectManager = projectManager;
|
|
21501
|
+
}
|
|
21502
|
+
list = async (projectId, input = {}) => {
|
|
21503
|
+
await this.ensureProject(projectId);
|
|
21504
|
+
const limit = this.requirePageLimit(input.limit);
|
|
21505
|
+
const cursor = input.cursor ? this.decodeCursor(input.cursor, "work item", "updatedAt") : void 0;
|
|
21506
|
+
const states = await this.store.listStates(projectId);
|
|
21507
|
+
if (input.stateId && !states.some((state) => state.id === input.stateId)) throw new ProjectWorkError("PROJECT_WORK_STATE_NOT_FOUND", "work item state was not found");
|
|
21508
|
+
const page = this.store.queries.listItems({
|
|
21509
|
+
projectId,
|
|
21510
|
+
includeDeleted: input.includeDeleted ?? false,
|
|
21511
|
+
limit,
|
|
21512
|
+
...input.stateId ? { stateId: input.stateId } : {},
|
|
21513
|
+
...cursor ? { cursor } : {}
|
|
21514
|
+
});
|
|
21515
|
+
const stateById = new Map(states.map((state) => [state.id, state]));
|
|
21516
|
+
const items = page.items.map((item) => ({
|
|
21517
|
+
...item,
|
|
21518
|
+
state: this.requireMappedState(stateById, item.stateId)
|
|
21519
|
+
}));
|
|
21520
|
+
const last = items.at(-1);
|
|
21521
|
+
return {
|
|
21522
|
+
items,
|
|
21523
|
+
total: page.total,
|
|
21524
|
+
nextCursor: page.hasMore && last ? this.encodeCursor({
|
|
21525
|
+
updatedAt: last.updatedAt,
|
|
21526
|
+
id: last.id
|
|
21527
|
+
}) : null
|
|
21528
|
+
};
|
|
21529
|
+
};
|
|
21530
|
+
summary = async (projectId) => {
|
|
21531
|
+
await this.ensureProject(projectId);
|
|
21532
|
+
return this.store.queries.summarizeItems(projectId);
|
|
21533
|
+
};
|
|
21534
|
+
listRecentArtifacts = async (projectId, input = {}) => {
|
|
21535
|
+
const project = await this.requireProject(projectId);
|
|
21536
|
+
await this.store.ensureProject(projectId);
|
|
21537
|
+
const limit = this.requirePageLimit(input.limit);
|
|
21538
|
+
const cursor = input.cursor ? this.decodeCursor(input.cursor, "project artifact", "createdAt") : void 0;
|
|
21539
|
+
const page = this.store.queries.listRecentArtifacts({
|
|
21540
|
+
projectId,
|
|
21541
|
+
limit,
|
|
21542
|
+
...cursor ? { cursor } : {}
|
|
21543
|
+
});
|
|
21544
|
+
const artifacts = await Promise.all(page.artifacts.map(async (artifact) => ({
|
|
21545
|
+
id: artifact.id,
|
|
21546
|
+
path: artifact.path,
|
|
21547
|
+
label: artifact.label,
|
|
21548
|
+
workItemId: artifact.work_item_id,
|
|
21549
|
+
workItemTitle: artifact.work_item_title,
|
|
21550
|
+
createdAt: artifact.created_at,
|
|
21551
|
+
exists: await this.isArtifactAvailable(project.rootPath, artifact.path)
|
|
21552
|
+
})));
|
|
21553
|
+
const last = artifacts.at(-1);
|
|
21554
|
+
return {
|
|
21555
|
+
artifacts,
|
|
21556
|
+
total: page.total,
|
|
21557
|
+
nextCursor: page.hasMore && last ? this.encodeCursor({
|
|
21558
|
+
createdAt: last.createdAt,
|
|
21559
|
+
id: last.id
|
|
21560
|
+
}) : null
|
|
21561
|
+
};
|
|
21562
|
+
};
|
|
21563
|
+
ensureProject = async (projectId) => {
|
|
21564
|
+
await this.requireProject(projectId);
|
|
21565
|
+
await this.store.ensureProject(projectId);
|
|
21566
|
+
};
|
|
21567
|
+
requireProject = async (projectId) => {
|
|
21568
|
+
const normalized = projectId.trim();
|
|
21569
|
+
const project = normalized ? await this.projectManager.getProjectById(normalized) : null;
|
|
21570
|
+
if (!project) throw new ProjectWorkError("PROJECT_NOT_FOUND", "project was not found");
|
|
21571
|
+
return project;
|
|
21572
|
+
};
|
|
21573
|
+
requireMappedState = (states, stateId) => {
|
|
21574
|
+
const state = states.get(stateId);
|
|
21575
|
+
if (!state) throw new ProjectWorkError("PROJECT_WORK_STATE_NOT_FOUND", "work item state was not found");
|
|
21576
|
+
return state;
|
|
21577
|
+
};
|
|
21578
|
+
requirePageLimit = (value) => {
|
|
21579
|
+
const limit = value ?? 20;
|
|
21580
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 100) throw new ProjectWorkError("PROJECT_WORK_VALIDATION_FAILED", "page limit must be an integer between 1 and 100");
|
|
21581
|
+
return limit;
|
|
21582
|
+
};
|
|
21583
|
+
encodeCursor = (value) => Buffer.from(JSON.stringify(value), "utf8").toString("base64url");
|
|
21584
|
+
decodeCursor = (value, label, timestampKey) => {
|
|
21585
|
+
try {
|
|
21586
|
+
const decoded = JSON.parse(Buffer.from(value, "base64url").toString("utf8"));
|
|
21587
|
+
if (!decoded || typeof decoded !== "object" || typeof decoded.id !== "string" || typeof decoded[timestampKey] !== "string" || Object.keys(decoded).some((key) => key !== "id" && key !== timestampKey) || Object.values(decoded).some((entry) => typeof entry !== "string" || !entry)) throw new Error("invalid cursor shape");
|
|
21588
|
+
return decoded;
|
|
21589
|
+
} catch {
|
|
21590
|
+
throw new ProjectWorkError("PROJECT_WORK_VALIDATION_FAILED", `${label} cursor is invalid`);
|
|
21591
|
+
}
|
|
21592
|
+
};
|
|
21593
|
+
isArtifactAvailable = async (projectRoot, projectRelativePath) => {
|
|
21594
|
+
try {
|
|
21595
|
+
return (await stat(resolve(projectRoot, projectRelativePath))).isFile();
|
|
21596
|
+
} catch {
|
|
21597
|
+
return false;
|
|
21598
|
+
}
|
|
21599
|
+
};
|
|
21600
|
+
};
|
|
21601
|
+
//#endregion
|
|
21245
21602
|
//#region src/features/projects/types/project-work.types.ts
|
|
21246
21603
|
const PROJECT_WORK_STATE_CATEGORIES = [
|
|
21247
21604
|
"backlog",
|
|
@@ -21259,9 +21616,11 @@ const PROJECT_WORK_ATTENTION_VALUES = [
|
|
|
21259
21616
|
//#region src/features/projects/managers/project-work.manager.ts
|
|
21260
21617
|
var ProjectWorkManager = class {
|
|
21261
21618
|
store;
|
|
21619
|
+
queries;
|
|
21262
21620
|
constructor(options) {
|
|
21263
21621
|
this.options = options;
|
|
21264
21622
|
this.store = new ProjectWorkStore(options.databasePath);
|
|
21623
|
+
this.queries = new ProjectWorkQueryService(this.store, options.projectManager);
|
|
21265
21624
|
}
|
|
21266
21625
|
initialize = async () => {
|
|
21267
21626
|
await this.store.initialize();
|
|
@@ -21272,31 +21631,9 @@ var ProjectWorkManager = class {
|
|
|
21272
21631
|
await this.requireProject(projectId);
|
|
21273
21632
|
await this.store.ensureProject(projectId);
|
|
21274
21633
|
};
|
|
21275
|
-
list = async (projectId,
|
|
21276
|
-
|
|
21277
|
-
|
|
21278
|
-
const stateById = new Map(states.map((state) => [state.id, state]));
|
|
21279
|
-
const detailed = await Promise.all(items.map(async (item) => ({
|
|
21280
|
-
...item,
|
|
21281
|
-
state: this.requireMappedState(stateById, item.stateId),
|
|
21282
|
-
artifacts: await this.store.listArtifacts(projectId, item.id)
|
|
21283
|
-
})));
|
|
21284
|
-
return {
|
|
21285
|
-
items: detailed,
|
|
21286
|
-
states,
|
|
21287
|
-
total: detailed.length
|
|
21288
|
-
};
|
|
21289
|
-
};
|
|
21290
|
-
summary = async (projectId) => {
|
|
21291
|
-
const { items } = await this.list(projectId);
|
|
21292
|
-
return {
|
|
21293
|
-
total: items.length,
|
|
21294
|
-
active: items.filter((item) => item.state.category !== "completed" && item.state.category !== "canceled").length,
|
|
21295
|
-
completed: items.filter((item) => item.state.category === "completed").length,
|
|
21296
|
-
attention: items.filter((item) => item.attention !== "none").length,
|
|
21297
|
-
updatedAt: items[0]?.updatedAt ?? null
|
|
21298
|
-
};
|
|
21299
|
-
};
|
|
21634
|
+
list = async (projectId, input = {}) => await this.queries.list(projectId, input);
|
|
21635
|
+
summary = async (projectId) => await this.queries.summary(projectId);
|
|
21636
|
+
listRecentArtifacts = async (projectId, input = {}) => await this.queries.listRecentArtifacts(projectId, input);
|
|
21300
21637
|
get = async (projectId, workItemId) => {
|
|
21301
21638
|
await this.ensureProject(projectId);
|
|
21302
21639
|
const item = await this.store.getItem(projectId, workItemId);
|
|
@@ -21465,11 +21802,6 @@ var ProjectWorkManager = class {
|
|
|
21465
21802
|
if (!project) throw new ProjectWorkError("PROJECT_NOT_FOUND", "project was not found");
|
|
21466
21803
|
return project;
|
|
21467
21804
|
};
|
|
21468
|
-
requireMappedState = (states, stateId) => {
|
|
21469
|
-
const state = states.get(stateId);
|
|
21470
|
-
if (!state) throw new ProjectWorkError("PROJECT_WORK_STATE_NOT_FOUND", "work item state was not found");
|
|
21471
|
-
return state;
|
|
21472
|
-
};
|
|
21473
21805
|
requireName = (value, label) => {
|
|
21474
21806
|
const normalized = typeof value === "string" ? value.trim() : "";
|
|
21475
21807
|
if (!normalized) throw new ProjectWorkError("PROJECT_WORK_VALIDATION_FAILED", `${label} is required`);
|
|
@@ -26702,13 +27034,29 @@ function agentActor(context) {
|
|
|
26702
27034
|
}
|
|
26703
27035
|
var ProjectWorkListTool = class {
|
|
26704
27036
|
name = "project_work_list";
|
|
26705
|
-
description = "List persistent work items
|
|
27037
|
+
description = "List one bounded page of persistent work items for the current project.";
|
|
26706
27038
|
parameters = {
|
|
26707
27039
|
type: "object",
|
|
26708
|
-
properties: {
|
|
26709
|
-
|
|
26710
|
-
|
|
26711
|
-
|
|
27040
|
+
properties: {
|
|
27041
|
+
include_deleted: {
|
|
27042
|
+
type: "boolean",
|
|
27043
|
+
description: "Include deleted work items."
|
|
27044
|
+
},
|
|
27045
|
+
state_id: {
|
|
27046
|
+
type: "string",
|
|
27047
|
+
description: "Only return work items in this custom state."
|
|
27048
|
+
},
|
|
27049
|
+
cursor: {
|
|
27050
|
+
type: "string",
|
|
27051
|
+
description: "Opaque next_cursor from an earlier list response."
|
|
27052
|
+
},
|
|
27053
|
+
limit: {
|
|
27054
|
+
type: "integer",
|
|
27055
|
+
minimum: 1,
|
|
27056
|
+
maximum: 100,
|
|
27057
|
+
description: "Page size. Defaults to 20."
|
|
27058
|
+
}
|
|
27059
|
+
},
|
|
26712
27060
|
additionalProperties: false
|
|
26713
27061
|
};
|
|
26714
27062
|
constructor(work, context) {
|
|
@@ -26717,7 +27065,12 @@ var ProjectWorkListTool = class {
|
|
|
26717
27065
|
}
|
|
26718
27066
|
execute = async (args) => {
|
|
26719
27067
|
const params = normalizeToolParams(args);
|
|
26720
|
-
return JSON.stringify(await this.work.list(this.context.projectId,
|
|
27068
|
+
return JSON.stringify(await this.work.list(this.context.projectId, {
|
|
27069
|
+
includeDeleted: optionalBoolean(params.include_deleted) ?? false,
|
|
27070
|
+
...optionalString$1(params.state_id) ? { stateId: optionalString$1(params.state_id) } : {},
|
|
27071
|
+
...optionalString$1(params.cursor) ? { cursor: optionalString$1(params.cursor) } : {},
|
|
27072
|
+
...optionalNumber(params.limit) ? { limit: optionalNumber(params.limit) } : {}
|
|
27073
|
+
}), null, 2);
|
|
26721
27074
|
};
|
|
26722
27075
|
};
|
|
26723
27076
|
var ProjectWorkGetTool = class {
|