@mafdet/cli 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/LICENSE +21 -0
  3. package/README.md +173 -0
  4. package/README.zh-CN.md +148 -0
  5. package/dist/agent/approvals.d.ts +38 -0
  6. package/dist/agent/approvals.d.ts.map +1 -0
  7. package/dist/agent/approvals.js +53 -0
  8. package/dist/agent/command-policy.d.ts +36 -0
  9. package/dist/agent/command-policy.d.ts.map +1 -0
  10. package/dist/agent/command-policy.js +133 -0
  11. package/dist/agent/loop.d.ts +107 -0
  12. package/dist/agent/loop.d.ts.map +1 -0
  13. package/dist/agent/loop.js +242 -0
  14. package/dist/agent/session.d.ts +62 -0
  15. package/dist/agent/session.d.ts.map +1 -0
  16. package/dist/agent/session.js +290 -0
  17. package/dist/agent/tools/read-only.d.ts +15 -0
  18. package/dist/agent/tools/read-only.d.ts.map +1 -0
  19. package/dist/agent/tools/read-only.js +254 -0
  20. package/dist/agent/tools/run-command.d.ts +20 -0
  21. package/dist/agent/tools/run-command.d.ts.map +1 -0
  22. package/dist/agent/tools/run-command.js +116 -0
  23. package/dist/agent/tools/types.d.ts +46 -0
  24. package/dist/agent/tools/types.d.ts.map +1 -0
  25. package/dist/agent/tools/types.js +16 -0
  26. package/dist/agent/tools/write.d.ts +3 -0
  27. package/dist/agent/tools/write.d.ts.map +1 -0
  28. package/dist/agent/tools/write.js +68 -0
  29. package/dist/api/account.d.ts +39 -0
  30. package/dist/api/account.d.ts.map +1 -0
  31. package/dist/api/account.js +39 -0
  32. package/dist/api/errors.d.ts +38 -0
  33. package/dist/api/errors.d.ts.map +1 -0
  34. package/dist/api/errors.js +149 -0
  35. package/dist/api/http.d.ts +63 -0
  36. package/dist/api/http.d.ts.map +1 -0
  37. package/dist/api/http.js +207 -0
  38. package/dist/api/models.d.ts +68 -0
  39. package/dist/api/models.d.ts.map +1 -0
  40. package/dist/api/models.js +91 -0
  41. package/dist/api/sse-parser.d.ts +72 -0
  42. package/dist/api/sse-parser.d.ts.map +1 -0
  43. package/dist/api/sse-parser.js +163 -0
  44. package/dist/commands/account.d.ts +4 -0
  45. package/dist/commands/account.d.ts.map +1 -0
  46. package/dist/commands/account.js +103 -0
  47. package/dist/commands/agent.d.ts +10 -0
  48. package/dist/commands/agent.d.ts.map +1 -0
  49. package/dist/commands/agent.js +268 -0
  50. package/dist/commands/chat.d.ts +13 -0
  51. package/dist/commands/chat.d.ts.map +1 -0
  52. package/dist/commands/chat.js +191 -0
  53. package/dist/commands/config.d.ts +15 -0
  54. package/dist/commands/config.d.ts.map +1 -0
  55. package/dist/commands/config.js +74 -0
  56. package/dist/commands/doctor.d.ts +15 -0
  57. package/dist/commands/doctor.d.ts.map +1 -0
  58. package/dist/commands/doctor.js +157 -0
  59. package/dist/commands/embed.d.ts +7 -0
  60. package/dist/commands/embed.d.ts.map +1 -0
  61. package/dist/commands/embed.js +105 -0
  62. package/dist/commands/models.d.ts +4 -0
  63. package/dist/commands/models.d.ts.map +1 -0
  64. package/dist/commands/models.js +151 -0
  65. package/dist/commands/runtime.d.ts +29 -0
  66. package/dist/commands/runtime.d.ts.map +1 -0
  67. package/dist/commands/runtime.js +56 -0
  68. package/dist/commands/speech.d.ts +7 -0
  69. package/dist/commands/speech.d.ts.map +1 -0
  70. package/dist/commands/speech.js +111 -0
  71. package/dist/config/config.d.ts +57 -0
  72. package/dist/config/config.d.ts.map +1 -0
  73. package/dist/config/config.js +129 -0
  74. package/dist/config/load.d.ts +20 -0
  75. package/dist/config/load.d.ts.map +1 -0
  76. package/dist/config/load.js +48 -0
  77. package/dist/config/paths.d.ts +11 -0
  78. package/dist/config/paths.d.ts.map +1 -0
  79. package/dist/config/paths.js +26 -0
  80. package/dist/filesystem/diff.d.ts +12 -0
  81. package/dist/filesystem/diff.d.ts.map +1 -0
  82. package/dist/filesystem/diff.js +109 -0
  83. package/dist/filesystem/sensitive-files.d.ts +19 -0
  84. package/dist/filesystem/sensitive-files.d.ts.map +1 -0
  85. package/dist/filesystem/sensitive-files.js +46 -0
  86. package/dist/filesystem/workspace.d.ts +92 -0
  87. package/dist/filesystem/workspace.d.ts.map +1 -0
  88. package/dist/filesystem/workspace.js +264 -0
  89. package/dist/index.d.ts +4 -0
  90. package/dist/index.d.ts.map +1 -0
  91. package/dist/index.js +109 -0
  92. package/dist/output/exit-codes.d.ts +25 -0
  93. package/dist/output/exit-codes.d.ts.map +1 -0
  94. package/dist/output/exit-codes.js +23 -0
  95. package/dist/output/format.d.ts +28 -0
  96. package/dist/output/format.d.ts.map +1 -0
  97. package/dist/output/format.js +56 -0
  98. package/dist/output/money.d.ts +16 -0
  99. package/dist/output/money.d.ts.map +1 -0
  100. package/dist/output/money.js +35 -0
  101. package/dist/output/write-file.d.ts +11 -0
  102. package/dist/output/write-file.d.ts.map +1 -0
  103. package/dist/output/write-file.js +38 -0
  104. package/dist/security/redact.d.ts +16 -0
  105. package/dist/security/redact.d.ts.map +1 -0
  106. package/dist/security/redact.js +61 -0
  107. package/package.json +51 -0
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Money never becomes a JavaScript number.
3
+ *
4
+ * The API speaks integer micro-USD as decimal strings, and 0.1 + 0.2 is the
5
+ * reason: a float round-trip can turn a balance into a number that does not
6
+ * match the invoice. Everything here is BigInt in, string out.
7
+ */
8
+ const MICROS_PER_USD = 1000000n;
9
+ export function parseMicroUsd(value) {
10
+ if (typeof value === "bigint")
11
+ return value;
12
+ const text = String(value).trim();
13
+ if (!/^-?\d+$/.test(text)) {
14
+ throw new TypeError(`not an integer micro-USD amount: ${text}`);
15
+ }
16
+ return BigInt(text);
17
+ }
18
+ /**
19
+ * Render micro-USD as dollars. `minimumFractionDigits` keeps small amounts
20
+ * readable ($0.00 is wrong for 1 µ$, so tiny values keep their precision).
21
+ */
22
+ export function formatUsd(microUsd, options = {}) {
23
+ const micros = parseMicroUsd(microUsd);
24
+ const negative = micros < 0n;
25
+ const abs = negative ? -micros : micros;
26
+ const whole = abs / MICROS_PER_USD;
27
+ const fraction = (abs % MICROS_PER_USD).toString().padStart(6, "0");
28
+ const minDigits = options.minFractionDigits ?? 2;
29
+ // Trim trailing zeros but never below the minimum, and never to nothing:
30
+ // a sub-cent charge must stay visible rather than round to $0.00.
31
+ let digits = fraction.replace(/0+$/, "");
32
+ if (digits.length < minDigits)
33
+ digits = fraction.slice(0, minDigits);
34
+ return `${negative ? "-" : ""}$${whole}.${digits}`;
35
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Write a result file without ever destroying one silently.
3
+ *
4
+ * Two rules: an existing file is refused unless `--force`, and the write is
5
+ * atomic — a temp file in the same directory, then a rename. A crash or a full
6
+ * disk halfway through must not leave a truncated .wav where the old one was.
7
+ */
8
+ export declare function writeOutputFile(path: string, bytes: Uint8Array | string, options?: {
9
+ force?: boolean;
10
+ }): void;
11
+ //# sourceMappingURL=write-file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write-file.d.ts","sourceRoot":"","sources":["../../src/output/write-file.ts"],"names":[],"mappings":"AAMA;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,UAAU,GAAG,MAAM,EAC1B,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GAChC,IAAI,CA4BN"}
@@ -0,0 +1,38 @@
1
+ import { existsSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { ExitCode } from "./exit-codes.js";
4
+ import { ApiError } from "../api/errors.js";
5
+ /**
6
+ * Write a result file without ever destroying one silently.
7
+ *
8
+ * Two rules: an existing file is refused unless `--force`, and the write is
9
+ * atomic — a temp file in the same directory, then a rename. A crash or a full
10
+ * disk halfway through must not leave a truncated .wav where the old one was.
11
+ */
12
+ export function writeOutputFile(path, bytes, options = {}) {
13
+ if (existsSync(path) && !options.force) {
14
+ throw new ApiError({
15
+ kind: "request",
16
+ exitCode: ExitCode.Usage,
17
+ message: `${path} already exists - pass --force to overwrite it`,
18
+ });
19
+ }
20
+ const temp = join(dirname(path) || ".", `.mafdet-${process.pid}-${Date.now()}.tmp`);
21
+ try {
22
+ writeFileSync(temp, bytes);
23
+ renameSync(temp, path);
24
+ }
25
+ catch (error) {
26
+ try {
27
+ unlinkSync(temp);
28
+ }
29
+ catch {
30
+ // The temp file may never have been created; nothing to clean up.
31
+ }
32
+ throw new ApiError({
33
+ kind: "request",
34
+ exitCode: ExitCode.Usage,
35
+ message: `cannot write ${path}: ${error.message}`,
36
+ });
37
+ }
38
+ }
@@ -0,0 +1,16 @@
1
+ export declare const REDACTED = "<redacted>";
2
+ /** Redact anything credential-shaped in a single string. */
3
+ export declare function redact(value: string): string;
4
+ /**
5
+ * Redact recursively through anything printable — objects, arrays, Errors.
6
+ * Used for `--debug` dumps and for error reporting, where the shape of the
7
+ * value is not known in advance.
8
+ */
9
+ export declare function redactDeep(value: unknown): unknown;
10
+ /**
11
+ * How a key may be shown: enough to tell two keys apart, never enough to use
12
+ * one. Mafdet keys carry a meaningful prefix (`sk-mafdet-live-…`), so the
13
+ * environment is visible without exposing the secret.
14
+ */
15
+ export declare function keyFingerprint(apiKey: string): string;
16
+ //# sourceMappingURL=redact.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redact.d.ts","sourceRoot":"","sources":["../../src/security/redact.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,QAAQ,eAAe,CAAC;AAErC,4DAA4D;AAC5D,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAY5C;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAkBlD;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAOrD"}
@@ -0,0 +1,61 @@
1
+ // Every string that can reach a terminal, a log, an error or telemetry passes
2
+ // through here first. Credentials leak through the boring paths — an exception
3
+ // message, a --debug dump, a pasted bug report — so redaction lives at the
4
+ // output boundary rather than at each call site that happens to remember.
5
+ const API_KEY = /sk-[A-Za-z0-9_.-]{4,}/g;
6
+ /** LiteLLM quotes the key's SHA-256 in 401 and 429 messages. */
7
+ const KEY_HASH = /\b[0-9a-f]{32,}\b/g;
8
+ const BEARER = /\b(?:Bearer|Authorization:)\s+\S+/gi;
9
+ export const REDACTED = "<redacted>";
10
+ /** Redact anything credential-shaped in a single string. */
11
+ export function redact(value) {
12
+ return value
13
+ .replace(BEARER, "Bearer " + REDACTED)
14
+ .replace(API_KEY, (match, offset, whole) =>
15
+ // Idempotent: `sk-mafdet-live-<redacted>` is a fingerprint this module
16
+ // already produced, and eating its prefix would leave "<redacted>"
17
+ // twice — throwing away the environment the operator needs to see.
18
+ whole.slice(offset + match.length).startsWith(REDACTED)
19
+ ? match
20
+ : REDACTED)
21
+ .replace(KEY_HASH, REDACTED);
22
+ }
23
+ /**
24
+ * Redact recursively through anything printable — objects, arrays, Errors.
25
+ * Used for `--debug` dumps and for error reporting, where the shape of the
26
+ * value is not known in advance.
27
+ */
28
+ export function redactDeep(value) {
29
+ if (typeof value === "string")
30
+ return redact(value);
31
+ if (Array.isArray(value))
32
+ return value.map(redactDeep);
33
+ if (value instanceof Error) {
34
+ const clone = new Error(redact(value.message));
35
+ clone.name = value.name;
36
+ if (value.stack)
37
+ clone.stack = redact(value.stack);
38
+ return clone;
39
+ }
40
+ if (value && typeof value === "object") {
41
+ return Object.fromEntries(Object.entries(value).map(([key, inner]) => [
42
+ key,
43
+ redactDeep(inner),
44
+ ]));
45
+ }
46
+ return value;
47
+ }
48
+ /**
49
+ * How a key may be shown: enough to tell two keys apart, never enough to use
50
+ * one. Mafdet keys carry a meaningful prefix (`sk-mafdet-live-…`), so the
51
+ * environment is visible without exposing the secret.
52
+ */
53
+ export function keyFingerprint(apiKey) {
54
+ const trimmed = apiKey.trim();
55
+ if (trimmed.length === 0)
56
+ return "<none>";
57
+ // Keep the scheme and environment segments, drop the secret one.
58
+ const parts = trimmed.split("-");
59
+ const head = parts.slice(0, 3).join("-");
60
+ return `${head}-${REDACTED}`;
61
+ }
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@mafdet/cli",
3
+ "version": "0.3.0",
4
+ "description": "Mafdet AI command line — model calls, account queries, and a permissioned local agent.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "Mafdet AI",
8
+ "homepage": "https://www.mafdet.ai",
9
+ "bugs": {
10
+ "email": "support@mafdet.ai"
11
+ },
12
+ "keywords": [
13
+ "mafdet",
14
+ "cli",
15
+ "ai",
16
+ "llm",
17
+ "agent",
18
+ "openai-compatible"
19
+ ],
20
+ "bin": {
21
+ "mafdet": "dist/index.js"
22
+ },
23
+ "main": "./dist/index.js",
24
+ "types": "./dist/index.d.ts",
25
+ "files": [
26
+ "dist",
27
+ "README.md",
28
+ "README.zh-CN.md",
29
+ "CHANGELOG.md",
30
+ "LICENSE"
31
+ ],
32
+ "publishConfig": {
33
+ "access": "public"
34
+ },
35
+ "scripts": {
36
+ "build": "tsc -p tsconfig.json",
37
+ "lint": "prettier --check \"src/**/*.ts\" \"test/**/*.mjs\" package.json tsconfig.json",
38
+ "test": "pnpm run build && node --test \"test/**/*.test.mjs\"",
39
+ "typecheck": "tsc --noEmit -p tsconfig.json"
40
+ },
41
+ "dependencies": {
42
+ "zod": "^4.4.3"
43
+ },
44
+ "devDependencies": {
45
+ "@types/node": "^20.19.0",
46
+ "typescript": "6.0.3"
47
+ },
48
+ "engines": {
49
+ "node": ">=20"
50
+ }
51
+ }