@productbrain/cli 0.1.0-beta.9294 → 0.1.0-beta.9328
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__/session-pinned-role.test.d.ts +2 -0
- package/dist/__tests__/session-pinned-role.test.d.ts.map +1 -0
- package/dist/__tests__/session-pinned-role.test.js +272 -0
- package/dist/__tests__/session-pinned-role.test.js.map +1 -0
- package/dist/commands/session.d.ts.map +1 -1
- package/dist/commands/session.js +12 -8
- package/dist/commands/session.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/client.js +1 -1
- package/dist/lib/client.js.map +1 -1
- package/dist/lib/errors.d.ts +4 -0
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +6 -0
- package/dist/lib/errors.js.map +1 -1
- package/dist/lib/pinnedRole.d.ts +28 -0
- package/dist/lib/pinnedRole.d.ts.map +1 -0
- package/dist/lib/pinnedRole.js +112 -0
- package/dist/lib/pinnedRole.js.map +1 -0
- package/dist/lib/productbrain-files.d.ts +1 -7
- package/dist/lib/productbrain-files.d.ts.map +1 -1
- package/dist/lib/productbrain-files.js +4 -17
- package/dist/lib/productbrain-files.js.map +1 -1
- package/dist/lib/productbrain-files.test.js +74 -1
- package/dist/lib/productbrain-files.test.js.map +1 -1
- package/dist/lib/projectConfig.d.ts +21 -5
- package/dist/lib/projectConfig.d.ts.map +1 -1
- package/dist/lib/projectConfig.js +57 -5
- package/dist/lib/projectConfig.js.map +1 -1
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* The repo's own Product Brain config files — locating them and reading what they declare.
|
|
3
3
|
*
|
|
4
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
|
|
5
|
+
* this module answers "what does THIS REPO say about itself" from the filesystem and git's index, while
|
|
6
6
|
* config.ts answers "which credential do we therefore use". Nothing here reads a credential
|
|
7
7
|
* store, an env var, or the global active profile — so it has no dependency on config.ts and
|
|
8
8
|
* cannot participate in an import cycle.
|
|
@@ -10,14 +10,17 @@
|
|
|
10
10
|
* Split config design:
|
|
11
11
|
* - config.json (committed): its PRESENCE declares a Product Brain project. The CLI never reads
|
|
12
12
|
* its siteUrl: a cloned repo controls that file, so it must not route a key (TEN-4500).
|
|
13
|
-
* - config.local.json (gitignored): profile pin
|
|
13
|
+
* - config.local.json (gitignored): profile pin, and an optional acting role (DEC-1961) —
|
|
14
|
+
* machine-local, never committed; a copy git tracks is refused (TEN-4501). Because it
|
|
14
15
|
* is gitignored, a fresh clone or worktree does NOT inherit it (INS-2462) — the caller must
|
|
15
16
|
* treat "no pin" as an explicit state, never as permission to guess a workspace.
|
|
16
17
|
*/
|
|
17
18
|
import { readFileSync, existsSync } from 'fs';
|
|
18
19
|
import { resolve } from 'path';
|
|
19
20
|
import { homedir } from 'os';
|
|
21
|
+
import { execFileSync } from 'child_process';
|
|
20
22
|
import { API_KEY_PREFIX } from './tokenConstants.js';
|
|
23
|
+
import { CLIError, ErrorCode } from './errors.js';
|
|
21
24
|
/**
|
|
22
25
|
* Walk up the directory tree (git-style) to find the nearest CLI project root.
|
|
23
26
|
*
|
|
@@ -65,13 +68,51 @@ export function projectConfigExists() {
|
|
|
65
68
|
return false;
|
|
66
69
|
return existsSync(resolve(projectRoot, '.productbrain', 'config.json'));
|
|
67
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Is `<root>/<rel>` tracked in git's index? A TRACKED file is never ignored regardless of
|
|
73
|
+
* .gitignore. `git ls-files -- <path>` prints the path when tracked, nothing when not. Not a git
|
|
74
|
+
* work tree, or git unavailable → null. Never throws. Lives here, not in productbrain-files.ts
|
|
75
|
+
* (which re-exports it), so the pin reader can use it without an import cycle.
|
|
76
|
+
*/
|
|
77
|
+
export function makeGitTrackedChecker(root) {
|
|
78
|
+
return (rel) => {
|
|
79
|
+
try {
|
|
80
|
+
const out = execFileSync('git', ['ls-files', '--', rel], { cwd: root, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] });
|
|
81
|
+
return out.trim().length > 0;
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return null; // not a git work tree, or git unavailable
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/** A printed entry id, e.g. ROL-12: the only shape a pinned role may take. */
|
|
89
|
+
const ROLE_ID = /^[A-Z]+-\d+$/;
|
|
90
|
+
/** Project roots whose pin git was asked about and does not track (one git spawn per root). */
|
|
91
|
+
const untrackedPinRoots = new Set();
|
|
92
|
+
/**
|
|
93
|
+
* TEN-4501 (TASK-639): a config.local.json that git tracks is committed, so its pin, its preview
|
|
94
|
+
* key and its role travel to every clone. Refuse to read it. Only a confirmed `true` refuses:
|
|
95
|
+
* outside a git work tree there is no index for the file to be in.
|
|
96
|
+
*/
|
|
97
|
+
function refuseTrackedPin(projectRoot) {
|
|
98
|
+
if (untrackedPinRoots.has(projectRoot))
|
|
99
|
+
return;
|
|
100
|
+
if (makeGitTrackedChecker(projectRoot)('.productbrain/config.local.json') === true) {
|
|
101
|
+
throw new CLIError(`${resolve(projectRoot, '.productbrain', 'config.local.json')} is tracked by git, so it is committed and every clone shares it. Refusing to read it.`, {
|
|
102
|
+
code: ErrorCode.CONFIG_INVALID,
|
|
103
|
+
category: 'config',
|
|
104
|
+
guidance: 'Untrack it with `git rm --cached .productbrain/config.local.json`, then run `pb files --fix` so git ignores it.',
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
untrackedPinRoots.add(projectRoot);
|
|
108
|
+
}
|
|
68
109
|
/**
|
|
69
110
|
* Read .productbrain/config.local.json from the walked-up project root.
|
|
70
|
-
* Returns the machine-local pin (`profile`)
|
|
71
|
-
* (`apiKey`, `siteUrl`, `preview`, `workspaceName`) written by
|
|
111
|
+
* Returns the machine-local pin (`profile`), the optional hand-added `role` (DEC-1961), and any
|
|
112
|
+
* preview-binding fields (`apiKey`, `siteUrl`, `preview`, `workspaceName`) written by
|
|
72
113
|
* `npm run pb:preview` — all gitignored, never committed.
|
|
73
114
|
* Returns null if file missing or project root not found.
|
|
74
|
-
* Warns to stderr on invalid JSON
|
|
115
|
+
* Warns to stderr on invalid JSON. Throws only when git tracks the file (refuseTrackedPin).
|
|
75
116
|
*/
|
|
76
117
|
export function readLocalProjectConfig() {
|
|
77
118
|
const projectRoot = findProjectRoot(process.cwd());
|
|
@@ -80,6 +121,7 @@ export function readLocalProjectConfig() {
|
|
|
80
121
|
const configPath = resolve(projectRoot, '.productbrain', 'config.local.json');
|
|
81
122
|
if (!existsSync(configPath))
|
|
82
123
|
return null;
|
|
124
|
+
refuseTrackedPin(projectRoot);
|
|
83
125
|
try {
|
|
84
126
|
const raw = readFileSync(configPath, 'utf8');
|
|
85
127
|
const parsed = JSON.parse(raw);
|
|
@@ -116,6 +158,16 @@ export function readLocalProjectConfig() {
|
|
|
116
158
|
if (typeof parsed.workspaceName === 'string' && parsed.workspaceName) {
|
|
117
159
|
result.workspaceName = parsed.workspaceName;
|
|
118
160
|
}
|
|
161
|
+
// Same fail-closed rule as apiKey: a present-but-unusable role is flagged, never dropped, so a
|
|
162
|
+
// session start refuses instead of acting as some other role (RULE-632). Only the printed id
|
|
163
|
+
// shape (ROL-12) is a role: anything else would reach the kernel and be echoed back raw.
|
|
164
|
+
if (parsed.role !== undefined && parsed.role !== null) {
|
|
165
|
+
const role = typeof parsed.role === 'string' ? parsed.role.trim() : '';
|
|
166
|
+
if (ROLE_ID.test(role))
|
|
167
|
+
result.role = role;
|
|
168
|
+
else
|
|
169
|
+
result.roleMalformed = true;
|
|
170
|
+
}
|
|
119
171
|
return result;
|
|
120
172
|
}
|
|
121
173
|
catch {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"projectConfig.js","sourceRoot":"","sources":["../../src/lib/projectConfig.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"projectConfig.js","sourceRoot":"","sources":["../../src/lib/projectConfig.ts"],"names":[],"mappings":"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"}
|