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.
Files changed (112) hide show
  1. package/config/roles/orchestrator/prompt.md +182 -25
  2. package/config/skills/agent/core/cancel-followup/SKILL.md +38 -0
  3. package/config/skills/agent/core/cancel-followup/execute.sh +111 -0
  4. package/config/skills/agent/core/cancel-followup/execute.test.sh +42 -0
  5. package/config/skills/agent/core/list-my-followups/SKILL.md +36 -0
  6. package/config/skills/agent/core/list-my-followups/execute.sh +93 -0
  7. package/config/skills/agent/core/list-my-followups/execute.test.sh +41 -0
  8. package/config/skills/agent/core/schedule-followup/SKILL.md +53 -0
  9. package/config/skills/agent/core/schedule-followup/execute.sh +195 -0
  10. package/config/skills/agent/core/schedule-followup/execute.test.sh +48 -0
  11. package/config/skills/agent/core/watch-for-event/SKILL.md +60 -0
  12. package/config/skills/agent/core/watch-for-event/execute.sh +177 -0
  13. package/config/skills/agent/core/watch-for-event/execute.test.sh +43 -0
  14. package/config/skills/orchestrator/credential-manager/SKILL.md +218 -0
  15. package/config/skills/orchestrator/credential-manager/execute.sh +166 -0
  16. package/dist/backend/backend/src/controllers/credentials/credentials.controller.d.ts +80 -0
  17. package/dist/backend/backend/src/controllers/credentials/credentials.controller.d.ts.map +1 -0
  18. package/dist/backend/backend/src/controllers/credentials/credentials.controller.js +365 -0
  19. package/dist/backend/backend/src/controllers/credentials/credentials.controller.js.map +1 -0
  20. package/dist/backend/backend/src/controllers/credentials/credentials.routes.d.ts +26 -0
  21. package/dist/backend/backend/src/controllers/credentials/credentials.routes.d.ts.map +1 -0
  22. package/dist/backend/backend/src/controllers/credentials/credentials.routes.js +40 -0
  23. package/dist/backend/backend/src/controllers/credentials/credentials.routes.js.map +1 -0
  24. package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.js +23 -14
  25. package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.js.map +1 -1
  26. package/dist/backend/backend/src/scripts/backfill-mission-priority.d.ts +3 -1
  27. package/dist/backend/backend/src/scripts/backfill-mission-priority.d.ts.map +1 -1
  28. package/dist/backend/backend/src/scripts/backfill-mission-priority.js +16 -4
  29. package/dist/backend/backend/src/scripts/backfill-mission-priority.js.map +1 -1
  30. package/dist/backend/backend/src/services/browser/browser-proxy.service.js +1 -1
  31. package/dist/backend/backend/src/services/browser/browser-proxy.service.js.map +1 -1
  32. package/dist/backend/backend/src/services/credential/credential-store.service.d.ts +161 -0
  33. package/dist/backend/backend/src/services/credential/credential-store.service.d.ts.map +1 -0
  34. package/dist/backend/backend/src/services/credential/credential-store.service.js +298 -0
  35. package/dist/backend/backend/src/services/credential/credential-store.service.js.map +1 -0
  36. package/dist/backend/backend/src/services/credential/helpers/gemini-cli-workspace.helper.d.ts +117 -0
  37. package/dist/backend/backend/src/services/credential/helpers/gemini-cli-workspace.helper.d.ts.map +1 -0
  38. package/dist/backend/backend/src/services/credential/helpers/gemini-cli-workspace.helper.js +293 -0
  39. package/dist/backend/backend/src/services/credential/helpers/gemini-cli-workspace.helper.js.map +1 -0
  40. package/dist/backend/backend/src/services/project/task.service.d.ts +18 -2
  41. package/dist/backend/backend/src/services/project/task.service.d.ts.map +1 -1
  42. package/dist/backend/backend/src/services/project/task.service.js +69 -53
  43. package/dist/backend/backend/src/services/project/task.service.js.map +1 -1
  44. package/dist/backend/backend/src/services/v3/contract-matcher.d.ts +20 -0
  45. package/dist/backend/backend/src/services/v3/contract-matcher.d.ts.map +1 -0
  46. package/dist/backend/backend/src/services/v3/contract-matcher.js +33 -0
  47. package/dist/backend/backend/src/services/v3/contract-matcher.js.map +1 -0
  48. package/dist/backend/backend/src/services/v3/escalation.service.d.ts +20 -1
  49. package/dist/backend/backend/src/services/v3/escalation.service.d.ts.map +1 -1
  50. package/dist/backend/backend/src/services/v3/escalation.service.js +97 -28
  51. package/dist/backend/backend/src/services/v3/escalation.service.js.map +1 -1
  52. package/dist/backend/backend/src/services/v3/service-contract-gate.service.d.ts +6 -4
  53. package/dist/backend/backend/src/services/v3/service-contract-gate.service.d.ts.map +1 -1
  54. package/dist/backend/backend/src/services/v3/service-contract-gate.service.js +18 -28
  55. package/dist/backend/backend/src/services/v3/service-contract-gate.service.js.map +1 -1
  56. package/dist/backend/backend/src/services/v3/team-trigger-reconciler.service.d.ts.map +1 -1
  57. package/dist/backend/backend/src/services/v3/team-trigger-reconciler.service.js +14 -9
  58. package/dist/backend/backend/src/services/v3/team-trigger-reconciler.service.js.map +1 -1
  59. package/dist/backend/backend/src/services/v3/trigger-engine.service.d.ts +34 -1
  60. package/dist/backend/backend/src/services/v3/trigger-engine.service.d.ts.map +1 -1
  61. package/dist/backend/backend/src/services/v3/trigger-engine.service.js +115 -5
  62. package/dist/backend/backend/src/services/v3/trigger-engine.service.js.map +1 -1
  63. package/dist/backend/backend/src/types/credential.types.d.ts +185 -0
  64. package/dist/backend/backend/src/types/credential.types.d.ts.map +1 -0
  65. package/dist/backend/backend/src/types/credential.types.js +76 -0
  66. package/dist/backend/backend/src/types/credential.types.js.map +1 -0
  67. package/dist/backend/backend/src/utils/encryption.utils.d.ts +57 -0
  68. package/dist/backend/backend/src/utils/encryption.utils.d.ts.map +1 -0
  69. package/dist/backend/backend/src/utils/encryption.utils.js +162 -0
  70. package/dist/backend/backend/src/utils/encryption.utils.js.map +1 -0
  71. package/dist/cli/backend/src/services/credential/credential-store.service.d.ts +161 -0
  72. package/dist/cli/backend/src/services/credential/credential-store.service.d.ts.map +1 -0
  73. package/dist/cli/backend/src/services/credential/credential-store.service.js +298 -0
  74. package/dist/cli/backend/src/services/credential/credential-store.service.js.map +1 -0
  75. package/dist/cli/backend/src/services/credential/helpers/gemini-cli-workspace.helper.d.ts +117 -0
  76. package/dist/cli/backend/src/services/credential/helpers/gemini-cli-workspace.helper.d.ts.map +1 -0
  77. package/dist/cli/backend/src/services/credential/helpers/gemini-cli-workspace.helper.js +293 -0
  78. package/dist/cli/backend/src/services/credential/helpers/gemini-cli-workspace.helper.js.map +1 -0
  79. package/dist/cli/backend/src/services/settings/settings.service.d.ts +168 -0
  80. package/dist/cli/backend/src/services/settings/settings.service.d.ts.map +1 -0
  81. package/dist/cli/backend/src/services/settings/settings.service.js +312 -0
  82. package/dist/cli/backend/src/services/settings/settings.service.js.map +1 -0
  83. package/dist/cli/backend/src/services/skill/skill-executor.service.d.ts +159 -0
  84. package/dist/cli/backend/src/services/skill/skill-executor.service.d.ts.map +1 -0
  85. package/dist/cli/backend/src/services/skill/skill-executor.service.js +626 -0
  86. package/dist/cli/backend/src/services/skill/skill-executor.service.js.map +1 -0
  87. package/dist/cli/backend/src/services/skill/skill.service.d.ts +273 -0
  88. package/dist/cli/backend/src/services/skill/skill.service.d.ts.map +1 -0
  89. package/dist/cli/backend/src/services/skill/skill.service.js +655 -0
  90. package/dist/cli/backend/src/services/skill/skill.service.js.map +1 -0
  91. package/dist/cli/backend/src/types/credential.types.d.ts +185 -0
  92. package/dist/cli/backend/src/types/credential.types.d.ts.map +1 -0
  93. package/dist/cli/backend/src/types/credential.types.js +76 -0
  94. package/dist/cli/backend/src/types/credential.types.js.map +1 -0
  95. package/dist/cli/backend/src/utils/encryption.utils.d.ts +57 -0
  96. package/dist/cli/backend/src/utils/encryption.utils.d.ts.map +1 -0
  97. package/dist/cli/backend/src/utils/encryption.utils.js +162 -0
  98. package/dist/cli/backend/src/utils/encryption.utils.js.map +1 -0
  99. package/dist/cli/backend/src/utils/skill-md-parser.d.ts +38 -0
  100. package/dist/cli/backend/src/utils/skill-md-parser.d.ts.map +1 -0
  101. package/dist/cli/backend/src/utils/skill-md-parser.js +47 -0
  102. package/dist/cli/backend/src/utils/skill-md-parser.js.map +1 -0
  103. package/frontend/dist/assets/{index-dc92ab64.css → index-6aaa0630.css} +1 -1
  104. package/frontend/dist/assets/{index-76d76633.js → index-9e6d97d1.js} +334 -328
  105. package/frontend/dist/index.html +2 -2
  106. package/package.json +1 -1
  107. package/config/experts/empathetic-resolver/expert.json +0 -11
  108. package/config/experts/empathetic-resolver.md +0 -32
  109. package/config/experts/pragmatic-architect/expert.json +0 -11
  110. package/config/experts/pragmatic-architect.md +0 -32
  111. package/config/experts/viral-alchemist/expert.json +0 -11
  112. 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":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,IAAI,CAAC;AACpC,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B,yDAAyD;AACzD,MAAM,cAAc,GAAG,CAAC,CAAC;AACzB,qEAAqE;AACrE,MAAM,SAAS,GAAG,EAAE,CAAC;AACrB,oDAAoD;AACpD,MAAM,eAAe,GAAG,EAAE,CAAC;AAC3B,wBAAwB;AACxB,MAAM,UAAU,GAAG,EAAE,CAAC;AACtB,gDAAgD;AAChD,MAAM,QAAQ,GAAG,KAAK,CAAC;AACvB,MAAM,QAAQ,GAAG,CAAC,CAAC;AACnB,MAAM,QAAQ,GAAG,CAAC,CAAC;AACnB;;;GAGG;AACH,MAAM,cAAc,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,CAAC;AACzC,8EAA8E;AAC9E,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,uBAAuB,EAAE,MAAM,CAAC,CAAC;AAEjE;;;;GAIG;AACH,MAAM,eAAe,GAAG,IAAI,GAAG,EAA2B,CAAC;AAE3D;;;;;;GAMG;AACH,KAAK,UAAU,SAAS,CAAC,aAAqB;IAC5C,MAAM,MAAM,GAAG,eAAe,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IAClD,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAE1B,MAAM,OAAO,GAAG,iBAAiB,CAAC,aAAa,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;QAC7D,oEAAoE;QACpE,eAAe,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;QACtC,MAAM,GAAG,CAAC;IACZ,CAAC,CAAC,CAAC;IACH,eAAe,CAAC,GAAG,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;IAC5C,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,KAAK,UAAU,iBAAiB,CAAC,aAAqB;IACpD,MAAM,SAAS,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;IACnD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,CAAC,MAAM,CACX,SAAS,EACT,WAAW,EACX,UAAU,EACV,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,cAAc,EAAE,EACjE,CAAC,GAAG,EAAE,UAAU,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAC/D,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB;IACnC,eAAe,CAAC,KAAK,EAAE,CAAC;AAC1B,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,aAAqB;IACzD,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;QAC/B,OAAO;IACT,CAAC;IAAC,MAAM,CAAC;QACP,kCAAkC;IACpC,CAAC;IACD,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACjE,MAAM,GAAG,GAAG,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;IACnC,MAAM,EAAE,CAAC,SAAS,CAAC,aAAa,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AAC1D,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,SAAiB,EACjB,aAAqB;IAErB,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,aAAa,CAAC,CAAC;IAC3C,MAAM,EAAE,GAAG,MAAM,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;IACzC,MAAM,MAAM,GAAG,MAAM,CAAC,cAAc,CAAC,aAAa,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;IAE7D,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC;QAC/B,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC;QAChC,MAAM,CAAC,KAAK,EAAE;KACf,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;IAEpC,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,MAAM,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,CAAC;QAC7B,EAAE;QACF,OAAO;QACP,UAAU;KACX,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,SAAiB,EACjB,aAAqB;IAErB,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,GAAG,SAAS,GAAG,eAAe,GAAG,CAAC,EAAE,CAAC;QAC3D,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;IAC/D,CAAC;IAED,MAAM,OAAO,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;IAC7B,IAAI,OAAO,KAAK,cAAc,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,0CAA0C,OAAO,EAAE,CAAC,CAAC;IACvE,CAAC;IAED,MAAM,EAAE,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,CAAC;IAChD,MAAM,OAAO,GAAG,SAAS,CAAC,QAAQ,CAChC,CAAC,GAAG,SAAS,EACb,CAAC,GAAG,SAAS,GAAG,eAAe,CAChC,CAAC;IACF,MAAM,UAAU,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC,GAAG,SAAS,GAAG,eAAe,CAAC,CAAC;IAEvE,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,aAAa,CAAC,CAAC;IAC3C,MAAM,QAAQ,GAAG,MAAM,CAAC,gBAAgB,CAAC,aAAa,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;IACjE,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IAE7B,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC;QAC9B,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC;QAC3B,QAAQ,CAAC,KAAK,EAAE;KACjB,CAAC,CAAC;IACH,OAAO,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AACpC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,qBAAqB;IACnC,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC;AAC3D,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB;IAClC,OAAO,IAAI,CAAC,IAAI,CAAC,qBAAqB,EAAE,EAAE,YAAY,CAAC,CAAC;AAC1D,CAAC"}
@@ -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"}