@caupulican/pi-adaptative 0.85.8 → 0.86.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (239) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/dist/core/agent-paths.d.ts +6 -3
  3. package/dist/core/agent-paths.d.ts.map +1 -1
  4. package/dist/core/agent-paths.js +10 -3
  5. package/dist/core/agent-paths.js.map +1 -1
  6. package/dist/core/agent-session.d.ts +5 -2
  7. package/dist/core/agent-session.d.ts.map +1 -1
  8. package/dist/core/agent-session.js +7 -1
  9. package/dist/core/agent-session.js.map +1 -1
  10. package/dist/core/autonomy/bounded-completion.d.ts.map +1 -1
  11. package/dist/core/autonomy/bounded-completion.js +19 -8
  12. package/dist/core/autonomy/bounded-completion.js.map +1 -1
  13. package/dist/core/autonomy/lane-tool-surface.d.ts +11 -5
  14. package/dist/core/autonomy/lane-tool-surface.d.ts.map +1 -1
  15. package/dist/core/autonomy/lane-tool-surface.js +34 -8
  16. package/dist/core/autonomy/lane-tool-surface.js.map +1 -1
  17. package/dist/core/autonomy/subagent-prompt.d.ts +3 -3
  18. package/dist/core/autonomy/subagent-prompt.js +9 -9
  19. package/dist/core/autonomy/subagent-prompt.js.map +1 -1
  20. package/dist/core/background-lane-controller.d.ts +10 -8
  21. package/dist/core/background-lane-controller.d.ts.map +1 -1
  22. package/dist/core/background-lane-controller.js +23 -13
  23. package/dist/core/background-lane-controller.js.map +1 -1
  24. package/dist/core/bash-execution-controller.d.ts +4 -0
  25. package/dist/core/bash-execution-controller.d.ts.map +1 -1
  26. package/dist/core/bash-execution-controller.js +11 -4
  27. package/dist/core/bash-execution-controller.js.map +1 -1
  28. package/dist/core/bash-executor.d.ts +2 -0
  29. package/dist/core/bash-executor.d.ts.map +1 -1
  30. package/dist/core/bash-executor.js +2 -1
  31. package/dist/core/bash-executor.js.map +1 -1
  32. package/dist/core/capability-contract.d.ts +1 -1
  33. package/dist/core/capability-contract.d.ts.map +1 -1
  34. package/dist/core/capability-contract.js +0 -1
  35. package/dist/core/capability-contract.js.map +1 -1
  36. package/dist/core/default-tool-surface.d.ts +1 -0
  37. package/dist/core/default-tool-surface.d.ts.map +1 -1
  38. package/dist/core/default-tool-surface.js +3 -2
  39. package/dist/core/default-tool-surface.js.map +1 -1
  40. package/dist/core/delegation/worker-agent-control-coordinator.d.ts +27 -8
  41. package/dist/core/delegation/worker-agent-control-coordinator.d.ts.map +1 -1
  42. package/dist/core/delegation/worker-agent-control-coordinator.js +191 -33
  43. package/dist/core/delegation/worker-agent-control-coordinator.js.map +1 -1
  44. package/dist/core/delegation/worker-agent-control.d.ts +45 -6
  45. package/dist/core/delegation/worker-agent-control.d.ts.map +1 -1
  46. package/dist/core/delegation/worker-agent-control.js +110 -13
  47. package/dist/core/delegation/worker-agent-control.js.map +1 -1
  48. package/dist/core/delegation/worker-attempt-executor.d.ts.map +1 -1
  49. package/dist/core/delegation/worker-attempt-executor.js +177 -158
  50. package/dist/core/delegation/worker-attempt-executor.js.map +1 -1
  51. package/dist/core/delegation/worker-authority-resolver.d.ts +23 -0
  52. package/dist/core/delegation/worker-authority-resolver.d.ts.map +1 -0
  53. package/dist/core/delegation/worker-authority-resolver.js +128 -0
  54. package/dist/core/delegation/worker-authority-resolver.js.map +1 -0
  55. package/dist/core/delegation/worker-conversation-store.d.ts.map +1 -1
  56. package/dist/core/delegation/worker-conversation-store.js +1 -1
  57. package/dist/core/delegation/worker-conversation-store.js.map +1 -1
  58. package/dist/core/delegation/worker-delegation-controller.d.ts +7 -1
  59. package/dist/core/delegation/worker-delegation-controller.d.ts.map +1 -1
  60. package/dist/core/delegation/worker-delegation-controller.js +260 -87
  61. package/dist/core/delegation/worker-delegation-controller.js.map +1 -1
  62. package/dist/core/delegation/worker-delegation-request.d.ts +25 -4
  63. package/dist/core/delegation/worker-delegation-request.d.ts.map +1 -1
  64. package/dist/core/delegation/worker-delegation-request.js +90 -1
  65. package/dist/core/delegation/worker-delegation-request.js.map +1 -1
  66. package/dist/core/delegation/worker-execution-policy.d.ts +2 -0
  67. package/dist/core/delegation/worker-execution-policy.d.ts.map +1 -1
  68. package/dist/core/delegation/worker-execution-policy.js +22 -12
  69. package/dist/core/delegation/worker-execution-policy.js.map +1 -1
  70. package/dist/core/delegation/worker-lifecycle.d.ts +1 -0
  71. package/dist/core/delegation/worker-lifecycle.d.ts.map +1 -1
  72. package/dist/core/delegation/worker-lifecycle.js +4 -2
  73. package/dist/core/delegation/worker-lifecycle.js.map +1 -1
  74. package/dist/core/delegation/worker-profile-resolver.d.ts +0 -2
  75. package/dist/core/delegation/worker-profile-resolver.d.ts.map +1 -1
  76. package/dist/core/delegation/worker-profile-resolver.js +8 -35
  77. package/dist/core/delegation/worker-profile-resolver.js.map +1 -1
  78. package/dist/core/delegation/worker-recovery-coordinator.d.ts.map +1 -1
  79. package/dist/core/delegation/worker-recovery-coordinator.js +1 -0
  80. package/dist/core/delegation/worker-recovery-coordinator.js.map +1 -1
  81. package/dist/core/delegation/worker-runner.d.ts +6 -1
  82. package/dist/core/delegation/worker-runner.d.ts.map +1 -1
  83. package/dist/core/delegation/worker-runner.js +20 -6
  84. package/dist/core/delegation/worker-runner.js.map +1 -1
  85. package/dist/core/delegation/worker-tree-budget-coordinator.d.ts +47 -0
  86. package/dist/core/delegation/worker-tree-budget-coordinator.d.ts.map +1 -0
  87. package/dist/core/delegation/worker-tree-budget-coordinator.js +210 -0
  88. package/dist/core/delegation/worker-tree-budget-coordinator.js.map +1 -0
  89. package/dist/core/memory/providers/file-store.d.ts +2 -0
  90. package/dist/core/memory/providers/file-store.d.ts.map +1 -1
  91. package/dist/core/memory/providers/file-store.js +22 -20
  92. package/dist/core/memory/providers/file-store.js.map +1 -1
  93. package/dist/core/models/runtime-process.d.ts.map +1 -1
  94. package/dist/core/models/runtime-process.js +5 -18
  95. package/dist/core/models/runtime-process.js.map +1 -1
  96. package/dist/core/orchestration/bounded-string-array.d.ts +11 -0
  97. package/dist/core/orchestration/bounded-string-array.d.ts.map +1 -0
  98. package/dist/core/orchestration/bounded-string-array.js +17 -0
  99. package/dist/core/orchestration/bounded-string-array.js.map +1 -0
  100. package/dist/core/orchestration/capability-gateway.d.ts +16 -0
  101. package/dist/core/orchestration/capability-gateway.d.ts.map +1 -1
  102. package/dist/core/orchestration/capability-gateway.js +38 -0
  103. package/dist/core/orchestration/capability-gateway.js.map +1 -1
  104. package/dist/core/orchestration/contracts.d.ts +11 -3
  105. package/dist/core/orchestration/contracts.d.ts.map +1 -1
  106. package/dist/core/orchestration/contracts.js +0 -1
  107. package/dist/core/orchestration/contracts.js.map +1 -1
  108. package/dist/core/orchestration/delegation-ledger.d.ts +1 -0
  109. package/dist/core/orchestration/delegation-ledger.d.ts.map +1 -1
  110. package/dist/core/orchestration/delegation-ledger.js +3 -0
  111. package/dist/core/orchestration/delegation-ledger.js.map +1 -1
  112. package/dist/core/orchestration/event-store.d.ts +12 -10
  113. package/dist/core/orchestration/event-store.d.ts.map +1 -1
  114. package/dist/core/orchestration/event-store.js +94 -104
  115. package/dist/core/orchestration/event-store.js.map +1 -1
  116. package/dist/core/orchestration/lane-tool-manifests.d.ts +2 -2
  117. package/dist/core/orchestration/lane-tool-manifests.d.ts.map +1 -1
  118. package/dist/core/orchestration/lane-tool-manifests.js +4 -1
  119. package/dist/core/orchestration/lane-tool-manifests.js.map +1 -1
  120. package/dist/core/orchestration/policy-compiler.d.ts.map +1 -1
  121. package/dist/core/orchestration/policy-compiler.js +8 -9
  122. package/dist/core/orchestration/policy-compiler.js.map +1 -1
  123. package/dist/core/orchestration/profile-registry.d.ts.map +1 -1
  124. package/dist/core/orchestration/profile-registry.js +4 -10
  125. package/dist/core/orchestration/profile-registry.js.map +1 -1
  126. package/dist/core/orchestration/task-runtime.d.ts +1 -0
  127. package/dist/core/orchestration/task-runtime.d.ts.map +1 -1
  128. package/dist/core/orchestration/task-runtime.js +58 -3
  129. package/dist/core/orchestration/task-runtime.js.map +1 -1
  130. package/dist/core/orchestration/worker-execution-contract.d.ts.map +1 -1
  131. package/dist/core/orchestration/worker-execution-contract.js +10 -17
  132. package/dist/core/orchestration/worker-execution-contract.js.map +1 -1
  133. package/dist/core/risk-classifier.js +3 -3
  134. package/dist/core/risk-classifier.js.map +1 -1
  135. package/dist/core/runtime-builder.d.ts +4 -1
  136. package/dist/core/runtime-builder.d.ts.map +1 -1
  137. package/dist/core/runtime-builder.js +34 -15
  138. package/dist/core/runtime-builder.js.map +1 -1
  139. package/dist/core/secrets/bitwarden-credential-storage.d.ts +51 -0
  140. package/dist/core/secrets/bitwarden-credential-storage.d.ts.map +1 -0
  141. package/dist/core/secrets/bitwarden-credential-storage.js +345 -0
  142. package/dist/core/secrets/bitwarden-credential-storage.js.map +1 -0
  143. package/dist/core/secrets/credential-exposure-guard.d.ts +8 -4
  144. package/dist/core/secrets/credential-exposure-guard.d.ts.map +1 -1
  145. package/dist/core/secrets/credential-exposure-guard.js +30 -31
  146. package/dist/core/secrets/credential-exposure-guard.js.map +1 -1
  147. package/dist/core/secrets/credential-manager.d.ts +109 -0
  148. package/dist/core/secrets/credential-manager.d.ts.map +1 -0
  149. package/dist/core/secrets/credential-manager.js +384 -0
  150. package/dist/core/secrets/credential-manager.js.map +1 -0
  151. package/dist/core/secrets/credential-project.d.ts +4 -0
  152. package/dist/core/secrets/credential-project.d.ts.map +1 -0
  153. package/dist/core/secrets/credential-project.js +145 -0
  154. package/dist/core/secrets/credential-project.js.map +1 -0
  155. package/dist/core/secrets/secret-dotenv.d.ts +1 -1
  156. package/dist/core/secrets/secret-dotenv.js +1 -1
  157. package/dist/core/secrets/secret-dotenv.js.map +1 -1
  158. package/dist/core/settings-manager.d.ts +4 -2
  159. package/dist/core/settings-manager.d.ts.map +1 -1
  160. package/dist/core/settings-manager.js +9 -5
  161. package/dist/core/settings-manager.js.map +1 -1
  162. package/dist/core/slash-commands.d.ts.map +1 -1
  163. package/dist/core/slash-commands.js +1 -0
  164. package/dist/core/slash-commands.js.map +1 -1
  165. package/dist/core/system-prompt.js +1 -1
  166. package/dist/core/system-prompt.js.map +1 -1
  167. package/dist/core/tool-capability-policy.js +2 -2
  168. package/dist/core/tool-capability-policy.js.map +1 -1
  169. package/dist/core/tools/delegate.d.ts +29 -1
  170. package/dist/core/tools/delegate.d.ts.map +1 -1
  171. package/dist/core/tools/delegate.js +179 -28
  172. package/dist/core/tools/delegate.js.map +1 -1
  173. package/dist/core/tools/git-filter.d.ts +1 -0
  174. package/dist/core/tools/git-filter.d.ts.map +1 -1
  175. package/dist/core/tools/git-filter.js +1 -1
  176. package/dist/core/tools/git-filter.js.map +1 -1
  177. package/dist/core/tools/profile-writer.js +3 -3
  178. package/dist/core/tools/profile-writer.js.map +1 -1
  179. package/dist/core/tools/python.d.ts +2 -0
  180. package/dist/core/tools/python.d.ts.map +1 -1
  181. package/dist/core/tools/python.js +17 -12
  182. package/dist/core/tools/python.js.map +1 -1
  183. package/dist/core/tools/run-process.d.ts +2 -0
  184. package/dist/core/tools/run-process.d.ts.map +1 -1
  185. package/dist/core/tools/run-process.js +7 -3
  186. package/dist/core/tools/run-process.js.map +1 -1
  187. package/dist/core/tools/secret-store.d.ts +8 -18
  188. package/dist/core/tools/secret-store.d.ts.map +1 -1
  189. package/dist/core/tools/secret-store.js +83 -348
  190. package/dist/core/tools/secret-store.js.map +1 -1
  191. package/dist/index.d.ts +3 -1
  192. package/dist/index.d.ts.map +1 -1
  193. package/dist/index.js +3 -1
  194. package/dist/index.js.map +1 -1
  195. package/dist/migrations.d.ts.map +1 -1
  196. package/dist/migrations.js +38 -1
  197. package/dist/migrations.js.map +1 -1
  198. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  199. package/dist/modes/interactive/components/settings-selector.js +35 -14
  200. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  201. package/dist/modes/interactive/interactive-mode.d.ts +1 -0
  202. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  203. package/dist/modes/interactive/interactive-mode.js +26 -3
  204. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  205. package/dist/modes/interactive/secret-menu-controller.d.ts +21 -0
  206. package/dist/modes/interactive/secret-menu-controller.d.ts.map +1 -0
  207. package/dist/modes/interactive/secret-menu-controller.js +276 -0
  208. package/dist/modes/interactive/secret-menu-controller.js.map +1 -0
  209. package/dist/utils/tools-manager.d.ts +2 -1
  210. package/dist/utils/tools-manager.d.ts.map +1 -1
  211. package/dist/utils/tools-manager.js +43 -48
  212. package/dist/utils/tools-manager.js.map +1 -1
  213. package/dist/utils/zip-extractor.d.ts +8 -0
  214. package/dist/utils/zip-extractor.d.ts.map +1 -0
  215. package/dist/utils/zip-extractor.js +275 -0
  216. package/dist/utils/zip-extractor.js.map +1 -0
  217. package/docs/harness-architecture.md +34 -22
  218. package/docs/index.md +1 -1
  219. package/docs/resources.md +1 -1
  220. package/docs/settings.md +39 -103
  221. package/docs/usage.md +7 -5
  222. package/docs/worker-profiles.md +9 -7
  223. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  224. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  225. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  226. package/examples/extensions/sandbox/package-lock.json +2 -2
  227. package/examples/extensions/sandbox/package.json +1 -1
  228. package/examples/extensions/with-deps/package-lock.json +2 -2
  229. package/examples/extensions/with-deps/package.json +1 -1
  230. package/npm-shrinkwrap.json +12 -12
  231. package/package.json +4 -4
  232. package/dist/core/secrets/secret-vault-format.d.ts +0 -112
  233. package/dist/core/secrets/secret-vault-format.d.ts.map +0 -1
  234. package/dist/core/secrets/secret-vault-format.js +0 -396
  235. package/dist/core/secrets/secret-vault-format.js.map +0 -1
  236. package/dist/core/secrets/secret-vault.d.ts +0 -72
  237. package/dist/core/secrets/secret-vault.d.ts.map +0 -1
  238. package/dist/core/secrets/secret-vault.js +0 -663
  239. package/dist/core/secrets/secret-vault.js.map +0 -1
@@ -0,0 +1,384 @@
1
+ import { realpathSync } from "node:fs";
2
+ import { isAbsolute, relative, resolve } from "node:path";
3
+ import { parseDotenvDocument, validateDotenvValue, validateDotenvVariableName } from "./secret-dotenv.js";
4
+ const PROFILE_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
5
+ const PROJECT_KEY_PATTERN = /^(?:git|local):[a-f0-9]{64}$/;
6
+ const MAX_PROFILE_CHARS = 96;
7
+ const MAX_DESCRIPTION_CHARS = 256;
8
+ const MAX_PROJECT_BINDINGS = 64;
9
+ const MAX_SESSION_KEY_BYTES = 16 * 1024;
10
+ export class CredentialManagerError extends Error {
11
+ constructor(code, message) {
12
+ super(message);
13
+ this.name = "CredentialManagerError";
14
+ this.code = code;
15
+ }
16
+ }
17
+ export class CredentialStorageError extends Error {
18
+ constructor(code, message) {
19
+ super(message);
20
+ this.name = "CredentialStorageError";
21
+ this.code = code;
22
+ }
23
+ }
24
+ export function validateCredentialProfileName(profile) {
25
+ if (!PROFILE_PATTERN.test(profile) || profile.length > MAX_PROFILE_CHARS) {
26
+ throw new CredentialManagerError("invalid_profile", "Credential profile names must use 1-96 portable letters, digits, dots, underscores, or hyphens.");
27
+ }
28
+ return profile;
29
+ }
30
+ export function validateCredentialDescription(description) {
31
+ if (description === undefined)
32
+ return undefined;
33
+ if (description.length === 0 ||
34
+ description.length > MAX_DESCRIPTION_CHARS ||
35
+ description.includes("\0") ||
36
+ /[\r\n]/.test(description)) {
37
+ throw new CredentialManagerError("invalid_description", "Credential descriptions must be a single printable line of at most 256 characters.");
38
+ }
39
+ return description;
40
+ }
41
+ function validateProjectIdentity(identity) {
42
+ if (!PROJECT_KEY_PATTERN.test(identity.key) ||
43
+ !isAbsolute(identity.root) ||
44
+ identity.label.length === 0 ||
45
+ identity.label.length > 256 ||
46
+ /[\0\r\n]/.test(identity.label)) {
47
+ throw new CredentialManagerError("invalid_project_identity", "The current project identity is invalid.");
48
+ }
49
+ return { ...identity, root: canonicalizeExistingPath(identity.root) };
50
+ }
51
+ export function validateCredentialProfileRecord(record) {
52
+ validateCredentialProfileName(record.profile);
53
+ validateCredentialDescription(record.description);
54
+ if (record.variables.length === 0) {
55
+ throw new CredentialManagerError("invalid_provider_record", "Credential profile has no variables.");
56
+ }
57
+ const variableNames = new Set();
58
+ for (const variable of record.variables) {
59
+ try {
60
+ validateDotenvVariableName(variable.name);
61
+ validateDotenvValue(variable.value);
62
+ }
63
+ catch {
64
+ throw new CredentialManagerError("invalid_provider_record", "Credential profile contains invalid variables.");
65
+ }
66
+ if (variableNames.has(variable.name)) {
67
+ throw new CredentialManagerError("invalid_provider_record", "Credential profile contains duplicate variables.");
68
+ }
69
+ variableNames.add(variable.name);
70
+ }
71
+ if (record.projectKeys.length === 0 ||
72
+ record.projectKeys.length > MAX_PROJECT_BINDINGS ||
73
+ new Set(record.projectKeys).size !== record.projectKeys.length ||
74
+ record.projectKeys.some((key) => !PROJECT_KEY_PATTERN.test(key))) {
75
+ throw new CredentialManagerError("invalid_provider_record", "Credential profile contains invalid project bindings.");
76
+ }
77
+ return record;
78
+ }
79
+ function validateSessionKey(sessionKey) {
80
+ if (sessionKey.trim().length === 0 ||
81
+ sessionKey.includes("\0") ||
82
+ /[\r\n]/.test(sessionKey) ||
83
+ Buffer.byteLength(sessionKey, "utf8") > MAX_SESSION_KEY_BYTES) {
84
+ throw new CredentialManagerError("invalid_session_key", "The Bitwarden session key is invalid.");
85
+ }
86
+ return sessionKey;
87
+ }
88
+ function isPathInside(root, target) {
89
+ const pathFromRoot = relative(root, target);
90
+ return pathFromRoot === "" || (!pathFromRoot.startsWith("..") && !isAbsolute(pathFromRoot));
91
+ }
92
+ function canonicalizeExistingPath(path) {
93
+ const absolute = resolve(path);
94
+ try {
95
+ return realpathSync.native(absolute);
96
+ }
97
+ catch {
98
+ return absolute;
99
+ }
100
+ }
101
+ function cloneEnvironment(values) {
102
+ return Object.fromEntries(Object.entries(values));
103
+ }
104
+ export class CredentialManager {
105
+ constructor(options) {
106
+ this.activeEnvironments = new Map();
107
+ this.sensitiveValues = new Set();
108
+ this.connectionGeneration = 0;
109
+ this.connected = false;
110
+ this.storage = options.storage;
111
+ this.resolveProject = options.resolveProject;
112
+ this.onEnvironmentChanged = options.onEnvironmentChanged ?? (() => { });
113
+ this.pendingSessionKey = options.initialSessionKey ?? process.env.BW_SESSION;
114
+ this.sessionKeyForRedaction = this.pendingSessionKey;
115
+ this.rebuildSensitiveValues();
116
+ }
117
+ get status() {
118
+ return { connected: this.connected, sessionAvailable: this.connected || Boolean(this.pendingSessionKey) };
119
+ }
120
+ async connect(sessionKey, signal) {
121
+ const previousConnection = this.connectionPromise;
122
+ this.connectionGeneration++;
123
+ let privateSessionKey = validateSessionKey(sessionKey);
124
+ this.pendingSessionKey = privateSessionKey;
125
+ this.sessionKeyForRedaction = privateSessionKey;
126
+ this.rebuildSensitiveValues();
127
+ this.connected = false;
128
+ try {
129
+ if (previousConnection) {
130
+ try {
131
+ await previousConnection;
132
+ }
133
+ catch {
134
+ // The superseded connection is expected to fail its generation check.
135
+ }
136
+ }
137
+ await this.ensureConnected(signal);
138
+ }
139
+ finally {
140
+ privateSessionKey = "";
141
+ }
142
+ }
143
+ async listForProject(cwd, signal) {
144
+ const { identity, summaries } = await this.refreshProjectProfiles(cwd, signal);
145
+ return summaries.map((summary) => ({
146
+ profile: validateCredentialProfileName(summary.profile),
147
+ ...(summary.description ? { description: validateCredentialDescription(summary.description) } : {}),
148
+ variableNames: summary.variableNames.map(validateDotenvVariableName),
149
+ boundToCurrentProject: summary.projectKeys.includes(identity.key),
150
+ }));
151
+ }
152
+ async activateForProject(cwd, profile, signal) {
153
+ const { generation, identity, summaries } = await this.refreshProjectProfiles(cwd, signal);
154
+ let selected;
155
+ if (profile !== undefined) {
156
+ validateCredentialProfileName(profile);
157
+ selected = summaries.find((summary) => summary.profile === profile);
158
+ if (!selected)
159
+ throw new CredentialManagerError("profile_not_found", "Credential profile was not found.");
160
+ if (!selected.projectKeys.includes(identity.key)) {
161
+ throw new CredentialManagerError("project_not_bound", "Credential profile is not authorized for the current project.");
162
+ }
163
+ }
164
+ else {
165
+ const candidates = summaries.filter((summary) => summary.projectKeys.includes(identity.key));
166
+ if (candidates.length === 0) {
167
+ throw new CredentialManagerError("profile_not_found", "No credential profile is bound to the current project. The owner can configure one with /secrets.");
168
+ }
169
+ if (candidates.length > 1) {
170
+ throw new CredentialManagerError("ambiguous_project_binding", "Multiple credential profiles are bound to the current project; select a profile by name.");
171
+ }
172
+ [selected] = candidates;
173
+ }
174
+ if (!selected)
175
+ throw new CredentialManagerError("profile_not_found", "Credential profile was not found.");
176
+ const record = validateCredentialProfileRecord(await this.storage.readProfile(selected.profile, signal));
177
+ this.assertAccessGeneration(generation);
178
+ if (!record.projectKeys.includes(identity.key)) {
179
+ throw new CredentialManagerError("project_not_bound", "Credential profile is not authorized for the current project.");
180
+ }
181
+ this.activate(identity.root, record);
182
+ return {
183
+ status: "activated",
184
+ profile: record.profile,
185
+ variableNames: record.variables.map((variable) => variable.name),
186
+ project: identity.label,
187
+ };
188
+ }
189
+ async storeForProject(cwd, input, signal) {
190
+ await this.ensureConnected(signal);
191
+ const generation = this.connectionGeneration;
192
+ this.assertAccessGeneration(generation);
193
+ const profile = validateCredentialProfileName(input.profile);
194
+ const description = validateCredentialDescription(input.description);
195
+ const identity = validateProjectIdentity(await this.resolveProject(cwd));
196
+ this.assertAccessGeneration(generation);
197
+ const parsed = parseDotenvDocument(input.dotenv);
198
+ const summaries = await this.storage.listProfiles(signal);
199
+ this.assertAccessGeneration(generation);
200
+ const existing = summaries.find((summary) => summary.profile === profile);
201
+ const existingRecord = existing
202
+ ? validateCredentialProfileRecord(await this.storage.readProfile(profile, signal))
203
+ : undefined;
204
+ this.assertAccessGeneration(generation);
205
+ const projectKeys = existingRecord ? [...new Set([...existingRecord.projectKeys, identity.key])] : [identity.key];
206
+ const record = {
207
+ profile,
208
+ ...((description ?? existingRecord?.description)
209
+ ? { description: description ?? existingRecord?.description }
210
+ : {}),
211
+ variables: parsed.variables.map((variable) => ({ name: variable.name, value: variable.value })),
212
+ projectKeys,
213
+ };
214
+ validateCredentialProfileRecord(record);
215
+ await this.storage.writeProfile(record, signal);
216
+ this.assertAccessGeneration(generation);
217
+ this.activate(identity.root, record);
218
+ return {
219
+ status: "stored",
220
+ profile,
221
+ variableNames: record.variables.map((variable) => variable.name),
222
+ project: identity.label,
223
+ portable: identity.portable,
224
+ };
225
+ }
226
+ async bindProfileToProject(cwd, profile, signal) {
227
+ await this.ensureConnected(signal);
228
+ const generation = this.connectionGeneration;
229
+ this.assertAccessGeneration(generation);
230
+ validateCredentialProfileName(profile);
231
+ const identity = validateProjectIdentity(await this.resolveProject(cwd));
232
+ this.assertAccessGeneration(generation);
233
+ const summaries = await this.storage.listProfiles(signal);
234
+ this.assertAccessGeneration(generation);
235
+ if (!summaries.some((summary) => summary.profile === profile)) {
236
+ throw new CredentialManagerError("profile_not_found", "Credential profile was not found.");
237
+ }
238
+ const record = validateCredentialProfileRecord(await this.storage.readProfile(profile, signal));
239
+ this.assertAccessGeneration(generation);
240
+ if (!record.projectKeys.includes(identity.key))
241
+ record.projectKeys.push(identity.key);
242
+ validateCredentialProfileRecord(record);
243
+ await this.storage.writeProfile(record, signal);
244
+ this.assertAccessGeneration(generation);
245
+ return {
246
+ status: "bound",
247
+ profile,
248
+ variableNames: record.variables.map((variable) => variable.name),
249
+ project: identity.label,
250
+ portable: identity.portable,
251
+ };
252
+ }
253
+ async removeProfile(profile, signal) {
254
+ await this.ensureConnected(signal);
255
+ const generation = this.connectionGeneration;
256
+ this.assertAccessGeneration(generation);
257
+ validateCredentialProfileName(profile);
258
+ const summaries = await this.storage.listProfiles(signal);
259
+ this.assertAccessGeneration(generation);
260
+ const summary = summaries.find((candidate) => candidate.profile === profile);
261
+ if (!summary)
262
+ throw new CredentialManagerError("profile_not_found", "Credential profile was not found.");
263
+ await this.storage.deleteProfile(profile, signal);
264
+ this.assertAccessGeneration(generation);
265
+ let removedActiveEnvironment = false;
266
+ for (const [root, active] of this.activeEnvironments) {
267
+ if (active.profile === profile) {
268
+ this.activeEnvironments.delete(root);
269
+ removedActiveEnvironment = true;
270
+ }
271
+ }
272
+ this.rebuildSensitiveValues();
273
+ if (removedActiveEnvironment)
274
+ this.onEnvironmentChanged();
275
+ return { status: "removed", profile, variableNames: [...summary.variableNames] };
276
+ }
277
+ getEnvironmentForCwd(cwd) {
278
+ const target = resolve(cwd);
279
+ const direct = this.findActiveEnvironment(target);
280
+ const selected = direct ?? this.findActiveEnvironment(canonicalizeExistingPath(target));
281
+ return selected ? cloneEnvironment(selected.values) : undefined;
282
+ }
283
+ findActiveEnvironment(target) {
284
+ let selected;
285
+ for (const active of this.activeEnvironments.values()) {
286
+ if (!isPathInside(active.root, target))
287
+ continue;
288
+ if (!selected || active.root.length > selected.root.length)
289
+ selected = active;
290
+ }
291
+ return selected;
292
+ }
293
+ hasEnvironmentForCwd(cwd) {
294
+ return this.getEnvironmentForCwd(cwd) !== undefined;
295
+ }
296
+ redactSensitiveText(text) {
297
+ let redacted = text;
298
+ for (const value of [...this.sensitiveValues].sort((left, right) => right.length - left.length)) {
299
+ if (value.length > 0)
300
+ redacted = redacted.split(value).join("[REDACTED_SECRET]");
301
+ }
302
+ return redacted;
303
+ }
304
+ lock() {
305
+ const hadActiveEnvironment = this.activeEnvironments.size > 0;
306
+ this.connectionGeneration++;
307
+ this.storage.lock();
308
+ this.connected = false;
309
+ this.pendingSessionKey = undefined;
310
+ this.sessionKeyForRedaction = undefined;
311
+ this.connectionPromise = undefined;
312
+ this.activeEnvironments.clear();
313
+ this.sensitiveValues.clear();
314
+ if (hadActiveEnvironment)
315
+ this.onEnvironmentChanged();
316
+ }
317
+ async ensureConnected(signal) {
318
+ if (this.connected)
319
+ return;
320
+ if (this.connectionPromise)
321
+ return this.connectionPromise;
322
+ let sessionKey = this.pendingSessionKey;
323
+ if (!sessionKey) {
324
+ throw new CredentialManagerError("owner_setup_required", "Bitwarden is not connected. The owner can provide a session key with /secrets.");
325
+ }
326
+ const generation = this.connectionGeneration;
327
+ const connectionPromise = (async () => {
328
+ try {
329
+ await this.storage.connect(sessionKey, signal);
330
+ if (generation !== this.connectionGeneration) {
331
+ this.storage.lock();
332
+ throw new CredentialManagerError("owner_setup_required", "Bitwarden connection was cancelled when the owner locked credentials.");
333
+ }
334
+ this.connected = true;
335
+ }
336
+ finally {
337
+ if (generation === this.connectionGeneration)
338
+ this.pendingSessionKey = undefined;
339
+ sessionKey = "";
340
+ }
341
+ })();
342
+ this.connectionPromise = connectionPromise;
343
+ try {
344
+ await connectionPromise;
345
+ }
346
+ finally {
347
+ if (this.connectionPromise === connectionPromise)
348
+ this.connectionPromise = undefined;
349
+ }
350
+ }
351
+ assertAccessGeneration(generation) {
352
+ if (generation === this.connectionGeneration && this.connected)
353
+ return;
354
+ throw new CredentialManagerError("owner_setup_required", "Bitwarden credential access was cancelled when the owner locked credentials.");
355
+ }
356
+ async refreshProjectProfiles(cwd, signal) {
357
+ await this.ensureConnected(signal);
358
+ const generation = this.connectionGeneration;
359
+ this.assertAccessGeneration(generation);
360
+ await this.storage.refresh?.(signal);
361
+ this.assertAccessGeneration(generation);
362
+ const identity = validateProjectIdentity(await this.resolveProject(cwd));
363
+ this.assertAccessGeneration(generation);
364
+ const summaries = await this.storage.listProfiles(signal);
365
+ this.assertAccessGeneration(generation);
366
+ return { generation, identity, summaries };
367
+ }
368
+ activate(root, record) {
369
+ const values = Object.fromEntries(record.variables.map((variable) => [variable.name, variable.value]));
370
+ this.activeEnvironments.set(resolve(root), { profile: record.profile, root: resolve(root), values });
371
+ this.rebuildSensitiveValues();
372
+ this.onEnvironmentChanged();
373
+ }
374
+ rebuildSensitiveValues() {
375
+ this.sensitiveValues.clear();
376
+ if (this.sessionKeyForRedaction)
377
+ this.sensitiveValues.add(this.sessionKeyForRedaction);
378
+ for (const active of this.activeEnvironments.values()) {
379
+ for (const value of Object.values(active.values))
380
+ this.sensitiveValues.add(value);
381
+ }
382
+ }
383
+ }
384
+ //# sourceMappingURL=credential-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credential-manager.js","sourceRoot":"","sources":["../../../src/core/secrets/credential-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import { realpathSync } from \"node:fs\";\nimport { isAbsolute, relative, resolve } from \"node:path\";\nimport { parseDotenvDocument, validateDotenvValue, validateDotenvVariableName } from \"./secret-dotenv.ts\";\n\nconst PROFILE_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;\nconst PROJECT_KEY_PATTERN = /^(?:git|local):[a-f0-9]{64}$/;\nconst MAX_PROFILE_CHARS = 96;\nconst MAX_DESCRIPTION_CHARS = 256;\nconst MAX_PROJECT_BINDINGS = 64;\nconst MAX_SESSION_KEY_BYTES = 16 * 1024;\n\nexport type CredentialManagerErrorCode =\n\t| \"ambiguous_project_binding\"\n\t| \"invalid_description\"\n\t| \"invalid_profile\"\n\t| \"invalid_project_identity\"\n\t| \"invalid_provider_record\"\n\t| \"invalid_session_key\"\n\t| \"owner_setup_required\"\n\t| \"profile_not_found\"\n\t| \"project_not_bound\";\n\nexport class CredentialManagerError extends Error {\n\treadonly code: CredentialManagerErrorCode;\n\n\tconstructor(code: CredentialManagerErrorCode, message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"CredentialManagerError\";\n\t\tthis.code = code;\n\t}\n}\n\nexport type CredentialStorageErrorCode =\n\t| \"malformed_provider_data\"\n\t| \"not_connected\"\n\t| \"profile_not_found\"\n\t| \"provider_command_failed\"\n\t| \"provider_unavailable\";\n\nexport class CredentialStorageError extends Error {\n\treadonly code: CredentialStorageErrorCode;\n\n\tconstructor(code: CredentialStorageErrorCode, message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"CredentialStorageError\";\n\t\tthis.code = code;\n\t}\n}\n\nexport interface CredentialProjectIdentity {\n\tkey: string;\n\troot: string;\n\tlabel: string;\n\tportable: boolean;\n}\n\nexport type CredentialProjectResolver = (cwd: string) => Promise<CredentialProjectIdentity>;\n\nexport interface CredentialVariable {\n\tname: string;\n\tvalue: string;\n}\n\nexport interface CredentialProfileRecord {\n\tprofile: string;\n\tdescription?: string;\n\tvariables: CredentialVariable[];\n\tprojectKeys: string[];\n}\n\nexport interface StoredCredentialProfileSummary {\n\tprofile: string;\n\tdescription?: string;\n\tvariableNames: string[];\n\tprojectKeys: string[];\n}\n\nexport interface CredentialProfileStorage {\n\tconnect(sessionKey: string, signal?: AbortSignal): Promise<void>;\n\trefresh?(signal?: AbortSignal): Promise<void>;\n\tlistProfiles(signal?: AbortSignal): Promise<StoredCredentialProfileSummary[]>;\n\treadProfile(profile: string, signal?: AbortSignal): Promise<CredentialProfileRecord>;\n\twriteProfile(record: CredentialProfileRecord, signal?: AbortSignal): Promise<void>;\n\tdeleteProfile(profile: string, signal?: AbortSignal): Promise<void>;\n\tlock(): void;\n}\n\nexport interface CredentialProfileSummary {\n\tprofile: string;\n\tdescription?: string;\n\tvariableNames: string[];\n\tboundToCurrentProject: boolean;\n}\n\nexport interface CredentialActivationResult {\n\tstatus: \"activated\";\n\tprofile: string;\n\tvariableNames: string[];\n\tproject: string;\n}\n\nexport interface CredentialMutationResult {\n\tstatus: \"bound\" | \"removed\" | \"stored\";\n\tprofile: string;\n\tvariableNames: string[];\n\tproject?: string;\n\tportable?: boolean;\n}\n\nexport interface CredentialConnectionStatus {\n\tconnected: boolean;\n\tsessionAvailable: boolean;\n}\n\nexport interface CredentialManagerOptions {\n\tstorage: CredentialProfileStorage;\n\tresolveProject: CredentialProjectResolver;\n\tinitialSessionKey?: string;\n\tonEnvironmentChanged?: () => void;\n}\n\ninterface ActiveCredentialEnvironment {\n\tprofile: string;\n\troot: string;\n\tvalues: Record<string, string>;\n}\n\nexport function validateCredentialProfileName(profile: string): string {\n\tif (!PROFILE_PATTERN.test(profile) || profile.length > MAX_PROFILE_CHARS) {\n\t\tthrow new CredentialManagerError(\n\t\t\t\"invalid_profile\",\n\t\t\t\"Credential profile names must use 1-96 portable letters, digits, dots, underscores, or hyphens.\",\n\t\t);\n\t}\n\treturn profile;\n}\n\nexport function validateCredentialDescription(description: string | undefined): string | undefined {\n\tif (description === undefined) return undefined;\n\tif (\n\t\tdescription.length === 0 ||\n\t\tdescription.length > MAX_DESCRIPTION_CHARS ||\n\t\tdescription.includes(\"\\0\") ||\n\t\t/[\\r\\n]/.test(description)\n\t) {\n\t\tthrow new CredentialManagerError(\n\t\t\t\"invalid_description\",\n\t\t\t\"Credential descriptions must be a single printable line of at most 256 characters.\",\n\t\t);\n\t}\n\treturn description;\n}\n\nfunction validateProjectIdentity(identity: CredentialProjectIdentity): CredentialProjectIdentity {\n\tif (\n\t\t!PROJECT_KEY_PATTERN.test(identity.key) ||\n\t\t!isAbsolute(identity.root) ||\n\t\tidentity.label.length === 0 ||\n\t\tidentity.label.length > 256 ||\n\t\t/[\\0\\r\\n]/.test(identity.label)\n\t) {\n\t\tthrow new CredentialManagerError(\"invalid_project_identity\", \"The current project identity is invalid.\");\n\t}\n\treturn { ...identity, root: canonicalizeExistingPath(identity.root) };\n}\n\nexport function validateCredentialProfileRecord(record: CredentialProfileRecord): CredentialProfileRecord {\n\tvalidateCredentialProfileName(record.profile);\n\tvalidateCredentialDescription(record.description);\n\tif (record.variables.length === 0) {\n\t\tthrow new CredentialManagerError(\"invalid_provider_record\", \"Credential profile has no variables.\");\n\t}\n\tconst variableNames = new Set<string>();\n\tfor (const variable of record.variables) {\n\t\ttry {\n\t\t\tvalidateDotenvVariableName(variable.name);\n\t\t\tvalidateDotenvValue(variable.value);\n\t\t} catch {\n\t\t\tthrow new CredentialManagerError(\"invalid_provider_record\", \"Credential profile contains invalid variables.\");\n\t\t}\n\t\tif (variableNames.has(variable.name)) {\n\t\t\tthrow new CredentialManagerError(\n\t\t\t\t\"invalid_provider_record\",\n\t\t\t\t\"Credential profile contains duplicate variables.\",\n\t\t\t);\n\t\t}\n\t\tvariableNames.add(variable.name);\n\t}\n\tif (\n\t\trecord.projectKeys.length === 0 ||\n\t\trecord.projectKeys.length > MAX_PROJECT_BINDINGS ||\n\t\tnew Set(record.projectKeys).size !== record.projectKeys.length ||\n\t\trecord.projectKeys.some((key) => !PROJECT_KEY_PATTERN.test(key))\n\t) {\n\t\tthrow new CredentialManagerError(\n\t\t\t\"invalid_provider_record\",\n\t\t\t\"Credential profile contains invalid project bindings.\",\n\t\t);\n\t}\n\treturn record;\n}\n\nfunction validateSessionKey(sessionKey: string): string {\n\tif (\n\t\tsessionKey.trim().length === 0 ||\n\t\tsessionKey.includes(\"\\0\") ||\n\t\t/[\\r\\n]/.test(sessionKey) ||\n\t\tBuffer.byteLength(sessionKey, \"utf8\") > MAX_SESSION_KEY_BYTES\n\t) {\n\t\tthrow new CredentialManagerError(\"invalid_session_key\", \"The Bitwarden session key is invalid.\");\n\t}\n\treturn sessionKey;\n}\n\nfunction isPathInside(root: string, target: string): boolean {\n\tconst pathFromRoot = relative(root, target);\n\treturn pathFromRoot === \"\" || (!pathFromRoot.startsWith(\"..\") && !isAbsolute(pathFromRoot));\n}\n\nfunction canonicalizeExistingPath(path: string): string {\n\tconst absolute = resolve(path);\n\ttry {\n\t\treturn realpathSync.native(absolute);\n\t} catch {\n\t\treturn absolute;\n\t}\n}\n\nfunction cloneEnvironment(values: Record<string, string>): Record<string, string> {\n\treturn Object.fromEntries(Object.entries(values));\n}\n\nexport class CredentialManager {\n\tprivate readonly storage: CredentialProfileStorage;\n\tprivate readonly resolveProject: CredentialProjectResolver;\n\tprivate readonly onEnvironmentChanged: () => void;\n\tprivate readonly activeEnvironments = new Map<string, ActiveCredentialEnvironment>();\n\tprivate readonly sensitiveValues = new Set<string>();\n\tprivate pendingSessionKey: string | undefined;\n\tprivate sessionKeyForRedaction: string | undefined;\n\tprivate connectionPromise: Promise<void> | undefined;\n\tprivate connectionGeneration = 0;\n\tprivate connected = false;\n\n\tconstructor(options: CredentialManagerOptions) {\n\t\tthis.storage = options.storage;\n\t\tthis.resolveProject = options.resolveProject;\n\t\tthis.onEnvironmentChanged = options.onEnvironmentChanged ?? (() => {});\n\t\tthis.pendingSessionKey = options.initialSessionKey ?? process.env.BW_SESSION;\n\t\tthis.sessionKeyForRedaction = this.pendingSessionKey;\n\t\tthis.rebuildSensitiveValues();\n\t}\n\n\tget status(): CredentialConnectionStatus {\n\t\treturn { connected: this.connected, sessionAvailable: this.connected || Boolean(this.pendingSessionKey) };\n\t}\n\n\tasync connect(sessionKey: string, signal?: AbortSignal): Promise<void> {\n\t\tconst previousConnection = this.connectionPromise;\n\t\tthis.connectionGeneration++;\n\t\tlet privateSessionKey = validateSessionKey(sessionKey);\n\t\tthis.pendingSessionKey = privateSessionKey;\n\t\tthis.sessionKeyForRedaction = privateSessionKey;\n\t\tthis.rebuildSensitiveValues();\n\t\tthis.connected = false;\n\t\ttry {\n\t\t\tif (previousConnection) {\n\t\t\t\ttry {\n\t\t\t\t\tawait previousConnection;\n\t\t\t\t} catch {\n\t\t\t\t\t// The superseded connection is expected to fail its generation check.\n\t\t\t\t}\n\t\t\t}\n\t\t\tawait this.ensureConnected(signal);\n\t\t} finally {\n\t\t\tprivateSessionKey = \"\";\n\t\t}\n\t}\n\n\tasync listForProject(cwd: string, signal?: AbortSignal): Promise<CredentialProfileSummary[]> {\n\t\tconst { identity, summaries } = await this.refreshProjectProfiles(cwd, signal);\n\t\treturn summaries.map((summary) => ({\n\t\t\tprofile: validateCredentialProfileName(summary.profile),\n\t\t\t...(summary.description ? { description: validateCredentialDescription(summary.description) } : {}),\n\t\t\tvariableNames: summary.variableNames.map(validateDotenvVariableName),\n\t\t\tboundToCurrentProject: summary.projectKeys.includes(identity.key),\n\t\t}));\n\t}\n\n\tasync activateForProject(cwd: string, profile?: string, signal?: AbortSignal): Promise<CredentialActivationResult> {\n\t\tconst { generation, identity, summaries } = await this.refreshProjectProfiles(cwd, signal);\n\t\tlet selected: StoredCredentialProfileSummary | undefined;\n\t\tif (profile !== undefined) {\n\t\t\tvalidateCredentialProfileName(profile);\n\t\t\tselected = summaries.find((summary) => summary.profile === profile);\n\t\t\tif (!selected) throw new CredentialManagerError(\"profile_not_found\", \"Credential profile was not found.\");\n\t\t\tif (!selected.projectKeys.includes(identity.key)) {\n\t\t\t\tthrow new CredentialManagerError(\n\t\t\t\t\t\"project_not_bound\",\n\t\t\t\t\t\"Credential profile is not authorized for the current project.\",\n\t\t\t\t);\n\t\t\t}\n\t\t} else {\n\t\t\tconst candidates = summaries.filter((summary) => summary.projectKeys.includes(identity.key));\n\t\t\tif (candidates.length === 0) {\n\t\t\t\tthrow new CredentialManagerError(\n\t\t\t\t\t\"profile_not_found\",\n\t\t\t\t\t\"No credential profile is bound to the current project. The owner can configure one with /secrets.\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (candidates.length > 1) {\n\t\t\t\tthrow new CredentialManagerError(\n\t\t\t\t\t\"ambiguous_project_binding\",\n\t\t\t\t\t\"Multiple credential profiles are bound to the current project; select a profile by name.\",\n\t\t\t\t);\n\t\t\t}\n\t\t\t[selected] = candidates;\n\t\t}\n\n\t\tif (!selected) throw new CredentialManagerError(\"profile_not_found\", \"Credential profile was not found.\");\n\t\tconst record = validateCredentialProfileRecord(await this.storage.readProfile(selected.profile, signal));\n\t\tthis.assertAccessGeneration(generation);\n\t\tif (!record.projectKeys.includes(identity.key)) {\n\t\t\tthrow new CredentialManagerError(\n\t\t\t\t\"project_not_bound\",\n\t\t\t\t\"Credential profile is not authorized for the current project.\",\n\t\t\t);\n\t\t}\n\t\tthis.activate(identity.root, record);\n\t\treturn {\n\t\t\tstatus: \"activated\",\n\t\t\tprofile: record.profile,\n\t\t\tvariableNames: record.variables.map((variable) => variable.name),\n\t\t\tproject: identity.label,\n\t\t};\n\t}\n\n\tasync storeForProject(\n\t\tcwd: string,\n\t\tinput: { profile: string; description?: string; dotenv: string },\n\t\tsignal?: AbortSignal,\n\t): Promise<CredentialMutationResult> {\n\t\tawait this.ensureConnected(signal);\n\t\tconst generation = this.connectionGeneration;\n\t\tthis.assertAccessGeneration(generation);\n\t\tconst profile = validateCredentialProfileName(input.profile);\n\t\tconst description = validateCredentialDescription(input.description);\n\t\tconst identity = validateProjectIdentity(await this.resolveProject(cwd));\n\t\tthis.assertAccessGeneration(generation);\n\t\tconst parsed = parseDotenvDocument(input.dotenv);\n\t\tconst summaries = await this.storage.listProfiles(signal);\n\t\tthis.assertAccessGeneration(generation);\n\t\tconst existing = summaries.find((summary) => summary.profile === profile);\n\t\tconst existingRecord = existing\n\t\t\t? validateCredentialProfileRecord(await this.storage.readProfile(profile, signal))\n\t\t\t: undefined;\n\t\tthis.assertAccessGeneration(generation);\n\t\tconst projectKeys = existingRecord ? [...new Set([...existingRecord.projectKeys, identity.key])] : [identity.key];\n\t\tconst record: CredentialProfileRecord = {\n\t\t\tprofile,\n\t\t\t...((description ?? existingRecord?.description)\n\t\t\t\t? { description: description ?? existingRecord?.description }\n\t\t\t\t: {}),\n\t\t\tvariables: parsed.variables.map((variable) => ({ name: variable.name, value: variable.value })),\n\t\t\tprojectKeys,\n\t\t};\n\t\tvalidateCredentialProfileRecord(record);\n\t\tawait this.storage.writeProfile(record, signal);\n\t\tthis.assertAccessGeneration(generation);\n\t\tthis.activate(identity.root, record);\n\t\treturn {\n\t\t\tstatus: \"stored\",\n\t\t\tprofile,\n\t\t\tvariableNames: record.variables.map((variable) => variable.name),\n\t\t\tproject: identity.label,\n\t\t\tportable: identity.portable,\n\t\t};\n\t}\n\n\tasync bindProfileToProject(cwd: string, profile: string, signal?: AbortSignal): Promise<CredentialMutationResult> {\n\t\tawait this.ensureConnected(signal);\n\t\tconst generation = this.connectionGeneration;\n\t\tthis.assertAccessGeneration(generation);\n\t\tvalidateCredentialProfileName(profile);\n\t\tconst identity = validateProjectIdentity(await this.resolveProject(cwd));\n\t\tthis.assertAccessGeneration(generation);\n\t\tconst summaries = await this.storage.listProfiles(signal);\n\t\tthis.assertAccessGeneration(generation);\n\t\tif (!summaries.some((summary) => summary.profile === profile)) {\n\t\t\tthrow new CredentialManagerError(\"profile_not_found\", \"Credential profile was not found.\");\n\t\t}\n\t\tconst record = validateCredentialProfileRecord(await this.storage.readProfile(profile, signal));\n\t\tthis.assertAccessGeneration(generation);\n\t\tif (!record.projectKeys.includes(identity.key)) record.projectKeys.push(identity.key);\n\t\tvalidateCredentialProfileRecord(record);\n\t\tawait this.storage.writeProfile(record, signal);\n\t\tthis.assertAccessGeneration(generation);\n\t\treturn {\n\t\t\tstatus: \"bound\",\n\t\t\tprofile,\n\t\t\tvariableNames: record.variables.map((variable) => variable.name),\n\t\t\tproject: identity.label,\n\t\t\tportable: identity.portable,\n\t\t};\n\t}\n\n\tasync removeProfile(profile: string, signal?: AbortSignal): Promise<CredentialMutationResult> {\n\t\tawait this.ensureConnected(signal);\n\t\tconst generation = this.connectionGeneration;\n\t\tthis.assertAccessGeneration(generation);\n\t\tvalidateCredentialProfileName(profile);\n\t\tconst summaries = await this.storage.listProfiles(signal);\n\t\tthis.assertAccessGeneration(generation);\n\t\tconst summary = summaries.find((candidate) => candidate.profile === profile);\n\t\tif (!summary) throw new CredentialManagerError(\"profile_not_found\", \"Credential profile was not found.\");\n\t\tawait this.storage.deleteProfile(profile, signal);\n\t\tthis.assertAccessGeneration(generation);\n\t\tlet removedActiveEnvironment = false;\n\t\tfor (const [root, active] of this.activeEnvironments) {\n\t\t\tif (active.profile === profile) {\n\t\t\t\tthis.activeEnvironments.delete(root);\n\t\t\t\tremovedActiveEnvironment = true;\n\t\t\t}\n\t\t}\n\t\tthis.rebuildSensitiveValues();\n\t\tif (removedActiveEnvironment) this.onEnvironmentChanged();\n\t\treturn { status: \"removed\", profile, variableNames: [...summary.variableNames] };\n\t}\n\n\tgetEnvironmentForCwd(cwd: string): Record<string, string> | undefined {\n\t\tconst target = resolve(cwd);\n\t\tconst direct = this.findActiveEnvironment(target);\n\t\tconst selected = direct ?? this.findActiveEnvironment(canonicalizeExistingPath(target));\n\t\treturn selected ? cloneEnvironment(selected.values) : undefined;\n\t}\n\n\tprivate findActiveEnvironment(target: string): ActiveCredentialEnvironment | undefined {\n\t\tlet selected: ActiveCredentialEnvironment | undefined;\n\t\tfor (const active of this.activeEnvironments.values()) {\n\t\t\tif (!isPathInside(active.root, target)) continue;\n\t\t\tif (!selected || active.root.length > selected.root.length) selected = active;\n\t\t}\n\t\treturn selected;\n\t}\n\n\thasEnvironmentForCwd(cwd: string): boolean {\n\t\treturn this.getEnvironmentForCwd(cwd) !== undefined;\n\t}\n\n\tredactSensitiveText(text: string): string {\n\t\tlet redacted = text;\n\t\tfor (const value of [...this.sensitiveValues].sort((left, right) => right.length - left.length)) {\n\t\t\tif (value.length > 0) redacted = redacted.split(value).join(\"[REDACTED_SECRET]\");\n\t\t}\n\t\treturn redacted;\n\t}\n\n\tlock(): void {\n\t\tconst hadActiveEnvironment = this.activeEnvironments.size > 0;\n\t\tthis.connectionGeneration++;\n\t\tthis.storage.lock();\n\t\tthis.connected = false;\n\t\tthis.pendingSessionKey = undefined;\n\t\tthis.sessionKeyForRedaction = undefined;\n\t\tthis.connectionPromise = undefined;\n\t\tthis.activeEnvironments.clear();\n\t\tthis.sensitiveValues.clear();\n\t\tif (hadActiveEnvironment) this.onEnvironmentChanged();\n\t}\n\n\tprivate async ensureConnected(signal?: AbortSignal): Promise<void> {\n\t\tif (this.connected) return;\n\t\tif (this.connectionPromise) return this.connectionPromise;\n\t\tlet sessionKey = this.pendingSessionKey;\n\t\tif (!sessionKey) {\n\t\t\tthrow new CredentialManagerError(\n\t\t\t\t\"owner_setup_required\",\n\t\t\t\t\"Bitwarden is not connected. The owner can provide a session key with /secrets.\",\n\t\t\t);\n\t\t}\n\t\tconst generation = this.connectionGeneration;\n\t\tconst connectionPromise = (async () => {\n\t\t\ttry {\n\t\t\t\tawait this.storage.connect(sessionKey, signal);\n\t\t\t\tif (generation !== this.connectionGeneration) {\n\t\t\t\t\tthis.storage.lock();\n\t\t\t\t\tthrow new CredentialManagerError(\n\t\t\t\t\t\t\"owner_setup_required\",\n\t\t\t\t\t\t\"Bitwarden connection was cancelled when the owner locked credentials.\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tthis.connected = true;\n\t\t\t} finally {\n\t\t\t\tif (generation === this.connectionGeneration) this.pendingSessionKey = undefined;\n\t\t\t\tsessionKey = \"\";\n\t\t\t}\n\t\t})();\n\t\tthis.connectionPromise = connectionPromise;\n\t\ttry {\n\t\t\tawait connectionPromise;\n\t\t} finally {\n\t\t\tif (this.connectionPromise === connectionPromise) this.connectionPromise = undefined;\n\t\t}\n\t}\n\n\tprivate assertAccessGeneration(generation: number): void {\n\t\tif (generation === this.connectionGeneration && this.connected) return;\n\t\tthrow new CredentialManagerError(\n\t\t\t\"owner_setup_required\",\n\t\t\t\"Bitwarden credential access was cancelled when the owner locked credentials.\",\n\t\t);\n\t}\n\n\tprivate async refreshProjectProfiles(\n\t\tcwd: string,\n\t\tsignal?: AbortSignal,\n\t): Promise<{\n\t\tgeneration: number;\n\t\tidentity: CredentialProjectIdentity;\n\t\tsummaries: StoredCredentialProfileSummary[];\n\t}> {\n\t\tawait this.ensureConnected(signal);\n\t\tconst generation = this.connectionGeneration;\n\t\tthis.assertAccessGeneration(generation);\n\t\tawait this.storage.refresh?.(signal);\n\t\tthis.assertAccessGeneration(generation);\n\t\tconst identity = validateProjectIdentity(await this.resolveProject(cwd));\n\t\tthis.assertAccessGeneration(generation);\n\t\tconst summaries = await this.storage.listProfiles(signal);\n\t\tthis.assertAccessGeneration(generation);\n\t\treturn { generation, identity, summaries };\n\t}\n\n\tprivate activate(root: string, record: CredentialProfileRecord): void {\n\t\tconst values = Object.fromEntries(record.variables.map((variable) => [variable.name, variable.value]));\n\t\tthis.activeEnvironments.set(resolve(root), { profile: record.profile, root: resolve(root), values });\n\t\tthis.rebuildSensitiveValues();\n\t\tthis.onEnvironmentChanged();\n\t}\n\n\tprivate rebuildSensitiveValues(): void {\n\t\tthis.sensitiveValues.clear();\n\t\tif (this.sessionKeyForRedaction) this.sensitiveValues.add(this.sessionKeyForRedaction);\n\t\tfor (const active of this.activeEnvironments.values()) {\n\t\t\tfor (const value of Object.values(active.values)) this.sensitiveValues.add(value);\n\t\t}\n\t}\n}\n"]}
@@ -0,0 +1,4 @@
1
+ import type { CredentialProjectIdentity } from "./credential-manager.ts";
2
+ export declare function createPortableGitProjectKey(remote: string): string;
3
+ export declare function resolveCredentialProject(cwd: string): Promise<CredentialProjectIdentity>;
4
+ //# sourceMappingURL=credential-project.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credential-project.d.ts","sourceRoot":"","sources":["../../../src/core/secrets/credential-project.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AAuCzE,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAIlE;AA+ED,wBAAsB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAkB9F"}
@@ -0,0 +1,145 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readFile, realpath, stat } from "node:fs/promises";
3
+ import { hostname } from "node:os";
4
+ import { basename, dirname, isAbsolute, join, resolve } from "node:path";
5
+ const MAX_GIT_POINTER_BYTES = 4 * 1024;
6
+ const MAX_GIT_CONFIG_BYTES = 1024 * 1024;
7
+ function opaqueKey(kind, identity) {
8
+ return `${kind}:${createHash("sha256").update(identity).digest("hex")}`;
9
+ }
10
+ function canonicalizeGitRemote(remote) {
11
+ const candidate = remote.trim();
12
+ if (!candidate || /[\0\r\n]/.test(candidate))
13
+ return undefined;
14
+ if (!candidate.includes("://")) {
15
+ const scp = candidate.match(/^(?:[^@/\s]+@)?([^:/\s]+):(.+)$/);
16
+ if (!scp)
17
+ return undefined;
18
+ const host = scp[1]?.toLowerCase();
19
+ const path = scp[2]
20
+ ?.replace(/^\/+/, "")
21
+ .replace(/\/+$/, "")
22
+ .replace(/\.git$/i, "");
23
+ return host && path ? `${host}/${path}` : undefined;
24
+ }
25
+ try {
26
+ const url = new URL(candidate);
27
+ if (!["git:", "http:", "https:", "ssh:"].includes(url.protocol) || !url.hostname)
28
+ return undefined;
29
+ const path = url.pathname
30
+ .replace(/^\/+/, "")
31
+ .replace(/\/+$/, "")
32
+ .replace(/\.git$/i, "");
33
+ if (!path)
34
+ return undefined;
35
+ const host = `${url.hostname.toLowerCase()}${url.port ? `:${url.port}` : ""}`;
36
+ return `${host}/${path}`;
37
+ }
38
+ catch {
39
+ return undefined;
40
+ }
41
+ }
42
+ export function createPortableGitProjectKey(remote) {
43
+ const canonical = canonicalizeGitRemote(remote);
44
+ if (!canonical)
45
+ throw new Error("Git remote is not portable.");
46
+ return opaqueKey("git", canonical);
47
+ }
48
+ async function isDirectory(path) {
49
+ try {
50
+ return (await stat(path)).isDirectory();
51
+ }
52
+ catch {
53
+ return false;
54
+ }
55
+ }
56
+ async function isFile(path) {
57
+ try {
58
+ return (await stat(path)).isFile();
59
+ }
60
+ catch {
61
+ return false;
62
+ }
63
+ }
64
+ async function readBounded(path, maxBytes) {
65
+ try {
66
+ const metadata = await stat(path);
67
+ if (!metadata.isFile() || metadata.size > maxBytes)
68
+ return undefined;
69
+ return await readFile(path, "utf8");
70
+ }
71
+ catch {
72
+ return undefined;
73
+ }
74
+ }
75
+ async function resolveGitDirectory(root, marker) {
76
+ if (await isDirectory(marker))
77
+ return marker;
78
+ if (!(await isFile(marker)))
79
+ return undefined;
80
+ const pointer = await readBounded(marker, MAX_GIT_POINTER_BYTES);
81
+ const gitDirValue = pointer?.match(/^gitdir:\s*(.+?)\s*$/im)?.[1];
82
+ if (!gitDirValue || /[\0\r\n]/.test(gitDirValue))
83
+ return undefined;
84
+ return resolve(root, gitDirValue);
85
+ }
86
+ async function findGitRepository(cwd) {
87
+ let current = await realpath(cwd).catch(() => resolve(cwd));
88
+ while (true) {
89
+ const gitDir = await resolveGitDirectory(current, join(current, ".git"));
90
+ if (gitDir)
91
+ return { root: current, gitDir };
92
+ const parent = dirname(current);
93
+ if (parent === current)
94
+ return undefined;
95
+ current = parent;
96
+ }
97
+ }
98
+ async function resolveGitConfigPath(gitDir) {
99
+ const direct = join(gitDir, "config");
100
+ if (await isFile(direct))
101
+ return direct;
102
+ const commonPointer = await readBounded(join(gitDir, "commondir"), MAX_GIT_POINTER_BYTES);
103
+ const commonValue = commonPointer?.trim();
104
+ if (!commonValue || /[\0\r\n]/.test(commonValue))
105
+ return undefined;
106
+ const commonDir = isAbsolute(commonValue) ? commonValue : resolve(gitDir, commonValue);
107
+ const shared = join(commonDir, "config");
108
+ return (await isFile(shared)) ? shared : undefined;
109
+ }
110
+ function readOriginRemote(config) {
111
+ let inOrigin = false;
112
+ for (const line of config.split(/\r?\n/)) {
113
+ const section = line.match(/^\s*\[([^\]]+)]\s*$/)?.[1];
114
+ if (section !== undefined) {
115
+ inOrigin = /^remote\s+"origin"$/i.test(section.trim());
116
+ continue;
117
+ }
118
+ if (!inOrigin)
119
+ continue;
120
+ const assignment = line.match(/^\s*url\s*=\s*(.*?)\s*$/i)?.[1];
121
+ if (assignment)
122
+ return assignment;
123
+ }
124
+ return undefined;
125
+ }
126
+ export async function resolveCredentialProject(cwd) {
127
+ const repository = await findGitRepository(cwd);
128
+ const root = repository?.root ?? (await realpath(cwd).catch(() => resolve(cwd)));
129
+ if (repository) {
130
+ const configPath = await resolveGitConfigPath(repository.gitDir);
131
+ const config = configPath ? await readBounded(configPath, MAX_GIT_CONFIG_BYTES) : undefined;
132
+ const remote = config ? readOriginRemote(config) : undefined;
133
+ const canonical = remote ? canonicalizeGitRemote(remote) : undefined;
134
+ if (canonical) {
135
+ return { key: opaqueKey("git", canonical), root, label: basename(root), portable: true };
136
+ }
137
+ }
138
+ return {
139
+ key: opaqueKey("local", `${hostname()}\0${root}`),
140
+ root,
141
+ label: basename(root) || root,
142
+ portable: false,
143
+ };
144
+ }
145
+ //# sourceMappingURL=credential-project.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credential-project.js","sourceRoot":"","sources":["../../../src/core/secrets/credential-project.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGzE,MAAM,qBAAqB,GAAG,CAAC,GAAG,IAAI,CAAC;AACvC,MAAM,oBAAoB,GAAG,IAAI,GAAG,IAAI,CAAC;AAEzC,SAAS,SAAS,CAAC,IAAqB,EAAE,QAAgB;IACzD,OAAO,GAAG,IAAI,IAAI,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;AACzE,CAAC;AAED,SAAS,qBAAqB,CAAC,MAAc;IAC5C,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;IAChC,IAAI,CAAC,SAAS,IAAI,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC;QAAE,OAAO,SAAS,CAAC;IAE/D,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAChC,MAAM,GAAG,GAAG,SAAS,CAAC,KAAK,CAAC,iCAAiC,CAAC,CAAC;QAC/D,IAAI,CAAC,GAAG;YAAE,OAAO,SAAS,CAAC;QAC3B,MAAM,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC;QACnC,MAAM,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC;YAClB,EAAE,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;aACpB,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;aACnB,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QACzB,OAAO,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IACrD,CAAC;IAED,IAAI,CAAC;QACJ,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC;QAC/B,IAAI,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ;YAAE,OAAO,SAAS,CAAC;QACnG,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ;aACvB,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;aACnB,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;aACnB,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QACzB,IAAI,CAAC,IAAI;YAAE,OAAO,SAAS,CAAC;QAC5B,MAAM,IAAI,GAAG,GAAG,GAAG,CAAC,QAAQ,CAAC,WAAW,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;QAC9E,OAAO,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,SAAS,CAAC;IAClB,CAAC;AACF,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,MAAc;IACzD,MAAM,SAAS,GAAG,qBAAqB,CAAC,MAAM,CAAC,CAAC;IAChD,IAAI,CAAC,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;IAC/D,OAAO,SAAS,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;AACpC,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,IAAY;IACtC,IAAI,CAAC;QACJ,OAAO,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,KAAK,CAAC;IACd,CAAC;AACF,CAAC;AAED,KAAK,UAAU,MAAM,CAAC,IAAY;IACjC,IAAI,CAAC;QACJ,OAAO,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,KAAK,CAAC;IACd,CAAC;AACF,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,IAAY,EAAE,QAAgB;IACxD,IAAI,CAAC;QACJ,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;QAClC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,QAAQ,CAAC,IAAI,GAAG,QAAQ;YAAE,OAAO,SAAS,CAAC;QACrE,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACrC,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,SAAS,CAAC;IAClB,CAAC;AACF,CAAC;AAOD,KAAK,UAAU,mBAAmB,CAAC,IAAY,EAAE,MAAc;IAC9D,IAAI,MAAM,WAAW,CAAC,MAAM,CAAC;QAAE,OAAO,MAAM,CAAC;IAC7C,IAAI,CAAC,CAAC,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC;QAAE,OAAO,SAAS,CAAC;IAC9C,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC;IACjE,MAAM,WAAW,GAAG,OAAO,EAAE,KAAK,CAAC,wBAAwB,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAClE,IAAI,CAAC,WAAW,IAAI,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC;QAAE,OAAO,SAAS,CAAC;IACnE,OAAO,OAAO,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;AACnC,CAAC;AAED,KAAK,UAAU,iBAAiB,CAAC,GAAW;IAC3C,IAAI,OAAO,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5D,OAAO,IAAI,EAAE,CAAC;QACb,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;QACzE,IAAI,MAAM;YAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;QAC7C,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QAChC,IAAI,MAAM,KAAK,OAAO;YAAE,OAAO,SAAS,CAAC;QACzC,OAAO,GAAG,MAAM,CAAC;IAClB,CAAC;AACF,CAAC;AAED,KAAK,UAAU,oBAAoB,CAAC,MAAc;IACjD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACtC,IAAI,MAAM,MAAM,CAAC,MAAM,CAAC;QAAE,OAAO,MAAM,CAAC;IACxC,MAAM,aAAa,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,EAAE,qBAAqB,CAAC,CAAC;IAC1F,MAAM,WAAW,GAAG,aAAa,EAAE,IAAI,EAAE,CAAC;IAC1C,IAAI,CAAC,WAAW,IAAI,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC;QAAE,OAAO,SAAS,CAAC;IACnE,MAAM,SAAS,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACvF,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IACzC,OAAO,CAAC,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;AACpD,CAAC;AAED,SAAS,gBAAgB,CAAC,MAAc;IACvC,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QAC1C,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,qBAAqB,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACvD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC3B,QAAQ,GAAG,sBAAsB,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;YACvD,SAAS;QACV,CAAC;QACD,IAAI,CAAC,QAAQ;YAAE,SAAS;QACxB,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,0BAA0B,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC/D,IAAI,UAAU;YAAE,OAAO,UAAU,CAAC;IACnC,CAAC;IACD,OAAO,SAAS,CAAC;AAClB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,GAAW;IACzD,MAAM,UAAU,GAAG,MAAM,iBAAiB,CAAC,GAAG,CAAC,CAAC;IAChD,MAAM,IAAI,GAAG,UAAU,EAAE,IAAI,IAAI,CAAC,MAAM,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACjF,IAAI,UAAU,EAAE,CAAC;QAChB,MAAM,UAAU,GAAG,MAAM,oBAAoB,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QACjE,MAAM,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC,MAAM,WAAW,CAAC,UAAU,EAAE,oBAAoB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC5F,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC7D,MAAM,SAAS,GAAG,MAAM,CAAC,CAAC,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACrE,IAAI,SAAS,EAAE,CAAC;YACf,OAAO,EAAE,GAAG,EAAE,SAAS,CAAC,KAAK,EAAE,SAAS,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;QAC1F,CAAC;IACF,CAAC;IACD,OAAO;QACN,GAAG,EAAE,SAAS,CAAC,OAAO,EAAE,GAAG,QAAQ,EAAE,KAAK,IAAI,EAAE,CAAC;QACjD,IAAI;QACJ,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI;QAC7B,QAAQ,EAAE,KAAK;KACf,CAAC;AACH,CAAC","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { readFile, realpath, stat } from \"node:fs/promises\";\nimport { hostname } from \"node:os\";\nimport { basename, dirname, isAbsolute, join, resolve } from \"node:path\";\nimport type { CredentialProjectIdentity } from \"./credential-manager.ts\";\n\nconst MAX_GIT_POINTER_BYTES = 4 * 1024;\nconst MAX_GIT_CONFIG_BYTES = 1024 * 1024;\n\nfunction opaqueKey(kind: \"git\" | \"local\", identity: string): string {\n\treturn `${kind}:${createHash(\"sha256\").update(identity).digest(\"hex\")}`;\n}\n\nfunction canonicalizeGitRemote(remote: string): string | undefined {\n\tconst candidate = remote.trim();\n\tif (!candidate || /[\\0\\r\\n]/.test(candidate)) return undefined;\n\n\tif (!candidate.includes(\"://\")) {\n\t\tconst scp = candidate.match(/^(?:[^@/\\s]+@)?([^:/\\s]+):(.+)$/);\n\t\tif (!scp) return undefined;\n\t\tconst host = scp[1]?.toLowerCase();\n\t\tconst path = scp[2]\n\t\t\t?.replace(/^\\/+/, \"\")\n\t\t\t.replace(/\\/+$/, \"\")\n\t\t\t.replace(/\\.git$/i, \"\");\n\t\treturn host && path ? `${host}/${path}` : undefined;\n\t}\n\n\ttry {\n\t\tconst url = new URL(candidate);\n\t\tif (![\"git:\", \"http:\", \"https:\", \"ssh:\"].includes(url.protocol) || !url.hostname) return undefined;\n\t\tconst path = url.pathname\n\t\t\t.replace(/^\\/+/, \"\")\n\t\t\t.replace(/\\/+$/, \"\")\n\t\t\t.replace(/\\.git$/i, \"\");\n\t\tif (!path) return undefined;\n\t\tconst host = `${url.hostname.toLowerCase()}${url.port ? `:${url.port}` : \"\"}`;\n\t\treturn `${host}/${path}`;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nexport function createPortableGitProjectKey(remote: string): string {\n\tconst canonical = canonicalizeGitRemote(remote);\n\tif (!canonical) throw new Error(\"Git remote is not portable.\");\n\treturn opaqueKey(\"git\", canonical);\n}\n\nasync function isDirectory(path: string): Promise<boolean> {\n\ttry {\n\t\treturn (await stat(path)).isDirectory();\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nasync function isFile(path: string): Promise<boolean> {\n\ttry {\n\t\treturn (await stat(path)).isFile();\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nasync function readBounded(path: string, maxBytes: number): Promise<string | undefined> {\n\ttry {\n\t\tconst metadata = await stat(path);\n\t\tif (!metadata.isFile() || metadata.size > maxBytes) return undefined;\n\t\treturn await readFile(path, \"utf8\");\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\ninterface GitRepositoryLocation {\n\troot: string;\n\tgitDir: string;\n}\n\nasync function resolveGitDirectory(root: string, marker: string): Promise<string | undefined> {\n\tif (await isDirectory(marker)) return marker;\n\tif (!(await isFile(marker))) return undefined;\n\tconst pointer = await readBounded(marker, MAX_GIT_POINTER_BYTES);\n\tconst gitDirValue = pointer?.match(/^gitdir:\\s*(.+?)\\s*$/im)?.[1];\n\tif (!gitDirValue || /[\\0\\r\\n]/.test(gitDirValue)) return undefined;\n\treturn resolve(root, gitDirValue);\n}\n\nasync function findGitRepository(cwd: string): Promise<GitRepositoryLocation | undefined> {\n\tlet current = await realpath(cwd).catch(() => resolve(cwd));\n\twhile (true) {\n\t\tconst gitDir = await resolveGitDirectory(current, join(current, \".git\"));\n\t\tif (gitDir) return { root: current, gitDir };\n\t\tconst parent = dirname(current);\n\t\tif (parent === current) return undefined;\n\t\tcurrent = parent;\n\t}\n}\n\nasync function resolveGitConfigPath(gitDir: string): Promise<string | undefined> {\n\tconst direct = join(gitDir, \"config\");\n\tif (await isFile(direct)) return direct;\n\tconst commonPointer = await readBounded(join(gitDir, \"commondir\"), MAX_GIT_POINTER_BYTES);\n\tconst commonValue = commonPointer?.trim();\n\tif (!commonValue || /[\\0\\r\\n]/.test(commonValue)) return undefined;\n\tconst commonDir = isAbsolute(commonValue) ? commonValue : resolve(gitDir, commonValue);\n\tconst shared = join(commonDir, \"config\");\n\treturn (await isFile(shared)) ? shared : undefined;\n}\n\nfunction readOriginRemote(config: string): string | undefined {\n\tlet inOrigin = false;\n\tfor (const line of config.split(/\\r?\\n/)) {\n\t\tconst section = line.match(/^\\s*\\[([^\\]]+)]\\s*$/)?.[1];\n\t\tif (section !== undefined) {\n\t\t\tinOrigin = /^remote\\s+\"origin\"$/i.test(section.trim());\n\t\t\tcontinue;\n\t\t}\n\t\tif (!inOrigin) continue;\n\t\tconst assignment = line.match(/^\\s*url\\s*=\\s*(.*?)\\s*$/i)?.[1];\n\t\tif (assignment) return assignment;\n\t}\n\treturn undefined;\n}\n\nexport async function resolveCredentialProject(cwd: string): Promise<CredentialProjectIdentity> {\n\tconst repository = await findGitRepository(cwd);\n\tconst root = repository?.root ?? (await realpath(cwd).catch(() => resolve(cwd)));\n\tif (repository) {\n\t\tconst configPath = await resolveGitConfigPath(repository.gitDir);\n\t\tconst config = configPath ? await readBounded(configPath, MAX_GIT_CONFIG_BYTES) : undefined;\n\t\tconst remote = config ? readOriginRemote(config) : undefined;\n\t\tconst canonical = remote ? canonicalizeGitRemote(remote) : undefined;\n\t\tif (canonical) {\n\t\t\treturn { key: opaqueKey(\"git\", canonical), root, label: basename(root), portable: true };\n\t\t}\n\t}\n\treturn {\n\t\tkey: opaqueKey(\"local\", `${hostname()}\\0${root}`),\n\t\troot,\n\t\tlabel: basename(root) || root,\n\t\tportable: false,\n\t};\n}\n"]}
@@ -18,7 +18,7 @@ export interface ParsedDotenvDocument {
18
18
  export declare function validateDotenvVariableName(name: string): string;
19
19
  export declare function validateDotenvValue(value: string): string;
20
20
  /**
21
- * Parse the practical dotenv surface Pi materializes: comments, blank lines, optional `export`,
21
+ * Parse the practical dotenv surface Pi accepts for provider-backed credential profiles: comments, blank lines, optional `export`,
22
22
  * unquoted values, and single/double/backtick quoted values (including multiline quoted values).
23
23
  * Error messages contain only a line number and grammar reason, never the rejected value.
24
24
  */