@goodandready/dsh-goal 0.2.7 → 0.2.9
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/lib/client.js +13 -13
- package/lib/goal-engine.js +1 -0
- package/lib/routes.js +87 -9
- package/package.json +6 -2
package/lib/client.js
CHANGED
|
@@ -4685,7 +4685,7 @@ window.__ModuleLoader__.load({
|
|
|
4685
4685
|
|
|
4686
4686
|
};
|
|
4687
4687
|
|
|
4688
|
-
module.exports.inject = ['slots', 'locale', '
|
|
4688
|
+
module.exports.inject = ['slots', 'locale', 'configForms'];
|
|
4689
4689
|
|
|
4690
4690
|
module.exports.apply = function apply(ctx) {
|
|
4691
4691
|
if (typeof ctx.effect === 'function') {
|
|
@@ -4694,9 +4694,9 @@ window.__ModuleLoader__.load({
|
|
|
4694
4694
|
ctx.locale?.register?.(NS, LOCALES);
|
|
4695
4695
|
} catch (err) { /* handled defensively */ }
|
|
4696
4696
|
|
|
4697
|
-
const
|
|
4698
|
-
ctx.
|
|
4699
|
-
? ctx.
|
|
4697
|
+
const configForms =
|
|
4698
|
+
ctx.configForms && typeof ctx.configForms.get === 'function'
|
|
4699
|
+
? ctx.configForms.get(NS)
|
|
4700
4700
|
: null;
|
|
4701
4701
|
|
|
4702
4702
|
const disposers = [];
|
|
@@ -4736,7 +4736,7 @@ window.__ModuleLoader__.load({
|
|
|
4736
4736
|
order: 60,
|
|
4737
4737
|
label: () => 'Goal',
|
|
4738
4738
|
locale: NS,
|
|
4739
|
-
inject: () => ({ ctx, scope:
|
|
4739
|
+
inject: () => ({ ctx, scope: configForms }),
|
|
4740
4740
|
},
|
|
4741
4741
|
GoalSettingsCard,
|
|
4742
4742
|
);
|
|
@@ -4747,7 +4747,7 @@ window.__ModuleLoader__.load({
|
|
|
4747
4747
|
name: 'plugins.row.config',
|
|
4748
4748
|
key: ROW_CONFIG_KEY,
|
|
4749
4749
|
locale: NS,
|
|
4750
|
-
inject: () => ({ ctx, scope:
|
|
4750
|
+
inject: () => ({ ctx, scope: configForms }),
|
|
4751
4751
|
},
|
|
4752
4752
|
GoalSettingsCard,
|
|
4753
4753
|
);
|
|
@@ -4758,7 +4758,7 @@ window.__ModuleLoader__.load({
|
|
|
4758
4758
|
name: 'settings.plugin.item',
|
|
4759
4759
|
key: NS,
|
|
4760
4760
|
locale: NS,
|
|
4761
|
-
inject: () => ({ ctx, scope:
|
|
4761
|
+
inject: () => ({ ctx, scope: configForms }),
|
|
4762
4762
|
},
|
|
4763
4763
|
GoalSettingsCard,
|
|
4764
4764
|
);
|
|
@@ -4774,9 +4774,9 @@ window.__ModuleLoader__.load({
|
|
|
4774
4774
|
ctx.locale?.register?.(NS, LOCALES);
|
|
4775
4775
|
} catch (err) { /* handled defensively */ }
|
|
4776
4776
|
|
|
4777
|
-
const
|
|
4778
|
-
ctx.
|
|
4779
|
-
? ctx.
|
|
4777
|
+
const configForms =
|
|
4778
|
+
ctx.configForms && typeof ctx.configForms.get === 'function'
|
|
4779
|
+
? ctx.configForms.get(NS)
|
|
4780
4780
|
: null;
|
|
4781
4781
|
|
|
4782
4782
|
const registerSlotSafe = (name, entry, comp) => {
|
|
@@ -4813,7 +4813,7 @@ window.__ModuleLoader__.load({
|
|
|
4813
4813
|
order: 60,
|
|
4814
4814
|
label: () => 'Goal',
|
|
4815
4815
|
locale: NS,
|
|
4816
|
-
inject: () => ({ ctx, scope:
|
|
4816
|
+
inject: () => ({ ctx, scope: configForms }),
|
|
4817
4817
|
},
|
|
4818
4818
|
GoalSettingsCard,
|
|
4819
4819
|
);
|
|
@@ -4824,7 +4824,7 @@ window.__ModuleLoader__.load({
|
|
|
4824
4824
|
name: 'plugins.row.config',
|
|
4825
4825
|
key: ROW_CONFIG_KEY,
|
|
4826
4826
|
locale: NS,
|
|
4827
|
-
inject: () => ({ ctx, scope:
|
|
4827
|
+
inject: () => ({ ctx, scope: configForms }),
|
|
4828
4828
|
},
|
|
4829
4829
|
GoalSettingsCard,
|
|
4830
4830
|
);
|
|
@@ -4835,7 +4835,7 @@ window.__ModuleLoader__.load({
|
|
|
4835
4835
|
name: 'settings.plugin.item',
|
|
4836
4836
|
key: NS,
|
|
4837
4837
|
locale: NS,
|
|
4838
|
-
inject: () => ({ ctx, scope:
|
|
4838
|
+
inject: () => ({ ctx, scope: configForms }),
|
|
4839
4839
|
},
|
|
4840
4840
|
GoalSettingsCard,
|
|
4841
4841
|
);
|
package/lib/goal-engine.js
CHANGED
|
@@ -225,6 +225,7 @@ export class GoalEngine {
|
|
|
225
225
|
title: cleanTitle,
|
|
226
226
|
description: options.description?.trim() || '',
|
|
227
227
|
lang: options.lang || detectLanguage(cleanTitle),
|
|
228
|
+
cwd: options.cwd ? String(options.cwd).trim() : null,
|
|
228
229
|
state: GoalState.RUNNING,
|
|
229
230
|
startedAt: now,
|
|
230
231
|
pausedAt: null,
|
package/lib/routes.js
CHANGED
|
@@ -3,16 +3,82 @@ import path from 'node:path';
|
|
|
3
3
|
import { MilestoneStatus, detectLanguage, sessionIdOf } from './goal-engine-constants.js';
|
|
4
4
|
import { saveGoalArtifact, rollbackToCheckpoint } from './engine-reports.js';
|
|
5
5
|
|
|
6
|
-
function
|
|
7
|
-
if (!
|
|
8
|
-
|
|
6
|
+
export function isLoopback(value) {
|
|
7
|
+
if (!value) return false;
|
|
8
|
+
const address = String(value).toLowerCase().replace(/^\[|\]$/g, '');
|
|
9
|
+
return Boolean(
|
|
10
|
+
address === 'localhost' ||
|
|
11
|
+
address === 'localhost.' ||
|
|
12
|
+
address === '::1' ||
|
|
13
|
+
address.startsWith('127.') ||
|
|
14
|
+
address.startsWith('::ffff:127.')
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function isTrustedCaller(req) {
|
|
19
|
+
if (!req) return false;
|
|
20
|
+
const remote = req.socket?.remoteAddress || req.connection?.remoteAddress || '';
|
|
21
|
+
if (isLoopback(remote)) return true;
|
|
22
|
+
|
|
23
|
+
const secSite = String(req.headers?.['sec-fetch-site'] || '').toLowerCase();
|
|
24
|
+
if (secSite === 'same-origin' || secSite === 'same-site' || secSite === 'none') return true;
|
|
25
|
+
if (secSite === 'cross-site') return false;
|
|
26
|
+
|
|
27
|
+
const host = String(req.headers?.host || '').toLowerCase();
|
|
28
|
+
const origin = String(req.headers?.origin || '').trim().toLowerCase();
|
|
29
|
+
if (origin) {
|
|
30
|
+
try {
|
|
31
|
+
const u = new URL(origin);
|
|
32
|
+
if (u.host === host) return true;
|
|
33
|
+
} catch (err) { /* invalid url */ }
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const referer = String(req.headers?.referer || '').trim().toLowerCase();
|
|
38
|
+
if (referer) {
|
|
39
|
+
try {
|
|
40
|
+
const u = new URL(referer);
|
|
41
|
+
if (u.host === host) return true;
|
|
42
|
+
} catch (err) { /* invalid url */ }
|
|
43
|
+
return false;
|
|
9
44
|
}
|
|
45
|
+
|
|
46
|
+
// Fallback for tests or local CLI without socket where host header targets loopback
|
|
47
|
+
if (!remote && host) {
|
|
48
|
+
const hostName = host.split(':')[0];
|
|
49
|
+
if (isLoopback(hostName)) {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function isPathInsideOrEqual(parent, child) {
|
|
58
|
+
if (!parent || !child) return false;
|
|
59
|
+
const rel = path.relative(path.resolve(parent), path.resolve(child));
|
|
60
|
+
return !rel || (!rel.startsWith('..') && !path.isAbsolute(rel));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function getSafeCwd(requestedCwd, allowedRoots = []) {
|
|
64
|
+
const defaultRoot = allowedRoots[0] || process.cwd();
|
|
65
|
+
if (!requestedCwd || typeof requestedCwd !== 'string' || !requestedCwd.trim()) {
|
|
66
|
+
return defaultRoot;
|
|
67
|
+
}
|
|
68
|
+
|
|
10
69
|
try {
|
|
11
70
|
const resolved = path.resolve(requestedCwd.trim());
|
|
12
71
|
const st = fs.statSync(resolved);
|
|
13
72
|
if (!st.isDirectory()) {
|
|
14
73
|
return null;
|
|
15
74
|
}
|
|
75
|
+
|
|
76
|
+
const roots = allowedRoots.length > 0 ? allowedRoots : [process.cwd()];
|
|
77
|
+
const isAllowed = roots.some((root) => isPathInsideOrEqual(root, resolved));
|
|
78
|
+
if (!isAllowed) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
|
|
16
82
|
return resolved;
|
|
17
83
|
} catch (err) {
|
|
18
84
|
return null;
|
|
@@ -144,8 +210,8 @@ export function registerRoutes(ctx, {
|
|
|
144
210
|
return res.end(JSON.stringify({ error: 'Forbidden: cross-site requests are rejected' }));
|
|
145
211
|
}
|
|
146
212
|
|
|
147
|
-
const origin = req.headers
|
|
148
|
-
const host = req.headers
|
|
213
|
+
const origin = req.headers?.origin;
|
|
214
|
+
const host = req.headers?.host;
|
|
149
215
|
if (origin && host) {
|
|
150
216
|
try {
|
|
151
217
|
const originHost = new URL(origin).host;
|
|
@@ -159,6 +225,11 @@ export function registerRoutes(ctx, {
|
|
|
159
225
|
}
|
|
160
226
|
}
|
|
161
227
|
|
|
228
|
+
if (!isTrustedCaller(req)) {
|
|
229
|
+
res.statusCode = 403;
|
|
230
|
+
return res.end(JSON.stringify({ error: 'Forbidden: untrusted caller origin' }));
|
|
231
|
+
}
|
|
232
|
+
|
|
162
233
|
let body = '';
|
|
163
234
|
let bodySize = 0;
|
|
164
235
|
const MAX_PAYLOAD_BYTES = 256 * 1024;
|
|
@@ -196,6 +267,7 @@ export function registerRoutes(ctx, {
|
|
|
196
267
|
description: typeof description === 'string' ? description.trim() : '',
|
|
197
268
|
maxIterations: conf.maxIterations || 25,
|
|
198
269
|
lang: detectedLang,
|
|
270
|
+
cwd: data.cwd,
|
|
199
271
|
}, sid);
|
|
200
272
|
|
|
201
273
|
const startPrompt = detectedLang === 'zh'
|
|
@@ -309,8 +381,11 @@ export function registerRoutes(ctx, {
|
|
|
309
381
|
res.statusCode = 400;
|
|
310
382
|
return res.end(JSON.stringify({ error: 'commit hash is required for rollback' }));
|
|
311
383
|
}
|
|
312
|
-
const
|
|
313
|
-
|
|
384
|
+
const sessionGoal = engine.getGoal(sid);
|
|
385
|
+
const allowedRoots = [process.cwd()];
|
|
386
|
+
if (sessionGoal?.cwd) allowedRoots.push(sessionGoal.cwd);
|
|
387
|
+
const safeCwd = getSafeCwd(data.cwd, allowedRoots);
|
|
388
|
+
if (!safeCwd) {
|
|
314
389
|
res.statusCode = 400;
|
|
315
390
|
return res.end(JSON.stringify({ error: 'Invalid or non-existent working directory' }));
|
|
316
391
|
}
|
|
@@ -329,8 +404,11 @@ export function registerRoutes(ctx, {
|
|
|
329
404
|
res.statusCode = 404;
|
|
330
405
|
return res.end(JSON.stringify({ error: 'No active goal to save artifact for' }));
|
|
331
406
|
}
|
|
332
|
-
const
|
|
333
|
-
|
|
407
|
+
const sessionGoal = engine.getGoal(sid);
|
|
408
|
+
const allowedRoots = [process.cwd()];
|
|
409
|
+
if (sessionGoal?.cwd) allowedRoots.push(sessionGoal.cwd);
|
|
410
|
+
const safeCwd = getSafeCwd(data.cwd, allowedRoots);
|
|
411
|
+
if (!safeCwd) {
|
|
334
412
|
res.statusCode = 400;
|
|
335
413
|
return res.end(JSON.stringify({ error: 'Invalid or non-existent working directory' }));
|
|
336
414
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodandready/dsh-goal",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.9",
|
|
4
4
|
"description": "Autonomous Goal Execution & Multi-Turn Task Tracking Engine with Sticky Header for DeepSeek Harness",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -45,7 +45,11 @@
|
|
|
45
45
|
},
|
|
46
46
|
"client": {
|
|
47
47
|
"platform": "web",
|
|
48
|
-
"inject": [
|
|
48
|
+
"inject": [
|
|
49
|
+
"@deepseek-ai/dsh-client-ui-slots",
|
|
50
|
+
"@deepseek-ai/dsh-client-locale",
|
|
51
|
+
"@deepseek-ai/dsh-client-ui-settings"
|
|
52
|
+
]
|
|
49
53
|
}
|
|
50
54
|
},
|
|
51
55
|
"peerDependencies": {
|