@sayknow-cli/utils 0.7.11 → 0.7.12
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/dist/types/broken-pipe.d.ts +4 -3
- package/dist/types/canonical-log-dir.d.ts +20 -0
- package/dist/types/cli-metadata.d.ts +33 -0
- package/dist/types/cli.d.ts +12 -0
- package/dist/types/crash-fingerprint.d.ts +82 -0
- package/dist/types/crash-journal.d.ts +84 -0
- package/dist/types/crash-redaction.d.ts +20 -0
- package/dist/types/dirs.d.ts +127 -36
- package/dist/types/env-file.d.ts +59 -0
- package/dist/types/env.d.ts +19 -1
- package/dist/types/error-classification.d.ts +12 -0
- package/dist/types/fetch-retry.d.ts +6 -3
- package/dist/types/header-value.d.ts +10 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/logger.d.ts +22 -8
- package/dist/types/native-process.d.ts +12 -0
- package/dist/types/procmgr.d.ts +1 -35
- package/dist/types/safe-error.d.ts +20 -0
- package/dist/types/sanitize-text.d.ts +10 -0
- package/dist/types/shell-config.d.ts +58 -0
- package/dist/types/stream.d.ts +2 -1
- package/dist/types/which.d.ts +1 -1
- package/package.json +2 -2
- package/src/broken-pipe.ts +16 -4
- package/src/canonical-log-dir.ts +59 -0
- package/src/cli-metadata.ts +74 -0
- package/src/cli.ts +30 -2
- package/src/crash-fingerprint.ts +294 -0
- package/src/crash-journal.ts +292 -0
- package/src/crash-redaction.ts +558 -0
- package/src/dirs.ts +583 -165
- package/src/env-file.ts +326 -20
- package/src/env.ts +321 -35
- package/src/error-classification.ts +31 -0
- package/src/fetch-retry.ts +23 -10
- package/src/frontmatter.ts +28 -2
- package/src/glob.ts +13 -10
- package/src/header-value.ts +15 -0
- package/src/index.ts +6 -0
- package/src/logger.ts +252 -97
- package/src/native-process.ts +19 -0
- package/src/postmortem.ts +1 -55
- package/src/procmgr.ts +16 -212
- package/src/ptree.ts +3 -2
- package/src/safe-error.ts +40 -0
- package/src/sanitize-text.ts +14 -0
- package/src/shell-config.ts +329 -0
- package/src/stream.ts +32 -1
- package/src/which.ts +6 -5
|
@@ -10,9 +10,10 @@ export declare function isKnownSinkPeerClosedError(error: unknown): boolean;
|
|
|
10
10
|
*/
|
|
11
11
|
export declare function isBrokenPipeError(error: unknown): boolean;
|
|
12
12
|
/**
|
|
13
|
-
* A classifier for process-level stdout
|
|
14
|
-
* evidence is private to each factory instance, so
|
|
15
|
-
* intercepted `process.stdout.write` can mark an error
|
|
13
|
+
* A classifier for process-level stdout peer-closed errors (`EPIPE`, `EIO`,
|
|
14
|
+
* `EBADF`). Its direct-write evidence is private to each factory instance, so
|
|
15
|
+
* only the owner that intercepted `process.stdout.write` can mark an error
|
|
16
|
+
* for this classifier.
|
|
16
17
|
*/
|
|
17
18
|
export interface ProcessStdoutEpipeClassifier {
|
|
18
19
|
markDirectProcessStdoutWriteError(error: unknown): void;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
interface ProjectEnvSnapshotLike {
|
|
2
|
+
values: Record<string, string>;
|
|
3
|
+
dynamic: Set<string>;
|
|
4
|
+
}
|
|
5
|
+
interface CanonicalLogDirInput {
|
|
6
|
+
home: string;
|
|
7
|
+
env: Record<string, string | undefined>;
|
|
8
|
+
projectEnv: ProjectEnvSnapshotLike;
|
|
9
|
+
xdgEligible: boolean;
|
|
10
|
+
pathExists?: (target: string) => boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Resolve the canonical user log directory without mutating the environment.
|
|
14
|
+
*
|
|
15
|
+
* The caller supplies filesystem existence checks so this leaf stays free of
|
|
16
|
+
* filesystem and resolver side effects. Both the production directory resolver
|
|
17
|
+
* and the test preload use this exact path selection logic.
|
|
18
|
+
*/
|
|
19
|
+
export declare function resolveCanonicalLogsDir(input: CanonicalLogDirInput): string;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure CLI metadata leaf.
|
|
3
|
+
*
|
|
4
|
+
* The actual `skc` entry (`packages/coding-agent/src/cli.ts`) must stay inert on
|
|
5
|
+
* import: no directory resolution, logger construction, NSS lookup, native load
|
|
6
|
+
* or environment probing may run before argv routing. `dirs.ts` performs all of
|
|
7
|
+
* those at module scope, so the handful of constants the entry really needs live
|
|
8
|
+
* here and `dirs.ts` re-exports them for its existing callers.
|
|
9
|
+
*
|
|
10
|
+
* Only build-time package metadata may be imported here.
|
|
11
|
+
*/
|
|
12
|
+
/** App name (e.g. "skc") */
|
|
13
|
+
export declare const APP_NAME: string;
|
|
14
|
+
/** Version (e.g. "1.0.0") */
|
|
15
|
+
export declare const VERSION: string;
|
|
16
|
+
/** Minimum Bun version */
|
|
17
|
+
export declare const MIN_BUN_VERSION: string;
|
|
18
|
+
/**
|
|
19
|
+
* Build the diagnostic shown when the Bun runtime executing `skc` is older
|
|
20
|
+
* than {@link MIN_BUN_VERSION}. This is the most common Windows native-install
|
|
21
|
+
* failure (issue #525): `bun install -g sayknow-cli` probes a recent Bun while
|
|
22
|
+
* the `skc` launcher resolves an older Bun still on PATH. The message names the
|
|
23
|
+
* exact detected runtime path and gives a platform-specific upgrade + PATH fix
|
|
24
|
+
* instead of a bare `bun upgrade`.
|
|
25
|
+
*
|
|
26
|
+
* Pure and platform-parameterized so it can be unit-tested cross-platform.
|
|
27
|
+
*/
|
|
28
|
+
export declare function formatBunRuntimeError(opts: {
|
|
29
|
+
currentVersion: string;
|
|
30
|
+
minVersion: string;
|
|
31
|
+
execPath?: string;
|
|
32
|
+
platform?: NodeJS.Platform;
|
|
33
|
+
}): string;
|
package/dist/types/cli.d.ts
CHANGED
|
@@ -6,6 +6,8 @@ export interface FlagDescriptor<K extends "string" | "boolean" | "integer" = "st
|
|
|
6
6
|
multiple?: boolean;
|
|
7
7
|
options?: readonly string[];
|
|
8
8
|
required?: boolean;
|
|
9
|
+
/** The string value may be omitted; parsers decide whether to consume it. */
|
|
10
|
+
optionalValue?: boolean;
|
|
9
11
|
}
|
|
10
12
|
export interface ArgDescriptor {
|
|
11
13
|
kind: "string";
|
|
@@ -21,6 +23,8 @@ interface FlagInput {
|
|
|
21
23
|
multiple?: boolean;
|
|
22
24
|
options?: readonly string[];
|
|
23
25
|
required?: boolean;
|
|
26
|
+
/** The string value may be omitted; parsers decide whether to consume it. */
|
|
27
|
+
optionalValue?: boolean;
|
|
24
28
|
}
|
|
25
29
|
interface ArgInput {
|
|
26
30
|
description?: string;
|
|
@@ -107,6 +111,14 @@ export interface CommandEntry {
|
|
|
107
111
|
name: string;
|
|
108
112
|
load: () => Promise<CommandCtor>;
|
|
109
113
|
aliases?: string[];
|
|
114
|
+
/** Owns parsing, help, loading and failures for this family when supplied. */
|
|
115
|
+
dispatch?: (argv: string[], context: CommandEntryContext) => Promise<void>;
|
|
116
|
+
}
|
|
117
|
+
export interface CommandEntryContext {
|
|
118
|
+
bin: string;
|
|
119
|
+
version: string;
|
|
120
|
+
/** Canonical registered entry name, including when invoked through an alias. */
|
|
121
|
+
command: string;
|
|
110
122
|
}
|
|
111
123
|
export interface RunOptions {
|
|
112
124
|
bin: string;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/** Algorithm version recorded beside every emitted fingerprint. */
|
|
2
|
+
export declare const CRASH_FINGERPRINT_VERSION = 1;
|
|
3
|
+
/** Hex length of a published fingerprint (128 bits). */
|
|
4
|
+
export declare const CRASH_FINGERPRINT_HEX_LENGTH = 32;
|
|
5
|
+
/** Matches exactly a published fingerprint. */
|
|
6
|
+
export declare const CRASH_FINGERPRINT_PATTERN: RegExp;
|
|
7
|
+
/** Marker used for the machine-readable identity line of each crash record. */
|
|
8
|
+
export declare const CRASH_RECORD_MARKER = "skc-crash-record.v1";
|
|
9
|
+
/** Marker embedded in an external issue body, outside crash-derived blocks. */
|
|
10
|
+
export declare const CRASH_ISSUE_MARKER_PREFIX = "skc-crash-fp.v1:";
|
|
11
|
+
/** Literal frame used when a stack carries no in-app frame at all. */
|
|
12
|
+
export declare const NO_APP_FRAME = "<no-app-frame>";
|
|
13
|
+
export interface CrashFingerprintInput {
|
|
14
|
+
readonly name: string;
|
|
15
|
+
readonly message: string;
|
|
16
|
+
readonly stack: string;
|
|
17
|
+
}
|
|
18
|
+
export interface CrashFingerprint {
|
|
19
|
+
/** 32 lowercase hex characters. */
|
|
20
|
+
readonly fingerprint: string;
|
|
21
|
+
/** Algorithm version (`fpv`). */
|
|
22
|
+
readonly version: number;
|
|
23
|
+
readonly errorName: string;
|
|
24
|
+
/** Normalized, placeholder-substituted message class (safe to display). */
|
|
25
|
+
readonly messageClass: string;
|
|
26
|
+
/** Normalized in-app frames that participated in the digest. */
|
|
27
|
+
readonly frames: readonly string[];
|
|
28
|
+
}
|
|
29
|
+
export interface CrashFingerprintOptions {
|
|
30
|
+
/** Install root used to relativize in-app frames. Defaults to the SKC install root. */
|
|
31
|
+
readonly installRoot?: string;
|
|
32
|
+
/** Home directory used for `<home>` substitution. Defaults to `os.homedir()`. */
|
|
33
|
+
readonly homeDir?: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Replace absolute path-like tokens with `<home>` / `<path>`.
|
|
37
|
+
*
|
|
38
|
+
* Semantically meaningful codes are untouched: this rule only fires on tokens
|
|
39
|
+
* that are recognizably absolute filesystem paths.
|
|
40
|
+
*/
|
|
41
|
+
export declare function replaceAbsolutePaths(text: string, homeDir?: string): string;
|
|
42
|
+
/**
|
|
43
|
+
* Typed message normalization.
|
|
44
|
+
*
|
|
45
|
+
* Deliberately *not* "strip all digits": HTTP statuses, exit codes and errno
|
|
46
|
+
* names are the difference between distinct crash classes, so runs of three or
|
|
47
|
+
* fewer digits and alphabetic error codes survive verbatim (`404` stays
|
|
48
|
+
* distinct from `500`). Only high-entropy identifiers are collapsed.
|
|
49
|
+
*/
|
|
50
|
+
export declare function normalizeCrashMessage(message: string, options?: CrashFingerprintOptions): string;
|
|
51
|
+
/**
|
|
52
|
+
* Normalized in-app frames, newest first, capped at three.
|
|
53
|
+
*
|
|
54
|
+
* A frame is `<install-root-relative path>#<function>` with no line or column
|
|
55
|
+
* numbers. Stacks with no in-app frame yield the single literal
|
|
56
|
+
* `<no-app-frame>`; distinct roots can merge under that literal, which is an
|
|
57
|
+
* accepted and documented v1 property.
|
|
58
|
+
*/
|
|
59
|
+
export declare function normalizeCrashFrames(stack: string, options?: CrashFingerprintOptions): string[];
|
|
60
|
+
/** Compute the v1 fingerprint of an already-captured fatal diagnostic. */
|
|
61
|
+
export declare function computeCrashFingerprint(input: CrashFingerprintInput, options?: CrashFingerprintOptions): CrashFingerprint;
|
|
62
|
+
/**
|
|
63
|
+
* Compute the stable identity used for a handled tool error.
|
|
64
|
+
*
|
|
65
|
+
* Tool failures often carry command output or other per-occurrence detail in
|
|
66
|
+
* their message. That text is useful in the record body but is not the failure
|
|
67
|
+
* identity: handled errors group by error class and the first in-app frame
|
|
68
|
+
* where the failure originated, rather than the full wrapper stack.
|
|
69
|
+
*/
|
|
70
|
+
export declare function computeHandledErrorFingerprint(input: CrashFingerprintInput, options?: CrashFingerprintOptions): CrashFingerprint;
|
|
71
|
+
/** The machine-readable identity line appended to every new crash record. */
|
|
72
|
+
export declare function formatCrashRecordMarker(fingerprint: string, version: number, recordId: string): string;
|
|
73
|
+
export interface CrashRecordMarker {
|
|
74
|
+
readonly fingerprint: string;
|
|
75
|
+
readonly version: number;
|
|
76
|
+
readonly recordId: string;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Parse an identity line. Records written before this feature carry no marker
|
|
80
|
+
* and are therefore `unmatchable`: this parser never guesses at them.
|
|
81
|
+
*/
|
|
82
|
+
export declare function parseCrashRecordMarker(line: string): CrashRecordMarker | undefined;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/** Hard cap for one journal line, including its newline. */
|
|
2
|
+
export declare const CRASH_EVENT_MAX_BYTES = 512;
|
|
3
|
+
/** Journal line format tag. */
|
|
4
|
+
export declare const CRASH_EVENT_KIND = "skc-crash-event.v1";
|
|
5
|
+
/** Preview cap for the message class carried by an event. */
|
|
6
|
+
export declare const CRASH_EVENT_MESSAGE_MAX_BYTES = 256;
|
|
7
|
+
/** Bounded execution provenance carried only on newly written occurrences. */
|
|
8
|
+
export type CrashProvenance = "product" | "eval" | "bun_test";
|
|
9
|
+
/**
|
|
10
|
+
* Classify only process-level harness modes that are explicit and stable.
|
|
11
|
+
* Everything else is product provenance, including source checkouts, CLI
|
|
12
|
+
* invocations, SDK/ACP hosts, and native-loader failures.
|
|
13
|
+
*/
|
|
14
|
+
export declare function detectCrashProvenance(argv?: readonly string[], env?: NodeJS.ProcessEnv, execArgv?: readonly string[]): CrashProvenance;
|
|
15
|
+
export type CrashEvent = CrashOccurrenceEvent | CrashRefusedEvent | CrashReportedEvent | CrashRelayedEvent | CrashAcknowledgedEvent | CrashNudgedEvent;
|
|
16
|
+
export interface CrashOccurrenceEvent {
|
|
17
|
+
readonly kind: "occurrence";
|
|
18
|
+
readonly fingerprint: string;
|
|
19
|
+
readonly fpv: number;
|
|
20
|
+
readonly recordId: string;
|
|
21
|
+
readonly at: number;
|
|
22
|
+
readonly errorName: string;
|
|
23
|
+
readonly messageClass: string;
|
|
24
|
+
/** Legacy events omit this and are treated as product crashes. */
|
|
25
|
+
readonly provenance?: CrashProvenance;
|
|
26
|
+
}
|
|
27
|
+
export interface CrashRefusedEvent {
|
|
28
|
+
readonly kind: "refused";
|
|
29
|
+
readonly fingerprint: string;
|
|
30
|
+
readonly fpv: number;
|
|
31
|
+
readonly recordId: string;
|
|
32
|
+
readonly contractVersion: string;
|
|
33
|
+
readonly at: number;
|
|
34
|
+
}
|
|
35
|
+
export interface CrashReportedEvent {
|
|
36
|
+
readonly kind: "reported";
|
|
37
|
+
readonly fingerprint: string;
|
|
38
|
+
readonly at: number;
|
|
39
|
+
readonly issueUrl: string;
|
|
40
|
+
/** Set when the submission was a "+1" comment rather than a new issue. */
|
|
41
|
+
readonly commented?: boolean;
|
|
42
|
+
}
|
|
43
|
+
export interface CrashRelayedEvent {
|
|
44
|
+
readonly kind: "relayed";
|
|
45
|
+
readonly fingerprint: string;
|
|
46
|
+
readonly at: number;
|
|
47
|
+
/** Sentry event id accepted upstream: 32 lowercase hex. */
|
|
48
|
+
readonly eventId: string;
|
|
49
|
+
/** Exact occurrence record represented by the accepted envelope. Absent on legacy pre-record-id relay lines. */
|
|
50
|
+
readonly recordId?: string;
|
|
51
|
+
}
|
|
52
|
+
export interface CrashAcknowledgedEvent {
|
|
53
|
+
readonly kind: "acknowledged";
|
|
54
|
+
readonly fingerprint: string;
|
|
55
|
+
readonly at: number;
|
|
56
|
+
}
|
|
57
|
+
export interface CrashNudgedEvent {
|
|
58
|
+
readonly kind: "nudged";
|
|
59
|
+
readonly at: number;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Serialize one event to a single journal line (newline included), bounded to
|
|
63
|
+
* `CRASH_EVENT_MAX_BYTES`. Oversized message previews are shortened, and if the
|
|
64
|
+
* line still does not fit the preview is dropped entirely rather than the event.
|
|
65
|
+
*/
|
|
66
|
+
export declare function formatCrashEventLine(event: CrashEvent): string;
|
|
67
|
+
/** Parse one journal line. Anything unexpected yields `undefined`, never a throw. */
|
|
68
|
+
export declare function parseCrashEventLine(line: string): CrashEvent | undefined;
|
|
69
|
+
/**
|
|
70
|
+
* Append one event synchronously with `O_APPEND`. Never throws.
|
|
71
|
+
*
|
|
72
|
+
* No parse, no lock, no rename, no read: the only I/O is one bounded write to
|
|
73
|
+
* an append-only file, so concurrent writers interleave whole lines instead of
|
|
74
|
+
* corrupting each other's records.
|
|
75
|
+
*/
|
|
76
|
+
export declare function appendCrashEvent(event: CrashEvent, journalPath: string): boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Fatal-path entry point: at most one journal append per process lifetime.
|
|
79
|
+
*
|
|
80
|
+
* A crash raised while handling a crash must not spend the process's remaining
|
|
81
|
+
* moments on journal bookkeeping, so the latch is never cleared — the process
|
|
82
|
+
* is exiting anyway. Returns whether this call wrote a line.
|
|
83
|
+
*/
|
|
84
|
+
export declare function appendFatalCrashEvent(event: CrashEvent, journalPath: string): boolean;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Best-effort credential scrubbing for durable crash records.
|
|
3
|
+
*
|
|
4
|
+
* This is a *persistence-time* scrub: it keeps obvious credential shapes out of
|
|
5
|
+
* a file SKC keeps indefinitely. It is explicitly NOT a privacy guarantee and
|
|
6
|
+
* must never be treated as one for data that leaves the machine — outbound
|
|
7
|
+
* text goes through `sanitizeExternalCrashV1` instead.
|
|
8
|
+
*/
|
|
9
|
+
/** Whether a field name ends in a credential label recognized by the redactor. */
|
|
10
|
+
export declare function isCredentialKey(key: string): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Scrub credential material from crash text before it is persisted.
|
|
13
|
+
* Covers bearer/basic-style headers, key=value or JSON key forms of common
|
|
14
|
+
* credential names, and well-known vendor token shapes. Normal messages and
|
|
15
|
+
* stack frames are untouched; matches are replaced in place so surrounding
|
|
16
|
+
* diagnostic context survives.
|
|
17
|
+
*/
|
|
18
|
+
export declare function redactCrashSecrets(text: string): string;
|
|
19
|
+
/** Every placeholder `redactCrashSecrets` can emit. Used by downstream normalizers. */
|
|
20
|
+
export declare const CRASH_REDACTION_MARKERS: readonly string[];
|
package/dist/types/dirs.d.ts
CHANGED
|
@@ -5,37 +5,38 @@
|
|
|
5
5
|
* config root and SKC_CODING_AGENT_DIR (legacy alias PI_CODING_AGENT_DIR) to
|
|
6
6
|
* override the agent directory.
|
|
7
7
|
*
|
|
8
|
-
* On Linux,
|
|
9
|
-
*
|
|
10
|
-
* $XDG_*_HOME/skc
|
|
11
|
-
*
|
|
12
|
-
*
|
|
8
|
+
* On Linux and macOS, a category (data, state, cache) is redirected to
|
|
9
|
+
* $XDG_*_HOME/skc/ only when the trusted XDG_*_HOME variable is set, the
|
|
10
|
+
* $XDG_*_HOME/skc directory already exists (`skc config init-xdg` creates it),
|
|
11
|
+
* and the agent directory is the default. Otherwise paths stay under the config
|
|
12
|
+
* root. `init-xdg` only creates the directories; existing data is not moved.
|
|
13
13
|
*/
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
import { canonicalEnvKey, type ProjectEnvSnapshot } from "./env-file";
|
|
15
|
+
/**
|
|
16
|
+
* Entry metadata lives in the pure `cli-metadata` leaf so the actual CLI entry can
|
|
17
|
+
* import it without loading this module's load-time directory resolution. These
|
|
18
|
+
* re-exports keep the existing `@sayknow-cli/utils/dirs` surface unchanged.
|
|
19
|
+
*/
|
|
20
|
+
export { APP_NAME, formatBunRuntimeError, MIN_BUN_VERSION, VERSION } from "./cli-metadata";
|
|
21
|
+
export { canonicalEnvKey };
|
|
16
22
|
/** Config directory name (e.g. ".skc") */
|
|
17
23
|
export declare const CONFIG_DIR_NAME: string;
|
|
18
|
-
/** Version (e.g. "1.0.0") */
|
|
19
|
-
export declare const VERSION: string;
|
|
20
|
-
/** Minimum Bun version */
|
|
21
|
-
export declare const MIN_BUN_VERSION: string;
|
|
22
24
|
/**
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* failure (issue #525): `bun install -g sayknow-cli` probes a recent Bun while
|
|
26
|
-
* the `skc` launcher resolves an older Bun still on PATH. The message names the
|
|
27
|
-
* exact detected runtime path and gives a platform-specific upgrade + PATH fix
|
|
28
|
-
* instead of a bare `bun upgrade`.
|
|
29
|
-
*
|
|
30
|
-
* Pure and platform-parameterized so it can be unit-tested cross-platform.
|
|
25
|
+
* On macOS, strip /private prefix only when both paths resolve to the same location.
|
|
26
|
+
* This preserves aliases like /private/tmp -> /tmp without rewriting unrelated paths.
|
|
31
27
|
*/
|
|
32
|
-
export declare function
|
|
33
|
-
currentVersion: string;
|
|
34
|
-
minVersion: string;
|
|
35
|
-
execPath?: string;
|
|
36
|
-
platform?: NodeJS.Platform;
|
|
37
|
-
}): string;
|
|
28
|
+
export declare function standardizeMacOSPath(p: string): string;
|
|
38
29
|
export declare function resolveEquivalentPath(inputPath: string): string;
|
|
30
|
+
/**
|
|
31
|
+
* Return a stable key for comparing existing filesystem paths.
|
|
32
|
+
*
|
|
33
|
+
* Windows path spelling is not a safe identity: most directories are
|
|
34
|
+
* case-insensitive, while directories can opt into case-sensitive names. Use
|
|
35
|
+
* the filesystem object's identity when it is available instead of folding
|
|
36
|
+
* path text. If the path does not exist or the filesystem has no usable inode,
|
|
37
|
+
* retain the resolved spelling so distinct case-sensitive paths never alias.
|
|
38
|
+
*/
|
|
39
|
+
export declare function pathIdentityKey(inputPath: string): string;
|
|
39
40
|
export declare function normalizePathForComparison(inputPath: string, platform?: NodeJS.Platform): string;
|
|
40
41
|
/** Return whether a relative path crosses above its root or is unexpectedly absolute. */
|
|
41
42
|
export declare function relativePathEscapesRoot(relative: string): boolean;
|
|
@@ -45,15 +46,54 @@ export declare function relativePathWithinRoot(root: string, candidate: string):
|
|
|
45
46
|
export declare function getProjectDir(): string;
|
|
46
47
|
/** Set the project directory. */
|
|
47
48
|
export declare function setProjectDir(dir: string): void;
|
|
48
|
-
export declare function getConfigDirName(): string;
|
|
49
|
-
/** Get the config agent directory name relative to home (e.g. ".skc/agent" or PI_CONFIG_DIR + "/agent"). */
|
|
50
49
|
export declare function getConfigAgentDirName(): string;
|
|
50
|
+
/**
|
|
51
|
+
* Project dotenv as captured when this module loaded.
|
|
52
|
+
* A later edit or delete of those files does not change the returned provenance.
|
|
53
|
+
*/
|
|
54
|
+
export declare function loadedProjectEnvSnapshot(): ProjectEnvSnapshot;
|
|
51
55
|
/** Get the config root directory (~/.skc). */
|
|
52
56
|
export declare function getConfigRootDir(): string;
|
|
53
|
-
/**
|
|
57
|
+
/**
|
|
58
|
+
* The authoritative home for user-scope state.
|
|
59
|
+
*
|
|
60
|
+
* Provenance-checked and resolved at call time: a home established or changed
|
|
61
|
+
* after this module loaded is honored, while a home the project dotenv could
|
|
62
|
+
* have planted is rejected in favor of the OS account database. See
|
|
63
|
+
* {@link resolveTrustedHome}.
|
|
64
|
+
*/
|
|
65
|
+
export declare function getTrustedHomeDir(): string;
|
|
66
|
+
/** Trusted config root, resolved at call time; preserves the configured nested config-dir name. */
|
|
67
|
+
export declare function getTrustedConfigRootDir(): string;
|
|
68
|
+
/**
|
|
69
|
+
* Set the coding agent directory. Creates a fresh resolver, invalidating all
|
|
70
|
+
* cached paths.
|
|
71
|
+
*
|
|
72
|
+
* This also exports `SKC_CODING_AGENT_DIR`, so child processes inherit the same
|
|
73
|
+
* selection and resolve the same storage lane.
|
|
74
|
+
*/
|
|
54
75
|
export declare function setAgentDir(dir: string): void;
|
|
76
|
+
/**
|
|
77
|
+
* Rebuild the resolver from the current trusted environment. Callers that
|
|
78
|
+
* temporarily used {@link setAgentDir} must first restore the original
|
|
79
|
+
* SKC_CODING_AGENT_DIR / PI_CODING_AGENT_DIR values, then call this function;
|
|
80
|
+
* an originally absent override remains absent and keeps following HOME.
|
|
81
|
+
*/
|
|
82
|
+
export declare function resetAgentDirFromEnvironment(): void;
|
|
55
83
|
/** Get the agent config directory (~/.skc/agent). */
|
|
56
84
|
export declare function getAgentDir(): string;
|
|
85
|
+
/** Resolver-owned profile classification, stable across HOME refreshes. */
|
|
86
|
+
export declare function getAgentProfileAuthority(): "default" | "custom";
|
|
87
|
+
export declare function getConfigDirName(): string;
|
|
88
|
+
/**
|
|
89
|
+
* Join a file under the provenance-checked agent directory, never the XDG
|
|
90
|
+
* state category. Automatic crash relay must not follow `XDG_STATE_HOME`:
|
|
91
|
+
* a checkout `.env` can set that variable and create `$XDG_STATE_HOME/skc`,
|
|
92
|
+
* which the ordinary state resolver would then treat as the crash store.
|
|
93
|
+
*/
|
|
94
|
+
export declare function getTrustedAgentFile(filename: string): string;
|
|
95
|
+
/** Whether the current checkout declares an environment key in its `.env`. */
|
|
96
|
+
export declare function isProjectEnvDeclaration(name: string): boolean;
|
|
57
97
|
/** Get the project-local config directory (.skc). */
|
|
58
98
|
export declare function getProjectAgentDir(cwd?: string): string;
|
|
59
99
|
/** Get the reports directory (~/.skc/reports). */
|
|
@@ -62,17 +102,43 @@ export declare function getReportsDir(): string;
|
|
|
62
102
|
export declare function getLogsDir(): string;
|
|
63
103
|
/** Get the path to a dated log file (~/.skc/logs/skc.YYYY-MM-DD.log). */
|
|
64
104
|
export declare function getLogPath(date?: Date): string;
|
|
105
|
+
/**
|
|
106
|
+
* The logs directory the process actually reads and writes.
|
|
107
|
+
*
|
|
108
|
+
* A trusted `SKC_LOG_DIR` redirects the sink — the test preload pins it to a
|
|
109
|
+
* per-process temp directory so `bun test` stops appending fixture
|
|
110
|
+
* `level:error` records to the operator's shared log (issue #5618) — and
|
|
111
|
+
* everything else falls back to {@link getLogsDir}.
|
|
112
|
+
*
|
|
113
|
+
* The override is provenance-checked through the same {@link trustedValue} rule
|
|
114
|
+
* as the config and agent directories: Bun loads `cwd/.env` into `process.env`
|
|
115
|
+
* before any module runs, so a repository could otherwise redirect where the
|
|
116
|
+
* operator's production logs are written. A dynamic (`$`/backtick) declaration
|
|
117
|
+
* is rejected there too, which fails closed onto the canonical path.
|
|
118
|
+
*
|
|
119
|
+
* This is deliberately *not* folded into {@link getLogsDir}: that function's
|
|
120
|
+
* config-root semantics are pinned by tests, and the preload sets `SKC_LOG_DIR`
|
|
121
|
+
* for every test process, so folding the override in would move it under every
|
|
122
|
+
* test. Writers and readers must both come through here instead — the split
|
|
123
|
+
* between the two is what let the transport and the log readers disagree.
|
|
124
|
+
*/
|
|
125
|
+
export declare function getEffectiveLogsDir(): string;
|
|
126
|
+
/** Get the dated log file under {@link getEffectiveLogsDir}. */
|
|
127
|
+
export declare function getEffectiveLogPath(date?: Date): string;
|
|
65
128
|
/**
|
|
66
129
|
* Get the plugins directory (~/.skc/plugins or its XDG equivalent).
|
|
67
130
|
*
|
|
68
131
|
* No-arg form (production callers) goes through the XDG-aware DirResolver so
|
|
69
|
-
* reads and writes always agree. The optional `home` parameter
|
|
70
|
-
*
|
|
71
|
-
* and returns `<home>/<configDir>/plugins
|
|
72
|
-
*
|
|
73
|
-
*
|
|
132
|
+
* reads and writes always agree. The optional `home` parameter names an explicit
|
|
133
|
+
* home: when it differs from the authoritative home resolved right now it
|
|
134
|
+
* short-circuits the resolver and returns `<home>/<configDir>/plugins`, giving
|
|
135
|
+
* callers that carry their own home (and tests with a temp HOME) a deterministic
|
|
136
|
+
* path. Passing the authoritative home explicitly is identical to the no-arg
|
|
137
|
+
* form — XDG semantics are preserved.
|
|
74
138
|
*/
|
|
75
139
|
export declare function getPluginsDir(home?: string): string;
|
|
140
|
+
/** Get the default-profile plugin directory without ambient profile authority. */
|
|
141
|
+
export declare function getDefaultPluginsDir(): string;
|
|
76
142
|
/** Where npm installs packages (~/.skc/plugins/node_modules). */
|
|
77
143
|
export declare function getPluginsNodeModules(): string;
|
|
78
144
|
/** Plugin manifest (~/.skc/plugins/package.json). */
|
|
@@ -113,10 +179,14 @@ export declare function getGpuCachePath(): string;
|
|
|
113
179
|
* cache file without touching the rest of the config root.
|
|
114
180
|
*/
|
|
115
181
|
export declare function getGithubCacheDbPath(): string;
|
|
182
|
+
/** Get the durable tool-choice capability cache path. */
|
|
183
|
+
export declare function getToolChoiceCapabilityCachePath(): string;
|
|
116
184
|
/** Get the resolved Claude Code client-version cache path (~/.skc/cache/claude-code-version.json). */
|
|
117
185
|
export declare function getClaudeCodeVersionCachePath(): string;
|
|
118
186
|
/** Get the natives directory (~/.skc/natives). */
|
|
119
187
|
export declare function getNativesDir(): string;
|
|
188
|
+
/** Get the local tool-issue report database path (~/.skc/tool-issues.db). */
|
|
189
|
+
export declare function getToolIssuesDbPath(): string;
|
|
120
190
|
/** Get the stats database path (~/.skc/stats.db). */
|
|
121
191
|
export declare function getStatsDbPath(): string;
|
|
122
192
|
/** Get the autoresearch state directory (~/.skc/autoresearch). */
|
|
@@ -137,6 +207,10 @@ export declare function getModelDbPath(agentDir?: string): string;
|
|
|
137
207
|
export declare function getSessionsDir(agentDir?: string): string;
|
|
138
208
|
/** Get the content-addressed blob store directory (~/.skc/agent/blobs). */
|
|
139
209
|
export declare function getBlobsDir(agentDir?: string): string;
|
|
210
|
+
/** Get the resident-text cache root for a profile agent directory. */
|
|
211
|
+
export declare function getResidentCacheRootDir(profileAgentDir: string): string;
|
|
212
|
+
/** Get the managed cold-history sidecar cache root for a profile agent directory. */
|
|
213
|
+
export declare function getSidecarCacheRootDir(profileAgentDir: string): string;
|
|
140
214
|
/** Get the custom themes directory (~/.skc/agent/themes). */
|
|
141
215
|
export declare function getCustomThemesDir(agentDir?: string): string;
|
|
142
216
|
/** Get the tools directory (~/.skc/agent/tools). */
|
|
@@ -153,6 +227,16 @@ export declare function getMemoriesDir(agentDir?: string): string;
|
|
|
153
227
|
export declare function getTerminalSessionsDir(agentDir?: string): string;
|
|
154
228
|
/** Get the crash log path (~/.skc/agent/skc-crash.log). */
|
|
155
229
|
export declare function getCrashLogPath(agentDir?: string): string;
|
|
230
|
+
/** Get the crash event journal path (~/.skc/agent/skc-crash-events.jsonl). */
|
|
231
|
+
export declare function getCrashEventsPath(agentDir?: string): string;
|
|
232
|
+
/** Get the compacted crash signature index path (~/.skc/agent/skc-crash-index.json). */
|
|
233
|
+
export declare function getCrashIndexPath(agentDir?: string): string;
|
|
234
|
+
/** Get the handled error log path (~/.skc/agent/skc-error.log). */
|
|
235
|
+
export declare function getHandledErrorLogPath(agentDir?: string): string;
|
|
236
|
+
/** Get the handled error event journal path (~/.skc/agent/skc-error-events.jsonl). */
|
|
237
|
+
export declare function getHandledErrorEventsPath(agentDir?: string): string;
|
|
238
|
+
/** Get the compacted handled error signature index path (~/.skc/agent/skc-error-index.json). */
|
|
239
|
+
export declare function getHandledErrorIndexPath(agentDir?: string): string;
|
|
156
240
|
/** Get the debug log path (~/.skc/agent/skc-debug.log). */
|
|
157
241
|
export declare function getDebugLogPath(agentDir?: string): string;
|
|
158
242
|
/** Get the project-level Python modules directory (.skc/modules). */
|
|
@@ -161,7 +245,14 @@ export declare function getProjectModulesDir(cwd?: string): string;
|
|
|
161
245
|
export declare function getProjectPromptsDir(cwd?: string): string;
|
|
162
246
|
/** Get the project-level plugin overrides path (.skc/plugin-overrides.json). */
|
|
163
247
|
export declare function getProjectPluginOverridesPath(cwd?: string): string;
|
|
164
|
-
/**
|
|
165
|
-
|
|
248
|
+
/**
|
|
249
|
+
* Get the primary MCP config file path (first candidate).
|
|
250
|
+
*
|
|
251
|
+
* User scope lives in the agent directory, so a profile override
|
|
252
|
+
* (`--agent-dir`, `SKC_CODING_AGENT_DIR`, `setAgentDir()`) moves it. Pass
|
|
253
|
+
* `agentDir` to resolve the scope of a session whose agent directory differs
|
|
254
|
+
* from the process-wide one.
|
|
255
|
+
*/
|
|
256
|
+
export declare function getMCPConfigPath(scope: "user" | "project", cwd?: string, agentDir?: string): string;
|
|
166
257
|
/** Get the SSH config file path. */
|
|
167
|
-
export declare function getSSHConfigPath(scope: "user" | "project", cwd?: string): string;
|
|
258
|
+
export declare function getSSHConfigPath(scope: "user" | "project", cwd?: string, agentDir?: string): string;
|
package/dist/types/env-file.d.ts
CHANGED
|
@@ -17,5 +17,64 @@ export declare function parseShellEnvFile(filePath: string): Record<string, stri
|
|
|
17
17
|
* Ignores lines that are empty or start with '#'. Trims whitespace.
|
|
18
18
|
* Allows values to be quoted with single or double quotes.
|
|
19
19
|
* Returns an object of key-value pairs.
|
|
20
|
+
*
|
|
21
|
+
* The trust guards (`trustedAgentDirOverrideFor`, `trustedConfigDirName`,
|
|
22
|
+
* `filterCredentialInheritedEnv`) decide provenance by comparing
|
|
23
|
+
* `process.env` against this parse, so the accepted syntax must be a superset
|
|
24
|
+
* of what Bun's own dotenv loader honors in `cwd/.env`: `export KEY=value`,
|
|
25
|
+
* whitespace around `=` or `:`, and `#` comments after unquoted values, including
|
|
26
|
+
* a backslash before `#` (quotes keep their `#`). Values that Bun would expand
|
|
27
|
+
* (`$VAR`, `${VAR}`, backticks,
|
|
28
|
+
* command substitution) are kept as their literal text: the trust rule only
|
|
29
|
+
* needs the parser to see the key at all, and an operator environment value
|
|
30
|
+
* cannot equal attacker-written expansion text, so a literal parse stays
|
|
31
|
+
* conservative.
|
|
20
32
|
*/
|
|
21
33
|
export declare function parseEnvFile(filePath: string): Record<string, string>;
|
|
34
|
+
/** Parse dotenv content that has already been read from a trusted file. */
|
|
35
|
+
export declare function parseEnvFileContent(content: string): Record<string, string>;
|
|
36
|
+
/**
|
|
37
|
+
* What the caller's checkout declares through its dotenv files.
|
|
38
|
+
*
|
|
39
|
+
* `values` is the merged declaration set, later layers winning. `dynamic` holds
|
|
40
|
+
* the keys whose surviving declaration is one Bun expands at load time, or whose
|
|
41
|
+
* raw text is quoted in a way this line parser does not decode. Provenance
|
|
42
|
+
* refuses those keys instead of comparing a value Bun may have loaded differently.
|
|
43
|
+
*/
|
|
44
|
+
export interface ProjectEnvSnapshot {
|
|
45
|
+
values: Record<string, string>;
|
|
46
|
+
dynamic: Set<string>;
|
|
47
|
+
/** True when at least one project dotenv layer could not be read safely. */
|
|
48
|
+
incomplete?: boolean;
|
|
49
|
+
}
|
|
50
|
+
export interface ProjectEnvSnapshotOptions {
|
|
51
|
+
/** Maximum bytes read from each dotenv layer; unsafe files mark the snapshot incomplete. */
|
|
52
|
+
maxFileBytes?: number;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Windows environment variable names are case-insensitive, so a project dotenv
|
|
56
|
+
* line `userprofile=...` is what `process.env.USERPROFILE` resolves to. Every
|
|
57
|
+
* provenance lookup is spelled in upper case, so the snapshot must be keyed the
|
|
58
|
+
* same way or the declaration is invisible to the guard while still being live
|
|
59
|
+
* in the process. POSIX names are case-sensitive and must not fold.
|
|
60
|
+
*/
|
|
61
|
+
export declare function canonicalEnvKey(name: string): string;
|
|
62
|
+
/**
|
|
63
|
+
* The layered dotenv declarations Bun overlays into `process.env` for a cwd.
|
|
64
|
+
*
|
|
65
|
+
* Lives in this leaf module so every consumer shares ONE notion of provenance.
|
|
66
|
+
* `dirs.ts` resolves the config, agent and log directories from it, and
|
|
67
|
+
* `scripts/test-preload.ts` decides test isolation from it — a second, narrower
|
|
68
|
+
* reader (only `cwd/.env`, its own regex, its own dynamic test) is how a
|
|
69
|
+
* `SKC_LOG_DIR` declared in `.env.local` / `.env.$NODE_ENV` came to be honored
|
|
70
|
+
* by the preload and then rejected in production, silently routing test log
|
|
71
|
+
* records to the operator's canonical sink. This module imports only `node:fs`,
|
|
72
|
+
* `node:path` and the import-free `./spawn-env`, so importing it has no side
|
|
73
|
+
* effects and cannot freeze resolver state the way importing `dirs.ts` would.
|
|
74
|
+
*
|
|
75
|
+
* `.env.local` is deliberately skipped when `NODE_ENV === "test"`, matching the
|
|
76
|
+
* convention that a local override file is not part of a test run.
|
|
77
|
+
*/
|
|
78
|
+
/** Files Bun overlays for a cwd. Unset NODE_ENV selects the development layer. */
|
|
79
|
+
export declare function projectEnvLayerFiles(nodeEnv: string | undefined): string[];
|
|
80
|
+
export declare function projectEnvSnapshot(cwd?: string, options?: ProjectEnvSnapshotOptions): ProjectEnvSnapshot;
|
package/dist/types/env.d.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
export { filterProcessEnv, isSafeEnvName, isSafeEnvValue } from "./spawn-env";
|
|
2
2
|
export { isValidEnvName, parseEnvFile, parseShellEnvFile } from "./env-file";
|
|
3
|
+
/** Project dotenv as it was when this module loaded, before a later `chdir` or file delete. */
|
|
4
|
+
export declare function startupProjectEnvSnapshot(): {
|
|
5
|
+
values: Record<string, string>;
|
|
6
|
+
dynamic: Set<string>;
|
|
7
|
+
};
|
|
3
8
|
export declare function $inheritedEnv(name: string): string | undefined;
|
|
4
9
|
/**
|
|
5
10
|
* Intentional re-export of Bun.env.
|
|
@@ -25,13 +30,26 @@ export declare function $pickenv(...keys: string[]): string | undefined;
|
|
|
25
30
|
* only use values explicitly inherited from the launching shell or SKC/user-owned config files.
|
|
26
31
|
*/
|
|
27
32
|
export declare function $credentialEnv(name: string): string | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* Resolve a credential that may rotate in the trusted agent `.env` while this
|
|
35
|
+
* process remains alive.
|
|
36
|
+
*
|
|
37
|
+
* Presence in the agent file establishes that file as the credential's
|
|
38
|
+
* authority, even when the launching shell inherited an older value. This is
|
|
39
|
+
* what lets an atomic token rotation repair a long-lived shell instead of
|
|
40
|
+
* falling back to its revoked snapshot. Removal is authoritative too, so a
|
|
41
|
+
* deleted token cannot silently reappear from that snapshot. Project `.env`
|
|
42
|
+
* files are never consulted; shell values retain their normal precedence for
|
|
43
|
+
* names the agent file does not own.
|
|
44
|
+
*/
|
|
45
|
+
export declare function $rotatingCredentialEnv(name: string): string | undefined;
|
|
28
46
|
/**
|
|
29
47
|
* Resolve the first credential env value from the given keys, excluding cwd/.env overlays.
|
|
30
48
|
*/
|
|
31
49
|
export declare function $pickCredentialEnv(...keys: string[]): string | undefined;
|
|
32
50
|
/**
|
|
33
51
|
* Parses a positive decimal integer from `$env[name]`.
|
|
34
|
-
* Empty, invalid,
|
|
52
|
+
* Empty, invalid, unsafe, zero, or negative values return `defaultValue`.
|
|
35
53
|
*/
|
|
36
54
|
export declare function $envpos(name: string, defaultValue: number): number;
|
|
37
55
|
/** True when `BUN_ENV` or `NODE_ENV` is the string `test`. */
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-package classification for errors that describe an expected outcome
|
|
3
|
+
* rather than an unexpected failure.
|
|
4
|
+
*
|
|
5
|
+
* The symbol is registered globally so the marker survives package duplication
|
|
6
|
+
* and worker/VM boundaries without relying on a particular Error constructor.
|
|
7
|
+
*/
|
|
8
|
+
export declare const DESIGNED_ERROR: unique symbol;
|
|
9
|
+
/** Mark an Error as a designed outcome before it crosses package boundaries. */
|
|
10
|
+
export declare function markDesignedError<T extends Error>(error: T): T;
|
|
11
|
+
/** Return true when a throwable carries the trusted designed-outcome marker. */
|
|
12
|
+
export declare function isDesignedError(error: unknown): boolean;
|