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,183 @@
|
|
|
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 { spawn } from 'child_process';
|
|
16
|
+
import * as fs from 'fs';
|
|
17
|
+
import * as os from 'os';
|
|
18
|
+
import * as path from 'path';
|
|
19
|
+
import { HARNESS_CONSTANTS } from '../../constants.js';
|
|
20
|
+
import { getCrewlyHomePath } from '../core/crewly-home.utils.js';
|
|
21
|
+
/**
|
|
22
|
+
* The user-owned npm prefix used when a global install is not permitted.
|
|
23
|
+
*
|
|
24
|
+
* @returns Absolute path, e.g. `~/.crewly/npm-global`
|
|
25
|
+
*/
|
|
26
|
+
export function getUserNpmPrefix() {
|
|
27
|
+
return path.join(getCrewlyHomePath(), HARNESS_CONSTANTS.USER_NPM_PREFIX_DIR);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The bin directory of the user-owned npm prefix.
|
|
31
|
+
*
|
|
32
|
+
* @returns Absolute path, e.g. `~/.crewly/npm-global/bin`
|
|
33
|
+
*/
|
|
34
|
+
export function getUserNpmBinDir() {
|
|
35
|
+
return path.join(getUserNpmPrefix(), 'bin');
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Build the PATH value harness processes run with.
|
|
39
|
+
*
|
|
40
|
+
* Prepends the user npm prefix bin dir and appends `~/.local/bin`, each only
|
|
41
|
+
* when not already present. Order of the existing entries is preserved.
|
|
42
|
+
*
|
|
43
|
+
* @param currentPath - Existing PATH (may be undefined)
|
|
44
|
+
* @param homeDir - Home directory (injectable for tests)
|
|
45
|
+
* @returns The PATH string
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* buildHarnessPath('/usr/bin', '/home/me');
|
|
50
|
+
* // '/home/me/.crewly/npm-global/bin:/usr/bin:/home/me/.local/bin'
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export function buildHarnessPath(currentPath, homeDir = os.homedir()) {
|
|
54
|
+
const entries = (currentPath ?? '').split(path.delimiter).filter((entry) => entry.length > 0);
|
|
55
|
+
const npmBin = getUserNpmBinDir();
|
|
56
|
+
const nativeBin = path.join(homeDir, HARNESS_CONSTANTS.NATIVE_INSTALLER_BIN_DIR);
|
|
57
|
+
const result = entries.includes(npmBin) ? [...entries] : [npmBin, ...entries];
|
|
58
|
+
if (!result.includes(nativeBin))
|
|
59
|
+
result.push(nativeBin);
|
|
60
|
+
return result.join(path.delimiter);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* PATH for running `npm`: the harness PATH plus the directory of the running
|
|
64
|
+
* Node binary, where npm lives in nvm / installer layouts. A backend started
|
|
65
|
+
* by launchd or systemd often has neither on its PATH.
|
|
66
|
+
*
|
|
67
|
+
* @param currentPath - Existing PATH
|
|
68
|
+
* @param homeDir - Home directory (injectable for tests)
|
|
69
|
+
* @param nodeBinDir - Directory of the Node binary (injectable for tests)
|
|
70
|
+
* @returns The PATH string
|
|
71
|
+
*/
|
|
72
|
+
export function buildNpmPath(currentPath, homeDir = os.homedir(), nodeBinDir = path.dirname(process.execPath)) {
|
|
73
|
+
const harnessPath = buildHarnessPath(currentPath, homeDir);
|
|
74
|
+
return harnessPath.split(path.delimiter).includes(nodeBinDir) ? harnessPath : `${harnessPath}${path.delimiter}${nodeBinDir}`;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Copy an environment with PATH set to {@link buildHarnessPath}.
|
|
78
|
+
*
|
|
79
|
+
* @param env - Source environment (defaults to process.env)
|
|
80
|
+
* @returns A new environment object
|
|
81
|
+
*/
|
|
82
|
+
export function withHarnessPath(env = process.env) {
|
|
83
|
+
return { ...env, PATH: buildHarnessPath(env.PATH) };
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Find an executable on a PATH, like `which`.
|
|
87
|
+
*
|
|
88
|
+
* @param command - Bare command name (or an absolute path, returned when executable)
|
|
89
|
+
* @param envPath - PATH to search
|
|
90
|
+
* @param isExecutable - Executable check (injectable for tests)
|
|
91
|
+
* @returns Absolute path, or null when not found
|
|
92
|
+
*/
|
|
93
|
+
export function resolveExecutable(command, envPath, isExecutable = defaultIsExecutable) {
|
|
94
|
+
if (command.includes(path.sep)) {
|
|
95
|
+
return isExecutable(command) ? command : null;
|
|
96
|
+
}
|
|
97
|
+
for (const dir of (envPath ?? '').split(path.delimiter)) {
|
|
98
|
+
if (!dir)
|
|
99
|
+
continue;
|
|
100
|
+
const candidate = path.join(dir, command);
|
|
101
|
+
if (isExecutable(candidate))
|
|
102
|
+
return candidate;
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Whether a file exists and is executable by this user.
|
|
108
|
+
*
|
|
109
|
+
* @param file - Absolute path
|
|
110
|
+
* @returns True when executable
|
|
111
|
+
*/
|
|
112
|
+
function defaultIsExecutable(file) {
|
|
113
|
+
try {
|
|
114
|
+
fs.accessSync(file, fs.constants.X_OK);
|
|
115
|
+
return fs.statSync(file).isFile();
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Run a command without a shell and collect its output.
|
|
123
|
+
*
|
|
124
|
+
* Never rejects: a spawn failure (ENOENT) or timeout comes back as
|
|
125
|
+
* `{ code: null, error }`. Secrets must go through `options.stdin`, never
|
|
126
|
+
* through `args`, so they cannot appear in `ps` output.
|
|
127
|
+
*
|
|
128
|
+
* @param command - Executable
|
|
129
|
+
* @param args - Arguments
|
|
130
|
+
* @param options - env, timeout, stdin, streaming output callback
|
|
131
|
+
* @returns Exit code and output
|
|
132
|
+
*/
|
|
133
|
+
export const runCommand = (command, args, options = {}) => new Promise((resolve) => {
|
|
134
|
+
let stdout = '';
|
|
135
|
+
let stderr = '';
|
|
136
|
+
let settled = false;
|
|
137
|
+
let timer = null;
|
|
138
|
+
const finish = (result) => {
|
|
139
|
+
if (settled)
|
|
140
|
+
return;
|
|
141
|
+
settled = true;
|
|
142
|
+
if (timer)
|
|
143
|
+
clearTimeout(timer);
|
|
144
|
+
resolve(result);
|
|
145
|
+
};
|
|
146
|
+
let child;
|
|
147
|
+
try {
|
|
148
|
+
child = spawn(command, [...args], {
|
|
149
|
+
env: options.env ?? process.env,
|
|
150
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
resolve({ code: null, stdout: '', stderr: '', error: error instanceof Error ? error.message : String(error) });
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
if (options.timeoutMs) {
|
|
158
|
+
timer = setTimeout(() => {
|
|
159
|
+
child.kill('SIGKILL');
|
|
160
|
+
finish({ code: null, stdout, stderr, error: `timed out after ${options.timeoutMs} ms` });
|
|
161
|
+
}, options.timeoutMs);
|
|
162
|
+
}
|
|
163
|
+
child.stdout?.on('data', (chunk) => {
|
|
164
|
+
const text = chunk.toString();
|
|
165
|
+
stdout += text;
|
|
166
|
+
options.onOutput?.(text);
|
|
167
|
+
});
|
|
168
|
+
child.stderr?.on('data', (chunk) => {
|
|
169
|
+
const text = chunk.toString();
|
|
170
|
+
stderr += text;
|
|
171
|
+
options.onOutput?.(text);
|
|
172
|
+
});
|
|
173
|
+
child.on('error', (error) => finish({ code: null, stdout, stderr, error: error.message }));
|
|
174
|
+
child.on('close', (code) => finish({ code, stdout, stderr }));
|
|
175
|
+
child.stdin?.on('error', () => undefined);
|
|
176
|
+
if (options.stdin !== undefined) {
|
|
177
|
+
child.stdin?.end(options.stdin);
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
child.stdin?.end();
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
//# sourceMappingURL=harness-exec.utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"harness-exec.utils.js","sourceRoot":"","sources":["../../../../../../backend/src/services/harness/harness-exec.utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Harness install — `npm install -g <pkg>@latest` as an async job.
|
|
3
|
+
*
|
|
4
|
+
* When the global prefix is not writable (EACCES / EPERM), the install is
|
|
5
|
+
* retried once under the user-owned prefix `<crewlyHome>/npm-global`
|
|
6
|
+
* (`npm install -g --prefix …`); its `bin` dir is on the PATH of everything
|
|
7
|
+
* Crewly spawns (harness-exec.utils). No sudo, no shell profile edits.
|
|
8
|
+
*
|
|
9
|
+
* One install runs per harness at a time: starting a second one while the
|
|
10
|
+
* first is running returns the running job.
|
|
11
|
+
*
|
|
12
|
+
* @module services/harness/harness-install.service
|
|
13
|
+
*/
|
|
14
|
+
import { type HarnessId, type HarnessLogger, type InstallJob, type RunCommand } from './harness.types.js';
|
|
15
|
+
/** Injectable dependencies. */
|
|
16
|
+
export interface HarnessInstallDeps {
|
|
17
|
+
run?: RunCommand;
|
|
18
|
+
env?: NodeJS.ProcessEnv;
|
|
19
|
+
/** Creates the user prefix dir */
|
|
20
|
+
mkdirp?: (dir: string) => void;
|
|
21
|
+
now?: () => number;
|
|
22
|
+
idFactory?: () => string;
|
|
23
|
+
logger?: HarnessLogger;
|
|
24
|
+
/** Called after a successful install (e.g. to refresh status caches) */
|
|
25
|
+
onInstalled?: (harnessId: HarnessId) => void;
|
|
26
|
+
}
|
|
27
|
+
/** Error with a machine-readable code, for the REST layer. */
|
|
28
|
+
export declare class HarnessInstallError extends Error {
|
|
29
|
+
readonly code: 'unknown_harness' | 'job_not_found';
|
|
30
|
+
/**
|
|
31
|
+
* @param code - `unknown_harness` or `job_not_found`
|
|
32
|
+
* @param message - Human-readable message
|
|
33
|
+
*/
|
|
34
|
+
constructor(code: 'unknown_harness' | 'job_not_found', message: string);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Whether npm output says the global prefix is not writable.
|
|
38
|
+
*
|
|
39
|
+
* @param output - npm stdout + stderr
|
|
40
|
+
* @returns True for EACCES / EPERM / permission denied
|
|
41
|
+
*/
|
|
42
|
+
export declare function isPermissionError(output: string): boolean;
|
|
43
|
+
/** Runs and tracks harness install jobs. */
|
|
44
|
+
export declare class HarnessInstallService {
|
|
45
|
+
private readonly run;
|
|
46
|
+
private readonly env;
|
|
47
|
+
private readonly mkdirp;
|
|
48
|
+
private readonly now;
|
|
49
|
+
private readonly idFactory;
|
|
50
|
+
private readonly logger;
|
|
51
|
+
private readonly onInstalled?;
|
|
52
|
+
private readonly jobs;
|
|
53
|
+
/**
|
|
54
|
+
* @param deps - Injectable dependencies (all optional)
|
|
55
|
+
*/
|
|
56
|
+
constructor(deps?: HarnessInstallDeps);
|
|
57
|
+
/**
|
|
58
|
+
* Start installing (or updating) a harness.
|
|
59
|
+
*
|
|
60
|
+
* @param harnessId - Harness id
|
|
61
|
+
* @returns The new job, or the job already running for this harness
|
|
62
|
+
* @throws HarnessInstallError `unknown_harness`
|
|
63
|
+
*/
|
|
64
|
+
startInstall(harnessId: string): InstallJob;
|
|
65
|
+
/**
|
|
66
|
+
* Look up a job.
|
|
67
|
+
*
|
|
68
|
+
* @param jobId - Job id
|
|
69
|
+
* @returns The job
|
|
70
|
+
* @throws HarnessInstallError `job_not_found`
|
|
71
|
+
*/
|
|
72
|
+
getJob(jobId: string): InstallJob;
|
|
73
|
+
/**
|
|
74
|
+
* Wait for a job to finish (CLI).
|
|
75
|
+
*
|
|
76
|
+
* @param jobId - Job id
|
|
77
|
+
* @returns The finished job
|
|
78
|
+
* @throws HarnessInstallError `job_not_found`
|
|
79
|
+
*/
|
|
80
|
+
waitForJob(jobId: string): Promise<InstallJob>;
|
|
81
|
+
/**
|
|
82
|
+
* Run npm, falling back to the user prefix on a permission error.
|
|
83
|
+
*
|
|
84
|
+
* @param record - Job record (mutated)
|
|
85
|
+
* @param npmPackage - Package to install
|
|
86
|
+
* @returns The finished job
|
|
87
|
+
*/
|
|
88
|
+
private execute;
|
|
89
|
+
/**
|
|
90
|
+
* Append to a job log, keeping only the tail.
|
|
91
|
+
*
|
|
92
|
+
* @param record - Job record
|
|
93
|
+
* @param text - Text to append
|
|
94
|
+
*/
|
|
95
|
+
private append;
|
|
96
|
+
/** Forget finished jobs older than the retention window. */
|
|
97
|
+
private pruneFinished;
|
|
98
|
+
/**
|
|
99
|
+
* Public view of a job.
|
|
100
|
+
*
|
|
101
|
+
* @param record - Job record
|
|
102
|
+
* @returns Contract shape
|
|
103
|
+
*/
|
|
104
|
+
private toPublic;
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=harness-install.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"harness-install.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/harness/harness-install.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAOH,OAAO,EAAyB,KAAK,SAAS,EAAE,KAAK,aAAa,EAAE,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAEjI,+BAA+B;AAC/B,MAAM,WAAW,kBAAkB;IAClC,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,kCAAkC;IAClC,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,wEAAwE;IACxE,WAAW,CAAC,EAAE,CAAC,SAAS,EAAE,SAAS,KAAK,IAAI,CAAC;CAC7C;AAED,8DAA8D;AAC9D,qBAAa,mBAAoB,SAAQ,KAAK;aAM5B,IAAI,EAAE,iBAAiB,GAAG,eAAe;IAL1D;;;OAGG;gBAEc,IAAI,EAAE,iBAAiB,GAAG,eAAe,EACzD,OAAO,EAAE,MAAM;CAKhB;AASD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAGzD;AAED,4CAA4C;AAC5C,qBAAa,qBAAqB;IACjC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAa;IACjC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAoB;IACxC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwB;IAC/C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IACvC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAiC;IAC9D,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAgC;IAErD;;OAEG;gBACS,IAAI,GAAE,kBAAuB;IAUzC;;;;;;OAMG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,UAAU;IAqB3C;;;;;;OAMG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU;IAMjC;;;;;;OAMG;IACG,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAOpD;;;;;;OAMG;YACW,OAAO;IAuCrB;;;;;OAKG;IACH,OAAO,CAAC,MAAM;IAMd,4DAA4D;IAC5D,OAAO,CAAC,aAAa;IAOrB;;;;;OAKG;IACH,OAAO,CAAC,QAAQ;CAShB"}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Harness install — `npm install -g <pkg>@latest` as an async job.
|
|
3
|
+
*
|
|
4
|
+
* When the global prefix is not writable (EACCES / EPERM), the install is
|
|
5
|
+
* retried once under the user-owned prefix `<crewlyHome>/npm-global`
|
|
6
|
+
* (`npm install -g --prefix …`); its `bin` dir is on the PATH of everything
|
|
7
|
+
* Crewly spawns (harness-exec.utils). No sudo, no shell profile edits.
|
|
8
|
+
*
|
|
9
|
+
* One install runs per harness at a time: starting a second one while the
|
|
10
|
+
* first is running returns the running job.
|
|
11
|
+
*
|
|
12
|
+
* @module services/harness/harness-install.service
|
|
13
|
+
*/
|
|
14
|
+
import { randomUUID } from 'crypto';
|
|
15
|
+
import * as fs from 'fs';
|
|
16
|
+
import { HARNESS_CONSTANTS } from '../../constants.js';
|
|
17
|
+
import { buildNpmPath, getUserNpmPrefix, runCommand } from './harness-exec.utils.js';
|
|
18
|
+
import { getHarnessDefinition } from './harness-registry.js';
|
|
19
|
+
import { SILENT_HARNESS_LOGGER } from './harness.types.js';
|
|
20
|
+
/** Error with a machine-readable code, for the REST layer. */
|
|
21
|
+
export class HarnessInstallError extends Error {
|
|
22
|
+
code;
|
|
23
|
+
/**
|
|
24
|
+
* @param code - `unknown_harness` or `job_not_found`
|
|
25
|
+
* @param message - Human-readable message
|
|
26
|
+
*/
|
|
27
|
+
constructor(code, message) {
|
|
28
|
+
super(message);
|
|
29
|
+
this.code = code;
|
|
30
|
+
this.name = 'HarnessInstallError';
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Whether npm output says the global prefix is not writable.
|
|
35
|
+
*
|
|
36
|
+
* @param output - npm stdout + stderr
|
|
37
|
+
* @returns True for EACCES / EPERM / permission denied
|
|
38
|
+
*/
|
|
39
|
+
export function isPermissionError(output) {
|
|
40
|
+
const lower = output.toLowerCase();
|
|
41
|
+
return HARNESS_CONSTANTS.PERMISSION_ERROR_PATTERNS.some((pattern) => lower.includes(pattern.toLowerCase()));
|
|
42
|
+
}
|
|
43
|
+
/** Runs and tracks harness install jobs. */
|
|
44
|
+
export class HarnessInstallService {
|
|
45
|
+
run;
|
|
46
|
+
env;
|
|
47
|
+
mkdirp;
|
|
48
|
+
now;
|
|
49
|
+
idFactory;
|
|
50
|
+
logger;
|
|
51
|
+
onInstalled;
|
|
52
|
+
jobs = new Map();
|
|
53
|
+
/**
|
|
54
|
+
* @param deps - Injectable dependencies (all optional)
|
|
55
|
+
*/
|
|
56
|
+
constructor(deps = {}) {
|
|
57
|
+
this.run = deps.run ?? runCommand;
|
|
58
|
+
this.env = deps.env ?? process.env;
|
|
59
|
+
this.mkdirp = deps.mkdirp ?? ((dir) => fs.mkdirSync(dir, { recursive: true }));
|
|
60
|
+
this.now = deps.now ?? Date.now;
|
|
61
|
+
this.idFactory = deps.idFactory ?? randomUUID;
|
|
62
|
+
this.logger = deps.logger ?? SILENT_HARNESS_LOGGER;
|
|
63
|
+
this.onInstalled = deps.onInstalled;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Start installing (or updating) a harness.
|
|
67
|
+
*
|
|
68
|
+
* @param harnessId - Harness id
|
|
69
|
+
* @returns The new job, or the job already running for this harness
|
|
70
|
+
* @throws HarnessInstallError `unknown_harness`
|
|
71
|
+
*/
|
|
72
|
+
startInstall(harnessId) {
|
|
73
|
+
const def = getHarnessDefinition(harnessId);
|
|
74
|
+
if (!def)
|
|
75
|
+
throw new HarnessInstallError('unknown_harness', `Unknown harness: ${harnessId}`);
|
|
76
|
+
this.pruneFinished();
|
|
77
|
+
const running = [...this.jobs.values()].find((job) => job.harnessId === def.id && job.state === 'running');
|
|
78
|
+
if (running)
|
|
79
|
+
return this.toPublic(running);
|
|
80
|
+
const record = {
|
|
81
|
+
jobId: this.idFactory(),
|
|
82
|
+
harnessId: def.id,
|
|
83
|
+
state: 'running',
|
|
84
|
+
log: '',
|
|
85
|
+
usedUserPrefix: false,
|
|
86
|
+
finishedAt: null,
|
|
87
|
+
done: null,
|
|
88
|
+
};
|
|
89
|
+
this.jobs.set(record.jobId, record);
|
|
90
|
+
record.done = this.execute(record, def.npmPackage);
|
|
91
|
+
return this.toPublic(record);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Look up a job.
|
|
95
|
+
*
|
|
96
|
+
* @param jobId - Job id
|
|
97
|
+
* @returns The job
|
|
98
|
+
* @throws HarnessInstallError `job_not_found`
|
|
99
|
+
*/
|
|
100
|
+
getJob(jobId) {
|
|
101
|
+
const record = this.jobs.get(jobId);
|
|
102
|
+
if (!record)
|
|
103
|
+
throw new HarnessInstallError('job_not_found', `Install job not found: ${jobId}`);
|
|
104
|
+
return this.toPublic(record);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Wait for a job to finish (CLI).
|
|
108
|
+
*
|
|
109
|
+
* @param jobId - Job id
|
|
110
|
+
* @returns The finished job
|
|
111
|
+
* @throws HarnessInstallError `job_not_found`
|
|
112
|
+
*/
|
|
113
|
+
async waitForJob(jobId) {
|
|
114
|
+
const record = this.jobs.get(jobId);
|
|
115
|
+
if (!record)
|
|
116
|
+
throw new HarnessInstallError('job_not_found', `Install job not found: ${jobId}`);
|
|
117
|
+
if (record.done)
|
|
118
|
+
await record.done;
|
|
119
|
+
return this.toPublic(record);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Run npm, falling back to the user prefix on a permission error.
|
|
123
|
+
*
|
|
124
|
+
* @param record - Job record (mutated)
|
|
125
|
+
* @param npmPackage - Package to install
|
|
126
|
+
* @returns The finished job
|
|
127
|
+
*/
|
|
128
|
+
async execute(record, npmPackage) {
|
|
129
|
+
const spec = `${npmPackage}@latest`;
|
|
130
|
+
const env = { ...this.env, PATH: buildNpmPath(this.env.PATH) };
|
|
131
|
+
try {
|
|
132
|
+
this.append(record, `$ npm install -g ${spec}\n`);
|
|
133
|
+
const first = await this.run('npm', ['install', '-g', spec], {
|
|
134
|
+
env,
|
|
135
|
+
timeoutMs: HARNESS_CONSTANTS.INSTALL_TIMEOUT_MS,
|
|
136
|
+
onOutput: (chunk) => this.append(record, chunk),
|
|
137
|
+
});
|
|
138
|
+
let ok = first.code === 0;
|
|
139
|
+
if (!ok && isPermissionError(`${first.stdout}\n${first.stderr}\n${first.error ?? ''}`)) {
|
|
140
|
+
const prefix = getUserNpmPrefix();
|
|
141
|
+
this.mkdirp(prefix);
|
|
142
|
+
record.usedUserPrefix = true;
|
|
143
|
+
this.append(record, `\nNo permission to write the global npm prefix; installing under ${prefix} instead.\n`);
|
|
144
|
+
this.append(record, `$ npm install -g --prefix ${prefix} ${spec}\n`);
|
|
145
|
+
const second = await this.run('npm', ['install', '-g', '--prefix', prefix, spec], {
|
|
146
|
+
env,
|
|
147
|
+
timeoutMs: HARNESS_CONSTANTS.INSTALL_TIMEOUT_MS,
|
|
148
|
+
onOutput: (chunk) => this.append(record, chunk),
|
|
149
|
+
});
|
|
150
|
+
ok = second.code === 0;
|
|
151
|
+
if (!ok && second.error)
|
|
152
|
+
this.append(record, `\n${second.error}\n`);
|
|
153
|
+
}
|
|
154
|
+
else if (!ok && first.error) {
|
|
155
|
+
this.append(record, `\n${first.error}\n`);
|
|
156
|
+
}
|
|
157
|
+
record.state = ok ? 'succeeded' : 'failed';
|
|
158
|
+
this.append(record, ok ? '\nInstalled.\n' : '\nInstall failed.\n');
|
|
159
|
+
if (ok)
|
|
160
|
+
this.onInstalled?.(record.harnessId);
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
record.state = 'failed';
|
|
164
|
+
this.append(record, `\n${error instanceof Error ? error.message : String(error)}\n`);
|
|
165
|
+
}
|
|
166
|
+
record.finishedAt = this.now();
|
|
167
|
+
this.logger.info('Harness install finished', { harnessId: record.harnessId, state: record.state, usedUserPrefix: record.usedUserPrefix });
|
|
168
|
+
return this.toPublic(record);
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Append to a job log, keeping only the tail.
|
|
172
|
+
*
|
|
173
|
+
* @param record - Job record
|
|
174
|
+
* @param text - Text to append
|
|
175
|
+
*/
|
|
176
|
+
append(record, text) {
|
|
177
|
+
const next = record.log + text;
|
|
178
|
+
const max = HARNESS_CONSTANTS.INSTALL_LOG_MAX_CHARS;
|
|
179
|
+
record.log = next.length > max ? next.slice(next.length - max) : next;
|
|
180
|
+
}
|
|
181
|
+
/** Forget finished jobs older than the retention window. */
|
|
182
|
+
pruneFinished() {
|
|
183
|
+
const cutoff = this.now() - HARNESS_CONSTANTS.INSTALL_JOB_RETENTION_MS;
|
|
184
|
+
for (const [id, job] of this.jobs) {
|
|
185
|
+
if (job.finishedAt !== null && job.finishedAt < cutoff)
|
|
186
|
+
this.jobs.delete(id);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Public view of a job.
|
|
191
|
+
*
|
|
192
|
+
* @param record - Job record
|
|
193
|
+
* @returns Contract shape
|
|
194
|
+
*/
|
|
195
|
+
toPublic(record) {
|
|
196
|
+
return {
|
|
197
|
+
jobId: record.jobId,
|
|
198
|
+
harnessId: record.harnessId,
|
|
199
|
+
state: record.state,
|
|
200
|
+
log: record.log,
|
|
201
|
+
usedUserPrefix: record.usedUserPrefix,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
//# sourceMappingURL=harness-install.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"harness-install.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/harness/harness-install.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrF,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,qBAAqB,EAAwE,MAAM,oBAAoB,CAAC;AAejI,8DAA8D;AAC9D,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAM5B;IALjB;;;OAGG;IACH,YACiB,IAAyC,EACzD,OAAe;QAEf,KAAK,CAAC,OAAO,CAAC,CAAC;QAHC,SAAI,GAAJ,IAAI,CAAqC;QAIzD,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;IACnC,CAAC;CACD;AASD;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAc;IAC/C,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC;IACnC,OAAO,iBAAiB,CAAC,yBAAyB,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;AAC7G,CAAC;AAED,4CAA4C;AAC5C,MAAM,OAAO,qBAAqB;IAChB,GAAG,CAAa;IAChB,GAAG,CAAoB;IACvB,MAAM,CAAwB;IAC9B,GAAG,CAAe;IAClB,SAAS,CAAe;IACxB,MAAM,CAAgB;IACtB,WAAW,CAAkC;IAC7C,IAAI,GAAG,IAAI,GAAG,EAAqB,CAAC;IAErD;;OAEG;IACH,YAAY,OAA2B,EAAE;QACxC,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,UAAU,CAAC;QAClC,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;QACnC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAC/E,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;QAChC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,UAAU,CAAC;QAC9C,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,qBAAqB,CAAC;QACnD,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;IACrC,CAAC;IAED;;;;;;OAMG;IACH,YAAY,CAAC,SAAiB;QAC7B,MAAM,GAAG,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAC;QAC5C,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,mBAAmB,CAAC,iBAAiB,EAAE,oBAAoB,SAAS,EAAE,CAAC,CAAC;QAC5F,IAAI,CAAC,aAAa,EAAE,CAAC;QACrB,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,SAAS,KAAK,GAAG,CAAC,EAAE,IAAI,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC;QAC3G,IAAI,OAAO;YAAE,OAAO,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAE3C,MAAM,MAAM,GAAc;YACzB,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE;YACvB,SAAS,EAAE,GAAG,CAAC,EAAE;YACjB,KAAK,EAAE,SAAS;YAChB,GAAG,EAAE,EAAE;YACP,cAAc,EAAE,KAAK;YACrB,UAAU,EAAE,IAAI;YAChB,IAAI,EAAE,IAAI;SACV,CAAC;QACF,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QACpC,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC;QACnD,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC9B,CAAC;IAED;;;;;;OAMG;IACH,MAAM,CAAC,KAAa;QACnB,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACpC,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,mBAAmB,CAAC,eAAe,EAAE,0BAA0B,KAAK,EAAE,CAAC,CAAC;QAC/F,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC9B,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,UAAU,CAAC,KAAa;QAC7B,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACpC,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,mBAAmB,CAAC,eAAe,EAAE,0BAA0B,KAAK,EAAE,CAAC,CAAC;QAC/F,IAAI,MAAM,CAAC,IAAI;YAAE,MAAM,MAAM,CAAC,IAAI,CAAC;QACnC,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC9B,CAAC;IAED;;;;;;OAMG;IACK,KAAK,CAAC,OAAO,CAAC,MAAiB,EAAE,UAAkB;QAC1D,MAAM,IAAI,GAAG,GAAG,UAAU,SAAS,CAAC;QACpC,MAAM,GAAG,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/D,IAAI,CAAC;YACJ,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,oBAAoB,IAAI,IAAI,CAAC,CAAC;YAClD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE;gBAC5D,GAAG;gBACH,SAAS,EAAE,iBAAiB,CAAC,kBAAkB;gBAC/C,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC;aAC/C,CAAC,CAAC;YACH,IAAI,EAAE,GAAG,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC;YAC1B,IAAI,CAAC,EAAE,IAAI,iBAAiB,CAAC,GAAG,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC;gBACxF,MAAM,MAAM,GAAG,gBAAgB,EAAE,CAAC;gBAClC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;gBACpB,MAAM,CAAC,cAAc,GAAG,IAAI,CAAC;gBAC7B,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,oEAAoE,MAAM,aAAa,CAAC,CAAC;gBAC7G,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,6BAA6B,MAAM,IAAI,IAAI,IAAI,CAAC,CAAC;gBACrE,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,SAAS,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;oBACjF,GAAG;oBACH,SAAS,EAAE,iBAAiB,CAAC,kBAAkB;oBAC/C,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC;iBAC/C,CAAC,CAAC;gBACH,EAAE,GAAG,MAAM,CAAC,IAAI,KAAK,CAAC,CAAC;gBACvB,IAAI,CAAC,EAAE,IAAI,MAAM,CAAC,KAAK;oBAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,MAAM,CAAC,KAAK,IAAI,CAAC,CAAC;YACrE,CAAC;iBAAM,IAAI,CAAC,EAAE,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;gBAC/B,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC;YAC3C,CAAC;YACD,MAAM,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC;YAC3C,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC;YACnE,IAAI,EAAE;gBAAE,IAAI,CAAC,WAAW,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC9C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,CAAC,KAAK,GAAG,QAAQ,CAAC;YACxB,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACtF,CAAC;QACD,MAAM,CAAC,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC/B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,0BAA0B,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,cAAc,EAAE,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC;QAC1I,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC9B,CAAC;IAED;;;;;OAKG;IACK,MAAM,CAAC,MAAiB,EAAE,IAAY;QAC7C,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,GAAG,IAAI,CAAC;QAC/B,MAAM,GAAG,GAAG,iBAAiB,CAAC,qBAAqB,CAAC;QACpD,MAAM,CAAC,GAAG,GAAG,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACvE,CAAC;IAED,4DAA4D;IACpD,aAAa;QACpB,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,iBAAiB,CAAC,wBAAwB,CAAC;QACvE,KAAK,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACnC,IAAI,GAAG,CAAC,UAAU,KAAK,IAAI,IAAI,GAAG,CAAC,UAAU,GAAG,MAAM;gBAAE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC9E,CAAC;IACF,CAAC;IAED;;;;;OAKG;IACK,QAAQ,CAAC,MAAiB;QACjC,OAAO;YACN,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,GAAG,EAAE,MAAM,CAAC,GAAG;YACf,cAAc,EAAE,MAAM,CAAC,cAAc;SACrC,CAAC;IACH,CAAC;CACD"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Harness registry — static definitions of the agent CLIs Crewly can set up.
|
|
3
|
+
*
|
|
4
|
+
* One entry per harness: display name, binary, npm package, how to print the
|
|
5
|
+
* version, and the login methods Crewly offers. A `broker` method names the
|
|
6
|
+
* harness's own login command, which the login broker runs in a PTY; an
|
|
7
|
+
* `api_key` method is a pasted key. Gemini CLI is detect-only (no methods).
|
|
8
|
+
*
|
|
9
|
+
* @module services/harness/harness-registry
|
|
10
|
+
*/
|
|
11
|
+
import type { HarnessId, HarnessLoginMethod, LoginMethodId, LoginMethodKind } from './harness.types.js';
|
|
12
|
+
/** The command a broker login method runs. */
|
|
13
|
+
export interface BrokerCommand {
|
|
14
|
+
/** Binary (resolved on the harness PATH) */
|
|
15
|
+
command: string;
|
|
16
|
+
args: readonly string[];
|
|
17
|
+
}
|
|
18
|
+
/** A login method definition (internal: includes the broker command). */
|
|
19
|
+
export interface LoginMethodDefinition {
|
|
20
|
+
id: LoginMethodId;
|
|
21
|
+
label: string;
|
|
22
|
+
kind: LoginMethodKind;
|
|
23
|
+
/** Command run by the login broker (kind `broker` only) */
|
|
24
|
+
broker?: BrokerCommand;
|
|
25
|
+
}
|
|
26
|
+
/** Static facts about one harness. */
|
|
27
|
+
export interface HarnessDefinition {
|
|
28
|
+
id: HarnessId;
|
|
29
|
+
displayName: string;
|
|
30
|
+
/** Binary name on PATH */
|
|
31
|
+
command: string;
|
|
32
|
+
npmPackage: string;
|
|
33
|
+
/** Arguments that print the version */
|
|
34
|
+
versionArgs: readonly string[];
|
|
35
|
+
loginMethods: readonly LoginMethodDefinition[];
|
|
36
|
+
}
|
|
37
|
+
/** Every harness Crewly knows, in display order (default first). */
|
|
38
|
+
export declare const HARNESS_DEFINITIONS: readonly HarnessDefinition[];
|
|
39
|
+
/**
|
|
40
|
+
* All harness definitions.
|
|
41
|
+
*
|
|
42
|
+
* @returns The registry, in display order
|
|
43
|
+
*/
|
|
44
|
+
export declare function listHarnessDefinitions(): readonly HarnessDefinition[];
|
|
45
|
+
/**
|
|
46
|
+
* Look up a harness definition.
|
|
47
|
+
*
|
|
48
|
+
* @param id - Harness id
|
|
49
|
+
* @returns The definition, or undefined for an unknown id
|
|
50
|
+
*/
|
|
51
|
+
export declare function getHarnessDefinition(id: string): HarnessDefinition | undefined;
|
|
52
|
+
/**
|
|
53
|
+
* Look up one login method of a harness.
|
|
54
|
+
*
|
|
55
|
+
* @param harnessId - Harness id
|
|
56
|
+
* @param methodId - Login method id
|
|
57
|
+
* @returns The method, or undefined when the harness does not offer it
|
|
58
|
+
*/
|
|
59
|
+
export declare function getLoginMethod(harnessId: string, methodId: string): LoginMethodDefinition | undefined;
|
|
60
|
+
/**
|
|
61
|
+
* The broker login method of a harness (the first one), if any.
|
|
62
|
+
*
|
|
63
|
+
* @param harnessId - Harness id
|
|
64
|
+
* @returns The method, or undefined when the harness has no broker login
|
|
65
|
+
*/
|
|
66
|
+
export declare function getBrokerLoginMethod(harnessId: string): LoginMethodDefinition | undefined;
|
|
67
|
+
/**
|
|
68
|
+
* Public view of a harness's login methods (no commands).
|
|
69
|
+
*
|
|
70
|
+
* @param def - Harness definition
|
|
71
|
+
* @returns `{ id, label, kind }` per method
|
|
72
|
+
*/
|
|
73
|
+
export declare function toPublicLoginMethods(def: HarnessDefinition): HarnessLoginMethod[];
|
|
74
|
+
/**
|
|
75
|
+
* Resolve a harness id from a full id or a short CLI alias.
|
|
76
|
+
*
|
|
77
|
+
* @param value - `claude-code`, `claude`, `codex-cli`, `codex`, `gemini-cli` or `gemini` (case-insensitive)
|
|
78
|
+
* @returns The harness id, or null when unknown
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```ts
|
|
82
|
+
* resolveHarnessAlias('codex'); // 'codex-cli'
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
export declare function resolveHarnessAlias(value: string | undefined | null): HarnessId | null;
|
|
86
|
+
//# sourceMappingURL=harness-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"harness-registry.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/harness/harness-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,kBAAkB,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAGxG,8CAA8C;AAC9C,MAAM,WAAW,aAAa;IAC7B,4CAA4C;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;CACxB;AAED,yEAAyE;AACzE,MAAM,WAAW,qBAAqB;IACrC,EAAE,EAAE,aAAa,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,eAAe,CAAC;IACtB,2DAA2D;IAC3D,MAAM,CAAC,EAAE,aAAa,CAAC;CACvB;AAED,sCAAsC;AACtC,MAAM,WAAW,iBAAiB;IACjC,EAAE,EAAE,SAAS,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,0BAA0B;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,YAAY,EAAE,SAAS,qBAAqB,EAAE,CAAC;CAC/C;AAED,oEAAoE;AACpE,eAAO,MAAM,mBAAmB,EAAE,SAAS,iBAAiB,EAyC3D,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,sBAAsB,IAAI,SAAS,iBAAiB,EAAE,CAErE;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAE9E;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,qBAAqB,GAAG,SAAS,CAErG;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,qBAAqB,GAAG,SAAS,CAEzF;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,iBAAiB,GAAG,kBAAkB,EAAE,CAEjF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,CAMtF"}
|