crewly 1.20.122 → 1.20.124
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config/skills/agent/core/whatsapp-draft/SKILL.md +80 -0
- package/config/skills/agent/core/whatsapp-draft/execute.sh +84 -0
- package/config/skills/agent/core/whatsapp-draft/execute.test.sh +70 -0
- package/config/skills/agent/core/whatsapp-inbox/SKILL.md +75 -0
- package/config/skills/agent/core/whatsapp-inbox/execute.sh +87 -0
- package/config/skills/agent/core/whatsapp-inbox/execute.test.sh +68 -0
- package/config/skills/agent/core/whatsapp-read/SKILL.md +80 -0
- package/config/skills/agent/core/whatsapp-read/execute.sh +116 -0
- package/config/skills/agent/core/whatsapp-read/execute.test.sh +84 -0
- package/config/skills/agent/core/whatsapp-send/SKILL.md +70 -0
- package/config/skills/agent/core/whatsapp-send/execute.sh +84 -0
- package/config/skills/agent/core/whatsapp-send/execute.test.sh +79 -0
- package/dist/backend/backend/src/constants.d.ts +225 -0
- package/dist/backend/backend/src/constants.d.ts.map +1 -1
- package/dist/backend/backend/src/constants.js +223 -0
- package/dist/backend/backend/src/constants.js.map +1 -1
- package/dist/backend/backend/src/controllers/harness/harness.controller.d.ts +54 -0
- package/dist/backend/backend/src/controllers/harness/harness.controller.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/harness/harness.controller.js +204 -0
- package/dist/backend/backend/src/controllers/harness/harness.controller.js.map +1 -0
- package/dist/backend/backend/src/controllers/harness/harness.routes.d.ts +27 -0
- package/dist/backend/backend/src/controllers/harness/harness.routes.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/harness/harness.routes.js +43 -0
- package/dist/backend/backend/src/controllers/harness/harness.routes.js.map +1 -0
- package/dist/backend/backend/src/controllers/whatsapp/whatsapp-inbox.controller.d.ts +73 -0
- package/dist/backend/backend/src/controllers/whatsapp/whatsapp-inbox.controller.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/whatsapp/whatsapp-inbox.controller.js +310 -0
- package/dist/backend/backend/src/controllers/whatsapp/whatsapp-inbox.controller.js.map +1 -0
- package/dist/backend/backend/src/controllers/whatsapp/whatsapp.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/whatsapp/whatsapp.controller.js +53 -4
- package/dist/backend/backend/src/controllers/whatsapp/whatsapp.controller.js.map +1 -1
- package/dist/backend/backend/src/routes/api.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/routes/api.routes.js +4 -0
- package/dist/backend/backend/src/routes/api.routes.js.map +1 -1
- package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts +5 -0
- package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/agent-registration.service.js +7 -0
- package/dist/backend/backend/src/services/agent/agent-registration.service.js.map +1 -1
- package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.d.ts.map +1 -1
- package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.js +5 -0
- package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.js.map +1 -1
- package/dist/backend/backend/src/services/cloud/cloud-client.service.d.ts +2 -1
- package/dist/backend/backend/src/services/cloud/cloud-client.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/cloud/cloud-client.service.js +4 -3
- package/dist/backend/backend/src/services/cloud/cloud-client.service.js.map +1 -1
- package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.js +43 -1
- package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.js.map +1 -1
- package/dist/backend/backend/src/services/harness/claude-config.utils.d.ts +67 -0
- package/dist/backend/backend/src/services/harness/claude-config.utils.d.ts.map +1 -0
- package/dist/backend/backend/src/services/harness/claude-config.utils.js +118 -0
- package/dist/backend/backend/src/services/harness/claude-config.utils.js.map +1 -0
- package/dist/backend/backend/src/services/harness/harness-api-key.service.d.ts +99 -0
- package/dist/backend/backend/src/services/harness/harness-api-key.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/harness/harness-api-key.service.js +157 -0
- package/dist/backend/backend/src/services/harness/harness-api-key.service.js.map +1 -0
- package/dist/backend/backend/src/services/harness/harness-credentials.store.d.ts +135 -0
- package/dist/backend/backend/src/services/harness/harness-credentials.store.d.ts.map +1 -0
- package/dist/backend/backend/src/services/harness/harness-credentials.store.js +208 -0
- package/dist/backend/backend/src/services/harness/harness-credentials.store.js.map +1 -0
- package/dist/backend/backend/src/services/harness/harness-exec.utils.d.ts +85 -0
- package/dist/backend/backend/src/services/harness/harness-exec.utils.d.ts.map +1 -0
- package/dist/backend/backend/src/services/harness/harness-exec.utils.js +183 -0
- package/dist/backend/backend/src/services/harness/harness-exec.utils.js.map +1 -0
- package/dist/backend/backend/src/services/harness/harness-install.service.d.ts +106 -0
- package/dist/backend/backend/src/services/harness/harness-install.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/harness/harness-install.service.js +205 -0
- package/dist/backend/backend/src/services/harness/harness-install.service.js.map +1 -0
- package/dist/backend/backend/src/services/harness/harness-registry.d.ts +86 -0
- package/dist/backend/backend/src/services/harness/harness-registry.d.ts.map +1 -0
- package/dist/backend/backend/src/services/harness/harness-registry.js +121 -0
- package/dist/backend/backend/src/services/harness/harness-registry.js.map +1 -0
- package/dist/backend/backend/src/services/harness/harness-status.service.d.ts +173 -0
- package/dist/backend/backend/src/services/harness/harness-status.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/harness/harness-status.service.js +324 -0
- package/dist/backend/backend/src/services/harness/harness-status.service.js.map +1 -0
- package/dist/backend/backend/src/services/harness/harness.service.d.ts +146 -0
- package/dist/backend/backend/src/services/harness/harness.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/harness/harness.service.js +193 -0
- package/dist/backend/backend/src/services/harness/harness.service.js.map +1 -0
- package/dist/backend/backend/src/services/harness/harness.types.d.ts +138 -0
- package/dist/backend/backend/src/services/harness/harness.types.d.ts.map +1 -0
- package/dist/backend/backend/src/services/harness/harness.types.js +51 -0
- package/dist/backend/backend/src/services/harness/harness.types.js.map +1 -0
- package/dist/backend/backend/src/services/harness/login-broker.service.d.ts +238 -0
- package/dist/backend/backend/src/services/harness/login-broker.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/harness/login-broker.service.js +495 -0
- package/dist/backend/backend/src/services/harness/login-broker.service.js.map +1 -0
- package/dist/backend/backend/src/services/harness/login-rules.d.ts +162 -0
- package/dist/backend/backend/src/services/harness/login-rules.d.ts.map +1 -0
- package/dist/backend/backend/src/services/harness/login-rules.js +344 -0
- package/dist/backend/backend/src/services/harness/login-rules.js.map +1 -0
- package/dist/backend/backend/src/services/harness/orc-harness.store.d.ts +59 -0
- package/dist/backend/backend/src/services/harness/orc-harness.store.d.ts.map +1 -0
- package/dist/backend/backend/src/services/harness/orc-harness.store.js +108 -0
- package/dist/backend/backend/src/services/harness/orc-harness.store.js.map +1 -0
- package/dist/backend/backend/src/services/whatsapp/index.d.ts +5 -1
- package/dist/backend/backend/src/services/whatsapp/index.d.ts.map +1 -1
- package/dist/backend/backend/src/services/whatsapp/index.js +5 -1
- package/dist/backend/backend/src/services/whatsapp/index.js.map +1 -1
- package/dist/backend/backend/src/services/whatsapp/whatsapp-connection-config.d.ts +59 -0
- package/dist/backend/backend/src/services/whatsapp/whatsapp-connection-config.d.ts.map +1 -0
- package/dist/backend/backend/src/services/whatsapp/whatsapp-connection-config.js +108 -0
- package/dist/backend/backend/src/services/whatsapp/whatsapp-connection-config.js.map +1 -0
- package/dist/backend/backend/src/services/whatsapp/whatsapp-draft-gate.d.ts +74 -0
- package/dist/backend/backend/src/services/whatsapp/whatsapp-draft-gate.d.ts.map +1 -0
- package/dist/backend/backend/src/services/whatsapp/whatsapp-draft-gate.js +82 -0
- package/dist/backend/backend/src/services/whatsapp/whatsapp-draft-gate.js.map +1 -0
- package/dist/backend/backend/src/services/whatsapp/whatsapp-inbox-capture.d.ts +184 -0
- package/dist/backend/backend/src/services/whatsapp/whatsapp-inbox-capture.d.ts.map +1 -0
- package/dist/backend/backend/src/services/whatsapp/whatsapp-inbox-capture.js +420 -0
- package/dist/backend/backend/src/services/whatsapp/whatsapp-inbox-capture.js.map +1 -0
- package/dist/backend/backend/src/services/whatsapp/whatsapp-inbox.store.d.ts +243 -0
- package/dist/backend/backend/src/services/whatsapp/whatsapp-inbox.store.d.ts.map +1 -0
- package/dist/backend/backend/src/services/whatsapp/whatsapp-inbox.store.js +538 -0
- package/dist/backend/backend/src/services/whatsapp/whatsapp-inbox.store.js.map +1 -0
- package/dist/backend/backend/src/services/whatsapp/whatsapp-initializer.d.ts +32 -2
- package/dist/backend/backend/src/services/whatsapp/whatsapp-initializer.d.ts.map +1 -1
- package/dist/backend/backend/src/services/whatsapp/whatsapp-initializer.js +65 -9
- package/dist/backend/backend/src/services/whatsapp/whatsapp-initializer.js.map +1 -1
- package/dist/backend/backend/src/services/whatsapp/whatsapp-orchestrator-bridge.d.ts.map +1 -1
- package/dist/backend/backend/src/services/whatsapp/whatsapp-orchestrator-bridge.js +11 -0
- package/dist/backend/backend/src/services/whatsapp/whatsapp-orchestrator-bridge.js.map +1 -1
- package/dist/backend/backend/src/services/whatsapp/whatsapp.service.d.ts +59 -1
- package/dist/backend/backend/src/services/whatsapp/whatsapp.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/whatsapp/whatsapp.service.js +159 -9
- package/dist/backend/backend/src/services/whatsapp/whatsapp.service.js.map +1 -1
- package/dist/backend/backend/src/types/whatsapp.types.d.ts +108 -0
- package/dist/backend/backend/src/types/whatsapp.types.d.ts.map +1 -1
- package/dist/backend/backend/src/types/whatsapp.types.js +9 -0
- package/dist/backend/backend/src/types/whatsapp.types.js.map +1 -1
- package/dist/backend/build-info.json +2 -2
- package/dist/cli/backend/src/constants.d.ts +225 -0
- package/dist/cli/backend/src/constants.d.ts.map +1 -1
- package/dist/cli/backend/src/constants.js +223 -0
- package/dist/cli/backend/src/constants.js.map +1 -1
- package/dist/cli/backend/src/services/agent/agent-registration.service.d.ts +5 -0
- package/dist/cli/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/agent/agent-registration.service.js +7 -0
- package/dist/cli/backend/src/services/agent/agent-registration.service.js.map +1 -1
- package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.d.ts.map +1 -1
- package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.js +5 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.js.map +1 -1
- package/dist/cli/backend/src/services/cloud/cloud-client.service.d.ts +2 -1
- package/dist/cli/backend/src/services/cloud/cloud-client.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/cloud/cloud-client.service.js +4 -3
- package/dist/cli/backend/src/services/cloud/cloud-client.service.js.map +1 -1
- package/dist/cli/backend/src/services/harness/claude-config.utils.d.ts +67 -0
- package/dist/cli/backend/src/services/harness/claude-config.utils.d.ts.map +1 -0
- package/dist/cli/backend/src/services/harness/claude-config.utils.js +118 -0
- package/dist/cli/backend/src/services/harness/claude-config.utils.js.map +1 -0
- package/dist/cli/backend/src/services/harness/harness-api-key.service.d.ts +99 -0
- package/dist/cli/backend/src/services/harness/harness-api-key.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/harness/harness-api-key.service.js +157 -0
- package/dist/cli/backend/src/services/harness/harness-api-key.service.js.map +1 -0
- package/dist/cli/backend/src/services/harness/harness-credentials.store.d.ts +135 -0
- package/dist/cli/backend/src/services/harness/harness-credentials.store.d.ts.map +1 -0
- package/dist/cli/backend/src/services/harness/harness-credentials.store.js +208 -0
- package/dist/cli/backend/src/services/harness/harness-credentials.store.js.map +1 -0
- package/dist/cli/backend/src/services/harness/harness-exec.utils.d.ts +85 -0
- package/dist/cli/backend/src/services/harness/harness-exec.utils.d.ts.map +1 -0
- package/dist/cli/backend/src/services/harness/harness-exec.utils.js +183 -0
- package/dist/cli/backend/src/services/harness/harness-exec.utils.js.map +1 -0
- package/dist/cli/backend/src/services/harness/harness-install.service.d.ts +106 -0
- package/dist/cli/backend/src/services/harness/harness-install.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/harness/harness-install.service.js +205 -0
- package/dist/cli/backend/src/services/harness/harness-install.service.js.map +1 -0
- package/dist/cli/backend/src/services/harness/harness-registry.d.ts +86 -0
- package/dist/cli/backend/src/services/harness/harness-registry.d.ts.map +1 -0
- package/dist/cli/backend/src/services/harness/harness-registry.js +121 -0
- package/dist/cli/backend/src/services/harness/harness-registry.js.map +1 -0
- package/dist/cli/backend/src/services/harness/harness-status.service.d.ts +173 -0
- package/dist/cli/backend/src/services/harness/harness-status.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/harness/harness-status.service.js +324 -0
- package/dist/cli/backend/src/services/harness/harness-status.service.js.map +1 -0
- package/dist/cli/backend/src/services/harness/harness.service.d.ts +146 -0
- package/dist/cli/backend/src/services/harness/harness.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/harness/harness.service.js +193 -0
- package/dist/cli/backend/src/services/harness/harness.service.js.map +1 -0
- package/dist/cli/backend/src/services/harness/harness.types.d.ts +138 -0
- package/dist/cli/backend/src/services/harness/harness.types.d.ts.map +1 -0
- package/dist/cli/backend/src/services/harness/harness.types.js +51 -0
- package/dist/cli/backend/src/services/harness/harness.types.js.map +1 -0
- package/dist/cli/backend/src/services/harness/login-broker.service.d.ts +238 -0
- package/dist/cli/backend/src/services/harness/login-broker.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/harness/login-broker.service.js +495 -0
- package/dist/cli/backend/src/services/harness/login-broker.service.js.map +1 -0
- package/dist/cli/backend/src/services/harness/login-rules.d.ts +162 -0
- package/dist/cli/backend/src/services/harness/login-rules.d.ts.map +1 -0
- package/dist/cli/backend/src/services/harness/login-rules.js +344 -0
- package/dist/cli/backend/src/services/harness/login-rules.js.map +1 -0
- package/dist/cli/backend/src/services/harness/orc-harness.store.d.ts +59 -0
- package/dist/cli/backend/src/services/harness/orc-harness.store.d.ts.map +1 -0
- package/dist/cli/backend/src/services/harness/orc-harness.store.js +108 -0
- package/dist/cli/backend/src/services/harness/orc-harness.store.js.map +1 -0
- package/dist/cli/cli/src/commands/harness-setup.d.ts +159 -0
- package/dist/cli/cli/src/commands/harness-setup.d.ts.map +1 -0
- package/dist/cli/cli/src/commands/harness-setup.js +377 -0
- package/dist/cli/cli/src/commands/harness-setup.js.map +1 -0
- package/dist/cli/cli/src/commands/harness.d.ts +56 -0
- package/dist/cli/cli/src/commands/harness.d.ts.map +1 -0
- package/dist/cli/cli/src/commands/harness.js +97 -0
- package/dist/cli/cli/src/commands/harness.js.map +1 -0
- package/dist/cli/cli/src/commands/onboard.d.ts +108 -50
- package/dist/cli/cli/src/commands/onboard.d.ts.map +1 -1
- package/dist/cli/cli/src/commands/onboard.js +200 -191
- package/dist/cli/cli/src/commands/onboard.js.map +1 -1
- package/dist/cli/cli/src/constants.d.ts +18 -0
- package/dist/cli/cli/src/constants.d.ts.map +1 -1
- package/dist/cli/cli/src/constants.js +18 -0
- package/dist/cli/cli/src/constants.js.map +1 -1
- package/dist/cli/cli/src/index.js +22 -3
- package/dist/cli/cli/src/index.js.map +1 -1
- package/dist/cli/cli/src/utils/harness-engine.d.ts +99 -0
- package/dist/cli/cli/src/utils/harness-engine.d.ts.map +1 -0
- package/dist/cli/cli/src/utils/harness-engine.js +143 -0
- package/dist/cli/cli/src/utils/harness-engine.js.map +1 -0
- package/dist/cli/cli/src/utils/prompt-io.d.ts +25 -0
- package/dist/cli/cli/src/utils/prompt-io.d.ts.map +1 -0
- package/dist/cli/cli/src/utils/prompt-io.js +47 -0
- package/dist/cli/cli/src/utils/prompt-io.js.map +1 -0
- package/frontend/dist/assets/{index-8327e63e.css → index-0a2f783c.css} +1 -1
- package/frontend/dist/assets/{index-07e338a9.js → index-aaf65055.js} +309 -309
- package/frontend/dist/index.html +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API-key login for harnesses.
|
|
3
|
+
*
|
|
4
|
+
* - Claude Code: the Anthropic key is checked against the Anthropic API (a
|
|
5
|
+
* 401/403 rejects it; a network problem does not block saving a
|
|
6
|
+
* well-formed key), stored in the harness credentials store and exported
|
|
7
|
+
* to agents as `ANTHROPIC_API_KEY`. Claude's config is updated so the key
|
|
8
|
+
* is pre-approved (claude-config.utils).
|
|
9
|
+
* - Codex: `codex login --with-api-key` with the key on **stdin** (never in
|
|
10
|
+
* argv); Codex saves it in its own `auth.json`.
|
|
11
|
+
*
|
|
12
|
+
* The key is never logged, echoed back or put in an error message.
|
|
13
|
+
*
|
|
14
|
+
* @module services/harness/harness-api-key.service
|
|
15
|
+
*/
|
|
16
|
+
import { HARNESS_CONSTANTS } from '../../constants.js';
|
|
17
|
+
import { prepareClaudeConfigForCrewlyLogin } from './claude-config.utils.js';
|
|
18
|
+
import { getHarnessCredentialsStore } from './harness-credentials.store.js';
|
|
19
|
+
import { buildHarnessPath, resolveExecutable, runCommand } from './harness-exec.utils.js';
|
|
20
|
+
import { getHarnessDefinition, getLoginMethod } from './harness-registry.js';
|
|
21
|
+
import { SILENT_HARNESS_LOGGER } from './harness.types.js';
|
|
22
|
+
import { redactSecrets } from './login-rules.js';
|
|
23
|
+
/** API-key login error. The message never contains the key. */
|
|
24
|
+
export class HarnessApiKeyError extends Error {
|
|
25
|
+
code;
|
|
26
|
+
/**
|
|
27
|
+
* @param code - Machine-readable code
|
|
28
|
+
* @param message - Human-readable message
|
|
29
|
+
*/
|
|
30
|
+
constructor(code, message) {
|
|
31
|
+
super(message);
|
|
32
|
+
this.code = code;
|
|
33
|
+
this.name = 'HarnessApiKeyError';
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Basic shape check shared by every provider.
|
|
38
|
+
*
|
|
39
|
+
* @param key - Candidate key (already trimmed)
|
|
40
|
+
* @returns True when the length is plausible and it has no whitespace
|
|
41
|
+
*/
|
|
42
|
+
export function isPlausibleApiKey(key) {
|
|
43
|
+
return key.length >= HARNESS_CONSTANTS.API_KEY_MIN_LENGTH && key.length <= HARNESS_CONSTANTS.API_KEY_MAX_LENGTH && !/\s/.test(key);
|
|
44
|
+
}
|
|
45
|
+
/** Validates and stores harness API keys. */
|
|
46
|
+
export class HarnessApiKeyService {
|
|
47
|
+
run;
|
|
48
|
+
fetchFn;
|
|
49
|
+
env;
|
|
50
|
+
credentials;
|
|
51
|
+
resolveCommand;
|
|
52
|
+
prepareClaudeConfig;
|
|
53
|
+
logger;
|
|
54
|
+
/**
|
|
55
|
+
* @param deps - Injectable dependencies (all optional)
|
|
56
|
+
*/
|
|
57
|
+
constructor(deps = {}) {
|
|
58
|
+
this.run = deps.run ?? runCommand;
|
|
59
|
+
this.fetchFn = deps.fetchFn ?? ((url, init) => fetch(url, init));
|
|
60
|
+
this.env = deps.env ?? process.env;
|
|
61
|
+
this.credentials = deps.credentials ?? getHarnessCredentialsStore();
|
|
62
|
+
this.resolveCommand = deps.resolveCommand ?? ((command) => resolveExecutable(command, buildHarnessPath(this.env.PATH)));
|
|
63
|
+
this.prepareClaudeConfig = deps.prepareClaudeConfig ?? ((apiKey) => void prepareClaudeConfigForCrewlyLogin({ apiKey }));
|
|
64
|
+
this.logger = deps.logger ?? SILENT_HARNESS_LOGGER;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Validate and store an API key for a harness.
|
|
68
|
+
*
|
|
69
|
+
* @param harnessId - Harness id
|
|
70
|
+
* @param rawKey - The key as pasted
|
|
71
|
+
* @throws HarnessApiKeyError unknown_harness | unsupported | invalid_key | not_installed | login_failed
|
|
72
|
+
*/
|
|
73
|
+
async submit(harnessId, rawKey) {
|
|
74
|
+
const def = getHarnessDefinition(harnessId);
|
|
75
|
+
if (!def)
|
|
76
|
+
throw new HarnessApiKeyError('unknown_harness', `Unknown harness: ${harnessId}`);
|
|
77
|
+
if (!getLoginMethod(def.id, 'api_key')) {
|
|
78
|
+
throw new HarnessApiKeyError('unsupported', `${def.displayName} does not take an API key in Crewly`);
|
|
79
|
+
}
|
|
80
|
+
const key = typeof rawKey === 'string' ? rawKey.trim() : '';
|
|
81
|
+
if (!isPlausibleApiKey(key)) {
|
|
82
|
+
throw new HarnessApiKeyError('invalid_key', 'That does not look like an API key');
|
|
83
|
+
}
|
|
84
|
+
if (def.id === HARNESS_CONSTANTS.IDS.CLAUDE_CODE) {
|
|
85
|
+
await this.submitAnthropicKey(key);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
await this.submitCodexKey(key);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Check an Anthropic key against the API.
|
|
92
|
+
*
|
|
93
|
+
* @param key - The key
|
|
94
|
+
* @returns valid | invalid | unverified (network error, unexpected status)
|
|
95
|
+
*/
|
|
96
|
+
async checkAnthropicKey(key) {
|
|
97
|
+
try {
|
|
98
|
+
const response = await this.fetchFn(HARNESS_CONSTANTS.CLAUDE.API_KEY_CHECK_URL, {
|
|
99
|
+
method: 'GET',
|
|
100
|
+
headers: { 'x-api-key': key, 'anthropic-version': HARNESS_CONSTANTS.CLAUDE.API_VERSION },
|
|
101
|
+
signal: AbortSignal.timeout(HARNESS_CONSTANTS.CLAUDE.API_KEY_CHECK_TIMEOUT_MS),
|
|
102
|
+
});
|
|
103
|
+
if (response.status >= 200 && response.status < 300)
|
|
104
|
+
return 'valid';
|
|
105
|
+
if (response.status === 401 || response.status === 403)
|
|
106
|
+
return 'invalid';
|
|
107
|
+
return 'unverified';
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return 'unverified';
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Validate and store an Anthropic key.
|
|
115
|
+
*
|
|
116
|
+
* @param key - The key
|
|
117
|
+
* @throws HarnessApiKeyError invalid_key
|
|
118
|
+
*/
|
|
119
|
+
async submitAnthropicKey(key) {
|
|
120
|
+
if (!key.startsWith(HARNESS_CONSTANTS.CLAUDE.API_KEY_PREFIX)) {
|
|
121
|
+
throw new HarnessApiKeyError('invalid_key', `Anthropic API keys start with ${HARNESS_CONSTANTS.CLAUDE.API_KEY_PREFIX}`);
|
|
122
|
+
}
|
|
123
|
+
const check = await this.checkAnthropicKey(key);
|
|
124
|
+
if (check === 'invalid')
|
|
125
|
+
throw new HarnessApiKeyError('invalid_key', 'Anthropic rejected this API key');
|
|
126
|
+
this.credentials.setAnthropicApiKey(key);
|
|
127
|
+
try {
|
|
128
|
+
this.prepareClaudeConfig(key);
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
this.logger.warn('Could not update Claude config after API key login', { error: error instanceof Error ? error.message : String(error) });
|
|
132
|
+
}
|
|
133
|
+
this.logger.info('Anthropic API key stored', { verified: check === 'valid' });
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Log Codex in with an OpenAI key (key on stdin).
|
|
137
|
+
*
|
|
138
|
+
* @param key - The key
|
|
139
|
+
* @throws HarnessApiKeyError not_installed | login_failed
|
|
140
|
+
*/
|
|
141
|
+
async submitCodexKey(key) {
|
|
142
|
+
const binary = this.resolveCommand('codex');
|
|
143
|
+
if (!binary)
|
|
144
|
+
throw new HarnessApiKeyError('not_installed', 'Codex is not installed');
|
|
145
|
+
const result = await this.run(binary, ['login', '--with-api-key'], {
|
|
146
|
+
env: { ...this.env, PATH: buildHarnessPath(this.env.PATH) },
|
|
147
|
+
stdin: `${key}\n`,
|
|
148
|
+
timeoutMs: HARNESS_CONSTANTS.LOGIN.VERIFY_TIMEOUT_MS,
|
|
149
|
+
});
|
|
150
|
+
if (result.code !== 0) {
|
|
151
|
+
const detail = redactSecrets(`${result.stdout}\n${result.stderr}\n${result.error ?? ''}`, [key]).trim();
|
|
152
|
+
throw new HarnessApiKeyError('login_failed', detail ? `Codex did not accept the key: ${detail}` : 'Codex did not accept the key');
|
|
153
|
+
}
|
|
154
|
+
this.logger.info('Codex logged in with an API key');
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=harness-api-key.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"harness-api-key.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/harness/harness-api-key.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Harness credentials store — the harness logins Crewly itself holds.
|
|
3
|
+
*
|
|
4
|
+
* Claude Code's `setup-token` prints a long-lived OAuth token instead of
|
|
5
|
+
* saving it, and an Anthropic API key has to reach agents somehow; both live
|
|
6
|
+
* here, in `<crewlyHome>/harness-credentials.json` with mode 0600. Codex
|
|
7
|
+
* writes its own credentials (`$CODEX_HOME/auth.json`), so nothing is stored
|
|
8
|
+
* for it unless a future flow needs the key.
|
|
9
|
+
*
|
|
10
|
+
* {@link harnessEnvForAgents} turns the stored credentials into the env vars
|
|
11
|
+
* injected into every agent PTY (and adds the user npm prefix to PATH).
|
|
12
|
+
*
|
|
13
|
+
* Secrets are never logged, never returned by the REST API and never put in
|
|
14
|
+
* an error message.
|
|
15
|
+
*
|
|
16
|
+
* @module services/harness/harness-credentials.store
|
|
17
|
+
*/
|
|
18
|
+
/** Stored credentials. Every field is optional. */
|
|
19
|
+
export interface HarnessCredentials {
|
|
20
|
+
claude?: {
|
|
21
|
+
/** Long-lived token from `claude setup-token` (exported as CLAUDE_CODE_OAUTH_TOKEN) */
|
|
22
|
+
oauthToken?: string;
|
|
23
|
+
/** Anthropic API key (exported as ANTHROPIC_API_KEY) */
|
|
24
|
+
anthropicApiKey?: string;
|
|
25
|
+
updatedAt?: string;
|
|
26
|
+
};
|
|
27
|
+
codex?: {
|
|
28
|
+
/** OpenAI API key — only kept if a flow needs it (Codex stores its own login) */
|
|
29
|
+
openaiApiKey?: string;
|
|
30
|
+
updatedAt?: string;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/** Which stored Claude credential is active. */
|
|
34
|
+
export type StoredClaudeCredential = 'oauth_token' | 'api_key' | null;
|
|
35
|
+
/**
|
|
36
|
+
* Reads and writes `harness-credentials.json`.
|
|
37
|
+
*
|
|
38
|
+
* Reads the file on every call (no cache), so the CLI and a running backend
|
|
39
|
+
* always see each other's writes.
|
|
40
|
+
*/
|
|
41
|
+
export declare class HarnessCredentialsStore {
|
|
42
|
+
private readonly filePath?;
|
|
43
|
+
/**
|
|
44
|
+
* @param filePath - Credentials file; defaults to `<crewlyHome>/harness-credentials.json`
|
|
45
|
+
* (resolved per call so `CREWLY_HOME` changes in tests are honoured)
|
|
46
|
+
*/
|
|
47
|
+
constructor(filePath?: string | undefined);
|
|
48
|
+
/**
|
|
49
|
+
* Absolute path of the credentials file.
|
|
50
|
+
*
|
|
51
|
+
* @returns The path
|
|
52
|
+
*/
|
|
53
|
+
getFilePath(): string;
|
|
54
|
+
/**
|
|
55
|
+
* Read the stored credentials.
|
|
56
|
+
*
|
|
57
|
+
* @returns The credentials; an empty object when the file is missing or unreadable
|
|
58
|
+
*/
|
|
59
|
+
read(): HarnessCredentials;
|
|
60
|
+
/**
|
|
61
|
+
* Write credentials atomically with mode 0600.
|
|
62
|
+
*
|
|
63
|
+
* The temp file is created with 0600 before any secret is written to it,
|
|
64
|
+
* then renamed over the target, and the target is chmod-ed again in case
|
|
65
|
+
* it pre-existed with a wider mode.
|
|
66
|
+
*
|
|
67
|
+
* @param credentials - Full credentials object
|
|
68
|
+
*/
|
|
69
|
+
write(credentials: HarnessCredentials): void;
|
|
70
|
+
/**
|
|
71
|
+
* Store Claude's long-lived OAuth token. Replaces a stored API key, so
|
|
72
|
+
* agents use exactly the login the owner chose last.
|
|
73
|
+
*
|
|
74
|
+
* @param token - Token printed by `claude setup-token`
|
|
75
|
+
* @throws Error when the token is empty
|
|
76
|
+
*/
|
|
77
|
+
setClaudeOauthToken(token: string): void;
|
|
78
|
+
/**
|
|
79
|
+
* Store an Anthropic API key. Replaces a stored OAuth token.
|
|
80
|
+
*
|
|
81
|
+
* @param key - Anthropic API key
|
|
82
|
+
* @throws Error when the key is empty
|
|
83
|
+
*/
|
|
84
|
+
setAnthropicApiKey(key: string): void;
|
|
85
|
+
/**
|
|
86
|
+
* Store an OpenAI API key (only when a flow needs Crewly to hold it).
|
|
87
|
+
*
|
|
88
|
+
* @param key - OpenAI API key
|
|
89
|
+
* @throws Error when the key is empty
|
|
90
|
+
*/
|
|
91
|
+
setOpenaiApiKey(key: string): void;
|
|
92
|
+
/**
|
|
93
|
+
* Forget the stored Claude credential.
|
|
94
|
+
*/
|
|
95
|
+
clearClaude(): void;
|
|
96
|
+
/**
|
|
97
|
+
* Which Claude credential Crewly holds (never the value).
|
|
98
|
+
*
|
|
99
|
+
* @returns `oauth_token`, `api_key` or null
|
|
100
|
+
*/
|
|
101
|
+
getClaudeCredentialKind(): StoredClaudeCredential;
|
|
102
|
+
/**
|
|
103
|
+
* Env vars to inject into agent sessions.
|
|
104
|
+
*
|
|
105
|
+
* Always sets PATH (user npm prefix bin first, see harness-exec.utils).
|
|
106
|
+
* Adds `CLAUDE_CODE_OAUTH_TOKEN` or `ANTHROPIC_API_KEY` when Crewly holds
|
|
107
|
+
* a Claude credential. Nothing is added for Codex: it reads its own
|
|
108
|
+
* `auth.json`.
|
|
109
|
+
*
|
|
110
|
+
* @param baseEnv - Environment whose PATH is extended (defaults to process.env)
|
|
111
|
+
* @returns Env map to merge into the agent's spawn env
|
|
112
|
+
*/
|
|
113
|
+
harnessEnvForAgents(baseEnv?: NodeJS.ProcessEnv): Record<string, string>;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* The default credentials store.
|
|
117
|
+
*
|
|
118
|
+
* @returns Store backed by `<crewlyHome>/harness-credentials.json`
|
|
119
|
+
*/
|
|
120
|
+
export declare function getHarnessCredentialsStore(): HarnessCredentialsStore;
|
|
121
|
+
/**
|
|
122
|
+
* Env vars to inject into agent sessions, from the default store.
|
|
123
|
+
*
|
|
124
|
+
* Never throws: a broken credentials file yields just the PATH.
|
|
125
|
+
*
|
|
126
|
+
* @param baseEnv - Environment whose PATH is extended (defaults to process.env)
|
|
127
|
+
* @returns Env map (PATH plus any Claude credential env var)
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* ```ts
|
|
131
|
+
* const env = { ...identityEnv, ...harnessEnvForAgents() };
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
134
|
+
export declare function harnessEnvForAgents(baseEnv?: NodeJS.ProcessEnv): Record<string, string>;
|
|
135
|
+
//# sourceMappingURL=harness-credentials.store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"harness-credentials.store.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/harness/harness-credentials.store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAQH,mDAAmD;AACnD,MAAM,WAAW,kBAAkB;IAClC,MAAM,CAAC,EAAE;QACR,uFAAuF;QACvF,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,wDAAwD;QACxD,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,SAAS,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,KAAK,CAAC,EAAE;QACP,iFAAiF;QACjF,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,SAAS,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;CACF;AAED,gDAAgD;AAChD,MAAM,MAAM,sBAAsB,GAAG,aAAa,GAAG,SAAS,GAAG,IAAI,CAAC;AAYtE;;;;;GAKG;AACH,qBAAa,uBAAuB;IAKvB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;IAJtC;;;OAGG;gBAC0B,QAAQ,CAAC,EAAE,MAAM,YAAA;IAE9C;;;;OAIG;IACH,WAAW,IAAI,MAAM;IAIrB;;;;OAIG;IACH,IAAI,IAAI,kBAAkB;IAS1B;;;;;;;;OAQG;IACH,KAAK,CAAC,WAAW,EAAE,kBAAkB,GAAG,IAAI;IAe5C;;;;;;OAMG;IACH,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAMxC;;;;;OAKG;IACH,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAMrC;;;;;OAKG;IACH,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAMlC;;OAEG;IACH,WAAW,IAAI,IAAI;IAQnB;;;;OAIG;IACH,uBAAuB,IAAI,sBAAsB;IAOjD;;;;;;;;;;OAUG;IACH,mBAAmB,CAAC,OAAO,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;CAUrF;AAKD;;;;GAIG;AACH,wBAAgB,0BAA0B,IAAI,uBAAuB,CAEpE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAMpG"}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Harness credentials store — the harness logins Crewly itself holds.
|
|
3
|
+
*
|
|
4
|
+
* Claude Code's `setup-token` prints a long-lived OAuth token instead of
|
|
5
|
+
* saving it, and an Anthropic API key has to reach agents somehow; both live
|
|
6
|
+
* here, in `<crewlyHome>/harness-credentials.json` with mode 0600. Codex
|
|
7
|
+
* writes its own credentials (`$CODEX_HOME/auth.json`), so nothing is stored
|
|
8
|
+
* for it unless a future flow needs the key.
|
|
9
|
+
*
|
|
10
|
+
* {@link harnessEnvForAgents} turns the stored credentials into the env vars
|
|
11
|
+
* injected into every agent PTY (and adds the user npm prefix to PATH).
|
|
12
|
+
*
|
|
13
|
+
* Secrets are never logged, never returned by the REST API and never put in
|
|
14
|
+
* an error message.
|
|
15
|
+
*
|
|
16
|
+
* @module services/harness/harness-credentials.store
|
|
17
|
+
*/
|
|
18
|
+
import * as fs from 'fs';
|
|
19
|
+
import * as path from 'path';
|
|
20
|
+
import { HARNESS_CONSTANTS } from '../../constants.js';
|
|
21
|
+
import { getCrewlyHomePath } from '../core/crewly-home.utils.js';
|
|
22
|
+
import { buildHarnessPath } from './harness-exec.utils.js';
|
|
23
|
+
/**
|
|
24
|
+
* Whether a value is a non-empty string.
|
|
25
|
+
*
|
|
26
|
+
* @param value - Candidate
|
|
27
|
+
* @returns True for a string with non-whitespace content
|
|
28
|
+
*/
|
|
29
|
+
function isNonEmptyString(value) {
|
|
30
|
+
return typeof value === 'string' && value.trim().length > 0;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Reads and writes `harness-credentials.json`.
|
|
34
|
+
*
|
|
35
|
+
* Reads the file on every call (no cache), so the CLI and a running backend
|
|
36
|
+
* always see each other's writes.
|
|
37
|
+
*/
|
|
38
|
+
export class HarnessCredentialsStore {
|
|
39
|
+
filePath;
|
|
40
|
+
/**
|
|
41
|
+
* @param filePath - Credentials file; defaults to `<crewlyHome>/harness-credentials.json`
|
|
42
|
+
* (resolved per call so `CREWLY_HOME` changes in tests are honoured)
|
|
43
|
+
*/
|
|
44
|
+
constructor(filePath) {
|
|
45
|
+
this.filePath = filePath;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Absolute path of the credentials file.
|
|
49
|
+
*
|
|
50
|
+
* @returns The path
|
|
51
|
+
*/
|
|
52
|
+
getFilePath() {
|
|
53
|
+
return this.filePath ?? path.join(getCrewlyHomePath(), HARNESS_CONSTANTS.CREDENTIALS_FILE);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Read the stored credentials.
|
|
57
|
+
*
|
|
58
|
+
* @returns The credentials; an empty object when the file is missing or unreadable
|
|
59
|
+
*/
|
|
60
|
+
read() {
|
|
61
|
+
try {
|
|
62
|
+
const parsed = JSON.parse(fs.readFileSync(this.getFilePath(), 'utf-8'));
|
|
63
|
+
return parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return {};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Write credentials atomically with mode 0600.
|
|
71
|
+
*
|
|
72
|
+
* The temp file is created with 0600 before any secret is written to it,
|
|
73
|
+
* then renamed over the target, and the target is chmod-ed again in case
|
|
74
|
+
* it pre-existed with a wider mode.
|
|
75
|
+
*
|
|
76
|
+
* @param credentials - Full credentials object
|
|
77
|
+
*/
|
|
78
|
+
write(credentials) {
|
|
79
|
+
const file = this.getFilePath();
|
|
80
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
81
|
+
const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;
|
|
82
|
+
const fd = fs.openSync(tmp, 'w', HARNESS_CONSTANTS.CREDENTIALS_FILE_MODE);
|
|
83
|
+
try {
|
|
84
|
+
fs.writeSync(fd, `${JSON.stringify(credentials, null, 2)}\n`);
|
|
85
|
+
}
|
|
86
|
+
finally {
|
|
87
|
+
fs.closeSync(fd);
|
|
88
|
+
}
|
|
89
|
+
fs.chmodSync(tmp, HARNESS_CONSTANTS.CREDENTIALS_FILE_MODE);
|
|
90
|
+
fs.renameSync(tmp, file);
|
|
91
|
+
fs.chmodSync(file, HARNESS_CONSTANTS.CREDENTIALS_FILE_MODE);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Store Claude's long-lived OAuth token. Replaces a stored API key, so
|
|
95
|
+
* agents use exactly the login the owner chose last.
|
|
96
|
+
*
|
|
97
|
+
* @param token - Token printed by `claude setup-token`
|
|
98
|
+
* @throws Error when the token is empty
|
|
99
|
+
*/
|
|
100
|
+
setClaudeOauthToken(token) {
|
|
101
|
+
if (!isNonEmptyString(token))
|
|
102
|
+
throw new Error('Claude OAuth token is empty');
|
|
103
|
+
const current = this.read();
|
|
104
|
+
this.write({ ...current, claude: { oauthToken: token.trim(), updatedAt: new Date().toISOString() } });
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Store an Anthropic API key. Replaces a stored OAuth token.
|
|
108
|
+
*
|
|
109
|
+
* @param key - Anthropic API key
|
|
110
|
+
* @throws Error when the key is empty
|
|
111
|
+
*/
|
|
112
|
+
setAnthropicApiKey(key) {
|
|
113
|
+
if (!isNonEmptyString(key))
|
|
114
|
+
throw new Error('Anthropic API key is empty');
|
|
115
|
+
const current = this.read();
|
|
116
|
+
this.write({ ...current, claude: { anthropicApiKey: key.trim(), updatedAt: new Date().toISOString() } });
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Store an OpenAI API key (only when a flow needs Crewly to hold it).
|
|
120
|
+
*
|
|
121
|
+
* @param key - OpenAI API key
|
|
122
|
+
* @throws Error when the key is empty
|
|
123
|
+
*/
|
|
124
|
+
setOpenaiApiKey(key) {
|
|
125
|
+
if (!isNonEmptyString(key))
|
|
126
|
+
throw new Error('OpenAI API key is empty');
|
|
127
|
+
const current = this.read();
|
|
128
|
+
this.write({ ...current, codex: { openaiApiKey: key.trim(), updatedAt: new Date().toISOString() } });
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Forget the stored Claude credential.
|
|
132
|
+
*/
|
|
133
|
+
clearClaude() {
|
|
134
|
+
const current = this.read();
|
|
135
|
+
if (!current.claude)
|
|
136
|
+
return;
|
|
137
|
+
const next = { ...current };
|
|
138
|
+
delete next.claude;
|
|
139
|
+
this.write(next);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Which Claude credential Crewly holds (never the value).
|
|
143
|
+
*
|
|
144
|
+
* @returns `oauth_token`, `api_key` or null
|
|
145
|
+
*/
|
|
146
|
+
getClaudeCredentialKind() {
|
|
147
|
+
const claude = this.read().claude;
|
|
148
|
+
if (isNonEmptyString(claude?.oauthToken))
|
|
149
|
+
return 'oauth_token';
|
|
150
|
+
if (isNonEmptyString(claude?.anthropicApiKey))
|
|
151
|
+
return 'api_key';
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Env vars to inject into agent sessions.
|
|
156
|
+
*
|
|
157
|
+
* Always sets PATH (user npm prefix bin first, see harness-exec.utils).
|
|
158
|
+
* Adds `CLAUDE_CODE_OAUTH_TOKEN` or `ANTHROPIC_API_KEY` when Crewly holds
|
|
159
|
+
* a Claude credential. Nothing is added for Codex: it reads its own
|
|
160
|
+
* `auth.json`.
|
|
161
|
+
*
|
|
162
|
+
* @param baseEnv - Environment whose PATH is extended (defaults to process.env)
|
|
163
|
+
* @returns Env map to merge into the agent's spawn env
|
|
164
|
+
*/
|
|
165
|
+
harnessEnvForAgents(baseEnv = process.env) {
|
|
166
|
+
const env = { PATH: buildHarnessPath(baseEnv.PATH) };
|
|
167
|
+
const claude = this.read().claude;
|
|
168
|
+
if (isNonEmptyString(claude?.oauthToken)) {
|
|
169
|
+
env[HARNESS_CONSTANTS.CLAUDE.OAUTH_TOKEN_ENV] = claude.oauthToken;
|
|
170
|
+
}
|
|
171
|
+
else if (isNonEmptyString(claude?.anthropicApiKey)) {
|
|
172
|
+
env[HARNESS_CONSTANTS.CLAUDE.API_KEY_ENV] = claude.anthropicApiKey;
|
|
173
|
+
}
|
|
174
|
+
return env;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/** Process-wide default store (path resolved per call from CREWLY_HOME). */
|
|
178
|
+
const defaultStore = new HarnessCredentialsStore();
|
|
179
|
+
/**
|
|
180
|
+
* The default credentials store.
|
|
181
|
+
*
|
|
182
|
+
* @returns Store backed by `<crewlyHome>/harness-credentials.json`
|
|
183
|
+
*/
|
|
184
|
+
export function getHarnessCredentialsStore() {
|
|
185
|
+
return defaultStore;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Env vars to inject into agent sessions, from the default store.
|
|
189
|
+
*
|
|
190
|
+
* Never throws: a broken credentials file yields just the PATH.
|
|
191
|
+
*
|
|
192
|
+
* @param baseEnv - Environment whose PATH is extended (defaults to process.env)
|
|
193
|
+
* @returns Env map (PATH plus any Claude credential env var)
|
|
194
|
+
*
|
|
195
|
+
* @example
|
|
196
|
+
* ```ts
|
|
197
|
+
* const env = { ...identityEnv, ...harnessEnvForAgents() };
|
|
198
|
+
* ```
|
|
199
|
+
*/
|
|
200
|
+
export function harnessEnvForAgents(baseEnv = process.env) {
|
|
201
|
+
try {
|
|
202
|
+
return defaultStore.harnessEnvForAgents(baseEnv);
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
return { PATH: buildHarnessPath(baseEnv.PATH) };
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=harness-credentials.store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"harness-credentials.store.js","sourceRoot":"","sources":["../../../../../../backend/src/services/harness/harness-credentials.store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process helpers for harness onboarding: the PATH every harness process gets
|
|
3
|
+
* and a shell-free command runner.
|
|
4
|
+
*
|
|
5
|
+
* PATH: when `npm install -g` hits EACCES, Crewly installs the harness under a
|
|
6
|
+
* user-owned prefix (`<crewlyHome>/npm-global`). Its `bin` dir is prepended to
|
|
7
|
+
* PATH for everything Crewly spawns — status probes, the login broker and agent
|
|
8
|
+
* sessions — so a harness installed there is found without touching the
|
|
9
|
+
* user's shell profile. Claude Code's native installer dir (`~/.local/bin`) is
|
|
10
|
+
* appended when missing, because a backend started by launchd/systemd often
|
|
11
|
+
* lacks it.
|
|
12
|
+
*
|
|
13
|
+
* @module services/harness/harness-exec.utils
|
|
14
|
+
*/
|
|
15
|
+
import type { RunCommand } from './harness.types.js';
|
|
16
|
+
/**
|
|
17
|
+
* The user-owned npm prefix used when a global install is not permitted.
|
|
18
|
+
*
|
|
19
|
+
* @returns Absolute path, e.g. `~/.crewly/npm-global`
|
|
20
|
+
*/
|
|
21
|
+
export declare function getUserNpmPrefix(): string;
|
|
22
|
+
/**
|
|
23
|
+
* The bin directory of the user-owned npm prefix.
|
|
24
|
+
*
|
|
25
|
+
* @returns Absolute path, e.g. `~/.crewly/npm-global/bin`
|
|
26
|
+
*/
|
|
27
|
+
export declare function getUserNpmBinDir(): string;
|
|
28
|
+
/**
|
|
29
|
+
* Build the PATH value harness processes run with.
|
|
30
|
+
*
|
|
31
|
+
* Prepends the user npm prefix bin dir and appends `~/.local/bin`, each only
|
|
32
|
+
* when not already present. Order of the existing entries is preserved.
|
|
33
|
+
*
|
|
34
|
+
* @param currentPath - Existing PATH (may be undefined)
|
|
35
|
+
* @param homeDir - Home directory (injectable for tests)
|
|
36
|
+
* @returns The PATH string
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* buildHarnessPath('/usr/bin', '/home/me');
|
|
41
|
+
* // '/home/me/.crewly/npm-global/bin:/usr/bin:/home/me/.local/bin'
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export declare function buildHarnessPath(currentPath: string | undefined, homeDir?: string): string;
|
|
45
|
+
/**
|
|
46
|
+
* PATH for running `npm`: the harness PATH plus the directory of the running
|
|
47
|
+
* Node binary, where npm lives in nvm / installer layouts. A backend started
|
|
48
|
+
* by launchd or systemd often has neither on its PATH.
|
|
49
|
+
*
|
|
50
|
+
* @param currentPath - Existing PATH
|
|
51
|
+
* @param homeDir - Home directory (injectable for tests)
|
|
52
|
+
* @param nodeBinDir - Directory of the Node binary (injectable for tests)
|
|
53
|
+
* @returns The PATH string
|
|
54
|
+
*/
|
|
55
|
+
export declare function buildNpmPath(currentPath: string | undefined, homeDir?: string, nodeBinDir?: string): string;
|
|
56
|
+
/**
|
|
57
|
+
* Copy an environment with PATH set to {@link buildHarnessPath}.
|
|
58
|
+
*
|
|
59
|
+
* @param env - Source environment (defaults to process.env)
|
|
60
|
+
* @returns A new environment object
|
|
61
|
+
*/
|
|
62
|
+
export declare function withHarnessPath(env?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
|
|
63
|
+
/**
|
|
64
|
+
* Find an executable on a PATH, like `which`.
|
|
65
|
+
*
|
|
66
|
+
* @param command - Bare command name (or an absolute path, returned when executable)
|
|
67
|
+
* @param envPath - PATH to search
|
|
68
|
+
* @param isExecutable - Executable check (injectable for tests)
|
|
69
|
+
* @returns Absolute path, or null when not found
|
|
70
|
+
*/
|
|
71
|
+
export declare function resolveExecutable(command: string, envPath: string | undefined, isExecutable?: (file: string) => boolean): string | null;
|
|
72
|
+
/**
|
|
73
|
+
* Run a command without a shell and collect its output.
|
|
74
|
+
*
|
|
75
|
+
* Never rejects: a spawn failure (ENOENT) or timeout comes back as
|
|
76
|
+
* `{ code: null, error }`. Secrets must go through `options.stdin`, never
|
|
77
|
+
* through `args`, so they cannot appear in `ps` output.
|
|
78
|
+
*
|
|
79
|
+
* @param command - Executable
|
|
80
|
+
* @param args - Arguments
|
|
81
|
+
* @param options - env, timeout, stdin, streaming output callback
|
|
82
|
+
* @returns Exit code and output
|
|
83
|
+
*/
|
|
84
|
+
export declare const runCommand: RunCommand;
|
|
85
|
+
//# sourceMappingURL=harness-exec.utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"harness-exec.utils.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/harness/harness-exec.utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAQH,OAAO,KAAK,EAAiB,UAAU,EAAqB,MAAM,oBAAoB,CAAC;AAEvF;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,GAAE,MAAqB,GAAG,MAAM,CAOxG;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAC3B,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,OAAO,GAAE,MAAqB,EAC9B,UAAU,GAAE,MAAuC,GACjD,MAAM,CAGR;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAAC,UAAU,CAEvF;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAChC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,YAAY,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAA6B,GAC3D,MAAM,GAAG,IAAI,CAUf;AAiBD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,UAAU,EAAE,UAsDtB,CAAC"}
|