crewly 1.5.22 → 1.6.0
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/config/roles/orchestrator/prompt.md +182 -25
- package/config/skills/agent/core/cancel-followup/SKILL.md +38 -0
- package/config/skills/agent/core/cancel-followup/execute.sh +111 -0
- package/config/skills/agent/core/cancel-followup/execute.test.sh +42 -0
- package/config/skills/agent/core/list-my-followups/SKILL.md +36 -0
- package/config/skills/agent/core/list-my-followups/execute.sh +93 -0
- package/config/skills/agent/core/list-my-followups/execute.test.sh +41 -0
- package/config/skills/agent/core/schedule-followup/SKILL.md +53 -0
- package/config/skills/agent/core/schedule-followup/execute.sh +195 -0
- package/config/skills/agent/core/schedule-followup/execute.test.sh +48 -0
- package/config/skills/agent/core/watch-for-event/SKILL.md +60 -0
- package/config/skills/agent/core/watch-for-event/execute.sh +177 -0
- package/config/skills/agent/core/watch-for-event/execute.test.sh +43 -0
- package/config/skills/orchestrator/credential-manager/SKILL.md +218 -0
- package/config/skills/orchestrator/credential-manager/execute.sh +166 -0
- package/dist/backend/backend/src/controllers/credentials/credentials.controller.d.ts +80 -0
- package/dist/backend/backend/src/controllers/credentials/credentials.controller.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/credentials/credentials.controller.js +365 -0
- package/dist/backend/backend/src/controllers/credentials/credentials.controller.js.map +1 -0
- package/dist/backend/backend/src/controllers/credentials/credentials.routes.d.ts +26 -0
- package/dist/backend/backend/src/controllers/credentials/credentials.routes.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/credentials/credentials.routes.js +40 -0
- package/dist/backend/backend/src/controllers/credentials/credentials.routes.js.map +1 -0
- package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.js +23 -14
- package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.js.map +1 -1
- package/dist/backend/backend/src/scripts/backfill-mission-priority.d.ts +3 -1
- package/dist/backend/backend/src/scripts/backfill-mission-priority.d.ts.map +1 -1
- package/dist/backend/backend/src/scripts/backfill-mission-priority.js +16 -4
- package/dist/backend/backend/src/scripts/backfill-mission-priority.js.map +1 -1
- package/dist/backend/backend/src/services/browser/browser-proxy.service.js +1 -1
- package/dist/backend/backend/src/services/browser/browser-proxy.service.js.map +1 -1
- package/dist/backend/backend/src/services/credential/credential-store.service.d.ts +161 -0
- package/dist/backend/backend/src/services/credential/credential-store.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/credential/credential-store.service.js +298 -0
- package/dist/backend/backend/src/services/credential/credential-store.service.js.map +1 -0
- package/dist/backend/backend/src/services/credential/helpers/gemini-cli-workspace.helper.d.ts +117 -0
- package/dist/backend/backend/src/services/credential/helpers/gemini-cli-workspace.helper.d.ts.map +1 -0
- package/dist/backend/backend/src/services/credential/helpers/gemini-cli-workspace.helper.js +293 -0
- package/dist/backend/backend/src/services/credential/helpers/gemini-cli-workspace.helper.js.map +1 -0
- package/dist/backend/backend/src/services/project/task.service.d.ts +18 -2
- package/dist/backend/backend/src/services/project/task.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/project/task.service.js +69 -53
- package/dist/backend/backend/src/services/project/task.service.js.map +1 -1
- package/dist/backend/backend/src/services/v3/contract-matcher.d.ts +20 -0
- package/dist/backend/backend/src/services/v3/contract-matcher.d.ts.map +1 -0
- package/dist/backend/backend/src/services/v3/contract-matcher.js +33 -0
- package/dist/backend/backend/src/services/v3/contract-matcher.js.map +1 -0
- package/dist/backend/backend/src/services/v3/escalation.service.d.ts +20 -1
- package/dist/backend/backend/src/services/v3/escalation.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/escalation.service.js +97 -28
- package/dist/backend/backend/src/services/v3/escalation.service.js.map +1 -1
- package/dist/backend/backend/src/services/v3/service-contract-gate.service.d.ts +6 -4
- package/dist/backend/backend/src/services/v3/service-contract-gate.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/service-contract-gate.service.js +18 -28
- package/dist/backend/backend/src/services/v3/service-contract-gate.service.js.map +1 -1
- package/dist/backend/backend/src/services/v3/team-trigger-reconciler.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/team-trigger-reconciler.service.js +14 -9
- package/dist/backend/backend/src/services/v3/team-trigger-reconciler.service.js.map +1 -1
- package/dist/backend/backend/src/services/v3/trigger-engine.service.d.ts +34 -1
- package/dist/backend/backend/src/services/v3/trigger-engine.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/trigger-engine.service.js +115 -5
- package/dist/backend/backend/src/services/v3/trigger-engine.service.js.map +1 -1
- package/dist/backend/backend/src/types/credential.types.d.ts +185 -0
- package/dist/backend/backend/src/types/credential.types.d.ts.map +1 -0
- package/dist/backend/backend/src/types/credential.types.js +76 -0
- package/dist/backend/backend/src/types/credential.types.js.map +1 -0
- package/dist/backend/backend/src/utils/encryption.utils.d.ts +57 -0
- package/dist/backend/backend/src/utils/encryption.utils.d.ts.map +1 -0
- package/dist/backend/backend/src/utils/encryption.utils.js +162 -0
- package/dist/backend/backend/src/utils/encryption.utils.js.map +1 -0
- package/dist/cli/backend/src/services/credential/credential-store.service.d.ts +161 -0
- package/dist/cli/backend/src/services/credential/credential-store.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/credential/credential-store.service.js +298 -0
- package/dist/cli/backend/src/services/credential/credential-store.service.js.map +1 -0
- package/dist/cli/backend/src/services/credential/helpers/gemini-cli-workspace.helper.d.ts +117 -0
- package/dist/cli/backend/src/services/credential/helpers/gemini-cli-workspace.helper.d.ts.map +1 -0
- package/dist/cli/backend/src/services/credential/helpers/gemini-cli-workspace.helper.js +293 -0
- package/dist/cli/backend/src/services/credential/helpers/gemini-cli-workspace.helper.js.map +1 -0
- package/dist/cli/backend/src/services/settings/settings.service.d.ts +168 -0
- package/dist/cli/backend/src/services/settings/settings.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/settings/settings.service.js +312 -0
- package/dist/cli/backend/src/services/settings/settings.service.js.map +1 -0
- package/dist/cli/backend/src/services/skill/skill-executor.service.d.ts +159 -0
- package/dist/cli/backend/src/services/skill/skill-executor.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/skill/skill-executor.service.js +626 -0
- package/dist/cli/backend/src/services/skill/skill-executor.service.js.map +1 -0
- package/dist/cli/backend/src/services/skill/skill.service.d.ts +273 -0
- package/dist/cli/backend/src/services/skill/skill.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/skill/skill.service.js +655 -0
- package/dist/cli/backend/src/services/skill/skill.service.js.map +1 -0
- package/dist/cli/backend/src/types/credential.types.d.ts +185 -0
- package/dist/cli/backend/src/types/credential.types.d.ts.map +1 -0
- package/dist/cli/backend/src/types/credential.types.js +76 -0
- package/dist/cli/backend/src/types/credential.types.js.map +1 -0
- package/dist/cli/backend/src/utils/encryption.utils.d.ts +57 -0
- package/dist/cli/backend/src/utils/encryption.utils.d.ts.map +1 -0
- package/dist/cli/backend/src/utils/encryption.utils.js +162 -0
- package/dist/cli/backend/src/utils/encryption.utils.js.map +1 -0
- package/dist/cli/backend/src/utils/skill-md-parser.d.ts +38 -0
- package/dist/cli/backend/src/utils/skill-md-parser.d.ts.map +1 -0
- package/dist/cli/backend/src/utils/skill-md-parser.js +47 -0
- package/dist/cli/backend/src/utils/skill-md-parser.js.map +1 -0
- package/frontend/dist/assets/{index-dc92ab64.css → index-6aaa0630.css} +1 -1
- package/frontend/dist/assets/{index-76d76633.js → index-9e6d97d1.js} +334 -328
- package/frontend/dist/index.html +2 -2
- package/package.json +1 -1
- package/config/experts/empathetic-resolver/expert.json +0 -11
- package/config/experts/empathetic-resolver.md +0 -32
- package/config/experts/pragmatic-architect/expert.json +0 -11
- package/config/experts/pragmatic-architect.md +0 -32
- package/config/experts/viral-alchemist/expert.json +0 -11
- package/config/experts/viral-alchemist.md +0 -32
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credential System Types
|
|
3
|
+
*
|
|
4
|
+
* Workspace-level credential store. Credentials (API keys, OAuth tokens) are
|
|
5
|
+
* decoupled from skills: skills declare slots, users add named credentials,
|
|
6
|
+
* agents bind specific credentials to slots at execution time.
|
|
7
|
+
*
|
|
8
|
+
* Registry entries are safe to show to LLM/agent contexts. Encrypted
|
|
9
|
+
* payloads (containing real token values) are never exposed to the agent —
|
|
10
|
+
* they are read only by the credential helper and by the skill executor
|
|
11
|
+
* for env-var injection into subprocesses.
|
|
12
|
+
*
|
|
13
|
+
* @module types/credential.types
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* High-level type of credential — determines which payload schema is used
|
|
17
|
+
* and which helper (if any) owns the refresh lifecycle.
|
|
18
|
+
*/
|
|
19
|
+
export type CredentialType = 'api-key' | 'google-oauth';
|
|
20
|
+
/**
|
|
21
|
+
* Lifecycle status of a credential.
|
|
22
|
+
*
|
|
23
|
+
* - `active`: usable; refresh tokens are valid
|
|
24
|
+
* - `revoked`: underlying refresh token no longer works. User must re-authorize.
|
|
25
|
+
*/
|
|
26
|
+
export type CredentialStatus = 'active' | 'revoked';
|
|
27
|
+
/**
|
|
28
|
+
* Identifier for the helper responsible for token acquisition and refresh.
|
|
29
|
+
*
|
|
30
|
+
* - `gemini-cli-workspace`: piggybacks on user's Gemini CLI Workspace extension (Phase 1)
|
|
31
|
+
* - `byo-oauth`: user's own registered GCP OAuth client (Phase 2)
|
|
32
|
+
* - `gcloud-adc`: Google Cloud Application Default Credentials (Phase 2)
|
|
33
|
+
* - `crewly-official`: Crewly's published OAuth app (Phase 3)
|
|
34
|
+
*/
|
|
35
|
+
export type CredentialHelperName = 'gemini-cli-workspace' | 'byo-oauth' | 'gcloud-adc' | 'crewly-official';
|
|
36
|
+
/**
|
|
37
|
+
* Public metadata for a credential. Never contains secret material.
|
|
38
|
+
* This is the exact shape exposed to agents / LLM contexts.
|
|
39
|
+
*/
|
|
40
|
+
export interface CredentialRegistryEntry {
|
|
41
|
+
/** UUID v4 — stable reference used everywhere (bindings, URLs, logs). */
|
|
42
|
+
id: string;
|
|
43
|
+
/** User-provided display name (e.g., "personal-gmail"). Not a reference. */
|
|
44
|
+
name: string;
|
|
45
|
+
/** Type discriminator — drives payload schema. */
|
|
46
|
+
type: CredentialType;
|
|
47
|
+
/** Provider identifier (e.g., "gemini", "google", "openai"). */
|
|
48
|
+
provider: string;
|
|
49
|
+
/** Helper that manages refresh (required for OAuth types). */
|
|
50
|
+
helper?: CredentialHelperName;
|
|
51
|
+
/** Granted OAuth scopes (OAuth types only). */
|
|
52
|
+
scopes?: string[];
|
|
53
|
+
/** Email address of the OAuth account — for user-visible display. */
|
|
54
|
+
accountEmail?: string;
|
|
55
|
+
/** ISO-8601 creation timestamp. */
|
|
56
|
+
createdAt: string;
|
|
57
|
+
/** ISO-8601 last metadata update. */
|
|
58
|
+
updatedAt: string;
|
|
59
|
+
/** ISO-8601 last injection into a skill run. */
|
|
60
|
+
lastUsedAt?: string;
|
|
61
|
+
/** Unix epoch ms when the current access token expires (OAuth only). */
|
|
62
|
+
expiresAt?: number;
|
|
63
|
+
/** Current status. */
|
|
64
|
+
status: CredentialStatus;
|
|
65
|
+
/** Relative path to the encrypted payload file (from credentials dir). */
|
|
66
|
+
encFile: string;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Registry file format stored at `~/.crewly/credentials/registry.json`.
|
|
70
|
+
*/
|
|
71
|
+
export interface CredentialRegistry {
|
|
72
|
+
schemaVersion: 1;
|
|
73
|
+
credentials: CredentialRegistryEntry[];
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Payload for a static API key credential.
|
|
77
|
+
*/
|
|
78
|
+
export interface ApiKeyPayload {
|
|
79
|
+
type: 'api-key';
|
|
80
|
+
value: string;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Payload for a Google OAuth credential with refreshable access.
|
|
84
|
+
*/
|
|
85
|
+
export interface GoogleOAuthPayload {
|
|
86
|
+
type: 'google-oauth';
|
|
87
|
+
accessToken: string;
|
|
88
|
+
refreshToken: string;
|
|
89
|
+
tokenType: 'Bearer';
|
|
90
|
+
/** Unix epoch ms when accessToken expires. */
|
|
91
|
+
expiresAt: number;
|
|
92
|
+
/** Scopes granted by user consent. */
|
|
93
|
+
scopes: string[];
|
|
94
|
+
/** Account email (also denormalized into registry entry for display). */
|
|
95
|
+
accountEmail?: string;
|
|
96
|
+
/** OAuth client_id used to mint this grant (required for refresh). */
|
|
97
|
+
clientId: string;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Discriminated union over all payload variants. Switch on `type`.
|
|
101
|
+
*/
|
|
102
|
+
export type CredentialPayload = ApiKeyPayload | GoogleOAuthPayload;
|
|
103
|
+
/**
|
|
104
|
+
* Concrete implementation of a credential helper strategy.
|
|
105
|
+
*
|
|
106
|
+
* Helpers are responsible for:
|
|
107
|
+
* - Returning a valid access token (refreshing if near expiry)
|
|
108
|
+
* - Recognizing `invalid_grant` / revocation and signalling via thrown error
|
|
109
|
+
*
|
|
110
|
+
* Implementations must be idempotent under concurrent calls for the same
|
|
111
|
+
* credential — the store's refresh manager wraps calls in a per-credential
|
|
112
|
+
* mutex, but helpers should not assume exclusive access across processes.
|
|
113
|
+
*/
|
|
114
|
+
export interface CredentialHelper {
|
|
115
|
+
readonly name: CredentialHelperName;
|
|
116
|
+
/**
|
|
117
|
+
* Return a valid access token for the given credential, refreshing if
|
|
118
|
+
* within the expiry buffer.
|
|
119
|
+
*
|
|
120
|
+
* @throws CredentialRevokedError if the refresh token is no longer valid
|
|
121
|
+
*/
|
|
122
|
+
getAccessToken(entry: CredentialRegistryEntry, payload: GoogleOAuthPayload): Promise<GoogleOAuthPayload>;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* A credential requirement declared in a SKILL.md frontmatter.
|
|
126
|
+
* Each requirement is a named "slot" the skill will read via an env var
|
|
127
|
+
* (`CREWLY_CRED_<SLOT>` for api-key, `CREWLY_CRED_<SLOT>_*` for OAuth).
|
|
128
|
+
*/
|
|
129
|
+
export interface SkillCredentialRequirement {
|
|
130
|
+
/** Slot name — used verbatim in env var prefix. Alphanumeric + hyphens. */
|
|
131
|
+
slot: string;
|
|
132
|
+
/** Credential type this slot expects. */
|
|
133
|
+
type: CredentialType;
|
|
134
|
+
/** Provider the skill expects (e.g., 'gemini', 'google'). */
|
|
135
|
+
provider: string;
|
|
136
|
+
/** Whether execution fails if no credential is bound to this slot. */
|
|
137
|
+
required: boolean;
|
|
138
|
+
/** UUID of a credential to use when no binding is supplied at runtime. */
|
|
139
|
+
default?: string;
|
|
140
|
+
/** OAuth scopes required for this slot (google-oauth only). */
|
|
141
|
+
requiredScopes?: string[];
|
|
142
|
+
/** Description shown in UI when selecting a credential for this slot. */
|
|
143
|
+
description?: string;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Runtime map from slot name to credential UUID, supplied by the agent in
|
|
147
|
+
* `execute_skill`. Overrides the skill's `default`.
|
|
148
|
+
*/
|
|
149
|
+
export type CredentialBindings = Record<string, string>;
|
|
150
|
+
/**
|
|
151
|
+
* Thrown when a required slot has neither a binding nor a default.
|
|
152
|
+
*/
|
|
153
|
+
export declare class MissingCredentialError extends Error {
|
|
154
|
+
readonly slot: string;
|
|
155
|
+
readonly provider: string;
|
|
156
|
+
constructor(slot: string, provider: string);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Thrown when a bound credential's scopes do not cover the slot's requirements.
|
|
160
|
+
*/
|
|
161
|
+
export declare class InsufficientScopeError extends Error {
|
|
162
|
+
readonly slot: string;
|
|
163
|
+
readonly required: string[];
|
|
164
|
+
readonly granted: string[];
|
|
165
|
+
readonly missing: string[];
|
|
166
|
+
constructor(slot: string, required: string[], granted: string[]);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Thrown when an OAuth credential's refresh token is no longer valid.
|
|
170
|
+
* Carries the remediation string that callers (UI / agent) can surface to users.
|
|
171
|
+
*/
|
|
172
|
+
export declare class CredentialRevokedError extends Error {
|
|
173
|
+
readonly credentialId: string;
|
|
174
|
+
readonly credentialName: string;
|
|
175
|
+
readonly remediation: string;
|
|
176
|
+
constructor(credentialId: string, credentialName: string, remediation: string);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Thrown when a referenced credential id doesn't exist in the registry.
|
|
180
|
+
*/
|
|
181
|
+
export declare class CredentialNotFoundError extends Error {
|
|
182
|
+
readonly credentialId: string;
|
|
183
|
+
constructor(credentialId: string);
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=credential.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credential.types.d.ts","sourceRoot":"","sources":["../../../../../backend/src/types/credential.types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credential System Types
|
|
3
|
+
*
|
|
4
|
+
* Workspace-level credential store. Credentials (API keys, OAuth tokens) are
|
|
5
|
+
* decoupled from skills: skills declare slots, users add named credentials,
|
|
6
|
+
* agents bind specific credentials to slots at execution time.
|
|
7
|
+
*
|
|
8
|
+
* Registry entries are safe to show to LLM/agent contexts. Encrypted
|
|
9
|
+
* payloads (containing real token values) are never exposed to the agent —
|
|
10
|
+
* they are read only by the credential helper and by the skill executor
|
|
11
|
+
* for env-var injection into subprocesses.
|
|
12
|
+
*
|
|
13
|
+
* @module types/credential.types
|
|
14
|
+
*/
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// Errors
|
|
17
|
+
// ============================================================================
|
|
18
|
+
/**
|
|
19
|
+
* Thrown when a required slot has neither a binding nor a default.
|
|
20
|
+
*/
|
|
21
|
+
export class MissingCredentialError extends Error {
|
|
22
|
+
slot;
|
|
23
|
+
provider;
|
|
24
|
+
constructor(slot, provider) {
|
|
25
|
+
super(`No credential bound to required slot '${slot}' (provider: ${provider})`);
|
|
26
|
+
this.slot = slot;
|
|
27
|
+
this.provider = provider;
|
|
28
|
+
this.name = 'MissingCredentialError';
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Thrown when a bound credential's scopes do not cover the slot's requirements.
|
|
33
|
+
*/
|
|
34
|
+
export class InsufficientScopeError extends Error {
|
|
35
|
+
slot;
|
|
36
|
+
required;
|
|
37
|
+
granted;
|
|
38
|
+
missing;
|
|
39
|
+
constructor(slot, required, granted) {
|
|
40
|
+
const missing = required.filter((s) => !granted.includes(s));
|
|
41
|
+
super(`Credential for slot '${slot}' is missing scope(s): ${missing.join(', ')}`);
|
|
42
|
+
this.slot = slot;
|
|
43
|
+
this.required = required;
|
|
44
|
+
this.granted = granted;
|
|
45
|
+
this.name = 'InsufficientScopeError';
|
|
46
|
+
this.missing = missing;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Thrown when an OAuth credential's refresh token is no longer valid.
|
|
51
|
+
* Carries the remediation string that callers (UI / agent) can surface to users.
|
|
52
|
+
*/
|
|
53
|
+
export class CredentialRevokedError extends Error {
|
|
54
|
+
credentialId;
|
|
55
|
+
credentialName;
|
|
56
|
+
remediation;
|
|
57
|
+
constructor(credentialId, credentialName, remediation) {
|
|
58
|
+
super(`Credential '${credentialName}' (${credentialId}) is revoked. ${remediation}`);
|
|
59
|
+
this.credentialId = credentialId;
|
|
60
|
+
this.credentialName = credentialName;
|
|
61
|
+
this.remediation = remediation;
|
|
62
|
+
this.name = 'CredentialRevokedError';
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Thrown when a referenced credential id doesn't exist in the registry.
|
|
67
|
+
*/
|
|
68
|
+
export class CredentialNotFoundError extends Error {
|
|
69
|
+
credentialId;
|
|
70
|
+
constructor(credentialId) {
|
|
71
|
+
super(`Credential not found: ${credentialId}`);
|
|
72
|
+
this.credentialId = credentialId;
|
|
73
|
+
this.name = 'CredentialNotFoundError';
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=credential.types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credential.types.js","sourceRoot":"","sources":["../../../../../backend/src/types/credential.types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AA4KH,+EAA+E;AAC/E,SAAS;AACT,+EAA+E;AAE/E;;GAEG;AACH,MAAM,OAAO,sBAAuB,SAAQ,KAAK;IAE7B;IACA;IAFlB,YACkB,IAAY,EACZ,QAAgB;QAEhC,KAAK,CAAC,yCAAyC,IAAI,gBAAgB,QAAQ,GAAG,CAAC,CAAC;QAHhE,SAAI,GAAJ,IAAI,CAAQ;QACZ,aAAQ,GAAR,QAAQ,CAAQ;QAGhC,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC;IACvC,CAAC;CACF;AAED;;GAEG;AACH,MAAM,OAAO,sBAAuB,SAAQ,KAAK;IAI7B;IACA;IACA;IALF,OAAO,CAAW;IAElC,YACkB,IAAY,EACZ,QAAkB,EAClB,OAAiB;QAEjC,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7D,KAAK,CACH,wBAAwB,IAAI,0BAA0B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC3E,CAAC;QAPc,SAAI,GAAJ,IAAI,CAAQ;QACZ,aAAQ,GAAR,QAAQ,CAAU;QAClB,YAAO,GAAP,OAAO,CAAU;QAMjC,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC;QACrC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,OAAO,sBAAuB,SAAQ,KAAK;IAE7B;IACA;IACA;IAHlB,YACkB,YAAoB,EACpB,cAAsB,EACtB,WAAmB;QAEnC,KAAK,CACH,eAAe,cAAc,MAAM,YAAY,iBAAiB,WAAW,EAAE,CAC9E,CAAC;QANc,iBAAY,GAAZ,YAAY,CAAQ;QACpB,mBAAc,GAAd,cAAc,CAAQ;QACtB,gBAAW,GAAX,WAAW,CAAQ;QAKnC,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC;IACvC,CAAC;CACF;AAED;;GAEG;AACH,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IACpB;IAA5B,YAA4B,YAAoB;QAC9C,KAAK,CAAC,yBAAyB,YAAY,EAAE,CAAC,CAAC;QADrB,iBAAY,GAAZ,YAAY,CAAQ;QAE9C,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;IACxC,CAAC;CACF"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credential Encryption Utilities
|
|
3
|
+
*
|
|
4
|
+
* AES-256-GCM encryption with scrypt-derived keys for credential payloads.
|
|
5
|
+
* Uses a per-install master.key file (mode 0600) as the base key material.
|
|
6
|
+
*
|
|
7
|
+
* File format:
|
|
8
|
+
* [1 byte: version=1] [12 bytes: IV] [16 bytes: auth tag] [N bytes: ciphertext]
|
|
9
|
+
*
|
|
10
|
+
* Threat model:
|
|
11
|
+
* - Primary defense: file exfiltration via backup / cloud sync / screen capture
|
|
12
|
+
* - NOT a defense against a compromised local user account or malicious code
|
|
13
|
+
* running as the user (both can read master.key and derive the same key)
|
|
14
|
+
*
|
|
15
|
+
* @module utils/encryption.utils
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Clear the derived-key cache. Primarily for tests — production code should
|
|
19
|
+
* not need this as master.key is assumed stable for the process lifetime.
|
|
20
|
+
*/
|
|
21
|
+
export declare function _resetDerivedKeyCache(): void;
|
|
22
|
+
/**
|
|
23
|
+
* Ensure a master.key file exists at the given path. If absent, generate
|
|
24
|
+
* a cryptographically random 32-byte key and write it with mode 0600.
|
|
25
|
+
*
|
|
26
|
+
* Idempotent: if the file already exists, does nothing.
|
|
27
|
+
*
|
|
28
|
+
* @param masterKeyPath - Absolute path to the master.key file
|
|
29
|
+
*/
|
|
30
|
+
export declare function ensureMasterKey(masterKeyPath: string): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Encrypt a UTF-8 string using AES-256-GCM.
|
|
33
|
+
*
|
|
34
|
+
* @param plaintext - The string to encrypt (caller typically JSON-serializes first)
|
|
35
|
+
* @param masterKeyPath - Absolute path to the master.key file
|
|
36
|
+
* @returns Buffer in the format `[version][iv][tag][ciphertext]`
|
|
37
|
+
*/
|
|
38
|
+
export declare function encrypt(plaintext: string, masterKeyPath: string): Promise<Buffer>;
|
|
39
|
+
/**
|
|
40
|
+
* Decrypt a buffer produced by `encrypt()`.
|
|
41
|
+
*
|
|
42
|
+
* @param encrypted - Buffer in the `[version][iv][tag][ciphertext]` format
|
|
43
|
+
* @param masterKeyPath - Absolute path to the master.key file
|
|
44
|
+
* @returns The decrypted UTF-8 string
|
|
45
|
+
* @throws Error if the buffer is malformed, the version is unsupported,
|
|
46
|
+
* or the auth tag does not match (tampering or wrong key)
|
|
47
|
+
*/
|
|
48
|
+
export declare function decrypt(encrypted: Buffer, masterKeyPath: string): Promise<string>;
|
|
49
|
+
/**
|
|
50
|
+
* Default credentials directory: `~/.crewly/credentials/`.
|
|
51
|
+
*/
|
|
52
|
+
export declare function defaultCredentialsDir(): string;
|
|
53
|
+
/**
|
|
54
|
+
* Default master.key path: `~/.crewly/credentials/master.key`.
|
|
55
|
+
*/
|
|
56
|
+
export declare function defaultMasterKeyPath(): string;
|
|
57
|
+
//# sourceMappingURL=encryption.utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encryption.utils.d.ts","sourceRoot":"","sources":["../../../../../backend/src/utils/encryption.utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAmEH;;;GAGG;AACH,wBAAgB,qBAAqB,IAAI,IAAI,CAE5C;AAED;;;;;;;GAOG;AACH,wBAAsB,eAAe,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAU1E;AAED;;;;;;GAMG;AACH,wBAAsB,OAAO,CAC3B,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,MAAM,CAAC,CAiBjB;AAED;;;;;;;;GAQG;AACH,wBAAsB,OAAO,CAC3B,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,MAAM,CAAC,CA0BjB;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAE9C;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credential Encryption Utilities
|
|
3
|
+
*
|
|
4
|
+
* AES-256-GCM encryption with scrypt-derived keys for credential payloads.
|
|
5
|
+
* Uses a per-install master.key file (mode 0600) as the base key material.
|
|
6
|
+
*
|
|
7
|
+
* File format:
|
|
8
|
+
* [1 byte: version=1] [12 bytes: IV] [16 bytes: auth tag] [N bytes: ciphertext]
|
|
9
|
+
*
|
|
10
|
+
* Threat model:
|
|
11
|
+
* - Primary defense: file exfiltration via backup / cloud sync / screen capture
|
|
12
|
+
* - NOT a defense against a compromised local user account or malicious code
|
|
13
|
+
* running as the user (both can read master.key and derive the same key)
|
|
14
|
+
*
|
|
15
|
+
* @module utils/encryption.utils
|
|
16
|
+
*/
|
|
17
|
+
import { promises as fs } from 'fs';
|
|
18
|
+
import path from 'path';
|
|
19
|
+
import os from 'os';
|
|
20
|
+
import crypto from 'crypto';
|
|
21
|
+
/** File-format version byte — bump if layout changes. */
|
|
22
|
+
const FORMAT_VERSION = 1;
|
|
23
|
+
/** AES-256-GCM uses 12-byte (96-bit) IVs per NIST recommendation. */
|
|
24
|
+
const IV_LENGTH = 12;
|
|
25
|
+
/** AES-GCM produces 16-byte (128-bit) auth tags. */
|
|
26
|
+
const AUTH_TAG_LENGTH = 16;
|
|
27
|
+
/** AES-256 key size. */
|
|
28
|
+
const KEY_LENGTH = 32;
|
|
29
|
+
/** scrypt N (iteration count). 2^15 = 32768. */
|
|
30
|
+
const SCRYPT_N = 32768;
|
|
31
|
+
const SCRYPT_R = 8;
|
|
32
|
+
const SCRYPT_P = 1;
|
|
33
|
+
/**
|
|
34
|
+
* scrypt memory cap. Default in Node is 32MB which is right at the edge for
|
|
35
|
+
* our parameters; give explicit headroom so derivation never fails.
|
|
36
|
+
*/
|
|
37
|
+
const SCRYPT_MAX_MEM = 128 * 1024 * 1024;
|
|
38
|
+
/** Fixed salt — changing invalidates all previously encrypted credentials. */
|
|
39
|
+
const SCRYPT_SALT = Buffer.from('crewly-credentials-v1', 'utf8');
|
|
40
|
+
/**
|
|
41
|
+
* Module-level cache of derived keys, keyed by master.key path. scrypt is
|
|
42
|
+
* intentionally expensive (~100ms per call), so we run it once per master
|
|
43
|
+
* key path per process. Concurrent callers share the in-flight promise.
|
|
44
|
+
*/
|
|
45
|
+
const derivedKeyCache = new Map();
|
|
46
|
+
/**
|
|
47
|
+
* Derive the AES key from the master.key file contents via scrypt. Results
|
|
48
|
+
* are cached by master-key path for the lifetime of the process.
|
|
49
|
+
*
|
|
50
|
+
* @param masterKeyPath - Absolute path to the master.key file
|
|
51
|
+
* @returns A 32-byte derived key
|
|
52
|
+
*/
|
|
53
|
+
async function deriveKey(masterKeyPath) {
|
|
54
|
+
const cached = derivedKeyCache.get(masterKeyPath);
|
|
55
|
+
if (cached)
|
|
56
|
+
return cached;
|
|
57
|
+
const promise = deriveKeyUncached(masterKeyPath).catch((err) => {
|
|
58
|
+
// On failure, remove the rejected promise so the next call retries.
|
|
59
|
+
derivedKeyCache.delete(masterKeyPath);
|
|
60
|
+
throw err;
|
|
61
|
+
});
|
|
62
|
+
derivedKeyCache.set(masterKeyPath, promise);
|
|
63
|
+
return promise;
|
|
64
|
+
}
|
|
65
|
+
async function deriveKeyUncached(masterKeyPath) {
|
|
66
|
+
const masterKey = await fs.readFile(masterKeyPath);
|
|
67
|
+
return new Promise((resolve, reject) => {
|
|
68
|
+
crypto.scrypt(masterKey, SCRYPT_SALT, KEY_LENGTH, { N: SCRYPT_N, r: SCRYPT_R, p: SCRYPT_P, maxmem: SCRYPT_MAX_MEM }, (err, derivedKey) => (err ? reject(err) : resolve(derivedKey)));
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Clear the derived-key cache. Primarily for tests — production code should
|
|
73
|
+
* not need this as master.key is assumed stable for the process lifetime.
|
|
74
|
+
*/
|
|
75
|
+
export function _resetDerivedKeyCache() {
|
|
76
|
+
derivedKeyCache.clear();
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Ensure a master.key file exists at the given path. If absent, generate
|
|
80
|
+
* a cryptographically random 32-byte key and write it with mode 0600.
|
|
81
|
+
*
|
|
82
|
+
* Idempotent: if the file already exists, does nothing.
|
|
83
|
+
*
|
|
84
|
+
* @param masterKeyPath - Absolute path to the master.key file
|
|
85
|
+
*/
|
|
86
|
+
export async function ensureMasterKey(masterKeyPath) {
|
|
87
|
+
try {
|
|
88
|
+
await fs.access(masterKeyPath);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// File doesn't exist — create it.
|
|
93
|
+
}
|
|
94
|
+
await fs.mkdir(path.dirname(masterKeyPath), { recursive: true });
|
|
95
|
+
const key = crypto.randomBytes(32);
|
|
96
|
+
await fs.writeFile(masterKeyPath, key, { mode: 0o600 });
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Encrypt a UTF-8 string using AES-256-GCM.
|
|
100
|
+
*
|
|
101
|
+
* @param plaintext - The string to encrypt (caller typically JSON-serializes first)
|
|
102
|
+
* @param masterKeyPath - Absolute path to the master.key file
|
|
103
|
+
* @returns Buffer in the format `[version][iv][tag][ciphertext]`
|
|
104
|
+
*/
|
|
105
|
+
export async function encrypt(plaintext, masterKeyPath) {
|
|
106
|
+
const key = await deriveKey(masterKeyPath);
|
|
107
|
+
const iv = crypto.randomBytes(IV_LENGTH);
|
|
108
|
+
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
|
|
109
|
+
const ciphertext = Buffer.concat([
|
|
110
|
+
cipher.update(plaintext, 'utf8'),
|
|
111
|
+
cipher.final(),
|
|
112
|
+
]);
|
|
113
|
+
const authTag = cipher.getAuthTag();
|
|
114
|
+
return Buffer.concat([
|
|
115
|
+
Buffer.from([FORMAT_VERSION]),
|
|
116
|
+
iv,
|
|
117
|
+
authTag,
|
|
118
|
+
ciphertext,
|
|
119
|
+
]);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Decrypt a buffer produced by `encrypt()`.
|
|
123
|
+
*
|
|
124
|
+
* @param encrypted - Buffer in the `[version][iv][tag][ciphertext]` format
|
|
125
|
+
* @param masterKeyPath - Absolute path to the master.key file
|
|
126
|
+
* @returns The decrypted UTF-8 string
|
|
127
|
+
* @throws Error if the buffer is malformed, the version is unsupported,
|
|
128
|
+
* or the auth tag does not match (tampering or wrong key)
|
|
129
|
+
*/
|
|
130
|
+
export async function decrypt(encrypted, masterKeyPath) {
|
|
131
|
+
if (encrypted.length < 1 + IV_LENGTH + AUTH_TAG_LENGTH + 1) {
|
|
132
|
+
throw new Error('Encrypted buffer is too short to be valid');
|
|
133
|
+
}
|
|
134
|
+
const version = encrypted[0];
|
|
135
|
+
if (version !== FORMAT_VERSION) {
|
|
136
|
+
throw new Error(`Unsupported encryption format version: ${version}`);
|
|
137
|
+
}
|
|
138
|
+
const iv = encrypted.subarray(1, 1 + IV_LENGTH);
|
|
139
|
+
const authTag = encrypted.subarray(1 + IV_LENGTH, 1 + IV_LENGTH + AUTH_TAG_LENGTH);
|
|
140
|
+
const ciphertext = encrypted.subarray(1 + IV_LENGTH + AUTH_TAG_LENGTH);
|
|
141
|
+
const key = await deriveKey(masterKeyPath);
|
|
142
|
+
const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
|
|
143
|
+
decipher.setAuthTag(authTag);
|
|
144
|
+
const plaintext = Buffer.concat([
|
|
145
|
+
decipher.update(ciphertext),
|
|
146
|
+
decipher.final(),
|
|
147
|
+
]);
|
|
148
|
+
return plaintext.toString('utf8');
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Default credentials directory: `~/.crewly/credentials/`.
|
|
152
|
+
*/
|
|
153
|
+
export function defaultCredentialsDir() {
|
|
154
|
+
return path.join(os.homedir(), '.crewly', 'credentials');
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Default master.key path: `~/.crewly/credentials/master.key`.
|
|
158
|
+
*/
|
|
159
|
+
export function defaultMasterKeyPath() {
|
|
160
|
+
return path.join(defaultCredentialsDir(), 'master.key');
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=encryption.utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encryption.utils.js","sourceRoot":"","sources":["../../../../../backend/src/utils/encryption.utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SKILL.md Parser
|
|
3
|
+
*
|
|
4
|
+
* Parses SKILL.md files containing YAML frontmatter and Markdown body.
|
|
5
|
+
* Used by SkillService and SkillCatalogService to load skill definitions
|
|
6
|
+
* from the new SKILL.md format (replacing skill.json + instructions.md).
|
|
7
|
+
*
|
|
8
|
+
* @module utils/skill-md-parser
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Result of parsing a SKILL.md file.
|
|
12
|
+
*/
|
|
13
|
+
export interface SkillMdParseResult {
|
|
14
|
+
/** Parsed YAML frontmatter as a plain object */
|
|
15
|
+
frontmatter: Record<string, unknown>;
|
|
16
|
+
/** Markdown body content after the frontmatter */
|
|
17
|
+
body: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Parse a SKILL.md file's content into frontmatter and body.
|
|
21
|
+
*
|
|
22
|
+
* Expects the format:
|
|
23
|
+
* ```
|
|
24
|
+
* ---
|
|
25
|
+
* name: Skill Name
|
|
26
|
+
* description: What it does
|
|
27
|
+
* ...
|
|
28
|
+
* ---
|
|
29
|
+
*
|
|
30
|
+
* # Markdown body here
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* @param content - Raw content of the SKILL.md file
|
|
34
|
+
* @returns Parsed frontmatter object and markdown body
|
|
35
|
+
* @throws Error if frontmatter delimiters are missing or YAML is invalid
|
|
36
|
+
*/
|
|
37
|
+
export declare function parseSkillMd(content: string): SkillMdParseResult;
|
|
38
|
+
//# sourceMappingURL=skill-md-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-md-parser.d.ts","sourceRoot":"","sources":["../../../../../backend/src/utils/skill-md-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,gDAAgD;IAChD,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,kBAAkB,CAuBhE"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SKILL.md Parser
|
|
3
|
+
*
|
|
4
|
+
* Parses SKILL.md files containing YAML frontmatter and Markdown body.
|
|
5
|
+
* Used by SkillService and SkillCatalogService to load skill definitions
|
|
6
|
+
* from the new SKILL.md format (replacing skill.json + instructions.md).
|
|
7
|
+
*
|
|
8
|
+
* @module utils/skill-md-parser
|
|
9
|
+
*/
|
|
10
|
+
import { parse as parseYAML } from 'yaml';
|
|
11
|
+
/**
|
|
12
|
+
* Parse a SKILL.md file's content into frontmatter and body.
|
|
13
|
+
*
|
|
14
|
+
* Expects the format:
|
|
15
|
+
* ```
|
|
16
|
+
* ---
|
|
17
|
+
* name: Skill Name
|
|
18
|
+
* description: What it does
|
|
19
|
+
* ...
|
|
20
|
+
* ---
|
|
21
|
+
*
|
|
22
|
+
* # Markdown body here
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* @param content - Raw content of the SKILL.md file
|
|
26
|
+
* @returns Parsed frontmatter object and markdown body
|
|
27
|
+
* @throws Error if frontmatter delimiters are missing or YAML is invalid
|
|
28
|
+
*/
|
|
29
|
+
export function parseSkillMd(content) {
|
|
30
|
+
const trimmed = content.trimStart();
|
|
31
|
+
if (!trimmed.startsWith('---')) {
|
|
32
|
+
throw new Error('SKILL.md missing opening frontmatter delimiter (---)');
|
|
33
|
+
}
|
|
34
|
+
// Find the closing --- delimiter (skip the first one)
|
|
35
|
+
const closingIndex = trimmed.indexOf('\n---', 3);
|
|
36
|
+
if (closingIndex === -1) {
|
|
37
|
+
throw new Error('SKILL.md missing closing frontmatter delimiter (---)');
|
|
38
|
+
}
|
|
39
|
+
const yamlContent = trimmed.slice(4, closingIndex); // skip opening "---\n"
|
|
40
|
+
const body = trimmed.slice(closingIndex + 4).trim(); // skip "\n---"
|
|
41
|
+
const frontmatter = parseYAML(yamlContent);
|
|
42
|
+
if (!frontmatter || typeof frontmatter !== 'object') {
|
|
43
|
+
throw new Error('SKILL.md frontmatter is not a valid YAML object');
|
|
44
|
+
}
|
|
45
|
+
return { frontmatter, body };
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=skill-md-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-md-parser.js","sourceRoot":"","sources":["../../../../../backend/src/utils/skill-md-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAY1C;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,YAAY,CAAC,OAAe;IAC1C,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAEpC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;IAC1E,CAAC;IAED,sDAAsD;IACtD,MAAM,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IACjD,IAAI,YAAY,KAAK,CAAC,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;IAC1E,CAAC;IAED,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,uBAAuB;IAC3E,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,YAAY,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,eAAe;IAEpE,MAAM,WAAW,GAAG,SAAS,CAAC,WAAW,CAA4B,CAAC;IAEtE,IAAI,CAAC,WAAW,IAAI,OAAO,WAAW,KAAK,QAAQ,EAAE,CAAC;QACpD,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACrE,CAAC;IAED,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;AAC/B,CAAC"}
|