@goodandready/dsh-goal 0.2.22 → 0.2.24

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.
@@ -7,11 +7,11 @@ import { getSafeCwd } from './goal-engine-constants.js';
7
7
  * @param {string} workspaceRoot
8
8
  * @returns {boolean}
9
9
  */
10
- export function isGitRepository(workspaceRoot) {
10
+ export function isGitRepository(workspaceRoot, allowedRoots = []) {
11
11
  if (!workspaceRoot || typeof workspaceRoot !== 'string') return false;
12
12
  try {
13
- const safePath = getSafeCwd(workspaceRoot);
14
- if (!fs.existsSync(safePath)) return false;
13
+ const safePath = getSafeCwd(workspaceRoot, allowedRoots);
14
+ if (!safePath || !fs.existsSync(safePath)) return false;
15
15
  execFileSync('git', ['rev-parse', '--is-inside-work-tree'], {
16
16
  cwd: safePath,
17
17
  stdio: ['ignore', 'pipe', 'ignore'],
@@ -61,9 +61,9 @@ export function sanitizeBranchSlug(title) {
61
61
  * @param {string} cwd
62
62
  * @returns {string} branch name or 'HEAD'
63
63
  */
64
- export function getCurrentGitBranch(cwd = process.cwd()) {
65
- const safeCwd = getSafeCwd(cwd);
66
- if (!isGitRepository(safeCwd)) return 'main';
64
+ export function getCurrentGitBranch(cwd = process.cwd(), allowedRoots = []) {
65
+ const safeCwd = getSafeCwd(cwd, allowedRoots);
66
+ if (!safeCwd || !isGitRepository(safeCwd, allowedRoots)) return 'main';
67
67
  try {
68
68
  return execFileSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], {
69
69
  cwd: safeCwd,
@@ -81,10 +81,10 @@ export function getCurrentGitBranch(cwd = process.cwd()) {
81
81
  * @param {string} branchName
82
82
  * @returns {boolean} success
83
83
  */
84
- export function createGoalBranch(cwd = process.cwd(), branchName) {
84
+ export function createGoalBranch(cwd = process.cwd(), branchName, allowedRoots = []) {
85
85
  if (!isValidGitRef(branchName)) return false;
86
- const safeCwd = getSafeCwd(cwd);
87
- if (!isGitRepository(safeCwd)) return false;
86
+ const safeCwd = getSafeCwd(cwd, allowedRoots);
87
+ if (!safeCwd || !isGitRepository(safeCwd, allowedRoots)) return false;
88
88
  try {
89
89
  execFileSync('git', ['checkout', '-b', branchName], {
90
90
  cwd: safeCwd,
@@ -104,12 +104,12 @@ export function createGoalBranch(cwd = process.cwd(), branchName) {
104
104
  * @param {string} targetBranch
105
105
  * @returns {boolean} success
106
106
  */
107
- export function mergeGoalBranch(cwd = process.cwd(), branchName, targetBranch = 'main') {
107
+ export function mergeGoalBranch(cwd = process.cwd(), branchName, targetBranch = 'main', allowedRoots = []) {
108
108
  if (!isValidGitRef(branchName) || !isValidGitRef(targetBranch)) return false;
109
- const safeCwd = getSafeCwd(cwd);
110
- if (!isGitRepository(safeCwd)) return false;
109
+ const safeCwd = getSafeCwd(cwd, allowedRoots);
110
+ if (!safeCwd || !isGitRepository(safeCwd, allowedRoots)) return false;
111
111
  try {
112
- execFileSync('git', ['checkout', '--', targetBranch], {
112
+ execFileSync('git', ['checkout', targetBranch], {
113
113
  cwd: safeCwd,
114
114
  encoding: 'utf8',
115
115
  stdio: ['ignore', 'pipe', 'pipe'],
@@ -132,12 +132,12 @@ export function mergeGoalBranch(cwd = process.cwd(), branchName, targetBranch =
132
132
  * @param {string} targetBranch
133
133
  * @returns {boolean} success
134
134
  */
135
- export function discardGoalBranch(cwd = process.cwd(), branchName, targetBranch = 'main') {
135
+ export function discardGoalBranch(cwd = process.cwd(), branchName, targetBranch = 'main', allowedRoots = []) {
136
136
  if (!isValidGitRef(branchName) || !isValidGitRef(targetBranch)) return false;
137
- const safeCwd = getSafeCwd(cwd);
138
- if (!isGitRepository(safeCwd)) return false;
137
+ const safeCwd = getSafeCwd(cwd, allowedRoots);
138
+ if (!safeCwd || !isGitRepository(safeCwd, allowedRoots)) return false;
139
139
  try {
140
- execFileSync('git', ['checkout', '--', targetBranch], {
140
+ execFileSync('git', ['checkout', targetBranch], {
141
141
  cwd: safeCwd,
142
142
  encoding: 'utf8',
143
143
  stdio: ['ignore', 'pipe', 'pipe'],
package/lib/routes.js CHANGED
@@ -18,7 +18,7 @@ export function isLoopback(value) {
18
18
  );
19
19
  }
20
20
 
21
- export function isTrustedCaller(req) {
21
+ export function isTrustedCaller(req, authToken = process.env.DSH_GOAL_AUTH_TOKEN) {
22
22
  if (!req) return false;
23
23
  const remote = req.socket?.remoteAddress || req.connection?.remoteAddress || "";
24
24
  const site = req.headers?.["sec-fetch-site"];
@@ -26,6 +26,19 @@ export function isTrustedCaller(req) {
26
26
  // 1. Cross-site and same-site browser requests are rejected
27
27
  if (site === "cross-site" || site === "same-site") return false;
28
28
 
29
+ // Optional authorization token check (supports x-dsh-auth-token or Authorization: Bearer <token>)
30
+ if (authToken) {
31
+ const headerVal = req.headers?.["x-dsh-auth-token"] || req.headers?.authorization;
32
+ const cleanHeader = headerVal ? String(headerVal).replace(/^Bearer\s+/i, "").trim() : "";
33
+ if (cleanHeader === authToken) {
34
+ return true;
35
+ }
36
+ // When authToken is explicitly configured, non-loopback callers without valid token are rejected
37
+ if (!isLoopback(remote)) {
38
+ return false;
39
+ }
40
+ }
41
+
29
42
  // 2. Same-origin browser UI requests (operator UI on LAN or localhost)
30
43
  if (site === "same-origin") {
31
44
  const host = String(req.headers?.host || "").toLowerCase();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodandready/dsh-goal",
3
- "version": "0.2.22",
3
+ "version": "0.2.24",
4
4
  "description": "Core Goal Addon (Token Budget, Milestones, Git Snapshots & Retrospective Reports) for DeepSeek Harness",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -19,7 +19,8 @@
19
19
  "LICENSE"
20
20
  ],
21
21
  "scripts": {
22
- "test": "NODE_ENV=test node --test test/*.test.mjs"
22
+ "build:client": "node scripts/build-client.mjs",
23
+ "test": "node scripts/build-client.mjs --check && NODE_ENV=test node --test test/*.test.mjs"
23
24
  },
24
25
  "keywords": [
25
26
  "dsh",