crewly 1.20.156 → 1.20.158
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/README.md +1 -0
- package/config/templates/growth-marketing-team.json +5 -5
- package/config/templates/personal-assistant-team.json +5 -5
- package/dist/backend/backend/src/constants.d.ts +161 -6
- package/dist/backend/backend/src/constants.d.ts.map +1 -1
- package/dist/backend/backend/src/constants.js +188 -11
- package/dist/backend/backend/src/constants.js.map +1 -1
- package/dist/backend/backend/src/controllers/owner-receipt/owner-receipt.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/owner-receipt/owner-receipt.controller.js +5 -0
- package/dist/backend/backend/src/controllers/owner-receipt/owner-receipt.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/tickets/tickets.controller.d.ts +18 -0
- package/dist/backend/backend/src/controllers/tickets/tickets.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/tickets/tickets.controller.js +42 -0
- package/dist/backend/backend/src/controllers/tickets/tickets.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/tickets/tickets.routes.d.ts +1 -0
- package/dist/backend/backend/src/controllers/tickets/tickets.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/tickets/tickets.routes.js +3 -1
- package/dist/backend/backend/src/controllers/tickets/tickets.routes.js.map +1 -1
- package/dist/backend/backend/src/index.d.ts.map +1 -1
- package/dist/backend/backend/src/index.js +8 -2
- package/dist/backend/backend/src/index.js.map +1 -1
- package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts +9 -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 +34 -29
- package/dist/backend/backend/src/services/agent/agent-registration.service.js.map +1 -1
- package/dist/backend/backend/src/services/agent/codex-runtime.service.d.ts +4 -3
- package/dist/backend/backend/src/services/agent/codex-runtime.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/codex-runtime.service.js +48 -3
- package/dist/backend/backend/src/services/agent/codex-runtime.service.js.map +1 -1
- package/dist/backend/backend/src/services/agent/crewly-agent/in-process-log-buffer.d.ts +9 -0
- package/dist/backend/backend/src/services/agent/crewly-agent/in-process-log-buffer.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/crewly-agent/in-process-log-buffer.js +18 -1
- package/dist/backend/backend/src/services/agent/crewly-agent/in-process-log-buffer.js.map +1 -1
- package/dist/backend/backend/src/services/agent/oauth-relogin-monitor.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/oauth-relogin-monitor.service.js +5 -1
- package/dist/backend/backend/src/services/agent/oauth-relogin-monitor.service.js.map +1 -1
- package/dist/backend/backend/src/services/agent/runtime-agent.service.abstract.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/runtime-agent.service.abstract.js +14 -4
- package/dist/backend/backend/src/services/agent/runtime-agent.service.abstract.js.map +1 -1
- package/dist/backend/backend/src/services/agent/tmux-command.service.d.ts +14 -2
- package/dist/backend/backend/src/services/agent/tmux-command.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/tmux-command.service.js +24 -5
- package/dist/backend/backend/src/services/agent/tmux-command.service.js.map +1 -1
- package/dist/backend/backend/src/services/agent/tmux.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/tmux.service.js +14 -9
- package/dist/backend/backend/src/services/agent/tmux.service.js.map +1 -1
- package/dist/backend/backend/src/services/backup/backup-archive.service.d.ts +8 -0
- package/dist/backend/backend/src/services/backup/backup-archive.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/backup/backup-archive.service.js +16 -8
- package/dist/backend/backend/src/services/backup/backup-archive.service.js.map +1 -1
- package/dist/backend/backend/src/services/backup/backup.types.d.ts +19 -0
- package/dist/backend/backend/src/services/backup/backup.types.d.ts.map +1 -1
- package/dist/backend/backend/src/services/backup/backup.types.js +19 -0
- package/dist/backend/backend/src/services/backup/backup.types.js.map +1 -1
- package/dist/backend/backend/src/services/security/secret-scrub.service.d.ts +108 -0
- package/dist/backend/backend/src/services/security/secret-scrub.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/security/secret-scrub.service.js +200 -0
- package/dist/backend/backend/src/services/security/secret-scrub.service.js.map +1 -0
- package/dist/backend/backend/src/services/session/pty/pty-session-backend.d.ts +13 -9
- package/dist/backend/backend/src/services/session/pty/pty-session-backend.d.ts.map +1 -1
- package/dist/backend/backend/src/services/session/pty/pty-session-backend.js +30 -14
- package/dist/backend/backend/src/services/session/pty/pty-session-backend.js.map +1 -1
- package/dist/backend/backend/src/services/session/pty/pty-session.d.ts.map +1 -1
- package/dist/backend/backend/src/services/session/pty/pty-session.js +6 -1
- package/dist/backend/backend/src/services/session/pty/pty-session.js.map +1 -1
- package/dist/backend/backend/src/services/session/session-command-helper.d.ts.map +1 -1
- package/dist/backend/backend/src/services/session/session-command-helper.js +3 -2
- package/dist/backend/backend/src/services/session/session-command-helper.js.map +1 -1
- package/dist/backend/backend/src/services/v3/owner-receipt/owner-receipt-data.d.ts +84 -0
- package/dist/backend/backend/src/services/v3/owner-receipt/owner-receipt-data.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/owner-receipt/owner-receipt-data.js +301 -6
- package/dist/backend/backend/src/services/v3/owner-receipt/owner-receipt-data.js.map +1 -1
- package/dist/backend/backend/src/services/v3/owner-receipt/owner-receipt.boot.d.ts +8 -0
- package/dist/backend/backend/src/services/v3/owner-receipt/owner-receipt.boot.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/owner-receipt/owner-receipt.boot.js +16 -0
- package/dist/backend/backend/src/services/v3/owner-receipt/owner-receipt.boot.js.map +1 -1
- package/dist/backend/backend/src/services/v3/owner-receipt/owner-receipt.renderer.d.ts +28 -10
- package/dist/backend/backend/src/services/v3/owner-receipt/owner-receipt.renderer.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/owner-receipt/owner-receipt.renderer.js +46 -193
- package/dist/backend/backend/src/services/v3/owner-receipt/owner-receipt.renderer.js.map +1 -1
- package/dist/backend/backend/src/services/v3/owner-receipt/owner-receipt.service.d.ts +15 -2
- package/dist/backend/backend/src/services/v3/owner-receipt/owner-receipt.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/owner-receipt/owner-receipt.service.js +26 -4
- package/dist/backend/backend/src/services/v3/owner-receipt/owner-receipt.service.js.map +1 -1
- package/dist/backend/backend/src/services/v3/owner-receipt/owner-receipt.types.d.ts +35 -0
- package/dist/backend/backend/src/services/v3/owner-receipt/owner-receipt.types.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/owner-receipt/owner-receipt.types.js.map +1 -1
- package/dist/backend/backend/src/services/v3/request.service.d.ts +2 -0
- package/dist/backend/backend/src/services/v3/request.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/request.service.js +16 -4
- package/dist/backend/backend/src/services/v3/request.service.js.map +1 -1
- package/dist/backend/backend/src/services/v3/ticket-ask-classifier.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/ticket-ask-classifier.js +2 -0
- package/dist/backend/backend/src/services/v3/ticket-ask-classifier.js.map +1 -1
- package/dist/backend/backend/src/services/v3/ticket-channel-hooks.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/ticket-channel-hooks.js +2 -1
- package/dist/backend/backend/src/services/v3/ticket-channel-hooks.js.map +1 -1
- package/dist/backend/backend/src/services/v3/ticket-hygiene.d.ts +162 -0
- package/dist/backend/backend/src/services/v3/ticket-hygiene.d.ts.map +1 -0
- package/dist/backend/backend/src/services/v3/ticket-hygiene.js +221 -0
- package/dist/backend/backend/src/services/v3/ticket-hygiene.js.map +1 -0
- package/dist/backend/backend/src/services/v3/ticket-intake.service.d.ts +71 -6
- package/dist/backend/backend/src/services/v3/ticket-intake.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/ticket-intake.service.js +151 -25
- package/dist/backend/backend/src/services/v3/ticket-intake.service.js.map +1 -1
- package/dist/backend/backend/src/services/v3/ticket-review.service.d.ts +22 -3
- package/dist/backend/backend/src/services/v3/ticket-review.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/ticket-review.service.js +99 -40
- package/dist/backend/backend/src/services/v3/ticket-review.service.js.map +1 -1
- package/dist/backend/backend/src/types/team-template.types.d.ts +1 -1
- package/dist/backend/backend/src/types/team-template.types.d.ts.map +1 -1
- package/dist/backend/backend/src/types/v2/request.types.d.ts +13 -0
- package/dist/backend/backend/src/types/v2/request.types.d.ts.map +1 -1
- package/dist/backend/backend/src/types/v2/request.types.js.map +1 -1
- package/dist/backend/backend/src/utils/secret-redactor.d.ts +131 -0
- package/dist/backend/backend/src/utils/secret-redactor.d.ts.map +1 -0
- package/dist/backend/backend/src/utils/secret-redactor.js +233 -0
- package/dist/backend/backend/src/utils/secret-redactor.js.map +1 -0
- package/dist/backend/backend/src/utils/shell-history.d.ts +48 -0
- package/dist/backend/backend/src/utils/shell-history.d.ts.map +1 -0
- package/dist/backend/backend/src/utils/shell-history.js +65 -0
- package/dist/backend/backend/src/utils/shell-history.js.map +1 -0
- package/dist/backend/build-info.json +2 -2
- package/dist/cli/backend/src/constants.d.ts +161 -6
- package/dist/cli/backend/src/constants.d.ts.map +1 -1
- package/dist/cli/backend/src/constants.js +188 -11
- package/dist/cli/backend/src/constants.js.map +1 -1
- package/dist/cli/backend/src/services/agent/agent-registration.service.d.ts +9 -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 +34 -29
- package/dist/cli/backend/src/services/agent/agent-registration.service.js.map +1 -1
- package/dist/cli/backend/src/services/agent/codex-runtime.service.d.ts +4 -3
- package/dist/cli/backend/src/services/agent/codex-runtime.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/agent/codex-runtime.service.js +48 -3
- package/dist/cli/backend/src/services/agent/codex-runtime.service.js.map +1 -1
- package/dist/cli/backend/src/services/agent/crewly-agent/in-process-log-buffer.d.ts +9 -0
- package/dist/cli/backend/src/services/agent/crewly-agent/in-process-log-buffer.d.ts.map +1 -1
- package/dist/cli/backend/src/services/agent/crewly-agent/in-process-log-buffer.js +18 -1
- package/dist/cli/backend/src/services/agent/crewly-agent/in-process-log-buffer.js.map +1 -1
- package/dist/cli/backend/src/services/agent/oauth-relogin-monitor.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/agent/oauth-relogin-monitor.service.js +5 -1
- package/dist/cli/backend/src/services/agent/oauth-relogin-monitor.service.js.map +1 -1
- package/dist/cli/backend/src/services/agent/runtime-agent.service.abstract.d.ts.map +1 -1
- package/dist/cli/backend/src/services/agent/runtime-agent.service.abstract.js +14 -4
- package/dist/cli/backend/src/services/agent/runtime-agent.service.abstract.js.map +1 -1
- package/dist/cli/backend/src/services/backup/backup-archive.service.d.ts +8 -0
- package/dist/cli/backend/src/services/backup/backup-archive.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/backup/backup-archive.service.js +16 -8
- package/dist/cli/backend/src/services/backup/backup-archive.service.js.map +1 -1
- package/dist/cli/backend/src/services/backup/backup.types.d.ts +19 -0
- package/dist/cli/backend/src/services/backup/backup.types.d.ts.map +1 -1
- package/dist/cli/backend/src/services/backup/backup.types.js +19 -0
- package/dist/cli/backend/src/services/backup/backup.types.js.map +1 -1
- package/dist/cli/backend/src/services/security/secret-scrub.service.d.ts +108 -0
- package/dist/cli/backend/src/services/security/secret-scrub.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/security/secret-scrub.service.js +200 -0
- package/dist/cli/backend/src/services/security/secret-scrub.service.js.map +1 -0
- package/dist/cli/backend/src/services/session/pty/pty-session-backend.d.ts +13 -9
- package/dist/cli/backend/src/services/session/pty/pty-session-backend.d.ts.map +1 -1
- package/dist/cli/backend/src/services/session/pty/pty-session-backend.js +30 -14
- package/dist/cli/backend/src/services/session/pty/pty-session-backend.js.map +1 -1
- package/dist/cli/backend/src/services/session/pty/pty-session.d.ts.map +1 -1
- package/dist/cli/backend/src/services/session/pty/pty-session.js +6 -1
- package/dist/cli/backend/src/services/session/pty/pty-session.js.map +1 -1
- package/dist/cli/backend/src/services/session/session-command-helper.d.ts.map +1 -1
- package/dist/cli/backend/src/services/session/session-command-helper.js +3 -2
- package/dist/cli/backend/src/services/session/session-command-helper.js.map +1 -1
- package/dist/cli/backend/src/services/v3/request.service.d.ts +2 -0
- package/dist/cli/backend/src/services/v3/request.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/v3/request.service.js +16 -4
- package/dist/cli/backend/src/services/v3/request.service.js.map +1 -1
- package/dist/cli/backend/src/services/v3/ticket-ask-classifier.d.ts.map +1 -1
- package/dist/cli/backend/src/services/v3/ticket-ask-classifier.js +2 -0
- package/dist/cli/backend/src/services/v3/ticket-ask-classifier.js.map +1 -1
- package/dist/cli/backend/src/services/v3/ticket-channel-hooks.d.ts.map +1 -1
- package/dist/cli/backend/src/services/v3/ticket-channel-hooks.js +2 -1
- package/dist/cli/backend/src/services/v3/ticket-channel-hooks.js.map +1 -1
- package/dist/cli/backend/src/services/v3/ticket-hygiene.d.ts +162 -0
- package/dist/cli/backend/src/services/v3/ticket-hygiene.d.ts.map +1 -0
- package/dist/cli/backend/src/services/v3/ticket-hygiene.js +221 -0
- package/dist/cli/backend/src/services/v3/ticket-hygiene.js.map +1 -0
- package/dist/cli/backend/src/services/v3/ticket-intake.service.d.ts +71 -6
- package/dist/cli/backend/src/services/v3/ticket-intake.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/v3/ticket-intake.service.js +151 -25
- package/dist/cli/backend/src/services/v3/ticket-intake.service.js.map +1 -1
- package/dist/cli/backend/src/services/v3/ticket-review.service.d.ts +22 -3
- package/dist/cli/backend/src/services/v3/ticket-review.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/v3/ticket-review.service.js +99 -40
- package/dist/cli/backend/src/services/v3/ticket-review.service.js.map +1 -1
- package/dist/cli/backend/src/types/team-template.types.d.ts +1 -1
- package/dist/cli/backend/src/types/team-template.types.d.ts.map +1 -1
- package/dist/cli/backend/src/types/v2/request.types.d.ts +13 -0
- package/dist/cli/backend/src/types/v2/request.types.d.ts.map +1 -1
- package/dist/cli/backend/src/types/v2/request.types.js.map +1 -1
- package/dist/cli/backend/src/utils/secret-redactor.d.ts +131 -0
- package/dist/cli/backend/src/utils/secret-redactor.d.ts.map +1 -0
- package/dist/cli/backend/src/utils/secret-redactor.js +233 -0
- package/dist/cli/backend/src/utils/secret-redactor.js.map +1 -0
- package/dist/cli/backend/src/utils/shell-history.d.ts +48 -0
- package/dist/cli/backend/src/utils/shell-history.d.ts.map +1 -0
- package/dist/cli/backend/src/utils/shell-history.js +65 -0
- package/dist/cli/backend/src/utils/shell-history.js.map +1 -0
- package/dist/cli/cli/src/commands/security.d.ts +41 -0
- package/dist/cli/cli/src/commands/security.d.ts.map +1 -0
- package/dist/cli/cli/src/commands/security.js +81 -0
- package/dist/cli/cli/src/commands/security.js.map +1 -0
- package/dist/cli/cli/src/index.js +9 -0
- package/dist/cli/cli/src/index.js.map +1 -1
- package/frontend/dist/assets/index-edd6f262.css +42 -0
- package/frontend/dist/assets/{index-8d4b3ad2.js → index-f79676aa.js} +268 -268
- package/frontend/dist/index.html +2 -2
- package/package.json +1 -1
- package/frontend/dist/assets/index-b6aded4c.css +0 -42
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secret redactor for persisted terminal output.
|
|
3
|
+
*
|
|
4
|
+
* Every byte of agent PTY output that Crewly writes to disk
|
|
5
|
+
* (~/.crewly/logs/sessions/*.log) passes through here, and so do the
|
|
6
|
+
* one-time scrub of existing logs and shell history files
|
|
7
|
+
* (`crewly security scrub-logs`). Three layers, applied in this order:
|
|
8
|
+
*
|
|
9
|
+
* 1. **Known values** — the exact value of every secret Crewly holds for the
|
|
10
|
+
* session (backend env + the session's spawn env, see
|
|
11
|
+
* collectSecretEnvValues). Catches secrets no pattern recognises (a Slack
|
|
12
|
+
* signing secret is plain hex). Masked as `[REDACTED <NAME>]`.
|
|
13
|
+
* 2. **Secret-named assignments** — `NAME=value` where NAME is a secret name
|
|
14
|
+
* (isSecretEnvKey: `…API_KEY`, `…TOKEN`, `…SECRET`, `…PASSWORD`,
|
|
15
|
+
* `…PRIVATE_KEY`, … plus SECRET_REDACTION_CONSTANTS.KNOWN_SECRET_ENV_NAMES).
|
|
16
|
+
* The value is masked whatever it looks like. Names are matched on the
|
|
17
|
+
* whole identifier, so `TOKEN_COUNT=5` or `MAX_TOKENS=4096` are left alone
|
|
18
|
+
* (the name does not END in a secret suffix); `$VAR` references and
|
|
19
|
+
* already-masked values are left alone too.
|
|
20
|
+
* 3. **Token shapes** — well-known raw credential formats wherever they
|
|
21
|
+
* appear: Slack `xoxb-/xoxp-/xoxa-/xoxe-/xapp-…`, OpenAI/DeepSeek `sk-…`,
|
|
22
|
+
* Anthropic `sk-ant-…`, Google `AIza…`, GitHub `ghp_/gho_/…` and
|
|
23
|
+
* `github_pat_…`, plus the wiki confidentiality patterns (JWTs, bearer
|
|
24
|
+
* headers, private-key blocks, connection strings).
|
|
25
|
+
*
|
|
26
|
+
* Redaction is idempotent: running it over already-redacted text changes
|
|
27
|
+
* nothing and reports a count of 0.
|
|
28
|
+
*
|
|
29
|
+
* Output is streamed in arbitrary chunks, so a secret can be split between
|
|
30
|
+
* two chunks. StreamingSecretRedactor holds back the tail after the last
|
|
31
|
+
* whitespace (secrets never contain whitespace) and prepends it to the next
|
|
32
|
+
* chunk, which keeps the whole thing linear with a small bounded carry.
|
|
33
|
+
*
|
|
34
|
+
* @module utils/secret-redactor
|
|
35
|
+
*/
|
|
36
|
+
import { type SecretEnvValue } from './secret-env.js';
|
|
37
|
+
/**
|
|
38
|
+
* Whether an assignment's name marks its value as secret.
|
|
39
|
+
*
|
|
40
|
+
* @param name - Variable name as it appeared (any case)
|
|
41
|
+
* @returns True for secret names (see module docs)
|
|
42
|
+
*/
|
|
43
|
+
export declare function isSecretAssignmentName(name: string): boolean;
|
|
44
|
+
/** Result of a counted redaction. */
|
|
45
|
+
export interface RedactionResult {
|
|
46
|
+
/** The masked text */
|
|
47
|
+
text: string;
|
|
48
|
+
/** Number of secrets masked (0 when the text was already clean) */
|
|
49
|
+
count: number;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Masks every secret in a complete piece of text and counts what was masked.
|
|
53
|
+
*
|
|
54
|
+
* @param text - Text to mask
|
|
55
|
+
* @param knownSecrets - Exact secret values to mask (see collectSecretEnvValues)
|
|
56
|
+
* @returns Masked text and the number of secrets masked
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```ts
|
|
60
|
+
* redactSecretsWithCount('export OPENAI_API_KEY=sk-abc…').count; // 1
|
|
61
|
+
* redactSecretsWithCount('TOKEN_COUNT=5').count; // 0
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export declare function redactSecretsWithCount(text: string, knownSecrets?: readonly SecretEnvValue[]): RedactionResult;
|
|
65
|
+
/**
|
|
66
|
+
* Masks every secret in a complete piece of text.
|
|
67
|
+
*
|
|
68
|
+
* @param text - Text to mask
|
|
69
|
+
* @param knownSecrets - Exact secret values to mask
|
|
70
|
+
* @returns The masked text
|
|
71
|
+
*/
|
|
72
|
+
export declare function redactSecrets(text: string, knownSecrets?: readonly SecretEnvValue[]): string;
|
|
73
|
+
/**
|
|
74
|
+
* Chunk-boundary-safe redactor for streamed output.
|
|
75
|
+
*
|
|
76
|
+
* `push()` returns the redacted text that is safe to emit now and holds back
|
|
77
|
+
* the tail after the last whitespace; `flush()` returns whatever is held back
|
|
78
|
+
* (call it when the stream closes). Only the new chunk is scanned for
|
|
79
|
+
* whitespace (the carry never contains any), so work is linear in the input.
|
|
80
|
+
* A run of more than MAX_CARRY_CHARS non-whitespace characters is cut at a
|
|
81
|
+
* non-token character near its end (or hard-cut when there is none), keeping
|
|
82
|
+
* the carry bounded.
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```ts
|
|
86
|
+
* const r = new StreamingSecretRedactor();
|
|
87
|
+
* log.write(r.push('export GEMINI_API_KEY=AIzaSy'));
|
|
88
|
+
* log.write(r.push('…rest of key\n'));
|
|
89
|
+
* log.write(r.flush());
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
export declare class StreamingSecretRedactor {
|
|
93
|
+
private carry;
|
|
94
|
+
private knownSecrets;
|
|
95
|
+
private readonly maxCarry;
|
|
96
|
+
private readonly minCarry;
|
|
97
|
+
/**
|
|
98
|
+
* @param knownSecrets - Exact secret values to mask
|
|
99
|
+
* @param maxCarry - Longest whitespace-free tail held back before forcing a cut
|
|
100
|
+
*/
|
|
101
|
+
constructor(knownSecrets?: readonly SecretEnvValue[], maxCarry?: number);
|
|
102
|
+
/**
|
|
103
|
+
* Replaces the exact-value secret set (e.g. after the session's env changes).
|
|
104
|
+
*
|
|
105
|
+
* @param knownSecrets - Exact secret values to mask
|
|
106
|
+
*/
|
|
107
|
+
setKnownSecrets(knownSecrets: readonly SecretEnvValue[]): void;
|
|
108
|
+
/**
|
|
109
|
+
* Feeds one chunk and returns the redacted text that can be emitted now.
|
|
110
|
+
*
|
|
111
|
+
* @param chunk - Next piece of output
|
|
112
|
+
* @returns Redacted text (possibly empty while a word is still arriving)
|
|
113
|
+
*/
|
|
114
|
+
push(chunk: string): string;
|
|
115
|
+
/**
|
|
116
|
+
* Returns (redacted) whatever is still held back and resets the carry.
|
|
117
|
+
*
|
|
118
|
+
* @returns Remaining redacted text
|
|
119
|
+
*/
|
|
120
|
+
flush(): string;
|
|
121
|
+
/**
|
|
122
|
+
* Picks a cut point for a whitespace-free run that outgrew the carry: the
|
|
123
|
+
* last non-token character before the final minCarry characters, so a
|
|
124
|
+
* credential near the end is not split; hard cut when there is none.
|
|
125
|
+
*
|
|
126
|
+
* @param buf - Buffered text whose tail has no whitespace
|
|
127
|
+
* @returns Index to cut at
|
|
128
|
+
*/
|
|
129
|
+
private forcedCut;
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=secret-redactor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secret-redactor.d.ts","sourceRoot":"","sources":["../../../../../backend/src/utils/secret-redactor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAIH,OAAO,EAAkB,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AA6CtE;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAO5D;AAED,qCAAqC;AACrC,MAAM,WAAW,eAAe;IAC/B,sBAAsB;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,mEAAmE;IACnE,KAAK,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,GAAE,SAAS,cAAc,EAAO,GAAG,eAAe,CA2BlH;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,GAAE,SAAS,cAAc,EAAO,GAAG,MAAM,CAEhG;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,uBAAuB;IACnC,OAAO,CAAC,KAAK,CAAM;IACnB,OAAO,CAAC,YAAY,CAA4B;IAChD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAElC;;;OAGG;gBAEF,YAAY,GAAE,SAAS,cAAc,EAAO,EAC5C,QAAQ,GAAE,MAAmD;IAO9D;;;;OAIG;IACH,eAAe,CAAC,YAAY,EAAE,SAAS,cAAc,EAAE,GAAG,IAAI;IAI9D;;;;;OAKG;IACH,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAyB3B;;;;OAIG;IACH,KAAK,IAAI,MAAM;IAMf;;;;;;;OAOG;IACH,OAAO,CAAC,SAAS;CAQjB"}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secret redactor for persisted terminal output.
|
|
3
|
+
*
|
|
4
|
+
* Every byte of agent PTY output that Crewly writes to disk
|
|
5
|
+
* (~/.crewly/logs/sessions/*.log) passes through here, and so do the
|
|
6
|
+
* one-time scrub of existing logs and shell history files
|
|
7
|
+
* (`crewly security scrub-logs`). Three layers, applied in this order:
|
|
8
|
+
*
|
|
9
|
+
* 1. **Known values** — the exact value of every secret Crewly holds for the
|
|
10
|
+
* session (backend env + the session's spawn env, see
|
|
11
|
+
* collectSecretEnvValues). Catches secrets no pattern recognises (a Slack
|
|
12
|
+
* signing secret is plain hex). Masked as `[REDACTED <NAME>]`.
|
|
13
|
+
* 2. **Secret-named assignments** — `NAME=value` where NAME is a secret name
|
|
14
|
+
* (isSecretEnvKey: `…API_KEY`, `…TOKEN`, `…SECRET`, `…PASSWORD`,
|
|
15
|
+
* `…PRIVATE_KEY`, … plus SECRET_REDACTION_CONSTANTS.KNOWN_SECRET_ENV_NAMES).
|
|
16
|
+
* The value is masked whatever it looks like. Names are matched on the
|
|
17
|
+
* whole identifier, so `TOKEN_COUNT=5` or `MAX_TOKENS=4096` are left alone
|
|
18
|
+
* (the name does not END in a secret suffix); `$VAR` references and
|
|
19
|
+
* already-masked values are left alone too.
|
|
20
|
+
* 3. **Token shapes** — well-known raw credential formats wherever they
|
|
21
|
+
* appear: Slack `xoxb-/xoxp-/xoxa-/xoxe-/xapp-…`, OpenAI/DeepSeek `sk-…`,
|
|
22
|
+
* Anthropic `sk-ant-…`, Google `AIza…`, GitHub `ghp_/gho_/…` and
|
|
23
|
+
* `github_pat_…`, plus the wiki confidentiality patterns (JWTs, bearer
|
|
24
|
+
* headers, private-key blocks, connection strings).
|
|
25
|
+
*
|
|
26
|
+
* Redaction is idempotent: running it over already-redacted text changes
|
|
27
|
+
* nothing and reports a count of 0.
|
|
28
|
+
*
|
|
29
|
+
* Output is streamed in arbitrary chunks, so a secret can be split between
|
|
30
|
+
* two chunks. StreamingSecretRedactor holds back the tail after the last
|
|
31
|
+
* whitespace (secrets never contain whitespace) and prepends it to the next
|
|
32
|
+
* chunk, which keeps the whole thing linear with a small bounded carry.
|
|
33
|
+
*
|
|
34
|
+
* @module utils/secret-redactor
|
|
35
|
+
*/
|
|
36
|
+
import { SECRET_REDACTION_CONSTANTS } from '../constants.js';
|
|
37
|
+
import { SECRET_PATTERNS } from '../services/wiki/wiki-redaction.js';
|
|
38
|
+
import { isSecretEnvKey } from './secret-env.js';
|
|
39
|
+
/**
|
|
40
|
+
* Raw token shapes beyond the wiki SECRET_PATTERNS (which require a numeric
|
|
41
|
+
* team segment for Slack tokens and do not know `xapp-` or `github_pat_`).
|
|
42
|
+
*/
|
|
43
|
+
const EXTRA_TOKEN_PATTERNS = [
|
|
44
|
+
{ name: 'slack_token', regex: /\bxox[abposre]-[A-Za-z0-9-]{10,}/g, mask: '[REDACTED slack_token]' },
|
|
45
|
+
{ name: 'slack_app_token', regex: /\bxapp-[A-Za-z0-9-]{10,}/g, mask: '[REDACTED slack_app_token]' },
|
|
46
|
+
{ name: 'anthropic_key', regex: /\bsk-ant-[A-Za-z0-9_-]{20,}/g, mask: '[REDACTED api_key]' },
|
|
47
|
+
{ name: 'github_pat', regex: /\bgithub_pat_[A-Za-z0-9_]{20,}/g, mask: '[REDACTED github_token]' },
|
|
48
|
+
];
|
|
49
|
+
/** Every token-shape detector, most specific first. */
|
|
50
|
+
const TOKEN_PATTERNS = [...EXTRA_TOKEN_PATTERNS, ...SECRET_PATTERNS];
|
|
51
|
+
/**
|
|
52
|
+
* `NAME=value` (optionally quoted value). The value stops at whitespace,
|
|
53
|
+
* quotes, backticks and shell separators. The identifier must start at a
|
|
54
|
+
* word boundary so it is matched whole.
|
|
55
|
+
*/
|
|
56
|
+
const ASSIGNMENT_RE = /\b([A-Za-z_][A-Za-z0-9_]*)=(["']?)([^\s"'`;&|]+)/g;
|
|
57
|
+
/** Prefix every mask this module emits starts with. */
|
|
58
|
+
const REDACTED_PREFIX = '[REDACTED';
|
|
59
|
+
/** Mask written in place of a secret-named assignment's value. */
|
|
60
|
+
const ASSIGNMENT_MASK = '[REDACTED]';
|
|
61
|
+
/** Characters that can appear inside a credential or a `NAME=value` pair. */
|
|
62
|
+
const TOKEN_CHAR_RE = /[A-Za-z0-9_\-=.:/+"'[\]]/;
|
|
63
|
+
/** Known secret names, upper-cased, for O(1) lookup. */
|
|
64
|
+
const KNOWN_SECRET_NAMES = new Set(SECRET_REDACTION_CONSTANTS.KNOWN_SECRET_ENV_NAMES);
|
|
65
|
+
/**
|
|
66
|
+
* Whether an assignment's name marks its value as secret.
|
|
67
|
+
*
|
|
68
|
+
* @param name - Variable name as it appeared (any case)
|
|
69
|
+
* @returns True for secret names (see module docs)
|
|
70
|
+
*/
|
|
71
|
+
export function isSecretAssignmentName(name) {
|
|
72
|
+
const upper = name.toUpperCase();
|
|
73
|
+
return (KNOWN_SECRET_NAMES.has(upper) ||
|
|
74
|
+
isSecretEnvKey(name) ||
|
|
75
|
+
SECRET_REDACTION_CONSTANTS.SECRET_NAME_PATTERNS.some((re) => re.test(upper)));
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Masks every secret in a complete piece of text and counts what was masked.
|
|
79
|
+
*
|
|
80
|
+
* @param text - Text to mask
|
|
81
|
+
* @param knownSecrets - Exact secret values to mask (see collectSecretEnvValues)
|
|
82
|
+
* @returns Masked text and the number of secrets masked
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```ts
|
|
86
|
+
* redactSecretsWithCount('export OPENAI_API_KEY=sk-abc…').count; // 1
|
|
87
|
+
* redactSecretsWithCount('TOKEN_COUNT=5').count; // 0
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
export function redactSecretsWithCount(text, knownSecrets = []) {
|
|
91
|
+
let out = text;
|
|
92
|
+
let count = 0;
|
|
93
|
+
for (const { name, value } of knownSecrets) {
|
|
94
|
+
if (!value || !out.includes(value))
|
|
95
|
+
continue;
|
|
96
|
+
const parts = out.split(value);
|
|
97
|
+
count += parts.length - 1;
|
|
98
|
+
out = parts.join(`${REDACTED_PREFIX} ${name}]`);
|
|
99
|
+
}
|
|
100
|
+
out = out.replace(ASSIGNMENT_RE, (match, name, quote, value) => {
|
|
101
|
+
if (!isSecretAssignmentName(name) || value.startsWith('$') || value.startsWith(REDACTED_PREFIX))
|
|
102
|
+
return match;
|
|
103
|
+
count++;
|
|
104
|
+
return `${name}=${quote}${ASSIGNMENT_MASK}`;
|
|
105
|
+
});
|
|
106
|
+
for (const p of TOKEN_PATTERNS) {
|
|
107
|
+
p.regex.lastIndex = 0;
|
|
108
|
+
out = out.replace(p.regex, (match) => {
|
|
109
|
+
if (match === p.mask)
|
|
110
|
+
return match;
|
|
111
|
+
count++;
|
|
112
|
+
return p.mask;
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
return { text: out, count };
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Masks every secret in a complete piece of text.
|
|
119
|
+
*
|
|
120
|
+
* @param text - Text to mask
|
|
121
|
+
* @param knownSecrets - Exact secret values to mask
|
|
122
|
+
* @returns The masked text
|
|
123
|
+
*/
|
|
124
|
+
export function redactSecrets(text, knownSecrets = []) {
|
|
125
|
+
return redactSecretsWithCount(text, knownSecrets).text;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Chunk-boundary-safe redactor for streamed output.
|
|
129
|
+
*
|
|
130
|
+
* `push()` returns the redacted text that is safe to emit now and holds back
|
|
131
|
+
* the tail after the last whitespace; `flush()` returns whatever is held back
|
|
132
|
+
* (call it when the stream closes). Only the new chunk is scanned for
|
|
133
|
+
* whitespace (the carry never contains any), so work is linear in the input.
|
|
134
|
+
* A run of more than MAX_CARRY_CHARS non-whitespace characters is cut at a
|
|
135
|
+
* non-token character near its end (or hard-cut when there is none), keeping
|
|
136
|
+
* the carry bounded.
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* ```ts
|
|
140
|
+
* const r = new StreamingSecretRedactor();
|
|
141
|
+
* log.write(r.push('export GEMINI_API_KEY=AIzaSy'));
|
|
142
|
+
* log.write(r.push('…rest of key\n'));
|
|
143
|
+
* log.write(r.flush());
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
146
|
+
export class StreamingSecretRedactor {
|
|
147
|
+
carry = '';
|
|
148
|
+
knownSecrets;
|
|
149
|
+
maxCarry;
|
|
150
|
+
minCarry;
|
|
151
|
+
/**
|
|
152
|
+
* @param knownSecrets - Exact secret values to mask
|
|
153
|
+
* @param maxCarry - Longest whitespace-free tail held back before forcing a cut
|
|
154
|
+
*/
|
|
155
|
+
constructor(knownSecrets = [], maxCarry = SECRET_REDACTION_CONSTANTS.MAX_CARRY_CHARS) {
|
|
156
|
+
this.knownSecrets = knownSecrets;
|
|
157
|
+
this.maxCarry = Math.max(maxCarry, 2);
|
|
158
|
+
this.minCarry = Math.max(1, Math.min(SECRET_REDACTION_CONSTANTS.MIN_FORCED_CARRY_CHARS, Math.floor(this.maxCarry / 2)));
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Replaces the exact-value secret set (e.g. after the session's env changes).
|
|
162
|
+
*
|
|
163
|
+
* @param knownSecrets - Exact secret values to mask
|
|
164
|
+
*/
|
|
165
|
+
setKnownSecrets(knownSecrets) {
|
|
166
|
+
this.knownSecrets = knownSecrets;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Feeds one chunk and returns the redacted text that can be emitted now.
|
|
170
|
+
*
|
|
171
|
+
* @param chunk - Next piece of output
|
|
172
|
+
* @returns Redacted text (possibly empty while a word is still arriving)
|
|
173
|
+
*/
|
|
174
|
+
push(chunk) {
|
|
175
|
+
if (!chunk)
|
|
176
|
+
return '';
|
|
177
|
+
const carryLength = this.carry.length;
|
|
178
|
+
const buf = this.carry + chunk;
|
|
179
|
+
let cut = -1;
|
|
180
|
+
for (let i = buf.length - 1; i >= carryLength; i--) {
|
|
181
|
+
if (isWhitespace(buf.charCodeAt(i))) {
|
|
182
|
+
cut = i + 1;
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (cut === -1 || buf.length - cut > this.maxCarry) {
|
|
187
|
+
if (buf.length <= this.maxCarry) {
|
|
188
|
+
this.carry = buf;
|
|
189
|
+
return '';
|
|
190
|
+
}
|
|
191
|
+
cut = this.forcedCut(buf);
|
|
192
|
+
}
|
|
193
|
+
this.carry = buf.slice(cut);
|
|
194
|
+
return cut > 0 ? redactSecrets(buf.slice(0, cut), this.knownSecrets) : '';
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Returns (redacted) whatever is still held back and resets the carry.
|
|
198
|
+
*
|
|
199
|
+
* @returns Remaining redacted text
|
|
200
|
+
*/
|
|
201
|
+
flush() {
|
|
202
|
+
const rest = this.carry;
|
|
203
|
+
this.carry = '';
|
|
204
|
+
return rest ? redactSecrets(rest, this.knownSecrets) : '';
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Picks a cut point for a whitespace-free run that outgrew the carry: the
|
|
208
|
+
* last non-token character before the final minCarry characters, so a
|
|
209
|
+
* credential near the end is not split; hard cut when there is none.
|
|
210
|
+
*
|
|
211
|
+
* @param buf - Buffered text whose tail has no whitespace
|
|
212
|
+
* @returns Index to cut at
|
|
213
|
+
*/
|
|
214
|
+
forcedCut(buf) {
|
|
215
|
+
const target = buf.length - this.minCarry;
|
|
216
|
+
const floor = Math.max(0, target - this.minCarry);
|
|
217
|
+
for (let i = target; i > floor; i--) {
|
|
218
|
+
if (!TOKEN_CHAR_RE.test(buf[i - 1]))
|
|
219
|
+
return i;
|
|
220
|
+
}
|
|
221
|
+
return target;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Whitespace test on a UTF-16 code unit (space, tab, CR, LF, VT, FF).
|
|
226
|
+
*
|
|
227
|
+
* @param code - Character code
|
|
228
|
+
* @returns True for ASCII whitespace
|
|
229
|
+
*/
|
|
230
|
+
function isWhitespace(code) {
|
|
231
|
+
return code === 32 || (code >= 9 && code <= 13);
|
|
232
|
+
}
|
|
233
|
+
//# sourceMappingURL=secret-redactor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secret-redactor.js","sourceRoot":"","sources":["../../../../../backend/src/utils/secret-redactor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keeps agent shells out of the user's shell history.
|
|
3
|
+
*
|
|
4
|
+
* Crewly used to type `export GEMINI_API_KEY=…` into agent shells, and bash
|
|
5
|
+
* wrote those lines to ~/.bash_history hundreds of times. Secrets now travel
|
|
6
|
+
* only in the spawn env (utils/secret-env), and on top of that agent shells
|
|
7
|
+
* never write a history file at all:
|
|
8
|
+
*
|
|
9
|
+
* - historyOffSpawnEnv() goes into every PTY's spawn environment;
|
|
10
|
+
* - shellHistoryDisableLine() is typed first by the runtime init sequence,
|
|
11
|
+
* after the rc files ran (they may set HISTFILE themselves);
|
|
12
|
+
* - quietShellLine() prefixes every other typed line with a space, which
|
|
13
|
+
* bash (HISTCONTROL=ignorespace) and zsh (HIST_IGNORE_SPACE) skip.
|
|
14
|
+
*
|
|
15
|
+
* See SHELL_HISTORY_CONSTANTS for why HISTSIZE/HISTFILESIZE are not touched.
|
|
16
|
+
*
|
|
17
|
+
* @module utils/shell-history
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Env vars that keep a freshly spawned bash/zsh from writing history.
|
|
21
|
+
*
|
|
22
|
+
* @returns A fresh copy (callers may spread or mutate it)
|
|
23
|
+
*/
|
|
24
|
+
export declare function historyOffSpawnEnv(): Record<string, string>;
|
|
25
|
+
/**
|
|
26
|
+
* Prefixes a line typed into a shell with a space so it is not recorded.
|
|
27
|
+
* Already-prefixed lines are returned unchanged.
|
|
28
|
+
*
|
|
29
|
+
* @param line - Shell command line
|
|
30
|
+
* @returns The line, space-prefixed
|
|
31
|
+
*/
|
|
32
|
+
export declare function quietShellLine(line: string): string;
|
|
33
|
+
/**
|
|
34
|
+
* The (space-prefixed) line that turns history off inside a running shell.
|
|
35
|
+
*
|
|
36
|
+
* @param shell - Shell binary path (defaults to $SHELL, then /bin/bash — what
|
|
37
|
+
* agent PTYs are spawned with)
|
|
38
|
+
* @returns The line to type, or null for a shell we do not know how to quiet
|
|
39
|
+
*/
|
|
40
|
+
export declare function shellHistoryDisableLine(shell?: string | undefined): string | null;
|
|
41
|
+
/**
|
|
42
|
+
* Quotes a value for a POSIX shell word (single quotes).
|
|
43
|
+
*
|
|
44
|
+
* @param value - Raw value
|
|
45
|
+
* @returns Single-quoted value safe for `sh -c`
|
|
46
|
+
*/
|
|
47
|
+
export declare function shellQuote(value: string): string;
|
|
48
|
+
//# sourceMappingURL=shell-history.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell-history.d.ts","sourceRoot":"","sources":["../../../../../backend/src/utils/shell-history.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAKH;;;;GAIG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAE3D;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGnD;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,GAAE,MAAM,GAAG,SAA4C,GAAG,MAAM,GAAG,IAAI,CAOnH;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhD"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keeps agent shells out of the user's shell history.
|
|
3
|
+
*
|
|
4
|
+
* Crewly used to type `export GEMINI_API_KEY=…` into agent shells, and bash
|
|
5
|
+
* wrote those lines to ~/.bash_history hundreds of times. Secrets now travel
|
|
6
|
+
* only in the spawn env (utils/secret-env), and on top of that agent shells
|
|
7
|
+
* never write a history file at all:
|
|
8
|
+
*
|
|
9
|
+
* - historyOffSpawnEnv() goes into every PTY's spawn environment;
|
|
10
|
+
* - shellHistoryDisableLine() is typed first by the runtime init sequence,
|
|
11
|
+
* after the rc files ran (they may set HISTFILE themselves);
|
|
12
|
+
* - quietShellLine() prefixes every other typed line with a space, which
|
|
13
|
+
* bash (HISTCONTROL=ignorespace) and zsh (HIST_IGNORE_SPACE) skip.
|
|
14
|
+
*
|
|
15
|
+
* See SHELL_HISTORY_CONSTANTS for why HISTSIZE/HISTFILESIZE are not touched.
|
|
16
|
+
*
|
|
17
|
+
* @module utils/shell-history
|
|
18
|
+
*/
|
|
19
|
+
import * as path from 'path';
|
|
20
|
+
import { SHELL_HISTORY_CONSTANTS } from '../constants.js';
|
|
21
|
+
/**
|
|
22
|
+
* Env vars that keep a freshly spawned bash/zsh from writing history.
|
|
23
|
+
*
|
|
24
|
+
* @returns A fresh copy (callers may spread or mutate it)
|
|
25
|
+
*/
|
|
26
|
+
export function historyOffSpawnEnv() {
|
|
27
|
+
return { ...SHELL_HISTORY_CONSTANTS.SPAWN_ENV };
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Prefixes a line typed into a shell with a space so it is not recorded.
|
|
31
|
+
* Already-prefixed lines are returned unchanged.
|
|
32
|
+
*
|
|
33
|
+
* @param line - Shell command line
|
|
34
|
+
* @returns The line, space-prefixed
|
|
35
|
+
*/
|
|
36
|
+
export function quietShellLine(line) {
|
|
37
|
+
const prefix = SHELL_HISTORY_CONSTANTS.TYPED_LINE_PREFIX;
|
|
38
|
+
return line.startsWith(prefix) ? line : `${prefix}${line}`;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The (space-prefixed) line that turns history off inside a running shell.
|
|
42
|
+
*
|
|
43
|
+
* @param shell - Shell binary path (defaults to $SHELL, then /bin/bash — what
|
|
44
|
+
* agent PTYs are spawned with)
|
|
45
|
+
* @returns The line to type, or null for a shell we do not know how to quiet
|
|
46
|
+
*/
|
|
47
|
+
export function shellHistoryDisableLine(shell = process.env.SHELL || '/bin/bash') {
|
|
48
|
+
const name = path.basename(shell || '').toLowerCase();
|
|
49
|
+
if (SHELL_HISTORY_CONSTANTS.POSIX_SHELLS.includes(name)) {
|
|
50
|
+
return quietShellLine(SHELL_HISTORY_CONSTANTS.DISABLE_COMMAND);
|
|
51
|
+
}
|
|
52
|
+
if (name === 'fish')
|
|
53
|
+
return quietShellLine(SHELL_HISTORY_CONSTANTS.FISH_DISABLE_COMMAND);
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Quotes a value for a POSIX shell word (single quotes).
|
|
58
|
+
*
|
|
59
|
+
* @param value - Raw value
|
|
60
|
+
* @returns Single-quoted value safe for `sh -c`
|
|
61
|
+
*/
|
|
62
|
+
export function shellQuote(value) {
|
|
63
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=shell-history.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell-history.js","sourceRoot":"","sources":["../../../../../backend/src/utils/shell-history.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAE1D;;;;GAIG;AACH,MAAM,UAAU,kBAAkB;IACjC,OAAO,EAAE,GAAG,uBAAuB,CAAC,SAAS,EAAE,CAAC;AACjD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY;IAC1C,MAAM,MAAM,GAAG,uBAAuB,CAAC,iBAAiB,CAAC;IACzD,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,MAAM,GAAG,IAAI,EAAE,CAAC;AAC5D,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,uBAAuB,CAAC,QAA4B,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,WAAW;IACnG,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IACtD,IAAI,uBAAuB,CAAC,YAAY,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACzD,OAAO,cAAc,CAAC,uBAAuB,CAAC,eAAe,CAAC,CAAC;IAChE,CAAC;IACD,IAAI,IAAI,KAAK,MAAM;QAAE,OAAO,cAAc,CAAC,uBAAuB,CAAC,oBAAoB,CAAC,CAAC;IACzF,OAAO,IAAI,CAAC;AACb,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,KAAa;IACvC,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC;AAC5C,CAAC"}
|