@productbrain/cli 0.1.0-beta.4804 → 0.1.0-beta.4809
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/__tests__/config.test.js +96 -0
- package/dist/__tests__/config.test.js.map +1 -1
- package/dist/__tests__/session-switch.test.js +29 -1
- package/dist/__tests__/session-switch.test.js.map +1 -1
- package/dist/__tests__/workspaceNotice.test.js +22 -4
- package/dist/__tests__/workspaceNotice.test.js.map +1 -1
- package/dist/commands/session.d.ts.map +1 -1
- package/dist/commands/session.js +8 -10
- package/dist/commands/session.js.map +1 -1
- package/dist/commands/whoami.d.ts.map +1 -1
- package/dist/commands/whoami.js +5 -2
- package/dist/commands/whoami.js.map +1 -1
- package/dist/lib/config.d.ts +22 -59
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +87 -141
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/projectConfig.d.ts +64 -0
- package/dist/lib/projectConfig.d.ts.map +1 -0
- package/dist/lib/projectConfig.js +129 -0
- package/dist/lib/projectConfig.js.map +1 -0
- package/dist/lib/workspaceNotice.d.ts +20 -8
- package/dist/lib/workspaceNotice.d.ts.map +1 -1
- package/dist/lib/workspaceNotice.js +23 -10
- package/dist/lib/workspaceNotice.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The repo's own Product Brain config files — locating them and reading what they declare.
|
|
3
|
+
*
|
|
4
|
+
* Split out of config.ts under STD-2 / DEC-1504. The seam is deliberate and worth keeping:
|
|
5
|
+
* this module answers "what does THIS REPO say about itself" from the filesystem alone, while
|
|
6
|
+
* config.ts answers "which credential do we therefore use". Nothing here reads a credential
|
|
7
|
+
* store, an env var, or the global active profile — so it has no dependency on config.ts and
|
|
8
|
+
* cannot participate in an import cycle.
|
|
9
|
+
*
|
|
10
|
+
* Split config design:
|
|
11
|
+
* - config.json (committed): siteUrl only — workspace gateway, safe to share with teammates.
|
|
12
|
+
* - config.local.json (gitignored): profile pin — machine-local, never committed. Because it
|
|
13
|
+
* is gitignored, a fresh clone or worktree does NOT inherit it (INS-2462) — the caller must
|
|
14
|
+
* treat "no pin" as an explicit state, never as permission to guess a workspace.
|
|
15
|
+
*/
|
|
16
|
+
/** Project config shape — .productbrain/config.json (committed, siteUrl only) */
|
|
17
|
+
export interface ProjectConfig {
|
|
18
|
+
siteUrl?: string;
|
|
19
|
+
}
|
|
20
|
+
/** Local project config shape — .productbrain/config.local.json (gitignored, machine-local) */
|
|
21
|
+
export interface LocalProjectConfig {
|
|
22
|
+
profile?: string;
|
|
23
|
+
/** Direct preview API key — set by `npm run pb:preview`, cleared by `npm run pb:real`. */
|
|
24
|
+
apiKey?: string;
|
|
25
|
+
/** Preview site URL — set alongside apiKey by `npm run pb:preview`. */
|
|
26
|
+
siteUrl?: string;
|
|
27
|
+
/** Marks this config as preview-bound; the CLI shows a loud banner on every command. */
|
|
28
|
+
preview?: boolean;
|
|
29
|
+
/** Human-readable preview workspace name for the banner (e.g. "Preview Workspace"). */
|
|
30
|
+
workspaceName?: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Walk up the directory tree (git-style) to find the nearest CLI project root.
|
|
34
|
+
*
|
|
35
|
+
* A `.productbrain/` directory only counts as a CLI project root when it carries
|
|
36
|
+
* CLI configuration — `config.json` (committed siteUrl gateway) or
|
|
37
|
+
* `config.local.json` (machine-local profile pin or preview binding). A
|
|
38
|
+
* projection-only `.productbrain/` written by `pb handshake` (briefing.md,
|
|
39
|
+
* context.md, rules/, etc.) does NOT count — otherwise a subrepo like
|
|
40
|
+
* `packages/cli/.productbrain/` would shadow the real configuring root and
|
|
41
|
+
* `npm run pb:preview` writes (which always land at the repo-root
|
|
42
|
+
* `.productbrain/`) would be invisible to commands run from the subrepo.
|
|
43
|
+
*
|
|
44
|
+
* Stops at home directory or filesystem root. Returns the directory containing
|
|
45
|
+
* a config-bearing `.productbrain/`, or null if not found.
|
|
46
|
+
*/
|
|
47
|
+
export declare function findProjectRoot(startDir: string): string | null;
|
|
48
|
+
/**
|
|
49
|
+
* Read .productbrain/config.json from the walked-up project root.
|
|
50
|
+
* Only reads siteUrl — profile and apiKey fields are ignored (legacy or misplaced).
|
|
51
|
+
* Returns null if file missing or project root not found.
|
|
52
|
+
* Warns to stderr on invalid JSON but does not throw.
|
|
53
|
+
*/
|
|
54
|
+
export declare function readProjectConfig(): ProjectConfig | null;
|
|
55
|
+
/**
|
|
56
|
+
* Read .productbrain/config.local.json from the walked-up project root.
|
|
57
|
+
* Returns the machine-local pin (`profile`) and any preview-binding fields
|
|
58
|
+
* (`apiKey`, `siteUrl`, `preview`, `workspaceName`) written by
|
|
59
|
+
* `npm run pb:preview` — all gitignored, never committed.
|
|
60
|
+
* Returns null if file missing or project root not found.
|
|
61
|
+
* Warns to stderr on invalid JSON but does not throw.
|
|
62
|
+
*/
|
|
63
|
+
export declare function readLocalProjectConfig(): LocalProjectConfig | null;
|
|
64
|
+
//# sourceMappingURL=projectConfig.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projectConfig.d.ts","sourceRoot":"","sources":["../../src/lib/projectConfig.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAOH,iFAAiF;AACjF,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,+FAA+F;AAC/F,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0FAA0F;IAC1F,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wFAAwF;IACxF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,uFAAuF;IACvF,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAgB/D;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,IAAI,aAAa,GAAG,IAAI,CAuBxD;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,IAAI,kBAAkB,GAAG,IAAI,CAmClE"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The repo's own Product Brain config files — locating them and reading what they declare.
|
|
3
|
+
*
|
|
4
|
+
* Split out of config.ts under STD-2 / DEC-1504. The seam is deliberate and worth keeping:
|
|
5
|
+
* this module answers "what does THIS REPO say about itself" from the filesystem alone, while
|
|
6
|
+
* config.ts answers "which credential do we therefore use". Nothing here reads a credential
|
|
7
|
+
* store, an env var, or the global active profile — so it has no dependency on config.ts and
|
|
8
|
+
* cannot participate in an import cycle.
|
|
9
|
+
*
|
|
10
|
+
* Split config design:
|
|
11
|
+
* - config.json (committed): siteUrl only — workspace gateway, safe to share with teammates.
|
|
12
|
+
* - config.local.json (gitignored): profile pin — machine-local, never committed. Because it
|
|
13
|
+
* is gitignored, a fresh clone or worktree does NOT inherit it (INS-2462) — the caller must
|
|
14
|
+
* treat "no pin" as an explicit state, never as permission to guess a workspace.
|
|
15
|
+
*/
|
|
16
|
+
import { readFileSync, existsSync } from 'fs';
|
|
17
|
+
import { resolve } from 'path';
|
|
18
|
+
import { homedir } from 'os';
|
|
19
|
+
import { API_KEY_PREFIX } from './tokenConstants.js';
|
|
20
|
+
/**
|
|
21
|
+
* Walk up the directory tree (git-style) to find the nearest CLI project root.
|
|
22
|
+
*
|
|
23
|
+
* A `.productbrain/` directory only counts as a CLI project root when it carries
|
|
24
|
+
* CLI configuration — `config.json` (committed siteUrl gateway) or
|
|
25
|
+
* `config.local.json` (machine-local profile pin or preview binding). A
|
|
26
|
+
* projection-only `.productbrain/` written by `pb handshake` (briefing.md,
|
|
27
|
+
* context.md, rules/, etc.) does NOT count — otherwise a subrepo like
|
|
28
|
+
* `packages/cli/.productbrain/` would shadow the real configuring root and
|
|
29
|
+
* `npm run pb:preview` writes (which always land at the repo-root
|
|
30
|
+
* `.productbrain/`) would be invisible to commands run from the subrepo.
|
|
31
|
+
*
|
|
32
|
+
* Stops at home directory or filesystem root. Returns the directory containing
|
|
33
|
+
* a config-bearing `.productbrain/`, or null if not found.
|
|
34
|
+
*/
|
|
35
|
+
export function findProjectRoot(startDir) {
|
|
36
|
+
let dir = startDir;
|
|
37
|
+
const home = homedir();
|
|
38
|
+
while (true) {
|
|
39
|
+
const pbDir = resolve(dir, '.productbrain');
|
|
40
|
+
if (existsSync(pbDir) &&
|
|
41
|
+
(existsSync(resolve(pbDir, 'config.json')) ||
|
|
42
|
+
existsSync(resolve(pbDir, 'config.local.json')))) {
|
|
43
|
+
return dir;
|
|
44
|
+
}
|
|
45
|
+
const parent = resolve(dir, '..');
|
|
46
|
+
if (parent === dir || dir === home)
|
|
47
|
+
return null;
|
|
48
|
+
dir = parent;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Read .productbrain/config.json from the walked-up project root.
|
|
53
|
+
* Only reads siteUrl — profile and apiKey fields are ignored (legacy or misplaced).
|
|
54
|
+
* Returns null if file missing or project root not found.
|
|
55
|
+
* Warns to stderr on invalid JSON but does not throw.
|
|
56
|
+
*/
|
|
57
|
+
export function readProjectConfig() {
|
|
58
|
+
const projectRoot = findProjectRoot(process.cwd());
|
|
59
|
+
if (!projectRoot)
|
|
60
|
+
return null;
|
|
61
|
+
const configPath = resolve(projectRoot, '.productbrain', 'config.json');
|
|
62
|
+
if (!existsSync(configPath))
|
|
63
|
+
return null;
|
|
64
|
+
try {
|
|
65
|
+
const raw = readFileSync(configPath, 'utf8');
|
|
66
|
+
const parsed = JSON.parse(raw);
|
|
67
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
68
|
+
process.stderr.write('Warning: .productbrain/config.json is not a JSON object, skipping.\n');
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
const result = {};
|
|
72
|
+
if (typeof parsed.siteUrl === 'string' && parsed.siteUrl) {
|
|
73
|
+
result.siteUrl = parsed.siteUrl;
|
|
74
|
+
}
|
|
75
|
+
// Silently ignore `profile` and `apiKey` in config.json — those must be in config.local.json
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
process.stderr.write('Warning: .productbrain/config.json contains invalid JSON, skipping.\n');
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Read .productbrain/config.local.json from the walked-up project root.
|
|
85
|
+
* Returns the machine-local pin (`profile`) and any preview-binding fields
|
|
86
|
+
* (`apiKey`, `siteUrl`, `preview`, `workspaceName`) written by
|
|
87
|
+
* `npm run pb:preview` — all gitignored, never committed.
|
|
88
|
+
* Returns null if file missing or project root not found.
|
|
89
|
+
* Warns to stderr on invalid JSON but does not throw.
|
|
90
|
+
*/
|
|
91
|
+
export function readLocalProjectConfig() {
|
|
92
|
+
const projectRoot = findProjectRoot(process.cwd());
|
|
93
|
+
if (!projectRoot)
|
|
94
|
+
return null;
|
|
95
|
+
const configPath = resolve(projectRoot, '.productbrain', 'config.local.json');
|
|
96
|
+
if (!existsSync(configPath))
|
|
97
|
+
return null;
|
|
98
|
+
try {
|
|
99
|
+
const raw = readFileSync(configPath, 'utf8');
|
|
100
|
+
const parsed = JSON.parse(raw);
|
|
101
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
102
|
+
process.stderr.write('Warning: .productbrain/config.local.json is not a JSON object, skipping.\n');
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
const result = {};
|
|
106
|
+
if (typeof parsed.profile === 'string' && parsed.profile) {
|
|
107
|
+
result.profile = parsed.profile;
|
|
108
|
+
}
|
|
109
|
+
// Preview binding fields — written by `npm run pb:preview`, cleared by `npm run pb:real`.
|
|
110
|
+
if (typeof parsed.apiKey === 'string' && parsed.apiKey.startsWith(API_KEY_PREFIX)) {
|
|
111
|
+
result.apiKey = parsed.apiKey;
|
|
112
|
+
}
|
|
113
|
+
if (typeof parsed.siteUrl === 'string' && parsed.siteUrl) {
|
|
114
|
+
result.siteUrl = parsed.siteUrl;
|
|
115
|
+
}
|
|
116
|
+
if (parsed.preview === true) {
|
|
117
|
+
result.preview = true;
|
|
118
|
+
}
|
|
119
|
+
if (typeof parsed.workspaceName === 'string' && parsed.workspaceName) {
|
|
120
|
+
result.workspaceName = parsed.workspaceName;
|
|
121
|
+
}
|
|
122
|
+
return result;
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
process.stderr.write('Warning: .productbrain/config.local.json contains invalid JSON, skipping.\n');
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=projectConfig.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projectConfig.js","sourceRoot":"","sources":["../../src/lib/projectConfig.ts"],"names":[],"mappings":"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"}
|
|
@@ -18,11 +18,15 @@
|
|
|
18
18
|
* label and its qualifier can never contradict — and there is no per-write network
|
|
19
19
|
* round-trip in front of the write.
|
|
20
20
|
*
|
|
21
|
-
* Output contract —
|
|
22
|
-
* - The notice is
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
21
|
+
* Output contract — follows capture.ts's grounding banner (the JSON-safety pattern):
|
|
22
|
+
* - The notice is written to STDERR, never stdout, so strict-JSON stdout is byte-for-byte
|
|
23
|
+
* unchanged in every mode.
|
|
24
|
+
* - `--json` suppresses only the PLAIN line for an explicit binding (no warning to carry,
|
|
25
|
+
* pure noise in agent logs). The unpinned-fallback WARNING is emitted in json mode too:
|
|
26
|
+
* agents invoke pb with `--json` and do most of the writing, so suppressing it there
|
|
27
|
+
* disabled this guard for its primary audience (TEN-3238).
|
|
28
|
+
* - The gate is the `json` flag alone — not `--quiet`: a quiet human run still names its
|
|
29
|
+
* target Chain.
|
|
26
30
|
*
|
|
27
31
|
* SSOT: one pure formatter + one thin emitter, called from all four write commands —
|
|
28
32
|
* no per-command copy-paste.
|
|
@@ -47,9 +51,17 @@ export interface WriteTargetNoticeInput {
|
|
|
47
51
|
*/
|
|
48
52
|
export declare function formatWriteTargetNotice({ profile, source }: WriteTargetNoticeInput): string | null;
|
|
49
53
|
/**
|
|
50
|
-
* Resolve identity and write the target notice to STDERR
|
|
51
|
-
*
|
|
52
|
-
*
|
|
54
|
+
* Resolve identity and write the target notice to STDERR. Pass the SAME `json` value the
|
|
55
|
+
* command computed (e.g. `isJsonMode() || options.json === true`).
|
|
56
|
+
*
|
|
57
|
+
* `json` suppresses only the PLAIN line for an explicit binding. The unpinned-fallback
|
|
58
|
+
* WARNING is never suppressed (TEN-3238): `--json` is how agents invoke pb, and agents are
|
|
59
|
+
* the bulk of Chain writers, so gating the warning on `json` switched this guard off in
|
|
60
|
+
* exactly the mode that needed it — a wrong-workspace write from an unpinned directory was
|
|
61
|
+
* silent for every agent. Emitting it in json mode is safe because it goes to STDERR, so
|
|
62
|
+
* strict JSON on stdout stays byte-for-byte unchanged either way (and the CLI already writes
|
|
63
|
+
* unconditional stderr warnings elsewhere, e.g. the preview-scrub misroute in config.ts).
|
|
64
|
+
* The plain line stays human-only: it carries no warning, so it is pure noise in agent logs.
|
|
53
65
|
*
|
|
54
66
|
* Never throws and does no network IO: a wrong/unreadable config degrades to silence, and
|
|
55
67
|
* the label comes from `resolveEffectiveProfile()` alone — only fast local config reads
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspaceNotice.d.ts","sourceRoot":"","sources":["../../src/lib/workspaceNotice.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"workspaceNotice.d.ts","sourceRoot":"","sources":["../../src/lib/workspaceNotice.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,EAA2B,KAAK,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAEnF,2CAA2C;AAC3C,MAAM,WAAW,sBAAsB;IACrC,yFAAyF;IACzF,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,oEAAoE;IACpE,MAAM,EAAE,sBAAsB,CAAC;CAChC;AAED;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,sBAAsB,GAAG,MAAM,GAAG,IAAI,CAQlG;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAexE"}
|
|
@@ -18,11 +18,15 @@
|
|
|
18
18
|
* label and its qualifier can never contradict — and there is no per-write network
|
|
19
19
|
* round-trip in front of the write.
|
|
20
20
|
*
|
|
21
|
-
* Output contract —
|
|
22
|
-
* - The notice is
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
21
|
+
* Output contract — follows capture.ts's grounding banner (the JSON-safety pattern):
|
|
22
|
+
* - The notice is written to STDERR, never stdout, so strict-JSON stdout is byte-for-byte
|
|
23
|
+
* unchanged in every mode.
|
|
24
|
+
* - `--json` suppresses only the PLAIN line for an explicit binding (no warning to carry,
|
|
25
|
+
* pure noise in agent logs). The unpinned-fallback WARNING is emitted in json mode too:
|
|
26
|
+
* agents invoke pb with `--json` and do most of the writing, so suppressing it there
|
|
27
|
+
* disabled this guard for its primary audience (TEN-3238).
|
|
28
|
+
* - The gate is the `json` flag alone — not `--quiet`: a quiet human run still names its
|
|
29
|
+
* target Chain.
|
|
26
30
|
*
|
|
27
31
|
* SSOT: one pure formatter + one thin emitter, called from all four write commands —
|
|
28
32
|
* no per-command copy-paste.
|
|
@@ -49,17 +53,23 @@ export function formatWriteTargetNotice({ profile, source }) {
|
|
|
49
53
|
return `→ ${name}`;
|
|
50
54
|
}
|
|
51
55
|
/**
|
|
52
|
-
* Resolve identity and write the target notice to STDERR
|
|
53
|
-
*
|
|
54
|
-
*
|
|
56
|
+
* Resolve identity and write the target notice to STDERR. Pass the SAME `json` value the
|
|
57
|
+
* command computed (e.g. `isJsonMode() || options.json === true`).
|
|
58
|
+
*
|
|
59
|
+
* `json` suppresses only the PLAIN line for an explicit binding. The unpinned-fallback
|
|
60
|
+
* WARNING is never suppressed (TEN-3238): `--json` is how agents invoke pb, and agents are
|
|
61
|
+
* the bulk of Chain writers, so gating the warning on `json` switched this guard off in
|
|
62
|
+
* exactly the mode that needed it — a wrong-workspace write from an unpinned directory was
|
|
63
|
+
* silent for every agent. Emitting it in json mode is safe because it goes to STDERR, so
|
|
64
|
+
* strict JSON on stdout stays byte-for-byte unchanged either way (and the CLI already writes
|
|
65
|
+
* unconditional stderr warnings elsewhere, e.g. the preview-scrub misroute in config.ts).
|
|
66
|
+
* The plain line stays human-only: it carries no warning, so it is pure noise in agent logs.
|
|
55
67
|
*
|
|
56
68
|
* Never throws and does no network IO: a wrong/unreadable config degrades to silence, and
|
|
57
69
|
* the label comes from `resolveEffectiveProfile()` alone — only fast local config reads
|
|
58
70
|
* (no probe) in front of the write. It is safe to call immediately before any Chain write.
|
|
59
71
|
*/
|
|
60
72
|
export async function emitWriteTargetNotice(json) {
|
|
61
|
-
if (json)
|
|
62
|
-
return; // strict-JSON stdout stays byte-for-byte unchanged.
|
|
63
73
|
let profile = null;
|
|
64
74
|
let source = 'none';
|
|
65
75
|
try {
|
|
@@ -70,6 +80,9 @@ export async function emitWriteTargetNotice(json) {
|
|
|
70
80
|
}
|
|
71
81
|
if (source === 'none')
|
|
72
82
|
return; // not configured — nothing to name.
|
|
83
|
+
const isUnpinnedFallback = source === 'global' || source === 'default';
|
|
84
|
+
if (json && !isUnpinnedFallback)
|
|
85
|
+
return;
|
|
73
86
|
const notice = formatWriteTargetNotice({ profile, source });
|
|
74
87
|
if (notice)
|
|
75
88
|
process.stderr.write(notice + '\n');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspaceNotice.js","sourceRoot":"","sources":["../../src/lib/workspaceNotice.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"workspaceNotice.js","sourceRoot":"","sources":["../../src/lib/workspaceNotice.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,EAAE,uBAAuB,EAA+B,MAAM,aAAa,CAAC;AAUnF;;;;;;;;;GASG;AACH,MAAM,UAAU,uBAAuB,CAAC,EAAE,OAAO,EAAE,MAAM,EAA0B;IACjF,IAAI,MAAM,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC;IACnC,MAAM,IAAI,GAAG,OAAO,EAAE,IAAI,EAAE,IAAI,SAAS,CAAC;IAC1C,IAAI,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QAChD,OAAO,KAAK,IAAI,oEAAoE,CAAC;IACvF,CAAC;IACD,sDAAsD;IACtD,OAAO,KAAK,IAAI,EAAE,CAAC;AACrB,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,IAAa;IACvD,IAAI,OAAO,GAAkB,IAAI,CAAC;IAClC,IAAI,MAAM,GAA2B,MAAM,CAAC;IAC5C,IAAI,CAAC;QACH,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,uBAAuB,EAAE,CAAC,CAAC;IACpD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,oEAAoE;IAC9E,CAAC;IACD,IAAI,MAAM,KAAK,MAAM;QAAE,OAAO,CAAC,oCAAoC;IAEnE,MAAM,kBAAkB,GAAG,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,SAAS,CAAC;IACvE,IAAI,IAAI,IAAI,CAAC,kBAAkB;QAAE,OAAO;IAExC,MAAM,MAAM,GAAG,uBAAuB,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;IAC5D,IAAI,MAAM;QAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AAClD,CAAC"}
|