@skillsmith/core 0.4.14 → 0.4.15
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 +2 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/src/api/client.d.ts +1 -0
- package/dist/src/api/client.d.ts.map +1 -1
- package/dist/src/api/client.health.d.ts +26 -0
- package/dist/src/api/client.health.d.ts.map +1 -0
- package/dist/src/api/client.health.js +74 -0
- package/dist/src/api/client.health.js.map +1 -0
- package/dist/src/api/client.js +3 -51
- package/dist/src/api/client.js.map +1 -1
- package/dist/src/db/createDatabase.d.ts +9 -1
- package/dist/src/db/createDatabase.d.ts.map +1 -1
- package/dist/src/db/createDatabase.js +9 -1
- package/dist/src/db/createDatabase.js.map +1 -1
- package/dist/src/db/drivers/sqljsDriver.d.ts.map +1 -1
- package/dist/src/db/drivers/sqljsDriver.js +5 -2
- package/dist/src/db/drivers/sqljsDriver.js.map +1 -1
- package/dist/src/db/migrations/v5b-change-type.d.ts +19 -0
- package/dist/src/db/migrations/v5b-change-type.d.ts.map +1 -0
- package/dist/src/db/migrations/v5b-change-type.js +22 -0
- package/dist/src/db/migrations/v5b-change-type.js.map +1 -0
- package/dist/src/db/migrations/v6-advisories.d.ts +32 -0
- package/dist/src/db/migrations/v6-advisories.d.ts.map +1 -0
- package/dist/src/db/migrations/v6-advisories.js +53 -0
- package/dist/src/db/migrations/v6-advisories.js.map +1 -0
- package/dist/src/db/migrations/v7-compatibility.d.ts +21 -0
- package/dist/src/db/migrations/v7-compatibility.d.ts.map +1 -0
- package/dist/src/db/migrations/v7-compatibility.js +23 -0
- package/dist/src/db/migrations/v7-compatibility.js.map +1 -0
- package/dist/src/db/migrations/v8-co-installs.d.ts +21 -0
- package/dist/src/db/migrations/v8-co-installs.d.ts.map +1 -0
- package/dist/src/db/migrations/v8-co-installs.js +33 -0
- package/dist/src/db/migrations/v8-co-installs.js.map +1 -0
- package/dist/src/db/schema.d.ts +5 -16
- package/dist/src/db/schema.d.ts.map +1 -1
- package/dist/src/db/schema.js +37 -22
- package/dist/src/db/schema.js.map +1 -1
- package/dist/src/exports/repositories.d.ts +5 -0
- package/dist/src/exports/repositories.d.ts.map +1 -1
- package/dist/src/exports/repositories.js +17 -0
- package/dist/src/exports/repositories.js.map +1 -1
- package/dist/src/exports/types.d.ts +1 -1
- package/dist/src/exports/types.d.ts.map +1 -1
- package/dist/src/exports/types.js.map +1 -1
- package/dist/src/indexer/SkillParser.d.ts +2 -0
- package/dist/src/indexer/SkillParser.d.ts.map +1 -1
- package/dist/src/indexer/SkillParser.js.map +1 -1
- package/dist/src/learning/PatternStore.d.ts +0 -2
- package/dist/src/learning/PatternStore.d.ts.map +1 -1
- package/dist/src/learning/PatternStore.helpers.d.ts +19 -1
- package/dist/src/learning/PatternStore.helpers.d.ts.map +1 -1
- package/dist/src/learning/PatternStore.helpers.js +51 -0
- package/dist/src/learning/PatternStore.helpers.js.map +1 -1
- package/dist/src/learning/PatternStore.js +5 -37
- package/dist/src/learning/PatternStore.js.map +1 -1
- package/dist/src/repositories/AdvisoryRepository.d.ts +80 -0
- package/dist/src/repositories/AdvisoryRepository.d.ts.map +1 -0
- package/dist/src/repositories/AdvisoryRepository.js +128 -0
- package/dist/src/repositories/AdvisoryRepository.js.map +1 -0
- package/dist/src/repositories/AdvisoryRepository.test.d.ts +6 -0
- package/dist/src/repositories/AdvisoryRepository.test.d.ts.map +1 -0
- package/dist/src/repositories/AdvisoryRepository.test.js +149 -0
- package/dist/src/repositories/AdvisoryRepository.test.js.map +1 -0
- package/dist/src/repositories/CoInstallRepository.d.ts +68 -0
- package/dist/src/repositories/CoInstallRepository.d.ts.map +1 -0
- package/dist/src/repositories/CoInstallRepository.js +122 -0
- package/dist/src/repositories/CoInstallRepository.js.map +1 -0
- package/dist/src/repositories/SkillRepository.d.ts.map +1 -1
- package/dist/src/repositories/SkillRepository.js +16 -5
- package/dist/src/repositories/SkillRepository.js.map +1 -1
- package/dist/src/services/SearchService.helpers.d.ts.map +1 -1
- package/dist/src/services/SearchService.helpers.js +4 -0
- package/dist/src/services/SearchService.helpers.js.map +1 -1
- package/dist/src/services/SearchService.types.d.ts +1 -0
- package/dist/src/services/SearchService.types.d.ts.map +1 -1
- package/dist/src/services/TaskRunner.d.ts +5 -81
- package/dist/src/services/TaskRunner.d.ts.map +1 -1
- package/dist/src/services/TaskRunner.js +7 -68
- package/dist/src/services/TaskRunner.js.map +1 -1
- package/dist/src/services/TaskRunner.process.d.ts +33 -0
- package/dist/src/services/TaskRunner.process.d.ts.map +1 -0
- package/dist/src/services/TaskRunner.process.js +70 -0
- package/dist/src/services/TaskRunner.process.js.map +1 -0
- package/dist/src/services/TaskRunner.types.d.ts +76 -0
- package/dist/src/services/TaskRunner.types.d.ts.map +1 -0
- package/dist/src/services/TaskRunner.types.js +22 -0
- package/dist/src/services/TaskRunner.types.js.map +1 -0
- package/dist/src/services/__tests__/TaskRunner.process.test.d.ts +13 -0
- package/dist/src/services/__tests__/TaskRunner.process.test.d.ts.map +1 -0
- package/dist/src/services/__tests__/TaskRunner.process.test.js +91 -0
- package/dist/src/services/__tests__/TaskRunner.process.test.js.map +1 -0
- package/dist/src/services/quarantine/QuarantineService.d.ts +3 -24
- package/dist/src/services/quarantine/QuarantineService.d.ts.map +1 -1
- package/dist/src/services/quarantine/QuarantineService.js +8 -205
- package/dist/src/services/quarantine/QuarantineService.js.map +1 -1
- package/dist/src/services/quarantine/QuarantineService.multiapproval.d.ts +57 -0
- package/dist/src/services/quarantine/QuarantineService.multiapproval.d.ts.map +1 -0
- package/dist/src/services/quarantine/QuarantineService.multiapproval.js +211 -0
- package/dist/src/services/quarantine/QuarantineService.multiapproval.js.map +1 -0
- package/dist/src/session/SessionManager.d.ts +2 -33
- package/dist/src/session/SessionManager.d.ts.map +1 -1
- package/dist/src/session/SessionManager.js +14 -238
- package/dist/src/session/SessionManager.js.map +1 -1
- package/dist/src/session/SessionManager.memory.d.ts +67 -0
- package/dist/src/session/SessionManager.memory.d.ts.map +1 -0
- package/dist/src/session/SessionManager.memory.js +262 -0
- package/dist/src/session/SessionManager.memory.js.map +1 -0
- package/dist/src/sync/SyncEngine.d.ts +13 -1
- package/dist/src/sync/SyncEngine.d.ts.map +1 -1
- package/dist/src/sync/SyncEngine.js +50 -1
- package/dist/src/sync/SyncEngine.js.map +1 -1
- package/dist/src/testing/MultiLLMProvider.d.ts +5 -6
- package/dist/src/testing/MultiLLMProvider.d.ts.map +1 -1
- package/dist/src/testing/MultiLLMProvider.js +18 -130
- package/dist/src/testing/MultiLLMProvider.js.map +1 -1
- package/dist/src/testing/MultiLLMProvider.metrics.d.ts +33 -0
- package/dist/src/testing/MultiLLMProvider.metrics.d.ts.map +1 -0
- package/dist/src/testing/MultiLLMProvider.metrics.js +87 -0
- package/dist/src/testing/MultiLLMProvider.metrics.js.map +1 -0
- package/dist/src/testing/MultiLLMProvider.selection.d.ts +77 -0
- package/dist/src/testing/MultiLLMProvider.selection.d.ts.map +1 -0
- package/dist/src/testing/MultiLLMProvider.selection.js +151 -0
- package/dist/src/testing/MultiLLMProvider.selection.js.map +1 -0
- package/dist/src/types/skill.d.ts +10 -0
- package/dist/src/types/skill.d.ts.map +1 -1
- package/dist/src/types.d.ts +30 -0
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/versioning/change-classifier.d.ts +38 -0
- package/dist/src/versioning/change-classifier.d.ts.map +1 -0
- package/dist/src/versioning/change-classifier.js +187 -0
- package/dist/src/versioning/change-classifier.js.map +1 -0
- package/dist/src/versioning/change-classifier.test.d.ts +6 -0
- package/dist/src/versioning/change-classifier.test.d.ts.map +1 -0
- package/dist/src/versioning/change-classifier.test.js +275 -0
- package/dist/src/versioning/change-classifier.test.js.map +1 -0
- package/dist/src/versioning/update-risk.d.ts +50 -0
- package/dist/src/versioning/update-risk.d.ts.map +1 -0
- package/dist/src/versioning/update-risk.js +80 -0
- package/dist/src/versioning/update-risk.js.map +1 -0
- package/dist/src/versioning/update-risk.test.d.ts +6 -0
- package/dist/src/versioning/update-risk.test.d.ts.map +1 -0
- package/dist/src/versioning/update-risk.test.js +200 -0
- package/dist/src/versioning/update-risk.test.js.map +1 -0
- package/dist/tests/AuditLogger.edge-cases.test.d.ts +10 -0
- package/dist/tests/AuditLogger.edge-cases.test.d.ts.map +1 -0
- package/dist/tests/AuditLogger.edge-cases.test.js +183 -0
- package/dist/tests/AuditLogger.edge-cases.test.js.map +1 -0
- package/dist/tests/CacheManager.test.d.ts +9 -0
- package/dist/tests/CacheManager.test.d.ts.map +1 -0
- package/dist/tests/CacheManager.test.js +163 -0
- package/dist/tests/CacheManager.test.js.map +1 -0
- package/dist/tests/GitHubIndexer.edge-cases.test.d.ts +10 -0
- package/dist/tests/GitHubIndexer.edge-cases.test.d.ts.map +1 -0
- package/dist/tests/GitHubIndexer.edge-cases.test.js +255 -0
- package/dist/tests/GitHubIndexer.edge-cases.test.js.map +1 -0
- package/dist/tests/SearchService.test.js +71 -0
- package/dist/tests/SearchService.test.js.map +1 -1
- package/dist/tests/SkillVersionRepository.test.d.ts +0 -11
- package/dist/tests/SkillVersionRepository.test.d.ts.map +1 -1
- package/dist/tests/SkillVersionRepository.test.js +131 -194
- package/dist/tests/SkillVersionRepository.test.js.map +1 -1
- package/dist/tests/api/client.health.test.d.ts +15 -0
- package/dist/tests/api/client.health.test.d.ts.map +1 -0
- package/dist/tests/api/client.health.test.js +111 -0
- package/dist/tests/api/client.health.test.js.map +1 -0
- package/dist/tests/db/betterSqlite3Driver.test.d.ts +8 -0
- package/dist/tests/db/betterSqlite3Driver.test.d.ts.map +1 -0
- package/dist/tests/db/betterSqlite3Driver.test.js +88 -0
- package/dist/tests/db/betterSqlite3Driver.test.js.map +1 -0
- package/dist/tests/db/database-abstraction.test.js +12 -0
- package/dist/tests/db/database-abstraction.test.js.map +1 -1
- package/dist/tests/db/schema-migrations.test.d.ts +10 -0
- package/dist/tests/db/schema-migrations.test.d.ts.map +1 -0
- package/dist/tests/db/schema-migrations.test.js +134 -0
- package/dist/tests/db/schema-migrations.test.js.map +1 -0
- package/dist/tests/db/sqljsDriver.test.d.ts +9 -0
- package/dist/tests/db/sqljsDriver.test.d.ts.map +1 -0
- package/dist/tests/db/sqljsDriver.test.js +75 -0
- package/dist/tests/db/sqljsDriver.test.js.map +1 -0
- package/dist/tests/helpers/database.d.ts +32 -0
- package/dist/tests/helpers/database.d.ts.map +1 -0
- package/dist/tests/helpers/database.js +54 -0
- package/dist/tests/helpers/database.js.map +1 -0
- package/dist/tests/learning/PatternStore.helpers.test.d.ts +17 -0
- package/dist/tests/learning/PatternStore.helpers.test.d.ts.map +1 -0
- package/dist/tests/learning/PatternStore.helpers.test.js +301 -0
- package/dist/tests/learning/PatternStore.helpers.test.js.map +1 -0
- package/dist/tests/repositories/CoInstallRepository.test.d.ts +9 -0
- package/dist/tests/repositories/CoInstallRepository.test.d.ts.map +1 -0
- package/dist/tests/repositories/CoInstallRepository.test.js +126 -0
- package/dist/tests/repositories/CoInstallRepository.test.js.map +1 -0
- package/dist/tests/routing/LanguageRouter.test.d.ts +9 -0
- package/dist/tests/routing/LanguageRouter.test.d.ts.map +1 -0
- package/dist/tests/routing/LanguageRouter.test.js +150 -0
- package/dist/tests/routing/LanguageRouter.test.js.map +1 -0
- package/dist/tests/session/SessionManager.memory.test.d.ts +11 -0
- package/dist/tests/session/SessionManager.memory.test.d.ts.map +1 -0
- package/dist/tests/session/SessionManager.memory.test.js +219 -0
- package/dist/tests/session/SessionManager.memory.test.js.map +1 -0
- package/dist/tests/sync/SyncEngine.test.js +32 -18
- package/dist/tests/sync/SyncEngine.test.js.map +1 -1
- package/dist/tests/testing/MultiLLMProvider.metrics.test.d.ts +13 -0
- package/dist/tests/testing/MultiLLMProvider.metrics.test.d.ts.map +1 -0
- package/dist/tests/testing/MultiLLMProvider.metrics.test.js +149 -0
- package/dist/tests/testing/MultiLLMProvider.metrics.test.js.map +1 -0
- package/dist/tests/testing/MultiLLMProvider.selection.test.d.ts +15 -0
- package/dist/tests/testing/MultiLLMProvider.selection.test.d.ts.map +1 -0
- package/dist/tests/testing/MultiLLMProvider.selection.test.js +249 -0
- package/dist/tests/testing/MultiLLMProvider.selection.test.js.map +1 -0
- package/dist/tests/unit/quarantine-query-builder.test.d.ts +10 -0
- package/dist/tests/unit/quarantine-query-builder.test.d.ts.map +1 -0
- package/dist/tests/unit/quarantine-query-builder.test.js +157 -0
- package/dist/tests/unit/quarantine-query-builder.test.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Multi-Approval Workflow for MALICIOUS Severity Quarantine
|
|
3
|
+
* @module @skillsmith/core/services/quarantine/QuarantineService.multiapproval
|
|
4
|
+
* @see SMI-2277: Persist multi-approval state to database
|
|
5
|
+
* @see SMI-2741: Split from QuarantineService.ts to meet 500-line standard
|
|
6
|
+
*
|
|
7
|
+
* Handles the multi-approval workflow required for MALICIOUS severity skills.
|
|
8
|
+
* MALICIOUS severity requires multiple independent reviewers to approve before
|
|
9
|
+
* a skill can be unquarantined, preventing single-reviewer compromise.
|
|
10
|
+
*/
|
|
11
|
+
import type { QuarantineRepository } from '../../repositories/quarantine/index.js';
|
|
12
|
+
import type { ApprovalRepository } from '../../repositories/quarantine/ApprovalRepository.js';
|
|
13
|
+
import type { AuditLogger } from '../../security/AuditLogger.js';
|
|
14
|
+
import type { AuthenticatedSession, AuthenticatedReviewInput, AuthenticatedReviewResult, MultiApprovalStatus } from './types.js';
|
|
15
|
+
/**
|
|
16
|
+
* Number of approvals required for MALICIOUS severity reviews
|
|
17
|
+
*/
|
|
18
|
+
export declare const MALICIOUS_APPROVAL_COUNT = 2;
|
|
19
|
+
/**
|
|
20
|
+
* Multi-approval timeout in milliseconds (24 hours)
|
|
21
|
+
*/
|
|
22
|
+
export declare const MULTI_APPROVAL_TIMEOUT_MS: number;
|
|
23
|
+
/**
|
|
24
|
+
* Handle approval for MALICIOUS severity skills
|
|
25
|
+
*
|
|
26
|
+
* MALICIOUS severity requires multiple reviewers to approve
|
|
27
|
+
* before a skill can be unquarantined. This prevents single
|
|
28
|
+
* reviewer compromise from allowing malicious skills.
|
|
29
|
+
*
|
|
30
|
+
* Approval state is persisted to the database (SMI-2277) so
|
|
31
|
+
* pending approvals survive service restarts.
|
|
32
|
+
*
|
|
33
|
+
* @param session - Authenticated session
|
|
34
|
+
* @param quarantineId - Quarantine entry ID
|
|
35
|
+
* @param skillId - Skill ID
|
|
36
|
+
* @param input - Review input
|
|
37
|
+
* @param repository - Quarantine repository
|
|
38
|
+
* @param approvalRepository - Approval repository
|
|
39
|
+
* @param auditLogger - Audit logger
|
|
40
|
+
* @returns Review result with multi-approval status
|
|
41
|
+
*/
|
|
42
|
+
export declare function handleMaliciousApproval(session: AuthenticatedSession, quarantineId: string, skillId: string, input: AuthenticatedReviewInput, repository: QuarantineRepository, approvalRepository: ApprovalRepository, auditLogger: AuditLogger): AuthenticatedReviewResult;
|
|
43
|
+
/**
|
|
44
|
+
* Build a MultiApprovalStatus from database rows
|
|
45
|
+
*
|
|
46
|
+
* Converts persisted approval entries into the MultiApprovalStatus
|
|
47
|
+
* interface expected by consumers.
|
|
48
|
+
*/
|
|
49
|
+
export declare function buildMultiApprovalStatus(quarantineId: string, approvals: Array<{
|
|
50
|
+
reviewerId: string;
|
|
51
|
+
reviewerEmail: string;
|
|
52
|
+
createdAt: string;
|
|
53
|
+
completedAt: string | null;
|
|
54
|
+
reason: string | null;
|
|
55
|
+
isComplete: boolean;
|
|
56
|
+
}>): MultiApprovalStatus;
|
|
57
|
+
//# sourceMappingURL=QuarantineService.multiapproval.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"QuarantineService.multiapproval.d.ts","sourceRoot":"","sources":["../../../../src/services/quarantine/QuarantineService.multiapproval.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wCAAwC,CAAA;AAClF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qDAAqD,CAAA;AAC7F,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAA;AAChE,OAAO,KAAK,EACV,oBAAoB,EACpB,wBAAwB,EACxB,yBAAyB,EACzB,mBAAmB,EAEpB,MAAM,YAAY,CAAA;AAGnB;;GAEG;AACH,eAAO,MAAM,wBAAwB,IAAI,CAAA;AAEzC;;GAEG;AACH,eAAO,MAAM,yBAAyB,QAAsB,CAAA;AAE5D;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,oBAAoB,EAC7B,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,wBAAwB,EAC/B,UAAU,EAAE,oBAAoB,EAChC,kBAAkB,EAAE,kBAAkB,EACtC,WAAW,EAAE,WAAW,GACvB,yBAAyB,CAoK3B;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,KAAK,CAAC;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,EAAE,MAAM,CAAA;IACrB,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,UAAU,EAAE,OAAO,CAAA;CACpB,CAAC,GACD,mBAAmB,CAoBrB"}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Multi-Approval Workflow for MALICIOUS Severity Quarantine
|
|
3
|
+
* @module @skillsmith/core/services/quarantine/QuarantineService.multiapproval
|
|
4
|
+
* @see SMI-2277: Persist multi-approval state to database
|
|
5
|
+
* @see SMI-2741: Split from QuarantineService.ts to meet 500-line standard
|
|
6
|
+
*
|
|
7
|
+
* Handles the multi-approval workflow required for MALICIOUS severity skills.
|
|
8
|
+
* MALICIOUS severity requires multiple independent reviewers to approve before
|
|
9
|
+
* a skill can be unquarantined, preventing single-reviewer compromise.
|
|
10
|
+
*/
|
|
11
|
+
import { QuarantineServiceError, requirePermission } from './types.js';
|
|
12
|
+
/**
|
|
13
|
+
* Number of approvals required for MALICIOUS severity reviews
|
|
14
|
+
*/
|
|
15
|
+
export const MALICIOUS_APPROVAL_COUNT = 2;
|
|
16
|
+
/**
|
|
17
|
+
* Multi-approval timeout in milliseconds (24 hours)
|
|
18
|
+
*/
|
|
19
|
+
export const MULTI_APPROVAL_TIMEOUT_MS = 24 * 60 * 60 * 1000;
|
|
20
|
+
/**
|
|
21
|
+
* Handle approval for MALICIOUS severity skills
|
|
22
|
+
*
|
|
23
|
+
* MALICIOUS severity requires multiple reviewers to approve
|
|
24
|
+
* before a skill can be unquarantined. This prevents single
|
|
25
|
+
* reviewer compromise from allowing malicious skills.
|
|
26
|
+
*
|
|
27
|
+
* Approval state is persisted to the database (SMI-2277) so
|
|
28
|
+
* pending approvals survive service restarts.
|
|
29
|
+
*
|
|
30
|
+
* @param session - Authenticated session
|
|
31
|
+
* @param quarantineId - Quarantine entry ID
|
|
32
|
+
* @param skillId - Skill ID
|
|
33
|
+
* @param input - Review input
|
|
34
|
+
* @param repository - Quarantine repository
|
|
35
|
+
* @param approvalRepository - Approval repository
|
|
36
|
+
* @param auditLogger - Audit logger
|
|
37
|
+
* @returns Review result with multi-approval status
|
|
38
|
+
*/
|
|
39
|
+
export function handleMaliciousApproval(session, quarantineId, skillId, input, repository, approvalRepository, auditLogger) {
|
|
40
|
+
// Require elevated permission for MALICIOUS review
|
|
41
|
+
requirePermission(session, 'quarantine:review_malicious');
|
|
42
|
+
// Check if this reviewer already approved (database-backed)
|
|
43
|
+
if (approvalRepository.hasReviewerApproved(quarantineId, session.userId)) {
|
|
44
|
+
// Retrieve existing approval for error details
|
|
45
|
+
const existingApprovals = approvalRepository.getPendingApprovals(quarantineId);
|
|
46
|
+
const existing = existingApprovals.find((a) => a.reviewerId === session.userId);
|
|
47
|
+
throw new QuarantineServiceError('You have already approved this entry', 'ALREADY_REVIEWED', {
|
|
48
|
+
quarantineId,
|
|
49
|
+
previousApprovalAt: existing?.createdAt ?? 'unknown',
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
// Check for approval timeout
|
|
53
|
+
const startTime = approvalRepository.getWorkflowStartTime(quarantineId);
|
|
54
|
+
if (startTime) {
|
|
55
|
+
const timeSinceStart = Date.now() - new Date(startTime).getTime();
|
|
56
|
+
if (timeSinceStart > MULTI_APPROVAL_TIMEOUT_MS) {
|
|
57
|
+
// Capture existing approvals before clearing for audit
|
|
58
|
+
const expiredApprovals = approvalRepository.getPendingApprovals(quarantineId);
|
|
59
|
+
// Reset approval workflow
|
|
60
|
+
approvalRepository.clearApprovals(quarantineId);
|
|
61
|
+
// Log timeout event so cleared reviewer work is auditable
|
|
62
|
+
auditLogger.log({
|
|
63
|
+
event_type: 'quarantine_multi_approval_timeout',
|
|
64
|
+
actor: 'system',
|
|
65
|
+
resource: skillId,
|
|
66
|
+
action: 'timeout',
|
|
67
|
+
result: 'success',
|
|
68
|
+
metadata: {
|
|
69
|
+
quarantineId,
|
|
70
|
+
timeoutMs: MULTI_APPROVAL_TIMEOUT_MS,
|
|
71
|
+
expiredApprovals: expiredApprovals.map((a) => ({
|
|
72
|
+
reviewerId: a.reviewerId,
|
|
73
|
+
email: a.reviewerEmail,
|
|
74
|
+
approvedAt: a.createdAt,
|
|
75
|
+
})),
|
|
76
|
+
triggeredBy: {
|
|
77
|
+
userId: session.userId,
|
|
78
|
+
email: session.email,
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
throw new QuarantineServiceError('Multi-approval workflow timed out. Please start again.', 'INVALID_INPUT', { quarantineId, timeoutMs: MULTI_APPROVAL_TIMEOUT_MS });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Record this approval in the database
|
|
86
|
+
approvalRepository.recordApproval({
|
|
87
|
+
skillId: quarantineId,
|
|
88
|
+
reviewerId: session.userId,
|
|
89
|
+
reviewerEmail: session.email,
|
|
90
|
+
decision: 'approved',
|
|
91
|
+
reason: input.reviewNotes,
|
|
92
|
+
requiredApprovals: MALICIOUS_APPROVAL_COUNT,
|
|
93
|
+
});
|
|
94
|
+
// Get current approval count and all pending approvals
|
|
95
|
+
const pendingApprovals = approvalRepository.getPendingApprovals(quarantineId);
|
|
96
|
+
const approvalCount = pendingApprovals.filter((a) => a.decision === 'approved').length;
|
|
97
|
+
// Build the multi-approval status from database state
|
|
98
|
+
const approvalStatus = buildMultiApprovalStatus(quarantineId, pendingApprovals);
|
|
99
|
+
// Log the approval
|
|
100
|
+
auditLogger.log({
|
|
101
|
+
event_type: 'quarantine_multi_approval',
|
|
102
|
+
actor: 'reviewer',
|
|
103
|
+
resource: skillId,
|
|
104
|
+
action: 'approve',
|
|
105
|
+
result: 'success',
|
|
106
|
+
metadata: {
|
|
107
|
+
quarantineId,
|
|
108
|
+
approvalNumber: approvalCount,
|
|
109
|
+
requiredApprovals: MALICIOUS_APPROVAL_COUNT,
|
|
110
|
+
reviewer: {
|
|
111
|
+
userId: session.userId,
|
|
112
|
+
email: session.email,
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
// Check if we have enough approvals
|
|
117
|
+
if (approvalCount >= MALICIOUS_APPROVAL_COUNT) {
|
|
118
|
+
// Mark approvals as complete in database
|
|
119
|
+
approvalRepository.markComplete(quarantineId);
|
|
120
|
+
approvalStatus.isComplete = true;
|
|
121
|
+
approvalStatus.completedAt = new Date();
|
|
122
|
+
// Perform the actual review
|
|
123
|
+
const approverEmails = pendingApprovals
|
|
124
|
+
.filter((a) => a.decision === 'approved')
|
|
125
|
+
.map((a) => a.reviewerEmail);
|
|
126
|
+
const reviewResult = repository.review(quarantineId, {
|
|
127
|
+
reviewedBy: approverEmails.join(', '),
|
|
128
|
+
reviewStatus: 'approved',
|
|
129
|
+
reviewNotes: `Multi-approval complete: ${approvalCount} reviewers approved. ${input.reviewNotes || ''}`,
|
|
130
|
+
});
|
|
131
|
+
// Log completion
|
|
132
|
+
auditLogger.log({
|
|
133
|
+
event_type: 'quarantine_multi_approval_complete',
|
|
134
|
+
actor: 'reviewer',
|
|
135
|
+
resource: skillId,
|
|
136
|
+
action: 'complete',
|
|
137
|
+
result: 'success',
|
|
138
|
+
metadata: {
|
|
139
|
+
quarantineId,
|
|
140
|
+
approvals: pendingApprovals
|
|
141
|
+
.filter((a) => a.decision === 'approved')
|
|
142
|
+
.map((a) => ({
|
|
143
|
+
reviewerId: a.reviewerId,
|
|
144
|
+
email: a.reviewerEmail,
|
|
145
|
+
approvedAt: a.createdAt,
|
|
146
|
+
})),
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
return {
|
|
150
|
+
success: true,
|
|
151
|
+
approved: true,
|
|
152
|
+
skillId,
|
|
153
|
+
severity: 'MALICIOUS',
|
|
154
|
+
canImport: reviewResult?.canImport ?? false,
|
|
155
|
+
warnings: [
|
|
156
|
+
'MALICIOUS skill approved through multi-approval workflow',
|
|
157
|
+
`Approved by: ${approverEmails.join(', ')}`,
|
|
158
|
+
],
|
|
159
|
+
reviewedBy: {
|
|
160
|
+
userId: session.userId,
|
|
161
|
+
email: session.email,
|
|
162
|
+
displayName: session.displayName,
|
|
163
|
+
},
|
|
164
|
+
multiApprovalStatus: approvalStatus,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
// Need more approvals
|
|
168
|
+
return {
|
|
169
|
+
success: true,
|
|
170
|
+
approved: false,
|
|
171
|
+
skillId,
|
|
172
|
+
severity: 'MALICIOUS',
|
|
173
|
+
canImport: false,
|
|
174
|
+
warnings: [
|
|
175
|
+
`Multi-approval in progress: ${approvalCount}/${MALICIOUS_APPROVAL_COUNT} approvals received`,
|
|
176
|
+
`Requires ${MALICIOUS_APPROVAL_COUNT - approvalCount} more approval(s)`,
|
|
177
|
+
],
|
|
178
|
+
reviewedBy: {
|
|
179
|
+
userId: session.userId,
|
|
180
|
+
email: session.email,
|
|
181
|
+
displayName: session.displayName,
|
|
182
|
+
},
|
|
183
|
+
multiApprovalStatus: approvalStatus,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Build a MultiApprovalStatus from database rows
|
|
188
|
+
*
|
|
189
|
+
* Converts persisted approval entries into the MultiApprovalStatus
|
|
190
|
+
* interface expected by consumers.
|
|
191
|
+
*/
|
|
192
|
+
export function buildMultiApprovalStatus(quarantineId, approvals) {
|
|
193
|
+
const currentApprovals = approvals.map((a) => ({
|
|
194
|
+
reviewerId: a.reviewerId,
|
|
195
|
+
reviewerEmail: a.reviewerEmail,
|
|
196
|
+
approvedAt: new Date(a.createdAt),
|
|
197
|
+
notes: a.reason ?? undefined,
|
|
198
|
+
}));
|
|
199
|
+
const startedAt = approvals.length > 0 ? new Date(approvals[0].createdAt) : new Date();
|
|
200
|
+
const isComplete = approvals.some((a) => a.isComplete);
|
|
201
|
+
const completedEntry = approvals.find((a) => a.completedAt);
|
|
202
|
+
return {
|
|
203
|
+
quarantineId,
|
|
204
|
+
requiredApprovals: MALICIOUS_APPROVAL_COUNT,
|
|
205
|
+
currentApprovals,
|
|
206
|
+
isComplete,
|
|
207
|
+
startedAt,
|
|
208
|
+
completedAt: completedEntry ? new Date(completedEntry.completedAt) : undefined,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
//# sourceMappingURL=QuarantineService.multiapproval.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"QuarantineService.multiapproval.js","sourceRoot":"","sources":["../../../../src/services/quarantine/QuarantineService.multiapproval.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Session Manager for Claude-Flow Memory Integration
|
|
3
3
|
* SMI-641: Session ID Storage in Claude-Flow Memory
|
|
4
4
|
* SMI-1518: V3 API Migration - Use direct API calls instead of spawn
|
|
5
|
+
* SMI-2741: Split to meet 500-line standard
|
|
5
6
|
*
|
|
6
7
|
* Manages session lifecycle with persistent storage in claude-flow memory
|
|
7
8
|
* to enable context restoration across sessions.
|
|
@@ -10,6 +11,7 @@ import type { SessionData, Checkpoint } from './SessionContext.js';
|
|
|
10
11
|
import type { CommandExecutor, SessionOptions } from './SessionManager.types.js';
|
|
11
12
|
export type { CommandExecutor, MemoryResult, SessionOptions } from './SessionManager.types.js';
|
|
12
13
|
export { DefaultCommandExecutor } from './SessionManager.helpers.js';
|
|
14
|
+
export { storeMemoryEntry, retrieveMemoryEntry, deleteMemoryEntry, runPreTaskHook, runPostTaskHook, } from './SessionManager.memory.js';
|
|
13
15
|
/**
|
|
14
16
|
* Session Manager for claude-flow memory integration
|
|
15
17
|
*
|
|
@@ -85,38 +87,5 @@ export declare class SessionManager {
|
|
|
85
87
|
* Clear the current session pointer
|
|
86
88
|
*/
|
|
87
89
|
private clearCurrentSession;
|
|
88
|
-
/**
|
|
89
|
-
* Store data in claude-flow memory
|
|
90
|
-
*
|
|
91
|
-
* SMI-674: Uses spawn() with argument array to prevent command injection
|
|
92
|
-
* SMI-1518: V3 API Migration - Use direct storeEntry() when available
|
|
93
|
-
*/
|
|
94
|
-
private storeMemory;
|
|
95
|
-
/**
|
|
96
|
-
* Retrieve data from claude-flow memory
|
|
97
|
-
*
|
|
98
|
-
* SMI-674: Uses spawn() with argument array to prevent command injection
|
|
99
|
-
* SMI-1518: V3 API Migration - Use direct getEntry() when available
|
|
100
|
-
*/
|
|
101
|
-
private retrieveMemory;
|
|
102
|
-
/**
|
|
103
|
-
* Delete data from claude-flow memory
|
|
104
|
-
*
|
|
105
|
-
* SMI-674: Uses spawn() with argument array to prevent command injection
|
|
106
|
-
* SMI-1518: V3 API Migration - Use callMCPTool('memory/delete') when available
|
|
107
|
-
*/
|
|
108
|
-
private deleteMemory;
|
|
109
|
-
/**
|
|
110
|
-
* Run pre-task hook
|
|
111
|
-
* SMI-674: Uses spawn() with argument array to prevent command injection
|
|
112
|
-
* SMI-1518: V3 API Migration - Use callMCPTool('hooks/pre-task') when available
|
|
113
|
-
*/
|
|
114
|
-
private runPreTaskHook;
|
|
115
|
-
/**
|
|
116
|
-
* Run post-task hook
|
|
117
|
-
* SMI-674: Uses spawn() with argument array to prevent command injection
|
|
118
|
-
* SMI-1518: V3 API Migration - Use callMCPTool('hooks/post-task') when available
|
|
119
|
-
*/
|
|
120
|
-
private runPostTaskHook;
|
|
121
90
|
}
|
|
122
91
|
//# sourceMappingURL=SessionManager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionManager.d.ts","sourceRoot":"","sources":["../../../src/session/SessionManager.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"SessionManager.d.ts","sourceRoot":"","sources":["../../../src/session/SessionManager.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAGlE,OAAO,KAAK,EAAE,eAAe,EAAgB,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAgB9F,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAC9F,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAA;AACpE,OAAO,EACL,gBAAgB,EAChB,mBAAmB,EACnB,iBAAiB,EACjB,cAAc,EACd,eAAe,GAChB,MAAM,4BAA4B,CAAA;AAEnC;;;;;;;;;;;;GAYG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAiB;IACjC,OAAO,CAAC,cAAc,CAA2B;IAEjD;;;OAGG;IACH,OAAO,CAAC,WAAW,CAAmC;gBAE1C,QAAQ,CAAC,EAAE,eAAe;IAItC;;;OAGG;YACW,QAAQ;IAiBtB;;OAEG;IACH,iBAAiB,IAAI,MAAM;IAI3B;;OAEG;IACG,YAAY,CAAC,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,WAAW,CAAC;IA6BtE;;;;;OAKG;IACG,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAqDhE;;;OAGG;IACG,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAezD;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAiBjC;;OAEG;IACH,iBAAiB,IAAI,WAAW,GAAG,IAAI;IAIvC;;OAEG;IACG,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAehE;;OAEG;IACG,mBAAmB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAenD;;OAEG;YACW,YAAY;IAM1B;;OAEG;YACW,iBAAiB;IAI/B;;OAEG;YACW,mBAAmB;CAGlC"}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Session Manager for Claude-Flow Memory Integration
|
|
3
3
|
* SMI-641: Session ID Storage in Claude-Flow Memory
|
|
4
4
|
* SMI-1518: V3 API Migration - Use direct API calls instead of spawn
|
|
5
|
+
* SMI-2741: Split to meet 500-line standard
|
|
5
6
|
*
|
|
6
7
|
* Manages session lifecycle with persistent storage in claude-flow memory
|
|
7
8
|
* to enable context restoration across sessions.
|
|
@@ -9,8 +10,11 @@
|
|
|
9
10
|
import { randomUUID } from 'node:crypto';
|
|
10
11
|
import { sanitizeSessionData } from './SessionManager.types.js';
|
|
11
12
|
// Import helpers
|
|
12
|
-
import {
|
|
13
|
+
import { MEMORY_KEYS, DefaultCommandExecutor } from './SessionManager.helpers.js';
|
|
14
|
+
// SMI-2741: Memory and hook operations extracted to companion file
|
|
15
|
+
import { storeMemoryEntry, retrieveMemoryEntry, deleteMemoryEntry, runPreTaskHook, runPostTaskHook, } from './SessionManager.memory.js';
|
|
13
16
|
export { DefaultCommandExecutor } from './SessionManager.helpers.js';
|
|
17
|
+
export { storeMemoryEntry, retrieveMemoryEntry, deleteMemoryEntry, runPreTaskHook, runPostTaskHook, } from './SessionManager.memory.js';
|
|
14
18
|
/**
|
|
15
19
|
* Session Manager for claude-flow memory integration
|
|
16
20
|
*
|
|
@@ -80,7 +84,7 @@ export class SessionManager {
|
|
|
80
84
|
await this.setCurrentSession(sessionId);
|
|
81
85
|
// Run pre-task hook if description provided
|
|
82
86
|
if (options.description) {
|
|
83
|
-
await
|
|
87
|
+
await runPreTaskHook(options.description, this.executor);
|
|
84
88
|
}
|
|
85
89
|
this.currentSession = session;
|
|
86
90
|
return session;
|
|
@@ -103,7 +107,7 @@ export class SessionManager {
|
|
|
103
107
|
memoryKey: `${MEMORY_KEYS.CHECKPOINT_PREFIX}${this.currentSession.sessionId}/${Date.now()}`,
|
|
104
108
|
};
|
|
105
109
|
// SMI-676: Store checkpoint data FIRST (before updating session)
|
|
106
|
-
const checkpointResult = await
|
|
110
|
+
const checkpointResult = await storeMemoryEntry(checkpoint.memoryKey, JSON.stringify(checkpoint), this.executor);
|
|
107
111
|
if (!checkpointResult.success) {
|
|
108
112
|
throw new Error(`Failed to store checkpoint: ${checkpointResult.error}`);
|
|
109
113
|
}
|
|
@@ -125,7 +129,7 @@ export class SessionManager {
|
|
|
125
129
|
this.currentSession.checkpoints = previousCheckpoints;
|
|
126
130
|
this.currentSession.lastActivity = previousLastActivity;
|
|
127
131
|
// Clean up the checkpoint from memory
|
|
128
|
-
await
|
|
132
|
+
await deleteMemoryEntry(checkpoint.memoryKey, this.executor);
|
|
129
133
|
throw err;
|
|
130
134
|
}
|
|
131
135
|
return checkpoint;
|
|
@@ -158,7 +162,7 @@ export class SessionManager {
|
|
|
158
162
|
this.currentSession.lastActivity = new Date().toISOString();
|
|
159
163
|
await this.storeSession(this.currentSession);
|
|
160
164
|
// Run post-task hook
|
|
161
|
-
await
|
|
165
|
+
await runPostTaskHook(this.currentSession.sessionId, this.executor);
|
|
162
166
|
// Clear current session pointer
|
|
163
167
|
await this.clearCurrentSession();
|
|
164
168
|
this.currentSession = null;
|
|
@@ -174,7 +178,7 @@ export class SessionManager {
|
|
|
174
178
|
*/
|
|
175
179
|
async getSession(sessionId) {
|
|
176
180
|
const memoryKey = `${MEMORY_KEYS.SESSION_PREFIX}${sessionId}`;
|
|
177
|
-
const result = await this.
|
|
181
|
+
const result = await retrieveMemoryEntry(memoryKey, this.executor);
|
|
178
182
|
if (!result.success || !result.data) {
|
|
179
183
|
return null;
|
|
180
184
|
}
|
|
@@ -189,7 +193,7 @@ export class SessionManager {
|
|
|
189
193
|
* Get the ID of the current session from memory
|
|
190
194
|
*/
|
|
191
195
|
async getCurrentSessionId() {
|
|
192
|
-
const result = await
|
|
196
|
+
const result = await retrieveMemoryEntry(MEMORY_KEYS.CURRENT, this.executor);
|
|
193
197
|
if (!result.success || !result.data) {
|
|
194
198
|
return null;
|
|
195
199
|
}
|
|
@@ -207,247 +211,19 @@ export class SessionManager {
|
|
|
207
211
|
async storeSession(session) {
|
|
208
212
|
const memoryKey = `${MEMORY_KEYS.SESSION_PREFIX}${session.sessionId}`;
|
|
209
213
|
const sanitized = sanitizeSessionData(session);
|
|
210
|
-
return
|
|
214
|
+
return storeMemoryEntry(memoryKey, JSON.stringify(sanitized), this.executor);
|
|
211
215
|
}
|
|
212
216
|
/**
|
|
213
217
|
* Set the current session pointer
|
|
214
218
|
*/
|
|
215
219
|
async setCurrentSession(sessionId) {
|
|
216
|
-
return
|
|
220
|
+
return storeMemoryEntry(MEMORY_KEYS.CURRENT, JSON.stringify({ sessionId }), this.executor);
|
|
217
221
|
}
|
|
218
222
|
/**
|
|
219
223
|
* Clear the current session pointer
|
|
220
224
|
*/
|
|
221
225
|
async clearCurrentSession() {
|
|
222
|
-
return
|
|
223
|
-
}
|
|
224
|
-
/**
|
|
225
|
-
* Store data in claude-flow memory
|
|
226
|
-
*
|
|
227
|
-
* SMI-674: Uses spawn() with argument array to prevent command injection
|
|
228
|
-
* SMI-1518: V3 API Migration - Use direct storeEntry() when available
|
|
229
|
-
*/
|
|
230
|
-
async storeMemory(key, value) {
|
|
231
|
-
if (!validateMemoryKey(key)) {
|
|
232
|
-
return { success: false, error: 'Invalid memory key' };
|
|
233
|
-
}
|
|
234
|
-
// SMI-1518, SMI-1609: Try V3 direct API first if enabled
|
|
235
|
-
if (USE_V3_API) {
|
|
236
|
-
try {
|
|
237
|
-
const memoryModule = await getClaudeFlowMemory();
|
|
238
|
-
if (memoryModule?.storeEntry) {
|
|
239
|
-
const result = await memoryModule.storeEntry({
|
|
240
|
-
key,
|
|
241
|
-
value,
|
|
242
|
-
namespace: MEMORY_NAMESPACE,
|
|
243
|
-
});
|
|
244
|
-
if (result.success) {
|
|
245
|
-
return { success: true };
|
|
246
|
-
}
|
|
247
|
-
console.warn(`V3 storeEntry failed: ${result.error}, falling back to spawn`);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
catch (error) {
|
|
251
|
-
console.warn(`V3 storeEntry exception: ${error}, falling back to spawn`);
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
try {
|
|
255
|
-
const args = ['claude-flow', 'memory', 'store', '--key', key, '--value', value];
|
|
256
|
-
if (this.executor.spawn) {
|
|
257
|
-
await this.executor.spawn('npx', args);
|
|
258
|
-
}
|
|
259
|
-
else {
|
|
260
|
-
const escapedValue = value.replace(/'/g, "'\\''");
|
|
261
|
-
const command = `npx claude-flow memory store --key "${key}" --value '${escapedValue}'`;
|
|
262
|
-
await this.executor.execute(command);
|
|
263
|
-
}
|
|
264
|
-
return { success: true };
|
|
265
|
-
}
|
|
266
|
-
catch (error) {
|
|
267
|
-
return {
|
|
268
|
-
success: false,
|
|
269
|
-
error: error instanceof Error ? error.message : String(error),
|
|
270
|
-
};
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
/**
|
|
274
|
-
* Retrieve data from claude-flow memory
|
|
275
|
-
*
|
|
276
|
-
* SMI-674: Uses spawn() with argument array to prevent command injection
|
|
277
|
-
* SMI-1518: V3 API Migration - Use direct getEntry() when available
|
|
278
|
-
*/
|
|
279
|
-
async retrieveMemory(key) {
|
|
280
|
-
if (!validateMemoryKey(key)) {
|
|
281
|
-
return { success: false, error: 'Invalid memory key' };
|
|
282
|
-
}
|
|
283
|
-
// SMI-1518, SMI-1609: Try V3 direct API first if enabled
|
|
284
|
-
if (USE_V3_API) {
|
|
285
|
-
try {
|
|
286
|
-
const memoryModule = await getClaudeFlowMemory();
|
|
287
|
-
if (memoryModule?.getEntry) {
|
|
288
|
-
const result = await memoryModule.getEntry({
|
|
289
|
-
key,
|
|
290
|
-
namespace: MEMORY_NAMESPACE,
|
|
291
|
-
});
|
|
292
|
-
if (result.success && result.found && result.entry) {
|
|
293
|
-
return { success: true, data: result.entry.content };
|
|
294
|
-
}
|
|
295
|
-
if (result.success && !result.found) {
|
|
296
|
-
return { success: false, error: 'Key not found' };
|
|
297
|
-
}
|
|
298
|
-
console.warn(`V3 getEntry failed: ${result.error}, falling back to spawn`);
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
catch (error) {
|
|
302
|
-
console.warn(`V3 getEntry exception: ${error}, falling back to spawn`);
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
try {
|
|
306
|
-
const args = ['claude-flow', 'memory', 'get', '--key', key];
|
|
307
|
-
let stdout;
|
|
308
|
-
if (this.executor.spawn) {
|
|
309
|
-
const result = await this.executor.spawn('npx', args);
|
|
310
|
-
stdout = result.stdout;
|
|
311
|
-
}
|
|
312
|
-
else {
|
|
313
|
-
const command = `npx claude-flow memory get --key "${key}"`;
|
|
314
|
-
const result = await this.executor.execute(command);
|
|
315
|
-
stdout = result.stdout;
|
|
316
|
-
}
|
|
317
|
-
return { success: true, data: stdout.trim() };
|
|
318
|
-
}
|
|
319
|
-
catch (error) {
|
|
320
|
-
return {
|
|
321
|
-
success: false,
|
|
322
|
-
error: error instanceof Error ? error.message : String(error),
|
|
323
|
-
};
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
/**
|
|
327
|
-
* Delete data from claude-flow memory
|
|
328
|
-
*
|
|
329
|
-
* SMI-674: Uses spawn() with argument array to prevent command injection
|
|
330
|
-
* SMI-1518: V3 API Migration - Use callMCPTool('memory/delete') when available
|
|
331
|
-
*/
|
|
332
|
-
async deleteMemory(key) {
|
|
333
|
-
if (!validateMemoryKey(key)) {
|
|
334
|
-
return { success: false, error: 'Invalid memory key' };
|
|
335
|
-
}
|
|
336
|
-
// SMI-1518, SMI-1609: Try V3 MCP API first if enabled
|
|
337
|
-
if (USE_V3_API) {
|
|
338
|
-
try {
|
|
339
|
-
const mcpModule = await getClaudeFlowMcp();
|
|
340
|
-
if (mcpModule?.callMCPTool) {
|
|
341
|
-
const result = (await mcpModule.callMCPTool('memory/delete', { key }));
|
|
342
|
-
if (result.success) {
|
|
343
|
-
return { success: true };
|
|
344
|
-
}
|
|
345
|
-
console.warn(`V3 memory/delete failed, falling back to spawn`);
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
catch (error) {
|
|
349
|
-
const mcpModule = await getClaudeFlowMcp();
|
|
350
|
-
const MCPClientError = mcpModule?.MCPClientError;
|
|
351
|
-
if (!MCPClientError || !(error instanceof MCPClientError)) {
|
|
352
|
-
console.warn(`V3 memory/delete exception: ${error}, falling back to spawn`);
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
try {
|
|
357
|
-
const args = ['claude-flow', 'memory', 'delete', '--key', key];
|
|
358
|
-
if (this.executor.spawn) {
|
|
359
|
-
await this.executor.spawn('npx', args);
|
|
360
|
-
}
|
|
361
|
-
else {
|
|
362
|
-
const command = `npx claude-flow memory delete --key "${key}"`;
|
|
363
|
-
await this.executor.execute(command);
|
|
364
|
-
}
|
|
365
|
-
return { success: true };
|
|
366
|
-
}
|
|
367
|
-
catch {
|
|
368
|
-
// Ignore delete errors (key may not exist)
|
|
369
|
-
return { success: true };
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
/**
|
|
373
|
-
* Run pre-task hook
|
|
374
|
-
* SMI-674: Uses spawn() with argument array to prevent command injection
|
|
375
|
-
* SMI-1518: V3 API Migration - Use callMCPTool('hooks/pre-task') when available
|
|
376
|
-
*/
|
|
377
|
-
async runPreTaskHook(description) {
|
|
378
|
-
// SMI-1518, SMI-1609: Try V3 MCP API first if enabled
|
|
379
|
-
if (USE_V3_API) {
|
|
380
|
-
try {
|
|
381
|
-
const mcpModule = await getClaudeFlowMcp();
|
|
382
|
-
if (mcpModule?.callMCPTool) {
|
|
383
|
-
await mcpModule.callMCPTool('hooks/pre-task', {
|
|
384
|
-
description,
|
|
385
|
-
memoryKey: 'session/current',
|
|
386
|
-
});
|
|
387
|
-
return;
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
catch {
|
|
391
|
-
// V3 API not available or failed, fall back to spawn
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
try {
|
|
395
|
-
const args = [
|
|
396
|
-
'claude-flow',
|
|
397
|
-
'hooks',
|
|
398
|
-
'pre-task',
|
|
399
|
-
'--description',
|
|
400
|
-
description,
|
|
401
|
-
'--memory-key',
|
|
402
|
-
'session/current',
|
|
403
|
-
];
|
|
404
|
-
if (this.executor.spawn) {
|
|
405
|
-
await this.executor.spawn('npx', args);
|
|
406
|
-
}
|
|
407
|
-
else {
|
|
408
|
-
const escapedDesc = description.replace(/'/g, "'\\''");
|
|
409
|
-
const command = `npx claude-flow hooks pre-task --description '${escapedDesc}' --memory-key "session/current"`;
|
|
410
|
-
await this.executor.execute(command);
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
catch {
|
|
414
|
-
// Hooks are optional, don't fail if they don't work
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
/**
|
|
418
|
-
* Run post-task hook
|
|
419
|
-
* SMI-674: Uses spawn() with argument array to prevent command injection
|
|
420
|
-
* SMI-1518: V3 API Migration - Use callMCPTool('hooks/post-task') when available
|
|
421
|
-
*/
|
|
422
|
-
async runPostTaskHook(taskId) {
|
|
423
|
-
// SMI-1518, SMI-1609: Try V3 MCP API first if enabled
|
|
424
|
-
if (USE_V3_API) {
|
|
425
|
-
try {
|
|
426
|
-
const mcpModule = await getClaudeFlowMcp();
|
|
427
|
-
if (mcpModule?.callMCPTool) {
|
|
428
|
-
await mcpModule.callMCPTool('hooks/post-task', {
|
|
429
|
-
taskId,
|
|
430
|
-
});
|
|
431
|
-
return;
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
catch {
|
|
435
|
-
// V3 API not available or failed, fall back to spawn
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
try {
|
|
439
|
-
const args = ['claude-flow', 'hooks', 'post-task', '--task-id', taskId];
|
|
440
|
-
if (this.executor.spawn) {
|
|
441
|
-
await this.executor.spawn('npx', args);
|
|
442
|
-
}
|
|
443
|
-
else {
|
|
444
|
-
const command = `npx claude-flow hooks post-task --task-id "${taskId}"`;
|
|
445
|
-
await this.executor.execute(command);
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
catch {
|
|
449
|
-
// Hooks are optional, don't fail if they don't work
|
|
450
|
-
}
|
|
226
|
+
return deleteMemoryEntry(MEMORY_KEYS.CURRENT, this.executor);
|
|
451
227
|
}
|
|
452
228
|
}
|
|
453
229
|
//# sourceMappingURL=SessionManager.js.map
|